@lavalogic/scoria 0.40.13 → 0.40.15
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 +35 -46
- package/dist/Components/AccordionGroupButton.svelte +68 -76
- package/dist/Components/Action.svelte +103 -135
- package/dist/Components/AlertModal.svelte +171 -190
- package/dist/Components/AppointmentCalendar/AppointmentCalendar.svelte +614 -697
- package/dist/Components/Base/BaseModal.svelte +90 -116
- package/dist/Components/Base/ContextWrapper.svelte +67 -72
- package/dist/Components/Base/Pagination.svelte +173 -196
- package/dist/Components/Base/Snippets.svelte +34 -36
- package/dist/Components/BigRadioSet.svelte +36 -38
- package/dist/Components/Bubble.svelte +78 -101
- package/dist/Components/Button.svelte +175 -208
- package/dist/Components/Checkbox.svelte +75 -86
- package/dist/Components/CollapsibleCard.svelte +72 -87
- package/dist/Components/CollapsibleMenuGroup.svelte +53 -57
- package/dist/Components/ConditionBuilder/ConditionBuilder.svelte +39 -64
- package/dist/Components/Contexts/ContextMenu.svelte +141 -156
- package/dist/Components/Contexts/ContextMenuDivider.svelte +15 -15
- package/dist/Components/Contexts/ContextMenuOption.svelte +53 -60
- package/dist/Components/Contexts/Toast.svelte +81 -86
- package/dist/Components/Contexts/ToastContainer.svelte +58 -62
- package/dist/Components/Contexts/Tooltip.svelte +36 -37
- package/dist/Components/DataMatrixIcon.svelte +95 -108
- package/dist/Components/DateInput.svelte +118 -146
- package/dist/Components/DatePicker.svelte +255 -294
- package/dist/Components/DesktopModal.svelte +70 -90
- package/dist/Components/Dial.svelte +167 -197
- package/dist/Components/DimensionInput.svelte +124 -169
- package/dist/Components/DragMenu/DragMenuHolder.svelte +98 -114
- package/dist/Components/DragMenu/DraggableCard.svelte +64 -76
- package/dist/Components/FileCard.svelte +21 -24
- package/dist/Components/FileUpload.svelte +190 -225
- package/dist/Components/GridInput.svelte +159 -196
- package/dist/Components/GridInputSet.svelte +24 -29
- package/dist/Components/HorizontalTabGroup.svelte +73 -89
- package/dist/Components/HorizontalTabGroupButton.svelte +40 -44
- package/dist/Components/Icon.svelte +99 -128
- package/dist/Components/Icons.js +357 -357
- package/dist/Components/InfoAlert.svelte +35 -38
- package/dist/Components/LoadingAnimation.svelte +691 -755
- package/dist/Components/LoadingModal.svelte +36 -44
- package/dist/Components/LoadingOverlay.svelte +22 -26
- package/dist/Components/Modal.svelte +204 -254
- package/dist/Components/MultiSelect.svelte +186 -216
- package/dist/Components/Nav/Nav.svelte +94 -110
- package/dist/Components/Nav/NavServiceMenuList.svelte +57 -61
- package/dist/Components/Nav/NavTab.svelte +58 -62
- package/dist/Components/Nav/ServiceMenuItem.svelte +103 -111
- package/dist/Components/Nav/ServicesNav.svelte +72 -79
- package/dist/Components/NumberInput.svelte +121 -155
- package/dist/Components/OptionCards.svelte +62 -73
- package/dist/Components/PageHeading.svelte +23 -25
- package/dist/Components/PasswordInput.svelte +69 -88
- package/dist/Components/PopoverButton.svelte +151 -0
- package/dist/Components/PopoverButton.svelte.d.ts +9 -0
- package/dist/Components/PopoverButtonProps.d.ts +21 -0
- package/dist/Components/PopoverButtonProps.js +1 -0
- package/dist/Components/Portal.svelte +29 -35
- package/dist/Components/ProgressBubble.svelte +91 -106
- package/dist/Components/QuerySelect.svelte +260 -346
- package/dist/Components/SidebarPanel.svelte +34 -36
- package/dist/Components/SingleSelect.svelte +196 -244
- package/dist/Components/StepButton.svelte +65 -70
- package/dist/Components/StepForm.svelte +115 -135
- package/dist/Components/Switch.svelte +57 -69
- package/dist/Components/Table/ARCHITECTURE.md +298 -298
- package/dist/Components/Table/Body/QuickSearchCell.svelte +578 -655
- package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +42 -51
- package/dist/Components/Table/Body/Rows/ColumnList.svelte +42 -58
- package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +403 -442
- package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +51 -78
- package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +165 -184
- package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +110 -123
- package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +134 -160
- package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +190 -231
- package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +82 -104
- package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +52 -87
- package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +122 -150
- package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +132 -159
- package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +190 -236
- package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +173 -211
- package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +204 -244
- package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +140 -177
- package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +188 -217
- package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +173 -193
- package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +166 -187
- package/dist/Components/Table/Body/Rows/TableRow.svelte +245 -288
- package/dist/Components/Table/Body/TableBody.svelte +141 -154
- package/dist/Components/Table/Headers/HighlightAllHeader.svelte +35 -41
- package/dist/Components/Table/Headers/SelectAllHeader.svelte +24 -30
- package/dist/Components/Table/Headers/TableHead.svelte +348 -402
- package/dist/Components/Table/Misc/ColumnPanel.svelte +37 -39
- package/dist/Components/Table/Misc/ColumnPanelModal.svelte +35 -39
- package/dist/Components/Table/Misc/FilterInput.svelte +556 -619
- package/dist/Components/Table/Misc/FilterPanel.svelte +170 -183
- package/dist/Components/Table/Misc/FilterPanelModal.svelte +25 -31
- package/dist/Components/Table/Misc/ShareViewModal.svelte +252 -281
- package/dist/Components/Table/Misc/SidePanel.svelte +32 -35
- package/dist/Components/Table/Misc/TableConfigurationModal.svelte +574 -629
- package/dist/Components/Table/Misc/TableFooter.svelte +326 -332
- package/dist/Components/Table/Misc/TableHorizontalBar.svelte +134 -140
- package/dist/Components/Table/Misc/TableSidebar.svelte +183 -198
- package/dist/Components/Table/Misc/TableViewDropdown.svelte +83 -103
- package/dist/Components/Table/Misc/ToolboxPanel.svelte +83 -100
- package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +88 -107
- package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte +163 -190
- package/dist/Components/Table/NestedTable.svelte +102 -128
- package/dist/Components/Table/Table.svelte +531 -663
- package/dist/Components/Test/TestContextConsumer.svelte +11 -13
- package/dist/Components/TextArea.svelte +95 -130
- package/dist/Components/TextInput.svelte +125 -176
- package/dist/Components/ThreeStateRadio.svelte +111 -130
- package/dist/Components/Touch/BoolCard.svelte +33 -45
- package/dist/Components/Touch/DateModal.svelte +164 -204
- package/dist/Components/Touch/EditCard.svelte +48 -59
- package/dist/Components/Touch/InfoCard.svelte +28 -31
- package/dist/Components/Touch/InfoTextCard.svelte +26 -28
- package/dist/Components/Touch/ModalExplanation.svelte +15 -17
- package/dist/Components/Touch/NumberKeyboard.svelte +181 -218
- package/dist/Components/Touch/NumberModal.svelte +59 -79
- package/dist/Components/Touch/PlusMinusCard.svelte +43 -46
- package/dist/Components/Touch/SelectModal.svelte +111 -126
- package/dist/Components/Touch/SummaryCard.svelte +80 -93
- package/dist/Components/Touch/TextAreaModal.svelte +72 -94
- package/dist/Components/Touch/TextModal.svelte +77 -105
- package/dist/Components/Touch/TouchCard.svelte +31 -33
- package/dist/Components/Touch/TouchModal.svelte +214 -270
- package/dist/Components/Touch/UtilityButton.svelte +88 -109
- package/dist/Components/VerticalTabGroup.svelte +60 -78
- package/dist/Components/VerticalTabGroupButton.svelte +54 -58
- package/dist/Helpers/Datamatrix.d.ts +19 -19
- package/dist/Helpers/Datamatrix.js +291 -291
- package/dist/Types/Examples/ExampleModal.svelte +31 -35
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/scss/_basics.scss +12 -12
- package/dist/scss/_colours.scss +134 -134
- package/dist/scss/_mixins.scss +3392 -3392
- package/dist/scss/_motion.scss +57 -57
- package/dist/scss/_sizing.scss +85 -85
- package/dist/scss/_transitions.scss +8 -8
- package/package.json +1 -1
|
@@ -1,456 +1,417 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script
|
|
4
|
-
lang="ts"
|
|
5
|
-
generics="T extends object, IdType extends Primitive"
|
|
6
|
-
>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
columnDef == null ? -1 : sortedColumns.findIndex((it) => it.id === columnDef.id)
|
|
29
|
-
);
|
|
30
|
-
const isAtTop = $derived(currentIndex <= 0);
|
|
31
|
-
const isAtBottom = $derived(currentIndex === -1 || currentIndex >= sortedColumns.length - 1);
|
|
32
|
-
|
|
33
|
-
function moveUp(): void {
|
|
34
|
-
if (columnDef == null || isAtTop) {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
preserveListScroll(() => {
|
|
38
|
-
tableContext.moveColumnTo(currentIndex, currentIndex - 1);
|
|
39
|
-
});
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script
|
|
4
|
+
lang="ts"
|
|
5
|
+
generics="T extends object, IdType extends Primitive"
|
|
6
|
+
>import Action from "../../../Action.svelte";
|
|
7
|
+
import Checkbox from "../../../Checkbox.svelte";
|
|
8
|
+
import ThreeStateRadio from "../../../ThreeStateRadio.svelte";
|
|
9
|
+
import { TableContext } from "../../Types/Context/TableContext.svelte.js";
|
|
10
|
+
import { Size } from "../../../../Types/Internal/Size.js";
|
|
11
|
+
import { Ternary } from "../../../../Types/Internal/Ternary.js";
|
|
12
|
+
import { getContext } from "svelte";
|
|
13
|
+
const tableContext = getContext(TableContext.identifier);
|
|
14
|
+
const { columnDef } = $props();
|
|
15
|
+
/**
|
|
16
|
+
* Position of this column inside the sorted column list. Drives the
|
|
17
|
+
* disabled state of the "move up" / "move down" buttons and supplies
|
|
18
|
+
* the target index to `moveColumnTo`. Sort by `index` (the stable
|
|
19
|
+
* layout identity) rather than relying on `columnDefs` iteration order.
|
|
20
|
+
*/
|
|
21
|
+
const sortedColumns = $derived(tableContext.columnDefs.slice().sort((a, b) => a.index - b.index));
|
|
22
|
+
const currentIndex = $derived(columnDef == null ? -1 : sortedColumns.findIndex((it) => it.id === columnDef.id));
|
|
23
|
+
const isAtTop = $derived(currentIndex <= 0);
|
|
24
|
+
const isAtBottom = $derived(currentIndex === -1 || currentIndex >= sortedColumns.length - 1);
|
|
25
|
+
function moveUp() {
|
|
26
|
+
if (columnDef == null || isAtTop) {
|
|
27
|
+
return;
|
|
40
28
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
});
|
|
29
|
+
preserveListScroll(() => {
|
|
30
|
+
tableContext.moveColumnTo(currentIndex, currentIndex - 1);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function moveDown() {
|
|
34
|
+
if (columnDef == null || isAtBottom) {
|
|
35
|
+
return;
|
|
49
36
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
37
|
+
preserveListScroll(() => {
|
|
38
|
+
tableContext.moveColumnTo(currentIndex, currentIndex + 1);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
let isBeingTargeted = $state(false);
|
|
42
|
+
/** Bound `<li>` element used by `preserveListScroll` to find the
|
|
43
|
+
* enclosing `.column-list` even when the event target is the
|
|
44
|
+
* visibility checkbox's hidden `<input>` (which is
|
|
45
|
+
* `position: absolute` with no positioned ancestor and therefore
|
|
46
|
+
* drifts away from the row in the layout tree). */
|
|
47
|
+
let rowEl = $state();
|
|
48
|
+
function disableDragVisual() {
|
|
49
|
+
isBeingTargeted = false;
|
|
50
|
+
}
|
|
51
|
+
function makeKVPairs(item, index) {
|
|
52
|
+
return [item, index];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Pre-click scroll snapshot.
|
|
56
|
+
*
|
|
57
|
+
* When the user clicks a checkbox label, the browser fires a synthetic
|
|
58
|
+
* click on the hidden `<input>` BEFORE `onchange` fires. That synthetic
|
|
59
|
+
* click focuses the input, and the focus side effect scrolls the
|
|
60
|
+
* column-list (and sometimes the window) to bring the 1x1px,
|
|
61
|
+
* `clip-path`-ed input's bounding box into view. Reading `scrollTop`
|
|
62
|
+
* inside `onchange` therefore captures the post-jump position and
|
|
63
|
+
* "restoring" to it freezes the list at the wrong spot.
|
|
64
|
+
*
|
|
65
|
+
* Snapshotting on `mousedown` (which fires before the focus step)
|
|
66
|
+
* captures the genuine pre-click position. The snapshot is re-applied
|
|
67
|
+
* at multiple time points after the mutation so any later focus /
|
|
68
|
+
* layout shift gets undone.
|
|
69
|
+
*/
|
|
70
|
+
let preClickListScroll = 0;
|
|
71
|
+
let preClickWindowScrollX = 0;
|
|
72
|
+
let preClickWindowScrollY = 0;
|
|
73
|
+
/** Milliseconds `Date.now()` stamp of the last `snapshotScrollPosition`
|
|
74
|
+
* call. Lets `preserveListScroll` tell apart "click triggered toggle
|
|
75
|
+
* whose mousedown just snapshotted real pre-click state" from
|
|
76
|
+
* "keyboard or other path where the mousedown snapshot is stale". */
|
|
77
|
+
let lastSnapshotAt = 0;
|
|
78
|
+
/** Stale snapshots more than this many ms old are ignored. Tuned
|
|
79
|
+
* loose enough to survive any focus / click latency the browser
|
|
80
|
+
* might insert, but tight enough that an old click from minutes
|
|
81
|
+
* ago can't poison a later keyboard toggle. */
|
|
82
|
+
const SNAPSHOT_FRESHNESS_MS = 500;
|
|
83
|
+
function snapshotScrollPosition() {
|
|
84
|
+
const list = rowEl?.closest(".column-list");
|
|
85
|
+
preClickListScroll = list?.scrollTop ?? 0;
|
|
86
|
+
preClickWindowScrollX = typeof window !== "undefined" ? window.scrollX : 0;
|
|
87
|
+
preClickWindowScrollY = typeof window !== "undefined" ? window.scrollY : 0;
|
|
88
|
+
lastSnapshotAt = Date.now();
|
|
89
|
+
}
|
|
90
|
+
function restoreScrollPosition() {
|
|
91
|
+
const list = rowEl?.closest(".column-list");
|
|
92
|
+
if (list != null) {
|
|
93
|
+
list.scrollTop = preClickListScroll;
|
|
61
94
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return [item, index];
|
|
95
|
+
if (typeof window !== "undefined") {
|
|
96
|
+
window.scrollTo(preClickWindowScrollX, preClickWindowScrollY);
|
|
65
97
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
let lastSnapshotAt = 0;
|
|
91
|
-
|
|
92
|
-
/** Stale snapshots more than this many ms old are ignored. Tuned
|
|
93
|
-
* loose enough to survive any focus / click latency the browser
|
|
94
|
-
* might insert, but tight enough that an old click from minutes
|
|
95
|
-
* ago can't poison a later keyboard toggle. */
|
|
96
|
-
const SNAPSHOT_FRESHNESS_MS = 500;
|
|
97
|
-
|
|
98
|
-
function snapshotScrollPosition(): void {
|
|
99
|
-
const list = rowEl?.closest('.column-list') as HTMLElement | null;
|
|
100
|
-
preClickListScroll = list?.scrollTop ?? 0;
|
|
101
|
-
preClickWindowScrollX = typeof window !== 'undefined' ? window.scrollX : 0;
|
|
102
|
-
preClickWindowScrollY = typeof window !== 'undefined' ? window.scrollY : 0;
|
|
103
|
-
lastSnapshotAt = Date.now();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Run `mutate` and then re-apply the pre-click scroll snapshot
|
|
101
|
+
* aggressively across the next ~400ms so any deferred focus / layout
|
|
102
|
+
* work that fights us at the tail of the event loop still ends up at
|
|
103
|
+
* the user's original scroll position.
|
|
104
|
+
*
|
|
105
|
+
* The snapshot is taken in capture phase from the row's `{@attach}`
|
|
106
|
+
* listener below, before the focus side effect can scroll. If invoked
|
|
107
|
+
* without a recent mousedown (keyboard toggle, programmatic call) a
|
|
108
|
+
* fresh snapshot is taken here.
|
|
109
|
+
*
|
|
110
|
+
* A temporary scroll listener on list + window forcibly reverts any
|
|
111
|
+
* scroll event during the lock window, complementing the discrete
|
|
112
|
+
* `rAF` / `setTimeout` restores. The listener no-ops when already at
|
|
113
|
+
* the snapshot, so the restore call inside it does not re-trigger.
|
|
114
|
+
*
|
|
115
|
+
* The active hidden input is blurred immediately after the mutation
|
|
116
|
+
* so the browser stops treating it as the "currently focused element
|
|
117
|
+
* to keep visible" on subsequent layout passes.
|
|
118
|
+
*/
|
|
119
|
+
function preserveListScroll(mutate) {
|
|
120
|
+
if (Date.now() - lastSnapshotAt > SNAPSHOT_FRESHNESS_MS) {
|
|
121
|
+
snapshotScrollPosition();
|
|
104
122
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
if (typeof window !== 'undefined') {
|
|
112
|
-
window.scrollTo(preClickWindowScrollX, preClickWindowScrollY);
|
|
113
|
-
}
|
|
123
|
+
const focused = typeof document !== "undefined" && document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
124
|
+
const list = rowEl?.closest(".column-list");
|
|
125
|
+
mutate();
|
|
126
|
+
if (focused != null && rowEl?.contains(focused) === true) {
|
|
127
|
+
focused.blur();
|
|
114
128
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
* Run `mutate` and then re-apply the pre-click scroll snapshot
|
|
118
|
-
* aggressively across the next ~400ms so any deferred focus / layout
|
|
119
|
-
* work that fights us at the tail of the event loop still ends up at
|
|
120
|
-
* the user's original scroll position.
|
|
121
|
-
*
|
|
122
|
-
* The snapshot is taken in capture phase from the row's `{@attach}`
|
|
123
|
-
* listener below, before the focus side effect can scroll. If invoked
|
|
124
|
-
* without a recent mousedown (keyboard toggle, programmatic call) a
|
|
125
|
-
* fresh snapshot is taken here.
|
|
126
|
-
*
|
|
127
|
-
* A temporary scroll listener on list + window forcibly reverts any
|
|
128
|
-
* scroll event during the lock window, complementing the discrete
|
|
129
|
-
* `rAF` / `setTimeout` restores. The listener no-ops when already at
|
|
130
|
-
* the snapshot, so the restore call inside it does not re-trigger.
|
|
131
|
-
*
|
|
132
|
-
* The active hidden input is blurred immediately after the mutation
|
|
133
|
-
* so the browser stops treating it as the "currently focused element
|
|
134
|
-
* to keep visible" on subsequent layout passes.
|
|
135
|
-
*/
|
|
136
|
-
function preserveListScroll(mutate: () => void): void {
|
|
137
|
-
if (Date.now() - lastSnapshotAt > SNAPSHOT_FRESHNESS_MS) {
|
|
138
|
-
snapshotScrollPosition();
|
|
139
|
-
}
|
|
140
|
-
const focused =
|
|
141
|
-
typeof document !== 'undefined' && document.activeElement instanceof HTMLElement
|
|
142
|
-
? document.activeElement
|
|
143
|
-
: null;
|
|
144
|
-
const list = rowEl?.closest('.column-list') as HTMLElement | null;
|
|
145
|
-
mutate();
|
|
146
|
-
if (focused != null && rowEl?.contains(focused) === true) {
|
|
147
|
-
focused.blur();
|
|
148
|
-
}
|
|
149
|
-
if (typeof requestAnimationFrame !== 'undefined') {
|
|
150
|
-
requestAnimationFrame(restoreScrollPosition);
|
|
151
|
-
}
|
|
152
|
-
setTimeout(restoreScrollPosition, 0);
|
|
153
|
-
setTimeout(restoreScrollPosition, 60);
|
|
154
|
-
setTimeout(restoreScrollPosition, 120);
|
|
155
|
-
setTimeout(restoreScrollPosition, 240);
|
|
156
|
-
|
|
157
|
-
// Scroll-lock: forcibly revert any rogue scroll for the next ~400ms.
|
|
158
|
-
// Anything in this window that scrolls the list or page (focus
|
|
159
|
-
// auto-scroll, scrollIntoView from a deferred reactive effect,
|
|
160
|
-
// etc.) is snapped back to the pre-click snapshot. The handler
|
|
161
|
-
// no-ops when already at the snapshot, so the restore inside it
|
|
162
|
-
// does not re-trigger.
|
|
163
|
-
if (list != null && typeof window !== 'undefined') {
|
|
164
|
-
const lockListHandler = (): void => {
|
|
165
|
-
if (list.scrollTop !== preClickListScroll) {
|
|
166
|
-
list.scrollTop = preClickListScroll;
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
const lockWindowHandler = (): void => {
|
|
170
|
-
if (window.scrollX !== preClickWindowScrollX || window.scrollY !== preClickWindowScrollY) {
|
|
171
|
-
window.scrollTo(preClickWindowScrollX, preClickWindowScrollY);
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
list.addEventListener('scroll', lockListHandler, { passive: true });
|
|
175
|
-
window.addEventListener('scroll', lockWindowHandler, { passive: true });
|
|
176
|
-
setTimeout(() => {
|
|
177
|
-
list.removeEventListener('scroll', lockListHandler);
|
|
178
|
-
window.removeEventListener('scroll', lockWindowHandler);
|
|
179
|
-
}, 400);
|
|
180
|
-
}
|
|
129
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
130
|
+
requestAnimationFrame(restoreScrollPosition);
|
|
181
131
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
132
|
+
setTimeout(restoreScrollPosition, 0);
|
|
133
|
+
setTimeout(restoreScrollPosition, 60);
|
|
134
|
+
setTimeout(restoreScrollPosition, 120);
|
|
135
|
+
setTimeout(restoreScrollPosition, 240);
|
|
136
|
+
// Scroll-lock: forcibly revert any rogue scroll for the next ~400ms.
|
|
137
|
+
// Anything in this window that scrolls the list or page (focus
|
|
138
|
+
// auto-scroll, scrollIntoView from a deferred reactive effect,
|
|
139
|
+
// etc.) is snapped back to the pre-click snapshot. The handler
|
|
140
|
+
// no-ops when already at the snapshot, so the restore inside it
|
|
141
|
+
// does not re-trigger.
|
|
142
|
+
if (list != null && typeof window !== "undefined") {
|
|
143
|
+
const lockListHandler = () => {
|
|
144
|
+
if (list.scrollTop !== preClickListScroll) {
|
|
145
|
+
list.scrollTop = preClickListScroll;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
const lockWindowHandler = () => {
|
|
149
|
+
if (window.scrollX !== preClickWindowScrollX || window.scrollY !== preClickWindowScrollY) {
|
|
150
|
+
window.scrollTo(preClickWindowScrollX, preClickWindowScrollY);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
list.addEventListener("scroll", lockListHandler, { passive: true });
|
|
154
|
+
window.addEventListener("scroll", lockWindowHandler, { passive: true });
|
|
155
|
+
setTimeout(() => {
|
|
156
|
+
list.removeEventListener("scroll", lockListHandler);
|
|
157
|
+
window.removeEventListener("scroll", lockWindowHandler);
|
|
158
|
+
}, 400);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Current pin state of this column expressed as a `Ternary` so the
|
|
163
|
+
* three states (`pinned-left` / `unpinned` / `pinned-right`) map
|
|
164
|
+
* cleanly onto `ThreeStateRadio`:
|
|
165
|
+
*
|
|
166
|
+
* - `Ternary.False` → pinned to the LEFT side (leftmost segment)
|
|
167
|
+
* - `Ternary.Neutral` → unpinned (middle segment)
|
|
168
|
+
* - `Ternary.True` → pinned to the RIGHT side (rightmost segment)
|
|
169
|
+
*/
|
|
170
|
+
const pinSide = $derived(columnDef == null ? Ternary.Neutral : tableContext.columnPinning.left?.has(columnDef.id) ? Ternary.False : tableContext.columnPinning.right?.has(columnDef.id) ? Ternary.True : Ternary.Neutral);
|
|
171
|
+
/**
|
|
172
|
+
* Set this column's pin state from a `Ternary`. The previous side (if
|
|
173
|
+
* any) is removed first so the column is never pinned to both sides.
|
|
174
|
+
* Wrapped in `preserveListScroll` so the column-list scroll position
|
|
175
|
+
* survives any focus side effect inside the ThreeStateRadio's hidden
|
|
176
|
+
* inputs.
|
|
177
|
+
*/
|
|
178
|
+
function setPin(next) {
|
|
179
|
+
if (columnDef == null) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
preserveListScroll(() => {
|
|
183
|
+
tableContext.setColumnPinning((pinning) => {
|
|
184
|
+
const leftKeys = [...pinning.left?.keys() ?? []].filter((id) => id !== columnDef.id);
|
|
185
|
+
const rightKeys = [...pinning.right?.keys() ?? []].filter((id) => id !== columnDef.id);
|
|
186
|
+
if (next === Ternary.False) {
|
|
187
|
+
leftKeys.push(columnDef.id);
|
|
188
|
+
} else if (next === Ternary.True) {
|
|
189
|
+
rightKeys.push(columnDef.id);
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
...pinning,
|
|
193
|
+
left: new Map(leftKeys.map(makeKVPairs)),
|
|
194
|
+
right: new Map(rightKeys.map(makeKVPairs))
|
|
195
|
+
};
|
|
228
196
|
});
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
const onVisibilityChange = (e) => {
|
|
200
|
+
if (!columnDef) {
|
|
201
|
+
console.warn("no def found", e);
|
|
202
|
+
return;
|
|
229
203
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
const checked = (e.target as HTMLInputElement).checked;
|
|
238
|
-
preserveListScroll(() => {
|
|
239
|
-
tableContext.updateVisibility((defs) => {
|
|
240
|
-
defs[columnDef.id] = checked;
|
|
241
|
-
return defs;
|
|
242
|
-
});
|
|
204
|
+
const checked = e.target.checked;
|
|
205
|
+
preserveListScroll(() => {
|
|
206
|
+
tableContext.updateVisibility((defs) => {
|
|
207
|
+
defs[columnDef.id] = checked;
|
|
208
|
+
return defs;
|
|
243
209
|
});
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (targetIndex === currentIndex) {
|
|
286
|
-
e.preventDefault();
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* Keyboard reorder support. Alt+ArrowUp/Down nudges the column,
|
|
214
|
+
* Alt+Home/End jumps to the ends. Bound on the row so any focusable
|
|
215
|
+
* child (eye, pin selector, move buttons) can trigger it.
|
|
216
|
+
*
|
|
217
|
+
* `Alt` is required so plain arrow keys still navigate radio groups
|
|
218
|
+
* (ThreeStateRadio) and the row's own scroll container.
|
|
219
|
+
*/
|
|
220
|
+
function onRowKeydown(e) {
|
|
221
|
+
if (!columnDef || !tableContext.allowColumnReordering) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (!e.altKey) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (currentIndex === -1) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const lastIndex = sortedColumns.length - 1;
|
|
231
|
+
let targetIndex;
|
|
232
|
+
switch (e.key) {
|
|
233
|
+
case "ArrowUp":
|
|
234
|
+
targetIndex = Math.max(0, currentIndex - 1);
|
|
235
|
+
break;
|
|
236
|
+
case "ArrowDown":
|
|
237
|
+
targetIndex = Math.min(lastIndex, currentIndex + 1);
|
|
238
|
+
break;
|
|
239
|
+
case "Home":
|
|
240
|
+
targetIndex = 0;
|
|
241
|
+
break;
|
|
242
|
+
case "End":
|
|
243
|
+
targetIndex = lastIndex;
|
|
244
|
+
break;
|
|
245
|
+
default: return;
|
|
246
|
+
}
|
|
247
|
+
if (targetIndex === currentIndex) {
|
|
290
248
|
e.preventDefault();
|
|
291
|
-
|
|
292
|
-
tableContext.moveColumnTo(currentIndex, targetIndex);
|
|
293
|
-
});
|
|
249
|
+
return;
|
|
294
250
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
};
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
{:
|
|
368
|
-
|
|
369
|
-
{
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
{
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
251
|
+
e.preventDefault();
|
|
252
|
+
preserveListScroll(() => {
|
|
253
|
+
tableContext.moveColumnTo(currentIndex, targetIndex);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
</script>
|
|
257
|
+
|
|
258
|
+
{#if columnDef}
|
|
259
|
+
{@const reorderLabel = typeof columnDef.header === 'string' ? columnDef.header : columnDef.id}
|
|
260
|
+
<!--
|
|
261
|
+
The `<li>` is a structural container for the eye, pin selector,
|
|
262
|
+
label, and reorder buttons - none activated by clicking the `<li>`
|
|
263
|
+
itself. The `onkeydown` here only captures Alt+Arrow / Alt+Home /
|
|
264
|
+
Alt+End bubbling up from whichever inner control has focus to
|
|
265
|
+
drive the reorder shortcut; the `<li>` is not an interactive
|
|
266
|
+
widget. The structure is a free-form list of cards (not a tree
|
|
267
|
+
or listbox), so the right call is to silence the warning rather
|
|
268
|
+
than fake `role="treeitem"` / `role="option"`.
|
|
269
|
+
-->
|
|
270
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
271
|
+
<li
|
|
272
|
+
bind:this={rowEl}
|
|
273
|
+
class="single-column"
|
|
274
|
+
class:targeted={isBeingTargeted}
|
|
275
|
+
class:draggable={tableContext.allowColumnReordering}
|
|
276
|
+
id="{tableContext.tableName}-column-settings-{columnDef.id}"
|
|
277
|
+
draggable={tableContext.allowColumnReordering}
|
|
278
|
+
onkeydown={onRowKeydown}
|
|
279
|
+
{@attach (node: HTMLLIElement) => {
|
|
280
|
+
// Capture-phase `mousedown` and `pointerdown` listeners. Both
|
|
281
|
+
// must fire BEFORE the synthetic click on the visibility /
|
|
282
|
+
// pin checkbox focuses its hidden input, because that focus
|
|
283
|
+
// triggers an auto-scroll to bring the 1x1px clip-pathed
|
|
284
|
+
// input into view. Capture phase runs before target / bubble,
|
|
285
|
+
// so this listener reliably snapshots the user's true
|
|
286
|
+
// pre-click scroll position.
|
|
287
|
+
//
|
|
288
|
+
// Both event types are wired because browsers differ on which
|
|
289
|
+
// fires first; the second is a cheap no-op snapshot.
|
|
290
|
+
const snapshot = (): void => {
|
|
291
|
+
snapshotScrollPosition();
|
|
292
|
+
};
|
|
293
|
+
node.addEventListener('pointerdown', snapshot, { capture: true });
|
|
294
|
+
node.addEventListener('mousedown', snapshot, { capture: true });
|
|
295
|
+
return () => {
|
|
296
|
+
node.removeEventListener('pointerdown', snapshot, { capture: true });
|
|
297
|
+
node.removeEventListener('mousedown', snapshot, { capture: true });
|
|
298
|
+
};
|
|
299
|
+
}}
|
|
300
|
+
ondragstart={() => {
|
|
301
|
+
tableContext.dragTarget = columnDef;
|
|
302
|
+
}}
|
|
303
|
+
ondragend={disableDragVisual}
|
|
304
|
+
ondragover={(e) => {
|
|
305
|
+
e.preventDefault();
|
|
306
|
+
if (tableContext.dragTarget?.id !== columnDef.id) {
|
|
307
|
+
isBeingTargeted = true;
|
|
308
|
+
}
|
|
309
|
+
}}
|
|
310
|
+
ondragleave={disableDragVisual}
|
|
311
|
+
ondrop={disableDragVisual}
|
|
312
|
+
>
|
|
313
|
+
<!--
|
|
314
|
+
`.row-main` stacks the column label on top and the visibility /
|
|
315
|
+
pin controls underneath so the label gets the full card width
|
|
316
|
+
to wrap into. `.reorder-buttons` is a sibling outside
|
|
317
|
+
`.row-main` so the up / down arrows stay vertically centred on
|
|
318
|
+
the right edge of the card.
|
|
319
|
+
-->
|
|
320
|
+
<div class="row-main">
|
|
321
|
+
<span
|
|
322
|
+
class="column-label"
|
|
323
|
+
title={typeof columnDef.header === 'string' ? columnDef.header : columnDef.id}
|
|
324
|
+
>
|
|
325
|
+
{#if typeof columnDef.header === 'function'}
|
|
326
|
+
<!-- FIXME this needs testing -->
|
|
327
|
+
{@render columnDef.header()}
|
|
328
|
+
{:else}
|
|
329
|
+
{columnDef.header}
|
|
330
|
+
{/if}
|
|
331
|
+
</span>
|
|
332
|
+
|
|
333
|
+
<div class="row-controls">
|
|
334
|
+
<div class="visibility-wrapper">
|
|
335
|
+
<Checkbox
|
|
336
|
+
icon={{ name: 'assessment', size: Size.MediumSmall }}
|
|
337
|
+
size={Size.FillWidth}
|
|
338
|
+
id={`is-visible-${tableContext.tableName}-${columnDef.id}`}
|
|
339
|
+
value={tableContext.columnVisibility[columnDef.id] !== false}
|
|
340
|
+
onchange={onVisibilityChange}
|
|
341
|
+
/>
|
|
342
|
+
</div>
|
|
343
|
+
|
|
344
|
+
{#if tableContext.allowColumnReordering}
|
|
345
|
+
<!--
|
|
346
|
+
Tri-state pin selector. The three `Ternary` slots map to:
|
|
347
|
+
False -> pinned LEFT (pin-left icon, leftmost)
|
|
348
|
+
Neutral -> unpinned (circle-cross icon, middle)
|
|
349
|
+
True -> pinned RIGHT (pin-right icon, rightmost)
|
|
350
|
+
Expand columns can never be pinned and disable the group.
|
|
351
|
+
-->
|
|
352
|
+
<div class="pin-selector">
|
|
353
|
+
<ThreeStateRadio
|
|
354
|
+
value={pinSide}
|
|
355
|
+
noLabel="Pin to left"
|
|
356
|
+
neutralLabel="Unpinned"
|
|
357
|
+
yesLabel="Pin to right"
|
|
358
|
+
noIcon={{ name: 'pin-left', size: Size.MediumSmall }}
|
|
359
|
+
neutralIcon={{ name: 'circle-cross', size: Size.MediumSmall }}
|
|
360
|
+
yesIcon={{ name: 'pin-right', size: Size.MediumSmall }}
|
|
361
|
+
onchange={(next: Ternary) => {
|
|
362
|
+
setPin(next);
|
|
363
|
+
}}
|
|
364
|
+
/>
|
|
365
|
+
</div>
|
|
366
|
+
{/if}
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
|
|
370
|
+
{#if tableContext.allowColumnReordering}
|
|
371
|
+
<!--
|
|
372
|
+
Up / down "move" buttons make column reordering
|
|
373
|
+
discoverable without relying on the user noticing a
|
|
374
|
+
drag-cursor. The whole row remains draggable for power
|
|
375
|
+
users. Buttons disable at the list boundaries so a no-op
|
|
376
|
+
`moveColumnTo` is never dispatched.
|
|
377
|
+
-->
|
|
378
|
+
<div class="reorder-buttons">
|
|
379
|
+
<Action
|
|
380
|
+
icon={{ name: 'chevron-up', size: Size.Medium }}
|
|
381
|
+
size={Size.Medium}
|
|
382
|
+
paddingSize={Size.Small}
|
|
383
|
+
label="Move {reorderLabel} up"
|
|
384
|
+
disabled={isAtTop}
|
|
385
|
+
onclick={moveUp}
|
|
386
|
+
/>
|
|
387
|
+
<Action
|
|
388
|
+
icon={{ name: 'chevron-down', size: Size.Medium }}
|
|
389
|
+
size={Size.Medium}
|
|
390
|
+
paddingSize={Size.Small}
|
|
391
|
+
label="Move {reorderLabel} down"
|
|
392
|
+
disabled={isAtBottom}
|
|
393
|
+
onclick={moveDown}
|
|
394
|
+
/>
|
|
395
|
+
</div>
|
|
396
|
+
{/if}
|
|
397
|
+
</li>
|
|
398
|
+
{:else}
|
|
399
|
+
<li
|
|
400
|
+
id="{tableContext.tableName}-column-settings-top-anchor"
|
|
401
|
+
class="single-column anchor"
|
|
402
|
+
class:targeted={isBeingTargeted}
|
|
403
|
+
ondragover={(e) => {
|
|
404
|
+
e.preventDefault();
|
|
405
|
+
|
|
406
|
+
if (tableContext.dragTarget?.id !== undefined) {
|
|
407
|
+
isBeingTargeted = true;
|
|
408
|
+
}
|
|
409
|
+
}}
|
|
410
|
+
ondragleave={disableDragVisual}
|
|
411
|
+
ondrop={disableDragVisual}
|
|
412
|
+
></li>
|
|
413
|
+
{/if}
|
|
414
|
+
|
|
454
415
|
<style>li {
|
|
455
416
|
box-sizing: border-box;
|
|
456
417
|
}
|
|
@@ -561,4 +522,4 @@ li.single-column:not(.anchor):last-of-type {
|
|
|
561
522
|
--colour: #1f81a0;
|
|
562
523
|
color: #1f81a0;
|
|
563
524
|
background-color: transparent;
|
|
564
|
-
}</style>
|
|
525
|
+
}</style>
|