@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.
Files changed (164) hide show
  1. package/components/cms/phi-cms-layout-renderer.js +1 -1
  2. package/components/controls/phi-collection-header-control.js +1 -1
  3. package/components/controls/phi-collection-view-control.js +2 -1
  4. package/components/controls/phi-drawer-control-adapter.d.ts +1 -1
  5. package/components/controls/phi-drawer-control-adapter.js +2 -2
  6. package/components/controls/phi-image-control.d.ts +6 -1
  7. package/components/controls/phi-image-control.js +23 -4
  8. package/components/controls/phi-lightbox-image-control-adapter.d.ts +2 -0
  9. package/components/controls/phi-lightbox-image-control-adapter.js +11 -0
  10. package/components/controls/phi-lightbox-image-control.d.ts +3 -2
  11. package/components/controls/phi-lightbox-image-control.js +7 -3
  12. package/components/controls/phi-modal-control-adapter.d.ts +1 -1
  13. package/components/controls/phi-modal-control-adapter.js +5 -2
  14. package/components/controls/phi-overlay-control-contract.d.ts +5 -0
  15. package/components/controls/phi-pagination-control-adapter.d.ts +2 -0
  16. package/components/controls/phi-pagination-control-adapter.js +15 -0
  17. package/components/controls/phi-pagination-control.d.ts +2 -1
  18. package/components/controls/phi-pagination-control.js +11 -7
  19. package/components/controls/phi-table-control.d.ts +6 -1
  20. package/components/controls/phi-table-control.js +17 -5
  21. package/components/controls/phi-timeline-control-adapter.d.ts +2 -0
  22. package/components/controls/phi-timeline-control-adapter.js +28 -0
  23. package/components/controls/phi-timeline-control.d.ts +3 -1
  24. package/components/controls/phi-timeline-control.js +8 -22
  25. package/components/controls/phi-tour-control.js +25 -7
  26. package/components/controls/phi-tour-viewport.d.ts +36 -0
  27. package/components/controls/phi-tour-viewport.js +86 -0
  28. package/components/controls/phi-zoom-image-overlay.d.ts +17 -0
  29. package/components/controls/phi-zoom-image-overlay.js +58 -0
  30. package/components/layouts/layout-definitions.d.ts +2 -20
  31. package/components/layouts/layout-definitions.js +13 -2
  32. package/components/layouts/plugins/carousel-layout-plugin.js +2 -1
  33. package/components/media/aspect-ratios.d.ts +14 -0
  34. package/components/media/aspect-ratios.js +13 -0
  35. package/components/motion/phi-sequence-run.d.ts +27 -0
  36. package/components/motion/phi-sequence-run.js +87 -0
  37. package/components/motion/phi-sequence-viewport.d.ts +16 -0
  38. package/components/motion/phi-sequence-viewport.js +97 -34
  39. package/components/overlays/phi-overlay-container-client.js +5 -2
  40. package/components/runtime/runtime-visual-anchors.d.ts +13 -2
  41. package/components/runtime/runtime-visual-anchors.js +49 -3
  42. package/components/shell/use-phi-shell-sticky-top.d.ts +5 -0
  43. package/components/shell/use-phi-shell-sticky-top.js +78 -0
  44. package/components/widgets/config/image-variant-options.d.ts +3 -5
  45. package/components/widgets/config/image-variant-options.js +11 -11
  46. package/components/widgets/config/sequence-settings.d.ts +75 -0
  47. package/components/widgets/config/sequence-settings.js +173 -0
  48. package/components/widgets/helpers/semantic-html-translation.d.ts +3 -1
  49. package/components/widgets/helpers/semantic-html-translation.js +8 -3
  50. package/components/widgets/label-sets/content-entries.d.ts +9 -0
  51. package/components/widgets/label-sets/content-entries.js +17 -0
  52. package/components/widgets/label-types/gallery.d.ts +2 -10
  53. package/components/widgets/label-types/sequence.d.ts +18 -0
  54. package/components/widgets/label-types/sequence.js +1 -0
  55. package/components/widgets/navigation-descriptor-resolver.server.js +6 -1
  56. package/components/widgets/shared/markdown-render.js +15 -5
  57. package/helpers/cms-layout-defaults.js +9 -2
  58. package/helpers/cms-region-keys.d.ts +1 -1
  59. package/helpers/motion.d.ts +7 -0
  60. package/helpers/motion.js +7 -0
  61. package/helpers/text-source.d.ts +19 -0
  62. package/helpers/text-source.js +20 -0
  63. package/next/config.mjs +1 -1
  64. package/package.json +1 -1
  65. package/plugins/runtime-modules/asset/data-providers.d.ts +4 -0
  66. package/plugins/runtime-modules/asset/data-providers.js +1 -0
  67. package/plugins/runtime-modules/asset/definition.d.ts +4 -0
  68. package/plugins/runtime-modules/asset/presets.d.ts +53 -1
  69. package/plugins/runtime-modules/asset/presets.js +16 -12
  70. package/plugins/runtime-modules/asset/tour.d.ts +44 -0
  71. package/plugins/runtime-modules/asset/tour.js +53 -0
  72. package/plugins/runtime-modules/auth/trees/phi-default-pub-confirm-page-tree.js +3 -0
  73. package/plugins/runtime-modules/auth/trees/phi-default-pub-login-page-tree.js +2 -0
  74. package/plugins/runtime-modules/auth/trees/phi-default-pub-registration-page-tree.js +3 -0
  75. package/plugins/runtime-modules/auth/trees/phi-default-pub-reset-password-page-tree.js +3 -0
  76. package/plugins/runtime-modules/builder/clients/inspector-config-field.js +42 -18
  77. package/plugins/runtime-modules/builder/clients/inspector-image-field.js +2 -2
  78. package/plugins/runtime-modules/builder/clients/widget-inspector.js +9 -0
  79. package/plugins/runtime-modules/builder/clients/workspace-scope-boundary.js +10 -0
  80. package/plugins/runtime-modules/builder/controller/workspace-controller.js +18 -3
  81. package/plugins/runtime-modules/builder/plugin-meta-labels.js +8 -0
  82. package/plugins/runtime-modules/builder/presets.js +5 -3
  83. package/plugins/runtime-modules/builder/tour.d.ts +316 -0
  84. package/plugins/runtime-modules/builder/tour.js +337 -0
  85. package/plugins/runtime-modules/builder/trees/phi-builder-inspector-area-overlay-tree.server.js +8 -1
  86. package/plugins/runtime-modules/builder/trees/phi-default-builder-area-preset-tree.js +4 -0
  87. package/plugins/runtime-modules/builder/widgets/structure-region/built-in.js +3 -2
  88. package/plugins/runtime-modules/core/widgets/card/server.js +4 -2
  89. package/plugins/runtime-modules/core/widgets/collection-view/client.js +10 -0
  90. package/plugins/runtime-modules/core/widgets/description/config.d.ts +28 -1
  91. package/plugins/runtime-modules/core/widgets/description/config.js +2 -0
  92. package/plugins/runtime-modules/core/widgets/description/server.js +10 -12
  93. package/plugins/runtime-modules/core/widgets/entry-detail/config.d.ts +2 -0
  94. package/plugins/runtime-modules/core/widgets/entry-detail/config.js +8 -1
  95. package/plugins/runtime-modules/core/widgets/entry-header/config.d.ts +3 -0
  96. package/plugins/runtime-modules/core/widgets/entry-header/config.js +8 -1
  97. package/plugins/runtime-modules/core/widgets/entry-list/client.d.ts +4 -1
  98. package/plugins/runtime-modules/core/widgets/entry-list/client.js +3 -2
  99. package/plugins/runtime-modules/core/widgets/entry-list/config.d.ts +392 -93
  100. package/plugins/runtime-modules/core/widgets/entry-list/config.js +21 -3
  101. package/plugins/runtime-modules/core/widgets/entry-list/server.js +4 -2
  102. package/plugins/runtime-modules/core/widgets/gallery/authoring.js +62 -10
  103. package/plugins/runtime-modules/core/widgets/gallery/client.d.ts +6 -1
  104. package/plugins/runtime-modules/core/widgets/gallery/client.js +7 -79
  105. package/plugins/runtime-modules/core/widgets/gallery/config.d.ts +337 -144
  106. package/plugins/runtime-modules/core/widgets/gallery/config.js +52 -113
  107. package/plugins/runtime-modules/core/widgets/gallery/defaults.d.ts +15 -0
  108. package/plugins/runtime-modules/core/widgets/gallery/defaults.js +20 -0
  109. package/plugins/runtime-modules/core/widgets/html/server.js +1 -1
  110. package/plugins/runtime-modules/core/widgets/image/client.js +1 -1
  111. package/plugins/runtime-modules/core/widgets/image/config.d.ts +29 -4
  112. package/plugins/runtime-modules/core/widgets/image/config.js +19 -2
  113. package/plugins/runtime-modules/core/widgets/image/editor.js +1 -1
  114. package/plugins/runtime-modules/core/widgets/markdown-toc/server.js +7 -2
  115. package/plugins/runtime-modules/core/widgets/quick-links/config.d.ts +3 -0
  116. package/plugins/runtime-modules/core/widgets/quick-links/config.js +2 -0
  117. package/plugins/runtime-modules/core/widgets/quick-links/plugin.js +4 -2
  118. package/plugins/runtime-modules/core/widgets/quick-links/server.d.ts +5 -1
  119. package/plugins/runtime-modules/core/widgets/quick-links/server.js +4 -2
  120. package/plugins/runtime-modules/core/widgets/result/config.d.ts +25 -1
  121. package/plugins/runtime-modules/core/widgets/simple-text/config.d.ts +28 -1
  122. package/plugins/runtime-modules/core/widgets/simple-text/config.js +2 -0
  123. package/plugins/runtime-modules/core/widgets/simple-text/plugin.js +1 -1
  124. package/plugins/runtime-modules/core/widgets/simple-text/server.d.ts +4 -1
  125. package/plugins/runtime-modules/core/widgets/simple-text/server.js +3 -3
  126. package/plugins/runtime-modules/core/widgets/table/client.js +1 -1
  127. package/plugins/runtime-modules/core/widgets/table/config.d.ts +80 -3
  128. package/plugins/runtime-modules/core/widgets/table/config.js +3 -0
  129. package/plugins/runtime-modules/core/widgets/tour-button/client.js +1 -1
  130. package/plugins/runtime-modules/phi-default-site-area-preset-tree.js +12 -4
  131. package/plugins/runtime-modules/public/trees/phi-default-pub-contact-page-tree.js +2 -0
  132. package/plugins/runtime-modules/public/trees/phi-default-pub-unsubscribe-page-tree.js +3 -0
  133. package/plugins/runtime-modules/public/trees/phi-default-pub-welcome-page-tree.js +2 -1
  134. package/plugins/runtime-modules/revisions/presets.d.ts +53 -2
  135. package/plugins/runtime-modules/revisions/presets.js +16 -12
  136. package/plugins/runtime-modules/revisions/tour.d.ts +43 -0
  137. package/plugins/runtime-modules/revisions/tour.js +51 -0
  138. package/plugins/runtime-modules/revisions/trees/phi-default-builder-revisions-page-tree.js +4 -0
  139. package/plugins/runtime-modules/theme/presets.d.ts +36 -4
  140. package/plugins/runtime-modules/theme/tour.d.ts +45 -11
  141. package/plugins/runtime-modules/theme/tour.js +39 -8
  142. package/plugins/runtime-modules/tour/controller/address.js +2 -2
  143. package/plugins/runtime-modules/tour/controller/client.js +43 -2
  144. package/plugins/runtime-modules/tour/controller/definition.js +2 -1
  145. package/server-helpers/cms-request.js +1 -0
  146. package/server-helpers/page-tour.d.ts +10 -3
  147. package/server-helpers/page-tour.js +25 -12
  148. package/server-helpers/translate.d.ts +9 -0
  149. package/server-helpers/translate.js +14 -0
  150. package/styles/controls.css +15 -1
  151. package/styles/root.css +20 -19
  152. package/types/cms-config.d.ts +3 -1
  153. package/types/cms-config.js +3 -3
  154. package/types/cms-plugins.d.ts +31 -4
  155. package/types/core-widget-placements.d.ts +5 -0
  156. package/types/table-widget.d.ts +2 -0
  157. package/types/tour-controller.d.ts +10 -0
  158. package/types/tour-controller.js +13 -0
  159. package/types/tours.d.ts +27 -25
  160. package/types/tours.js +9 -23
  161. package/types/visual-anchors.d.ts +15 -0
  162. package/types/visual-anchors.js +12 -0
  163. package/types.d.ts +2 -0
  164. package/types.js +4 -1
