@phis/ui 0.4.2 → 0.4.3
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/cms/phi-cms-layout-renderer.js +4 -3
- package/components/cms/phi-cms-page-render-scope.d.ts +4 -5
- package/components/cms/phi-cms-page-render-scope.js +43 -22
- package/components/cms/phi-cms-render-diagnostic-client.js +7 -3
- package/components/cms/phi-cms-render-error-boundary.js +2 -1
- package/components/controls/phi-bullet-list-control.js +31 -2
- package/components/controls/phi-ribbon-control.d.ts +29 -0
- package/components/controls/phi-ribbon-control.js +53 -0
- package/components/controls/phi-table-control.d.ts +3 -0
- package/components/controls/phi-table-control.js +13 -1
- package/components/controls/phi-tree-control.js +3 -3
- package/components/forms/shared-form-provider-registry.js +1 -4
- package/components/media/asset-collection-runtime.js +6 -16
- package/components/media/media-font-metrics.d.ts +3 -0
- package/components/media/media-font-metrics.js +43 -0
- package/components/media/phi-area-upload-widget.js +53 -97
- package/components/media/phi-image-preview-data.d.ts +13 -0
- package/components/media/phi-image-preview-data.js +184 -9
- package/components/media/phi-media-picker-binding.js +16 -13
- package/components/media/phi-media-upload.d.ts +15 -8
- package/components/media/phi-media-upload.js +18 -9
- package/components/media/phi-site-font-assets.js +7 -8
- package/components/overlays/phi-overlay-container-client.js +16 -1
- package/components/regions/presets/phi-settings-page-shell-tree.js +48 -1
- package/components/runtime/runtime-controller-materialization.js +12 -1
- package/components/state/scoped-state-store.js +20 -3
- package/components/tables/client/phi-table-binding.d.ts +2 -1
- package/components/tables/client/phi-table-binding.js +45 -21
- package/components/trees/client/phi-tree-binding.js +19 -11
- package/components/widgets/client/bullet-list-entries-editor.d.ts +26 -0
- package/components/widgets/client/bullet-list-entries-editor.js +103 -0
- package/components/widgets/config/choice-shared.js +12 -13
- package/components/widgets/config/command-buttons.d.ts +5 -1
- package/components/widgets/config/command-buttons.js +38 -81
- package/components/widgets/config/control-signal-config.d.ts +3 -1
- package/components/widgets/config/control-signal-config.js +19 -17
- package/components/widgets/config/image-source-parser.d.ts +1 -0
- package/components/widgets/config/image-source-parser.js +7 -8
- package/components/widgets/config/mask.d.ts +6 -1
- package/components/widgets/config/mask.js +24 -1
- package/components/widgets/config/parser-primitives.js +72 -0
- package/components/widgets/config/stack-tabs.d.ts +1 -1
- package/components/widgets/config/stack-tabs.js +2 -3
- package/components/widgets/config/strict-config-reader.d.ts +40 -0
- package/components/widgets/config/strict-config-reader.js +174 -0
- package/components/widgets/label-sets/effects.js +2 -0
- package/components/widgets/label-types/effects.d.ts +1 -0
- package/components/widgets/label-types/effects.js +1 -0
- package/components/widgets/navigation-descriptor-resolver.server.js +20 -8
- package/components/widgets/shared/bullet-list-vocabulary.d.ts +17 -1
- package/components/widgets/shared/bullet-list-vocabulary.js +19 -2
- package/components/widgets/shared/card-body-client.d.ts +16 -4
- package/components/widgets/shared/card-body-client.js +81 -24
- package/components/widgets/shared/card-vocabulary.d.ts +21 -4
- package/components/widgets/shared/card-vocabulary.js +28 -2
- package/components/widgets/signals/control-signal-capabilities.d.ts +0 -18
- package/components/widgets/signals/control-signal-capabilities.js +10 -22
- package/components/workspace/catalog-store.d.ts +4 -3
- package/components/workspace/catalog-store.js +6 -5
- package/gateway/dashboard-cards-route.js +1 -0
- package/gateway/internal-references.d.ts +6 -1
- package/gateway/internal-references.js +48 -23
- package/gateway/site-cms-write-validation.d.ts +9 -0
- package/gateway/site-cms-write-validation.js +78 -0
- package/gateway/site-proxy.js +19 -3
- package/helpers/renderable-block-serialization.js +1 -1
- package/next/config.d.mts +3 -0
- package/next/config.mjs +28 -1
- package/package.json +2 -2
- package/plugins/runtime/phi-slot-child-viewport-effects-observer.js +5 -1
- package/plugins/runtime-modules/accounting/presets.d.ts +9 -0
- package/plugins/runtime-modules/accounting/presets.js +37 -0
- package/plugins/runtime-modules/accounting/server.d.ts +1 -4
- package/plugins/runtime-modules/accounting/server.js +4 -7
- package/plugins/runtime-modules/app/presets.d.ts +18 -0
- package/plugins/runtime-modules/app/presets.js +67 -0
- package/plugins/runtime-modules/app/server.d.ts +1 -4
- package/plugins/runtime-modules/app/server.js +4 -7
- package/plugins/runtime-modules/asset/controller/runtime.js +2 -2
- package/plugins/runtime-modules/asset/services/collection.js +9 -1
- package/plugins/runtime-modules/asset/services/options.js +2 -2
- package/plugins/runtime-modules/builder/area-settings-answer.js +2 -1
- package/plugins/runtime-modules/builder/authoring-canvas.js +6 -6
- package/plugins/runtime-modules/builder/cards.js +3 -5
- package/plugins/runtime-modules/builder/clients/builder-region-overrides.js +3 -3
- package/plugins/runtime-modules/builder/clients/canvas-widget.js +15 -54
- package/plugins/runtime-modules/builder/clients/inspector-config-field.d.ts +4 -1
- package/plugins/runtime-modules/builder/clients/inspector-config-field.js +5 -34
- package/plugins/runtime-modules/builder/clients/inspector-link-target-field.js +1 -1
- package/plugins/runtime-modules/builder/clients/inspector-section-widget.js +8 -8
- package/plugins/runtime-modules/builder/clients/inspector-title.js +6 -6
- package/plugins/runtime-modules/builder/clients/layout-inspector.d.ts +1 -1
- package/plugins/runtime-modules/builder/clients/layout-inspector.js +11 -25
- package/plugins/runtime-modules/builder/clients/mode-switch.js +1 -1
- package/plugins/runtime-modules/builder/clients/pages-header.js +11 -11
- package/plugins/runtime-modules/builder/clients/pages-workspace.js +1 -1
- package/plugins/runtime-modules/builder/clients/region-visibility-fields.js +1 -1
- package/plugins/runtime-modules/builder/clients/runtime-module-authoring-boundary.js +1 -1
- package/plugins/runtime-modules/builder/clients/shells-workspace.js +15 -31
- package/plugins/runtime-modules/builder/clients/structure-canvas.js +1 -1
- package/plugins/runtime-modules/builder/clients/use-region-draft-hydration.d.ts +26 -0
- package/plugins/runtime-modules/builder/clients/use-region-draft-hydration.js +36 -0
- package/plugins/runtime-modules/builder/clients/widget-effects-editor.js +1 -1
- package/plugins/runtime-modules/builder/clients/widget-inspector.js +1 -0
- package/plugins/runtime-modules/builder/clients/workspace-scope-boundary.js +2 -2
- package/plugins/runtime-modules/builder/cms-instance-allocator.js +8 -28
- package/plugins/runtime-modules/builder/controller/client.d.ts +0 -15
- package/plugins/runtime-modules/builder/controller/definition.d.ts +0 -15
- package/plugins/runtime-modules/builder/controller/definition.js +0 -8
- package/plugins/runtime-modules/builder/controller/draft-command-controller.d.ts +1 -2
- package/plugins/runtime-modules/builder/controller/draft-command-controller.js +11 -30
- package/plugins/runtime-modules/builder/controller/draft-status-controller.js +15 -2
- package/plugins/runtime-modules/builder/controller/inspector-controller.d.ts +1 -2
- package/plugins/runtime-modules/builder/controller/inspector-controller.js +4 -7
- package/plugins/runtime-modules/builder/controller/page-controller.d.ts +1 -2
- package/plugins/runtime-modules/builder/controller/page-controller.js +16 -68
- package/plugins/runtime-modules/builder/controller/server-definition.d.ts +0 -15
- package/plugins/runtime-modules/builder/controller/signal-wiring-controller.d.ts +2 -5
- package/plugins/runtime-modules/builder/controller/signal-wiring-controller.js +6 -50
- package/plugins/runtime-modules/builder/controller/workspace-controller.d.ts +2 -3
- package/plugins/runtime-modules/builder/controller/workspace-controller.js +89 -153
- package/plugins/runtime-modules/builder/data-providers.d.ts +1 -0
- package/plugins/runtime-modules/builder/data-providers.js +1 -0
- package/plugins/runtime-modules/builder/definition.d.ts +1 -0
- package/plugins/runtime-modules/builder/developer-workspace-store.d.ts +64 -30
- package/plugins/runtime-modules/builder/developer-workspace-store.js +255 -61
- package/plugins/runtime-modules/builder/edit-scaffold-drawer.js +1 -1
- package/plugins/runtime-modules/builder/history.d.ts +3 -1
- package/plugins/runtime-modules/builder/options-providers.js +3 -2
- package/plugins/runtime-modules/builder/page-catalog-client.js +19 -7
- package/plugins/runtime-modules/builder/page-meta-form.js +1 -0
- package/plugins/runtime-modules/builder/page-references-provider.js +1 -1
- package/plugins/runtime-modules/builder/persistence.js +33 -69
- package/plugins/runtime-modules/builder/public-route-collisions.d.ts +2 -2
- package/plugins/runtime-modules/builder/public-route-collisions.js +6 -19
- package/plugins/runtime-modules/builder/render-root-node-preview.server.js +20 -3
- package/plugins/runtime-modules/builder/render-root-node-scaffold.js +3 -3
- package/plugins/runtime-modules/builder/runtime-module-selection.d.ts +2 -12
- package/plugins/runtime-modules/builder/runtime-module-selection.js +16 -42
- package/plugins/runtime-modules/builder/services/navigation-table.js +9 -15
- package/plugins/runtime-modules/builder/services/page-source-tree.js +8 -8
- package/plugins/runtime-modules/builder/services/runtime-modules-table.js +10 -10
- package/plugins/runtime-modules/builder/services/signal-routes-table.js +3 -3
- package/plugins/runtime-modules/builder/signal-route-selection.d.ts +1 -2
- package/plugins/runtime-modules/builder/signal-route-selection.js +2 -2
- package/plugins/runtime-modules/builder/signal-wiring-options.js +3 -2
- package/plugins/runtime-modules/builder/structure-dnd.js +8 -61
- package/plugins/runtime-modules/builder/trees/phi-default-builder-area-preset-tree.js +13 -10
- package/plugins/runtime-modules/builder/widgets/structure-region/built-in.js +12 -18
- package/plugins/runtime-modules/builder/workspace-catalog-actions.d.ts +55 -0
- package/plugins/runtime-modules/builder/workspace-catalog-actions.js +142 -0
- package/plugins/runtime-modules/client-manifests/common.js +1 -1
- package/plugins/runtime-modules/{runtime-calendar-adapters.js → core/calendar-adapters.js} +2 -2
- package/plugins/runtime-modules/{client-calendar-adapters/core.js → core/client-calendar-adapters.js} +1 -1
- package/plugins/runtime-modules/core/content-entries/table.js +1 -1
- package/plugins/runtime-modules/core/data-providers.d.ts +3 -0
- package/plugins/runtime-modules/core/data-providers.js +2 -1
- package/plugins/runtime-modules/core/definition.d.ts +3 -0
- package/plugins/runtime-modules/core/definition.js +1 -1
- package/plugins/runtime-modules/core/messaging/data-providers.d.ts +1 -0
- package/plugins/runtime-modules/core/messaging/data-providers.js +1 -0
- package/plugins/runtime-modules/core/messaging/thread-inbox-table.js +1 -1
- package/plugins/runtime-modules/core/server.js +1 -1
- package/plugins/runtime-modules/core/themes.d.ts +44 -0
- package/plugins/runtime-modules/core/themes.js +12 -0
- package/plugins/runtime-modules/core/widgets/card/collection-view-binding.js +25 -9
- package/plugins/runtime-modules/core/widgets/card/config.d.ts +296 -254
- package/plugins/runtime-modules/core/widgets/card/config.js +52 -3
- package/plugins/runtime-modules/core/widgets/card/editor.js +30 -3
- package/plugins/runtime-modules/core/widgets/card/server.d.ts +3 -0
- package/plugins/runtime-modules/core/widgets/card/server.js +31 -3
- package/plugins/runtime-modules/core/widgets/collection-view/asset-binding.js +7 -6
- package/plugins/runtime-modules/core/widgets/collection-view/config.js +9 -2
- package/plugins/runtime-modules/core/widgets/description/config.d.ts +1 -25
- package/plugins/runtime-modules/core/widgets/description/editor.js +4 -61
- package/plugins/runtime-modules/core/widgets/entry-list/config.d.ts +1 -25
- package/plugins/runtime-modules/core/widgets/gallery/config.d.ts +1 -25
- package/plugins/runtime-modules/core/widgets/image/config.js +2 -2
- package/plugins/runtime-modules/core/widgets/markdown/editor.js +2 -3
- package/plugins/runtime-modules/core/widgets/result/config.d.ts +4 -25
- package/plugins/runtime-modules/core/widgets/result/config.js +2 -0
- package/plugins/runtime-modules/core/widgets/result/server.js +25 -19
- package/plugins/runtime-modules/core/widgets/simple-text/config.d.ts +1 -25
- package/plugins/runtime-modules/core/widgets/slot-upload/client.js +11 -2
- package/plugins/runtime-modules/core/widgets/table/client.js +8 -4
- package/plugins/runtime-modules/core/widgets/table/config.d.ts +3 -83
- package/plugins/runtime-modules/core/widgets/table/config.js +302 -452
- package/plugins/runtime-modules/core/widgets/tree/config.js +5 -2
- package/plugins/runtime-modules/dashboard/card-rows.d.ts +5 -0
- package/plugins/runtime-modules/dashboard/card-rows.js +59 -0
- package/plugins/runtime-modules/dashboard/clients/dashboard-card-view.js +4 -6
- package/plugins/runtime-modules/dashboard/services/collection.js +17 -7
- package/plugins/runtime-modules/groups/data-providers.d.ts +2 -0
- package/plugins/runtime-modules/groups/data-providers.js +3 -3
- package/plugins/runtime-modules/groups/definition.d.ts +2 -0
- package/plugins/runtime-modules/localization/data-providers.d.ts +3 -0
- package/plugins/runtime-modules/localization/data-providers.js +2 -0
- package/plugins/runtime-modules/localization/definition.d.ts +3 -0
- package/plugins/runtime-modules/localization/services/table.js +3 -7
- package/plugins/runtime-modules/observability/data-providers.d.ts +1 -0
- package/plugins/runtime-modules/observability/data-providers.js +1 -0
- package/plugins/runtime-modules/observability/definition.d.ts +1 -0
- package/plugins/runtime-modules/observability/module.d.ts +1 -0
- package/plugins/runtime-modules/observability/services/table.js +1 -1
- package/plugins/runtime-modules/phi-default-site-area-preset-tree.js +2 -105
- package/plugins/runtime-modules/public/presets.d.ts +115 -0
- package/plugins/runtime-modules/{area-base-presets.js → public/presets.js} +19 -119
- package/plugins/runtime-modules/public/server.d.ts +1 -4
- package/plugins/runtime-modules/public/server.js +4 -7
- package/plugins/runtime-modules/public/trees/phi-default-pub-error-page-tree.js +7 -8
- package/plugins/runtime-modules/public-base-page-references.d.ts +1 -1
- package/plugins/runtime-modules/public-base-page-references.js +1 -1
- package/plugins/runtime-modules/revisions/controller/client.js +11 -11
- package/plugins/runtime-modules/revisions/services/table.js +3 -3
- package/plugins/runtime-modules/theme/trees/phi-default-builder-theme-page-tree.js +22 -0
- package/plugins/runtime-modules/theme/widgets/brand-controls/client.d.ts +2 -1
- package/plugins/runtime-modules/theme/widgets/brand-controls/client.js +93 -58
- package/plugins/runtime-modules/theme/widgets/brand-controls/config.d.ts +2 -1
- package/plugins/runtime-modules/theme/widgets/brand-controls/config.js +23 -22
- package/plugins/runtime-modules/theme/widgets/brand-controls/plugin.js +1 -1
- package/plugins/runtime-modules/theme-descriptors.js +1 -1
- package/plugins/runtime-modules/user-management/data-providers.d.ts +2 -0
- package/plugins/runtime-modules/user-management/data-providers.js +1 -0
- package/plugins/runtime-modules/user-management/definition.d.ts +2 -0
- package/styles/control-shape.css +11 -11
- package/styles/controls.css +3 -3
- package/styles/layout.css +16 -7
- package/types/cms-config.js +142 -185
- package/types/cms-plugins.d.ts +4 -11
- package/types/core-widget-placements.d.ts +14 -3
- package/types/dashboard-cards.d.ts +9 -0
- package/types/media.d.ts +0 -3
- package/types/renderable-block.d.ts +6 -1
- package/types/table-widget.d.ts +9 -0
- package/types/table-widget.js +20 -0
- package/types.d.ts +1 -1
- package/widget-config.d.ts +2 -0
- package/widget-config.js +6 -0
- package/plugins/runtime-modules/area-base-presets.d.ts +0 -139
- /package/plugins/runtime-modules/{runtime-calendar-adapters.d.ts → core/calendar-adapters.d.ts} +0 -0
- /package/plugins/runtime-modules/{client-calendar-adapters/core.d.ts → core/client-calendar-adapters.d.ts} +0 -0
|
@@ -3,6 +3,7 @@ import { isPhiCmsNavigationOverlayTarget, readPhiCmsNavigationTargetPath, } from
|
|
|
3
3
|
import { createPhiPresetCmsInstanceId } from "../../types/cms-instance-id";
|
|
4
4
|
import { createGlobalTranslator, createSiteTranslator, PHI_TR_CTX_WEB_UI_LABEL, } from "../../gateway/tr";
|
|
5
5
|
import { resolvePhiCmsNavigationOverlay, resolvePhiCmsActiveNavigationSurfaces, resolvePhiCmsRoutePresetByPageId, } from "../../plugins/runtime-modules/descriptor-compiler";
|
|
6
|
+
import { cache } from "react";
|
|
6
7
|
import { logRuntimeEvent } from "../../net/log";
|
|
7
8
|
import { phiRuntime } from "../../server-helpers/phi-runtime";
|
|
8
9
|
import { getPhiRequestNavigationContext } from "../../server-helpers/request-runtime";
|
|
@@ -58,19 +59,30 @@ function mapResolvedNavigationItem(item, translatedLabels) {
|
|
|
58
59
|
: {}),
|
|
59
60
|
};
|
|
60
61
|
}
|
|
62
|
+
/*
|
|
63
|
+
* Every active navigation surface of an Area, resolved once per request (audit K15).
|
|
64
|
+
*
|
|
65
|
+
* A page carries a navigation Widget per surface -- header, footer, sider -- and each one resolved all of
|
|
66
|
+
* the Area's surfaces to pick its own, hashing an instance id per item each time. The request's navigation
|
|
67
|
+
* context and its viewer are one object each per request, so React's `cache` keyed by them keeps the
|
|
68
|
+
* answer for exactly that request and nothing longer.
|
|
69
|
+
*/
|
|
70
|
+
const resolveActiveNavigationSurfaces = cache((context, area, viewer) => resolvePhiCmsActiveNavigationSurfaces({
|
|
71
|
+
catalog: context.catalog,
|
|
72
|
+
area,
|
|
73
|
+
activeModuleIds: context.activeModuleIds,
|
|
74
|
+
routeTable: context.routeTable,
|
|
75
|
+
viewer,
|
|
76
|
+
}));
|
|
61
77
|
export async function resolvePhiDescriptorNavigationItems(runtime, navKey, overlay = null) {
|
|
62
78
|
const normalizedNavKey = navKey.trim().toLowerCase();
|
|
63
79
|
if (!normalizedNavKey.startsWith(`${runtime.area}:`)) {
|
|
64
80
|
return null;
|
|
65
81
|
}
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
activeModuleIds,
|
|
71
|
-
routeTable,
|
|
72
|
-
viewer: runtime.viewer,
|
|
73
|
-
}).find((candidate) => candidate.navKey === normalizedNavKey);
|
|
82
|
+
const navigationContext = getPhiRequestNavigationContext(runtime.area);
|
|
83
|
+
const { routeTable } = navigationContext;
|
|
84
|
+
const descriptorSurface = resolveActiveNavigationSurfaces(navigationContext, runtime.area, runtime.viewer)
|
|
85
|
+
.find((candidate) => candidate.navKey === normalizedNavKey);
|
|
74
86
|
if (!descriptorSurface && !overlay) {
|
|
75
87
|
return null;
|
|
76
88
|
}
|
|
@@ -46,6 +46,15 @@ export declare function readPhiBulletListStyle(value: unknown): PhiBulletListSty
|
|
|
46
46
|
export declare function resolvePhiBulletListStyle(style: PhiBulletListStyle | null | undefined): PhiResolvedBulletListStyle;
|
|
47
47
|
/** The level an entry at `depth` (1 for the outermost) is drawn at. */
|
|
48
48
|
export declare function resolvePhiBulletListLevel(depth: number): PhiBulletListLevel;
|
|
49
|
+
/**
|
|
50
|
+
* What an entry says about the thing the list belongs to, where the list is a list of what is in it -- a
|
|
51
|
+
* plan's features. `included` draws a tick, `excluded` a dash and the entry's words quieter; `neutral`
|
|
52
|
+
* is the list's own marker, and is what an entry without a state is. Stored only where it is not neutral.
|
|
53
|
+
*/
|
|
54
|
+
export declare const PHI_BULLET_LIST_ENTRY_STATES: readonly ["included", "excluded", "neutral"];
|
|
55
|
+
export type PhiBulletListEntryState = (typeof PHI_BULLET_LIST_ENTRY_STATES)[number];
|
|
56
|
+
/** The state after `state`, in the order a click on a marker steps through them. */
|
|
57
|
+
export declare function nextPhiBulletListEntryState(state: PhiBulletListEntryState | undefined): PhiBulletListEntryState;
|
|
49
58
|
/**
|
|
50
59
|
* One entry of a list written as a list: its text, how deep it stands, and the values its text names.
|
|
51
60
|
*
|
|
@@ -58,11 +67,17 @@ export type PhiBulletListEntry = {
|
|
|
58
67
|
/** 1 for the outermost level, which is what an entry without one stands at. */
|
|
59
68
|
level?: number;
|
|
60
69
|
values?: string[];
|
|
70
|
+
/** Absent is `neutral`. */
|
|
71
|
+
state?: Exclude<PhiBulletListEntryState, "neutral">;
|
|
61
72
|
};
|
|
62
73
|
export declare function readPhiBulletListEntries(value: unknown): PhiBulletListEntry[];
|
|
63
74
|
/** An entry of the tree the Control draws: what it shows, and the entries one level below it. */
|
|
64
75
|
export type PhiBulletListNode<Content> = {
|
|
65
76
|
content: Content;
|
|
77
|
+
/** Absent is `neutral`: the marker of the entry's level. */
|
|
78
|
+
state?: PhiBulletListEntryState;
|
|
79
|
+
/** Set by an editor: the marker becomes a button that steps the entry's state on. */
|
|
80
|
+
onMarkerClick?: () => void;
|
|
66
81
|
children?: PhiBulletListNode<Content>[];
|
|
67
82
|
};
|
|
68
83
|
/**
|
|
@@ -72,7 +87,8 @@ export type PhiBulletListNode<Content> = {
|
|
|
72
87
|
*/
|
|
73
88
|
export declare function buildPhiBulletListTree<Entry extends {
|
|
74
89
|
level?: number;
|
|
75
|
-
|
|
90
|
+
state?: PhiBulletListEntryState;
|
|
91
|
+
}, Content>(entries: readonly Entry[], content: (entry: Entry, index: number) => Content, onMarkerClick?: (entry: Entry, index: number) => void): PhiBulletListNode<Content>[];
|
|
76
92
|
/**
|
|
77
93
|
* The Inspector fields for a list style stored under `key`, under a divider titled `heading`. Every Widget
|
|
78
94
|
* with a list offers these, so a list is set the same way wherever it stands.
|
|
@@ -69,6 +69,17 @@ export function resolvePhiBulletListLevel(depth) {
|
|
|
69
69
|
const deepest = PHI_BULLET_LIST_LEVELS[PHI_BULLET_LIST_LEVELS.length - 1];
|
|
70
70
|
return Math.min(Math.max(1, Math.trunc(depth) || 1), deepest);
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* What an entry says about the thing the list belongs to, where the list is a list of what is in it -- a
|
|
74
|
+
* plan's features. `included` draws a tick, `excluded` a dash and the entry's words quieter; `neutral`
|
|
75
|
+
* is the list's own marker, and is what an entry without a state is. Stored only where it is not neutral.
|
|
76
|
+
*/
|
|
77
|
+
export const PHI_BULLET_LIST_ENTRY_STATES = ["included", "excluded", "neutral"];
|
|
78
|
+
/** The state after `state`, in the order a click on a marker steps through them. */
|
|
79
|
+
export function nextPhiBulletListEntryState(state) {
|
|
80
|
+
const index = PHI_BULLET_LIST_ENTRY_STATES.indexOf(state ?? "neutral");
|
|
81
|
+
return PHI_BULLET_LIST_ENTRY_STATES[(index + 1) % PHI_BULLET_LIST_ENTRY_STATES.length];
|
|
82
|
+
}
|
|
72
83
|
export function readPhiBulletListEntries(value) {
|
|
73
84
|
if (!Array.isArray(value)) {
|
|
74
85
|
return [];
|
|
@@ -82,10 +93,12 @@ export function readPhiBulletListEntries(value) {
|
|
|
82
93
|
const values = Array.isArray(record.values)
|
|
83
94
|
? record.values.map((item) => (typeof item === "string" || typeof item === "number" ? String(item) : ""))
|
|
84
95
|
: [];
|
|
96
|
+
const state = record.state === "included" || record.state === "excluded" ? record.state : undefined;
|
|
85
97
|
return [{
|
|
86
98
|
text: record.text,
|
|
87
99
|
...(level > 1 ? { level } : {}),
|
|
88
100
|
...(values.length > 0 ? { values } : {}),
|
|
101
|
+
...(state ? { state } : {}),
|
|
89
102
|
}];
|
|
90
103
|
});
|
|
91
104
|
}
|
|
@@ -94,14 +107,18 @@ export function readPhiBulletListEntries(value) {
|
|
|
94
107
|
* more than a level hangs one level below it all the same: a level cannot be skipped in a nested list,
|
|
95
108
|
* and an entry that tried stays with the entry it follows rather than jumping.
|
|
96
109
|
*/
|
|
97
|
-
export function buildPhiBulletListTree(entries, content) {
|
|
110
|
+
export function buildPhiBulletListTree(entries, content, onMarkerClick) {
|
|
98
111
|
const root = [];
|
|
99
112
|
const open = [root];
|
|
100
113
|
entries.forEach((entry, index) => {
|
|
101
114
|
const wanted = resolvePhiBulletListLevel(entry.level ?? 1);
|
|
102
115
|
const level = Math.min(wanted, open.length);
|
|
103
116
|
open.length = level;
|
|
104
|
-
const node = {
|
|
117
|
+
const node = {
|
|
118
|
+
content: content(entry, index),
|
|
119
|
+
...(entry.state && entry.state !== "neutral" ? { state: entry.state } : {}),
|
|
120
|
+
...(onMarkerClick ? { onMarkerClick: () => onMarkerClick(entry, index) } : {}),
|
|
121
|
+
};
|
|
105
122
|
open[level - 1].push(node);
|
|
106
123
|
node.children = [];
|
|
107
124
|
open.push(node.children);
|
|
@@ -4,8 +4,8 @@ import type { PhiImagePresentation } from "../../media/image-presentation";
|
|
|
4
4
|
import type { PhiClientBlockBaseProps } from "../../../types";
|
|
5
5
|
import type { PhiSurface } from "../../../types/surface";
|
|
6
6
|
import type { PhiSignalRouteSet } from "../../../types/signals";
|
|
7
|
-
import type { PhiCardHeadingLevel, PhiCardHoverEffect, PhiCardIconPlacement, PhiCardTextAlign, PhiCardTextSlot, PhiCardVariant, PhiCardWidgetBody } from "./card-vocabulary";
|
|
8
|
-
export type { PhiCardHeadingLevel, PhiCardHoverEffect, PhiCardIconPlacement, PhiCardTextAlign, PhiCardTextSlot, PhiCardVariant, PhiCardWidgetBody, } from "./card-vocabulary";
|
|
7
|
+
import type { PhiCardBillingPeriod, PhiCardHeadingLevel, PhiCardHoverEffect, PhiCardIconPlacement, PhiCardTextAlign, PhiCardTextSlot, PhiCardVariant, PhiCardWidgetBody } from "./card-vocabulary";
|
|
8
|
+
export type { PhiCardBillingPeriod, PhiCardHeadingLevel, PhiCardHoverEffect, PhiCardIconPlacement, PhiCardTextAlign, PhiCardTextSlot, PhiCardVariant, PhiCardWidgetBody, } from "./card-vocabulary";
|
|
9
9
|
export type PhiCardWidgetClientLabels = {
|
|
10
10
|
eyebrow?: string;
|
|
11
11
|
/**
|
|
@@ -19,6 +19,13 @@ export type PhiCardWidgetClientLabels = {
|
|
|
19
19
|
actionLabel?: string;
|
|
20
20
|
/** The figure a `stat` body draws. Data rather than copy, and never translated. */
|
|
21
21
|
value?: string;
|
|
22
|
+
/** The band at the card's end edge. */
|
|
23
|
+
badge?: string;
|
|
24
|
+
/** A `plan` body's prices, data and never translated, and their periods, which are words. */
|
|
25
|
+
price?: string;
|
|
26
|
+
pricePeriod?: string;
|
|
27
|
+
priceYearly?: string;
|
|
28
|
+
pricePeriodYearly?: string;
|
|
22
29
|
};
|
|
23
30
|
/**
|
|
24
31
|
* What is true about a card's content right now, as opposed to what the card says.
|
|
@@ -63,6 +70,10 @@ export type PhiCardWidgetClientConfig = {
|
|
|
63
70
|
variant?: PhiCardVariant;
|
|
64
71
|
body?: PhiCardWidgetBody;
|
|
65
72
|
highlight?: boolean;
|
|
73
|
+
/** The badge's ground; absent, the Theme's primary. */
|
|
74
|
+
badgeColor?: string;
|
|
75
|
+
/** Which price a `plan` shows until a `billingPeriod` signal says otherwise. Absent is `monthly`. */
|
|
76
|
+
billingPeriod?: PhiCardBillingPeriod;
|
|
66
77
|
hoverEffect?: PhiCardHoverEffect;
|
|
67
78
|
/** The card's wired signals (`PHI_CARD_SIGNALS`); absent where nothing is wired, as on the canvas. */
|
|
68
79
|
signalRoutes?: PhiSignalRouteSet | null;
|
|
@@ -78,10 +89,11 @@ export type PhiCardWidgetAction = {
|
|
|
78
89
|
export type PhiCardWidgetClientProps = PhiClientBlockBaseProps<PhiCardWidgetClientLabels, PhiCardWidgetClientConfig> & {
|
|
79
90
|
binding?: PhiCardWidgetClientBinding;
|
|
80
91
|
/**
|
|
81
|
-
* What a `list` body draws, already drawn.
|
|
92
|
+
* What a `list` body draws, and a `plan` body's features, already drawn.
|
|
82
93
|
*
|
|
83
94
|
* Handed in rather than drawn here: the Card is on Public pages, and a list Control imported by it
|
|
84
|
-
* would load on every page with a Card. A Dashboard draws its entries and passes them
|
|
95
|
+
* would load on every page with a Card. A Dashboard draws its entries and passes them; a plan card's
|
|
96
|
+
* server half draws its features on the server, and its editor draws them as fields.
|
|
85
97
|
*/
|
|
86
98
|
listContent?: ReactNode;
|
|
87
99
|
/**
|
|
@@ -4,6 +4,7 @@ import { useState } from "react";
|
|
|
4
4
|
import { PhiButtonControl } from "../../controls/phi-button-control";
|
|
5
5
|
import { PhiFlexControl } from "../../controls/phi-flex-control";
|
|
6
6
|
import { PhiImageControl } from "../../controls/phi-image-control";
|
|
7
|
+
import { PhiRibbonControl } from "../../controls/phi-ribbon-control";
|
|
7
8
|
import { PhiSkeletonControl } from "../../controls/phi-skeleton-control";
|
|
8
9
|
import { PhiStatisticControl } from "../../controls/phi-statistic-control";
|
|
9
10
|
import { PhiTypographyControl } from "../../controls/phi-typography-control";
|
|
@@ -14,8 +15,9 @@ import { PhiSurfaceGroundLayer } from "../../surface/phi-surface-ground";
|
|
|
14
15
|
import { PhiSurfaceTone } from "../../surface/phi-surface-tone";
|
|
15
16
|
import { resolvePhiSurfaceStyle } from "../../../helpers/surface-style";
|
|
16
17
|
import { usePhiControlSignalController } from "../client/shared/phi-control-signals";
|
|
17
|
-
import { PHI_SPACE } from "../../../theme/antd-css-var-contract";
|
|
18
|
-
|
|
18
|
+
import { PHI_COLOR, PHI_SPACE } from "../../../theme/antd-css-var-contract";
|
|
19
|
+
import { PHI_CARD_TEXT_SLOTS, readPhiCardBillingPeriod } from "./card-vocabulary";
|
|
20
|
+
const PHI_CARD_SIGNAL_TEXT_SLOTS = new Set(PHI_CARD_TEXT_SLOTS);
|
|
19
21
|
/*
|
|
20
22
|
* What each variant sets, in one row per variant. A further variant -- a featured card -- is a row here
|
|
21
23
|
* and an entry in `PHI_CARD_VARIANTS`.
|
|
@@ -71,13 +73,19 @@ const PHI_CARD_HIDDEN_TEXT = {
|
|
|
71
73
|
whiteSpace: "nowrap",
|
|
72
74
|
border: 0,
|
|
73
75
|
};
|
|
74
|
-
const PHI_CARD_THEME_CORNER = "var(--phi-surface-radius
|
|
76
|
+
const PHI_CARD_THEME_CORNER = "var(--phi-surface-radius)";
|
|
75
77
|
/** The box a picture is shown in takes the picture's own proportion; 3:2 where nothing is known. */
|
|
76
78
|
function resolveMediaAspectRatio(presentation) {
|
|
77
79
|
return presentation.width && presentation.height
|
|
78
80
|
? `${presentation.width} / ${presentation.height}`
|
|
79
81
|
: "3 / 2";
|
|
80
82
|
}
|
|
83
|
+
/*
|
|
84
|
+
* Every colour the card paints is the Theme's variable (`PHI_COLOR`), never a token's resolved value: a
|
|
85
|
+
* Surface with a `tone` writes the other mode's variables on the card's box, and only a variable reaches
|
|
86
|
+
* the words inside it. Resolved values were the page's mode written in by hand, and dark words stayed
|
|
87
|
+
* dark on a photograph darkened for light ones.
|
|
88
|
+
*/
|
|
81
89
|
export function PhiCardWidgetClient({ labels: placedLabels, config, binding, listContent, renderText, }) {
|
|
82
90
|
const { token } = usePhiConfig();
|
|
83
91
|
const [overrides, setOverrides] = useState({});
|
|
@@ -94,6 +102,11 @@ export function PhiCardWidgetClient({ labels: placedLabels, config, binding, lis
|
|
|
94
102
|
else if (capabilityId === "loading" || capabilityId === "highlight") {
|
|
95
103
|
setOverrides((current) => ({ ...current, [capabilityId]: signal.value === true }));
|
|
96
104
|
}
|
|
105
|
+
else if (capabilityId === "billingPeriod") {
|
|
106
|
+
const billingPeriod = readPhiCardBillingPeriod(signal.value);
|
|
107
|
+
if (billingPeriod)
|
|
108
|
+
setOverrides((current) => ({ ...current, billingPeriod }));
|
|
109
|
+
}
|
|
97
110
|
return true;
|
|
98
111
|
},
|
|
99
112
|
});
|
|
@@ -157,15 +170,28 @@ export function PhiCardWidgetClient({ labels: placedLabels, config, binding, lis
|
|
|
157
170
|
const title = textOf("title");
|
|
158
171
|
const description = textOf("description");
|
|
159
172
|
const meta = textOf("meta");
|
|
173
|
+
const badge = textOf("badge");
|
|
160
174
|
const eyebrowType = { fontSize: token.fontSizeSM, letterSpacing: "0.04em", textTransform: "uppercase" };
|
|
161
175
|
/* On the picture's corner where the variant puts it there and there is a picture to put it on. */
|
|
162
176
|
const eyebrowOnMedia = metrics.eyebrowOnMedia === true && image != null;
|
|
163
|
-
const heading = title != null ? (_jsx(PhiTypographyControl, { presentation: "title", level: Number(headingLevel.slice(1)), style: { margin: 0, ...headingType[headingLevel], color:
|
|
177
|
+
const heading = title != null ? (_jsx(PhiTypographyControl, { presentation: "title", level: Number(headingLevel.slice(1)), style: { margin: 0, ...headingType[headingLevel], color: PHI_COLOR.textHeading, ...slotWidth }, children: title })) : null;
|
|
164
178
|
/*
|
|
165
179
|
* The icon's colours as custom properties, which `.phi-card__icon` paints (`styles/layout.css`): the
|
|
166
180
|
* `icon` hover effect turns them round, and a colour stated inline could not be turned by a rule.
|
|
167
181
|
*/
|
|
168
|
-
const iconColours = (color, background) => ({
|
|
182
|
+
const iconColours = (color, background) => ({
|
|
183
|
+
"--phi-card-icon-color": color,
|
|
184
|
+
"--phi-card-icon-background": background,
|
|
185
|
+
/* The mark's colour once the `icon` effect turns them round: a ground it can be read on. */
|
|
186
|
+
"--phi-card-icon-hover-color": config?.iconBackground ?? PHI_COLOR.bgContainer,
|
|
187
|
+
});
|
|
188
|
+
/*
|
|
189
|
+
* The corner of the icon's ground: the Theme's Control shape at the large size, scaled from a large
|
|
190
|
+
* Control's height to the ground's side. So `square` draws a square, `pill` a circle -- half the large
|
|
191
|
+
* height scaled to the side is half the side -- and the steps between keep their proportion, as a
|
|
192
|
+
* Segmented's items follow their track. The Theme's plain corner did not: a pill Site drew square marks.
|
|
193
|
+
*/
|
|
194
|
+
const iconCorner = (side) => `calc(var(--phi-control-radius-lg, ${token.borderRadiusLG}px) * ${side / token.controlHeightLG})`;
|
|
169
195
|
const iconMark = (size, framed) => iconName ? (_jsx("span", { "aria-hidden": "true", className: "phi-card__icon", style: {
|
|
170
196
|
display: "inline-flex",
|
|
171
197
|
flex: "none",
|
|
@@ -173,21 +199,37 @@ export function PhiCardWidgetClient({ labels: placedLabels, config, binding, lis
|
|
|
173
199
|
justifyContent: "center",
|
|
174
200
|
width: framed,
|
|
175
201
|
height: framed,
|
|
176
|
-
borderRadius:
|
|
177
|
-
...iconColours(config?.iconColor ?? (highlight ?
|
|
202
|
+
borderRadius: iconCorner(framed),
|
|
203
|
+
...iconColours(config?.iconColor ?? (highlight ? PHI_COLOR.primary : PHI_COLOR.textSecondary), config?.iconBackground ?? PHI_COLOR.fillQuaternary),
|
|
178
204
|
}, children: _jsx(PhiIcon, { name: iconName, size: size }) })) : null;
|
|
179
205
|
/*
|
|
180
206
|
* A failure stands where the figure would, at body size, because a sentence in figure type is
|
|
181
207
|
* unreadable. A card whose figure is the only thing it has makes the figure the link.
|
|
182
208
|
*/
|
|
183
209
|
const valueText = renderText ? renderText("value", labels.value) : null;
|
|
184
|
-
const figure = valueText != null ? (_jsx("div", { style: { fontSize: token.fontSizeHeading3, color: highlight ?
|
|
210
|
+
const figure = valueText != null ? (_jsx("div", { style: { fontSize: token.fontSizeHeading3, color: highlight ? PHI_COLOR.primary : PHI_COLOR.textHeading, ...slotWidth }, children: valueText })) : (_jsx(PhiStatisticControl, { value: binding?.error ?? labels.value ?? "", loading: binding?.loading === true || overrides.loading === true, styles: {
|
|
185
211
|
content: binding?.error
|
|
186
|
-
? { color:
|
|
187
|
-
: { color: highlight ?
|
|
212
|
+
? { color: PHI_COLOR.error, fontSize: token.fontSize }
|
|
213
|
+
: { color: highlight ? PHI_COLOR.primary : PHI_COLOR.textHeading },
|
|
188
214
|
} }));
|
|
189
215
|
/* A failure stands where the lines would, as it does for a figure; a list still coming is three lines. */
|
|
190
|
-
const list = binding?.error ? (_jsx(PhiTypographyControl, { style: { color:
|
|
216
|
+
const list = binding?.error ? (_jsx(PhiTypographyControl, { style: { color: PHI_COLOR.error, ...slotWidth }, children: binding.error })) : binding?.loading === true || overrides.loading === true ? (_jsx(PhiSkeletonControl, { lines: 3 })) : listContent ?? null;
|
|
217
|
+
/*
|
|
218
|
+
* A plan's price: the period's own where it states one, the monthly one where it does not, so a switch
|
|
219
|
+
* to yearly never leaves a card without a price. An editor is asked for the shown period's fields.
|
|
220
|
+
*/
|
|
221
|
+
const yearly = (overrides.billingPeriod ?? config?.billingPeriod) === "yearly";
|
|
222
|
+
const showsYearly = yearly && (renderText != null || Boolean(labels.priceYearly));
|
|
223
|
+
const priceText = body === "plan" ? textOf(showsYearly ? "priceYearly" : "price") : null;
|
|
224
|
+
const periodText = body === "plan" ? textOf(showsYearly ? "pricePeriodYearly" : "pricePeriod") : null;
|
|
225
|
+
const plan = body === "plan" ? (_jsxs("div", { style: { display: "grid", gap, justifySelf: "stretch", justifyItems, minWidth: 0 }, children: [priceText != null || periodText != null ? (_jsxs("div", { style: { display: "flex", flexWrap: "wrap", alignItems: "baseline", justifyContent: justify, columnGap: PHI_SPACE.xs, ...slotWidth }, children: [priceText != null ? (_jsx("span", { className: "phi-card__price", style: {
|
|
226
|
+
fontSize: token.fontSizeHeading2,
|
|
227
|
+
lineHeight: token.lineHeightHeading2,
|
|
228
|
+
fontWeight: 600,
|
|
229
|
+
fontVariantNumeric: "tabular-nums",
|
|
230
|
+
color: highlight ? PHI_COLOR.primary : PHI_COLOR.textHeading,
|
|
231
|
+
...slotWidth,
|
|
232
|
+
}, children: priceText })) : null, periodText != null ? (_jsx("span", { style: { color: PHI_COLOR.textSecondary, ...slotWidth }, children: periodText })) : null] })) : null, list != null ? _jsx("div", { style: { justifySelf: "stretch", minWidth: 0, textAlign: "start" }, children: list }) : null] })) : null;
|
|
191
233
|
const headingRow = iconPlacement === "inline" && iconName ? (_jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: justify, gap, ...slotWidth }, children: [iconMark(iconSize, iconSize + 16), heading] })) : heading;
|
|
192
234
|
const media = image ? (_jsxs("div", { className: "phi-card__media", style: {
|
|
193
235
|
position: "relative",
|
|
@@ -208,9 +250,9 @@ export function PhiCardWidgetClient({ labels: placedLabels, config, binding, lis
|
|
|
208
250
|
justifyContent: "center",
|
|
209
251
|
width: topIconSize + 24,
|
|
210
252
|
height: topIconSize + 24,
|
|
211
|
-
borderRadius:
|
|
253
|
+
borderRadius: iconCorner(topIconSize + 24),
|
|
212
254
|
// A ground of its own, so the mark reads on a bright picture and on a dark one alike.
|
|
213
|
-
...iconColours(config?.iconColor ?? (highlight ?
|
|
255
|
+
...iconColours(config?.iconColor ?? (highlight ? PHI_COLOR.primary : PHI_COLOR.text), config?.iconBackground ?? `color-mix(in srgb, ${PHI_COLOR.bgContainer} 88%, transparent)`),
|
|
214
256
|
boxShadow: token.boxShadowTertiary,
|
|
215
257
|
}, children: _jsx(PhiIcon, { name: iconName, size: topIconSize }) })) : null, eyebrowOnMedia && eyebrow != null ? (_jsx(PhiTypographyControl, { type: "secondary", style: {
|
|
216
258
|
...eyebrowType,
|
|
@@ -222,8 +264,8 @@ export function PhiCardWidgetClient({ labels: placedLabels, config, binding, lis
|
|
|
222
264
|
paddingInline: token.paddingXS,
|
|
223
265
|
borderRadius: token.borderRadiusSM,
|
|
224
266
|
// A ground of its own, as the icon has, so the words read on any picture.
|
|
225
|
-
background: `color-mix(in srgb, ${
|
|
226
|
-
color: highlight ?
|
|
267
|
+
background: `color-mix(in srgb, ${PHI_COLOR.bgContainer} 88%, transparent)`,
|
|
268
|
+
color: highlight ? PHI_COLOR.primary : PHI_COLOR.text,
|
|
227
269
|
}, children: eyebrow })) : null] })) : null;
|
|
228
270
|
const boxStyle = {
|
|
229
271
|
...(config?.surface ? {} : { borderRadius: PHI_CARD_THEME_CORNER }),
|
|
@@ -235,21 +277,36 @@ export function PhiCardWidgetClient({ labels: placedLabels, config, binding, lis
|
|
|
235
277
|
minWidth: 0,
|
|
236
278
|
overflow: "hidden",
|
|
237
279
|
textAlign,
|
|
238
|
-
color:
|
|
280
|
+
color: PHI_COLOR.text,
|
|
239
281
|
/* The ring's colour; `.phi-card--highlight` draws it (`styles/layout.css`). */
|
|
240
|
-
...(highlight ? { "--phi-card-highlight-color":
|
|
282
|
+
...(highlight ? { "--phi-card-highlight-color": PHI_COLOR.primary } : {}),
|
|
241
283
|
};
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
284
|
+
/*
|
|
285
|
+
* A plan's button stands at the card's foot: plans side by side in a row have lists of different
|
|
286
|
+
* lengths, and their buttons belong on one line. The words keep their grid; the grid and the button
|
|
287
|
+
* stand in a column that fills the card, and the button takes what is left above it.
|
|
288
|
+
*/
|
|
289
|
+
const footAction = body === "plan" && action != null;
|
|
290
|
+
const actionButton = action ? (_jsx("div", { className: "phi-card__action",
|
|
291
|
+
/* At the foot, it stands where the words' alignment puts it, as it does among them. */
|
|
292
|
+
style: footAction ? { marginTop: "auto", alignSelf: textAlign === "center" ? "center" : textAlign === "end" ? "flex-end" : "flex-start" } : undefined, children: _jsx(PhiButtonControl, { type: highlight ? "primary" : "default", size: buttonSize, ...(action.href ? { href: action.href, newTab: action.newTab, external: action.external } : {}), label: labels.actionLabel || undefined, ...(labels.actionLabel ? {} : { ariaLabel: action.ariaLabel }), ...(action.icon ? { icon: _jsx(PhiIcon, { name: action.icon, size: "1em" }) } : {}), ...(activates ? { onClick: () => controlSignals.emitCapability("activate", null) } : {}) }) })) : null;
|
|
293
|
+
/*
|
|
294
|
+
* The band in the card's top end corner, floating (`PhiRibbonControl`): the words below it start where
|
|
295
|
+
* they start in a card without one, so plans side by side keep their headings on one line.
|
|
296
|
+
*/
|
|
297
|
+
const ribbon = badge != null ? (_jsx(PhiRibbonControl, { color: config?.badgeColor, placement: "corner", ...(renderText ? { style: { pointerEvents: "auto", minWidth: "6em" } } : {}), children: badge })) : null;
|
|
298
|
+
const words = (_jsxs("div", { style: { display: "grid", gap, padding: footAction ? 0 : inset, justifyItems }, children: [iconAtTop && iconName && !image ? (_jsx("div", { style: { justifySelf: markSide === "center" ? "center" : markSide === "end" ? "end" : "start" }, children: iconMark(topIconSize, topIconSize + 24) })) : null, eyebrow != null && !eyebrowOnMedia && !labels.eyebrowEnd ? (_jsx(PhiTypographyControl, { type: "secondary", style: { ...eyebrowType, color: highlight ? PHI_COLOR.primary : PHI_COLOR.textTertiary, ...slotWidth }, children: eyebrow })) : null, labels.eyebrowEnd && !eyebrowOnMedia ? (_jsxs(PhiFlexControl, { justify: "space-between", align: "baseline", wrap: true, gap: PHI_SPACE.xs, style: { justifySelf: "stretch", minWidth: 0 }, children: [_jsx(PhiTypographyControl, { type: "secondary", style: { ...eyebrowType, color: highlight ? PHI_COLOR.primary : PHI_COLOR.textTertiary }, children: eyebrow ?? "" }), _jsx(PhiTypographyControl, { type: "secondary", style: { ...eyebrowType, color: PHI_COLOR.textTertiary }, children: labels.eyebrowEnd })] })) : null, headingRow, body === "stat" ? figure : null, body === "list" && list != null ? (_jsx("div", { style: { justifySelf: "stretch", minWidth: 0 }, children: list })) : null, description != null ? (_jsx(PhiTypographyControl, { presentation: "paragraph", style: {
|
|
299
|
+
marginBottom: 0,
|
|
300
|
+
color: PHI_COLOR.textSecondary,
|
|
301
|
+
fontSize: descriptionSize,
|
|
302
|
+
...slotWidth,
|
|
303
|
+
}, children: description })) : null, plan, meta != null ? (_jsx(PhiTypographyControl, { type: "secondary", style: { fontSize: token.fontSizeSM, color: PHI_COLOR.textTertiary, ...slotWidth }, children: meta })) : null, footAction ? null : actionButton] }));
|
|
304
|
+
const content = (_jsxs(_Fragment, { children: [media, footAction ? (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap, padding: inset, flex: "1 1 auto", minWidth: 0 }, children: [words, actionButton] })) : words, cardLink] }));
|
|
248
305
|
return (_jsxs("article", { className: [
|
|
249
306
|
"phi-card",
|
|
250
307
|
href ? "phi-card--link" : null,
|
|
251
308
|
highlight ? "phi-card--highlight" : null,
|
|
252
309
|
hoverEffect !== "none" ? `phi-card--hover-${hoverEffect}` : null,
|
|
253
310
|
surface.className,
|
|
254
|
-
].filter(Boolean).join(" "), "data-phi-card-variant": variant, style: boxStyle, children: [_jsx(PhiSurfaceGroundLayer, { ground: surface.ground, className: "phi-card__ground" }), surface.className ? _jsx(PhiSurfaceTone, { tone: config?.surface?.tone, children: content }) : content] }));
|
|
311
|
+
].filter(Boolean).join(" "), "data-phi-card-variant": variant, style: boxStyle, children: [_jsx(PhiSurfaceGroundLayer, { ground: surface.ground, className: "phi-card__ground" }), ribbon, surface.className ? _jsx(PhiSurfaceTone, { tone: config?.surface?.tone, children: content }) : content] }));
|
|
255
312
|
}
|
|
@@ -4,12 +4,24 @@
|
|
|
4
4
|
* `text` is a heading. `stat` is a labelled figure: the label is the heading, so a card that counts
|
|
5
5
|
* something says what it counted in the same place and type a text card says what it is about, and the
|
|
6
6
|
* figure is drawn by the house's one statistic Control. `list` is a heading over a few lines the caller
|
|
7
|
-
* draws and hands in (`listContent`), which only a Dashboard does today.
|
|
7
|
+
* draws and hands in (`listContent`), which only a Dashboard does today. `plan` is an offer: a price
|
|
8
|
+
* and what it is paid per, a monthly and a yearly one of each, over a list of what the plan includes and
|
|
9
|
+
* what it does not (`PhiBulletListEntry.state`). Several plan cards side by side are a Layout's row, and
|
|
10
|
+
* the recommended one is the card that is `highlight`ed.
|
|
8
11
|
*
|
|
9
12
|
* The body is chosen, never inferred from whether a value happens to have arrived. A card that decided
|
|
10
13
|
* by presence would draw as text and reflow into a statistic the moment its figure landed.
|
|
11
14
|
*/
|
|
12
|
-
export type PhiCardWidgetBody = "text" | "stat" | "list";
|
|
15
|
+
export type PhiCardWidgetBody = "text" | "stat" | "list" | "plan";
|
|
16
|
+
/**
|
|
17
|
+
* Which of a plan card's two prices it shows. A plan states one, the other, or both; one that states
|
|
18
|
+
* only the monthly price shows it under `yearly` as well, so a period switch on the page never leaves a
|
|
19
|
+
* card without a price. The page switches every plan at once by sending `billingPeriod`.
|
|
20
|
+
*/
|
|
21
|
+
export declare const PHI_CARD_BILLING_PERIODS: readonly ["monthly", "yearly"];
|
|
22
|
+
export type PhiCardBillingPeriod = (typeof PHI_CARD_BILLING_PERIODS)[number];
|
|
23
|
+
/** A sent period as one of the two; anything else is not a period and changes nothing. */
|
|
24
|
+
export declare function readPhiCardBillingPeriod(value: unknown): PhiCardBillingPeriod | undefined;
|
|
13
25
|
/**
|
|
14
26
|
* The tag of the card's heading, and the step it is drawn at.
|
|
15
27
|
*
|
|
@@ -48,6 +60,11 @@ export type PhiCardHoverEffect = (typeof PHI_CARD_HOVER_EFFECTS)[number];
|
|
|
48
60
|
*/
|
|
49
61
|
export declare const PHI_CARD_VARIANTS: readonly ["default", "compact", "center"];
|
|
50
62
|
export type PhiCardVariant = (typeof PHI_CARD_VARIANTS)[number];
|
|
51
|
-
/**
|
|
52
|
-
|
|
63
|
+
/**
|
|
64
|
+
* The words a card shows, each in its own place; what an editor can put a field in, and what a signal can
|
|
65
|
+
* write. `badge` is the band at the card's end edge, any body may carry one. The prices are figures like
|
|
66
|
+
* `value` and are never translated: a price that differs by country is sent by a Controller or comes
|
|
67
|
+
* from a Provider's row, not from a translator. The periods (`/ month`) are words and are.
|
|
68
|
+
*/
|
|
69
|
+
export declare const PHI_CARD_TEXT_SLOTS: readonly ["eyebrow", "title", "description", "meta", "value", "badge", "price", "pricePeriod", "priceYearly", "pricePeriodYearly"];
|
|
53
70
|
export type PhiCardTextSlot = (typeof PHI_CARD_TEXT_SLOTS)[number];
|
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
* The Card Widget's words, apart from its Client half: the Widget's definition is read on the server, and
|
|
3
3
|
* a value imported from the Client file would make that file a client reference of every route.
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* Which of a plan card's two prices it shows. A plan states one, the other, or both; one that states
|
|
7
|
+
* only the monthly price shows it under `yearly` as well, so a period switch on the page never leaves a
|
|
8
|
+
* card without a price. The page switches every plan at once by sending `billingPeriod`.
|
|
9
|
+
*/
|
|
10
|
+
export const PHI_CARD_BILLING_PERIODS = ["monthly", "yearly"];
|
|
11
|
+
/** A sent period as one of the two; anything else is not a period and changes nothing. */
|
|
12
|
+
export function readPhiCardBillingPeriod(value) {
|
|
13
|
+
return PHI_CARD_BILLING_PERIODS.includes(value) ? value : undefined;
|
|
14
|
+
}
|
|
5
15
|
/**
|
|
6
16
|
* The tag of the card's heading, and the step it is drawn at.
|
|
7
17
|
*
|
|
@@ -35,5 +45,21 @@ export const PHI_CARD_HOVER_EFFECTS = ["none", "lift", "zoom", "icon"];
|
|
|
35
45
|
* not another branch in every size the card draws. `default` is what a card without one is.
|
|
36
46
|
*/
|
|
37
47
|
export const PHI_CARD_VARIANTS = ["default", "compact", "center"];
|
|
38
|
-
/**
|
|
39
|
-
|
|
48
|
+
/**
|
|
49
|
+
* The words a card shows, each in its own place; what an editor can put a field in, and what a signal can
|
|
50
|
+
* write. `badge` is the band at the card's end edge, any body may carry one. The prices are figures like
|
|
51
|
+
* `value` and are never translated: a price that differs by country is sent by a Controller or comes
|
|
52
|
+
* from a Provider's row, not from a translator. The periods (`/ month`) are words and are.
|
|
53
|
+
*/
|
|
54
|
+
export const PHI_CARD_TEXT_SLOTS = [
|
|
55
|
+
"eyebrow",
|
|
56
|
+
"title",
|
|
57
|
+
"description",
|
|
58
|
+
"meta",
|
|
59
|
+
"value",
|
|
60
|
+
"badge",
|
|
61
|
+
"price",
|
|
62
|
+
"pricePeriod",
|
|
63
|
+
"priceYearly",
|
|
64
|
+
"pricePeriodYearly",
|
|
65
|
+
];
|
|
@@ -328,21 +328,3 @@ export declare const PHI_MULTI_SELECT_CONTROL_SIGNALS: {
|
|
|
328
328
|
valueType: "none";
|
|
329
329
|
})[];
|
|
330
330
|
};
|
|
331
|
-
export declare const PHI_DRAG_SOURCE_CONTROL_SIGNALS: {
|
|
332
|
-
emits: ({
|
|
333
|
-
id: string;
|
|
334
|
-
action: "start";
|
|
335
|
-
valueType: "json";
|
|
336
|
-
valueSchema: `${string}/signals/${string}`;
|
|
337
|
-
} | {
|
|
338
|
-
id: string;
|
|
339
|
-
action: "change";
|
|
340
|
-
valueType: "json";
|
|
341
|
-
valueSchema: `${string}/signals/${string}`;
|
|
342
|
-
} | {
|
|
343
|
-
id: string;
|
|
344
|
-
action: "stop";
|
|
345
|
-
valueType: "json";
|
|
346
|
-
valueSchema: `${string}/signals/${string}`;
|
|
347
|
-
})[];
|
|
348
|
-
};
|
|
@@ -89,6 +89,16 @@ export const PHI_CARD_SIGNALS = {
|
|
|
89
89
|
{ id: "description", channel: "description", action: "change", valueType: "string" },
|
|
90
90
|
{ id: "meta", channel: "meta", action: "change", valueType: "string" },
|
|
91
91
|
{ id: "highlight", channel: "highlight", action: "change", valueType: "boolean" },
|
|
92
|
+
{ id: "badge", channel: "badge", action: "change", valueType: "string" },
|
|
93
|
+
/*
|
|
94
|
+
* A plan's prices, for a Controller that knows them better than the page does -- by the reader's
|
|
95
|
+
* country, say -- and the period a switch on the page shows (`monthly` or `yearly`).
|
|
96
|
+
*/
|
|
97
|
+
{ id: "price", channel: "price", action: "change", valueType: "string" },
|
|
98
|
+
{ id: "pricePeriod", channel: "pricePeriod", action: "change", valueType: "string" },
|
|
99
|
+
{ id: "priceYearly", channel: "priceYearly", action: "change", valueType: "string" },
|
|
100
|
+
{ id: "pricePeriodYearly", channel: "pricePeriodYearly", action: "change", valueType: "string" },
|
|
101
|
+
{ id: "billingPeriod", channel: "billingPeriod", action: "change", valueType: "string" },
|
|
92
102
|
],
|
|
93
103
|
};
|
|
94
104
|
export const PHI_TEXT_CONTROL_SIGNALS = {
|
|
@@ -227,25 +237,3 @@ export const PHI_MULTI_SELECT_CONTROL_SIGNALS = {
|
|
|
227
237
|
{ ...PHI_CONTROL_CLEAR_SIGNAL, channel: "selection" },
|
|
228
238
|
],
|
|
229
239
|
};
|
|
230
|
-
export const PHI_DRAG_SOURCE_CONTROL_SIGNALS = {
|
|
231
|
-
emits: [
|
|
232
|
-
{
|
|
233
|
-
id: "dragStart",
|
|
234
|
-
action: "start",
|
|
235
|
-
valueType: "json",
|
|
236
|
-
valueSchema: PHI_SIGNAL_VALUE_SCHEMAS.dragDrop,
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
id: "dragChange",
|
|
240
|
-
action: "change",
|
|
241
|
-
valueType: "json",
|
|
242
|
-
valueSchema: PHI_SIGNAL_VALUE_SCHEMAS.dragDrop,
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
id: "dragEnd",
|
|
246
|
-
action: "stop",
|
|
247
|
-
valueType: "json",
|
|
248
|
-
valueSchema: PHI_SIGNAL_VALUE_SCHEMAS.dragDrop,
|
|
249
|
-
},
|
|
250
|
-
],
|
|
251
|
-
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { PhiWorkspaceCatalogState } from "./catalog-state";
|
|
2
2
|
export type { PhiWorkspaceCatalogArea, PhiWorkspaceCatalogState } from "./catalog-state";
|
|
3
3
|
/**
|
|
4
|
-
* One scope per workspace host. Both the Builder and, later, the Editor write into
|
|
4
|
+
* One scope per workspace host, not one per Area. Both the Builder and, later, the Editor write into it;
|
|
5
|
+
* which Area is open is `area` inside it, and what belongs to an Area is keyed by Area there.
|
|
5
6
|
*/
|
|
6
7
|
export declare const PHI_WORKSPACE_CATALOG_SCOPE = "public";
|
|
7
8
|
export declare const phiWorkspaceCatalogStore: import("../state/plugin-state-store").PhiPluginStateStore<PhiWorkspaceCatalogState>;
|
|
8
|
-
export declare function usePhiWorkspaceCatalogValue<TSelected>(
|
|
9
|
-
export declare function getPhiWorkspaceCatalogSnapshot(
|
|
9
|
+
export declare function usePhiWorkspaceCatalogValue<TSelected>(selector: (state: PhiWorkspaceCatalogState) => TSelected): TSelected;
|
|
10
|
+
export declare function getPhiWorkspaceCatalogSnapshot(): PhiWorkspaceCatalogState;
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { createEmptyPhiBuilderModulePresetPagesByArea } from "../../helpers/cms-page-catalog";
|
|
3
3
|
import { createPhiPluginStateStore } from "../state/plugin-state-store";
|
|
4
4
|
/**
|
|
5
|
-
* One scope per workspace host. Both the Builder and, later, the Editor write into
|
|
5
|
+
* One scope per workspace host, not one per Area. Both the Builder and, later, the Editor write into it;
|
|
6
|
+
* which Area is open is `area` inside it, and what belongs to an Area is keyed by Area there.
|
|
6
7
|
*/
|
|
7
8
|
export const PHI_WORKSPACE_CATALOG_SCOPE = "public";
|
|
8
9
|
function createDefaultWorkspaceCatalogState(scopeKey) {
|
|
@@ -25,9 +26,9 @@ function createDefaultWorkspaceCatalogState(scopeKey) {
|
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
export const phiWorkspaceCatalogStore = createPhiPluginStateStore("@phis/ui/workspace-catalog", createDefaultWorkspaceCatalogState);
|
|
28
|
-
export function usePhiWorkspaceCatalogValue(
|
|
29
|
-
return phiWorkspaceCatalogStore.useStoreSelector(
|
|
29
|
+
export function usePhiWorkspaceCatalogValue(selector) {
|
|
30
|
+
return phiWorkspaceCatalogStore.useStoreSelector(PHI_WORKSPACE_CATALOG_SCOPE, selector);
|
|
30
31
|
}
|
|
31
|
-
export function getPhiWorkspaceCatalogSnapshot(
|
|
32
|
-
return phiWorkspaceCatalogStore.getSnapshot(
|
|
32
|
+
export function getPhiWorkspaceCatalogSnapshot() {
|
|
33
|
+
return phiWorkspaceCatalogStore.getSnapshot(PHI_WORKSPACE_CATALOG_SCOPE);
|
|
33
34
|
}
|
|
@@ -79,6 +79,7 @@ async function resolveDashboardCardScope({ bridge, area, locale, path, cookieHea
|
|
|
79
79
|
locale,
|
|
80
80
|
area,
|
|
81
81
|
viewer: requestContext.viewer,
|
|
82
|
+
runtimeModuleCatalog: bridge.runtimeModuleCatalog,
|
|
82
83
|
};
|
|
83
84
|
/*
|
|
84
85
|
* A route handler has no request runtime bound, and the label sets a card reads for its own title
|