@vectoriox/iox-builder 11.11.0 → 11.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/vectoriox-iox-builder.mjs +126 -106
- package/fesm2022/vectoriox-iox-builder.mjs.map +1 -1
- package/index.d.ts +30 -25
- package/package.json +2 -2
|
@@ -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,
|
|
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, gateHoverRule, 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, splitPressFeedback, isHoverPointer, isContextAction, contextActionEffect, resolveContextValue, effectiveActionType, INTERACTION_HIDDEN_ATTRIBUTE, motionAllowed, settleInteraction, settleModeFor, playInteraction, resolveInteractionEasing, entranceDelay, repeatedRowIndex, entrancePreStateStyle, buildInteractionKeyframes, cascadeChain, declaredInputNames, emptyImagePlaceholder, syncNodeDesignClasses, nodeTraitValue, syncNodeA11yAttributes, nodeA11yAttributes, safeSetInput, colorSchemeOf, withStateTransitions, composeVirtualTraits, stateNamesIn, partClassName, switchElementStyle, releaseElementStyle, countOverrides, setOverride, breakpointDef, breakpointForWidth, FormRenderState, needsAccessibleName, resolveInputs, setInputOverride, barReserveClass, collectContextSlotKeys, withContextSlots, compilePageStyle, nodeUsesContext, isScrolled, SCROLLED_ATTRIBUTE, 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';
|
|
@@ -1346,11 +1346,13 @@ const ACTION_TYPE_OPTIONS = [
|
|
|
1346
1346
|
];
|
|
1347
1347
|
/** Dialog actions, for the engines' dispatch branch (mirror of SWIPER's set). */
|
|
1348
1348
|
const DIALOG_ACTION_TYPES = new Set(['openDialog', 'closeDialog', 'toggleDialog']);
|
|
1349
|
+
/** The keyword easings — the panel lists the look's easing tokens (iox-ui `EASING_TOKENS`) and a custom curve beside them. */
|
|
1349
1350
|
const EASING_OPTIONS = ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out'];
|
|
1350
1351
|
/** Interaction pseudo-classes — compiled to .iox-node-{id}:state */
|
|
1351
1352
|
const INTERACTION_STATES = ['hover', 'active', 'focus'];
|
|
1352
|
-
/** Structural pseudo-classes —
|
|
1353
|
-
|
|
1353
|
+
/** Structural pseudo-classes — matched on the outer element by the shared `planStateRule` (@vectoriox/iox-ui), the
|
|
1354
|
+
* engine's planner too (iox-client-engine#14). */
|
|
1355
|
+
const STRUCTURAL_STATES = [...STRUCTURAL_STATES$1];
|
|
1354
1356
|
/**
|
|
1355
1357
|
* Relational pseudo-classes — the styled element is a descendant, the pseudo-class
|
|
1356
1358
|
* lives on an ANCESTOR. Compiled to `.iox-node-{parentId}:{base} .iox-node-{id} { … }`,
|
|
@@ -1397,7 +1399,7 @@ const VALIDATION_STATE_CLASS = VALIDATION_STATE_CLASS$1;
|
|
|
1397
1399
|
* (`stateSelectorSuffix` in @vectoriox/iox-ui), never a pseudo-class. Offered per type by
|
|
1398
1400
|
* `componentStatesFor`.
|
|
1399
1401
|
*/
|
|
1400
|
-
const COMPONENT_STATES = ['current', 'on'];
|
|
1402
|
+
const COMPONENT_STATES = ['current', 'on', 'disabled', 'busy', 'selected', 'scrolled'];
|
|
1401
1403
|
const SUPPORTED_STATES = [...INTERACTION_STATES, ...STRUCTURAL_STATES, ...RELATIONAL_STATES, ...TARGETED_STATES, ...VALIDATION_STATES, ...COMPONENT_STATES];
|
|
1402
1404
|
/**
|
|
1403
1405
|
* Build a full StyleTraitGroup[] schema populated with values from a flat
|
|
@@ -1827,6 +1829,19 @@ class EffectsGroupStyleConfig extends GroupStyleConfig {
|
|
|
1827
1829
|
new TraitConfig('rotate', 'Rotate', TraitInputType.Scrub, { min: -360, max: 360, step: 1, units: UNITS_DEG }, '0deg', true),
|
|
1828
1830
|
new TraitConfig('skewX', 'Skew X', TraitInputType.Scrub, { min: -180, max: 180, step: 1, units: UNITS_DEG }, '0deg', true),
|
|
1829
1831
|
new TraitConfig('skewY', 'Skew Y', TraitInputType.Scrub, { min: -180, max: 180, step: 1, units: UNITS_DEG }, '0deg', true),
|
|
1832
|
+
// G3: where the element scales and turns FROM — a popover grows from its trigger (Emil 47). Physical
|
|
1833
|
+
// keywords: transform-origin has no logical ones. The default is the CSS initial (never emitted).
|
|
1834
|
+
new TraitConfig('transformOrigin', 'Origin', TraitInputType.Select, [
|
|
1835
|
+
{ label: 'Center', value: 'center' },
|
|
1836
|
+
{ label: 'Top', value: 'top' },
|
|
1837
|
+
{ label: 'Bottom', value: 'bottom' },
|
|
1838
|
+
{ label: 'Left', value: 'left' },
|
|
1839
|
+
{ label: 'Right', value: 'right' },
|
|
1840
|
+
{ label: 'Top left', value: 'top left' },
|
|
1841
|
+
{ label: 'Top right', value: 'top right' },
|
|
1842
|
+
{ label: 'Bottom left', value: 'bottom left' },
|
|
1843
|
+
{ label: 'Bottom right', value: 'bottom right' },
|
|
1844
|
+
], 'center'),
|
|
1830
1845
|
// ── Filter (virtual — composed to CSS filter: …) ──────
|
|
1831
1846
|
new TraitConfig('_sep_filter', 'Filter', TraitInputType.SectionLabel),
|
|
1832
1847
|
new TraitConfig('filterBlur', 'Blur', TraitInputType.Scrub, { min: 0, max: 50, step: 1, units: ['px'] }, '0px', true),
|
|
@@ -1859,8 +1874,10 @@ class EffectsGroupStyleConfig extends GroupStyleConfig {
|
|
|
1859
1874
|
{ label: 'Hexagon', value: 'polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%)' },
|
|
1860
1875
|
{ label: 'Chevron', value: 'polygon(0 0, 75% 0, 100% 50%, 75% 100%, 0 100%, 25% 50%)' },
|
|
1861
1876
|
], 'none'),
|
|
1862
|
-
// ── Transition (virtual — composed to CSS transition
|
|
1877
|
+
// ── Transition (virtual — composed to one CSS transition entry per property, never `all`: G2) ──
|
|
1863
1878
|
new TraitConfig('_sep_transition', 'Transition', TraitInputType.SectionLabel),
|
|
1879
|
+
// Empty: the properties the element's states change (the renderers fill them in); a list wins.
|
|
1880
|
+
new TraitConfig('transitionProperty', 'Properties', TraitInputType.Text, undefined, ''),
|
|
1864
1881
|
new TraitConfig('transitionDuration', 'Duration', TraitInputType.Scrub, { min: 0, max: 3000, step: 50, units: ['ms'] }, '0ms', true),
|
|
1865
1882
|
new TraitConfig('transitionDelay', 'Delay', TraitInputType.Scrub, { min: 0, max: 2000, step: 50, units: ['ms'] }, '0ms', true),
|
|
1866
1883
|
new TraitConfig('transitionTimingFunction', 'Easing', TraitInputType.Select, [
|
|
@@ -2145,26 +2162,6 @@ class StyleRegistryService {
|
|
|
2145
2162
|
this.styleEl = null;
|
|
2146
2163
|
this._flushPending = false;
|
|
2147
2164
|
}
|
|
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
2165
|
/**
|
|
2169
2166
|
* Properties that need !important on the inner element to beat Angular's scoped-CSS
|
|
2170
2167
|
* specificity boost. CANVAS-ONLY (H2 audit, 2026-10-11): the shared compiler never forces
|
|
@@ -2221,8 +2218,7 @@ class StyleRegistryService {
|
|
|
2221
2218
|
// breakpoint is scoped identically without re-resolving it (see upsertResponsive).
|
|
2222
2219
|
if (relativeToId || triggerId)
|
|
2223
2220
|
this.stateScopes.set(key, { relativeToId, triggerId });
|
|
2224
|
-
const
|
|
2225
|
-
const css = selector ? this.compile(selector, styles) : '';
|
|
2221
|
+
const css = this.stateRuleCss(nodeId, state, styles, relativeToId, triggerId, DeclarationContext.Base);
|
|
2226
2222
|
if (css)
|
|
2227
2223
|
this.rules.set(key, css);
|
|
2228
2224
|
else
|
|
@@ -2256,9 +2252,10 @@ class StyleRegistryService {
|
|
|
2256
2252
|
if (!nodeId || !partClassName)
|
|
2257
2253
|
return;
|
|
2258
2254
|
const key = `${nodeId}:part:${partClassName}${state ? ':' + state : ''}`;
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
const
|
|
2255
|
+
// The shared planner the engine uses (iox-client-engine#14): a part's hover / focus is the PART's own pseudo-class
|
|
2256
|
+
// — before, any non-validation state compiled to the bare part selector and applied at all times.
|
|
2257
|
+
const selector = planPartRule(nodeId, partClassName, state);
|
|
2258
|
+
const css = selector && Object.keys(styles).length ? gateHoverRule(state, this.compileSelector(selector, styles)) : '';
|
|
2262
2259
|
if (css)
|
|
2263
2260
|
this.rules.set(key, css);
|
|
2264
2261
|
else
|
|
@@ -2304,10 +2301,9 @@ class StyleRegistryService {
|
|
|
2304
2301
|
this.setResponsiveRule(nodeId, bp, `${nodeId}::marker`, marker);
|
|
2305
2302
|
for (const [state, map] of Object.entries(layer.stateStyles)) {
|
|
2306
2303
|
const scope = this.stateScopes.get(`${nodeId}:${state}`);
|
|
2307
|
-
const selector = this.stateSelector(nodeId, state, scope?.relativeToId, scope?.triggerId);
|
|
2308
2304
|
// A relational/targeted state whose scope isn't known yet is skipped; it is re-emitted
|
|
2309
2305
|
// when the base state rule arrives with its scope (see upsert).
|
|
2310
|
-
const css =
|
|
2306
|
+
const css = this.stateRuleCss(nodeId, state, this.ownProps(nodeId, map), scope?.relativeToId, scope?.triggerId, DeclarationContext.Override);
|
|
2311
2307
|
if (css)
|
|
2312
2308
|
this.setResponsiveRule(nodeId, bp, `${nodeId}:${state}`, css);
|
|
2313
2309
|
}
|
|
@@ -2333,37 +2329,21 @@ class StyleRegistryService {
|
|
|
2333
2329
|
return pos === 'fixed' || pos === 'absolute';
|
|
2334
2330
|
}
|
|
2335
2331
|
/**
|
|
2336
|
-
*
|
|
2337
|
-
* state has no scope yet (emitting it unscoped would be wrong — a page-global `:has()`).
|
|
2332
|
+
* A state rule's CSS through the SHARED planner the engine emits with (`planStateRule`, iox-client-engine#14), or ''
|
|
2333
|
+
* when a relational / targeted state has no scope yet (emitting it unscoped would be wrong — a page-global `:has()`).
|
|
2334
|
+
* `relativeToId` is the parent for a relational state (parent-hover) and the nearest common ancestor of the node and
|
|
2335
|
+
* its trigger for a targeted one (trigger-hover, trigger-on). A structural state is matched on the outer element and
|
|
2336
|
+
* reaches the node's own; its outer props stay outer.
|
|
2338
2337
|
*/
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
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)}`;
|
|
2338
|
+
stateRuleCss(nodeId, state, styles, relativeToId, triggerId, context) {
|
|
2339
|
+
const targeted = TARGETED_STATES.includes(state);
|
|
2340
|
+
const plan = planStateRule(nodeId, state, targeted ? { scopeId: relativeToId, triggerId } : { parentId: relativeToId });
|
|
2341
|
+
if (!plan)
|
|
2342
|
+
return '';
|
|
2343
|
+
const { inner, outer } = plan.outer ? splitOuterProps(styles) : { inner: styles, outer: {} };
|
|
2344
|
+
// A hover state's rule only where there is a hover (G1) — the engine gates the same rules the same way.
|
|
2345
|
+
return gateHoverRule(state, [this.compileSelector(plan.inner, inner, context), plan.outer ? this.compileSelector(plan.outer, outer, context) : '']
|
|
2346
|
+
.filter(Boolean).join('\n'));
|
|
2367
2347
|
}
|
|
2368
2348
|
/**
|
|
2369
2349
|
* The inner + outer rule (joined) and the `::marker` rule for one composed style map.
|
|
@@ -2380,7 +2360,7 @@ class StyleRegistryService {
|
|
|
2380
2360
|
for (const [k, v] of Object.entries(styles)) {
|
|
2381
2361
|
if (k === '__markerStyles')
|
|
2382
2362
|
continue;
|
|
2383
|
-
const wantsOuter =
|
|
2363
|
+
const wantsOuter = OUTER_STYLE_PROPS.has(k);
|
|
2384
2364
|
// width goes to inner for fixed/absolute (the positioned element owns its own width)
|
|
2385
2365
|
if (wantsOuter && !(isOutOfFlow && k === 'width')) {
|
|
2386
2366
|
outer[k] = v;
|
|
@@ -2526,14 +2506,16 @@ class StyleRegistryService {
|
|
|
2526
2506
|
}
|
|
2527
2507
|
}
|
|
2528
2508
|
compile(className, styles, context = DeclarationContext.Base) {
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2509
|
+
return this.compileSelector(`.${className}`, styles, context);
|
|
2510
|
+
}
|
|
2511
|
+
/** One rule for a FULL selector. Declaration compilation (kebab-case, viewport-unit rewrite, !important) lives in
|
|
2512
|
+
* the shared style-compiler so the SSR engine produces byte-identical output. */
|
|
2513
|
+
compileSelector(selector, styles, context = DeclarationContext.Base) {
|
|
2532
2514
|
const decls = compileDeclarations(styles, StyleRegistryService.IMPORTANT_PROPS, context);
|
|
2533
2515
|
if (!decls.length)
|
|
2534
2516
|
return '';
|
|
2535
2517
|
const body = decls.map(d => ` ${d};`).join('\n');
|
|
2536
|
-
return
|
|
2518
|
+
return `${selector} {\n${body}\n}`;
|
|
2537
2519
|
}
|
|
2538
2520
|
flush() {
|
|
2539
2521
|
if (!this.styleEl || this._flushPending)
|
|
@@ -4085,6 +4067,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
|
|
|
4085
4067
|
// Re-exported from the shared iox-ui package so iox-builder consumers
|
|
4086
4068
|
// don't need to import from two different packages.
|
|
4087
4069
|
|
|
4070
|
+
/** What the canvas never writes inline (a transform inside the scaled page breaks Lenis). */
|
|
4071
|
+
const CANVAS_SKIPPED_PROPS = new Set(['transform']);
|
|
4088
4072
|
/**
|
|
4089
4073
|
* Wires ComponentNode interactions to live DOM elements via the
|
|
4090
4074
|
* OverlayService node-element registry.
|
|
@@ -4444,6 +4428,9 @@ class InteractionEngineService {
|
|
|
4444
4428
|
requestAnimationFrame(poll);
|
|
4445
4429
|
}
|
|
4446
4430
|
attachClick(node, ix, element) {
|
|
4431
|
+
// G7: press FEEDBACK (an animation of the pressed element that hides and shows nothing) lands on pointer-down,
|
|
4432
|
+
// at once; the rest commits on the click. A toggle keeps its whole list on the click (the shared split, iox-ui).
|
|
4433
|
+
const { onPress, onClick } = ix.reverseActions?.length ? { onPress: [], onClick: ix.actions } : splitPressFeedback(ix.actions);
|
|
4447
4434
|
const handler = () => {
|
|
4448
4435
|
if (ix.reverseActions?.length) {
|
|
4449
4436
|
// Toggle: alternate between primary and reverse actions on each click.
|
|
@@ -4452,18 +4439,29 @@ class InteractionEngineService {
|
|
|
4452
4439
|
this.toggleStates.set(ix.id, !isToggled);
|
|
4453
4440
|
}
|
|
4454
4441
|
else {
|
|
4455
|
-
this.runActions(node,
|
|
4442
|
+
this.runActions(node, onClick, element);
|
|
4456
4443
|
}
|
|
4457
4444
|
};
|
|
4445
|
+
const press = (e) => { if (e.button === 0 || e.pointerType !== 'mouse')
|
|
4446
|
+
this.runActions(node, onPress, element); };
|
|
4458
4447
|
// CAPTURE, like the composite runtime: on the canvas every part's own listener stops the click to select
|
|
4459
4448
|
// that part, so a click on a button's LABEL never bubbled to the button's click interaction — the live
|
|
4460
4449
|
// site runs it (CC2.1: "Open an item" on a ButtonBlock did nothing on the canvas).
|
|
4461
4450
|
element.addEventListener('click', handler, true);
|
|
4462
|
-
|
|
4451
|
+
if (onPress.length)
|
|
4452
|
+
element.addEventListener('pointerdown', press, true);
|
|
4453
|
+
return () => {
|
|
4454
|
+
element.removeEventListener('click', handler, true);
|
|
4455
|
+
element.removeEventListener('pointerdown', press, true);
|
|
4456
|
+
};
|
|
4463
4457
|
}
|
|
4464
4458
|
attachHover(node, ix, element) {
|
|
4465
|
-
|
|
4466
|
-
const
|
|
4459
|
+
// G7: a hover trigger answers a mouse or a pen — a finger's emulated hover stuck until the next tap elsewhere.
|
|
4460
|
+
const enterHandler = (e) => { if (isHoverPointer(e))
|
|
4461
|
+
this.runActions(node, ix.actions, element); };
|
|
4462
|
+
const leaveHandler = (e) => {
|
|
4463
|
+
if (!isHoverPointer(e))
|
|
4464
|
+
return;
|
|
4467
4465
|
// Reverse animations on leave for hover triggers
|
|
4468
4466
|
for (const action of ix.actions) {
|
|
4469
4467
|
if (!isActionEnabled(action))
|
|
@@ -4480,11 +4478,11 @@ class InteractionEngineService {
|
|
|
4480
4478
|
this.reverseAction(target, action);
|
|
4481
4479
|
}
|
|
4482
4480
|
};
|
|
4483
|
-
element.addEventListener('
|
|
4484
|
-
element.addEventListener('
|
|
4481
|
+
element.addEventListener('pointerenter', enterHandler);
|
|
4482
|
+
element.addEventListener('pointerleave', leaveHandler);
|
|
4485
4483
|
return () => {
|
|
4486
|
-
element.removeEventListener('
|
|
4487
|
-
element.removeEventListener('
|
|
4484
|
+
element.removeEventListener('pointerenter', enterHandler);
|
|
4485
|
+
element.removeEventListener('pointerleave', leaveHandler);
|
|
4488
4486
|
};
|
|
4489
4487
|
}
|
|
4490
4488
|
// ── Actions ──────────────────────────────────────────────
|
|
@@ -4706,22 +4704,28 @@ class InteractionEngineService {
|
|
|
4706
4704
|
element.removeAttribute(INTERACTION_HIDDEN_ATTRIBUTE);
|
|
4707
4705
|
else if (this.EXIT_TYPES.has(type))
|
|
4708
4706
|
element.setAttribute(INTERACTION_HIDDEN_ATTRIBUTE, '');
|
|
4709
|
-
// Reduced motion (CC1 R1): no animation runs — the element
|
|
4707
|
+
// Reduced motion (CC1 R1): no animation runs — the element is left as the run would leave it (the shared settle
|
|
4708
|
+
// rule: an entrance releases to the stylesheet, an exit holds its hidden frame).
|
|
4710
4709
|
if (!motionAllowed()) {
|
|
4711
|
-
|
|
4710
|
+
settleInteraction(element, keyframes, settleModeFor(type), this.settleOptions());
|
|
4712
4711
|
this.settleAfter(element, type);
|
|
4713
4712
|
return;
|
|
4714
4713
|
}
|
|
4715
|
-
//
|
|
4714
|
+
// The ONE call the live site makes too: fill both (holds the first frame through a delay), started from the live
|
|
4715
|
+
// value, settled by the shared rule — an element that has entered keeps its hover and press (iox-ui#41). A look
|
|
4716
4716
|
// entrance on a Repeater's template staggers its rows: each copy adds its row's share (shared `entranceDelay`).
|
|
4717
|
-
const anim =
|
|
4717
|
+
const anim = playInteraction(element, { type, params: action.params }, keyframes, {
|
|
4718
4718
|
duration: action.duration,
|
|
4719
4719
|
delay: entranceDelay(action, repeatedRowIndex(element)),
|
|
4720
|
-
easing
|
|
4721
|
-
|
|
4722
|
-
});
|
|
4720
|
+
// A keyword, a curve, or the look's easing token as this element sees it (G6) — the live site's resolver.
|
|
4721
|
+
easing: resolveInteractionEasing(action.easing, element),
|
|
4722
|
+
}, this.settleOptions());
|
|
4723
4723
|
anim.finished.then(() => this.settleAfter(element, type)).catch(() => { });
|
|
4724
4724
|
}
|
|
4725
|
+
/** On the canvas a run never writes a transform inline (one inside the scaled page breaks Lenis). */
|
|
4726
|
+
settleOptions() {
|
|
4727
|
+
return this.isBuilderMode() ? { skip: CANVAS_SKIPPED_PROPS } : {};
|
|
4728
|
+
}
|
|
4725
4729
|
/** What an element is left as once an action is done: a revealed one interactive, a hidden one inert. */
|
|
4726
4730
|
settleAfter(element, type) {
|
|
4727
4731
|
if (this.ENTRANCE_TYPES.has(type)) {
|
|
@@ -4738,15 +4742,6 @@ class InteractionEngineService {
|
|
|
4738
4742
|
element.style.setProperty('pointer-events', 'none');
|
|
4739
4743
|
}
|
|
4740
4744
|
}
|
|
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
4745
|
/**
|
|
4751
4746
|
* Freeze an element at the animation's starting frame immediately, before any
|
|
4752
4747
|
* trigger fires. Called for ALL entrance-type actions regardless of trigger so
|
|
@@ -4787,20 +4782,22 @@ class InteractionEngineService {
|
|
|
4787
4782
|
element.style.removeProperty('pointer-events');
|
|
4788
4783
|
element.removeAttribute(INTERACTION_HIDDEN_ATTRIBUTE);
|
|
4789
4784
|
}
|
|
4785
|
+
/** A hover-out: the action played back from where the element IS (never from its end frame), settled by the shared
|
|
4786
|
+
* rule — played back to hidden, it holds hidden; to shown, it releases. */
|
|
4790
4787
|
reverseAction(element, action) {
|
|
4791
|
-
const
|
|
4788
|
+
const type = effectiveActionType(action.type, element.hasAttribute(INTERACTION_HIDDEN_ATTRIBUTE));
|
|
4789
|
+
const keyframes = this.keyframesFor(type, action);
|
|
4792
4790
|
if (!keyframes.length)
|
|
4793
4791
|
return;
|
|
4792
|
+
const frames = [...keyframes].reverse();
|
|
4794
4793
|
if (!motionAllowed()) {
|
|
4795
|
-
|
|
4794
|
+
settleInteraction(element, frames, settleModeFor(type, true), this.settleOptions());
|
|
4796
4795
|
return;
|
|
4797
4796
|
}
|
|
4798
|
-
|
|
4799
|
-
animateAction(element, action, [...keyframes].reverse(), {
|
|
4797
|
+
playInteraction(element, { type, params: action.params }, frames, {
|
|
4800
4798
|
duration: action.duration,
|
|
4801
|
-
easing: action.easing,
|
|
4802
|
-
|
|
4803
|
-
}, { reverse: true });
|
|
4799
|
+
easing: resolveInteractionEasing(action.easing, element),
|
|
4800
|
+
}, { ...this.settleOptions(), reverse: true });
|
|
4804
4801
|
}
|
|
4805
4802
|
/** The action's keyframes from the ONE shared table both runtimes animate from (composite T2). */
|
|
4806
4803
|
keyframesFor(type, action) {
|
|
@@ -5713,7 +5710,9 @@ class RenderDirective {
|
|
|
5713
5710
|
// The default hover transition (shared, marker-aware — the engine emits the same one):
|
|
5714
5711
|
// without it a hovering node's base rule, re-upserted here on every render, dropped the
|
|
5715
5712
|
// transition page-ui had added on load, and the canvas snapped where the site eased.
|
|
5716
|
-
|
|
5713
|
+
// The transition names what the node's states change (G2) — the engine composes the same, from the same call.
|
|
5714
|
+
const timedBase = withStateTransitions(initialStyle, node.stateStyles, node.elementStyle, node.responsive);
|
|
5715
|
+
const composedBase = composeVirtualTraits(timedBase);
|
|
5717
5716
|
this.styleRegistry.upsert(cssId, composedBase);
|
|
5718
5717
|
// Restore per-state overrides saved from a previous session.
|
|
5719
5718
|
// Every state set at base OR at any breakpoint — so a breakpoint-only state (a Mobile-only
|
|
@@ -5760,7 +5759,7 @@ class RenderDirective {
|
|
|
5760
5759
|
}
|
|
5761
5760
|
}
|
|
5762
5761
|
// Breakpoint layers — AFTER the state loop, so every scope resolved above is recorded.
|
|
5763
|
-
this.styleRegistry.upsertResponsive(cssId,
|
|
5762
|
+
this.styleRegistry.upsertResponsive(cssId, timedBase, node.stateStyles, node.responsive);
|
|
5764
5763
|
// Part styles (board 7 — "the failure design"). Simpler than the state loop above:
|
|
5765
5764
|
// a part+state combo always compounds onto THIS node (no relational/targeted scope
|
|
5766
5765
|
// to resolve), so every entry just needs its base plus each of its own state overrides.
|
|
@@ -6962,8 +6961,8 @@ function nodeStyleMap(node) {
|
|
|
6962
6961
|
return base;
|
|
6963
6962
|
}
|
|
6964
6963
|
/**
|
|
6965
|
-
* Re-register a node's compiled rules from its CURRENT style values — the base rule (
|
|
6966
|
-
*
|
|
6964
|
+
* Re-register a node's compiled rules from its CURRENT style values — the base rule (its transition naming what its
|
|
6965
|
+
* states change, G2), its self-scoped state rules and its breakpoint layers — exactly as the
|
|
6967
6966
|
* RenderDirective registers them at render. For edits that rewrite a node's styles from outside the Style
|
|
6968
6967
|
* panel (a Card's layout preset, a reset to defaults).
|
|
6969
6968
|
*/
|
|
@@ -6973,13 +6972,14 @@ function upsertNodeStyles(styleRegistry, node) {
|
|
|
6973
6972
|
return;
|
|
6974
6973
|
const base = nodeStyleMap(node);
|
|
6975
6974
|
styleRegistry.setNodeType(cssId, node.type, colorSchemeOf(node));
|
|
6976
|
-
|
|
6975
|
+
const timedBase = withStateTransitions(base, node.stateStyles, node.elementStyle, node.responsive);
|
|
6976
|
+
styleRegistry.upsert(cssId, composeVirtualTraits(timedBase));
|
|
6977
6977
|
for (const [state, map] of Object.entries(node.stateStyles ?? {})) {
|
|
6978
6978
|
if (DOM_SCOPED_STATES.has(state) || !map || !Object.keys(map).length)
|
|
6979
6979
|
continue;
|
|
6980
6980
|
styleRegistry.upsert(cssId, composeVirtualTraits(map, base), state);
|
|
6981
6981
|
}
|
|
6982
|
-
styleRegistry.upsertResponsive(cssId,
|
|
6982
|
+
styleRegistry.upsertResponsive(cssId, timedBase, node.stateStyles, node.responsive);
|
|
6983
6983
|
}
|
|
6984
6984
|
/** Push every content trait of a node to its rendered component (a Card title's link switched on). */
|
|
6985
6985
|
function syncNodeInputs(ref, node) {
|
|
@@ -10007,6 +10007,15 @@ class BuilderComponent {
|
|
|
10007
10007
|
this.globalOverlayRef?.nativeElement?.style.setProperty('--preview-vw', vwVal);
|
|
10008
10008
|
this.canvasFrameHeight = Math.round(frameH);
|
|
10009
10009
|
}
|
|
10010
|
+
/** Keep the Scrolled attribute (iox-ui SCROLLED_ATTRIBUTE) on the canvas page and the header overlay — written only
|
|
10011
|
+
* when it changes, so the overlay's mutation observers never wake for a scroll tick. */
|
|
10012
|
+
syncScrolled(offset) {
|
|
10013
|
+
const scrolled = isScrolled(offset);
|
|
10014
|
+
for (const el of [this.previewCanvas?.nativeElement, this.globalOverlayRef?.nativeElement]) {
|
|
10015
|
+
if (el && el.hasAttribute(SCROLLED_ATTRIBUTE) !== scrolled)
|
|
10016
|
+
el.toggleAttribute(SCROLLED_ATTRIBUTE, scrolled);
|
|
10017
|
+
}
|
|
10018
|
+
}
|
|
10010
10019
|
initLenis() {
|
|
10011
10020
|
this.destroyLenis();
|
|
10012
10021
|
if (!this.previewScrollRef?.nativeElement || !this.lenisContentRef?.nativeElement)
|
|
@@ -10021,6 +10030,8 @@ class BuilderComponent {
|
|
|
10021
10030
|
this.lenis.on('scroll', (instance) => {
|
|
10022
10031
|
// A Scene's stage is held against this very scroll: a frame later, it would paint carried off its pin.
|
|
10023
10032
|
this.sceneCanvas?.pinNow();
|
|
10033
|
+
// The Scrolled state (G4) on the canvas: the page and its header globals know the canvas has moved.
|
|
10034
|
+
this.syncScrolled(instance.scroll);
|
|
10024
10035
|
const clientH = this.previewScrollRef.nativeElement.clientHeight;
|
|
10025
10036
|
this.onPageScroll({
|
|
10026
10037
|
scrollTop: instance.scroll,
|
|
@@ -11147,6 +11158,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
|
|
|
11147
11158
|
type: Output
|
|
11148
11159
|
}] } });
|
|
11149
11160
|
|
|
11161
|
+
/** The formClass a Form's canvas twin wears (form.config). */
|
|
11162
|
+
const FORM_CLASS = 'iox-form';
|
|
11150
11163
|
const ACCORDION_TRACK_WRAPPER = 'track';
|
|
11151
11164
|
/**
|
|
11152
11165
|
* Directive-host Container (Phase 2 of the Moveable migration = task 9c). The real tag (div/section/
|
|
@@ -11157,6 +11170,14 @@ const ACCORDION_TRACK_WRAPPER = 'track';
|
|
|
11157
11170
|
* See moveable-migration.md.
|
|
11158
11171
|
*/
|
|
11159
11172
|
class BuilderContainerComponent {
|
|
11173
|
+
/** Which state the canvas is showing. Only meaningful on a form host: its submit buttons follow it (busy, disabled
|
|
11174
|
+
* while Sending — the ONE `syncSubmitButtons` both live paths call, iox-ui#42); "While sending" is the shared CSS's. */
|
|
11175
|
+
set formState(state) {
|
|
11176
|
+
this._formState = state;
|
|
11177
|
+
if (this.formClass === FORM_CLASS)
|
|
11178
|
+
syncSubmitButtons(this.elRef?.nativeElement, state);
|
|
11179
|
+
}
|
|
11180
|
+
get formState() { return this._formState; }
|
|
11160
11181
|
/** The wrapper a child renders in: an Accordion item's header → its heading level; an item's panel (either
|
|
11161
11182
|
* kind) → the track. A disclosure's toggle has no heading — it is a button in the flow. */
|
|
11162
11183
|
wrapperOf(child) {
|
|
@@ -11186,8 +11207,7 @@ class BuilderContainerComponent {
|
|
|
11186
11207
|
* stylesheet treat them exactly as it treats the engine's own twins.
|
|
11187
11208
|
*/
|
|
11188
11209
|
this.formClass = '';
|
|
11189
|
-
|
|
11190
|
-
this.formState = 'idle';
|
|
11210
|
+
this._formState = 'idle';
|
|
11191
11211
|
this.childClick = new EventEmitter();
|
|
11192
11212
|
this.childMouseEnter = new EventEmitter();
|
|
11193
11213
|
this.childMouseLeave = new EventEmitter();
|