@@ -1,34 +1,68 @@
1
+ import type { PhiCmsInstanceId } from "../../../types/cms-instance-id";
2
+ import type { PhiTourStepSignal } from "../../../types/tours";
1
3
  /**
2
4
  * The Theme workspace's Tour (TODOS.md, "A Tour for the Theme workspace").
3
5
  *
4
- * What the workspace does not say out loud is its first step: a Module's Theme is offered and never
5
- * taken, so installing a package that ships a look changes nothing until somebody picks it here. The
6
- * rest is the order a person meets the workspace in.
6
+ * What the workspace does not say out loud is its first step: the selector picks what a draft starts
7
+ * from. A Module's Theme is a base to start from, never edited through it, and installing a package
8
+ * that ships a look changes nothing for the Site. The rest is the order a person meets the workspace in.
7
9
  */
8
10
  export declare const PHI_THEME_WORKSPACE_TOUR: {
9
11
  readonly id: "@phis/ui/modules/theme/workspace";
10
12
  readonly version: 1;
11
13
  readonly steps: readonly [{
12
14
  readonly key: "theme-choice";
13
- readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}`;
14
- readonly title: "Which theme you are working on";
15
- readonly description: "The Site's published theme, its draft, or a theme a Module brings. A Module's theme changes nothing until it is picked here.";
15
+ readonly target: `cms:${PhiCmsInstanceId}`;
16
+ readonly title: "What your draft starts from";
17
+ readonly description: "The Site's published theme, its draft, or a theme a Module brings. Picking one makes it the base of your theme draft; the Module's theme itself stays as it is.";
16
18
  readonly placement: "bottom";
17
19
  }, {
18
20
  readonly key: "theme-parts";
19
- readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}`;
20
- readonly title: "Colour and style";
21
- readonly description: "Switch between the palette and the shapes and type of the theme.";
21
+ readonly target: `cms:${PhiCmsInstanceId}`;
22
+ readonly title: "Four parts of the theme";
23
+ readonly description: "Colour, style, background and brand: each opens its own controls on the left, with the preview beside them.";
22
24
  readonly placement: "bottom";
25
+ }, {
26
+ readonly key: string;
27
+ readonly target: `cms:${PhiCmsInstanceId}`;
28
+ readonly title: string;
29
+ readonly description: string;
30
+ readonly placement: "right";
31
+ readonly prepare: readonly [PhiTourStepSignal];
32
+ readonly release: readonly [PhiTourStepSignal];
33
+ }, {
34
+ readonly key: string;
35
+ readonly target: `cms:${PhiCmsInstanceId}`;
36
+ readonly title: string;
37
+ readonly description: string;
38
+ readonly placement: "right";
39
+ readonly prepare: readonly [PhiTourStepSignal];
40
+ readonly release: readonly [PhiTourStepSignal];
41
+ }, {
42
+ readonly key: string;
43
+ readonly target: `cms:${PhiCmsInstanceId}`;
44
+ readonly title: string;
45
+ readonly description: string;
46
+ readonly placement: "right";
47
+ readonly prepare: readonly [PhiTourStepSignal];
48
+ readonly release: readonly [PhiTourStepSignal];
49
+ }, {
50
+ readonly key: string;
51
+ readonly target: `cms:${PhiCmsInstanceId}`;
52
+ readonly title: string;
53
+ readonly description: string;
54
+ readonly placement: "right";
55
+ readonly prepare: readonly [PhiTourStepSignal];
56
+ readonly release: readonly [PhiTourStepSignal];
23
57
  }, {
24
58
  readonly key: "preview-mode";
25
- readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}`;
59
+ readonly target: `cms:${PhiCmsInstanceId}`;
26
60
  readonly title: "Light and dark";
27
61
  readonly description: "See the theme in either mode. This switches the preview, not the Site.";
28
62
  readonly placement: "bottom";
29
63
  }, {
30
64
  readonly key: "draft-status";
31
- readonly target: `cms:${import("@phis/contracts/cms").PhiCmsInstanceId}`;
65
+ readonly target: `cms:${PhiCmsInstanceId}`;
32
66
  readonly title: "Where your changes stand";
33
67
  readonly description: "What you change here is a draft. The Site keeps its published theme until the draft is published.";
34
68
  readonly placement: "bottom";
@@ -1,11 +1,38 @@
1
+ import { PHI_CMS_SEQUENTIAL_LAYOUT_SLOTS } from "../../../constants/cms-layout-types";
1
2
  import { PHI_THEME_RUNTIME_MODULE_ID } from "./ids";
2
- import { PHI_THEME_PAGE_WIDGET_IDS } from "./page-ids";
3
+ import { PHI_THEME_PAGE_LAYOUT_IDS, PHI_THEME_PAGE_WIDGET_IDS } from "./page-ids";
4
+ /** Turns the workspace's Stack to one of its four panels, as the Segmented above it does. */
5
+ function showPanel(slot) {
6
+ return {
7
+ receiver: `cms:${PHI_THEME_PAGE_LAYOUT_IDS.layoutBrandStack}`,
8
+ channel: "activeSlotKey",
9
+ action: "change",
10
+ valueType: "string",
11
+ value: PHI_CMS_SEQUENTIAL_LAYOUT_SLOTS[slot].key,
12
+ };
13
+ }
14
+ /**
15
+ * One panel of the workspace, pointed at by its controls. The step turns the Stack to the panel and back
16
+ * to Color when it is left, the panel the workspace opens with; from one panel step to the next both
17
+ * arrive together, so the Stack simply moves on.
18
+ */
19
+ function panelStep(key, slot, controls, title, description) {
20
+ return {
21
+ key,
22
+ target: `cms:${controls}`,
23
+ title,
24
+ description,
25
+ placement: "right",
26
+ prepare: [showPanel(slot)],
27
+ release: [showPanel(0)],
28
+ };
29
+ }
3
30
  /**
4
31
  * The Theme workspace's Tour (TODOS.md, "A Tour for the Theme workspace").
5
32
  *
6
- * What the workspace does not say out loud is its first step: a Module's Theme is offered and never
7
- * taken, so installing a package that ships a look changes nothing until somebody picks it here. The
8
- * rest is the order a person meets the workspace in.
33
+ * What the workspace does not say out loud is its first step: the selector picks what a draft starts
34
+ * from. A Module's Theme is a base to start from, never edited through it, and installing a package
35
+ * that ships a look changes nothing for the Site. The rest is the order a person meets the workspace in.
9
36
  */
10
37
  export const PHI_THEME_WORKSPACE_TOUR = {
11
38
  id: `${PHI_THEME_RUNTIME_MODULE_ID}/workspace`,
@@ -14,17 +41,21 @@ export const PHI_THEME_WORKSPACE_TOUR = {
14
41
  {
15
42
  key: "theme-choice",
16
43
  target: `cms:${PHI_THEME_PAGE_WIDGET_IDS.widgetBrandContextSelect}`,
17
- title: "Which theme you are working on",
18
- description: "The Site's published theme, its draft, or a theme a Module brings. A Module's theme changes nothing until it is picked here.",
44
+ title: "What your draft starts from",
45
+ description: "The Site's published theme, its draft, or a theme a Module brings. Picking one makes it the base of your theme draft; the Module's theme itself stays as it is.",
19
46
  placement: "bottom",
20
47
  },
21
48
  {
22
49
  key: "theme-parts",
23
50
  target: `cms:${PHI_THEME_PAGE_WIDGET_IDS.widgetThemeStackSegmented}`,
24
- title: "Colour and style",
25
- description: "Switch between the palette and the shapes and type of the theme.",
51
+ title: "Four parts of the theme",
52
+ description: "Colour, style, background and brand: each opens its own controls on the left, with the preview beside them.",
26
53
  placement: "bottom",
27
54
  },
55
+ panelStep("color", 0, PHI_THEME_PAGE_WIDGET_IDS.widgetBrandThemeControls, "Colour from a few seeds", "A palette is picked whole, or each colour is set on its own. Every colour is a seed: Primary, Info, Success, Warning, Error, Link, the text and the background each grow into the shades for hover, borders and backgrounds, and Custom grows into ten steps of its own."),
56
+ panelStep("style", 1, PHI_THEME_PAGE_WIDGET_IDS.widgetBrandStyleControls, "Buttons and corners", "A shape sets the corners at once -- square, subtle, rounded or pill -- for buttons and fields alike. Below it the details: the buttons' shadows, the corner radius in three sizes, how tall controls are, and the fonts and their size."),
57
+ panelStep("background", 2, PHI_THEME_PAGE_WIDGET_IDS.widgetBrandBackgroundControls, "Behind everything", "The root background is one layer behind the whole Site: a colour, a gradient or an image, still or in parallax, with an overlay of colour, pattern or noise on top. The chrome overlay is a second ground the header, the sider and the footer share, and the chrome shadow sets how they cast onto the Page. Each is set for light and dark."),
58
+ panelStep("brand", 3, PHI_THEME_PAGE_WIDGET_IDS.widgetBrandIdentityControls, "The Site's own marks", "The logo, one per mode, and where a click on it leads; the wordmark, the Site's name in type; and the two short lines the Brand shows beside them."),
28
59
  {
29
60
  key: "preview-mode",
30
61
  target: `cms:${PHI_THEME_PAGE_WIDGET_IDS.widgetBrandPreviewModeSwitch}`,
@@ -1,3 +1,3 @@
1
- import { PHI_TOUR_CONTROLLER_KEY, PHI_TOUR_CONTROLLER_PLUGIN_KEY } from "../../../../types/tours";
2
- // The Controller's address is the foundation's (`types/tours.ts`): the Tour button, which is Core's, asks it.
1
+ import { PHI_TOUR_CONTROLLER_KEY, PHI_TOUR_CONTROLLER_PLUGIN_KEY } from "../../../../types/tour-controller";
2
+ // The Controller's address is the foundation's (`types/tour-controller.ts`): the Tour button, which is Core's, asks it.
3
3
  export const PHI_TOUR_CONTROLLER_TYPE = `${PHI_TOUR_CONTROLLER_PLUGIN_KEY}/${PHI_TOUR_CONTROLLER_KEY}`;
@@ -3,16 +3,19 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { PHI_SIGNAL_VALUE_SCHEMAS } from "../../../../types/signals";
5
5
  import { PhiTourControl } from "../../../../components/controls/phi-tour-control";
6
+ import { holdPhiTourViewport, releasePhiTourViewport } from "../../../../components/controls/phi-tour-viewport";
6
7
  import { createPhiRuntimeControllerClient } from "../../../../components/runtime/runtime-controller-client-factory";
7
8
  import { usePhiSignalDispatcher, usePhiSignalListener } from "../../../../components/runtime/runtime-signal-bus";
8
9
  import { usePhiSignalRuntimePartition } from "../../../../components/runtime/runtime-signal-partition";
9
- import { readPhiVisualAnchor, waitForPhiVisualAnchor } from "../../../../components/runtime/runtime-visual-anchors";
10
+ import { readPhiVisualAnchor, waitForPhiVisualAnchor, waitForPhiVisualAnchorToSettle, } from "../../../../components/runtime/runtime-visual-anchors";
10
11
  import { fetchPhiUserState, writePhiUserState } from "../../../../components/runtime/user-state-client";
11
12
  import { PHI_TOUR_PROGRESS_USER_STATE_KEY } from "../ids";
12
13
  import { PHI_TOUR_RUNTIME_CONTROLLER_DEFINITION } from "./definition";
13
14
  import { readPhiTourProgress, recordPhiTourProgress, shouldOfferPhiTour, } from "./progress";
14
15
  /** How long a step waits for its target before its `ifMissing` decides. */
15
16
  const TARGET_WAIT_MS = 4000;
17
+ /** How long a found target may keep moving before it is pointed at where it stands. */
18
+ const TARGET_SETTLE_MS = 1000;
16
19
  const PROGRESS_DESCRIPTOR = {
17
20
  key: PHI_TOUR_PROGRESS_USER_STATE_KEY,
18
21
  shape: "value",
@@ -115,6 +118,31 @@ function PhiTourControllerView({ address, config }) {
115
118
  * never pointed at anything but its own address in this partition.
116
119
  */
117
120
  const step = run ? run.tour.steps[run.index] ?? null : null;
121
+ const send = useCallback((signals) => {
122
+ for (const signal of signals ?? []) {
123
+ dispatchSignal({
124
+ scope: "area",
125
+ channel: signal.channel,
126
+ action: signal.action,
127
+ value: signal.value ?? null,
128
+ valueType: signal.valueType,
129
+ ...(signal.valueSchema ? { valueSchema: signal.valueSchema } : {}),
130
+ sender: address,
131
+ receiver: signal.receiver,
132
+ });
133
+ }
134
+ }, [address, dispatchSignal]);
135
+ /*
136
+ * The step asked for gets its `prepare` before its target is waited for, and its `release` the moment it
137
+ * is no longer the one asked for -- the next step, a step passed over, the Tour closed or finished, the
138
+ * Page left. One effect per step, so every prepare is answered by exactly one release.
139
+ */
140
+ useEffect(() => {
141
+ if (!step)
142
+ return undefined;
143
+ send(step.prepare);
144
+ return () => send(step.release);
145
+ }, [send, step]);
118
146
  // A step centered on purpose has nothing to wait for.
119
147
  const presented = run && step?.target == null ? run.index : run?.presented ?? null;
120
148
  useEffect(() => {
@@ -122,10 +150,16 @@ function PhiTourControllerView({ address, config }) {
122
150
  return undefined;
123
151
  const abort = new AbortController();
124
152
  void waitForPhiVisualAnchor(partition, step.target, { timeoutMs: TARGET_WAIT_MS, signal: abort.signal })
125
- .then((element) => {
153
+ .then(async (element) => {
126
154
  if (abort.signal.aborted)
127
155
  return;
128
156
  if (element) {
157
+ // Pointed at once it stands still: a target a `prepare` just opened is often still moving in.
158
+ await waitForPhiVisualAnchorToSettle(element, { timeoutMs: TARGET_SETTLE_MS, signal: abort.signal });
159
+ if (abort.signal.aborted)
160
+ return;
161
+ // Scrolled to below the sticky bands, once the step is presented (`phi-tour-viewport.ts`).
162
+ holdPhiTourViewport(element);
129
163
  setRun({ ...run, presented: run.index });
130
164
  return;
131
165
  }
@@ -136,6 +170,13 @@ function PhiTourControllerView({ address, config }) {
136
170
  });
137
171
  return () => abort.abort();
138
172
  }, [partition, run, step]);
173
+ // The window's scroll padding is the Tour's only while a Tour runs.
174
+ const running = run != null;
175
+ useEffect(() => {
176
+ if (!running)
177
+ return undefined;
178
+ return releasePhiTourViewport;
179
+ }, [running]);
139
180
  const steps = useMemo(() => (run?.tour.steps ?? []).map((definition) => ({
140
181
  key: definition.key,
141
182
  title: definition.title,
@@ -1,5 +1,6 @@
1
1
  import { PHI_SIGNAL_VALUE_SCHEMAS } from "../../../../types/signals";
2
- import { PHI_PAGE_TOUR_CONFIG_KEY, PHI_PAGE_TOUR_CONTROLLER_FLAG, PHI_TOUR_CONTROLLER_KEY, PHI_TOUR_CONTROLLER_PLUGIN_KEY, readPhiResolvedPageTour, } from "../../../../types/tours";
2
+ import { PHI_TOUR_CONTROLLER_KEY, PHI_TOUR_CONTROLLER_PLUGIN_KEY } from "../../../../types/tour-controller";
3
+ import { PHI_PAGE_TOUR_CONFIG_KEY, PHI_PAGE_TOUR_CONTROLLER_FLAG, readPhiResolvedPageTour, } from "../../../../types/tours";
3
4
  /**
4
5
  * Runs the Tour of whichever Page is shown (design/TOURS.md).
5
6
  *
@@ -320,6 +320,7 @@ export async function resolvePhiCmsRequest({ siteKey, locale, area, path, cookie
320
320
  pageTour: routeTour
321
321
  ? {
322
322
  definition: routeTour,
323
+ ownerModuleId: routeBinding.descriptor.ownerModuleId,
323
324
  sourceLocale: catalog.sourceLocaleByModuleId.get(routeBinding.descriptor.ownerModuleId),
324
325
  }
325
326
  : null,
@@ -1,11 +1,18 @@
1
1
  import "server-only";
2
+ import type { PhiRuntimeModuleId } from "../types/cms-module-descriptors";
2
3
  import type { PhiResolvedPageTour, PhiTourDefinition } from "../types/tours";
3
4
  /**
4
- * A Page's Tour in the viewer's language: every step's words translated from the owning Module's
5
- * language, and the step buttons from Core's label set. Addresses and keys are not words and pass through.
5
+ * A Page's Tour in the viewer's language: every step's words translated, and the step buttons from Core's
6
+ * label set. Addresses, keys and step signals are not words and pass through.
7
+ *
8
+ * Where the words are kept depends on who wrote them. A built-in Tour's text is this package's, the same
9
+ * on every Site, so it is translated once in the global set. A third-party Module's Tour is translated per
10
+ * Site from the language that Module writes in, where the Site's own corrections apply. Either way the
11
+ * Tour's id is the context, so a translator sees which Tour a sentence belongs to.
6
12
  */
7
- export declare function resolvePhiPageTour({ definition, sourceLocale, locale, }: {
13
+ export declare function resolvePhiPageTour({ definition, ownerModuleId, sourceLocale, locale, }: {
8
14
  definition: PhiTourDefinition;
15
+ ownerModuleId: PhiRuntimeModuleId;
9
16
  sourceLocale?: string;
10
17
  locale: string;
11
18
  }): Promise<PhiResolvedPageTour>;
@@ -1,26 +1,39 @@
1
1
  import "server-only";
2
2
  import { getPhiTourWidgetLabels } from "../components/widgets/label-sets/tour";
3
+ import { PHI_SHARED_PACKAGE_NAME } from "../constants/package";
3
4
  import { readPhiServerApiCredentials } from "../helpers/phis-server-credentials";
4
- import { trForLocale } from "./translate";
5
+ import { trBulkForLocale, trGlobalBulkForLocale } from "./translate";
6
+ /** A Tour this package ships, whose words are the same on every Site. */
7
+ function isPhiBuiltInTour(ownerModuleId) {
8
+ return ownerModuleId.startsWith(`${PHI_SHARED_PACKAGE_NAME}/`);
9
+ }
5
10
  /**
6
- * A Page's Tour in the viewer's language: every step's words translated from the owning Module's
7
- * language, and the step buttons from Core's label set. Addresses and keys are not words and pass through.
11
+ * A Page's Tour in the viewer's language: every step's words translated, and the step buttons from Core's
12
+ * label set. Addresses, keys and step signals are not words and pass through.
13
+ *
14
+ * Where the words are kept depends on who wrote them. A built-in Tour's text is this package's, the same
15
+ * on every Site, so it is translated once in the global set. A third-party Module's Tour is translated per
16
+ * Site from the language that Module writes in, where the Site's own corrections apply. Either way the
17
+ * Tour's id is the context, so a translator sees which Tour a sentence belongs to.
8
18
  */
9
- export async function resolvePhiPageTour({ definition, sourceLocale, locale, }) {
10
- const translate = (text) => trForLocale(locale, text, 0, 0, "text", sourceLocale);
19
+ export async function resolvePhiPageTour({ definition, ownerModuleId, sourceLocale, locale, }) {
20
+ const texts = definition.steps.flatMap((step) => [step.title, step.description ?? ""]);
21
+ const translate = isPhiBuiltInTour(ownerModuleId)
22
+ ? trGlobalBulkForLocale(locale, texts, definition.id)
23
+ : trBulkForLocale(locale, texts, definition.id, "text", sourceLocale);
11
24
  const credentials = readPhiServerApiCredentials();
12
- const [labels, steps] = await Promise.all([
25
+ const [labels, translated] = await Promise.all([
13
26
  getPhiTourWidgetLabels({ apiBaseUrl: credentials.apiBaseUrl, internalToken: credentials.internalToken, locale }),
14
- Promise.all(definition.steps.map(async (step) => ({
15
- ...step,
16
- title: await translate(step.title),
17
- ...(step.description ? { description: await translate(step.description) } : {}),
18
- }))),
27
+ translate,
19
28
  ]);
20
29
  return {
21
30
  id: definition.id,
22
31
  version: definition.version,
23
- steps,
32
+ steps: definition.steps.map((step, index) => ({
33
+ ...step,
34
+ title: translated[index * 2] ?? step.title,
35
+ ...(step.description ? { description: translated[index * 2 + 1] ?? step.description } : {}),
36
+ })),
24
37
  labels: { next: labels.next, previous: labels.previous, finish: labels.finish },
25
38
  };
26
39
  }
@@ -1,10 +1,19 @@
1
1
  import "server-only";
2
2
  import { type PhiTranslationParams } from "../helpers/translation-format";
3
3
  import { PHI_TR_CTX_WEB_UI_LABEL, type PhiTranslationFormat } from "../gateway/tr";
4
+ import { type PhiTextSource } from "../helpers/text-source";
4
5
  export { PHI_TR_CTX_WEB_UI_LABEL };
5
6
  export type { PhiTranslationParams } from "../helpers/translation-format";
6
7
  export declare function tr(msg: string, params?: PhiTranslationParams, ctx?: string | number, format?: PhiTranslationFormat): Promise<string>;
7
8
  export declare function trGlobal(msg: string, params?: PhiTranslationParams, ctx?: string | number, format?: PhiTranslationFormat): Promise<string>;
9
+ /**
10
+ * A text Widget's own words in the render's locale, from the store their source names.
11
+ *
12
+ * `textSource: "preset"` is a Preset's sentence and the platform's, so the global store answers it;
13
+ * anything else an author wrote and the Site's store does (`helpers/text-source.ts`). The locale is the
14
+ * render's where there is one: a static render has no request to read it from.
15
+ */
16
+ export declare function trWidgetText(locale: string | undefined, msg: string, textSource: PhiTextSource | undefined): Promise<string>;
8
17
  export declare function trForLocale(localeInput: string, msg: string, params?: PhiTranslationParams, ctx?: string | number, format?: PhiTranslationFormat,
9
18
  /** The language `msg` is written in, when it is not the Site's own -- a Module's text. */
10
19
  sourceLocale?: string): Promise<string>;
@@ -6,6 +6,7 @@ import { PHI_TR_CTX_WEB_UI_LABEL, trBulk as requestInternalTranslationBulk, tr a
6
6
  import { getPhiRequestRuntime } from "./request-runtime";
7
7
  import { resolvePhiRequestLocale } from "./request-locale";
8
8
  import { readPhiServerApiCredentials } from "../helpers/phis-server-credentials";
9
+ import { PHI_TEXT_SOURCE_PRESET } from "../helpers/text-source";
9
10
  export { PHI_TR_CTX_WEB_UI_LABEL };
10
11
  function buildTranslatorOptions(locale, sourceLocale) {
11
12
  const requestRuntime = getPhiRequestRuntime();
@@ -46,6 +47,19 @@ export async function trGlobal(msg, params = 0, ctx = 0, format = "text") {
46
47
  const locale = await resolvePhiRequestLocale();
47
48
  return trGlobalForLocale(locale, msg, params, ctx, format);
48
49
  }
50
+ /**
51
+ * A text Widget's own words in the render's locale, from the store their source names.
52
+ *
53
+ * `textSource: "preset"` is a Preset's sentence and the platform's, so the global store answers it;
54
+ * anything else an author wrote and the Site's store does (`helpers/text-source.ts`). The locale is the
55
+ * render's where there is one: a static render has no request to read it from.
56
+ */
57
+ export async function trWidgetText(locale, msg, textSource) {
58
+ if (textSource === PHI_TEXT_SOURCE_PRESET) {
59
+ return locale ? trGlobalForLocale(locale, msg) : trGlobal(msg);
60
+ }
61
+ return locale ? trForLocale(locale, msg) : tr(msg);
62
+ }
49
63
  /**
50
64
  * Trimmed, one per message and in order: an empty message stays in its place, and `trBulk` answers it
51
65
  * with an empty text, so every answer lines up with the message it belongs to.
@@ -492,10 +492,24 @@ h5.phi-typography, .phi-typography h5 {
492
492
  * as a Table closed with one instead of with a footer. Clipping the Table is what both cases want, which
493
493
  * is why the rule names neither.
494
494
  */
495
+ /*
496
+ * A sticky header row sticks under the Shell's Header, which publishes where it ends right now
497
+ * (`components/shell/use-phi-shell-sticky-top.ts`), and at the top edge of an Overlay's body,
498
+ * which scrolls on its own. Ant Design writes the offset inline, so the position is stated over it.
499
+ */
500
+ .phi-table-control.phi-table-control--sticky-header [class~="ant-table-sticky-holder"] {
501
+ top: var(--phi-table-sticky-top, var(--phi-shell-sticky-top, 0px)) !important;
502
+ }
503
+
504
+ :is([data-phi-modal-body-content], [data-phi-drawer-body-content]) {
505
+ --phi-table-sticky-top: 0px;
506
+ }
507
+
495
508
  .phi-table-control.phi-table-control--closing-block [class~="ant-table"] {
496
509
  border-end-start-radius: var(--phi-surface-radius, var(--ant-border-radius-lg));
497
510
  border-end-end-radius: var(--phi-surface-radius, var(--ant-border-radius-lg));
498
- overflow: hidden;
511
+ /* `clip`, not `hidden`, so the corners are cut without making this the sticky header's scroller. */
512
+ overflow: clip;
499
513
  }
500
514
 
501
515
  /*
package/styles/root.css CHANGED
@@ -114,29 +114,30 @@ samp {
114
114
  animation-timing-function: var(--phi-effects-animation-easing, ease-out);
115
115
  }
116
116
 
117
- :where([data-phi-renderable-block="true"][data-phi-effects-transition="composite"][data-phi-effects-trigger="on_visible"]) {
118
- animation-composition: var(--phi-effects-animation-composition, replace);
119
- animation-delay: var(--phi-effects-animation-delay, 0ms);
120
- animation-duration: var(--phi-effects-animation-duration, 1000ms);
121
- animation-name: var(--phi-effects-animation-name);
122
- animation-play-state: paused;
123
- animation-timing-function: var(--phi-effects-animation-easing, ease-out);
124
- }
125
-
126
- :where([data-phi-renderable-block="true"][data-phi-effects-transition="composite"][data-phi-effects-trigger="on_visible"][data-phi-effects-state="running"]) {
127
- animation-play-state: running;
128
- }
129
-
130
117
  /*
131
- * On ready, the whole rule inside the query that asks whether anything can release it.
118
+ * On visible and on ready, the whole rule inside the query that asks whether anything can release it.
132
119
  *
133
- * The animation is held at its first frame until the page has finished arriving, so a Widget whose
134
- * content the server streams in a segment of its own can land, and everything below it can settle,
135
- * behind an entrance that has not started. Only scripting sets the state that releases it; where there
136
- * is none the Block would stand at that first frame for good, which for a fade in means invisible. So
137
- * without scripting there is no animation at all and the Block is simply there.
120
+ * The animation is held at its first frame -- until the Block is scrolled into view, or until the page
121
+ * has finished arriving, so a Widget whose content the server streams in a segment of its own can land,
122
+ * and everything below it can settle, behind an entrance that has not started. Only scripting sets the
123
+ * state that releases it; where there is none the Block would stand at that first frame for good, which
124
+ * for a fade in means invisible. So without scripting there is no animation at all and the Block is
125
+ * simply there.
138
126
  */
139
127
  @media (scripting: enabled) {
128
+ :where([data-phi-renderable-block="true"][data-phi-effects-transition="composite"][data-phi-effects-trigger="on_visible"]) {
129
+ animation-composition: var(--phi-effects-animation-composition, replace);
130
+ animation-delay: var(--phi-effects-animation-delay, 0ms);
131
+ animation-duration: var(--phi-effects-animation-duration, 1000ms);
132
+ animation-name: var(--phi-effects-animation-name);
133
+ animation-play-state: paused;
134
+ animation-timing-function: var(--phi-effects-animation-easing, ease-out);
135
+ }
136
+
137
+ :where([data-phi-renderable-block="true"][data-phi-effects-transition="composite"][data-phi-effects-trigger="on_visible"][data-phi-effects-state="running"]) {
138
+ animation-play-state: running;
139
+ }
140
+
140
141
  :where([data-phi-renderable-block="true"][data-phi-effects-transition="composite"][data-phi-effects-trigger="on_ready"]) {
141
142
  animation-composition: var(--phi-effects-animation-composition, replace);
142
143
  animation-delay: var(--phi-effects-animation-delay, 0ms);
@@ -6,6 +6,7 @@ import { type PhiCssLength } from "./length";
6
6
  import type { PhiResponsiveValue } from "./responsive";
7
7
  import type { PhiRenderableBlockBase, PhiRenderableBlockAnchor, PhiRenderableBlockResponsiveSize } from "./renderable-block";
8
8
  import type { PhiBaseLayoutSlotStates } from "../components/layouts/phi-layout-contract";
9
+ import { type PhiSpacingScaleKey } from "../components/widgets/config/spacing-options";
9
10
  export type PhiCmsPluginConfigBase = Record<string, unknown>;
10
11
  export type PhiCmsRenderableBlockConfigBase = PhiCmsPluginConfigBase & PhiRenderableBlockBase;
11
12
  export type PhiCmsLayerBase = PhiCmsRenderableBlockConfigBase & {
@@ -120,7 +121,8 @@ export type PhiCmsCarouselLayoutConfig = PhiCmsLayerBase & {
120
121
  transition?: PhiSequenceTransition;
121
122
  transitionDurationMs?: number;
122
123
  transitionEasing?: PhiMotionEasing;
123
- slotGap?: string;
124
+ /** A step of the spacing scale between the slots of a window. */
125
+ slotGap?: PhiSpacingScaleKey;
124
126
  /** Which of its own controls the Carousel draws. Pager Widgets work regardless of this. */
125
127
  controls?: "none" | "arrows" | "dots" | "both";
126
128
  loop?: boolean;
@@ -9,6 +9,7 @@ import { applyPhiLayoutDefaults } from "../helpers/cms-layout-defaults";
9
9
  import { resolvePhiLayoutDefaults } from "../helpers/cms-layout-defaults";
10
10
  import { normalizeRenderableBlockAnchor } from "../helpers/renderable-block-anchor";
11
11
  import { isPhiRecord } from "../helpers/is-record";
12
+ import { PHI_SPACING_SCALE_KEYS } from "../components/widgets/config/spacing-options";
12
13
  import { PHI_GRID_COLUMN_COUNTS, isPhiGridColumnCount } from "../constants/grid-columns";
13
14
  function readInitialSlotStates(value) {
14
15
  if (Array.isArray(value)) {
@@ -334,6 +335,7 @@ export function parsePhiCmsCarouselLayoutConfig(config) {
334
335
  const loop = readCarouselFlag(config.loop, "loop");
335
336
  const autoplayMs = readCarouselCount(config.autoplayMs, "autoplayMs", 0);
336
337
  const lookahead = readCarouselCount(config.lookahead, "lookahead", 0);
338
+ const slotGap = readCarouselMember(config.slotGap, PHI_SPACING_SCALE_KEYS, "slotGap");
337
339
  return applyPhiLayoutDefaults({
338
340
  ...readRenderableBlockConfig(config),
339
341
  labelEnd: readPhiLayoutLabelEnd(config.labelEnd),
@@ -342,15 +344,13 @@ export function parsePhiCmsCarouselLayoutConfig(config) {
342
344
  ...(visibleSlots === undefined ? {} : { visibleSlots }),
343
345
  ...(windowAnchor === undefined ? {} : { windowAnchor }),
344
346
  ...(transition === undefined ? {} : { transition }),
345
- // Absent means "whatever the theme does", which is why nothing is written here rather than a
346
- // number this parser invented.
347
347
  ...(config.transitionDurationMs === undefined || config.transitionDurationMs === null ? {} : {
348
348
  transitionDurationMs: clampPhiSequenceTransitionMs(config.transitionDurationMs),
349
349
  }),
350
350
  ...(config.transitionEasing === undefined ? {} : {
351
351
  transitionEasing: readPhiMotionEasing(config.transitionEasing, undefined),
352
352
  }),
353
- slotGap: readString(config.slotGap),
353
+ ...(slotGap === undefined ? {} : { slotGap }),
354
354
  ...(controls === undefined ? {} : { controls }),
355
355
  ...(loop === undefined ? {} : { loop }),
356
356
  // Zero is how "does not move on its own" is written, so it is the one number below the floor
@@ -93,7 +93,12 @@ export type PhiCmsConfigFieldChoicePresentation = "select" | "segmented" | "tabs
93
93
  */
94
94
  export type PhiCmsConfigFieldCollectionPresentation = "inline" | "overlay" | "select";
95
95
  export type PhiCmsConfigFieldChoiceMode = "single" | "multiple";
96
- export type PhiCmsConfigFieldChoiceValueType = "string" | "string[]";
96
+ /**
97
+ * What a `choice` stores. A text or a list of texts is the rule; `number` is a closed list of numbers --
98
+ * a key that is a number everywhere else, in the parser, the URL and the contracts, as the Image's
99
+ * Variant is -- and is stored as that number rather than spelled as a text the reader has to convert.
100
+ */
101
+ export type PhiCmsConfigFieldChoiceValueType = "string" | "string[]" | "number";
97
102
  export type PhiCmsConfigFieldChoiceCreateBehavior = "none" | "accept-custom" | "create-record";
98
103
  export type PhiCmsConfigFieldColorMode = "single" | "gradient" | "both";
99
104
  export type PhiCmsConfigFieldValueStorage = "field" | "self";
@@ -135,6 +140,12 @@ export type PhiCmsConfigField = (PhiCmsConfigFieldBase & {
135
140
  }) | (PhiCmsConfigFieldBase & {
136
141
  type: "data-provider";
137
142
  providerKind?: PhiRuntimeDataProviderKind;
143
+ /**
144
+ * The one record Provider this binding names, for a Widget that can only read one: a Gallery
145
+ * reads a Folder, an entry header an entry. The Inspector then offers no choice of Provider -- a list
146
+ * that holds a single right answer and several wrong ones -- and asks only for its parameters.
147
+ */
148
+ providerKey?: PhiRuntimeDataProviderKey;
138
149
  }) | (PhiCmsConfigFieldBase & {
139
150
  type: "calendar-adapter";
140
151
  }) | (PhiCmsConfigFieldBase & {
@@ -235,10 +246,25 @@ export type PhiCmsConfigField = (PhiCmsConfigFieldBase & {
235
246
  */
236
247
  presentation?: "slider";
237
248
  suffix?: string;
249
+ }) | (PhiCmsConfigFieldBase & {
250
+ type: "choice";
251
+ valueType: "number";
252
+ options: PhiControlOption<number>[];
253
+ emptyOption?: PhiControlOption;
254
+ emptyValue?: null | undefined;
255
+ placeholder?: string;
256
+ mode?: never;
257
+ presentation?: never;
258
+ optionsProvider?: never;
259
+ allowCustom?: never;
260
+ createBehavior?: never;
261
+ filter?: never;
262
+ patchOnChange?: never;
263
+ optionPresets?: never;
238
264
  }) | (PhiCmsConfigFieldBase & {
239
265
  type: "choice";
240
266
  mode?: PhiCmsConfigFieldChoiceMode;
241
- valueType?: PhiCmsConfigFieldChoiceValueType;
267
+ valueType?: Exclude<PhiCmsConfigFieldChoiceValueType, "number">;
242
268
  presentation?: PhiCmsConfigFieldChoicePresentation;
243
269
  options?: PhiControlOption[];
244
270
  optionsProvider?: PhiControlOptionsProviderConfig;
@@ -635,11 +661,12 @@ export type PhiResolvedCmsRequest = {
635
661
  runtime: PhiBlockRuntime;
636
662
  serverCapabilities: import("./server-capabilities").PhiCapabilitySnapshot | null;
637
663
  /**
638
- * The Tour the Page's route carries, untranslated, with the language its owning Module writes in.
639
- * Translated only where the Area has a Controller to hand it to (types/tours.ts).
664
+ * The Tour the Page's route carries, untranslated, with the Module that owns it and the language that
665
+ * Module writes in. Translated only where the Area has a Controller to hand it to (types/tours.ts).
640
666
  */
641
667
  pageTour?: {
642
668
  definition: import("./tours").PhiTourDefinition;
669
+ ownerModuleId: PhiRuntimeModuleId;
643
670
  sourceLocale?: string;
644
671
  } | null;
645
672
  };
@@ -1,5 +1,6 @@
1
1
  import type { PhiBulletListEntry, PhiBulletListStyle } from "../components/widgets/shared/bullet-list-vocabulary";
2
2
  import type { PhiRenderableBlockBase } from "./renderable-block";
3
+ import type { PhiTextSource } from "../helpers/text-source";
3
4
  import type { PhiRuntimeConditionExpression } from "./runtime-condition";
4
5
  import type { PhiSignalRoute, PhiSignalValue } from "./signals";
5
6
  import type { PhiCssLength } from "./length";
@@ -71,6 +72,8 @@ export type PhiSimpleTextWidgetPlacement = PhiWidgetPlacementBase & {
71
72
  tone?: PhiTextTone;
72
73
  marks?: PhiSimpleTextMark[];
73
74
  disabled?: boolean;
75
+ /** `"preset"` while the words are a Preset's (`helpers/text-source.ts`). */
76
+ textSource?: PhiTextSource;
74
77
  };
75
78
  export type PhiDescriptionWidgetPlacement = PhiWidgetPlacementBase & {
76
79
  eyebrow?: string;
@@ -81,6 +84,8 @@ export type PhiDescriptionWidgetPlacement = PhiWidgetPlacementBase & {
81
84
  asideList?: PhiBulletListStyle;
82
85
  footer?: string;
83
86
  tone?: PhiTextTone;
87
+ /** `"preset"` while the words are a Preset's (`helpers/text-source.ts`). */
88
+ textSource?: PhiTextSource;
84
89
  };
85
90
  export type PhiButtonWidgetPlacement = PhiWidgetPlacementBase & PhiControlPlacement & PhiControlBadgeConfig & {
86
91
  action?: PhiCommonControlActionKey;
@@ -306,6 +306,8 @@ export type PhiTableWidgetPresentation = PhiControlPresentationConfig & {
306
306
  borders?: boolean;
307
307
  /** The header row; shown unless this says `false`. */
308
308
  header?: boolean;
309
+ /** Whether the header row stays at the top while the rows scroll under it; on unless this says `false`. */
310
+ stickyHeader?: boolean;
309
311
  footer?: PhiTableFooterConfig;
310
312
  summary?: PhiTableSummaryConfig;
311
313
  row?: {
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Where a Tour is asked for: the Tour Controller of the Area, which the Tour Module mounts.
3
+ *
4
+ * Here, in the foundation, rather than in that Module: the Tour button is Core's, so any Area -- and any
5
+ * Module's own Page -- can place it without depending on the Tour Module. With the Module off nobody
6
+ * answers at this address, and the button, never told that a Tour is there, stays hidden.
7
+ */
8
+ export declare const PHI_TOUR_CONTROLLER_PLUGIN_KEY = "@phis/ui/modules/tour/controller";
9
+ export declare const PHI_TOUR_CONTROLLER_KEY = "default";
10
+ export declare const PHI_TOUR_CONTROLLER_ADDRESS: `controller:${string}/${string}:${string}`;