@panphora/bevel 0.1.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 (539) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +226 -0
  3. package/bevel-fonts.css +16 -0
  4. package/bevel.css +3075 -0
  5. package/bevel.js +2126 -0
  6. package/dist/react/components/activity-feed.d.ts +89 -0
  7. package/dist/react/components/activity-feed.js +54 -0
  8. package/dist/react/components/alert-dialog.d.ts +14 -0
  9. package/dist/react/components/alert-dialog.js +19 -0
  10. package/dist/react/components/app-shell.d.ts +98 -0
  11. package/dist/react/components/app-shell.js +86 -0
  12. package/dist/react/components/app-toolbar.d.ts +19 -0
  13. package/dist/react/components/app-toolbar.js +22 -0
  14. package/dist/react/components/avatar-group.d.ts +67 -0
  15. package/dist/react/components/avatar-group.js +49 -0
  16. package/dist/react/components/badge-and-pill.d.ts +34 -0
  17. package/dist/react/components/badge-and-pill.js +12 -0
  18. package/dist/react/components/breadcrumbs.d.ts +56 -0
  19. package/dist/react/components/breadcrumbs.js +44 -0
  20. package/dist/react/components/bulk-bar.d.ts +22 -0
  21. package/dist/react/components/bulk-bar.js +59 -0
  22. package/dist/react/components/button.d.ts +15 -0
  23. package/dist/react/components/button.js +20 -0
  24. package/dist/react/components/callout-and-hint.d.ts +17 -0
  25. package/dist/react/components/callout-and-hint.js +8 -0
  26. package/dist/react/components/checkbox.d.ts +5 -0
  27. package/dist/react/components/checkbox.js +13 -0
  28. package/dist/react/components/chip.d.ts +149 -0
  29. package/dist/react/components/chip.js +29 -0
  30. package/dist/react/components/code-block.d.ts +50 -0
  31. package/dist/react/components/code-block.js +77 -0
  32. package/dist/react/components/code-editor.d.ts +122 -0
  33. package/dist/react/components/code-editor.js +695 -0
  34. package/dist/react/components/column-manager.d.ts +28 -0
  35. package/dist/react/components/column-manager.js +85 -0
  36. package/dist/react/components/combobox.d.ts +32 -0
  37. package/dist/react/components/combobox.js +37 -0
  38. package/dist/react/components/data-grid.d.ts +108 -0
  39. package/dist/react/components/data-grid.js +159 -0
  40. package/dist/react/components/data-table.d.ts +2 -0
  41. package/dist/react/components/data-table.js +6 -0
  42. package/dist/react/components/date.d.ts +23 -0
  43. package/dist/react/components/date.js +11 -0
  44. package/dist/react/components/details.d.ts +13 -0
  45. package/dist/react/components/details.js +20 -0
  46. package/dist/react/components/dialog.d.ts +74 -0
  47. package/dist/react/components/dialog.js +31 -0
  48. package/dist/react/components/diff-view.d.ts +73 -0
  49. package/dist/react/components/diff-view.js +68 -0
  50. package/dist/react/components/documents.d.ts +70 -0
  51. package/dist/react/components/documents.js +10 -0
  52. package/dist/react/components/drawer.d.ts +45 -0
  53. package/dist/react/components/drawer.js +35 -0
  54. package/dist/react/components/editable-grid-cells.d.ts +32 -0
  55. package/dist/react/components/editable-grid-cells.js +116 -0
  56. package/dist/react/components/eyebrow-kicker-lede-pull-quote.d.ts +39 -0
  57. package/dist/react/components/eyebrow-kicker-lede-pull-quote.js +12 -0
  58. package/dist/react/components/field.d.ts +32 -0
  59. package/dist/react/components/field.js +27 -0
  60. package/dist/react/components/file-tree.d.ts +75 -0
  61. package/dist/react/components/file-tree.js +247 -0
  62. package/dist/react/components/file.d.ts +43 -0
  63. package/dist/react/components/file.js +73 -0
  64. package/dist/react/components/files-table.d.ts +174 -0
  65. package/dist/react/components/files-table.js +237 -0
  66. package/dist/react/components/filter-builder.d.ts +37 -0
  67. package/dist/react/components/filter-builder.js +109 -0
  68. package/dist/react/components/form-error-summary.d.ts +16 -0
  69. package/dist/react/components/form-error-summary.js +49 -0
  70. package/dist/react/components/icon-button.d.ts +8 -0
  71. package/dist/react/components/icon-button.js +14 -0
  72. package/dist/react/components/icon.d.ts +7 -0
  73. package/dist/react/components/icon.js +15 -0
  74. package/dist/react/components/image.d.ts +72 -0
  75. package/dist/react/components/image.js +24 -0
  76. package/dist/react/components/inline-edit.d.ts +70 -0
  77. package/dist/react/components/inline-edit.js +149 -0
  78. package/dist/react/components/input.d.ts +29 -0
  79. package/dist/react/components/input.js +20 -0
  80. package/dist/react/components/kanban-board.d.ts +140 -0
  81. package/dist/react/components/kanban-board.js +374 -0
  82. package/dist/react/components/layout.d.ts +51 -0
  83. package/dist/react/components/layout.js +28 -0
  84. package/dist/react/components/list.d.ts +28 -0
  85. package/dist/react/components/list.js +6 -0
  86. package/dist/react/components/masthead-and-verdict.d.ts +60 -0
  87. package/dist/react/components/masthead-and-verdict.js +8 -0
  88. package/dist/react/components/menu.d.ts +26 -0
  89. package/dist/react/components/menu.js +31 -0
  90. package/dist/react/components/multi-select-picker.d.ts +49 -0
  91. package/dist/react/components/multi-select-picker.js +20 -0
  92. package/dist/react/components/note.d.ts +33 -0
  93. package/dist/react/components/note.js +8 -0
  94. package/dist/react/components/number-field.d.ts +16 -0
  95. package/dist/react/components/number-field.js +17 -0
  96. package/dist/react/components/pagination.d.ts +45 -0
  97. package/dist/react/components/pagination.js +87 -0
  98. package/dist/react/components/persistent-action-banner.d.ts +71 -0
  99. package/dist/react/components/persistent-action-banner.js +39 -0
  100. package/dist/react/components/popconfirm.d.ts +42 -0
  101. package/dist/react/components/popconfirm.js +59 -0
  102. package/dist/react/components/popover.d.ts +16 -0
  103. package/dist/react/components/popover.js +21 -0
  104. package/dist/react/components/progress-and-gauge.d.ts +44 -0
  105. package/dist/react/components/progress-and-gauge.js +19 -0
  106. package/dist/react/components/radio.d.ts +7 -0
  107. package/dist/react/components/radio.js +13 -0
  108. package/dist/react/components/rich-text-toolbar.d.ts +26 -0
  109. package/dist/react/components/rich-text-toolbar.js +64 -0
  110. package/dist/react/components/rows-and-card.d.ts +88 -0
  111. package/dist/react/components/rows-and-card.js +10 -0
  112. package/dist/react/components/save-status-indicator.d.ts +11 -0
  113. package/dist/react/components/save-status-indicator.js +33 -0
  114. package/dist/react/components/segmented-control.d.ts +21 -0
  115. package/dist/react/components/segmented-control.js +15 -0
  116. package/dist/react/components/select.d.ts +29 -0
  117. package/dist/react/components/select.js +29 -0
  118. package/dist/react/components/selectable-cards.d.ts +30 -0
  119. package/dist/react/components/selectable-cards.js +29 -0
  120. package/dist/react/components/settings-rows.d.ts +51 -0
  121. package/dist/react/components/settings-rows.js +8 -0
  122. package/dist/react/components/sidebar-nav.d.ts +77 -0
  123. package/dist/react/components/sidebar-nav.js +41 -0
  124. package/dist/react/components/skeleton-and-empty.d.ts +51 -0
  125. package/dist/react/components/skeleton-and-empty.js +14 -0
  126. package/dist/react/components/slider.d.ts +19 -0
  127. package/dist/react/components/slider.js +17 -0
  128. package/dist/react/components/small-charts.d.ts +60 -0
  129. package/dist/react/components/small-charts.js +76 -0
  130. package/dist/react/components/sortable.d.ts +30 -0
  131. package/dist/react/components/sortable.js +85 -0
  132. package/dist/react/components/split-button.d.ts +38 -0
  133. package/dist/react/components/split-button.js +43 -0
  134. package/dist/react/components/split-panes.d.ts +35 -0
  135. package/dist/react/components/split-panes.js +102 -0
  136. package/dist/react/components/stat-tiles.d.ts +54 -0
  137. package/dist/react/components/stat-tiles.js +8 -0
  138. package/dist/react/components/steps.d.ts +11 -0
  139. package/dist/react/components/steps.js +13 -0
  140. package/dist/react/components/switch.d.ts +6 -0
  141. package/dist/react/components/switch.js +13 -0
  142. package/dist/react/components/table.d.ts +61 -0
  143. package/dist/react/components/table.js +6 -0
  144. package/dist/react/components/tabs.d.ts +7 -0
  145. package/dist/react/components/tabs.js +13 -0
  146. package/dist/react/components/tags.d.ts +33 -0
  147. package/dist/react/components/tags.js +83 -0
  148. package/dist/react/components/textarea.d.ts +10 -0
  149. package/dist/react/components/textarea.js +6 -0
  150. package/dist/react/components/toast.d.ts +35 -0
  151. package/dist/react/components/toast.js +90 -0
  152. package/dist/react/components/tooltip-and-key.d.ts +20 -0
  153. package/dist/react/components/tooltip-and-key.js +20 -0
  154. package/dist/react/components/tree-view.d.ts +34 -0
  155. package/dist/react/components/tree-view.js +118 -0
  156. package/dist/react/components/upload-dropzone.d.ts +92 -0
  157. package/dist/react/components/upload-dropzone.js +116 -0
  158. package/dist/react/components/wizard-shell.d.ts +78 -0
  159. package/dist/react/components/wizard-shell.js +176 -0
  160. package/dist/react/fonts.css.d.ts +1 -0
  161. package/dist/react/index.d.ts +76 -0
  162. package/dist/react/index.js +77 -0
  163. package/dist/react/integrations/quickcrop.d.ts +58 -0
  164. package/dist/react/integrations/quickcrop.js +137 -0
  165. package/dist/react/integrations/richclay.d.ts +43 -0
  166. package/dist/react/integrations/richclay.js +129 -0
  167. package/dist/react/internal/classes.d.ts +3 -0
  168. package/dist/react/internal/classes.js +10 -0
  169. package/dist/react/internal/close-mark.d.ts +1 -0
  170. package/dist/react/internal/close-mark.js +10 -0
  171. package/dist/react/internal/controllable-state.d.ts +9 -0
  172. package/dist/react/internal/controllable-state.js +22 -0
  173. package/dist/react/internal/drop-effect.d.ts +28 -0
  174. package/dist/react/internal/drop-effect.js +8 -0
  175. package/dist/react/internal/form-reset.d.ts +2 -0
  176. package/dist/react/internal/form-reset.js +22 -0
  177. package/dist/react/internal/format.d.ts +1 -0
  178. package/dist/react/internal/format.js +14 -0
  179. package/dist/react/internal/link.d.ts +2 -0
  180. package/dist/react/internal/link.js +8 -0
  181. package/dist/react/internal/part.d.ts +20 -0
  182. package/dist/react/internal/part.js +47 -0
  183. package/dist/react/internal/pointer-session.d.ts +8 -0
  184. package/dist/react/internal/pointer-session.js +60 -0
  185. package/dist/react/internal/refs.d.ts +2 -0
  186. package/dist/react/internal/refs.js +22 -0
  187. package/dist/react/internal/rich-text.d.ts +28 -0
  188. package/dist/react/internal/rich-text.js +4 -0
  189. package/dist/react/internal/table-context.d.ts +39 -0
  190. package/dist/react/internal/table-context.js +12 -0
  191. package/dist/react/internal/use-object-url.d.ts +1 -0
  192. package/dist/react/internal/use-object-url.js +15 -0
  193. package/dist/react/layout.css +274 -0
  194. package/dist/react/layout.css.d.ts +1 -0
  195. package/dist/react/models/filter.d.ts +31 -0
  196. package/dist/react/models/filter.js +79 -0
  197. package/dist/react/models/kanban.d.ts +51 -0
  198. package/dist/react/models/kanban.js +35 -0
  199. package/dist/react/models/reorder.d.ts +9 -0
  200. package/dist/react/models/reorder.js +23 -0
  201. package/dist/react/models/table.d.ts +85 -0
  202. package/dist/react/models/table.js +144 -0
  203. package/dist/react/models/tree.d.ts +16 -0
  204. package/dist/react/models/tree.js +26 -0
  205. package/dist/react/provider.d.ts +18 -0
  206. package/dist/react/provider.js +31 -0
  207. package/dist/react/recipes/context-hypercms.d.ts +17 -0
  208. package/dist/react/recipes/context-hypercms.js +71 -0
  209. package/dist/react/recipes/context-quickcrop.d.ts +8 -0
  210. package/dist/react/recipes/context-quickcrop.js +49 -0
  211. package/dist/react/theme.css +8004 -0
  212. package/dist/react/theme.css.d.ts +1 -0
  213. package/examples/README.md +34 -0
  214. package/examples/components/activity-feed.html +28 -0
  215. package/examples/components/app-shell.html +12 -0
  216. package/examples/components/app-toolbar.html +38 -0
  217. package/examples/components/avatar-group.html +27 -0
  218. package/examples/components/breadcrumbs.html +26 -0
  219. package/examples/components/bulk-bar.html +22 -0
  220. package/examples/components/code-block.html +12 -0
  221. package/examples/components/code-editor.html +25 -0
  222. package/examples/components/column-manager.html +9 -0
  223. package/examples/components/combobox.html +13 -0
  224. package/examples/components/context-hypercms.css +41 -0
  225. package/examples/components/context-hypercms.html +40 -0
  226. package/examples/components/context-hypercms.js +62 -0
  227. package/examples/components/context-quickcrop.html +137 -0
  228. package/examples/components/diff-view.html +9 -0
  229. package/examples/components/drawer.html +11 -0
  230. package/examples/components/editable-grid-cells.html +9 -0
  231. package/examples/components/file-browser.html +53 -0
  232. package/examples/components/file-tree.html +34 -0
  233. package/examples/components/files-table.html +80 -0
  234. package/examples/components/filter-builder.html +11 -0
  235. package/examples/components/form-error-summary.html +11 -0
  236. package/examples/components/index.html +51 -0
  237. package/examples/components/inline-edit.html +42 -0
  238. package/examples/components/kanban-board.html +42 -0
  239. package/examples/components/manifest.json +3279 -0
  240. package/examples/components/modal.html +11 -0
  241. package/examples/components/multi-select-picker.html +13 -0
  242. package/examples/components/pagination.html +32 -0
  243. package/examples/components/persistent-action-banner.html +23 -0
  244. package/examples/components/popconfirm.html +13 -0
  245. package/examples/components/popover-menu.html +13 -0
  246. package/examples/components/reference/baseline/DECISIONS.md +85 -0
  247. package/examples/components/reference/baseline/README.md +58 -0
  248. package/examples/components/reference/baseline/bevel-fonts.css +16 -0
  249. package/examples/components/reference/baseline/bevel.css +1339 -0
  250. package/examples/components/reference/baseline/bevel.js +496 -0
  251. package/examples/components/reference/baseline/fonts/LICENSE-ibm-plex-mono.txt +93 -0
  252. package/examples/components/reference/baseline/fonts/LICENSE-ibm-plex-sans.txt +93 -0
  253. package/examples/components/reference/baseline/fonts/LICENSE-newsreader.txt +93 -0
  254. package/examples/components/reference/baseline/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  255. package/examples/components/reference/baseline/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  256. package/examples/components/reference/baseline/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  257. package/examples/components/reference/baseline/fonts/ibm-plex-sans-latin-400-italic.woff2 +0 -0
  258. package/examples/components/reference/baseline/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  259. package/examples/components/reference/baseline/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  260. package/examples/components/reference/baseline/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  261. package/examples/components/reference/baseline/fonts/newsreader-latin-opsz-italic.woff2 +0 -0
  262. package/examples/components/reference/baseline/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  263. package/examples/components/reference/fable/IMPROVEMENTS.md +106 -0
  264. package/examples/components/reference/fable/PARENT-REPAIRS.md +17 -0
  265. package/examples/components/reference/fable/__pycache__/build_repairs.cpython-311.pyc +0 -0
  266. package/examples/components/reference/fable/__pycache__/kanban.cpython-311.pyc +0 -0
  267. package/examples/components/reference/fable/as-authored/IMPROVEMENTS-completed.md +106 -0
  268. package/examples/components/reference/fable/as-authored/base-astra.css +17 -0
  269. package/examples/components/reference/fable/as-authored/base-parent.css +416 -0
  270. package/examples/components/reference/fable/as-authored/build.py +858 -0
  271. package/examples/components/reference/fable/as-authored/fable.css +823 -0
  272. package/examples/components/reference/fable/as-authored/kanban-completed.css +235 -0
  273. package/examples/components/reference/fable/as-authored/kanban-completed.html +53 -0
  274. package/examples/components/reference/fable/as-authored/kanban-completed.py +212 -0
  275. package/examples/components/reference/fable/as-authored/kanban.py +207 -0
  276. package/examples/components/reference/fable/base-astra.css +17 -0
  277. package/examples/components/reference/fable/base-parent.css +416 -0
  278. package/examples/components/reference/fable/build.py +858 -0
  279. package/examples/components/reference/fable/build_repairs.py +25 -0
  280. package/examples/components/reference/fable/components/activity-feed.html +3 -0
  281. package/examples/components/reference/fable/components/app-toolbar.html +3 -0
  282. package/examples/components/reference/fable/components/avatar-group.html +4 -0
  283. package/examples/components/reference/fable/components/breadcrumbs.html +3 -0
  284. package/examples/components/reference/fable/components/code-block.html +9 -0
  285. package/examples/components/reference/fable/components/column-manager.html +3 -0
  286. package/examples/components/reference/fable/components/combobox.html +6 -0
  287. package/examples/components/reference/fable/components/context-hypercms.html +3 -0
  288. package/examples/components/reference/fable/components/context-quickcrop.html +4 -0
  289. package/examples/components/reference/fable/components/diff-view.html +13 -0
  290. package/examples/components/reference/fable/components/drawer.html +3 -0
  291. package/examples/components/reference/fable/components/editable-grid-cells.html +3 -0
  292. package/examples/components/reference/fable/components/filter-builder.html +3 -0
  293. package/examples/components/reference/fable/components/form-error-summary.html +4 -0
  294. package/examples/components/reference/fable/components/inline-edit.html +3 -0
  295. package/examples/components/reference/fable/components/kanban-board.html +53 -0
  296. package/examples/components/reference/fable/components/modal.html +3 -0
  297. package/examples/components/reference/fable/components/multi-select-picker.html +5 -0
  298. package/examples/components/reference/fable/components/pagination.html +3 -0
  299. package/examples/components/reference/fable/components/persistent-action-banner.html +4 -0
  300. package/examples/components/reference/fable/components/popconfirm.html +5 -0
  301. package/examples/components/reference/fable/components/popover-menu.html +3 -0
  302. package/examples/components/reference/fable/components/rich-text-toolbar.html +3 -0
  303. package/examples/components/reference/fable/components/save-status-indicator.html +2 -0
  304. package/examples/components/reference/fable/components/selectable-cards.html +3 -0
  305. package/examples/components/reference/fable/components/settings-rows.html +3 -0
  306. package/examples/components/reference/fable/components/sidebar-nav.html +4 -0
  307. package/examples/components/reference/fable/components/small-charts.html +5 -0
  308. package/examples/components/reference/fable/components/split-button.html +4 -0
  309. package/examples/components/reference/fable/components/split-panes.html +8 -0
  310. package/examples/components/reference/fable/components/stat-tiles.html +3 -0
  311. package/examples/components/reference/fable/components/toast-stack.html +5 -0
  312. package/examples/components/reference/fable/components/tree-view.html +4 -0
  313. package/examples/components/reference/fable/components/upload-dropzone.html +6 -0
  314. package/examples/components/reference/fable/components/wizard-shell.html +3 -0
  315. package/examples/components/reference/fable/coverage.json +414 -0
  316. package/examples/components/reference/fable/fable.css +823 -0
  317. package/examples/components/reference/fable/index.html +5 -0
  318. package/examples/components/reference/fable/kanban.css +237 -0
  319. package/examples/components/reference/fable/kanban.py +212 -0
  320. package/examples/components/reference/fable/parent-repairs.css +9 -0
  321. package/examples/components/reference/fixtures/data.json +102 -0
  322. package/examples/components/reference/fixtures/host.js +47 -0
  323. package/examples/components/reference/fixtures/static-host.js +39 -0
  324. package/examples/components/reference/fixtures/workspace.jpg +0 -0
  325. package/examples/components/reference/parent/CHANGES.md +179 -0
  326. package/examples/components/reference/parent/__pycache__/kanban.cpython-311.pyc +0 -0
  327. package/examples/components/reference/parent/base-astra.css +17 -0
  328. package/examples/components/reference/parent/base-parent.css +416 -0
  329. package/examples/components/reference/parent/build.py +148 -0
  330. package/examples/components/reference/parent/components/activity-feed.html +2 -0
  331. package/examples/components/reference/parent/components/app-toolbar.html +2 -0
  332. package/examples/components/reference/parent/components/avatar-group.html +4 -0
  333. package/examples/components/reference/parent/components/breadcrumbs.html +2 -0
  334. package/examples/components/reference/parent/components/code-block.html +8 -0
  335. package/examples/components/reference/parent/components/column-manager.html +2 -0
  336. package/examples/components/reference/parent/components/combobox.html +6 -0
  337. package/examples/components/reference/parent/components/context-hypercms.html +2 -0
  338. package/examples/components/reference/parent/components/context-quickcrop.html +4 -0
  339. package/examples/components/reference/parent/components/diff-view.html +4 -0
  340. package/examples/components/reference/parent/components/drawer.html +2 -0
  341. package/examples/components/reference/parent/components/editable-grid-cells.html +2 -0
  342. package/examples/components/reference/parent/components/filter-builder.html +2 -0
  343. package/examples/components/reference/parent/components/form-error-summary.html +2 -0
  344. package/examples/components/reference/parent/components/inline-edit.html +2 -0
  345. package/examples/components/reference/parent/components/kanban-board.html +84 -0
  346. package/examples/components/reference/parent/components/modal.html +2 -0
  347. package/examples/components/reference/parent/components/multi-select-picker.html +5 -0
  348. package/examples/components/reference/parent/components/pagination.html +2 -0
  349. package/examples/components/reference/parent/components/persistent-action-banner.html +4 -0
  350. package/examples/components/reference/parent/components/popconfirm.html +5 -0
  351. package/examples/components/reference/parent/components/popover-menu.html +2 -0
  352. package/examples/components/reference/parent/components/rich-text-toolbar.html +2 -0
  353. package/examples/components/reference/parent/components/save-status-indicator.html +2 -0
  354. package/examples/components/reference/parent/components/selectable-cards.html +2 -0
  355. package/examples/components/reference/parent/components/settings-rows.html +2 -0
  356. package/examples/components/reference/parent/components/sidebar-nav.html +4 -0
  357. package/examples/components/reference/parent/components/small-charts.html +2 -0
  358. package/examples/components/reference/parent/components/split-button.html +5 -0
  359. package/examples/components/reference/parent/components/split-panes.html +8 -0
  360. package/examples/components/reference/parent/components/stat-tiles.html +2 -0
  361. package/examples/components/reference/parent/components/toast-stack.html +5 -0
  362. package/examples/components/reference/parent/components/tree-view.html +4 -0
  363. package/examples/components/reference/parent/components/upload-dropzone.html +6 -0
  364. package/examples/components/reference/parent/components/wizard-shell.html +2 -0
  365. package/examples/components/reference/parent/coverage.json +414 -0
  366. package/examples/components/reference/parent/index.html +1 -0
  367. package/examples/components/reference/parent/kanban.css +114 -0
  368. package/examples/components/reference/parent/kanban.py +55 -0
  369. package/examples/components/reference/parent/refinement.css +53 -0
  370. package/examples/components/reference/selected/build.py +70 -0
  371. package/examples/components/reference/selected/components/activity-feed.html +3 -0
  372. package/examples/components/reference/selected/components/app-toolbar.html +3 -0
  373. package/examples/components/reference/selected/components/avatar-group.html +4 -0
  374. package/examples/components/reference/selected/components/breadcrumbs.html +3 -0
  375. package/examples/components/reference/selected/components/code-block.html +9 -0
  376. package/examples/components/reference/selected/components/column-manager.html +3 -0
  377. package/examples/components/reference/selected/components/combobox.html +6 -0
  378. package/examples/components/reference/selected/components/context-hypercms.html +3 -0
  379. package/examples/components/reference/selected/components/context-quickcrop.html +4 -0
  380. package/examples/components/reference/selected/components/diff-view.html +13 -0
  381. package/examples/components/reference/selected/components/drawer.html +3 -0
  382. package/examples/components/reference/selected/components/editable-grid-cells.html +3 -0
  383. package/examples/components/reference/selected/components/filter-builder.html +3 -0
  384. package/examples/components/reference/selected/components/form-error-summary.html +4 -0
  385. package/examples/components/reference/selected/components/inline-edit.html +3 -0
  386. package/examples/components/reference/selected/components/kanban-board.html +53 -0
  387. package/examples/components/reference/selected/components/modal.html +2 -0
  388. package/examples/components/reference/selected/components/multi-select-picker.html +5 -0
  389. package/examples/components/reference/selected/components/pagination.html +3 -0
  390. package/examples/components/reference/selected/components/persistent-action-banner.html +4 -0
  391. package/examples/components/reference/selected/components/popconfirm.html +5 -0
  392. package/examples/components/reference/selected/components/popover-menu.html +3 -0
  393. package/examples/components/reference/selected/components/rich-text-toolbar.html +3 -0
  394. package/examples/components/reference/selected/components/save-status-indicator.html +2 -0
  395. package/examples/components/reference/selected/components/selectable-cards.html +3 -0
  396. package/examples/components/reference/selected/components/settings-rows.html +3 -0
  397. package/examples/components/reference/selected/components/sidebar-nav.html +4 -0
  398. package/examples/components/reference/selected/components/small-charts.html +5 -0
  399. package/examples/components/reference/selected/components/split-button.html +4 -0
  400. package/examples/components/reference/selected/components/split-panes.html +8 -0
  401. package/examples/components/reference/selected/components/stat-tiles.html +3 -0
  402. package/examples/components/reference/selected/components/toast-stack.html +5 -0
  403. package/examples/components/reference/selected/components/tree-view.html +4 -0
  404. package/examples/components/reference/selected/components/upload-dropzone.html +6 -0
  405. package/examples/components/reference/selected/components/wizard-shell.html +3 -0
  406. package/examples/components/reference/selected/corrections.css +11 -0
  407. package/examples/components/reference/selected/coverage.json +413 -0
  408. package/examples/components/reference/selected/gallery-build.mjs +49 -0
  409. package/examples/components/reference/selected/gallery-spec.json +12 -0
  410. package/examples/components/reference/selected/index.html +1 -0
  411. package/examples/components/reference/selected/kanban-comparison.json +6 -0
  412. package/examples/components/reference/selected/manifest.json +476 -0
  413. package/examples/components/reference/sha256.json +142 -0
  414. package/examples/components/reference/sources/diagonal-close.svg +1 -0
  415. package/examples/components/reference/sources/hyperclay/public-assets/css/hyperclay-app-source.css +614 -0
  416. package/examples/components/reference/sources/hyperclay/public-assets/js/app.js +94 -0
  417. package/examples/components/reference/sources/hyperclayjs/src/ui/theModal.js +897 -0
  418. package/examples/components/reference/sources/hypercms/scripts/build-theme.js +203 -0
  419. package/examples/components/reference/sources/hypercms/src/anchor.js +24 -0
  420. package/examples/components/reference/sources/hypercms/src/apply-loop.js +167 -0
  421. package/examples/components/reference/sources/hypercms/src/diagnostics.js +44 -0
  422. package/examples/components/reference/sources/hypercms/src/enhance.js +173 -0
  423. package/examples/components/reference/sources/hypercms/src/events.js +955 -0
  424. package/examples/components/reference/sources/hypercms/src/form-builder.js +518 -0
  425. package/examples/components/reference/sources/hypercms/src/form-rules.js +208 -0
  426. package/examples/components/reference/sources/hypercms/src/hooks.js +100 -0
  427. package/examples/components/reference/sources/hypercms/src/hypercms-bundle.js +10 -0
  428. package/examples/components/reference/sources/hypercms/src/hypercms.js +347 -0
  429. package/examples/components/reference/sources/hypercms/src/morph.js +10 -0
  430. package/examples/components/reference/sources/hypercms/src/path.js +61 -0
  431. package/examples/components/reference/sources/hypercms/src/place.js +131 -0
  432. package/examples/components/reference/sources/hypercms/src/platform.js +131 -0
  433. package/examples/components/reference/sources/hypercms/src/refresh.js +87 -0
  434. package/examples/components/reference/sources/hypercms/src/richclay-bridge.js +140 -0
  435. package/examples/components/reference/sources/hypercms/src/row-identity.js +111 -0
  436. package/examples/components/reference/sources/hypercms/src/scaffold.js +10 -0
  437. package/examples/components/reference/sources/hypercms/src/session.js +303 -0
  438. package/examples/components/reference/sources/hypercms/src/shell.js +248 -0
  439. package/examples/components/reference/sources/hypercms/src/styles.css +298 -0
  440. package/examples/components/reference/sources/hypercms/src/targets.js +144 -0
  441. package/examples/components/reference/sources/hypercms/src/templates.js +656 -0
  442. package/examples/components/reference/sources/hypercms/src/theme/pixel-quiet.overrides.css +761 -0
  443. package/examples/components/reference/sources/hypercms/src/theme.generated.css +1772 -0
  444. package/examples/components/reference/sources/hypercms/src/toggle.js +666 -0
  445. package/examples/components/reference/sources/hypercms/src/unresolved.js +211 -0
  446. package/examples/components/reference/sources/hypercms/src/vendor/mirk.vendor.js +212 -0
  447. package/examples/components/reference/sources/hypercms/src/views/inline-layer.js +421 -0
  448. package/examples/components/reference/sources/hypercms/src/views/inline.js +1072 -0
  449. package/examples/components/reference/sources/hypercms/src/views/sidebar.js +97 -0
  450. package/examples/components/reference/sources/modal-reference.html +301 -0
  451. package/examples/components/reference/sources/quickcrop/quickcrop.css +124 -0
  452. package/examples/components/reference/sources/quickcrop/quickcrop.js +400 -0
  453. package/examples/components/reference/sources/richclay/richclay.css +416 -0
  454. package/examples/components/reference/sources/richclay/richclay.js +2962 -0
  455. package/examples/components/reference/sources/richclay/src/a11y.js +67 -0
  456. package/examples/components/reference/sources/richclay/src/browser-global.js +7 -0
  457. package/examples/components/reference/sources/richclay/src/buttons.js +254 -0
  458. package/examples/components/reference/sources/richclay/src/hyperclay.js +470 -0
  459. package/examples/components/reference/sources/richclay/src/normalize.js +380 -0
  460. package/examples/components/reference/sources/richclay/src/richclay.js +1356 -0
  461. package/examples/components/reference/sources/richclay/src/sanitize.js +189 -0
  462. package/examples/components/reference/sources/richclay/src/styles.js +23 -0
  463. package/examples/components/reference/sources/richclay/src/toolbar-float.js +175 -0
  464. package/examples/components/reference/sources/richclay/src/toolbar.js +394 -0
  465. package/examples/components/reference/sources/richclay/src/vendor-bundle.js +15 -0
  466. package/examples/components/reference/sources/richclay/vendor/DOMPURIFY-LICENSE +202 -0
  467. package/examples/components/reference/sources/richclay/vendor/DOMPURIFY-LICENSE-MPL +373 -0
  468. package/examples/components/reference/sources/richclay/vendor/SQUIRE-LICENSE +21 -0
  469. package/examples/components/reference/sources/richclay/vendor/purify.es.mjs +2249 -0
  470. package/examples/components/reference/sources/richclay/vendor/purify.min.js +3 -0
  471. package/examples/components/reference/sources/richclay/vendor/squire.js +22 -0
  472. package/examples/components/reference/sources/richclay/vendor/squire.mjs +22 -0
  473. package/examples/components/reference/sources/richclay/vendor/squire.provenance.json +19 -0
  474. package/examples/components/rich-text-toolbar.html +43 -0
  475. package/examples/components/save-status-indicator.html +11 -0
  476. package/examples/components/selectable-cards.html +9 -0
  477. package/examples/components/settings-rows.html +10 -0
  478. package/examples/components/sidebar-nav.html +9 -0
  479. package/examples/components/small-charts.html +21 -0
  480. package/examples/components/split-button.html +13 -0
  481. package/examples/components/split-panes-editor.html +6 -0
  482. package/examples/components/split-panes.html +9 -0
  483. package/examples/components/stat-tiles.html +23 -0
  484. package/examples/components/toast-stack.html +21 -0
  485. package/examples/components/tree-view.html +9 -0
  486. package/examples/components/upload-dropzone.html +88 -0
  487. package/examples/components/wizard-shell.html +50 -0
  488. package/examples/datastar/index.html +38 -0
  489. package/examples/datastar/server.mjs +31 -0
  490. package/examples/fixtures/field-guide.pdf +14 -0
  491. package/examples/fixtures/workspace.jpg +0 -0
  492. package/examples/setup-peers.mjs +35 -0
  493. package/fonts/LICENSE-ibm-plex-mono.txt +93 -0
  494. package/fonts/LICENSE-ibm-plex-sans.txt +93 -0
  495. package/fonts/LICENSE-newsreader.txt +93 -0
  496. package/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  497. package/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  498. package/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  499. package/fonts/ibm-plex-sans-latin-400-italic.woff2 +0 -0
  500. package/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  501. package/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  502. package/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  503. package/fonts/newsreader-latin-opsz-italic.woff2 +0 -0
  504. package/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  505. package/icons/LICENSE +21 -0
  506. package/icons/house.json +71 -0
  507. package/icons/icons.d.ts +17 -0
  508. package/icons/icons.js +648 -0
  509. package/icons/icons.svg +126 -0
  510. package/icons/marks/build-tray.source.mjs +45 -0
  511. package/icons/marks/hyperclay-local.svg +11 -0
  512. package/icons/phosphor.json +53 -0
  513. package/icons/round.json +8 -0
  514. package/integrations/datastar.js +202 -0
  515. package/integrations/quickcrop.css +26 -0
  516. package/integrations/quickcrop.js +56 -0
  517. package/integrations/richclay.css +100 -0
  518. package/integrations/richclay.js +131 -0
  519. package/package.json +630 -0
  520. package/react/README.md +244 -0
  521. package/src/README.md +83 -0
  522. package/vendor/datastar/datastar-1.0.3.js +10 -0
  523. package/vendor/datastar/provenance.json +6 -0
  524. package/vendor/floating-ui/LICENSE-core +20 -0
  525. package/vendor/floating-ui/LICENSE-dom +20 -0
  526. package/vendor/floating-ui/floating-ui.core.umd.min.js +1 -0
  527. package/vendor/floating-ui/floating-ui.dom.umd.min.js +1 -0
  528. package/vendor/floating-ui/provenance.json +17 -0
  529. package/vendor/richclay/LICENSE +16 -0
  530. package/vendor/richclay/THIRD-PARTY-NOTICES.md +45 -0
  531. package/vendor/richclay/provenance.json +17 -0
  532. package/vendor/richclay/richclay.css +404 -0
  533. package/vendor/richclay/richclay.js +2940 -0
  534. package/vendor/richclay/vendor/DOMPURIFY-LICENSE +202 -0
  535. package/vendor/richclay/vendor/DOMPURIFY-LICENSE-MPL +373 -0
  536. package/vendor/richclay/vendor/SQUIRE-LICENSE +21 -0
  537. package/vendor/richclay/vendor/purify.min.js +3 -0
  538. package/vendor/richclay/vendor/squire.js +22 -0
  539. package/vendor/richclay/vendor/squire.provenance.json +30 -0
