@vectoriox/iox-ui 5.5.2 → 5.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/actions-core/index.d.ts +10 -2
- package/consent-core/index.d.ts +49 -4
- package/fesm2022/vectoriox-iox-ui-actions-core.mjs +20 -2
- package/fesm2022/vectoriox-iox-ui-actions-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-consent-core.mjs +88 -6
- package/fesm2022/vectoriox-iox-ui-consent-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui.mjs +202 -1
- package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
- package/index.d.ts +111 -62
- package/package.json +1 -1
- package/src/actions-core/action.model.ts +3 -1
- package/src/actions-core/element-actions.ts +22 -1
- package/src/consent-core/consent-mode.ts +4 -2
- package/src/consent-core/decisions.ts +111 -0
- package/src/consent-core/public-api.ts +1 -0
- package/src/consent-core/storage.ts +7 -3
package/index.d.ts
CHANGED
|
@@ -1490,6 +1490,115 @@ declare const PART_CLASS_NAMES: Record<string, Record<string, string>>;
|
|
|
1490
1490
|
* Undefined when the type declares no such part (or no parts at all). */
|
|
1491
1491
|
declare function partClassName(type: string, partKey: string): string | undefined;
|
|
1492
1492
|
|
|
1493
|
+
/**
|
|
1494
|
+
* Flavors (flavors-and-theming-plan.md, F1): the design language a studio launches with,
|
|
1495
|
+
* expressed as data. THE invariant — a flavor never writes a literal into a node; it writes
|
|
1496
|
+
* tokens, and element defaults that REFERENCE tokens. To that end a pack's `defaults` map
|
|
1497
|
+
* holds TOKEN NAMES, not CSS values: the resolver builds `var(--iox-<name>, <packValue>)`,
|
|
1498
|
+
* so smuggling a literal in is impossible by construction, and the validator only has to
|
|
1499
|
+
* check that every referenced token exists.
|
|
1500
|
+
*
|
|
1501
|
+
* The fallback is the bootstrap: until F2 seeds the org's `designtokens` rows, no `--iox-*`
|
|
1502
|
+
* var is defined anywhere, and `var(--iox-radius-control, 4px)` must still paint identically
|
|
1503
|
+
* in the builder canvas and the SSR page. Once tokens ARE seeded (or re-themed), the var wins
|
|
1504
|
+
* over the fallback everywhere — which is exactly the §3 propagation story.
|
|
1505
|
+
*/
|
|
1506
|
+
interface FlavorTokenDef {
|
|
1507
|
+
name: string;
|
|
1508
|
+
value: string;
|
|
1509
|
+
/** Dark-scheme override, seeded into DesignToken.values.dark at F2. */
|
|
1510
|
+
values?: {
|
|
1511
|
+
dark?: string;
|
|
1512
|
+
};
|
|
1513
|
+
type?: 'color' | 'spacing' | 'size' | 'font' | 'other';
|
|
1514
|
+
/** Tokens-panel grouping (flavors plan §10 C5). */
|
|
1515
|
+
group?: 'colour' | 'type' | 'space' | 'radius' | 'shadow' | 'motion';
|
|
1516
|
+
}
|
|
1517
|
+
interface FlavorPack {
|
|
1518
|
+
id: string;
|
|
1519
|
+
/** Bumped when the pack's values change; seeding stamps `flavor:<id>@<version>` as DesignToken.source. */
|
|
1520
|
+
version: number;
|
|
1521
|
+
label: string;
|
|
1522
|
+
blurb: string;
|
|
1523
|
+
tokens: FlavorTokenDef[];
|
|
1524
|
+
/** type → styleTrait → TOKEN NAME (never a CSS value — see the module header). */
|
|
1525
|
+
defaults: Record<string, Record<string, string>>;
|
|
1526
|
+
/** F2: the accent's hue rotation (degrees in OKLab) from the brand seed — a pack opinion. */
|
|
1527
|
+
accentHueDeg?: number;
|
|
1528
|
+
}
|
|
1529
|
+
/** trait → `var(--iox-<token>, <packValue>)` for one node type. Unknown type → {}. */
|
|
1530
|
+
declare function resolveFlavorDefaults(type: string, pack: FlavorPack | null | undefined): Record<string, string>;
|
|
1531
|
+
/**
|
|
1532
|
+
* The ENGINE's half of the cascade: kebab `prop: var(…)` declarations for every pack default
|
|
1533
|
+
* the node's persisted styleProps does NOT set. Appended beside renderDefaultDeclarations —
|
|
1534
|
+
* same guard-on-undefined discipline, so a node saved post-F1 (var already serialised via the
|
|
1535
|
+
* builder's trait defaults) is untouched, and a legacy node picks the pack default up at
|
|
1536
|
+
* render time. Node styleProps always wins.
|
|
1537
|
+
*/
|
|
1538
|
+
declare function flavorDefaultDeclarations(type: string, styleProps: Record<string, any> | null | undefined, pack: FlavorPack | null | undefined): string[];
|
|
1539
|
+
/**
|
|
1540
|
+
* The CI gate (flavors plan §10 C4): a pack may only name types and traits that exist in the
|
|
1541
|
+
* live component registry, and tokens it defines itself. Returns human-readable errors; a
|
|
1542
|
+
* consumer spec feeds it the REAL registry and asserts [].
|
|
1543
|
+
*/
|
|
1544
|
+
declare function validateFlavorPack(pack: FlavorPack, knownTraitsByType: Record<string, ReadonlySet<string> | string[]>): string[];
|
|
1545
|
+
/**
|
|
1546
|
+
* Swiss — the default pack (flavors plan §8: "the flavor that can never embarrass anyone").
|
|
1547
|
+
* Deliberately SMALL at F1: radius + control padding on the clickables, surface radius on Card.
|
|
1548
|
+
* Colour, type and motion arrive with the brand step (F2) — a colour default here would repaint
|
|
1549
|
+
* legacy pages before anyone chose anything.
|
|
1550
|
+
*/
|
|
1551
|
+
declare const SWISS_FLAVOR: FlavorPack;
|
|
1552
|
+
/** What both renderers use until F2 lets an org choose. */
|
|
1553
|
+
declare const DEFAULT_FLAVOR: FlavorPack;
|
|
1554
|
+
|
|
1555
|
+
/**
|
|
1556
|
+
* BRAND DERIVATION (flavors plan §6, F2): one colour in, an asserted accessible ramp out.
|
|
1557
|
+
*
|
|
1558
|
+
* Competitors ask for a palette of five and hope; we ask for ONE seed and compute the rest so
|
|
1559
|
+
* that contrast is a TEST, not a promise. WCAG 2 ratios are the gate (every guarantee below is
|
|
1560
|
+
* unit-asserted across seeds); APCA is exposed as the picker's quality read-out only.
|
|
1561
|
+
*
|
|
1562
|
+
* Colour math is OKLab (perceptually even lightness — the ramp steps LOOK even), hand-rolled
|
|
1563
|
+
* from the published matrices because this module must stay dependency-free: it runs in the
|
|
1564
|
+
* builder, the engine and (later) the generator's Node service.
|
|
1565
|
+
*/
|
|
1566
|
+
interface Rgb {
|
|
1567
|
+
r: number;
|
|
1568
|
+
g: number;
|
|
1569
|
+
b: number;
|
|
1570
|
+
}
|
|
1571
|
+
interface OkLab {
|
|
1572
|
+
L: number;
|
|
1573
|
+
a: number;
|
|
1574
|
+
b: number;
|
|
1575
|
+
}
|
|
1576
|
+
declare function parseHexColor(hex: string): Rgb | null;
|
|
1577
|
+
declare function toHexColor(c: Rgb): string;
|
|
1578
|
+
declare function rgbToOklab(c: Rgb): OkLab;
|
|
1579
|
+
declare function oklabToRgb(c: OkLab): Rgb;
|
|
1580
|
+
declare function relativeLuminance(c: Rgb): number;
|
|
1581
|
+
/** WCAG 2 contrast ratio between two colours (order-independent), 1..21. */
|
|
1582
|
+
declare function wcagContrastRatio(a: string | Rgb, b: string | Rgb): number;
|
|
1583
|
+
/**
|
|
1584
|
+
* APCA-W3 lightness contrast Lc (0.0.98G-4g constants), text-on-background and signed:
|
|
1585
|
+
* positive for dark-on-light, negative for light-on-dark; |Lc| ≈ 106–108 at the extremes.
|
|
1586
|
+
* Shown in the picker as the perceptual read-out; never used as the pass/fail gate here.
|
|
1587
|
+
*/
|
|
1588
|
+
declare function apcaLc(text: string | Rgb, bg: string | Rgb): number;
|
|
1589
|
+
/**
|
|
1590
|
+
* The derived brand tokens (plan §6's table, every row asserted in brand.spec.ts):
|
|
1591
|
+
* `brand-50…900` (monotonic OKLab lightness, gamut-safe) · `brandInk` (black or white, ≥4.5 on
|
|
1592
|
+
* brand-500 — brand-500's lightness is nudged when neither side clears it) · `surface`/`ink`/
|
|
1593
|
+
* `inkMuted` (seed-tinted neutrals, ≥7:1 / ≥4.5:1 on surface) · `line` (the LIGHTEST neutral that
|
|
1594
|
+
* still clears 3:1 on surface) · `accent` (pack-rotated hue, ≥4.5:1 on surface) — with dark-scheme
|
|
1595
|
+
* values for the neutral family riding `values.dark`.
|
|
1596
|
+
*
|
|
1597
|
+
* Throws on an unparseable seed: callers (the picker) validate; a silent fallback here would
|
|
1598
|
+
* launch a studio into a palette nobody chose.
|
|
1599
|
+
*/
|
|
1600
|
+
declare function deriveBrandTokens(seed: string, pack: FlavorPack): FlavorTokenDef[];
|
|
1601
|
+
|
|
1493
1602
|
/** Trait names whose value Swiper reads per breakpoint. Anything else is config-wide. */
|
|
1494
1603
|
declare const SWIPER_RESPONSIVE_TRAITS: readonly string[];
|
|
1495
1604
|
/** `{ [minWidth]: { slidesPerView, … } }` — Swiper's own `breakpoints` shape. */
|
|
@@ -1553,66 +1662,6 @@ declare function compileTokenCss(tokens: ReadonlyArray<DesignTokenLike> | null |
|
|
|
1553
1662
|
dark: string;
|
|
1554
1663
|
};
|
|
1555
1664
|
|
|
1556
|
-
/**
|
|
1557
|
-
* Flavors (flavors-and-theming-plan.md, F1): the design language a studio launches with,
|
|
1558
|
-
* expressed as data. THE invariant — a flavor never writes a literal into a node; it writes
|
|
1559
|
-
* tokens, and element defaults that REFERENCE tokens. To that end a pack's `defaults` map
|
|
1560
|
-
* holds TOKEN NAMES, not CSS values: the resolver builds `var(--iox-<name>, <packValue>)`,
|
|
1561
|
-
* so smuggling a literal in is impossible by construction, and the validator only has to
|
|
1562
|
-
* check that every referenced token exists.
|
|
1563
|
-
*
|
|
1564
|
-
* The fallback is the bootstrap: until F2 seeds the org's `designtokens` rows, no `--iox-*`
|
|
1565
|
-
* var is defined anywhere, and `var(--iox-radius-control, 4px)` must still paint identically
|
|
1566
|
-
* in the builder canvas and the SSR page. Once tokens ARE seeded (or re-themed), the var wins
|
|
1567
|
-
* over the fallback everywhere — which is exactly the §3 propagation story.
|
|
1568
|
-
*/
|
|
1569
|
-
interface FlavorTokenDef {
|
|
1570
|
-
name: string;
|
|
1571
|
-
value: string;
|
|
1572
|
-
/** Dark-scheme override, seeded into DesignToken.values.dark at F2. */
|
|
1573
|
-
values?: {
|
|
1574
|
-
dark?: string;
|
|
1575
|
-
};
|
|
1576
|
-
type?: 'color' | 'spacing' | 'size' | 'font' | 'other';
|
|
1577
|
-
/** Tokens-panel grouping (flavors plan §10 C5). */
|
|
1578
|
-
group?: 'colour' | 'type' | 'space' | 'radius' | 'shadow' | 'motion';
|
|
1579
|
-
}
|
|
1580
|
-
interface FlavorPack {
|
|
1581
|
-
id: string;
|
|
1582
|
-
/** Bumped when the pack's values change; seeding stamps `flavor:<id>@<version>` as DesignToken.source. */
|
|
1583
|
-
version: number;
|
|
1584
|
-
label: string;
|
|
1585
|
-
blurb: string;
|
|
1586
|
-
tokens: FlavorTokenDef[];
|
|
1587
|
-
/** type → styleTrait → TOKEN NAME (never a CSS value — see the module header). */
|
|
1588
|
-
defaults: Record<string, Record<string, string>>;
|
|
1589
|
-
}
|
|
1590
|
-
/** trait → `var(--iox-<token>, <packValue>)` for one node type. Unknown type → {}. */
|
|
1591
|
-
declare function resolveFlavorDefaults(type: string, pack: FlavorPack | null | undefined): Record<string, string>;
|
|
1592
|
-
/**
|
|
1593
|
-
* The ENGINE's half of the cascade: kebab `prop: var(…)` declarations for every pack default
|
|
1594
|
-
* the node's persisted styleProps does NOT set. Appended beside renderDefaultDeclarations —
|
|
1595
|
-
* same guard-on-undefined discipline, so a node saved post-F1 (var already serialised via the
|
|
1596
|
-
* builder's trait defaults) is untouched, and a legacy node picks the pack default up at
|
|
1597
|
-
* render time. Node styleProps always wins.
|
|
1598
|
-
*/
|
|
1599
|
-
declare function flavorDefaultDeclarations(type: string, styleProps: Record<string, any> | null | undefined, pack: FlavorPack | null | undefined): string[];
|
|
1600
|
-
/**
|
|
1601
|
-
* The CI gate (flavors plan §10 C4): a pack may only name types and traits that exist in the
|
|
1602
|
-
* live component registry, and tokens it defines itself. Returns human-readable errors; a
|
|
1603
|
-
* consumer spec feeds it the REAL registry and asserts [].
|
|
1604
|
-
*/
|
|
1605
|
-
declare function validateFlavorPack(pack: FlavorPack, knownTraitsByType: Record<string, ReadonlySet<string> | string[]>): string[];
|
|
1606
|
-
/**
|
|
1607
|
-
* Swiss — the default pack (flavors plan §8: "the flavor that can never embarrass anyone").
|
|
1608
|
-
* Deliberately SMALL at F1: radius + control padding on the clickables, surface radius on Card.
|
|
1609
|
-
* Colour, type and motion arrive with the brand step (F2) — a colour default here would repaint
|
|
1610
|
-
* legacy pages before anyone chose anything.
|
|
1611
|
-
*/
|
|
1612
|
-
declare const SWISS_FLAVOR: FlavorPack;
|
|
1613
|
-
/** What both renderers use until F2 lets an org choose. */
|
|
1614
|
-
declare const DEFAULT_FLAVOR: FlavorPack;
|
|
1615
|
-
|
|
1616
1665
|
/**
|
|
1617
1666
|
* page-style — the ONE compiler that turns a page's style settings into CSS.
|
|
1618
1667
|
*
|
|
@@ -2782,5 +2831,5 @@ declare class GalleryLoop {
|
|
|
2782
2831
|
private emit;
|
|
2783
2832
|
}
|
|
2784
2833
|
|
|
2785
|
-
export { AOSService, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, CONTEXT_ACTION_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ContainerComponent, ContextStore, DEFAULT_FLAVOR, 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, DeclarationContext, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, LinkedContainerComponent, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, RESERVED_ALIAS_PREFIX, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_RESPONSIVE_TRAITS, SWISS_FLAVOR, SectionComponent, TRANSITION_FEELS, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, activeActions, applyBindingPipes, applyFeel, breakpointDef, breakpointForWidth, breakpointQueryFor, buildDataSourceFilter, buildPartSelector, buildRelatedFilter, buildSwiperBreakpoints, buildTargetedStateSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, collectContextSlotKeys, collectReferencedAliases, compileDeclarations, compilePageStyle, compileTokenCss, composeVirtualTraits, computeRelative, contextActionEffect, contextSlotKeys, countOverrides, dataSourcePlanToRequest, declaredInputNames, defaultMessageResolver, deriveSeoFromContent, describeSeoFields, effectiveTransitionId, excludeSelf, feelFor, flavorDefaultDeclarations, getByPath, getSeoValue, initialSlotValues, isActionEnabled, isContextAction, isInputOverriddenAt, isOverriddenAt, isReservedAlias, isValidSlotKey, legacyEnterToTransition, matchRouteParams, mediaQueryFor, mergeSwiperBreakpoints, nearestCommonAncestorId, nodeCssId, nodeUsesContext, normalizeCollectionResult, normalizeGalleryImage, normalizeIconClass, parentBreakpoint, parseUrlList, partClassName, planDataSource, planResponsiveRules, referencedDataSources, referencedDataSourcesDeep, renderDefaultDeclarations, resetSeoField, resolveBoundValue, resolveContextValue, resolveDerivedInto, resolveFlavorDefaults, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveRelativeInto, resolveRepeaterItems, resolveSeo, resolveSiblings, resolveSingleItemId, resolveStateStyles, resolveStyleProps, resolveValueChain, rewriteViewportUnits, safeSetInput, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, stateNamesIn, stripSuppressedProps, styleKeyToKebab, tokensToDarkVarMap, tokensToVarMap, validateFlavorPack, variableAliases, variableItemsFrom, withContextSlots, wrapInBreakpointQuery, wrapInMediaQuery };
|
|
2786
|
-
export type { BoundValueSpec, BreakpointDef, BuildRelatedFilterOptions, ContextActionEffect, ContextActionLike, ContextItemRef, ContextSlotDefault, ContextValueSpec, ContextWriteScope, DataSourceDomain, DataSourceEndpoints, DataSourceMode, DataSourcePlan, DataSourceQueryFilter, DataSourceRequest, DataSourceRequestSpec, DataSourceRouteFilter, DataSourceSpec, DesignTokenLike, FlavorPack, FlavorTokenDef, FormMessageResolver, GalleryBoundSource, GalleryDurations, GalleryEffect, GalleryEffectDescriptor, GalleryImage, GalleryLoopOptions, GalleryLoopScheduler, GalleryLoopState, GallerySlideState, IBuilderEventEmitter, InputMap, IoxContextSlot, IoxPipe, OverrideBreakpoint, PageStyleSettings, PageTransitionCategory, PageTransitionCategoryMeta, PageTransitionOptions, PageTransitionPreset, PipeArgSpec, PipeContext, PipeDescriptor, RelativeInputs, RelativeSourceKind, RelativeSourceSpec, ResolveSiblingsOptions, ResolvedPageTransition, ResponsiveLayer, ResponsiveOverrides, ResponsiveRuleSet, RouteContext, SeoDerivedContent, SeoFieldSource, SeoFieldState, SeoResolveContext, SetContextParams, SiblingsResult, StateStyleMap, StyleMap, StyleTreeNode, SwiperBreakpointMap, ToggleableAction, TransitionFeel, TransitionPhase, TransitionTimelineInput, ValueSource, VirtualTraitGroup };
|
|
2834
|
+
export { AOSService, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, CONTEXT_ACTION_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ContainerComponent, ContextStore, DEFAULT_FLAVOR, 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, DeclarationContext, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, LinkedContainerComponent, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, RESERVED_ALIAS_PREFIX, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_RESPONSIVE_TRAITS, SWISS_FLAVOR, SectionComponent, TRANSITION_FEELS, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, activeActions, apcaLc, applyBindingPipes, applyFeel, breakpointDef, breakpointForWidth, breakpointQueryFor, buildDataSourceFilter, buildPartSelector, buildRelatedFilter, buildSwiperBreakpoints, buildTargetedStateSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, collectContextSlotKeys, collectReferencedAliases, compileDeclarations, compilePageStyle, compileTokenCss, composeVirtualTraits, computeRelative, contextActionEffect, contextSlotKeys, countOverrides, dataSourcePlanToRequest, declaredInputNames, defaultMessageResolver, deriveBrandTokens, deriveSeoFromContent, describeSeoFields, effectiveTransitionId, excludeSelf, feelFor, flavorDefaultDeclarations, getByPath, getSeoValue, initialSlotValues, isActionEnabled, isContextAction, isInputOverriddenAt, isOverriddenAt, isReservedAlias, isValidSlotKey, legacyEnterToTransition, matchRouteParams, mediaQueryFor, mergeSwiperBreakpoints, nearestCommonAncestorId, nodeCssId, nodeUsesContext, normalizeCollectionResult, normalizeGalleryImage, normalizeIconClass, oklabToRgb, parentBreakpoint, parseHexColor, parseUrlList, partClassName, planDataSource, planResponsiveRules, referencedDataSources, referencedDataSourcesDeep, relativeLuminance, renderDefaultDeclarations, resetSeoField, resolveBoundValue, resolveContextValue, resolveDerivedInto, resolveFlavorDefaults, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveRelativeInto, resolveRepeaterItems, resolveSeo, resolveSiblings, resolveSingleItemId, resolveStateStyles, resolveStyleProps, resolveValueChain, rewriteViewportUnits, rgbToOklab, safeSetInput, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, stateNamesIn, stripSuppressedProps, styleKeyToKebab, toHexColor, tokensToDarkVarMap, tokensToVarMap, validateFlavorPack, variableAliases, variableItemsFrom, wcagContrastRatio, withContextSlots, wrapInBreakpointQuery, wrapInMediaQuery };
|
|
2835
|
+
export type { BoundValueSpec, BreakpointDef, BuildRelatedFilterOptions, ContextActionEffect, ContextActionLike, ContextItemRef, ContextSlotDefault, ContextValueSpec, ContextWriteScope, DataSourceDomain, DataSourceEndpoints, DataSourceMode, DataSourcePlan, DataSourceQueryFilter, DataSourceRequest, DataSourceRequestSpec, DataSourceRouteFilter, DataSourceSpec, DesignTokenLike, FlavorPack, FlavorTokenDef, FormMessageResolver, GalleryBoundSource, GalleryDurations, GalleryEffect, GalleryEffectDescriptor, GalleryImage, GalleryLoopOptions, GalleryLoopScheduler, GalleryLoopState, GallerySlideState, IBuilderEventEmitter, InputMap, IoxContextSlot, IoxPipe, OkLab, OverrideBreakpoint, PageStyleSettings, PageTransitionCategory, PageTransitionCategoryMeta, PageTransitionOptions, PageTransitionPreset, PipeArgSpec, PipeContext, PipeDescriptor, RelativeInputs, RelativeSourceKind, RelativeSourceSpec, ResolveSiblingsOptions, ResolvedPageTransition, ResponsiveLayer, ResponsiveOverrides, ResponsiveRuleSet, Rgb, RouteContext, SeoDerivedContent, SeoFieldSource, SeoFieldState, SeoResolveContext, SetContextParams, SiblingsResult, StateStyleMap, StyleMap, StyleTreeNode, SwiperBreakpointMap, ToggleableAction, TransitionFeel, TransitionPhase, TransitionTimelineInput, ValueSource, VirtualTraitGroup };
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@ export type ActionKind = 'animated' | 'instant' | 'server';
|
|
|
11
11
|
|
|
12
12
|
/** Picker grouping. Flat lists die at ~10 entries; the group is data, never a second list. */
|
|
13
13
|
export type ActionGroup =
|
|
14
|
-
| 'motion' | 'visibility' | 'navigate' | 'dialog' | 'form' | 'data' | 'service';
|
|
14
|
+
| 'motion' | 'visibility' | 'navigate' | 'dialog' | 'form' | 'data' | 'consent' | 'service';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* What a step does inside the live builder canvas (the canvas RUNS interactions):
|
|
@@ -25,6 +25,8 @@ export interface ActionParamSpec {
|
|
|
25
25
|
name: string;
|
|
26
26
|
type: 'number' | 'string' | 'boolean' | 'page' | 'node' | 'form';
|
|
27
27
|
label: string;
|
|
28
|
+
/** Closed value set — the editor renders a select instead of a free text input. */
|
|
29
|
+
options?: readonly { value: string; labelKey: string }[];
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
/** Does this action point at another node — and may it be any node or only certain types? */
|
|
@@ -59,6 +59,22 @@ export const ELEMENT_ACTION_DESCRIPTORS: readonly ActionDescriptor[] = Object.fr
|
|
|
59
59
|
{ type: 'copyToClipboard', labelKey: 'builder.actions.copyToClipboard', kind: 'instant', group: 'data', builderBehaviour: 'run',
|
|
60
60
|
params: [{ name: 'text', type: 'string', label: 'Text' }] },
|
|
61
61
|
|
|
62
|
+
// ── Consent (C3 — the banner's vocabulary; all pure writes of the consent cookie) ──
|
|
63
|
+
// The canvas SIMULATES the recording ones: the builder runs in the ADMIN's browser, and an
|
|
64
|
+
// admin's cookie is not a visitor's decision. Opening the preferences dialog is honest to run.
|
|
65
|
+
{ type: 'grantConsent', labelKey: 'builder.actions.grantConsent', kind: 'instant', group: 'consent', builderBehaviour: 'simulate' },
|
|
66
|
+
{ type: 'denyConsent', labelKey: 'builder.actions.denyConsent', kind: 'instant', group: 'consent', builderBehaviour: 'simulate' },
|
|
67
|
+
{ type: 'setConsentCategory', labelKey: 'builder.actions.setConsentCategory', kind: 'instant', group: 'consent', builderBehaviour: 'simulate',
|
|
68
|
+
params: [
|
|
69
|
+
{ name: 'category', type: 'string', label: 'Category', options: [
|
|
70
|
+
{ value: 'analytics', labelKey: 'builder.actions.params.categoryAnalytics' },
|
|
71
|
+
{ value: 'marketing', labelKey: 'builder.actions.params.categoryMarketing' },
|
|
72
|
+
] },
|
|
73
|
+
{ name: 'granted', type: 'boolean', label: 'Granted' },
|
|
74
|
+
] },
|
|
75
|
+
{ type: 'openConsentPreferences', labelKey: 'builder.actions.openConsentPreferences', kind: 'instant', group: 'consent', builderBehaviour: 'run',
|
|
76
|
+
target: { required: true, types: ['Dialog'] } },
|
|
77
|
+
|
|
62
78
|
// ── Services (D-A5: visible, greyed, with the reason — never hidden, never live) ──
|
|
63
79
|
{ type: 'signIn', labelKey: 'builder.actions.signIn', kind: 'server', group: 'service', builderBehaviour: 'inert',
|
|
64
80
|
planned: true, entitlement: 'visitorIdentity' },
|
|
@@ -71,9 +87,14 @@ export const ELEMENT_ACTION_DESCRIPTORS: readonly ActionDescriptor[] = Object.fr
|
|
|
71
87
|
export const BEHAVIOUR_ACTION_TYPES: ReadonlySet<string> =
|
|
72
88
|
new Set(['navigate', 'scrollTo', 'submitForm', 'copyToClipboard']);
|
|
73
89
|
|
|
90
|
+
/** The consent actions' dispatch set (C3) — the engines branch on it beside DIALOG/BEHAVIOUR.
|
|
91
|
+
* `openConsentPreferences` is dialog-opening by nature and included so ONE branch owns consent. */
|
|
92
|
+
export const CONSENT_ACTION_TYPES: ReadonlySet<string> =
|
|
93
|
+
new Set(['grantConsent', 'denyConsent', 'setConsentCategory', 'openConsentPreferences']);
|
|
94
|
+
|
|
74
95
|
/** Picker order — most-used groups first, services last (they are the shop window, not the shop). */
|
|
75
96
|
export const ELEMENT_ACTION_GROUP_ORDER: readonly string[] =
|
|
76
|
-
Object.freeze(['motion', 'visibility', 'navigate', 'dialog', 'form', 'data', 'service']);
|
|
97
|
+
Object.freeze(['motion', 'visibility', 'navigate', 'dialog', 'form', 'data', 'consent', 'service']);
|
|
77
98
|
|
|
78
99
|
/** The catalog, grouped and ordered for the picker — one derivation, no hand-kept second list. */
|
|
79
100
|
export function groupedElementActions(): ReadonlyArray<{ group: string; actions: ActionDescriptor[] }> {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ConsentCategory, ConsentDecision, hasConsent } from './consent.model';
|
|
2
|
+
import { gtagLoaderUrl } from './decisions';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Google Consent Mode v2 (consent plan §4): four parameters, mandatory for EEA/UK measurement
|
|
@@ -44,10 +45,11 @@ export function consentModeBootstrapJs(): string {
|
|
|
44
45
|
*/
|
|
45
46
|
export function analyticsInjectionHtml(measurementId: string, decision: ConsentDecision): string {
|
|
46
47
|
const id = (measurementId ?? '').trim();
|
|
47
|
-
|
|
48
|
+
const loader = gtagLoaderUrl(id);
|
|
49
|
+
if (!loader) return '';
|
|
48
50
|
if (!hasConsent(decision, ConsentCategory.Analytics)) return '';
|
|
49
51
|
const signals = JSON.stringify(toConsentModeSignals(decision));
|
|
50
52
|
return `<script>gtag('consent','update',${signals});</script>` +
|
|
51
|
-
`<script async src="
|
|
53
|
+
`<script async src="${loader}"></script>` +
|
|
52
54
|
`<script>gtag('js',new Date());gtag('config','${id}');</script>`;
|
|
53
55
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { ConsentCategory, ConsentDecision } from './consent.model';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Decision construction for the banner's actions (consent plan C3). Pure on purpose: the builder
|
|
5
|
+
* canvas SIMULATES these (it is the admin's browser, not a visitor's), the engine executes them,
|
|
6
|
+
* and the e2e asserts the cookies they produce — one implementation of what each button means.
|
|
7
|
+
*
|
|
8
|
+
* `necessary` is always granted and never asked. A deny writes an EXPLICIT false per offered
|
|
9
|
+
* category — a recorded "no" is a decision (`needsPrompt` contract, C1), never an absence.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** What an org offers when it has not chosen yet — the two non-necessary categories. */
|
|
13
|
+
export const DEFAULT_OFFERED_CATEGORIES: readonly ConsentCategory[] =
|
|
14
|
+
Object.freeze([ConsentCategory.Analytics, ConsentCategory.Marketing]);
|
|
15
|
+
|
|
16
|
+
function base(anonymousId: string, policyVersion: string, now: Date): ConsentDecision {
|
|
17
|
+
return {
|
|
18
|
+
categories: { [ConsentCategory.Necessary]: true },
|
|
19
|
+
policyVersion,
|
|
20
|
+
decidedAt: now.toISOString(),
|
|
21
|
+
anonymousId,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** "Accept all": every OFFERED category granted. */
|
|
26
|
+
export function grantAllDecision(
|
|
27
|
+
offered: readonly ConsentCategory[],
|
|
28
|
+
anonymousId: string,
|
|
29
|
+
policyVersion: string,
|
|
30
|
+
now: Date = new Date(),
|
|
31
|
+
): ConsentDecision {
|
|
32
|
+
const decision = base(anonymousId, policyVersion, now);
|
|
33
|
+
for (const category of offered) {
|
|
34
|
+
if (category !== ConsentCategory.Necessary) decision.categories[category] = true;
|
|
35
|
+
}
|
|
36
|
+
return decision;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** "Reject all": every OFFERED category recorded as an explicit no. */
|
|
40
|
+
export function denyAllDecision(
|
|
41
|
+
offered: readonly ConsentCategory[],
|
|
42
|
+
anonymousId: string,
|
|
43
|
+
policyVersion: string,
|
|
44
|
+
now: Date = new Date(),
|
|
45
|
+
): ConsentDecision {
|
|
46
|
+
const decision = base(anonymousId, policyVersion, now);
|
|
47
|
+
for (const category of offered) {
|
|
48
|
+
if (category !== ConsentCategory.Necessary) decision.categories[category] = false;
|
|
49
|
+
}
|
|
50
|
+
return decision;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* One category set explicitly (the preferences modal's per-category buttons). Carries every
|
|
55
|
+
* other category of the PREVIOUS decision forward; with no previous decision the rest stay
|
|
56
|
+
* unrecorded — the prompt logic still owes the visitor a first-layer ask.
|
|
57
|
+
*/
|
|
58
|
+
export function setCategoryDecision(
|
|
59
|
+
previous: ConsentDecision | null | undefined,
|
|
60
|
+
category: ConsentCategory,
|
|
61
|
+
granted: boolean,
|
|
62
|
+
anonymousId: string,
|
|
63
|
+
policyVersion: string,
|
|
64
|
+
now: Date = new Date(),
|
|
65
|
+
): ConsentDecision {
|
|
66
|
+
const decision = base(anonymousId, policyVersion, now);
|
|
67
|
+
if (previous?.categories) {
|
|
68
|
+
for (const [key, value] of Object.entries(previous.categories)) {
|
|
69
|
+
decision.categories[key as ConsentCategory] = value === true;
|
|
70
|
+
}
|
|
71
|
+
decision.categories[ConsentCategory.Necessary] = true;
|
|
72
|
+
}
|
|
73
|
+
if (category !== ConsentCategory.Necessary) decision.categories[category] = granted;
|
|
74
|
+
return decision;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The per-site config the SSR server embeds for the prompt + the post-grant loader (C3). */
|
|
78
|
+
export interface ConsentSiteConfig {
|
|
79
|
+
policyVersion: string;
|
|
80
|
+
/** The categories the org offers beyond `necessary`. */
|
|
81
|
+
categories: readonly ConsentCategory[];
|
|
82
|
+
/** Site-tag id so a grant can inject the loader WITHOUT a reload; null = none configured. */
|
|
83
|
+
measurementId?: string | null;
|
|
84
|
+
/** Route of the org's policy page, for the banner's link; null = not chosen (C5 blocks publish). */
|
|
85
|
+
policyRoute?: string | null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The window global the engine's runtime reads the config from. */
|
|
89
|
+
export const CONSENT_CONFIG_GLOBAL = '__ioxConsentCfg';
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The inline `<head>` embed carrying the config to the browser. Values are JSON-escaped and
|
|
93
|
+
* `<` is unicode-escaped so user-set strings (a policy version is author input) can never
|
|
94
|
+
* close the script tag.
|
|
95
|
+
*/
|
|
96
|
+
export function consentConfigJs(config: ConsentSiteConfig): string {
|
|
97
|
+
const json = JSON.stringify({
|
|
98
|
+
policyVersion: config.policyVersion,
|
|
99
|
+
categories: config.categories,
|
|
100
|
+
measurementId: config.measurementId ?? null,
|
|
101
|
+
policyRoute: config.policyRoute ?? null,
|
|
102
|
+
}).replace(/</g, '\\u003c');
|
|
103
|
+
return `window.${CONSENT_CONFIG_GLOBAL}=${json};`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** The gtag loader URL for a measurement id — one spelling for SSR injection and client inject. */
|
|
107
|
+
export function gtagLoaderUrl(measurementId: string): string | null {
|
|
108
|
+
const id = (measurementId ?? '').trim();
|
|
109
|
+
if (!id || !/^[A-Za-z0-9-]+$/.test(id)) return null;
|
|
110
|
+
return `https://www.googletagmanager.com/gtag/js?id=${id}`;
|
|
111
|
+
}
|
|
@@ -36,8 +36,12 @@ export function decode(raw: string | null | undefined): ConsentDecision | null {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
/**
|
|
40
|
-
|
|
39
|
+
/**
|
|
40
|
+
* `Set-Cookie` attributes for the decision: a NECESSARY cookie, 12 months, first-party.
|
|
41
|
+
* `secure` is a parameter because a Secure cookie is silently DROPPED on plain http — the
|
|
42
|
+
* engine passes `location.protocol === 'https:'` so dev servers and the e2e still record.
|
|
43
|
+
*/
|
|
44
|
+
export function cookieAttributes(maxMonths = 12, secure = true): string {
|
|
41
45
|
const maxAge = Math.floor(maxMonths * 30.44 * 24 * 60 * 60);
|
|
42
|
-
return `Path=/; Max-Age=${maxAge}; SameSite=Lax; Secure`;
|
|
46
|
+
return `Path=/; Max-Age=${maxAge}; SameSite=Lax${secure ? '; Secure' : ''}`;
|
|
43
47
|
}
|