@phis/ui 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/components/cms/phi-cms-layout-renderer.js +4 -3
  2. package/components/cms/phi-cms-page-render-scope.d.ts +4 -5
  3. package/components/cms/phi-cms-page-render-scope.js +43 -22
  4. package/components/cms/phi-cms-render-diagnostic-client.js +7 -3
  5. package/components/cms/phi-cms-render-error-boundary.js +2 -1
  6. package/components/controls/phi-bullet-list-control.js +31 -2
  7. package/components/controls/phi-ribbon-control.d.ts +29 -0
  8. package/components/controls/phi-ribbon-control.js +53 -0
  9. package/components/controls/phi-table-control.d.ts +3 -0
  10. package/components/controls/phi-table-control.js +13 -1
  11. package/components/controls/phi-tree-control.js +3 -3
  12. package/components/forms/shared-form-provider-registry.js +1 -4
  13. package/components/media/asset-collection-runtime.js +6 -16
  14. package/components/media/media-font-metrics.d.ts +3 -0
  15. package/components/media/media-font-metrics.js +43 -0
  16. package/components/media/phi-area-upload-widget.js +53 -97
  17. package/components/media/phi-image-preview-data.d.ts +13 -0
  18. package/components/media/phi-image-preview-data.js +184 -9
  19. package/components/media/phi-media-picker-binding.js +16 -13
  20. package/components/media/phi-media-upload.d.ts +15 -8
  21. package/components/media/phi-media-upload.js +18 -9
  22. package/components/media/phi-site-font-assets.js +7 -8
  23. package/components/overlays/phi-overlay-container-client.js +16 -1
  24. package/components/regions/presets/phi-settings-page-shell-tree.js +48 -1
  25. package/components/runtime/runtime-controller-materialization.js +12 -1
  26. package/components/state/scoped-state-store.js +20 -3
  27. package/components/tables/client/phi-table-binding.d.ts +2 -1
  28. package/components/tables/client/phi-table-binding.js +45 -21
  29. package/components/trees/client/phi-tree-binding.js +19 -11
  30. package/components/widgets/client/bullet-list-entries-editor.d.ts +26 -0
  31. package/components/widgets/client/bullet-list-entries-editor.js +103 -0
  32. package/components/widgets/config/choice-shared.js +12 -13
  33. package/components/widgets/config/command-buttons.d.ts +5 -1
  34. package/components/widgets/config/command-buttons.js +38 -81
  35. package/components/widgets/config/control-signal-config.d.ts +3 -1
  36. package/components/widgets/config/control-signal-config.js +19 -17
  37. package/components/widgets/config/image-source-parser.d.ts +1 -0
  38. package/components/widgets/config/image-source-parser.js +7 -8
  39. package/components/widgets/config/mask.d.ts +6 -1
  40. package/components/widgets/config/mask.js +24 -1
  41. package/components/widgets/config/parser-primitives.js +72 -0
  42. package/components/widgets/config/stack-tabs.d.ts +1 -1
  43. package/components/widgets/config/stack-tabs.js +2 -3
  44. package/components/widgets/config/strict-config-reader.d.ts +40 -0
  45. package/components/widgets/config/strict-config-reader.js +174 -0
  46. package/components/widgets/label-sets/effects.js +2 -0
  47. package/components/widgets/label-types/effects.d.ts +1 -0
  48. package/components/widgets/label-types/effects.js +1 -0
  49. package/components/widgets/navigation-descriptor-resolver.server.js +20 -8
  50. package/components/widgets/shared/bullet-list-vocabulary.d.ts +17 -1
  51. package/components/widgets/shared/bullet-list-vocabulary.js +19 -2
  52. package/components/widgets/shared/card-body-client.d.ts +16 -4
  53. package/components/widgets/shared/card-body-client.js +81 -24
  54. package/components/widgets/shared/card-vocabulary.d.ts +21 -4
  55. package/components/widgets/shared/card-vocabulary.js +28 -2
  56. package/components/widgets/shared/markdown-body-client.d.ts +2 -1
  57. package/components/widgets/shared/markdown-body-client.js +3 -1
  58. package/components/widgets/shared/markdown-render.js +8 -1
  59. package/components/widgets/signals/control-signal-capabilities.d.ts +0 -18
  60. package/components/widgets/signals/control-signal-capabilities.js +10 -22
  61. package/components/workspace/catalog-store.d.ts +4 -3
  62. package/components/workspace/catalog-store.js +6 -5
  63. package/gateway/dashboard-cards-route.js +1 -0
  64. package/gateway/internal-references.d.ts +6 -1
  65. package/gateway/internal-references.js +48 -23
  66. package/gateway/site-cms-write-validation.d.ts +9 -0
  67. package/gateway/site-cms-write-validation.js +78 -0
  68. package/gateway/site-proxy.js +19 -3
  69. package/helpers/renderable-block-serialization.js +1 -1
  70. package/next/config.d.mts +3 -0
  71. package/next/config.mjs +28 -1
  72. package/package.json +2 -2
  73. package/plugins/runtime/phi-slot-child-viewport-effects-observer.js +5 -1
  74. package/plugins/runtime-modules/accounting/presets.d.ts +9 -0
  75. package/plugins/runtime-modules/accounting/presets.js +37 -0
  76. package/plugins/runtime-modules/accounting/server.d.ts +1 -4
  77. package/plugins/runtime-modules/accounting/server.js +4 -7
  78. package/plugins/runtime-modules/app/presets.d.ts +18 -0
  79. package/plugins/runtime-modules/app/presets.js +67 -0
  80. package/plugins/runtime-modules/app/server.d.ts +1 -4
  81. package/plugins/runtime-modules/app/server.js +4 -7
  82. package/plugins/runtime-modules/asset/controller/runtime.js +2 -2
  83. package/plugins/runtime-modules/asset/services/collection.js +9 -1
  84. package/plugins/runtime-modules/asset/services/options.js +2 -2
  85. package/plugins/runtime-modules/builder/area-settings-answer.js +2 -1
  86. package/plugins/runtime-modules/builder/authoring-canvas.js +6 -6
  87. package/plugins/runtime-modules/builder/cards.js +3 -5
  88. package/plugins/runtime-modules/builder/clients/builder-region-overrides.js +3 -3
  89. package/plugins/runtime-modules/builder/clients/canvas-widget.js +15 -54
  90. package/plugins/runtime-modules/builder/clients/inspector-config-field.d.ts +4 -1
  91. package/plugins/runtime-modules/builder/clients/inspector-config-field.js +5 -34
  92. package/plugins/runtime-modules/builder/clients/inspector-link-target-field.js +1 -1
  93. package/plugins/runtime-modules/builder/clients/inspector-section-widget.js +8 -8
  94. package/plugins/runtime-modules/builder/clients/inspector-title.js +6 -6
  95. package/plugins/runtime-modules/builder/clients/layout-inspector.d.ts +1 -1
  96. package/plugins/runtime-modules/builder/clients/layout-inspector.js +11 -25
  97. package/plugins/runtime-modules/builder/clients/mode-switch.js +1 -1
  98. package/plugins/runtime-modules/builder/clients/pages-header.js +11 -11
  99. package/plugins/runtime-modules/builder/clients/pages-workspace.js +1 -1
  100. package/plugins/runtime-modules/builder/clients/region-visibility-fields.js +1 -1
  101. package/plugins/runtime-modules/builder/clients/runtime-module-authoring-boundary.js +1 -1
  102. package/plugins/runtime-modules/builder/clients/shells-workspace.js +15 -31
  103. package/plugins/runtime-modules/builder/clients/structure-canvas.js +1 -1
  104. package/plugins/runtime-modules/builder/clients/use-region-draft-hydration.d.ts +26 -0
  105. package/plugins/runtime-modules/builder/clients/use-region-draft-hydration.js +36 -0
  106. package/plugins/runtime-modules/builder/clients/widget-effects-editor.js +1 -1
  107. package/plugins/runtime-modules/builder/clients/widget-inspector.js +1 -0
  108. package/plugins/runtime-modules/builder/clients/workspace-scope-boundary.js +2 -2
  109. package/plugins/runtime-modules/builder/cms-instance-allocator.js +8 -28
  110. package/plugins/runtime-modules/builder/controller/client.d.ts +0 -15
  111. package/plugins/runtime-modules/builder/controller/definition.d.ts +0 -15
  112. package/plugins/runtime-modules/builder/controller/definition.js +0 -8
  113. package/plugins/runtime-modules/builder/controller/draft-command-controller.d.ts +1 -2
  114. package/plugins/runtime-modules/builder/controller/draft-command-controller.js +11 -30
  115. package/plugins/runtime-modules/builder/controller/draft-status-controller.js +15 -2
  116. package/plugins/runtime-modules/builder/controller/inspector-controller.d.ts +1 -2
  117. package/plugins/runtime-modules/builder/controller/inspector-controller.js +4 -7
  118. package/plugins/runtime-modules/builder/controller/page-controller.d.ts +1 -2
  119. package/plugins/runtime-modules/builder/controller/page-controller.js +16 -68
  120. package/plugins/runtime-modules/builder/controller/server-definition.d.ts +0 -15
  121. package/plugins/runtime-modules/builder/controller/signal-wiring-controller.d.ts +2 -5
  122. package/plugins/runtime-modules/builder/controller/signal-wiring-controller.js +6 -50
  123. package/plugins/runtime-modules/builder/controller/workspace-controller.d.ts +2 -3
  124. package/plugins/runtime-modules/builder/controller/workspace-controller.js +89 -153
  125. package/plugins/runtime-modules/builder/data-providers.d.ts +1 -0
  126. package/plugins/runtime-modules/builder/data-providers.js +1 -0
  127. package/plugins/runtime-modules/builder/definition.d.ts +1 -0
  128. package/plugins/runtime-modules/builder/developer-workspace-store.d.ts +64 -30
  129. package/plugins/runtime-modules/builder/developer-workspace-store.js +255 -61
  130. package/plugins/runtime-modules/builder/edit-scaffold-drawer.js +1 -1
  131. package/plugins/runtime-modules/builder/history.d.ts +3 -1
  132. package/plugins/runtime-modules/builder/options-providers.js +3 -2
  133. package/plugins/runtime-modules/builder/page-catalog-client.js +19 -7
  134. package/plugins/runtime-modules/builder/page-meta-form.js +1 -0
  135. package/plugins/runtime-modules/builder/page-references-provider.js +1 -1
  136. package/plugins/runtime-modules/builder/persistence.js +33 -69
  137. package/plugins/runtime-modules/builder/public-route-collisions.d.ts +2 -2
  138. package/plugins/runtime-modules/builder/public-route-collisions.js +6 -19
  139. package/plugins/runtime-modules/builder/render-root-node-preview.server.js +20 -3
  140. package/plugins/runtime-modules/builder/render-root-node-scaffold.js +3 -3
  141. package/plugins/runtime-modules/builder/runtime-module-selection.d.ts +2 -12
  142. package/plugins/runtime-modules/builder/runtime-module-selection.js +16 -42
  143. package/plugins/runtime-modules/builder/services/navigation-table.js +9 -15
  144. package/plugins/runtime-modules/builder/services/page-source-tree.js +8 -8
  145. package/plugins/runtime-modules/builder/services/runtime-modules-table.js +10 -10
  146. package/plugins/runtime-modules/builder/services/signal-routes-table.js +3 -3
  147. package/plugins/runtime-modules/builder/signal-route-selection.d.ts +1 -2
  148. package/plugins/runtime-modules/builder/signal-route-selection.js +2 -2
  149. package/plugins/runtime-modules/builder/signal-wiring-options.js +3 -2
  150. package/plugins/runtime-modules/builder/structure-dnd.js +8 -61
  151. package/plugins/runtime-modules/builder/trees/phi-default-builder-area-preset-tree.js +13 -10
  152. package/plugins/runtime-modules/builder/widgets/structure-region/built-in.js +12 -18
  153. package/plugins/runtime-modules/builder/workspace-catalog-actions.d.ts +55 -0
  154. package/plugins/runtime-modules/builder/workspace-catalog-actions.js +142 -0
  155. package/plugins/runtime-modules/client-manifests/common.js +1 -1
  156. package/plugins/runtime-modules/{runtime-calendar-adapters.js → core/calendar-adapters.js} +2 -2
  157. package/plugins/runtime-modules/{client-calendar-adapters/core.js → core/client-calendar-adapters.js} +1 -1
  158. package/plugins/runtime-modules/core/content-entries/table.js +1 -1
  159. package/plugins/runtime-modules/core/data-providers.d.ts +3 -0
  160. package/plugins/runtime-modules/core/data-providers.js +2 -1
  161. package/plugins/runtime-modules/core/definition.d.ts +3 -0
  162. package/plugins/runtime-modules/core/definition.js +1 -1
  163. package/plugins/runtime-modules/core/messaging/data-providers.d.ts +1 -0
  164. package/plugins/runtime-modules/core/messaging/data-providers.js +1 -0
  165. package/plugins/runtime-modules/core/messaging/thread-inbox-table.js +1 -1
  166. package/plugins/runtime-modules/core/server.js +1 -1
  167. package/plugins/runtime-modules/core/themes.d.ts +44 -0
  168. package/plugins/runtime-modules/core/themes.js +12 -0
  169. package/plugins/runtime-modules/core/widgets/card/collection-view-binding.js +25 -9
  170. package/plugins/runtime-modules/core/widgets/card/config.d.ts +296 -254
  171. package/plugins/runtime-modules/core/widgets/card/config.js +52 -3
  172. package/plugins/runtime-modules/core/widgets/card/editor.js +30 -3
  173. package/plugins/runtime-modules/core/widgets/card/server.d.ts +3 -0
  174. package/plugins/runtime-modules/core/widgets/card/server.js +31 -3
  175. package/plugins/runtime-modules/core/widgets/collection-view/asset-binding.js +7 -6
  176. package/plugins/runtime-modules/core/widgets/collection-view/config.js +9 -2
  177. package/plugins/runtime-modules/core/widgets/description/config.d.ts +1 -25
  178. package/plugins/runtime-modules/core/widgets/description/editor.js +4 -61
  179. package/plugins/runtime-modules/core/widgets/entry-list/config.d.ts +1 -25
  180. package/plugins/runtime-modules/core/widgets/gallery/config.d.ts +1 -25
  181. package/plugins/runtime-modules/core/widgets/image/config.js +2 -2
  182. package/plugins/runtime-modules/core/widgets/markdown/config.d.ts +3 -1
  183. package/plugins/runtime-modules/core/widgets/markdown/config.js +14 -0
  184. package/plugins/runtime-modules/core/widgets/markdown/editor.js +2 -3
  185. package/plugins/runtime-modules/core/widgets/result/config.d.ts +4 -25
  186. package/plugins/runtime-modules/core/widgets/result/config.js +2 -0
  187. package/plugins/runtime-modules/core/widgets/result/server.js +25 -19
  188. package/plugins/runtime-modules/core/widgets/simple-text/config.d.ts +1 -25
  189. package/plugins/runtime-modules/core/widgets/slot-upload/client.js +11 -2
  190. package/plugins/runtime-modules/core/widgets/table/client.js +8 -4
  191. package/plugins/runtime-modules/core/widgets/table/config.d.ts +3 -83
  192. package/plugins/runtime-modules/core/widgets/table/config.js +302 -452
  193. package/plugins/runtime-modules/core/widgets/tree/config.js +5 -2
  194. package/plugins/runtime-modules/dashboard/card-rows.d.ts +5 -0
  195. package/plugins/runtime-modules/dashboard/card-rows.js +59 -0
  196. package/plugins/runtime-modules/dashboard/clients/dashboard-card-view.js +4 -6
  197. package/plugins/runtime-modules/dashboard/services/collection.js +17 -7
  198. package/plugins/runtime-modules/groups/data-providers.d.ts +2 -0
  199. package/plugins/runtime-modules/groups/data-providers.js +3 -3
  200. package/plugins/runtime-modules/groups/definition.d.ts +2 -0
  201. package/plugins/runtime-modules/localization/data-providers.d.ts +3 -0
  202. package/plugins/runtime-modules/localization/data-providers.js +2 -0
  203. package/plugins/runtime-modules/localization/definition.d.ts +3 -0
  204. package/plugins/runtime-modules/localization/services/table.js +3 -7
  205. package/plugins/runtime-modules/observability/data-providers.d.ts +1 -0
  206. package/plugins/runtime-modules/observability/data-providers.js +1 -0
  207. package/plugins/runtime-modules/observability/definition.d.ts +1 -0
  208. package/plugins/runtime-modules/observability/module.d.ts +1 -0
  209. package/plugins/runtime-modules/observability/services/table.js +1 -1
  210. package/plugins/runtime-modules/phi-default-site-area-preset-tree.js +2 -105
  211. package/plugins/runtime-modules/public/presets.d.ts +115 -0
  212. package/plugins/runtime-modules/{area-base-presets.js → public/presets.js} +19 -119
  213. package/plugins/runtime-modules/public/server.d.ts +1 -4
  214. package/plugins/runtime-modules/public/server.js +4 -7
  215. package/plugins/runtime-modules/public/trees/phi-default-pub-error-page-tree.js +7 -8
  216. package/plugins/runtime-modules/public-base-page-references.d.ts +1 -1
  217. package/plugins/runtime-modules/public-base-page-references.js +1 -1
  218. package/plugins/runtime-modules/revisions/controller/client.js +11 -11
  219. package/plugins/runtime-modules/revisions/services/table.js +3 -3
  220. package/plugins/runtime-modules/theme/trees/phi-default-builder-theme-page-tree.js +22 -0
  221. package/plugins/runtime-modules/theme/widgets/brand-controls/client.d.ts +2 -1
  222. package/plugins/runtime-modules/theme/widgets/brand-controls/client.js +93 -58
  223. package/plugins/runtime-modules/theme/widgets/brand-controls/config.d.ts +2 -1
  224. package/plugins/runtime-modules/theme/widgets/brand-controls/config.js +23 -22
  225. package/plugins/runtime-modules/theme/widgets/brand-controls/plugin.js +1 -1
  226. package/plugins/runtime-modules/theme-descriptors.js +1 -1
  227. package/plugins/runtime-modules/user-management/data-providers.d.ts +2 -0
  228. package/plugins/runtime-modules/user-management/data-providers.js +1 -0
  229. package/plugins/runtime-modules/user-management/definition.d.ts +2 -0
  230. package/styles/control-shape.css +11 -11
  231. package/styles/controls.css +29 -3
  232. package/styles/layout.css +16 -7
  233. package/types/cms-config.js +142 -185
  234. package/types/cms-plugins.d.ts +4 -11
  235. package/types/core-widget-placements.d.ts +17 -3
  236. package/types/dashboard-cards.d.ts +9 -0
  237. package/types/media.d.ts +0 -3
  238. package/types/renderable-block.d.ts +6 -1
  239. package/types/table-widget.d.ts +9 -0
  240. package/types/table-widget.js +20 -0
  241. package/types.d.ts +1 -1
  242. package/widget-config.d.ts +2 -0
  243. package/widget-config.js +6 -0
  244. package/plugins/runtime-modules/area-base-presets.d.ts +0 -139
  245. /package/plugins/runtime-modules/{runtime-calendar-adapters.d.ts → core/calendar-adapters.d.ts} +0 -0
  246. /package/plugins/runtime-modules/{client-calendar-adapters/core.d.ts → core/client-calendar-adapters.d.ts} +0 -0
