@limetech/lime-crm-building-blocks 2.3.0 → 2.4.0

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 (293) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cjs/{condition-tree-translate-B_qIQz9s.js → condition-tree-translate-BQwKtiNZ.js} +1 -1
  3. package/dist/cjs/{filter-group-logic-Bt5tIbfP.js → filter-group-logic-CeGmYgsu.js} +1 -1
  4. package/dist/cjs/{filter-operators-CzIq9qKC.js → filter-operators-C3T2czC_.js} +1 -1
  5. package/dist/cjs/{index-mDIvByXc.js → index-Du4GyM0Y.js} +13 -8
  6. package/dist/cjs/index.cjs.js +3 -3
  7. package/dist/cjs/{index.esm-D1EzGa8X.js → index.esm-C2zw0QJK.js} +2 -2
  8. package/dist/cjs/lime-crm-building-blocks.cjs.js +3 -3
  9. package/dist/cjs/{lime-query-validation-Df4GZRGm.js → lime-query-validation-CzYkphmi.js} +1 -1
  10. package/dist/cjs/limebb-alert-dialog.cjs.entry.js +1 -1
  11. package/dist/cjs/limebb-browser.cjs.entry.js +2 -2
  12. package/dist/cjs/limebb-chat-icon-list_3.cjs.entry.js +2 -2
  13. package/dist/cjs/limebb-chat-list.cjs.entry.js +9 -4
  14. package/dist/cjs/limebb-color-palette-picker.cjs.entry.js +1 -1
  15. package/dist/cjs/limebb-color-palette-swatches.cjs.entry.js +1 -1
  16. package/dist/cjs/limebb-component-config.cjs.entry.js +3 -3
  17. package/dist/cjs/limebb-component-picker.cjs.entry.js +2 -2
  18. package/dist/cjs/limebb-composer-toolbar.cjs.entry.js +2 -2
  19. package/dist/cjs/limebb-condition-tree-editor.cjs.entry.js +24 -9
  20. package/dist/cjs/limebb-condition-tree.cjs.entry.js +121 -34
  21. package/dist/cjs/limebb-currency-picker.cjs.entry.js +2 -2
  22. package/dist/cjs/limebb-dashboard-widget.cjs.entry.js +1 -1
  23. package/dist/cjs/limebb-data-cells.cjs.entry.js +2 -2
  24. package/dist/cjs/limebb-date-picker.cjs.entry.js +2 -2
  25. package/dist/cjs/limebb-date-range.cjs.entry.js +1 -1
  26. package/dist/cjs/limebb-document-chips.cjs.entry.js +1 -1
  27. package/dist/cjs/limebb-document-item.cjs.entry.js +1 -1
  28. package/dist/cjs/limebb-document-picker.cjs.entry.js +1 -1
  29. package/dist/cjs/limebb-empty-state.cjs.entry.js +1 -1
  30. package/dist/cjs/limebb-feed-item-thumbnail-file-info.cjs.entry.js +1 -1
  31. package/dist/cjs/limebb-feed-timeline-item.cjs.entry.js +2 -2
  32. package/dist/cjs/limebb-feed.cjs.entry.js +2 -2
  33. package/dist/cjs/limebb-filter-chip_6.cjs.entry.js +4 -4
  34. package/dist/cjs/limebb-icon-picker.cjs.entry.js +1 -1
  35. package/dist/cjs/limebb-info-tile-currency-format.cjs.entry.js +1 -1
  36. package/dist/cjs/limebb-info-tile-date-format.cjs.entry.js +1 -1
  37. package/dist/cjs/limebb-info-tile-decimal-format.cjs.entry.js +1 -1
  38. package/dist/cjs/limebb-info-tile-format.cjs.entry.js +1 -1
  39. package/dist/cjs/limebb-info-tile-relative-date-format.cjs.entry.js +1 -1
  40. package/dist/cjs/limebb-info-tile-unit-format.cjs.entry.js +1 -1
  41. package/dist/cjs/limebb-info-tile.cjs.entry.js +3 -3
  42. package/dist/cjs/limebb-inline-image.cjs.entry.js +2 -2
  43. package/dist/cjs/limebb-kanban-group.cjs.entry.js +2 -2
  44. package/dist/cjs/limebb-kanban-item.cjs.entry.js +2 -2
  45. package/dist/cjs/limebb-kanban.cjs.entry.js +1 -1
  46. package/dist/cjs/limebb-lime-query-builder.cjs.entry.js +3 -3
  47. package/dist/cjs/limebb-lime-query-filter-builder.cjs.entry.js +3 -3
  48. package/dist/cjs/limebb-lime-query-filter-comparison_2.cjs.entry.js +4 -4
  49. package/dist/cjs/limebb-lime-query-order-by-editor_2.cjs.entry.js +3 -3
  50. package/dist/cjs/limebb-lime-query-order-by-item.cjs.entry.js +1 -1
  51. package/dist/cjs/limebb-lime-query-response-format-builder.cjs.entry.js +3 -3
  52. package/dist/cjs/limebb-lime-query-response-format-editor_2.cjs.entry.js +1 -1
  53. package/dist/cjs/limebb-limeobject-file-viewer.cjs.entry.js +2 -2
  54. package/dist/cjs/limebb-live-docs-info.cjs.entry.js +1 -1
  55. package/dist/cjs/limebb-loader.cjs.entry.js +2 -2
  56. package/dist/cjs/limebb-locale-picker.cjs.entry.js +2 -2
  57. package/dist/cjs/limebb-mention-group-counter.cjs.entry.js +1 -1
  58. package/dist/cjs/limebb-mention.cjs.entry.js +2 -2
  59. package/dist/cjs/limebb-navigation-button_2.cjs.entry.js +1 -1
  60. package/dist/cjs/limebb-notification-item.cjs.entry.js +2 -2
  61. package/dist/cjs/limebb-notification-list.cjs.entry.js +2 -2
  62. package/dist/cjs/limebb-object-chip.cjs.entry.js +3 -3
  63. package/dist/cjs/limebb-percentage-visualizer.cjs.entry.js +2 -2
  64. package/dist/cjs/limebb-property-selector.cjs.entry.js +3 -3
  65. package/dist/cjs/limebb-rule-arg-filter-editor.cjs.entry.js +2 -2
  66. package/dist/cjs/limebb-rule-condition-chip.cjs.entry.js +71 -0
  67. package/dist/cjs/limebb-rule-condition-editor-CHWTMOmi.js +285 -0
  68. package/dist/cjs/limebb-rule-condition-editor.cjs.entry.js +9 -0
  69. package/dist/cjs/limebb-rule-editor.cjs.entry.js +217 -573
  70. package/dist/cjs/limebb-rule-gate.cjs.entry.js +2 -2
  71. package/dist/cjs/limebb-text-editor-picker.cjs.entry.js +1 -1
  72. package/dist/cjs/limebb-text-editor.cjs.entry.js +3 -3
  73. package/dist/cjs/limebb-trend-indicator.cjs.entry.js +2 -2
  74. package/dist/cjs/limebb-value-comparison-editor.cjs.entry.js +3 -3
  75. package/dist/cjs/{limetype-BPuuRLtH.js → limetype-BzVDjAKS.js} +1 -1
  76. package/dist/cjs/loader.cjs.js +2 -2
  77. package/dist/cjs/{web-component-template-DRMW9wHN.js → web-component-template-C-AkY5as.js} +1 -1
  78. package/dist/collection/collection-manifest.json +3 -2
  79. package/dist/collection/components/chat-list/chat-list.js +7 -2
  80. package/dist/collection/components/condition-tree/condition-tree-editor/condition-tree-editor.js +46 -5
  81. package/dist/collection/components/condition-tree/condition-tree-nodes.js +13 -6
  82. package/dist/collection/components/condition-tree/condition-tree.js +69 -18
  83. package/dist/collection/components/condition-tree/group-node-view.js +41 -5
  84. package/dist/collection/components/condition-tree/leaf-node-view.js +19 -2
  85. package/dist/collection/components/rule-editor/condition-tree-adapter.js +229 -0
  86. package/dist/collection/components/rule-editor/rule-condition-chip/rule-condition-chip.css +4 -0
  87. package/dist/collection/components/rule-editor/rule-condition-chip/rule-condition-chip.js +322 -0
  88. package/dist/collection/components/rule-editor/rule-condition-editor/rule-condition-editor.css +5 -0
  89. package/dist/collection/components/rule-editor/rule-condition-editor/rule-condition-editor.js +259 -0
  90. package/dist/collection/components/rule-editor/rule-editor.css +1 -73
  91. package/dist/collection/components/rule-editor/rule-editor.js +143 -153
  92. package/dist/collection/components/rule-editor/rule-issues.js +89 -0
  93. package/dist/collection/components/rule-editor/view-helpers.js +19 -41
  94. package/dist/components/condition-tree-adapter.js +1 -0
  95. package/dist/components/condition-tree-editor.js +1 -1
  96. package/dist/components/condition-tree.js +1 -0
  97. package/dist/components/limebb-chat-list.js +1 -1
  98. package/dist/components/limebb-condition-tree.js +1 -1
  99. package/dist/components/limebb-rule-condition-chip.d.ts +11 -0
  100. package/dist/components/limebb-rule-condition-chip.js +1 -0
  101. package/dist/components/limebb-rule-condition-editor.d.ts +11 -0
  102. package/dist/components/limebb-rule-condition-editor.js +1 -0
  103. package/dist/components/limebb-rule-editor.js +1 -1
  104. package/dist/esm/{condition-tree-translate-CBeXbZ_z.js → condition-tree-translate-ZQptbaXF.js} +1 -1
  105. package/dist/esm/{filter-group-logic-CzThKf85.js → filter-group-logic-nqCMmV0e.js} +1 -1
  106. package/dist/esm/{filter-operators-u3T1PKhm.js → filter-operators-QbKK26m2.js} +1 -1
  107. package/dist/esm/{index-COtSgrC5.js → index-BK566_jX.js} +13 -8
  108. package/dist/esm/{index.esm-B9D_MXA6.js → index.esm-C__n3hug.js} +1 -1
  109. package/dist/esm/index.js +3 -3
  110. package/dist/esm/lime-crm-building-blocks.js +4 -4
  111. package/dist/esm/{lime-query-validation-dGoN_93p.js → lime-query-validation-BvB2v-yP.js} +1 -1
  112. package/dist/esm/limebb-alert-dialog.entry.js +1 -1
  113. package/dist/esm/limebb-browser.entry.js +2 -2
  114. package/dist/esm/limebb-chat-icon-list_3.entry.js +2 -2
  115. package/dist/esm/limebb-chat-list.entry.js +9 -4
  116. package/dist/esm/limebb-color-palette-picker.entry.js +1 -1
  117. package/dist/esm/limebb-color-palette-swatches.entry.js +1 -1
  118. package/dist/esm/limebb-component-config.entry.js +3 -3
  119. package/dist/esm/limebb-component-picker.entry.js +2 -2
  120. package/dist/esm/limebb-composer-toolbar.entry.js +2 -2
  121. package/dist/esm/limebb-condition-tree-editor.entry.js +24 -9
  122. package/dist/esm/limebb-condition-tree.entry.js +121 -34
  123. package/dist/esm/limebb-currency-picker.entry.js +2 -2
  124. package/dist/esm/limebb-dashboard-widget.entry.js +1 -1
  125. package/dist/esm/limebb-data-cells.entry.js +2 -2
  126. package/dist/esm/limebb-date-picker.entry.js +2 -2
  127. package/dist/esm/limebb-date-range.entry.js +1 -1
  128. package/dist/esm/limebb-document-chips.entry.js +1 -1
  129. package/dist/esm/limebb-document-item.entry.js +1 -1
  130. package/dist/esm/limebb-document-picker.entry.js +1 -1
  131. package/dist/esm/limebb-empty-state.entry.js +1 -1
  132. package/dist/esm/limebb-feed-item-thumbnail-file-info.entry.js +1 -1
  133. package/dist/esm/limebb-feed-timeline-item.entry.js +2 -2
  134. package/dist/esm/limebb-feed.entry.js +2 -2
  135. package/dist/esm/limebb-filter-chip_6.entry.js +4 -4
  136. package/dist/esm/limebb-icon-picker.entry.js +1 -1
  137. package/dist/esm/limebb-info-tile-currency-format.entry.js +1 -1
  138. package/dist/esm/limebb-info-tile-date-format.entry.js +1 -1
  139. package/dist/esm/limebb-info-tile-decimal-format.entry.js +1 -1
  140. package/dist/esm/limebb-info-tile-format.entry.js +1 -1
  141. package/dist/esm/limebb-info-tile-relative-date-format.entry.js +1 -1
  142. package/dist/esm/limebb-info-tile-unit-format.entry.js +1 -1
  143. package/dist/esm/limebb-info-tile.entry.js +3 -3
  144. package/dist/esm/limebb-inline-image.entry.js +2 -2
  145. package/dist/esm/limebb-kanban-group.entry.js +2 -2
  146. package/dist/esm/limebb-kanban-item.entry.js +2 -2
  147. package/dist/esm/limebb-kanban.entry.js +1 -1
  148. package/dist/esm/limebb-lime-query-builder.entry.js +3 -3
  149. package/dist/esm/limebb-lime-query-filter-builder.entry.js +3 -3
  150. package/dist/esm/limebb-lime-query-filter-comparison_2.entry.js +4 -4
  151. package/dist/esm/limebb-lime-query-order-by-editor_2.entry.js +3 -3
  152. package/dist/esm/limebb-lime-query-order-by-item.entry.js +1 -1
  153. package/dist/esm/limebb-lime-query-response-format-builder.entry.js +3 -3
  154. package/dist/esm/limebb-lime-query-response-format-editor_2.entry.js +1 -1
  155. package/dist/esm/limebb-limeobject-file-viewer.entry.js +2 -2
  156. package/dist/esm/limebb-live-docs-info.entry.js +1 -1
  157. package/dist/esm/limebb-loader.entry.js +2 -2
  158. package/dist/esm/limebb-locale-picker.entry.js +2 -2
  159. package/dist/esm/limebb-mention-group-counter.entry.js +1 -1
  160. package/dist/esm/limebb-mention.entry.js +2 -2
  161. package/dist/esm/limebb-navigation-button_2.entry.js +1 -1
  162. package/dist/esm/limebb-notification-item.entry.js +2 -2
  163. package/dist/esm/limebb-notification-list.entry.js +2 -2
  164. package/dist/esm/limebb-object-chip.entry.js +3 -3
  165. package/dist/esm/limebb-percentage-visualizer.entry.js +2 -2
  166. package/dist/esm/limebb-property-selector.entry.js +3 -3
  167. package/dist/esm/limebb-rule-arg-filter-editor.entry.js +2 -2
  168. package/dist/esm/limebb-rule-condition-chip.entry.js +69 -0
  169. package/dist/esm/limebb-rule-condition-editor-DeY758N-.js +281 -0
  170. package/dist/esm/limebb-rule-condition-editor.entry.js +3 -0
  171. package/dist/esm/limebb-rule-editor.entry.js +217 -573
  172. package/dist/esm/limebb-rule-gate.entry.js +2 -2
  173. package/dist/esm/limebb-text-editor-picker.entry.js +1 -1
  174. package/dist/esm/limebb-text-editor.entry.js +3 -3
  175. package/dist/esm/limebb-trend-indicator.entry.js +2 -2
  176. package/dist/esm/limebb-value-comparison-editor.entry.js +3 -3
  177. package/dist/esm/{limetype-BcELY1NF.js → limetype-Muf9VutX.js} +1 -1
  178. package/dist/esm/loader.js +3 -3
  179. package/dist/esm/{web-component-template-DxbwHsvw.js → web-component-template-D1JpeNy_.js} +1 -1
  180. package/dist/lime-crm-building-blocks/index.esm.js +1 -1
  181. package/dist/lime-crm-building-blocks/lime-crm-building-blocks.esm.js +1 -1
  182. package/dist/lime-crm-building-blocks/p-0621e884.entry.js +1 -0
  183. package/dist/lime-crm-building-blocks/{p-ab0f8170.entry.js → p-087d5651.entry.js} +1 -1
  184. package/dist/lime-crm-building-blocks/{p-ce2755a0.entry.js → p-1917956c.entry.js} +1 -1
  185. package/dist/lime-crm-building-blocks/{p-07a9c8f2.entry.js → p-1ae4962d.entry.js} +1 -1
  186. package/dist/lime-crm-building-blocks/{p-021e452a.entry.js → p-23d2632b.entry.js} +1 -1
  187. package/dist/lime-crm-building-blocks/{p-ffca266e.entry.js → p-251784bf.entry.js} +1 -1
  188. package/dist/lime-crm-building-blocks/{p-635a45ad.entry.js → p-27240bb0.entry.js} +1 -1
  189. package/dist/lime-crm-building-blocks/p-2adeac66.entry.js +1 -0
  190. package/dist/lime-crm-building-blocks/{p-6a8422d2.entry.js → p-32339f09.entry.js} +1 -1
  191. package/dist/lime-crm-building-blocks/{p-1990e369.entry.js → p-3446290c.entry.js} +1 -1
  192. package/dist/lime-crm-building-blocks/{p-d495cc19.entry.js → p-35d9b3ad.entry.js} +1 -1
  193. package/dist/lime-crm-building-blocks/{p-eed0b2c2.entry.js → p-3865eec4.entry.js} +1 -1
  194. package/dist/lime-crm-building-blocks/{p-dacf9e77.entry.js → p-394b521a.entry.js} +1 -1
  195. package/dist/lime-crm-building-blocks/{p-8c36b847.entry.js → p-39a36450.entry.js} +1 -1
  196. package/dist/lime-crm-building-blocks/{p-97e75dee.entry.js → p-3b672249.entry.js} +1 -1
  197. package/dist/lime-crm-building-blocks/{p-ad5dcefd.entry.js → p-47b56a75.entry.js} +1 -1
  198. package/dist/lime-crm-building-blocks/{p-402a9c97.entry.js → p-4af3ea2c.entry.js} +1 -1
  199. package/dist/lime-crm-building-blocks/{p-e7dee7ac.entry.js → p-4bdaf436.entry.js} +1 -1
  200. package/dist/lime-crm-building-blocks/{p-cf66256a.entry.js → p-4f60b7bc.entry.js} +1 -1
  201. package/dist/lime-crm-building-blocks/{p-90f0c96b.entry.js → p-4fd9d615.entry.js} +1 -1
  202. package/dist/lime-crm-building-blocks/{p-28550961.entry.js → p-53b44865.entry.js} +1 -1
  203. package/dist/lime-crm-building-blocks/{p-7ae27308.entry.js → p-550606f6.entry.js} +1 -1
  204. package/dist/lime-crm-building-blocks/p-573f9e95.entry.js +1 -0
  205. package/dist/lime-crm-building-blocks/{p-a5f3913d.entry.js → p-5c541d0c.entry.js} +1 -1
  206. package/dist/lime-crm-building-blocks/{p-ce723c21.entry.js → p-612c6b28.entry.js} +1 -1
  207. package/dist/lime-crm-building-blocks/{p-5f682c06.entry.js → p-627b0c79.entry.js} +1 -1
  208. package/dist/lime-crm-building-blocks/{p-49d042aa.entry.js → p-6303201b.entry.js} +1 -1
  209. package/dist/lime-crm-building-blocks/{p-6f923112.entry.js → p-687640ed.entry.js} +1 -1
  210. package/dist/lime-crm-building-blocks/{p-8e9fcf3c.entry.js → p-6bd61290.entry.js} +1 -1
  211. package/dist/lime-crm-building-blocks/{p-dcb49a62.entry.js → p-6d2dba20.entry.js} +1 -1
  212. package/dist/lime-crm-building-blocks/{p-0193ed83.entry.js → p-6e2553b4.entry.js} +1 -1
  213. package/dist/lime-crm-building-blocks/{p-Cz5s8qGk.js → p-7DQ7qvMa.js} +1 -1
  214. package/dist/lime-crm-building-blocks/p-7c3d3e06.entry.js +1 -0
  215. package/dist/lime-crm-building-blocks/{p-dd02821e.entry.js → p-7d53756b.entry.js} +1 -1
  216. package/dist/lime-crm-building-blocks/{p-79d743bb.entry.js → p-895c8966.entry.js} +1 -1
  217. package/dist/lime-crm-building-blocks/{p-da35bcc7.entry.js → p-8c0455e4.entry.js} +1 -1
  218. package/dist/lime-crm-building-blocks/{p-a201df35.entry.js → p-8f80d024.entry.js} +1 -1
  219. package/dist/lime-crm-building-blocks/{p-32a81271.entry.js → p-949ed056.entry.js} +1 -1
  220. package/dist/lime-crm-building-blocks/{p-cf03a756.entry.js → p-985ae787.entry.js} +1 -1
  221. package/dist/lime-crm-building-blocks/p-9959019d.entry.js +1 -0
  222. package/dist/lime-crm-building-blocks/{p-DLec4QaM.js → p-B7whyaAL.js} +1 -1
  223. package/dist/lime-crm-building-blocks/{p-COtSgrC5.js → p-BK566_jX.js} +1 -1
  224. package/dist/lime-crm-building-blocks/p-ClYOFrOo.js +1 -0
  225. package/dist/lime-crm-building-blocks/{p-DVypQWZ4.js → p-Cve-3L4y.js} +1 -1
  226. package/dist/lime-crm-building-blocks/p-DOULPRTn.js +1 -0
  227. package/dist/lime-crm-building-blocks/{p-Cj_3f9vm.js → p-DhGRAeCH.js} +1 -1
  228. package/dist/lime-crm-building-blocks/p-DjoMDjMo.js +1 -0
  229. package/dist/lime-crm-building-blocks/p-DuWEcQ7n.js +1 -0
  230. package/dist/lime-crm-building-blocks/{p-dfaef740.entry.js → p-a1052482.entry.js} +1 -1
  231. package/dist/lime-crm-building-blocks/{p-3babfbe0.entry.js → p-a14bed52.entry.js} +1 -1
  232. package/dist/lime-crm-building-blocks/{p-cc53a8ca.entry.js → p-a2e291b4.entry.js} +1 -1
  233. package/dist/lime-crm-building-blocks/{p-8a17a7ea.entry.js → p-a2ebd5a4.entry.js} +1 -1
  234. package/dist/lime-crm-building-blocks/{p-0cbe2180.entry.js → p-a4556649.entry.js} +1 -1
  235. package/dist/lime-crm-building-blocks/{p-f5c1ed0c.entry.js → p-a598f92f.entry.js} +1 -1
  236. package/dist/lime-crm-building-blocks/{p-b44b9dbc.entry.js → p-a938384f.entry.js} +1 -1
  237. package/dist/lime-crm-building-blocks/{p-f3539042.entry.js → p-ab0210b2.entry.js} +1 -1
  238. package/dist/lime-crm-building-blocks/{p-c0d49532.entry.js → p-b272730c.entry.js} +1 -1
  239. package/dist/lime-crm-building-blocks/{p-0370200d.entry.js → p-b4571d32.entry.js} +1 -1
  240. package/dist/lime-crm-building-blocks/{p-551a36f6.entry.js → p-b72c748b.entry.js} +1 -1
  241. package/dist/lime-crm-building-blocks/{p-0ed9a7cc.entry.js → p-bac9d3a9.entry.js} +1 -1
  242. package/dist/lime-crm-building-blocks/p-bd6c12bc.entry.js +1 -0
  243. package/dist/lime-crm-building-blocks/{p-81783bd4.entry.js → p-bf693702.entry.js} +1 -1
  244. package/dist/lime-crm-building-blocks/{p-e6cc320a.entry.js → p-c58067ec.entry.js} +1 -1
  245. package/dist/lime-crm-building-blocks/{p-08797129.entry.js → p-c7bfa2e4.entry.js} +1 -1
  246. package/dist/lime-crm-building-blocks/{p-a953d48e.entry.js → p-d4635a39.entry.js} +1 -1
  247. package/dist/lime-crm-building-blocks/{p-d3c58bc1.entry.js → p-d6099dc7.entry.js} +1 -1
  248. package/dist/lime-crm-building-blocks/{p-bf05d9de.entry.js → p-d6c7cf0f.entry.js} +1 -1
  249. package/dist/lime-crm-building-blocks/{p-1ebe45b8.entry.js → p-da79c5fc.entry.js} +1 -1
  250. package/dist/lime-crm-building-blocks/p-e18a2cef.entry.js +1 -0
  251. package/dist/lime-crm-building-blocks/{p-f9991c35.entry.js → p-e19f4b3b.entry.js} +1 -1
  252. package/dist/lime-crm-building-blocks/{p-46aec244.entry.js → p-eb95767a.entry.js} +1 -1
  253. package/dist/lime-crm-building-blocks/{p-ddc4ede8.entry.js → p-ec9b5e30.entry.js} +1 -1
  254. package/dist/lime-crm-building-blocks/{p-95ba7a84.entry.js → p-f49250f3.entry.js} +1 -1
  255. package/dist/types/components/condition-tree/condition-tree-editor/condition-tree-editor.d.ts +20 -3
  256. package/dist/types/components/condition-tree/condition-tree-nodes.d.ts +11 -4
  257. package/dist/types/components/condition-tree/condition-tree-view-context.d.ts +3 -2
  258. package/dist/types/components/condition-tree/condition-tree.d.ts +41 -8
  259. package/dist/types/components/condition-tree/condition-tree.types.d.ts +21 -2
  260. package/dist/types/components/rule-editor/condition-tree-adapter.d.ts +67 -0
  261. package/dist/types/components/rule-editor/rule-condition-chip/rule-condition-chip.d.ts +82 -0
  262. package/dist/types/components/rule-editor/rule-condition-editor/rule-condition-editor.d.ts +61 -0
  263. package/dist/types/components/rule-editor/rule-editor.d.ts +105 -47
  264. package/dist/types/components/rule-editor/rule-issues.d.ts +12 -0
  265. package/dist/types/components/rule-editor/view-helpers.d.ts +16 -35
  266. package/dist/types/components.d.ts +304 -79
  267. package/dist/types/stencil-public-runtime.d.ts +61 -55
  268. package/package.json +4 -4
  269. package/dist/cjs/limebb-rule-chip-popover.cjs.entry.js +0 -82
  270. package/dist/collection/components/rule-editor/chip-picker-view.js +0 -148
  271. package/dist/collection/components/rule-editor/group-node-view.js +0 -79
  272. package/dist/collection/components/rule-editor/rule-chip-popover/rule-chip-popover.css +0 -36
  273. package/dist/collection/components/rule-editor/rule-chip-popover/rule-chip-popover.js +0 -344
  274. package/dist/collection/components/rule-editor/rule-editor-views.js +0 -43
  275. package/dist/collection/components/rule-editor/rule-operations.js +0 -145
  276. package/dist/components/limebb-rule-chip-popover.d.ts +0 -11
  277. package/dist/components/limebb-rule-chip-popover.js +0 -1
  278. package/dist/components/rule-chip-popover.js +0 -1
  279. package/dist/esm/limebb-rule-chip-popover.entry.js +0 -80
  280. package/dist/lime-crm-building-blocks/p-1578c8f0.entry.js +0 -1
  281. package/dist/lime-crm-building-blocks/p-229d018c.entry.js +0 -1
  282. package/dist/lime-crm-building-blocks/p-6331f9c5.entry.js +0 -1
  283. package/dist/lime-crm-building-blocks/p-705c53c0.entry.js +0 -1
  284. package/dist/lime-crm-building-blocks/p-84ad9159.entry.js +0 -1
  285. package/dist/lime-crm-building-blocks/p-BxCq236x.js +0 -1
  286. package/dist/lime-crm-building-blocks/p-Css8537O.js +0 -1
  287. package/dist/lime-crm-building-blocks/p-a58c9e1e.entry.js +0 -1
  288. package/dist/lime-crm-building-blocks/p-hsyotUav.js +0 -1
  289. package/dist/types/components/rule-editor/chip-picker-view.d.ts +0 -35
  290. package/dist/types/components/rule-editor/group-node-view.d.ts +0 -11
  291. package/dist/types/components/rule-editor/rule-chip-popover/rule-chip-popover.d.ts +0 -43
  292. package/dist/types/components/rule-editor/rule-editor-views.d.ts +0 -58
  293. package/dist/types/components/rule-editor/rule-operations.d.ts +0 -70
