@vectoriox/iox-ui 5.29.1 → 5.31.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.
Files changed (35) hide show
  1. package/composite-core/index.d.ts +270 -0
  2. package/composite-core/package.json +3 -0
  3. package/design-core/index.d.ts +870 -41
  4. package/fesm2022/vectoriox-iox-ui-composite-core.mjs +698 -0
  5. package/fesm2022/vectoriox-iox-ui-composite-core.mjs.map +1 -0
  6. package/fesm2022/vectoriox-iox-ui-design-core.mjs +1090 -82
  7. package/fesm2022/vectoriox-iox-ui-design-core.mjs.map +1 -1
  8. package/fesm2022/vectoriox-iox-ui-forms-core.mjs +74 -1
  9. package/fesm2022/vectoriox-iox-ui-forms-core.mjs.map +1 -1
  10. package/fesm2022/vectoriox-iox-ui-generation-core.mjs +110 -30
  11. package/fesm2022/vectoriox-iox-ui-generation-core.mjs.map +1 -1
  12. package/fesm2022/vectoriox-iox-ui.mjs +210 -31
  13. package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
  14. package/forms-core/index.d.ts +45 -2
  15. package/generation-core/index.d.ts +35 -23
  16. package/index.d.ts +42 -15
  17. package/package.json +5 -1
  18. package/src/composite-core/badge.ts +103 -0
  19. package/src/composite-core/card.ts +524 -0
  20. package/src/composite-core/composite-node.ts +143 -0
  21. package/src/composite-core/public-api.ts +11 -0
  22. package/src/composite-core/structure.ts +58 -0
  23. package/src/design-core/contrast-audit.ts +38 -5
  24. package/src/design-core/flavor-fixtures.generated.ts +144 -144
  25. package/src/design-core/flavor-spec.generated.ts +970 -26
  26. package/src/design-core/node-classes.ts +16 -4
  27. package/src/design-core/node-links.ts +29 -0
  28. package/src/forms-core/nesting.ts +92 -0
  29. package/src/forms-core/public-api.ts +1 -0
  30. package/src/generation-core/compose-site.ts +55 -1
  31. package/src/generation-core/node-baselines.generated.ts +14 -2
  32. package/src/generation-core/patterns.ts +29 -2
  33. package/src/generation-core/serialized-node.ts +2 -0
  34. package/src/generation-core/site-spec.generated.ts +13 -26
  35. package/styles/iox-design.css +62 -0
@@ -419,5 +419,48 @@ declare const FORM_PRESETS: readonly FormPresetDef[];
419
419
  /** The preset with this id, or undefined. */
420
420
  declare function formPresetById(id: string | null | undefined): FormPresetDef | undefined;
421
421
 