package/bevel.js ADDED
@@ -0,0 +1,2126 @@
1
+ /*
2
+ * bevel.js — the delegated runtime for the bevel UI kit, 0.1.0.
3
+ * https://github.com/panphora/bevel
4
+ *
5
+ * One listener per interaction on `document`. No init(), no MutationObserver:
6
+ * every current AND future element is handled, so injected, re-rendered, and
7
+ * saved-and-reopened markup just works. Idempotent; safe to include twice.
8
+ *
9
+ * The markup carries the initial state (chips are real DOM, the slider fill is
10
+ * an inline --bevel-value, native inputs hold their own value), so the page is
11
+ * correct before this script runs. The script does two things on top:
12
+ * 1. Drives the transitions (steppers, chips, tabs, drag order, previews,
13
+ * the grid's sort, select-all, expand, search and pages).
14
+ * 2. Mirrors live control state back into attributes (checked, selected,
15
+ * value, the textarea's text), so document.documentElement.outerHTML
16
+ * round-trips every visible state of every bevel control.
17
+ */
18
+ (function () {
19
+ if (window.__bevel) return;
20
+ window.__bevel = true;
21
+
22
+ const X_SVG = '<svg aria-hidden="true" viewBox="0 0 256 256" fill="currentColor"><path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"/></svg>';
23
+
24
+ const on = (type, selector, handler, options) => {
25
+ document.addEventListener(type, (e) => {
26
+ const target = e.target instanceof Element ? e.target.closest(selector) : null;
27
+ if (target) handler(e, target);
28
+ }, options);
29
+ };
30
+ const emit = (el, name, detail) => el.dispatchEvent(new CustomEvent(name, { bubbles: true, detail }));
31
+ const nativeChange = (input) => {
32
+ input.dispatchEvent(new Event("input", { bubbles: true }));
33
+ input.dispatchEvent(new Event("change", { bubbles: true }));
34
+ };
35
+ const clearInput = (input) => { input.value = ""; input.removeAttribute("value"); };
36
+
37
+ /* ---------- state mirroring: the outerHTML contract ---------- */
38
+ const MIRROR_VALUE = ".bevel-input, .bevel-number__input, .bevel-slider__input, .bevel-date__field, .bevel-sortable__field, .bevel-tags__input";
39
+ on("input", MIRROR_VALUE, (e, input) => input.setAttribute("value", input.value));
40
+ on("input", ".bevel-textarea", (e, area) => { area.textContent = area.value; });
41
+ on("change", ".bevel-checkbox input, .bevel-toggle input, .bevel-table__pick input", (e, input) => {
42
+ input.toggleAttribute("checked", input.checked);
43
+ });
44
+ on("change", ".bevel-radio input", (e, input) => {
45
+ for (const r of document.querySelectorAll('input[type="radio"]')) {
46
+ if (r.name === input.name && r.form === input.form) r.toggleAttribute("checked", r.checked);
47
+ }
48
+ });
49
+ on("change", ".bevel-select__field", (e, select) => {
50
+ for (const o of select.options) o.toggleAttribute("selected", o.selected);
51
+ });
52
+
53
+ /* ---------- number ---------- */
54
+ on("click", ".bevel-number__step", (e, step) => {
55
+ const input = step.closest(".bevel-number")?.querySelector(".bevel-number__input");
56
+ if (!input || input.disabled) return;
57
+ try { step.dataset.step === "down" ? input.stepDown() : input.stepUp(); } catch { return; }
58
+ nativeChange(input);
59
+ });
60
+
61
+ /* ---------- slider ---------- */
62
+ const percent = (input) => {
63
+ const min = input.min === "" ? 0 : Number(input.min);
64
+ const max = input.max === "" ? 100 : Number(input.max);
65
+ if (!(max > min)) return "0%";
66
+ const pct = ((Number(input.value) - min) / (max - min)) * 100;
67
+ return `${+Math.min(100, Math.max(0, pct)).toFixed(2)}%`;
68
+ };
69
+ const syncSlider = (input) => {
70
+ const wrap = input.closest(".bevel-slider");
71
+ if (!wrap) return;
72
+ wrap.style.setProperty("--bevel-value", percent(input));
73
+ const out = wrap.closest(".bevel-sliderrow")?.querySelector("output, .bevel-sliderrow__value");
74
+ if (out) { if (out.tagName === "OUTPUT") out.value = input.value; else out.textContent = input.value; }
75
+ };
76
+ on("input", ".bevel-slider__input", (e, input) => syncSlider(input));
77
+
78
+ /* ---------- file ---------- */
79
+ const fileName = (wrap) => wrap.querySelector(".bevel-file__name");
80
+ const clearFile = (wrap, name) => {
81
+ name.textContent = name.dataset.empty || "No file chosen";
82
+ name.removeAttribute("data-filled");
83
+ if (name.tagName === "A") name.removeAttribute("href");
84
+ wrap.querySelector(".bevel-file__remove")?.remove();
85
+ };
86
+ on("change", ".bevel-file__input", (e, input) => {
87
+ const wrap = input.closest(".bevel-file");
88
+ const name = wrap && fileName(wrap);
89
+ if (!name) return;
90
+ if (!name.dataset.empty && !name.hasAttribute("data-filled")) name.dataset.empty = name.textContent;
91
+ const files = input.files;
92
+ if (files && files.length) {
93
+ name.textContent = files.length === 1 ? files[0].name : `${files.length} files`;
94
+ name.setAttribute("data-filled", "");
95
+ if (!wrap.querySelector(".bevel-file__remove")) {
96
+ const remove = document.createElement("button");
97
+ remove.type = "button";
98
+ remove.className = "bevel-file__remove";
99
+ remove.setAttribute("aria-label", "Remove file");
100
+ remove.innerHTML = X_SVG;
101
+ name.after(remove);
102
+ }
103
+ } else clearFile(wrap, name);
104
+ });
105
+ on("click", ".bevel-file__remove", (e, button) => {
106
+ const wrap = button.closest(".bevel-file");
107
+ if (!wrap) return;
108
+ const input = wrap.querySelector(".bevel-file__input");
109
+ const name = fileName(wrap);
110
+ if (input) { input.value = ""; nativeChange(input); }
111
+ if (name && name.hasAttribute("data-filled")) clearFile(wrap, name);
112
+ emit(wrap, "bevel:remove");
113
+ });
114
+
115
+ /* ---------- image ---------- */
116
+ const showPreview = (wrap, src) => {
117
+ const img = wrap.querySelector(".bevel-image__preview");
118
+ const placeholder = wrap.querySelector(".bevel-image__placeholder");
119
+ if (!img) return null;
120
+ if (img.dataset.blob) { URL.revokeObjectURL(img.dataset.blob); delete img.dataset.blob; }
121
+ if (src) { img.src = src; img.hidden = false; if (placeholder) placeholder.hidden = true; }
122
+ else { img.removeAttribute("src"); img.hidden = true; if (placeholder) placeholder.hidden = false; }
123
+ return img;
124
+ };
125
+ on("change", ".bevel-image__input", (e, input) => {
126
+ const wrap = input.closest(".bevel-image");
127
+ const file = input.files && input.files[0];
128
+ if (!wrap) return;
129
+ if (!file) { showPreview(wrap, null); return; }
130
+ const url = URL.createObjectURL(file);
131
+ const img = showPreview(wrap, url);
132
+ if (img) img.dataset.blob = url; else URL.revokeObjectURL(url);
133
+ });
134
+ on("click", ".bevel-image__remove", (e, button) => {
135
+ const wrap = button.closest(".bevel-image");
136
+ const input = wrap?.querySelector(".bevel-image__input");
137
+ if (!wrap) return;
138
+ if (input) input.value = "";
139
+ showPreview(wrap, null);
140
+ emit(wrap, "bevel:remove");
141
+ });
142
+
143
+ /* ---------- tags ---------- */
144
+ const addTag = (wrap, raw) => {
145
+ const text = raw.trim();
146
+ if (!text) return false;
147
+ const taken = [...wrap.querySelectorAll(".bevel-tags__chip input")].some((i) => i.value.toLowerCase() === text.toLowerCase());
148
+ if (taken) return false;
149
+ const chip = document.createElement("span");
150
+ chip.className = "bevel-tags__chip";
151
+ chip.append(text);
152
+ const hidden = document.createElement("input");
153
+ hidden.type = "hidden";
154
+ hidden.name = wrap.dataset.name || "tags[]";
155
+ hidden.value = text;
156
+ const remove = document.createElement("button");
157
+ remove.type = "button";
158
+ remove.className = "bevel-tags__remove";
159
+ remove.setAttribute("aria-label", `Remove ${text}`);
160
+ remove.textContent = "×";
161
+ chip.append(hidden, remove);
162
+ wrap.querySelector(".bevel-tags__input").before(chip);
163
+ emit(wrap, "bevel:tags");
164
+ return true;
165
+ };
166
+ on("keydown", ".bevel-tags__input", (e, input) => {
167
+ const wrap = input.closest(".bevel-tags");
168
+ if (!wrap) return;
169
+ if (e.key === "Enter" || e.key === ",") {
170
+ e.preventDefault();
171
+ if (addTag(wrap, input.value) || !input.value.trim()) clearInput(input);
172
+ } else if (e.key === "Backspace" && !input.value) {
173
+ const chips = wrap.querySelectorAll(".bevel-tags__chip");
174
+ const last = chips[chips.length - 1];
175
+ if (last) { last.remove(); emit(wrap, "bevel:tags"); }
176
+ }
177
+ });
178
+ on("focusout", ".bevel-tags__input", (e, input) => {
179
+ const wrap = input.closest(".bevel-tags");
180
+ if (wrap && input.value.trim() && addTag(wrap, input.value)) clearInput(input);
181
+ });
182
+ on("click", ".bevel-tags__remove", (e, button) => {
183
+ const wrap = button.closest(".bevel-tags");
184
+ button.closest(".bevel-tags__chip")?.remove();
185
+ if (wrap) emit(wrap, "bevel:tags");
186
+ });
187
+ on("click", ".bevel-tags", (e, wrap) => {
188
+ if (e.target === wrap) wrap.querySelector(".bevel-tags__input")?.focus();
189
+ });
190
+
191
+ /* ---------- sortable ---------- */
192
+ let drag = null;
193
+ const rows = (list) => [...list.querySelectorAll(":scope > .bevel-sortable__item")];
194
+ on("pointerdown", ".bevel-sortable__grip", (e, grip) => {
195
+ if (e.pointerType === "mouse" && e.button !== 0) return;
196
+ const item = grip.closest(".bevel-sortable__item");
197
+ const list = item?.parentElement;
198
+ if (!list) return;
199
+ drag = { item, list, id: e.pointerId, from: rows(list).indexOf(item) };
200
+ item.classList.add("is-dragging");
201
+ grip.setPointerCapture(e.pointerId);
202
+ e.preventDefault();
203
+ });
204
+ document.addEventListener("pointermove", (e) => {
205
+ if (!drag || e.pointerId !== drag.id) return;
206
+ const { item, list } = drag;
207
+ for (const sibling of rows(list)) {
208
+ if (sibling === item) continue;
209
+ const box = sibling.getBoundingClientRect();
210
+ const mid = box.top + box.height / 2;
211
+ const follows = item.compareDocumentPosition(sibling) & Node.DOCUMENT_POSITION_FOLLOWING;
212
+ if (follows && e.clientY > mid) sibling.after(item);
213
+ else if (!follows && e.clientY < mid) sibling.before(item);
214
+ }
215
+ });
216
+ const endDrag = (e) => {
217
+ if (!drag || e.pointerId !== drag.id) return;
218
+ const { item, list, from } = drag;
219
+ item.classList.remove("is-dragging");
220
+ drag = null;
221
+ const to = rows(list).indexOf(item);
222
+ if (to !== from) emit(list, "bevel:reorder", { from, to });
223
+ };
224
+ document.addEventListener("pointerup", endDrag);
225
+ document.addEventListener("pointercancel", endDrag);
226
+
227
+ /* ---------- chip ---------- */
228
+ on("click", "[data-bevel-chip]", (e, button) => {
229
+ const chip = button.closest(".bevel-chip");
230
+ if (!chip) return;
231
+ const action = button.dataset.bevelChip;
232
+ if (action === "open") chip.classList.add("bevel-chip--open");
233
+ else if (action === "collapse") chip.classList.remove("bevel-chip--open");
234
+ else if (action === "changes") chip.querySelector(".bevel-chip__panel")?.classList.toggle("is-changes");
235
+ });
236
+
237
+ /* ---------- tabs ---------- */
238
+ const selectTab = (tab) => {
239
+ const list = tab.closest(".bevel-tabs");
240
+ if (!list) return;
241
+ for (const t of list.querySelectorAll(".bevel-tabs__tab")) {
242
+ const selected = t === tab;
243
+ t.setAttribute("aria-selected", String(selected));
244
+ t.tabIndex = selected ? 0 : -1;
245
+ const id = t.getAttribute("aria-controls");
246
+ const panel = id && document.getElementById(id);
247
+ if (panel) panel.hidden = !selected;
248
+ }
249
+ emit(list, "bevel:tab", { tab });
250
+ };
251
+ on("click", ".bevel-tabs__tab", (e, tab) => selectTab(tab));
252
+ on("keydown", ".bevel-tabs__tab", (e, tab) => {
253
+ const list = tab.closest(".bevel-tabs");
254
+ if (!list) return;
255
+ const tabs = [...list.querySelectorAll(".bevel-tabs__tab")];
256
+ const i = tabs.indexOf(tab);
257
+ const next = e.key === "ArrowRight" ? tabs[(i + 1) % tabs.length]
258
+ : e.key === "ArrowLeft" ? tabs[(i - 1 + tabs.length) % tabs.length]
259
+ : e.key === "Home" ? tabs[0]
260
+ : e.key === "End" ? tabs[tabs.length - 1]
261
+ : null;
262
+ if (!next) return;
263
+ e.preventDefault();
264
+ next.focus();
265
+ selectTab(next);
266
+ });
267
+
268
+ /* ---------- segmented control ---------- */
269
+ on("click", ".bevel-seg > .bevel-button[aria-pressed]", (e, button) => {
270
+ const group = button.parentElement;
271
+ for (const b of group.querySelectorAll(":scope > .bevel-button[aria-pressed]")) b.setAttribute("aria-pressed", String(b === button));
272
+ emit(group, "bevel:seg", { value: button.dataset.value ?? button.textContent.trim(), button });
273
+ });
274
+
275
+ /* ---------- grid ---------- */
276
+ /* Sort, select-all, expand, search and pages. Rows never leave the DOM:
277
+ search and paging set hidden, sorting reorders, and every state is an
278
+ attribute (aria-sort, checked, aria-expanded, data-page, value). The
279
+ select-all's mixed flag and a detail row's hidden are derived from the
280
+ rows on every layout, so a saved page recovers them. */
281
+ const gridOf = (el) => el.closest(".bevel-grid");
282
+ const gridRows = (grid) => [...grid.querySelectorAll(".bevel-grid__table > tbody > .bevel-grid__row")];
283
+ const detailOf = (row) => {
284
+ const next = row.nextElementSibling;
285
+ return next && next.classList.contains("bevel-grid__detail") ? next : null;
286
+ };
287
+ const isOpen = (row) => row.querySelector(".bevel-grid__expand")?.getAttribute("aria-expanded") === "true";
288
+ const pageButton = (n, current, standalone = false) => {
289
+ const b = document.createElement("button");
290
+ b.type = "button";
291
+ b.className = `bevel-button bevel-button--small ${standalone ? "bevel-pagination__page" : "bevel-grid__page"}`;
292
+ b.dataset[standalone ? "bevelPage" : "page"] = n;
293
+ b.innerHTML = '<span class="bevel-button__label"></span>';
294
+ b.firstChild.textContent = n;
295
+ if (n === current) b.setAttribute("aria-current", "page");
296
+ return b;
297
+ };
298
+ const renderPages = (pages, total, current) => {
299
+ const standalone = pages.classList.contains("bevel-pagination__pages");
300
+ const selector = standalone ? "data-bevel-page" : "data-page";
301
+ const prev = pages.querySelector(`[${selector}="prev"]`);
302
+ const next = pages.querySelector(`[${selector}="next"]`);
303
+ const items = [];
304
+ if (total <= 7) for (let n = 1; n <= total; n++) items.push(n);
305
+ else {
306
+ items.push(1);
307
+ if (current > 3) items.push("…");
308
+ for (let n = Math.max(2, current - 1); n <= Math.min(total - 1, current + 1); n++) items.push(n);
309
+ if (current < total - 2) items.push("…");
310
+ items.push(total);
311
+ }
312
+ const rendered = [...pages.children].filter(node => node !== prev && node !== next);
313
+ const matches = rendered.length === items.length && rendered.every((node, index) => items[index] === "…" ? node.textContent === "…" && node.getAttribute("aria-hidden") === "true" : node.getAttribute(selector) === String(items[index]) && node.hasAttribute("aria-current") === (items[index] === current));
314
+ if (!matches) {
315
+ rendered.forEach(node => node.remove());
316
+ const frag = document.createDocumentFragment();
317
+ for (const n of items) {
318
+ if (n === "…") {
319
+ const gap = document.createElement("span");
320
+ gap.className = standalone ? "bevel-pagination__gap" : "bevel-grid__gap";
321
+ gap.setAttribute("aria-hidden", "true");
322
+ gap.textContent = n;
323
+ frag.append(gap);
324
+ } else frag.append(pageButton(n, current, standalone));
325
+ }
326
+ if (next) next.before(frag); else pages.append(frag);
327
+ }
328
+ if (prev && prev.disabled !== (current <= 1)) prev.disabled = current <= 1;
329
+ if (next && next.disabled !== (current >= total)) next.disabled = current >= total;
330
+ };
331
+ const renderStandalonePages = (list, total, current) => {
332
+ const previous = list.querySelector('[data-bevel-page="prev"]'), next = list.querySelector('[data-bevel-page="next"]'), items = [];
333
+ if (total <= 7) for (let page = 1; page <= total; page++) items.push(page);
334
+ else if (current <= 2) items.push(1, 2, 3, "…", total);
335
+ else if (current >= total - 1) items.push(1, "…", total - 2, total - 1, total);
336
+ else {
337
+ items.push(1);
338
+ if (current > 3) items.push("…");
339
+ items.push(current - 1, current, current + 1);
340
+ if (current < total - 2) items.push("…");
341
+ items.push(total);
342
+ }
343
+ const rendered = [...list.children].filter((item) => item !== previous && item !== next), matches = rendered.length === items.length && rendered.every((node, index) => items[index] === "…" ? node.matches('.bevel-pagination__gap[aria-hidden="true"]') && node.textContent === "…" : node.matches(`.bevel-pagination__page[data-bevel-page="${items[index]}"]`) && node.hasAttribute("aria-current") === (items[index] === current));
344
+ if (!matches) {
345
+ rendered.forEach((item) => item.remove());
346
+ for (const item of items) {
347
+ const node = item === "…" ? document.createElement("span") : pageButton(item, current, true);
348
+ if (item === "…") { node.className = "bevel-pagination__gap"; node.setAttribute("aria-hidden", "true"); node.textContent = item; }
349
+ if (next) next.before(node); else list.append(node);
350
+ }
351
+ }
352
+ if (previous && previous.disabled !== (current <= 1)) previous.disabled = current <= 1;
353
+ if (next && next.disabled !== (current >= total)) next.disabled = current >= total;
354
+ };
355
+ const syncPick = (grid) => {
356
+ const head = grid.querySelector(".bevel-grid__table > thead .bevel-grid__pick input");
357
+ const all = [...grid.querySelectorAll(".bevel-grid__table > tbody .bevel-grid__pick input")];
358
+ const shown = all.filter((i) => !i.disabled && !i.closest("tr").hidden);
359
+ const on = shown.filter((i) => i.checked).length;
360
+ if (head) {
361
+ const checked = shown.length > 0 && on === shown.length, mixed = on > 0 && on < shown.length;
362
+ if (head.checked !== checked) head.checked = checked;
363
+ if (head.indeterminate !== mixed) head.indeterminate = mixed;
364
+ if (head.hasAttribute("checked") !== checked) head.toggleAttribute("checked", checked);
365
+ }
366
+ const out = grid.querySelector(".bevel-grid__selected");
367
+ if (out) { const count = String(all.filter(i => i.checked).length); if (out.textContent !== count) out.textContent = count; }
368
+ };
369
+ const matchesView = (row, view) => {
370
+ const values = (view?.dataset.gridMatch || "").toLowerCase().split("|").filter(Boolean);
371
+ return !values.length || values.includes(cellKey(row, Number(view.dataset.gridColumn), "text"));
372
+ };
373
+ const syncStats = (grid, rows) => {
374
+ for (const output of grid.querySelectorAll("[data-grid-aggregate]")) {
375
+ const values = rows.map((row) => cellKey(row, Number(output.dataset.gridColumn), "number"));
376
+ const sum = values.reduce((total, value) => total + value, 0);
377
+ const kind = output.dataset.gridAggregate;
378
+ const value = kind === "count" ? rows.length : kind === "average" ? (values.length ? sum / values.length : 0) : sum;
379
+ const format = output.dataset.gridFormat;
380
+ const text = format === "currency" ? new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 }).format(value)
381
+ : format === "percent" ? `${Math.round(value)}%` : new Intl.NumberFormat("en-US", { maximumFractionDigits: 1 }).format(value);
382
+ if (output.textContent !== text) output.textContent = text;
383
+ }
384
+ };
385
+ const filterDefinitions = root => { try { const node=root.querySelector('script[type="application/json"][data-bevel-filter-fields]'); return node ? JSON.parse(node.textContent) : []; } catch { return []; } };
386
+ const filterRead = root => ({ match: root.dataset.bevelMatch === "any" ? "any" : "all", rules:[...root.querySelectorAll('.bevel-filter__rules > .bevel-filter__rule')].map(row=>({ key:row.dataset.bevelKey, field:row.dataset.bevelField, operator:row.dataset.bevelOperator, value:row.querySelector('[data-bevel-filter-value]')?.value ?? "" })) });
387
+ const filterEmpty = value => value == null || value === "";
388
+ const filterDate = value => { if(!/^\d{4}-\d{2}-\d{2}$/.test(String(value||"")))return false; const date=new Date(`${value}T00:00:00Z`); return !Number.isNaN(date.valueOf())&&date.toISOString().slice(0,10)===value; };
389
+ const filterOperators = { text: ["equals", "contains", "starts-with", "is-empty"], number: ["eq", "ne", "gt", "gte", "lt", "lte", "is-empty"], date: ["before", "on", "after", "is-empty"], enum: ["is", "is-not", "is-empty"] };
390
+ const filterRuleValid = (rule, field) => {
391
+ if (!field || !filterOperators[field.type || "text"]?.includes(rule.operator)) return false;
392
+ if (rule.operator === "is-empty") return true;
393
+ if (field.type === "number") return String(rule.value).trim() !== "" && Number.isFinite(Number(rule.value));
394
+ if (field.type === "date") return filterDate(rule.value);
395
+ if (field.type === "enum" && field.options) return field.options.some(option => String(option.id) === String(rule.value));
396
+ return true;
397
+ };
398
+ const refreshFilterRule = row => {
399
+ const field = filterDefinitions(row.closest(".bevel-filter")).find(item => item.id === row.dataset.bevelField);
400
+ if (!field) return;
401
+ const operator = row.querySelector("[data-bevel-filter-operator]"), previous = row.querySelector("[data-bevel-filter-value]");
402
+ if (!operator || !previous) return;
403
+ const choices = filterOperators[field.type || "text"], labels = { "starts-with": "starts with", "is-empty": "is empty", "is-not": "is not", eq: "=", ne: "≠", gt: ">", gte: "≥", lt: "<", lte: "≤" };
404
+ const selected = choices.includes(row.dataset.bevelOperator) ? row.dataset.bevelOperator : choices[0];
405
+ operator.replaceChildren(...choices.map(value => { const option = document.createElement("option"); option.value = value; option.textContent = labels[value] || value; option.selected = value === selected; option.toggleAttribute("selected", option.selected); return option; }));
406
+ row.dataset.bevelOperator = selected;
407
+ const value = previous.value, input = document.createElement(field.type === "enum" && field.options ? "select" : "input");
408
+ input.dataset.bevelFilterValue = ""; input.setAttribute("aria-label", `${field.label || field.id} filter value`);
409
+ if (input.tagName === "SELECT") {
410
+ input.className = "bevel-select__field";
411
+ for (const option of field.options) { const node = document.createElement("option"); node.value = String(option.id); node.textContent = option.label; input.append(node); }
412
+ input.value = field.options.some(option => String(option.id) === value) ? value : String(field.options[0]?.id || "");
413
+ for (const option of input.options) option.toggleAttribute("selected", option.selected);
414
+ } else { input.type = "text"; input.className = "bevel-input bevel-input--small"; input.value = value; input.setAttribute("value", value); if (field.type === "number") input.inputMode = "decimal"; }
415
+ const oldWrapper = previous.parentElement.classList.contains("bevel-select") ? previous.parentElement : previous;
416
+ if (input.tagName === "SELECT") { const wrapper = document.createElement("span"); wrapper.className = "bevel-select"; wrapper.append(input); oldWrapper.replaceWith(wrapper); }
417
+ else oldWrapper.replaceWith(input);
418
+ input.disabled = selected === "is-empty";
419
+ };
420
+ const filterMatches = (record, expression, fields=[]) => {
421
+ const definitions = new Map(fields.map(field=>[field.id,field])), rules=expression?.rules || [];
422
+ const test = rule => { const field=definitions.get(rule.field); if(!field)return false; const actual=record[rule.field], op=rule.operator, wanted=rule.value; if(op==="is-empty")return filterEmpty(actual); if(field.type==="number"){ if(filterEmpty(actual)||filterEmpty(wanted)||String(actual).trim()===""||String(wanted).trim()===""||!Number.isFinite(Number(actual))||!Number.isFinite(Number(wanted)))return false; const a=Number(actual),b=Number(wanted); return op==="eq"?a===b:op==="ne"?a!==b:op==="gt"?a>b:op==="gte"?a>=b:op==="lt"?a<b:op==="lte"?a<=b:false; } if(field.type==="date"){ if(!filterDate(actual)||!filterDate(wanted))return false; return op==="before"?actual<wanted:op==="on"?actual===wanted:op==="after"?actual>wanted:false; } if(field.type==="enum")return op==="is"?String(actual)===String(wanted):op==="is-not"?String(actual)!==String(wanted):false; const a=String(actual??"").toLowerCase(),b=String(wanted??"").toLowerCase(); return op==="equals"?a===b:op==="contains"?a.includes(b):op==="starts-with"?a.startsWith(b):false; };
423
+ return !rules.length || (expression.match==="any" ? rules.some(test) : rules.every(test));
424
+ };
425
+ const gridRecord = row => Object.fromEntries([...row.children].filter(cell=>cell.dataset.bevelColumn).map(cell=>[cell.dataset.bevelColumn,cell.dataset.value ?? cell.dataset.sort ?? cell.textContent.trim()]));
426
+ const gridFilter = grid => { const root=grid.id&&document.querySelector(`.bevel-filter[data-bevel-grid="${CSS.escape(grid.id)}"]`); if(!root)return null; return { expression:filterRead(root), fields:filterDefinitions(root) }; };
427
+ const searchedGridRows = grid => {
428
+ const query = (grid.querySelector(".bevel-grid__search")?.value || "").trim().toLowerCase(), activeFilter = gridFilter(grid);
429
+ return gridRows(grid).filter(row => (!query || row.textContent.toLowerCase().includes(query)) && (!activeFilter || filterMatches(gridRecord(row), activeFilter.expression, activeFilter.fields)));
430
+ };
431
+ const layoutGrid = (grid) => {
432
+ const size = Number(grid.dataset.pageSize) || 0;
433
+ const rows = gridRows(grid);
434
+ const searched = searchedGridRows(grid);
435
+ const view = grid.querySelector('.bevel-grid__view[aria-pressed="true"]');
436
+ const matches = new Set(searched.filter((row) => matchesView(row, view)));
437
+ const resultLabel = grid.closest(".bevel-grid-result")?.querySelector(".bevel-grid-result__label");
438
+ if (resultLabel) { const label = `Applied / ${matches.size} matching record${matches.size === 1 ? "" : "s"}`; if (resultLabel.textContent !== label) resultLabel.textContent = label; }
439
+
440
+ for (const tab of grid.querySelectorAll(".bevel-grid__view")) {
441
+ const count = tab.querySelector("span");
442
+ if (count) { const text = String(searched.filter(row => matchesView(row, tab)).length); if (count.textContent !== text) count.textContent = text; }
443
+ }
444
+ syncStats(grid, [...matches]);
445
+ const total = size ? Math.max(1, Math.ceil(matches.size / size)) : 1;
446
+ const current = Math.min(total, Math.max(1, Number(grid.dataset.page) || 1));
447
+ if (size && grid.dataset.page !== String(current)) grid.dataset.page = String(current);
448
+ const first = size ? (current - 1) * size : 0;
449
+ const last = size ? first + size : matches.size;
450
+ let i = 0;
451
+ for (const row of rows) {
452
+ const match = matches.has(row);
453
+ const show = match && i >= first && i < last;
454
+ if (match) i++;
455
+ if (row.hidden !== !show) row.hidden = !show;
456
+ const detail = detailOf(row);
457
+ if (detail && detail.hidden !== (!show || !isOpen(row))) detail.hidden = !show || !isOpen(row);
458
+ }
459
+ for (const group of grid.querySelectorAll(".bevel-grid__table > tbody > .bevel-grid__group")) {
460
+ let any = false;
461
+ for (let tr = group.nextElementSibling; tr && !tr.classList.contains("bevel-grid__group"); tr = tr.nextElementSibling) {
462
+ if (tr.classList.contains("bevel-grid__row") && !tr.hidden) { any = true; break; }
463
+ }
464
+ if (group.hidden !== !any) group.hidden = !any;
465
+ }
466
+ const empty = grid.querySelector(".bevel-grid__table > tbody > .bevel-grid__empty");
467
+ if (empty && rows.length && empty.hidden !== (matches.size > 0)) empty.hidden = matches.size > 0;
468
+ const pages = grid.querySelector(".bevel-grid__pages");
469
+ if (pages && size) renderPages(pages, total, current);
470
+ const range = grid.querySelector(".bevel-grid__range");
471
+ if (range) { const text = matches.size ? `${first + 1} to ${Math.min(last, matches.size)} of ${matches.size}` : "0 of 0"; if (range.textContent !== text) range.textContent = text; }
472
+ syncPick(grid);
473
+ if (grid.id) document.querySelectorAll(`.bevel-pagination[data-bevel-target="${CSS.escape(grid.id)}"]`).forEach(pager => refreshPager(pager));
474
+ };
475
+ const cellKey = (row, index, type) => {
476
+ const cell = row.children[index];
477
+ const raw = cell ? (cell.dataset.sort ?? cell.textContent.trim()) : "";
478
+ if (type === "number") return Number(raw.replace(/[^\d.-]/g, "")) || 0;
479
+ return raw.toLowerCase();
480
+ };
481
+ on("click", ".bevel-grid__sort", (e, button) => {
482
+ const th = button.closest("th");
483
+ const table = th?.closest(".bevel-grid__table");
484
+ const grid = table && gridOf(table);
485
+ if (!grid) return;
486
+ const dir = th.getAttribute("aria-sort") === "ascending" ? "descending" : "ascending";
487
+ if (grid.dataset.bevelDataMode === "remote") {
488
+ remoteRequest(grid, "sort", th.dataset.bevelColumn || String([...th.parentElement.children].indexOf(th)), th.getAttribute("aria-sort") || "none", dir);
489
+ return;
490
+ }
491
+ for (const h of table.querySelectorAll("thead [aria-sort]")) h.setAttribute("aria-sort", h === th ? dir : "none");
492
+ const index = [...th.parentElement.children].indexOf(th);
493
+ const type = th.dataset.type || "text";
494
+ const sign = dir === "ascending" ? 1 : -1;
495
+ const tbody = table.tBodies[0];
496
+ const segments = [{ head: [], units: [] }];
497
+ for (const tr of [...tbody.children]) {
498
+ const seg = segments[segments.length - 1];
499
+ if (tr.classList.contains("bevel-grid__group")) segments.push({ head: [tr], units: [] });
500
+ else if (tr.classList.contains("bevel-grid__row")) seg.units.push([tr]);
501
+ else if (seg.units.length) seg.units[seg.units.length - 1].push(tr);
502
+ else seg.head.push(tr);
503
+ }
504
+ for (const seg of segments) {
505
+ seg.units.sort((a, b) => {
506
+ const x = cellKey(a[0], index, type), y = cellKey(b[0], index, type);
507
+ return x < y ? -sign : x > y ? sign : 0;
508
+ });
509
+ tbody.append(...seg.head, ...seg.units.flat());
510
+ }
511
+ layoutGrid(grid);
512
+ emit(grid, "bevel:sort", { index, dir, th });
513
+ });
514
+ on("change", ".bevel-grid__table > thead .bevel-grid__pick input", (e, input) => {
515
+ const grid = gridOf(input);
516
+ if (!grid) return;
517
+ for (const i of grid.querySelectorAll(".bevel-grid__table > tbody .bevel-grid__pick input")) {
518
+ if (i.disabled || i.closest("tr").hidden) continue;
519
+ i.checked = input.checked;
520
+ i.toggleAttribute("checked", i.checked);
521
+ }
522
+ syncPick(grid);
523
+ emit(grid, "bevel:select", { all: input.checked });
524
+ });
525
+ on("change", ".bevel-grid__table > tbody .bevel-grid__pick input", (e, input) => {
526
+ const grid = gridOf(input);
527
+ if (!grid) return;
528
+ syncPick(grid);
529
+ emit(grid, "bevel:select", { row: input.closest("tr"), checked: input.checked });
530
+ });
531
+ on("click", ".bevel-grid__expand", (e, button) => {
532
+ const row = button.closest("tr");
533
+ const open = button.getAttribute("aria-expanded") !== "true";
534
+ button.setAttribute("aria-expanded", String(open));
535
+ const detail = detailOf(row) || document.getElementById(button.getAttribute("aria-controls") || "");
536
+ if (detail) detail.hidden = !open;
537
+ const grid = gridOf(button);
538
+ if (grid) emit(grid, "bevel:expand", { row, open });
539
+ });
540
+ const changeGridPage = (grid, token) => {
541
+ if (!grid) return false;
542
+ const current = Number(grid.dataset.page) || 1;
543
+ const want = token === "prev" ? current - 1 : token === "next" ? current + 1 : Number(token);
544
+ if (!want || want < 1) return false;
545
+ if (grid.dataset.bevelDataMode === "remote") {
546
+ const total = Number(grid.dataset.bevelTotal), size = Number(grid.dataset.pageSize) || 10;
547
+ if (grid.hasAttribute("data-bevel-total") && want > Math.max(1, Math.ceil(Math.max(0, total) / size))) return false;
548
+ remoteRequest(grid, "page", String(want), current, want);
549
+ return true;
550
+ }
551
+ grid.dataset.page = String(want);
552
+ layoutGrid(grid);
553
+ emit(grid, "bevel:page", { page: Number(grid.dataset.page) });
554
+ return true;
555
+ };
556
+ on("click", ".bevel-grid__page", (e, button) => {
557
+ if (button.disabled) return;
558
+ changeGridPage(gridOf(button), button.dataset.page);
559
+ });
560
+ on("click", ".bevel-grid__view", (e, button) => {
561
+ const grid = gridOf(button);
562
+ if (!grid) return;
563
+ if (grid.dataset.bevelDataMode === "remote") { remoteRequest(grid, "view", button.dataset.value || "", grid.querySelector('.bevel-grid__view[aria-pressed="true"]')?.dataset.value || "", button.dataset.value || ""); return; }
564
+ for (const view of grid.querySelectorAll(".bevel-grid__view")) view.setAttribute("aria-pressed", String(view === button));
565
+ if (grid.dataset.pageSize) grid.dataset.page = "1";
566
+ layoutGrid(grid);
567
+ const scroll = grid.querySelector(".bevel-grid__scroll");
568
+ if (scroll) scroll.scrollTop = 0;
569
+ emit(grid, "bevel:view", { value: button.dataset.value, button });
570
+ });
571
+ on("input", ".bevel-grid__search", (e, input) => {
572
+ const grid = gridOf(input);
573
+ if (!grid) return;
574
+ if (grid.dataset.bevelDataMode === "remote") { input.setAttribute("value", input.value); remoteRequest(grid, "search", "", grid.dataset.bevelQuery || "", input.value); return; }
575
+ if (grid.dataset.pageSize) grid.dataset.page = "1";
576
+ layoutGrid(grid);
577
+ emit(grid, "bevel:search", { query: input.value });
578
+ });
579
+
580
+ /* ---------- files table ---------- */
581
+ const filesPicks = (root) => [...root.querySelectorAll(".bevel-files__table > tbody .bevel-files__pick input")].filter((input) => !input.disabled);
582
+ const syncFiles = (root) => {
583
+ const head = root.querySelector(".bevel-files__table > thead .bevel-files__pick input");
584
+ if (!head) return;
585
+ const picks = filesPicks(root), count = picks.filter((input) => input.checked).length;
586
+ const checked = picks.length > 0 && count === picks.length, mixed = count > 0 && !checked;
587
+ if (head.checked !== checked) head.checked = checked;
588
+ if (head.indeterminate !== mixed) head.indeterminate = mixed;
589
+ if (head.hasAttribute("checked") !== checked) head.toggleAttribute("checked", checked);
590
+ };
591
+ on("change", ".bevel-files__table > thead .bevel-files__pick input", (e, input) => {
592
+ const root = input.closest(".bevel-files");
593
+ if (!root) return;
594
+ for (const pick of filesPicks(root)) {
595
+ pick.checked = input.checked;
596
+ pick.toggleAttribute("checked", pick.checked);
597
+ }
598
+ syncFiles(root);
599
+ emit(root, "bevel:select", { all: input.checked });
600
+ });
601
+ on("change", ".bevel-files__table > tbody .bevel-files__pick input", (e, input) => {
602
+ const root = input.closest(".bevel-files");
603
+ if (!root) return;
604
+ const row = input.closest("tr");
605
+ syncFiles(root);
606
+ emit(root, "bevel:select", { row, key: row?.dataset.bevelKey ?? "", checked: input.checked });
607
+ });
608
+ on("click", ".bevel-files__sort", (e, button) => {
609
+ const th = button.closest("th"), root = button.closest(".bevel-files");
610
+ if (!th || !root) return;
611
+ const direction = th.getAttribute("aria-sort") === "ascending" ? "descending" : "ascending";
612
+ for (const head of th.parentElement.querySelectorAll("th[aria-sort]")) head.setAttribute("aria-sort", head === th ? direction : "none");
613
+ emit(root, "bevel:sort", { column: th.dataset.bevelColumn ?? "", direction, th });
614
+ });
615
+ on("click", ".bevel-files__row", (e, row) => {
616
+ if (!row.contains(e.target) || e.target.closest('a, button, input, label, select, textarea, summary, [role="button"], [role="menuitem"], .bevel-files__pick')) return;
617
+ row.querySelector(".bevel-files__title")?.click();
618
+ });
619
+
620
+ /* ---------- functional component foundation ---------- */
621
+ const sessions = new Map();
622
+ let overlaySequence = Date.now();
623
+ const uploadFiles = new WeakMap();
624
+ const uploadSize = bytes => bytes < 1024 ? `${bytes} B` : `${Math.round(bytes / 1024)} KB`;
625
+ const wizardForms = new Map();
626
+ const rootFor = (el, selector) => el?.closest(selector);
627
+ const disabled = (el) => !el || el.disabled || el.readOnly || el.getAttribute("aria-disabled") === "true" || !!el.closest("fieldset:disabled");
628
+ const targetById = (id) => id ? document.getElementById(id) : null;
629
+ const componentName = (root) => [...root.classList].find((name) => /^bevel-(modal|menu|drawer|popconfirm|combobox|multiselect|choice-card|dropzone|inline-edit|settings|errors|split-button|app-shell|sidebar|toolbar|bulk-bar|breadcrumbs|pagination|file-tree|tree|wizard|action-banner|code|diff|activity|stats|avatars|chart|filter|columns|split|richtext|kanban|grid|files)$/.test(name))?.slice(6) || "component";
630
+ const jsonValue = (value) => value == null || typeof value !== "object" ? value : JSON.parse(JSON.stringify(value));
631
+ const transition = (root, action, key, previous, next, mutate, source = "user") => {
632
+ const detail = { component: componentName(root), action, key: String(key ?? ""), previous: jsonValue(previous), next: jsonValue(next) };
633
+ if (!root.dispatchEvent(new CustomEvent("bevel:beforechange", { bubbles: true, cancelable: true, detail }))) return false;
634
+ mutate();
635
+ if (source !== "external") emit(root, "bevel:change", detail);
636
+ return true;
637
+ };
638
+ const action = (root, name, key = "") => emit(root, "bevel:action", { component: componentName(root), action: name, key: String(key) });
639
+ const error = (root, code, message) => {
640
+ emit(root, "bevel:error", { component: componentName(root), code, message });
641
+ console.error(`[Bevel ${code}] ${message}`);
642
+ };
643
+ const focusable = (root) => [...root.querySelectorAll("button,a[href],input,select,textarea,[tabindex]")].find((el) => !disabled(el) && el.tabIndex >= 0 && !el.hidden);
644
+ const panelTrigger = (panel) => {
645
+ const id = panel.id;
646
+ return id ? document.querySelector(`[role="combobox"][aria-controls="${CSS.escape(id)}"],[data-bevel-open="${CSS.escape(id)}"]`) : null;
647
+ };
648
+ const setExpanded = (panel, value) => {
649
+ if (!panel.id) return;
650
+ for (const control of document.querySelectorAll(`[aria-controls="${CSS.escape(panel.id)}"]`)) control.setAttribute("aria-expanded", String(value));
651
+ };
652
+ const isModal = (root) => root.matches("dialog.bevel-modal, dialog.bevel-drawer");
653
+ const isAnchored = (root) => root.matches(".bevel-menu,.bevel-popconfirm,.bevel-combobox__listbox,.bevel-multiselect__panel");
654
+ const opened = () => [...document.querySelectorAll("[data-bevel-open]:is(dialog,[popover])")].filter((el) => el.hasAttribute("open") || el.matches(":popover-open")).sort((a, b) => (sessions.get(a)?.openOrder || Number(a.dataset.bevelOpenOrder) || 0) - (sessions.get(b)?.openOrder || Number(b.dataset.bevelOpenOrder) || 0));
655
+ const position = (panel, trigger) => {
656
+ const floating = window.FloatingUIDOM;
657
+ if (!floating) {
658
+ error(panel, "missing-positioner", "Anchored components require the vendored Floating UI core and DOM scripts.");
659
+ return false;
660
+ }
661
+ let active = true;
662
+ const middleware = [floating.offset(8), floating.flip(), floating.shift({ padding: 8 }), floating.size({ padding: 8, apply({ availableHeight, elements }) { if (active) elements.floating.style.setProperty("--bevel-available-height", `${Math.max(120, availableHeight)}px`); } })];
663
+ const arrow = panel.querySelector("[data-bevel-arrow]");
664
+ if (arrow) middleware.push(floating.arrow({ element: arrow }));
665
+ const reference = panel.matches(".bevel-combobox__listbox,.bevel-multiselect__panel") ? trigger.closest(".bevel-combobox__control,.bevel-multiselect__control") || trigger : trigger;
666
+ const update = () => floating.computePosition(reference, panel, { strategy: "fixed", placement: panel.dataset.bevelPlacement || "bottom-start", middleware }).then(({ x, y, placement, middlewareData }) => {
667
+ if (!active || !panel.isConnected) return;
668
+ panel.style.left = `${x}px`; panel.style.top = `${y}px`; panel.dataset.bevelActualPlacement = placement;
669
+ if (arrow && middlewareData.arrow) {
670
+ arrow.style.left = middlewareData.arrow.x == null ? "" : `${middlewareData.arrow.x}px`;
671
+ arrow.style.top = middlewareData.arrow.y == null ? "" : `${middlewareData.arrow.y}px`;
672
+ }
673
+ });
674
+ const stop = floating.autoUpdate(reference, panel, update);
675
+ const cleanup = () => { active = false; stop(); };
676
+ panel.dataset.bevelGeometry = "";
677
+ sessions.set(panel, { ...(sessions.get(panel) || {}), cleanup, trigger });
678
+ return true;
679
+ };
680
+ const closeOverlay = (root, { reason = "api", source = "user", focus = true } = {}) => {
681
+ if (!root || !(root.hasAttribute("open") || root.matches(":popover-open") || root.hasAttribute("data-bevel-open"))) return false;
682
+ const session = sessions.get(root);
683
+ const commit = () => {
684
+ session?.cleanup?.(); sessions.delete(root);
685
+ root.removeAttribute("data-bevel-open");
686
+ if (root.matches("dialog") && root.open) root.close();
687
+ else if (root.matches("[popover]") && root.matches(":popover-open")) root.hidePopover();
688
+ const trigger = session?.trigger || panelTrigger(root);
689
+ trigger?.setAttribute("aria-expanded", "false");
690
+ setExpanded(root, false);
691
+ root.style.removeProperty("left"); root.style.removeProperty("top"); root.style.removeProperty("--bevel-available-height");
692
+ root.removeAttribute("data-bevel-geometry");
693
+ if (focus && trigger?.isConnected && !disabled(trigger)) trigger.focus();
694
+ };
695
+ return transition(root, "close", root.id, true, false, commit, source);
696
+ };
697
+ const openOverlay = (root, { trigger = panelTrigger(root), source = "user" } = {}) => {
698
+ if (!root || root.hasAttribute("open") || root.matches(":popover-open")) return false;
699
+ if (!(root instanceof HTMLDialogElement) && !root.matches("[popover]")) { error(root, "unsupported-overlay", "Overlay roots must be a dialog or manual popover."); return false; }
700
+ if (isAnchored(root) && (!trigger || disabled(trigger))) return false;
701
+ if (root.matches(".bevel-combobox__listbox") && (disabled(root.closest(".bevel-combobox")) || disabled(root.closest(".bevel-combobox").querySelector('[role="combobox"]')))) return false;
702
+ if (isAnchored(root) && !window.FloatingUIDOM) { error(root, "missing-positioner", "Anchored components require the vendored Floating UI core and DOM scripts."); return false; }
703
+ if (isModal(root)) {
704
+ for (const panel of opened().filter((el) => isAnchored(el) && !root.contains(el))) if (!closeOverlay(panel, { reason: "modal-open", focus: false })) return false;
705
+ }
706
+ const commit = () => {
707
+ root.setAttribute("data-bevel-open", "");
708
+ if (isModal(root)) {
709
+ root.dataset.bevelOpenOrder = String(++overlaySequence);
710
+ root.showModal();
711
+ } else {
712
+ root.showPopover();
713
+ position(root, trigger);
714
+ }
715
+ sessions.set(root, { ...(sessions.get(root) || {}), trigger, openOrder: ++overlaySequence });
716
+ if (trigger) trigger.setAttribute("aria-expanded", "true");
717
+ setExpanded(root, true);
718
+ if (!root.matches(".bevel-combobox__listbox")) requestAnimationFrame(() => (root.querySelector("[autofocus]") || focusable(root))?.focus());
719
+ };
720
+ return transition(root, "open", root.id, false, true, commit, source);
721
+ };
722
+
723
+ const requestScopes = new Map();
724
+ let requestSequence = 0;
725
+ const remoteRequest = (root, name, key, previous, next, scope = name) => {
726
+ const requestId = `bevel-${Date.now().toString(36)}-${++requestSequence}`;
727
+ const scopes = requestScopes.get(root) || new Map();
728
+ scopes.set(scope, requestId); requestScopes.set(root, scopes);
729
+ root.toggleAttribute("data-bevel-pending", true);
730
+ emit(root, "bevel:request", { component: componentName(root), action: name, key: String(key ?? ""), requestId, revision: root.dataset.bevelRevision || null, previous: jsonValue(previous), next: jsonValue(next) });
731
+ return requestId;
732
+ };
733
+ const localOrRemote = (root, name, key, previous, next, mutate, scope) => root.dataset.bevelDataMode === "remote"
734
+ ? (remoteRequest(root, name, key, previous, next, scope), true)
735
+ : transition(root, name, key, previous, next, mutate);
736
+
737
+ const refreshCombo = (root, restore = false) => {
738
+ const input = root.querySelector('[role="combobox"]');
739
+ const hidden = root.querySelector('input[type="hidden"]');
740
+ const list = root.querySelector('[role="listbox"]');
741
+ if (!input || !list) return;
742
+ const key = hidden?.value || root.dataset.bevelValue || "";
743
+ const option = [...list.querySelectorAll('[role="option"]')].find((el) => el.dataset.bevelKey === key && el.getAttribute("aria-disabled") !== "true");
744
+ for (const el of list.querySelectorAll('[role="option"]')) { const selected = String(el === option); if (el.getAttribute("aria-selected") !== selected) el.setAttribute("aria-selected", selected); }
745
+ if (root.dataset.bevelValue !== key) root.dataset.bevelValue = key;
746
+ if (hidden && hidden.getAttribute("value") !== key) hidden.setAttribute("value", key);
747
+ const label = option?.dataset.bevelLabel || option?.textContent.trim() || "";
748
+ if (restore || document.activeElement !== input || !input.value) { input.value = label; if (input.getAttribute("value") !== label) input.setAttribute("value", label); }
749
+ input.setCustomValidity(input.required && (!option || input.value !== label) ? "Choose an option." : "");
750
+ };
751
+ const refreshMulti = (root) => {
752
+ const name = root.dataset.bevelName || "values[]";
753
+ const selected = [...root.querySelectorAll('.bevel-multiselect__panel input[type="checkbox"]:checked')];
754
+ const wanted = selected.map((checkbox) => checkbox.value);
755
+ const generated = [...root.querySelectorAll(':scope > input[type="hidden"][data-bevel-generated]')];
756
+ if (generated.length !== wanted.length || generated.some((input, index) => input.name !== name || input.value !== wanted[index])) {
757
+ generated.forEach((input) => input.remove());
758
+ for (const value of wanted) {
759
+ const hidden = document.createElement("input"); hidden.type = "hidden"; hidden.name = name; hidden.value = value; hidden.dataset.bevelGenerated = "multiselect"; root.append(hidden);
760
+ }
761
+ }
762
+ for (const checkbox of root.querySelectorAll('.bevel-multiselect__panel input[type="checkbox"]')) if (checkbox.hasAttribute("checked") !== checkbox.checked) checkbox.toggleAttribute("checked", checkbox.checked);
763
+ const summary = root.querySelector(".bevel-multiselect__summary"), text = selected.length ? `${selected.length} selected` : "Choose options"; if (summary && summary.textContent !== text) summary.textContent = text;
764
+ for (const count of root.querySelectorAll("[data-bevel-multiselect-count]")) if (count.textContent !== String(selected.length)) count.textContent = String(selected.length);
765
+ const tags = root.querySelector(".bevel-multiselect__tags");
766
+ if (tags) {
767
+ const present = [...tags.querySelectorAll(".bevel-multiselect__tag")].map((tag) => tag.dataset.bevelKey);
768
+ if (present.length !== wanted.length || present.some((key, index) => key !== wanted[index])) {
769
+ tags.replaceChildren(...selected.map((checkbox) => {
770
+ const tag = document.createElement("span"); tag.className = "bevel-multiselect__tag"; tag.dataset.bevelGenerated = "multiselect-tag"; tag.dataset.bevelKey = checkbox.value;
771
+ const label = document.createElement("span"); label.textContent = checkbox.dataset.bevelLabel || checkbox.closest("label")?.textContent.trim() || checkbox.value;
772
+ if (checkbox.dataset.bevelTone) { const dot = document.createElement("i"); dot.className = `bevel-label-dot bevel-label-dot--${checkbox.dataset.bevelTone}`; dot.setAttribute("aria-hidden", "true"); tag.append(dot); }
773
+ tag.append(label);
774
+ if (!disabled(checkbox)) { const remove = document.createElement("button"); remove.type = "button"; remove.dataset.bevelMultiselectRemove = checkbox.value; remove.setAttribute("aria-label", `Remove ${label.textContent}`); remove.innerHTML = '<svg aria-hidden="true" height="11" width="11" viewBox="0 0 256 256" fill="currentColor"><path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"/></svg>'; tag.append(remove); }
775
+ return tag;
776
+ }));
777
+ }
778
+ }
779
+ };
780
+ const splitSessions = new Map();
781
+ const splitMedia = matchMedia("(max-width: 640px)");
782
+ const splitValue = root => Number(root.getAttribute(root.hasAttribute("data-bevel-split") ? "data-bevel-split" : "data-bevel-ratio")) || 50;
783
+ const splitBounds = root => {
784
+ const vertical = root.dataset.bevelOrientation === "vertical", extent = vertical ? root.clientHeight : root.clientWidth;
785
+ const divider = root.querySelector(":scope > .bevel-split__divider"), gap = divider ? vertical ? divider.offsetHeight : divider.offsetWidth : 10;
786
+ const first = Math.max(0, Number(root.dataset.bevelMinFirst) || 180), second = Math.max(0, Number(root.dataset.bevelMinSecond) || 180);
787
+ if (extent <= 0) return [25, 75];
788
+ if (extent - gap < first + second) { const value = 100 * Math.max(0, extent - gap) * first / ((first + second) * extent); return [value, value]; }
789
+ const low = Math.max(25, first / extent * 100), high = Math.min(75, (extent - gap - second) / extent * 100);
790
+ if (low > high) { const value = Math.min((extent - gap - second) / extent * 100, Math.max(first / extent * 100, 50)); return [value, value]; }
791
+ return [low, high];
792
+ };
793
+ const splitClamped = (root, value) => { const [min, max] = splitBounds(root); return Math.min(max, Math.max(min, value)); };
794
+ const refreshSplit = root => {
795
+ const divider = root.querySelector(":scope > .bevel-split__divider");
796
+ if (!divider) return;
797
+ let session = splitSessions.get(root);
798
+ if (!session) {
799
+ session = { drag: null, observer: new ResizeObserver(() => { if (root.isConnected) refreshSplit(root); }) };
800
+ splitSessions.set(root, session); session.observer.observe(root);
801
+ }
802
+ const stacked = splitMedia.matches, [min, max] = splitBounds(root), value = splitClamped(root, splitValue(root)), inactive = stacked || min === max || root.matches('[aria-disabled="true"],[disabled]');
803
+ if (session.drag && (inactive || !root.isConnected || splitValue(root) !== session.drag.previous || root.dataset.bevelOrientation !== session.drag.orientation)) session.drag.stop(false);
804
+ if (!session.drag) { const size = `${value}%`; if (root.style.getPropertyValue("--bevel-split-size") !== size) root.style.setProperty("--bevel-split-size", size); }
805
+ const attrs = { role: "separator", "aria-orientation": root.dataset.bevelOrientation === "vertical" ? "horizontal" : "vertical", "aria-valuemin": String(Math.round(min * 100) / 100), "aria-valuemax": String(Math.round(max * 100) / 100), "aria-valuenow": String(Math.round(value * 100) / 100), "aria-disabled": String(inactive), tabindex: inactive ? "-1" : "0" };
806
+ for (const [name, text] of Object.entries(attrs)) if (divider.getAttribute(name) !== text) divider.setAttribute(name, text);
807
+ if (divider.hidden !== stacked) divider.hidden = stacked;
808
+ const caption = root.nextElementSibling;
809
+ if (caption?.matches(".bevel-split__caption")) { const text = caption.querySelector("output"), label = `${root.dataset.bevelLabel || "List"} ${Math.round(value)}% · minimum ${Math.round(min)}% · drag the divider`; if (text && text.textContent !== label) text.textContent = label; }
810
+ };
811
+ const commitSplit = (root, next) => {
812
+ const previous = splitValue(root);
813
+ if (previous === next) return false;
814
+ return transition(root, "resize", root.dataset.bevelKey || "", previous, next, () => { root.setAttribute(root.hasAttribute("data-bevel-split") ? "data-bevel-split" : "data-bevel-ratio", String(next)); refreshSplit(root); });
815
+ };
816
+ splitMedia.addEventListener("change", () => { for (const root of splitSessions.keys()) if (root.isConnected) refreshSplit(root); });
817
+ const codeFeedback = new Map();
818
+ const clearCodeFeedback = (root) => {
819
+ const session = codeFeedback.get(root);
820
+ if (session?.timer) clearTimeout(session.timer);
821
+ codeFeedback.delete(root);
822
+ root.querySelectorAll('[data-bevel-transient="clipboard"]').forEach(node => node.remove());
823
+ };
824
+ const refreshCode = (root) => {
825
+ const wrapped = root.hasAttribute("data-bevel-wrap");
826
+ for (const button of root.querySelectorAll("[data-bevel-code-wrap]")) if (button.getAttribute("aria-pressed") !== String(wrapped)) button.setAttribute("aria-pressed", String(wrapped));
827
+ };
828
+ const diffVersions = new WeakMap();
829
+ const refreshDiff = (root) => {
830
+ const template = root.querySelector("template[data-bevel-diff-records]");
831
+ if (!template) return;
832
+ const layout = root.dataset.bevelLayout === "split" ? "split" : "unified";
833
+ if (root.dataset.bevelLayout !== layout) root.dataset.bevelLayout = layout;
834
+ for (const button of root.querySelectorAll("button[data-bevel-layout]")) if (button.getAttribute("aria-pressed") !== String(button.dataset.bevelLayout === layout)) button.setAttribute("aria-pressed", String(button.dataset.bevelLayout === layout));
835
+ const signature = `${layout}:${template.innerHTML}:${root.dataset.bevelFilename || ""}:${root.dataset.bevelHunk || ""}`;
836
+ if (diffVersions.get(root) === signature && root.querySelector('[data-bevel-generated="diff"]')) return;
837
+ const records = [...template.content.querySelectorAll("[data-bevel-kind]")];
838
+ for (const count of root.querySelectorAll(".bevel-diff__count[data-bevel-kind]")) {
839
+ const kind = count.dataset.bevelKind, text = `${kind === "remove" ? "−" : "+"}${records.filter(record => record.dataset.bevelKind === kind).length}`;
840
+ if (count.textContent !== text) count.textContent = text;
841
+ }
842
+ const line = (record, side) => {
843
+ const row = document.createElement("div"), kind = record.dataset.bevelKind, code = record.querySelector("code");
844
+ row.className = "bevel-diff__line"; row.dataset.bevelKind = kind; row.dataset.bevelKey = record.dataset.bevelKey || "";
845
+ row.setAttribute("role", "listitem");
846
+ const number = document.createElement("span"), symbol = document.createElement("span"), label = document.createElement("span");
847
+ number.textContent = record.dataset[side === "before" || kind === "remove" ? "bevelOld" : "bevelNew"] || ""; number.setAttribute("aria-hidden", "true");
848
+ symbol.textContent = kind === "add" ? "+" : kind === "remove" ? "−" : " "; symbol.setAttribute("aria-hidden", "true");
849
+ label.className = "bevel-sr-only"; label.textContent = `${kind === "add" ? "Added" : kind === "remove" ? "Removed" : "Unchanged"}, line ${number.textContent}: `;
850
+ row.append(number, symbol, label, code ? code.cloneNode(true) : document.createElement("code"));
851
+ return row;
852
+ };
853
+ const body = document.createElement("div"); body.className = "bevel-diff__body"; body.dataset.bevelGenerated = "diff";
854
+ if (layout === "unified") {
855
+ const hunk = document.createElement("div"); hunk.className = "bevel-diff__hunk"; hunk.textContent = root.dataset.bevelHunk || "";
856
+ const list = document.createElement("div"); list.setAttribute("role", "list"); list.setAttribute("aria-label", "Changes");
857
+ list.append(...records.map(record => line(record))); body.append(hunk, list);
858
+ } else for (const side of ["before", "after"]) {
859
+ const section = document.createElement("section"), title = document.createElement("h3"), filename = document.createElement("small"), list = document.createElement("div");
860
+ title.textContent = side === "before" ? "Before " : "After "; filename.textContent = root.dataset.bevelFilename || ""; title.append(filename);
861
+ list.setAttribute("role", "list"); list.setAttribute("aria-label", side === "before" ? "Before" : "After");
862
+ list.append(...records.filter(record => record.dataset.bevelKind !== (side === "before" ? "add" : "remove")).map(record => line(record, side)));
863
+ section.append(title, list); body.append(section);
864
+ }
865
+ const old = root.querySelector('[data-bevel-generated="diff"]');
866
+ if (old) old.replaceWith(body); else template.after(body);
867
+ diffVersions.set(root, signature);
868
+ };
869
+ const refreshPager = (root, notifyClamp = false) => {
870
+ const target = root.dataset.bevelTarget && document.getElementById(root.dataset.bevelTarget), grid = target?.matches(".bevel-grid") ? target : null, items = target && !grid ? [...target.children] : [], remote = grid?.dataset.bevelDataMode === "remote";
871
+ if (grid) {
872
+ const page = grid.dataset.page || "1", size = grid.dataset.pageSize || "10";
873
+ if (root.dataset.bevelPage !== page) root.dataset.bevelPage = page;
874
+ if (root.dataset.bevelPageSize !== size) root.dataset.bevelPageSize = size;
875
+ if (remote) {
876
+ if (grid.hasAttribute("data-bevel-total")) {
877
+ if (root.dataset.bevelTotal !== grid.dataset.bevelTotal) root.dataset.bevelTotal = grid.dataset.bevelTotal;
878
+ }
879
+ else if (root.hasAttribute("data-bevel-total")) root.removeAttribute("data-bevel-total");
880
+ } else {
881
+ const view = grid.querySelector('.bevel-grid__view[aria-pressed="true"]');
882
+ const total = String(searchedGridRows(grid).filter(row => matchesView(row, view)).length);
883
+ if (root.dataset.bevelTotal !== total) root.dataset.bevelTotal = total;
884
+ }
885
+ }
886
+ if (target && !grid && root.dataset.bevelTotal !== String(items.length)) root.dataset.bevelTotal = String(items.length);
887
+ const knownTotal = root.hasAttribute("data-bevel-total"), page = Math.max(1, Number(root.dataset.bevelPage) || 1), size = Math.max(1, Number(root.dataset.bevelPageSize) || 10), total = knownTotal ? Math.max(0, Number(root.dataset.bevelTotal) || 0) : null, pages = total == null ? null : Math.max(1, Math.ceil(total / size));
888
+ const current = pages == null ? page : Math.min(page, pages), normalized = String(current), clamped = current !== page; if (root.dataset.bevelPage !== normalized) root.dataset.bevelPage = normalized;
889
+ const previous = root.querySelector('[data-bevel-page="prev"]'), next = root.querySelector('[data-bevel-page="next"]');
890
+ if (previous?.hasAttribute("disabled") !== (current <= 1)) previous?.toggleAttribute("disabled", current <= 1);
891
+ if (next?.hasAttribute("disabled") !== (pages != null && current >= pages)) next?.toggleAttribute("disabled", pages != null && current >= pages);
892
+ for (const control of root.querySelectorAll("select[data-bevel-page-size]")) {
893
+ if (control.value !== String(size)) control.value = String(size);
894
+ for (const option of control.options) if (option.hasAttribute("selected") !== option.selected) option.toggleAttribute("selected", option.selected);
895
+ }
896
+ const list = root.querySelector(".bevel-pagination__pages");
897
+ if (list) {
898
+ if (pages == null) {
899
+ for (const item of [...list.children]) if (!item.matches('[data-bevel-page="prev"],[data-bevel-page="next"]')) item.remove();
900
+ }
901
+ else renderStandalonePages(list, pages, current);
902
+ }
903
+ const out = root.querySelector(".bevel-pagination__range"), label = total == null ? `${(current - 1) * size + 1} to ${current * size}` : total ? `${(current - 1) * size + 1} to ${Math.min(current * size, total)} of ${total}` : "0 of 0"; if (out && out.textContent !== label) out.textContent = label;
904
+ if (items.length) { const first = (current - 1) * size, last = first + size; items.forEach((item, index) => { const hidden = index < first || index >= last; if (item.hidden !== hidden) item.hidden = hidden; }); }
905
+ if (clamped && notifyClamp) emit(root, "bevel:change", { component: "pagination", action: "page", key: String(current), previous: page, next: current });
906
+ };
907
+ const treeItems = (root) => [...root.querySelectorAll('[role="treeitem"][data-bevel-key], button[data-bevel-key], summary[data-bevel-key], a[data-bevel-key]')].filter((item) => item.closest(".bevel-tree") === root);
908
+ const treeVisible = (root) => treeItems(root).filter((item) => {
909
+ if (disabled(item) || item.hidden || item.closest("[hidden]")) return false;
910
+ for (let branch = item.parentElement?.closest("details"); branch && root.contains(branch); branch = branch.parentElement?.closest("details")) if (!branch.open && item !== branch.querySelector(":scope > summary")) return false;
911
+ return true;
912
+ });
913
+ const refreshTree = (root) => {
914
+ if (root.getAttribute("role") !== "tree") root.setAttribute("role", "tree");
915
+ const items = treeItems(root), selected = root.dataset.bevelSelected || "";
916
+ for (const branch of root.querySelectorAll("details")) { if (branch.closest(".bevel-tree") !== root) continue; const value = String(branch.open), summary = branch.querySelector(":scope > summary"); if (branch.dataset.bevelExpanded !== value) branch.dataset.bevelExpanded = value; if (summary && summary.getAttribute("aria-expanded") !== value) summary.setAttribute("aria-expanded", value); }
917
+ for (const item of items) { if (item.getAttribute("role") !== "treeitem") item.setAttribute("role", "treeitem"); const value = String(item.dataset.bevelKey === selected); if (item.getAttribute("aria-selected") !== value) item.setAttribute("aria-selected", value); }
918
+ const visible = treeVisible(root), current = visible.find((item) => item === document.activeElement) || visible.find((item) => item.tabIndex === 0) || visible.find((item) => item.dataset.bevelKey === selected) || visible[0];
919
+ for (const item of items) { const value = item === current ? "0" : "-1"; if (item.getAttribute("tabindex") !== value) item.setAttribute("tabindex", value); }
920
+ };
921
+ const refreshSidebar = (root) => {
922
+ const collapsed = root.hasAttribute("data-bevel-collapsed");
923
+ for (const button of root.querySelectorAll("[data-bevel-sidebar-toggle]")) { const expanded = String(!collapsed), label = collapsed ? "Expand sidebar" : "Collapse sidebar"; if (button.getAttribute("aria-expanded") !== expanded) button.setAttribute("aria-expanded", expanded); if (button.getAttribute("aria-label") !== label) button.setAttribute("aria-label", label); if (button.title !== label) button.title = label; }
924
+ };
925
+ const refreshShell = (root) => {
926
+ const open = String(root.hasAttribute("data-bevel-nav-open"));
927
+ for (const button of root.querySelectorAll("[data-bevel-shell-nav]")) if (button.getAttribute("aria-expanded") !== open) button.setAttribute("aria-expanded", open);
928
+ for (const side of root.querySelectorAll(".bevel-app-shell__side")) {
929
+ const collapsed = side.hasAttribute("data-bevel-collapsed"), expanded = String(!collapsed);
930
+ for (const button of side.querySelectorAll("[data-bevel-shell-collapse]")) { const label = collapsed ? button.dataset.bevelLabelExpand || "Expand sidebar" : button.dataset.bevelLabelCollapse || "Collapse sidebar"; if (button.getAttribute("aria-expanded") !== expanded) button.setAttribute("aria-expanded", expanded); if (button.getAttribute("aria-label") !== label) button.setAttribute("aria-label", label); if (button.title !== label) button.title = label; }
931
+ }
932
+ };
933
+ const wizardPanels = (root) => [...root.querySelectorAll(".bevel-wizard__panel")].filter((panel) => panel.closest(".bevel-wizard") === root);
934
+ const wizardKeyed = (panels) => panels.some((panel) => panel.hasAttribute("data-bevel-key"));
935
+ const wizardKey = (panels, panel, index = panels.indexOf(panel)) => wizardKeyed(panels) ? panel.dataset.bevelKey : String(index);
936
+ const wizardControls = (panel) => [...panel.querySelectorAll("input,select,textarea")].filter((control) => control.closest(".bevel-wizard__panel") === panel && control.closest(".bevel-wizard") === panel.closest(".bevel-wizard"));
937
+ const invalidControls = (controls) => [...controls].filter((control) => !disabled(control) && !control.closest('.bevel-wizard__panel[disabled],.bevel-wizard__panel[aria-disabled="true"]') && control.willValidate && !control.validity.valid);
938
+ const wizardInvalid = (panels) => invalidControls(panels.flatMap(wizardControls));
939
+ const setupWizardForm = (root) => {
940
+ const form = root.closest("form");
941
+ if (!form?.matches("[data-bevel-validate]")) return;
942
+ let session = wizardForms.get(form);
943
+ if (!session) {
944
+ session = { authoredNoValidate: form.noValidate, roots: new Set(), initialSteps: new Map() };
945
+ wizardForms.set(form, session);
946
+ form.noValidate = true;
947
+ }
948
+ session.roots.add(root);
949
+ if (!session.initialSteps.has(root)) session.initialSteps.set(root, root.dataset.bevelStep);
950
+ };
951
+ const refreshWizard = (root) => {
952
+ const panels = wizardPanels(root), keyed = wizardKeyed(panels);
953
+ const key = (panel, index) => keyed ? panel.dataset.bevelKey : String(index);
954
+ let index = panels.findIndex((panel, i) => key(panel, i) === String(root.dataset.bevelStep ?? "0") && !disabled(panel));
955
+ if (index < 0) index = panels.findIndex((panel) => !disabled(panel));
956
+ if (index < 0) return;
957
+ const active = key(panels[index], index); if (root.dataset.bevelStep !== active) root.dataset.bevelStep = active;
958
+ panels.forEach((panel, i) => { if (panel.hidden !== (i !== index)) panel.hidden = i !== index; panel.toggleAttribute("inert", i !== index); });
959
+ root.querySelectorAll(":scope > .bevel-wizard__steps > .bevel-wizard__step").forEach((el, i) => {
960
+ const state = i < index ? "done" : i === index ? "current" : "next";
961
+ if (el.dataset.bevelState !== state) el.dataset.bevelState = state;
962
+ if (i === index && el.getAttribute("aria-current") !== "step") el.setAttribute("aria-current", "step"); else if (i !== index) el.removeAttribute("aria-current");
963
+ const panel = panels[i];
964
+ el.toggleAttribute("disabled", !panel || disabled(panel));
965
+ const inactive = String(!panel || disabled(panel));
966
+ if (el.getAttribute("aria-disabled") !== inactive) el.setAttribute("aria-disabled", inactive);
967
+ const note = el.querySelector(".bevel-wizard__step-note");
968
+ const label = i < index ? "Complete" : i === index ? "Current step" : "Up next";
969
+ if (note && note.textContent !== label) note.textContent = label;
970
+ });
971
+ setupWizardForm(root);
972
+ };
973
+ const revealWizardControl = (control) => {
974
+ const panel = control?.closest(".bevel-wizard__panel"), root = panel?.closest(".bevel-wizard");
975
+ if (!panel || !root) return false;
976
+ const panels = wizardPanels(root);
977
+ root.dataset.bevelStep = wizardKey(panels, panel);
978
+ refreshWizard(root);
979
+ return true;
980
+ };
981
+ const setFieldError = (message, text) => {
982
+ if (!message) return;
983
+ (message.querySelector("[data-bevel-error-message]") || message).textContent = text;
984
+ message.hidden = !text;
985
+ };
986
+ const refreshErrors = (form, controls = [...form.elements]) => {
987
+ const summary = form.querySelector(".bevel-errors"), list = summary?.querySelector(".bevel-errors__list");
988
+ const candidates = new Set(controls);
989
+ const invalid = invalidControls(candidates);
990
+ if (!summary || !list) return invalid;
991
+ const reported = new Set(invalid);
992
+ for (const control of form.elements) if (control instanceof HTMLElement && candidates.has(control) && control.willValidate && !reported.has(control)) {
993
+ control.removeAttribute("aria-invalid");
994
+ const authored = (control.dataset.bevelAuthoredDescribedby || "").split(/\s+/).filter(Boolean);
995
+ if (control.dataset.bevelErrorId) {
996
+ setFieldError(document.getElementById(control.dataset.bevelErrorId), "");
997
+ const ids = (control.getAttribute("aria-describedby") || "").split(/\s+/).filter((id) => id !== control.dataset.bevelErrorId);
998
+ if (ids.length) control.setAttribute("aria-describedby", ids.join(" ")); else control.removeAttribute("aria-describedby");
999
+ }
1000
+ if (authored.length) control.setAttribute("aria-describedby", authored.join(" "));
1001
+ }
1002
+ list.replaceChildren(...invalid.map((control, index) => {
1003
+ if (!control.id) control.id = `${form.id || "bevel-form"}-invalid-${index + 1}`;
1004
+ control.setAttribute("aria-invalid", "true");
1005
+ let message = form.querySelector(`[data-bevel-error-for="${CSS.escape(control.id)}"]`);
1006
+ if (!message) { message = document.createElement("small"); message.className = "bevel-field-error"; message.dataset.bevelErrorFor = control.id; control.insertAdjacentElement("afterend", message); }
1007
+ if (!message.id) message.id = `${control.id}-error`;
1008
+ if (!control.dataset.bevelAuthoredDescribedby) control.dataset.bevelAuthoredDescribedby = control.getAttribute("aria-describedby") || "";
1009
+ const validationMessage = control.validity.customError ? control.validationMessage : control.dataset.bevelValidationMessage || control.validationMessage;
1010
+ control.dataset.bevelErrorId = message.id; setFieldError(message, validationMessage);
1011
+ const ids = new Set((control.getAttribute("aria-describedby") || "").split(/\s+/).filter(Boolean)); ids.add(message.id); control.setAttribute("aria-describedby", [...ids].join(" "));
1012
+ const item = document.createElement("li"), link = document.createElement("a"); link.href = `#${control.id}`; link.textContent = validationMessage; link.addEventListener("click", event => { event.preventDefault(); const focus = () => { control.focus(); control.scrollIntoView({ block: "center" }); }; if (revealWizardControl(control)) requestAnimationFrame(focus); else focus(); }); item.append(link); return item;
1013
+ }));
1014
+ summary.hidden = invalid.length === 0;
1015
+ return invalid;
1016
+ };
1017
+ const kanbanCards = root => [...root.querySelectorAll(".bevel-kanban__list > .bevel-kanban__card")].filter(card => card.closest(".bevel-kanban") === root);
1018
+ const kanbanSelected = root => kanbanCards(root).find(card => card.dataset.bevelKey === root.dataset.bevelSelected);
1019
+ const kanbanItems = card => [...card.querySelectorAll('.bevel-kanban__checklist-data input[data-bevel-check-key]')];
1020
+ const kanbanDisabled = node => disabled(node) || disabled(node.closest(".bevel-kanban"));
1021
+ const kanbanText = (node, text) => { if (node && node.textContent !== String(text)) node.textContent = String(text); };
1022
+ const kanbanAttr = (node, name, value) => { if (node && node.getAttribute(name) !== String(value)) node.setAttribute(name, String(value)); };
1023
+ const kanbanFlag = (node, name, value) => { if (node && node.hasAttribute(name) !== !!value) node.toggleAttribute(name, !!value); };
1024
+ const kanbanNode = (tag, className, text) => { const node = document.createElement(tag); if (className) node.className = className; if (text !== undefined) node.textContent = text; return node; };
1025
+ const kanbanSnapshot = card => card ? { key: card.dataset.bevelKey, column: card.closest("[data-bevel-column-key]")?.dataset.bevelColumnKey || "", title: card.querySelector(".bevel-kanban__title,h3")?.textContent || "", description: card.querySelector(".bevel-kanban__description,.bevel-kanban__desc")?.textContent || "", assignee: card.dataset.bevelAssignee || "", priority: card.dataset.bevelPriority || "normal", due: card.dataset.bevelDue || "", labels: [...card.querySelectorAll(".bevel-kanban__labels > [data-bevel-label]")].map(label => label.dataset.bevelLabel), archived: card.hasAttribute("data-bevel-archived") } : null;
1026
+ const kanbanToday = root => {
1027
+ const now = new Date();
1028
+ return root.dataset.bevelToday || `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,"0")}-${String(now.getDate()).padStart(2,"0")}`;
1029
+ };
1030
+ const kanbanDue = (root, value, done = false) => {
1031
+ const node = kanbanNode("span", "bevel-kanban__due");
1032
+ node.dataset.bevelDue = done ? "done" : value === kanbanToday(root) ? "today" : value && value < kanbanToday(root) ? "overdue" : "";
1033
+ node.textContent = value ? `${value === kanbanToday(root) && !done ? "Today, " : ""}${new Date(`${value}T12:00:00`).toLocaleDateString("en-US", {month:"short", day:"2-digit"})}` : "No due date";
1034
+ const text=node.textContent;node.innerHTML='<svg aria-hidden="true" width="12" height="12" viewBox="0 0 256 256" fill="currentColor"><path d="M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM72,48v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80H48V48ZM208,208H48V96H208V208Z"/></svg>';node.append(document.createTextNode(text));
1035
+ return node;
1036
+ };
1037
+ const kanbanMember = (root, key) => root.querySelector(`[data-bevel-kanban-form] [name="assignee"] option[value="${CSS.escape(key)}"]`)?.textContent || key || "Unassigned";
1038
+ const kanbanLabels = values => values.map(value => { const node = kanbanNode("span", "bevel-kanban__label", value[0]?.toUpperCase() + value.slice(1)); node.dataset.bevelLabel = value; return node; });
1039
+ const kanbanProgress = (scope, items) => {
1040
+ const done = items.filter(item => item.checked).length, percent = items.length ? Math.round(100 * done / items.length) : 0;
1041
+ kanbanText(scope.querySelector(".bevel-kanban__check-count"), `${done}/${items.length}`);
1042
+ const bar = scope.querySelector(".bevel-kanban__check-bar b"); if (bar && bar.style.width !== `${percent}%`) bar.style.width = `${percent}%`;
1043
+ kanbanAttr(scope, "aria-label", `Checklist ${done} of ${items.length}`);
1044
+ };
1045
+ const kanbanInspectorStates = new WeakMap();
1046
+ const refreshKanbanInspector = root => {
1047
+ const panel = root.querySelector("[data-bevel-kanban-inspector]"), template = root.querySelector("template[data-bevel-kanban-inspector-template]"), card = kanbanSelected(root);
1048
+ if (!panel) return;
1049
+ kanbanFlag(panel, "hidden", !card);
1050
+ const work = root.querySelector(".bevel-kanban__work"); if (work && work.classList.contains("bevel-kanban--has-panel") !== !!card) work.classList.toggle("bevel-kanban--has-panel", !!card);
1051
+ if (!card) {
1052
+ panel.querySelectorAll('[data-bevel-transient="kanban-inspector-body"]').forEach(node => node.remove());
1053
+ kanbanInspectorStates.delete(panel);
1054
+ return;
1055
+ }
1056
+ if (!template) return;
1057
+ const value = kanbanSnapshot(card), items = kanbanItems(card), details = card.querySelector("template[data-bevel-task-details]");
1058
+ const signature = JSON.stringify([value, items.map(item => [item.dataset.bevelCheckKey, item.checked, item.parentElement.textContent]), details?.innerHTML, card.hidden, disabled(root)]);
1059
+ if (kanbanInspectorStates.get(panel) === signature && panel.querySelector('[data-bevel-transient="kanban-inspector-body"]')) return;
1060
+ kanbanInspectorStates.set(panel, signature);
1061
+ const body = kanbanNode("div"); body.dataset.bevelTransient = "kanban-inspector-body"; body.append(template.content.cloneNode(true));
1062
+ kanbanText(body.querySelector(".bevel-kanban__panel-head .bevel-kanban__id"), value.key);
1063
+ kanbanText(body.querySelector("h2"), value.title);
1064
+ kanbanText(body.querySelector(".bevel-kanban__panel-lede"), value.description || "No description");
1065
+ const status = body.querySelector(".bevel-kanban__status"); status.dataset.bevelColumnKey = value.column;
1066
+ kanbanText(status, card.closest(".bevel-kanban__column")?.querySelector("h2")?.textContent || value.column);
1067
+ const field = name => body.querySelector(`[data-bevel-task-field="${name}"]`);
1068
+ const member = kanbanNode("span", "bevel-avatar bevel-avatar--small", value.assignee || "?"); member.dataset.who = value.assignee; member.title = kanbanMember(root, value.assignee);
1069
+ field("assignee").replaceChildren(member, document.createTextNode(` ${member.title}`));
1070
+ const priority = card.querySelector('.bevel-kanban__priority')?.cloneNode(true) || kanbanNode("span", "bevel-kanban__priority", value.priority); priority.dataset.bevelPriority = value.priority; field("priority").replaceChildren(priority);
1071
+ field("due").replaceChildren(kanbanDue(root, value.due, value.column === "done"));
1072
+ field("labels").replaceChildren(...kanbanLabels(value.labels));
1073
+ const checklist = body.querySelector("[data-bevel-kanban-checklist]");
1074
+ for (const source of items) {
1075
+ const row = kanbanNode("div"); row.dataset.bevelCheckRow = source.dataset.bevelCheckKey;
1076
+ const label = kanbanNode("label", "bevel-checkbox bevel-checkbox--small bevel-kanban__check-item");
1077
+ const input = kanbanNode("input", "bevel-sr-only"); input.type = "checkbox"; input.checked = source.checked; input.toggleAttribute("checked", source.checked); input.dataset.bevelCheckKey = source.dataset.bevelCheckKey;
1078
+ const box = kanbanNode("span", "bevel-checkbox__box"); box.ariaHidden = "true";
1079
+ label.append(input, box, kanbanNode("span", "bevel-checkbox__label", source.parentElement.textContent.trim()));
1080
+ const remove = kanbanNode("button", "bevel-kanban__icon"); remove.type = "button"; remove.dataset.bevelKanbanCheckRemove = source.dataset.bevelCheckKey; remove.setAttribute("aria-label", `Remove ${source.parentElement.textContent.trim()}`); remove.innerHTML = X_SVG;
1081
+ row.append(label, remove); checklist.append(row);
1082
+ }
1083
+ kanbanProgress(checklist, items);
1084
+ const extra = body.querySelector("[data-bevel-task-details]");
1085
+ if (details) extra.append(details.content.cloneNode(true));
1086
+ const estimate=extra.querySelector('[data-bevel-task-field="estimate"]'),created=extra.querySelector('[data-bevel-task-created]');
1087
+ kanbanText(field('estimate'),estimate?.textContent || '');kanbanFlag(field('estimate'),'hidden',!estimate);kanbanFlag(field('estimate').previousElementSibling,'hidden',!estimate);estimate?.closest('dl').remove();
1088
+ kanbanText(body.querySelector('.bevel-kanban__panel-foot [data-bevel-task-created]'),created?.textContent || '');created?.remove();
1089
+ kanbanFlag(body.querySelector("[data-bevel-kanban-outside]"), "hidden", !card.hidden);
1090
+ kanbanText(body.querySelector("[data-bevel-kanban-outside]"), value.archived ? "This task is archived. Restore it to return it to the board." : "This task is outside the current filters.");
1091
+ body.querySelectorAll('[data-bevel-action="open-task"],[data-bevel-action="task-options"],[data-bevel-action="comment"]').forEach(node => node.dataset.bevelKey = value.key);
1092
+ const archive = body.querySelector('[data-bevel-open]'); kanbanFlag(archive, "hidden", value.archived);
1093
+ const confirm = root.querySelector('[data-bevel-kanban-archive]')?.closest("[popover]"); if (archive && confirm) { archive.dataset.bevelOpen = confirm.id; archive.setAttribute("aria-controls", confirm.id); }
1094
+ kanbanFlag(body.querySelector('[data-bevel-kanban-restore]'), "hidden", !value.archived);
1095
+ const destination = body.querySelector('[data-bevel-kanban-move-column]'); destination.replaceChildren(...[...root.querySelectorAll('.bevel-kanban__column')].filter(column => column.closest('.bevel-kanban') === root).map(column => { const option = new Option(column.querySelector('h2')?.textContent || column.dataset.bevelColumnKey, column.dataset.bevelColumnKey); option.selected = option.value === value.column; return option; }));
1096
+ if (disabled(root)) body.querySelectorAll('button,input,select,textarea').forEach(node => node.disabled = true);
1097
+ panel.replaceChildren(body); kanbanAttr(panel, "aria-label", `Task ${value.key}`);
1098
+ };
1099
+ const kanbanFaces = new WeakMap();
1100
+ const refreshKanbanSelection = (root, cards = kanbanCards(root)) => {
1101
+ const loaded = new Set(cards.map(card => card.dataset.bevelKey));
1102
+ if (root.dataset.bevelSelected && !loaded.has(root.dataset.bevelSelected)) root.removeAttribute('data-bevel-selected');
1103
+ let keys = [];
1104
+ if (root.hasAttribute('data-bevel-selected-keys')) {
1105
+ try { const value = JSON.parse(root.dataset.bevelSelectedKeys); if (Array.isArray(value)) keys = [...new Set(value.map(String))].filter(key => loaded.has(key)); } catch {}
1106
+ kanbanAttr(root, 'data-bevel-selected-keys', JSON.stringify(keys));
1107
+ }
1108
+ const selected = new Set([...keys, root.dataset.bevelSelected].filter(Boolean));
1109
+ for (const card of cards) {
1110
+ const key = card.dataset.bevelKey, active = selected.has(key);
1111
+ if (card.classList.contains('bevel-kanban--selected') !== active) card.classList.toggle('bevel-kanban--selected', active);
1112
+ kanbanAttr(card, 'aria-selected', active);
1113
+ if (key === root.dataset.bevelSelected) kanbanAttr(card, 'aria-current', 'true'); else kanbanFlag(card, 'aria-current', false);
1114
+ }
1115
+ refreshKanbanInspector(root);
1116
+ };
1117
+ const kanbanFilterKinds = ["assignee", "label", "priority", "due-from", "due-to"];
1118
+ const kanbanFiltered = root => !!((root.querySelector('.bevel-kanban__search input') || root.querySelector('input.bevel-kanban__search'))?.value.trim() || kanbanFilterKinds.some(kind => root.getAttribute(`data-bevel-filter-${kind}`)) || root.dataset.bevelFilterDue);
1119
+ const refreshKanban = root => {
1120
+ if (root.dataset.bevelDataMode === 'remote') { refreshKanbanSelection(root); return; }
1121
+ const cards = kanbanCards(root), search = root.querySelector(".bevel-kanban__search input") || root.querySelector("input.bevel-kanban__search"), query = (search?.value || "").trim().toLowerCase();
1122
+ const assignee = root.dataset.bevelFilterAssignee || "", label = root.dataset.bevelFilterLabel || "", priority = root.dataset.bevelFilterPriority || "", from = root.dataset.bevelFilterDueFrom || "", to = root.dataset.bevelFilterDueTo || "";
1123
+ for (const card of cards) {
1124
+ const value = kanbanSnapshot(card), done = value.column === "done", items = kanbanItems(card);
1125
+ const faceKey=JSON.stringify([value.priority,value.assignee,value.due,done,kanbanToday(root)]);
1126
+ if(kanbanFaces.get(card)!==faceKey){
1127
+ kanbanFaces.set(card,faceKey);
1128
+ const priority=card.querySelector('.bevel-kanban__priority');if(priority){kanbanAttr(priority,'data-bevel-priority',value.priority);if(priority.textContent.trim().toLowerCase()!==value.priority){for(const node of [...priority.childNodes])if(node.nodeType===Node.TEXT_NODE)node.remove();priority.append(document.createTextNode(value.priority));}}
1129
+ const due=card.querySelector('.bevel-kanban__card-foot .bevel-kanban__due');if(due){const next=kanbanDue(root,value.due,done);if(value.due===kanbanToday(root)&&!done)next.lastChild.textContent='Today';due.replaceWith(next);}
1130
+ const people=card.querySelector('.bevel-kanban__card-foot .bevel-kanban__people');if(people){people.replaceChildren();if(value.assignee){const avatar=kanbanNode('span','bevel-avatar bevel-avatar--small',value.assignee);avatar.dataset.who=value.assignee;avatar.title=kanbanMember(root,value.assignee);avatar.setAttribute('aria-label',avatar.title);people.append(avatar);}}
1131
+ }
1132
+ kanbanFlag(card, "hidden", value.archived || (query && !card.textContent.toLowerCase().includes(query)) || (assignee && value.assignee !== assignee) || (label && !value.labels.includes(label)) || (priority && value.priority !== priority) || (from && (!value.due || value.due < from)) || (to && (!value.due || value.due > to)) || (root.dataset.bevelFilterDue === "set" && !value.due) || (root.dataset.bevelFilterDue === "empty" && !!value.due));
1133
+ if (card.classList.contains("bevel-kanban--done") !== done) card.classList.toggle("bevel-kanban--done", done);
1134
+ let progress = card.querySelector(".bevel-kanban__check");
1135
+ if (!progress && items.length) { progress=kanbanNode('div','bevel-kanban__check');const bar=kanbanNode('i','bevel-kanban__check-bar');bar.append(kanbanNode('b'));progress.append(bar,kanbanNode('span','bevel-kanban__check-count'));card.insertBefore(progress,card.querySelector('.bevel-kanban__card-foot')); }
1136
+ if (progress) { kanbanProgress(progress, items); kanbanFlag(progress, "hidden", !items.length); }
1137
+ }
1138
+ const active = cards.filter(card => !card.hasAttribute("data-bevel-archived")), done = active.filter(card => card.closest("[data-bevel-column-key]")?.dataset.bevelColumnKey === "done").length;
1139
+ for (const column of root.querySelectorAll(".bevel-kanban__column")) {
1140
+ if(column.closest(".bevel-kanban") !== root) continue;
1141
+ const count = active.filter(card => card.closest(".bevel-kanban__column") === column).length, limit = Number(column.dataset.bevelWip || 0);
1142
+ kanbanText(column.querySelector(".bevel-kanban__count"), count);
1143
+ if (limit) { const wip = column.querySelector(".bevel-kanban__wip"); kanbanText(wip, `WIP ${count} / ${limit}`); kanbanAttr(wip, "data-full", count >= limit); }
1144
+ kanbanFlag(column.querySelector('[data-bevel-kanban-column-empty]'), "hidden", count !== 0);
1145
+ }
1146
+ const percent = active.length ? Math.round(100 * done / active.length) : 0;
1147
+ for (const [name, value] of Object.entries({total:active.length, done, open:active.length-done, blocked:active.filter(card => card.classList.contains('bevel-kanban--blocked')).length, progress:`${percent}%`, people:new Set(active.map(card => card.dataset.bevelAssignee).filter(Boolean)).size, working:active.filter(card => card.closest('[data-bevel-column-key]').dataset.bevelColumnKey === 'progress').length})) root.querySelectorAll(`[data-bevel-kanban-${name}]`).forEach(node => kanbanText(node, value));
1148
+ kanbanText(root.querySelector('[data-bevel-kanban-limit]'), `of ${root.querySelector('.bevel-kanban__column[data-bevel-column-key="progress"]')?.dataset.bevelWip || '∞'}`);
1149
+ kanbanText(root.querySelector('[data-bevel-kanban-due-label]'),from || to ? `Due: ${from || 'any'} to ${to || 'any'}` : 'Due: any time');
1150
+ kanbanAttr(root.querySelector('[data-bevel-open="kanban-due-filter"]'),'aria-pressed',!!(from || to));
1151
+ const meter = root.querySelector('[data-bevel-kanban-meter]'); if (meter && meter.style.width !== `${percent}%`) meter.style.width = `${percent}%`;
1152
+ const zero = active.length > 0 && !cards.some(card => !card.hidden);
1153
+ kanbanFlag(root, 'data-bevel-zero-results', zero);
1154
+ kanbanFlag(root.querySelector('[data-bevel-kanban-empty]'), 'hidden', active.length !== 0);
1155
+ kanbanFlag(root.querySelector('[data-bevel-kanban-zero]'), 'hidden', !zero);
1156
+ for (const control of root.querySelectorAll('[data-bevel-kanban-filter]')) {
1157
+ const value = root.getAttribute(`data-bevel-filter-${control.dataset.bevelKanbanFilter}`) || '';
1158
+ if (control.matches('select')) { if (value && ![...control.options].some(option => option.value === value)) control.add(new Option(value, value)); for (const option of control.options) { const selected = option.value === value; if (option.selected !== selected) option.selected = selected; kanbanFlag(option, 'selected', selected); } }
1159
+ else if (control.matches('input')) { if (control.value !== value) control.value = value; kanbanAttr(control, 'value', value); }
1160
+ }
1161
+ const labels = root.querySelector('[data-bevel-kanban-filter="label"]'); if (labels) for (const value of new Set(cards.flatMap(card => kanbanSnapshot(card).labels))) if (![...labels.options].some(option => option.value === value)) labels.add(new Option(value[0].toUpperCase()+value.slice(1), value));
1162
+ if (!kanbanFiltered(root) && root.hasAttribute('data-bevel-move-message')) root.removeAttribute('data-bevel-move-message');
1163
+ refreshKanbanSelection(root, cards);
1164
+ };
1165
+ const kanbanEditor = (root, card = null, column = "") => {
1166
+ const dialog = root.querySelector("[data-bevel-kanban-editor]"), form = dialog?.querySelector("[data-bevel-kanban-form]");
1167
+ if (!form) return false;
1168
+ const value = kanbanSnapshot(card) || { key:"", column, title:"", description:"", assignee:"", priority:"normal", due:"", labels:[] };
1169
+ for (const name of ['key','column','title','description','assignee','priority','due']) form.elements[name].value = value[name];
1170
+ form.elements.labels.value = value.labels.join(', '); form.elements.title.setCustomValidity('');
1171
+ kanbanText(form.querySelector('[data-bevel-kanban-form-heading]'), card ? `Edit ${value.key}` : 'New task');
1172
+ return openOverlay(dialog, {trigger:document.activeElement});
1173
+ };
1174
+ const updateKanbanCard = (card, value) => {
1175
+ for (const name of ['assignee','priority','due']) card.setAttribute(`data-bevel-${name}`, value[name]);
1176
+ kanbanText(card.querySelector('.bevel-kanban__title,h3'), value.title);
1177
+ let description = card.querySelector('.bevel-kanban__description,.bevel-kanban__desc'); if (!description) { description = kanbanNode('p','bevel-kanban__description bevel-kanban__desc'); card.querySelector('h3').after(description); }
1178
+ kanbanText(description, value.description); description.hidden = !value.description;
1179
+ const priority = card.querySelector('.bevel-kanban__priority'); if (priority) { priority.dataset.bevelPriority = value.priority; for(const text of [...priority.childNodes])if(text.nodeType===Node.TEXT_NODE)text.remove();priority.append(document.createTextNode(value.priority)); }
1180
+ let labels = card.querySelector('.bevel-kanban__labels'); if (!labels) { labels = kanbanNode('div','bevel-kanban__labels'); description.after(labels); } labels.replaceChildren(...kanbanLabels(value.labels));
1181
+ const root = card.closest('.bevel-kanban');
1182
+ const foot = card.querySelector('.bevel-kanban__card-foot'); if (foot && root) { const due = foot.querySelector('.bevel-kanban__due'); due?.replaceWith(kanbanDue(root,value.due,value.column === 'done')); const people = foot.querySelector('.bevel-kanban__people'); if (people) { people.replaceChildren(); if (value.assignee) { const avatar = kanbanNode('span','bevel-avatar bevel-avatar--small',value.assignee); avatar.dataset.who=value.assignee; avatar.title=kanbanMember(root,value.assignee); people.append(avatar); } } }
1183
+ };
1184
+ const kanbanCard = value => {
1185
+ const card = kanbanNode('article','bevel-kanban__card'); card.tabIndex=0; card.dataset.bevelKey=value.key;
1186
+ const top = kanbanNode('div','bevel-kanban__card-top'); top.append(kanbanNode('span','bevel-kanban__id',value.key),kanbanNode('span','bevel-kanban__priority'));
1187
+ const foot = kanbanNode('footer','bevel-kanban__card-foot'); foot.append(kanbanNode('span','bevel-kanban__due'),kanbanNode('span','bevel-kanban__people'));
1188
+ card.append(top,kanbanNode('h3','bevel-kanban__title'),foot); updateKanbanCard(card,value); return card;
1189
+ };
1190
+ const kanbanMove = (root, card, list, index) => {
1191
+ if (!card?.isConnected || !list?.isConnected || list.closest('.bevel-kanban') !== root || kanbanDisabled(card)) return false;
1192
+ const source = card.parentElement, siblings = [...list.children].filter(node => node.matches('.bevel-kanban__card') && node !== card), position = Math.max(0,Math.min(index,siblings.length));
1193
+ const previous = {column:source.closest('[data-bevel-column-key]').dataset.bevelColumnKey,index:[...source.children].filter(node=>node.matches('.bevel-kanban__card')).indexOf(card)}, next = {column:list.closest('[data-bevel-column-key]').dataset.bevelColumnKey,index:position};
1194
+ if (previous.column === next.column && previous.index === next.index) return false;
1195
+ return localOrRemote(root,'move',card.dataset.bevelKey,previous,next,()=>{ list.insertBefore(card,siblings[position] || list.querySelector('.bevel-kanban__add')); refreshKanban(root); card.focus(); },`move:${card.dataset.bevelKey}`);
1196
+ };
1197
+ const refreshActivity = (root) => {
1198
+ const expanded = root.hasAttribute("data-bevel-expanded");
1199
+ const groups = [...root.querySelectorAll("[data-bevel-activity-older]")].filter((group) => group.closest(".bevel-activity") === root);
1200
+ for (const group of groups) if (group.hidden === expanded) group.hidden = !expanded;
1201
+ const button = root.querySelector("[data-bevel-activity-more]");
1202
+ if (!button) return;
1203
+ const count = groups.reduce((total, group) => total + group.querySelectorAll("[data-bevel-key]").length, 0);
1204
+ button.setAttribute("aria-expanded", String(expanded));
1205
+ const label = button.querySelector(".bevel-button__label");
1206
+ if (!expanded) {
1207
+ button.disabled = count === 0;
1208
+ if (label) label.textContent = count === 1 ? "Show yesterday (1)" : `Show older (${count})`;
1209
+ } else if (root.hasAttribute("data-bevel-has-more")) {
1210
+ button.disabled = false;
1211
+ if (label) label.textContent = "Load more";
1212
+ } else {
1213
+ button.disabled = true;
1214
+ if (label) label.textContent = "All activity shown";
1215
+ }
1216
+ };
1217
+ const showAvatarFallback = (image) => {
1218
+ const person = image.closest(".bevel-avatars__person");
1219
+ if (!person) return;
1220
+ person.dataset.bevelImageFailed = "";
1221
+ image.hidden = true;
1222
+ const fallback = person.querySelector(".bevel-avatars__fallback");
1223
+ if (fallback) fallback.hidden = false;
1224
+ };
1225
+ const refreshAvatars = (root) => {
1226
+ for (const person of root.querySelectorAll(".bevel-avatars__person")) {
1227
+ if (person.closest(".bevel-avatars") !== root) continue;
1228
+ const image = person.querySelector(".bevel-avatars__image"), fallback = person.querySelector(".bevel-avatars__fallback");
1229
+ if (!image || !fallback) continue;
1230
+ if (person.hasAttribute("data-bevel-image-failed") || (image.complete && image.naturalWidth === 0)) showAvatarFallback(image);
1231
+ else { image.hidden = false; fallback.hidden = true; }
1232
+ }
1233
+ };
1234
+ const refreshChart = (root) => {
1235
+ for (const button of root.querySelectorAll("[data-bevel-series]")) {
1236
+ if (button.closest(".bevel-chart") !== root) continue;
1237
+ const visible = button.getAttribute("aria-pressed") !== "false", key = button.dataset.bevelSeries;
1238
+ button.setAttribute("aria-pressed", String(visible));
1239
+ for (const group of root.querySelectorAll(`[data-bevel-series-key="${CSS.escape(key)}"]`)) if (group.closest(".bevel-chart") === root) group.toggleAttribute("hidden", !visible);
1240
+ }
1241
+ };
1242
+ const refresh = (scope = document, { source = "api" } = {}) => {
1243
+ const roots = (selector) => [...(scope.matches?.(selector) ? [scope] : []), ...scope.querySelectorAll(selector)];
1244
+ for (const [root, session] of [...sessions]) if (session.valid && (root === scope || scope.contains(root) || root.contains(scope)) && !session.valid()) session.cancel();
1245
+ roots(".bevel-grid").forEach((grid) => {
1246
+ if (grid.dataset.bevelDataMode !== "remote") { layoutGrid(grid); return; }
1247
+ syncPick(grid);
1248
+ if (grid.id) document.querySelectorAll(`.bevel-pagination[data-bevel-target="${CSS.escape(grid.id)}"]`).forEach(pager => refreshPager(pager));
1249
+ });
1250
+ roots(".bevel-files").forEach(syncFiles);
1251
+ roots(".bevel-slider__input").forEach(syncSlider);
1252
+ roots(".bevel-combobox").forEach(refreshCombo);
1253
+ roots(".bevel-multiselect").forEach(refreshMulti);
1254
+ roots(".bevel-pagination").forEach((root) => refreshPager(root, source === "api"));
1255
+ roots(".bevel-sidebar").forEach(refreshSidebar);
1256
+ roots(".bevel-app-shell").forEach(refreshShell);
1257
+ roots(".bevel-file-tree").forEach(syncFileTree);
1258
+ roots(".bevel-tree").forEach(refreshTree);
1259
+ roots(".bevel-code").forEach(refreshCode);
1260
+ roots(".bevel-diff").forEach(refreshDiff);
1261
+ roots(".bevel-split").forEach(refreshSplit);
1262
+ roots(".bevel-wizard").forEach(refreshWizard);
1263
+ roots(".bevel-kanban").forEach(refreshKanban);
1264
+ roots(".bevel-activity").forEach(refreshActivity);
1265
+ roots(".bevel-columns").forEach(refreshColumns);
1266
+ roots(".bevel-avatars").forEach(refreshAvatars);
1267
+ roots(".bevel-chart").forEach(refreshChart);
1268
+ roots("[data-bevel-open]:is(dialog,[popover])").sort((a, b) => Number(a.dataset.bevelOpenOrder || 0) - Number(b.dataset.bevelOpenOrder || 0)).forEach((panel) => {
1269
+ if (sessions.has(panel) && (panel.matches("dialog") ? panel.matches(":modal") : panel.matches(":popover-open"))) return;
1270
+ panel.removeAttribute("open");
1271
+ if (panel.matches("[popover]")) panel.hidePopover?.();
1272
+ openOverlay(panel, { trigger: panelTrigger(panel), source: "external" });
1273
+ });
1274
+ return scope;
1275
+ };
1276
+ const destroy = (scope, { preserve = [] } = {}) => {
1277
+ const owns = (node) => (node === scope || scope.contains?.(node)) && !preserve.some((root) => root === node || root.contains?.(node));
1278
+ for (const [root, session] of [...sessions]) if (owns(root) || (isAnchored(root) && session.trigger && owns(session.trigger))) {
1279
+ if (session.cancel) session.cancel(); else session.cleanup?.();
1280
+ sessions.delete(root);
1281
+ if (root.matches?.("dialog,[popover]")) {
1282
+ root.removeAttribute("data-bevel-open");
1283
+ if (root.matches("dialog") && root.open) root.close();
1284
+ else if (root.matches("[popover]") && root.matches(":popover-open")) root.hidePopover();
1285
+ const trigger = session.trigger?.isConnected ? session.trigger : panelTrigger(root);
1286
+ trigger?.setAttribute("aria-expanded", "false");
1287
+ setExpanded(root, false);
1288
+ root.style.removeProperty("left"); root.style.removeProperty("top"); root.style.removeProperty("--bevel-available-height");
1289
+ root.removeAttribute("data-bevel-geometry");
1290
+ queueMicrotask(() => { if (trigger?.isConnected && !disabled(trigger)) trigger.focus(); });
1291
+ }
1292
+ }
1293
+ for (const root of requestScopes.keys()) if (owns(root)) { requestScopes.delete(root); root.removeAttribute("data-bevel-pending"); }
1294
+ for (const root of codeFeedback.keys()) if (owns(root)) clearCodeFeedback(root);
1295
+ for (const [root, session] of splitSessions) if (owns(root)) { session.drag?.stop(false); session.observer.disconnect(); splitSessions.delete(root); }
1296
+ for (const [form, session] of [...wizardForms]) {
1297
+ for (const root of [...session.roots]) if (owns(root)) session.roots.delete(root);
1298
+ if (!session.roots.size || owns(form)) {
1299
+ form.noValidate = session.authoredNoValidate;
1300
+ wizardForms.delete(form);
1301
+ }
1302
+ }
1303
+ for (const root of [...(scope.matches?.(".bevel-filter,.bevel-grid__cell[data-bevel-edit]") ? [scope] : []), ...scope.querySelectorAll(".bevel-filter,.bevel-grid__cell[data-bevel-edit]")]) if (owns(root)) clearGridDrafts(root);
1304
+ for(const root of [...(scope.matches?.('.bevel-kanban') ? [scope] : []), ...scope.querySelectorAll('.bevel-kanban')])if(owns(root)){root.querySelectorAll('[data-bevel-transient="kanban-inspector-body"]').forEach(node=>node.remove());clearKanbanDrafts(root);}
1305
+ window.Bevel?.richtext?.unmount?.(scope, { preserve });
1306
+ for (const [toast, timer] of [...toastTimers || []]) if (owns(toast)) { if (timer.id) clearTimeout(timer.id); toastTimers.delete(toast); }
1307
+ if ([...(scope.matches?.(".bevel-saved") ? [scope] : []), ...scope.querySelectorAll(".bevel-saved")].some(owns)) { if (savedTimer) clearTimeout(savedTimer); savedTimer = null; }
1308
+ for (const root of [...(scope.matches?.(".bevel-dropzone") ? [scope] : []), ...(scope.querySelectorAll?.(".bevel-dropzone") || [])]) {
1309
+ if (!owns(root)) continue;
1310
+ for (const entry of uploadFiles.get(root)?.values() || []) if (entry.preview) URL.revokeObjectURL(entry.preview);
1311
+ uploadFiles.delete(root);
1312
+ }
1313
+ return scope;
1314
+ };
1315
+ const clearInlineDrafts = (scope) => {
1316
+ const roots = [...(scope.matches?.(".bevel-inline-edit") ? [scope] : []), ...scope.querySelectorAll(".bevel-inline-edit")];
1317
+ for (const root of roots) {
1318
+ root.removeAttribute("data-bevel-editing");
1319
+ clearInlineError(root);
1320
+ const value = inlinePart(root, ".bevel-inline-edit__value"), input = inlinePart(root, ".bevel-inline-edit__input");
1321
+ if (value && input && input.matches("input,textarea")) {
1322
+ input.value = value.textContent;
1323
+ if (input.matches("textarea")) input.textContent = input.value;
1324
+ else input.setAttribute("value", input.value);
1325
+ input.removeAttribute("aria-invalid");
1326
+ }
1327
+ }
1328
+ };
1329
+ const clearPickerDrafts = (scope) => {
1330
+ scope.querySelectorAll("[data-bevel-filtered]").forEach((el) => el.removeAttribute("data-bevel-filtered"));
1331
+ scope.querySelectorAll('.bevel-combobox [role="combobox"][aria-activedescendant]').forEach((el) => el.removeAttribute("aria-activedescendant"));
1332
+ scope.querySelectorAll(".bevel-combobox__empty,.bevel-multiselect__empty").forEach((el) => el.hidden = true);
1333
+ };
1334
+ const expirePendingUploads = (scope) => {
1335
+ for (const row of scope.querySelectorAll('.bevel-dropzone [data-bevel-file-key][data-bevel-status="selected"],.bevel-dropzone [data-bevel-file-key][data-bevel-status="uploading"]')) {
1336
+ row.dataset.bevelStatus = "error";
1337
+ row.dataset.bevelError = "Choose this file again";
1338
+ row.removeAttribute("data-bevel-progress");
1339
+ const status = row.querySelector(".bevel-upload__status");
1340
+ if (status) status.textContent = "Choose this file again";
1341
+ const progress = row.querySelector("progress");
1342
+ if (progress) progress.removeAttribute("value");
1343
+ const retry = row.querySelector("[data-bevel-upload-retry]");
1344
+ if (retry) retry.hidden = true;
1345
+ }
1346
+ for (const image of scope.querySelectorAll('.bevel-upload__preview img[src^="blob:"]')) { image.removeAttribute("src"); image.hidden = true; }
1347
+ };
1348
+ const clearGridDrafts = scope => {
1349
+ const roots = selector => [...(scope.matches?.(selector) ? [scope] : []), ...scope.querySelectorAll(selector)];
1350
+ for (const root of roots(".bevel-filter")) {
1351
+ root.querySelector(".bevel-filter__draft")?.replaceChildren();
1352
+ const draft = root.querySelector(".bevel-filter__draft"), committed = root.querySelector(".bevel-filter__rules");
1353
+ if (draft) draft.hidden = true;
1354
+ if (committed) committed.hidden = false;
1355
+ root.removeAttribute("data-bevel-editing"); root.removeAttribute("data-bevel-invalid");
1356
+ }
1357
+ for (const cell of roots(".bevel-grid__cell[data-bevel-edit]")) {
1358
+ cell.querySelector(".bevel-grid__cell-editor")?.remove();
1359
+ cell.removeAttribute("data-bevel-editing"); cell.removeAttribute("data-bevel-invalid");
1360
+ const error = cell.querySelector(".bevel-grid__cell-error"); if (error) error.hidden = true;
1361
+ }
1362
+ for (const header of roots(".bevel-grid th[data-bevel-width]")) {
1363
+ header.style.width = `${Number(header.dataset.bevelWidth)}px`;
1364
+ const handle = header.querySelector(".bevel-grid__resize"); if (handle) handle.setAttribute("aria-valuenow", header.dataset.bevelWidth);
1365
+ }
1366
+ for (const target of roots("[data-bevel-drop-target]")) target.removeAttribute("data-bevel-drop-target");
1367
+ };
1368
+ const clearKanbanDrafts = scope => {
1369
+ for (const dialog of [...(scope.matches?.('[data-bevel-kanban-editor]') ? [scope] : []), ...scope.querySelectorAll('[data-bevel-kanban-editor]')]) {
1370
+ for (const name of ['open','data-bevel-open','data-bevel-open-order']) dialog.removeAttribute(name);
1371
+ const form=dialog.querySelector('form');if(form)for(const input of form.elements){if(input.matches('input,textarea')){input.value='';input.removeAttribute('value');if(input.matches('textarea'))input.textContent='';}}
1372
+ }
1373
+ };
1374
+ const prepareForSave = (cloneRoot) => {
1375
+ cloneRoot.querySelectorAll('[data-bevel-transient], [data-bevel-generated]').forEach((el) => el.remove());
1376
+ cloneRoot.querySelectorAll("[data-bevel-pending], [data-bevel-dragging], [data-bevel-editing]").forEach((el) => { el.removeAttribute("data-bevel-pending"); el.removeAttribute("data-bevel-dragging"); el.removeAttribute("data-bevel-editing"); });
1377
+ cloneRoot.querySelectorAll("[data-bevel-geometry]").forEach((el) => { el.style.removeProperty("left"); el.style.removeProperty("top"); el.style.removeProperty("--bevel-available-height"); el.removeAttribute("data-bevel-geometry"); if (!el.getAttribute("style")) el.removeAttribute("style"); });
1378
+ cloneRoot.querySelectorAll("dialog.bevel-modal[open],dialog.bevel-drawer[open]").forEach((el) => el.removeAttribute("open"));
1379
+ [...(cloneRoot.matches?.(".bevel-app-shell") ? [cloneRoot] : []), ...cloneRoot.querySelectorAll(".bevel-app-shell")].forEach((shell) => { shell.removeAttribute("data-bevel-nav-open"); shell.querySelectorAll("[data-bevel-shell-nav]").forEach((button) => button.setAttribute("aria-expanded", "false")); });
1380
+ clearGridDrafts(cloneRoot);
1381
+ clearKanbanDrafts(cloneRoot);
1382
+ clearPickerDrafts(cloneRoot);
1383
+ clearInlineDrafts(cloneRoot);
1384
+ expirePendingUploads(cloneRoot);
1385
+ window.Bevel.richtext?.prepareForSave?.(cloneRoot);
1386
+ for (const root of cloneRoot.querySelectorAll(".bevel-split")) root.style.setProperty("--bevel-split-size", `${splitValue(root)}%`);
1387
+ return cloneRoot;
1388
+ };
1389
+
1390
+ on("click", "[data-bevel-open]", (e, trigger) => { if (trigger.matches("dialog,[popover]")) return; const root = targetById(trigger.dataset.bevelOpen); if (root) { e.preventDefault(); if (root.matches(".bevel-menu:popover-open")) { if (!disabled(trigger)) closeOverlay(root, { reason: "toggle" }); return; } let comboInput = null; if (root.matches(".bevel-combobox__listbox") && !trigger.matches('[role="combobox"]')) { const combo = root.closest(".bevel-combobox"); comboInput = combo?.querySelector('[role="combobox"]'); if (comboInput) { comboInput.value = ""; comboFilter(combo, ""); } } openOverlay(root, { trigger }); if (comboInput) requestAnimationFrame(() => comboInput.focus()); } });
1391
+ on("click", "[data-bevel-close]", (e, button) => { const root = button.closest("dialog,[popover]"); if (root) { e.preventDefault(); closeOverlay(root, { reason: button.dataset.bevelClose || "button" }); } });
1392
+ document.addEventListener("cancel", (e) => { if (e.target.matches("dialog.bevel-modal,dialog.bevel-drawer")) { e.preventDefault(); closeOverlay(e.target, { reason: "escape" }); } }, true);
1393
+ document.addEventListener("close", (e) => { const root = e.target; if (!root.matches?.("dialog.bevel-modal,dialog.bevel-drawer") || root.open) return; const session = sessions.get(root); session?.cleanup?.(); sessions.delete(root); root.removeAttribute("data-bevel-open"); const trigger = session?.trigger || panelTrigger(root); trigger?.setAttribute("aria-expanded", "false"); setExpanded(root, false); }, true);
1394
+ document.addEventListener("toggle", (e) => { const root = e.target; if (!root.matches?.("[popover].bevel-menu,[popover].bevel-popconfirm,[popover].bevel-combobox__listbox,[popover].bevel-multiselect__panel") || root.matches(":popover-open")) return; const session = sessions.get(root); session?.cleanup?.(); sessions.delete(root); root.removeAttribute("data-bevel-open"); root.removeAttribute("data-bevel-geometry"); const trigger = session?.trigger || panelTrigger(root); trigger?.setAttribute("aria-expanded", "false"); setExpanded(root, false); }, true);
1395
+ document.addEventListener("keydown", (e) => { if (e.defaultPrevented || e.key !== "Escape") return; const panels = opened(), modal = panels.filter(isModal).at(-1), top = panels.filter(panel => !modal || panel === modal || modal.contains(panel)).at(-1); if (top) { e.preventDefault(); closeOverlay(top, { reason: "escape" }); } });
1396
+ document.addEventListener("pointerdown", (e) => { for (const root of opened().filter(isAnchored).reverse()) if (!root.contains(e.target) && !sessions.get(root)?.trigger?.contains(e.target)) closeOverlay(root, { reason: "outside", focus: false }); });
1397
+
1398
+ on("keydown", ".bevel-menu [role^='menuitem']", (e, item) => {
1399
+ const menu = item.closest(".bevel-menu"), items = [...menu.querySelectorAll('[role^="menuitem"]')].filter((el) => !disabled(el)), index = items.indexOf(item);
1400
+ const next = e.key === "ArrowDown" ? items[(index + 1) % items.length] : e.key === "ArrowUp" ? items[(index - 1 + items.length) % items.length] : e.key === "Home" ? items[0] : e.key === "End" ? items.at(-1) : null;
1401
+ if (next) { e.preventDefault(); items.forEach((el) => el.tabIndex = el === next ? 0 : -1); next.focus(); }
1402
+ else if (e.key === "Enter" || e.key === " ") { e.preventDefault(); item.click(); }
1403
+ else if (e.key === "Tab") closeOverlay(menu, { reason: "tab", focus: false });
1404
+ else if (e.key.length === 1 && !e.altKey && !e.ctrlKey && !e.metaKey) {
1405
+ const session = sessions.get(menu) || {}, now = performance.now(), buffer = now - (session.typeTime || 0) > 500 ? e.key : `${session.typeBuffer || ""}${e.key}`;
1406
+ session.typeBuffer = buffer.toLowerCase(); session.typeTime = now; sessions.set(menu, session);
1407
+ const match = items.find((candidate) => candidate.textContent.trim().toLowerCase().startsWith(session.typeBuffer));
1408
+ if (match) { e.preventDefault(); items.forEach((el) => el.tabIndex = el === match ? 0 : -1); match.focus(); }
1409
+ }
1410
+ });
1411
+ on("click", ".bevel-menu [role^='menuitem']", (e, item) => { if (disabled(item)) { e.preventDefault(); return; } const root = item.closest(".bevel-menu"), role = item.getAttribute("role"), previous = item.getAttribute("aria-checked") === "true"; if (role === "menuitemcheckbox" || role === "menuitemradio") { const next = role === "menuitemradio" ? true : !previous; if (!transition(root, "check", item.dataset.bevelKey, previous, next, () => { if (role === "menuitemradio") root.querySelectorAll('[role="menuitemradio"]').forEach((peer) => peer.setAttribute("aria-checked", String(peer === item))); else item.setAttribute("aria-checked", String(next)); })) return; } action(root, item.dataset.bevelAction || "select", item.dataset.bevelKey); if (item.hasAttribute("data-bevel-update-default")) { const split = root.closest(".bevel-split-button"), main = split?.querySelector("[data-bevel-split-default]"), label = main?.querySelector(".bevel-button__label-text,.bevel-button__label"); if (main && label) { main.dataset.bevelSplitDefault = item.dataset.bevelAction || "select"; main.dataset.bevelKey = item.dataset.bevelKey || ""; label.textContent = item.dataset.bevelDefaultLabel || item.textContent.trim(); } } if (role !== "menuitemcheckbox") closeOverlay(root, { reason: "select" }); });
1412
+ on("click", ".bevel-popconfirm [data-bevel-confirm]", (e, button) => { const root = button.closest(".bevel-popconfirm"); if (disabled(button)) return; if (closeOverlay(root, { reason: "confirm" })) action(root, button.dataset.bevelConfirm, button.dataset.bevelKey); });
1413
+
1414
+ const comboOptions = (root) => [...root.querySelectorAll('[role="option"]')].filter((el) => !el.hidden && !el.hasAttribute("data-bevel-filtered") && el.getAttribute("aria-disabled") !== "true");
1415
+ const comboFilter = (root, query) => { let matches = 0; for (const option of root.querySelectorAll('[role="option"]')) { const filtered = !(option.dataset.bevelLabel || option.textContent).toLowerCase().includes(query.toLowerCase()); option.toggleAttribute("data-bevel-filtered", filtered); if (!option.hidden && !filtered && option.getAttribute("aria-disabled") !== "true") matches++; } const empty = root.querySelector(".bevel-combobox__empty"); if (empty) empty.hidden = matches > 0; };
1416
+ const comboCommit = (root, option) => { if (disabled(root) || disabled(root.querySelector('[role="combobox"]')) || (option && disabled(option))) return false; const input = root.querySelector('[role="combobox"]'), hidden = root.querySelector('input[type="hidden"]'), previous = hidden?.value || "", next = option?.dataset.bevelKey || ""; transition(root, "select", next, previous, next, () => { if (hidden) { hidden.value = next; if (hidden.getAttribute("value") !== next) hidden.setAttribute("value", next); } if (root.dataset.bevelValue !== next) root.dataset.bevelValue = next; if (input) { const label = option?.dataset.bevelLabel || option?.textContent.trim() || ""; input.value = label; if (input.getAttribute("value") !== label) input.setAttribute("value", label); input.removeAttribute("aria-activedescendant"); } refreshCombo(root); const panel = root.querySelector('[role="listbox"]'); if (panel?.matches(":popover-open")) closeOverlay(panel, { reason: "select", focus: false }); }); };
1417
+ on("input", ".bevel-combobox [role='combobox']", (e, input) => { if (e.isComposing || disabled(input) || disabled(input.closest(".bevel-combobox"))) return; const root = input.closest(".bevel-combobox"), list = root.querySelector('[role="listbox"]'); comboFilter(root, input.value); refreshCombo(root); if (!list.matches(":popover-open")) openOverlay(list, { trigger: input }); });
1418
+ on("keydown", ".bevel-combobox [role='combobox']", (e, input) => { if (e.isComposing || disabled(input) || disabled(input.closest(".bevel-combobox"))) return; const root = input.closest(".bevel-combobox"), list = root.querySelector('[role="listbox"]'), options = comboOptions(root), active = targetById(input.getAttribute("aria-activedescendant")), index = options.indexOf(active); if (e.key === "ArrowDown" || e.key === "ArrowUp") { e.preventDefault(); if (!list.matches(":popover-open")) openOverlay(list, { trigger: input }); const next = e.key === "ArrowDown" ? options[(index + 1) % options.length] : options[(index - 1 + options.length) % options.length]; if (next) { if (!next.id) next.id = `${root.id || "bevel-combo"}-option-${options.indexOf(next)}`; input.setAttribute("aria-activedescendant", next.id); } } else if (e.key === "Enter" && active) { e.preventDefault(); comboCommit(root, active); } else if (e.key === "Escape") { refreshCombo(root, true); input.removeAttribute("aria-activedescendant"); if (list.matches(":popover-open")) closeOverlay(list, { reason: "escape" }); } else if (e.key === "Tab") { refreshCombo(root, true); input.removeAttribute("aria-activedescendant"); if (list.matches(":popover-open")) closeOverlay(list, { reason: "tab", focus: false }); } });
1419
+ on("pointerdown", ".bevel-combobox [role='option']", (e, option) => { e.preventDefault(); comboCommit(option.closest(".bevel-combobox"), option); });
1420
+ on("click", ".bevel-combobox [data-bevel-combo-clear]", (e, button) => { const root = button.closest(".bevel-combobox"), input = root.querySelector('[role="combobox"]'); if (input?.required) return; comboCommit(root, null); input?.focus(); });
1421
+ on("change", ".bevel-multiselect__panel input[type='checkbox']", (e, input) => { const root = input.closest(".bevel-multiselect"), nextChecked = input.checked, previousChecked = !nextChecked, before = [...root.querySelectorAll('.bevel-multiselect__panel input[type="checkbox"]')].filter((item) => item !== input ? item.checked : previousChecked).map((item) => item.value), after = [...root.querySelectorAll('.bevel-multiselect__panel input[type="checkbox"]')].filter((item) => item.checked).map((item) => item.value); if (!transition(root, "select", input.value, before, after, () => refreshMulti(root))) { input.checked = previousChecked; input.toggleAttribute("checked", previousChecked); refreshMulti(root); } });
1422
+ on("input", ".bevel-multiselect__search", (e, input) => { const root = input.closest(".bevel-multiselect"), query = input.value.toLowerCase(); let matches = 0; for (const label of root.querySelectorAll(".bevel-multiselect__option")) { const checkbox = label.querySelector('input[type="checkbox"]'), filtered = !label.textContent.toLowerCase().includes(query); label.toggleAttribute("data-bevel-filtered", filtered); if (!label.hidden && !filtered && !disabled(checkbox)) matches++; } const empty = root.querySelector(".bevel-multiselect__empty"); if (empty) empty.hidden = matches > 0; });
1423
+ on("keydown", ".bevel-multiselect__search", (e, input) => { if (e.key === "Backspace" && !input.value) { const remove = [...input.closest(".bevel-multiselect").querySelectorAll("[data-bevel-multiselect-remove]:not(:disabled)")].at(-1); if (remove) { e.preventDefault(); remove.focus(); } } });
1424
+ on("click", ".bevel-multiselect [data-bevel-multiselect-remove]", (e, button) => { const root = button.closest(".bevel-multiselect"), buttons = [...root.querySelectorAll("[data-bevel-multiselect-remove]")], index = buttons.indexOf(button), checkbox = [...root.querySelectorAll('.bevel-multiselect__panel input[type="checkbox"]')].find((input) => input.value === button.dataset.bevelMultiselectRemove); if (!checkbox || disabled(checkbox)) return; checkbox.click(); requestAnimationFrame(() => { const remaining = [...root.querySelectorAll("[data-bevel-multiselect-remove]")]; (remaining[index] || remaining[index - 1] || root.querySelector(".bevel-multiselect__summary"))?.focus(); }); });
1425
+
1426
+ const acceptFiles = (root, files) => {
1427
+ if (!root) return { accepted: [], rejected: [] };
1428
+ const limit = Number(root.dataset.bevelMaxBytes) || Infinity, accept = (root.querySelector('input[type="file"]')?.accept || "").split(",").map((s) => s.trim()).filter(Boolean), accepted = [], rejected = [];
1429
+ for (const file of files || []) { const validType = !accept.length || accept.some((rule) => rule.endsWith("/*") ? file.type.startsWith(rule.slice(0, -1)) : rule.startsWith(".") ? file.name.toLowerCase().endsWith(rule.toLowerCase()) : file.type.toLowerCase() === rule.toLowerCase()); if (file.size > limit) rejected.push({ name: file.name, reason: `File exceeds the ${limit.toLocaleString()} byte limit` }); else if (!validType) rejected.push({ name: file.name, reason: "File type is not accepted" }); else accepted.push({ key: crypto.randomUUID?.() || `file-${Date.now()}-${Math.random().toString(36).slice(2)}`, file }); }
1430
+ const store = uploadFiles.get(root) || new Map();
1431
+ const list = root.querySelector("[data-bevel-upload-list]") || (() => { const node = document.createElement("div"); node.className = "bevel-upload"; node.dataset.bevelUploadList = ""; root.append(node); return node; })();
1432
+ for (const item of accepted) {
1433
+ const preview = item.file.type.startsWith("image/") ? URL.createObjectURL(item.file) : "";
1434
+ store.set(item.key, { file: item.file, preview });
1435
+ let row = list.querySelector(`[data-bevel-file-key="${CSS.escape(item.key)}"]`);
1436
+ if (!row) { row = document.createElement("article"); row.className = "bevel-upload__row"; row.dataset.bevelFileKey = item.key; row.innerHTML = '<div class="bevel-upload__preview" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="square" stroke-linejoin="miter"><path d="M5 3h9l5 5v13H5ZM14 3v6h5"/></svg><img alt="" hidden></div><div class="bevel-upload__copy"><strong class="bevel-upload__name"></strong><span class="bevel-upload__meta"></span><span class="bevel-upload__status" role="status"></span><progress class="bevel-upload__progress" max="100" value="0" aria-label="Upload progress"></progress></div><span class="bevel-upload__state" aria-hidden="true"></span><div class="bevel-upload__actions"><button class="bevel-button bevel-button--small" type="button" data-bevel-upload-retry hidden>Retry</button><button class="bevel-button bevel-button--small" type="button" data-bevel-upload-remove>Remove</button></div>'; list.append(row); }
1437
+ row.dataset.bevelStatus = "selected";
1438
+ row.dataset.bevelName = item.file.name;
1439
+ row.querySelector(".bevel-upload__name").textContent = item.file.name;
1440
+ row.querySelector(".bevel-upload__meta").textContent = uploadSize(item.file.size);
1441
+ row.querySelector(".bevel-upload__status").textContent = "Selected";
1442
+ const image = row.querySelector(".bevel-upload__preview img");
1443
+ if (preview) { image.src = preview; image.hidden = false; }
1444
+ }
1445
+ for (const item of rejected) { const row = document.createElement("p"); row.className = "bevel-upload__rejection"; row.dataset.bevelRejected = ""; row.setAttribute("role", "alert"); row.textContent = `${item.name}: ${item.reason}`; list.append(row); }
1446
+ uploadFiles.set(root, store); emit(root, "bevel:files", { files: accepted, rejected }); return { accepted, rejected };
1447
+ };
1448
+ on("change", ".bevel-dropzone input[type='file']", (e, input) => { acceptFiles(input.closest(".bevel-dropzone"), input.files); input.value = ""; });
1449
+ on("dragover", ".bevel-dropzone", (e, root) => { e.preventDefault(); root.dataset.bevelDragging = ""; });
1450
+ on("dragleave", ".bevel-dropzone", (e, root) => root.removeAttribute("data-bevel-dragging"));
1451
+ on("drop", ".bevel-dropzone", (e, root) => { e.preventDefault(); root.removeAttribute("data-bevel-dragging"); acceptFiles(root, e.dataTransfer.files); });
1452
+ on("click", ".bevel-dropzone [data-bevel-upload-remove]", (e, button) => upload.remove(button.closest(".bevel-dropzone"), button.closest("[data-bevel-file-key]").dataset.bevelFileKey));
1453
+ on("click", ".bevel-dropzone [data-bevel-upload-retry]", (e, button) => { const root = button.closest(".bevel-dropzone"), key = button.closest("[data-bevel-file-key]").dataset.bevelFileKey; if (uploadFiles.get(root)?.has(key)) action(root, "retry", key); });
1454
+
1455
+ const inlinePart = (root, selector) => [...root.querySelectorAll(selector)].find(node => node.closest(".bevel-inline-edit") === root);
1456
+ const inlineReadOnly = root => disabled(root) || root.getAttribute("aria-readonly") === "true" || root.hasAttribute("data-bevel-readonly");
1457
+ const clearInlineError = root => {
1458
+ const message = inlinePart(root, "[data-bevel-edit-error]");
1459
+ setFieldError(message, "");
1460
+ };
1461
+ const startEdit = (root) => {
1462
+ if (!root?.matches(".bevel-inline-edit") || inlineReadOnly(root)) return false;
1463
+ if (sessions.get(root)?.input) return true;
1464
+ const value = inlinePart(root, ".bevel-inline-edit__value"), container = inlinePart(root, ".bevel-inline-edit__editor"), trigger = inlinePart(root, "[data-bevel-edit-start]");
1465
+ if (!value || !container || (trigger && disabled(trigger))) return false;
1466
+ const template = inlinePart(root, "template[data-bevel-edit-template]");
1467
+ const draft = template?.content.cloneNode(true);
1468
+ let input = draft?.querySelector(".bevel-inline-edit__input") || inlinePart(root, ".bevel-inline-edit__input");
1469
+ if (!input || disabled(input)) return false;
1470
+ const rich = root.dataset.bevelEditKind === "rich";
1471
+ if (rich && !window.Bevel.richtext) { error(root, "missing-richclay", "Load the Richclay integration before editing rich content."); return false; }
1472
+ if (rich && typeof window.RichClay !== "function") { error(root, "missing-richclay", "Load Richclay before editing rich content."); return false; }
1473
+ if (draft) {
1474
+ for (const child of draft.children) child.setAttribute("data-bevel-transient", "inline-draft");
1475
+ (inlinePart(root, "[data-bevel-edit-slot]") || container).prepend(draft);
1476
+ }
1477
+ clearInlineError(root);
1478
+ let editor, toolbarHost;
1479
+ const cleanup = (focus = false) => {
1480
+ if (rich) window.Bevel.richtext?.unmount(input);
1481
+ toolbarHost?.remove();
1482
+ if (template) container.querySelectorAll('[data-bevel-transient="inline-draft"]').forEach(node => node.remove());
1483
+ else { input.value = value.textContent; input.removeAttribute("aria-invalid"); }
1484
+ root.removeAttribute("data-bevel-editing");
1485
+ clearInlineError(root);
1486
+ sessions.delete(root);
1487
+ if (focus && trigger?.isConnected && !disabled(trigger)) trigger.focus();
1488
+ };
1489
+ root.setAttribute("data-bevel-editing", "");
1490
+ try {
1491
+ if (rich) {
1492
+ input.setAttribute("data-bevel-richtext", "");
1493
+ if (input.hasAttribute("required")) input.setAttribute("aria-required", "true");
1494
+ input.innerHTML = value.innerHTML;
1495
+ const singleLine = root.hasAttribute("data-bevel-single-line");
1496
+ const insideModal = !!root.closest("dialog");
1497
+ if (insideModal && !singleLine) {
1498
+ toolbarHost = document.createElement("div");
1499
+ toolbarHost.setAttribute("data-bevel-transient", "inline-draft");
1500
+ container.prepend(toolbarHost);
1501
+ }
1502
+ [editor] = window.Bevel.richtext.mount(input, { inline: !insideModal || singleLine, singleLine, toolbar: singleLine ? false : "inline", toolbarContainer: toolbarHost, placeholder: input.dataset.bevelPlaceholder || "", hyperclay: false });
1503
+ editor.focus();
1504
+ } else {
1505
+ input.value = value.textContent;
1506
+ input.focus();
1507
+ if (input.type !== "number") input.select();
1508
+ }
1509
+ const original = rich ? value.innerHTML : value.textContent;
1510
+ const valid = () => inlinePart(root, ".bevel-inline-edit__value") === value && inlinePart(root, ".bevel-inline-edit__editor") === container && container.contains(input) && (rich ? value.innerHTML : value.textContent) === original;
1511
+ sessions.set(root, { input, editor, rich, trigger, cleanup, valid, cancel: () => cleanup() });
1512
+ return true;
1513
+ } catch (cause) {
1514
+ cleanup();
1515
+ error(root, "editor-start", cause.message);
1516
+ return false;
1517
+ }
1518
+ };
1519
+ const endEdit = (root, commit) => {
1520
+ if (!root) return false;
1521
+ const session = sessions.get(root);
1522
+ if (!session?.input) return false;
1523
+ if (!session.valid()) { session.cleanup(); return false; }
1524
+ if (!commit) { session.cleanup(true); return true; }
1525
+ if (inlineReadOnly(root) || disabled(session.input)) return false;
1526
+ const { input, rich, editor } = session;
1527
+ const valid = rich ? !(input.hasAttribute("required") && !input.textContent.trim()) : input.checkValidity();
1528
+ if (!valid) {
1529
+ input.setAttribute("aria-invalid", "true");
1530
+ const message = inlinePart(root, "[data-bevel-edit-error]");
1531
+ if (message) {
1532
+ setFieldError(message, root.dataset.bevelValidationMessage || (rich ? "Enter a value before saving." : input.validationMessage));
1533
+ }
1534
+ input.focus();
1535
+ if (!rich && !message) input.reportValidity();
1536
+ return false;
1537
+ }
1538
+ const value = inlinePart(root, ".bevel-inline-edit__value");
1539
+ const previous = rich ? value.innerHTML : value.textContent;
1540
+ const next = rich ? editor.getHTML() : input.value;
1541
+ return transition(root, "edit", root.dataset.bevelKey, previous, next, () => {
1542
+ if (rich) value.innerHTML = next;
1543
+ else value.textContent = next;
1544
+ session.cleanup(true);
1545
+ });
1546
+ };
1547
+ on("click", ".bevel-inline-edit [data-bevel-edit-start]", (event, button) => { event.preventDefault(); startEdit(button.closest(".bevel-inline-edit")); });
1548
+ on("click", ".bevel-inline-edit [data-bevel-edit-save]", (event, button) => { event.preventDefault(); if (!disabled(button)) endEdit(button.closest(".bevel-inline-edit"), true); });
1549
+ on("click", ".bevel-inline-edit [data-bevel-edit-cancel]", (event, button) => { event.preventDefault(); if (!disabled(button)) endEdit(button.closest(".bevel-inline-edit"), false); });
1550
+ on("keydown", ".bevel-inline-edit__input", (event, input) => {
1551
+ if (event.isComposing) return;
1552
+ const root = input.closest(".bevel-inline-edit"), session = sessions.get(root);
1553
+ if (!session || session.input !== input) return;
1554
+ if (event.key === "Escape") {
1555
+ if (event.defaultPrevented) return;
1556
+ event.preventDefault(); endEdit(root, false);
1557
+ } else if (event.key === "Enter" && (event.ctrlKey || event.metaKey || (session.rich ? root.hasAttribute("data-bevel-single-line") : input.tagName !== "TEXTAREA"))) {
1558
+ event.preventDefault(); endEdit(root, true);
1559
+ }
1560
+ }, true);
1561
+
1562
+ on("click", ".bevel-split-button [data-bevel-split-default]", (e, button) => { if (!disabled(button)) action(button.closest(".bevel-split-button"), button.dataset.bevelSplitDefault, button.dataset.bevelKey); });
1563
+
1564
+ on("click", ".bevel-toolbar [data-bevel-toggle]", (event, button) => { if (disabled(button)) return; const root = button.closest(".bevel-toolbar"), group = button.closest("[data-bevel-toggle-group]"); if (group) { const peers = [...group.querySelectorAll("[data-bevel-toggle]")], previous = peers.find((peer) => peer.getAttribute("aria-pressed") === "true")?.dataset.bevelToggle || "", next = button.dataset.bevelToggle; if (previous === next) return; transition(root, "toggle", next, previous, next, () => peers.forEach((peer) => peer.setAttribute("aria-pressed", String(peer === button)))); } else { const previous = button.getAttribute("aria-pressed") === "true", next = !previous; transition(root, "toggle", button.dataset.bevelToggle, previous, next, () => button.setAttribute("aria-pressed", String(next))); } });
1565
+
1566
+ on("click", ".bevel-pagination [data-bevel-page]", (e, button) => { const root = button.closest(".bevel-pagination"); if (disabled(button)) return; const target = root.dataset.bevelTarget && document.getElementById(root.dataset.bevelTarget), grid = target?.matches(".bevel-grid") ? target : null, previous = Number(grid?.dataset.page || root.dataset.bevelPage) || 1, size = Number(grid?.dataset.pageSize || root.dataset.bevelPageSize) || 10, total = grid ? (grid.hasAttribute("data-bevel-total") ? Number(grid.dataset.bevelTotal) : null) : Number(root.dataset.bevelTotal) || 0, pages = total == null ? null : Math.max(1, Math.ceil(Math.max(0, total) / size)), token = button.dataset.bevelPage, next = token === "prev" ? previous - 1 : token === "next" ? previous + 1 : Number(token); if (!next || next < 1 || (pages != null && next > pages)) return; if (grid) changeGridPage(grid, token); else transition(root, "page", next, previous, next, () => { root.dataset.bevelPage = String(next); refreshPager(root); }); });
1567
+ on("change", ".bevel-pagination select[data-bevel-page-size]", (event, control) => { const root = control.closest(".bevel-pagination"), target = root.dataset.bevelTarget && document.getElementById(root.dataset.bevelTarget), grid = target?.matches(".bevel-grid") ? target : null, previous = Number(grid?.dataset.pageSize || root.dataset.bevelPageSize) || 10, next = Number(control.value); if (!Number.isFinite(next) || next < 1 || next === previous) { refreshPager(root); return; } const before = { page: Number(grid?.dataset.page || root.dataset.bevelPage) || 1, pageSize: previous }, after = { page: 1, pageSize: next }; if (grid?.dataset.bevelDataMode === "remote") { remoteRequest(grid, "page", "size", before, after); refreshPager(root); return; } if (grid) { grid.dataset.pageSize = String(next); grid.dataset.page = "1"; layoutGrid(grid); emit(grid, "bevel:page", { page: Number(grid.dataset.page), pageSize: next }); } else transition(root, "page-size", next, before, after, () => { root.dataset.bevelPageSize = String(next); root.dataset.bevelPage = "1"; refreshPager(root); }); });
1568
+ on("click", ".bevel-sidebar [data-bevel-sidebar-toggle]", (event, button) => { if (disabled(button)) return; event.preventDefault(); const root = button.closest(".bevel-sidebar"), previous = root.hasAttribute("data-bevel-collapsed"), next = !previous; transition(root, "collapse", "sidebar", previous, next, () => { root.toggleAttribute("data-bevel-collapsed", next); refreshSidebar(root); }); });
1569
+ on("click", ".bevel-app-shell [data-bevel-shell-nav]", (event, button) => { if (disabled(button)) return; event.preventDefault(); const root = button.closest(".bevel-app-shell"), previous = root.hasAttribute("data-bevel-nav-open"), next = !previous; transition(root, "open", "sidebar", previous, next, () => { root.toggleAttribute("data-bevel-nav-open", next); refreshShell(root); }); });
1570
+ on("click", ".bevel-app-shell .bevel-app-shell__side [data-bevel-shell-collapse]", (event, button) => { if (disabled(button)) return; event.preventDefault(); const root = button.closest(".bevel-app-shell"), side = button.closest(".bevel-app-shell__side"), previous = side.hasAttribute("data-bevel-collapsed"), next = !previous; transition(root, "collapse", "sidebar", previous, next, () => { side.toggleAttribute("data-bevel-collapsed", next); refreshShell(root); }); });
1571
+ const selectTreeItem = (root, item) => { const previous = root.dataset.bevelSelected || "", next = item.dataset.bevelKey; return transition(root, "select", next, previous, next, () => { root.dataset.bevelSelected = next; refreshTree(root); }); };
1572
+ const setTreeExpanded = (root, item, next) => { const branch = item.closest("details"); if (!branch || branch.querySelector(":scope > summary") !== item) return false; const previous = branch.open; if (previous === next) return true; return transition(root, next ? "expand" : "collapse", item.dataset.bevelKey, previous, next, () => { branch.open = next; branch.dataset.bevelExpanded = String(next); refreshTree(root); }); };
1573
+ on("click", '.bevel-tree [role="treeitem"][data-bevel-key]', (event, item) => { const root = item.closest(".bevel-tree"); if (disabled(item)) { event.preventDefault(); return; } if (item.tagName === "SUMMARY") { event.preventDefault(); setTreeExpanded(root, item, !item.closest("details").open); } else selectTreeItem(root, item); });
1574
+ on("keydown", '.bevel-tree [role="treeitem"][data-bevel-key]', (event, item) => {
1575
+ if (event.isComposing || disabled(item)) return;
1576
+ const root = item.closest(".bevel-tree"), visible = treeVisible(root), index = visible.indexOf(item);
1577
+ const focus = (next) => { if (!next) return; event.preventDefault(); for (const node of treeItems(root)) node.tabIndex = node === next ? 0 : -1; next.focus(); };
1578
+ if (event.key === "ArrowDown") return focus(visible[index + 1] || visible[0]);
1579
+ if (event.key === "ArrowUp") return focus(visible[index - 1] || visible.at(-1));
1580
+ if (event.key === "Home") return focus(visible[0]);
1581
+ if (event.key === "End") return focus(visible.at(-1));
1582
+ if (event.key === "ArrowRight" && item.tagName === "SUMMARY") { const branch = item.closest("details"); if (!branch.open) { event.preventDefault(); setTreeExpanded(root, item, true); } else focus(treeVisible(root).find((candidate) => candidate !== item && branch.contains(candidate))); return; }
1583
+ if (event.key === "ArrowLeft") { if (item.tagName === "SUMMARY" && item.closest("details").open) { event.preventDefault(); setTreeExpanded(root, item, false); return; } const owner = item.tagName === "SUMMARY" ? item.closest("details")?.parentElement : item.parentElement, parent = owner?.closest("details")?.querySelector(":scope > summary"); if (parent) focus(parent); return; }
1584
+ if (event.key === "Enter") { if (item.tagName !== "A") event.preventDefault(); selectTreeItem(root, item); return; }
1585
+ if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) { const now = performance.now(), prior = root._bevelTypeahead || { value: "", at: 0 }, value = now - prior.at > 700 ? event.key : prior.value + event.key; root._bevelTypeahead = { value: value.toLocaleLowerCase(), at: now }; const ordered = [...visible.slice(index + 1), ...visible.slice(0, index + 1)], match = ordered.find((candidate) => (candidate.dataset.bevelLabel || candidate.textContent).trim().toLocaleLowerCase().startsWith(root._bevelTypeahead.value)); if (match) focus(match); }
1586
+ });
1587
+ document.addEventListener("toggle", (event) => { const branch = event.target; if (!(branch instanceof HTMLDetailsElement)) return; const root = branch.closest(".bevel-tree"), item = branch.querySelector(":scope > summary"); if (!root || !item) return; if (!branch.open && branch.contains(document.activeElement) && document.activeElement !== item) item.focus(); refreshTree(root); }, true);
1588
+ /* ---------- file tree ---------- */
1589
+ const fileTreeRows = (root) => [...root.querySelectorAll(".bevel-file-tree__row")].filter((row) => row.closest(".bevel-file-tree") === root);
1590
+ const fileTreeVisible = (root) => fileTreeRows(root).filter((row) => !row.hidden && !row.closest("[hidden]"));
1591
+ const fileTreeGroup = (row) => targetById(row.getAttribute("aria-controls"));
1592
+ const syncFileTree = (root) => {
1593
+ if (root.getAttribute("role") !== "tree") root.setAttribute("role", "tree");
1594
+ const selected = root.dataset.bevelSelected || "";
1595
+ for (const row of fileTreeRows(root)) {
1596
+ if (row.getAttribute("role") !== "treeitem") row.setAttribute("role", "treeitem");
1597
+ const group = fileTreeGroup(row);
1598
+ if (group) group.hidden = row.hidden || row.getAttribute("aria-expanded") !== "true";
1599
+ if (!group) { const value = String(row.dataset.bevelKey === selected); if (row.getAttribute("aria-selected") !== value) row.setAttribute("aria-selected", value); }
1600
+ }
1601
+ const visible = fileTreeVisible(root);
1602
+ const current = visible.find((row) => row === document.activeElement) || visible.find((row) => row.tabIndex === 0) || visible.find((row) => row.dataset.bevelKey === selected) || visible[0];
1603
+ for (const row of fileTreeRows(root)) { const value = row === current ? "0" : "-1"; if (row.getAttribute("tabindex") !== value) row.setAttribute("tabindex", value); }
1604
+ const empty = root.querySelector(".bevel-file-tree__empty");
1605
+ if (empty) empty.hidden = visible.length > 0;
1606
+ };
1607
+ const setFileTreeExpanded = (root, row, next) => {
1608
+ const previous = row.getAttribute("aria-expanded") === "true";
1609
+ if (previous === next) return true;
1610
+ return transition(root, next ? "expand" : "collapse", row.dataset.bevelKey, previous, next, () => {
1611
+ row.setAttribute("aria-expanded", String(next));
1612
+ row.dataset.bevelExpanded = String(next);
1613
+ syncFileTree(root);
1614
+ });
1615
+ };
1616
+ const selectFileTreeRow = (root, row) => {
1617
+ const previous = root.dataset.bevelSelected || "", next = row.dataset.bevelKey;
1618
+ return transition(root, "select", next, previous, next, () => { root.dataset.bevelSelected = next; syncFileTree(root); });
1619
+ };
1620
+
1621
+ const filterFileTree = (root, query) => {
1622
+ const value = query.trim().toLocaleLowerCase(), rows = fileTreeRows(root);
1623
+ for (const row of rows) {
1624
+ const label = (row.dataset.bevelLabel || row.textContent).trim().toLocaleLowerCase();
1625
+ row.hidden = Boolean(value) && !label.includes(value);
1626
+ if (!value && row.hasAttribute("aria-expanded")) row.setAttribute("aria-expanded", row.dataset.bevelExpanded || "false");
1627
+ }
1628
+ if (value) for (const row of rows) {
1629
+ if (row.hidden) continue;
1630
+ for (let group = row.parentElement?.closest(".bevel-file-tree__group"); group && root.contains(group); group = group.parentElement?.closest(".bevel-file-tree__group")) {
1631
+ const owner = rows.find((candidate) => candidate.getAttribute("aria-controls") === group.id);
1632
+ if (!owner) break;
1633
+ owner.hidden = false;
1634
+ owner.setAttribute("aria-expanded", "true");
1635
+ }
1636
+ }
1637
+ syncFileTree(root);
1638
+ emit(root, "bevel:filter", { component: "file-tree", value: query, matches: fileTreeVisible(root).filter((row) => !row.hasAttribute("aria-controls")).length });
1639
+ };
1640
+ on("click", ".bevel-file-tree__row", (event, row) => {
1641
+ const root = row.closest(".bevel-file-tree");
1642
+ if (!root || disabled(row)) { event.preventDefault(); return; }
1643
+ if (row.hasAttribute("aria-expanded")) { event.preventDefault(); setFileTreeExpanded(root, row, row.getAttribute("aria-expanded") !== "true"); return; }
1644
+ selectFileTreeRow(root, row);
1645
+ });
1646
+ on("keydown", ".bevel-file-tree__row", (event, row) => {
1647
+ if (event.isComposing || disabled(row)) return;
1648
+ const root = row.closest(".bevel-file-tree");
1649
+ if (!root) return;
1650
+ const visible = fileTreeVisible(root), index = visible.indexOf(row);
1651
+ const focus = (next) => { if (!next) return; event.preventDefault(); for (const item of fileTreeRows(root)) item.tabIndex = item === next ? 0 : -1; next.focus(); };
1652
+ if (event.key === "ArrowDown") return focus(visible[index + 1] || visible[0]);
1653
+ if (event.key === "ArrowUp") return focus(visible[index - 1] || visible.at(-1));
1654
+ if (event.key === "Home") return focus(visible[0]);
1655
+ if (event.key === "End") return focus(visible.at(-1));
1656
+ if (event.key === "ArrowRight" && row.hasAttribute("aria-expanded")) {
1657
+ if (row.getAttribute("aria-expanded") !== "true") { event.preventDefault(); setFileTreeExpanded(root, row, true); }
1658
+ else focus(fileTreeGroup(row)?.querySelector(".bevel-file-tree__row"));
1659
+ return;
1660
+ }
1661
+ if (event.key === "ArrowLeft") {
1662
+ if (row.hasAttribute("aria-expanded") && row.getAttribute("aria-expanded") === "true") { event.preventDefault(); setFileTreeExpanded(root, row, false); return; }
1663
+ const group = row.parentElement?.closest(".bevel-file-tree__group");
1664
+ if (group) focus(fileTreeRows(root).find((candidate) => candidate.getAttribute("aria-controls") === group.id));
1665
+ return;
1666
+ }
1667
+ if (event.key === "Enter" && row.tagName !== "A") { event.preventDefault(); row.click(); }
1668
+ });
1669
+ on("input", "[data-bevel-file-tree]", (event, input) => {
1670
+ const root = targetById(input.dataset.bevelFileTree);
1671
+ if (root) filterFileTree(root, input.value);
1672
+ });
1673
+
1674
+ const reportFormErrors = (form, controls) => {
1675
+ if (!form) {
1676
+ const invalid = invalidControls(controls);
1677
+ if (invalid.length) requestAnimationFrame(() => invalid[0].focus());
1678
+ return invalid;
1679
+ }
1680
+ const invalid = refreshErrors(form, controls);
1681
+ if (!invalid.length) return invalid;
1682
+ revealWizardControl(invalid[0]);
1683
+ const summary = form.querySelector(".bevel-errors");
1684
+ if (summary && !summary.hasAttribute("tabindex")) summary.tabIndex = -1;
1685
+ requestAnimationFrame(() => (summary || invalid[0]).focus());
1686
+ return invalid;
1687
+ };
1688
+ on("click", ".bevel-wizard [data-bevel-step]", (event, button) => {
1689
+ const root = button.closest(".bevel-wizard");
1690
+ if (button === root || !button.matches("button,[role='button']") || disabled(button)) return;
1691
+ const panels = wizardPanels(root), current = panels.findIndex((panel, index) => wizardKey(panels, panel, index) === String(root.dataset.bevelStep));
1692
+ const token = button.dataset.bevelStep;
1693
+ const target = token === "next" ? current + 1 : token === "prev" ? current - 1 : panels.findIndex((panel, index) => wizardKey(panels, panel, index) === token);
1694
+ if (current < 0 || target < 0 || target >= panels.length || disabled(panels[target])) return;
1695
+ if (target > current) {
1696
+ const requiredPanels = (token === "next" ? [panels[current]] : panels.slice(0, target)).filter((panel) => !disabled(panel));
1697
+ const invalid = wizardInvalid(requiredPanels);
1698
+ if (invalid.length) {
1699
+ event.preventDefault();
1700
+ reportFormErrors(root.closest("form"), wizardControls(invalid[0].closest(".bevel-wizard__panel")));
1701
+ return;
1702
+ }
1703
+ }
1704
+ const previous = wizardKey(panels, panels[current], current), next = wizardKey(panels, panels[target], target);
1705
+ transition(root, "step", next, previous, next, () => { root.dataset.bevelStep = next; refreshWizard(root); });
1706
+ });
1707
+ document.addEventListener("submit", (event) => {
1708
+ const form = event.target.closest?.("form[data-bevel-validate]");
1709
+ if (!form) return;
1710
+ const root = [...form.querySelectorAll(".bevel-wizard")].find((wizard) => wizard.closest("form") === form);
1711
+ const invalid = reportFormErrors(form, [...form.elements]);
1712
+ if (invalid.length) {
1713
+ event.preventDefault();
1714
+ event.stopImmediatePropagation();
1715
+ return;
1716
+ }
1717
+ if (root) action(root, "finish", root.dataset.bevelStep || "");
1718
+ }, true);
1719
+ document.addEventListener("invalid", (event) => { const form = event.target.form?.matches("[data-bevel-validate]") ? event.target.form : null; if (form) requestAnimationFrame(() => refreshErrors(form)); }, true);
1720
+ document.addEventListener("input", (event) => { const form = event.target.form?.matches("[data-bevel-validate]") ? event.target.form : null; if (form && event.target.hasAttribute("aria-invalid")) queueMicrotask(() => { if (event.target.validity.valid) event.target.removeAttribute("aria-invalid"); refreshErrors(form); }); }, true);
1721
+ document.addEventListener("reset", (event) => { const form = event.target.closest?.("form[data-bevel-validate]"); if (form) requestAnimationFrame(() => { for (const control of form.querySelectorAll('[aria-invalid="true"]')) control.removeAttribute("aria-invalid"); for (const message of form.querySelectorAll("[data-bevel-error-for]")) setFieldError(message, ""); const summary = form.querySelector(".bevel-errors"); if (summary) summary.hidden = true; summary?.querySelector(".bevel-errors__list")?.replaceChildren(); const session = wizardForms.get(form); for (const [root, step] of session?.initialSteps || []) { if (step == null) delete root.dataset.bevelStep; else root.dataset.bevelStep = step; refreshWizard(root); } }); }, true);
1722
+ document.addEventListener("reset", (event) => requestAnimationFrame(() => refresh(event.target)), true);
1723
+ on("click", "[data-bevel-action]", (e, button) => { if (disabled(button) || button.closest(".bevel-menu") || (button.hasAttribute("data-bevel-toggle") && button.closest(".bevel-toolbar"))) return; const root = button.closest(".bevel-action-banner,.bevel-bulk-bar,.bevel-toolbar,.bevel-breadcrumbs,.bevel-kanban,.bevel-settings,.bevel-popconfirm,.bevel-sidebar,.bevel-files") || button; action(root, button.dataset.bevelAction, button.dataset.bevelKey); });
1724
+ on("click", ".bevel-action-banner [data-bevel-banner-dismiss]", (event, button) => { const root = button.closest(".bevel-action-banner"); if (disabled(button)) return; transition(root, "dismiss", root.dataset.bevelKey || "", false, true, () => { root.hidden = true; root.dataset.bevelDismissed = ""; }); });
1725
+ on("keydown", ".bevel-bulk-bar button", (event, button) => {
1726
+ if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
1727
+ const buttons = [...button.closest(".bevel-bulk-bar").querySelectorAll("button")].filter(item => !disabled(item) && !item.hidden), index = buttons.indexOf(button), count = buttons.length;
1728
+ if (index < 0) return;
1729
+ event.preventDefault();
1730
+ buttons[event.key === "Home" ? 0 : event.key === "End" ? count - 1 : (index + (event.key === "ArrowRight" ? 1 : -1) + count) % count].focus();
1731
+ });
1732
+
1733
+ on("click", ".bevel-code [data-bevel-code-wrap]", (e, button) => { if (disabled(button)) return; const root = button.closest(".bevel-code"), previous = root.hasAttribute("data-bevel-wrap"); transition(root, "wrap", "wrap", previous, !previous, () => { root.toggleAttribute("data-bevel-wrap", !previous); refreshCode(root); }); });
1734
+ on("click", ".bevel-code [data-bevel-copy]", async (e, button) => {
1735
+ if (disabled(button)) return;
1736
+ const root = button.closest(".bevel-code"), code = root.querySelector("pre code") || root.querySelector("code"), text = code?.textContent || "";
1737
+ clearCodeFeedback(root);
1738
+ const session = {}; codeFeedback.set(root, session);
1739
+ let copied = false;
1740
+ try { await navigator.clipboard.writeText(text); copied = true; } catch {}
1741
+ if (!root.isConnected || codeFeedback.get(root) !== session) return;
1742
+ const notice = document.createElement("p"); notice.className = "bevel-code__notice"; notice.dataset.bevelTransient = "clipboard"; notice.setAttribute("role", "status");
1743
+ notice.textContent = copied ? "Copied" : "Could not copy. Select the code below and copy it manually."; root.append(notice);
1744
+ if (copied) session.timer = setTimeout(() => clearCodeFeedback(root), 1500);
1745
+ else {
1746
+ const fallback = document.createElement("textarea"); fallback.className = "bevel-code__fallback"; fallback.dataset.bevelTransient = "clipboard"; fallback.readOnly = true; fallback.setAttribute("aria-label", "Code to copy manually"); fallback.value = text; root.append(fallback); fallback.focus(); fallback.select();
1747
+ error(root, "clipboard", "Could not copy code to the clipboard.");
1748
+ }
1749
+ });
1750
+ on("click", ".bevel-diff button[data-bevel-layout]", (e, button) => { if (disabled(button)) return; const root = button.closest(".bevel-diff"), previous = root.dataset.bevelLayout || "unified", next = button.dataset.bevelLayout; if (!["unified", "split"].includes(next) || previous === next) return; transition(root, "layout", next, previous, next, () => { root.dataset.bevelLayout = next; refreshDiff(root); }); });
1751
+ on("click", ".bevel-activity [data-bevel-activity-more]", (event, button) => {
1752
+ if (disabled(button)) return;
1753
+ const root = button.closest(".bevel-activity");
1754
+ if (root.hasAttribute("data-bevel-expanded")) action(root, "load-more", root.dataset.bevelKey || "");
1755
+ else transition(root, "reveal", root.dataset.bevelKey || "", false, true, () => { root.dataset.bevelExpanded = ""; refreshActivity(root); });
1756
+ });
1757
+ document.addEventListener("error", (event) => { if (event.target?.matches?.(".bevel-avatars__image")) showAvatarFallback(event.target); }, true);
1758
+ on("click", ".bevel-chart [data-bevel-series]", (event, button) => {
1759
+ if (disabled(button)) return;
1760
+ const root = button.closest(".bevel-chart"), previous = button.getAttribute("aria-pressed") !== "false", next = !previous;
1761
+ transition(root, "series", button.dataset.bevelSeries, previous, next, () => { button.setAttribute("aria-pressed", String(next)); refreshChart(root); });
1762
+ });
1763
+ const kanbanSelect = (root, key) => {
1764
+ if (disabled(root) || root.dataset.bevelSelected === key) return;
1765
+ transition(root,'select',key,root.dataset.bevelSelected || '',key,()=>{ if (key) root.dataset.bevelSelected=key; else delete root.dataset.bevelSelected; if (root.hasAttribute('data-bevel-selected-keys')) root.dataset.bevelSelectedKeys=JSON.stringify(key ? [key] : []); refreshKanban(root); });
1766
+ };
1767
+ on('input','.bevel-kanban__search input,input.bevel-kanban__search',(event,input)=>{
1768
+ const root=input.closest('.bevel-kanban'),previous=input.getAttribute('value') || '',next=input.value;
1769
+ if (kanbanDisabled(input)) { input.value=previous; return; }
1770
+ const committed=localOrRemote(root,'search','',previous,next,()=>{input.setAttribute('value',next);root.dataset.bevelQuery=next;refreshKanban(root);});
1771
+ if (!committed || root.dataset.bevelDataMode==='remote') input.value=previous;
1772
+ });
1773
+ on('click','.bevel-kanban__card',(event,card)=>{if (!kanbanDisabled(card) && !event.target.closest('button,a,input,select,textarea')) kanbanSelect(card.closest('.bevel-kanban'),card.dataset.bevelKey);});
1774
+ on('keydown','.bevel-kanban__card',(event,card)=>{
1775
+ if (event.target !== card || kanbanDisabled(card)) return;
1776
+ const root=card.closest('.bevel-kanban');
1777
+ if (event.key==='Enter' || event.key===' ') {event.preventDefault();kanbanSelect(root,card.dataset.bevelKey);return;}
1778
+ if (!event.altKey || !['ArrowLeft','ArrowRight','ArrowUp','ArrowDown'].includes(event.key)) return;
1779
+ event.preventDefault();const list=card.parentElement,columns=[...root.querySelectorAll('.bevel-kanban__list')].filter(node=>node.closest('.bevel-kanban')===root),ci=columns.indexOf(list),siblings=[...list.children].filter(node=>node.matches('.bevel-kanban__card')),index=siblings.indexOf(card);
1780
+ if (event.key==='ArrowUp' || event.key==='ArrowDown') kanbanMove(root,card,list,Math.max(0,Math.min(siblings.length-1,index+(event.key==='ArrowUp'?-1:1))));
1781
+ else {const destination=columns[ci+(event.key==='ArrowLeft'?-1:1)];if(destination)kanbanMove(root,card,destination,[...destination.children].filter(node=>node.matches('.bevel-kanban__card')).length);}
1782
+ });
1783
+ on('click','[data-bevel-kanban-create]',(event,button)=>{if(kanbanDisabled(button))return;const root=button.closest('.bevel-kanban');kanbanEditor(root,null,button.dataset.bevelColumn || button.closest('.bevel-kanban__column')?.dataset.bevelColumnKey || root.querySelector('.bevel-kanban__column')?.dataset.bevelColumnKey);});
1784
+ on('click','[data-bevel-kanban-edit]',(event,button)=>{if(kanbanDisabled(button))return;const root=button.closest('.bevel-kanban'),card=kanbanSelected(root);if(card)kanbanEditor(root,card);});
1785
+ on('click','[data-bevel-kanban-close]',(event,button)=>{if(kanbanDisabled(button))return;const root=button.closest('.bevel-kanban'),card=kanbanSelected(root);kanbanSelect(root,'');if(!root.dataset.bevelSelected)card?.focus();});
1786
+ on('input','[data-bevel-kanban-form] [name="title"]',(event,input)=>input.setCustomValidity(''));
1787
+ on('keydown','[data-bevel-kanban-form]',(event)=>{if(event.isComposing && event.key==='Enter')event.preventDefault();});
1788
+ on('submit','[data-bevel-kanban-form]',(event,form)=>{
1789
+ event.preventDefault();event.stopPropagation();const root=form.closest('.bevel-kanban');
1790
+ if(disabled(root)||disabled(event.submitter || form.querySelector('[data-bevel-kanban-save]')))return;
1791
+ const key=form.elements.key.value,existing=kanbanCards(root).find(card=>card.dataset.bevelKey===key),title=form.elements.title;
1792
+ title.setCustomValidity(title.value.trim() ? '' : 'Enter a task title.');if(!form.reportValidity())return;
1793
+ const value={key:key || `FN-${Math.max(100,...kanbanCards(root).map(card=>Number(card.dataset.bevelKey.replace(/\D/g,''))||0))+1}`,column:form.elements.column.value,title:title.value.trim(),description:form.elements.description.value.trim(),assignee:form.elements.assignee.value,priority:form.elements.priority.value,due:form.elements.due.value,labels:[...new Set(form.elements.labels.value.split(',').map(label=>label.trim().toLowerCase()).filter(Boolean))]};
1794
+ const column=root.querySelector(`.bevel-kanban__column[data-bevel-column-key="${CSS.escape(value.column)}"] .bevel-kanban__list`);if(!column)return;
1795
+ const operation=existing?'edit':'create';
1796
+ localOrRemote(root,operation,value.key,kanbanSnapshot(existing),value,()=>{
1797
+ if(existing)updateKanbanCard(existing,value);else{const card=kanbanCard(value);column.insertBefore(card,column.querySelector('.bevel-kanban__add'));updateKanbanCard(card,value);}
1798
+ root.dataset.bevelSelected=value.key;closeOverlay(form.closest('dialog'),{focus:false});refreshKanban(root);root.querySelector('[data-bevel-kanban-edit]')?.focus();
1799
+ },`${operation}:${value.key}`);
1800
+ });
1801
+ on('click','[data-bevel-kanban-archive],[data-bevel-kanban-restore]',(event,button)=>{
1802
+ if(kanbanDisabled(button))return;const root=button.closest('.bevel-kanban'),card=kanbanSelected(root);if(!card)return;
1803
+ const previous=card.hasAttribute('data-bevel-archived'),next=!button.hasAttribute('data-bevel-kanban-restore');if(previous===next)return;
1804
+ localOrRemote(root,next?'archive':'restore',card.dataset.bevelKey,previous,next,()=>{card.toggleAttribute('data-bevel-archived',next);if(button.closest('[popover]'))closeOverlay(button.closest('[popover]'),{focus:false});refreshKanban(root);root.querySelector(next?'[data-bevel-kanban-restore]':'[data-bevel-kanban-edit]')?.focus();},`archive:${card.dataset.bevelKey}`);
1805
+ });
1806
+ on('change','[data-bevel-kanban-inspector] [data-bevel-check-key]',(event,input)=>{
1807
+ const root=input.closest('.bevel-kanban'),card=kanbanSelected(root),source=card && kanbanItems(card).find(node=>node.dataset.bevelCheckKey===input.dataset.bevelCheckKey);if(!source)return;
1808
+ const previous=source.checked,next=input.checked;
1809
+ const committed=!kanbanDisabled(input)&&localOrRemote(root,'checklist',card.dataset.bevelKey,{key:source.dataset.bevelCheckKey,checked:previous},{key:source.dataset.bevelCheckKey,checked:next},()=>{source.checked=next;source.toggleAttribute('checked',next);refreshKanban(root);root.querySelector(`[data-bevel-kanban-inspector] [data-bevel-check-key="${CSS.escape(source.dataset.bevelCheckKey)}"]`)?.focus();},`checklist:${card.dataset.bevelKey}:${source.dataset.bevelCheckKey}`);
1810
+ if(!committed || root.dataset.bevelDataMode==='remote'){input.checked=previous;input.toggleAttribute('checked',previous);}
1811
+ });
1812
+ on('click','[data-bevel-kanban-check-add],[data-bevel-kanban-check-cancel]',(event,button)=>{
1813
+ if(kanbanDisabled(button))return;const root=button.closest('.bevel-kanban'),form=root.querySelector('[data-bevel-kanban-check-form]');if(!form)return;
1814
+ form.hidden=button.hasAttribute('data-bevel-kanban-check-cancel');if(form.hidden){form.reset();root.querySelector('[data-bevel-kanban-check-add]')?.focus();}else form.elements.text.focus();
1815
+ });
1816
+ on('input','[data-bevel-kanban-check-form] [name="text"]',(event,input)=>input.setCustomValidity(''));
1817
+ on('submit','[data-bevel-kanban-check-form]',(event,form)=>{
1818
+ event.preventDefault();event.stopPropagation();const root=form.closest('.bevel-kanban'),card=kanbanSelected(root);if(!card||disabled(root)||disabled(event.submitter))return;
1819
+ const text=form.elements.text.value.trim();form.elements.text.setCustomValidity(text ? '' : 'Enter a checklist item.');if(!form.reportValidity())return;const next={key:`check-${crypto.randomUUID()}`,text,checked:false};
1820
+ localOrRemote(root,'checklist-add',card.dataset.bevelKey,null,next,()=>{let list=card.querySelector('.bevel-kanban__checklist-data');if(!list){list=kanbanNode('div','bevel-kanban__checklist-data');list.hidden=true;card.append(list);}const label=kanbanNode('label'),input=kanbanNode('input');input.type='checkbox';input.dataset.bevelCheckKey=next.key;label.append(input,document.createTextNode(text));list.append(label);refreshKanban(root);root.querySelector('[data-bevel-kanban-check-add]')?.focus();},`checklist:${card.dataset.bevelKey}`);
1821
+ });
1822
+ on('click','[data-bevel-kanban-check-remove]',(event,button)=>{
1823
+ if(kanbanDisabled(button))return;const root=button.closest('.bevel-kanban'),card=kanbanSelected(root),input=card && kanbanItems(card).find(item=>item.dataset.bevelCheckKey===button.dataset.bevelKanbanCheckRemove);if(!input)return;
1824
+ localOrRemote(root,'checklist-remove',card.dataset.bevelKey,{key:input.dataset.bevelCheckKey,text:input.parentElement.textContent.trim(),checked:input.checked},null,()=>{input.parentElement.remove();refreshKanban(root);root.querySelector('[data-bevel-kanban-check-add]')?.focus();},`checklist:${card.dataset.bevelKey}`);
1825
+ });
1826
+ on('click','[data-bevel-kanban-move]',(event,button)=>{
1827
+ if(kanbanDisabled(button))return;const root=button.closest('.bevel-kanban'),card=kanbanSelected(root),key=root.querySelector('[data-bevel-kanban-move-column]').value,list=root.querySelector(`.bevel-kanban__column[data-bevel-column-key="${CSS.escape(key)}"] .bevel-kanban__list`);
1828
+ if(list)kanbanMove(root,card,list,root.querySelector('[data-bevel-kanban-move-position]').value==='first'?0:[...list.children].filter(node=>node.matches('.bevel-kanban__card')).length);
1829
+ });
1830
+ on('change','[data-bevel-kanban-filter]',(event,control)=>{
1831
+ const root=control.closest('.bevel-kanban'),kind=control.dataset.bevelKanbanFilter;if(!kanbanFilterKinds.includes(kind))return;
1832
+ const attribute=`data-bevel-filter-${kind}`,previous=root.getAttribute(attribute)||'',next=control.value;
1833
+ const committed=!kanbanDisabled(control)&&localOrRemote(root,'filter',kind,previous,next,()=>{if(next)root.setAttribute(attribute,next);else root.removeAttribute(attribute);refreshKanban(root);},`filter:${kind}`);
1834
+ if(!committed||root.dataset.bevelDataMode==='remote'){control.value=previous;if(control.matches('input'))control.setAttribute('value',previous);else for(const option of control.options)option.toggleAttribute('selected',option.value===previous);}
1835
+ });
1836
+ on('click','[data-bevel-kanban-filter-clear]',(event,button)=>{
1837
+ if(kanbanDisabled(button))return;const root=button.closest('.bevel-kanban'),previous={from:root.dataset.bevelFilterDueFrom||'',to:root.dataset.bevelFilterDueTo||''};
1838
+ localOrRemote(root,'filter','due',previous,{from:'',to:''},()=>{delete root.dataset.bevelFilterDueFrom;delete root.dataset.bevelFilterDueTo;refreshKanban(root);},'filter:due');
1839
+ });
1840
+ on('keydown','.bevel-kanban',(event,root)=>{if(event.metaKey || event.ctrlKey){if(event.key.toLowerCase()==='k'){event.preventDefault();(root.querySelector('.bevel-kanban__search input')||root.querySelector('input.bevel-kanban__search'))?.focus();}else if(event.key==='Enter' && event.target.closest('.bevel-kanban__composer')){event.preventDefault();event.target.closest('.bevel-kanban__composer').click();}}});
1841
+ on('pointerdown','.bevel-kanban__card' ,(event,card)=>{
1842
+ if(event.button!==0||kanbanDisabled(card)||event.target.closest('button,a,input,select,textarea')||sessions.has(card))return;
1843
+ const root=card.closest('.bevel-kanban');if(kanbanFiltered(root)){root.dataset.bevelMoveMessage='Clear filters to drag tasks. Use Move while filters are active.';return;}
1844
+ const pointerId=event.pointerId,startX=event.clientX,startY=event.clientY,session={active:false,list:null,target:null,before:false,cancel:()=>stop(false)};
1845
+ const clear=()=>{session.target?.removeAttribute('data-bevel-drop-target');session.target=null;session.list=null;};
1846
+ const move=current=>{
1847
+ if(current.pointerId!==pointerId)return;if(!session.active&&Math.hypot(current.clientX-startX,current.clientY-startY)<5)return;
1848
+ session.active=true;card.dataset.bevelDragging='';clear();const hit=document.elementFromPoint(current.clientX,current.clientY),target=hit?.closest('.bevel-kanban__card'),list=hit?.closest('.bevel-kanban__list')||hit?.closest('.bevel-kanban__column')?.querySelector('.bevel-kanban__list');
1849
+ if(!list||list.closest('.bevel-kanban')!==root||target===card)return;session.list=list;
1850
+ if(target&&target!==card){session.target=target;const rect=target.getBoundingClientRect();session.before=current.clientY<rect.top+rect.height/2;target.dataset.bevelDropTarget=session.before?'before':'after';}
1851
+ };
1852
+ const stop=commit=>{
1853
+ if(!sessions.has(card))return;document.removeEventListener('pointermove',move);document.removeEventListener('pointerup',up);document.removeEventListener('pointercancel',cancel);document.removeEventListener('lostpointercapture',cancel);document.removeEventListener('keydown',escape,true);sessions.delete(card);card.removeAttribute('data-bevel-dragging');
1854
+ const {list,target,before}=session;clear();if(!commit||!session.active||!list||!root.isConnected||!card.isConnected||target&&!target.isConnected)return;
1855
+ const siblings=[...list.children].filter(node=>node.matches('.bevel-kanban__card')&&node!==card),index=target?siblings.indexOf(target)+(before?0:1):siblings.length;
1856
+ kanbanMove(root,card,list,index);
1857
+ };
1858
+ const up=current=>{if(current.pointerId===pointerId)stop(true);},cancel=current=>{if(current.pointerId===pointerId)stop(false);},escape=current=>{if(current.key==='Escape'){current.preventDefault();stop(false);}};
1859
+ sessions.set(card,session);document.addEventListener('pointermove',move);document.addEventListener('pointerup',up);document.addEventListener('pointercancel',cancel);document.addEventListener('lostpointercapture',cancel);document.addEventListener('keydown',escape,true);
1860
+ });
1861
+
1862
+ const cancelGridEdit = (cell, focus = true) => { const session=sessions.get(cell); if(!session)return; session.editor.remove(); cell.removeAttribute("data-bevel-editing"); cell.removeAttribute("data-bevel-invalid"); sessions.delete(cell); const error=cell.querySelector(".bevel-grid__cell-error");if(error)error.hidden=true;if (focus && session.button?.isConnected) session.button.focus(); };
1863
+ const beginGridEdit = cell => { const grid=gridOf(cell); if(!grid||disabled(cell))return; const active=grid.querySelector('.bevel-grid__cell[data-bevel-editing]'); if(active&&active!==cell)cancelGridEdit(active); if(sessions.has(cell))return; const button=cell.querySelector('[data-bevel-cell-edit]'), value=cell.dataset.value ?? button?.querySelector('.bevel-grid__cell-value')?.textContent ?? cell.textContent.trim(), type=cell.dataset.bevelEdit;
1864
+ const editor=document.createElement("span"); editor.className="bevel-grid__cell-editor"; editor.dataset.bevelTransient="grid-editor"; let input;
1865
+ if(type==="enum"){ input=document.createElement("select"); input.className="bevel-select__field"; for(const option of JSON.parse(cell.dataset.bevelOptions||"[]")){ const node=document.createElement("option"); node.value=option.id; node.textContent=option.label; input.append(node); } }
1866
+ else { input=document.createElement("input"); input.className="bevel-grid__cell-input"; input.type="text"; if(type==="number") input.inputMode="decimal"; }
1867
+ input.value=value; input.setAttribute("aria-label",cell.dataset.bevelLabel||`Edit ${cell.dataset.bevelColumn||"cell"}`); editor.append(input);
1868
+ for (const [name, icon] of [["save", '<svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.71" stroke-linecap="square" stroke-linejoin="miter"><path d="m5 12 4 4L19 6"/></svg>'], ["cancel", '<svg aria-hidden="true" width="14" height="14" viewBox="0 0 256 256" fill="currentColor"><path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"/></svg>']]) {
1869
+ const control=document.createElement("button");control.type="button";control.className="bevel-grid__cell-action";control.dataset.bevelCellAction=name;control.setAttribute("aria-label", name==="save"?"Save cell":"Cancel cell");control.innerHTML=icon;editor.append(control);
1870
+ } cell.append(editor); cell.dataset.bevelEditing=""; const canonical = [cell.dataset.value, cell.dataset.sort, button?.querySelector(".bevel-grid__cell-value")?.textContent]; const valid = () => cell.contains(editor) && cell.querySelector("[data-bevel-cell-edit]") === button && JSON.stringify([cell.dataset.value, cell.dataset.sort, button?.querySelector(".bevel-grid__cell-value")?.textContent]) === JSON.stringify(canonical); sessions.set(cell,{editor,input,button,value,valid,cancel:()=>cancelGridEdit(cell,false)}); input.focus(); input.select?.(); };
1871
+ const gridEditable = grid => [...grid.querySelectorAll('.bevel-grid__cell[data-bevel-edit]')].filter(cell=>!cell.hidden&&!cell.closest("tr")?.hidden&&!disabled(cell));
1872
+ const resortGrid = grid => { const th=grid.querySelector('thead th[aria-sort="ascending"],thead th[aria-sort="descending"]'); if(!th)return; const index=[...th.parentElement.children].indexOf(th),type=th.dataset.type||"text",sign=th.getAttribute("aria-sort")==="ascending"?1:-1,tbody=grid.querySelector("tbody"),units=[]; for(const row of [...tbody.children])if(row.classList.contains("bevel-grid__row")){const unit=[row],detail=detailOf(row);if(detail)unit.push(detail);units.push(unit);} units.sort((a,b)=>{const x=cellKey(a[0],index,type),y=cellKey(b[0],index,type);return x<y?-sign:x>y?sign:0;});tbody.append(...units.flat()); };
1873
+ const commitGridEdit = (cell,direction=0) => { const session=sessions.get(cell),grid=gridOf(cell); if(!session||!grid)return false; if(!session.valid()){cancelGridEdit(cell,false);return false;} const input=session.input,next=input.value,type=cell.dataset.bevelEdit,key=cell.closest("tr")?.dataset.bevelKey||"",column=cell.dataset.bevelColumn||""; if((type==="number"&&(next.trim()===""||!Number.isFinite(Number(next))))||!input.checkValidity()){ cell.dataset.bevelInvalid=""; input.setAttribute("aria-invalid","true"); const error=cell.querySelector('.bevel-grid__cell-error'); if(error){error.hidden=false;if(!error.id)error.id=`bevel-grid-error-${crypto.randomUUID()}`;input.setAttribute("aria-describedby",error.id);} input.focus(); return false; } const previous=cell.dataset.value??session.value, display=type==="number"&&cell.dataset.bevelFormat==="currency"?new Intl.NumberFormat("en-US",{style:"currency",currency:"USD"}).format(Number(next)):type==="enum"?input.selectedOptions[0]?.textContent||next:next; const finish=()=>{ const value=cell.querySelector('.bevel-grid__cell-value'); if(value)value.textContent=display; cell.dataset.value=next; cell.dataset.sort=next; session.editor.remove(); cell.removeAttribute("data-bevel-editing"); cell.removeAttribute("data-bevel-invalid"); sessions.delete(cell); const error=cell.querySelector(".bevel-grid__cell-error");if(error)error.hidden=true;resortGrid(grid); layoutGrid(grid); const target=grid.querySelector(`tr[data-bevel-key="${CSS.escape(key)}"] [data-bevel-column="${CSS.escape(column)}"]`); if(target?.closest("tr")?.hidden){ (gridEditable(grid).find(candidate=>!candidate.closest("tr")?.hidden)?.querySelector("[data-bevel-cell-edit]")||grid.querySelector(".bevel-grid__status,[tabindex]")||grid)?.focus(); } else if(direction){ const cells=gridEditable(grid),index=cells.indexOf(target); (cells[index+direction]||target)?.querySelector("[data-bevel-cell-edit]")?.focus(); } else target?.querySelector("[data-bevel-cell-edit]")?.focus(); };
1874
+ return grid.dataset.bevelDataMode==="remote" ? (remoteRequest(grid,"edit",key,{column,value:previous},{column,value:next},`edit:${key}:${column}`),cancelGridEdit(cell),true) : transition(grid,"edit",key,{column,value:previous},{column,value:next},finish); };
1875
+ on("click", ".bevel-grid__cell [data-bevel-cell-action]", (event, button) => { const cell=button.closest(".bevel-grid__cell");button.dataset.bevelCellAction==="save"?commitGridEdit(cell):cancelGridEdit(cell); });
1876
+ on("click", ".bevel-grid__cell [data-bevel-cell-edit]",(e,button)=>beginGridEdit(button.closest(".bevel-grid__cell")));
1877
+ on("dblclick", ".bevel-grid__cell[data-bevel-edit]",(e,cell)=>beginGridEdit(cell));
1878
+ on("keydown", ".bevel-grid__cell[data-bevel-edit]",(e,cell)=>{ if(!cell.hasAttribute("data-bevel-editing")&&(e.key==="Enter"||e.key==="F2")){e.preventDefault();beginGridEdit(cell);} });
1879
+ on("keydown", ".bevel-grid__cell[data-bevel-edit] :is(input,select)",(e,input)=>{ if(e.isComposing)return; const cell=input.closest(".bevel-grid__cell"); if(e.key==="Escape"){e.preventDefault();cancelGridEdit(cell);} else if(e.key==="Enter"){e.preventDefault();commitGridEdit(cell);} else if(e.key==="Tab"){e.preventDefault();commitGridEdit(cell,e.shiftKey?-1:1);} });
1880
+ on("pointerdown","body",(e)=>{ for(const cell of document.querySelectorAll('.bevel-grid__cell[data-bevel-editing]')) if(!cell.contains(e.target))cancelGridEdit(cell); });
1881
+ const filterDraft = root => root.querySelector(".bevel-filter__draft");
1882
+ on("click",".bevel-filter [data-bevel-filter-edit]",(e,button)=>{ const root=button.closest(".bevel-filter"),draft=filterDraft(root),committed=root.querySelector(".bevel-filter__rules"); draft.replaceChildren(...[...committed.children].map(node=>node.cloneNode(true))); draft.querySelectorAll(".bevel-filter__rule").forEach(refreshFilterRule); draft.hidden=false; committed.hidden=true; root.dataset.bevelEditing=""; });
1883
+ on("change",".bevel-filter__draft [data-bevel-filter-field]",(e,select)=>{const row=select.closest(".bevel-filter__rule");row.dataset.bevelField=select.value;refreshFilterRule(row);});
1884
+ on("change",".bevel-filter__draft [data-bevel-filter-operator]",(e,select)=>{const row=select.closest(".bevel-filter__rule");row.dataset.bevelOperator=select.value;const input=row.querySelector("[data-bevel-filter-value]");if(input)input.disabled=select.value==="is-empty";});
1885
+ on("click",".bevel-filter [data-bevel-filter-add]",(e,button)=>{const root=button.closest(".bevel-filter"),template=root.querySelector("template[data-bevel-filter-rule]"),rule=template?.content.firstElementChild?.cloneNode(true);if(rule){rule.dataset.bevelKey=crypto.randomUUID?.()||String(Date.now());filterDraft(root).append(rule);refreshFilterRule(rule);rule.querySelector("select,input")?.focus();}});
1886
+ on("click",".bevel-filter [data-bevel-filter-remove]",(e,button)=>button.closest(".bevel-filter__rule")?.remove());
1887
+ on("click",".bevel-filter [data-bevel-filter-cancel]",(e,button)=>{ const root=button.closest(".bevel-filter"); filterDraft(root).replaceChildren(); filterDraft(root).hidden=true; root.querySelector(".bevel-filter__rules").hidden=false; root.removeAttribute("data-bevel-editing"); });
1888
+ on("click", ".bevel-filter [data-bevel-filter-apply]", (e, button) => { const root=button.closest(".bevel-filter"),draft=filterDraft(root),committed=root.querySelector(".bevel-filter__rules"),definitions=filterDefinitions(root),expression={match:root.dataset.bevelMatch==="any"?"any":"all",rules:[...draft.children].map(row=>({key:row.dataset.bevelKey,field:row.dataset.bevelField,operator:row.dataset.bevelOperator,value:row.querySelector('[data-bevel-filter-value]')?.value??""}))}; if(expression.rules.some(rule=>!filterRuleValid(rule,definitions.find(item=>item.id===rule.field)))){root.dataset.bevelInvalid="";return;} const grid=targetById(root.dataset.bevelGrid),previous=filterRead(root); const commit=()=>{committed.replaceChildren(...[...draft.children].map(node=>node.cloneNode(true))); draft.replaceChildren(); draft.hidden=true; committed.hidden=false; root.removeAttribute("data-bevel-editing"); root.removeAttribute("data-bevel-invalid"); if(grid){grid.dataset.page="1";layoutGrid(grid);} }; if(grid)localOrRemote(grid,"filter","",previous,expression,commit); else transition(root,"filter","",previous,expression,commit); });
1889
+ const columnHeaders = grid => [...grid.querySelectorAll("thead > tr:first-child > [data-bevel-column]")];
1890
+ const rememberGridFields = grid => {
1891
+ const cells = [...grid.querySelector("thead > tr")?.children || []];
1892
+ for (const node of grid.querySelectorAll("[data-grid-column]")) if (!node.dataset.bevelField) {
1893
+ const key = cells[Number(node.dataset.gridColumn)]?.dataset.bevelColumn;
1894
+ if (key) node.dataset.bevelField = key;
1895
+ }
1896
+ };
1897
+ const orderColumn = (manager, key, destination, before) => {
1898
+ const grid = targetById(manager.dataset.bevelGrid), headers = columnHeaders(grid);
1899
+ rememberGridFields(grid);
1900
+ const header = headers.find(node => node.dataset.bevelColumn === key), target = headers.find(node => node.dataset.bevelColumn === destination);
1901
+ if (header && target) before ? target.before(header) : target.after(header);
1902
+ refreshColumns(manager);
1903
+ };
1904
+ const refreshColumns = manager => {
1905
+ const grid = targetById(manager.dataset.bevelGrid), table = grid?.querySelector(".bevel-grid__table");
1906
+ if (!table) return;
1907
+ rememberGridFields(grid);
1908
+ const headers = columnHeaders(grid), order = headers.map(node => node.dataset.bevelColumn);
1909
+ const attr = (node, name, value) => { if (node.getAttribute(name) !== String(value)) node.setAttribute(name, String(value)); };
1910
+ const flag = (node, name, value) => { if (node.hasAttribute(name) !== Boolean(value)) node.toggleAttribute(name, Boolean(value)); };
1911
+ const style = (node, name, value) => { if (node.style[name] !== value) node.style[name] = value; };
1912
+ const managerItems = [...manager.querySelectorAll(":scope > .bevel-columns__item")];
1913
+ const arranged = order.map(key => managerItems.find(node => node.dataset.bevelColumn === key)).filter(Boolean);
1914
+ if (arranged.some((node, index) => node !== managerItems[index])) manager.append(...arranged);
1915
+ for (const row of table.querySelectorAll("tbody > tr,tfoot > tr")) {
1916
+ const cells = [...row.children].filter(node => node.dataset.bevelColumn), arrangedCells = order.map(key => cells.find(node => node.dataset.bevelColumn === key)).filter(Boolean);
1917
+ if (arrangedCells.some((node, index) => node !== cells[index])) row.append(...arrangedCells);
1918
+ }
1919
+ let colgroup = table.querySelector(":scope > colgroup");
1920
+ if (!colgroup) { colgroup = document.createElement("colgroup"); table.prepend(colgroup); }
1921
+ flag(colgroup, "data-bevel-columns", true);
1922
+ const allHeaders = [...table.querySelector("thead > tr").children];
1923
+ while (colgroup.children.length < allHeaders.length) colgroup.append(document.createElement("col"));
1924
+ for (const node of grid.querySelectorAll("[data-bevel-field][data-grid-column]")) {
1925
+ const index = allHeaders.findIndex(header => header.dataset.bevelColumn === node.dataset.bevelField);
1926
+ if (index >= 0) attr(node, "data-grid-column", index);
1927
+ }
1928
+ let left = 0, visibleCount = 0;
1929
+ for (const [index, header] of allHeaders.entries()) {
1930
+ const key = header.dataset.bevelColumn, col = colgroup.children[index];
1931
+ if (!key) continue;
1932
+ const item = arranged.find(node => node.dataset.bevelColumn === key), required = item?.hasAttribute("data-bevel-column-required");
1933
+ if (required) { flag(header, "hidden", false); flag(header, "data-bevel-pinned", true); }
1934
+ const visible = !header.hidden, pinned = header.hasAttribute("data-bevel-pinned"), width = header.dataset.bevelWidth;
1935
+ attr(col, "data-bevel-column", key); style(col, "width", visible ? width ? `${Number(width)}px` : "" : "0px");
1936
+ for (const cell of table.querySelectorAll(`[data-bevel-column="${CSS.escape(key)}"]`)) {
1937
+ if (cell.tagName === "COL") continue;
1938
+ flag(cell, "hidden", !visible); flag(cell, "data-bevel-pinned", pinned);
1939
+ style(cell, "width", width ? `${Number(width)}px` : "");
1940
+ style(cell, "left", pinned && visible ? `${left}px` : "");
1941
+ }
1942
+ if (visible) { visibleCount++; if (pinned) left += header.getBoundingClientRect().width; }
1943
+ const input = item?.querySelector("[data-bevel-column-visible]");
1944
+ if (input) { if (input.checked !== visible) input.checked = visible; flag(input, "checked", visible); if (required) input.disabled = true; }
1945
+ const number = item?.querySelector("[data-bevel-column-position]");
1946
+ if (number && number.textContent !== String(index + 1)) number.textContent = String(index + 1);
1947
+ const up = item?.querySelector('[data-bevel-column-move="up"]'), down = item?.querySelector('[data-bevel-column-move="down"]');
1948
+ if (up) up.disabled = Boolean(required || index === 0 || arranged[index - 1]?.hasAttribute("data-bevel-column-required"));
1949
+ if (down) down.disabled = Boolean(required || index === allHeaders.length - 1);
1950
+ const pin = item?.querySelector("[data-bevel-column-pin]"); if (pin) { attr(pin, "aria-pressed", pinned); pin.disabled = Boolean(required); }
1951
+ const handle = header.querySelector(".bevel-grid__resize");
1952
+ if (handle) { attr(handle, "aria-valuemin", Number(header.dataset.columnMin) || 80); attr(handle, "aria-valuemax", Number(header.dataset.columnMax) || 800); attr(handle, "aria-valuenow", Number(width) || Math.round(header.getBoundingClientRect().width)); }
1953
+ }
1954
+ left = 0;
1955
+ for (const header of headers) {
1956
+ const pinned = header.hasAttribute("data-bevel-pinned") && !header.hidden;
1957
+ for (const cell of table.querySelectorAll(`[data-bevel-column="${CSS.escape(header.dataset.bevelColumn)}"]`)) if (cell.tagName !== "COL") style(cell, "left", pinned ? `${left}px` : "");
1958
+ if (pinned) left += header.getBoundingClientRect().width;
1959
+ }
1960
+ const span = allHeaders.filter(header => !header.hidden).length;
1961
+ for (const cell of table.querySelectorAll(".bevel-grid__detail > td,.bevel-grid__group > td,.bevel-grid__empty > td")) if (cell.colSpan !== span) cell.colSpan = span;
1962
+ const summary = manager.closest(".bevel-columns-panel")?.querySelector("[data-bevel-column-summary]"), text = `${visibleCount} of ${headers.length} shown`;
1963
+ if (summary && summary.textContent !== text) summary.textContent = text;
1964
+ attr(grid, "data-bevel-column-order", order.join(" "));
1965
+ layoutGrid(grid);
1966
+ };
1967
+ on("click", ".bevel-columns [data-bevel-column-move]", (event, button) => {
1968
+ if (disabled(button)) return;
1969
+ const manager = button.closest(".bevel-columns"), item = button.closest(".bevel-columns__item"), up = button.dataset.bevelColumnMove === "up", sibling = up ? item.previousElementSibling : item.nextElementSibling;
1970
+ if (!sibling || item.hasAttribute("data-bevel-column-required") || sibling.hasAttribute("data-bevel-column-required")) return;
1971
+ transition(manager, "column-order", item.dataset.bevelColumn, [...manager.children].indexOf(item), [...manager.children].indexOf(sibling), () => orderColumn(manager, item.dataset.bevelColumn, sibling.dataset.bevelColumn, up));
1972
+ });
1973
+ on("change", ".bevel-columns [data-bevel-column-visible]", (event, input) => {
1974
+ const manager = input.closest(".bevel-columns"), item = input.closest(".bevel-columns__item"), grid = targetById(manager.dataset.bevelGrid), header = columnHeaders(grid).find(node => node.dataset.bevelColumn === item.dataset.bevelColumn), checked = input.checked, previous = !header.hidden;
1975
+ const remaining = columnHeaders(grid).filter(node => !node.hidden && node !== header && !manager.querySelector(`[data-bevel-column="${CSS.escape(node.dataset.bevelColumn)}"]`)?.hasAttribute("data-bevel-column-required"));
1976
+ if (item.hasAttribute("data-bevel-column-required") || (!checked && !remaining.length) || !transition(manager, "column-visibility", item.dataset.bevelColumn, previous, checked, () => { header.hidden = !checked; refreshColumns(manager); })) {
1977
+ input.checked = previous; input.toggleAttribute("checked", previous);
1978
+ }
1979
+ });
1980
+ on("click", ".bevel-columns [data-bevel-column-pin]", (event, button) => {
1981
+ if (disabled(button)) return;
1982
+ const manager = button.closest(".bevel-columns"), item = button.closest(".bevel-columns__item"), grid = targetById(manager.dataset.bevelGrid), header = columnHeaders(grid).find(node => node.dataset.bevelColumn === item.dataset.bevelColumn);
1983
+ if (item.hasAttribute("data-bevel-column-required")) return;
1984
+ const previous = header.hasAttribute("data-bevel-pinned");
1985
+ transition(manager, "column-pin", item.dataset.bevelColumn, previous, !previous, () => { header.toggleAttribute("data-bevel-pinned", !previous); refreshColumns(manager); });
1986
+ });
1987
+ const resizeColumn=(handle,width)=>{const header=handle.closest("th"),grid=gridOf(header),min=Number(header.dataset.columnMin)||80,max=Number(header.dataset.columnMax)||800,next=Math.min(max,Math.max(min,width)),previous=Number(header.dataset.bevelWidth)||Math.round(header.getBoundingClientRect().width); return transition(grid,"column-resize",header.dataset.bevelColumn,previous,next,()=>{header.dataset.bevelWidth=String(Math.round(next));handle.setAttribute("aria-valuenow",String(Math.round(next)));const manager=document.querySelector(`.bevel-columns[data-bevel-grid="${CSS.escape(grid.id)}"]`);if(manager)refreshColumns(manager);});};
1988
+ on("keydown", ".bevel-grid__resize",(e,handle)=>{if(!["ArrowLeft","ArrowRight","Home","End"].includes(e.key))return;e.preventDefault();const header=handle.closest("th"),min=Number(header.dataset.columnMin)||80,max=Number(header.dataset.columnMax)||800,current=Number(header.dataset.bevelWidth)||Math.round(header.getBoundingClientRect().width),step=e.shiftKey?32:8;resizeColumn(handle,e.key==="Home"?min:e.key==="End"?max:current+(e.key==="ArrowRight"?step:-step));});
1989
+ on("pointerdown", ".bevel-grid__resize",(e,handle)=>{if(e.button!==0||sessions.has(handle))return;const header=handle.closest("th"),start=e.clientX,pointerId=e.pointerId,previous=Number(header.dataset.bevelWidth)||Math.round(header.getBoundingClientRect().width);e.preventDefault();handle.setPointerCapture(pointerId);const session={current:previous,cancel:()=>stop(false)},move=current=>{if(current.pointerId===pointerId){session.current=Math.min(Number(header.dataset.columnMax)||800,Math.max(Number(header.dataset.columnMin)||80,previous+current.clientX-start));header.style.width=`${session.current}px`;handle.setAttribute("aria-valuenow",String(Math.round(session.current)));}},stop=commit=>{if(!sessions.has(handle))return;handle.removeEventListener("pointermove",move);handle.removeEventListener("pointerup",up);handle.removeEventListener("pointercancel",cancel);handle.removeEventListener("lostpointercapture",lost);document.removeEventListener("keydown",escape,true);header.style.width="";handle.setAttribute("aria-valuenow",String(previous));sessions.delete(handle);if(handle.hasPointerCapture(pointerId))handle.releasePointerCapture(pointerId);if(commit&&handle.isConnected)resizeColumn(handle,session.current);},up=current=>{if(current.pointerId===pointerId)stop(true);},cancel=current=>{if(current.pointerId===pointerId)stop(false);},lost=()=>stop(false),escape=current=>{if(current.key==="Escape"){current.preventDefault();stop(false);}};sessions.set(handle,session);handle.addEventListener("pointermove",move);handle.addEventListener("pointerup",up);handle.addEventListener("pointercancel",cancel);handle.addEventListener("lostpointercapture",lost);document.addEventListener("keydown",escape,true);});
1990
+ on("pointerdown", ".bevel-columns__grip",(e,grip)=>{if(e.button!==0||sessions.has(grip))return;const item=grip.closest(".bevel-columns__item"),manager=grip.closest(".bevel-columns");if(item.hasAttribute("data-bevel-column-required"))return;const pointerId=e.pointerId,previous=[...manager.children].indexOf(item),session={target:null,before:false,cancel:()=>stop(false)},clear=()=>{session.target?.removeAttribute("data-bevel-drop-target");session.target=null;},move=current=>{if(current.pointerId!==pointerId)return;const target=[...manager.querySelectorAll(":scope > .bevel-columns__item")].find(node=>{const rect=node.getBoundingClientRect();return current.clientY>=rect.top&&current.clientY<=rect.bottom;});clear();if(target&&target!==item&&!target.hasAttribute("data-bevel-column-required")){session.target=target;const rect=target.getBoundingClientRect();session.before=current.clientY<rect.top+rect.height/2;target.dataset.bevelDropTarget=session.before?"before":"after";}},stop=commit=>{if(!sessions.has(grip))return;document.removeEventListener("pointermove",move);document.removeEventListener("pointerup",up);document.removeEventListener("pointercancel",cancel);document.removeEventListener("keydown",escape,true);sessions.delete(grip);const target=session.target,before=session.before;clear();if(commit&&grip.isConnected&&target?.isConnected){const children=[...manager.children],targetIndex=children.indexOf(target)+(before?0:1),next=targetIndex>(children.indexOf(item))?targetIndex-1:targetIndex;if(next!==previous)transition(manager,"column-order",item.dataset.bevelColumn,previous,next,()=>orderColumn(manager,item.dataset.bevelColumn,target.dataset.bevelColumn,before));}},up=current=>{if(current.pointerId===pointerId)stop(true);},cancel=current=>{if(current.pointerId===pointerId)stop(false);},escape=current=>{if(current.key==="Escape"){current.preventDefault();stop(false);}};e.preventDefault();sessions.set(grip,session);document.addEventListener("pointermove",move);document.addEventListener("pointerup",up);document.addEventListener("pointercancel",cancel);document.addEventListener("keydown",escape,true);});
1991
+ on("keydown", ".bevel-split__divider", (event, divider) => {
1992
+ if (disabled(divider) || event.isComposing) return;
1993
+ const root = divider.closest(".bevel-split"), vertical = root.dataset.bevelOrientation === "vertical", forward = vertical ? "ArrowDown" : "ArrowRight", backward = vertical ? "ArrowUp" : "ArrowLeft";
1994
+ if (![forward, backward, "Home", "End"].includes(event.key)) return;
1995
+ event.preventDefault(); const [min, max] = splitBounds(root), step = event.shiftKey ? 10 : 2;
1996
+ const value = event.key === "Home" ? min : event.key === "End" ? max : splitClamped(root, splitValue(root) + (event.key === forward ? step : -step));
1997
+ commitSplit(root, Math.round(value * 100) / 100);
1998
+ });
1999
+ on("pointerdown", ".bevel-split__divider", (event, divider) => {
2000
+ if (event.button !== 0 || disabled(divider)) return;
2001
+ const root = divider.closest(".bevel-split"); refreshSplit(root);
2002
+ const session = splitSessions.get(root); if (session.drag || disabled(divider)) return;
2003
+ event.preventDefault(); divider.focus();
2004
+ const previous = splitValue(root), vertical = root.dataset.bevelOrientation === "vertical", extent = vertical ? root.clientHeight : root.clientWidth;
2005
+ const start = vertical ? event.clientY : event.clientX, pointerId = event.pointerId;
2006
+ const drag = { previous, orientation: root.dataset.bevelOrientation, current: splitClamped(root, previous), stop(commit) {
2007
+ if (session.drag !== drag) return;
2008
+ session.drag = null; root.removeAttribute("data-bevel-dragging");
2009
+ divider.removeEventListener("pointermove", move); divider.removeEventListener("pointerup", up); divider.removeEventListener("pointercancel", cancel); divider.removeEventListener("lostpointercapture", cancel); document.removeEventListener("keydown", escape, true);
2010
+ if (divider.hasPointerCapture(pointerId)) divider.releasePointerCapture(pointerId);
2011
+ if (commit && root.isConnected) commitSplit(root, Math.round(drag.current * 100) / 100);
2012
+ refreshSplit(root);
2013
+ } };
2014
+ const move = current => { if (current.pointerId !== pointerId) return; if (!root.isConnected || splitMedia.matches || disabled(divider)) return drag.stop(false); const offset = (vertical ? current.clientY : current.clientX) - start; drag.current = splitClamped(root, splitClamped(root, previous) + offset / extent * 100); root.style.setProperty("--bevel-split-size", `${drag.current}%`); divider.setAttribute("aria-valuenow", String(Math.round(drag.current * 100) / 100)); };
2015
+ const up = current => { if (current.pointerId === pointerId) drag.stop(true); };
2016
+ const cancel = current => { if (current.pointerId === pointerId) drag.stop(false); };
2017
+ const escape = current => { if (current.key === "Escape") { current.preventDefault(); current.stopPropagation(); drag.stop(false); } };
2018
+ session.drag = drag; root.dataset.bevelDragging = "";
2019
+ divider.addEventListener("pointermove", move); divider.addEventListener("pointerup", up); divider.addEventListener("pointercancel", cancel); divider.addEventListener("lostpointercapture", cancel); document.addEventListener("keydown", escape, true);
2020
+ divider.setPointerCapture(pointerId);
2021
+ });
2022
+
2023
+ const toastTimers = new Map(), toastReturns = new WeakMap();
2024
+ const toastStack = () => { let stack = document.querySelector(".bevel-toasts"); if (!stack) { stack = document.createElement("div"); stack.className = "bevel-toasts"; stack.setAttribute("aria-live", "polite"); document.body.append(stack); } return stack; };
2025
+ const restoreToastFocus = ({ previous, fallback } = {}) => { for (const target of [previous, fallback]) { if (!target?.isConnected || typeof target.focus !== "function") continue; target.focus(); if (document.activeElement === target) return; } };
2026
+ const dismissToast = toast => {
2027
+ const timer = toastTimers.get(toast); if (timer?.id) clearTimeout(timer.id); toastTimers.delete(toast);
2028
+ const focused = toast.contains(document.activeElement), next = toast.nextElementSibling || toast.previousElementSibling, entry = toastReturns.get(toast);
2029
+ toast.remove(); if (!focused) return; const target = next ? focusable(next) : null; target ? target.focus() : restoreToastFocus(entry);
2030
+ };
2031
+ const scheduleToast = (toast, delay) => {
2032
+ const timer = toastTimers.get(toast); if (!timer || delay <= 0) return;
2033
+ timer.remaining = delay; timer.started = performance.now(); timer.id = setTimeout(() => dismissToast(toast), delay);
2034
+ };
2035
+ const pauseToast = toast => { const timer = toastTimers.get(toast); if (!timer?.id) return; clearTimeout(timer.id); timer.id = null; timer.remaining = Math.max(0, timer.remaining - (performance.now() - timer.started)); };
2036
+ const resumeToast = toast => { const timer = toastTimers.get(toast); if (timer && !timer.id && !toast.matches(":hover") && !toast.contains(document.activeElement) && !document.hidden) scheduleToast(toast, timer.remaining); };
2037
+ const toastIcon = tone => tone === "success" ? '<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="square" stroke-linejoin="miter"><path d="m5 12 4 4L19 6"/></svg>' : tone === "error" ? '<svg aria-hidden="true" viewBox="0 0 256 256" fill="currentColor"><path d="M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z"/></svg>' : '<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="square" stroke-linejoin="miter"><path d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0M12 7.75v.85M10.3 11.15H12v5.1M10.3 16.25h3.4"/></svg>';
2038
+ const notify = ({ title = "Notice", message, tone = "info", key = crypto.randomUUID?.() || String(Date.now()), action: item, duration, focus = false, returnFocus } = {}) => {
2039
+ const stack = toastStack(), template = stack.querySelector("template[data-bevel-toast-template]"), toast = template?.content.firstElementChild?.cloneNode(true) || document.createElement("article"); if (!toast.classList.contains("bevel-toast")) toast.className = "bevel-toast"; toast.dataset.bevelTone = tone; toast.dataset.bevelKey = String(key); toast.dataset.bevelTransient = "toast";
2040
+ const holder = [...stack.children].find(node => toastReturns.has(node) && node.contains(document.activeElement)), inherited = holder && toastReturns.get(holder); toastReturns.set(toast, { previous: inherited ? inherited.previous : document.activeElement, fallback: returnFocus ?? inherited?.fallback });
2041
+ if (tone === "error") toast.setAttribute("role", "alert");
2042
+ if (focus) toast.setAttribute("aria-live", "off");
2043
+ if (!template) toast.innerHTML = '<span class="bevel-toast__icon"></span><div class="bevel-toast__body"><h2 class="bevel-toast__title"></h2><p class="bevel-toast__message"></p></div><button type="button" class="bevel-toast__close"></button>';
2044
+ toast.querySelector(".bevel-toast__icon").innerHTML = toastIcon(tone); const close = toast.querySelector(".bevel-toast__close"); close.innerHTML = '<svg aria-hidden="true" viewBox="0 0 256 256" fill="currentColor"><path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"/></svg>'; close.setAttribute("aria-label", `Dismiss ${title}`);
2045
+ toast.querySelector(".bevel-toast__title").textContent = title; toast.querySelector(".bevel-toast__message").textContent = message || "";
2046
+ let button = toast.querySelector(".bevel-toast__action"); if (item) { if (!button) { button = document.createElement("button"); button.type = "button"; button.className = "bevel-button bevel-button--small bevel-toast__action"; toast.querySelector(".bevel-toast__body").append(button); } button.replaceChildren(); const actionIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg"); actionIcon.setAttribute("aria-hidden", "true"); actionIcon.setAttribute("viewBox", "0 0 256 256"); actionIcon.setAttribute("fill", "currentColor"); actionIcon.innerHTML = '<path d="M224,128a96,96,0,0,1-94.71,96H128A95.38,95.38,0,0,1,62.1,197.8a8,8,0,0,1,11-11.63A80,80,0,1,0,71.43,71.39a3.07,3.07,0,0,1-.26.25L44.59,96H72a8,8,0,0,1,0,16H24a8,8,0,0,1-8-8V56a8,8,0,0,1,16,0V85.8L60.25,60A96,96,0,0,1,224,128Z"/>'; if ((item.icon || item.id) === "retry") actionIcon.style.transform = "scaleX(-1)"; const label = document.createElement("span"); label.className = "bevel-button__label"; label.textContent = item.label; button.append(actionIcon, label); button.addEventListener("click", () => { action(stack, item.id, key); dismissToast(toast); }, { once: true }); } else button?.remove();
2047
+ toast.querySelector(".bevel-toast__close").addEventListener("click", () => dismissToast(toast), { once: true });
2048
+ toast.addEventListener("mouseenter", () => pauseToast(toast)); toast.addEventListener("mouseleave", () => resumeToast(toast)); toast.addEventListener("focusin", () => pauseToast(toast)); toast.addEventListener("focusout", () => queueMicrotask(() => resumeToast(toast)));
2049
+ stack.append(toast); const timeout = duration ?? (tone === "error" || item || focus ? 0 : 4000); if (timeout > 0) { toastTimers.set(toast, { remaining: timeout, id: null, started: 0 }); scheduleToast(toast, timeout); } if (focus) { toast.tabIndex = -1; toast.focus({ preventScroll: true }); } return toast;
2050
+ };
2051
+ document.addEventListener("visibilitychange", () => { for (const toast of toastTimers.keys()) document.hidden ? pauseToast(toast) : resumeToast(toast); });
2052
+ let savedTimer = null;
2053
+ const saved = ({ duration = 2000 } = {}) => { let chip = document.querySelector(".bevel-saved"); if (!chip) { chip = document.createElement("div"); chip.className = "bevel-saved"; chip.dataset.bevelTransient = "saved"; chip.setAttribute("role", "status"); chip.setAttribute("aria-live", "polite"); chip.textContent = "Saved"; document.body.append(chip); } if (savedTimer) clearTimeout(savedTimer); chip.hidden = false; savedTimer = duration > 0 ? setTimeout(() => { chip.hidden = true; savedTimer = null; }, duration) : null; return chip; };
2054
+ const upload = {
2055
+ update(root, key, state) {
2056
+ const row = root?.querySelector(`[data-bevel-file-key="${CSS.escape(String(key))}"]`);
2057
+ if (!row) return false;
2058
+ const next = { ...state }, allowed = new Set(["selected", "uploading", "uploaded", "error"]);
2059
+ if (next.status && !allowed.has(next.status)) return false;
2060
+ if ("progress" in next) next.progress = Math.min(100, Math.max(0, Number(next.progress) || 0));
2061
+ const candidateStatus = next.status || row.dataset.bevelStatus;
2062
+ const candidateUrl = "url" in next ? next.url : row.dataset.bevelUrl;
2063
+ const candidateError = "error" in next ? next.error : row.dataset.bevelError;
2064
+ if (candidateStatus === "uploaded") {
2065
+ let protocol; try { protocol = new URL(candidateUrl, document.baseURI).protocol; } catch { return false; }
2066
+ if (!candidateUrl || !["http:", "https:", "data:"].includes(protocol)) return false;
2067
+ }
2068
+ for (const name of ["status", "progress", "url", "name", "error"]) if (name in next) { const value = next[name]; if (value == null || value === "") row.removeAttribute(`data-bevel-${name}`); else row.setAttribute(`data-bevel-${name}`, String(value)); }
2069
+ const progress = row.querySelector("progress");
2070
+ if (progress && "progress" in next) progress.value = next.progress;
2071
+ const stateMark = row.querySelector(".bevel-upload__state");
2072
+ if (stateMark) stateMark.textContent = candidateStatus === "uploading" ? `${"progress" in next ? next.progress : row.dataset.bevelProgress || 0}%` : candidateStatus === "uploaded" ? "✓" : candidateStatus === "error" ? "Failed" : "";
2073
+ const status = row.querySelector(".bevel-upload__status");
2074
+ if (status && ("status" in next || "error" in next)) status.textContent = candidateError || (candidateStatus === "uploaded" ? "Uploaded" : candidateStatus[0].toUpperCase() + candidateStatus.slice(1));
2075
+ const name = row.querySelector(".bevel-upload__name");
2076
+ if (name && next.name) name.textContent = next.name;
2077
+ const retry = row.querySelector("[data-bevel-upload-retry]");
2078
+ if (retry) retry.hidden = candidateStatus !== "error" || !uploadFiles.get(root)?.has(String(key));
2079
+ let link = row.querySelector(".bevel-upload__link");
2080
+ if (candidateStatus === "uploaded" && ("status" in next || "url" in next)) {
2081
+ if (!link) { link = document.createElement("a"); link.className = "bevel-upload__link"; link.textContent = "Open file"; row.querySelector(".bevel-upload__actions")?.prepend(link); }
2082
+ link.href = candidateUrl; link.rel = "noopener noreferrer"; if (root.hasAttribute("data-bevel-download")) link.download = next.name || row.dataset.bevelName || "";
2083
+ const entry = uploadFiles.get(root)?.get(String(key)); if (entry?.preview) URL.revokeObjectURL(entry.preview); uploadFiles.get(root)?.delete(String(key));
2084
+ const preview = row.querySelector(".bevel-upload__preview img"); if (preview?.src.startsWith("blob:")) { preview.removeAttribute("src"); preview.hidden = true; }
2085
+ }
2086
+ return true;
2087
+ },
2088
+ remove(root, key) {
2089
+ const id = String(key), row = root?.querySelector(`[data-bevel-file-key="${CSS.escape(id)}"]`);
2090
+ if (!row) return false;
2091
+ const files = uploadFiles.get(root), entry = files?.get(id);
2092
+ if (entry?.preview) URL.revokeObjectURL(entry.preview);
2093
+ files?.delete(id); row.remove(); action(root, "remove", id); return true;
2094
+ }
2095
+ };
2096
+ const edit = {
2097
+ start: startEdit,
2098
+ commit(root) { return endEdit(root, true); },
2099
+ cancel(root) { return endEdit(root, false); }
2100
+ };
2101
+ const remote = { resolve(root, requestId, { status, message = "" }) { if (!root?.isConnected || !["accepted", "rejected"].includes(status)) return false; const scopes = requestScopes.get(root); if (!scopes || ![...scopes.values()].includes(requestId)) return false; for (const [scope, id] of scopes) if (id === requestId) scopes.delete(scope); if (!scopes.size) root.removeAttribute("data-bevel-pending"); root.dataset.bevelResolution = status; if (message) root.dataset.bevelMessage = message; return true; } };
2102
+ window.Bevel = { open: openOverlay, close: closeOverlay, refresh, destroy, prepareForSave, notify, saved, upload, edit, filter:{ read:filterRead, matches:filterMatches }, remote, _transition: transition, _request: remoteRequest };
2103
+
2104
+ /* ---------- first paint: fill any slider whose markup forgot --bevel-value ---------- */
2105
+ const boot = () => {
2106
+ document.querySelectorAll("[data-bevel-transient]").forEach((el) => el.remove());
2107
+ clearGridDrafts(document);
2108
+ clearKanbanDrafts(document);
2109
+ clearPickerDrafts(document);
2110
+ clearInlineDrafts(document);
2111
+ for (const grid of document.querySelectorAll(".bevel-grid")) if (grid.dataset.bevelDataMode !== "remote") layoutGrid(grid);
2112
+ for (const input of document.querySelectorAll(".bevel-slider__input")) {
2113
+ const wrap = input.closest(".bevel-slider");
2114
+ if (wrap && !wrap.style.getPropertyValue("--bevel-value")) syncSlider(input);
2115
+ }
2116
+ for (const name of document.querySelectorAll(".bevel-file__name[data-filled][data-empty]")) {
2117
+ const wrap = name.closest(".bevel-file");
2118
+ const input = wrap?.querySelector(".bevel-file__input");
2119
+ if (input && !input.files.length) clearFile(wrap, name);
2120
+ }
2121
+ refresh(document, { source: "boot" });
2122
+ expirePendingUploads(document);
2123
+ window.addEventListener("beforeunload", () => { for (const root of document.querySelectorAll(".bevel-dropzone")) for (const entry of uploadFiles.get(root)?.values() || []) if (entry.preview) URL.revokeObjectURL(entry.preview); }, { once: true });
2124
+ };
2125
+ document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", boot) : boot();
2126
+ })();