@swiftwc/ui 0.0.0-dev.7 → 0.0.0-dev.71

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 (343) hide show
  1. package/README.md +15 -0
  2. package/generated/buses/alert-dialog.d.ts +2 -0
  3. package/generated/buses/alert-dialog.js +1 -0
  4. package/generated/buses/confirmation-dialog.d.ts +2 -0
  5. package/generated/buses/confirmation-dialog.js +1 -0
  6. package/generated/buses/index.d.ts +6 -0
  7. package/generated/buses/index.js +6 -0
  8. package/generated/buses/lifecycle-observer.d.ts +2 -0
  9. package/generated/buses/lifecycle-observer.js +1 -0
  10. package/generated/client/index.d.ts +25 -4
  11. package/generated/client/index.js +234 -181
  12. package/generated/components/alert-dialog.d.ts +8 -0
  13. package/generated/components/alert-dialog.js +99 -0
  14. package/generated/components/body-view.js +5 -2
  15. package/generated/components/bordered-button.d.ts +17 -0
  16. package/generated/components/bordered-button.js +56 -0
  17. package/generated/components/bordered-prominent-button.d.ts +17 -0
  18. package/generated/components/bordered-prominent-button.js +56 -0
  19. package/generated/components/borderless-button.d.ts +12 -4
  20. package/generated/components/borderless-button.js +47 -16
  21. package/generated/components/color-scheme.d.ts +7 -0
  22. package/generated/components/color-scheme.js +54 -0
  23. package/generated/components/confirmation-dialog.d.ts +9 -0
  24. package/generated/components/confirmation-dialog.js +113 -0
  25. package/generated/components/content-unavailable-view.d.ts +24 -0
  26. package/generated/components/content-unavailable-view.js +81 -0
  27. package/generated/components/date-picker.d.ts +36 -0
  28. package/generated/components/date-picker.js +461 -0
  29. package/generated/components/disclosure-group.d.ts +9 -7
  30. package/generated/components/disclosure-group.js +51 -65
  31. package/generated/components/fine-tooltip.d.ts +7 -0
  32. package/generated/components/fine-tooltip.js +107 -0
  33. package/generated/components/form-view.d.ts +9 -0
  34. package/generated/components/form-view.js +63 -0
  35. package/generated/components/glass-button.d.ts +16 -0
  36. package/generated/components/glass-button.js +53 -0
  37. package/generated/components/glass-prominent-button.d.ts +16 -0
  38. package/generated/components/glass-prominent-button.js +53 -0
  39. package/generated/components/h-stack.d.ts +16 -0
  40. package/generated/components/h-stack.js +25 -0
  41. package/generated/components/image-view.d.ts +9 -0
  42. package/generated/components/image-view.js +46 -0
  43. package/generated/components/index.d.ts +42 -10
  44. package/generated/components/index.js +42 -15
  45. package/generated/components/label-view.d.ts +14 -0
  46. package/generated/components/label-view.js +56 -0
  47. package/generated/components/labeled-content.d.ts +9 -0
  48. package/generated/components/labeled-content.js +115 -0
  49. package/generated/components/list-view.d.ts +8 -0
  50. package/generated/components/list-view.js +32 -0
  51. package/generated/components/menu-view.d.ts +12 -0
  52. package/generated/components/menu-view.js +142 -0
  53. package/generated/components/navigation-large-title.d.ts +6 -0
  54. package/generated/components/navigation-large-title.js +181 -0
  55. package/generated/components/navigation-split-view.d.ts +7 -2
  56. package/generated/components/navigation-split-view.js +19 -3
  57. package/generated/components/navigation-stack.d.ts +6 -2
  58. package/generated/components/navigation-stack.js +23 -59
  59. package/generated/components/navigation-title.d.ts +8 -0
  60. package/generated/components/navigation-title.js +50 -0
  61. package/generated/components/picker-view.d.ts +55 -0
  62. package/generated/components/picker-view.js +1132 -0
  63. package/generated/components/plain-button.d.ts +7 -0
  64. package/generated/components/plain-button.js +22 -0
  65. package/generated/components/progress-view.d.ts +13 -0
  66. package/generated/components/progress-view.js +120 -0
  67. package/generated/components/screen-view.d.ts +1 -1
  68. package/generated/components/screen-view.js +4 -2
  69. package/generated/components/scroll-view-proxy.d.ts +8 -0
  70. package/generated/components/scroll-view-proxy.js +17 -0
  71. package/generated/components/scroll-view.d.ts +4 -2
  72. package/generated/components/scroll-view.js +179 -64
  73. package/generated/components/search-view.d.ts +7 -0
  74. package/generated/components/search-view.js +30 -0
  75. package/generated/components/section-view.d.ts +9 -0
  76. package/generated/components/section-view.js +85 -0
  77. package/generated/components/sheet-view.d.ts +3 -4
  78. package/generated/components/sheet-view.js +53 -17
  79. package/generated/components/sidebar-toggle.d.ts +1 -1
  80. package/generated/components/sidebar-toggle.js +76 -72
  81. package/generated/components/sidebar-view.d.ts +2 -4
  82. package/generated/components/sidebar-view.js +25 -17
  83. package/generated/components/sticky-container.d.ts +6 -0
  84. package/generated/components/sticky-container.js +26 -0
  85. package/generated/components/tab-bar.d.ts +5 -4
  86. package/generated/components/tab-bar.js +34 -17
  87. package/generated/components/tab-item.d.ts +4 -3
  88. package/generated/components/tab-item.js +125 -29
  89. package/generated/components/tab-view.d.ts +17 -2
  90. package/generated/components/tab-view.js +252 -17
  91. package/generated/components/table-view.d.ts +12 -0
  92. package/generated/components/table-view.js +153 -0
  93. package/generated/components/text-field.d.ts +29 -0
  94. package/generated/components/text-field.js +496 -0
  95. package/generated/components/toggle-view.d.ts +28 -0
  96. package/generated/components/toggle-view.js +222 -0
  97. package/generated/components/tool-bar-item-group.d.ts +5 -0
  98. package/generated/components/tool-bar-item-group.js +28 -0
  99. package/generated/components/tool-bar-item.d.ts +8 -0
  100. package/generated/components/tool-bar-item.js +66 -0
  101. package/generated/components/tool-bar.d.ts +19 -0
  102. package/generated/components/tool-bar.js +93 -0
  103. package/generated/components/v-keyboard.d.ts +4 -0
  104. package/generated/components/v-keyboard.js +72 -70
  105. package/generated/components/v-stack.d.ts +19 -0
  106. package/generated/components/v-stack.js +28 -0
  107. package/generated/css/index.css +8966 -0
  108. package/generated/events/alert-dialog.d.ts +8 -0
  109. package/generated/events/confirmation-dialog.d.ts +9 -0
  110. package/generated/events/confirmation-dialog.js +1 -0
  111. package/generated/events/date-picker.d.ts +8 -0
  112. package/generated/events/date-picker.js +1 -0
  113. package/generated/events/index.d.ts +9 -0
  114. package/generated/events/index.js +9 -0
  115. package/generated/events/lifecycle-observer.d.ts +10 -0
  116. package/generated/events/lifecycle-observer.js +1 -0
  117. package/generated/events/picker-view.d.ts +15 -0
  118. package/generated/events/picker-view.js +1 -0
  119. package/generated/events/scroll-view.d.ts +10 -0
  120. package/generated/events/scroll-view.js +1 -0
  121. package/generated/events/tab-view.d.ts +28 -0
  122. package/generated/events/tab-view.js +1 -0
  123. package/generated/events/text-field.d.ts +8 -0
  124. package/generated/events/text-field.js +1 -0
  125. package/generated/events/toggle-view.d.ts +9 -0
  126. package/generated/events/toggle-view.js +1 -0
  127. package/generated/i18n/index.d.ts +34 -0
  128. package/generated/i18n/index.js +167 -0
  129. package/generated/i18n/locales/el.d.ts +16 -0
  130. package/generated/i18n/locales/el.js +16 -0
  131. package/generated/i18n/locales/en.d.ts +16 -0
  132. package/generated/i18n/locales/en.js +16 -0
  133. package/generated/i18n/locales/index.d.ts +4 -0
  134. package/generated/i18n/locales/index.js +4 -0
  135. package/generated/internal/class/cleanup-registry.d.ts +5 -0
  136. package/generated/internal/class/cleanup-registry.js +38 -0
  137. package/generated/internal/class/css-style-observer.d.ts +8 -0
  138. package/generated/internal/class/css-style-observer.js +26 -0
  139. package/generated/internal/class/form-associated-base.d.ts +16 -0
  140. package/generated/internal/class/form-associated-base.js +78 -0
  141. package/generated/internal/class/mutation-observer-set.d.ts +9 -0
  142. package/generated/internal/class/mutation-observer-set.js +42 -0
  143. package/generated/internal/class/mutation-observer-singleton.d.ts +10 -0
  144. package/generated/internal/class/mutation-observer-singleton.js +64 -0
  145. package/generated/internal/class/navigation-path.d.ts +15 -0
  146. package/generated/internal/class/navigation-path.js +84 -0
  147. package/generated/internal/class/navigation-view.d.ts +8 -0
  148. package/generated/internal/class/navigation-view.js +72 -0
  149. package/generated/internal/class/resize-observer-singleton.d.ts +7 -0
  150. package/generated/internal/class/resize-observer-singleton.js +22 -0
  151. package/generated/internal/decorators/adaptive-slot.d.ts +1 -0
  152. package/generated/internal/decorators/adaptive-slot.js +50 -0
  153. package/generated/internal/decorators/custom-element.d.ts +5 -0
  154. package/generated/internal/decorators/custom-element.js +5 -0
  155. package/generated/internal/decorators/index.d.ts +6 -0
  156. package/generated/internal/decorators/index.js +6 -0
  157. package/generated/internal/decorators/microtask-on-connected.d.ts +39 -0
  158. package/generated/internal/decorators/microtask-on-connected.js +67 -0
  159. package/generated/internal/privateNamespace.d.ts +14 -0
  160. package/generated/internal/privateNamespace.js +168 -0
  161. package/generated/internal/utils/add.d.ts +1 -0
  162. package/generated/internal/utils/add.js +7 -0
  163. package/generated/internal/utils/cash/ancestors.d.ts +1 -0
  164. package/generated/internal/utils/cash/ancestors.js +27 -0
  165. package/generated/internal/utils/cash/index.d.ts +23 -0
  166. package/generated/internal/utils/cash/index.js +25 -0
  167. package/generated/internal/utils/cash/next-all.d.ts +1 -0
  168. package/generated/internal/utils/cash/next-all.js +10 -0
  169. package/generated/internal/utils/cash/next.d.ts +1 -0
  170. package/generated/internal/utils/cash/next.js +9 -0
  171. package/generated/internal/utils/cash/prev-all.d.ts +1 -0
  172. package/generated/internal/utils/cash/prev-all.js +10 -0
  173. package/generated/internal/utils/cash/prev.d.ts +1 -0
  174. package/generated/internal/utils/cash/prev.js +9 -0
  175. package/generated/internal/utils/cash/prop.d.ts +18 -0
  176. package/generated/internal/utils/cash/prop.js +30 -0
  177. package/generated/internal/utils/cash/siblings.d.ts +1 -0
  178. package/generated/internal/utils/cash/siblings.js +10 -0
  179. package/generated/internal/utils/clamp.d.ts +1 -0
  180. package/generated/internal/utils/clamp.js +4 -0
  181. package/generated/internal/utils/compare-big-decimals.d.ts +1 -0
  182. package/generated/internal/utils/compare-big-decimals.js +90 -0
  183. package/generated/internal/utils/debounce.d.ts +1 -0
  184. package/generated/internal/utils/debounce.js +13 -0
  185. package/generated/internal/utils/dev-flags.d.ts +3 -0
  186. package/generated/internal/utils/dev-flags.js +5 -0
  187. package/generated/internal/utils/ensure-placeholder.d.ts +1 -0
  188. package/generated/internal/utils/ensure-placeholder.js +55 -0
  189. package/generated/internal/utils/frame.d.ts +1 -0
  190. package/generated/internal/utils/frame.js +6 -0
  191. package/generated/internal/utils/index.d.ts +21 -1
  192. package/generated/internal/utils/index.js +24 -1
  193. package/generated/internal/utils/is-inside.d.ts +1 -0
  194. package/generated/internal/utils/is-inside.js +4 -0
  195. package/generated/internal/utils/kebab-case.d.ts +1 -1
  196. package/generated/internal/utils/list-active.d.ts +7 -0
  197. package/generated/internal/utils/list-active.js +83 -0
  198. package/generated/internal/utils/microtask.d.ts +1 -0
  199. package/generated/internal/utils/microtask.js +3 -0
  200. package/generated/internal/utils/onoff.d.ts +45 -0
  201. package/generated/internal/utils/onoff.js +31 -0
  202. package/generated/internal/utils/render-label-icon.d.ts +2 -0
  203. package/generated/internal/utils/render-label-icon.js +19 -0
  204. package/generated/internal/utils/render-label-title.d.ts +2 -0
  205. package/generated/internal/utils/render-label-title.js +16 -0
  206. package/generated/internal/utils/render-label.d.ts +1 -0
  207. package/generated/internal/utils/render-label.js +19 -0
  208. package/generated/internal/utils/set.d.ts +1 -0
  209. package/generated/internal/utils/set.js +7 -0
  210. package/generated/internal/utils/sleep.d.ts +1 -0
  211. package/generated/internal/utils/sleep.js +3 -0
  212. package/generated/internal/utils/slow-hide-show.d.ts +1 -0
  213. package/generated/internal/utils/slow-hide-show.js +16 -0
  214. package/generated/internal/utils/timeout.d.ts +5 -0
  215. package/generated/internal/utils/timeout.js +16 -0
  216. package/generated/internal/utils/touch-glass.d.ts +7 -0
  217. package/generated/internal/utils/touch-glass.js +19 -0
  218. package/generated/namespace-browser/base.d.ts +45 -0
  219. package/generated/namespace-browser/base.js +115 -0
  220. package/generated/namespace-browser/index.d.ts +7 -0
  221. package/generated/namespace-browser/index.js +1 -0
  222. package/generated/snapshot/index.d.ts +4 -11
  223. package/generated/snapshot/index.js +55 -145
  224. package/generated/tpl/html.d.ts +5 -0
  225. package/generated/tpl/html.js +3 -0
  226. package/generated/tpl/index.d.ts +4 -0
  227. package/generated/tpl/index.js +4 -0
  228. package/generated/tpl/render.d.ts +2 -0
  229. package/generated/tpl/render.js +131 -0
  230. package/package.json +34 -9
  231. package/scss/_components.scss +62 -13
  232. package/scss/_final.scss +1 -0
  233. package/scss/_functions.scss +10 -0
  234. package/scss/_mixins.scss +4 -169
  235. package/scss/_placeholders.scss +3 -0
  236. package/scss/_transitions.scss +22 -1
  237. package/scss/_utils.scss +9 -0
  238. package/scss/_vars.scss +144 -23
  239. package/scss/base/_layout.scss +40 -16
  240. package/scss/base/_reboot.scss +151 -15
  241. package/scss/base/_root.scss +358 -25
  242. package/scss/colors/_index.scss +843 -70
  243. package/scss/components/_alert-dialog.scss +111 -0
  244. package/scss/components/_body-view.scss +12 -3
  245. package/scss/components/_bordered-button.scss +57 -0
  246. package/scss/components/_bordered-prominent-button.scss +64 -0
  247. package/scss/components/_borderless-button.scss +36 -6
  248. package/scss/components/_confirmation-dialog.scss +102 -0
  249. package/scss/components/_content-unavailable-view.scss +54 -0
  250. package/scss/components/_date-picker.scss +30 -0
  251. package/scss/components/_detail-placeholder.scss +153 -0
  252. package/scss/components/_disclosure-group.scss +147 -48
  253. package/scss/components/_fine-tooltip.scss +84 -0
  254. package/scss/components/_form-view.scss +233 -0
  255. package/scss/components/_full-screen.scss +7 -5
  256. package/scss/components/_glass-button.scss +59 -0
  257. package/scss/components/_glass-prominent-button.scss +70 -0
  258. package/scss/components/_h-divider.scss +13 -0
  259. package/scss/components/_h-flex-stack.scss +23 -0
  260. package/scss/components/_h-stack.scss +45 -0
  261. package/scss/components/_image-view.scss +9 -0
  262. package/scss/components/_index.scss +243 -7
  263. package/scss/components/_label-view copy.scss.txt +420 -0
  264. package/scss/components/_label-view.scss +372 -0
  265. package/scss/components/_label-view.scss.txt +536 -0
  266. package/scss/components/_labeled-content.scss +67 -0
  267. package/scss/components/_list-view.scss +269 -0
  268. package/scss/components/_menu-view.scss +223 -0
  269. package/scss/components/_navigation-large-title.scss +27 -0
  270. package/scss/components/_navigation-split-view.scss +329 -269
  271. package/scss/components/_navigation-stack.scss +6 -6
  272. package/scss/components/_navigation-title.scss +10 -0
  273. package/scss/components/_picker-view.scss +322 -0
  274. package/scss/components/_plain-button.scss +18 -0
  275. package/scss/components/_progress-view.scss +98 -0
  276. package/scss/components/_scroll-view-proxy.scss +81 -0
  277. package/scss/components/_scroll-view-toolbars.scss +126 -0
  278. package/scss/components/_scroll-view.scss +114 -66
  279. package/scss/components/_search-view.scss +22 -0
  280. package/scss/components/_section-view.scss +120 -0
  281. package/scss/components/_sheet-view.scss +78 -35
  282. package/scss/components/_sidebar-toggle.scss +87 -27
  283. package/scss/components/_sidebar-view.scss +30 -40
  284. package/scss/components/_sticky-container.scss +42 -0
  285. package/scss/components/_tab-bar.scss +215 -84
  286. package/scss/components/_tab-item.scss +25 -0
  287. package/scss/components/_tab-view.scss +270 -126
  288. package/scss/components/_table-column.scss +54 -0
  289. package/scss/components/_table-row.scss +240 -0
  290. package/scss/components/_table-view.scss +231 -0
  291. package/scss/components/_text-field.scss +57 -0
  292. package/scss/components/_toggle-view.scss +79 -0
  293. package/scss/components/_tool-bar-item-group.scss +30 -19
  294. package/scss/components/_tool-bar-item.scss +81 -71
  295. package/scss/components/_tool-bar.scss +123 -60
  296. package/scss/components/_v-divider.scss +13 -0
  297. package/scss/components/_v-flex-stack.scss +29 -0
  298. package/scss/components/_v-keyboard.scss +6 -4
  299. package/scss/components/_v-stack.scss +15 -9
  300. package/scss/final/_touch-glass.scss +91 -0
  301. package/scss/index.scss +6 -2
  302. package/scss/mixins/_button.scss +44 -0
  303. package/scss/mixins/_color.scss +16 -0
  304. package/scss/mixins/_dialog.scss +89 -0
  305. package/scss/mixins/_index.scss +461 -0
  306. package/scss/placeholders/_buttons.scss +117 -0
  307. package/scss/placeholders/_lists.scss +596 -0
  308. package/scss/transitions/_alert-dialog.scss +18 -0
  309. package/scss/transitions/_bwd.navbar.scss +3 -13
  310. package/scss/transitions/_bwd.scss +4 -20
  311. package/scss/transitions/_confirmation-dialog.scss +27 -0
  312. package/scss/transitions/_disclosure-group.scss +35 -0
  313. package/scss/transitions/_fine-tooltip.scss +21 -0
  314. package/scss/transitions/_fwd.navbar.scss +4 -9
  315. package/scss/transitions/_fwd.scss +4 -34
  316. package/scss/transitions/_glass-button.scss +12 -0
  317. package/scss/transitions/_glass-prominent-button.scss +12 -0
  318. package/scss/transitions/_index.scss +35 -143
  319. package/scss/transitions/_menu-view.scss +28 -0
  320. package/scss/transitions/_navigation-split-view.scss +23 -0
  321. package/scss/transitions/_progress-view.scss +23 -0
  322. package/scss/transitions/_sheet-view.scss +30 -0
  323. package/scss/transitions/_sidebar-view.scss +36 -0
  324. package/scss/transitions/_tab-bar.scss +20 -0
  325. package/scss/utils/_frame.scss +91 -0
  326. package/scss/utils/_index.scss +83 -0
  327. package/scss/utils/_scroll-view.scss +14 -0
  328. package/scss/utils/_spacing.scss +25 -0
  329. package/scss/utils/_stacks.scss +225 -0
  330. package/web-components.html-data/en.json +950 -0
  331. package/generated/components/bottom-bar.d.ts +0 -10
  332. package/generated/components/bottom-bar.js +0 -73
  333. package/generated/components/navigation-bar.d.ts +0 -10
  334. package/generated/components/navigation-bar.js +0 -80
  335. package/generated/internal/class.d.ts +0 -18
  336. package/generated/internal/class.js +0 -18
  337. package/generated/namespace/index.d.ts +0 -6
  338. package/generated/styles.css +0 -1305
  339. package/scss/_dev.scss +0 -68
  340. package/scss/components/_tab-bar-stack.scss +0 -101
  341. package/scss/components/_toolbars.scss +0 -100
  342. package/scss/transitions/_dialog.scss +0 -32
  343. /package/generated/{namespace/index.js → events/alert-dialog.js} +0 -0
