@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
@@ -60,6 +60,9 @@ function groupPhiSelectOptions(resolved) {
60
60
  }
61
61
  export function PhiSelectControl({ id, value, label, description, ariaLabel, placeholder, disabled, readOnly, presentation = "select", allowCustom, allowClear, options, size, variant, popupMatchSelectWidth = false, getPopupContainer, popupRootClassName, popupZIndex, style, onFocus, onBlur, onOpenChange, onSearch, filterOptionsLocally = true, onChange, }) {
62
62
  const { labelId, labelledBy } = usePhiControlLabel(label, ariaLabel);
63
+ // A compact field opens a compact list: its options write small, as the field does (controls.css).
64
+ const popupClassName = [popupRootClassName, size === "small" ? "phi-select-popup--sm" : null]
65
+ .filter(Boolean).join(" ") || undefined;
63
66
  const resolvedOptions = groupPhiSelectOptions(separatePhiSelectOptions(options.map((option) => ({
64
67
  value: option.value,
65
68
  // rc-select synthesizes native HTML title attributes from primitive labels.
@@ -74,7 +77,7 @@ export function PhiSelectControl({ id, value, label, description, ariaLabel, pla
74
77
  (value === undefined || typeof value === "string") &&
75
78
  options.every((option) => typeof option.value === "string");
76
79
  if (canUseTextEntry) {
77
- const control = (_jsx(AutoComplete, { "aria-label": ariaLabel, "aria-labelledby": labelledBy, value: value, placeholder: placeholder, disabled: controlDisabled, allowClear: allowClear, options: resolvedOptions, optionRender: (resolvedOption) => isPhiSelectSeparatorEntry(resolvedOption.data) ? renderPhiSelectSeparator() : (_jsx(PhiControlOptionContent, { option: resolvedOption.data.option, presentation: "dropdown" })), size: size, variant: variant, popupMatchSelectWidth: popupMatchSelectWidth, getPopupContainer: getPopupContainer, classNames: popupRootClassName ? { popup: { root: popupRootClassName } } : undefined, styles: popupZIndex == null ? undefined : { popup: { root: { zIndex: popupZIndex } } }, filterOption: !filterOptionsLocally
80
+ const control = (_jsx(AutoComplete, { "aria-label": ariaLabel, "aria-labelledby": labelledBy, value: value, placeholder: placeholder, disabled: controlDisabled, allowClear: allowClear, options: resolvedOptions, optionRender: (resolvedOption) => isPhiSelectSeparatorEntry(resolvedOption.data) ? renderPhiSelectSeparator() : (_jsx(PhiControlOptionContent, { option: resolvedOption.data.option, presentation: "dropdown" })), size: size, variant: variant, popupMatchSelectWidth: popupMatchSelectWidth, getPopupContainer: getPopupContainer, classNames: popupClassName ? { popup: { root: popupClassName } } : undefined, styles: popupZIndex == null ? undefined : { popup: { root: { zIndex: popupZIndex } } }, filterOption: !filterOptionsLocally
78
81
  ? false
79
82
  : (inputValue, option) => {
80
83
  // A group heading has no search label of its own; antd filters the options inside it.
@@ -90,6 +93,6 @@ export function PhiSelectControl({ id, value, label, description, ariaLabel, pla
90
93
  return selected
91
94
  ? _jsx(PhiControlOptionContent, { option: selected, presentation: "selection" })
92
95
  : selectedOption.label;
93
- }, size: size, variant: variant, popupMatchSelectWidth: popupMatchSelectWidth, getPopupContainer: getPopupContainer, classNames: popupRootClassName ? { popup: { root: popupRootClassName } } : undefined, styles: popupZIndex == null ? undefined : { popup: { root: { zIndex: popupZIndex } } }, showSearch: true, optionFilterProp: ["searchLabel", "value"], filterOption: filterOptionsLocally ? undefined : false, onSearch: onSearch, onFocus: onFocus, onBlur: onBlur, onOpenChange: onOpenChange, onChange: onChange, style: style }));
96
+ }, size: size, variant: variant, popupMatchSelectWidth: popupMatchSelectWidth, getPopupContainer: getPopupContainer, classNames: popupClassName ? { popup: { root: popupClassName } } : undefined, styles: popupZIndex == null ? undefined : { popup: { root: { zIndex: popupZIndex } } }, showSearch: true, optionFilterProp: ["searchLabel", "value"], filterOption: filterOptionsLocally ? undefined : false, onSearch: onSearch, onFocus: onFocus, onBlur: onBlur, onOpenChange: onOpenChange, onChange: onChange, style: style }));
94
97
  return _jsx(PhiLabeledControl, { label: label, labelId: labelId, description: description, fill: style?.width === "100%", children: control });
95
98
  }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Figures over time: a line per series across the width it is given.
3
+ *
4
+ * Drawn as SVG by hand rather than through a chart library. What a card asks of it is a shape -- rising,
5
+ * falling, a spike last Tuesday -- and its latest figure, not axes to read values off; a library would
6
+ * bring a second rendering model and a dependency to every Site for that. No hooks and no Ant Design, so
7
+ * a server render draws it as well as a client one.
8
+ *
9
+ * The drawing is stretched to its box (`preserveAspectRatio="none"`) with strokes that do not stretch
10
+ * with it, so the chart takes its container's width at any size. The last point of each series is the
11
+ * emphasized one, drawn over the SVG rather than in it: a circle in a stretched drawing would be an ellipse.
12
+ *
13
+ * Every colour is the Theme's variable (`PHI_COLOR`), never a resolved value, so a Surface `tone` reaches
14
+ * the lines. The first series is the Theme's primary, with an area under it; the second and third are the
15
+ * text colours, the third dashed, so two lines differ in more than hue.
16
+ *
17
+ * The scale starts at zero for figures that are never negative, which counts are: a line that starts at
18
+ * its own minimum turns a day with one more visitor into a cliff.
19
+ */
20
+ export type PhiSeriesChartControlSeries = {
21
+ label: string;
22
+ /** One value per key, in the keys' order. */
23
+ values: readonly number[];
24
+ };
25
+ export type PhiSeriesChartControlProps = {
26
+ /** What each point is called, already in words -- its day -- oldest first. */
27
+ keys: readonly string[];
28
+ series: readonly PhiSeriesChartControlSeries[];
29
+ /** A word for what is counted, already translated; said after each latest figure. */
30
+ unit?: string;
31
+ /** The page's locale, for the figures in the spoken label. */
32
+ locale: string;
33
+ /** What the chart is of, said before its latest figures. */
34
+ title?: string;
35
+ /** The drawing's height in pixels; its width is the container's. */
36
+ height?: number;
37
+ };
38
+ /** Where each series' points stand in the drawing's own coordinates. */
39
+ export declare function computePhiSeriesChartGeometry(series: readonly PhiSeriesChartControlSeries[], pointCount: number): {
40
+ baseline: number;
41
+ top: number;
42
+ series: {
43
+ x: number;
44
+ y: number;
45
+ value: number;
46
+ }[][];
47
+ };
48
+ export declare function PhiSeriesChartControl({ keys, series, unit, locale, title, height, }: PhiSeriesChartControlProps): import("react").JSX.Element;
@@ -0,0 +1,82 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PHI_COLOR, PHI_FONT_SIZE, PHI_SPACE } from "../../theme/antd-css-var-contract";
3
+ const VIEW_WIDTH = 100;
4
+ const VIEW_HEIGHT = 100;
5
+ /* Room above the highest point and below the baseline, so the emphasized dot is not clipped. */
6
+ const VIEW_INSET = 8;
7
+ const SERIES_STROKES = [
8
+ { color: PHI_COLOR.primary },
9
+ { color: PHI_COLOR.textSecondary },
10
+ { color: PHI_COLOR.textTertiary, dash: "4 3" },
11
+ ];
12
+ function strokeOf(index) {
13
+ return SERIES_STROKES[index % SERIES_STROKES.length];
14
+ }
15
+ function finite(values) {
16
+ return values.filter((value) => Number.isFinite(value));
17
+ }
18
+ /** Where each series' points stand in the drawing's own coordinates. */
19
+ export function computePhiSeriesChartGeometry(series, pointCount) {
20
+ const all = series.flatMap((entry) => finite(entry.values.slice(0, pointCount)));
21
+ const low = Math.min(0, ...all);
22
+ const high = Math.max(0, ...all);
23
+ const range = high - low || 1;
24
+ const xOf = (index) => (pointCount <= 1 ? VIEW_WIDTH / 2 : (index / (pointCount - 1)) * VIEW_WIDTH);
25
+ const yOf = (value) => VIEW_INSET + (1 - (value - low) / range) * (VIEW_HEIGHT - 2 * VIEW_INSET);
26
+ return {
27
+ baseline: yOf(Math.max(low, 0)),
28
+ top: yOf(high),
29
+ series: series.map((entry) => entry.values.slice(0, pointCount).flatMap((value, index) => Number.isFinite(value) ? [{ x: xOf(index), y: yOf(value), value }] : [])),
30
+ };
31
+ }
32
+ function round(value) {
33
+ return Math.round(value * 100) / 100;
34
+ }
35
+ function linePath(points) {
36
+ return points.map((point, index) => `${index === 0 ? "M" : "L"}${round(point.x)} ${round(point.y)}`).join(" ");
37
+ }
38
+ export function PhiSeriesChartControl({ keys, series, unit, locale, title, height = 64, }) {
39
+ const pointCount = keys.length;
40
+ const geometry = computePhiSeriesChartGeometry(series, pointCount);
41
+ const numbers = new Intl.NumberFormat(locale);
42
+ const lastKey = keys[pointCount - 1];
43
+ const latest = series.flatMap((entry) => {
44
+ const value = entry.values[pointCount - 1];
45
+ return value !== undefined && Number.isFinite(value)
46
+ ? [`${entry.label} ${numbers.format(value)}${unit ? ` ${unit}` : ""}`]
47
+ : [];
48
+ });
49
+ /* What a reader who cannot see the lines is told: what it is, the latest day, each series' figure on it. */
50
+ const spoken = [title, lastKey && latest.length > 0 ? `${lastKey}: ${latest.join(", ")}` : null]
51
+ .filter(Boolean)
52
+ .join(" -- ");
53
+ const first = geometry.series[0] ?? [];
54
+ const area = first.length > 1
55
+ ? `${linePath(first)} L${round(first[first.length - 1].x)} ${round(geometry.baseline)} L${round(first[0].x)} ${round(geometry.baseline)} Z`
56
+ : null;
57
+ return (_jsxs("figure", { style: { margin: 0, display: "grid", gap: PHI_SPACE.xxs, minWidth: 0 }, "data-phi-series-chart": "", children: [_jsxs("div", { style: { position: "relative", height, minWidth: 0 }, children: [_jsxs("svg", { role: "img", "aria-label": spoken || undefined, viewBox: `0 0 ${VIEW_WIDTH} ${VIEW_HEIGHT}`, preserveAspectRatio: "none", width: "100%", height: height, style: { display: "block", overflow: "visible" }, children: [spoken ? _jsx("title", { children: spoken }) : null, _jsx("line", { x1: 0, x2: VIEW_WIDTH, y1: round(geometry.top), y2: round(geometry.top), vectorEffect: "non-scaling-stroke", strokeWidth: 1, strokeDasharray: "2 3", style: { stroke: PHI_COLOR.split } }), _jsx("line", { x1: 0, x2: VIEW_WIDTH, y1: round(geometry.baseline), y2: round(geometry.baseline), vectorEffect: "non-scaling-stroke", strokeWidth: 1, style: { stroke: PHI_COLOR.border } }), area ? (_jsx("path", { d: area, style: { fill: `color-mix(in srgb, ${PHI_COLOR.primary} 14%, transparent)`, stroke: "none" } })) : null, geometry.series.map((points, index) => points.length > 1 ? (_jsx("path", { d: linePath(points), fill: "none", vectorEffect: "non-scaling-stroke", strokeWidth: index === 0 ? 2 : 1.5, strokeLinejoin: "round", strokeLinecap: "round", ...(strokeOf(index).dash ? { strokeDasharray: strokeOf(index).dash } : {}), style: { stroke: strokeOf(index).color } }, index)) : null)] }), geometry.series.map((points, index) => {
58
+ /* The latest day's point, where the series has a figure for that day. */
59
+ const last = points[points.length - 1];
60
+ return last && Number.isFinite(series[index].values[pointCount - 1]) ? (_jsx("span", { "aria-hidden": "true", "data-phi-series-chart-last": "", style: {
61
+ position: "absolute",
62
+ left: `${round((last.x / VIEW_WIDTH) * 100)}%`,
63
+ top: `${round((last.y / VIEW_HEIGHT) * 100)}%`,
64
+ width: 8,
65
+ height: 8,
66
+ borderRadius: "50%",
67
+ transform: "translate(-50%, -50%)",
68
+ background: strokeOf(index).color,
69
+ boxShadow: `0 0 0 2px ${PHI_COLOR.bgContainer}`,
70
+ } }, index)) : null;
71
+ })] }), series.length > 1 || pointCount > 1 ? (_jsxs("figcaption", { "aria-hidden": "true", style: {
72
+ display: "grid",
73
+ gap: PHI_SPACE.xxs,
74
+ fontSize: PHI_FONT_SIZE.sm,
75
+ color: PHI_COLOR.textTertiary,
76
+ minWidth: 0,
77
+ }, children: [pointCount > 1 ? (_jsxs("span", { style: { display: "flex", justifyContent: "space-between", columnGap: PHI_SPACE.sm, minWidth: 0 }, children: [_jsx("span", { children: keys[0] }), _jsx("span", { children: lastKey })] })) : null, series.length > 1 ? (_jsx("span", { style: { display: "flex", flexWrap: "wrap", columnGap: PHI_SPACE.sm, minWidth: 0 }, children: series.map((entry, index) => (_jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: PHI_SPACE.xxs }, children: [_jsx("span", { style: {
78
+ display: "inline-block",
79
+ width: 10,
80
+ borderTop: `2px ${strokeOf(index).dash ? "dashed" : "solid"} ${strokeOf(index).color}`,
81
+ } }), entry.label] }, index))) })) : null] })) : null] }));
82
+ }
@@ -44,7 +44,10 @@ export type PhiTableControlColumn<TRow extends Record<string, unknown>> = {
44
44
  export type PhiTableControlPagination = {
45
45
  page: number;
46
46
  pageSize: number;
47
+ /** The rows the whole query has. Absent, the Table pages without a count: back, this page, next. */
47
48
  total?: number;
49
+ /** Without a count: whether a page follows this one. */
50
+ hasMore?: boolean;
48
51
  pageSizeOptions?: readonly number[];
49
52
  showSizeChanger?: boolean;
50
53
  onChange: (page: number, pageSize: number) => void;
@@ -1022,7 +1022,19 @@ export function PhiTableControl({ rows, rowIdentityPath, columns, columnOrder, s
1022
1022
  ...{ [PHI_VISUAL_ANCHOR_PART_ATTRIBUTE]: "pagination" },
1023
1023
  current: pagination.page,
1024
1024
  pageSize: pagination.pageSize,
1025
- total: pagination.total,
1025
+ ...(pagination.total === undefined
1026
+ ? {
1027
+ /*
1028
+ * No count: a total that reaches exactly one row past this page when another follows, and
1029
+ * only the current page's number drawn -- page numbers beyond it would be a guess.
1030
+ */
1031
+ total: pagination.page * pagination.pageSize + (pagination.hasMore ? 1 : 0),
1032
+ showLessItems: true,
1033
+ itemRender: (page, type, element) => type === "page" ? (page === pagination.page ? element : null)
1034
+ : type === "jump-prev" || type === "jump-next" ? null
1035
+ : element,
1036
+ }
1037
+ : { total: pagination.total }),
1026
1038
  pageSizeOptions: pagination.pageSizeOptions?.map(String),
1027
1039
  showSizeChanger: pagination.showSizeChanger,
1028
1040
  onChange: pagination.onChange,
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { Input } from "antd";
3
+ import { Input, theme } from "antd";
4
4
  import { PhiLabeledControl, usePhiControlLabel } from "./phi-labeled-control";
5
5
  import { PhiIcon } from "../shell/phi-icon";
6
6
  function resolveTextInputKind(inputType) {
@@ -22,12 +22,63 @@ function resolveTextInputKind(inputType) {
22
22
  return { type: "text", inputMode: "text" };
23
23
  }
24
24
  }
25
+ /*
26
+ * The first line of a text area, where its clear button belongs.
27
+ *
28
+ * Ant Design hangs the button absolutely from the box's top at `paddingXS`, which is the middle of the
29
+ * first line only for a text area at its own padding. A field restyled to stand in its text -- no
30
+ * padding, the text's own type, as the inline editors are -- had the cross half a line below its line.
31
+ * Where the field states its own top padding, the button is given one line of the field from there,
32
+ * with the cross in its middle: `1lh` in the field's type. A field at Ant Design's padding keeps
33
+ * Ant Design's place.
34
+ */
35
+ function readBlockStartPadding(style) {
36
+ const value = style?.paddingBlockStart ?? style?.paddingTop ?? style?.paddingBlock ?? style?.padding;
37
+ if (value == null)
38
+ return null;
39
+ if (typeof value === "number")
40
+ return `${value}px`;
41
+ // The first value of a shorthand, read past the spaces inside a `calc()`.
42
+ const text = String(value).trim();
43
+ let depth = 0;
44
+ for (let index = 0; index < text.length; index += 1) {
45
+ const character = text[index];
46
+ if (character === "(")
47
+ depth += 1;
48
+ else if (character === ")")
49
+ depth -= 1;
50
+ else if (depth === 0 && /\s/u.test(character ?? ""))
51
+ return text.slice(0, index);
52
+ }
53
+ return text || null;
54
+ }
55
+ function resolveTextAreaClearStyle(style, inputStyle) {
56
+ const fieldPadding = readBlockStartPadding(inputStyle);
57
+ if (fieldPadding == null)
58
+ return null;
59
+ const boxPadding = readBlockStartPadding(style) ?? "0px";
60
+ const type = inputStyle?.font != null
61
+ ? { font: inputStyle.font }
62
+ : {
63
+ fontSize: inputStyle?.fontSize ?? "inherit",
64
+ lineHeight: inputStyle?.lineHeight ?? "inherit",
65
+ fontFamily: inputStyle?.fontFamily ?? "inherit",
66
+ };
67
+ return {
68
+ ...type,
69
+ insetBlockStart: `calc(${boxPadding} + ${fieldPadding})`,
70
+ height: "1lh",
71
+ display: "flex",
72
+ alignItems: "center",
73
+ };
74
+ }
25
75
  export function PhiTextControl({ id, value, label, description, inputType = "text", presentation = "input", disabled = false, readOnly = false, allowClear = true, clearLabel, placeholder, ariaLabel, prefix, autoFocus, autoComplete, minLength, maxLength, rows, cols, autoSize, tabIndex, size, variant, inputRef, textareaRef, onChange, onPressEnter, onKeyDown, onFocus, onBlur, onClear, style, inputStyle, }) {
26
76
  const { labelId, labelledBy } = usePhiControlLabel(label, ariaLabel);
27
77
  const resolvedKind = resolveTextInputKind(inputType);
78
+ const { token } = theme.useToken();
28
79
  const stableAllowClear = {
29
80
  disabled: !allowClear,
30
- clearIcon: (_jsx(PhiIcon, { name: "close", size: "inherit", ariaLabel: clearLabel })),
81
+ clearIcon: (_jsx(PhiIcon, { name: "close", size: token.fontSizeIcon, ariaLabel: clearLabel })),
31
82
  };
32
83
  const commonProps = {
33
84
  ref: inputRef,
@@ -56,7 +107,10 @@ export function PhiTextControl({ id, value, label, description, inputType = "tex
56
107
  }
57
108
  let control;
58
109
  if (presentation === "textarea") {
59
- control = (_jsx(Input.TextArea, { ...commonProps, styles: inputStyle ? { textarea: inputStyle } : undefined, ref: textareaRef, allowClear: stableAllowClear, onClear: onClear, autoSize: autoSize, rows: rows, cols: cols, onPressEnter: onPressEnter }));
110
+ control = (_jsx(Input.TextArea, { ...commonProps, styles: {
111
+ ...(inputStyle ? { textarea: inputStyle } : {}),
112
+ ...(allowClear ? { clear: resolveTextAreaClearStyle(style, inputStyle) ?? undefined } : {}),
113
+ }, ref: textareaRef, allowClear: stableAllowClear, onClear: onClear, autoSize: autoSize, rows: rows, cols: cols, onPressEnter: onPressEnter }));
60
114
  }
61
115
  else if (presentation === "password") {
62
116
  control = (_jsx(Input.Password, { ...commonProps, styles: inputStyle ? { input: inputStyle } : undefined, allowClear: stableAllowClear, onClear: onClear, prefix: prefix, onPressEnter: onPressEnter }));
@@ -102,7 +102,7 @@ function PhiTreeNodeTitle({ node, presentation, editing, actions, controlSize, o
102
102
  beforeNodeIdentity: dropMode === "before" ? node.identity : null,
103
103
  afterNodeIdentity: dropMode === "after" ? node.identity : null,
104
104
  });
105
- }, children: [iconEditable ? (_jsx(PhiIconPickerControl, { value: typeof icon === "string" ? icon : null, buttonSize: controlSize, buttonType: "text", onChange: (value) => void onCommitField?.(node.identity, iconFieldKey, value) })) : presentation.showIcon !== false && typeof icon === "string" ? _jsx(PhiIcon, { name: icon }) : null, _jsxs("div", { style: { display: "grid", flex: 1, minWidth: 0 }, children: [titleEditable ? (_jsx(PhiTextControl, { value: draftTitle, size: controlSize, variant: presentation.node.titleEditor?.variant ?? "underlined", onChange: (value) => setDraftTitle(value ?? ""), onPressEnter: commitTitle, onBlur: commitTitle })) : _jsx("span", { children: title }), description != null && description !== "" ? (_jsx(Typography.Text, { type: "secondary", ellipsis: true, children: String(description) })) : null] }), actions.length ? (_jsx(Flex, { align: "center", gap: 2, onClick: (event) => event.stopPropagation(), children: actions.map((action) => (_jsx(PhiTreeActionButton, { action: action.execution === "link" && action.hrefPath
105
+ }, children: [iconEditable ? (_jsx(PhiIconPickerControl, { value: typeof icon === "string" ? icon : null, buttonSize: controlSize, buttonType: "text", onChange: (value) => void onCommitField?.(node.identity, iconFieldKey, value) })) : presentation.showIcon !== false && typeof icon === "string" ? _jsx(PhiIcon, { name: icon }) : null, _jsxs("div", { style: { display: "grid", flex: 1, minWidth: 0 }, children: [titleEditable ? (_jsx(PhiTextControl, { value: draftTitle, size: controlSize, variant: presentation.node.titleEditor?.variant ?? "underlined", onChange: (value) => setDraftTitle(value ?? ""), onPressEnter: commitTitle, onBlur: commitTitle })) : _jsx("span", { children: title }), description != null && description !== "" ? (_jsx(Typography.Text, { type: "secondary", ellipsis: true, style: { fontSize: "inherit" }, children: String(description) })) : null] }), actions.length ? (_jsx(Flex, { align: "center", gap: 2, onClick: (event) => event.stopPropagation(), children: actions.map((action) => (_jsx(PhiTreeActionButton, { action: action.execution === "link" && action.hrefPath
106
106
  ? { ...action, href: String(readPath(node.record, action.hrefPath) ?? "") }
107
107
  : action, size: controlSize, onActivate: () => onAction?.(action, node.identity) }, action.key))) })) : null] }));
108
108
  }
@@ -154,8 +154,8 @@ export function PhiTreeControl({ nodes, nodeIdentityPath, parentNodeIdentityPath
154
154
  padding: token.paddingSM,
155
155
  background: token.colorBgContainer,
156
156
  border: `1px solid ${token.colorBorderSecondary}`,
157
- // The frame is the surface; its corner is the Site's step, with the old token as the fallback
158
- // for a Tree rendered outside the Provider.
159
- borderRadius: "var(--phi-surface-radius, var(--ant-border-radius-lg))",
157
+ // The frame is the surface; its corner is the Site's step, declared by the Provider every Tree
158
+ // renders under (THEME.md, "Control shape").
159
+ borderRadius: "var(--phi-surface-radius)",
160
160
  }, children: tree })) : tree;
161
161
  }
@@ -0,0 +1,15 @@
1
+ import { type ReactElement, type ReactNode, type Ref } from "react";
2
+ /**
3
+ * A hover text that says what an ellipsis cut off, and only then.
4
+ *
5
+ * Around one element that cuts its own text short (`text-overflow: ellipsis`): pointed at, the element is
6
+ * measured, and the whole text shows in the house tooltip when it did not fit. An element whose text fits
7
+ * shows nothing -- a tooltip repeating the words that stand right there is noise. The child is the element
8
+ * itself, not a component, so the measuring reaches the box that cuts.
9
+ */
10
+ export declare function PhiTruncatedHoverText({ title, children, }: {
11
+ title: ReactNode;
12
+ children: ReactElement<{
13
+ ref?: Ref<HTMLElement>;
14
+ }>;
15
+ }): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { cloneElement, lazy, Suspense, useState } from "react";
4
+ /* The same Tooltip `PhiHoverText` loads, and loaded the same way: only where one is drawn. */
5
+ const PhiLazyTooltip = lazy(() => import("antd/es/tooltip"));
6
+ /** Whether an element shows less than its text: cut off at its end by an ellipsis, or by its height. */
7
+ function isPhiElementTruncated(element) {
8
+ return element != null
9
+ && (element.scrollWidth > element.clientWidth + 1 || element.scrollHeight > element.clientHeight + 1);
10
+ }
11
+ /**
12
+ * A hover text that says what an ellipsis cut off, and only then.
13
+ *
14
+ * Around one element that cuts its own text short (`text-overflow: ellipsis`): pointed at, the element is
15
+ * measured, and the whole text shows in the house tooltip when it did not fit. An element whose text fits
16
+ * shows nothing -- a tooltip repeating the words that stand right there is noise. The child is the element
17
+ * itself, not a component, so the measuring reaches the box that cuts.
18
+ */
19
+ export function PhiTruncatedHoverText({ title, children, }) {
20
+ // A callback ref held as state: the element is read when it is pointed at, never while rendering.
21
+ const [element, setElement] = useState(null);
22
+ const [open, setOpen] = useState(false);
23
+ const target = cloneElement(children, { ref: setElement });
24
+ return (_jsx(Suspense, { fallback: children, children: _jsx(PhiLazyTooltip, { title: title, open: open, onOpenChange: (next) => setOpen(next && isPhiElementTruncated(element)), children: target }) }));
25
+ }
@@ -1,4 +1,4 @@
1
- import { type ComponentType } from "react";
1
+ import type { ComponentType } from "react";
2
2
  import type { PhiFormFieldProviderProps } from "./form-provider-registry";
3
3
  /**
4
4
  * The `link-target` field: one link, to a Page of an Area or to an address elsewhere (`FORMS.md`).
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { useEffect, useState } from "react";
4
3
  import { isPhiCmsAreaKey } from "../../constants/cms-areas";
5
4
  import { PhiLinkTargetControl } from "../widgets/client/shared/phi-link-target-control";
5
+ import { usePhiPageReferenceOptions } from "../widgets/client/shared/use-page-reference-options";
6
6
  import { PHI_FORM_LINK_TARGET_LABEL_KEYS } from "./form-provider-contract";
7
7
  const DEFAULT_LABELS = {
8
8
  none: "None",
@@ -13,32 +13,6 @@ const DEFAULT_LABELS = {
13
13
  newTab: "Open in a new tab",
14
14
  pagesUnavailable: "The Pages could not be loaded.",
15
15
  };
16
- /*
17
- * One request per Area for as long as the page lives: the create and the edit dialog offer the same
18
- * Pages, and a list that changed meanwhile is a Page somebody published while this one was being written.
19
- * A failed request is forgotten, so the next dialog asks again.
20
- */
21
- const pageOptionsByArea = new Map();
22
- function loadPageOptions(area) {
23
- const cached = pageOptionsByArea.get(area);
24
- if (cached)
25
- return cached;
26
- const request = (async () => {
27
- const response = await fetch(`/api/site/page-references?area=${encodeURIComponent(area)}`, {
28
- cache: "no-store",
29
- credentials: "same-origin",
30
- headers: { accept: "application/json" },
31
- });
32
- const payload = (await response.json().catch(() => null));
33
- if (!response.ok || !Array.isArray(payload?.options)) {
34
- throw new Error(`Page references for "${area}" answered ${response.status}.`);
35
- }
36
- return payload.options;
37
- })();
38
- pageOptionsByArea.set(area, request);
39
- request.catch(() => pageOptionsByArea.delete(area));
40
- return request;
41
- }
42
16
  /**
43
17
  * The `link-target` field: one link, to a Page of an Area or to an address elsewhere (`FORMS.md`).
44
18
  *
@@ -48,24 +22,7 @@ function loadPageOptions(area) {
48
22
  */
49
23
  export const PhiLinkTargetFormControl = ({ field, value, labels, disabled, readOnly, onChange, }) => {
50
24
  const area = isPhiCmsAreaKey(field.config?.area) ? field.config.area : "public";
51
- const [options, setOptions] = useState([]);
52
- const [failed, setFailed] = useState(false);
53
- useEffect(() => {
54
- let current = true;
55
- loadPageOptions(area).then((next) => {
56
- if (current) {
57
- setOptions(next);
58
- setFailed(false);
59
- }
60
- }, (error) => {
61
- console.warn("[phi-form] Page references could not be loaded.", { area, error });
62
- if (current)
63
- setFailed(true);
64
- });
65
- return () => {
66
- current = false;
67
- };
68
- }, [area]);
25
+ const { options, failed } = usePhiPageReferenceOptions(area, "phi-form");
69
26
  const read = (key, fallback) => labels?.[key]?.trim() || fallback;
70
27
  const controlLabels = {
71
28
  none: read(PHI_FORM_LINK_TARGET_LABEL_KEYS.none, DEFAULT_LABELS.none),
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { createContext, Fragment, useContext, useEffect, useMemo, useRef, useState, } from "react";
4
- import { PHI_COLOR, PHI_SPACE } from "../../theme/antd-css-var-contract";
4
+ import { PHI_FONT_SIZE, PHI_COLOR, PHI_SPACE } from "../../theme/antd-css-var-contract";
5
5
  import { PHI_THEME_BORDER_WIDTH } from "../../helpers/border-widget-style";
6
6
  import { normalizePhiCssSize, PHI_LAYOUT_SURFACE_RADIUS, PHI_SLOT_INLINE_PLACEMENT_MARGIN_STYLE, } from "../layouts/phi-layout-contract";
7
7
  import { PhiButtonControl } from "../controls/phi-button-control";
@@ -109,7 +109,9 @@ export function PhiFormWidgetFrame({ submit, card, maxFormWidth, links, layout,
109
109
  * Widget rather than beside it: a Widget in the next slot stands in the Layout's box, not on the
110
110
  * form's grid, and no percentage of the outer width lands on that column once the grid has gaps.
111
111
  */
112
- const linksRow = links && links.length > 0 ? (_jsx("div", { className: "phi-form-descriptor-actions", children: _jsx("div", { className: "phi-form-cell phi-form-cell--control", style: { display: "flex", flexWrap: "wrap", alignItems: "center", gap: PHI_SPACE.sm }, children: links.map((link, index) => (_jsxs(Fragment, { children: [index > 0 ? _jsx("span", { "aria-hidden": true, style: { color: PHI_COLOR.textTertiary }, children: "|" }) : null, _jsx(PhiLink, { href: link.href, children: link.label })] }, link.key))) }) })) : null;
112
+ const linksRow = links && links.length > 0 ? (_jsx("div", { className: "phi-form-descriptor-actions", children: _jsx("div", { className: "phi-form-cell phi-form-cell--control",
113
+ // The ways out are asides to the form, taken now and then, so they write small (`sm`).
114
+ style: { display: "flex", flexWrap: "wrap", alignItems: "center", gap: PHI_SPACE.sm, fontSize: PHI_FONT_SIZE.sm }, children: links.map((link, index) => (_jsxs(Fragment, { children: [index > 0 ? _jsx("span", { "aria-hidden": true, style: { color: PHI_COLOR.textTertiary }, children: "|" }) : null, _jsx(PhiLink, { href: link.href, children: link.label })] }, link.key))) }) })) : null;
113
115
  /*
114
116
  * The ways out come first, then the button.
115
117
  *
@@ -31,11 +31,8 @@ placeholderRows = 1) {
31
31
  * Control line, which on the one-row placeholder is exactly the capsule and on a taller one is the
32
32
  * capsule held -- the same answer the field itself will give when it arrives, so the shape does not
33
33
  * change under the reader at the moment the chunk lands.
34
- *
35
- * Here the fallback is the plain surface radius and not the large one: this is an ordinary `div`,
36
- * not an Ant Design Control, so nothing has redefined `--ant-border-radius` on it.
37
34
  */
38
- borderRadius: "var(--phi-control-radius-grown-md, var(--ant-border-radius))",
35
+ borderRadius: "var(--phi-control-radius-grown-md)",
39
36
  background: "var(--ant-color-fill-quaternary)",
40
37
  } }));
41
38
  return function PhiLazyFormFieldControl(props) {
@@ -75,6 +75,8 @@ function renderColumn(isAuthoringRender, key, child, width, flex, anchor, slotRo
75
75
  width: "100%",
76
76
  } })) : null] }, key));
77
77
  }
78
+ /** How much faster the middle slot grows than each balanced side. */
79
+ const PHI_THREE_COLUMN_MIDDLE_GROW = 2;
78
80
  export function PhiThreeColumnLayout({ blockId, slots, balancedSides = true, gap = PHI_THREE_COLUMN_LAYOUT_DEFAULTS.gap, wrap = PHI_THREE_COLUMN_LAYOUT_DEFAULTS.wrap, align, justify, leftWidth, middleWidth, rightWidth, editSlotAnchor = "center", editSlotAction, editRenderInsertControl, renderMode = "live", ...layoutProps }) {
79
81
  const isAuthoringRender = isPhiLayoutAuthoringRender({ editSlotAction, renderMode });
80
82
  const { style, layoutKind = "threecol", padding, paddingTop, paddingRight, paddingBottom, paddingLeft, surface, labelEnd, size, minSize, maxSize, collapsedSizeHint, } = layoutProps;
@@ -83,7 +85,14 @@ export function PhiThreeColumnLayout({ blockId, slots, balancedSides = true, gap
83
85
  const resolvedMiddleWidth = normalizePhiCssSize(middleWidth);
84
86
  const resolvedRightWidth = normalizePhiCssSize(rightWidth);
85
87
  const leftFlex = resolvedLeftWidth ? `0 0 ${resolvedLeftWidth}` : balancedSides ? "1 1 0" : "0 1 auto";
86
- const middleFlex = resolvedMiddleWidth ? `0 0 ${resolvedMiddleWidth}` : "1 1 0";
88
+ /*
89
+ * The middle is the main thing and takes the larger share: with balanced sides it grows twice as fast as
90
+ * each side, so the sides stay equal to each other and the middle stays centred; without, it takes what
91
+ * the sides' content leaves.
92
+ */
93
+ const middleFlex = resolvedMiddleWidth
94
+ ? `0 0 ${resolvedMiddleWidth}`
95
+ : `${PHI_THREE_COLUMN_MIDDLE_GROW} 1 0`;
87
96
  const rightFlex = resolvedRightWidth ? `0 0 ${resolvedRightWidth}` : balancedSides ? "1 1 0" : "0 1 auto";
88
97
  const showInsertButton = resolvedRenderMode === "editor";
89
98
  const { style: resolvedLayoutStyle, ground, hasExplicitLayoutBackground, } = resolvePhiBaseLayoutChrome({
@@ -3,9 +3,9 @@ import { useEffect, useMemo } from "react";
3
3
  import { PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS } from "../../constants/media-library-provider-keys";
4
4
  import { usePhiCollectionProvider } from "../widgets/client/shared/phi-collection-provider";
5
5
  import { usePhiMediaSpaceSelectionAllowed } from "./media-space-selection";
6
+ import { readPhiMediaAssetTiles, readPhiMediaFolders, readPhiMediaPagination, readPhiMediaSpace, readPhiMediaSpaces, } from "./phi-image-preview-data";
6
7
  import { PHI_ASSET_CONTROLLER_STORE_KEY, } from "./asset-controller-signals";
7
8
  import { setPhiImagePreviewError, setPhiImagePreviewLoading, setPhiImagePreviewResults, usePhiImagePreviewStore, } from "./phi-image-preview-store";
8
- import { isPhiRecord } from "../../helpers/is-record";
9
9
  export const PHI_ASSET_COLLECTION_DATA_SOURCE = {
10
10
  providerKey: PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS.collection,
11
11
  resourceKey: "assets",
@@ -29,22 +29,12 @@ export function buildPhiAssetCollectionQuery(state) {
29
29
  };
30
30
  }
31
31
  export function applyPhiAssetCollectionData(data, resolvedCollectionRequestKey) {
32
- const folders = Array.isArray(data.meta?.folders)
33
- ? data.meta.folders.filter(isPhiRecord)
34
- : [];
35
- const pagination = isPhiRecord(data.meta?.pagination)
36
- ? data.meta.pagination
37
- : null;
38
32
  setPhiImagePreviewResults(PHI_ASSET_CONTROLLER_STORE_KEY, {
39
- activeSpace: isPhiRecord(data.meta?.activeSpace)
40
- ? data.meta.activeSpace
41
- : null,
42
- spaces: Array.isArray(data.meta?.spaces)
43
- ? data.meta.spaces.filter(isPhiRecord)
44
- : [],
45
- assets: data.items.filter(isPhiRecord),
46
- folders,
47
- pagination,
33
+ activeSpace: readPhiMediaSpace(data.meta?.activeSpace),
34
+ spaces: readPhiMediaSpaces(data.meta?.spaces),
35
+ assets: readPhiMediaAssetTiles(data.items),
36
+ folders: readPhiMediaFolders(data.meta?.folders),
37
+ pagination: readPhiMediaPagination(data.meta?.pagination),
48
38
  resolvedCollectionRequestKey,
49
39
  });
50
40
  }
@@ -0,0 +1,3 @@
1
+ import { type PhiFontMetrics } from "@phis/contracts/media";
2
+ import { type PhiConfigReader } from "../widgets/config/strict-config-reader";
3
+ export declare function readPhiFontMetricsFields(reader: PhiConfigReader): PhiFontMetrics;
@@ -0,0 +1,43 @@
1
+ import { isPhiFontSubsetKey } from "@phis/contracts/media";
2
+ import { requirePhiField, } from "../widgets/config/strict-config-reader";
3
+ /*
4
+ * A font's metrics, read as the contract states them (`@phis/contracts/media`).
5
+ *
6
+ * The server measures them once, at upload, and writes every field. Two readers took them on trust: the
7
+ * public reference projection checked nothing below `font`, and the font list of the Theme took a stored
8
+ * `meta.font` for metrics as soon as it had a unit grid -- a record missing its ascent would have set a
9
+ * fallback face with `NaN` overrides. Both read through this now, so a wrong record is an error naming
10
+ * the field. `coverage` is the one field allowed to be absent: a font uploaded before cuts existed has
11
+ * none and is delivered whole.
12
+ */
13
+ const nullableNumber = (reader, key) => reader.number(key) ?? null;
14
+ const requiredNumber = (reader, key) => requirePhiField(reader, key, "a number", reader.number);
15
+ function readPhiFontCoverage(reader) {
16
+ const subsets = requirePhiField(reader, "subsets", "a list of subset keys", (key) => reader.value(key, "a list of subset keys", (value) => Array.isArray(value) && value.every(isPhiFontSubsetKey) ? value : undefined));
17
+ const restRanges = reader.value("restRanges", "a list of [start, end] code point ranges", (value) => Array.isArray(value) && value.every((entry) => Array.isArray(entry) && entry.length === 2 &&
18
+ Number.isInteger(entry[0]) && Number.isInteger(entry[1]) &&
19
+ entry[0] >= 0 && entry[0] <= entry[1] && entry[1] <= 0x10ffff)
20
+ ? value
21
+ : undefined) ?? [];
22
+ return { subsets, restRanges };
23
+ }
24
+ export function readPhiFontMetricsFields(reader) {
25
+ const unitsPerEm = requiredNumber(reader, "unitsPerEm");
26
+ if (unitsPerEm <= 0)
27
+ reader.fail("unitsPerEm", "a positive number");
28
+ const coverage = reader.object("coverage", readPhiFontCoverage);
29
+ return {
30
+ format: requirePhiField(reader, "format", "a font container", (key) => reader.oneOf(key, ["sfnt", "woff", "woff2"])),
31
+ familyName: reader.string("familyName")?.trim() || null,
32
+ unitsPerEm,
33
+ ascent: requiredNumber(reader, "ascent"),
34
+ descent: requiredNumber(reader, "descent"),
35
+ lineGap: requiredNumber(reader, "lineGap"),
36
+ capHeight: nullableNumber(reader, "capHeight"),
37
+ xHeight: nullableNumber(reader, "xHeight"),
38
+ xWidthAvg: nullableNumber(reader, "xWidthAvg"),
39
+ xWidthAvgSource: reader.oneOf("xWidthAvgSource", ["glyphs", "os2"]) ?? null,
40
+ category: reader.oneOf("category", ["serif", "sans-serif", "monospace"]) ?? null,
41
+ ...(coverage ? { coverage } : {}),
42
+ };
43
+ }