@vectoriox/iox-ui 5.79.0 → 5.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4387,6 +4387,101 @@ function normaliseErrors(raw) {
4387
4387
  return Object.keys(errors).length ? errors : undefined;
4388
4388
  }
4389
4389
 
4390
+ /**
4391
+ * The submit LIFECYCLE's decisions — what a form shows after the server answers, what it says when a
4392
+ * submit fails, what a client needs to validate a form, and how its submit button follows its state.
4393
+ *
4394
+ * Two runtimes act on these and must agree: `IoxFormComponent` (the engine's component path) and the
4395
+ * string-form runtime (every header / footer global and every string-rendered page —
4396
+ * vectoriox/iox-client-engine#13). The builder canvas reads `submitButtonView` too, for its Sending state.
4397
+ *
4398
+ * ⚠️ Framework-free, like the rest of forms-core: no Angular, no DOM.
4399
+ */
4400
+ /** Which of the form's four states is showing — written on the form as `data-iox-state`. */
4401
+ var FormRenderState;
4402
+ (function (FormRenderState) {
4403
+ FormRenderState["Idle"] = "idle";
4404
+ FormRenderState["Submitting"] = "submitting";
4405
+ FormRenderState["Success"] = "success";
4406
+ FormRenderState["Error"] = "error";
4407
+ })(FormRenderState || (FormRenderState = {}));
4408
+ /** The attribute every renderer writes a form's state on (the canvas switcher, both live paths). */
4409
+ const FORM_STATE_ATTRIBUTE = 'data-iox-state';
4410
+ /**
4411
+ * The server's answer → what the visitor sees. Only a confirmed success is Success; per-field verdicts
4412
+ * land on the fields; anything else (a dropped network included) is the designed Error block.
4413
+ */
4414
+ function submitOutcome(response) {
4415
+ if (response?.ok)
4416
+ return { state: FormRenderState.Success, reset: true, ...(response.redirect ? { redirect: response.redirect } : {}) };
4417
+ if (response?.errors && Object.keys(response.errors).length)
4418
+ return { state: FormRenderState.Idle, errors: response.errors, reset: false };
4419
+ return { state: FormRenderState.Error, reset: false };
4420
+ }
4421
+ /** What the live region says after a refused submit: how many fields need attention, or that it did not send. */
4422
+ function failureAnnouncement(errors) {
4423
+ const count = errors ? Object.keys(errors).length : 0;
4424
+ if (!count)
4425
+ return 'That did not send.';
4426
+ return `${count} ${count === 1 ? 'field needs' : 'fields need'} attention.`;
4427
+ }
4428
+ const FORM_FIELDS_ATTRIBUTE = 'data-iox-form-fields';
4429
+ function clientFormSpec(spec) {
4430
+ return { feedback: spec.feedback ?? FeedbackTiming.Smart, fields: [...(spec.fields ?? [])] };
4431
+ }
4432
+ /** The attribute's value back into a spec, or null when it is missing or not one. */
4433
+ function parseClientFormSpec(raw) {
4434
+ if (!raw)
4435
+ return null;
4436
+ try {
4437
+ const parsed = JSON.parse(raw);
4438
+ if (!parsed || !Array.isArray(parsed.fields))
4439
+ return null;
4440
+ return { feedback: typeof parsed.feedback === 'string' ? parsed.feedback : FeedbackTiming.Smart, fields: parsed.fields };
4441
+ }
4442
+ catch {
4443
+ return null;
4444
+ }
4445
+ }
4446
+ /** While its form sends, a submit button is busy, and disabled unless its author turned that off — ONE rule for the
4447
+ * canvas's Sending state, the component path and the string path. Its "Sending…" label is the shared CSS's, keyed on
4448
+ * the same `data-iox-state`. */
4449
+ function submitButtonView(state, disableWhileSubmitting = true) {
4450
+ const busy = state === FormRenderState.Submitting;
4451
+ return { busy, disabled: busy && disableWhileSubmitting !== false };
4452
+ }
4453
+ /** On a submit button whose author turned "Disable while sending" off. */
4454
+ const SUBMIT_KEEP_ENABLED_ATTRIBUTE = 'data-iox-keep-enabled';
4455
+ var SubmitLabelClass;
4456
+ (function (SubmitLabelClass) {
4457
+ SubmitLabelClass["Labels"] = "iox-submit__labels";
4458
+ SubmitLabelClass["Idle"] = "iox-submit__idle";
4459
+ SubmitLabelClass["Busy"] = "iox-submit__busy";
4460
+ })(SubmitLabelClass || (SubmitLabelClass = {}));
4461
+ /**
4462
+ * A submit button's words, as markup: its label, or — when it has a "While sending" label — both, stacked in one
4463
+ * cell (the button keeps the longer one's width and never jumps). The string renderer's copy of
4464
+ * `IoxSubmitButtonComponent`'s template; `escape` is the renderer's.
4465
+ */
4466
+ function submitButtonLabelsHtml(label, loadingLabel, escape) {
4467
+ if (!loadingLabel)
4468
+ return escape(label);
4469
+ return `<span class="${SubmitLabelClass.Labels}"><span class="${SubmitLabelClass.Idle}">${escape(label)}</span>` +
4470
+ `<span class="${SubmitLabelClass.Busy}">${escape(loadingLabel)}</span></span>`;
4471
+ }
4472
+ const FORM_HONEYPOT_CLASS = 'iox-form__hp';
4473
+ const FORM_ANNOUNCER_ATTRIBUTE = 'data-iox-form-announcer';
4474
+ /**
4475
+ * The honeypot and the failure announcer every form holds, as markup — what `IoxFormComponent`'s template
4476
+ * renders, for the string renderer. The honeypot is off-screen (some bots skip hidden inputs), out of the
4477
+ * Tab order and the accessibility tree; the announcer is present from the start (a live region added at
4478
+ * the moment it gains text is not reliably announced).
4479
+ */
4480
+ function formChromeHtml(honeypotField) {
4481
+ return `<div class="${FORM_HONEYPOT_CLASS}" aria-hidden="true"><input type="text" name="${honeypotField}" tabindex="-1" autocomplete="off"></div>` +
4482
+ `<div class="${FORM_HONEYPOT_CLASS}" ${FORM_ANNOUNCER_ATTRIBUTE} role="status" aria-live="assertive" aria-atomic="true"></div>`;
4483
+ }
4484
+
4390
4485
  /**
4391
4486
  * FORM PRESETS — a complete, working form as one insert: its fields, its submit label and the words of
4392
4487
  * its Success and Error blocks. ONE definition (W1.1): the builder's Forms tray builds a preset from
@@ -4663,6 +4758,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
4663
4758
  type: Injectable
4664
4759
  }] });
4665
4760
 
4761
+ const SUBMIT_BUTTON_SELECTOR = '.iox-submit';
4762
+ const FORM_SELECTOR = 'form, .iox-form';
4763
+ /**
4764
+ * Put every submit button of `form` in the form's state (vectoriox/iox-ui#42): busy (`aria-busy`) and — unless its
4765
+ * author turned it off — disabled while the form sends; itself again otherwise. The ONE applier: the component path's
4766
+ * form, the string runtime and the canvas's Sending state all call it, from the shared `submitButtonView`. A button
4767
+ * inside a nested form belongs to that form, not this one.
4768
+ */
4769
+ function syncSubmitButtons(form, state) {
4770
+ if (!form?.querySelectorAll)
4771
+ return;
4772
+ for (const button of Array.from(form.querySelectorAll(SUBMIT_BUTTON_SELECTOR))) {
4773
+ if (button.closest?.(FORM_SELECTOR) !== form)
4774
+ continue;
4775
+ const view = submitButtonView(state, !button.hasAttribute(SUBMIT_KEEP_ENABLED_ATTRIBUTE));
4776
+ if (view.busy)
4777
+ button.setAttribute('aria-busy', 'true');
4778
+ else
4779
+ button.removeAttribute('aria-busy');
4780
+ if (view.disabled)
4781
+ button.setAttribute('disabled', '');
4782
+ else
4783
+ button.removeAttribute('disabled');
4784
+ }
4785
+ }
4786
+
4666
4787
  /**
4667
4788
  * How a host makes the form actually send. The SSR engine provides one (an HttpClient POST); the
4668
4789
  * builder canvas deliberately does NOT — an author pressing Submit while designing must never file
@@ -4672,14 +4793,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
4672
4793
  const IOX_FORM_TRANSPORT = new InjectionToken('IOX_FORM_TRANSPORT');
4673
4794
  /** Where a successful submit sends the visitor. Provided by the host; absent on the canvas. */