@@ -0,0 +1,1132 @@
1
+ var _a;
2
+ import { I18n } from '../i18n';
3
+ import { CleanupRegistry } from '../internal/class/cleanup-registry';
4
+ import { FormAssociatedBase, getInternals } from '../internal/class/form-associated-base';
5
+ import { MutationObserverSet } from '../internal/class/mutation-observer-set';
6
+ import { NavigationPath } from '../internal/class/navigation-path';
7
+ import { queryInsertPosition, startViewTransition } from '../internal/privateNamespace';
8
+ import { $, devFlags, kebabCase, onoff, renderLabel, renderLabelIcon, renderLabelTitle } from '../internal/utils';
9
+ import { html, render } from '../tpl';
10
+ const pickerStyles = ['menu', 'inline', 'navigation-link', 'sheet', 'automatic'];
11
+ const update = (path, node, overwrite = true) => {
12
+ if (devFlags.debug)
13
+ console.debug(`PickerView: update`);
14
+ if (!(path instanceof NavigationPath))
15
+ throw new Error('invalid view');
16
+ const { component, page } = path;
17
+ if (!component)
18
+ return;
19
+ const position = queryInsertPosition(component); //'afterend'
20
+ // const lookFor = 'beforebegin' === position ? 'previousElementSibling' : 'nextElementSibling'
21
+ if (page)
22
+ page.insertAdjacentElement(position, node);
23
+ };
24
+ const reflectSpawnedPage = (current, source) => {
25
+ if (devFlags.debug)
26
+ console.debug(`PickerView: reflectSpawnedPage`);
27
+ const { page: oldSv, toolBarConfig: oldToolbar } = new NavigationPath(current).hydrate(), { page: newSv, toolBarConfig: newToolbar } = new NavigationPath(source).hydrate();
28
+ const oldList = oldSv?.querySelector('list-view'), newList = newSv?.querySelector('list-view'), oldBackBtn = oldToolbar?.at(0)?.querySelector('button'), newBackBtn = newToolbar?.at(0)?.querySelector('button');
29
+ // pre replace list (always exists)
30
+ const oldOpenedDetails = [...(oldList?.querySelectorAll('list-view details[open]>summary') ?? [])].map((item) => item.textContent.trim());
31
+ // replace list (always exists)
32
+ if (oldList && newList)
33
+ oldList.replaceWith(newList);
34
+ // post replace list (always exists)
35
+ for (const label of oldOpenedDetails)
36
+ for (const summary of newList?.querySelectorAll('list-view details>summary') ?? [])
37
+ if (summary.textContent.trim() === label) {
38
+ summary.parentElement?.setAttribute('open', 'open');
39
+ break;
40
+ }
41
+ // replace backbtns (always exist)
42
+ if (oldBackBtn && newBackBtn)
43
+ oldBackBtn.replaceWith(newBackBtn);
44
+ // overwrite navtitle (always exists)
45
+ oldSv?.setAttribute('navigation-inline-title', newSv?.getAttribute('navigation-inline-title') ?? '');
46
+ // replace search (if exists)
47
+ oldList?.style.setProperty('--list--sticky-block-size', newList?.style.getPropertyValue('--list--sticky-block-size') ?? '');
48
+ // replace search (if exists)
49
+ const oldSearch = oldSv?.querySelector('sticky-container'), newSearch = newSv?.querySelector('sticky-container');
50
+ // if already exists, DO NOTHING dont mess up the input element
51
+ if (newSearch) {
52
+ if (!oldSearch)
53
+ oldSv?.insertAdjacentHTML('afterend', `<sticky-container edge="navbar" style="order: -1" padding>
54
+ <v-stack spacing="0" alignment="fill">
55
+ <input is="search-view" placeholder="Search">
56
+ <!--<button type="button">Filters</button>-->
57
+ </v-stack>
58
+ </sticky-container>`);
59
+ }
60
+ else
61
+ oldSearch?.remove();
62
+ };
63
+ const extractTag = (node) => {
64
+ if (devFlags.debug)
65
+ console.debug(`PickerView: extractTag`);
66
+ return node instanceof HTMLOptionElement ? (((node.getAttribute('value') ?? node.textContent?.trim()) || node.getAttribute('label')) ?? '') : (node.value ?? '');
67
+ }, extractLabel = (node) => {
68
+ if (devFlags.debug)
69
+ console.debug(`PickerView: extractLabelFromGroup`);
70
+ if (node instanceof Element)
71
+ if (node instanceof HTMLOptionElement)
72
+ return (node.getAttribute('label') ?? node.getAttribute('value') ?? node.textContent?.trim()) || null;
73
+ else
74
+ return node.getAttribute('DATALIST' === node.tagName ? 'data-label' : 'label');
75
+ else
76
+ return node.title ?? node.value ?? null;
77
+ // return node instanceof Element ? node.getAttribute('DATALIST' === node.tagName ? 'data-label' : 'label') : (node.title ?? node.value ?? null)
78
+ }, extractIcon = (node) => {
79
+ if (devFlags.debug)
80
+ console.debug(`PickerView: extractIcon`);
81
+ // return node instanceof Element ? node.getAttribute('data-system-image') : (node.systemImage ?? null)
82
+ if (node instanceof Element)
83
+ return node.getAttribute('data-system-image');
84
+ else
85
+ return node.systemImage ?? null;
86
+ }, extractSubtitle = (node) => {
87
+ if (devFlags.debug)
88
+ console.debug(`PickerView: extractSubtitle`);
89
+ if (node instanceof Element)
90
+ return node.getAttribute('data-subtitle');
91
+ else
92
+ return node.subtitle ?? null;
93
+ };
94
+ const allLeaves = (node) => node.children.every((c) => 0 === c.children.length);
95
+ const parseDictionary = (value) => {
96
+ let dictionary = [];
97
+ try {
98
+ dictionary = JSON.parse(value ?? '[]');
99
+ }
100
+ catch {
101
+ console.error('invalid-dictionary');
102
+ }
103
+ return dictionary;
104
+ };
105
+ const indexGroups = (nodes, parentPath = '') => {
106
+ const map = new Map();
107
+ nodes.forEach((node, i) => {
108
+ const id = parentPath ? `${parentPath}.${i}` : `${i}`;
109
+ map.set(id, node); // no DOM write — works identically for Element and DictEntry
110
+ const children = node instanceof Element ? Array.from(node.children) : node.children;
111
+ for (const [cid, cnode] of indexGroups(children, id))
112
+ map.set(cid, cnode);
113
+ });
114
+ return map;
115
+ };
116
+ const collectLeafValues = (node) => (node.children.length ? node.children.flatMap(collectLeafValues) : [node.value]);
117
+ /**
118
+ *
119
+ * @attr {menu|inline|navigation-link|sheet|automatic} picker-style
120
+ *
121
+ * @attr {string} help - Adds a help tooltip to the trigger of the picker, if style supports one
122
+ *
123
+ * @attr {DictEntry[]} dictionary - Renders all options using this array
124
+ *
125
+ * @attr {vertical|horizontal|auto} label-value-placement
126
+ *
127
+ * @slot list
128
+ */
129
+ export class PickerView extends FormAssociatedBase {
130
+ static get ATTR() {
131
+ return {
132
+ PLACEHOLDER: 'prompt',
133
+ PLACEHOLDER_ICON: 'prompt-icon',
134
+ LABEL: 'label',
135
+ PICKER_STYLE: 'picker-style',
136
+ SELECTION: 'selection',
137
+ SEARCHABLE: 'searchable',
138
+ CURRENT_VALUE_LABEL: 'current-value-label',
139
+ CURRENT_VALUE_ICON: 'current-value-icon',
140
+ TRIGGER_HELP: 'help',
141
+ DICTIONARY: 'dictionary',
142
+ REQUIRED: 'required',
143
+ };
144
+ }
145
+ static get observedAttributes() {
146
+ return Object.values(this.ATTR);
147
+ }
148
+ static #templates = new Map();
149
+ #spawn;
150
+ #makeGroupClickHandler(el, groupId) {
151
+ return async (evt) => {
152
+ evt.stopImmediatePropagation();
153
+ evt.preventDefault();
154
+ const { target } = evt;
155
+ if (!(target instanceof HTMLElement))
156
+ return;
157
+ const newPage = this.#spawnPage(el instanceof Element ? Array.from(el.children) : el.children, 'body-view', groupId, this.hasAttribute('searchable'), extractLabel(el));
158
+ if (!newPage)
159
+ return;
160
+ newPage.dataset.groupId = groupId; // <-- tag the spawned page
161
+ const path = new NavigationPath(target)?.hydrate();
162
+ await startViewTransition(target, 'forwards', async () => {
163
+ update(path, newPage);
164
+ this.#reflectSelectionOnButtons();
165
+ });
166
+ };
167
+ }
168
+ #spawnPage = (elements, tag, parentGroupId, searchable = false, title) => {
169
+ if (devFlags.debug)
170
+ console.debug(`${_a.name} #spawnPage`);
171
+ const body = $(`<${'sheet-view' === tag ? 'dialog is="sheet-view"' : 'body-view'}><scroll-view><v-stack placement="leading fill"><list-view preferred-expanded-style="inset"></list-view></v-stack></scroll-view><tool-bar><tool-bar-item slot="top-bar-leading"><button type="button" tabindex="0"><label-view system-image="${'sheet-view' === tag ? 'x' : 'caret-left'}"></label-view></button></tool-bar-item></tool-bar></${'sheet-view' === tag ? 'dialog' : 'body-view'}>`, '>1'), sv = body.querySelector('scroll-view'), list = body.querySelector('list-view'), backBtn = body.querySelector('button');
172
+ if (title)
173
+ sv?.setAttribute('navigation-inline-title', title);
174
+ if (searchable) {
175
+ list?.style.setProperty('--list--sticky-block-size', '70px');
176
+ list?.insertAdjacentHTML('afterend', `<sticky-container edge="navbar" style="order: -1" padding>
177
+ <v-stack spacing="0" alignment="fill">
178
+ <input is="search-view" placeholder="Search">
179
+ <!--<button type="button">Filters</button>-->
180
+ </v-stack>
181
+ </sticky-container>`);
182
+ const searchInput = sv?.querySelector('input'); // FIXME: compoennt is=search-view??
183
+ searchInput?.addEventListener('focus', ({ target }) => {
184
+ if (this.#spawn)
185
+ this.dispatchEvent(new CustomEvent('picker:searchfocus', {
186
+ detail: { element: this.#spawn, search: target instanceof HTMLInputElement ? target.value : '' },
187
+ bubbles: true,
188
+ composed: true,
189
+ }));
190
+ });
191
+ searchInput?.addEventListener('blur', ({ target }) => {
192
+ if (this.#spawn)
193
+ this.dispatchEvent(new CustomEvent('picker:searchblur', {
194
+ detail: { element: this.#spawn, search: target instanceof HTMLInputElement ? target.value : '' },
195
+ bubbles: true,
196
+ composed: true,
197
+ }));
198
+ });
199
+ searchInput?.addEventListener('input', ({ target }) => {
200
+ if (this.#spawn)
201
+ this.dispatchEvent(new CustomEvent('picker:searchinput', {
202
+ detail: { element: this.#spawn, search: target instanceof HTMLInputElement ? target.value : '' },
203
+ bubbles: true,
204
+ composed: true,
205
+ }));
206
+ });
207
+ }
208
+ backBtn?.setAttribute('help', I18n.t('ButtonRole').Back);
209
+ backBtn?.addEventListener('click', async (evt) => {
210
+ evt.stopImmediatePropagation();
211
+ evt.preventDefault();
212
+ const { target } = evt;
213
+ if (!(target instanceof HTMLElement))
214
+ return;
215
+ const { component } = new NavigationPath(target)?.hydrate();
216
+ await startViewTransition(target, 'backwards', async () => {
217
+ component?.remove();
218
+ });
219
+ });
220
+ for (const [i, el] of elements.entries()) {
221
+ if (el instanceof Element)
222
+ switch (el.tagName) {
223
+ case 'OPTGROUP': {
224
+ const group = _a.#wrapOptgroupTag(el);
225
+ _a.#reflectButtons([...el.children], group);
226
+ for (const btn of group.querySelectorAll(':scope>button'))
227
+ btn.addEventListener('click', this.#handlePageBtnClick);
228
+ list?.appendChild(group);
229
+ break;
230
+ }
231
+ case 'OPTION': {
232
+ const btn = _a.#wrapOptionTag(el);
233
+ btn.addEventListener('click', this.#handlePageBtnClick);
234
+ list?.appendChild(btn);
235
+ break;
236
+ }
237
+ default: {
238
+ const btn = _a.#wrapOptionSpawnTag(el);
239
+ const groupId = parentGroupId ? `${parentGroupId}.${i}` : `${i}`;
240
+ // const btn = $<HTMLButtonElement>(
241
+ // `<button type="button" tabindex="0" navigation-link><h-stack distribution="leading" template="auto spacer"><label-view data-role="check" style="visibility: hidden"><image-view slot="icon" system-name="check"></image-view></label-view><label-view><span></span></label-view></h-stack></button>`,
242
+ // '>1'
243
+ // ),
244
+ // label = btn.querySelector<LabelView>(':scope>h-stack>label-view:nth-child(2)')
245
+ // const node = el as HTMLElement
246
+ // if (label && node.dataset.label) renderLabelTitle(label, node.dataset.label) //label?.setAttribute('title', el.dataset.label)
247
+ btn.dataset.groupId = groupId;
248
+ btn.addEventListener('click', this.#makeGroupClickHandler(el, groupId));
249
+ list?.appendChild(btn);
250
+ break;
251
+ }
252
+ }
253
+ else {
254
+ if (el.children.length)
255
+ if (allLeaves(el)) {
256
+ const group = _a.#wrapOptgroupTag(el);
257
+ _a.#reflectButtons(el.children, group);
258
+ for (const btn of group.querySelectorAll(':scope>button'))
259
+ btn.addEventListener('click', this.#handlePageBtnClick);
260
+ list?.appendChild(group);
261
+ }
262
+ else {
263
+ const btn = _a.#wrapOptionSpawnTag(el);
264
+ const groupId = parentGroupId ? `${parentGroupId}.${i}` : `${i}`;
265
+ btn.dataset.groupId = groupId;
266
+ btn.addEventListener('click', this.#makeGroupClickHandler(el, groupId));
267
+ list?.appendChild(btn);
268
+ }
269
+ else {
270
+ const btn = _a.#wrapOptionTag(el);
271
+ btn.addEventListener('click', this.#handlePageBtnClick);
272
+ list?.appendChild(btn);
273
+ }
274
+ }
275
+ }
276
+ return body;
277
+ };
278
+ #resyncSpawnedPages = (freshRoot) => {
279
+ if (!this.#spawn)
280
+ return;
281
+ const groupMap = indexGroups(freshRoot);
282
+ for (const el of this.#spawn.querySelectorAll('body-view')) {
283
+ const depth = $.ancestors('body-view,[is=sheet-view]', el).indexOf(this.#spawn);
284
+ if (0 >= depth)
285
+ continue;
286
+ const groupId = el.dataset.groupId;
287
+ const source = groupId ? groupMap.get(groupId) : undefined;
288
+ if (!source) {
289
+ el.remove();
290
+ continue;
291
+ }
292
+ const children = source instanceof Element ? Array.from(source.children) : source.children;
293
+ const title = source instanceof Element ? extractLabel(source) : (source.title ?? null);
294
+ const newPage = this.#spawnPage(children, 'body-view', groupId, this.hasAttribute('searchable'), title);
295
+ newPage.dataset.groupId = groupId;
296
+ reflectSpawnedPage(el, newPage);
297
+ }
298
+ };
299
+ #renderButtons(input) {
300
+ if (devFlags.debug)
301
+ console.debug(`${_a.name} #renderButtons`);
302
+ switch (input.mode) {
303
+ case 'dictionary': {
304
+ const flattenDictionary = (tree) => {
305
+ const out1 = {}, out2 = {}, stack = [tree], idx = [0];
306
+ while (stack.length) {
307
+ const frame = stack[stack.length - 1], i = idx[idx.length - 1];
308
+ if (i >= frame.length) {
309
+ stack.pop();
310
+ idx.pop();
311
+ continue;
312
+ }
313
+ idx[idx.length - 1]++;
314
+ const { value, title, systemImage, children } = frame[i];
315
+ out1[value] = title;
316
+ out2[value] = systemImage;
317
+ if (children.length) {
318
+ stack.push(children);
319
+ idx.push(0);
320
+ }
321
+ }
322
+ return { labels: out1, icons: out2 };
323
+ };
324
+ this.#lastRenderedLabelMap = flattenDictionary(input.source).labels;
325
+ this.#lastRenderedIconMap = flattenDictionary(input.source).icons;
326
+ // const collectLeafValues = (node: DictEntry): string[] => (node.children.length ? node.children.flatMap(collectLeafValues) : [node.value])
327
+ // collectGroups = (nodes: DictEntry[]): string[][] => {
328
+ // const groups: string[][] = []
329
+ // const walk = (list: DictEntry[]) => {
330
+ // for (const node of list) {
331
+ // if (!node.children.length) continue
332
+ // if (!allLeaves(node)) groups.push(collectLeafValues(node))
333
+ // walk(node.children)
334
+ // }
335
+ // }
336
+ // walk(nodes)
337
+ // return groups
338
+ // }
339
+ this.#lastIndexedRoot = input.source; //this.#lastRenderedGroupMap = collectGroups(dictionary)
340
+ break;
341
+ }
342
+ case 'list': {
343
+ this.#lastRenderedLabelMap = {};
344
+ this.#lastRenderedIconMap = {};
345
+ for (const el of input.source)
346
+ if (el.matches('option')) {
347
+ const k = extractTag(el);
348
+ this.#lastRenderedLabelMap[k] = el.getAttribute('label') ?? undefined;
349
+ this.#lastRenderedIconMap[k] = extractIcon(el) ?? undefined;
350
+ }
351
+ else
352
+ for (const opt of el.querySelectorAll('option')) {
353
+ const k = extractTag(opt);
354
+ this.#lastRenderedLabelMap[k] = opt.getAttribute('label') ?? undefined;
355
+ this.#lastRenderedIconMap[k] = extractIcon(opt) ?? undefined;
356
+ }
357
+ this.#lastIndexedRoot = input.source;
358
+ // this.#lastRenderedGroupMap = assigned.flatMap<string[]>((el) =>
359
+ // [...(el.matches('datalist') ? [el as HTMLDataListElement] : []), ...el.querySelectorAll<HTMLDataListElement>('datalist')].map((dl) =>
360
+ // Array.from(dl.options).map((opt) => extractTagFromOption(opt))
361
+ // )
362
+ // )
363
+ break;
364
+ }
365
+ }
366
+ switch (this.pickerStyle) {
367
+ case 'sheet':
368
+ case 'navigation-link': {
369
+ // current value label only
370
+ const currentValueLabel = this.querySelector(':scope>label-view:not([slot])') ?? this.appendChild($(`<label-view></label-view>`, '>1'));
371
+ // reset state
372
+ // if (currentValueLabel) {
373
+ // renderLabelIcon(currentValueLabel, 'dots-three') // overwritten
374
+ // renderLabelTitle(currentValueLabel, this.#currentValueLabel) // overwritten
375
+ // }
376
+ // clear all siblings
377
+ for (const el of this.querySelectorAll(':scope>:not([slot])'))
378
+ if (currentValueLabel !== el)
379
+ el.remove();
380
+ CleanupRegistry.unregister(this, 'trigger');
381
+ CleanupRegistry.register(this, onoff('click', this.#handleTriggerClick, currentValueLabel).on(), 'trigger');
382
+ if (this.hasAttribute(this.constructor.ATTR.TRIGGER_HELP))
383
+ currentValueLabel?.setAttribute(this.constructor.ATTR.TRIGGER_HELP, this.getAttribute(this.constructor.ATTR.TRIGGER_HELP) ?? '');
384
+ // rebuild snapshot(tree)
385
+ if (!this.#spawn)
386
+ break;
387
+ // rerender level 0
388
+ reflectSpawnedPage(this.#spawn, this.#spawnPage(input.source, 'DIALOG' === this.#spawn.tagName ? 'sheet-view' : 'body-view', undefined, this.hasAttribute('searchable'), this.getAttribute('label')));
389
+ this.#resyncSpawnedPages(input.source);
390
+ // // FIXME:
391
+ // for (const el of this.#spawn.querySelectorAll<HTMLElement>('body-view')) {
392
+ // const depth = $.ancestors('body-view,[is=sheet-view]', el).indexOf(this.#spawn)
393
+ // if (0 >= depth) continue
394
+ // const datalist = this.querySelector<HTMLElement>(`:scope>${Array.from({ length: depth }, () => 'datalist').join('>')}`)
395
+ // if (!datalist) {
396
+ // el.remove()
397
+ // break
398
+ // }
399
+ // reflectSpawnedElement(el, this.#spawnPage(Array.from(datalist.children), 'body-view', this.hasAttribute('searchable'), datalist.dataset.label))
400
+ // }
401
+ break;
402
+ }
403
+ case 'menu': {
404
+ const menu = this.querySelector(':scope>menu-view:not([slot])') ?? this.appendChild($(`<menu-view></menu-view>`, '>1'));
405
+ // reset state
406
+ menu.innerHTML = '';
407
+ menu.tabIndex = 0;
408
+ // clear all siblings
409
+ for (const el of this.querySelectorAll(':scope>:not([slot])'))
410
+ if (menu !== el)
411
+ el.remove();
412
+ const currentValueLabel = menu.querySelector(':scope>label-view[slot=label]') ?? menu.appendChild($(`<label-view slot="label"></label-view>`, '>1'));
413
+ // if (currentValueLabel) {
414
+ // renderLabelIcon(currentValueLabel, 'dots-three') // overwritten
415
+ // renderLabelTitle(currentValueLabel, this.#currentValueLabel) // overwritten
416
+ // }
417
+ if (this.hasAttribute(this.constructor.ATTR.TRIGGER_HELP))
418
+ currentValueLabel?.setAttribute(this.constructor.ATTR.TRIGGER_HELP, this.getAttribute(this.constructor.ATTR.TRIGGER_HELP) ?? '');
419
+ _a.#reflectButtons(input.source, menu);
420
+ break;
421
+ }
422
+ case 'inline':
423
+ default: {
424
+ const sectionTpl = `<section-view></section-view>`;
425
+ const inlineList = this.querySelector(':scope>list-view:not([slot])') ?? this.appendChild($(`<list-view>${sectionTpl}</list-view>`, '>1')), section = inlineList.querySelector(':scope>section-view') ?? inlineList.appendChild($(sectionTpl, '>1'));
426
+ // reset state
427
+ section.innerHTML = '';
428
+ // clear all siblings
429
+ for (const el of this.querySelectorAll(':scope>:not([slot])'))
430
+ if (inlineList !== el)
431
+ el.remove();
432
+ // add label as a plain element
433
+ const value = this.getAttribute(this.constructor.ATTR.LABEL);
434
+ if (value) {
435
+ const hStack = $(`<h-stack distribution="leading" template="auto spacer"><label-view data-role="check" style="visibility: hidden"><image-view slot="icon" system-name="check"></image-view></label-view><label-view><span></span></label-view></h-stack>`, '>1');
436
+ renderLabel(':scope>label-view:nth-child(2)', `<label-view><span></span></label-view>`, hStack, value);
437
+ // if (label) renderLabelTitle(label, value) //label.setAttribute('title', value)
438
+ section.insertAdjacentElement('beforeend', hStack);
439
+ }
440
+ if ('dictionary' === input.mode)
441
+ _a.#reflectButtons(input.source.filter((el) => 0 === el.children.length), section);
442
+ else
443
+ _a.#reflectButtons(input.source.filter((el) => el.matches('option')), section);
444
+ break;
445
+ }
446
+ }
447
+ this.#reflectSelectionOnButtons();
448
+ this.#reflectSelectionOnCurrentValueLabel();
449
+ }
450
+ #renderDictionary = (dictionary) => {
451
+ if (devFlags.debug)
452
+ console.debug(`${_a.name} ⚡️ mutation`);
453
+ this.#renderButtons({ mode: 'dictionary', source: dictionary });
454
+ };
455
+ #renderSlotted = (entries) => {
456
+ if (devFlags.debug)
457
+ console.debug(`${_a.name} ⚡️ mutation`);
458
+ this.#renderButtons({ mode: 'list', source: this.#slots?.get('list')?.assignedElements({ flatten: true }) ?? [] });
459
+ };
460
+ #renderValidityMsgs = (entries) => {
461
+ if (devFlags.debug)
462
+ console.debug(`${_a.name} ⚡️ mutation`);
463
+ this.setValidity(this.validity, this.validationMessage);
464
+ };
465
+ #lastRenderedStyle; //string | null
466
+ #shadowRoot;
467
+ #slots = new Map();
468
+ #validityObservers = new MutationObserverSet(this.#renderValidityMsgs);
469
+ #observers = new MutationObserverSet(this.#renderSlotted);
470
+ #customValidity = '';
471
+ #selection = '';
472
+ get selection() {
473
+ return this.#selection;
474
+ }
475
+ set selection(v) {
476
+ if (Object.is(this.#selection, v))
477
+ return;
478
+ this.#selection = v;
479
+ this.#reflectSelectionOnButtons();
480
+ this.#reflectSelectionOnCurrentValueLabel();
481
+ this.#sendValueToForm();
482
+ }
483
+ #lastRenderedLabelMap = {};
484
+ #lastRenderedIconMap = {};
485
+ #lastIndexedRoot = [];
486
+ get #currentValueLabel() {
487
+ return ((this.getAttribute(this.constructor.ATTR.CURRENT_VALUE_LABEL) ?? '').replaceAll('{{selection}}', this.#selection).replaceAll('{{currentValueLabel}}', this.#selection) ||
488
+ this.#lastRenderedLabelMap[this.#selection]);
489
+ }
490
+ get #currentValueIcon() {
491
+ return (this.getAttribute(this.constructor.ATTR.CURRENT_VALUE_ICON) ?? '') || this.#lastRenderedIconMap[this.#selection];
492
+ }
493
+ get #internals() {
494
+ return getInternals(this);
495
+ }
496
+ get template() {
497
+ if (!_a.#templates.has(this.pickerStyle))
498
+ switch (this.pickerStyle) {
499
+ case 'sheet':
500
+ case 'navigation-link':
501
+ _a.#templates.set(this.pickerStyle, $(String.raw `
502
+ <label part="root picker-stack">
503
+ <div part="root picker-label-stack">
504
+ <slot name="label"></slot>
505
+ </div>
506
+ <div part="root picker-input-stack">
507
+ <slot></slot>
508
+ </div>
509
+ <slot name="list" hidden></slot>
510
+ <slot name="validity-options" hidden></slot>
511
+ </label>
512
+ `));
513
+ break;
514
+ case 'menu':
515
+ _a.#templates.set(this.pickerStyle, $(String.raw `
516
+ <label part="root picker-stack">
517
+ <div part="root picker-label-stack">
518
+ <slot name="label"></slot>
519
+ </div>
520
+ <div part="root picker-input-stack">
521
+ <slot></slot>
522
+ </div>
523
+ <slot name="list" hidden></slot>
524
+ <slot name="validity-options" hidden></slot>
525
+ </label>
526
+ `));
527
+ // break
528
+ // case 'compact':
529
+ // PickerView.#templates.set(
530
+ // style,
531
+ // Object.assign(document.createElement('template'), {
532
+ // innerHTML: String.raw`
533
+ // <label part="compact-picker">
534
+ // <input type="text" part="compact-input">
535
+ // </label>
536
+ // `,
537
+ // })
538
+ // )
539
+ // break
540
+ // case 'fancy':
541
+ // PickerView.#templates.set(
542
+ // style,
543
+ // Object.assign(document.createElement('template'), {
544
+ // innerHTML: String.raw`
545
+ // <div part="fancy-picker">
546
+ // <span>Fancy Picker</span>
547
+ // <input type="text" part="fancy-input">
548
+ // </div>
549
+ // `,
550
+ // })
551
+ // )
552
+ break;
553
+ // case 'gg':
554
+ // PickerView.#templates.set(
555
+ // style,
556
+ // Object.assign(document.createElement('template'), {
557
+ // innerHTML: String.raw`
558
+ // <label part="root text-field-stack">
559
+ // <div part="root text-field-label-stack">
560
+ // <slot name="label"></slot>
561
+ // </div>
562
+ // <div part="root text-field-input-stack">
563
+ // <input type="text" part="root input text-field-form-input" list="tickmarks">
564
+ // <datalist id="tickmarks">
565
+ // <option value="0" label="0%"></option>
566
+ // </datalist>
567
+ // </div>
568
+ // <slot name="list" hidden></slot>
569
+ // </label>`,
570
+ // })
571
+ // )
572
+ // break
573
+ case 'inline':
574
+ default:
575
+ _a.#templates.set(this.pickerStyle, $(String.raw `
576
+ <label part="root picker-stack">
577
+ <div part="root picker-label-stack">
578
+ <slot name="label"></slot>
579
+ </div>
580
+ <div part="root picker-input-stack">
581
+ <slot></slot>
582
+ </div>
583
+ <slot name="list" hidden></slot>
584
+ <slot name="validity-options" hidden></slot>
585
+ </label>`));
586
+ break;
587
+ }
588
+ return _a.#templates.get(this.pickerStyle);
589
+ }
590
+ constructor() {
591
+ super();
592
+ this.#shadowRoot = this.attachShadow({ mode: 'closed' });
593
+ }
594
+ attributeChangedCallback(name, oldValue, newValue) {
595
+ if (devFlags.debug)
596
+ console.debug(`${_a.name} ⚡️ attr-change [${name}] ("${oldValue}" → "${newValue}")`);
597
+ switch (name) {
598
+ case this.constructor.ATTR.PLACEHOLDER:
599
+ case this.constructor.ATTR.PLACEHOLDER_ICON:
600
+ if (oldValue === newValue)
601
+ break;
602
+ this.#reflectSelectionOnCurrentValueLabel(); //this.#reflectPlaceholder(newValue)
603
+ break;
604
+ case this.constructor.ATTR.LABEL:
605
+ this.#reflectLabel(newValue);
606
+ // this.#sendValueToForm()
607
+ break;
608
+ case this.constructor.ATTR.PICKER_STYLE:
609
+ if (oldValue === newValue)
610
+ break;
611
+ this.#render();
612
+ break;
613
+ case this.constructor.ATTR.SEARCHABLE:
614
+ if (oldValue === newValue)
615
+ break;
616
+ this.#renderSlotted([]);
617
+ break;
618
+ case this.constructor.ATTR.CURRENT_VALUE_LABEL:
619
+ case this.constructor.ATTR.CURRENT_VALUE_ICON:
620
+ // if (oldValue === newValue) break
621
+ this.#reflectSelectionOnCurrentValueLabel();
622
+ break;
623
+ case this.constructor.ATTR.TRIGGER_HELP:
624
+ // if (oldValue === newValue) break
625
+ this.#reflectTriggerHelp();
626
+ break;
627
+ case this.constructor.ATTR.DICTIONARY:
628
+ if (oldValue === newValue)
629
+ break;
630
+ this.#renderDictionary(parseDictionary(newValue));
631
+ break;
632
+ case this.constructor.ATTR.REQUIRED:
633
+ if (oldValue === newValue)
634
+ break;
635
+ this.#sendValueToForm(false);
636
+ break;
637
+ case this.constructor.ATTR.SELECTION:
638
+ // nothing happens
639
+ break;
640
+ }
641
+ }
642
+ disconnectedCallback() {
643
+ if (devFlags.debug)
644
+ console.debug(`${_a.name} ⚡️ disconnect`);
645
+ CleanupRegistry.unregister(this);
646
+ this.#validityObservers.unobserveAll();
647
+ this.#observers.unobserveAll();
648
+ }
649
+ connectedCallback() {
650
+ if (devFlags.debug)
651
+ console.debug(`${_a.name} ⚡️ connect`);
652
+ CleanupRegistry.register(this, onoff('localechange', () => {
653
+ this.#renderSlotted([]);
654
+ }, I18n.on).on());
655
+ CleanupRegistry.register(this, onoff('click', this.#handleClick, this).on());
656
+ if (!this.hasAttribute(this.constructor.ATTR.PICKER_STYLE))
657
+ this.#render(); // skip if already rendered by attr-change during upgrade!
658
+ // finally
659
+ if (!this.hasAttribute('selection'))
660
+ return;
661
+ this.#selection = this.getAttribute('selection') ?? '';
662
+ this.#sendValueToForm(false);
663
+ }
664
+ get pickerStyle() {
665
+ const attr = this.constructor.ATTR.PICKER_STYLE;
666
+ return pickerStyles.includes(this.getAttribute(attr) ?? '') ? this.getAttribute(attr) : 'automatic';
667
+ }
668
+ #render() {
669
+ if (devFlags.debug)
670
+ console.debug(`${_a.name} ⚡️ render (${this.pickerStyle})`);
671
+ // const style = this.getAttribute((this.constructor as typeof PickerView).ATTR.PICKER_STYLE)
672
+ if (this.#lastRenderedStyle === this.pickerStyle)
673
+ return; // skip if already applied
674
+ this.#lastRenderedStyle = this.pickerStyle;
675
+ // clear shadow DOM
676
+ this.#shadowRoot.replaceChildren(document.importNode(this.template, true));
677
+ CleanupRegistry.unregister(this, 'slots');
678
+ for (const slot of this.#shadowRoot.querySelectorAll('slot'))
679
+ this.#slots?.set(slot.name, slot);
680
+ CleanupRegistry.register(this, () => {
681
+ this.#slots = new Map();
682
+ }, 'slots');
683
+ CleanupRegistry.unregister(this, 'validities');
684
+ CleanupRegistry.register(this, onoff('slotchange', this.#handleValiditiesSlotchange, this.#slots?.get('validity-options')).on(), 'validities');
685
+ CleanupRegistry.unregister(this, 'datalist'); //off1()
686
+ CleanupRegistry.register(this, onoff('slotchange', this.#handleListSlotchange, this.#slots?.get('list')).on(), 'datalist');
687
+ // #renderSlotted should run automatically now by slotchange initial event
688
+ // switch (this.pickerStyle) {
689
+ // }
690
+ }
691
+ #sendValueToForm = (dispatchEvent = true) => {
692
+ // input.value has already been updated/synced !!
693
+ if (this.matches(':disabled'))
694
+ return this.setValidity({});
695
+ if (this.hasAttribute('required')) {
696
+ if (!this.#selection) {
697
+ this.setValidity({ badInput: true }, 'invalid-selection');
698
+ }
699
+ else
700
+ this.setValidity({});
701
+ }
702
+ else
703
+ this.setValidity({});
704
+ const entries = new FormData();
705
+ entries.append(this.name, this.#selection);
706
+ this.#internals.setFormValue(entries);
707
+ if (dispatchEvent)
708
+ this.dispatchEvent(new CustomEvent('selection', { detail: { selection: this.#selection }, bubbles: true, composed: true }));
709
+ };
710
+ #handleTriggerClick = async ({ type, target }) => {
711
+ if (devFlags.debug)
712
+ console.debug(`${_a.name} ⚡️ ${type}`);
713
+ if (!(target instanceof HTMLElement))
714
+ return;
715
+ this.#spawn?.remove?.();
716
+ const level0 = this.#spawnPage(this.hasAttribute(this.constructor.ATTR.DICTIONARY) ? parseDictionary(this.getAttribute('dictionary')) : (this.#slots?.get('list')?.assignedElements() ?? []), 'sheet' === this.pickerStyle ? 'sheet-view' : 'body-view', undefined, this.hasAttribute('searchable'), this.getAttribute('label'));
717
+ if (!level0)
718
+ return;
719
+ const path = new NavigationPath(target)?.hydrate();
720
+ await startViewTransition(target, 'forwards', async () => {
721
+ this.#spawn = level0;
722
+ update(path, level0);
723
+ this.#reflectSelectionOnButtons();
724
+ });
725
+ };
726
+ #handlePageBtnClick = async (evt) => {
727
+ if (devFlags.debug)
728
+ console.debug(`${_a.name} ⚡️ ${evt?.type}`);
729
+ evt.stopImmediatePropagation();
730
+ evt.preventDefault();
731
+ const { currentTarget: btn } = evt;
732
+ if (!(btn instanceof HTMLElement))
733
+ return;
734
+ // const btn = target.closest('button')
735
+ // if (!btn) return
736
+ const proceed = () => {
737
+ this.#spawn?.remove();
738
+ this.#selection = btn.getAttribute('value') ?? '';
739
+ this.#reflectSelectionOnButtons();
740
+ this.#reflectSelectionOnCurrentValueLabel();
741
+ this.#sendValueToForm();
742
+ };
743
+ const { body } = new NavigationPath(this.#spawn)?.hydrate();
744
+ if (!body)
745
+ return proceed();
746
+ await startViewTransition(body, 'backwards', async () => {
747
+ proceed();
748
+ });
749
+ };
750
+ #handleClick({ type, target }) {
751
+ if (devFlags.debug)
752
+ console.debug(`${_a.name} ⚡️ ${type}`);
753
+ if (!(target instanceof HTMLElement))
754
+ return;
755
+ const btn = target.closest('button');
756
+ if (!btn)
757
+ return;
758
+ this.#selection = btn.getAttribute('value') ?? '';
759
+ this.#reflectSelectionOnButtons();
760
+ this.#reflectSelectionOnCurrentValueLabel();
761
+ this.#sendValueToForm();
762
+ }
763
+ #handleValiditiesSlotchange = ({ type, target: slot }) => {
764
+ if (devFlags.debug)
765
+ console.debug(`${_a.name} ⚡️ ${type}`);
766
+ if (!(slot instanceof HTMLSlotElement))
767
+ return;
768
+ const assigned = slot.assignedElements();
769
+ this.#validityObservers.syncObservations(assigned, ['value', 'label']);
770
+ // if (0 < assigned.length)
771
+ this.#renderValidityMsgs([]);
772
+ };
773
+ #handleListSlotchange = ({ type, target: slot }) => {
774
+ if (devFlags.debug)
775
+ console.debug(`${_a.name} ⚡️ ${type}`);
776
+ if (!(slot instanceof HTMLSlotElement))
777
+ return;
778
+ const assigned = slot.assignedElements();
779
+ this.#observers.syncObservations(assigned);
780
+ // if (0 < assigned.length)
781
+ this.#renderSlotted([]);
782
+ };
783
+ static #wrapOptionSpawnTag(node) {
784
+ if (devFlags.debug)
785
+ console.debug(`${_a.name} #wrapOptionSpawnTag`);
786
+ const title = extractLabel(node), subtitle = extractSubtitle(node), icon = extractIcon(node);
787
+ const mount = document.createElement('div');
788
+ render(html `<button type="button" tabindex="0" navigation-link>
789
+ <h-stack distribution="leading" template="auto spacer">
790
+ <label-view data-role="check" style="visibility: hidden">
791
+ <image-view slot="icon" system-name="check"></image-view>
792
+ </label-view>
793
+ <h-stack distribution="fill" template="spacer auto">
794
+ ${icon ? html `<label-view system-image="${icon}"></label-view>` : null}
795
+ <v-stack spacing="3" alignment="fill">
796
+ <label-view title="${title}"></label-view>
797
+ ${subtitle ? html `<label-view foreground="secondary" font="callout" title="${subtitle}"></label-view>` : null}
798
+ </v-stack>
799
+ </h-stack>
800
+ </h-stack>
801
+ </button>`, mount);
802
+ // const btn = $<HTMLButtonElement>(
803
+ // `<button type="button" tabindex="0" navigation-link><h-stack distribution="leading" template="auto spacer"><label-view data-role="check" style="visibility: hidden"><image-view slot="icon" system-name="check"></image-view></label-view><label-view><span></span></label-view></h-stack></button>`,
804
+ // '>1'
805
+ // ),
806
+ // hStack = btn.querySelector<LabelView>(':scope>h-stack') ?? undefined
807
+ // renderLabel(
808
+ // ':scope>label-view:nth-child(2)',
809
+ // `<label-view><span></span></label-view>`,
810
+ // hStack,
811
+ // extractLabelFromGroup(node as HTMLDataListElement),
812
+ // extractImgFromGroup(node as HTMLDataListElement)
813
+ // )
814
+ // // label = btn.querySelector<LabelView>(':scope>h-stack>label-view:nth-child(2)')
815
+ // // if (label) {
816
+ // // const lbl = extractLabelFromGroup(node as HTMLDataListElement),
817
+ // // img =extractImgFromGroup(node as HTMLDataListElement)
818
+ // // if(lbl) renderLabelTitle(label, node.dataset.label) //label?.setAttribute('title', el.dataset.label)
819
+ // // }
820
+ return mount.firstElementChild; //btn
821
+ }
822
+ static #wrapOptionTag(node) {
823
+ if (devFlags.debug)
824
+ console.debug(`${_a.name} #wrapOptionTag`);
825
+ const tag = extractTag(node), title = extractLabel(node), subtitle = extractSubtitle(node), icon = extractIcon(node);
826
+ const mount = document.createElement('div');
827
+ render(html `<button type="button" tabindex="0" value="${tag}">
828
+ <h-stack distribution="leading" template="auto spacer">
829
+ <label-view data-role="check">
830
+ <image-view slot="icon" system-name="check"></image-view>
831
+ </label-view>
832
+ <h-stack distribution="fill" template="spacer auto">
833
+ ${icon ? html `<label-view system-image="${icon}"></label-view>` : null}
834
+ <v-stack spacing="3" alignment="fill">
835
+ <label-view title="${title}"></label-view>
836
+ ${subtitle ? html `<label-view foreground="secondary" font="callout" title="${subtitle}"></label-view>` : null}
837
+ </v-stack>
838
+ </h-stack>
839
+ </h-stack>
840
+ </button>`, mount);
841
+ // const btn = $(
842
+ // `<button type="button" tabindex="0"><h-stack distribution="leading" template="auto spacer"><label-view data-role="check"><image-view slot="icon" system-name="check"></image-view></label-view></h-stack></button>`,
843
+ // '>1'
844
+ // ),
845
+ // hStack = btn.querySelector<HTMLElement>(':scope>h-stack')
846
+ // // chevron = hStack?.querySelector<HTMLElement>(':scope>label-view')
847
+ // btn.setAttribute('value', extractTagFromOption(node))
848
+ // // if (selection !== btn.getAttribute('value')) chevron?.style.setProperty('visibility', 'hidden')
849
+ // const label = $<LabelView>(`<label-view></label-view>`, '>1')
850
+ // renderLabelTitle(label, extractLabel(node)) // label.querySelector('span')!.textContent = extractCurrentValueFromOption(node) //label.setAttribute('title', extractCurrentValueFromOption(node))
851
+ // renderLabelIcon(label, extractIcon(node))
852
+ // hStack?.appendChild(label)
853
+ return mount.firstElementChild; //btn
854
+ }
855
+ static #wrapOptgroupTag(node) {
856
+ if (devFlags.debug)
857
+ console.debug(`${_a.name} #wrapOptgroupTag`);
858
+ const title = extractLabel(node), subtitle = extractSubtitle(node), icon = extractIcon(node);
859
+ const mount = document.createElement('div');
860
+ render(html `<details is="disclosure-group" disclosure-style="marker-trailing">
861
+ <summary>
862
+ <h-stack distribution="leading" template="auto spacer">
863
+ <label-view data-role="check" style="visibility: hidden">
864
+ <image-view slot="icon" system-name="check"></image-view>
865
+ </label-view>
866
+ <h-stack distribution="fill" template="spacer auto">
867
+ ${icon ? html `<label-view system-image="${icon}"></label-view>` : null}
868
+ <v-stack spacing="3" alignment="fill">
869
+ <label-view title="${title}"></label-view>
870
+ ${subtitle ? html `<label-view foreground="secondary" font="callout" title="${subtitle}"></label-view>` : null}
871
+ </v-stack>
872
+ </h-stack>
873
+ </h-stack>
874
+ </summary>
875
+ </details>`, mount);
876
+ return mount.firstElementChild;
877
+ // const labelT = `<label-view><span></span></label-view>`,
878
+ // summaryT = `<summary><h-stack distribution="leading" template="auto spacer"><label-view data-role="check" style="visibility: hidden"><image-view slot="icon" system-name="check"></image-view></label-view>${labelT}</h-stack></summary>`
879
+ // const group = $(`<details is="disclosure-group" disclosure-style="marker-trailing">${summaryT}</details>`, '>1'), // NOTE: already applied, here it covers spawned sheets
880
+ // hStack = group.querySelector(':scope>summary>h-stack') ?? undefined // ?? group.appendChild($(summaryT, '>1'))
881
+ // // summaryLabel = summary.querySelector(':scope>label-view') ?? summary.appendChild($(labelT, '>1'))
882
+ // // if (node.hasAttribute('label')) summaryLabel.setAttribute('title', node.getAttribute('label') ?? '')
883
+ // // if (node.hasAttribute('data-system-image')) summaryLabel.setAttribute('system-image', node.getAttribute('data-system-image') ?? '')
884
+ // renderLabel(':scope>label-view:nth-child(2)', labelT, hStack, extractLabel(node), extractIcon(node))
885
+ // return group
886
+ }
887
+ // static #reflectButtons(nodes: Element[], container: Element): void
888
+ // static #reflectButtons(nodes: Dictionary, container: Element): void
889
+ static #reflectButtons(nodes, container) {
890
+ if (devFlags.debug)
891
+ console.debug(`${_a.name} #reflectButtons`);
892
+ for (const node of nodes)
893
+ if (node instanceof Element)
894
+ switch (node.tagName) {
895
+ case 'DATALIST': {
896
+ const title = extractLabel(node), subtitle = extractSubtitle(node), icon = extractIcon(node);
897
+ const mount = document.createElement('div');
898
+ render(html `<menu-view tabindex="0">
899
+ <h-stack slot="label" distribution="leading" template="auto spacer">
900
+ <label-view data-role="check" style="visibility: hidden">
901
+ <image-view slot="icon" system-name="check"></image-view>
902
+ </label-view>
903
+ <h-stack distribution="fill" template="spacer auto">
904
+ ${icon ? html `<label-view system-image="${icon}"></label-view>` : null}
905
+ <v-stack spacing="3" alignment="fill">
906
+ <label-view title="${title}"></label-view>
907
+ ${subtitle ? html `<label-view foreground="secondary" font="callout" title="${subtitle}"></label-view>` : null}
908
+ </v-stack>
909
+ </h-stack>
910
+ </h-stack>
911
+ </menu-view>`, mount);
912
+ const group = mount.firstElementChild;
913
+ // const group = $(
914
+ // `<menu-view tabindex="0"><h-stack slot="label" distribution="leading" template="auto spacer"><label-view data-role="check" style="visibility: hidden"><image-view slot="icon" system-name="check"></image-view></label-view><label-view><span></span></label-view></h-stack></menu-view>`,
915
+ // '>1'
916
+ // ),
917
+ // hStack = group.querySelector(':scope>h-stack[slot=label]') ?? undefined
918
+ // // label = group.querySelector(':scope>label-view[slot=label]') ?? group.appendChild($(`<label-view slot="label"></label-view>`, '>1'))
919
+ // // if (node.hasAttribute('data-label')) label.setAttribute('title', node.getAttribute('data-label') ?? '')
920
+ // // if (node.hasAttribute('data-system-image')) label.setAttribute('system-image', node.getAttribute('data-system-image') ?? '')
921
+ // renderLabel(':scope>label-view:nth-child(2)', `<label-view><span></span></label-view>`, hStack, extractLabel(node as HTMLDataListElement), extractIcon(node as HTMLDataListElement))
922
+ _a.#reflectButtons([...node.children], group);
923
+ container.appendChild(group);
924
+ break;
925
+ }
926
+ case 'OPTGROUP': {
927
+ const group = _a.#wrapOptgroupTag(node);
928
+ _a.#reflectButtons([...node.children], group);
929
+ container.appendChild(group);
930
+ break;
931
+ }
932
+ case 'OPTION':
933
+ default: {
934
+ container.appendChild(_a.#wrapOptionTag(node));
935
+ break;
936
+ }
937
+ }
938
+ else {
939
+ if (node.children.length)
940
+ if (allLeaves(node)) {
941
+ const group = _a.#wrapOptgroupTag(node);
942
+ _a.#reflectButtons(node.children, group);
943
+ container.appendChild(group);
944
+ }
945
+ else {
946
+ const title = extractLabel(node), subtitle = extractSubtitle(node), icon = extractIcon(node);
947
+ const mount = document.createElement('div');
948
+ render(html `<menu-view tabindex="0">
949
+ <h-stack slot="label" distribution="leading" template="auto spacer">
950
+ <label-view data-role="check" style="visibility: hidden">
951
+ <image-view slot="icon" system-name="check"></image-view>
952
+ </label-view>
953
+ <h-stack distribution="fill" template="spacer auto">
954
+ ${icon ? html `<label-view system-image="${icon}"></label-view>` : null}
955
+ <v-stack spacing="3" alignment="fill">
956
+ <label-view title="${title}"></label-view>
957
+ ${subtitle ? html `<label-view foreground="secondary" font="callout" title="${subtitle}"></label-view>` : null}
958
+ </v-stack>
959
+ </h-stack>
960
+ </h-stack>
961
+ </menu-view>`, mount);
962
+ const group = mount.firstElementChild;
963
+ // const group = $(`<menu-view tabindex="0"></menu-view>`, '>1')
964
+ // renderLabel(':scope>label-view[slot=label]', `<label-view slot="label"><span></span></label-view>`, group, extractLabel(node), extractIcon(node))
965
+ _a.#reflectButtons(node.children, group);
966
+ container.appendChild(group);
967
+ }
968
+ else
969
+ container.appendChild(_a.#wrapOptionTag(node));
970
+ }
971
+ }
972
+ // #reflectPlaceholder(value: string | null) {
973
+ // if (devFlags.debug) console.debug(`#reflectPlaceholder`)
974
+ // const input = this.#shadowRoot.querySelector('input')
975
+ // if (input) {
976
+ // if (value) input.setAttribute((this.constructor as typeof PickerView).ATTR.PLACEHOLDER, value)
977
+ // else input.removeAttribute((this.constructor as typeof PickerView).ATTR.PLACEHOLDER)
978
+ // }
979
+ // }
980
+ #reflectLabel(value) {
981
+ if (devFlags.debug)
982
+ console.debug(`${_a.name} #reflectLabel`);
983
+ renderLabel(':scope>label-view[slot=label]', `<label-view slot="label"><span></span></label-view>`, this, value);
984
+ this.#renderSlotted([]);
985
+ }
986
+ #reflectSelectionOnButtons() {
987
+ if (devFlags.debug)
988
+ console.debug(`${_a.name} #reflectSelectionOnButtons`);
989
+ self.requestAnimationFrame(() => {
990
+ this.ariaCurrent = this.#selection;
991
+ const groupMap = indexGroups(this.#lastIndexedRoot);
992
+ const groupContainsSelection = (source) => source instanceof Element ? [...source.querySelectorAll('option')].some((opt) => extractTag(opt) === this.#selection) : collectLeafValues(source).includes(this.#selection);
993
+ const syncButtons = (root) => {
994
+ // 1. plain value buttons — unchanged
995
+ for (const el of root.querySelectorAll('button[value]:not([slot])'))
996
+ // $.prop('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden', el.querySelector<HTMLElement>('label-view[data-role="check"]'))
997
+ el.querySelector('label-view[data-role="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden');
998
+ // 2. details/optgroups — unchanged
999
+ for (const details of root.querySelectorAll('details[is="disclosure-group"]')) {
1000
+ const hasSelectedDescendant = [...details.querySelectorAll('button[value]')].some((btn) => btn.getAttribute('value') === this.#selection);
1001
+ details.querySelector(':scope>summary label-view[data-role="check"]')?.style.setProperty('visibility', hasSelectedDescendant ? 'visible' : 'hidden');
1002
+ }
1003
+ // 3. nav-link buttons — resolved by groupId, same map used for resync
1004
+ for (const btn of root.querySelectorAll('button[navigation-link]:not([value])')) {
1005
+ const source = btn.dataset.groupId ? groupMap.get(btn.dataset.groupId) : undefined;
1006
+ const hasSelectedDescendant = source ? groupContainsSelection(source) : false;
1007
+ btn.querySelector('label-view[data-role="check"]')?.style.setProperty('visibility', hasSelectedDescendant ? 'visible' : 'hidden');
1008
+ }
1009
+ };
1010
+ syncButtons(this);
1011
+ if (this.#spawn)
1012
+ syncButtons(this.#spawn);
1013
+ });
1014
+ }
1015
+ /**
1016
+ * Overwrite cvlabel with the label prop of the current(find[value === #selection]) option/dictentry
1017
+ */
1018
+ #reflectSelectionOnCurrentValueLabel() {
1019
+ if (devFlags.debug)
1020
+ console.debug(`${_a.name} #reflectSelectionOnCurrentValueLabel`);
1021
+ self.requestAnimationFrame(() => {
1022
+ switch (this.pickerStyle) {
1023
+ case 'sheet':
1024
+ case 'navigation-link': {
1025
+ const currentValueLabel = this.querySelector(':scope>label-view:not([slot])');
1026
+ if (!currentValueLabel)
1027
+ break;
1028
+ // if (!cvl) currentValueLabel.setAttribute('foreground', 'secondary')
1029
+ // else currentValueLabel.removeAttribute('foreground')
1030
+ renderLabelTitle(currentValueLabel, this.#currentValueLabel || this.#selection || this.getAttribute(this.constructor.ATTR.PLACEHOLDER));
1031
+ renderLabelIcon(currentValueLabel, this.#currentValueIcon || this.getAttribute(this.constructor.ATTR.PLACEHOLDER_ICON));
1032
+ break;
1033
+ }
1034
+ case 'menu': {
1035
+ const currentValueLabel = this.querySelector(':scope>menu-view:not([slot])>label-view[slot=label]');
1036
+ if (!currentValueLabel)
1037
+ break;
1038
+ // if (!cvl) currentValueLabel.setAttribute('foreground', 'secondary')
1039
+ // else currentValueLabel.removeAttribute('foreground')
1040
+ renderLabelTitle(currentValueLabel, this.#currentValueLabel || this.#selection || this.getAttribute(this.constructor.ATTR.PLACEHOLDER));
1041
+ renderLabelIcon(currentValueLabel, this.#currentValueIcon || this.getAttribute(this.constructor.ATTR.PLACEHOLDER_ICON));
1042
+ break;
1043
+ }
1044
+ case 'inline':
1045
+ default: {
1046
+ //
1047
+ break;
1048
+ }
1049
+ }
1050
+ });
1051
+ }
1052
+ #reflectTriggerHelp() {
1053
+ if (devFlags.debug)
1054
+ console.debug(`${_a.name} #reflectTriggerHelp`);
1055
+ let trigger;
1056
+ switch (this.pickerStyle) {
1057
+ case 'sheet':
1058
+ case 'navigation-link': {
1059
+ trigger = this.querySelector(':scope>label-view:not([slot])') ?? undefined;
1060
+ }
1061
+ case 'menu': {
1062
+ trigger = this.querySelector(':scope>menu-view:not([slot])>label-view[slot=label]') ?? undefined;
1063
+ }
1064
+ case 'inline':
1065
+ default: {
1066
+ //udnefined
1067
+ }
1068
+ }
1069
+ if (this.hasAttribute(this.constructor.ATTR.TRIGGER_HELP))
1070
+ trigger?.setAttribute(this.constructor.ATTR.TRIGGER_HELP, this.getAttribute(this.constructor.ATTR.TRIGGER_HELP) ?? '');
1071
+ else
1072
+ trigger?.removeAttribute(this.constructor.ATTR.TRIGGER_HELP);
1073
+ }
1074
+ // Optional: form participation properties
1075
+ get name() {
1076
+ return this.getAttribute('name') ?? this.getAttribute('label') ?? this.querySelector(':scope>[slot=label]')?.textContent ?? '';
1077
+ }
1078
+ setValidity = (flags, message, anchor) => {
1079
+ // let msg
1080
+ // if (message)
1081
+ for (const k in flags) {
1082
+ const key = k; // ✅ type-safe cast
1083
+ if (true !== flags[key])
1084
+ continue;
1085
+ for (const el of this.#slots?.get('validity-options')?.assignedElements({ flatten: true }) ?? []) {
1086
+ if (!el.matches('option'))
1087
+ continue;
1088
+ const { label, value } = el;
1089
+ if (`${kebabCase(key)}` === value) {
1090
+ message = label;
1091
+ break;
1092
+ }
1093
+ else if (`${kebabCase(key)}:${message}` === value) {
1094
+ message = label;
1095
+ break;
1096
+ }
1097
+ }
1098
+ }
1099
+ if (!message)
1100
+ for (const k in flags) {
1101
+ const key = k; // ✅ type-safe cast
1102
+ if (true !== flags[key])
1103
+ continue;
1104
+ message = kebabCase(key);
1105
+ break;
1106
+ }
1107
+ if (devFlags.debug)
1108
+ console.debug(`${_a.name} ⚡️ validity-change`);
1109
+ return this.#internals.setValidity(flags, this.#customValidity || message, anchor);
1110
+ };
1111
+ setCustomValidity = (message) => {
1112
+ this.#customValidity = message;
1113
+ if (this.#customValidity)
1114
+ this.#internals.setValidity({ ...this.#internals.validity, customError: true }, message);
1115
+ else
1116
+ this.#sendValueToForm(false);
1117
+ };
1118
+ formAssociatedCallback = (form) => {
1119
+ this.#sendValueToForm();
1120
+ };
1121
+ formDisabledCallback = (disabled) => {
1122
+ for (const btn of this.#shadowRoot.querySelectorAll('button'))
1123
+ btn.toggleAttribute('disabled', !disabled);
1124
+ };
1125
+ formResetCallback = () => {
1126
+ this.#selection = '';
1127
+ this.#reflectSelectionOnButtons();
1128
+ this.#reflectSelectionOnCurrentValueLabel();
1129
+ this.#sendValueToForm();
1130
+ };
1131
+ }
1132
+ _a = PickerView;