@vectoriox/iox-builder 11.11.0 → 11.11.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.
@@ -17,7 +17,7 @@ import { TooltipModule } from 'primeng/tooltip';
17
17
  import * as i20 from 'primeng/dialog';
18
18
  import { DialogModule } from 'primeng/dialog';
19
19
  import * as i1$1 from '@vectoriox/iox-ui';
20
- import { nodeCssId, needsGroupName, resolveBoundValue, VALIDATION_STATES as VALIDATION_STATES$1, VALIDATION_STATE_CLASS as VALIDATION_STATE_CLASS$1, DEFAULT_WIDTH_BY_TYPE, TEXT_DIRECTION_KEY, TEXT_ALIGN_KEY, TextAlign, COLOR_SCHEME_TRAIT, COLOR_SCHEMES, ColorScheme, LinkType, MAX_LINES_KEY, A11yTrait, BREAKPOINTS, Breakpoint, withSchemeInk, stripSuppressedProps, DeclarationContext, buildPartSelector, planResponsiveRules, resolveStyleProps, targetedStateSelector, stateSelectorSuffix, compileTokenCssBlocks, catalogFontId, fontsForTokens, GOOGLE_FONTS_FILE_ORIGIN, googleFontHref, compileDeclarations, isLastEmittedStateKey, wrapInBreakpointQuery, BreakpointQueryKind, NEW_NODE_STYLE_DEFAULTS, defaultElementStyleFor, applyElementStyle, planDataSource, dataSourcePlanToRequest, normalizeCollectionResult, resolveRepeaterItems, getByPath, computeRelative, ContextStore, ENTRANCE_ACTION_TYPES, EXIT_ACTION_TYPES, ComponentInstanceRegistryService, entranceStartsHidden, isActionEnabled, isContextAction, contextActionEffect, resolveContextValue, effectiveActionType, INTERACTION_HIDDEN_ATTRIBUTE, motionAllowed, finalKeyframe, animateAction, entranceDelay, repeatedRowIndex, keyframeStyleEntries, entrancePreStateStyle, buildInteractionKeyframes, cascadeChain, declaredInputNames, emptyImagePlaceholder, syncNodeDesignClasses, nodeTraitValue, syncNodeA11yAttributes, nodeA11yAttributes, safeSetInput, colorSchemeOf, composeVirtualTraits, withDefaultHoverTransition, stateNamesIn, partClassName, switchElementStyle, releaseElementStyle, countOverrides, setOverride, breakpointDef, breakpointForWidth, FormRenderState, needsAccessibleName, resolveInputs, setInputOverride, barReserveClass, collectContextSlotKeys, withContextSlots, compilePageStyle, nodeUsesContext, IS_PREVIEW, buttonBlockHref, bootSlider, IoxPageModule, PALETTE_PRESETS, elementStyleById, PART_CLASS_NAMES } from '@vectoriox/iox-ui';
20
+ import { nodeCssId, needsGroupName, resolveBoundValue, STRUCTURAL_STATES as STRUCTURAL_STATES$1, VALIDATION_STATES as VALIDATION_STATES$1, VALIDATION_STATE_CLASS as VALIDATION_STATE_CLASS$1, DEFAULT_WIDTH_BY_TYPE, TEXT_DIRECTION_KEY, TEXT_ALIGN_KEY, TextAlign, COLOR_SCHEME_TRAIT, COLOR_SCHEMES, ColorScheme, LinkType, MAX_LINES_KEY, A11yTrait, BREAKPOINTS, Breakpoint, withSchemeInk, stripSuppressedProps, DeclarationContext, planPartRule, planResponsiveRules, resolveStyleProps, planStateRule, splitOuterProps, OUTER_STYLE_PROPS, compileTokenCssBlocks, catalogFontId, fontsForTokens, GOOGLE_FONTS_FILE_ORIGIN, googleFontHref, compileDeclarations, isLastEmittedStateKey, wrapInBreakpointQuery, BreakpointQueryKind, NEW_NODE_STYLE_DEFAULTS, defaultElementStyleFor, applyElementStyle, planDataSource, dataSourcePlanToRequest, normalizeCollectionResult, resolveRepeaterItems, getByPath, computeRelative, ContextStore, ENTRANCE_ACTION_TYPES, EXIT_ACTION_TYPES, ComponentInstanceRegistryService, entranceStartsHidden, isActionEnabled, isContextAction, contextActionEffect, resolveContextValue, effectiveActionType, INTERACTION_HIDDEN_ATTRIBUTE, motionAllowed, settleInteraction, settleModeFor, playInteraction, entranceDelay, repeatedRowIndex, entrancePreStateStyle, buildInteractionKeyframes, cascadeChain, declaredInputNames, emptyImagePlaceholder, syncNodeDesignClasses, nodeTraitValue, syncNodeA11yAttributes, nodeA11yAttributes, safeSetInput, colorSchemeOf, composeVirtualTraits, withDefaultHoverTransition, stateNamesIn, partClassName, switchElementStyle, releaseElementStyle, countOverrides, setOverride, breakpointDef, breakpointForWidth, FormRenderState, needsAccessibleName, resolveInputs, setInputOverride, barReserveClass, collectContextSlotKeys, withContextSlots, compilePageStyle, nodeUsesContext, syncSubmitButtons, IS_PREVIEW, buttonBlockHref, bootSlider, IoxPageModule, PALETTE_PRESETS, elementStyleById, PART_CLASS_NAMES } from '@vectoriox/iox-ui';
21
21
  export { ComponentInstanceRegistryService, VIRTUAL_TRAIT_KEYS, composeVirtualTraits } from '@vectoriox/iox-ui';
