@phis/ui 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/components/cms/phi-cms-layout-renderer.js +4 -3
  2. package/components/cms/phi-cms-page-render-scope.d.ts +4 -5
  3. package/components/cms/phi-cms-page-render-scope.js +43 -22
  4. package/components/cms/phi-cms-render-diagnostic-client.js +7 -3
  5. package/components/cms/phi-cms-render-error-boundary.js +2 -1
  6. package/components/controls/phi-bullet-list-control.js +31 -2
  7. package/components/controls/phi-ribbon-control.d.ts +29 -0
  8. package/components/controls/phi-ribbon-control.js +53 -0
  9. package/components/controls/phi-table-control.d.ts +3 -0
  10. package/components/controls/phi-table-control.js +13 -1
  11. package/components/controls/phi-tree-control.js +3 -3
  12. package/components/forms/shared-form-provider-registry.js +1 -4
  13. package/components/media/asset-collection-runtime.js +6 -16
  14. package/components/media/media-font-metrics.d.ts +3 -0
  15. package/components/media/media-font-metrics.js +43 -0
  16. package/components/media/phi-area-upload-widget.js +53 -97
  17. package/components/media/phi-image-preview-data.d.ts +13 -0
  18. package/components/media/phi-image-preview-data.js +184 -9
  19. package/components/media/phi-media-picker-binding.js +16 -13
  20. package/components/media/phi-media-upload.d.ts +15 -8
  21. package/components/media/phi-media-upload.js +18 -9
  22. package/components/media/phi-site-font-assets.js +7 -8
  23. package/components/overlays/phi-overlay-container-client.js +16 -1
  24. package/components/regions/presets/phi-settings-page-shell-tree.js +48 -1
  25. package/components/runtime/runtime-controller-materialization.js +12 -1
  26. package/components/state/scoped-state-store.js +20 -3
  27. package/components/tables/client/phi-table-binding.d.ts +2 -1
  28. package/components/tables/client/phi-table-binding.js +45 -21
  29. package/components/trees/client/phi-tree-binding.js +19 -11
  30. package/components/widgets/client/bullet-list-entries-editor.d.ts +26 -0
  31. package/components/widgets/client/bullet-list-entries-editor.js +103 -0
  32. package/components/widgets/config/choice-shared.js +12 -13
  33. package/components/widgets/config/command-buttons.d.ts +5 -1
  34. package/components/widgets/config/command-buttons.js +38 -81
  35. package/components/widgets/config/control-signal-config.d.ts +3 -1
  36. package/components/widgets/config/control-signal-config.js +19 -17
  37. package/components/widgets/config/image-source-parser.d.ts +1 -0
  38. package/components/widgets/config/image-source-parser.js +7 -8
  39. package/components/widgets/config/mask.d.ts +6 -1
  40. package/components/widgets/config/mask.js +24 -1
  41. package/components/widgets/config/parser-primitives.js +72 -0
  42. package/components/widgets/config/stack-tabs.d.ts +1 -1
  43. package/components/widgets/config/stack-tabs.js +2 -3
  44. package/components/widgets/config/strict-config-reader.d.ts +40 -0
  45. package/components/widgets/config/strict-config-reader.js +174 -0
  46. package/components/widgets/label-sets/effects.js +2 -0
  47. package/components/widgets/label-types/effects.d.ts +1 -0
  48. package/components/widgets/label-types/effects.js +1 -0
  49. package/components/widgets/navigation-descriptor-resolver.server.js +20 -8
  50. package/components/widgets/shared/bullet-list-vocabulary.d.ts +17 -1
  51. package/components/widgets/shared/bullet-list-vocabulary.js +19 -2
  52. package/components/widgets/shared/card-body-client.d.ts +16 -4
  53. package/components/widgets/shared/card-body-client.js +81 -24
  54. package/components/widgets/shared/card-vocabulary.d.ts +21 -4
  55. package/components/widgets/shared/card-vocabulary.js +28 -2
  56. package/components/widgets/shared/markdown-body-client.d.ts +2 -1
  57. package/components/widgets/shared/markdown-body-client.js +3 -1
  58. package/components/widgets/shared/markdown-render.js +8 -1
  59. package/components/widgets/signals/control-signal-capabilities.d.ts +0 -18
  60. package/components/widgets/signals/control-signal-capabilities.js +10 -22
  61. package/components/workspace/catalog-store.d.ts +4 -3
  62. package/components/workspace/catalog-store.js +6 -5
  63. package/gateway/dashboard-cards-route.js +1 -0
  64. package/gateway/internal-references.d.ts +6 -1
  65. package/gateway/internal-references.js +48 -23
  66. package/gateway/site-cms-write-validation.d.ts +9 -0
  67. package/gateway/site-cms-write-validation.js +78 -0
  68. package/gateway/site-proxy.js +19 -3
  69. package/helpers/renderable-block-serialization.js +1 -1
  70. package/next/config.d.mts +3 -0
  71. package/next/config.mjs +28 -1
  72. package/package.json +2 -2
  73. package/plugins/runtime/phi-slot-child-viewport-effects-observer.js +5 -1
  74. package/plugins/runtime-modules/accounting/presets.d.ts +9 -0
  75. package/plugins/runtime-modules/accounting/presets.js +37 -0
  76. package/plugins/runtime-modules/accounting/server.d.ts +1 -4
  77. package/plugins/runtime-modules/accounting/server.js +4 -7
  78. package/plugins/runtime-modules/app/presets.d.ts +18 -0
  79. package/plugins/runtime-modules/app/presets.js +67 -0
  80. package/plugins/runtime-modules/app/server.d.ts +1 -4
  81. package/plugins/runtime-modules/app/server.js +4 -7
  82. package/plugins/runtime-modules/asset/controller/runtime.js +2 -2
  83. package/plugins/runtime-modules/asset/services/collection.js +9 -1
  84. package/plugins/runtime-modules/asset/services/options.js +2 -2
  85. package/plugins/runtime-modules/builder/area-settings-answer.js +2 -1
  86. package/plugins/runtime-modules/builder/authoring-canvas.js +6 -6
  87. package/plugins/runtime-modules/builder/cards.js +3 -5
  88. package/plugins/runtime-modules/builder/clients/builder-region-overrides.js +3 -3
  89. package/plugins/runtime-modules/builder/clients/canvas-widget.js +15 -54
  90. package/plugins/runtime-modules/builder/clients/inspector-config-field.d.ts +4 -1
  91. package/plugins/runtime-modules/builder/clients/inspector-config-field.js +5 -34
  92. package/plugins/runtime-modules/builder/clients/inspector-link-target-field.js +1 -1
  93. package/plugins/runtime-modules/builder/clients/inspector-section-widget.js +8 -8
  94. package/plugins/runtime-modules/builder/clients/inspector-title.js +6 -6
  95. package/plugins/runtime-modules/builder/clients/layout-inspector.d.ts +1 -1
  96. package/plugins/runtime-modules/builder/clients/layout-inspector.js +11 -25
  97. package/plugins/runtime-modules/builder/clients/mode-switch.js +1 -1
  98. package/plugins/runtime-modules/builder/clients/pages-header.js +11 -11
  99. package/plugins/runtime-modules/builder/clients/pages-workspace.js +1 -1
  100. package/plugins/runtime-modules/builder/clients/region-visibility-fields.js +1 -1
  101. package/plugins/runtime-modules/builder/clients/runtime-module-authoring-boundary.js +1 -1
  102. package/plugins/runtime-modules/builder/clients/shells-workspace.js +15 -31
  103. package/plugins/runtime-modules/builder/clients/structure-canvas.js +1 -1
  104. package/plugins/runtime-modules/builder/clients/use-region-draft-hydration.d.ts +26 -0
  105. package/plugins/runtime-modules/builder/clients/use-region-draft-hydration.js +36 -0
  106. package/plugins/runtime-modules/builder/clients/widget-effects-editor.js +1 -1
  107. package/plugins/runtime-modules/builder/clients/widget-inspector.js +1 -0
  108. package/plugins/runtime-modules/builder/clients/workspace-scope-boundary.js +2 -2
  109. package/plugins/runtime-modules/builder/cms-instance-allocator.js +8 -28
  110. package/plugins/runtime-modules/builder/controller/client.d.ts +0 -15
  111. package/plugins/runtime-modules/builder/controller/definition.d.ts +0 -15
  112. package/plugins/runtime-modules/builder/controller/definition.js +0 -8
  113. package/plugins/runtime-modules/builder/controller/draft-command-controller.d.ts +1 -2
  114. package/plugins/runtime-modules/builder/controller/draft-command-controller.js +11 -30
  115. package/plugins/runtime-modules/builder/controller/draft-status-controller.js +15 -2
  116. package/plugins/runtime-modules/builder/controller/inspector-controller.d.ts +1 -2
  117. package/plugins/runtime-modules/builder/controller/inspector-controller.js +4 -7
  118. package/plugins/runtime-modules/builder/controller/page-controller.d.ts +1 -2
  119. package/plugins/runtime-modules/builder/controller/page-controller.js +16 -68
  120. package/plugins/runtime-modules/builder/controller/server-definition.d.ts +0 -15
  121. package/plugins/runtime-modules/builder/controller/signal-wiring-controller.d.ts +2 -5
  122. package/plugins/runtime-modules/builder/controller/signal-wiring-controller.js +6 -50
  123. package/plugins/runtime-modules/builder/controller/workspace-controller.d.ts +2 -3
  124. package/plugins/runtime-modules/builder/controller/workspace-controller.js +89 -153
  125. package/plugins/runtime-modules/builder/data-providers.d.ts +1 -0
  126. package/plugins/runtime-modules/builder/data-providers.js +1 -0
  127. package/plugins/runtime-modules/builder/definition.d.ts +1 -0
  128. package/plugins/runtime-modules/builder/developer-workspace-store.d.ts +64 -30
  129. package/plugins/runtime-modules/builder/developer-workspace-store.js +255 -61
  130. package/plugins/runtime-modules/builder/edit-scaffold-drawer.js +1 -1
  131. package/plugins/runtime-modules/builder/history.d.ts +3 -1
  132. package/plugins/runtime-modules/builder/options-providers.js +3 -2
  133. package/plugins/runtime-modules/builder/page-catalog-client.js +19 -7
  134. package/plugins/runtime-modules/builder/page-meta-form.js +1 -0
  135. package/plugins/runtime-modules/builder/page-references-provider.js +1 -1
  136. package/plugins/runtime-modules/builder/persistence.js +33 -69
  137. package/plugins/runtime-modules/builder/public-route-collisions.d.ts +2 -2
  138. package/plugins/runtime-modules/builder/public-route-collisions.js +6 -19
  139. package/plugins/runtime-modules/builder/render-root-node-preview.server.js +20 -3
  140. package/plugins/runtime-modules/builder/render-root-node-scaffold.js +3 -3
  141. package/plugins/runtime-modules/builder/runtime-module-selection.d.ts +2 -12
  142. package/plugins/runtime-modules/builder/runtime-module-selection.js +16 -42
  143. package/plugins/runtime-modules/builder/services/navigation-table.js +9 -15
  144. package/plugins/runtime-modules/builder/services/page-source-tree.js +8 -8
  145. package/plugins/runtime-modules/builder/services/runtime-modules-table.js +10 -10
  146. package/plugins/runtime-modules/builder/services/signal-routes-table.js +3 -3
  147. package/plugins/runtime-modules/builder/signal-route-selection.d.ts +1 -2
  148. package/plugins/runtime-modules/builder/signal-route-selection.js +2 -2
  149. package/plugins/runtime-modules/builder/signal-wiring-options.js +3 -2
  150. package/plugins/runtime-modules/builder/structure-dnd.js +8 -61
  151. package/plugins/runtime-modules/builder/trees/phi-default-builder-area-preset-tree.js +13 -10
  152. package/plugins/runtime-modules/builder/widgets/structure-region/built-in.js +12 -18
  153. package/plugins/runtime-modules/builder/workspace-catalog-actions.d.ts +55 -0
  154. package/plugins/runtime-modules/builder/workspace-catalog-actions.js +142 -0
  155. package/plugins/runtime-modules/client-manifests/common.js +1 -1
  156. package/plugins/runtime-modules/{runtime-calendar-adapters.js → core/calendar-adapters.js} +2 -2
  157. package/plugins/runtime-modules/{client-calendar-adapters/core.js → core/client-calendar-adapters.js} +1 -1
  158. package/plugins/runtime-modules/core/content-entries/table.js +1 -1
  159. package/plugins/runtime-modules/core/data-providers.d.ts +3 -0
  160. package/plugins/runtime-modules/core/data-providers.js +2 -1
  161. package/plugins/runtime-modules/core/definition.d.ts +3 -0
  162. package/plugins/runtime-modules/core/definition.js +1 -1
  163. package/plugins/runtime-modules/core/messaging/data-providers.d.ts +1 -0
  164. package/plugins/runtime-modules/core/messaging/data-providers.js +1 -0
  165. package/plugins/runtime-modules/core/messaging/thread-inbox-table.js +1 -1
  166. package/plugins/runtime-modules/core/server.js +1 -1
  167. package/plugins/runtime-modules/core/themes.d.ts +44 -0
  168. package/plugins/runtime-modules/core/themes.js +12 -0
  169. package/plugins/runtime-modules/core/widgets/card/collection-view-binding.js +25 -9
  170. package/plugins/runtime-modules/core/widgets/card/config.d.ts +296 -254
  171. package/plugins/runtime-modules/core/widgets/card/config.js +52 -3
  172. package/plugins/runtime-modules/core/widgets/card/editor.js +30 -3
  173. package/plugins/runtime-modules/core/widgets/card/server.d.ts +3 -0
  174. package/plugins/runtime-modules/core/widgets/card/server.js +31 -3
  175. package/plugins/runtime-modules/core/widgets/collection-view/asset-binding.js +7 -6
  176. package/plugins/runtime-modules/core/widgets/collection-view/config.js +9 -2
  177. package/plugins/runtime-modules/core/widgets/description/config.d.ts +1 -25
  178. package/plugins/runtime-modules/core/widgets/description/editor.js +4 -61
  179. package/plugins/runtime-modules/core/widgets/entry-list/config.d.ts +1 -25
  180. package/plugins/runtime-modules/core/widgets/gallery/config.d.ts +1 -25
  181. package/plugins/runtime-modules/core/widgets/image/config.js +2 -2
  182. package/plugins/runtime-modules/core/widgets/markdown/config.d.ts +3 -1
  183. package/plugins/runtime-modules/core/widgets/markdown/config.js +14 -0
  184. package/plugins/runtime-modules/core/widgets/markdown/editor.js +2 -3
  185. package/plugins/runtime-modules/core/widgets/result/config.d.ts +4 -25
  186. package/plugins/runtime-modules/core/widgets/result/config.js +2 -0
  187. package/plugins/runtime-modules/core/widgets/result/server.js +25 -19
  188. package/plugins/runtime-modules/core/widgets/simple-text/config.d.ts +1 -25
  189. package/plugins/runtime-modules/core/widgets/slot-upload/client.js +11 -2
  190. package/plugins/runtime-modules/core/widgets/table/client.js +8 -4
  191. package/plugins/runtime-modules/core/widgets/table/config.d.ts +3 -83
  192. package/plugins/runtime-modules/core/widgets/table/config.js +302 -452
  193. package/plugins/runtime-modules/core/widgets/tree/config.js +5 -2
  194. package/plugins/runtime-modules/dashboard/card-rows.d.ts +5 -0
  195. package/plugins/runtime-modules/dashboard/card-rows.js +59 -0
  196. package/plugins/runtime-modules/dashboard/clients/dashboard-card-view.js +4 -6
  197. package/plugins/runtime-modules/dashboard/services/collection.js +17 -7
  198. package/plugins/runtime-modules/groups/data-providers.d.ts +2 -0
  199. package/plugins/runtime-modules/groups/data-providers.js +3 -3
  200. package/plugins/runtime-modules/groups/definition.d.ts +2 -0
  201. package/plugins/runtime-modules/localization/data-providers.d.ts +3 -0
  202. package/plugins/runtime-modules/localization/data-providers.js +2 -0
  203. package/plugins/runtime-modules/localization/definition.d.ts +3 -0
  204. package/plugins/runtime-modules/localization/services/table.js +3 -7
  205. package/plugins/runtime-modules/observability/data-providers.d.ts +1 -0
  206. package/plugins/runtime-modules/observability/data-providers.js +1 -0
  207. package/plugins/runtime-modules/observability/definition.d.ts +1 -0
  208. package/plugins/runtime-modules/observability/module.d.ts +1 -0
  209. package/plugins/runtime-modules/observability/services/table.js +1 -1
  210. package/plugins/runtime-modules/phi-default-site-area-preset-tree.js +2 -105
  211. package/plugins/runtime-modules/public/presets.d.ts +115 -0
  212. package/plugins/runtime-modules/{area-base-presets.js → public/presets.js} +19 -119
  213. package/plugins/runtime-modules/public/server.d.ts +1 -4
  214. package/plugins/runtime-modules/public/server.js +4 -7
  215. package/plugins/runtime-modules/public/trees/phi-default-pub-error-page-tree.js +7 -8
  216. package/plugins/runtime-modules/public-base-page-references.d.ts +1 -1
  217. package/plugins/runtime-modules/public-base-page-references.js +1 -1
  218. package/plugins/runtime-modules/revisions/controller/client.js +11 -11
  219. package/plugins/runtime-modules/revisions/services/table.js +3 -3
  220. package/plugins/runtime-modules/theme/trees/phi-default-builder-theme-page-tree.js +22 -0
  221. package/plugins/runtime-modules/theme/widgets/brand-controls/client.d.ts +2 -1
  222. package/plugins/runtime-modules/theme/widgets/brand-controls/client.js +93 -58
  223. package/plugins/runtime-modules/theme/widgets/brand-controls/config.d.ts +2 -1
  224. package/plugins/runtime-modules/theme/widgets/brand-controls/config.js +23 -22
  225. package/plugins/runtime-modules/theme/widgets/brand-controls/plugin.js +1 -1
  226. package/plugins/runtime-modules/theme-descriptors.js +1 -1
  227. package/plugins/runtime-modules/user-management/data-providers.d.ts +2 -0
  228. package/plugins/runtime-modules/user-management/data-providers.js +1 -0
  229. package/plugins/runtime-modules/user-management/definition.d.ts +2 -0
  230. package/styles/control-shape.css +11 -11
  231. package/styles/controls.css +29 -3
  232. package/styles/layout.css +16 -7
  233. package/types/cms-config.js +142 -185
  234. package/types/cms-plugins.d.ts +4 -11
  235. package/types/core-widget-placements.d.ts +17 -3
  236. package/types/dashboard-cards.d.ts +9 -0
  237. package/types/media.d.ts +0 -3
  238. package/types/renderable-block.d.ts +6 -1
  239. package/types/table-widget.d.ts +9 -0
  240. package/types/table-widget.js +20 -0
  241. package/types.d.ts +1 -1
  242. package/widget-config.d.ts +2 -0
  243. package/widget-config.js +6 -0
  244. package/plugins/runtime-modules/area-base-presets.d.ts +0 -139
  245. /package/plugins/runtime-modules/{runtime-calendar-adapters.d.ts → core/calendar-adapters.d.ts} +0 -0
  246. /package/plugins/runtime-modules/{client-calendar-adapters/core.d.ts → core/client-calendar-adapters.d.ts} +0 -0
