@vectoriox/iox-builder 5.0.0 → 5.1.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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, inject, NgZone, EventEmitter, createComponent, Output, Input, Inject, Directive, ChangeDetectionStrategy, Component, InjectionToken, ViewChild, effect, Optional, ContentChildren, NO_ERRORS_SCHEMA, NgModule } from '@angular/core';
2
+ import { Injectable, inject, NgZone, InjectionToken, EventEmitter, createComponent, Output, Input, Inject, Directive, ChangeDetectionStrategy, Component, ViewChild, effect, Optional, ContentChildren, NO_ERRORS_SCHEMA, NgModule } from '@angular/core';
3
3
  import * as i2 from '@angular/common';
4
4
  import { DOCUMENT, CommonModule } from '@angular/common';
5
5
  import * as i16 from '@angular/forms';
@@ -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, Breakpoint, BREAKPOINTS, countOverrides, setOverride, defaultElementStyleFor, applyElementStyle, stripSuppressedProps, DeclarationContext, buildPartSelector, planResponsiveRules, resolveStyleProps, buildTargetedStateSelector, statePseudoClass, compileTokenCssBlocks, compileDeclarations, wrapInBreakpointQuery, BreakpointQueryKind, breakpointDef, breakpointForWidth, FormRenderState, safeSetInput, ContextStore, ComponentInstanceRegistryService, isActionEnabled, isContextAction, contextActionEffect, resolveContextValue, composeVirtualTraits, stateNamesIn, partClassName, syncElementStyleMarker, 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, compileDeclarations, wrapInBreakpointQuery, BreakpointQueryKind, breakpointDef, breakpointForWidth, FormRenderState, safeSetInput, ContextStore, ComponentInstanceRegistryService, isActionEnabled, isContextAction, contextActionEffect, resolveContextValue, IS_PREVIEW, emptyImageSrc, composeVirtualTraits, withDefaultHoverTransition, stateNamesIn, partClassName, syncNodeDesignClasses, resolveInputs, setInputOverride, collectContextSlotKeys, withContextSlots, compilePageStyle, nodeUsesContext, resolveBoundValue, planDataSource, dataSourcePlanToRequest, normalizeCollectionResult, resolveRepeaterItems, getByPath, computeRelative, IoxPageModule, PALETTE_PRESETS, 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';
@@ -655,6 +655,15 @@ class ListGroupStyleConfig extends GroupStyleConfig {
655
655
  * Every call produces new TraitConfig instances so different component nodes
656
656
  * never share mutable state.
657
657
  */
658
+ /**
659
+ * The section colour scheme (design-system §3c) — a CONTENT trait, stored in `props` and saved with
660
+ * the page, never a style prop: it becomes the class `iox-scheme-<id>` (the shared
661
+ * `nodeDesignClasses`) where the token sheet re-declares the section's tokens. Edited in the Style
662
+ * panel beside the element style; the Bindings panel does not list it.
663
+ */
664
+ function buildColorSchemeTrait() {
665
+ return new TraitConfig(COLOR_SCHEME_TRAIT, 'Section colours', TraitInputType.Select, COLOR_SCHEMES.map(s => ({ label: s.label, value: s.id })), ColorScheme.Default);
666
+ }
658
667
  function buildFullStyleTraits() {
659
668
  return [
660
669
  new PositionGroupStyleConfig(),
@@ -1559,9 +1568,7 @@ class ComponentRegistryService {
1559
1568
  const config = this.createBaseConfig(type);
1560
1569
  if (!config)
1561
1570
  return null;
1562
- const id = defaultElementStyleFor(type, traitValues(config));
1563
- if (id)
1564
- applyElementStyle(config, id);
1571
+ wearDefaultStyles(config);
1565
1572
  return config;
1566
1573
  }
1567
1574
  /**
@@ -1594,6 +1601,16 @@ class ComponentRegistryService {
1594
1601
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: ComponentRegistryService, decorators: [{
1595
1602
  type: Injectable
1596
1603
  }] });
1604
+ /** A new node AND the default children its config builds (a palette Form's fields, submit and
1605
+ * notices) are all new elements: each wears its type's default style. */
1606
+ function wearDefaultStyles(node) {
1607
+ const id = defaultElementStyleFor(node.type, traitValues(node));
1608
+ if (id)
1609
+ applyElementStyle(node, id);
1610
+ for (const child of node.children ?? [])
1611
+ if (child?.type)
1612
+ wearDefaultStyles(child);
1613
+ }
1597
1614
  function traitValues(config) {
1598
1615
  const out = {};
1599
1616
  for (const trait of config.traits ?? [])
@@ -4487,6 +4504,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
4487
4504
  args: [{ providedIn: 'root' }]
4488
4505
  }] });
