@phis/ui 0.4.3 → 0.4.4
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/components/controls/phi-anchor-nav-control.js +4 -1
- package/components/controls/phi-background-control.js +21 -6
- package/components/controls/phi-bullet-list-control.d.ts +7 -2
- package/components/controls/phi-bullet-list-control.js +4 -3
- package/components/controls/phi-color-control.js +4 -2
- package/components/controls/phi-drawer-control-adapter.js +3 -2
- package/components/controls/phi-inline-text-editor.js +7 -1
- package/components/controls/phi-length-control.js +1 -1
- package/components/controls/phi-ribbon-control.d.ts +13 -4
- package/components/controls/phi-ribbon-control.js +10 -6
- package/components/controls/phi-ribbon-wrap-control.d.ts +26 -0
- package/components/controls/phi-ribbon-wrap-control.js +25 -0
- package/components/controls/phi-select-control.js +5 -2
- package/components/controls/phi-series-chart-control.d.ts +48 -0
- package/components/controls/phi-series-chart-control.js +82 -0
- package/components/controls/phi-text-control.js +57 -3
- package/components/controls/phi-tree-control.js +1 -1
- package/components/controls/phi-truncated-hover-text.d.ts +15 -0
- package/components/controls/phi-truncated-hover-text.js +25 -0
- package/components/forms/link-target-form-control.d.ts +1 -1
- package/components/forms/link-target-form-control.js +2 -45
- package/components/forms/phi-form-widget-frame.js +4 -2
- package/components/layouts/clients/phi-three-column-layout-client.js +10 -1
- package/components/navigation/phi-link.js +6 -1
- package/components/regions/clients/cms-region-container-client.js +6 -1
- package/components/regions/phi-cms-region-container.js +3 -0
- package/components/regions/phi-cms-region-shell.d.ts +12 -1
- package/components/regions/phi-cms-region-shell.js +81 -19
- package/components/root/phi-config-provider.d.ts +13 -0
- package/components/root/phi-config-provider.js +12 -1
- package/components/root/phi-root-background.d.ts +7 -0
- package/components/root/phi-root-background.js +20 -1
- package/components/root/phi-root-theme-resolver.js +3 -0
- package/components/root/phi-shell-chrome-overlay.d.ts +12 -1
- package/components/root/phi-shell-chrome-overlay.js +19 -3
- package/components/shell/phi-antd-icon.d.ts +1 -0
- package/components/shell/phi-antd-icon.js +3 -0
- package/components/shell/use-phi-shell-sticky-top.js +61 -16
- package/components/surface/phi-surface-tone.d.ts +13 -0
- package/components/surface/phi-surface-tone.js +21 -0
- package/components/widgets/client/bullet-list-entries-editor.js +7 -1
- package/components/widgets/client/html-editor.js +1 -1
- package/components/widgets/client/shared/use-page-reference-options.d.ts +17 -0
- package/components/widgets/client/shared/use-page-reference-options.js +57 -0
- package/components/widgets/config/background.d.ts +32 -1
- package/components/widgets/config/background.js +41 -2
- package/components/widgets/label-sets/background.js +2 -0
- package/components/widgets/label-types/background.d.ts +1 -0
- package/components/widgets/label-types/background.js +1 -0
- package/components/widgets/shared/bullet-list-vocabulary.d.ts +10 -0
- package/components/widgets/shared/bullet-list-vocabulary.js +5 -2
- package/components/widgets/shared/card-body-client.d.ts +5 -3
- package/components/widgets/shared/card-body-client.js +49 -6
- package/components/widgets/shared/card-vocabulary.d.ts +12 -2
- package/components/widgets/shared/card-vocabulary.js +7 -0
- package/components/widgets/shared/markdown-body-client.js +39 -10
- package/helpers/cms-container-chrome.js +6 -1
- package/helpers/locale.d.ts +8 -0
- package/helpers/locale.js +18 -0
- package/helpers/shell-region-style.js +2 -1
- package/helpers/surface-style.d.ts +12 -1
- package/helpers/surface-style.js +28 -2
- package/module-dashboard-cards.d.ts +1 -1
- package/module-dashboard-cards.js +1 -1
- package/package.json +1 -1
- package/plugins/runtime-modules/area-definitions.d.ts +0 -42
- package/plugins/runtime-modules/area-definitions.js +3 -8
- package/plugins/runtime-modules/builder/clients/inspector-config-field.js +20 -1
- package/plugins/runtime-modules/builder/clients/layout-scaffold-overlays.js +10 -5
- package/plugins/runtime-modules/builder/edit-scaffold-drawer.js +3 -2
- package/plugins/runtime-modules/builder/render-root-node-scaffold.js +25 -15
- package/plugins/runtime-modules/builder/trees/phi-default-builder-area-preset-tree.js +4 -0
- package/plugins/runtime-modules/builder/widgets/structure-region/built-in.js +15 -4
- package/plugins/runtime-modules/client-authoring-module.d.ts +8 -1
- package/plugins/runtime-modules/client-authoring-module.js +13 -3
- package/plugins/runtime-modules/core/widgets/brand/client.d.ts +12 -3
- package/plugins/runtime-modules/core/widgets/brand/client.js +3 -11
- package/plugins/runtime-modules/core/widgets/brand/home.d.ts +15 -0
- package/plugins/runtime-modules/core/widgets/brand/home.js +18 -0
- package/plugins/runtime-modules/core/widgets/brand/plugin.js +3 -2
- package/plugins/runtime-modules/core/widgets/brand/server.d.ts +2 -2
- package/plugins/runtime-modules/core/widgets/brand/server.js +2 -2
- package/plugins/runtime-modules/core/widgets/card/config.d.ts +6 -1
- package/plugins/runtime-modules/core/widgets/card/config.js +19 -3
- package/plugins/runtime-modules/core/widgets/card/editor.js +13 -3
- package/plugins/runtime-modules/core/widgets/card/server.js +1 -0
- package/plugins/runtime-modules/core/widgets/description/client.js +3 -3
- package/plugins/runtime-modules/core/widgets/description/config.d.ts +1 -0
- package/plugins/runtime-modules/core/widgets/description/editor.js +3 -2
- package/plugins/runtime-modules/core/widgets/description/server.js +3 -2
- package/plugins/runtime-modules/core/widgets/entry-list/config.d.ts +1 -0
- package/plugins/runtime-modules/core/widgets/gallery/config.d.ts +1 -0
- package/plugins/runtime-modules/core/widgets/header-navigation/client.js +2 -2
- package/plugins/runtime-modules/core/widgets/html/client.js +1 -1
- package/plugins/runtime-modules/core/widgets/markdown/config.d.ts +320 -2
- package/plugins/runtime-modules/core/widgets/markdown/config.js +18 -4
- package/plugins/runtime-modules/core/widgets/markdown/editor.js +9 -1
- package/plugins/runtime-modules/core/widgets/markdown-toc/client.js +6 -1
- package/plugins/runtime-modules/core/widgets/markdown-toc/config.d.ts +14 -0
- package/plugins/runtime-modules/core/widgets/markdown-toc/config.js +14 -2
- package/plugins/runtime-modules/core/widgets/quick-links/authoring.js +7 -2
- package/plugins/runtime-modules/core/widgets/quick-links/client.js +2 -2
- package/plugins/runtime-modules/core/widgets/quick-links/items.d.ts +0 -2
- package/plugins/runtime-modules/core/widgets/quick-links/items.js +0 -15
- package/plugins/runtime-modules/core/widgets/result/config.d.ts +1 -0
- package/plugins/runtime-modules/core/widgets/sidebar-navigation/authoring.js +1 -1
- package/plugins/runtime-modules/core/widgets/sidebar-navigation/client.js +1 -1
- package/plugins/runtime-modules/core/widgets/simple-text/client.js +8 -3
- package/plugins/runtime-modules/core/widgets/simple-text/config.d.ts +1 -0
- package/plugins/runtime-modules/core/widgets/simple-text/editor.js +2 -2
- package/plugins/runtime-modules/core/widgets/table/config.d.ts +3 -0
- package/plugins/runtime-modules/dashboard/card-rows.d.ts +3 -1
- package/plugins/runtime-modules/dashboard/card-rows.js +30 -2
- package/plugins/runtime-modules/dashboard/clients/dashboard-card-view.js +29 -7
- package/plugins/runtime-modules/dashboard/trees/phi-default-builder-dashboard-page-tree.js +5 -40
- package/plugins/runtime-modules/theme/widgets/brand-controls/client.js +19 -15
- package/plugins/runtime-modules/theme/widgets/brand-controls/config.js +5 -9
- package/server-helpers/cms-link-targets.js +6 -0
- package/styles/controls.css +64 -2
- package/styles/layout.css +45 -2
- package/styles/shell.css +73 -0
- package/theme/phi-antd-token-resolver.js +20 -1
- package/theme/phi-theme-tokens.d.ts +1 -1
- package/theme/phi-theme-tokens.js +1 -0
- package/theme/phi-theme.d.ts +5 -0
- package/theme/phi-theme.js +21 -0
- package/theme/phi-tokens.d.ts +16 -15
- package/theme/phi-tokens.js +16 -15
- package/types/cms-plugins.d.ts +2 -0
- package/types/dashboard-cards.d.ts +39 -4
- package/types/dashboard-cards.js +6 -0
- package/types/site-theme.d.ts +10 -2
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { cloneElement, isValidElement, Suspense, useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
-
import {
|
|
4
|
+
import { usePhiPageModeToken } from "../../../components/root/phi-config-provider";
|
|
5
|
+
import { usePhiPageModeTone } from "../../../components/surface/phi-surface-tone";
|
|
5
6
|
import { PhiEditScaffoldDrawer } from "./edit-scaffold-drawer";
|
|
6
7
|
import { comparePhiCmsInstanceIds } from "../../../types/cms-instance-id";
|
|
7
8
|
import { PhiSlotChildFrame } from "../../../plugins/runtime/phi-slot-child-frame";
|
|
@@ -10,7 +11,7 @@ import { PhiPlusButtonWidget, } from "./clients/layout-scaffold-buttons";
|
|
|
10
11
|
import { usePhiStructureDroppable, } from "./structure-dnd";
|
|
11
12
|
import { PhiInlineTextEditor } from "../../../components/controls/phi-inline-text-editor";
|
|
12
13
|
import { PhiButtonControl } from "../../../components/controls/phi-button-control";
|
|
13
|
-
import {
|
|
14
|
+
import { usePhiAuthoringLayoutDefinitionResolver, usePhiRuntimeModuleAuthoringRegistration, } from "../../../plugins/runtime-modules/client-authoring-module";
|
|
14
15
|
import { usePhiRuntimeModuleState } from "../../../components/runtime/runtime-module-context";
|
|
15
16
|
import { PhiCmsRenderDiagnostic } from "../../../components/cms/phi-cms-render-diagnostic";
|
|
16
17
|
import { PhiCmsRenderErrorBoundary } from "../../../components/cms/phi-cms-render-error-boundary";
|
|
@@ -116,7 +117,8 @@ function PhiAuthoringWidgetLoader({ widget, config, slotSizePolicy, demandContro
|
|
|
116
117
|
function PhiAuthoringLayoutLoader({ type, kind, blockId, children, }) {
|
|
117
118
|
const activeModules = usePhiRuntimeModuleState();
|
|
118
119
|
const layoutType = resolvePhiAuthoringLayoutType(type);
|
|
119
|
-
const
|
|
120
|
+
const resolveLayoutDefinition = usePhiAuthoringLayoutDefinitionResolver();
|
|
121
|
+
const definition = resolveLayoutDefinition(layoutType);
|
|
120
122
|
if (!definition) {
|
|
121
123
|
return _jsx(PhiCmsRenderDiagnostic, { issue: {
|
|
122
124
|
code: "missing-renderer",
|
|
@@ -138,7 +140,7 @@ function PhiAuthoringLayoutLoader({ type, kind, blockId, children, }) {
|
|
|
138
140
|
} });
|
|
139
141
|
}
|
|
140
142
|
const LazyAuthoringLayout = definition.component;
|
|
141
|
-
return (_jsx(PhiCmsRenderErrorBoundary, { kind: kind, blockId: blockId, typeKey: type, moduleId: definition.ownerModuleId, children: _jsx(Suspense, { fallback: null, children: _jsx(LazyAuthoringLayout, { children: (plugin) => children(plugin, definition, activeModules.widgetDefinitionsByType) }) }) }));
|
|
143
|
+
return (_jsx(PhiCmsRenderErrorBoundary, { kind: kind, blockId: blockId, typeKey: type, moduleId: definition.ownerModuleId, children: _jsx(Suspense, { fallback: null, children: _jsx(LazyAuthoringLayout, { children: (plugin) => children(plugin, definition, activeModules.widgetDefinitionsByType, resolveLayoutDefinition) }) }) }));
|
|
142
144
|
}
|
|
143
145
|
function resolveRootNodeEditSlotAnchor(rootNode) {
|
|
144
146
|
return rootNode.editSlotAnchor ?? undefined;
|
|
@@ -206,10 +208,12 @@ function resolveEffectsPreviewDurationMs(config) {
|
|
|
206
208
|
}
|
|
207
209
|
function PhiEffectsPreviewButton({ config, blockKind, onRun, }) {
|
|
208
210
|
const previewLabel = formatEffectsPreviewLabel(config, blockKind);
|
|
211
|
+
// In the editor's mode, not a tone's: the button is the Builder's, whatever mode the Widget is drawn in.
|
|
212
|
+
const pageModeTone = usePhiPageModeTone();
|
|
209
213
|
return (_jsx("span", { style: { display: "contents" }, onMouseDown: (event) => {
|
|
210
214
|
event.preventDefault();
|
|
211
215
|
event.stopPropagation();
|
|
212
|
-
}, onClick: (event) => event.stopPropagation(), children: _jsx(PhiButtonControl, { className: "phi-layout-affordance phi-layout-affordance--effects-preview", ariaLabel: previewLabel, tooltip: previewLabel, icon: _jsx(PhiIcon, { name: "node-index", size: "inherit" }), type: "text", size: "small", onClick: onRun, style: {
|
|
216
|
+
}, onClick: (event) => event.stopPropagation(), children: pageModeTone.wrap(_jsx(PhiButtonControl, { className: "phi-layout-affordance phi-layout-affordance--effects-preview", ariaLabel: previewLabel, tooltip: previewLabel, icon: _jsx(PhiIcon, { name: "node-index", size: "inherit" }), type: "text", size: "small", onClick: onRun, style: {
|
|
213
217
|
position: "absolute",
|
|
214
218
|
top: "50%",
|
|
215
219
|
left: "50%",
|
|
@@ -219,7 +223,7 @@ function PhiEffectsPreviewButton({ config, blockKind, onRun, }) {
|
|
|
219
223
|
// whose children `styles/layout.css` stretches, and a stylesheet answer did not hold there.
|
|
220
224
|
width: "var(--ant-control-height)",
|
|
221
225
|
"--phi-layout-affordance-size": "var(--ant-control-height)",
|
|
222
|
-
} }) }));
|
|
226
|
+
} })) }));
|
|
223
227
|
}
|
|
224
228
|
function PhiSignalWiringToolButton({ onOpen }) {
|
|
225
229
|
return (_jsx("span", { style: { display: "contents" }, onPointerDown: (event) => {
|
|
@@ -264,7 +268,8 @@ function findPhiWidgetAuthoringTextTarget(container, clientX, clientY) {
|
|
|
264
268
|
}) ?? null;
|
|
265
269
|
}
|
|
266
270
|
function PhiWidgetEffectsPreviewFrame({ kind, slotSizePolicy, surfacePolicy, blockId, regionKey, config: committedConfig, title, scaffoldLabel, editorInteraction = "inert", onSelect, dropTarget, tools, style, children, }) {
|
|
267
|
-
|
|
271
|
+
// The editor's colours, not a tone's: a Widget inside a toned Surface is marked in the page's mode.
|
|
272
|
+
const token = usePhiPageModeToken();
|
|
268
273
|
const selected = usePhiDeveloperBuilderStateValue((state) => blockId != null &&
|
|
269
274
|
regionKey != null &&
|
|
270
275
|
state.selectedRootRegionKey === regionKey &&
|
|
@@ -525,10 +530,6 @@ function PhiLayoutEffectsPreviewFrame({ kind, slotSizePolicy, blockId, config: c
|
|
|
525
530
|
});
|
|
526
531
|
} })) : null] }, hasPreviewEffects ? `layout-effects-preview-${blockId ?? "root"}-${previewRunId}` : undefined));
|
|
527
532
|
}
|
|
528
|
-
function PhiAuthoringLayoutEffectsPreviewFrame({ type, ...props }) {
|
|
529
|
-
const definition = usePhiAuthoringLayoutDefinition(resolvePhiAuthoringLayoutType(type));
|
|
530
|
-
return (_jsx(PhiLayoutEffectsPreviewFrame, { ...props, slotSizePolicy: definition?.slotSizePolicy }));
|
|
531
|
-
}
|
|
532
533
|
/**
|
|
533
534
|
* The band under a scaffold: what this node is called, and then what it is.
|
|
534
535
|
*
|
|
@@ -821,11 +822,20 @@ export function renderPhiRootNodeScaffold(rootNode, onOpenInsert, onOpenInspecto
|
|
|
821
822
|
nodeKind: normalizedRootNode.kind,
|
|
822
823
|
})
|
|
823
824
|
: undefined, chrome: renderRootScaffoldToolbar(), children: wrapRootScaffoldChild(child) }));
|
|
824
|
-
|
|
825
|
+
/*
|
|
826
|
+
* A nested Layout's frame states its policy on the element the parent Layout sizes its slot by.
|
|
827
|
+
*
|
|
828
|
+
* It was looked up inside that element, which is too late: the parent reads the element's props
|
|
829
|
+
* before it renders, found a Layout with no policy, took the Layout default -- fill both ways -- and
|
|
830
|
+
* let the slot grow and shrink in its column. A Grid of cards in a Flex was squeezed below its
|
|
831
|
+
* content on the canvas and the cards ran out of their scaffold, while the page laid it out at its
|
|
832
|
+
* content height, as its `fill-inline` says.
|
|
833
|
+
*/
|
|
834
|
+
const renderChildLayoutNodeWith = (resolveLayoutDefinition) => function renderChildLayoutNode(node) {
|
|
825
835
|
const nodeKind = "layout";
|
|
826
836
|
const rootProps = resolveLayoutNodeRootProps(node);
|
|
827
837
|
const rootPropsGeometry = resolvePhiRenderableBlockGeometry(normalizePhiBuilderRootNodeDraft(rootProps).rootNodeGeometry);
|
|
828
|
-
return (_jsx(
|
|
838
|
+
return (_jsx(PhiLayoutEffectsPreviewFrame, { kind: nodeKind, slotSizePolicy: resolveLayoutDefinition(resolvePhiAuthoringLayoutType(node.widgetType))?.slotSizePolicy, blockId: node.id, config: node.config, explicitInlineSize: rootPropsGeometry.explicitInline, explicitBlockSize: rootPropsGeometry.explicitBlock, children: renderPhiRootNodeScaffold({ ...rootProps, regionKey }, (options) => onOpenInsert?.({
|
|
829
839
|
...options,
|
|
830
840
|
slotIndex: options?.slotIndex ?? 0,
|
|
831
841
|
targetNodeId: node.id,
|
|
@@ -861,8 +871,8 @@ export function renderPhiRootNodeScaffold(rootNode, onOpenInsert, onOpenInspecto
|
|
|
861
871
|
renderInsertPicker: options?.renderInsertPicker,
|
|
862
872
|
}) }));
|
|
863
873
|
};
|
|
864
|
-
const scaffold = (_jsx(PhiAuthoringLayoutLoader, { type: normalizedRootNode.typeKey, kind: "layout", blockId: normalizedRootNode.id ?? -1, children: (plugin, definition, widgetDefinitionsByType) => {
|
|
865
|
-
const rendered = resolvePhiRootNodeRenderedBody(normalizedRootNode, "editor", plugin, definition, widgetDefinitionsByType, regionKey,
|
|
874
|
+
const scaffold = (_jsx(PhiAuthoringLayoutLoader, { type: normalizedRootNode.typeKey, kind: "layout", blockId: normalizedRootNode.id ?? -1, children: (plugin, definition, widgetDefinitionsByType, resolveLayoutDefinition) => {
|
|
875
|
+
const rendered = resolvePhiRootNodeRenderedBody(normalizedRootNode, "editor", plugin, definition, widgetDefinitionsByType, regionKey, renderChildLayoutNodeWith(resolveLayoutDefinition), onDeleteRootNode
|
|
866
876
|
? (widget) => onDeleteRootNode({
|
|
867
877
|
nodeId: widget.id,
|
|
868
878
|
nodeKey: widget.widgetType,
|
|
@@ -1490,14 +1490,18 @@ async function buildPhiDefaultBuilderPagePresetTemplateTree({ page, runtime, pre
|
|
|
1490
1490
|
key: "createPage",
|
|
1491
1491
|
emits: [{ capabilityId: "command", value: "createPage" }],
|
|
1492
1492
|
label: labels.pages.newPage,
|
|
1493
|
+
tooltip: labels.pages.newPage,
|
|
1493
1494
|
icon: "plus",
|
|
1495
|
+
display: "icon",
|
|
1494
1496
|
variant: "primary",
|
|
1495
1497
|
},
|
|
1496
1498
|
{
|
|
1497
1499
|
key: "editPageMeta",
|
|
1498
1500
|
emits: [{ capabilityId: "command", value: "editPageMeta" }],
|
|
1499
1501
|
label: labels.pages.pageMeta,
|
|
1502
|
+
tooltip: labels.pages.pageMeta,
|
|
1500
1503
|
icon: "edit",
|
|
1504
|
+
display: "icon",
|
|
1501
1505
|
},
|
|
1502
1506
|
{
|
|
1503
1507
|
key: "deletePage",
|
|
@@ -40,6 +40,7 @@ import { PhiTypographyControl } from "../../../../../components/controls/phi-typ
|
|
|
40
40
|
import { PhiIcon } from "../../../../../components/shell/phi-icon";
|
|
41
41
|
import { PhiSurfaceTone } from "../../../../../components/surface/phi-surface-tone";
|
|
42
42
|
import { resolvePhiSurfaceToneClassName } from "../../../../../types/surface";
|
|
43
|
+
import { PHI_SURFACE_TONE_GROUND, phiSurfaceTakesToneGround } from "../../../../../helpers/surface-style";
|
|
43
44
|
/** The Region's root Layout placed into another tree, at the slot it is moved to. */
|
|
44
45
|
function buildStructureRootLayoutNode(draft, parentLayoutNodeId, slotIndex) {
|
|
45
46
|
if (!draft.rootNode) {
|
|
@@ -353,16 +354,26 @@ export function PhiStructureRegionScaffold({ config, serverPreview, structureDra
|
|
|
353
354
|
const shouldStretchAvailableHeight = shouldFillAvailableHeight || shouldStretchContentRegion;
|
|
354
355
|
const hasExplicitSidebarWidth = isFullHeightRegion && draftGeometry.explicitInline;
|
|
355
356
|
const regionToneClassName = resolvePhiSurfaceToneClassName(effectiveDraft?.surface?.tone);
|
|
356
|
-
const slotBackgroundStyle = effectiveDraft?.surface?.background
|
|
357
|
-
? resolvePhiBackgroundWidgetStyle(effectiveDraft.surface.background)
|
|
358
|
-
: {};
|
|
359
357
|
/*
|
|
360
358
|
* Whether the author gave this Region a ground. The Builder writes a Background config onto every
|
|
361
359
|
* Region draft it persists, so only a config that paints something counts. An unauthored Region is
|
|
362
360
|
* transparent in the live Shell, and the Canvas keeps that legible: its slot paints a half-transparent
|
|
363
361
|
* container tint, so the Theme Root Background stays visible through it.
|
|
364
362
|
*/
|
|
365
|
-
const
|
|
363
|
+
const paintsAuthoredGround = phiBackgroundWidgetConfigPaintsGround(effectiveDraft?.surface?.background ?? null);
|
|
364
|
+
/*
|
|
365
|
+
* A toned Region takes its tone's ground where its Background paints none, by the rule the live Region
|
|
366
|
+
* shell follows (`phiSurfaceTakesToneGround`). The slot carries the tone's class, so the ground resolves
|
|
367
|
+
* in the tone's mode; it is a ground of the Region's own, so the Canvas tint does not cover it.
|
|
368
|
+
*/
|
|
369
|
+
const toneGround = phiSurfaceTakesToneGround(effectiveDraft?.surface?.tone, paintsAuthoredGround);
|
|
370
|
+
const hasAuthoredGround = paintsAuthoredGround || toneGround;
|
|
371
|
+
const slotBackgroundStyle = {
|
|
372
|
+
...(effectiveDraft?.surface?.background
|
|
373
|
+
? resolvePhiBackgroundWidgetStyle(effectiveDraft.surface.background)
|
|
374
|
+
: {}),
|
|
375
|
+
...(toneGround ? { backgroundColor: PHI_SURFACE_TONE_GROUND } : {}),
|
|
376
|
+
};
|
|
366
377
|
const slotBorderStyle = effectiveDraft?.surface?.border ? resolvePhiBorderWidgetStyle(effectiveDraft.surface.border) : {};
|
|
367
378
|
const regionConfig = (effectiveDraft?.regionConfig ?? null);
|
|
368
379
|
const regionPaddingStyle = resolvePhiPaddingStyle(normalizePhiPaddingWidgetConfig(effectiveDraft?.regionConfig) ?? {});
|
|
@@ -28,4 +28,11 @@ export declare function createPhiRuntimeModuleAuthoringClient({ moduleId, Widget
|
|
|
28
28
|
layouts?: readonly PhiAuthoringLayoutModuleLoader[];
|
|
29
29
|
}): ComponentType<PhiRuntimeModuleAuthoringClientProps>;
|
|
30
30
|
export declare function usePhiRuntimeModuleAuthoringRegistration(moduleId: PhiRuntimeModuleId | null | undefined): PhiRuntimeModuleAuthoringRegistration | null;
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* The authoring Layout definitions, as a lookup by type rather than one type's answer.
|
|
33
|
+
*
|
|
34
|
+
* A Layout names its nested Layouts only while it renders its children, and a parent Layout sizes each
|
|
35
|
+
* slot by the props of the element standing in it, before that element renders: a nested Layout's
|
|
36
|
+
* element has to carry its policy already, and a hook inside it would be too late.
|
|
37
|
+
*/
|
|
38
|
+
export declare function usePhiAuthoringLayoutDefinitionResolver(): (type: string) => PhiAuthoringLayoutDefinition | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { createContext, lazy, useContext, useMemo, } from "react";
|
|
3
|
+
import { createContext, lazy, useCallback, useContext, useMemo, } from "react";
|
|
4
4
|
const PhiRuntimeModuleAuthoringContext = createContext(new Map());
|
|
5
5
|
function createPhiLazyAuthoringLayout(load) {
|
|
6
6
|
return lazy(async () => {
|
|
@@ -54,8 +54,7 @@ export function usePhiRuntimeModuleAuthoringRegistration(moduleId) {
|
|
|
54
54
|
const modules = useContext(PhiRuntimeModuleAuthoringContext);
|
|
55
55
|
return moduleId ? modules.get(moduleId) ?? null : null;
|
|
56
56
|
}
|
|
57
|
-
|
|
58
|
-
const modules = useContext(PhiRuntimeModuleAuthoringContext);
|
|
57
|
+
function findPhiAuthoringLayoutDefinition(modules, type) {
|
|
59
58
|
for (const registration of modules.values()) {
|
|
60
59
|
const definition = registration.layoutsByType.get(type);
|
|
61
60
|
if (definition) {
|
|
@@ -64,3 +63,14 @@ export function usePhiAuthoringLayoutDefinition(type) {
|
|
|
64
63
|
}
|
|
65
64
|
return null;
|
|
66
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* The authoring Layout definitions, as a lookup by type rather than one type's answer.
|
|
68
|
+
*
|
|
69
|
+
* A Layout names its nested Layouts only while it renders its children, and a parent Layout sizes each
|
|
70
|
+
* slot by the props of the element standing in it, before that element renders: a nested Layout's
|
|
71
|
+
* element has to carry its policy already, and a hook inside it would be too late.
|
|
72
|
+
*/
|
|
73
|
+
export function usePhiAuthoringLayoutDefinitionResolver() {
|
|
74
|
+
const modules = useContext(PhiRuntimeModuleAuthoringContext);
|
|
75
|
+
return useCallback((type) => findPhiAuthoringLayoutDefinition(modules, type), [modules]);
|
|
76
|
+
}
|
|
@@ -11,10 +11,19 @@ export type PhiBrandWidgetConfig = {
|
|
|
11
11
|
export declare const PHI_BRAND_LINE_FALLBACK_ICONS: Record<PhiBrandWidgetLineMode, string>;
|
|
12
12
|
export type PhiBrandWidgetClientProps = PhiClientBlockBaseProps<PhiNoLabels, PhiBrandWidgetConfig> & {
|
|
13
13
|
fallbackTitle?: ReactNode;
|
|
14
|
-
/**
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Where the Brand leads, resolved on the Server: the Theme's `homeLinkTarget`, or the Site root in the
|
|
16
|
+
* language being read. A Page reference means nothing in a browser, so the address travels instead.
|
|
17
|
+
*/
|
|
18
|
+
home?: PhiBrandWidgetHome;
|
|
16
19
|
interactive?: boolean;
|
|
17
20
|
};
|
|
21
|
+
/** A ready address, and whether it leaves the Site -- the shape `resolvePhiLinkHref` answers. */
|
|
22
|
+
export type PhiBrandWidgetHome = {
|
|
23
|
+
href: string;
|
|
24
|
+
newTab: boolean;
|
|
25
|
+
external: boolean;
|
|
26
|
+
};
|
|
18
27
|
/**
|
|
19
28
|
* The Brand in the Site frame: what `PhiBrandControl` shows, and where clicking it leads.
|
|
20
29
|
*
|
|
@@ -22,4 +31,4 @@ export type PhiBrandWidgetClientProps = PhiClientBlockBaseProps<PhiNoLabels, Phi
|
|
|
22
31
|
* because the Theme workspace preview shows the same Brand without being in a frame and without
|
|
23
32
|
* leading anywhere.
|
|
24
33
|
*/
|
|
25
|
-
export declare function PhiBrandWidgetClient({ config,
|
|
34
|
+
export declare function PhiBrandWidgetClient({ config, home, fallbackTitle, interactive, }: PhiBrandWidgetClientProps): import("react").JSX.Element | null;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import Link from "next/link";
|
|
4
3
|
import { PhiBrandControl, PhiBrandLineControl, phiBrandControlIsEmpty, } from "../../../../../components/controls/phi-brand-control";
|
|
5
4
|
import { isPhiBrandWidgetLineMode, } from "./mode";
|
|
6
5
|
import { usePhiConfig } from "../../../../../components/root/phi-config-provider";
|
|
7
6
|
import { usePhiSiteBrand } from "../../../../../components/root/phi-root-live-theme-provider";
|
|
8
7
|
import { resolvePhiWidgetFontFamily } from "../../../../../components/widgets/helpers/font-family";
|
|
9
8
|
import { resolvePhiWidgetFontSize } from "../../../../../components/widgets/helpers/font-size";
|
|
10
|
-
import {
|
|
9
|
+
import { PhiLink } from "../../../../../components/navigation/phi-link";
|
|
11
10
|
/** What a line shows in front of itself where the Brand names no icon for it. */
|
|
12
11
|
export const PHI_BRAND_LINE_FALLBACK_ICONS = {
|
|
13
12
|
slogan: "antd:star",
|
|
@@ -20,7 +19,7 @@ export const PHI_BRAND_LINE_FALLBACK_ICONS = {
|
|
|
20
19
|
* because the Theme workspace preview shows the same Brand without being in a frame and without
|
|
21
20
|
* leading anywhere.
|
|
22
21
|
*/
|
|
23
|
-
export function PhiBrandWidgetClient({ config,
|
|
22
|
+
export function PhiBrandWidgetClient({ config, home, fallbackTitle, interactive = true, }) {
|
|
24
23
|
const brand = usePhiSiteBrand();
|
|
25
24
|
// The Theme's mode, not the Widget's: one picks the Logo's picture, the other what is drawn at all.
|
|
26
25
|
const { fonts, token, mode: themeMode } = usePhiConfig();
|
|
@@ -54,13 +53,6 @@ export function PhiBrandWidgetClient({ config, locale, fallbackTitle, interactiv
|
|
|
54
53
|
if (phiBrandControlIsEmpty(presentation)) {
|
|
55
54
|
return null;
|
|
56
55
|
}
|
|
57
|
-
/*
|
|
58
|
-
* The front page in the language being read. `/` alone is answered with a redirect to the visitor's
|
|
59
|
-
* preferred language, so a reader of `/de` could be led to `/en`; and Next's prefetch of it, which loses
|
|
60
|
-
* its `_rsc` on that redirect, came back with the static tree's own address and a 404
|
|
61
|
-
* (AUDIT-2026-10-13, PM15).
|
|
62
|
-
*/
|
|
63
|
-
const homeHref = brand?.homeHref?.trim() || (locale ? localizePath(locale, "/") : "/");
|
|
64
56
|
const linkStyle = {
|
|
65
57
|
color: "inherit",
|
|
66
58
|
textDecoration: "none",
|
|
@@ -73,5 +65,5 @@ export function PhiBrandWidgetClient({ config, locale, fallbackTitle, interactiv
|
|
|
73
65
|
if (!interactive) {
|
|
74
66
|
return (_jsx("span", { "aria-disabled": "true", style: linkStyle, children: _jsx(PhiBrandControl, { ...presentation }) }));
|
|
75
67
|
}
|
|
76
|
-
return (_jsx(
|
|
68
|
+
return (_jsx(PhiLink, { href: home?.href ?? "/", external: home?.external, newTab: home?.newTab, style: linkStyle, children: _jsx(PhiBrandControl, { ...presentation }) }));
|
|
77
69
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PhiBlockRuntime } from "../../../../../types";
|
|
2
|
+
import { type PhiResolvedLinkTargets } from "../../../../../types/references";
|
|
3
|
+
import type { PhiBrandWidgetHome } from "./client";
|
|
4
|
+
/**
|
|
5
|
+
* Where the Brand leads, as an address: the Theme's target, resolved in this render's batch of link
|
|
6
|
+
* targets (`resolvePhiCmsTreeLinkTargets` collects the Brand's beside the tree's), or the Site root.
|
|
7
|
+
*
|
|
8
|
+
* The root is the root in the language being read. `/` alone is answered with a redirect to the
|
|
9
|
+
* visitor's preferred language, so a reader of `/de` could be led to `/en`; and Next's prefetch of it,
|
|
10
|
+
* which loses its `_rsc` on that redirect, came back with the static tree's own address and a 404
|
|
11
|
+
* (AUDIT-2026-10-13, PM15). A target that does not resolve -- a Page deleted, unpublished, or brought by
|
|
12
|
+
* a Module that is switched off -- leads there too: the Brand is the way home and always leads somewhere.
|
|
13
|
+
* A resolved Page path is already locale- and Area-correct (`REFERENCES.md`).
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolvePhiBrandWidgetHome(runtime: Pick<PhiBlockRuntime, "site" | "locale">, links: PhiResolvedLinkTargets | null | undefined): PhiBrandWidgetHome;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { resolvePhiLinkHref } from "../../../../../helpers/link-target";
|
|
2
|
+
import { localizePath } from "../../../../../helpers/locale";
|
|
3
|
+
import { readPhiLinkTarget } from "../../../../../types/references";
|
|
4
|
+
/**
|
|
5
|
+
* Where the Brand leads, as an address: the Theme's target, resolved in this render's batch of link
|
|
6
|
+
* targets (`resolvePhiCmsTreeLinkTargets` collects the Brand's beside the tree's), or the Site root.
|
|
7
|
+
*
|
|
8
|
+
* The root is the root in the language being read. `/` alone is answered with a redirect to the
|
|
9
|
+
* visitor's preferred language, so a reader of `/de` could be led to `/en`; and Next's prefetch of it,
|
|
10
|
+
* which loses its `_rsc` on that redirect, came back with the static tree's own address and a 404
|
|
11
|
+
* (AUDIT-2026-10-13, PM15). A target that does not resolve -- a Page deleted, unpublished, or brought by
|
|
12
|
+
* a Module that is switched off -- leads there too: the Brand is the way home and always leads somewhere.
|
|
13
|
+
* A resolved Page path is already locale- and Area-correct (`REFERENCES.md`).
|
|
14
|
+
*/
|
|
15
|
+
export function resolvePhiBrandWidgetHome(runtime, links) {
|
|
16
|
+
return resolvePhiLinkHref(readPhiLinkTarget(runtime.site.theme?.brand?.homeLinkTarget), links)
|
|
17
|
+
?? { href: localizePath(runtime.locale.current, "/"), newTab: false, external: false };
|
|
18
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { resolvePhiBrandWordmarkText } from "../../../../../helpers/brand-wordmark";
|
|
3
|
+
import { resolvePhiBrandWidgetHome } from "./home";
|
|
3
4
|
import { PhiBrandWidget } from "./server";
|
|
4
5
|
import { PHI_BRAND_WIDGET_DEFINITION, PHI_BRAND_WIDGET_PLUGIN_TYPE, } from "./config";
|
|
5
6
|
/*
|
|
@@ -13,7 +14,7 @@ import { PHI_BRAND_WIDGET_DEFINITION, PHI_BRAND_WIDGET_PLUGIN_TYPE, } from "./co
|
|
|
13
14
|
*/
|
|
14
15
|
export const PHI_BRAND_WIDGET_PLUGIN = {
|
|
15
16
|
...PHI_BRAND_WIDGET_DEFINITION,
|
|
16
|
-
render: ({ widget, config, runtime }) => (_jsx(PhiBrandWidget, { fallbackTitle: resolvePhiBrandWordmarkText(runtime),
|
|
17
|
-
renderPreview: ({ widget, config, runtime }) => (_jsx(PhiBrandWidget, { fallbackTitle: resolvePhiBrandWordmarkText(runtime),
|
|
17
|
+
render: ({ widget, config, runtime, links }) => (_jsx(PhiBrandWidget, { fallbackTitle: resolvePhiBrandWordmarkText(runtime), home: resolvePhiBrandWidgetHome(runtime, links), mode: config.mode, fontFamily: config.fontFamily, fontSize: config.fontSize }, `widget-${widget.id}`)),
|
|
18
|
+
renderPreview: ({ widget, config, runtime, links }) => (_jsx(PhiBrandWidget, { fallbackTitle: resolvePhiBrandWordmarkText(runtime), home: resolvePhiBrandWidgetHome(runtime, links), mode: config.mode, fontFamily: config.fontFamily, fontSize: config.fontSize, interactive: false }, `widget-${widget.id}`)),
|
|
18
19
|
};
|
|
19
20
|
export { PHI_BRAND_WIDGET_PLUGIN_TYPE };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { PhiBrandWidgetClientProps, PhiBrandWidgetConfig } from "./client";
|
|
2
|
-
export type PhiBrandWidgetProps = Pick<PhiBrandWidgetClientProps, "fallbackTitle" | "interactive" | "
|
|
3
|
-
export declare function PhiBrandWidget({ fallbackTitle, interactive,
|
|
2
|
+
export type PhiBrandWidgetProps = Pick<PhiBrandWidgetClientProps, "fallbackTitle" | "interactive" | "home"> & PhiBrandWidgetConfig;
|
|
3
|
+
export declare function PhiBrandWidget({ fallbackTitle, interactive, home, mode, fontFamily, fontSize, }: PhiBrandWidgetProps): import("react").JSX.Element;
|
|
@@ -5,12 +5,12 @@ import { PhiRuntimeModuleRenderClientHost } from "../../../../../components/runt
|
|
|
5
5
|
* The Brand itself is not read here. It comes from the root on the Client (`usePhiSiteBrand`), which
|
|
6
6
|
* holds it with the Theme Set's Logo folded in and follows a live Theme draft.
|
|
7
7
|
*/
|
|
8
|
-
export function PhiBrandWidget({ fallbackTitle, interactive,
|
|
8
|
+
export function PhiBrandWidget({ fallbackTitle, interactive, home, mode, fontFamily, fontSize, }) {
|
|
9
9
|
const config = { mode, fontFamily, fontSize };
|
|
10
10
|
return (_jsx(PhiRuntimeModuleRenderClientHost, { type: PhiCmsWidgetType.Brand, componentProps: {
|
|
11
11
|
config,
|
|
12
12
|
fallbackTitle,
|
|
13
|
-
|
|
13
|
+
home,
|
|
14
14
|
interactive,
|
|
15
15
|
} }));
|
|
16
16
|
}
|
|
@@ -2,7 +2,7 @@ import type { PhiCmsConfigField } from "../../../../../types/cms-plugins";
|
|
|
2
2
|
import type { PhiImageAssetVariantKeyValue } from "../../../../../types/media";
|
|
3
3
|
import { type PhiLinkTarget } from "../../../../../types/references";
|
|
4
4
|
import { type PhiBulletListEntry, type PhiBulletListStyle } from "../../../../../components/widgets/shared/bullet-list-vocabulary";
|
|
5
|
-
import { type PhiCardBillingPeriod, type PhiCardHeadingLevel, type PhiCardHoverEffect, type PhiCardIconPlacement, type PhiCardTextAlign, type PhiCardVariant, type PhiCardWidgetBody } from "../../../../../components/widgets/shared/card-vocabulary";
|
|
5
|
+
import { type PhiCardBillingPeriod, type PhiCardHeadingLevel, type PhiCardHoverEffect, type PhiCardIconPlacement, type PhiCardTextAlign, type PhiCardBadgeStyle, type PhiCardVariant, type PhiCardWidgetBody } from "../../../../../components/widgets/shared/card-vocabulary";
|
|
6
6
|
import { type PhiSignalRouteSet } from "../../../../../types/signals";
|
|
7
7
|
import { type PhiCmsWidgetConfigBase } from "../../../../../components/widgets/config/parser-primitives";
|
|
8
8
|
type PhiCardImageSource = {
|
|
@@ -27,6 +27,8 @@ export type PhiCmsCardWidgetConfig = PhiCmsWidgetConfigBase & PhiCardImageSource
|
|
|
27
27
|
badge?: string;
|
|
28
28
|
/** The band's colour; absent, the Theme's primary. */
|
|
29
29
|
badgeColor?: string;
|
|
30
|
+
/** How the badge is drawn (`PHI_CARD_BADGE_STYLES`); absent, `none`. */
|
|
31
|
+
badgeStyle?: PhiCardBadgeStyle;
|
|
30
32
|
/** A `plan` body's prices, never translated, and what each is paid per (`/ month`), which is. */
|
|
31
33
|
price?: string;
|
|
32
34
|
pricePeriod?: string;
|
|
@@ -106,6 +108,7 @@ export declare const PHI_CARD_WIDGET_DEFINITION: {
|
|
|
106
108
|
imageOpacity: number;
|
|
107
109
|
headingLevel: "h3";
|
|
108
110
|
textAlign: "start";
|
|
111
|
+
badgeStyle: "none";
|
|
109
112
|
};
|
|
110
113
|
creationConfig: {
|
|
111
114
|
surface: {
|
|
@@ -130,6 +133,7 @@ export declare const PHI_CARD_WIDGET_DEFINITION: {
|
|
|
130
133
|
imageOpacity: number;
|
|
131
134
|
headingLevel: "h3";
|
|
132
135
|
textAlign: "start";
|
|
136
|
+
badgeStyle: "none";
|
|
133
137
|
};
|
|
134
138
|
fields: (({
|
|
135
139
|
key: string;
|
|
@@ -142,6 +146,7 @@ export declare const PHI_CARD_WIDGET_DEFINITION: {
|
|
|
142
146
|
} & {
|
|
143
147
|
type: "string" | "url" | "icon";
|
|
144
148
|
emptyValue?: "";
|
|
149
|
+
placeholder?: string;
|
|
145
150
|
}) | ({
|
|
146
151
|
key: string;
|
|
147
152
|
label: string;
|
|
@@ -4,7 +4,7 @@ import { readPhiLinkTarget } from "../../../../../types/references";
|
|
|
4
4
|
import { readPhiMediaImageSourceConfig } from "../../../../../components/widgets/config/image-source-parser";
|
|
5
5
|
import { PHI_SURFACE_CARD } from "../../../../../helpers/surface-presets";
|
|
6
6
|
import { buildPhiBulletListFields, readPhiBulletListEntries, readPhiBulletListStyle, } from "../../../../../components/widgets/shared/bullet-list-vocabulary";
|
|
7
|
-
import { PHI_CARD_HEADING_LEVELS, PHI_CARD_HOVER_EFFECTS, PHI_CARD_ICON_PLACEMENTS, PHI_CARD_TEXT_ALIGNS, PHI_CARD_VARIANTS, readPhiCardBillingPeriod, } from "../../../../../components/widgets/shared/card-vocabulary";
|
|
7
|
+
import { PHI_CARD_HEADING_LEVELS, PHI_CARD_HOVER_EFFECTS, PHI_CARD_ICON_PLACEMENTS, PHI_CARD_TEXT_ALIGNS, PHI_CARD_BADGE_STYLES, PHI_CARD_VARIANTS, readPhiCardBillingPeriod, } from "../../../../../components/widgets/shared/card-vocabulary";
|
|
8
8
|
import { readPhiControlLabel } from "../../../../../components/widgets/config/common-action-field";
|
|
9
9
|
import { PHI_CARD_SIGNALS } from "../../../../../components/widgets/signals/control-signal-capabilities";
|
|
10
10
|
import { readPhiSignalRouteSet } from "../../../../../types/signals";
|
|
@@ -27,6 +27,7 @@ function parsePhiCmsCardWidgetConfig(config) {
|
|
|
27
27
|
value: readString(config.value),
|
|
28
28
|
badge: readString(config.badge),
|
|
29
29
|
badgeColor: readString(config.badgeColor),
|
|
30
|
+
badgeStyle: readChoice(readString(config.badgeStyle), PHI_CARD_BADGE_STYLES),
|
|
30
31
|
price: readString(config.price),
|
|
31
32
|
pricePeriod: readString(config.pricePeriod),
|
|
32
33
|
priceYearly: readString(config.priceYearly),
|
|
@@ -68,6 +69,7 @@ const PHI_CARD_DEFAULT_CONFIG = {
|
|
|
68
69
|
imageOpacity: 100,
|
|
69
70
|
headingLevel: "h3",
|
|
70
71
|
textAlign: "start",
|
|
72
|
+
badgeStyle: "none",
|
|
71
73
|
};
|
|
72
74
|
export const PHI_CARD_WIDGET_DEFINITION = {
|
|
73
75
|
kind: "widget",
|
|
@@ -259,12 +261,26 @@ export const PHI_CARD_WIDGET_DEFINITION = {
|
|
|
259
261
|
],
|
|
260
262
|
},
|
|
261
263
|
{ key: "highlight", type: "boolean", label: "Highlight" },
|
|
262
|
-
/* The band's words are written on the card;
|
|
264
|
+
/* The band's words are written on the card; how it is drawn and its colour are asked here. */
|
|
265
|
+
{
|
|
266
|
+
key: "badgeStyle",
|
|
267
|
+
type: "choice",
|
|
268
|
+
heading: "Badge",
|
|
269
|
+
label: "Style",
|
|
270
|
+
presentation: "segmented",
|
|
271
|
+
description: "Corner: a band in the card's top corner. Tag: a rounded mark a little down the edge. Ribbon: hangs over the card's edge with a fold, and needs room beside the card -- a Layout that clips at the card's edge cuts it off. None: hides the badge and keeps its words.",
|
|
272
|
+
options: [
|
|
273
|
+
{ value: "none", label: "None" },
|
|
274
|
+
{ value: "corner", label: "Corner" },
|
|
275
|
+
{ value: "tag", label: "Tag" },
|
|
276
|
+
{ value: "ribbon", label: "Ribbon" },
|
|
277
|
+
],
|
|
278
|
+
},
|
|
263
279
|
{
|
|
264
280
|
key: "badgeColor",
|
|
265
281
|
type: "color",
|
|
266
|
-
heading: "Badge",
|
|
267
282
|
label: "Color",
|
|
283
|
+
visibleWhen: { field: "badgeStyle", notEquals: "none" },
|
|
268
284
|
mode: "single",
|
|
269
285
|
defaultToken: "colorPrimary",
|
|
270
286
|
},
|
|
@@ -36,7 +36,11 @@ const PHI_CARD_BODY_TEXT_SLOTS = {
|
|
|
36
36
|
* Slots a card often goes without. Empty, they stand on the canvas only while the card is under the
|
|
37
37
|
* pointer or being written in (`styles/layout.css`), so an empty card does not read as four empty lines.
|
|
38
38
|
*/
|
|
39
|
-
|
|
39
|
+
/*
|
|
40
|
+
* Not the badge: it is drawn only once a style is chosen for it, and a band or ribbon that disappeared
|
|
41
|
+
* whenever the pointer left the card left nothing to see of the choice just made.
|
|
42
|
+
*/
|
|
43
|
+
const PHI_CARD_OPTIONAL_TEXT_SLOTS = new Set(["eyebrow", "meta"]);
|
|
40
44
|
/**
|
|
41
45
|
* One of the card's words, edited where it stands: underlined, in the type of the text it edits.
|
|
42
46
|
*
|
|
@@ -55,7 +59,12 @@ function PhiCardInlineText({ slot, text, onCommit, }) {
|
|
|
55
59
|
* into beside them, and its wrapping is the card's own anyway. The text still wraps and aligns as
|
|
56
60
|
* the card sets it.
|
|
57
61
|
*/
|
|
58
|
-
|
|
62
|
+
/*
|
|
63
|
+
* The ground stays the card's, here as well as on the text: with a clear button the field is a
|
|
64
|
+
* wrapper around the text, and Ant Design's underlined wrapper paints the container ground -- white
|
|
65
|
+
* over the badge's band, whose light words then vanished on it.
|
|
66
|
+
*/
|
|
67
|
+
style: { width: "100%", backgroundColor: "transparent" },
|
|
59
68
|
/* The text's own type, and the page showing through: an edit in place reads like what it edits. */
|
|
60
69
|
inputStyle: {
|
|
61
70
|
paddingInline: 0,
|
|
@@ -116,7 +125,7 @@ export function PhiCardWidgetEditor({ config, onChange }) {
|
|
|
116
125
|
&& presentation.width != null
|
|
117
126
|
&& canvasWidth != null
|
|
118
127
|
&& presentation.width < canvasWidth;
|
|
119
|
-
return (_jsxs("div", { ref: canvasRef, className: "phi-card-editor", style: { position: "relative", width: "100%" }, children: [_jsx(PhiCardWidgetClient, { labels: {
|
|
128
|
+
return (_jsxs("div", { ref: canvasRef, className: "phi-card-editor", style: { position: "relative", display: "flex", width: "100%" }, children: [_jsx(PhiCardWidgetClient, { labels: {
|
|
120
129
|
eyebrow: config.eyebrow,
|
|
121
130
|
title: config.title,
|
|
122
131
|
description: config.description,
|
|
@@ -165,6 +174,7 @@ export function PhiCardWidgetEditor({ config, onChange }) {
|
|
|
165
174
|
body: config.body,
|
|
166
175
|
highlight: config.highlight,
|
|
167
176
|
badgeColor: config.badgeColor,
|
|
177
|
+
badgeStyle: config.badgeStyle,
|
|
168
178
|
billingPeriod: config.billingPeriod,
|
|
169
179
|
hoverEffect: config.linkTarget ? config.hoverEffect : undefined,
|
|
170
180
|
}, ...(config.body === "plan" ? {
|
|
@@ -113,6 +113,7 @@ export async function PhiCardWidget({ labels, config, runtime, links, translate,
|
|
|
113
113
|
body: config?.body,
|
|
114
114
|
highlight: config?.highlight,
|
|
115
115
|
badgeColor: config?.badgeColor,
|
|
116
|
+
badgeStyle: config?.badgeStyle,
|
|
116
117
|
billingPeriod: config?.billingPeriod,
|
|
117
118
|
// Only a card that leads somewhere answers the pointer; the preview has no link but shows it.
|
|
118
119
|
hoverEffect: config?.linkTarget ? config.hoverEffect : undefined,
|
|
@@ -8,7 +8,6 @@ import { PhiBulletListControl } from "../../../../../components/controls/phi-bul
|
|
|
8
8
|
import { buildPhiBulletListTree, resolvePhiBulletListStyle, } from "../../../../../components/widgets/shared/bullet-list-vocabulary";
|
|
9
9
|
import { resolvePhiTextPlaceholders } from "../../../../../helpers/text-placeholders";
|
|
10
10
|
const PHI_LINE_HEIGHT_BASE = 1.5;
|
|
11
|
-
const PHI_LINE_HEIGHT_LG = 1.6;
|
|
12
11
|
const PHI_FONT_WEIGHT_STRONG = 600;
|
|
13
12
|
export function PhiDescriptionWidgetClient({ labels, config, }) {
|
|
14
13
|
const { token } = usePhiConfig();
|
|
@@ -18,6 +17,7 @@ export function PhiDescriptionWidgetClient({ labels, config, }) {
|
|
|
18
17
|
borderRadius: 999,
|
|
19
18
|
paddingInline: token.paddingSM,
|
|
20
19
|
paddingBlock: token.paddingXXS,
|
|
20
|
+
fontSize: token.fontSizeSM,
|
|
21
21
|
fontWeight: PHI_FONT_WEIGHT_STRONG,
|
|
22
22
|
letterSpacing: "0.04em",
|
|
23
23
|
color: token.colorTextTertiary,
|
|
@@ -26,8 +26,8 @@ export function PhiDescriptionWidgetClient({ labels, config, }) {
|
|
|
26
26
|
}, children: labels.eyebrow })) : null, labels.title || labels.description ? (_jsxs("div", { children: [labels.title ? (_jsx(PhiTypographyControl, { presentation: "title", level: 2, style: { marginBottom: labels.description ? token.paddingXS : 0 }, children: labels.title })) : null, labels.description ? (_jsx(PhiTypographyControl, { presentation: "paragraph", type: config?.tone, style: {
|
|
27
27
|
marginBottom: 0,
|
|
28
28
|
color: config?.tone ? undefined : token.colorTextSecondary,
|
|
29
|
-
fontSize: token.
|
|
30
|
-
lineHeight:
|
|
29
|
+
fontSize: token.fontSize,
|
|
30
|
+
lineHeight: PHI_LINE_HEIGHT_BASE,
|
|
31
31
|
}, children: labels.description })) : null] })) : null, labels.asideTitle || asideItems.length > 0 ? (_jsxs(PhiFlexControl, { vertical: true, gap: token.paddingMD, style: { width: "100%" }, children: [labels.asideTitle ? (_jsx(PhiTypographyControl, { strong: true, style: {
|
|
32
32
|
fontSize: token.fontSize,
|
|
33
33
|
lineHeight: PHI_LINE_HEIGHT_BASE,
|
|
@@ -60,6 +60,7 @@ export function PhiDescriptionWidgetEditor({ config, onChange, }) {
|
|
|
60
60
|
borderRadius: 999,
|
|
61
61
|
paddingInline: "var(--ant-padding-sm)",
|
|
62
62
|
paddingBlock: "var(--ant-padding-xxs)",
|
|
63
|
+
fontSize: "var(--ant-font-size-sm)",
|
|
63
64
|
fontWeight: 600,
|
|
64
65
|
letterSpacing: "0.04em",
|
|
65
66
|
color: "var(--ant-color-text-tertiary)",
|
|
@@ -74,8 +75,8 @@ export function PhiDescriptionWidgetEditor({ config, onChange, }) {
|
|
|
74
75
|
} }), _jsx(PhiTextControl, { presentation: "textarea", value: draft.description, readOnly: !onChange, variant: "borderless", placeholder: "Description", autoSize: { minRows: 2, maxRows: 8 }, onChange: (value) => updateField("description", value ?? ""), onBlur: () => commitField("description"), style: {
|
|
75
76
|
...EDITOR_TEXTAREA_STYLE,
|
|
76
77
|
color: "var(--ant-color-text-secondary)",
|
|
77
|
-
fontSize: "
|
|
78
|
-
lineHeight: 1.
|
|
78
|
+
fontSize: "var(--ant-font-size)",
|
|
79
|
+
lineHeight: 1.5,
|
|
79
80
|
} })] })) : null, sections.aside ? (_jsxs(PhiFlexControl, { vertical: true, gap: 12, style: { width: "100%" }, children: [_jsx(PhiTextControl, { presentation: "textarea", value: draft.asideTitle, readOnly: !onChange, variant: "borderless", placeholder: "Aside title", autoSize: { minRows: 1, maxRows: 3 }, onChange: (value) => updateField("asideTitle", value ?? ""), onBlur: () => commitField("asideTitle"), style: {
|
|
80
81
|
...EDITOR_TEXTAREA_STYLE,
|
|
81
82
|
color: "var(--ant-color-text-heading)",
|
|
@@ -64,6 +64,7 @@ export async function PhiDescriptionWidget({ labels, config, runtime, translate,
|
|
|
64
64
|
borderRadius: "9999px",
|
|
65
65
|
paddingInline: PHI_SPACE.sm,
|
|
66
66
|
paddingBlock: PHI_SPACE.xxs,
|
|
67
|
+
fontSize: PHI_FONT_SIZE.sm,
|
|
67
68
|
fontWeight: 600,
|
|
68
69
|
letterSpacing: "0.04em",
|
|
69
70
|
color: PHI_COLOR.textTertiary,
|
|
@@ -82,8 +83,8 @@ export async function PhiDescriptionWidget({ labels, config, runtime, translate,
|
|
|
82
83
|
margin: 0,
|
|
83
84
|
marginBottom: 0,
|
|
84
85
|
color: resolveDescriptionToneColor(config?.tone),
|
|
85
|
-
fontSize: PHI_FONT_SIZE.
|
|
86
|
-
lineHeight: PHI_LINE_HEIGHT.
|
|
86
|
+
fontSize: PHI_FONT_SIZE.base,
|
|
87
|
+
lineHeight: PHI_LINE_HEIGHT.base,
|
|
87
88
|
}, children: description })) : null] })) : null, asideTitle || resolvedAsideItems.length > 0 ? (_jsxs("div", { style: subStackStyle, children: [asideTitle ? (_jsx("span", { style: {
|
|
88
89
|
display: "block",
|
|
89
90
|
fontSize: PHI_FONT_SIZE.base,
|