package/styles/layout.css CHANGED
@@ -561,6 +561,11 @@
561
561
  /*
562
562
  * Hover effects: a card answers the pointer, and only a pointer that hovers.
563
563
  * Keyboard focus gets the same answer, so the effect never says something a keyboard reader misses.
564
+ *
565
+ * Lift raises the card and gives it the Theme's next shadow up. The resting shadow is the Surface's,
566
+ * written on the card itself, so the raised one has to be `!important` to stand over it; a card whose
567
+ * Surface has no shadow gets one when it lifts, which is what lifting means. Two pixels and the same
568
+ * shadow read as nothing at all; zoom at 4 % did not read either.
564
569
  */
565
570
  :where(.phi-card--hover-lift, .phi-card--hover-zoom) {
566
571
  transition: translate 200ms ease, box-shadow 200ms ease;
@@ -573,25 +578,29 @@
573
578
 
574
579
  @media (hover: hover) {
575
580
  :where(.phi-card--hover-lift):hover {
576
- translate: 0 -2px;
581
+ translate: 0 -4px;
582
+ box-shadow: var(--ant-box-shadow-secondary) !important;
577
583
  }
578
584
 
579
585
  :where(.phi-card--hover-zoom):hover :where(.phi-card__ground, .phi-card__media img) {
580
- scale: 1.04;
586
+ scale: 1.08;
581
587
  }
582
588
  }
583
589
 
584
590
  :where(.phi-card--hover-lift):focus-within {
585
- translate: 0 -2px;
591
+ translate: 0 -4px;
592
+ box-shadow: var(--ant-box-shadow-secondary) !important;
586
593
  }
587
594
 
588
595
  :where(.phi-card--hover-zoom):focus-within :where(.phi-card__ground, .phi-card__media img) {
589
- scale: 1.04;
596
+ scale: 1.08;
590
597
  }
591
598
 
592
599
  /*
593
600
  * The icon paints the colours the card hands it as custom properties, so that the `icon` effect can turn
594
- * them round: the colour becomes the ground and the ground the colour. A change of colour, not a
601
+ * them round: the colour becomes the ground, and the mark takes `--phi-card-icon-hover-color` -- the
602
+ * stated ground where one was stated, the container's otherwise. Not simply the ground: the Theme's
603
+ * ground for a mark is a fill a few percent opaque, and a mark painted in it vanished. A change of colour, not a
595
604
  * movement, so it is not taken back for a reader who asked for less motion -- only its easing is.
596
605
  */
597
606
  :where(.phi-card__icon) {
@@ -605,13 +614,13 @@
605
614
 
606
615
  @media (hover: hover) {
607
616
  :where(.phi-card--hover-icon):hover :where(.phi-card__icon) {
608
- color: var(--phi-card-icon-background);
617
+ color: var(--phi-card-icon-hover-color);
609
618
  background: var(--phi-card-icon-color);
610
619
  }
611
620
  }
612
621
 
613
622
  :where(.phi-card--hover-icon):focus-within :where(.phi-card__icon) {
614
- color: var(--phi-card-icon-background);
623
+ color: var(--phi-card-icon-hover-color);
615
624
  background: var(--phi-card-icon-color);
616
625
  }
617
626
 
@@ -4,42 +4,28 @@ import { readPhiCmsMountPolicy } from "./cms-mount-policy";
4
4
  import { clampPhiSequenceTransitionMs, readPhiMotionEasing, PHI_SEQUENCE_ANCHORS, PHI_SEQUENCE_TRANSITIONS, } from "../helpers/motion";
5
5
  import { readPhiLengthValue } from "./length";
6
6
  import { isPhiAnchorWidgetPlacement, resolvePhiRenderableBlockAnchor, } from "../components/controls/phi-anchor-control-contract";
7
- import { readBoolean, readCssSize, readNumber, readRenderableBlockConfig, readString, } from "../components/widgets/config/parser-primitives";
7
+ import { readCssSize, readRenderableBlockConfig, } from "../components/widgets/config/parser-primitives";
8
8
  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 { createPhiConfigReader } from "../components/widgets/config/strict-config-reader";
12
13
  import { PHI_SPACING_SCALE_KEYS } from "../components/widgets/config/spacing-options";
13
14
  import { PHI_GRID_COLUMN_COUNTS, isPhiGridColumnCount } from "../constants/grid-columns";
14
- function readInitialSlotStates(value) {
15
- if (Array.isArray(value)) {
16
- const normalized = value.map((candidate) => candidate === "expanded" || candidate === "collapsed" || candidate === "hidden" ? candidate : undefined);
17
- return normalized.some((candidate) => candidate !== undefined) ? normalized : undefined;
18
- }
19
- if (!value || typeof value !== "object") {
20
- return undefined;
21
- }
22
- const raw = value;
23
- const next = {};
24
- for (const [key, candidate] of Object.entries(raw)) {
25
- const slotIndex = Number(key);
26
- if (!Number.isInteger(slotIndex) || slotIndex < 0) {
27
- continue;
28
- }
29
- if (candidate === "expanded" || candidate === "collapsed" || candidate === "hidden") {
30
- next[slotIndex] = candidate;
15
+ const PHI_LAYOUT_SLOT_STATES = ["expanded", "collapsed", "hidden"];
16
+ /** A Layout's starting slot states: a list by slot, gaps allowed, or a record keyed by slot index. */
17
+ function readInitialSlotStates(reader) {
18
+ return reader.value("initialSlotStates", "slot states by slot index", (value) => {
19
+ const isState = (candidate) => candidate == null || PHI_LAYOUT_SLOT_STATES.includes(candidate);
20
+ if (Array.isArray(value)) {
21
+ return value.every(isState) ? value : undefined;
31
22
  }
32
- }
33
- return Object.keys(next).length > 0 ? next : undefined;
34
- }
35
- function readStringArray(value) {
36
- if (!Array.isArray(value)) {
37
- return undefined;
38
- }
39
- const next = value
40
- .map((candidate) => (typeof candidate === "string" ? candidate.trim() : ""))
41
- .filter((candidate) => candidate.length > 0);
42
- return [...new Set(next)];
23
+ if (!isPhiRecord(value))
24
+ return undefined;
25
+ return Object.entries(value).every(([key, candidate]) => Number.isInteger(Number(key)) && Number(key) >= 0 && isState(candidate))
26
+ ? value
27
+ : undefined;
28
+ });
43
29
  }
44
30
  export function normalizePhiPaddingWidgetConfig(config) {
45
31
  if (!isPhiRecord(config)) {
@@ -153,18 +139,14 @@ function readRenderableBlockAnchorOrPlacement(value) {
153
139
  ? resolvePhiRenderableBlockAnchor(value)
154
140
  : undefined;
155
141
  }
156
- function readGridResponsiveCount(value, accepts) {
157
- if (!isPhiRecord(value))
158
- return undefined;
159
- const record = value;
160
- const read = (key) => {
161
- const candidate = readNumber(record[key]);
162
- return candidate != null && Number.isInteger(candidate) && accepts(candidate) ? candidate : undefined;
163
- };
164
- const responsive = { compact: read("compact"), medium: read("medium"), wide: read("wide") };
165
- return responsive.compact != null || responsive.medium != null || responsive.wide != null
166
- ? responsive
167
- : undefined;
142
+ function readLayoutAnchor(reader) {
143
+ return reader.value("anchor", "an anchor or a placement", readRenderableBlockAnchorOrPlacement);
144
+ }
145
+ function readGridResponsiveCount(reader, key, expected, accepts) {
146
+ return reader.object(key, (countReader) => {
147
+ const read = (width) => countReader.value(width, expected, (candidate) => typeof candidate === "number" && Number.isInteger(candidate) && accepts(candidate) ? candidate : undefined);
148
+ return { compact: read("compact"), medium: read("medium"), wide: read("wide") };
149
+ });
168
150
  }
169
151
  const PHI_GRID_MAX_COLUMNS = Math.max(...PHI_GRID_COLUMN_COUNTS);
170
152
  /*
@@ -172,32 +154,43 @@ const PHI_GRID_MAX_COLUMNS = Math.max(...PHI_GRID_COLUMN_COUNTS);
172
154
  * they fit the row at a given width is answered where the row is known, by clamping
173
155
  * (`resolvePhiGridSlotPlacement`): a Grid whose columns were reduced keeps its stated placements.
174
156
  */
175
- function readGridSlotPlacement(value, slotIndexFromArray) {
176
- if (!isPhiRecord(value)) {
177
- return null;
178
- }
179
- const slot = value;
180
- const slotIndex = readNumber(slot.slotIndex) ?? slotIndexFromArray;
181
- if (!Number.isInteger(slotIndex) || slotIndex == null || slotIndex < 0) {
182
- return null;
183
- }
184
- const span = readGridResponsiveCount(slot.span, (candidate) => candidate >= 1 && candidate <= PHI_GRID_MAX_COLUMNS);
185
- const offset = readGridResponsiveCount(slot.offset, (candidate) => candidate >= 0 && candidate < PHI_GRID_MAX_COLUMNS);
157
+ function readGridSlotPlacement(reader, slotIndexFromArray) {
158
+ const slotIndex = reader.value("slotIndex", "a slot index", (candidate) => typeof candidate === "number" && Number.isInteger(candidate) && candidate >= 0 ? candidate : undefined) ?? slotIndexFromArray;
159
+ const span = readGridResponsiveCount(reader, "span", `a whole number from 1 to ${PHI_GRID_MAX_COLUMNS}`, (candidate) => candidate >= 1 && candidate <= PHI_GRID_MAX_COLUMNS);
160
+ const offset = readGridResponsiveCount(reader, "offset", `a whole number from 0 to ${PHI_GRID_MAX_COLUMNS - 1}`, (candidate) => candidate >= 0 && candidate < PHI_GRID_MAX_COLUMNS);
186
161
  if (!span && !offset)
187
- return null;
162
+ reader.fail("span", "a span or an offset", undefined);
188
163
  return { slotIndex, span, offset };
189
164
  }
190
165
  /**
191
- * A shared Layout field, read the same way wherever it appears: a line on the 24-track form grid, so
192
- * anything outside 2..24 is not a narrower column but a mistake, and is dropped rather than clamped.
166
+ * The fields many Layouts share, read the same way wherever they appear. A label end is a line on the
167
+ * 24-track form grid, so anything outside 2..24 is not a narrower column but a mistake.
193
168
  */
194
- function readPhiLayoutLabelEnd(value) {
195
- const labelEnd = readNumber(value);
196
- return labelEnd !== undefined && Number.isInteger(labelEnd) && labelEnd >= 2 && labelEnd <= 24
197
- ? labelEnd
198
- : undefined;
169
+ function readCssSizeField(reader, key) {
170
+ return reader.value(key, "a CSS size", (value) => (typeof value === "number" && Number.isFinite(value)) || typeof value === "string" ? value : undefined);
171
+ }
172
+ function readLengthField(reader, key) {
173
+ return reader.value(key, "a CSS length", (value) => readPhiLengthValue(value) ?? undefined);
174
+ }
175
+ function readLayoutLabelEnd(reader) {
176
+ return reader.value("labelEnd", "a whole number from 2 to 24", (value) => typeof value === "number" && Number.isInteger(value) && value >= 2 && value <= 24 ? value : undefined);
177
+ }
178
+ function readLayoutPadding(reader) {
179
+ return {
180
+ padding: readCssSizeField(reader, "padding"),
181
+ paddingLeft: readCssSizeField(reader, "paddingLeft"),
182
+ paddingRight: readCssSizeField(reader, "paddingRight"),
183
+ paddingTop: readCssSizeField(reader, "paddingTop"),
184
+ paddingBottom: readCssSizeField(reader, "paddingBottom"),
185
+ };
186
+ }
187
+ function readFlexWrap(reader) {
188
+ return reader.value("wrap", "true, false or a flex wrap", (value) => typeof value === "boolean" || value === "nowrap" || value === "wrap" || value === "wrap-reverse"
189
+ ? value
190
+ : undefined) ?? false;
199
191
  }
200
192
  export function parsePhiCmsContentLayoutConfig(config) {
193
+ const reader = createPhiConfigReader(config);
201
194
  return applyPhiLayoutDefaults({
202
195
  ...readRenderableBlockConfig(config),
203
196
  /*
@@ -207,82 +200,65 @@ export function parsePhiCmsContentLayoutConfig(config) {
207
200
  * constraints never had the second way at all -- nothing has ever read a flat `maxWidth`.
208
201
  */
209
202
  size: normalizePhiRenderableBlockResponsiveSize(config.size),
210
- margin: readCssSize(config.margin),
211
- labelEnd: readPhiLayoutLabelEnd(config.labelEnd),
212
- padding: readCssSize(config.padding),
213
- paddingLeft: readCssSize(config.paddingLeft),
214
- paddingRight: readCssSize(config.paddingRight),
215
- paddingTop: readCssSize(config.paddingTop),
216
- paddingBottom: readCssSize(config.paddingBottom),
203
+ margin: readCssSizeField(reader, "margin"),
204
+ labelEnd: readLayoutLabelEnd(reader),
205
+ ...readLayoutPadding(reader),
217
206
  /*
218
207
  * Handed on as the block anchor, like every other Layout's. This one used to be folded into a
219
208
  * placement name here, and the renderer then asked the name for its `horizontal` -- a string has
220
209
  * none, so a Content Layout anchored left drew centred wherever the parsed config reached it, in
221
210
  * preview and live, while the Builder, which hands the object straight through, drew it left.
222
211
  */
223
- anchor: readRenderableBlockAnchorOrPlacement(config.anchor),
212
+ anchor: readLayoutAnchor(reader),
224
213
  }, resolvePhiLayoutDefaults("content"));
225
214
  }
226
215
  export function parsePhiCmsFlexLayoutConfig(config) {
227
- const distribution = readString(config.distribution);
216
+ const reader = createPhiConfigReader(config);
228
217
  return applyPhiLayoutDefaults({
229
218
  ...readRenderableBlockConfig(config),
230
- labelEnd: readPhiLayoutLabelEnd(config.labelEnd),
231
- initialSlotStates: readInitialSlotStates(config.initialSlotStates),
232
- padding: readCssSize(config.padding),
233
- paddingLeft: readCssSize(config.paddingLeft),
234
- paddingRight: readCssSize(config.paddingRight),
235
- paddingTop: readCssSize(config.paddingTop),
236
- paddingBottom: readCssSize(config.paddingBottom),
237
- gap: readCssSize(config.gap),
238
- verticalSeparators: readBoolean(config.verticalSeparators) ?? false,
239
- separatorBeforeFirst: readBoolean(config.separatorBeforeFirst) ?? false,
240
- separatorSpan: readPhiLengthValue(config.separatorSpan) ?? undefined,
241
- anchor: readRenderableBlockAnchorOrPlacement(config.anchor),
242
- distribution: distribution === "between" || distribution === "around" || distribution === "evenly"
243
- ? distribution
244
- : "anchor",
245
- wrap: readBoolean(config.wrap) ??
246
- readString(config.wrap) ??
247
- false,
219
+ labelEnd: readLayoutLabelEnd(reader),
220
+ initialSlotStates: readInitialSlotStates(reader),
221
+ ...readLayoutPadding(reader),
222
+ gap: readCssSizeField(reader, "gap"),
223
+ verticalSeparators: reader.boolean("verticalSeparators") ?? false,
224
+ separatorBeforeFirst: reader.boolean("separatorBeforeFirst") ?? false,
225
+ separatorSpan: readLengthField(reader, "separatorSpan"),
226
+ anchor: readLayoutAnchor(reader),
227
+ distribution: reader.oneOf("distribution", ["anchor", "between", "around", "evenly"]) ?? "anchor",
228
+ wrap: readFlexWrap(reader),
248
229
  }, resolvePhiLayoutDefaults("flex"));
249
230
  }
250
231
  export function parsePhiCmsFlexVerticalLayoutConfig(config) {
232
+ const reader = createPhiConfigReader(config);
251
233
  return applyPhiLayoutDefaults({
252
234
  ...readRenderableBlockConfig(config),
253
- labelEnd: readPhiLayoutLabelEnd(config.labelEnd),
254
- initialSlotStates: readInitialSlotStates(config.initialSlotStates),
255
- padding: readCssSize(config.padding),
256
- paddingLeft: readCssSize(config.paddingLeft),
257
- paddingRight: readCssSize(config.paddingRight),
258
- paddingTop: readCssSize(config.paddingTop),
259
- paddingBottom: readCssSize(config.paddingBottom),
260
- gap: readCssSize(config.gap),
261
- anchor: readRenderableBlockAnchorOrPlacement(config.anchor),
235
+ labelEnd: readLayoutLabelEnd(reader),
236
+ initialSlotStates: readInitialSlotStates(reader),
237
+ ...readLayoutPadding(reader),
238
+ gap: readCssSizeField(reader, "gap"),
239
+ anchor: readLayoutAnchor(reader),
262
240
  }, resolvePhiLayoutDefaults("verticalflex"));
263
241
  }
264
242
  export function parsePhiCmsMasonryLayoutConfig(config) {
243
+ const reader = createPhiConfigReader(config);
265
244
  return applyPhiLayoutDefaults({
266
245
  ...readRenderableBlockConfig(config),
267
- labelEnd: readPhiLayoutLabelEnd(config.labelEnd),
268
- padding: readCssSize(config.padding),
269
- paddingLeft: readCssSize(config.paddingLeft),
270
- paddingRight: readCssSize(config.paddingRight),
271
- paddingTop: readCssSize(config.paddingTop),
272
- paddingBottom: readCssSize(config.paddingBottom),
273
- columns: readGridResponsiveCount(config.columns, isPhiGridColumnCount),
274
- gap: readCssSize(config.gap),
246
+ labelEnd: readLayoutLabelEnd(reader),
247
+ ...readLayoutPadding(reader),
248
+ columns: readGridResponsiveCount(reader, "columns", "a Grid column count", isPhiGridColumnCount),
249
+ gap: readCssSizeField(reader, "gap"),
275
250
  }, resolvePhiLayoutDefaults("masonry"));
276
251
  }
277
252
  export function parsePhiCmsStackLayoutConfig(config) {
253
+ const reader = createPhiConfigReader(config);
278
254
  return applyPhiLayoutDefaults({
279
255
  ...readRenderableBlockConfig(config),
280
- labelEnd: readPhiLayoutLabelEnd(config.labelEnd),
281
- padding: readCssSize(config.padding),
282
- defaultActiveSlotKey: readString(config.defaultActiveSlotKey),
283
- slotDisplay: config.slotDisplay === "stacked" ? "stacked" : "single",
256
+ labelEnd: readLayoutLabelEnd(reader),
257
+ padding: readCssSizeField(reader, "padding"),
258
+ defaultActiveSlotKey: reader.string("defaultActiveSlotKey"),
259
+ slotDisplay: reader.oneOf("slotDisplay", ["single", "stacked"]) ?? "single",
284
260
  mountPolicy: readPhiCmsMountPolicy(config.mountPolicy, "remount"),
285
- slotTransition: config.slotTransition === "fade-over" ? "fade-over" : "none",
261
+ slotTransition: reader.oneOf("slotTransition", ["none", "fade-over"]) ?? "none",
286
262
  // Absent means "whatever the theme does", which is why the fallback is read from the token
287
263
  // rather than written here as a number.
288
264
  ...(config.slotTransitionDurationMs === undefined ? {} : {
@@ -338,9 +314,9 @@ export function parsePhiCmsCarouselLayoutConfig(config) {
338
314
  const slotGap = readCarouselMember(config.slotGap, PHI_SPACING_SCALE_KEYS, "slotGap");
339
315
  return applyPhiLayoutDefaults({
340
316
  ...readRenderableBlockConfig(config),
341
- labelEnd: readPhiLayoutLabelEnd(config.labelEnd),
342
- padding: readCssSize(config.padding),
343
- defaultActiveSlotKey: readString(config.defaultActiveSlotKey),
317
+ labelEnd: readLayoutLabelEnd(createPhiConfigReader(config)),
318
+ padding: readCssSizeField(createPhiConfigReader(config), "padding"),
319
+ defaultActiveSlotKey: createPhiConfigReader(config).string("defaultActiveSlotKey"),
344
320
  ...(visibleSlots === undefined ? {} : { visibleSlots }),
345
321
  ...(windowAnchor === undefined ? {} : { windowAnchor }),
346
322
  ...(transition === undefined ? {} : { transition }),
@@ -361,100 +337,81 @@ export function parsePhiCmsCarouselLayoutConfig(config) {
361
337
  ...(lookahead === undefined ? {} : { lookahead }),
362
338
  }, resolvePhiLayoutDefaults("carousel"));
363
339
  }
364
- function readCollapsibleTrigger(value) {
365
- return value === "header" || value === "icon" || value === "disabled" ? value : undefined;
366
- }
367
- function readCollapsibleSize(value) {
368
- return readPhiControlSize(value);
369
- }
370
- function readExpandIconPlacement(value) {
371
- return value === "start" || value === "end" ? value : undefined;
372
- }
373
- function readSlotTexts(value) {
374
- if (!Array.isArray(value)) {
340
+ /** Panel titles or descriptions by slot: texts, an empty one for a slot that has none; trailing empties trimmed. */
341
+ function readSlotTexts(reader, key) {
342
+ const texts = reader.value(key, "a list of texts", (value) => Array.isArray(value) && value.every((candidate) => typeof candidate === "string")
343
+ ? value.map((candidate) => candidate.trim())
344
+ : undefined);
345
+ if (!texts)
375
346
  return undefined;
376
- }
377
- const texts = value.map((candidate) => (typeof candidate === "string" ? candidate.trim() : ""));
378
- while (texts.length > 0 && !texts[texts.length - 1]) {
347
+ while (texts.length > 0 && !texts[texts.length - 1])
379
348
  texts.pop();
380
- }
381
349
  return texts.length > 0 ? texts : undefined;
382
350
  }
383
351
  export function parsePhiCmsCollapsibleLayoutConfig(config) {
352
+ const reader = createPhiConfigReader(config);
353
+ const defaultOpenSlotKeys = reader.stringList("defaultOpenSlotKeys");
384
354
  return applyPhiLayoutDefaults({
385
355
  ...readRenderableBlockConfig(config),
386
- labelEnd: readPhiLayoutLabelEnd(config.labelEnd),
387
- initialSlotStates: readInitialSlotStates(config.initialSlotStates),
388
- padding: readCssSize(config.padding),
389
- paddingLeft: readCssSize(config.paddingLeft),
390
- paddingRight: readCssSize(config.paddingRight),
391
- paddingTop: readCssSize(config.paddingTop),
392
- paddingBottom: readCssSize(config.paddingBottom),
393
- anchor: readRenderableBlockAnchorOrPlacement(config.anchor),
394
- panelMinHeight: readPhiLengthValue(config.panelMinHeight) ?? undefined,
395
- accordion: readBoolean(config.accordion),
396
- slotTitles: readSlotTexts(config.slotTitles),
397
- slotDescriptions: readSlotTexts(config.slotDescriptions),
398
- defaultOpenSlotKeys: readStringArray(config.defaultOpenSlotKeys),
399
- collapsible: readCollapsibleTrigger(config.collapsible),
400
- ghost: readBoolean(config.ghost),
401
- expandIconPlacement: readExpandIconPlacement(config.expandIconPlacement),
402
- collapseSize: readCollapsibleSize(config.collapseSize),
403
- titleStrong: readBoolean(config.titleStrong),
404
- headerPadding: readCssSize(config.headerPadding),
405
- innerPadding: readCssSize(config.innerPadding),
356
+ labelEnd: readLayoutLabelEnd(reader),
357
+ initialSlotStates: readInitialSlotStates(reader),
358
+ ...readLayoutPadding(reader),
359
+ anchor: readLayoutAnchor(reader),
360
+ panelMinHeight: readLengthField(reader, "panelMinHeight"),
361
+ accordion: reader.boolean("accordion"),
362
+ slotTitles: readSlotTexts(reader, "slotTitles"),
363
+ slotDescriptions: readSlotTexts(reader, "slotDescriptions"),
364
+ defaultOpenSlotKeys: defaultOpenSlotKeys
365
+ ? [...new Set(defaultOpenSlotKeys.map((key) => key.trim()).filter(Boolean))]
366
+ : undefined,
367
+ collapsible: reader.oneOf("collapsible", ["header", "icon", "disabled"]),
368
+ ghost: reader.boolean("ghost"),
369
+ expandIconPlacement: reader.oneOf("expandIconPlacement", ["start", "end"]),
370
+ collapseSize: reader.value("collapseSize", "a control size", readPhiControlSize),
371
+ titleStrong: reader.boolean("titleStrong"),
372
+ headerPadding: readCssSizeField(reader, "headerPadding"),
373
+ innerPadding: readCssSizeField(reader, "innerPadding"),
406
374
  }, resolvePhiLayoutDefaults("collapsible"));
407
375
  }
408
376
  export function parsePhiCmsGridLayoutConfig(config) {
377
+ const reader = createPhiConfigReader(config);
409
378
  return applyPhiLayoutDefaults({
410
379
  ...readRenderableBlockConfig(config),
411
- labelEnd: readPhiLayoutLabelEnd(config.labelEnd),
412
- padding: readCssSize(config.padding),
413
- paddingLeft: readCssSize(config.paddingLeft),
414
- paddingRight: readCssSize(config.paddingRight),
415
- paddingTop: readCssSize(config.paddingTop),
416
- paddingBottom: readCssSize(config.paddingBottom),
417
- gap: readCssSize(config.gap),
418
- anchor: readRenderableBlockAnchorOrPlacement(config.anchor),
419
- columns: readGridResponsiveCount(config.columns, isPhiGridColumnCount),
420
- slotPlacements: Array.isArray(config.slotPlacements)
421
- ? config.slotPlacements
422
- .map((slot, slotIndex) => readGridSlotPlacement(slot, slotIndex))
423
- .filter((slot) => slot !== null)
424
- : undefined,
380
+ labelEnd: readLayoutLabelEnd(reader),
381
+ ...readLayoutPadding(reader),
382
+ gap: readCssSizeField(reader, "gap"),
383
+ anchor: readLayoutAnchor(reader),
384
+ columns: readGridResponsiveCount(reader, "columns", "a Grid column count", isPhiGridColumnCount),
385
+ slotPlacements: reader.list("slotPlacements", readGridSlotPlacement),
425
386
  }, resolvePhiLayoutDefaults("grid"));
426
387
  }
427
388
  export function parsePhiCmsThreeColumnLayoutConfig(config) {
389
+ const reader = createPhiConfigReader(config);
428
390
  return applyPhiLayoutDefaults({
429
391
  ...readRenderableBlockConfig(config),
430
- labelEnd: readPhiLayoutLabelEnd(config.labelEnd),
431
- padding: readCssSize(config.padding),
432
- paddingLeft: readCssSize(config.paddingLeft),
433
- paddingRight: readCssSize(config.paddingRight),
434
- paddingTop: readCssSize(config.paddingTop),
435
- paddingBottom: readCssSize(config.paddingBottom),
436
- balancedSides: readBoolean(config.balancedSides) ?? true,
437
- gap: readCssSize(config.gap),
438
- anchor: readRenderableBlockAnchorOrPlacement(config.anchor),
439
- align: readString(config.align),
440
- justify: readString(config.justify),
441
- wrap: readBoolean(config.wrap) ??
442
- readString(config.wrap) ??
443
- false,
444
- leftWidth: readPhiLengthValue(config.leftWidth) ?? undefined,
445
- middleWidth: readPhiLengthValue(config.middleWidth) ?? undefined,
446
- rightWidth: readPhiLengthValue(config.rightWidth) ?? undefined,
447
- contentAlign: readString(config.contentAlign),
392
+ labelEnd: readLayoutLabelEnd(reader),
393
+ ...readLayoutPadding(reader),
394
+ balancedSides: reader.boolean("balancedSides") ?? true,
395
+ gap: readCssSizeField(reader, "gap"),
396
+ anchor: readLayoutAnchor(reader),
397
+ align: reader.string("align"),
398
+ justify: reader.string("justify"),
399
+ wrap: readFlexWrap(reader),
400
+ leftWidth: readLengthField(reader, "leftWidth"),
401
+ middleWidth: readLengthField(reader, "middleWidth"),
402
+ rightWidth: readLengthField(reader, "rightWidth"),
403
+ contentAlign: reader.string("contentAlign"),
448
404
  }, resolvePhiLayoutDefaults("threecol"));
449
405
  }
450
406
  export function parsePhiCmsSplitCardLayoutConfig(config) {
407
+ const reader = createPhiConfigReader(config);
451
408
  return applyPhiLayoutDefaults({
452
409
  ...readRenderableBlockConfig(config),
453
- labelEnd: readPhiLayoutLabelEnd(config.labelEnd),
454
- gap: readCssSize(config.gap),
455
- swapRatio: readBoolean(config.swapRatio),
456
- padding: readCssSize(config.padding),
457
- paddingTop: readCssSize(config.paddingTop),
458
- paddingBottom: readCssSize(config.paddingBottom),
410
+ labelEnd: readLayoutLabelEnd(reader),
411
+ gap: readCssSizeField(reader, "gap"),
412
+ swapRatio: reader.boolean("swapRatio"),
413
+ padding: readCssSizeField(reader, "padding"),
414
+ paddingTop: readCssSizeField(reader, "paddingTop"),
415
+ paddingBottom: readCssSizeField(reader, "paddingBottom"),
459
416
  }, resolvePhiLayoutDefaults("split"));
460
417
  }
@@ -101,7 +101,6 @@ export type PhiCmsConfigFieldChoiceMode = "single" | "multiple";
101
101
  export type PhiCmsConfigFieldChoiceValueType = "string" | "string[]" | "number";
102
102
  export type PhiCmsConfigFieldChoiceCreateBehavior = "none" | "accept-custom" | "create-record";
103
103
  export type PhiCmsConfigFieldColorMode = "single" | "gradient" | "both";
104
- export type PhiCmsConfigFieldValueStorage = "field" | "self";
105
104
  export type PhiCmsConfigFieldChoiceFilter = {
106
105
  widgetType?: string;
107
106
  group?: string;
@@ -290,14 +289,6 @@ export type PhiCmsConfigField = (PhiCmsConfigFieldBase & {
290
289
  presentation?: PhiCmsConfigFieldCollectionPresentation;
291
290
  /** What the button that opens the Overlay says. */
292
291
  editLabel?: string;
293
- }) | (PhiCmsConfigFieldBase & {
294
- type: "background";
295
- section?: string;
296
- storage?: PhiCmsConfigFieldValueStorage;
297
- }) | (PhiCmsConfigFieldBase & {
298
- type: "border";
299
- section?: string;
300
- storage?: PhiCmsConfigFieldValueStorage;
301
292
  }) | (PhiCmsConfigFieldBase & {
302
293
  /**
303
294
  * A Surface (`types/surface.ts`) of something the Widget draws inside itself -- each entry of a
@@ -308,7 +299,7 @@ export type PhiCmsConfigField = (PhiCmsConfigFieldBase & {
308
299
  /** Whether the mode is offered; left out, it is. */
309
300
  tone?: false;
310
301
  }) | (PhiCmsConfigFieldBase & {
311
- type: "shadow" | "grid-placement" | "column-count";
302
+ type: "grid-placement" | "column-count";
312
303
  section?: string;
313
304
  });
314
305
  export type PhiCmsLayoutSlotDefinition = {
@@ -575,7 +566,9 @@ export type PhiCmsRuntimeRenderRegistry = {
575
566
  dataProviderDescriptorsByKey: ReadonlyMap<PhiRuntimeDataProviderKey, PhiRuntimeModuleDataProviderDescriptor>;
576
567
  formDefinitionsById: ReadonlyMap<string, PhiRuntimeModuleFormDefinition>;
577
568
  };
578
- export type PhiCmsRenderIssueCode = "missing-module" | "missing-renderer" | "renderer-load-failed" | "render-failed" | "invalid-render-output"
569
+ export type PhiCmsRenderIssueCode = "missing-module" | "missing-renderer" | "renderer-load-failed" | "render-failed"
570
+ /** The stored config holds a value the block cannot read (`PhiConfigError`); the detail names the field. */
571
+ | "invalid-config" | "invalid-render-output"
579
572
  /** A node whose `visibleWhen` reads a feature namespace whose Module could not be asked this render. */
580
573
  | "feature-unavailable"
581
574
  /** A Region or Overlay zone names a root layout node the tree does not contain. */
@@ -18,7 +18,7 @@ import type { PhiTextTone } from "../components/widgets/config/text-tone";
18
18
  import type { PhiCommonControlActionKey } from "../components/widgets/label-types/common-controls";
19
19
  import type { PhiMaskConfig } from "../components/widgets/config/mask";
20
20
  import type { PhiSpacingScaleKey } from "../components/widgets/config/spacing-options";
21
- import type { PhiCardVariant } from "../components/widgets/shared/card-vocabulary";
21
+ import type { PhiCardBillingPeriod, PhiCardVariant } from "../components/widgets/shared/card-vocabulary";
22
22
  import type { PhiControlBadgeConfig, PhiControlConfig } from "../components/widgets/config/control-signal-config";
23
23
  /**
24
24
  * The part of a placement every Widget shares, whatever it is.
@@ -42,6 +42,8 @@ export type PhiWidgetPlacementSignalRoutes = {
42
42
  type PhiControlPlacement = Omit<PhiControlConfig, "signalRoutes">;
43
43
  /** Logical, not left and right: a Site in an RTL locale reads "start" as the right-hand edge. */
44
44
  export type PhiMarkdownTextAlign = "start" | "center" | "end" | "justify";
45
+ /** Which edge the text's pictures stand at, with the words flowing past them; logical like the align. */
46
+ export type PhiMarkdownImageFloat = "start" | "end";
45
47
  export type PhiMarkdownWidgetPlacement = PhiWidgetPlacementBase & {
46
48
  sourceMode?: "inline" | "url";
47
49
  markdown?: string;
@@ -49,6 +51,7 @@ export type PhiMarkdownWidgetPlacement = PhiWidgetPlacementBase & {
49
51
  sourceLocale?: string;
50
52
  revalidateSeconds?: number;
51
53
  textAlign?: PhiMarkdownTextAlign;
54
+ imageFloat?: PhiMarkdownImageFloat;
52
55
  textBlockSpacingBefore?: PhiSpacingScaleKey;
53
56
  textBlockSpacingAfter?: PhiSpacingScaleKey;
54
57
  headingBlockSpacingBefore?: PhiSpacingScaleKey;
@@ -282,10 +285,21 @@ export type PhiCmsCollectionCardPresentation = {
282
285
  actionLabel?: string;
283
286
  actionHref?: string;
284
287
  variant?: PhiCardVariant;
285
- /** Every card in the View draws this body; `stat` needs `value`. */
286
- body?: "text" | "stat";
288
+ /** Every card in the View draws this body; `stat` needs `value`, `plan` a `price`. */
289
+ body?: "text" | "stat" | "plan";
287
290
  /** A field whose truth marks the card as highlighted. */
288
291
  highlight?: string;
292
+ /** The words on the card's band. */
293
+ badge?: string;
294
+ /** A `plan`'s prices and their periods, as a provider answers them -- per country, if it knows one. */
295
+ price?: string;
296
+ pricePeriod?: string;
297
+ priceYearly?: string;
298
+ pricePeriodYearly?: string;
299
+ /** A field holding the plan's features, each `{ text, state? }` (`PhiBulletListEntry`). */
300
+ features?: string;
301
+ /** Which price every plan card of the View shows. Not a path: the View's own choice. */
302
+ billingPeriod?: PhiCardBillingPeriod;
289
303
  };
290
304
  export type PhiCollectionViewWidgetPlacement = PhiWidgetPlacementBase & {
291
305
  presentation?: {
@@ -1,3 +1,4 @@
1
+ import type { PhiRuntimeModuleCatalog } from "./cms-plugins";
1
2
  import type { PhiCmsAreaKey } from "../constants/cms-areas";
2
3
  import type { PhiTimelineEntry } from "@phis/contracts/timeline";
3
4
  import type { PhiAccessViewer, PhiViewerAccessPolicy } from "./access";
@@ -133,6 +134,14 @@ export type PhiDashboardCardContext = {
133
134
  locale: string;
134
135
  area: PhiCmsAreaKey;
135
136
  viewer: PhiAccessViewer;
137
+ /**
138
+ * The Module catalog of the Area the Dashboard stands in.
139
+ *
140
+ * A card about structure -- which Area has an open draft -- has to know the Area shells, and the
141
+ * only catalog it may know is the one it is running in. Importing the first-party catalog instead
142
+ * would compile every Module into the provider (MODULES.md, "The dependency rule").
143
+ */
144
+ runtimeModuleCatalog: PhiRuntimeModuleCatalog;
136
145
  };
137
146
  /**
138
147
  * One Module's answer, loaded on the server and never reached from the browser.
package/types/media.d.ts CHANGED
@@ -69,7 +69,6 @@ export type PhiMediaAssetVariant = {
69
69
  deliveryUrl: string;
70
70
  contentType: string;
71
71
  bytes: number | null;
72
- checksumSha256: string | null;
73
72
  width: number;
74
73
  height: number;
75
74
  blurDataUrl: string | null;
@@ -90,7 +89,6 @@ export type PhiMediaAsset = {
90
89
  contentType: string;
91
90
  kind: PhiMediaKindValue;
92
91
  bytes: number | null;
93
- checksumSha256: string | null;
94
92
  width: number | null;
95
93
  height: number | null;
96
94
  blurDataUrl: string | null;
@@ -127,7 +125,6 @@ export type PhiMediaAssetTile = {
127
125
  width: number | null;
128
126
  height: number | null;
129
127
  bytes: number | null;
130
- checksumSha256: string | null;
131
128
  lifecycleStatus: number;
132
129
  deliveryPolicy: number;
133
130
  deliveryRevision: number;