@@ -3,7 +3,8 @@ import { PhiCmsWidgetType } from "../../../../../constants/cms-widget-types";
3
3
  import { readPhiLinkTarget } from "../../../../../types/references";
4
4
  import { readPhiMediaImageSourceConfig } from "../../../../../components/widgets/config/image-source-parser";
5
5
  import { PHI_SURFACE_CARD } from "../../../../../helpers/surface-presets";
6
- import { PHI_CARD_HEADING_LEVELS, PHI_CARD_HOVER_EFFECTS, PHI_CARD_ICON_PLACEMENTS, PHI_CARD_TEXT_ALIGNS, PHI_CARD_VARIANTS, } from "../../../../../components/widgets/shared/card-vocabulary";
6
+ import { buildPhiBulletListFields, readPhiBulletListEntries, readPhiBulletListStyle, } from "../../../../../components/widgets/shared/bullet-list-vocabulary";
7
+ import { PHI_CARD_HEADING_LEVELS, PHI_CARD_HOVER_EFFECTS, PHI_CARD_ICON_PLACEMENTS, PHI_CARD_TEXT_ALIGNS, PHI_CARD_VARIANTS, readPhiCardBillingPeriod, } from "../../../../../components/widgets/shared/card-vocabulary";
7
8
  import { readPhiControlLabel } from "../../../../../components/widgets/config/common-action-field";
