@vectoriox/iox-builder 6.0.1 → 7.0.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.
@@ -16,7 +16,7 @@ import { SelectModule } from 'primeng/select';
16
16
  import { TooltipModule } from 'primeng/tooltip';
17
17
  import { DialogModule } from 'primeng/dialog';
18
18
  import * as i1 from '@vectoriox/iox-ui';
19
- import { VALIDATION_STATES as VALIDATION_STATES$1, VALIDATION_STATE_CLASS as VALIDATION_STATE_CLASS$1, DEFAULT_WIDTH_BY_TYPE, COLOR_SCHEME_TRAIT, COLOR_SCHEMES, ColorScheme, Breakpoint, BREAKPOINTS, countOverrides, setOverride, defaultElementStyleFor, applyElementStyle, stripSuppressedProps, DeclarationContext, buildPartSelector, planResponsiveRules, resolveStyleProps, buildTargetedStateSelector, statePseudoClass, compileTokenCssBlocks, fontsForTokens, GOOGLE_FONTS_FILE_ORIGIN, googleFontHref, compileDeclarations, wrapInBreakpointQuery, BreakpointQueryKind, breakpointDef, breakpointForWidth, FormRenderState, safeSetInput, ContextStore, ComponentInstanceRegistryService, isActionEnabled, isContextAction, contextActionEffect, resolveContextValue, declaredInputNames, emptyImagePlaceholder, syncNodeDesignClasses, composeVirtualTraits, withDefaultHoverTransition, stateNamesIn, partClassName, resolveInputs, setInputOverride, collectContextSlotKeys, withContextSlots, compilePageStyle, nodeUsesContext, resolveBoundValue, IS_PREVIEW, planDataSource, dataSourcePlanToRequest, normalizeCollectionResult, resolveRepeaterItems, getByPath, computeRelative, IoxPageModule, PALETTE_PRESETS, PART_CLASS_NAMES } from '@vectoriox/iox-ui';
19
+ import { VALIDATION_STATES as VALIDATION_STATES$1, VALIDATION_STATE_CLASS as VALIDATION_STATE_CLASS$1, DEFAULT_WIDTH_BY_TYPE, COLOR_SCHEME_TRAIT, COLOR_SCHEMES, ColorScheme, Breakpoint, BREAKPOINTS, countOverrides, setOverride, defaultElementStyleFor, applyElementStyle, stripSuppressedProps, DeclarationContext, buildPartSelector, planResponsiveRules, resolveStyleProps, buildTargetedStateSelector, statePseudoClass, compileTokenCssBlocks, catalogFontId, fontsForTokens, GOOGLE_FONTS_FILE_ORIGIN, googleFontHref, compileDeclarations, wrapInBreakpointQuery, BreakpointQueryKind, breakpointDef, breakpointForWidth, FormRenderState, safeSetInput, ContextStore, ComponentInstanceRegistryService, isActionEnabled, isContextAction, contextActionEffect, resolveContextValue, declaredInputNames, emptyImagePlaceholder, syncNodeDesignClasses, composeVirtualTraits, withDefaultHoverTransition, stateNamesIn, partClassName, resolveInputs, setInputOverride, collectContextSlotKeys, withContextSlots, compilePageStyle, nodeUsesContext, resolveBoundValue, IS_PREVIEW, planDataSource, dataSourcePlanToRequest, normalizeCollectionResult, resolveRepeaterItems, getByPath, computeRelative, IoxPageModule, PALETTE_PRESETS, elementStyleById, PART_CLASS_NAMES } from '@vectoriox/iox-ui';
20
20
  export { ComponentInstanceRegistryService, VIRTUAL_TRAIT_KEYS, composeVirtualTraits } from '@vectoriox/iox-ui';
21
21
  import Lenis from 'lenis';
22
22
  import { Subject, BehaviorSubject, of, forkJoin } from 'rxjs';
@@ -1982,6 +1982,7 @@ class StyleRegistryService {
1982
1982
  this.nodeTypes = new Map();
1983
1983
  this.tokenCss = { base: '', dark: '' };
1984
1984
  this.tokenRows = [];
1985
+ this.uploadedFamilies = [];
1985
1986
  this.tokenMode = 'auto';
1986
1987
  this.styleEl = null;
1987
1988
  this._flushPending = false;
@@ -2287,6 +2288,25 @@ class StyleRegistryService {
2287
2288
  this.styleEl = null;
2288
2289
  }
2289
2290
  }
2291
+ /**
2292
+ * The families the org UPLOADED (the host reads them from storage). Such a family renders from the
2293
+ * org's own faces, so the canvas never links Google's — even when the catalog has one of that name:
2294
+ * an uploaded Inter variable file sat beside Google's static Inter cuts and lost to them (DS5.1). A
2295
+ * link added before the list arrived is removed, or it would keep shadowing the upload.
2296
+ */
2297
+ setUploadedFamilies(families) {
2298
+ this.uploadedFamilies = [...(families ?? [])];
2299
+ const head = this.styleEl?.ownerDocument?.head ?? (typeof document !== 'undefined' ? document.head : null);
2300
+ for (const family of this.uploadedFamilies) {
2301
+ const id = catalogFontId(family);
2302
+ if (!id || !head)
2303
+ continue;
2304
+ head.querySelectorAll(`link[rel="stylesheet"][${BUILDER_FONT_LINK_ATTR}]`)
2305
+ .forEach(l => { if (l.getAttribute(BUILDER_FONT_LINK_ATTR) === id)
2306
+ l.remove(); });
2307
+ }
2308
+ this.flush();
2309
+ }
2290
2310
  /**
2291
2311
  * The Google families the org's font tokens name — the SAME catalog and URL the SSR engine links
2292
2312
  * (design-core `fontsForTokens` / `googleFontHref`), one `<link>` per family. They only declare
@@ -2294,11 +2314,19 @@ class StyleRegistryService {
2294
2314
  * Poppins. Links stay for the session: a trial that switches back costs no refetch.
2295
2315
  */
