@vectoriox/iox-ui 5.13.1 → 5.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/design-core/index.d.ts +91 -15
- package/fesm2022/vectoriox-iox-ui-design-core.mjs +118 -24
- package/fesm2022/vectoriox-iox-ui-design-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui.mjs +39 -6
- package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
- package/index.d.ts +25 -4
- package/package.json +1 -1
- package/src/design-core/element-styles.ts +4 -20
- package/src/design-core/empty-image.ts +52 -0
- package/src/design-core/node-classes.ts +82 -0
- package/src/design-core/public-api.ts +2 -0
- package/styles/iox-design.css +30 -0
package/index.d.ts
CHANGED
|
@@ -183,6 +183,12 @@ declare class BuilderImageComponent {
|
|
|
183
183
|
src: string;
|
|
184
184
|
alt: string;
|
|
185
185
|
nodeId: string;
|
|
186
|
+
/**
|
|
187
|
+
* What a MARKED image shows while it has no source (design-system DS3) — set by each renderer from
|
|
188
|
+
* the shared `emptyImageSrc` (`@vectoriox/iox-ui/design-core`): the "Add image" label on the canvas,
|
|
189
|
+
* the blank on the live site, `null` for an unmarked image, which therefore renders as before.
|
|
190
|
+
*/
|
|
191
|
+
emptySrc: string | null;
|
|
186
192
|
/**
|
|
187
193
|
* The `src` attribute value, or `null` to OMIT the attribute entirely when there is no image.
|
|
188
194
|
*
|
|
@@ -194,8 +200,10 @@ declare class BuilderImageComponent {
|
|
|
194
200
|
* filled when the visitor interacts (see element-context-state-plan.md).
|
|
195
201
|
*/
|
|
196
202
|
get srcAttr(): string | null;
|
|
203
|
+
/** `data-iox-empty` while the placeholder stands in — the hook the shared fill rule keys on. */
|
|
204
|
+
get emptyAttr(): '' | null;
|
|
197
205
|
static ɵfac: i0.ɵɵFactoryDeclaration<BuilderImageComponent, never>;
|
|
198
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<BuilderImageComponent, "img[iox-img]", never, { "src": { "alias": "src"; "required": false; }; "alt": { "alias": "alt"; "required": false; }; "nodeId": { "alias": "nodeId"; "required": false; }; }, {}, never, never, false, never>;
|
|
206
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<BuilderImageComponent, "img[iox-img]", never, { "src": { "alias": "src"; "required": false; }; "alt": { "alias": "alt"; "required": false; }; "nodeId": { "alias": "nodeId"; "required": false; }; "emptySrc": { "alias": "emptySrc"; "required": false; }; }, {}, never, never, false, never>;
|
|
199
207
|
}
|
|
200
208
|
|
|
201
209
|
/** Directive-host spacer: the `<div>` IS the host — no wrapper. All styles on `.iox-node`. */
|
|
@@ -1634,6 +1642,19 @@ interface PageStyleSettings {
|
|
|
1634
1642
|
*/
|
|
1635
1643
|
maxWidth: string;
|
|
1636
1644
|
}
|
|
1645
|
+
interface PageStyleOptions {
|
|
1646
|
+
/**
|
|
1647
|
+
* `pageSettings.designSystem` (design-system DS3). A page created with the design system — or
|
|
1648
|
+
* adopted into it (DS7) — falls back to the org's tokens for whatever it does not set itself:
|
|
1649
|
+
* `background: var(--iox-page-bg)` (a PAINT — Glass and Midnight are gradients, so the shorthand),
|
|
1650
|
+
* `color: var(--iox-text)`, `font-family: var(--iox-font-body)`. A page without the flag (every
|
|
1651
|
+
* page saved before DS3) compiles exactly as before, so a seeded org never puts a new ground under
|
|
1652
|
+
* legacy text — the page follows the same opt-in rule as the element-style marker (§3b-bis).
|
|
1653
|
+
*/
|
|
1654
|
+
designSystem?: boolean;
|
|
1655
|
+
}
|
|
1656
|
+
/** The token fallbacks of a design-system page (spec tokens only). */
|
|
1657
|
+
declare const PAGE_TOKEN_FALLBACKS: Readonly<Record<string, string>>;
|
|
1637
1658
|
/**
|
|
1638
1659
|
* Compile page style settings into CSS declarations.
|
|
1639
1660
|
*
|
|
@@ -1643,7 +1664,7 @@ interface PageStyleSettings {
|
|
|
1643
1664
|
* `backgroundType` selects which background field wins — it is derived from the author's
|
|
1644
1665
|
* colour/image choice rather than picked directly (see the builder's page-background.util).
|
|
1645
1666
|
*/
|
|
1646
|
-
declare function compilePageStyle(style: Partial<PageStyleSettings> | null | undefined): Record<string, string>;
|
|
1667
|
+
declare function compilePageStyle(style: Partial<PageStyleSettings> | null | undefined, options?: PageStyleOptions): Record<string, string>;
|
|
1647
1668
|
|
|
1648
1669
|
/**
|
|
1649
1670
|
* Shared data-source resolving pattern — the SINGLE source of truth for how an IoxDataSource
|
|
@@ -2772,5 +2793,5 @@ declare class GalleryLoop {
|
|
|
2772
2793
|
private emit;
|
|
2773
2794
|
}
|
|
2774
2795
|
|
|
2775
|
-
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_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, 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, 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, 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 };
|
|
2776
|
-
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, 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 };
|
|
2796
|
+
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_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, 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, 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, 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 };
|
|
2797
|
+
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, 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
|
@@ -272,10 +272,10 @@ export function switchElementStyle(
|
|
|
272
272
|
|
|
273
273
|
/**
|
|
274
274
|
* The element-style MARKER (design-system §3b-bis, spec `elementStyleMarker`). A node that wears an
|
|
275
|
-
* element style renders `iox-es iox-es-<id>` in BOTH renderers
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
275
|
+
* element style renders `iox-es iox-es-<id>` in BOTH renderers, through the one node-class helper
|
|
276
|
+
* `nodeDesignClasses` (node-classes.ts); every piece of component CSS that carries the system look is
|
|
277
|
+
* scoped to it, so a node saved before DS2 (no `elementStyle`) keeps its exact pre-DS2 component CSS
|
|
278
|
+
* no matter which tokens the org holds.
|
|
279
279
|
*/
|
|
280
280
|
export const ELEMENT_STYLE_MARKER_CLASS = 'iox-es';
|
|
281
281
|
|
|
@@ -286,22 +286,6 @@ export function elementStyleClass(id: string | null | undefined): string | null
|
|
|
286
286
|
return elementStyleById(id) ? `${MARKER_PREFIX}${id}` : null;
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
/** The marker classes a node renders: `'iox-es iox-es-<id>'`, or `''` for a node with no (known) style. */
|
|
290
|
-
export function elementStyleClasses(node: { elementStyle?: string | null } | null | undefined): string {
|
|
291
|
-
const own = elementStyleClass(node?.elementStyle);
|
|
292
|
-
return own ? `${ELEMENT_STYLE_MARKER_CLASS} ${own}` : '';
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/** Make an element's marker match `elementStyle` — the live switch on the canvas, and the
|
|
296
|
-
* component path's one-time stamp. Removes a stale `iox-es-<old>`; touches no other class. */
|
|
297
|
-
export function syncElementStyleMarker(el: { classList: DOMTokenList } | null | undefined, elementStyle: string | null | undefined): void {
|
|
298
|
-
if (!el?.classList) return;
|
|
299
|
-
const stale = Array.from(el.classList).filter(c => c === ELEMENT_STYLE_MARKER_CLASS || c.startsWith(MARKER_PREFIX));
|
|
300
|
-
for (const c of stale) el.classList.remove(c);
|
|
301
|
-
const classes = elementStyleClasses({ elementStyle });
|
|
302
|
-
if (classes) el.classList.add(...classes.split(' '));
|
|
303
|
-
}
|
|
304
|
-
|
|
305
289
|
/**
|
|
306
290
|
* The spec's `headingLevelRule`: changing a Heading's level moves it to the new level's DEFAULT
|
|
307
291
|
* style only when it still wears its old level's default (author changes kept, via
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { hasElementStyleMarker } from './node-classes';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Empty images (design-system DS3). A MARKED Image (`.iox-es-media`) with no source renders a box in
|
|
5
|
+
* the `surface-2` token with its aspect ratio kept — on the canvas with an "Add image" label, on the
|
|
6
|
+
* live site with nothing in it. An UNMARKED empty Image renders exactly as before (no `src`).
|
|
7
|
+
*
|
|
8
|
+
* Why a placeholder `src` instead of a src-less `<img>`: Chromium paints a src-less image as a broken
|
|
9
|
+
* image (a frame, an icon and the alt text) and ignores `aspect-ratio` once alt text is present, and no
|
|
10
|
+
* pseudo-element paints on an `<img>`. A real image with no natural size of its own (an SVG with no
|
|
11
|
+
* width/height) is a proper replaced element: `aspect-ratio`/`object-fit` apply, the background shows
|
|
12
|
+
* through, and its default object size (300×150) is the SAME for the canvas label and the live blank,
|
|
13
|
+
* so both renderers lay the box out identically. The fill itself is shared CSS (`iox-design.css`)
|
|
14
|
+
* keyed on {@link EMPTY_IMAGE_ATTRIBUTE}.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Set on a marked Image while it has no source — the hook the shared fill rule keys on. */
|
|
18
|
+
export const EMPTY_IMAGE_ATTRIBUTE = 'data-iox-empty';
|
|
19
|
+
|
|
20
|
+
const svgUri = (svg: string) => `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
|
21
|
+
|
|
22
|
+
/** The live site's blank: an SVG with no intrinsic size and nothing in it. */
|
|
23
|
+
export const EMPTY_IMAGE_SRC = svgUri('<svg xmlns="http://www.w3.org/2000/svg"/>');
|
|
24
|
+
|
|
25
|
+
const xmlEscape = (s: string) => s.replace(/[&<>"']/g, c => `&#${c.charCodeAt(0)};`);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The canvas blank: the same no-size SVG with `label` centred as a chip. The chip carries its own
|
|
29
|
+
* light ground, so it reads on every flavor's `surface-2`, light or dark; it is builder chrome, never
|
|
30
|
+
* site content. Width is estimated from the label length (an image cannot measure text).
|
|
31
|
+
*/
|
|
32
|
+
export function emptyImageLabelSrc(label: string): string {
|
|
33
|
+
const text = xmlEscape(label);
|
|
34
|
+
const width = Math.round(28 + label.length * 7);
|
|
35
|
+
return svgUri(
|
|
36
|
+
'<svg xmlns="http://www.w3.org/2000/svg"><svg x="50%" y="50%" overflow="visible">'
|
|
37
|
+
+ `<rect x="${-width / 2}" y="-13" width="${width}" height="26" rx="13" fill="#ffffff" fill-opacity="0.92"/>`
|
|
38
|
+
+ '<text x="0" y="0" text-anchor="middle" dominant-baseline="central" font-family="system-ui, -apple-system, \'Segoe UI\', sans-serif" '
|
|
39
|
+
+ `font-size="12" font-weight="500" fill="#1f1d1b">${text}</text></svg></svg>`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The `src` a node's Image renders when its own source is empty: the canvas label (when `label` is
|
|
45
|
+
* given) or the live blank for a MARKED node; `null` — render exactly as before — for an unmarked
|
|
46
|
+
* node or one that has a source.
|
|
47
|
+
*/
|
|
48
|
+
export function emptyImageSrc(node: { elementStyle?: string | null } | null | undefined, src: unknown, label?: string | null): string | null {
|
|
49
|
+
if (src != null && src !== '') return null;
|
|
50
|
+
if (!hasElementStyleMarker(node)) return null;
|
|
51
|
+
return label ? emptyImageLabelSrc(label) : EMPTY_IMAGE_SRC;
|
|
52
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ELEMENT_STYLE_MARKER_CLASS, elementStyleClass } from './element-styles';
|
|
2
|
+
import { SPEC_SCHEMES } from './flavor-spec.generated';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The design-system classes a node renders, in BOTH renderers (builder canvas, engine component
|
|
6
|
+
* path, engine string path) — ONE helper, so the three can never disagree about which class a node
|
|
7
|
+
* carries:
|
|
8
|
+
*
|
|
9
|
+
* - the element-style MARKER `iox-es iox-es-<id>` (design-system §3b-bis) — component CSS carrying
|
|
10
|
+
* the system look is scoped to it, so a node saved before DS2 keeps its pre-DS2 look;
|
|
11
|
+
* - the section colour scheme `iox-scheme-<id>` (§3c) for a non-default `colorScheme` — the class
|
|
12
|
+
* `compileSchemeCss` re-declares tokens on, so everything inside re-resolves.
|
|
13
|
+
*
|
|
14
|
+
* Renderers read `elementStyle` and `colorScheme` for exactly this and nothing else.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** The section colour schemes — the contract's `schemes` keys. */
|
|
18
|
+
export enum ColorScheme {
|
|
19
|
+
Default = 'default',
|
|
20
|
+
Subtle = 'subtle',
|
|
21
|
+
Inverse = 'inverse',
|
|
22
|
+
Accent = 'accent',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The content trait (stored in `props`, not a style prop) that picks a node's scheme. */
|
|
26
|
+
export const COLOR_SCHEME_TRAIT = 'colorScheme';
|
|
27
|
+
|
|
28
|
+
/** The node types that offer a scheme (spec §3c: Section/Container; LinkedContainer is a Container that links). */
|
|
29
|
+
export const COLOR_SCHEME_TYPES: ReadonlyArray<string> = ['Section', 'Container', 'LinkedContainer'];
|
|
30
|
+
|
|
31
|
+
/** Every scheme in contract order, with the contract's label (Default · Subtle · Inverse · Brand). */
|
|
32
|
+
export const COLOR_SCHEMES: ReadonlyArray<{ readonly id: ColorScheme; readonly label: string }> =
|
|
33
|
+
(Object.keys(SPEC_SCHEMES) as ColorScheme[]).map(id => ({ id, label: (SPEC_SCHEMES as Record<string, { label: string }>)[id].label }));
|
|
34
|
+
|
|
35
|
+
const SCHEME_IDS = new Set<string>(Object.keys(SPEC_SCHEMES));
|
|
36
|
+
const SCHEME_PREFIX = 'iox-scheme-';
|
|
37
|
+
const MARKER_PREFIX = `${ELEMENT_STYLE_MARKER_CLASS}-`;
|
|
38
|
+
|
|
39
|
+
/** `iox-scheme-<id>` for a known NON-default scheme, else null — an unknown id never reaches a class attribute. */
|
|
40
|
+
export function colorSchemeClass(id: unknown): string | null {
|
|
41
|
+
return typeof id === 'string' && id !== ColorScheme.Default && SCHEME_IDS.has(id) ? `${SCHEME_PREFIX}${id}` : null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The node shapes this module reads: a serialized node (`props`) or a builder node (`traits[]`). */
|
|
45
|
+
export interface DesignClassSource {
|
|
46
|
+
elementStyle?: string | null;
|
|
47
|
+
props?: Readonly<Record<string, unknown>> | null;
|
|
48
|
+
traits?: ReadonlyArray<{ name?: string; default?: unknown }> | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** A node's `colorScheme`, from `props` (serialized) or its trait (builder). */
|
|
52
|
+
export function colorSchemeOf(node: DesignClassSource | null | undefined): string | null {
|
|
53
|
+
if (!node) return null;
|
|
54
|
+
const fromProps = node.props?.[COLOR_SCHEME_TRAIT];
|
|
55
|
+
if (fromProps !== undefined) return typeof fromProps === 'string' ? fromProps : null;
|
|
56
|
+
const trait = node.traits?.find(t => t?.name === COLOR_SCHEME_TRAIT)?.default;
|
|
57
|
+
return typeof trait === 'string' ? trait : null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** True when the node wears a known element style — the marker is on it. */
|
|
61
|
+
export function hasElementStyleMarker(node: { elementStyle?: string | null } | null | undefined): boolean {
|
|
62
|
+
return elementStyleClass(node?.elementStyle) !== null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The classes a node renders: `'iox-es iox-es-card iox-scheme-inverse'`, or `''` for none. */
|
|
66
|
+
export function nodeDesignClasses(node: DesignClassSource | null | undefined): string {
|
|
67
|
+
const marker = elementStyleClass(node?.elementStyle);
|
|
68
|
+
return [marker ? `${ELEMENT_STYLE_MARKER_CLASS} ${marker}` : '', colorSchemeClass(colorSchemeOf(node)) ?? '']
|
|
69
|
+
.filter(Boolean).join(' ');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const isDesignClass = (c: string) =>
|
|
73
|
+
c === ELEMENT_STYLE_MARKER_CLASS || c.startsWith(MARKER_PREFIX) || c.startsWith(SCHEME_PREFIX);
|
|
74
|
+
|
|
75
|
+
/** Make an element's design classes match the node — the live switch on the canvas and the
|
|
76
|
+
* component path's one-time stamp. Removes stale ones; touches no other class. */
|
|
77
|
+
export function syncNodeDesignClasses(el: { classList: DOMTokenList } | null | undefined, node: DesignClassSource | null | undefined): void {
|
|
78
|
+
if (!el?.classList) return;
|
|
79
|
+
for (const c of Array.from(el.classList).filter(isDesignClass)) el.classList.remove(c);
|
|
80
|
+
const classes = nodeDesignClasses(node);
|
|
81
|
+
if (classes) el.classList.add(...classes.split(' '));
|
|
82
|
+
}
|
|
@@ -14,6 +14,8 @@ export * from './css-names';
|
|
|
14
14
|
export * from './token-css';
|
|
15
15
|
export * from './flavor';
|
|
16
16
|
export * from './element-styles';
|
|
17
|
+
export * from './node-classes';
|
|
18
|
+
export * from './empty-image';
|
|
17
19
|
export * from './brand';
|
|
18
20
|
export * from './flavor-tokens';
|
|
19
21
|
export * from './flavor-seed';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* Design-system component CSS that EVERY renderer needs — the ONE copy.
|
|
2
|
+
*
|
|
3
|
+
* Same reason as iox-forms.css: the SSR engine's string renderer emits plain markup, the engine's
|
|
4
|
+
* component path renders the shared components, and the builder canvas renders its own twins, so a
|
|
5
|
+
* component-scoped copy in each would be three copies that drift. Consumers add this file to their
|
|
6
|
+
* `angular.json` styles.
|
|
7
|
+
*
|
|
8
|
+
* Everything here is scoped to the element-style marker (design-system §3b-bis): a node saved
|
|
9
|
+
* before element styles never matches, so it renders exactly as before.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* Empty images (DS3). A marked Image with no source carries `data-iox-empty` and a size-less
|
|
13
|
+
placeholder src (design-core `emptyImageSrc`), so it stays a real replaced element: its own
|
|
14
|
+
aspect-ratio and object-fit apply and this fill shows through — on the canvas behind the
|
|
15
|
+
"Add image" label, on the live site on its own. Out-specifies the node rule (0,2,1 vs 0,1,0)
|
|
16
|
+
only while the image is empty. */
|
|
17
|
+
img.iox-es-media[data-iox-empty] {
|
|
18
|
+
background-color: var(--iox-surface-2);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* An empty marked Gallery takes the same fill, on the element that carries its node rule (size,
|
|
22
|
+
radius): the string renderer's one div, or the component's inner `.iox-gallery` under the
|
|
23
|
+
`<iox-gallery>` host that wears the marker. Its cinematic overlay has nothing to darken. */
|
|
24
|
+
.iox-gallery.iox-es-media[data-iox-empty],
|
|
25
|
+
iox-gallery.iox-es-media[data-iox-empty] > .iox-gallery {
|
|
26
|
+
background-color: var(--iox-surface-2);
|
|
27
|
+
}
|
|
28
|
+
.iox-es-media[data-iox-empty] .iox-gallery__overlay {
|
|
29
|
+
display: none;
|
|
30
|
+
}
|