8
9
  import { PHI_CARD_SIGNALS } from "../../../../../components/widgets/signals/control-signal-capabilities";
9
10
  import { readPhiSignalRouteSet } from "../../../../../types/signals";
@@ -24,7 +25,16 @@ function parsePhiCmsCardWidgetConfig(config) {
24
25
  description: readString(config.description),
25
26
  meta: readString(config.meta),
26
27
  value: readString(config.value),
27
- body: readChoice(readString(config.body), ["text", "stat"]),
28
+ badge: readString(config.badge),
29
+ badgeColor: readString(config.badgeColor),
30
+ price: readString(config.price),
31
+ pricePeriod: readString(config.pricePeriod),
32
+ priceYearly: readString(config.priceYearly),
33
+ pricePeriodYearly: readString(config.pricePeriodYearly),
34
+ billingPeriod: readPhiCardBillingPeriod(config.billingPeriod),
35
+ features: config.features === undefined ? undefined : readPhiBulletListEntries(config.features),
36
+ featureList: readPhiBulletListStyle(config.featureList),
37
+ body: readChoice(readString(config.body), ["text", "stat", "plan"]),
28
38
  headingLevel: readChoice(readString(config.headingLevel), PHI_CARD_HEADING_LEVELS),
29
39
  textAlign: readChoice(readString(config.textAlign), PHI_CARD_TEXT_ALIGNS),
30
40
  icon: readString(config.icon),
@@ -69,7 +79,17 @@ export const PHI_CARD_WIDGET_DEFINITION = {
69
79
  icon: "antd:idcard-outlined",
70
80
  iconFamily: "basic",
71
81
  translatesOwnText: true,
72
- slotSizePolicy: "fill-inline",
82
+ /*
83
+ * A card fills its slot both ways, as a Layout does.
84
+ *
85
+ * It filled only across, and stood at its own content's height inside the slot: three cards whose
86
+ * texts differ, side by side in a Grid row, ended at three different lines although the row had laid
87
+ * their slots out at one height. Filling the block axis lets the row's height reach the card. Where
88
+ * the slot has no height of its own -- a Stack or a Masonry column of content height -- a percentage
89
+ * against it stays auto and the card is as tall as its content, as before. A card in a container of
90
+ * fixed height grows into it, as a Layout there does; a card that should not says so with a height.
91
+ */
92
+ slotSizePolicy: "fill",
73
93
  /*
74
94
  * The card draws its Surface itself: its picture zooms inside the box under the pointer, and the box
75
95
  * clips it to its corners, so the ground has to be the card's own and not the slot frame's.
@@ -102,6 +122,22 @@ export const PHI_CARD_WIDGET_DEFINITION = {
102
122
  options: [
103
123
  { value: "text", label: "Text" },
104
124
  { value: "stat", label: "Statistic" },
125
+ { value: "plan", label: "Plan" },
126
+ ],
127
+ },
128
+ /*
129
+ * The prices and the features are written on the card; asked here is only which price it shows
130
+ * first. The canvas shows that one, so the yearly price is written by choosing Yearly here.
131
+ */
132
+ {
133
+ key: "billingPeriod",
134
+ type: "choice",
135
+ label: "Price shown",
136
+ presentation: "segmented",
137
+ visibleWhen: { field: "body", equals: "plan" },
138
+ options: [
139
+ { value: "monthly", label: "Monthly" },
140
+ { value: "yearly", label: "Yearly" },
105
141
  ],
106
142
  },
107
143
  {
@@ -223,6 +259,19 @@ export const PHI_CARD_WIDGET_DEFINITION = {
223
259
  ],
224
260
  },
225
261
  { key: "highlight", type: "boolean", label: "Highlight" },
262
+ /* The band's words are written on the card; its colour is the one thing asked here. */
263
+ {
264
+ key: "badgeColor",
265
+ type: "color",
266
+ heading: "Badge",
267
+ label: "Color",
268
+ mode: "single",
269
+ defaultToken: "colorPrimary",
270
+ },
271
+ ...buildPhiBulletListFields("featureList", "Features").map((field) => ({
272
+ ...field,
273
+ visibleWhen: { field: "body", equals: "plan" },
274
+ })),
226
275
  ],
227
276
  parseConfig: parsePhiCmsCardWidgetConfig,
228
277
  };
