@lavalogic/scoria 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -58
- package/dist/Components/AccordionGroup.svelte +46 -42
- package/dist/Components/AccordionGroupButton.svelte +106 -106
- package/dist/Components/AccordionGroupButtonProps.d.ts +27 -8
- package/dist/Components/AccordionGroupProps.d.ts +10 -2
- package/dist/Components/Action.svelte +137 -139
- package/dist/Components/ActionProps.d.ts +23 -3
- package/dist/Components/AlertModal.svelte +176 -125
- package/dist/Components/AppointmentCalendar/AppointmentCalendar.svelte +1126 -0
- package/dist/Components/AppointmentCalendar/AppointmentCalendar.svelte.d.ts +4 -0
- package/dist/Components/AppointmentCalendar/AppointmentCalendarProps.d.ts +92 -0
- package/dist/Components/AppointmentCalendar/AppointmentCalendarProps.js +12 -0
- package/dist/Components/Base/BaseModal.svelte +172 -0
- package/dist/Components/Base/BaseModal.svelte.d.ts +4 -0
- package/dist/Components/Base/BaseModalProps.d.ts +73 -0
- package/dist/Components/Base/BaseModalProps.js +1 -0
- package/dist/Components/Base/ContextWrapper.svelte +72 -50
- package/dist/Components/Base/ContextWrapper.svelte.d.ts +24 -5
- package/dist/Components/Base/ContextWrapperProps.d.ts +22 -0
- package/dist/Components/Base/Pagination.svelte +198 -190
- package/dist/Components/Base/Pagination.svelte.d.ts +12 -2
- package/dist/Components/Base/PaginationProps.d.ts +60 -3
- package/dist/Components/Base/Snippets.svelte +36 -29
- package/dist/Components/Base/Snippets.svelte.d.ts +5 -1
- package/dist/Components/BigRadioSet.svelte +39 -30
- package/dist/Components/BigRadioSetProps.d.ts +17 -1
- package/dist/Components/Bubble.svelte +110 -77
- package/dist/Components/Bubble.svelte.d.ts +23 -2
- package/dist/Components/BubbleProps.d.ts +55 -4
- package/dist/Components/Button.svelte +209 -197
- package/dist/Components/ButtonProps.d.ts +66 -1
- package/dist/Components/Checkbox.svelte +100 -72
- package/dist/Components/Checkbox.svelte.d.ts +1 -12
- package/dist/Components/CheckboxProps.d.ts +27 -0
- package/dist/Components/CheckboxProps.js +1 -0
- package/dist/Components/CollapsibleCard.svelte +114 -83
- package/dist/Components/CollapsibleCardProps.d.ts +22 -1
- package/dist/Components/Contexts/ContextMenu.svelte +178 -157
- package/dist/Components/Contexts/ContextMenu.svelte.d.ts +26 -0
- package/dist/Components/Contexts/ContextMenuDivider.svelte +19 -11
- package/dist/Components/Contexts/ContextMenuDivider.svelte.d.ts +9 -0
- package/dist/Components/Contexts/ContextMenuOption.svelte +66 -57
- package/dist/Components/Contexts/ContextMenuOption.svelte.d.ts +12 -1
- package/dist/Components/Contexts/ContextMenuOptionProps.d.ts +34 -2
- package/dist/Components/Contexts/Toast.svelte +88 -64
- package/dist/Components/Contexts/Toast.svelte.d.ts +14 -0
- package/dist/Components/Contexts/ToastContainer.svelte +68 -49
- package/dist/Components/Contexts/ToastContainer.svelte.d.ts +14 -0
- package/dist/Components/Contexts/ToastProps.d.ts +14 -1
- package/dist/Components/Contexts/Tooltip.svelte +47 -36
- package/dist/Components/Contexts/Tooltip.svelte.d.ts +10 -0
- package/dist/Components/DataMatrixIcon.svelte +113 -42
- package/dist/Components/DataMatrixIconProps.d.ts +73 -1
- package/dist/Components/DateInput.svelte +144 -109
- package/dist/Components/DateInput.svelte.d.ts +1 -1
- package/dist/Components/DateOnlyInputProps.d.ts +31 -5
- package/dist/Components/DatePicker.svelte +301 -313
- package/dist/Components/DatePickerProps.d.ts +18 -1
- package/dist/Components/DateTimeInputProps.d.ts +30 -5
- package/dist/Components/DesktopModal.svelte +90 -119
- package/dist/Components/DesktopModalProps.d.ts +5 -4
- package/dist/Components/Dial.svelte +213 -151
- package/dist/Components/DialProps.d.ts +74 -2
- package/dist/Components/DimensionInput.svelte +171 -185
- package/dist/Components/DimensionInput.svelte.d.ts +1 -1
- package/dist/Components/DimensionInputProps.d.ts +39 -4
- package/dist/Components/DragMenu/DragMenuHolder.svelte +119 -107
- package/dist/Components/DragMenu/DragMenuHolderProps.d.ts +30 -1
- package/dist/Components/DragMenu/DraggableCard.svelte +77 -41
- package/dist/Components/DragMenu/DraggableCardProps.d.ts +27 -1
- package/dist/Components/FileCard.svelte +24 -20
- package/dist/Components/FileCardProps.d.ts +17 -1
- package/dist/Components/FileUpload.svelte +236 -196
- package/dist/Components/FileUploadProps.d.ts +34 -4
- package/dist/Components/GridInput.svelte +201 -191
- package/dist/Components/GridInput.svelte.d.ts +5 -5
- package/dist/Components/GridInputProps.d.ts +4 -1
- package/dist/Components/GridInputSet.svelte +31 -24
- package/dist/Components/GridInputSet.svelte.d.ts +5 -5
- package/dist/Components/GridInputSetProps.d.ts +4 -2
- package/dist/Components/HorizontalTabGroup.svelte +81 -79
- package/dist/Components/HorizontalTabGroupButton.svelte +51 -103
- package/dist/Components/HorizontalTabGroupProps.d.ts +15 -2
- package/dist/Components/Icon.svelte +144 -103
- package/dist/Components/IconProps.d.ts +54 -6
- package/dist/Components/Icons.d.ts +8 -0
- package/dist/Components/Icons.js +591 -572
- package/dist/Components/LoadingAnimation.svelte +39 -39
- package/dist/Components/LoadingAnimation.svelte.d.ts +1 -3
- package/dist/Components/LoadingAnimationProps.d.ts +16 -0
- package/dist/Components/LoadingAnimationProps.js +1 -0
- package/dist/Components/LoadingModal.svelte +48 -50
- package/dist/Components/LoadingModal.svelte.d.ts +1 -4
- package/dist/Components/LoadingModalProps.d.ts +17 -0
- package/dist/Components/LoadingModalProps.js +1 -0
- package/dist/Components/LoadingOverlay.svelte +28 -30
- package/dist/Components/LoadingOverlay.svelte.d.ts +1 -5
- package/dist/Components/LoadingOverlayProps.d.ts +28 -0
- package/dist/Components/LoadingOverlayProps.js +1 -0
- package/dist/Components/Modal.svelte +250 -255
- package/dist/Components/Modal.svelte.d.ts +1 -25
- package/dist/Components/ModalProps.d.ts +65 -0
- package/dist/Components/ModalProps.js +1 -0
- package/dist/Components/MultiSelect.svelte +238 -226
- package/dist/Components/MultiSelectCommonProps.d.ts +33 -4
- package/dist/Components/MultiSelectPropsAsObject.d.ts +5 -0
- package/dist/Components/MultiSelectPropsAsPrimitive.d.ts +6 -0
- package/dist/Components/Nav/CallbackButtonWithIcon.d.ts +22 -1
- package/dist/Components/Nav/Nav.svelte +115 -110
- package/dist/Components/Nav/Nav.svelte.d.ts +23 -2
- package/dist/Components/Nav/NavProps.d.ts +50 -3
- package/dist/Components/Nav/NavServiceMenuList.svelte +44 -35
- package/dist/Components/Nav/NavServiceMenuListProps.d.ts +25 -1
- package/dist/Components/Nav/NavTab.svelte +62 -91
- package/dist/Components/Nav/NavTab.svelte.d.ts +23 -2
- package/dist/Components/Nav/NavTabProps.d.ts +16 -2
- package/dist/Components/Nav/ServiceMenuItem.svelte +101 -92
- package/dist/Components/Nav/ServicesNav.svelte +96 -70
- package/dist/Components/Nav/ServicesNav.svelte.d.ts +23 -2
- package/dist/Components/Nav/ServicesNavProps.d.ts +20 -4
- package/dist/Components/Nav/SideMenuItemProps.d.ts +16 -0
- package/dist/Components/NumberInput.svelte +155 -145
- package/dist/Components/NumberInputProps.d.ts +37 -0
- package/dist/Components/PageHeading.svelte +25 -36
- package/dist/Components/PageHeading.svelte.d.ts +1 -6
- package/dist/Components/PageHeadingProps.d.ts +23 -0
- package/dist/Components/PageHeadingProps.js +1 -0
- package/dist/Components/PasswordInput.svelte +85 -73
- package/dist/Components/PasswordInput.svelte.d.ts +2 -2
- package/dist/Components/PasswordInputProps.d.ts +21 -0
- package/dist/Components/PasswordInputProps.js +1 -0
- package/dist/Components/Portal.svelte +36 -25
- package/dist/Components/PortalProps.d.ts +20 -1
- package/dist/Components/ProgressBubble.svelte +129 -84
- package/dist/Components/ProgressBubbleProps.d.ts +23 -2
- package/dist/Components/QuerySelect.svelte +384 -440
- package/dist/Components/QuerySelect.svelte.d.ts +2 -29
- package/dist/Components/QuerySelectProps.d.ts +66 -0
- package/dist/Components/QuerySelectProps.js +1 -0
- package/dist/Components/SingleSelect.svelte +268 -290
- package/dist/Components/SingleSelectCommonProps.d.ts +43 -5
- package/dist/Components/SingleSelectPropsAsObject.d.ts +7 -0
- package/dist/Components/SingleSelectPropsAsPrimitive.d.ts +8 -0
- package/dist/Components/StepButton.svelte +70 -120
- package/dist/Components/StepForm.svelte +147 -147
- package/dist/Components/StepFormProps.d.ts +16 -2
- package/dist/Components/Switch.svelte +84 -47
- package/dist/Components/Switch.svelte.d.ts +1 -1
- package/dist/Components/SwitchProps.d.ts +46 -0
- package/dist/Components/Table/Body/HeightData.d.ts +1 -6
- package/dist/Components/Table/Body/HeightData.js +8 -0
- package/dist/Components/Table/Body/QuickSearchCell.svelte +697 -641
- package/dist/Components/Table/Body/QuickSearchCell.svelte.d.ts +12 -11
- package/dist/Components/Table/Body/QuickSearchCellProps.d.ts +6 -0
- package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +68 -34
- package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte.d.ts +24 -2
- package/dist/Components/Table/Body/Rows/ColumnHighlightProps.d.ts +3 -0
- package/dist/Components/Table/Body/Rows/ColumnList.svelte +66 -56
- package/dist/Components/Table/Body/Rows/ColumnList.svelte.d.ts +12 -11
- package/dist/Components/Table/Body/Rows/ColumnListProps.d.ts +1 -0
- package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +515 -182
- package/dist/Components/Table/Body/Rows/ColumnListRow.svelte.d.ts +12 -11
- package/dist/Components/Table/Body/Rows/ColumnListRowProps.d.ts +5 -0
- package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +79 -49
- package/dist/Components/Table/Body/Rows/Columns/AccessorComponentProps.d.ts +12 -0
- package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +203 -191
- package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte.d.ts +12 -12
- package/dist/Components/Table/Body/Rows/Columns/ActionsColumn.d.ts +9 -0
- package/dist/Components/Table/Body/Rows/Columns/ActionsTableCell.d.ts +5 -0
- package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +150 -115
- package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte.d.ts +14 -13
- package/dist/Components/Table/Body/Rows/Columns/BubbleCellProps.d.ts +12 -0
- package/dist/Components/Table/Body/Rows/Columns/BubbleCellProps.js +1 -0
- package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +149 -129
- package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +149 -126
- package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte.d.ts +12 -11
- package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCellProps.d.ts +7 -0
- package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +142 -108
- package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte.d.ts +17 -11
- package/dist/Components/Table/Body/Rows/Columns/RowSelectionColumn.d.ts +8 -0
- package/dist/Components/Table/Body/Rows/Columns/RowSelectionTableCell.d.ts +5 -0
- package/dist/Components/Table/Body/Rows/Columns/SelectColumn.d.ts +11 -0
- package/dist/Components/Table/Body/Rows/Columns/SelectTableCell.d.ts +11 -0
- package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +87 -72
- package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte.d.ts +11 -0
- package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +148 -137
- package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte.d.ts +13 -18
- package/dist/Components/Table/Body/Rows/Columns/TableCheckboxProps.d.ts +17 -0
- package/dist/Components/Table/Body/Rows/Columns/TableCheckboxProps.js +1 -0
- package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +159 -144
- package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte.d.ts +1 -7
- package/dist/Components/Table/Body/Rows/Columns/TableColumnProps.d.ts +16 -0
- package/dist/Components/Table/Body/Rows/Columns/TableColumnProps.js +1 -0
- package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +279 -249
- package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte.d.ts +12 -11
- package/dist/Components/Table/Body/Rows/Columns/TableDatePickerProps.d.ts +9 -0
- package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +249 -370
- package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte.d.ts +20 -12
- package/dist/Components/Table/Body/Rows/Columns/TableQueryCell.d.ts +8 -0
- package/dist/Components/Table/Body/Rows/Columns/TableQueryColumn.d.ts +9 -0
- package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +291 -354
- package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte.d.ts +12 -11
- package/dist/Components/Table/Body/Rows/Columns/TableQuerySelectProps.d.ts +8 -0
- package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +201 -189
- package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte.d.ts +2 -5
- package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckboxProps.d.ts +14 -0
- package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckboxProps.js +1 -0
- package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +263 -325
- package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte.d.ts +13 -12
- package/dist/Components/Table/Body/Rows/Columns/TableSelectProps.d.ts +6 -0
- package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +230 -381
- package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte.d.ts +12 -11
- package/dist/Components/Table/Body/Rows/Columns/TableTextInputProps.d.ts +6 -0
- package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +187 -177
- package/dist/Components/Table/Body/Rows/Columns/_helpers/useAsyncEditable.svelte.d.ts +104 -0
- package/dist/Components/Table/Body/Rows/Columns/_helpers/useAsyncEditable.svelte.js +215 -0
- package/dist/Components/Table/Body/Rows/TableRow.svelte +331 -263
- package/dist/Components/Table/Body/Rows/TableRow.svelte.d.ts +2 -10
- package/dist/Components/Table/Body/Rows/TableRowProps.d.ts +11 -2
- package/dist/Components/Table/Body/TableBody.svelte +198 -81
- package/dist/Components/Table/Body/TableBody.svelte.d.ts +15 -22
- package/dist/Components/Table/Body/TableBodyProps.d.ts +9 -7
- package/dist/Components/Table/ColumnDefinitions/ExampleItemColumnDefRepository.svelte.js +18 -12
- package/dist/Components/Table/ColumnFactory.svelte.d.ts +43 -0
- package/dist/Components/Table/ColumnFactory.svelte.js +339 -0
- package/dist/Components/Table/Headers/HighlightAllHeader.svelte +41 -39
- package/dist/Components/Table/Headers/HighlightAllHeader.svelte.d.ts +12 -11
- package/dist/Components/Table/Headers/SelectAllHeader.svelte +30 -28
- package/dist/Components/Table/Headers/SelectAllHeader.svelte.d.ts +12 -11
- package/dist/Components/Table/Headers/TableHead.svelte +434 -294
- package/dist/Components/Table/Headers/TableHead.svelte.d.ts +13 -16
- package/dist/Components/Table/Headers/TableHeadProps.d.ts +10 -0
- package/dist/Components/Table/Headers/TableHeadProps.js +1 -0
- package/dist/Components/Table/Misc/ColumnPanel.svelte +154 -96
- package/dist/Components/Table/Misc/ColumnPanel.svelte.d.ts +12 -11
- package/dist/Components/Table/Misc/ColumnPanelModal.svelte +112 -99
- package/dist/Components/Table/Misc/ColumnPanelModal.svelte.d.ts +12 -11
- package/dist/Components/Table/Misc/ColumnPanelModalProps.d.ts +5 -0
- package/dist/Components/Table/Misc/FilterInput.svelte +545 -556
- package/dist/Components/Table/Misc/FilterInput.svelte.d.ts +12 -11
- package/dist/Components/Table/Misc/FilterPanel.svelte +409 -51
- package/dist/Components/Table/Misc/FilterPanel.svelte.d.ts +12 -11
- package/dist/Components/Table/Misc/FilterPanelModal.svelte +30 -77
- package/dist/Components/Table/Misc/FilterPanelModal.svelte.d.ts +2 -23
- package/dist/Components/Table/Misc/SidePanel.svelte +35 -32
- package/dist/Components/Table/Misc/SidePanel.svelte.d.ts +1 -7
- package/dist/Components/Table/Misc/SidePanelProps.d.ts +17 -0
- package/dist/Components/Table/Misc/SidePanelProps.js +1 -0
- package/dist/Components/Table/Misc/TableFooter.svelte +315 -306
- package/dist/Components/Table/Misc/TableFooter.svelte.d.ts +13 -14
- package/dist/Components/Table/Misc/TableFooterProps.d.ts +8 -0
- package/dist/Components/Table/Misc/TableFooterProps.js +1 -0
- package/dist/Components/Table/Misc/TableHorizontalBar.svelte +126 -157
- package/dist/Components/Table/Misc/TableHorizontalBar.svelte.d.ts +12 -11
- package/dist/Components/Table/Misc/TableSidebar.svelte +158 -177
- package/dist/Components/Table/Misc/TableSidebar.svelte.d.ts +12 -11
- package/dist/Components/Table/Misc/ToolboxPanel.svelte +147 -81
- package/dist/Components/Table/Misc/ToolboxPanel.svelte.d.ts +12 -11
- package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +107 -100
- package/dist/Components/Table/Misc/ToolboxPanelModal.svelte.d.ts +12 -11
- package/dist/Components/Table/Misc/ToolboxPanelModalProps.d.ts +5 -0
- package/dist/Components/Table/Misc/numberDateFilterTypes.d.ts +7 -4
- package/dist/Components/Table/Misc/numberDateFilterTypes.js +8 -3
- package/dist/Components/Table/Misc/stringFilterTypes.d.ts +6 -4
- package/dist/Components/Table/Misc/stringFilterTypes.js +7 -3
- package/dist/Components/Table/NestedTable.svelte +132 -88
- package/dist/Components/Table/NestedTable.svelte.d.ts +2 -10
- package/dist/Components/Table/NestedTableProps.d.ts +25 -0
- package/dist/Components/Table/NestedTableProps.js +1 -0
- package/dist/Components/Table/SubApis.svelte.d.ts +23 -0
- package/dist/Components/Table/SubApis.svelte.js +359 -0
- package/dist/Components/Table/Table.svelte +694 -519
- package/dist/Components/Table/Table.svelte.d.ts +24 -1
- package/dist/Components/Table/TableHelpers.svelte.d.ts +58 -0
- package/dist/Components/Table/TableHelpers.svelte.js +88 -0
- package/dist/Components/Table/Types/Columns/Column.d.ts +10 -0
- package/dist/Components/Table/Types/Columns/ColumnPinningState.d.ts +12 -0
- package/dist/Components/Table/Types/Columns/ColumnSizingState.d.ts +8 -0
- package/dist/Components/Table/Types/Columns/ColumnType.d.ts +17 -0
- package/dist/Components/Table/Types/Columns/ColumnType.js +18 -1
- package/dist/Components/Table/Types/Columns/DateColumn.d.ts +6 -0
- package/dist/Components/Table/Types/Columns/DateTableCell.d.ts +7 -0
- package/dist/Components/Table/Types/Columns/Definitions/AccessorFn.d.ts +13 -0
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/AccessorDef.svelte.d.ts +28 -3
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/AccessorDef.svelte.js +15 -0
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/AccessorType.d.ts +10 -0
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/AccessorType.js +11 -1
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/CheckboxDef.svelte.d.ts +23 -2
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/CheckboxDef.svelte.js +9 -2
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/DateInputDef.svelte.d.ts +21 -2
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/DateInputDef.svelte.js +8 -0
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/NumberInputDef.svelte.d.ts +29 -3
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/NumberInputDef.svelte.js +11 -0
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/QueryDef.svelte.d.ts +23 -0
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/QueryDef.svelte.js +8 -0
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/SelectDef.svelte.d.ts +31 -0
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/SelectDef.svelte.js +17 -3
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/TextInputDef.svelte.d.ts +21 -2
- package/dist/Components/Table/Types/Columns/Definitions/Accessors/TextInputDef.svelte.js +7 -0
- package/dist/Components/Table/Types/Columns/Definitions/Actions/ActionsDef.svelte.d.ts +29 -1
- package/dist/Components/Table/Types/Columns/Definitions/Actions/ActionsDef.svelte.js +17 -3
- package/dist/Components/Table/Types/Columns/Definitions/ColumnDef.svelte.d.ts +122 -11
- package/dist/Components/Table/Types/Columns/Definitions/ColumnDef.svelte.js +77 -11
- package/dist/Components/Table/Types/Columns/Definitions/ColumnDefSet.d.ts +9 -0
- package/dist/Components/Table/Types/Columns/Definitions/Display/AbstractDisplayDef.svelte.d.ts +41 -1
- package/dist/Components/Table/Types/Columns/Definitions/Display/AbstractDisplayDef.svelte.js +16 -0
- package/dist/Components/Table/Types/Columns/Definitions/Display/BubbleDef.svelte.d.ts +19 -1
- package/dist/Components/Table/Types/Columns/Definitions/Display/BubbleDef.svelte.js +12 -1
- package/dist/Components/Table/Types/Columns/Definitions/Display/DisplayDef.svelte.d.ts +11 -0
- package/dist/Components/Table/Types/Columns/Definitions/Display/DisplayDef.svelte.js +6 -3
- package/dist/Components/Table/Types/Columns/Definitions/Display/ProgressBarDef.svelte.d.ts +19 -0
- package/dist/Components/Table/Types/Columns/Definitions/Display/ProgressBarDef.svelte.js +25 -1
- package/dist/Components/Table/Types/Columns/Definitions/Display/ValidityDef.svelte.d.ts +26 -0
- package/dist/Components/Table/Types/Columns/Definitions/Display/ValidityDef.svelte.js +20 -5
- package/dist/Components/Table/Types/Columns/Definitions/Expand/ExpandDef.svelte.d.ts +43 -7
- package/dist/Components/Table/Types/Columns/Definitions/Expand/ExpandDef.svelte.js +18 -3
- package/dist/Components/Table/Types/Columns/Definitions/FilterOnly/FilterOnlyDef.svelte.d.ts +19 -5
- package/dist/Components/Table/Types/Columns/Definitions/FilterOnly/FilterOnlyDef.svelte.js +16 -5
- package/dist/Components/Table/Types/Columns/Definitions/JSONColumnDefSet.d.ts +14 -0
- package/dist/Components/Table/Types/Columns/Definitions/RowSelection/RowSelectionDef.svelte.d.ts +34 -2
- package/dist/Components/Table/Types/Columns/Definitions/RowSelection/RowSelectionDef.svelte.js +30 -7
- package/dist/Components/Table/Types/Columns/Definitions/ValidationFn.d.ts +17 -0
- package/dist/Components/Table/Types/Columns/DefsWrapper.d.ts +10 -0
- package/dist/Components/Table/Types/Columns/DisplayType.d.ts +13 -0
- package/dist/Components/Table/Types/Columns/DisplayType.js +14 -1
- package/dist/Components/Table/Types/Columns/ExpandColumn.d.ts +5 -0
- package/dist/Components/Table/Types/Columns/ExpandTableCell.d.ts +7 -0
- package/dist/Components/Table/Types/Columns/JSONColumnPinningState.d.ts +6 -0
- package/dist/Components/Table/Types/Columns/ProgressBarColumn.d.ts +5 -0
- package/dist/Components/Table/Types/Columns/ProgressBarTableCell.d.ts +9 -0
- package/dist/Components/Table/Types/Columns/TableActionButton.d.ts +12 -0
- package/dist/Components/Table/Types/Columns/TableCell.d.ts +16 -2
- package/dist/Components/Table/Types/Columns/VisibilityState.d.ts +12 -0
- package/dist/Components/Table/Types/Context/ColumnLayoutState.svelte.d.ts +173 -0
- package/dist/Components/Table/Types/Context/ColumnLayoutState.svelte.js +241 -0
- package/dist/Components/Table/Types/Context/FilteringState.svelte.d.ts +166 -0
- package/dist/Components/Table/Types/Context/FilteringState.svelte.js +180 -0
- package/dist/Components/Table/Types/Context/FocusState.svelte.d.ts +316 -0
- package/dist/Components/Table/Types/Context/FocusState.svelte.js +751 -0
- package/dist/Components/Table/Types/Context/ITable.d.ts +10 -0
- package/dist/Components/Table/Types/Context/PreferencesState.svelte.d.ts +242 -0
- package/dist/Components/Table/Types/Context/PreferencesState.svelte.js +317 -0
- package/dist/Components/Table/Types/Context/ReadonlyRowModel.d.ts +14 -3
- package/dist/Components/Table/Types/Context/Row.d.ts +18 -0
- package/dist/Components/Table/Types/Context/RowModel.d.ts +11 -0
- package/dist/Components/Table/Types/Context/SelectionState.svelte.d.ts +260 -0
- package/dist/Components/Table/Types/Context/SelectionState.svelte.js +613 -0
- package/dist/Components/Table/Types/Context/SortingState.svelte.d.ts +112 -0
- package/dist/Components/Table/Types/Context/SortingState.svelte.js +272 -0
- package/dist/Components/Table/Types/Context/TableContext.svelte.d.ts +547 -53
- package/dist/Components/Table/Types/Context/TableContext.svelte.js +959 -1259
- package/dist/Components/Table/Types/Context/TableInitOptions.d.ts +16 -0
- package/dist/Components/Table/Types/Context/TableSettings.d.ts +20 -0
- package/dist/Components/Table/Types/Context/VirtualisationState.svelte.d.ts +167 -0
- package/dist/Components/Table/Types/Context/VirtualisationState.svelte.js +226 -0
- package/dist/Components/Table/Types/Coordinates/CellCoordinates.d.ts +12 -0
- package/dist/Components/Table/Types/Coordinates/CellCoordinatesWithoutColumn.d.ts +5 -0
- package/dist/Components/Table/Types/Coordinates/ColumnIndices.d.ts +16 -2
- package/dist/Components/Table/Types/DataRepository/BuiltInRemoteRepository.svelte.d.ts +143 -0
- package/dist/Components/Table/Types/DataRepository/BuiltInRemoteRepository.svelte.js +454 -0
- package/dist/Components/Table/Types/DataRepository/IDataRepository.d.ts +21 -0
- package/dist/Components/Table/Types/DataRepository/ILocalDataRepository.d.ts +10 -0
- package/dist/Components/Table/Types/DataRepository/IRemoteDataRepository.d.ts +16 -0
- package/dist/Components/Table/Types/DataRepository/LocalTableDataRepository.svelte.d.ts +87 -3
- package/dist/Components/Table/Types/DataRepository/LocalTableDataRepository.svelte.js +112 -7
- package/dist/Components/Table/Types/DataRepository/SortingState.d.ts +12 -0
- package/dist/Components/Table/Types/DataRepository/TableDataRepository.svelte.d.ts +87 -2
- package/dist/Components/Table/Types/DataRepository/TableDataRepository.svelte.js +74 -1
- package/dist/Components/Table/Types/ExpandedPanel.d.ts +9 -0
- package/dist/Components/Table/Types/ExpandedPanel.js +10 -1
- package/dist/Components/Table/Types/Filtering/ColumnFiltersState.d.ts +17 -0
- package/dist/Components/Table/Types/Filtering/CustomFilter.d.ts +21 -0
- package/dist/Components/Table/Types/Filtering/CustomFilterType.d.ts +5 -0
- package/dist/Components/Table/Types/Filtering/CustomFilterType.js +6 -1
- package/dist/Components/Table/Types/Filtering/CustomRemoteFilters.d.ts +6 -0
- package/dist/Components/Table/Types/Filtering/FilterFn.d.ts +28 -0
- package/dist/Components/Table/Types/Filtering/FilterMode.d.ts +7 -0
- package/dist/Components/Table/Types/Filtering/FilterType.d.ts +8 -0
- package/dist/Components/Table/Types/Filtering/FilterType.js +9 -1
- package/dist/Components/Table/Types/Filtering/FilterValueType.d.ts +11 -0
- package/dist/Components/Table/Types/Filtering/FilterValueType.js +12 -1
- package/dist/Components/Table/Types/Filtering/HasCustomFilterFunction.d.ts +23 -2
- package/dist/Components/Table/Types/Filtering/NumberDateFilterMode.d.ts +19 -12
- package/dist/Components/Table/Types/Filtering/NumberDateFilterMode.js +18 -14
- package/dist/Components/Table/Types/Filtering/StringFilterMode.d.ts +22 -15
- package/dist/Components/Table/Types/Filtering/StringFilterMode.js +22 -16
- package/dist/Components/Table/Types/Focus/MultiFocusEdge.d.ts +8 -0
- package/dist/Components/Table/Types/Focus/MultiFocusEdge.js +9 -1
- package/dist/Components/Table/Types/Focus/TableFocusDetails.svelte.d.ts +22 -0
- package/dist/Components/Table/Types/Focus/TableFocusDetails.svelte.js +22 -0
- package/dist/Components/Table/Types/Header.d.ts +8 -0
- package/dist/Components/Table/Types/HeaderGroup.d.ts +7 -0
- package/dist/Components/Table/Types/Layout/CellPinning.d.ts +1 -0
- package/dist/Components/Table/Types/Layout/CellPinning.js +8 -0
- package/dist/Components/Table/Types/OnChangeFn.d.ts +11 -0
- package/dist/Components/Table/Types/Pagination/PaginationState.d.ts +9 -0
- package/dist/Components/Table/Types/Pagination/PaginationType.d.ts +10 -0
- package/dist/Components/Table/Types/Pagination/PaginationType.js +11 -1
- package/dist/Components/Table/Types/Public/AnyEditEvent.d.ts +30 -0
- package/dist/Components/Table/Types/Public/AnyEditEvent.js +1 -0
- package/dist/Components/Table/Types/Public/BaseColumnOptions.d.ts +44 -0
- package/dist/Components/Table/Types/Public/BaseColumnOptions.js +1 -0
- package/dist/Components/Table/Types/Public/ColumnFactory.d.ts +35 -0
- package/dist/Components/Table/Types/Public/ColumnFactory.js +1 -0
- package/dist/Components/Table/Types/Public/ColumnOptions.d.ts +238 -0
- package/dist/Components/Table/Types/Public/ColumnOptions.js +1 -0
- package/dist/Components/Table/Types/Public/ColumnSpec.d.ts +39 -0
- package/dist/Components/Table/Types/Public/ColumnSpec.js +1 -0
- package/dist/Components/Table/Types/Public/CreateTableOptions.d.ts +87 -0
- package/dist/Components/Table/Types/Public/CreateTableOptions.js +1 -0
- package/dist/Components/Table/Types/Public/KeyConstraints.d.ts +42 -0
- package/dist/Components/Table/Types/Public/KeyConstraints.js +22 -0
- package/dist/Components/Table/Types/Public/RowSource.d.ts +87 -0
- package/dist/Components/Table/Types/Public/RowSource.js +1 -0
- package/dist/Components/Table/Types/Public/SelectionOptions.d.ts +26 -0
- package/dist/Components/Table/Types/Public/SelectionOptions.js +1 -0
- package/dist/Components/Table/Types/Public/Table.d.ts +39 -0
- package/dist/Components/Table/Types/Public/Table.js +1 -0
- package/dist/Components/Table/Types/Public/TableInitialState.d.ts +26 -0
- package/dist/Components/Table/Types/Public/TableInitialState.js +1 -0
- package/dist/Components/Table/Types/Public/TablePersistence.d.ts +38 -0
- package/dist/Components/Table/Types/Public/TablePersistence.js +1 -0
- package/dist/Components/Table/Types/Public/TableSubApis.d.ts +157 -0
- package/dist/Components/Table/Types/Public/TableSubApis.js +1 -0
- package/dist/Components/Table/Types/Public/index.d.ts +20 -0
- package/dist/Components/Table/Types/Public/index.js +8 -0
- package/dist/Components/Table/Types/Toolbar/ToolbarPosition.d.ts +4 -0
- package/dist/Components/Table/Types/Toolbar/ToolbarPosition.js +5 -1
- package/dist/Components/Table/createTable.svelte.d.ts +53 -0
- package/dist/Components/Table/createTable.svelte.js +269 -0
- package/dist/Components/Test/TestContextConsumer.svelte +13 -13
- package/dist/Components/TextArea.svelte +127 -108
- package/dist/Components/TextAreaProps.d.ts +37 -0
- package/dist/Components/TextInput.svelte +166 -147
- package/dist/Components/TextInputProps.d.ts +58 -1
- package/dist/Components/ThreeStateRadio.svelte +140 -143
- package/dist/Components/ThreeStateRadioProps.d.ts +29 -1
- package/dist/Components/Touch/BoolCard.svelte +45 -52
- package/dist/Components/Touch/BoolCard.svelte.d.ts +1 -10
- package/dist/Components/Touch/BoolCardProps.d.ts +21 -0
- package/dist/Components/Touch/BoolCardProps.js +1 -0
- package/dist/Components/Touch/DateModal.svelte +209 -217
- package/dist/Components/Touch/DateModal.svelte.d.ts +1 -17
- package/dist/Components/Touch/DateModalProps.d.ts +36 -0
- package/dist/Components/Touch/DateModalProps.js +1 -0
- package/dist/Components/Touch/EditCard.svelte +59 -52
- package/dist/Components/Touch/EditCardProps.d.ts +21 -0
- package/dist/Components/Touch/InfoCard.svelte +35 -28
- package/dist/Components/Touch/InfoCardProps.d.ts +9 -0
- package/dist/Components/Touch/InfoTextCard.svelte +28 -21
- package/dist/Components/Touch/InfoTextCardProps.d.ts +8 -0
- package/dist/Components/Touch/ModalExplanation.svelte +17 -20
- package/dist/Components/Touch/ModalExplanation.svelte.d.ts +1 -6
- package/dist/Components/Touch/ModalExplanationProps.d.ts +22 -0
- package/dist/Components/Touch/ModalExplanationProps.js +1 -0
- package/dist/Components/Touch/NumberKeyboard.svelte +225 -268
- package/dist/Components/Touch/NumberKeyboardProps.d.ts +19 -0
- package/dist/Components/Touch/NumberModal.svelte +79 -69
- package/dist/Components/Touch/NumberModalProps.d.ts +23 -1
- package/dist/Components/Touch/PlusMinusCard.svelte +48 -52
- package/dist/Components/Touch/PlusMinusCard.svelte.d.ts +1 -6
- package/dist/Components/Touch/PlusMinusCardProps.d.ts +18 -0
- package/dist/Components/Touch/PlusMinusCardProps.js +1 -0
- package/dist/Components/Touch/SelectModal.svelte +123 -105
- package/dist/Components/Touch/SelectModalProps.d.ts +22 -1
- package/dist/Components/Touch/SummaryCard.svelte +107 -65
- package/dist/Components/Touch/SummaryCardProps.d.ts +16 -3
- package/dist/Components/Touch/TextAreaModal.svelte +95 -86
- package/dist/Components/Touch/TextAreaModalProps.d.ts +21 -0
- package/dist/Components/Touch/TextModal.svelte +106 -123
- package/dist/Components/Touch/TextModalProps.d.ts +28 -1
- package/dist/Components/Touch/TouchCard.svelte +34 -72
- package/dist/Components/Touch/TouchCardProps.d.ts +21 -0
- package/dist/Components/Touch/TouchModal.svelte +265 -259
- package/dist/Components/Touch/TouchModalProps.d.ts +35 -1
- package/dist/Components/Touch/UtilityButton.svelte +116 -98
- package/dist/Components/Touch/UtilityButtonProps.d.ts +16 -0
- package/dist/Components/VerticalTabGroup.svelte +80 -102
- package/dist/Components/VerticalTabGroupButton.svelte +59 -98
- package/dist/Components/VerticalTabGroupProps.d.ts +15 -2
- package/dist/Contexts/AlertContext.svelte.d.ts +92 -8
- package/dist/Contexts/AlertContext.svelte.js +69 -6
- package/dist/Contexts/ContextMenuContext.svelte.d.ts +54 -8
- package/dist/Contexts/ContextMenuContext.svelte.js +48 -2
- package/dist/Contexts/TestContext.svelte.d.ts +27 -1
- package/dist/Contexts/TestContext.svelte.js +29 -1
- package/dist/Contexts/ToastContext.svelte.d.ts +29 -1
- package/dist/Contexts/ToastContext.svelte.js +93 -24
- package/dist/Contexts/TooltipContext.svelte.d.ts +68 -1
- package/dist/Contexts/TooltipContext.svelte.js +70 -7
- package/dist/Contexts/UntypedInputContext.svelte.d.ts +54 -5
- package/dist/Contexts/UntypedInputContext.svelte.js +87 -34
- package/dist/Delay/Delay.js +1 -1
- package/dist/Helpers/AsyncFlag.svelte.d.ts +39 -0
- package/dist/Helpers/AsyncFlag.svelte.js +72 -0
- package/dist/Helpers/Datamatrix.d.ts +19 -10
- package/dist/Helpers/Datamatrix.js +291 -2
- package/dist/Helpers/Helpers.svelte.d.ts +252 -17
- package/dist/Helpers/Helpers.svelte.js +271 -60
- package/dist/Helpers/INamed.d.ts +6 -0
- package/dist/Helpers/Storage.d.ts +67 -0
- package/dist/Helpers/Storage.js +138 -0
- package/dist/Helpers/dropdownPortal.d.ts +52 -0
- package/dist/Helpers/dropdownPortal.js +232 -0
- package/dist/Helpers/dropdownPortal.svelte.d.ts +2 -0
- package/dist/Helpers/dropdownPortal.svelte.js +5 -0
- package/dist/Helpers/memoiseByRow.d.ts +45 -0
- package/dist/Helpers/memoiseByRow.js +78 -0
- package/dist/Helpers/prefersReducedMotion.svelte.d.ts +27 -0
- package/dist/Helpers/prefersReducedMotion.svelte.js +18 -0
- package/dist/Helpers/useTooltip.svelte.d.ts +53 -0
- package/dist/Helpers/useTooltip.svelte.js +62 -0
- package/dist/Types/Examples/ExampleModal.svelte +35 -34
- package/dist/Types/Examples/ExampleSelectEnum.js +1 -1
- package/dist/Types/Internal/AsyncFlag.d.ts +7 -0
- package/dist/Types/Internal/AsyncFlag.js +1 -0
- package/dist/Types/Internal/BigRadioOption.d.ts +8 -0
- package/dist/Types/Internal/ButtonProp.d.ts +24 -2
- package/dist/Types/Internal/CallbackButton.d.ts +12 -0
- package/dist/Types/Internal/DateChange.d.ts +8 -0
- package/dist/Types/Internal/DatePickerType.d.ts +5 -0
- package/dist/Types/Internal/DatePickerType.js +6 -1
- package/dist/Types/Internal/IdentifiableToastMessage.d.ts +8 -0
- package/dist/Types/Internal/InputVariant.d.ts +5 -0
- package/dist/Types/Internal/InputVariant.js +6 -1
- package/dist/Types/Internal/Misc.d.ts +91 -0
- package/dist/Types/Internal/RefWrapper.d.ts +6 -0
- package/dist/Types/Internal/SelectOption.d.ts +8 -0
- package/dist/Types/Internal/Shape.d.ts +1 -0
- package/dist/Types/Internal/Shape.js +2 -1
- package/dist/Types/Internal/Side.d.ts +1 -0
- package/dist/Types/Internal/Side.js +2 -1
- package/dist/Types/Internal/Size.d.ts +5 -0
- package/dist/Types/Internal/Size.js +6 -1
- package/dist/Types/Internal/TabOption.d.ts +15 -5
- package/dist/Types/Internal/Ternary.d.ts +6 -0
- package/dist/Types/Internal/Ternary.js +7 -1
- package/dist/Types/Internal/ToastMessage.d.ts +28 -0
- package/dist/Types/Internal/Validity.d.ts +6 -0
- package/dist/Types/Internal/Validity.js +7 -1
- package/dist/Types/Internal/ValidityWrapper.d.ts +8 -0
- package/dist/Types/Internal/Variant.d.ts +5 -0
- package/dist/Types/Internal/Variant.js +6 -1
- package/dist/index.d.ts +27 -82
- package/dist/index.js +24 -62
- package/dist/scss/_basics.scss +12 -12
- package/dist/scss/_colours.scss +134 -134
- package/dist/scss/_mixins.scss +3300 -3016
- package/dist/scss/_motion.scss +57 -0
- package/dist/scss/_sizing.scss +85 -69
- package/dist/scss/_transitions.scss +8 -8
- package/dist/scss/colours.js +2 -2
- package/dist/scss/sizing.d.ts +9 -0
- package/dist/scss/sizing.js +10 -1
- package/package.json +6 -2
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { browser, dev } from '$app/environment';
|
|
2
|
-
import { asyncAll,
|
|
2
|
+
import { asyncAll, asyncMap, devCatch, getCanvasContext, isKeyOf, } from '../../../../Helpers/Helpers.svelte.js';
|
|
3
|
+
import { PreferencesState } from './PreferencesState.svelte.js';
|
|
4
|
+
import { SelectionState } from './SelectionState.svelte.js';
|
|
3
5
|
import { tick, untrack } from 'svelte';
|
|
4
6
|
import { SvelteMap } from 'svelte/reactivity';
|
|
5
7
|
import { ColumnType } from '../Columns/ColumnType.js';
|
|
@@ -13,17 +15,41 @@ import { BubbleDef } from '../Columns/Definitions/Display/BubbleDef.svelte.js';
|
|
|
13
15
|
import { DisplayDef } from '../Columns/Definitions/Display/DisplayDef.svelte.js';
|
|
14
16
|
import { ProgressBarDef } from '../Columns/Definitions/Display/ProgressBarDef.svelte.js';
|
|
15
17
|
import { RowSelectionDef } from '../Columns/Definitions/RowSelection/RowSelectionDef.svelte.js';
|
|
18
|
+
import { SortingState } from './SortingState.svelte.js';
|
|
19
|
+
import { FilteringState } from './FilteringState.svelte.js';
|
|
20
|
+
import { ColumnLayoutState } from './ColumnLayoutState.svelte.js';
|
|
21
|
+
import { FocusState } from './FocusState.svelte.js';
|
|
22
|
+
import { VirtualisationState } from './VirtualisationState.svelte.js';
|
|
16
23
|
import { PaginationType } from '../Pagination/PaginationType.js';
|
|
17
24
|
import { ToolbarPosition } from '../Toolbar/ToolbarPosition.js';
|
|
18
25
|
const SORTING_ICON_WIDTH = 24;
|
|
19
26
|
const InternalSymbol = Symbol();
|
|
20
27
|
const uncopyableColumns = new Set([
|
|
21
|
-
//
|
|
28
|
+
// RowSelect column is a special case and is handled by tableContext directly
|
|
22
29
|
ColumnType.Expand,
|
|
23
30
|
ColumnType.Actions,
|
|
24
31
|
ColumnType.FilterOnly,
|
|
25
32
|
ColumnType.Custom,
|
|
26
33
|
]);
|
|
34
|
+
/**
|
|
35
|
+
* Default values applied by `TableContext.init` for any `TableInitOptions`
|
|
36
|
+
* field the consumer omits. Frozen so callers (notably `NestedTable`)
|
|
37
|
+
* can spread it without risking accidental shared-object mutation.
|
|
38
|
+
*
|
|
39
|
+
* Mirrors the default literals consumed inside `init`; both share this
|
|
40
|
+
* constant so the two cannot drift.
|
|
41
|
+
*/
|
|
42
|
+
export const DEFAULT_TABLE_INIT_OPTIONS = Object.freeze({
|
|
43
|
+
allowCopy: true,
|
|
44
|
+
enableResize: true,
|
|
45
|
+
allowColumnReordering: true,
|
|
46
|
+
allowQuickFiltering: true,
|
|
47
|
+
showQuickFilterByDefault: true,
|
|
48
|
+
allowAdvancedFiltering: true,
|
|
49
|
+
toolbar: ToolbarPosition.Right,
|
|
50
|
+
displayFooter: true,
|
|
51
|
+
debug: false,
|
|
52
|
+
});
|
|
27
53
|
export class TableContext {
|
|
28
54
|
defaultColumnDefs;
|
|
29
55
|
tableName;
|
|
@@ -40,8 +66,27 @@ export class TableContext {
|
|
|
40
66
|
_onEditCell;
|
|
41
67
|
debug;
|
|
42
68
|
static identifier = Symbol();
|
|
69
|
+
/**
|
|
70
|
+
* Factory for `TableContext`. Coalesces every optional flag from the
|
|
71
|
+
* caller-supplied `options` against `DEFAULT_TABLE_INIT_OPTIONS` so
|
|
72
|
+
* the constructor receives a complete set of values. The constructor
|
|
73
|
+
* itself is `protected` and gated by an internal symbol to make it
|
|
74
|
+
* impossible to construct a `TableContext` without going through
|
|
75
|
+
* `init`.
|
|
76
|
+
*/
|
|
43
77
|
static init(defaultColumnDefs, tableName, dataRepo, options) {
|
|
44
|
-
const context = new this(InternalSymbol, defaultColumnDefs, tableName, dataRepo, options.allowCopy ??
|
|
78
|
+
const context = new this(InternalSymbol, defaultColumnDefs, tableName, dataRepo, options.allowCopy ?? DEFAULT_TABLE_INIT_OPTIONS.allowCopy, options.enableResize ?? DEFAULT_TABLE_INIT_OPTIONS.enableResize, options.allowColumnReordering ?? DEFAULT_TABLE_INIT_OPTIONS.allowColumnReordering, options.allowQuickFiltering ?? DEFAULT_TABLE_INIT_OPTIONS.allowQuickFiltering, options.showQuickFilterByDefault ?? DEFAULT_TABLE_INIT_OPTIONS.showQuickFilterByDefault, options.allowAdvancedFiltering ?? DEFAULT_TABLE_INIT_OPTIONS.allowAdvancedFiltering, options.toolbar ?? DEFAULT_TABLE_INIT_OPTIONS.toolbar, options.displayFooter ?? DEFAULT_TABLE_INIT_OPTIONS.displayFooter,
|
|
79
|
+
// Fallback identity extractor when no `selectionExtractor`
|
|
80
|
+
// is supplied. The nested-table call path (see
|
|
81
|
+
// `ColumnFactory.expand`) always passes one through, but
|
|
82
|
+
// other consumers may construct an options object directly
|
|
83
|
+
// without it. Returning the row object itself preserves
|
|
84
|
+
// row identity (the cell wrappers use reference equality
|
|
85
|
+
// via the keying cache) and avoids the
|
|
86
|
+
// `tableContext.selectionExtractor is not a function`
|
|
87
|
+
// crash inside `SelectionState.cachedKey` /
|
|
88
|
+
// `TableRow.svelte`'s id-resolution effect.
|
|
89
|
+
options.selectionExtractor ?? ((row) => row), options.getUserId, options.onEditCell, options.debug ?? DEFAULT_TABLE_INIT_OPTIONS.debug);
|
|
45
90
|
return context;
|
|
46
91
|
}
|
|
47
92
|
// #region constructor
|
|
@@ -66,7 +111,6 @@ export class TableContext {
|
|
|
66
111
|
throw new Error('This class may only be instantiated through the static init() method');
|
|
67
112
|
}
|
|
68
113
|
this._dataRepo = $state(dataRepo);
|
|
69
|
-
// this.dataRepo.forceRefresh = this.forceVisibilityUpdate;
|
|
70
114
|
$effect(() => {
|
|
71
115
|
void this.currentPageData;
|
|
72
116
|
// void this.selectedItems;
|
|
@@ -77,7 +121,6 @@ export class TableContext {
|
|
|
77
121
|
this.resetHighlightedRows();
|
|
78
122
|
})
|
|
79
123
|
.catch(devCatch);
|
|
80
|
-
// this.recalculateAllSelectedInCurrentPage();
|
|
81
124
|
}
|
|
82
125
|
});
|
|
83
126
|
});
|
|
@@ -96,12 +139,11 @@ export class TableContext {
|
|
|
96
139
|
// throw new Error(`filtered indices are not in a tracking context`);
|
|
97
140
|
// }
|
|
98
141
|
if (!this.dataRepo.filtering.length) {
|
|
99
|
-
this.
|
|
142
|
+
this._filtering.setFilteredIndices(data.map((_item, index) => {
|
|
100
143
|
return index;
|
|
101
|
-
});
|
|
144
|
+
}));
|
|
102
145
|
}
|
|
103
146
|
else {
|
|
104
|
-
// await asyncSleep(300);
|
|
105
147
|
const filtered = [];
|
|
106
148
|
for (let index = 0; index < data.length; ++index) {
|
|
107
149
|
if (currentController.signal.aborted) {
|
|
@@ -133,7 +175,7 @@ export class TableContext {
|
|
|
133
175
|
filtered.push(index);
|
|
134
176
|
}
|
|
135
177
|
}
|
|
136
|
-
this.
|
|
178
|
+
this._filtering.setFilteredIndices(filtered);
|
|
137
179
|
}
|
|
138
180
|
})().catch((e) => {
|
|
139
181
|
devCatch(e);
|
|
@@ -144,7 +186,7 @@ export class TableContext {
|
|
|
144
186
|
void this.dataRepo.data.length;
|
|
145
187
|
void this.selectedItems;
|
|
146
188
|
void this.paginationRepo?.pageStartIndex;
|
|
147
|
-
const invalidValues = this.
|
|
189
|
+
const invalidValues = this.invalidValues;
|
|
148
190
|
const allSelectedInCurrentPage = [...this.selectedItems.keys()];
|
|
149
191
|
const currentIncrement = ++lastIncrement;
|
|
150
192
|
void this.allSelectedInCurrentPage;
|
|
@@ -165,9 +207,7 @@ export class TableContext {
|
|
|
165
207
|
const current = (latest_pageColData = latest_pageColData + 1);
|
|
166
208
|
if (!this.focusStart || !this.focusEnd) {
|
|
167
209
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
168
|
-
this.
|
|
169
|
-
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
170
|
-
this._focusedColumnIds = new Set();
|
|
210
|
+
this._focus.setFocusedDerived(new Set(), new Set());
|
|
171
211
|
return;
|
|
172
212
|
}
|
|
173
213
|
const { row: startRowIndexId } = this.focusStart;
|
|
@@ -178,9 +218,7 @@ export class TableContext {
|
|
|
178
218
|
const endRowIndex = model.rows.findIndex((row) => row.visibleIndex === endRowIndexId);
|
|
179
219
|
if (startRowIndex == -1 || endRowIndex == -1) {
|
|
180
220
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
181
|
-
this.
|
|
182
|
-
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
183
|
-
this._focusedColumnIds = new Set();
|
|
221
|
+
this._focus.setFocusedDerived(new Set(), new Set());
|
|
184
222
|
return;
|
|
185
223
|
}
|
|
186
224
|
const minRowIndex = Math.min(startRowIndex, endRowIndex);
|
|
@@ -199,8 +237,7 @@ export class TableContext {
|
|
|
199
237
|
for (let i = minRowIndex; i <= maxRowIndex; i++) {
|
|
200
238
|
indices.add(parseInt(model.rows[i].id));
|
|
201
239
|
}
|
|
202
|
-
this.
|
|
203
|
-
this._focusedColumnIds = affectedColumns;
|
|
240
|
+
this._focus.setFocusedDerived(indices, affectedColumns);
|
|
204
241
|
}
|
|
205
242
|
});
|
|
206
243
|
let latest_paginationRowModel = 0;
|
|
@@ -282,40 +319,36 @@ export class TableContext {
|
|
|
282
319
|
.catch(devCatch);
|
|
283
320
|
});
|
|
284
321
|
$effect(() => {
|
|
285
|
-
if (this.highlightedItems.size === 0) {
|
|
286
|
-
this.
|
|
322
|
+
if (this._selection.highlightedItems.size === 0) {
|
|
323
|
+
this._selection.setAllHighlighted(false);
|
|
287
324
|
return;
|
|
288
325
|
}
|
|
289
326
|
const pageStartIndex = this.paginationRepo ? this.paginationRepo.pageStartIndex : 0;
|
|
290
327
|
const pageEndIndexExclusive = this.paginationRepo
|
|
291
328
|
? Math.min(this.paginationRepo.pageEndIndexExclusive, this.sortedData.length)
|
|
292
329
|
: this.sortedData.length;
|
|
293
|
-
const allHighlightedResult = this.
|
|
330
|
+
const allHighlightedResult = this._selection.computeAllHighlightedInRange(pageStartIndex, pageEndIndexExclusive);
|
|
294
331
|
if (allHighlightedResult instanceof Promise) {
|
|
295
332
|
allHighlightedResult
|
|
296
333
|
.then((flag) => {
|
|
297
|
-
this.
|
|
334
|
+
this._selection.setAllHighlighted(flag);
|
|
298
335
|
})
|
|
299
336
|
.catch(devCatch);
|
|
300
337
|
}
|
|
301
338
|
else {
|
|
302
|
-
this.
|
|
339
|
+
this._selection.setAllHighlighted(allHighlightedResult);
|
|
303
340
|
}
|
|
304
341
|
});
|
|
305
342
|
if (defaultColumnDefs.remoteFilters) {
|
|
306
|
-
this.
|
|
343
|
+
this._filtering.setRemoteFilters(defaultColumnDefs.remoteFilters);
|
|
307
344
|
}
|
|
308
345
|
if (hasPagination(dataRepo)) {
|
|
309
346
|
this.paginationRepo = dataRepo;
|
|
310
|
-
// this.tableOptions.onSortingChange = dataRepo.updateSorting;
|
|
311
|
-
// this.tableOptions.manualSorting = dataRepo.manualSorting;
|
|
312
347
|
}
|
|
313
|
-
this.userId = $derived(getUserId());
|
|
314
348
|
if (browser) {
|
|
315
349
|
this._getUserDefaults();
|
|
316
350
|
const removeInvalidPresets = () => {
|
|
317
|
-
|
|
318
|
-
localStorage.removeItem(`${this.userId}-${tableName}-selected-preset-name`);
|
|
351
|
+
this._preferences.clearPresetsAndSelection();
|
|
319
352
|
const visibilityState = {};
|
|
320
353
|
this.columnDefs.forEach((def) => {
|
|
321
354
|
if (def.defaultHidden || def.columnType === ColumnType.FilterOnly) {
|
|
@@ -323,7 +356,7 @@ export class TableContext {
|
|
|
323
356
|
}
|
|
324
357
|
});
|
|
325
358
|
const defaultPreset = {
|
|
326
|
-
name: '
|
|
359
|
+
name: 'Default',
|
|
327
360
|
columnDefs: this.columnDefs,
|
|
328
361
|
sortingState: [],
|
|
329
362
|
visibilityState,
|
|
@@ -331,16 +364,15 @@ export class TableContext {
|
|
|
331
364
|
};
|
|
332
365
|
this.selectedPreset = defaultPreset;
|
|
333
366
|
this.columnVisibility = visibilityState;
|
|
334
|
-
this.
|
|
367
|
+
this._preferences.setPresets([...this.columnPresets, defaultPreset]);
|
|
335
368
|
};
|
|
336
369
|
this.setupDefaultColumnDefs()
|
|
337
370
|
.then((defs) => {
|
|
338
371
|
this.showQuickFiltering = this.showQuickFilterByDefault;
|
|
339
372
|
this.isSelectable = defs.some((it) => it.id.startsWith('is-selectable-'));
|
|
340
373
|
if (this.allowColumnReordering) {
|
|
341
|
-
const
|
|
342
|
-
if (
|
|
343
|
-
const presets = JSON.parse(savedPresets);
|
|
374
|
+
const presets = this._preferences.loadPresets();
|
|
375
|
+
if (presets.length > 0) {
|
|
344
376
|
if (presets[0] && areDefsEquivalent(defs, presets[0].columnDefs)) {
|
|
345
377
|
return this.reassignDefsToPresets(presets).then((p) => p);
|
|
346
378
|
}
|
|
@@ -356,7 +388,7 @@ export class TableContext {
|
|
|
356
388
|
removeInvalidPresets();
|
|
357
389
|
return;
|
|
358
390
|
}
|
|
359
|
-
const savedPresetName =
|
|
391
|
+
const savedPresetName = this._preferences.loadSelectedPresetName();
|
|
360
392
|
if (savedPresetName) {
|
|
361
393
|
const preset = presets.find((it) => it.name === savedPresetName);
|
|
362
394
|
if (preset && areDefsEquivalent(this.columnDefs, preset.columnDefs)) {
|
|
@@ -366,13 +398,13 @@ export class TableContext {
|
|
|
366
398
|
this.selectedPreset = preset;
|
|
367
399
|
}
|
|
368
400
|
else {
|
|
369
|
-
localStorage.removeItem(
|
|
401
|
+
localStorage.removeItem(this._preferences.storageKey('selected-preset-name'));
|
|
370
402
|
this.selectedPreset = presets[0];
|
|
371
403
|
if (presets.at(0)?.visibilityState) {
|
|
372
404
|
this.columnVisibility = presets[0].visibilityState;
|
|
373
405
|
}
|
|
374
406
|
}
|
|
375
|
-
this.
|
|
407
|
+
this._preferences.setPresets(presets);
|
|
376
408
|
}
|
|
377
409
|
})
|
|
378
410
|
.catch(devCatch);
|
|
@@ -380,7 +412,6 @@ export class TableContext {
|
|
|
380
412
|
}
|
|
381
413
|
// #endregion
|
|
382
414
|
// #region stores
|
|
383
|
-
userId;
|
|
384
415
|
isSelectable = $state(false);
|
|
385
416
|
_paginationRepo = $state();
|
|
386
417
|
get paginationRepo() {
|
|
@@ -389,20 +420,17 @@ export class TableContext {
|
|
|
389
420
|
set paginationRepo(v) {
|
|
390
421
|
this._paginationRepo = v;
|
|
391
422
|
}
|
|
392
|
-
|
|
423
|
+
/**
|
|
424
|
+
* Delegating accessor preserved for backwards compatibility; reads /
|
|
425
|
+
* writes through to `_filtering.showQuickFiltering`. The sub-context
|
|
426
|
+
* owns the `allowQuickFiltering` gate so the setter logic stays
|
|
427
|
+
* identical to the pre-extraction implementation.
|
|
428
|
+
*/
|
|
393
429
|
get showQuickFiltering() {
|
|
394
|
-
return this.
|
|
430
|
+
return this._filtering.showQuickFiltering;
|
|
395
431
|
}
|
|
396
432
|
set showQuickFiltering(v) {
|
|
397
|
-
|
|
398
|
-
if (!this.allowQuickFiltering) {
|
|
399
|
-
console.log(`attempted to toggle quick filtering when allowQuickFiltering = false`);
|
|
400
|
-
}
|
|
401
|
-
else {
|
|
402
|
-
console.log(`setting quick filtering = ${v}`);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
this._showQuickFiltering = this.allowQuickFiltering && v;
|
|
433
|
+
this._filtering.showQuickFiltering = v;
|
|
406
434
|
}
|
|
407
435
|
_dataRepo;
|
|
408
436
|
get dataRepo() {
|
|
@@ -411,28 +439,49 @@ export class TableContext {
|
|
|
411
439
|
set dataRepo(v) {
|
|
412
440
|
this._dataRepo = v;
|
|
413
441
|
}
|
|
414
|
-
|
|
442
|
+
/**
|
|
443
|
+
* Delegating getter preserved for backwards compatibility. Reads
|
|
444
|
+
* through to `_filtering.remoteFilters`; the parent uses
|
|
445
|
+
* `_filtering.setRemoteFilters` during column-def setup to write the
|
|
446
|
+
* caller-supplied value.
|
|
447
|
+
*/
|
|
415
448
|
get remoteFilters() {
|
|
416
|
-
return this.
|
|
417
|
-
}
|
|
418
|
-
set remoteFilters(v) {
|
|
419
|
-
this._remoteFilters = v;
|
|
449
|
+
return this._filtering.remoteFilters;
|
|
420
450
|
}
|
|
421
|
-
|
|
451
|
+
/**
|
|
452
|
+
* Delegating accessor preserved for backwards compatibility; reads /
|
|
453
|
+
* writes through to `_filtering.showSelectedOnly`. The sub-context
|
|
454
|
+
* preserves the idempotency short-circuit (re-assigning the same
|
|
455
|
+
* value is a no-op).
|
|
456
|
+
*/
|
|
422
457
|
get showSelectedOnly() {
|
|
423
|
-
return this.
|
|
458
|
+
return this._filtering.showSelectedOnly;
|
|
424
459
|
}
|
|
425
460
|
set showSelectedOnly(v) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
461
|
+
this._filtering.showSelectedOnly = v;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Delegating getters that relay the per-type `FilterFn` from
|
|
465
|
+
* `_filtering`. The sub-context's `$derived` chain reads through
|
|
466
|
+
* `paginationRepo` via the thunk so the source of truth stays
|
|
467
|
+
* with the data repository; consumers that bind these getters
|
|
468
|
+
* (notably `setupDefaultColumnDefs`) need no migration.
|
|
469
|
+
*/
|
|
470
|
+
get dateFilter() {
|
|
471
|
+
return this._filtering.dateFilter;
|
|
472
|
+
}
|
|
473
|
+
get numberFilter() {
|
|
474
|
+
return this._filtering.numberFilter;
|
|
475
|
+
}
|
|
476
|
+
get selectFilter() {
|
|
477
|
+
return this._filtering.selectFilter;
|
|
478
|
+
}
|
|
479
|
+
get boolFilter() {
|
|
480
|
+
return this._filtering.boolFilter;
|
|
481
|
+
}
|
|
482
|
+
get stringFilter() {
|
|
483
|
+
return this._filtering.stringFilter;
|
|
430
484
|
}
|
|
431
|
-
dateFilter = $derived(this.paginationRepo?.dateFilter);
|
|
432
|
-
numberFilter = $derived(this.paginationRepo?.numberFilter);
|
|
433
|
-
selectFilter = $derived(this.paginationRepo?.selectFilter);
|
|
434
|
-
boolFilter = $derived(this.paginationRepo?.boolFilter);
|
|
435
|
-
stringFilter = $derived(this.paginationRepo?.stringFilter);
|
|
436
485
|
_limitFooter = $state(false);
|
|
437
486
|
get limitFooter() {
|
|
438
487
|
return this._limitFooter;
|
|
@@ -447,69 +496,104 @@ export class TableContext {
|
|
|
447
496
|
set dragTarget(v) {
|
|
448
497
|
this._dragTarget = v;
|
|
449
498
|
}
|
|
450
|
-
|
|
499
|
+
/**
|
|
500
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
501
|
+
* actual `$state` slot lives on `_columnLayout`; this getter / setter
|
|
502
|
+
* pair forwards reads and writes so existing consumers need no
|
|
503
|
+
* migration. See `_columnLayout` below for the sub-context.
|
|
504
|
+
*/
|
|
451
505
|
get columnDefs() {
|
|
452
|
-
return this.
|
|
506
|
+
return this._columnLayout.columnDefs;
|
|
453
507
|
}
|
|
454
508
|
set columnDefs(v) {
|
|
455
|
-
this.
|
|
509
|
+
this._columnLayout.columnDefs = v;
|
|
456
510
|
}
|
|
457
|
-
|
|
511
|
+
/**
|
|
512
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
513
|
+
* actual `$state` slot lives on `_preferences._columnPresets`
|
|
514
|
+
* (Phase 6.4). The setter is private because the consumer-facing
|
|
515
|
+
* write path goes through `saveColumnPreset` (which also persists
|
|
516
|
+
* the list to localStorage); internal call sites that need to
|
|
517
|
+
* mutate the list directly call `_preferences.setPresets(...)`.
|
|
518
|
+
*/
|
|
458
519
|
get columnPresets() {
|
|
459
|
-
return this.
|
|
520
|
+
return this._preferences.columnPresets;
|
|
460
521
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
522
|
+
/**
|
|
523
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
524
|
+
* actual `$state` slot lives on `_preferences._selectedPreset`
|
|
525
|
+
* (Phase 6.4). Reads / writes through to the sub-context.
|
|
526
|
+
*/
|
|
466
527
|
get selectedPreset() {
|
|
467
|
-
return this.
|
|
528
|
+
return this._preferences.selectedPreset;
|
|
468
529
|
}
|
|
469
530
|
set selectedPreset(v) {
|
|
470
|
-
this.
|
|
531
|
+
this._preferences.selectedPreset = v;
|
|
471
532
|
}
|
|
472
|
-
|
|
473
|
-
|
|
533
|
+
/**
|
|
534
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
535
|
+
* `$state` slot lives on `_focus._focusedRowIndices` (Phase 6.6).
|
|
536
|
+
* Populated by the parent's focus-derived `$effect` through
|
|
537
|
+
* `_focus.setFocusedDerived(rows, cols)`.
|
|
538
|
+
*/
|
|
474
539
|
get focusedRowIndices() {
|
|
475
|
-
return this.
|
|
540
|
+
return this._focus.focusedRowIndices;
|
|
476
541
|
}
|
|
477
|
-
|
|
542
|
+
/**
|
|
543
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
544
|
+
* actual `$state` slot lives on `_selection._highlightedItems`
|
|
545
|
+
* (Phase 6.5).
|
|
546
|
+
*/
|
|
478
547
|
get highlightedItems() {
|
|
479
|
-
return this.
|
|
548
|
+
return this._selection.highlightedItems;
|
|
480
549
|
}
|
|
481
|
-
|
|
482
|
-
|
|
550
|
+
/**
|
|
551
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
552
|
+
* `$state` slot lives on `_focus._focusedColumnIds` (Phase 6.6).
|
|
553
|
+
*/
|
|
483
554
|
get focusedColumnIds() {
|
|
484
|
-
return this.
|
|
555
|
+
return this._focus.focusedColumnIds;
|
|
485
556
|
}
|
|
486
|
-
|
|
557
|
+
/**
|
|
558
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
559
|
+
* `$state` slot lives on `_focus._isMousingDown` (Phase 6.6).
|
|
560
|
+
*/
|
|
487
561
|
get isMousingDown() {
|
|
488
|
-
return this.
|
|
562
|
+
return this._focus.isMousingDown;
|
|
489
563
|
}
|
|
490
564
|
set isMousingDown(v) {
|
|
491
|
-
this.
|
|
565
|
+
this._focus.isMousingDown = v;
|
|
492
566
|
}
|
|
493
|
-
|
|
567
|
+
/**
|
|
568
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
569
|
+
* `$state` slot lives on `_focus._focusStart` (Phase 6.6). The
|
|
570
|
+
* setter remains read-only at this surface; the write path is
|
|
571
|
+
* `moveFocus` / `unfocus` / `tabForward` / `tabBackward` /
|
|
572
|
+
* `endFocus` / `setFocusEnd` / `startFocus` (all delegated below).
|
|
573
|
+
*/
|
|
494
574
|
get focusStart() {
|
|
495
|
-
return this.
|
|
575
|
+
return this._focus.focusStart;
|
|
496
576
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
577
|
+
/**
|
|
578
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
579
|
+
* `$state` slot lives on `_focus._focusStartIsBeingEdited`
|
|
580
|
+
* (Phase 6.6).
|
|
581
|
+
*/
|
|
501
582
|
get focusStartIsBeingEdited() {
|
|
502
|
-
return this.
|
|
583
|
+
return this._focus.focusStartIsBeingEdited;
|
|
503
584
|
}
|
|
504
585
|
set focusStartIsBeingEdited(v) {
|
|
505
|
-
this.
|
|
586
|
+
this._focus.focusStartIsBeingEdited = v;
|
|
506
587
|
}
|
|
507
|
-
|
|
588
|
+
/**
|
|
589
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
590
|
+
* `$state` slot lives on `_focus._focusEnd` (Phase 6.6).
|
|
591
|
+
*/
|
|
508
592
|
get focusEnd() {
|
|
509
|
-
return this.
|
|
593
|
+
return this._focus.focusEnd;
|
|
510
594
|
}
|
|
511
595
|
set focusEnd(v) {
|
|
512
|
-
this.
|
|
596
|
+
this._focus.focusEnd = v;
|
|
513
597
|
}
|
|
514
598
|
_singleClickEditing = $state(false);
|
|
515
599
|
get singleClickEditing() {
|
|
@@ -553,26 +637,41 @@ export class TableContext {
|
|
|
553
637
|
set commaSeparatedCopy(v) {
|
|
554
638
|
this._commaSeparatedCopy = v;
|
|
555
639
|
}
|
|
556
|
-
|
|
640
|
+
/**
|
|
641
|
+
* Delegating accessor preserved for backwards compatibility. Reads
|
|
642
|
+
* through to `_columnLayout.columnSizing`. Per-key mutations (e.g.
|
|
643
|
+
* `this.columnSizing[id] = w` in `autoResize`) work because the
|
|
644
|
+
* underlying `$state` slot is a Svelte proxy.
|
|
645
|
+
*/
|
|
557
646
|
get columnSizing() {
|
|
558
|
-
return this.
|
|
647
|
+
return this._columnLayout.columnSizing;
|
|
559
648
|
}
|
|
560
649
|
set columnSizing(v) {
|
|
561
|
-
this.
|
|
650
|
+
this._columnLayout.columnSizing = v;
|
|
562
651
|
}
|
|
563
|
-
|
|
652
|
+
/**
|
|
653
|
+
* Delegating accessor preserved for backwards compatibility. Reads
|
|
654
|
+
* through to `_columnLayout.columnPinning`. The `setColumnPinning`
|
|
655
|
+
* updater below honours the `allowColumnReordering` master gate; the
|
|
656
|
+
* direct setter does not, by design (used for preset switches and
|
|
657
|
+
* the init flow where the gate has already been checked).
|
|
658
|
+
*/
|
|
564
659
|
get columnPinning() {
|
|
565
|
-
return this.
|
|
660
|
+
return this._columnLayout.columnPinning;
|
|
566
661
|
}
|
|
567
662
|
set columnPinning(v) {
|
|
568
|
-
this.
|
|
663
|
+
this._columnLayout.columnPinning = v;
|
|
569
664
|
}
|
|
570
|
-
|
|
665
|
+
/**
|
|
666
|
+
* Delegating accessor preserved for backwards compatibility. Reads
|
|
667
|
+
* through to `_columnLayout.columnVisibility`. Tri-state semantics
|
|
668
|
+
* (`true` / `false` / `undefined`) are preserved.
|
|
669
|
+
*/
|
|
571
670
|
get columnVisibility() {
|
|
572
|
-
return this.
|
|
671
|
+
return this._columnLayout.columnVisibility;
|
|
573
672
|
}
|
|
574
673
|
set columnVisibility(v) {
|
|
575
|
-
this.
|
|
674
|
+
this._columnLayout.columnVisibility = v;
|
|
576
675
|
}
|
|
577
676
|
_openModalCoordinates = $state(null);
|
|
578
677
|
get openModalCoordinates() {
|
|
@@ -581,131 +680,57 @@ export class TableContext {
|
|
|
581
680
|
set openModalCoordinates(v) {
|
|
582
681
|
this._openModalCoordinates = v;
|
|
583
682
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
const pageStartIndex = this.paginationRepo ? this.paginationRepo.pageStartIndex : 0;
|
|
592
|
-
const pageEndIndexExclusive = this.paginationRepo
|
|
593
|
-
? Math.min(this.paginationRepo.pageEndIndexExclusive, sortedData.length)
|
|
594
|
-
: sortedData.length;
|
|
595
|
-
const selectableMethod = this.columnDefs.find((it) => it instanceof RowSelectionDef)?.isSelectable;
|
|
596
|
-
const result = this.allSelectedBySelectableMethod(pageStartIndex, pageEndIndexExclusive, selectableMethod);
|
|
597
|
-
if (result instanceof Promise) {
|
|
598
|
-
result
|
|
599
|
-
.then((flag) => {
|
|
600
|
-
this._allSelectedAsync = flag;
|
|
601
|
-
})
|
|
602
|
-
.catch(devCatch);
|
|
603
|
-
return this._allSelectedAsync;
|
|
604
|
-
}
|
|
605
|
-
return result;
|
|
606
|
-
});
|
|
607
|
-
get allSelectedInCurrentPage() {
|
|
608
|
-
return this._allSelectedInCurrentPage;
|
|
683
|
+
// Per-table identifier of the ActionsCell that currently has its popup open.
|
|
684
|
+
// Lives on the context rather than at module scope so multiple tables on the
|
|
685
|
+
// same page do not share open-popup state (security: cross-table state
|
|
686
|
+
// poisoning).
|
|
687
|
+
_shownActionsPopup = $state();
|
|
688
|
+
get shownActionsPopup() {
|
|
689
|
+
return this._shownActionsPopup;
|
|
609
690
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
const emptyPromise = new Promise(() => { });
|
|
613
|
-
if (selectableMethod) {
|
|
614
|
-
const pending = [];
|
|
615
|
-
for (let i = pageStartIndex; i < pageEndIndexExclusive; i++) {
|
|
616
|
-
const item = sortedData[i];
|
|
617
|
-
const relIdx = i - pageStartIndex;
|
|
618
|
-
const raw = this._cachedKey(item);
|
|
619
|
-
const checkUnselected = (key) => {
|
|
620
|
-
const selectable = selectableMethod(item, relIdx);
|
|
621
|
-
if (selectable instanceof Promise) {
|
|
622
|
-
return selectable.then((s) => s && !this.selectedItems.has(key));
|
|
623
|
-
}
|
|
624
|
-
return selectable && !this.selectedItems.has(key);
|
|
625
|
-
};
|
|
626
|
-
const r = raw instanceof Promise ? raw.then(checkUnselected) : checkUnselected(raw);
|
|
627
|
-
if (r === true) {
|
|
628
|
-
return false;
|
|
629
|
-
}
|
|
630
|
-
if (r instanceof Promise) {
|
|
631
|
-
pending.push(r);
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
if (pending.length === 0) {
|
|
635
|
-
return true;
|
|
636
|
-
}
|
|
637
|
-
return Promise.race([
|
|
638
|
-
Promise.race(pending.map(async (p) => ((await p) ? false : emptyPromise))),
|
|
639
|
-
Promise.all(pending).then((results) => {
|
|
640
|
-
return !results.some(Boolean);
|
|
641
|
-
}),
|
|
642
|
-
]);
|
|
643
|
-
}
|
|
644
|
-
else {
|
|
645
|
-
const pending = [];
|
|
646
|
-
for (let i = pageStartIndex; i < pageEndIndexExclusive; i++) {
|
|
647
|
-
const raw = this._cachedKey(sortedData[i]);
|
|
648
|
-
if (raw instanceof Promise) {
|
|
649
|
-
pending.push(raw.then((key) => !this.selectedItems.has(key)));
|
|
650
|
-
}
|
|
651
|
-
else if (!this.selectedItems.has(raw)) {
|
|
652
|
-
return false;
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
if (pending.length === 0) {
|
|
656
|
-
return true;
|
|
657
|
-
}
|
|
658
|
-
return Promise.race([
|
|
659
|
-
Promise.race(pending.map(async (p) => ((await p) ? false : emptyPromise))),
|
|
660
|
-
Promise.all(pending).then((results) => !results.some(Boolean)),
|
|
661
|
-
]);
|
|
662
|
-
}
|
|
691
|
+
set shownActionsPopup(v) {
|
|
692
|
+
this._shownActionsPopup = v;
|
|
663
693
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
if (!this.highlightedItems.has(key)) {
|
|
672
|
-
return false;
|
|
673
|
-
}
|
|
674
|
-
for (let j = index + 1; j < pageEndIndexExclusive; j++) {
|
|
675
|
-
const r = this._cachedKey(sortedData[j]);
|
|
676
|
-
const k = r instanceof Promise ? await r : r;
|
|
677
|
-
if (!this.highlightedItems.has(k)) {
|
|
678
|
-
return false;
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
return true;
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
if (!this.highlightedItems.has(raw)) {
|
|
685
|
-
return false;
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
return true;
|
|
694
|
+
/**
|
|
695
|
+
* Delegating getter preserved for backwards compatibility. The
|
|
696
|
+
* actual `$derived` chain (and the async-result cache) live on
|
|
697
|
+
* `_selection.allSelectedInCurrentPage` (Phase 6.5).
|
|
698
|
+
*/
|
|
699
|
+
get allSelectedInCurrentPage() {
|
|
700
|
+
return this._selection.allSelectedInCurrentPage;
|
|
689
701
|
}
|
|
690
|
-
|
|
702
|
+
/**
|
|
703
|
+
* Delegating getter preserved for backwards compatibility. Reads
|
|
704
|
+
* through to `_selection.allHighlighted`; the value is published by
|
|
705
|
+
* the `_allHighlightedEffect` below which lives on this class
|
|
706
|
+
* because it reads `paginationRepo.pageStartIndex` / `pageEndIndexExclusive`
|
|
707
|
+
* directly.
|
|
708
|
+
*/
|
|
691
709
|
get allHighlighted() {
|
|
692
|
-
return this.
|
|
710
|
+
return this._selection.allHighlighted;
|
|
693
711
|
}
|
|
694
|
-
|
|
712
|
+
/**
|
|
713
|
+
* Delegating getter preserved for backwards compatibility. The
|
|
714
|
+
* `$derived` lives on `_selection.hasHighlightedItems` (Phase 6.5).
|
|
715
|
+
*/
|
|
695
716
|
get hasHighlightedItems() {
|
|
696
|
-
return this.
|
|
717
|
+
return this._selection.hasHighlightedItems;
|
|
697
718
|
}
|
|
698
|
-
|
|
699
|
-
|
|
719
|
+
/**
|
|
720
|
+
* Delegating accessors preserved for backwards compatibility. The
|
|
721
|
+
* `$state` slots live on `_selection` (Phase 6.5). `selectedItems`
|
|
722
|
+
* is read-only at this surface; the consumer-facing writers are
|
|
723
|
+
* `toggleSelected` / `setSelected` / `resetSelectedItems` /
|
|
724
|
+
* `setSelectionMap` below.
|
|
725
|
+
*/
|
|
700
726
|
get selectedItems() {
|
|
701
|
-
return this.
|
|
727
|
+
return this._selection.selectedItems;
|
|
702
728
|
}
|
|
703
|
-
_invalidValues = $state(new SvelteMap());
|
|
704
729
|
get invalidValues() {
|
|
705
|
-
return this.
|
|
730
|
+
return this._selection.invalidValues;
|
|
706
731
|
}
|
|
707
732
|
set invalidValues(v) {
|
|
708
|
-
this.
|
|
733
|
+
this._selection.invalidValues = v;
|
|
709
734
|
}
|
|
710
735
|
_hasInvalidSelectedItems = $state(false);
|
|
711
736
|
get hasInvalidSelectedItems() {
|
|
@@ -714,116 +739,292 @@ export class TableContext {
|
|
|
714
739
|
set hasInvalidSelectedItems(v) {
|
|
715
740
|
this._hasInvalidSelectedItems = v;
|
|
716
741
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
742
|
+
/**
|
|
743
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
744
|
+
* id->def lookup map is owned by `_columnLayout.columnDefsById`
|
|
745
|
+
* (Phase 6.3); previously this was a `$derived` private field on
|
|
746
|
+
* `TableContext`. Made `public get` (instead of the previous
|
|
747
|
+
* `private` field) so the dependent sub-contexts (`SortingState`)
|
|
748
|
+
* and helpers (`generateTableOptions`) can read it through the
|
|
749
|
+
* legacy surface without piercing the sub-context boundary.
|
|
750
|
+
*/
|
|
751
|
+
get columnDefsById() {
|
|
752
|
+
return this._columnLayout.columnDefsById;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Phase 6.3 sub-context that owns the column-layout state:
|
|
756
|
+
* `columnDefs`, `columnSizing`, `columnPinning`, `columnVisibility`,
|
|
757
|
+
* the `columnDefsById` reverse-lookup map, the `visualColumns`
|
|
758
|
+
* cached array (and the paired `visualColumnIndexById` O(1) lookup),
|
|
759
|
+
* the gated `setColumnPinning` updater, the `updateDefsBasedOnPinning`
|
|
760
|
+
* helper, and the `setNewPinningState` helper.
|
|
761
|
+
*
|
|
762
|
+
* Dependencies are passed as thunks so the sub-context never holds
|
|
763
|
+
* a reference to the parent. The `allowColumnReordering` thunk
|
|
764
|
+
* gates the public `setColumnPinning` updater; the `debug` thunk
|
|
765
|
+
* mirrors the parent's logger gate.
|
|
766
|
+
*/
|
|
767
|
+
_columnLayout = new ColumnLayoutState({
|
|
768
|
+
getAllowColumnReordering: () => this.allowColumnReordering,
|
|
769
|
+
getDebug: () => this.debug,
|
|
770
|
+
});
|
|
771
|
+
/**
|
|
772
|
+
* Phase 6.2 sub-context that owns the filtering-related state: the
|
|
773
|
+
* per-row filter result (`filteredIndices`), the `filteredData`
|
|
774
|
+
* derived chain, the user-facing toggles (`showQuickFiltering`,
|
|
775
|
+
* `showSelectedOnly`), the per-type `FilterFn` relays, the
|
|
776
|
+
* remote-filter slot, and the `updateFilters` `OnChangeFn` slot.
|
|
777
|
+
*
|
|
778
|
+
* Dependencies are passed as thunks so the sub-context never holds
|
|
779
|
+
* a reference to the parent; the population effect for
|
|
780
|
+
* `_filteredIndices` stays in `TableContext` because it calls
|
|
781
|
+
* `this.createRow` (parent concern) - the effect publishes its
|
|
782
|
+
* result via `this._filtering.setFilteredIndices`.
|
|
783
|
+
*/
|
|
784
|
+
_filtering = new FilteringState({
|
|
785
|
+
getRepoData: () => this.dataRepo.data,
|
|
786
|
+
getPaginationType: () => this.paginationRepo?.paginationType,
|
|
787
|
+
getDateFilterFn: () => this.paginationRepo?.dateFilter,
|
|
788
|
+
getNumberFilterFn: () => this.paginationRepo?.numberFilter,
|
|
789
|
+
getSelectFilterFn: () => this.paginationRepo?.selectFilter,
|
|
790
|
+
getBoolFilterFn: () => this.paginationRepo?.boolFilter,
|
|
791
|
+
getStringFilterFn: () => this.paginationRepo?.stringFilter,
|
|
792
|
+
getSelectedItems: () => this.selectedItems,
|
|
793
|
+
getAllowQuickFiltering: () => this.allowQuickFiltering,
|
|
794
|
+
getDebug: () => this.debug,
|
|
795
|
+
});
|
|
796
|
+
/**
|
|
797
|
+
* Delegating getter preserved for backwards compatibility. Reads
|
|
798
|
+
* through to `_filtering.filteredIndices`, which is populated by
|
|
799
|
+
* the per-row filter `$effect` in this class (the effect calls
|
|
800
|
+
* `this.createRow` and must stay where `createRow` lives).
|
|
801
|
+
*/
|
|
752
802
|
get filteredIndices() {
|
|
753
|
-
return this.
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
803
|
+
return this._filtering.filteredIndices;
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Delegating getter preserved for backwards compatibility. Reads
|
|
807
|
+
* through to `_filtering.filteredData`, which owns the local /
|
|
808
|
+
* remote / show-selected-only branching.
|
|
809
|
+
*/
|
|
810
|
+
get filteredData() {
|
|
811
|
+
return this._filtering.filteredData;
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Phase 6.1 sub-context that owns the sort-related derived chain and
|
|
815
|
+
* the per-`ColumnDef` comparator dispatch. The dependency thunks
|
|
816
|
+
* close over `this.columnDefsById` / `this.filteredData` /
|
|
817
|
+
* `this.dataRepo` / `this.paginationRepo` / `this.debug`, so the
|
|
818
|
+
* sub-context never holds a direct reference to the parent and the
|
|
819
|
+
* reactivity graph remains threaded through `$derived` only.
|
|
820
|
+
*
|
|
821
|
+
* Exposed as a public field so future consumers can read sorting
|
|
822
|
+
* state directly; the legacy `sortedData` getter below preserves the
|
|
823
|
+
* pre-decomposition surface for backwards compatibility.
|
|
824
|
+
*/
|
|
825
|
+
_sorting = new SortingState({
|
|
826
|
+
getColumnDefsById: () => this.columnDefsById,
|
|
827
|
+
getFilteredData: () => this._filtering.filteredData,
|
|
828
|
+
getSortingArray: () => this.dataRepo.sorting,
|
|
829
|
+
getPaginationType: () => this.paginationRepo?.paginationType,
|
|
830
|
+
getDataLength: () => this.dataRepo.data.length,
|
|
831
|
+
getDebug: () => this.debug,
|
|
776
832
|
});
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
833
|
+
/**
|
|
834
|
+
* Phase 6.4 sub-context that owns every persistence / preferences
|
|
835
|
+
* concern previously held directly on `TableContext`:
|
|
836
|
+
*
|
|
837
|
+
* - the saved column-presets list and currently-selected preset
|
|
838
|
+
* (`columnPresets`, `selectedPreset`);
|
|
839
|
+
* - the `userIdSegment` `$derived` (used to namespace every
|
|
840
|
+
* `scoria-table:<user>:<tableName>:<suffix>` storage key);
|
|
841
|
+
* - the `saveColumnPreset` / `setSelectedPreset` writers (which
|
|
842
|
+
* apply the layout AND persist);
|
|
843
|
+
* - the `loadPresets` / `loadSelectedPresetName` /
|
|
844
|
+
* `loadSavedSettings` boot-time hydration helpers;
|
|
845
|
+
* - the `persistSettings` writer used by the in-app settings
|
|
846
|
+
* toggles (`setIsCompact`, `setAlternateRowColours`, etc.);
|
|
847
|
+
* - the schema-validation typeguards (exported alongside the
|
|
848
|
+
* sub-context for unit testing).
|
|
849
|
+
*
|
|
850
|
+
* The settings fields themselves (`isCompact`, `singleClickEditing`,
|
|
851
|
+
* `alternateRowColours`, etc.) stay on `TableContext` because each
|
|
852
|
+
* one drives reactive UI; only the localStorage round-trip moved.
|
|
853
|
+
* `TableContext` retains delegating getters/setters/methods for
|
|
854
|
+
* every legacy surface (`columnPresets`, `selectedPreset`,
|
|
855
|
+
* `saveColumnPreset`, `setSelectedPreset`, `setIsCompact`, etc.).
|
|
856
|
+
*/
|
|
857
|
+
_preferences = new PreferencesState({
|
|
858
|
+
getTableName: () => this.tableName,
|
|
859
|
+
getUserId: () => this.getUserId(),
|
|
860
|
+
getColumnDefs: () => this.columnDefs,
|
|
861
|
+
getColumnVisibility: () => this.columnVisibility,
|
|
862
|
+
getColumnPinning: () => this.columnPinning,
|
|
863
|
+
getSortingArray: () => this.paginationRepo?.sorting,
|
|
864
|
+
setColumnDefs: (defs) => {
|
|
865
|
+
this.columnDefs = defs;
|
|
866
|
+
},
|
|
867
|
+
setColumnVisibility: (state) => {
|
|
868
|
+
this.columnVisibility = state;
|
|
869
|
+
},
|
|
870
|
+
setColumnPinning: (state) => {
|
|
871
|
+
this.columnPinning = state;
|
|
872
|
+
},
|
|
873
|
+
applySortingState: (state) => {
|
|
874
|
+
this.paginationRepo?.updateSorting?.(state);
|
|
875
|
+
},
|
|
876
|
+
getDebug: () => this.debug,
|
|
877
|
+
});
|
|
878
|
+
/**
|
|
879
|
+
* Phase 6.5 sub-context that owns every selection + highlighting +
|
|
880
|
+
* key-cache concern previously held directly on `TableContext`:
|
|
881
|
+
*
|
|
882
|
+
* - `selectedItems` / `invalidValues` / `highlightedItems`
|
|
883
|
+
* (`SvelteMap` slots, keyed by `selectionExtractor(item)`);
|
|
884
|
+
* - the `WeakMap<T, RowIdType>` key-cache that memoises the
|
|
885
|
+
* extractor result for each row reference;
|
|
886
|
+
* - the pending-flush buffer and its debounced microtask
|
|
887
|
+
* (`_pendingSelectionChanges` -> `tick().then(flush)`);
|
|
888
|
+
* - the `allSelectedInCurrentPage` `$derived` (with the async-
|
|
889
|
+
* result cache for promise-returning `selectionExtractor` /
|
|
890
|
+
* `RowSelectionDef.isSelectable`);
|
|
891
|
+
* - the `selectAllInCurrentPage` / `updateSelectedItems` /
|
|
892
|
+
* `onHighlightAll` flows;
|
|
893
|
+
* - the `handleItemMutation` split that powers the parent's
|
|
894
|
+
* `onEditCell` - the sub-context owns the key-cache delete +
|
|
895
|
+
* selection rebind; the user-supplied callback stays on this
|
|
896
|
+
* class.
|
|
897
|
+
*
|
|
898
|
+
* Dependencies are passed as thunks so the sub-context never holds
|
|
899
|
+
* a direct reference to `TableContext`. The two `$effect`s that
|
|
900
|
+
* historically lived on this class continue to live here (they
|
|
901
|
+
* read `paginationRepo` directly) but publish their results
|
|
902
|
+
* through `_selection.setAllHighlighted(...)` and
|
|
903
|
+
* `_selection.updateSelectedItems()`.
|
|
904
|
+
*/
|
|
905
|
+
_selection = new SelectionState({
|
|
906
|
+
getSortedData: () => this.sortedData,
|
|
907
|
+
getCurrentPageData: () => this.currentPageData,
|
|
908
|
+
getColumnDefs: () => this.columnDefs,
|
|
909
|
+
getPageStartIndex: () => this.paginationRepo?.pageStartIndex,
|
|
910
|
+
getPageEndIndexExclusive: () => this.paginationRepo?.pageEndIndexExclusive,
|
|
911
|
+
getSelectionExtractor: () => this.selectionExtractor,
|
|
912
|
+
getIsSelectable: () => this.isSelectable,
|
|
913
|
+
getDebug: () => this.debug,
|
|
914
|
+
});
|
|
915
|
+
/**
|
|
916
|
+
* Phase 6.6 sub-context that owns every focus + keyboard-navigation
|
|
917
|
+
* concern previously held directly on `TableContext`:
|
|
918
|
+
*
|
|
919
|
+
* - `focusStart` / `focusEnd` / `focusStartIsBeingEdited` /
|
|
920
|
+
* `isMousingDown` (the `$state` slots that define the focused
|
|
921
|
+
* cell-range);
|
|
922
|
+
* - the `focusedRowIndices` / `focusedColumnIds` sets (populated
|
|
923
|
+
* by the parent's `$effect` through `setFocusedDerived(...)`);
|
|
924
|
+
* - every focus writer (`startFocus`, `moveFocus`, `unfocus`,
|
|
925
|
+
* `endFocus`, `setFocusEnd`, `focusToTop`, `focusToBottom`,
|
|
926
|
+
* `focusToStart`, `focusToEnd`, `moveToTop`, `moveToBottom`,
|
|
927
|
+
* `moveToStart`, `moveToEnd`, `setFocusUp` / `setFocusDown` /
|
|
928
|
+
* `setFocusLeft` / `setFocusRight`, and the
|
|
929
|
+
* `moveFocusUp` / `moveFocusDown` / `moveFocusLeft` /
|
|
930
|
+
* `moveFocusRight` range-extension variants);
|
|
931
|
+
* - `handleTdKeydown` (the root `onkeydown` router) and the
|
|
932
|
+
* private `handleKeystroke` dispatch.
|
|
933
|
+
*
|
|
934
|
+
* Dependencies are passed as thunks so the sub-context never holds
|
|
935
|
+
* a direct reference to `TableContext`. The Ctrl+F / Tab / Ctrl+C
|
|
936
|
+
* paths in `handleTdKeydown` and `handleKeystroke` call back into
|
|
937
|
+
* the parent through the `toggleQuickFiltering` / `tabForward` /
|
|
938
|
+
* `tabBackward` / `copyDataToClipboard` thunks because those
|
|
939
|
+
* methods read fields owned by other sub-contexts (or the parent
|
|
940
|
+
* directly) and would otherwise need to import them.
|
|
941
|
+
*
|
|
942
|
+
* The focus-derived `$effect` that publishes
|
|
943
|
+
* `focusedRowIndices` / `focusedColumnIds` lives on `TableContext`
|
|
944
|
+
* because it reads `paginationRowModel` + `_visualColumns` directly;
|
|
945
|
+
* it calls `_focus.setFocusedDerived(rows, cols)` to publish.
|
|
946
|
+
*
|
|
947
|
+
* Group 2 Item C (per-cell `TableFocusDetails` reuse cache) is
|
|
948
|
+
* deferred for Phase 6.6 - see the class-level JSDoc on
|
|
949
|
+
* `FocusState` for the deferral rationale.
|
|
950
|
+
*/
|
|
951
|
+
_focus = new FocusState({
|
|
952
|
+
getAllowCopy: () => this.allowCopy,
|
|
953
|
+
getAllowQuickFiltering: () => this.allowQuickFiltering,
|
|
954
|
+
getColumnDefs: () => this.columnDefs,
|
|
955
|
+
getColumnVisibility: () => this.columnVisibility,
|
|
956
|
+
getVisualColumns: () => this._visualColumns,
|
|
957
|
+
getVisualColumnIndexById: (id) => this.columnVisualIndex(id),
|
|
958
|
+
getSortedDataLength: () => this.sortedData.length,
|
|
959
|
+
getPaginationRowModel: () => this.paginationRowModel,
|
|
960
|
+
copyDataToClipboard: () => this.copyDataToClipboard(),
|
|
961
|
+
toggleQuickFiltering: () => {
|
|
962
|
+
this.toggleQuickFiltering();
|
|
963
|
+
},
|
|
964
|
+
tabForward: () => {
|
|
965
|
+
this.tabForward();
|
|
966
|
+
},
|
|
967
|
+
tabBackward: () => {
|
|
968
|
+
this.tabBackward();
|
|
969
|
+
},
|
|
970
|
+
getDebug: () => this.debug,
|
|
971
|
+
// Phase 7.1 Item C - `getFocusDetailsFor` needs the parent
|
|
972
|
+
// `TableContext` to construct `TableFocusDetails` on cache-miss.
|
|
973
|
+
// Pass via a thunk so the sub-context never closes over `this`
|
|
974
|
+
// at construction time.
|
|
975
|
+
getTableContextRef: () => this,
|
|
826
976
|
});
|
|
977
|
+
/**
|
|
978
|
+
* Phase 7.4 row-virtualisation sub-context. Owns the visible-row
|
|
979
|
+
* window plus the top / bottom spacer-height derived chain that
|
|
980
|
+
* `TableBody.svelte` renders. Threshold defaults to 100 rows
|
|
981
|
+
* (below that the legacy code path renders every row directly);
|
|
982
|
+
* overscan defaults to 10 rows above and below the viewport so a
|
|
983
|
+
* fast flick of the wheel does not surface a blank band before
|
|
984
|
+
* the next paint.
|
|
985
|
+
*
|
|
986
|
+
* Dependencies are thunks - the sub-context reads
|
|
987
|
+
* `paginationRowModel.rows` through `getRows`, the max-side
|
|
988
|
+
* observed height from the `_columnLayout` row-height pool
|
|
989
|
+
* through `getRowHeight`, and the per-table-density estimate
|
|
990
|
+
* through `getEstimatedRowHeight`.
|
|
991
|
+
*/
|
|
992
|
+
/**
|
|
993
|
+
* Rendered widths of header cells, keyed by columnId. TableHead
|
|
994
|
+
* observes each `<div role="columnheader">` with a `ResizeObserver`
|
|
995
|
+
* and writes the actual rendered width here; TableRow / TableBody
|
|
996
|
+
* read these widths when computing sticky pinning offsets so that
|
|
997
|
+
* pinned columns can flex with `minmax(W, 1fr)` like centre columns
|
|
998
|
+
* (matching width-preservation expectation on pinning) while the
|
|
999
|
+
* sticky-cell offsets stay accurate.
|
|
1000
|
+
*
|
|
1001
|
+
* Fallback when a column has not yet been measured: 0. The first
|
|
1002
|
+
* frame after pinning toggles may render with stale offsets but the
|
|
1003
|
+
* resize observer fires immediately afterwards.
|
|
1004
|
+
*/
|
|
1005
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
1006
|
+
_columnRenderedWidths = new SvelteMap();
|
|
1007
|
+
_virtualisation = new VirtualisationState({
|
|
1008
|
+
getRows: () => this.paginationRowModel.rows,
|
|
1009
|
+
// Row alignment is browser-handled in the single-grid layout, so
|
|
1010
|
+
// the per-side height pool was deleted along with its observers.
|
|
1011
|
+
// VirtualisationState falls back to `getEstimatedRowHeight` when
|
|
1012
|
+
// no measured value is available, which is sufficient for
|
|
1013
|
+
// determining the spacer heights to within one overscan band.
|
|
1014
|
+
getRowHeight: () => 0,
|
|
1015
|
+
getEstimatedRowHeight: () => (this.isCompact ? 28 : 48),
|
|
1016
|
+
});
|
|
1017
|
+
/**
|
|
1018
|
+
* Delegating getter preserved for backwards compatibility. Reads
|
|
1019
|
+
* through to `_sorting.sortedData`, which owns the comparator-hoist
|
|
1020
|
+
* optimisation and the `AbortController` short-circuit. Consumers
|
|
1021
|
+
* inside `src/lib` already point at `tableContext.sortedData`; the
|
|
1022
|
+
* delegation will be removed once Phase 6 v-next migrates every
|
|
1023
|
+
* reader to `tableContext._sorting.sortedData` directly.
|
|
1024
|
+
*/
|
|
1025
|
+
get sortedData() {
|
|
1026
|
+
return this._sorting.sortedData;
|
|
1027
|
+
}
|
|
827
1028
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
828
1029
|
_sortedRowModel = $state({ rows: [], rowsById: new Map() });
|
|
829
1030
|
get sortedRowModel() {
|
|
@@ -972,483 +1173,132 @@ export class TableContext {
|
|
|
972
1173
|
}
|
|
973
1174
|
return makeRow(def);
|
|
974
1175
|
};
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1176
|
+
// Iterating `this.columnDefs.filter(...)` instead of
|
|
1177
|
+
// `this.columnPinning.left?.keys()` puts pinned-left body
|
|
1178
|
+
// cells in declaration order (matching the header rendering
|
|
1179
|
+
// and the `getVisualColumns` tab-order). Iterating the
|
|
1180
|
+
// pinning Map's keys produced *insertion* order so that
|
|
1181
|
+
// pinning Name then ID gave a body row of [Name, ID] while
|
|
1182
|
+
// the header above it stayed [ID, Name]. See the duplicate
|
|
1183
|
+
// factory in `createRow` below for the same fix.
|
|
1184
|
+
const leftVisibleCells = $derived(this.columnDefs
|
|
978
1185
|
.filter((it) => {
|
|
979
|
-
return
|
|
1186
|
+
return (this.columnVisibility[it.id] !== false && this.columnPinning.left?.has(it.id) === true);
|
|
980
1187
|
})
|
|
981
1188
|
.map(makeRow));
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
_flushScheduled = false;
|
|
1018
|
-
_flushPendingSelectionChanges() {
|
|
1019
|
-
if (this._pendingSelectionChanges.size === 0) {
|
|
1020
|
-
return;
|
|
1021
|
-
}
|
|
1022
|
-
// Mutate in place to preserve SvelteMap's per-key reactive tracking.
|
|
1023
|
-
// Replacing _selectedItems with a cloned map would invalidate every .has()/.get()
|
|
1024
|
-
// subscriber across all visible rows, not just the changed keys.
|
|
1025
|
-
for (const [key, item] of this._pendingSelectionChanges) {
|
|
1026
|
-
if (item === null) {
|
|
1027
|
-
this.selectedItems.delete(key);
|
|
1028
|
-
}
|
|
1029
|
-
else {
|
|
1030
|
-
this.selectedItems.set(key, item);
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
this._pendingSelectionChanges.clear();
|
|
1034
|
-
this._flushScheduled = false;
|
|
1035
|
-
}
|
|
1036
|
-
_schedulePendingFlush() {
|
|
1037
|
-
if (this._flushScheduled) {
|
|
1038
|
-
return;
|
|
1039
|
-
}
|
|
1040
|
-
this._flushScheduled = true;
|
|
1041
|
-
tick()
|
|
1042
|
-
.then(() => {
|
|
1043
|
-
this._flushPendingSelectionChanges();
|
|
1044
|
-
})
|
|
1045
|
-
.catch(devCatch);
|
|
1046
|
-
}
|
|
1047
|
-
selectionContains = (item) => {
|
|
1048
|
-
void item;
|
|
1049
|
-
void this.isSelectable;
|
|
1050
|
-
void this.selectedItems.size;
|
|
1051
|
-
// if (!$effect.tracking()) {
|
|
1052
|
-
// throw new Error(`selectionContains is not ran in a tracking context`);
|
|
1053
|
-
// }
|
|
1054
|
-
if (!this.isSelectable) {
|
|
1055
|
-
return true;
|
|
1056
|
-
}
|
|
1057
|
-
const rawIdentity = this._cachedKey(item);
|
|
1058
|
-
return rawIdentity instanceof Promise
|
|
1059
|
-
? rawIdentity.then((identity) => this.selectedItems.has(identity))
|
|
1060
|
-
: this.selectedItems.has(rawIdentity);
|
|
1061
|
-
};
|
|
1062
|
-
addInvalidValue = (item) => {
|
|
1063
|
-
const raw = this._cachedKey(item);
|
|
1064
|
-
if (raw instanceof Promise) {
|
|
1065
|
-
return raw.then((key) => {
|
|
1066
|
-
this.invalidValues.set(key, item);
|
|
1067
|
-
});
|
|
1068
|
-
}
|
|
1069
|
-
this.invalidValues.set(raw, item);
|
|
1070
|
-
};
|
|
1071
|
-
removeInvalidValue = (item) => {
|
|
1072
|
-
const raw = this._cachedKey(item);
|
|
1073
|
-
if (raw instanceof Promise) {
|
|
1074
|
-
return raw.then((key) => {
|
|
1075
|
-
this.invalidValues.delete(key);
|
|
1076
|
-
});
|
|
1077
|
-
}
|
|
1078
|
-
this.invalidValues.delete(raw);
|
|
1079
|
-
};
|
|
1080
|
-
setOpenModalCoordinates = (coordinates) => {
|
|
1081
|
-
this.openModalCoordinates = coordinates ? { ...coordinates } : null;
|
|
1082
|
-
};
|
|
1083
|
-
toggleHighlightedRow = (item) => {
|
|
1084
|
-
const raw = this._cachedKey(item);
|
|
1085
|
-
if (raw instanceof Promise) {
|
|
1086
|
-
return raw.then((key) => {
|
|
1087
|
-
if (this.highlightedItems.has(key)) {
|
|
1088
|
-
this.highlightedItems.delete(key);
|
|
1089
|
-
}
|
|
1090
|
-
else {
|
|
1091
|
-
this.highlightedItems.set(key, item);
|
|
1092
|
-
}
|
|
1093
|
-
});
|
|
1094
|
-
}
|
|
1095
|
-
if (this.highlightedItems.has(raw)) {
|
|
1096
|
-
this.highlightedItems.delete(raw);
|
|
1097
|
-
}
|
|
1098
|
-
else {
|
|
1099
|
-
this.highlightedItems.set(raw, item);
|
|
1100
|
-
}
|
|
1101
|
-
};
|
|
1102
|
-
resetHighlightedRows = () => {
|
|
1103
|
-
this.highlightedItems.clear();
|
|
1104
|
-
};
|
|
1105
|
-
toggleSelected = (item) => {
|
|
1106
|
-
const raw = this._cachedKey(item);
|
|
1107
|
-
if (raw instanceof Promise) {
|
|
1108
|
-
return raw.then((key) => {
|
|
1109
|
-
const pendingValue = this._pendingSelectionChanges.get(key);
|
|
1110
|
-
const isSelected = pendingValue !== undefined ? pendingValue !== null : this.selectedItems.has(key);
|
|
1111
|
-
this._pendingSelectionChanges.set(key, isSelected ? null : item);
|
|
1112
|
-
this._schedulePendingFlush();
|
|
1113
|
-
});
|
|
1114
|
-
}
|
|
1115
|
-
const pendingValue = this._pendingSelectionChanges.get(raw);
|
|
1116
|
-
const isSelected = pendingValue !== undefined ? pendingValue !== null : this.selectedItems.has(raw);
|
|
1117
|
-
this._pendingSelectionChanges.set(raw, isSelected ? null : item);
|
|
1118
|
-
this._schedulePendingFlush();
|
|
1119
|
-
};
|
|
1120
|
-
setSelected = (item, selected) => {
|
|
1121
|
-
const raw = this._cachedKey(item);
|
|
1122
|
-
if (raw instanceof Promise) {
|
|
1123
|
-
return raw.then((key) => {
|
|
1124
|
-
this._pendingSelectionChanges.set(key, selected ? item : null);
|
|
1125
|
-
this._schedulePendingFlush();
|
|
1126
|
-
});
|
|
1127
|
-
}
|
|
1128
|
-
this._pendingSelectionChanges.set(raw, selected ? item : null);
|
|
1129
|
-
this._schedulePendingFlush();
|
|
1130
|
-
};
|
|
1131
|
-
setSelectionMap = (set) => {
|
|
1132
|
-
this._selectedItems = set;
|
|
1133
|
-
};
|
|
1134
|
-
updateSelectedItems = async () => {
|
|
1135
|
-
if (this.selectedItems.size === 0) {
|
|
1136
|
-
return;
|
|
1137
|
-
}
|
|
1138
|
-
const pageStartIndex = this.paginationRepo ? this.paginationRepo.pageStartIndex : 0;
|
|
1139
|
-
const sortedData = this.sortedData;
|
|
1140
|
-
const pageEndIndexExclusive = this.paginationRepo
|
|
1141
|
-
? Math.min(this.paginationRepo.pageEndIndexExclusive, sortedData.length)
|
|
1142
|
-
: sortedData.length;
|
|
1143
|
-
// Build key → item map for the current page (O(page_size) extractions)
|
|
1144
|
-
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
1145
|
-
const pageKeyMap = new Map();
|
|
1146
|
-
const pending = [];
|
|
1147
|
-
for (let i = pageStartIndex; i < pageEndIndexExclusive; i++) {
|
|
1148
|
-
const item = sortedData[i];
|
|
1149
|
-
const raw = this._cachedKey(item);
|
|
1150
|
-
if (raw instanceof Promise) {
|
|
1151
|
-
pending.push(raw.then((key) => {
|
|
1152
|
-
pageKeyMap.set(key, item);
|
|
1153
|
-
}));
|
|
1154
|
-
}
|
|
1155
|
-
else {
|
|
1156
|
-
pageKeyMap.set(raw, item);
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
if (pending.length > 0) {
|
|
1160
|
-
await Promise.all(pending);
|
|
1161
|
-
}
|
|
1162
|
-
// Update only selected items that appear on this page (O(selected_size))
|
|
1163
|
-
for (const key of this.selectedItems.keys()) {
|
|
1164
|
-
const newItem = pageKeyMap.get(key);
|
|
1165
|
-
if (newItem !== undefined) {
|
|
1166
|
-
this.selectedItems.set(key, newItem);
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
};
|
|
1170
|
-
resetSelectedItems = () => {
|
|
1171
|
-
this._selectedItems.clear();
|
|
1172
|
-
};
|
|
1173
|
-
selectAllInCurrentPage = async () => {
|
|
1174
|
-
void this.paginationRepo?.pageStartIndex;
|
|
1175
|
-
const sortedData = this.sortedData;
|
|
1176
|
-
const pageStartIndex = this.paginationRepo ? this.paginationRepo.pageStartIndex : 0;
|
|
1177
|
-
const pageEndIndexExclusive = this.paginationRepo
|
|
1178
|
-
? Math.min(this.paginationRepo.pageEndIndexExclusive, sortedData.length)
|
|
1179
|
-
: sortedData.length;
|
|
1180
|
-
// Will be doing one full reassignment to this.selectedItems to avoid svelteMap redraws
|
|
1181
|
-
// const cloned = new SvelteMap(this.selectedItems);
|
|
1182
|
-
const sel = this.selectedItems; // HACK: keep the same svelteMap going so that we can support setSelectionMap()
|
|
1183
|
-
// if this is deemed problematic, we'll need to find another way to ask for a new map from the context consumer
|
|
1184
|
-
const pending = [];
|
|
1185
|
-
if (this.allSelectedInCurrentPage) {
|
|
1186
|
-
for (let i = pageStartIndex; i < pageEndIndexExclusive; i++) {
|
|
1187
|
-
const raw = this._cachedKey(sortedData[i]);
|
|
1188
|
-
if (raw instanceof Promise) {
|
|
1189
|
-
pending.push(raw.then((key) => {
|
|
1190
|
-
sel.delete(key);
|
|
1191
|
-
}));
|
|
1192
|
-
}
|
|
1193
|
-
else {
|
|
1194
|
-
sel.delete(raw);
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
else {
|
|
1199
|
-
const selectableMethod = this.columnDefs.find((it) => it instanceof RowSelectionDef)?.isSelectable;
|
|
1200
|
-
if (selectableMethod) {
|
|
1201
|
-
for (let i = pageStartIndex; i < pageEndIndexExclusive; i++) {
|
|
1202
|
-
const item = sortedData[i];
|
|
1203
|
-
const relIdx = i - pageStartIndex;
|
|
1204
|
-
const s = selectableMethod(item, relIdx);
|
|
1205
|
-
const doSet = (ok) => {
|
|
1206
|
-
if (!ok) {
|
|
1207
|
-
return;
|
|
1208
|
-
}
|
|
1209
|
-
const raw = this._cachedKey(item);
|
|
1210
|
-
if (raw instanceof Promise) {
|
|
1211
|
-
return raw.then((key) => {
|
|
1212
|
-
sel.set(key, item);
|
|
1213
|
-
});
|
|
1214
|
-
}
|
|
1215
|
-
sel.set(raw, item);
|
|
1216
|
-
};
|
|
1217
|
-
const r = s instanceof Promise ? s.then(doSet) : doSet(s);
|
|
1218
|
-
if (r instanceof Promise) {
|
|
1219
|
-
pending.push(r);
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
else {
|
|
1224
|
-
for (let i = pageStartIndex; i < pageEndIndexExclusive; i++) {
|
|
1225
|
-
const item = sortedData[i];
|
|
1226
|
-
const raw = this._cachedKey(item);
|
|
1227
|
-
if (raw instanceof Promise) {
|
|
1228
|
-
pending.push(raw.then((key) => {
|
|
1229
|
-
sel.set(key, item);
|
|
1230
|
-
}));
|
|
1231
|
-
}
|
|
1232
|
-
else {
|
|
1233
|
-
sel.set(raw, item);
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
if (pending.length > 0) {
|
|
1239
|
-
await Promise.all(pending);
|
|
1240
|
-
}
|
|
1241
|
-
// HACK
|
|
1242
|
-
// this._selectedItems = cloned;
|
|
1243
|
-
};
|
|
1244
|
-
onHighlightAll = () => {
|
|
1245
|
-
if (this.allHighlighted) {
|
|
1246
|
-
// console.debug('All were highlighted. Clearing');
|
|
1247
|
-
this.highlightedItems.clear();
|
|
1248
|
-
return Promise.resolve();
|
|
1249
|
-
}
|
|
1250
|
-
else {
|
|
1251
|
-
const items = this.currentPageData;
|
|
1252
|
-
return asyncForEach(items, (item) => {
|
|
1253
|
-
const raw = this._cachedKey(item);
|
|
1254
|
-
if (raw instanceof Promise) {
|
|
1255
|
-
return raw.then((key) => {
|
|
1256
|
-
this.highlightedItems.set(key, item);
|
|
1257
|
-
});
|
|
1258
|
-
}
|
|
1259
|
-
this.highlightedItems.set(raw, item);
|
|
1260
|
-
return;
|
|
1261
|
-
});
|
|
1262
|
-
}
|
|
1263
|
-
};
|
|
1264
|
-
onEditCell = async (item, coords, value) => {
|
|
1265
|
-
const rawOldKey = this._cachedKey(item);
|
|
1266
|
-
const oldKey = rawOldKey instanceof Promise ? await rawOldKey : rawOldKey;
|
|
1267
|
-
const wasSelected = this.selectedItems.has(oldKey);
|
|
1268
|
-
if (wasSelected) {
|
|
1269
|
-
this.selectedItems.delete(oldKey);
|
|
1270
|
-
}
|
|
1271
|
-
// Invalidate cache — _onEditCell may mutate the item's key field in-place
|
|
1272
|
-
this._keyCache.delete(item);
|
|
1273
|
-
await this._onEditCell?.(item, coords, value);
|
|
1274
|
-
if (wasSelected && this.isSelectable) {
|
|
1275
|
-
const rawNewKey = this._cachedKey(item);
|
|
1276
|
-
const newKey = rawNewKey instanceof Promise ? await rawNewKey : rawNewKey;
|
|
1277
|
-
if (newKey) {
|
|
1278
|
-
this.selectedItems.set(newKey, item);
|
|
1279
|
-
}
|
|
1280
|
-
// this.recalculateAllSelectedInCurrentPage();
|
|
1281
|
-
}
|
|
1282
|
-
};
|
|
1283
|
-
handleTdKeydown = (e) => {
|
|
1284
|
-
e.stopPropagation();
|
|
1285
|
-
if (e.key.toLocaleUpperCase() === 'F' && e.ctrlKey && this.allowQuickFiltering) {
|
|
1286
|
-
e.preventDefault();
|
|
1287
|
-
this.toggleQuickFiltering();
|
|
1288
|
-
}
|
|
1289
|
-
else if (e.key === 'Escape') {
|
|
1290
|
-
e.preventDefault();
|
|
1291
|
-
this.focusStartIsBeingEdited = false;
|
|
1292
|
-
}
|
|
1293
|
-
else if (e.key === 'Tab') {
|
|
1294
|
-
//allow regular operation in side panels
|
|
1295
|
-
const target = e.target;
|
|
1296
|
-
if (target?.closest('.side-panel') != null ||
|
|
1297
|
-
target?.attributes.getNamedItem('data-type')?.value === 'quicksearch') {
|
|
1298
|
-
return;
|
|
1299
|
-
}
|
|
1300
|
-
e.preventDefault();
|
|
1301
|
-
if (e.getModifierState('Shift')) {
|
|
1302
|
-
this.tabBackward();
|
|
1303
|
-
}
|
|
1304
|
-
else {
|
|
1305
|
-
this.tabForward();
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
if (e.target.tagName === 'INPUT' &&
|
|
1309
|
-
e.target.attributes.getNamedItem('type')?.value !== 'checkbox') {
|
|
1310
|
-
return;
|
|
1311
|
-
}
|
|
1312
|
-
this.handleKeystroke(e).catch(devCatch);
|
|
1313
|
-
};
|
|
1314
|
-
async handleKeystroke(e) {
|
|
1315
|
-
switch (e.key) {
|
|
1316
|
-
case 'Enter':
|
|
1317
|
-
case 'F2':
|
|
1318
|
-
if (!e.target?.classList.contains('editable')) {
|
|
1319
|
-
break;
|
|
1320
|
-
}
|
|
1321
|
-
e.preventDefault();
|
|
1322
|
-
e.target?.dispatchEvent(new CustomEvent('edit', { detail: { selectAll: e.key === 'Enter' } }));
|
|
1323
|
-
// focusStartIsBeingEdited.set(true);
|
|
1324
|
-
break;
|
|
1325
|
-
case 'ArrowUp': {
|
|
1326
|
-
e.preventDefault();
|
|
1327
|
-
if (e.getModifierState('Control')) {
|
|
1328
|
-
if (e.getModifierState('Shift')) {
|
|
1329
|
-
if (!this.allowCopy) {
|
|
1330
|
-
return;
|
|
1331
|
-
}
|
|
1332
|
-
this.focusToTop();
|
|
1333
|
-
}
|
|
1334
|
-
else {
|
|
1335
|
-
this.moveToTop();
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
else if (e.getModifierState('Shift')) {
|
|
1339
|
-
if (!this.allowCopy) {
|
|
1340
|
-
return;
|
|
1341
|
-
}
|
|
1342
|
-
this.moveFocusUp();
|
|
1343
|
-
}
|
|
1344
|
-
else {
|
|
1345
|
-
this.setFocusUp();
|
|
1346
|
-
}
|
|
1347
|
-
return;
|
|
1348
|
-
}
|
|
1349
|
-
case 'ArrowDown': {
|
|
1350
|
-
e.preventDefault();
|
|
1351
|
-
if (e.getModifierState('Control')) {
|
|
1352
|
-
if (e.getModifierState('Shift')) {
|
|
1353
|
-
if (!this.allowCopy) {
|
|
1354
|
-
return;
|
|
1355
|
-
}
|
|
1356
|
-
this.focusToBottom();
|
|
1357
|
-
}
|
|
1358
|
-
else {
|
|
1359
|
-
this.moveToBottom();
|
|
1360
|
-
}
|
|
1361
|
-
}
|
|
1362
|
-
else if (e.getModifierState('Shift')) {
|
|
1363
|
-
if (!this.allowCopy) {
|
|
1364
|
-
return;
|
|
1365
|
-
}
|
|
1366
|
-
this.moveFocusDown();
|
|
1367
|
-
}
|
|
1368
|
-
else {
|
|
1369
|
-
this.setFocusDown();
|
|
1370
|
-
}
|
|
1371
|
-
return;
|
|
1372
|
-
}
|
|
1373
|
-
// TODO add ctrl-home and ctrl-end as ways to get to top-left/bottom-right
|
|
1374
|
-
case 'ArrowLeft':
|
|
1375
|
-
case 'Home': {
|
|
1376
|
-
e.preventDefault();
|
|
1377
|
-
if (e.getModifierState('Control') || e.key === 'Home') {
|
|
1378
|
-
if (e.getModifierState('Shift')) {
|
|
1379
|
-
if (!this.allowCopy) {
|
|
1380
|
-
return;
|
|
1381
|
-
}
|
|
1382
|
-
this.focusToStart();
|
|
1383
|
-
}
|
|
1384
|
-
else {
|
|
1385
|
-
this.moveToStart();
|
|
1386
|
-
}
|
|
1387
|
-
//these two at the bottom shouldn't happen on a home press
|
|
1388
|
-
}
|
|
1389
|
-
else if (e.getModifierState('Shift') && this.allowCopy) {
|
|
1390
|
-
this.moveFocusLeft();
|
|
1391
|
-
}
|
|
1392
|
-
else {
|
|
1393
|
-
this.setFocusLeft();
|
|
1394
|
-
}
|
|
1395
|
-
return;
|
|
1396
|
-
}
|
|
1397
|
-
case 'ArrowRight':
|
|
1398
|
-
case 'End': {
|
|
1399
|
-
e.preventDefault();
|
|
1400
|
-
if (e.getModifierState('Control') || e.key === 'End') {
|
|
1401
|
-
if (e.getModifierState('Shift')) {
|
|
1402
|
-
if (!this.allowCopy) {
|
|
1403
|
-
return;
|
|
1404
|
-
}
|
|
1405
|
-
this.focusToEnd();
|
|
1406
|
-
}
|
|
1407
|
-
else {
|
|
1408
|
-
this.moveToEnd();
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
else if (e.getModifierState('Shift') && this.allowCopy) {
|
|
1412
|
-
this.moveFocusRight();
|
|
1413
|
-
}
|
|
1414
|
-
else {
|
|
1415
|
-
this.setFocusRight();
|
|
1416
|
-
}
|
|
1417
|
-
return;
|
|
1418
|
-
}
|
|
1419
|
-
default: {
|
|
1420
|
-
if (this.debug) {
|
|
1421
|
-
console.log(`handleKeystroke: ignoring key ${e.key}`);
|
|
1422
|
-
}
|
|
1423
|
-
break;
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
if (this.allowCopy) {
|
|
1427
|
-
switch (e.key) {
|
|
1428
|
-
case 'a': {
|
|
1429
|
-
if (e.ctrlKey) {
|
|
1430
|
-
e.preventDefault();
|
|
1431
|
-
this.moveToTop();
|
|
1432
|
-
this.moveToStart();
|
|
1433
|
-
this.focusToBottom();
|
|
1434
|
-
this.focusToEnd();
|
|
1435
|
-
}
|
|
1436
|
-
break;
|
|
1437
|
-
}
|
|
1438
|
-
case 'c':
|
|
1439
|
-
case 'C':
|
|
1440
|
-
if (e.getModifierState('Control')) {
|
|
1441
|
-
e.preventDefault();
|
|
1442
|
-
await this.copyDataToClipboard();
|
|
1443
|
-
}
|
|
1444
|
-
break;
|
|
1445
|
-
default: {
|
|
1446
|
-
//nothing
|
|
1447
|
-
break;
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1189
|
+
const rightVisibleCells = $derived(this.columnDefs
|
|
1190
|
+
.filter((it) => {
|
|
1191
|
+
return (this.columnVisibility[it.id] !== false &&
|
|
1192
|
+
this.columnPinning.right?.has(it.id) === true);
|
|
1193
|
+
})
|
|
1194
|
+
.map(makeRow));
|
|
1195
|
+
const centerVisibleCells = $derived(this.columnDefs
|
|
1196
|
+
.filter((it) => {
|
|
1197
|
+
return (this.columnVisibility[it.id] !== false &&
|
|
1198
|
+
!(this.columnPinning.left?.has(it.id) || this.columnPinning.right?.has(it.id)));
|
|
1199
|
+
})
|
|
1200
|
+
.map(makeRow));
|
|
1201
|
+
return row;
|
|
1202
|
+
});
|
|
1203
|
+
});
|
|
1204
|
+
_paginationRowModel = $state({
|
|
1205
|
+
rows: [],
|
|
1206
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
1207
|
+
rowsById: new Map(),
|
|
1208
|
+
});
|
|
1209
|
+
get paginationRowModel() {
|
|
1210
|
+
return this._paginationRowModel;
|
|
1211
|
+
}
|
|
1212
|
+
// #endregion
|
|
1213
|
+
// #region public methods
|
|
1214
|
+
/**
|
|
1215
|
+
* Delegating wrapper preserved for backwards compatibility. The
|
|
1216
|
+
* key-extraction cache lives on `_selection` (Phase 6.5); this
|
|
1217
|
+
* method forwards to `_selection.cachedKey(item)` so legacy call
|
|
1218
|
+
* sites in `getHighlightedRowData` / `getFocusElementData` /
|
|
1219
|
+
* `originalRowIndices` / `sortedRowModel` / `onHighlightAll` see
|
|
1220
|
+
* no behavioural change.
|
|
1221
|
+
*/
|
|
1222
|
+
_cachedKey(item) {
|
|
1223
|
+
return this._selection.cachedKey(item);
|
|
1451
1224
|
}
|
|
1225
|
+
/** Delegating accessor preserved for backwards compatibility. The
|
|
1226
|
+
* implementation lives on `_selection.selectionContains` (Phase 6.5). */
|
|
1227
|
+
selectionContains = (item) => {
|
|
1228
|
+
return this._selection.selectionContains(item);
|
|
1229
|
+
};
|
|
1230
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1231
|
+
addInvalidValue = (item) => {
|
|
1232
|
+
return this._selection.addInvalidValue(item);
|
|
1233
|
+
};
|
|
1234
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1235
|
+
removeInvalidValue = (item) => {
|
|
1236
|
+
return this._selection.removeInvalidValue(item);
|
|
1237
|
+
};
|
|
1238
|
+
/** Stores or clears the coordinates of the open per-cell modal. */
|
|
1239
|
+
setOpenModalCoordinates = (coordinates) => {
|
|
1240
|
+
this.openModalCoordinates = coordinates ? { ...coordinates } : null;
|
|
1241
|
+
};
|
|
1242
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1243
|
+
toggleHighlightedRow = (item) => {
|
|
1244
|
+
return this._selection.toggleHighlightedRow(item);
|
|
1245
|
+
};
|
|
1246
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1247
|
+
resetHighlightedRows = () => {
|
|
1248
|
+
this._selection.resetHighlightedRows();
|
|
1249
|
+
};
|
|
1250
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1251
|
+
toggleSelected = (item) => {
|
|
1252
|
+
return this._selection.toggleSelected(item);
|
|
1253
|
+
};
|
|
1254
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1255
|
+
setSelected = (item, selected) => {
|
|
1256
|
+
return this._selection.setSelected(item, selected);
|
|
1257
|
+
};
|
|
1258
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1259
|
+
setSelectionMap = (set) => {
|
|
1260
|
+
this._selection.setSelectionMap(set);
|
|
1261
|
+
};
|
|
1262
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1263
|
+
updateSelectedItems = async () => {
|
|
1264
|
+
await this._selection.updateSelectedItems();
|
|
1265
|
+
};
|
|
1266
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1267
|
+
resetSelectedItems = () => {
|
|
1268
|
+
this._selection.resetSelectedItems();
|
|
1269
|
+
};
|
|
1270
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1271
|
+
selectAllInCurrentPage = async () => {
|
|
1272
|
+
await this._selection.selectAllInCurrentPage();
|
|
1273
|
+
};
|
|
1274
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1275
|
+
onHighlightAll = () => {
|
|
1276
|
+
return this._selection.onHighlightAll();
|
|
1277
|
+
};
|
|
1278
|
+
/**
|
|
1279
|
+
* Invoked by every editable cell on commit. Split between this
|
|
1280
|
+
* class (the user-supplied edit callback) and `_selection`
|
|
1281
|
+
* (the key-cache invalidation + selection-rebind half). The
|
|
1282
|
+
* sub-context's `handleItemMutation` accepts a thunk that runs
|
|
1283
|
+
* the consumer callback between the pre- and post-edit selection
|
|
1284
|
+
* bookkeeping so the boundary stays explicit.
|
|
1285
|
+
*/
|
|
1286
|
+
onEditCell = async (item, coords, value) => {
|
|
1287
|
+
await this._selection.handleItemMutation(item, async () => {
|
|
1288
|
+
await this._onEditCell?.(item, coords, value);
|
|
1289
|
+
});
|
|
1290
|
+
};
|
|
1291
|
+
/**
|
|
1292
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
1293
|
+
* implementation lives on `_focus.handleTdKeydown` (Phase 6.6).
|
|
1294
|
+
* Bound to the root `<table>` container's `onkeydown` listener;
|
|
1295
|
+
* routes Ctrl+F / Escape / Tab synchronously and dispatches the
|
|
1296
|
+
* arrow-key / Home / End / Ctrl+A / Ctrl+C path asynchronously.
|
|
1297
|
+
*/
|
|
1298
|
+
handleTdKeydown = (e) => {
|
|
1299
|
+
this._focus.handleTdKeydown(e);
|
|
1300
|
+
};
|
|
1301
|
+
/** Toggles quick-filtering visibility and refocuses the matching cell. */
|
|
1452
1302
|
toggleQuickFiltering = () => {
|
|
1453
1303
|
if (this.allowQuickFiltering) {
|
|
1454
1304
|
const fs = this.focusStart;
|
|
@@ -1466,13 +1316,26 @@ export class TableContext {
|
|
|
1466
1316
|
.catch(devCatch);
|
|
1467
1317
|
}
|
|
1468
1318
|
};
|
|
1319
|
+
/**
|
|
1320
|
+
* Applies an updater to the column-pinning state (left / right
|
|
1321
|
+
* pins). Delegates to `_columnLayout.setColumnPinning`, which
|
|
1322
|
+
* enforces the `allowColumnReordering` master gate.
|
|
1323
|
+
*/
|
|
1469
1324
|
setColumnPinning = (updater) => {
|
|
1470
|
-
|
|
1471
|
-
return;
|
|
1472
|
-
}
|
|
1473
|
-
this.columnPinning = updater(this.columnPinning);
|
|
1325
|
+
this._columnLayout.setColumnPinning(updater);
|
|
1474
1326
|
};
|
|
1475
|
-
|
|
1327
|
+
/**
|
|
1328
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
1329
|
+
* actual `$state` slot lives on `_filtering`; this getter / setter
|
|
1330
|
+
* pair forwards reads and writes so existing consumers (notably
|
|
1331
|
+
* `generateTableOptions`) need no migration.
|
|
1332
|
+
*/
|
|
1333
|
+
get updateFilters() {
|
|
1334
|
+
return this._filtering.updateFilters;
|
|
1335
|
+
}
|
|
1336
|
+
set updateFilters(v) {
|
|
1337
|
+
this._filtering.updateFilters = v;
|
|
1338
|
+
}
|
|
1476
1339
|
updateVisibility = (stateOrCallback) => {
|
|
1477
1340
|
if (typeof stateOrCallback == 'function') {
|
|
1478
1341
|
this.columnVisibility = stateOrCallback(this.columnVisibility);
|
|
@@ -1495,6 +1358,7 @@ export class TableContext {
|
|
|
1495
1358
|
// return this._rowModel;
|
|
1496
1359
|
// }
|
|
1497
1360
|
lastPage = $derived(this.dataRepo.lastPage ?? 0);
|
|
1361
|
+
/** Canvas-measured width of a column header label, plus padding. */
|
|
1498
1362
|
measureHeaderWidth = (columnDef) => {
|
|
1499
1363
|
const label = columnDef.header;
|
|
1500
1364
|
if (typeof label === 'string' && browser) {
|
|
@@ -1516,6 +1380,7 @@ export class TableContext {
|
|
|
1516
1380
|
}
|
|
1517
1381
|
return 0;
|
|
1518
1382
|
};
|
|
1383
|
+
/** Sizes a column to fit its widest current-page cell label. */
|
|
1519
1384
|
autoResize = async (columnDef) => {
|
|
1520
1385
|
if (!browser) {
|
|
1521
1386
|
return;
|
|
@@ -1541,6 +1406,11 @@ export class TableContext {
|
|
|
1541
1406
|
}
|
|
1542
1407
|
this.columnSizing[colId] = maxWidth + 32; //standard extra cell padding
|
|
1543
1408
|
};
|
|
1409
|
+
/**
|
|
1410
|
+
* Drops the in-progress column drag onto the supplied target,
|
|
1411
|
+
* updating `columnDefs`, pinning state, and focus. No-op when
|
|
1412
|
+
* column reordering is disabled.
|
|
1413
|
+
*/
|
|
1544
1414
|
reorderColumn = (dropTarget) => {
|
|
1545
1415
|
if (!this.allowColumnReordering) {
|
|
1546
1416
|
return;
|
|
@@ -1602,6 +1472,41 @@ export class TableContext {
|
|
|
1602
1472
|
this.columnDefs = newDefs;
|
|
1603
1473
|
// this.forceVisibilityUpdate();
|
|
1604
1474
|
};
|
|
1475
|
+
/**
|
|
1476
|
+
* Programmatic column-reorder primitive used by keyboard accessibility
|
|
1477
|
+
* shortcuts in the column-management panel. Moves the column at
|
|
1478
|
+
* {@link currentIndex} to {@link newIndex} (both expressed in the
|
|
1479
|
+
* sorted-by-`index` order of {@link columnDefs}). No-op when column
|
|
1480
|
+
* reordering is disabled or indices are out of range / unchanged.
|
|
1481
|
+
*/
|
|
1482
|
+
moveColumnTo = (currentIndex, newIndex) => {
|
|
1483
|
+
if (!this.allowColumnReordering) {
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
const newDefs = this.columnDefs.concat();
|
|
1487
|
+
newDefs
|
|
1488
|
+
.sort((a, b) => a.index - b.index)
|
|
1489
|
+
.forEach((element, index) => {
|
|
1490
|
+
element.index = index;
|
|
1491
|
+
});
|
|
1492
|
+
if (currentIndex < 0 ||
|
|
1493
|
+
currentIndex >= newDefs.length ||
|
|
1494
|
+
newIndex < 0 ||
|
|
1495
|
+
newIndex >= newDefs.length ||
|
|
1496
|
+
currentIndex === newIndex) {
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
const [moved] = newDefs.splice(currentIndex, 1);
|
|
1500
|
+
newDefs.splice(newIndex, 0, moved);
|
|
1501
|
+
newDefs.forEach((element, index) => {
|
|
1502
|
+
element.index = index;
|
|
1503
|
+
});
|
|
1504
|
+
this._setNewPinningState();
|
|
1505
|
+
this.updateDefsBasedOnPinning();
|
|
1506
|
+
this.unfocus();
|
|
1507
|
+
this.columnDefs = newDefs;
|
|
1508
|
+
};
|
|
1509
|
+
/** Copies the highlighted rows to the OS clipboard as TSV / CSV. */
|
|
1605
1510
|
copyHighlightedRowsToClipboard = async () => {
|
|
1606
1511
|
// NOTE: False Positive. Clipboard does not exist in http environments
|
|
1607
1512
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
@@ -1611,6 +1516,7 @@ export class TableContext {
|
|
|
1611
1516
|
}
|
|
1612
1517
|
return this._performRowCopy(await this.getHighlightedRowData());
|
|
1613
1518
|
};
|
|
1519
|
+
/** Copies the currently focused cell range to the OS clipboard. */
|
|
1614
1520
|
copyDataToClipboard = async () => {
|
|
1615
1521
|
// NOTE: False Positive. Clipboard does not exist in http environments
|
|
1616
1522
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
@@ -1645,11 +1551,13 @@ export class TableContext {
|
|
|
1645
1551
|
const value = columnOptions?.find((it) => it.value === focusedElementData[0][0])?.label ??
|
|
1646
1552
|
focusedElementData[0][0]?.toString() ??
|
|
1647
1553
|
'';
|
|
1648
|
-
|
|
1554
|
+
// Single-cell copy bypasses TSV/CSV wrapping but still lands
|
|
1555
|
+
// in a spreadsheet; neutralise formula-injection prefixes.
|
|
1556
|
+
return escapeForSpreadsheet(value);
|
|
1649
1557
|
}
|
|
1650
1558
|
else {
|
|
1651
1559
|
const value = focusedElementData[0][0]?.toString().trim() ?? '';
|
|
1652
|
-
return value;
|
|
1560
|
+
return escapeForSpreadsheet(value);
|
|
1653
1561
|
}
|
|
1654
1562
|
}
|
|
1655
1563
|
return `${shouldCopyHeaders ? `${focusedColumnIds.join(separator)}\n` : ''}${focusedElementData
|
|
@@ -1678,238 +1586,124 @@ export class TableContext {
|
|
|
1678
1586
|
})();
|
|
1679
1587
|
navigator.clipboard.writeText(value).catch(devCatch);
|
|
1680
1588
|
};
|
|
1589
|
+
/** Delegating accessor preserved for backwards compatibility. The
|
|
1590
|
+
* implementation lives on `_focus.startFocus` (Phase 6.6). */
|
|
1681
1591
|
startFocus = (coords) => {
|
|
1682
|
-
this.
|
|
1683
|
-
this.moveFocus(coords);
|
|
1592
|
+
this._focus.startFocus(coords);
|
|
1684
1593
|
};
|
|
1594
|
+
/** Delegating accessor preserved for backwards compatibility. The
|
|
1595
|
+
* implementation lives on `_focus.moveFocus` (Phase 6.6). */
|
|
1685
1596
|
moveFocus = (coords, startEditing = false) => {
|
|
1686
|
-
this.
|
|
1687
|
-
this.focusEnd = { ...coords };
|
|
1688
|
-
if (startEditing) {
|
|
1689
|
-
tick()
|
|
1690
|
-
.then(() => {
|
|
1691
|
-
this.focusStartIsBeingEdited = true;
|
|
1692
|
-
})
|
|
1693
|
-
.catch(devCatch);
|
|
1694
|
-
}
|
|
1597
|
+
this._focus.moveFocus(coords, startEditing);
|
|
1695
1598
|
};
|
|
1599
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1696
1600
|
unfocus = () => {
|
|
1697
|
-
this.
|
|
1698
|
-
this.focusStart = null;
|
|
1699
|
-
this.focusEnd = null;
|
|
1601
|
+
this._focus.unfocus();
|
|
1700
1602
|
};
|
|
1603
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1701
1604
|
focusToTop = () => {
|
|
1702
|
-
|
|
1703
|
-
return;
|
|
1704
|
-
}
|
|
1705
|
-
this._moveFocusEndRow(-Infinity);
|
|
1605
|
+
this._focus.focusToTop();
|
|
1706
1606
|
};
|
|
1607
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1707
1608
|
focusToBottom = () => {
|
|
1708
|
-
|
|
1709
|
-
return;
|
|
1710
|
-
}
|
|
1711
|
-
this._moveFocusEndRow(Infinity);
|
|
1609
|
+
this._focus.focusToBottom();
|
|
1712
1610
|
};
|
|
1611
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1713
1612
|
moveToBottom = () => {
|
|
1714
|
-
this.
|
|
1613
|
+
this._focus.moveToBottom();
|
|
1715
1614
|
};
|
|
1615
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1716
1616
|
moveToTop = () => {
|
|
1717
|
-
this.
|
|
1617
|
+
this._focus.moveToTop();
|
|
1718
1618
|
};
|
|
1619
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1719
1620
|
moveFocusUp = () => {
|
|
1720
|
-
|
|
1721
|
-
return;
|
|
1722
|
-
}
|
|
1723
|
-
this._moveFocusEndRow(-1);
|
|
1621
|
+
this._focus.moveFocusUp();
|
|
1724
1622
|
};
|
|
1623
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1725
1624
|
setFocusUp = () => {
|
|
1726
|
-
this.
|
|
1625
|
+
this._focus.setFocusUp();
|
|
1727
1626
|
};
|
|
1627
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1728
1628
|
tabForward = () => {
|
|
1729
|
-
|
|
1730
|
-
return;
|
|
1731
|
-
}
|
|
1732
|
-
const defs = this.columnDefs;
|
|
1733
|
-
const visibility = this.columnVisibility;
|
|
1734
|
-
const indexBounds = defs.length;
|
|
1735
|
-
const rowBounds = this.sortedData.length;
|
|
1736
|
-
if (!this.focusStart || !rowBounds) {
|
|
1737
|
-
return;
|
|
1738
|
-
}
|
|
1739
|
-
let { column: index, row } = this.focusStart;
|
|
1740
|
-
++index;
|
|
1741
|
-
while (row < rowBounds) {
|
|
1742
|
-
while (index < indexBounds) {
|
|
1743
|
-
const newId = defs[index].id;
|
|
1744
|
-
if (newId && visibility[newId] !== false) {
|
|
1745
|
-
this.moveFocus({ id: newId, row, column: index }, this.focusStartIsBeingEdited);
|
|
1746
|
-
return;
|
|
1747
|
-
}
|
|
1748
|
-
++index;
|
|
1749
|
-
}
|
|
1750
|
-
index = 0;
|
|
1751
|
-
++row;
|
|
1752
|
-
}
|
|
1629
|
+
this._focus.tabForward();
|
|
1753
1630
|
};
|
|
1631
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1754
1632
|
tabBackward = () => {
|
|
1755
|
-
|
|
1756
|
-
return;
|
|
1757
|
-
}
|
|
1758
|
-
const maxIndexRight = this.columnDefs.length - 1;
|
|
1759
|
-
const defs = this.columnDefs;
|
|
1760
|
-
const visibility = this.columnVisibility;
|
|
1761
|
-
if (!this.focusStart) {
|
|
1762
|
-
return;
|
|
1763
|
-
}
|
|
1764
|
-
let { column: index, row } = this.focusStart;
|
|
1765
|
-
--index;
|
|
1766
|
-
while (row > -1) {
|
|
1767
|
-
while (index > -1) {
|
|
1768
|
-
const newId = defs[index].id;
|
|
1769
|
-
if (newId && visibility[newId] !== false) {
|
|
1770
|
-
this.moveFocus({ id: newId, row, column: index }, this.focusStartIsBeingEdited);
|
|
1771
|
-
return;
|
|
1772
|
-
}
|
|
1773
|
-
--index;
|
|
1774
|
-
}
|
|
1775
|
-
index = maxIndexRight;
|
|
1776
|
-
--row;
|
|
1777
|
-
}
|
|
1633
|
+
this._focus.tabBackward();
|
|
1778
1634
|
};
|
|
1635
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1779
1636
|
focusToStart = () => {
|
|
1780
|
-
|
|
1781
|
-
return;
|
|
1782
|
-
}
|
|
1783
|
-
if (!this.focusEnd) {
|
|
1784
|
-
return;
|
|
1785
|
-
}
|
|
1786
|
-
const visualCols = this._visualColumns;
|
|
1787
|
-
if (visualCols.length > 0) {
|
|
1788
|
-
const first = visualCols[0];
|
|
1789
|
-
this.focusEnd = { ...this.focusEnd, id: first.id, column: first.index };
|
|
1790
|
-
}
|
|
1637
|
+
this._focus.focusToStart();
|
|
1791
1638
|
};
|
|
1639
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1792
1640
|
moveToStart = () => {
|
|
1793
|
-
|
|
1794
|
-
return;
|
|
1795
|
-
}
|
|
1796
|
-
const visualCols = this._visualColumns;
|
|
1797
|
-
if (visualCols.length > 0) {
|
|
1798
|
-
const first = visualCols[0];
|
|
1799
|
-
this.moveFocus({ ...this.focusStart, id: first.id, column: first.index });
|
|
1800
|
-
}
|
|
1641
|
+
this._focus.moveToStart();
|
|
1801
1642
|
};
|
|
1643
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1802
1644
|
focusToEnd = () => {
|
|
1803
|
-
|
|
1804
|
-
return;
|
|
1805
|
-
}
|
|
1806
|
-
if (!this.focusEnd) {
|
|
1807
|
-
return;
|
|
1808
|
-
}
|
|
1809
|
-
const visualCols = this._visualColumns;
|
|
1810
|
-
if (visualCols.length > 0) {
|
|
1811
|
-
const last = visualCols[visualCols.length - 1];
|
|
1812
|
-
this.focusEnd = { ...this.focusEnd, id: last.id, column: last.index };
|
|
1813
|
-
}
|
|
1645
|
+
this._focus.focusToEnd();
|
|
1814
1646
|
};
|
|
1647
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1815
1648
|
moveToEnd = () => {
|
|
1816
|
-
|
|
1817
|
-
return;
|
|
1818
|
-
}
|
|
1819
|
-
const visualCols = this._visualColumns;
|
|
1820
|
-
if (visualCols.length > 0) {
|
|
1821
|
-
const last = visualCols[visualCols.length - 1];
|
|
1822
|
-
this.moveFocus({ ...this.focusStart, id: last.id, column: last.index });
|
|
1823
|
-
}
|
|
1649
|
+
this._focus.moveToEnd();
|
|
1824
1650
|
};
|
|
1651
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1825
1652
|
moveFocusDown = () => {
|
|
1826
|
-
|
|
1827
|
-
return;
|
|
1828
|
-
}
|
|
1829
|
-
this._moveFocusEndRow(1);
|
|
1653
|
+
this._focus.moveFocusDown();
|
|
1830
1654
|
};
|
|
1655
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1831
1656
|
setFocusDown = () => {
|
|
1832
|
-
this.
|
|
1657
|
+
this._focus.setFocusDown();
|
|
1833
1658
|
};
|
|
1659
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1834
1660
|
moveFocusLeft = () => {
|
|
1835
|
-
|
|
1836
|
-
return;
|
|
1837
|
-
}
|
|
1838
|
-
if (this.focusEnd != null) {
|
|
1839
|
-
const visualCols = this._visualColumns;
|
|
1840
|
-
const visualIndex = this.columnVisualIndex(this.focusEnd.id);
|
|
1841
|
-
if (visualIndex > 0) {
|
|
1842
|
-
const prev = visualCols[visualIndex - 1];
|
|
1843
|
-
this.focusEnd = { ...this.focusEnd, id: prev.id, column: prev.index };
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1661
|
+
this._focus.moveFocusLeft();
|
|
1846
1662
|
};
|
|
1663
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1847
1664
|
setFocusLeft = () => {
|
|
1848
|
-
|
|
1849
|
-
return;
|
|
1850
|
-
}
|
|
1851
|
-
const visualCols = this._visualColumns;
|
|
1852
|
-
const visualIndex = this.columnVisualIndex(this.focusStart.id);
|
|
1853
|
-
if (visualIndex > 0) {
|
|
1854
|
-
const prev = visualCols[visualIndex - 1];
|
|
1855
|
-
this.moveFocus({ ...this.focusStart, id: prev.id, column: prev.index });
|
|
1856
|
-
}
|
|
1665
|
+
this._focus.setFocusLeft();
|
|
1857
1666
|
};
|
|
1667
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1858
1668
|
moveFocusRight = () => {
|
|
1859
|
-
|
|
1860
|
-
return;
|
|
1861
|
-
}
|
|
1862
|
-
if (this.focusEnd != null) {
|
|
1863
|
-
const visualCols = this._visualColumns;
|
|
1864
|
-
const visualIndex = this.columnVisualIndex(this.focusEnd.id);
|
|
1865
|
-
if (visualIndex < visualCols.length - 1) {
|
|
1866
|
-
const next = visualCols[visualIndex + 1];
|
|
1867
|
-
this.focusEnd = { ...this.focusEnd, id: next.id, column: next.index };
|
|
1868
|
-
}
|
|
1869
|
-
}
|
|
1669
|
+
this._focus.moveFocusRight();
|
|
1870
1670
|
};
|
|
1671
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1871
1672
|
setFocusRight = () => {
|
|
1872
|
-
|
|
1873
|
-
return;
|
|
1874
|
-
}
|
|
1875
|
-
const visualCols = this._visualColumns;
|
|
1876
|
-
const visualIndex = this.columnVisualIndex(this.focusStart.id);
|
|
1877
|
-
if (visualIndex < visualCols.length - 1) {
|
|
1878
|
-
const next = visualCols[visualIndex + 1];
|
|
1879
|
-
this.moveFocus({ ...this.focusStart, id: next.id, column: next.index });
|
|
1880
|
-
}
|
|
1673
|
+
this._focus.setFocusRight();
|
|
1881
1674
|
};
|
|
1675
|
+
/** Delegating accessor preserved for backwards compatibility. */
|
|
1882
1676
|
endFocus = (coords) => {
|
|
1883
|
-
|
|
1884
|
-
this.isMousingDown = false;
|
|
1885
|
-
if (!this.allowCopy) {
|
|
1886
|
-
return;
|
|
1887
|
-
}
|
|
1888
|
-
this.focusEnd = { ...coords };
|
|
1889
|
-
}
|
|
1677
|
+
this._focus.endFocus(coords);
|
|
1890
1678
|
};
|
|
1679
|
+
/** Toggles compact row sizing and persists to localStorage. */
|
|
1891
1680
|
setIsCompact = (value) => {
|
|
1892
1681
|
this.isCompact = value;
|
|
1893
1682
|
this._updateLocalStorageSettings();
|
|
1894
1683
|
};
|
|
1684
|
+
/** Toggles alternating row colours and persists to localStorage. */
|
|
1895
1685
|
setAlternateRowColours = (value) => {
|
|
1896
1686
|
this.alternateRowColours = value;
|
|
1897
1687
|
this._updateLocalStorageSettings();
|
|
1898
1688
|
};
|
|
1689
|
+
/** Toggles single-click editing and persists to localStorage. */
|
|
1899
1690
|
setSingleClickEditing = (value) => {
|
|
1900
1691
|
this.singleClickEditing = value;
|
|
1901
1692
|
this._updateLocalStorageSettings();
|
|
1902
1693
|
};
|
|
1694
|
+
/** Toggles row-hover colour change and persists to localStorage. */
|
|
1903
1695
|
setChangeColourOnHover = (value) => {
|
|
1904
1696
|
this.changeColourOnHover = value;
|
|
1905
1697
|
this._updateLocalStorageSettings();
|
|
1906
1698
|
};
|
|
1699
|
+
/** Toggles whether copy-to-clipboard prepends a header row and persists. */
|
|
1907
1700
|
setCopyWithHeaders = (value) => {
|
|
1908
1701
|
if (this.allowCopy) {
|
|
1909
1702
|
this.copyWithHeaders = value;
|
|
1910
1703
|
this._updateLocalStorageSettings();
|
|
1911
1704
|
}
|
|
1912
1705
|
};
|
|
1706
|
+
/** Toggles CSV vs TSV copy-to-clipboard separator and persists. */
|
|
1913
1707
|
setCommaSeparatedCopy = (value) => {
|
|
1914
1708
|
if (this.allowCopy) {
|
|
1915
1709
|
this.commaSeparatedCopy = value;
|
|
@@ -1929,63 +1723,41 @@ export class TableContext {
|
|
|
1929
1723
|
// });
|
|
1930
1724
|
// }
|
|
1931
1725
|
// };
|
|
1726
|
+
/**
|
|
1727
|
+
* Delegating method preserved for backwards compatibility. Applies
|
|
1728
|
+
* the preset to the live column layout, forwards the preset's
|
|
1729
|
+
* `sortingState` to the data-repository updater, and persists the
|
|
1730
|
+
* selected name. The underlying implementation lives on
|
|
1731
|
+
* `_preferences.setSelectedPreset` (Phase 6.4).
|
|
1732
|
+
*
|
|
1733
|
+
* The pre-extraction debug warning ("no sorting state found") was
|
|
1734
|
+
* emitted unconditionally when `paginationRepo.updateSorting` was
|
|
1735
|
+
* absent; the sub-context skips that warning because the
|
|
1736
|
+
* `applySortingState` thunk already no-ops in that case. The
|
|
1737
|
+
* warning is reinstated below so the surface is byte-identical.
|
|
1738
|
+
*/
|
|
1932
1739
|
setSelectedPreset = (preset) => {
|
|
1933
|
-
if (this.debug) {
|
|
1934
|
-
console.log('setting selected preset:', preset);
|
|
1935
|
-
}
|
|
1936
|
-
this.columnDefs = preset.columnDefs;
|
|
1937
1740
|
if (!this.paginationRepo?.updateSorting) {
|
|
1938
1741
|
console.warn('no sorting state found');
|
|
1939
1742
|
}
|
|
1940
|
-
this.
|
|
1941
|
-
this.columnVisibility = preset.visibilityState;
|
|
1942
|
-
this.columnPinning = preset.columnPinningState;
|
|
1943
|
-
this.selectedPreset = preset;
|
|
1944
|
-
localStorage.setItem(`${this.userId}-${this.tableName}-selected-preset-name`, preset.name);
|
|
1743
|
+
this._preferences.setSelectedPreset(preset);
|
|
1945
1744
|
};
|
|
1745
|
+
/**
|
|
1746
|
+
* Delegating method preserved for backwards compatibility. Adds or
|
|
1747
|
+
* replaces a saved preset for the current column layout and
|
|
1748
|
+
* persists the full preset list to localStorage. Delegates to
|
|
1749
|
+
* `_preferences.saveColumnPreset` (Phase 6.4).
|
|
1750
|
+
*/
|
|
1946
1751
|
saveColumnPreset = (name) => {
|
|
1947
|
-
|
|
1948
|
-
return;
|
|
1949
|
-
}
|
|
1950
|
-
const visibilityState = this.columnVisibility;
|
|
1951
|
-
const columnPinningState = this.columnPinning;
|
|
1952
|
-
const columnDefs = this.columnDefs;
|
|
1953
|
-
const sortingState = this.paginationRepo?.sorting || [];
|
|
1954
|
-
const newPreset = { columnDefs, name, visibilityState, columnPinningState, sortingState };
|
|
1955
|
-
const lowercasePresetname = name.toLocaleLowerCase();
|
|
1956
|
-
const indexToReplace = this.columnPresets.findIndex((oldPreset) => oldPreset.name.toLocaleLowerCase() === lowercasePresetname);
|
|
1957
|
-
const newPresets = this.columnPresets.concat();
|
|
1958
|
-
if (indexToReplace > -1) {
|
|
1959
|
-
newPresets[indexToReplace] = newPreset;
|
|
1960
|
-
}
|
|
1961
|
-
else {
|
|
1962
|
-
newPresets.push(newPreset);
|
|
1963
|
-
}
|
|
1964
|
-
this.columnPresets = newPresets;
|
|
1965
|
-
const jsonFriendly = newPresets.map((preset) => {
|
|
1966
|
-
return {
|
|
1967
|
-
...preset,
|
|
1968
|
-
columnDefs: preset.columnDefs.map((it) => {
|
|
1969
|
-
return { ...it.toJSON(), header: it.header.toString() };
|
|
1970
|
-
}),
|
|
1971
|
-
columnPinningState: {
|
|
1972
|
-
left: [...(preset.columnPinningState.left?.entries() ?? [])],
|
|
1973
|
-
right: [...(preset.columnPinningState.right?.entries() ?? [])],
|
|
1974
|
-
},
|
|
1975
|
-
};
|
|
1976
|
-
});
|
|
1977
|
-
const stringified = JSON.stringify(jsonFriendly);
|
|
1978
|
-
localStorage.setItem(`${this.userId}-${this.tableName}-presets`, stringified);
|
|
1979
|
-
this.setSelectedPreset(newPreset);
|
|
1752
|
+
this._preferences.saveColumnPreset(name);
|
|
1980
1753
|
};
|
|
1981
1754
|
/**
|
|
1982
1755
|
* This listener should be added and removed based on isMousingDown
|
|
1983
1756
|
*/
|
|
1757
|
+
/** Delegating accessor preserved for backwards compatibility. The
|
|
1758
|
+
* implementation lives on `_focus.setFocusEnd` (Phase 6.6). */
|
|
1984
1759
|
setFocusEnd = (coords) => {
|
|
1985
|
-
|
|
1986
|
-
return;
|
|
1987
|
-
}
|
|
1988
|
-
this.focusEnd = coords;
|
|
1760
|
+
this._focus.setFocusEnd(coords);
|
|
1989
1761
|
};
|
|
1990
1762
|
// #region private methods
|
|
1991
1763
|
createRow(item, index, unsortedIndex) {
|
|
@@ -2048,11 +1820,36 @@ export class TableContext {
|
|
|
2048
1820
|
}
|
|
2049
1821
|
return makeRow(def);
|
|
2050
1822
|
};
|
|
2051
|
-
|
|
2052
|
-
|
|
1823
|
+
// Visibility filter applied to every cell collection so the cell
|
|
1824
|
+
// count in TableRow stays in sync with the visible-column count
|
|
1825
|
+
// that drives `--cols` in Table.svelte. Without this, a hidden
|
|
1826
|
+
// non-pinned column would still produce an empty cell wrapper in
|
|
1827
|
+
// the row, which would overflow the grid template and force the
|
|
1828
|
+
// extra cell onto an implicit grid row (visually splitting each
|
|
1829
|
+
// data row into two or three lines).
|
|
1830
|
+
//
|
|
1831
|
+
// Iterating `this.columnDefs.filter(...)` instead of
|
|
1832
|
+
// `this.columnPinning.left?.keys()` puts pinned-left body cells
|
|
1833
|
+
// in declaration order (matching the header rendering and the
|
|
1834
|
+
// `getVisualColumns` tab-order). Iterating the pinning Map's
|
|
1835
|
+
// keys produced *insertion* order: pinning Name then ID gave a
|
|
1836
|
+
// body row of [Name, ID] while the header row above it was
|
|
1837
|
+
// [ID, Name], and tab navigation walked [Name, ID] instead of
|
|
1838
|
+
// the visually-left-to-right [ID, Name].
|
|
1839
|
+
const leftVisibleCells = $derived(this.columnDefs
|
|
1840
|
+
.filter((it) => {
|
|
1841
|
+
return (this.columnVisibility[it.id] !== false && this.columnPinning.left?.has(it.id) === true);
|
|
1842
|
+
})
|
|
1843
|
+
.map(makeRow));
|
|
1844
|
+
const rightVisibleCells = $derived(this.columnDefs
|
|
1845
|
+
.filter((it) => {
|
|
1846
|
+
return (this.columnVisibility[it.id] !== false && this.columnPinning.right?.has(it.id) === true);
|
|
1847
|
+
})
|
|
1848
|
+
.map(makeRow));
|
|
2053
1849
|
const centerVisibleCells = $derived(this.columnDefs
|
|
2054
1850
|
.filter((it) => {
|
|
2055
|
-
return
|
|
1851
|
+
return (this.columnVisibility[it.id] !== false &&
|
|
1852
|
+
!(this.columnPinning.left?.has(it.id) || this.columnPinning.right?.has(it.id)));
|
|
2056
1853
|
})
|
|
2057
1854
|
.map(makeRow));
|
|
2058
1855
|
return row;
|
|
@@ -2062,87 +1859,57 @@ export class TableContext {
|
|
|
2062
1859
|
this.columnVisibility[it.id] !== false &&
|
|
2063
1860
|
!uncopyableColumns.has(it.columnType));
|
|
2064
1861
|
};
|
|
1862
|
+
/**
|
|
1863
|
+
* Builds the user-controlled settings blob from the current
|
|
1864
|
+
* `$state` slots and delegates the actual `localStorage.setItem`
|
|
1865
|
+
* call to `_preferences.persistSettings` (Phase 6.4). The settings
|
|
1866
|
+
* field set (`isCompact`, `singleClickEditing`, etc.) still lives
|
|
1867
|
+
* on `TableContext` because each one drives reactive UI; only the
|
|
1868
|
+
* persistence side of the boundary moved.
|
|
1869
|
+
*/
|
|
2065
1870
|
_updateLocalStorageSettings() {
|
|
2066
|
-
const isCompact = this.isCompact;
|
|
2067
|
-
const singleClickEditing = this.singleClickEditing;
|
|
2068
|
-
const alternateRowColours = this.alternateRowColours;
|
|
2069
|
-
const copyWithHeaders = this.copyWithHeaders;
|
|
2070
|
-
const commaSeparatedCopy = this.commaSeparatedCopy;
|
|
2071
|
-
const changeColourOnHover = this.changeColourOnHover;
|
|
2072
1871
|
const settingsObject = {
|
|
2073
|
-
isCompact,
|
|
2074
|
-
singleClickEditing,
|
|
2075
|
-
alternateRowColours,
|
|
2076
|
-
changeColourOnHover,
|
|
2077
|
-
copyWithHeaders,
|
|
2078
|
-
commaSeparatedCopy,
|
|
1872
|
+
isCompact: this.isCompact,
|
|
1873
|
+
singleClickEditing: this.singleClickEditing,
|
|
1874
|
+
alternateRowColours: this.alternateRowColours,
|
|
1875
|
+
changeColourOnHover: this.changeColourOnHover,
|
|
1876
|
+
copyWithHeaders: this.copyWithHeaders,
|
|
1877
|
+
commaSeparatedCopy: this.commaSeparatedCopy,
|
|
2079
1878
|
};
|
|
2080
|
-
|
|
1879
|
+
this._preferences.persistSettings(settingsObject);
|
|
2081
1880
|
}
|
|
2082
1881
|
/**
|
|
2083
|
-
*
|
|
2084
|
-
*
|
|
1882
|
+
* Delegating accessor preserved for backwards compatibility. The
|
|
1883
|
+
* cached visual-column array and its paired O(1) id->index map now
|
|
1884
|
+
* live on `_columnLayout` (Phase 6.3). The underscore prefix is
|
|
1885
|
+
* preserved on `TableContext` to keep the existing read sites in
|
|
1886
|
+
* `tabForward` / `focusToStart` / `moveFocusLeft` / etc. untouched.
|
|
2085
1887
|
*/
|
|
2086
1888
|
get _visualColumns() {
|
|
2087
|
-
|
|
2088
|
-
const pinLeft = this.columnPinning.left;
|
|
2089
|
-
const pinRight = this.columnPinning.right;
|
|
2090
|
-
const visible = this.columnDefs.filter((it) => visibility[it.id] !== false);
|
|
2091
|
-
return [
|
|
2092
|
-
...visible.filter((it) => pinLeft?.has(it.id)),
|
|
2093
|
-
...visible.filter((it) => !pinLeft?.has(it.id) && !pinRight?.has(it.id)),
|
|
2094
|
-
...visible.filter((it) => pinRight?.has(it.id)),
|
|
2095
|
-
];
|
|
1889
|
+
return this._columnLayout.visualColumns;
|
|
2096
1890
|
}
|
|
2097
|
-
/** Returns the visual index of the column with the given id, or
|
|
1891
|
+
/** Returns the visual index of the column with the given id, or
|
|
1892
|
+
* -1 if not found. Delegates to `_columnLayout.columnVisualIndex`
|
|
1893
|
+
* which provides the O(1) lookup. */
|
|
2098
1894
|
columnVisualIndex(id) {
|
|
2099
|
-
return this.
|
|
1895
|
+
return this._columnLayout.columnVisualIndex(id);
|
|
2100
1896
|
}
|
|
1897
|
+
/**
|
|
1898
|
+
* Delegating getter preserved for backwards compatibility. The
|
|
1899
|
+
* implementation lives on `_focus.focusStartVisualColumn`
|
|
1900
|
+
* (Phase 6.6); the visual-index lookup itself is owned by
|
|
1901
|
+
* `_columnLayout.columnVisualIndex` (Phase 6.3).
|
|
1902
|
+
*/
|
|
2101
1903
|
get focusStartVisualColumn() {
|
|
2102
|
-
|
|
2103
|
-
return -1;
|
|
2104
|
-
}
|
|
2105
|
-
return this.columnVisualIndex(this.focusStart.id);
|
|
1904
|
+
return this._focus.focusStartVisualColumn;
|
|
2106
1905
|
}
|
|
1906
|
+
/**
|
|
1907
|
+
* Delegating getter preserved for backwards compatibility. The
|
|
1908
|
+
* implementation lives on `_focus.focusEndVisualColumn`
|
|
1909
|
+
* (Phase 6.6).
|
|
1910
|
+
*/
|
|
2107
1911
|
get focusEndVisualColumn() {
|
|
2108
|
-
|
|
2109
|
-
return -1;
|
|
2110
|
-
}
|
|
2111
|
-
return this.columnVisualIndex(this.focusEnd.id);
|
|
2112
|
-
}
|
|
2113
|
-
_moveFocusEndRow(offset) {
|
|
2114
|
-
if (!this.allowCopy) {
|
|
2115
|
-
return;
|
|
2116
|
-
}
|
|
2117
|
-
if (!this.focusEnd) {
|
|
2118
|
-
console.warn('no focusEnd found');
|
|
2119
|
-
return;
|
|
2120
|
-
}
|
|
2121
|
-
// if (!$effect.tracking()) {
|
|
2122
|
-
// throw new Error(`_moveFocusEndRow is not in a tracking context.`);
|
|
2123
|
-
// }
|
|
2124
|
-
const focusEnd = this.focusEnd;
|
|
2125
|
-
const prm = this.paginationRowModel;
|
|
2126
|
-
const rows = prm.rows;
|
|
2127
|
-
const endRowIndex = rows.findIndex((row) => row.visibleIndex === focusEnd.row);
|
|
2128
|
-
this.focusEnd = {
|
|
2129
|
-
...this.focusEnd,
|
|
2130
|
-
row: rows[Math.max(Math.min(endRowIndex + offset, rows.length - 1), 0)].visibleIndex,
|
|
2131
|
-
};
|
|
2132
|
-
}
|
|
2133
|
-
_moveFocusRow(offset) {
|
|
2134
|
-
if (!this.focusStart) {
|
|
2135
|
-
console.warn('no focusStart found');
|
|
2136
|
-
return;
|
|
2137
|
-
}
|
|
2138
|
-
const focusStart = this.focusStart;
|
|
2139
|
-
const newEndRowIndex = (() => {
|
|
2140
|
-
const prm = this.paginationRowModel;
|
|
2141
|
-
const rows = prm.rows;
|
|
2142
|
-
const endRowIndex = rows.findIndex((row) => row.visibleIndex === focusStart.row);
|
|
2143
|
-
return rows[Math.max(Math.min(endRowIndex + offset, rows.length - 1), 0)].visibleIndex;
|
|
2144
|
-
})();
|
|
2145
|
-
this.moveFocus({ ...this.focusStart, row: newEndRowIndex });
|
|
1912
|
+
return this._focus.focusEndVisualColumn;
|
|
2146
1913
|
}
|
|
2147
1914
|
async getHighlightedRowData() {
|
|
2148
1915
|
const affectedColumns = this.columnDefs.filter(this.columnDefReturnsTextValue);
|
|
@@ -2289,28 +2056,7 @@ export class TableContext {
|
|
|
2289
2056
|
};
|
|
2290
2057
|
}
|
|
2291
2058
|
updateDefsBasedOnPinning = () => {
|
|
2292
|
-
|
|
2293
|
-
const midDefs = [];
|
|
2294
|
-
const rightDefs = [];
|
|
2295
|
-
this.columnDefs.forEach((def) => {
|
|
2296
|
-
if (this.columnPinning.left?.has(def.id)) {
|
|
2297
|
-
leftDefs.push(def);
|
|
2298
|
-
}
|
|
2299
|
-
else if (this.columnPinning.right?.has(def.id)) {
|
|
2300
|
-
rightDefs.push(def);
|
|
2301
|
-
}
|
|
2302
|
-
else {
|
|
2303
|
-
midDefs.push(def);
|
|
2304
|
-
}
|
|
2305
|
-
});
|
|
2306
|
-
leftDefs.sort((a, b) => a.index - b.index);
|
|
2307
|
-
midDefs.sort((a, b) => a.index - b.index);
|
|
2308
|
-
rightDefs.sort((a, b) => a.index - b.index);
|
|
2309
|
-
const allDefs = leftDefs.concat(midDefs, rightDefs);
|
|
2310
|
-
allDefs.forEach((def, index) => {
|
|
2311
|
-
def.index = index;
|
|
2312
|
-
});
|
|
2313
|
-
return allDefs;
|
|
2059
|
+
return this._columnLayout.updateDefsBasedOnPinning();
|
|
2314
2060
|
};
|
|
2315
2061
|
assertNoDuplicateIds() {
|
|
2316
2062
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
@@ -2322,10 +2068,8 @@ export class TableContext {
|
|
|
2322
2068
|
foundIds.add(def.id);
|
|
2323
2069
|
});
|
|
2324
2070
|
}
|
|
2325
|
-
// FIXME
|
|
2326
2071
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
2327
2072
|
async setupDefaultColumnDefs() {
|
|
2328
|
-
// const helper = createColumnHelper<T>();
|
|
2329
2073
|
this.assertNoDuplicateIds();
|
|
2330
2074
|
const adjustedDefsPromises = this.defaultColumnDefs.defs.map((def) => {
|
|
2331
2075
|
if (def instanceof AccessorDef) {
|
|
@@ -2460,47 +2204,44 @@ export class TableContext {
|
|
|
2460
2204
|
this.columnDefs = adjustedDefs;
|
|
2461
2205
|
return adjustedDefs;
|
|
2462
2206
|
}
|
|
2207
|
+
/**
|
|
2208
|
+
* Reads the persisted per-table user-settings blob through
|
|
2209
|
+
* `_preferences.loadSavedSettings` (Phase 6.4) and applies each
|
|
2210
|
+
* field to the live `$state` slot. Field defaults mirror the
|
|
2211
|
+
* pre-extraction behaviour:
|
|
2212
|
+
*
|
|
2213
|
+
* - missing slot: `singleClickEditing` defaults to `true`; every
|
|
2214
|
+
* other field keeps its constructor default.
|
|
2215
|
+
* - present slot: each field is applied if it would deviate from
|
|
2216
|
+
* the constructor default (the "only alter when the user
|
|
2217
|
+
* explicitly disabled" semantics are preserved verbatim).
|
|
2218
|
+
*/
|
|
2463
2219
|
_getUserDefaults() {
|
|
2464
|
-
const
|
|
2465
|
-
if (
|
|
2466
|
-
const saved = JSON.parse(savedSettings);
|
|
2467
|
-
this.singleClickEditing = saved.singleClickEditing ?? true;
|
|
2468
|
-
if (saved.alternateRowColours) {
|
|
2469
|
-
this.alternateRowColours = true;
|
|
2470
|
-
}
|
|
2471
|
-
if (saved.commaSeparatedCopy) {
|
|
2472
|
-
this.commaSeparatedCopy = true;
|
|
2473
|
-
}
|
|
2474
|
-
// these are true by default so we only want to alter them if the user wants them disabled
|
|
2475
|
-
if (saved.isCompact === false) {
|
|
2476
|
-
this.isCompact = false;
|
|
2477
|
-
}
|
|
2478
|
-
if (saved.copyWithHeaders === false) {
|
|
2479
|
-
this.copyWithHeaders = false;
|
|
2480
|
-
}
|
|
2481
|
-
if (saved.changeColourOnHover === false) {
|
|
2482
|
-
this.changeColourOnHover = false;
|
|
2483
|
-
}
|
|
2484
|
-
}
|
|
2485
|
-
else {
|
|
2220
|
+
const saved = this._preferences.loadSavedSettings();
|
|
2221
|
+
if (saved === null) {
|
|
2486
2222
|
this.singleClickEditing = true;
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
this.singleClickEditing = saved.singleClickEditing ?? true;
|
|
2226
|
+
if (saved.alternateRowColours) {
|
|
2227
|
+
this.alternateRowColours = true;
|
|
2228
|
+
}
|
|
2229
|
+
if (saved.commaSeparatedCopy) {
|
|
2230
|
+
this.commaSeparatedCopy = true;
|
|
2231
|
+
}
|
|
2232
|
+
// these are true by default so we only want to alter them if the user wants them disabled
|
|
2233
|
+
if (saved.isCompact === false) {
|
|
2234
|
+
this.isCompact = false;
|
|
2235
|
+
}
|
|
2236
|
+
if (saved.copyWithHeaders === false) {
|
|
2237
|
+
this.copyWithHeaders = false;
|
|
2238
|
+
}
|
|
2239
|
+
if (saved.changeColourOnHover === false) {
|
|
2240
|
+
this.changeColourOnHover = false;
|
|
2487
2241
|
}
|
|
2488
2242
|
}
|
|
2489
2243
|
_setNewPinningState() {
|
|
2490
|
-
this.
|
|
2491
|
-
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
2492
|
-
left: new Map(this.columnPinning.left &&
|
|
2493
|
-
this.columnDefs
|
|
2494
|
-
.filter((col) => {
|
|
2495
|
-
return this.columnPinning.left?.has(col.id);
|
|
2496
|
-
})
|
|
2497
|
-
.map((col, index) => [col.id, index])),
|
|
2498
|
-
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
2499
|
-
right: new Map(this.columnPinning.right &&
|
|
2500
|
-
this.columnDefs
|
|
2501
|
-
.filter((col) => this.columnPinning.right?.has(col.id))
|
|
2502
|
-
.map((col, index) => [col.id, index])),
|
|
2503
|
-
}; // TODO
|
|
2244
|
+
this._columnLayout.setNewPinningState();
|
|
2504
2245
|
}
|
|
2505
2246
|
getToggleSortingHandler = (def) => () => {
|
|
2506
2247
|
this.paginationRepo?.updateSorting?.((state) => {
|
|
@@ -2524,7 +2265,6 @@ export class TableContext {
|
|
|
2524
2265
|
return newState;
|
|
2525
2266
|
});
|
|
2526
2267
|
};
|
|
2527
|
-
// FIXME
|
|
2528
2268
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
2529
2269
|
async reassignDefsToPresets(presets) {
|
|
2530
2270
|
const adjustedDefs = this.columnDefs;
|
|
@@ -2591,9 +2331,51 @@ export function isSearchable(def) {
|
|
|
2591
2331
|
function hasPagination(repository) {
|
|
2592
2332
|
return 'setPage' in repository;
|
|
2593
2333
|
}
|
|
2334
|
+
/**
|
|
2335
|
+
* Neutralises CSV / TSV formula-injection prefixes recognised by Excel,
|
|
2336
|
+
* LibreOffice Calc, Google Sheets, and Apple Numbers.
|
|
2337
|
+
*
|
|
2338
|
+
* When a clipboard payload reaches a spreadsheet, any cell whose first
|
|
2339
|
+
* character is one of `=`, `+`, `-`, `@`, `\t`, `\r` is treated as a
|
|
2340
|
+
* formula and evaluated in the user's context. Untrusted row data
|
|
2341
|
+
* containing `=cmd|'/c calc'!A1` (or DDE / web-query equivalents) is the
|
|
2342
|
+
* canonical attack vector. Prefixing such a value with a single quote
|
|
2343
|
+
* (`'`) makes the spreadsheet interpret the cell as a literal string
|
|
2344
|
+
* instead.
|
|
2345
|
+
*
|
|
2346
|
+
* Trade-off: the leading apostrophe is invisible in the rendered cell of
|
|
2347
|
+
* every major spreadsheet, but the underlying cell value differs from the
|
|
2348
|
+
* source when read back by another formula referencing the cell. This is
|
|
2349
|
+
* the accepted industry mitigation (OWASP CSV Injection cheat sheet).
|
|
2350
|
+
*
|
|
2351
|
+
* Private helper - keep this colocated with `encapsulate` so every
|
|
2352
|
+
* clipboard-bound string flows through both functions before being
|
|
2353
|
+
* written to the OS clipboard.
|
|
2354
|
+
*/
|
|
2355
|
+
function escapeForSpreadsheet(value) {
|
|
2356
|
+
if (value.length === 0) {
|
|
2357
|
+
return value;
|
|
2358
|
+
}
|
|
2359
|
+
const first = value.charCodeAt(0);
|
|
2360
|
+
// '=' 61, '+' 43, '-' 45, '@' 64, '\t' 9, '\r' 13
|
|
2361
|
+
if (first === 61 || first === 43 || first === 45 || first === 64 || first === 9 || first === 13) {
|
|
2362
|
+
return `'${value}`;
|
|
2363
|
+
}
|
|
2364
|
+
return value;
|
|
2365
|
+
}
|
|
2594
2366
|
function encapsulate(value) {
|
|
2595
|
-
|
|
2367
|
+
const safe = escapeForSpreadsheet(value);
|
|
2368
|
+
return `"${safe.replace(/[\n]/gm, ' ').replace(/["]/g, '""')}"`;
|
|
2596
2369
|
}
|
|
2370
|
+
/**
|
|
2371
|
+
* Internal exports for tests. Not part of the public API; do not import
|
|
2372
|
+
* from consumer code. Allows the clipboard formula-escape regression test
|
|
2373
|
+
* to call the module-private helpers directly.
|
|
2374
|
+
*/
|
|
2375
|
+
export const _internal = {
|
|
2376
|
+
escapeForSpreadsheet,
|
|
2377
|
+
encapsulate,
|
|
2378
|
+
};
|
|
2597
2379
|
function generateTableOptions(contextObj) {
|
|
2598
2380
|
const _pagination = $derived.by(() => {
|
|
2599
2381
|
void contextObj.paginationRepo?.paginationState.pageSize;
|
|
@@ -2616,6 +2398,22 @@ function generateTableOptions(contextObj) {
|
|
|
2616
2398
|
let originalWidth = 0;
|
|
2617
2399
|
let mouseStartingPoint = 0;
|
|
2618
2400
|
function addOverrideResizeHandler(e) {
|
|
2401
|
+
// Capture the column's currently rendered width as the
|
|
2402
|
+
// drag's starting point, then mark this column as
|
|
2403
|
+
// user-resized. Going forward the grid template will use a
|
|
2404
|
+
// fixed `${width}px` track for this column (see
|
|
2405
|
+
// `Table.svelte` colsTemplate) so dragging the handle only
|
|
2406
|
+
// changes this column. Other columns that have not yet
|
|
2407
|
+
// been user-resized continue to flex via `minmax(W, 1fr)`,
|
|
2408
|
+
// which means they automatically take up any horizontal
|
|
2409
|
+
// space the resized column gives back when shrunk - the
|
|
2410
|
+
// table never ends up narrower than its container while
|
|
2411
|
+
// any column still flexes.
|
|
2412
|
+
const rendered = contextObj._columnRenderedWidths.get(def.id);
|
|
2413
|
+
if (rendered != null && rendered > 0) {
|
|
2414
|
+
def.width = rendered;
|
|
2415
|
+
}
|
|
2416
|
+
def.userResized = true;
|
|
2619
2417
|
originalWidth = def.width;
|
|
2620
2418
|
mouseStartingPoint = e.clientX;
|
|
2621
2419
|
def.isResizing = true;
|
|
@@ -2623,7 +2421,14 @@ function generateTableOptions(contextObj) {
|
|
|
2623
2421
|
window.addEventListener('mouseup', removeOverrideResizeHandler);
|
|
2624
2422
|
}
|
|
2625
2423
|
function onResize(e) {
|
|
2626
|
-
|
|
2424
|
+
// Clamp the resize floor so the column can never reach a
|
|
2425
|
+
// negative or zero width. With the div+grid layout a
|
|
2426
|
+
// non-positive `width` produces an invalid track size
|
|
2427
|
+
// in `grid-template-columns`, which makes the browser
|
|
2428
|
+
// fall back to single-column block layout and visually
|
|
2429
|
+
// collapses the row.
|
|
2430
|
+
const next = originalWidth - (mouseStartingPoint - e.clientX);
|
|
2431
|
+
def.width = Math.max(next, 20);
|
|
2627
2432
|
}
|
|
2628
2433
|
function removeOverrideResizeHandler() {
|
|
2629
2434
|
def.isResizing = false;
|
|
@@ -2753,111 +2558,6 @@ function generateTableOptions(contextObj) {
|
|
|
2753
2558
|
// }
|
|
2754
2559
|
};
|
|
2755
2560
|
}
|
|
2756
|
-
function checkboxSortingFn(id, desc) {
|
|
2757
|
-
return desc
|
|
2758
|
-
? (a, b) => {
|
|
2759
|
-
if (a[id] && !b[id]) {
|
|
2760
|
-
return 1;
|
|
2761
|
-
}
|
|
2762
|
-
if (!a[id] && b[id]) {
|
|
2763
|
-
return -1;
|
|
2764
|
-
}
|
|
2765
|
-
return 0;
|
|
2766
|
-
}
|
|
2767
|
-
: (a, b) => {
|
|
2768
|
-
if (a[id] && !b[id]) {
|
|
2769
|
-
return -1;
|
|
2770
|
-
}
|
|
2771
|
-
if (!a[id] && b[id]) {
|
|
2772
|
-
return 1;
|
|
2773
|
-
}
|
|
2774
|
-
return 0;
|
|
2775
|
-
};
|
|
2776
|
-
}
|
|
2777
|
-
function comparableSortingFn(id, desc) {
|
|
2778
|
-
return desc
|
|
2779
|
-
? (a, b) => {
|
|
2780
|
-
if (a[id] == undefined && b[id] != undefined) {
|
|
2781
|
-
return 1;
|
|
2782
|
-
}
|
|
2783
|
-
if (b[id] == undefined && a[id] != undefined) {
|
|
2784
|
-
return -1;
|
|
2785
|
-
}
|
|
2786
|
-
if (a[id] < b[id]) {
|
|
2787
|
-
return 1;
|
|
2788
|
-
}
|
|
2789
|
-
if (a[id] > b[id]) {
|
|
2790
|
-
return -1;
|
|
2791
|
-
}
|
|
2792
|
-
return 0;
|
|
2793
|
-
}
|
|
2794
|
-
: (a, b) => {
|
|
2795
|
-
if (a[id] == undefined && b[id] != undefined) {
|
|
2796
|
-
return 1;
|
|
2797
|
-
}
|
|
2798
|
-
if (b[id] == undefined && a[id] != undefined) {
|
|
2799
|
-
return -1;
|
|
2800
|
-
}
|
|
2801
|
-
if (a[id] < b[id]) {
|
|
2802
|
-
return -1;
|
|
2803
|
-
}
|
|
2804
|
-
if (a[id] > b[id]) {
|
|
2805
|
-
return 1;
|
|
2806
|
-
}
|
|
2807
|
-
return 0;
|
|
2808
|
-
};
|
|
2809
|
-
}
|
|
2810
|
-
function progressSortingFn(id, desc) {
|
|
2811
|
-
return desc
|
|
2812
|
-
? (a, b) => {
|
|
2813
|
-
const [minA, maxA] = a[id];
|
|
2814
|
-
const [minB, maxB] = b[id];
|
|
2815
|
-
if (minA == undefined || maxA == undefined || maxA == 0) {
|
|
2816
|
-
if (maxB != undefined && maxB != 0) {
|
|
2817
|
-
return 1;
|
|
2818
|
-
}
|
|
2819
|
-
else {
|
|
2820
|
-
return 0;
|
|
2821
|
-
}
|
|
2822
|
-
}
|
|
2823
|
-
if (minB == undefined || maxB == undefined || maxB == 0) {
|
|
2824
|
-
return -1;
|
|
2825
|
-
}
|
|
2826
|
-
const valueA = minA / maxA;
|
|
2827
|
-
const valueB = minB / maxB;
|
|
2828
|
-
if (valueA < valueB) {
|
|
2829
|
-
return 1;
|
|
2830
|
-
}
|
|
2831
|
-
if (valueB < valueA) {
|
|
2832
|
-
return -1;
|
|
2833
|
-
}
|
|
2834
|
-
return 0;
|
|
2835
|
-
}
|
|
2836
|
-
: (a, b) => {
|
|
2837
|
-
const [minA, maxA] = a[id];
|
|
2838
|
-
const [minB, maxB] = b[id];
|
|
2839
|
-
if (minA == undefined || maxA == undefined || maxA == 0) {
|
|
2840
|
-
if (maxB != undefined && maxB != 0) {
|
|
2841
|
-
return -1;
|
|
2842
|
-
}
|
|
2843
|
-
else {
|
|
2844
|
-
return 0;
|
|
2845
|
-
}
|
|
2846
|
-
}
|
|
2847
|
-
if (minB == undefined || maxB == undefined || maxB == 0) {
|
|
2848
|
-
return 1;
|
|
2849
|
-
}
|
|
2850
|
-
const valueA = minA / maxA;
|
|
2851
|
-
const valueB = minB / maxB;
|
|
2852
|
-
if (valueA < valueB) {
|
|
2853
|
-
return -1;
|
|
2854
|
-
}
|
|
2855
|
-
if (valueB < valueA) {
|
|
2856
|
-
return 1;
|
|
2857
|
-
}
|
|
2858
|
-
return 0;
|
|
2859
|
-
};
|
|
2860
|
-
}
|
|
2861
2561
|
function adaptJSONPinningState(state) {
|
|
2862
2562
|
//HACK ignore previously broken pinning states
|
|
2863
2563
|
return {
|