4489
4506
 
4507
+ /**
4508
+ * Canvas data preview used to come from host-provided content services (IOX_CONTENT_SERVICE /
4509
+ * IOX_SHOP_SERVICE), whose `getContent(contentType, orgId)` hit the ADMIN resources endpoint and
4510
+ * accepted no filters — so the Repeater/Slider ignored every data-source filter. Data now flows
4511
+ * through BuilderDataSourceService + the shared planDataSource, configured with
4512
+ * IOX_DATA_SOURCE_ENDPOINTS. Those tokens are gone; do not reintroduce a fetch that bypasses it.
4513
+ */
4514
+ /** Optional font manager. Provide from the host app to populate the fontFamily dropdown. */
4515
+ const IOX_FONT_MANAGER = new InjectionToken('IOX_FONT_MANAGER');
4516
+ /**
4517
+ * Translates the builder library's OWN chrome.
4518
+ *
4519
+ * The library cannot depend on ngx-translate — that is the host app's choice — but its toolbar was
4520
+ * hardcoded English, which shows through as untranslated controls in a Hebrew/RTL admin. This token
4521
+ * lets the host route those strings through its own i18n while the library keeps working standalone:
4522
+ * unprovided, `defaultBuilderLabels` returns the English text, so nothing ever renders a raw key.
4523
+ *
4524
+ * Keys are namespaced `builder.chrome.*` and listed in `BUILDER_CHROME_EN` below — that object is
4525
+ * the contract. A host providing this token should add those keys to its own bundles.
4526
+ */
4527
+ const IOX_BUILDER_LABELS = new InjectionToken('IOX_BUILDER_LABELS', {
4528
+ providedIn: 'root',
4529
+ factory: () => defaultBuilderLabels,
4530
+ });
4531
+ /** Standalone behaviour: the English text the library ships with. */
4532
+ const defaultBuilderLabels = (_key, fallback) => fallback;
4533
+ /**
4534
+ * Every user-visible string in the builder's own chrome, so a host can copy this into its bundles
4535
+ * in one go rather than discovering keys one missing label at a time.
4536
+ */
4537
+ const BUILDER_CHROME_EN = Object.freeze({
4538
+ 'builder.chrome.select': 'Select',
4539
+ 'builder.chrome.pan': 'Pan',
4540
+ 'builder.chrome.style': 'Style',
4541
+ 'builder.chrome.publish': 'Publish',
4542
+ 'builder.chrome.unpublish': 'Unpublish',
4543
+ 'builder.chrome.breakpoint': 'Breakpoint',
4544
+ 'builder.chrome.screenWidth': 'Screen width',
4545
+ 'builder.chrome.zoom': 'Zoom',
4546
+ 'builder.chrome.audit': 'Responsive audit',
4547
+ 'builder.chrome.auditHint': 'Responsive audit — what is broken at this width',
4548
+ 'builder.chrome.undo': 'Undo',
4549
+ 'builder.chrome.redo': 'Redo',
4550
+ 'builder.chrome.save': 'Save',
4551
+ 'builder.chrome.preview': 'Preview',
4552
+ 'builder.chrome.addImage': 'Add image',
4553
+ });
4554
+
4490
4555
  // Module-level counter — unique drop-list ID per Section instance across the whole app session.
4491
4556
  let dropListCounter = 0;
4492
4557
  /**
@@ -4549,6 +4614,8 @@ class RenderDirective {
4549
4614
  this.onMouseEnter = new EventEmitter();
4550
4615
  this.onMouseLeave = new EventEmitter();
4551
4616
  this.childSubs = [];
4617
+ this.isPreview = inject(IS_PREVIEW, { optional: true }) ?? false;
4618
+ this.labels = inject(IOX_BUILDER_LABELS);
4552
4619
  /** Host element drag was registered on (collapsed path) — unregistered on destroy. */
4553
4620
  this.hostDragEl = null;
4554
4621
  }
