@aglyn/besigner-ui 1.0.0-beta.143

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 (486) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +7 -0
  3. package/package.json +73 -0
  4. package/src/besigner.d.ts +88 -0
  5. package/src/besigner.js +89 -0
  6. package/src/besigner.js.map +1 -0
  7. package/src/index.d.ts +17 -0
  8. package/src/index.js +18 -0
  9. package/src/index.js.map +1 -0
  10. package/src/lib/box-styler/box-styler.d.ts +57 -0
  11. package/src/lib/box-styler/box-styler.js +240 -0
  12. package/src/lib/box-styler/box-styler.js.map +1 -0
  13. package/src/lib/box-styler/components/box-diagram.d.ts +89 -0
  14. package/src/lib/box-styler/components/box-diagram.js +661 -0
  15. package/src/lib/box-styler/components/box-diagram.js.map +1 -0
  16. package/src/lib/box-styler/components/legend.d.ts +56 -0
  17. package/src/lib/box-styler/components/legend.js +130 -0
  18. package/src/lib/box-styler/components/legend.js.map +1 -0
  19. package/src/lib/box-styler/components/spacing-editor.d.ts +55 -0
  20. package/src/lib/box-styler/components/spacing-editor.js +347 -0
  21. package/src/lib/box-styler/components/spacing-editor.js.map +1 -0
  22. package/src/lib/box-styler/constants.d.ts +17 -0
  23. package/src/lib/box-styler/constants.js +30 -0
  24. package/src/lib/box-styler/constants.js.map +1 -0
  25. package/src/lib/box-styler/index.d.ts +2 -0
  26. package/src/lib/box-styler/index.js +4 -0
  27. package/src/lib/box-styler/index.js.map +1 -0
  28. package/src/lib/box-styler/region-fills.d.ts +107 -0
  29. package/src/lib/box-styler/region-fills.js +139 -0
  30. package/src/lib/box-styler/region-fills.js.map +1 -0
  31. package/src/lib/box-styler/spacing-value.d.ts +104 -0
  32. package/src/lib/box-styler/spacing-value.js +223 -0
  33. package/src/lib/box-styler/spacing-value.js.map +1 -0
  34. package/src/lib/box-styler/types.d.ts +29 -0
  35. package/src/lib/box-styler/types.js +18 -0
  36. package/src/lib/box-styler/types.js.map +1 -0
  37. package/src/lib/components/accordion-list.component.d.ts +50 -0
  38. package/src/lib/components/accordion-list.component.js +228 -0
  39. package/src/lib/components/accordion-list.component.js.map +1 -0
  40. package/src/lib/components/add-controls.component.d.ts +22 -0
  41. package/src/lib/components/add-controls.component.js +50 -0
  42. package/src/lib/components/add-controls.component.js.map +1 -0
  43. package/src/lib/components/app-bar-breadcrumbs.component.d.ts +29 -0
  44. package/src/lib/components/app-bar-breadcrumbs.component.js +279 -0
  45. package/src/lib/components/app-bar-breadcrumbs.component.js.map +1 -0
  46. package/src/lib/components/app-bar-primary.component.d.ts +21 -0
  47. package/src/lib/components/app-bar-primary.component.js +108 -0
  48. package/src/lib/components/app-bar-primary.component.js.map +1 -0
  49. package/src/lib/components/app-bar-secondary.component.d.ts +21 -0
  50. package/src/lib/components/app-bar-secondary.component.js +94 -0
  51. package/src/lib/components/app-bar-secondary.component.js.map +1 -0
  52. package/src/lib/components/aside-panel.component.d.ts +23 -0
  53. package/src/lib/components/aside-panel.component.js +505 -0
  54. package/src/lib/components/aside-panel.component.js.map +1 -0
  55. package/src/lib/components/besigner-conflict-alert.component.d.ts +36 -0
  56. package/src/lib/components/besigner-conflict-alert.component.js +51 -0
  57. package/src/lib/components/besigner-conflict-alert.component.js.map +1 -0
  58. package/src/lib/components/besigner-dnd-context.component.d.ts +32 -0
  59. package/src/lib/components/besigner-dnd-context.component.js +75 -0
  60. package/src/lib/components/besigner-dnd-context.component.js.map +1 -0
  61. package/src/lib/components/besigner-draft-alert.component.d.ts +70 -0
  62. package/src/lib/components/besigner-draft-alert.component.js +155 -0
  63. package/src/lib/components/besigner-draft-alert.component.js.map +1 -0
  64. package/src/lib/components/besigner-root-provider.component.d.ts +33 -0
  65. package/src/lib/components/besigner-root-provider.component.js +68 -0
  66. package/src/lib/components/besigner-root-provider.component.js.map +1 -0
  67. package/src/lib/components/binding-preview-controls.component.d.ts +28 -0
  68. package/src/lib/components/binding-preview-controls.component.js +63 -0
  69. package/src/lib/components/binding-preview-controls.component.js.map +1 -0
  70. package/src/lib/components/closeable-drawer.component.d.ts +32 -0
  71. package/src/lib/components/closeable-drawer.component.js +134 -0
  72. package/src/lib/components/closeable-drawer.component.js.map +1 -0
  73. package/src/lib/components/component-accordion-list.d.ts +28 -0
  74. package/src/lib/components/component-accordion-list.js +205 -0
  75. package/src/lib/components/component-accordion-list.js.map +1 -0
  76. package/src/lib/components/component-icon.component.d.ts +32 -0
  77. package/src/lib/components/component-icon.component.js +51 -0
  78. package/src/lib/components/component-icon.component.js.map +1 -0
  79. package/src/lib/components/component-picker.d.ts +27 -0
  80. package/src/lib/components/component-picker.js +368 -0
  81. package/src/lib/components/component-picker.js.map +1 -0
  82. package/src/lib/components/components-grid-list.component.d.ts +31 -0
  83. package/src/lib/components/components-grid-list.component.js +162 -0
  84. package/src/lib/components/components-grid-list.component.js.map +1 -0
  85. package/src/lib/components/custom-css-form.component.d.ts +115 -0
  86. package/src/lib/components/custom-css-form.component.js +699 -0
  87. package/src/lib/components/custom-css-form.component.js.map +1 -0
  88. package/src/lib/components/device-preview-controls.component.d.ts +22 -0
  89. package/src/lib/components/device-preview-controls.component.js +150 -0
  90. package/src/lib/components/device-preview-controls.component.js.map +1 -0
  91. package/src/lib/components/dnd/canvas-drop-indicator.d.ts +20 -0
  92. package/src/lib/components/dnd/canvas-drop-indicator.js +61 -0
  93. package/src/lib/components/dnd/canvas-drop-indicator.js.map +1 -0
  94. package/src/lib/components/dnd/draggable-droppable.d.ts +31 -0
  95. package/src/lib/components/dnd/draggable-droppable.js +383 -0
  96. package/src/lib/components/dnd/draggable-droppable.js.map +1 -0
  97. package/src/lib/components/dnd/draggable.d.ts +42 -0
  98. package/src/lib/components/dnd/draggable.js +66 -0
  99. package/src/lib/components/dnd/draggable.js.map +1 -0
  100. package/src/lib/components/dnd/drop-indicator.d.ts +31 -0
  101. package/src/lib/components/dnd/drop-indicator.js +139 -0
  102. package/src/lib/components/dnd/drop-indicator.js.map +1 -0
  103. package/src/lib/components/dnd/droppable.d.ts +43 -0
  104. package/src/lib/components/dnd/droppable.js +63 -0
  105. package/src/lib/components/dnd/droppable.js.map +1 -0
  106. package/src/lib/components/element-card.component.d.ts +23 -0
  107. package/src/lib/components/element-card.component.js +49 -0
  108. package/src/lib/components/element-card.component.js.map +1 -0
  109. package/src/lib/components/element-classes-field.component.d.ts +41 -0
  110. package/src/lib/components/element-classes-field.component.js +140 -0
  111. package/src/lib/components/element-classes-field.component.js.map +1 -0
  112. package/src/lib/components/element-detail-overlay.component.d.ts +55 -0
  113. package/src/lib/components/element-detail-overlay.component.js +157 -0
  114. package/src/lib/components/element-detail-overlay.component.js.map +1 -0
  115. package/src/lib/components/element-detail.component.d.ts +56 -0
  116. package/src/lib/components/element-detail.component.js +171 -0
  117. package/src/lib/components/element-detail.component.js.map +1 -0
  118. package/src/lib/components/element-info-details.component.d.ts +18 -0
  119. package/src/lib/components/element-info-details.component.js +169 -0
  120. package/src/lib/components/element-info-details.component.js.map +1 -0
  121. package/src/lib/components/element-interactions-form.component.d.ts +21 -0
  122. package/src/lib/components/element-interactions-form.component.js +199 -0
  123. package/src/lib/components/element-interactions-form.component.js.map +1 -0
  124. package/src/lib/components/element-preview.component.d.ts +75 -0
  125. package/src/lib/components/element-preview.component.js +346 -0
  126. package/src/lib/components/element-preview.component.js.map +1 -0
  127. package/src/lib/components/element-props-form.component.d.ts +565 -0
  128. package/src/lib/components/element-props-form.component.js +2392 -0
  129. package/src/lib/components/element-props-form.component.js.map +1 -0
  130. package/src/lib/components/element-styles-form-template.component.d.ts +27 -0
  131. package/src/lib/components/element-styles-form-template.component.js +89 -0
  132. package/src/lib/components/element-styles-form-template.component.js.map +1 -0
  133. package/src/lib/components/element-styles-form.component.d.ts +52 -0
  134. package/src/lib/components/element-styles-form.component.js +1350 -0
  135. package/src/lib/components/element-styles-form.component.js.map +1 -0
  136. package/src/lib/components/empty-document-slot.d.ts +39 -0
  137. package/src/lib/components/empty-document-slot.js +121 -0
  138. package/src/lib/components/empty-document-slot.js.map +1 -0
  139. package/src/lib/components/empty-results.d.ts +22 -0
  140. package/src/lib/components/empty-results.js +131 -0
  141. package/src/lib/components/empty-results.js.map +1 -0
  142. package/src/lib/components/history-controls.component.d.ts +21 -0
  143. package/src/lib/components/history-controls.component.js +67 -0
  144. package/src/lib/components/history-controls.component.js.map +1 -0
  145. package/src/lib/components/inline-markdown-editor.component.d.ts +17 -0
  146. package/src/lib/components/inline-markdown-editor.component.js +220 -0
  147. package/src/lib/components/inline-markdown-editor.component.js.map +1 -0
  148. package/src/lib/components/inline-text-editor.component.d.ts +48 -0
  149. package/src/lib/components/inline-text-editor.component.js +905 -0
  150. package/src/lib/components/inline-text-editor.component.js.map +1 -0
  151. package/src/lib/components/insert-token-menu.component.d.ts +86 -0
  152. package/src/lib/components/insert-token-menu.component.js +232 -0
  153. package/src/lib/components/insert-token-menu.component.js.map +1 -0
  154. package/src/lib/components/instance-attr-overrides.component.d.ts +50 -0
  155. package/src/lib/components/instance-attr-overrides.component.js +298 -0
  156. package/src/lib/components/instance-attr-overrides.component.js.map +1 -0
  157. package/src/lib/components/interact-controls.component.d.ts +22 -0
  158. package/src/lib/components/interact-controls.component.js +74 -0
  159. package/src/lib/components/interact-controls.component.js.map +1 -0
  160. package/src/lib/components/markdown-attribute-field.component.d.ts +40 -0
  161. package/src/lib/components/markdown-attribute-field.component.js +103 -0
  162. package/src/lib/components/markdown-attribute-field.component.js.map +1 -0
  163. package/src/lib/components/node-card.d.ts +24 -0
  164. package/src/lib/components/node-card.js +86 -0
  165. package/src/lib/components/node-card.js.map +1 -0
  166. package/src/lib/components/node-context-menu.d.ts +25 -0
  167. package/src/lib/components/node-context-menu.js +558 -0
  168. package/src/lib/components/node-context-menu.js.map +1 -0
  169. package/src/lib/components/node-leaf.d.ts +39 -0
  170. package/src/lib/components/node-leaf.js +628 -0
  171. package/src/lib/components/node-leaf.js.map +1 -0
  172. package/src/lib/components/node-outline.d.ts +24 -0
  173. package/src/lib/components/node-outline.js +156 -0
  174. package/src/lib/components/node-outline.js.map +1 -0
  175. package/src/lib/components/node-overlay.d.ts +23 -0
  176. package/src/lib/components/node-overlay.js +299 -0
  177. package/src/lib/components/node-overlay.js.map +1 -0
  178. package/src/lib/components/node-quick-actions.d.ts +24 -0
  179. package/src/lib/components/node-quick-actions.js +284 -0
  180. package/src/lib/components/node-quick-actions.js.map +1 -0
  181. package/src/lib/components/node-tree-view.d.ts +106 -0
  182. package/src/lib/components/node-tree-view.js +820 -0
  183. package/src/lib/components/node-tree-view.js.map +1 -0
  184. package/src/lib/components/panel-controls.component.d.ts +22 -0
  185. package/src/lib/components/panel-controls.component.js +76 -0
  186. package/src/lib/components/panel-controls.component.js.map +1 -0
  187. package/src/lib/components/picker-search-field.d.ts +38 -0
  188. package/src/lib/components/picker-search-field.js +79 -0
  189. package/src/lib/components/picker-search-field.js.map +1 -0
  190. package/src/lib/components/plugin-settings-field.component.d.ts +38 -0
  191. package/src/lib/components/plugin-settings-field.component.js +264 -0
  192. package/src/lib/components/plugin-settings-field.component.js.map +1 -0
  193. package/src/lib/components/properties-dialog.component.d.ts +22 -0
  194. package/src/lib/components/properties-dialog.component.js +40 -0
  195. package/src/lib/components/properties-dialog.component.js.map +1 -0
  196. package/src/lib/components/property-binding-field.component.d.ts +142 -0
  197. package/src/lib/components/property-binding-field.component.js +325 -0
  198. package/src/lib/components/property-binding-field.component.js.map +1 -0
  199. package/src/lib/components/property-values-form.component.d.ts +43 -0
  200. package/src/lib/components/property-values-form.component.js +223 -0
  201. package/src/lib/components/property-values-form.component.js.map +1 -0
  202. package/src/lib/components/scheme-preview-controls.component.d.ts +26 -0
  203. package/src/lib/components/scheme-preview-controls.component.js +56 -0
  204. package/src/lib/components/scheme-preview-controls.component.js.map +1 -0
  205. package/src/lib/components/screen-link-field.component.d.ts +118 -0
  206. package/src/lib/components/screen-link-field.component.js +282 -0
  207. package/src/lib/components/screen-link-field.component.js.map +1 -0
  208. package/src/lib/components/site-theme-color-tokens-provider.component.d.ts +33 -0
  209. package/src/lib/components/site-theme-color-tokens-provider.component.js +53 -0
  210. package/src/lib/components/site-theme-color-tokens-provider.component.js.map +1 -0
  211. package/src/lib/components/subtree-json-dialog.component.d.ts +46 -0
  212. package/src/lib/components/subtree-json-dialog.component.js +125 -0
  213. package/src/lib/components/subtree-json-dialog.component.js.map +1 -0
  214. package/src/lib/components/token-pill.component.d.ts +51 -0
  215. package/src/lib/components/token-pill.component.js +164 -0
  216. package/src/lib/components/token-pill.component.js.map +1 -0
  217. package/src/lib/components/token-text-field.component.d.ts +146 -0
  218. package/src/lib/components/token-text-field.component.js +603 -0
  219. package/src/lib/components/token-text-field.component.js.map +1 -0
  220. package/src/lib/components/tree/tree-list.d.ts +6 -0
  221. package/src/lib/components/tree/tree-list.js +108 -0
  222. package/src/lib/components/tree/tree-list.js.map +1 -0
  223. package/src/lib/components/viewport-canvas.component.d.ts +23 -0
  224. package/src/lib/components/viewport-canvas.component.js +128 -0
  225. package/src/lib/components/viewport-canvas.component.js.map +1 -0
  226. package/src/lib/components/viewport-frame.component.d.ts +47 -0
  227. package/src/lib/components/viewport-frame.component.js +332 -0
  228. package/src/lib/components/viewport-frame.component.js.map +1 -0
  229. package/src/lib/components/viewport-root.component.d.ts +22 -0
  230. package/src/lib/components/viewport-root.component.js +112 -0
  231. package/src/lib/components/viewport-root.component.js.map +1 -0
  232. package/src/lib/components/viewport-zoom-controls.d.ts +29 -0
  233. package/src/lib/components/viewport-zoom-controls.js +147 -0
  234. package/src/lib/components/viewport-zoom-controls.js.map +1 -0
  235. package/src/lib/components/workspace-editor.component.d.ts +23 -0
  236. package/src/lib/components/workspace-editor.component.js +191 -0
  237. package/src/lib/components/workspace-editor.component.js.map +1 -0
  238. package/src/lib/components/workspace-panel.component.d.ts +28 -0
  239. package/src/lib/components/workspace-panel.component.js +147 -0
  240. package/src/lib/components/workspace-panel.component.js.map +1 -0
  241. package/src/lib/constants/sample-data.d.ts +18 -0
  242. package/src/lib/constants/sample-data.js +106 -0
  243. package/src/lib/constants/sample-data.js.map +1 -0
  244. package/src/lib/constants/shared.d.ts +17 -0
  245. package/src/lib/constants/shared.js +18 -0
  246. package/src/lib/constants/shared.js.map +1 -0
  247. package/src/lib/contexts/besigner-app-context.d.ts +30 -0
  248. package/src/lib/contexts/besigner-app-context.js +41 -0
  249. package/src/lib/contexts/besigner-app-context.js.map +1 -0
  250. package/src/lib/contexts/binding-picker-context.d.ts +78 -0
  251. package/src/lib/contexts/binding-picker-context.js +21 -0
  252. package/src/lib/contexts/binding-picker-context.js.map +1 -0
  253. package/src/lib/contexts/canvas-host-tokens-context.d.ts +49 -0
  254. package/src/lib/contexts/canvas-host-tokens-context.js +63 -0
  255. package/src/lib/contexts/canvas-host-tokens-context.js.map +1 -0
  256. package/src/lib/contexts/canvas-reveal-context.d.ts +32 -0
  257. package/src/lib/contexts/canvas-reveal-context.js +34 -0
  258. package/src/lib/contexts/canvas-reveal-context.js.map +1 -0
  259. package/src/lib/contexts/component-promotion-context.d.ts +89 -0
  260. package/src/lib/contexts/component-promotion-context.js +21 -0
  261. package/src/lib/contexts/component-promotion-context.js.map +1 -0
  262. package/src/lib/contexts/components-drawer-context.provider.d.ts +32 -0
  263. package/src/lib/contexts/components-drawer-context.provider.js +80 -0
  264. package/src/lib/contexts/components-drawer-context.provider.js.map +1 -0
  265. package/src/lib/contexts/element-drawer-context.d.ts +1005 -0
  266. package/src/lib/contexts/element-drawer-context.js +45 -0
  267. package/src/lib/contexts/element-drawer-context.js.map +1 -0
  268. package/src/lib/contexts/inspected-selection.d.ts +32 -0
  269. package/src/lib/contexts/inspected-selection.js +38 -0
  270. package/src/lib/contexts/inspected-selection.js.map +1 -0
  271. package/src/lib/contexts/inspector-extras-context.d.ts +56 -0
  272. package/src/lib/contexts/inspector-extras-context.js +35 -0
  273. package/src/lib/contexts/inspector-extras-context.js.map +1 -0
  274. package/src/lib/contexts/interactions-context.d.ts +99 -0
  275. package/src/lib/contexts/interactions-context.js +22 -0
  276. package/src/lib/contexts/interactions-context.js.map +1 -0
  277. package/src/lib/contexts/layout-chrome-context.d.ts +37 -0
  278. package/src/lib/contexts/layout-chrome-context.js +75 -0
  279. package/src/lib/contexts/layout-chrome-context.js.map +1 -0
  280. package/src/lib/contexts/media-asset-facts-context.d.ts +74 -0
  281. package/src/lib/contexts/media-asset-facts-context.js +27 -0
  282. package/src/lib/contexts/media-asset-facts-context.js.map +1 -0
  283. package/src/lib/contexts/media-picker-context.d.ts +88 -0
  284. package/src/lib/contexts/media-picker-context.js +21 -0
  285. package/src/lib/contexts/media-picker-context.js.map +1 -0
  286. package/src/lib/contexts/rendered-canvas-elements.d.ts +42 -0
  287. package/src/lib/contexts/rendered-canvas-elements.js +79 -0
  288. package/src/lib/contexts/rendered-canvas-elements.js.map +1 -0
  289. package/src/lib/contexts/repeat-record-context.d.ts +38 -0
  290. package/src/lib/contexts/repeat-record-context.js +21 -0
  291. package/src/lib/contexts/repeat-record-context.js.map +1 -0
  292. package/src/lib/contexts/repeat-rows-context.d.ts +53 -0
  293. package/src/lib/contexts/repeat-rows-context.js +38 -0
  294. package/src/lib/contexts/repeat-rows-context.js.map +1 -0
  295. package/src/lib/contexts/toolbar-extras-context.d.ts +31 -0
  296. package/src/lib/contexts/toolbar-extras-context.js +33 -0
  297. package/src/lib/contexts/toolbar-extras-context.js.map +1 -0
  298. package/src/lib/drafts/besigner-draft-store.d.ts +110 -0
  299. package/src/lib/drafts/besigner-draft-store.js +125 -0
  300. package/src/lib/drafts/besigner-draft-store.js.map +1 -0
  301. package/src/lib/drafts/besigner-server-draft.d.ts +130 -0
  302. package/src/lib/drafts/besigner-server-draft.js +211 -0
  303. package/src/lib/drafts/besigner-server-draft.js.map +1 -0
  304. package/src/lib/form-fields/components/shared/inline-form-control.d.ts +32 -0
  305. package/src/lib/form-fields/components/shared/inline-form-control.js +85 -0
  306. package/src/lib/form-fields/components/shared/inline-form-control.js.map +1 -0
  307. package/src/lib/form-fields/components/shared/toggle-button-form-control.d.ts +33 -0
  308. package/src/lib/form-fields/components/shared/toggle-button-form-control.js +83 -0
  309. package/src/lib/form-fields/components/shared/toggle-button-form-control.js.map +1 -0
  310. package/src/lib/form-fields/form-fields.d.ts +20 -0
  311. package/src/lib/form-fields/form-fields.js +21 -0
  312. package/src/lib/form-fields/form-fields.js.map +1 -0
  313. package/src/lib/form-fields/index.d.ts +1 -0
  314. package/src/lib/form-fields/index.js +3 -0
  315. package/src/lib/form-fields/index.js.map +1 -0
  316. package/src/lib/hooks/use-add-element-drawer-callback.d.ts +28 -0
  317. package/src/lib/hooks/use-add-element-drawer-callback.js +112 -0
  318. package/src/lib/hooks/use-add-element-drawer-callback.js.map +1 -0
  319. package/src/lib/hooks/use-aglyn-besigner-flag.d.ts +23 -0
  320. package/src/lib/hooks/use-aglyn-besigner-flag.js +47 -0
  321. package/src/lib/hooks/use-aglyn-besigner-flag.js.map +1 -0
  322. package/src/lib/hooks/use-aglyn-besigner-panel-value.d.ts +23 -0
  323. package/src/lib/hooks/use-aglyn-besigner-panel-value.js +54 -0
  324. package/src/lib/hooks/use-aglyn-besigner-panel-value.js.map +1 -0
  325. package/src/lib/hooks/use-aglyn-besigner-panel.d.ts +23 -0
  326. package/src/lib/hooks/use-aglyn-besigner-panel.js +47 -0
  327. package/src/lib/hooks/use-aglyn-besigner-panel.js.map +1 -0
  328. package/src/lib/hooks/use-anchored-rect.d.ts +68 -0
  329. package/src/lib/hooks/use-anchored-rect.js +131 -0
  330. package/src/lib/hooks/use-anchored-rect.js.map +1 -0
  331. package/src/lib/hooks/use-besigner-app-context.d.ts +18 -0
  332. package/src/lib/hooks/use-besigner-app-context.js +19 -0
  333. package/src/lib/hooks/use-besigner-app-context.js.map +1 -0
  334. package/src/lib/hooks/use-besigner-document.d.ts +309 -0
  335. package/src/lib/hooks/use-besigner-document.js +500 -0
  336. package/src/lib/hooks/use-besigner-document.js.map +1 -0
  337. package/src/lib/hooks/use-besigner-draft.d.ts +209 -0
  338. package/src/lib/hooks/use-besigner-draft.js +429 -0
  339. package/src/lib/hooks/use-besigner-draft.js.map +1 -0
  340. package/src/lib/hooks/use-clipboard-callbacks.d.ts +29 -0
  341. package/src/lib/hooks/use-clipboard-callbacks.js +82 -0
  342. package/src/lib/hooks/use-clipboard-callbacks.js.map +1 -0
  343. package/src/lib/hooks/use-clipboard-shortcuts.d.ts +52 -0
  344. package/src/lib/hooks/use-clipboard-shortcuts.js +102 -0
  345. package/src/lib/hooks/use-clipboard-shortcuts.js.map +1 -0
  346. package/src/lib/hooks/use-component-propagation-notice.d.ts +96 -0
  347. package/src/lib/hooks/use-component-propagation-notice.js +177 -0
  348. package/src/lib/hooks/use-component-propagation-notice.js.map +1 -0
  349. package/src/lib/hooks/use-debounced-commit.d.ts +43 -0
  350. package/src/lib/hooks/use-debounced-commit.js +88 -0
  351. package/src/lib/hooks/use-debounced-commit.js.map +1 -0
  352. package/src/lib/hooks/use-delete-element-callback.d.ts +24 -0
  353. package/src/lib/hooks/use-delete-element-callback.js +82 -0
  354. package/src/lib/hooks/use-delete-element-callback.js.map +1 -0
  355. package/src/lib/hooks/use-detail-hover-intent.d.ts +76 -0
  356. package/src/lib/hooks/use-detail-hover-intent.js +123 -0
  357. package/src/lib/hooks/use-detail-hover-intent.js.map +1 -0
  358. package/src/lib/hooks/use-document-repair-callbacks.d.ts +50 -0
  359. package/src/lib/hooks/use-document-repair-callbacks.js +237 -0
  360. package/src/lib/hooks/use-document-repair-callbacks.js.map +1 -0
  361. package/src/lib/hooks/use-insert-token-options.d.ts +52 -0
  362. package/src/lib/hooks/use-insert-token-options.js +230 -0
  363. package/src/lib/hooks/use-insert-token-options.js.map +1 -0
  364. package/src/lib/hooks/use-leaf-drag.d.ts +24 -0
  365. package/src/lib/hooks/use-leaf-drag.js +28 -0
  366. package/src/lib/hooks/use-leaf-drag.js.map +1 -0
  367. package/src/lib/hooks/use-leaf-drop.d.ts +31 -0
  368. package/src/lib/hooks/use-leaf-drop.js +43 -0
  369. package/src/lib/hooks/use-leaf-drop.js.map +1 -0
  370. package/src/lib/hooks/use-markdown-media-picker.d.ts +63 -0
  371. package/src/lib/hooks/use-markdown-media-picker.js +110 -0
  372. package/src/lib/hooks/use-markdown-media-picker.js.map +1 -0
  373. package/src/lib/hooks/use-media-asset-facts-overlay.d.ts +43 -0
  374. package/src/lib/hooks/use-media-asset-facts-overlay.js +133 -0
  375. package/src/lib/hooks/use-media-asset-facts-overlay.js.map +1 -0
  376. package/src/lib/hooks/use-move-node-callbacks.d.ts +22 -0
  377. package/src/lib/hooks/use-move-node-callbacks.js +57 -0
  378. package/src/lib/hooks/use-move-node-callbacks.js.map +1 -0
  379. package/src/lib/hooks/use-node-with-host-tokens.d.ts +42 -0
  380. package/src/lib/hooks/use-node-with-host-tokens.js +59 -0
  381. package/src/lib/hooks/use-node-with-host-tokens.js.map +1 -0
  382. package/src/lib/hooks/use-picker-filter.d.ts +61 -0
  383. package/src/lib/hooks/use-picker-filter.js +122 -0
  384. package/src/lib/hooks/use-picker-filter.js.map +1 -0
  385. package/src/lib/hooks/use-repeat-preview.d.ts +64 -0
  386. package/src/lib/hooks/use-repeat-preview.js +214 -0
  387. package/src/lib/hooks/use-repeat-preview.js.map +1 -0
  388. package/src/lib/hooks/use-repeat-sources.d.ts +17 -0
  389. package/src/lib/hooks/use-repeat-sources.js +41 -0
  390. package/src/lib/hooks/use-repeat-sources.js.map +1 -0
  391. package/src/lib/hooks/use-visible-component-categories.d.ts +37 -0
  392. package/src/lib/hooks/use-visible-component-categories.js +79 -0
  393. package/src/lib/hooks/use-visible-component-categories.js.map +1 -0
  394. package/src/lib/utils/active-host-theme.d.ts +28 -0
  395. package/src/lib/utils/active-host-theme.js +77 -0
  396. package/src/lib/utils/active-host-theme.js.map +1 -0
  397. package/src/lib/utils/attr-target.d.ts +170 -0
  398. package/src/lib/utils/attr-target.js +229 -0
  399. package/src/lib/utils/attr-target.js.map +1 -0
  400. package/src/lib/utils/attribute-field-value.d.ts +21 -0
  401. package/src/lib/utils/attribute-field-value.js +28 -0
  402. package/src/lib/utils/attribute-field-value.js.map +1 -0
  403. package/src/lib/utils/canvas-context-menu.d.ts +28 -0
  404. package/src/lib/utils/canvas-context-menu.js +56 -0
  405. package/src/lib/utils/canvas-context-menu.js.map +1 -0
  406. package/src/lib/utils/canvas-reveal.d.ts +115 -0
  407. package/src/lib/utils/canvas-reveal.js +165 -0
  408. package/src/lib/utils/canvas-reveal.js.map +1 -0
  409. package/src/lib/utils/describe-element.d.ts +72 -0
  410. package/src/lib/utils/describe-element.js +159 -0
  411. package/src/lib/utils/describe-element.js.map +1 -0
  412. package/src/lib/utils/device-preview-styles.d.ts +89 -0
  413. package/src/lib/utils/device-preview-styles.js +228 -0
  414. package/src/lib/utils/device-preview-styles.js.map +1 -0
  415. package/src/lib/utils/docs-help.d.ts +22 -0
  416. package/src/lib/utils/docs-help.generated.d.ts +47 -0
  417. package/src/lib/utils/docs-help.generated.js +229 -0
  418. package/src/lib/utils/docs-help.generated.js.map +1 -0
  419. package/src/lib/utils/docs-help.js +37 -0
  420. package/src/lib/utils/docs-help.js.map +1 -0
  421. package/src/lib/utils/held-answers-store.d.ts +59 -0
  422. package/src/lib/utils/held-answers-store.js +94 -0
  423. package/src/lib/utils/held-answers-store.js.map +1 -0
  424. package/src/lib/utils/in-place-edit-surface.d.ts +118 -0
  425. package/src/lib/utils/in-place-edit-surface.js +164 -0
  426. package/src/lib/utils/in-place-edit-surface.js.map +1 -0
  427. package/src/lib/utils/inline-markdown-edit.store.d.ts +71 -0
  428. package/src/lib/utils/inline-markdown-edit.store.js +90 -0
  429. package/src/lib/utils/inline-markdown-edit.store.js.map +1 -0
  430. package/src/lib/utils/inline-text-edit.store.d.ts +85 -0
  431. package/src/lib/utils/inline-text-edit.store.js +78 -0
  432. package/src/lib/utils/inline-text-edit.store.js.map +1 -0
  433. package/src/lib/utils/instance-leaf-hit.d.ts +39 -0
  434. package/src/lib/utils/instance-leaf-hit.js +56 -0
  435. package/src/lib/utils/instance-leaf-hit.js.map +1 -0
  436. package/src/lib/utils/muted-classes.d.ts +76 -0
  437. package/src/lib/utils/muted-classes.js +108 -0
  438. package/src/lib/utils/muted-classes.js.map +1 -0
  439. package/src/lib/utils/muted-styles.d.ts +125 -0
  440. package/src/lib/utils/muted-styles.js +193 -0
  441. package/src/lib/utils/muted-styles.js.map +1 -0
  442. package/src/lib/utils/numeric-text-value.d.ts +42 -0
  443. package/src/lib/utils/numeric-text-value.js +47 -0
  444. package/src/lib/utils/numeric-text-value.js.map +1 -0
  445. package/src/lib/utils/preset-interactions.d.ts +37 -0
  446. package/src/lib/utils/preset-interactions.js +87 -0
  447. package/src/lib/utils/preset-interactions.js.map +1 -0
  448. package/src/lib/utils/publish-failure-message.d.ts +35 -0
  449. package/src/lib/utils/publish-failure-message.js +51 -0
  450. package/src/lib/utils/publish-failure-message.js.map +1 -0
  451. package/src/lib/utils/rank-picker-results.d.ts +45 -0
  452. package/src/lib/utils/rank-picker-results.js +136 -0
  453. package/src/lib/utils/rank-picker-results.js.map +1 -0
  454. package/src/lib/utils/responsive-sx.d.ts +65 -0
  455. package/src/lib/utils/responsive-sx.js +129 -0
  456. package/src/lib/utils/responsive-sx.js.map +1 -0
  457. package/src/lib/utils/rich-text-commands.d.ts +28 -0
  458. package/src/lib/utils/rich-text-commands.js +43 -0
  459. package/src/lib/utils/rich-text-commands.js.map +1 -0
  460. package/src/lib/utils/sanitize-rich-text.d.ts +52 -0
  461. package/src/lib/utils/sanitize-rich-text.js +173 -0
  462. package/src/lib/utils/sanitize-rich-text.js.map +1 -0
  463. package/src/lib/utils/state-sx.d.ts +255 -0
  464. package/src/lib/utils/state-sx.js +338 -0
  465. package/src/lib/utils/state-sx.js.map +1 -0
  466. package/src/lib/utils/style-field-groups.d.ts +136 -0
  467. package/src/lib/utils/style-field-groups.js +874 -0
  468. package/src/lib/utils/style-field-groups.js.map +1 -0
  469. package/src/lib/utils/style-field-search.d.ts +84 -0
  470. package/src/lib/utils/style-field-search.js +505 -0
  471. package/src/lib/utils/style-field-search.js.map +1 -0
  472. package/src/lib/utils/style-target.d.ts +69 -0
  473. package/src/lib/utils/style-target.js +203 -0
  474. package/src/lib/utils/style-target.js.map +1 -0
  475. package/src/lib/utils/theme-scale-options.d.ts +169 -0
  476. package/src/lib/utils/theme-scale-options.js +702 -0
  477. package/src/lib/utils/theme-scale-options.js.map +1 -0
  478. package/src/lib/utils/token-editable-dom.d.ts +82 -0
  479. package/src/lib/utils/token-editable-dom.js +280 -0
  480. package/src/lib/utils/token-editable-dom.js.map +1 -0
  481. package/src/lib/utils/token-segments.d.ts +111 -0
  482. package/src/lib/utils/token-segments.js +246 -0
  483. package/src/lib/utils/token-segments.js.map +1 -0
  484. package/src/lib/utils/visibility-styles.d.ts +38 -0
  485. package/src/lib/utils/visibility-styles.js +54 -0
  486. package/src/lib/utils/visibility-styles.js.map +1 -0
