@phis/ui 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) 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-anchor-nav-control.js +4 -1
  7. package/components/controls/phi-background-control.js +21 -6
  8. package/components/controls/phi-bullet-list-control.d.ts +7 -2
  9. package/components/controls/phi-bullet-list-control.js +35 -5
  10. package/components/controls/phi-color-control.js +4 -2
  11. package/components/controls/phi-drawer-control-adapter.js +3 -2
  12. package/components/controls/phi-inline-text-editor.js +7 -1
  13. package/components/controls/phi-length-control.js +1 -1
  14. package/components/controls/phi-ribbon-control.d.ts +38 -0
  15. package/components/controls/phi-ribbon-control.js +57 -0
  16. package/components/controls/phi-ribbon-wrap-control.d.ts +26 -0
  17. package/components/controls/phi-ribbon-wrap-control.js +25 -0
  18. package/components/controls/phi-select-control.js +5 -2
  19. package/components/controls/phi-series-chart-control.d.ts +48 -0
  20. package/components/controls/phi-series-chart-control.js +82 -0
  21. package/components/controls/phi-table-control.d.ts +3 -0
  22. package/components/controls/phi-table-control.js +13 -1
  23. package/components/controls/phi-text-control.js +57 -3
  24. package/components/controls/phi-tree-control.js +4 -4
  25. package/components/controls/phi-truncated-hover-text.d.ts +15 -0
  26. package/components/controls/phi-truncated-hover-text.js +25 -0
  27. package/components/forms/link-target-form-control.d.ts +1 -1
  28. package/components/forms/link-target-form-control.js +2 -45
  29. package/components/forms/phi-form-widget-frame.js +4 -2
  30. package/components/forms/shared-form-provider-registry.js +1 -4
  31. package/components/layouts/clients/phi-three-column-layout-client.js +10 -1
  32. package/components/media/asset-collection-runtime.js +6 -16
  33. package/components/media/media-font-metrics.d.ts +3 -0
  34. package/components/media/media-font-metrics.js +43 -0
  35. package/components/media/phi-area-upload-widget.js +53 -97
  36. package/components/media/phi-image-preview-data.d.ts +13 -0
  37. package/components/media/phi-image-preview-data.js +184 -9
  38. package/components/media/phi-media-picker-binding.js +16 -13
  39. package/components/media/phi-media-upload.d.ts +15 -8
  40. package/components/media/phi-media-upload.js +18 -9
  41. package/components/media/phi-site-font-assets.js +7 -8
  42. package/components/navigation/phi-link.js +6 -1
  43. package/components/overlays/phi-overlay-container-client.js +16 -1
  44. package/components/regions/clients/cms-region-container-client.js +6 -1
  45. package/components/regions/phi-cms-region-container.js +3 -0
  46. package/components/regions/phi-cms-region-shell.d.ts +12 -1
  47. package/components/regions/phi-cms-region-shell.js +81 -19
  48. package/components/regions/presets/phi-settings-page-shell-tree.js +48 -1
  49. package/components/root/phi-config-provider.d.ts +13 -0
  50. package/components/root/phi-config-provider.js +12 -1
  51. package/components/root/phi-root-background.d.ts +7 -0
  52. package/components/root/phi-root-background.js +20 -1
  53. package/components/root/phi-root-theme-resolver.js +3 -0
  54. package/components/root/phi-shell-chrome-overlay.d.ts +12 -1
  55. package/components/root/phi-shell-chrome-overlay.js +19 -3
  56. package/components/runtime/runtime-controller-materialization.js +12 -1
  57. package/components/shell/phi-antd-icon.d.ts +1 -0
  58. package/components/shell/phi-antd-icon.js +3 -0
  59. package/components/shell/use-phi-shell-sticky-top.js +61 -16
  60. package/components/state/scoped-state-store.js +20 -3
  61. package/components/surface/phi-surface-tone.d.ts +13 -0
  62. package/components/surface/phi-surface-tone.js +21 -0
  63. package/components/tables/client/phi-table-binding.d.ts +2 -1
  64. package/components/tables/client/phi-table-binding.js +45 -21
  65. package/components/trees/client/phi-tree-binding.js +19 -11
  66. package/components/widgets/client/bullet-list-entries-editor.d.ts +26 -0
  67. package/components/widgets/client/bullet-list-entries-editor.js +109 -0
  68. package/components/widgets/client/html-editor.js +1 -1
  69. package/components/widgets/client/shared/use-page-reference-options.d.ts +17 -0
  70. package/components/widgets/client/shared/use-page-reference-options.js +57 -0
  71. package/components/widgets/config/background.d.ts +32 -1
  72. package/components/widgets/config/background.js +41 -2
  73. package/components/widgets/config/choice-shared.js +12 -13
  74. package/components/widgets/config/command-buttons.d.ts +5 -1
  75. package/components/widgets/config/command-buttons.js +38 -81
  76. package/components/widgets/config/control-signal-config.d.ts +3 -1
  77. package/components/widgets/config/control-signal-config.js +19 -17
  78. package/components/widgets/config/image-source-parser.d.ts +1 -0
  79. package/components/widgets/config/image-source-parser.js +7 -8
  80. package/components/widgets/config/mask.d.ts +6 -1
  81. package/components/widgets/config/mask.js +24 -1
  82. package/components/widgets/config/parser-primitives.js +72 -0
  83. package/components/widgets/config/stack-tabs.d.ts +1 -1
  84. package/components/widgets/config/stack-tabs.js +2 -3
  85. package/components/widgets/config/strict-config-reader.d.ts +40 -0
  86. package/components/widgets/config/strict-config-reader.js +174 -0
  87. package/components/widgets/label-sets/background.js +2 -0
  88. package/components/widgets/label-sets/effects.js +2 -0
  89. package/components/widgets/label-types/background.d.ts +1 -0
  90. package/components/widgets/label-types/background.js +1 -0
  91. package/components/widgets/label-types/effects.d.ts +1 -0
  92. package/components/widgets/label-types/effects.js +1 -0
  93. package/components/widgets/navigation-descriptor-resolver.server.js +20 -8
  94. package/components/widgets/shared/bullet-list-vocabulary.d.ts +27 -1
  95. package/components/widgets/shared/bullet-list-vocabulary.js +24 -4
  96. package/components/widgets/shared/card-body-client.d.ts +18 -4
  97. package/components/widgets/shared/card-body-client.js +126 -26
  98. package/components/widgets/shared/card-vocabulary.d.ts +31 -4
  99. package/components/widgets/shared/card-vocabulary.js +35 -2
  100. package/components/widgets/shared/markdown-body-client.js +39 -10
  101. package/components/widgets/signals/control-signal-capabilities.d.ts +0 -18
  102. package/components/widgets/signals/control-signal-capabilities.js +10 -22
  103. package/components/workspace/catalog-store.d.ts +4 -3
  104. package/components/workspace/catalog-store.js +6 -5
  105. package/gateway/dashboard-cards-route.js +1 -0
  106. package/gateway/internal-references.d.ts +6 -1
  107. package/gateway/internal-references.js +48 -23
  108. package/gateway/site-cms-write-validation.d.ts +9 -0
  109. package/gateway/site-cms-write-validation.js +78 -0
  110. package/gateway/site-proxy.js +19 -3
  111. package/helpers/cms-container-chrome.js +6 -1
  112. package/helpers/locale.d.ts +8 -0
  113. package/helpers/locale.js +18 -0
  114. package/helpers/renderable-block-serialization.js +1 -1
  115. package/helpers/shell-region-style.js +2 -1
  116. package/helpers/surface-style.d.ts +12 -1
  117. package/helpers/surface-style.js +28 -2
  118. package/module-dashboard-cards.d.ts +1 -1
  119. package/module-dashboard-cards.js +1 -1
  120. package/next/config.d.mts +3 -0
  121. package/next/config.mjs +28 -1
  122. package/package.json +2 -2
  123. package/plugins/runtime/phi-slot-child-viewport-effects-observer.js +5 -1
  124. package/plugins/runtime-modules/accounting/presets.d.ts +9 -0
  125. package/plugins/runtime-modules/accounting/presets.js +37 -0
  126. package/plugins/runtime-modules/accounting/server.d.ts +1 -4
  127. package/plugins/runtime-modules/accounting/server.js +4 -7
  128. package/plugins/runtime-modules/app/presets.d.ts +18 -0
  129. package/plugins/runtime-modules/app/presets.js +67 -0
  130. package/plugins/runtime-modules/app/server.d.ts +1 -4
  131. package/plugins/runtime-modules/app/server.js +4 -7
  132. package/plugins/runtime-modules/area-definitions.d.ts +0 -42
  133. package/plugins/runtime-modules/area-definitions.js +3 -8
  134. package/plugins/runtime-modules/asset/controller/runtime.js +2 -2
  135. package/plugins/runtime-modules/asset/services/collection.js +9 -1
  136. package/plugins/runtime-modules/asset/services/options.js +2 -2
  137. package/plugins/runtime-modules/builder/area-settings-answer.js +2 -1
  138. package/plugins/runtime-modules/builder/authoring-canvas.js +6 -6
  139. package/plugins/runtime-modules/builder/cards.js +3 -5
  140. package/plugins/runtime-modules/builder/clients/builder-region-overrides.js +3 -3
  141. package/plugins/runtime-modules/builder/clients/canvas-widget.js +15 -54
  142. package/plugins/runtime-modules/builder/clients/inspector-config-field.d.ts +4 -1
  143. package/plugins/runtime-modules/builder/clients/inspector-config-field.js +25 -35
  144. package/plugins/runtime-modules/builder/clients/inspector-link-target-field.js +1 -1
  145. package/plugins/runtime-modules/builder/clients/inspector-section-widget.js +8 -8
  146. package/plugins/runtime-modules/builder/clients/inspector-title.js +6 -6
  147. package/plugins/runtime-modules/builder/clients/layout-inspector.d.ts +1 -1
  148. package/plugins/runtime-modules/builder/clients/layout-inspector.js +11 -25
  149. package/plugins/runtime-modules/builder/clients/layout-scaffold-overlays.js +10 -5
  150. package/plugins/runtime-modules/builder/clients/mode-switch.js +1 -1
  151. package/plugins/runtime-modules/builder/clients/pages-header.js +11 -11
  152. package/plugins/runtime-modules/builder/clients/pages-workspace.js +1 -1
  153. package/plugins/runtime-modules/builder/clients/region-visibility-fields.js +1 -1
  154. package/plugins/runtime-modules/builder/clients/runtime-module-authoring-boundary.js +1 -1
  155. package/plugins/runtime-modules/builder/clients/shells-workspace.js +15 -31
  156. package/plugins/runtime-modules/builder/clients/structure-canvas.js +1 -1
  157. package/plugins/runtime-modules/builder/clients/use-region-draft-hydration.d.ts +26 -0
  158. package/plugins/runtime-modules/builder/clients/use-region-draft-hydration.js +36 -0
  159. package/plugins/runtime-modules/builder/clients/widget-effects-editor.js +1 -1
  160. package/plugins/runtime-modules/builder/clients/widget-inspector.js +1 -0
  161. package/plugins/runtime-modules/builder/clients/workspace-scope-boundary.js +2 -2
  162. package/plugins/runtime-modules/builder/cms-instance-allocator.js +8 -28
  163. package/plugins/runtime-modules/builder/controller/client.d.ts +0 -15
  164. package/plugins/runtime-modules/builder/controller/definition.d.ts +0 -15
  165. package/plugins/runtime-modules/builder/controller/definition.js +0 -8
  166. package/plugins/runtime-modules/builder/controller/draft-command-controller.d.ts +1 -2
  167. package/plugins/runtime-modules/builder/controller/draft-command-controller.js +11 -30
  168. package/plugins/runtime-modules/builder/controller/draft-status-controller.js +15 -2
  169. package/plugins/runtime-modules/builder/controller/inspector-controller.d.ts +1 -2
  170. package/plugins/runtime-modules/builder/controller/inspector-controller.js +4 -7
  171. package/plugins/runtime-modules/builder/controller/page-controller.d.ts +1 -2
  172. package/plugins/runtime-modules/builder/controller/page-controller.js +16 -68
  173. package/plugins/runtime-modules/builder/controller/server-definition.d.ts +0 -15
  174. package/plugins/runtime-modules/builder/controller/signal-wiring-controller.d.ts +2 -5
  175. package/plugins/runtime-modules/builder/controller/signal-wiring-controller.js +6 -50
  176. package/plugins/runtime-modules/builder/controller/workspace-controller.d.ts +2 -3
  177. package/plugins/runtime-modules/builder/controller/workspace-controller.js +89 -153
  178. package/plugins/runtime-modules/builder/data-providers.d.ts +1 -0
  179. package/plugins/runtime-modules/builder/data-providers.js +1 -0
  180. package/plugins/runtime-modules/builder/definition.d.ts +1 -0
  181. package/plugins/runtime-modules/builder/developer-workspace-store.d.ts +64 -30
  182. package/plugins/runtime-modules/builder/developer-workspace-store.js +255 -61
  183. package/plugins/runtime-modules/builder/edit-scaffold-drawer.js +4 -3
  184. package/plugins/runtime-modules/builder/history.d.ts +3 -1
  185. package/plugins/runtime-modules/builder/options-providers.js +3 -2
  186. package/plugins/runtime-modules/builder/page-catalog-client.js +19 -7
  187. package/plugins/runtime-modules/builder/page-meta-form.js +1 -0
  188. package/plugins/runtime-modules/builder/page-references-provider.js +1 -1
  189. package/plugins/runtime-modules/builder/persistence.js +33 -69
  190. package/plugins/runtime-modules/builder/public-route-collisions.d.ts +2 -2
  191. package/plugins/runtime-modules/builder/public-route-collisions.js +6 -19
  192. package/plugins/runtime-modules/builder/render-root-node-preview.server.js +20 -3
  193. package/plugins/runtime-modules/builder/render-root-node-scaffold.js +28 -18
  194. package/plugins/runtime-modules/builder/runtime-module-selection.d.ts +2 -12
  195. package/plugins/runtime-modules/builder/runtime-module-selection.js +16 -42
  196. package/plugins/runtime-modules/builder/services/navigation-table.js +9 -15
  197. package/plugins/runtime-modules/builder/services/page-source-tree.js +8 -8
  198. package/plugins/runtime-modules/builder/services/runtime-modules-table.js +10 -10
  199. package/plugins/runtime-modules/builder/services/signal-routes-table.js +3 -3
  200. package/plugins/runtime-modules/builder/signal-route-selection.d.ts +1 -2
  201. package/plugins/runtime-modules/builder/signal-route-selection.js +2 -2
  202. package/plugins/runtime-modules/builder/signal-wiring-options.js +3 -2
  203. package/plugins/runtime-modules/builder/structure-dnd.js +8 -61
  204. package/plugins/runtime-modules/builder/trees/phi-default-builder-area-preset-tree.js +17 -10
  205. package/plugins/runtime-modules/builder/widgets/structure-region/built-in.js +27 -22
  206. package/plugins/runtime-modules/builder/workspace-catalog-actions.d.ts +55 -0
  207. package/plugins/runtime-modules/builder/workspace-catalog-actions.js +142 -0
  208. package/plugins/runtime-modules/client-authoring-module.d.ts +8 -1
  209. package/plugins/runtime-modules/client-authoring-module.js +13 -3
  210. package/plugins/runtime-modules/client-manifests/common.js +1 -1
  211. package/plugins/runtime-modules/{runtime-calendar-adapters.js → core/calendar-adapters.js} +2 -2
  212. package/plugins/runtime-modules/{client-calendar-adapters/core.js → core/client-calendar-adapters.js} +1 -1
  213. package/plugins/runtime-modules/core/content-entries/table.js +1 -1
  214. package/plugins/runtime-modules/core/data-providers.d.ts +3 -0
  215. package/plugins/runtime-modules/core/data-providers.js +2 -1
  216. package/plugins/runtime-modules/core/definition.d.ts +3 -0
  217. package/plugins/runtime-modules/core/definition.js +1 -1
  218. package/plugins/runtime-modules/core/messaging/data-providers.d.ts +1 -0
  219. package/plugins/runtime-modules/core/messaging/data-providers.js +1 -0
  220. package/plugins/runtime-modules/core/messaging/thread-inbox-table.js +1 -1
  221. package/plugins/runtime-modules/core/server.js +1 -1
  222. package/plugins/runtime-modules/core/themes.d.ts +44 -0
  223. package/plugins/runtime-modules/core/themes.js +12 -0
  224. package/plugins/runtime-modules/core/widgets/brand/client.d.ts +12 -3
  225. package/plugins/runtime-modules/core/widgets/brand/client.js +3 -11
  226. package/plugins/runtime-modules/core/widgets/brand/home.d.ts +15 -0
  227. package/plugins/runtime-modules/core/widgets/brand/home.js +18 -0
  228. package/plugins/runtime-modules/core/widgets/brand/plugin.js +3 -2
  229. package/plugins/runtime-modules/core/widgets/brand/server.d.ts +2 -2
  230. package/plugins/runtime-modules/core/widgets/brand/server.js +2 -2
  231. package/plugins/runtime-modules/core/widgets/card/collection-view-binding.js +25 -9
  232. package/plugins/runtime-modules/core/widgets/card/config.d.ts +301 -254
  233. package/plugins/runtime-modules/core/widgets/card/config.js +68 -3
  234. package/plugins/runtime-modules/core/widgets/card/editor.js +41 -4
  235. package/plugins/runtime-modules/core/widgets/card/server.d.ts +3 -0
  236. package/plugins/runtime-modules/core/widgets/card/server.js +32 -3
  237. package/plugins/runtime-modules/core/widgets/collection-view/asset-binding.js +7 -6
  238. package/plugins/runtime-modules/core/widgets/collection-view/config.js +9 -2
  239. package/plugins/runtime-modules/core/widgets/description/client.js +3 -3
  240. package/plugins/runtime-modules/core/widgets/description/config.d.ts +2 -25
  241. package/plugins/runtime-modules/core/widgets/description/editor.js +7 -63
  242. package/plugins/runtime-modules/core/widgets/description/server.js +3 -2
  243. package/plugins/runtime-modules/core/widgets/entry-list/config.d.ts +2 -25
  244. package/plugins/runtime-modules/core/widgets/gallery/config.d.ts +2 -25
  245. package/plugins/runtime-modules/core/widgets/header-navigation/client.js +2 -2
  246. package/plugins/runtime-modules/core/widgets/html/client.js +1 -1
  247. package/plugins/runtime-modules/core/widgets/image/config.js +2 -2
  248. package/plugins/runtime-modules/core/widgets/markdown/config.d.ts +320 -2
  249. package/plugins/runtime-modules/core/widgets/markdown/config.js +18 -4
  250. package/plugins/runtime-modules/core/widgets/markdown/editor.js +11 -4
  251. package/plugins/runtime-modules/core/widgets/markdown-toc/client.js +6 -1
  252. package/plugins/runtime-modules/core/widgets/markdown-toc/config.d.ts +14 -0
  253. package/plugins/runtime-modules/core/widgets/markdown-toc/config.js +14 -2
  254. package/plugins/runtime-modules/core/widgets/quick-links/authoring.js +7 -2
  255. package/plugins/runtime-modules/core/widgets/quick-links/client.js +2 -2
  256. package/plugins/runtime-modules/core/widgets/quick-links/items.d.ts +0 -2
  257. package/plugins/runtime-modules/core/widgets/quick-links/items.js +0 -15
  258. package/plugins/runtime-modules/core/widgets/result/config.d.ts +5 -25
  259. package/plugins/runtime-modules/core/widgets/result/config.js +2 -0
  260. package/plugins/runtime-modules/core/widgets/result/server.js +25 -19
  261. package/plugins/runtime-modules/core/widgets/sidebar-navigation/authoring.js +1 -1
  262. package/plugins/runtime-modules/core/widgets/sidebar-navigation/client.js +1 -1
  263. package/plugins/runtime-modules/core/widgets/simple-text/client.js +8 -3
  264. package/plugins/runtime-modules/core/widgets/simple-text/config.d.ts +2 -25
  265. package/plugins/runtime-modules/core/widgets/simple-text/editor.js +2 -2
  266. package/plugins/runtime-modules/core/widgets/slot-upload/client.js +11 -2
  267. package/plugins/runtime-modules/core/widgets/table/client.js +8 -4
  268. package/plugins/runtime-modules/core/widgets/table/config.d.ts +6 -83
  269. package/plugins/runtime-modules/core/widgets/table/config.js +302 -452
  270. package/plugins/runtime-modules/core/widgets/tree/config.js +5 -2
  271. package/plugins/runtime-modules/dashboard/card-rows.d.ts +7 -0
  272. package/plugins/runtime-modules/dashboard/card-rows.js +87 -0
  273. package/plugins/runtime-modules/dashboard/clients/dashboard-card-view.js +32 -12
  274. package/plugins/runtime-modules/dashboard/services/collection.js +17 -7
  275. package/plugins/runtime-modules/dashboard/trees/phi-default-builder-dashboard-page-tree.js +5 -40
  276. package/plugins/runtime-modules/groups/data-providers.d.ts +2 -0
  277. package/plugins/runtime-modules/groups/data-providers.js +3 -3
  278. package/plugins/runtime-modules/groups/definition.d.ts +2 -0
  279. package/plugins/runtime-modules/localization/data-providers.d.ts +3 -0
  280. package/plugins/runtime-modules/localization/data-providers.js +2 -0
  281. package/plugins/runtime-modules/localization/definition.d.ts +3 -0
  282. package/plugins/runtime-modules/localization/services/table.js +3 -7
  283. package/plugins/runtime-modules/observability/data-providers.d.ts +1 -0
  284. package/plugins/runtime-modules/observability/data-providers.js +1 -0
  285. package/plugins/runtime-modules/observability/definition.d.ts +1 -0
  286. package/plugins/runtime-modules/observability/module.d.ts +1 -0
  287. package/plugins/runtime-modules/observability/services/table.js +1 -1
  288. package/plugins/runtime-modules/phi-default-site-area-preset-tree.js +2 -105
  289. package/plugins/runtime-modules/public/presets.d.ts +115 -0
  290. package/plugins/runtime-modules/{area-base-presets.js → public/presets.js} +19 -119
  291. package/plugins/runtime-modules/public/server.d.ts +1 -4
  292. package/plugins/runtime-modules/public/server.js +4 -7
  293. package/plugins/runtime-modules/public/trees/phi-default-pub-error-page-tree.js +7 -8
  294. package/plugins/runtime-modules/public-base-page-references.d.ts +1 -1
  295. package/plugins/runtime-modules/public-base-page-references.js +1 -1
  296. package/plugins/runtime-modules/revisions/controller/client.js +11 -11
  297. package/plugins/runtime-modules/revisions/services/table.js +3 -3
  298. package/plugins/runtime-modules/theme/trees/phi-default-builder-theme-page-tree.js +22 -0
  299. package/plugins/runtime-modules/theme/widgets/brand-controls/client.d.ts +2 -1
  300. package/plugins/runtime-modules/theme/widgets/brand-controls/client.js +111 -72
  301. package/plugins/runtime-modules/theme/widgets/brand-controls/config.d.ts +2 -1
  302. package/plugins/runtime-modules/theme/widgets/brand-controls/config.js +28 -31
  303. package/plugins/runtime-modules/theme/widgets/brand-controls/plugin.js +1 -1
  304. package/plugins/runtime-modules/theme-descriptors.js +1 -1
  305. package/plugins/runtime-modules/user-management/data-providers.d.ts +2 -0
  306. package/plugins/runtime-modules/user-management/data-providers.js +1 -0
  307. package/plugins/runtime-modules/user-management/definition.d.ts +2 -0
  308. package/server-helpers/cms-link-targets.js +6 -0
  309. package/styles/control-shape.css +11 -11
  310. package/styles/controls.css +67 -5
  311. package/styles/layout.css +61 -9
  312. package/styles/shell.css +73 -0
  313. package/theme/phi-antd-token-resolver.js +20 -1
  314. package/theme/phi-theme-tokens.d.ts +1 -1
  315. package/theme/phi-theme-tokens.js +1 -0
  316. package/theme/phi-theme.d.ts +5 -0
  317. package/theme/phi-theme.js +21 -0
  318. package/theme/phi-tokens.d.ts +16 -15
  319. package/theme/phi-tokens.js +16 -15
  320. package/types/cms-config.js +142 -185
  321. package/types/cms-plugins.d.ts +6 -11
  322. package/types/core-widget-placements.d.ts +14 -3
  323. package/types/dashboard-cards.d.ts +48 -4
  324. package/types/dashboard-cards.js +6 -0
  325. package/types/media.d.ts +0 -3
  326. package/types/renderable-block.d.ts +6 -1
  327. package/types/site-theme.d.ts +10 -2
  328. package/types/table-widget.d.ts +9 -0
  329. package/types/table-widget.js +20 -0
  330. package/types.d.ts +1 -1
  331. package/widget-config.d.ts +2 -0
  332. package/widget-config.js +6 -0
  333. package/plugins/runtime-modules/area-base-presets.d.ts +0 -139
  334. /package/plugins/runtime-modules/{runtime-calendar-adapters.d.ts → core/calendar-adapters.d.ts} +0 -0
  335. /package/plugins/runtime-modules/{client-calendar-adapters/core.d.ts → core/client-calendar-adapters.d.ts} +0 -0