2296
2316
  loadTokenFonts(nodeCss) {
2297
- const head = this.styleEl?.ownerDocument?.head;
2298
- if (!head)
2299
- return;
2300
- const ids = fontsForTokens(this.tokenRows, { usedCss: nodeCss });
2301
- if (!ids.length)
2317
+ this.linkFonts(fontsForTokens(this.tokenRows, { usedCss: nodeCss, uploaded: this.uploadedFamilies }));
2318
+ }
2319
+ /**
2320
+ * Link the Google families OTHER rows name — a specimen wearing another look (the Design side-card's
2321
+ * Look tiles and pairing cards). The same catalog, URL and dedupe as the canvas's own fonts, so a
2322
+ * look that is later tried or applied costs no second fetch. Never touches the canvas's tokens.
2323
+ */
2324
+ loadFontsFor(tokens) {
2325
+ this.linkFonts(fontsForTokens(tokens ?? [], { uploaded: this.uploadedFamilies }));
2326
+ }
2327
+ linkFonts(ids) {
2328
+ const head = this.styleEl?.ownerDocument?.head ?? (typeof document !== 'undefined' ? document.head : null);
2329
+ if (!head || !ids.length)
2302
2330
  return;
2303
2331
  const doc = head.ownerDocument;
2304
2332
  if (!head.querySelector(`link[rel="preconnect"][${BUILDER_FONT_LINK_ATTR}]`)) {
@@ -10405,8 +10433,8 @@ const click = (actions) => ({
10405
10433
  target: 'self', duration: 0, delay: 0, easing: 'ease', ...a,
10406
10434
  })),
10407
10435
  });
