@vectoriox/iox-ui 5.38.0 → 5.40.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 +219 -8
- 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 +107 -3
- 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 +147 -1
- package/src/generation-core/nav-shape.ts +53 -3
- 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). */
|
|
@@ -6856,14 +6911,14 @@ const GLOBAL_POSITION = {
|
|
|
6856
6911
|
[GlobalSlot.Footer]: PatternGlobalPosition.After,
|
|
6857
6912
|
};
|
|
6858
6913
|
const ROUTE = /^\/(?:(?:[a-z0-9]+(?:-[a-z0-9]+)*|:[a-zA-Z_][a-zA-Z0-9_]*)(?:\/(?:[a-z0-9]+(?:-[a-z0-9]+)*|:[a-zA-Z_][a-zA-Z0-9_]*))*)?$/;
|
|
6859
|
-
const ROUTE_PARAM = /:([a-zA-Z_][a-zA-Z0-9_]*)/g;
|
|
6914
|
+
const ROUTE_PARAM$1 = /:([a-zA-Z_][a-zA-Z0-9_]*)/g;
|
|
6860
6915
|
const isObject = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
6861
6916
|
const isNonEmptyString = (v) => typeof v === 'string' && v.trim() !== '';
|
|
6862
6917
|
const has = (table, key) => Object.prototype.hasOwnProperty.call(table, key);
|
|
6863
6918
|
const at = (path, key) => (path ? `${path}.${key}` : key);
|
|
6864
6919
|
/** The `:param` names of a route, in order. */
|
|
6865
6920
|
function routeParams(route) {
|
|
6866
|
-
return [...route.matchAll(ROUTE_PARAM)].map(m => m[1]);
|
|
6921
|
+
return [...route.matchAll(ROUTE_PARAM$1)].map(m => m[1]);
|
|
6867
6922
|
}
|
|
6868
6923
|
class Collector {
|
|
6869
6924
|
constructor() {
|
|
@@ -7430,6 +7485,21 @@ function navPages(root) {
|
|
|
7430
7485
|
const source = links.length ? links : collectHeaderLinks(root);
|
|
7431
7486
|
return source.filter(isPageLink).map(n => ({ label: textOf(n), route: String(prop(n, 'pageRoute')) }));
|
|
7432
7487
|
}
|
|
7488
|
+
/** Where a phone menu's pages come from: the header's own page Links, else the studio's pages. */
|
|
7489
|
+
var NavPagesSource;
|
|
7490
|
+
(function (NavPagesSource) {
|
|
7491
|
+
NavPagesSource["Header"] = "header";
|
|
7492
|
+
NavPagesSource["Site"] = "site";
|
|
7493
|
+
NavPagesSource["None"] = "none";
|
|
7494
|
+
})(NavPagesSource || (NavPagesSource = {}));
|
|
7495
|
+
/** The pages a phone menu lists: the header's (`navPages`), else `sitePages` — a hand-built header with no
|
|
7496
|
+
* page links still gets a working menu. */
|
|
7497
|
+
function phoneMenuPages(root, sitePages = []) {
|
|
7498
|
+
const own = navPages(root);
|
|
7499
|
+
if (own.length)
|
|
7500
|
+
return { pages: own, source: NavPagesSource.Header };
|
|
7501
|
+
return sitePages.length ? { pages: [...sitePages], source: NavPagesSource.Site } : { pages: [], source: NavPagesSource.None };
|
|
7502
|
+
}
|
|
7433
7503
|
/** A hand-built header: every page Link outside a drawer. */
|
|
7434
7504
|
function collectHeaderLinks(root) {
|
|
7435
7505
|
const out = [];
|
|
@@ -7653,7 +7723,7 @@ function planNavShape(globals, shape, options = {}, navId) {
|
|
|
7653
7723
|
throw new NavShapeError(`a phone menu cannot switch at "${at}"`);
|
|
7654
7724
|
const kit = makeKit(out, root.id, options.newId);
|
|
7655
7725
|
adoptRoles(root);
|
|
7656
|
-
const pages =
|
|
7726
|
+
const { pages } = phoneMenuPages(root, options.pages);
|
|
7657
7727
|
const wantDrawer = shape !== NavShape.Bar;
|
|
7658
7728
|
const wantBar = shape !== NavShape.Drawer;
|
|
7659
7729
|
const wantMenuButton = shape === NavShape.Drawer;
|
|
@@ -7773,7 +7843,7 @@ function addBarItem(globals, options = {}) {
|
|
|
7773
7843
|
if (kids.length >= BAR_ITEMS_MAX)
|
|
7774
7844
|
return { globals: out, added: false };
|
|
7775
7845
|
const linked = new Set(kids.filter(c => c.role === NavRole.BarItem).map(c => prop(c, 'pageRoute')));
|
|
7776
|
-
const page = barPages(root,
|
|
7846
|
+
const page = barPages(root, phoneMenuPages(root, options.pages).pages, BAR_ITEMS_MAX + 1).find(p => !linked.has(p.route))
|
|
7777
7847
|
?? { label: NAV_SHAPE_COPY.home, route: HOME_ROUTE };
|
|
7778
7848
|
const item = barItem(makeKit(out, root.id, options.newId), kids.length, page);
|
|
7779
7849
|
const more = kids.findIndex(c => c.role === NavRole.More);
|
|
@@ -7783,6 +7853,32 @@ function addBarItem(globals, options = {}) {
|
|
|
7783
7853
|
kids.push(item);
|
|
7784
7854
|
return { globals: out, added: true };
|
|
7785
7855
|
}
|
|
7856
|
+
function barOf(globals) {
|
|
7857
|
+
const out = clone$1(globals);
|
|
7858
|
+
const b = find(out, NavRole.Bar);
|
|
7859
|
+
return { out, kids: b ? (b.children ?? (b.children = [])) : null };
|
|
7860
|
+
}
|
|
7861
|
+
/**
|
|
7862
|
+
* The bar's Items list "Remove" (board 2): the item goes, More included — refused below three items (Q2:
|
|
7863
|
+
* 3–5), so the bar never shrinks into something a thumb cannot use. Input not mutated.
|
|
7864
|
+
*/
|
|
7865
|
+
function removeBarItem(globals, itemId) {
|
|
7866
|
+
const { out, kids } = barOf(globals);
|
|
7867
|
+
const i = kids ? kids.findIndex(c => c.id === itemId) : -1;
|
|
7868
|
+
if (!kids || i < 0 || kids.length <= BAR_ITEMS_MIN)
|
|
7869
|
+
return { globals: out, changed: false };
|
|
7870
|
+
kids.splice(i, 1);
|
|
7871
|
+
return { globals: out, changed: true };
|
|
7872
|
+
}
|
|
7873
|
+
/** The bar's Items list drag-to-reorder (board 2): item `from` moves to `to`. Input not mutated. */
|
|
7874
|
+
function moveBarItem(globals, from, to) {
|
|
7875
|
+
const { out, kids } = barOf(globals);
|
|
7876
|
+
if (!kids || from === to || from < 0 || to < 0 || from >= kids.length || to >= kids.length)
|
|
7877
|
+
return { globals: out, changed: false };
|
|
7878
|
+
const [item] = kids.splice(from, 1);
|
|
7879
|
+
kids.splice(to, 0, item);
|
|
7880
|
+
return { globals: out, changed: true };
|
|
7881
|
+
}
|
|
7786
7882
|
|
|
7787
7883
|
/**
|
|
7788
7884
|
* THE composer — a Site Plan in, ordinary IOX data out (design-system plan §6b, W1). Deterministic and
|
|
@@ -8266,6 +8362,8 @@ const CARD_BINDS = {
|
|
|
8266
8362
|
function composeRecipe(pn, id, repeater, ctx) {
|
|
8267
8363
|
if (pn.recipe === PatternRecipe.Accordion)
|
|
8268
8364
|
return composeAccordion(pn, id, ctx);
|
|
8365
|
+
if (pn.recipe === PatternRecipe.Tabs)
|
|
8366
|
+
return composeTabs(pn, id, ctx);
|
|
8269
8367
|
if (pn.recipe === PatternRecipe.Carousel)
|
|
8270
8368
|
return composeCarousel(pn, id, ctx);
|
|
8271
8369
|
if (pn.recipe === PatternRecipe.Lightbox)
|
|
@@ -8355,6 +8453,71 @@ function composeAccordion(pn, id, ctx) {
|
|
|
8355
8453
|
addBinding(accordionAnswerOf(template), { prop: ACCORDION_BIND_PROP, source: binding, path: picks.answer });
|
|
8356
8454
|
return root;
|
|
8357
8455
|
}
|
|
8456
|
+
/** The content type a section binding lists, through its data source. */
|
|
8457
|
+
function boundContentType(binding, ctx) {
|
|
8458
|
+
const source = ctx.plan.dataSources.find(d => d.alias === binding);
|
|
8459
|
+
return source && 'contentType' in source ? ctx.plan.contentTypes.find(t => t.name === source.contentType) : undefined;
|
|
8460
|
+
}
|
|
8461
|
+
const TABS_ROOT_KEY = 'tabs';
|
|
8462
|
+
const TABS_HOLDER_KEYS = new Set(['list', 'panels']);
|
|
8463
|
+
/**
|
|
8464
|
+
* Tabs (CC4a) — the composite-core recipe stamped with this composer's factory. Manual (no section binding): three
|
|
8465
|
+
* tabs whose words are placeholders the studio rewrites (`ph: 'copy'`) and whose pictures it supplies — never invented.
|
|
8466
|
+
* From content (the section names a list `binding`, R3's AI path): the tab list and the panels holder are BOTH
|
|
8467
|
+
* Repeaters over that list, each holding its one template (one row → its tab AND its panel, paired by the row's id at
|
|
8468
|
+
* render), the tab's words, the panel's words and its picture bound to the type's first short-text, long-text and image
|
|
8469
|
+
* fields (`pickTabsAutoBinds`, the builder switch's rule), limit 6. The tab list is named by the pattern's `name`.
|
|
8470
|
+
*/
|
|
8471
|
+
function composeTabs(pn, id, ctx) {
|
|
8472
|
+
const binding = pn.src === PatternDirective.OptionalBinding || pn.src === PatternDirective.Binding ? ctx.section?.binding ?? null : null;
|
|
8473
|
+
if (pn.src === PatternDirective.Binding && !binding)
|
|
8474
|
+
throw new ComposeError(`${id}: "$binding" with no section binding`);
|
|
8475
|
+
const factory = (spec) => {
|
|
8476
|
+
const type = binding && TABS_HOLDER_KEYS.has(spec.key) ? REPEATER : spec.type;
|
|
8477
|
+
return create(spec.key === TABS_ROOT_KEY ? id : `${id}-${spec.key}`, type, { ...(spec.props ?? {}) }, spec.elementStyle ?? null);
|
|
8478
|
+
};
|
|
8479
|
+
const root = buildTabsRecipe(factory, { tabs: binding ? TABS_WORDS.slice(0, 1) : undefined, label: pn.name });
|
|
8480
|
+
const tree = root;
|
|
8481
|
+
if (!binding) {
|
|
8482
|
+
if (pn.ph === PatternPlaceholder.Copy) {
|
|
8483
|
+
for (const tab of tabsTriggers(tree)) {
|
|
8484
|
+
const words = tabLabelNodeOf(tab);
|
|
8485
|
+
if (words)
|
|
8486
|
+
ctx.placeholders.push({ nodeId: words.id, owner: ctx.owner, kind: PlaceholderKind.Copy, text: String(words.props?.[TABS_LABEL_BIND_PROP] ?? '') });
|
|
8487
|
+
}
|
|
8488
|
+
for (const panel of tabsPanels(tree)) {
|
|
8489
|
+
const text = tabPanelTextOf(panel);
|
|
8490
|
+
if (text)
|
|
8491
|
+
ctx.placeholders.push({ nodeId: text.id, owner: ctx.owner, kind: PlaceholderKind.Copy, text: String(text.props?.[TABS_TEXT_BIND_PROP] ?? '') });
|
|
8492
|
+
const image = tabPanelImageOf(panel);
|
|
8493
|
+
if (image)
|
|
8494
|
+
ctx.placeholders.push({ nodeId: image.id, owner: ctx.owner, kind: PlaceholderKind.Image, text: '' });
|
|
8495
|
+
}
|
|
8496
|
+
}
|
|
8497
|
+
return root;
|
|
8498
|
+
}
|
|
8499
|
+
const type = boundContentType(binding, ctx);
|
|
8500
|
+
const picks = pickTabsAutoBinds((type?.fields ?? []).map(f => ({ name: f.name, type: f.type, controller: f.controller })));
|
|
8501
|
+
if (!picks.label || !picks.text) {
|
|
8502
|
+
throw new ComposeError(`${id}: "${binding}" has no short-text field for the tab and long-text field for its panel`);
|
|
8503
|
+
}
|
|
8504
|
+
for (const holder of [tabsListOf(tree), tabsPanelsOf(tree)]) {
|
|
8505
|
+
addBinding(holder, { prop: 'source', source: binding, path: '' });
|
|
8506
|
+
setProp(holder, 'limit', TABS_CONTENT_LIMIT);
|
|
8507
|
+
}
|
|
8508
|
+
const [tab] = tabsTriggers(tree);
|
|
8509
|
+
const [panel] = tabsPanels(tree);
|
|
8510
|
+
delete tab.itemKey;
|
|
8511
|
+
delete panel.itemKey;
|
|
8512
|
+
addBinding(tabLabelNodeOf(tab), { prop: TABS_LABEL_BIND_PROP, source: binding, path: picks.label });
|
|
8513
|
+
addBinding(tabPanelTextOf(panel), { prop: TABS_TEXT_BIND_PROP, source: binding, path: picks.text });
|
|
8514
|
+
const image = tabPanelImageOf(panel);
|
|
8515
|
+
if (image && picks.image) {
|
|
8516
|
+
addBinding(image, { prop: TABS_IMAGE_BIND_PROP, source: binding, path: picks.image });
|
|
8517
|
+
addBinding(image, { prop: 'alt', source: binding, path: picks.label });
|
|
8518
|
+
}
|
|
8519
|
+
return root;
|
|
8520
|
+
}
|
|
8358
8521
|
/** A recipe's node factory for this composer: ids derive from the recipe's keys (`<id>-<key>`, the root `<id>`). */
|
|
8359
8522
|
function recipeFactory(id, rootKey) {
|
|
8360
8523
|
return spec => create(spec.key === rootKey ? id : `${id}-${spec.key}`, spec.type, { ...(spec.props ?? {}) }, spec.elementStyle ?? null);
|
|
@@ -8512,6 +8675,54 @@ function composeSite(plan, context) {
|
|
|
8512
8675
|
issues,
|
|
8513
8676
|
};
|
|
8514
8677
|
}
|
|
8678
|
+
// ── The site header on its own (CC1.1) ──────────────────────────────────────────────────────────
|
|
8679
|
+
/** The header pattern the Studio template stamps — and the builder palette's "Navigation bar". */
|
|
8680
|
+
const SITE_HEADER_PATTERN = 'nav.inline-cta';
|
|
8681
|
+
const ROUTE_PARAM = ':';
|
|
8682
|
+
const HOME_PATH = '/';
|
|
8683
|
+
const KEY_UNSAFE = /[^a-z0-9]+/gi;
|
|
8684
|
+
/** The pages a navigation links to: every page but Home (the brand links there) and template pages
|
|
8685
|
+
* (a `/work/:slug` route has no single address), in the studio's order. */
|
|
8686
|
+
function siteNavPages(pages) {
|
|
8687
|
+
return pages.filter(p => !!p.route && !p.isHome && p.route !== HOME_PATH && !p.route.includes(ROUTE_PARAM));
|
|
8688
|
+
}
|
|
8689
|
+
function navPagePlans(pages) {
|
|
8690
|
+
const used = new Set();
|
|
8691
|
+
return pages.map((p, i) => {
|
|
8692
|
+
let key = p.route.replace(KEY_UNSAFE, '-').replace(/^-+|-+$/g, '').toLowerCase() || `page-${i}`;
|
|
8693
|
+
for (let n = 2; used.has(key); n++)
|
|
8694
|
+
key = `${key}-${n}`;
|
|
8695
|
+
used.add(key);
|
|
8696
|
+
return { key, name: p.name, route: p.route, inNav: true, isHome: p.isHome === true, sections: [] };
|
|
8697
|
+
});
|
|
8698
|
+
}
|
|
8699
|
+
/**
|
|
8700
|
+
* The Studio template's header, composed for a studio that has no Site Plan — the builder palette's
|
|
8701
|
+
* "Navigation bar". THE SAME pattern and the same composer as `composeSite`: the brand (the business name),
|
|
8702
|
+
* one plain Link per navigation page, the call to action (W1.1's one dangling-link rule when the studio has
|
|
8703
|
+
* no /contact page: an email to the business, else removed), and the pattern's phone shape. Pure.
|
|
8704
|
+
*/
|
|
8705
|
+
function composeNavHeader(input, options) {
|
|
8706
|
+
if (!nonEmpty(options?.id))
|
|
8707
|
+
throw new ComposeError('composeNavHeader needs the header id');
|
|
8708
|
+
const pattern = patternById(SITE_HEADER_PATTERN);
|
|
8709
|
+
const all = navPagePlans(input.pages);
|
|
8710
|
+
const navRoutes = new Set(siteNavPages(input.pages).map(p => p.route));
|
|
8711
|
+
const pages = all.map(p => ({ ...p, inNav: navRoutes.has(p.route) }));
|
|
8712
|
+
const plan = { business: input.business, pages, dataSources: [], contentTypes: [] };
|
|
8713
|
+
const email = nonEmpty(input.business.facts?.email) ? input.business.facts.email.trim() : null;
|
|
8714
|
+
const issues = [];
|
|
8715
|
+
const ctx = {
|
|
8716
|
+
owner: GlobalSlot.Header, page: null, section: null, plan, year: 0, email, navPage: null, nextAlias: null,
|
|
8717
|
+
placeholders: [], issues, routes: new Set(pages.map(p => p.route)),
|
|
8718
|
+
};
|
|
8719
|
+
const root = composePattern(pattern, options.id, ctx);
|
|
8720
|
+
root.isGlobal = true;
|
|
8721
|
+
root.globalPosition = pattern.global;
|
|
8722
|
+
const shape = pattern.phoneMenu;
|
|
8723
|
+
const header = shape ? planNavShape([root], shape, { newId: options.newId }, root.id).globals[0] : root;
|
|
8724
|
+
return { header, issues };
|
|
8725
|
+
}
|
|
8515
8726
|
|
|
8516
8727
|
/**
|
|
8517
8728
|
* @vectoriox/iox-ui/generation-core — the shared, FRAMEWORK-FREE site-plan core (design-system plan
|
|
@@ -8529,5 +8740,5 @@ function composeSite(plan, context) {
|
|
|
8529
8740
|
* Generated bundle index. Do not edit.
|
|
8530
8741
|
*/
|
|
8531
8742
|
|
|
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 };
|
|
8743
|
+
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, NavPagesSource, NavRole, NavShape, NavShapeError, PatternDirective, PatternGlobalPosition, PatternOpens, PatternPlaceholder, PatternRecipe, PhoneMenuBreakpoint, PlaceholderKind, PlanDataSourceMode, PlanDataSourceType, RelativeOp, SAMPLE_IMAGE_REF, SITE_FACT_LABELS, SITE_HEADER_PATTERN, 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, composeNavHeader, composeSite, createSerializedNode, findNavRoot, moveBarItem, navPages, navShapeBreakpoint, navShapeOf, nextSourceOf, nodeBindPaths, parseSampleImageRef, patternById, patternNeedsBinding, patternNodes, patternOptionalParts, patternScopes, patternSlots, patternTakesBinding, phoneMenuPages, planFromTemplate, planNavShape, releasedLayoutDefaults, removeBarItem, requiredShapeKeys, routeParams, sampleImageRef, setStyleProps, shapeKeys, siteNavPages, validateSitePlan, walkSerialized };
|
|
8533
8744
|
//# sourceMappingURL=vectoriox-iox-ui-generation-core.mjs.map
|