@chelone/core 0.6.0 → 0.7.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/dist/assistant/assistant-session.d.ts +6 -0
- package/dist/assistant/assistant-session.js +204 -181
- package/dist/assistant/echo-transport.js +26 -29
- package/dist/assistant/http-transport.d.ts +10 -0
- package/dist/assistant/http-transport.js +98 -0
- package/dist/assistant/types.d.ts +1 -7
- package/dist/auth/access-control.d.ts +13 -5
- package/dist/auth/access-control.js +25 -21
- package/dist/auth/auth-options.d.ts +21 -17
- package/dist/auth/auth-options.js +80 -48
- package/dist/auth/permission-matrix.d.ts +12 -0
- package/dist/auth/permission-matrix.js +67 -0
- package/dist/auth/types.d.ts +14 -4
- package/dist/block-editor/block-document.d.ts +60 -0
- package/dist/block-editor/block-document.js +143 -0
- package/dist/block-editor/data-blocks.d.ts +63 -0
- package/dist/block-editor/data-blocks.js +117 -0
- package/dist/chart/chart-data.js +46 -44
- package/dist/command-menu/hotkey.js +58 -47
- package/dist/command-menu/recents.js +8 -11
- package/dist/command-menu/registry.d.ts +9 -5
- package/dist/command-menu/registry.js +36 -46
- package/dist/controller/calendar-dates.js +82 -113
- package/dist/controller/calendar-events.js +93 -88
- package/dist/controller/client-list-controller.d.ts +2 -2
- package/dist/controller/client-list-controller.js +251 -254
- package/dist/controller/data-table-columns.d.ts +2 -0
- package/dist/controller/data-table-columns.js +50 -56
- package/dist/controller/deep-equal.d.ts +1 -0
- package/dist/controller/deep-equal.js +14 -0
- package/dist/controller/filter-toggle.js +36 -44
- package/dist/controller/in-place-editor.d.ts +42 -0
- package/dist/controller/in-place-editor.js +42 -0
- package/dist/controller/infinite-list-controller.js +219 -189
- package/dist/controller/inspector.d.ts +49 -0
- package/dist/controller/inspector.js +64 -0
- package/dist/controller/job-monitor.d.ts +3 -4
- package/dist/controller/job-monitor.js +37 -47
- package/dist/controller/kanban-board.d.ts +2 -3
- package/dist/controller/kanban-board.js +43 -24
- package/dist/controller/list-controller.d.ts +5 -5
- package/dist/controller/list-controller.js +444 -400
- package/dist/controller/list-params.js +96 -87
- package/dist/controller/pivot-grid.js +89 -95
- package/dist/controller/prev-next.d.ts +2 -2
- package/dist/controller/prev-next.js +74 -54
- package/dist/controller/record-controllers.d.ts +14 -6
- package/dist/controller/record-controllers.js +288 -257
- package/dist/controller/record-diff.d.ts +21 -0
- package/dist/controller/record-diff.js +50 -0
- package/dist/controller/record-timeline.js +42 -30
- package/dist/controller/record-tree.js +57 -47
- package/dist/controller/record-workflow.d.ts +38 -16
- package/dist/controller/record-workflow.js +85 -64
- package/dist/controller/reference-many-controller.js +198 -192
- package/dist/controller/reference-one-controller.d.ts +1 -1
- package/dist/controller/reference-one-controller.js +63 -56
- package/dist/controller/saved-queries.js +104 -91
- package/dist/controller/translate-fallback.js +10 -6
- package/dist/csv/export.d.ts +2 -1
- package/dist/csv/export.js +43 -40
- package/dist/csv/import-controller.js +185 -169
- package/dist/csv/match-headers.js +29 -22
- package/dist/csv/parse-with.js +60 -54
- package/dist/csv/run-import.js +69 -72
- package/dist/csv/validate-rows.d.ts +1 -1
- package/dist/csv/validate-rows.js +65 -52
- package/dist/data/cache-ops.d.ts +5 -4
- package/dist/data/cache-ops.js +208 -188
- package/dist/data/combine-data-providers.js +25 -23
- package/dist/data/data-provider-log.d.ts +45 -0
- package/dist/data/data-provider-log.js +93 -0
- package/dist/data/errors.js +14 -10
- package/dist/data/fake-provider.d.ts +6 -3
- package/dist/data/fake-provider.js +129 -114
- package/dist/data/fetch-utils.js +61 -44
- package/dist/data/filter-branch.d.ts +5 -0
- package/dist/data/filter-branch.js +10 -0
- package/dist/data/get-many-aggregate.js +84 -76
- package/dist/data/http-error.js +17 -12
- package/dist/data/keys.js +50 -36
- package/dist/data/mutation-options.d.ts +14 -9
- package/dist/data/mutation-options.js +93 -109
- package/dist/data/query-options.js +73 -68
- package/dist/data/remove-empty.d.ts +3 -0
- package/dist/data/remove-empty.js +11 -0
- package/dist/data/undoable.d.ts +2 -2
- package/dist/data/undoable.js +190 -174
- package/dist/data/with-lifecycle-callbacks.d.ts +45 -17
- package/dist/data/with-lifecycle-callbacks.js +66 -52
- package/dist/environment/application-update-store.js +58 -55
- package/dist/environment/offline-store.js +21 -21
- package/dist/form/array-binding.d.ts +30 -11
- package/dist/form/array-binding.js +184 -166
- package/dist/form/choice-ids.d.ts +7 -0
- package/dist/form/choice-ids.js +11 -0
- package/dist/form/choices-controller.d.ts +3 -2
- package/dist/form/choices-controller.js +302 -275
- package/dist/form/date-format.d.ts +38 -0
- package/dist/form/date-format.js +89 -0
- package/dist/form/dirty-values.d.ts +2 -2
- package/dist/form/dirty-values.js +22 -11
- package/dist/form/duration.d.ts +22 -0
- package/dist/form/duration.js +49 -0
- package/dist/form/edit-create-form.d.ts +12 -5
- package/dist/form/edit-create-form.js +243 -188
- package/dist/form/event-value.d.ts +6 -0
- package/dist/form/event-value.js +17 -0
- package/dist/form/field-error.d.ts +14 -0
- package/dist/form/field-error.js +28 -0
- package/dist/form/field-name.d.ts +20 -0
- package/dist/form/field-name.js +6 -0
- package/dist/form/filter-form-values.js +63 -49
- package/dist/form/form-controller.d.ts +9 -1
- package/dist/form/form-controller.js +165 -160
- package/dist/form/form-groups.d.ts +4 -3
- package/dist/form/form-groups.js +79 -79
- package/dist/form/form-handle.d.ts +175 -0
- package/dist/form/form-handle.js +68 -0
- package/dist/form/input-binding.d.ts +56 -31
- package/dist/form/input-binding.js +169 -118
- package/dist/form/input-values.d.ts +130 -0
- package/dist/form/input-values.js +177 -0
- package/dist/form/json-text.js +51 -37
- package/dist/form/save-pipeline.d.ts +6 -8
- package/dist/form/save-pipeline.js +91 -82
- package/dist/form/set-form-values.d.ts +6 -0
- package/dist/form/set-form-values.js +10 -0
- package/dist/form/source.js +97 -65
- package/dist/form/text-of.d.ts +2 -0
- package/dist/form/text-of.js +7 -0
- package/dist/form/validators.d.ts +58 -19
- package/dist/form/validators.js +97 -71
- package/dist/geo/bbox.d.ts +5 -4
- package/dist/geo/bbox.js +69 -48
- package/dist/geo/bounds.d.ts +14 -3
- package/dist/geo/bounds.js +98 -68
- package/dist/geo/constraints.js +88 -93
- package/dist/geo/coordinates.js +31 -23
- package/dist/geo/editor.d.ts +13 -13
- package/dist/geo/editor.js +176 -153
- package/dist/geo/geocoding.d.ts +4 -4
- package/dist/geo/geocoding.js +38 -41
- package/dist/geo/geojson.d.ts +12 -74
- package/dist/geo/geojson.js +52 -40
- package/dist/geo/geometry-math.d.ts +4 -4
- package/dist/geo/geometry-math.js +100 -93
- package/dist/geo/index.d.ts +7 -6
- package/dist/geo/map-layers.d.ts +42 -7
- package/dist/geo/map-layers.js +88 -62
- package/dist/geo/map-style.js +31 -32
- package/dist/geo/messages.js +48 -45
- package/dist/geo/nominatim.js +88 -80
- package/dist/geo/osm-presets.d.ts +4 -3
- package/dist/geo/osm-presets.js +108 -86
- package/dist/geo/osm-tags.js +14 -12
- package/dist/geo/overpass.d.ts +14 -2
- package/dist/geo/overpass.js +83 -90
- package/dist/geo/shape-value.d.ts +9 -5
- package/dist/geo/shape-value.js +169 -146
- package/dist/geo/snap-to-roads.d.ts +3 -3
- package/dist/geo/snap-to-roads.js +55 -50
- package/dist/geo/throttle.js +36 -39
- package/dist/guards.d.ts +37 -0
- package/dist/guards.js +62 -0
- package/dist/i18n/default-messages.js +34 -29
- package/dist/i18n/english-messages.js +187 -189
- package/dist/i18n/i18n.d.ts +2 -2
- package/dist/i18n/i18n.js +47 -60
- package/dist/i18n/inflection.d.ts +34 -0
- package/dist/i18n/inflection.js +82 -0
- package/dist/i18n/messages.d.ts +10 -2
- package/dist/i18n/messages.js +17 -4
- package/dist/i18n/missing-keys.js +66 -62
- package/dist/i18n/translatable.d.ts +16 -1
- package/dist/i18n/translatable.js +40 -47
- package/dist/index.d.ts +71 -24
- package/dist/index.js +40 -288
- package/dist/json/deep-equal.d.ts +8 -0
- package/dist/json/deep-equal.js +24 -0
- package/dist/json/strip-json-comments.js +38 -42
- package/dist/noop.d.ts +2 -0
- package/dist/noop.js +4 -0
- package/dist/notification/error-message.d.ts +2 -0
- package/dist/notification/error-message.js +10 -0
- package/dist/notification/notification-store.d.ts +3 -2
- package/dist/notification/notification-store.js +41 -45
- package/dist/onboarding-tour/placement.js +63 -78
- package/dist/onboarding-tour/tour-controller.js +46 -54
- package/dist/realtime/add-events-for-mutations.js +75 -81
- package/dist/realtime/announce-lock.js +16 -13
- package/dist/realtime/broadcast-channel-transport.js +65 -59
- package/dist/realtime/copy-members.js +22 -14
- package/dist/realtime/in-memory-lock-provider.js +40 -48
- package/dist/realtime/live.js +95 -101
- package/dist/realtime/lock-options.js +16 -16
- package/dist/realtime/lock-session.js +91 -78
- package/dist/realtime/memory-transport.js +25 -25
- package/dist/realtime/presence.js +120 -122
- package/dist/realtime/realtime-data-provider.d.ts +10 -0
- package/dist/realtime/realtime-data-provider.js +45 -42
- package/dist/realtime/reconnect-backoff.js +17 -0
- package/dist/realtime/sse-transport.d.ts +31 -0
- package/dist/realtime/sse-transport.js +235 -0
- package/dist/realtime/status-store.js +23 -28
- package/dist/realtime/topic-hub.js +62 -71
- package/dist/realtime/topics.js +10 -12
- package/dist/realtime/types.js +10 -12
- package/dist/realtime/unread-count.js +21 -21
- package/dist/realtime/websocket-transport.d.ts +32 -0
- package/dist/realtime/websocket-transport.js +306 -0
- package/dist/resource/describe-resource.d.ts +2 -0
- package/dist/resource/describe-resource.js +232 -219
- package/dist/resource/get-at-path.js +29 -14
- package/dist/resource/identifier.d.ts +18 -0
- package/dist/resource/identifier.js +27 -0
- package/dist/resource/record.d.ts +4 -0
- package/dist/resource/record.js +10 -0
- package/dist/resource/register.d.ts +7 -0
- package/dist/resource/registry-store.d.ts +45 -15
- package/dist/resource/registry-store.js +67 -60
- package/dist/resource/registry.d.ts +10 -0
- package/dist/resource/registry.js +8 -4
- package/dist/resource/source-path.d.ts +16 -1
- package/dist/rich-text/rich-text-extras.d.ts +132 -0
- package/dist/rich-text/rich-text-extras.js +179 -0
- package/dist/rich-text/rich-text-image.d.ts +124 -0
- package/dist/rich-text/rich-text-image.js +163 -0
- package/dist/rich-text/rich-text.d.ts +6 -0
- package/dist/rich-text/rich-text.js +125 -88
- package/dist/router/location.d.ts +22 -4
- package/dist/router/location.js +41 -18
- package/dist/router/match.js +47 -39
- package/dist/router/paths.js +40 -34
- package/dist/router/record-from-location.d.ts +1 -1
- package/dist/router/record-from-location.js +25 -17
- package/dist/store/adapters.js +51 -55
- package/dist/store/micro-store.js +57 -59
- package/dist/store/shallow-equal.d.ts +12 -0
- package/dist/store/shallow-equal.js +20 -0
- package/dist/store/store.js +54 -57
- package/package.json +13 -11
|
@@ -1,266 +1,263 @@
|
|
|
1
|
+
import { isFilterBranch } from "../data/filter-branch.js";
|
|
2
|
+
import { removeEmpty } from "../data/remove-empty.js";
|
|
1
3
|
import { getAtPath } from "../resource/get-at-path.js";
|
|
2
4
|
import { createMicroStore } from "../store/micro-store.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
import { listParamsFromPatch, removeFilterPath } from "./list-controller.js";
|
|
6
|
+
// src/controller/client-list-controller.ts
|
|
7
|
+
var DEFAULT_PER_PAGE = 1e3;
|
|
8
|
+
var DEFAULT_SORT = [];
|
|
6
9
|
function normalizeSort(sort) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return entries.map((entry) => ({ ...entry }));
|
|
10
|
-
}
|
|
11
|
-
function removeEmpty(filter) {
|
|
12
|
-
const result = {};
|
|
13
|
-
for (const [key, value] of Object.entries(filter)) {
|
|
14
|
-
if (value === "" || value === null || value === void 0) continue;
|
|
15
|
-
result[key] = value;
|
|
16
|
-
}
|
|
17
|
-
return result;
|
|
10
|
+
if (!sort) return DEFAULT_SORT.map((entry) => ({ ...entry }));
|
|
11
|
+
return (Array.isArray(sort) ? sort : [sort]).map((entry) => ({ ...entry }));
|
|
18
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Flatten a nested filter object into dotted keys (`{nested:{level:5}}` →
|
|
15
|
+
* `{'nested.level':5}`), matching ra-core's `flattenObject`. Arrays and
|
|
16
|
+
* non-plain values are treated as LEAVES (never recursed).
|
|
17
|
+
*/
|
|
19
18
|
function flattenFilter(filter, prefix = "") {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return result;
|
|
19
|
+
const result = {};
|
|
20
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
21
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
22
|
+
if (isFilterBranch(value)) Object.assign(result, flattenFilter(value, path));
|
|
23
|
+
else result[path] = value;
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
30
26
|
}
|
|
27
|
+
/** Does one record satisfy every flattened filter entry (ra-core semantics)? */
|
|
31
28
|
function matchesFilter(record, flatFilter) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return Object.values(record).some(
|
|
43
|
-
(value) => typeof value === "string" && value.toLowerCase().includes(needle)
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
return filterValue == recordValue;
|
|
47
|
-
});
|
|
29
|
+
return Object.entries(flatFilter).every(([filterName, filterValue]) => {
|
|
30
|
+
const recordValue = getAtPath(record, filterName);
|
|
31
|
+
if (Array.isArray(recordValue)) return Array.isArray(filterValue) ? recordValue.some((item) => filterValue.includes(item)) : recordValue.includes(filterValue);
|
|
32
|
+
if (Array.isArray(filterValue)) return filterValue.includes(recordValue);
|
|
33
|
+
if (filterName === "q") {
|
|
34
|
+
const needle = String(filterValue).toLowerCase();
|
|
35
|
+
return Object.values(record).some((value) => typeof value === "string" && value.toLowerCase().includes(needle));
|
|
36
|
+
}
|
|
37
|
+
return filterValue == recordValue;
|
|
38
|
+
});
|
|
48
39
|
}
|
|
49
40
|
function applyFilterAndSort(records, filter, sort, filterCallback) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return 0;
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
return result;
|
|
41
|
+
let result = records;
|
|
42
|
+
const flatFilter = flattenFilter(filter);
|
|
43
|
+
if (Object.keys(flatFilter).length > 0) result = result.filter((record) => matchesFilter(record, flatFilter));
|
|
44
|
+
result = result.filter(filterCallback);
|
|
45
|
+
const keys = sort.filter((entry) => entry.field);
|
|
46
|
+
if (keys.length > 0) result = [...result].sort((a, b) => {
|
|
47
|
+
for (const { field, order } of keys) {
|
|
48
|
+
const asc = order === "ASC";
|
|
49
|
+
const av = getAtPath(a, field);
|
|
50
|
+
const bv = getAtPath(b, field);
|
|
51
|
+
if (av === bv || av == null && bv == null) continue;
|
|
52
|
+
if (av == null) return asc ? -1 : 1;
|
|
53
|
+
if (bv == null) return asc ? 1 : -1;
|
|
54
|
+
if (av > bv) return asc ? 1 : -1;
|
|
55
|
+
if (av < bv) return asc ? -1 : 1;
|
|
56
|
+
}
|
|
57
|
+
return 0;
|
|
58
|
+
});
|
|
59
|
+
return result;
|
|
73
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Vanilla client-side list controller. Pure state + closures over a
|
|
63
|
+
* {@link createMicroStore}; NO QueryObserver, NO subscriptions to acquire —
|
|
64
|
+
* every derived value is recomputed synchronously from the current params +
|
|
65
|
+
* source data. Selection is local (never KV-synced — an in-memory list has no
|
|
66
|
+
* cross-instance identity).
|
|
67
|
+
*
|
|
68
|
+
* Shape parity: it satisfies {@link ListController} so the React `useList` hook
|
|
69
|
+
* returns the SAME `UseListControllerResult` a `<ListContextProvider>` /
|
|
70
|
+
* `useListContext` consumer already reads.
|
|
71
|
+
*/
|
|
74
72
|
function createClientListController(options) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
73
|
+
const filterCallback = options.filterCallback ?? ((record) => Boolean(record));
|
|
74
|
+
let source = options.data;
|
|
75
|
+
const store = createMicroStore({
|
|
76
|
+
page: options.page ?? 1,
|
|
77
|
+
perPage: options.perPage ?? DEFAULT_PER_PAGE,
|
|
78
|
+
sort: normalizeSort(options.sort),
|
|
79
|
+
filter: removeEmpty(options.filter ?? {}),
|
|
80
|
+
displayedFilters: {},
|
|
81
|
+
selectedIds: [],
|
|
82
|
+
data: void 0,
|
|
83
|
+
total: void 0,
|
|
84
|
+
pageInfo: void 0,
|
|
85
|
+
isPending: options.isPending ?? false,
|
|
86
|
+
isFetching: options.isFetching ?? false,
|
|
87
|
+
error: options.error ?? null,
|
|
88
|
+
hasNextPage: void 0,
|
|
89
|
+
hasPreviousPage: void 0
|
|
90
|
+
});
|
|
91
|
+
let destroyed = false;
|
|
92
|
+
store.onDestroy(() => {
|
|
93
|
+
destroyed = true;
|
|
94
|
+
});
|
|
95
|
+
/** Recompute the derived slice (data/total/paging flags) from source + params. */
|
|
96
|
+
const recompute = () => {
|
|
97
|
+
const s = store.getSnapshot();
|
|
98
|
+
if (s.isPending || source === void 0) {
|
|
99
|
+
store.setState({
|
|
100
|
+
data: void 0,
|
|
101
|
+
total: void 0,
|
|
102
|
+
hasNextPage: false,
|
|
103
|
+
hasPreviousPage: false
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const filteredAndSorted = applyFilterAndSort(source, s.filter, s.sort, filterCallback);
|
|
108
|
+
const total = filteredAndSorted.length;
|
|
109
|
+
const lastPage = Math.max(Math.ceil(total / s.perPage), 1);
|
|
110
|
+
const page = Math.min(Math.max(s.page, 1), lastPage);
|
|
111
|
+
const start = (page - 1) * s.perPage;
|
|
112
|
+
const paginated = filteredAndSorted.slice(start, start + s.perPage);
|
|
113
|
+
store.setState({
|
|
114
|
+
page,
|
|
115
|
+
data: paginated,
|
|
116
|
+
total,
|
|
117
|
+
hasNextPage: page * s.perPage < total,
|
|
118
|
+
hasPreviousPage: page > 1
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
recompute();
|
|
122
|
+
const setPage = (page) => {
|
|
123
|
+
if (destroyed) return;
|
|
124
|
+
store.setState({ page });
|
|
125
|
+
recompute();
|
|
126
|
+
};
|
|
127
|
+
const setPerPage = (perPage) => {
|
|
128
|
+
if (destroyed) return;
|
|
129
|
+
store.setState({
|
|
130
|
+
perPage,
|
|
131
|
+
page: 1
|
|
132
|
+
});
|
|
133
|
+
recompute();
|
|
134
|
+
};
|
|
135
|
+
const setSort = (input) => {
|
|
136
|
+
if (destroyed) return;
|
|
137
|
+
const current = store.getSnapshot().sort;
|
|
138
|
+
const single = current.length === 1 ? current[0] : void 0;
|
|
139
|
+
let next;
|
|
140
|
+
if (Array.isArray(input)) next = input.map((entry) => ({ ...entry }));
|
|
141
|
+
else if (single && single.field === input.field) next = [{
|
|
142
|
+
field: input.field,
|
|
143
|
+
order: single.order === "ASC" ? "DESC" : "ASC"
|
|
144
|
+
}];
|
|
145
|
+
else next = [{
|
|
146
|
+
field: input.field,
|
|
147
|
+
order: input.order ?? "ASC"
|
|
148
|
+
}];
|
|
149
|
+
store.setState({
|
|
150
|
+
sort: next,
|
|
151
|
+
page: 1
|
|
152
|
+
});
|
|
153
|
+
recompute();
|
|
154
|
+
};
|
|
155
|
+
const setFilters = (filter, displayedFilters) => {
|
|
156
|
+
if (destroyed) return;
|
|
157
|
+
store.setState({
|
|
158
|
+
filter: removeEmpty(filter),
|
|
159
|
+
displayedFilters: displayedFilters === void 0 ? store.getSnapshot().displayedFilters : { ...displayedFilters },
|
|
160
|
+
page: 1
|
|
161
|
+
});
|
|
162
|
+
recompute();
|
|
163
|
+
};
|
|
164
|
+
const setParams = (patch) => {
|
|
165
|
+
if (destroyed) return;
|
|
166
|
+
const next = listParamsFromPatch(patch);
|
|
167
|
+
if (Object.keys(next).length === 0) return;
|
|
168
|
+
store.setState(next);
|
|
169
|
+
recompute();
|
|
170
|
+
};
|
|
171
|
+
const showFilter = (name, defaultValue) => {
|
|
172
|
+
if (destroyed) return;
|
|
173
|
+
const s = store.getSnapshot();
|
|
174
|
+
store.setState({
|
|
175
|
+
filter: defaultValue === void 0 ? s.filter : removeEmpty({
|
|
176
|
+
...s.filter,
|
|
177
|
+
[name]: defaultValue
|
|
178
|
+
}),
|
|
179
|
+
displayedFilters: {
|
|
180
|
+
...s.displayedFilters,
|
|
181
|
+
[name]: true
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
recompute();
|
|
185
|
+
};
|
|
186
|
+
const hideFilter = (name) => {
|
|
187
|
+
if (destroyed) return;
|
|
188
|
+
const s = store.getSnapshot();
|
|
189
|
+
const filter = removeFilterPath(s.filter, name);
|
|
190
|
+
const { [name]: _shown, ...displayedFilters } = s.displayedFilters;
|
|
191
|
+
store.setState({
|
|
192
|
+
filter,
|
|
193
|
+
displayedFilters,
|
|
194
|
+
page: 1
|
|
195
|
+
});
|
|
196
|
+
recompute();
|
|
197
|
+
};
|
|
198
|
+
const select = (ids) => {
|
|
199
|
+
if (destroyed) return;
|
|
200
|
+
store.setState({ selectedIds: [...ids] });
|
|
201
|
+
};
|
|
202
|
+
const toggleItem = (id) => {
|
|
203
|
+
if (destroyed) return;
|
|
204
|
+
const current = store.getSnapshot().selectedIds;
|
|
205
|
+
store.setState({ selectedIds: current.includes(id) ? current.filter((existing) => existing !== id) : [...current, id] });
|
|
206
|
+
};
|
|
207
|
+
const unselectAll = () => {
|
|
208
|
+
if (destroyed) return;
|
|
209
|
+
store.setState({ selectedIds: [] });
|
|
210
|
+
};
|
|
211
|
+
const selectAll = async (opts) => {
|
|
212
|
+
if (destroyed) return;
|
|
213
|
+
if (source === void 0) return;
|
|
214
|
+
const s = store.getSnapshot();
|
|
215
|
+
const filteredAndSorted = applyFilterAndSort(source, s.filter, s.sort, filterCallback);
|
|
216
|
+
const limited = opts?.limit != null ? filteredAndSorted.slice(0, opts.limit) : filteredAndSorted;
|
|
217
|
+
store.setState({ selectedIds: limited.map((record) => record.id) });
|
|
218
|
+
};
|
|
219
|
+
const refetch = () => {};
|
|
220
|
+
const setData = (data) => {
|
|
221
|
+
if (destroyed) return;
|
|
222
|
+
source = data;
|
|
223
|
+
recompute();
|
|
224
|
+
};
|
|
225
|
+
const setSourceFilter = (filter) => {
|
|
226
|
+
if (destroyed) return;
|
|
227
|
+
store.setState({
|
|
228
|
+
filter: removeEmpty(filter),
|
|
229
|
+
page: 1
|
|
230
|
+
});
|
|
231
|
+
recompute();
|
|
232
|
+
};
|
|
233
|
+
const setLoadingState = (patch) => {
|
|
234
|
+
if (destroyed) return;
|
|
235
|
+
const s = store.getSnapshot();
|
|
236
|
+
const next = {};
|
|
237
|
+
if (patch.isPending !== void 0 && patch.isPending !== s.isPending) next.isPending = patch.isPending;
|
|
238
|
+
if (patch.isFetching !== void 0 && patch.isFetching !== s.isFetching) next.isFetching = patch.isFetching;
|
|
239
|
+
if (patch.error !== void 0 && patch.error !== s.error) next.error = patch.error;
|
|
240
|
+
if (Object.keys(next).length === 0) return;
|
|
241
|
+
store.setState(next);
|
|
242
|
+
if (next.isPending !== void 0) recompute();
|
|
243
|
+
};
|
|
244
|
+
return {
|
|
245
|
+
...store,
|
|
246
|
+
setPage,
|
|
247
|
+
setPerPage,
|
|
248
|
+
setSort,
|
|
249
|
+
setFilters,
|
|
250
|
+
setParams,
|
|
251
|
+
showFilter,
|
|
252
|
+
hideFilter,
|
|
253
|
+
select,
|
|
254
|
+
toggleItem,
|
|
255
|
+
unselectAll,
|
|
256
|
+
selectAll,
|
|
257
|
+
refetch,
|
|
258
|
+
setData,
|
|
259
|
+
setSourceFilter,
|
|
260
|
+
setLoadingState
|
|
261
|
+
};
|
|
263
262
|
}
|
|
264
|
-
export {
|
|
265
|
-
createClientListController
|
|
266
|
-
};
|
|
263
|
+
export { createClientListController };
|
|
@@ -20,4 +20,6 @@ export declare function arrangeColumns(ids: readonly (string | undefined)[], { h
|
|
|
20
20
|
}): number[];
|
|
21
21
|
export declare function toggleHiddenColumn(hidden: readonly string[], id: string): string[];
|
|
22
22
|
export declare function moveColumn(order: readonly string[], ids: readonly (string | undefined)[], id: string, direction: -1 | 1): string[];
|
|
23
|
+
/** Moves `id` to `index` in the resolved order, clamping `index` to the ends. */
|
|
24
|
+
export declare function moveColumnTo(order: readonly string[], ids: readonly (string | undefined)[], id: string, index: number): string[];
|
|
23
25
|
export declare function columnLabelMatches(label: string, query: string): boolean;
|
|
@@ -1,73 +1,67 @@
|
|
|
1
|
+
// src/controller/data-table-columns.ts
|
|
1
2
|
function defaultDataTableStoreKey(resource) {
|
|
2
|
-
|
|
3
|
+
return `${resource}.datatable`;
|
|
3
4
|
}
|
|
4
5
|
function dataTableColumnKeys(storeKey) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
return {
|
|
7
|
+
hidden: storeKey,
|
|
8
|
+
order: `${storeKey}_columnOrder`,
|
|
9
|
+
columns: `${storeKey}_columns`
|
|
10
|
+
};
|
|
10
11
|
}
|
|
11
|
-
function getDataTableColumnId({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}) {
|
|
15
|
-
if (typeof source === "string" && source !== "") return source;
|
|
16
|
-
if (typeof label === "string" && label !== "") return label;
|
|
17
|
-
return void 0;
|
|
12
|
+
function getDataTableColumnId({ source, label }) {
|
|
13
|
+
if (typeof source === "string" && source !== "") return source;
|
|
14
|
+
if (typeof label === "string" && label !== "") return label;
|
|
18
15
|
}
|
|
16
|
+
/** The distinct ids in display order: the stored order first, then the rest as declared. */
|
|
19
17
|
function resolveOrder(ids, order) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
const present = /* @__PURE__ */ new Set();
|
|
19
|
+
for (const id of ids) if (id !== void 0) present.add(id);
|
|
20
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
21
|
+
for (const id of order) if (present.has(id)) resolved.add(id);
|
|
22
|
+
for (const id of present) resolved.add(id);
|
|
23
|
+
return [...resolved];
|
|
26
24
|
}
|
|
27
25
|
function arrangeColumns(ids, { hidden, order }) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
);
|
|
42
|
-
return arranged.filter((index) => {
|
|
43
|
-
const id = ids[index];
|
|
44
|
-
return id === void 0 || !hidden.includes(id);
|
|
45
|
-
});
|
|
26
|
+
const indexesById = /* @__PURE__ */ new Map();
|
|
27
|
+
ids.forEach((id, index) => {
|
|
28
|
+
if (id === void 0) return;
|
|
29
|
+
const indexes = indexesById.get(id);
|
|
30
|
+
if (indexes) indexes.push(index);
|
|
31
|
+
else indexesById.set(id, [index]);
|
|
32
|
+
});
|
|
33
|
+
const sequence = resolveOrder(ids, order).flatMap((id) => indexesById.get(id) ?? []);
|
|
34
|
+
let next = 0;
|
|
35
|
+
return ids.map((id, index) => id === void 0 ? index : sequence[next++] ?? index).filter((index) => {
|
|
36
|
+
const id = ids[index];
|
|
37
|
+
return id === void 0 || !hidden.includes(id);
|
|
38
|
+
});
|
|
46
39
|
}
|
|
47
40
|
function toggleHiddenColumn(hidden, id) {
|
|
48
|
-
|
|
41
|
+
return hidden.includes(id) ? hidden.filter((entry) => entry !== id) : [...hidden, id];
|
|
49
42
|
}
|
|
50
43
|
function moveColumn(order, ids, id, direction) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
44
|
+
const resolved = resolveOrder(ids, order);
|
|
45
|
+
const from = resolved.indexOf(id);
|
|
46
|
+
const to = from + direction;
|
|
47
|
+
if (from < 0 || to < 0 || to >= resolved.length) return resolved;
|
|
48
|
+
resolved.splice(to, 0, ...resolved.splice(from, 1));
|
|
49
|
+
return resolved;
|
|
50
|
+
}
|
|
51
|
+
/** Moves `id` to `index` in the resolved order, clamping `index` to the ends. */
|
|
52
|
+
function moveColumnTo(order, ids, id, index) {
|
|
53
|
+
const resolved = resolveOrder(ids, order);
|
|
54
|
+
const from = resolved.indexOf(id);
|
|
55
|
+
if (from < 0) return resolved;
|
|
56
|
+
const to = Math.min(Math.max(index, 0), resolved.length - 1);
|
|
57
|
+
resolved.splice(to, 0, ...resolved.splice(from, 1));
|
|
58
|
+
return resolved;
|
|
57
59
|
}
|
|
58
60
|
function fold(text) {
|
|
59
|
-
|
|
61
|
+
return text.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
|
|
60
62
|
}
|
|
61
63
|
function columnLabelMatches(label, query) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
if (query === "") return true;
|
|
65
|
+
return fold(label).includes(fold(query));
|
|
64
66
|
}
|
|
65
|
-
export {
|
|
66
|
-
arrangeColumns,
|
|
67
|
-
columnLabelMatches,
|
|
68
|
-
dataTableColumnKeys,
|
|
69
|
-
defaultDataTableStoreKey,
|
|
70
|
-
getDataTableColumnId,
|
|
71
|
-
moveColumn,
|
|
72
|
-
toggleHiddenColumn
|
|
73
|
-
};
|
|
67
|
+
export { arrangeColumns, columnLabelMatches, dataTableColumnKeys, defaultDataTableStoreKey, getDataTableColumnId, moveColumn, moveColumnTo, toggleHiddenColumn };
|