@swiftwc/ui 0.0.0-dev.5 → 0.0.0-dev.50

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 (328) 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 +22 -4
  11. package/generated/client/index.js +219 -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/confirmation-dialog.d.ts +9 -0
  22. package/generated/components/confirmation-dialog.js +113 -0
  23. package/generated/components/content-unavailable-view.d.ts +24 -0
  24. package/generated/components/content-unavailable-view.js +81 -0
  25. package/generated/components/date-picker.d.ts +36 -0
  26. package/generated/components/date-picker.js +461 -0
  27. package/generated/components/disclosure-group.d.ts +9 -7
  28. package/generated/components/disclosure-group.js +51 -65
  29. package/generated/components/fine-tooltip.d.ts +7 -0
  30. package/generated/components/fine-tooltip.js +107 -0
  31. package/generated/components/form-view.d.ts +9 -0
  32. package/generated/components/form-view.js +63 -0
  33. package/generated/components/glass-button.d.ts +16 -0
  34. package/generated/components/glass-button.js +53 -0
  35. package/generated/components/glass-prominent-button.d.ts +16 -0
  36. package/generated/components/glass-prominent-button.js +53 -0
  37. package/generated/components/image-view.d.ts +9 -0
  38. package/generated/components/image-view.js +46 -0
  39. package/generated/components/index.d.ts +39 -10
  40. package/generated/components/index.js +39 -15
  41. package/generated/components/label-view.d.ts +9 -0
  42. package/generated/components/label-view.js +51 -0
  43. package/generated/components/list-view.d.ts +8 -0
  44. package/generated/components/list-view.js +32 -0
  45. package/generated/components/menu-view.d.ts +12 -0
  46. package/generated/components/menu-view.js +142 -0
  47. package/generated/components/navigation-large-title.d.ts +6 -0
  48. package/generated/components/navigation-large-title.js +181 -0
  49. package/generated/components/navigation-link.d.ts +10 -0
  50. package/generated/components/navigation-link.js +25 -0
  51. package/generated/components/navigation-split-view.d.ts +7 -2
  52. package/generated/components/navigation-split-view.js +19 -3
  53. package/generated/components/navigation-stack.d.ts +6 -2
  54. package/generated/components/navigation-stack.js +23 -59
  55. package/generated/components/navigation-title.d.ts +8 -0
  56. package/generated/components/navigation-title.js +50 -0
  57. package/generated/components/picker-view.d.ts +31 -0
  58. package/generated/components/picker-view.js +764 -0
  59. package/generated/components/plain-button.d.ts +7 -0
  60. package/generated/components/plain-button.js +22 -0
  61. package/generated/components/progress-view.d.ts +13 -0
  62. package/generated/components/progress-view.js +120 -0
  63. package/generated/components/screen-view.d.ts +1 -1
  64. package/generated/components/screen-view.js +4 -2
  65. package/generated/components/scroll-view-proxy.d.ts +8 -0
  66. package/generated/components/scroll-view-proxy.js +17 -0
  67. package/generated/components/scroll-view.d.ts +4 -2
  68. package/generated/components/scroll-view.js +179 -64
  69. package/generated/components/search-view.d.ts +7 -0
  70. package/generated/components/search-view.js +30 -0
  71. package/generated/components/section-view.d.ts +9 -0
  72. package/generated/components/section-view.js +85 -0
  73. package/generated/components/sheet-view.d.ts +3 -4
  74. package/generated/components/sheet-view.js +53 -17
  75. package/generated/components/sidebar-toggle.d.ts +1 -1
  76. package/generated/components/sidebar-toggle.js +76 -72
  77. package/generated/components/sidebar-view.d.ts +2 -4
  78. package/generated/components/sidebar-view.js +25 -17
  79. package/generated/components/sticky-container.d.ts +6 -0
  80. package/generated/components/sticky-container.js +26 -0
  81. package/generated/components/tab-bar.d.ts +5 -4
  82. package/generated/components/tab-bar.js +34 -17
  83. package/generated/components/tab-item.d.ts +4 -3
  84. package/generated/components/tab-item.js +125 -29
  85. package/generated/components/tab-view.d.ts +17 -2
  86. package/generated/components/tab-view.js +252 -17
  87. package/generated/components/table-view.d.ts +10 -0
  88. package/generated/components/table-view.js +177 -0
  89. package/generated/components/text-field.d.ts +29 -0
  90. package/generated/components/text-field.js +496 -0
  91. package/generated/components/tool-bar-item-group.d.ts +5 -0
  92. package/generated/components/tool-bar-item-group.js +28 -0
  93. package/generated/components/tool-bar-item.d.ts +8 -0
  94. package/generated/components/tool-bar-item.js +66 -0
  95. package/generated/components/tool-bar.d.ts +19 -0
  96. package/generated/components/tool-bar.js +93 -0
  97. package/generated/components/v-keyboard.d.ts +4 -0
  98. package/generated/components/v-keyboard.js +72 -67
  99. package/generated/components/v-stack.d.ts +10 -0
  100. package/generated/components/v-stack.js +19 -0
  101. package/generated/css/index.css +8475 -0
  102. package/generated/events/alert-dialog.d.ts +8 -0
  103. package/generated/events/confirmation-dialog.d.ts +9 -0
  104. package/generated/events/confirmation-dialog.js +1 -0
  105. package/generated/events/date-picker.d.ts +8 -0
  106. package/generated/events/date-picker.js +1 -0
  107. package/generated/events/index.d.ts +8 -0
  108. package/generated/events/index.js +8 -0
  109. package/generated/events/lifecycle-observer.d.ts +10 -0
  110. package/generated/events/lifecycle-observer.js +1 -0
  111. package/generated/events/picker-view.d.ts +15 -0
  112. package/generated/events/picker-view.js +1 -0
  113. package/generated/events/scroll-view.d.ts +10 -0
  114. package/generated/events/scroll-view.js +1 -0
  115. package/generated/events/tab-view.d.ts +28 -0
  116. package/generated/events/tab-view.js +1 -0
  117. package/generated/events/text-field.d.ts +8 -0
  118. package/generated/events/text-field.js +1 -0
  119. package/generated/i18n/index.d.ts +34 -0
  120. package/generated/i18n/index.js +167 -0
  121. package/generated/i18n/locales/el.d.ts +16 -0
  122. package/generated/i18n/locales/el.js +16 -0
  123. package/generated/i18n/locales/en.d.ts +16 -0
  124. package/generated/i18n/locales/en.js +16 -0
  125. package/generated/i18n/locales/index.d.ts +4 -0
  126. package/generated/i18n/locales/index.js +4 -0
  127. package/generated/internal/class/cleanup-registry.d.ts +5 -0
  128. package/generated/internal/class/cleanup-registry.js +38 -0
  129. package/generated/internal/class/css-style-observer.d.ts +8 -0
  130. package/generated/internal/class/css-style-observer.js +26 -0
  131. package/generated/internal/class/form-associated-base.d.ts +16 -0
  132. package/generated/internal/class/form-associated-base.js +78 -0
  133. package/generated/internal/class/mutation-observer-set.d.ts +9 -0
  134. package/generated/internal/class/mutation-observer-set.js +42 -0
  135. package/generated/internal/class/mutation-observer-singleton.d.ts +10 -0
  136. package/generated/internal/class/mutation-observer-singleton.js +64 -0
  137. package/generated/internal/class/navigation-path.d.ts +15 -0
  138. package/generated/internal/class/navigation-path.js +84 -0
  139. package/generated/internal/class/navigation-view.d.ts +8 -0
  140. package/generated/internal/class/navigation-view.js +72 -0
  141. package/generated/internal/class/resize-observer-singleton.d.ts +7 -0
  142. package/generated/internal/class/resize-observer-singleton.js +22 -0
  143. package/generated/internal/decorators/adaptive-slot.d.ts +1 -0
  144. package/generated/internal/decorators/adaptive-slot.js +50 -0
  145. package/generated/internal/decorators/custom-element.d.ts +5 -0
  146. package/generated/internal/decorators/custom-element.js +5 -0
  147. package/generated/internal/decorators/index.d.ts +6 -0
  148. package/generated/internal/decorators/index.js +6 -0
  149. package/generated/internal/decorators/microtask-on-connected.d.ts +39 -0
  150. package/generated/internal/decorators/microtask-on-connected.js +67 -0
  151. package/generated/internal/privateNamespace.d.ts +14 -0
  152. package/generated/internal/privateNamespace.js +168 -0
  153. package/generated/internal/utils/add.d.ts +1 -0
  154. package/generated/internal/utils/add.js +7 -0
  155. package/generated/internal/utils/cash/ancestors.d.ts +1 -0
  156. package/generated/internal/utils/cash/ancestors.js +27 -0
  157. package/generated/internal/utils/cash/index.d.ts +23 -0
  158. package/generated/internal/utils/cash/index.js +25 -0
  159. package/generated/internal/utils/cash/next-all.d.ts +1 -0
  160. package/generated/internal/utils/cash/next-all.js +10 -0
  161. package/generated/internal/utils/cash/next.d.ts +1 -0
  162. package/generated/internal/utils/cash/next.js +9 -0
  163. package/generated/internal/utils/cash/prev-all.d.ts +1 -0
  164. package/generated/internal/utils/cash/prev-all.js +10 -0
  165. package/generated/internal/utils/cash/prev.d.ts +1 -0
  166. package/generated/internal/utils/cash/prev.js +9 -0
  167. package/generated/internal/utils/cash/prop.d.ts +18 -0
  168. package/generated/internal/utils/cash/prop.js +30 -0
  169. package/generated/internal/utils/cash/siblings.d.ts +1 -0
  170. package/generated/internal/utils/cash/siblings.js +10 -0
  171. package/generated/internal/utils/clamp.d.ts +1 -0
  172. package/generated/internal/utils/clamp.js +4 -0
  173. package/generated/internal/utils/compare-big-decimals.d.ts +1 -0
  174. package/generated/internal/utils/compare-big-decimals.js +90 -0
  175. package/generated/internal/utils/debounce.d.ts +1 -0
  176. package/generated/internal/utils/debounce.js +13 -0
  177. package/generated/internal/utils/dev-flags.d.ts +3 -0
  178. package/generated/internal/utils/dev-flags.js +5 -0
  179. package/generated/internal/utils/ensure-placeholder.d.ts +1 -0
  180. package/generated/internal/utils/ensure-placeholder.js +55 -0
  181. package/generated/internal/utils/frame.d.ts +1 -0
  182. package/generated/internal/utils/frame.js +6 -0
  183. package/generated/internal/utils/index.d.ts +21 -1
  184. package/generated/internal/utils/index.js +24 -1
  185. package/generated/internal/utils/is-inside.d.ts +1 -0
  186. package/generated/internal/utils/is-inside.js +4 -0
  187. package/generated/internal/utils/kebab-case.d.ts +1 -1
  188. package/generated/internal/utils/list-active.d.ts +7 -0
  189. package/generated/internal/utils/list-active.js +83 -0
  190. package/generated/internal/utils/microtask.d.ts +1 -0
  191. package/generated/internal/utils/microtask.js +3 -0
  192. package/generated/internal/utils/onoff.d.ts +45 -0
  193. package/generated/internal/utils/onoff.js +31 -0
  194. package/generated/internal/utils/render-label-icon.d.ts +2 -0
  195. package/generated/internal/utils/render-label-icon.js +19 -0
  196. package/generated/internal/utils/render-label-title.d.ts +2 -0
  197. package/generated/internal/utils/render-label-title.js +16 -0
  198. package/generated/internal/utils/render-label.d.ts +1 -0
  199. package/generated/internal/utils/render-label.js +19 -0
  200. package/generated/internal/utils/set.d.ts +1 -0
  201. package/generated/internal/utils/set.js +7 -0
  202. package/generated/internal/utils/sleep.d.ts +1 -0
  203. package/generated/internal/utils/sleep.js +3 -0
  204. package/generated/internal/utils/slow-hide-show.d.ts +1 -0
  205. package/generated/internal/utils/slow-hide-show.js +16 -0
  206. package/generated/internal/utils/timeout.d.ts +5 -0
  207. package/generated/internal/utils/timeout.js +16 -0
  208. package/generated/internal/utils/touch-glass.d.ts +7 -0
  209. package/generated/internal/utils/touch-glass.js +19 -0
  210. package/generated/namespace-browser/base.d.ts +45 -0
  211. package/generated/namespace-browser/base.js +115 -0
  212. package/generated/namespace-browser/index.d.ts +7 -0
  213. package/generated/namespace-browser/index.js +1 -0
  214. package/generated/snapshot/index.d.ts +4 -11
  215. package/generated/snapshot/index.js +55 -145
  216. package/package.json +33 -9
  217. package/scss/_components.scss +60 -13
  218. package/scss/_final.scss +1 -0
  219. package/scss/_functions.scss +10 -0
  220. package/scss/_mixins.scss +4 -169
  221. package/scss/_placeholders.scss +3 -0
  222. package/scss/_transitions.scss +22 -1
  223. package/scss/_utils.scss +9 -0
  224. package/scss/_vars.scss +143 -23
  225. package/scss/base/_layout.scss +40 -16
  226. package/scss/base/_reboot.scss +151 -15
  227. package/scss/base/_root.scss +348 -25
  228. package/scss/colors/_index.scss +788 -70
  229. package/scss/components/_alert-dialog.scss +111 -0
  230. package/scss/components/_body-view.scss +12 -3
  231. package/scss/components/_bordered-button.scss +57 -0
  232. package/scss/components/_bordered-prominent-button.scss +64 -0
  233. package/scss/components/_borderless-button.scss +36 -6
  234. package/scss/components/_confirmation-dialog.scss +102 -0
  235. package/scss/components/_content-unavailable-view.scss +54 -0
  236. package/scss/components/_date-picker.scss +30 -0
  237. package/scss/components/_detail-placeholder.scss +153 -0
  238. package/scss/components/_disclosure-group.scss +143 -48
  239. package/scss/components/_fine-tooltip.scss +84 -0
  240. package/scss/components/_form-view.scss +233 -0
  241. package/scss/components/_full-screen.scss +7 -5
  242. package/scss/components/_glass-button.scss +59 -0
  243. package/scss/components/_glass-prominent-button.scss +70 -0
  244. package/scss/components/_h-divider.scss +13 -0
  245. package/scss/components/_h-flex-stack.scss +23 -0
  246. package/scss/components/_h-stack.scss +29 -0
  247. package/scss/components/_image-view.scss +9 -0
  248. package/scss/components/_index.scss +235 -7
  249. package/scss/components/_label-view copy.scss.txt +420 -0
  250. package/scss/components/_label-view.scss +367 -0
  251. package/scss/components/_label-view.scss.txt +536 -0
  252. package/scss/components/_labeled-content.scss +9 -0
  253. package/scss/components/_list-view.scss +269 -0
  254. package/scss/components/_menu-view.scss +215 -0
  255. package/scss/components/_navigation-large-title.scss +27 -0
  256. package/scss/components/_navigation-split-view.scss +329 -269
  257. package/scss/components/_navigation-stack.scss +6 -6
  258. package/scss/components/_navigation-title.scss +10 -0
  259. package/scss/components/_picker-view.scss +131 -0
  260. package/scss/components/_plain-button.scss +18 -0
  261. package/scss/components/_progress-view.scss +98 -0
  262. package/scss/components/_scroll-view-proxy.scss +79 -0
  263. package/scss/components/_scroll-view-toolbars.scss +126 -0
  264. package/scss/components/_scroll-view.scss +114 -66
  265. package/scss/components/_search-view.scss +22 -0
  266. package/scss/components/_section-view.scss +119 -0
  267. package/scss/components/_sheet-view.scss +78 -35
  268. package/scss/components/_sidebar-toggle.scss +87 -27
  269. package/scss/components/_sidebar-view.scss +30 -40
  270. package/scss/components/_sticky-container.scss +42 -0
  271. package/scss/components/_tab-bar.scss +207 -84
  272. package/scss/components/_tab-item.scss +25 -0
  273. package/scss/components/_tab-view.scss +270 -126
  274. package/scss/components/_table-column.scss +52 -0
  275. package/scss/components/_table-row.scss +173 -0
  276. package/scss/components/_table-view.scss +223 -0
  277. package/scss/components/_text-field.scss +37 -0
  278. package/scss/components/_tool-bar-item-group.scss +30 -19
  279. package/scss/components/_tool-bar-item.scss +81 -71
  280. package/scss/components/_tool-bar.scss +126 -63
  281. package/scss/components/_v-divider.scss +13 -0
  282. package/scss/components/_v-flex-stack.scss +29 -0
  283. package/scss/components/_v-keyboard.scss +6 -4
  284. package/scss/components/_v-stack.scss +15 -9
  285. package/scss/final/_touch-glass.scss +91 -0
  286. package/scss/index.scss +6 -2
  287. package/scss/mixins/_button.scss +44 -0
  288. package/scss/mixins/_color.scss +16 -0
  289. package/scss/mixins/_dialog.scss +89 -0
  290. package/scss/mixins/_index.scss +453 -0
  291. package/scss/placeholders/_buttons.scss +117 -0
  292. package/scss/placeholders/_lists.scss +594 -0
  293. package/scss/transitions/_alert-dialog.scss +18 -0
  294. package/scss/transitions/_bwd.navbar.scss +3 -13
  295. package/scss/transitions/_bwd.scss +4 -20
  296. package/scss/transitions/_confirmation-dialog.scss +27 -0
  297. package/scss/transitions/_disclosure-group.scss +35 -0
  298. package/scss/transitions/_fine-tooltip.scss +21 -0
  299. package/scss/transitions/_fwd.navbar.scss +4 -9
  300. package/scss/transitions/_fwd.scss +4 -34
  301. package/scss/transitions/_glass-button.scss +12 -0
  302. package/scss/transitions/_glass-prominent-button.scss +12 -0
  303. package/scss/transitions/_index.scss +35 -143
  304. package/scss/transitions/_menu-view.scss +26 -0
  305. package/scss/transitions/_navigation-split-view.scss +23 -0
  306. package/scss/transitions/_progress-view.scss +23 -0
  307. package/scss/transitions/_sheet-view.scss +30 -0
  308. package/scss/transitions/_sidebar-view.scss +36 -0
  309. package/scss/transitions/_tab-bar.scss +20 -0
  310. package/scss/utils/_frame.scss +91 -0
  311. package/scss/utils/_index.scss +78 -0
  312. package/scss/utils/_scroll-view.scss +14 -0
  313. package/scss/utils/_spacing.scss +25 -0
  314. package/scss/utils/_stacks.scss +209 -0
  315. package/web-components.html-data/en.json +576 -0
  316. package/generated/components/bottom-bar.d.ts +0 -10
  317. package/generated/components/bottom-bar.js +0 -73
  318. package/generated/components/navigation-bar.d.ts +0 -10
  319. package/generated/components/navigation-bar.js +0 -80
  320. package/generated/internal/class.d.ts +0 -18
  321. package/generated/internal/class.js +0 -18
  322. package/generated/namespace/index.d.ts +0 -6
  323. package/generated/styles.css +0 -1305
  324. package/scss/_dev.scss +0 -68
  325. package/scss/components/_tab-bar-stack.scss +0 -101
  326. package/scss/components/_toolbars.scss +0 -100
  327. package/scss/transitions/_dialog.scss +0 -32
  328. /package/generated/{namespace/index.js → events/alert-dialog.js} +0 -0