@@ -1,177 +1,106 @@
1
- import { h, r as registerInstance, c as createEvent, H as Host, g as getElement } from './index-COtSgrC5.js';
2
- import { M as Mt, n } from './index.esm-B9D_MXA6.js';
1
+ import { h, r as registerInstance, c as createEvent, H as Host, g as getElement } from './index-BK566_jX.js';
2
+ import { M as Mt, n } from './index.esm-C__n3hug.js';
3
+ import { c as createTranslator } from './condition-tree-translate-ZQptbaXF.js';
4
+ import { c as createRuleConditionTreeAdapter, g as groupOf } from './limebb-rule-condition-editor-DeY758N-.js';
5
+ import './limebb-rule-condition-chip.entry.js';
3
6
  import { r as ruleForMode, b as ruleMode, A as ALWAYS_PRIMITIVE_ID } from './rule-editor-modes-BCglve2Z.js';
4
7
  import { c as createRandomString } from './random-string-C2JP2nLw.js';
8
+ import './web-component-template-D1JpeNy_.js';
5
9
 
6
10
  /**
7
- * Remove the node at `path`.
11
+ * The problems the registry found, gathered by the node each belongs to.
8
12
  *
9
- * Removing the root yields an empty `all`-group so the tree always has
10
- * something to render. Returns `rule` unchanged when `path` does not
11
- * address a removable node.
12
- * @param rule
13
- * @param path
14
- */
15
- function deleteNode(rule, path) {
16
- if (path.length === 0) {
17
- return { type: 'all', rules: [] };
18
- }
19
- const parentPath = path.slice(0, -1);
20
- const key = path.at(-1);
21
- const parent = getNode(rule, parentPath);
22
- if (parent === undefined) {
23
- return rule;
24
- }
25
- if ((parent.type === 'all' || parent.type === 'any') &&
26
- typeof key === 'number') {
27
- const rules = [...parent.rules];
28
- rules.splice(key, 1);
29
- return replaceAt(rule, parentPath, Object.assign(Object.assign({}, parent), { rules }));
30
- }
31
- if (parent.type === 'not' && key === 'rule') {
32
- return replaceAt(rule, parentPath, { type: 'all', rules: [] });
33
- }
34
- return rule;
35
- }
36
- /**
37
- * Replace the `args` of a `ref` node. Returns `rule` unchanged when
38
- * `path` does not resolve to a `ref`.
39
- * @param rule
40
- * @param path
41
- * @param args
42
- */
43
- function updateArgs(rule, path, args) {
44
- const node = getNode(rule, path);
45
- if (node === undefined || node.type !== 'ref') {
46
- return rule;
47
- }
48
- return replaceAt(rule, path, Object.assign(Object.assign({}, node), { args }));
49
- }
50
- /**
51
- * Replace the node at `path` with `replacement`, returning a new rule
52
- * tree. Used by callers that need to apply a non-atomic change as a
53
- * single emit (e.g. updating a group's whole `rules` array at once).
54
- * @param rule
55
- * @param path
56
- * @param replacement
57
- */
58
- function replaceNode(rule, path, replacement) {
59
- return replaceAt(rule, path, replacement);
60
- }
61
- /**
62
- * Wrap `group` in a `not` when `isNegated`, otherwise return it
63
- * unchanged. Used when re-emitting a group whose negation flag should
64
- * be preserved.
65
- * @param group
66
- * @param isNegated
67
- */
68
- function wrapNegated(group, isNegated) {
69
- return isNegated ? { type: 'not', rule: group } : group;
70
- }
71
- /**
72
- * Append `child` to a group's `rules`, returning a new group. The
73
- * input is not mutated.
74
- * @param group
75
- * @param child
13
+ * The registry addresses an issue by path — `['rules', 0, 'rule']` for
14
+ * `root.rules[0].rule` — while the tree asks about one node at a time and never
15
+ * says where it is. Resolving the paths once, here, bridges the two.
16
+ * @param root
17
+ * @param issues
76
18
  */