4674
4795
  const IOX_FORM_NAVIGATOR = new InjectionToken('IOX_FORM_NAVIGATOR');
4675
- /** Which of the form's four states the canvas / runtime is showing. */
4676
- var FormRenderState;
4677
- (function (FormRenderState) {
4678
- FormRenderState["Idle"] = "idle";
4679
- FormRenderState["Submitting"] = "submitting";
4680
- FormRenderState["Success"] = "success";
4681
- FormRenderState["Error"] = "error";
4682
- })(FormRenderState || (FormRenderState = {}));
4683
4796
  /**
4684
4797
  * Directive-host Form: the real `<form>` IS the host — no wrapper, no inner/outer split. Children
4685
4798
  * (fields, layout, the submit button, and the two state blocks) render into it via `childrenHost`.
@@ -4697,6 +4810,13 @@ var FormRenderState;
4697
4810
  * which are styleable, translated and announced. Validation still runs — ours does it.
4698
4811
  */
4699
4812
  class IoxFormComponent {
4813
+ /** Which state shows. Every change puts the submit buttons in it too (busy / disabled — iox-ui#42), through the
4814
+ * ONE applier the string runtime and the canvas call. */
4815
+ set renderState(state) {
4816
+ this._renderState = state;
4817
+ syncSubmitButtons(this.host?.nativeElement, state);
4818
+ }
4819
+ get renderState() { return this._renderState; }
4700
4820
  /**
4701
4821
  * Constructor injection rather than `inject()`, for the same reason as Field: this library's
4702
4822
  * specs construct components directly. `runtime` is provided by THIS component (see
@@ -4712,7 +4832,7 @@ class IoxFormComponent {
4712
4832
  this.formId = '';
4713
4833
  /** Author-facing name — what the submissions inbox groups by. */
4714
4834
  this.name = '';
4715
- this.renderState = FormRenderState.Idle;
4835
+ this._renderState = FormRenderState.Idle;
4716
4836
  this.feedback = FeedbackTiming.Smart;
4717
4837
  /**
4718
4838
  * Move focus to the first field that failed, instead of leaving the visitor to hunt for red
@@ -4798,30 +4918,28 @@ class IoxFormComponent {
4798
4918
  this.runtime.setSubmitting(false);
4799
4919
  this.applyResponse(response);
4800
4920
  }
4801
- /** Turn the server's answer into what the visitor sees. Public so a host-owned transport can reuse it. */
4921
+ /** Turn the server's answer into what the visitor sees — the shared `submitOutcome`, which the string runtime
4922
+ * applies too. Public so a host-owned transport can reuse it. */
4802
4923
  applyResponse(response) {
4803
- if (!response?.ok) {
4804
- // Per-field verdicts go back onto the fields; anything else is a whole-form failure,
4805
- // which is what the designed Error block is for.
4806
- if (response?.errors) {
4807
- this.runtime.applyErrors(response.errors);
4808
- this.renderState = FormRenderState.Idle;
4809
- this.announce(response.errors);
4810
- if (this.focusFirstProblem)
4811
- this.focusFirstInvalid();
4812
- return;
4813
- }
4814
- this.renderState = FormRenderState.Error;
4924
+ const outcome = submitOutcome(response);
4925
+ this.renderState = outcome.state;
4926
+ if (outcome.errors) {
4927
+ this.runtime.applyErrors(outcome.errors);
4928
+ this.announce(outcome.errors);
4929
+ if (this.focusFirstProblem)
4930
+ this.focusFirstInvalid();
4931
+ return;
4932
+ }
4933
+ if (outcome.state === FormRenderState.Error) {
4815
4934
  this.announce(null);
4816
4935
  return;
4817
4936
  }
4818
- this.renderState = FormRenderState.Success;
4819
4937
  // Clear the values so a visitor who stays on the page does not see their old answers behind
4820
4938
  // the success message, and cannot resubmit them by pressing enter.
4821
4939
  this.runtime.reset();
4822
4940
  this.resetControls();
4823
- if (response.redirect && this.navigate)
4824
- this.navigate(response.redirect);
4941
+ if (outcome.redirect && this.navigate)
4942
+ this.navigate(outcome.redirect);
4825
4943
  }
4826
4944
  /** The honeypot input's value, if the host rendered one. Absent reads as empty — never as spam. */
4827
4945
  honeypotValue() {
@@ -4843,10 +4961,7 @@ class IoxFormComponent {
4843
4961
  announce(errors) {
4844
4962
  if (!this.announceFailure)
4845
4963
  return;
4846
- const count = errors ? Object.keys(errors).length : 0;
4847
- const next = count
4848
- ? `${count} ${count === 1 ? 'field needs' : 'fields need'} attention.`
4849
- : 'That did not send.';
4964
+ const next = failureAnnouncement(errors);
4850
4965
  this.announcement = '';
4851
4966
  // A microtask is enough for the empty value to be committed before the new one lands.
4852
4967
  Promise.resolve().then(() => { this.announcement = next; });
@@ -4880,7 +4995,7 @@ class IoxFormComponent {
4880
4995
  aria-live="assertive"
4881
4996
  aria-atomic="true">{{ announcement }}</div>
4882
4997
  <ng-container #childrenHost></ng-container>
4883
- `, isInline: true, styles: [":host{box-sizing:border-box}.iox-form__hp{position:absolute;inset-inline-start:-9999px;width:1px;height:1px;overflow:hidden}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
4998
+ `, isInline: true, styles: [":host{box-sizing:border-box}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
4884
4999
  }
4885
5000
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: IoxFormComponent, decorators: [{
4886
5001
  type: Component,
@@ -4904,7 +5019,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
4904
5019
  '[attr.data-iox-state]': 'renderState',
4905
5020
  '[attr.aria-busy]': "renderState === 'submitting' ? 'true' : null",
4906
5021
  '(submit)': 'onSubmit($event)',
4907
- }, providers: [FormRuntimeService], standalone: false, styles: [":host{box-sizing:border-box}.iox-form__hp{position:absolute;inset-inline-start:-9999px;width:1px;height:1px;overflow:hidden}\n"] }]
5022
+ }, providers: [FormRuntimeService], standalone: false, styles: [":host{box-sizing:border-box}\n"] }]
4908
5023
  }], ctorParameters: () => [{ type: FormRuntimeService }, { type: i0.ElementRef }, { type: undefined, decorators: [{
4909
5024
  type: Optional
4910
5025
  }, {
@@ -5398,10 +5513,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
5398
5513
  *
5399
5514
  * It carries no destination configuration of its own — "what happens with the data" is an ordered
5400
5515
  * action list on the FORM (decision D9), so there is nothing here to wire and nothing to get out of
5401
- * sync. The button owns only its own appearance and its busy state.
5516
+ * sync. The button owns only its own appearance.
5402
5517
  *
5403
- * While submitting it shows `loadingLabel` and is disabled, which is the whole double-submit
5404
- * defence on the client side (the server's rate limit is the real one).
5518
+ * It follows its FORM's state (vectoriox/iox-ui#42): its two labels are both in the markup and the shared
5519
+ * iox-forms.css shows "While sending" while the form's `data-iox-state` is submitting — on the canvas's Sending
5520
+ * state, the component path and the string path alike; busy and disabled are written by the ONE applier
5521
+ * (`syncSubmitButtons`) the form's runtimes call. Before, the button switched only on a `submitting` input
5522
+ * nothing ever wrote. The string renderer writes the same markup (forms-core `submitButtonLabelsHtml`).
5405
5523
  */
5406
5524
  class IoxSubmitButtonComponent {
5407
5525
  constructor() {
@@ -5409,19 +5527,17 @@ class IoxSubmitButtonComponent {
5409
5527
  this.label = 'Submit';
5410
5528
  this.loadingLabel = 'Sending…';
5411
5529
  this.disableWhileSubmitting = true;
5412
- /** Driven by the form's lifecycle, not by the author. */
5413
- this.submitting = false;
5414
5530
  }
5415
5531
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: IoxSubmitButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5416
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.31", type: IoxSubmitButtonComponent, isStandalone: false, selector: "button[iox-submit]", inputs: { nodeId: "nodeId", label: "label", loadingLabel: "loadingLabel", disableWhileSubmitting: "disableWhileSubmitting", submitting: "submitting" }, host: { properties: { "class": "'iox-submit iox-node-' + nodeId", "attr.type": "'submit'", "attr.disabled": "submitting && disableWhileSubmitting ? \"\" : null", "attr.aria-busy": "submitting ? \"true\" : null" } }, ngImport: i0, template: `{{ submitting && loadingLabel ? loadingLabel : label }}`, isInline: true, styles: [":host{box-sizing:border-box;cursor:pointer}\n"] }); }
5532
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", type: IoxSubmitButtonComponent, isStandalone: false, selector: "button[iox-submit]", inputs: { nodeId: "nodeId", label: "label", loadingLabel: "loadingLabel", disableWhileSubmitting: "disableWhileSubmitting" }, host: { properties: { "class": "'iox-submit iox-node-' + nodeId", "attr.type": "'submit'", "attr.data-iox-keep-enabled": "disableWhileSubmitting === false ? \"\" : null" } }, ngImport: i0, template: `@if (loadingLabel) {<span class="iox-submit__labels"><span class="iox-submit__idle">{{ label }}</span><span class="iox-submit__busy">{{ loadingLabel }}</span></span>} @else {<ng-container>{{ label }}</ng-container>}`, isInline: true, styles: [":host{box-sizing:border-box;cursor:pointer}\n"] }); }
5417
5533
  }