@@ -8,18 +8,35 @@ import { PhiInlineTextEditor } from "../../../../../components/controls/phi-inli
8
8
  import { PhiTypographyControl } from "../../../../../components/controls/phi-typography-control";
9
9
  import { PhiCardWidgetClient, } from "../../../../../components/widgets/shared/card-body-client";
10
10
  import { PHI_CARD_WIDGET_DEFAULT_LABELS } from "../../../../../components/widgets/label-types/card";
11
+ import { PhiBulletListEntriesEditor } from "../../../../../components/widgets/client/bullet-list-entries-editor";
12
+ import { resolvePhiBulletListStyle, } from "../../../../../components/widgets/shared/bullet-list-vocabulary";
13
+ /* One empty list for every card without features, so the list editor's draft is not reset per render. */
14
+ const PHI_CARD_NO_FEATURES = [];
11
15
  const PHI_CARD_TEXT_PLACEHOLDERS = {
12
16
  eyebrow: "Eyebrow",
13
17
  title: "Title",
14
18
  description: "Description",
15
19
  meta: "Meta",
16
20
  value: "Value",
21
+ badge: "Badge",
22
+ price: "Price",
23
+ pricePeriod: "/ month",
24
+ priceYearly: "Yearly price",
25
+ pricePeriodYearly: "/ year",
26
+ };
27
+ /* Slots only one body draws; under any other the editor offers no field for them. */
28
+ const PHI_CARD_BODY_TEXT_SLOTS = {
29
+ value: "stat",
30
+ price: "plan",
31
+ pricePeriod: "plan",
32
+ priceYearly: "plan",
33
+ pricePeriodYearly: "plan",
17
34
  };
18
35
  /*
19
36
  * Slots a card often goes without. Empty, they stand on the canvas only while the card is under the
20
37
  * pointer or being written in (`styles/layout.css`), so an empty card does not read as four empty lines.
21
38
  */
