@lavalogic/scoria 0.40.0 → 0.40.2
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 -46
- package/dist/Components/AccordionGroupButton.svelte +76 -76
- package/dist/Components/Action.svelte +136 -136
- package/dist/Components/AlertModal.svelte +195 -195
- package/dist/Components/AppointmentCalendar/AppointmentCalendar.svelte +729 -729
- package/dist/Components/Base/BaseModal.svelte +125 -125
- package/dist/Components/Base/ContextWrapper.svelte +72 -72
- package/dist/Components/Base/Pagination.svelte +198 -198
- package/dist/Components/Base/Snippets.svelte +36 -36
- package/dist/Components/BigRadioSet.svelte +38 -38
- package/dist/Components/Bubble.svelte +101 -101
- package/dist/Components/Button.svelte +209 -209
- package/dist/Components/Checkbox.svelte +86 -86
- package/dist/Components/CollapsibleCard.svelte +78 -78
- package/dist/Components/Contexts/ContextMenu.svelte +164 -164
- package/dist/Components/Contexts/ContextMenuDivider.svelte +15 -15
- package/dist/Components/Contexts/ContextMenuOption.svelte +60 -60
- package/dist/Components/Contexts/Toast.svelte +86 -86
- package/dist/Components/Contexts/ToastContainer.svelte +65 -65
- package/dist/Components/Contexts/Tooltip.svelte +37 -37
- package/dist/Components/DataMatrixIcon.svelte +112 -112
- package/dist/Components/DateInput.svelte +148 -148
- package/dist/Components/DatePicker.svelte +300 -300
- package/dist/Components/DesktopModal.svelte +90 -90
- package/dist/Components/Dial.svelte +204 -204
- package/dist/Components/DimensionInput.svelte +170 -170
- package/dist/Components/DragMenu/DragMenuHolder.svelte +119 -119
- package/dist/Components/DragMenu/DraggableCard.svelte +77 -77
- package/dist/Components/FileCard.svelte +24 -24
- package/dist/Components/FileUpload.svelte +236 -236
- package/dist/Components/GridInput.svelte +196 -196
- package/dist/Components/GridInputSet.svelte +29 -29
- package/dist/Components/HorizontalTabGroup.svelte +82 -82
- package/dist/Components/HorizontalTabGroupButton.svelte +44 -44
- package/dist/Components/Icon.svelte +128 -128
- package/dist/Components/Icons.js +357 -357
- package/dist/Components/InfoAlert.svelte +35 -35
- package/dist/Components/LoadingAnimation.svelte +29 -29
- package/dist/Components/LoadingModal.svelte +45 -45
- package/dist/Components/LoadingOverlay.svelte +25 -25
- package/dist/Components/Modal.svelte +258 -258
- package/dist/Components/MultiSelect.svelte +220 -220
- package/dist/Components/Nav/Nav.svelte +110 -110
- package/dist/Components/Nav/NavServiceMenuList.svelte +43 -43
- package/dist/Components/Nav/NavTab.svelte +62 -62
- package/dist/Components/Nav/ServiceMenuItem.svelte +112 -112
- package/dist/Components/Nav/ServicesNav.svelte +80 -80
- package/dist/Components/NumberInput.svelte +157 -157
- package/dist/Components/PageHeading.svelte +25 -25
- package/dist/Components/PasswordInput.svelte +88 -88
- package/dist/Components/Portal.svelte +36 -36
- package/dist/Components/ProgressBubble.svelte +107 -107
- package/dist/Components/QuerySelect.svelte +366 -366
- package/dist/Components/SingleSelect.svelte +250 -250
- package/dist/Components/StepButton.svelte +70 -70
- package/dist/Components/StepForm.svelte +140 -140
- package/dist/Components/Switch.svelte +69 -69
- package/dist/Components/Table/ARCHITECTURE.md +298 -298
- package/dist/Components/Table/Body/QuickSearchCell.svelte +679 -679
- package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +51 -51
- package/dist/Components/Table/Body/Rows/ColumnList.svelte +59 -59
- package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +454 -454
- package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +79 -79
- package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +185 -185
- package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +124 -124
- package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +131 -120
- package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +234 -234
- package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +105 -105
- package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +87 -87
- package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +126 -126
- package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +159 -159
- package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +246 -246
- package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +216 -216
- package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +252 -252
- package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +179 -179
- package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +221 -221
- package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +197 -197
- package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +190 -190
- package/dist/Components/Table/Body/Rows/TableRow.svelte +290 -290
- package/dist/Components/Table/Body/TableBody.svelte +155 -155
- package/dist/Components/Table/ColumnFactory.svelte.js +1 -0
- package/dist/Components/Table/Headers/HighlightAllHeader.svelte +41 -41
- package/dist/Components/Table/Headers/SelectAllHeader.svelte +30 -30
- package/dist/Components/Table/Headers/TableHead.svelte +415 -415
- package/dist/Components/Table/Misc/ColumnPanel.svelte +39 -39
- package/dist/Components/Table/Misc/ColumnPanelModal.svelte +39 -39
- package/dist/Components/Table/Misc/FilterInput.svelte +641 -641
- package/dist/Components/Table/Misc/FilterPanel.svelte +190 -190
- package/dist/Components/Table/Misc/FilterPanelModal.svelte +31 -31
- package/dist/Components/Table/Misc/ShareViewModal.svelte +291 -291
- package/dist/Components/Table/Misc/SidePanel.svelte +35 -35
- package/dist/Components/Table/Misc/TableConfigurationModal.svelte +644 -644
- package/dist/Components/Table/Misc/TableFooter.svelte +340 -340
- package/dist/Components/Table/Misc/TableHorizontalBar.svelte +140 -140
- package/dist/Components/Table/Misc/TableSidebar.svelte +200 -200
- package/dist/Components/Table/Misc/TableViewDropdown.svelte +105 -105
- package/dist/Components/Table/Misc/ToolboxPanel.svelte +100 -100
- package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +107 -107
- package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte +192 -192
- package/dist/Components/Table/NestedTable.svelte +130 -130
- package/dist/Components/Table/Table.svelte +692 -692
- package/dist/Components/Table/Types/Columns/Definitions/Expand/ExpandDef.svelte.d.ts +5 -0
- package/dist/Components/Table/Types/Columns/Definitions/Expand/ExpandDef.svelte.js +3 -0
- package/dist/Components/Table/Types/Public/ColumnOptions.d.ts +10 -0
- package/dist/Components/Test/TestContextConsumer.svelte +13 -13
- package/dist/Components/TextArea.svelte +130 -130
- package/dist/Components/TextInput.svelte +176 -176
- package/dist/Components/ThreeStateRadio.svelte +131 -131
- package/dist/Components/Touch/BoolCard.svelte +45 -45
- package/dist/Components/Touch/DateModal.svelte +209 -209
- package/dist/Components/Touch/EditCard.svelte +59 -59
- package/dist/Components/Touch/InfoCard.svelte +31 -31
- package/dist/Components/Touch/InfoTextCard.svelte +28 -28
- package/dist/Components/Touch/ModalExplanation.svelte +17 -17
- package/dist/Components/Touch/NumberKeyboard.svelte +225 -225
- package/dist/Components/Touch/NumberModal.svelte +79 -79
- package/dist/Components/Touch/PlusMinusCard.svelte +46 -46
- package/dist/Components/Touch/SelectModal.svelte +127 -127
- package/dist/Components/Touch/SummaryCard.svelte +93 -93
- package/dist/Components/Touch/TextAreaModal.svelte +95 -95
- package/dist/Components/Touch/TextModal.svelte +106 -106
- package/dist/Components/Touch/TouchCard.svelte +33 -33
- package/dist/Components/Touch/TouchModal.svelte +274 -274
- package/dist/Components/Touch/UtilityButton.svelte +109 -109
- package/dist/Components/VerticalTabGroup.svelte +79 -79
- package/dist/Components/VerticalTabGroupButton.svelte +58 -58
- package/dist/Helpers/Datamatrix.d.ts +19 -19
- package/dist/Helpers/Datamatrix.js +291 -291
- package/dist/Types/Examples/ExampleModal.svelte +35 -35
- 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,342 +1,342 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script
|
|
4
|
-
lang="ts"
|
|
5
|
-
module
|
|
6
|
-
>
|
|
7
|
-
import Action from '../../Action.svelte';
|
|
8
|
-
import Button from '../../Button.svelte';
|
|
9
|
-
import Icon from '../../Icon.svelte';
|
|
10
|
-
import NumberInput from '../../NumberInput.svelte';
|
|
11
|
-
import SingleSelect from '../../SingleSelect.svelte';
|
|
12
|
-
import { AlertContext } from '../../../Contexts/AlertContext.svelte.js';
|
|
13
|
-
import { autoPluralise, devCatch } from '../../../Helpers/Helpers.svelte.js';
|
|
14
|
-
import { Colour, ColourSet } from '../../../scss/colours.js';
|
|
15
|
-
import type { SelectOption } from '../../../Types/Internal/SelectOption.js';
|
|
16
|
-
import { Side } from '../../../Types/Internal/Side.js';
|
|
17
|
-
import { Size } from '../../../Types/Internal/Size.js';
|
|
18
|
-
import { Variant } from '../../../Types/Internal/Variant.js';
|
|
19
|
-
import { getContext, tick } from 'svelte';
|
|
20
|
-
import { TableContext } from '../Types/Context/TableContext.svelte.js';
|
|
21
|
-
import type { Primitive } from '../Types/Context/TableInitOptions.js';
|
|
22
|
-
import { FilterType } from '../Types/Filtering/FilterType.js';
|
|
23
|
-
import type { TableFooterProps } from './TableFooterProps.js';
|
|
24
|
-
</script>
|
|
25
|
-
|
|
26
|
-
<script
|
|
27
|
-
lang="ts"
|
|
28
|
-
generics="T extends object, IdType extends Primitive"
|
|
29
|
-
>
|
|
30
|
-
let { onkeydown }: TableFooterProps = $props();
|
|
31
|
-
|
|
32
|
-
const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
|
|
33
|
-
const alertContext = getContext<AlertContext>(AlertContext.identifier);
|
|
34
|
-
|
|
35
|
-
const pageSizes: Array<SelectOption<number>> = $state([
|
|
36
|
-
{ label: '20', value: 20 },
|
|
37
|
-
{ label: '40', value: 40 },
|
|
38
|
-
{ label: '60', value: 60 },
|
|
39
|
-
{ label: '80', value: 80 },
|
|
40
|
-
{ label: '100', value: 100 },
|
|
41
|
-
]);
|
|
42
|
-
|
|
43
|
-
// eslint-disable-next-line svelte/prefer-writable-derived
|
|
44
|
-
let pageValue = $state((tableContext.paginationRepo?.paginationState.pageIndex ?? 0) + 1);
|
|
45
|
-
let currentSize: SelectOption<number> | null = $state(null);
|
|
46
|
-
|
|
47
|
-
function createOption(newSize: SelectOption<number>) {
|
|
48
|
-
pageSizes.push({ ...newSize, value: parseInt(newSize.value.toString()) });
|
|
49
|
-
pageSizes.sort((a, b) => a.value - b.value);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
$effect.pre(() => {
|
|
53
|
-
if (tableContext.paginationRepo) {
|
|
54
|
-
const found = pageSizes.find(
|
|
55
|
-
(size) => size.value === tableContext.paginationRepo?.paginationState.pageSize
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
if (found) {
|
|
59
|
-
currentSize = found;
|
|
60
|
-
} else {
|
|
61
|
-
const newValue = {
|
|
62
|
-
label: tableContext.paginationRepo.paginationState.pageSize.toString(),
|
|
63
|
-
value: tableContext.paginationRepo.paginationState.pageSize,
|
|
64
|
-
};
|
|
65
|
-
createOption(newValue);
|
|
66
|
-
currentSize = newValue;
|
|
67
|
-
}
|
|
68
|
-
} else {
|
|
69
|
-
currentSize = null;
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
let debounceTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
74
|
-
function onNumberInput(e: Event) {
|
|
75
|
-
const page = parseInt((e.target as HTMLInputElement).value);
|
|
76
|
-
|
|
77
|
-
if (debounceTimeout !== undefined) {
|
|
78
|
-
clearTimeout(debounceTimeout);
|
|
79
|
-
}
|
|
80
|
-
if (!isNaN(page)) {
|
|
81
|
-
debounceTimeout = setTimeout(() => {
|
|
82
|
-
tableContext.paginationRepo?.setPage(page);
|
|
83
|
-
tick()
|
|
84
|
-
.then(() => {
|
|
85
|
-
//force rerender
|
|
86
|
-
if (pageValue == (tableContext.paginationRepo?.paginationState.pageIndex ?? 0) + 1) {
|
|
87
|
-
pageValue = 0;
|
|
88
|
-
pageValue = (tableContext.paginationRepo?.paginationState.pageIndex ?? 0) + 1;
|
|
89
|
-
}
|
|
90
|
-
})
|
|
91
|
-
.catch(devCatch);
|
|
92
|
-
}, 400);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
$effect(() => {
|
|
97
|
-
return () => {
|
|
98
|
-
if (debounceTimeout !== undefined) {
|
|
99
|
-
clearTimeout(debounceTimeout);
|
|
100
|
-
debounceTimeout = undefined;
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
$effect(() => {
|
|
106
|
-
pageValue = (tableContext.paginationRepo?.paginationState.pageIndex ?? 0) + 1;
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
let pageButtons: Array<number> = $state([]);
|
|
110
|
-
|
|
111
|
-
$effect(() => {
|
|
112
|
-
if (tableContext.paginationRepo?.paginationState.pageIndex == null) {
|
|
113
|
-
pageButtons = [1];
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
const current = tableContext.paginationRepo.paginationState.pageIndex + 1;
|
|
117
|
-
const last = tableContext.lastPage;
|
|
118
|
-
// Empty / no-results case: render a single "1" button rather than
|
|
119
|
-
// a "0" button. `lastPage` is a 1-indexed count, never 0; a
|
|
120
|
-
// misbehaving data repo that returns 0 would otherwise leak
|
|
121
|
-
// through to a "Previous 0 1 Next" strip (see the page-0
|
|
122
|
-
// regression captured in image 2 of the bug report).
|
|
123
|
-
if (last < 1) {
|
|
124
|
-
pageButtons = [1];
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
// `pages` accumulates the dedup'd, sorted button set. The minimum
|
|
128
|
-
// the user always sees between the Previous and Next arrows is the
|
|
129
|
-
// current page, the next page (when one exists), and the last
|
|
130
|
-
// page. On top of that we still surface a small window around the
|
|
131
|
-
// current page for visual context. Every value pushed is clamped
|
|
132
|
-
// to `[1, last]` defensively so an out-of-range current page (e.g.
|
|
133
|
-
// a stale `pageIndex` waiting for the next remote reload to
|
|
134
|
-
// settle) cannot resurface a page-0 button.
|
|
135
|
-
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
136
|
-
const pages = new Set<number>();
|
|
137
|
-
const windowStart = Math.max(1, Math.min(current - 2, last - 4));
|
|
138
|
-
for (let i = windowStart; i < windowStart + 5 && i <= last; i++) {
|
|
139
|
-
pages.add(i);
|
|
140
|
-
}
|
|
141
|
-
if (current >= 1 && current <= last) {
|
|
142
|
-
pages.add(current);
|
|
143
|
-
if (current + 1 <= last) {
|
|
144
|
-
pages.add(current + 1);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
pages.add(last);
|
|
148
|
-
pageButtons = [...pages].sort((a, b) => a - b);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
let hasNoSelectedItems = $derived(tableContext.selectedItems.size == 0);
|
|
152
|
-
let selectedItemIconColour = $derived(
|
|
153
|
-
hasNoSelectedItems ? Colour['$ui-blue-4'] : Colour['$ui-blue-10']
|
|
154
|
-
);
|
|
155
|
-
</script>
|
|
156
|
-
|
|
157
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
158
|
-
<div
|
|
159
|
-
class="spread_row bottom_row"
|
|
160
|
-
class:limited={tableContext.limitFooter}
|
|
161
|
-
onkeydown={(e) => {
|
|
162
|
-
e.stopPropagation();
|
|
163
|
-
onkeydown?.(e);
|
|
164
|
-
}}
|
|
165
|
-
>
|
|
166
|
-
<div class="block">
|
|
167
|
-
<span
|
|
168
|
-
>Actioning: {tableContext.dataRepo.filterType === FilterType.Real ? 'Loaded' : 'All'} Records</span
|
|
169
|
-
>
|
|
170
|
-
</div>
|
|
171
|
-
|
|
172
|
-
<div class="block">
|
|
173
|
-
<span>{tableContext.selectedItems.size} Records Selected</span>
|
|
174
|
-
<Action
|
|
175
|
-
size={Size.FillHeight}
|
|
176
|
-
disabled={hasNoSelectedItems}
|
|
177
|
-
colourSet={ColourSet.Auxiliary}
|
|
178
|
-
icon={{
|
|
179
|
-
name: 'assessment',
|
|
180
|
-
size: Size.Medium,
|
|
181
|
-
colour: tableContext.showSelectedOnly ? Colour['$brand-primary'] : selectedItemIconColour,
|
|
182
|
-
}}
|
|
183
|
-
onclick={() => {
|
|
184
|
-
tableContext.showSelectedOnly = !tableContext.showSelectedOnly;
|
|
185
|
-
}}
|
|
186
|
-
/>
|
|
187
|
-
<Action
|
|
188
|
-
size={Size.FillHeight}
|
|
189
|
-
disabled={hasNoSelectedItems}
|
|
190
|
-
colourSet={ColourSet.Auxiliary}
|
|
191
|
-
icon={{
|
|
192
|
-
name: 'eraser',
|
|
193
|
-
size: Size.Medium,
|
|
194
|
-
colour: selectedItemIconColour,
|
|
195
|
-
}}
|
|
196
|
-
onclick={() => {
|
|
197
|
-
alertContext.prompt('Are you sure you want to clear your selection?', {
|
|
198
|
-
onYes: () => {
|
|
199
|
-
tableContext.showSelectedOnly = false;
|
|
200
|
-
tableContext.resetSelectedItems();
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
}}
|
|
204
|
-
/>
|
|
205
|
-
</div>
|
|
206
|
-
|
|
207
|
-
<div class="block">
|
|
208
|
-
<span>{tableContext.paginationRepo?.totalRows ?? 0} Rows</span>
|
|
209
|
-
</div>
|
|
210
|
-
|
|
211
|
-
{#if tableContext.paginationRepo}
|
|
212
|
-
<!-- forceUpwards={!nested} -->
|
|
213
|
-
<div class="block records-per-page">
|
|
214
|
-
<span>Records per Page:</span>
|
|
215
|
-
<!-- label="Records per Page:" -->
|
|
216
|
-
<SingleSelect
|
|
217
|
-
lazyDropdown
|
|
218
|
-
name="Page Size"
|
|
219
|
-
borderless
|
|
220
|
-
creatable
|
|
221
|
-
createFilter={(value: string) => {
|
|
222
|
-
const parsed = parseInt(value);
|
|
223
|
-
return !(parsed >= 1 && parsed <= 500);
|
|
224
|
-
}}
|
|
225
|
-
onCreateOption={(newSize: SelectOption<number>) => {
|
|
226
|
-
newSize.value = Math.max(Math.min(newSize.value, 500), 1);
|
|
227
|
-
|
|
228
|
-
const found = pageSizes.find((size) => size.value === newSize.value);
|
|
229
|
-
|
|
230
|
-
if (found) {
|
|
231
|
-
currentSize = found;
|
|
232
|
-
} else {
|
|
233
|
-
createOption(newSize);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
tableContext.paginationRepo?.setPageSize(newSize.value);
|
|
237
|
-
}}
|
|
238
|
-
value={currentSize}
|
|
239
|
-
options={pageSizes}
|
|
240
|
-
labelProp="label"
|
|
241
|
-
valueProp="value"
|
|
242
|
-
valueAsObject
|
|
243
|
-
onchange={(newSelection: SelectOption<number> | null) => {
|
|
244
|
-
if (newSelection?.value) {
|
|
245
|
-
tableContext.paginationRepo?.setPageSize(newSelection.value);
|
|
246
|
-
}
|
|
247
|
-
}}
|
|
248
|
-
/>
|
|
249
|
-
</div>
|
|
250
|
-
|
|
251
|
-
<div class="spacer"></div>
|
|
252
|
-
<!-- {/* TODO switch to a lib element for this */} -->
|
|
253
|
-
|
|
254
|
-
<div class="scrollable">
|
|
255
|
-
<div class="block">
|
|
256
|
-
<Icon
|
|
257
|
-
name="paper"
|
|
258
|
-
colour={Colour['$ui-blue-6']}
|
|
259
|
-
/>
|
|
260
|
-
<span>{tableContext.lastPage} {autoPluralise('Page', tableContext.lastPage)}</span>
|
|
261
|
-
</div>
|
|
262
|
-
|
|
263
|
-
<div class="block">
|
|
264
|
-
<label for="{tableContext.tableName}-jump-to-page"
|
|
265
|
-
><Icon
|
|
266
|
-
name="arrow-move-horizontal"
|
|
267
|
-
colour={Colour['$ui-blue-6']}
|
|
268
|
-
/></label
|
|
269
|
-
>
|
|
270
|
-
|
|
271
|
-
<label for="{tableContext.tableName}-jump-to-page">Jump to page:</label>
|
|
272
|
-
|
|
273
|
-
<NumberInput
|
|
274
|
-
id="{tableContext.tableName}-jump-to-page"
|
|
275
|
-
value={pageValue}
|
|
276
|
-
oninput={onNumberInput}
|
|
277
|
-
onfocus={(e) => {
|
|
278
|
-
// Select the existing value so typing replaces it, instead of
|
|
279
|
-
// appending to it (typing "5" on a "4" otherwise yields "45").
|
|
280
|
-
(e.target as HTMLInputElement).select();
|
|
281
|
-
}}
|
|
282
|
-
disabled={tableContext.showSelectedOnly}
|
|
283
|
-
/>
|
|
284
|
-
</div>
|
|
285
|
-
|
|
286
|
-
<div class="block nopad page-arrow">
|
|
287
|
-
<Button
|
|
288
|
-
variant={Variant.Toolbar}
|
|
289
|
-
disabled={tableContext.showSelectedOnly ||
|
|
290
|
-
tableContext.paginationRepo.paginationState.pageIndex === 0}
|
|
291
|
-
side={Side.Bottom}
|
|
292
|
-
onclick={() => {
|
|
293
|
-
tableContext.paginationRepo?.prevPage();
|
|
294
|
-
}}
|
|
295
|
-
size={Size.FillHeight}
|
|
296
|
-
iconLeft={{
|
|
297
|
-
name: 'left-close',
|
|
298
|
-
size: Size.FillHeight,
|
|
299
|
-
}}>Previous Page</Button
|
|
300
|
-
>
|
|
301
|
-
</div>
|
|
302
|
-
|
|
303
|
-
{#key pageButtons}
|
|
304
|
-
{#each pageButtons as pageNumber (pageNumber)}
|
|
305
|
-
<div class="block nopad">
|
|
306
|
-
<Button
|
|
307
|
-
variant={Variant.Toolbar}
|
|
308
|
-
disabled={tableContext.showSelectedOnly}
|
|
309
|
-
side={Side.Bottom}
|
|
310
|
-
size={Size.FillHeight}
|
|
311
|
-
toggled={tableContext.paginationRepo.paginationState.pageIndex === pageNumber - 1}
|
|
312
|
-
onclick={() => {
|
|
313
|
-
tableContext.paginationRepo?.setPage(pageNumber);
|
|
314
|
-
}}>{pageNumber}</Button
|
|
315
|
-
>
|
|
316
|
-
</div>
|
|
317
|
-
{/each}
|
|
318
|
-
{/key}
|
|
319
|
-
|
|
320
|
-
<div class="block nopad page-arrow">
|
|
321
|
-
<Button
|
|
322
|
-
variant={Variant.Toolbar}
|
|
323
|
-
side={Side.Bottom}
|
|
324
|
-
size={Size.FillHeight}
|
|
325
|
-
onclick={() => {
|
|
326
|
-
tableContext.paginationRepo?.nextPage();
|
|
327
|
-
}}
|
|
328
|
-
disabled={tableContext.showSelectedOnly ||
|
|
329
|
-
tableContext.paginationRepo.paginationState.pageIndex + 1 >= tableContext.lastPage}
|
|
330
|
-
iconRight={{
|
|
331
|
-
name: 'right-close',
|
|
332
|
-
size: Size.FillHeight,
|
|
333
|
-
}}>Next Page</Button
|
|
334
|
-
>
|
|
335
|
-
</div>
|
|
336
|
-
</div>
|
|
337
|
-
{/if}
|
|
338
|
-
</div>
|
|
339
|
-
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script
|
|
4
|
+
lang="ts"
|
|
5
|
+
module
|
|
6
|
+
>
|
|
7
|
+
import Action from '../../Action.svelte';
|
|
8
|
+
import Button from '../../Button.svelte';
|
|
9
|
+
import Icon from '../../Icon.svelte';
|
|
10
|
+
import NumberInput from '../../NumberInput.svelte';
|
|
11
|
+
import SingleSelect from '../../SingleSelect.svelte';
|
|
12
|
+
import { AlertContext } from '../../../Contexts/AlertContext.svelte.js';
|
|
13
|
+
import { autoPluralise, devCatch } from '../../../Helpers/Helpers.svelte.js';
|
|
14
|
+
import { Colour, ColourSet } from '../../../scss/colours.js';
|
|
15
|
+
import type { SelectOption } from '../../../Types/Internal/SelectOption.js';
|
|
16
|
+
import { Side } from '../../../Types/Internal/Side.js';
|
|
17
|
+
import { Size } from '../../../Types/Internal/Size.js';
|
|
18
|
+
import { Variant } from '../../../Types/Internal/Variant.js';
|
|
19
|
+
import { getContext, tick } from 'svelte';
|
|
20
|
+
import { TableContext } from '../Types/Context/TableContext.svelte.js';
|
|
21
|
+
import type { Primitive } from '../Types/Context/TableInitOptions.js';
|
|
22
|
+
import { FilterType } from '../Types/Filtering/FilterType.js';
|
|
23
|
+
import type { TableFooterProps } from './TableFooterProps.js';
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<script
|
|
27
|
+
lang="ts"
|
|
28
|
+
generics="T extends object, IdType extends Primitive"
|
|
29
|
+
>
|
|
30
|
+
let { onkeydown }: TableFooterProps = $props();
|
|
31
|
+
|
|
32
|
+
const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
|
|
33
|
+
const alertContext = getContext<AlertContext>(AlertContext.identifier);
|
|
34
|
+
|
|
35
|
+
const pageSizes: Array<SelectOption<number>> = $state([
|
|
36
|
+
{ label: '20', value: 20 },
|
|
37
|
+
{ label: '40', value: 40 },
|
|
38
|
+
{ label: '60', value: 60 },
|
|
39
|
+
{ label: '80', value: 80 },
|
|
40
|
+
{ label: '100', value: 100 },
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
// eslint-disable-next-line svelte/prefer-writable-derived
|
|
44
|
+
let pageValue = $state((tableContext.paginationRepo?.paginationState.pageIndex ?? 0) + 1);
|
|
45
|
+
let currentSize: SelectOption<number> | null = $state(null);
|
|
46
|
+
|
|
47
|
+
function createOption(newSize: SelectOption<number>) {
|
|
48
|
+
pageSizes.push({ ...newSize, value: parseInt(newSize.value.toString()) });
|
|
49
|
+
pageSizes.sort((a, b) => a.value - b.value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$effect.pre(() => {
|
|
53
|
+
if (tableContext.paginationRepo) {
|
|
54
|
+
const found = pageSizes.find(
|
|
55
|
+
(size) => size.value === tableContext.paginationRepo?.paginationState.pageSize
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
if (found) {
|
|
59
|
+
currentSize = found;
|
|
60
|
+
} else {
|
|
61
|
+
const newValue = {
|
|
62
|
+
label: tableContext.paginationRepo.paginationState.pageSize.toString(),
|
|
63
|
+
value: tableContext.paginationRepo.paginationState.pageSize,
|
|
64
|
+
};
|
|
65
|
+
createOption(newValue);
|
|
66
|
+
currentSize = newValue;
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
currentSize = null;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
let debounceTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
74
|
+
function onNumberInput(e: Event) {
|
|
75
|
+
const page = parseInt((e.target as HTMLInputElement).value);
|
|
76
|
+
|
|
77
|
+
if (debounceTimeout !== undefined) {
|
|
78
|
+
clearTimeout(debounceTimeout);
|
|
79
|
+
}
|
|
80
|
+
if (!isNaN(page)) {
|
|
81
|
+
debounceTimeout = setTimeout(() => {
|
|
82
|
+
tableContext.paginationRepo?.setPage(page);
|
|
83
|
+
tick()
|
|
84
|
+
.then(() => {
|
|
85
|
+
//force rerender
|
|
86
|
+
if (pageValue == (tableContext.paginationRepo?.paginationState.pageIndex ?? 0) + 1) {
|
|
87
|
+
pageValue = 0;
|
|
88
|
+
pageValue = (tableContext.paginationRepo?.paginationState.pageIndex ?? 0) + 1;
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
.catch(devCatch);
|
|
92
|
+
}, 400);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
$effect(() => {
|
|
97
|
+
return () => {
|
|
98
|
+
if (debounceTimeout !== undefined) {
|
|
99
|
+
clearTimeout(debounceTimeout);
|
|
100
|
+
debounceTimeout = undefined;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
$effect(() => {
|
|
106
|
+
pageValue = (tableContext.paginationRepo?.paginationState.pageIndex ?? 0) + 1;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
let pageButtons: Array<number> = $state([]);
|
|
110
|
+
|
|
111
|
+
$effect(() => {
|
|
112
|
+
if (tableContext.paginationRepo?.paginationState.pageIndex == null) {
|
|
113
|
+
pageButtons = [1];
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const current = tableContext.paginationRepo.paginationState.pageIndex + 1;
|
|
117
|
+
const last = tableContext.lastPage;
|
|
118
|
+
// Empty / no-results case: render a single "1" button rather than
|
|
119
|
+
// a "0" button. `lastPage` is a 1-indexed count, never 0; a
|
|
120
|
+
// misbehaving data repo that returns 0 would otherwise leak
|
|
121
|
+
// through to a "Previous 0 1 Next" strip (see the page-0
|
|
122
|
+
// regression captured in image 2 of the bug report).
|
|
123
|
+
if (last < 1) {
|
|
124
|
+
pageButtons = [1];
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// `pages` accumulates the dedup'd, sorted button set. The minimum
|
|
128
|
+
// the user always sees between the Previous and Next arrows is the
|
|
129
|
+
// current page, the next page (when one exists), and the last
|
|
130
|
+
// page. On top of that we still surface a small window around the
|
|
131
|
+
// current page for visual context. Every value pushed is clamped
|
|
132
|
+
// to `[1, last]` defensively so an out-of-range current page (e.g.
|
|
133
|
+
// a stale `pageIndex` waiting for the next remote reload to
|
|
134
|
+
// settle) cannot resurface a page-0 button.
|
|
135
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
136
|
+
const pages = new Set<number>();
|
|
137
|
+
const windowStart = Math.max(1, Math.min(current - 2, last - 4));
|
|
138
|
+
for (let i = windowStart; i < windowStart + 5 && i <= last; i++) {
|
|
139
|
+
pages.add(i);
|
|
140
|
+
}
|
|
141
|
+
if (current >= 1 && current <= last) {
|
|
142
|
+
pages.add(current);
|
|
143
|
+
if (current + 1 <= last) {
|
|
144
|
+
pages.add(current + 1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
pages.add(last);
|
|
148
|
+
pageButtons = [...pages].sort((a, b) => a - b);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
let hasNoSelectedItems = $derived(tableContext.selectedItems.size == 0);
|
|
152
|
+
let selectedItemIconColour = $derived(
|
|
153
|
+
hasNoSelectedItems ? Colour['$ui-blue-4'] : Colour['$ui-blue-10']
|
|
154
|
+
);
|
|
155
|
+
</script>
|
|
156
|
+
|
|
157
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
158
|
+
<div
|
|
159
|
+
class="spread_row bottom_row"
|
|
160
|
+
class:limited={tableContext.limitFooter}
|
|
161
|
+
onkeydown={(e) => {
|
|
162
|
+
e.stopPropagation();
|
|
163
|
+
onkeydown?.(e);
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
<div class="block">
|
|
167
|
+
<span
|
|
168
|
+
>Actioning: {tableContext.dataRepo.filterType === FilterType.Real ? 'Loaded' : 'All'} Records</span
|
|
169
|
+
>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<div class="block">
|
|
173
|
+
<span>{tableContext.selectedItems.size} Records Selected</span>
|
|
174
|
+
<Action
|
|
175
|
+
size={Size.FillHeight}
|
|
176
|
+
disabled={hasNoSelectedItems}
|
|
177
|
+
colourSet={ColourSet.Auxiliary}
|
|
178
|
+
icon={{
|
|
179
|
+
name: 'assessment',
|
|
180
|
+
size: Size.Medium,
|
|
181
|
+
colour: tableContext.showSelectedOnly ? Colour['$brand-primary'] : selectedItemIconColour,
|
|
182
|
+
}}
|
|
183
|
+
onclick={() => {
|
|
184
|
+
tableContext.showSelectedOnly = !tableContext.showSelectedOnly;
|
|
185
|
+
}}
|
|
186
|
+
/>
|
|
187
|
+
<Action
|
|
188
|
+
size={Size.FillHeight}
|
|
189
|
+
disabled={hasNoSelectedItems}
|
|
190
|
+
colourSet={ColourSet.Auxiliary}
|
|
191
|
+
icon={{
|
|
192
|
+
name: 'eraser',
|
|
193
|
+
size: Size.Medium,
|
|
194
|
+
colour: selectedItemIconColour,
|
|
195
|
+
}}
|
|
196
|
+
onclick={() => {
|
|
197
|
+
alertContext.prompt('Are you sure you want to clear your selection?', {
|
|
198
|
+
onYes: () => {
|
|
199
|
+
tableContext.showSelectedOnly = false;
|
|
200
|
+
tableContext.resetSelectedItems();
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}}
|
|
204
|
+
/>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div class="block">
|
|
208
|
+
<span>{tableContext.paginationRepo?.totalRows ?? 0} Rows</span>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
{#if tableContext.paginationRepo}
|
|
212
|
+
<!-- forceUpwards={!nested} -->
|
|
213
|
+
<div class="block records-per-page">
|
|
214
|
+
<span>Records per Page:</span>
|
|
215
|
+
<!-- label="Records per Page:" -->
|
|
216
|
+
<SingleSelect
|
|
217
|
+
lazyDropdown
|
|
218
|
+
name="Page Size"
|
|
219
|
+
borderless
|
|
220
|
+
creatable
|
|
221
|
+
createFilter={(value: string) => {
|
|
222
|
+
const parsed = parseInt(value);
|
|
223
|
+
return !(parsed >= 1 && parsed <= 500);
|
|
224
|
+
}}
|
|
225
|
+
onCreateOption={(newSize: SelectOption<number>) => {
|
|
226
|
+
newSize.value = Math.max(Math.min(newSize.value, 500), 1);
|
|
227
|
+
|
|
228
|
+
const found = pageSizes.find((size) => size.value === newSize.value);
|
|
229
|
+
|
|
230
|
+
if (found) {
|
|
231
|
+
currentSize = found;
|
|
232
|
+
} else {
|
|
233
|
+
createOption(newSize);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
tableContext.paginationRepo?.setPageSize(newSize.value);
|
|
237
|
+
}}
|
|
238
|
+
value={currentSize}
|
|
239
|
+
options={pageSizes}
|
|
240
|
+
labelProp="label"
|
|
241
|
+
valueProp="value"
|
|
242
|
+
valueAsObject
|
|
243
|
+
onchange={(newSelection: SelectOption<number> | null) => {
|
|
244
|
+
if (newSelection?.value) {
|
|
245
|
+
tableContext.paginationRepo?.setPageSize(newSelection.value);
|
|
246
|
+
}
|
|
247
|
+
}}
|
|
248
|
+
/>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<div class="spacer"></div>
|
|
252
|
+
<!-- {/* TODO switch to a lib element for this */} -->
|
|
253
|
+
|
|
254
|
+
<div class="scrollable">
|
|
255
|
+
<div class="block">
|
|
256
|
+
<Icon
|
|
257
|
+
name="paper"
|
|
258
|
+
colour={Colour['$ui-blue-6']}
|
|
259
|
+
/>
|
|
260
|
+
<span>{tableContext.lastPage} {autoPluralise('Page', tableContext.lastPage)}</span>
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
<div class="block">
|
|
264
|
+
<label for="{tableContext.tableName}-jump-to-page"
|
|
265
|
+
><Icon
|
|
266
|
+
name="arrow-move-horizontal"
|
|
267
|
+
colour={Colour['$ui-blue-6']}
|
|
268
|
+
/></label
|
|
269
|
+
>
|
|
270
|
+
|
|
271
|
+
<label for="{tableContext.tableName}-jump-to-page">Jump to page:</label>
|
|
272
|
+
|
|
273
|
+
<NumberInput
|
|
274
|
+
id="{tableContext.tableName}-jump-to-page"
|
|
275
|
+
value={pageValue}
|
|
276
|
+
oninput={onNumberInput}
|
|
277
|
+
onfocus={(e) => {
|
|
278
|
+
// Select the existing value so typing replaces it, instead of
|
|
279
|
+
// appending to it (typing "5" on a "4" otherwise yields "45").
|
|
280
|
+
(e.target as HTMLInputElement).select();
|
|
281
|
+
}}
|
|
282
|
+
disabled={tableContext.showSelectedOnly}
|
|
283
|
+
/>
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
<div class="block nopad page-arrow">
|
|
287
|
+
<Button
|
|
288
|
+
variant={Variant.Toolbar}
|
|
289
|
+
disabled={tableContext.showSelectedOnly ||
|
|
290
|
+
tableContext.paginationRepo.paginationState.pageIndex === 0}
|
|
291
|
+
side={Side.Bottom}
|
|
292
|
+
onclick={() => {
|
|
293
|
+
tableContext.paginationRepo?.prevPage();
|
|
294
|
+
}}
|
|
295
|
+
size={Size.FillHeight}
|
|
296
|
+
iconLeft={{
|
|
297
|
+
name: 'left-close',
|
|
298
|
+
size: Size.FillHeight,
|
|
299
|
+
}}>Previous Page</Button
|
|
300
|
+
>
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
{#key pageButtons}
|
|
304
|
+
{#each pageButtons as pageNumber (pageNumber)}
|
|
305
|
+
<div class="block nopad">
|
|
306
|
+
<Button
|
|
307
|
+
variant={Variant.Toolbar}
|
|
308
|
+
disabled={tableContext.showSelectedOnly}
|
|
309
|
+
side={Side.Bottom}
|
|
310
|
+
size={Size.FillHeight}
|
|
311
|
+
toggled={tableContext.paginationRepo.paginationState.pageIndex === pageNumber - 1}
|
|
312
|
+
onclick={() => {
|
|
313
|
+
tableContext.paginationRepo?.setPage(pageNumber);
|
|
314
|
+
}}>{pageNumber}</Button
|
|
315
|
+
>
|
|
316
|
+
</div>
|
|
317
|
+
{/each}
|
|
318
|
+
{/key}
|
|
319
|
+
|
|
320
|
+
<div class="block nopad page-arrow">
|
|
321
|
+
<Button
|
|
322
|
+
variant={Variant.Toolbar}
|
|
323
|
+
side={Side.Bottom}
|
|
324
|
+
size={Size.FillHeight}
|
|
325
|
+
onclick={() => {
|
|
326
|
+
tableContext.paginationRepo?.nextPage();
|
|
327
|
+
}}
|
|
328
|
+
disabled={tableContext.showSelectedOnly ||
|
|
329
|
+
tableContext.paginationRepo.paginationState.pageIndex + 1 >= tableContext.lastPage}
|
|
330
|
+
iconRight={{
|
|
331
|
+
name: 'right-close',
|
|
332
|
+
size: Size.FillHeight,
|
|
333
|
+
}}>Next Page</Button
|
|
334
|
+
>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
{/if}
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
340
|
<style>.spread_row {
|
|
341
341
|
border-top: solid 1px #cbd4da;
|
|
342
342
|
overflow: visible;
|
|
@@ -409,4 +409,4 @@
|
|
|
409
409
|
}
|
|
410
410
|
.spread_row .spacer {
|
|
411
411
|
flex-grow: 2;
|
|
412
|
-
}</style>
|
|
412
|
+
}</style>
|