@swiftwc/ui 0.0.0-dev.99 → 1.0.0-canary.0

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