422
- export { ControlType, DEFAULT_EN_MESSAGES, FIELD_NODE_TYPE, FORM_ERROR_KEYS, FORM_NODE_TYPE, FORM_PRESETS, FeedbackTiming, FormPresetId, HONEYPOT_FIELD, IMPLIED_PATTERN, INITIAL_FORM_STATE, MESSAGE_NAMESPACE, MULTI_VALUE_CONTROLS, PATTERN_MESSAGE_KEYS, PATTERN_PRESETS, PATTERN_PRESET_OPTIONS, PatternPreset, RENDERED_AT_FIELD, RULE_DESCRIPTORS, RULE_MESSAGE_KEYS, RuleName, RuleParamKind, STORE_ACTION, addableRules, applyServerErrors, collectFieldNodes, collectFormSpec, deriveFieldName, fieldOptionsOf, firstInvalidField, formPresetById, interpretSubmitResponse, onFieldBlur, onFieldChange, onSubmitAttempt, parseFieldOptions, resetFormState, rulesFor, shouldShowError, submitEndpointPath, uniqueFieldName, validateField, validateForm, withAntiSpamFields };
423
- export type { FieldError, FieldOption, FieldOptionSource, FieldResult, FieldSpec, FormNodeLike, FormPresetDef, FormPresetField, FormResult, FormRuntimeState, FormSpec, FormTransport, RuleDescriptor, SubmitAction, SubmitRequest, SubmitResponse, ValidationRule };
422
+ declare enum InteractiveTag {
423
+ Anchor = "a",
424
+ Button = "button"
425
+ }
426
+ /**
427
+ * Every node type that renders as an interactive element, and the element(s) it can render. A ButtonBlock
428
+ * renders `<a>` or `<button>` by its `tag` trait — both interactive, so switching the tag never lifts the
429
+ * rule. Pinned to the engine components' selectors by `interactive-hosts.spec.ts`.
430
+ */
431
+ declare const INTERACTIVE_HOST_TAGS: Readonly<Record<string, readonly InteractiveTag[]>>;
432
+ declare const FIELD_GROUP_NODE_TYPE = "FieldGroup";
433
+ declare const SUBMIT_BUTTON_NODE_TYPE = "SubmitButton";
434
+ /**
435
+ * Form content — what a link or a button must never hold: a Form (its controls), a Field (an input, select
436
+ * or textarea), a SubmitButton (a `<button>`) and a FieldGroup (it exists only to hold fields, so it moves as
437
+ * one). A Success / Error block is text and lives only inside its form, so it is not on the list.
438
+ */
439
+ declare const FORM_CONTENT_TYPES: ReadonlySet<string>;
440
+ /** The minimum a node must look like — a builder ComponentNode and a saved layout node both do. */
441
+ interface NestingNode {
442
+ id?: string;
443
+ type?: string;
444
+ label?: string;
445
+ children?: NestingNode[] | null;
446
+ }
447
+ declare function isInteractiveHost(node: NestingNode | null | undefined): boolean;
448
+ declare function isFormContent(node: NestingNode | null | undefined): boolean;
449
+ /**
450
+ * The first form-content node in `node`'s subtree that sits inside an interactive host, or null. `insideHost`
451
+ * is true when `node` is placed under a host already — so a Form dropped into a link and a link that holds a
452
+ * Form are the same finding.
453
+ */
454
+ declare function formContentInHost(node: NestingNode | null | undefined, insideHost?: boolean): NestingNode | null;
455
+ /** One place a tree holds form content inside a link or a button. */
456
+ interface MisplacedFormContent<N extends NestingNode = NestingNode> {
457
+ /** The OUTERMOST interactive host holding it. */
458
+ host: N;
459
+ /** A MAXIMAL form-content subtree inside it — the Form, never each of its fields. */
460
+ content: N;
461
+ }
462
+ /** Every form-content subtree a tree holds inside an interactive host, in document order. */
463
+ declare function findFormContentInHosts<N extends NestingNode>(roots: readonly N[] | null | undefined): MisplacedFormContent<N>[];
464
+
465
+ export { ControlType, DEFAULT_EN_MESSAGES, FIELD_GROUP_NODE_TYPE, FIELD_NODE_TYPE, FORM_CONTENT_TYPES, FORM_ERROR_KEYS, FORM_NODE_TYPE, FORM_PRESETS, FeedbackTiming, FormPresetId, HONEYPOT_FIELD, IMPLIED_PATTERN, INITIAL_FORM_STATE, INTERACTIVE_HOST_TAGS, InteractiveTag, MESSAGE_NAMESPACE, MULTI_VALUE_CONTROLS, PATTERN_MESSAGE_KEYS, PATTERN_PRESETS, PATTERN_PRESET_OPTIONS, PatternPreset, RENDERED_AT_FIELD, RULE_DESCRIPTORS, RULE_MESSAGE_KEYS, RuleName, RuleParamKind, STORE_ACTION, SUBMIT_BUTTON_NODE_TYPE, addableRules, applyServerErrors, collectFieldNodes, collectFormSpec, deriveFieldName, fieldOptionsOf, findFormContentInHosts, firstInvalidField, formContentInHost, formPresetById, interpretSubmitResponse, isFormContent, isInteractiveHost, onFieldBlur, onFieldChange, onSubmitAttempt, parseFieldOptions, resetFormState, rulesFor, shouldShowError, submitEndpointPath, uniqueFieldName, validateField, validateForm, withAntiSpamFields };
466
+ export type { FieldError, FieldOption, FieldOptionSource, FieldResult, FieldSpec, FormNodeLike, FormPresetDef, FormPresetField, FormResult, FormRuntimeState, FormSpec, FormTransport, MisplacedFormContent, NestingNode, RuleDescriptor, SubmitAction, SubmitRequest, SubmitResponse, ValidationRule };
@@ -378,30 +378,21 @@ declare const SPEC_SITE_PATTERNS: {
378
378
  };
379
379
  };