22
- const PHI_CARD_OPTIONAL_TEXT_SLOTS = new Set(["eyebrow", "meta"]);
39
+ const PHI_CARD_OPTIONAL_TEXT_SLOTS = new Set(["eyebrow", "meta", "badge"]);
23
40
  /**
24
41
  * One of the card's words, edited where it stands: underlined, in the type of the text it edits.
25
42
  *
@@ -106,8 +123,14 @@ export function PhiCardWidgetEditor({ config, onChange }) {
106
123
  meta: config.meta,
107
124
  actionLabel: config.actionLabel ?? PHI_CARD_WIDGET_DEFAULT_LABELS.actionLabel,
108
125
  value: config.value,
126
+ badge: config.badge,
127
+ price: config.price,
128
+ pricePeriod: config.pricePeriod,
129
+ priceYearly: config.priceYearly,
130
+ pricePeriodYearly: config.pricePeriodYearly,
109
131
  }, renderText: (slot, text) => {
110
- if (slot === "value" && config.body !== "stat")
132
+ const onlyUnder = PHI_CARD_BODY_TEXT_SLOTS[slot];
133
+ if (onlyUnder && (config.body ?? "text") !== onlyUnder)
111
134
  return null;
112
135
  const field = (_jsx(PhiCardInlineText, { slot: slot, text: text ?? "", ...(onChange ? { onCommit: (next) => onChange({ [slot]: next || undefined }) } : {}) }));
113
136
  return !text && PHI_CARD_OPTIONAL_TEXT_SLOTS.has(slot)
@@ -141,8 +164,12 @@ export function PhiCardWidgetEditor({ config, onChange }) {
141
164
  variant: config.variant,
142
165
  body: config.body,
143
166
  highlight: config.highlight,
167
+ badgeColor: config.badgeColor,
168
+ billingPeriod: config.billingPeriod,
144
169
  hoverEffect: config.linkTarget ? config.hoverEffect : undefined,
145
- } }), pictureTooNarrow ? (_jsx(PhiTypographyControl, { style: {
170
+ }, ...(config.body === "plan" ? {
171
+ listContent: (_jsx(PhiBulletListEntriesEditor, { entries: config.features ?? PHI_CARD_NO_FEATURES, listStyle: resolvePhiBulletListStyle(config.featureList), states: true, ...(onChange ? { onCommit: (features) => onChange({ features }) } : {}) })),
172
+ } : {}) }), pictureTooNarrow ? (_jsx(PhiTypographyControl, { style: {
146
173
  position: "absolute",
147
174
  left: "0.375rem",
148
175
  top: "0.375rem",
@@ -7,6 +7,9 @@ export type PhiCardWidgetLabels = {
7
7
  description?: string;
8
8
  meta?: string;
9
9
  actionLabel?: string;
10
+ badge?: string;
11
+ pricePeriod?: string;
12
+ pricePeriodYearly?: string;
10
13
  };
11
14
  export type PhiCardWidgetProps = PhiServerBlockBaseProps<PhiCardWidgetLabels, PhiCmsCardWidgetConfig> & {
12
15
  links?: PhiResolvedLinkTargets | null;
@@ -7,6 +7,9 @@ import { resolvePhiLinkHref } from "../../../../../helpers/link-target";
7
7
  import { PhiCmsWidgetType } from "../../../../../constants/cms-widget-types";
8
8
  import { PhiRuntimeModuleRenderClientHost } from "../../../../../components/runtime/runtime-module-render-client-manifest";
9
9
  import { getPhiCardWidgetLabelsForRuntime } from "../../../../../components/widgets/label-sets/card";
10
+ import { PhiBulletListControl } from "../../../../../components/controls/phi-bullet-list-control";
11
+ import { buildPhiBulletListTree, resolvePhiBulletListStyle, } from "../../../../../components/widgets/shared/bullet-list-vocabulary";
12
+ import { maskPhiTextPlaceholders, resolvePhiTextPlaceholders } from "../../../../../helpers/text-placeholders";
10
13
  export async function PhiCardWidget({ labels, config, runtime, links, translate, preview = false, }) {
11
14
  /*
12
15
  * The button's words: the author's, or none where they emptied them for an icon-only button, or the
@@ -15,19 +18,36 @@ export async function PhiCardWidget({ labels, config, runtime, links, translate,
15
18
  const actionEnabled = config?.actionEnabled === true;
16
19
  const defaultLabels = actionEnabled ? await getPhiCardWidgetLabelsForRuntime(runtime) : null;
17
20
  const ownActionLabel = labels.actionLabel ?? config?.actionLabel;
21
+ const plan = config?.body === "plan";
18
22
  const textEntries = [
19
23
  ["eyebrow", labels.eyebrow ?? config?.eyebrow],
20
24
  ["title", labels.title ?? config?.title],
21
25
  ["description", labels.description ?? config?.description],
22
26
  ["meta", labels.meta ?? config?.meta],
23
27
  ["actionLabel", actionEnabled ? ownActionLabel : undefined],
28
+ ["badge", labels.badge ?? config?.badge],
29
+ ["pricePeriod", plan ? labels.pricePeriod ?? config?.pricePeriod : undefined],
30
+ ["pricePeriodYearly", plan ? labels.pricePeriodYearly ?? config?.pricePeriodYearly : undefined],
24
31
  ];
32
+ /*
33
+ * A plan's features, masked as every list entry is: their values stand as `%1` onwards and never reach
34
+ * a translator, and `{site.name}` is filled after (`helpers/text-placeholders.ts`).
35
+ */
36
+ const features = plan ? (config?.features ?? []).filter((entry) => Boolean(entry.text)) : [];
37
+ const maskedFeatures = features.map((entry) => maskPhiTextPlaceholders(entry.text, entry.values?.length ?? 0));
38
+ const sourceTexts = [...textEntries.map(([, value]) => value ?? ""), ...maskedFeatures.map((masked) => masked.text)];
25
39
  const translatedTexts = translate
26
40
  ? await (runtime?.locale.current
27
- ? trBulkForLocale(runtime.locale.current, textEntries.map(([, value]) => value ?? ""))
28
- : trBulk(textEntries.map(([, value]) => value ?? "")))
29
- : textEntries.map(([, value]) => value ?? "");
41
+ ? trBulkForLocale(runtime.locale.current, sourceTexts)
42
+ : trBulk(sourceTexts))
43
+ : sourceTexts;
30
44
  const translatedByKey = new Map(textEntries.map(([key], index) => [key, translatedTexts[index] || undefined]));
