@vectoriox/iox-ui 5.37.0 → 5.38.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.
Files changed (38) hide show
  1. package/actions-core/index.d.ts +6 -1
  2. package/composite-core/index.d.ts +294 -14
  3. package/design-core/index.d.ts +130 -3
  4. package/fesm2022/vectoriox-iox-ui-actions-core.mjs +9 -3
  5. package/fesm2022/vectoriox-iox-ui-actions-core.mjs.map +1 -1
  6. package/fesm2022/vectoriox-iox-ui-composite-core.mjs +694 -59
  7. package/fesm2022/vectoriox-iox-ui-composite-core.mjs.map +1 -1
  8. package/fesm2022/vectoriox-iox-ui-design-core.mjs +172 -3
  9. package/fesm2022/vectoriox-iox-ui-design-core.mjs.map +1 -1
  10. package/fesm2022/vectoriox-iox-ui-generation-core.mjs +410 -10
  11. package/fesm2022/vectoriox-iox-ui-generation-core.mjs.map +1 -1
  12. package/fesm2022/vectoriox-iox-ui-media-core.mjs +571 -0
  13. package/fesm2022/vectoriox-iox-ui-media-core.mjs.map +1 -0
  14. package/fesm2022/vectoriox-iox-ui.mjs +478 -118
  15. package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
  16. package/generation-core/index.d.ts +167 -4
  17. package/index.d.ts +206 -24
  18. package/media-core/index.d.ts +277 -0
  19. package/media-core/package.json +3 -0
  20. package/package.json +5 -1
  21. package/src/actions-core/element-actions.ts +9 -2
  22. package/src/composite-core/composite-node.ts +4 -0
  23. package/src/composite-core/lightbox.ts +192 -0
  24. package/src/composite-core/public-api.ts +3 -0
  25. package/src/composite-core/slider-runtime.ts +180 -0
  26. package/src/composite-core/slider.ts +378 -0
  27. package/src/composite-core/structure.ts +17 -4
  28. package/src/design-core/element-styles.ts +27 -0
  29. package/src/design-core/flavor-spec.generated.ts +146 -2
  30. package/src/generation-core/baseline-roles.ts +2 -0
  31. package/src/generation-core/compose-site.ts +80 -4
  32. package/src/generation-core/node-baselines.generated.ts +148 -4
  33. package/src/generation-core/patterns.ts +17 -0
  34. package/src/generation-core/site-spec.generated.ts +164 -0
  35. package/src/media-core/public-api.ts +9 -0
  36. package/src/media-core/video-runtime.ts +239 -0
  37. package/src/media-core/video.ts +443 -0
  38. package/styles/iox-design.css +96 -0
@@ -1,5 +1,12 @@
1
1
  import { readOwnedTrait, writeOwnedTrait, elementStyleById, switchElementStyle, LinkType, setColorScheme, COLOR_SCHEME_TRAIT } from '@vectoriox/iox-ui/design-core';
2
2
 
3
+ /** What a recipe asks its consumer to create: a NEW node of `type`, the way that consumer creates one
4
+ * (the builder's `createConfig`, generation-core's `createSerializedNode`), with these props and this
5
+ * element style. `key` is stable per recipe node — a consumer that needs deterministic ids builds them
6
+ * from it (generation-core: `<root id>-<key>`). */
7
+ /** `elementStyle: NO_ELEMENT_STYLE` asks for a node that wears NO element style (undefined / null = the type's default,
8
+ * as both node factories have always read it): a migrated slider's bare chevrons, a lightbox's slide (CC3). */
9
+ const NO_ELEMENT_STYLE = '';
3
10
  /** A recipe part: a NEW node (`create`) playing `role` under `label`, with the layout `base` styles its recipe
4
11
  * gives it — its LOOK is its element style. Shared by every recipe (Accordion, Disclosure). */
5
12
  function makeCompositePart(create, spec, role, label, base) {
@@ -271,7 +278,7 @@ const ACCORDION_WORDS = [
271
278
  { question: 'Write the third question', answer: 'Answer it plainly, in two or three short sentences.' },
272
279
  ];
273
280
  const ACCORDION_NEW_ITEM_WORDS = { question: 'Write a new question', answer: 'Answer it plainly, in two or three short sentences.' };
274
- var Type$2;
281
+ var Type$3;
275
282
  (function (Type) {
276
283
  Type["Container"] = "Container";
277
284
  Type["LinkedContainer"] = "LinkedContainer";
@@ -279,8 +286,8 @@ var Type$2;
279
286
  Type["Text"] = "Text";
280
287
  Type["Icon"] = "Icon";
281
288
  Type["Repeater"] = "Repeater";
282
- })(Type$2 || (Type$2 = {}));
283
- var Style$2;
289
+ })(Type$3 || (Type$3 = {}));
290
+ var Style$3;
284
291
  (function (Style) {
285
292
  Style["Root"] = "box";
286
293
  Style["Item"] = "accordion-item";
@@ -289,7 +296,7 @@ var Style$2;
289
296
  Style["Icon"] = "icon";
290
297
  Style["Panel"] = "accordion-panel";
291
298
  Style["Answer"] = "body";
292
- })(Style$2 || (Style$2 = {}));
299
+ })(Style$3 || (Style$3 = {}));
293
300
  const ZERO_BOX$2 = '0px 0px 0px 0px';
294
301
  const HEADING_LEVELS = ['h2', 'h3', 'h4', 'h5', 'h6'];
295
302
  const DEFAULT_LEVEL = 'h3';
