@vectoriox/iox-ui 5.36.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 (43) hide show
  1. package/actions-core/index.d.ts +17 -3
  2. package/composite-core/index.d.ts +463 -31
  3. package/design-core/index.d.ts +130 -3
  4. package/fesm2022/vectoriox-iox-ui-actions-core.mjs +33 -3
  5. package/fesm2022/vectoriox-iox-ui-actions-core.mjs.map +1 -1
  6. package/fesm2022/vectoriox-iox-ui-composite-core.mjs +1062 -121
  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 +482 -120
  15. package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
  16. package/generation-core/index.d.ts +167 -4
  17. package/index.d.ts +209 -25
  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/action.model.ts +4 -2
  22. package/src/actions-core/element-actions.ts +37 -2
  23. package/src/composite-core/accordion-runtime.ts +117 -28
  24. package/src/composite-core/accordion.ts +89 -45
  25. package/src/composite-core/composite-kinds.ts +110 -0
  26. package/src/composite-core/composite-node.ts +15 -0
  27. package/src/composite-core/disclosure.ts +125 -0
  28. package/src/composite-core/lightbox.ts +192 -0
  29. package/src/composite-core/public-api.ts +7 -1
  30. package/src/composite-core/slider-runtime.ts +180 -0
  31. package/src/composite-core/slider.ts +378 -0
  32. package/src/composite-core/structure.ts +25 -5
  33. package/src/design-core/element-styles.ts +27 -0
  34. package/src/design-core/flavor-spec.generated.ts +146 -2
  35. package/src/generation-core/baseline-roles.ts +2 -0
  36. package/src/generation-core/compose-site.ts +80 -4
  37. package/src/generation-core/node-baselines.generated.ts +148 -4
  38. package/src/generation-core/patterns.ts +17 -0
  39. package/src/generation-core/site-spec.generated.ts +164 -0
  40. package/src/media-core/public-api.ts +9 -0
  41. package/src/media-core/video-runtime.ts +239 -0
  42. package/src/media-core/video.ts +443 -0
  43. package/styles/iox-design.css +96 -0
@@ -1,6 +1,6 @@
1
1
  import { NEW_NODE_STYLE_DEFAULTS, defaultElementStyleFor, elementStyleById, ownedTraitsOf, applyElementStyle, flavorPackById, parseHexColor, SPEC_AXES, LinkType, resolveDesignChoices } from '@vectoriox/iox-ui/design-core';
2
2
  import { formPresetById } from '@vectoriox/iox-ui/forms-core';
3
- import { CardRole, CardLayout, CARD_LAYOUTS, CARD_PARTS, buildCardRecipe, findByRole, AccordionRole, buildAccordionRecipe, ACCORDION_WORDS, accordionItems, accordionQuestionOf, accordionAnswerOf, ACCORDION_BIND_PROP, pickAccordionAutoBinds, ACCORDION_CONTENT_LIMIT } from '@vectoriox/iox-ui/composite-core';
3
+ import { CardRole, CardLayout, CARD_LAYOUTS, CARD_PARTS, buildCardRecipe, findByRole, AccordionRole, buildAccordionRecipe, ACCORDION_WORDS, accordionItems, accordionQuestionOf, accordionAnswerOf, ACCORDION_BIND_PROP, pickAccordionAutoBinds, ACCORDION_CONTENT_LIMIT, buildCarouselRecipe, SLIDE_TYPE, buildLightboxRecipe, LIGHTBOX_LABEL, SLIDE_TO_ROW } from '@vectoriox/iox-ui/composite-core';
4
4
 
5
5
  // GENERATED from iox-ai-guidance flavor-spec.v1.json — do not edit; re-run scripts/gen-flavor-spec.mjs
6
6
  // (npm run gen:flavor-spec). The JSON is itself generated by iox-ai-guidance
@@ -980,6 +980,170 @@ const SPEC_SITE_PATTERNS = {
980
980
  }
981
981
  ]
982
982
  }