@@ -1,5 +1,10 @@
1
1
  import { isPhiRecord } from "../../../helpers/is-record";
2
2
  import { mergePhiCmsRenderableBlockConfigDefaults } from "../../../helpers/cms-config-serialization";
3
+ import { readPhiViewerAccessPolicy } from "../../../types/access";
4
+ import { createPhiConfigReader } from "./strict-config-reader";
5
+ import { PHI_BACKGROUND_FILTERS, readPhiShadow } from "../../../types/layout-style";
6
+ import { PHI_CMS_BORDER_SOURCES } from "../../../types/cms-border-source";
7
+ import { PHI_SURFACE_TONES } from "../../../types/surface";
3
8
  export function readString(value) {
4
9
  return typeof value === "string" && value.trim() ? value : undefined;
5
10
  }
@@ -22,7 +27,74 @@ export function readNumberList(value) {
22
27
  export function readCssSize(value) {
23
28
  return readNumber(value) ?? readString(value);
24
29
  }
30
+ /*
31
+ * The fields every block carries, checked strictly before they are normalized
32
+ * (`strict-config-reader.ts`): a render mode, visibility or size of the wrong kind is an error naming the
33
+ * field, not the default put in its place. The check is one level deep -- a surface, a size or the
34
+ * effects must be what they claim to be; what is inside them is still normalized as before.
35
+ *
36
+ * The normalization itself stays lenient, because the Builder runs it on drafts while an author edits.
37
+ * This reader is what a stored config is read with: every Widget's `parseConfig` and the Layouts'.
38
+ */
39
+ /*
40
+ * A Surface, one level into each of its parts: a background's base, overlay, filter and motion are what
41
+ * they claim to be, a border is an object of the border's fields, a shadow is one the Theme knows. What is
42
+ * inside a gradient's stops or an overlay's pattern is still normalized as before.
43
+ */
44
+ function assertPhiSurfaceConfig(surface) {
45
+ const cssSize = (value) => (typeof value === "number" && Number.isFinite(value)) || typeof value === "string" ? value : undefined;
46
+ surface.object("background", (background) => {
47
+ background.value("base", "a background base: a CSS value or an object with a kind", (value) => typeof value === "string" ||
48
+ (isPhiRecord(value) && ["color", "gradient", "image", "none"].includes(String(value.kind)))
49
+ ? value
50
+ : undefined);
51
+ background.value("overlay", "a background overlay", (value) => (isPhiRecord(value) ? value : undefined));
52
+ background.oneOf("filter", PHI_BACKGROUND_FILTERS);
53
+ background.value("motion", "a background motion", (value) => (isPhiRecord(value) ? value : undefined));
54
+ });
55
+ surface.oneOf("borderSource", PHI_CMS_BORDER_SOURCES);
56
+ surface.object("border", (border) => {
57
+ border.number("borderWidth");
58
+ border.string("borderStyle");
59
+ border.string("borderColor");
60
+ for (const corner of ["borderTopLeftRadius", "borderTopRightRadius", "borderBottomLeftRadius", "borderBottomRightRadius"]) {
61
+ border.value(corner, "a CSS size", cssSize);
62
+ }
63
+ });
64
+ surface.value("shadow", "a shadow", readPhiShadow);
65
+ surface.oneOf("tone", PHI_SURFACE_TONES);
66
+ }
67
+ function assertPhiRenderableBlockBaseConfig(config) {
68
+ const reader = createPhiConfigReader(config);
69
+ const sizeLike = (value) => isPhiRecord(value) || typeof value === "string" || (typeof value === "number" && Number.isFinite(value))
70
+ ? value
71
+ : undefined;
72
+ reader.oneOf("renderMode", ["live", "preview", "editor"]);
73
+ reader.oneOf("visibility", ["visible", "hidden", "collapsed"]);
74
+ reader.value("accessPolicy", "an access policy", (value) => readPhiViewerAccessPolicy(value) ?? undefined);
75
+ reader.value("viewportFlags", "a whole number from 0 to 6", (value) => typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 6 ? value : undefined);
76
+ reader.boolean("enabled");
77
+ reader.boolean("debugMode");
78
+ reader.value("anchor", "an anchor", (value) => (isPhiRecord(value) || typeof value === "string" ? value : undefined));
79
+ reader.integer("zIndex");
80
+ reader.number("opacity");
81
+ reader.object("surface", assertPhiSurfaceConfig);
82
+ reader.string("className");
83
+ reader.value("size", "a size", sizeLike);
84
+ reader.value("minSize", "a size", sizeLike);
85
+ reader.value("maxSize", "a size", sizeLike);
86
+ reader.value("collapsedSizeHint", "a size", sizeLike);
87
+ reader.object("effects", (effects) => {
88
+ effects.list("viewportEffects", (effect) => {
89
+ // `z` turns flat in the page plane; nothing moves or fades along it.
90
+ if (effect.record.axis === "z" && effect.record.property !== "rotate") {
91
+ effect.fail("axis", '"x" or "y" ("z" only turns a "rotate")');
92
+ }
93
+ });
94
+ });
95
+ }
25
96
  export function readRenderableBlockConfig(config) {
97
+ assertPhiRenderableBlockBaseConfig(config);
26
98
  return mergePhiCmsRenderableBlockConfigDefaults(config);
27
99
  }
28
100
  export function readNavItems(value) {
@@ -1,4 +1,4 @@
1
- import { type PhiCmsWidgetConfigBase } from "./parser-primitives";
1
+ import type { PhiCmsWidgetConfigBase } from "./parser-primitives";
2
2
  import { type PhiStackChoiceControlConfig } from "./choice-shared";
3
3
  export type PhiCmsTabBarWidgetConfig = PhiCmsWidgetConfigBase & PhiStackChoiceControlConfig & {
4
4
  key?: string;
@@ -1,12 +1,11 @@
1
- import { readString } from "./parser-primitives";
1
+ import { createPhiConfigReader } from "./strict-config-reader";
2
2
  import { PHI_CHOICE_CONTROL_OPTION_FIELDS, PHI_CHOICE_CONTROL_CONFIG_FIELDS, PHI_CHOICE_CONTROL_VALUE_MODE_FIELD, parsePhiStackChoiceControlConfig, } from "./choice-shared";
3
3
  import { createPhiCmsWidgetDefinition } from "./helpers";
4
4
  import { PHI_SELECT_CONTROL_SIGNALS } from "../signals/control-signal-capabilities";
5
5
  function parsePhiCmsTabBarWidgetConfig(config) {
6
- const placement = readString(config.placement);
7
6
  return {
8
7
  ...parsePhiStackChoiceControlConfig(config),
9
- placement: placement === "bottom" || placement === "left" || placement === "right" ? placement : "top",
8
+ placement: createPhiConfigReader(config).oneOf("placement", ["top", "bottom", "left", "right"]) ?? "top",
10
9
  };
11
10
  }
12
11
  export const PHI_TAB_BAR_WIDGET_DEFINITION = createPhiCmsWidgetDefinition({
@@ -0,0 +1,40 @@
1
+ export declare class PhiConfigError extends Error {
2
+ readonly path: string;
3
+ readonly expected: string;
4
+ readonly received: unknown;
5
+ constructor(path: string, expected: string, received: unknown);
6
+ }
7
+ export declare function isPhiConfigError(error: unknown): error is PhiConfigError;
8
+ /** The render issue code for an error: a config nobody can read is said as such, not as a crash. */
9
+ export declare function resolvePhiRenderFailureCode(error: unknown): "invalid-config" | "render-failed";
10
+ export type PhiConfigReader = {
11
+ /** Where this reader stands, for nested readers and errors. */
12
+ readonly path: string;
13
+ readonly record: Readonly<Record<string, unknown>>;
14
+ has(key: string): boolean;
15
+ string(key: string): string | undefined;
16
+ number(key: string): number | undefined;
17
+ integer(key: string): number | undefined;
18
+ boolean(key: string): boolean | undefined;
19
+ /** One of a closed list of values. */
20
+ oneOf<const T extends string | number>(key: string, values: readonly T[]): T | undefined;
21
+ /** A value a reader of the caller's checks; `read` answers `undefined` for a value it does not accept. */
22
+ value<T>(key: string, expected: string, read: (value: unknown) => T | undefined): T | undefined;
23
+ /** A nested object, read by a reader of its own. */
24
+ object<T>(key: string, read: (reader: PhiConfigReader) => T): T | undefined;
25
+ /** A list of objects, each read by a reader of its own. */
26
+ list<T>(key: string, read: (reader: PhiConfigReader, index: number) => T): T[] | undefined;
27
+ /** A list of texts. */
28
+ stringList(key: string): string[] | undefined;
29
+ /** An error at a field of this reader, for a rule the field types cannot state. */
30
+ fail(key: string, expected: string, received?: unknown): never;
31
+ };
32
+ export declare function createPhiConfigReader(record: Readonly<Record<string, unknown>>, path?: string): PhiConfigReader;
33
+ /**
34
+ * The same strict reader over a server's answer. `source` names who answered (`"The media listing"`),
35
+ * so the error says whose shape it was. A value is read as in a config: absent is allowed and reads as
36
+ * absent, present and wrong throws. What the answer must carry, the caller asks for with `required`.
37
+ */
38
+ export declare function createPhiResponseReader(value: unknown, source: string, path?: string): PhiConfigReader;
39
+ /** A field the record must carry: what `read` answers, or the reader's error for the field. */
40
+ export declare function requirePhiField<T>(reader: PhiConfigReader, key: string, expected: string, read: (key: string) => T | undefined): T;
@@ -0,0 +1,174 @@
1
+ import { isPhiRecord } from "../../../helpers/is-record";
2
+ /*
3
+ * Reading a Widget's or Layout's stored config strictly.
4
+ *
5
+ * A value that is absent -- `undefined`, `null`, or an empty text -- is allowed and reads as absent; the
6
+ * parser decides what absent means. A value that is present and wrong is an error, never a silent
7
+ * `undefined` or a default put in its place: a config that says something the Widget cannot read is a
8
+ * config somebody has to correct, and they can only do that if it is said.
9
+ *
10
+ * The error names where it is (`columns[2].sizing.width`), what was expected and what was found. It is
11
+ * thrown from `parseConfig`; the renderer catches it per block (`phi-cms-layout-renderer.tsx`), so a live
12
+ * page leaves the one Widget out and logs it, while the Builder and the preview show it in the Widget's
13
+ * place, where the field can be corrected.
14
+ */
15
+ export class PhiConfigError extends Error {
16
+ path;
17
+ expected;
18
+ received;
19
+ constructor(path, expected, received) {
20
+ super(`Config field "${path}" expects ${expected}, found ${describePhiConfigValue(received)}.`);
21
+ this.name = "PhiConfigError";
22
+ this.path = path;
23
+ this.expected = expected;
24
+ this.received = received;
25
+ }
26
+ }
27
+ export function isPhiConfigError(error) {
28
+ return error instanceof PhiConfigError;
29
+ }
30
+ /** The render issue code for an error: a config nobody can read is said as such, not as a crash. */
31
+ export function resolvePhiRenderFailureCode(error) {
32
+ return error instanceof PhiConfigError || (error instanceof Error && error.name === "PhiConfigError")
33
+ ? "invalid-config"
34
+ : "render-failed";
35
+ }
36
+ function describePhiConfigValue(value) {
37
+ if (value === undefined)
38
+ return "nothing";
39
+ if (value === null)
40
+ return "null";
41
+ if (Array.isArray(value))
42
+ return "a list";
43
+ if (isPhiRecord(value))
44
+ return "an object";
45
+ if (typeof value === "string")
46
+ return `the text ${JSON.stringify(value.length > 40 ? `${value.slice(0, 40)}...` : value)}`;
47
+ return `${typeof value} ${String(value)}`;
48
+ }
49
+ function isAbsent(value) {
50
+ return value === undefined || value === null || (typeof value === "string" && value.trim() === "");
51
+ }
52
+ /**
53
+ * A field a reader found wrong, in a record that did not come from a stored config: a server's answer.
54
+ *
55
+ * The same reading, said as what it is. A response the client cannot read is the server's or the
56
+ * contract's fault, not something an author can correct, so it is not a `PhiConfigError` and no
57
+ * Builder offers to fix it.
58
+ */
59
+ class PhiResponseShapeError extends Error {
60
+ source;
61
+ path;
62
+ expected;
63
+ received;
64
+ constructor(source, path, expected, received) {
65
+ super(`${source} answered "${path}" with ${describePhiConfigValue(received)}, expected ${expected}.`);
66
+ this.name = "PhiResponseShapeError";
67
+ this.source = source;
68
+ this.path = path;
69
+ this.expected = expected;
70
+ this.received = received;
71
+ }
72
+ }
73
+ export function createPhiConfigReader(record, path = "") {
74
+ return createPhiStrictReader(record, path, (at, expected, received) => new PhiConfigError(at, expected, received));
75
+ }
76
+ /**
77
+ * The same strict reader over a server's answer. `source` names who answered (`"The media listing"`),
78
+ * so the error says whose shape it was. A value is read as in a config: absent is allowed and reads as
79
+ * absent, present and wrong throws. What the answer must carry, the caller asks for with `required`.
80
+ */
81
+ export function createPhiResponseReader(value, source, path = "") {
82
+ if (!isPhiRecord(value)) {
83
+ throw new PhiResponseShapeError(source, path || "(answer)", "an object", value);
84
+ }
85
+ return createPhiStrictReader(value, path, (at, expected, received) => new PhiResponseShapeError(source, at, expected, received));
86
+ }
87
+ /** A field the record must carry: what `read` answers, or the reader's error for the field. */
88
+ export function requirePhiField(reader, key, expected, read) {
89
+ const value = read(key);
90
+ return value === undefined ? reader.fail(key, expected) : value;
91
+ }
92
+ function createPhiStrictReader(record, path, makeError) {
93
+ const at = (key) => (path ? `${path}.${key}` : key);
94
+ const fail = (key, expected, received = record[key]) => {
95
+ throw makeError(at(key), expected, received);
96
+ };
97
+ const reader = {
98
+ path,
99
+ record,
100
+ has: (key) => !isAbsent(record[key]),
101
+ string(key) {
102
+ const value = record[key];
103
+ if (isAbsent(value))
104
+ return undefined;
105
+ return typeof value === "string" ? value : fail(key, "a text");
106
+ },
107
+ number(key) {
108
+ const value = record[key];
109
+ if (isAbsent(value))
110
+ return undefined;
111
+ return typeof value === "number" && Number.isFinite(value) ? value : fail(key, "a number");
112
+ },
113
+ integer(key) {
114
+ const value = record[key];
115
+ if (isAbsent(value))
116
+ return undefined;
117
+ return typeof value === "number" && Number.isInteger(value) ? value : fail(key, "a whole number");
118
+ },
119
+ boolean(key) {
120
+ const value = record[key];
121
+ if (isAbsent(value))
122
+ return undefined;
123
+ return typeof value === "boolean" ? value : fail(key, "true or false");
124
+ },
125
+ oneOf(key, values) {
126
+ const value = record[key];
127
+ if (isAbsent(value))
128
+ return undefined;
129
+ return values.includes(value)
130
+ ? value
131
+ : fail(key, `one of ${values.map((entry) => JSON.stringify(entry)).join(", ")}`);
132
+ },
133
+ value(key, expected, read) {
134
+ const value = record[key];
135
+ if (isAbsent(value))
136
+ return undefined;
137
+ const result = read(value);
138
+ return result === undefined ? fail(key, expected) : result;
139
+ },
140
+ object(key, read) {
141
+ const value = record[key];
142
+ if (isAbsent(value))
143
+ return undefined;
144
+ return isPhiRecord(value) ? read(createPhiStrictReader(value, at(key), makeError)) : fail(key, "an object");
145
+ },
146
+ list(key, read) {
147
+ const value = record[key];
148
+ if (isAbsent(value))
149
+ return undefined;
150
+ if (!Array.isArray(value))
151
+ return fail(key, "a list");
152
+ return value.map((entry, index) => {
153
+ const entryPath = `${at(key)}[${index}]`;
154
+ if (!isPhiRecord(entry))
155
+ throw makeError(entryPath, "an object", entry);
156
+ return read(createPhiStrictReader(entry, entryPath, makeError), index);
157
+ });
158
+ },
159
+ stringList(key) {
160
+ const value = record[key];
161
+ if (isAbsent(value))
162
+ return undefined;
163
+ if (!Array.isArray(value))
164
+ return fail(key, "a list of texts");
165
+ return value.map((entry, index) => {
166
+ if (typeof entry !== "string")
167
+ throw makeError(`${at(key)}[${index}]`, "a text", entry);
168
+ return entry;
169
+ });
170
+ },
171
+ fail,
172
+ };
173
+ return reader;
174
+ }
@@ -18,6 +18,7 @@ const PHI_BACKGROUND_WIDGET_LABEL_SET = definePhiLabelSet({
18
18
  base_color: PHI_BACKGROUND_WIDGET_DEFAULT_LABELS.base.color,
19
19
  base_gradient: PHI_BACKGROUND_WIDGET_DEFAULT_LABELS.base.gradient,
20
20
  base_image: PHI_BACKGROUND_WIDGET_DEFAULT_LABELS.base.image,
21
+ base_theme: PHI_BACKGROUND_WIDGET_DEFAULT_LABELS.base.theme,
21
22
  image_url: PHI_BACKGROUND_WIDGET_DEFAULT_LABELS.image.url,
22
23
  image_alt: PHI_BACKGROUND_WIDGET_DEFAULT_LABELS.image.alt,
23
24
  image_position: PHI_BACKGROUND_WIDGET_DEFAULT_LABELS.image.position,
@@ -104,6 +105,7 @@ export async function getPhiBackgroundWidgetLabels(options) {
104
105
  color: labels.base_color,
105
106
  gradient: labels.base_gradient,
106
107
  image: labels.base_image,
108
+ theme: labels.base_theme,
107
109
  },
108
110
  image: {
109
111
  url: labels.image_url,
@@ -81,6 +81,7 @@ const PHI_EFFECTS_WIDGET_LABEL_SET = definePhiLabelSet({
81
81
  origin_bottom_right: PHI_EFFECTS_WIDGET_DEFAULT_LABELS.origins.bottomRight,
82
82
  viewport_x: PHI_EFFECTS_WIDGET_DEFAULT_LABELS.viewport.x,
83
83
  viewport_y: PHI_EFFECTS_WIDGET_DEFAULT_LABELS.viewport.y,
84
+ viewport_z: PHI_EFFECTS_WIDGET_DEFAULT_LABELS.viewport.z,
84
85
  viewport_translate: PHI_EFFECTS_WIDGET_DEFAULT_LABELS.viewport.translate,
85
86
  viewport_opacity: PHI_EFFECTS_WIDGET_DEFAULT_LABELS.viewport.opacity,
86
87
  viewport_rotate: PHI_EFFECTS_WIDGET_DEFAULT_LABELS.viewport.rotate,
@@ -191,6 +192,7 @@ export async function getPhiEffectsWidgetLabels(options) {
191
192
  viewport: {
192
193
  x: labels.viewport_x,
193
194
  y: labels.viewport_y,
195
+ z: labels.viewport_z,
194
196
  translate: labels.viewport_translate,
195
197
  opacity: labels.viewport_opacity,
196
198
  rotate: labels.viewport_rotate,
@@ -14,6 +14,7 @@ export type PhiBackgroundWidgetLabels = {
14
14
  color: string;
15
15
  gradient: string;
16
16
  image: string;
17
+ theme: string;
17
18
  };
18
19
  image: {
19
20
  url: string;
@@ -14,6 +14,7 @@ export const PHI_BACKGROUND_WIDGET_DEFAULT_LABELS = {
14
14
  color: "Color",
15
15
  gradient: "Gradient",
16
16
  image: "Image",
17
+ theme: "Theme",
17
18
  },
18
19
  image: {
19
20
  url: "Image URL",
@@ -92,6 +92,7 @@ export type PhiEffectsWidgetLabels = {
92
92
  viewport: {
93
93
  x: string;
94
94
  y: string;
95
+ z: string;
95
96
  translate: string;
96
97
  opacity: string;
97
98
  rotate: string;
@@ -91,6 +91,7 @@ export const PHI_EFFECTS_WIDGET_DEFAULT_LABELS = {
91
91
  viewport: {
92
92
  x: "X",
93
93
  y: "Y",
94
+ z: "Z",
94
95
  translate: "Translate",
95
96
  opacity: "Opacity",
96
97
  rotate: "Rotate",
@@ -3,6 +3,7 @@ import { isPhiCmsNavigationOverlayTarget, readPhiCmsNavigationTargetPath, } from
3
3
  import { createPhiPresetCmsInstanceId } from "../../types/cms-instance-id";
4
4
  import { createGlobalTranslator, createSiteTranslator, PHI_TR_CTX_WEB_UI_LABEL, } from "../../gateway/tr";
5
5
  import { resolvePhiCmsNavigationOverlay, resolvePhiCmsActiveNavigationSurfaces, resolvePhiCmsRoutePresetByPageId, } from "../../plugins/runtime-modules/descriptor-compiler";
6
+ import { cache } from "react";
6
7
  import { logRuntimeEvent } from "../../net/log";
7
8
  import { phiRuntime } from "../../server-helpers/phi-runtime";
8
9
  import { getPhiRequestNavigationContext } from "../../server-helpers/request-runtime";
@@ -58,19 +59,30 @@ function mapResolvedNavigationItem(item, translatedLabels) {
58
59
  : {}),
59
60
  };
60
61
  }
62
+ /*
63
+ * Every active navigation surface of an Area, resolved once per request (audit K15).
64
+ *
65
+ * A page carries a navigation Widget per surface -- header, footer, sider -- and each one resolved all of
66
+ * the Area's surfaces to pick its own, hashing an instance id per item each time. The request's navigation
67
+ * context and its viewer are one object each per request, so React's `cache` keyed by them keeps the
68
+ * answer for exactly that request and nothing longer.
69
+ */
70
+ const resolveActiveNavigationSurfaces = cache((context, area, viewer) => resolvePhiCmsActiveNavigationSurfaces({
71
+ catalog: context.catalog,
72
+ area,
73
+ activeModuleIds: context.activeModuleIds,
74
+ routeTable: context.routeTable,
75
+ viewer,
76
+ }));
61
77
  export async function resolvePhiDescriptorNavigationItems(runtime, navKey, overlay = null) {
62
78
  const normalizedNavKey = navKey.trim().toLowerCase();
63
79
  if (!normalizedNavKey.startsWith(`${runtime.area}:`)) {
64
80
  return null;
65
81
  }
66
- const { catalog, activeModuleIds, routeTable } = getPhiRequestNavigationContext(runtime.area);
67
- const descriptorSurface = resolvePhiCmsActiveNavigationSurfaces({
68
- catalog,
69
- area: runtime.area,
70
- activeModuleIds,
71
- routeTable,
72
- viewer: runtime.viewer,
73
- }).find((candidate) => candidate.navKey === normalizedNavKey);
82
+ const navigationContext = getPhiRequestNavigationContext(runtime.area);
83
+ const { routeTable } = navigationContext;
84
+ const descriptorSurface = resolveActiveNavigationSurfaces(navigationContext, runtime.area, runtime.viewer)
85
+ .find((candidate) => candidate.navKey === normalizedNavKey);
74
86
  if (!descriptorSurface && !overlay) {
75
87
  return null;
76
88
  }
@@ -41,11 +41,30 @@ export type PhiResolvedBulletListStyle = {
41
41
  gap: PhiSpacingScaleKey;
42
42
  flags: number;
43
43
  };
44
+ /**
45
+ * The markers a browser cycles through, and a tight gap. Exported so a Widget can state them as its
46
+ * defaults, which is what an Inspector shows for a list nobody has set yet.
47
+ */
48
+ export declare const PHI_BULLET_LIST_DEFAULT_STYLE: {
49
+ readonly level1: "dot";
50
+ readonly level2: "circle";
51
+ readonly level3: "dash";
52
+ readonly gap: "xs";
53
+ };
44
54
  /** A stored list style, part by part; whatever is not one of its values is left out. */
45
55
  export declare function readPhiBulletListStyle(value: unknown): PhiBulletListStyle | undefined;
46
56
  export declare function resolvePhiBulletListStyle(style: PhiBulletListStyle | null | undefined): PhiResolvedBulletListStyle;
47
57
  /** The level an entry at `depth` (1 for the outermost) is drawn at. */
48
58
  export declare function resolvePhiBulletListLevel(depth: number): PhiBulletListLevel;
59
+ /**
60
+ * What an entry says about the thing the list belongs to, where the list is a list of what is in it -- a
61
+ * plan's features. `included` draws a tick, `excluded` a dash and the entry's words quieter; `neutral`
62
+ * is the list's own marker, and is what an entry without a state is. Stored only where it is not neutral.
63
+ */
64
+ export declare const PHI_BULLET_LIST_ENTRY_STATES: readonly ["included", "excluded", "neutral"];
65
+ export type PhiBulletListEntryState = (typeof PHI_BULLET_LIST_ENTRY_STATES)[number];
66
+ /** The state after `state`, in the order a click on a marker steps through them. */
67
+ export declare function nextPhiBulletListEntryState(state: PhiBulletListEntryState | undefined): PhiBulletListEntryState;
49
68
  /**
50
69
  * One entry of a list written as a list: its text, how deep it stands, and the values its text names.
51
70
  *
@@ -58,11 +77,17 @@ export type PhiBulletListEntry = {
58
77
  /** 1 for the outermost level, which is what an entry without one stands at. */
59
78
  level?: number;
60
79
  values?: string[];
80
+ /** Absent is `neutral`. */
81
+ state?: Exclude<PhiBulletListEntryState, "neutral">;
61
82
  };
62
83
  export declare function readPhiBulletListEntries(value: unknown): PhiBulletListEntry[];
63
84
  /** An entry of the tree the Control draws: what it shows, and the entries one level below it. */
64
85
  export type PhiBulletListNode<Content> = {
65
86
  content: Content;
87
+ /** Absent is `neutral`: the marker of the entry's level. */
88
+ state?: PhiBulletListEntryState;
89
+ /** Set by an editor: the marker becomes a button that steps the entry's state on. */
90
+ onMarkerClick?: () => void;
66
91
  children?: PhiBulletListNode<Content>[];
67
92
  };
68
93
  /**
@@ -72,7 +97,8 @@ export type PhiBulletListNode<Content> = {
72
97
  */
73
98
  export declare function buildPhiBulletListTree<Entry extends {
74
99
  level?: number;
75
- }, Content>(entries: readonly Entry[], content: (entry: Entry, index: number) => Content): PhiBulletListNode<Content>[];
100
+ state?: PhiBulletListEntryState;
101
+ }, Content>(entries: readonly Entry[], content: (entry: Entry, index: number) => Content, onMarkerClick?: (entry: Entry, index: number) => void): PhiBulletListNode<Content>[];
76
102
  /**
77
103
  * The Inspector fields for a list style stored under `key`, under a divider titled `heading`. Every Widget
78
104
  * with a list offers these, so a list is set the same way wherever it stands.
@@ -20,8 +20,11 @@ export const PhiBulletListFlag = {
20
20
  /** A line between entries, for entries without a ground of their own. */
21
21
  Dividers: 1 << 0,
22
22
  };
23
- /** The markers a browser cycles through, and a tight gap. */
24
- const PHI_BULLET_LIST_DEFAULT_STYLE = {
23
+ /**
24
+ * The markers a browser cycles through, and a tight gap. Exported so a Widget can state them as its
25
+ * defaults, which is what an Inspector shows for a list nobody has set yet.
26
+ */
27
+ export const PHI_BULLET_LIST_DEFAULT_STYLE = {
25
28
  level1: "dot",
26
29
  level2: "circle",
27
30
  level3: "dash",
@@ -69,6 +72,17 @@ export function resolvePhiBulletListLevel(depth) {
69
72
  const deepest = PHI_BULLET_LIST_LEVELS[PHI_BULLET_LIST_LEVELS.length - 1];
70
73
  return Math.min(Math.max(1, Math.trunc(depth) || 1), deepest);
71
74
  }
75
+ /**
76
+ * What an entry says about the thing the list belongs to, where the list is a list of what is in it -- a
77
+ * plan's features. `included` draws a tick, `excluded` a dash and the entry's words quieter; `neutral`
78
+ * is the list's own marker, and is what an entry without a state is. Stored only where it is not neutral.
79
+ */
80
+ export const PHI_BULLET_LIST_ENTRY_STATES = ["included", "excluded", "neutral"];
81
+ /** The state after `state`, in the order a click on a marker steps through them. */
82
+ export function nextPhiBulletListEntryState(state) {
83
+ const index = PHI_BULLET_LIST_ENTRY_STATES.indexOf(state ?? "neutral");
84
+ return PHI_BULLET_LIST_ENTRY_STATES[(index + 1) % PHI_BULLET_LIST_ENTRY_STATES.length];
85
+ }
72
86
  export function readPhiBulletListEntries(value) {
73
87
  if (!Array.isArray(value)) {
74
88
  return [];
@@ -82,10 +96,12 @@ export function readPhiBulletListEntries(value) {
82
96
  const values = Array.isArray(record.values)
83
97
  ? record.values.map((item) => (typeof item === "string" || typeof item === "number" ? String(item) : ""))
84
98
  : [];
99
+ const state = record.state === "included" || record.state === "excluded" ? record.state : undefined;
85
100
  return [{
86
101
  text: record.text,
87
102
  ...(level > 1 ? { level } : {}),
88
103
  ...(values.length > 0 ? { values } : {}),
104
+ ...(state ? { state } : {}),
89
105
  }];
90
106
  });
91
107
  }
@@ -94,14 +110,18 @@ export function readPhiBulletListEntries(value) {
94
110
  * more than a level hangs one level below it all the same: a level cannot be skipped in a nested list,
95
111
  * and an entry that tried stays with the entry it follows rather than jumping.
96
112
  */
97
- export function buildPhiBulletListTree(entries, content) {
113
+ export function buildPhiBulletListTree(entries, content, onMarkerClick) {
98
114
  const root = [];
99
115
  const open = [root];
100
116
  entries.forEach((entry, index) => {
101
117
  const wanted = resolvePhiBulletListLevel(entry.level ?? 1);
102
118
  const level = Math.min(wanted, open.length);
103
119
  open.length = level;
104
- const node = { content: content(entry, index) };
120
+ const node = {
121
+ content: content(entry, index),
122
+ ...(entry.state && entry.state !== "neutral" ? { state: entry.state } : {}),
123
+ ...(onMarkerClick ? { onMarkerClick: () => onMarkerClick(entry, index) } : {}),
124
+ };
105
125
  open[level - 1].push(node);
106
126
  node.children = [];
107
127
  open.push(node.children);
@@ -4,8 +4,8 @@ import type { PhiImagePresentation } from "../../media/image-presentation";
4
4
  import type { PhiClientBlockBaseProps } from "../../../types";
5
5
  import type { PhiSurface } from "../../../types/surface";
6
6
  import type { PhiSignalRouteSet } from "../../../types/signals";
7
- import type { PhiCardHeadingLevel, PhiCardHoverEffect, PhiCardIconPlacement, PhiCardTextAlign, PhiCardTextSlot, PhiCardVariant, PhiCardWidgetBody } from "./card-vocabulary";
8
- export type { PhiCardHeadingLevel, PhiCardHoverEffect, PhiCardIconPlacement, PhiCardTextAlign, PhiCardTextSlot, PhiCardVariant, PhiCardWidgetBody, } from "./card-vocabulary";
7
+ import type { PhiCardBadgeStyle, PhiCardBillingPeriod, PhiCardHeadingLevel, PhiCardHoverEffect, PhiCardIconPlacement, PhiCardTextAlign, PhiCardTextSlot, PhiCardVariant, PhiCardWidgetBody } from "./card-vocabulary";
8
+ export type { PhiCardBadgeStyle, PhiCardBillingPeriod, PhiCardHeadingLevel, PhiCardHoverEffect, PhiCardIconPlacement, PhiCardTextAlign, PhiCardTextSlot, PhiCardVariant, PhiCardWidgetBody, } from "./card-vocabulary";
9
9
  export type PhiCardWidgetClientLabels = {
10
10
  eyebrow?: string;
11
11
  /**
@@ -19,6 +19,13 @@ export type PhiCardWidgetClientLabels = {
19
19
  actionLabel?: string;
20
20
  /** The figure a `stat` body draws. Data rather than copy, and never translated. */
21
21
  value?: string;
22
+ /** The band at the card's end edge. */
23
+ badge?: string;
24
+ /** A `plan` body's prices, data and never translated, and their periods, which are words. */
25
+ price?: string;
26
+ pricePeriod?: string;
27
+ priceYearly?: string;
28
+ pricePeriodYearly?: string;
22
29
  };
23
30
  /**
24
31
  * What is true about a card's content right now, as opposed to what the card says.
@@ -63,6 +70,12 @@ export type PhiCardWidgetClientConfig = {
63
70
  variant?: PhiCardVariant;
64
71
  body?: PhiCardWidgetBody;
65
72
  highlight?: boolean;
73
+ /** The badge's ground; absent, the Theme's primary. */
74
+ badgeColor?: string;
75
+ /** How the badge is drawn; absent, `none`. */
76
+ badgeStyle?: PhiCardBadgeStyle;
77
+ /** Which price a `plan` shows until a `billingPeriod` signal says otherwise. Absent is `monthly`. */
78
+ billingPeriod?: PhiCardBillingPeriod;
66
79
  hoverEffect?: PhiCardHoverEffect;
67
80
  /** The card's wired signals (`PHI_CARD_SIGNALS`); absent where nothing is wired, as on the canvas. */
68
81
  signalRoutes?: PhiSignalRouteSet | null;
@@ -78,10 +91,11 @@ export type PhiCardWidgetAction = {
78
91
  export type PhiCardWidgetClientProps = PhiClientBlockBaseProps<PhiCardWidgetClientLabels, PhiCardWidgetClientConfig> & {
79
92
  binding?: PhiCardWidgetClientBinding;
80
93
  /**
81
- * What a `list` body draws, already drawn.
94
+ * What a `list` body draws, a `series` body's chart, and a `plan` body's features, already drawn.
82
95
  *
83
96
  * Handed in rather than drawn here: the Card is on Public pages, and a list Control imported by it
84
- * would load on every page with a Card. A Dashboard draws its entries and passes them.
97
+ * would load on every page with a Card. A Dashboard draws its entries and passes them; a plan card's
98
+ * server half draws its features on the server, and its editor draws them as fields.
85
99
  */
86
100
  listContent?: ReactNode;
87
101
  /**