5418
5534
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: IoxSubmitButtonComponent, decorators: [{
5419
5535
  type: Component,
5420
- args: [{ selector: 'button[iox-submit]', template: `{{ submitting && loadingLabel ? loadingLabel : label }}`, host: {
5536
+ args: [{ selector: 'button[iox-submit]', template: `@if (loadingLabel) {<span class="iox-submit__labels"><span class="iox-submit__idle">{{ label }}</span><span class="iox-submit__busy">{{ loadingLabel }}</span></span>} @else {<ng-container>{{ label }}</ng-container>}`, host: {
5421
5537
  '[class]': "'iox-submit iox-node-' + nodeId",
5422
5538
  '[attr.type]': "'submit'",
5423
- '[attr.disabled]': 'submitting && disableWhileSubmitting ? "" : null',
5424
- '[attr.aria-busy]': 'submitting ? "true" : null',
5539
+ // forms-core SUBMIT_KEEP_ENABLED_ATTRIBUTE — read by `syncSubmitButtons`.
5540
+ '[attr.data-iox-keep-enabled]': 'disableWhileSubmitting === false ? "" : null',
5425
5541
  }, standalone: false, styles: [":host{box-sizing:border-box;cursor:pointer}\n"] }]
5426
5542
  }], propDecorators: { nodeId: [{
5427
5543
  type: Input
@@ -5431,8 +5547,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
5431
5547
  type: Input
5432
5548
  }], disableWhileSubmitting: [{
5433
5549
  type: Input
5434
- }], submitting: [{
5435
- type: Input
5436
5550
  }] } });
5437
5551
 
5438
5552
  /**
@@ -6029,6 +6143,84 @@ function partClassName(type, partKey) {
6029
6143
  return PART_CLASS_NAMES[type]?.[partKey];
6030
6144
  }
6031
6145
 
6146
+ /**
6147
+ * WHERE a state rule lands — the ONE selector planner both renderers emit state and part rules through
6148
+ * (vectoriox/iox-client-engine#14). Before it the builder and the engine each kept a copy: the builder matched a
6149
+ * structural state (first / last / odd / even child) on the OUTER element, the engine on `.iox-node` — on the
6150
+ * engine's component path the INNER element, the only child of its host, so "last child" matched every item; and
6151
+ * the engine dropped every part state but Wrong / Accepted while the builder applied a part's hover at all times.
6152
+ *
6153
+ * Returns full selectors (with their dots). Framework-free.
6154
+ */
6155
+ /**
6156
+ * Properties that govern how a node sits in its PARENT's layout (flex / grid item, block child) — they live on the
6157
+ * OUTER element: the component's host, or the one element the string renderer and the canvas's collapsed hosts write.
6158
+ * Everything else is "inner". One list for both renderers (it was typed twice).
6159
+ */
6160
+ const OUTER_STYLE_PROPS = new Set([
6161
+ 'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft',
6162
+ 'alignSelf', 'flexGrow', 'flexShrink', 'flexBasis', 'order',
6163
+ 'gridColumn', 'gridRow', 'justifySelf',
6164
+ 'width',
6165
+ ]);
6166
+ /** Structural pseudo-classes — matched on the element that IS the sibling among siblings (the outer). */
6167
+ const STRUCTURAL_STATES = ['first-child', 'last-child', 'nth-child(odd)', 'nth-child(even)'];
6168
+ /** States whose pseudo-class lives on the PARENT (`parent-hover` → the parent's `:hover`). */
6169
+ const RELATIONAL_STATE_PSEUDO = { 'parent-hover': 'hover' };
6170
+ function isStructuralState(state) {
6171
+ return STRUCTURAL_STATES.includes(state);
6172
+ }
6173
+ /**
6174
+ * The selectors of `cssId`'s rule for `state`, or null when the state cannot be scoped yet (a relational state with
6175
+ * no parent, a targeted one with no scope — never an unscoped, page-global rule).
6176
+ *
6177
+ * A structural state is matched on the outer element, and its styles reach the node's own element whether that is
6178
+ * the same element (the string path, a one-element node, the canvas's collapsed hosts) or inside it (a component's
6179
+ * host, a Repeater's row wrapper); its outer props stay on the outer element. Every other state is the node's own.
6180
+ */
6181
+ function planStateRule(cssId, state, scope = {}) {
6182
+ if (TARGETED_STATE_SUFFIX[state]) {
6183
+ const body = scope.scopeId && scope.triggerId ? targetedStateSelector(state, scope.scopeId, scope.triggerId, cssId) : null;
6184
+ return body ? { inner: `.${body}` } : null;
6185
+ }
6186
+ const relational = RELATIONAL_STATE_PSEUDO[state];
6187
+ if (relational)
6188
+ return scope.parentId ? { inner: `.iox-node-${scope.parentId}:${relational} .iox-node-${cssId}` } : null;
6189
+ const validationClass = VALIDATION_STATE_CLASS[state];
6190
+ if (validationClass)
6191
+ return { inner: `.${buildPartSelector(cssId, undefined, validationClass)}` };
6192
+ if (isStructuralState(state)) {
6193
+ const outer = `.iox-outer-${cssId}:${state}`;
6194
+ return { inner: `${outer}.iox-node-${cssId}, ${outer} .iox-node-${cssId}`, outer };
6195
+ }
6196
+ return { inner: `.iox-node-${cssId}${stateSelectorSuffix(state)}` };
6197
+ }
6198
+ /**
6199
+ * The selector of a PART's rule (a Field's label, control, help, error — `PART_CLASS_NAMES`), optionally in a state:
6200
+ * Wrong / Accepted and a component state on the NODE (that is where the class / attribute is), Hover / Pressed /
6201
+ * Focus on the PART itself. Null for a state a part does not take (structural, relational, targeted).
6202
+ */
6203
+ function planPartRule(cssId, partClass, state) {
6204
+ if (!state)
6205
+ return `.${buildPartSelector(cssId, partClass)}`;
6206
+ const validationClass = VALIDATION_STATE_CLASS[state];
6207
+ if (validationClass)
6208
+ return `.${buildPartSelector(cssId, partClass, validationClass)}`;
6209
+ if (isStructuralState(state) || RELATIONAL_STATE_PSEUDO[state] || TARGETED_STATE_SUFFIX[state])
6210
+ return null;
6211
+ const suffix = stateSelectorSuffix(state);
6212
+ // An attribute state (`current`, `on`) is the node's; a pseudo-class is the part's own.
6213
+ return suffix.startsWith('[') ? `.iox-node-${cssId}${suffix} .${partClass}` : `.iox-node-${cssId} .${partClass}${suffix}`;
6214
+ }
6215
+ /** Split a style map into the node's own props and its OUTER_STYLE_PROPS. */
6216
+ function splitOuterProps(styles) {
6217
+ const inner = {};
6218
+ const outer = {};
6219
+ for (const [k, v] of Object.entries(styles))
6220
+ (OUTER_STYLE_PROPS.has(k) ? outer : inner)[k] = v;
6221
+ return { inner, outer };
6222
+ }
6223
+
6032
6224
  /**
6033
6225
  * "This link is the page you're on" (CC1, N2). ONE decision for the builder canvas and both engine
6034
6226
  * paths: a link whose target is the current page renders `aria-current="page"` — in the raw SSR too —
@@ -6284,6 +6476,96 @@ function clipFrames(params, inlineSign = 1) {
6284
6476
  return [{ opacity: 1, clipPath: revealClipPath(0, from, inlineSign) }, { opacity: 1, clipPath: revealClipPath(SHOWN_PERCENT, from, inlineSign) }];
6285
6477
  }
6286
6478
  const INLINE_FROMS = new Set([SceneRevealFrom.Start, SceneRevealFrom.End]);
6479
+ /** The id every interaction run's Animation carries — how a re-trigger finds the runs it interrupts. */
6480
+ const INTERACTION_ANIMATION_ID = 'iox-interaction';
6481
+ /**
6482
+ * What a run that has ended leaves on its element (vectoriox/iox-client-engine#12, iox-ui#41) — ONE rule for the
6483
+ * canvas and the live site.
6484
+ *
6485
+ * `Release`: the last frame IS the element's natural state (an entrance, an exit played back, a one-shot like a
6486
+ * rotate). The run is cancelled and no frame is written inline, so the stylesheet applies again — the element
6487
+ * style's `:hover` / `:active` transforms included. Before, the last frame (`transform: translateY(0)`) was locked
6488
+ * inline on the site and kept by `fill: both` on the canvas, and an element that had entered never pressed again.
6489
+ *
6490
+ * `Hold`: the last frame HIDES the element (an exit, an entrance played back): it is kept, inline.
6491
+ */
6492
+ var SettleMode;
6493
+ (function (SettleMode) {
6494
+ SettleMode["Release"] = "release";
6495
+ SettleMode["Hold"] = "hold";
6496
+ })(SettleMode || (SettleMode = {}));
6497
+ function settleModeFor(type, reverse = false) {
6498
+ const hides = reverse ? ENTRANCE_ACTION_TYPES.has(type) : EXIT_ACTION_TYPES.has(type);
6499
+ return hides ? SettleMode.Hold : SettleMode.Release;
6500
+ }
6501
+ /** Every CSS property a run's frames touch. */
6502
+ function framePropertyNames(frames) {
6503
+ const names = new Set();
6504
+ for (const frame of frames ?? [])
6505
+ for (const key of Object.keys(frame))
6506
+ names.add(keyframePropertyName(key));
6507
+ return [...names];
6508
+ }
6509
+ /**
6510
+ * Leave `el` as a run that has ended — or one that never plays, under reduced motion — should (`SettleMode`). The
6511
+ * frame is written (Hold) or released (Release) BEFORE `anim` is cancelled, so nothing flashes between the two.
6512
+ */
6513
+ function settleInteraction(el, frames, mode, options = {}, anim) {
6514
+ if (mode === SettleMode.Hold) {
6515
+ for (const [prop, value] of keyframeStyleEntries(finalKeyframe(frames))) {
6516
+ if (!options.skip?.has(prop))
6517
+ el.style.setProperty(prop, value);
6518
+ }
6519
+ }
6520
+ else {
6521
+ for (const prop of framePropertyNames(frames))
6522
+ el.style.removeProperty(prop);
6523
+ }
6524
+ anim?.cancel();
6525
+ }
6526
+ /** The keyframe properties an Animation animates (camelCase, as `getKeyframes` names them). */
6527
+ function animatedKeys(anim) {
6528
+ const keyframes = anim.effect?.getKeyframes?.() ?? [];
6529
+ const skip = new Set(['offset', 'computedOffset', 'easing', 'composite']);
6530
+ return keyframes.flatMap(k => Object.keys(k).filter(key => !skip.has(key)));
6531
+ }
6532
+ /**
6533
+ * Interrupt (Emil 52/59/62): the interaction runs in flight on `el` over the same properties are stopped — never
6534
+ * stacked — and the new run starts from what the element shows NOW (its presentation value), not from frame 0: a
6535
+ * hover-out halfway through the hover-in turns back from where it is, a re-trigger continues. Untouched when
6536
+ * nothing is running (the run starts from its own first frame, as before).
6537
+ */
6538
+ function framesFromLiveValue(el, frames) {
6539
+ const first = frames[0];
6540
+ if (!first)
6541
+ return [...frames];
6542
+ const keys = new Set(Object.keys(first));
6543
+ const running = (el.getAnimations?.() ?? [])
6544
+ .filter(a => a.id === INTERACTION_ANIMATION_ID && animatedKeys(a).some(k => keys.has(k)));
6545
+ if (!running.length)
6546
+ return [...frames];
6547
+ const style = el.ownerDocument?.defaultView?.getComputedStyle(el);
6548
+ const live = { ...first };
6549
+ for (const key of keys) {
6550
+ const value = style?.getPropertyValue(keyframePropertyName(key));
6551
+ if (value)
6552
+ live[key] = value;
6553
+ }
6554
+ for (const anim of running)
6555
+ anim.cancel();
6556
+ return [live, ...frames.slice(1)];
6557
+ }
6558
+ /**
6559
+ * Play an element action and settle it when it ends — THE call both runtimes make for an animated action: it starts
6560
+ * from the live value (`framesFromLiveValue`, inside `animateAction`) and ends by the ONE settle rule. A run that is
6561
+ * interrupted is never settled: the run that interrupted it owns the element now.
6562
+ */
6563
+ function playInteraction(el, action, frames, timing, options = {}) {
6564
+ const anim = animateAction(el, action, frames, { fill: 'both', ...timing }, { reverse: options.reverse });
6565
+ const mode = settleModeFor(action.type, options.reverse);
6566
+ anim.finished?.then(() => settleInteraction(el, frames, mode, options, anim)).catch(() => { });
6567
+ return anim;
6568
+ }
6287
6569
  /**
6288
6570
  * Start an element action's animation: THE call both runtimes make. Most actions are one `element.animate`; a text
6289
6571
  * reveal plays its pieces and a clip reveal opens from the side the page's direction makes of Start and End, then takes
@@ -6297,15 +6579,22 @@ function animateAction(el, action, frames, timing, options = {}) {
6297
6579
  return reveal;
6298
6580
  }
6299
6581
  if (action.type !== RevealActionType.Clip)
6300
- return el.animate(frames, timing);
6582
+ return tagged(el.animate(framesFromLiveValue(el, frames), timing));
6301
6583
  const rtl = INLINE_FROMS.has(revealFromOf(action.params)) && el.ownerDocument.defaultView?.getComputedStyle(el).direction === 'rtl';
6302
6584
  const mirrored = rtl ? clipFrames(action.params, -1) : frames;
6303
6585
  const played = rtl && options.reverse ? [...mirrored].reverse() : mirrored;
6304
- const anim = el.animate(played, timing);
6586
+ const anim = tagged(el.animate(framesFromLiveValue(el, played), timing));
6305
6587
  if (!options.reverse)
6306
6588
  anim.addEventListener('finish', () => anim.cancel(), { once: true });
6307
6589
  return anim;
6308
6590
  }
6591
+ function tagged(anim) {
6592
+ try {
6593
+ anim.id = INTERACTION_ANIMATION_ID;
6594
+ }
6595
+ catch { /* a stub without an id */ }
6596
+ return anim;
6597
+ }
6309
6598
  /** `translateX` → `translate-x`, `opacity` → `opacity`: the CSS property name of a keyframe key. */