983
+ },
984
+ "hero.full-bleed-media": {
985
+ "family": "hero",
986
+ "label": "Hero — words over a background video",
987
+ "node": {
988
+ "t": "Section",
989
+ "es": "section",
990
+ "scheme": "inverse",
991
+ "lay": {
992
+ "position": "relative",
993
+ "overflow": "hidden",
994
+ "display": "flex",
995
+ "flexDirection": "column",
996
+ "justifyContent": "flex-end",
997
+ "minHeight": "70vh"
998
+ },
999
+ "c": [
1000
+ {
1001
+ "t": "Video",
1002
+ "es": "media",
1003
+ "video": "background",
1004
+ "ph": "video",
1005
+ "lay": {
1006
+ "position": "absolute",
1007
+ "top": "0px",
1008
+ "left": "0px",
1009
+ "width": "100%",
1010
+ "height": "100%",
1011
+ "aspectRatio": "auto",
1012
+ "borderRadius": "0px",
1013
+ "zIndex": "0"
1014
+ }
1015
+ },
1016
+ {
1017
+ "t": "Container",
1018
+ "es": "media-scrim",
1019
+ "lay": {
1020
+ "display": "flex",
1021
+ "flexDirection": "column",
1022
+ "gap": "14px",
1023
+ "position": "relative",
1024
+ "zIndex": "1",
1025
+ "padding": "32px 32px 32px 32px",
1026
+ "maxWidth": "760px"
1027
+ },
1028
+ "c": [
1029
+ {
1030
+ "t": "Heading",
1031
+ "es": "display",
1032
+ "level": "h1",
1033
+ "slot": "title",
1034
+ "ph": "copy",
1035
+ "text": "Rooms that hold the light"
1036
+ },
1037
+ {
1038
+ "t": "Text",
1039
+ "es": "lead",
1040
+ "slot": "lead",
1041
+ "fromBusiness": "tagline",
1042
+ "ph": "copy"
1043
+ },
1044
+ {
1045
+ "t": "ButtonBlock",
1046
+ "es": "button-primary",
1047
+ "href": "/contact",
1048
+ "slot": "cta",
1049
+ "text": "Get in touch"
1050
+ }
1051
+ ]
1052
+ }
1053
+ ]
1054
+ }
1055
+ },
1056
+ "proof.testimonial-carousel": {
1057
+ "family": "proof",
1058
+ "label": "Testimonials that rotate",
1059
+ "node": {
1060
+ "t": "Section",
1061
+ "es": "section",
1062
+ "c": [
1063
+ {
1064
+ "t": "Container",
1065
+ "es": "box",
1066
+ "lay": {
1067
+ "display": "flex",
1068
+ "flexDirection": "column",
1069
+ "gap": "14px",
1070
+ "maxWidth": "860px",
1071
+ "width": "100%",
1072
+ "margin": "0px auto 0px auto"
1073
+ },
1074
+ "c": [
1075
+ {
1076
+ "t": "Text",
1077
+ "es": "eyebrow",
1078
+ "slot": "eyebrow",
1079
+ "ph": "copy",
1080
+ "text": "What clients say"
1081
+ },
1082
+ {
1083
+ "t": "SliderContainer",
1084
+ "recipe": "carousel",
1085
+ "ph": "copy"
1086
+ }
1087
+ ]
1088
+ }
1089
+ ]
1090
+ }
1091
+ },
1092
+ "collection.masonry-gallery": {
1093
+ "family": "collection",
1094
+ "label": "Gallery that opens full screen",
1095
+ "node": {
1096
+ "t": "Section",
1097
+ "es": "section",
1098
+ "c": [
1099
+ {
1100
+ "t": "Repeater",
1101
+ "src": "route.gallery",
1102
+ "lay": {
1103
+ "display": "grid",
1104
+ "gridTemplateColumns": "repeat(3, minmax(0, 1fr))",
1105
+ "gap": "var(--iox-space-gap)"
1106
+ },
1107
+ "resp": {
1108
+ "mobile": {
1109
+ "gridTemplateColumns": "repeat(2, minmax(0, 1fr))"
1110
+ }
1111
+ },
1112
+ "c": [
1113
+ {
1114
+ "t": "ButtonBlock",
1115
+ "es": "button-ghost",
1116
+ "opens": "lightbox",
1117
+ "name": "Open the image",
1118
+ "lay": {
1119
+ "display": "block",
1120
+ "width": "100%",
1121
+ "padding": "0px 0px 0px 0px"
1122
+ },
1123
+ "c": [
1124
+ {
1125
+ "t": "Image",
1126
+ "es": "media",
1127
+ "bind": "item",
1128
+ "alt": "",
1129
+ "aspect": "4 / 3"
1130
+ }
1131
+ ]
1132
+ }
1133
+ ]
1134
+ },
1135
+ {
1136
+ "t": "Dialog",
1137
+ "recipe": "lightbox",
1138
+ "src": "route.gallery"
1139
+ }
1140
+ ],
1141
+ "lay": {
1142
+ "display": "flex",
1143
+ "flexDirection": "column",
1144
+ "gap": "32px"
1145
+ }
1146
+ }
983
1147
  }