@@ -4697,6 +4764,14 @@ class RenderDirective {
4697
4764
  // safeSetInput for every component that does not declare it.
4698
4765
  if (node.responsive)
4699
4766
  safeSetInput(componentRef, 'responsive', node.responsive);
4767
+ // Empty images (DS3): a MARKED Image with no source shows the shared placeholder — the
4768
+ // "Add image" label on the canvas, the bare blank in Preview (IS_PREVIEW), which is what the
4769
+ // live site shows. An unmarked Image gets null and renders exactly as before.
4770
+ if (node.type === 'Image') {
4771
+ const src = node.traits?.find(t => t.name === 'src')?.default;
4772
+ const label = this.isPreview ? null : this.labels('builder.chrome.addImage', 'Add image');
4773
+ safeSetInput(componentRef, 'emptySrc', emptyImageSrc(node, src, label));
4774
+ }
4700
4775
  // Build initial style map from styleTraits defaults.
4701
4776
  const initialStyle = {};
4702
4777
  if (node.styleTraits?.length) {
@@ -4714,7 +4789,10 @@ class RenderDirective {
4714
4789
  // registry strips the props a type's renderer owns (a Dialog's positioning) on every rule.
4715
4790
  if (cssId) {
4716
4791
  this.styleRegistry.setNodeType(cssId, node.type);
4717
- const composedBase = composeVirtualTraits(initialStyle);
4792
+ // The default hover transition (shared, marker-aware — the engine emits the same one):
4793
+ // without it a hovering node's base rule, re-upserted here on every render, dropped the
4794
+ // transition page-ui had added on load, and the canvas snapped where the site eased.
4795
+ const composedBase = composeVirtualTraits(withDefaultHoverTransition(initialStyle, node.stateStyles, node.elementStyle));
4718
4796
  this.styleRegistry.upsert(cssId, composedBase);
4719
4797
  // Restore per-state overrides saved from a previous session.
4720
4798
  // Every state set at base OR at any breakpoint — so a breakpoint-only state (a Mobile-only
@@ -4813,10 +4891,12 @@ class RenderDirective {
4813
4891
  // For layout components the host element is <app-section>; for leaf components
4814
4892
  // it is e.g. <app-card>. Either way this is the correct inspectable element.
4815
4893
  const nativeEl = componentRef.location?.nativeElement ?? null;
4816
- // The element-style marker (design-system §3b-bis) — on the HOST, as both engine paths do:
4817
- // component CSS that carries the system look is scoped to it, so a node saved before DS2
4818
- // keeps its exact pre-DS2 look. Clones carry it too (they render the same look).
4819
- syncElementStyleMarker(nativeEl, node.elementStyle);
4894
+ // The design-system classes — the element-style marker (§3b-bis) and the section colour scheme
4895
+ // (§3c) — on the HOST, as both engine paths do, from the ONE shared helper. Component CSS that
4896
+ // carries the system look is scoped to the marker, so a node saved before DS2 keeps its exact
4897
+ // pre-DS2 look; the scheme class is where the token sheet re-declares the section's tokens.
4898
+ // Clones carry both (they render the same look).
4899
+ syncNodeDesignClasses(nativeEl, node);
4820
4900
  // Custom element host defaults to display:inline. Set it to block so it participates
4821
4901
  // properly in layout and CDK can capture its bounding rect. SKIP for directive-hosts: the
4822
4902
  // host is a real element with its own display, and an inline style here out-ranks both
@@ -5557,53 +5637,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
5557
5637
  type: Output
5558
5638
  }] } });
5559
5639
 