@@ -0,0 +1,2392 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
3
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
4
+ /**
5
+ * @license
6
+ * Copyright 2026 Aglyn LLC
7
+ *
8
+ * Licensed under the Apache License, Version 2.0 (the "License");
9
+ * you may not use this file except in compliance with the License.
10
+ * You may obtain a copy of the License at
11
+ *
12
+ * http://www.apache.org/licenses/LICENSE-2.0
13
+ *
14
+ * Unless required by applicable law or agreed to in writing, software
15
+ * distributed under the License is distributed on an "AS IS" BASIS,
16
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ * See the License for the specific language governing permissions and
18
+ * limitations under the License.
19
+ */ import PluginSettingsField, { PLUGIN_SETTINGS_FIELD_COMPONENT } from "./plugin-settings-field.component.js";
20
+ import { ANIMATION_DEFAULT_DELAY_MS, ANIMATION_DEFAULT_DURATION_MS, ANIMATION_DEFAULT_EASE, ANIMATION_DEFAULT_STAGGER_STEP_MS, ANIMATION_DEFAULT_TRIGGER, ANIMATION_MAX_DURATION_MS, ANIMATION_MAX_STAGGER_STEP_MS, ANIMATION_NONE, ANIMATION_PRESETS, canvas, COMPONENT_PROP_NAME_PATTERN, components, displayBindingTokens, ENTITY_PICKER_BROWSE_LIMIT, entityListState, EntityPickerContext, entityValueNeedsResolution, FEATURE_FLAG, FieldComponentType, getKnownPluginInstalls, getKnownPluginInstallsVersion, hasBindings, inheritedMediaAlt, intrinsicMediaSize, linkTargetKind, VIDEO_COMPONENT_ID, videoMediaProps, MISSING_BINDING_LABEL, NODE_ANIMATION_DELAY_PROP, NODE_ANIMATION_DURATION_PROP, NODE_ANIMATION_EASE_PROP, NODE_ANIMATION_PROP, NODE_ANIMATION_REPEAT_PROP, NODE_ANIMATION_STAGGER_PROP, NODE_ANIMATION_STAGGER_STEP_PROP, NODE_ANIMATION_TRIGGER_PROP, NODE_HIDE_IF_PROP, NODE_HIDE_UNLESS_PROP, normalizeBindingTokens, readInstanceIconValue, readReusablePropValue, readYesNoValue, reusablePropConditionValue, reusablePropDefaultValue, reusablePropKind, reusablePropTakesSeveral, evaluateReusablePropRule, REUSABLE_INSTANCE_COMPONENT_ID, REUSABLE_INSTANCE_PROP_VALUES_KEY, ScreenLinkContext, screenLinkTargetOptions, subscribeKnownPluginInstalls, unresolvedScreenOption } from "@aglyn/aglyn";
21
+ // By path, not the `@aglyn/aglyn` barrel: a repeat's bounds and the sources
22
+ // an editor offers are editor business, and a published page must not pull
23
+ // either in behind them.
24
+ import { repeatScope } from "@aglyn/aglyn/app-utils/expand-repeatables";
25
+ import { REPEAT_NODE_CAPABILITY } from "@aglyn/aglyn/app-utils/node-capabilities";
26
+ import { repeatSourceOf } from "@aglyn/aglyn/app-utils/repeat-sources";
27
+ import { useAglynSiteTheme } from "@aglyn/aglyn-node-renderer";
28
+ import { FormRenderer, FormSpy, FIELD_MAP_BREAKPOINT_SPAN, FIELD_MAP_CHECKBOX, FIELD_MAP_COLOR_PICKER, FIELD_MAP_CSS_BORDER, FIELD_MAP_CSS_DIMENSION, FIELD_MAP_CSS_GRADIENT, FIELD_MAP_DATA_TABLE, FIELD_MAP_DUAL_LIST_SELECT, FIELD_MAP_ICON_PICKER, FIELD_MAP_PRESET_CHOICE, FIELD_MAP_RADIO, FIELD_MAP_SLIDER, FIELD_MAP_THEME_SCALE, FIELD_MAP_TOGGLE_BUTTON, simpleComponentMapper, useFormApi } from "@aglyn/shared-ui-jsx-forms";
29
+ import { useHostThemeDocument } from "@aglyn/shared-ui-theme";
30
+ import { getMdiIconPath, iconPathPropName, mdiContentSave } from "@aglyn/shared-data-mdi";
31
+ import { HelpTip, MdiIcon } from "@aglyn/shared-ui-jsx";
32
+ import { Alert, NoSsr, Tooltip } from "@mui/material";
33
+ import Box from "@mui/material/Box";
34
+ import Button from "@mui/material/Button";
35
+ import FormControl from "@mui/material/FormControl";
36
+ import { Grid } from "@mui/material";
37
+ import { observer } from "mobx-react-lite";
38
+ import * as Besigner from "@aglyn/besigner";
39
+ import { forwardRef, memo, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
40
+ import { BindingPickerContext } from "../contexts/binding-picker-context.js";
41
+ import { InsertTokenMenu } from "./insert-token-menu.component.js";
42
+ import { TokenTextField, TOKEN_TEXT_FIELD_COMPONENT } from "./token-text-field.component.js";
43
+ import { MarkdownAttributeField, MARKDOWN_ATTRIBUTE_FIELD_COMPONENT } from "./markdown-attribute-field.component.js";
44
+ import { ScreenLinkField, ScreenTargetField, SCREEN_LINK_FIELD_COMPONENT, SCREEN_TARGET_FIELD_COMPONENT } from "./screen-link-field.component.js";
45
+ import { PropertyBindingField, PROPERTY_BINDING_FIELD_COMPONENT, withPropertyBinding } from "./property-binding-field.component.js";
46
+ import { readAttributeFieldValue } from "../utils/attribute-field-value.js";
47
+ import { besignerDocsUrl } from "../utils/docs-help.js";
48
+ import { buildStyleThemeScales } from "../utils/theme-scale-options.js";
49
+ import { numericTextValue } from "../utils/numeric-text-value.js";
50
+ import ElementInfoDetails from "./element-info-details.component.js";
51
+ import useInsertTokenOptions from "../hooks/use-insert-token-options.js";
52
+ import useRepeatSources from "../hooks/use-repeat-sources.js";
53
+ import { InteractionsContext, nodeElementSelector } from "../contexts/interactions-context.js";
54
+ import { MediaPickerContext } from "../contexts/media-picker-context.js";
55
+ import { ComponentPromotionContext } from "../contexts/component-promotion-context.js";
56
+ import { InstanceAttrOverrides } from "./instance-attr-overrides.component.js";
57
+ import useDeleteElementCallback from "../hooks/use-delete-element-callback.js";
58
+ // The AGL-567 debounce now lives in `../hooks/use-debounced-commit` so the
59
+ // draft snapshotter can share it (AGL-1256) without importing this form and
60
+ // its whole data-driven-forms dependency tree. Re-exported here because this
61
+ // is where every existing caller and test imports it from.
62
+ export { ATTRIBUTE_COMMIT_DEBOUNCE_MS, useDebouncedCommit } from "../hooks/use-debounced-commit.js";
63
+ export { readAttributeFieldValue } from "../utils/attribute-field-value.js";
64
+ import { useDebouncedCommit } from "../hooks/use-debounced-commit.js";
65
+ import { useNodeWithMediaAssetFacts } from "../hooks/use-media-asset-facts-overlay.js";
66
+ // Subscribes to form value changes via FormSpy and schedules a debounced
67
+ // commit when dirty. The spy is needed because MUI Select uses a Portal, so
68
+ // its onChange never bubbles through the <form> element as a DOM event.
69
+ const AutoSaveOnChange = /*#__PURE__*/ memo(function AutoSaveOnChange({ values, pristine, valid, onSchedule }) {
70
+ const isFirstRender = useRef(true);
71
+ useEffect(()=>{
72
+ if (isFirstRender.current) {
73
+ isFirstRender.current = false;
74
+ return;
75
+ }
76
+ if (!pristine && valid) {
77
+ onSchedule();
78
+ }
79
+ }, [
80
+ values
81
+ ]); // eslint-disable-line react-hooks/exhaustive-deps
82
+ return null;
83
+ });
84
+ /**
85
+ * @TODO ⚠️ remove and reimplement following PR merge
86
+ * https://github.com/data-driven-forms/react-forms/pull/1218
87
+ */ /**
88
+ * Whether a node's text is FORMATTED — it carries a `html` prop the renderer
89
+ * draws in preference to `children` (AGL-2486).
90
+ *
91
+ * An instance is excluded: its text rides `propValues`, not a `html` prop of
92
+ * its own, so the question does not arise there.
93
+ */ export function isFormattedText(node) {
94
+ var _node_props;
95
+ if (!node) return false;
96
+ if (node.componentId === REUSABLE_INSTANCE_COMPONENT_ID) return false;
97
+ const html = (_node_props = node.props) == null ? void 0 : _node_props.html;
98
+ return typeof html === 'string' && html.length > 0;
99
+ }
100
+ /**
101
+ * The same props with the formatting dropped and the words kept
102
+ * (AGL-2486).
103
+ *
104
+ * `children` is deliberately untouched. It already holds the plain reading
105
+ * of the markup — and since `richTextToPlain` keeps line breaks, that
106
+ * reading has the author's breaks in it — so the element goes on saying the
107
+ * same thing in the same shape, in a form this panel can edit.
108
+ */ export function withoutFormatting(props) {
109
+ const next = _extends({}, props != null ? props : {});
110
+ delete next['html'];
111
+ return next;
112
+ }
113
+ export const ElementPropsFormTemplate = /*#__PURE__*/ forwardRef((props, ref)=>{
114
+ const { formFields, schema } = props, rest = _object_without_properties_loose(props, [
115
+ "formFields",
116
+ "schema"
117
+ ]);
118
+ const { handleSubmit } = useFormApi();
119
+ const { schedule, flush } = useDebouncedCommit(handleSubmit);
120
+ return /*#__PURE__*/ _jsxs("form", _extends({
121
+ ref: ref,
122
+ onSubmit: handleSubmit,
123
+ noValidate: true
124
+ }, rest, {
125
+ // Focus leaving any field (clicking another node, tabbing away, pressing
126
+ // Save) forces the pending debounced commit out immediately, so an edit
127
+ // is never stranded in the debounce window while switching selection
128
+ // (AGL-567). Placed after {...rest} so this handler always wins.
129
+ onBlur: flush,
130
+ children: [
131
+ schema.title,
132
+ /*#__PURE__*/ _jsx(Grid, {
133
+ spacing: 2,
134
+ container: true,
135
+ children: formFields
136
+ }),
137
+ /*#__PURE__*/ _jsx(FormSpy, {
138
+ subscription: {
139
+ values: true,
140
+ pristine: true,
141
+ valid: true
142
+ },
143
+ children: ({ values, pristine, valid })=>/*#__PURE__*/ _jsx(AutoSaveOnChange, {
144
+ values: values,
145
+ pristine: pristine,
146
+ valid: valid,
147
+ onSchedule: schedule
148
+ })
149
+ }),
150
+ /*#__PURE__*/ _jsx(FormSpy, {
151
+ children: ({ submitting, pristine, valid })=>/*#__PURE__*/ _jsx(Box, {
152
+ sx: {
153
+ mt: 2
154
+ },
155
+ children: /*#__PURE__*/ _jsx(FormControl, {
156
+ margin: "normal",
157
+ fullWidth: true,
158
+ children: /*#__PURE__*/ _jsx(Button, {
159
+ color: "primary",
160
+ disabled: submitting || !valid || pristine,
161
+ startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
162
+ path: mdiContentSave.path
163
+ }),
164
+ style: {
165
+ marginRight: 8
166
+ },
167
+ type: "submit",
168
+ variant: "contained",
169
+ fullWidth: true,
170
+ children: "Save Element"
171
+ })
172
+ })
173
+ })
174
+ })
175
+ ]
176
+ }));
177
+ });
178
+ ElementPropsFormTemplate.displayName = 'ElementPropsFormTemplate';
179
+ ElementPropsFormTemplate.aglyn = true;
180
+ // Attribute editors available to canvas component schemas: the simple set
181
+ // plus pickers components actually declare (icon picker AGL-146, checkbox
182
+ // AGL-162, color picker AGL-584 — the email blocks declare 5 color
183
+ // attributes and an unregistered type makes the form renderer throw,
184
+ // which blanked the email designer's whole attributes panel).
185
+ export const elementPropsComponentMapper = _extends({}, simpleComponentMapper, {
186
+ [FieldComponentType.ICON_PICKER]: FIELD_MAP_ICON_PICKER,
187
+ [FieldComponentType.CHECKBOX]: FIELD_MAP_CHECKBOX,
188
+ [FieldComponentType.COLOR_PICKER]: FIELD_MAP_COLOR_PICKER,
189
+ // Number + unit editor for CSS length attributes (AGL-1219): width,
190
+ // height and friends were free text, so the author had to type the unit
191
+ // and a bare `320` silently did nothing.
192
+ [FieldComponentType.CSS_DIMENSION]: FIELD_MAP_CSS_DIMENSION,
193
+ // Background fill editor for gradient-capable attributes (AGL-1331);
194
+ // the Styles panel reaches it through the shared componentMapper.
195
+ [FieldComponentType.CSS_GRADIENT]: FIELD_MAP_CSS_GRADIENT,
196
+ [FieldComponentType.DATA_TABLE]: FIELD_MAP_DATA_TABLE,
197
+ // Per-breakpoint span/offset row for Grid cells (AGL-2486). Registered
198
+ // here or the attributes memo's unknown-editor filter drops Span and
199
+ // Offset from the panel entirely rather than throwing (AGL-584).
200
+ [FieldComponentType.BREAKPOINT_SPAN]: FIELD_MAP_BREAKPOINT_SPAN,
201
+ // The rest of the attribute schema's value-holding field kinds (AGL-2893).
202
+ // A coded component could always declare them and the panel silently
203
+ // dropped them; a component or layout property of the same kind is edited
204
+ // with the same control, so each is registered here once, for both.
205
+ [FieldComponentType.CSS_BORDER]: FIELD_MAP_CSS_BORDER,
206
+ [FieldComponentType.PRESET_CHOICE]: FIELD_MAP_PRESET_CHOICE,
207
+ [FieldComponentType.THEME_SCALE]: FIELD_MAP_THEME_SCALE,
208
+ [FieldComponentType.RADIO]: FIELD_MAP_RADIO,
209
+ [FieldComponentType.SLIDER]: FIELD_MAP_SLIDER,
210
+ [FieldComponentType.TOGGLE_BUTTON]: FIELD_MAP_TOGGLE_BUTTON,
211
+ [FieldComponentType.DUAL_LIST_SELECT]: FIELD_MAP_DUAL_LIST_SELECT,
212
+ // Pill-rendering editor for token-capable free-text attributes
213
+ // (AGL-586); the attributes memo rewrites TEXT_FIELD/TEXTAREA to it.
214
+ [TOKEN_TEXT_FIELD_COMPONENT]: TokenTextField,
215
+ // Screen picker + external-URL escape hatch for `Link`-typed component
216
+ // props (AGL-1335), which were plain text boxes storing a raw path.
217
+ [SCREEN_LINK_FIELD_COMPONENT]: ScreenLinkField,
218
+ // The attributes' own Screen pickers (`SCREEN_SELECT`), searchable and
219
+ // offering entries (AGL-3119); `resolveAttributeField` rewrites to it.
220
+ [SCREEN_TARGET_FIELD_COMPONENT]: ScreenTargetField,
221
+ // The markdown-lite WYSIWYG for document-valued attributes (AGL-1616),
222
+ // registered under BOTH the schema-declared type and the internal key so a
223
+ // schema can ask for it directly and the memo below can rewrite to it.
224
+ [FieldComponentType.MARKDOWN]: MarkdownAttributeField,
225
+ [MARKDOWN_ATTRIBUTE_FIELD_COMPONENT]: MarkdownAttributeField,
226
+ // A placed plugin's declared settings as real fields (AGL-1049); the
227
+ // attributes memo rewrites the plugin's JSON attribute to it.
228
+ [PLUGIN_SETTINGS_FIELD_COMPONENT]: PluginSettingsField,
229
+ // A field with no text box inside a component editor, wrapped so it can be
230
+ // bound to one of the component's properties; the attributes memo wraps
231
+ // them (see `withPropertyBinding`).
232
+ [PROPERTY_BINDING_FIELD_COMPONENT]: PropertyBindingField
233
+ });
234
+ /**
235
+ * A number-typed field persists a NUMBER, not the text that was typed.
236
+ *
237
+ * Every control in this panel can only hand back a string, and for a prop
238
+ * the renderer reads as a number that changes what the value DOES: MUI
239
+ * renders `size: 24` as 24px and passes the string `'24'` through verbatim,
240
+ * where it is not CSS and the declaration is dropped — the icon silently
241
+ * goes back to its default size. Clearing the box to retype the same number
242
+ * is enough to do it.
243
+ *
244
+ * Gated on the field's declared `type: 'number'` rather than on the value's
245
+ * shape, which is how the styles panel decides ({@link numericTextValue}):
246
+ * every value there is CSS, while an attribute holds arbitrary authored
247
+ * content, and a Text element reading `2026` is text that must stay text.
248
+ *
249
+ * Implemented as final-form's `parse` rather than data-driven-forms'
250
+ * `dataType`, which converts the same way but ALSO attaches a pattern
251
+ * validator. These fields are token-capable, so a `{{var:id}}` binding —
252
+ * legitimate in a numeric prop — would make the form invalid, and this
253
+ * panel's autosave only commits while it is valid: every attribute on the
254
+ * element would stop saving, with nothing on screen to say so.
255
+ */ export function withNumericValueParse(field) {
256
+ var _field_FieldProps;
257
+ if (field['type'] !== 'number') return field;
258
+ return _extends({}, field, {
259
+ FieldProps: _extends({}, (_field_FieldProps = field['FieldProps']) != null ? _field_FieldProps : {}, {
260
+ parse: numericTextValue
261
+ })
262
+ });
263
+ }
264
+ /**
265
+ * The field, with its `resolveProps` handed the element it edits as a fourth
266
+ * argument (see {@link Aglyn.AttributeFieldContext}).
267
+ *
268
+ * data-driven-forms calls `resolveProps` with the field's value and the form's
269
+ * values, which are the element's own props. A warning about the element as a
270
+ * whole also needs what is inside it — a Link Container holding only an icon
271
+ * renders an unnamed link (AGL-2886) — and nothing else tells a field which
272
+ * element on the canvas it belongs to.
273
+ *
274
+ * The context is read when the field renders, not when this runs, so the
275
+ * lookup answers from the live canvas rather than from a copy taken when the
276
+ * element was selected. A field that declares no `resolveProps` is returned
277
+ * untouched.
278
+ */ export function withAttributeFieldContext(field, context) {
279
+ const resolveProps = field['resolveProps'];
280
+ if (!context || typeof resolveProps !== 'function') return field;
281
+ return _extends({}, field, {
282
+ resolveProps: (...args)=>resolveProps(args[0], args[1], args[2], context)
283
+ });
284
+ }
285
+ /**
286
+ * The switch's position as the page stores it: a real boolean, read with the
287
+ * spellings every Yes / no reader accepts, so a value stored as the text
288
+ * `'false'` or `'off'` shows off rather than on.
289
+ *
290
+ * Unset is not a position a switch has, and it is the one a page needs to hand
291
+ * the choice back to the default — so the field's clear button is the way
292
+ * there, and it removes the value rather than storing either answer.
293
+ */ export function parseYesNoPropValue(value) {
294
+ if (value === undefined || value === null || value === '') return undefined;
295
+ return readYesNoValue(value);
296
+ }
297
+ /**
298
+ * The path each icon picker's companion prop should hold once these values
299
+ * are saved: `iconId` → `iconPath`, `startIconId` → `startIconPath`
300
+ * (AGL-1212).
301
+ *
302
+ * The id stays the source of truth and the path travels with the document,
303
+ * because the catalog is ~2.9 MB, only picker surfaces load it, and a
304
+ * published page has to draw the icon without it.
305
+ *
306
+ * The lookup runs on every save of the panel, and the panel saves edits to
307
+ * every other attribute too — from the moment it opens, before the picker has
308
+ * finished loading the catalog (AGL-2879). So a miss is not evidence the icon
309
+ * has no path: for an id that did NOT change it keeps the path already
310
+ * stored. For a changed id the lookup decides, and a miss clears the path,
311
+ * because the old icon's path is never the new icon's.
312
+ */ export function iconPathsForSave(attributes, values, stored) {
313
+ const paths = {};
314
+ for (const attribute of attributes != null ? attributes : []){
315
+ var _getMdiIconPath;
316
+ if ((attribute == null ? void 0 : attribute.component) !== FieldComponentType.ICON_PICKER) continue;
317
+ const pathProp = iconPathPropName(attribute.name);
318
+ if (pathProp === attribute.name) continue;
319
+ const pickedId = values[attribute.name];
320
+ if (typeof pickedId !== 'string' || !pickedId) {
321
+ paths[pathProp] = undefined;
322
+ continue;
323
+ }
324
+ const unchanged = pickedId === (stored == null ? void 0 : stored[attribute.name]);
325
+ const storedPath = stored == null ? void 0 : stored[pathProp];
326
+ paths[pathProp] = (_getMdiIconPath = getMdiIconPath(pickedId)) != null ? _getMdiIconPath : unchanged && typeof storedPath === 'string' ? storedPath : undefined;
327
+ }
328
+ return paths;
329
+ }
330
+ /**
331
+ * An icon property's stored pick as the icon picker shows it: the icon's id,
332
+ * or `''` for nothing picked.
333
+ */ export function formatIconPropValue(value) {
334
+ var _ref;
335
+ var _readInstanceIconValue;
336
+ return (_ref = (_readInstanceIconValue = readInstanceIconValue(value)) == null ? void 0 : _readInstanceIconValue.iconId) != null ? _ref : '';
337
+ }
338
+ /**
339
+ * The icon picker's choice as the instance stores it: the id AND its SVG path,
340
+ * or unset.
341
+ *
342
+ * The path is looked up here, at the moment of the pick, for the reason
343
+ * `handleElementSave` stores `iconPath` beside an Icon element's `iconId`
344
+ * (AGL-1212): a published page never loads the icon catalog, so an id alone
345
+ * draws the empty Icon placeholder. This is the one moment the catalog is
346
+ * certainly loaded — the picker needed it to offer the icon — and an
347
+ * untouched field is never parsed again, so no later edit can look the id up
348
+ * against a catalog that has not arrived.
349
+ */ export function parseIconPropValue(value) {
350
+ if (typeof value !== 'string' || !value) return undefined;
351
+ const iconPath = getMdiIconPath(value);
352
+ return iconPath ? {
353
+ iconId: value,
354
+ iconPath
355
+ } : {
356
+ iconId: value
357
+ };
358
+ }
359
+ /** The attribute controls that draw the reset-to-unset corner button. */ const CLEARABLE_FIELD_KINDS = new Set([
360
+ FieldComponentType.CHECKBOX,
361
+ FieldComponentType.COLOR_PICKER,
362
+ FieldComponentType.CSS_BORDER,
363
+ FieldComponentType.CSS_DIMENSION,
364
+ FieldComponentType.CSS_GRADIENT,
365
+ FieldComponentType.ICON_PICKER,
366
+ FieldComponentType.PRESET_CHOICE,
367
+ FieldComponentType.RADIO,
368
+ FieldComponentType.SELECT,
369
+ FieldComponentType.SLIDER,
370
+ FieldComponentType.SWITCH,
371
+ FieldComponentType.THEME_SCALE,
372
+ FieldComponentType.TOGGLE_BUTTON,
373
+ // Drawn as dropdowns once resolved.
374
+ FieldComponentType.CATEGORY_SELECT,
375
+ FieldComponentType.COLLECTION_SELECT,
376
+ FieldComponentType.DATASET_FIELD_SELECT,
377
+ FieldComponentType.DATASET_SELECT,
378
+ FieldComponentType.FORM_SELECT,
379
+ FieldComponentType.NODE_SELECT,
380
+ FieldComponentType.PLUGIN_SELECT,
381
+ FieldComponentType.PRODUCT_SELECT
382
+ ]);
383
+ /** A property's answers as a field's options: by label, falling back to the value. */ function propertyAnswers(prop) {
384
+ var _prop_options;
385
+ return ((_prop_options = prop.options) != null ? _prop_options : []).filter((option)=>option == null ? void 0 : option.value).map((option)=>({
386
+ value: option.value,
387
+ label: option.label || option.value
388
+ }));
389
+ }
390
+ /**
391
+ * The line under a page's field naming the default an empty field falls back
392
+ * to, or `undefined` where there is none to name or the control names it
393
+ * itself.
394
+ */ function describePropertyDefault(prop, answers) {
395
+ const fallback = reusablePropDefaultValue(prop);
396
+ if (fallback === undefined) return undefined;
397
+ if (answers.length) {
398
+ const chosen = (Array.isArray(fallback) ? fallback : [
399
+ fallback
400
+ ]).map((value)=>{
401
+ var _answers_find;
402
+ return (_answers_find = answers.find((answer)=>answer.value === value)) == null ? void 0 : _answers_find.label;
403
+ }).filter(Boolean);
404
+ return chosen.length ? `Defaults to ${chosen.join(', ')}` : undefined;
405
+ }
406
+ const text = Array.isArray(fallback) ? fallback.join(', ') : String(fallback);
407
+ return text ? `Defaults to "${text}"` : undefined;
408
+ }
409
+ /**
410
+ * A property's condition as the form renderer evaluates one (AGL-2893): each
411
+ * rule reads the named property's field, and compares what that property is
412
+ * worth on the page — its own value, else its default — the way the published
413
+ * page compares it, so the field shows exactly where the value applies.
414
+ *
415
+ * The operators are data-driven-forms' own; each rule becomes a function `is`
416
+ * only so an unset field can be read as its default first.
417
+ */ export function propertyFieldCondition(condition, declared, valuePath) {
418
+ if (condition == null) return undefined;
419
+ const byName = new Map(declared.map((prop)=>[
420
+ prop.name,
421
+ prop
422
+ ]));
423
+ const convert = (entry)=>{
424
+ if (Array.isArray(entry)) return entry.map(convert);
425
+ if (!entry || typeof entry !== 'object') return entry;
426
+ if ('and' in entry) {
427
+ const and = entry.and;
428
+ return {
429
+ and: (Array.isArray(and) ? and : [
430
+ and
431
+ ]).map(convert)
432
+ };
433
+ }
434
+ if ('or' in entry) {
435
+ const or = entry.or;
436
+ return {
437
+ or: (Array.isArray(or) ? or : [
438
+ or
439
+ ]).map(convert)
440
+ };
441
+ }
442
+ if ('not' in entry) return {
443
+ not: convert(entry.not)
444
+ };
445
+ const { when } = entry, rule = _object_without_properties_loose(entry, [
446
+ "when"
447
+ ]);
448
+ const target = byName.get(when);
449
+ return {
450
+ when: valuePath(when),
451
+ is: (value)=>{
452
+ const own = readReusablePropValue(value, target);
453
+ return evaluateReusablePropRule(rule, reusablePropConditionValue(target, own === undefined ? reusablePropDefaultValue(target) : own));
454
+ }
455
+ };
456
+ };
457
+ return convert(condition);
458
+ }
459
+ /**
460
+ * One component or layout property as an Attributes field (AGL-1247,
461
+ * AGL-2893): the attribute schema field its kind names — a switch for a
462
+ * Yes / no, the icon picker for an Icon, the color picker for a Color — with
463
+ * the kind's options and settings, and the property's condition.
464
+ *
465
+ * The result is an attribute field like any coded component's, so it is
466
+ * finished by {@link resolveAttributeField} and drawn by
467
+ * `elementPropsComponentMapper`: the Properties dialog's Default, an
468
+ * instance's Attributes and a screen's layout values all edit a property with
469
+ * the control a coded component's attribute of that kind gets.
470
+ */ export function buildPropertyField(prop, options) {
471
+ var _prop_type, _prop_settings, _kind_fieldProps;
472
+ const { name, role, declared = [], valuePath = (propName)=>propName, noun = 'component', tokenOptions, tokenLabelContext, themeScales } = options;
473
+ const kind = reusablePropKind(prop.type);
474
+ const type = (_prop_type = prop.type) != null ? _prop_type : 'text';
475
+ const label = prop.label || prop.name;
476
+ const settings = (_prop_settings = prop.settings) != null ? _prop_settings : {};
477
+ const answers = propertyAnswers(prop);
478
+ const pageSets = role === 'value';
479
+ const defaultNote = pageSets ? describePropertyDefault(prop, answers) : undefined;
480
+ const condition = pageSets ? propertyFieldCondition(prop.condition, declared, valuePath) : undefined;
481
+ const base = _extends({
482
+ name,
483
+ label,
484
+ component: kind.field
485
+ }, (_kind_fieldProps = kind.fieldProps) != null ? _kind_fieldProps : {}, prop.description ? {
486
+ help: {
487
+ title: label,
488
+ excerpt: prop.description
489
+ }
490
+ } : {}, defaultNote ? {
491
+ description: defaultNote
492
+ } : {}, condition ? {
493
+ condition
494
+ } : {}, CLEARABLE_FIELD_KINDS.has(kind.field) ? {
495
+ clearable: true
496
+ } : {});
497
+ const ownersDefault = `the ${noun} default`;
498
+ switch(type){
499
+ case 'boolean':
500
+ case FieldComponentType.CHECKBOX:
501
+ {
502
+ if (reusablePropTakesSeveral(prop)) {
503
+ return _extends({}, base, {
504
+ clearable: undefined,
505
+ options: answers
506
+ });
507
+ }
508
+ // Yes, No, or the owner's default — three answers, and a switch shows
509
+ // two. So an empty field shows the position of the default it renders
510
+ // with, says that is where the position comes from, and the corner ✕
511
+ // is the way back to it once the page has chosen.
512
+ const fallback = pageSets ? prop.defaultValue : undefined;
513
+ const fallbackAnswer = readYesNoValue(fallback) === true ? 'Yes' : 'No';
514
+ return _extends({}, base, {
515
+ description: undefined,
516
+ // The default's position while the page has set nothing, since that
517
+ // is what the page renders.
518
+ unsetChecked: readYesNoValue(fallback) === true,
519
+ // Read with the spellings every Yes / no reader accepts, so a value
520
+ // stored as the text `'false'` shows off rather than on.
521
+ FieldProps: {
522
+ parse: parseYesNoPropValue
523
+ }
524
+ }, pageSets ? {
525
+ resolveProps: (_props, _field, formOptions)=>{
526
+ var _formOptions_getFieldState, _formOptions_getFieldState1;
527
+ const stored = formOptions == null ? void 0 : (_formOptions_getFieldState1 = formOptions.getFieldState) == null ? void 0 : (_formOptions_getFieldState = _formOptions_getFieldState1.call(formOptions, name)) == null ? void 0 : _formOptions_getFieldState.value;
528
+ return stored === undefined || stored === null || stored === '' ? {
529
+ description: `Uses ${ownersDefault} (${fallbackAnswer})`
530
+ } : {};
531
+ }
532
+ } : {});
533
+ }
534
+ case 'choice':
535
+ {
536
+ const several = reusablePropTakesSeveral(prop);
537
+ const fallback = reusablePropDefaultValue(prop);
538
+ const fallbackLabels = (Array.isArray(fallback) ? fallback : [
539
+ fallback
540
+ ]).map((value)=>{
541
+ var _answers_find;
542
+ return (_answers_find = answers.find((answer)=>answer.value === value)) == null ? void 0 : _answers_find.label;
543
+ }).filter(Boolean);
544
+ return _extends({}, base, {
545
+ options: answers
546
+ }, several ? {
547
+ isMulti: true
548
+ } : {}, {
549
+ description: undefined,
550
+ placeholder: pageSets ? fallbackLabels.length ? `Use ${ownersDefault} (${fallbackLabels.join(', ')})` : 'Not set' : 'No default'
551
+ });
552
+ }
553
+ case FieldComponentType.RADIO:
554
+ case FieldComponentType.TOGGLE_BUTTON:
555
+ case FieldComponentType.DUAL_LIST_SELECT:
556
+ return _extends({}, base, {
557
+ options: answers
558
+ });
559
+ case 'icon':
560
+ // An icon has no placeholder to show, so what unset means is said in
561
+ // the help tip, and the corner ✕ is the way back to it.
562
+ return _extends({}, base, {
563
+ description: undefined
564
+ }, pageSets ? {
565
+ help: {
566
+ title: label,
567
+ excerpt: [
568
+ prop.description,
569
+ prop.defaultValue ? `Leave it unset to show the ${noun}'s default icon.` : 'Leave it unset to show no icon.'
570
+ ].filter(Boolean).join(' ')
571
+ }
572
+ } : {}, {
573
+ FieldProps: {
574
+ format: formatIconPropValue,
575
+ parse: parseIconPropValue
576
+ }
577
+ });
578
+ case 'href':
579
+ // The screen picker a Button's own "Link to screen" field offers, with
580
+ // the external-address escape hatch built in (AGL-1335). The default
581
+ // travels under its own key: `defaultValue` would be read by the form
582
+ // renderer as an initial value and saved onto every instance.
583
+ return _extends({}, base, {
584
+ component: SCREEN_LINK_FIELD_COMPONENT,
585
+ description: undefined
586
+ }, pageSets ? {
587
+ propDefault: prop.defaultValue,
588
+ propDefaultOwner: noun
589
+ } : {
590
+ emptyLabel: 'No default'
591
+ });
592
+ case 'number':
593
+ return _extends({}, base, pageSets && prop.defaultValue !== undefined ? {
594
+ placeholder: String(prop.defaultValue)
595
+ } : {});
596
+ case FieldComponentType.SLIDER:
597
+ return _extends({}, base, [
598
+ 'min',
599
+ 'max',
600
+ 'step'
601
+ ].reduce((range, key)=>{
602
+ const numeric = Number(settings[key]);
603
+ if (settings[key] !== undefined && Number.isFinite(numeric)) {
604
+ range[key] = numeric;
605
+ }
606
+ return range;
607
+ }, {}));
608
+ case FieldComponentType.THEME_SCALE:
609
+ var _ref, _settings_scale;
610
+ return _extends({}, base, {
611
+ scaleOptions: (_ref = themeScales == null ? void 0 : themeScales[String((_settings_scale = settings['scale']) != null ? _settings_scale : '')]) != null ? _ref : []
612
+ });
613
+ case FieldComponentType.PRESET_CHOICE:
614
+ {
615
+ var _settings_presets, _ref1;
616
+ const presets = String((_settings_presets = settings['presets']) != null ? _settings_presets : '');
617
+ return _extends({}, base, {
618
+ choices: (_ref1 = themeScales == null ? void 0 : themeScales[presets]) != null ? _ref1 : []
619
+ }, presets === 'shadow' || presets === 'fontFamily' ? {
620
+ previewKind: presets === 'shadow' ? 'shadow' : 'font'
621
+ } : {});
622
+ }
623
+ case FieldComponentType.DATASET_FIELD_SELECT:
624
+ return _extends({}, base, settings['datasetId'] ? {
625
+ datasetId: String(settings['datasetId'])
626
+ } : {});
627
+ case FieldComponentType.PLUGIN_SETTINGS:
628
+ {
629
+ var _settings_pluginProperty;
630
+ const pluginProperty = String((_settings_pluginProperty = settings['pluginProperty']) != null ? _settings_pluginProperty : '');
631
+ const plugin = declared.find((entry)=>entry.name === pluginProperty);
632
+ const pluginDefault = reusablePropDefaultValue(plugin);
633
+ return _extends({}, base, pluginProperty ? {
634
+ listingField: pageSets ? valuePath(pluginProperty) : undefined,
635
+ listingFallback: typeof pluginDefault === 'string' ? pluginDefault : undefined
636
+ } : {});
637
+ }
638
+ case 'text':
639
+ case 'richText':
640
+ case 'image':
641
+ // Token-capable, so an override can itself carry a `{{var:id}}`: the
642
+ // graft substitutes first and the binding resolver runs after it, so
643
+ // those still resolve.
644
+ return _extends({}, base, pageSets && prop.defaultValue !== undefined ? {
645
+ placeholder: String(prop.defaultValue)
646
+ } : {}, {
647
+ tokenOptions,
648
+ tokenLabelContext
649
+ });
650
+ default:
651
+ return base;
652
+ }
653
+ }
654
+ /**
655
+ * One Attributes field per prop a reusable component declares (AGL-1247),
656
+ * so the same hero can carry different copy on eleven pages instead of
657
+ * being copied onto each — each drawn by {@link buildPropertyField} and
658
+ * finished by {@link resolveAttributeField}, like the component's own
659
+ * attributes.
660
+ *
661
+ * Fields are named for the `propValues.<name>` path the graft reads, so the
662
+ * form round-trips them through final-form's nested-path handling with no
663
+ * flatten/unflatten step. A name that is not an identifier is skipped
664
+ * rather than rendered as a field that could not save.
665
+ */ export function buildInstancePropFields(declared, tokenOptions, tokenLabelContext, context) {
666
+ return buildPropertyValueFields(declared, {
667
+ pathOf: (name)=>`${REUSABLE_INSTANCE_PROP_VALUES_KEY}.${name}`,
668
+ noun: 'component',
669
+ context: _extends({}, context, {
670
+ insertOptions: tokenOptions,
671
+ tokenLabelContext
672
+ }),
673
+ themeScales: context == null ? void 0 : context.themeScales
674
+ });
675
+ }
676
+ /**
677
+ * One field per declared property for a page setting their values, at the
678
+ * paths `pathOf` names — an instance's `propValues`, or a screen's values for
679
+ * its layout.
680
+ */ export function buildPropertyValueFields(declared, options) {
681
+ if (!(declared == null ? void 0 : declared.length)) return [];
682
+ const valid = declared.filter((prop)=>{
683
+ var _ref;
684
+ return COMPONENT_PROP_NAME_PATTERN.test((_ref = prop == null ? void 0 : prop.name) != null ? _ref : '');
685
+ });
686
+ return valid.map((prop)=>{
687
+ var _options_context, _options_context1;
688
+ return resolveAttributeField(buildPropertyField(prop, {
689
+ name: options.pathOf(prop.name),
690
+ role: 'value',
691
+ declared: valid,
692
+ valuePath: options.pathOf,
693
+ noun: options.noun,
694
+ tokenOptions: (_options_context = options.context) == null ? void 0 : _options_context.insertOptions,
695
+ tokenLabelContext: (_options_context1 = options.context) == null ? void 0 : _options_context1.tokenLabelContext,
696
+ themeScales: options.themeScales
697
+ }), options.context);
698
+ });
699
+ }
700
+ /**
701
+ * An attribute field as the panel draws it: a declared kind that needs the
702
+ * site's data resolved into a dropdown the form renderer knows, a free-text
703
+ * field upgraded to the token editor, and every other field as declared.
704
+ *
705
+ * Pure, and the one place this happens — the element's own attributes, a
706
+ * component instance's properties and a screen's layout values all pass
707
+ * through it, so a kind cannot draw one way as an attribute and another as a
708
+ * property.
709
+ */ export function resolveAttributeField(field, context = {}) {
710
+ const { screens, labels, values, entityOptions = {}, nodeOptions = [], ancestorDatasetId, hasFormattedText, insertOptions, tokenLabelContext } = context;
711
+ const stored = readAttributeFieldValue(values, field.name);
712
+ if (field.component === FieldComponentType.SCREEN_SELECT) {
713
+ // The host's screens by path, then its collection listings
714
+ // (AGL-2799) and feeds — built by the function the `Link`-typed prop
715
+ // picker uses too, so neither can offer a target the other does not.
716
+ const options = [
717
+ {
718
+ value: '',
719
+ label: 'None (use external URL)'
720
+ },
721
+ ...screenLinkTargetOptions(screens, labels, 'path').map(({ value, label })=>({
722
+ value,
723
+ label
724
+ }))
725
+ ];
726
+ // A stored target the host no longer has renders as a BLANK
727
+ // picker, which reads as "no link set" while the element still
728
+ // behaves as linked (AGL-1893). Naming it is the only way the
729
+ // author finds out before publishing rather than after. An entry is
730
+ // not stranded for being absent here — the map holds no entries, and
731
+ // the lookup names one by reading it (AGL-3119).
732
+ const stranded = unresolvedScreenOption(stored, screens);
733
+ if (stranded && linkTargetKind(stranded.value) !== 'entry' && !options.some((o)=>o.value === stranded.value)) {
734
+ options.push(stranded);
735
+ }
736
+ // Drawn by the searchable lookup (AGL-3119), which offers these and
737
+ // finds entries as the author types.
738
+ return _extends({}, field, {
739
+ component: SCREEN_TARGET_FIELD_COMPONENT,
740
+ options
741
+ });
742
+ }
743
+ if (field.component === FieldComponentType.PLUGIN_SETTINGS) {
744
+ // Rendered by a bespoke editor rather than expanded into N fields
745
+ // here: the settings live in ONE stored JSON attribute, and the
746
+ // editor reads the sibling plugin selection to know which manifest's
747
+ // props to offer.
748
+ return _extends({}, field, {
749
+ component: PLUGIN_SETTINGS_FIELD_COMPONENT
750
+ });
751
+ }
752
+ if (field.component === FieldComponentType.PLUGIN_SELECT) {
753
+ // Installed plugins (AGL-1030), from the set the console publishes
754
+ // for the drawer — host and org pins both, host winning where it
755
+ // shadows. No extra read: the pins already carry the display name.
756
+ const installs = getKnownPluginInstalls();
757
+ return _extends({}, field, {
758
+ component: FieldComponentType.SELECT,
759
+ options: [
760
+ {
761
+ value: '',
762
+ label: installs.length ? 'None' : 'No plugins installed for this site'
763
+ },
764
+ ...installs.map((install)=>{
765
+ var _install_displayName;
766
+ return {
767
+ value: install.listingId,
768
+ label: (_install_displayName = install.displayName) != null ? _install_displayName : /* An install with no name is still choosable — better a raw
769
+ id in one option than a plugin that cannot be placed. */ install.listingId
770
+ };
771
+ })
772
+ ]
773
+ });
774
+ }
775
+ if (field.component === FieldComponentType.NODE_SELECT) {
776
+ // Canvas-element picker (AGL-557).
777
+ return _extends({}, field, {
778
+ component: FieldComponentType.SELECT,
779
+ options: [
780
+ {
781
+ value: '',
782
+ label: 'None'
783
+ },
784
+ ...nodeOptions
785
+ ]
786
+ });
787
+ }
788
+ if (field.component === FieldComponentType.DATASET_FIELD_SELECT) {
789
+ var _ref;
790
+ var _entityOptions_datasetFields;
791
+ // Model order, never alphabetized — it mirrors the schema dialog. A
792
+ // field naming its own dataset (a Dataset field property) reads that
793
+ // one; every other reads the nearest ancestor's.
794
+ const datasetId = typeof field['datasetId'] === 'string' && field['datasetId'] ? field['datasetId'] : ancestorDatasetId;
795
+ const modelFields = datasetId ? (_ref = (_entityOptions_datasetFields = entityOptions.datasetFields) == null ? void 0 : _entityOptions_datasetFields[datasetId]) != null ? _ref : [] : [];
796
+ return _extends({}, field, {
797
+ component: FieldComponentType.SELECT,
798
+ options: [
799
+ {
800
+ value: '',
801
+ label: modelFields.length ? 'None (match by field name)' : 'No dataset selected on the form'
802
+ },
803
+ ...modelFields.map((modelField)=>({
804
+ value: modelField.id,
805
+ label: modelField.label
806
+ }))
807
+ ]
808
+ });
809
+ }
810
+ // Id-based entity pickers (AGL-343/344), including the Form element's
811
+ // `formId`. Recognised by the attribute's declared type, so a surface
812
+ // whose picker context is missing renders a picker that SAYS so instead
813
+ // of one that is not there. The stored value travels with the field: a
814
+ // selection outside the browse window is offered from its own keyed read,
815
+ // so a bound element never renders as unbound.
816
+ const entityKind = ENTITY_PICKER_KINDS[field.component];
817
+ if (entityKind) {
818
+ return buildEntityPickerField(field, entityKind, entityOptions, stored);
819
+ }
820
+ if (field.component === FieldComponentType.DATE_PICKER || field.component === FieldComponentType.TIME_PICKER) {
821
+ // The browser's own date and time inputs, storing `YYYY-MM-DD` and
822
+ // `HH:mm` — the text every renderer reads (a Video's publication date is
823
+ // one). The date-picker editors hand back a date object instead, and need
824
+ // a date adapter the besigner does not load, so a field declared as one
825
+ // is drawn as the input that stores what it means.
826
+ return _extends({}, field, {
827
+ component: FieldComponentType.TEXT_FIELD,
828
+ type: field.component === FieldComponentType.DATE_PICKER ? 'date' : 'time'
829
+ });
830
+ }
831
+ // Formatted text is owned by the canvas (AGL-2486). Shown, with
832
+ // the reason, rather than silently editable into a prop the
833
+ // renderer ignores.
834
+ if (field.name === 'children' && hasFormattedText) {
835
+ return _extends({}, field, {
836
+ component: TOKEN_TEXT_FIELD_COMPONENT,
837
+ multiline: true,
838
+ isReadOnly: true,
839
+ description: 'This text is formatted — double-click the element on the ' + 'canvas to edit it. Remove formatting to edit it here.',
840
+ tokenOptions: insertOptions,
841
+ tokenLabelContext
842
+ });
843
+ }
844
+ if ((field.component === FieldComponentType.TEXT_FIELD || field.component === FieldComponentType.TEXTAREA) && !field.isReadOnly) {
845
+ var _field_tokenOptions, _field_tokenLabelContext;
846
+ // Token-capable free-text fields (children, href, src, …)
847
+ // render through the pill editor (AGL-586): stored `{{...}}`
848
+ // tokens display as named colored pills, the {x} adornment
849
+ // inserts at the caret (AGL-583), and raw {{...}} typing keeps
850
+ // working (it materializes into a pill on blur).
851
+ return _extends({}, field, {
852
+ component: TOKEN_TEXT_FIELD_COMPONENT,
853
+ multiline: field.component === FieldComponentType.TEXTAREA,
854
+ tokenOptions: (_field_tokenOptions = field['tokenOptions']) != null ? _field_tokenOptions : insertOptions,
855
+ tokenLabelContext: (_field_tokenLabelContext = field['tokenLabelContext']) != null ? _field_tokenLabelContext : tokenLabelContext
856
+ });
857
+ }
858
+ return field;
859
+ }
860
+ /**
861
+ * What a "Browse media" pick should ALSO write for `alt` (AGL-1896).
862
+ *
863
+ * The DAM has stored per-asset alt text since AGL-173 and nothing ever read
864
+ * it back, so an author placing the same logo on eight pages typed its alt
865
+ * eight times — and a field that must be retyped per placement is a field
866
+ * that ships blank, on the customer's published site.
867
+ *
868
+ * Exported and pure so the decision is testable on real inputs; the handler
869
+ * that calls it is inside a component wired to the live canvas.
870
+ *
871
+ * Two narrowings, both deliberate:
872
+ *
873
+ * * **`src` only.** The Browse button is offered on every media-ish
874
+ * attribute — `poster`, `background`, `thumbnail`, `logo`. A poster
875
+ * frame's description is not the element's alt text, and no attribute
876
+ * pairing anywhere says which of those an `alt` belongs to. Guessing would
877
+ * put the wrong sentence on the element and look like a bug.
878
+ * * **Only where the SCHEMA declares an `alt`.** Read off the element's own
879
+ * attributes rather than assumed, so a component without one is never
880
+ * handed a prop its renderer would drop on the floor.
881
+ *
882
+ * Returns `{}` — not `{ alt: undefined }` — whenever nothing should change,
883
+ * because the caller spreads the result into a props object that
884
+ * `updateNodeProps` REPLACES wholesale. An `alt` key present with an
885
+ * undefined value is indistinguishable from an authored empty alt to
886
+ * everything downstream, and would silently clear one.
887
+ */ export function inheritedAltPatch(options) {
888
+ const { propName, declaresAlt, props, assetAlt } = options != null ? options : {};
889
+ if (!declaresAlt || propName !== 'src') return {};
890
+ const alt = inheritedMediaAlt({
891
+ placementAlt: props == null ? void 0 : props['alt'],
892
+ // The author's explicit "screen readers should skip this" (AGL-1305).
893
+ // `image.tsx` forces `alt=""` over any alt text when it is on, so
894
+ // inheriting into such a node writes text the renderer discards.
895
+ decorative: props == null ? void 0 : props['decorative'],
896
+ assetAlt
897
+ });
898
+ return alt ? {
899
+ alt
900
+ } : {};
901
+ }
902
+ /**
903
+ * The one kind of file a "Browse media" pick can place in an attribute, or
904
+ * `undefined` where the picker stays unnarrowed (AGL-2953).
905
+ *
906
+ * The Video element's `src` plays a film and its `poster` is the still shown
907
+ * before anyone presses play, so each opens a picker that lists only what it
908
+ * can use and uploads nothing else. Every other media attribute is left as it
909
+ * was, because nothing on it says what the element's renderer accepts.
910
+ *
911
+ * Exported and pure for the reason {@link inheritedAltPatch} is; keyed on the
912
+ * persisted component id, which is never renamed.
913
+ */ export function browseMediaKind(options) {
914
+ const { componentId, propName } = options != null ? options : {};
915
+ if (componentId !== VIDEO_COMPONENT_ID) return undefined;
916
+ if (propName === 'src') return 'video';
917
+ if (propName === 'poster') return 'image';
918
+ return undefined;
919
+ }
920
+ /**
921
+ * The two visibility fields every node inside a component definition gets
922
+ * (AGL-1314), so "this part is optional" is something an author declares by
923
+ * clicking rather than something the component hard-codes.
924
+ *
925
+ * Token-capable, because the value that decides is almost always the
926
+ * definition's own `{{prop.*}}`: `hideIf` takes a `boolean` prop ("Hide the
927
+ * mockup"), `hideUnless` takes the prop that would have supplied the
928
+ * content ("no link, no button" — AGL-1348).
929
+ *
930
+ * Offered ONLY where `{{prop.*}}` exists — inside a component editor,
931
+ * signalled by `BindingPickerContext.componentProps` exactly as the insert
932
+ * picker uses it. On an ordinary screen the directive is inert (the graft
933
+ * is the only thing that evaluates it), and a field that silently does
934
+ * nothing is worse than no field.
935
+ */ export function buildVisibilityFields(tokenOptions, tokenLabelContext) {
936
+ const field = (name, label, description)=>({
937
+ name,
938
+ label,
939
+ description,
940
+ // Same tooltip treatment the schema attributes get (AGL-600); this
941
+ // list never reaches the `withAttributeHelp` map above.
942
+ help: {
943
+ title: label,
944
+ excerpt: description
945
+ },
946
+ component: TOKEN_TEXT_FIELD_COMPONENT,
947
+ tokenOptions,
948
+ tokenLabelContext
949
+ });
950
+ return [
951
+ field(NODE_HIDE_IF_PROP, 'Hide when', 'Remove this element (and everything inside it) from pages where ' + 'this value is on. Usually a property of this component, e.g. ' + '{{prop.hideMedia}}.'),
952
+ field(NODE_HIDE_UNLESS_PROP, 'Hide unless', 'The reverse: remove this element from pages that leave this value ' + 'empty. Point it at the property that fills the element — a button ' + 'bound to {{prop.ctaLink}} then disappears instead of shipping a ' + 'link with nowhere to go.')
953
+ ];
954
+ }
955
+ /**
956
+ * The animation fields every element gets (AGL-2486).
957
+ *
958
+ * Presets rather than a keyframes box, because the audience includes people
959
+ * who have never written CSS: an author picks "Slide up", "on scroll into
960
+ * view", and adjusts a few obvious knobs. The knobs named — duration,
961
+ * delay, easing — plus stagger, and this is that list and nothing more. There
962
+ * is deliberately no keyframe editor and no general trigger picker; the
963
+ * things that are NOT offered (the distance a slide travels, the difference
964
+ * between a transition and a keyframe run, arbitrary curves) are decided for
965
+ * the author, and an author who needs them has the sx tab.
966
+ *
967
+ * Easing is a named list, never a `cubic-bezier()` text box. That keeps the
968
+ * curve out of author free-text — the id becomes a class and the curve is
969
+ * looked up in the tenant's own stylesheet — so there is nothing here for the
970
+ * sx sanitizer to have to catch.
971
+ *
972
+ * "Stagger children" is the one control that changes WHAT animates rather
973
+ * than how: the element stops animating and its children animate in
974
+ * sequence instead. It reads as a switch rather than a separate "Stagger"
975
+ * preset because stagger is orthogonal to fade/slide/zoom — a "Stagger"
976
+ * preset would have had to answer "stagger which animation?".
977
+ *
978
+ * Offered on every element, unlike the visibility directives above, because an
979
+ * animation is meaningful on any node on any screen — there is no context in
980
+ * which the field would be inert.
981
+ *
982
+ * The "None" option carries a real value, never `''` (AGL-1451/AGL-1191): an
983
+ * empty value cannot persist, so an author who picked a preset could never
984
+ * turn it back off.
985
+ *
986
+ * The dependent fields are gated on the PRESET rather than on "not none", so
987
+ * an element that has never been animated shows one dropdown and nothing else.
988
+ * `is` takes the explicit list; a `notMatch` against `'none'` would be true for
989
+ * the undefined case too and would show all five fields on every element.
990
+ */ export function buildAnimationFields() {
991
+ const animated = {
992
+ when: NODE_ANIMATION_PROP,
993
+ is: ANIMATION_PRESETS.filter((preset)=>preset !== ANIMATION_NONE)
994
+ };
995
+ // Stagger is an entrance idea. Named rather than inlined twice so the
996
+ // switch and its step field can never drift apart and leave a step control
997
+ // visible for a trigger that ignores it.
998
+ const staggerable = {
999
+ when: NODE_ANIMATION_TRIGGER_PROP,
1000
+ is: [
1001
+ 'scroll',
1002
+ 'load'
1003
+ ]
1004
+ };
1005
+ const help = (label, description)=>({
1006
+ label,
1007
+ description,
1008
+ // Same tooltip treatment the schema attributes get (AGL-600); this list
1009
+ // never reaches the `withAttributeHelp` map above.
1010
+ help: {
1011
+ title: label,
1012
+ excerpt: description
1013
+ }
1014
+ });
1015
+ return [
1016
+ _extends({
1017
+ name: NODE_ANIMATION_PROP
1018
+ }, help('Animation', 'Adds movement to this element. Motion is skipped automatically for ' + 'visitors who have asked their device to reduce it.'), {
1019
+ component: FieldComponentType.SELECT,
1020
+ options: [
1021
+ {
1022
+ value: ANIMATION_NONE,
1023
+ label: 'None'
1024
+ },
1025
+ {
1026
+ value: 'fade',
1027
+ label: 'Fade in'
1028
+ },
1029
+ {
1030
+ value: 'slide-up',
1031
+ label: 'Slide up'
1032
+ },
1033
+ {
1034
+ value: 'slide-down',
1035
+ label: 'Slide down'
1036
+ },
1037
+ {
1038
+ value: 'slide-left',
1039
+ label: 'Slide left'
1040
+ },
1041
+ {
1042
+ value: 'slide-right',
1043
+ label: 'Slide right'
1044
+ },
1045
+ {
1046
+ value: 'zoom-in',
1047
+ label: 'Zoom in'
1048
+ },
1049
+ {
1050
+ value: 'zoom-out',
1051
+ label: 'Zoom out'
1052
+ }
1053
+ ]
1054
+ }),
1055
+ _extends({
1056
+ name: NODE_ANIMATION_TRIGGER_PROP
1057
+ }, help('Plays', 'When the animation runs. "On scroll into view" is the usual choice ' + 'for anything below the top of the page.'), {
1058
+ component: FieldComponentType.SELECT,
1059
+ condition: animated,
1060
+ initialValue: ANIMATION_DEFAULT_TRIGGER,
1061
+ options: [
1062
+ {
1063
+ value: 'scroll',
1064
+ label: 'On scroll into view'
1065
+ },
1066
+ {
1067
+ value: 'load',
1068
+ label: 'On page load'
1069
+ },
1070
+ {
1071
+ value: 'hover',
1072
+ label: 'On hover'
1073
+ }
1074
+ ]
1075
+ }),
1076
+ _extends({
1077
+ name: NODE_ANIMATION_DURATION_PROP
1078
+ }, help('Duration (ms)', `How long the animation takes, in milliseconds. ${ANIMATION_DEFAULT_DURATION_MS} is a ` + `natural default; anything over ${ANIMATION_MAX_DURATION_MS} is capped.`), {
1079
+ component: FieldComponentType.TEXT_FIELD,
1080
+ type: 'number',
1081
+ condition: animated,
1082
+ initialValue: ANIMATION_DEFAULT_DURATION_MS
1083
+ }),
1084
+ _extends({
1085
+ name: NODE_ANIMATION_DELAY_PROP
1086
+ }, help('Delay (ms)', 'How long to wait before starting. Stagger a row of cards by giving ' + 'each one a slightly larger delay.'), {
1087
+ component: FieldComponentType.TEXT_FIELD,
1088
+ type: 'number',
1089
+ condition: animated,
1090
+ initialValue: ANIMATION_DEFAULT_DELAY_MS
1091
+ }),
1092
+ _extends({
1093
+ name: NODE_ANIMATION_EASE_PROP
1094
+ }, help('Easing', 'The shape of the motion — whether it eases off as it arrives, moves ' + 'at one steady speed, or travels a little past its mark and comes ' + 'back to it.'), {
1095
+ component: FieldComponentType.SELECT,
1096
+ condition: animated,
1097
+ initialValue: ANIMATION_DEFAULT_EASE,
1098
+ options: [
1099
+ {
1100
+ value: 'smooth',
1101
+ label: 'Smooth'
1102
+ },
1103
+ {
1104
+ value: 'steady',
1105
+ label: 'Steady'
1106
+ },
1107
+ {
1108
+ value: 'gentle-start',
1109
+ label: 'Gentle start'
1110
+ },
1111
+ {
1112
+ value: 'gentle-end',
1113
+ label: 'Gentle end'
1114
+ },
1115
+ {
1116
+ value: 'gentle-both',
1117
+ label: 'Gentle start and end'
1118
+ },
1119
+ // The LABEL changed, the value did not (AGL-2486). `overshoot` is the
1120
+ // stored id: it is written into `aglynAnimationEase` on every node
1121
+ // that uses it, published as the `aglyn-anim-ease--overshoot` class,
1122
+ // and keyed in `EASE_CURVES`. Renaming the value would silently drop
1123
+ // the easing from every document already using it, which is why only
1124
+ // the human-facing string here moves.
1125
+ {
1126
+ value: 'overshoot',
1127
+ label: 'Settles into place'
1128
+ }
1129
+ ]
1130
+ }),
1131
+ _extends({
1132
+ name: NODE_ANIMATION_STAGGER_PROP
1133
+ }, help('Stagger children', 'Animate the things inside this element one after another, instead ' + 'of animating the element as a whole. Turn this on for a row of ' + 'cards or a list.'), {
1134
+ component: FieldComponentType.SWITCH,
1135
+ // Not offered for hover: a hover effect has to reverse the moment the
1136
+ // pointer leaves, and a staggered one would strand half a row.
1137
+ condition: [
1138
+ animated,
1139
+ staggerable
1140
+ ]
1141
+ }),
1142
+ _extends({
1143
+ name: NODE_ANIMATION_STAGGER_STEP_PROP
1144
+ }, help('Stagger step (ms)', `How much later each one starts than the one before it. ` + `${ANIMATION_DEFAULT_STAGGER_STEP_MS} is a natural default; ` + `anything over ${ANIMATION_MAX_STAGGER_STEP_MS} is capped, ` + `because this gap multiplies down the row.`), {
1145
+ component: FieldComponentType.TEXT_FIELD,
1146
+ type: 'number',
1147
+ // `is` accepts the string form too: a switch that has been round-tripped
1148
+ // through a text-shaped store comes back as `'true'`, and a step field
1149
+ // that silently stopped appearing would be blamed on the switch.
1150
+ condition: [
1151
+ animated,
1152
+ staggerable,
1153
+ {
1154
+ when: NODE_ANIMATION_STAGGER_PROP,
1155
+ is: [
1156
+ true,
1157
+ 'true'
1158
+ ]
1159
+ }
1160
+ ],
1161
+ initialValue: ANIMATION_DEFAULT_STAGGER_STEP_MS
1162
+ }),
1163
+ _extends({
1164
+ name: NODE_ANIMATION_REPEAT_PROP
1165
+ }, help('Replay each time', 'Play the animation again every time the element scrolls back into ' + 'view, instead of only the first time.'), {
1166
+ component: FieldComponentType.SWITCH,
1167
+ // Both conditions must hold: DDF ANDs an array of conditions. Replay is
1168
+ // meaningless for the other two triggers — a page loads once, and hover
1169
+ // is a transition that already reverses.
1170
+ condition: [
1171
+ animated,
1172
+ {
1173
+ when: NODE_ANIMATION_TRIGGER_PROP,
1174
+ is: 'scroll'
1175
+ }
1176
+ ]
1177
+ })
1178
+ ];
1179
+ }
1180
+ /**
1181
+ * The Repeat fields every element gets (AGL-3111).
1182
+ *
1183
+ * Repeating used to be one component's feature: only the MUI Stack declared
1184
+ * `repeatDataset`, so a card, an image or a placed component could not be
1185
+ * data-driven at all. It is a node capability now, offered here the way
1186
+ * visibility and animation are — and on a component instance too, which is the
1187
+ * shape most authors reach for first.
1188
+ *
1189
+ * WHAT an element repeats over is not decided here. Each registered
1190
+ * {@link RepeatSource} contributes its own key field under its own prop name,
1191
+ * and the plugin that owns the rows registers it; this list names no kind of
1192
+ * data. With nothing registered there is no Repeat section at all, which is
1193
+ * correct rather than degraded: an editor with no source has nothing to repeat
1194
+ * over.
1195
+ *
1196
+ * The bounds are shared by every source, because they are properties of the
1197
+ * REPEAT and not of the rows: the composition applies the same filter, sort
1198
+ * and limit whatever answered with the records. They are declared once, in
1199
+ * core, as {@link REPEAT_NODE_CAPABILITY} — this panel draws them, and
1200
+ * anything that has to describe what an author can build reads the same list.
1201
+ * While the list lived here it was invisible to every other reader, and a
1202
+ * reader that walked component schemas to find repeat found nothing at all.
1203
+ *
1204
+ * `hasChildren` decides whether the scope field is offered at all. An element
1205
+ * with nothing inside it has only one possible reading — it repeats itself —
1206
+ * and a control whose two options do the same thing is worse than no control.
1207
+ */ export function buildRepeatFields(options) {
1208
+ const { sources, hasChildren, resolveKeyField } = options;
1209
+ if (!sources.length) return [];
1210
+ const help = (label, description, anchor)=>({
1211
+ label,
1212
+ description,
1213
+ // Same tooltip treatment the schema attributes get (AGL-600); these
1214
+ // fields never reach the `withAttributeHelp` map above. The docs link is
1215
+ // the one thing a tooltip cannot carry: which scope to pick and what the
1216
+ // hundred-record ceiling means are a page, not a sentence.
1217
+ help: {
1218
+ title: label,
1219
+ excerpt: description,
1220
+ href: besignerDocsUrl('repeat', anchor)
1221
+ }
1222
+ });
1223
+ /**
1224
+ * True while the element repeats over ANYTHING. One rule per source, ORed,
1225
+ * so a second source added later widens it without touching the fields the
1226
+ * bounds are on.
1227
+ */ const repeating = sources.length === 1 ? {
1228
+ when: sources[0].keyProp,
1229
+ isNotEmpty: true
1230
+ } : {
1231
+ or: sources.map((source)=>({
1232
+ when: source.keyProp,
1233
+ isNotEmpty: true
1234
+ }))
1235
+ };
1236
+ return [
1237
+ ...sources.map((source)=>{
1238
+ var _source_keyAttribute_label, _source_keyAttribute_description;
1239
+ return resolveKeyField(_extends({}, source.keyAttribute, {
1240
+ name: source.keyProp,
1241
+ help: {
1242
+ title: (_source_keyAttribute_label = source.keyAttribute.label) != null ? _source_keyAttribute_label : source.label,
1243
+ excerpt: (_source_keyAttribute_description = source.keyAttribute.description) != null ? _source_keyAttribute_description : '',
1244
+ href: besignerDocsUrl('repeat')
1245
+ }
1246
+ }));
1247
+ }),
1248
+ ...REPEAT_NODE_CAPABILITY.attributes.filter((attribute)=>hasChildren || !attribute.requiresChildren).map((attribute)=>{
1249
+ const { label, description, docsAnchor, requiresChildren: _requiresChildren } = attribute, field = _object_without_properties_loose(attribute, [
1250
+ "label",
1251
+ "description",
1252
+ "docsAnchor",
1253
+ "requiresChildren"
1254
+ ]);
1255
+ return _extends({}, field, help(String(label != null ? label : ''), description != null ? description : '', docsAnchor), {
1256
+ condition: repeating
1257
+ });
1258
+ })
1259
+ ];
1260
+ }
1261
+ /**
1262
+ * The list each entity picker DISPLAYS, keyed by the attribute type a
1263
+ * component schema declares.
1264
+ *
1265
+ * One table rather than two switches, because the demand signal and the
1266
+ * option list have to agree about which list a picker means. When they
1267
+ * disagreed the failure was silent and total: the panel would open a
1268
+ * listener on one collection and read its options out of another, and the
1269
+ * dropdown stayed empty on a site full of data.
1270
+ *
1271
+ * `DATASET_FIELD_SELECT` is deliberately absent. Its options are the model
1272
+ * fields of the dataset an ANCESTOR chose, so it needs the dataset list to
1273
+ * resolve against but is not itself a picker OF datasets — it asks for the
1274
+ * list below and renders its own options.
1275
+ */ export const ENTITY_PICKER_KINDS = {
1276
+ [FieldComponentType.PRODUCT_SELECT]: 'products',
1277
+ [FieldComponentType.COLLECTION_SELECT]: 'collections',
1278
+ [FieldComponentType.CATEGORY_SELECT]: 'categories',
1279
+ [FieldComponentType.DATASET_SELECT]: 'datasets',
1280
+ [FieldComponentType.FORM_SELECT]: 'forms'
1281
+ };
1282
+ /** Where an author makes more of each kind, named in the empty picker. */ const ENTITY_PICKER_ORIGIN = {
1283
+ products: {
1284
+ singular: 'product',
1285
+ plural: 'products',
1286
+ page: 'the Products page'
1287
+ },
1288
+ collections: {
1289
+ singular: 'collection',
1290
+ plural: 'collections',
1291
+ page: 'the Collections page'
1292
+ },
1293
+ categories: {
1294
+ singular: 'category',
1295
+ plural: 'categories',
1296
+ page: 'the Categories page'
1297
+ },
1298
+ datasets: {
1299
+ singular: 'dataset',
1300
+ plural: 'datasets',
1301
+ page: 'the Data page'
1302
+ },
1303
+ forms: {
1304
+ singular: 'form',
1305
+ plural: 'forms',
1306
+ page: 'the Forms page'
1307
+ }
1308
+ };
1309
+ /**
1310
+ * The label on an entity picker's empty first option.
1311
+ *
1312
+ * An empty dropdown and a broken dropdown look identical, and an author who
1313
+ * cannot tell them apart goes looking for a form they already made. So the
1314
+ * four reasons a picker has nothing say four different things, and only the
1315
+ * settled one claims anything about the site.
1316
+ */ export function entityPickerPlaceholder(kind, state, count) {
1317
+ if (count > 0) return 'None';
1318
+ const { plural, page } = ENTITY_PICKER_ORIGIN[kind];
1319
+ switch(state){
1320
+ case 'error':
1321
+ return `Could not load ${plural} — reopen this panel to try again`;
1322
+ case 'loading':
1323
+ return `Loading ${plural}…`;
1324
+ case 'unavailable':
1325
+ return `This editor cannot list ${plural}`;
1326
+ default:
1327
+ return `No ${plural} yet — add one on ${page}`;
1328
+ }
1329
+ }
1330
+ /**
1331
+ * What a picker owes the reader about the list it is showing.
1332
+ *
1333
+ * A browse window is a PAGE of the site's catalog, and a page that does not
1334
+ * say so is the silent-truncation defect: "not in the picker" and "does not
1335
+ * exist" are the same absence to look at, and only one of them is a fact
1336
+ * about their site. The inbox's form filter carries the same sentence for the
1337
+ * same reason.
1338
+ *
1339
+ * Two sentences, because the second half is only true for a kind whose
1340
+ * documents carry the name-search keys. A picker that promised to search the
1341
+ * whole catalog and then searched 25 rows would be a worse lie than saying
1342
+ * nothing.
1343
+ */ export function entityPickerBrowseNotice(kind, context) {
1344
+ var _context_truncated, _context_searchable;
1345
+ if (!(context == null ? void 0 : (_context_truncated = context.truncated) == null ? void 0 : _context_truncated[kind])) return undefined;
1346
+ const { plural } = ENTITY_PICKER_ORIGIN[kind];
1347
+ const first = `Showing the first ${ENTITY_PICKER_BROWSE_LIMIT} ${plural} — this site has more. `;
1348
+ return ((_context_searchable = context.searchable) == null ? void 0 : _context_searchable[kind]) ? `${first}Type to search all of them.` : `${first}Typing narrows these ${ENTITY_PICKER_BROWSE_LIMIT} only.`;
1349
+ }
1350
+ /**
1351
+ * What the dropdown says when the reader's typing matches nothing it holds.
1352
+ *
1353
+ * On a truncated list that cannot be searched on the server, "no match" is a
1354
+ * statement about the WINDOW and must not be read as one about the site —
1355
+ * this is where an author concludes a form they already made does not exist
1356
+ * and makes a second one.
1357
+ */ export function entityPickerNoMatchText(kind, context) {
1358
+ var _context_truncated, _context_searchable;
1359
+ const { plural } = ENTITY_PICKER_ORIGIN[kind];
1360
+ if ((context == null ? void 0 : (_context_truncated = context.truncated) == null ? void 0 : _context_truncated[kind]) && !(context == null ? void 0 : (_context_searchable = context.searchable) == null ? void 0 : _context_searchable[kind])) {
1361
+ return `No match in the first ${ENTITY_PICKER_BROWSE_LIMIT} ${plural} — this site has more.`;
1362
+ }
1363
+ return `No ${plural} match.`;
1364
+ }
1365
+ /**
1366
+ * The extra option a picker needs when its stored value is not in the window.
1367
+ *
1368
+ * The counterpart to {@link Aglyn.unresolvedScreenOption}, and it exists for
1369
+ * the same reason: a picker whose value matches no option renders BLANK, which
1370
+ * reads as "nothing chosen" on an element that is bound. An author repairs
1371
+ * that by choosing again — which is how a correct reference gets replaced with
1372
+ * a different one.
1373
+ *
1374
+ * Returns the stored value UNCHANGED as the option's value. Naming a
1375
+ * selection must never rewrite it.
1376
+ *
1377
+ * A value nothing has answered for yet shows as the raw id, NOT as a warning.
1378
+ * Flashing "unavailable" over every live reference for the beat before the
1379
+ * keyed read lands would teach authors to ignore the one warning that means
1380
+ * something — the same call `unavailableScreenLabel` makes.
1381
+ *
1382
+ * The exception is a picker that has settled and has no way to look anything
1383
+ * up. There is no answer coming, so the raw value is all there will ever be,
1384
+ * and left unmarked it would read as a resolved name — which is exactly what
1385
+ * a free-text caption stored where an id belongs looks like.
1386
+ */ export function entitySelectionOption(context, kind, value) {
1387
+ var _ref;
1388
+ var _context_resolved_kind, _context_resolved;
1389
+ const id = typeof value === 'string' ? value.trim() : '';
1390
+ if (!id) return undefined;
1391
+ if (((_ref = context == null ? void 0 : context[kind]) != null ? _ref : []).some((entity)=>entity.id === id)) {
1392
+ return undefined;
1393
+ }
1394
+ const resolved = context == null ? void 0 : (_context_resolved = context.resolved) == null ? void 0 : (_context_resolved_kind = _context_resolved[kind]) == null ? void 0 : _context_resolved_kind[id];
1395
+ if (resolved) return {
1396
+ value: id,
1397
+ label: resolved.label
1398
+ };
1399
+ const { singular } = ENTITY_PICKER_ORIGIN[kind];
1400
+ if (resolved === null) {
1401
+ return {
1402
+ value: id,
1403
+ label: `⚠ Unavailable ${singular} (${id}) — deleted`
1404
+ };
1405
+ }
1406
+ const settledWithNoLookup = entityListState(context, kind) === 'ready' && !(context == null ? void 0 : context.resolve);
1407
+ return {
1408
+ value: id,
1409
+ label: settledWithNoLookup ? `⚠ Unrecognized ${singular} (${id})` : id
1410
+ };
1411
+ }
1412
+ /**
1413
+ * An entity picker rendered as the SELECT the form renderer knows how to
1414
+ * draw, resolved against whatever the surface's picker context can offer.
1415
+ *
1416
+ * Three things this must not do, each of which renders as something other
1417
+ * than a broken picker.
1418
+ *
1419
+ * Every option carries the entity's ID, never its label. The label is
1420
+ * resolved fresh at edit time and a rename moves it; the id is the whole
1421
+ * reference, and for a form it is the difference between one submission list
1422
+ * and two (`docs/specs/reusable-forms.md` §2c).
1423
+ *
1424
+ * The field is decided by the attribute's own TYPE, not by whether a list
1425
+ * happens to have arrived. Keying off the list meant a surface with no picker
1426
+ * context dropped the attribute from the panel entirely — no control, no
1427
+ * explanation, indistinguishable from a component that simply has no such
1428
+ * setting. A picker with nothing to offer must still be a picker, and must
1429
+ * say what is wrong.
1430
+ *
1431
+ * And the STORED VALUE is offered whether or not the browse window contains
1432
+ * it, which is what lets that window be a console page rather than hundreds
1433
+ * of documents. A picker that took its options and its current selection from
1434
+ * one list needs the list wide enough to hold whatever an author picked last
1435
+ * month, and past that width renders a bound element as unbound anyway. The
1436
+ * selection is a keyed read; this is where its answer is offered.
1437
+ */ export function buildEntityPickerField(field, kind, context, value) {
1438
+ var _ref;
1439
+ const entities = (_ref = context == null ? void 0 : context[kind]) != null ? _ref : [];
1440
+ const selected = entitySelectionOption(context, kind, value);
1441
+ const notice = entityPickerBrowseNotice(kind, context);
1442
+ const search = context == null ? void 0 : context.search;
1443
+ const request = context == null ? void 0 : context.request;
1444
+ return _extends({}, field, {
1445
+ component: FieldComponentType.SELECT
1446
+ }, request ? {
1447
+ onOpen: ()=>request(kind)
1448
+ } : {}, {
1449
+ // The dropdown's input is read-only without this, so a list of any size
1450
+ // could only be scrolled. Narrowing 25 rows by typing is the cheapest
1451
+ // half of reaching past them.
1452
+ isSearchable: true
1453
+ }, search ? {
1454
+ // `onSearchInput` and NOT `onInputChange`: the latter is owned by
1455
+ // `@data-driven-forms/common/select`, which assigns it after
1456
+ // spreading the field's props, so a handler declared here would be
1457
+ // overwritten before the mapper saw it — and a dropped handler is
1458
+ // invisible, since the dropdown still opens and still filters.
1459
+ //
1460
+ // Only a typed character asks for anything. A `reset` fires when
1461
+ // the field takes its own value back on mount and when a choice is
1462
+ // made, and treating those as a query would spend a search read on
1463
+ // opening the panel — the read this whole arc is about not making.
1464
+ onSearchInput: (text, reason)=>{
1465
+ if (reason && reason !== 'input') return;
1466
+ search(kind, text);
1467
+ }
1468
+ } : {}, notice ? {
1469
+ helperText: notice
1470
+ } : {}, {
1471
+ noOptionsText: ()=>entityPickerNoMatchText(kind, context),
1472
+ options: [
1473
+ {
1474
+ value: '',
1475
+ label: entityPickerPlaceholder(kind, entityListState(context, kind), entities.length)
1476
+ },
1477
+ ...[
1478
+ ...entities
1479
+ ].sort((a, b)=>a.label.localeCompare(b.label)).map((entity)=>({
1480
+ value: entity.id,
1481
+ label: entity.label
1482
+ })),
1483
+ ...selected ? [
1484
+ selected
1485
+ ] : []
1486
+ ]
1487
+ });
1488
+ }
1489
+ /**
1490
+ * Which entity lists a node's attributes will need (AGL-703).
1491
+ *
1492
+ * The provider that owns those lists reads them from Firestore, and it used
1493
+ * to read all four the moment the besigner opened — up to 300 products, 200
1494
+ * catalog collections, 200 categories and 200 datasets — for pickers most
1495
+ * editing sessions never open. This is the demand signal that replaced that:
1496
+ * a node whose schema declares the picker.
1497
+ *
1498
+ * `DATASET_FIELD_SELECT` asks for `datasets` and not for some list of its
1499
+ * own, which is the one mapping worth stating: its options are the model
1500
+ * fields of the dataset an ANCESTOR chose, so it cannot resolve one without
1501
+ * the dataset list to resolve it against.
1502
+ *
1503
+ * Exported for the test that pins the mapping — a picker silently missing
1504
+ * from {@link ENTITY_PICKER_KINDS} renders as a permanently empty dropdown,
1505
+ * which looks exactly like a site with no products.
1506
+ */ export function entityKindsForAttributes(attributes) {
1507
+ const kinds = new Set();
1508
+ for (const field of attributes != null ? attributes : []){
1509
+ if (field.component === FieldComponentType.DATASET_FIELD_SELECT) {
1510
+ kinds.add('datasets');
1511
+ continue;
1512
+ }
1513
+ const kind = ENTITY_PICKER_KINDS[field.component];
1514
+ if (kind) kinds.add(kind);
1515
+ }
1516
+ return [
1517
+ ...kinds
1518
+ ];
1519
+ }
1520
+ const ElementPropsFormRaw = /*#__PURE__*/ forwardRef((props, ref)=>{
1521
+ var _ref, _ref1, _interactions_automations, _interactions_sectionExperiments;
1522
+ var _schema_flags, _shownNode_props;
1523
+ const { node } = props, rest = _object_without_properties_loose(props, [
1524
+ "node"
1525
+ ]);
1526
+ const schema = node == null ? void 0 : node.componentSchema;
1527
+ const nodeProps = node == null ? void 0 : node.props;
1528
+ const deleteElementCallback = useDeleteElementCallback();
1529
+ const rawAttributes = schema == null ? void 0 : schema.attributes;
1530
+ // Reusable-component flows (AGL-35): actions appear only when the host
1531
+ // app provides callbacks; locked nodes (layout chrome) never promote.
1532
+ const { onPromote, onDemote, onEditComponent, definitions } = useContext(ComponentPromotionContext);
1533
+ const isInstance = (node == null ? void 0 : node.componentId) === REUSABLE_INSTANCE_COMPONENT_ID;
1534
+ const unlocked = Besigner.dnd.canDragNode(node);
1535
+ // The properties the placed component declares (AGL-1247), when this is
1536
+ // an instance.
1537
+ const instanceDeclared = useMemo(()=>{
1538
+ var _node_props, _definitions_refId;
1539
+ if (!isInstance) return undefined;
1540
+ const refId = node == null ? void 0 : (_node_props = node.props) == null ? void 0 : _node_props.refId;
1541
+ return refId ? definitions == null ? void 0 : (_definitions_refId = definitions[refId]) == null ? void 0 : _definitions_refId.props : undefined;
1542
+ }, [
1543
+ isInstance,
1544
+ node,
1545
+ definitions
1546
+ ]);
1547
+ // Every field the panel will draw, as its schema DECLARES it — the
1548
+ // element's attributes and the instance's properties as the attribute
1549
+ // kinds they are edited with. What the panel reads for its pickers
1550
+ // (entity lists, canvas elements, the ancestor dataset) follows this, so
1551
+ // a property of a picker kind is fed exactly as an attribute of one.
1552
+ const demandAttributes = useMemo(()=>[
1553
+ ...rawAttributes != null ? rawAttributes : [],
1554
+ ...(instanceDeclared != null ? instanceDeclared : []).map((prop)=>({
1555
+ name: `${REUSABLE_INSTANCE_PROP_VALUES_KEY}.${prop == null ? void 0 : prop.name}`,
1556
+ component: reusablePropKind(prop == null ? void 0 : prop.type).field
1557
+ }))
1558
+ ], [
1559
+ rawAttributes,
1560
+ instanceDeclared
1561
+ ]);
1562
+ // Screen-select fields can't carry static options (the host's screens
1563
+ // are only known at edit time), so resolve them here from the routing
1564
+ // map + labels the console provides via ScreenLinkContext. Entity
1565
+ // pickers (AGL-343/344) resolve the same way from EntityPickerContext.
1566
+ const { screens, labels } = useContext(ScreenLinkContext);
1567
+ const entityOptions = useContext(EntityPickerContext);
1568
+ /**
1569
+ * Ask for the entity lists this node's pickers will actually show
1570
+ * (AGL-703).
1571
+ *
1572
+ * The provider used to read all four collections when the besigner
1573
+ * opened — up to 900 documents on a site with a catalog — for pickers
1574
+ * most editing sessions never open. Moving a heading does not need the
1575
+ * product list.
1576
+ *
1577
+ * The demand signal is the node's own schema, which is already scanned
1578
+ * twice below for exactly this kind of question (`wantsNodes`,
1579
+ * `hasDatasetFieldSelect`). `DATASET_FIELD_SELECT` asks for `datasets`
1580
+ * too: its options come from the chosen dataset's model, so it cannot
1581
+ * resolve one without the list.
1582
+ */ const requestEntities = entityOptions.request;
1583
+ useEffect(()=>{
1584
+ if (!requestEntities) return;
1585
+ for (const kind of entityKindsForAttributes(demandAttributes)){
1586
+ requestEntities(kind);
1587
+ }
1588
+ }, [
1589
+ demandAttributes,
1590
+ requestEntities
1591
+ ]);
1592
+ /**
1593
+ * Learn the name of the entity this node is ALREADY bound to.
1594
+ *
1595
+ * The browse list is a page now, so it answers this only by luck. A keyed
1596
+ * read answers it always, and asking for one is gated hard: only for a
1597
+ * node that carries a value, only once the browse read has settled
1598
+ * without supplying it, and never twice for the same id. A site whose
1599
+ * catalog fits in the window makes no such read at all.
1600
+ */ const resolveEntity = entityOptions.resolve;
1601
+ useEffect(()=>{
1602
+ if (!resolveEntity) return;
1603
+ for (const field of demandAttributes){
1604
+ const kind = ENTITY_PICKER_KINDS[field.component];
1605
+ if (!kind) continue;
1606
+ const id = entityValueNeedsResolution(entityOptions, kind, readAttributeFieldValue(nodeProps, field.name));
1607
+ if (id) resolveEntity(kind, id);
1608
+ }
1609
+ }, [
1610
+ demandAttributes,
1611
+ nodeProps,
1612
+ entityOptions,
1613
+ resolveEntity
1614
+ ]);
1615
+ /**
1616
+ * The same keyed read for the entity a REPEAT already names (AGL-3111).
1617
+ *
1618
+ * Its picker is not in `demandAttributes` — Repeat is offered on every
1619
+ * element, so a schema-shaped demand there would read a whole list for
1620
+ * every selection. This asks for the one document instead, and only for a
1621
+ * node that already repeats, so an existing repeat opens showing its
1622
+ * dataset's name rather than a bare id.
1623
+ */ const repeatSources = useRepeatSources();
1624
+ useEffect(()=>{
1625
+ if (!resolveEntity) return;
1626
+ const repeat = repeatSourceOf({
1627
+ props: nodeProps
1628
+ });
1629
+ const kind = repeat && ENTITY_PICKER_KINDS[repeat.source.keyAttribute.component];
1630
+ if (!kind) return;
1631
+ const id = entityValueNeedsResolution(entityOptions, kind, repeat == null ? void 0 : repeat.key);
1632
+ if (id) resolveEntity(kind, id);
1633
+ }, [
1634
+ repeatSources,
1635
+ nodeProps,
1636
+ entityOptions,
1637
+ resolveEntity
1638
+ ]);
1639
+ // Canvas-node options for NODE_SELECT attributes (AGL-557): every
1640
+ // other element on the canvas, labeled by component name + a text
1641
+ // snippet, with a short id suffix to tell repeats apart. The edited
1642
+ // node itself is excluded (revealing yourself is never meaningful).
1643
+ const nodeOptions = useMemo(()=>{
1644
+ var _canvas_toJSON_nodes;
1645
+ const wantsNodes = demandAttributes.some((field)=>field.component === FieldComponentType.NODE_SELECT);
1646
+ if (!wantsNodes) return [];
1647
+ const canvasNodes = (_canvas_toJSON_nodes = canvas.toJSON().nodes) != null ? _canvas_toJSON_nodes : {};
1648
+ return Object.entries(canvasNodes).filter(([id])=>id && id !== (node == null ? void 0 : node.$id)).map(([id, candidate])=>{
1649
+ var _ref, _ref1;
1650
+ var _components_getSchema, _candidate_props;
1651
+ const displayName = (_ref = (_ref1 = (_components_getSchema = components.getSchema(candidate == null ? void 0 : candidate.componentId)) == null ? void 0 : _components_getSchema.displayName) != null ? _ref1 : candidate == null ? void 0 : candidate.componentId) != null ? _ref : 'Element';
1652
+ const text = typeof (candidate == null ? void 0 : (_candidate_props = candidate.props) == null ? void 0 : _candidate_props.children) === 'string' ? candidate.props.children.trim().slice(0, 24) : '';
1653
+ return {
1654
+ value: id,
1655
+ label: `${displayName}${text ? ` "${text}"` : ''} · ${id.slice(0, 6)}`
1656
+ };
1657
+ }).sort((a, b)=>a.label.localeCompare(b.label));
1658
+ }, [
1659
+ demandAttributes,
1660
+ node == null ? void 0 : node.$id
1661
+ ]);
1662
+ // Dataset-field selects (AGL-556) list the model fields of the nearest
1663
+ // ancestor's chosen dataset (e.g. the form field's parent form). The
1664
+ // ancestor persists the dataset id; legacy nodes carrying only a name
1665
+ // resolve it by matching the current display labels.
1666
+ const hasDatasetFieldSelect = demandAttributes.some((field)=>field.component === FieldComponentType.DATASET_FIELD_SELECT);
1667
+ const ancestorDatasetId = useMemo(()=>{
1668
+ if (!hasDatasetFieldSelect || !(node == null ? void 0 : node.$id)) return undefined;
1669
+ const nodes = canvas.toJSON().nodes;
1670
+ let current = nodes[node.$id];
1671
+ for(let hops = 0; current && hops < 100; hops += 1){
1672
+ var _current_props;
1673
+ const props = (_current_props = current.props) != null ? _current_props : {};
1674
+ if (typeof props.datasetId === 'string' && props.datasetId) {
1675
+ return props.datasetId;
1676
+ }
1677
+ if (typeof props.datasetName === 'string' && props.datasetName) {
1678
+ var _entityOptions_datasets;
1679
+ const byLabel = ((_entityOptions_datasets = entityOptions.datasets) != null ? _entityOptions_datasets : []).find((dataset)=>dataset.label === props.datasetName);
1680
+ if (byLabel) return byLabel.id;
1681
+ }
1682
+ current = current.parentId ? nodes[current.parentId] : undefined;
1683
+ }
1684
+ return undefined;
1685
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1686
+ }, [
1687
+ hasDatasetFieldSelect,
1688
+ node,
1689
+ entityOptions.datasets
1690
+ ]);
1691
+ // Insert-picker options + pill display-name inputs (AGL-583/586),
1692
+ // assembled from this node's ancestor context — shared with the
1693
+ // inline text editor via the extracted hook.
1694
+ const { options: insertOptions, labelContext: tokenLabelContext } = useInsertTokenOptions(node);
1695
+ // Re-render when the installed-plugin set changes (AGL-1030).
1696
+ const knownPluginInstallsVersion = useSyncExternalStore(subscribeKnownPluginInstalls, getKnownPluginInstallsVersion, getKnownPluginInstallsVersion);
1697
+ /**
1698
+ * Whether this element's text is FORMATTED, i.e. it carries a `html`
1699
+ * prop that the renderer draws in preference to `children` (AGL-2486).
1700
+ *
1701
+ * When it does, the two fields disagree about which one is the content:
1702
+ * the canvas draws `html`, and this panel's Text field edits `children`,
1703
+ * so typing here changes a prop nothing renders. The field appears to do
1704
+ * nothing, which is the worst of the three possible behaviours.
1705
+ *
1706
+ * The alternative considered and rejected was letting a plain edit
1707
+ * silently clear `html`. Its failure mode is typing one character and
1708
+ * losing every link in the paragraph, with no sign until the damage is
1709
+ * done. Merging plain text back into markup was rejected too: mapping
1710
+ * arbitrary text onto a marked-up tree has no correct answer once the
1711
+ * edit is structural. So the canvas owns formatted text, this field
1712
+ * shows it read-only and says why, and dropping the formatting is an
1713
+ * explicit, undoable act — see {@link handleRemoveFormatting}.
1714
+ */ const hasFormattedText = isFormattedText(node);
1715
+ // The props of the component being edited, set ONLY inside a component
1716
+ // editor — the same "am I editing a definition" signal the insert picker
1717
+ // keys `{{prop.*}}` off. It decides whether a field with no text box can
1718
+ // be bound to a property, and whether the visibility directives below
1719
+ // are offered.
1720
+ const { componentProps: editedComponentProps, componentPropsOwner } = useContext(BindingPickerContext);
1721
+ // What an attribute's `resolveProps` may read beyond the element's props:
1722
+ // the element itself and the canvas lookup for what is inside it.
1723
+ const fieldContext = useMemo(()=>node ? {
1724
+ node,
1725
+ getNode: (id)=>canvas.getNode(id)
1726
+ } : undefined, [
1727
+ node
1728
+ ]);
1729
+ // The site theme's own scales, for Theme scale and Theme preset
1730
+ // properties — the lists the Styles panel offers through the same
1731
+ // controls (AGL-2486).
1732
+ const hostThemeDoc = useHostThemeDocument();
1733
+ const siteTheme = useAglynSiteTheme({
1734
+ theme: hostThemeDoc
1735
+ });
1736
+ const themeScales = useMemo(()=>buildStyleThemeScales(siteTheme), [
1737
+ siteTheme
1738
+ ]);
1739
+ // What `resolveAttributeField` reads beyond each field. Its identity
1740
+ // moves when a different node is selected or a save commits — the form
1741
+ // owns the values the author is typing, so not on every keystroke.
1742
+ const resolveContext = useMemo(()=>({
1743
+ screens,
1744
+ labels,
1745
+ // Read by the Screen pickers to name a target the map has lost
1746
+ // (AGL-1893), and by entity pickers to offer what is stored.
1747
+ values: nodeProps,
1748
+ entityOptions,
1749
+ nodeOptions,
1750
+ ancestorDatasetId,
1751
+ hasFormattedText,
1752
+ insertOptions,
1753
+ tokenLabelContext,
1754
+ // The installed-plugin set arrives from a live subscription and can
1755
+ // land after this memo first ran (AGL-1030) — without it the picker
1756
+ // would sit on "No plugins installed for this site" until the panel
1757
+ // remounted.
1758
+ pluginInstallsVersion: knownPluginInstallsVersion
1759
+ }), [
1760
+ screens,
1761
+ labels,
1762
+ nodeProps,
1763
+ entityOptions,
1764
+ nodeOptions,
1765
+ ancestorDatasetId,
1766
+ hasFormattedText,
1767
+ insertOptions,
1768
+ tokenLabelContext,
1769
+ knownPluginInstallsVersion
1770
+ ]);
1771
+ const attributes = useMemo(()=>{
1772
+ // Every described attribute gets a help tooltip beside the field
1773
+ // (AGL-600) — the definition's own description, no docs link since
1774
+ // attributes are component-specific.
1775
+ const withAttributeHelp = (field)=>{
1776
+ var _field_label;
1777
+ return field.description && !field['help'] ? _extends({}, field, {
1778
+ help: {
1779
+ title: (_field_label = field['label']) != null ? _field_label : field.name,
1780
+ excerpt: field.description
1781
+ }
1782
+ }) : field;
1783
+ };
1784
+ return (rawAttributes != null ? rawAttributes : []).map(withAttributeHelp).map((field)=>resolveAttributeField(field, resolveContext)).map((field, index)=>{
1785
+ var _rawAttributes_index;
1786
+ return(// One field in, one out, so `index` still names the attribute as
1787
+ // the schema DECLARED it. The rewrites above change `component` —
1788
+ // a Screen picker is the link lookup by now — and what a field can
1789
+ // be bound to follows what it holds, not how it is drawn.
1790
+ withPropertyBinding(field, {
1791
+ declaredComponent: rawAttributes == null ? void 0 : (_rawAttributes_index = rawAttributes[index]) == null ? void 0 : _rawAttributes_index.component,
1792
+ declaredField: rawAttributes == null ? void 0 : rawAttributes[index],
1793
+ componentProps: editedComponentProps,
1794
+ owner: componentPropsOwner,
1795
+ control: elementPropsComponentMapper[field.component],
1796
+ tokenLabelContext
1797
+ }));
1798
+ })// Every rewrite above spreads the field, so a declared `resolveProps`
1799
+ // is still on it here to be handed the element.
1800
+ .map((field)=>withAttributeFieldContext(field, fieldContext)).filter((field)=>{
1801
+ // Unknown editor types must degrade to a skipped attribute, never
1802
+ // kill the whole form: the renderer throws on unregistered
1803
+ // components, which blanked the email designer's attributes panel
1804
+ // when COLOR_PICKER wasn't mapped (AGL-584).
1805
+ const known = field.component in elementPropsComponentMapper;
1806
+ if (!known && process.env.NODE_ENV !== 'production') {
1807
+ console.warn(`[ElementPropsForm] attribute "${field.name}" uses unregistered ` + `editor "${field.component}" — skipped; register it in ` + 'elementPropsComponentMapper.');
1808
+ }
1809
+ return known;
1810
+ });
1811
+ }, [
1812
+ rawAttributes,
1813
+ resolveContext,
1814
+ tokenLabelContext,
1815
+ editedComponentProps,
1816
+ componentPropsOwner,
1817
+ fieldContext
1818
+ ]);
1819
+ // The placed component's properties (AGL-1247), each drawn with the
1820
+ // control its kind names and finished by the same resolver as the
1821
+ // attributes above (AGL-2893).
1822
+ const instancePropFields = useMemo(()=>isInstance ? buildInstancePropFields(instanceDeclared, insertOptions, tokenLabelContext, _extends({}, resolveContext, {
1823
+ themeScales
1824
+ })) : [], [
1825
+ isInstance,
1826
+ instanceDeclared,
1827
+ insertOptions,
1828
+ tokenLabelContext,
1829
+ resolveContext,
1830
+ themeScales
1831
+ ]);
1832
+ // Visibility directives (AGL-1314), inside a component editor only —
1833
+ // `componentProps` is the same "am I editing a definition" signal the
1834
+ // insert picker keys `{{prop.*}}` off.
1835
+ const visibilityFields = useMemo(()=>editedComponentProps ? buildVisibilityFields(insertOptions, tokenLabelContext) : [], [
1836
+ editedComponentProps,
1837
+ insertOptions,
1838
+ tokenLabelContext
1839
+ ]);
1840
+ // Element animation (AGL-2486). Static, so it is built once rather than
1841
+ // per render; unlike the visibility directives it is offered everywhere.
1842
+ const animationFields = useMemo(()=>buildAnimationFields(), []);
1843
+ /**
1844
+ * Repeat (AGL-3111), offered on every element and every instance — the
1845
+ * whole point of the capability — from whatever sources the loaded plugins
1846
+ * registered. Nothing registered, no section.
1847
+ *
1848
+ * The key field is finished by the same resolver the schema attributes go
1849
+ * through, so a source that names an entity picker gets the real picker
1850
+ * with its options, its search and its stored value resolved.
1851
+ */ const repeatFields = useMemo(()=>{
1852
+ var _node_nodes;
1853
+ return buildRepeatFields({
1854
+ sources: repeatSources,
1855
+ hasChildren: Boolean(node == null ? void 0 : (_node_nodes = node.nodes) == null ? void 0 : _node_nodes.length),
1856
+ resolveKeyField: (field)=>resolveAttributeField(field, resolveContext)
1857
+ });
1858
+ }, [
1859
+ repeatSources,
1860
+ node == null ? void 0 : node.nodes,
1861
+ resolveContext
1862
+ ]);
1863
+ /**
1864
+ * What this element's repeat does, in words, or nothing when it does not
1865
+ * repeat (AGL-168, AGL-3111).
1866
+ *
1867
+ * Named through {@link repeatSourceOf} rather than by reading a prop, so
1868
+ * the sentence follows whatever source the author picked and core stays
1869
+ * free of the word "dataset".
1870
+ */ const repeatNotice = useMemo(()=>{
1871
+ var _ref;
1872
+ var _entitySelectionOption;
1873
+ const repeat = repeatSourceOf({
1874
+ props: nodeProps
1875
+ });
1876
+ if (!repeat) return '';
1877
+ // The key as an author knows it, when the source's control is a picker
1878
+ // that can name it. A source drawn with anything else — and a picker
1879
+ // whose list has not answered — is named by the stored key itself.
1880
+ const kind = ENTITY_PICKER_KINDS[repeat.source.keyAttribute.component];
1881
+ const label = (_ref = kind ? (_entitySelectionOption = entitySelectionOption(entityOptions, kind, repeat.key)) == null ? void 0 : _entitySelectionOption.label : undefined) != null ? _ref : repeat.key;
1882
+ return repeatScope({
1883
+ props: nodeProps,
1884
+ nodes: node == null ? void 0 : node.nodes
1885
+ }) === 'self' ? `This element renders once per record of ${repeat.source.label.toLowerCase()} "${label}" on the live site.` : `What is inside this element renders once per record of ${repeat.source.label.toLowerCase()} "${label}" on the live site.`;
1886
+ // `repeatSources` keys it: a source registering later changes the answer.
1887
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1888
+ }, [
1889
+ nodeProps,
1890
+ node == null ? void 0 : node.nodes,
1891
+ entityOptions,
1892
+ repeatSources
1893
+ ]);
1894
+ const formFieldSchema = useMemo(()=>[
1895
+ ...attributes,
1896
+ ...instancePropFields,
1897
+ ...visibilityFields,
1898
+ ...animationFields,
1899
+ ...repeatFields
1900
+ ].map(withNumericValueParse), [
1901
+ attributes,
1902
+ instancePropFields,
1903
+ visibilityFields,
1904
+ animationFields,
1905
+ repeatFields
1906
+ ]);
1907
+ // Image-typed declared props get the same library browse button the
1908
+ // component's own media attributes get (AGL-341).
1909
+ const instanceMediaProps = useMemo(()=>{
1910
+ if (!isInstance) return [];
1911
+ return (instanceDeclared != null ? instanceDeclared : []).filter((prop)=>{
1912
+ var _prop_name;
1913
+ return (prop == null ? void 0 : prop.type) === 'image' && COMPONENT_PROP_NAME_PATTERN.test((_prop_name = prop.name) != null ? _prop_name : '');
1914
+ }).map((prop)=>({
1915
+ name: prop.name,
1916
+ label: prop.label || prop.name
1917
+ }));
1918
+ }, [
1919
+ isInstance,
1920
+ instanceDeclared
1921
+ ]);
1922
+ // Text elements (AGL-130): text-editable elements and any element
1923
+ // declaring text attributes.
1924
+ const textEditable = (((_ref = schema == null ? void 0 : (_schema_flags = schema.flags) == null ? void 0 : _schema_flags.textEditable) != null ? _ref : FEATURE_FLAG.DISABLED) & FEATURE_FLAG.ENABLED) !== 0;
1925
+ const hasTextAttributes = ((_ref1 = schema == null ? void 0 : schema.attributes) != null ? _ref1 : []).some((field)=>field.component === FieldComponentType.TEXT_FIELD || field.component === FieldComponentType.TEXTAREA);
1926
+ // Insert binding (AGL-100): appends a {{token}} to the element text.
1927
+ // Variable/function docs come from the host app; the commit spreads
1928
+ // current props — updateNodeProps REPLACES the props object.
1929
+ const { variables: bindingVariables, functions: bindingFunctions } = useContext(BindingPickerContext);
1930
+ const [bindingAnchor, setBindingAnchor] = useState(null);
1931
+ // Friendly summary of bound string props (AGL-193): id tokens display
1932
+ // as current names; missing referents surface in warning color.
1933
+ const boundPropSummaries = useMemo(()=>{
1934
+ const summaries = [];
1935
+ for (const [key, value] of Object.entries(nodeProps != null ? nodeProps : {})){
1936
+ if (typeof value !== 'string' || !hasBindings(value)) continue;
1937
+ const display = displayBindingTokens(value, bindingVariables != null ? bindingVariables : {}, bindingFunctions != null ? bindingFunctions : {});
1938
+ summaries.push({
1939
+ prop: key,
1940
+ display,
1941
+ missing: display.includes(MISSING_BINDING_LABEL)
1942
+ });
1943
+ }
1944
+ return summaries;
1945
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1946
+ }, [
1947
+ JSON.stringify(nodeProps != null ? nodeProps : {}),
1948
+ bindingVariables,
1949
+ bindingFunctions
1950
+ ]);
1951
+ const handleInsertBinding = useCallback((token)=>{
1952
+ var _current_props;
1953
+ setBindingAnchor(null);
1954
+ const current = canvas.toJSON().nodes[node == null ? void 0 : node.$id];
1955
+ const text = typeof (current == null ? void 0 : (_current_props = current.props) == null ? void 0 : _current_props.children) === 'string' ? current.props.children : '';
1956
+ canvas.updateNodeProps(node, _extends({}, current == null ? void 0 : current.props, {
1957
+ children: text ? `${text} ${token}` : token
1958
+ }));
1959
+ }, [
1960
+ node
1961
+ ]);
1962
+ // Browse media (AGL-106): elements with a `src` attribute can pick an
1963
+ // asset from the host's media library; the commit spreads current props
1964
+ // (updateNodeProps REPLACES the props object).
1965
+ const { onPickMedia } = useContext(MediaPickerContext);
1966
+ // Interactions (AGL-258): element-scoped automations + section A/B.
1967
+ const interactions = useContext(InteractionsContext);
1968
+ const nodeSelector = (node == null ? void 0 : node.$id) ? nodeElementSelector(node.$id) : '';
1969
+ const nodeAutomations = ((_interactions_automations = interactions.automations) != null ? _interactions_automations : []).filter((automation)=>automation.selector === nodeSelector);
1970
+ const nodeExperiment = ((_interactions_sectionExperiments = interactions.sectionExperiments) != null ? _interactions_sectionExperiments : []).find((experiment)=>experiment.nodeId === (node == null ? void 0 : node.$id));
1971
+ // Media-bearing attributes (AGL-341): every image/media URL field gets
1972
+ // a library browse button; the text field itself stays as the manual
1973
+ // URL escape hatch.
1974
+ const mediaAttributes = useMemo(()=>(rawAttributes != null ? rawAttributes : []).filter((field)=>(field == null ? void 0 : field.component) === FieldComponentType.TEXT_FIELD && typeof (field == null ? void 0 : field.name) === 'string' && /^(src|poster)$|(image|logo|avatar|media|thumbnail|photo|background)(Url)?$/i.test(field.name)), [
1975
+ rawAttributes
1976
+ ]);
1977
+ /**
1978
+ * True when this element declares an `alt` attribute of its own — Image
1979
+ * and Email image today. Read off the SCHEMA rather than assumed, so a
1980
+ * component without one is never handed a prop its renderer would drop.
1981
+ */ const declaresAlt = useMemo(()=>(rawAttributes != null ? rawAttributes : []).some((field)=>(field == null ? void 0 : field.name) === 'alt'), [
1982
+ rawAttributes
1983
+ ]);
1984
+ /**
1985
+ * Drops the formatting and keeps the words (AGL-2486).
1986
+ *
1987
+ * Destructive to formatting and labelled as such. It is ONE
1988
+ * `updateNodeProps`, so it is one undo entry — undo restores the markup,
1989
+ * not merely the text, which is what makes offering it honest.
1990
+ *
1991
+ * `children` is left exactly as it is: since the projection keeps line
1992
+ * breaks it already holds the plain reading of the markup, so the
1993
+ * element goes on saying the same thing in the same shape.
1994
+ */ const handleRemoveFormatting = useCallback(()=>{
1995
+ if (!(node == null ? void 0 : node.$id)) return;
1996
+ const current = canvas.toJSON().nodes[node.$id];
1997
+ canvas.updateNodeProps(node, withoutFormatting(current == null ? void 0 : current.props));
1998
+ }, [
1999
+ node
2000
+ ]);
2001
+ const handleBrowseMedia = useCallback((propName)=>()=>{
2002
+ // The one kind this attribute can hold, when there is one (AGL-2953).
2003
+ const kind = browseMediaKind({
2004
+ componentId: node == null ? void 0 : node.componentId,
2005
+ propName
2006
+ });
2007
+ // Written through verbatim: the host app decides the persisted form
2008
+ // (today a media reference — AGL-1215), the renderer resolves it.
2009
+ onPickMedia == null ? void 0 : onPickMedia((value, asset)=>{
2010
+ const current = canvas.toJSON().nodes[node == null ? void 0 : node.$id];
2011
+ canvas.updateNodeProps(node, _extends({}, current == null ? void 0 : current.props, {
2012
+ [propName]: value
2013
+ }, inheritedAltPatch({
2014
+ propName,
2015
+ declaresAlt,
2016
+ props: current == null ? void 0 : current.props,
2017
+ assetAlt: asset == null ? void 0 : asset.alt
2018
+ }), intrinsicMediaSize({
2019
+ componentId: node == null ? void 0 : node.componentId,
2020
+ propName,
2021
+ assetWidth: asset == null ? void 0 : asset.width,
2022
+ assetHeight: asset == null ? void 0 : asset.height
2023
+ }), videoMediaProps({
2024
+ componentId: node == null ? void 0 : node.componentId,
2025
+ propName,
2026
+ assetVideo: asset == null ? void 0 : asset.video,
2027
+ assetPoster: asset == null ? void 0 : asset.poster
2028
+ })));
2029
+ }, {
2030
+ kind
2031
+ });
2032
+ }, [
2033
+ onPickMedia,
2034
+ node,
2035
+ declaresAlt
2036
+ ]);
2037
+ // The same picker for an instance's image-typed declared prop, writing
2038
+ // into the nested `propValues` object (AGL-1247). Both levels are
2039
+ // spread because `updateNodeProps` REPLACES the props object rather
2040
+ // than merging into it.
2041
+ const handleBrowseInstanceMedia = useCallback((propName)=>()=>{
2042
+ if (!(node == null ? void 0 : node.$id)) return;
2043
+ onPickMedia == null ? void 0 : onPickMedia((value)=>{
2044
+ var _current_props;
2045
+ const current = canvas.toJSON().nodes[node.$id];
2046
+ canvas.updateNodeProps(node, _extends({}, current == null ? void 0 : current.props, {
2047
+ [REUSABLE_INSTANCE_PROP_VALUES_KEY]: _extends({}, current == null ? void 0 : (_current_props = current.props) == null ? void 0 : _current_props[REUSABLE_INSTANCE_PROP_VALUES_KEY], {
2048
+ [propName]: value
2049
+ })
2050
+ }));
2051
+ });
2052
+ }, [
2053
+ onPickMedia,
2054
+ node
2055
+ ]);
2056
+ /**
2057
+ * Hang the media browser off the FIELDS it fills (AGL-2236).
2058
+ *
2059
+ * The picker has existed since AGL-341, but as full-width buttons after
2060
+ * the form — below `Save Element`, past every attribute the element
2061
+ * declares. The `src` field's own helper text says to "Pick from your
2062
+ * media library with 'Browse media'", so an author who read it looked
2063
+ * beside the field, found nothing, and concluded the only way to change
2064
+ * an image was to hand-type a `media:org:…/…` reference. Attaching the
2065
+ * control to the field makes the instruction true.
2066
+ *
2067
+ * Only fields the token editor renders get one: a read-only attribute
2068
+ * keeps no picker, because a control that writes an unwritable field is
2069
+ * the same class of lie this issue is about.
2070
+ */ const fieldsWithMediaPickers = useMemo(()=>{
2071
+ if (!onPickMedia) return formFieldSchema;
2072
+ const browse = new Map();
2073
+ for (const field of mediaAttributes){
2074
+ browse.set(field.name, handleBrowseMedia(field.name));
2075
+ }
2076
+ for (const field of instanceMediaProps){
2077
+ browse.set(`${REUSABLE_INSTANCE_PROP_VALUES_KEY}.${field.name}`, handleBrowseInstanceMedia(field.name));
2078
+ }
2079
+ if (!browse.size) return formFieldSchema;
2080
+ return formFieldSchema.map((field)=>browse.has(field.name) && field.component === TOKEN_TEXT_FIELD_COMPONENT ? _extends({}, field, {
2081
+ onBrowseMedia: browse.get(field.name)
2082
+ }) : field);
2083
+ }, [
2084
+ formFieldSchema,
2085
+ mediaAttributes,
2086
+ instanceMediaProps,
2087
+ onPickMedia,
2088
+ handleBrowseMedia,
2089
+ handleBrowseInstanceMedia
2090
+ ]);
2091
+ /**
2092
+ * Which running time the published page uses, beside the one the field
2093
+ * holds (AGL-2838).
2094
+ *
2095
+ * The page lays a placed film's current DAM records over its node, and the
2096
+ * asset's running time wins over the one the pick stored, so after a
2097
+ * replace the field can hold a number the page no longer publishes. The
2098
+ * field goes on editing the stored value — nothing here writes a draft —
2099
+ * and says which value the page uses rather than implying it is this one.
2100
+ */ const shownNode = useNodeWithMediaAssetFacts(node);
2101
+ const answered = shownNode !== node;
2102
+ const answeredSeconds = answered ? shownNode == null ? void 0 : (_shownNode_props = shownNode.props) == null ? void 0 : _shownNode_props['durationSeconds'] : undefined;
2103
+ const storedSeconds = nodeProps == null ? void 0 : nodeProps['durationSeconds'];
2104
+ const fieldsWithAssetFacts = useMemo(()=>{
2105
+ if (!answered || answeredSeconds === storedSeconds) {
2106
+ return fieldsWithMediaPickers;
2107
+ }
2108
+ const note = answeredSeconds === undefined ? 'The media library no longer records a running time for this ' + 'film, so the published page gives none.' : `The media library records ${answeredSeconds} seconds for this ` + 'film, and the published page uses that.';
2109
+ return fieldsWithMediaPickers.map((field)=>field.name === 'durationSeconds' ? _extends({}, field, {
2110
+ helperText: note
2111
+ }) : field);
2112
+ }, [
2113
+ fieldsWithMediaPickers,
2114
+ answered,
2115
+ answeredSeconds,
2116
+ storedSeconds
2117
+ ]);
2118
+ // The field names whose value is a number, instance overrides included
2119
+ // under the `propValues.` path the schema names them by.
2120
+ const numericFieldNames = useMemo(()=>new Set(formFieldSchema.filter((field)=>(field == null ? void 0 : field.type) === 'number').map((field)=>field.name)), [
2121
+ formFieldSchema
2122
+ ]);
2123
+ const handleFormCancel = useCallback((e, reason)=>{}, []);
2124
+ const handleElementSave = useCallback((values)=>{
2125
+ var _node_componentSchema;
2126
+ // Hand-typed {{name}} tokens normalize to their rename-safe
2127
+ // {{var:id}} form at save (AGL-186); unknown names pass through.
2128
+ const normalizeToken = (value)=>typeof value === 'string' && hasBindings(value) ? normalizeBindingTokens(value, bindingVariables != null ? bindingVariables : {}, bindingFunctions != null ? bindingFunctions : {}) : value;
2129
+ // Numbers typed as text are converted by the field's own `parse`
2130
+ // (see `withNumericValueParse`) — repeated here because a document
2131
+ // authored before that landed carries the string, and an untouched
2132
+ // field never parses. The element goes on rendering at its default
2133
+ // until something rewrites the prop, so the next commit does.
2134
+ const normalizeNumeric = (name, value)=>numericFieldNames.has(name) ? numericTextValue(value) : value;
2135
+ const normalized = _extends({}, values);
2136
+ for (const [key, value] of Object.entries(values)){
2137
+ normalized[key] = normalizeNumeric(key, normalizeToken(value));
2138
+ }
2139
+ // A component instance's overrides sit one level down (AGL-1247),
2140
+ // so the walk above skips them — normalize inside, or a hand-typed
2141
+ // {{name}} in an override would persist in its rename-unsafe form.
2142
+ const overrides = values[REUSABLE_INSTANCE_PROP_VALUES_KEY];
2143
+ if (overrides && typeof overrides === 'object') {
2144
+ const nextOverrides = {};
2145
+ for (const [key, value] of Object.entries(overrides)){
2146
+ nextOverrides[key] = normalizeNumeric(`${REUSABLE_INSTANCE_PROP_VALUES_KEY}.${key}`, normalizeToken(value));
2147
+ }
2148
+ normalized[REUSABLE_INSTANCE_PROP_VALUES_KEY] = nextOverrides;
2149
+ }
2150
+ // Denormalize each picked icon's SVG path next to its id (AGL-1212) —
2151
+ // see `iconPathsForSave` for why a miss does not always clear it.
2152
+ Object.assign(normalized, iconPathsForSave(node == null ? void 0 : (_node_componentSchema = node.componentSchema) == null ? void 0 : _node_componentSchema.attributes, normalized, node == null ? void 0 : node.props));
2153
+ canvas.updateNodeProps(node, normalized);
2154
+ }, [
2155
+ node,
2156
+ bindingVariables,
2157
+ bindingFunctions,
2158
+ numericFieldNames
2159
+ ]);
2160
+ return /*#__PURE__*/ _jsx(_Fragment, {
2161
+ children: /*#__PURE__*/ _jsx(NoSsr, {
2162
+ children: /*#__PURE__*/ _jsx(FormRenderer, _extends({
2163
+ componentMapper: elementPropsComponentMapper,
2164
+ onCancel: handleFormCancel,
2165
+ onSubmit: handleElementSave,
2166
+ initialValues: nodeProps,
2167
+ schema: {
2168
+ fields: fieldsWithAssetFacts
2169
+ }
2170
+ }, rest, {
2171
+ children: (_0)=>{
2172
+ let { formFields, schema } = _0, rest = _object_without_properties_loose(_0, [
2173
+ "formFields",
2174
+ "schema"
2175
+ ]);
2176
+ return /*#__PURE__*/ _jsxs(_Fragment, {
2177
+ children: [
2178
+ textEditable || hasTextAttributes ? /*#__PURE__*/ _jsxs(Box, {
2179
+ sx: {
2180
+ display: 'flex',
2181
+ alignItems: 'center',
2182
+ justifyContent: 'flex-end',
2183
+ // The pull-up closes the gap under a row that holds
2184
+ // only the help icon, which is shorter than the line
2185
+ // box it sits in. `Remove formatting` is a full-height
2186
+ // Button, so the same -8px drives its label into the
2187
+ // outlined field's notch and overprints the `Text`
2188
+ // legend (AGL-2486) — found while capturing the docs
2189
+ // shot of this exact control.
2190
+ mb: hasFormattedText ? 0 : -1
2191
+ },
2192
+ children: [
2193
+ hasFormattedText ? /* A warning-coloured button named for a deletion has to
2194
+ say WHAT it deletes before it is pressed, not after
2195
+ (AGL-2486). The words and the line breaks survive —
2196
+ which is the half an author actually worries about —
2197
+ so the tooltip leads with that, then names what goes,
2198
+ then says it is undoable. Without it the only way to
2199
+ find out is to press it. */ /*#__PURE__*/ _jsx(Tooltip, {
2200
+ title: 'Keeps every word and line break. Removes bold, ' + 'italic, underline, links and lists, so the text ' + 'becomes editable in this field. One undo brings ' + 'the formatting back.',
2201
+ children: /*#__PURE__*/ _jsx(Button, {
2202
+ size: "small",
2203
+ color: "warning",
2204
+ onClick: handleRemoveFormatting,
2205
+ sx: {
2206
+ mr: 'auto',
2207
+ textTransform: 'none'
2208
+ },
2209
+ children: 'Remove formatting'
2210
+ })
2211
+ }) : null,
2212
+ hasFormattedText ? /*#__PURE__*/ _jsx(HelpTip, {
2213
+ title: "This text is formatted",
2214
+ excerpt: "Formatted text is edited on the canvas — double-click the element. Remove formatting to edit it in this field instead; the words and line breaks are kept.",
2215
+ href: besignerDocsUrl('textEditing', '#text-field-read-only'),
2216
+ sx: {
2217
+ fontSize: '0.9em'
2218
+ }
2219
+ }) : /*#__PURE__*/ _jsx(HelpTip, {
2220
+ title: "Editing text",
2221
+ excerpt: "The Text attribute and double-clicking the element on the canvas edit the same value. Rich text is opt-in per element.",
2222
+ href: besignerDocsUrl('textEditing', '#the-text-attribute'),
2223
+ sx: {
2224
+ fontSize: '0.9em'
2225
+ }
2226
+ })
2227
+ ]
2228
+ }) : null,
2229
+ /*#__PURE__*/ _jsx(ElementPropsFormTemplate, _extends({
2230
+ formFields: formFields,
2231
+ schema: schema
2232
+ }, rest)),
2233
+ isInstance ? /*#__PURE__*/ _jsx(InstanceAttrOverrides, {
2234
+ node: node,
2235
+ componentMapper: elementPropsComponentMapper
2236
+ }) : null,
2237
+ boundPropSummaries.length ? /*#__PURE__*/ _jsxs(Box, {
2238
+ sx: {
2239
+ mt: 2
2240
+ },
2241
+ children: [
2242
+ /*#__PURE__*/ _jsxs(Box, {
2243
+ component: "span",
2244
+ sx: {
2245
+ fontSize: 12,
2246
+ color: 'text.secondary',
2247
+ textTransform: 'uppercase',
2248
+ letterSpacing: 0.5
2249
+ },
2250
+ children: [
2251
+ 'Bindings on this element',
2252
+ /*#__PURE__*/ _jsx(HelpTip, {
2253
+ title: "Bindings on this element",
2254
+ excerpt: "Each row is a bound attribute, shown with its current value. A row in warning color means the variable or function it points at no longer exists.",
2255
+ href: besignerDocsUrl('bindings', '#where-used--safety'),
2256
+ sx: {
2257
+ ml: 0.25,
2258
+ fontSize: '0.9em'
2259
+ }
2260
+ })
2261
+ ]
2262
+ }),
2263
+ boundPropSummaries.map((summary)=>/*#__PURE__*/ _jsxs(Box, {
2264
+ sx: {
2265
+ display: 'flex',
2266
+ gap: 1,
2267
+ fontSize: 13,
2268
+ mt: 0.5,
2269
+ color: summary.missing ? 'warning.main' : 'text.primary'
2270
+ },
2271
+ children: [
2272
+ /*#__PURE__*/ _jsx(Box, {
2273
+ component: "span",
2274
+ sx: {
2275
+ color: 'text.secondary'
2276
+ },
2277
+ children: summary.prop
2278
+ }),
2279
+ /*#__PURE__*/ _jsx(Box, {
2280
+ component: "span",
2281
+ sx: {
2282
+ overflow: 'hidden',
2283
+ textOverflow: 'ellipsis',
2284
+ whiteSpace: 'nowrap'
2285
+ },
2286
+ children: summary.display
2287
+ })
2288
+ ]
2289
+ }, summary.prop))
2290
+ ]
2291
+ }) : null,
2292
+ insertOptions.length && textEditable ? /*#__PURE__*/ _jsxs(FormControl, {
2293
+ margin: "none",
2294
+ fullWidth: true,
2295
+ children: [
2296
+ /*#__PURE__*/ _jsx(Button, {
2297
+ color: "primary",
2298
+ onClick: (event)=>setBindingAnchor(event.currentTarget),
2299
+ sx: {
2300
+ mt: 2
2301
+ },
2302
+ fullWidth: true,
2303
+ children: "Insert binding"
2304
+ }),
2305
+ /*#__PURE__*/ _jsx(InsertTokenMenu, {
2306
+ anchorEl: bindingAnchor,
2307
+ open: Boolean(bindingAnchor),
2308
+ onClose: ()=>setBindingAnchor(null),
2309
+ options: insertOptions,
2310
+ onInsert: handleInsertBinding
2311
+ })
2312
+ ]
2313
+ }) : null,
2314
+ repeatNotice ? /*#__PURE__*/ _jsx(FormControl, {
2315
+ margin: "none",
2316
+ fullWidth: true,
2317
+ children: /*#__PURE__*/ _jsx(Alert, {
2318
+ severity: "info",
2319
+ sx: {
2320
+ mt: 2
2321
+ },
2322
+ children: repeatNotice
2323
+ })
2324
+ }) : null,
2325
+ onPromote && !isInstance && unlocked ? /*#__PURE__*/ _jsx(FormControl, {
2326
+ margin: "none",
2327
+ fullWidth: true,
2328
+ children: /*#__PURE__*/ _jsx(Button, {
2329
+ color: "primary",
2330
+ onClick: ()=>onPromote(node),
2331
+ sx: {
2332
+ mt: 2
2333
+ },
2334
+ fullWidth: true,
2335
+ children: "Save as reusable component"
2336
+ })
2337
+ }) : null,
2338
+ onEditComponent && isInstance ? /*#__PURE__*/ _jsx(FormControl, {
2339
+ margin: "none",
2340
+ fullWidth: true,
2341
+ children: /*#__PURE__*/ _jsx(Button, {
2342
+ color: "primary",
2343
+ onClick: ()=>onEditComponent(node),
2344
+ sx: {
2345
+ mt: 2
2346
+ },
2347
+ fullWidth: true,
2348
+ children: "Edit component"
2349
+ })
2350
+ }) : null,
2351
+ onDemote && isInstance && unlocked ? /*#__PURE__*/ _jsx(FormControl, {
2352
+ margin: "none",
2353
+ fullWidth: true,
2354
+ children: /*#__PURE__*/ _jsx(Button, {
2355
+ color: "primary",
2356
+ onClick: ()=>onDemote(node),
2357
+ sx: {
2358
+ mt: 2
2359
+ },
2360
+ fullWidth: true,
2361
+ children: "Detach from component"
2362
+ })
2363
+ }) : null,
2364
+ /*#__PURE__*/ _jsx(FormControl, {
2365
+ margin: "none",
2366
+ fullWidth: true,
2367
+ children: /*#__PURE__*/ _jsx(Button, {
2368
+ onClick: ()=>deleteElementCallback(node),
2369
+ sx: {
2370
+ mt: 2,
2371
+ color: 'error.main'
2372
+ },
2373
+ fullWidth: true,
2374
+ children: "Delete Element"
2375
+ })
2376
+ }),
2377
+ /*#__PURE__*/ _jsx(ElementInfoDetails, {
2378
+ node: node
2379
+ })
2380
+ ]
2381
+ });
2382
+ }
2383
+ }))
2384
+ })
2385
+ });
2386
+ });
2387
+ ElementPropsFormRaw.displayName = 'ElementPropsForm';
2388
+ ElementPropsFormRaw.aglyn = true;
2389
+ export const ElementPropsForm = observer(ElementPropsFormRaw);
2390
+ export default ElementPropsForm;
2391
+
2392
+ //# sourceMappingURL=element-props-form.component.js.map