@sectile/core 0.1.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/LICENSE +21 -0
- package/README.md +16 -0
- package/dist/accordion.d.ts +43 -0
- package/dist/accordion.d.ts.map +1 -0
- package/dist/accordion.js +83 -0
- package/dist/accordion.js.map +1 -0
- package/dist/alert-dialog.d.ts +18 -0
- package/dist/alert-dialog.d.ts.map +1 -0
- package/dist/alert-dialog.js +9 -0
- package/dist/alert-dialog.js.map +1 -0
- package/dist/calendar.d.ts +2 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +2 -0
- package/dist/calendar.js.map +1 -0
- package/dist/carousel.d.ts +40 -0
- package/dist/carousel.d.ts.map +1 -0
- package/dist/carousel.js +51 -0
- package/dist/carousel.js.map +1 -0
- package/dist/checkbox-group.d.ts +12 -0
- package/dist/checkbox-group.d.ts.map +1 -0
- package/dist/checkbox-group.js +12 -0
- package/dist/checkbox-group.js.map +1 -0
- package/dist/checkbox.d.ts +2 -0
- package/dist/checkbox.d.ts.map +1 -0
- package/dist/checkbox.js +2 -0
- package/dist/checkbox.js.map +1 -0
- package/dist/combobox.d.ts +16 -0
- package/dist/combobox.d.ts.map +1 -0
- package/dist/combobox.js +42 -0
- package/dist/combobox.js.map +1 -0
- package/dist/date-field.d.ts +58 -0
- package/dist/date-field.d.ts.map +1 -0
- package/dist/date-field.js +233 -0
- package/dist/date-field.js.map +1 -0
- package/dist/date-picker.d.ts +53 -0
- package/dist/date-picker.d.ts.map +1 -0
- package/dist/date-picker.js +157 -0
- package/dist/date-picker.js.map +1 -0
- package/dist/date-range-picker.d.ts +28 -0
- package/dist/date-range-picker.d.ts.map +1 -0
- package/dist/date-range-picker.js +85 -0
- package/dist/date-range-picker.js.map +1 -0
- package/dist/date-time-field.d.ts +53 -0
- package/dist/date-time-field.d.ts.map +1 -0
- package/dist/date-time-field.js +253 -0
- package/dist/date-time-field.js.map +1 -0
- package/dist/date-time-picker.d.ts +54 -0
- package/dist/date-time-picker.d.ts.map +1 -0
- package/dist/date-time-picker.js +151 -0
- package/dist/date-time-picker.js.map +1 -0
- package/dist/date-time-range-picker.d.ts +67 -0
- package/dist/date-time-range-picker.d.ts.map +1 -0
- package/dist/date-time-range-picker.js +226 -0
- package/dist/date-time-range-picker.js.map +1 -0
- package/dist/dialog.d.ts +16 -0
- package/dist/dialog.d.ts.map +1 -0
- package/dist/dialog.js +5 -0
- package/dist/dialog.js.map +1 -0
- package/dist/disclosure.d.ts +2 -0
- package/dist/disclosure.d.ts.map +1 -0
- package/dist/disclosure.js +2 -0
- package/dist/disclosure.js.map +1 -0
- package/dist/feed.d.ts +29 -0
- package/dist/feed.d.ts.map +1 -0
- package/dist/feed.js +26 -0
- package/dist/feed.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/interaction.d.ts +14 -0
- package/dist/interaction.d.ts.map +1 -0
- package/dist/interaction.js +38 -0
- package/dist/interaction.js.map +1 -0
- package/dist/internal/composites/calendar.d.ts +35 -0
- package/dist/internal/composites/calendar.d.ts.map +1 -0
- package/dist/internal/composites/calendar.js +139 -0
- package/dist/internal/composites/calendar.js.map +1 -0
- package/dist/internal/composites/combobox.d.ts +41 -0
- package/dist/internal/composites/combobox.d.ts.map +1 -0
- package/dist/internal/composites/combobox.js +184 -0
- package/dist/internal/composites/combobox.js.map +1 -0
- package/dist/internal/composites/grid-control.d.ts +36 -0
- package/dist/internal/composites/grid-control.d.ts.map +1 -0
- package/dist/internal/composites/grid-control.js +86 -0
- package/dist/internal/composites/grid-control.js.map +1 -0
- package/dist/internal/composites/linear-action.d.ts +35 -0
- package/dist/internal/composites/linear-action.d.ts.map +1 -0
- package/dist/internal/composites/linear-action.js +92 -0
- package/dist/internal/composites/linear-action.js.map +1 -0
- package/dist/internal/composites/linear-choice.d.ts +41 -0
- package/dist/internal/composites/linear-choice.d.ts.map +1 -0
- package/dist/internal/composites/linear-choice.js +139 -0
- package/dist/internal/composites/linear-choice.js.map +1 -0
- package/dist/internal/composites/listbox.d.ts +50 -0
- package/dist/internal/composites/listbox.d.ts.map +1 -0
- package/dist/internal/composites/listbox.js +243 -0
- package/dist/internal/composites/listbox.js.map +1 -0
- package/dist/internal/composites/menu.d.ts +28 -0
- package/dist/internal/composites/menu.d.ts.map +1 -0
- package/dist/internal/composites/menu.js +72 -0
- package/dist/internal/composites/menu.js.map +1 -0
- package/dist/internal/composites/popup.d.ts +20 -0
- package/dist/internal/composites/popup.d.ts.map +1 -0
- package/dist/internal/composites/popup.js +8 -0
- package/dist/internal/composites/popup.js.map +1 -0
- package/dist/internal/composites/slider.d.ts +20 -0
- package/dist/internal/composites/slider.d.ts.map +1 -0
- package/dist/internal/composites/slider.js +69 -0
- package/dist/internal/composites/slider.js.map +1 -0
- package/dist/internal/composites/tree-grid.d.ts +65 -0
- package/dist/internal/composites/tree-grid.d.ts.map +1 -0
- package/dist/internal/composites/tree-grid.js +274 -0
- package/dist/internal/composites/tree-grid.js.map +1 -0
- package/dist/internal/composites/tree-view.d.ts +39 -0
- package/dist/internal/composites/tree-view.d.ts.map +1 -0
- package/dist/internal/composites/tree-view.js +175 -0
- package/dist/internal/composites/tree-view.js.map +1 -0
- package/dist/internal/editing/text.d.ts +64 -0
- package/dist/internal/editing/text.d.ts.map +1 -0
- package/dist/internal/editing/text.js +238 -0
- package/dist/internal/editing/text.js.map +1 -0
- package/dist/internal/kernel/decimal.d.ts +22 -0
- package/dist/internal/kernel/decimal.d.ts.map +1 -0
- package/dist/internal/kernel/decimal.js +137 -0
- package/dist/internal/kernel/decimal.js.map +1 -0
- package/dist/internal/kernel/foundation.d.ts +12 -0
- package/dist/internal/kernel/foundation.d.ts.map +1 -0
- package/dist/internal/kernel/foundation.js +132 -0
- package/dist/internal/kernel/foundation.js.map +1 -0
- package/dist/internal/kernel/indexed-sequence.d.ts +26 -0
- package/dist/internal/kernel/indexed-sequence.d.ts.map +1 -0
- package/dist/internal/kernel/indexed-sequence.js +117 -0
- package/dist/internal/kernel/indexed-sequence.js.map +1 -0
- package/dist/internal/kernel/machine.d.ts +8 -0
- package/dist/internal/kernel/machine.d.ts.map +1 -0
- package/dist/internal/kernel/machine.js +6 -0
- package/dist/internal/kernel/machine.js.map +1 -0
- package/dist/internal/runtime/revision.d.ts +22 -0
- package/dist/internal/runtime/revision.d.ts.map +1 -0
- package/dist/internal/runtime/revision.js +72 -0
- package/dist/internal/runtime/revision.js.map +1 -0
- package/dist/internal/state/checked.d.ts +21 -0
- package/dist/internal/state/checked.d.ts.map +1 -0
- package/dist/internal/state/checked.js +29 -0
- package/dist/internal/state/checked.js.map +1 -0
- package/dist/internal/state/cursor.d.ts +13 -0
- package/dist/internal/state/cursor.d.ts.map +1 -0
- package/dist/internal/state/cursor.js +24 -0
- package/dist/internal/state/cursor.js.map +1 -0
- package/dist/internal/state/expansion.d.ts +8 -0
- package/dist/internal/state/expansion.d.ts.map +1 -0
- package/dist/internal/state/expansion.js +23 -0
- package/dist/internal/state/expansion.js.map +1 -0
- package/dist/internal/state/open-state.d.ts +16 -0
- package/dist/internal/state/open-state.d.ts.map +1 -0
- package/dist/internal/state/open-state.js +25 -0
- package/dist/internal/state/open-state.js.map +1 -0
- package/dist/internal/state/selection.d.ts +23 -0
- package/dist/internal/state/selection.d.ts.map +1 -0
- package/dist/internal/state/selection.js +110 -0
- package/dist/internal/state/selection.js.map +1 -0
- package/dist/listbox.d.ts +2 -0
- package/dist/listbox.d.ts.map +1 -0
- package/dist/listbox.js +2 -0
- package/dist/listbox.js.map +1 -0
- package/dist/menu-button.d.ts +3 -0
- package/dist/menu-button.d.ts.map +1 -0
- package/dist/menu-button.js +3 -0
- package/dist/menu-button.js.map +1 -0
- package/dist/menu.d.ts +8 -0
- package/dist/menu.d.ts.map +1 -0
- package/dist/menu.js +4 -0
- package/dist/menu.js.map +1 -0
- package/dist/menubar.d.ts +3 -0
- package/dist/menubar.d.ts.map +1 -0
- package/dist/menubar.js +3 -0
- package/dist/menubar.js.map +1 -0
- package/dist/multi-thumb-slider.d.ts +35 -0
- package/dist/multi-thumb-slider.d.ts.map +1 -0
- package/dist/multi-thumb-slider.js +68 -0
- package/dist/multi-thumb-slider.js.map +1 -0
- package/dist/number-field.d.ts +49 -0
- package/dist/number-field.d.ts.map +1 -0
- package/dist/number-field.js +454 -0
- package/dist/number-field.js.map +1 -0
- package/dist/pagination.d.ts +63 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +190 -0
- package/dist/pagination.js.map +1 -0
- package/dist/pin-input.d.ts +29 -0
- package/dist/pin-input.d.ts.map +1 -0
- package/dist/pin-input.js +49 -0
- package/dist/pin-input.js.map +1 -0
- package/dist/quantity-field.d.ts +58 -0
- package/dist/quantity-field.d.ts.map +1 -0
- package/dist/quantity-field.js +250 -0
- package/dist/quantity-field.js.map +1 -0
- package/dist/radio-group.d.ts +22 -0
- package/dist/radio-group.d.ts.map +1 -0
- package/dist/radio-group.js +16 -0
- package/dist/radio-group.js.map +1 -0
- package/dist/rating.d.ts +17 -0
- package/dist/rating.d.ts.map +1 -0
- package/dist/rating.js +15 -0
- package/dist/rating.js.map +1 -0
- package/dist/result.d.ts +10 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +19 -0
- package/dist/result.js.map +1 -0
- package/dist/revision.d.ts +2 -0
- package/dist/revision.d.ts.map +1 -0
- package/dist/revision.js +2 -0
- package/dist/revision.js.map +1 -0
- package/dist/select.d.ts +30 -0
- package/dist/select.d.ts.map +1 -0
- package/dist/select.js +28 -0
- package/dist/select.js.map +1 -0
- package/dist/shared.d.ts +42 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +3 -0
- package/dist/shared.js.map +1 -0
- package/dist/slider.d.ts +2 -0
- package/dist/slider.d.ts.map +1 -0
- package/dist/slider.js +2 -0
- package/dist/slider.js.map +1 -0
- package/dist/spin-button.d.ts +29 -0
- package/dist/spin-button.d.ts.map +1 -0
- package/dist/spin-button.js +61 -0
- package/dist/spin-button.js.map +1 -0
- package/dist/stepper.d.ts +17 -0
- package/dist/stepper.d.ts.map +1 -0
- package/dist/stepper.js +12 -0
- package/dist/stepper.js.map +1 -0
- package/dist/structures/grid.d.ts +27 -0
- package/dist/structures/grid.d.ts.map +1 -0
- package/dist/structures/grid.js +197 -0
- package/dist/structures/grid.js.map +1 -0
- package/dist/structures/range.d.ts +39 -0
- package/dist/structures/range.d.ts.map +1 -0
- package/dist/structures/range.js +242 -0
- package/dist/structures/range.js.map +1 -0
- package/dist/structures/sequence.d.ts +17 -0
- package/dist/structures/sequence.d.ts.map +1 -0
- package/dist/structures/sequence.js +24 -0
- package/dist/structures/sequence.js.map +1 -0
- package/dist/structures/tree.d.ts +32 -0
- package/dist/structures/tree.d.ts.map +1 -0
- package/dist/structures/tree.js +234 -0
- package/dist/structures/tree.js.map +1 -0
- package/dist/switch.d.ts +20 -0
- package/dist/switch.d.ts.map +1 -0
- package/dist/switch.js +4 -0
- package/dist/switch.js.map +1 -0
- package/dist/tabs.d.ts +15 -0
- package/dist/tabs.d.ts.map +1 -0
- package/dist/tabs.js +23 -0
- package/dist/tabs.js.map +1 -0
- package/dist/tags-input.d.ts +41 -0
- package/dist/tags-input.d.ts.map +1 -0
- package/dist/tags-input.js +44 -0
- package/dist/tags-input.js.map +1 -0
- package/dist/text.d.ts +2 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +2 -0
- package/dist/text.js.map +1 -0
- package/dist/time-field.d.ts +46 -0
- package/dist/time-field.d.ts.map +1 -0
- package/dist/time-field.js +148 -0
- package/dist/time-field.js.map +1 -0
- package/dist/toggle-button.d.ts +19 -0
- package/dist/toggle-button.d.ts.map +1 -0
- package/dist/toggle-button.js +16 -0
- package/dist/toggle-button.js.map +1 -0
- package/dist/toolbar.d.ts +2 -0
- package/dist/toolbar.d.ts.map +1 -0
- package/dist/toolbar.js +2 -0
- package/dist/toolbar.js.map +1 -0
- package/dist/tooltip.d.ts +2 -0
- package/dist/tooltip.d.ts.map +1 -0
- package/dist/tooltip.js +2 -0
- package/dist/tooltip.js.map +1 -0
- package/dist/tree-grid.d.ts +10 -0
- package/dist/tree-grid.d.ts.map +1 -0
- package/dist/tree-grid.js +14 -0
- package/dist/tree-grid.js.map +1 -0
- package/dist/tree-view.d.ts +2 -0
- package/dist/tree-view.d.ts.map +1 -0
- package/dist/tree-view.js +2 -0
- package/dist/tree-view.js.map +1 -0
- package/dist/units.d.ts +72 -0
- package/dist/units.d.ts.map +1 -0
- package/dist/units.js +496 -0
- package/dist/units.js.map +1 -0
- package/dist/window-splitter.d.ts +2 -0
- package/dist/window-splitter.d.ts.map +1 -0
- package/dist/window-splitter.js +2 -0
- package/dist/window-splitter.js.map +1 -0
- package/package.json +304 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { fail, ok, validateSafeCeiling } from './internal/kernel/foundation.js';
|
|
2
|
+
import { createMachineUpdate } from './internal/kernel/machine.js';
|
|
3
|
+
export function createPaginationModel(options) {
|
|
4
|
+
const itemsPerPage = options.itemsPerPage ?? 10;
|
|
5
|
+
const siblingCount = options.siblingCount ?? 2;
|
|
6
|
+
for (const [value, name, minimum] of [
|
|
7
|
+
[options.total, 'total', 0],
|
|
8
|
+
[itemsPerPage, 'itemsPerPage', 1],
|
|
9
|
+
[siblingCount, 'siblingCount', 0],
|
|
10
|
+
]) {
|
|
11
|
+
const error = validateSafeCeiling(value, name, minimum);
|
|
12
|
+
if (error !== null)
|
|
13
|
+
return { ok: false, error };
|
|
14
|
+
}
|
|
15
|
+
if (siblingCount > 1_000) {
|
|
16
|
+
return fail('resource-rejection', 'pagination-sibling-ceiling-exceeded', 'Pagination siblingCount exceeds its safety ceiling.', { siblingCount, ceiling: 1_000 });
|
|
17
|
+
}
|
|
18
|
+
if (options.showEdges !== undefined && typeof options.showEdges !== 'boolean') {
|
|
19
|
+
return fail('construction', 'invalid-pagination-show-edges', 'Pagination showEdges must be boolean.');
|
|
20
|
+
}
|
|
21
|
+
if (options.showControls !== undefined && typeof options.showControls !== 'boolean') {
|
|
22
|
+
return fail('construction', 'invalid-pagination-show-controls', 'Pagination showControls must be boolean.');
|
|
23
|
+
}
|
|
24
|
+
return ok(Object.freeze({
|
|
25
|
+
total: options.total,
|
|
26
|
+
itemsPerPage,
|
|
27
|
+
siblingCount,
|
|
28
|
+
showEdges: options.showEdges ?? false,
|
|
29
|
+
showControls: options.showControls ?? true,
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
export function createPaginationState(model, page = 1, itemsPerPage = model.itemsPerPage) {
|
|
33
|
+
const modelError = validatePaginationModel(model);
|
|
34
|
+
if (modelError !== null)
|
|
35
|
+
return { ok: false, error: modelError };
|
|
36
|
+
const sizeError = validateSafeCeiling(itemsPerPage, 'itemsPerPage', 1);
|
|
37
|
+
if (sizeError !== null)
|
|
38
|
+
return { ok: false, error: sizeError };
|
|
39
|
+
const pageCount = derivePageCount(model.total, itemsPerPage);
|
|
40
|
+
if (!Number.isSafeInteger(page) || page < 1 || page > pageCount) {
|
|
41
|
+
return fail('construction', 'pagination-page-out-of-range', 'Pagination page must be a safe integer inside the page range.', { page, pageCount });
|
|
42
|
+
}
|
|
43
|
+
return ok(Object.freeze({ page, itemsPerPage }));
|
|
44
|
+
}
|
|
45
|
+
export function applyPaginationEvent(model, state, event) {
|
|
46
|
+
const validState = createPaginationState(model, state.page, state.itemsPerPage);
|
|
47
|
+
if (!validState.ok)
|
|
48
|
+
return transitionFailure(validState);
|
|
49
|
+
if (!isPaginationEvent(event)) {
|
|
50
|
+
return fail('transition-rejection', 'invalid-pagination-event', 'Pagination event must select a page or request a page boundary movement.', { event });
|
|
51
|
+
}
|
|
52
|
+
if (typeof event === 'object' && event.type === 'set-items-per-page') {
|
|
53
|
+
const sizeError = validateSafeCeiling(event.itemsPerPage, 'itemsPerPage', 1);
|
|
54
|
+
if (sizeError !== null)
|
|
55
|
+
return { ok: false, error: { ...sizeError, class: 'transition-rejection' } };
|
|
56
|
+
}
|
|
57
|
+
const pageCount = derivePageCount(model.total, state.itemsPerPage);
|
|
58
|
+
const nextState = typeof event === 'object' && event.type === 'set-items-per-page'
|
|
59
|
+
? createPaginationState(model, Math.min(derivePageCount(model.total, event.itemsPerPage), Math.floor(((state.page - 1) * state.itemsPerPage) / event.itemsPerPage) + 1), event.itemsPerPage)
|
|
60
|
+
: createPaginationState(model, typeof event === 'object' ? event.page
|
|
61
|
+
: event === 'first-page' ? 1
|
|
62
|
+
: event === 'last-page' ? pageCount
|
|
63
|
+
: event === 'previous-page' ? Math.max(1, state.page - 1)
|
|
64
|
+
: Math.min(pageCount, state.page + 1), state.itemsPerPage);
|
|
65
|
+
if (!nextState.ok)
|
|
66
|
+
return transitionFailure(nextState);
|
|
67
|
+
const commands = [];
|
|
68
|
+
if (nextState.value.itemsPerPage !== state.itemsPerPage) {
|
|
69
|
+
commands.push({ type: 'items-per-page-changed', itemsPerPage: nextState.value.itemsPerPage });
|
|
70
|
+
}
|
|
71
|
+
if (nextState.value.page !== state.page)
|
|
72
|
+
commands.push({ type: 'page-changed', page: nextState.value.page });
|
|
73
|
+
return createMachineUpdate(nextState.value, commands);
|
|
74
|
+
}
|
|
75
|
+
export function getPaginationPageCount(model, state) {
|
|
76
|
+
const validState = createPaginationState(model, state.page, state.itemsPerPage);
|
|
77
|
+
return validState.ok
|
|
78
|
+
? ok(derivePageCount(model.total, validState.value.itemsPerPage))
|
|
79
|
+
: transitionFailure(validState);
|
|
80
|
+
}
|
|
81
|
+
export function getPaginationItems(model, state) {
|
|
82
|
+
const validState = createPaginationState(model, state.page, state.itemsPerPage);
|
|
83
|
+
if (!validState.ok)
|
|
84
|
+
return transitionFailure(validState);
|
|
85
|
+
const pageCount = derivePageCount(model.total, state.itemsPerPage);
|
|
86
|
+
const pages = getVisiblePages(state.page, pageCount, model.siblingCount, model.showEdges);
|
|
87
|
+
const items = [];
|
|
88
|
+
if (model.showControls) {
|
|
89
|
+
items.push(control('first-page', 1, state.page === 1), control('previous-page', Math.max(1, state.page - 1), state.page === 1));
|
|
90
|
+
}
|
|
91
|
+
for (const page of pages) {
|
|
92
|
+
items.push(typeof page === 'number'
|
|
93
|
+
? Object.freeze({ type: 'page', page, selected: page === state.page })
|
|
94
|
+
: Object.freeze({ type: 'ellipsis', side: page }));
|
|
95
|
+
}
|
|
96
|
+
if (model.showControls) {
|
|
97
|
+
items.push(control('next-page', Math.min(pageCount, state.page + 1), state.page === pageCount), control('last-page', pageCount, state.page === pageCount));
|
|
98
|
+
}
|
|
99
|
+
return ok(Object.freeze(items));
|
|
100
|
+
}
|
|
101
|
+
export function getPaginationItemRange(model, state) {
|
|
102
|
+
const validState = createPaginationState(model, state.page, state.itemsPerPage);
|
|
103
|
+
if (!validState.ok)
|
|
104
|
+
return transitionFailure(validState);
|
|
105
|
+
if (model.total === 0)
|
|
106
|
+
return ok(Object.freeze({ start: 0, end: 0, total: 0 }));
|
|
107
|
+
const start = (state.page - 1) * state.itemsPerPage + 1;
|
|
108
|
+
const count = Math.min(state.itemsPerPage, model.total - start + 1);
|
|
109
|
+
return ok(Object.freeze({ start, end: start + count - 1, total: model.total }));
|
|
110
|
+
}
|
|
111
|
+
function getVisiblePages(currentPage, pageCount, siblingCount, showEdges) {
|
|
112
|
+
const leftSibling = Math.max(currentPage - siblingCount, 1);
|
|
113
|
+
const rightSibling = Math.min(currentPage + siblingCount, pageCount);
|
|
114
|
+
if (!showEdges) {
|
|
115
|
+
const itemCount = siblingCount * 2 + 1;
|
|
116
|
+
if (pageCount <= itemCount)
|
|
117
|
+
return range(1, pageCount);
|
|
118
|
+
if (currentPage <= siblingCount + 1)
|
|
119
|
+
return range(1, itemCount);
|
|
120
|
+
if (pageCount - currentPage <= siblingCount)
|
|
121
|
+
return range(pageCount - itemCount + 1, pageCount);
|
|
122
|
+
return range(leftSibling, rightSibling);
|
|
123
|
+
}
|
|
124
|
+
const totalPageNumbers = Math.min(siblingCount * 2 + 5, pageCount);
|
|
125
|
+
const interiorCount = totalPageNumbers - 2;
|
|
126
|
+
const showStartEllipsis = leftSibling > 3
|
|
127
|
+
&& Math.abs(pageCount - interiorCount) > 2
|
|
128
|
+
&& Math.abs(leftSibling - 1) > 2;
|
|
129
|
+
const showEndEllipsis = rightSibling < pageCount - 2
|
|
130
|
+
&& Math.abs(pageCount - interiorCount) > 2
|
|
131
|
+
&& Math.abs(pageCount - rightSibling) > 2;
|
|
132
|
+
if (!showStartEllipsis && showEndEllipsis) {
|
|
133
|
+
return [...range(1, interiorCount), 'end', pageCount];
|
|
134
|
+
}
|
|
135
|
+
if (showStartEllipsis && !showEndEllipsis) {
|
|
136
|
+
return [1, 'start', ...range(pageCount - interiorCount + 1, pageCount)];
|
|
137
|
+
}
|
|
138
|
+
if (showStartEllipsis && showEndEllipsis) {
|
|
139
|
+
return [1, 'start', ...range(leftSibling, rightSibling), 'end', pageCount];
|
|
140
|
+
}
|
|
141
|
+
return range(1, pageCount);
|
|
142
|
+
}
|
|
143
|
+
function range(start, end) {
|
|
144
|
+
return Array.from({ length: Math.max(0, end - start + 1) }, (_, index) => start + index);
|
|
145
|
+
}
|
|
146
|
+
function derivePageCount(total, itemsPerPage) {
|
|
147
|
+
return Math.max(1, Math.ceil(total / itemsPerPage));
|
|
148
|
+
}
|
|
149
|
+
function control(controlType, targetPage, disabled) {
|
|
150
|
+
return Object.freeze({ type: 'control', control: controlType, targetPage, disabled });
|
|
151
|
+
}
|
|
152
|
+
function validatePaginationModel(model) {
|
|
153
|
+
if (typeof model !== 'object' || model === null) {
|
|
154
|
+
return {
|
|
155
|
+
class: 'construction',
|
|
156
|
+
code: 'invalid-pagination-model',
|
|
157
|
+
message: 'Pagination model must be an object.',
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (!Number.isSafeInteger(model.total) || model.total < 0
|
|
161
|
+
|| !Number.isSafeInteger(model.itemsPerPage) || model.itemsPerPage < 1
|
|
162
|
+
|| !Number.isSafeInteger(model.siblingCount) || model.siblingCount < 0
|
|
163
|
+
|| typeof model.showEdges !== 'boolean' || typeof model.showControls !== 'boolean') {
|
|
164
|
+
return {
|
|
165
|
+
class: 'construction',
|
|
166
|
+
code: 'invalid-pagination-model',
|
|
167
|
+
message: 'Pagination model fields must agree with a valid pagination domain.',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
function isPaginationEvent(value) {
|
|
173
|
+
if (typeof value === 'string') {
|
|
174
|
+
return value === 'first-page' || value === 'previous-page'
|
|
175
|
+
|| value === 'next-page' || value === 'last-page';
|
|
176
|
+
}
|
|
177
|
+
if (typeof value !== 'object' || value === null || !('type' in value))
|
|
178
|
+
return false;
|
|
179
|
+
return value.type === 'go-to-page'
|
|
180
|
+
? 'page' in value && Number.isSafeInteger(value.page)
|
|
181
|
+
: value.type === 'set-items-per-page'
|
|
182
|
+
&& 'itemsPerPage' in value && Number.isSafeInteger(value.itemsPerPage);
|
|
183
|
+
}
|
|
184
|
+
function transitionFailure(result) {
|
|
185
|
+
if (result.ok) {
|
|
186
|
+
return fail('internal-invariant', 'unexpected-pagination-success', 'Expected a pagination failure.');
|
|
187
|
+
}
|
|
188
|
+
return { ok: false, error: { ...result.error, class: 'transition-rejection' } };
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=pagination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Result } from './shared.js';
|
|
2
|
+
export interface PinInputState {
|
|
3
|
+
readonly values: readonly string[];
|
|
4
|
+
readonly current: number;
|
|
5
|
+
}
|
|
6
|
+
export type PinInputEvent = 'next' | 'previous' | 'backspace' | 'delete' | {
|
|
7
|
+
readonly type: 'focus';
|
|
8
|
+
readonly index: number;
|
|
9
|
+
} | {
|
|
10
|
+
readonly type: 'input';
|
|
11
|
+
readonly value: string;
|
|
12
|
+
};
|
|
13
|
+
export type PinInputCommand = {
|
|
14
|
+
readonly type: 'focus-cell';
|
|
15
|
+
readonly index: number;
|
|
16
|
+
} | {
|
|
17
|
+
readonly type: 'complete';
|
|
18
|
+
readonly value: string;
|
|
19
|
+
};
|
|
20
|
+
export interface PinInputPolicies {
|
|
21
|
+
readonly accept?: (value: string, index: number) => boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface PinInputUpdate {
|
|
24
|
+
readonly state: PinInputState;
|
|
25
|
+
readonly commands: readonly PinInputCommand[];
|
|
26
|
+
}
|
|
27
|
+
export declare function createPinInputState(length: number, value?: string | readonly string[]): Result<PinInputState>;
|
|
28
|
+
export declare function applyPinInputEvent(length: number, state: PinInputState, event: PinInputEvent, policies?: PinInputPolicies): Result<PinInputUpdate>;
|
|
29
|
+
//# sourceMappingURL=pin-input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pin-input.d.ts","sourceRoot":"","sources":["../src/pin-input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,MAAM,WAAW,aAAa;IAAG,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAC/F,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACnL,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAC9I,MAAM,WAAW,gBAAgB;IAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE;AACjG,MAAM,WAAW,cAAc;IAAG,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAA;CAAE;AAEhH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,GAAG,SAAS,MAAM,EAAO,GAAG,MAAM,CAAC,aAAa,CAAC,CAOjH;AACD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,GAAE,gBAAqB,GAAG,MAAM,CAAC,cAAc,CAAC,CAWtJ"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { fail, freezeArray, ok } from './internal/kernel/foundation.js';
|
|
2
|
+
import { createMachineUpdate } from './internal/kernel/machine.js';
|
|
3
|
+
export function createPinInputState(length, value = '') {
|
|
4
|
+
if (!Number.isSafeInteger(length) || length < 1)
|
|
5
|
+
return fail('construction', 'invalid-pin-input-length', 'Pin input length must be a positive safe integer.');
|
|
6
|
+
const source = typeof value === 'string' ? Array.from(value) : [...value];
|
|
7
|
+
if (source.length > length || source.some((part) => typeof part !== 'string' || Array.from(part).length > 1))
|
|
8
|
+
return fail('construction', 'invalid-pin-input-value', 'Pin input values must contain at most one character per cell.');
|
|
9
|
+
const values = Array.from({ length }, (_, index) => source[index] ?? '');
|
|
10
|
+
const current = Math.min(source.length, length - 1);
|
|
11
|
+
return ok(Object.freeze({ values: freezeArray(values), current }));
|
|
12
|
+
}
|
|
13
|
+
export function applyPinInputEvent(length, state, event, policies = {}) {
|
|
14
|
+
const valid = createPinInputState(length, state.values);
|
|
15
|
+
if (!valid.ok || state.current < 0 || state.current >= length)
|
|
16
|
+
return fail('transition-rejection', 'invalid-pin-input-state', 'Pin input state must match its declared length.');
|
|
17
|
+
const focus = (index) => createMachineUpdate(Object.freeze({ values: state.values, current: Math.max(0, Math.min(length - 1, index)) }), [{ type: 'focus-cell', index: Math.max(0, Math.min(length - 1, index)) }]);
|
|
18
|
+
if (event === 'next')
|
|
19
|
+
return focus(state.current + 1);
|
|
20
|
+
if (event === 'previous')
|
|
21
|
+
return focus(state.current - 1);
|
|
22
|
+
if (typeof event === 'object' && event.type === 'focus')
|
|
23
|
+
return Number.isSafeInteger(event.index) ? focus(event.index) : fail('transition-rejection', 'invalid-pin-input-index', 'Pin input focus index must be a safe integer.');
|
|
24
|
+
const values = [...state.values];
|
|
25
|
+
if (event === 'delete')
|
|
26
|
+
values[state.current] = '';
|
|
27
|
+
else if (event === 'backspace') {
|
|
28
|
+
if (values[state.current] === '' && state.current > 0) {
|
|
29
|
+
values[state.current - 1] = '';
|
|
30
|
+
return createMachineUpdate(Object.freeze({ values: freezeArray(values), current: state.current - 1 }), [{ type: 'focus-cell', index: state.current - 1 }]);
|
|
31
|
+
}
|
|
32
|
+
values[state.current] = '';
|
|
33
|
+
}
|
|
34
|
+
else if (typeof event === 'object' && event.type === 'input') {
|
|
35
|
+
const characters = Array.from(event.value);
|
|
36
|
+
if (characters.length !== 1 || policies.accept?.(event.value, state.current) === false)
|
|
37
|
+
return fail('transition-rejection', 'pin-input-value-rejected', 'Pin input accepts one permitted character at a time.');
|
|
38
|
+
values[state.current] = event.value;
|
|
39
|
+
const current = Math.min(length - 1, state.current + 1);
|
|
40
|
+
const commands = current === state.current ? [] : [{ type: 'focus-cell', index: current }];
|
|
41
|
+
if (values.every(Boolean))
|
|
42
|
+
commands.push({ type: 'complete', value: values.join('') });
|
|
43
|
+
return createMachineUpdate(Object.freeze({ values: freezeArray(values), current }), commands);
|
|
44
|
+
}
|
|
45
|
+
else
|
|
46
|
+
return fail('transition-rejection', 'invalid-pin-input-event', 'Pin input event is not recognized.');
|
|
47
|
+
return createMachineUpdate(Object.freeze({ values: freezeArray(values), current: state.current }));
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=pin-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pin-input.js","sourceRoot":"","sources":["../src/pin-input.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Result } from './shared.js';
|
|
2
|
+
import { type DecimalRounding } from './internal/kernel/decimal.js';
|
|
3
|
+
import { type NumberFieldEvent, type NumericExpressionEvaluator } from './number-field.js';
|
|
4
|
+
import { type TextEditingState } from './text.js';
|
|
5
|
+
import type { UnitExpression, UnitID, UnitRegistry, UnitSystemProfile } from './units.js';
|
|
6
|
+
export interface QuantityValue {
|
|
7
|
+
readonly value: string;
|
|
8
|
+
readonly unit: UnitID;
|
|
9
|
+
}
|
|
10
|
+
export interface QuantityFieldState {
|
|
11
|
+
readonly quantity: QuantityValue | null;
|
|
12
|
+
readonly displayUnit: UnitID;
|
|
13
|
+
readonly inputState: TextEditingState;
|
|
14
|
+
}
|
|
15
|
+
export type QuantityFieldEvent = NumberFieldEvent | {
|
|
16
|
+
readonly type: 'set-display-unit';
|
|
17
|
+
readonly unit: UnitID;
|
|
18
|
+
};
|
|
19
|
+
export type QuantityFieldCommand = {
|
|
20
|
+
readonly type: 'input-state-changed';
|
|
21
|
+
readonly value: TextEditingState;
|
|
22
|
+
} | {
|
|
23
|
+
readonly type: 'display-unit-changed';
|
|
24
|
+
readonly unit: UnitID;
|
|
25
|
+
} | {
|
|
26
|
+
readonly type: 'quantity-committed';
|
|
27
|
+
readonly value: QuantityValue | null;
|
|
28
|
+
readonly expression: string;
|
|
29
|
+
readonly displayUnit: UnitID;
|
|
30
|
+
};
|
|
31
|
+
export interface QuantityFieldPolicies {
|
|
32
|
+
readonly registry: UnitRegistry;
|
|
33
|
+
readonly canonicalUnit: UnitID;
|
|
34
|
+
readonly unitSystem?: UnitSystemProfile;
|
|
35
|
+
readonly evaluator?: NumericExpressionEvaluator;
|
|
36
|
+
readonly precision?: number;
|
|
37
|
+
readonly rounding?: DecimalRounding;
|
|
38
|
+
readonly min?: string;
|
|
39
|
+
readonly max?: string;
|
|
40
|
+
readonly required?: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface QuantityInputParserOptions {
|
|
43
|
+
readonly registry: UnitRegistry;
|
|
44
|
+
readonly evaluator?: NumericExpressionEvaluator;
|
|
45
|
+
}
|
|
46
|
+
export interface ParsedQuantityInput {
|
|
47
|
+
readonly value: string | null;
|
|
48
|
+
readonly expression: string;
|
|
49
|
+
readonly unitExpression: UnitExpression | null;
|
|
50
|
+
}
|
|
51
|
+
export interface QuantityFieldUpdate {
|
|
52
|
+
readonly state: QuantityFieldState;
|
|
53
|
+
readonly commands: readonly QuantityFieldCommand[];
|
|
54
|
+
}
|
|
55
|
+
export declare function createQuantityFieldState(policies: QuantityFieldPolicies, quantity?: QuantityValue | null, displayUnit?: UnitID, inputState?: TextEditingState): Result<QuantityFieldState>;
|
|
56
|
+
export declare function applyQuantityFieldEvent(state: QuantityFieldState, event: QuantityFieldEvent, policies: QuantityFieldPolicies): Result<QuantityFieldUpdate>;
|
|
57
|
+
export declare function parseQuantityInput(input: string, options: QuantityInputParserOptions): Result<ParsedQuantityInput>;
|
|
58
|
+
//# sourceMappingURL=quantity-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quantity-field.d.ts","sourceRoot":"","sources":["../src/quantity-field.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { compareDecimal, parseDecimal } from './internal/kernel/decimal.js';
|
|
2
|
+
import { fail, ok } from './internal/kernel/foundation.js';
|
|
3
|
+
import { createMachineUpdate } from './internal/kernel/machine.js';
|
|
4
|
+
import { applyNumberFieldEvent, createNumberFieldState, } from './number-field.js';
|
|
5
|
+
import { createTextEditingState } from './text.js';
|
|
6
|
+
export function createQuantityFieldState(policies, quantity = null, displayUnit, inputState) {
|
|
7
|
+
const validPolicies = validatePolicies(policies);
|
|
8
|
+
if (!validPolicies.ok)
|
|
9
|
+
return validPolicies;
|
|
10
|
+
const resolvedDisplayUnit = displayUnit
|
|
11
|
+
?? policies.unitSystem?.getDefaultUnit(policies.canonicalUnit)
|
|
12
|
+
?? policies.canonicalUnit;
|
|
13
|
+
const display = policies.registry.get(resolvedDisplayUnit);
|
|
14
|
+
if (display === null)
|
|
15
|
+
return fail('construction', 'unknown-display-unit', 'Quantity field display unit must be registered.', { displayUnit: resolvedDisplayUnit });
|
|
16
|
+
if (!policies.registry.compatible(policies.canonicalUnit, resolvedDisplayUnit)) {
|
|
17
|
+
return fail('construction', 'incompatible-display-unit', 'Quantity field display unit must match the canonical dimension.', { displayUnit: resolvedDisplayUnit, canonicalUnit: policies.canonicalUnit });
|
|
18
|
+
}
|
|
19
|
+
let normalizedQuantity = null;
|
|
20
|
+
if (quantity !== null) {
|
|
21
|
+
if (typeof quantity !== 'object' || typeof quantity.value !== 'string' || typeof quantity.unit !== 'string') {
|
|
22
|
+
return fail('construction', 'invalid-quantity-value', 'Quantity must contain decimal value text and a unit ID.');
|
|
23
|
+
}
|
|
24
|
+
const converted = policies.registry.convert(quantity.value, quantity.unit, policies.canonicalUnit, conversionOptions(policies));
|
|
25
|
+
if (!converted.ok)
|
|
26
|
+
return asConstruction(converted);
|
|
27
|
+
normalizedQuantity = Object.freeze({ value: converted.value.value, unit: policies.canonicalUnit });
|
|
28
|
+
}
|
|
29
|
+
let input = inputState;
|
|
30
|
+
if (input === undefined) {
|
|
31
|
+
const displayed = displayValue(normalizedQuantity, resolvedDisplayUnit, policies);
|
|
32
|
+
if (!displayed.ok)
|
|
33
|
+
return displayed;
|
|
34
|
+
const text = displayed.value ?? '';
|
|
35
|
+
const created = createTextEditingState(text, {
|
|
36
|
+
anchorCodeUnitOffset: text.length,
|
|
37
|
+
focusCodeUnitOffset: text.length,
|
|
38
|
+
});
|
|
39
|
+
if (!created.ok)
|
|
40
|
+
return created;
|
|
41
|
+
input = created.value;
|
|
42
|
+
}
|
|
43
|
+
const number = createNumberFieldState(null, input);
|
|
44
|
+
if (!number.ok)
|
|
45
|
+
return number;
|
|
46
|
+
return ok(Object.freeze({
|
|
47
|
+
quantity: normalizedQuantity,
|
|
48
|
+
displayUnit: resolvedDisplayUnit,
|
|
49
|
+
inputState: number.value.inputState,
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
export function applyQuantityFieldEvent(state, event, policies) {
|
|
53
|
+
const valid = createQuantityFieldState(policies, state.quantity, state.displayUnit, state.inputState);
|
|
54
|
+
if (!valid.ok)
|
|
55
|
+
return { ok: false, error: { ...valid.error, class: 'transition-rejection' } };
|
|
56
|
+
const bounds = normalizeBounds(policies);
|
|
57
|
+
if (!bounds.ok)
|
|
58
|
+
return bounds;
|
|
59
|
+
if (typeof event === 'object' && event.type === 'set-display-unit') {
|
|
60
|
+
return changeDisplayUnit(valid.value, event.unit, policies);
|
|
61
|
+
}
|
|
62
|
+
if (event === 'commit')
|
|
63
|
+
return commitQuantityField(valid.value, policies, bounds.value);
|
|
64
|
+
const displayed = displayValue(valid.value.quantity, valid.value.displayUnit, policies);
|
|
65
|
+
if (!displayed.ok)
|
|
66
|
+
return displayed;
|
|
67
|
+
const numberState = createNumberFieldState(displayed.value, valid.value.inputState);
|
|
68
|
+
if (!numberState.ok)
|
|
69
|
+
return numberState;
|
|
70
|
+
const reduced = applyNumberFieldEvent(numberState.value, event, {
|
|
71
|
+
...(policies.evaluator === undefined ? {} : { evaluator: policies.evaluator }),
|
|
72
|
+
...(policies.required === undefined ? {} : { required: policies.required }),
|
|
73
|
+
});
|
|
74
|
+
if (!reduced.ok)
|
|
75
|
+
return reduced;
|
|
76
|
+
const next = Object.freeze({ ...valid.value, inputState: reduced.value.state.inputState });
|
|
77
|
+
const commands = reduced.value.commands
|
|
78
|
+
.filter((command) => command.type === 'input-state-changed')
|
|
79
|
+
.map((command) => ({ type: 'input-state-changed', value: command.value }));
|
|
80
|
+
return createMachineUpdate(next, commands);
|
|
81
|
+
}
|
|
82
|
+
export function parseQuantityInput(input, options) {
|
|
83
|
+
if (typeof input !== 'string')
|
|
84
|
+
return fail('transition-rejection', 'invalid-quantity-input', 'Quantity input must be text.');
|
|
85
|
+
const expression = input.trim();
|
|
86
|
+
if (expression.length === 0)
|
|
87
|
+
return ok(Object.freeze({ value: null, expression: input, unitExpression: null }));
|
|
88
|
+
const direct = evaluateQuantityExpression(expression, options.evaluator);
|
|
89
|
+
if (direct.ok)
|
|
90
|
+
return ok(Object.freeze({ value: direct.value, expression: input, unitExpression: null }));
|
|
91
|
+
for (let position = expression.length - 1; position > 0; position -= 1) {
|
|
92
|
+
const numeric = expression.slice(0, position).trimEnd();
|
|
93
|
+
const unitSource = expression.slice(position).trimStart();
|
|
94
|
+
if (numeric.length === 0 || unitSource.length === 0)
|
|
95
|
+
continue;
|
|
96
|
+
const unitExpression = options.registry.parse(unitSource);
|
|
97
|
+
if (!unitExpression.ok)
|
|
98
|
+
continue;
|
|
99
|
+
const value = evaluateQuantityExpression(numeric, options.evaluator);
|
|
100
|
+
if (!value.ok)
|
|
101
|
+
continue;
|
|
102
|
+
return ok(Object.freeze({ value: value.value, expression: input, unitExpression: unitExpression.value }));
|
|
103
|
+
}
|
|
104
|
+
return fail('transition-rejection', 'invalid-quantity-input', 'Quantity input must contain a valid number or expression followed by an optional unit expression.', { input });
|
|
105
|
+
}
|
|
106
|
+
function commitQuantityField(state, policies, bounds) {
|
|
107
|
+
if (state.inputState.composition !== null) {
|
|
108
|
+
return fail('transition-rejection', 'quantity-commit-during-composition', 'Quantity field cannot commit during active composition.');
|
|
109
|
+
}
|
|
110
|
+
const parsedInput = parseQuantityInput(state.inputState.snapshot.text, {
|
|
111
|
+
registry: policies.registry,
|
|
112
|
+
...(policies.evaluator === undefined ? {} : { evaluator: policies.evaluator }),
|
|
113
|
+
});
|
|
114
|
+
if (!parsedInput.ok)
|
|
115
|
+
return parsedInput;
|
|
116
|
+
if (parsedInput.value.value === null) {
|
|
117
|
+
if (policies.required === true)
|
|
118
|
+
return fail('transition-rejection', 'required-quantity', 'Quantity field requires a value.');
|
|
119
|
+
const input = createTextEditingState('', { anchorCodeUnitOffset: 0, focusCodeUnitOffset: 0 });
|
|
120
|
+
if (!input.ok)
|
|
121
|
+
return input;
|
|
122
|
+
return createMachineUpdate(Object.freeze({ ...state, quantity: null, inputState: input.value }), [
|
|
123
|
+
{ type: 'input-state-changed', value: input.value },
|
|
124
|
+
{ type: 'quantity-committed', value: null, expression: parsedInput.value.expression, displayUnit: state.displayUnit },
|
|
125
|
+
]);
|
|
126
|
+
}
|
|
127
|
+
const canonical = parsedInput.value.unitExpression === null
|
|
128
|
+
? policies.registry.convert(parsedInput.value.value, state.displayUnit, policies.canonicalUnit, conversionOptions(policies))
|
|
129
|
+
: policies.registry.convertExpression(parsedInput.value.value, parsedInput.value.unitExpression, policies.canonicalUnit, conversionOptions(policies));
|
|
130
|
+
if (!canonical.ok)
|
|
131
|
+
return canonical;
|
|
132
|
+
const canonicalValue = canonical.value.value;
|
|
133
|
+
const parsedCanonical = parseDecimal(canonicalValue);
|
|
134
|
+
if (parsedCanonical === null)
|
|
135
|
+
return fail('internal-invariant', 'invalid-canonical-quantity', 'Unit registry returned invalid canonical decimal text.');
|
|
136
|
+
if (bounds.min !== null && compareDecimal(parsedCanonical, bounds.min) < 0) {
|
|
137
|
+
return fail('transition-rejection', 'quantity-below-minimum', 'Quantity is below its canonical minimum.', { value: canonicalValue, min: policies.min });
|
|
138
|
+
}
|
|
139
|
+
if (bounds.max !== null && compareDecimal(parsedCanonical, bounds.max) > 0) {
|
|
140
|
+
return fail('transition-rejection', 'quantity-above-maximum', 'Quantity is above its canonical maximum.', { value: canonicalValue, max: policies.max });
|
|
141
|
+
}
|
|
142
|
+
const requestedUnit = parsedInput.value.unitExpression?.resolvedUnit;
|
|
143
|
+
const displayUnit = requestedUnit !== null && requestedUnit !== undefined
|
|
144
|
+
&& policies.registry.compatible(policies.canonicalUnit, requestedUnit)
|
|
145
|
+
? requestedUnit
|
|
146
|
+
: state.displayUnit;
|
|
147
|
+
const display = policies.registry.convert(canonicalValue, policies.canonicalUnit, displayUnit, conversionOptions(policies));
|
|
148
|
+
if (!display.ok)
|
|
149
|
+
return display;
|
|
150
|
+
const input = createTextEditingState(display.value.value, {
|
|
151
|
+
anchorCodeUnitOffset: display.value.value.length,
|
|
152
|
+
focusCodeUnitOffset: display.value.value.length,
|
|
153
|
+
});
|
|
154
|
+
if (!input.ok)
|
|
155
|
+
return input;
|
|
156
|
+
const quantity = Object.freeze({ value: canonicalValue, unit: policies.canonicalUnit });
|
|
157
|
+
return createMachineUpdate(Object.freeze({ quantity, displayUnit, inputState: input.value }), [
|
|
158
|
+
{ type: 'input-state-changed', value: input.value },
|
|
159
|
+
...(displayUnit === state.displayUnit ? [] : [{ type: 'display-unit-changed', unit: displayUnit }]),
|
|
160
|
+
{ type: 'quantity-committed', value: quantity, expression: parsedInput.value.expression, displayUnit },
|
|
161
|
+
]);
|
|
162
|
+
}
|
|
163
|
+
function evaluateQuantityExpression(expression, evaluator) {
|
|
164
|
+
const input = createTextEditingState(expression, {
|
|
165
|
+
anchorCodeUnitOffset: expression.length,
|
|
166
|
+
focusCodeUnitOffset: expression.length,
|
|
167
|
+
});
|
|
168
|
+
if (!input.ok)
|
|
169
|
+
return input;
|
|
170
|
+
const state = createNumberFieldState(null, input.value);
|
|
171
|
+
if (!state.ok)
|
|
172
|
+
return state;
|
|
173
|
+
const committed = applyNumberFieldEvent(state.value, 'commit', {
|
|
174
|
+
...(evaluator === undefined ? {} : { evaluator }),
|
|
175
|
+
});
|
|
176
|
+
if (!committed.ok)
|
|
177
|
+
return committed;
|
|
178
|
+
const command = committed.value.commands.find((candidate) => candidate.type === 'value-committed');
|
|
179
|
+
return command === undefined
|
|
180
|
+
? fail('internal-invariant', 'missing-quantity-expression-result', 'Number field commit must produce a value command.')
|
|
181
|
+
: ok(command.value);
|
|
182
|
+
}
|
|
183
|
+
function changeDisplayUnit(state, displayUnit, policies) {
|
|
184
|
+
if (policies.registry.get(displayUnit) === null) {
|
|
185
|
+
return fail('transition-rejection', 'unknown-display-unit', 'Quantity field display unit must be registered.', { displayUnit });
|
|
186
|
+
}
|
|
187
|
+
if (!policies.registry.compatible(policies.canonicalUnit, displayUnit)) {
|
|
188
|
+
return fail('transition-rejection', 'incompatible-display-unit', 'Quantity field display unit must match the canonical dimension.', { displayUnit });
|
|
189
|
+
}
|
|
190
|
+
if (displayUnit === state.displayUnit)
|
|
191
|
+
return createMachineUpdate(state);
|
|
192
|
+
const displayed = displayValue(state.quantity, displayUnit, policies);
|
|
193
|
+
if (!displayed.ok)
|
|
194
|
+
return displayed;
|
|
195
|
+
const text = displayed.value ?? '';
|
|
196
|
+
const input = createTextEditingState(text, {
|
|
197
|
+
anchorCodeUnitOffset: text.length,
|
|
198
|
+
focusCodeUnitOffset: text.length,
|
|
199
|
+
});
|
|
200
|
+
if (!input.ok)
|
|
201
|
+
return input;
|
|
202
|
+
return createMachineUpdate(Object.freeze({ ...state, displayUnit, inputState: input.value }), [
|
|
203
|
+
{ type: 'input-state-changed', value: input.value },
|
|
204
|
+
{ type: 'display-unit-changed', unit: displayUnit },
|
|
205
|
+
]);
|
|
206
|
+
}
|
|
207
|
+
function displayValue(quantity, displayUnit, policies) {
|
|
208
|
+
if (quantity === null)
|
|
209
|
+
return ok(null);
|
|
210
|
+
const converted = policies.registry.convert(quantity.value, quantity.unit, displayUnit, conversionOptions(policies));
|
|
211
|
+
return converted.ok ? ok(converted.value.value) : converted;
|
|
212
|
+
}
|
|
213
|
+
function validatePolicies(policies) {
|
|
214
|
+
if (typeof policies !== 'object' || policies === null
|
|
215
|
+
|| typeof policies.registry !== 'object' || policies.registry === null
|
|
216
|
+
|| typeof policies.registry.get !== 'function'
|
|
217
|
+
|| typeof policies.registry.compatible !== 'function'
|
|
218
|
+
|| typeof policies.registry.convert !== 'function') {
|
|
219
|
+
return fail('construction', 'invalid-unit-registry', 'Quantity field requires a unit registry.');
|
|
220
|
+
}
|
|
221
|
+
if (typeof policies.canonicalUnit !== 'string' || policies.registry.get(policies.canonicalUnit) === null) {
|
|
222
|
+
return fail('construction', 'unknown-canonical-unit', 'Quantity field canonical unit must be registered.', { canonicalUnit: policies.canonicalUnit });
|
|
223
|
+
}
|
|
224
|
+
if (policies.unitSystem !== undefined && (typeof policies.unitSystem !== 'object' || policies.unitSystem === null
|
|
225
|
+
|| typeof policies.unitSystem.getDefaultUnit !== 'function' || typeof policies.unitSystem.getUnits !== 'function')) {
|
|
226
|
+
return fail('construction', 'invalid-unit-system', 'Quantity field unit system must provide default and compatible display units.');
|
|
227
|
+
}
|
|
228
|
+
return ok(true);
|
|
229
|
+
}
|
|
230
|
+
function normalizeBounds(policies) {
|
|
231
|
+
const min = policies.min === undefined ? null : parseDecimal(policies.min);
|
|
232
|
+
const max = policies.max === undefined ? null : parseDecimal(policies.max);
|
|
233
|
+
if (policies.min !== undefined && min === null)
|
|
234
|
+
return fail('construction', 'invalid-quantity-minimum', 'Quantity minimum must be canonical decimal text.');
|
|
235
|
+
if (policies.max !== undefined && max === null)
|
|
236
|
+
return fail('construction', 'invalid-quantity-maximum', 'Quantity maximum must be canonical decimal text.');
|
|
237
|
+
if (min !== null && max !== null && compareDecimal(min, max) > 0)
|
|
238
|
+
return fail('construction', 'inverted-quantity-bounds', 'Quantity minimum must not exceed its maximum.');
|
|
239
|
+
return ok(Object.freeze({ min, max }));
|
|
240
|
+
}
|
|
241
|
+
function conversionOptions(policies) {
|
|
242
|
+
return Object.freeze({
|
|
243
|
+
...(policies.precision === undefined ? {} : { precision: policies.precision }),
|
|
244
|
+
...(policies.rounding === undefined ? {} : { rounding: policies.rounding }),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function asConstruction(result) {
|
|
248
|
+
return result.ok ? result : { ok: false, error: { ...result.error, class: 'construction' } };
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=quantity-field.js.map
|