From 1a6d427339bba32b28661df730cb0693ee287a45 Mon Sep 17 00:00:00 2001 From: George Gevoian Date: Thu, 17 Nov 2022 15:17:51 -0500 Subject: [PATCH] (core) Update sort and filter UI Summary: The sort and filter UI now has a more unified UI, with similar capabilities that are accessible from different parts of Grist. It's now also possible to pin individual filters to the filter bar, which replaces the old toggle for showing all filters in the filter bar. Test Plan: Various tests (browser, migration, project). Reviewers: jarek, dsagal Reviewed By: jarek, dsagal Subscribers: dsagal Differential Revision: https://phab.getgrist.com/D3669 --- app/client/components/BaseView.js | 6 +- app/client/components/GridView.js | 28 +- app/client/components/ViewConfigTab.js | 517 +------------------ app/client/components/ViewLayout.ts | 20 +- app/client/components/commandList.js | 10 +- app/client/declarations.d.ts | 7 +- app/client/models/ColumnFilter.ts | 20 +- app/client/models/ColumnFilterMenuModel.ts | 26 +- app/client/models/entities/ViewSectionRec.ts | 138 +++-- app/client/ui/ColumnFilterMenu.ts | 159 ++++-- app/client/ui/FilterBar.ts | 109 ++-- app/client/ui/FilterConfig.ts | 149 ++++++ app/client/ui/RightPanel.ts | 17 +- app/client/ui/RightPanelStyles.ts | 32 ++ app/client/ui/SortConfig.ts | 372 +++++++++++++ app/client/ui/SortFilterConfig.ts | 68 +++ app/client/ui/ViewSectionMenu.ts | 312 +++++------ app/client/ui2018/IconList.ts | 4 + app/client/ui2018/buttons.ts | 1 + app/common/schema.ts | 4 +- app/server/lib/initialDocSql.ts | 8 +- sandbox/grist/migrations.py | 42 ++ sandbox/grist/schema.py | 7 +- sandbox/grist/test_useractions.py | 12 +- static/icons/icons.css | 2 + static/locales/en.client.json | 31 +- static/locales/fr.client.json | 31 +- static/ui-icons/UI/PinTilted.svg | 7 + static/ui-icons/UI/Revert.svg | 8 + test/client/models/ColumnFilter.ts | 11 +- test/fixtures/docs/Hello.grist | Bin 61440 -> 61440 bytes test/fixtures/docs/World-v33.grist | Bin 0 -> 493568 bytes test/nbrowser/ReferenceList.ts | 5 +- test/nbrowser/gristUtils.ts | 116 +++-- 34 files changed, 1348 insertions(+), 931 deletions(-) create mode 100644 app/client/ui/FilterConfig.ts create mode 100644 app/client/ui/SortConfig.ts create mode 100644 app/client/ui/SortFilterConfig.ts create mode 100644 static/ui-icons/UI/PinTilted.svg create mode 100644 static/ui-icons/UI/Revert.svg create mode 100644 test/fixtures/docs/World-v33.grist diff --git a/app/client/components/BaseView.js b/app/client/components/BaseView.js index 0099c68e..f2298731 100644 --- a/app/client/components/BaseView.js +++ b/app/client/components/BaseView.js @@ -430,7 +430,7 @@ BaseView.prototype.filterByThisCellValue = function() { } filterValues = [value]; } - this.viewSection.setFilter(col.getRowId(), JSON.stringify({included: filterValues})); + this.viewSection.setFilter(col.getRowId(), {filter: JSON.stringify({included: filterValues})}); }; /** @@ -732,9 +732,9 @@ BaseView.prototype.getLastDataRowIndex = function() { /** * Creates and opens ColumnFilterMenu for a given field/column, and returns its PopupControl. */ -BaseView.prototype.createFilterMenu = function(openCtl, filterInfo, onClose) { +BaseView.prototype.createFilterMenu = function(openCtl, filterInfo, options) { return createFilterMenu(openCtl, this._sectionFilter, filterInfo, this._mainRowSource, - this.tableModel.tableData, onClose); + this.tableModel.tableData, options); }; /** diff --git a/app/client/components/GridView.js b/app/client/components/GridView.js index 2f1e745d..162ebc41 100644 --- a/app/client/components/GridView.js +++ b/app/client/components/GridView.js @@ -46,6 +46,7 @@ const {mouseDragMatchElem} = require('app/client/ui/mouseDrag'); const {menuToggle} = require('app/client/ui/MenuToggle'); const {showTooltip} = require('app/client/ui/tooltips'); const {parsePasteForView} = require("./BaseView2"); +const {NEW_FILTER_JSON} = require('app/client/models/ColumnFilter'); const {CombinedStyle} = require("app/client/models/Styles"); // A threshold for interpreting a motionless click as a click rather than a drag. @@ -1073,12 +1074,16 @@ GridView.prototype.buildDom = function() { // Select the column if it's not part of a multiselect. dom.on('click', (ev) => this.maybeSelectColumn(ev.currentTarget.parentNode, field)), (elem) => { - filterTriggerCtl = setPopupToCreateDom(elem, ctl => this._columnFilterMenu(ctl, field), { - attach: 'body', - placement: 'bottom-start', - boundaries: 'viewport', - trigger: [], - }); + filterTriggerCtl = setPopupToCreateDom( + elem, + ctl => this._columnFilterMenu(ctl, field, {showAllFiltersButton: true}), + { + attach: 'body', + placement: 'bottom-start', + boundaries: 'viewport', + trigger: [], + } + ); }, menu(ctl => this.columnContextMenu(ctl, this.getSelection(), field, filterTriggerCtl)), testId('column-menu-trigger'), @@ -1623,11 +1628,18 @@ GridView.prototype._getColumnMenuOptions = function(copySelection) { }; } -GridView.prototype._columnFilterMenu = function(ctl, field) { +GridView.prototype._columnFilterMenu = function(ctl, field, options) { this.ctxMenuHolder.autoDispose(ctl); const filterInfo = this.viewSection.filters() .find(({fieldOrColumn}) => fieldOrColumn.origCol().origColRef() === field.column().origColRef()); - return this.createFilterMenu(ctl, filterInfo); + if (!filterInfo.isFiltered.peek()) { + // This is a new filter - initialize its spec and pin it. + this.viewSection.setFilter(filterInfo.fieldOrColumn.origCol().origColRef(), { + filter: NEW_FILTER_JSON, + pinned: true, + }); + } + return this.createFilterMenu(ctl, filterInfo, options); }; GridView.prototype.maybeSelectColumn = function (elem, field) { diff --git a/app/client/components/ViewConfigTab.js b/app/client/components/ViewConfigTab.js index e86b4583..bdec20a9 100644 --- a/app/client/components/ViewConfigTab.js +++ b/app/client/components/ViewConfigTab.js @@ -8,22 +8,13 @@ var koArray = require('../lib/koArray'); var commands = require('./commands'); var {CustomSectionElement} = require('../lib/CustomSectionElement'); const {ChartConfig} = require('./ChartView'); -const {Computed, dom: grainjsDom, makeTestId, Observable, styled, MultiHolder} = require('grainjs'); +const {Computed, dom: grainjsDom, makeTestId} = require('grainjs'); -const {addToSort} = require('app/client/lib/sortUtil'); -const {updatePositions} = require('app/client/lib/sortUtil'); -const {attachColumnFilterMenu} = require('app/client/ui/ColumnFilterMenu'); -const {addFilterMenu} = require('app/client/ui/FilterBar'); -const {cssIcon, cssRow} = require('app/client/ui/RightPanelStyles'); -const {basicButton, primaryButton} = require('app/client/ui2018/buttons'); -const {labeledLeftSquareCheckbox} = require("app/client/ui2018/checkbox"); -const {theme} = require('app/client/ui2018/cssVars'); -const {cssDragger} = require('app/client/ui2018/draggableList'); -const {menu, menuItem, select} = require('app/client/ui2018/menus'); +const {cssRow} = require('app/client/ui/RightPanelStyles'); +const {SortFilterConfig} = require('app/client/ui/SortFilterConfig'); +const {primaryButton} = require('app/client/ui2018/buttons'); +const {select} = require('app/client/ui2018/menus'); const {confirmModal} = require('app/client/ui2018/modals'); -const {Sort} = require('app/common/SortSpec'); -const isEqual = require('lodash/isEqual'); -const {cssMenuItem} = require('popweasel'); const {makeT} = require('app/client/lib/localization'); const testId = makeTestId('test-vconfigtab-'); @@ -56,282 +47,40 @@ function ViewConfigTab(options) { .setAutoDisposeValues() ); - this.activeSectionData = this.autoDispose(ko.computed(function() { - return _.find(self.viewSectionData.all(), function(sectionData) { - return sectionData.section && - sectionData.section.getRowId() === self.viewModel.activeSectionId(); - }) || self.viewSectionData.at(0); - })); this.isDetail = this.autoDispose(ko.computed(function() { return ['detail', 'single'].includes(this.viewModel.activeSection().parentKey()); }, this)); this.isChart = this.autoDispose(ko.computed(function() { - return this.viewModel.activeSection().parentKey() === 'chart';}, this)); + return this.viewModel.activeSection().parentKey() === 'chart';}, this)); this.isGrid = this.autoDispose(ko.computed(function() { - return this.viewModel.activeSection().parentKey() === 'record';}, this)); + return this.viewModel.activeSection().parentKey() === 'record';}, this)); this.isCustom = this.autoDispose(ko.computed(function() { - return this.viewModel.activeSection().parentKey() === 'custom';}, this)); -} -dispose.makeDisposable(ViewConfigTab); - - -ViewConfigTab.prototype.buildSortDom = function() { - return grainjsDom.maybe(this.activeSectionData, (sectionData) => { - const section = sectionData.section; - - // Computed to indicate if sort has changed from saved. - const hasChanged = Computed.create(null, (use) => - !isEqual(use(section.activeSortSpec), Sort.parseSortColRefs(use(section.sortColRefs)))); - - // Computed array of sortable columns. - const columns = Computed.create(null, (use) => { - // Columns is an observable holding an observable array - must call 'use' on it 2x. - const cols = use(use(use(section.table).columns)); - return cols.filter(col => !use(col.isHiddenCol)) - .map(col => ({ - label: use(col.colId), - value: col.getRowId(), - icon: 'FieldColumn', - type: col.type() - })); - }); - - // We only want to recreate rows, when the actual columns change. - const colRefs = Computed.create(null, (use) => { - return use(section.activeSortSpec).map(col => Sort.getColRef(col)); - }); - const sortRows = koArray(colRefs.get()); - colRefs.addListener((curr, prev) => { - if (!isEqual(curr, prev)){ - sortRows.assign(curr); - } - }) + return this.viewModel.activeSection().parentKey() === 'custom';}, this)); + this.isRaw = this.autoDispose(ko.computed(function() { + return this.viewModel.activeSection().isRaw();}, this)); - // Sort row create function for each sort row in the draggableList. - const rowCreateFn = colRef => - this._buildSortRow(colRef, section.activeSortSpec, columns); - - // Reorder function called when sort rows are reordered via dragging. - const reorder = (...args) => { - const spec = Sort.reorderSortRefs(section.activeSortSpec.peek(), ...args); - this._saveSort(spec); - }; + this.activeRawSectionData = this.autoDispose(ko.computed(function() { + return self.isRaw() ? ViewSectionData.create(self.viewModel.activeSection()) : null; + })); - return grainjsDom('div', - grainjsDom.autoDispose(hasChanged), - grainjsDom.autoDispose(columns), - grainjsDom.autoDispose(colRefs), - grainjsDom.autoDispose(sortRows), - // Sort rows. - kf.draggableList(sortRows, rowCreateFn, { - reorder, - removeButton: false, - drag_indicator: cssDragger, - itemClass: cssDragRow.className - }), - // Add to sort btn & menu & fake sort row. - this._buildAddToSortBtn(columns), - // Update/save/reset buttons visible when the sort has changed. - cssRow( - cssExtraMarginTop.cls(''), - grainjsDom.maybe(hasChanged, () => [ - primaryButton(t('Save'), {style: 'margin-right: 8px;'}, - grainjsDom.on('click', () => { section.activeSortJson.save(); }), - testId('sort-save'), - grainjsDom.boolAttr('disabled', this.gristDoc.isReadonly), - ), - // Let's use same label (revert) as the similar button which appear in the view section. - // menu. - basicButton(t('Revert'), - grainjsDom.on('click', () => { section.activeSortJson.revert(); }), - testId('sort-reset') - ) - ]), - cssFlex(), - grainjsDom.maybe(section.isSorted, () => - basicButton(t('UpdateData'), {style: 'margin-left: 8px; white-space: nowrap;'}, - grainjsDom.on('click', () => { updatePositions(this.gristDoc, section); }), - testId('sort-update'), - grainjsDom.show((use) => use(use(section.table).supportsManualSort)), - grainjsDom.boolAttr('disabled', this.gristDoc.isReadonly), - ) - ), - grainjsDom.show((use) => use(hasChanged) || use(section.isSorted)) - ), - testId('sort-menu') + this.activeSectionData = this.autoDispose(ko.computed(function() { + return ( + _.find(self.viewSectionData.all(), function(sectionData) { + return sectionData.section && + sectionData.section.getRowId() === self.viewModel.activeSectionId(); + }) + || self.activeRawSectionData() + || self.viewSectionData.at(0) ); - }); -}; - -// Builds a single row of the sort dom -// Takes the colRef, current sortSpec and array of column select options to show -// in the column select dropdown. -ViewConfigTab.prototype._buildSortRow = function(colRef, sortSpec, columns) { - const holder = new MultiHolder(); - - const col = Computed.create(holder, () => colRef); - const details = Computed.create(holder, (use) => Sort.specToDetails(Sort.findCol(use(sortSpec), colRef))); - const hasSpecs = Computed.create(holder, details, (_, details) => Sort.hasOptions(details)); - const isAscending = Computed.create(holder, details, (_, details) => details.direction === Sort.ASC); - - col.onWrite((newRef) => { - let specs = sortSpec.peek(); - const colSpec = Sort.findCol(specs, colRef); - const newSpec = Sort.findCol(specs, newRef); - if (newSpec) { - // this column is already there so only swap order - specs = Sort.swap(specs, colRef, newRef); - // but keep the directions - specs = Sort.setSortDirection(specs, colRef, Sort.direction(newSpec)) - specs = Sort.setSortDirection(specs, newRef, Sort.direction(colSpec)) - } else { - specs = Sort.replace(specs, colRef, Sort.createColSpec(newRef, Sort.direction(colSpec))); - } - this._saveSort(specs); - }); - - const computedFlag = (flag, allowedTypes, label) => { - const computed = Computed.create(holder, details, (_, details) => details[flag] || false); - computed.onWrite(value => { - const specs = sortSpec.peek(); - // Get existing details - const details = Sort.specToDetails(Sort.findCol(specs, colRef)); - // Update flags - details[flag] = value; - // Replace the colSpec at the index - this._saveSort(Sort.replace(specs, Sort.getColRef(colRef), details)); - }); - return {computed, allowedTypes, flag, label}; - } - const orderByChoice = computedFlag('orderByChoice', ['Choice'], t('UseChoicePosition')); - const naturalSort = computedFlag('naturalSort', ['Text'], t('NaturalSort')); - const emptyLast = computedFlag('emptyLast', null, t('EmptyValuesLast')); - const flags = [orderByChoice, emptyLast, naturalSort]; - - const column = columns.get().find(col => col.value === Sort.getColRef(colRef)); + })); +} +dispose.makeDisposable(ViewConfigTab); - return cssSortRow( - grainjsDom.autoDispose(holder), - cssSortSelect( - select(col, columns) - ), - // Use domComputed method for this icon, for dynamic testId, otherwise - // we are not able add it dynamically. - grainjsDom.domComputed(isAscending, isAscending => - cssSortIconPrimaryBtn( - "Sort", - grainjsDom.style("transform", isAscending ? "scaleY(-1)" : "none"), - grainjsDom.on("click", () => { - this._saveSort(Sort.flipSort(sortSpec.peek(), colRef)); - }), - testId("sort-order"), - testId(isAscending ? "sort-order-asc" : "sort-order-desc") - ) - ), - cssSortIconBtn('Remove', - grainjsDom.on('click', () => { - const specs = sortSpec.peek(); - if (Sort.findCol(specs, colRef)) { - this._saveSort(Sort.removeCol(specs, colRef)); - } - }), - testId('sort-remove') - ), - cssMenu( - cssBigIconWrapper( - cssIcon('Dots', grainjsDom.cls(cssBgAccent.className, hasSpecs)), - testId('sort-options-icon'), - ), - menu(_ctl => flags.map(({computed, allowedTypes, flag, label}) => { - // when allowedTypes is null, flag can be used for every column - const enabled = !allowedTypes || allowedTypes.includes(column.type); - return cssMenuItem( - labeledLeftSquareCheckbox( - computed, - label, - grainjsDom.prop('disabled', !enabled), - ), - grainjsDom.cls(cssOptionMenuItem.className), - grainjsDom.cls('disabled', !enabled), - testId('sort-option'), - testId(`sort-option-${flag}`), - ); - }, - )) - ), - testId('sort-row') - ); -}; -// Build the button to open the menu to add a sort item to the sort dom. -// Takes the full array of sortable column select options. -ViewConfigTab.prototype._buildAddToSortBtn = function(columns) { - // Observable indicating whether the add new column row is visible. - const showAddNew = Observable.create(null, false); - const available = Computed.create(null, (use) => { - const currentSection = use(this.activeSectionData).section; - const currentSortSpec = use(currentSection.activeSortSpec); - const specRowIds = new Set(currentSortSpec.map(_sortRef => Sort.getColRef(_sortRef))); - return use(columns) - .filter(_col => !specRowIds.has(_col.value)) +ViewConfigTab.prototype.buildSortFilterDom = function() { + return grainjsDom.maybe(this.activeSectionData, ({section}) => { + return grainjsDom.create(SortFilterConfig, section, this.gristDoc); }); - return [ - // Add column button. - cssRow( - grainjsDom.autoDispose(showAddNew), - grainjsDom.autoDispose(available), - cssTextBtn( - cssPlusIcon('Plus'), t('AddColumn'), - testId('sort-add') - ), - grainjsDom.hide((use) => use(showAddNew) || !use(available).length), - grainjsDom.on('click', () => { showAddNew.set(true); }), - ), - // Fake add column row that appears only when the menu is open to select a new column - // to add to the sort. Immediately destroyed when menu is closed. - grainjsDom.maybe((use) => use(showAddNew) && use(available), _columns => { - const col = Observable.create(null, 0); - const currentSection = this.activeSectionData().section; - // Function called when a column select value is clicked. - const onClick = (_col) => { - showAddNew.set(false); // Remove add row ASAP to prevent flickering - addToSort(currentSection.activeSortSpec, _col.value, 1); - }; - const menuCols = _columns.map(_col => - menuItem(() => onClick(_col), - cssMenuIcon(_col.icon), - _col.label, - testId('sort-add-menu-row') - ) - ); - return cssRow(cssSortRow( - dom.autoDispose(col), - cssSortSelect( - select(col, [], {defaultLabel: t('AddColumn')}), - menu(() => [ - menuCols, - grainjsDom.onDispose(() => { showAddNew.set(false); }) - ], { - // Trigger to make menu open immediately - trigger: [(elem, ctl) => { - ctl.open(); - grainjsDom.onElem(elem, 'click', () => { ctl.close(); }); - }], - stretchToSelector: `.${cssSortSelect.className}` - }) - ), - cssSortIconPrimaryBtn('Sort', - grainjsDom.style('transform', 'scaleY(-1)') - ), - cssSortIconBtn('Remove'), - cssBigIconWrapper(cssIcon('Dots')), - )); - }) - ]; -}; - -ViewConfigTab.prototype._saveSort = function(sortSpec) { - this.activeSectionData().section.activeSortSpec(sortSpec); }; ViewConfigTab.prototype._makeOnDemand = function(table) { @@ -394,90 +143,6 @@ ViewConfigTab.prototype._buildAdvancedSettingsDom = function() { }); }; -ViewConfigTab.prototype._buildFilterDom = function() { - return grainjsDom.maybe(this.activeSectionData, (sectionData) => { - const section = sectionData.section; - const docModel = this.gristDoc.docModel; - const popupControls = new WeakMap(); - const activeFilterBar = section.activeFilterBar; - - const hasChangedObs = Computed.create(null, (use) => use(section.filterSpecChanged) || !use(section.activeFilterBar.isSaved)) - - async function save() { - await docModel.docData.bundleActions(t("UpdateFilterSettings"), () => Promise.all([ - section.saveFilters(), // Save filter - section.activeFilterBar.save(), // Save bar - ])); - } - function revert() { - section.revertFilters(); // Revert filter - section.activeFilterBar.revert(); // Revert bar - } - - return [ - grainjsDom.forEach(section.activeFilters, (filterInfo) => { - return cssRow( - cssIconWrapper( - cssFilterIcon('FilterSimple', cssNoMarginLeft.cls('')), - attachColumnFilterMenu(section, filterInfo, { - placement: 'bottom-end', attach: 'body', - trigger: [ - 'click', - (_el, popupControl) => popupControls.set(filterInfo.fieldOrColumn.origCol(), popupControl) - ], - }), - ), - cssLabel(grainjsDom.text(filterInfo.fieldOrColumn.label)), - cssIconWrapper( - cssFilterIcon('Remove', - dom.on('click', () => section.setFilter(filterInfo.fieldOrColumn.origCol().origColRef(), '')), - testId('remove-filter') - ), - ), - testId('filter'), - ); - }), - cssRow( - grainjsDom.domComputed((use) => { - const filters = use(section.filters); - return cssTextBtn( - cssPlusIcon('Plus'), t('AddFilter'), - addFilterMenu(filters, section, popupControls, {placement: 'bottom-end'}), - testId('add-filter-btn'), - ); - }), - ), - grainjsDom.maybe((use) => !use(section.isRaw), - () => cssRow(cssTextBtn( - testId('toggle-filter-bar'), - grainjsDom.domComputed((use) => { - const filterBar = use(activeFilterBar); - return cssPlusIcon( - filterBar ? "Tick" : "Plus", - cssIcon.cls('-green', Boolean(filterBar)), - testId('toggle-filter-bar-icon'), - ); - }), - grainjsDom.on('click', () => activeFilterBar(!activeFilterBar.peek())), - 'Toggle Filter Bar', - ))), - grainjsDom.maybe(hasChangedObs, () => cssRow( - cssExtraMarginTop.cls(''), - testId('save-filter-btns'), - primaryButton( - t('Save'), {style: 'margin-right: 8px'}, - grainjsDom.on('click', save), - grainjsDom.boolAttr('disabled', this.gristDoc.isReadonly), - ), - basicButton( - t('Revert'), - grainjsDom.on('click', revert), - ) - )) - ]; - }); -}; - ViewConfigTab.prototype._buildThemeDom = function() { return kd.maybe(this.activeSectionData, (sectionData) => { var section = sectionData.section; @@ -570,132 +235,4 @@ ViewConfigTab.prototype._buildCustomTypeItems = function() { }]; }; -const cssMenuIcon = styled(cssIcon, ` - margin: 0 8px 0 0; - - .${cssMenuItem.className}-sel > & { - background-color: ${theme.iconButtonFg}; - } -`); - -// Note that the width is set to 0 so that flex-shrink works properly with long text values. -const cssSortSelect = styled('div', ` - flex: 1 1 0px; - margin: 0 6px 0 0; - min-width: 0; -`); - -const cssSortIconBtn = styled(cssIcon, ` - flex: none; - margin: 0 6px; - cursor: pointer; - background-color: ${theme.controlSecondaryFg}; - - &:hover { - background-color: ${theme.controlSecondaryHoverFg}; - } -`); - -const cssSortIconPrimaryBtn = styled(cssSortIconBtn, ` - background-color: ${theme.controlFg}; - - &:hover { - background-color: ${theme.controlHoverFg}; - } -`); - -const cssTextBtn = styled('div', ` - color: ${theme.controlFg}; - cursor: pointer; - - &:hover { - color: ${theme.controlHoverFg}; - } -`); - -const cssPlusIcon = styled(cssIcon, ` - background-color: ${theme.controlFg}; - cursor: pointer; - margin: 0px 4px 3px 0; - - .${cssTextBtn.className}:hover > & { - background-color: ${theme.controlHoverFg}; - } -`); - -const cssDragRow = styled('div', ` - display: flex !important; - align-items: center; - margin: 0 16px 0px 0px; - & > .kf_draggable_content { - margin: 6px 0; - flex: 1 1 0px; - min-width: 0px; - } -`); - -const cssSortRow = styled('div', ` - display: flex; - align-items: center; - width: 100%; -`); - -const cssFlex = styled('div', ` - flex: 1 1 0; -`); - -const cssLabel = styled('div', ` - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - flex-grow: 1; -`); - -const cssExtraMarginTop = styled('div', ` - margin-top: 28px; -`); - -const cssFilterIcon = cssSortIconBtn; - -const cssNoMarginLeft = styled('div', ` - margin-left: 0; -`); - -const cssIconWrapper = styled('div', ``); - -const cssBigIconWrapper = styled('div', ` - padding: 3px; - border-radius: 3px; - cursor: pointer; - user-select: none; -`); - -const cssMenu = styled('div', ` - display: inline-flex; - cursor: pointer; - border-radius: 3px; - border: 1px solid transparent; - &:hover, &.weasel-popup-open { - background-color: ${theme.hover}; - } -`); - -const cssBgAccent = styled(`div`, ` - background: ${theme.accentIcon} -`) - -const cssOptionMenuItem = styled('div', ` - &:hover { - background-color: ${theme.hover}; - } - & label { - flex: 1; - cursor: pointer; - } - &.disabled * { - color: ${theme.menuItemDisabledFg} important; - cursor: not-allowed; - } -`) - module.exports = ViewConfigTab; diff --git a/app/client/components/ViewLayout.ts b/app/client/components/ViewLayout.ts index 0a3af1f4..c27719f1 100644 --- a/app/client/components/ViewLayout.ts +++ b/app/client/components/ViewLayout.ts @@ -155,6 +155,7 @@ export class ViewLayout extends DisposableWithEvents implements IDomComponent { nextSection: () => { this._otherSection(+1); }, prevSection: () => { this._otherSection(-1); }, printSection: () => { printViewSection(this._layout, this.viewModel.activeSection()).catch(reportError); }, + sortFilterMenuOpen: (sectionId?: number) => { this._openSortFilterMenu(sectionId); }, }; this.autoDispose(commands.createGroup(commandGroup, this, true)); } @@ -267,6 +268,20 @@ export class ViewLayout extends DisposableWithEvents implements IDomComponent { this.gristDoc.viewModel.activeSectionId(layoutBox.leafId.peek()); } } + + /** + * Opens the sort and filter menu of the active view section. + * + * Optionally accepts a `sectionId` for opening a specific section's menu. + */ + private _openSortFilterMenu(sectionId?: number) { + const id = sectionId ?? this.viewModel.activeSectionId(); + const leafBoxDom = this._layout.getLeafBox(id)?.dom; + if (!leafBoxDom) { return; } + + const menu: HTMLElement | null = leafBoxDom.querySelector('.test-section-menu-sortAndFilter'); + menu?.click(); + } } export function buildViewSectionDom(options: { @@ -305,11 +320,10 @@ export function buildViewSectionDom(options: { buildWidgetTitle(vs, options, testId('viewsection-title'), cssTestClick(testId("viewsection-blank"))), viewInstance.buildTitleControls(), dom('span.viewsection_buttons', - dom.create(viewSectionMenu, gristDoc.docModel, vs, gristDoc.isReadonly) + dom.create(viewSectionMenu, gristDoc, vs) ) )), - dom.maybe((use) => use(vs.activeFilterBar) || use(vs.isRaw) && use(vs.activeFilters).length, - () => dom.create(filterBar, vs)), + dom.create(filterBar, vs), dom.maybe(vs.viewInstance, (viewInstance) => [ dom('div.view_data_pane_container.flexvbox', cssResizing.cls('', isResizing), diff --git a/app/client/components/commandList.js b/app/client/components/commandList.js index 3d07f1e5..22a4c309 100644 --- a/app/client/components/commandList.js +++ b/app/client/components/commandList.js @@ -66,11 +66,6 @@ exports.groups = [{ keys: ['Esc'], desc: null, // Shortcut to close active menu }, - { - name: 'filterMenuOpen', - keys: [], - desc: 'Shortcut to open filter menu' - }, { name: 'docTabOpen', keys: [], @@ -91,6 +86,11 @@ exports.groups = [{ keys: [], desc: 'Shortcut to sort & filter tab' }, + { + name: 'sortFilterMenuOpen', + keys: [], + desc: 'Shortcut to open sort & filter menu' + }, { name: 'dataSelectionTabOpen', keys: [], diff --git a/app/client/declarations.d.ts b/app/client/declarations.d.ts index 3be3229e..f44b488f 100644 --- a/app/client/declarations.d.ts +++ b/app/client/declarations.d.ts @@ -39,6 +39,7 @@ declare module "app/client/components/BaseView" { import {ViewFieldRec, ViewSectionRec} from "app/client/models/DocModel"; import {FilterInfo} from 'app/client/models/entities/ViewSectionRec'; import {SortedRowSet} from 'app/client/models/rowset'; + import {IColumnFilterMenuOptions} from 'app/client/ui/ColumnFilterMenu'; import {FieldBuilder} from "app/client/widgets/FieldBuilder"; import {DomArg} from 'grainjs'; import {IOpenController} from 'popweasel'; @@ -66,7 +67,8 @@ declare module "app/client/components/BaseView" { constructor(gristDoc: GristDoc, viewSectionModel: any, options?: {addNewRow?: boolean, isPreview?: boolean}); public setCursorPos(cursorPos: CursorPos): void; - public createFilterMenu(ctl: IOpenController, filterInfo: FilterInfo, onClose?: () => void): HTMLElement; + public createFilterMenu(ctl: IOpenController, filterInfo: FilterInfo, + options?: IColumnFilterMenuOptions): HTMLElement; public buildTitleControls(): DomArg; public getLoadingDonePromise(): Promise; public activateEditorAtCursor(options?: Options): void; @@ -95,10 +97,9 @@ declare module "app/client/components/ViewConfigTab" { class ViewConfigTab extends Disposable { constructor(options: {gristDoc: GristDoc, viewModel: ViewRec}); - public buildSortDom(): DomContents; + public buildSortFilterDom(): DomContents; // TODO: these should be made private or renamed. public _buildAdvancedSettingsDom(): DomArg; - public _buildFilterDom(): DomArg; public _buildThemeDom(): DomArg; public _buildChartConfigDom(): DomContents; public _buildLayoutDom(): DomArg; diff --git a/app/client/models/ColumnFilter.ts b/app/client/models/ColumnFilter.ts index e91fedc5..d30dac48 100644 --- a/app/client/models/ColumnFilter.ts +++ b/app/client/models/ColumnFilter.ts @@ -40,6 +40,10 @@ export class ColumnFilter extends Disposable { return this._columnType; } + public get initialFilterJson() { + return this._initialFilterJson; + } + public setState(filterJson: string|FilterSpec) { const state = makeFilterState(filterJson); if (isRangeFilter(state)) { @@ -138,4 +142,18 @@ export class ColumnFilter extends Disposable { } } -export const allInclusive = '{"excluded":[]}'; +/** + * A JSON-encoded filter spec that includes every value. + */ +export const ALL_INCLUSIVE_FILTER_JSON = '{"excluded":[]}'; + +/** + * A blank JSON-encoded filter spec. + * + * This is interpreted the same as `ALL_INCLUSIVE_FILTER_JSON` in the context + * of parsing filters. However, it's still useful in scenarios where it's + * necessary to discern between new filters and existing filters; initializing + * a `ColumnFilter` with `NEW_FIlTER_JSON` makes it clear that a new filter + * is being created. + */ +export const NEW_FILTER_JSON = '{}'; diff --git a/app/client/models/ColumnFilterMenuModel.ts b/app/client/models/ColumnFilterMenuModel.ts index e6193b61..fffdcd33 100644 --- a/app/client/models/ColumnFilterMenuModel.ts +++ b/app/client/models/ColumnFilterMenuModel.ts @@ -1,4 +1,5 @@ import { ColumnFilter } from "app/client/models/ColumnFilter"; +import { FilterInfo } from "app/client/models/entities/ViewSectionRec"; import { CellValue } from "app/plugin/GristData"; import { Computed, Disposable, Observable } from "grainjs"; import escapeRegExp = require("lodash/escapeRegExp"); @@ -23,7 +24,21 @@ type ICompare = (a: T, b: T) => number const localeCompare = new Intl.Collator('en-US', {numeric: true}).compare; +interface ColumnFilterMenuModelParams { + columnFilter: ColumnFilter; + filterInfo: FilterInfo; + valueCount: Array<[CellValue, IFilterCount]>; + limitShow?: number; +} + export class ColumnFilterMenuModel extends Disposable { + public readonly columnFilter = this._params.columnFilter; + + public readonly filterInfo = this._params.filterInfo; + + public readonly initialPinned = this.filterInfo.isPinned.peek(); + + public readonly limitShown = this._params.limitShow ?? MAXIMUM_SHOWN_FILTER_ITEMS; public readonly searchValue = Observable.create(this, ''); @@ -34,7 +49,7 @@ export class ColumnFilterMenuModel extends Disposable { const searchRegex = new RegExp(escapeRegExp(searchValue), 'i'); const showAllOptions = ['Bool', 'Choice', 'ChoiceList'].includes(this.columnFilter.columnType); return new Set( - this._valueCount + this._params.valueCount .filter(([_, {label, count}]) => (showAllOptions ? true : count) && searchRegex.test(label)) .map(([key]) => key) ); @@ -56,7 +71,7 @@ export class ColumnFilterMenuModel extends Disposable { return localeCompare(a, b); }; - return this._valueCount + return this._params.valueCount .filter(([key]) => filter.has(key)) .sort((a, b) => comparator(a[1][prop], b[1][prop])); } @@ -64,12 +79,12 @@ export class ColumnFilterMenuModel extends Disposable { // computes the array of all values that does NOT matches the search text public readonly otherValues = Computed.create(this, this.filterSet, (_use, filter) => { - return this._valueCount.filter(([key]) => !filter.has(key)); + return this._params.valueCount.filter(([key]) => !filter.has(key)); }); // computes the array of keys that matches the search text public readonly filteredKeys = Computed.create(this, this.filterSet, (_use, filter) => { - return this._valueCount + return this._params.valueCount .filter(([key]) => filter.has(key)) .map(([key]) => key); }); @@ -78,8 +93,7 @@ export class ColumnFilterMenuModel extends Disposable { return filteredValues.slice(this.limitShown); }); - constructor(public columnFilter: ColumnFilter, private _valueCount: Array<[CellValue, IFilterCount]>, - public limitShown: number = MAXIMUM_SHOWN_FILTER_ITEMS) { + constructor(private _params: ColumnFilterMenuModelParams) { super(); } } diff --git a/app/client/models/entities/ViewSectionRec.ts b/app/client/models/entities/ViewSectionRec.ts index f597ab5c..7da67ebb 100644 --- a/app/client/models/entities/ViewSectionRec.ts +++ b/app/client/models/entities/ViewSectionRec.ts @@ -19,6 +19,7 @@ import {RowId} from 'app/client/models/rowset'; import {LinkConfig} from 'app/client/ui/selectBy'; import {getWidgetTypes} from 'app/client/ui/widgetTypes'; import {AccessLevel, ICustomWidget} from 'app/common/CustomWidget'; +import {UserAction} from 'app/common/DocActions'; import {arrayRepeat} from 'app/common/gutil'; import {Sort} from 'app/common/SortSpec'; import {ColumnsToMap, WidgetColumnMap} from 'app/plugin/CustomSectionAPI'; @@ -72,7 +73,7 @@ export interface ViewSectionRec extends IRowModel<"_grist_Views_section">, RuleO * * NOTE: See `filters`, where `_unsavedFilters` is merged with `savedFilters`. */ - _unsavedFilters: Map; + _unsavedFilters: Map>; /** * Filter information for all fields/section in the section. @@ -86,6 +87,9 @@ export interface ViewSectionRec extends IRowModel<"_grist_Views_section">, RuleO // Subset of `filters` containing non-blank active filters. activeFilters: Computed; + // Subset of `activeFilters` that are pinned. + pinnedActiveFilters: Computed; + // Helper metadata item which indicates whether any of the section's fields/columns have unsaved // changes to their filters. (True indicates unsaved changes) filterSpecChanged: Computed; @@ -146,7 +150,6 @@ export interface ViewSectionRec extends IRowModel<"_grist_Views_section">, RuleO isSorted: ko.Computed; disableDragRows: ko.Computed; - activeFilterBar: modelUtil.CustomComputed; // Number of frozen columns rawNumFrozen: modelUtil.CustomComputed; // Number for frozen columns to display. @@ -191,8 +194,11 @@ export interface ViewSectionRec extends IRowModel<"_grist_Views_section">, RuleO // Revert all filters of fields/columns in the section. revertFilters(): void; - // Apply `filter` to the field or column identified by `colRef`. - setFilter(colRef: number, filter: string): void; + // Set `filter` for the field or column identified by `colRef`. + setFilter(colRef: number, filter: Partial): void; + + // Revert the filter of the field or column identified by `colRef`. + revertFilter(colRef: number): void; // Saves custom definition (bundles change) saveCustomDef(): Promise; @@ -236,14 +242,25 @@ export interface CustomViewSectionDef { sectionId: modelUtil.KoSaveableObservable; } -// Information about filters for a field or hidden column. +/** Information about filters for a field or hidden column. */ export interface FilterInfo { - // The field or column associated with this filter info (field if column is visible, else column). + /** The section that's being filtered. */ + viewSection: ViewSectionRec; + /** The field or column that's being filtered. (Field if column is visible.) */ fieldOrColumn: ViewFieldRec|ColumnRec; - // Filter that applies to this field/column, if any. + /** Filter that applies to this field/column, if any. */ filter: modelUtil.CustomComputed; - // True if `filter` has a non-blank value. + /** Whether this filter is pinned to the filter bar. */ + pinned: modelUtil.CustomComputed; + /** True if `filter` has a non-blank value. */ isFiltered: ko.PureComputed; + /** True if `pinned` is true. */ + isPinned: ko.PureComputed; +} + +export interface Filter { + filter: string; + pinned: boolean; } export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel): void { @@ -262,7 +279,6 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel): horizontalGridlines: true, zebraStripes: false, customView: '', - filterBar: false, numFrozen: 0 }; this.optionsObj = modelUtil.jsonObservable(this.options, @@ -365,7 +381,7 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel): this._unsavedFilters = new Map(); /** - * Filter information for all fields/section in the section. + * Filter information for all fields/columns in the section. * * Re-computed on changes to `savedFilters`, as well as any changes to `viewFields` or `columns`. Any * unsaved filters saved in `_unsavedFilters` are applied on computation, taking priority over saved @@ -377,30 +393,43 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel): return this.columns().map(column => { const savedFilter = savedFiltersByColRef.get(column.origColRef()); + // Initialize with a saved filter, if one exists. Otherwise, use a blank filter. const filter = modelUtil.customComputed({ - // Initialize with a saved filter, if one exists. Otherwise, use a blank filter. read: () => { return savedFilter ? savedFilter.activeFilter() : ''; }, }); + const pinned = modelUtil.customComputed({ + read: () => { return savedFilter ? savedFilter.pinned() : false; }, + }); - // If an unsaved filter exists, overwrite `filter` with it. + // If an unsaved filter exists, overwrite the filter with it. const unsavedFilter = this._unsavedFilters.get(column.origColRef()); - if (unsavedFilter !== undefined) { filter(unsavedFilter); } + if (unsavedFilter) { + const {filter: f, pinned: p} = unsavedFilter; + if (f !== undefined) { filter(f); } + if (p !== undefined) { pinned(p); } + } return { + viewSection: this, filter, + pinned, fieldOrColumn: viewFieldsByColRef.get(column.origColRef()) ?? column, - isFiltered: ko.pureComputed(() => filter() !== '') + isFiltered: ko.pureComputed(() => filter() !== ''), + isPinned: ko.pureComputed(() => pinned()), }; }); })); // List of `filters` that have non-blank active filters. - this.activeFilters = Computed.create(this, use => use(this.filters).filter(col => use(col.isFiltered))); + this.activeFilters = Computed.create(this, use => use(this.filters).filter(f => use(f.isFiltered))); + + // List of `activeFilters` that are pinned. + this.pinnedActiveFilters = Computed.create(this, use => use(this.activeFilters).filter(f => use(f.isPinned))); // Helper metadata item which indicates whether any of the section's fields/columns have unsaved // changes to their filters. (True indicates unsaved changes) this.filterSpecChanged = Computed.create(this, use => { - return use(this.filters).some(col => !use(col.filter.isSaved)); + return use(this.filters).some(col => !use(col.filter.isSaved) || !use(col.pinned.isSaved)); }); // Save all filters of fields/columns in the section. @@ -408,52 +437,72 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel): return docModel.docData.bundleActions(`Save all filters in ${this.titleDef()}`, async () => { const savedFiltersByColRef = new Map(this._savedFilters().all().map(f => [f.colRef(), f])); - const updatedFilters: [number, string][] = []; // Pairs of row ids and filters to update. + const updatedFilters: [number, Filter][] = []; // Pairs of row ids and filters to update. const removedFilterIds: number[] = []; // Row ids of filters to remove. - const newFilters: [number, string][] = []; // Pairs of column refs and filters to add. + const newFilters: [number, Filter][] = []; // Pairs of column refs and filters to add. - for (const c of this.filters()) { + for (const f of this.filters()) { + const {fieldOrColumn, filter, pinned} = f; // Skip saved filters (i.e. filters whose local values are unchanged from server). - if (c.filter.isSaved()) { continue; } + if (filter.isSaved() && pinned.isSaved()) { continue; } - const savedFilter = savedFiltersByColRef.get(c.fieldOrColumn.origCol().origColRef()); + const savedFilter = savedFiltersByColRef.get(fieldOrColumn.origCol().origColRef()); if (!savedFilter) { + // Never save blank filters. (This is primarily a sanity check.) + if (filter() === '') { continue; } + // Since no saved filter exists, we must add a new record to the filters table. - newFilters.push([c.fieldOrColumn.origCol().origColRef(), c.filter()]); - } else if (c.filter() === '') { + newFilters.push([fieldOrColumn.origCol().origColRef(), { + filter: filter(), + pinned: pinned(), + }]); + } else if (filter() === '') { // Mark the saved filter for removal from the filters table. removedFilterIds.push(savedFilter.id()); } else { // Mark the saved filter for update in the filters table. - updatedFilters.push([savedFilter.id(), c.filter()]); + updatedFilters.push([savedFilter.id(), { + filter: filter(), + pinned: pinned(), + }]); } } + const actions: UserAction[] = []; + // Remove records of any deleted filters. if (removedFilterIds.length > 0) { - await docModel.filters.sendTableAction(['BulkRemoveRecord', removedFilterIds]); + actions.push(['BulkRemoveRecord', removedFilterIds]); } // Update existing filter records with new filter values. if (updatedFilters.length > 0) { - await docModel.filters.sendTableAction(['BulkUpdateRecord', + actions.push(['BulkUpdateRecord', updatedFilters.map(([id]) => id), - {filter: updatedFilters.map(([, filter]) => filter)} + { + filter: updatedFilters.map(([, {filter}]) => filter), + pinned: updatedFilters.map(([, {pinned}]) => pinned), + } ]); } // Add new filter records. if (newFilters.length > 0) { - await docModel.filters.sendTableAction(['BulkAddRecord', + actions.push(['BulkAddRecord', arrayRepeat(newFilters.length, null), { viewSectionRef: arrayRepeat(newFilters.length, this.id()), colRef: newFilters.map(([colRef]) => colRef), - filter: newFilters.map(([, filter]) => filter), + filter: newFilters.map(([, {filter}]) => filter), + pinned: newFilters.map(([, {pinned}]) => pinned), } ]); } + if (actions.length > 0) { + await docModel.filters.sendTableActions(actions); + } + // Reset client filter state. this.revertFilters(); } @@ -462,15 +511,32 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel): // Revert all filters of fields/columns in the section. this.revertFilters = () => { - this._unsavedFilters = new Map(); - this.filters().forEach(c => { c.filter.revert(); }); + this._unsavedFilters.clear(); + this.filters().forEach(c => { + c.filter.revert(); + c.pinned.revert(); + }); + }; + + // Set `filter` for the field or column identified by `colRef`. + this.setFilter = (colRef: number, filter: Partial) => { + this._unsavedFilters.set(colRef, {...this._unsavedFilters.get(colRef), ...filter}); + const filterInfo = this.filters().find(c => c.fieldOrColumn.origCol().origColRef() === colRef); + if (!filterInfo) { return; } + + const {filter: newFilter, pinned: newPinned} = filter; + if (newFilter !== undefined) { filterInfo.filter(newFilter); } + if (newPinned !== undefined) { filterInfo.pinned(newPinned); } }; - // Apply `filter` to the field or column identified by `colRef`. - this.setFilter = (colRef: number, filter: string) => { - this._unsavedFilters.set(colRef, filter); + // Revert the filter of the field or column identified by `colRef`. + this.revertFilter = (colRef: number) => { + this._unsavedFilters.delete(colRef); const filterInfo = this.filters().find(c => c.fieldOrColumn.origCol().origColRef() === colRef); - filterInfo?.filter(filter); + if (!filterInfo) { return; } + + filterInfo.filter.revert(); + filterInfo.pinned.revert(); }; // Customizable version of the JSON-stringified sort spec. It may diverge from the saved one. @@ -571,8 +637,6 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel): this.isSorted = ko.pureComputed(() => this.activeSortSpec().length > 0); this.disableDragRows = ko.pureComputed(() => this.isSorted() || !this.table().supportsManualSort()); - this.activeFilterBar = modelUtil.customValue(this.optionsObj.prop('filterBar')); - // Number of frozen columns this.rawNumFrozen = modelUtil.customValue(this.optionsObj.prop('numFrozen')); // Number for frozen columns to display diff --git a/app/client/ui/ColumnFilterMenu.ts b/app/client/ui/ColumnFilterMenu.ts index e3d03e10..5bb27d75 100644 --- a/app/client/ui/ColumnFilterMenu.ts +++ b/app/client/ui/ColumnFilterMenu.ts @@ -3,16 +3,18 @@ * callback that's triggered on Apply or on Cancel. Changes to the UI result in changes to the underlying model, * but on Cancel the model is reset to its initial state prior to menu closing. */ +import * as commands from 'app/client/components/commands'; import {makeT} from 'app/client/lib/localization'; -import {allInclusive, ColumnFilter} from 'app/client/models/ColumnFilter'; +import {ColumnFilter, NEW_FILTER_JSON} from 'app/client/models/ColumnFilter'; import {ColumnFilterMenuModel, IFilterCount} from 'app/client/models/ColumnFilterMenuModel'; -import {ColumnRec, ViewFieldRec, ViewSectionRec} from 'app/client/models/DocModel'; +import {ColumnRec, ViewFieldRec} from 'app/client/models/DocModel'; import {FilterInfo} from 'app/client/models/entities/ViewSectionRec'; import {RowId, RowSource} from 'app/client/models/rowset'; import {ColumnFilterFunc, SectionFilter} from 'app/client/models/SectionFilter'; import {TableData} from 'app/client/models/TableData'; import {cssInput} from 'app/client/ui/cssInput'; -import {basicButton, primaryButton} from 'app/client/ui2018/buttons'; +import {cssPinButton} from 'app/client/ui/RightPanelStyles'; +import {basicButton, primaryButton, textButton} from 'app/client/ui2018/buttons'; import {cssLabel as cssCheckboxLabel, cssCheckboxSquare, cssLabelText, Indeterminate, labeledTriStateSquareCheckbox } from 'app/client/ui2018/checkbox'; import {theme, vars} from 'app/client/ui2018/cssVars'; @@ -40,19 +42,24 @@ const t = makeT('ColumnFilterMenu'); export interface IFilterMenuOptions { model: ColumnFilterMenuModel; valueCounts: Map; - doSave: (reset: boolean) => void; - onClose: () => void; - renderValue: (key: CellValue, value: IFilterCount) => DomElementArg; - rangeInputOptions?: IRangeInputOptions + rangeInputOptions?: IRangeInputOptions; + showAllFiltersButton?: boolean; + doCancel(): void; + doSave(reset: boolean): void; + renderValue(key: CellValue, value: IFilterCount): DomElementArg; + onClose(): void; } const testId = makeTestId('test-filter-menu-'); +/** + * Returns the DOM content for the column filter menu. + * + * For use with setPopupToCreateDom(). + */ export function columnFilterMenu(owner: IDisposableOwner, opts: IFilterMenuOptions): HTMLElement { - const { model, doSave, onClose, rangeInputOptions = {}, renderValue } = opts; - const { columnFilter } = model; - // Save the initial state to allow reverting back to it on Cancel - const initialStateJson = columnFilter.makeFilterJson(); + const { model, doCancel, doSave, onClose, rangeInputOptions = {}, renderValue, showAllFiltersButton } = opts; + const { columnFilter, filterInfo } = model; // Map to keep track of displayed checkboxes const checkboxMap: Map = new Map(); @@ -74,6 +81,7 @@ export function columnFilterMenu(owner: IDisposableOwner, opts: IFilterMenuOptio let searchInput: HTMLInputElement; let minRangeInput: HTMLInputElement; + let cancel = false; let reset = false; // Gives focus to the searchInput on open (or to the min input if the range filter is present). @@ -84,7 +92,8 @@ export function columnFilterMenu(owner: IDisposableOwner, opts: IFilterMenuOptio testId('wrapper'), dom.cls(menuCssClass), dom.autoDispose(filterListener), - dom.onDispose(() => doSave(reset)), // Save on disposal, which should always happen as part of closing. + // Save or cancel on disposal, which should always happen as part of closing. + dom.onDispose(() => cancel ? doCancel() : doSave(reset)), dom.onKeyDown({ Enter: () => onClose(), Escape: () => onClose() @@ -205,13 +214,39 @@ export function columnFilterMenu(owner: IDisposableOwner, opts: IFilterMenuOptio ]; } }), - cssMenuItem( - cssApplyButton('Apply', testId('apply-btn'), - dom.on('click', () => { reset = true; onClose(); })), - basicButton('Cancel', testId('cancel-btn'), - dom.on('click', () => { columnFilter.setState(initialStateJson); onClose(); } )) - ) - ) + cssFooterButtons( + dom('div', + cssPrimaryButton('Close', testId('apply-btn'), + dom.on('click', () => { + reset = true; + onClose(); + }), + ), + basicButton('Cancel', testId('cancel-btn'), + dom.on('click', () => { + cancel = true; + onClose(); + }), + ), + !showAllFiltersButton ? null : cssAllFiltersButton( + 'All filters', + dom.on('click', () => { + onClose(); + commands.allCommands.sortFilterMenuOpen.run(filterInfo.viewSection.getRowId()); + }), + testId('all-filters-btn'), + ), + ), + dom('div', + cssPinButton( + icon('PinTilted'), + cssPinButton.cls('-pinned', model.filterInfo.isPinned), + dom.on('click', () => filterInfo.pinned(!filterInfo.pinned())), + testId('pin-btn'), + ), + ), + ), + ), ); return filterMenu; } @@ -350,17 +385,31 @@ function getEmptyCountMap(fieldOrColumn: ViewFieldRec|ColumnRec): Map [v, {label: String(v), count: 0, displayValue: v}])); } +export interface IColumnFilterMenuOptions { + // Callback for when the filter menu is closed. + onClose?: () => void; + // If true, shows a button that opens the sort & filter widget menu. + showAllFiltersButton?: boolean; +} + /** * Returns content for the newly created columnFilterMenu; for use with setPopupToCreateDom(). */ -export function createFilterMenu(openCtl: IOpenController, sectionFilter: SectionFilter, filterInfo: FilterInfo, - rowSource: RowSource, tableData: TableData, onClose: () => void = noop) { +export function createFilterMenu( + openCtl: IOpenController, + sectionFilter: SectionFilter, + filterInfo: FilterInfo, + rowSource: RowSource, + tableData: TableData, + options: IColumnFilterMenuOptions = {} +) { + const {onClose = noop, showAllFiltersButton} = options; + // Go through all of our shown and hidden rows, and count them up by the values in this column. - const fieldOrColumn = filterInfo.fieldOrColumn; + const {fieldOrColumn, filter} = filterInfo; const columnType = fieldOrColumn.origCol.peek().type.peek(); const visibleColumnType = fieldOrColumn.visibleColModel.peek()?.type.peek() || columnType; - const {keyMapFunc, labelMapFunc, valueMapFunc} = getMapFuncs(columnType, tableData, filterInfo.fieldOrColumn); - const activeFilterBar = sectionFilter.viewSection.activeFilterBar; + const {keyMapFunc, labelMapFunc, valueMapFunc} = getMapFuncs(columnType, tableData, fieldOrColumn); // range input options const valueParser = (fieldOrColumn as any).createValueParser?.(); @@ -387,10 +436,14 @@ export function createFilterMenu(openCtl: IOpenController, sectionFilter: Sectio areHiddenRows: true, valueMapFunc}); const valueCountsArr = Array.from(valueCounts); - const columnFilter = ColumnFilter.create(openCtl, filterInfo.filter.peek(), columnType, visibleColumnType, + const columnFilter = ColumnFilter.create(openCtl, filter.peek(), columnType, visibleColumnType, valueCountsArr.map((arr) => arr[0])); sectionFilter.setFilterOverride(fieldOrColumn.origCol().getRowId(), columnFilter); // Will be removed on menu disposal - const model = ColumnFilterMenuModel.create(openCtl, columnFilter, valueCountsArr); + const model = ColumnFilterMenuModel.create(openCtl, { + columnFilter, + filterInfo, + valueCount: valueCountsArr, + }); return columnFilterMenu(openCtl, { model, @@ -398,20 +451,32 @@ export function createFilterMenu(openCtl: IOpenController, sectionFilter: Sectio onClose: () => { openCtl.close(); onClose(); }, doSave: (reset: boolean = false) => { const spec = columnFilter.makeFilterJson(); - // If filter is moot and filter bar is hidden, let's remove the filter. sectionFilter.viewSection.setFilter( fieldOrColumn.origCol().origColRef(), - spec === allInclusive && !activeFilterBar.peek() ? '' : spec + {filter: spec} ); if (reset) { sectionFilter.resetTemporaryRows(); } }, + doCancel: () => { + if (columnFilter.initialFilterJson === NEW_FILTER_JSON) { + sectionFilter.viewSection.revertFilter(fieldOrColumn.origCol().origColRef()); + } else { + const initialFilter = columnFilter.initialFilterJson; + columnFilter.setState(initialFilter); + sectionFilter.viewSection.setFilter( + fieldOrColumn.origCol().origColRef(), + {filter: initialFilter, pinned: model.initialPinned} + ); + } + }, renderValue: getRenderFunc(columnType, fieldOrColumn), rangeInputOptions: { valueParser, valueFormatter, - } + }, + showAllFiltersButton, }); } @@ -571,20 +636,25 @@ const defaultPopupOptions: IPopupOptions = { trigger: ['click'], }; -interface IColumnFilterMenuOptions extends IPopupOptions { - // callback for when the content of the menu is closed by clicking the apply or revert buttons - onCloseContent?: () => void; +interface IColumnFilterPopupOptions { + // Options to pass to the popup component. + popupOptions?: IPopupOptions; } +type IAttachColumnFilterMenuOptions = IColumnFilterPopupOptions & IColumnFilterMenuOptions; + // Helper to attach the column filter menu. -export function attachColumnFilterMenu(viewSection: ViewSectionRec, filterInfo: FilterInfo, - popupOptions: IColumnFilterMenuOptions): DomElementMethod { - const options = {...defaultPopupOptions, ...popupOptions}; +export function attachColumnFilterMenu( + filterInfo: FilterInfo, + options: IAttachColumnFilterMenuOptions = {} +): DomElementMethod { + const {popupOptions, ...filterMenuOptions} = options; + const popupOptionsWithDefaults = {...defaultPopupOptions, ...popupOptions}; return (elem) => { - const instance = viewSection.viewInstance(); + const instance = filterInfo.viewSection.viewInstance(); if (instance && instance.createFilterMenu) { // Should be set if using BaseView - setPopupToCreateDom(elem, ctl => - instance.createFilterMenu(ctl, filterInfo, popupOptions.onCloseContent), options); + setPopupToCreateDom(elem, ctl => instance.createFilterMenu( + ctl, filterInfo, filterMenuOptions), popupOptionsWithDefaults); } }; } @@ -654,8 +724,17 @@ const cssMenuFooter = styled('div', ` flex-direction: column; padding-top: 4px; `); -const cssApplyButton = styled(primaryButton, ` - margin-right: 4px; +const cssFooterButtons = styled('div', ` + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 16px; +`); +const cssPrimaryButton = styled(primaryButton, ` + margin-right: 8px; +`); +const cssAllFiltersButton = styled(textButton, ` + margin-left: 8px; `); const cssSearch = styled(input, ` color: ${theme.inputFg}; diff --git a/app/client/ui/FilterBar.ts b/app/client/ui/FilterBar.ts index a972836f..18afba06 100644 --- a/app/client/ui/FilterBar.ts +++ b/app/client/ui/FilterBar.ts @@ -1,58 +1,82 @@ -import { makeT } from "app/client/lib/localization"; -import { allInclusive } from "app/client/models/ColumnFilter"; -import { ColumnRec, ViewFieldRec, ViewSectionRec } from "app/client/models/DocModel"; +import { NEW_FILTER_JSON } from "app/client/models/ColumnFilter"; +import { ColumnRec, ViewSectionRec } from "app/client/models/DocModel"; import { FilterInfo } from "app/client/models/entities/ViewSectionRec"; import { attachColumnFilterMenu } from "app/client/ui/ColumnFilterMenu"; -import { cssButton, cssButtonGroup } from "app/client/ui2018/buttons"; -import { testId, theme } from "app/client/ui2018/cssVars"; +import { cssButton } from "app/client/ui2018/buttons"; +import { testId, theme, vars } from "app/client/ui2018/cssVars"; import { icon } from "app/client/ui2018/icons"; import { menu, menuItemAsync } from "app/client/ui2018/menus"; import { dom, IDisposableOwner, IDomArgs, styled } from "grainjs"; import { IMenuOptions, PopupControl } from "popweasel"; -const t = makeT('FilterBar'); - export function filterBar(_owner: IDisposableOwner, viewSection: ViewSectionRec) { const popupControls = new WeakMap(); return cssFilterBar( testId('filter-bar'), - dom.forEach(viewSection.activeFilters, (filterInfo) => makeFilterField(viewSection, filterInfo, popupControls)), + dom.forEach(viewSection.activeFilters, (filterInfo) => makeFilterField(filterInfo, popupControls)), makePlusButton(viewSection, popupControls), + cssFilterBar.cls('-hidden', use => use(viewSection.pinnedActiveFilters).length === 0), ); } -function makeFilterField(viewSection: ViewSectionRec, filterInfo: FilterInfo, - popupControls: WeakMap) { +function makeFilterField(filterInfo: FilterInfo, popupControls: WeakMap) { + const {fieldOrColumn, filter, pinned, isPinned} = filterInfo; return cssFilterBarItem( testId('filter-field'), primaryButton( testId('btn'), cssIcon('FilterSimple'), - cssMenuTextLabel(dom.text(filterInfo.fieldOrColumn.origCol().label)), - cssBtn.cls('-grayed', filterInfo.filter.isSaved), - attachColumnFilterMenu(viewSection, filterInfo, { - placement: 'bottom-start', attach: 'body', - trigger: ['click', (_el, popupControl) => popupControls.set(filterInfo.fieldOrColumn.origCol(), popupControl)] + cssMenuTextLabel(dom.text(fieldOrColumn.origCol().label)), + cssBtn.cls('-grayed', use => use(filter.isSaved) && use(pinned.isSaved)), + attachColumnFilterMenu(filterInfo, { + popupOptions: { + placement: 'bottom-start', + attach: 'body', + trigger: [ + 'click', + (_el, popupControl) => popupControls.set(fieldOrColumn.origCol(), popupControl), + ], + }, + showAllFiltersButton: true, }), ), - deleteButton( - testId('delete'), - cssIcon('CrossSmall'), - cssBtn.cls('-grayed', filterInfo.filter.isSaved), - dom.on('click', () => viewSection.setFilter(filterInfo.fieldOrColumn.origCol().origColRef(), '')), - ) + cssFilterBarItem.cls('-unpinned', use => !use(isPinned)), ); } -export function addFilterMenu(filters: FilterInfo[], viewSection: ViewSectionRec, - popupControls: WeakMap, options?: IMenuOptions) { +export interface AddFilterMenuOptions { + /** + * If 'only-unfiltered', only columns without active filters will be selectable in + * the menu. + * + * If 'unpinned-or-unfiltered', columns that have active filters but are not pinned + * will also be selectable. + * + * Defaults to `only-unfiltered'. + */ + allowedColumns?: 'only-unfiltered' | 'unpinned-or-unfiltered'; + /** + * Options that are passed to the menu component. + */ + menuOptions?: IMenuOptions; +} + +export function addFilterMenu( + filters: FilterInfo[], + popupControls: WeakMap, + options: AddFilterMenuOptions = {} +) { + const {allowedColumns, menuOptions} = options; return ( menu((ctl) => [ ...filters.map((filterInfo) => ( menuItemAsync( - () => turnOnAndOpenFilter(filterInfo.fieldOrColumn, viewSection, popupControls), + () => openFilter(filterInfo, popupControls), filterInfo.fieldOrColumn.origCol().label.peek(), - dom.cls('disabled', filterInfo.isFiltered), + dom.cls('disabled', allowedColumns === 'unpinned-or-unfiltered' + ? use => use(filterInfo.isPinned) && use(filterInfo.isFiltered) + : use => use(filterInfo.isFiltered) + ), testId('add-filter-item'), ) )), @@ -62,25 +86,30 @@ export function addFilterMenu(filters: FilterInfo[], viewSection: ViewSectionRec ctl.close(); ev.stopPropagation(); }), - ], options) + ], menuOptions) ); } -function turnOnAndOpenFilter(fieldOrColumn: ViewFieldRec|ColumnRec, viewSection: ViewSectionRec, - popupControls: WeakMap) { - viewSection.setFilter(fieldOrColumn.origCol().origColRef(), allInclusive); +function openFilter( + {fieldOrColumn, isFiltered, viewSection}: FilterInfo, + popupControls: WeakMap, +) { + viewSection.setFilter(fieldOrColumn.origCol().origColRef(), { + filter: isFiltered.peek() ? undefined : NEW_FILTER_JSON, + pinned: true, + }); popupControls.get(fieldOrColumn.origCol())?.open(); } function makePlusButton(viewSectionRec: ViewSectionRec, popupControls: WeakMap) { return dom.domComputed((use) => { const filters = use(viewSectionRec.filters); - const anyFilter = use(viewSectionRec.activeFilters).length > 0; return cssPlusButton( cssBtn.cls('-grayed'), cssIcon('Plus'), - addFilterMenu(filters, viewSectionRec, popupControls), - anyFilter ? null : cssPlusLabel(t('AddFilter')), + addFilterMenu(filters, popupControls, { + allowedColumns: 'unpinned-or-unfiltered', + }), testId('add-filter-btn') ); }); @@ -96,12 +125,16 @@ const cssFilterBar = styled('div.filter_bar', ` &::-webkit-scrollbar { display: none; } + &-hidden { + display: none; + } `); -const cssFilterBarItem = styled(cssButtonGroup, ` +const cssFilterBarItem = styled('div', ` + border-radius: ${vars.controlBorderRadius}; flex-shrink: 0; margin: 0 4px; - & > .${cssButton.className}:first-child { - border-right: 0.5px solid white; + &-unpinned { + display: none; } `); const cssMenuTextLabel = styled('span', ` @@ -134,12 +167,6 @@ const primaryButton = (...args: IDomArgs) => ( dom('div', cssButton.cls(''), cssButton.cls('-primary'), cssBtn.cls(''), ...args) ); -const deleteButton = styled(primaryButton, ` - padding: 3px 4px; -`); const cssPlusButton = styled(primaryButton, ` padding: 3px 3px `); -const cssPlusLabel = styled('span', ` - margin: 0 12px 0 4px; -`); diff --git a/app/client/ui/FilterConfig.ts b/app/client/ui/FilterConfig.ts new file mode 100644 index 00000000..07a97178 --- /dev/null +++ b/app/client/ui/FilterConfig.ts @@ -0,0 +1,149 @@ +import {makeT} from 'app/client/lib/localization'; +import {ViewSectionRec} from 'app/client/models/DocModel'; +import {attachColumnFilterMenu} from 'app/client/ui/ColumnFilterMenu'; +import {addFilterMenu} from 'app/client/ui/FilterBar'; +import {cssIcon, cssPinButton, cssRow, cssSortFilterColumn} from 'app/client/ui/RightPanelStyles'; +import {theme} from 'app/client/ui2018/cssVars'; +import {icon} from 'app/client/ui2018/icons'; +import {Computed, Disposable, dom, makeTestId, styled} from 'grainjs'; +import {IMenuOptions} from 'popweasel'; + +const testId = makeTestId('test-filter-config-'); + +const t = makeT('SortConfig'); + +export interface FilterConfigOptions { + /** Options to pass to the menu and popup components. */ + menuOptions?: IMenuOptions; +} + +/** + * Component that renders controls for managing filters for a view section. + * + * Active filters (i.e. columns that have non-blank filters set) are displayed in + * a vertical list of pill-shaped buttons. These buttons can be clicked to open their + * respective filter menu. Additionally, there are buttons to the right of each filter + * for removing and pinning them. + */ +export class FilterConfig extends Disposable { + private _popupControls = new WeakMap(); + + private _canAddFilter = Computed.create(this, (use) => { + return use(this._section.filters).some(f => !use(f.isFiltered)); + }); + + constructor(private _section: ViewSectionRec, private _options: FilterConfigOptions = {}) { + super(); + } + + public buildDom() { + const {menuOptions} = this._options; + return dom('div', + dom.forEach(this._section.activeFilters, (filterInfo) => { + const {fieldOrColumn, filter, pinned, isPinned} = filterInfo; + return cssRow( + cssSortFilterColumn( + cssIconWrapper( + cssFilterIcon('FilterSimple', + cssFilterIcon.cls('-accent', use => !use(filter.isSaved) || !use(pinned.isSaved)), + testId('filter-icon'), + ), + ), + cssLabel(dom.text(fieldOrColumn.label)), + attachColumnFilterMenu(filterInfo, { + popupOptions: { + placement: 'bottom-end', + ...menuOptions, + trigger: [ + 'click', + (_el, popupControl) => this._popupControls.set(fieldOrColumn.origCol(), popupControl) + ], + }, + }), + testId('column'), + ), + cssPinFilterButton( + icon('PinTilted'), + dom.on('click', () => this._section.setFilter(fieldOrColumn.origCol().origColRef(), { + pinned: !isPinned.peek() + })), + cssPinButton.cls('-pinned', isPinned), + testId('pin-filter'), + ), + cssIconWrapper( + cssRemoveFilterButton('Remove', + dom.on('click', + () => this._section.setFilter(fieldOrColumn.origCol().origColRef(), { + filter: '', + pinned: false, + })), + testId('remove-filter'), + ), + ), + testId('filter'), + ); + }), + cssRow( + dom.domComputed((use) => { + const filters = use(this._section.filters); + return cssTextBtn( + t('AddColumn'), + addFilterMenu(filters, this._popupControls, { + menuOptions: { + placement: 'bottom-end', + ...this._options.menuOptions, + }, + }), + dom.on('click', (ev) => ev.stopPropagation()), + dom.hide(u => !u(this._canAddFilter)), + testId('add-filter-btn'), + ); + }), + ), + testId('container'), + ); + } +} + +const cssIconWrapper = styled('div', ``); + +const cssLabel = styled('div', ` + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + flex-grow: 1; +`); + +const cssTextBtn = styled('div', ` + color: ${theme.controlFg}; + cursor: pointer; + + &:hover { + color: ${theme.controlHoverFg}; + } +`); + +const cssFilterIcon = styled(cssIcon, ` + flex: none; + margin: 0px 6px 0px 0px; + background-color: ${theme.controlSecondaryFg}; + + &-accent { + background-color: ${theme.accentIcon}; + } +`); + +const cssRemoveFilterButton = styled(cssIcon, ` + flex: none; + margin: 0 6px; + background-color: ${theme.controlSecondaryFg}; + cursor: pointer; + + &:hover { + background-color: ${theme.controlSecondaryHoverFg}; + } +`); + +const cssPinFilterButton = styled(cssPinButton, ` + margin-left: 6px; +`); diff --git a/app/client/ui/RightPanel.ts b/app/client/ui/RightPanel.ts index e3baa4a3..a86dcbd1 100644 --- a/app/client/ui/RightPanel.ts +++ b/app/client/ui/RightPanel.ts @@ -28,6 +28,7 @@ import {GridOptions} from 'app/client/ui/GridOptions'; import {attachPageWidgetPicker, IPageWidget, toPageWidget} from 'app/client/ui/PageWidgetPicker'; import {linkId, selectBy} from 'app/client/ui/selectBy'; import {CustomSectionConfig} from 'app/client/ui/CustomSectionConfig'; +import {cssLabel} from 'app/client/ui/RightPanelStyles'; import {VisibleFieldsConfig} from 'app/client/ui/VisibleFieldsConfig'; import {IWidgetType, widgetTypes} from 'app/client/ui/widgetTypes'; import {basicButton, primaryButton} from 'app/client/ui2018/buttons'; @@ -429,14 +430,7 @@ export class RightPanel extends Disposable { private _buildPageSortFilterConfig(owner: MultiHolder) { const viewConfigTab = this._createViewConfigTab(owner); - return [ - cssLabel(t('Sort')), - dom.maybe(viewConfigTab, (vct) => vct.buildSortDom()), - cssSeparator(), - - cssLabel(t('Filter')), - dom.maybe(viewConfigTab, (vct) => dom('div', vct._buildFilterDom())), - ]; + return dom.maybe(viewConfigTab, (vct) => vct.buildSortFilterDom()); } private _buildPageDataConfig(owner: MultiHolder, activeSection: ViewSectionRec) { @@ -635,13 +629,6 @@ const cssBottomText = styled('span', ` padding: 4px 16px; `); -const cssLabel = styled('div', ` - color: ${theme.text}; - text-transform: uppercase; - margin: 16px 16px 12px 16px; - font-size: ${vars.xsmallFontSize}; -`); - const cssRow = styled('div', ` color: ${theme.text}; display: flex; diff --git a/app/client/ui/RightPanelStyles.ts b/app/client/ui/RightPanelStyles.ts index 4e47f133..89e1e1bc 100644 --- a/app/client/ui/RightPanelStyles.ts +++ b/app/client/ui/RightPanelStyles.ts @@ -33,6 +33,18 @@ export const cssRow = styled('div', ` } `); +export const cssSortFilterColumn = styled('div', ` + cursor: pointer; + display: flex; + flex-grow: 1; + align-items: center; + color: ${theme.text}; + background-color: ${theme.hover}; + overflow: hidden; + border-radius: 4px; + padding: 4px 8px; +`); + export const cssBlockedCursor = styled('span', ` &, & * { cursor: not-allowed !important; @@ -51,3 +63,23 @@ export const cssSeparator = styled('div', ` border-bottom: 1px solid ${theme.pagePanelsBorder}; margin-top: 16px; `); + +export const cssSaveButtonsRow = styled('div', ` + margin: 16px 16px 12px 16px; +`); + +export const cssPinButton = styled('div', ` + cursor: pointer; + --icon-color: ${theme.controlSecondaryFg}; + border-radius: ${vars.controlBorderRadius}; + padding: 3px; + + &-pinned { + background-color: ${theme.controlPrimaryBg}; + --icon-color: ${theme.controlPrimaryFg}; + } + + &:not(&-pinned):hover { + background-color: ${theme.hover}; + } +`); diff --git a/app/client/ui/SortConfig.ts b/app/client/ui/SortConfig.ts new file mode 100644 index 00000000..2d235654 --- /dev/null +++ b/app/client/ui/SortConfig.ts @@ -0,0 +1,372 @@ +import {GristDoc} from 'app/client/components/GristDoc'; +import koArray from 'app/client/lib/koArray'; +import * as kf from 'app/client/lib/koForm'; +import {makeT} from 'app/client/lib/localization'; +import {addToSort, updatePositions} from 'app/client/lib/sortUtil'; +import {ViewSectionRec} from 'app/client/models/DocModel'; +import {ObjObservable} from 'app/client/models/modelUtil'; +import {cssIcon, cssRow, cssSortFilterColumn} from 'app/client/ui/RightPanelStyles'; +import {labeledLeftSquareCheckbox} from 'app/client/ui2018/checkbox'; +import {theme} from 'app/client/ui2018/cssVars'; +import {cssDragger} from 'app/client/ui2018/draggableList'; +import {menu, menuItem} from 'app/client/ui2018/menus'; +import {Sort} from 'app/common/SortSpec'; +import {Computed, Disposable, dom, makeTestId, MultiHolder, styled} from 'grainjs'; +import difference = require('lodash/difference'); +import isEqual = require('lodash/isEqual'); +import {cssMenuItem, IMenuOptions} from 'popweasel'; + +interface SortableColumn { + label: string; + value: number; + icon: 'FieldColumn'; + type: string; +} + +export interface SortConfigOptions { + /** Options to pass to all menus created by `SortConfig`. */ + menuOptions?: IMenuOptions; +} + +const testId = makeTestId('test-sort-config-'); + +const t = makeT('SortConfig'); + +/** + * Component that renders controls for managing sorting for a view section. + * + * Sorted columns are displayed in a vertical list of pill-shaped buttons. These + * buttons can be clicked to toggle their sort direction, and can be clicked and + * dragged to re-arrange their order. Additionally, there are buttons to the right + * of each sorted column for removing them, and opening a menu with advanced sort + * options. + */ +export class SortConfig extends Disposable { + // Computed array of sortable columns. + private _columns: Computed = Computed.create(this, (use) => { + // Columns is an observable holding an observable array - must call 'use' on it 2x. + const cols = use(use(use(this._section.table).columns).getObservable()); + return cols.filter(col => !use(col.isHiddenCol)).map(col => ({ + label: use(col.label), + value: col.getRowId(), + icon: 'FieldColumn', + type: col.type(), + })); + }); + + // We only want to recreate rows, when the actual columns change. + private _colRefs = Computed.create(this, (use) => { + return use(this._section.activeSortSpec).map(col => Sort.getColRef(col)); + }); + private _sortRows = this.autoDispose(koArray(this._colRefs.get())); + + private _changedColRefs = Computed.create(this, (use) => { + const changedSpecs = difference( + use(this._section.activeSortSpec), + Sort.parseSortColRefs(use(this._section.sortColRefs)) + ); + return new Set(changedSpecs.map(spec => Sort.getColRef(spec))); + }); + + constructor(private _section: ViewSectionRec, private _gristDoc: GristDoc, private _options: SortConfigOptions = {}) { + super(); + + this.autoDispose(this._colRefs.addListener((curr, prev) => { + if (!isEqual(curr, prev)){ + this._sortRows.assign(curr); + } + })); + } + + public buildDom() { + return dom('div', + // Sort rows. + kf.draggableList(this._sortRows, (colRef: number) => this._createRow(colRef), { + reorder: (colRef: number, nextColRef: number | null) => this._reorder(colRef, nextColRef), + removeButton: false, + drag_indicator: cssDragger, + itemClass: cssDragRow.className, + }), + // Add to sort btn & menu. + this._buildAddToSortButton(this._columns), + this._buildUpdateDataButton(), + testId('container'), + ); + } + + private _createRow(colRef: number) { + return this._buildSortRow(colRef, this._section.activeSortSpec, this._columns); + } + + /** + * Builds a single row of the sort dom. + * Takes the colRef, current sortSpec and array of column select options to show + * in the column select dropdown. + */ + private _buildSortRow( + colRef: number, + sortSpec: ObjObservable, + columns: Computed + ) { + const holder = new MultiHolder(); + const {menuOptions} = this._options; + + const col = Computed.create(holder, () => colRef); + const details = Computed.create(holder, (use) => Sort.specToDetails(Sort.findCol(use(sortSpec), colRef)!)); + const hasSpecs = Computed.create(holder, details, (_, specDetails) => Sort.hasOptions(specDetails)); + const isAscending = Computed.create(holder, details, (_, specDetails) => specDetails.direction === Sort.ASC); + + col.onWrite((newRef) => { + let specs = sortSpec.peek(); + const colSpec = Sort.findCol(specs, colRef); + const newSpec = Sort.findCol(specs, newRef); + if (newSpec) { + // this column is already there so only swap order + specs = Sort.swap(specs, colRef, newRef); + // but keep the directions + specs = Sort.setSortDirection(specs, colRef, Sort.direction(newSpec)); + specs = Sort.setSortDirection(specs, newRef, Sort.direction(colSpec!)); + } else { + specs = Sort.replace(specs, colRef, Sort.createColSpec(newRef, Sort.direction(colSpec!))); + } + this._saveSort(specs); + }); + + const computedFlag = ( + flag: keyof Sort.ColSpecDetails, + allowedTypes: string[] | null, + label: string + ) => { + const computed = Computed.create(holder, details, (_, d) => d[flag] || false); + computed.onWrite(value => { + const specs = sortSpec.peek(); + // Get existing details + const specDetails = Sort.specToDetails(Sort.findCol(specs, colRef)!) as any; + // Update flags + specDetails[flag] = value; + // Replace the colSpec at the index + this._saveSort(Sort.replace(specs, Sort.getColRef(colRef), specDetails)); + }); + return {computed, allowedTypes, flag, label}; + }; + const orderByChoice = computedFlag('orderByChoice', ['Choice'], t('UseChoicePosition')); + const naturalSort = computedFlag('naturalSort', ['Text'], t('NaturalSort')); + const emptyLast = computedFlag('emptyLast', null, t('EmptyValuesLast')); + const flags = [orderByChoice, emptyLast, naturalSort]; + + const column = columns.get().find(c => c.value === Sort.getColRef(colRef)); + + return cssSortRow( + dom.autoDispose(holder), + cssSortFilterColumn( + dom.domComputed(isAscending, ascending => + cssSortIcon( + "Sort", + cssSortIcon.cls('-accent', use => use(this._changedColRefs).has(column!.value)), + dom.style("transform", ascending ? "scaleY(-1)" : "none"), + testId('order'), + testId(ascending ? "sort-order-asc" : "sort-order-desc"), + ) + ), + cssLabel(column!.label), + dom.on("click", () => { + this._saveSort(Sort.flipSort(sortSpec.peek(), colRef)); + }), + testId('column'), + ), + cssMenu( + cssBigIconWrapper( + cssIcon('Dots', dom.cls(cssBgAccent.className, hasSpecs)), + testId('options-icon'), + ), + menu(_ctl => flags.map(({computed, allowedTypes, flag, label}) => { + // when allowedTypes is null, flag can be used for every column + const enabled = !allowedTypes || allowedTypes.includes(column!.type); + return cssMenuItem( + labeledLeftSquareCheckbox( + computed as any, + label, + dom.prop('disabled', !enabled), + ), + dom.cls(cssOptionMenuItem.className), + dom.cls('disabled', !enabled), + testId('option'), + testId(`option-${flag}`), + ); + }, + ), menuOptions), + ), + cssSortIconBtn('Remove', + dom.on('click', () => { + const specs = sortSpec.peek(); + if (Sort.findCol(specs, colRef)) { + this._saveSort(Sort.removeCol(specs, colRef)); + } + }), + testId('remove') + ), + testId('row'), + ); + } + + private _buildAddToSortButton(columns: Computed) { + const available = Computed.create(null, (use) => { + const currentSection = this._section; + const currentSortSpec = use(currentSection.activeSortSpec); + const specRowIds = new Set(currentSortSpec.map(_sortRef => Sort.getColRef(_sortRef))); + return use(columns).filter(_col => !specRowIds.has(_col.value)); + }); + const {menuOptions} = this._options; + return cssButtonRow( + dom.autoDispose(available), + dom.domComputed(use => { + const cols = use(available); + return cssTextBtn( + t('AddColumn'), + menu((ctl) => [ + ...cols.map((col) => ( + menuItem( + () => addToSort(this._section.activeSortSpec, col.value, 1), + col.label, + testId('add-menu-row') + ) + )), + // We need to stop click event to propagate otherwise it would cause view section menu to + // close. + dom.on('click', (ev) => { + ctl.close(); + ev.stopPropagation(); + }), + ], menuOptions), + dom.on('click', (ev) => { ev.stopPropagation(); }), + testId('add'), + ); + }), + dom.hide(use => !use(available).length), + ); + } + + private _buildUpdateDataButton() { + return dom.maybe(this._section.isSorted, () => + cssButtonRow( + cssTextBtn(t('UpdateData'), + dom.on('click', () => updatePositions(this._gristDoc, this._section)), + testId('update'), + dom.show((use) => ( + use(use(this._section.table).supportsManualSort) + && !use(this._gristDoc.isReadonly) + )), + ), + ), + ); + } + + private _reorder(colRef: number, nextColRef: number | null) { + const activeSortSpec = this._section.activeSortSpec.peek(); + const colSpec = Sort.findCol(activeSortSpec, colRef); + if (colSpec === undefined) { + throw new Error(`Col ${colRef} not found in active sort spec`); + } + + const newSpec = Sort.reorderSortRefs(this._section.activeSortSpec.peek(), colSpec, nextColRef); + this._saveSort(newSpec); + } + + private _saveSort(sortSpec: Sort.SortSpec) { + this._section.activeSortSpec(sortSpec); + } +} + +const cssDragRow = styled('div', ` + display: flex !important; + align-items: center; + margin: 0 16px 0px 0px; + & > .kf_draggable_content { + margin: 4px 0; + flex: 1 1 0px; + min-width: 0px; + } +`); + +const cssLabel = styled('div', ` + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + flex-grow: 1; +`); + +const cssSortRow = styled('div', ` + display: flex; + align-items: center; + width: 100%; +`); + +const cssTextBtn = styled('div', ` + color: ${theme.controlFg}; + cursor: pointer; + + &:hover { + color: ${theme.controlHoverFg}; + } +`); + +const cssSortIconBtn = styled(cssIcon, ` + flex: none; + margin: 0 6px; + cursor: pointer; + background-color: ${theme.controlSecondaryFg}; + + &:hover { + background-color: ${theme.controlSecondaryHoverFg}; + } +`); + +const cssSortIcon = styled(cssIcon, ` + flex: none; + margin: 0px 6px 0px 0px; + background-color: ${theme.controlSecondaryFg}; + + &-accent { + background-color: ${theme.accentIcon}; + } +`); + +const cssBigIconWrapper = styled('div', ` + padding: 3px; + border-radius: 3px; + cursor: pointer; + user-select: none; +`); + +const cssBgAccent = styled(`div`, ` + background: ${theme.accentIcon} +`); + +const cssMenu = styled('div', ` + display: inline-flex; + cursor: pointer; + border-radius: 3px; + border: 1px solid transparent; + margin-left: 6px; + &:hover, &.weasel-popup-open { + background-color: ${theme.hover}; + } +`); + +const cssOptionMenuItem = styled('div', ` + &:hover { + background-color: ${theme.hover}; + } + & label { + flex: 1; + cursor: pointer; + } + &.disabled * { + color: ${theme.menuItemDisabledFg} important; + cursor: not-allowed; + } +`); + +const cssButtonRow = styled(cssRow, ` + margin-top: 4px; +`); diff --git a/app/client/ui/SortFilterConfig.ts b/app/client/ui/SortFilterConfig.ts new file mode 100644 index 00000000..37f09796 --- /dev/null +++ b/app/client/ui/SortFilterConfig.ts @@ -0,0 +1,68 @@ +import {GristDoc} from 'app/client/components/GristDoc'; +import {makeT} from 'app/client/lib/localization'; +import {ViewSectionRec} from 'app/client/models/DocModel'; +import {FilterConfig} from 'app/client/ui/FilterConfig'; +import {cssLabel, cssSaveButtonsRow} from 'app/client/ui/RightPanelStyles'; +import {SortConfig} from 'app/client/ui/SortConfig'; +import {basicButton, primaryButton} from 'app/client/ui2018/buttons'; +import {Computed, Disposable, dom, makeTestId, styled} from 'grainjs'; + +const testId = makeTestId('test-sort-filter-config-'); + +const t = makeT('SortFilterConfig'); + +export class SortFilterConfig extends Disposable { + private _docModel = this._gristDoc.docModel; + private _isReadonly = this._gristDoc.isReadonly; + + private _hasChanges: Computed = Computed.create(this, (use) => ( + use(this._section.filterSpecChanged) || !use(this._section.activeSortJson.isSaved) + )); + + constructor(private _section: ViewSectionRec, private _gristDoc: GristDoc) { + super(); + } + + public buildDom() { + return [ + cssLabel(t('Sort')), + dom.create(SortConfig, this._section, this._gristDoc, { + menuOptions: {attach: 'body'}, + }), + cssLabel(t('Filter')), + dom.create(FilterConfig, this._section, { + menuOptions: {attach: 'body'}, + }), + dom.maybe(this._hasChanges, () => [ + cssSaveButtonsRow( + cssSaveButton(t('Save'), + dom.on('click', () => this._save()), + dom.boolAttr('disabled', this._isReadonly), + testId('save'), + ), + basicButton(t('Revert'), + dom.on('click', () => this._revert()), + testId('revert'), + ), + testId('save-btns'), + ), + ]), + ]; + } + + private async _save() { + await this._docModel.docData.bundleActions(t('UpdateSortFilterSettings'), () => Promise.all([ + this._section.activeSortJson.save(), + this._section.saveFilters(), + ])); + } + + private _revert() { + this._section.activeSortJson.revert(); + this._section.revertFilters(); + } +} + +const cssSaveButton = styled(primaryButton, ` + margin-right: 8px; +`); diff --git a/app/client/ui/ViewSectionMenu.ts b/app/client/ui/ViewSectionMenu.ts index f20b0daf..9a8c97e7 100644 --- a/app/client/ui/ViewSectionMenu.ts +++ b/app/client/ui/ViewSectionMenu.ts @@ -1,20 +1,18 @@ +import {GristDoc} from 'app/client/components/GristDoc'; import {makeT} from 'app/client/lib/localization'; import {reportError} from 'app/client/models/AppModel'; -import {ColumnRec, DocModel, ViewSectionRec} from 'app/client/models/DocModel'; -import {FilterInfo} from 'app/client/models/entities/ViewSectionRec'; -import {CustomComputed} from 'app/client/models/modelUtil'; -import {attachColumnFilterMenu} from 'app/client/ui/ColumnFilterMenu'; -import {addFilterMenu} from 'app/client/ui/FilterBar'; +import {DocModel, ViewSectionRec} from 'app/client/models/DocModel'; +import {FilterConfig} from 'app/client/ui/FilterConfig'; +import {cssLabel, cssSaveButtonsRow} from 'app/client/ui/RightPanelStyles'; import {hoverTooltip} from 'app/client/ui/tooltips'; +import {SortConfig} from 'app/client/ui/SortConfig'; import {makeViewLayoutMenu} from 'app/client/ui/ViewLayoutMenu'; import {basicButton, primaryButton} from 'app/client/ui2018/buttons'; import {theme, vars} from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; import {menu} from 'app/client/ui2018/menus'; -import {Sort} from 'app/common/SortSpec'; -import {Computed, dom, fromKo, IDisposableOwner, makeTestId, Observable, styled} from 'grainjs'; -import {PopupControl} from 'popweasel'; -import difference = require('lodash/difference'); +import {Computed, dom, IDisposableOwner, makeTestId, styled} from 'grainjs'; +import {defaultMenuOptions} from 'popweasel'; const testId = makeTestId('test-section-menu-'); const t = makeT('ViewSectionMenu'); @@ -24,7 +22,6 @@ async function doSave(docModel: DocModel, viewSection: ViewSectionRec): Promise< await docModel.docData.bundleActions(t("UpdateSortFilterSettings"), () => Promise.all([ viewSection.activeSortJson.save(), // Save sort viewSection.saveFilters(), // Save filter - viewSection.activeFilterBar.save(), // Save bar viewSection.activeCustomOptions.save(), // Save widget options ])); } @@ -33,24 +30,24 @@ async function doSave(docModel: DocModel, viewSection: ViewSectionRec): Promise< function doRevert(viewSection: ViewSectionRec) { viewSection.activeSortJson.revert(); // Revert sort viewSection.revertFilters(); // Revert filter - viewSection.activeFilterBar.revert(); // Revert bar viewSection.activeCustomOptions.revert(); // Revert widget options } -// [Filter Icon] (v) (x) - Filter toggle and all the components in the menu. -export function viewSectionMenu(owner: IDisposableOwner, docModel: DocModel, viewSection: ViewSectionRec, - isReadonly: Observable) { +// [Filter Icon] - Filter toggle and all the components in the menu. +export function viewSectionMenu( + owner: IDisposableOwner, + gristDoc: GristDoc, + viewSection: ViewSectionRec, +) { + const {docModel, isReadonly} = gristDoc; - const popupControls = new WeakMap(); + // If there is any filter (should [Filter Icon] background be filled). + const anyFilter = Computed.create(owner, (use) => Boolean(use(viewSection.activeFilters).length)); - // If there is any filter (should [Filter Icon] be green). - const anyFilter = Computed.create(owner, (use) => Boolean(use(viewSection.activeFilters).length)); - - // Should border be green, and should we show [Save] [Revert] (v) (x) buttons. + // Should we show [Save] [Revert] buttons. const displaySaveObs: Computed = Computed.create(owner, (use) => ( use(viewSection.filterSpecChanged) || !use(viewSection.activeSortJson.isSaved) - || !use(viewSection.activeFilterBar.isSaved) || !use(viewSection.activeCustomOptions.isSaved) )); @@ -64,189 +61,111 @@ export function viewSectionMenu(owner: IDisposableOwner, docModel: DocModel, vie testId('wrapper'), cssMenu( testId('sortAndFilter'), - // [Filter icon] grey or green + // [Filter icon] cssFilterIconWrapper( testId('filter-icon'), - // Make green when there are some filters. If there are only sort options, leave grey. + // Fill background when there are some filters. Ignore sort options. cssFilterIconWrapper.cls('-any', anyFilter), cssFilterIcon('Filter'), hoverTooltip('Sort and filter', {key: 'sortFilterBtnTooltip'}), ), - menu(ctl => [ - // Sorted by section. - dom.domComputed(use => { - use(viewSection.activeSortJson.isSaved); // Rebuild sort panel if sort gets saved. A little hacky. - return makeSortPanel(viewSection, use(viewSection.activeSortSpec), - (row: number) => docModel.columns.getRowModel(row)); - }), - // Filtered by section. - dom.domComputed(viewSection.activeFilters, filters => - makeFilterPanel(viewSection, filters, popupControls, () => ctl.close())), - // [+] Add filter - makeAddFilterButton(viewSection, popupControls), - // [+] Toggle filter bar - dom.maybe((use) => !use(viewSection.isRaw), - () => makeFilterBarToggle(viewSection.activeFilterBar)), - // Widget options - dom.maybe(use => use(viewSection.parentKey) === 'custom', () => - makeCustomOptions(viewSection) - ), - // [Save] [Revert] buttons - dom.domComputed(displaySaveObs, displaySave => [ - displaySave ? cssMenuInfoHeader( - cssSaveButton(t('Save'), testId('btn-save'), - dom.on('click', () => { save(); ctl.close(); }), - dom.boolAttr('disabled', isReadonly)), - basicButton(t('Revert'), testId('btn-revert'), - dom.on('click', () => { revert(); ctl.close(); })) - ) : null, - ]), - ]), ), - // Two icons (v) (x) left to the toggle, when there are unsaved filters or sort options. - // Those buttons are equivalent of the [Save] [Revert] buttons in the menu. - dom.maybe(displaySaveObs, () => cssSaveIconsWrapper( - // (v) - cssSmallIconWrapper( - cssIcon('Tick'), cssSmallIconWrapper.cls('-green'), + // [Save] [Revert] buttons when there are unsaved options. + dom.maybe(displaySaveObs, () => cssSectionSaveButtonsWrapper( + cssSaveTextButton( + t('Save'), + cssSaveTextButton.cls('-accent'), dom.on('click', save), hoverTooltip('Save sort & filter settings', {key: 'sortFilterBtnTooltip'}), testId('small-btn-save'), dom.hide(isReadonly), ), - // (x) - cssSmallIconWrapper( - cssIcon('CrossSmall'), cssSmallIconWrapper.cls('-gray'), + cssRevertIconButton( + cssRevertIcon('Revert', cssRevertIcon.cls('-normal')), dom.on('click', revert), hoverTooltip('Revert sort & filter settings', {key: 'sortFilterBtnTooltip'}), testId('small-btn-revert'), ), )), + menu(ctl => [ + // Sort section. + makeSortPanel(viewSection, gristDoc), + // Filter section. + makeFilterPanel(viewSection), + // Widget options + dom.maybe(use => use(viewSection.parentKey) === 'custom', () => + makeCustomOptions(viewSection) + ), + // [Save] [Revert] buttons + dom.domComputed(displaySaveObs, displaySave => [ + displaySave ? cssSaveButtonsRow( + cssSaveButton(t('Save'), testId('btn-save'), + dom.on('click', () => { ctl.close(); save(); }), + dom.boolAttr('disabled', isReadonly)), + basicButton(t('Revert'), testId('btn-revert'), + dom.on('click', () => { ctl.close(); revert(); })) + ) : null, + ]), + // Updates to active sort or filters can cause menu contents to grow, while + // leaving the position of the popup unchanged. This can sometimes lead to + // the menu growing beyond the boundaries of the viewport. To mitigate this, + // we subscribe to changes to the sort/filters and manually update the popup's + // position, which will re-position the popup if necessary so that it's fully + // visible. + dom.autoDispose(viewSection.activeFilters.addListener(() => ctl.update())), + dom.autoDispose(viewSection.activeSortJson.subscribe(() => ctl.update())), + ], {...defaultMenuOptions, placement: 'bottom-end', trigger: [ + // Toggle the menu whenever the filter icon button is clicked. + (el, ctl) => dom.onMatchElem(el, '.test-section-menu-sortAndFilter', 'click', () => { + ctl.toggle(); + }), + // Close the menu whenever the save or revert button is clicked. + (el, ctl) => dom.onMatchElem(el, '.test-section-menu-small-btn-save', 'click', () => { + ctl.close(); + }), + (el, ctl) => dom.onMatchElem(el, '.test-section-menu-small-btn-revert', 'click', () => { + ctl.close(); + }), + ]}), ), cssMenu( testId('viewLayout'), cssFixHeight.cls(''), cssDotsIconWrapper(cssIcon('Dots')), - menu(_ctl => makeViewLayoutMenu(viewSection, isReadonly.get())) + menu(_ctl => makeViewLayoutMenu(viewSection, isReadonly.get()), { + ...defaultMenuOptions, + placement: 'bottom-end', + }) ) ]; } -// Sorted by section (and all columns underneath or (Default) label). -function makeSortPanel(section: ViewSectionRec, sortSpec: Sort.SortSpec, getColumn: (row: number) => ColumnRec) { - const changedColumns = difference(sortSpec, Sort.parseSortColRefs(section.sortColRefs.peek())); - const sortColumns = sortSpec.map(colSpec => { - // colRef is a rowId of a column or its negative value (indicating descending order). - const col = getColumn(Sort.getColRef(colSpec)); - return cssMenuText( - cssMenuIconWrapper( - cssMenuIconWrapper.cls('-changed', changedColumns.includes(colSpec)), - cssMenuIconWrapper.cls(Sort.isAscending(colSpec) ? '-asc' : '-desc'), - cssIcon('Sort', - dom.style('transform', Sort.isAscending(colSpec) ? 'scaleY(-1)' : 'none'), - dom.on('click', () => { - section.activeSortSpec(Sort.flipSort(sortSpec, colSpec)); - }) - ) - ), - cssMenuTextLabel(col.colId()), - cssMenuIconWrapper( - cssIcon('Remove', testId('btn-remove-sort'), dom.on('click', () => { - if (Sort.findCol(sortSpec, colSpec)) { - section.activeSortSpec(Sort.removeCol(sortSpec, colSpec)); - } - })) - ), - testId('sort-col') - ); - }); - +function makeSortPanel(section: ViewSectionRec, gristDoc: GristDoc) { return [ - cssMenuInfoHeader(t('SortedBy'), testId('heading-sorted')), - sortColumns.length > 0 ? sortColumns : cssGrayedMenuText('(Default)') + cssLabel(t('Sort'), testId('heading-sort')), + dom.create(SortConfig, section, gristDoc, { + // Attach content to triggerElem's parent, which is needed to prevent view + // section menu to close when clicking an item in the advanced sort menu. + menuOptions: {attach: null}, + }), ]; } -// [+] Add Filter. -export function makeAddFilterButton(viewSectionRec: ViewSectionRec, popupControls: WeakMap) { - return dom.domComputed((use) => { - const filters = use(viewSectionRec.filters); - return cssMenuText( - cssMenuIconWrapper( - cssIcon('Plus'), - addFilterMenu(filters, viewSectionRec, popupControls, { - placement: 'bottom-end', - // Attach content to triggerElem's parent, which is needed to prevent view section menu to - // close when clicking an item of the add filter menu. - attach: null - }), - testId('plus-button'), - dom.on('click', (ev) => ev.stopPropagation()), - ), - cssMenuTextLabel(t('AddFilter')), - ); - }); -} - -// [v] or [x] Toggle Filter Bar. -export function makeFilterBarToggle(activeFilterBar: CustomComputed) { - return cssMenuText( - cssMenuIconWrapper( - testId('btn'), - cssMenuIconWrapper.cls('-changed', (use) => !use(activeFilterBar.isSaved)), - dom.domComputed((use) => { - const filterBar = use(activeFilterBar); - const isSaved = use(activeFilterBar.isSaved); - return cssIcon(filterBar ? "Tick" : (isSaved ? "Plus" : "CrossSmall"), - cssIcon.cls('-green', Boolean(filterBar)), - testId('icon')); - }), - ), - dom.on('click', () => activeFilterBar(!activeFilterBar.peek())), - cssMenuTextLabel(t("ToggleFilterBar")), - ); -} - -// Filtered by - section in the menu (contains all filtered columns or (Not filtered) label). -function makeFilterPanel(section: ViewSectionRec, activeFilters: FilterInfo[], - popupControls: WeakMap, - onCloseContent: () => void) { - const filters = activeFilters.map(filterInfo => { - const filterChanged = Computed.create(null, fromKo(filterInfo.filter.isSaved), (_use, isSaved) => !isSaved); - return cssMenuText( - cssMenuIconWrapper( - cssMenuIconWrapper.cls('-changed', filterChanged), - cssIcon('FilterSimple'), - attachColumnFilterMenu(section, filterInfo, { - placement: 'bottom-end', - trigger: [ - 'click', - (_el, popupControl) => popupControls.set(filterInfo.fieldOrColumn.origCol(), popupControl) - ], - onCloseContent, - }), - testId('filter-icon'), - ), - cssMenuTextLabel(filterInfo.fieldOrColumn.label()), - cssMenuIconWrapper(cssIcon('Remove', - dom.on('click', () => section.setFilter(filterInfo.fieldOrColumn.origCol().origColRef(), ''))), - testId('btn-remove-filter') - ), - testId('filter-col') - ); - }); - +function makeFilterPanel(section: ViewSectionRec) { return [ - cssMenuInfoHeader(t('FilteredBy'), {style: 'margin-top: 4px'}, testId('heading-filtered')), - activeFilters.length > 0 ? filters : cssGrayedMenuText('(Not filtered)') + cssLabel(t('Filter'), testId('heading-filter')), + dom.create(FilterConfig, section, { + // Attach content to triggerElem's parent, which is needed to prevent view + // section menu to close when clicking an item of the add filter menu. + menuOptions: {attach: null}, + }), ]; } - // Custom Options // (empty)|(customized)|(modified) [Remove Icon] function makeCustomOptions(section: ViewSectionRec) { - const color = Computed.create(null, use => use(section.activeCustomOptions.isSaved) ? "-gray" : "-green"); + const color = Computed.create(null, use => use(section.activeCustomOptions.isSaved) ? "-normal" : "-accent"); const text = Computed.create(null, use => { if (use(section.activeCustomOptions)) { return use(section.activeCustomOptions.isSaved) ? t("Customized") : t("Modified"); @@ -348,7 +267,7 @@ const cssIcon = styled(icon, ` background-color: ${theme.controlPrimaryFg}; } - &-green { + &-accent { background-color: ${theme.accentIcon}; } `); @@ -363,14 +282,18 @@ const cssDotsIconWrapper = styled(cssIconWrapper, ` const cssFilterIconWrapper = styled(cssIconWrapper, ` border-radius: 2px 0px 0px 2px; + &-any { + border-radius: 2px; + background-color: ${theme.controlSecondaryFg}; + } .${cssFilterMenuWrapper.className}-unsaved & { - background-color: ${theme.accentIcon}; + background-color: ${theme.controlPrimaryBg}; } `); const cssFilterIcon = styled(cssIcon, ` .${cssFilterIconWrapper.className}-any & { - background-color: ${theme.accentIcon}; + background-color: ${theme.controlPrimaryFg}; } .${cssFilterMenuWrapper.className}-unsaved & { background-color: ${theme.controlPrimaryFg}; @@ -390,51 +313,48 @@ const cssMenuText = styled('div', ` padding: 0px 24px 8px 24px; cursor: default; white-space: nowrap; - &-green { + &-accent { color: ${theme.accentText}; } - &-gray { + &-normal { color: ${theme.lightText}; } `); -const cssGrayedMenuText = styled(cssMenuText, ` - color: ${theme.lightText}; +const cssSaveButton = styled(primaryButton, ` + margin-right: 8px; `); -const cssMenuTextLabel = styled('span', ` - color: ${theme.menuItemFg}; - flex-grow: 1; - padding: 0 4px; - overflow: hidden; - text-overflow: ellipsis; +const cssSaveTextButton = styled('div', ` + display: flex; + align-items: center; + cursor: pointer; + font-size: ${vars.mediumFontSize}; + padding: 0px 5px; + border-right: 1px solid ${theme.accentBorder}; + + &-accent { + color: ${theme.accentText}; + } `); -const cssSaveButton = styled(primaryButton, ` - margin-right: 8px; +const cssRevertIconButton = styled('div', ` + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; `); -const cssSmallIconWrapper = styled('div', ` - width: 16px; - height: 16px; - border-radius: 8px; +const cssRevertIcon = styled(icon, ` + --icon-color: ${theme.accentIcon}; margin: 0 5px 0 5px; - - &-green { - background-color: ${theme.accentIcon}; - } - &-gray { - background-color: ${theme.lightText}; - } - & > .${cssIcon.className} { - background-color: ${theme.controlPrimaryFg}; - } `); -const cssSaveIconsWrapper = styled('div', ` +const cssSectionSaveButtonsWrapper = styled('div', ` padding: 0 1px 0 1px; display: flex; justify-content: space-between; + align-self: normal; `); const cssSpacer = styled('div', ` diff --git a/app/client/ui2018/IconList.ts b/app/client/ui2018/IconList.ts index b11c5b06..a9014e28 100644 --- a/app/client/ui2018/IconList.ts +++ b/app/client/ui2018/IconList.ts @@ -90,6 +90,7 @@ export type IconName = "ChartArea" | "Pencil" | "PinBig" | "PinSmall" | + "PinTilted" | "Pivot" | "PivotLight" | "Plus" | @@ -101,6 +102,7 @@ export type IconName = "ChartArea" | "Remove" | "Repl" | "ResizePanel" | + "Revert" | "RightAlign" | "Script" | "Search" | @@ -220,6 +222,7 @@ export const IconList: IconName[] = ["ChartArea", "Pencil", "PinBig", "PinSmall", + "PinTilted", "Pivot", "PivotLight", "Plus", @@ -231,6 +234,7 @@ export const IconList: IconName[] = ["ChartArea", "Remove", "Repl", "ResizePanel", + "Revert", "RightAlign", "Script", "Search", diff --git a/app/client/ui2018/buttons.ts b/app/client/ui2018/buttons.ts index 6c4dd985..83378ac2 100644 --- a/app/client/ui2018/buttons.ts +++ b/app/client/ui2018/buttons.ts @@ -22,6 +22,7 @@ export const cssButton = styled('button', ` outline: none; border-style: none; line-height: normal; + user-select: none; /* Vars */ font-size: ${vars.mediumFontSize}; diff --git a/app/common/schema.ts b/app/common/schema.ts index d4564fcc..6142e47d 100644 --- a/app/common/schema.ts +++ b/app/common/schema.ts @@ -4,7 +4,7 @@ import { GristObjCode } from "app/plugin/GristData"; // tslint:disable:object-literal-key-quotes -export const SCHEMA_VERSION = 33; +export const SCHEMA_VERSION = 34; export const schema = { @@ -194,6 +194,7 @@ export const schema = { viewSectionRef : "Ref:_grist_Views_section", colRef : "Ref:_grist_Tables_column", filter : "Text", + pinned : "Bool", }, "_grist_Cells": { @@ -397,6 +398,7 @@ export interface SchemaTypes { viewSectionRef: number; colRef: number; filter: string; + pinned: boolean; }; "_grist_Cells": { diff --git a/app/server/lib/initialDocSql.ts b/app/server/lib/initialDocSql.ts index 8c43cb89..8f4ce689 100644 --- a/app/server/lib/initialDocSql.ts +++ b/app/server/lib/initialDocSql.ts @@ -6,7 +6,7 @@ export const GRIST_DOC_SQL = ` PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE IF NOT EXISTS "_grist_DocInfo" (id INTEGER PRIMARY KEY, "docId" TEXT DEFAULT '', "peers" TEXT DEFAULT '', "basketId" TEXT DEFAULT '', "schemaVersion" INTEGER DEFAULT 0, "timezone" TEXT DEFAULT '', "documentSettings" TEXT DEFAULT ''); -INSERT INTO _grist_DocInfo VALUES(1,'','','',33,'',''); +INSERT INTO _grist_DocInfo VALUES(1,'','','',34,'',''); CREATE TABLE IF NOT EXISTS "_grist_Tables" (id INTEGER PRIMARY KEY, "tableId" TEXT DEFAULT '', "primaryViewId" INTEGER DEFAULT 0, "summarySourceTable" INTEGER DEFAULT 0, "onDemand" BOOLEAN DEFAULT 0, "rawViewSectionRef" INTEGER DEFAULT 0); CREATE TABLE IF NOT EXISTS "_grist_Tables_column" (id INTEGER PRIMARY KEY, "parentId" INTEGER DEFAULT 0, "parentPos" NUMERIC DEFAULT 1e999, "colId" TEXT DEFAULT '', "type" TEXT DEFAULT '', "widgetOptions" TEXT DEFAULT '', "isFormula" BOOLEAN DEFAULT 0, "formula" TEXT DEFAULT '', "label" TEXT DEFAULT '', "untieColIdFromLabel" BOOLEAN DEFAULT 0, "summarySourceCol" INTEGER DEFAULT 0, "displayCol" INTEGER DEFAULT 0, "visibleCol" INTEGER DEFAULT 0, "rules" TEXT DEFAULT NULL, "recalcWhen" INTEGER DEFAULT 0, "recalcDeps" TEXT DEFAULT NULL); CREATE TABLE IF NOT EXISTS "_grist_Imports" (id INTEGER PRIMARY KEY, "tableRef" INTEGER DEFAULT 0, "origFileName" TEXT DEFAULT '', "parseFormula" TEXT DEFAULT '', "delimiter" TEXT DEFAULT '', "doublequote" BOOLEAN DEFAULT 0, "escapechar" TEXT DEFAULT '', "quotechar" TEXT DEFAULT '', "skipinitialspace" BOOLEAN DEFAULT 0, "encoding" TEXT DEFAULT '', "hasHeaders" BOOLEAN DEFAULT 0); @@ -33,7 +33,7 @@ INSERT INTO _grist_ACLPrincipals VALUES(2,'group','','','Admins',''); INSERT INTO _grist_ACLPrincipals VALUES(3,'group','','','Editors',''); INSERT INTO _grist_ACLPrincipals VALUES(4,'group','','','Viewers',''); CREATE TABLE IF NOT EXISTS "_grist_ACLMemberships" (id INTEGER PRIMARY KEY, "parent" INTEGER DEFAULT 0, "child" INTEGER DEFAULT 0); -CREATE TABLE IF NOT EXISTS "_grist_Filters" (id INTEGER PRIMARY KEY, "viewSectionRef" INTEGER DEFAULT 0, "colRef" INTEGER DEFAULT 0, "filter" TEXT DEFAULT ''); +CREATE TABLE IF NOT EXISTS "_grist_Filters" (id INTEGER PRIMARY KEY, "viewSectionRef" INTEGER DEFAULT 0, "colRef" INTEGER DEFAULT 0, "filter" TEXT DEFAULT '', "pinned" BOOLEAN DEFAULT 0); CREATE TABLE IF NOT EXISTS "_grist_Cells" (id INTEGER PRIMARY KEY, "tableRef" INTEGER DEFAULT 0, "colRef" INTEGER DEFAULT 0, "rowId" INTEGER DEFAULT 0, "root" BOOLEAN DEFAULT 0, "parentId" INTEGER DEFAULT 0, "type" INTEGER DEFAULT 0, "content" TEXT DEFAULT '', "userRef" TEXT DEFAULT ''); CREATE INDEX _grist_Attachments_fileIdent ON _grist_Attachments(fileIdent); COMMIT; @@ -43,7 +43,7 @@ export const GRIST_DOC_WITH_TABLE1_SQL = ` PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE IF NOT EXISTS "_grist_DocInfo" (id INTEGER PRIMARY KEY, "docId" TEXT DEFAULT '', "peers" TEXT DEFAULT '', "basketId" TEXT DEFAULT '', "schemaVersion" INTEGER DEFAULT 0, "timezone" TEXT DEFAULT '', "documentSettings" TEXT DEFAULT ''); -INSERT INTO _grist_DocInfo VALUES(1,'','','',33,'',''); +INSERT INTO _grist_DocInfo VALUES(1,'','','',34,'',''); CREATE TABLE IF NOT EXISTS "_grist_Tables" (id INTEGER PRIMARY KEY, "tableId" TEXT DEFAULT '', "primaryViewId" INTEGER DEFAULT 0, "summarySourceTable" INTEGER DEFAULT 0, "onDemand" BOOLEAN DEFAULT 0, "rawViewSectionRef" INTEGER DEFAULT 0); INSERT INTO _grist_Tables VALUES(1,'Table1',1,0,0,2); CREATE TABLE IF NOT EXISTS "_grist_Tables_column" (id INTEGER PRIMARY KEY, "parentId" INTEGER DEFAULT 0, "parentPos" NUMERIC DEFAULT 1e999, "colId" TEXT DEFAULT '', "type" TEXT DEFAULT '', "widgetOptions" TEXT DEFAULT '', "isFormula" BOOLEAN DEFAULT 0, "formula" TEXT DEFAULT '', "label" TEXT DEFAULT '', "untieColIdFromLabel" BOOLEAN DEFAULT 0, "summarySourceCol" INTEGER DEFAULT 0, "displayCol" INTEGER DEFAULT 0, "visibleCol" INTEGER DEFAULT 0, "rules" TEXT DEFAULT NULL, "recalcWhen" INTEGER DEFAULT 0, "recalcDeps" TEXT DEFAULT NULL); @@ -86,7 +86,7 @@ INSERT INTO _grist_ACLPrincipals VALUES(2,'group','','','Admins',''); INSERT INTO _grist_ACLPrincipals VALUES(3,'group','','','Editors',''); INSERT INTO _grist_ACLPrincipals VALUES(4,'group','','','Viewers',''); CREATE TABLE IF NOT EXISTS "_grist_ACLMemberships" (id INTEGER PRIMARY KEY, "parent" INTEGER DEFAULT 0, "child" INTEGER DEFAULT 0); -CREATE TABLE IF NOT EXISTS "_grist_Filters" (id INTEGER PRIMARY KEY, "viewSectionRef" INTEGER DEFAULT 0, "colRef" INTEGER DEFAULT 0, "filter" TEXT DEFAULT ''); +CREATE TABLE IF NOT EXISTS "_grist_Filters" (id INTEGER PRIMARY KEY, "viewSectionRef" INTEGER DEFAULT 0, "colRef" INTEGER DEFAULT 0, "filter" TEXT DEFAULT '', "pinned" BOOLEAN DEFAULT 0); CREATE TABLE IF NOT EXISTS "_grist_Cells" (id INTEGER PRIMARY KEY, "tableRef" INTEGER DEFAULT 0, "colRef" INTEGER DEFAULT 0, "rowId" INTEGER DEFAULT 0, "root" BOOLEAN DEFAULT 0, "parentId" INTEGER DEFAULT 0, "type" INTEGER DEFAULT 0, "content" TEXT DEFAULT '', "userRef" TEXT DEFAULT ''); CREATE TABLE IF NOT EXISTS "Table1" (id INTEGER PRIMARY KEY, "manualSort" NUMERIC DEFAULT 1e999, "A" BLOB DEFAULT NULL, "B" BLOB DEFAULT NULL, "C" BLOB DEFAULT NULL); CREATE INDEX _grist_Attachments_fileIdent ON _grist_Attachments(fileIdent); diff --git a/sandbox/grist/migrations.py b/sandbox/grist/migrations.py index 99842769..9fc6f025 100644 --- a/sandbox/grist/migrations.py +++ b/sandbox/grist/migrations.py @@ -1112,3 +1112,45 @@ def migration33(tdset): ] return tdset.apply_doc_actions(doc_actions) + +@migration(schema_version=34) +def migration34(tdset): + """" + Add pinned column to _grist_Filters and populate based on existing sections. + + When populating, pinned will be set to true for filters that either belong to + a section where the filter bar is toggled or a raw view section. + + From this version on, _grist_Views_section.options.filterBar is deprecated. + """ + doc_actions = [add_column('_grist_Filters', 'pinned', 'Bool')] + + tables = list(actions.transpose_bulk_action(tdset.all_tables['_grist_Tables'])) + sections = list(actions.transpose_bulk_action(tdset.all_tables['_grist_Views_section'])) + filters = list(actions.transpose_bulk_action(tdset.all_tables['_grist_Filters'])) + raw_section_ids = set(t.rawViewSectionRef for t in tables) + filter_bar_by_section_id = { + # Pre-migration, raw sections always showed the filter bar in the UI. Since we want + # existing raw section filters to continue appearing in the filter bar, we'll pretend + # here that raw sections have a filterBar value of True. Note that after this migration + # it will be possible for raw sections to have unpinned filters. + s.id: bool(s.id in raw_section_ids or safe_parse(s.options).get('filterBar', False)) + for s in sections + } + + # List of (filter_rec, pinned) pairs. + filter_updates = [] + for filter_rec in filters: + filter_updates.append(( + filter_rec, + filter_bar_by_section_id.get(filter_rec.viewSectionRef, False) + )) + + if filter_updates: + doc_actions.append(actions.BulkUpdateRecord( + '_grist_Filters', + [filter_rec.id for filter_rec, _ in filter_updates], + {'pinned': [pinned for _, pinned in filter_updates]}, + )) + + return tdset.apply_doc_actions(doc_actions) diff --git a/sandbox/grist/schema.py b/sandbox/grist/schema.py index 682f561d..2ab9a460 100644 --- a/sandbox/grist/schema.py +++ b/sandbox/grist/schema.py @@ -15,7 +15,7 @@ import six import actions -SCHEMA_VERSION = 33 +SCHEMA_VERSION = 34 def make_column(col_id, col_type, formula='', isFormula=False): return { @@ -315,7 +315,10 @@ def schema_create_actions(): # `excluded` string to an array of column values: # Ex1: { included: ['foo', 'bar'] } # Ex2: { excluded: ['apple', 'orange'] } - make_column("filter", "Text") + make_column("filter", "Text"), + # Filters can be pinned to the filter bar, which causes a button to be displayed + # that opens the filter menu when clicked. + make_column("pinned", "Bool"), ]), # Additional metadata for cells diff --git a/sandbox/grist/test_useractions.py b/sandbox/grist/test_useractions.py index 236bc7d7..19f3710f 100644 --- a/sandbox/grist/test_useractions.py +++ b/sandbox/grist/test_useractions.py @@ -954,14 +954,16 @@ class TestUserActions(test_engine.EngineTestCase): self.apply_user_action(['BulkAddRecord', '_grist_Filters', [None], { "viewSectionRef": [1], "colRef": [1], - "filter": [json.dumps({"included": ["b", "c"]})] + "filter": [json.dumps({"included": ["b", "c"]})], + "pinned": [True], }]) # Add the same filter for second column (to make sure it is not renamed) self.apply_user_action(['BulkAddRecord', '_grist_Filters', [None], { "viewSectionRef": [1], "colRef": [2], - "filter": [json.dumps({"included": ["b", "c"]})] + "filter": [json.dumps({"included": ["b", "c"]})], + "pinned": [False], }]) # Rename choices @@ -971,9 +973,9 @@ class TestUserActions(test_engine.EngineTestCase): # Test filters self.assertTableData('_grist_Filters', data=[ - ["id", "colRef", "filter", "setAutoRemove", "viewSectionRef"], - [1, 1, json.dumps({"included": ["z", "b"]}), None, 1], - [2, 2, json.dumps({"included": ["b", "c"]}), None, 1] + ["id", "colRef", "filter", "setAutoRemove", "viewSectionRef", "pinned"], + [1, 1, json.dumps({"included": ["z", "b"]}), None, 1, True], + [2, 2, json.dumps({"included": ["b", "c"]}), None, 1, False] ]) def test_add_or_update(self): diff --git a/static/icons/icons.css b/static/icons/icons.css index f91caa6f..447c9dfa 100644 --- a/static/icons/icons.css +++ b/static/icons/icons.css @@ -91,6 +91,7 @@ --icon-Pencil: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9Ii0yIC0yIDIwIDIwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxnIGNsYXNzPSJuYy1pY29uLXdyYXBwZXIiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzIxMjEyMSIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0xMyAuNUwxNS41IDMgNy41IDExIDQgMTIgNSA4LjV6TTExIDIuNUwxMy41IDUiLz48cGF0aCBkPSJNMTMuNSw5LjV2NSBjMCwwLjU1Mi0wLjQ0OCwxLTEsMWgtMTFjLTAuNTUyLDAtMS0wLjQ0OC0xLTF2LTExYzAtMC41NTIsMC40NDgtMSwxLTFoNSIvPjwvZz48L3N2Zz4='); --icon-PinBig: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTExLjgyOTcyODEsLTcuNjAyODA3MjdlLTEzIEw0LjE5Mjk3NDkxLC03LjU3NjE2MTkyZS0xMyBDMy42MjcyODk0OCwtNy41MzE3NTNlLTEzIDMuMjAzMDI1NDEsMC40MTAyNTY0MSAzLjIwMzAyNTQxLDAuOTU3MjY0OTU3IEMzLjIwMzAyNTQxLDEuNTA0MjczNSAzLjYyNzI4OTQ4LDEuOTE0NTI5OTEgNC4xOTI5NzQ5MSwxLjkxNDUyOTkxIEw1LjA0MTUwMzA0LDEuOTE0NTI5OTEgTDIuODQ5NDcyMDIsOS4wOTQwMTcwOSBMMS40MzUyNTg0Niw5LjA5NDAxNzA5IEMwLjg2OTU3MzAzNiw5LjA5NDAxNzA5IDAuNDQ1MzA4OTY3LDkuNTA0MjczNSAwLjQ0NTMwODk2NywxMC4wNTEyODIxIEMwLjQ0NTMwODk2NywxMC41OTgyOTA2IDAuODY5NTczMDM2LDExLjAwODU0NyAxLjQzNTI1ODQ2LDExLjAwODU0NyBMMy41NTY1Nzg4LDExLjAwODU0NyBMNi45NTA2OTEzNSwxMS4wMDg1NDcgTDcuMDIxNDAyMDMsMTYgTDkuMDAxMzAxMDIsMTYgTDkuMDAxMzAxMDIsMTAuOTQwMTcwOSBMMTQuNjU4MTU1MywxMC45NDAxNzA5IEMxNS4zNjUyNjIxLDEwLjk0MDE3MDkgMTUuNjQ4MTA0OCwxMC4yNTY0MTAzIDE1LjY0ODEwNDgsOS45ODI5MDU5OCBDMTUuNjQ4MTA0OCw5LjQzNTg5NzQ0IDE1LjIyMzg0MDcsOS4wMjU2NDEwMyAxNC42NTgxNTUzLDkuMDI1NjQxMDMgTDEzLjI0Mzk0MTcsOS4wMjU2NDEwMyBMMTEuMTIyNjIxNCwxLjkxNDUyOTkxIEwxMS45NzExNDk1LDEuOTE0NTI5OTEgQzEyLjgxOTY3NzYsMS45MTQ1Mjk5MSAxMi45NjEwOTksMS4yMzA3NjkyMyAxMi45NjEwOTksMC45NTcyNjQ5NTcgQzEyLjg5MDM4ODMsMC40Nzg2MzI0NzkgMTIuMzk1NDEzNiwtNy42NjQ5Nzk3NmUtMTMgMTEuODI5NzI4MSwtNy42MDI4MDcyN2UtMTMgWiBNMTEuMDUxOTEwNyw5LjA5NDAxNzA5IEw0Ljk3MDc5MjM3LDkuMDk0MDE3MDkgTDcuMDkyMTEyNzEsMS45ODI5MDU5OCBMOC45MzA1OTAzNCwxLjk4MjkwNTk4IEwxMS4wNTE5MTA3LDkuMDk0MDE3MDkgWiIgZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJub256ZXJvIi8+PC9zdmc+'); --icon-PinSmall: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTcuNSwxMSBMMi41LDExIEMyLjIyMzg1NzYzLDExIDIsMTAuNzc2MTQyNCAyLDEwLjUgQzIsMTAuMjIzODU3NiAyLjIyMzg1NzYzLDEwIDIuNSwxMCBMNC4yNSwxMCBMNS43NSw0IEw0LjUsNCBDNC4yMjM4NTc2Myw0IDQsMy43NzYxNDIzNyA0LDMuNSBDNCwzLjIyMzg1NzYzIDQuMjIzODU3NjMsMyA0LjUsMyBMMTEuNSwzIEMxMS43NzYxNDI0LDMgMTIsMy4yMjM4NTc2MyAxMiwzLjUgQzEyLDMuNzc2MTQyMzcgMTEuNzc2MTQyNCw0IDExLjUsNCBMMTAuMjUsNCBMMTEuNzUsMTAgTDEzLjUsMTAgQzEzLjc3NjE0MjQsMTAgMTQsMTAuMjIzODU3NiAxNCwxMC41IEMxNCwxMC43NzYxNDI0IDEzLjc3NjE0MjQsMTEgMTMuNSwxMSBMOC41LDExIEw4LjUsMTQgTDcuNSwxNCBMNy41LDExIFogTTUuMjgwNzc2NDEsMTAgTDEwLjcxOTIyMzYsMTAgTDkuMjE5MjIzNTksNCBMNi43ODA3NzY0MSw0IEw1LjI4MDc3NjQxLDEwIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg=='); + --icon-PinTilted: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIgNi4wMDAyNEwxMCAxNC4wMDAyTTkuMjAwMiAyTDE0LjAwMDIgNi44TTMuMzE2ODkgNy4zMTY2MUwxMC41MTQ1IDMuMzE0MjFNOC42ODMxMSAxMi42ODNMMTIuNjg1NSA1LjQ4NTM1TTQuOCAxMS4yMDAyTDIgMTQuMDAwMiIgc3Ryb2tlPSIjMTZCMzc4IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4='); --icon-Pivot: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMsMSBMMTMsMSBDMTQuMTA0NTY5NSwxIDE1LDEuODk1NDMwNSAxNSwzIEwxNSwxMyBDMTUsMTQuMTA0NTY5NSAxNC4xMDQ1Njk1LDE1IDEzLDE1IEwzLDE1IEMxLjg5NTQzMDUsMTUgMSwxNC4xMDQ1Njk1IDEsMTMgTDEsMyBDMSwxLjg5NTQzMDUgMS44OTU0MzA1LDEgMywxIFogTTUuMzc1LDQuNSBMNS4zNzUsNC43ODU0NjM0MSBMNy42MzQ2NTAxLDguMDIyNzA3MzIgTDUuMzc1LDExLjA5NDUxMjIgTDUuMzc1LDExLjUgTDEwLjYyNSwxMS41IEwxMC42MjUsMTAuMDMyODE3MSBMOS44MjY0OTkwMSwxMC43MjY0MTQ2IEw2LjkxOTczMTYxLDEwLjcyNjQxNDYgTDYuOTE5NzMxNjEsMTAuNDE2OTYzNCBMOC45MDI4MzMsNy42MTU4NTM2NiBMNi45MTk3MzE2MSw0Ljk5MDg1MzY2IEw5Ljc3NDMxMjEzLDQuOTg3MzUzNjYgTDEwLjYyNSw2LjA5NTMxNzA3IEwxMC42MjUsNC41IEw1LjM3NSw0LjUgWiIgZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJub256ZXJvIi8+PC9zdmc+'); --icon-PivotLight: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cmVjdCBzdHJva2U9IiM5Nzk3OTciIHg9Ii41IiB5PSIuNSIgd2lkdGg9IjE1IiBoZWlnaHQ9IjE1IiByeD0iMiIvPjxwYXRoIGZpbGw9IiM5Nzk3OTciIGQ9Ik01LjM3NSA0LjVMMTAuNjI1IDQuNSAxMC42MjUgNi4wOTUzMTcwNyA5Ljc3NDMxMjEzIDQuOTg3MzUzNjYgNi45MTk3MzE2MSA0Ljk5MDg1MzY2IDguOTAyODMzIDcuNjE1ODUzNjYgNi45MTk3MzE2MSAxMC40MTY5NjM0IDYuOTE5NzMxNjEgMTAuNzI2NDE0NiA5LjgyNjQ5OTAxIDEwLjcyNjQxNDYgMTAuNjI1IDEwLjAzMjgxNzEgMTAuNjI1IDExLjUgNS4zNzUgMTEuNSA1LjM3NSAxMS4wOTQ1MTIyIDcuNjM0NjUwMSA4LjAyMjcwNzMyIDUuMzc1IDQuNzg1NDYzNDF6Ii8+PC9nPjwvc3ZnPg=='); --icon-Plus: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTguNSw3LjUgTDEzLDcuNSBDMTMuMjc2MTQyNCw3LjUgMTMuNSw3LjcyMzg1NzYzIDEzLjUsOCBDMTMuNSw4LjI3NjE0MjM3IDEzLjI3NjE0MjQsOC41IDEzLDguNSBMOC41LDguNSBMOC41LDEzIEM4LjUsMTMuMjc2MTQyNCA4LjI3NjE0MjM3LDEzLjUgOCwxMy41IEM3LjcyMzg1NzYzLDEzLjUgNy41LDEzLjI3NjE0MjQgNy41LDEzIEw3LjUsOC41IEwzLDguNSBDMi43MjM4NTc2Myw4LjUgMi41LDguMjc2MTQyMzcgMi41LDggQzIuNSw3LjcyMzg1NzYzIDIuNzIzODU3NjMsNy41IDMsNy41IEw3LjUsNy41IEw3LjUsMyBDNy41LDIuNzIzODU3NjMgNy43MjM4NTc2MywyLjUgOCwyLjUgQzguMjc2MTQyMzcsMi41IDguNSwyLjcyMzg1NzYzIDguNSwzIEw4LjUsNy41IFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg=='); @@ -102,6 +103,7 @@ --icon-Remove: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTYsNCBMNiwyLjUgQzYsMi4yMjM4NTc2MyA2LjIyMzg1NzYzLDIgNi41LDIgTDkuNSwyIEM5Ljc3NjE0MjM3LDIgMTAsMi4yMjM4NTc2MyAxMCwyLjUgTDEwLDQgTDEzLjUsNCBDMTMuNzc2MTQyNCw0IDE0LDQuMjIzODU3NjMgMTQsNC41IEMxNCw0Ljc3NjE0MjM3IDEzLjc3NjE0MjQsNSAxMy41LDUgTDIuNSw1IEMyLjIyMzg1NzYzLDUgMiw0Ljc3NjE0MjM3IDIsNC41IEMyLDQuMjIzODU3NjMgMi4yMjM4NTc2Myw0IDIuNSw0IEw2LDQgWiBNNyw0IEw5LDQgTDksMyBMNywzIEw3LDQgWiBNMTEsNi41IEMxMSw2LjIyMzg1NzYzIDExLjIyMzg1NzYsNiAxMS41LDYgQzExLjc3NjE0MjQsNiAxMiw2LjIyMzg1NzYzIDEyLDYuNSBMMTIsMTIuNSBDMTIsMTMuMzI4NDI3MSAxMS4zMjg0MjcxLDE0IDEwLjUsMTQgTDUuNSwxNCBDNC42NzE1NzI4OCwxNCA0LDEzLjMyODQyNzEgNCwxMi41IEw0LDYuNSBDNCw2LjIyMzg1NzYzIDQuMjIzODU3NjMsNiA0LjUsNiBDNC43NzYxNDIzNyw2IDUsNi4yMjM4NTc2MyA1LDYuNSBMNSwxMi41IEM1LDEyLjc3NjE0MjQgNS4yMjM4NTc2MywxMyA1LjUsMTMgTDEwLjUsMTMgQzEwLjc3NjE0MjQsMTMgMTEsMTIuNzc2MTQyNCAxMSwxMi41IEwxMSw2LjUgWiIgZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJub256ZXJvIi8+PC9zdmc+'); --icon-Repl: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE0LjI5Mjg5MzIsMTIgTDEyLjE0NjQ0NjYsOS44NTM1NTMzOSBDMTEuOTUxMTg0NSw5LjY1ODI5MTI0IDExLjk1MTE4NDUsOS4zNDE3MDg3NiAxMi4xNDY0NDY2LDkuMTQ2NDQ2NjEgQzEyLjM0MTcwODgsOC45NTExODQ0NiAxMi42NTgyOTEyLDguOTUxMTg0NDYgMTIuODUzNTUzNCw5LjE0NjQ0NjYxIEwxNS44NTM1NTM0LDEyLjE0NjQ0NjYgQzE2LjA0ODgxNTUsMTIuMzQxNzA4OCAxNi4wNDg4MTU1LDEyLjY1ODI5MTIgMTUuODUzNTUzNCwxMi44NTM1NTM0IEwxMi44NTM1NTM0LDE1Ljg1MzU1MzQgQzEyLjY1ODI5MTIsMTYuMDQ4ODE1NSAxMi4zNDE3MDg4LDE2LjA0ODgxNTUgMTIuMTQ2NDQ2NiwxNS44NTM1NTM0IEMxMS45NTExODQ1LDE1LjY1ODI5MTIgMTEuOTUxMTg0NSwxNS4zNDE3MDg4IDEyLjE0NjQ0NjYsMTUuMTQ2NDQ2NiBMMTQuMjkyODkzMiwxMyBMMTIuMjk3LDEzIEMxMS4wMTE0MDE0LDEzIDkuNzg3MzEwNDYsMTIuNDUwMDg5NiA4LjkzMzM4NDQzLDExLjQ4OTI4MjEgTDcuOTA0Mzg0NDMsMTAuMzMyMjgyMSBDNy43MjA4NzAwOSwxMC4xMjU5Mzk5IDcuNzM5Mzc1Nyw5LjgwOTg5ODc4IDcuOTQ1NzE3ODgsOS42MjYzODQ0MyBDOC4xNTIwNjAwNiw5LjQ0Mjg3MDA5IDguNDY4MTAxMjIsOS40NjEzNzU3IDguNjUxNjE1NTcsOS42Njc3MTc4OCBMOS42ODA3Mjg4LDEwLjgyNDg0NTIgQzEwLjM0NTAyNTUsMTEuNTcyMjg4NCAxMS4yOTcxMDQsMTIgMTIuMjk3LDEyIEwxNC4yOTI4OTMyLDEyIFogTTAuNSw0IEMwLjIyMzg1NzYyNSw0IC01LjE5NTg0Mzc2ZS0xNCwzLjc3NjE0MjM3IC01LjE5NTg0Mzc2ZS0xNCwzLjUgQy01LjE5NTg0Mzc2ZS0xNCwzLjIyMzg1NzYzIDAuMjIzODU3NjI1LDMgMC41LDMgTDAuNzA0LDMgQzEuOTg5NTk4NTUsMyAzLjIxMzY4OTU0LDMuNTQ5OTEwNDIgNC4wNjc3NzU5NSw0LjUxMDg5ODMgTDUuMDk1Nzc1OTUsNS42Njc4OTgzIEM1LjI3OTE5MDY1LDUuODc0MzI5MDUgNS4yNjA1MzI0Niw2LjE5MDM2MTI0IDUuMDU0MTAxNyw2LjM3Mzc3NTk1IEM0Ljg0NzY3MDk1LDYuNTU3MTkwNjUgNC41MzE2Mzg3Niw2LjUzODUzMjQ2IDQuMzQ4MjI0MDUsNi4zMzIxMDE3IEwzLjMyMDI3MTIsNS4xNzUxNTQ3NiBDMi42NTU5NzQ1Miw0LjQyNzcxMTU5IDEuNzAzODk1OTksNCAwLjcwNCw0IEwwLjUsNCBaIE0xNC4yOTE4OTMyLDIuOTk5IEwxMi4xNDY0NDY2LDAuODUzNTUzMzkxIEMxMS45NTExODQ1LDAuNjU4MjkxMjQ1IDExLjk1MTE4NDUsMC4zNDE3MDg3NTUgMTIuMTQ2NDQ2NiwwLjE0NjQ0NjYwOSBDMTIuMzQxNzA4OCwtMC4wNDg4MTU1MzY1IDEyLjY1ODI5MTIsLTAuMDQ4ODE1NTM2NSAxMi44NTM1NTM0LDAuMTQ2NDQ2NjA5IEwxNS44MzQxOTg2LDMuMTI3MDkxODMgQzE1LjkzMTE4OSwzLjIxNDMwNTE3IDE1Ljk5Mzg4MDQsMy4zMzg5MTI3OCAxNS45OTk1NzU5LDMuNDc4MjE3NzQgQzE1Ljk5OTgzNzEsMy40ODUzNzc4MyAxNS45OTk5OTI4LDMuNDkyNDM5ODEgMTUuOTk5OTk5OCwzLjQ5OTUwMTkyIEMxNS45OTk5ODg0LDMuNTExMDYxMTMgMTUuOTk5NTg0OCwzLjUyMjUyODQ5IDE1Ljk5ODgwMTUsMy41MzM4OTE0MyBDMTUuOTkwOTk0NCwzLjY1MDMzMjU2IDE1Ljk0MjU1OTgsMy43NjQ1NDcwMiAxNS44NTM1NTM0LDMuODUzNTUzMzkgTDEyLjg1MzU1MzQsNi44NTM1NTMzOSBDMTIuNjU4MjkxMiw3LjA0ODgxNTU0IDEyLjM0MTcwODgsNy4wNDg4MTU1NCAxMi4xNDY0NDY2LDYuODUzNTUzMzkgQzExLjk1MTE4NDUsNi42NTgyOTEyNCAxMS45NTExODQ1LDYuMzQxNzA4NzYgMTIuMTQ2NDQ2Niw2LjE0NjQ0NjYxIEwxNC4yOTM4OTMyLDMuOTk5IEwxMi4yOTcsMy45OTkgQzExLjI5NzEwNCwzLjk5OSAxMC4zNDUwMjU1LDQuNDI2NzExNTkgOS42ODA3MTQ0NSw1LjE3NDE3MDkgTDQuMDY3NzI4OCwxMS40ODkxNTQ4IEMzLjIxMzY4OTU0LDEyLjQ1MDA4OTYgMS45ODk1OTg1NSwxMyAwLjcwNCwxMyBMMC41LDEzIEMwLjIyMzg1NzYyNSwxMyAtMi4yNjQ4NTQ5N2UtMTMsMTIuNzc2MTQyNCAtMi4yNjQ4NTQ5N2UtMTMsMTIuNSBDLTIuMjY0ODU0OTdlLTEzLDEyLjIyMzg1NzYgMC4yMjM4NTc2MjUsMTIgMC41LDEyIEwwLjcwNCwxMiBDMS43MDM4OTU5OSwxMiAyLjY1NTk3NDUyLDExLjU3MjI4ODQgMy4zMjAyODU1NSwxMC44MjQ4MjkxIEw4LjkzMzI3MTIsNC41MDk4NDUyNCBDOS43ODczMTA0NiwzLjU0ODkxMDQyIDExLjAxMTQwMTQsMi45OTkgMTIuMjk3LDIuOTk5IEwxNC4yOTE4OTMyLDIuOTk5IFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg=='); --icon-ResizePanel: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3QgZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJub256ZXJvIiB4PSI0IiB5PSIyIiB3aWR0aD0iMiIgaGVpZ2h0PSIxMiIgcng9IjEiLz48L3N2Zz4='); + --icon-Revert: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMuMTIzMTcgMy4wMDMzTDMuMDg3MjggNi45MTEwM0w2LjgxMzM2IDUuNjY0MzYiIHN0cm9rZT0iIzE2QjM3OCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTEzLjYxNTggMTAuMDc5OUMxNC4wMzY0IDguNjg0NzggMTMuODg1NSA3LjE3OTc0IDEzLjE5NjUgNS44OTU4NiAxMi41MDc0IDQuNjExOTggMTEuMzM2NSAzLjY1NDQzIDkuOTQxMzcgMy4yMzM4NSA4LjU0NjI3IDIuODEzMjcgNy4wNDEyMyAyLjk2NDExIDUuNzU3MzUgMy42NTMyIDQuNDczNDcgNC4zNDIzIDMuNTE1OTIgNS41MTMxOCAzLjA5NTM0IDYuOTA4MjhNNi40Mjk0MyAxMy41ODc2QzUuODQ4OCAxMy4zNjgzIDUuMzA5NjIgMTMuMDUyIDQuODM0ODQgMTIuNjUyMk0xMC4wMzk4IDEzLjY4MTZDOS40OTk1MyAxMy44NTM0IDguOTM1OTUgMTMuOTQwNyA4LjM2OTAzIDEzLjk0MDJIOC4yNDMwNE0xMi44ODY4IDExLjU3MjhDMTIuNTQ4MiAxMi4wNjA4IDEyLjEzMzIgMTIuNDkxMiAxMS42NTc3IDEyLjg0NzNNMy42NjIxNSAxMS4yODA0QzMuMzUxNDggMTAuNzY2NiAzLjEyNzc2IDEwLjIwNTEgMyA5LjYxODQxIiBzdHJva2U9IiMxNkIzNzgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg=='); --icon-RightAlign: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuNSw4LjUgQzIuMjIzODU3NjMsOC41IDIsOC4yNzYxNDIzNyAyLDggQzIsNy43MjM4NTc2MyAyLjIyMzg1NzYzLDcuNSAyLjUsNy41IEwxMy41LDcuNSBDMTMuNzc2MTQyNCw3LjUgMTQsNy43MjM4NTc2MyAxNCw4IEMxNCw4LjI3NjE0MjM3IDEzLjc3NjE0MjQsOC41IDEzLjUsOC41IEwyLjUsOC41IFogTTIuNSw0IEMyLjIyMzg1NzYzLDQgMiwzLjc3NjE0MjM3IDIsMy41IEMyLDMuMjIzODU3NjMgMi4yMjM4NTc2MywzIDIuNSwzIEwxMy41LDMgQzEzLjc3NjE0MjQsMyAxNCwzLjIyMzg1NzYzIDE0LDMuNSBDMTQsMy43NzYxNDIzNyAxMy43NzYxNDI0LDQgMTMuNSw0IEwyLjUsNCBaIE04LjUsMTMgQzguMjIzODU3NjMsMTMgOCwxMi43NzYxNDI0IDgsMTIuNSBDOCwxMi4yMjM4NTc2IDguMjIzODU3NjMsMTIgOC41LDEyIEwxMy41LDEyIEMxMy43NzYxNDI0LDEyIDE0LDEyLjIyMzg1NzYgMTQsMTIuNSBDMTQsMTIuNzc2MTQyNCAxMy43NzYxNDI0LDEzIDEzLjUsMTMgTDguNSwxMyBaIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L3N2Zz4='); --icon-Script: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0iZmVhdGhlciBmZWF0aGVyLWRhdGFiYXNlIj48cmVjdCB3aWR0aD0iMTkuMzI4IiBoZWlnaHQ9IjE5LjMyOCIgeD0iMi4zMzYiIHk9IjIuMzM2IiByeT0iNC4wMjYiIHJ4PSIzLjc0NSIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PGcgc3Ryb2tlLXdpZHRoPSIxLjU0MyIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciI+PHBhdGggZD0iTTE4LjczNDQ0OSA5LjAyMTI1NTZMNS4yNjU1NTEyIDkuMDA5NTM2OE0xOC43MzQ0NDkgMTQuNzM4NTg2TDUuMjY1NTUxMiAxNC43MjY4NjciIHRyYW5zZm9ybT0ibWF0cml4KC45Mzk2OSAwIDAgMS4wMDUzMSAuNzI0IC0uMDYzKSIvPjwvZz48L3N2Zz4='); --icon-Search: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTExLjQzNjIxMjcsMTAuNzI5MTA1OSBMMTQuODUzNTUzNCwxNC4xNDY0NDY2IEMxNS4wNDg4MTU1LDE0LjM0MTcwODggMTUuMDQ4ODE1NSwxNC42NTgyOTEyIDE0Ljg1MzU1MzQsMTQuODUzNTUzNCBDMTQuNjU4MjkxMiwxNS4wNDg4MTU1IDE0LjM0MTcwODgsMTUuMDQ4ODE1NSAxNC4xNDY0NDY2LDE0Ljg1MzU1MzQgTDEwLjcyOTEwNTksMTEuNDM2MjEyNyBDOS41OTIzMzg0OCwxMi40MTEwNDg3IDguMTE0OTQ3NzEsMTMgNi41LDEzIEMyLjkxMDE0OTEzLDEzIDAsMTAuMDg5ODUwOSAwLDYuNSBDMCwyLjkxMDE0OTEzIDIuOTEwMTQ5MTMsMCA2LjUsMCBDMTAuMDg5ODUwOSwwIDEzLDIuOTEwMTQ5MTMgMTMsNi41IEMxMyw4LjExNDk0NzcxIDEyLjQxMTA0ODcsOS41OTIzMzg0OCAxMS40MzYyMTI3LDEwLjcyOTEwNTkgWiBNMTAuNDA5NTc0NywxMC4zNjg0OTIxIEMxMS4zOTI4MzI1LDkuMzc0ODU3OCAxMiw4LjAwODMzNDY4IDEyLDYuNSBDMTIsMy40NjI0MzM4OCA5LjUzNzU2NjEyLDEgNi41LDEgQzMuNDYyNDMzODgsMSAxLDMuNDYyNDMzODggMSw2LjUgQzEsOS41Mzc1NjYxMiAzLjQ2MjQzMzg4LDEyIDYuNSwxMiBDOC4wMDgzMzQ2OCwxMiA5LjM3NDg1NzgsMTEuMzkyODMyNSAxMC4zNjg0OTIxLDEwLjQwOTU3NDcgQzEwLjM3NDkwMDEsMTAuNDAyMzg3OSAxMC4zODE1NTE2LDEwLjM5NTM0MTYgMTAuMzg4NDQ2NiwxMC4zODg0NDY2IEMxMC4zOTUzNDE2LDEwLjM4MTU1MTYgMTAuNDAyMzg3OSwxMC4zNzQ5MDAxIDEwLjQwOTU3NDcsMTAuMzY4NDkyMSBaIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L3N2Zz4='); diff --git a/static/locales/en.client.json b/static/locales/en.client.json index b2bd8434..e8d5e2ab 100644 --- a/static/locales/en.client.json +++ b/static/locales/en.client.json @@ -240,8 +240,8 @@ "Settings_savecommon": "Save as common settings", "Settings_revertcommon": "Revert to common settings" }, - "FilterBar": { - "AddFilter": "Add Filter" + "FilterConfig":{ + "AddColumn": "Add Column" }, "GridOptions": { "GridOptions": "Grid Options", @@ -469,6 +469,20 @@ "SwitchSites":"Switch Sites", "CreateNewTeamSite":"Create new team site" }, + "SortConfig":{ + "AddColumn": "Add Column", + "UpdateData": "Update Data", + "UseChoicePosition": "Use choice position", + "NaturalSort": "Natural sort", + "EmptyValuesLast": "Empty values last" + }, + "SortFilterConfig":{ + "Save": "Save", + "Revert": "Revert", + "Sort": "SORT", + "Filter": "FILTER", + "UpdateSortFilterSettings": "Update Sort & Filter settings" + }, "ThemeConfig": { "Appearance": "Appearance ", "SyncWithOS": "Switch appearance automatically to match system" @@ -533,7 +547,9 @@ "Customized":"(customized)", "Modified":"(modified)", "Empty":"(empty)", - "CustomOptions":"Custom options" + "CustomOptions":"Custom options", + "Sort": "SORT", + "Filter": "FILTER" }, "aclui": { "AccessRules": { @@ -687,13 +703,6 @@ "Rows": "Rows" }, "ViewConfigTab": { - "Save": "Save", - "Revert": "Revert", - "UpdateData": "Update Data", - "UseChoicePosition": "Use choice position", - "NaturalSort": "Natural sort", - "EmptyValuesLast": "Empty values last", - "AddColumn": "Add Column", "UnmarkOnDemandTitle": "Unmark table On-Demand?", "UnmarkOnDemandButton": "Unmark On-Demand", "UnmarkOnDemandText": "If you unmark table {{- table}}' as On-Demand, its data will be loaded into the calculation engine and will be available for use in formulas. For a big table, this may greatly increase load times.{{- br}}{{-br}}Changing this setting will reload the document for all users.", @@ -702,8 +711,6 @@ "MakeOnDemandText": "If you make table {{table}} On-Demand, its data will no longer be loaded into the calculation engine and will not be available for use in formulas. It will remain available for viewing and editing.", "AdvancedSettings": "Advanced settings", "BigTablesMayBeMarked": "Big tables may be marked as \"on-demand\" to avoid loading them into the data engine.", - "UpdateFilterSettings": "Update Filter settings", - "AddFilter": "Add Filter", "Form": "Form", "Compact": "Compact", "Blocks": "Blocks", diff --git a/static/locales/fr.client.json b/static/locales/fr.client.json index 23d0e9ae..e59e5f5c 100644 --- a/static/locales/fr.client.json +++ b/static/locales/fr.client.json @@ -238,8 +238,8 @@ "Settings_savecommon": "Save common settings", "Settings_revertcommon": "Revert common settings" }, - "FilterBar": { - "AddFilter": "Ajouter un filtre" + "FilterConfig": { + "AddColumn": "Ajouter une colonne" }, "GridOptions": { "GridOptions": "Options de la grille", @@ -466,6 +466,20 @@ "SwitchSites": "Changer d’espace", "CreateNewTeamSite": "Créer un nouvel espace d'équipe" }, + "SortConfig":{ + "AddColumn": "Ajouter une colonne", + "UpdateData": "Mettre à jour les données", + "UseChoicePosition": "Use choice position", + "NaturalSort": "Natural sort", + "EmptyValuesLast": "Valeurs vides en dernier" + }, + "SortFilterConfig":{ + "Save": "Enregistrer", + "Revert": "Restaurer", + "Sort": "TRI", + "Filter": "FILTRE", + "UpdateSortFilterSettings": "Mettre à jour le tri et le filtre" + }, "ThemeConfig": { "Appearance": "Apparence ", "SyncWithOS": "Adapter l'apparence au système" @@ -530,7 +544,9 @@ "Customized": "(personnalisé)", "Modified": "(modifié)", "Empty": "(vide)", - "CustomOptions": "Options personnalisées" + "CustomOptions": "Options personnalisées", + "Sort": "TRI", + "Filter": "FILTRE" }, "aclui": { "AccessRules": { @@ -684,13 +700,6 @@ "Rows": "Lignes" }, "ViewConfigTab": { - "Save": "Enregistrer", - "Revert": "Restaurer", - "UpdateData": "Mettre à jour les données", - "UseChoicePosition": "Use choice position", - "NaturalSort": "Natural sort", - "EmptyValuesLast": "Valeurs vides en dernier", - "AddColumn": "Ajouter une colonne", "UnmarkOnDemandTitle": "Unmark table On-Demand?", "UnmarkOnDemandButton": "Unmark On-Demand", "UnmarkOnDemandText": "If you unmark table {{- table}}' as On-Demand, its data will be loaded into the calculation engine and will be available for use in formulas. For a big table, this may greatly increase load times.{{- br}}{{-br}}Changing this setting will reload the document for all users.", @@ -699,8 +708,6 @@ "MakeOnDemandText": "If you make table {{table}} On-Demand, its data will no longer be loaded into the calculation engine and will not be available for use in formulas. It will remain available for viewing and editing.", "AdvancedSettings": "Paramètres avancés", "BigTablesMayBeMarked": "Big tables may be marked as \"on-demand\" to avoid loading them into the data engine.", - "UpdateFilterSettings": "Régler les filtres", - "AddFilter": "Ajouter un filtre", "Form": "Formulaire", "Compact": "Compact", "Blocks": "Blocs", diff --git a/static/ui-icons/UI/PinTilted.svg b/static/ui-icons/UI/PinTilted.svg new file mode 100644 index 00000000..134f814d --- /dev/null +++ b/static/ui-icons/UI/PinTilted.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/ui-icons/UI/Revert.svg b/static/ui-icons/UI/Revert.svg new file mode 100644 index 00000000..cd79d2e8 --- /dev/null +++ b/static/ui-icons/UI/Revert.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/test/client/models/ColumnFilter.ts b/test/client/models/ColumnFilter.ts index e96d3605..6d401ecc 100644 --- a/test/client/models/ColumnFilter.ts +++ b/test/client/models/ColumnFilter.ts @@ -1,4 +1,4 @@ -import {allInclusive, ColumnFilter} from 'app/client/models/ColumnFilter'; +import {ALL_INCLUSIVE_FILTER_JSON, ColumnFilter} from 'app/client/models/ColumnFilter'; import {GristObjCode} from 'app/plugin/GristData'; import {CellValue} from 'app/common/DocActions'; import {assert} from 'chai'; @@ -57,10 +57,10 @@ describe('ColumnFilter', function() { assert.isTrue(filter.includes('Carol')); }); - it('should generate an all-inclusive filter from empty string or null', async function() { + it('should generate an all-inclusive filter from empty string/object or null', async function() { const filter = new ColumnFilter(''); const defaultJson = filter.makeFilterJson(); - assert.equal(defaultJson, allInclusive); + assert.equal(defaultJson, ALL_INCLUSIVE_FILTER_JSON); filter.clear(); assert.equal(filter.makeFilterJson(), '{"included":[]}'); @@ -69,7 +69,10 @@ describe('ColumnFilter', function() { assert.equal(filter.makeFilterJson(), defaultJson); // Check that the string 'null' initializes properly - assert.equal(new ColumnFilter('null').makeFilterJson(), allInclusive); + assert.equal(new ColumnFilter('null').makeFilterJson(), ALL_INCLUSIVE_FILTER_JSON); + + // Check that the empty object initializes properly + assert.equal(new ColumnFilter('{}').makeFilterJson(), ALL_INCLUSIVE_FILTER_JSON); }); it('should generate a proper FilterFunc and JSON string', async function() { diff --git a/test/fixtures/docs/Hello.grist b/test/fixtures/docs/Hello.grist index 63c17756e2be2ab3704a572c1b8db80d2e0b2482..ff0579d6df21c20654b5b1b2d1047d0db5122099 100644 GIT binary patch delta 118 zcmZp8z})bFd4jZ{AOiz~4G_bC@I)PBM!}5usMC@2+V=H;cPC@DDk`}?>$`YE`$x;cjWgeVwH7G61z O1!(r3&0AM`y8!@4#~z6Q delta 92 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 literal 0 HcmV?d00001 diff --git a/test/nbrowser/ReferenceList.ts b/test/nbrowser/ReferenceList.ts index 4ab1e1e6..ce5ce710 100644 --- a/test/nbrowser/ReferenceList.ts +++ b/test/nbrowser/ReferenceList.ts @@ -345,9 +345,8 @@ describe('ReferenceList', function() { await driver.find('.test-config-sortAndFilter').click(); // Sort the Favorite Film column. - await driver.find('.test-vconfigtab-sort-add').click(); - await driver.findContent('.test-vconfigtab-sort-add-menu-row', /Favorite_Film/).click(); - await driver.find('.test-vconfigtab-sort-save').click(); + await gu.addColumnToSort('Favorite Film'); + await gu.saveSortConfig(); // Check that the records are sorted by display value. assert.deepEqual( diff --git a/test/nbrowser/gristUtils.ts b/test/nbrowser/gristUtils.ts index d010defa..c83278e4 100644 --- a/test/nbrowser/gristUtils.ts +++ b/test/nbrowser/gristUtils.ts @@ -1103,6 +1103,13 @@ export async function selectWidget( await waitForServer(); } +export async function changeWidget(type: string) { + await openWidgetPanel(); + await driver.findContent('.test-right-panel button', /Change Widget/).click(); + await selectWidget(type); + await waitForServer(); +} + /** * Toggle elem if not selected. Expects elem to be clickable and to have a class ending with * -selected when selected. @@ -1329,6 +1336,14 @@ export async function openWidgetPanel() { await driver.find('.test-right-tab-pagewidget').click(); } +/** + * Opens a Creator Panel on Widget/Table settings tab. + */ + export async function openColumnPanel() { + await toggleSidePanel('right', 'open'); + await driver.find('.test-right-tab-field').click(); +} + /** * Moves a column from a hidden to visible section. * Needs a visible Creator panel. @@ -1448,25 +1463,6 @@ export async function closeRawTable() { await driver.find('.test-raw-data-close-button').click(); } -/** - * Toggles (opens or closes) the filter bar for a section. - */ -export async function toggleFilterBar(goal: 'open'|'close'|'toggle' = 'toggle', - options: {section?: string|WebElement, save?: boolean} = {}) { - const isOpen = await driver.find('.test-filter-bar').isPresent(); - if ((goal === 'close') && !isOpen || - (goal === 'open') && isOpen ) { - return; - } - const menu = await openSectionMenu('sortAndFilter', options.section); - await menu.findContent('.grist-floating-menu > div', /Toggle Filter Bar/).find('.test-section-menu-btn').click(); - if (options.save) { - await menu.findContent('.grist-floating-menu button', /Save/).click(); - await waitForServer(); - } - await menu.sendKeys(Key.ESCAPE); -} - /** * Opens the section menu for a section, or the active section if no section is given. */ @@ -1521,13 +1517,17 @@ export async function deleteColumn(col: IColHeader|string) { /** * Sets the type of the currently selected field to value. */ -export async function setType(type: RegExp|string, options: {skipWait?: boolean} = {}) { + export async function setType(type: RegExp|string, options: {skipWait?: boolean, apply?: boolean} = {}) { await toggleSidePanel('right', 'open'); await driver.find('.test-right-tab-field').click(); await driver.find('.test-fbuilder-type-select').click(); type = typeof type === 'string' ? exactMatch(type) : type; await driver.findContentWait('.test-select-menu .test-select-row', type, 500).click(); - if (!options.skipWait) { await waitForServer(); } + if (!options.skipWait || options.apply) { await waitForServer(); } + if (options.apply) { + await driver.findWait('.test-type-transform-apply', 1000).click(); + await waitForServer(); + } } /** @@ -2504,58 +2504,52 @@ export async function setRefTable(table: string) { // Add column to sort. export async function addColumnToSort(colName: RegExp|string) { - await driver.find(".test-vconfigtab-sort-add").click(); - await driver.findContent(".test-vconfigtab-sort-add-menu-row", colName).click(); - await driver.findContentWait(".test-vconfigtab-sort-row", colName, 100); + await driver.find(".test-sort-config-add").click(); + await driver.findContent(".test-sort-config-add-menu-row", colName).click(); + await driver.findContentWait(".test-sort-config-row", colName, 100); } // Remove column from sort. export async function removeColumnFromSort(colName: RegExp|string) { - await findSortRow(colName).find(".test-vconfigtab-sort-remove").click(); + await findSortRow(colName).find(".test-sort-config-remove").click(); } // Toggle column sort order from ascending to descending, or vice-versa. export async function toggleSortOrder(colName: RegExp|string) { - await findSortRow(colName).find(".test-vconfigtab-sort-order").click(); -} - -// Change the column at the given sort position. -export async function changeSortDropdown(colName: RegExp|string, newColName: RegExp|string) { - await findSortRow(colName).find(".test-select-row").click(); - await driver.findContent("li .test-select-row", newColName).click(); + await findSortRow(colName).find(".test-sort-config-order").click(); } // Reset the sort to the last saved sort. export async function revertSortConfig() { - await driver.find(".test-vconfigtab-sort-reset").click(); + await driver.find(".test-sort-filter-config-revert").click(); } // Save the sort. export async function saveSortConfig() { - await driver.find(".test-vconfigtab-sort-save").click(); + await driver.find(".test-sort-filter-config-save").click(); await waitForServer(); } // Update the data positions to the given sort. export async function updateRowsBySort() { - await driver.find(".test-vconfigtab-sort-update").click(); + await driver.find(".test-sort-config-update").click(); await waitForServer(10000); } // Returns a WebElementPromise for the sort row of the given col name. export function findSortRow(colName: RegExp|string) { - return driver.findContent(".test-vconfigtab-sort-row", colName); + return driver.findContent(".test-sort-config-row", colName); } // Opens more sort options menu export async function openMoreSortOptions(colName: RegExp|string) { const row = await findSortRow(colName); - return row.find(".test-vconfigtab-sort-options-icon").click(); + return row.find(".test-sort-config-options-icon").click(); } // Selects one of the options in the more options menu. export async function toggleSortOption(option: SortOption) { - const label = await driver.find(`.test-vconfigtab-sort-option-${option} label`); + const label = await driver.find(`.test-sort-config-option-${option} label`); await label.click(); await waitForServer(); } @@ -2572,7 +2566,7 @@ export const SortOptions: ReadonlyArray = ["orderByChoice", "emptyLa export async function getSortOptions(): Promise { const options: SortOption[] = []; for(const option of SortOptions) { - const list = await driver.findAll(`.test-vconfigtab-sort-option-${option} input:checked`); + const list = await driver.findAll(`.test-sort-config-option-${option} input:checked`); if (list.length) { options.push(option); } @@ -2585,7 +2579,7 @@ export async function getSortOptions(): Promise { export async function getEnabledOptions(): Promise { const options: SortOption[] = []; for(const option of SortOptions) { - const list = await driver.findAll(`.test-vconfigtab-sort-option-${option}:not(.disabled)`); + const list = await driver.findAll(`.test-sort-config-option-${option}:not(.disabled)`); if (list.length) { options.push(option); } @@ -2647,6 +2641,48 @@ export async function filterBy(col: IColHeader|string, save: boolean, values: (s await waitForServer(); } +export interface PinnedFilter { + name: string; + hasUnsavedChanges: boolean; +} + +/** + * Returns a list of all pinned filters in the active section. + */ +export async function getPinnedFilters(): Promise { + const filterBar = await driver.find('.active_section .test-filter-bar'); + const allFilters = await filterBar.findAll('.test-filter-field', async (el) => { + const button = await el.find('.test-btn'); + const buttonClass = await button.getAttribute('class'); + return { + name: await el.getText(), + isPinned: await el.getCssValue('display') !== 'none', + hasUnsavedChanges: !/\b\w+-grayed\b/.test(buttonClass), + }; + }); + const pinnedFilters = allFilters.filter(({isPinned}) => isPinned); + return pinnedFilters.map(({name, hasUnsavedChanges}) => ({name, hasUnsavedChanges})); +} + +export interface FilterMenuValue { + checked: boolean; + value: string; + count: number; +} + +/** + * Returns a list of all values in the filter menu and their associated state. + */ +export async function getFilterMenuState(): Promise { + const items = await driver.findAll('.test-filter-menu-list > *'); + return await Promise.all(items.map(async item => { + const checked = (await item.find('input').getAttribute('checked')) === null ? false : true; + const value = await item.find('label').getText(); + const count = parseInt(await item.find('label + div').getText(), 10); + return {checked, value, count}; + })); +} + /** * Refresh browser and dismiss alert that is shown (for refreshing during edits). */