22
22
  import { StructureEdit, isAccordionPart, isDisclosurePart, isSliderPart, isTabsPart, isSegmentedPart, structureRefusal, StructureRefusal, SegmentedRole, SEGMENTED_MAX_OPTIONS, AccordionRole, SEGMENTED_MIN_OPTIONS, isExclusiveItemPart, TabsRole, ITEM_OPEN_STATE, readStyle, isSegmentedRoot, segmentedDefaultOn, segmentedOptions, isTabsRoot, isTabsFromContent, tabsTriggers, isAccordionRoot, isAccordionFromContent, accordionItems, initialAccordionOpen, accordionBehaviour, exclusiveItemLabel, exclusiveRootOf, tabTriggerOf, tabLabel, removeTabPair, tabPanelOf, tabsListOf, tabsPanelsOf, nextTabKey, CompositeBehaviourTrait, ACCORDION_DEFAULT_ONS, TabsBehaviourTrait, TABS_ACTIVATE_ONS, TABS_ORIENTATIONS, onCompositeItemState, isCompositeItemOn, runCompositeItemAction, shouldScrollToItem, rowIndexOf as rowIndexOf$1, slideIndexForRow, DisclosureRole, isExclusiveTrigger, compositeScopeId, compositeRowKeys, isExclusiveRoot, ExclusiveRuntime, exclusiveDefaultOf, exclusiveLabelOf, exclusiveBehaviourOf, exclusiveKindOf, compositeKindOfRole, AccordionRuntime, isToggleKind, closestAccordionItem, isAnyCompositeRoot, SceneRole, isExclusiveList, buildCardRecipe, buildBadge, buildChip, buildAccordionRecipe, buildDisclosureRecipe, buildTabsRecipe, buildSegmentedRecipe, buildLightboxRecipe, NO_ELEMENT_STYLE, CompositeKind, SliderRole, accordionItemLabel, segmentedOptionLabel, isExclusivePanels, isLockedPart, accordionRootOf, accordionItemOf, addAccordionItem, addTabPair, segmentedAddRefusal, addSegmentedOption, isCompositeItem, accordionHeadingLevel, isCompositePanel, isCompositeTrigger, splitSliderChildren, hasSliderParts } from '@vectoriox/iox-ui/composite-core';