380
380
  readonly c: readonly [{
381
- readonly t: "LinkedContainer";
382
- readonly es: "card";
381
+ readonly t: "Container";
382
+ readonly recipe: "card";
383
+ readonly layout: "top";
384
+ readonly parts: {
385
+ readonly badge: false;
386
+ readonly actions: false;
387
+ };
383
388
  readonly href: "/work/:slug";
384
389
  readonly param: "item.slug";
385
- readonly lay: {
386
- readonly display: "flex";
387
- readonly flexDirection: "column";
388
- readonly gap: "12px";
390
+ readonly binds: {
391
+ readonly image: "item.cover";
392
+ readonly eyebrow: "item.year";
393
+ readonly title: "item.title";
394
+ readonly text: "item.summary";
389
395
  };
390
- readonly c: readonly [{
391
- readonly t: "Image";
392
- readonly es: "media";
393
- readonly bind: "item.cover";
394
- readonly aspect: "4 / 3";
395
- }, {
396
- readonly t: "Heading";
397
- readonly es: "h3";
398
- readonly level: "h3";
399
- readonly bind: "item.title";
400
- }, {
401
- readonly t: "Text";
402
- readonly es: "small";
403
- readonly bind: "item.summary";
404
- }];
405
396
  }];
406
397
  }];