984
1148
  };
985
1149
  const SPEC_SITE_TEMPLATES = {
@@ -4230,7 +4394,8 @@ const NODE_BASELINES = {
4230
4394
  "enableParallax": false,
4231
4395
  "enableGrid": false,
4232
4396
  "gridRows": 2,
4233
- "breakpoints": ""
4397
+ "breakpoints": "",
4398
+ "ariaLabel": ""
4234
4399
  },
4235
4400
  "styleProps": {
4236
4401
  "position": "static",
@@ -4340,7 +4505,9 @@ const NODE_BASELINES = {
4340
4505
  "enableGrid",
4341
4506
  "gridRows",
4342
4507
  "__responsiveLabel",
4343
- "breakpoints"
4508
+ "breakpoints",
4509
+ "__accessibilityLabel",
4510
+ "ariaLabel"
4344
4511
  ],
4345
4512
  "styleTraits": [
4346
4513
  "position",
@@ -5858,7 +6025,8 @@ const NODE_BASELINES = {
5858
6025
  "showCloseButton": true,
5859
6026
  "consentRole": false,
5860
6027
  "closeAbove": "",
5861
- "ariaLabel": ""
6028
+ "ariaLabel": "",
6029
+ "colorScheme": "default"
5862
6030
  },
5863
6031
  "styleProps": {
5864
6032
  "position": "static",
@@ -5951,7 +6119,8 @@ const NODE_BASELINES = {
5951
6119
  "__responsiveLabel",
5952
6120
  "closeAbove",
5953
6121
  "__accessibilityLabel",
5954
- "ariaLabel"
6122
+ "ariaLabel",
6123
+ "colorScheme"
5955
6124
  ],
5956
6125
  "styleTraits": [
5957
6126
  "position",
@@ -6045,6 +6214,145 @@ const NODE_BASELINES = {
6045
6214
  "transitionTimingFunction"
6046
6215
  ],
6047
6216
  "container": true
6217
+ },
6218
+ "Video": {
6219
+ "props": {
6220
+ "videoSource": "youtube",
6221
+ "src": "",
6222
+ "poster": "",
6223
+ "mode": "inline",
6224
+ "autoplay": false,
6225
+ "title": "",
6226
+ "captions": "",
6227
+ "captionsLang": "en",
6228
+ "consentCategory": "marketing"
6229
+ },
6230
+ "styleProps": {
6231
+ "position": "static",
6232
+ "top": "auto",
6233
+ "right": "auto",
6234
+ "bottom": "auto",
6235
+ "left": "auto",
6236
+ "zIndex": "0",
6237
+ "width": "100%",
6238
+ "height": "auto",
6239
+ "minWidth": "auto",
6240
+ "minHeight": "auto",
6241
+ "maxWidth": "auto",
6242
+ "maxHeight": "auto",
6243
+ "aspectRatio": "16 / 9",
6244
+ "objectFit": "cover",
6245
+ "overflow": "visible",
6246
+ "margin": "0px 0px 0px 0px",
6247
+ "padding": "0px 0px 0px 0px",
6248
+ "borderWidth": "0px",
6249
+ "borderStyle": "solid",
6250
+ "borderColor": "#CCCCCC",
6251
+ "borderRadius": "0px",
6252
+ "opacity": "1",
6253
+ "mixBlendMode": "normal",
6254
+ "cursor": "auto",
6255
+ "boxShadow": "",
6256
+ "textShadow": "",
6257
+ "translateX": "0px",
6258
+ "translateY": "0px",
6259
+ "scaleX": "1",
6260
+ "scaleY": "1",
6261
+ "rotate": "0deg",
6262
+ "skewX": "0deg",
6263
+ "skewY": "0deg",
6264
+ "filterBlur": "0px",
6265
+ "filterHueRotate": "0deg",
6266
+ "filterBrightness": "1",
6267
+ "filterContrast": "1",
6268
+ "filterSaturate": "1",
6269
+ "filterGrayscale": "0",
6270
+ "filterSepia": "0",
6271
+ "filterInvert": "0",
6272
+ "backdropFilter": "none",
6273
+ "backdropBlur": "0px",
6274
+ "backdropBrightness": "1",
6275
+ "backdropContrast": "1",
6276
+ "backdropSaturate": "1",
6277
+ "clipPath": "none",
6278
+ "transitionDuration": "0ms",
6279
+ "transitionDelay": "0ms",
6280
+ "transitionTimingFunction": "ease"
6281
+ },
6282
+ "traits": [
6283
+ "__mediaLabel",
6284
+ "videoSource",
6285
+ "src",
6286
+ "poster",
6287
+ "__behaviourLabel",
6288
+ "mode",
6289
+ "autoplay",
6290
+ "__accessibilityLabel",
6291
+ "title",
6292
+ "captions",
6293
+ "captionsLang",
6294
+ "__privacyLabel",
6295
+ "consentCategory"
6296
+ ],
6297
+ "styleTraits": [
6298
+ "position",
6299
+ "top",
6300
+ "right",
6301
+ "bottom",
6302
+ "left",
6303
+ "zIndex",
6304
+ "width",
6305
+ "height",
6306
+ "minWidth",
6307
+ "minHeight",
6308
+ "maxWidth",
6309
+ "maxHeight",
6310
+ "aspectRatio",
6311
+ "objectFit",
6312
+ "overflow",
6313
+ "margin",
6314
+ "padding",
6315
+ "borderWidth",
6316
+ "borderStyle",
6317
+ "borderColor",
6318
+ "borderRadius",
6319
+ "opacity",
6320
+ "mixBlendMode",
6321
+ "cursor",
6322
+ "_sep_shadow",
6323
+ "boxShadow",
6324
+ "textShadow",
6325
+ "_sep_transform",
6326
+ "translateX",
6327
+ "translateY",
6328
+ "scaleX",
6329
+ "scaleY",
6330
+ "rotate",
6331
+ "skewX",
6332
+ "skewY",
6333
+ "_sep_filter",
6334
+ "filterBlur",
6335
+ "filterHueRotate",
6336
+ "filterBrightness",
6337
+ "filterContrast",
6338
+ "filterSaturate",
6339
+ "filterGrayscale",
6340
+ "filterSepia",
6341
+ "filterInvert",
6342
+ "_sep_backdrop",
6343
+ "backdropFilter",
6344
+ "backdropBlur",
6345
+ "backdropBrightness",
6346
+ "backdropContrast",
6347
+ "backdropSaturate",
6348
+ "_sep_clip",
6349
+ "clipPath",
6350
+ "_sep_transition",
6351
+ "transitionDuration",
6352
+ "transitionDelay",
6353
+ "transitionTimingFunction"
6354
+ ],
6355
+ "container": false
6048
6356
  }
6049
6357
  };
6050
6358
  /** What every style trait saves as when no constructor sets it (buildFullStyleTraits' defaults). */
@@ -6192,6 +6500,8 @@ const BASELINE_ROLES = {
6192
6500
  FormSuccess: { width: Behaviour, color: Look, padding: Look, borderRadius: Look, backgroundColor: Look },
6193
6501
  FormError: { width: Behaviour, color: Look, padding: Look, borderRadius: Look, backgroundColor: Look },
6194
6502
  Dialog: { width: Behaviour, padding: Look, borderRadius: Look, backgroundColor: Look, boxShadow: Look },
6503
+ // CC3: a Video fills its column at 16 / 9 — the shape the poster and the player take.
6504
+ Video: { width: Behaviour, aspectRatio: Behaviour },
6195
6505
  };
6196
6506
  /** The style values a type's constructor sets for an empty palette drop, each mapped to the value the
6197
6507
  * trait saves when nobody sets it — what a composed node of that type starts from. */
@@ -6249,6 +6559,10 @@ var PatternRecipe;
6249
6559
  /** CC2: the Accordion — Manual (three placeholder items), or From content when the section names a list
6250
6560
  * `binding` (`src: '$binding?'`): one template item bound to the type's question / answer fields. */
6251
6561
  PatternRecipe["Accordion"] = "accordion";
6562
+ /** CC3: the accessible carousel — a named Slider whose controls are parts, one quote per slide (placeholders). */
6563
+ PatternRecipe["Carousel"] = "carousel";
6564
+ /** CC3: the Lightbox — a Dialog holding a Slider over the node's `src` (the gallery's own source). */
6565
+ PatternRecipe["Lightbox"] = "lightbox";
6252
6566
  })(PatternRecipe || (PatternRecipe = {}));
6253
6567
  /** A recipe node's `binds` keys → the part each binds (Card v2). */
6254
6568
  var CardBindTarget;
@@ -6273,7 +6587,14 @@ var PatternPlaceholder;
6273
6587
  (function (PatternPlaceholder) {
6274
6588
  PatternPlaceholder["Copy"] = "copy";
6275
6589
  PatternPlaceholder["Image"] = "image";
6590
+ /** CC3: a clip the studio uploads (a Video's file): the poster blank until then, publish-blocking. */
6591
+ PatternPlaceholder["Video"] = "video";
6276
6592
  })(PatternPlaceholder || (PatternPlaceholder = {}));
6593
+ /** CC3: what an `opens` node opens. */
6594
+ var PatternOpens;
6595
+ (function (PatternOpens) {
6596
+ PatternOpens["Lightbox"] = "lightbox";
6597
+ })(PatternOpens || (PatternOpens = {}));
6277
6598
  /** The composer directives a pattern writes where a plan supplies the value. */
6278
6599
  var PatternDirective;
6279
6600
  (function (PatternDirective) {
@@ -7004,7 +7325,7 @@ const BAR_ITEMS_MAX = 5;
7004
7325
  class NavShapeError extends Error {
7005
7326
  }
7006
7327
  const CONTAINER = 'Container';
7007
- const DIALOG = 'Dialog';
7328
+ const DIALOG$1 = 'Dialog';
7008
7329
  const LINK = 'Link';
7009
7330
  const LINKED = 'LinkedContainer';
7010
7331
  const BUTTON = 'ButtonBlock';
@@ -7113,7 +7434,7 @@ function navPages(root) {
7113
7434
  function collectHeaderLinks(root) {
7114
7435
  const out = [];
7115
7436
  const visit = (n) => {
7116
- if (n.type === DIALOG)
7437
+ if (n.type === DIALOG$1)
7117
7438
  return;
7118
7439
  if (n.type === LINK)
7119
7440
  out.push(n);
@@ -7200,7 +7521,7 @@ function pageLink(kit, suffix, page, role, lay = {}) {
7200
7521
  return part(kit, suffix, LINK, { label: page.label, linkType: LinkType.Page, pageRoute: page.route }, 'link', role, lay);
7201
7522
  }
7202
7523
  function drawer(kit, root, pages, at) {
7203
- const dr = part(kit, 'drawer', DIALOG, {
7524
+ const dr = part(kit, 'drawer', DIALOG$1, {
7204
7525
  placement: 'end', closeOnBackdrop: true, showCloseButton: false, ariaLabel: NAV_SHAPE_COPY.menu, closeAbove: at,
7205
7526
  }, 'dialog', NavRole.Drawer, { width: '86vw', maxWidth: '360px', display: 'flex', flexDirection: 'column', gap: '20px' });
7206
7527
  const brandWords = textOf(find([root], NavRole.Brand) ?? root) || NAV_PART_LABELS[NavRole.Root];
@@ -7484,6 +7805,8 @@ var PlaceholderKind;
7484
7805
  PlaceholderKind["Copy"] = "copy";
7485
7806
  /** An image slot with no image: the empty-image placeholder (DS3). */
7486
7807
  PlaceholderKind["Image"] = "image";
7808
+ /** CC3: a video slot with no clip — the studio uploads its own (the poster blank meanwhile). */
7809
+ PlaceholderKind["Video"] = "video";
7487
7810
  })(PlaceholderKind || (PlaceholderKind = {}));
7488
7811
  var ComposeIssueCode;
7489
7812
  (function (ComposeIssueCode) {
@@ -7541,6 +7864,10 @@ const BUTTON_BLOCK = 'ButtonBlock';
7541
7864
  const FORM = 'Form';
7542
7865
  const FIELD = 'Field';
7543
7866
  const IMAGE = 'Image';
7867
+ const VIDEO = 'Video';
7868
+ /** A Video's source prop (media-core `VideoTrait.Source` — not `source`, which names a data source). */
7869
+ const VIDEO_SOURCE_PROP = 'videoSource';
7870
+ const DIALOG = 'Dialog';
7544
7871
  const SUBMIT_BUTTON = 'SubmitButton';
7545
7872
  const FORM_SUCCESS = 'FormSuccess';
7546
7873
  const FORM_ERROR = 'FormError';
@@ -7827,6 +8154,24 @@ function composeNode(pn, id, repeater, ctx) {
7827
8154
  setProp(node, prop, words.value);
7828
8155
  }
7829
8156
  }
8157
+ // CC3: a Video in background mode is an uploaded file's; its clip is the studio's own (a placeholder until then).
8158
+ if (pn.video) {
8159
+ if (type !== VIDEO)
8160
+ throw new ComposeError(`${id}: "video" belongs on a Video`);
8161
+ setProp(node, VIDEO_SOURCE_PROP, 'file');
8162
+ setProp(node, 'mode', pn.video);
8163
+ }
8164
+ if (pn.ph === PatternPlaceholder.Video) {
8165
+ setProp(node, 'src', '');
8166
+ ctx.placeholders.push({ nodeId: id, owner: ctx.owner, kind: PlaceholderKind.Video, text: '' });
8167
+ }
8168
+ if (pn.name)
8169
+ setProp(node, 'ariaLabel', pn.name);
8170
+ if (pn.opens) {
8171
+ if (pn.opens !== PatternOpens.Lightbox)
8172
+ throw new ComposeError(`${id}: "opens" names the pattern's lightbox`);
8173
+ (ctx.openers ??= []).push(id);
8174
+ }
7830
8175
  // Images: an empty slot, or a bound one — both fall back to the DS3 empty image, never a stock URL.
7831
8176
  if (pn.ph === PatternPlaceholder.Image) {
7832
8177
  setProp(node, 'src', '');
@@ -7921,6 +8266,10 @@ const CARD_BINDS = {
7921
8266
  function composeRecipe(pn, id, repeater, ctx) {
7922
8267
  if (pn.recipe === PatternRecipe.Accordion)
7923
8268
  return composeAccordion(pn, id, ctx);
8269
+ if (pn.recipe === PatternRecipe.Carousel)
8270
+ return composeCarousel(pn, id, ctx);
8271
+ if (pn.recipe === PatternRecipe.Lightbox)
8272
+ return composeLightbox(pn, id, repeater, ctx);
7924
8273
  if (pn.recipe !== PatternRecipe.Card)
7925
8274
  throw new ComposeError(`${id}: no recipe "${pn.recipe}" in composite-core`);
7926
8275
  const layout = (pn.layout ?? CardLayout.Top);
@@ -8006,12 +8355,63 @@ function composeAccordion(pn, id, ctx) {
8006
8355
  addBinding(accordionAnswerOf(template), { prop: ACCORDION_BIND_PROP, source: binding, path: picks.answer });
8007
8356
  return root;
8008
8357
  }
8358
+ /** A recipe's node factory for this composer: ids derive from the recipe's keys (`<id>-<key>`, the root `<id>`). */
8359
+ function recipeFactory(id, rootKey) {
8360
+ return spec => create(spec.key === rootKey ? id : `${id}-${spec.key}`, spec.type, { ...(spec.props ?? {}) }, spec.elementStyle ?? null);
8361
+ }
8362
+ /**
8363
+ * CC3: the testimonial carousel — composite-core's recipe (the Slider with its control parts), stamped with this
8364
+ * composer's factory. Every quote and every "client · project" line is a placeholder the studio rewrites (`ph: copy`):
8365
+ * testimonials are facts, never invented.
8366
+ */
8367
+ function composeCarousel(pn, id, ctx) {
8368
+ const slider = buildCarouselRecipe(recipeFactory(id, 'carousel'));
8369
+ if (pn.ph === PatternPlaceholder.Copy) {
8370
+ for (const slide of (slider.children ?? []).filter(c => c.type === SLIDE_TYPE)) {
8371
+ for (const text of slide.children ?? [])
8372
+ ctx.placeholders.push({ nodeId: text.id, owner: ctx.owner, kind: PlaceholderKind.Copy, text: String(text.props?.['content'] ?? '') });
8373
+ }
8374
+ }
8375
+ return slider;
8376
+ }
8377
+ /**
8378
+ * CC3: the Lightbox — a Dialog holding a Slider bound to the node's `src` (the gallery's own source): one template
8379
+ * slide whose image reads the row (a bare URL row: decorative alt), its arrows hidden on a phone.
8380
+ */
8381
+ function composeLightbox(pn, id, repeater, ctx) {
8382
+ if (!pn.src)
8383
+ throw new ComposeError(`${id}: a lightbox needs the gallery's source (src)`);
8384
+ const scoped = resolveScoped(pn.src, repeater, ctx, id);
8385
+ const sourcePath = scoped.path === ITEM_VALUE_PATH ? '' : scoped.path;
8386
+ return buildLightboxRecipe(recipeFactory(id, 'lightbox'), { source: scoped.source, sourcePath, imageField: 'item' });
8387
+ }
8388
+ /** CC3: every `opens: lightbox` node of a stamped pattern opens the pattern's lightbox at its own row. */
8389
+ function wireOpeners(root, openers) {
8390
+ if (!openers.length)
8391
+ return;
8392
+ let lightbox = null;
8393
+ const find = (n) => { if (!lightbox && n.type === DIALOG && n.label === LIGHTBOX_LABEL)
8394
+ lightbox = n; (n.children ?? []).forEach(find); };
8395
+ find(root);
8396
+ if (!lightbox)
8397
+ throw new ComposeError(`${root.id}: "opens: lightbox" with no lightbox in the pattern`);
8398
+ const target = lightbox.id;
8399
+ const visit = (n) => {
8400
+ if (openers.includes(n.id)) {
8401
+ n.interactions = [{ id: `${n.id}-opens`, trigger: 'click', actions: [{ type: 'openDialog', target, params: { slideTo: SLIDE_TO_ROW }, duration: 0, delay: 0, easing: 'ease' }] }];
8402
+ }
8403
+ (n.children ?? []).forEach(visit);
8404
+ };
8405
+ visit(root);
8406
+ }
8009
8407
  function composePattern(pattern, id, ctx) {
8010
8408
  if (pattern.node.each)
8011
8409
  throw new ComposeError(`${id}: a pattern's root cannot be stamped per page`);
8012
- const node = composeNode(pattern.node, id, null, ctx);
8410
+ const own = { ...ctx, openers: [] };
8411
+ const node = composeNode(pattern.node, id, null, own);
8013
8412
  if (!node)
8014
8413
  throw new ComposeError(`${id}: a pattern's root cannot be optional or a link`);
8414
+ wireOpeners(node, own.openers ?? []);
8015
8415
  node.label = pattern.label;
8016
8416
  return node;
8017
8417
  }
@@ -8129,5 +8529,5 @@ function composeSite(plan, context) {
8129
8529
  * Generated bundle index. Do not edit.
8130
8530
  */
8131
8531
 
8132
- 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, patternTakesBinding, planFromTemplate, planNavShape, releasedLayoutDefaults, requiredShapeKeys, routeParams, sampleImageRef, setStyleProps, shapeKeys, validateSitePlan, walkSerialized };
8532
+ 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, PatternOpens, 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, patternTakesBinding, planFromTemplate, planNavShape, releasedLayoutDefaults, requiredShapeKeys, routeParams, sampleImageRef, setStyleProps, shapeKeys, validateSitePlan, walkSerialized };
8133
8533
  //# sourceMappingURL=vectoriox-iox-ui-generation-core.mjs.map