fluid-primitives 0.19.1 → 1.0.0-beta.1

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.
Files changed (67) hide show
  1. package/README.md +9 -0
  2. package/dist/Client-HrMIqv7d.js +1934 -0
  3. package/dist/accordion.d.ts +2 -2
  4. package/dist/accordion.js +25 -29
  5. package/dist/button.d.ts +52 -0
  6. package/dist/button.js +135 -0
  7. package/dist/checkbox-group.d.ts +6 -5
  8. package/dist/checkbox-group.js +36 -20
  9. package/dist/{checkbox-group.registry-CGwuF7SF.js → checkbox-group.registry-D8P8A4LY.js} +13 -8
  10. package/dist/checkbox.d.ts +18 -4
  11. package/dist/checkbox.js +30 -27
  12. package/dist/client.d.ts +2 -2
  13. package/dist/client.js +2 -2
  14. package/dist/clipboard.d.ts +12 -5
  15. package/dist/clipboard.js +14 -16
  16. package/dist/collapsible.d.ts +3 -3
  17. package/dist/collapsible.js +13 -17
  18. package/dist/combobox.d.ts +37 -0
  19. package/dist/combobox.js +151 -0
  20. package/dist/date-picker.d.ts +67 -0
  21. package/dist/date-picker.js +289 -0
  22. package/dist/dialog.d.ts +3 -3
  23. package/dist/dialog.js +11 -12
  24. package/dist/field-array.d.ts +97 -0
  25. package/dist/field-array.js +223 -0
  26. package/dist/field.d.ts +30 -54
  27. package/dist/field.js +589 -175
  28. package/dist/fieldset.d.ts +15 -0
  29. package/dist/fieldset.js +27 -0
  30. package/dist/file-upload.d.ts +85 -0
  31. package/dist/file-upload.js +255 -0
  32. package/dist/form.d.ts +2 -83
  33. package/dist/form.fields-B58TZukv.js +329 -0
  34. package/dist/form.js +263 -276
  35. package/dist/index-B06gIN_k.d.ts +1358 -0
  36. package/dist/input.d.ts +96 -0
  37. package/dist/input.js +233 -0
  38. package/dist/menu.d.ts +55 -0
  39. package/dist/menu.js +144 -0
  40. package/dist/navigation-menu.d.ts +3 -3
  41. package/dist/navigation-menu.js +29 -43
  42. package/dist/number-input.d.ts +13 -4
  43. package/dist/number-input.js +30 -31
  44. package/dist/popover.d.ts +11 -3
  45. package/dist/popover.js +17 -18
  46. package/dist/radio-group.d.ts +4 -4
  47. package/dist/radio-group.js +35 -42
  48. package/dist/scroll-area.d.ts +3 -3
  49. package/dist/scroll-area.js +10 -9
  50. package/dist/select.d.ts +13 -51
  51. package/dist/select.js +62 -55
  52. package/dist/slider.d.ts +27 -0
  53. package/dist/slider.js +71 -0
  54. package/dist/switch.d.ts +4 -4
  55. package/dist/switch.js +24 -33
  56. package/dist/tabs.d.ts +3 -3
  57. package/dist/tabs.js +12 -15
  58. package/dist/textarea.d.ts +101 -0
  59. package/dist/textarea.js +231 -0
  60. package/dist/tooltip.d.ts +11 -3
  61. package/dist/tooltip.js +11 -10
  62. package/package.json +79 -24
  63. package/dist/Client-C51NSHxg.js +0 -248
  64. package/dist/field.dom-CJQXpQbZ.js +0 -11
  65. package/dist/form.registry-Cb8Pvoe8.d.ts +0 -7
  66. package/dist/form.registry-CmpTny_s.js +0 -51
  67. package/dist/index-B8JCdyld.d.ts +0 -118