45
+ const translatedFeatures = features.map((entry, index) => ({
46
+ ...entry,
47
+ text: resolvePhiTextPlaceholders(translatedTexts[textEntries.length + index] ?? entry.text, maskedFeatures[index].names, runtime, entry.values),
48
+ }));
49
+ /* Drawn here, so the list Control never reaches the browser with the Card (`listContent`). */
50
+ const featureList = translatedFeatures.length > 0 ? (_jsx(PhiBulletListControl, { listStyle: resolvePhiBulletListStyle(config?.featureList), items: buildPhiBulletListTree(translatedFeatures, (entry) => entry.text) })) : null;
31
51
  /*
32
52
  * The figure is left out of the translation on purpose: a Site's own number is not a word, and
33
53
  * `translate` is a per-Widget switch, so a stat card that turned it off to protect the figure would
@@ -92,6 +112,8 @@ export async function PhiCardWidget({ labels, config, runtime, links, translate,
92
112
  variant: config?.variant,
93
113
  body: config?.body,
94
114
  highlight: config?.highlight,
115
+ badgeColor: config?.badgeColor,
116
+ billingPeriod: config?.billingPeriod,
95
117
  // Only a card that leads somewhere answers the pointer; the preview has no link but shows it.
96
118
  hoverEffect: config?.linkTarget ? config.hoverEffect : undefined,
97
119
  signalRoutes: preview ? null : config?.signalRoutes ?? null,
@@ -106,7 +128,13 @@ export async function PhiCardWidget({ labels, config, runtime, links, translate,
106
128
  ? defaultLabels?.actionLabel
107
129
  : translatedByKey.get("actionLabel") ?? "",
108
130
  value,
131
+ badge: translatedByKey.get("badge"),
132
+ price: plan ? config?.price : undefined,
133
+ pricePeriod: translatedByKey.get("pricePeriod"),
134
+ priceYearly: plan ? config?.priceYearly : undefined,
135
+ pricePeriodYearly: translatedByKey.get("pricePeriodYearly"),
109
136
  },
110
137
  config: clientConfig,
138
+ ...(featureList ? { listContent: featureList } : {}),
111
139
  } }));
112
140
  }
@@ -23,6 +23,7 @@ import { buildPhiMediaSpaceOptions, usePhiMediaSpaceSelectionAllowed, } from "..
23
23
  import { buildPhiMediaFolderCascaderOptions, buildPhiMediaFolderValueById, combinePhiMediaFlagValues, resolvePhiMediaFolderIdFromValue, } from "../../../../../components/media/media-folder-options";
24
24
  import { PHI_ASSET_CONTROLLER_STORE_KEY } from "../../../../../components/media/asset-controller-signals";
25
25
  import { applyPhiAssetCollectionData } from "../../../../../components/media/asset-collection-runtime";
26
+ import { readPhiMediaAssetTiles, readPhiMediaFolders } from "../../../../../components/media/phi-image-preview-data";
26
27
  import { PhiAssetFolderControl } from "../../../../../components/media/phi-asset-folder-control";
27
28
  import { PhiAreaUploadBinding } from "../../../../../components/media/phi-area-upload-widget";
28
29
  import { PHI_MEDIA_WIDGET_DEFAULT_LABELS } from "../../../../../components/media/media-widget-labels";
@@ -65,10 +66,9 @@ export function PhiAssetCollectionViewBinding({ config, binding, labels: rawLabe
65
66
  const selectionState = usePhiImagePreviewStore(PHI_ASSET_CONTROLLER_STORE_KEY);
66
67
  const query = binding.query;
67
68
  const setQuery = binding.setQuery;
68
- const assets = (binding.data?.items ?? []);
69
- const folders = Array.isArray(binding.data?.meta?.folders)
70
- ? binding.data.meta.folders
71
- : [];
69
+ // A Collection carries items of no particular type; they are read as tiles, not cast to them.
70
+ const assets = useMemo(() => readPhiMediaAssetTiles(binding.data?.items ?? []), [binding.data?.items]);
71
+ const folders = useMemo(() => readPhiMediaFolders(binding.data?.meta?.folders), [binding.data?.meta?.folders]);
72
72
  const queryKind = query.filters?.kind;
73
73
  const queryFlags = query.filters?.presentationFlags;
74
74
  const queryFolderId = query.filters?.folderId;
@@ -82,7 +82,8 @@ export function PhiAssetCollectionViewBinding({ config, binding, labels: rawLabe
82
82
  pageSize: query.pageSize ?? 20,
83
83
  assets,
84
84
  folders,
85
- pagination: { total: binding.data?.total ?? assets.length },
85
+ // Nothing read yet is nothing counted yet; the assets of a page are never the count.
86
+ pagination: { total: binding.data?.total ?? 0 },
86
87
  loading: binding.loading,
87
88
  error: binding.error,
88
89
  uploadPanelOpen: binding.openPanelKey === "upload",
@@ -259,7 +260,7 @@ export function PhiAssetCollectionViewBinding({ config, binding, labels: rawLabe
259
260
  } })) : null, diagnostics: state.error ? _jsx(PhiAlertControl, { level: "error", showIcon: true, title: state.error }) : null, body: body, items: state.assets.map((asset) => (_jsx(PhiMediaAssetTileControl, { asset: asset, minColumnWidth: minColumnWidth, selected: state.selectedAssetId === asset.id, mode: mode, deleting: deletingAssetIds.includes(asset.id), onSelect: selectAsset, onDelete: (nextAsset) => { void deleteAsset(nextAsset); }, deleteLabel: labels.grid.deleteLabel, showTypeLabel: true, showDeleteAction: true, showDimensions: true, showIdLabel: true }, asset.id))), mode: mode, gap: gap, minColumnWidth: minColumnWidth, pagination: features.pagination?.enabled === false ? null : {
260
261
  page: state.page,
261
262
  pageSize: state.pageSize,
262
- total: state.pagination?.total ?? state.assets.length,
263
+ total: state.pagination?.total ?? 0,
263
264
  simple: features.pagination?.compact,
264
265
  showSizeChanger: (features.pagination?.pageSizes?.length ?? 0) > 0,
265
266
  pageSizeOptions: features.pagination?.pageSizes,
@@ -1,5 +1,5 @@
1
1
  import { resolvePhiCmsWidgetPluginKey } from "../../../../../constants/cms-widget-types";
2
- import { PHI_CARD_VARIANTS } from "../../../../../components/widgets/shared/card-vocabulary";
2
+ import { PHI_CARD_VARIANTS, readPhiCardBillingPeriod } from "../../../../../components/widgets/shared/card-vocabulary";
3
3
  import { PhiCmsWidgetType } from "../../../../../constants/cms-widget-types";
4
4
  import { readPhiProviderResourceSource } from "../../../../../types/runtime-data-provider";
5
5
  import { PHI_SIGNAL_VALUE_SCHEMAS, readPhiSignalRouteSet } from "../../../../../types/signals";
@@ -25,8 +25,15 @@ function readCardPresentation(value) {
25
25
  actionLabel: readString(value.actionLabel),
26
26
  actionHref: readString(value.actionHref),
27
27
  variant: PHI_CARD_VARIANTS.find((candidate) => candidate === variant),
28
- body: value.body === "text" || value.body === "stat" ? value.body : undefined,
28
+ body: value.body === "text" || value.body === "stat" || value.body === "plan" ? value.body : undefined,
29
29
  highlight: readString(value.highlight),
30
+ badge: readString(value.badge),
31
+ price: readString(value.price),
32
+ pricePeriod: readString(value.pricePeriod),
33
+ priceYearly: readString(value.priceYearly),
34
+ pricePeriodYearly: readString(value.pricePeriodYearly),
35
+ features: readString(value.features),
36
+ billingPeriod: readPhiCardBillingPeriod(value.billingPeriod),
30
37
  };
31
38
  }
32
39
  function readMode(value) {
@@ -312,30 +312,6 @@ export declare const PHI_DESCRIPTION_WIDGET_DEFINITION: {
312
312
  editorPlacement?: "inspector" | "geometry" | "toolbar";
313
313
  visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
314
314
  heading?: string;
315
- } & {
316
- type: "background";
317
- section?: string;
318
- storage?: import("../../../../../types").PhiCmsConfigFieldValueStorage;
319
- }) | ({
320
- key: string;
321
- label: string;
322
- description?: string;
323
- required?: boolean;
324
- editorPlacement?: "inspector" | "geometry" | "toolbar";
325
- visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
326
- heading?: string;
327
- } & {
328
- type: "border";
329
- section?: string;
330
- storage?: import("../../../../../types").PhiCmsConfigFieldValueStorage;
331
- }) | ({
332
- key: string;
333
- label: string;
334
- description?: string;
335
- required?: boolean;
336
- editorPlacement?: "inspector" | "geometry" | "toolbar";
337
- visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
338
- heading?: string;
339
315
  } & {
340
316
  type: "surface";
341
317
  tone?: false;
@@ -348,7 +324,7 @@ export declare const PHI_DESCRIPTION_WIDGET_DEFINITION: {
348
324
  visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
349
325
  heading?: string;
350
326
  } & {
351
- type: "shadow" | "grid-placement" | "column-count";
327
+ type: "grid-placement" | "column-count";
352
328
  section?: string;
353
329
  }) | {
354
330
  key: string;
@@ -1,12 +1,12 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useMemo, useRef, useState } from "react";
3
+ import { useMemo, useState } from "react";
4
4
  import { PhiTagControl } from "../../../../../components/controls/phi-tag-control";
5
5
  import { PhiTextControl } from "../../../../../components/controls/phi-text-control";
6
6
  import { PhiFlexControl } from "../../../../../components/controls/phi-flex-control";
7
7
  import { resolvePhiDescriptionEditorSections } from "../../../../../components/widgets/client/description-editor-sections";
8
- import { PhiBulletListControl } from "../../../../../components/controls/phi-bullet-list-control";
9
- import { buildPhiBulletListTree, resolvePhiBulletListLevel, resolvePhiBulletListStyle, } from "../../../../../components/widgets/shared/bullet-list-vocabulary";
8
+ import { PhiBulletListEntriesEditor } from "../../../../../components/widgets/client/bullet-list-entries-editor";
9
+ import { resolvePhiBulletListStyle, } from "../../../../../components/widgets/shared/bullet-list-vocabulary";
10
10
  function resolveEditorConfig(config) {
11
11
  return {
12
12
  eyebrow: config?.eyebrow ?? "",
@@ -36,7 +36,6 @@ export function PhiDescriptionWidgetEditor({ config, onChange, }) {
36
36
  * would be gone before its blur could write the empty value.
37
37
  */
