@phis/ui 0.3.0 → 0.4.1
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 +1 -1
- package/components/controls/phi-collection-header-control.js +1 -1
- package/components/controls/phi-collection-view-control.js +2 -1
- package/components/controls/phi-drawer-control-adapter.d.ts +1 -1
- package/components/controls/phi-drawer-control-adapter.js +2 -2
- package/components/controls/phi-image-control.d.ts +6 -1
- package/components/controls/phi-image-control.js +23 -4
- package/components/controls/phi-lightbox-image-control-adapter.d.ts +2 -0
- package/components/controls/phi-lightbox-image-control-adapter.js +11 -0
- package/components/controls/phi-lightbox-image-control.d.ts +3 -2
- package/components/controls/phi-lightbox-image-control.js +7 -3
- package/components/controls/phi-modal-control-adapter.d.ts +1 -1
- package/components/controls/phi-modal-control-adapter.js +5 -2
- package/components/controls/phi-overlay-control-contract.d.ts +5 -0
- package/components/controls/phi-pagination-control-adapter.d.ts +2 -0
- package/components/controls/phi-pagination-control-adapter.js +15 -0
- package/components/controls/phi-pagination-control.d.ts +2 -1
- package/components/controls/phi-pagination-control.js +11 -7
- package/components/controls/phi-table-control.d.ts +6 -1
- package/components/controls/phi-table-control.js +17 -5
- package/components/controls/phi-timeline-control-adapter.d.ts +2 -0
- package/components/controls/phi-timeline-control-adapter.js +28 -0
- package/components/controls/phi-timeline-control.d.ts +3 -1
- package/components/controls/phi-timeline-control.js +8 -22
- package/components/controls/phi-tour-control.js +25 -7
- package/components/controls/phi-tour-viewport.d.ts +36 -0
- package/components/controls/phi-tour-viewport.js +86 -0
- package/components/controls/phi-zoom-image-overlay.d.ts +17 -0
- package/components/controls/phi-zoom-image-overlay.js +58 -0
- package/components/layouts/layout-definitions.d.ts +2 -20
- package/components/layouts/layout-definitions.js +13 -2
- package/components/layouts/plugins/carousel-layout-plugin.js +2 -1
- package/components/media/aspect-ratios.d.ts +14 -0
- package/components/media/aspect-ratios.js +13 -0
- package/components/motion/phi-sequence-run.d.ts +27 -0
- package/components/motion/phi-sequence-run.js +87 -0
- package/components/motion/phi-sequence-viewport.d.ts +16 -0
- package/components/motion/phi-sequence-viewport.js +97 -34
- package/components/overlays/phi-overlay-container-client.js +5 -2
- package/components/runtime/runtime-visual-anchors.d.ts +13 -2
- package/components/runtime/runtime-visual-anchors.js +49 -3
- package/components/shell/use-phi-shell-sticky-top.d.ts +5 -0
- package/components/shell/use-phi-shell-sticky-top.js +78 -0
- package/components/widgets/config/image-variant-options.d.ts +3 -5
- package/components/widgets/config/image-variant-options.js +11 -11
- package/components/widgets/config/sequence-settings.d.ts +75 -0
- package/components/widgets/config/sequence-settings.js +173 -0
- package/components/widgets/helpers/semantic-html-translation.d.ts +3 -1
- package/components/widgets/helpers/semantic-html-translation.js +8 -3
- package/components/widgets/label-sets/content-entries.d.ts +9 -0
- package/components/widgets/label-sets/content-entries.js +17 -0
- package/components/widgets/label-types/gallery.d.ts +2 -10
- package/components/widgets/label-types/sequence.d.ts +18 -0
- package/components/widgets/label-types/sequence.js +1 -0
- package/components/widgets/navigation-descriptor-resolver.server.js +6 -1
- package/components/widgets/shared/markdown-render.js +15 -5
- package/helpers/cms-layout-defaults.js +9 -2
- package/helpers/cms-region-keys.d.ts +1 -1
- package/helpers/motion.d.ts +7 -0
- package/helpers/motion.js +7 -0
- package/helpers/text-source.d.ts +19 -0
- package/helpers/text-source.js +20 -0
- package/next/config.mjs +1 -1
- package/package.json +1 -1
- package/plugins/runtime-modules/asset/data-providers.d.ts +4 -0
- package/plugins/runtime-modules/asset/data-providers.js +1 -0
- package/plugins/runtime-modules/asset/definition.d.ts +4 -0
- package/plugins/runtime-modules/asset/presets.d.ts +53 -1
- package/plugins/runtime-modules/asset/presets.js +16 -12
- package/plugins/runtime-modules/asset/tour.d.ts +44 -0
- package/plugins/runtime-modules/asset/tour.js +53 -0
- package/plugins/runtime-modules/auth/trees/phi-default-pub-confirm-page-tree.js +3 -0
- package/plugins/runtime-modules/auth/trees/phi-default-pub-login-page-tree.js +2 -0
- package/plugins/runtime-modules/auth/trees/phi-default-pub-registration-page-tree.js +3 -0
- package/plugins/runtime-modules/auth/trees/phi-default-pub-reset-password-page-tree.js +3 -0
- package/plugins/runtime-modules/builder/clients/inspector-config-field.js +42 -18
- package/plugins/runtime-modules/builder/clients/inspector-image-field.js +2 -2
- package/plugins/runtime-modules/builder/clients/widget-inspector.js +9 -0
- package/plugins/runtime-modules/builder/clients/workspace-scope-boundary.js +10 -0
- package/plugins/runtime-modules/builder/controller/workspace-controller.js +18 -3
- package/plugins/runtime-modules/builder/plugin-meta-labels.js +8 -0
- package/plugins/runtime-modules/builder/presets.js +5 -3
- package/plugins/runtime-modules/builder/tour.d.ts +316 -0
- package/plugins/runtime-modules/builder/tour.js +337 -0
- package/plugins/runtime-modules/builder/trees/phi-builder-inspector-area-overlay-tree.server.js +8 -1
- 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 +3 -2
- package/plugins/runtime-modules/core/widgets/card/server.js +4 -2
- package/plugins/runtime-modules/core/widgets/collection-view/client.js +10 -0
- package/plugins/runtime-modules/core/widgets/description/config.d.ts +28 -1
- package/plugins/runtime-modules/core/widgets/description/config.js +2 -0
- package/plugins/runtime-modules/core/widgets/description/server.js +10 -12
- package/plugins/runtime-modules/core/widgets/entry-detail/config.d.ts +2 -0
- package/plugins/runtime-modules/core/widgets/entry-detail/config.js +8 -1
- package/plugins/runtime-modules/core/widgets/entry-header/config.d.ts +3 -0
- package/plugins/runtime-modules/core/widgets/entry-header/config.js +8 -1
- package/plugins/runtime-modules/core/widgets/entry-list/client.d.ts +4 -1
- package/plugins/runtime-modules/core/widgets/entry-list/client.js +3 -2
- package/plugins/runtime-modules/core/widgets/entry-list/config.d.ts +392 -93
- package/plugins/runtime-modules/core/widgets/entry-list/config.js +21 -3
- package/plugins/runtime-modules/core/widgets/entry-list/server.js +4 -2
- package/plugins/runtime-modules/core/widgets/gallery/authoring.js +62 -10
- package/plugins/runtime-modules/core/widgets/gallery/client.d.ts +6 -1
- package/plugins/runtime-modules/core/widgets/gallery/client.js +7 -79
- package/plugins/runtime-modules/core/widgets/gallery/config.d.ts +337 -144
- package/plugins/runtime-modules/core/widgets/gallery/config.js +52 -113
- package/plugins/runtime-modules/core/widgets/gallery/defaults.d.ts +15 -0
- package/plugins/runtime-modules/core/widgets/gallery/defaults.js +20 -0
- package/plugins/runtime-modules/core/widgets/html/server.js +1 -1
- package/plugins/runtime-modules/core/widgets/image/client.js +1 -1
- package/plugins/runtime-modules/core/widgets/image/config.d.ts +29 -4
- package/plugins/runtime-modules/core/widgets/image/config.js +19 -2
- package/plugins/runtime-modules/core/widgets/image/editor.js +1 -1
- package/plugins/runtime-modules/core/widgets/markdown-toc/server.js +7 -2
- package/plugins/runtime-modules/core/widgets/quick-links/config.d.ts +3 -0
- package/plugins/runtime-modules/core/widgets/quick-links/config.js +2 -0
- package/plugins/runtime-modules/core/widgets/quick-links/plugin.js +4 -2
- package/plugins/runtime-modules/core/widgets/quick-links/server.d.ts +5 -1
- package/plugins/runtime-modules/core/widgets/quick-links/server.js +4 -2
- package/plugins/runtime-modules/core/widgets/result/config.d.ts +25 -1
- package/plugins/runtime-modules/core/widgets/simple-text/config.d.ts +28 -1
- package/plugins/runtime-modules/core/widgets/simple-text/config.js +2 -0
- package/plugins/runtime-modules/core/widgets/simple-text/plugin.js +1 -1
- package/plugins/runtime-modules/core/widgets/simple-text/server.d.ts +4 -1
- package/plugins/runtime-modules/core/widgets/simple-text/server.js +3 -3
- package/plugins/runtime-modules/core/widgets/table/client.js +1 -1
- package/plugins/runtime-modules/core/widgets/table/config.d.ts +80 -3
- package/plugins/runtime-modules/core/widgets/table/config.js +3 -0
- package/plugins/runtime-modules/core/widgets/tour-button/client.js +1 -1
- package/plugins/runtime-modules/phi-default-site-area-preset-tree.js +12 -4
- package/plugins/runtime-modules/public/trees/phi-default-pub-contact-page-tree.js +2 -0
- package/plugins/runtime-modules/public/trees/phi-default-pub-unsubscribe-page-tree.js +3 -0
- package/plugins/runtime-modules/public/trees/phi-default-pub-welcome-page-tree.js +2 -1
- package/plugins/runtime-modules/revisions/presets.d.ts +53 -2
- package/plugins/runtime-modules/revisions/presets.js +16 -12
- package/plugins/runtime-modules/revisions/tour.d.ts +43 -0
- package/plugins/runtime-modules/revisions/tour.js +51 -0
- package/plugins/runtime-modules/revisions/trees/phi-default-builder-revisions-page-tree.js +4 -0
- package/plugins/runtime-modules/theme/presets.d.ts +36 -4
- package/plugins/runtime-modules/theme/tour.d.ts +45 -11
- package/plugins/runtime-modules/theme/tour.js +39 -8
- package/plugins/runtime-modules/tour/controller/address.js +2 -2
- package/plugins/runtime-modules/tour/controller/client.js +43 -2
- package/plugins/runtime-modules/tour/controller/definition.js +2 -1
- package/server-helpers/cms-request.js +1 -0
- package/server-helpers/page-tour.d.ts +10 -3
- package/server-helpers/page-tour.js +25 -12
- package/server-helpers/translate.d.ts +9 -0
- package/server-helpers/translate.js +14 -0
- package/styles/controls.css +15 -1
- package/styles/root.css +20 -19
- package/types/cms-config.d.ts +3 -1
- package/types/cms-config.js +3 -3
- package/types/cms-plugins.d.ts +31 -4
- package/types/core-widget-placements.d.ts +5 -0
- package/types/table-widget.d.ts +2 -0
- package/types/tour-controller.d.ts +10 -0
- package/types/tour-controller.js +13 -0
- package/types/tours.d.ts +27 -25
- package/types/tours.js +9 -23
- package/types/visual-anchors.d.ts +15 -0
- package/types/visual-anchors.js +12 -0
- package/types.d.ts +2 -0
- package/types.js +4 -1
|
@@ -12,6 +12,7 @@ import { emitPhiSignalCapability, usePhiSignalEmitter, PhiSignalIdentityProvider
|
|
|
12
12
|
import { usePhiSignalListener } from "../runtime/runtime-signal-bus";
|
|
13
13
|
import { registerPhiSignalInstance } from "../runtime/runtime-signal-registry";
|
|
14
14
|
import { usePhiSignalRuntimePartition } from "../runtime/runtime-signal-partition";
|
|
15
|
+
import { usePhiVisualAnchor } from "../runtime/runtime-visual-anchors";
|
|
15
16
|
import { PhiModalControl } from "../controls/phi-modal-control";
|
|
16
17
|
import { PhiDrawerControl } from "../controls/phi-drawer-control";
|
|
17
18
|
import { PhiAlertControl } from "../controls/phi-alert-control";
|
|
@@ -72,6 +73,8 @@ export function PhiOverlayContainerClient({ overlayId, overlayType, config: rawC
|
|
|
72
73
|
const openedAtPathname = useRef(null);
|
|
73
74
|
const pathname = usePhiPathname();
|
|
74
75
|
const emitSignal = usePhiSignalEmitter(receiver);
|
|
76
|
+
// The Overlay's panel, found by a Tour under the Overlay's own address while it is drawn.
|
|
77
|
+
const panelRef = usePhiVisualAnchor(receiver);
|
|
75
78
|
const loadZones = usePhiOverlayZonesLoaderIfAny();
|
|
76
79
|
const [loadedZones, setLoadedZones] = useState(null);
|
|
77
80
|
const listenRoutes = useMemo(() => config.signalRoutes?.listens ?? [], [config.signalRoutes?.listens]);
|
|
@@ -261,7 +264,7 @@ export function PhiOverlayContainerClient({ overlayId, overlayType, config: rawC
|
|
|
261
264
|
*/
|
|
262
265
|
const surfaceStyle = { ...resolvePhiCmsContainerSurface(config).style, padding: 0 };
|
|
263
266
|
if (overlayType === "drawer") {
|
|
264
|
-
return (_jsxs(_Fragment, { children: [zoneControllers, _jsx(PhiDrawerControl, { open: open, title: runtimeTitle, header: renderZone(zones?.header), body: renderZone(zones?.body), footer: renderZone(zones?.footer), closable: behaviour.closable, keyboard: behaviour.keyboard, mask: behaviour.mask, mountPolicy: config.mountPolicy, placement: config.placement, size: config.size, maxSize: config.maxSize, resizable: behaviour.resizable, push: behaviour.push, containerStyle: surfaceStyle, onDismiss: requestClose })] }));
|
|
267
|
+
return (_jsxs(_Fragment, { children: [zoneControllers, _jsx(PhiDrawerControl, { open: open, title: runtimeTitle, header: renderZone(zones?.header), body: renderZone(zones?.body), footer: renderZone(zones?.footer), closable: behaviour.closable, keyboard: behaviour.keyboard, mask: behaviour.mask, mountPolicy: config.mountPolicy, placement: config.placement, size: config.size, maxSize: config.maxSize, resizable: behaviour.resizable, push: behaviour.push, containerStyle: surfaceStyle, onDismiss: requestClose, panelRef: panelRef })] }));
|
|
265
268
|
}
|
|
266
|
-
return (_jsxs(_Fragment, { children: [zoneControllers, _jsx(PhiModalControl, { open: open, title: runtimeTitle, header: renderZone(zones?.header), body: renderZone(zones?.body == null ? null : _jsx(PhiDialogBodyProvider, { children: zones.body })), footer: renderZone(zones?.footer), closable: behaviour.closable, keyboard: behaviour.keyboard, mask: behaviour.mask, mountPolicy: config.mountPolicy, centered: behaviour.centered, controlSize: runtimeControlSize ?? config.controlSize, size: runtimeSize, width: config.width, containerStyle: surfaceStyle, onDismiss: requestClose })] }));
|
|
269
|
+
return (_jsxs(_Fragment, { children: [zoneControllers, _jsx(PhiModalControl, { open: open, title: runtimeTitle, header: renderZone(zones?.header), body: renderZone(zones?.body == null ? null : _jsx(PhiDialogBodyProvider, { children: zones.body })), footer: renderZone(zones?.footer), closable: behaviour.closable, keyboard: behaviour.keyboard, mask: behaviour.mask, mountPolicy: config.mountPolicy, centered: behaviour.centered, controlSize: runtimeControlSize ?? config.controlSize, size: runtimeSize, width: config.width, containerStyle: surfaceStyle, onDismiss: requestClose, panelRef: panelRef })] }));
|
|
267
270
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type PhiVisualAnchorAddress } from "../../types/
|
|
1
|
+
import { type PhiVisualAnchorAddress } from "../../types/visual-anchors";
|
|
2
2
|
import { type PhiSignalRuntimePartition } from "./runtime-signal-partition";
|
|
3
3
|
/**
|
|
4
4
|
* One registration. The element is reached through a getter rather than held as a value, so whoever reads
|
|
@@ -16,7 +16,9 @@ export type PhiVisualAnchorEntry = {
|
|
|
16
16
|
export declare function registerPhiVisualAnchor(partition: PhiSignalRuntimePartition, entry: PhiVisualAnchorEntry): () => void;
|
|
17
17
|
/**
|
|
18
18
|
* The element an address is drawn as, in this partition and nowhere else, or `null` when it is not there
|
|
19
|
-
* to point at: not mounted, mounted twice, detached,
|
|
19
|
+
* to point at: not mounted, mounted twice, detached, rendered without a box (hidden, `display: none`), or
|
|
20
|
+
* with a box of no area -- a slot that hides itself because it has nothing to show still lays out at zero
|
|
21
|
+
* height, and a Tour pointing at it pointed at nothing.
|
|
20
22
|
*/
|
|
21
23
|
export declare function readPhiVisualAnchor(partition: PhiSignalRuntimePartition, address: PhiVisualAnchorAddress): HTMLElement | null;
|
|
22
24
|
/**
|
|
@@ -28,6 +30,15 @@ export declare function waitForPhiVisualAnchor(partition: PhiSignalRuntimePartit
|
|
|
28
30
|
timeoutMs: number;
|
|
29
31
|
signal?: AbortSignal;
|
|
30
32
|
}): Promise<HTMLElement | null>;
|
|
33
|
+
/**
|
|
34
|
+
* Waits until a found target stops moving -- a Drawer sliding in, a slot opening -- and answers whether it
|
|
35
|
+
* did before `timeoutMs`. Whoever points at a target measures it once: measured mid-motion, the pointer
|
|
36
|
+
* stays where the target was on its way, off screen for a Drawer that is still coming in.
|
|
37
|
+
*/
|
|
38
|
+
export declare function waitForPhiVisualAnchorToSettle(element: HTMLElement, { timeoutMs, signal }: {
|
|
39
|
+
timeoutMs: number;
|
|
40
|
+
signal?: AbortSignal;
|
|
41
|
+
}): Promise<boolean>;
|
|
31
42
|
/**
|
|
32
43
|
* Registers the element a ref lands on as the visual anchor of `address` in the surrounding partition.
|
|
33
44
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useCallback, useEffect, useRef } from "react";
|
|
3
|
-
import { PHI_VISUAL_ANCHOR_PART_ATTRIBUTE } from "../../types/
|
|
3
|
+
import { PHI_VISUAL_ANCHOR_PART_ATTRIBUTE } from "../../types/visual-anchors";
|
|
4
4
|
import { usePhiSignalRuntimePartition } from "./runtime-signal-partition";
|
|
5
5
|
function notify(partition) {
|
|
6
6
|
for (const subscriber of partition.visualAnchorSubscribers) {
|
|
@@ -33,7 +33,9 @@ export function registerPhiVisualAnchor(partition, entry) {
|
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* The element an address is drawn as, in this partition and nowhere else, or `null` when it is not there
|
|
36
|
-
* to point at: not mounted, mounted twice, detached,
|
|
36
|
+
* to point at: not mounted, mounted twice, detached, rendered without a box (hidden, `display: none`), or
|
|
37
|
+
* with a box of no area -- a slot that hides itself because it has nothing to show still lays out at zero
|
|
38
|
+
* height, and a Tour pointing at it pointed at nothing.
|
|
37
39
|
*/
|
|
38
40
|
export function readPhiVisualAnchor(partition, address) {
|
|
39
41
|
const entries = partition.visualAnchors.get(address);
|
|
@@ -41,7 +43,8 @@ export function readPhiVisualAnchor(partition, address) {
|
|
|
41
43
|
if (!element || !element.isConnected || element.getClientRects().length === 0) {
|
|
42
44
|
return null;
|
|
43
45
|
}
|
|
44
|
-
|
|
46
|
+
const box = element.getBoundingClientRect();
|
|
47
|
+
return box.width > 0 && box.height > 0 ? element : null;
|
|
45
48
|
}
|
|
46
49
|
/** How often a wait looks again for a target that is registered but not yet laid out. */
|
|
47
50
|
const WAIT_POLL_INTERVAL_MS = 100;
|
|
@@ -76,6 +79,49 @@ export function waitForPhiVisualAnchor(partition, address, { timeoutMs, signal }
|
|
|
76
79
|
signal?.addEventListener("abort", abort, { once: true });
|
|
77
80
|
});
|
|
78
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* How many frames in a row a target has to stand still before it counts as placed. More than two: an
|
|
84
|
+
* opening slot or Drawer often holds still for a frame or two before its motion starts, and measured
|
|
85
|
+
* then, the pointer is fixed to where it started.
|
|
86
|
+
*/
|
|
87
|
+
const SETTLE_STILL_FRAMES = 8;
|
|
88
|
+
/**
|
|
89
|
+
* Waits until a found target stops moving -- a Drawer sliding in, a slot opening -- and answers whether it
|
|
90
|
+
* did before `timeoutMs`. Whoever points at a target measures it once: measured mid-motion, the pointer
|
|
91
|
+
* stays where the target was on its way, off screen for a Drawer that is still coming in.
|
|
92
|
+
*/
|
|
93
|
+
export function waitForPhiVisualAnchorToSettle(element, { timeoutMs, signal }) {
|
|
94
|
+
return new Promise((resolve) => {
|
|
95
|
+
let last = "";
|
|
96
|
+
let still = 0;
|
|
97
|
+
let frame = 0;
|
|
98
|
+
const finish = (settled) => {
|
|
99
|
+
window.cancelAnimationFrame(frame);
|
|
100
|
+
window.clearTimeout(timeout);
|
|
101
|
+
signal?.removeEventListener("abort", abort);
|
|
102
|
+
resolve(settled);
|
|
103
|
+
};
|
|
104
|
+
const abort = () => finish(false);
|
|
105
|
+
const look = () => {
|
|
106
|
+
const box = element.getBoundingClientRect();
|
|
107
|
+
const next = `${box.x},${box.y},${box.width},${box.height}`;
|
|
108
|
+
still = next === last ? still + 1 : 0;
|
|
109
|
+
last = next;
|
|
110
|
+
if (still >= SETTLE_STILL_FRAMES) {
|
|
111
|
+
finish(true);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
frame = window.requestAnimationFrame(look);
|
|
115
|
+
};
|
|
116
|
+
const timeout = window.setTimeout(() => finish(false), timeoutMs);
|
|
117
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
118
|
+
if (signal?.aborted) {
|
|
119
|
+
finish(false);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
frame = window.requestAnimationFrame(look);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
79
125
|
/**
|
|
80
126
|
* Registers the element a ref lands on as the visual anchor of `address` in the surrounding partition.
|
|
81
127
|
*
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* How far down the viewport the Shell's Header holds right now, published on the Shell.
|
|
5
|
+
*
|
|
6
|
+
* Something that sticks to the top of the page -- a Table's header row -- has to stick under the Header
|
|
7
|
+
* rather than behind it, and where the Header ends is not a constant: each band sticks or scrolls by its
|
|
8
|
+
* own config, and a revealing `header_top` slides away while the visitor reads down and comes back when
|
|
9
|
+
* they scroll up. So the edge is measured, not configured: the lowest bottom of every Header band that is
|
|
10
|
+
* stuck at this moment, written as `--phi-shell-sticky-top` on the Shell element. Unset, a reader falls
|
|
11
|
+
* back to `0px`, which is right for a Shell without a sticky Header.
|
|
12
|
+
*
|
|
13
|
+
* Run by whatever sticks under it -- every sticky Table does -- so a page without one loads nothing for
|
|
14
|
+
* it. Each runner measures all the bands and they all write the same value. Not inside an Overlay: a
|
|
15
|
+
* Modal's or Drawer's body scrolls on its own, and its top edge is the line.
|
|
16
|
+
*/
|
|
17
|
+
const PHI_SHELL_STICKY_TOP_PROPERTY = "--phi-shell-sticky-top";
|
|
18
|
+
const HEADER_BANDS = '[data-phi-region-key^="header_"]:not([hidden])';
|
|
19
|
+
function measurePhiShellStickyTop(shell) {
|
|
20
|
+
let bottom = 0;
|
|
21
|
+
for (const band of shell.querySelectorAll(HEADER_BANDS)) {
|
|
22
|
+
const style = window.getComputedStyle(band);
|
|
23
|
+
if (style.position !== "sticky" && style.position !== "fixed") {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const rect = band.getBoundingClientRect();
|
|
27
|
+
const stuckAt = Number.parseFloat(style.top) || 0;
|
|
28
|
+
// Below its sticking line it is still in flow; above the viewport it has slid away.
|
|
29
|
+
if (rect.height === 0 || rect.top > stuckAt + 1 || rect.bottom <= 0) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
bottom = Math.max(bottom, rect.bottom);
|
|
33
|
+
}
|
|
34
|
+
return `${Math.round(bottom)}px`;
|
|
35
|
+
}
|
|
36
|
+
const OVERLAY_BODIES = "[data-phi-modal-body-content], [data-phi-drawer-body-content]";
|
|
37
|
+
export function usePhiShellStickyTop({ active, elementRef, }) {
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const element = elementRef.current;
|
|
40
|
+
const shell = active && element && !element.closest(OVERLAY_BODIES)
|
|
41
|
+
? element.closest("[data-phi-shell-layout]")
|
|
42
|
+
: null;
|
|
43
|
+
if (!element || !shell) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
let frame = 0;
|
|
47
|
+
const publish = () => {
|
|
48
|
+
frame = 0;
|
|
49
|
+
const next = measurePhiShellStickyTop(shell);
|
|
50
|
+
if (shell.style.getPropertyValue(PHI_SHELL_STICKY_TOP_PROPERTY) !== next) {
|
|
51
|
+
shell.style.setProperty(PHI_SHELL_STICKY_TOP_PROPERTY, next);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const schedule = () => {
|
|
55
|
+
if (frame === 0) {
|
|
56
|
+
frame = window.requestAnimationFrame(publish);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
publish();
|
|
60
|
+
window.addEventListener("scroll", schedule, { passive: true });
|
|
61
|
+
window.addEventListener("resize", schedule);
|
|
62
|
+
// A revealing band moves by a transition; its end is where it settled.
|
|
63
|
+
shell.addEventListener("transitionend", schedule);
|
|
64
|
+
const observer = new ResizeObserver(schedule);
|
|
65
|
+
for (const band of shell.querySelectorAll(HEADER_BANDS)) {
|
|
66
|
+
observer.observe(band);
|
|
67
|
+
}
|
|
68
|
+
return () => {
|
|
69
|
+
if (frame !== 0) {
|
|
70
|
+
window.cancelAnimationFrame(frame);
|
|
71
|
+
}
|
|
72
|
+
window.removeEventListener("scroll", schedule);
|
|
73
|
+
window.removeEventListener("resize", schedule);
|
|
74
|
+
shell.removeEventListener("transitionend", schedule);
|
|
75
|
+
observer.disconnect();
|
|
76
|
+
};
|
|
77
|
+
}, [active, elementRef]);
|
|
78
|
+
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
label: string;
|
|
5
|
-
}[];
|
|
1
|
+
import type { PhiControlOption } from "../../controls/phi-control-options";
|
|
2
|
+
/** The renditions an Asset is offered in, for every field that picks one; stored as the number the key is. */
|
|
3
|
+
export declare const PHI_IMAGE_VARIANT_OPTIONS: PhiControlOption<number>[];
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { PhiImageAssetVariantKey } from "../../../constants/media";
|
|
2
|
-
/** The renditions an Asset is offered in, for every field that picks one. */
|
|
2
|
+
/** The renditions an Asset is offered in, for every field that picks one; stored as the number the key is. */
|
|
3
3
|
export const PHI_IMAGE_VARIANT_OPTIONS = [
|
|
4
|
-
{ value:
|
|
5
|
-
{ value:
|
|
6
|
-
{ value:
|
|
7
|
-
{ value:
|
|
8
|
-
{ value:
|
|
9
|
-
{ value:
|
|
10
|
-
{ value:
|
|
11
|
-
{ value:
|
|
12
|
-
{ value:
|
|
13
|
-
{ value:
|
|
4
|
+
{ value: PhiImageAssetVariantKey.Thumbnail, label: "Thumbnail" },
|
|
5
|
+
{ value: PhiImageAssetVariantKey.Preview, label: "Preview" },
|
|
6
|
+
{ value: PhiImageAssetVariantKey.Banner, label: "Banner" },
|
|
7
|
+
{ value: PhiImageAssetVariantKey.Header, label: "Header" },
|
|
8
|
+
{ value: PhiImageAssetVariantKey.Card, label: "Card" },
|
|
9
|
+
{ value: PhiImageAssetVariantKey.Hero, label: "Hero" },
|
|
10
|
+
{ value: PhiImageAssetVariantKey.Avatar, label: "Avatar" },
|
|
11
|
+
{ value: PhiImageAssetVariantKey.Logo, label: "Logo" },
|
|
12
|
+
{ value: PhiImageAssetVariantKey.Landscape, label: "Landscape" },
|
|
13
|
+
{ value: PhiImageAssetVariantKey.Portrait, label: "Portrait" },
|
|
14
14
|
];
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type PhiMotionEasing, type PhiSequenceTransition } from "../../../helpers/motion";
|
|
2
|
+
import { type PhiCmsMountPolicy } from "../../../types/cms-mount-policy";
|
|
3
|
+
import type { PhiCmsConfigField, PhiCmsConfigFieldVisibilityRule } from "../../../types/cms-plugins";
|
|
4
|
+
import { type PhiSpacingScaleKey } from "./spacing-options";
|
|
5
|
+
/**
|
|
6
|
+
* How a Widget that shows a run of things a few at a time moves through them.
|
|
7
|
+
*
|
|
8
|
+
* The Gallery runs through the pictures of a Folder, the Entries list through cards of News or Blog;
|
|
9
|
+
* what moves differs, how it moves does not. So the settings are one vocabulary with one reader, one
|
|
10
|
+
* set of Inspector fields and one set of defaults, and `PhiSequenceRun` draws them. The Carousel Layout
|
|
11
|
+
* asks the same questions under its own older keys (`visibleSlots`, `transitionDurationMs`).
|
|
12
|
+
*
|
|
13
|
+
* No window anchor, unlike the Carousel. A run moves a page at a time -- one to three, then four to
|
|
14
|
+
* six -- and nothing outside it names an item to show; a centred anchor would only shift every page by
|
|
15
|
+
* one and leave the dots pointing between pages. The window starts at its active item.
|
|
16
|
+
*/
|
|
17
|
+
export declare const PHI_SEQUENCE_CONTROLS: readonly ["none", "arrows", "dots", "both"];
|
|
18
|
+
export type PhiSequenceControls = (typeof PHI_SEQUENCE_CONTROLS)[number];
|
|
19
|
+
export type PhiSequenceSettings = {
|
|
20
|
+
visibleCount?: number;
|
|
21
|
+
transition?: PhiSequenceTransition;
|
|
22
|
+
durationMs?: number;
|
|
23
|
+
easing?: PhiMotionEasing;
|
|
24
|
+
/** A step of the spacing scale between the items of a window. */
|
|
25
|
+
gap?: PhiSpacingScaleKey;
|
|
26
|
+
/**
|
|
27
|
+
* How many items beyond the window are kept mounted, and therefore told to load early.
|
|
28
|
+
*
|
|
29
|
+
* The browser will not do this for us. `loading="lazy"` decides from an element's distance to the
|
|
30
|
+
* viewport and knows nothing about a track: on a fade every item sits in the same place and all of
|
|
31
|
+
* them load at once, while on a slide the next one is off to the side and loads as it arrives --
|
|
32
|
+
* during the transition, which is the worst moment. Next's escape hatch for that, `lazyRoot`, is a
|
|
33
|
+
* deprecated no-op. So the window decides, and the window is ours.
|
|
34
|
+
*/
|
|
35
|
+
lookahead?: number;
|
|
36
|
+
mountPolicy?: PhiCmsMountPolicy;
|
|
37
|
+
controls?: PhiSequenceControls;
|
|
38
|
+
loop?: boolean;
|
|
39
|
+
/** Absent or zero means it does not move on its own. */
|
|
40
|
+
autoplayMs?: number;
|
|
41
|
+
};
|
|
42
|
+
/** What a run does where nobody said otherwise, stated so the Inspector shows it as the standing answer. */
|
|
43
|
+
export declare const PHI_SEQUENCE_SETTINGS_DEFAULTS: {
|
|
44
|
+
readonly visibleCount: 1;
|
|
45
|
+
readonly transition: "slide";
|
|
46
|
+
readonly durationMs: 320;
|
|
47
|
+
readonly easing: "linear" | "ease" | "ease-in" | "ease-out" | "ease-in-out";
|
|
48
|
+
readonly gap: "xs";
|
|
49
|
+
readonly lookahead: 1;
|
|
50
|
+
readonly mountPolicy: "lazy-keep";
|
|
51
|
+
readonly controls: "both";
|
|
52
|
+
readonly loop: false;
|
|
53
|
+
readonly autoplayMs: 0;
|
|
54
|
+
};
|
|
55
|
+
type PhiSequenceSettingsFail = (key: string, value: unknown, expected: string) => never;
|
|
56
|
+
export declare function readPhiSequenceSettings(raw: Record<string, unknown>, fail: PhiSequenceSettingsFail): PhiSequenceSettings;
|
|
57
|
+
/** The run's settings out of a Widget's config, for a client half that needs these and nothing else. */
|
|
58
|
+
export declare function pickPhiSequenceSettings(config: PhiSequenceSettings): PhiSequenceSettings;
|
|
59
|
+
/**
|
|
60
|
+
* The Inspector's fields for a run, in three groups a Widget places among its own: the window (how many
|
|
61
|
+
* stand side by side and how far apart), the movement, and the controls.
|
|
62
|
+
*
|
|
63
|
+
* `visibleWhen` is added to every field, for a Widget where the run is one presentation among others.
|
|
64
|
+
* `heading` names the window group; the Gallery leaves it out, its window stands under "Picture".
|
|
65
|
+
*/
|
|
66
|
+
export declare function buildPhiSequenceSettingsFields({ countLabel, heading, visibleWhen, }: {
|
|
67
|
+
countLabel: string;
|
|
68
|
+
heading?: string;
|
|
69
|
+
visibleWhen?: PhiCmsConfigFieldVisibilityRule;
|
|
70
|
+
}): {
|
|
71
|
+
window: PhiCmsConfigField[];
|
|
72
|
+
movement: PhiCmsConfigField[];
|
|
73
|
+
controls: PhiCmsConfigField[];
|
|
74
|
+
};
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { PHI_MOTION_EASING_FIELD_OPTIONS, PHI_SEQUENCE_DEFAULT_DURATION_MS, PHI_SEQUENCE_DEFAULT_EASING, PHI_SEQUENCE_TRANSITION_MAX_MS, PHI_SEQUENCE_TRANSITION_MIN_MS, PHI_SEQUENCE_TRANSITION_STEP_MS, PHI_SEQUENCE_TRANSITIONS, clampPhiSequenceTransitionMs, readPhiMotionEasing, } from "../../../helpers/motion";
|
|
2
|
+
import { PHI_CMS_MOUNT_POLICY_FIELD_OPTIONS, readPhiCmsMountPolicy, } from "../../../types/cms-mount-policy";
|
|
3
|
+
import { PHI_SPACING_SCALE_KEYS } from "./spacing-options";
|
|
4
|
+
/**
|
|
5
|
+
* How a Widget that shows a run of things a few at a time moves through them.
|
|
6
|
+
*
|
|
7
|
+
* The Gallery runs through the pictures of a Folder, the Entries list through cards of News or Blog;
|
|
8
|
+
* what moves differs, how it moves does not. So the settings are one vocabulary with one reader, one
|
|
9
|
+
* set of Inspector fields and one set of defaults, and `PhiSequenceRun` draws them. The Carousel Layout
|
|
10
|
+
* asks the same questions under its own older keys (`visibleSlots`, `transitionDurationMs`).
|
|
11
|
+
*
|
|
12
|
+
* No window anchor, unlike the Carousel. A run moves a page at a time -- one to three, then four to
|
|
13
|
+
* six -- and nothing outside it names an item to show; a centred anchor would only shift every page by
|
|
14
|
+
* one and leave the dots pointing between pages. The window starts at its active item.
|
|
15
|
+
*/
|
|
16
|
+
export const PHI_SEQUENCE_CONTROLS = ["none", "arrows", "dots", "both"];
|
|
17
|
+
/** What a run does where nobody said otherwise, stated so the Inspector shows it as the standing answer. */
|
|
18
|
+
export const PHI_SEQUENCE_SETTINGS_DEFAULTS = {
|
|
19
|
+
visibleCount: 1,
|
|
20
|
+
transition: "slide",
|
|
21
|
+
durationMs: PHI_SEQUENCE_DEFAULT_DURATION_MS,
|
|
22
|
+
easing: PHI_SEQUENCE_DEFAULT_EASING,
|
|
23
|
+
gap: "xs",
|
|
24
|
+
lookahead: 1,
|
|
25
|
+
mountPolicy: "lazy-keep",
|
|
26
|
+
controls: "both",
|
|
27
|
+
loop: false,
|
|
28
|
+
autoplayMs: 0,
|
|
29
|
+
};
|
|
30
|
+
function readMember(value, members, key, fail) {
|
|
31
|
+
if (value === undefined || value === null)
|
|
32
|
+
return undefined;
|
|
33
|
+
if (typeof value !== "string" || !members.includes(value)) {
|
|
34
|
+
fail(key, value, `one of ${members.join(", ")}`);
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
function readCount(value, key, min, fail) {
|
|
39
|
+
if (value === undefined || value === null)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < min) {
|
|
42
|
+
fail(key, value, `a whole number of at least ${min}`);
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
export function readPhiSequenceSettings(raw, fail) {
|
|
47
|
+
const visibleCount = readCount(raw.visibleCount, "visibleCount", 1, fail);
|
|
48
|
+
const transition = readMember(raw.transition, PHI_SEQUENCE_TRANSITIONS, "transition", fail);
|
|
49
|
+
const easing = readPhiMotionEasing(raw.easing, undefined);
|
|
50
|
+
const gap = readMember(raw.gap, PHI_SPACING_SCALE_KEYS, "gap", fail);
|
|
51
|
+
const lookahead = readCount(raw.lookahead, "lookahead", 0, fail);
|
|
52
|
+
const controls = readMember(raw.controls, PHI_SEQUENCE_CONTROLS, "controls", fail);
|
|
53
|
+
if (raw.loop !== undefined && raw.loop !== null && typeof raw.loop !== "boolean") {
|
|
54
|
+
fail("loop", raw.loop, "true or false");
|
|
55
|
+
}
|
|
56
|
+
const loop = typeof raw.loop === "boolean" ? raw.loop : undefined;
|
|
57
|
+
const autoplayMs = readCount(raw.autoplayMs, "autoplayMs", 0, fail);
|
|
58
|
+
return {
|
|
59
|
+
...(visibleCount === undefined ? {} : { visibleCount }),
|
|
60
|
+
...(transition === undefined ? {} : { transition }),
|
|
61
|
+
...(raw.durationMs === undefined || raw.durationMs === null
|
|
62
|
+
? {}
|
|
63
|
+
: { durationMs: clampPhiSequenceTransitionMs(raw.durationMs) }),
|
|
64
|
+
...(easing === undefined ? {} : { easing }),
|
|
65
|
+
...(gap === undefined ? {} : { gap }),
|
|
66
|
+
...(lookahead === undefined ? {} : { lookahead }),
|
|
67
|
+
mountPolicy: readPhiCmsMountPolicy(raw.mountPolicy, PHI_SEQUENCE_SETTINGS_DEFAULTS.mountPolicy),
|
|
68
|
+
...(controls === undefined ? {} : { controls }),
|
|
69
|
+
...(loop === undefined ? {} : { loop }),
|
|
70
|
+
// Zero is how "does not move on its own" is written, so it is the one number below the floor
|
|
71
|
+
// that means something.
|
|
72
|
+
...(autoplayMs === undefined || autoplayMs === 0
|
|
73
|
+
? {}
|
|
74
|
+
: { autoplayMs: clampPhiSequenceTransitionMs(autoplayMs) }),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const PHI_SEQUENCE_SETTINGS_KEYS = Object.keys(PHI_SEQUENCE_SETTINGS_DEFAULTS);
|
|
78
|
+
/** The run's settings out of a Widget's config, for a client half that needs these and nothing else. */
|
|
79
|
+
export function pickPhiSequenceSettings(config) {
|
|
80
|
+
return Object.fromEntries(PHI_SEQUENCE_SETTINGS_KEYS.filter((key) => config[key] !== undefined).map((key) => [key, config[key]]));
|
|
81
|
+
}
|
|
82
|
+
function withRule(field, rule) {
|
|
83
|
+
if (!rule)
|
|
84
|
+
return field;
|
|
85
|
+
const own = field.visibleWhen == null ? [] : Array.isArray(field.visibleWhen) ? field.visibleWhen : [field.visibleWhen];
|
|
86
|
+
return { ...field, visibleWhen: [...own, rule] };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The Inspector's fields for a run, in three groups a Widget places among its own: the window (how many
|
|
90
|
+
* stand side by side and how far apart), the movement, and the controls.
|
|
91
|
+
*
|
|
92
|
+
* `visibleWhen` is added to every field, for a Widget where the run is one presentation among others.
|
|
93
|
+
* `heading` names the window group; the Gallery leaves it out, its window stands under "Picture".
|
|
94
|
+
*/
|
|
95
|
+
export function buildPhiSequenceSettingsFields({ countLabel, heading, visibleWhen, }) {
|
|
96
|
+
const window = [
|
|
97
|
+
{ key: "visibleCount", type: "number", label: countLabel, min: 1, ...(heading ? { heading } : {}) },
|
|
98
|
+
{
|
|
99
|
+
key: "gap",
|
|
100
|
+
type: "choice",
|
|
101
|
+
label: "Gap",
|
|
102
|
+
options: PHI_SPACING_SCALE_KEYS.map((key) => ({ value: key, label: key })),
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
const movement = [
|
|
106
|
+
{
|
|
107
|
+
key: "transition",
|
|
108
|
+
type: "choice",
|
|
109
|
+
heading: "Transition",
|
|
110
|
+
label: "Transition",
|
|
111
|
+
options: [
|
|
112
|
+
{ value: "none", label: "None" },
|
|
113
|
+
{ value: "fade", label: "Fade" },
|
|
114
|
+
{ value: "slide", label: "Slide" },
|
|
115
|
+
{ value: "diagonal", label: "Diagonal" },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
key: "durationMs",
|
|
120
|
+
type: "number",
|
|
121
|
+
label: "Duration",
|
|
122
|
+
min: PHI_SEQUENCE_TRANSITION_MIN_MS,
|
|
123
|
+
max: PHI_SEQUENCE_TRANSITION_MAX_MS,
|
|
124
|
+
step: PHI_SEQUENCE_TRANSITION_STEP_MS,
|
|
125
|
+
prefix: "ms",
|
|
126
|
+
visibleWhen: { field: "transition", notEquals: "none" },
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
key: "easing",
|
|
130
|
+
type: "choice",
|
|
131
|
+
label: "Easing",
|
|
132
|
+
options: [...PHI_MOTION_EASING_FIELD_OPTIONS],
|
|
133
|
+
visibleWhen: { field: "transition", notEquals: "none" },
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
// Zero is the one value below the floor that means something: it does not move on its own.
|
|
137
|
+
key: "autoplayMs",
|
|
138
|
+
type: "number",
|
|
139
|
+
label: "Autoplay",
|
|
140
|
+
min: 0,
|
|
141
|
+
max: PHI_SEQUENCE_TRANSITION_MAX_MS,
|
|
142
|
+
step: PHI_SEQUENCE_TRANSITION_STEP_MS,
|
|
143
|
+
prefix: "ms",
|
|
144
|
+
},
|
|
145
|
+
{ key: "lookahead", type: "number", label: "Preload Ahead", min: 0 },
|
|
146
|
+
{
|
|
147
|
+
key: "mountPolicy",
|
|
148
|
+
type: "choice",
|
|
149
|
+
label: "Mounting",
|
|
150
|
+
options: [...PHI_CMS_MOUNT_POLICY_FIELD_OPTIONS],
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
const controls = [
|
|
154
|
+
{
|
|
155
|
+
key: "controls",
|
|
156
|
+
type: "choice",
|
|
157
|
+
heading: "Controls",
|
|
158
|
+
label: "Controls",
|
|
159
|
+
options: [
|
|
160
|
+
{ value: "both", label: "Arrows and dots" },
|
|
161
|
+
{ value: "arrows", label: "Arrows" },
|
|
162
|
+
{ value: "dots", label: "Dots" },
|
|
163
|
+
{ value: "none", label: "None" },
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
{ key: "loop", type: "boolean", label: "Wrap Around" },
|
|
167
|
+
];
|
|
168
|
+
return {
|
|
169
|
+
window: window.map((field) => withRule(field, visibleWhen)),
|
|
170
|
+
movement: movement.map((field) => withRule(field, visibleWhen)),
|
|
171
|
+
controls: controls.map((field) => withRule(field, visibleWhen)),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import "server-only";
|
|
2
|
-
export declare function translateSemanticHtml(sourceHtml: string, sourceLocale?: string
|
|
2
|
+
export declare function translateSemanticHtml(sourceHtml: string, sourceLocale?: string,
|
|
3
|
+
/** The render's locale. Without it the request's is asked, and a static render has no request. */
|
|
4
|
+
locale?: string): Promise<string>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "server-only";
|
|
2
2
|
import renderHtml from "dom-serializer";
|
|
3
3
|
import { parseDocument } from "htmlparser2";
|
|
4
|
-
import { trBulk } from "../../../server-helpers/translate";
|
|
4
|
+
import { trBulk, trBulkForLocale } from "../../../server-helpers/translate";
|
|
5
5
|
import { sanitizePhiHtmlWidgetMarkup } from "./html-content";
|
|
6
6
|
const SEMANTIC_HTML_BLOCKS = new Set([
|
|
7
7
|
"p",
|
|
@@ -49,7 +49,9 @@ function renderNodes(nodes) {
|
|
|
49
49
|
function hasVisibleText(html) {
|
|
50
50
|
return html.replace(/<[^>]+>/g, "").replace(/ /g, " ").trim().length > 0;
|
|
51
51
|
}
|
|
52
|
-
export async function translateSemanticHtml(sourceHtml, sourceLocale
|
|
52
|
+
export async function translateSemanticHtml(sourceHtml, sourceLocale,
|
|
53
|
+
/** The render's locale. Without it the request's is asked, and a static render has no request. */
|
|
54
|
+
locale) {
|
|
53
55
|
const sanitized = sanitizePhiHtmlWidgetMarkup(sourceHtml);
|
|
54
56
|
if (!sanitized) {
|
|
55
57
|
return "";
|
|
@@ -64,7 +66,10 @@ export async function translateSemanticHtml(sourceHtml, sourceLocale) {
|
|
|
64
66
|
if (units.length === 0) {
|
|
65
67
|
return sanitized;
|
|
66
68
|
}
|
|
67
|
-
const
|
|
69
|
+
const msgs = units.map((unit) => unit.html);
|
|
70
|
+
const translated = locale
|
|
71
|
+
? await trBulkForLocale(locale, msgs, 0, "html", sourceLocale)
|
|
72
|
+
: await trBulk(msgs, 0, "html", sourceLocale);
|
|
68
73
|
units.forEach((unit, index) => {
|
|
69
74
|
const fragment = parseDocument(translated[index] ?? unit.html);
|
|
70
75
|
unit.node.children = fragment.children ?? [];
|
|
@@ -124,5 +124,14 @@ export declare function getPhiContentEntryReaderLabels(options: PhiGlobalTransla
|
|
|
124
124
|
unavailable: string;
|
|
125
125
|
readMore: string;
|
|
126
126
|
readEntry: string;
|
|
127
|
+
sequence: {
|
|
128
|
+
previous: string;
|
|
129
|
+
next: string;
|
|
130
|
+
page: string;
|
|
131
|
+
pause: string;
|
|
132
|
+
play: string;
|
|
133
|
+
region: string;
|
|
134
|
+
pages: string;
|
|
135
|
+
};
|
|
127
136
|
}>;
|
|
128
137
|
export type PhiContentEntryReaderLabels = Awaited<ReturnType<typeof getPhiContentEntryReaderLabels>>;
|
|
@@ -183,6 +183,14 @@ const PHI_CONTENT_ENTRY_READER_LABEL_SET = definePhiLabelSet({
|
|
|
183
183
|
unavailable: definePhiMessageLabel("The entries could not be loaded just now."),
|
|
184
184
|
read_more: "Read more",
|
|
185
185
|
read_entry: "Read",
|
|
186
|
+
// A slideshow's buttons and dots, and its name for a screen reader.
|
|
187
|
+
previous: "Previous",
|
|
188
|
+
next: "Next",
|
|
189
|
+
page: definePhiMessageLabel("Show entries %1 to %2"),
|
|
190
|
+
pause: "Pause",
|
|
191
|
+
play: "Play",
|
|
192
|
+
region: "Entries",
|
|
193
|
+
pages: definePhiMessageLabel("Choose entries"),
|
|
186
194
|
},
|
|
187
195
|
});
|
|
188
196
|
export async function getPhiContentEntryReaderLabels(options) {
|
|
@@ -193,5 +201,14 @@ export async function getPhiContentEntryReaderLabels(options) {
|
|
|
193
201
|
unavailable: labels.unavailable,
|
|
194
202
|
readMore: labels.read_more,
|
|
195
203
|
readEntry: labels.read_entry,
|
|
204
|
+
sequence: {
|
|
205
|
+
previous: labels.previous,
|
|
206
|
+
next: labels.next,
|
|
207
|
+
page: labels.page,
|
|
208
|
+
pause: labels.pause,
|
|
209
|
+
play: labels.play,
|
|
210
|
+
region: labels.region,
|
|
211
|
+
pages: labels.pages,
|
|
212
|
+
},
|
|
196
213
|
};
|
|
197
214
|
}
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
next: string;
|
|
4
|
-
/** `%1` and `%2` are the first and last picture the page shows. */
|
|
5
|
-
page: string;
|
|
6
|
-
pause: string;
|
|
7
|
-
play: string;
|
|
8
|
-
/** The box's name for a screen reader, which calls it a carousel. */
|
|
9
|
-
region: string;
|
|
10
|
-
pages: string;
|
|
1
|
+
import type { PhiSequenceControlLabels } from "./sequence";
|
|
2
|
+
export type PhiGalleryWidgetLabels = PhiSequenceControlLabels & {
|
|
11
3
|
unavailable: string;
|
|
12
4
|
};
|
|
13
5
|
export declare const PHI_GALLERY_WIDGET_DEFAULT_LABELS: PhiGalleryWidgetLabels;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The words around a run that moves: its buttons, its dots, and its name for a screen reader.
|
|
3
|
+
*
|
|
4
|
+
* Every Widget that draws `PhiSequenceRun` brings them from its own Label Set, because what is being
|
|
5
|
+
* stepped through is its own -- pictures in a Gallery, entries in a list -- and "Show pictures 1 to 3"
|
|
6
|
+
* is not a sentence about News.
|
|
7
|
+
*/
|
|
8
|
+
export type PhiSequenceControlLabels = {
|
|
9
|
+
previous: string;
|
|
10
|
+
next: string;
|
|
11
|
+
/** `%1` and `%2` are the first and last item the page shows. */
|
|
12
|
+
page: string;
|
|
13
|
+
pause: string;
|
|
14
|
+
play: string;
|
|
15
|
+
/** The box's name for a screen reader, which calls it a carousel. */
|
|
16
|
+
region: string;
|
|
17
|
+
pages: string;
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|