diff --git a/app/client/models/entities/ColumnRec.ts b/app/client/models/entities/ColumnRec.ts index 3791baa6..8a4b8aa7 100644 --- a/app/client/models/entities/ColumnRec.ts +++ b/app/client/models/entities/ColumnRec.ts @@ -230,6 +230,23 @@ export function formatterForRec( return func(args); } +export function labelsOrder(a: ColumnRec, b: ColumnRec): number { + const left = a.label.peek().toLowerCase(); + const right = b.label.peek().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; } + if (left[0] === '#' && right[0] !== '#') { return 1; } + if (left[0] !== '#' && right[0] === '#') { return -1; } + return left.localeCompare(right); +} + + /** * A chat message. Either send by the user or by the AI. */ diff --git a/app/client/ui/CustomSectionConfig.ts b/app/client/ui/CustomSectionConfig.ts index 52e0f78c..4ce4bf77 100644 --- a/app/client/ui/CustomSectionConfig.ts +++ b/app/client/ui/CustomSectionConfig.ts @@ -7,6 +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 { cssDeveloperLink, cssWidgetMetadata, @@ -75,13 +76,12 @@ class ColumnPicker extends Disposable { void use(refreshTrigger); const columnsAsOptions: IOption[] = use(canBeMapped) + .sort(labelsOrder) .map((col) => ({ value: col.getRowId(), label: col.label.peek() || '', icon: 'FieldColumn', })); - // Order it by label. - columnsAsOptions.sort((a, b) => a.label.localeCompare(b.label)); // For optional mappings, add 'Blank' option but only if the value is set. // This option will allow to clear the selection. @@ -205,6 +205,7 @@ class ColumnListPicker extends Disposable { const wrongTypeCount = notMapped.get().length - typedColumns.get().length; return [ ...typedColumns.get() + .sort(labelsOrder) .map((col) => menuItem( () => this._addColumn(col), col.label.peek(), diff --git a/app/client/ui/PageWidgetPicker.ts b/app/client/ui/PageWidgetPicker.ts index dbd7cf84..8c64b736 100644 --- a/app/client/ui/PageWidgetPicker.ts +++ b/app/client/ui/PageWidgetPicker.ts @@ -32,6 +32,7 @@ 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'); @@ -407,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, (col) => + dom.forEach([...cols].sort(labelsOrder), (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())),