38
38
  const sections = resolvePhiDescriptionEditorSections(resolvedConfig, Boolean(onChange));
39
- const asideListRef = useRef(null);
40
39
  /*
41
40
  * Typing changes the draft and nothing else.
42
41
  *
@@ -56,51 +55,6 @@ export function PhiDescriptionWidgetEditor({ config, onChange, }) {
56
55
  }
57
56
  onChange?.({ [key]: draft[key] });
58
57
  }
59
- function updateAsideItem(index, value) {
60
- setDraftState((current) => {
61
- const currentDraft = current.source === config ? current.value : resolvedConfig;
62
- const nextItems = [...currentDraft.asideItems];
63
- nextItems[index] = { ...nextItems[index], text: value };
64
- return {
65
- source: config,
66
- value: {
67
- ...currentDraft,
68
- asideItems: nextItems,
69
- },
70
- };
71
- });
72
- }
73
- function commitAsideItems(items = draft.asideItems) {
74
- if (JSON.stringify(items) === JSON.stringify(resolvedConfig.asideItems)) {
75
- return;
76
- }
77
- onChange?.({ asideItems: items });
78
- }
79
- /*
80
- * Tab moves an entry a level down and Shift+Tab a level up, as in every outline editor. The move is
81
- * written at once, with whatever was typed into the entry so far: it is a change of structure, and
82
- * waiting for the blur would let the entry jump back as the tree is drawn again.
83
- */
84
- function shiftAsideItemLevel(index, delta) {
85
- const items = draft.asideItems;
86
- const entry = items[index];
87
- if (!entry)
88
- return;
89
- const level = resolvePhiBulletListLevel((entry.level ?? 1) + delta);
90
- if (level === (entry.level ?? 1))
91
- return;
92
- const { level: _level, ...rest } = entry;
93
- void _level;
94
- const next = items.map((item, itemIndex) => (itemIndex === index ? (level > 1 ? { ...rest, level } : rest) : item));
95
- setDraftState({ source: config, value: { ...draft, asideItems: next } });
96
- commitAsideItems(next);
97
- // The entry now hangs elsewhere in the tree and its field is a new one; the caret goes back into it.
98
- requestAnimationFrame(() => {
99
- const field = asideListRef.current?.querySelectorAll("textarea")[index];
100
- field?.focus();
101
- field?.setSelectionRange(field.value.length, field.value.length);
102
- });
103
- }
104
58
  return (_jsx(PhiFlexControl, { vertical: true, gap: 16, style: { width: "100%", minWidth: 0 }, children: _jsxs(PhiFlexControl, { vertical: true, gap: 10, style: { width: "100%", minWidth: 0 }, children: [sections.eyebrowTag ? (_jsx(PhiTagControl, { color: "default", style: {
105
59
  width: "fit-content",
106
60
  borderRadius: 999,
@@ -126,18 +80,7 @@ export function PhiDescriptionWidgetEditor({ config, onChange, }) {
126
80
  ...EDITOR_TEXTAREA_STYLE,
127
81
  color: "var(--ant-color-text-heading)",
128
82
  fontWeight: 600,
129
- } }), _jsx("div", { ref: asideListRef, style: { width: "100%", minWidth: 0 }, children: _jsx(PhiBulletListControl, { listStyle: resolvePhiBulletListStyle(config?.asideList),
130
- // Each entry is a borderless text field, whose first line stands at the field's height.
131
- markerLineHeight: "var(--ant-control-height)", items: buildPhiBulletListTree(draft.asideItems, (item, index) => (_jsx(PhiTextControl, { presentation: "textarea", value: item.text, readOnly: !onChange, variant: "borderless", placeholder: `Item ${index + 1}`, autoSize: { minRows: 1, maxRows: 6 }, onChange: (value) => updateAsideItem(index, value ?? ""), onBlur: () => commitAsideItems(), onKeyDown: (event) => {
132
- if (event.key !== "Tab" || !onChange)
133
- return;
134
- event.preventDefault();
135
- shiftAsideItemLevel(index, event.shiftKey ? -1 : 1);
136
- }, style: {
137
- ...EDITOR_TEXTAREA_STYLE,
138
- borderBottom: "none",
139
- paddingBlock: 0,
140
- } }))) }) })] })) : null, _jsx(PhiTextControl, { presentation: "textarea", value: draft.footer, readOnly: !onChange, variant: "borderless", placeholder: "Footer", autoSize: { minRows: 1, maxRows: 5 }, onChange: (value) => updateField("footer", value ?? ""), onBlur: () => commitField("footer"), style: {
83
+ } }), _jsx(PhiBulletListEntriesEditor, { entries: resolvedConfig.asideItems, listStyle: resolvePhiBulletListStyle(config?.asideList), ...(onChange ? { onCommit: (asideItems) => onChange({ asideItems }) } : {}) })] })) : null, _jsx(PhiTextControl, { presentation: "textarea", value: draft.footer, readOnly: !onChange, variant: "borderless", placeholder: "Footer", autoSize: { minRows: 1, maxRows: 5 }, onChange: (value) => updateField("footer", value ?? ""), onBlur: () => commitField("footer"), style: {
141
84
  ...EDITOR_TEXTAREA_STYLE,
142
85
  color: "var(--ant-color-text-tertiary)",
143
86
  } })] }) }));
