@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
|
@@ -1,125 +1,176 @@
|
|
|
1
|
+
import { isThenable } from "../guards.js";
|
|
2
|
+
import { toFieldName } from "./field-name.js";
|
|
3
|
+
import { composeValidators, isRequired } from "./validators.js";
|
|
1
4
|
import { FieldApi } from "@tanstack/form-core";
|
|
2
|
-
|
|
3
|
-
|
|
5
|
+
// src/form/input-binding.ts
|
|
6
|
+
/**
|
|
7
|
+
* ra's default format: render `null`/`undefined` as `''` so text inputs stay
|
|
8
|
+
* controlled; every other value (including `0`/`false`) passes through.
|
|
9
|
+
*/
|
|
4
10
|
function defaultFormat(value) {
|
|
5
|
-
|
|
11
|
+
return value == null ? "" : value;
|
|
6
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* ra's default parse: store a cleared text input (`''`) as `null` — the
|
|
15
|
+
* shape most backends expect; every other value (including `0`/`false`)
|
|
16
|
+
* passes through.
|
|
17
|
+
*/
|
|
7
18
|
function defaultParse(input) {
|
|
8
|
-
|
|
19
|
+
return input === "" ? null : input;
|
|
20
|
+
}
|
|
21
|
+
/** A validator list (ra call style) rather than a single validator. */
|
|
22
|
+
function isValidatorList(validate) {
|
|
23
|
+
return Array.isArray(validate);
|
|
9
24
|
}
|
|
10
25
|
function createFieldBinding(form, options) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
26
|
+
const { source } = options;
|
|
27
|
+
const name = toFieldName(source);
|
|
28
|
+
let current = options;
|
|
29
|
+
/**
|
|
30
|
+
* Runs the binding's validator stack against a value: field schema first
|
|
31
|
+
* (its issues short-circuit the fn validators), then the composed ra-style
|
|
32
|
+
* validators with `(value, allValues, meta)`. Synchronous while every
|
|
33
|
+
* consulted step is synchronous.
|
|
34
|
+
*/
|
|
35
|
+
const runValidators = (value) => {
|
|
36
|
+
const runFns = () => {
|
|
37
|
+
const { validate } = current;
|
|
38
|
+
if (!validate) return void 0;
|
|
39
|
+
const composed = isValidatorList(validate) ? composeValidators(validate) : validate;
|
|
40
|
+
const meta = {
|
|
41
|
+
source,
|
|
42
|
+
...current.validatorMeta
|
|
43
|
+
};
|
|
44
|
+
return composed(value, form.store.state.values, meta);
|
|
45
|
+
};
|
|
46
|
+
const { schema } = current;
|
|
47
|
+
if (schema) {
|
|
48
|
+
const outcome = schema["~standard"].validate(value);
|
|
49
|
+
if (isAsyncSchemaOutcome(outcome)) return Promise.resolve(outcome).then((resolved) => {
|
|
50
|
+
const issues = resolved.issues;
|
|
51
|
+
return issues && issues.length > 0 ? issues : runFns();
|
|
52
|
+
});
|
|
53
|
+
const issues = outcome.issues;
|
|
54
|
+
if (issues && issues.length > 0) return issues;
|
|
55
|
+
}
|
|
56
|
+
return runFns();
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* True once a validation run returned a thenable. From then on the sync slot
|
|
60
|
+
* stands down and the async slot (lazily injected below) owns the stack —
|
|
61
|
+
* awaited results land under the same `onDynamic` errorMap key.
|
|
62
|
+
* Deliberately sticky: un-latching on option updates would re-probe (and
|
|
63
|
+
* double-run) async validators on every render.
|
|
64
|
+
*/
|
|
65
|
+
let asyncLatched = false;
|
|
66
|
+
/** The first thenable, kept so the async slot consumes it instead of re-running the stack. */
|
|
67
|
+
let pendingRun;
|
|
68
|
+
const onDynamicAsync = async ({ value }) => {
|
|
69
|
+
const held = pendingRun;
|
|
70
|
+
pendingRun = void 0;
|
|
71
|
+
return await (held && Object.is(held.value, value) ? held.result : runValidators(value)) ?? void 0;
|
|
72
|
+
};
|
|
73
|
+
const bindingValidators = { onDynamic: ({ value }) => {
|
|
74
|
+
if (asyncLatched) return void 0;
|
|
75
|
+
const result = runValidators(value);
|
|
76
|
+
if (isThenable(result)) {
|
|
77
|
+
asyncLatched = true;
|
|
78
|
+
pendingRun = {
|
|
79
|
+
value,
|
|
80
|
+
result
|
|
81
|
+
};
|
|
82
|
+
bindingValidators.onDynamicAsync = onDynamicAsync;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
return result ?? void 0;
|
|
86
|
+
} };
|
|
87
|
+
/** A value for this path, spelled as form-core's `DeepValue` for the write. */
|
|
88
|
+
const toFieldData = (value) => value;
|
|
89
|
+
/**
|
|
90
|
+
* The FieldApi options for the CURRENT binding options and form values.
|
|
91
|
+
* `defaultValue` carries ra precedence, not form-core's: form-core seeds a
|
|
92
|
+
* field-level default over any untouched form value (probed on 1.33 —
|
|
93
|
+
* mount and update both overwrite), so the default is passed through ONLY
|
|
94
|
+
* while the slot is a hole (`undefined`). Re-evaluated on every attach and
|
|
95
|
+
* update, which is also what re-seeds a reopened hole after a reset.
|
|
96
|
+
*/
|
|
97
|
+
const buildOptions = () => ({
|
|
98
|
+
form,
|
|
99
|
+
name,
|
|
100
|
+
defaultValue: current.defaultValue !== void 0 && form.getFieldValue(name) === void 0 ? toFieldData(current.defaultValue) : void 0,
|
|
101
|
+
validators: bindingValidators
|
|
102
|
+
});
|
|
103
|
+
const field = new FieldApi(buildOptions());
|
|
104
|
+
let destroyed = false;
|
|
105
|
+
/** Non-undefined exactly while attached (mount cleanup). */
|
|
106
|
+
let unmountField;
|
|
107
|
+
const subscriptions = /* @__PURE__ */ new Set();
|
|
108
|
+
/** Runs the mount cleanup at most once per attach cycle. */
|
|
109
|
+
const detach = () => {
|
|
110
|
+
const unmount = unmountField;
|
|
111
|
+
unmountField = void 0;
|
|
112
|
+
unmount?.();
|
|
113
|
+
};
|
|
114
|
+
/** The binding's `format`, or the ra default, applied to the stored value. */
|
|
115
|
+
const formatValue = () => {
|
|
116
|
+
const value = field.store.state.value;
|
|
117
|
+
const { format } = current;
|
|
118
|
+
if (format !== void 0) return format(value);
|
|
119
|
+
return defaultFormat(value);
|
|
120
|
+
};
|
|
121
|
+
/** The binding's `parse`, or the ra default, applied to the input. */
|
|
122
|
+
const parseInput = (input) => {
|
|
123
|
+
const { parse } = current;
|
|
124
|
+
return toFieldData(parse ? parse(input) : defaultParse(input));
|
|
125
|
+
};
|
|
126
|
+
return {
|
|
127
|
+
field,
|
|
128
|
+
source,
|
|
129
|
+
subscribe(listener) {
|
|
130
|
+
if (destroyed) return () => {};
|
|
131
|
+
const storeSubscription = field.store.subscribe(listener);
|
|
132
|
+
const subscription = { off: () => storeSubscription.unsubscribe() };
|
|
133
|
+
const isFirst = subscriptions.size === 0;
|
|
134
|
+
subscriptions.add(subscription);
|
|
135
|
+
if (isFirst) {
|
|
136
|
+
field.update(buildOptions());
|
|
137
|
+
unmountField = field.mount();
|
|
138
|
+
}
|
|
139
|
+
return () => {
|
|
140
|
+
if (!subscriptions.delete(subscription)) return;
|
|
141
|
+
subscription.off();
|
|
142
|
+
if (subscriptions.size === 0 && !destroyed) detach();
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
getSnapshot: () => field.store.state,
|
|
146
|
+
store: field.store,
|
|
147
|
+
getValue: formatValue,
|
|
148
|
+
setValue: (input) => field.handleChange(parseInput(input)),
|
|
149
|
+
blur: () => field.handleBlur(),
|
|
150
|
+
isRequired: () => isRequired(current.validate),
|
|
151
|
+
update(next) {
|
|
152
|
+
current = {
|
|
153
|
+
source,
|
|
154
|
+
...next
|
|
155
|
+
};
|
|
156
|
+
if (destroyed || unmountField === void 0) return;
|
|
157
|
+
field.update(buildOptions());
|
|
158
|
+
},
|
|
159
|
+
destroy() {
|
|
160
|
+
if (destroyed) return;
|
|
161
|
+
destroyed = true;
|
|
162
|
+
detach();
|
|
163
|
+
for (const subscription of subscriptions) subscription.off();
|
|
164
|
+
subscriptions.clear();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* A schema outcome that is a thenable of an outcome. `isThenable` already
|
|
170
|
+
* answers the question for any thenable; the predicate keeps the resolved
|
|
171
|
+
* value typed so the async branch reads `issues` off it.
|
|
172
|
+
*/
|
|
173
|
+
function isAsyncSchemaOutcome(outcome) {
|
|
174
|
+
return isThenable(outcome);
|
|
120
175
|
}
|
|
121
|
-
export {
|
|
122
|
-
createFieldBinding,
|
|
123
|
-
defaultFormat,
|
|
124
|
-
defaultParse
|
|
125
|
-
};
|
|
176
|
+
export { createFieldBinding, defaultFormat, defaultParse };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The value each input family edits, and one guard per family that reads it out
|
|
3
|
+
* of form state.
|
|
4
|
+
*
|
|
5
|
+
* The guards are total — every family has a value for every input, so an input
|
|
6
|
+
* never has to cast its own value. They convert, not validate: a value the
|
|
7
|
+
* family cannot hold lands on that family's empty value.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* A value read out of form state: whatever the data provider put there, until a
|
|
11
|
+
* guard names the family an input edits.
|
|
12
|
+
*/
|
|
13
|
+
export type FormValue = unknown;
|
|
14
|
+
/** What a date input edits: the `YYYY-MM-DD`/ISO string, the epoch number, a live `Date`, or nothing. */
|
|
15
|
+
export type DateInputValue = string | number | Date | null;
|
|
16
|
+
/** What a file input holds: a picked `File`, the stored reference (a URL) of the file on the record, or nothing. */
|
|
17
|
+
export type FileInputValue = File | string | null;
|
|
18
|
+
/** The storage shape a `<WebhookEndpointInput>` edits. */
|
|
19
|
+
export interface WebhookValue {
|
|
20
|
+
url: string;
|
|
21
|
+
secret: string;
|
|
22
|
+
eventTypes: string[];
|
|
23
|
+
lastDelivery?: {
|
|
24
|
+
status: "ok" | "failed" | "pending";
|
|
25
|
+
at: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The value a choice list stores for one choice: ra's `choices` carry an `id`
|
|
30
|
+
* and the form holds that id, while a plain list of scalars holds the scalar.
|
|
31
|
+
*/
|
|
32
|
+
export type ChoiceValueOf<TChoice> = TChoice extends {
|
|
33
|
+
id: infer TId;
|
|
34
|
+
} ? TId : TChoice;
|
|
35
|
+
/**
|
|
36
|
+
* The value a choice list stores for one choice: its `id` when it has one (ra's
|
|
37
|
+
* `choices` carry `{id, name}` pairs), the choice itself otherwise.
|
|
38
|
+
*/
|
|
39
|
+
export declare function choiceIdOf<TChoice>(choice: TChoice): ChoiceValueOf<TChoice>;
|
|
40
|
+
/**
|
|
41
|
+
* The value a single-choice input holds, which is the value of one of its
|
|
42
|
+
* choices. An empty field is the empty value the input was given, which no
|
|
43
|
+
* choice equals, so it is only ever compared.
|
|
44
|
+
*/
|
|
45
|
+
export declare function toChoiceValue<TChoice>(value: FormValue): ChoiceValueOf<TChoice>;
|
|
46
|
+
/**
|
|
47
|
+
* The value a choice stores: what was read at its `optionValue` path, else its
|
|
48
|
+
* {@link choiceIdOf id}.
|
|
49
|
+
*/
|
|
50
|
+
export declare function resolveChoiceValue<TChoice>(choice: TChoice, atOptionValue: FormValue): ChoiceValueOf<TChoice>;
|
|
51
|
+
/**
|
|
52
|
+
* The values a multi-choice input holds: the stored array, one value for a
|
|
53
|
+
* stored scalar, and a shared empty list for a missing one (so a render that
|
|
54
|
+
* finds nothing keeps the same reference). `''` is missing too: it is what the
|
|
55
|
+
* default format makes of a nullish value.
|
|
56
|
+
*/
|
|
57
|
+
export declare function toChoiceValues<TChoice>(value: FormValue): readonly ChoiceValueOf<TChoice>[];
|
|
58
|
+
/** The text a text input edits: `''` for a nullish value so the input stays controlled. */
|
|
59
|
+
export declare function toTextValue(value: FormValue): string;
|
|
60
|
+
/** The rows of a text-array input: every entry as text, `[]` for a non-array. */
|
|
61
|
+
export declare function toTextList(value: FormValue): string[];
|
|
62
|
+
/** A number input's value: `null` for a cleared or non-numeric one (`''` is cleared, `0` is not). */
|
|
63
|
+
export declare function toNumberValue(value: FormValue): number | null;
|
|
64
|
+
/** A boolean input's value: only `false`, `0`, `''`, `"false"`, `"0"` and a nullish value read as false. */
|
|
65
|
+
export declare function toBooleanValue(value: FormValue): boolean;
|
|
66
|
+
/** A tri-state boolean input's value: `true` or `false` as stored, `null` for every other value (unset is not false). */
|
|
67
|
+
export declare function toNullableBooleanValue(value: FormValue): boolean | null;
|
|
68
|
+
/**
|
|
69
|
+
* A date input's value, unconverted: the string/number/`Date` it can edit,
|
|
70
|
+
* `null` for a cleared one. Formatting to the input's own shape stays with the
|
|
71
|
+
* input (it is the family that knows whether it wants `YYYY-MM-DD` or a time).
|
|
72
|
+
*/
|
|
73
|
+
export declare function toDateInputValue(value: FormValue): DateInputValue;
|
|
74
|
+
/**
|
|
75
|
+
* A file input's value: a live `File`, the stored reference (a URL string) of
|
|
76
|
+
* the file already on the record, `null` for a cleared one. `File` is checked
|
|
77
|
+
* through the global so the guard also runs where there is none (Node).
|
|
78
|
+
*/
|
|
79
|
+
export declare function toFileInputValue(value: FormValue): FileInputValue;
|
|
80
|
+
/** A picked `File`, which may carry the object URL its preview holds. */
|
|
81
|
+
export type FileWithPreview = File & {
|
|
82
|
+
preview?: string;
|
|
83
|
+
};
|
|
84
|
+
/** What `<FileInput>` stores for a dropped file: the file with the preview URL and title a preview child reads. */
|
|
85
|
+
export type TransformedFile = {
|
|
86
|
+
rawFile: FileWithPreview;
|
|
87
|
+
src: string;
|
|
88
|
+
title: string;
|
|
89
|
+
};
|
|
90
|
+
/** A file already on the record: the `{ src, title }` object a data provider stores. Other keys ride along. */
|
|
91
|
+
export type StoredFile = {
|
|
92
|
+
src?: string;
|
|
93
|
+
title?: string;
|
|
94
|
+
};
|
|
95
|
+
/** One file a `<FileInput>` holds: a stored file may also be just its URL. */
|
|
96
|
+
export type FileEntry = FileWithPreview | TransformedFile | StoredFile | string;
|
|
97
|
+
/** What a `<FileInput>` holds: one entry, a list of them with `multiple`, or nothing. */
|
|
98
|
+
export type FileSelection = FileEntry | FileEntry[] | null;
|
|
99
|
+
/** The preview record `<FileInput>` makes of a dropped file. */
|
|
100
|
+
export declare function isTransformedFile(value: FormValue): value is TransformedFile;
|
|
101
|
+
/**
|
|
102
|
+
* A file input's selection: the entry, the entries of a list (anything that is
|
|
103
|
+
* not a file, a record or a URL string dropped), `null` for a cleared one.
|
|
104
|
+
*/
|
|
105
|
+
export declare function toFileSelection(value: FormValue): FileSelection;
|
|
106
|
+
/** The entries of a file input's value, in order. */
|
|
107
|
+
export declare function toFileEntries(value: FormValue): FileEntry[];
|
|
108
|
+
/** A stable list key for a file entry: the dropped file's name, else the stored `src` or URL, else its place. */
|
|
109
|
+
export declare function fileEntryKey(entry: FileEntry, index: number): string | number;
|
|
110
|
+
/** The object URL a file entry's preview holds, to revoke when it goes. */
|
|
111
|
+
export declare function fileEntryPreview(entry: FileEntry): string | undefined;
|
|
112
|
+
/** A currency input's value: the amount, `null` when cleared (minor-vs-major units stay with the input). */
|
|
113
|
+
export declare function toCurrencyValue(value: FormValue): number | null;
|
|
114
|
+
/** What a multi-currency `<CurrencyInput>` stores: the amount and its currency code, when it has one. */
|
|
115
|
+
export interface CurrencyComposite {
|
|
116
|
+
amount: number;
|
|
117
|
+
currency: string | undefined;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* A multi-currency input's value: the amount with its currency, `null` for
|
|
121
|
+
* anything without a numeric amount (an absent value, or a bare number from
|
|
122
|
+
* single-currency mode). A currency that is missing or not text reads as
|
|
123
|
+
* `undefined`, so the saved amount survives it.
|
|
124
|
+
*/
|
|
125
|
+
export declare function toCurrencyComposite(value: FormValue): CurrencyComposite | null;
|
|
126
|
+
/**
|
|
127
|
+
* A webhook input's value: the endpoint object, filled in from whatever the
|
|
128
|
+
* record holds so every member the input edits is present.
|
|
129
|
+
*/
|
|
130
|
+
export declare function toWebhookValue(value: FormValue): WebhookValue;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// src/form/input-values.ts
|
|
2
|
+
/**
|
|
3
|
+
* The value a choice list stores for one choice: its `id` when it has one (ra's
|
|
4
|
+
* `choices` carry `{id, name}` pairs), the choice itself otherwise.
|
|
5
|
+
*/
|
|
6
|
+
function choiceIdOf(choice) {
|
|
7
|
+
if (typeof choice === "object" && choice !== null && "id" in choice) return choice.id;
|
|
8
|
+
return choice;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The value a single-choice input holds, which is the value of one of its
|
|
12
|
+
* choices. An empty field is the empty value the input was given, which no
|
|
13
|
+
* choice equals, so it is only ever compared.
|
|
14
|
+
*/
|
|
15
|
+
function toChoiceValue(value) {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The value a choice stores: what was read at its `optionValue` path, else its
|
|
20
|
+
* {@link choiceIdOf id}.
|
|
21
|
+
*/
|
|
22
|
+
function resolveChoiceValue(choice, atOptionValue) {
|
|
23
|
+
return atOptionValue !== void 0 ? toChoiceValue(atOptionValue) : choiceIdOf(choice);
|
|
24
|
+
}
|
|
25
|
+
var NO_CHOICE_VALUES = Object.freeze([]);
|
|
26
|
+
/**
|
|
27
|
+
* The values a multi-choice input holds: the stored array, one value for a
|
|
28
|
+
* stored scalar, and a shared empty list for a missing one (so a render that
|
|
29
|
+
* finds nothing keeps the same reference). `''` is missing too: it is what the
|
|
30
|
+
* default format makes of a nullish value.
|
|
31
|
+
*/
|
|
32
|
+
function toChoiceValues(value) {
|
|
33
|
+
if (value == null || value === "") return NO_CHOICE_VALUES;
|
|
34
|
+
if (Array.isArray(value)) return value;
|
|
35
|
+
return [toChoiceValue(value)];
|
|
36
|
+
}
|
|
37
|
+
/** The text a text input edits: `''` for a nullish value so the input stays controlled. */
|
|
38
|
+
function toTextValue(value) {
|
|
39
|
+
if (value == null) return "";
|
|
40
|
+
if (typeof value === "string") return value;
|
|
41
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
42
|
+
return "";
|
|
43
|
+
}
|
|
44
|
+
/** The rows of a text-array input: every entry as text, `[]` for a non-array. */
|
|
45
|
+
function toTextList(value) {
|
|
46
|
+
return Array.isArray(value) ? value.map(toTextValue) : [];
|
|
47
|
+
}
|
|
48
|
+
/** A number input's value: `null` for a cleared or non-numeric one (`''` is cleared, `0` is not). */
|
|
49
|
+
function toNumberValue(value) {
|
|
50
|
+
if (value == null || value === "") return null;
|
|
51
|
+
if (typeof value === "number") return Number.isNaN(value) ? null : value;
|
|
52
|
+
if (typeof value === "boolean") return value ? 1 : 0;
|
|
53
|
+
if (typeof value !== "string") return null;
|
|
54
|
+
const parsed = Number(value);
|
|
55
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
56
|
+
}
|
|
57
|
+
/** A boolean input's value: only `false`, `0`, `''`, `"false"`, `"0"` and a nullish value read as false. */
|
|
58
|
+
function toBooleanValue(value) {
|
|
59
|
+
if (typeof value === "boolean") return value;
|
|
60
|
+
if (value == null) return false;
|
|
61
|
+
if (typeof value === "number") return value !== 0;
|
|
62
|
+
if (typeof value === "string") return value !== "" && value !== "false" && value !== "0";
|
|
63
|
+
return Boolean(value);
|
|
64
|
+
}
|
|
65
|
+
/** A tri-state boolean input's value: `true` or `false` as stored, `null` for every other value (unset is not false). */
|
|
66
|
+
function toNullableBooleanValue(value) {
|
|
67
|
+
return typeof value === "boolean" ? value : null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A date input's value, unconverted: the string/number/`Date` it can edit,
|
|
71
|
+
* `null` for a cleared one. Formatting to the input's own shape stays with the
|
|
72
|
+
* input (it is the family that knows whether it wants `YYYY-MM-DD` or a time).
|
|
73
|
+
*/
|
|
74
|
+
function toDateInputValue(value) {
|
|
75
|
+
if (value == null || value === "") return null;
|
|
76
|
+
if (typeof value === "string" || typeof value === "number" || value instanceof Date) return value;
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* A file input's value: a live `File`, the stored reference (a URL string) of
|
|
81
|
+
* the file already on the record, `null` for a cleared one. `File` is checked
|
|
82
|
+
* through the global so the guard also runs where there is none (Node).
|
|
83
|
+
*/
|
|
84
|
+
function toFileInputValue(value) {
|
|
85
|
+
if (value == null) return null;
|
|
86
|
+
if (typeof value === "string") return value;
|
|
87
|
+
if (typeof File !== "undefined" && value instanceof File) return value;
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
/** `File` is read through the global so the guards also run where there is none (Node). */
|
|
91
|
+
function isFile(value) {
|
|
92
|
+
return typeof File !== "undefined" && value instanceof File;
|
|
93
|
+
}
|
|
94
|
+
function isFileEntry(value) {
|
|
95
|
+
return isFile(value) || typeof value === "string" && value !== "" || typeof value === "object" && value !== null && !Array.isArray(value);
|
|
96
|
+
}
|
|
97
|
+
/** The preview record `<FileInput>` makes of a dropped file. */
|
|
98
|
+
function isTransformedFile(value) {
|
|
99
|
+
return typeof value === "object" && value !== null && "rawFile" in value && isFile(value.rawFile) && "src" in value && typeof value.src === "string" && "title" in value && typeof value.title === "string";
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A file input's selection: the entry, the entries of a list (anything that is
|
|
103
|
+
* not a file, a record or a URL string dropped), `null` for a cleared one.
|
|
104
|
+
*/
|
|
105
|
+
function toFileSelection(value) {
|
|
106
|
+
if (Array.isArray(value)) return value.filter(isFileEntry);
|
|
107
|
+
return isFileEntry(value) ? value : null;
|
|
108
|
+
}
|
|
109
|
+
/** The entries of a file input's value, in order. */
|
|
110
|
+
function toFileEntries(value) {
|
|
111
|
+
const selection = toFileSelection(value);
|
|
112
|
+
if (selection === null) return [];
|
|
113
|
+
return Array.isArray(selection) ? selection : [selection];
|
|
114
|
+
}
|
|
115
|
+
/** A stable list key for a file entry: the dropped file's name, else the stored `src` or URL, else its place. */
|
|
116
|
+
function fileEntryKey(entry, index) {
|
|
117
|
+
if (typeof entry === "string") return entry;
|
|
118
|
+
if (isTransformedFile(entry)) return entry.rawFile.name;
|
|
119
|
+
if (isFile(entry)) return index;
|
|
120
|
+
return entry.src ?? index;
|
|
121
|
+
}
|
|
122
|
+
/** The object URL a file entry's preview holds, to revoke when it goes. */
|
|
123
|
+
function fileEntryPreview(entry) {
|
|
124
|
+
if (typeof entry === "string") return void 0;
|
|
125
|
+
if (isTransformedFile(entry)) return entry.rawFile.preview;
|
|
126
|
+
return isFile(entry) ? entry.preview : void 0;
|
|
127
|
+
}
|
|
128
|
+
/** A currency input's value: the amount, `null` when cleared (minor-vs-major units stay with the input). */
|
|
129
|
+
function toCurrencyValue(value) {
|
|
130
|
+
return toNumberValue(value);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* A multi-currency input's value: the amount with its currency, `null` for
|
|
134
|
+
* anything without a numeric amount (an absent value, or a bare number from
|
|
135
|
+
* single-currency mode). A currency that is missing or not text reads as
|
|
136
|
+
* `undefined`, so the saved amount survives it.
|
|
137
|
+
*/
|
|
138
|
+
function toCurrencyComposite(value) {
|
|
139
|
+
if (typeof value !== "object" || value === null) return null;
|
|
140
|
+
if (!("amount" in value)) return null;
|
|
141
|
+
const amount = toNumberValue(value.amount);
|
|
142
|
+
if (amount === null) return null;
|
|
143
|
+
return {
|
|
144
|
+
amount,
|
|
145
|
+
currency: "currency" in value && typeof value.currency === "string" ? value.currency : void 0
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** The empty endpoint a webhook input starts from. */
|
|
149
|
+
var EMPTY_WEBHOOK_VALUE = {
|
|
150
|
+
url: "",
|
|
151
|
+
secret: "",
|
|
152
|
+
eventTypes: []
|
|
153
|
+
};
|
|
154
|
+
/** The last-delivery line, read out of a record that may hold anything under it. */
|
|
155
|
+
function toWebhookDelivery(value) {
|
|
156
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
157
|
+
if (!("at" in value) || typeof value.at !== "string") return void 0;
|
|
158
|
+
return {
|
|
159
|
+
status: "status" in value && (value.status === "ok" || value.status === "failed" || value.status === "pending") ? value.status : "pending",
|
|
160
|
+
at: value.at
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* A webhook input's value: the endpoint object, filled in from whatever the
|
|
165
|
+
* record holds so every member the input edits is present.
|
|
166
|
+
*/
|
|
167
|
+
function toWebhookValue(value) {
|
|
168
|
+
if (typeof value !== "object" || value === null) return { ...EMPTY_WEBHOOK_VALUE };
|
|
169
|
+
const lastDelivery = "lastDelivery" in value ? toWebhookDelivery(value.lastDelivery) : void 0;
|
|
170
|
+
return {
|
|
171
|
+
url: "url" in value ? toTextValue(value.url) : "",
|
|
172
|
+
secret: "secret" in value ? toTextValue(value.secret) : "",
|
|
173
|
+
eventTypes: "eventTypes" in value ? toTextList(value.eventTypes) : [],
|
|
174
|
+
...lastDelivery === void 0 ? {} : { lastDelivery }
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
export { choiceIdOf, fileEntryKey, fileEntryPreview, isTransformedFile, resolveChoiceValue, toBooleanValue, toChoiceValue, toChoiceValues, toCurrencyComposite, toCurrencyValue, toDateInputValue, toFileEntries, toFileInputValue, toFileSelection, toNullableBooleanValue, toNumberValue, toTextList, toTextValue, toWebhookValue };
|