@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
|
@@ -50,7 +50,7 @@ export function PhiSimpleTextWidgetClient({ blockId, labels, config, }) {
|
|
|
50
50
|
const resolvedFontFamily = styleValue.code
|
|
51
51
|
? token.fontFamilyCode
|
|
52
52
|
: resolvePhiWidgetFontFamily(fontFamilyValue, fonts, token);
|
|
53
|
-
const resolvedFontSize = resolvePhiWidgetFontSize(fontSizeValue, token, "
|
|
53
|
+
const resolvedFontSize = resolvePhiWidgetFontSize(fontSizeValue, token, "base");
|
|
54
54
|
const receiver = createPhiRenderableBlockReceiver("widget", blockId);
|
|
55
55
|
usePhiRenderableBlockSignalListener(receiver, (signal) => {
|
|
56
56
|
if (signal.channel === "text") {
|
|
@@ -105,16 +105,21 @@ export function PhiSimpleTextWidgetClient({ blockId, labels, config, }) {
|
|
|
105
105
|
code: typeof stylePayload.code === "boolean" ? stylePayload.code : current.code,
|
|
106
106
|
}));
|
|
107
107
|
});
|
|
108
|
+
/*
|
|
109
|
+
* A linked text's colour is the link's (`PhiLink`), which answers the pointer; the words inside inherit
|
|
110
|
+
* it. Stated on them as well, the colour stood over the link's and the hover never showed.
|
|
111
|
+
*/
|
|
112
|
+
const contentColor = hasHref ? undefined : textColor;
|
|
108
113
|
const contentNode = (_jsxs(PhiFlexControl, { align: "center", gap: 8, style: {
|
|
109
114
|
width: "fit-content",
|
|
110
115
|
maxWidth: "100%",
|
|
111
116
|
minWidth: 0,
|
|
112
117
|
fontSize: resolvedFontSize ?? "inherit",
|
|
113
118
|
lineHeight: resolvedFontSize ? PHI_LINE_HEIGHT_LG : "inherit",
|
|
114
|
-
color:
|
|
119
|
+
color: contentColor ?? undefined,
|
|
115
120
|
fontFamily: resolvedFontFamily,
|
|
116
121
|
}, children: [iconValue ? _jsx(PhiIcon, { name: iconValue, size: "1.25em" }) : null, _jsx(PhiTypographyControl, { type: config?.tone, strong: styleValue.strong, italic: styleValue.italic, underline: styleValue.underline, delete: styleValue.delete, disabled: styleValue.disabled, code: styleValue.code, style: {
|
|
117
|
-
color:
|
|
122
|
+
color: contentColor ?? "inherit",
|
|
118
123
|
fontSize: resolvedFontSize ?? "inherit",
|
|
119
124
|
lineHeight: resolvedFontSize ? PHI_LINE_HEIGHT_LG : "inherit",
|
|
120
125
|
...(resolvedFontFamily ? { fontFamily: resolvedFontFamily } : {}),
|
|
@@ -43,7 +43,7 @@ export function PhiSimpleTextWidgetColorButton({ config, onChange, }) {
|
|
|
43
43
|
}
|
|
44
44
|
export function PhiSimpleTextWidgetTypographyButton({ config, onChange, }) {
|
|
45
45
|
const labels = usePhiAuthoringToolsLabels();
|
|
46
|
-
return (_jsx(PhiWidgetTypographyToolButton, { fontFamily: config?.fontFamily, fontSize: config?.fontSize, defaultFontSize: "
|
|
46
|
+
return (_jsx(PhiWidgetTypographyToolButton, { fontFamily: config?.fontFamily, fontSize: config?.fontSize, defaultFontSize: "base", ariaLabel: labels.text.typography, onChange: ({ fontFamily, fontSize }) => onChange({
|
|
47
47
|
...(fontFamily !== undefined ? { fontFamily: fontFamily ?? undefined } : {}),
|
|
48
48
|
...(fontSize !== undefined ? { fontSize: fontSize ?? undefined } : {}),
|
|
49
49
|
}) }));
|
|
@@ -71,7 +71,7 @@ export function PhiSimpleTextWidgetEditor({ text, config, onChangeText, }) {
|
|
|
71
71
|
const resolvedFontFamily = hasPhiSimpleTextMark(config, "code")
|
|
72
72
|
? token.fontFamilyCode
|
|
73
73
|
: resolvePhiWidgetFontFamily(config?.fontFamily, fonts, token);
|
|
74
|
-
const resolvedFontSize = resolvePhiWidgetFontSize(config?.fontSize, token, "
|
|
74
|
+
const resolvedFontSize = resolvePhiWidgetFontSize(config?.fontSize, token, "base");
|
|
75
75
|
return (_jsxs(PhiFlexControl, { align: "center", gap: 8, style: {
|
|
76
76
|
width: "fit-content",
|
|
77
77
|
maxWidth: "100%",
|
|
@@ -73,6 +73,7 @@ export declare const PHI_TABLE_WIDGET_DEFINITION: {
|
|
|
73
73
|
} & {
|
|
74
74
|
type: "string" | "url" | "icon";
|
|
75
75
|
emptyValue?: "";
|
|
76
|
+
placeholder?: string;
|
|
76
77
|
}) | ({
|
|
77
78
|
key: string;
|
|
78
79
|
label: string;
|
|
@@ -373,6 +374,7 @@ export declare const PHI_TABLE_WIDGET_DEFINITION: {
|
|
|
373
374
|
heading?: string;
|
|
374
375
|
type: "string" | "url" | "icon";
|
|
375
376
|
emptyValue?: "";
|
|
377
|
+
placeholder?: string;
|
|
376
378
|
} | {
|
|
377
379
|
visibleWhen: {
|
|
378
380
|
field: string;
|
|
@@ -738,6 +740,7 @@ export declare const PHI_TABLE_WIDGET_DEFINITION: {
|
|
|
738
740
|
heading?: string;
|
|
739
741
|
type: "string" | "url" | "icon";
|
|
740
742
|
emptyValue?: "";
|
|
743
|
+
placeholder?: string;
|
|
741
744
|
} | {
|
|
742
745
|
visibleWhen: {
|
|
743
746
|
field: string;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { type PhiDashboardCardPayload, type PhiDashboardCardRow } from "../../../types/dashboard-cards";
|
|
1
|
+
import { type PhiDashboardCardPayload, type PhiDashboardCardSeries, type PhiDashboardCardRow } from "../../../types/dashboard-cards";
|
|
2
2
|
/** The card rows a list answer carries, or a Collection holds. */
|
|
3
3
|
export declare function readPhiDashboardCardRows(value: unknown, source?: string): PhiDashboardCardRow[];
|
|
4
4
|
/** One card's refreshed payload. */
|
|
5
5
|
export declare function readPhiDashboardCardPayload(value: unknown): PhiDashboardCardPayload;
|
|
6
|
+
/** The latest figure of the first series: the headline of a `series` card whose provider states none. */
|
|
7
|
+
export declare function readPhiDashboardSeriesHeadline(series: PhiDashboardCardSeries, locale: string): string | undefined;
|
|
@@ -9,16 +9,38 @@ import { createPhiResponseReader, requirePhiField, } from "../../../components/w
|
|
|
9
9
|
* shape rendered as an empty card. Read field by field now, a wrong answer is an error naming the field,
|
|
10
10
|
* which the Collection shows as the card list's error.
|
|
11
11
|
*/
|
|
12
|
-
const PHI_DASHBOARD_CARD_FORMS = ["stat", "list"];
|
|
12
|
+
const PHI_DASHBOARD_CARD_FORMS = ["stat", "list", "series"];
|
|
13
13
|
function readCardId(reader) {
|
|
14
14
|
return requirePhiField(reader, "cardId", "a Dashboard card id", (key) => reader.value(key, "a Dashboard card id", (value) => (isPhiDashboardCardId(value) ? value : undefined)));
|
|
15
15
|
}
|
|
16
16
|
function readTimelineEntry(reader, index) {
|
|
17
17
|
return readPhiTimelineEntry(reader.record) ?? reader.fail(String(index), "a timeline entry with a key", reader.record);
|
|
18
18
|
}
|
|
19
|
+
/*
|
|
20
|
+
* A series is read whole: a point with one value too few would draw a line that drops to nothing on the
|
|
21
|
+
* day it lost, which looks like data. So each point states a finite number for every label, or the payload
|
|
22
|
+
* is an error naming the point.
|
|
23
|
+
*/
|
|
24
|
+
function readSeries(reader) {
|
|
25
|
+
const labels = requirePhiField(reader, "labels", "a list of series labels", reader.stringList);
|
|
26
|
+
if (labels.length === 0) {
|
|
27
|
+
reader.fail("labels", "at least one series label", labels);
|
|
28
|
+
}
|
|
29
|
+
const points = requirePhiField(reader, "points", "a list of points", (key) => reader.list(key, (point) => ({
|
|
30
|
+
at: requirePhiField(point, "at", "an ISO date or instant", point.string),
|
|
31
|
+
values: requirePhiField(point, "values", `${labels.length} finite numbers, one per label`, (valuesKey) => point.value(valuesKey, `${labels.length} finite numbers, one per label`, (value) => Array.isArray(value)
|
|
32
|
+
&& value.length === labels.length
|
|
33
|
+
&& value.every((entry) => typeof entry === "number" && Number.isFinite(entry))
|
|
34
|
+
? value
|
|
35
|
+
: undefined)),
|
|
36
|
+
})));
|
|
37
|
+
const unit = reader.string("unit");
|
|
38
|
+
return { labels, points, ...(unit === undefined ? {} : { unit }) };
|
|
39
|
+
}
|
|
19
40
|
function readPayloadFields(reader) {
|
|
20
41
|
const value = reader.string("value");
|
|
21
42
|
const entries = reader.list("entries", readTimelineEntry);
|
|
43
|
+
const series = reader.object("series", readSeries);
|
|
22
44
|
const description = reader.string("description");
|
|
23
45
|
const meta = reader.string("meta");
|
|
24
46
|
const error = reader.string("error");
|
|
@@ -26,6 +48,7 @@ function readPayloadFields(reader) {
|
|
|
26
48
|
cardId: readCardId(reader),
|
|
27
49
|
...(value === undefined ? {} : { value }),
|
|
28
50
|
...(entries === undefined ? {} : { entries }),
|
|
51
|
+
...(series === undefined ? {} : { series }),
|
|
29
52
|
...(description === undefined ? {} : { description }),
|
|
30
53
|
...(meta === undefined ? {} : { meta }),
|
|
31
54
|
...(error === undefined ? {} : { error }),
|
|
@@ -40,7 +63,7 @@ function readRowFields(reader) {
|
|
|
40
63
|
const payload = reader.object("payload", readPayloadFields);
|
|
41
64
|
return {
|
|
42
65
|
cardId: readCardId(reader),
|
|
43
|
-
form: requirePhiField(reader, "form", '"stat" or "
|
|
66
|
+
form: requirePhiField(reader, "form", '"stat", "list" or "series"', (key) => reader.oneOf(key, PHI_DASHBOARD_CARD_FORMS)),
|
|
44
67
|
title: requirePhiField(reader, "title", "a text", reader.string),
|
|
45
68
|
...(eyebrow === undefined ? {} : { eyebrow }),
|
|
46
69
|
...(description === undefined ? {} : { description }),
|
|
@@ -57,3 +80,8 @@ export function readPhiDashboardCardRows(value, source = "The Dashboard card lis
|
|
|
57
80
|
export function readPhiDashboardCardPayload(value) {
|
|
58
81
|
return readPayloadFields(createPhiResponseReader(value, "The Dashboard card"));
|
|
59
82
|
}
|
|
83
|
+
/** The latest figure of the first series: the headline of a `series` card whose provider states none. */
|
|
84
|
+
export function readPhiDashboardSeriesHeadline(series, locale) {
|
|
85
|
+
const latest = series.points[series.points.length - 1]?.values[0];
|
|
86
|
+
return latest === undefined ? undefined : new Intl.NumberFormat(locale).format(latest);
|
|
87
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useMemo, useState } from "react";
|
|
4
4
|
import { readPhiTimelineEntries } from "@phis/contracts/timeline";
|
|
5
|
-
import { PHI_DASHBOARD_CARD_LIST_LIMIT, } from "../../../../types/dashboard-cards";
|
|
5
|
+
import { PHI_DASHBOARD_CARD_LIST_LIMIT, PHI_DASHBOARD_CARD_SERIES_LIMIT, } from "../../../../types/dashboard-cards";
|
|
6
6
|
import { PhiCardWidgetClient } from "../../../../components/widgets/shared/card-body-client";
|
|
7
7
|
import {} from "../../../../components/widgets/shared/card-vocabulary";
|
|
8
8
|
import { PHI_SURFACE_CARD } from "../../../../helpers/surface-presets";
|
|
@@ -10,10 +10,11 @@ import { PhiCollectionViewControl } from "../../../../components/controls/phi-co
|
|
|
10
10
|
import { PhiAlertControl } from "../../../../components/controls/phi-alert-control";
|
|
11
11
|
import { PhiEmptyControl } from "../../../../components/controls/phi-empty-control";
|
|
12
12
|
import { usePhiPathname } from "../../../../components/runtime/use-phi-pathname";
|
|
13
|
-
import { readPhiDashboardCardPayload, readPhiDashboardCardRows } from "../card-rows";
|
|
13
|
+
import { readPhiDashboardCardPayload, readPhiDashboardCardRows, readPhiDashboardSeriesHeadline, } from "../card-rows";
|
|
14
14
|
import { PhiTimelineControl } from "../../../../components/controls/phi-timeline-control";
|
|
15
|
+
import { PhiSeriesChartControl } from "../../../../components/controls/phi-series-chart-control";
|
|
15
16
|
import { usePhiIntlLocale } from "../../../../components/root/phi-intl-locale";
|
|
16
|
-
import { formatPhiRelativeTime } from "../../../../helpers/format-date-time";
|
|
17
|
+
import { formatPhiDate, formatPhiRelativeTime } from "../../../../helpers/format-date-time";
|
|
17
18
|
/**
|
|
18
19
|
* A Dashboard, drawn.
|
|
19
20
|
*
|
|
@@ -33,8 +34,7 @@ const CARD_MIN_COLUMN_WIDTH = 260;
|
|
|
33
34
|
* A Module's form, and the Core body that draws it.
|
|
34
35
|
*
|
|
35
36
|
* Two vocabularies that agree today, and deliberately not one type. `form` is what a Module
|
|
36
|
-
* declares about its contribution; `body` is what Core draws. They agree now and are allowed to stop
|
|
37
|
-
* a `series` form would reasonably draw in whatever body Core has for a graphic, under another name.
|
|
37
|
+
* declares about its contribution; `body` is what Core draws. They agree now and are allowed to stop.
|
|
38
38
|
*
|
|
39
39
|
* A total map rather than a condition, so a form that joins the vocabulary stops this file compiling
|
|
40
40
|
* instead of quietly falling back to a heading.
|
|
@@ -42,6 +42,7 @@ const CARD_MIN_COLUMN_WIDTH = 260;
|
|
|
42
42
|
const CARD_BODY_BY_FORM = {
|
|
43
43
|
stat: "stat",
|
|
44
44
|
list: "list",
|
|
45
|
+
series: "series",
|
|
45
46
|
};
|
|
46
47
|
/**
|
|
47
48
|
* A `list` card's entries as the Timeline draws them: the first few, each time in the page's locale.
|
|
@@ -62,6 +63,24 @@ function PhiDashboardCardList({ entries }) {
|
|
|
62
63
|
...(entry.pending ? { pending: true } : {}),
|
|
63
64
|
})) }));
|
|
64
65
|
}
|
|
66
|
+
/*
|
|
67
|
+
* A point's day as the calendar day it names. An ISO date read by `Date` is midnight in UTC, which is
|
|
68
|
+
* the day before west of Greenwich; an instant is formatted where the reader is.
|
|
69
|
+
*/
|
|
70
|
+
function formatSeriesKey(at, locale) {
|
|
71
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(at)
|
|
72
|
+
? new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeZone: "UTC" }).format(new Date(`${at}T00:00:00Z`))
|
|
73
|
+
: formatPhiDate(at, locale);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A `series` card's chart: its first few series over every point, each day in the page's locale. Drawn
|
|
77
|
+
* here and handed to the card, as a list is, so a Card on a Public page never loads the chart Control.
|
|
78
|
+
*/
|
|
79
|
+
function PhiDashboardCardSeriesChart({ series, title }) {
|
|
80
|
+
const locale = usePhiIntlLocale();
|
|
81
|
+
const labels = series.labels.slice(0, PHI_DASHBOARD_CARD_SERIES_LIMIT);
|
|
82
|
+
return (_jsx(PhiSeriesChartControl, { locale: locale, title: title, keys: series.points.map((point) => formatSeriesKey(point.at, locale)), series: labels.map((label, index) => ({ label, values: series.points.map((point) => point.values[index]) })), ...(series.unit ? { unit: series.unit } : {}) }));
|
|
83
|
+
}
|
|
65
84
|
function readRows(binding) {
|
|
66
85
|
// A Collection holds items of no particular type; the rows are read again, not cast.
|
|
67
86
|
return readPhiDashboardCardRows(binding.data?.items ?? [], "The Dashboard Collection");
|
|
@@ -84,6 +103,7 @@ function PhiDashboardCard({ row, area, pathname, }) {
|
|
|
84
103
|
* says the same thing by comparison: an answer to a question no longer being asked is simply not this
|
|
85
104
|
* card's answer.
|
|
86
105
|
*/
|
|
106
|
+
const locale = usePhiIntlLocale();
|
|
87
107
|
const requestKey = `${area}\u0000${pathname}\u0000${row.cardId}`;
|
|
88
108
|
const delivered = row.payload ?? null;
|
|
89
109
|
const [resolved, setResolved] = useState(null);
|
|
@@ -125,6 +145,8 @@ function PhiDashboardCard({ row, area, pathname, }) {
|
|
|
125
145
|
const payload = current?.payload ?? null;
|
|
126
146
|
const error = current?.failure ?? payload?.error ?? null;
|
|
127
147
|
const entries = row.form === "list" ? readPhiTimelineEntries(payload?.entries) : [];
|
|
148
|
+
const series = row.form === "series" ? payload?.series ?? null : null;
|
|
149
|
+
const value = payload?.value ?? (series ? readPhiDashboardSeriesHeadline(series, locale) : undefined);
|
|
128
150
|
return (_jsx(PhiCardWidgetClient, { labels: {
|
|
129
151
|
...(row.eyebrow ? { eyebrow: row.eyebrow } : {}),
|
|
130
152
|
/*
|
|
@@ -135,7 +157,7 @@ function PhiDashboardCard({ row, area, pathname, }) {
|
|
|
135
157
|
* screen together and the card reads the same before and after it resolves.
|
|
136
158
|
*/
|
|
137
159
|
title: row.title,
|
|
138
|
-
...(
|
|
160
|
+
...(value ? { value } : {}),
|
|
139
161
|
...(payload?.description ?? row.description
|
|
140
162
|
? { description: payload?.description ?? row.description }
|
|
141
163
|
: {}),
|
|
@@ -152,7 +174,7 @@ function PhiDashboardCard({ row, area, pathname, }) {
|
|
|
152
174
|
* this; that this file still does is what will move to a Controller when the Dashboard gets a
|
|
153
175
|
* clock (DASHBOARD.md section 4).
|
|
154
176
|
*/
|
|
155
|
-
binding: { loading: current === null, error }, listContent: entries.length > 0 ? _jsx(PhiDashboardCardList, { entries: entries }) : null }));
|
|
177
|
+
binding: { loading: current === null, error }, listContent: series ? (_jsx(PhiDashboardCardSeriesChart, { series: series, title: row.title })) : entries.length > 0 ? _jsx(PhiDashboardCardList, { entries: entries }) : null }));
|
|
156
178
|
}
|
|
157
179
|
export function PhiDashboardCardCollectionViewBinding({ config, binding, widgetId, }) {
|
|
158
180
|
void widgetId;
|
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
import { createPhiPresetCmsInstanceIdMap } from "../../../../types/cms-instance-id";
|
|
2
2
|
import { PHI_DASHBOARD_RUNTIME_MODULE_ID } from "../ids";
|
|
3
|
-
import {
|
|
4
|
-
import { PhiCmsFlags, PhiCmsPageType, PhiCmsRegionType } from "../../../../constants/phi-cms";
|
|
3
|
+
import { PhiCmsPageType } from "../../../../constants/phi-cms";
|
|
5
4
|
import { createPhiCmsPresetNodes } from "../../../../helpers/cms-preset-nodes";
|
|
6
|
-
import { resolvePhiShellHeaderHeight } from "../../../../helpers/shell-region-style";
|
|
7
5
|
import { buildPhiBasePageContentScaffold } from "../../../../components/regions/presets/phi-base-page-layout";
|
|
8
6
|
import { getPhiBuilderChromeWidgetLabels } from "../../../../components/widgets/label-sets/builder-chrome";
|
|
9
7
|
import { readPhiServerApiCredentials } from "../../../../helpers/phis-server-credentials";
|
|
10
8
|
import { buildPhiDashboardCardsWidget } from "./dashboard-cards-page-tree";
|
|
11
9
|
import { getPhiDashboardCardsEmptyDescription } from "./area-dashboard-label-set";
|
|
10
|
+
/*
|
|
11
|
+
* No Header bottom band: the Dashboard has no commands -- nothing to save, preview or publish -- and the
|
|
12
|
+
* band stood empty under the title.
|
|
13
|
+
*/
|
|
12
14
|
const SYNTHETIC_BUILDER_DASHBOARD_REGION_IDS = {
|
|
13
|
-
regionHeaderBottom: -580,
|
|
14
15
|
regionContent: -581,
|
|
15
16
|
};
|
|
16
|
-
const SYNTHETIC_BUILDER_DASHBOARD_LAYOUT_IDS = createPhiPresetCmsInstanceIdMap({
|
|
17
|
-
domain: "page",
|
|
18
|
-
ownerModuleId: PHI_DASHBOARD_RUNTIME_MODULE_ID,
|
|
19
|
-
presetKey: "builder-dashboard-page",
|
|
20
|
-
}, [
|
|
21
|
-
"layoutHeaderBottom",
|
|
22
|
-
]);
|
|
23
17
|
const SYNTHETIC_BUILDER_DASHBOARD_WIDGET_IDS = createPhiPresetCmsInstanceIdMap({
|
|
24
18
|
domain: "page",
|
|
25
19
|
ownerModuleId: PHI_DASHBOARD_RUNTIME_MODULE_ID,
|
|
@@ -56,38 +50,9 @@ export async function buildPhiDefaultBuilderDashboardPageTree({ page, runtime, }
|
|
|
56
50
|
},
|
|
57
51
|
overlays: [],
|
|
58
52
|
regions: [
|
|
59
|
-
nodes.region({
|
|
60
|
-
id: SYNTHETIC_BUILDER_DASHBOARD_REGION_IDS.regionHeaderBottom,
|
|
61
|
-
regionType: PhiCmsRegionType.HeaderBottom,
|
|
62
|
-
rootLayoutNodeId: SYNTHETIC_BUILDER_DASHBOARD_LAYOUT_IDS.layoutHeaderBottom,
|
|
63
|
-
sortOrder: 25,
|
|
64
|
-
config: {
|
|
65
|
-
flags: PhiCmsFlags.Sticky,
|
|
66
|
-
/*
|
|
67
|
-
* No Effect or Shadow of its own, like every other Builder Header. A `glass` authored here
|
|
68
|
-
* frosts this one Region against the Site's Shell Chrome Overlay, so the Dashboard's Header
|
|
69
|
-
* stopped matching the Sider and Footer beside it. What the frame looks like belongs to the Theme.
|
|
70
|
-
*/
|
|
71
|
-
size: { height: `${resolvePhiShellHeaderHeight(runtime.site.theme?.shell, "bottom")}px` },
|
|
72
|
-
offsetTop: resolvePhiShellHeaderHeight(runtime.site.theme?.shell, "main"),
|
|
73
|
-
},
|
|
74
|
-
}),
|
|
75
53
|
scaffold.region,
|
|
76
54
|
],
|
|
77
55
|
layoutNodes: [
|
|
78
|
-
nodes.layout({
|
|
79
|
-
creationPreset: { layoutKind: "threecol", preset: "panel" },
|
|
80
|
-
typeKey: "three-column",
|
|
81
|
-
id: SYNTHETIC_BUILDER_DASHBOARD_LAYOUT_IDS.layoutHeaderBottom,
|
|
82
|
-
parentLayoutNodeId: null,
|
|
83
|
-
slotIndex: PHI_CMS_DEFAULT_SLOT_INDEX,
|
|
84
|
-
sortOrder: 0,
|
|
85
|
-
label: "builder dashboard header bottom",
|
|
86
|
-
config: {
|
|
87
|
-
balancedSides: true,
|
|
88
|
-
contentAlign: "center",
|
|
89
|
-
},
|
|
90
|
-
}),
|
|
91
56
|
scaffold.layoutNode,
|
|
92
57
|
],
|
|
93
58
|
contentWidgets: [
|
|
@@ -24,7 +24,7 @@ import { PHI_SPACING_TOKEN_KEYS } from "../../../../../components/widgets/config
|
|
|
24
24
|
import { PhiColorFieldControl } from "../../../../../components/controls/phi-color-field-control";
|
|
25
25
|
import { PhiBrandControl, resolvePhiBrandLogoUrl } from "../../../../../components/controls/phi-brand-control";
|
|
26
26
|
import { PhiBackgroundControl } from "../../../../../components/controls/phi-background-control";
|
|
27
|
-
import { PHI_ROOT_BACKGROUND_IMAGE_SOURCE_KINDS, PHI_ROOT_BACKGROUND_FILTERS, PHI_ROOT_BACKGROUND_MOTION_MODES, resolvePhiRootBackgroundPaintStyle, } from "../../../../../components/root/phi-root-background";
|
|
27
|
+
import { PHI_ROOT_BACKGROUND_BASE_KINDS, PHI_ROOT_BACKGROUND_IMAGE_SOURCE_KINDS, PHI_ROOT_BACKGROUND_FILTERS, PHI_ROOT_BACKGROUND_MOTION_MODES, resolvePhiRootBackgroundPaintStyle, } from "../../../../../components/root/phi-root-background";
|
|
28
28
|
import { PHI_SHELL_CHROME_OVERLAY_BASE_KINDS, PHI_SHELL_CHROME_OVERLAY_FILTERS, PHI_SHELL_CHROME_OVERLAY_IMAGE_SOURCE_KINDS, PHI_SHELL_CHROME_OVERLAY_MOTION_MODES, resolvePhiShellChromeOverlayConfig, resolvePhiShellChromePaneShadow, resolvePhiShellChromePaneShadows, resolvePhiShellChromeOverlayStyle, } from "../../../../../components/root/phi-shell-chrome-overlay";
|
|
29
29
|
import { normalizePhiBackgroundWidgetConfig } from "../../../../../components/widgets/config/background";
|
|
30
30
|
import { PhiMediaPickerBinding } from "../../../../../components/media/phi-media-picker-binding";
|
|
@@ -34,13 +34,12 @@ import { buildPhiMediaAssetContentDeliveryUrl, PhiMediaAssetFlags, PhiMediaKind,
|
|
|
34
34
|
import { createPhiMediaPickerAssetControllerRoutes } from "../../../../../components/media/asset-controller-routes";
|
|
35
35
|
import { PhiPresetSizeControl } from "../../../../../components/controls/phi-preset-size-control";
|
|
36
36
|
import { PhiButtonControl } from "../../../../../components/controls/phi-button-control";
|
|
37
|
-
import { PhiCascaderControl } from "../../../../../components/controls/phi-cascader-control";
|
|
38
37
|
import { PhiCheckboxControl } from "../../../../../components/controls/phi-checkbox-control";
|
|
39
38
|
import { PhiLabeledControl } from "../../../../../components/controls/phi-labeled-control";
|
|
40
39
|
import { PhiNumberControl } from "../../../../../components/controls/phi-number-control";
|
|
41
40
|
import { PhiColorControl } from "../../../../../components/controls/phi-color-control";
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
41
|
+
import { PhiLinkTargetControl } from "../../../../../components/widgets/client/shared/phi-link-target-control";
|
|
42
|
+
import { usePhiPageReferenceOptions } from "../../../../../components/widgets/client/shared/use-page-reference-options";
|
|
44
43
|
import { PhiShadowControl } from "../../../../../components/controls/phi-shadow-control";
|
|
45
44
|
import { PhiIcon } from "../../../../../components/shell/phi-icon";
|
|
46
45
|
import { PhiIconPickerControl } from "../../../../../components/controls/phi-icon-picker-control";
|
|
@@ -1476,13 +1475,11 @@ export function PhiBuilderBrandIdentityControlsWidgetClient({ runtime, config, }
|
|
|
1476
1475
|
*/
|
|
1477
1476
|
const fallbackWordmark = runtime.site.name ?? runtime.site.key;
|
|
1478
1477
|
/*
|
|
1479
|
-
* The Pages of the public Area, from the
|
|
1480
|
-
*
|
|
1481
|
-
*
|
|
1478
|
+
* The Pages of the public Area as references, from the list every link picker outside the Builder's
|
|
1479
|
+
* own catalog reads (`/api/site/page-references`). The Brand leads into the public Area wherever it
|
|
1480
|
+
* is drawn, so that is the Area written into the target.
|
|
1482
1481
|
*/
|
|
1483
|
-
const { options:
|
|
1484
|
-
optionsProvider: { providerKey: PHI_AUTHORING_OPTION_PROVIDER_KEYS.pages },
|
|
1485
|
-
});
|
|
1482
|
+
const { options: homePageOptions, failed: homePagesFailed } = usePhiPageReferenceOptions("public", "phi-theme-brand");
|
|
1486
1483
|
function updateWordmarkPart(index, patch) {
|
|
1487
1484
|
publishDraft(mergeThemeWordmarkParts(state.draft, editableWordmarkParts.map((part, at) => (at === index ? { ...part, ...patch } : part))));
|
|
1488
1485
|
}
|
|
@@ -1506,7 +1503,7 @@ export function PhiBuilderBrandIdentityControlsWidgetClient({ runtime, config, }
|
|
|
1506
1503
|
backgroundSize: "contain",
|
|
1507
1504
|
backgroundRepeat: "no-repeat",
|
|
1508
1505
|
backgroundPosition: "left center",
|
|
1509
|
-
} })) : (_jsx(PhiTypographyControl, { type: "secondary", style: { color: readAliasTokenString(logoGroundToken, "colorTextTertiary") }, children: "No Logo" })) }), _jsxs(PhiFlexControl, { align: "
|
|
1506
|
+
} })) : (_jsx(PhiTypographyControl, { type: "secondary", style: { color: readAliasTokenString(logoGroundToken, "colorTextTertiary") }, children: "No Logo" })) }), _jsxs(PhiFlexControl, { vertical: true, align: "stretch", gap: clientToken.paddingXXS, style: { flexShrink: 0 }, children: [_jsx(PhiBrandBlockResetButton, { disabled: brand.logo?.[mode] == null, blockTitle: markSet.title, onReset: () => publishDraft(mergeThemeBrandLogo(state.draft, mode, undefined)) }), _jsx(PhiBrandCopyModeButton, { mode: mode, disabled: JSON.stringify(logo) === JSON.stringify(logos[otherMode] ?? null), onCopy: () => publishDraft(mergeThemeBrandLogo(state.draft, otherMode, logo ?? { sourceKind: "none" })) })] })] }), _jsx(PhiMediaPickerBinding, { config: {
|
|
1510
1507
|
mediaType: PhiMediaKind.Image,
|
|
1511
1508
|
pageSize: 12,
|
|
1512
1509
|
showPagination: true,
|
|
@@ -1530,8 +1527,15 @@ export function PhiBuilderBrandIdentityControlsWidgetClient({ runtime, config, }
|
|
|
1530
1527
|
})) }) }), _jsx(PhiLabeledControl, { label: "Offset", fill: true, children: _jsx(PhiNumberControl, { prefix: "px", style: { width: "100%" }, value: brand.logoYOffset ?? 0, step: 1, min: -16, max: 16, onChange: (next) => publishDraft(mergeThemeBrand(state.draft, {
|
|
1531
1528
|
// No offset is no key, the way `Forward` spells its default away below.
|
|
1532
1529
|
logoYOffset: next ? next : null,
|
|
1533
|
-
})) }) }), _jsx(PhiLabeledControl, { label: "Forward", fill: true, children: _jsx(
|
|
1534
|
-
|
|
1530
|
+
})) }) }), _jsx(PhiLabeledControl, { label: "Forward", fill: true, children: _jsx(PhiLinkTargetControl, { value: brand.homeLinkTarget, options: homePageOptions, area: "public", labels: {
|
|
1531
|
+
none: "Site root",
|
|
1532
|
+
page: "Page",
|
|
1533
|
+
external: "Address",
|
|
1534
|
+
pagePlaceholder: homePagesFailed ? "The Pages could not be loaded." : "Select a Page",
|
|
1535
|
+
externalPlaceholder: "https://example.com",
|
|
1536
|
+
newTab: "Open in a new tab",
|
|
1537
|
+
}, onChange: (next) => publishDraft(mergeThemeBrand(state.draft, {
|
|
1538
|
+
homeLinkTarget: next ?? null,
|
|
1535
1539
|
})) }) })] })] })),
|
|
1536
1540
|
},
|
|
1537
1541
|
{
|
|
@@ -1611,12 +1615,12 @@ export function PhiBuilderBrandBackgroundControlsWidgetClient({ runtime, config,
|
|
|
1611
1615
|
{
|
|
1612
1616
|
key: "root",
|
|
1613
1617
|
label: _jsx(PhiTypographyControl, { strong: true, children: "Root Background" }),
|
|
1614
|
-
children: (_jsxs(PhiFlexControl, { vertical: true, gap: clientToken.paddingXS, children: [_jsxs(PhiFlexControl, { align: "center", justify: "space-between", gap: clientToken.paddingXS, children: [_jsxs(PhiTypographyControl, { type: "secondary", children: [modeLabel, ". One layer behind the whole Site, fixed to the viewport."] }), _jsxs(PhiFlexControl, { align: "
|
|
1618
|
+
children: (_jsxs(PhiFlexControl, { vertical: true, gap: clientToken.paddingXS, children: [_jsxs(PhiFlexControl, { align: "center", justify: "space-between", gap: clientToken.paddingXS, children: [_jsxs(PhiTypographyControl, { type: "secondary", children: [modeLabel, ". One layer behind the whole Site, fixed to the viewport."] }), _jsxs(PhiFlexControl, { vertical: true, align: "stretch", gap: clientToken.paddingXXS, style: { flexShrink: 0 }, children: [_jsx(PhiBrandBlockResetButton, { disabled: state.draft.root?.background?.[mode] == null, blockTitle: composition.ground.title, onReset: () => publishDraft(clearThemeRootSurface(state.draft, "background", mode)) }), _jsx(PhiBrandCopyModeButton, { mode: mode, disabled: isSamePhiBackgroundConfig(rootBackground, otherRootBackground), onCopy: () => publishDraft(mergeThemeRootSurface(state.draft, "background", otherMode, rootBackground)) })] })] }), _jsx(PhiBackgroundControl, { value: rootBackground, motionModes: PHI_ROOT_BACKGROUND_MOTION_MODES, filters: PHI_ROOT_BACKGROUND_FILTERS, baseKinds: PHI_ROOT_BACKGROUND_BASE_KINDS, imageSourceKinds: PHI_ROOT_BACKGROUND_IMAGE_SOURCE_KINDS, renderMediaPicker: renderPhiThemeRootBackgroundMediaPicker, editTransaction: editTransaction, onChange: (value) => publishDraft(mergeThemeRootSurface(state.draft, "background", mode, value)) }, mode)] })),
|
|
1615
1619
|
},
|
|
1616
1620
|
{
|
|
1617
1621
|
key: "chrome",
|
|
1618
1622
|
label: _jsx(PhiTypographyControl, { strong: true, children: "Chrome Overlay" }),
|
|
1619
|
-
children: (_jsxs(PhiFlexControl, { vertical: true, gap: clientToken.paddingXS, children: [_jsxs(PhiFlexControl, { align: "center", justify: "space-between", gap: clientToken.paddingXS, children: [_jsxs(PhiTypographyControl, { type: "secondary", children: [modeLabel, ". Shared by the Header, Sider and Footer Regions. Content and Hero never take it, and a Region that authors its own Background or Effect paints over it."] }), _jsxs(PhiFlexControl, { align: "
|
|
1623
|
+
children: (_jsxs(PhiFlexControl, { vertical: true, gap: clientToken.paddingXS, children: [_jsxs(PhiFlexControl, { align: "center", justify: "space-between", gap: clientToken.paddingXS, children: [_jsxs(PhiTypographyControl, { type: "secondary", children: [modeLabel, ". Shared by the Header, Sider and Footer Regions. Content and Hero never take it, and a Region that authors its own Background or Effect paints over it."] }), _jsxs(PhiFlexControl, { vertical: true, align: "stretch", gap: clientToken.paddingXXS, style: { flexShrink: 0 }, children: [_jsx(PhiBrandBlockResetButton, { disabled: state.draft.root?.chrome?.[mode] == null, blockTitle: composition.ground.title, onReset: () => publishDraft(clearThemeRootSurface(state.draft, "chrome", mode)) }), _jsx(PhiBrandCopyModeButton, { mode: mode, disabled: isSamePhiBackgroundConfig(chromeOverlay, otherChromeOverlay), onCopy: () => publishDraft(mergeThemeRootSurface(state.draft, "chrome", otherMode, chromeOverlay)) })] })] }), _jsx(PhiBackgroundControl, { value: chromeOverlay, motionModes: PHI_SHELL_CHROME_OVERLAY_MOTION_MODES, filters: PHI_SHELL_CHROME_OVERLAY_FILTERS, baseKinds: PHI_SHELL_CHROME_OVERLAY_BASE_KINDS, imageSourceKinds: PHI_SHELL_CHROME_OVERLAY_IMAGE_SOURCE_KINDS, renderMediaPicker: renderPhiThemeRootBackgroundMediaPicker, editTransaction: editTransaction, onChange: (value) => publishDraft(mergeThemeRootSurface(state.draft, "chrome", mode, value)) }, `chrome-${mode}`)] })),
|
|
1620
1624
|
},
|
|
1621
1625
|
{
|
|
1622
1626
|
key: "shadow",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isPhiCmsAreaKey, } from "../../../../../constants/cms-areas";
|
|
2
2
|
import { PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS } from "../../../../../constants/media-library-provider-keys";
|
|
3
|
-
import { PHI_AUTHORING_OPTION_PROVIDER_KEYS } from "../../../../../constants/authoring-option-provider-keys";
|
|
4
3
|
import { PHI_THEME_SIGNAL_CHANNELS } from "../../../../../plugins/runtime-modules/theme/controller/signals";
|
|
5
4
|
import { createPhiCmsWidgetDefinition } from "../../../../../components/widgets/config/helpers";
|
|
6
5
|
import { readString } from "../../../../../components/widgets/config/parser-primitives";
|
|
@@ -52,15 +51,12 @@ export const PHI_BUILDER_BRAND_IDENTITY_CONTROLS_WIDGET_DEFINITION = createPhiCm
|
|
|
52
51
|
iconFamily: "theme",
|
|
53
52
|
slotSizePolicy: "fill-inline",
|
|
54
53
|
/*
|
|
55
|
-
* The Logo is picked from the Media library and
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
54
|
+
* The Logo is picked from the Media library, and a Page mounts only the Data Providers its tree asks
|
|
55
|
+
* for. The scan reads this declaration; a Provider a Widget reaches for from inside its client code is
|
|
56
|
+
* invisible to it. Same reason the Background panel declares its own. The Brand's link needs none: its
|
|
57
|
+
* Pages come from `/api/site/page-references`, as every link picker's outside the Builder catalog do.
|
|
59
58
|
*/
|
|
60
|
-
requiredDataProviders: [
|
|
61
|
-
PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS.collection,
|
|
62
|
-
PHI_AUTHORING_OPTION_PROVIDER_KEYS.pages,
|
|
63
|
-
],
|
|
59
|
+
requiredDataProviders: [PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS.collection],
|
|
64
60
|
runtimeSignals: PHI_BUILDER_BRAND_PREVIEW_MODE_SIGNALS,
|
|
65
61
|
fields: [
|
|
66
62
|
{ key: "themeKey", type: "string", label: "Theme Key" },
|
|
@@ -21,6 +21,12 @@ export async function resolvePhiCmsTreeLinkTargets(tree, runtime) {
|
|
|
21
21
|
for (const node of [...tree.regions, ...tree.overlays, ...tree.layoutNodes, ...tree.contentWidgets]) {
|
|
22
22
|
collectPhiLinkTargetReferences(node.config, references);
|
|
23
23
|
}
|
|
24
|
+
/*
|
|
25
|
+
* And the Brand's, which is the Site's and not any node's (THEME.md "Brand"): the Brand Widget leads
|
|
26
|
+
* where the Theme says, and that target resolves with the rest of the page rather than in a round
|
|
27
|
+
* trip of its own per Brand drawn -- a header and a footer would be two.
|
|
28
|
+
*/
|
|
29
|
+
collectPhiLinkTargetReferences(runtime.site.theme?.brand, references);
|
|
24
30
|
return resolvePhiLinkTargetReferences(references, runtime);
|
|
25
31
|
}
|
|
26
32
|
/**
|
package/styles/controls.css
CHANGED
|
@@ -501,6 +501,18 @@ h5.phi-typography, .phi-typography h5 {
|
|
|
501
501
|
top: var(--phi-table-sticky-top, var(--phi-shell-sticky-top, 0px)) !important;
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
+
/*
|
|
505
|
+
* A sticky header row is a table of its own, and its columns are the body's: Ant Design measures the
|
|
506
|
+
* body's columns and writes their widths into the header's `<colgroup>`. In `auto` layout those widths
|
|
507
|
+
* are only a floor, and a `content` column's header cell carries `width: 1%` -- so each header cell shrank
|
|
508
|
+
* to its title and a `fill` column took the rest, out of line with the rows under it. Fixed layout makes
|
|
509
|
+
* the `<colgroup>` the header's widths. The body keeps the configured `layout.mode`; Ant Design writes
|
|
510
|
+
* that inline on both tables, so the header's is stated over it.
|
|
511
|
+
*/
|
|
512
|
+
.phi-table-control.phi-table-control--sticky-header [class~="ant-table-sticky-holder"] > table {
|
|
513
|
+
table-layout: fixed !important;
|
|
514
|
+
}
|
|
515
|
+
|
|
504
516
|
:is([data-phi-modal-body-content], [data-phi-drawer-body-content]) {
|
|
505
517
|
--phi-table-sticky-top: 0px;
|
|
506
518
|
}
|
|
@@ -562,6 +574,55 @@ h5.phi-typography, .phi-typography h5 {
|
|
|
562
574
|
align-self: center;
|
|
563
575
|
}
|
|
564
576
|
|
|
577
|
+
/*
|
|
578
|
+
* Its arrow on the switcher's own centre. antd sets the switcher's line height to the row height and lets
|
|
579
|
+
* the arrow stand on that line's baseline, which leaves a small icon above the middle of the box. The box
|
|
580
|
+
* keeps a compact control's height, so its hover ground is the square it was.
|
|
581
|
+
*/
|
|
582
|
+
.phi-tree-control.phi-tree-control [class~="ant-tree-switcher"] {
|
|
583
|
+
display: inline-flex;
|
|
584
|
+
align-items: center;
|
|
585
|
+
justify-content: center;
|
|
586
|
+
min-block-size: var(--ant-control-height-sm);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/* --- Compact Select ------------------------------------------------------------------------------ */
|
|
590
|
+
|
|
591
|
+
/*
|
|
592
|
+
* A compact control writes small: Input, InputNumber, DatePicker and Button take the scale's `sm` through
|
|
593
|
+
* their component tokens (theme/phi-theme.ts). Select has no such token -- it sizes its words by variables
|
|
594
|
+
* of its own and gives `-sm` the body's -- so its compact form is told here. Both variables move together:
|
|
595
|
+
* Select reckons its padding as (height - font height) / 2, so a smaller font size with the body's font
|
|
596
|
+
* height would leave the box short of `controlHeightSM` by the difference.
|
|
597
|
+
*/
|
|
598
|
+
[class~="ant-select"][class~="ant-select-sm"] {
|
|
599
|
+
--ant-select-font-size: var(--ant-font-size-sm);
|
|
600
|
+
--ant-select-font-height: calc(var(--ant-font-size-sm) * var(--ant-select-line-height));
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/* The list a compact Select opens writes as the field does; Ant Design gives every Select's list the body size. */
|
|
604
|
+
.phi-select-popup--sm [class~="ant-select-item"] {
|
|
605
|
+
font-size: var(--ant-font-size-sm);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/* --- Running text --------------------------------------------------------------------------------- */
|
|
609
|
+
|
|
610
|
+
/*
|
|
611
|
+
* Markdown and HTML text breaks long words at their syllables, by the rules of the page's language
|
|
612
|
+
* (`<html lang>`), rather than leaving a ragged edge or running a word past its column. Code keeps its
|
|
613
|
+
* characters as written.
|
|
614
|
+
*/
|
|
615
|
+
.phi-markdown-text,
|
|
616
|
+
.phi-html-widget__content {
|
|
617
|
+
hyphens: auto;
|
|
618
|
+
overflow-wrap: break-word;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.phi-markdown-text :is(pre, code),
|
|
622
|
+
.phi-html-widget__content :is(pre, code) {
|
|
623
|
+
hyphens: manual;
|
|
624
|
+
}
|
|
625
|
+
|
|
565
626
|
/* --- Plain table --------------------------------------------------------------------------------- */
|
|
566
627
|
|
|
567
628
|
/* Wide tables scroll inside their own box instead of widening the Page. */
|
|
@@ -570,11 +631,12 @@ h5.phi-typography, .phi-typography h5 {
|
|
|
570
631
|
overflow-x: auto;
|
|
571
632
|
}
|
|
572
633
|
|
|
634
|
+
/* A table is read across, not down: its cells take the small size, as the Table Control's do. */
|
|
573
635
|
.phi-plain-table {
|
|
574
636
|
width: 100%;
|
|
575
637
|
border-collapse: collapse;
|
|
576
|
-
font-size: var(--ant-font-size);
|
|
577
|
-
line-height: var(--ant-line-height);
|
|
638
|
+
font-size: var(--ant-font-size-sm);
|
|
639
|
+
line-height: var(--ant-line-height-sm);
|
|
578
640
|
color: var(--ant-color-text);
|
|
579
641
|
}
|
|
580
642
|
|
package/styles/layout.css
CHANGED
|
@@ -493,8 +493,13 @@
|
|
|
493
493
|
* COLOUR the page already computed from them, not the variable -- so the box states its text colour again
|
|
494
494
|
* and its content reads it from the variables in force there. When the content is already in that mode,
|
|
495
495
|
* the class has no variables of its own and this resolves to what the page has anyway.
|
|
496
|
+
*
|
|
497
|
+
* The box only, not an Ant Design component inside it: the tone scope keys its variables to the same
|
|
498
|
+
* class, and Ant Design puts that key on every component it draws there. Stated on those too, the colour
|
|
499
|
+
* stopped inheritance at each of them -- a Quick Link's words kept the text colour while the link turned
|
|
500
|
+
* to its hover colour around them.
|
|
496
501
|
*/
|
|
497
|
-
:where(.phi-tone-light, .phi-tone-dark, .phi-tone-inverse) {
|
|
502
|
+
:where(.phi-tone-light, .phi-tone-dark, .phi-tone-inverse):where(:not([class^="ant-"], [class*=" ant-"])) {
|
|
498
503
|
color: var(--ant-color-text);
|
|
499
504
|
}
|
|
500
505
|
|
|
@@ -576,6 +581,43 @@
|
|
|
576
581
|
transition: scale 300ms ease;
|
|
577
582
|
}
|
|
578
583
|
|
|
584
|
+
/*
|
|
585
|
+
* A card with a ribbon stands in the ribbon's wrapper (`PhiRibbonWrapControl`): a column that takes the
|
|
586
|
+
* slot's size and hands it to the card. A card that lifts carries its ribbon with it -- the ribbon is the
|
|
587
|
+
* card's sibling, so the wrapper moves it when the card moves.
|
|
588
|
+
*/
|
|
589
|
+
:where(.phi-card-ribbon) {
|
|
590
|
+
display: flex;
|
|
591
|
+
flex-direction: column;
|
|
592
|
+
min-width: 0;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
:where(.phi-card-ribbon) > :where(.phi-card) {
|
|
596
|
+
flex: 1 1 auto;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/*
|
|
600
|
+
* A little lower than Ant Design sets it, so the ribbon stands centred on the eyebrow's line. Outside
|
|
601
|
+
* `:where`: Ant Design's own `.ant-ribbon` states `top`, and this has to outrank it.
|
|
602
|
+
*/
|
|
603
|
+
.phi-card-ribbon > .ant-ribbon {
|
|
604
|
+
top: calc(var(--ant-margin-xs) + 3px);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
:where(.phi-card-ribbon--lift) > :where(.ant-ribbon) {
|
|
608
|
+
transition: translate 200ms ease;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
@media (hover: hover) {
|
|
612
|
+
:where(.phi-card-ribbon--lift):has(> .phi-card:hover) > :where(.ant-ribbon) {
|
|
613
|
+
translate: 0 -4px;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
:where(.phi-card-ribbon--lift):has(> .phi-card:focus-within) > :where(.ant-ribbon) {
|
|
618
|
+
translate: 0 -4px;
|
|
619
|
+
}
|
|
620
|
+
|
|
579
621
|
@media (hover: hover) {
|
|
580
622
|
:where(.phi-card--hover-lift):hover {
|
|
581
623
|
translate: 0 -4px;
|
|
@@ -629,7 +671,8 @@
|
|
|
629
671
|
transition: none;
|
|
630
672
|
}
|
|
631
673
|
|
|
632
|
-
.phi-card--hover-lift:is(:hover, :focus-within)
|
|
674
|
+
.phi-card--hover-lift:is(:hover, :focus-within),
|
|
675
|
+
.phi-card-ribbon--lift > .ant-ribbon {
|
|
633
676
|
translate: none;
|
|
634
677
|
}
|
|
635
678
|
|