@@ -356,30 +356,6 @@ export declare const PHI_ENTRY_LIST_WIDGET_DEFINITION: {
356
356
  editorPlacement?: "inspector" | "geometry" | "toolbar";
357
357
  visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
358
358
  heading?: string;
359
- } & {
360
- type: "background";
361
- section?: string;
362
- storage?: import("../../../../../types").PhiCmsConfigFieldValueStorage;
363
- }) | ({
364
- key: string;
365
- label: string;
366
- description?: string;
367
- required?: boolean;
368
- editorPlacement?: "inspector" | "geometry" | "toolbar";
369
- visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
370
- heading?: string;
371
- } & {
372
- type: "border";
373
- section?: string;
374
- storage?: import("../../../../../types").PhiCmsConfigFieldValueStorage;
375
- }) | ({
376
- key: string;
377
- label: string;
378
- description?: string;
379
- required?: boolean;
380
- editorPlacement?: "inspector" | "geometry" | "toolbar";
381
- visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
382
- heading?: string;
383
359
  } & {
384
360
  type: "surface";
385
361
  tone?: false;
@@ -392,7 +368,7 @@ export declare const PHI_ENTRY_LIST_WIDGET_DEFINITION: {
392
368
  visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
393
369
  heading?: string;
394
370
  } & {
395
- type: "shadow" | "grid-placement" | "column-count";
371
+ type: "grid-placement" | "column-count";
396
372
  section?: string;
397
373
  }) | {
398
374
  key: string;
@@ -318,30 +318,6 @@ export declare const PHI_GALLERY_WIDGET_DEFINITION: {
318
318
  editorPlacement?: "inspector" | "geometry" | "toolbar";
319
319
  visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
320
320
  heading?: string;
321
- } & {
322
- type: "background";
323
- section?: string;
324
- storage?: import("../../../../../types").PhiCmsConfigFieldValueStorage;
325
- }) | ({
326
- key: string;
327
- label: string;
328
- description?: string;
329
- required?: boolean;
330
- editorPlacement?: "inspector" | "geometry" | "toolbar";
331
- visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
332
- heading?: string;
333
- } & {
334
- type: "border";
335
- section?: string;
336
- storage?: import("../../../../../types").PhiCmsConfigFieldValueStorage;
337
- }) | ({
338
- key: string;
339
- label: string;
340
- description?: string;
341
- required?: boolean;
342
- editorPlacement?: "inspector" | "geometry" | "toolbar";
343
- visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
344
- heading?: string;
345
321
  } & {
346
322
  type: "surface";
347
323
  tone?: false;
@@ -354,7 +330,7 @@ export declare const PHI_GALLERY_WIDGET_DEFINITION: {
354
330
  visibleWhen?: import("../../../../../types").PhiCmsConfigFieldVisibilityRule | readonly import("../../../../../types").PhiCmsConfigFieldVisibilityRule[];
355
331
  heading?: string;
356
332
  } & {
357
- type: "shadow" | "grid-placement" | "column-count";
333
+ type: "grid-placement" | "column-count";
358
334
  section?: string;
359
335
  }) | {
360
336
  key: string;
@@ -5,7 +5,7 @@ import { PhiCmsWidgetType } from "../../../../../constants/cms-widget-types";
5
5
  import { PhiImageAssetVariantKey } from "../../../../../constants/media";
6
6
  import { readPhiMediaImageSourceConfig } from "../../../../../components/widgets/config/image-source-parser";
7
7
  import { readBoolean, readCssSize, readRenderableBlockConfig, readString, } from "../../../../../components/widgets/config/parser-primitives";
8
- import { normalizePhiMaskConfig, } from "../../../../../components/widgets/config/mask";
8
+ import { readPhiMaskConfig, } from "../../../../../components/widgets/config/mask";
9
9
  function readImageSize(value) {
10
10
  if (!isPhiRecord(value)) {
11
11
  return undefined;
@@ -64,7 +64,7 @@ export function normalizePhiImageWidgetConfig(config) {
64
64
  borderTopRightRadius: readCssSize(raw.borderTopRightRadius),
65
65
  borderBottomLeftRadius: readCssSize(raw.borderBottomLeftRadius),
66
66
  borderBottomRightRadius: readCssSize(raw.borderBottomRightRadius),
67
- mask: normalizePhiMaskConfig(raw.mask),
67
+ mask: readPhiMaskConfig(raw.mask),
68
68
  };
69
69
  /*
70
70
  * An Asset without a word about its variant gets the Card variant; `null` is a word -- the Inspector's
@@ -1,4 +1,4 @@
1
- import type { PhiMarkdownTextAlign } from "../../../../../types/core-widget-placements";
1
+ import type { PhiMarkdownImageFloat, PhiMarkdownTextAlign } from "../../../../../types/core-widget-placements";
2
2
  import { type PhiSpacingScaleKey } from "../../../../../components/widgets/config/spacing-options";
3
3
  import type { PhiCmsResolvedContent } from "../../../../../types/cms";
4
4
  import { type PhiBulletListStyle } from "../../../../../components/widgets/shared/bullet-list-vocabulary";
@@ -13,6 +13,8 @@ export type PhiCmsMarkdownWidgetConfig = PhiCmsWidgetConfigBase & {
13
13
  /** Set at render from the node's `NoTranslate` flag, or by a caller that knows; never authored. */
14
14
  translate?: boolean;
15
15
  textAlign?: PhiMarkdownTextAlign;
16
+ /** Pictures stand at this edge and the text flows past them; absent, a picture is a line of its own. */
17
+ imageFloat?: PhiMarkdownImageFloat;
16
18
  textBlockSpacingBefore?: PhiSpacingScaleKey;
17
19
  textBlockSpacingAfter?: PhiSpacingScaleKey;
18
20
  headingBlockSpacingBefore?: PhiSpacingScaleKey;
@@ -9,6 +9,13 @@ const PHI_MARKDOWN_TEXT_ALIGN_OPTIONS = [
9
9
  { value: "end", label: "End" },
10
10
  { value: "justify", label: "Justify" },
11
11
  ];
12
+ const PHI_MARKDOWN_IMAGE_FLOAT_OPTIONS = [
13
+ { value: "start", label: "Start" },
14
+ { value: "end", label: "End" },
15
+ ];
16
+ function readMarkdownImageFloat(value) {
17
+ return value === "start" || value === "end" ? value : undefined;
18
+ }
12
19
  // The one spacing scale every padding and gap speaks (`theme/phi-tokens.ts`), stored by its key.
13
20
  const PHI_MARKDOWN_SPACING_OPTIONS = PHI_SPACING_SCALE_KEYS.map((key) => ({ value: key, label: key }));
14
21
  function readMarkdownSourceMode(value) {
@@ -41,6 +48,7 @@ export function parsePhiCmsMarkdownWidgetConfig(config) {
41
48
  sourceLocale: readString(config.sourceLocale),
42
49
  revalidateSeconds: readNumber(config.revalidateSeconds),
43
50
  textAlign: readMarkdownTextAlign(config.textAlign),
51
+ imageFloat: readMarkdownImageFloat(config.imageFloat),
44
52
  textBlockSpacingBefore: readMarkdownSpacingKey(config.textBlockSpacingBefore),
45
53
  textBlockSpacingAfter: readMarkdownSpacingKey(config.textBlockSpacingAfter),
46
54
  headingBlockSpacingBefore: readMarkdownSpacingKey(config.headingBlockSpacingBefore),
@@ -115,6 +123,12 @@ export const PHI_MARKDOWN_WIDGET_DEFINITION = {
115
123
  label: "Align",
116
124
  options: PHI_MARKDOWN_TEXT_ALIGN_OPTIONS,
117
125
  },
126
+ {
127
+ key: "imageFloat",
128
+ type: "choice",
129
+ label: "Image Float",
130
+ options: PHI_MARKDOWN_IMAGE_FLOAT_OPTIONS,
131
+ },
118
132
  {
119
133
  key: "textBlockSpacingBefore",
120
134
  type: "choice",
@@ -92,10 +92,9 @@ export function PhiMarkdownWidgetEditor({ blockId, config, onChange, onSourceLoc
92
92
  * as a capsule.
93
93
  *
94
94
  * `grown-md` is what that stylesheet would have given it -- the capsule of one Control line,
95
- * held at every height (THEME.md, "Control shape"). The fallback is the LARGE surface radius,
96
- * because it is the one radius on this element that a component token does not redefine.
95
+ * held at every height (THEME.md, "Control shape").
97
96
  */
98
- borderRadius: "var(--phi-control-radius-grown-md, var(--ant-border-radius-lg))",
97
+ borderRadius: "var(--phi-control-radius-grown-md)",
99
98
  background: "var(--ant-color-bg-container)",
100
99
  resize: "none",
101
100
  fontFamily: "var(--ant-font-family-code, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace)",