@@ -316,7 +323,7 @@ function make$2(create, key, type, role, style, props = {}, label) {
316
323
  * ITS item (`stateTriggers`). ONE builder for both kinds. */
317
324
  function buildCompositeIcon(create, key, icon, itemId, role = AccordionRole.Icon) {
318
325
  const glyph = ACCORDION_ICON_GLYPH[icon];
319
- const node = makeCompositePart(create, { key, type: Type$2.Icon, props: { iconClass: glyph.iconClass }, elementStyle: Style$2.Icon }, role, 'Icon', PART_BASE$1[AccordionRole.Icon]);
326
+ const node = makeCompositePart(create, { key, type: Type$3.Icon, props: { iconClass: glyph.iconClass }, elementStyle: Style$3.Icon }, role, 'Icon', PART_BASE$1[AccordionRole.Icon]);
320
327
  node.stateStyles = { ...(node.stateStyles ?? {}), [ITEM_OPEN_STATE]: { rotate: glyph.turn } };
321
328
  if (itemId)
322
329
  node.stateTriggers = { ...(node.stateTriggers ?? {}), [ITEM_OPEN_STATE]: itemId };
@@ -324,14 +331,14 @@ function buildCompositeIcon(create, key, icon, itemId, role = AccordionRole.Icon
324
331
  }
325
332
  /** One item: header (question + icon) and panel (answer). `key` makes every part's recipe key unique. */
326
333
  function buildAccordionItem(create, words, itemKey, icon = AccordionIcon.Chevron) {
327
- const item = make$2(create, `${itemKey}`, Type$2.Container, AccordionRole.Item, Style$2.Item, {}, 'Item');
334
+ const item = make$2(create, `${itemKey}`, Type$3.Container, AccordionRole.Item, Style$3.Item, {}, 'Item');
328
335
  item.itemKey = itemKey;
329
- const trigger = make$2(create, `${itemKey}-trigger`, Type$2.LinkedContainer, AccordionRole.Trigger, Style$2.Trigger, {}, 'Header');
330
- const question = make$2(create, `${itemKey}-question`, Type$2.Heading, null, Style$2.Question, { content: words.question, level: DEFAULT_LEVEL }, 'Heading');
336
+ const trigger = make$2(create, `${itemKey}-trigger`, Type$3.LinkedContainer, AccordionRole.Trigger, Style$3.Trigger, {}, 'Header');
337
+ const question = make$2(create, `${itemKey}-question`, Type$3.Heading, null, Style$3.Question, { content: words.question, level: DEFAULT_LEVEL }, 'Heading');
331
338
  writeStyles(question, QUESTION_BASE);
332
339
  trigger.children = [question, ...(icon === AccordionIcon.None ? [] : [buildCompositeIcon(create, `${itemKey}-icon`, icon, item.id)])];
333
- const panel = make$2(create, `${itemKey}-panel`, Type$2.Container, AccordionRole.Panel, Style$2.Panel, {}, 'Panel');
334
- const answer = make$2(create, `${itemKey}-answer`, Type$2.Text, null, Style$2.Answer, { content: words.answer, tag: 'p' }, 'Text');
340
+ const panel = make$2(create, `${itemKey}-panel`, Type$3.Container, AccordionRole.Panel, Style$3.Panel, {}, 'Panel');
341
+ const answer = make$2(create, `${itemKey}-answer`, Type$3.Text, null, Style$3.Answer, { content: words.answer, tag: 'p' }, 'Text');
335
342
  writeStyles(answer, ANSWER_BASE);
336
343
  panel.children = [answer];
337
344
  item.children = [trigger, panel];
@@ -339,7 +346,7 @@ function buildAccordionItem(create, words, itemKey, icon = AccordionIcon.Chevron
339
346
  }
340
347
  /** A fresh Accordion: three items, one open at a time, the first open by default (board 4). */
341
348
  function buildAccordionRecipe(create, options = {}) {
342
- const root = make$2(create, 'accordion', Type$2.Container, AccordionRole.Accordion, Style$2.Root, {}, 'Accordion');
349
+ const root = make$2(create, 'accordion', Type$3.Container, AccordionRole.Accordion, Style$3.Root, {}, 'Accordion');
343
350
  writeProp(root, CompositeBehaviourTrait.Multiple, options.multiple ?? false);
344
351
  writeProp(root, CompositeBehaviourTrait.DefaultOn, options.defaultOn ?? AccordionDefaultOn.First);
345
352
  const words = options.items ?? ACCORDION_WORDS;
@@ -378,7 +385,7 @@ function accordionItemOf(node, path) {
378
385
  }
379
386
  /** True when the root takes its items from content (a Repeater bound to a list, R3). */
380
387
  function isAccordionFromContent(root) {
381
- return !!root && root.type === Type$2.Repeater;
388
+ return !!root && root.type === Type$3.Repeater;
382
389
  }
383
390
  /** The root's items in order — every item (Manual), or the one template item (From content). */
384
391
  function accordionItems(root) {
@@ -424,9 +431,9 @@ function accordionQuestionOf(item) {
424
431
  let text;
425
432
  if (trigger)
426
433
  walkComposite(trigger, n => {
427
- if (!hit && n.type === Type$2.Heading)
434
+ if (!hit && n.type === Type$3.Heading)
428
435
  hit = n;
429
- if (!text && n.type === Type$2.Text)
436
+ if (!text && n.type === Type$3.Text)
430
437
  text = n;
431
438
  });
432
439
  return hit ?? text;
@@ -436,7 +443,7 @@ function accordionAnswerOf(item) {
436
443
  const panel = accordionPanelOf(item);
437
444
  let hit;
438
445
  if (panel)
439
- walkComposite(panel, n => { if (!hit && n.type === Type$2.Text)
446
+ walkComposite(panel, n => { if (!hit && n.type === Type$3.Text)
440
447
  hit = n; });
441
448
  return hit;
442
449
  }
@@ -457,7 +464,7 @@ function accordionHeadingLevel(trigger) {
457
464
  const visit = (n) => {
458
465
  if (level !== undefined)
459
466
  return;
460
- if (n.type === Type$2.Heading) {
467
+ if (n.type === Type$3.Heading) {
461
468
  level = readProp(n, 'level');
462
469
  return;
463
470
  }
@@ -1168,14 +1175,14 @@ function detachAccordion(runtime) {
1168
1175
  /** Every live runtime by its root — the canvas's and the site's alike (a runtime registers itself and leaves on
1169
1176
  * destroy), so an item action finds the runtime of whatever item it targets. */
1170
1177
  const RUNTIMES = new WeakMap();
1171
- const ROW_INDEX_ATTRIBUTE = 'data-iox-row-idx';
1178
+ const ROW_INDEX_ATTRIBUTE$1 = 'data-iox-row-idx';
1172
1179
  /** The repeated rows an element sits in, outermost first — each row's index as its row wrappers stamp it
1173
1180
  * (`data-iox-row-idx`: the builder's Repeater rows, the engine's component path). The builder folds them into a
1174
1181
  * composite's scope id (`compositeScopeId`, vectoriox/iox-ui#20); the element itself is not counted. */
1175
1182
  function compositeRowKeys(el) {
1176
1183
  const keys = [];
1177
- for (let at = el?.parentElement?.closest?.(`[${ROW_INDEX_ATTRIBUTE}]`); at; at = at.parentElement?.closest(`[${ROW_INDEX_ATTRIBUTE}]`)) {
1178
- keys.unshift(at.getAttribute(ROW_INDEX_ATTRIBUTE) ?? '');
1184
+ for (let at = el?.parentElement?.closest?.(`[${ROW_INDEX_ATTRIBUTE$1}]`); at; at = at.parentElement?.closest(`[${ROW_INDEX_ATTRIBUTE$1}]`)) {
1185
+ keys.unshift(at.getAttribute(ROW_INDEX_ATTRIBUTE$1) ?? '');
1179
1186
  }
1180
1187
  return keys;
1181
1188
  }
@@ -1219,20 +1226,20 @@ const DISCLOSURE_WORDS = {
1219
1226
  };
1220
1227
  /** A disclosure's one item key — its root IS its item. */
1221
1228
  const DISCLOSURE_ITEM_KEY = 'k1';
1222
- var Type$1;
1229
+ var Type$2;
1223
1230
  (function (Type) {
1224
1231
  Type["Container"] = "Container";
1225
1232
  Type["LinkedContainer"] = "LinkedContainer";
1226
1233
  Type["Text"] = "Text";
1227
- })(Type$1 || (Type$1 = {}));
1228
- var Style$1;
1234
+ })(Type$2 || (Type$2 = {}));
1235
+ var Style$2;
1229
1236
  (function (Style) {
1230
1237
  Style["Root"] = "box";
1231
1238
  Style["Trigger"] = "accordion-trigger";
1232
1239
  Style["Word"] = "body";
1233
1240
  Style["Panel"] = "accordion-panel";
1234
1241
  Style["Body"] = "body";
1235
- })(Style$1 || (Style$1 = {}));
1242
+ })(Style$2 || (Style$2 = {}));
1236
1243
  const ZERO_BOX$1 = '0px 0px 0px 0px';
1237
1244
  const HIDDEN = 'none';
1238
1245
  const INHERIT = 'inherit';
@@ -1262,7 +1269,7 @@ function make$1(create, key, type, role, style, props, label) {
1262
1269
  }
1263
1270
  /** One of the trigger's two words: shown while the disclosure is closed (`more`) or open (`less`). */
1264
1271
  function buildWord(create, role, words, rootId) {
1265
- const node = make$1(create, role === DisclosureRole.More ? 'more' : 'less', Type$1.Text, role, Style$1.Word, { content: words, tag: 'span' }, 'Text');
1272
+ const node = make$1(create, role === DisclosureRole.More ? 'more' : 'less', Type$2.Text, role, Style$2.Word, { content: words, tag: 'span' }, 'Text');
1266
1273
  node.stateStyles = { ...(node.stateStyles ?? {}), [ITEM_OPEN_STATE]: { display: role === DisclosureRole.More ? HIDDEN : SHOWN } };
1267
1274
  if (rootId)
1268
1275
  node.stateTriggers = { ...(node.stateTriggers ?? {}), [ITEM_OPEN_STATE]: rootId };
@@ -1271,18 +1278,18 @@ function buildWord(create, role, words, rootId) {
1271
1278
  /** A fresh Disclosure: "Read more" opens the text below and reads "Read less" while it is open; closed by default. */
1272
1279
  function buildDisclosureRecipe(create, options = {}) {
1273
1280
  const words = { ...DISCLOSURE_WORDS, ...(options.words ?? {}) };
1274
- const root = make$1(create, 'disclosure', Type$1.Container, DisclosureRole.Disclosure, Style$1.Root, {}, 'Disclosure');
1281
+ const root = make$1(create, 'disclosure', Type$2.Container, DisclosureRole.Disclosure, Style$2.Root, {}, 'Disclosure');
1275
1282
  root.itemKey = DISCLOSURE_ITEM_KEY;
1276
1283
  writeProp(root, CompositeBehaviourTrait.DefaultOn, options.open ? AccordionDefaultOn.First : AccordionDefaultOn.None);
1277
1284
  const icon = options.icon ?? AccordionIcon.Chevron;
1278
- const trigger = make$1(create, 'trigger', Type$1.LinkedContainer, DisclosureRole.Trigger, Style$1.Trigger, {}, 'Toggle');
1285
+ const trigger = make$1(create, 'trigger', Type$2.LinkedContainer, DisclosureRole.Trigger, Style$2.Trigger, {}, 'Toggle');
1279
1286
  trigger.children = [
1280
1287
  buildWord(create, DisclosureRole.More, words.more, root.id),
1281
1288
  buildWord(create, DisclosureRole.Less, words.less, root.id),
1282
1289
  ...(icon === AccordionIcon.None ? [] : [buildCompositeIcon(create, 'icon', icon, root.id, DisclosureRole.Icon)]),
1283
1290
  ];
1284
- const panel = make$1(create, 'panel', Type$1.Container, DisclosureRole.Panel, Style$1.Panel, {}, 'Panel');
1285
- const body = make$1(create, 'body', Type$1.Text, null, Style$1.Body, { content: words.body, tag: 'p' }, 'Text');
1291
+ const panel = make$1(create, 'panel', Type$2.Container, DisclosureRole.Panel, Style$2.Panel, {}, 'Panel');
1292
+ const body = make$1(create, 'body', Type$2.Text, null, Style$2.Body, { content: words.body, tag: 'p' }, 'Text');
1286
1293
  writeStyles(body, BODY_BASE);
1287
1294
  panel.children = [body];
1288
1295
  root.children = [trigger, panel];
@@ -1445,7 +1452,7 @@ const CARD_WORDS = {
1445
1452
  };
1446
1453
  /** The breakpoint whose layer stacks an "Image side" card (Breakpoint.Mobile, ≤767px). */
1447
1454
  const CARD_STACK_BREAKPOINT = 'mobile';
1448
- var Type;
1455
+ var Type$1;
1449
1456
  (function (Type) {
1450
1457
  Type["Container"] = "Container";
1451
1458
  Type["Image"] = "Image";
@@ -1453,8 +1460,8 @@ var Type;
1453
1460
  Type["Text"] = "Text";
1454
1461
  Type["Icon"] = "Icon";
1455
1462
  Type["ButtonBlock"] = "ButtonBlock";
1456
- })(Type || (Type = {}));
1457
- var Style;
1463
+ })(Type$1 || (Type$1 = {}));
1464
+ var Style$1;
1458
1465
  (function (Style) {
1459
1466
  Style["Card"] = "card";
1460
1467
  Style["Box"] = "box";
@@ -1467,7 +1474,7 @@ var Style;
1467
1474
  Style["Ghost"] = "button-ghost";
1468
1475
  Style["Icon"] = "icon";
1469
1476
  Style["Scrim"] = "media-scrim";
1470
- })(Style || (Style = {}));
1477
+ })(Style$1 || (Style$1 = {}));
1471
1478
  var Scheme;
1472
1479
  (function (Scheme) {
1473
1480
  Scheme["Default"] = "default";
@@ -1477,9 +1484,9 @@ const LINK_TYPE = 'linkType';
1477
1484
  const SAVE_ICON = 'ph ph-bookmark-simple';
1478
1485
  /** A value an element style writes — read from the contract, never retyped. */
1479
1486
  const styleValue = (style, trait) => String(elementStyleById(style)?.base[trait] ?? '');
1480
- const CARD_PAD = styleValue(Style.Card, 'padding');
1481
- const IMAGE_RADIUS = styleValue(Style.Media, 'borderRadius');
1482
- const TITLE_SIZE = styleValue(Style.Title, 'fontSize');
1487
+ const CARD_PAD = styleValue(Style$1.Card, 'padding');
1488
+ const IMAGE_RADIUS = styleValue(Style$1.Media, 'borderRadius');
1489
+ const TITLE_SIZE = styleValue(Style$1.Title, 'fontSize');
1483
1490
  const LEAD_SIZE = 'var(--iox-size-lead)';
1484
1491
  const ZERO_BOX = '0px 0px 0px 0px';
1485
1492
  /** What every card part holds in every layout — the recipe's box. */
@@ -1496,7 +1503,7 @@ const CARD_BASE = {
1496
1503
  // and the row's auto top margin takes the slack.
1497
1504
  [CardRole.Actions]: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '8px', margin: 'auto 0px 0px 0px', padding: '6px 0px 0px 0px', width: '100%', minHeight: 'auto' },
1498
1505
  };
1499
- const IMAGE_BASE = { display: 'block', width: '100%', maxWidth: '100%' };
1506
+ const IMAGE_BASE$1 = { display: 'block', width: '100%', maxWidth: '100%' };
1500
1507
  /** A button's label and a pill's words: inherit the type and ink of what holds them. */
1501
1508
  const LABEL_BASE = { width: 'auto', minHeight: 'auto', padding: ZERO_BOX, margin: ZERO_BOX };
1502
1509
  const INLINE_ICON = { fontSize: '1em' };
@@ -1507,7 +1514,7 @@ const LAYOUTS = {
1507
1514
  card: { flexDirection: 'column', alignItems: 'stretch', justifyContent: 'flex-start', minHeight: 'auto', padding: CARD_PAD },
1508
1515
  media: { ...IN_FLOW, flexBasis: 'auto' },
1509
1516
  image: { aspectRatio: '4 / 3', height: 'auto', minHeight: 'auto', borderRadius: IMAGE_RADIUS },
1510
- content: GROWING_CONTENT, contentStyle: Style.Box, contentScheme: Scheme.Default,
1517
+ content: GROWING_CONTENT, contentStyle: Style$1.Box, contentScheme: Scheme.Default,
1511
1518
  title: { fontSize: TITLE_SIZE },
1512
1519
  badgeOnMedia: true,
1513
1520
  },
@@ -1515,7 +1522,7 @@ const LAYOUTS = {
1515
1522
  card: { flexDirection: 'row', alignItems: 'stretch', justifyContent: 'flex-start', minHeight: 'auto', padding: CARD_PAD },
1516
1523
  media: { ...IN_FLOW, flexBasis: '42%' },
1517
1524
  image: { aspectRatio: 'auto', height: '100%', minHeight: '180px', borderRadius: IMAGE_RADIUS },
1518
- content: GROWING_CONTENT, contentStyle: Style.Box, contentScheme: Scheme.Default,
1525
+ content: GROWING_CONTENT, contentStyle: Style$1.Box, contentScheme: Scheme.Default,
1519
1526
  title: { fontSize: TITLE_SIZE },
1520
1527
  badgeOnMedia: true,
1521
1528
  stack: {
@@ -1529,7 +1536,7 @@ const LAYOUTS = {
1529
1536
  media: { position: 'absolute', top: '0px', right: '0px', bottom: '0px', left: '0px', flexBasis: 'auto' },
1530
1537
  image: { aspectRatio: 'auto', height: '100%', minHeight: 'auto', borderRadius: '0px' },
1531
1538
  content: { position: 'relative', flexGrow: '0', flexShrink: '0', flexBasis: 'auto', padding: `72px ${CARD_PAD} ${CARD_PAD} ${CARD_PAD}` },
1532
- contentStyle: Style.Scrim, contentScheme: Scheme.Inverse,
1539
+ contentStyle: Style$1.Scrim, contentScheme: Scheme.Inverse,
1533
1540
  title: { fontSize: TITLE_SIZE },
1534
1541
  badgeOnMedia: false,
1535
1542
  },
@@ -1537,7 +1544,7 @@ const LAYOUTS = {
1537
1544
  card: { flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', minHeight: 'auto', padding: CARD_PAD },
1538
1545
  media: { ...IN_FLOW, flexBasis: '72px' },
1539
1546
  image: { aspectRatio: '1 / 1', height: 'auto', minHeight: 'auto', borderRadius: IMAGE_RADIUS },
1540
- content: GROWING_CONTENT, contentStyle: Style.Box, contentScheme: Scheme.Default,
1547
+ content: GROWING_CONTENT, contentStyle: Style$1.Box, contentScheme: Scheme.Default,
1541
1548
  title: { fontSize: LEAD_SIZE },
1542
1549
  badgeOnMedia: false,
1543
1550
  partsOff: [CardPart.Text, CardPart.Actions],
@@ -1558,16 +1565,16 @@ function make(create, key, type, role, elementStyle, props = {}, label) {
1558
1565
  return node;
1559
1566
  }
1560
1567
  function buttonLabel(create, key, words) {
1561
- const label = make(create, key, Type.Text, null, Style.Label, { content: words, tag: 'span' });
1568
+ const label = make(create, key, Type$1.Text, null, Style$1.Label, { content: words, tag: 'span' });
1562
1569
  writeStyles(label, LABEL_BASE);
1563
1570
  return label;
1564
1571
  }
1565
1572
  /** Each optional part (and the locked title) as the recipe builds it. */
1566
1573
  const PART_BUILDERS = {
1567
1574
  [CardPart.Media]: (create) => {
1568
- const media = make(create, 'media', Type.Container, CardRole.Media, Style.Box, {}, 'Media');
1569
- const image = make(create, 'image', Type.Image, null, Style.Media, { src: '', alt: '' });
1570
- writeStyles(image, IMAGE_BASE);
1575
+ const media = make(create, 'media', Type$1.Container, CardRole.Media, Style$1.Box, {}, 'Media');
1576
+ const image = make(create, 'image', Type$1.Image, null, Style$1.Media, { src: '', alt: '' });
1577
+ writeStyles(image, IMAGE_BASE$1);
1571
1578
  media.children = [image];
1572
1579
  return media;
1573
1580
  },
@@ -1577,14 +1584,14 @@ const PART_BUILDERS = {
1577
1584
  badge.label = 'Badge';
1578
1585
  return badge;
1579
1586
  },
1580
- [CardPart.Eyebrow]: (create, words) => make(create, 'eyebrow', Type.Text, CardRole.Eyebrow, Style.Eyebrow, { content: words.eyebrow, tag: 'p' }, 'Eyebrow'),
1581
- [CardPart.Text]: (create, words) => make(create, 'text', Type.Text, CardRole.Text, Style.Body, { content: words.text, tag: 'p' }, 'Text'),
1587
+ [CardPart.Eyebrow]: (create, words) => make(create, 'eyebrow', Type$1.Text, CardRole.Eyebrow, Style$1.Eyebrow, { content: words.eyebrow, tag: 'p' }, 'Eyebrow'),
1588
+ [CardPart.Text]: (create, words) => make(create, 'text', Type$1.Text, CardRole.Text, Style$1.Body, { content: words.text, tag: 'p' }, 'Text'),
1582
1589
  [CardPart.Actions]: (create, words) => {
1583
- const actions = make(create, 'actions', Type.Container, CardRole.Actions, Style.Box, {}, 'Buttons');
1584
- const primary = make(create, 'primary', Type.ButtonBlock, null, Style.Primary);
1590
+ const actions = make(create, 'actions', Type$1.Container, CardRole.Actions, Style$1.Box, {}, 'Buttons');
1591
+ const primary = make(create, 'primary', Type$1.ButtonBlock, null, Style$1.Primary);
1585
1592
  primary.children = [buttonLabel(create, 'primary-label', words.primary)];
1586
- const ghost = make(create, 'secondary', Type.ButtonBlock, null, Style.Ghost);
1587
- const icon = make(create, 'secondary-icon', Type.Icon, null, Style.Icon, { iconClass: SAVE_ICON });
1593
+ const ghost = make(create, 'secondary', Type$1.ButtonBlock, null, Style$1.Ghost);
1594
+ const icon = make(create, 'secondary-icon', Type$1.Icon, null, Style$1.Icon, { iconClass: SAVE_ICON });
1588
1595
  writeStyles(icon, INLINE_ICON);
1589
1596
  ghost.children = [icon, buttonLabel(create, 'secondary-label', words.secondary)];
1590
1597
  actions.children = [primary, ghost];
@@ -1592,15 +1599,15 @@ const PART_BUILDERS = {
1592
1599
  },
1593
1600
  };
1594
1601
  function buildTitle(create, words) {
1595
- return make(create, 'title', Type.Heading, CardRole.Title, Style.Title, { content: words.title, level: 'h3', [LINK_TYPE]: LinkType.None }, 'Title');
1602
+ return make(create, 'title', Type$1.Heading, CardRole.Title, Style$1.Title, { content: words.title, level: 'h3', [LINK_TYPE]: LinkType.None }, 'Title');
1596
1603
  }
1597
1604
  /** A fresh Card v2: every part a node with its role and element style, laid out by `options.layout`. */
1598
1605
  function buildCardRecipe(create, options = {}) {
1599
1606
  const words = { ...CARD_WORDS, ...(options.words ?? {}) };
1600
1607
  const tone = options.badgeTone ?? BadgeTone.Accent;
1601
1608
  const on = (part) => options.parts?.[part] !== false;
1602
- const card = make(create, 'card', Type.Container, CardRole.Card, Style.Card, {}, 'Card');
1603
- const content = make(create, 'content', Type.Container, CardRole.Content, Style.Box, {}, 'Content');
1609
+ const card = make(create, 'card', Type$1.Container, CardRole.Card, Style$1.Card, {}, 'Card');
1610
+ const content = make(create, 'content', Type$1.Container, CardRole.Content, Style$1.Box, {}, 'Content');
1604
1611
  content.children = [
1605
1612
  ...(on(CardPart.Eyebrow) ? [PART_BUILDERS[CardPart.Eyebrow](create, words, tone)] : []),
1606
1613
  buildTitle(create, words),
@@ -1631,7 +1638,7 @@ function cardRootOf(node, path) {
1631
1638
  function cardPartsOf(card) {
1632
1639
  return Object.fromEntries(CARD_PARTS.map(part => [part, !!findByRole(card, CARD_PART_ROLE[part])]));
1633
1640
  }
1634
- const imageOf = (media) => media?.children?.find(c => c.type === Type.Image);
1641
+ const imageOf = (media) => media?.children?.find(c => c.type === Type$1.Image);
1635
1642
  /** Put the badge where the layout says: the Media's top corner, or the head of the content column
1636
1643
  * (Image behind text, Compact, or no Media at all). The corner's insets are logical, in iox-design.css
1637
1644
  * (mirrored in RTL); `position` is the node's own, because the editing canvas forces every node's
@@ -1663,7 +1670,7 @@ function applyPartLayout(card, spec, part, baseline) {
1663
1670
  setStack(part, spec.stack?.media, STACK_KEYS.media);
1664
1671
  const image = imageOf(part);
1665
1672
  if (image) {
1666
- writeStyles(image, { ...IMAGE_BASE, ...spec.image });
1673
+ writeStyles(image, { ...IMAGE_BASE$1, ...spec.image });
1667
1674
  setStack(image, spec.stack?.image, STACK_KEYS.image);
1668
1675
  }
1669
1676
  return;
@@ -1835,6 +1842,317 @@ function setCardLinked(card, on) {
1835
1842
  return writeProp(title, LINK_TYPE, next);
1836
1843
  }
1837
1844
 
1845
+ /**
1846
+ * THE ACCESSIBLE CAROUSEL (CC3 — complex-components plan §7c, board 6; board 9 Q13). The Slider's controls are no
1847
+ * longer drawn by Swiper: Previous, Next, Pause and Pagination are REAL nodes with roles — selectable on the canvas,
1848
+ * in the layer tree and the breadcrumb, and styled on their own (R2) — handed to Swiper as its navigation and
1849
+ * pagination elements.
1850
+ *
1851
+ * SliderContainer (.swiper)
1852
+ * ├─ ButtonBlock `button-secondary` role slider.pause FIRST focus stop; present while Autoplay is on, locked
1853
+ * ├─ ButtonBlock `button-secondary` role slider.prev › Icon caret (points the reading way in RTL)
1854
+ * ├─ ButtonBlock `button-secondary` role slider.next › Icon caret
1855
+ * ├─ (the slides — `.swiper-wrapper` › SliderSlide …)
1856
+ * └─ Container role slider.pagination Swiper draws the dots (or "3 / 6") inside it
1857
+ *
1858
+ * The parts render INSIDE the `.swiper` element, never inside `.swiper-wrapper` (slider.md: a `.swiper-slide` must be
1859
+ * the wrapper's direct child): the parts that come before the first slide in the tree render before the wrapper, the
1860
+ * rest after it. By default they sit over the slides like Swiper's own controls (the shared CSS, iox-design.css, at
1861
+ * zero specificity — a part's own styles win), so a saved slider migrated from Swiper-drawn controls keeps its look.
1862
+ *
1863
+ * The Bindings switches stay the source of truth: Navigation arrows ⇔ Previous + Next, Pagination ⇔ Pagination,
1864
+ * Autoplay ⇔ Pause (WCAG 2.2.2 — "added while Autoplay is on, cannot be removed"). `syncSliderParts` is the one edit.
1865
+ */
1866
+ var SliderRole;
1867
+ (function (SliderRole) {
1868
+ SliderRole["Pause"] = "slider.pause";
1869
+ SliderRole["Prev"] = "slider.prev";
1870
+ SliderRole["Next"] = "slider.next";
1871
+ SliderRole["Pagination"] = "slider.pagination";
1872
+ })(SliderRole || (SliderRole = {}));
1873
+ /** Parts in the order they are stamped: the pause control first (APG: the first focus stop), the dots last. */
1874
+ const SLIDER_PART_ROLES = [SliderRole.Pause, SliderRole.Prev, SliderRole.Next, SliderRole.Pagination];
1875
+ const SLIDER_TYPE = 'SliderContainer';
1876
+ const SLIDE_TYPE = 'SliderSlide';
1877
+ /** The Bindings switches that own the parts. */
1878
+ var SliderFlag;
1879
+ (function (SliderFlag) {
1880
+ SliderFlag["Navigation"] = "enableNavigation";
1881
+ SliderFlag["Pagination"] = "enablePagination";
1882
+ SliderFlag["Autoplay"] = "enableAutoplay";
1883
+ })(SliderFlag || (SliderFlag = {}));
1884
+ const FLAG_PARTS = [
1885
+ [SliderFlag.Autoplay, [SliderRole.Pause]],
1886
+ [SliderFlag.Navigation, [SliderRole.Prev, SliderRole.Next]],
1887
+ [SliderFlag.Pagination, [SliderRole.Pagination]],
1888
+ ];
1889
+ /** The trait that names the carousel (Slider ▸ Accessibility ▸ Carousel name) — the A11y trait every named part uses. */
1890
+ const SLIDER_NAME_TRAIT = 'ariaLabel';
1891
+ function isSliderPart(node) {
1892
+ return !!node?.role && SLIDER_PART_ROLES.includes(node.role);
1893
+ }
1894
+ function isSlider(node) {
1895
+ return node?.type === SLIDER_TYPE;
1896
+ }
1897
+ /** The slider's own part playing `role` (a direct child), or undefined. */
1898
+ function sliderPart(slider, role) {
1899
+ return (slider.children ?? []).find(c => c?.role === role);
1900
+ }
1901
+ /** True when the slider's controls are parts (it was created or migrated in CC3) — Swiper then draws none of its own. */
1902
+ function hasSliderParts(slider) {
1903
+ return (slider.children ?? []).some(isSliderPart);
1904
+ }
1905
+ /** A slider's children split as they render: parts before the first slide, the slides (into `.swiper-wrapper`), and
1906
+ * the parts after it. */
1907
+ function splitSliderChildren(children) {
1908
+ const lead = [];
1909
+ const slides = [];
1910
+ const trail = [];
1911
+ for (const child of children ?? []) {
1912
+ if (!isSliderPart(child))
1913
+ slides.push(child);
1914
+ else
1915
+ (slides.length ? trail : lead).push(child);
1916
+ }
1917
+ return { lead, slides, trail };
1918
+ }
1919
+ /** The slides only (the template slide of a bound slider is `sliderSlides(children)[0]`). */
1920
+ function sliderSlides(children) {
1921
+ return (children ?? []).filter(c => !isSliderPart(c));
1922
+ }
1923
+ // ── the parts' recipe ───────────────────────────────────────────────────────────────────────────────
1924
+ var Type;
1925
+ (function (Type) {
1926
+ Type["ButtonBlock"] = "ButtonBlock";
1927
+ Type["Icon"] = "Icon";
1928
+ Type["Container"] = "Container";
1929
+ })(Type || (Type = {}));
1930
+ var Style;
1931
+ (function (Style) {
1932
+ Style["Control"] = "button-secondary";
1933
+ Style["Icon"] = "icon";
1934
+ })(Style || (Style = {}));
1935
+ /** Each part's glyph (Phosphor) and its default accessible name. */
1936
+ const SLIDER_PART_GLYPH = {
1937
+ [SliderRole.Pause]: 'ph ph-pause',
1938
+ [SliderRole.Prev]: 'ph ph-caret-left',
1939
+ [SliderRole.Next]: 'ph ph-caret-right',
1940
+ };
1941
+ const SLIDER_PLAY_GLYPH = 'ph-fill ph-play';
1942
+ const PART_LABEL = {
1943
+ [SliderRole.Pause]: 'Pause',
1944
+ [SliderRole.Prev]: 'Previous',
1945
+ [SliderRole.Next]: 'Next',
1946
+ [SliderRole.Pagination]: 'Pagination',
1947
+ };
1948
+ const ZERO$1 = '0px 0px 0px 0px';
1949
+ /** A round 44px control (board 6 `.st-ctl`), over the slides (z above `.swiper-wrapper`). The PLACE (which edge) is the
1950
+ * shared CSS's, in logical insets, so a carousel reads the same way in RTL. */
1951
+ const CONTROL_BASE = {
1952
+ position: 'absolute', zIndex: '10', display: 'flex', alignItems: 'center', justifyContent: 'center',
1953
+ width: '44px', height: '44px', minWidth: '44px', padding: ZERO$1, margin: ZERO$1, borderRadius: '999px',
1954
+ };
1955
+ const ICON_BASE = { fontSize: '20px', color: 'inherit' };
1956
+ /** The arrows' vertical middle over the slides: half the 44px control above the centre line. */
1957
+ const ARROW_TOP = 'calc(50% - 22px)';
1958
+ const PAGINATION_BASE = {
1959
+ position: 'absolute', zIndex: '10', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px',
1960
+ width: 'auto', minHeight: 'auto', height: 'auto', padding: ZERO$1, margin: ZERO$1, color: 'var(--iox-accent)',
1961
+ };
1962
+ const SWIPER_THEME_COLOR = '#007aff';
1963
+ function legacyLookOf(slider) {
1964
+ const color = readProp(slider, 'navigationColor');
1965
+ const size = Number(readProp(slider, 'navigationSize'));
1966
+ const dots = readProp(slider, 'paginationColor');
1967
+ return {
1968
+ navigationColor: typeof color === 'string' && color ? color : SWIPER_THEME_COLOR,
1969
+ navigationSize: Number.isFinite(size) && size > 0 ? size : 44,
1970
+ paginationColor: typeof dots === 'string' && dots ? dots : SWIPER_THEME_COLOR,
1971
+ };
1972
+ }
1973
+ /** One part, from the recipe (board 6: `button-secondary` controls, token dots) or in a migrated slider's legacy look. */
1974
+ function buildSliderPart(create, role, legacy) {
1975
+ if (role === SliderRole.Pagination) {
1976
+ const dots = makeCompositePart(create, { key: 'pagination', type: Type.Container, props: {}, elementStyle: NO_ELEMENT_STYLE }, role, PART_LABEL[role], legacy?.paginationColor ? { ...PAGINATION_BASE, color: legacy.paginationColor } : PAGINATION_BASE);
1977
+ dots.children = [];
1978
+ return dots;
1979
+ }
1980
+ const key = role.split('.')[1];
1981
+ // Previous / Next sit at the vertical middle (their own `top`, so a part set back in flow drops it); the inline edge
1982
+ // is the shared CSS's, logical, so the arrows swap sides in RTL.
1983
+ const placed = role === SliderRole.Pause ? CONTROL_BASE : { ...CONTROL_BASE, top: ARROW_TOP };
1984
+ const base = legacy
1985
+ ? { ...placed, backgroundColor: 'transparent', borderWidth: ZERO$1, color: legacy.navigationColor ?? SWIPER_THEME_COLOR, boxShadow: 'none' }
1986
+ : placed;
1987
+ // Previous / Next carry their accessible name (Swiper's A11y module reads it); the pause control's name is its
1988
+ // runtime's — it says what pressing it will do.
1989
+ const props = { tag: 'button', type: 'button' };
1990
+ if (role !== SliderRole.Pause)
1991
+ props[SLIDER_NAME_TRAIT] = role === SliderRole.Prev ? SLIDER_WORDS.en.prev : SLIDER_WORDS.en.next;
1992
+ const button = makeCompositePart(create, { key, type: Type.ButtonBlock, props, elementStyle: legacy ? NO_ELEMENT_STYLE : Style.Control }, role, PART_LABEL[role], base);
1993
+ const iconBase = legacy ? { ...ICON_BASE, fontSize: `${Math.round((legacy.navigationSize ?? 44) * 0.6)}px` } : ICON_BASE;
1994
+ const icon = makeCompositePart(create, { key: `${key}-icon`, type: Type.Icon, props: { iconClass: SLIDER_PART_GLYPH[role] }, elementStyle: legacy ? NO_ELEMENT_STYLE : Style.Icon }, null, 'Icon', iconBase);
1995
+ button.children = [icon];
1996
+ return button;
1997
+ }
1998
+ const truthy = (v) => v === true || v === 'true';
1999
+ /** The parts the slider's switches call for, in stamping order. */
2000
+ function wantedSliderParts(slider) {
2001
+ return FLAG_PARTS.filter(([flag]) => truthy(readProp(slider, flag))).flatMap(([, roles]) => roles);
2002
+ }
2003
+ /**
2004
+ * Make the slider's parts match its switches — ONE edit (a palette switch, the migration, the composer). Missing
2005
+ * parts are stamped from the recipe (or, `legacy`, in the migrated look) at their place: the pause control, Previous
2006
+ * and Next before the first slide, the pagination after the last; parts whose switch is off are removed. Everything
2007
+ * else (the slides, the author's edits to an existing part) is kept.
2008
+ */
2009
+ function syncSliderParts(slider, create, legacy) {
2010
+ const wanted = new Set(wantedSliderParts(slider));
2011
+ const children = [...(slider.children ?? [])];
2012
+ const removed = children.filter(c => isSliderPart(c) && !wanted.has(c.role));
2013
+ const kept = children.filter(c => !removed.includes(c));
2014
+ const added = [];
2015
+ for (const role of SLIDER_PART_ROLES) {
2016
+ if (!wanted.has(role) || kept.some(c => c.role === role))
2017
+ continue;
2018
+ const part = buildSliderPart(create, role, legacy);
2019
+ added.push(part);
2020
+ if (role === SliderRole.Pagination) {
2021
+ kept.push(part);
2022
+ }
2023
+ else {
2024
+ // Before the first slide, after the parts that come before it in stamping order.
2025
+ const order = SLIDER_PART_ROLES.indexOf(role);
2026
+ const firstSlide = kept.findIndex(c => !isSliderPart(c));
2027
+ let at = firstSlide < 0 ? kept.length : firstSlide;
2028
+ for (let i = 0; i < kept.length; i++) {
2029
+ const r = kept[i].role;
2030
+ if (isSliderPart(kept[i]) && SLIDER_PART_ROLES.indexOf(r) > order && i < at) {
2031
+ at = i;
2032
+ break;
2033
+ }
2034
+ }
2035
+ kept.splice(at, 0, part);
2036
+ }
2037
+ }
2038
+ if (added.length || removed.length)
2039
+ slider.children = kept;
2040
+ return { added, removed };
2041
+ }
2042
+ /** True when a saved slider still relies on Swiper-drawn controls: a switch is on and no part exists yet. */
2043
+ function needsSliderMigration(slider) {
2044
+ return !!slider && isSlider(slider) && !hasSliderParts(slider) && wantedSliderParts(slider).length > 0;
2045
+ }
2046
+ /**
2047
+ * The migration of a saved slider (board 9 Q13): its Swiper-drawn arrows, dots — and, when Autoplay is on, the pause
2048
+ * control WCAG 2.2.2 requires — become parts, in the place and the colours Swiper drew them. Idempotent: a slider
2049
+ * that already has parts is left alone. True when it changed.
2050
+ */
2051
+ function migrateSliderControls(slider, create) {
2052
+ if (!needsSliderMigration(slider))
2053
+ return false;
2054
+ const change = syncSliderParts(slider, create, legacyLookOf(slider));
2055
+ return change.added.length > 0;
2056
+ }
2057
+ /**
2058
+ * A switch flipped in the panel: on a slider still on Swiper-drawn controls, migrate it first (so its other controls
2059
+ * keep their look), then stamp / remove the parts the new switch calls for. ONE edit.
2060
+ */
2061
+ function setSliderFlag(slider, flag, on, create) {
2062
+ const wasLegacy = !hasSliderParts(slider);
2063
+ const before = wantedSliderParts(slider);
2064
+ const migrated = [];
2065
+ if (wasLegacy && before.length) {
2066
+ syncSliderParts(slider, create, legacyLookOf(slider)).added.forEach(p => migrated.push(p));
2067
+ }
2068
+ writeProp(slider, flag, on);
2069
+ const change = syncSliderParts(slider, create);
2070
+ return { added: [...migrated, ...change.added], removed: change.removed };
2071
+ }
2072
+ const SLIDER_WORDS = {
2073
+ en: {
2074
+ stop: 'Stop automatic slide show', start: 'Start automatic slide show', prev: 'Previous slide', next: 'Next slide',
2075
+ carousel: 'carousel', slide: 'slide', slideLabel: '{{index}} of {{slidesLength}}', goTo: 'Go to slide {{index}}',
2076
+ },
2077
+ he: {
2078
+ stop: 'עצירת המצגת האוטומטית', start: 'הפעלת המצגת האוטומטית', prev: 'השקופית הקודמת', next: 'השקופית הבאה',
2079
+ carousel: 'קרוסלה', slide: 'שקופית', slideLabel: '{{index}} מתוך {{slidesLength}}', goTo: 'מעבר לשקופית {{index}}',
2080
+ },
2081
+ };
2082
+ function sliderWords(lang) {
2083
+ return (lang ?? '').toLowerCase().startsWith('he') ? SLIDER_WORDS.he : SLIDER_WORDS.en;
2084
+ }
2085
+ /** The wrapper's live region (APG): silent while the slides move on their own, polite otherwise. */
2086
+ function sliderLiveRegion(rotating) {
2087
+ return rotating ? 'off' : 'polite';
2088
+ }
2089
+ /**
2090
+ * The pause control's state (APG carousel): its NAME says what it will do ("Stop automatic slide show" while it
2091
+ * rotates, "Start automatic slide show" when stopped). An author who named the control keeps the name, and the
2092
+ * state is `aria-pressed` instead (pressed = stopped).
2093
+ */
2094
+ function sliderPauseAttributes(rotating, words = SLIDER_WORDS.en, authorName) {
2095
+ const name = (authorName ?? '').trim();
2096
+ return name
2097
+ ? { 'aria-label': name, 'aria-pressed': rotating ? 'false' : 'true' }
2098
+ : { 'aria-label': rotating ? words.stop : words.start };
2099
+ }
2100
+ /** The pause control's glyph: pause while it rotates, play when stopped. */
2101
+ function sliderPauseGlyph(rotating) {
2102
+ return rotating ? SLIDER_PART_GLYPH[SliderRole.Pause] : SLIDER_PLAY_GLYPH;
2103
+ }
2104
+ /** Board 6's sample quotes — PLACEHOLDERS: testimonials are facts the studio gives, never invented. */
2105
+ const CAROUSEL_QUOTES = [
2106
+ { quote: 'A sample quote, two lines long, in the client’s own words.', who: '[Client name] · [Project, city]' },
2107
+ { quote: 'Testimonials are facts: replace these with words a client actually said.', who: '[Client name] · [Project, city]' },
2108
+ { quote: 'A third sample quote so the rotation has somewhere to go.', who: '[Client name] · [Project, city]' },
2109
+ ];
2110
+ /** A quote card. The carousel's controls sit in a ROW above the slides and the dots below them (board 6), so the
2111
+ * card needs no room kept for an overlay. */
2112
+ const QUOTE_SLIDE_BASE = {
2113
+ width: '100%', aspectRatio: 'auto', minHeight: 'auto', height: 'auto', display: 'flex', flexDirection: 'column', gap: '12px', padding: '32px 32px 32px 32px',
2114
+ };
2115
+ /** Board 6's carousel: the pause control, Previous and Next in one row above the slides (in flow — they read in the
2116
+ * page's direction), the dots under them. */
2117
+ const ROW_CONTROL_BASE = { position: 'static', display: 'inline-flex', margin: '0px 4px 12px 4px' };
2118
+ const ROW_DOTS_BASE = { position: 'static', margin: '12px 0px 0px 0px' };
2119
+ const QUOTE_TEXT_BASE = { margin: ZERO$1, padding: ZERO$1, minHeight: 'auto', width: 'auto' };
2120
+ /**
2121
+ * The testimonial carousel (board 6): a named Slider, one quote per slide (each a `card`), rotating every 5s with its
2122
+ * control parts — the pause control first, Previous · Next, the dots. ONE recipe for the composer and the builder.
2123
+ */
2124
+ function buildCarouselRecipe(create, options = {}) {
2125
+ const slider = makeCompositePart(create, { key: 'carousel', type: SLIDER_TYPE, props: {}, elementStyle: NO_ELEMENT_STYLE }, null, 'Carousel', { width: '100%' });
2126
+ for (const [name, value] of Object.entries({
2127
+ // Rewind: Previous and Next are always there to press (board 6) — neither greys out at an end.
2128
+ slidesPerView: 1, spaceBetween: 0, loop: 'rewind', enableNavigation: true, enablePagination: true, enableAutoplay: true, autoplayDelay: 5000,
2129
+ autoplayPauseOnMouseEnter: true, autoHeight: true, [SLIDER_NAME_TRAIT]: options.name ?? 'What clients say',
2130
+ }))
2131
+ writeProp(slider, name, value);
2132
+ slider.children = (options.quotes ?? CAROUSEL_QUOTES).map((q, i) => {
2133
+ const slide = makeCompositePart(create, { key: `slide-${i + 1}`, type: SLIDE_TYPE, props: {}, elementStyle: 'card' }, null, 'Slide', QUOTE_SLIDE_BASE);
2134
+ slide.children = [
2135
+ makeCompositePart(create, { key: `quote-${i + 1}`, type: 'Text', props: { content: q.quote, tag: 'p' }, elementStyle: 'lead' }, null, 'Quote', QUOTE_TEXT_BASE),
2136
+ makeCompositePart(create, { key: `who-${i + 1}`, type: 'Text', props: { content: q.who, tag: 'p' }, elementStyle: 'small' }, null, 'Client', QUOTE_TEXT_BASE),
2137
+ ];
2138
+ return slide;
2139
+ });
2140
+ syncSliderParts(slider, create);
2141
+ for (const part of slider.children ?? []) {
2142
+ if (part.role === SliderRole.Pagination)
2143
+ writeStyles(part, ROW_DOTS_BASE);
2144
+ else if (isSliderPart(part))
2145
+ writeStyles(part, ROW_CONTROL_BASE);
2146
+ }
2147
+ return slider;
2148
+ }
2149
+ /** The slide a lightbox opens at for the clicked row (clamped to the slides there are). */
2150
+ function slideIndexForRow(rowIndex, slideCount) {
2151
+ if (!Number.isFinite(rowIndex) || rowIndex < 0 || slideCount <= 0)
2152
+ return 0;
2153
+ return Math.min(Math.floor(rowIndex), slideCount - 1);
2154
+ }
2155
+
1838
2156
  const COMPOSITE_ROLE_SPECS = {
1839
2157
  [CardRole.Card]: { parent: null },
1840
2158
  [CardRole.Media]: { parent: CardRole.Card, part: CardPart.Media },
@@ -1857,6 +2175,13 @@ const COMPOSITE_ROLE_SPECS = {
1857
2175
  [DisclosureRole.More]: { parent: DisclosureRole.Trigger },
1858
2176
  [DisclosureRole.Less]: { parent: DisclosureRole.Trigger },
1859
2177
  [DisclosureRole.Icon]: { parent: DisclosureRole.Trigger },
2178
+ // CC3 — a Slider's controls are its own direct children (Swiper is handed them). The pause control stays while
2179
+ // Autoplay is on (WCAG 2.2.2): the Autoplay switch removes it, never Delete. The arrows and the dots follow
2180
+ // their switches and may be deleted.
2181
+ [SliderRole.Pause]: { parent: null, parentType: SLIDER_TYPE, locked: true },
2182
+ [SliderRole.Prev]: { parent: null, parentType: SLIDER_TYPE, pinned: true },
2183
+ [SliderRole.Next]: { parent: null, parentType: SLIDER_TYPE, pinned: true },
2184
+ [SliderRole.Pagination]: { parent: null, parentType: SLIDER_TYPE, pinned: true },
1860
2185
  };
1861
2186
  var StructureEdit;
1862
2187
  (function (StructureEdit) {
@@ -1877,12 +2202,13 @@ var StructureRefusal;
1877
2202
  */
1878
2203
  function structureRefusal(node, edit, ancestors = []) {
1879
2204
  const spec = node?.role ? COMPOSITE_ROLE_SPECS[node.role] : undefined;
1880
- if (!spec?.locked)
2205
+ if (!spec?.locked && !spec?.pinned)
1881
2206
  return null;
1882
2207
  if (edit === StructureEdit.Delete)
1883
- return StructureRefusal.LockedPart;
2208
+ return spec.locked ? StructureRefusal.LockedPart : null;
1884
2209
  const parent = ancestors[ancestors.length - 1];
1885
- return parent?.role === spec.parent ? null : StructureRefusal.LockedPlacement;
2210
+ const placed = spec.parentType ? parent?.type === spec.parentType : parent?.role === spec.parent;
2211
+ return placed ? null : StructureRefusal.LockedPlacement;
1886
2212
  }
1887
2213
  /** True when `node` is a locked part (shown with a lock and no delete in the layer tree). Deleting a whole
1888
2214
  * composite is always allowed — only its locked parts are refused. */
@@ -1890,6 +2216,315 @@ function isLockedPart(node) {
1890
2216
  return !!(node?.role && COMPOSITE_ROLE_SPECS[node.role]?.locked);
1891
2217
  }
1892
2218
 
2219
+ const SLIDE_VISIBLE = 'swiper-slide-visible';
2220
+ const RUNTIME_KEY = '__ioxCarousel';
2221
+ const ROTATION_EVENTS = 'autoplayStart autoplayStop autoplayPause autoplayResume';
2222
+ const SLIDE_EVENTS = 'slideChange transitionEnd resize slidesLengthChange';
2223
+ const AUTHOR_NAME_KEY = '__ioxAuthorName';
2224
+ /** The name the AUTHOR gave the pause control (its rendered `aria-label` before this runtime first relabelled it). */
2225
+ function authorNameOf(el) {
2226
+ if (!el)
2227
+ return null;
2228
+ const host = el;
2229
+ if (host[AUTHOR_NAME_KEY] === undefined)
2230
+ host[AUTHOR_NAME_KEY] = el.getAttribute('aria-label') ?? '';
2231
+ return host[AUTHOR_NAME_KEY] || null;
2232
+ }
2233
+ /** The part of `role` that belongs to THIS slider (never one of a slider nested in a slide). */
2234
+ function sliderPartElement(sliderEl, role) {
2235
+ const cls = `iox-role-${role.replace('.', '-')}`;
2236
+ for (const el of Array.from(sliderEl.querySelectorAll(`.${cls}`))) {
2237
+ if (el.closest('.swiper') === sliderEl && !el.closest('.swiper-wrapper'))
2238
+ return el;
2239
+ }
2240
+ return null;
2241
+ }
2242
+ class CarouselA11y {
2243
+ constructor(swiper, options = {}) {
2244
+ this.swiper = swiper;
2245
+ /** The visitor pressed pause: rotation stays stopped until they press it again. */
2246
+ this.stoppedByVisitor = false;
2247
+ this.focusInside = false;
2248
+ /** The pointer is over the carousel and Swiper holds rotation for it (`pauseOnMouseEnter`). */
2249
+ this.hovering = false;
2250
+ this.cleanups = [];
2251
+ const el = swiper.el;
2252
+ this.words = options.words ?? sliderWords(el.ownerDocument?.documentElement?.getAttribute('lang'));
2253
+ this.pause = sliderPartElement(el, SliderRole.Pause);
2254
+ this.authorName = authorNameOf(this.pause);
2255
+ this.listen(el, 'focusin', () => this.onFocusIn());
2256
+ this.listen(el, 'pointerenter', () => { this.hovering = this.hoverPauses; this.sync(); });
2257
+ this.listen(el, 'pointerleave', () => { this.hovering = false; this.sync(); });
2258
+ this.listen(el, 'focusout', (e) => this.onFocusOut(e));
2259
+ if (this.pause)
2260
+ this.listen(this.pause, 'click', (e) => { e.preventDefault(); this.togglePause(); });
2261
+ const sync = () => this.sync();
2262
+ swiper.on(ROTATION_EVENTS, sync);
2263
+ swiper.on(SLIDE_EVENTS, sync);
2264
+ this.cleanups.push(() => { swiper.off(ROTATION_EVENTS, sync); swiper.off(SLIDE_EVENTS, sync); });
2265
+ el[RUNTIME_KEY] = this;
2266
+ this.sync();
2267
+ }
2268
+ /** True while the slides move on their own: autoplay runs and nothing holds it — not focus inside, not the
2269
+ * pointer over it. (Swiper's own `paused` also flips during every transition, so it is not read here.) */
2270
+ get rotating() {
2271
+ return !!this.swiper.autoplay?.running && !this.focusInside && !this.hovering;
2272
+ }
2273
+ get hoverPauses() {
2274
+ const autoplay = this.swiper.params?.autoplay;
2275
+ return typeof autoplay === 'object' && !!autoplay?.pauseOnMouseEnter;
2276
+ }
2277
+ /** The pause control: stop rotation for good, or start it again. */
2278
+ togglePause() {
2279
+ const a = this.swiper.autoplay;
2280
+ if (!a)
2281
+ return;
2282
+ if (a.running) {
2283
+ this.stoppedByVisitor = true;
2284
+ a.stop();
2285
+ }
2286
+ else {
2287
+ this.stoppedByVisitor = false;
2288
+ a.start();
2289
+ // Focus is on the pause control itself — inside the carousel: the rotation it just started waits until
2290
+ // focus leaves (APG), but the control already reads "stop".
2291
+ if (this.focusInside)
2292
+ a.pause();
2293
+ }
2294
+ this.sync();
2295
+ }
2296
+ destroy() {
2297
+ this.cleanups.splice(0).forEach(fn => fn());
2298
+ delete this.swiper.el[RUNTIME_KEY];
2299
+ }
2300
+ listen(target, type, fn) {
2301
+ target.addEventListener(type, fn);
2302
+ this.cleanups.push(() => target.removeEventListener(type, fn));
2303
+ }
2304
+ onFocusIn() {
2305
+ this.focusInside = true;
2306
+ if (this.swiper.autoplay?.running && !this.swiper.autoplay.paused)
2307
+ this.swiper.autoplay.pause();
2308
+ this.sync();
2309
+ }
2310
+ onFocusOut(e) {
2311
+ const next = e.relatedTarget;
2312
+ if (next && this.swiper.el.contains(next))
2313
+ return;
2314
+ this.focusInside = false;
2315
+ if (!this.stoppedByVisitor && this.swiper.autoplay?.running && this.swiper.autoplay.paused)
2316
+ this.swiper.autoplay.resume();
2317
+ this.sync();
2318
+ }
2319
+ /** Re-state the carousel: the live region, the pause control, the slides out of view. */
2320
+ sync() {
2321
+ if (this.swiper.destroyed)
2322
+ return;
2323
+ const rotating = this.rotating;
2324
+ this.swiper.wrapperEl?.setAttribute('aria-live', sliderLiveRegion(rotating));
2325
+ if (this.pause) {
2326
+ const running = !!this.swiper.autoplay?.running;
2327
+ const attrs = sliderPauseAttributes(running, this.words, this.authorName);
2328
+ for (const [name, value] of Object.entries(attrs))
2329
+ this.pause.setAttribute(name, value);
2330
+ if (!('aria-pressed' in attrs))
2331
+ this.pause.removeAttribute('aria-pressed');
2332
+ const icon = this.pause.querySelector('i');
2333
+ if (icon)
2334
+ icon.className = `${sliderPauseGlyph(running)} ${Array.from(icon.classList).filter(c => !c.startsWith('ph')).join(' ')}`.trim();
2335
+ }
2336
+ this.syncHiddenSlides();
2337
+ }
2338
+ syncHiddenSlides() {
2339
+ const slides = this.swiper.slides ?? [];
2340
+ const anyVisibleMarked = slides.some(s => s.classList?.contains(SLIDE_VISIBLE));
2341
+ slides.forEach((slide, i) => {
2342
+ const visible = anyVisibleMarked ? slide.classList.contains(SLIDE_VISIBLE) : i === this.swiper.activeIndex;
2343
+ if (visible) {
2344
+ slide.removeAttribute('inert');
2345
+ slide.removeAttribute('aria-hidden');
2346
+ }
2347
+ else {
2348
+ slide.setAttribute('inert', '');
2349
+ slide.setAttribute('aria-hidden', 'true');
2350
+ }
2351
+ });
2352
+ }
2353
+ }
2354
+ /** The carousel runtime attached to a slider element, if any. */
2355
+ function carouselOf(el) {
2356
+ return el?.[RUNTIME_KEY] ?? null;
2357
+ }
2358
+ /** Attach once per Swiper (a re-init gets a fresh runtime). */
2359
+ function attachCarouselA11y(swiper, options = {}) {
2360
+ carouselOf(swiper.el)?.destroy();
2361
+ return new CarouselA11y(swiper, options);
2362
+ }
2363
+
2364
+ /**
2365
+ * THE LIGHTBOX (CC3 — complex-components plan §7d, board 6). Not a component: a Dialog holding a Slider bound to the
2366
+ * SAME source as the grid of thumbnails. The one new thing is `openDialog`'s "Open at the clicked item"
2367
+ * (`slideTo: 'row'`): a thumbnail in a repeater row opens the lightbox at ITS slide.
2368
+ *
2369
+ * Dialog `dialog` · centre · full viewport · Inverse scheme "Lightbox" (a free-standing Dialog: board A4)
2370
+ * ├─ ButtonBlock `button-ghost` "Close" › Icon × click → closeDialog
2371
+ * └─ SliderContainer one slide in view · keyboard · the source's rows
2372
+ * ├─ Previous · Next (slider.prev / slider.next, button-secondary) hidden on a phone (swipe)
2373
+ * ├─ SliderSlide › Image `media` (contain, keeps its alt) + Text `small` caption
2374
+ * └─ Pagination (slider.pagination) — the counter "3 / 6"
2375
+ *
2376
+ * Keyboard ← → are the Slider's Keyboard module; Esc and the focus trap are the `<dialog>`'s; focus returns to the
2377
+ * thumbnail that opened it (the dialog opener's own rule).
2378
+ */
2379
+ const SLIDE_TO_ROW = 'row';
2380
+ /** The attribute a renderer writes on every repeated row (the context-state provenance, both engine paths, the canvas). */
2381
+ const ROW_INDEX_ATTRIBUTE = 'data-iox-row-idx';
2382
+ const LIGHTBOX_LABEL = 'Lightbox';
2383
+ const DIALOG = 'Dialog';
2384
+ const REPEATER = 'Repeater';
2385
+ const SOURCE = 'source';
2386
+ const SOURCE_PATH = 'sourcePath';
2387
+ const ZERO = '0px 0px 0px 0px';
2388
+ const DIALOG_BASE = {
2389
+ width: '100vw', maxWidth: '100vw', height: '100dvh', maxHeight: '100dvh', borderRadius: '0px', padding: '18px 18px 18px 18px',
2390
+ display: 'flex', flexDirection: 'column', gap: '0px',
2391
+ };
2392
+ /** The top row (board 6): the counter at its start, Close at its end — Close sits above the slider, which rises into
2393
+ * the row so its counter shares it. */
2394
+ const CLOSE_BASE = {
2395
+ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '44px', height: '44px', minWidth: '44px',
2396
+ padding: ZERO, margin: ZERO, borderRadius: '999px', alignSelf: 'flex-end', flexShrink: '0', position: 'relative', zIndex: '20',
2397
+ };
2398
+ const SLIDER_BASE = { width: '100%', height: '100%', minHeight: '0px', flexGrow: '1', margin: '-44px 0px 0px 0px' };
2399
+ const SLIDE_BASE = {
2400
+ width: '100%', height: '100%', aspectRatio: 'auto', display: 'flex', flexDirection: 'column', alignItems: 'center',
2401
+ justifyContent: 'center', gap: '12px', padding: '56px 64px 16px 64px',
2402
+ };
2403
+ const IMAGE_BASE = { width: '100%', height: 'auto', maxHeight: '72dvh', minHeight: '0px', objectFit: 'contain', aspectRatio: '4 / 3', flexShrink: '1' };
2404
+ const CAPTION_BASE = { textAlign: 'center', margin: ZERO, padding: ZERO, minHeight: 'auto', width: 'auto' };
2405
+ /** The counter "3 / 6" sits at the START of the top row, Close at its end (board 6). */
2406
+ const COUNTER_BASE = { color: 'var(--iox-text-muted)', top: '12px', alignItems: 'flex-start', justifyContent: 'flex-start' };
2407
+ /** On a phone the arrows give way to a swipe (board 6, "the arrows hide on touch-only widths"). */
2408
+ const LIGHTBOX_ARROWS_HIDE_AT = 'mobile';
2409
+ /** A click on `node` runs one instant action on `target`. */
2410
+ function onClick(node, type, target, params) {
2411
+ node.interactions = [{
2412
+ id: `${node.id ?? 'node'}-click`, trigger: 'click',
2413
+ actions: [{ type, target, duration: 0, delay: 0, easing: 'ease', ...(params ? { params } : {}) }],
2414
+ }];
2415
+ }
2416
+ /** A binding of `prop` to the row field `field` of `source` (`item` = the row itself). */
2417
+ function bindRow(node, prop, source, field) {
2418
+ const bindings = (node.bindings ?? []).filter((b) => b.prop !== prop);
2419
+ bindings.push({ prop, source, path: field === 'item' ? '' : field });
2420
+ node.bindings = bindings;
2421
+ }
2422
+ function buildSlide(create, key, options, rowAlias) {
2423
+ const slide = makeCompositePart(create, { key, type: SLIDE_TYPE, props: {}, elementStyle: NO_ELEMENT_STYLE }, null, 'Slide', SLIDE_BASE);
2424
+ const image = makeCompositePart(create, { key: `${key}-image`, type: 'Image', props: { src: '', alt: '' }, elementStyle: 'media' }, null, 'Image', IMAGE_BASE);
2425
+ const caption = makeCompositePart(create, { key: `${key}-caption`, type: 'Text', props: { content: 'Caption', tag: 'p' }, elementStyle: 'small' }, null, 'Caption', CAPTION_BASE);
2426
+ if (rowAlias) {
2427
+ bindRow(image, 'src', rowAlias, options.imageField ?? 'item');
2428
+ if (options.altField)
2429
+ bindRow(image, 'alt', rowAlias, options.altField);
2430
+ if (options.captionField)
2431
+ bindRow(caption, 'content', rowAlias, options.captionField);
2432
+ }
2433
+ slide.children = options.captionField || !rowAlias ? [image, caption] : [image];
2434
+ return slide;
2435
+ }
2436
+ /** A fresh lightbox: the Dialog, its Close part and the Slider (bound to `source`, or three manual slides). */
2437
+ function buildLightboxRecipe(create, options = {}) {
2438
+ const dialog = makeCompositePart(create, { key: 'lightbox', type: DIALOG, props: {}, elementStyle: 'dialog' }, null, LIGHTBOX_LABEL, DIALOG_BASE);
2439
+ writeProp(dialog, 'placement', 'center');
2440
+ writeProp(dialog, 'closeOnBackdrop', true);
2441
+ writeProp(dialog, 'showCloseButton', false);
2442
+ writeProp(dialog, 'ariaLabel', 'Gallery');
2443
+ setColorScheme(dialog, 'inverse');
2444
+ const close = makeCompositePart(create, { key: 'close', type: 'ButtonBlock', props: { tag: 'button', type: 'button', ariaLabel: 'Close' }, elementStyle: 'button-ghost' }, null, 'Close', CLOSE_BASE);
2445
+ close.children = [makeCompositePart(create, { key: 'close-icon', type: 'Icon', props: { iconClass: 'ph ph-x' }, elementStyle: 'icon' }, null, 'Icon', { fontSize: '22px', color: 'inherit' })];
2446
+ onClick(close, 'closeDialog', dialog.id ?? '');
2447
+ const slider = makeCompositePart(create, { key: 'slider', type: SLIDER_TYPE, props: {}, elementStyle: NO_ELEMENT_STYLE }, null, 'Slider', SLIDER_BASE);
2448
+ for (const [name, value] of Object.entries({
2449
+ slidesPerView: 1, spaceBetween: 0, loop: 'off', enableNavigation: true, enablePagination: true, paginationType: 'fraction',
2450
+ enableKeyboard: true, ariaLabel: 'Gallery', limit: 50,
2451
+ }))
2452
+ writeProp(slider, name, value);
2453
+ const rowAlias = options.source ? lastSegment(options.sourcePath) || options.source : null;
2454
+ if (options.source) {
2455
+ writeProp(slider, SOURCE, options.source);
2456
+ if (options.sourcePath)
2457
+ writeProp(slider, SOURCE_PATH, options.sourcePath);
2458
+ }
2459
+ const prev = buildSliderPart(create, SliderRole.Prev);
2460
+ const next = buildSliderPart(create, SliderRole.Next);
2461
+ for (const arrow of [prev, next]) {
2462
+ const layers = { ...(arrow.responsive ?? {}) };
2463
+ layers[LIGHTBOX_ARROWS_HIDE_AT] = { styleProps: { display: 'none' } };
2464
+ arrow.responsive = layers;
2465
+ }
2466
+ const counter = buildSliderPart(create, SliderRole.Pagination);
2467
+ counter.label = 'Counter';
2468
+ writeStyles(counter, COUNTER_BASE);
2469
+ const slides = rowAlias ? [buildSlide(create, 'slide', options, rowAlias)] : [1, 2, 3].map(n => buildSlide(create, `slide-${n}`, options, null));
2470
+ slider.children = [prev, next, ...slides, counter];
2471
+ dialog.children = [close, slider];
2472
+ return dialog;
2473
+ }
2474
+ const lastSegment = (path) => (path ? String(path).split('.').filter(Boolean).pop() ?? '' : '');
2475
+ /** What a Repeater (its `source` binding) or a bound Slider (its `source` / `sourcePath` traits) repeats over. */
2476
+ function repeatSourceOf(node) {
2477
+ if (!node || (node.type !== REPEATER && node.type !== SLIDER_TYPE))
2478
+ return null;
2479
+ const binding = (node.bindings ?? []).find(b => b?.prop === SOURCE);
2480
+ if (binding?.source)
2481
+ return { source: binding.source, path: binding.path ?? '' };
2482
+ const source = readProp(node, SOURCE);
2483
+ if (typeof source !== 'string' || !source)
2484
+ return null;
2485
+ const path = readProp(node, SOURCE_PATH);
2486
+ return { source, path: typeof path === 'string' ? path : '' };
2487
+ }
2488
+ function sameRepeatSource(a, b) {
2489
+ return !!a && !!b && a.source === b.source && (a.path ?? '') === (b.path ?? '');
2490
+ }
2491
+ /** The Slider a lightbox Dialog holds (the first one, depth first), or null. */
2492
+ function lightboxSliderOf(dialog) {
2493
+ for (const child of dialog?.children ?? []) {
2494
+ if (child.type === SLIDER_TYPE)
2495
+ return child;
2496
+ const below = lightboxSliderOf(child);
2497
+ if (below)
2498
+ return below;
2499
+ }
2500
+ return null;
2501
+ }
2502
+ /** The repeated rows the element is in: the nearest Repeater or bound Slider among its ancestors (`path` = root → the
2503
+ * element, inclusive). */
2504
+ function rowSourceOf(path) {
2505
+ for (let i = path.length - 2; i >= 0; i--) {
2506
+ const source = repeatSourceOf(path[i]);
2507
+ if (source)
2508
+ return source;
2509
+ }
2510
+ return null;
2511
+ }
2512
+ /**
2513
+ * Interactions ▸ openDialog ▸ "Open at the clicked item" is offered only when it can work (board 9): the clicked
2514
+ * element sits in a repeater row AND the target Dialog holds a Slider bound to the same source.
2515
+ */
2516
+ function canOpenAtClickedItem(ownerPath, dialog) {
2517
+ if (!dialog || dialog.type !== DIALOG)
2518
+ return false;
2519
+ return sameRepeatSource(rowSourceOf(ownerPath), repeatSourceOf(lightboxSliderOf(dialog)));
2520
+ }
2521
+ /** The repeated row an element sits in (its row index), 0 when it is in none (the canvas' template row). */
2522
+ function rowIndexOf(el) {
2523
+ const row = el?.closest?.(`[${ROW_INDEX_ATTRIBUTE}]`);
2524
+ const index = Number(row?.getAttribute(ROW_INDEX_ATTRIBUTE));
2525
+ return Number.isInteger(index) && index >= 0 ? index : 0;
2526
+ }
2527
+
1893
2528
  /**
1894
2529
  * @vectoriox/iox-ui/composite-core — composites built from the builder's own parts (composite-components
1895
2530
  * plan §3a). FRAMEWORK-FREE: the builder, the SSR engine and iox-cms (generation-core's composer, the site
@@ -1904,5 +2539,5 @@ function isLockedPart(node) {
1904
2539
  * Generated bundle index. Do not edit.
1905
2540
  */
1906
2541
 
1907
- export { ACCORDION_BIND_PROP, ACCORDION_CONTENT_LIMIT, ACCORDION_DEFAULT_ONS, ACCORDION_ICONS, ACCORDION_ICON_GLYPH, ACCORDION_NEW_ITEM_WORDS, ACCORDION_WORDS, AccordionClass, AccordionDefaultOn, AccordionEditRefusal, AccordionIcon, AccordionKeyAction, AccordionRole, AccordionRuntime, BADGE_TONES, BADGE_TONE_ICONS, BadgeTone, CARD_LAYOUTS, CARD_LAYOUT_CUSTOM, CARD_PARTS, CARD_PART_ROLE, CARD_STACK_BREAKPOINT, CARD_WORDS, CHIP_ICON, CHIP_STYLE, COMPOSITE_ATTRIBUTE, COMPOSITE_KIND_ROLES, COMPOSITE_ROLE_SPECS, CardLayout, CardPart, CardRole, CompositeBehaviourTrait, CompositeItemAction, CompositeKind, DEFAULT_ON_ATTRIBUTE, DISCLOSURE_ITEM_KEY, DISCLOSURE_WORDS, DisclosureRole, ITEM_KEY_ATTRIBUTE, ITEM_OFF_EVENT, ITEM_ON_EVENT, ITEM_OPEN_STATE, MULTIPLE_ATTRIBUTE, ON_ATTRIBUTE, ON_STATE, PILL_LABEL_STYLE, SCOPE_ATTRIBUTE, StructureEdit, StructureRefusal, UNTIL_FOUND, accordionAnswerOf, accordionBehaviour, accordionDomIds, accordionFocusIndex, accordionHeadingLevel, accordionItemAttributes, accordionItemLabel, accordionItemOf, accordionItemState, accordionItems, accordionKeyAction, accordionPanelAttributes, accordionPanelOf, accordionPartStyle, accordionQuestionOf, accordionRegionAttributes, accordionRemovalRefusal, accordionRootAttributes, accordionRootOf, accordionRuntimeOf, accordionScope, accordionToContent, accordionToManual, accordionTrackAttributes, accordionTriggerAttributes, accordionTriggerOf, addAccordionItem, applyCardLayout, attachAccordionAt, attachAccordions, badgeStyleId, badgeToneOf, buildAccordionItem, buildAccordionRecipe, buildBadge, buildCardRecipe, buildChip, buildCompositeIcon, buildDisclosureRecipe, cardPartsOf, cardRootOf, cardTitleOf, clearLayerStyles, closeAccordionItem, closestAccordion, closestAccordionItem, compositeItemOf, compositeItems, compositeKindOfRole, compositeRootOf, compositeRowKeys, compositeScopeId, detachAccordion, detectAccordionIcon, detectCardLayout, disclosureOpenByDefault, domSafeKey, findByRole, hasLabelledRegion, initialAccordionOpen, isAccordionFromContent, isAccordionItemOn, isAccordionPart, isAccordionRoot, isCardLinked, isCardRoot, isCompositeItem, isCompositePanel, isCompositeRoot, isCompositeTrigger, isDisclosurePart, isDisclosureRoot, isLockedPart, isPillNode, itemOpenTriggerIds, makeCompositePart, moveAccordionItem, nextAccordionState, nextItemKey, onAccordionItemState, openAccordionItem, parentInComposite, pickAccordionAutoBinds, readLayerStyle, readProp, readStyle, removeFromComposite, renameAccordionItem, rowItemKey, runCompositeItemAction, serializeDomAttributes, setAccordionBehaviour, setAccordionIcon, setBadgeTone, setCardLinked, setCardPart, shouldScrollToItem, structureRefusal, walkComposite, writeLayerStyles, writeProp, writeStyles };
2542
+ export { ACCORDION_BIND_PROP, ACCORDION_CONTENT_LIMIT, ACCORDION_DEFAULT_ONS, ACCORDION_ICONS, ACCORDION_ICON_GLYPH, ACCORDION_NEW_ITEM_WORDS, ACCORDION_WORDS, AccordionClass, AccordionDefaultOn, AccordionEditRefusal, AccordionIcon, AccordionKeyAction, AccordionRole, AccordionRuntime, BADGE_TONES, BADGE_TONE_ICONS, BadgeTone, CARD_LAYOUTS, CARD_LAYOUT_CUSTOM, CARD_PARTS, CARD_PART_ROLE, CARD_STACK_BREAKPOINT, CARD_WORDS, CAROUSEL_QUOTES, CHIP_ICON, CHIP_STYLE, COMPOSITE_ATTRIBUTE, COMPOSITE_KIND_ROLES, COMPOSITE_ROLE_SPECS, CardLayout, CardPart, CardRole, CarouselA11y, CompositeBehaviourTrait, CompositeItemAction, CompositeKind, DEFAULT_ON_ATTRIBUTE, DISCLOSURE_ITEM_KEY, DISCLOSURE_WORDS, DisclosureRole, ITEM_KEY_ATTRIBUTE, ITEM_OFF_EVENT, ITEM_ON_EVENT, ITEM_OPEN_STATE, LIGHTBOX_ARROWS_HIDE_AT, LIGHTBOX_LABEL, MULTIPLE_ATTRIBUTE, NO_ELEMENT_STYLE, ON_ATTRIBUTE, ON_STATE, PILL_LABEL_STYLE, ROW_INDEX_ATTRIBUTE, SCOPE_ATTRIBUTE, SLIDER_NAME_TRAIT, SLIDER_PART_GLYPH, SLIDER_PART_ROLES, SLIDER_PLAY_GLYPH, SLIDER_TYPE, SLIDER_WORDS, SLIDE_TO_ROW, SLIDE_TYPE, SWIPER_THEME_COLOR, SliderFlag, SliderRole, StructureEdit, StructureRefusal, UNTIL_FOUND, accordionAnswerOf, accordionBehaviour, accordionDomIds, accordionFocusIndex, accordionHeadingLevel, accordionItemAttributes, accordionItemLabel, accordionItemOf, accordionItemState, accordionItems, accordionKeyAction, accordionPanelAttributes, accordionPanelOf, accordionPartStyle, accordionQuestionOf, accordionRegionAttributes, accordionRemovalRefusal, accordionRootAttributes, accordionRootOf, accordionRuntimeOf, accordionScope, accordionToContent, accordionToManual, accordionTrackAttributes, accordionTriggerAttributes, accordionTriggerOf, addAccordionItem, applyCardLayout, attachAccordionAt, attachAccordions, attachCarouselA11y, badgeStyleId, badgeToneOf, buildAccordionItem, buildAccordionRecipe, buildBadge, buildCardRecipe, buildCarouselRecipe, buildChip, buildCompositeIcon, buildDisclosureRecipe, buildLightboxRecipe, buildSliderPart, canOpenAtClickedItem, cardPartsOf, cardRootOf, cardTitleOf, carouselOf, clearLayerStyles, closeAccordionItem, closestAccordion, closestAccordionItem, compositeItemOf, compositeItems, compositeKindOfRole, compositeRootOf, compositeRowKeys, compositeScopeId, detachAccordion, detectAccordionIcon, detectCardLayout, disclosureOpenByDefault, domSafeKey, findByRole, hasLabelledRegion, hasSliderParts, initialAccordionOpen, isAccordionFromContent, isAccordionItemOn, isAccordionPart, isAccordionRoot, isCardLinked, isCardRoot, isCompositeItem, isCompositePanel, isCompositeRoot, isCompositeTrigger, isDisclosurePart, isDisclosureRoot, isLockedPart, isPillNode, isSlider, isSliderPart, itemOpenTriggerIds, lightboxSliderOf, makeCompositePart, migrateSliderControls, moveAccordionItem, needsSliderMigration, nextAccordionState, nextItemKey, onAccordionItemState, openAccordionItem, parentInComposite, pickAccordionAutoBinds, readLayerStyle, readProp, readStyle, removeFromComposite, renameAccordionItem, repeatSourceOf, rowIndexOf, rowItemKey, rowSourceOf, runCompositeItemAction, sameRepeatSource, serializeDomAttributes, setAccordionBehaviour, setAccordionIcon, setBadgeTone, setCardLinked, setCardPart, setSliderFlag, shouldScrollToItem, slideIndexForRow, sliderLiveRegion, sliderPart, sliderPartElement, sliderPauseAttributes, sliderPauseGlyph, sliderSlides, sliderWords, splitSliderChildren, structureRefusal, syncSliderParts, walkComposite, wantedSliderParts, writeLayerStyles, writeProp, writeStyles };
1908
2543
  //# sourceMappingURL=vectoriox-iox-ui-composite-core.mjs.map