@@ -0,0 +1,764 @@
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
+ const pickerStyles = ['menu', 'inline', 'navigation-link', 'sheet', 'automatic'];
10
+ const update = (path, node, overwrite = true) => {
11
+ if (devFlags.debug)
12
+ console.debug(`PickerView: update`);
13
+ if (!(path instanceof NavigationPath))
14
+ throw new Error('invalid view');
15
+ const { component, page } = path;
16
+ if (!component)
17
+ return;
18
+ const position = queryInsertPosition(component); //'afterend'
19
+ // const lookFor = 'beforebegin' === position ? 'previousElementSibling' : 'nextElementSibling'
20
+ if (page)
21
+ page.insertAdjacentElement(position, node);
22
+ };
23
+ const reflectSpawnedElement = (current, source) => {
24
+ if (devFlags.debug)
25
+ console.debug(`PickerView: reflectSpawnedElement`);
26
+ const { page: oldSv, toolBarConfig: oldToolbar } = new NavigationPath(current).hydrate(), { page: newSv, toolBarConfig: newToolbar } = new NavigationPath(source).hydrate();
27
+ const oldList = oldSv?.querySelector('list-view'), newList = newSv?.querySelector('list-view'), oldBackBtn = oldToolbar?.at(0)?.querySelector('button'), newBackBtn = newToolbar?.at(0)?.querySelector('button');
28
+ // pre replace list (always exists)
29
+ const oldOpenedDetails = [...(oldList?.querySelectorAll('list-view details[open]>summary') ?? [])].map((item) => item.textContent.trim());
30
+ // replace list (always exists)
31
+ if (oldList && newList)
32
+ oldList.replaceWith(newList);
33
+ // post replace list (always exists)
34
+ for (const label of oldOpenedDetails)
35
+ for (const summary of newList?.querySelectorAll('list-view details>summary') ?? [])
36
+ if (summary.textContent.trim() === label) {
37
+ summary.parentElement?.setAttribute('open', 'open');
38
+ break;
39
+ }
40
+ // replace backbtns (always exists)
41
+ if (oldBackBtn && newBackBtn)
42
+ oldBackBtn.replaceWith(newBackBtn);
43
+ // overwrite navtitle (always exists)
44
+ oldSv?.setAttribute('navigation-inline-title', newSv?.getAttribute('navigation-inline-title') ?? '');
45
+ // replace search (if exists)
46
+ oldList?.style.setProperty('--list--sticky-block-size', newList?.style.getPropertyValue('--list--sticky-block-size') ?? '');
47
+ // replace search (if exists)
48
+ const oldSearch = oldSv?.querySelector('sticky-container'), newSearch = newSv?.querySelector('sticky-container');
49
+ // if already exists, DO NOTHING dont mess up the input element
50
+ if (newSearch) {
51
+ if (!oldSearch)
52
+ oldSv?.insertAdjacentHTML('afterend', `<sticky-container edge="navbar" style="order: -1" padding>
53
+ <v-stack spacing="0" alignment="fill">
54
+ <input is="search-view" placeholder="Search">
55
+ <button type="button">Filters</button>
56
+ </v-stack>
57
+ </sticky-container>`);
58
+ }
59
+ else
60
+ oldSearch?.remove();
61
+ };
62
+ const extractTagFromOption = (node) => {
63
+ if (devFlags.debug)
64
+ console.debug(`PickerView: extractTagFromOption`);
65
+ return ((node.getAttribute('value') ?? node.textContent?.trim()) || node.getAttribute('label')) ?? '';
66
+ };
67
+ const extractCurrentValueFromOption = (node) => {
68
+ if (devFlags.debug)
69
+ console.debug(`PickerView: extractCurrentValueFromOption`);
70
+ return (node.getAttribute('label') ?? node.getAttribute('value') ?? node.textContent?.trim()) || '';
71
+ };
72
+ export class PickerView extends FormAssociatedBase {
73
+ static get ATTR() {
74
+ return {
75
+ PLACEHOLDER: 'placeholder',
76
+ LABEL: 'label',
77
+ PICKER_STYLE: 'picker-style',
78
+ SELECTION: 'selection',
79
+ SEARCHABLE: 'searchable',
80
+ CURRENT_VALUE_LABEL: 'current-value-label',
81
+ TRIGGER_HELP: 'help',
82
+ };
83
+ }
84
+ static get observedAttributes() {
85
+ return Object.values(this.ATTR);
86
+ }
87
+ static #templates = new Map();
88
+ #spawn;
89
+ #spawnPage = (tag, searchable = false, title, node) => {
90
+ if (devFlags.debug)
91
+ console.debug(`${_a.name} #spawnPage`);
92
+ 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="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');
93
+ if (title)
94
+ sv?.setAttribute('navigation-inline-title', title);
95
+ if (searchable) {
96
+ list?.style.setProperty('--list--sticky-block-size', '70px');
97
+ list?.insertAdjacentHTML('afterend', `<sticky-container edge="navbar" style="order: -1" padding>
98
+ <v-stack spacing="0" alignment="fill">
99
+ <input is="search-view" placeholder="Search">
100
+ <button type="button">Filters</button>
101
+ </v-stack>
102
+ </sticky-container>`);
103
+ const searchInput = sv?.querySelector('input'); // FIXME: compoennt is=search-view??
104
+ searchInput?.addEventListener('focus', ({ target }) => {
105
+ if (this.#spawn)
106
+ this.dispatchEvent(new CustomEvent('picker:searchfocus', {
107
+ detail: { element: this.#spawn, search: target instanceof HTMLInputElement ? target.value : '' },
108
+ bubbles: true,
109
+ composed: true,
110
+ }));
111
+ });
112
+ searchInput?.addEventListener('blur', ({ target }) => {
113
+ if (this.#spawn)
114
+ this.dispatchEvent(new CustomEvent('picker:searchblur', {
115
+ detail: { element: this.#spawn, search: target instanceof HTMLInputElement ? target.value : '' },
116
+ bubbles: true,
117
+ composed: true,
118
+ }));
119
+ });
120
+ searchInput?.addEventListener('input', ({ target }) => {
121
+ if (this.#spawn)
122
+ this.dispatchEvent(new CustomEvent('picker:searchinput', {
123
+ detail: { element: this.#spawn, search: target instanceof HTMLInputElement ? target.value : '' },
124
+ bubbles: true,
125
+ composed: true,
126
+ }));
127
+ });
128
+ }
129
+ backBtn?.setAttribute('help', I18n.t('ButtonRole').Back);
130
+ backBtn?.addEventListener('click', async (evt) => {
131
+ evt.stopImmediatePropagation();
132
+ evt.preventDefault();
133
+ const { target } = evt;
134
+ if (!(target instanceof HTMLElement))
135
+ return;
136
+ const { component } = new NavigationPath(target)?.hydrate();
137
+ await startViewTransition(target, 'backwards', async () => {
138
+ component?.remove();
139
+ });
140
+ });
141
+ const elements = node ? Array.from(node.children) : (this.#slots?.get('list')?.assignedElements() ?? []);
142
+ for (const el of elements) {
143
+ if (!(el instanceof HTMLElement))
144
+ continue;
145
+ switch (el.tagName) {
146
+ case 'OPTGROUP': {
147
+ const details = _a.#wrapOptgroupTag(el);
148
+ _a.#reflectButtons([...el.children], details);
149
+ for (const btn of details.querySelectorAll(':scope>button'))
150
+ btn.addEventListener('click', this.#handlePageBtnClick);
151
+ list?.appendChild(details);
152
+ break;
153
+ }
154
+ case 'OPTION': {
155
+ const btn = _a.#wrapOptionTag(el);
156
+ btn.addEventListener('click', this.#handlePageBtnClick);
157
+ list?.appendChild(btn);
158
+ break;
159
+ }
160
+ default: {
161
+ const btn = $(`<button type="button" tabindex="0" is="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>`, '>1'), label = btn.querySelector(':scope>h-stack>label-view:nth-child(2)');
162
+ if (label && el.dataset.label)
163
+ renderLabelTitle(label, el.dataset.label); //label?.setAttribute('title', el.dataset.label)
164
+ btn.addEventListener('click', async (evt) => {
165
+ evt.stopImmediatePropagation();
166
+ evt.preventDefault();
167
+ const { target } = evt;
168
+ if (!(target instanceof HTMLElement))
169
+ return;
170
+ const newPage = this.#spawnPage('body-view', this.hasAttribute('searchable'), el.dataset.label, el);
171
+ if (!newPage)
172
+ return;
173
+ const path = new NavigationPath(target)?.hydrate();
174
+ await startViewTransition(target, 'forwards', async () => {
175
+ update(path, newPage);
176
+ this.#reflectSelectionOnButtons();
177
+ });
178
+ });
179
+ list?.appendChild(btn);
180
+ break;
181
+ }
182
+ }
183
+ }
184
+ return body;
185
+ };
186
+ #renderSlotted = (entries) => {
187
+ if (devFlags.debug)
188
+ console.debug(`${_a.name} ⚡️ mutation`);
189
+ switch (this.pickerStyle) {
190
+ case 'sheet':
191
+ case 'navigation-link': {
192
+ // current value label only
193
+ const currentValueLabel = this.querySelector(':scope>label-view:not([slot])') ?? this.appendChild($(`<label-view></label-view>`, '>1'));
194
+ // reset state
195
+ if (currentValueLabel) {
196
+ renderLabelIcon(currentValueLabel, 'dots-three'); // overwritten
197
+ renderLabelTitle(currentValueLabel, this.#currentValueLabel); // overwritten
198
+ }
199
+ // clear all siblings
200
+ for (const el of this.querySelectorAll(':scope>:not([slot])'))
201
+ if (currentValueLabel !== el)
202
+ el.remove();
203
+ CleanupRegistry.unregister(this, 'trigger');
204
+ CleanupRegistry.register(this, onoff('click', this.#handleTriggerClick, currentValueLabel).on(), 'trigger');
205
+ // rebuild snapshot(tree)
206
+ if (!this.#spawn)
207
+ break;
208
+ // rerender level 0
209
+ reflectSpawnedElement(this.#spawn, this.#spawnPage('body-view', this.hasAttribute('searchable'), this.getAttribute('label')));
210
+ for (const el of this.#spawn.querySelectorAll('body-view')) {
211
+ const depth = $.ancestors('body-view,[is=sheet-view]', el).indexOf(this.#spawn);
212
+ if (0 >= depth)
213
+ continue;
214
+ const datalist = this.querySelector(`:scope>${Array.from({ length: depth }, () => 'datalist').join('>')}`);
215
+ if (!datalist) {
216
+ el.remove();
217
+ break;
218
+ }
219
+ reflectSpawnedElement(el, this.#spawnPage('body-view', this.hasAttribute('searchable'), datalist.dataset.label, datalist));
220
+ }
221
+ break;
222
+ }
223
+ case 'menu': {
224
+ const menu = this.querySelector(':scope>menu-view:not([slot])') ?? this.appendChild($(`<menu-view></menu-view>`, '>1'));
225
+ // reset state
226
+ menu.innerHTML = '';
227
+ menu.tabIndex = 0;
228
+ // clear all siblings
229
+ for (const el of this.querySelectorAll(':scope>:not([slot])'))
230
+ if (menu !== el)
231
+ el.remove();
232
+ const currentValueLabel = menu.querySelector(':scope>label-view[slot=label]') ?? menu.appendChild($(`<label-view slot="label"></label-view>`, '>1'));
233
+ if (currentValueLabel) {
234
+ renderLabelIcon(currentValueLabel, 'dots-three'); // overwritten
235
+ renderLabelTitle(currentValueLabel, this.#currentValueLabel); // overwritten
236
+ }
237
+ if (this.hasAttribute('help'))
238
+ currentValueLabel?.setAttribute('help', this.getAttribute('help') ?? '');
239
+ _a.#reflectButtons([...(this.#slots?.get('list')?.assignedElements() ?? [])], menu);
240
+ break;
241
+ }
242
+ case 'inline':
243
+ default: {
244
+ const sectionTpl = `<section-view></section-view>`;
245
+ 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'));
246
+ // reset state
247
+ section.innerHTML = '';
248
+ // clear all siblings
249
+ for (const el of this.querySelectorAll(':scope>:not([slot])'))
250
+ if (inlineList !== el)
251
+ el.remove();
252
+ const value = this.getAttribute(this.constructor.ATTR.LABEL);
253
+ if (value) {
254
+ const label = $(`<label-view><span></span></label-view>`, '>1');
255
+ if (label)
256
+ renderLabelTitle(label, value); //label.setAttribute('title', value)
257
+ section.insertAdjacentElement('beforeend', label);
258
+ }
259
+ _a.#reflectButtons([...(this.#slots?.get('list')?.assignedElements({ flatten: true }) ?? [])].filter((el) => el.matches('option')), section);
260
+ break;
261
+ }
262
+ }
263
+ this.#reflectSelectionOnButtons();
264
+ this.#reflectCurrentValueLabel();
265
+ };
266
+ #renderValidityMsgs = (entries) => {
267
+ if (devFlags.debug)
268
+ console.debug(`${_a.name} ⚡️ mutation`);
269
+ this.setValidity(this.validity, this.validationMessage);
270
+ };
271
+ #lastRenderedStyle; //string | null
272
+ #shadowRoot;
273
+ #slots = new Map();
274
+ #validityObservers = new MutationObserverSet(this.#renderValidityMsgs);
275
+ #observers = new MutationObserverSet(this.#renderSlotted);
276
+ #customValidity = '';
277
+ #selection = '';
278
+ get selection() {
279
+ return this.#selection;
280
+ }
281
+ set selection(v) {
282
+ if (Object.is(this.#selection, v))
283
+ return;
284
+ this.#selection = v;
285
+ this.#reflectSelectionOnButtons();
286
+ this.#reflectCurrentValueLabel();
287
+ this.#sendValueToForm();
288
+ }
289
+ get #internals() {
290
+ return getInternals(this);
291
+ }
292
+ get template() {
293
+ if (!_a.#templates.has(this.pickerStyle))
294
+ switch (this.pickerStyle) {
295
+ case 'sheet':
296
+ case 'navigation-link':
297
+ _a.#templates.set(this.pickerStyle, $(String.raw `
298
+ <label part="root picker-stack">
299
+ <div part="root picker-label-stack">
300
+ <slot name="label"></slot>
301
+ </div>
302
+ <div part="root picker-input-stack">
303
+ <slot></slot>
304
+ </div>
305
+ <slot name="list" hidden></slot>
306
+ <slot name="validity-options" hidden></slot>
307
+ </label>
308
+ `));
309
+ break;
310
+ case 'menu':
311
+ _a.#templates.set(this.pickerStyle, $(String.raw `
312
+ <label part="root picker-stack">
313
+ <div part="root picker-label-stack">
314
+ <slot name="label"></slot>
315
+ </div>
316
+ <div part="root picker-input-stack">
317
+ <slot></slot>
318
+ </div>
319
+ <slot name="list" hidden></slot>
320
+ <slot name="validity-options" hidden></slot>
321
+ </label>
322
+ `));
323
+ // break
324
+ // case 'compact':
325
+ // PickerView.#templates.set(
326
+ // style,
327
+ // Object.assign(document.createElement('template'), {
328
+ // innerHTML: String.raw`
329
+ // <label part="compact-picker">
330
+ // <input type="text" part="compact-input">
331
+ // </label>
332
+ // `,
333
+ // })
334
+ // )
335
+ // break
336
+ // case 'fancy':
337
+ // PickerView.#templates.set(
338
+ // style,
339
+ // Object.assign(document.createElement('template'), {
340
+ // innerHTML: String.raw`
341
+ // <div part="fancy-picker">
342
+ // <span>Fancy Picker</span>
343
+ // <input type="text" part="fancy-input">
344
+ // </div>
345
+ // `,
346
+ // })
347
+ // )
348
+ break;
349
+ // case 'gg':
350
+ // PickerView.#templates.set(
351
+ // style,
352
+ // Object.assign(document.createElement('template'), {
353
+ // innerHTML: String.raw`
354
+ // <label part="root text-field-stack">
355
+ // <div part="root text-field-label-stack">
356
+ // <slot name="label"></slot>
357
+ // </div>
358
+ // <div part="root text-field-input-stack">
359
+ // <input type="text" part="root input text-field-form-input" list="tickmarks">
360
+ // <datalist id="tickmarks">
361
+ // <option value="0" label="0%"></option>
362
+ // </datalist>
363
+ // </div>
364
+ // <slot name="list" hidden></slot>
365
+ // </label>`,
366
+ // })
367
+ // )
368
+ // break
369
+ case 'inline':
370
+ default:
371
+ _a.#templates.set(this.pickerStyle, $(String.raw `
372
+ <label part="root picker-stack">
373
+ <div part="root picker-label-stack">
374
+ <slot name="label"></slot>
375
+ </div>
376
+ <div part="root picker-input-stack">
377
+ <slot></slot>
378
+ </div>
379
+ <slot name="list" hidden></slot>
380
+ <slot name="validity-options" hidden></slot>
381
+ </label>`));
382
+ break;
383
+ }
384
+ return _a.#templates.get(this.pickerStyle);
385
+ }
386
+ constructor() {
387
+ super();
388
+ this.#shadowRoot = this.attachShadow({ mode: 'closed' });
389
+ }
390
+ attributeChangedCallback(name, oldValue, newValue) {
391
+ if (devFlags.debug)
392
+ console.debug(`${_a.name} ⚡️ attr-change [${name}] ("${oldValue}" → "${newValue}")`);
393
+ switch (name) {
394
+ case this.constructor.ATTR.PLACEHOLDER:
395
+ this.#reflectPlaceholder(newValue);
396
+ break;
397
+ case this.constructor.ATTR.LABEL:
398
+ this.#reflectLabel(newValue);
399
+ // this.#sendValueToForm()
400
+ break;
401
+ case this.constructor.ATTR.PICKER_STYLE:
402
+ if (oldValue === newValue)
403
+ break;
404
+ this.#render();
405
+ break;
406
+ case this.constructor.ATTR.SEARCHABLE:
407
+ if (oldValue === newValue)
408
+ break;
409
+ this.#renderSlotted([]);
410
+ break;
411
+ case this.constructor.ATTR.CURRENT_VALUE_LABEL:
412
+ // if (oldValue === newValue) break
413
+ this.#reflectCurrentValueLabel();
414
+ break;
415
+ case this.constructor.ATTR.TRIGGER_HELP:
416
+ // if (oldValue === newValue) break
417
+ this.#reflectTriggerHelp();
418
+ break;
419
+ case this.constructor.ATTR.SELECTION:
420
+ // nothing happens
421
+ break;
422
+ }
423
+ }
424
+ disconnectedCallback() {
425
+ if (devFlags.debug)
426
+ console.debug(`${_a.name} ⚡️ disconnect`);
427
+ CleanupRegistry.unregister(this);
428
+ this.#validityObservers.unobserveAll();
429
+ this.#observers.unobserveAll();
430
+ }
431
+ connectedCallback() {
432
+ if (devFlags.debug)
433
+ console.debug(`${_a.name} ⚡️ connect`);
434
+ CleanupRegistry.register(this, onoff('localechange', () => {
435
+ this.#renderSlotted([]);
436
+ }, I18n.on).on());
437
+ CleanupRegistry.register(this, onoff('click', this.#handleClick, this).on());
438
+ if (!this.hasAttribute(this.constructor.ATTR.PICKER_STYLE))
439
+ this.#render(); // skip if already rendered by attr-change during upgrade!
440
+ // finally
441
+ if (!this.hasAttribute('selection'))
442
+ return;
443
+ this.#selection = this.getAttribute('selection') ?? '';
444
+ this.#sendValueToForm(false);
445
+ }
446
+ get pickerStyle() {
447
+ const attr = this.constructor.ATTR.PICKER_STYLE;
448
+ return pickerStyles.includes(this.getAttribute(attr) ?? '') ? this.getAttribute(attr) : 'automatic';
449
+ }
450
+ #render() {
451
+ if (devFlags.debug)
452
+ console.debug(`${_a.name} ⚡️ render (${this.pickerStyle})`);
453
+ // const style = this.getAttribute((this.constructor as typeof PickerView).ATTR.PICKER_STYLE)
454
+ if (this.#lastRenderedStyle === this.pickerStyle)
455
+ return; // skip if already applied
456
+ this.#lastRenderedStyle = this.pickerStyle;
457
+ // clear shadow DOM
458
+ this.#shadowRoot.replaceChildren(document.importNode(this.template, true));
459
+ CleanupRegistry.unregister(this, 'slots');
460
+ for (const slot of this.#shadowRoot.querySelectorAll('slot'))
461
+ this.#slots?.set(slot.name, slot);
462
+ CleanupRegistry.register(this, () => {
463
+ this.#slots = new Map();
464
+ }, 'slots');
465
+ CleanupRegistry.unregister(this, 'validities');
466
+ CleanupRegistry.register(this, onoff('slotchange', this.#handleValiditiesSlotchange, this.#slots?.get('validity-options')).on(), 'validities');
467
+ CleanupRegistry.unregister(this, 'datalist'); //off1()
468
+ CleanupRegistry.register(this, onoff('slotchange', this.#handleListSlotchange, this.#slots?.get('list')).on(), 'datalist');
469
+ // #renderSlotted should run automatically now by slotchange initial event
470
+ // switch (this.pickerStyle) {
471
+ // }
472
+ }
473
+ #sendValueToForm = (dispatchEvent = true) => {
474
+ // input.value has already been updated/synced !!
475
+ if (this.matches(':disabled'))
476
+ return this.setValidity({});
477
+ if (this.hasAttribute('required')) {
478
+ if (!this.#selection) {
479
+ this.setValidity({ badInput: true }, 'invalid-selection');
480
+ }
481
+ else
482
+ this.setValidity({});
483
+ }
484
+ else
485
+ this.setValidity({});
486
+ const entries = new FormData();
487
+ entries.append(this.name, this.#selection);
488
+ this.#internals.setFormValue(entries);
489
+ if (dispatchEvent)
490
+ this.dispatchEvent(new CustomEvent('selection', { detail: { selection: this.#selection }, bubbles: true, composed: true }));
491
+ };
492
+ #handleTriggerClick = async ({ type, target }) => {
493
+ if (devFlags.debug)
494
+ console.debug(`${_a.name} ⚡️ ${type}`);
495
+ if (!(target instanceof HTMLElement))
496
+ return;
497
+ this.#spawn?.remove?.();
498
+ const level0 = this.#spawnPage('sheet' === this.pickerStyle ? 'sheet-view' : 'body-view', this.hasAttribute('searchable'), this.getAttribute('label'));
499
+ if (!level0)
500
+ return;
501
+ const path = new NavigationPath(target)?.hydrate();
502
+ await startViewTransition(target, 'forwards', async () => {
503
+ this.#spawn = level0;
504
+ update(path, level0);
505
+ this.#reflectSelectionOnButtons();
506
+ });
507
+ };
508
+ #handlePageBtnClick = async (evt) => {
509
+ if (devFlags.debug)
510
+ console.debug(`${_a.name} ⚡️ ${evt?.type}`);
511
+ evt.stopImmediatePropagation();
512
+ evt.preventDefault();
513
+ const { currentTarget: btn } = evt;
514
+ if (!(btn instanceof HTMLElement))
515
+ return;
516
+ // const btn = target.closest('button')
517
+ // if (!btn) return
518
+ const proceed = () => {
519
+ this.#spawn?.remove();
520
+ this.#selection = btn.getAttribute('value') ?? '';
521
+ this.#reflectSelectionOnButtons();
522
+ this.#reflectCurrentValueLabel();
523
+ this.#sendValueToForm();
524
+ };
525
+ const { body } = new NavigationPath(this.#spawn)?.hydrate();
526
+ if (!body)
527
+ return proceed();
528
+ await startViewTransition(body, 'backwards', async () => {
529
+ proceed();
530
+ });
531
+ };
532
+ #handleClick({ type, target }) {
533
+ if (devFlags.debug)
534
+ console.debug(`${_a.name} ⚡️ ${type}`);
535
+ if (!(target instanceof HTMLElement))
536
+ return;
537
+ const btn = target.closest('button');
538
+ if (!btn)
539
+ return;
540
+ this.#selection = btn.getAttribute('value') ?? '';
541
+ this.#reflectSelectionOnButtons();
542
+ this.#reflectCurrentValueLabel();
543
+ this.#sendValueToForm();
544
+ }
545
+ #handleValiditiesSlotchange = ({ type, target: slot }) => {
546
+ if (devFlags.debug)
547
+ console.debug(`${_a.name} ⚡️ ${type}`);
548
+ if (!(slot instanceof HTMLSlotElement))
549
+ return;
550
+ const assigned = slot.assignedElements();
551
+ this.#validityObservers.syncObservations(assigned, ['value', 'label']);
552
+ // if (0 < assigned.length)
553
+ this.#renderValidityMsgs([]);
554
+ };
555
+ #handleListSlotchange = ({ type, target: slot }) => {
556
+ if (devFlags.debug)
557
+ console.debug(`${_a.name} ⚡️ ${type}`);
558
+ if (!(slot instanceof HTMLSlotElement))
559
+ return;
560
+ const assigned = slot.assignedElements();
561
+ this.#observers.syncObservations(assigned);
562
+ // if (0 < assigned.length)
563
+ this.#renderSlotted([]);
564
+ };
565
+ static #wrapOptionTag(node) {
566
+ if (devFlags.debug)
567
+ console.debug(`${_a.name} #wrapOptionTag`);
568
+ const btn = $(`<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>`, '>1'), hStack = btn.querySelector(':scope>h-stack');
569
+ // chevron = hStack?.querySelector<HTMLElement>(':scope>label-view')
570
+ btn.setAttribute('value', extractTagFromOption(node));
571
+ // if (selection !== btn.getAttribute('value')) chevron?.style.setProperty('visibility', 'hidden')
572
+ const label = $(`<label-view><span></span></label-view>`, '>1');
573
+ renderLabelTitle(label, extractCurrentValueFromOption(node)); // label.querySelector('span')!.textContent = extractCurrentValueFromOption(node) //label.setAttribute('title', extractCurrentValueFromOption(node))
574
+ hStack?.appendChild(label);
575
+ return btn;
576
+ }
577
+ static #wrapOptgroupTag(node) {
578
+ if (devFlags.debug)
579
+ console.debug(`${_a.name} #wrapOptgroupTag`);
580
+ const labelT = `<label-view><span></span></label-view>`, 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>`;
581
+ const group = $(`<details is="disclosure-group">${summaryT}</details>`, '>1'), hStack = group.querySelector(':scope>summary>h-stack'); // ?? group.appendChild($(summaryT, '>1'))
582
+ // summaryLabel = summary.querySelector(':scope>label-view') ?? summary.appendChild($(labelT, '>1'))
583
+ // if (node.hasAttribute('label')) summaryLabel.setAttribute('title', node.getAttribute('label') ?? '')
584
+ // if (node.hasAttribute('data-system-image')) summaryLabel.setAttribute('system-image', node.getAttribute('data-system-image') ?? '')
585
+ if (hStack)
586
+ renderLabel(hStack, ':scope>label-view:nth-child(2)', labelT, node.getAttribute('label'), node.getAttribute('data-system-image'));
587
+ return group;
588
+ }
589
+ static #reflectButtons(nodes, container) {
590
+ if (devFlags.debug)
591
+ console.debug(`${_a.name} #reflectButtons`);
592
+ for (const node of nodes)
593
+ switch (node.tagName) {
594
+ case 'DATALIST': {
595
+ const group = $(`<menu-view tabindex="0"></menu-view>`, '>1');
596
+ // label = group.querySelector(':scope>label-view[slot=label]') ?? group.appendChild($(`<label-view slot="label"></label-view>`, '>1'))
597
+ // if (node.hasAttribute('data-label')) label.setAttribute('title', node.getAttribute('data-label') ?? '')
598
+ // if (node.hasAttribute('data-system-image')) label.setAttribute('system-image', node.getAttribute('data-system-image') ?? '')
599
+ renderLabel(group, ':scope>label-view[slot=label]', `<label-view slot="label"><span></span></label-view>`, node.getAttribute('data-label'), node.getAttribute('data-system-image'));
600
+ _a.#reflectButtons([...node.children], group);
601
+ container.appendChild(group);
602
+ break;
603
+ }
604
+ case 'OPTGROUP': {
605
+ const group = _a.#wrapOptgroupTag(node);
606
+ _a.#reflectButtons([...node.children], group);
607
+ container.appendChild(group);
608
+ break;
609
+ }
610
+ case 'OPTION':
611
+ default: {
612
+ container.appendChild(_a.#wrapOptionTag(node));
613
+ break;
614
+ }
615
+ }
616
+ }
617
+ #reflectPlaceholder(value) {
618
+ if (devFlags.debug)
619
+ console.debug(`#reflectPlaceholder`);
620
+ const input = this.#shadowRoot.querySelector('input');
621
+ if (input) {
622
+ if (value)
623
+ input.setAttribute('placeholder', value);
624
+ else
625
+ input.removeAttribute('placeholder');
626
+ }
627
+ }
628
+ #reflectLabel(value) {
629
+ if (devFlags.debug)
630
+ console.debug(`${_a.name} #reflectLabel`);
631
+ renderLabel(this, ':scope>label-view[slot=label]', `<label-view slot="label" foreground="secondary"><span></span></label-view>`, value);
632
+ this.#renderSlotted([]);
633
+ }
634
+ #reflectSelectionOnButtons() {
635
+ if (devFlags.debug)
636
+ console.debug(`${_a.name} #reflectSelectionOnButtons`);
637
+ // walk all rendered buttons (inline has buttons in list, menu has buttons in menu-view)
638
+ for (const el of this.querySelectorAll('button[value]:not([slot])'))
639
+ el.querySelector('label-view[data-role="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden');
640
+ // also sync the spawn (sheet/navigation) if open
641
+ if (this.#spawn)
642
+ for (const el of this.#spawn.querySelectorAll('list-view button[value]:not([slot])'))
643
+ el.querySelector('label-view[data-role="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden');
644
+ }
645
+ get #currentTag() {
646
+ return this.#slots
647
+ ?.get('list')
648
+ ?.assignedElements({ flatten: true })
649
+ .flatMap((el) => [...(el.matches('option') ? [el] : []), ...el.querySelectorAll('option')])
650
+ .find((el) => this.#selection === extractTagFromOption(el));
651
+ }
652
+ get #currentValueLabel() {
653
+ const cvl = this.#currentTag ? extractCurrentValueFromOption(this.#currentTag) : '';
654
+ return (this.getAttribute('current-value-label') ?? '').replaceAll('{{selection}}', this.#selection).replaceAll('{{currentValueLabel}}', this.#selection) || cvl || this.#selection;
655
+ }
656
+ #reflectCurrentValueLabel() {
657
+ if (devFlags.debug)
658
+ console.debug(`${_a.name} #reflectCurrentValueLabel`);
659
+ switch (this.pickerStyle) {
660
+ case 'sheet':
661
+ case 'navigation-link': {
662
+ const currentValueLabel = this.querySelector(':scope>label-view:not([slot])');
663
+ if (!currentValueLabel)
664
+ break;
665
+ renderLabelTitle(currentValueLabel, this.#currentValueLabel);
666
+ renderLabelIcon(currentValueLabel, 'dots-three');
667
+ break;
668
+ }
669
+ case 'menu': {
670
+ const currentValueLabel = this.querySelector(':scope>menu-view:not([slot])>label-view[slot=label]');
671
+ if (!currentValueLabel)
672
+ break;
673
+ renderLabelTitle(currentValueLabel, this.#currentValueLabel);
674
+ renderLabelIcon(currentValueLabel, 'dots-three');
675
+ break;
676
+ }
677
+ case 'inline':
678
+ default: {
679
+ //
680
+ break;
681
+ }
682
+ }
683
+ }
684
+ #reflectTriggerHelp() {
685
+ if (devFlags.debug)
686
+ console.debug(`${_a.name} #reflectTriggerHelp`);
687
+ let trigger;
688
+ switch (this.pickerStyle) {
689
+ case 'sheet':
690
+ case 'navigation-link': {
691
+ trigger = this.querySelector(':scope>label-view:not([slot])') ?? undefined;
692
+ }
693
+ case 'menu': {
694
+ trigger = this.querySelector(':scope>menu-view:not([slot])>label-view[slot=label]') ?? undefined;
695
+ }
696
+ case 'inline':
697
+ default: {
698
+ //udnefined
699
+ }
700
+ }
701
+ if (this.hasAttribute('help'))
702
+ trigger?.setAttribute('help', this.getAttribute('help') ?? '');
703
+ else
704
+ trigger?.removeAttribute('help');
705
+ }
706
+ // Optional: form participation properties
707
+ get name() {
708
+ return this.getAttribute('name') ?? this.getAttribute('label') ?? this.querySelector(':scope>[slot=label]')?.textContent ?? '';
709
+ }
710
+ setValidity = (flags, message, anchor) => {
711
+ // let msg
712
+ // if (message)
713
+ for (const k in flags) {
714
+ const key = k; // ✅ type-safe cast
715
+ if (true !== flags[key])
716
+ continue;
717
+ for (const el of this.#slots?.get('validity-options')?.assignedElements({ flatten: true }) ?? []) {
718
+ if (!el.matches('option'))
719
+ continue;
720
+ const { label, value } = el;
721
+ if (`${kebabCase(key)}` === value) {
722
+ message = label;
723
+ break;
724
+ }
725
+ else if (`${kebabCase(key)}:${message}` === value) {
726
+ message = label;
727
+ break;
728
+ }
729
+ }
730
+ }
731
+ if (!message)
732
+ for (const k in flags) {
733
+ const key = k; // ✅ type-safe cast
734
+ if (true !== flags[key])
735
+ continue;
736
+ message = kebabCase(key);
737
+ break;
738
+ }
739
+ if (devFlags.debug)
740
+ console.debug(`${_a.name} ⚡️ validity-change`);
741
+ return this.#internals.setValidity(flags, this.#customValidity || message, anchor);
742
+ };
743
+ setCustomValidity = (message) => {
744
+ this.#customValidity = message;
745
+ if (this.#customValidity)
746
+ this.#internals.setValidity({ ...this.#internals.validity, customError: true }, message);
747
+ else
748
+ this.#sendValueToForm(false);
749
+ };
750
+ formAssociatedCallback = (form) => {
751
+ this.#sendValueToForm();
752
+ };
753
+ formDisabledCallback = (disabled) => {
754
+ for (const btn of this.#shadowRoot.querySelectorAll('button'))
755
+ btn.toggleAttribute('disabled', !disabled);
756
+ };
757
+ formResetCallback = () => {
758
+ this.#selection = '';
759
+ this.#reflectSelectionOnButtons();
760
+ this.#reflectCurrentValueLabel();
761
+ this.#sendValueToForm();
762
+ };
763
+ }
764
+ _a = PickerView;