5560
- /**
5561
- * Canvas data preview used to come from host-provided content services (IOX_CONTENT_SERVICE /
5562
- * IOX_SHOP_SERVICE), whose `getContent(contentType, orgId)` hit the ADMIN resources endpoint and
5563
- * accepted no filters — so the Repeater/Slider ignored every data-source filter. Data now flows
5564
- * through BuilderDataSourceService + the shared planDataSource, configured with
5565
- * IOX_DATA_SOURCE_ENDPOINTS. Those tokens are gone; do not reintroduce a fetch that bypasses it.
5566
- */
5567
- /** Optional font manager. Provide from the host app to populate the fontFamily dropdown. */
5568
- const IOX_FONT_MANAGER = new InjectionToken('IOX_FONT_MANAGER');
5569
- /**
5570
- * Translates the builder library's OWN chrome.
5571
- *
5572
- * The library cannot depend on ngx-translate — that is the host app's choice — but its toolbar was
5573
- * hardcoded English, which shows through as untranslated controls in a Hebrew/RTL admin. This token
5574
- * lets the host route those strings through its own i18n while the library keeps working standalone:
5575
- * unprovided, `defaultBuilderLabels` returns the English text, so nothing ever renders a raw key.
5576
- *
5577
- * Keys are namespaced `builder.chrome.*` and listed in `BUILDER_CHROME_EN` below — that object is
5578
- * the contract. A host providing this token should add those keys to its own bundles.
5579
- */
5580
- const IOX_BUILDER_LABELS = new InjectionToken('IOX_BUILDER_LABELS', {
5581
- providedIn: 'root',
5582
- factory: () => defaultBuilderLabels,
5583
- });
5584
- /** Standalone behaviour: the English text the library ships with. */
5585
- const defaultBuilderLabels = (_key, fallback) => fallback;
5586
- /**
5587
- * Every user-visible string in the builder's own chrome, so a host can copy this into its bundles
5588
- * in one go rather than discovering keys one missing label at a time.
5589
- */
5590
- const BUILDER_CHROME_EN = Object.freeze({
5591
- 'builder.chrome.select': 'Select',
5592
- 'builder.chrome.pan': 'Pan',
5593
- 'builder.chrome.style': 'Style',
5594
- 'builder.chrome.publish': 'Publish',
5595
- 'builder.chrome.unpublish': 'Unpublish',
5596
- 'builder.chrome.breakpoint': 'Breakpoint',
5597
- 'builder.chrome.screenWidth': 'Screen width',
5598
- 'builder.chrome.zoom': 'Zoom',
5599
- 'builder.chrome.audit': 'Responsive audit',
5600
- 'builder.chrome.auditHint': 'Responsive audit — what is broken at this width',
5601
- 'builder.chrome.undo': 'Undo',
5602
- 'builder.chrome.redo': 'Redo',
5603
- 'builder.chrome.save': 'Save',
5604
- 'builder.chrome.preview': 'Preview',
5605
- });
5606
-
5607
5640
  class ToolbarComponent {
5608
5641
  get isPublished() {
5609
5642
  return this.pageStatus === 'published';
@@ -6532,7 +6565,7 @@ class BuilderComponent {
6532
6565
  this.initLenis();
6533
6566
  // Background / typography: until this existed the Page panel wrote a background
6534
6567
  // that only appeared once the site was published.
6535
- this.pageStyleCss = compilePageStyle(this.pageSettings?.style);
6568
+ this.pageStyleCss = compilePageStyle(this.pageSettings?.style, { designSystem: this.pageSettings?.designSystem === true });
6536
6569
  }
6537
6570
  if (changes['globalElements']) {
6538
6571
  // The *ngIf on the global overlay renders after this hook — defer so
@@ -10627,6 +10660,7 @@ class BuilderContainerComponentConfig extends ComponentConfig {
10627
10660
  this.children = [];
10628
10661
  this.traits = [
10629
10662
  new TraitConfig('htmlTag', 'HTML Tag', TraitInputType.Select, ['div', 'section', 'article', 'header', 'footer', 'main', 'nav', 'ul', 'ol', 'li'], 'div'),
10663
+ buildColorSchemeTrait(),
10630
10664
  ];
10631
10665
  this.styleTraits = buildFullStyleTraits();
10632
10666
  this.applyStyleDefaults({
@@ -11052,6 +11086,7 @@ class BuilderLinkedContainerConfig extends ComponentConfig {
11052
11086
  { label: 'Parent frame', value: '_parent' },
11053
11087
  { label: 'Full window', value: '_top' },
11054
11088
  ], '_self'),
11089
+ buildColorSchemeTrait(),
11055
11090
  ];
11056
11091
  this.styleTraits = buildFullStyleTraits();
11057
11092
  this.applyStyleDefaults({
@@ -11178,7 +11213,7 @@ class SectionComponentConfig extends ComponentConfig {
11178
11213
  constructor() {
11179
11214
  super('Section', 'app-section', 'ph-thin ph-rows', 'Layout');
11180
11215
  this.children = [];
11181
- this.traits = [];
11216
+ this.traits = [buildColorSchemeTrait()];
11182
11217
  this.styleTraits = buildFullStyleTraits();
11183
11218
  this.applyStyleDefaults({
11184
11219
  width: 'auto',