@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
@@ -39,7 +39,18 @@ export function materializePhiWidgetRuntimeControllerSettings({ widget, tree, ow
39
39
  if (!plugin.requiredRuntimeControllers) {
40
40
  return [];
41
41
  }
42
- const config = plugin.parseConfig(widget.config);
42
+ /*
43
+ * A Widget whose config cannot be read asks for no Controller. It is not rendered either: the renderer
44
+ * meets the same error in the Widget's own place, leaves it out of a live page and shows it in the
45
+ * Builder. Thrown here, outside any block, it took the whole page with it.
46
+ */
47
+ let config;
48
+ try {
49
+ config = plugin.parseConfig(widget.config);
50
+ }
51
+ catch {
52
+ return [];
53
+ }
43
54
  const settingsByKey = new Map();
44
55
  for (const requirement of plugin.requiredRuntimeControllers({ widget, tree, config })) {
45
56
  const setting = {
@@ -17,6 +17,7 @@ declare const PHI_ANTD_ICON_LOADERS: {
17
17
  readonly CalendarOutlined: () => Promise<typeof import("@ant-design/icons-svg/es/asn/CalendarOutlined")>;
18
18
  readonly CheckOutlined: () => Promise<typeof import("@ant-design/icons-svg/es/asn/CheckOutlined")>;
19
19
  readonly CloseOutlined: () => Promise<typeof import("@ant-design/icons-svg/es/asn/CloseOutlined")>;
20
+ readonly CloudDownloadOutlined: () => Promise<typeof import("@ant-design/icons-svg/es/asn/CloudDownloadOutlined")>;
20
21
  readonly CloudUploadOutlined: () => Promise<typeof import("@ant-design/icons-svg/es/asn/CloudUploadOutlined")>;
21
22
  readonly ClusterOutlined: () => Promise<typeof import("@ant-design/icons-svg/es/asn/ClusterOutlined")>;
22
23
  readonly CodeOutlined: () => Promise<typeof import("@ant-design/icons-svg/es/asn/CodeOutlined")>;
@@ -38,6 +38,7 @@ const PHI_ANTD_ICON_LOADERS = {
38
38
  CalendarOutlined: () => import("@ant-design/icons-svg/es/asn/CalendarOutlined"),
39
39
  CheckOutlined: () => import("@ant-design/icons-svg/es/asn/CheckOutlined"),
40
40
  CloseOutlined: () => import("@ant-design/icons-svg/es/asn/CloseOutlined"),
41
+ CloudDownloadOutlined: () => import("@ant-design/icons-svg/es/asn/CloudDownloadOutlined"),
41
42
  CloudUploadOutlined: () => import("@ant-design/icons-svg/es/asn/CloudUploadOutlined"),
42
43
  ClusterOutlined: () => import("@ant-design/icons-svg/es/asn/ClusterOutlined"),
43
44
  CodeOutlined: () => import("@ant-design/icons-svg/es/asn/CodeOutlined"),
@@ -327,6 +328,8 @@ export const PHI_ANTD_ICON_NAMES = {
327
328
  "video-camera-two-tone": "VideoCameraTwoTone",
328
329
  warning: "WarningOutlined",
329
330
  "warning-outlined": "WarningOutlined",
331
+ "cloud-download": "CloudDownloadOutlined",
332
+ "cloud-download-outlined": "CloudDownloadOutlined",
330
333
  "cloud-upload": "CloudUploadOutlined",
331
334
  "cloud-upload-outlined": "CloudUploadOutlined",
332
335
  "eye-invisible": "EyeInvisibleOutlined",
@@ -1,32 +1,35 @@
1
1
  "use client";
2
2
  import { useEffect } from "react";
3
+ import { PHI_SHELL_STICKY_TOP_PROPERTY } from "../root/phi-shell-chrome-overlay";
3
4
  /**
4
5
  * How far down the viewport the Shell's Header holds right now, published on the Shell.
5
6
  *
6
7
  * Something that sticks to the top of the page -- a Table's header row -- has to stick under the Header
7
8
  * rather than behind it, and where the Header ends is not a constant: each band sticks or scrolls by its
8
9
  * own config, and a revealing `header_top` slides away while the visitor reads down and comes back when
9
- * they scroll up. So the edge is measured, not configured: the lowest bottom of every Header band that is
10
- * stuck at this moment, written as `--phi-shell-sticky-top` on the Shell element. Unset, a reader falls
10
+ * they scroll up. So the edge is measured, not configured: the lowest visible bottom of the Header bands,
11
+ * stuck or in the flow, written as `--phi-shell-sticky-top` on the Shell element. Unset, a reader falls
11
12
  * back to `0px`, which is right for a Shell without a sticky Header.
12
13
  *
13
- * Run by whatever sticks under it -- every sticky Table does -- so a page without one loads nothing for
14
- * it. Each runner measures all the bands and they all write the same value. Not inside an Overlay: a
15
- * Modal's or Drawer's body scrolls on its own, and its top edge is the line.
14
+ * Run by whatever sticks under it -- every sticky Table does, and the Region renderer for a stuck Sider
15
+ * (`phiCmsRegionSiderSticks`) -- so a page without one loads nothing for it. Each runner measures all the
16
+ * bands and they all write the same value. Not inside an Overlay: a Modal's or Drawer's body scrolls on
17
+ * its own, and its top edge is the line.
16
18
  */
17
- const PHI_SHELL_STICKY_TOP_PROPERTY = "--phi-shell-sticky-top";
18
19
  const HEADER_BANDS = '[data-phi-region-key^="header_"]:not([hidden])';
19
20
  function measurePhiShellStickyTop(shell) {
21
+ /*
22
+ * The lowest visible bottom of the Header bands, stuck or not. Counting only the bands stuck at this
23
+ * moment made the edge jump: a band leaving its sticking line for its place in the flow -- the main
24
+ * band while the top band scrolls back in above it -- dropped out of the count at once, and whatever
25
+ * stuck under it leapt up by the band's height. Counted while it is in view, the edge moves with the
26
+ * scroll instead. A band in the flow stands above the content anyway, so nothing sticks too low for it.
27
+ */
20
28
  let bottom = 0;
21
29
  for (const band of shell.querySelectorAll(HEADER_BANDS)) {
22
- const style = window.getComputedStyle(band);
23
- if (style.position !== "sticky" && style.position !== "fixed") {
24
- continue;
25
- }
26
30
  const rect = band.getBoundingClientRect();
27
- const stuckAt = Number.parseFloat(style.top) || 0;
28
- // Below its sticking line it is still in flow; above the viewport it has slid away.
29
- if (rect.height === 0 || rect.top > stuckAt + 1 || rect.bottom <= 0) {
31
+ // Empty, or scrolled or slid out of the viewport.
32
+ if (rect.height === 0 || rect.bottom <= 0) {
30
33
  continue;
31
34
  }
32
35
  bottom = Math.max(bottom, rect.bottom);
@@ -57,10 +60,46 @@ export function usePhiShellStickyTop({ active, elementRef, }) {
57
60
  }
58
61
  };
59
62
  publish();
63
+ /*
64
+ * A revealing band moves by a transition. Measured only where it settled, whatever sticks under it
65
+ * would stand still while the band slides and then jump; so while a transition runs in the Shell the
66
+ * edge is measured every frame, and once more where it ends.
67
+ */
68
+ let following = 0;
69
+ let followFrame = 0;
70
+ const follow = () => {
71
+ publish();
72
+ followFrame = following > 0 ? window.requestAnimationFrame(follow) : 0;
73
+ };
74
+ // Only a Header band's own transitions move the edge; a button's hover elsewhere in the Shell does not.
75
+ const isBandTransition = (event) => event.target instanceof Element && event.target.closest(HEADER_BANDS) !== null;
76
+ // A transition whose element leaves the page never ends; the follow stops on its own regardless.
77
+ let followTimeout = 0;
78
+ const stopFollowing = () => {
79
+ following = 0;
80
+ followTimeout = 0;
81
+ };
82
+ const transitionStarted = (event) => {
83
+ if (!isBandTransition(event))
84
+ return;
85
+ following += 1;
86
+ window.clearTimeout(followTimeout);
87
+ followTimeout = window.setTimeout(stopFollowing, 2000);
88
+ if (followFrame === 0) {
89
+ followFrame = window.requestAnimationFrame(follow);
90
+ }
91
+ };
92
+ const transitionStopped = (event) => {
93
+ if (!isBandTransition(event))
94
+ return;
95
+ following = Math.max(0, following - 1);
96
+ schedule();
97
+ };
60
98
  window.addEventListener("scroll", schedule, { passive: true });
61
99
  window.addEventListener("resize", schedule);
62
- // A revealing band moves by a transition; its end is where it settled.
63
- shell.addEventListener("transitionend", schedule);
100
+ shell.addEventListener("transitionrun", transitionStarted);
101
+ shell.addEventListener("transitionend", transitionStopped);
102
+ shell.addEventListener("transitioncancel", transitionStopped);
64
103
  const observer = new ResizeObserver(schedule);
65
104
  for (const band of shell.querySelectorAll(HEADER_BANDS)) {
66
105
  observer.observe(band);
@@ -69,9 +108,15 @@ export function usePhiShellStickyTop({ active, elementRef, }) {
69
108
  if (frame !== 0) {
70
109
  window.cancelAnimationFrame(frame);
71
110
  }
111
+ if (followFrame !== 0) {
112
+ window.cancelAnimationFrame(followFrame);
113
+ }
114
+ window.clearTimeout(followTimeout);
72
115
  window.removeEventListener("scroll", schedule);
73
116
  window.removeEventListener("resize", schedule);
74
- shell.removeEventListener("transitionend", schedule);
117
+ shell.removeEventListener("transitionrun", transitionStarted);
118
+ shell.removeEventListener("transitionend", transitionStopped);
119
+ shell.removeEventListener("transitioncancel", transitionStopped);
75
120
  observer.disconnect();
76
121
  };
77
122
  }, [active, elementRef]);
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { useRef, useSyncExternalStore } from "react";
2
+ import { useCallback, useRef, useSyncExternalStore } from "react";
3
3
  const scopedStateStoreKey = "__phiScopedStateStores__";
4
4
  function normalizeScopedStateKey(scopeKey) {
5
5
  return scopeKey.trim() || "default";
@@ -92,7 +92,12 @@ export function createScopedStateStore(storeId, createDefaultState) {
92
92
  }
93
93
  function useStore(scopeKey) {
94
94
  const normalizedScopeKey = normalizeScopedStateKey(scopeKey);
95
- return useSyncExternalStore((listener) => subscribe(normalizedScopeKey, listener), () => getSnapshot(normalizedScopeKey), () => getHydrationSnapshot(normalizedScopeKey));
95
+ /*
96
+ * Held per scope. An inline `subscribe` is a new function every render, and `useSyncExternalStore`
97
+ * unsubscribes and subscribes again for each new one.
98
+ */
99
+ const subscribeToScope = useCallback((listener) => subscribe(normalizedScopeKey, listener), [normalizedScopeKey]);
100
+ return useSyncExternalStore(subscribeToScope, () => getSnapshot(normalizedScopeKey), () => getHydrationSnapshot(normalizedScopeKey));
96
101
  }
97
102
  function useStoreSelector(scopeKey, selector) {
98
103
  const normalizedScopeKey = normalizeScopedStateKey(scopeKey);
@@ -124,14 +129,26 @@ export function createScopedStateStore(storeId, createDefaultState) {
124
129
  selectedHydrationSnapshotRef.current = { source, selector, selected };
125
130
  return selected;
126
131
  };
127
- return useSyncExternalStore((listener) => subscribe(normalizedScopeKey, listener), getSelectedSnapshot, getSelectedHydrationSnapshot);
132
+ const subscribeToScope = useCallback((listener) => subscribe(normalizedScopeKey, listener), [normalizedScopeKey]);
133
+ return useSyncExternalStore(subscribeToScope, getSelectedSnapshot, getSelectedHydrationSnapshot);
128
134
  }
129
135
  function reset(scopeKey) {
130
136
  patch(scopeKey, () => createDefaultState(normalizeScopedStateKey(scopeKey)));
131
137
  }
138
+ /*
139
+ * Forgets a scope's state. A component still mounted on it keeps its subscription on the entry it
140
+ * subscribed to, so dropping that entry left it listening to nothing: the next write went into a new
141
+ * entry nobody was told about. With listeners, the entry stays and starts over from the default
142
+ * instead, and they hear it.
143
+ */
132
144
  function deleteScope(scopeKey) {
133
145
  const normalizedScopeKey = normalizeScopedStateKey(scopeKey);
134
146
  const bucket = getScopedBucket();
147
+ const entry = bucket.get(normalizedScopeKey);
148
+ if (entry && entry.listeners.size > 0) {
149
+ replace(normalizedScopeKey, createDefaultState(normalizedScopeKey));
150
+ return;
151
+ }
135
152
  bucket.delete(normalizedScopeKey);
136
153
  }
137
154
  return {
@@ -16,3 +16,16 @@ export declare function PhiSurfaceTone({ tone, children, }: {
16
16
  tone: PhiSurfaceToneValue | null | undefined;
17
17
  children: ReactNode;
18
18
  }): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
19
+ /**
20
+ * The page's own mode around something that belongs to the page's tools rather than to its content.
21
+ *
22
+ * The Builder draws its scaffolds -- tool bars, drag handles, the effects preview -- inside the Surfaces
23
+ * they edit, and a toned Surface would turn them with its content. This turns them back: inside a tone
24
+ * scope it mounts the page's mode again (its Ant Design components take it by themselves), outside one it
25
+ * renders the children as they are. `className` is the class an element of the caller's that reads the
26
+ * Theme's variables itself must carry, so they resolve in the page's mode there too; empty outside a tone.
27
+ */
28
+ export declare function usePhiPageModeTone(): {
29
+ className: string | undefined;
30
+ wrap: (children: ReactNode) => ReactNode;
31
+ };
@@ -34,3 +34,24 @@ export function PhiSurfaceTone({ tone, children, }) {
34
34
  }
35
35
  return (_jsx(Suspense, { fallback: null, children: _jsx(PhiSurfaceToneScope, { mode: target, cssVarKey: cssVarKey, source: source, children: children }) }));
36
36
  }
37
+ /**
38
+ * The page's own mode around something that belongs to the page's tools rather than to its content.
39
+ *
40
+ * The Builder draws its scaffolds -- tool bars, drag handles, the effects preview -- inside the Surfaces
41
+ * they edit, and a toned Surface would turn them with its content. This turns them back: inside a tone
42
+ * scope it mounts the page's mode again (its Ant Design components take it by themselves), outside one it
43
+ * renders the children as they are. `className` is the class an element of the caller's that reads the
44
+ * Theme's variables itself must carry, so they resolve in the page's mode there too; empty outside a tone.
45
+ */
46
+ export function usePhiPageModeTone() {
47
+ const { mode } = usePhiConfig();
48
+ const source = usePhiThemeToneSource();
49
+ const pageMode = source?.pageMode ?? mode;
50
+ if (pageMode === mode) {
51
+ return { className: undefined, wrap: (children) => children };
52
+ }
53
+ return {
54
+ className: resolvePhiSurfaceToneClassName(pageMode),
55
+ wrap: (children) => _jsx(PhiSurfaceTone, { tone: pageMode, children: children }),
56
+ };
57
+ }
@@ -22,7 +22,8 @@ export declare function usePhiTableBinding({ source, initialQuery, externalQuery
22
22
  resource: PhiTableProviderResourceDescriptor | null;
23
23
  bindingError: string | null;
24
24
  rows: readonly TableRow[];
25
- total: number;
25
+ total: number | null;
26
+ hasMore: boolean;
26
27
  summary: Readonly<Record<string, PhiTableSummaryValue>>;
27
28
  contractError: string | null;
28
29
  loading: boolean;
@@ -76,7 +76,14 @@ export function usePhiTableBinding({ source, initialQuery = {}, externalQuery, f
76
76
  expandedRowIdentities: initialQuery.expandedRowIdentities,
77
77
  }));
78
78
  const [rows, setRows] = useState([]);
79
- const [total, setTotal] = useState(0);
79
+ /*
80
+ * The number of rows the whole query has, where the Provider counts them (`query.total` on its
81
+ * resource), and `null` where it does not. Counting is a query of its own on a large table, so a
82
+ * Provider answers it only where a Table shows it; the rows on this page are never put in its place.
83
+ */
84
+ const [total, setTotal] = useState(null);
85
+ /** Whether a page follows this one, for a Table that pages without a count. */
86
+ const [hasMore, setHasMore] = useState(false);
80
87
  const [summary, setSummary] = useState({});
81
88
  const [queryLoading, setQueryLoading] = useState(Boolean(source));
82
89
  const [pendingMutationTargets, setPendingMutationTargets] = useState(() => new Set());
@@ -102,6 +109,30 @@ export function usePhiTableBinding({ source, initialQuery = {}, externalQuery, f
102
109
  controller.abort();
103
110
  mutationControllers.current.clear();
104
111
  }, []);
112
+ /*
113
+ * A new source starts from nothing, and it does so in the render that brought it.
114
+ *
115
+ * The reset used to run in a microtask after the effects, so the query effect below had already
116
+ * fetched the new source with the old page and cursor -- one request thrown away on every source or
117
+ * `bindingParamsChange`. Adjusting state while rendering is React's way to follow a prop: the effects
118
+ * of this render see page 1 already. It also leaves the first render alone, so an `initialQuery` page
119
+ * and `initialExpandedRowIdentities` are no longer reset away on mount.
120
+ */
121
+ const [renderedSourceKey, setRenderedSourceKey] = useState(sourceKey);
122
+ if (renderedSourceKey !== sourceKey) {
123
+ setRenderedSourceKey(sourceKey);
124
+ setPendingMutationTargets(new Set());
125
+ setRows([]);
126
+ setTotal(null);
127
+ setHasMore(false);
128
+ setSummary({});
129
+ setSelectedRowIdentities([]);
130
+ setExpandedRowIdentities([]);
131
+ setQuery((current) => ({ ...current, page: 1, cursor: null }));
132
+ setQueryLoading(sourceKey !== "null");
133
+ setError(null);
134
+ }
135
+ // What a render cannot reset: requests in flight and the bookkeeping kept beside state.
105
136
  useEffect(() => {
106
137
  for (const controller of mutationControllers.current)
107
138
  controller.abort();
@@ -109,21 +140,6 @@ export function usePhiTableBinding({ source, initialQuery = {}, externalQuery, f
109
140
  latestMutationByTarget.current.clear();
110
141
  pendingMutationCountsByTarget.current.clear();
111
142
  rowsRef.current = [];
112
- let cancelled = false;
113
- queueMicrotask(() => {
114
- if (cancelled)
115
- return;
116
- setPendingMutationTargets(new Set());
117
- setRows([]);
118
- setTotal(0);
119
- setSummary({});
120
- setSelectedRowIdentities([]);
121
- setExpandedRowIdentities([]);
122
- setQuery((current) => ({ ...current, page: 1, cursor: null }));
123
- setQueryLoading(sourceKey !== "null");
124
- setError(null);
125
- });
126
- return () => { cancelled = true; };
127
143
  }, [sourceKey]);
128
144
  /*
129
145
  * Held by content, not identity: a caller that writes the object inline hands in a new one every
@@ -158,7 +174,8 @@ export function usePhiTableBinding({ source, initialQuery = {}, externalQuery, f
158
174
  if (data.rows.some((row) => readPhiTableRowIdentity(row, resource.rowIdentityPath) == null)) {
159
175
  setRows([]);
160
176
  rowsRef.current = [];
161
- setTotal(0);
177
+ setTotal(null);
178
+ setHasMore(false);
162
179
  setSummary({});
163
180
  setQueryLoading(false);
164
181
  setError(new PhiTableProviderError("missing-row-identity", `Table Provider row is missing identity field "${resource.rowIdentityPath}".`));
@@ -168,7 +185,8 @@ export function usePhiTableBinding({ source, initialQuery = {}, externalQuery, f
168
185
  if (summaryError) {
169
186
  setRows([]);
170
187
  rowsRef.current = [];
171
- setTotal(0);
188
+ setTotal(null);
189
+ setHasMore(false);
172
190
  setSummary({});
173
191
  setQueryLoading(false);
174
192
  setError(new PhiTableProviderError("invalid-summary", summaryError));
@@ -176,7 +194,11 @@ export function usePhiTableBinding({ source, initialQuery = {}, externalQuery, f
176
194
  }
177
195
  rowsRef.current = data.rows;
178
196
  setRows(data.rows);
179
- setTotal(data.total ?? data.rows.length);
197
+ // A resource that does not page answers every row at once, so its rows are its count.
198
+ const pages = resource.query.pagination === "offset" || resource.query.pagination === "cursor";
199
+ setTotal(data.total ?? (pages ? null : data.rows.length));
200
+ const pageSize = data.pageSize ?? resolvedQuery.pageSize;
201
+ setHasMore(data.nextCursor != null || (data.total == null && pageSize != null && data.rows.length >= pageSize));
180
202
  setSummary(data.summary ?? {});
181
203
  setQueryLoading(false);
182
204
  setError(null);
@@ -188,7 +210,7 @@ export function usePhiTableBinding({ source, initialQuery = {}, externalQuery, f
188
210
  setSelectedRowIdentities((current) => current.filter((identity) => available.has(String(identity))));
189
211
  }
190
212
  onDataRef.current?.(data);
191
- }, [preserveSelectedRowIdentities, resource]);
213
+ }, [preserveSelectedRowIdentities, resolvedQuery.pageSize, resource]);
192
214
  useEffect(() => {
193
215
  if (!source || !provider || !resource || resourceError)
194
216
  return;
@@ -254,7 +276,8 @@ export function usePhiTableBinding({ source, initialQuery = {}, externalQuery, f
254
276
  if (!background) {
255
277
  setRows([]);
256
278
  rowsRef.current = [];
257
- setTotal(0);
279
+ setTotal(null);
280
+ setHasMore(false);
258
281
  setSummary({});
259
282
  }
260
283
  setQueryLoading(false);
@@ -505,6 +528,7 @@ export function usePhiTableBinding({ source, initialQuery = {}, externalQuery, f
505
528
  bindingError,
506
529
  rows,
507
530
  total,
531
+ hasMore,
508
532
  summary,
509
533
  contractError: resourceError,
510
534
  loading: queryLoading && !bindingError && !resourceError,
@@ -104,19 +104,27 @@ export function usePhiTreeBinding({ source, initialQuery = PHI_TREE_DEFAULT_INIT
104
104
  controllers.clear();
105
105
  };
106
106
  }, []);
107
+ /*
108
+ * A new source, or new defaults, start the tree over -- in the render that brought them, so the query
109
+ * effect below asks once, with the reset query, instead of first with the old one.
110
+ *
111
+ * The search a reader typed stays. Resetting it to the initial query showed the unfiltered tree for a
112
+ * moment and fetched it, and the Widget's debounce put the search back 250 ms later for a third request:
113
+ * a new binding parameter narrows what the tree is about, not what the reader is looking for.
114
+ */
115
+ const [renderedResetInputs, setRenderedResetInputs] = useState({ sourceKey, initialQuery, defaultExpandedNodeIdentities });
116
+ if (renderedResetInputs.sourceKey !== sourceKey ||
117
+ renderedResetInputs.initialQuery !== initialQuery ||
118
+ renderedResetInputs.defaultExpandedNodeIdentities !== defaultExpandedNodeIdentities) {
119
+ setRenderedResetInputs({ sourceKey, initialQuery, defaultExpandedNodeIdentities });
120
+ setNodes([]);
121
+ setSelectedNodeIdentities([]);
122
+ setCheckedNodeIdentities([]);
123
+ setExpandedNodeIdentities(defaultExpandedNodeIdentities);
124
+ setQuery((current) => (current.search ? { ...initialQuery, search: current.search } : initialQuery));
125
+ }
107
126
  useEffect(() => {
108
127
  expandedInitialSnapshot.current = false;
109
- let cancelled = false;
110
- queueMicrotask(() => {
111
- if (cancelled)
112
- return;
113
- setNodes([]);
114
- setSelectedNodeIdentities([]);
115
- setCheckedNodeIdentities([]);
116
- setExpandedNodeIdentities(defaultExpandedNodeIdentities);
117
- setQuery(initialQuery);
118
- });
119
- return () => { cancelled = true; };
120
128
  }, [defaultExpandedNodeIdentities, initialQuery, sourceKey]);
121
129
  useEffect(() => {
122
130
  if (!source || !provider || !resource)
@@ -0,0 +1,26 @@
1
+ import { type CSSProperties } from "react";
2
+ import { type PhiBulletListEntry, type PhiResolvedBulletListStyle } from "../shared/bullet-list-vocabulary";
3
+ export type PhiBulletListEntriesEditorProps = {
4
+ entries: readonly PhiBulletListEntry[];
5
+ listStyle: PhiResolvedBulletListStyle;
6
+ /** Absent, the entries are only shown: a read-only canvas. */
7
+ onCommit?: (entries: PhiBulletListEntry[]) => void;
8
+ /** Whether a click on an entry's marker steps its state on (`PHI_BULLET_LIST_ENTRY_STATES`). */
9
+ states?: boolean;
10
+ /** Each entry's field, apart from what every field here is: borderless, its text's own type. */
11
+ fieldStyle?: CSSProperties;
12
+ };
13
+ /**
14
+ * A list written in place, entry by entry, as the Widgets that carry one edit it.
15
+ *
16
+ * Every entry is a text field standing where the entry stands, so the list is set as it will be read.
17
+ * What is typed stays here until the field is left, and the Builder's history records one change per
18
+ * entry rather than one per keystroke. What changes the list's shape is written at once, with what was
19
+ * typed so far: Enter opens an entry after this one, Backspace in an empty entry removes it, Tab and
20
+ * Shift+Tab move an entry a level down and up, and -- where the list has `states` -- a click on the
21
+ * marker steps the entry from included to excluded to neither. Waiting for the blur would let the entry
22
+ * jump back while the tree is drawn again.
23
+ *
24
+ * A list with no entries still offers one empty field, so there is something to type into.
25
+ */
26
+ export declare function PhiBulletListEntriesEditor({ entries, listStyle, onCommit, states, fieldStyle, }: PhiBulletListEntriesEditorProps): import("react").JSX.Element;
@@ -0,0 +1,109 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useRef, useState } from "react";
4
+ import { PhiBulletListControl } from "../../controls/phi-bullet-list-control";
5
+ import { PhiTextControl } from "../../controls/phi-text-control";
6
+ import { buildPhiBulletListTree, nextPhiBulletListEntryState, resolvePhiBulletListLevel, } from "../shared/bullet-list-vocabulary";
7
+ /**
8
+ * A list written in place, entry by entry, as the Widgets that carry one edit it.
9
+ *
10
+ * Every entry is a text field standing where the entry stands, so the list is set as it will be read.
11
+ * What is typed stays here until the field is left, and the Builder's history records one change per
12
+ * entry rather than one per keystroke. What changes the list's shape is written at once, with what was
13
+ * typed so far: Enter opens an entry after this one, Backspace in an empty entry removes it, Tab and
14
+ * Shift+Tab move an entry a level down and up, and -- where the list has `states` -- a click on the
15
+ * marker steps the entry from included to excluded to neither. Waiting for the blur would let the entry
16
+ * jump back while the tree is drawn again.
17
+ *
18
+ * A list with no entries still offers one empty field, so there is something to type into.
19
+ */
20
+ export function PhiBulletListEntriesEditor({ entries, listStyle, onCommit, states = false, fieldStyle, }) {
21
+ const [draftState, setDraftState] = useState(() => ({ source: entries, value: [...entries] }));
22
+ const draft = draftState.source === entries ? draftState.value : [...entries];
23
+ const shown = draft.length > 0 || !onCommit ? draft : [{ text: "" }];
24
+ const listRef = useRef(null);
25
+ function focusEntry(index) {
26
+ requestAnimationFrame(() => {
27
+ const field = listRef.current?.querySelectorAll("textarea")[index];
28
+ field?.focus();
29
+ field?.setSelectionRange(field.value.length, field.value.length);
30
+ });
31
+ }
32
+ function commit(next) {
33
+ // The empty field an empty list offers is not an entry until something is typed into it.
34
+ if (entries.length === 0 && next.length === 1 && next[0].text === "" && !next[0].state)
35
+ return;
36
+ setDraftState({ source: entries, value: next });
37
+ if (JSON.stringify(next) !== JSON.stringify(entries))
38
+ onCommit?.(next);
39
+ }
40
+ function updateText(index, text) {
41
+ const next = [...shown];
42
+ next[index] = { ...next[index], text };
43
+ setDraftState({ source: entries, value: next });
44
+ }
45
+ function shiftLevel(index, delta) {
46
+ const entry = shown[index];
47
+ if (!entry)
48
+ return;
49
+ const level = resolvePhiBulletListLevel((entry.level ?? 1) + delta);
50
+ if (level === (entry.level ?? 1))
51
+ return;
52
+ const { level: _level, ...rest } = entry;
53
+ void _level;
54
+ commit(shown.map((item, itemIndex) => (itemIndex === index ? (level > 1 ? { ...rest, level } : rest) : item)));
55
+ focusEntry(index);
56
+ }
57
+ function insertAfter(index) {
58
+ const entry = shown[index];
59
+ const level = entry?.level;
60
+ commit([...shown.slice(0, index + 1), { text: "", ...(level ? { level } : {}) }, ...shown.slice(index + 1)]);
61
+ focusEntry(index + 1);
62
+ }
63
+ function remove(index) {
64
+ commit(shown.filter((_, itemIndex) => itemIndex !== index));
65
+ focusEntry(Math.max(0, index - 1));
66
+ }
67
+ function stepState(index) {
68
+ const entry = shown[index];
69
+ if (!entry)
70
+ return;
71
+ const { state: _state, ...rest } = entry;
72
+ void _state;
73
+ const state = nextPhiBulletListEntryState(entry.state);
74
+ commit(shown.map((item, itemIndex) => (itemIndex === index ? (state === "neutral" ? rest : { ...rest, state }) : item)));
75
+ }
76
+ return (_jsx("div", { ref: listRef, style: { width: "100%", minWidth: 0 }, children: _jsx(PhiBulletListControl, { listStyle: listStyle,
77
+ // Each entry is a borderless text field, whose first line stands at the field's height.
78
+ markerLineHeight: "var(--ant-control-height)", items: buildPhiBulletListTree(shown, (item, index) => (_jsx(PhiTextControl, { presentation: "textarea", value: item.text, readOnly: !onCommit, variant: "borderless", placeholder: `Item ${index + 1}`, autoSize: { minRows: 1, maxRows: 6 }, onChange: (value) => updateText(index, value ?? ""), onBlur: () => commit(shown), onKeyDown: (event) => {
79
+ if (!onCommit)
80
+ return;
81
+ if (event.key === "Tab") {
82
+ event.preventDefault();
83
+ shiftLevel(index, event.shiftKey ? -1 : 1);
84
+ }
85
+ else if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
86
+ event.preventDefault();
87
+ insertAfter(index);
88
+ }
89
+ else if (event.key === "Backspace" && item.text === "" && shown.length > 1) {
90
+ event.preventDefault();
91
+ remove(index);
92
+ }
93
+ }, style: {
94
+ paddingInline: 0,
95
+ paddingBlock: 0,
96
+ borderRadius: 0,
97
+ borderWidth: 0,
98
+ backgroundColor: "transparent",
99
+ resize: "none",
100
+ color: "inherit",
101
+ ...fieldStyle,
102
+ },
103
+ /*
104
+ * The text's own padding, stated: half of what the line leaves of the field's height, so the
105
+ * first line's middle is the marker's (`markerLineHeight` above). That is the padding the
106
+ * field had from Ant Design already; stated, the clear button can find the line too.
107
+ */
108
+ inputStyle: { paddingBlock: "calc((var(--ant-control-height) - 1lh) / 2)" } })), states && onCommit ? (_entry, index) => stepState(index) : undefined) }) }));
109
+ }
@@ -388,7 +388,7 @@ export function PhiHtmlWidgetEditor({ blockId, html, fontFamily, fontSize, onCha
388
388
  const initialHtmlRef = useRef(normalizedHtml);
389
389
  const latestHtmlRef = useRef(normalizedHtml);
390
390
  const resolvedFontFamily = resolvePhiWidgetFontFamily(fontFamily, fonts, token);
391
- const resolvedFontSize = resolvePhiWidgetFontSize(fontSize, token, "lg");
391
+ const resolvedFontSize = resolvePhiWidgetFontSize(fontSize, token, "base");
392
392
  const editable = onChange != null;
393
393
  useEffect(() => {
394
394
  latestHtmlRef.current = normalizedHtml;
@@ -0,0 +1,17 @@
1
+ import type { PhiCmsAreaKey } from "../../../../constants/cms-areas";
2
+ import type { PhiTreeOption } from "../../../../types/tree";
3
+ import type { PhiPageReferenceSelection } from "./phi-page-reference-picker";
4
+ type PageReferenceOptions = PhiTreeOption<PhiPageReferenceSelection>[];
5
+ /**
6
+ * The Pages of one Area a link may point at, as the tree `PhiLinkTargetControl` offers, for a surface
7
+ * that stands outside the Builder's own catalog: a Form's `link-target` field, the Theme's Brand.
8
+ *
9
+ * Answered by `/api/site/page-references` (`gateway/page-references-route.ts`), which merges the Area's
10
+ * Module Pages and Core's the way the Builder's picker does. `failed` is set when the list could not be
11
+ * read, so the surface can say so instead of showing an empty list that looks like a Site without Pages.
12
+ */
13
+ export declare function usePhiPageReferenceOptions(area: PhiCmsAreaKey, logScope: string): {
14
+ options: PageReferenceOptions;
15
+ failed: boolean;
16
+ };
17
+ export {};