diff --git a/app/client/models/entities/ColumnRec.ts b/app/client/models/entities/ColumnRec.ts index 2ecdd471..8f0b4e09 100644 --- a/app/client/models/entities/ColumnRec.ts +++ b/app/client/models/entities/ColumnRec.ts @@ -235,17 +235,13 @@ function peekLabel(info: ColumnInfo): string { return typeof info.label === 'string' ? info.label : info.label.peek(); } -export function labelsOrder(a: ColumnInfo, b: ColumnInfo): number { - const left = peekLabel(a).toLowerCase(); - const right = peekLabel(b).toLowerCase(); - - // Order is as follows: - // - First columns with normal labels starting with a letter. - // - Second all columns starting with '_' (treated as private) - // - Third all columns starting with '#' (treated as private) - // - Rest. - if (left[0] === '_' && right[0] !== '_') { return 1; } - if (left[0] !== '_' && right[0] === '_') { return -1; } +/** + * Helper function to sort columns based on the label. Puts # columns at the end as this is + * treated as private columns. + */ +export function columnsOrder(a: ColumnInfo, b: ColumnInfo): number { + const left = peekLabel(a)?.toLowerCase() || ''; + const right = peekLabel(b)?.toLowerCase() || ''; if (left[0] === '#' && right[0] !== '#') { return 1; } if (left[0] !== '#' && right[0] === '#') { return -1; } return left.localeCompare(right); diff --git a/app/client/models/entities/ViewSectionRec.ts b/app/client/models/entities/ViewSectionRec.ts index c99849f0..37e12176 100644 --- a/app/client/models/entities/ViewSectionRec.ts +++ b/app/client/models/entities/ViewSectionRec.ts @@ -16,7 +16,7 @@ import { ViewFieldRec, ViewRec } from 'app/client/models/DocModel'; -import {BEHAVIOR, labelsOrder} from 'app/client/models/entities/ColumnRec'; +import {BEHAVIOR, columnsOrder} from 'app/client/models/entities/ColumnRec'; import * as modelUtil from 'app/client/models/modelUtil'; import {removeRule, RuleOwner} from 'app/client/models/RuleOwner'; import {LinkConfig} from 'app/client/ui/selectBy'; @@ -521,7 +521,7 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel): const savedFiltersByColRef = new Map(this._savedFilters().all().map(f => [f.colRef(), f])); const viewFieldsByColRef = new Map(this.viewFields().all().map(f => [f.origCol().getRowId(), f])); - return [...this.columns()].sort(labelsOrder).map(column => { + return [...this.columns()].sort(columnsOrder).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({ @@ -700,7 +700,7 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel): const included = new Set(this.viewFields().all().map((f) => f.column().origColRef())); return this.columns() .filter(c => !included.has(c.getRowId())) - .sort(labelsOrder); + .sort(columnsOrder); })); this.hasFocus = ko.pureComputed({ diff --git a/app/client/ui/CustomSectionConfig.ts b/app/client/ui/CustomSectionConfig.ts index 82245f3e..7c60548a 100644 --- a/app/client/ui/CustomSectionConfig.ts +++ b/app/client/ui/CustomSectionConfig.ts @@ -7,7 +7,7 @@ import {makeT} from 'app/client/lib/localization'; import {localStorageBoolObs} from 'app/client/lib/localStorageObs'; import {ColumnToMapImpl} from 'app/client/models/ColumnToMap'; import {ColumnRec, ViewSectionRec} from 'app/client/models/DocModel'; -import {labelsOrder} from 'app/client/models/entities/ColumnRec'; +import {columnsOrder} from 'app/client/models/entities/ColumnRec'; import { cssDeveloperLink, cssWidgetMetadata, @@ -81,7 +81,7 @@ class ColumnPicker extends Disposable { label: col.label.peek() || '', icon: 'FieldColumn' as const, })) - .sort(labelsOrder); + .sort(columnsOrder); // For optional mappings, add 'Blank' option but only if the value is set. @@ -205,8 +205,8 @@ class ColumnListPicker extends Disposable { menu(() => { const wrongTypeCount = notMapped.get().length - typedColumns.get().length; return [ - ...typedColumns.get() - .sort(labelsOrder) + ...typedColumns.get() // returns a temp table. + .sort(columnsOrder) .map((col) => menuItem( () => this._addColumn(col), col.label.peek(), diff --git a/app/client/ui/PageWidgetPicker.ts b/app/client/ui/PageWidgetPicker.ts index 8c64b736..bf1b5ede 100644 --- a/app/client/ui/PageWidgetPicker.ts +++ b/app/client/ui/PageWidgetPicker.ts @@ -4,6 +4,7 @@ import {FocusLayer} from 'app/client/lib/FocusLayer'; import {makeT} from 'app/client/lib/localization'; import {reportError} from 'app/client/models/AppModel'; import {ColumnRec, TableRec, ViewSectionRec} from 'app/client/models/DocModel'; +import {columnsOrder} from 'app/client/models/entities/ColumnRec'; import {PERMITTED_CUSTOM_WIDGETS} from "app/client/models/features"; import {linkId, NoLink} from 'app/client/ui/selectBy'; import {overflowTooltip, withInfoTooltip} from 'app/client/ui/tooltips'; @@ -32,7 +33,6 @@ import { import Popper from 'popper.js'; import {IOpenController, popupOpen, setPopupToCreateDom} from 'popweasel'; import without = require('lodash/without'); -import {labelsOrder} from 'app/client/models/entities/ColumnRec'; const t = makeT('PageWidgetPicker'); @@ -408,7 +408,7 @@ export class PageWidgetSelect extends Disposable { (use) => use(this._columns) .filter((col) => !col.isHiddenCol() && col.parentId() === use(this._value.table)), (cols) => cols ? - dom.forEach([...cols].sort(labelsOrder), (col) => + dom.forEach([...cols].sort(columnsOrder), (col) => cssEntry(cssIcon('FieldColumn'), cssFieldLabel(dom.text(col.label)), dom.on('click', () => this._toggleColumnId(col.id())), cssEntry.cls('-selected', (use) => use(this._value.columns).includes(col.id())), diff --git a/app/client/ui/SortConfig.ts b/app/client/ui/SortConfig.ts index ec675188..94d1ffc4 100644 --- a/app/client/ui/SortConfig.ts +++ b/app/client/ui/SortConfig.ts @@ -4,7 +4,7 @@ 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 {labelsOrder} from 'app/client/models/entities/ColumnRec'; +import {columnsOrder} from 'app/client/models/entities/ColumnRec'; import {ObjObservable} from 'app/client/models/modelUtil'; import {dropdownWithSearch} from 'app/client/ui/searchDropdown'; import {cssIcon, cssRow, cssSortFilterColumn} from 'app/client/ui/RightPanelStyles'; @@ -216,7 +216,7 @@ export class SortConfig extends Disposable { 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)).sort(labelsOrder); + return use(columns).filter(_col => !specRowIds.has(_col.value)).sort(columnsOrder); }); const {menuOptions} = this._options; return cssButtonRow(