@chelone/core 0.5.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 +28 -0
- package/dist/assistant/assistant-session.js +208 -0
- package/dist/assistant/echo-transport.d.ts +7 -0
- package/dist/assistant/echo-transport.js +28 -0
- package/dist/assistant/http-transport.d.ts +10 -0
- package/dist/assistant/http-transport.js +98 -0
- package/dist/assistant/types.d.ts +54 -0
- 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.d.ts +25 -0
- package/dist/chart/chart-data.js +61 -0
- 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 +220 -190
- package/dist/controller/inspector.d.ts +49 -0
- package/dist/controller/inspector.js +64 -0
- package/dist/controller/job-monitor.d.ts +41 -0
- package/dist/controller/job-monitor.js +49 -0
- 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.d.ts +36 -0
- package/dist/controller/pivot-grid.js +99 -0
- 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.d.ts +35 -0
- package/dist/controller/record-timeline.js +48 -0
- 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 -190
- 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 +8 -2
- package/dist/i18n/i18n.js +47 -48
- 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.d.ts +19 -0
- package/dist/i18n/missing-keys.js +68 -0
- package/dist/i18n/translatable.d.ts +16 -1
- package/dist/i18n/translatable.js +40 -47
- package/dist/index.d.ts +96 -22
- package/dist/index.js +52 -257
- 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.d.ts +34 -0
- package/dist/onboarding-tour/placement.js +67 -0
- package/dist/onboarding-tour/tour-controller.d.ts +25 -0
- package/dist/onboarding-tour/tour-controller.js +50 -0
- package/dist/realtime/add-events-for-mutations.js +75 -81
- package/dist/realtime/announce-lock.d.ts +7 -0
- package/dist/realtime/announce-lock.js +18 -0
- package/dist/realtime/broadcast-channel-transport.d.ts +14 -0
- package/dist/realtime/broadcast-channel-transport.js +69 -0
- package/dist/realtime/copy-members.js +22 -14
- package/dist/realtime/in-memory-lock-provider.js +41 -60
- 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 +26 -67
- 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.d.ts +15 -0
- 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.d.ts +24 -0
- package/dist/realtime/topic-hub.js +66 -0
- 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 +62 -0
- package/dist/rich-text/rich-text.js +142 -0
- 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
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// src/form/date-format.ts
|
|
2
|
+
var leftPad2 = (value) => `00${value}`.slice(-2);
|
|
3
|
+
var leftPad4 = (value) => `0000${value}`.slice(-4);
|
|
4
|
+
var dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
5
|
+
/** Convert a `Date` to a local "YYYY-MM-DD" string, or "" for an invalid date. */
|
|
6
|
+
function dateToYmd(value) {
|
|
7
|
+
if (!(value instanceof Date) || Number.isNaN(value.getDate())) return "";
|
|
8
|
+
return `${value.getFullYear().toString()}-${leftPad2(value.getMonth() + 1)}-${leftPad2(value.getDate())}`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Format a form value (string / ISO / `Date` / timestamp / "") to a bare
|
|
12
|
+
* "YYYY-MM-DD" string for `<input type="date">`. Returns `null` for nullish/empty.
|
|
13
|
+
*/
|
|
14
|
+
function formatDate(value) {
|
|
15
|
+
if (value == null || value === "") return null;
|
|
16
|
+
if (value instanceof Date) return dateToYmd(value);
|
|
17
|
+
if (typeof value === "string" && dateRegex.test(value)) return value;
|
|
18
|
+
return dateToYmd(new Date(value));
|
|
19
|
+
}
|
|
20
|
+
/** Whether a raw `<input type="date">` string is empty or a valid date. */
|
|
21
|
+
function isValidDate(raw) {
|
|
22
|
+
if (raw === "") return true;
|
|
23
|
+
return dateRegex.test(raw) && !Number.isNaN(new Date(raw).getTime());
|
|
24
|
+
}
|
|
25
|
+
var dateTimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/;
|
|
26
|
+
/** Convert a `Date` to a local "yyyy-MM-ddThh:mm" string, or "" if invalid. */
|
|
27
|
+
function dateToLocalDateTime(value) {
|
|
28
|
+
if (!(value instanceof Date) || Number.isNaN(value.getDate())) return "";
|
|
29
|
+
return `${leftPad4(value.getFullYear())}-${leftPad2(value.getMonth() + 1)}-${leftPad2(value.getDate())}T${leftPad2(value.getHours())}:${leftPad2(value.getMinutes())}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Format a form value (ISO string / `Date` / timestamp) to the local
|
|
33
|
+
* `datetime-local` display string. Empty → "" (keeps the input controlled).
|
|
34
|
+
*/
|
|
35
|
+
function formatDateTime(value) {
|
|
36
|
+
if (value == null || value === "") return "";
|
|
37
|
+
if (value instanceof Date) return dateToLocalDateTime(value);
|
|
38
|
+
if (typeof value === "string" && dateTimeRegex.test(value)) return value;
|
|
39
|
+
return dateToLocalDateTime(new Date(value));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Parse a local `datetime-local` string back to an ISO 8601 string (browser
|
|
43
|
+
* timezone). Returns `null` for empty/unparseable so a required validator fires.
|
|
44
|
+
*/
|
|
45
|
+
function parseDateTimeToISO(value) {
|
|
46
|
+
if (value === "") return null;
|
|
47
|
+
const date = new Date(value);
|
|
48
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
49
|
+
}
|
|
50
|
+
/** Whether a raw `datetime-local` string is empty or parses to a valid date. */
|
|
51
|
+
function isValidDateTime(raw) {
|
|
52
|
+
if (raw === "") return true;
|
|
53
|
+
return !Number.isNaN(new Date(raw).getTime());
|
|
54
|
+
}
|
|
55
|
+
var timeRegex = /^\d{2}:\d{2}(:\d{2})?$/;
|
|
56
|
+
/** Convert a `Date` to a local "HH:mm" string, or "" if invalid. */
|
|
57
|
+
function dateToHm(value) {
|
|
58
|
+
if (!(value instanceof Date) || Number.isNaN(value.getDate())) return "";
|
|
59
|
+
return `${leftPad2(value.getHours())}:${leftPad2(value.getMinutes())}`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Format a form value (`Date` / "HH:mm" string) to the "HH:mm" display string.
|
|
63
|
+
* Empty → "" (keeps the input controlled).
|
|
64
|
+
*/
|
|
65
|
+
function formatTime(value) {
|
|
66
|
+
if (value == null || value === "") return "";
|
|
67
|
+
if (value instanceof Date) return dateToHm(value);
|
|
68
|
+
if (typeof value === "string" && timeRegex.test(value)) return value;
|
|
69
|
+
return dateToHm(new Date(value));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Parse a "HH:mm"/"HH:mm:ss" string to a `Date` (today at that local time), for
|
|
73
|
+
* dataProvider compatibility. Returns `null` for empty/unparseable so a required
|
|
74
|
+
* validator still fires.
|
|
75
|
+
*/
|
|
76
|
+
function parseTimeToDate(value) {
|
|
77
|
+
if (timeRegex.test(value)) {
|
|
78
|
+
const [hh = "0", mm = "0", ss = "0"] = value.split(":");
|
|
79
|
+
const d = /* @__PURE__ */ new Date();
|
|
80
|
+
d.setHours(Number.parseInt(hh, 10), Number.parseInt(mm, 10), Number.parseInt(ss, 10), 0);
|
|
81
|
+
return d;
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/** Whether a raw `<input type="time">` string is empty or a valid time. */
|
|
86
|
+
function isValidTime(raw) {
|
|
87
|
+
return raw === "" || timeRegex.test(raw);
|
|
88
|
+
}
|
|
89
|
+
export { formatDate, formatDateTime, formatTime, isValidDate, isValidDateTime, isValidTime, parseDateTimeToISO, parseTimeToDate };
|
|
@@ -9,6 +9,6 @@
|
|
|
9
9
|
* `address.city` yields `address`. A field never touched, or only registered,
|
|
10
10
|
* is left out.
|
|
11
11
|
*/
|
|
12
|
-
export declare function getDirtyFormValues(values:
|
|
12
|
+
export declare function getDirtyFormValues<TValues extends object>(values: TValues, fieldMeta: Record<string, {
|
|
13
13
|
isDirty: boolean;
|
|
14
|
-
} | undefined>):
|
|
14
|
+
} | undefined>): Partial<TValues>;
|
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
// src/form/dirty-values.ts
|
|
2
|
+
/**
|
|
3
|
+
* Reads the touched fields off a form-core form, so both adapters report the
|
|
4
|
+
* same diff. form-core has no `dirtyFields` twin: its `fieldMeta` is keyed by
|
|
5
|
+
* leaf path and each entry carries a sticky `isDirty`.
|
|
6
|
+
*/
|
|
7
|
+
/** `address.city` and `tags[0]` both belong to the top-level field. */
|
|
8
|
+
var topLevelName = (path) => path.split(/[.[]/, 1)[0] ?? path;
|
|
9
|
+
/**
|
|
10
|
+
* Returns `{ [topLevelName]: values[topLevelName] }` for every field path whose
|
|
11
|
+
* meta is dirty. A dirty nested path yields the whole top-level value, so
|
|
12
|
+
* `address.city` yields `address`. A field never touched, or only registered,
|
|
13
|
+
* is left out.
|
|
14
|
+
*/
|
|
2
15
|
function getDirtyFormValues(values, fieldMeta) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
16
|
+
const dirty = {};
|
|
17
|
+
for (const [path, meta] of Object.entries(fieldMeta)) {
|
|
18
|
+
if (!meta?.isDirty) continue;
|
|
19
|
+
const name = topLevelName(path);
|
|
20
|
+
dirty[name] = values[name];
|
|
21
|
+
}
|
|
22
|
+
return dirty;
|
|
10
23
|
}
|
|
11
|
-
export {
|
|
12
|
-
getDirtyFormValues
|
|
13
|
-
};
|
|
24
|
+
export { getDirtyFormValues };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface ParsedDuration {
|
|
2
|
+
days?: number;
|
|
3
|
+
hours?: number;
|
|
4
|
+
minutes?: number;
|
|
5
|
+
seconds?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Parses an ISO-8601 duration string (e.g. `PT2H30M`, `P1DT4H`) into a
|
|
9
|
+
* `ParsedDuration` object. Returns `null` for unparseable input.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseIsoDuration(s: string): ParsedDuration | null;
|
|
12
|
+
/** Renders a `ParsedDuration` in compact human-readable form, e.g. `2h 30m`. */
|
|
13
|
+
export declare function compactDuration(p: ParsedDuration): string;
|
|
14
|
+
/**
|
|
15
|
+
* Renders a `ParsedDuration` in relative long-form, e.g. `2 hours 30 minutes`.
|
|
16
|
+
*
|
|
17
|
+
* Clean-room reimplementation of date-fns's `formatDuration` (the only shape
|
|
18
|
+
* `<DurationField displayFormat="relative">` needs), so chelone avoids a
|
|
19
|
+
* date-fns dependency for a single formatter. Emits English unit words with
|
|
20
|
+
* the usual singular/plural rule, space-joined in largest-to-smallest order.
|
|
21
|
+
*/
|
|
22
|
+
export declare function relativeDuration(p: ParsedDuration): string;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// src/form/duration.ts
|
|
2
|
+
var ISO_RE = /^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/;
|
|
3
|
+
/**
|
|
4
|
+
* Parses an ISO-8601 duration string (e.g. `PT2H30M`, `P1DT4H`) into a
|
|
5
|
+
* `ParsedDuration` object. Returns `null` for unparseable input.
|
|
6
|
+
*/
|
|
7
|
+
function parseIsoDuration(s) {
|
|
8
|
+
const m = s.match(ISO_RE);
|
|
9
|
+
if (!m) return null;
|
|
10
|
+
const [, d, h, mn, sc] = m;
|
|
11
|
+
const out = {};
|
|
12
|
+
if (d) out.days = +d;
|
|
13
|
+
if (h) out.hours = +h;
|
|
14
|
+
if (mn) out.minutes = +mn;
|
|
15
|
+
if (sc) out.seconds = +sc;
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
/** Renders a `ParsedDuration` in compact human-readable form, e.g. `2h 30m`. */
|
|
19
|
+
function compactDuration(p) {
|
|
20
|
+
const parts = [];
|
|
21
|
+
if (p.days) parts.push(`${p.days}d`);
|
|
22
|
+
if (p.hours) parts.push(`${p.hours}h`);
|
|
23
|
+
if (p.minutes) parts.push(`${p.minutes}m`);
|
|
24
|
+
if (p.seconds) parts.push(`${p.seconds}s`);
|
|
25
|
+
return parts.join(" ") || "0m";
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Renders a `ParsedDuration` in relative long-form, e.g. `2 hours 30 minutes`.
|
|
29
|
+
*
|
|
30
|
+
* Clean-room reimplementation of date-fns's `formatDuration` (the only shape
|
|
31
|
+
* `<DurationField displayFormat="relative">` needs), so chelone avoids a
|
|
32
|
+
* date-fns dependency for a single formatter. Emits English unit words with
|
|
33
|
+
* the usual singular/plural rule, space-joined in largest-to-smallest order.
|
|
34
|
+
*/
|
|
35
|
+
function relativeDuration(p) {
|
|
36
|
+
const units = [
|
|
37
|
+
["days", "day"],
|
|
38
|
+
["hours", "hour"],
|
|
39
|
+
["minutes", "minute"],
|
|
40
|
+
["seconds", "second"]
|
|
41
|
+
];
|
|
42
|
+
const parts = [];
|
|
43
|
+
for (const [key, word] of units) {
|
|
44
|
+
const n = p[key];
|
|
45
|
+
if (n) parts.push(`${n} ${word}${n === 1 ? "" : "s"}`);
|
|
46
|
+
}
|
|
47
|
+
return parts.join(" ") || "0 seconds";
|
|
48
|
+
}
|
|
49
|
+
export { compactDuration, parseIsoDuration, relativeDuration };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { type CreateController, type CreateControllerState, type EditController, type EditControllerState, type RecordControllerContext, type RedirectRequest, type SaveTransform } from "../controller/record-controllers.js";
|
|
1
|
+
import { type CreateController, type CreateControllerState, type EditController, type EditControllerState, type RecordControllerContext, type RedirectRequest, type SaveOptions, type SaveTransform } from "../controller/record-controllers.js";
|
|
2
2
|
import type { MutationMode } from "../data/mutation-options.js";
|
|
3
3
|
import type { Identifier } from "../resource/identifier.js";
|
|
4
4
|
import type { RecordOf, ResourceMap } from "../resource/registry.js";
|
|
5
5
|
import { type CheloneFormApi, type CheloneFormState, type FormControllerOptions } from "./form-controller.js";
|
|
6
|
+
import { type ResourceFormHandle } from "./form-handle.js";
|
|
6
7
|
/**
|
|
7
8
|
* What the composed form controllers need: the P4 record-controller services
|
|
8
9
|
* plus (optionally) the resource registry — a resource's zod schema becomes
|
|
@@ -65,20 +66,26 @@ export interface CreateFormControllerState<TRecord> extends CreateControllerStat
|
|
|
65
66
|
* unsubscribe releases both. `getSnapshot` composes the two reference-stable
|
|
66
67
|
* sub-snapshots with identity caching (uSES-safe).
|
|
67
68
|
*/
|
|
68
|
-
export interface EditFormController<TRecord> {
|
|
69
|
+
export interface EditFormController<TRecord, TName extends string = string> {
|
|
69
70
|
form: CheloneFormApi<Partial<TRecord>>;
|
|
71
|
+
/** The form as a handle tagged with the resource it edits. */
|
|
72
|
+
handle: ResourceFormHandle<TName, Partial<TRecord>>;
|
|
70
73
|
/** The composed P4 edit controller — imperative `save` with call-time options lives here. */
|
|
71
74
|
recordController: EditController<TRecord>;
|
|
72
|
-
|
|
75
|
+
/** Validates, persists, reconciles. `options` apply to this one save, as `recordController.save`'s do. */
|
|
76
|
+
submit(options?: SaveOptions<TRecord>): Promise<void>;
|
|
73
77
|
subscribe(listener: () => void): () => void;
|
|
74
78
|
getSnapshot(): EditFormControllerState<TRecord>;
|
|
75
79
|
destroy(): void;
|
|
76
80
|
}
|
|
77
|
-
export interface CreateFormController<TRecord> {
|
|
81
|
+
export interface CreateFormController<TRecord, TName extends string = string> {
|
|
78
82
|
form: CheloneFormApi<Partial<TRecord>>;
|
|
83
|
+
/** The form as a handle tagged with the resource it creates. */
|
|
84
|
+
handle: ResourceFormHandle<TName, Partial<TRecord>>;
|
|
79
85
|
/** The composed P4 create controller. */
|
|
80
86
|
recordController: CreateController<TRecord>;
|
|
81
|
-
|
|
87
|
+
/** Validates, persists, reconciles. `options` apply to this one save, as `recordController.save`'s do. */
|
|
88
|
+
submit(options?: SaveOptions<TRecord>): Promise<void>;
|
|
82
89
|
subscribe(listener: () => void): () => void;
|
|
83
90
|
getSnapshot(): CreateFormControllerState<TRecord>;
|
|
84
91
|
destroy(): void;
|