77
- function appendChild(group, child) {
78
- return Object.assign(Object.assign({}, group), { rules: [...group.rules, child] });
19
+ function collectRuleIssues(root, issues) {
20
+ var _a, _b;
21
+ const owners = displayOwners(root);
22
+ const byNode = new Map();
23
+ for (const issue of issues) {
24
+ const node = nodeAt(root, issue.path);
25
+ // An empty path means the rule as a whole, and one the path cannot
26
+ // reach has nowhere better to go than the root either.
27
+ const owner = (_a = (node && owners.get(node))) !== null && _a !== void 0 ? _a : root;
28
+ byNode.set(owner, [...((_b = byNode.get(owner)) !== null && _b !== void 0 ? _b : []), issue.message]);
29
+ }
30
+ return byNode;
79
31
  }
80
32
  /**
81
- * Structural equality on two paths.
82
- * @param a
83
- * @param b
33
+ * Which node each node is drawn as part of. Most stand for themselves; the
34
+ * exception is everything under a `not`, which is drawn as that `not` — the
35
+ * two are one thing on screen, and only the wrapper has somewhere to show it.
36
+ * @param root
84
37
  */
85
- function pathsEqual(a, b) {
86
- if (a.length !== b.length) {
87
- return false;
88
- }
89
- return a.every((value, index) => value === b[index]);
90
- }
91
- /**
92
- * Filter validation issues to those whose `path` matches `path`.
93
- * @param issues
94
- * @param path
95
- */
96
- function issuesAt(issues, path) {
97
- return issues.filter((issue) => pathsEqual(issue.path, path));
38
+ function displayOwners(root) {
39
+ const owners = new Map();
40
+ const visit = (node, owner) => {
41
+ // A hand-edited rule can hold a `null` where a node should be. It is
42
+ // drawn, as something that cannot be read, so it is walked past here.
43
+ if (typeof node !== 'object' || node === null) {
44
+ return;
45
+ }
46
+ owners.set(node, owner);
47
+ if (node.type === 'not') {
48
+ visit(node.rule, owner);
49
+ return;
50
+ }
51
+ if ((node.type === 'all' || node.type === 'any') &&
52
+ Array.isArray(node.rules)) {
53
+ for (const child of node.rules) {
54
+ visit(child, child);
55
+ }
56
+ }
57
+ };
58
+ visit(root, root);
59
+ return owners;
98
60
  }
99
61
  /**
100
- * Resolve the node at `path` by walking each segment from the root.
101
- * Returns `undefined` when a segment indexes past the end of a
102
- * combinator's `rules`, so callers must guard the result.
103
- * @param rule
62
+ * The node a validation path addresses, or nothing when it addresses none.
63
+ *
64
+ * Walked one hop at a time because the two the registry emits have different
65
+ * shapes: into a negation is the single segment `rule`, into the *n*-th child
66
+ * of a group is the pair `rules`, *n*. Anything else is not a path this rule
67
+ * can be walked by.
68
+ * @param root
104
69
  * @param path
105
70
  */
106
- function getNode(rule, path) {
107
- let node = rule;
108
- for (const key of path) {
109
- if (node === undefined) {
71
+ function nodeAt(root, path) {
72
+ let node = root;
73
+ let index = 0;
74
+ while (index < path.length) {
75
+ // The same `null` can be standing one hop short of the path's end,
76
+ // with no hop to take from it. Asked for `type`, it would throw.
77
+ if (node === null || typeof node !== 'object') {
110
78
  return undefined;
111
79
  }
112
- node = stepIntoNode(node, key);
113
- }
114
- return node;
115
- }
116
- function replaceAt(root, path, replacement) {
117
- if (path.length === 0) {
118
- return replacement;
119
- }
120
- const [head, ...tail] = path;
121
- if (root.type === 'not' && head === 'rule') {
122
- return Object.assign(Object.assign({}, root), { rule: replaceAt(root.rule, tail, replacement) });
123
- }
124
- if ((root.type === 'all' || root.type === 'any') && head === 'rules') {
125
- if (tail.length === 0) {
126
- return replacement;
80
+ const key = path[index];
81
+ if (key === 'rule' && node.type === 'not') {
82
+ node = node.rule;
83
+ index += 1;
84
+ continue;
127
85
  }
128
- const [index, ...rest] = tail;
129
- if (typeof index !== 'number') {
130
- return root;
86
+ const child = path[index + 1];
87
+ if (key === 'rules' &&
88
+ (node.type === 'all' || node.type === 'any') &&
89
+ Array.isArray(node.rules) &&
90
+ typeof child === 'number') {
91
+ node = node.rules[child];
92
+ index += 2;
93
+ continue;
131
94
  }
132
- const rules = [...root.rules];
133
- rules[index] = replaceAt(rules[index], rest, replacement);
134
- return Object.assign(Object.assign({}, root), { rules });
135
- }
136
- return root;
137
- }
138
- function stepIntoNode(node, key) {
139
- if (node.type === 'not' && key === 'rule') {
140
- return node.rule;
141
- }
142
- if ((node.type === 'all' || node.type === 'any') && key === 'rules') {
143
- return node;
144
- }
145
- if ((node.type === 'all' || node.type === 'any') &&
146
- typeof key === 'number') {
147
- return node.rules[key];
95
+ return undefined;
148
96
  }
149
97
  return node;
150
98
  }
151
99
 
152
- const UNKNOWN_ICON = {
153
- name: 'question',
154
- color: 'rgb(var(--color-glaucous-light))',
155
- };
156
- /**
157
- * Class map for a rule-node container. Adds the type modifier and an
158
- * `invalid` flag when validation issues attach to this node.
159
- * @param type
160
- * @param issues
161
- */
162
- function nodeClasses(type, issues) {
163
- return {
164
- 'rule-node': true,
165
- [`rule-node--${type}`]: true,
166
- 'rule-node--invalid': issues.length > 0,
167
- };
168
- }
169
100
  /**
170
- * Build the searchable list shown by the primitive pickers. Cached on
171
- * the render context so it isn't recomputed per node. `meta.title` and
172
- * `meta.description` are passed through the translator so callers can
173
- * register translation keys as metadata — plain strings travel through
174
- * unchanged.
101
+ * The primitives the add button offers. Titles and descriptions go through the
102
+ * translator, so a caller can register translation keys as metadata — plain
103
+ * strings travel through unchanged.
175
104
  * @param metadataById
176
105
  * @param translator
177
106
  */
@@ -179,7 +108,7 @@ function createPrimitiveOptions(metadataById, translator) {
179
108
  return [...metadataById.values()]
180
109
  .map((meta) => ({
181
110
  value: meta.id,
182
- text: meta.title ? translator.get(meta.title) : meta.id,
111
+ text: primitiveTitle(meta, translator),
183
112
  secondaryText: meta.description
184
113
  ? translator.get(meta.description)
185
114
  : '',
@@ -187,6 +116,15 @@ function createPrimitiveOptions(metadataById, translator) {
187
116
  }))
188
117
  .sort((a, b) => a.text.localeCompare(b.text));
189
118
  }
119
+ /**
120
+ * How a primitive reads: its translated title, or its bare id when it has none
121
+ * to give. The add menu and the chip both read it from here, so they agree.
122
+ * @param meta
123
+ * @param translator
124
+ */
125
+ function primitiveTitle(meta, translator) {
126
+ return meta.title ? translator.get(meta.title) : meta.id;
127
+ }
190
128
  /**
191
129
  * Whether every context key a primitive reads is in the reachable set.
192
130
  * A primitive that reads nothing is always covered.
@@ -197,23 +135,9 @@ function primitiveReadsCovered(meta, reachable) {
197
135
  return meta.reads.every((key) => reachable.has(key));
198
136
  }
199
137
  /**
200
- * Find the picker item for `id`, falling back to a placeholder marked
201
- * with the unknown-primitive icon when the id isn't in the registry.
202
- * @param allItems
203
- * @param id
204
- */
205
- function resolveItem(allItems, id) {
206
- var _a;
207
- return ((_a = allItems.find((item) => item.value === id)) !== null && _a !== void 0 ? _a : {
208
- value: id,
209
- text: id,
210
- icon: UNKNOWN_ICON,
211
- });
212
- }
213
- /**
214
- * Build a picker `searcher` bound to a fixed item list. Both the ref
215
- * picker and the chip picker want exactly this — wrapping
216
- * {@link searchItems} in a promise — and deserve to share it.
138
+ * Narrow the primitives as the user types, for the search field at the top of
139
+ * the add menu. Every word has to match somewhere — title, description or id —
140
+ * so that "deal won" finds "Won deals".
217
141
  * @param items
218
142
  */
219
143
  function createSearcher(items) {
@@ -228,273 +152,12 @@ function searchItems(allItems, query) {
228
152
  var _a, _b;
229
153
  return item.text.toLocaleLowerCase().includes(part) ||
230
154
  ((_a = item.secondaryText) === null || _a === void 0 ? void 0 : _a.toLocaleLowerCase().includes(part)) ||
231
- ((_b = item.value) === null || _b === void 0 ? void 0 : _b.toLocaleLowerCase().includes(part));
155
+ String((_b = item.value) !== null && _b !== void 0 ? _b : '')
156
+ .toLocaleLowerCase()
157
+ .includes(part);
232
158
  }));
233
159
  }
234
160
 
235
- const CHIP_TOKEN_PREFIX = '__chip__:';
236
- function chipTokenFor(index) {
237
- return `${CHIP_TOKEN_PREFIX}${index}`;
238
- }
239
- // The address of a chip inside a group is the group's path followed
240
- // by a two-segment suffix `['rules', index]`. Build and parse via
241
- // these helpers so both sides stay in sync.
242
- const CHIP_PATH_SUFFIX_LENGTH = 2;
243
- /**
244
- * Address of the chip at `index` inside a group: the group's path plus
245
- * the `['rules', index]` suffix. Built here so the picker and popover
246
- * address chips the same way.
247
- * @param groupPath
248
- * @param index
249
- */
250
- function buildChipPath(groupPath, index) {
251
- return [...groupPath, 'rules', index];
252
- }
253
- function parentGroupPath(chipPath) {
254
- return chipPath.slice(0, -CHIP_PATH_SUFFIX_LENGTH);
255
- }
256
- /**
257
- * If `node` is a `ref` or a `not(ref)`, return the underlying ref
258
- * together with its negation flag. Used to decide whether a child of a
259
- * group renders as a chip (this returns non-null) or as a nested box
260
- * (this returns null).
261
- * @param node
262
- */
263
- function extractRef(node) {
264
- if (node.type === 'ref') {
265
- return { ref: node, isNegated: false };
266
- }
267
- if (node.type === 'not' && node.rule.type === 'ref') {
268
- return { ref: node.rule, isNegated: true };
269
- }
270
- return null;
271
- }
272
- const GroupChipsView = ({ groupNode, groupPath, chipChildren, ctx }) => {
273
- const chipsByToken = new Map();
274
- const value = chipChildren.map((entry) => {
275
- const token = chipTokenFor(entry.index);
276
- chipsByToken.set(token, entry);
277
- const base = resolveItem(ctx.allPrimitiveOptions, entry.ref.id);
278
- return Object.assign(Object.assign({}, base), { text: entry.isNegated ? `¬ ${base.text}` : base.text, value: token });
279
- });
280
- const searcher = createSearcher(ctx.availablePrimitiveOptions);
281
- const openChip = findOpenChip(chipChildren, groupPath, ctx.openChipPath);
282
- const delimiter = groupNode.type === 'all' ? '&' : '|';
283
- return (h("limel-popover", { class: "rule-node__chips-popover", open: openChip !== null, openDirection: "bottom-start", onClose: ctx.onPopoverClose }, h("div", { slot: "trigger", class: "rule-node__chips" }, h("limel-picker", { multiple: true, label: ctx.translator.get('webclient.rule-editor.rules-label'), value: value, searcher: searcher, delimiter: delimiter, readonly: ctx.readonly, disabled: ctx.disabled, onChange: chipsChangeHandler(groupNode, groupPath, chipsByToken, ctx), onInteract: chipInteractHandler(groupPath, chipsByToken, ctx) })), h("div", null, renderChipPopoverContent(openChip, groupPath, ctx))));
284
- };
285
- function renderChipPopoverContent(openChip, groupPath, ctx) {
286
- if (!openChip) {
287
- return null;
288
- }
289
- const openChipPath = buildChipPath(groupPath, openChip.index);
290
- return (h("limebb-rule-chip-popover", { platform: ctx.platform, context: ctx.context, refNode: openChip.ref, isNegated: openChip.isNegated, metadata: ctx.metadataById.get(openChip.ref.id), readonly: ctx.readonly, disabled: ctx.disabled, isMutable: ctx.isMutable, focusOnOpen: ctx.focusOnOpen, onNegate: chipNegateHandler(openChip, openChipPath, ctx), onUpdateArgs: chipArgsHandler(openChip, openChipPath, ctx), onDeleteChip: chipDeleteHandler(openChipPath, ctx) }));
291
- }
292
- const chipsChangeHandler = (groupNode, groupPath, chipsByToken, ctx) => (event) => {
293
- event.stopPropagation();
294
- const items = Array.isArray(event.detail) ? event.detail : [];
295
- const openIndex = openChipIndexInGroup(groupPath, ctx.openChipPath);
296
- if (openIndex !== null) {
297
- const openToken = chipTokenFor(openIndex);
298
- if (items.every((item) => item.value !== openToken)) {
299
- ctx.onPopoverClose();
300
- }
301
- }
302
- const newChipNodes = items.flatMap((item) => {
303
- if (typeof item.value !== 'string') {
304
- return [];
305
- }
306
- const existing = chipsByToken.get(item.value);
307
- if (existing) {
308
- return [
309
- existing.isNegated
310
- ? { type: 'not', rule: existing.ref }
311
- : existing.ref,
312
- ];
313
- }
314
- return [{ type: 'ref', id: item.value }];
315
- });
316
- const nestedGroupRules = groupNode.rules.filter((rule) => extractRef(rule) === null);
317
- ctx.onReplaceNode(groupPath, Object.assign(Object.assign({}, groupNode), { rules: [...newChipNodes, ...nestedGroupRules] }));
318
- };
319
- const chipInteractHandler = (groupPath, chipsByToken, ctx) => (event) => {
320
- event.stopPropagation();
321
- const token = interactedToken(event.detail);
322
- const chip = token ? chipsByToken.get(token) : undefined;
323
- if (!chip) {
324
- return;
325
- }
326
- ctx.onChipInteract([...groupPath, 'rules', chip.index]);
327
- };
328
- const chipNegateHandler = (openChip, openChipPath, ctx) => (event) => {
329
- event.stopPropagation();
330
- const next = event.detail
331
- ? { type: 'not', rule: openChip.ref }
332
- : openChip.ref;
333
- ctx.onReplaceNode(openChipPath, next);
334
- };
335
- const chipArgsHandler = (openChip, openChipPath, ctx) => (event) => {
336
- event.stopPropagation();
337
- const argsPath = openChip.isNegated
338
- ? [...openChipPath, 'rule']
339
- : openChipPath;
340
- ctx.onUpdateArgs(argsPath, event.detail);
341
- };
342
- const chipDeleteHandler = (openChipPath, ctx) => () => {
343
- ctx.onDelete(openChipPath);
344
- };
345
- /**
346
- * `limel-picker` declares its `interact` event as
347
- * `EventEmitter<ListItem<PickerValue>>` but the runtime actually emits
348
- * the chip's value (a string) — see picker.tsx's handleInteract. This
349
- * unwraps either shape into the string token.
350
- * @param detail
351
- */
352
- function interactedToken(detail) {
353
- if (typeof detail === 'string') {
354
- return detail;
355
- }
356
- if (detail && typeof detail === 'object' && 'value' in detail) {
357
- const value = detail.value;
358
- return typeof value === 'string' ? value : undefined;
359
- }
360
- return undefined;
361
- }
362
- function findOpenChip(chipChildren, groupPath, openChipPath) {
363
- var _a;
364
- const index = openChipIndexInGroup(groupPath, openChipPath);
365
- if (index === null) {
366
- return null;
367
- }
368
- return (_a = chipChildren.find((entry) => entry.index === index)) !== null && _a !== void 0 ? _a : null;
369
- }
370
- function openChipIndexInGroup(groupPath, openChipPath) {
371
- if (!openChipPath || openChipPath.length < CHIP_PATH_SUFFIX_LENGTH) {
372
- return null;
373
- }
374
- if (!pathsEqual(parentGroupPath(openChipPath), groupPath)) {
375
- return null;
376
- }
377
- const lastKey = openChipPath.at(-1);
378
- return typeof lastKey === 'number' ? lastKey : null;
379
- }
380
-
381
- const GroupNodeView = ({ node, path, isNegated, ctx }) => {
382
- const groupBasePath = isNegated ? [...path, 'rule'] : path;
383
- const issuesHere = isNegated
384
- ? [
385
- ...issuesAt(ctx.issues, path),
386
- ...issuesAt(ctx.issues, groupBasePath),
387
- ]
388
- : issuesAt(ctx.issues, path);
389
- const chipChildren = [];
390
- const nestedGroups = [];
391
- for (const [index, child] of node.rules.entries()) {
392
- const extracted = extractRef(child);
393
- if (extracted) {
394
- chipChildren.push(Object.assign(Object.assign({}, extracted), { index }));
395
- }
396
- else {
397
- nestedGroups.push({ child, index });
398
- }
399
- }
400
- const options = combinatorOptions(ctx);
401
- const selectedCombinator = options.find((option) => option.value === node.type);
402
- return (h("div", { class: nodeClasses(node.type, issuesHere), key: pathKey(path) }, h("div", { class: "rule-node__header" }, h("limel-select", { class: "rule-node__combinator-select", value: selectedCombinator, options: options, disabled: !ctx.isMutable, onChange: combinatorChangeHandler(node, path, isNegated, ctx) }), renderMutableGroupActions(node, path, isNegated, ctx)), h("div", { class: "rule-node__body" }, h(IssuesView, { issues: issuesHere }), h(GroupChipsView, { groupNode: node, groupPath: groupBasePath, chipChildren: chipChildren, ctx: ctx }), renderNestedGroups(nestedGroups, groupBasePath, ctx), renderAddGroupFooter(node, path, isNegated, ctx))));
403
- };
404
- function pathKey(path) {
405
- return path.length === 0 ? 'root' : path.join('/');
406
- }
407
- function combinatorOptions(ctx) {
408
- return [
409
- {
410
- value: 'all',
411
- text: ctx.translator.get('webclient.combinator.all'),
412
- },
413
- {
414
- value: 'any',
415
- text: ctx.translator.get('webclient.combinator.any'),
416
- },
417
- ];
418
- }
419
- function renderMutableGroupActions(node, path, isNegated, ctx) {
420
- if (!ctx.isMutable) {
421
- return null;
422
- }
423
- return (h("div", { class: "rule-node__header-actions" }, h("limel-switch", { class: "rule-node__negate-switch", label: ctx.translator.get('webclient.negate'), value: isNegated, onChange: negateGroupHandler(node, path, ctx) }), h("limel-icon-button", { icon: "trash", label: ctx.translator.get('webclient.rule-editor.delete'), onClick: () => ctx.onDelete(path) })));
424
- }
425
- function renderNestedGroups(nestedGroups, groupBasePath, ctx) {
426
- if (nestedGroups.length === 0) {
427
- return null;
428
- }
429
- return (h("div", { class: "rule-node__children" }, nestedGroups.map((entry) => (h(RuleNodeView, { node: entry.child, path: [...groupBasePath, 'rules', entry.index], ctx: ctx })))));
430
- }
431
- function renderAddGroupFooter(node, path, isNegated, ctx) {
432
- if (!ctx.isMutable) {
433
- return null;
434
- }
435
- return (h("div", { class: "rule-node__footer" }, h("limel-button", { icon: "add", label: ctx.translator.get('webclient.rule-editor.add-nested-group'), onClick: addGroupHandler(node, path, isNegated, ctx) })));
436
- }
437
- function replaceGroup(nextGroup, path, isNegated, ctx) {
438
- ctx.onReplaceNode(path, wrapNegated(nextGroup, isNegated));
439
- }
440
- const combinatorChangeHandler = (node, path, isNegated, ctx) => (event) => {
441
- event.stopPropagation();
442
- const newType = event.detail.value;
443
- if (newType !== node.type) {
444
- replaceGroup(Object.assign(Object.assign({}, node), { type: newType }), path, isNegated, ctx);
445
- }
446
- };
447
- const addGroupHandler = (node, path, isNegated, ctx) => () => {
448
- const expanded = appendChild(node, { type: 'all', rules: [] });
449
- replaceGroup(expanded, path, isNegated, ctx);
450
- };
451
- const negateGroupHandler = (node, path, ctx) => (event) => {
452
- event.stopPropagation();
453
- ctx.onReplaceNode(path, wrapNegated(node, event.detail));
454
- };
455
-
456
- /**
457
- * Render a `Rule` node and recurse into its children.
458
- * @param props
459
- */
460
- const RuleNodeView = (props) => {
461
- const { node, path, ctx } = props;
462
- const group = extractGroup(node);
463
- if (!group) {
464
- return null;
465
- }
466
- return (h(GroupNodeView, { node: group.node, path: path, isNegated: group.isNegated, ctx: ctx }));
467
- };
468
- const isNegatedGroup = (node) => node.type === 'not' &&
469
- (node.rule.type === 'all' || node.rule.type === 'any');
470
- /**
471
- * If `node` is a group (`all`/`any`) or a negated group, return the
472
- * inner group together with its negation flag. Otherwise `null`.
473
- * Used by the dispatcher and by the editor's input normalization.
474
- * @param node
475
- */
476
- function extractGroup(node) {
477
- if (node.type === 'all' || node.type === 'any') {
478
- return { node, isNegated: false };
479
- }
480
- if (isNegatedGroup(node)) {
481
- return { node: node.rule, isNegated: true };
482
- }
483
- if (node.type === 'not') {
484
- const inner = extractGroup(node.rule);
485
- if (inner) {
486
- return { node: inner.node, isNegated: !inner.isNegated };
487
- }
488
- }
489
- return null;
490
- }
491
- const IssuesView = ({ issues }) => {
492
- if (issues.length === 0) {
493
- return null;
494
- }
495
- return issues.map((issue) => (h("div", { class: "rule-node__issue" }, issue.message)));
496
- };
497
-
498
161
  /**
499
162
  * The shared string each mode is labelled from. Adverbs answering "when
500
163
  * does this apply?", so they carry no rule-editor namespace and are
@@ -527,7 +190,7 @@ const RuleModeView = (props) => {
527
190
  } }));
528
191
  };
529
192
 
530
- const ruleEditorCss = () => `@charset "UTF-8";:host{display:block;position:relative}.limel-notched-outline{--limel-notched-outline-background-color:transparent;padding:0.5rem 0 0 0}limel-notched-outline:not([invalid]:not([invalid=false])) .limel-notched-outline{--limel-notched-outline-border-color:transparent}.rule-editor--disabled{opacity:0.6;pointer-events:none}.rule-node{border:1px solid rgb(var(--contrast-500));border-radius:0.5rem;margin:0.25rem 0;overflow:hidden;background-color:rgb(var(--contrast-100))}.rule-node--invalid{border-color:rgb(var(--color-red-default))}.rule-node--all>.rule-node__header,.rule-node--any>.rule-node__header{background-color:rgb(var(--contrast-300));padding:0.375rem 0.75rem;border-bottom:1px solid rgb(var(--contrast-500))}.rule-node__header{display:flex;align-items:center;gap:0.5rem}.rule-node__header-actions{margin-left:auto;display:grid;grid-auto-flow:column;grid-gap:0.5rem;padding:0.125rem;align-items:center}.rule-node__combinator-select{min-width:6rem}.rule-node__body{padding:0.75rem;display:flex;flex-direction:column;gap:0.75rem}limel-popover.rule-node__chips-popover{display:contents;--popover-surface-width:min(calc(100vw - 4rem), 50rem)}.rule-node__chips{display:block;width:100%}.rule-node__chips limel-picker{display:block;width:100%}.rule-node__children{display:flex;flex-direction:column;gap:0.5rem}.rule-node__footer{display:flex;justify-content:center}.rule-node__issue{color:rgb(var(--color-red-default));font-size:0.875rem}:host(limebb-rule-editor:focus),:host(limebb-rule-editor:focus-visible),:host(limebb-rule-editor:focus-within){--limel-h-l-grid-template-rows-transition-speed:0.46s;--limel-h-l-grid-template-rows:1fr}:host(limebb-rule-editor){--limel-h-l-grid-template-rows-transition-speed:0.3s;--limel-h-l-grid-template-rows:0fr}:host(limebb-rule-editor:focus) limel-helper-line,:host(limebb-rule-editor:focus-visible) limel-helper-line,:host(limebb-rule-editor:focus-within) limel-helper-line,:host(limebb-rule-editor:hover) limel-helper-line{will-change:grid-template-rows}limel-help{position:absolute;top:calc(var(--form-row-gap, 1rem) * -0.5);left:calc(var(--form-column-gap, 1rem) * -0.5);z-index:1}`;
193
+ const ruleEditorCss = () => `@charset "UTF-8";:host{display:block;position:relative}.limel-notched-outline{--limel-notched-outline-background-color:transparent;padding:0.5rem 0 0 0}limel-notched-outline:not([invalid]:not([invalid=false])) .limel-notched-outline{--limel-notched-outline-border-color:transparent}.rule-editor--disabled{opacity:0.6;pointer-events:none}limebb-condition-tree{--popover-surface-width:min(calc(100vw - 4rem), 50rem)}:host(limebb-rule-editor:focus),:host(limebb-rule-editor:focus-visible),:host(limebb-rule-editor:focus-within){--limel-h-l-grid-template-rows-transition-speed:0.46s;--limel-h-l-grid-template-rows:1fr}:host(limebb-rule-editor){--limel-h-l-grid-template-rows-transition-speed:0.3s;--limel-h-l-grid-template-rows:0fr}:host(limebb-rule-editor:focus) limel-helper-line,:host(limebb-rule-editor:focus-visible) limel-helper-line,:host(limebb-rule-editor:focus-within) limel-helper-line,:host(limebb-rule-editor:hover) limel-helper-line{will-change:grid-template-rows}limel-help{position:absolute;top:calc(var(--form-row-gap, 1rem) * -0.5);left:calc(var(--form-column-gap, 1rem) * -0.5);z-index:1}`;
531
194
 
532
195
  var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
533
196
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -543,8 +206,6 @@ const RuleEditor = class {
543
206
  constructor(hostRef) {
544
207
  registerInstance(this, hostRef);
545
208
  this.change = createEvent(this, "change");
546
- this.issues = [];
547
- this.openChipPath = null;
548
209
  /**
549
210
  * The mode the author last clicked, held until `value` reads back
550
211
  * as it.
@@ -555,10 +216,27 @@ const RuleEditor = class {
555
216
  * what the author just clicked.
556
217
  */
557
218
  this.modeOverride = null;
558
- this.focusOnOpen = false;
559
219
  this.metadataById = new Map();
560
- this.allPrimitiveOptions = [];
220
+ /**
221
+ * How each registered primitive reads, by id. Translated once here
222
+ * because the platform's translator parses the message afresh on
223
+ * every call, and the adapter would otherwise do that per condition,
224
+ * several times over, on every edit.
225
+ */
226
+ this.primitiveTitles = new Map();
561
227
  this.availablePrimitiveOptions = [];
228
+ this.searcher = createSearcher([]);
229
+ /**
230
+ * What validation found, by the node it belongs to.
231
+ */
232
+ this.issuesByNode = new Map();
233
+ /**
234
+ * The conditions added here rather than loaded, which a `RuleRef`
235
+ * does not record. The adapter reads it to decide which condition
236
+ * opens itself. Replaced when the tree goes, since a condition still
237
+ * listed when the author comes back would open itself again.
238
+ */
239
+ this.created = new WeakSet();
562
240
  this.reachableSlots = new Set();
563
241
  this.normalizedCache = null;
564
242
  this.hasLoaded = false;
@@ -572,6 +250,9 @@ const RuleEditor = class {
572
250
  * conditions are gone, rather than handing back the ones they
573
251
  * removed. With nothing stashed, `conditional` emits no rule, which
574
252
  * is how the editor already says "the author cleared this".
253
+ *
254
+ * What is not stashed is which conditions had just been added, so
255
+ * that none of them opens itself again on the way back.
575
256
  * @param mode
576
257
  */
577
258
  this.handleModeChange = (mode) => {
@@ -579,32 +260,28 @@ const RuleEditor = class {
579
260
  if (this.currentMode === 'conditional') {
580
261
  this.stashedTree = this.value;
581
262
  }
263
+ if (mode !== 'conditional') {
264
+ this.created = new WeakSet();
265
+ }
582
266
  this.modeOverride = mode;
583
267
  this.change.emit((_a = ruleForMode(mode)) !== null && _a !== void 0 ? _a : this.stashedTree);
584
268
  };
585
- this.handleDelete = (path) => {
586
- this.openChipPath = null;
587
- this.focusOnOpen = false;
588
- this.emitIfChanged(deleteNode(this.normalizedValue, path));
589
- };
590
- this.handleUpdateArgs = (path, args) => {
591
- this.emitIfChanged(updateArgs(this.normalizedValue, path, args));
592
- };
593
- this.handleReplaceNode = (path, replacement) => {
594
- const autoOpenPath = this.autoOpenPathFor(path, replacement);
595
- this.focusOnOpen = autoOpenPath !== null;
596
- if (autoOpenPath !== null) {
597
- this.openChipPath = autoOpenPath;
598
- }
599
- this.emitIfChanged(replaceNode(this.normalizedValue, path, replacement));
600
- };
601
- this.handleChipInteract = (path) => {
602
- this.openChipPath = path;
603
- this.focusOnOpen = false;
604
- };
605
- this.handlePopoverClose = () => {
606
- this.openChipPath = null;
607
- this.focusOnOpen = false;
269
+ /**
270
+ * Take in a tree the author changed. The tree reports `null` when
271
+ * the last condition goes, which for a rule means there is none —
272
+ * the same thing an emptied editor has always emitted.
273
+ *
274
+ * Editing the tree is only possible in `conditional`, so the edit
275
+ * itself records the mode: without this, a tree emptied to nothing
276
+ * would read back as the first offered mode and rewrite the setting
277
+ * under the author.
278
+ * @param event
279
+ */
280
+ this.handleTreeChange = (event) => {
281
+ var _a;
282
+ event.stopPropagation();
283
+ this.modeOverride = 'conditional';
284
+ this.change.emit((_a = event.detail) !== null && _a !== void 0 ? _a : undefined);
608
285
  };
609
286
  }
610
287
  componentWillLoad() {
@@ -615,6 +292,7 @@ const RuleEditor = class {
615
292
  this.indexMetadata();
616
293
  this.rebuildAvailableOptions();
617
294
  this.runValidation();
295
+ this.rebuildAdapter();
618
296
  }
619
297
  /**
620
298
  * Re-walk reachable slots when the editor is re-attached to the DOM,
@@ -629,6 +307,7 @@ const RuleEditor = class {
629
307
  this.computeReachableSlots();
630
308
  this.rebuildAvailableOptions();
631
309
  this.runValidation();
310
+ this.rebuildAdapter();
632
311
  }
633
312
  componentDidLoad() {
634
313
  this.hasLoaded = true;
@@ -645,7 +324,7 @@ const RuleEditor = class {
645
324
  this.rebuildFromRegistries();
646
325
  }
647
326
  render() {
648
- return (h(Host, { key: '98380987c6687ce58cca822ea2fd2f106787897b' }, this.renderHelp(), h("limel-notched-outline", { key: 'bbf2d6da192524c4cf8e2e616ca2dab6206a43a6', label: this.label, labelId: this.labelId, required: this.required, readonly: this.readonly, disabled: this.disabled, invalid: this.invalid, hasValue: true, hasFloatingLabel: true }, h("div", { key: '9e7043bae1b8f60f9ead7b0ed1874dae32944e9e', slot: "content", class: {
327
+ return (h(Host, { key: 'eb89af0c748f82d8e60b1209c4fcce3475ac2934' }, this.renderHelp(), h("limel-notched-outline", { key: '9d454ec87268aa9ff07776d38882365a544a64e7', label: this.label, labelId: this.labelId, required: this.required, readonly: this.readonly, disabled: this.disabled, invalid: this.invalid, hasValue: true, hasFloatingLabel: true }, h("div", { key: '57293e75c641d4909a4d2e1d3a3d2c20a60a4512', slot: "content", class: {
649
328
  'rule-editor': true,
650
329
  'rule-editor--disabled': this.disabled,
651
330
  }, role: "group", "aria-labelledby": this.label ? this.labelId : undefined, "aria-describedby": this.helperText ? this.helperTextId : undefined }, this.renderModes(), this.renderTree())), this.renderHelperLine()));
@@ -662,13 +341,18 @@ const RuleEditor = class {
662
341
  * The condition tree, shown unless a mode other than `conditional`
663
342
  * is selected — an unconditional rule has no conditions to show.
664
343
  * A caller offering no modes at all gets the tree, as before.
344
+ *
345
+ * An unset rule is handed over as unset, rather than as an empty
346
+ * group: the tree draws the group an author's first condition will
347
+ * land in, and drawing one here would only be a second opinion on
348
+ * what that looks like.
665
349
  */
666
350
  renderTree() {
667
351
  const mode = this.currentMode;
668
352
  if (mode && mode !== 'conditional') {
669
353
  return null;
670
354
  }
671
- return (h(RuleNodeView, { node: this.normalizedValue, path: [], ctx: this.createRenderContext() }));
355
+ return (h("limebb-condition-tree", { platform: this.platform, context: this.effectiveContext, value: this.normalizedValue, adapter: this.adapter, addOptions: this.availablePrimitiveOptions, addSearcher: this.searcher, readonly: this.readonly, disabled: this.disabled, onChange: this.handleTreeChange }));
672
356
  }
673
357
  renderHelperLine() {
674
358
  if (!this.helperText) {
@@ -682,27 +366,6 @@ const RuleEditor = class {
682
366
  }
683
367
  return h("limel-help", { value: this.help });
684
368
  }
685
- createRenderContext() {
686
- return {
687
- issues: this.issues,
688
- metadataById: this.metadataById,
689
- allPrimitiveOptions: this.allPrimitiveOptions,
690
- availablePrimitiveOptions: this.availablePrimitiveOptions,
691
- isMutable: !this.readonly && !this.disabled,
692
- readonly: this.readonly,
693
- disabled: this.disabled,
694
- platform: this.platform,
695
- context: this.effectiveContext,
696
- translator: this.translator,
697
- openChipPath: this.openChipPath,
698
- focusOnOpen: this.focusOnOpen,
699
- onDelete: this.handleDelete,
700
- onUpdateArgs: this.handleUpdateArgs,
701
- onReplaceNode: this.handleReplaceNode,
702
- onChipInteract: this.handleChipInteract,
703
- onPopoverClose: this.handlePopoverClose,
704
- };
705
- }
706
369
  /**
707
370
  * The mode the editor is in, or `undefined` when it offers none —
708
371
  * then it is the plain tree editor it was before modes existed.
@@ -736,51 +399,14 @@ const RuleEditor = class {
736
399
  this.modeOverride = null;
737
400
  }
738
401
  }
739
- /**
740
- * Path of a newly added chip whose primitive declares a config
741
- * component, so its args editor opens immediately on add. The new
742
- * ref is found by object identity — the picker reuses existing ref
743
- * objects and only the added chip is a fresh node — so adding a
744
- * second instance of a primitive already in the group is still
745
- * detected. Returns `null` unless exactly one ref was added and it
746
- * has an editor, leaving the open chip untouched for removals,
747
- * multi-adds, and negation.
748
- * @param path
749
- * @param replacement
750
- */
751
- autoOpenPathFor(path, replacement) {
752
- var _a;
753
- if (replacement.type !== 'all' && replacement.type !== 'any') {
754
- return null;
755
- }
756
- const previous = getNode(this.normalizedValue, path);
757
- if (previous === undefined ||
758
- (previous.type !== 'all' && previous.type !== 'any')) {
759
- return null;
760
- }
761
- const previousRefs = new Set(previous.rules
762
- .map((rule) => { var _a; return (_a = extractRef(rule)) === null || _a === void 0 ? void 0 : _a.ref; })
763
- .filter((ref) => ref !== undefined));
764
- const added = replacement.rules
765
- .map((rule, index) => {
766
- var _a;
767
- const ref = (_a = extractRef(rule)) === null || _a === void 0 ? void 0 : _a.ref;
768
- return ref && !previousRefs.has(ref) ? { ref, index } : null;
769
- })
770
- .filter((entry) => entry !== null);
771
- if (added.length !== 1) {
772
- return null;
773
- }
774
- const [{ ref, index }] = added;
775
- return ((_a = this.metadataById.get(ref.id)) === null || _a === void 0 ? void 0 : _a.configComponent)
776
- ? buildChipPath(path, index)
777
- : null;
778
- }
779
402
  indexMetadata() {
780
403
  var _a, _b, _c;
781
404
  const metadata = (_c = (_b = (_a = this.ruleRegistry).listMetadata) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : [];
782
405
  this.metadataById = new Map(metadata.map((item) => [item.id, item]));
783
- this.allPrimitiveOptions = createPrimitiveOptions(this.metadataById, this.translator);
406
+ this.primitiveTitles = new Map(metadata.map((item) => [
407
+ item.id,
408
+ primitiveTitle(item, this.translator),
409
+ ]));
784
410
  }
785
411
  /**
786
412
  * Recompute the cached add-palette options. The inputs — indexed
@@ -790,6 +416,7 @@ const RuleEditor = class {
790
416
  */
791
417
  rebuildAvailableOptions() {
792
418
  this.availablePrimitiveOptions = createPrimitiveOptions(this.availablePrimitiveMetadata(), this.translator);
419
+ this.searcher = createSearcher(this.availablePrimitiveOptions);
793
420
  }
794
421
  /**
795
422
  * The subset of registered metadata offered for adding in the
@@ -797,7 +424,7 @@ const RuleEditor = class {
797
424
  * reachable slots are dropped, and so is the always primitive —
798
425
  * it reads nothing, so it is always covered, but an "Always" chip
799
426
  * among a group's conditions says nothing. Filtering only the
800
- * palette — not the full `allPrimitiveOptions` — keeps
427
+ * palette — the adapter still reads the whole index — keeps
801
428
  * already-placed chips resolving to their proper title even when
802
429
  * their read slot isn't reachable.
803
430
  */
@@ -837,45 +464,81 @@ const RuleEditor = class {
837
464
  ]);
838
465
  }
839
466
  runValidation() {
840
- if (!this.value) {
841
- this.issues = [];
842
- return;
843
- }
844
- const result = this.ruleRegistry.validate(this.normalizedValue, [
845
- ...this.reachableSlots,
846
- ]);
847
- this.issues = result.ok ? [] : result.issues;
467
+ this.issuesByNode = this.findIssues();
848
468
  }
849
469
  /**
850
- * Emit an edited tree. Editing the tree is only possible in
851
- * `conditional`, so the edit itself records the mode: an edit that
852
- * empties the tree emits no rule, and without this the empty value
853
- * would read back as the first offered mode.
854
- * @param next
470
+ * What the registry finds wrong with the rule, gathered by the node
471
+ * each problem belongs to. Validation runs against the normalized
472
+ * rule, which is also what the tree is handed, so the paths the
473
+ * registry reports address the very nodes the tree draws.
474
+ *
475
+ * A rule the registry cannot even walk — one hand-edited into a shape
476
+ * that is not a rule — makes it throw rather than report. That is the
477
+ * rule an author most needs the editor for, so the throw is caught
478
+ * and the editor drawn anyway: the tree marks the offending condition
479
+ * unreadable itself, which is the same thing validation would have
480
+ * said and in the place it belongs.
855
481
  */
856
- emitIfChanged(next) {
857
- if (next === this.normalizedValue) {
858
- return;
482
+ findIssues() {
483
+ const root = this.normalizedValue;
484
+ if (!root) {
485
+ return new Map();
486
+ }
487
+ try {
488
+ const result = this.ruleRegistry.validate(root, [
489
+ ...this.reachableSlots,
490
+ ]);
491
+ return result.ok
492
+ ? new Map()
493
+ : collectRuleIssues(root, result.issues);
494
+ }
495
+ catch (error) {
496
+ // The throw names the node it choked on. What the author needs is
497
+ // already in the tree; this is what tells a developer an unreadable
498
+ // rule from a registry that could not answer for a readable one.
499
+ console.warn('rule-editor could not validate the rule', {
500
+ rule: root,
501
+ error,
502
+ });
503
+ return new Map();
859
504
  }
860
- this.modeOverride = 'conditional';
861
- this.change.emit(collapseEmptyRoot(next));
862
505
  }
863
506
  /**
864
- * Wrap any non-group `value` in a top-level `all` so the editor
865
- * always has a group to render against. When `value` is unset,
866
- * returns a fresh empty `all`-group as a render-only placeholder —
867
- * `emitNormalization` will *not* push that placeholder back to the
868
- * caller, so an unset value stays unset until the user edits.
507
+ * Gather what the tree draws from into a new adapter, which is how a
508
+ * change the rule does not carry reaches the screen at all.
509
+ *
510
+ * Not rebuilt for a validation pass: that runs because the rule
511
+ * changed, and the tree is already redrawing for the same reason.
512
+ * Rebuilding there would sync its open state twice for one edit.
513
+ */
514
+ rebuildAdapter() {
515
+ this.adapter = createRuleConditionTreeAdapter({
516
+ metadata: this.metadataById,
517
+ titles: this.primitiveTitles,
518
+ translate: createTranslator(this.platform),
519
+ issues: () => this.issuesByNode,
520
+ created: () => this.created,
521
+ });
522
+ }
523
+ /**
524
+ * Wrap any non-group `value` in a top-level `all`, so that a lone
525
+ * condition has a group to grow in. An unset value stays unset: the
526
+ * tree draws the group a first condition will land in, and
527
+ * `emitNormalization` never pushes that placeholder back to the
528
+ * caller, so "no rule yet" stays distinguishable from "the author
529
+ * cleared this".
869
530
  *
870
531
  * An unconditional mode is the exception: its sentinel is left
871
532
  * exactly as it came in, because wrapping it would rewrite the
872
533
  * shape the mode is read back from.
873
534
  *
874
- * Memoized so callers that read this several times in one handler
875
- * see the same reference — the identity check in `emitIfChanged`
876
- * depends on it.
535
+ * Memoized, so that the tree is handed the same rule on every
536
+ * render and redraws only when the rule itself changes.
877
537
  */
878
538
  get normalizedValue() {
539
+ if (!this.value) {
540
+ return undefined;
541
+ }
879
542
  const mode = this.currentMode;
880
543
  if (this.normalizedCache !== null &&
881
544
  this.normalizedCache.source === this.value &&
@@ -942,35 +605,16 @@ const EMPTY_CONTEXT = Object.freeze({
942
605
  id: null,
943
606
  });
944
607
  function normalize(value, mode) {
945
- if (!value) {
946
- return { type: 'all', rules: [] };
947
- }
948
608
  // Neither sentinel is a group, and wrapping one would rewrite the
949
609
  // very shape the mode is read back from.
950
610
  if (mode === 'always' || mode === 'never') {
951
611
  return value;
952
612
  }
953
- if (extractGroup(value)) {
613
+ if (groupOf(value)) {
954
614
  return value;
955
615
  }
956
616
  return { type: 'all', rules: [value] };
957
617
  }
958
- /**
959
- * Treat an empty root `all`/`any` as "no rule". Mapping that back to
960
- * `undefined` on emit keeps the form contract a clean
961
- * `Rule | undefined` — callers don't have to inspect rule shape to
962
- * tell "user cleared this" from "user has a real rule". A negated
963
- * empty group (`not(all())` etc.) is left intact — vacuous either
964
- * direction, but the user explicitly opted into the wrapper, so we
965
- * don't second-guess.
966
- * @param rule
967
- */
968
- function collapseEmptyRoot(rule) {
969
- if (rule.type !== 'all' && rule.type !== 'any') {
970
- return rule;
971
- }
972
- return rule.rules.length === 0 ? undefined : rule;
973
- }
974
618
  RuleEditor.style = ruleEditorCss();
975
619
 
976
620
  export { RuleEditor as limebb_rule_editor };