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;