10408
- const TOKEN$1 = (name, fallback) => `var(--iox-${name}, ${fallback})`;
10409
- function button(label, createConfig, interaction, styles = {}) {
10436
+ const TOKEN = (name, fallback) => `var(--iox-${name}, ${fallback})`;
10437
+ function button$1(label, createConfig, interaction, styles = {}) {
10410
10438
  const btn = createConfig('ButtonBlock');
10411
10439
  const text = textNode(label, createConfig);
10412
10440
  if (!btn || !text)
@@ -10425,20 +10453,20 @@ function button(label, createConfig, interaction, styles = {}) {
10425
10453
  // width:'auto' + flexShrink:'0' on every voice: a consent button never stretches to its row's
10426
10454
  // height nor shrinks until its label wraps — both happened on the canvas (Rule 0, 2026-10-10).
10427
10455
  const SOLID = {
10428
- backgroundColor: TOKEN$1('brand-500', '#1f1d1b'), color: TOKEN$1('brandInk', '#ffffff'),
10456
+ backgroundColor: TOKEN('brand-500', '#1f1d1b'), color: TOKEN('brandInk', '#ffffff'),
10429
10457
  fontSize: '12px', fontWeight: '600', padding: '9px 16px',
10430
- borderRadius: TOKEN$1('radius-control', '4px'),
10458
+ borderRadius: TOKEN('radius-control', '4px'),
10431
10459
  width: 'auto', flexShrink: '0',
10432
10460
  };
10433
10461
  const OUTLINED = {
10434
- backgroundColor: 'transparent', color: TOKEN$1('ink', '#1f1d1b'),
10435
- borderWidth: '1px', borderStyle: 'solid', borderColor: TOKEN$1('ink', '#1f1d1b'),
10462
+ backgroundColor: 'transparent', color: TOKEN('ink', '#1f1d1b'),
10463
+ borderWidth: '1px', borderStyle: 'solid', borderColor: TOKEN('ink', '#1f1d1b'),
10436
10464
  fontSize: '12px', fontWeight: '600', padding: '8px 16px',
10437
- borderRadius: TOKEN$1('radius-control', '4px'),
10465
+ borderRadius: TOKEN('radius-control', '4px'),
10438
10466
  width: 'auto', flexShrink: '0',
10439
10467
  };
10440
10468
  const QUIET = {
10441
- backgroundColor: 'transparent', color: TOKEN$1('inkMuted', '#4a453f'),
10469
+ backgroundColor: 'transparent', color: TOKEN('inkMuted', '#4a453f'),
10442
10470
  fontSize: '11px', fontWeight: '400', padding: '2px 4px', textDecoration: 'underline',
10443
10471
  width: 'auto', flexShrink: '0',
10444
10472
  };
@@ -10471,26 +10499,26 @@ function buildConsentPreset(createConfig) {
10471
10499
  setStyleTrait(banner, 'width', '100%');
10472
10500
  setStyleTrait(banner, 'borderRadius', '0px');
10473
10501
  setStyleTrait(banner, 'padding', '14px 20px');
10474
- setStyleTrait(banner, 'backgroundColor', TOKEN$1('surface', '#ffffff'));
10502
+ setStyleTrait(banner, 'backgroundColor', TOKEN('surface', '#ffffff'));
10475
10503
  setStyleTrait(banner, 'borderWidth', '1px');
10476
10504
  setStyleTrait(banner, 'borderStyle', 'solid');
10477
- setStyleTrait(banner, 'borderColor', TOKEN$1('ink', '#1f1d1b'));
10505
+ setStyleTrait(banner, 'borderColor', TOKEN('ink', '#1f1d1b'));
10478
10506
  setStyleTrait(banner, 'boxShadow', 'none');
10479
- const heading = text$1('Cookies on this site', { fontSize: '13px', fontWeight: '600', color: TOKEN$1('ink', '#1f1d1b') }, createConfig);
10507
+ const heading = text$1('Cookies on this site', { fontSize: '13px', fontWeight: '600', color: TOKEN('ink', '#1f1d1b') }, createConfig);
10480
10508
  // The first layer NAMES the three categories (plan §7) — the board does it in the copy.
10481
- const body = text$1('We use analytics and marketing cookies only if you allow them. Necessary ones always run.', { fontSize: '11px', color: TOKEN$1('inkMuted', '#4a453f'), lineHeight: '1.5', maxWidth: '330px' }, createConfig);
10509
+ const body = text$1('We use analytics and marketing cookies only if you allow them. Necessary ones always run.', { fontSize: '11px', color: TOKEN('inkMuted', '#4a453f'), lineHeight: '1.5', maxWidth: '330px' }, createConfig);
10482
10510
  // width:'auto' throughout — the Container default is 100%, and two 100%-wide columns
10483
10511
  // cannot share the bar's row (they wrap into a stack; Rule 0 catch, 2026-10-10).
10484
10512
  const textCol = row(createConfig, { display: 'flex', flexDirection: 'column', gap: '4px', flexGrow: '1', width: 'auto' }, [heading, body]);
10485
- const accept = button('Accept all', createConfig, click([
10513
+ const accept = button$1('Accept all', createConfig, click([
10486
10514
  { type: 'grantConsent' },
10487
10515
  { type: 'closeDialog', target: banner.id },
10488
10516
  ]), SOLID);
10489
- const reject = button('Reject all', createConfig, click([
10517
+ const reject = button$1('Reject all', createConfig, click([
10490
10518
  { type: 'denyConsent' },
10491
10519
  { type: 'closeDialog', target: banner.id },
10492
10520
  ]), OUTLINED);
10493
- const prefs = button('Preferences', createConfig, click([
10521
+ const prefs = button$1('Preferences', createConfig, click([
10494
10522
  { type: 'openConsentPreferences', target: preferences.id },
10495
10523
  ]), QUIET);
10496
10524
  const acceptRejectRow = row(createConfig, { display: 'flex', gap: '7px', alignItems: 'center', width: 'auto' }, [accept, reject]);
@@ -10508,19 +10536,19 @@ function buildConsentPreset(createConfig) {
10508
10536
  setStyleTrait(preferences, 'display', 'flex');
10509
10537
  setStyleTrait(preferences, 'flexDirection', 'column');
10510
10538
  setStyleTrait(preferences, 'gap', '12px');
10511
- const prefsHeading = text$1('Your privacy choices', { fontSize: '16px', fontWeight: '600', color: TOKEN$1('ink', '#1f1d1b') }, createConfig);
10512
- const prefsSub = text$1('Change these any time from “Cookie settings” in the footer.', { fontSize: '12px', color: TOKEN$1('inkMuted', '#6b6560') }, createConfig);
10539
+ const prefsHeading = text$1('Your privacy choices', { fontSize: '16px', fontWeight: '600', color: TOKEN('ink', '#1f1d1b') }, createConfig);
10540
+ const prefsSub = text$1('Change these any time from “Cookie settings” in the footer.', { fontSize: '12px', color: TOKEN('inkMuted', '#6b6560') }, createConfig);
10513
10541
  const CARD = {
10514
10542
  display: 'flex', gap: '12px', alignItems: 'flex-start',
10515
- borderWidth: '1px', borderStyle: 'solid', borderColor: TOKEN$1('line', '#e6e3e1'),
10516
- borderRadius: TOKEN$1('radius-surface', '8px'), padding: '12px 14px',
10543
+ borderWidth: '1px', borderStyle: 'solid', borderColor: TOKEN('line', '#e6e3e1'),
10544
+ borderRadius: TOKEN('radius-surface', '8px'), padding: '12px 14px',
10517
10545
  };
10518
- const nameText = (s) => text$1(s, { fontSize: '13px', fontWeight: '600', color: TOKEN$1('ink', '#1f1d1b') }, createConfig);
10519
- const descText = (s) => text$1(s, { fontSize: '11px', color: TOKEN$1('inkMuted', '#6b6560'), lineHeight: '1.5' }, createConfig);
10546
+ const nameText = (s) => text$1(s, { fontSize: '13px', fontWeight: '600', color: TOKEN('ink', '#1f1d1b') }, createConfig);
10547
+ const descText = (s) => text$1(s, { fontSize: '11px', color: TOKEN('inkMuted', '#6b6560'), lineHeight: '1.5' }, createConfig);
10520
10548
  const textColOf = (name, desc) => row(createConfig, { display: 'flex', flexDirection: 'column', gap: '2px', flexGrow: '1', width: 'auto' }, [nameText(name), descText(desc)]);
10521
10549
  const alwaysOn = text$1('ALWAYS ON', {
10522
- fontSize: '10px', fontWeight: '600', color: TOKEN$1('inkMuted', '#6b6560'),
10523
- borderWidth: '1px', borderStyle: 'solid', borderColor: TOKEN$1('line', '#e6e3e1'),
10550
+ fontSize: '10px', fontWeight: '600', color: TOKEN('inkMuted', '#6b6560'),
10551
+ borderWidth: '1px', borderStyle: 'solid', borderColor: TOKEN('line', '#e6e3e1'),
10524
10552
  borderRadius: '999px', padding: '3px 8px',
10525
10553
  width: 'auto', flexShrink: '0', // a chip hugs its word (Text defaults to 100%)
10526
10554
  }, createConfig);
@@ -10546,14 +10574,14 @@ function buildConsentPreset(createConfig) {
10546
10574
  const analyticsCard = categoryCard('analytics', 'Analytics', 'Anonymous counts of visits and pages. Off means the analytics script never loads.');
10547
10575
  const marketingCard = categoryCard('marketing', 'Marketing', 'Ads measurement and remarketing. Off means no ad cookies and no data shared.');
10548
10576
  // Board C2's footer: bulk exits live here too — Reject | (grow) | Done | Accept.
10549
- const prefsReject = button('Reject all', createConfig, click([
10577
+ const prefsReject = button$1('Reject all', createConfig, click([
10550
10578
  { type: 'denyConsent' },
10551
10579
  { type: 'closeDialog', target: preferences.id },
10552
10580
  ]), OUTLINED);
10553
- const prefsDone = button('Done', createConfig, click([
10581
+ const prefsDone = button$1('Done', createConfig, click([
10554
10582
  { type: 'closeDialog', target: preferences.id },
10555
- ]), { ...OUTLINED, color: TOKEN$1('inkMuted', '#3d3833'), borderColor: TOKEN$1('line', '#d8d4d1') });
10556
- const prefsAccept = button('Accept all', createConfig, click([
10583
+ ]), { ...OUTLINED, color: TOKEN('inkMuted', '#3d3833'), borderColor: TOKEN('line', '#d8d4d1') });
10584
+ const prefsAccept = button$1('Accept all', createConfig, click([
10557
10585
  { type: 'grantConsent' },
10558
10586
  { type: 'closeDialog', target: preferences.id },
10559
10587
  ]), SOLID);
@@ -10566,7 +10594,7 @@ function buildConsentPreset(createConfig) {
10566
10594
  return null;
10567
10595
  preferences.children = [prefsHeading, prefsSub, necessaryCard, analyticsCard, marketingCard, footer];
10568
10596
  // ── the withdrawal path (§7.3) ───────────────────────────────────────────
10569
- const withdrawal = button('Cookie settings', createConfig, click([
10597
+ const withdrawal = button$1('Cookie settings', createConfig, click([
10570
10598
  { type: 'openConsentPreferences', target: preferences.id },
10571
10599
  ]), QUIET);
10572
10600
  if (!withdrawal)
@@ -10576,17 +10604,23 @@ function buildConsentPreset(createConfig) {
10576
10604
  }
10577
10605
 
10578
10606
  /**
10579
- * THE FLAVOR SAMPLER (flavors plan §7, F2) — the fixed tree the picker renders through the REAL
10580
- * builder pipeline with candidate tokens applied. Never a screenshot, never a second renderer:
10581
- * `render-parity.md` is the record of what a convenience renderer costs.
10607
+ * THE DESIGN SPECIMENS (design-system plan §5, Board 6) — the small trees the Design side-card
10608
+ * renders through the REAL builder pipeline (`[ioxRender]`), never a hand-drawn HTML mock: a look's
10609
+ * tile on the Look tab, and one specimen per element style on the Element styles tab. Every node is
10610
+ * a real config wearing a real element style, so what the author sees is what the style renders.
10611
+ *
10612
+ * Specimens are display-only: every node is marked like a repeater's preview clone, so the canvas
10613
+ * never registers, selects or drags one (RenderDirective skips overlay wiring for them).
10582
10614
  *
10583
- * Every colour in it is a token REFERENCE with a Swiss-neutral fallback — so before a brand is
10584
- * chosen the sampler is the monochrome study the F1 board shows, and the moment candidate tokens
10585
- * are upserted it recolours live (the F2 board), proving the exact mechanism a later re-theme
10586
- * uses on a built site: nothing moves, only tokens change.
10615
+ * The words come from the host (translated); the only sizes set here are the board's specimen
10616
+ * scale-downs (a Display heading at 24px fits a 36%-wide card; the live style does not).
10587
10617
  */
10588
- const BRAND = (step, fallback) => `var(--iox-brand-${step}, ${fallback})`;
10589
- const TOKEN = (name, fallback) => `var(--iox-${name}, ${fallback})`;
10618
+ /** What a specimen node is marked with — RenderDirective's "display only" flag. */
10619
+ const SPECIMEN_FLAG = '__previewClone';
10620
+ /** Board 6's specimen font sizes for the heading styles — the live sizes do not fit the card. */
10621
+ const HEADING_SPECIMEN_SIZE = { display: '24px', h1: '21px', h2: '18px', h3: '15px' };
10622
+ /** The four dots on a look's tile, left to right (Board 6 `TILE`). */
10623
+ const TILE_DOTS = ['accent', 'text', 'surface-2', 'line-strong'];
10590
10624
  function styled(node, styles) {
10591
10625
  if (!node)
10592
10626
  return null;
@@ -10594,90 +10628,149 @@ function styled(node, styles) {
10594
10628
  setStyleTrait(node, prop, value);
10595
10629
  return node;
10596
10630
  }
10597
- function text(content, styles, createConfig) {
10598
- return styled(textNode(content, createConfig), styles);
10631
+ function wearing(node, styleId) {
10632
+ if (!node)
10633
+ return null;
10634
+ applyElementStyle(node, styleId);
10635
+ return node;
10599
10636
  }
10600
- function sampleCard(title, meta, createConfig) {
10601
- const card = createConfig('Card');
10602
- if (!card)
10637
+ function text(content, createConfig, styleId, styles = {}) {
10638
+ const node = textNode(content, createConfig);
10639
+ if (styleId)
10640
+ wearing(node, styleId);
10641
+ return styled(node, styles);
10642
+ }
10643
+ function withChildren(node, children) {
10644
+ if (!node || children.some(c => !c))
10603
10645
  return null;
10604
- styled(card, {
10605
- backgroundColor: TOKEN('surface', '#ffffff'),
10606
- borderWidth: '1px', borderStyle: 'solid', borderColor: TOKEN('line', '#949494'),
10607
- padding: '10px', display: 'flex', flexDirection: 'column', gap: '8px', width: '150px',
10608
- });
10609
- const media = styled(createConfig('Container'), {
10610
- height: '48px', width: '100%', backgroundColor: BRAND(50, '#ebe8e5'),
10611
- });
10612
- const name = text(title, { fontSize: '12px', fontWeight: '600', color: TOKEN('ink', '#1f1d1b') }, createConfig);
10613
- const sub = text(meta, { fontSize: '11px', color: TOKEN('inkMuted', '#6b6560') }, createConfig);
10614
- if (!media || !name || !sub)
10646
+ node.children = children;
10647
+ return node;
10648
+ }
10649
+ /** Mark a tree display-only (not selectable, not draggable). Returns the root. */
10650
+ function markSpecimen(root) {
10651
+ if (!root)
10652
+ return root;
10653
+ root[SPECIMEN_FLAG] = true;
10654
+ for (const child of root.children ?? [])
10655
+ markSpecimen(child);
10656
+ return root;
10657
+ }
10658
+ /** Whether a node was built as a specimen. */
10659
+ function isSpecimen(node) {
10660
+ return !!node?.[SPECIMEN_FLAG];
10661
+ }
10662
+ function button(styleId, label, createConfig, styles = {}) {
10663
+ const btn = wearing(createConfig('ButtonBlock'), styleId);
10664
+ if (!btn)
10615
10665
  return null;
10616
- card.children = [media, name, sub];
10617
- return card;
10666
+ setTrait(btn, 'tag', 'button');
10667
+ styled(btn, styles);
10668
+ return withChildren(btn, [text(label, createConfig, undefined, { color: 'inherit' })]);
10669
+ }
10670
+ function boxed(styleId, createConfig, children, styles) {
10671
+ return withChildren(styled(wearing(createConfig('Container'), styleId), styles), children);
10618
10672
  }
10619
10673
  /**
10620
- * Build the sampler tree (the F1/F2 boards' hero + two cards). Null when a type is missing —
10621
- * a picker that previews half a sampler would be lying about the flavor.
10674
+ * The specimen for one element style (Board 6, Element styles tab). Null for an unknown id or a
10675
+ * missing type — a list that shows half a specimen would be lying about the style.
10622
10676
  */
10623
- function buildFlavorSampler(createConfig) {
10624
- const root = styled(createConfig('Container'), {
10625
- display: 'flex', gap: '20px', alignItems: 'stretch', width: '100%',
10626
- padding: '18px 22px', backgroundColor: TOKEN('surface', '#ffffff'),
10627
- });
10628
- if (!root)
10677
+ function buildElementStyleSpecimen(id, createConfig, copy) {
10678
+ const style = elementStyleById(id);
10679
+ if (!style)
10629
10680
  return null;
10630
- root.label = 'Flavor sampler';
10631
- const hero = styled(createConfig('Container'), {
10632
- display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: '10px', flexGrow: '1',
10633
- });
10634
- // Rendered already-uppercase: textTransform is not in the style-trait vocabulary, and the
10635
- // sampler only uses traits that LAND (asserted in the spec — setStyleTrait no-ops silently).
10636
- const badge = text('STUDIO', {
10637
- fontSize: '10px', letterSpacing: '0.14em',
10638
- fontWeight: '600', color: BRAND(600, '#6b6560'),
10639
- }, createConfig);
10640
- const heading = text('Interiors, measured twice.', {
10641
- fontSize: '30px', fontWeight: '600', lineHeight: '1.05', letterSpacing: '-0.02em',
10642
- maxWidth: '320px', color: TOKEN('ink', '#1f1d1b'),
10643
- }, createConfig);
10644
- const button = (label, styles) => {
10645
- const btn = createConfig('ButtonBlock');
10646
- const inner = text(label, { fontSize: '12px', fontWeight: '600', color: 'inherit' }, createConfig);
10647
- if (!btn || !inner)
10648
- return null;
10649
- setTrait(btn, 'tag', 'button');
10650
- styled(btn, styles);
10651
- btn.children = [inner];
10652
- return btn;
10653
- };
10654
- const primary = button('Book a visit', {
10655
- backgroundColor: BRAND(500, '#1f1d1b'), color: TOKEN('brandInk', '#ffffff'),
10656
- });
10657
- const secondary = button('Our work', {
10658
- backgroundColor: 'transparent', color: TOKEN('ink', '#1f1d1b'),
10659
- borderWidth: '1px', borderStyle: 'solid', borderColor: TOKEN('ink', '#1f1d1b'),
10681
+ let root = null;
10682
+ switch (style.group) {
10683
+ case 'Buttons':
10684
+ root = button(id, copy.label, createConfig);
10685
+ break;
10686
+ case 'Headings':
10687
+ if (id === 'eyebrow') {
10688
+ root = text(copy.eyebrow, createConfig, id, {});
10689
+ }
10690
+ else {
10691
+ const heading = wearing(createConfig('Heading'), id);
10692
+ if (heading) {
10693
+ setTrait(heading, 'content', copy.heading);
10694
+ setTrait(heading, 'level', id === 'display' ? 'h1' : id);
10695
+ styled(heading, { fontSize: HEADING_SPECIMEN_SIZE[id] ?? '18px', margin: '0' });
10696
+ }
10697
+ root = heading;
10698
+ }
10699
+ break;
10700
+ case 'Text':
10701
+ if (id === 'link') {
10702
+ const link = wearing(createConfig('Link'), id);
10703
+ if (link) {
10704
+ setTrait(link, 'label', copy.link);
10705
+ setTrait(link, 'url', '');
10706
+ }
10707
+ root = link;
10708
+ }
10709
+ else {
10710
+ root = text(copy.body, createConfig, id, { margin: '0' });
10711
+ }
10712
+ break;
10713
+ default:
10714
+ root = layoutSpecimen(id, createConfig, copy);
10715
+ }
10716
+ return markSpecimen(root);
10717
+ }
10718
+ function layoutSpecimen(id, createConfig, copy) {
10719
+ switch (id) {
10720
+ case 'section':
10721
+ case 'box':
10722
+ return text(copy.note, createConfig, 'small', { margin: '0' });
10723
+ case 'card':
10724
+ case 'panel':
10725
+ case 'dialog':
10726
+ return boxed(id, createConfig, [text(copy.label, createConfig, 'small', { margin: '0' })], { padding: '10px 12px', display: 'inline-block', minWidth: '120px', width: 'auto', maxWidth: '100%' });
10727
+ case 'nav':
10728
+ return boxed(id, createConfig, [
10729
+ text(copy.brand, createConfig, undefined, { fontWeight: '700', fontSize: '11px', margin: '0' }),
10730
+ text(copy.navLink, createConfig, undefined, { fontSize: '11px', margin: '0' }),
10731
+ ], { padding: '6px 10px', display: 'flex', gap: '10px', alignItems: 'center' });
10732
+ case 'media':
10733
+ return styled(wearing(createConfig('Image'), id), { width: '80px', aspectRatio: '4 / 3', height: 'auto' });
10734
+ case 'rule':
10735
+ return styled(wearing(createConfig('Divider'), id), { width: '120px' });
10736
+ case 'icon': {
10737
+ const icon = wearing(createConfig('Icon'), id);
10738
+ if (icon)
10739
+ setTrait(icon, 'iconClass', 'ph-thin ph-sparkle');
10740
+ return styled(icon, { fontSize: '20px', display: 'inline-flex' });
10741
+ }
10742
+ default:
10743
+ return boxed(id, createConfig, [text(copy.label, createConfig, undefined, { fontSize: '12px', margin: '0' })], { padding: '6px 10px' });
10744
+ }
10745
+ }
10746
+ /**
10747
+ * A look's tile (Board 6, Look tab — the board's `TILE`): four token dots, a Primary button and a
10748
+ * Card holding "Aa" in the display face beside the look's name. Every colour is a token reference,
10749
+ * so the tile wears whichever look's tokens its wrapper declares.
10750
+ */
10751
+ function buildFlavorSpecimen(name, createConfig, copy) {
10752
+ const dots = styled(createConfig('Container'), { display: 'flex', gap: '4px', alignSelf: 'start', width: 'auto' });
10753
+ const dotNodes = TILE_DOTS.map(token => styled(createConfig('Container'), {
10754
+ width: '14px', height: '14px', borderRadius: '50%', backgroundColor: `var(--iox-${token})`,
10755
+ borderWidth: '1px', borderStyle: 'solid', borderColor: 'rgba(127, 127, 127, 0.25)', flexShrink: '0',
10756
+ }));
10757
+ const primary = button('button-primary', copy.bookNow, createConfig, { padding: '4px 9px', fontSize: '11px', justifySelf: 'end' });
10758
+ const aa = text('Aa', createConfig, undefined, {
10759
+ fontFamily: 'var(--iox-font-display)', fontWeight: 'var(--iox-weight-display)', fontSize: '20px',
10760
+ letterSpacing: 'var(--iox-tracking-display)', lineHeight: '1', color: 'var(--iox-text)', margin: '0',
10660
10761
  });
10661
- const buttonsRow = styled(createConfig('Container'), { display: 'flex', gap: '8px' });
10662
- if (!badge || !heading || !primary || !secondary || !buttonsRow || !hero)
10663
- return null;
10664
- buttonsRow.children = [primary, secondary];
10665
- hero.children = [badge, heading, buttonsRow];
10666
- // The F1 board's hero/cards separator — a 1px Container, because the border-inline traits
10667
- // do not exist in the vocabulary and a physical border side would break RTL anyway.
10668
- const divider = styled(createConfig('Container'), {
10669
- width: '1px', backgroundColor: TOKEN('line', '#e6e3e1'),
10762
+ const label = text(name, createConfig, 'small', { margin: '0' });
10763
+ const card = boxed('card', createConfig, [aa, label], {
10764
+ gridColumn: '1 / -1', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
10765
+ gap: '10px', padding: '7px 9px', width: 'auto',
10670
10766
  });
10671
- const cards = styled(createConfig('Container'), {
10672
- display: 'grid', gridTemplateColumns: 'repeat(2, 150px)', gap: '12px', alignContent: 'center',
10767
+ const root = styled(createConfig('Container'), {
10768
+ display: 'grid', gridTemplateColumns: '1fr auto', gap: '6px', alignContent: 'space-between',
10769
+ height: '96px', padding: '10px', width: '100%',
10673
10770
  });
10674
- const cardA = sampleCard('Kitchen, Jaffa', '2025', createConfig);
10675
- const cardB = sampleCard('Loft, Florentin', '2024', createConfig);
10676
- if (!divider || !cards || !cardA || !cardB)
10677
- return null;
10678
- cards.children = [cardA, cardB];
10679
- root.children = [hero, divider, cards];
10680
- return root;
10771
+ if (root)
10772
+ root.label = 'Look specimen';
10773
+ return markSpecimen(withChildren(root, [withChildren(dots, dotNodes), primary, card]));
10681
10774
  }
10682
10775
 
10683
10776
  /**
@@ -11551,5 +11644,5 @@ class BuilderSpacerComponentConfig extends ComponentConfig {
11551
11644
  * Generated bundle index. Do not edit.
11552
11645
  */
11553
11646
 
11554
- export { ACTION_TYPE_OPTIONS, AuditCode, AuditPanelComponent, AuditSeverity, BODY_CRUMB_LABEL, BREAKPOINT_PRESENTATION, BUILDER_CANVAS_CONTAINER, BUILDER_CHROME_EN, BUILDER_FONT_LINK_ATTR, BUILDER_TOKEN_SCOPE_CLASS, BackgroundGroupStyleConfig, BorderGroupStyleConfig, BreadcrumbBarComponent, BuilderButtonBlockComponent, BuilderButtonComponentConfig, BuilderClipboardService, BuilderComponent, BuilderContainerComponent, BuilderContainerComponentConfig, BuilderDataSourceService, BuilderDialogComponent, BuilderDividerComponentConfig, BuilderHeadingComponentConfig, BuilderHistoryService, BuilderIconComponentConfig, BuilderImageComponentConfig, BuilderLinkComponentConfig, BuilderLinkedContainerComponent, BuilderLinkedContainerConfig, BuilderListComponentConfig, BuilderListItemComponentConfig, BuilderMode, BuilderRepeaterComponent, BuilderSpacerComponentConfig, ButtonBlockComponentConfig, CANVAS_EMPTY_IMAGE_LABEL_EN, CANVAS_EMPTY_IMAGE_LABEL_KEY, CANVAS_ROOT_RESET_CSS, CardComponentConfig, ChildLayoutGroupStyleConfig, ComponentConfig, ComponentRegistryService, DEFAULT_ESSENTIAL_TRAITS, DRAG_HANDLE_ATTR, DataSourceRegistryService, DataSourceType, DialogComponentConfig, DimensionsGroupStyleConfig, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, EDITOR_CHROME_ATTRIBUTE, ESSENTIAL_TRAITS_BY_TYPE, EffectsGroupStyleConfig, FIELD_KIND_PRESETS, FIELD_PARTS, FORM_PRESETS, FORM_TYPE, FieldComponentConfig, FieldGroupComponentConfig, FormComponentConfig, FormErrorComponentConfig, FormStateService, FormSuccessComponentConfig, GalleryConfig, GroupStyleConfig, INTERACTION_STATES, INVERSE_ACTION, IOX_BUILDER_CANVAS, IOX_BUILDER_LABELS, IOX_DATA_SOURCE_ENDPOINTS, InteractionEngineService, InteractionPresetRegistryService, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LAYOUT_PRESETS, LayerTreeComponent, LayoutGroupStyleConfig, ListGroupStyleConfig, NodeAction, OverlayComponent, OverlayService, PALETTE_PRESET_DEFS, PanelChildComponent, PanelComponent, PanelEventService, PanelEventTypes, PanelTypes, PositionGroupStyleConfig, PresetRegistryService, RELATIONAL_STATES, RELATIONAL_STATE_BASE, ROUTE_ANIMATION_OPTIONS, RenderDirective, RepeaterComponentConfig, SCREEN_WIDTH_OPTIONS, STRUCTURAL_STATES, SUPPORTED_STATES, SectionComponent, SectionComponentConfig, SizeGroupStyleConfig, SliderContainerComponent, SliderContainerConfig, SliderSlideComponent, SliderSlideConfig, SpacingGroupStyleConfig, StyleCategory, StyleRegistryService, TraitConfig as StyleTraitConfig, SubmitButtonComponentConfig, SymbolRegistryService, TARGETED_STATES, TARGETED_STATE_BASE, TRIGGER_OPTIONS, TextBlockComponentConfig, ToolbarAction, ToolbarComponent, TraitConfig, TraitInputType, TypographyGroupStyleConfig, UNITS_ALL, UNITS_DEG, UNITS_FIXED, UNITS_NO_VW, VALIDATION_STATES, VALIDATION_STATE_CLASS, ViewportService, ZOOM_OPTIONS, arrayItemSchemaFields, arraySchemaFields, auditNodes, buildConsentPreset, buildFieldKindPreset, buildFlavorSampler, buildFormPreset, buildFullStyleTraits, buildLayoutPreset, buildPalettePreset, buildPresetStyleTraits, canFluidise, canvasEmptyImageLabel, collapseColumnsProps, columnCountOf, defaultBuilderLabels, defaultPageSettings, dropTargetLabel, essentialTraitsFor, findNodeLocation, findParentOf, flattenSchemaFields, fluidiseProps, generateNodeId, isHiddenAt, matchesControlSearch, nearestFormNode, nodeAncestorPath, nodeAncestorPathInEither, reorderProps, resolveInsertTarget, resolveTraitControllerType, resolveTraitOptions, setStyleTrait, setTrait, stackChildrenProps, summarise, syncEmptyImagePlaceholder, syncNodeDesign, textNode, visibilityProps, wrapNode };
11647
+ export { ACTION_TYPE_OPTIONS, AuditCode, AuditPanelComponent, AuditSeverity, BODY_CRUMB_LABEL, BREAKPOINT_PRESENTATION, BUILDER_CANVAS_CONTAINER, BUILDER_CHROME_EN, BUILDER_FONT_LINK_ATTR, BUILDER_TOKEN_SCOPE_CLASS, BackgroundGroupStyleConfig, BorderGroupStyleConfig, BreadcrumbBarComponent, BuilderButtonBlockComponent, BuilderButtonComponentConfig, BuilderClipboardService, BuilderComponent, BuilderContainerComponent, BuilderContainerComponentConfig, BuilderDataSourceService, BuilderDialogComponent, BuilderDividerComponentConfig, BuilderHeadingComponentConfig, BuilderHistoryService, BuilderIconComponentConfig, BuilderImageComponentConfig, BuilderLinkComponentConfig, BuilderLinkedContainerComponent, BuilderLinkedContainerConfig, BuilderListComponentConfig, BuilderListItemComponentConfig, BuilderMode, BuilderRepeaterComponent, BuilderSpacerComponentConfig, ButtonBlockComponentConfig, CANVAS_EMPTY_IMAGE_LABEL_EN, CANVAS_EMPTY_IMAGE_LABEL_KEY, CANVAS_ROOT_RESET_CSS, CardComponentConfig, ChildLayoutGroupStyleConfig, ComponentConfig, ComponentRegistryService, DEFAULT_ESSENTIAL_TRAITS, DRAG_HANDLE_ATTR, DataSourceRegistryService, DataSourceType, DialogComponentConfig, DimensionsGroupStyleConfig, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, EDITOR_CHROME_ATTRIBUTE, ESSENTIAL_TRAITS_BY_TYPE, EffectsGroupStyleConfig, FIELD_KIND_PRESETS, FIELD_PARTS, FORM_PRESETS, FORM_TYPE, FieldComponentConfig, FieldGroupComponentConfig, FormComponentConfig, FormErrorComponentConfig, FormStateService, FormSuccessComponentConfig, GalleryConfig, GroupStyleConfig, INTERACTION_STATES, INVERSE_ACTION, IOX_BUILDER_CANVAS, IOX_BUILDER_LABELS, IOX_DATA_SOURCE_ENDPOINTS, InteractionEngineService, InteractionPresetRegistryService, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LAYOUT_PRESETS, LayerTreeComponent, LayoutGroupStyleConfig, ListGroupStyleConfig, NodeAction, OverlayComponent, OverlayService, PALETTE_PRESET_DEFS, PanelChildComponent, PanelComponent, PanelEventService, PanelEventTypes, PanelTypes, PositionGroupStyleConfig, PresetRegistryService, RELATIONAL_STATES, RELATIONAL_STATE_BASE, ROUTE_ANIMATION_OPTIONS, RenderDirective, RepeaterComponentConfig, SCREEN_WIDTH_OPTIONS, STRUCTURAL_STATES, SUPPORTED_STATES, SectionComponent, SectionComponentConfig, SizeGroupStyleConfig, SliderContainerComponent, SliderContainerConfig, SliderSlideComponent, SliderSlideConfig, SpacingGroupStyleConfig, StyleCategory, StyleRegistryService, TraitConfig as StyleTraitConfig, SubmitButtonComponentConfig, SymbolRegistryService, TARGETED_STATES, TARGETED_STATE_BASE, TRIGGER_OPTIONS, TextBlockComponentConfig, ToolbarAction, ToolbarComponent, TraitConfig, TraitInputType, TypographyGroupStyleConfig, UNITS_ALL, UNITS_DEG, UNITS_FIXED, UNITS_NO_VW, VALIDATION_STATES, VALIDATION_STATE_CLASS, ViewportService, ZOOM_OPTIONS, arrayItemSchemaFields, arraySchemaFields, auditNodes, buildConsentPreset, buildElementStyleSpecimen, buildFieldKindPreset, buildFlavorSpecimen, buildFormPreset, buildFullStyleTraits, buildLayoutPreset, buildPalettePreset, buildPresetStyleTraits, canFluidise, canvasEmptyImageLabel, collapseColumnsProps, columnCountOf, defaultBuilderLabels, defaultPageSettings, dropTargetLabel, essentialTraitsFor, findNodeLocation, findParentOf, flattenSchemaFields, fluidiseProps, generateNodeId, isHiddenAt, isSpecimen, markSpecimen, matchesControlSearch, nearestFormNode, nodeAncestorPath, nodeAncestorPathInEither, reorderProps, resolveInsertTarget, resolveTraitControllerType, resolveTraitOptions, setStyleTrait, setTrait, stackChildrenProps, summarise, syncEmptyImagePlaceholder, syncNodeDesign, textNode, visibilityProps, wrapNode };
11555
11648
  //# sourceMappingURL=vectoriox-iox-builder.mjs.map