6310
6599
  function keyframePropertyName(key) {
6311
6600
  return key.replace(/([A-Z])/g, '-$1').toLowerCase();
@@ -8179,6 +8468,251 @@ function playDualLayerTransition(resolved, layers) {
8179
8468
  return inc.animate(resolved.inFrames, resolved.inOptions).finished.then(() => undefined, () => undefined);
8180
8469
  }
8181
8470
 
8471
+ const FIELD_ATTRIBUTE = 'data-iox-field';
8472
+ const CONTROL_CLASS = 'iox-field__control';
8473
+ const ERROR_CLASS = 'iox-field__error';
8474
+ const HELP_CLASS = 'iox-field__help';
8475
+ const INVALID_CLASS = 'iox-invalid';
8476
+ const BOOTED = '__ioxStringForm';
8477
+ /**
8478
+ * A form the SSR engine's string renderer wrote — every header / footer global, every string-rendered page —
8479
+ * run with the behaviour `IoxFormComponent` gives a component-path form (vectoriox/iox-client-engine#13): the
8480
+ * forms-core runtime decides when an error shows, a valid submit POSTs through the host's transport with the
8481
+ * anti-spam fields, the form moves idle → submitting → success / error on `data-iox-state` (the shared CSS shows
8482
+ * the designed block), the first problem takes focus and the failure is announced. Nothing posts natively.
8483
+ *
8484
+ * Its fields come from the form itself (`data-iox-form-fields`, the spec minus its destinations), so the
8485
+ * runtime needs no node tree. Idempotent per element.
8486
+ */
8487
+ class StringFormRuntime {
8488
+ constructor(form, spec, options = {}) {
8489
+ this.form = form;
8490
+ this.spec = spec;
8491
+ this.options = options;
8492
+ this.state = INITIAL_FORM_STATE;
8493
+ this.inFlight = false;
8494
+ this.renderedAt = Date.now();
8495
+ this.listeners = [];
8496
+ this.messages = options.messages ?? defaultMessageResolver;
8497
+ this.listen('input', e => this.onEdit(e));
8498
+ this.listen('change', e => this.onEdit(e));
8499
+ this.listen('focusout', e => this.onLeave(e));
8500
+ this.listen('submit', e => this.onSubmit(e));
8501
+ form[BOOTED] = this;
8502
+ }
8503
+ destroy() {
8504
+ for (const [type, fn] of this.listeners)
8505
+ this.form.removeEventListener(type, fn);
8506
+ this.listeners.length = 0;
8507
+ delete this.form[BOOTED];
8508
+ }
8509
+ /** The visitor's answers, keyed by the spec's field names. */
8510
+ values() {
8511
+ const out = {};
8512
+ for (const field of this.spec.fields)
8513
+ out[field.name] = this.valueOf(field);
8514
+ return out;
8515
+ }
8516
+ listen(type, fn) {
8517
+ this.form.addEventListener(type, fn);
8518
+ this.listeners.push([type, fn]);
8519
+ }
8520
+ fieldNameOf(event) {
8521
+ const target = event.target;
8522
+ if (!target?.classList?.contains(CONTROL_CLASS))
8523
+ return null;
8524
+ return target.closest(`[${FIELD_ATTRIBUTE}]`)?.getAttribute(FIELD_ATTRIBUTE) ?? null;
8525
+ }
8526
+ onEdit(event) {
8527
+ const name = this.fieldNameOf(event);
8528
+ if (!name)
8529
+ return;
8530
+ this.state = onFieldChange(this.state, name, this.values(), this.fullSpec());
8531
+ this.render();
8532
+ }
8533
+ onLeave(event) {
8534
+ const name = this.fieldNameOf(event);
8535
+ if (!name)
8536
+ return;
8537
+ this.state = onFieldBlur(this.state, name, this.values(), this.fullSpec());
8538
+ this.render();
8539
+ }
8540
+ onSubmit(event) {
8541
+ // Never the browser's own submission: it would reload the page with the answers in the address and send nothing.
8542
+ event.preventDefault();
8543
+ if (this.inFlight)
8544
+ return;
8545
+ const values = this.values();
8546
+ this.state = onSubmitAttempt(this.state, values, this.fullSpec());
8547
+ this.render();
8548
+ if (Object.keys(this.state.errors).length) {
8549
+ this.announce(this.state.errors);
8550
+ this.focusFirstInvalid();
8551
+ return;
8552
+ }
8553
+ if (!this.options.transport)
8554
+ return;
8555
+ void this.send(values);
8556
+ }
8557
+ async send(values) {
8558
+ this.inFlight = true;
8559
+ this.setRenderState(FormRenderState.Submitting);
8560
+ let response;
8561
+ try {
8562
+ response = await this.options.transport({
8563
+ formId: this.form.getAttribute('data-iox-form') ?? '',
8564
+ values: withAntiSpamFields(values, this.renderedAt, this.honeypotValue()),
8565
+ });
8566
+ }
8567
+ catch {
8568
+ // The call never reached the server — the Error block, never Success.
8569
+ response = { ok: false };
8570
+ }
8571
+ this.inFlight = false;
8572
+ this.apply(response);
8573
+ }
8574
+ apply(response) {
8575
+ const outcome = submitOutcome(response);
8576
+ this.setRenderState(outcome.state);
8577
+ if (outcome.errors) {
8578
+ this.state = { ...this.state, submitted: true, submitting: false, errors: { ...outcome.errors } };
8579
+ this.render();
8580
+ this.announce(outcome.errors);
8581
+ this.focusFirstInvalid();
8582
+ return;
8583
+ }
8584
+ if (outcome.state === FormRenderState.Error) {
8585
+ this.announce(null);
8586
+ return;
8587
+ }
8588
+ this.form.reset();
8589
+ this.state = resetFormState();
8590
+ this.render();
8591
+ if (outcome.redirect)
8592
+ this.options.navigate?.(outcome.redirect);
8593
+ }
8594
+ setRenderState(state) {
8595
+ this.form.setAttribute(FORM_STATE_ATTRIBUTE, state);
8596
+ if (state === FormRenderState.Submitting)
8597
+ this.form.setAttribute('aria-busy', 'true');
8598
+ else
8599
+ this.form.removeAttribute('aria-busy');
8600
+ syncSubmitButtons(this.form, state);
8601
+ this.state = { ...this.state, submitting: state === FormRenderState.Submitting };
8602
+ }
8603
+ fullSpec() {
8604
+ return { formId: '', name: '', feedback: this.spec.feedback, fields: this.spec.fields, actions: [], antiSpam: { honeypot: true, minFillMs: 0 } };
8605
+ }
8606
+ fieldElement(name) {
8607
+ // Compared, never interpolated into a selector: a field key may hold characters a selector chokes on.
8608
+ for (const el of Array.from(this.form.querySelectorAll(`[${FIELD_ATTRIBUTE}]`))) {
8609
+ if (el.getAttribute(FIELD_ATTRIBUTE) === name && el.closest('form') === this.form)
8610
+ return el;
8611
+ }
8612
+ return null;
8613
+ }
8614
+ controlsOf(field) {
8615
+ return Array.from(field.querySelectorAll(`.${CONTROL_CLASS}`));
8616
+ }
8617
+ valueOf(spec) {
8618
+ const el = this.fieldElement(spec.name);
8619
+ if (!el)
8620
+ return undefined;
8621
+ const controls = this.controlsOf(el);
8622
+ const first = controls[0];
8623
+ if (!first)
8624
+ return undefined;
8625
+ if (first.type === 'radio')
8626
+ return controls.find(c => c.checked)?.value ?? '';
8627
+ if (first.type === 'checkbox')
8628
+ return first.checked;
8629
+ if (first.type === 'range')
8630
+ return Number(first.value);
8631
+ return first.value;
8632
+ }
8633
+ /** Every field's error, aria-invalid, describedby and invalid class — as `IoxFieldComponent` renders them. */
8634
+ render() {
8635
+ for (const field of this.spec.fields) {
8636
+ const el = this.fieldElement(field.name);
8637
+ if (!el)
8638
+ continue;
8639
+ const error = this.state.errors[field.name];
8640
+ const shown = !!error && shouldShowError(field.name, this.state, this.spec.feedback);
8641
+ const text = shown ? this.messages(error.messageKey, error.params) : '';
8642
+ el.classList.toggle(INVALID_CLASS, shown && !!text);
8643
+ this.renderError(el, shown ? text : '');
8644
+ }
8645
+ }
8646
+ renderError(field, text) {
8647
+ const controls = this.controlsOf(field);
8648
+ const id = (controls[0]?.id || `iox-field-${field.getAttribute(FIELD_ATTRIBUTE)}`) + '-error';
8649
+ let p = field.querySelector(`.${ERROR_CLASS}`);
8650
+ if (text) {
8651
+ if (!p) {
8652
+ p = field.ownerDocument.createElement('p');
8653
+ p.className = ERROR_CLASS;
8654
+ p.id = id;
8655
+ p.setAttribute('role', 'alert');
8656
+ field.appendChild(p);
8657
+ }
8658
+ p.textContent = text;
8659
+ }
8660
+ else {
8661
+ p?.remove();
8662
+ }
8663
+ const helpId = field.querySelector(`.${HELP_CLASS}`)?.id;
8664
+ const describedBy = [helpId, text ? id : ''].filter(Boolean).join(' ');
8665
+ for (const control of controls) {
8666
+ if (text)
8667
+ control.setAttribute('aria-invalid', 'true');
8668
+ else
8669
+ control.removeAttribute('aria-invalid');
8670
+ if (describedBy)
8671
+ control.setAttribute('aria-describedby', describedBy);
8672
+ else
8673
+ control.removeAttribute('aria-describedby');
8674
+ }
8675
+ }
8676
+ announce(errors) {
8677
+ const region = this.form.querySelector(`[${FORM_ANNOUNCER_ATTRIBUTE}]`);
8678
+ if (!region)
8679
+ return;
8680
+ // Cleared first: a live region speaks on CHANGE, and the same failure twice must be said twice.
8681
+ region.textContent = '';
8682
+ const next = failureAnnouncement(errors);
8683
+ Promise.resolve().then(() => { region.textContent = next; });
8684
+ }
8685
+ focusFirstInvalid() {
8686
+ for (const field of this.spec.fields) {
8687
+ if (!this.state.errors[field.name])
8688
+ continue;
8689
+ const el = this.fieldElement(field.name);
8690
+ this.controlsOf(el ?? this.form)[0]?.focus();
8691
+ return;
8692
+ }
8693
+ }
8694
+ honeypotValue() {
8695
+ return this.form.querySelector(`input[name="${HONEYPOT_FIELD}"]`)?.value ?? '';
8696
+ }
8697
+ }
8698
+ /** The runtime already running a form element, if any. */
8699
+ function stringFormRuntimeOf(form) {
8700
+ return form?.[BOOTED] ?? null;
8701
+ }
8702
+ /**
8703
+ * Run a string-rendered form (one carrying `data-iox-form-fields`) — once: a form already running is returned as is.
8704
+ * Null for an element that is not one (a component-path form never carries the attribute; it runs itself).
8705
+ */
8706
+ function attachStringForm(form, options = {}) {
8707
+ if (!form || form.tagName !== 'FORM')
8708
+ return null;
8709
+ const running = stringFormRuntimeOf(form);
8710
+ if (running)
8711
+ return running;
8712
+ const spec = parseClientFormSpec(form.getAttribute(FORM_FIELDS_ATTRIBUTE));
8713
+ return spec ? new StringFormRuntime(form, spec, options) : null;
8714
+ }
8715
+
8182
8716
  /*
8183
8717
  * Public API Surface of iox-ui
8184
8718
  */
@@ -8187,5 +8721,5 @@ function playDualLayerTransition(resolved, layers) {
8187
8721
  * Generated bundle index. Do not edit.
8188
8722
  */
8189
8723
 
8190
- export { A11yAttribute, A11yTrait, ACCESSIBLE_NAME_TYPES, AOSService, ARIA_CURRENT_PAGE, BACKDROP_TWIN_KEY, BACKDROP_WHOLE_KEY, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, COMPONENT_STATE_SELECTOR, CONTAINER_HTML_TAGS, CONTEXT_ACTION_TYPES, CURRENT_LINK_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ComponentState, ContainerComponent, ContextStore, DECORATIVE_TYPES, DEFAULT_GALLERY_SCHEDULER, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DIALOG_CLOSE_ABOVE_ATTRIBUTE, DIALOG_NODE_TYPE, DeclarationContext, ENTRANCE_ACTION_TYPES, ENTRANCE_FALLBACK_CSS, ENTRANCE_NOSCRIPT_CSS, ENTRANCE_PRESTATE_ATTRIBUTE, EXIT_ACTION_TYPES, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION, HOVER_STATES, INTERACTION_HIDDEN_ATTRIBUTE, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxDialogComponent, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, IoxVideoComponent, LAST_EMITTED_STATES, LEGACY_DIRECTION_KEY, LEGACY_HOVER_TRANSITION, LEGACY_SWIPER_CONTROLS, LinkedContainerComponent, MANAGED_A11Y_ATTRIBUTES, MAX_LINES_KEY, NAMED_GROUP_ROLES, NO_PAGE_TRANSITION, ON_STATE_ROLES, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PART_PAGINATION_MODIFIER, PHONE_TRANSITION_QUERY, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, REDUCED_MOTION_QUERY, RevealActionType, SCROLL_EASINGS, SELF_PLAYING_TRIGGERS, SLIDER_INPUTS_ATTRIBUTE, SLIDER_INPUT_NAMES, STATE_PSEUDO_CLASS, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_A11Y_OPTIONS, SWIPER_RESPONSIVE_TRAITS, SWIPER_TRIGGER_EVENTS, ScrollEasing, SectionComponent, SwiperModuleName, TARGETED_STATE_SUFFIX, TEXT_ALIGN_KEY, TEXT_DIRECTION_KEY, TRANSITION_FEELS, TextAlign, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, VisibilityActionType, activeActions, animateAction, applyBindingPipes, applyFeel, bootSlider, breakpointDef, breakpointForWidth, breakpointQueryFor, buildInteractionKeyframes, buildPartSelector, buildSwiperBreakpoints, buildTargetedStateSelector, buildTargetedSuffixSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, closeAboveMediaQuery, collectContextSlotKeys, compileDeclarations, compilePageStyle, componentStatesFor, composeVirtualTraits, containerHtmlTag, contextActionEffect, contextSlotKeys, countOverrides, declaredInputNames, decomposeBackdropFilter, defaultHoverTransition, defaultMessageResolver, deriveSeoFromContent, describePageStyle, describeSeoFields, dialogCloseAboveMinWidth, domHasSliderParts, effectiveActionType, effectiveTransitionId, entrancePreStateStyle, entranceStartsHidden, feelFor, finalKeyframe, getSeoValue, hasHoverState, hasOwnPageTransition, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isCurrentLink, isExternalOrFragment, isGradientPaint, isInputOverriddenAt, isLastEmittedStateKey, isOverriddenAt, isValidSlotKey, isWholeBackdropSet, keyframePropertyName, keyframeStyleEntries, legacyEnterToTransition, mediaQueryFor, mergeSwiperBreakpoints, migrateLegacyDirection, migrateLegacyTextAlign, motionAllowed, nearestCommonAncestorId, needsAccessibleName, needsGroupName, nodeA11yAttributes, nodeCssId, nodeTraitValue, nodeUsesContext, normalizeAppPath, normalizeGalleryImage, normalizeIconClass, orderStatesForEmit, pageTransitionOf, parentBreakpoint, parseUrlList, partClassName, planResponsiveRules, planScroll, planSwiperOptions, playDualLayerTransition, prefersReducedMotion, renderDefaultDeclarations, resetPageStyleField, resetSeoField, resolveBoundValue, resolveContextValue, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveSeo, resolveStateStyles, resolveStyleProps, resolveToggleVisibility, resolveValueChain, rewriteViewportUnits, safeSetInput, serializeA11yAttributes, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, sliderControlElements, sliderInputsFromProps, sliderInputsOf, stateNamesIn, statePseudoClass, stateSelectorSuffix, stripSuppressedProps, swiperAutoplayAllowed, swiperLoopOptions, swiperModuleNames, syncNodeA11yAttributes, targetedStateSelector, viewportTransitionContext, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
8724
+ export { A11yAttribute, A11yTrait, ACCESSIBLE_NAME_TYPES, AOSService, ARIA_CURRENT_PAGE, BACKDROP_TWIN_KEY, BACKDROP_WHOLE_KEY, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, COMPONENT_STATE_SELECTOR, CONTAINER_HTML_TAGS, CONTEXT_ACTION_TYPES, CURRENT_LINK_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ComponentState, ContainerComponent, ContextStore, DECORATIVE_TYPES, DEFAULT_GALLERY_SCHEDULER, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DIALOG_CLOSE_ABOVE_ATTRIBUTE, DIALOG_NODE_TYPE, DeclarationContext, ENTRANCE_ACTION_TYPES, ENTRANCE_FALLBACK_CSS, ENTRANCE_NOSCRIPT_CSS, ENTRANCE_PRESTATE_ATTRIBUTE, EXIT_ACTION_TYPES, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION, HOVER_STATES, INTERACTION_ANIMATION_ID, INTERACTION_HIDDEN_ATTRIBUTE, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxDialogComponent, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, IoxVideoComponent, LAST_EMITTED_STATES, LEGACY_DIRECTION_KEY, LEGACY_HOVER_TRANSITION, LEGACY_SWIPER_CONTROLS, LinkedContainerComponent, MANAGED_A11Y_ATTRIBUTES, MAX_LINES_KEY, NAMED_GROUP_ROLES, NO_PAGE_TRANSITION, ON_STATE_ROLES, OUTER_STYLE_PROPS, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PART_PAGINATION_MODIFIER, PHONE_TRANSITION_QUERY, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, REDUCED_MOTION_QUERY, RELATIONAL_STATE_PSEUDO, RevealActionType, SCROLL_EASINGS, SELF_PLAYING_TRIGGERS, SLIDER_INPUTS_ATTRIBUTE, SLIDER_INPUT_NAMES, STATE_PSEUDO_CLASS, STRUCTURAL_STATES, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_A11Y_OPTIONS, SWIPER_RESPONSIVE_TRAITS, SWIPER_TRIGGER_EVENTS, ScrollEasing, SectionComponent, SettleMode, StringFormRuntime, SwiperModuleName, TARGETED_STATE_SUFFIX, TEXT_ALIGN_KEY, TEXT_DIRECTION_KEY, TRANSITION_FEELS, TextAlign, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, VisibilityActionType, activeActions, animateAction, applyBindingPipes, applyFeel, attachStringForm, bootSlider, breakpointDef, breakpointForWidth, breakpointQueryFor, buildInteractionKeyframes, buildPartSelector, buildSwiperBreakpoints, buildTargetedStateSelector, buildTargetedSuffixSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, closeAboveMediaQuery, collectContextSlotKeys, compileDeclarations, compilePageStyle, componentStatesFor, composeVirtualTraits, containerHtmlTag, contextActionEffect, contextSlotKeys, countOverrides, declaredInputNames, decomposeBackdropFilter, defaultHoverTransition, defaultMessageResolver, deriveSeoFromContent, describePageStyle, describeSeoFields, dialogCloseAboveMinWidth, domHasSliderParts, effectiveActionType, effectiveTransitionId, entrancePreStateStyle, entranceStartsHidden, feelFor, finalKeyframe, framesFromLiveValue, getSeoValue, hasHoverState, hasOwnPageTransition, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isCurrentLink, isExternalOrFragment, isGradientPaint, isInputOverriddenAt, isLastEmittedStateKey, isOverriddenAt, isStructuralState, isValidSlotKey, isWholeBackdropSet, keyframePropertyName, keyframeStyleEntries, legacyEnterToTransition, mediaQueryFor, mergeSwiperBreakpoints, migrateLegacyDirection, migrateLegacyTextAlign, motionAllowed, nearestCommonAncestorId, needsAccessibleName, needsGroupName, nodeA11yAttributes, nodeCssId, nodeTraitValue, nodeUsesContext, normalizeAppPath, normalizeGalleryImage, normalizeIconClass, orderStatesForEmit, pageTransitionOf, parentBreakpoint, parseUrlList, partClassName, planPartRule, planResponsiveRules, planScroll, planStateRule, planSwiperOptions, playDualLayerTransition, playInteraction, prefersReducedMotion, renderDefaultDeclarations, resetPageStyleField, resetSeoField, resolveBoundValue, resolveContextValue, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveSeo, resolveStateStyles, resolveStyleProps, resolveToggleVisibility, resolveValueChain, rewriteViewportUnits, safeSetInput, serializeA11yAttributes, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, settleInteraction, settleModeFor, sliderControlElements, sliderInputsFromProps, sliderInputsOf, splitOuterProps, stateNamesIn, statePseudoClass, stateSelectorSuffix, stringFormRuntimeOf, stripSuppressedProps, swiperAutoplayAllowed, swiperLoopOptions, swiperModuleNames, syncNodeA11yAttributes, syncSubmitButtons, targetedStateSelector, viewportTransitionContext, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
8191
8725
  //# sourceMappingURL=vectoriox-iox-ui.mjs.map