You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gristlabs_grist-core/app/client/components/RecordLayoutEditor.js

154 lines
5.1 KiB

var _ = require('underscore');
var BackboneEvents = require('backbone').Events;
var dispose = require('app/client/lib/dispose');
var {makeT} = require('app/client/lib/localization');
var commands = require('./commands');
var LayoutEditor = require('./LayoutEditor');
const t = makeT('components.RecordLayoutEditor');
const {basicButton, cssButton, primaryButton} = require('app/client/ui2018/buttons');
const {icon} = require('app/client/ui2018/icons');
const {menu, menuDivider, menuItem} = require('app/client/ui2018/menus');
const {testId} = require('app/client/ui2018/cssVars');
const {dom, Observable, styled} = require('grainjs');
//----------------------------------------------------------------------
/**
* An extension of LayoutEditor which includes commands and the option for a callback function.
*
* Used by RecordLayout.js
*
* @param {layoutSpec} observable - An observable evaluating to the original layoutSpec of the layout.
* @param {optResizeCallback} Function - An optional function to be called after every resize during
* layout editing.
*/
function RecordLayoutEditor(recordLayout, layout, optResizeCallback) {
this.recordLayout = recordLayout;
this.layout = layout;
this.layoutEditor = this.autoDispose(LayoutEditor.LayoutEditor.create(layout));
this._hiddenColumns = this.autoDispose(Observable.create(null, this.getHiddenColumns()));
this.listenTo(layout, 'layoutChanged', function() {
this._hiddenColumns.set(this.getHiddenColumns());
});
if (optResizeCallback) {
this.listenTo(layout, 'layoutChanged', optResizeCallback);
this.listenTo(layout, 'layoutResized', optResizeCallback);
}
// Command group implementing the commands available while editing the layout.
this.autoDispose(commands.createGroup(RecordLayoutEditor.editLayoutCommands, this, true));
}
dispose.makeDisposable(RecordLayoutEditor);
_.extend(RecordLayoutEditor.prototype, BackboneEvents);
/**
* Commands active while editing the record layout.
*/
RecordLayoutEditor.editLayoutCommands = {
accept: function() {
this.recordLayout.onEditLayoutSave(this.layout.getLayoutSpec());
},
cancel: function() {
this.layout.buildLayout(this.recordLayout.layoutSpec());
this.recordLayout.onEditLayoutCancel();
},
};
/**
* Returns the list of columns that are not included in the current layout.
*/
RecordLayoutEditor.prototype.getHiddenColumns = function() {
var included = new Set(this.layout.getAllLeafIds().map(function(leafId) {
var f = this.recordLayout.getField(leafId);
return f.isNewField ? f.colRef : f.colRef.peek();
}, this));
return this.recordLayout.viewSection.table().columns().all().filter(function(col) {
return !included.has(col.getRowId()) && !col.isHiddenCol();
});
};
RecordLayoutEditor.prototype._addField = function(leafId) {
var newBox = this.layout.buildLayoutBox({ leaf: leafId });
var rows = this.layout.rootBox().childBoxes.peek();
if (rows.length >= 1 && _.last(rows).isLeaf()) {
// Add a new child to the last row.
_.last(rows).addChild(newBox, true);
} else {
// Add a new row.
this.layout.rootBox().addChild(newBox, true);
}
};
RecordLayoutEditor.prototype.buildEditorDom = function() {
const addNewField = () => { this._addField(':New_Field:'); };
const showField = (col) => {
// Use setTimeout, since showing a field synchronously removes it from the list, which would
// prevent the menu from closing if we don't let the event to run its course.
setTimeout(() => this._addField(col.getRowId() + ':' + col.label()), 0);
};
return cssControls(
basicButton(t('AddField'), cssCollapseIcon('Collapse'),
menu((ctl) => [
menuItem(() => addNewField(), t('CreateNewField')),
dom.maybe((use) => use(this._hiddenColumns).length > 0,
() => menuDivider()),
dom.forEach(this._hiddenColumns, (col) =>
menuItem(() => showField(col), t("ShowField", {label:col.label()}))
),
testId('edit-layout-add-menu'),
]),
),
dom('div.flexauto', {style: 'margin-left: 8px'}),
this.buildFinishButtons(),
testId('edit-layout-controls'),
);
};
RecordLayoutEditor.prototype.buildFinishButtons = function() {
return [
primaryButton(t('SaveLayout'),
dom.on('click', () => commands.allCommands.accept.run()),
),
basicButton(t('Cancel'),
dom.on('click', () => commands.allCommands.cancel.run()),
{style: 'margin-left: 8px'},
),
];
}
RecordLayoutEditor.prototype.buildLeafDom = function() {
return dom('div.layout_grabbable.g_record_layout_editing',
dom('div.g_record_delete_field.glyphicon.glyphicon-remove',
dom.on('mousedown', (ev) => ev.stopPropagation()),
dom.on('click', (ev, elem) => {
ev.preventDefault();
ev.stopPropagation();
this.layoutEditor.removeContainingBox(elem);
})
)
);
};
const cssControls = styled('div', `
display: flex;
align-items: flex-start;
& > .${cssButton.className} {
white-space: nowrap;
overflow: hidden;
}
`);
const cssCollapseIcon = styled(icon, `
margin: -3px -2px -2px 2px;
`);
module.exports = RecordLayoutEditor;