@vectoriox/iox-builder 4.27.0 → 4.28.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.
@@ -21,7 +21,7 @@ export { ComponentInstanceRegistryService, VIRTUAL_TRAIT_KEYS, composeVirtualTra
21
21
  import Lenis from 'lenis';
22
22
  import { Subject, BehaviorSubject, of, forkJoin } from 'rxjs';
23
23
  import interact from 'interactjs';
24
- import { BEHAVIOUR_ACTION_TYPES } from '@vectoriox/iox-ui/actions-core';
24
+ import { BEHAVIOUR_ACTION_TYPES, CONSENT_ACTION_TYPES } from '@vectoriox/iox-ui/actions-core';
25
25
  import Moveable from 'moveable';
26
26
  import Selecto from 'selecto';
27
27
  import { map, catchError } from 'rxjs/operators';
@@ -917,6 +917,9 @@ var PanelEventTypes;
917
917
  * would (`string` component type, or a block / symbol object). The builder resolves WHERE from
918
918
  * the current selection — see `resolveInsertTarget`. */
919
919
  PanelEventTypes["INSERT_COMPONENT"] = "insertcomponent";
920
+ /** Insert prebuilt node(s) as GLOBAL elements (data = ComponentNode[]) — the consent preset's
921
+ * path (D-C5: site-wide by construction). Mirrors the MakeGlobal toolbar action. */
922
+ PanelEventTypes["INSERT_GLOBAL"] = "insertglobal";
920
923
  /** A panel wants to select a NODE it already has a reference to (e.g. the Form's nested field
921
924
  * list). Carries `{ node }`. `OverlayService.setSelect()` alone only updates the overlay's own
922
925
  * visual state — `selectedItem`/`selectedPath` (breadcrumb) and every other panel's idea of
@@ -3278,6 +3281,13 @@ class InteractionEngineService {
3278
3281
  this.executeBehaviourAction(node, action, selfEl);
3279
3282
  continue;
3280
3283
  }
3284
+ // Consent actions (C3). Recording ones SIMULATE — the canvas is the ADMIN's browser,
3285
+ // and an admin's cookie is not a visitor's decision; the toast says what would be
3286
+ // recorded. Opening the preferences dialog is dialog-opening and honestly RUNS.
3287
+ if (CONSENT_ACTION_TYPES.has(action.type)) {
3288
+ this.executeConsentAction(action);
3289
+ continue;
3290
+ }
3281
3291
  // Context actions write STATE, not animation. `selfEl` is deliberate: $item is read by
3282
3292
  // walking up from the element actually hovered/clicked, which for a repeater PREVIEW ROW
3283
3293
  // is the clone — so each row publishes its own item, exactly as on the live site.
@@ -3349,6 +3359,15 @@ class InteractionEngineService {
3349
3359
  }
3350
3360
  }
3351
3361
  }
3362
+ executeConsentAction(action) {
3363
+ if (action.type === 'openConsentPreferences') {
3364
+ const dialog = action.target && action.target !== 'self' ? this.instanceRegistry.get(action.target) : null;
3365
+ dialog?.open?.();
3366
+ return;
3367
+ }
3368
+ const detail = { type: action.type, params: action.params ?? {}, target: action.target };
3369
+ window.dispatchEvent(new CustomEvent('iox-action-simulated', { detail }));
3370
+ }
3352
3371
  executeDialogAction(action) {
3353
3372
  const dialog = action.target && action.target !== 'self' ? this.instanceRegistry.get(action.target) : null;
3354
3373
  if (!dialog)
@@ -7267,6 +7286,23 @@ class BuilderComponent {
7267
7286
  this.insertFromPanel(panelEvent.data);
7268
7287
  return;
7269
7288
  }
7289
+ // Prebuilt roots straight into the GLOBAL elements (the consent preset, D-C5) — the same
7290
+ // moves as the MakeGlobal toolbar action, minus the remove-from-layout step.
7291
+ if (panelEvent.type === PanelEventTypes.INSERT_GLOBAL) {
7292
+ const nodes = (Array.isArray(panelEvent.data) ? panelEvent.data : [panelEvent.data]);
7293
+ for (const node of nodes) {
7294
+ if (!node)
7295
+ continue;
7296
+ this.autoLabel(node);
7297
+ node.isGlobal = true;
7298
+ node.globalPosition = node.globalPosition ?? 'after';
7299
+ this.globalElements.push(node);
7300
+ }
7301
+ this.globalElementsChange.emit([...this.globalElements]);
7302
+ this.cdr.markForCheck();
7303
+ this.panelEventService.emit(PanelEventTypes.LAYOUT_CHANGED, this.layout);
7304
+ return;
7305
+ }
7270
7306
  // A panel (e.g. the Form's nested field list) wants to select a node it already holds a
7271
7307
  // reference to — same path onTreeNodeSelect/onCrumbSelect use, so selectedItem/selectedPath
7272
7308
  // and every panel stay in sync, not just the overlay's own visual selection box.
@@ -9920,6 +9956,120 @@ function setTrait(node, name, value) {
9920
9956
  trait.default = value;
9921
9957
  }
9922
9958
 
9959
+ /**
9960
+ * THE CONSENT PRESET (consent plan C3) — the banner as builder elements, which is the whole bet:
9961
+ * Wix ships a banner you cannot design, Webflow ships a design system and no banner; ours is a
9962
+ * Dialog made of ordinary nodes, so it arrives flavored, stylable, translatable and RTL-correct
9963
+ * like everything else on the canvas.
9964
+ *
9965
+ * Three roots, inserted as GLOBAL elements (D-C5 — site-wide by construction):
9966
+ * 1. the banner Dialog (`consentRole` — the live runtime opens it when `needsPrompt` says so),
9967
+ * 2. the preferences Dialog (per-category Allow/Refuse, recorded immediately — state-free),
9968
+ * 3. a small "Cookie settings" element — the withdrawal path (§7.3), free once actions exist.
9969
+ *
9970
+ * Design rules carried in the tree (§7): Accept and Reject are the SAME component with the same
9971
+ * defaults on the same row — reject is as easy as accept; the three categories are named on the
9972
+ * first layer; `closeOnBackdrop` is OFF — clicking away is not consent; the X stays, because the
9973
+ * live runtime records a close-without-decision as a "no".
9974
+ *
9975
+ * The TEXTS are the customer's (D-C7). These strings are neutral mechanism-microcopy seeded for
9976
+ * editing — never policy language, which we do not write (§8).
9977
+ */
9978
+ const click = (actions) => ({
9979
+ id: generateNodeId(),
9980
+ trigger: 'click',
9981
+ actions: actions.map(a => ({
9982
+ target: 'self', duration: 0, delay: 0, easing: 'ease', ...a,
9983
+ })),
9984
+ });
9985
+ function button(label, createConfig, interaction) {
9986
+ const btn = createConfig('ButtonBlock');
9987
+ const text = textNode(label, createConfig);
9988
+ if (!btn || !text)
9989
+ return null;
9990
+ setTrait(btn, 'tag', 'button');
9991
+ btn.children = [text];
9992
+ btn.interactions = [interaction];
9993
+ return btn;
9994
+ }
9995
+ function row(createConfig, styles, children) {
9996
+ const container = createConfig('Container');
9997
+ if (!container || children.some(c => !c))
9998
+ return null;
9999
+ for (const [prop, value] of Object.entries(styles))
10000
+ setStyleTrait(container, prop, value);
10001
+ container.children = children;
10002
+ return container;
10003
+ }
10004
+ /** Build the three roots. Null when any type is missing — a half-built banner never ships. */
10005
+ function buildConsentPreset(createConfig) {
10006
+ const banner = createConfig('Dialog');
10007
+ const preferences = createConfig('Dialog');
10008
+ if (!banner || !preferences)
10009
+ return null;
10010
+ // ── the banner ────────────────────────────────────────────────────────────
10011
+ banner.label = 'Consent banner';
10012
+ setTrait(banner, 'consentRole', true);
10013
+ setTrait(banner, 'closeOnBackdrop', false); // clicking away is NOT consent
10014
+ setTrait(banner, 'showCloseButton', true); // the runtime records X as a "no"
10015
+ setStyleTrait(banner, 'width', 'min(560px, calc(100vw - 32px))');
10016
+ setStyleTrait(banner, 'margin', 'auto auto 24px auto'); // bottom-of-viewport placement, restylable
10017
+ const heading = textNode('Privacy choices', createConfig);
10018
+ if (heading)
10019
+ setStyleTrait(heading, 'fontWeight', '600');
10020
+ const body = textNode('We use cookies to understand how this site is used. You choose what we may use, and you can change your mind at any time.', createConfig);
10021
+ const categoriesLine = textNode('Necessary — always on · Analytics · Marketing', createConfig);
10022
+ const accept = button('Accept all', createConfig, click([
10023
+ { type: 'grantConsent' },
10024
+ { type: 'closeDialog', target: banner.id },
10025
+ ]));
10026
+ const reject = button('Reject all', createConfig, click([
10027
+ { type: 'denyConsent' },
10028
+ { type: 'closeDialog', target: banner.id },
10029
+ ]));
10030
+ const prefs = button('Preferences', createConfig, click([
10031
+ { type: 'openConsentPreferences', target: preferences.id },
10032
+ ]));
10033
+ const buttonsRow = row(createConfig, { display: 'flex', gap: '12px', flexWrap: 'wrap', alignItems: 'center' }, [accept, reject, prefs]);
10034
+ if (!heading || !body || !categoriesLine || !buttonsRow)
10035
+ return null;
10036
+ banner.children = [heading, body, categoriesLine, buttonsRow];
10037
+ // ── the preferences modal ────────────────────────────────────────────────
10038
+ preferences.label = 'Consent preferences';
10039
+ setTrait(preferences, 'closeOnBackdrop', true);
10040
+ setTrait(preferences, 'showCloseButton', true);
10041
+ const prefsHeading = textNode('Your privacy choices', createConfig);
10042
+ if (prefsHeading)
10043
+ setStyleTrait(prefsHeading, 'fontWeight', '600');
10044
+ const categoryRow = (category, name) => {
10045
+ const allow = button('Allow', createConfig, click([
10046
+ { type: 'setConsentCategory', params: { category, granted: true } },
10047
+ ]));
10048
+ const refuse = button('Refuse', createConfig, click([
10049
+ { type: 'setConsentCategory', params: { category, granted: false } },
10050
+ ]));
10051
+ const pair = row(createConfig, { display: 'flex', gap: '8px' }, [allow, refuse]);
10052
+ return row(createConfig, { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '12px', flexWrap: 'wrap' }, [textNode(name, createConfig), pair]);
10053
+ };
10054
+ const necessaryLine = textNode('Necessary — always on', createConfig);
10055
+ const analyticsRow = categoryRow('analytics', 'Analytics');
10056
+ const marketingRow = categoryRow('marketing', 'Marketing');
10057
+ const done = button('Done', createConfig, click([
10058
+ { type: 'closeDialog', target: preferences.id },
10059
+ ]));
10060
+ if (!prefsHeading || !necessaryLine || !analyticsRow || !marketingRow || !done)
10061
+ return null;
10062
+ preferences.children = [prefsHeading, necessaryLine, analyticsRow, marketingRow, done];
10063
+ // ── the withdrawal path (§7.3) ───────────────────────────────────────────
10064
+ const withdrawal = button('Cookie settings', createConfig, click([
10065
+ { type: 'openConsentPreferences', target: preferences.id },
10066
+ ]));
10067
+ if (!withdrawal)
10068
+ return null;
10069
+ withdrawal.label = 'Cookie settings';
10070
+ return { banner, preferences, withdrawal };
10071
+ }
10072
+
9923
10073
  /**
9924
10074
  * Dialog (actions plan A1b) — an ordinary container whose children are normal nodes, rendered
9925
10075
  * on the live site as a real `<dialog>` (top layer, free focus trap/Esc) and in the CANVAS as a
@@ -9934,10 +10084,14 @@ class DialogComponentConfig extends ComponentConfig {
9934
10084
  this.inputs = {
9935
10085
  closeOnBackdrop: 'checkbox',
9936
10086
  showCloseButton: 'checkbox',
10087
+ consentRole: 'checkbox',
9937
10088
  };
9938
10089
  this.traits = [
9939
10090
  new TraitConfig('closeOnBackdrop', 'Close on backdrop', TraitInputType.Checkbox, undefined, true),
9940
10091
  new TraitConfig('showCloseButton', 'Close button', TraitInputType.Checkbox, undefined, true),
10092
+ // C3: the live runtime opens THIS dialog when consent is owed (needsPrompt), and a
10093
+ // close without a decision records a "no". The canvas never auto-opens anything.
10094
+ new TraitConfig('consentRole', 'Consent banner role', TraitInputType.Checkbox, undefined, false),
9941
10095
  ];
9942
10096
  this.styleTraits = buildFullStyleTraits();
9943
10097
  this.applyStyleDefaults({
@@ -10766,5 +10920,5 @@ class BuilderSpacerComponentConfig extends ComponentConfig {
10766
10920
  * Generated bundle index. Do not edit.
10767
10921
  */
10768
10922
 
10769
- export { ACTION_TYPE_OPTIONS, AuditCode, AuditPanelComponent, AuditSeverity, BODY_CRUMB_LABEL, BREAKPOINT_PRESENTATION, BUILDER_CANVAS_CONTAINER, BUILDER_CHROME_EN, 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, CardComponentConfig, ChildLayoutGroupStyleConfig, ComponentConfig, ComponentRegistryService, DEFAULT_ESSENTIAL_TRAITS, DRAG_HANDLE_ATTR, DataSourceRegistryService, DataSourceType, DialogComponentConfig, DimensionsGroupStyleConfig, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, ESSENTIAL_TRAITS_BY_TYPE, EffectsGroupStyleConfig, FIELD_PARTS, FORM_PRESETS, FORM_TYPE, FieldComponentConfig, FieldGroupComponentConfig, FormComponentConfig, FormErrorComponentConfig, FormStateService, FormSuccessComponentConfig, GalleryConfig, GroupStyleConfig, INTERACTION_STATES, INVERSE_ACTION, IOX_BUILDER_LABELS, IOX_DATA_SOURCE_ENDPOINTS, IOX_FONT_MANAGER, InteractionEngineService, InteractionPresetRegistryService, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LAYOUT_PRESETS, LayerTreeComponent, LayoutGroupStyleConfig, ListGroupStyleConfig, NodeAction, OverlayComponent, OverlayService, 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, buildFormPreset, buildFullStyleTraits, buildLayoutPreset, buildPresetStyleTraits, canFluidise, collapseColumnsProps, columnCountOf, defaultBuilderLabels, defaultPageSettings, dropTargetLabel, essentialTraitsFor, findNodeLocation, findParentOf, flattenSchemaFields, fluidiseProps, generateNodeId, isHiddenAt, matchesControlSearch, nearestFormNode, nodeAncestorPath, nodeAncestorPathInEither, reorderProps, resolveInsertTarget, resolveTraitControllerType, resolveTraitOptions, setStyleTrait, stackChildrenProps, summarise, visibilityProps, wrapNode };
10923
+ export { ACTION_TYPE_OPTIONS, AuditCode, AuditPanelComponent, AuditSeverity, BODY_CRUMB_LABEL, BREAKPOINT_PRESENTATION, BUILDER_CANVAS_CONTAINER, BUILDER_CHROME_EN, 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, CardComponentConfig, ChildLayoutGroupStyleConfig, ComponentConfig, ComponentRegistryService, DEFAULT_ESSENTIAL_TRAITS, DRAG_HANDLE_ATTR, DataSourceRegistryService, DataSourceType, DialogComponentConfig, DimensionsGroupStyleConfig, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, ESSENTIAL_TRAITS_BY_TYPE, EffectsGroupStyleConfig, FIELD_PARTS, FORM_PRESETS, FORM_TYPE, FieldComponentConfig, FieldGroupComponentConfig, FormComponentConfig, FormErrorComponentConfig, FormStateService, FormSuccessComponentConfig, GalleryConfig, GroupStyleConfig, INTERACTION_STATES, INVERSE_ACTION, IOX_BUILDER_LABELS, IOX_DATA_SOURCE_ENDPOINTS, IOX_FONT_MANAGER, InteractionEngineService, InteractionPresetRegistryService, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LAYOUT_PRESETS, LayerTreeComponent, LayoutGroupStyleConfig, ListGroupStyleConfig, NodeAction, OverlayComponent, OverlayService, 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, buildFormPreset, buildFullStyleTraits, buildLayoutPreset, buildPresetStyleTraits, canFluidise, collapseColumnsProps, columnCountOf, defaultBuilderLabels, defaultPageSettings, dropTargetLabel, essentialTraitsFor, findNodeLocation, findParentOf, flattenSchemaFields, fluidiseProps, generateNodeId, isHiddenAt, matchesControlSearch, nearestFormNode, nodeAncestorPath, nodeAncestorPathInEither, reorderProps, resolveInsertTarget, resolveTraitControllerType, resolveTraitOptions, setStyleTrait, setTrait, stackChildrenProps, summarise, textNode, visibilityProps, wrapNode };
10770
10924
  //# sourceMappingURL=vectoriox-iox-builder.mjs.map