23
23
  import { formContentInHost, isInteractiveHost, findFormContentInHosts } from '@vectoriox/iox-ui/forms-core';
@@ -1349,8 +1349,9 @@ const DIALOG_ACTION_TYPES = new Set(['openDialog', 'closeDialog', 'toggleDialog'
1349
1349
  const EASING_OPTIONS = ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out'];
1350
1350
  /** Interaction pseudo-classes — compiled to .iox-node-{id}:state */
1351
1351
  const INTERACTION_STATES = ['hover', 'active', 'focus'];
1352
- /** Structural pseudo-classes — compiled to .iox-outer-{id}:state */
1353
- const STRUCTURAL_STATES = ['first-child', 'last-child', 'nth-child(odd)', 'nth-child(even)'];
1352
+ /** Structural pseudo-classes — matched on the outer element by the shared `planStateRule` (@vectoriox/iox-ui), the
1353
+ * engine's planner too (iox-client-engine#14). */
1354
+ const STRUCTURAL_STATES = [...STRUCTURAL_STATES$1];
1354
1355
  /**
1355
1356
  * Relational pseudo-classes — the styled element is a descendant, the pseudo-class
1356
1357
  * lives on an ANCESTOR. Compiled to `.iox-node-{parentId}:{base} .iox-node-{id} { … }`,
@@ -2145,26 +2146,6 @@ class StyleRegistryService {
2145
2146
  this.styleEl = null;
2146
2147
  this._flushPending = false;
2147
2148
  }
2148
- /**
2149
- * Properties that must live on the OUTER wrapper (.iox-outer-{id}) because they
2150
- * affect how the element participates in its PARENT layout (flex/block flow).
2151
- * Everything else goes to the INNER element (.iox-node-{id}).
2152
- *
2153
- * Exception — out-of-flow positioning (position:fixed/absolute):
2154
- * When position is fixed or absolute, width is redirected to the inner element
2155
- * (the positioned one) and a default width:100% is added if none is set, so the
2156
- * element stays visible. top/right/bottom/left/zIndex are always inner because
2157
- * they are co-ordinates of the positioned element, not the flow wrapper.
2158
- * position itself goes to inner with `!important` to beat component-scoped CSS.
2159
- */
2160
- static { this.OUTER_PROPS = new Set([
2161
- 'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft',
2162
- 'alignSelf', 'flexGrow', 'flexShrink', 'flexBasis', 'order',
2163
- // Grid-child placement — like the flex-child props above, these position the node
2164
- // within the PARENT's grid, so they must land on the outer host, not the inner div.
2165
- 'gridColumn', 'gridRow', 'justifySelf',
2166
- 'width',
2167
- ]); }
2168
2149
  /**
2169
2150
  * Properties that need !important on the inner element to beat Angular's scoped-CSS
2170
2151
  * specificity boost. CANVAS-ONLY (H2 audit, 2026-10-11): the shared compiler never forces
@@ -2221,8 +2202,7 @@ class StyleRegistryService {
2221
2202
  // breakpoint is scoped identically without re-resolving it (see upsertResponsive).
2222
2203
  if (relativeToId || triggerId)
2223
2204
  this.stateScopes.set(key, { relativeToId, triggerId });
2224
- const selector = this.stateSelector(nodeId, state, relativeToId, triggerId);
2225
- const css = selector ? this.compile(selector, styles) : '';
2205
+ const css = this.stateRuleCss(nodeId, state, styles, relativeToId, triggerId, DeclarationContext.Base);
2226
2206
  if (css)
2227
2207
  this.rules.set(key, css);
2228
2208
  else
@@ -2256,9 +2236,10 @@ class StyleRegistryService {
2256
2236
  if (!nodeId || !partClassName)
2257
2237
  return;
2258
2238
  const key = `${nodeId}:part:${partClassName}${state ? ':' + state : ''}`;
2259
- const validationClass = state ? VALIDATION_STATE_CLASS[state] : undefined;
2260
- const selector = buildPartSelector(nodeId, partClassName, validationClass);
2261
- const css = Object.keys(styles).length ? this.compile(selector, styles) : '';
2239
+ // The shared planner the engine uses (iox-client-engine#14): a part's hover / focus is the PART's own pseudo-class
2240
+ // — before, any non-validation state compiled to the bare part selector and applied at all times.
2241
+ const selector = planPartRule(nodeId, partClassName, state);
2242
+ const css = selector && Object.keys(styles).length ? this.compileSelector(selector, styles) : '';
2262
2243
  if (css)
2263
2244
  this.rules.set(key, css);
2264
2245
  else
@@ -2304,10 +2285,9 @@ class StyleRegistryService {
2304
2285
  this.setResponsiveRule(nodeId, bp, `${nodeId}::marker`, marker);
2305
2286
  for (const [state, map] of Object.entries(layer.stateStyles)) {
2306
2287
  const scope = this.stateScopes.get(`${nodeId}:${state}`);
2307
- const selector = this.stateSelector(nodeId, state, scope?.relativeToId, scope?.triggerId);
2308
2288
  // A relational/targeted state whose scope isn't known yet is skipped; it is re-emitted
2309
2289
  // when the base state rule arrives with its scope (see upsert).
2310
- const css = selector ? this.compile(selector, this.ownProps(nodeId, map), DeclarationContext.Override) : '';
2290
+ const css = this.stateRuleCss(nodeId, state, this.ownProps(nodeId, map), scope?.relativeToId, scope?.triggerId, DeclarationContext.Override);
2311
2291
  if (css)
2312
2292
  this.setResponsiveRule(nodeId, bp, `${nodeId}:${state}`, css);
2313
2293
  }
@@ -2333,37 +2313,20 @@ class StyleRegistryService {
2333
2313
  return pos === 'fixed' || pos === 'absolute';
2334
2314
  }
2335
2315
  /**
2336
- * The selector BODY (no leading dot) for a state rule, or null when a relational / targeted
2337
- * state has no scope yet (emitting it unscoped would be wrong — a page-global `:has()`).
2316
+ * A state rule's CSS through the SHARED planner the engine emits with (`planStateRule`, iox-client-engine#14), or ''
2317
+ * when a relational / targeted state has no scope yet (emitting it unscoped would be wrong — a page-global `:has()`).
2318
+ * `relativeToId` is the parent for a relational state (parent-hover) and the nearest common ancestor of the node and
2319
+ * its trigger for a targeted one (trigger-hover, trigger-on). A structural state is matched on the outer element and
2320
+ * reaches the node's own; its outer props stay outer.
2338
2321
  */
2339
- stateSelector(nodeId, state, relativeToId, triggerId) {
2340
- // Targeted states (trigger-hover): the styled node restyles when an ARBITRARY chosen
2341
- // element is hovered. The selector is `:has()`-scoped to `relativeToId` (the nearest
2342
- // common ancestor of styled + trigger) so it stays repetition-safe with one shared rule.
2343
- // `trigger-on` (CC2 "Item open"): the same scoping, keyed on the item's `[data-iox-on]` — the ONE
2344
- // shared `targetedStateSelector` the engine uses too.
2345
- if (TARGETED_STATES.includes(state)) {
2346
- return relativeToId && triggerId ? targetedStateSelector(state, relativeToId, triggerId, nodeId) : null;
2347
- }
2348
- // Relational states (parent-hover): the pseudo-class lives on an ANCESTOR
2349
- // and cascades to this element. Requires the ancestor's css id.
2350
- if (RELATIONAL_STATES.includes(state)) {
2351
- const base = RELATIONAL_STATE_BASE[state];
2352
- return relativeToId && base ? `iox-node-${relativeToId}:${base} .iox-node-${nodeId}` : null;
2353
- }
2354
- // Validation states (wrong/accepted, board 7) are not real CSS pseudo-classes — `:wrong`
2355
- // doesn't exist. Compound a CLASS instead, matching the host class IoxFieldComponent
2356
- // already sets from forceInvalid/showValid. Reuses buildPartSelector with no part, since
2357
- // "whole node, when wrong" is exactly that function's no-part case.
2358
- const validationClass = VALIDATION_STATE_CLASS[state];
2359
- if (validationClass)
2360
- return buildPartSelector(nodeId, undefined, validationClass);
2361
- // Structural pseudo-classes (first-child, last-child, nth-child) target the
2362
- // outer wrapper element so they participate correctly in grid/flex layout.
2363
- const prefix = STRUCTURAL_STATES.includes(state) ? 'iox-outer' : 'iox-node';
2364
- // `focus` compiles to :focus-visible (DS2) and a component state to its attribute (`current` →
2365
- // [aria-current="page"], CC1) — shared with the engine via stateSelectorSuffix.
2366
- return `${prefix}-${nodeId}${stateSelectorSuffix(state)}`;
2322
+ stateRuleCss(nodeId, state, styles, relativeToId, triggerId, context) {
2323
+ const targeted = TARGETED_STATES.includes(state);
2324
+ const plan = planStateRule(nodeId, state, targeted ? { scopeId: relativeToId, triggerId } : { parentId: relativeToId });
2325
+ if (!plan)
2326
+ return '';
2327
+ const { inner, outer } = plan.outer ? splitOuterProps(styles) : { inner: styles, outer: {} };
2328
+ return [this.compileSelector(plan.inner, inner, context), plan.outer ? this.compileSelector(plan.outer, outer, context) : '']
2329
+ .filter(Boolean).join('\n');
2367
2330
  }
2368
2331
  /**
2369
2332
  * The inner + outer rule (joined) and the `::marker` rule for one composed style map.
@@ -2380,7 +2343,7 @@ class StyleRegistryService {
2380
2343
  for (const [k, v] of Object.entries(styles)) {
2381
2344
  if (k === '__markerStyles')
2382
2345
  continue;
2383
- const wantsOuter = StyleRegistryService.OUTER_PROPS.has(k);
2346
+ const wantsOuter = OUTER_STYLE_PROPS.has(k);
2384
2347
  // width goes to inner for fixed/absolute (the positioned element owns its own width)
2385
2348
  if (wantsOuter && !(isOutOfFlow && k === 'width')) {
2386
2349
  outer[k] = v;
@@ -2526,14 +2489,16 @@ class StyleRegistryService {
2526
2489
  }
2527
2490
  }
2528
2491
  compile(className, styles, context = DeclarationContext.Base) {
2529
- // Declaration compilation (kebab-case, viewport-unit rewrite, !important) lives in
2530
- // the shared style-compiler so the SSR engine produces byte-identical output.
2531
- // The inner/outer partition + selector emission stay here (builder-only concern).
2492
+ return this.compileSelector(`.${className}`, styles, context);
2493
+ }
2494
+ /** One rule for a FULL selector. Declaration compilation (kebab-case, viewport-unit rewrite, !important) lives in
2495
+ * the shared style-compiler so the SSR engine produces byte-identical output. */
2496
+ compileSelector(selector, styles, context = DeclarationContext.Base) {
2532
2497
  const decls = compileDeclarations(styles, StyleRegistryService.IMPORTANT_PROPS, context);
2533
2498
  if (!decls.length)
2534
2499
  return '';
2535
2500
  const body = decls.map(d => ` ${d};`).join('\n');
2536
- return `.${className} {\n${body}\n}`;
2501
+ return `${selector} {\n${body}\n}`;
2537
2502
  }
2538
2503
  flush() {
2539
2504
  if (!this.styleEl || this._flushPending)
@@ -4085,6 +4050,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
4085
4050
  // Re-exported from the shared iox-ui package so iox-builder consumers
4086
4051
  // don't need to import from two different packages.
4087
4052
 
4053
+ /** What the canvas never writes inline (a transform inside the scaled page breaks Lenis). */
4054
+ const CANVAS_SKIPPED_PROPS = new Set(['transform']);
4088
4055
  /**
4089
4056
  * Wires ComponentNode interactions to live DOM elements via the
4090
4057
  * OverlayService node-element registry.
@@ -4706,22 +4673,27 @@ class InteractionEngineService {
4706
4673
  element.removeAttribute(INTERACTION_HIDDEN_ATTRIBUTE);
4707
4674
  else if (this.EXIT_TYPES.has(type))
4708
4675
  element.setAttribute(INTERACTION_HIDDEN_ATTRIBUTE, '');
4709
- // Reduced motion (CC1 R1): no animation runs — the element takes its final frame at once.
4676
+ // Reduced motion (CC1 R1): no animation runs — the element is left as the run would leave it (the shared settle
4677
+ // rule: an entrance releases to the stylesheet, an exit holds its hidden frame).
4710
4678
  if (!motionAllowed()) {
4711
- this.applyFrame(element, finalKeyframe(keyframes));
4679
+ settleInteraction(element, keyframes, settleModeFor(type), this.settleOptions());
4712
4680
  this.settleAfter(element, type);
4713
4681
  return;
4714
4682
  }
4715
- // 'both' = backwards (holds first keyframe during delay) + forwards (holds last keyframe after end). A look
4683
+ // The ONE call the live site makes too: fill both (holds the first frame through a delay), started from the live
4684
+ // value, settled by the shared rule — an element that has entered keeps its hover and press (iox-ui#41). A look
4716
4685
  // entrance on a Repeater's template staggers its rows: each copy adds its row's share (shared `entranceDelay`).
4717
- const anim = animateAction(element, { type, params: action.params }, keyframes, {
4686
+ const anim = playInteraction(element, { type, params: action.params }, keyframes, {
4718
4687
  duration: action.duration,
4719
4688
  delay: entranceDelay(action, repeatedRowIndex(element)),
4720
4689
  easing: action.easing,
4721
- fill: 'both',
4722
- });
4690
+ }, this.settleOptions());
4723
4691
  anim.finished.then(() => this.settleAfter(element, type)).catch(() => { });
4724
4692
  }
4693
+ /** On the canvas a run never writes a transform inline (one inside the scaled page breaks Lenis). */
4694
+ settleOptions() {
4695
+ return this.isBuilderMode() ? { skip: CANVAS_SKIPPED_PROPS } : {};
4696
+ }
4725
4697
  /** What an element is left as once an action is done: a revealed one interactive, a hidden one inert. */
4726
4698
  settleAfter(element, type) {
4727
4699
  if (this.ENTRANCE_TYPES.has(type)) {
@@ -4738,15 +4710,6 @@ class InteractionEngineService {
4738
4710
  element.style.setProperty('pointer-events', 'none');
4739
4711
  }
4740
4712
  }
4741
- /** Write a frame inline. On the canvas never a transform: one inside the scaled page breaks Lenis. */
4742
- applyFrame(element, frame) {
4743
- const canvas = this.isBuilderMode();
4744
- for (const [prop, value] of keyframeStyleEntries(frame)) {
4745
- if (canvas && prop === 'transform')
4746
- continue;
4747
- element.style.setProperty(prop, value);
4748
- }
4749
- }
4750
4713
  /**
4751
4714
  * Freeze an element at the animation's starting frame immediately, before any
4752
4715
  * trigger fires. Called for ALL entrance-type actions regardless of trigger so
@@ -4787,20 +4750,22 @@ class InteractionEngineService {
4787
4750
  element.style.removeProperty('pointer-events');
4788
4751
  element.removeAttribute(INTERACTION_HIDDEN_ATTRIBUTE);
4789
4752
  }
4753
+ /** A hover-out: the action played back from where the element IS (never from its end frame), settled by the shared
4754
+ * rule — played back to hidden, it holds hidden; to shown, it releases. */
4790
4755
  reverseAction(element, action) {
4791
- const keyframes = this.keyframesFor(effectiveActionType(action.type, element.hasAttribute(INTERACTION_HIDDEN_ATTRIBUTE)), action);
4756
+ const type = effectiveActionType(action.type, element.hasAttribute(INTERACTION_HIDDEN_ATTRIBUTE));
4757
+ const keyframes = this.keyframesFor(type, action);
4792
4758
  if (!keyframes.length)
4793
4759
  return;
4760
+ const frames = [...keyframes].reverse();
4794
4761
  if (!motionAllowed()) {
4795
- this.applyFrame(element, keyframes[0]);
4762
+ settleInteraction(element, frames, settleModeFor(type, true), this.settleOptions());
4796
4763
  return;
4797
4764
  }
4798
- // Reverse: play the keyframes backwards
4799
- animateAction(element, action, [...keyframes].reverse(), {
4765
+ playInteraction(element, { type, params: action.params }, frames, {
4800
4766
  duration: action.duration,
4801
4767
  easing: action.easing,
4802
- fill: 'forwards',
4803
- }, { reverse: true });
4768
+ }, { ...this.settleOptions(), reverse: true });
4804
4769
  }
4805
4770
  /** The action's keyframes from the ONE shared table both runtimes animate from (composite T2). */
4806
4771
  keyframesFor(type, action) {
@@ -11147,6 +11112,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
11147
11112
  type: Output
11148
11113
  }] } });
11149
11114
 
11115
+ /** The formClass a Form's canvas twin wears (form.config). */
11116
+ const FORM_CLASS = 'iox-form';
11150
11117
  const ACCORDION_TRACK_WRAPPER = 'track';
11151
11118
  /**
11152
11119
  * Directive-host Container (Phase 2 of the Moveable migration = task 9c). The real tag (div/section/
@@ -11157,6 +11124,14 @@ const ACCORDION_TRACK_WRAPPER = 'track';
11157
11124
  * See moveable-migration.md.
11158
11125
  */
11159
11126
  class BuilderContainerComponent {
11127
+ /** Which state the canvas is showing. Only meaningful on a form host: its submit buttons follow it (busy, disabled
11128
+ * while Sending — the ONE `syncSubmitButtons` both live paths call, iox-ui#42); "While sending" is the shared CSS's. */
11129
+ set formState(state) {
11130
+ this._formState = state;
11131
+ if (this.formClass === FORM_CLASS)
11132
+ syncSubmitButtons(this.elRef?.nativeElement, state);
11133
+ }
11134
+ get formState() { return this._formState; }
11160
11135
  /** The wrapper a child renders in: an Accordion item's header → its heading level; an item's panel (either
11161
11136
  * kind) → the track. A disclosure's toggle has no heading — it is a button in the flow. */
11162
11137
  wrapperOf(child) {
@@ -11186,8 +11161,7 @@ class BuilderContainerComponent {
11186
11161
  * stylesheet treat them exactly as it treats the engine's own twins.
11187
11162
  */
11188
11163
  this.formClass = '';
11189
- /** Which state the canvas is showing. Only meaningful on a form host. */
11190
- this.formState = 'idle';
11164
+ this._formState = 'idle';
11191
11165
  this.childClick = new EventEmitter();
11192
11166
  this.childMouseEnter = new EventEmitter();
11193
11167
  this.childMouseLeave = new EventEmitter();