@vectoriox/iox-ui 5.38.0 → 5.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/composite-core/index.d.ts +392 -16
- package/design-core/index.d.ts +322 -3
- package/fesm2022/vectoriox-iox-ui-actions-core.mjs +4 -2
- package/fesm2022/vectoriox-iox-ui-actions-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-composite-core.mjs +1129 -146
- package/fesm2022/vectoriox-iox-ui-composite-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-design-core.mjs +383 -3
- package/fesm2022/vectoriox-iox-ui-design-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs +125 -3
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui.mjs +16 -9
- package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
- package/generation-core/index.d.ts +43 -0
- package/index.d.ts +11 -6
- package/package.json +1 -1
- package/src/actions-core/element-actions.ts +4 -2
- package/src/composite-core/accordion-runtime.ts +3 -15
- package/src/composite-core/accordion.ts +4 -2
- package/src/composite-core/composite-kinds.ts +121 -7
- package/src/composite-core/composite-runtime.ts +68 -0
- package/src/composite-core/exclusive-runtime.ts +358 -0
- package/src/composite-core/public-api.ts +5 -2
- package/src/composite-core/structure.ts +22 -2
- package/src/composite-core/tabs.ts +628 -0
- package/src/design-core/contrast-audit.ts +5 -0
- package/src/design-core/element-styles.ts +7 -3
- package/src/design-core/flavor-spec.generated.ts +371 -0
- package/src/generation-core/compose-site.ts +68 -1
- package/src/generation-core/node-baselines.generated.ts +8 -2
- package/src/generation-core/patterns.ts +3 -0
- package/src/generation-core/site-spec.generated.ts +46 -0
- package/styles/iox-design.css +16 -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, buildCarouselRecipe, SLIDE_TYPE, buildLightboxRecipe, LIGHTBOX_LABEL, SLIDE_TO_ROW } 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, buildTabsRecipe, TABS_WORDS, tabsTriggers, tabLabelNodeOf, TABS_LABEL_BIND_PROP, tabsPanels, tabPanelTextOf, TABS_TEXT_BIND_PROP, tabPanelImageOf, pickTabsAutoBinds, tabsListOf, tabsPanelsOf, TABS_CONTENT_LIMIT, TABS_IMAGE_BIND_PROP, 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
|
|
@@ -981,6 +981,52 @@ const SPEC_SITE_PATTERNS = {
|
|
|
981
981
|
]
|
|
982
982
|
}
|
|
983
983
|
},
|
|
984
|
+
"feature.tabbed": {
|
|
985
|
+
"family": "feature",
|
|
986
|
+
"label": "Features — tabs that switch",
|
|
987
|
+
"node": {
|
|
988
|
+
"t": "Section",
|
|
989
|
+
"es": "section",
|
|
990
|
+
"c": [
|
|
991
|
+
{
|
|
992
|
+
"t": "Container",
|
|
993
|
+
"es": "box",
|
|
994
|
+
"lay": {
|
|
995
|
+
"display": "flex",
|
|
996
|
+
"flexDirection": "column",
|
|
997
|
+
"gap": "14px",
|
|
998
|
+
"maxWidth": "1100px",
|
|
999
|
+
"width": "100%",
|
|
1000
|
+
"margin": "0px auto 0px auto"
|
|
1001
|
+
},
|
|
1002
|
+
"c": [
|
|
1003
|
+
{
|
|
1004
|
+
"t": "Text",
|
|
1005
|
+
"es": "eyebrow",
|
|
1006
|
+
"slot": "eyebrow",
|
|
1007
|
+
"ph": "copy",
|
|
1008
|
+
"text": "What we offer"
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
"t": "Heading",
|
|
1012
|
+
"es": "h2",
|
|
1013
|
+
"level": "h2",
|
|
1014
|
+
"slot": "title",
|
|
1015
|
+
"ph": "copy",
|
|
1016
|
+
"text": "What we design"
|
|
1017
|
+
},
|
|
1018
|
+
{
|
|
1019
|
+
"t": "Container",
|
|
1020
|
+
"recipe": "tabs",
|
|
1021
|
+
"src": "$binding?",
|
|
1022
|
+
"ph": "copy",
|
|
1023
|
+
"name": "What we design"
|
|
1024
|
+
}
|
|
1025
|
+
]
|
|
1026
|
+
}
|
|
1027
|
+
]
|
|
1028
|
+
}
|
|
1029
|
+
},
|
|
984
1030
|
"hero.full-bleed-media": {
|
|
985
1031
|
"family": "hero",
|
|
986
1032
|
"label": "Hero — words over a background video",
|
|
@@ -2029,7 +2075,10 @@ const NODE_BASELINES = {
|
|
|
2029
2075
|
"htmlTag",
|
|
2030
2076
|
"colorScheme",
|
|
2031
2077
|
"multiple",
|
|
2032
|
-
"defaultOn"
|
|
2078
|
+
"defaultOn",
|
|
2079
|
+
"activateOn",
|
|
2080
|
+
"orientation",
|
|
2081
|
+
"ariaLabel"
|
|
2033
2082
|
],
|
|
2034
2083
|
"styleTraits": [
|
|
2035
2084
|
"position",
|
|
@@ -4262,7 +4311,10 @@ const NODE_BASELINES = {
|
|
|
4262
4311
|
"source",
|
|
4263
4312
|
"limit",
|
|
4264
4313
|
"multiple",
|
|
4265
|
-
"defaultOn"
|
|
4314
|
+
"defaultOn",
|
|
4315
|
+
"activateOn",
|
|
4316
|
+
"orientation",
|
|
4317
|
+
"ariaLabel"
|
|
4266
4318
|
],
|
|
4267
4319
|
"styleTraits": [
|
|
4268
4320
|
"position",
|
|
@@ -6559,6 +6611,9 @@ var PatternRecipe;
|
|
|
6559
6611
|
/** CC2: the Accordion — Manual (three placeholder items), or From content when the section names a list
|
|
6560
6612
|
* `binding` (`src: '$binding?'`): one template item bound to the type's question / answer fields. */
|
|
6561
6613
|
PatternRecipe["Accordion"] = "accordion";
|
|
6614
|
+
/** CC4a: Tabs — Manual (three placeholder tabs), or From content when the section names a list `binding`: the tab
|
|
6615
|
+
* list and the panels are both Repeaters over it, one row giving its tab AND its panel. */
|
|
6616
|
+
PatternRecipe["Tabs"] = "tabs";
|
|
6562
6617
|
/** CC3: the accessible carousel — a named Slider whose controls are parts, one quote per slide (placeholders). */
|
|
6563
6618
|
PatternRecipe["Carousel"] = "carousel";
|
|
6564
6619
|
/** CC3: the Lightbox — a Dialog holding a Slider over the node's `src` (the gallery's own source). */
|
|
@@ -8266,6 +8321,8 @@ const CARD_BINDS = {
|
|
|
8266
8321
|
function composeRecipe(pn, id, repeater, ctx) {
|
|
8267
8322
|
if (pn.recipe === PatternRecipe.Accordion)
|
|
8268
8323
|
return composeAccordion(pn, id, ctx);
|
|
8324
|
+
if (pn.recipe === PatternRecipe.Tabs)
|
|
8325
|
+
return composeTabs(pn, id, ctx);
|
|
8269
8326
|
if (pn.recipe === PatternRecipe.Carousel)
|
|
8270
8327
|
return composeCarousel(pn, id, ctx);
|
|
8271
8328
|
if (pn.recipe === PatternRecipe.Lightbox)
|
|
@@ -8355,6 +8412,71 @@ function composeAccordion(pn, id, ctx) {
|
|
|
8355
8412
|
addBinding(accordionAnswerOf(template), { prop: ACCORDION_BIND_PROP, source: binding, path: picks.answer });
|
|
8356
8413
|
return root;
|
|
8357
8414
|
}
|
|
8415
|
+
/** The content type a section binding lists, through its data source. */
|
|
8416
|
+
function boundContentType(binding, ctx) {
|
|
8417
|
+
const source = ctx.plan.dataSources.find(d => d.alias === binding);
|
|
8418
|
+
return source && 'contentType' in source ? ctx.plan.contentTypes.find(t => t.name === source.contentType) : undefined;
|
|
8419
|
+
}
|
|
8420
|
+
const TABS_ROOT_KEY = 'tabs';
|
|
8421
|
+
const TABS_HOLDER_KEYS = new Set(['list', 'panels']);
|
|
8422
|
+
/**
|
|
8423
|
+
* Tabs (CC4a) — the composite-core recipe stamped with this composer's factory. Manual (no section binding): three
|
|
8424
|
+
* tabs whose words are placeholders the studio rewrites (`ph: 'copy'`) and whose pictures it supplies — never invented.
|
|
8425
|
+
* From content (the section names a list `binding`, R3's AI path): the tab list and the panels holder are BOTH
|
|
8426
|
+
* Repeaters over that list, each holding its one template (one row → its tab AND its panel, paired by the row's id at
|
|
8427
|
+
* render), the tab's words, the panel's words and its picture bound to the type's first short-text, long-text and image
|
|
8428
|
+
* fields (`pickTabsAutoBinds`, the builder switch's rule), limit 6. The tab list is named by the pattern's `name`.
|
|
8429
|
+
*/
|
|
8430
|
+
function composeTabs(pn, id, ctx) {
|
|
8431
|
+
const binding = pn.src === PatternDirective.OptionalBinding || pn.src === PatternDirective.Binding ? ctx.section?.binding ?? null : null;
|
|
8432
|
+
if (pn.src === PatternDirective.Binding && !binding)
|
|
8433
|
+
throw new ComposeError(`${id}: "$binding" with no section binding`);
|
|
8434
|
+
const factory = (spec) => {
|
|
8435
|
+
const type = binding && TABS_HOLDER_KEYS.has(spec.key) ? REPEATER : spec.type;
|
|
8436
|
+
return create(spec.key === TABS_ROOT_KEY ? id : `${id}-${spec.key}`, type, { ...(spec.props ?? {}) }, spec.elementStyle ?? null);
|
|
8437
|
+
};
|
|
8438
|
+
const root = buildTabsRecipe(factory, { tabs: binding ? TABS_WORDS.slice(0, 1) : undefined, label: pn.name });
|
|
8439
|
+
const tree = root;
|
|
8440
|
+
if (!binding) {
|
|
8441
|
+
if (pn.ph === PatternPlaceholder.Copy) {
|
|
8442
|
+
for (const tab of tabsTriggers(tree)) {
|
|
8443
|
+
const words = tabLabelNodeOf(tab);
|
|
8444
|
+
if (words)
|
|
8445
|
+
ctx.placeholders.push({ nodeId: words.id, owner: ctx.owner, kind: PlaceholderKind.Copy, text: String(words.props?.[TABS_LABEL_BIND_PROP] ?? '') });
|
|
8446
|
+
}
|
|
8447
|
+
for (const panel of tabsPanels(tree)) {
|
|
8448
|
+
const text = tabPanelTextOf(panel);
|
|
8449
|
+
if (text)
|
|
8450
|
+
ctx.placeholders.push({ nodeId: text.id, owner: ctx.owner, kind: PlaceholderKind.Copy, text: String(text.props?.[TABS_TEXT_BIND_PROP] ?? '') });
|
|
8451
|
+
const image = tabPanelImageOf(panel);
|
|
8452
|
+
if (image)
|
|
8453
|
+
ctx.placeholders.push({ nodeId: image.id, owner: ctx.owner, kind: PlaceholderKind.Image, text: '' });
|
|
8454
|
+
}
|
|
8455
|
+
}
|
|
8456
|
+
return root;
|
|
8457
|
+
}
|
|
8458
|
+
const type = boundContentType(binding, ctx);
|
|
8459
|
+
const picks = pickTabsAutoBinds((type?.fields ?? []).map(f => ({ name: f.name, type: f.type, controller: f.controller })));
|
|
8460
|
+
if (!picks.label || !picks.text) {
|
|
8461
|
+
throw new ComposeError(`${id}: "${binding}" has no short-text field for the tab and long-text field for its panel`);
|
|
8462
|
+
}
|
|
8463
|
+
for (const holder of [tabsListOf(tree), tabsPanelsOf(tree)]) {
|
|
8464
|
+
addBinding(holder, { prop: 'source', source: binding, path: '' });
|
|
8465
|
+
setProp(holder, 'limit', TABS_CONTENT_LIMIT);
|
|
8466
|
+
}
|
|
8467
|
+
const [tab] = tabsTriggers(tree);
|
|
8468
|
+
const [panel] = tabsPanels(tree);
|
|
8469
|
+
delete tab.itemKey;
|
|
8470
|
+
delete panel.itemKey;
|
|
8471
|
+
addBinding(tabLabelNodeOf(tab), { prop: TABS_LABEL_BIND_PROP, source: binding, path: picks.label });
|
|
8472
|
+
addBinding(tabPanelTextOf(panel), { prop: TABS_TEXT_BIND_PROP, source: binding, path: picks.text });
|
|
8473
|
+
const image = tabPanelImageOf(panel);
|
|
8474
|
+
if (image && picks.image) {
|
|
8475
|
+
addBinding(image, { prop: TABS_IMAGE_BIND_PROP, source: binding, path: picks.image });
|
|
8476
|
+
addBinding(image, { prop: 'alt', source: binding, path: picks.label });
|
|
8477
|
+
}
|
|
8478
|
+
return root;
|
|
8479
|
+
}
|
|
8358
8480
|
/** A recipe's node factory for this composer: ids derive from the recipe's keys (`<id>-<key>`, the root `<id>`). */
|
|
8359
8481
|
function recipeFactory(id, rootKey) {
|
|
8360
8482
|
return spec => create(spec.key === rootKey ? id : `${id}-${spec.key}`, spec.type, { ...(spec.props ?? {}) }, spec.elementStyle ?? null);
|