@@ -0,0 +1,1934 @@
1
+ import * as asyncList from "@zag-js/async-list";
2
+ import { VanillaMachine, mergeProps, normalizeProps, normalizeProps as normalizeProps$1, spreadProps } from "@zag-js/vanilla";
3
+ import { ListCollection } from "@zag-js/collection";
4
+ import { dataAttr } from "@zag-js/dom-query";
5
+ import { match, uuid } from "@zag-js/utils";
6
+ import { createAnatomy } from "@zag-js/anatomy";
7
+
8
+ //#region Resources/Private/Client/src/lib/machine.ts
9
+ /**
10
+ * The Zag machine runtime that components run on. It takes `Partial` props: the `id` that Zag
11
+ * requires is guaranteed at runtime by `Component.initHydrator()`.
12
+ */
13
+ var Machine = class extends VanillaMachine {
14
+ constructor(machine, userProps) {
15
+ super(machine, userProps);
16
+ }
17
+ /** Changes props at runtime. Only the ones you pass change, the others stay. */
18
+ updateProps(newProps) {
19
+ super.updateProps(newProps);
20
+ }
21
+ };
22
+
23
+ //#endregion
24
+ //#region Resources/Private/Client/src/lib/async-list.ts
25
+ /**
26
+ * Wraps the machine of `@zag-js/async-list` for use without any DOM or hydration: construct it, call
27
+ * `init()`, `subscribe()` to state changes and `destroy()` when done. Rendering is yours, do it from
28
+ * inside `subscribe()`.
29
+ *
30
+ * Debouncing or throttling `setFilter()` is left to the caller, wrap the call site with the
31
+ * `debounce` or `throttle` of `@zag-js/utils`, which this library already depends on.
32
+ */
33
+ var AsyncList = class {
34
+ machine;
35
+ constructor(options) {
36
+ this.machine = new Machine(asyncList.machine, options);
37
+ }
38
+ /** The api of the current state of the list. It is read fresh on every access, don't hold on to it. */
39
+ get api() {
40
+ return asyncList.connect(this.machine.service);
41
+ }
42
+ /**
43
+ * Calls `fn` with the api on every future state change. It does not fire for the current state,
44
+ * read `api` first if you need it.
45
+ *
46
+ * @param fn - The listener.
47
+ * @returns A function that removes the listener.
48
+ */
49
+ subscribe(fn) {
50
+ return this.machine.subscribe((service) => fn(asyncList.connect(service)));
51
+ }
52
+ /** Sets the filter and reloads the list with it. */
53
+ setFilter(filter) {
54
+ this.api.setFilter(filter);
55
+ }
56
+ /**
57
+ * Changes options at runtime, e.g. `dependencies` to trigger an `autoReload`. `load` is fixed
58
+ * when the list is constructed and can't be swapped here.
59
+ */
60
+ updateProps(newProps) {
61
+ this.machine.updateProps(newProps);
62
+ }
63
+ /** Starts the machine, which loads the list for the first time. Call it once, after constructing. */
64
+ init() {
65
+ this.machine.start();
66
+ }
67
+ /** Stops the machine and cancels a load that is in flight. */
68
+ destroy() {
69
+ this.machine.stop();
70
+ }
71
+ };
72
+
73
+ //#endregion
74
+ //#region Resources/Private/Client/src/lib/component.ts
75
+ /**
76
+ * Base class of every primitive. A subclass builds its machine in `initMachine()`, connects it in
77
+ * `initApi()` and spreads the props of its parts onto the DOM in `render()`. Call `init()` once to
78
+ * start it.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * class Counter extends Component<CounterProps, CounterApi> {
83
+ * static componentName = 'counter';
84
+ *
85
+ * initMachine(props: CounterProps) {
86
+ * return new Machine(counter.machine, props);
87
+ * }
88
+ *
89
+ * initApi() {
90
+ * return counter.connect(this.machine.service, normalizeProps);
91
+ * }
92
+ *
93
+ * render() {
94
+ * const rootEl = this.hydrator.query('root');
95
+ * if (rootEl) this.spreadProps(rootEl, this.api.getRootProps());
96
+ * }
97
+ * }
98
+ * ```
99
+ */
100
+ var Component = class {
101
+ /** The document the component renders into. */
102
+ document;
103
+ #machine;
104
+ #api;
105
+ #hydrator;
106
+ #userProps;
107
+ /**
108
+ * The Fluid namespace identifier (e.g. "ui") this instance was mounted under - assigned by
109
+ * `mountAll`/`mount` right after their callback returns, so not yet available while it runs
110
+ * (`init()` included).
111
+ */
112
+ namespace;
113
+ /** The name of the component, in camelCase. Every subclass has to set it. */
114
+ static componentName;
115
+ /** Alias of `document`. */
116
+ get doc() {
117
+ return this.document;
118
+ }
119
+ constructor(initialProps, userDocument = document) {
120
+ this.initialProps = initialProps;
121
+ this.document = userDocument;
122
+ }
123
+ /** Created by {@see init} - reading it earlier throws. */
124
+ get machine() {
125
+ return this.#requireInit(this.#machine, "machine");
126
+ }
127
+ /** Created by {@see init} - reading it earlier throws. */
128
+ get api() {
129
+ return this.#requireInit(this.#api, "api");
130
+ }
131
+ set api(api) {
132
+ this.#api = api;
133
+ }
134
+ /**
135
+ * Finds the component's parts by name, see {@see ComponentHydrator.query}. Created by
136
+ * {@see init} - reading it earlier throws.
137
+ */
138
+ get hydrator() {
139
+ return this.#requireInit(this.#hydrator, "hydrator");
140
+ }
141
+ /**
142
+ * The props the instance runs on - what {@see transformProps} made of the ones it was created
143
+ * with. Created by {@see init} - reading it earlier throws.
144
+ */
145
+ get userProps() {
146
+ return this.#requireInit(this.#userProps, "userProps");
147
+ }
148
+ #requireInit(value, member) {
149
+ if (value === void 0) throw new Error(`${this.getName()}: \`${member}\` was accessed before init(). It only exists once init() has run - call init() first.`);
150
+ return value;
151
+ }
152
+ /** Creates the hydrator that finds the parts of the component. It needs the `id` prop. */
153
+ initHydrator(props) {
154
+ const id = props.id;
155
+ if (!id) throw new Error("ComponentHydrator requires an id prop to initialize.");
156
+ return new ComponentHydrator(this.getName(), id, this.doc);
157
+ }
158
+ /**
159
+ * Builds everything the instance runs on - `userProps`, hydrator, machine and api - then renders
160
+ * once and starts the machine. Call it once after constructing.
161
+ */
162
+ init() {
163
+ this.#userProps = this.transformProps(this.initialProps);
164
+ this.#hydrator = this.initHydrator(this.#userProps);
165
+ this.#machine = this.initMachine(this.#userProps);
166
+ this.#api = this.initApi();
167
+ this.render();
168
+ this.#machine.subscribe(() => {
169
+ this.#api = this.initApi();
170
+ this.render();
171
+ });
172
+ this.#machine.start();
173
+ }
174
+ /**
175
+ * Renders again with no prop change behind it. Use it after changing the DOM directly, e.g.
176
+ * after inserting a new `Template` instance, for a primitive with no prop that causes a render on
177
+ * its own. Combobox and Select don't need it: `updateProps({ collection })` already renders.
178
+ */
179
+ refresh() {
180
+ this.machine.notify();
181
+ }
182
+ /** The `componentName` of the class. */
183
+ getName() {
184
+ return this.constructor.componentName;
185
+ }
186
+ /**
187
+ * Kebab form of {@see getName} - what actually appears in DOM-facing identifiers (part attribute
188
+ * names, hydration keys). See {@see ComponentHydrator.clientComponentName}.
189
+ */
190
+ getClientName() {
191
+ return toKebabCase(this.getName());
192
+ }
193
+ /**
194
+ * Override to adjust the props before anything else sees them, e.g. to wrap a callback prop.
195
+ * Runs once, first in {@see init}: the result becomes `userProps`, which `initHydrator()` and
196
+ * `initMachine()` receive.
197
+ */
198
+ transformProps(props) {
199
+ return props;
200
+ }
201
+ /**
202
+ * Another already-mounted instance of this same component, by root id - for a primitive that
203
+ * composes two independent instances of itself (Menu submenus). Only resolves once
204
+ * `mountAll`/`mount` assigned {@see namespace}.
205
+ */
206
+ getPeerInstance(rootId) {
207
+ return this.namespace ? getComponentInstance(`${this.namespace}:${this.getName()}`, rootId) : void 0;
208
+ }
209
+ /** Changes props at runtime. Only the ones you pass change, the others stay. */
210
+ updateProps(newProps) {
211
+ this.machine.updateProps(newProps);
212
+ }
213
+ /** Stops the machine and releases the hydrator. */
214
+ destroy() {
215
+ this.machine.stop();
216
+ this.hydrator.destroy();
217
+ }
218
+ /** Applies the attributes and event handlers of a part's props to its element. */
219
+ spreadProps(node, attrs) {
220
+ spreadProps(node, attrs, this.machine.scope.id);
221
+ }
222
+ /** {@see spreadTextControlProps}: for a text control that mirrors what the user typed. */
223
+ spreadTextControlProps(node, attrs) {
224
+ spreadTextControlProps(node, attrs, this.machine.scope.id);
225
+ }
226
+ /**
227
+ * For every element matching `part`, reads its own `data-value` and hands `{el, value}` to
228
+ * `getProps`; if it returns a props object, spreads it onto that element via `spreadProps`.
229
+ * Returning `null`/`undefined` skips the element (e.g. a value that no longer resolves
230
+ * against a collection). Deliberately does not read any other attribute
231
+ * (disabled/invalid/current/...) or resolve a collection itself - callers read whatever flags
232
+ * they need off `el` and do their own value resolution, since both vary per primitive.
233
+ */
234
+ spreadPropsByValue(part, getProps, options) {
235
+ this.hydrator.queryAll(part, options?.parent).forEach((el) => {
236
+ const value = el.dataset.value;
237
+ if (value === void 0) return;
238
+ const props = getProps({
239
+ el,
240
+ value
241
+ });
242
+ if (props) this.spreadProps(el, props);
243
+ });
244
+ }
245
+ spreadPropsByOptionalValue(part, getProps, options) {
246
+ this.hydrator.queryAll(part, options?.parent).forEach((el) => {
247
+ const value = el.dataset.value;
248
+ const props = getProps({
249
+ el,
250
+ value
251
+ });
252
+ if (props) this.spreadProps(el, props);
253
+ });
254
+ }
255
+ };
256
+
257
+ //#endregion
258
+ //#region Resources/Private/Client/src/lib/portal.ts
259
+ const wrappersByOrigin = /* @__PURE__ */ new WeakMap();
260
+ /**
261
+ * @internal
262
+ *
263
+ * Moves every `[data-portal]` wrapper inside `root` to its destination. A wrapper whose container
264
+ * does not exist stays where it is, unless it is the default portal, which goes to the end of the body.
265
+ */
266
+ function movePortals(root, doc = document) {
267
+ for (const wrapper of root.querySelectorAll("[data-portal]")) {
268
+ const name = wrapper.dataset.portal || "default";
269
+ const container = Array.from(doc.querySelectorAll("template[data-portal-container]")).find((el) => el.dataset.portalContainer === name);
270
+ if (!container && name !== "default") continue;
271
+ const origin = doc.createElement("template");
272
+ origin.setAttribute("data-portal-origin", "");
273
+ wrapper.replaceWith(origin);
274
+ wrappersByOrigin.set(origin, wrapper);
275
+ if (container) container.after(wrapper);
276
+ else doc.body.append(wrapper);
277
+ }
278
+ }
279
+ /**
280
+ * @internal
281
+ *
282
+ * Removes the portals that `root` moved out of itself, call it before removing `root`.
283
+ */
284
+ function removePortalsWithin(root) {
285
+ for (const origin of root.querySelectorAll("template[data-portal-origin]")) {
286
+ const wrapper = wrappersByOrigin.get(origin);
287
+ if (!wrapper) continue;
288
+ removePortalsWithin(wrapper);
289
+ wrapper.remove();
290
+ }
291
+ }
292
+
293
+ //#endregion
294
+ //#region Resources/Private/Client/src/lib/hydration.ts
295
+ /**
296
+ * @internal
297
+ *
298
+ * A part's own name is lowerCamelCase (mirroring zag-js's own `ids` prop keys), but its attribute
299
+ * always renders lower-kebab. Keep in sync with: Classes/Utility/ComponentNameUtility.php's
300
+ * camelCaseToLowerCaseDashed().
301
+ */
302
+ function toKebabCase(part) {
303
+ return part.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
304
+ }
305
+ /**
306
+ * Splits a required `"namespace:name"` string (e.g. `"ui:select"`) into its two parts - every
307
+ * public lookup below (`getHydrationData`, `mountAll`, `mount`, `getComponentInstance`) requires
308
+ * this form, with no bare-name fallback: two collections can legitimately define a same-named
309
+ * component with a different shape (a styled wrapper around a primitive it doesn't expose every
310
+ * prop of), so the caller always states which one it means.
311
+ */
312
+ function parseNamespacedComponentName(componentName) {
313
+ const separatorIndex = componentName.indexOf(":");
314
+ if (separatorIndex === -1) throw new Error(`[fluid-primitives] "${componentName}" must include a namespace, e.g. "ui:${componentName}" - mountAll/mount/getHydrationData/getComponentInstance always require one.`);
315
+ return {
316
+ namespace: componentName.slice(0, separatorIndex),
317
+ baseName: componentName.slice(separatorIndex + 1)
318
+ };
319
+ }
320
+ function getHydrationData(component, id) {
321
+ const hydrationData = window.FluidPrimitives?.hydrationData;
322
+ if (!hydrationData || typeof hydrationData !== "object") return null;
323
+ if (!component) return hydrationData;
324
+ const { namespace, baseName } = parseNamespacedComponentName(component);
325
+ const clientBaseName = toKebabCase(baseName);
326
+ let instances = hydrationData[namespace];
327
+ for (const segment of clientBaseName.split(".")) {
328
+ if (!instances || typeof instances !== "object") return null;
329
+ instances = instances[segment];
330
+ }
331
+ if (!instances) return null;
332
+ if (!id) return instances;
333
+ return instances[id] || null;
334
+ }
335
+ /**
336
+ * Root components PHP found nested inside the tracked scope `scopeId` (a `ui:template` stencil's
337
+ * own id, or a FieldArray row's own id) - see `HydrationRegistry::recordNestedComponent()`'s own
338
+ * docblock for why this is authoritative, PHP-recorded data rather than something inferred from
339
+ * scanning rendered DOM shape (which can't find a component that renders no DOM element of its own,
340
+ * e.g. `Dialog`'s `Root`, or whose content has since portaled elsewhere).
341
+ */
342
+ function getNestedComponents(scopeId) {
343
+ return window.FluidPrimitives?.nestedComponents?.[scopeId] ?? [];
344
+ }
345
+ /**
346
+ * Finds a component instance that is already mounted, by the namespaced name it was mounted with and
347
+ * its hydration id (the `id` prop it was rendered with). Works for instances from `mountAll()` and
348
+ * `mount()` alike. It is for primitives that compose two independent instances of themselves at
349
+ * runtime, e.g. the submenus of a Menu.
350
+ *
351
+ * @param componentName - The namespaced name, e.g. `ui:menu`.
352
+ * @param id - The hydration id of the instance.
353
+ * @returns The instance, or `undefined` if none is mounted under that id.
354
+ */
355
+ function getComponentInstance(componentName, id) {
356
+ const { namespace, baseName } = parseNamespacedComponentName(componentName);
357
+ return window.FluidPrimitives?.componentInstances?.[namespace]?.[toKebabCase(baseName)]?.[id];
358
+ }
359
+ /** @internal */
360
+ function getGlobals() {
361
+ const globals = window.FluidPrimitives?.globals;
362
+ if (!globals || typeof globals !== "object") return null;
363
+ return globals;
364
+ }
365
+ /** @internal */
366
+ function getGlobal(key) {
367
+ const globals = getGlobals();
368
+ if (!globals || !(key in globals)) return;
369
+ return globals[key];
370
+ }
371
+ /**
372
+ * Mounts every instance of a component that is not mounted yet and was not rendered with
373
+ * `autoMount="{false}"`. It is safe to call more than once, e.g. after lazily inserted DOM adds new
374
+ * instances: mounted ones are skipped.
375
+ *
376
+ * The `props` in the callback are typed from `componentName` via `HydrationPropsFor`. A component
377
+ * without generated types gets an untyped bag.
378
+ *
379
+ * @param componentName - The namespaced name, e.g. `ui:select`.
380
+ * @param callback - Creates the instance of one hydration entry and returns it.
381
+ *
382
+ * @example
383
+ * ```typescript
384
+ * mountAll('ui:accordion', ({ props }) => {
385
+ * const accordion = new Accordion(props);
386
+ * accordion.init();
387
+ * return accordion;
388
+ * });
389
+ * ```
390
+ */
391
+ function mountAll(componentName, callback) {
392
+ const { namespace, baseName } = parseNamespacedComponentName(componentName);
393
+ const hydrationInstances = getHydrationData(componentName);
394
+ if (!hydrationInstances) return;
395
+ const clientBaseName = toKebabCase(baseName);
396
+ window.FluidPrimitives.componentInstances[namespace] ??= {};
397
+ window.FluidPrimitives.componentInstances[namespace][clientBaseName] ??= {};
398
+ const mountedInstances = window.FluidPrimitives.componentInstances[namespace][clientBaseName];
399
+ Object.keys(hydrationInstances).forEach((id) => {
400
+ if (hydrationInstances[id].autoMount === false) return;
401
+ if (mountedInstances[id]) return;
402
+ const instance = callback({
403
+ ...hydrationInstances[id],
404
+ props: applyClientPropConverters(baseName, hydrationInstances[id].props),
405
+ createHydrator: () => new ComponentHydrator(baseName, id)
406
+ });
407
+ if (!instance) return;
408
+ instance.namespace = namespace;
409
+ mountedInstances[id] = instance;
410
+ });
411
+ }
412
+ /**
413
+ * Destroys every mounted instance whose root element is `root` or inside it, and forgets it. Call it
414
+ * before you remove a subtree from the DOM, e.g. a row of a FieldArray.
415
+ *
416
+ * Only the `root` part of an instance counts. A component without one, such as Dialog, is never
417
+ * matched.
418
+ *
419
+ * @param root - The element that is about to be removed.
420
+ */
421
+ function destroyComponentsWithin(root) {
422
+ if (root instanceof Element) removePortalsWithin(root);
423
+ if (!window.FluidPrimitives) return;
424
+ for (const namespaceBucket of Object.values(window.FluidPrimitives.componentInstances)) for (const instances of Object.values(namespaceBucket)) for (const [id, instance] of Object.entries(instances)) {
425
+ const rootPartSelector = instance.hydrator.selector("root");
426
+ if (!rootPartSelector) continue;
427
+ const isRootItself = root instanceof Element && root.matches(rootPartSelector);
428
+ const hasRootPartInNode = root.querySelector(rootPartSelector) !== null;
429
+ if (isRootItself || hasRootPartInNode) {
430
+ console.log(`Destroying component instance: ${instance.getClientName()}:${id}`);
431
+ instance.destroy();
432
+ delete instances[id];
433
+ }
434
+ }
435
+ }
436
+ /**
437
+ * Mounts one instance by its `rootId`, also one that was rendered with `autoMount="{false}"`. Unlike
438
+ * `mountAll()`, it creates a new instance on every call, which then replaces the one tracked before.
439
+ * `getComponentInstance()` and `destroyComponentsWithin()` find it like any other.
440
+ *
441
+ * The `props` in the callback are typed like those of `mountAll()`.
442
+ *
443
+ * @param componentName - The namespaced name, e.g. `ui:dialog`.
444
+ * @param rootId - The `rootId` the component was rendered with.
445
+ * @param callback - Creates the instance and returns it.
446
+ * @returns The instance, or `undefined` if there is no hydration entry for `rootId`.
447
+ */
448
+ function mount(componentName, rootId, callback) {
449
+ const { namespace, baseName } = parseNamespacedComponentName(componentName);
450
+ const hydrationData = getHydrationData(componentName, rootId);
451
+ if (!hydrationData) return void 0;
452
+ const instance = callback({
453
+ ...hydrationData,
454
+ props: applyClientPropConverters(baseName, hydrationData.props),
455
+ createHydrator: () => new ComponentHydrator(baseName, rootId)
456
+ });
457
+ if (!instance) return void 0;
458
+ instance.namespace = namespace;
459
+ const clientBaseName = toKebabCase(baseName);
460
+ window.FluidPrimitives.componentInstances[namespace] ??= {};
461
+ window.FluidPrimitives.componentInstances[namespace][clientBaseName] ??= {};
462
+ window.FluidPrimitives.componentInstances[namespace][clientBaseName][rootId] = instance;
463
+ return instance;
464
+ }
465
+ /**
466
+ * Finds the parts of one component instance. Every part that `ui:ref` renders carries a
467
+ * `data-<component>-<part>="<rootId>"` attribute, so a lookup needs no id and searches the whole
468
+ * document by default. That is also what finds content that was portaled away from the root.
469
+ */
470
+ var ComponentHydrator = class {
471
+ /** The name of the component, in camelCase. */
472
+ componentName;
473
+ /** The kebab-case form of `componentName`, as it appears in attribute names. */
474
+ clientComponentName;
475
+ /** The document the parts are searched in. */
476
+ doc;
477
+ /** The `rootId` the component was rendered with. */
478
+ rootId;
479
+ /**
480
+ * @param componentName - The name of the component, in camelCase.
481
+ * @param rootId - The `rootId` the component was rendered with. Throws if it is missing.
482
+ * @param doc - The document the parts are searched in.
483
+ */
484
+ constructor(componentName, rootId, doc = document) {
485
+ this.componentName = componentName;
486
+ this.clientComponentName = toKebabCase(componentName);
487
+ this.doc = doc;
488
+ if (!rootId) throw new Error(`Root ID is required for component hydration: ${componentName}`);
489
+ this.rootId = rootId;
490
+ }
491
+ get attrPrefix() {
492
+ return `data-${this.clientComponentName.replaceAll(".", "-")}-`;
493
+ }
494
+ /** The name of the attribute that marks `part`, e.g. `data-accordion-item-trigger`. */
495
+ attr(part) {
496
+ return this.attrPrefix + toKebabCase(part);
497
+ }
498
+ /** The CSS selector of every element of `part` that belongs to this instance. */
499
+ selector(part) {
500
+ return `[${this.attr(part)}="${this.rootId}"]`;
501
+ }
502
+ /** The key that identifies `part`, and optionally one `value` of it, across this instance. */
503
+ scopeKey(part, value) {
504
+ const key = `${this.clientComponentName}:${this.rootId}:${toKebabCase(part)}`;
505
+ return value ? `${key}:${value}` : key;
506
+ }
507
+ /**
508
+ * Finds the first element of `part`.
509
+ *
510
+ * @param part - The name of the part, in camelCase.
511
+ * @param parent - Where to search, the whole document by default.
512
+ * @returns The element, or `null` if there is none.
513
+ */
514
+ query(part, parent = this.doc) {
515
+ return parent.querySelector(this.selector(part));
516
+ }
517
+ /**
518
+ * Finds every element of `part`.
519
+ *
520
+ * @param part - The name of the part, in camelCase.
521
+ * @param parent - Where to search, the whole document by default.
522
+ */
523
+ queryAll(part, parent = this.doc) {
524
+ return Array.from(parent.querySelectorAll(this.selector(part)));
525
+ }
526
+ /**
527
+ * Marks an element as `part` of this instance, like `ui:ref` does on the server. For elements
528
+ * you create after hydration.
529
+ *
530
+ * @param element - The element to mark.
531
+ * @param part - The name of the part, in camelCase.
532
+ * @param value - Sets `data-value`, for a part that exists once per item.
533
+ */
534
+ stamp(element, part, value) {
535
+ element.setAttribute(this.attr(part), this.rootId);
536
+ if (value !== void 0) element.setAttribute("data-value", value);
537
+ }
538
+ /** The part this component's own scope marks `el` as (kebab-case), if any. */
539
+ getOwnPart(el) {
540
+ for (const { name, value } of el.attributes) if (value === this.rootId && name.startsWith(this.attrPrefix)) return name.slice(this.attrPrefix.length);
541
+ return null;
542
+ }
543
+ /**
544
+ * Sets `data-value` on every element within `root` (root included) that is a part of *this*
545
+ * component's own scope, for a new, real `value` - the shared half of
546
+ * {@see restampValue}/{@see renameValue}, which differ only in what they each then do about any
547
+ * nested, independent root component `root` happens to compose (a fresh copy vs. an in-place
548
+ * rename - see `restampValue`'s own docblock for why the two are never auto-detected from one
549
+ * another).
550
+ *
551
+ * `attributes`, if given, are additionally set on the same elements `value` is applied to - for
552
+ * primitives whose `render()` reads plain per-element data attributes rather than resolving a
553
+ * collection item (e.g. RadioGroup's `disabled`/`invalid`, NavigationMenu's `Link` `current`).
554
+ * A boolean value toggles a bare `data-x` attribute (e.g. `{ disabled: true }` -> `data-disabled`,
555
+ * present only when true); a string value sets `data-x="value"` directly.
556
+ */
557
+ restampOwnScope(root, value, attributes) {
558
+ const restamp = (el) => {
559
+ if (this.getOwnPart(el) === null) return;
560
+ el.setAttribute("data-value", value);
561
+ for (const [name, attributeValue] of Object.entries(attributes ?? {})) if (typeof attributeValue === "string") el.setAttribute(`data-${name}`, attributeValue);
562
+ else el.toggleAttribute(`data-${name}`, attributeValue);
563
+ };
564
+ restamp(root);
565
+ root.querySelectorAll("*").forEach(restamp);
566
+ }
567
+ /**
568
+ * @internal
569
+ *
570
+ * Re-stamps `root` for a new, real `value` (via {@see restampOwnScope}) and prepares any
571
+ * nested, *independent* root component found inside it (e.g. a `Field`+`Input` composed inside
572
+ * a `FieldArray` row, or any other primitive that composes another one inside its own per-item
573
+ * markup) as a brand-new instance copied from its own pristine stencil data, via
574
+ * {@see prepareNestedRootComponents}. Use after cloning a `<template>` (see `Template`) to make
575
+ * the clone represent one real item/row in a single call, instead of manually setting
576
+ * `data-value` on the root and separately on every nested value-scoped part (e.g. a combobox
577
+ * item's own `item-text`/`item-indicator`) *and* separately preparing whatever independent
578
+ * components that item happens to compose. Fully generic - not
579
+ * tied to any one primitive - so a `<template>` item "just works" regardless of what it
580
+ * contains, without the calling primitive needing to know or handle that itself.
581
+ *
582
+ * Only for a *freshly cloned* item - one that has never been through this or
583
+ * {@see renameValue} before (a `<template>`'s content is never mutated by a previous clone, so
584
+ * this is always true right after `content.cloneNode(true)`). For an *existing* item pulled
585
+ * from the live DOM whose value is merely changing (e.g. `FieldArray` reindexing a later row
586
+ * down by one after an earlier row was removed), call {@see renameValue} instead.
587
+ *
588
+ * The two used to be auto-detected from a nested component's own *current* id shape (no
589
+ * `:value` suffix yet vs. already has one) rather than asked for explicitly - but that shape
590
+ * can't actually tell a never-touched, already-live server-rendered row apart from a
591
+ * never-touched template clone; both look identical. That silently mis-classified an
592
+ * already-mounted row as "fresh" the first time an earlier row's removal caused it to be
593
+ * reindexed: its DOM references moved out from under it (via `restampIdReferences`, which both
594
+ * paths run) without its `ComponentHydrator.rootId`/`componentInstances` entry following along -
595
+ * only {@see renameValue}'s path updates those - leaving its already-mounted `Field`/`Input`
596
+ * instances silently unable to find their own elements again. The caller already knows
597
+ * unambiguously which case it's in (a fresh `Template` clone vs. an element already in the
598
+ * document), so it now says so by calling the matching method instead of leaving it to a guess.
599
+ *
600
+ * `stencilKey` is the stencil's own {@see scopeKey} (see `Template`'s constructor, its only
601
+ * caller) - what nested root components `root` composes is looked up from
602
+ * `window.FluidPrimitives.nestedComponents[stencilKey]` (PHP-recorded, see
603
+ * `HydrationRegistry::recordNestedComponent()`), not found by scanning `root` itself. That's
604
+ * what lets this find a nested component regardless of what DOM shape it renders - including
605
+ * one that renders no identifiable wrapper of its own at all (e.g. `Dialog`'s `Root`, which is
606
+ * just `<f:slot />` - see `ui:portal`'s own `isRenderStencil` handling for the other half of
607
+ * making a portalled nested component work inside a stencil).
608
+ *
609
+ * Returns the distinct client component names of any nested root components found and
610
+ * prepared (empty when `root` contains none, the common case - e.g. `FileUpload`'s own item
611
+ * previews) - call the matching `mountAll()`s for each of these after inserting a freshly
612
+ * cloned item into the document, per `mountAll`'s own docblock ("after lazily-inserted DOM
613
+ * adds new instances").
614
+ */
615
+ restampValue(root, value, stencilKey, attributes) {
616
+ this.restampOwnScope(root, value, attributes);
617
+ const ownPart = this.getOwnPart(root);
618
+ return prepareNestedRootComponents(stencilKey, root, value, ownPart ? this.scopeKey(ownPart, value) : null);
619
+ }
620
+ /**
621
+ * @internal
622
+ *
623
+ * Re-stamps `root` for a new, real `value` (via {@see restampOwnScope}) and re-keys every
624
+ * nested, independent root component found inside it (e.g. a `FieldArray` row's own
625
+ * `Field`+`Input`) to reference `value` instead of whatever position it previously held - both
626
+ * their DOM references (via {@see restampIdReferences}) and, for ones that are already live/mounted,
627
+ * their own `ComponentHydrator.rootId` and their entry in
628
+ * `window.FluidPrimitives.componentInstances`/`hydrationData` (via
629
+ * {@see renameNestedComponentEntry}), so their future re-renders keep resolving their own
630
+ * elements and later lookups (`getComponentInstance`, `destroyComponentsWithin`) keep finding
631
+ * them under their new id.
632
+ *
633
+ * For an *existing* item pulled from the live DOM whose position is merely shifting - never for
634
+ * a freshly cloned `<template>` item, which has no existing instance/hydration-data entry to
635
+ * re-key in the first place; use {@see restampValue} for that. See that method's own docblock
636
+ * for why the two are never auto-detected from one another.
637
+ *
638
+ * Looks up what `root` composes from `window.FluidPrimitives.nestedComponents[<scopeKey>]`,
639
+ * keyed by `root`'s own part and `data-value` *before* this call restamps it, which is why
640
+ * that's read first, before {@see restampOwnScope} changes it. That's a correct lookup key both
641
+ * for a row rendered for real (PHP records it there too - see `ComponentHydrationCollector`'s
642
+ * FieldArray-ambient-context check) and for one `restampValue` prepared earlier (which migrates
643
+ * the same entry to the row's new key as its own last step, for exactly this reason).
644
+ *
645
+ * Restamps id *references* (via {@see restampIdReferences}) across the whole document, not just
646
+ * `root`'s own subtree - unlike {@see restampValue}, where a fresh clone can't yet have anything
647
+ * portaled out of it (nothing composed by it has mounted/opened yet), `root` here is an existing,
648
+ * already-live item, so a nested component it composes may already have portaled its own content
649
+ * elsewhere in the document (e.g. an open `Popover`'s positioner, rendered via `ui:portal` into
650
+ * the page footer) by the time this runs. Scoping to `root` alone would restamp that component's
651
+ * trigger in place but silently miss its portaled content, leaving it referencing a now-stale id.
652
+ */
653
+ renameValue(root, value) {
654
+ const ownPart = this.getOwnPart(root);
655
+ const previousScopeKey = ownPart ? this.scopeKey(ownPart, root.getAttribute("data-value") ?? void 0) : null;
656
+ this.restampOwnScope(root, value);
657
+ if (!ownPart || !previousScopeKey) return;
658
+ const remaps = nestedRootRemapsFromMetadata(previousScopeKey, value);
659
+ if (remaps.length === 0) return;
660
+ restampIdReferences(this.doc, remaps);
661
+ for (const { componentName, oldRootId, newRootId } of remaps) renameNestedComponentEntry(componentName, oldRootId, newRootId, remaps);
662
+ migrateNestedComponentsScope(previousScopeKey, this.scopeKey(ownPart, value), remaps);
663
+ }
664
+ /** Nothing to release yet, it exists so every instance can be destroyed alike. */
665
+ destroy() {}
666
+ };
667
+ /**
668
+ * True when `id` references `rootId` as a whole id-segment - i.e. `id` either *is* `rootId`, or
669
+ * contains it immediately followed by `:` - not merely as a substring. A plain `.includes()`
670
+ * would false-positive match row `2`'s root id against row `20`'s (both contain the digit `2`).
671
+ */
672
+ function referencesRootId(id, rootId) {
673
+ const index = id.indexOf(rootId);
674
+ if (index === -1) return false;
675
+ const charAfter = id[index + rootId.length];
676
+ return charAfter === void 0 || charAfter === ":";
677
+ }
678
+ /**
679
+ * Rewrites, on every element in `root` (root itself included), whatever references one of
680
+ * `remaps`' `oldRootId` so it references the matching `newRootId` instead: every part attribute of
681
+ * that nested component (`data-<nested>-<part>="oldRootId"`), and any real `id` that embeds it (a
682
+ * part given an explicit id through `ids`, see `ui:ref`). The id is rewritten via plain string
683
+ * substitution, not recomputed from a formula - an Input's `label`/`input` parts are server-stamped
684
+ * with their enclosing Field's own `label`/`control` ids (see `FieldIdMapping`), which an id
685
+ * derived from Input's own root id would silently lose. Scoped to `root` (the whole cloned item,
686
+ * not just one nested component's own subtree) since an override can reference a sibling
687
+ * component's id, not just an ancestor's - or, from {@see ComponentHydrator.renameValue}, to the
688
+ * whole document, when a nested component's content may already live outside `root` entirely (see
689
+ * that method's own docblock).
690
+ */
691
+ function restampIdReferences(root, remaps) {
692
+ const restamp = (el) => {
693
+ for (const { name, value } of Array.from(el.attributes)) if (name === "id") {
694
+ const match = remaps.find(({ oldRootId }) => referencesRootId(value, oldRootId));
695
+ if (match) el.setAttribute(name, value.replace(match.oldRootId, match.newRootId));
696
+ } else if (name.startsWith("data-")) {
697
+ const match = remaps.find(({ oldRootId }) => oldRootId === value);
698
+ if (match) el.setAttribute(name, match.newRootId);
699
+ }
700
+ };
701
+ if (root instanceof Element) restamp(root);
702
+ root.querySelectorAll("*").forEach(restamp);
703
+ }
704
+ /**
705
+ * Remaps every value in a nested root component's `ids` prop override map using the same
706
+ * old->new root id substitutions {@see restampIdReferences} applied to the DOM, so the hydration
707
+ * props a later `mountAll()` constructs an instance from stay consistent with where its parts
708
+ * actually live post-restamp - without this, a component like Input (whose `ids.label`/`ids.input`
709
+ * may reference its enclosing Field's own ids, not its own) would keep looking for its label/
710
+ * input elements at their stale, pre-restamp ids and find nothing.
711
+ */
712
+ function remapIdsProp(ids, remaps) {
713
+ const remapped = {};
714
+ for (const [key, value] of Object.entries(ids)) {
715
+ const match = remaps.find(({ oldRootId }) => referencesRootId(value, oldRootId));
716
+ remapped[key] = match ? value.replace(match.oldRootId, match.newRootId) : value;
717
+ }
718
+ return remapped;
719
+ }
720
+ /**
721
+ * Copies a stencil's cached hydration props to a freshly derived id, for a nested root component
722
+ * found in a freshly cloned `<template>` item (see {@see prepareNestedRootComponents}). Deep-clones
723
+ * the stencil's own props
724
+ * (registered once at server-render time, when that one stencil instance rendered) rather than
725
+ * moving them, since the stencil's own entry must stay put for the *next* clone to copy from too -
726
+ * unlike {@see renameNestedComponentEntry}, which re-keys a single existing entry in place.
727
+ *
728
+ * Rewrites a `[]` "append" placeholder in any string `name` prop to `value` (the same trailing-
729
+ * `[]`-means-append convention `form.path.ts`'s `appendFieldPathSegment`/`FieldContext.php` use).
730
+ * Returns whether a stencil entry was actually found and copied, so a caller only counts this
731
+ * component name as "found" when there was real hydration data to prepare.
732
+ */
733
+ function copyNestedComponentEntry(componentName, stencilRootId, newRootId, remaps, value) {
734
+ const stencilData = getHydrationData(componentName, stencilRootId);
735
+ if (!stencilData) return false;
736
+ const rewrittenProps = {
737
+ ...stencilData.props,
738
+ id: newRootId,
739
+ ids: remapIdsProp(stencilData.props.ids, remaps)
740
+ };
741
+ if (typeof rewrittenProps.name === "string" && rewrittenProps.name.includes("[]")) rewrittenProps.name = rewrittenProps.name.replace("[]", `[${value}]`);
742
+ const { namespace, baseName } = parseNamespacedComponentName(componentName);
743
+ const clientBaseName = toKebabCase(baseName);
744
+ window.FluidPrimitives.hydrationData[namespace] ??= {};
745
+ window.FluidPrimitives.hydrationData[namespace][clientBaseName] ??= {};
746
+ window.FluidPrimitives.hydrationData[namespace][clientBaseName][newRootId] = {
747
+ ...stencilData,
748
+ props: rewrittenProps
749
+ };
750
+ return true;
751
+ }
752
+ /**
753
+ * Re-keys a single nested root component's own entry from `oldRootId` to `newRootId` in place -
754
+ * for an existing item pulled from the live DOM (see {@see ComponentHydrator.renameValue}), as
755
+ * opposed to a fresh one still in its pristine stencil state (see
756
+ * {@see copyNestedComponentEntry}). Updates both its `window.FluidPrimitives.
757
+ * hydrationData` entry and, for one that's already live/mounted, its own `ComponentHydrator.
758
+ * rootId` and its entry in `window.FluidPrimitives.componentInstances`, so its future
759
+ * re-renders keep resolving their own elements correctly and later lookups
760
+ * (`getComponentInstance`, `destroyComponentsWithin`) keep finding it under its new id.
761
+ */
762
+ function renameNestedComponentEntry(componentName, oldRootId, newRootId, remaps) {
763
+ const { namespace, baseName } = parseNamespacedComponentName(componentName);
764
+ const clientBaseName = toKebabCase(baseName);
765
+ const instances = window.FluidPrimitives?.componentInstances?.[namespace]?.[clientBaseName];
766
+ const instance = instances?.[oldRootId];
767
+ if (instance) {
768
+ delete instances[oldRootId];
769
+ instances[newRootId] = instance;
770
+ instance.hydrator.rootId = newRootId;
771
+ }
772
+ const hydrationInstances = window.FluidPrimitives?.hydrationData?.[namespace]?.[clientBaseName];
773
+ const stencilData = hydrationInstances?.[oldRootId];
774
+ if (hydrationInstances && stencilData) {
775
+ delete hydrationInstances[oldRootId];
776
+ hydrationInstances[newRootId] = {
777
+ ...stencilData,
778
+ props: {
779
+ ...stencilData.props,
780
+ id: newRootId,
781
+ ids: remapIdsProp(stencilData.props.ids, remaps)
782
+ }
783
+ };
784
+ }
785
+ }
786
+ /**
787
+ * Builds the `value`-suffixed id each root component PHP recorded as nested inside `scopeKey`
788
+ * (see {@see getNestedComponents}) should move to - the metadata itself, not this formula, is what
789
+ * tells the two apart from any *other* root component on the page.
790
+ *
791
+ * Recovers each one's stable, stencil-rooted prefix from its *current* recorded id first -
792
+ * stripping back to before the last `:` segment, if it has one - so a component already renamed
793
+ * once (`x:0` -> `x:1`) has that suffix *replaced* rather than accumulated (`x:0:1`). A component
794
+ * that's never been touched at all (fresh from either a never-rendered-before-now server row or an
795
+ * untouched `<template>` stencil) has no `:` in its rootId to strip, so this is a correct no-op for
796
+ * it - its whole current id already is the stable prefix.
797
+ *
798
+ * Shared by both {@see prepareNestedRootComponents} (fresh `<template>` clones) and
799
+ * {@see ComponentHydrator.renameValue} (existing items whose position is shifting) - the remap
800
+ * shape itself doesn't differ between the two; only what each one does with it afterward (copy a
801
+ * stencil's props to the new id vs. re-key an existing entry in place) does.
802
+ */
803
+ function nestedRootRemapsFromMetadata(scopeKey, value) {
804
+ return getNestedComponents(scopeKey).map(({ name, id: currentRootId }) => {
805
+ const lastColonIndex = currentRootId.lastIndexOf(":");
806
+ return {
807
+ componentName: name,
808
+ oldRootId: currentRootId,
809
+ newRootId: `${lastColonIndex === -1 ? currentRootId : currentRootId.slice(0, lastColonIndex)}:${value}`
810
+ };
811
+ });
812
+ }
813
+ /**
814
+ * Records a copy of `window.FluidPrimitives.nestedComponents[oldScopeKey]` (if present) under
815
+ * `newScopeKey`, rewriting each entry's own `id` through the same `remaps`
816
+ * {@see nestedRootRemapsFromMetadata} just computed from it - so a *later*
817
+ * {@see ComponentHydrator.renameValue} on this same item (a second reindex) still finds it under
818
+ * the id the item actually has by then, rather than the one it was first recorded under. Leaves
819
+ * `oldScopeKey` in place: for a fresh clone that is the stencil, which the *next* clone needs too.
820
+ * Returns whether there was anything to copy.
821
+ */
822
+ function copyNestedComponentsScope(oldScopeKey, newScopeKey, remaps) {
823
+ const entries = window.FluidPrimitives?.nestedComponents?.[oldScopeKey];
824
+ if (!entries || oldScopeKey === newScopeKey) return false;
825
+ window.FluidPrimitives.nestedComponents ??= {};
826
+ window.FluidPrimitives.nestedComponents[newScopeKey] = entries.map((entry) => {
827
+ const match = remaps.find((remap) => remap.componentName === entry.name && remap.oldRootId === entry.id);
828
+ return match ? {
829
+ name: entry.name,
830
+ id: match.newRootId
831
+ } : entry;
832
+ });
833
+ return true;
834
+ }
835
+ /**
836
+ * {@see copyNestedComponentsScope}, then drops `oldScopeKey` - for an existing item whose own key
837
+ * is changing (see {@see ComponentHydrator.renameValue}), as opposed to a fresh clone.
838
+ */
839
+ function migrateNestedComponentsScope(oldScopeKey, newScopeKey, remaps) {
840
+ if (copyNestedComponentsScope(oldScopeKey, newScopeKey, remaps)) delete window.FluidPrimitives.nestedComponents?.[oldScopeKey];
841
+ }
842
+ /**
843
+ * Prepares every nested, independent root component inside a freshly cloned `<template>` item for
844
+ * `value` - the fresh-clone half of {@see ComponentHydrator.restampValue}, which this backs. Every
845
+ * element that *references* one of those root ids - not just the root element itself - is
846
+ * re-stamped too, via {@see restampIdReferences} (which also fixes up any part whose id references
847
+ * a *different* nested component's own id, e.g. Input's label/input reusing Field's), plus the
848
+ * same substitution applied to each one's own `ids` prop override map via {@see remapIdsProp}.
849
+ * Without this, a nested component's parts would keep the root id of the stencil they were cloned
850
+ * from and stay attached to it, since they're outside `restampValue`'s own scope (which only
851
+ * covers the *outer* component's own parts).
852
+ *
853
+ * Copies each one's stencil hydration data (registered once at server-render time, when that one
854
+ * stencil instance rendered) to the new id via {@see copyNestedComponentEntry}, so a subsequent
855
+ * `mountAll()` can construct it. Deliberately does not construct component instances itself -
856
+ * there is no registry mapping a componentName to its constructor anywhere in this library (each
857
+ * primitive's own `mountAll('field', callback)` call, with its own constructor, is defined
858
+ * per-consumer in their own entry.ts) - see {@see ComponentHydrator.restampValue}'s own docblock
859
+ * for what to do with the client component names this returns instead.
860
+ */
861
+ function prepareNestedRootComponents(stencilKey, root, value, newScopeKey) {
862
+ const remaps = nestedRootRemapsFromMetadata(stencilKey, value);
863
+ if (remaps.length === 0) return [];
864
+ restampIdReferences(root, remaps);
865
+ const componentNames = /* @__PURE__ */ new Set();
866
+ for (const { componentName, oldRootId, newRootId } of remaps) if (copyNestedComponentEntry(componentName, oldRootId, newRootId, remaps, value)) componentNames.add(componentName);
867
+ if (newScopeKey) copyNestedComponentsScope(stencilKey, newScopeKey, remaps);
868
+ return Array.from(componentNames);
869
+ }
870
+ function getByPath(item, path) {
871
+ return path.split(".").reduce((value, key) => value?.[key], item);
872
+ }
873
+ function getListCollectionFromHydrationData(hydrationCollection) {
874
+ if (hydrationCollection instanceof ListCollection) return hydrationCollection;
875
+ const { itemToValueKey, itemToStringKey, isItemDisabledKey, groupByKey, groupSort } = hydrationCollection;
876
+ return new ListCollection({
877
+ items: hydrationCollection.items,
878
+ itemToValue: itemToValueKey ? (item) => getByPath(item, itemToValueKey) : void 0,
879
+ itemToString: itemToStringKey ? (item) => getByPath(item, itemToStringKey) : void 0,
880
+ isItemDisabled: isItemDisabledKey ? (item) => getByPath(item, isItemDisabledKey) : void 0,
881
+ groupBy: groupByKey ? (item) => getByPath(item, groupByKey) : void 0,
882
+ groupSort: groupSort ?? void 0
883
+ });
884
+ }
885
+
886
+ //#endregion
887
+ //#region Resources/Private/Client/src/lib/client-prop-converters.ts
888
+ const converters = /* @__PURE__ */ new Map();
889
+ /**
890
+ * Registers which props of a component need converting before a `mountAll()` or `mount()` callback
891
+ * receives them, e.g. the `collection` of a Select: JSON on the wire, a `ListCollection` once
892
+ * converted.
893
+ *
894
+ * @param componentName - The component, e.g. `select`.
895
+ * @param propConverters - The converters, by prop name.
896
+ */
897
+ function registerClientPropConverters(componentName, propConverters) {
898
+ converters.set(toKebabCase(componentName), propConverters);
899
+ }
900
+ /**
901
+ * @internal
902
+ *
903
+ * Runs every registered converter for `componentName` against `props`, called for every
904
+ * registered key regardless of whether it's actually present on `props` - a converter is
905
+ * registered per prop *name*, not per wire value, so it can supply a default for a prop the wire
906
+ * data omitted entirely (e.g. Combobox's `collection` when a purely async, searchUrl-only combobox
907
+ * sends none).
908
+ */
909
+ function applyClientPropConverters(componentName, props) {
910
+ const componentConverters = converters.get(toKebabCase(componentName));
911
+ if (!componentConverters) return props;
912
+ const result = { ...props };
913
+ for (const [propName, convert] of Object.entries(componentConverters)) result[propName] = convert(result[propName], result);
914
+ return result;
915
+ }
916
+
917
+ //#endregion
918
+ //#region Resources/Private/Client/src/lib/delayed-indicator.ts
919
+ const DEFAULT_SHOW_DELAY_MS = 150;
920
+ const DEFAULT_MIN_VISIBLE_MS = 300;
921
+ function assertFiniteNonNegative(value, name) {
922
+ if (!Number.isFinite(value) || value < 0) throw new RangeError(`DelayedIndicator: ${name} must be a finite number >= 0, got ${value}.`);
923
+ return value;
924
+ }
925
+ /**
926
+ * Delays showing a transient value long enough to avoid flicker, and keeps it visible for a minimum
927
+ * duration once shown. Feed it every new value through `set()`, typically straight from a subscribe
928
+ * callback, and it forwards them to `onChange`. The values `isTransient` marks are held back for
929
+ * `showDelayMs`, so a fast operation never flashes them. Once shown they stay for at least
930
+ * `minVisibleMs`, so they can't vanish the instant they appeared.
931
+ *
932
+ * It works on any value, not only a boolean: one instance can drive a spinner and a status text
933
+ * from the same source, so they never disagree.
934
+ */
935
+ var DelayedIndicator = class {
936
+ showDelayMs;
937
+ minVisibleMs;
938
+ isTransient;
939
+ onChange;
940
+ phase = "idle";
941
+ latestValue;
942
+ timer = null;
943
+ shownAt = 0;
944
+ constructor(options) {
945
+ this.showDelayMs = assertFiniteNonNegative(options.showDelayMs ?? DEFAULT_SHOW_DELAY_MS, "showDelayMs");
946
+ this.minVisibleMs = assertFiniteNonNegative(options.minVisibleMs ?? DEFAULT_MIN_VISIBLE_MS, "minVisibleMs");
947
+ this.isTransient = options.isTransient;
948
+ this.onChange = options.onChange;
949
+ }
950
+ /**
951
+ * Passes a new value on, delayed as described above. It does not deduplicate: the same value
952
+ * again can still call `onChange` again, as this filters state over time, it is not a
953
+ * `distinctUntilChanged()`.
954
+ *
955
+ * @param value - The new value.
956
+ */
957
+ set(value) {
958
+ this.latestValue = value;
959
+ const transient = this.isTransient(value);
960
+ if (this.phase === "idle") {
961
+ if (!transient) {
962
+ this.onChange(value);
963
+ return;
964
+ }
965
+ this.phase = "waiting";
966
+ this.timer = setTimeout(() => {
967
+ this.timer = null;
968
+ this.phase = "shown";
969
+ this.shownAt = performance.now();
970
+ this.onChange(this.latestValue);
971
+ }, this.showDelayMs);
972
+ return;
973
+ }
974
+ if (this.phase === "waiting") {
975
+ if (transient) return;
976
+ if (this.timer !== null) clearTimeout(this.timer);
977
+ this.timer = null;
978
+ this.phase = "idle";
979
+ this.onChange(value);
980
+ return;
981
+ }
982
+ if (transient) {
983
+ if (this.timer !== null) {
984
+ clearTimeout(this.timer);
985
+ this.timer = null;
986
+ }
987
+ this.onChange(value);
988
+ return;
989
+ }
990
+ if (this.timer !== null) return;
991
+ const remaining = this.minVisibleMs - (performance.now() - this.shownAt);
992
+ const commit = () => {
993
+ this.timer = null;
994
+ this.phase = "idle";
995
+ this.onChange(this.latestValue);
996
+ };
997
+ if (remaining > 0) this.timer = setTimeout(commit, remaining);
998
+ else commit();
999
+ }
1000
+ /**
1001
+ * Cancels any timer that is running. Call it when the indicator is no longer needed, e.g. on
1002
+ * teardown: otherwise a scheduled value can still reach `onChange` afterwards. It is safe to call
1003
+ * when nothing is transient or shown.
1004
+ */
1005
+ destroy() {
1006
+ if (this.timer !== null) {
1007
+ clearTimeout(this.timer);
1008
+ this.timer = null;
1009
+ }
1010
+ this.phase = "idle";
1011
+ }
1012
+ };
1013
+
1014
+ //#endregion
1015
+ //#region Resources/Private/Client/src/lib/extbase.ts
1016
+ /**
1017
+ * Reads the Extbase argument namespace (`tx_docs_docs`) of a frontend action URL, from its
1018
+ * `[controller]` or `[action]` query key. `UriBuilder::uriFor()` nests those under it, also for a
1019
+ * custom `pluginNamespace`.
1020
+ *
1021
+ * @param url - The URL of the action.
1022
+ * @returns The namespace, or `null` for a URL without such a key.
1023
+ */
1024
+ function getArgumentPrefix(url) {
1025
+ for (const key of new URL(url, document.baseURI).searchParams.keys()) {
1026
+ const prefix = key.match(/^([^[\]]+)\[(?:action|controller)\]$/)?.[1];
1027
+ if (prefix) return prefix;
1028
+ }
1029
+ return null;
1030
+ }
1031
+ /** Bracket notation (`demand[city]`, `ids[0]`) is what Extbase's argument mapper reads. */
1032
+ function flatten(value, key) {
1033
+ if (value == null) return [];
1034
+ if (Array.isArray(value) || value.constructor === Object) return Object.entries(value).flatMap(([k, v]) => flatten(v, key ? `${key}[${k}]` : k));
1035
+ return [[key, value instanceof Blob ? value : String(value)]];
1036
+ }
1037
+ /**
1038
+ * Sends GET and POST requests to an Extbase controller action from your own code, e.g. from the
1039
+ * `load` callback of an async search. The data is nested under the Extbase argument namespace of the
1040
+ * URL, the way `f:uri.action` builds it.
1041
+ */
1042
+ const extbase = {
1043
+ /**
1044
+ * GETs `url` with `data` appended as query parameters under its Extbase argument namespace. The
1045
+ * URL's own query, `cHash` included, is kept - so the new parameters have to be listed in
1046
+ * `FE.cacheHash.excludedParameters`, or TYPO3 rejects the request.
1047
+ *
1048
+ * @param url - The URL of the action, absolute or relative.
1049
+ * @param data - The arguments. Nested objects and arrays are flattened into bracket notation.
1050
+ * @param init - More options for `fetch`, e.g. `{ signal }`.
1051
+ * @returns The native `Response`. A non-2xx status resolves normally, check `response.ok`.
1052
+ */
1053
+ async get(url, data, init) {
1054
+ const target = new URL(url, document.baseURI);
1055
+ for (const [key, value] of flatten(data, getArgumentPrefix(url) ?? "")) target.searchParams.append(key, String(value));
1056
+ return fetch(target, {
1057
+ ...init,
1058
+ method: "GET"
1059
+ });
1060
+ },
1061
+ /**
1062
+ * POSTs `data` as `multipart/form-data`, nested under the URL's Extbase argument namespace. A
1063
+ * URL without one gets `data` unprefixed.
1064
+ *
1065
+ * @param url - The URL of the action.
1066
+ * @param data - The arguments. Nested objects and arrays are flattened into bracket notation, `null`
1067
+ * and `undefined` are skipped, a `File` or `Blob` is sent as it is.
1068
+ * @param init - More options for `fetch`, e.g. `{ signal }`.
1069
+ * @returns The native `Response`. A non-2xx status resolves normally, check `response.ok`.
1070
+ */
1071
+ async post(url, data, init) {
1072
+ const body = new FormData();
1073
+ for (const [key, value] of flatten(data, getArgumentPrefix(url) ?? "")) body.append(key, value);
1074
+ return fetch(url, {
1075
+ ...init,
1076
+ method: "POST",
1077
+ body
1078
+ });
1079
+ },
1080
+ getArgumentPrefix
1081
+ };
1082
+
1083
+ //#endregion
1084
+ //#region Resources/Private/Primitives/Form/src/form.path.ts
1085
+ /** Matches one path segment: either a bare run of non-delimiter chars, or bracket contents. */
1086
+ const fieldPathSegmentPattern = /([^.[\]]+)|\[(.*?)\]/g;
1087
+ /** Sentinel for an empty `[]` segment - "append to this array" - distinct from any real field name. */
1088
+ const appendFieldPathSegment = Symbol("append-field-path-segment");
1089
+ /** Strips a trailing `[]` (the manual-bracket array-submission marker, e.g. `a11yNeeds[]`) if present. */
1090
+ function trimArraySuffix(fieldName) {
1091
+ return fieldName.replace(/\[\]$/, "");
1092
+ }
1093
+ /**
1094
+ * Splits a field name into its path segments, treating `.` and `[...]` as equivalent
1095
+ * delimiters - `person.country` and `person[country]` both parse to `['person', 'country']`.
1096
+ * An all-digit segment becomes a number, and an empty `[]` becomes {@link appendFieldPathSegment}.
1097
+ */
1098
+ function parseFieldPath(fieldName) {
1099
+ const fieldPath = [];
1100
+ for (const match of fieldName.matchAll(fieldPathSegmentPattern)) {
1101
+ if (match[1]) {
1102
+ const segment = match[1];
1103
+ fieldPath.push(/^\d+$/.test(segment) ? Number(segment) : segment);
1104
+ continue;
1105
+ }
1106
+ const bracketSegment = match[2] ?? "";
1107
+ if (bracketSegment === "") {
1108
+ fieldPath.push(appendFieldPathSegment);
1109
+ continue;
1110
+ }
1111
+ fieldPath.push(/^\d+$/.test(bracketSegment) ? Number(bracketSegment) : bracketSegment);
1112
+ }
1113
+ return fieldPath;
1114
+ }
1115
+ /**
1116
+ * Joins path segments back into one bracketed name: the first segment stays bare, every
1117
+ * segment after it gets wrapped in `[...]`, and {@link appendFieldPathSegment} becomes `[]`.
1118
+ */
1119
+ function stringifyFieldPathAsBrackets(fieldPath) {
1120
+ let fieldName = "";
1121
+ for (const segment of fieldPath) {
1122
+ if (segment === appendFieldPathSegment) {
1123
+ fieldName += "[]";
1124
+ continue;
1125
+ }
1126
+ const segmentValue = String(segment);
1127
+ fieldName += fieldName === "" ? segmentValue : `[${segmentValue}]`;
1128
+ }
1129
+ return fieldName;
1130
+ }
1131
+ /**
1132
+ * Parses `fieldName` (dot or bracket notation) and rebuilds it fully bracketed, with
1133
+ * `prefix` and `objectName` unshifted onto the front in that order. Used to rewrite submitted
1134
+ * `FormData` keys into the form's real Extbase argument name just before `fetch()`.
1135
+ */
1136
+ function prefixFieldName(fieldName, prefix, objectName) {
1137
+ if (fieldName === "") return "";
1138
+ const fieldPath = parseFieldPath(fieldName);
1139
+ if (objectName) fieldPath.unshift(objectName);
1140
+ if (prefix) fieldPath.unshift(prefix);
1141
+ return stringifyFieldPathAsBrackets(fieldPath);
1142
+ }
1143
+ /**
1144
+ * Resolves any field-name string (dot or bracket, optionally objectName-prefixed) to the exact
1145
+ * bracket-notation key the field registry uses.
1146
+ */
1147
+ function toRegisteredFieldName(fieldName, objectName) {
1148
+ const fieldPath = parseFieldPath(fieldName);
1149
+ if (fieldPath.length === 0) return trimArraySuffix(fieldName);
1150
+ if (objectName && fieldPath[0] === objectName) fieldPath.shift();
1151
+ return trimArraySuffix(stringifyFieldPathAsBrackets(fieldPath));
1152
+ }
1153
+
1154
+ //#endregion
1155
+ //#region Resources/Private/Primitives/Form/src/form.registry.ts
1156
+ const registry$1 = /* @__PURE__ */ new WeakMap();
1157
+ function registerFormMachine(form, service) {
1158
+ if (!form) return;
1159
+ registry$1.set(form, {
1160
+ machine: service,
1161
+ fields: /* @__PURE__ */ new Map(),
1162
+ expectedFieldCount: form.querySelectorAll("[data-field-root]").length || 0
1163
+ });
1164
+ form.dispatchEvent(new CustomEvent("fluid-primitives:form:registered", { bubbles: true }));
1165
+ }
1166
+ function registerFieldMachineForForm(el, fieldMachine) {
1167
+ if (!el) return;
1168
+ const form = resolveElToForm(el);
1169
+ if (!form) return;
1170
+ const handleEntry = (entry) => {
1171
+ entry.fields.set(trimArraySuffix(fieldMachine.prop("name")), fieldMachine);
1172
+ if (entry.fields.size === entry.expectedFieldCount) entry.machine.notify();
1173
+ else if (entry.fields.size > entry.expectedFieldCount) entry.machine.notify?.();
1174
+ };
1175
+ const handler = () => {
1176
+ const entry = registry$1.get(form);
1177
+ if (!entry) return;
1178
+ handleEntry(entry);
1179
+ form.removeEventListener("fluid-primitives:form:registered", handler);
1180
+ };
1181
+ const entry = registry$1.get(form);
1182
+ if (entry) handleEntry(entry);
1183
+ else form.addEventListener("fluid-primitives:form:registered", handler);
1184
+ }
1185
+ /**
1186
+ * Drops a field's entry by identity rather than by name: the entry may have been re-keyed by a
1187
+ * rename, and a destroyed field's stopped machine must not keep counting as a field of the form.
1188
+ */
1189
+ function unregisterFieldMachineForForm(el, fieldMachine) {
1190
+ const fields = registry$1.get(resolveElToForm(el))?.fields;
1191
+ if (!fields) return;
1192
+ for (const [name, registered] of fields) if (registered === fieldMachine) fields.delete(name);
1193
+ }
1194
+ /**
1195
+ * Re-keys a registered field machine under a new field name and updates its
1196
+ * `name` prop, without touching its value/touched/dirty/error state. This is
1197
+ * how recurring-field rows stay contiguously indexed after removing a row in
1198
+ * the middle: later rows are renamed down by one instead of copying values
1199
+ * between DOM elements, which keeps working regardless of what kind of
1200
+ * control (native input, Select, NumberInput, ...) the field wraps.
1201
+ */
1202
+ function renameFieldMachineForForm(el, oldName, newName) {
1203
+ const form = resolveElToForm(el);
1204
+ if (!form) return;
1205
+ const entry = registry$1.get(form);
1206
+ if (!entry) return;
1207
+ const normalizedOldName = trimArraySuffix(oldName);
1208
+ const fieldMachine = entry.fields.get(normalizedOldName);
1209
+ if (!fieldMachine) return;
1210
+ entry.fields.delete(normalizedOldName);
1211
+ fieldMachine.updateProps({ name: newName });
1212
+ entry.fields.set(trimArraySuffix(newName), fieldMachine);
1213
+ }
1214
+ function resolveElToForm(el) {
1215
+ if (!el) return null;
1216
+ if (el instanceof HTMLFormElement) return el;
1217
+ return el.closest("form");
1218
+ }
1219
+ function getFormMachineFor(el) {
1220
+ if (!el) return;
1221
+ const form = resolveElToForm(el);
1222
+ if (!form) return;
1223
+ return registry$1.get(form)?.machine;
1224
+ }
1225
+ function getFieldMachinesFor(el) {
1226
+ if (!el) return /* @__PURE__ */ new Map();
1227
+ const form = resolveElToForm(el);
1228
+ if (!form) return /* @__PURE__ */ new Map();
1229
+ const entry = registry$1.get(form);
1230
+ if (!entry) return /* @__PURE__ */ new Map();
1231
+ return entry.fields;
1232
+ }
1233
+
1234
+ //#endregion
1235
+ //#region Resources/Private/Primitives/Field/src/field.anatomy.ts
1236
+ /**
1237
+ * Derived from @zag-js/field of Zag.js (https://github.com/chakra-ui/zag, packages/machines/field,
1238
+ * commit 0ce8e3de63b571d37e438bcf28e64759466e56e9), MIT License, Copyright (c) 2021 Chakra UI.
1239
+ * Adapted for Fluid Primitives, the git history shows what changed from the original.
1240
+ */
1241
+ const anatomy = createAnatomy("field").parts("root", "label", "control", "helperText", "errorText", "indicator");
1242
+ const parts = anatomy.build();
1243
+
1244
+ //#endregion
1245
+ //#region Resources/Private/Primitives/Field/src/field.dom.ts
1246
+ /** Dispatched on the field root once its settled value differs from the last one it announced. */
1247
+ const FIELD_VALUE_CHANGE_EVENT = "fluid-primitives:field:valuechange";
1248
+ const getRootId = (ctx) => ctx.ids?.root ?? ctx.id;
1249
+ const getControlId = (ctx) => ctx.ids?.control ?? `field:${ctx.id}:control`;
1250
+ const getLabelId = (ctx) => ctx.ids?.label ?? `field:${ctx.id}:label`;
1251
+ const getErrorTextId = (ctx, match, id) => {
1252
+ if (id) return id;
1253
+ if (typeof match === "string") return `field:${ctx.id}:error-text:${match}`;
1254
+ return ctx.ids?.errorText ?? `field:${ctx.id}:error-text`;
1255
+ };
1256
+ const getHelperTextId = (ctx) => ctx.ids?.helperText ?? `field:${ctx.id}:helper-text`;
1257
+ const queryRootEl = (ctx) => ctx.query(ctx.selector(parts.root));
1258
+ const getControlEl = (ctx) => ctx.getById(getControlId(ctx));
1259
+ const hasHelperText = (rootEl) => rootEl.querySelector(`[${parts.helperText.attr}]`) !== null;
1260
+ const getVisibleErrorTextIds = (rootEl) => Array.from(rootEl.querySelectorAll(`[${parts.errorText.attr}]`)).filter((el) => !el.hidden && el.id).map((el) => el.id);
1261
+ const componentRootAttribute = /^(data-.+-)root$/;
1262
+ /**
1263
+ * Whether `target` belongs to the field: inside its root, or a portaled part (a popup, say) of a
1264
+ * component rendered inside it. Those parts sit outside the root but carry
1265
+ * `data-<component>-<part>="<id of that component's root>"`, like the root itself.
1266
+ */
1267
+ function isInsideField(rootEl, target) {
1268
+ if (!(target instanceof Element)) return false;
1269
+ if (rootEl.contains(target)) return true;
1270
+ const prefixById = /* @__PURE__ */ new Map();
1271
+ for (const el of rootEl.querySelectorAll("*")) for (const { name, value } of Array.from(el.attributes)) {
1272
+ const match = componentRootAttribute.exec(name);
1273
+ if (match && value) prefixById.set(value, match[1]);
1274
+ }
1275
+ if (prefixById.size === 0) return false;
1276
+ for (let el = target; el; el = el.parentElement) for (const { name, value } of Array.from(el.attributes)) {
1277
+ const prefix = prefixById.get(value);
1278
+ if (prefix && name.startsWith(prefix)) return true;
1279
+ }
1280
+ return false;
1281
+ }
1282
+
1283
+ //#endregion
1284
+ //#region Resources/Private/Primitives/Field/src/field.utils.ts
1285
+ const VALIDITY_KEYS = [
1286
+ "badInput",
1287
+ "customError",
1288
+ "patternMismatch",
1289
+ "rangeOverflow",
1290
+ "rangeUnderflow",
1291
+ "stepMismatch",
1292
+ "tooLong",
1293
+ "tooShort",
1294
+ "typeMismatch",
1295
+ "valueMissing"
1296
+ ];
1297
+ const VALID_SNAPSHOT = Object.freeze({
1298
+ badInput: false,
1299
+ customError: false,
1300
+ patternMismatch: false,
1301
+ rangeOverflow: false,
1302
+ rangeUnderflow: false,
1303
+ stepMismatch: false,
1304
+ tooLong: false,
1305
+ tooShort: false,
1306
+ typeMismatch: false,
1307
+ valueMissing: false,
1308
+ valid: true
1309
+ });
1310
+ /** Copy the live `ValidityState` — its getters are recomputed on every read. */
1311
+ function getValiditySnapshot(el) {
1312
+ const snapshot = {};
1313
+ for (const key of VALIDITY_KEYS) snapshot[key] = el.validity[key];
1314
+ snapshot.valid = el.validity.valid;
1315
+ return snapshot;
1316
+ }
1317
+ /**
1318
+ * A pristine empty required field is not an error yet. When `valueMissing` is the
1319
+ * only failure, report the field as valid until the user interacts or a submit forces it.
1320
+ */
1321
+ function suppressValueMissing(validity) {
1322
+ if (!validity.valueMissing) return validity;
1323
+ if (!VALIDITY_KEYS.every((key) => key === "valueMissing" || !validity[key])) return validity;
1324
+ return {
1325
+ ...validity,
1326
+ valueMissing: false,
1327
+ valid: true
1328
+ };
1329
+ }
1330
+ /**
1331
+ * The native flags with `valueMissing` replaced. The field's own `required` and its value decide it,
1332
+ * not the control's `required` attribute: a Combobox's visible input holds label text, a group has no
1333
+ * control at all, and a placeholder option is always selected.
1334
+ */
1335
+ function withValueMissing(validity, valueMissing) {
1336
+ const next = {
1337
+ ...validity,
1338
+ valueMissing
1339
+ };
1340
+ next.valid = VALIDITY_KEYS.every((key) => !next[key]);
1341
+ return next;
1342
+ }
1343
+ function toErrorArray(result) {
1344
+ if (result == null) return [];
1345
+ return (Array.isArray(result) ? result : [result]).filter((error) => typeof error === "string" && error.length > 0);
1346
+ }
1347
+ /**
1348
+ * Priority: custom `validate` errors outrank native constraint errors.
1349
+ * (The controlled `invalid` prop outranks both, at the computed level.)
1350
+ */
1351
+ function resolveValidation(options) {
1352
+ const { customErrors, validity, nativeMessage } = options;
1353
+ if (customErrors.length > 0) return {
1354
+ errors: customErrors,
1355
+ validity: {
1356
+ ...validity,
1357
+ customError: true,
1358
+ valid: false
1359
+ }
1360
+ };
1361
+ if (!validity.valid) return {
1362
+ errors: nativeMessage ? [nativeMessage] : [],
1363
+ validity
1364
+ };
1365
+ return {
1366
+ errors: [],
1367
+ validity
1368
+ };
1369
+ }
1370
+ /**
1371
+ * Description first, then visible error ids in DOM order.
1372
+ * Hidden error texts must not be referenced — screen readers still announce them.
1373
+ */
1374
+ function composeDescribedBy(options) {
1375
+ const ids = [];
1376
+ if (options.hasHelperText) ids.push(options.helperTextId);
1377
+ ids.push(...options.errorTextIds);
1378
+ return ids.length > 0 ? ids.join(" ") : void 0;
1379
+ }
1380
+ /**
1381
+ * Whether this event should commit validation (make it visible).
1382
+ * `VALIDATE` / `SUBMIT.INVALID` always commit and do not go through this.
1383
+ *
1384
+ * Two rules go beyond the modes: an error that is showing revalidates on every change in every
1385
+ * mode, so it clears the moment it is fixed; and a blur only commits once the value was edited or a
1386
+ * submit was attempted, so tabbing through a pristine field, or a popup taking focus from its
1387
+ * trigger, shows nothing.
1388
+ */
1389
+ function shouldCommit(options) {
1390
+ const { mode, submitAttempted, eventType, edited, showingErrors } = options;
1391
+ if (eventType === "CONTROL.BLUR") return mode === "onBlur" && (edited || submitAttempted);
1392
+ if (eventType === "CONTROL.CHANGE") return mode === "onChange" || mode === "onSubmit" && submitAttempted || showingErrors;
1393
+ return false;
1394
+ }
1395
+ /** Whether the error text should show for the given `match`. */
1396
+ function isErrorMatch(match, options) {
1397
+ const { validity, invalid, disabled } = options;
1398
+ if (match === true) return true;
1399
+ if (match === false || disabled) return false;
1400
+ if (typeof match === "string") return validity?.[match] === true;
1401
+ return invalid;
1402
+ }
1403
+
1404
+ //#endregion
1405
+ //#region Resources/Private/Primitives/Field/src/field.connect.ts
1406
+ function connect(service, normalize) {
1407
+ const { send, context, prop, computed, scope } = service;
1408
+ const disabled = computed("disabled");
1409
+ const invalid = computed("invalid");
1410
+ const valid = computed("valid");
1411
+ const required = !!prop("required");
1412
+ const readOnly = !!prop("readOnly");
1413
+ const focused = !disabled && context.get("focused");
1414
+ const touched = context.get("touched");
1415
+ const dirty = context.get("dirty");
1416
+ const filled = context.get("filled");
1417
+ const validating = context.get("validating");
1418
+ const ids = {
1419
+ root: getRootId(scope),
1420
+ control: getControlId(scope),
1421
+ label: getLabelId(scope),
1422
+ errorText: getErrorTextId(scope),
1423
+ helperText: getHelperTextId(scope)
1424
+ };
1425
+ const ariaDescribedby = composeDescribedBy({
1426
+ helperTextId: ids.helperText,
1427
+ hasHelperText: context.get("hasHelperText"),
1428
+ errorTextIds: context.get("errorTextIds")
1429
+ });
1430
+ function getFieldState() {
1431
+ return {
1432
+ disabled,
1433
+ invalid,
1434
+ valid,
1435
+ required,
1436
+ readOnly,
1437
+ touched,
1438
+ dirty,
1439
+ filled,
1440
+ focused,
1441
+ validating
1442
+ };
1443
+ }
1444
+ function getErrorTextState(props = {}) {
1445
+ const shown = isErrorMatch(props.match, {
1446
+ validity: context.get("validity"),
1447
+ invalid,
1448
+ disabled
1449
+ });
1450
+ return {
1451
+ ...getFieldState(),
1452
+ hidden: !shown
1453
+ };
1454
+ }
1455
+ function getIndicatorState(props) {
1456
+ const fieldState = getFieldState();
1457
+ const shown = match(props.type, {
1458
+ required: () => fieldState.required,
1459
+ invalid: () => fieldState.invalid,
1460
+ valid: () => fieldState.valid === true,
1461
+ validating: () => fieldState.validating
1462
+ });
1463
+ return {
1464
+ ...fieldState,
1465
+ type: props.type,
1466
+ hidden: !shown
1467
+ };
1468
+ }
1469
+ function getControlBaseProps() {
1470
+ const fieldState = getFieldState();
1471
+ return {
1472
+ ...parts.control.attrs(scope.id),
1473
+ id: ids.control,
1474
+ name: prop("name"),
1475
+ dir: prop("dir"),
1476
+ disabled: fieldState.disabled,
1477
+ required: fieldState.required,
1478
+ "aria-invalid": fieldState.invalid || void 0,
1479
+ "aria-describedby": ariaDescribedby,
1480
+ ...getDataAttrs(fieldState)
1481
+ };
1482
+ }
1483
+ return {
1484
+ ids,
1485
+ disabled,
1486
+ invalid,
1487
+ valid,
1488
+ required,
1489
+ readOnly,
1490
+ focused,
1491
+ touched,
1492
+ dirty,
1493
+ filled,
1494
+ validating,
1495
+ errors: context.get("errors"),
1496
+ validity: context.get("validity"),
1497
+ ariaDescribedby,
1498
+ validate() {
1499
+ send({ type: "VALIDATE" });
1500
+ },
1501
+ clearErrors() {
1502
+ send({ type: "ERRORS.CLEAR" });
1503
+ },
1504
+ reset() {
1505
+ send({ type: "RESET" });
1506
+ },
1507
+ getRootState: getFieldState,
1508
+ getRootProps() {
1509
+ return normalize.element({
1510
+ ...parts.root.attrs(scope.id),
1511
+ dir: prop("dir"),
1512
+ /** The name of the field. */
1513
+ "data-name": prop("name"),
1514
+ ...getDataAttrs(getFieldState())
1515
+ });
1516
+ },
1517
+ getLabelState: getFieldState,
1518
+ getLabelProps() {
1519
+ return normalize.label({
1520
+ ...parts.label.attrs(scope.id),
1521
+ id: ids.label,
1522
+ dir: prop("dir"),
1523
+ htmlFor: ids.control,
1524
+ ...getDataAttrs(getFieldState())
1525
+ });
1526
+ },
1527
+ getControlState: getFieldState,
1528
+ getControlProps() {
1529
+ return normalize.element({ ...getControlBaseProps() });
1530
+ },
1531
+ getInputProps() {
1532
+ return normalize.input({
1533
+ ...getControlBaseProps(),
1534
+ readOnly: getFieldState().readOnly
1535
+ });
1536
+ },
1537
+ getTextareaProps() {
1538
+ return normalize.textarea({
1539
+ ...getControlBaseProps(),
1540
+ readOnly: getFieldState().readOnly
1541
+ });
1542
+ },
1543
+ getSelectProps() {
1544
+ return normalize.select({ ...getControlBaseProps() });
1545
+ },
1546
+ getHelperTextState: getFieldState,
1547
+ getHelperTextProps() {
1548
+ return normalize.element({
1549
+ ...parts.helperText.attrs(scope.id),
1550
+ id: ids.helperText,
1551
+ dir: prop("dir"),
1552
+ ...getDataAttrs(getFieldState())
1553
+ });
1554
+ },
1555
+ getErrorTextState,
1556
+ getErrorTextProps(props = {}) {
1557
+ const errorTextState = getErrorTextState(props);
1558
+ return normalize.element({
1559
+ ...parts.errorText.attrs(scope.id),
1560
+ id: getErrorTextId(scope, props.match, props.id),
1561
+ dir: prop("dir"),
1562
+ hidden: errorTextState.hidden,
1563
+ "aria-live": "polite",
1564
+ ...getDataAttrs(errorTextState)
1565
+ });
1566
+ },
1567
+ getIndicatorState,
1568
+ getIndicatorProps(props) {
1569
+ const indicatorState = getIndicatorState(props);
1570
+ return normalize.element({
1571
+ ...parts.indicator.attrs(scope.id),
1572
+ dir: prop("dir"),
1573
+ /** The state the indicator reflects. */
1574
+ "data-type": indicatorState.type,
1575
+ "aria-hidden": true,
1576
+ hidden: indicatorState.hidden,
1577
+ ...getDataAttrs(indicatorState)
1578
+ });
1579
+ }
1580
+ };
1581
+ }
1582
+ function getDataAttrs(state) {
1583
+ return {
1584
+ /** Present when the field is disabled, or it is inside a disabled fieldset. */
1585
+ "data-disabled": dataAttr(state.disabled),
1586
+ /** Present when the field is invalid: a native constraint failed, or `validate`, the form or the server reported an error. */
1587
+ "data-invalid": dataAttr(state.invalid),
1588
+ /** Present when the field has been validated and passed. */
1589
+ "data-valid": dataAttr(state.valid === true),
1590
+ /** Present when the field is required. */
1591
+ "data-required": dataAttr(state.required),
1592
+ /** Present when the field is read-only. */
1593
+ "data-readonly": dataAttr(state.readOnly),
1594
+ /** Present once the user has left the field at least once. */
1595
+ "data-touched": dataAttr(state.touched),
1596
+ /** Present when the value differs from the one the field started with. It goes away again when the user reverts the edit. */
1597
+ "data-dirty": dataAttr(state.dirty),
1598
+ /** Present when the field has a value. */
1599
+ "data-filled": dataAttr(state.filled),
1600
+ /** Present when focus is inside the field. */
1601
+ "data-focus": dataAttr(state.focused)
1602
+ };
1603
+ }
1604
+
1605
+ //#endregion
1606
+ //#region Resources/Private/Primitives/Field/src/field.value.ts
1607
+ function getCurrentFieldValue(rootEl, name, defaultValue) {
1608
+ if (!rootEl) return getDefaultFieldValue(defaultValue);
1609
+ return getFieldValueFromContainer(rootEl, name) ?? getDefaultFieldValue(defaultValue);
1610
+ }
1611
+ /**
1612
+ * The field value as one string, which is what the machine compares and tests for emptiness:
1613
+ * `''` for nothing (`null`, `[]`, or only the `''` entries a placeholder option leaves behind), a
1614
+ * plain string as is - not trimmed, native `valueMissing` treats whitespace as a value - and a
1615
+ * serialization for lists and files.
1616
+ */
1617
+ function toComparableString(value) {
1618
+ if (value === null) return "";
1619
+ if (typeof value === "string") return value;
1620
+ if (Array.isArray(value)) {
1621
+ const entries = value.filter((entry) => entry !== "");
1622
+ return entries.length > 0 ? serializeFieldValue(entries) : "";
1623
+ }
1624
+ return serializeFieldValue(value);
1625
+ }
1626
+ function getComparableFieldValue(rootEl, name, defaultValue) {
1627
+ return toComparableString(getCurrentFieldValue(rootEl, name, defaultValue));
1628
+ }
1629
+ function getDefaultFieldValue(defaultValue) {
1630
+ if (defaultValue === null || defaultValue === void 0 || defaultValue === false) return null;
1631
+ if (defaultValue instanceof File) return defaultValue;
1632
+ if (Array.isArray(defaultValue)) return defaultValue.filter((value) => value instanceof File || typeof value === "string");
1633
+ if (typeof defaultValue === "string") return defaultValue;
1634
+ return null;
1635
+ }
1636
+ function getFieldValueFromContainer(container, fieldName) {
1637
+ const normalizedFieldName = trimArraySuffix(fieldName);
1638
+ const selector = [
1639
+ `input[name="${CSS.escape(normalizedFieldName)}"]`,
1640
+ `input[name="${CSS.escape(normalizedFieldName)}[]"]`,
1641
+ `select[name="${CSS.escape(normalizedFieldName)}"]`,
1642
+ `select[name="${CSS.escape(normalizedFieldName)}[]"]`,
1643
+ `textarea[name="${CSS.escape(normalizedFieldName)}"]`,
1644
+ `textarea[name="${CSS.escape(normalizedFieldName)}[]"]`
1645
+ ].join(", ");
1646
+ const elements = Array.from(container.querySelectorAll(selector));
1647
+ if (elements.length === 0) return null;
1648
+ if (elements.every((el) => el instanceof HTMLInputElement && el.type === "checkbox")) {
1649
+ const checkboxes = elements;
1650
+ const checkedValues = checkboxes.filter((input) => input.checked).map((input) => input.value || "on");
1651
+ if (fieldName.endsWith("[]") || elements.some((el) => el.name.endsWith("[]")) || checkboxes.length > 1) return checkedValues;
1652
+ return checkedValues[0] ?? null;
1653
+ }
1654
+ if (elements.every((el) => el instanceof HTMLInputElement && el.type === "radio")) return elements.find((input) => input.checked)?.value ?? null;
1655
+ if (elements.length === 1) {
1656
+ const [element] = elements;
1657
+ if (element instanceof HTMLInputElement && element.type === "file") {
1658
+ const files = element.files ? Array.from(element.files) : [];
1659
+ return element.multiple ? files : files[0] ?? null;
1660
+ }
1661
+ if (element instanceof HTMLSelectElement && element.multiple) return Array.from(element.selectedOptions).map((option) => option.value);
1662
+ return element.value ?? null;
1663
+ }
1664
+ return elements.map((element) => {
1665
+ if (element instanceof HTMLInputElement && element.type === "file") return element.files?.[0];
1666
+ return element.value;
1667
+ }).filter((value) => value !== void 0);
1668
+ }
1669
+ function getFieldElement(form, fieldName) {
1670
+ return form.querySelector([
1671
+ `input[name="${CSS.escape(fieldName)}"]`,
1672
+ `input[name="${CSS.escape(fieldName)}[]"]`,
1673
+ `select[name="${CSS.escape(fieldName)}"]`,
1674
+ `select[name="${CSS.escape(fieldName)}[]"]`,
1675
+ `textarea[name="${CSS.escape(fieldName)}"]`,
1676
+ `textarea[name="${CSS.escape(fieldName)}[]"]`
1677
+ ].join(", "));
1678
+ }
1679
+ function serializeFieldValue(value) {
1680
+ return JSON.stringify(toSerializableFieldValue(value));
1681
+ }
1682
+ function toSerializableFieldValue(value) {
1683
+ if (Array.isArray(value)) return value.map(toSerializableFieldValue);
1684
+ if (value instanceof File) return {
1685
+ name: value.name,
1686
+ size: value.size,
1687
+ type: value.type,
1688
+ lastModified: value.lastModified
1689
+ };
1690
+ return value;
1691
+ }
1692
+
1693
+ //#endregion
1694
+ //#region Resources/Private/Primitives/Field/src/field.handle.ts
1695
+ /** Dispatched on a field's root whenever a field named in its own `listenTo` changes value. */
1696
+ const FIELD_DEPENDENCY_CHANGE_EVENT = "fluid-primitives:field:dependencychange";
1697
+ function connectField(machine) {
1698
+ return {
1699
+ ...connect(machine.service, normalizeProps),
1700
+ ...createFieldHandleExtras(machine)
1701
+ };
1702
+ }
1703
+ function createFieldHandleExtras(machine) {
1704
+ const { context, prop, refs } = machine;
1705
+ return {
1706
+ name: prop("name"),
1707
+ getValue: () => getCurrentFieldValue(refs.get("rootEl"), prop("name"), prop("defaultValue")),
1708
+ getErrorText() {
1709
+ const errors = context.get("errors");
1710
+ return errors.length > 0 ? errors.join(" ") : null;
1711
+ },
1712
+ getRootEl: () => refs.get("rootEl"),
1713
+ getFormMachine: () => getFormMachineFor(refs.get("rootEl")),
1714
+ setDisabled: (disabled) => machine.updateProps({ disabled }),
1715
+ setRequired: (required) => machine.updateProps({ required }),
1716
+ setReadOnly: (readOnly) => machine.updateProps({ readOnly }),
1717
+ addDependencyChangeListener(callback) {
1718
+ const rootEl = refs.get("rootEl");
1719
+ if (!rootEl) return () => {};
1720
+ const handler = (event) => {
1721
+ callback(event.detail);
1722
+ };
1723
+ rootEl.addEventListener(FIELD_DEPENDENCY_CHANGE_EVENT, handler);
1724
+ return () => rootEl.removeEventListener(FIELD_DEPENDENCY_CHANGE_EVENT, handler);
1725
+ }
1726
+ };
1727
+ }
1728
+ /** Reads the machine directly instead of building the whole api: the Form makes one per field per render. */
1729
+ function createFieldHandle(machine) {
1730
+ const { context, computed, prop } = machine;
1731
+ const disabled = computed("disabled");
1732
+ return {
1733
+ disabled,
1734
+ required: !!prop("required"),
1735
+ readOnly: !!prop("readOnly"),
1736
+ invalid: computed("invalid"),
1737
+ valid: computed("valid"),
1738
+ touched: context.get("touched"),
1739
+ dirty: context.get("dirty"),
1740
+ filled: context.get("filled"),
1741
+ focused: !disabled && context.get("focused"),
1742
+ validating: context.get("validating"),
1743
+ errors: context.get("errors"),
1744
+ validate: () => machine.send({ type: "VALIDATE" }),
1745
+ clearErrors: () => machine.send({ type: "ERRORS.CLEAR" }),
1746
+ reset: () => machine.send({ type: "RESET" }),
1747
+ ...createFieldHandleExtras(machine)
1748
+ };
1749
+ }
1750
+
1751
+ //#endregion
1752
+ //#region Resources/Private/Primitives/Field/src/field.registry.ts
1753
+ const registry = /* @__PURE__ */ new WeakMap();
1754
+ function registerFieldMachine(field, service) {
1755
+ if (!field) return;
1756
+ registry.set(field, service);
1757
+ field.dispatchEvent(new CustomEvent("fluid-primitives:field:registered", { bubbles: true }));
1758
+ }
1759
+ function unregisterFieldMachine(field) {
1760
+ registry.delete(field);
1761
+ }
1762
+ function getFieldMachineFor(el) {
1763
+ if (!el) return;
1764
+ return registry.get(el);
1765
+ }
1766
+
1767
+ //#endregion
1768
+ //#region Resources/Private/Client/src/lib/field-aware-component.ts
1769
+ const fieldProps = [...[
1770
+ "invalid",
1771
+ "disabled",
1772
+ "readOnly",
1773
+ "required"
1774
+ ], "name"];
1775
+ /**
1776
+ * Base class of a primitive that can sit inside a Field. It takes `name`, `disabled`, `readOnly`,
1777
+ * `required` and `invalid` from the closest Field and follows its changes, and it tells the field
1778
+ * once it has hydrated.
1779
+ */
1780
+ var FieldAwareComponent = class extends Component {
1781
+ subscribedToField = false;
1782
+ fieldMachine;
1783
+ closestField = null;
1784
+ /** The field's current api, for what a primitive merges into its own props when it renders. */
1785
+ get field() {
1786
+ return this.fieldMachine ? connectField(this.fieldMachine) : void 0;
1787
+ }
1788
+ getClosestField() {
1789
+ return this.closestField || this.hydrator.query("root")?.closest("[data-field-root]") || null;
1790
+ }
1791
+ /** Builds the component like `Component.init()`, then tells the field that it is hydrated. */
1792
+ init() {
1793
+ super.init();
1794
+ this.notifyFieldHydrated();
1795
+ }
1796
+ withFieldProps(props) {
1797
+ this.closestField = this.getClosestField();
1798
+ if (!this.closestField) return props;
1799
+ this.fieldMachine = getFieldMachineFor(this.closestField);
1800
+ if (this.fieldMachine) return this.propsWithField(props, connectField(this.fieldMachine));
1801
+ else {
1802
+ const handler = () => {
1803
+ this.fieldMachine = getFieldMachineFor(this.closestField);
1804
+ this.updateProps(this.propsWithField(this.userProps, connectField(this.fieldMachine)));
1805
+ this.notifyFieldHydrated();
1806
+ this.closestField?.removeEventListener("fluid-primitives:field:registered", handler);
1807
+ };
1808
+ this.closestField.addEventListener("fluid-primitives:field:registered", handler);
1809
+ }
1810
+ return props;
1811
+ }
1812
+ /**
1813
+ * Applies every later change of the field to the machine props. It waits for the field if that
1814
+ * has not registered yet, and does nothing when there is no field around.
1815
+ */
1816
+ subscribeToFieldService() {
1817
+ if (this.subscribedToField) return;
1818
+ this.closestField = this.getClosestField();
1819
+ if (!this.closestField) return;
1820
+ if (!this.fieldMachine) this.fieldMachine = getFieldMachineFor(this.closestField);
1821
+ if (this.fieldMachine) {
1822
+ const fieldMachine = this.fieldMachine;
1823
+ fieldMachine.subscribe(() => {
1824
+ queueMicrotask(() => {
1825
+ const field = connectField(fieldMachine);
1826
+ const propsToUpdate = {};
1827
+ for (const prop of fieldProps) {
1828
+ const newValue = prop === "name" ? field.name : !!field[prop];
1829
+ if (newValue !== (prop === "name" ? this.machine.prop(prop) : !!this.machine.prop(prop))) propsToUpdate[prop] = newValue;
1830
+ }
1831
+ if (Object.keys(propsToUpdate).length > 0) this.updateProps(propsToUpdate);
1832
+ else this.machine.notify();
1833
+ });
1834
+ });
1835
+ this.subscribedToField = true;
1836
+ } else {
1837
+ const handler = () => {
1838
+ this.subscribeToFieldService();
1839
+ this.closestField.removeEventListener("fluid-primitives:field:registered", handler);
1840
+ };
1841
+ this.closestField.addEventListener("fluid-primitives:field:registered", handler);
1842
+ }
1843
+ }
1844
+ /**
1845
+ * The field measured its starting value from the server HTML; a primitive may hydrate to a
1846
+ * different state (a defaultChecked checkbox, a Select's selected option).
1847
+ */
1848
+ notifyFieldHydrated() {
1849
+ this.fieldMachine?.send({ type: "BASELINE" });
1850
+ }
1851
+ };
1852
+
1853
+ //#endregion
1854
+ //#region Resources/Private/Client/src/lib/spread-props.ts
1855
+ const lastValues = /* @__PURE__ */ new WeakMap();
1856
+ /**
1857
+ * Like `spreadProps` for a text control, but it does not assign a `value` the control already
1858
+ * shows. Any assignment, even of the same text, makes the browser treat the value as set by a
1859
+ * script, and it stops reporting `tooShort` (`minlength`) for such a value. These controls mirror
1860
+ * what the user typed into their machine, so that assignment would happen on every keystroke.
1861
+ *
1862
+ * As in `spreadProps`, a value is only considered when it changed since the last call: the control
1863
+ * can be one event ahead of the machine, and must not be reset to the machine's older value.
1864
+ */
1865
+ function spreadTextControlProps(node, attrs, scopeKey) {
1866
+ const { value, ...rest } = attrs;
1867
+ const cleanup = spreadProps(node, rest, scopeKey);
1868
+ if (!lastValues.has(node) || lastValues.get(node) !== value) {
1869
+ const next = value == null ? "" : String(value);
1870
+ if (node.value !== next) node.value = next;
1871
+ }
1872
+ lastValues.set(node, value);
1873
+ return cleanup;
1874
+ }
1875
+
1876
+ //#endregion
1877
+ //#region Resources/Private/Client/src/lib/template.ts
1878
+ /**
1879
+ * Clones the content of a `<template>` part, which must have exactly one root element, and throws
1880
+ * otherwise. It is a `DocumentFragment`, so you can append it directly: `root` stays a valid
1881
+ * reference to the element after the fragment has been emptied by the insertion.
1882
+ *
1883
+ * It works for any "clone a `<template>` and fill it with data that doesn't exist at server render
1884
+ * time" case, such as async search results, file upload previews or the rows of a FieldArray.
1885
+ */
1886
+ var Template = class extends DocumentFragment {
1887
+ /** The one root element of the clone. */
1888
+ root;
1889
+ /**
1890
+ * The client names of the independent root components found inside the clone, empty without
1891
+ * `options.value` or when there are none. Call their `mountAll()` after you insert the clone into
1892
+ * the document.
1893
+ */
1894
+ componentNames = [];
1895
+ /**
1896
+ * @param hydrator - The hydrator of the component that owns the `<template>`.
1897
+ * @param part - The name of the `<template>` part.
1898
+ * @param options - See `TemplateOptions`.
1899
+ */
1900
+ constructor(hydrator, part, options = {}) {
1901
+ super();
1902
+ this.hydrator = hydrator;
1903
+ const templateEl = hydrator.query(part);
1904
+ if (!templateEl) throw new Error(`Template: no <template> found for part "${part}".`);
1905
+ const content = templateEl.content.cloneNode(true);
1906
+ if (content.children.length !== 1) throw new Error(`Template: "${part}" must have exactly one root element, found ${content.children.length}.`);
1907
+ this.append(content);
1908
+ this.root = this.firstElementChild;
1909
+ if (options.value !== void 0) this.componentNames = hydrator.restampValue(this.root, options.value, hydrator.scopeKey(part), options.attributes);
1910
+ movePortals(this);
1911
+ }
1912
+ /** Finds the first element of `part` inside the clone. */
1913
+ query(part) {
1914
+ return this.querySelector(this.hydrator.selector(part));
1915
+ }
1916
+ /** Finds every element of `part` inside the clone. */
1917
+ queryAll(part) {
1918
+ return Array.from(this.querySelectorAll(this.hydrator.selector(part)));
1919
+ }
1920
+ };
1921
+
1922
+ //#endregion
1923
+ //#region Resources/Private/Client/src/lib/uid.ts
1924
+ /**
1925
+ * Creates an id that is unique within the page, e.g. for a part that needs an `id` to reference.
1926
+ *
1927
+ * @param prefix - Starts the id.
1928
+ */
1929
+ function uid(prefix = "f") {
1930
+ return "«" + prefix + uuid() + "»";
1931
+ }
1932
+
1933
+ //#endregion
1934
+ export { getListCollectionFromHydrationData as $, queryRootEl as A, stringifyFieldPathAsBrackets as B, toErrorArray as C, getVisibleErrorTextIds as D, getControlEl as E, renameFieldMachineForForm as F, applyClientPropConverters as G, trimArraySuffix as H, unregisterFieldMachineForForm as I, destroyComponentsWithin as J, registerClientPropConverters as K, appendFieldPathSegment as L, getFormMachineFor as M, registerFieldMachineForForm as N, hasHelperText as O, registerFormMachine as P, getHydrationData as Q, parseFieldPath as R, suppressValueMissing as S, FIELD_VALUE_CHANGE_EVENT as T, extbase as U, toRegisteredFieldName as V, DelayedIndicator as W, getGlobal as X, getComponentInstance as Y, getGlobals as Z, serializeFieldValue as _, normalizeProps$1 as a, Machine as at, resolveValidation as b, registerFieldMachine as c, connectField as d, mount as et, createFieldHandle as f, getFieldValueFromContainer as g, getFieldElement as h, spreadTextControlProps as i, AsyncList as it, getFieldMachinesFor as j, isInsideField as k, unregisterFieldMachine as l, getCurrentFieldValue as m, Template as n, toKebabCase as nt, mergeProps as o, getComparableFieldValue as p, ComponentHydrator as q, spreadProps as r, Component as rt, FieldAwareComponent as s, uid as t, mountAll as tt, FIELD_DEPENDENCY_CHANGE_EVENT as u, VALID_SNAPSHOT as v, withValueMissing as w, shouldCommit as x, getValiditySnapshot as y, prefixFieldName as z };