407
398
  readonly lay: {
@@ -1169,7 +1160,9 @@ interface SitePlan {
1169
1160
  * per page in the navigation (`$navPages`) · `href` a page route (`$page.route` = the stamped page's) ·
1170
1161
  * `param` the value of the route's `:param` · `optional` a part a page may leave out (`without`) ·
1171
1162
  * `preset` a forms-core form preset (its fields, submit and Success/Error words) · `control` / `label` /
1172
- * `required` a Field's · `c` children.
1163
+ * `required` a Field's · `c` children · `recipe` a composite-core recipe stamped whole (`card`: Card v2 —
1164
+ * its `layout`, the optional `parts` it leaves out, and `binds`, a binding path per part: `image`,
1165
+ * `eyebrow`, `title`, `text`; `href` + `param` link the whole card through its title).
1173
1166
  */
1174
1167
  interface PatternNode {
1175
1168
  readonly t: string;
@@ -1196,7 +1189,24 @@ interface PatternNode {
1196
1189
  readonly label?: string;
1197
1190
  readonly required?: boolean;
1198
1191
  readonly c?: ReadonlyArray<PatternNode>;
1192
+ readonly recipe?: string;
1193
+ readonly layout?: string;
1194
+ readonly parts?: Readonly<Record<string, boolean>>;
1195
+ readonly binds?: Readonly<Record<string, string>>;
1196
+ }
1197
+ /** The composite-core recipes a pattern may stamp (`recipe`). */
1198
+ declare enum PatternRecipe {
1199
+ Card = "card"
1200
+ }
1201
+ /** A recipe node's `binds` keys → the part each binds (Card v2). */
1202
+ declare enum CardBindTarget {
1203
+ Image = "image",
1204
+ Eyebrow = "eyebrow",
1205
+ Title = "title",
1206
+ Text = "text"
1199
1207
  }
1208
+ /** Every binding path a node reads: its own `bind`, and a recipe's `binds`. */
1209
+ declare function nodeBindPaths(node: PatternNode): string[];
1200
1210
  declare enum PatternGlobalPosition {
1201
1211
  Before = "before",
1202
1212
  After = "after"
@@ -1306,6 +1316,8 @@ interface SerializedNode {
1306
1316
  stateStyles?: Partial<Record<string, Record<string, unknown>>>;
1307
1317
  responsive?: SerializedResponsive;
1308
1318
  elementStyle?: string;
1319
+ /** The part this node plays in a composite (composite-core: `card.title`). */
1320
+ role?: string;
1309
1321
  bindings: SerializedBinding[];
1310
1322
  interactions: unknown[];
1311
1323
  isGlobal?: boolean;
@@ -1525,5 +1537,5 @@ declare function parseSampleImageRef(value: unknown): number | null;
1525
1537
  */
1526
1538
  declare function composeSite(plan: SitePlan, context: ComposeContext): ComposedSite;
1527
1539
 
1528
- export { BASELINE_ROLES, BaselineRole, BindScope, ComposeError, ComposeIssueCode, DANGLING_LINK_CODES, FACT_KEYS, GlobalSlot, NEUTRAL_STYLE_PROPS, NODE_BASELINES, PatternDirective, PatternGlobalPosition, PatternPlaceholder, 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, altSourceScopes, assertStyleSlots, assertValidSitePlan, composeSite, createSerializedNode, nextSourceOf, parseSampleImageRef, patternById, patternNeedsBinding, patternNodes, patternOptionalParts, patternScopes, patternSlots, planFromTemplate, releasedLayoutDefaults, requiredShapeKeys, routeParams, sampleImageRef, setStyleProps, shapeKeys, validateSitePlan, walkSerialized };
1540
+ export { BASELINE_ROLES, BaselineRole, BindScope, CardBindTarget, ComposeError, ComposeIssueCode, DANGLING_LINK_CODES, FACT_KEYS, GlobalSlot, NEUTRAL_STYLE_PROPS, NODE_BASELINES, PatternDirective, PatternGlobalPosition, PatternPlaceholder, PatternRecipe, 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, altSourceScopes, assertStyleSlots, assertValidSitePlan, composeSite, createSerializedNode, nextSourceOf, nodeBindPaths, parseSampleImageRef, patternById, patternNeedsBinding, patternNodes, patternOptionalParts, patternScopes, patternSlots, planFromTemplate, releasedLayoutDefaults, requiredShapeKeys, routeParams, sampleImageRef, setStyleProps, shapeKeys, validateSitePlan, walkSerialized };
1529
1541
  export type { CmsDataSourcePlan, ComposeContext, ComposeIssue, ComposedDataSource, ComposedItem, ComposedPage, ComposedPlaceholder, ComposedRoute, ComposedSite, ContentFieldPlan, ContentSeedPlan, ContentTypePlan, CreateNodeOptions, DataSourcePlan, FactKey, NodeBaseline, PagePlan, PageSectionPlan, PatternNode, RelativeDataSourcePlan, SerializedBinding, SerializedNode, SerializedResponsive, SiteBusiness, SitePattern, SitePlan, SitePlanIssue, SitePlanSource, SitePlanValidation, SiteTemplate, TemplatePlanInput };
package/index.d.ts CHANGED
@@ -313,6 +313,19 @@ declare class BuilderButtonComponent implements OnInit {
313
313
  static ɵcmp: i0.ɵɵComponentDeclaration<BuilderButtonComponent, "a[iox-button]", never, { "label": { "alias": "label"; "required": false; }; "link": { "alias": "link"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "nodeId": { "alias": "nodeId"; "required": false; }; "node": { "alias": "node"; "required": false; }; }, {}, never, never, false, never>;
314
314
  }
315
315
 
316
+ /**
317
+ * Where a node links (W1.1). Link and LinkedContainer always had two link types — an external URL or a
318
+ * website PAGE by its route — and ButtonBlock now has the same pair, so a composed "Get in touch" button
319
+ * is a page link the builder shows as `/contact`, not a hand-built URL. Framework-free: the composer
320
+ * (generation-core) writes it, the builder canvas and both engine render paths read it.
321
+ */
322
+ declare enum LinkType {
323
+ /** Links nowhere — a Heading's default; "Link the whole card" (Card v2) turns its title's link on. */
324
+ None = "none",
325
+ External = "external",
326
+ Page = "page"
327
+ }
328
+
316
329
  interface EditableNode$1 {
317
330
  bindings?: Array<{
318
331
  prop: string;
@@ -328,16 +341,35 @@ interface EditableNode$1 {
328
341
  * (engine: `createComponent({ hostElement: document.createElement(level) })`; builder:
329
342
  * RenderDirective does the same), NOT by an internal ngSwitch. `level` is kept as an input
330
343
  * for that creation logic + serialization. Editing (contenteditable) runs in the builder only.
344
+ *
345
+ * A Heading may LINK (Card v2, "Link the whole card"): its words then render inside ONE `<a>` — the
346
+ * card's title is its link, so the link's name is the title. The shared CSS stretches it over the card
347
+ * (iox-design.css). On the editing canvas the anchor never navigates and carries `data-iox-canvas`, so
348
+ * the stretch is off and every part of the card stays clickable; a click still selects the heading.
331
349
  */
332
350
  declare class BuilderHeadingComponent implements OnInit {
333
351
  private events;
334
352
  private cdr;
353
+ isPreview: boolean | null;
335
354
  content: string;
336
355
  level: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
337
356
  nodeId: string;
338
357
  node: EditableNode$1 | null;
358
+ linkType: LinkType;
359
+ url: string;
360
+ pageRoute: string;
361
+ target: '_self' | '_blank' | '_parent' | '_top';
339
362
  editing: boolean;
340
- constructor(events: IBuilderEventEmitter | null, cdr: ChangeDetectorRef);
363
+ readonly linkClass = "iox-card-link";
364
+ constructor(events: IBuilderEventEmitter | null, cdr: ChangeDetectorRef, isPreview: boolean | null);
365
+ /** Where the heading links, or null (plain words). The engine hands `pageRoute` in already resolved;
366
+ * the canvas shows the route as authored, as its Link does. */
367
+ get href(): string | null;
368
+ get linkTarget(): string | null;
369
+ get linkRel(): string | null;
370
+ /** On the editing canvas a link never navigates (plain, modified and middle clicks); the click still
371
+ * bubbles to the heading, which is what selects it. */
372
+ onLinkClick(event: MouseEvent): void;
341
373
  ngOnInit(): void;
342
374
  onMouseDown(event: Event): void;
343
375
  startEdit(event: Event): void;
@@ -345,8 +377,8 @@ declare class BuilderHeadingComponent implements OnInit {
345
377
  onKeyDown(event: Event): void;
346
378
  private isBound;
347
379
  private syncTraitToPanel;
348
- static ɵfac: i0.ɵɵFactoryDeclaration<BuilderHeadingComponent, [{ optional: true; }, null]>;
349
- static ɵcmp: i0.ɵɵComponentDeclaration<BuilderHeadingComponent, "h1[iox-heading],h2[iox-heading],h3[iox-heading],h4[iox-heading],h5[iox-heading],h6[iox-heading]", never, { "content": { "alias": "content"; "required": false; }; "level": { "alias": "level"; "required": false; }; "nodeId": { "alias": "nodeId"; "required": false; }; "node": { "alias": "node"; "required": false; }; }, {}, never, never, false, never>;
380
+ static ɵfac: i0.ɵɵFactoryDeclaration<BuilderHeadingComponent, [{ optional: true; }, null, { optional: true; }]>;
381
+ static ɵcmp: i0.ɵɵComponentDeclaration<BuilderHeadingComponent, "h1[iox-heading],h2[iox-heading],h3[iox-heading],h4[iox-heading],h5[iox-heading],h6[iox-heading]", never, { "content": { "alias": "content"; "required": false; }; "level": { "alias": "level"; "required": false; }; "nodeId": { "alias": "nodeId"; "required": false; }; "node": { "alias": "node"; "required": false; }; "linkType": { "alias": "linkType"; "required": false; }; "url": { "alias": "url"; "required": false; }; "pageRoute": { "alias": "pageRoute"; "required": false; }; "target": { "alias": "target"; "required": false; }; }, {}, never, never, false, never>;
350
382
  }
351
383
 
352
384
  interface EditableNode {
@@ -446,17 +478,6 @@ declare class ClientRepeaterComponent {
446
478
  static ɵcmp: i0.ɵɵComponentDeclaration<ClientRepeaterComponent, "iox-repeater", never, { "nodeId": { "alias": "nodeId"; "required": false; }; }, {}, never, never, false, never>;
447
479
  }
448
480
 
449
- /**
450
- * Where a node links (W1.1). Link and LinkedContainer always had two link types — an external URL or a
451
- * website PAGE by its route — and ButtonBlock now has the same pair, so a composed "Get in touch" button
452
- * is a page link the builder shows as `/contact`, not a hand-built URL. Framework-free: the composer
453
- * (generation-core) writes it, the builder canvas and both engine render paths read it.
454
- */
455
- declare enum LinkType {
456
- External = "external",
457
- Page = "page"
458
- }
459
-
460
481
  /**
461
482
  * Directive-host ButtonBlock: the real `<a>`/`<button>` IS the host — no `app-button-block`
462
483
  * wrapper, no inner/outer split. The tag is chosen by whoever CREATES the component (engine
@@ -1371,6 +1392,7 @@ declare class IoxComponentRegistryService {
1371
1392
  private readonly map;
1372
1393
  getComponent(type: string): Type<any> | undefined;
1373
1394
  hasComponent(type: string): boolean;
1395
+ registeredTypes(): string[];
1374
1396
  static ɵfac: i0.ɵɵFactoryDeclaration<IoxComponentRegistryService, never>;
1375
1397
  static ɵprov: i0.ɵɵInjectableDeclaration<IoxComponentRegistryService>;
1376
1398
  }
@@ -1505,6 +1527,11 @@ interface VirtualTraitGroup {
1505
1527
  * (`::marker` styles are a nested map, not a single cancellable property). */
1506
1528
  readonly reset: string | null;
1507
1529
  }
1530
+ /** "Max lines" (Card v2, board 8 — opt-in on a Title / Text): a whole number clamps the text to that many
1531
+ * lines with an ellipsis. It composes into the four declarations a line clamp takes; anything else
1532
+ * (`none`, empty, 0) clamps nothing. */
1533
+ declare const MAX_LINES_KEY = "maxLines";
1534
+ declare function isGradientPaint(value: unknown): boolean;
1508
1535
  /** The whole-value backdrop trait (DS2). A plain key — it is copied, not stripped — but it feeds
1509
1536
  * the backdrop group's output, so a breakpoint layer that sets it must count as touching it. */
1510
1537
  declare const BACKDROP_WHOLE_KEY = "backdropFilter";
@@ -2867,5 +2894,5 @@ declare class GalleryLoop {
2867
2894
  private emit;
2868
2895
  }
2869
2896
 
2870
- export { AOSService, BACKDROP_TWIN_KEY, BACKDROP_WHOLE_KEY, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, CONTAINER_HTML_TAGS, CONTEXT_ACTION_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ContainerComponent, ContextStore, DEFAULT_GALLERY_SCHEDULER, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DeclarationContext, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION, HOVER_STATES, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxDialogComponent, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, LEGACY_HOVER_TRANSITION, LinkedContainerComponent, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, RESERVED_ALIAS_PREFIX, STATE_PSEUDO_CLASS, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_RESPONSIVE_TRAITS, SectionComponent, TRANSITION_FEELS, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, activeActions, applyBindingPipes, applyFeel, breakpointDef, breakpointForWidth, breakpointQueryFor, buildDataSourceFilter, buildPartSelector, buildRelatedFilter, buildSwiperBreakpoints, buildTargetedStateSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, collectContextSlotKeys, collectReferencedAliases, compileDeclarations, compilePageStyle, composeVirtualTraits, computeRelative, containerHtmlTag, contextActionEffect, contextSlotKeys, countOverrides, dataSourcePlanToRequest, declaredInputNames, decomposeBackdropFilter, defaultHoverTransition, defaultMessageResolver, deriveSeoFromContent, describePageStyle, describeSeoFields, effectiveTransitionId, excludeSelf, feelFor, getByPath, getSeoValue, hasHoverState, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isInputOverriddenAt, isOverriddenAt, isReservedAlias, isValidSlotKey, isWholeBackdropSet, legacyEnterToTransition, matchRouteParams, mediaQueryFor, mergeSwiperBreakpoints, nearestCommonAncestorId, nodeCssId, nodeUsesContext, normalizeCollectionResult, normalizeGalleryImage, normalizeIconClass, parentBreakpoint, parseUrlList, partClassName, planDataSource, planResponsiveRules, referencedDataSources, referencedDataSourcesDeep, renderDefaultDeclarations, resetPageStyleField, resetSeoField, resolveBoundValue, resolveContextValue, resolveDerivedInto, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveRelativeInto, resolveRepeaterItems, resolveSeo, resolveSiblings, resolveSingleItemId, resolveStateStyles, resolveStyleProps, resolveValueChain, rewriteViewportUnits, safeSetInput, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, stateNamesIn, statePseudoClass, stripSuppressedProps, variableAliases, variableItemsFrom, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
2897
+ export { AOSService, BACKDROP_TWIN_KEY, BACKDROP_WHOLE_KEY, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, CONTAINER_HTML_TAGS, CONTEXT_ACTION_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ContainerComponent, ContextStore, DEFAULT_GALLERY_SCHEDULER, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DeclarationContext, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION, HOVER_STATES, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxDialogComponent, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, LEGACY_HOVER_TRANSITION, LinkedContainerComponent, MAX_LINES_KEY, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, RESERVED_ALIAS_PREFIX, STATE_PSEUDO_CLASS, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_RESPONSIVE_TRAITS, SectionComponent, TRANSITION_FEELS, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, activeActions, applyBindingPipes, applyFeel, breakpointDef, breakpointForWidth, breakpointQueryFor, buildDataSourceFilter, buildPartSelector, buildRelatedFilter, buildSwiperBreakpoints, buildTargetedStateSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, collectContextSlotKeys, collectReferencedAliases, compileDeclarations, compilePageStyle, composeVirtualTraits, computeRelative, containerHtmlTag, contextActionEffect, contextSlotKeys, countOverrides, dataSourcePlanToRequest, declaredInputNames, decomposeBackdropFilter, defaultHoverTransition, defaultMessageResolver, deriveSeoFromContent, describePageStyle, describeSeoFields, effectiveTransitionId, excludeSelf, feelFor, getByPath, getSeoValue, hasHoverState, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isGradientPaint, isInputOverriddenAt, isOverriddenAt, isReservedAlias, isValidSlotKey, isWholeBackdropSet, legacyEnterToTransition, matchRouteParams, mediaQueryFor, mergeSwiperBreakpoints, nearestCommonAncestorId, nodeCssId, nodeUsesContext, normalizeCollectionResult, normalizeGalleryImage, normalizeIconClass, parentBreakpoint, parseUrlList, partClassName, planDataSource, planResponsiveRules, referencedDataSources, referencedDataSourcesDeep, renderDefaultDeclarations, resetPageStyleField, resetSeoField, resolveBoundValue, resolveContextValue, resolveDerivedInto, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveRelativeInto, resolveRepeaterItems, resolveSeo, resolveSiblings, resolveSingleItemId, resolveStateStyles, resolveStyleProps, resolveValueChain, rewriteViewportUnits, safeSetInput, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, stateNamesIn, statePseudoClass, stripSuppressedProps, variableAliases, variableItemsFrom, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
2871
2898
  export type { BoundValueSpec, BreakpointDef, BuildRelatedFilterOptions, ContextActionEffect, ContextActionLike, ContextItemRef, ContextSlotDefault, ContextValueSpec, ContextWriteScope, DataSourceDomain, DataSourceEndpoints, DataSourceMode, DataSourcePlan, DataSourceQueryFilter, DataSourceRequest, DataSourceRequestSpec, DataSourceRouteFilter, DataSourceSpec, FormMessageResolver, GalleryBoundSource, GalleryDurations, GalleryEffect, GalleryEffectDescriptor, GalleryImage, GalleryLoopOptions, GalleryLoopScheduler, GalleryLoopState, GallerySlideState, IBuilderEventEmitter, InputMap, IoxContextSlot, IoxPipe, OverrideBreakpoint, PageStyleFieldState, PageStyleOptions, PageStyleSettings, PageTransitionCategory, PageTransitionCategoryMeta, PageTransitionOptions, PageTransitionPreset, PipeArgSpec, PipeContext, PipeDescriptor, RelativeInputs, RelativeSourceKind, RelativeSourceSpec, ResolveSiblingsOptions, ResolvedPageTransition, ResponsiveLayer, ResponsiveOverrides, ResponsiveRuleSet, RouteContext, SeoDerivedContent, SeoFieldSource, SeoFieldState, SeoResolveContext, SetContextParams, SiblingsResult, StateStyleMap, StyleMap, StyleTreeNode, SwiperBreakpointMap, ToggleableAction, TransitionFeel, TransitionPhase, TransitionTimelineInput, ValueSource, VirtualTraitGroup };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectoriox/iox-ui",
3
- "version": "5.29.1",
3
+ "version": "5.31.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=20.0.0",
6
6
  "@angular/core": ">=20.0.0",
@@ -23,6 +23,10 @@
23
23
  "types": "./actions-core/index.d.ts",
24
24
  "default": "./fesm2022/vectoriox-iox-ui-actions-core.mjs"
25
25
  },
26
+ "./composite-core": {
27
+ "types": "./composite-core/index.d.ts",
28
+ "default": "./fesm2022/vectoriox-iox-ui-composite-core.mjs"
29
+ },
26
30
  "./consent-core": {
27
31
  "types": "./consent-core/index.d.ts",
28
32
  "default": "./fesm2022/vectoriox-iox-ui-consent-core.mjs"
@@ -0,0 +1,103 @@
1
+ import { elementStyleById, switchElementStyle } from '@vectoriox/iox-ui/design-core';
2
+
3
+ import { CompositeNode, CompositeNodeFactory, writeStyles } from './composite-node';
4
+
5
+ /**
6
+ * BADGE + CHIP (composite T25; complex-components board 8). A Badge is a Container wearing
7
+ * `badge-<tone>` › Icon + Text; its TONE is its element style, so Badge ▸ Tone writes the style and
8
+ * nothing else, and a re-theme restyles every badge. A Chip is the outlined static style only (no
9
+ * behaviour until the K5 Data view). Inside a card the badge plays `card.badge`; anywhere else it is
10
+ * recognised by its style.
11
+ */
12
+
13
+ export enum BadgeTone {
14
+ Neutral = 'neutral',
15
+ Success = 'success',
16
+ Warning = 'warning',
17
+ Accent = 'accent',
18
+ }
19
+
20
+ export const BADGE_TONES: ReadonlyArray<BadgeTone> = [BadgeTone.Neutral, BadgeTone.Success, BadgeTone.Warning, BadgeTone.Accent];
21
+
22
+ /** The tone's element style (`badge-<tone>`, the contract's). */
23
+ export function badgeStyleId(tone: BadgeTone): string {
24
+ return `badge-${tone}`;
25
+ }
26
+
27
+ export const CHIP_STYLE = 'chip';
28
+
29
+ /** The glyph a fresh badge of each tone shows (the board's tone grid). */
30
+ export const BADGE_TONE_ICONS: Readonly<Record<BadgeTone, string>> = {
31
+ [BadgeTone.Neutral]: 'ph ph-tag',
32
+ [BadgeTone.Success]: 'ph ph-check',
33
+ [BadgeTone.Warning]: 'ph ph-warning',
34
+ [BadgeTone.Accent]: 'ph ph-sparkle',
35
+ };
36
+ export const CHIP_ICON = 'ph ph-tag';
37
+
38
+ const TONE_BY_STYLE = new Map(BADGE_TONES.map(tone => [badgeStyleId(tone), tone]));
39
+
40
+ /** The badge tone a node wears, or null when it is no badge. */
41
+ export function badgeToneOf(node: { elementStyle?: string | null } | null | undefined): BadgeTone | null {
42
+ return TONE_BY_STYLE.get(node?.elementStyle ?? '') ?? null;
43
+ }
44
+
45
+ /** True for a node wearing a badge tone or the chip style — the pills (their Icon reads their ink). */
46
+ export function isPillNode(node: { elementStyle?: string | null } | null | undefined): boolean {
47
+ return badgeToneOf(node) !== null || node?.elementStyle === CHIP_STYLE;
48
+ }
49
+
50
+ /**
51
+ * Badge ▸ Tone: move the badge to another tone's element style (an edit the author made to the old
52
+ * tone's look is kept, design-core's switch rule). Nothing else changes — not the icon, not the words.
53
+ */
54
+ export function setBadgeTone(node: CompositeNode, tone: BadgeTone, baseline: Readonly<Record<string, unknown>> = {}): boolean {
55
+ const current = node.elementStyle ?? null;
56
+ const next = badgeStyleId(tone);
57
+ if (!badgeToneOf(node) || current === next || !elementStyleById(next)) return false;
58
+ const result = switchElementStyle(node, current, next, baseline);
59
+ if (result.applied) node.elementStyle = next;
60
+ return result.applied;
61
+ }
62
+
63
+ /** A pill's box: hugs its words, never stretches in a column. */
64
+ const PILL_BASE: Readonly<Record<string, string>> = {
65
+ display: 'inline-flex', alignItems: 'center', gap: '5px', width: 'auto', minHeight: 'auto', alignSelf: 'flex-start', lineHeight: '1.4',
66
+ };
67
+ const PILL_ICON: Readonly<Record<string, string>> = { fontSize: '1em' };
68
+ const PILL_WORDS: Readonly<Record<string, string>> = { width: 'auto', minHeight: 'auto', padding: '0px 0px 0px 0px', margin: '0px 0px 0px 0px' };
69
+ /** The pill's words inherit the pill's type and ink (the contract's inherit-everything text style). */
70
+ const PILL_WORDS_STYLE = 'button-label';
71
+
72
+ export interface PillOptions {
73
+ text?: string;
74
+ icon?: string;
75
+ /** Key prefix for the consumer's ids (`badge` → `badge`, `badge-icon`, `badge-text`). */
76
+ key?: string;
77
+ }
78
+
79
+ function buildPill(create: CompositeNodeFactory, elementStyle: string, text: string, icon: string, key: string): CompositeNode {
80
+ const pill = create({ key, type: 'Container', elementStyle });
81
+ writeStyles(pill, PILL_BASE);
82
+ const glyph = create({ key: `${key}-icon`, type: 'Icon', props: { iconClass: icon }, elementStyle: 'icon' });
83
+ writeStyles(glyph, PILL_ICON);
84
+ const words = create({ key: `${key}-text`, type: 'Text', props: { content: text, tag: 'span' }, elementStyle: PILL_WORDS_STYLE });
85
+ writeStyles(words, PILL_WORDS);
86
+ pill.children = [glyph, words];
87
+ return pill;
88
+ }
89
+
90
+ /** A fresh Badge: Container `badge-<tone>` › Icon + Text. */
91
+ export function buildBadge(create: CompositeNodeFactory, options: PillOptions & { tone?: BadgeTone } = {}): CompositeNode {
92
+ const tone = options.tone ?? BadgeTone.Accent;
93
+ const badge = buildPill(create, badgeStyleId(tone), options.text ?? 'New', options.icon ?? BADGE_TONE_ICONS[tone], options.key ?? 'badge');
94
+ badge.label = 'Badge';
95
+ return badge;
96
+ }
97
+
98
+ /** A fresh Chip: Container `chip` › Icon + Text (outlined, static). */
99
+ export function buildChip(create: CompositeNodeFactory, options: PillOptions = {}): CompositeNode {
100
+ const chip = buildPill(create, CHIP_STYLE, options.text ?? 'Chip', options.icon ?? CHIP_ICON, options.key ?? 'chip');
101
+ chip.label = 'Chip';
102
+ return chip;
103
+ }