@vectoriox/iox-ui 5.31.2 → 5.32.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.
@@ -105,10 +105,12 @@ declare const SPEC_SITE_PATTERNS: {
105
105
  readonly family: "nav";
106
106
  readonly label: "Navigation bar";
107
107
  readonly global: "before";
108
+ readonly phoneMenu: "drawer";
108
109
  readonly node: {
109
110
  readonly t: "Container";
110
111
  readonly es: "nav";
111
112
  readonly tag: "nav";
113
+ readonly role: "nav";
112
114
  readonly lay: {
113
115
  readonly display: "flex";
114
116
  readonly flexWrap: "wrap";
@@ -120,6 +122,7 @@ declare const SPEC_SITE_PATTERNS: {
120
122
  readonly t: "LinkedContainer";
121
123
  readonly es: "box";
122
124
  readonly href: "/";
125
+ readonly role: "nav.brand";
123
126
  readonly c: readonly [{
124
127
  readonly t: "Heading";
125
128
  readonly es: "h3";
@@ -130,6 +133,7 @@ declare const SPEC_SITE_PATTERNS: {
130
133
  }, {
131
134
  readonly t: "Container";
132
135
  readonly es: "box";
136
+ readonly role: "nav.links";
133
137
  readonly lay: {
134
138
  readonly display: "flex";
135
139
  readonly flexWrap: "wrap";
@@ -143,12 +147,14 @@ declare const SPEC_SITE_PATTERNS: {
143
147
  readonly each: "$navPages";
144
148
  readonly bind: "page.name";
145
149
  readonly href: "$page.route";
150
+ readonly role: "nav.link";
146
151
  }, {
147
152
  readonly t: "ButtonBlock";
148
153
  readonly es: "button-primary";
149
154
  readonly href: "/contact";
150
155
  readonly slot: "cta";
151
156
  readonly text: "Get in touch";
157
+ readonly role: "nav.cta";
152
158
  }];
153
159
  }];
154
160
  };
@@ -1160,9 +1166,11 @@ interface SitePlan {
1160
1166
  * per page in the navigation (`$navPages`) · `href` a page route (`$page.route` = the stamped page's) ·
1161
1167
  * `param` the value of the route's `:param` · `optional` a part a page may leave out (`without`) ·
1162
1168
  * `preset` a forms-core form preset (its fields, submit and Success/Error words) · `control` / `label` /
1163
- * `required` a Field's · `c` children · `recipe` a composite-core recipe stamped whole (`card`: Card v2 —
1164
- * its `layout`, the optional `parts` it leaves out, and `binds`, a binding path per part: `image`,
1165
- * `eyebrow`, `title`, `text`; `href` + `param` link the whole card through its title).
1169
+ * `required` a Field's · `role` the node's part in its composite (`nav.brand`…) · `c` children · `recipe` a
1170
+ * composite-core recipe stamped whole (`card`: Card v2 — its `layout`, the optional `parts` it leaves out, and
1171
+ * `binds`, a binding path per part: `image`, `eyebrow`, `title`, `text`; `href` + `param` link the whole card
1172
+ * through its title). A header pattern's `phoneMenu` names its phone shape (drawer · bar · both), stamped by
1173
+ * `planNavShape` (CC1).
1166
1174
  */
1167
1175
  interface PatternNode {
1168
1176
  readonly t: string;
@@ -1188,6 +1196,8 @@ interface PatternNode {
1188
1196
  readonly control?: string;
1189
1197
  readonly label?: string;
1190
1198
  readonly required?: boolean;
1199
+ /** The node's part in its composite (`nav.brand`, `nav.links`…), written to the node's `role`. */
1200
+ readonly role?: string;
1191
1201
  readonly c?: ReadonlyArray<PatternNode>;
1192
1202
  readonly recipe?: string;
1193
1203
  readonly layout?: string;
@@ -1216,6 +1226,8 @@ interface SitePattern {
1216
1226
  readonly label: string;
1217
1227
  /** Set on a header/footer pattern: it renders on every page, before or after the page. */
1218
1228
  readonly global?: `${PatternGlobalPosition}`;
1229
+ /** A navigation pattern's phone shape (CC1): `planNavShape` adds it after the pattern is stamped. */
1230
+ readonly phoneMenu?: 'drawer' | 'bar' | 'both';
1219
1231
  readonly node: PatternNode;
1220
1232
  }
1221
1233
  declare const SITE_PATTERNS: Readonly<Record<string, SitePattern>>;
@@ -1316,7 +1328,8 @@ interface SerializedNode {
1316
1328
  stateStyles?: Partial<Record<string, Record<string, unknown>>>;
1317
1329
  responsive?: SerializedResponsive;
1318
1330
  elementStyle?: string;
1319
- /** The part this node plays in a composite (composite-core: `card.title`). */
1331
+ /** The part this node plays in a composite (composite-core: `card.title`; a navigation's `nav.drawer`,
1332
+ * written by `planNavShape`). Additive; a node without one is a plain node. */
1320
1333
  role?: string;
1321
1334
  bindings: SerializedBinding[];
1322
1335
  interactions: unknown[];
@@ -1537,5 +1550,112 @@ declare function parseSampleImageRef(value: unknown): number | null;
1537
1550
  */
1538
1551
  declare function composeSite(plan: SitePlan, context: ComposeContext): ComposedSite;
1539
1552
 
1540
- export { BASELINE_ROLES, BaselineRole, BindScope, CardBindTarget, ComposeError, ComposeIssueCode, DANGLING_LINK_CODES, FACT_KEYS, GlobalSlot, NEUTRAL_STYLE_PROPS, NODE_BASELINES, PatternDirective, PatternGlobalPosition, PatternPlaceholder, PatternRecipe, PlaceholderKind, PlanDataSourceMode, PlanDataSourceType, RelativeOp, SAMPLE_IMAGE_REF, SITE_FACT_LABELS, SITE_PATTERNS, SITE_PLAN_VERSION, SITE_TEMPLATES, SPEC_SITE_FACTS, SPEC_SITE_PATTERNS, SPEC_SITE_PLAN_SHAPE, SPEC_SITE_TEMPLATES, STUDIO_TEMPLATE, SitePlanError, SitePlanIssueCode, SitePlanSourceKind, SiteTemplateError, SiteTemplateId, altSourceScopes, assertStyleSlots, assertValidSitePlan, composeSite, createSerializedNode, nextSourceOf, nodeBindPaths, parseSampleImageRef, patternById, patternNeedsBinding, patternNodes, patternOptionalParts, patternScopes, patternSlots, planFromTemplate, releasedLayoutDefaults, requiredShapeKeys, routeParams, sampleImageRef, setStyleProps, shapeKeys, validateSitePlan, walkSerialized };
1541
- export type { CmsDataSourcePlan, ComposeContext, ComposeIssue, ComposedDataSource, ComposedItem, ComposedPage, ComposedPlaceholder, ComposedRoute, ComposedSite, ContentFieldPlan, ContentSeedPlan, ContentTypePlan, CreateNodeOptions, DataSourcePlan, FactKey, NodeBaseline, PagePlan, PageSectionPlan, PatternNode, RelativeDataSourcePlan, SerializedBinding, SerializedNode, SerializedResponsive, SiteBusiness, SitePattern, SitePlan, SitePlanIssue, SitePlanSource, SitePlanValidation, SiteTemplate, TemplatePlanInput };
1553
+ /**
1554
+ * The phone shapes of a navigation bar (complex-components plan CC1, §7a + R1; boards 1–3). ONE pure tree
1555
+ * edit, `planNavShape`, turns a site's header into the shape the author (the Navigation bar's "Phone
1556
+ * menu" section) or the composer (`nav.inline-cta`'s `phoneMenu`) asks for:
1557
+ *
1558
+ * - **Drawer** — a menu button in the header opens a slide-in Dialog at the end edge that lists every
1559
+ * page (board 1). The drawer is a child of the Navigation bar.
1560
+ * - **Bottom bar** — an app-style bar fixed to the bottom of the screen with up to five pages, no drawer
1561
+ * (board 2). The phone header keeps only the brand (Q4).
1562
+ * - **Both** — the bar with four pages and More, which opens the drawer; the drawer is then its own
1563
+ * global, beside the Navigation bar (board 2's layer tree).
1564
+ *
1565
+ * Every part is a real node with a `role` (R2): editable, selectable, styled by its element style. Parts
1566
+ * a shape keeps are passed through untouched, so an author's edits survive a change of shape; the parts
1567
+ * it no longer has are removed (undo brings them back — the caller records ONE history step). Nothing
1568
+ * here reads a clock or a random source: ids come from `newId` (deterministic by default, so the
1569
+ * composer's output is byte-stable).
1570
+ */
1571
+ declare enum NavShape {
1572
+ Drawer = "drawer",
1573
+ Bar = "bar",
1574
+ Both = "both"
1575
+ }
1576
+ /** The roles of a navigation's parts (the composite plan's `role` field, here for the nav). */
1577
+ declare enum NavRole {
1578
+ Root = "nav",
1579
+ Brand = "nav.brand",
1580
+ Links = "nav.links",
1581
+ Link = "nav.link",
1582
+ Cta = "nav.cta",
1583
+ MenuButton = "nav.menu-button",
1584
+ Drawer = "nav.drawer",
1585
+ DrawerHead = "nav.drawer-head",
1586
+ DrawerClose = "nav.drawer-close",
1587
+ DrawerLinks = "nav.drawer-links",
1588
+ DrawerLink = "nav.drawer-link",
1589
+ DrawerCta = "nav.drawer-cta",
1590
+ Bar = "nav.bar",
1591
+ BarItem = "nav.bar-item",
1592
+ More = "nav.more"
1593
+ }
1594
+ /** The breakpoints a phone menu may switch at (the responsive Breakpoint ids; Q5: Mobile landscape). */
1595
+ declare enum PhoneMenuBreakpoint {
1596
+ Tablet = "tablet",
1597
+ Mobile = "mobile",
1598
+ Small = "small"
1599
+ }
1600
+ declare const DEFAULT_PHONE_MENU_BREAKPOINT = PhoneMenuBreakpoint.Mobile;
1601
+ /** The bar's item count, More included (Q2: 3–5; iOS and Material both cap at five). */
1602
+ declare const BAR_ITEMS_MIN = 3;
1603
+ declare const BAR_ITEMS_MAX = 5;
1604
+ interface NavShapeOptions {
1605
+ /** Where the phone shape takes over (≤ this breakpoint). Default Mobile landscape (≤767). */
1606
+ at?: PhoneMenuBreakpoint | `${PhoneMenuBreakpoint}`;
1607
+ /** Items in the bar, More included (3–5). Default: 5 for Bottom bar, 4 + More for Both. */
1608
+ barItems?: number;
1609
+ /** A new part's id (the builder passes its id generator). Default: `<nav id>-<suffix>`. */
1610
+ newId?: (suffix: string) => string;
1611
+ }
1612
+ interface NavShapeResult {
1613
+ /** The globals after the edit (the Navigation bar edited in place, the drawer/bar added or removed). */
1614
+ globals: SerializedNode[];
1615
+ shape: NavShape;
1616
+ at: PhoneMenuBreakpoint;
1617
+ }
1618
+ declare class NavShapeError extends Error {
1619
+ }
1620
+ /** The words the parts carry — site copy, editable like any other (the composer writes English copy). */
1621
+ declare const NAV_SHAPE_COPY: {
1622
+ readonly menu: "Menu";
1623
+ readonly closeMenu: "Close menu";
1624
+ readonly more: "More";
1625
+ readonly home: "Home";
1626
+ };
1627
+ /** The layer names of the parts (R2: the tree, the breadcrumb and the toolbar read them). */
1628
+ declare const NAV_PART_LABELS: Readonly<Record<string, string>>;
1629
+ declare function barIconForRoute(route: string): string;
1630
+ /** One page the navigation links to, read off its links. */
1631
+ interface NavPage {
1632
+ label: string;
1633
+ route: string;
1634
+ }
1635
+ /** The Navigation bar: the global with the root role, else the first global wearing the `nav` style. */
1636
+ declare function findNavRoot(globals: ReadonlyArray<SerializedNode>, navId?: string | null): SerializedNode | null;
1637
+ /** The shape a site's globals are in now, or null when there is no Navigation bar. */
1638
+ declare function navShapeOf(globals: ReadonlyArray<SerializedNode>): NavShape | null;
1639
+ /** The breakpoint the phone shape takes over at, read off the header's links row (default Mobile). */
1640
+ declare function navShapeBreakpoint(globals: ReadonlyArray<SerializedNode>): PhoneMenuBreakpoint;
1641
+ /** The pages the header links to, in order: its Links (W1.1 — one plain Link per page in the navigation). */
1642
+ declare function navPages(root: SerializedNode): NavPage[];
1643
+ /**
1644
+ * Put a site's header into `shape`. The Navigation bar is `navId` or the global carrying the root role
1645
+ * (else the first Container wearing `nav`, which this then marks with roles). Returns new globals;
1646
+ * the input is not mutated.
1647
+ */
1648
+ declare function planNavShape(globals: ReadonlyArray<SerializedNode>, shape: NavShape, options?: NavShapeOptions, navId?: string | null): NavShapeResult;
1649
+ interface AddBarItemResult {
1650
+ globals: SerializedNode[];
1651
+ /** False when the bar is full (5 items, More included — Q2) or there is no bar. */
1652
+ added: boolean;
1653
+ }
1654
+ /**
1655
+ * The bar's Items list "Add item" (board 2): one more item, before More — the next navigation page the
1656
+ * bar does not link to yet (Home when every page is there). Refused at five (Q2). Input not mutated.
1657
+ */
1658
+ declare function addBarItem(globals: ReadonlyArray<SerializedNode>, options?: Pick<NavShapeOptions, 'newId'>): AddBarItemResult;
1659
+
1660
+ export { BAR_ITEMS_MAX, BAR_ITEMS_MIN, BASELINE_ROLES, BaselineRole, BindScope, CardBindTarget, ComposeError, ComposeIssueCode, DANGLING_LINK_CODES, DEFAULT_PHONE_MENU_BREAKPOINT, FACT_KEYS, GlobalSlot, NAV_PART_LABELS, NAV_SHAPE_COPY, NEUTRAL_STYLE_PROPS, NODE_BASELINES, NavRole, NavShape, NavShapeError, PatternDirective, PatternGlobalPosition, PatternPlaceholder, PatternRecipe, PhoneMenuBreakpoint, PlaceholderKind, PlanDataSourceMode, PlanDataSourceType, RelativeOp, SAMPLE_IMAGE_REF, SITE_FACT_LABELS, SITE_PATTERNS, SITE_PLAN_VERSION, SITE_TEMPLATES, SPEC_SITE_FACTS, SPEC_SITE_PATTERNS, SPEC_SITE_PLAN_SHAPE, SPEC_SITE_TEMPLATES, STUDIO_TEMPLATE, SitePlanError, SitePlanIssueCode, SitePlanSourceKind, SiteTemplateError, SiteTemplateId, addBarItem, altSourceScopes, assertStyleSlots, assertValidSitePlan, barIconForRoute, composeSite, createSerializedNode, findNavRoot, navPages, navShapeBreakpoint, navShapeOf, nextSourceOf, nodeBindPaths, parseSampleImageRef, patternById, patternNeedsBinding, patternNodes, patternOptionalParts, patternScopes, patternSlots, planFromTemplate, planNavShape, releasedLayoutDefaults, requiredShapeKeys, routeParams, sampleImageRef, setStyleProps, shapeKeys, validateSitePlan, walkSerialized };
1661
+ export type { AddBarItemResult, CmsDataSourcePlan, ComposeContext, ComposeIssue, ComposedDataSource, ComposedItem, ComposedPage, ComposedPlaceholder, ComposedRoute, ComposedSite, ContentFieldPlan, ContentSeedPlan, ContentTypePlan, CreateNodeOptions, DataSourcePlan, FactKey, NavPage, NavShapeOptions, NavShapeResult, NodeBaseline, PagePlan, PageSectionPlan, PatternNode, RelativeDataSourcePlan, SerializedBinding, SerializedNode, SerializedResponsive, SiteBusiness, SitePattern, SitePlan, SitePlanIssue, SitePlanSource, SitePlanValidation, SiteTemplate, TemplatePlanInput };
package/index.d.ts CHANGED
@@ -5,6 +5,7 @@ import { Observable } from 'rxjs';
5
5
  import * as i3 from '@angular/platform-browser/animations';
6
6
  import Lenis from 'lenis';
7
7
  import * as i2 from '@angular/common';
8
+ import { OpenerNodeLike } from '@vectoriox/iox-ui/actions-core';
8
9
  export * from '@vectoriox/iox-ui/design-core';
9
10
 
10
11
  declare class ViewPositionDirective implements AfterViewInit {
@@ -1397,6 +1398,48 @@ declare class IoxComponentRegistryService {
1397
1398
  static ɵprov: i0.ɵɵInjectableDeclaration<IoxComponentRegistryService>;
1398
1399
  }
1399
1400
 
1401
+ /**
1402
+ * Which Swiper modules a Slider loads, and how its autoplay boots — a pure decision, so the module list
1403
+ * is unit-tested (CC1 R1/R2 floor; complex-components plan §7c). Swiper's A11y module is ALWAYS loaded:
1404
+ * it gives the container `aria-roledescription="carousel"`, every slide "slide" and an "n of m" label, and
1405
+ * keeps the live region quiet while it rotates. Autoplay never boots under reduced motion (Publishable
1406
+ * gate #6). The rotation pause control and the slider parts as nodes are CC3.
1407
+ */
1408
+ declare enum SwiperModuleName {
1409
+ A11y = "A11y",
1410
+ Navigation = "Navigation",
1411
+ Pagination = "Pagination",
1412
+ Scrollbar = "Scrollbar",
1413
+ Autoplay = "Autoplay",
1414
+ Keyboard = "Keyboard",
1415
+ Mousewheel = "Mousewheel",
1416
+ Zoom = "Zoom",
1417
+ Grid = "Grid",
1418
+ Parallax = "Parallax"
1419
+ }
1420
+ interface SwiperModuleFlags {
1421
+ enableNavigation?: boolean;
1422
+ enablePagination?: boolean;
1423
+ enableScrollbar?: boolean;
1424
+ enableAutoplay?: boolean;
1425
+ enableKeyboard?: boolean;
1426
+ enableMousewheel?: boolean;
1427
+ enableZoom?: boolean;
1428
+ enableGrid?: boolean;
1429
+ enableParallax?: boolean;
1430
+ }
1431
+ /** True when the Slider's autoplay may start: on, and the visitor has not asked for reduced motion. */
1432
+ declare function swiperAutoplayAllowed(enableAutoplay: boolean | undefined, motionAllowed: boolean): boolean;
1433
+ /** The modules to load, A11y first. */
1434
+ declare function swiperModuleNames(flags: SwiperModuleFlags, motionAllowed: boolean): SwiperModuleName[];
1435
+ /** Swiper's A11y options: carousel / slide role descriptions and an "n of m" slide label. */
1436
+ declare const SWIPER_A11Y_OPTIONS: {
1437
+ readonly enabled: true;
1438
+ readonly containerRoleDescriptionMessage: "carousel";
1439
+ readonly itemRoleDescriptionMessage: "slide";
1440
+ readonly slideLabelMessage: "{{index}} of {{slidesLength}}";
1441
+ };
1442
+
1400
1443
  /**
1401
1444
  * style-compiler — the SINGLE source of truth for turning a node's style map into
1402
1445
  * CSS declarations. Framework-agnostic (no Angular): consumed by both renderers.
@@ -1465,6 +1508,24 @@ declare function compileDeclarations(props: Record<string, any>, importantProps?
1465
1508
  declare const STATE_PSEUDO_CLASS: Readonly<Record<string, string>>;
1466
1509
  /** The pseudo-class a stored state compiles to (`focus` → `focus-visible`, everything else itself). */
1467
1510
  declare function statePseudoClass(state: string): string;
1511
+ /**
1512
+ * Component states (CC1 N2; composite plan T12): states a node is IN because of what it is, not where
1513
+ * the pointer is — so they compile to an ATTRIBUTE the renderers write, never a pseudo-class. `current`
1514
+ * is the link to the page the visitor is on (`aria-current="page"`, written by `nodeA11yAttributes`),
1515
+ * so the look and the announcement are one fact.
1516
+ */
1517
+ declare enum ComponentState {
1518
+ Current = "current"
1519
+ }
1520
+ /** The attribute selector each component state compiles to. */
1521
+ declare const COMPONENT_STATE_SELECTOR: Readonly<Record<string, string>>;
1522
+ /** The component states a type offers in the Style panel's "Component state" group. */
1523
+ declare function componentStatesFor(type: string | null | undefined): ComponentState[];
1524
+ /**
1525
+ * What follows `.iox-node-<id>` in a stored state's rule: `:hover`, `:focus-visible`, or a component
1526
+ * state's attribute selector (`[aria-current="page"]`). Shared by the builder canvas and both engine paths.
1527
+ */
1528
+ declare function stateSelectorSuffix(state: string): string;
1468
1529
  /** The states driven by a pointer hovering something — each earns the default hover transition. */
1469
1530
  declare const HOVER_STATES: ReadonlyArray<string>;
1470
1531
  /** True when the node restyles on hover (its own, its parent's or a chosen trigger's). */
@@ -1680,6 +1741,182 @@ declare function buildSwiperBreakpoints(inputs: InputMap | null | undefined, res
1680
1741
  */
1681
1742
  declare function mergeSwiperBreakpoints(derived: SwiperBreakpointMap, advancedJson: string | null | undefined): SwiperBreakpointMap | undefined;
1682
1743
 
1744
+ /**
1745
+ * "This link is the page you're on" (CC1, N2). ONE decision for the builder canvas and both engine
1746
+ * paths: a link whose target is the current page renders `aria-current="page"` — in the raw SSR too —
1747
+ * and the `current` style state compiles to that attribute (`[aria-current="page"]`), so the look and
1748
+ * the announcement can never disagree.
1749
+ *
1750
+ * Paths are compared APP-relative: the engine's string path writes base-relative hrefs (`work`, `./`
1751
+ * for home), the canvas writes the route (`/work`), and a browser hands back an absolute pathname
1752
+ * under the tenant's `<base href="/<org>/">` — `baseHref` strips that.
1753
+ */
1754
+ interface CurrentLinkOptions {
1755
+ /** Also current on the pages BELOW the link's (`/work` while on `/work/sample-project`). Never for home. */
1756
+ prefix?: boolean;
1757
+ /** The document's base path (`/<org>/`), stripped from both sides when present. */
1758
+ baseHref?: string | null;
1759
+ }
1760
+ /** The value a current link carries. */
1761
+ declare const ARIA_CURRENT_PAGE = "page";
1762
+ /** True for an href that leaves the site (a scheme, protocol-relative) or stays on the page (`#…`, empty). */
1763
+ declare function isExternalOrFragment(href: string | null | undefined): boolean;
1764
+ /**
1765
+ * An app path in one comparable form: no query or hash, no base, no leading `./` or slashes, no
1766
+ * trailing slash. Home is `''`.
1767
+ */
1768
+ declare function normalizeAppPath(path: string | null | undefined, baseHref?: string | null): string;
1769
+ /** Whether `href` points at the page at `currentPath` (both app-relative, or absolute under `baseHref`). */
1770
+ declare function isCurrentLink(href: string | null | undefined, currentPath: string | null | undefined, options?: CurrentLinkOptions): boolean;
1771
+
1772
+ /**
1773
+ * The content traits the accessibility attributes are read from (CC1). Import-free on purpose: the
1774
+ * builder's component configs declare these traits, and the node-baseline generator bundles those
1775
+ * configs against this file directly.
1776
+ */
1777
+ declare enum A11yTrait {
1778
+ /** "Accessibility ▸ Accessible name" — the name of an icon-only control (and of a Dialog). */
1779
+ AccessibleName = "ariaLabel",
1780
+ /** A Dialog's "Close when wider than": a breakpoint id, or empty = never. */
1781
+ CloseAbove = "closeAbove"
1782
+ }
1783
+ /** The types that carry an Accessible name trait. */
1784
+ declare const ACCESSIBLE_NAME_TYPES: ReadonlyArray<string>;
1785
+ /** The types that link to a page and can therefore be the current page's link. */
1786
+ declare const CURRENT_LINK_TYPES: ReadonlyArray<string>;
1787
+
1788
+ /**
1789
+ * The accessibility attributes a node renders (CC1: N2 current page, N3 dialog openers, N4 a drawer
1790
+ * that closes when the window widens, N6 accessible names). ONE function decides them and every
1791
+ * renderer applies the answer with its own transport: the engine's string path serialises it, the
1792
+ * engine's component path and the builder canvas set it on the node's element. A node that wants
1793
+ * none of it gets `{}` and renders exactly as before.
1794
+ */
1795
+ /** The types that are pure decoration to assistive technology (an Icon has no name of its own). */
1796
+ declare const DECORATIVE_TYPES: ReadonlyArray<string>;
1797
+ declare const DIALOG_NODE_TYPE = "Dialog";
1798
+ /** Carries the min-width (px) at which an open dialog closes itself; the engine listens on it. */
1799
+ declare const DIALOG_CLOSE_ABOVE_ATTRIBUTE = "data-iox-dialog-close-above";
1800
+ declare enum A11yAttribute {
1801
+ Label = "aria-label",
1802
+ Hidden = "aria-hidden",
1803
+ Current = "aria-current",
1804
+ Id = "id"
1805
+ }
1806
+ /** Every attribute this module may set — what `syncNodeA11yAttributes` owns on an element. */
1807
+ declare const MANAGED_A11Y_ATTRIBUTES: ReadonlyArray<string>;
1808
+ /** A node as any renderer holds it: serialized (`props`), or the builder's (`traits[]`). */
1809
+ interface A11yNodeSource extends OpenerNodeLike {
1810
+ id?: string;
1811
+ styleId?: string;
1812
+ type?: string;
1813
+ props?: Readonly<Record<string, unknown>> | null;
1814
+ traits?: ReadonlyArray<{
1815
+ name?: string;
1816
+ default?: unknown;
1817
+ }> | Readonly<Record<string, {
1818
+ name?: string;
1819
+ default?: unknown;
1820
+ }>> | null;
1821
+ }
1822
+ /** A content trait's value from `props` (serialized) or `traits[]` (builder). */
1823
+ declare function nodeTraitValue(node: A11yNodeSource | null | undefined, name: string): unknown;
1824
+ /** The min-width (CSS px) above a breakpoint — where a dialog set to "close when wider than" it closes. */
1825
+ declare function dialogCloseAboveMinWidth(breakpoint: unknown): number | null;
1826
+ /** `(min-width: 768px)` for a min-width carried in `data-iox-dialog-close-above`. */
1827
+ declare function closeAboveMediaQuery(minWidth: number | string): string;
1828
+ interface NodeA11yContext {
1829
+ /** The app path of the page being rendered (`/work`). Absent = no current-link decision. */
1830
+ currentPath?: string | null;
1831
+ /** The href the renderer gives this node (a page link's resolved route, or its URL). */
1832
+ href?: string | null;
1833
+ currentLink?: CurrentLinkOptions;
1834
+ }
1835
+ /** The attributes a node renders. */
1836
+ declare function nodeA11yAttributes(node: A11yNodeSource | null | undefined, ctx?: NodeA11yContext): Record<string, string>;
1837
+ /** The attributes as an HTML string fragment (` name="value"`…) for a string renderer. */
1838
+ declare function serializeA11yAttributes(attrs: Readonly<Record<string, string>>, escape: (v: string) => string): string;
1839
+ /**
1840
+ * Make an element's managed attributes match `attrs` — set what is there, remove what this function
1841
+ * set before and is gone now. An attribute it never set (a component's own `aria-*`) is left alone.
1842
+ */
1843
+ declare function syncNodeA11yAttributes(el: {
1844
+ setAttribute(n: string, v: string): void;
1845
+ removeAttribute(n: string): void;
1846
+ } | null | undefined, attrs: Readonly<Record<string, string>>): void;
1847
+ /**
1848
+ * True for a control that shows ONLY an icon and has no Accessible name — announced as an unnamed
1849
+ * button or link (WCAG 4.1.2, CC1 N6). The responsive audit flags it; an Icon on its own is decoration.
1850
+ */
1851
+ declare function needsAccessibleName(node: (A11yNodeSource & {
1852
+ children?: ReadonlyArray<unknown> | null;
1853
+ }) | null | undefined): boolean;
1854
+
1855
+ /**
1856
+ * Reduced motion (CC1, R1 — complex-components plan §2d). The token sheet already collapses the
1857
+ * motion tokens under `prefers-reduced-motion`; everything that moves through SCRIPT must ask the
1858
+ * same question, and ask it HERE: the interaction engines (builder canvas + live site), the Slider's
1859
+ * autoplay and the Gallery's crossfade loop. Publishable gate #6: reduced motion = motion dial 0.
1860
+ */
1861
+ declare const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
1862
+ /** Anything with `matchMedia` — a window, or a stub in a spec. */
1863
+ interface MatchMediaHost {
1864
+ matchMedia?: (query: string) => {
1865
+ matches: boolean;
1866
+ };
1867
+ }
1868
+ /** True when the visitor asked their system for reduced motion. No `matchMedia` (SSR) → false. */
1869
+ declare function prefersReducedMotion(host?: MatchMediaHost | null | undefined): boolean;
1870
+ /** True when script may animate. The ONE question every runtime asks before moving anything. */
1871
+ declare function motionAllowed(host?: MatchMediaHost | null | undefined): boolean;
1872
+
1873
+ /**
1874
+ * THE interaction keyframes (composite plan T2 · CC1 N5). Both runtimes — the builder's
1875
+ * InteractionEngineService and the live site's ClientInteractionService — animate an element action
1876
+ * from this ONE table. Before it, each kept its own copy and they drifted: `show`/`hide` animated
1877
+ * `visibility` on the canvas and not on the site, and `toggleVisibility` ran 0→1 on the canvas and
1878
+ * 1→0 on the site, so the same hamburger opened on one and closed on the other.
1879
+ *
1880
+ * `toggleVisibility` is stateful: it HIDES a visible element and SHOWS a hidden one
1881
+ * (`resolveToggleVisibility`), and its frames are then exactly `hide`'s or `show`'s. Hidden ends at
1882
+ * `visibility: hidden`, so a hidden element leaves the tab order and the accessibility tree too.
1883
+ * It is still visual-only — the element keeps its box — which is why the actions picker says so and
1884
+ * why the mobile-nav pattern uses a Dialog instead (D9).
1885
+ */
1886
+ type InteractionKeyframe = Readonly<Record<string, string | number>>;
1887
+ /** The attribute a runtime puts on an element an interaction has hidden (and removes on reveal). */
1888
+ declare const INTERACTION_HIDDEN_ATTRIBUTE = "data-iox-hidden";
1889
+ declare enum VisibilityActionType {
1890
+ Show = "show",
1891
+ Hide = "hide",
1892
+ Toggle = "toggleVisibility"
1893
+ }
1894
+ /** Actions that reveal an element (hidden → visible). Their FIRST frame is the pre-state. */
1895
+ declare const ENTRANCE_ACTION_TYPES: ReadonlySet<string>;
1896
+ /** Actions that hide an element (visible → hidden). */
1897
+ declare const EXIT_ACTION_TYPES: ReadonlySet<string>;
1898
+ /** What `toggleVisibility` does to an element: show it when hidden, hide it when visible. */
1899
+ declare function resolveToggleVisibility(isHidden: boolean): VisibilityActionType.Show | VisibilityActionType.Hide;
1900
+ /** The action a runtime actually plays: `toggleVisibility` resolved against the element's state. */
1901
+ declare function effectiveActionType(type: string, isHidden: boolean): string;
1902
+ /**
1903
+ * The keyframes of an animation action, or null for a type that is not an animation (dialogs,
1904
+ * context, navigation…). `toggleVisibility` must be resolved first (`effectiveActionType`); asked
1905
+ * directly it answers with `hide`'s frames, the toggle of an element that starts visible.
1906
+ */
1907
+ declare function buildInteractionKeyframes(type: string, params?: Readonly<Record<string, unknown>> | null): InteractionKeyframe[] | null;
1908
+ /** `translateX` → `translate-x`, `opacity` → `opacity`: the CSS property name of a keyframe key. */
1909
+ declare function keyframePropertyName(key: string): string;
1910
+ /** A keyframe as `[css-property, value]` pairs — what a runtime writes inline. */
1911
+ declare function keyframeStyleEntries(frame: InteractionKeyframe | null | undefined): Array<[string, string]>;
1912
+ /**
1913
+ * The inline pre-state an ENTRANCE action puts on its element before it plays — its first frame as
1914
+ * CSS — so the server render ships it already hidden (no visible→hidden flash). `{}` for anything else.
1915
+ */
1916
+ declare function entrancePreStateStyle(type: string, params?: Readonly<Record<string, unknown>> | null): Record<string, string>;
1917
+ /** The frame an animation comes to rest on (what reduced motion applies at once). */
1918
+ declare function finalKeyframe(frames: ReadonlyArray<InteractionKeyframe> | null | undefined): InteractionKeyframe | null;
1919
+
1683
1920
  /**
1684
1921
  * page-style — the ONE compiler that turns a page's style settings into CSS.
1685
1922
  *
@@ -2866,12 +3103,18 @@ interface GalleryLoopOptions {
2866
3103
  fadeMs: number;
2867
3104
  onChange: (state: GalleryLoopState) => void;
2868
3105
  scheduler?: GalleryLoopScheduler;
3106
+ /**
3107
+ * False under reduced motion (`motionAllowed()`, CC1 R1): the loop shows the first image and never
3108
+ * advances. Default true. The pause control for a moving gallery is CC3 (WCAG 2.2.2).
3109
+ */
3110
+ motionAllowed?: boolean;
2869
3111
  }
2870
3112
  declare class GalleryLoop {
2871
3113
  private readonly scheduler;
2872
3114
  private showMs;
2873
3115
  private fadeMs;
2874
3116
  private readonly onChange;
3117
+ private readonly motionAllowed;
2875
3118
  private count;
2876
3119
  private currentIndex;
2877
3120
  private fadingOutIndex;
@@ -2894,5 +3137,5 @@ declare class GalleryLoop {
2894
3137
  private emit;
2895
3138
  }
2896
3139
 
2897
- export { AOSService, BACKDROP_TWIN_KEY, BACKDROP_WHOLE_KEY, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, CONTAINER_HTML_TAGS, CONTEXT_ACTION_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ContainerComponent, ContextStore, 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, HOVER_DEFAULT_TRANSITION, HOVER_STATES, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxDialogComponent, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, LEGACY_HOVER_TRANSITION, LinkedContainerComponent, MAX_LINES_KEY, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, RESERVED_ALIAS_PREFIX, STATE_PSEUDO_CLASS, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_RESPONSIVE_TRAITS, 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, composeVirtualTraits, computeRelative, containerHtmlTag, contextActionEffect, contextSlotKeys, countOverrides, dataSourcePlanToRequest, declaredInputNames, decomposeBackdropFilter, defaultHoverTransition, defaultMessageResolver, deriveSeoFromContent, describePageStyle, describeSeoFields, effectiveTransitionId, excludeSelf, feelFor, getByPath, getSeoValue, hasHoverState, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isGradientPaint, isInputOverriddenAt, isOverriddenAt, isReservedAlias, isValidSlotKey, isWholeBackdropSet, legacyEnterToTransition, matchRouteParams, mediaQueryFor, mergeSwiperBreakpoints, nearestCommonAncestorId, nodeCssId, nodeUsesContext, normalizeCollectionResult, normalizeGalleryImage, normalizeIconClass, parentBreakpoint, parseUrlList, partClassName, planDataSource, planResponsiveRules, referencedDataSources, referencedDataSourcesDeep, renderDefaultDeclarations, resetPageStyleField, resetSeoField, resolveBoundValue, resolveContextValue, resolveDerivedInto, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveRelativeInto, resolveRepeaterItems, resolveSeo, resolveSiblings, resolveSingleItemId, resolveStateStyles, resolveStyleProps, resolveValueChain, rewriteViewportUnits, safeSetInput, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, stateNamesIn, statePseudoClass, stripSuppressedProps, variableAliases, variableItemsFrom, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
2898
- export type { BoundValueSpec, BreakpointDef, BuildRelatedFilterOptions, ContextActionEffect, ContextActionLike, ContextItemRef, ContextSlotDefault, ContextValueSpec, ContextWriteScope, DataSourceDomain, DataSourceEndpoints, DataSourceMode, DataSourcePlan, DataSourceQueryFilter, DataSourceRequest, DataSourceRequestSpec, DataSourceRouteFilter, DataSourceSpec, FormMessageResolver, GalleryBoundSource, GalleryDurations, GalleryEffect, GalleryEffectDescriptor, GalleryImage, GalleryLoopOptions, GalleryLoopScheduler, GalleryLoopState, GallerySlideState, IBuilderEventEmitter, InputMap, IoxContextSlot, IoxPipe, OverrideBreakpoint, PageStyleFieldState, PageStyleOptions, 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 };
3140
+ export { A11yAttribute, A11yTrait, ACCESSIBLE_NAME_TYPES, AOSService, ARIA_CURRENT_PAGE, BACKDROP_TWIN_KEY, BACKDROP_WHOLE_KEY, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, COMPONENT_STATE_SELECTOR, CONTAINER_HTML_TAGS, CONTEXT_ACTION_TYPES, CURRENT_LINK_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ComponentState, ContainerComponent, ContextStore, DECORATIVE_TYPES, DEFAULT_GALLERY_SCHEDULER, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DIALOG_CLOSE_ABOVE_ATTRIBUTE, DIALOG_NODE_TYPE, DeclarationContext, ENTRANCE_ACTION_TYPES, EXIT_ACTION_TYPES, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION, HOVER_STATES, INTERACTION_HIDDEN_ATTRIBUTE, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxDialogComponent, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, LEGACY_HOVER_TRANSITION, LinkedContainerComponent, MANAGED_A11Y_ATTRIBUTES, MAX_LINES_KEY, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, REDUCED_MOTION_QUERY, RESERVED_ALIAS_PREFIX, STATE_PSEUDO_CLASS, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_A11Y_OPTIONS, SWIPER_RESPONSIVE_TRAITS, SectionComponent, SwiperModuleName, TRANSITION_FEELS, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, VisibilityActionType, activeActions, applyBindingPipes, applyFeel, breakpointDef, breakpointForWidth, breakpointQueryFor, buildDataSourceFilter, buildInteractionKeyframes, buildPartSelector, buildRelatedFilter, buildSwiperBreakpoints, buildTargetedStateSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, closeAboveMediaQuery, collectContextSlotKeys, collectReferencedAliases, compileDeclarations, compilePageStyle, componentStatesFor, composeVirtualTraits, computeRelative, containerHtmlTag, contextActionEffect, contextSlotKeys, countOverrides, dataSourcePlanToRequest, declaredInputNames, decomposeBackdropFilter, defaultHoverTransition, defaultMessageResolver, deriveSeoFromContent, describePageStyle, describeSeoFields, dialogCloseAboveMinWidth, effectiveActionType, effectiveTransitionId, entrancePreStateStyle, excludeSelf, feelFor, finalKeyframe, getByPath, getSeoValue, hasHoverState, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isCurrentLink, isExternalOrFragment, isGradientPaint, isInputOverriddenAt, isOverriddenAt, isReservedAlias, isValidSlotKey, isWholeBackdropSet, keyframePropertyName, keyframeStyleEntries, legacyEnterToTransition, matchRouteParams, mediaQueryFor, mergeSwiperBreakpoints, motionAllowed, nearestCommonAncestorId, needsAccessibleName, nodeA11yAttributes, nodeCssId, nodeTraitValue, nodeUsesContext, normalizeAppPath, normalizeCollectionResult, normalizeGalleryImage, normalizeIconClass, parentBreakpoint, parseUrlList, partClassName, planDataSource, planResponsiveRules, prefersReducedMotion, referencedDataSources, referencedDataSourcesDeep, renderDefaultDeclarations, resetPageStyleField, resetSeoField, resolveBoundValue, resolveContextValue, resolveDerivedInto, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveRelativeInto, resolveRepeaterItems, resolveSeo, resolveSiblings, resolveSingleItemId, resolveStateStyles, resolveStyleProps, resolveToggleVisibility, resolveValueChain, rewriteViewportUnits, safeSetInput, serializeA11yAttributes, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, stateNamesIn, statePseudoClass, stateSelectorSuffix, stripSuppressedProps, swiperAutoplayAllowed, swiperModuleNames, syncNodeA11yAttributes, variableAliases, variableItemsFrom, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
3141
+ export type { A11yNodeSource, BoundValueSpec, BreakpointDef, BuildRelatedFilterOptions, ContextActionEffect, ContextActionLike, ContextItemRef, ContextSlotDefault, ContextValueSpec, ContextWriteScope, CurrentLinkOptions, DataSourceDomain, DataSourceEndpoints, DataSourceMode, DataSourcePlan, DataSourceQueryFilter, DataSourceRequest, DataSourceRequestSpec, DataSourceRouteFilter, DataSourceSpec, FormMessageResolver, GalleryBoundSource, GalleryDurations, GalleryEffect, GalleryEffectDescriptor, GalleryImage, GalleryLoopOptions, GalleryLoopScheduler, GalleryLoopState, GallerySlideState, IBuilderEventEmitter, InputMap, InteractionKeyframe, IoxContextSlot, IoxPipe, MatchMediaHost, NodeA11yContext, OverrideBreakpoint, PageStyleFieldState, PageStyleOptions, 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, SwiperModuleFlags, ToggleableAction, TransitionFeel, TransitionPhase, TransitionTimelineInput, ValueSource, VirtualTraitGroup };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectoriox/iox-ui",
3
- "version": "5.31.2",
3
+ "version": "5.32.1",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=20.0.0",
6
6
  "@angular/core": ">=20.0.0",
@@ -0,0 +1,89 @@
1
+ /**
2
+ * What an element that OPENS a dialog says about itself (CC1, N3 — APG disclosure/dialog pattern).
3
+ * The element whose click runs `openDialog` / `toggleDialog` (a menu button, a bottom bar's More) is
4
+ * announced as one: `aria-haspopup="dialog"`, `aria-controls` = the dialog's DOM id, and
5
+ * `aria-expanded` — `false` in the server render, kept in step on open and close by each runtime.
6
+ *
7
+ * ONE decision for every renderer (the builder canvas, the engine's component path and its string
8
+ * path), framework-free so iox-cms can read it too. The dialog's DOM id comes from `dialogDomId`, the
9
+ * one place that names it, so an opener and its dialog can never disagree.
10
+ */
11
+
12
+ /** The interaction trigger that opens a dialog from an element. */
13
+ export const DIALOG_OPENER_TRIGGER = 'click';
14
+
15
+ /** The actions that make an element a dialog OPENER (close-only actions do not). */
16
+ export const DIALOG_OPENER_ACTION_TYPES: ReadonlySet<string> = new Set(['openDialog', 'toggleDialog', 'openConsentPreferences']);
17
+
18
+ /** Every action that opens, closes or toggles a dialog — what a runtime watches to sync `aria-expanded`. */
19
+ export const DIALOG_ACTION_TYPE_SET: ReadonlySet<string> = new Set(['openDialog', 'closeDialog', 'toggleDialog']);
20
+
21
+ /** The selector of every dialog a renderer emits (the live `<dialog data-iox-dialog>`). */
22
+ export const DIALOG_SELECTOR = 'dialog[data-iox-dialog]';
23
+
24
+ const DIALOG_ID_PREFIX = 'iox-dialog-';
25
+
26
+ /** The DOM id of a Dialog node's `<dialog>` — what an opener's `aria-controls` names. */
27
+ export function dialogDomId(nodeId: string): string {
28
+ return `${DIALOG_ID_PREFIX}${nodeId}`;
29
+ }
30
+
31
+ /** The node id a dialog DOM id names, or null. */
32
+ export function nodeIdOfDialogDomId(domId: string | null | undefined): string | null {
33
+ return domId && domId.startsWith(DIALOG_ID_PREFIX) ? domId.slice(DIALOG_ID_PREFIX.length) : null;
34
+ }
35
+
36
+ export enum OpenerAttribute {
37
+ HasPopup = 'aria-haspopup',
38
+ Expanded = 'aria-expanded',
39
+ Controls = 'aria-controls',
40
+ }
41
+
42
+ export interface OpenerActionLike {
43
+ type?: string;
44
+ target?: string;
45
+ disabled?: boolean;
46
+ }
47
+
48
+ export interface OpenerInteractionLike {
49
+ trigger?: string;
50
+ actions?: ReadonlyArray<OpenerActionLike> | null;
51
+ }
52
+
53
+ export interface OpenerNodeLike {
54
+ interactions?: ReadonlyArray<OpenerInteractionLike> | null;
55
+ }
56
+
57
+ const SELF = 'self';
58
+
59
+ /** The Dialog node id the element's click opens (its first enabled opener action), or null. */
60
+ export function openedDialogId(node: OpenerNodeLike | null | undefined): string | null {
61
+ for (const ix of node?.interactions ?? []) {
62
+ if (ix?.trigger !== DIALOG_OPENER_TRIGGER) continue;
63
+ for (const action of ix.actions ?? []) {
64
+ if (!action || action.disabled === true || !DIALOG_OPENER_ACTION_TYPES.has(String(action.type))) continue;
65
+ if (action.target && action.target !== SELF) return action.target;
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+
71
+ /**
72
+ * The opener attributes for a node: `{}` when its click opens no dialog, else
73
+ * `aria-haspopup="dialog"`, `aria-controls="iox-dialog-<id>"` and `aria-expanded` (`false` unless
74
+ * `open` says the dialog is open right now).
75
+ */
76
+ export function dialogOpenerAttributes(node: OpenerNodeLike | null | undefined, open = false): Record<string, string> {
77
+ const dialogId = openedDialogId(node);
78
+ if (!dialogId) return {};
79
+ return {
80
+ [OpenerAttribute.HasPopup]: 'dialog',
81
+ [OpenerAttribute.Controls]: dialogDomId(dialogId),
82
+ [OpenerAttribute.Expanded]: open ? 'true' : 'false',
83
+ };
84
+ }
85
+
86
+ /** The selector of every opener of one dialog (by its DOM id) — what a runtime updates on open/close. */
87
+ export function dialogOpenersSelector(dialogDomIdValue: string): string {
88
+ return `[${OpenerAttribute.Controls}="${dialogDomIdValue}"][${OpenerAttribute.HasPopup}="dialog"]`;
89
+ }
@@ -11,3 +11,4 @@
11
11
  export * from './action.model';
12
12
  export * from './availability';
13
13
  export * from './element-actions';
14
+ export * from './dialog-opener';
@@ -100,7 +100,11 @@ export function auditDesignContrast(rows: ReadonlyArray<DesignTokenLike>, mode:
100
100
  null,
101
101
  ...styles.filter(e => CONTAINER_GROUPS.has(e.group) && ownStyle(e)['backgroundColor'] && e.id !== SECTION_STYLE),
102
102
  ];
103
- const content = styles.filter(e => !CONTAINER_GROUPS.has(e.group) && (ownStyle(e)['color'] || ownStyle(e)['backgroundColor'])
103
+ // An ITEM style — a Layout style that paints ink but no fill of its own (the bottom bar's item, CC1) —
104
+ // sits ON a container like any content, so it is judged as content (the contract's audit, 1:1).
105
+ const isItem = (e: ElementStyle) => CONTAINER_GROUPS.has(e.group) && !ownStyle(e)['backgroundColor']
106
+ && !!ownStyle(e)['color'] && ownStyle(e)['color'] !== 'inherit';
107
+ const content = styles.filter(e => (!CONTAINER_GROUPS.has(e.group) || isItem(e)) && (ownStyle(e)['color'] || ownStyle(e)['backgroundColor'])
104
108
  && e.group !== FORMS_GROUP && !NOT_CONTENT.has(e.id) && !SCRIM_STYLES.has(e.id));
105
109
  const notices = styles.filter(e => e.group === FORMS_GROUP && ownStyle(e)['color']);
106
110
  const section = ownStyle(byId.get(SECTION_STYLE) as ElementStyle);
@@ -135,7 +139,12 @@ export function auditDesignContrast(rows: ReadonlyArray<DesignTokenLike>, mode:
135
139
 
136
140
  check(box?.id ?? SECTION, 'text', boxInk, bgs, WCAG_AA_TEXT);
137
141
  for (const e of content) {
138
- const states: Array<[string, StyleMap]> = [['base', ownStyle(e)], ...(e.hover ? [['hover', { ...ownStyle(e), ...e.hover }] as [string, StyleMap]] : [])];
142
+ // `current` (CC1 N2): the page-you're-on link and bar item, on every ground the state can sit on.
143
+ const states: Array<[string, StyleMap]> = [
144
+ ['base', ownStyle(e)],
145
+ ...(e.hover ? [['hover', { ...ownStyle(e), ...e.hover }] as [string, StyleMap]] : []),
146
+ ...(e.current ? [['current', { ...ownStyle(e), ...e.current }] as [string, StyleMap]] : []),
147
+ ];
139
148
  const glyph = e.group === GLYPH_GROUP;
140
149
  for (const [state, s] of states) {
141
150
  const fill = s['backgroundColor'] ? paintColours(resolveIn(values, s['backgroundColor']), boxInk) : [];