@vielzeug/refine 26.10.7 → 26.10.10

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 (326) hide show
  1. package/README.md +6 -0
  2. package/dist/accordion-item.cjs +2 -2
  3. package/dist/accordion-item.cjs.map +1 -1
  4. package/dist/accordion-item.js +6 -6
  5. package/dist/accordion-item.js.map +1 -1
  6. package/dist/accordion.cjs +2 -2
  7. package/dist/accordion.cjs.map +1 -1
  8. package/dist/accordion.js +2 -2
  9. package/dist/accordion.js.map +1 -1
  10. package/dist/alert.cjs +2 -2
  11. package/dist/alert.cjs.map +1 -1
  12. package/dist/alert.js +2 -2
  13. package/dist/alert.js.map +1 -1
  14. package/dist/avatar.cjs +3 -3
  15. package/dist/avatar.cjs.map +1 -1
  16. package/dist/avatar.js +2 -2
  17. package/dist/avatar.js.map +1 -1
  18. package/dist/badge.cjs +2 -2
  19. package/dist/badge.cjs.map +1 -1
  20. package/dist/badge.js +2 -2
  21. package/dist/badge.js.map +1 -1
  22. package/dist/box.cjs +2 -2
  23. package/dist/box.cjs.map +1 -1
  24. package/dist/box.js +2 -2
  25. package/dist/box.js.map +1 -1
  26. package/dist/button-group.cjs +2 -2
  27. package/dist/button-group.cjs.map +1 -1
  28. package/dist/button-group.js +2 -2
  29. package/dist/button-group.js.map +1 -1
  30. package/dist/button.cjs +9 -9
  31. package/dist/button.cjs.map +1 -1
  32. package/dist/button.js +8 -8
  33. package/dist/button.js.map +1 -1
  34. package/dist/card.cjs +2 -2
  35. package/dist/card.cjs.map +1 -1
  36. package/dist/card.js +2 -2
  37. package/dist/card.js.map +1 -1
  38. package/dist/carousel.cjs +6 -6
  39. package/dist/carousel.cjs.map +1 -1
  40. package/dist/carousel.js +6 -6
  41. package/dist/carousel.js.map +1 -1
  42. package/dist/chat-message.cjs +4 -4
  43. package/dist/chat-message.cjs.map +1 -1
  44. package/dist/chat-message.js +4 -4
  45. package/dist/chat-message.js.map +1 -1
  46. package/dist/checkbox-group.cjs +1 -1
  47. package/dist/checkbox-group.cjs.map +1 -1
  48. package/dist/checkbox-group.js +1 -1
  49. package/dist/checkbox-group.js.map +1 -1
  50. package/dist/checkbox.cjs +1 -1
  51. package/dist/checkbox.cjs.map +1 -1
  52. package/dist/checkbox.js +1 -1
  53. package/dist/checkbox.js.map +1 -1
  54. package/dist/chip.cjs +8 -8
  55. package/dist/chip.cjs.map +1 -1
  56. package/dist/chip.js +8 -8
  57. package/dist/chip.js.map +1 -1
  58. package/dist/combobox.cjs +8 -8
  59. package/dist/combobox.cjs.map +1 -1
  60. package/dist/combobox.js +11 -11
  61. package/dist/combobox.js.map +1 -1
  62. package/dist/content/avatar/avatar.d.ts +2 -1
  63. package/dist/content/avatar/avatar.d.ts.map +1 -1
  64. package/dist/content/card/card.d.ts +6 -3
  65. package/dist/content/card/card.d.ts.map +1 -1
  66. package/dist/content/carousel/carousel.d.ts +5 -3
  67. package/dist/content/carousel/carousel.d.ts.map +1 -1
  68. package/dist/content/chat-message/chat-message.d.ts +2 -1
  69. package/dist/content/chat-message/chat-message.d.ts.map +1 -1
  70. package/dist/content/copy-command/copy-command.d.ts +1 -1
  71. package/dist/content/copy-command/copy-command.d.ts.map +1 -1
  72. package/dist/content/keyboard-key/keyboard-key.d.ts +2 -1
  73. package/dist/content/keyboard-key/keyboard-key.d.ts.map +1 -1
  74. package/dist/content/list/list.d.ts +2 -1
  75. package/dist/content/list/list.d.ts.map +1 -1
  76. package/dist/content/marquee/marquee.d.ts +3 -2
  77. package/dist/content/marquee/marquee.d.ts.map +1 -1
  78. package/dist/content/pagination/pagination.d.ts +1 -1
  79. package/dist/content/pagination/pagination.d.ts.map +1 -1
  80. package/dist/content/qr-code/qr-code.d.ts +0 -41
  81. package/dist/content/qr-code/qr-code.d.ts.map +1 -1
  82. package/dist/content/separator/separator.d.ts +1 -1
  83. package/dist/content/separator/separator.d.ts.map +1 -1
  84. package/dist/content/stepper/step.d.ts +1 -1
  85. package/dist/content/stepper/step.d.ts.map +1 -1
  86. package/dist/content/table/table.d.ts +2 -1
  87. package/dist/content/table/table.d.ts.map +1 -1
  88. package/dist/content/text/text.d.ts +13 -6
  89. package/dist/content/text/text.d.ts.map +1 -1
  90. package/dist/cookie-banner.cjs +1 -1
  91. package/dist/cookie-banner.cjs.map +1 -1
  92. package/dist/cookie-banner.js +1 -1
  93. package/dist/cookie-banner.js.map +1 -1
  94. package/dist/copy-command.cjs +6 -6
  95. package/dist/copy-command.cjs.map +1 -1
  96. package/dist/copy-command.js +7 -7
  97. package/dist/copy-command.js.map +1 -1
  98. package/dist/core/checkable.cjs +1 -1
  99. package/dist/core/checkable.cjs.map +1 -1
  100. package/dist/core/checkable.d.ts +6 -7
  101. package/dist/core/checkable.d.ts.map +1 -1
  102. package/dist/core/checkable.js +1 -1
  103. package/dist/core/checkable.js.map +1 -1
  104. package/dist/core/choice-field.cjs +1 -1
  105. package/dist/core/choice-field.cjs.map +1 -1
  106. package/dist/core/choice-field.d.ts +6 -6
  107. package/dist/core/choice-field.d.ts.map +1 -1
  108. package/dist/core/choice-field.js +1 -1
  109. package/dist/core/choice-field.js.map +1 -1
  110. package/dist/core/text-field.cjs +1 -1
  111. package/dist/core/text-field.cjs.map +1 -1
  112. package/dist/core/text-field.d.ts +8 -8
  113. package/dist/core/text-field.d.ts.map +1 -1
  114. package/dist/core/text-field.js +1 -1
  115. package/dist/core/text-field.js.map +1 -1
  116. package/dist/counter.cjs +2 -2
  117. package/dist/counter.cjs.map +1 -1
  118. package/dist/counter.js +2 -2
  119. package/dist/counter.js.map +1 -1
  120. package/dist/custom-elements.json +886 -749
  121. package/dist/datagrid.cjs +47 -47
  122. package/dist/datagrid.cjs.map +1 -1
  123. package/dist/datagrid.js +51 -51
  124. package/dist/datagrid.js.map +1 -1
  125. package/dist/date-picker.cjs +9 -9
  126. package/dist/date-picker.cjs.map +1 -1
  127. package/dist/date-picker.js +11 -11
  128. package/dist/date-picker.js.map +1 -1
  129. package/dist/dialog.cjs +3 -3
  130. package/dist/dialog.cjs.map +1 -1
  131. package/dist/dialog.js +3 -3
  132. package/dist/dialog.js.map +1 -1
  133. package/dist/disclosure/accordion/accordion.d.ts +4 -2
  134. package/dist/disclosure/accordion/accordion.d.ts.map +1 -1
  135. package/dist/disclosure/accordion-item/accordion-item.d.ts +1 -1
  136. package/dist/disclosure/accordion-item/accordion-item.d.ts.map +1 -1
  137. package/dist/disclosure/tab-item/tab-item.d.ts +1 -1
  138. package/dist/disclosure/tab-item/tab-item.d.ts.map +1 -1
  139. package/dist/disclosure/tabs/tabs.d.ts +1 -1
  140. package/dist/disclosure/tabs/tabs.d.ts.map +1 -1
  141. package/dist/drawer.cjs +4 -4
  142. package/dist/drawer.cjs.map +1 -1
  143. package/dist/drawer.js +2 -2
  144. package/dist/drawer.js.map +1 -1
  145. package/dist/feedback/alert/alert.d.ts +2 -1
  146. package/dist/feedback/alert/alert.d.ts.map +1 -1
  147. package/dist/feedback/badge/badge.d.ts +7 -4
  148. package/dist/feedback/badge/badge.d.ts.map +1 -1
  149. package/dist/feedback/chip/chip.d.ts +7 -5
  150. package/dist/feedback/chip/chip.d.ts.map +1 -1
  151. package/dist/grid-item.cjs +2 -2
  152. package/dist/grid-item.cjs.map +1 -1
  153. package/dist/grid-item.js +2 -2
  154. package/dist/grid-item.js.map +1 -1
  155. package/dist/grid.cjs +2 -2
  156. package/dist/grid.cjs.map +1 -1
  157. package/dist/grid.js +2 -2
  158. package/dist/grid.js.map +1 -1
  159. package/dist/input.cjs +7 -7
  160. package/dist/input.cjs.map +1 -1
  161. package/dist/input.js +8 -8
  162. package/dist/input.js.map +1 -1
  163. package/dist/inputs/button/button.d.ts +3 -2
  164. package/dist/inputs/button/button.d.ts.map +1 -1
  165. package/dist/inputs/button-group/button-group.d.ts +1 -37
  166. package/dist/inputs/button-group/button-group.d.ts.map +1 -1
  167. package/dist/inputs/combobox/combobox.types.d.ts +2 -2
  168. package/dist/inputs/combobox/combobox.types.d.ts.map +1 -1
  169. package/dist/inputs/counter/counter.d.ts +3 -2
  170. package/dist/inputs/counter/counter.d.ts.map +1 -1
  171. package/dist/inputs/datagrid/datagrid-model.cjs +1 -1
  172. package/dist/inputs/datagrid/datagrid-model.cjs.map +1 -1
  173. package/dist/inputs/datagrid/datagrid-model.d.ts +2 -1
  174. package/dist/inputs/datagrid/datagrid-model.d.ts.map +1 -1
  175. package/dist/inputs/datagrid/datagrid-model.js +1 -1
  176. package/dist/inputs/datagrid/datagrid-model.js.map +1 -1
  177. package/dist/inputs/datagrid/datagrid.d.ts +4 -2
  178. package/dist/inputs/datagrid/datagrid.d.ts.map +1 -1
  179. package/dist/inputs/date-picker/date-picker.d.ts +3 -2
  180. package/dist/inputs/date-picker/date-picker.d.ts.map +1 -1
  181. package/dist/inputs/input/input.d.ts +5 -3
  182. package/dist/inputs/input/input.d.ts.map +1 -1
  183. package/dist/inputs/message-composer/message-composer.d.ts +3 -2
  184. package/dist/inputs/message-composer/message-composer.d.ts.map +1 -1
  185. package/dist/inputs/number-input/number-input.d.ts +1 -1
  186. package/dist/inputs/number-input/number-input.d.ts.map +1 -1
  187. package/dist/inputs/otp-input/otp-input.d.ts +3 -2
  188. package/dist/inputs/otp-input/otp-input.d.ts.map +1 -1
  189. package/dist/inputs/query-bar/query-bar.d.ts.map +1 -1
  190. package/dist/inputs/select/select.d.ts +2 -2
  191. package/dist/inputs/select/select.d.ts.map +1 -1
  192. package/dist/inputs/textarea/textarea.d.ts +4 -3
  193. package/dist/inputs/textarea/textarea.d.ts.map +1 -1
  194. package/dist/inputs/time-picker/time-picker.d.ts +2 -2
  195. package/dist/inputs/time-picker/time-picker.d.ts.map +1 -1
  196. package/dist/keyboard-key.cjs +3 -3
  197. package/dist/keyboard-key.cjs.map +1 -1
  198. package/dist/keyboard-key.js +3 -3
  199. package/dist/keyboard-key.js.map +1 -1
  200. package/dist/layout/box/box.d.ts +6 -3
  201. package/dist/layout/box/box.d.ts.map +1 -1
  202. package/dist/layout/grid/grid.d.ts +11 -6
  203. package/dist/layout/grid/grid.d.ts.map +1 -1
  204. package/dist/layout/grid-item/grid-item.d.ts +8 -4
  205. package/dist/layout/grid-item/grid-item.d.ts.map +1 -1
  206. package/dist/layout/navbar/navbar.d.ts +4 -2
  207. package/dist/layout/navbar/navbar.d.ts.map +1 -1
  208. package/dist/layout/sidebar/sidebar.d.ts +2 -1
  209. package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
  210. package/dist/list-item.cjs +1 -1
  211. package/dist/list-item.cjs.map +1 -1
  212. package/dist/list-item.js +1 -1
  213. package/dist/list-item.js.map +1 -1
  214. package/dist/list.cjs +2 -2
  215. package/dist/list.cjs.map +1 -1
  216. package/dist/list.js +2 -2
  217. package/dist/list.js.map +1 -1
  218. package/dist/marquee.cjs +3 -3
  219. package/dist/marquee.cjs.map +1 -1
  220. package/dist/marquee.js +3 -3
  221. package/dist/marquee.js.map +1 -1
  222. package/dist/menu.cjs +3 -3
  223. package/dist/menu.cjs.map +1 -1
  224. package/dist/menu.js +3 -3
  225. package/dist/menu.js.map +1 -1
  226. package/dist/message-composer.cjs +10 -10
  227. package/dist/message-composer.cjs.map +1 -1
  228. package/dist/message-composer.js +7 -7
  229. package/dist/message-composer.js.map +1 -1
  230. package/dist/navbar.cjs +10 -10
  231. package/dist/navbar.cjs.map +1 -1
  232. package/dist/navbar.js +13 -13
  233. package/dist/navbar.js.map +1 -1
  234. package/dist/number-input.cjs +3 -3
  235. package/dist/number-input.cjs.map +1 -1
  236. package/dist/number-input.js +4 -4
  237. package/dist/number-input.js.map +1 -1
  238. package/dist/otp-input.cjs +4 -4
  239. package/dist/otp-input.cjs.map +1 -1
  240. package/dist/otp-input.js +4 -4
  241. package/dist/otp-input.js.map +1 -1
  242. package/dist/overlay/dialog/dialog.d.ts +7 -4
  243. package/dist/overlay/dialog/dialog.d.ts.map +1 -1
  244. package/dist/overlay/drawer/drawer.d.ts +4 -2
  245. package/dist/overlay/drawer/drawer.d.ts.map +1 -1
  246. package/dist/overlay/menu/menu.d.ts +2 -1
  247. package/dist/overlay/menu/menu.d.ts.map +1 -1
  248. package/dist/pagination.cjs +9 -9
  249. package/dist/pagination.cjs.map +1 -1
  250. package/dist/pagination.js +9 -9
  251. package/dist/pagination.js.map +1 -1
  252. package/dist/qr-code.cjs +2 -2
  253. package/dist/qr-code.cjs.map +1 -1
  254. package/dist/qr-code.js +2 -2
  255. package/dist/qr-code.js.map +1 -1
  256. package/dist/query-bar.cjs +7 -7
  257. package/dist/query-bar.cjs.map +1 -1
  258. package/dist/query-bar.js +8 -8
  259. package/dist/query-bar.js.map +1 -1
  260. package/dist/radio-group.cjs +1 -1
  261. package/dist/radio-group.cjs.map +1 -1
  262. package/dist/radio-group.js +1 -1
  263. package/dist/radio-group.js.map +1 -1
  264. package/dist/refine.cjs +123 -123
  265. package/dist/refine.cjs.map +1 -1
  266. package/dist/refine.iife.js +123 -123
  267. package/dist/refine.iife.js.map +1 -1
  268. package/dist/refine.js +124 -124
  269. package/dist/refine.js.map +1 -1
  270. package/dist/select.cjs +13 -13
  271. package/dist/select.cjs.map +1 -1
  272. package/dist/select.js +15 -15
  273. package/dist/select.js.map +1 -1
  274. package/dist/separator.cjs +4 -4
  275. package/dist/separator.cjs.map +1 -1
  276. package/dist/separator.js +4 -4
  277. package/dist/separator.js.map +1 -1
  278. package/dist/shared/prop-bundles.cjs +1 -1
  279. package/dist/shared/prop-bundles.cjs.map +1 -1
  280. package/dist/shared/prop-bundles.d.ts +7 -8
  281. package/dist/shared/prop-bundles.d.ts.map +1 -1
  282. package/dist/shared/prop-bundles.js +1 -1
  283. package/dist/shared/prop-bundles.js.map +1 -1
  284. package/dist/shared/types.cjs +2 -0
  285. package/dist/shared/types.cjs.map +1 -0
  286. package/dist/shared/types.d.ts +27 -9
  287. package/dist/shared/types.d.ts.map +1 -1
  288. package/dist/shared/types.js +2 -0
  289. package/dist/shared/types.js.map +1 -0
  290. package/dist/sidebar.cjs +7 -7
  291. package/dist/sidebar.cjs.map +1 -1
  292. package/dist/sidebar.js +4 -4
  293. package/dist/sidebar.js.map +1 -1
  294. package/dist/step.cjs +10 -10
  295. package/dist/step.cjs.map +1 -1
  296. package/dist/step.js +10 -10
  297. package/dist/step.js.map +1 -1
  298. package/dist/tab-item.cjs +4 -4
  299. package/dist/tab-item.cjs.map +1 -1
  300. package/dist/tab-item.js +5 -5
  301. package/dist/tab-item.js.map +1 -1
  302. package/dist/table.cjs +2 -2
  303. package/dist/table.cjs.map +1 -1
  304. package/dist/table.js +2 -2
  305. package/dist/table.js.map +1 -1
  306. package/dist/tabs.cjs +2 -2
  307. package/dist/tabs.cjs.map +1 -1
  308. package/dist/tabs.js +4 -4
  309. package/dist/tabs.js.map +1 -1
  310. package/dist/text.cjs +2 -2
  311. package/dist/text.cjs.map +1 -1
  312. package/dist/text.js +2 -2
  313. package/dist/text.js.map +1 -1
  314. package/dist/textarea.cjs +7 -7
  315. package/dist/textarea.cjs.map +1 -1
  316. package/dist/textarea.js +7 -7
  317. package/dist/textarea.js.map +1 -1
  318. package/dist/time-picker.cjs +7 -7
  319. package/dist/time-picker.cjs.map +1 -1
  320. package/dist/time-picker.js +7 -7
  321. package/dist/time-picker.js.map +1 -1
  322. package/dist/tooltip.cjs +1 -1
  323. package/dist/tooltip.cjs.map +1 -1
  324. package/dist/tooltip.js +1 -1
  325. package/dist/tooltip.js.map +1 -1
  326. package/package.json +15 -14
@@ -1 +1 @@
1
- {"version":3,"file":"time-picker.js","names":[],"sources":["../src/inputs/time-picker/time-picker.ts"],"sourcesContent":["import { bind, define, getHost, html, onMounted, prop, ref, useField } from '@vielzeug/ore';\nimport { computed, signal } from '@vielzeug/ripple';\nimport { createDropdownPositioner } from '../../core';\nimport type { VisualVariant } from '../../shared';\n\nimport { disablableBundle, roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, reducedMotionMixin } from '../../styles';\nimport { defineFieldValue, dispatchNativeFieldEvent, setFieldValue } from '../shared/native-field-event';\nimport componentStyles from './time-picker.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\nexport { INPUT_TAG } from '../input/input';\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type OreTimePickerEvents = {\n change: Event;\n input: Event;\n};\n\nexport type OreTimePickerProps = {\n /** Theme color */\n color?: string;\n /** Disable the picker */\n disabled?: boolean;\n /** Validation error message */\n error?: string;\n /** Expand to container width */\n fullwidth?: boolean;\n /** Helper text below the field */\n helper?: string;\n /** Visible label */\n label?: string;\n /** Label placement */\n 'label-placement'?: 'inset' | 'outside';\n /** Maximum selectable time in HH:MM format (24-hour) */\n max?: string;\n /** Minimum selectable time in HH:MM format (24-hour) */\n min?: string;\n /** Minute step interval (1–59) */\n 'minute-step'?: number;\n /** Form field name */\n name?: string;\n /** Placeholder shown in the trigger */\n placeholder?: string;\n /** Mark field as required */\n required?: boolean;\n /** Border radius */\n rounded?: string;\n /** Component size */\n size?: string;\n /**\n * Shows an inline green check icon inside the field to confirm the value has\n * passed validation. Ignored while `error` is set: an error always wins.\n */\n success?: boolean;\n /** Display format: '12' for AM/PM, '24' for 24-hour (default: '24') */\n 'time-format'?: '12' | '24';\n /** Selected time in HH:MM format (24-hour) */\n value?: string;\n /** Visual variant */\n variant?: Exclude<VisualVariant, 'text' | 'frost'>;\n};\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/** Parse \"HH:MM\" → { hours: 0-23, minutes: 0-59 } | null */\nfunction parseTime(val: string | undefined | null): { hours: number; minutes: number } | null {\n if (!val) return null;\n\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(val.trim());\n\n if (!match) return null;\n\n const h = Number(match[1]);\n const m = Number(match[2]);\n\n if (h < 0 || h > 23 || m < 0 || m > 59) return null;\n\n return { hours: h, minutes: m };\n}\n\n/** Format { hours, minutes } → \"HH:MM\" (24-hour, zero-padded) */\nfunction formatTime(h: number, m: number): string {\n return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;\n}\n\n/** Format for display label (respects 12/24 mode) */\nfunction formatDisplay(h: number, m: number, format: '12' | '24'): string {\n if (format === '12') {\n const period = h >= 12 ? 'PM' : 'AM';\n const displayH = h % 12 === 0 ? 12 : h % 12;\n\n return `${String(displayH).padStart(2, '0')}:${String(m).padStart(2, '0')} ${period}`;\n }\n\n return formatTime(h, m);\n}\n\n/** Clamp value to [min, max] in total minutes */\nfunction clampTime(\n h: number,\n m: number,\n minVal: string | undefined | null,\n maxVal: string | undefined | null,\n): { hours: number; minutes: number } {\n const total = h * 60 + m;\n const minParsed = parseTime(minVal);\n const maxParsed = parseTime(maxVal);\n const minMins = minParsed ? minParsed.hours * 60 + minParsed.minutes : 0;\n const maxMins = maxParsed ? maxParsed.hours * 60 + maxParsed.minutes : 23 * 60 + 59;\n const clamped = Math.min(Math.max(total, minMins), maxMins);\n\n return { hours: Math.floor(clamped / 60), minutes: clamped % 60 };\n}\n\n/**\n * An accessible time picker with a scrollable clock dropdown.\n * Supports 12/24-hour display formats, min/max bounds, minute steps, and form association.\n *\n * @element ore-time-picker\n *\n * @attr {string} value - Selected time in HH:MM (24-hour) format\n * @attr {string} min - Minimum selectable time (HH:MM)\n * @attr {string} max - Maximum selectable time (HH:MM)\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} placeholder - Trigger placeholder\n * @attr {boolean} disabled - Disable the picker\n * @attr {boolean} required - Required field\n * @attr {string} name - Form field name\n * @attr {string} error - Error message\n * @attr {boolean} success - Show an inline success check icon (suppressed while `error` is set)\n * @attr {string} helper - Helper text\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} time-format - '12' or '24' (default: '24')\n * @attr {number} minute-step - Minute increment (default: 5)\n *\n * @fires input - Fired when a time is selected.\n * @fires change - Fired when a time is selected.\n *\n * @slot label - Custom label for the trigger field\n * @slot prefix - Content before the trigger text (e.g. icon)\n * @slot helper - Custom helper text\n * @slot error - Custom error content\n *\n * @cssprop --time-picker-bg - Dropdown background\n * @cssprop --time-picker-border-color - Dropdown border color\n * @cssprop --time-picker-radius - Dropdown border radius\n * @cssprop --time-picker-shadow - Dropdown shadow\n * @cssprop --time-picker-selected-bg - Selected option background\n * @cssprop --time-picker-option-hover-bg - Option hover background\n *\n * @part field - The trigger field\n * @part dropdown - The floating time dropdown\n * @part column - A scrollable column (hours / minutes / period)\n * @part option - An individual time option cell\n *\n * @example\n * ```html\n * <!-- 24-hour clock, 15-minute steps -->\n * <ore-time-picker label=\"Meeting time\" value=\"09:30\" minute-step=\"15\" color=\"primary\"></ore-time-picker>\n *\n * <!-- 12-hour AM/PM format -->\n * <ore-time-picker label=\"Appointment\" time-format=\"12\" minute-step=\"30\"></ore-time-picker>\n *\n * <!-- Outside label, bordered variant -->\n * <ore-time-picker label=\"Start time\" label-placement=\"outside\" variant=\"bordered\"></ore-time-picker>\n * ```\n */\nexport const TIME_PICKER_TAG = 'ore-time-picker' as const;\n\ndefine<OreTimePickerProps>(TIME_PICKER_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...roundableBundle,\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n max: prop.string(),\n min: prop.string(),\n 'minute-step': prop.number(5),\n name: prop.string(),\n placeholder: prop.string(),\n required: prop.bool(false),\n success: prop.bool(false),\n 'time-format': prop.oneOf(['12', '24'] as const, '24'),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n\n setup(props) {\n const el = getHost();\n\n // ── Signals ──────────────────────────────────────────────────────────────\n\n const isOpen = signal(false);\n const selectedTime = signal<{ hours: number; minutes: number } | null>(parseTime(props.value.value));\n\n defineFieldValue(\n el,\n () => {\n const value = selectedTime.value;\n\n return value ? formatTime(value.hours, value.minutes) : '';\n },\n (value) => {\n selectedTime.value = parseTime(value);\n },\n );\n\n // ── Floating position ────────────────────────────────────────────────────\n // Same Orbit-powered positioner ore-date-picker's `.calendar` popup uses (see its own\n // comment for the full rationale): `fixed` + computed `top`/`left` instead of a static\n // `position: absolute; top: 100%`, so the dropdown flips/shifts to stay within the nearest\n // clipping ancestor instead of silently overflowing it.\n const triggerRef = ref<HTMLElement>();\n const dropdownRef = ref<HTMLElement>();\n\n const positioner = createDropdownPositioner({\n getFloating: () => dropdownRef.value ?? null,\n getOffsetPx: () => 4,\n getReference: () => triggerRef.value ?? null,\n matchWidth: false,\n });\n\n let stopAutoUpdate: (() => void) | null = null;\n\n const isDisabled = computed(() => Boolean(props.disabled.value));\n\n // ── Form value ────────────────────────────────────────────────────────────\n\n useField<string>({\n disabled: isDisabled,\n toFormValue: (v) => v || null,\n value: computed(() => {\n const t = selectedTime.value;\n\n return t ? formatTime(t.hours, t.minutes) : '';\n }),\n });\n\n // ── Computed helpers ──────────────────────────────────────────────────────\n\n const fmt = computed(() => props['time-format'].value ?? '24');\n const minuteStep = computed(() => Math.max(1, Math.min(59, props['minute-step'].value ?? 5)));\n\n const triggerText = computed(() => {\n if (!selectedTime.value) return props.placeholder.value || '';\n\n return formatDisplay(selectedTime.value.hours, selectedTime.value.minutes, fmt.value as '12' | '24');\n });\n\n /** All hour options for the column (0-23 in 24h; 1-12 in 12h) */\n const hourOptions = computed<number[]>(() => {\n if (fmt.value === '12') {\n return Array.from({ length: 12 }, (_, i) => i + 1);\n }\n\n return Array.from({ length: 24 }, (_, i) => i);\n });\n\n /** All minute options for the column, stepped */\n const minuteOptions = computed<number[]>(() => {\n const step = minuteStep.value;\n const opts: number[] = [];\n\n for (let m = 0; m < 60; m += step) opts.push(m);\n\n return opts;\n });\n\n const dialogId = `time-picker-${Math.random().toString(36).slice(2, 9)}-dropdown`;\n\n /** Convert 24h hour to display hour in current format */\n function toDisplayHour(h24: number): number {\n if (fmt.value === '24') return h24;\n\n const h12 = h24 % 12;\n\n return h12 === 0 ? 12 : h12;\n }\n\n /** Convert display hour + period → 24h hour */\n function to24Hour(displayHour: number, period: 'AM' | 'PM'): number {\n if (fmt.value === '24') return displayHour;\n\n if (period === 'AM') return displayHour === 12 ? 0 : displayHour;\n\n return displayHour === 12 ? 12 : displayHour + 12;\n }\n\n /** Whether a given HH:MM is out of [min, max] */\n function isTimeDisabled(h: number, m: number): boolean {\n const total = h * 60 + m;\n const minParsed = parseTime(props.min.value);\n const maxParsed = parseTime(props.max.value);\n\n if (minParsed && total < minParsed.hours * 60 + minParsed.minutes) return true;\n\n if (maxParsed && total > maxParsed.hours * 60 + maxParsed.minutes) return true;\n\n return false;\n }\n\n // ── State for focused/pending selection ───────────────────────────────────\n\n /** Pending hour (24h) while user is navigating columns */\n const pendingHour = signal<number>(selectedTime.value?.hours ?? 0);\n /** Pending minute */\n const pendingMinute = signal<number>(selectedTime.value?.minutes ?? 0);\n /** Pending period (12h mode) */\n const pendingPeriod = signal<'AM' | 'PM'>((selectedTime.value?.hours ?? 0) >= 12 ? 'PM' : 'AM');\n\n // ── Sync value prop externally ────────────────────────────────────────────\n\n let lastValueProp = props.value.value;\n\n onMounted(() => {\n const interval = setInterval(() => {\n const current = props.value.value;\n\n if (current !== lastValueProp) {\n lastValueProp = current;\n\n const parsed = parseTime(current);\n\n selectedTime.value = parsed;\n\n if (parsed) {\n pendingHour.value = parsed.hours;\n pendingMinute.value = parsed.minutes;\n pendingPeriod.value = parsed.hours >= 12 ? 'PM' : 'AM';\n }\n }\n }, 50);\n\n return () => clearInterval(interval);\n });\n\n // ── Commit ────────────────────────────────────────────────────────────────\n\n function commit(h24: number, m: number): void {\n const clamped = clampTime(h24, m, props.min.value, props.max.value);\n const snapped = Math.round(clamped.minutes / minuteStep.value) * minuteStep.value;\n const snappedMin = Math.min(snapped, 59);\n\n selectedTime.value = { hours: clamped.hours, minutes: snappedMin };\n setFieldValue(el, formatTime(clamped.hours, snappedMin));\n dispatchNativeFieldEvent(el, 'input');\n dispatchNativeFieldEvent(el, 'change');\n }\n\n function commitFromMinute(m: number): void {\n const h24 = fmt.value === '12' ? to24Hour(pendingHour.value, pendingPeriod.value) : pendingHour.value;\n\n pendingMinute.value = m;\n commit(h24, m);\n closePicker();\n }\n\n // ── Open / close ──────────────────────────────────────────────────────────\n\n function openPicker(): void {\n if (isDisabled.value) return;\n\n const current = selectedTime.value;\n\n if (current) {\n pendingHour.value = fmt.value === '12' ? toDisplayHour(current.hours) : current.hours;\n pendingMinute.value = current.minutes;\n pendingPeriod.value = current.hours >= 12 ? 'PM' : 'AM';\n } else {\n pendingHour.value = fmt.value === '12' ? 12 : 0;\n pendingMinute.value = 0;\n pendingPeriod.value = 'AM';\n }\n\n isOpen.value = true;\n positioner.update();\n stopAutoUpdate = positioner.startAutoUpdate?.() ?? null;\n }\n\n function closePicker(): void {\n isOpen.value = false;\n stopAutoUpdate?.();\n stopAutoUpdate = null;\n }\n\n function handleTriggerClick(): void {\n if (isOpen.value) closePicker();\n else openPicker();\n }\n\n // ── Outside click ─────────────────────────────────────────────────────────\n\n function handleOutsideClick(e: PointerEvent | MouseEvent): void {\n if (!isOpen.value) return;\n\n if (!e.composedPath().includes(el)) closePicker();\n }\n\n onMounted(() => {\n document.addEventListener('pointerdown', handleOutsideClick, { capture: true });\n\n return () => {\n document.removeEventListener('pointerdown', handleOutsideClick, { capture: true });\n stopAutoUpdate?.();\n stopAutoUpdate = null;\n };\n });\n\n // ── Keyboard ──────────────────────────────────────────────────────────────\n\n function handleTriggerKeydown(e: KeyboardEvent): void {\n if (isDisabled.value) return;\n\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleTriggerClick();\n } else if (e.key === 'Escape' && isOpen.value) {\n e.preventDefault();\n closePicker();\n } else if ((e.key === 'ArrowDown' || e.key === 'ArrowUp') && !isOpen.value) {\n e.preventDefault();\n openPicker();\n }\n }\n\n function handleDropdownKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n closePicker();\n }\n }\n\n function handleHourKeydown(e: KeyboardEvent): void {\n const opts = hourOptions.value;\n const idx = opts.indexOf(pendingHour.value);\n\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n pendingHour.value = opts[(idx - 1 + opts.length) % opts.length];\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n pendingHour.value = opts[(idx + 1) % opts.length];\n }\n }\n\n function handleMinuteKeydown(e: KeyboardEvent): void {\n const opts = minuteOptions.value;\n const idx = opts.indexOf(pendingMinute.value);\n\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n pendingMinute.value = opts[(idx - 1 + opts.length) % opts.length];\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n pendingMinute.value = opts[(idx + 1) % opts.length];\n }\n }\n\n function handlePeriodKeydown(e: KeyboardEvent): void {\n if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n e.preventDefault();\n pendingPeriod.value = pendingPeriod.value === 'AM' ? 'PM' : 'AM';\n }\n }\n\n // ── Host bindings ─────────────────────────────────────────────────────────\n\n bind({\n attr: {\n open: () => (isOpen.value ? true : undefined),\n },\n });\n\n // ── ore-input prop helpers ────────────────────────────────────────────────\n\n const inputValue = () => triggerText.value;\n const inputLabel = () => props.label.value ?? '';\n const inputPlaceholder = () => props.placeholder.value ?? '';\n const inputLabelPlacement = () => props['label-placement'].value ?? 'inset';\n const inputColor = () => props.color?.value ?? undefined;\n const inputSize = () => props.size?.value ?? undefined;\n const inputVariant = () => props.variant?.value ?? undefined;\n const inputRounded = () => props.rounded?.value ?? undefined;\n const inputHelper = () => props.helper.value ?? '';\n const inputError = () => props.error.value ?? '';\n const inputDisabled = () => (isDisabled.value ? true : undefined);\n const inputRequired = () => (props.required.value ? true : undefined);\n const inputFullwidth = () => (props.fullwidth.value ? true : undefined);\n\n return html`\n <!-- Trigger -->\n <ore-input\n class=\"trigger\"\n ref=\"${triggerRef}\"\n readonly\n tabindex=\"0\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-controls=\"${dialogId}\"\n aria-expanded=\"${() => String(isOpen.value)}\"\n aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n value=\"${inputValue}\"\n label=\"${inputLabel}\"\n placeholder=\"${inputPlaceholder}\"\n label-placement=\"${inputLabelPlacement}\"\n color=\"${inputColor}\"\n size=\"${inputSize}\"\n variant=\"${inputVariant}\"\n rounded=\"${inputRounded}\"\n helper=\"${inputHelper}\"\n error=\"${inputError}\"\n ?disabled=\"${inputDisabled}\"\n ?required=\"${inputRequired}\"\n ?fullwidth=\"${inputFullwidth}\"\n ?success=\"${() => props.success.value}\"\n @click=\"${handleTriggerClick}\"\n @keydown=\"${handleTriggerKeydown}\">\n <ore-icon slot=\"suffix\" name=\"clock\" size=\"16\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n </ore-input>\n\n <!-- Dropdown -->\n <div\n class=\"dropdown\"\n ref=\"${dropdownRef}\"\n id=\"${dialogId}\"\n role=\"listbox\"\n aria-label=\"${() => (props.label.value ? `${props.label.value}: select time` : 'Select time')}\"\n ?data-open=\"${isOpen}\"\n @keydown=\"${handleDropdownKeydown}\">\n <div class=\"cols-row\">\n <!-- Hours column -->\n <div class=\"col\" part=\"column\" role=\"group\" aria-label=\"Hours\">\n <div class=\"col-label\" aria-hidden=\"true\">HH</div>\n <div class=\"col-scroll\">\n ${() =>\n hourOptions.value.map((h) => {\n const h24 = fmt.value === '12' ? to24Hour(h, pendingPeriod.value) : h;\n const disabled = isTimeDisabled(h24, pendingMinute.value);\n\n return html`\n <div\n class=\"option\"\n part=\"option\"\n role=\"option\"\n tabindex=\"${() => (pendingHour.value === h && !disabled ? '0' : '-1')}\"\n aria-selected=\"${() => String(pendingHour.value === h)}\"\n aria-disabled=\"${() => String(disabled)}\"\n ?data-disabled=\"${() => disabled}\"\n @click=\"${() => {\n if (!disabled) pendingHour.value = h;\n }}\"\n @keydown=\"${handleHourKeydown}\">\n ${String(h).padStart(2, '0')}\n </div>\n `;\n })}\n </div>\n </div>\n\n <div class=\"col-sep\" aria-hidden=\"true\">:</div>\n\n <!-- Minutes column -->\n <div class=\"col\" part=\"column\" role=\"group\" aria-label=\"Minutes\">\n <div class=\"col-label\" aria-hidden=\"true\">MM</div>\n <div class=\"col-scroll\">\n ${() =>\n minuteOptions.value.map((m) => {\n const h24 = fmt.value === '12' ? to24Hour(pendingHour.value, pendingPeriod.value) : pendingHour.value;\n const disabled = isTimeDisabled(h24, m);\n\n return html`\n <div\n class=\"option\"\n part=\"option\"\n role=\"option\"\n tabindex=\"${() => (pendingMinute.value === m && !disabled ? '0' : '-1')}\"\n aria-selected=\"${() => String(pendingMinute.value === m)}\"\n aria-disabled=\"${() => String(disabled)}\"\n ?data-disabled=\"${() => disabled}\"\n @click=\"${() => {\n if (!disabled) commitFromMinute(m);\n }}\"\n @keydown=\"${handleMinuteKeydown}\">\n ${String(m).padStart(2, '0')}\n </div>\n `;\n })}\n </div>\n </div>\n\n <!-- AM/PM column (12h mode only) -->\n ${() =>\n fmt.value === '12'\n ? html`\n <div class=\"col col-period\" part=\"column\" role=\"group\" aria-label=\"Period\">\n <div class=\"col-label\" aria-hidden=\"true\">AM/PM</div>\n <div class=\"col-scroll\">\n ${(['AM', 'PM'] as const).map(\n (p) => html`\n <div\n class=\"option\"\n part=\"option\"\n role=\"option\"\n tabindex=\"${() => (pendingPeriod.value === p ? '0' : '-1')}\"\n aria-selected=\"${() => String(pendingPeriod.value === p)}\"\n @click=\"${() => {\n pendingPeriod.value = p;\n }}\"\n @keydown=\"${handlePeriodKeydown}\">\n ${p}\n </div>\n `,\n )}\n </div>\n </div>\n `\n : html``}\n </div>\n </div>\n `;\n },\n\n shadow: { delegatesFocus: true },\n styles: [colorThemeMixin, reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"time-picker.js","names":[],"sources":["../src/inputs/time-picker/time-picker.ts"],"sourcesContent":["import { bind, define, getHost, html, onMounted, prop, ref, useField } from '@vielzeug/ore';\nimport { computed, signal } from '@vielzeug/ripple';\nimport { createDropdownPositioner } from '../../core';\nimport {\n disablableBundle,\n FIELD_VARIANTS,\n type FieldVariant,\n roundableBundle,\n sizableBundle,\n themableBundle,\n} from '../../shared';\nimport { colorThemeMixin, reducedMotionMixin } from '../../styles';\nimport { defineFieldValue, dispatchNativeFieldEvent, setFieldValue } from '../shared/native-field-event';\nimport componentStyles from './time-picker.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\nexport { INPUT_TAG } from '../input/input';\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type OreTimePickerEvents = {\n change: Event;\n input: Event;\n};\n\nexport type OreTimePickerProps = {\n /** Theme color */\n color?: string;\n /** Disable the picker */\n disabled?: boolean;\n /** Validation error message */\n error?: string;\n /** Expand to container width */\n fullwidth?: boolean;\n /** Helper text below the field */\n helper?: string;\n /** Visible label */\n label?: string;\n /** Label placement */\n 'label-placement'?: 'inset' | 'outside';\n /** Maximum selectable time in HH:MM format (24-hour) */\n max?: string;\n /** Minimum selectable time in HH:MM format (24-hour) */\n min?: string;\n /** Minute step interval (1–59) */\n 'minute-step'?: number;\n /** Form field name */\n name?: string;\n /** Placeholder shown in the trigger */\n placeholder?: string;\n /** Mark field as required */\n required?: boolean;\n /** Border radius */\n rounded?: string;\n /** Component size */\n size?: string;\n /**\n * Shows an inline green check icon inside the field to confirm the value has\n * passed validation. Ignored while `error` is set: an error always wins.\n */\n success?: boolean;\n /** Display format: '12' for AM/PM, '24' for 24-hour (default: '24') */\n 'time-format'?: '12' | '24';\n /** Selected time in HH:MM format (24-hour) */\n value?: string;\n /** Visual variant */\n variant?: FieldVariant;\n};\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/** Parse \"HH:MM\" → { hours: 0-23, minutes: 0-59 } | null */\nfunction parseTime(val: string | undefined | null): { hours: number; minutes: number } | null {\n if (!val) return null;\n\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(val.trim());\n\n if (!match) return null;\n\n const h = Number(match[1]);\n const m = Number(match[2]);\n\n if (h < 0 || h > 23 || m < 0 || m > 59) return null;\n\n return { hours: h, minutes: m };\n}\n\n/** Format { hours, minutes } → \"HH:MM\" (24-hour, zero-padded) */\nfunction formatTime(h: number, m: number): string {\n return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;\n}\n\n/** Format for display label (respects 12/24 mode) */\nfunction formatDisplay(h: number, m: number, format: '12' | '24'): string {\n if (format === '12') {\n const period = h >= 12 ? 'PM' : 'AM';\n const displayH = h % 12 === 0 ? 12 : h % 12;\n\n return `${String(displayH).padStart(2, '0')}:${String(m).padStart(2, '0')} ${period}`;\n }\n\n return formatTime(h, m);\n}\n\n/** Clamp value to [min, max] in total minutes */\nfunction clampTime(\n h: number,\n m: number,\n minVal: string | undefined | null,\n maxVal: string | undefined | null,\n): { hours: number; minutes: number } {\n const total = h * 60 + m;\n const minParsed = parseTime(minVal);\n const maxParsed = parseTime(maxVal);\n const minMins = minParsed ? minParsed.hours * 60 + minParsed.minutes : 0;\n const maxMins = maxParsed ? maxParsed.hours * 60 + maxParsed.minutes : 23 * 60 + 59;\n const clamped = Math.min(Math.max(total, minMins), maxMins);\n\n return { hours: Math.floor(clamped / 60), minutes: clamped % 60 };\n}\n\n/**\n * An accessible time picker with a scrollable clock dropdown.\n * Supports 12/24-hour display formats, min/max bounds, minute steps, and form association.\n *\n * @element ore-time-picker\n *\n * @attr {string} value - Selected time in HH:MM (24-hour) format\n * @attr {string} min - Minimum selectable time (HH:MM)\n * @attr {string} max - Maximum selectable time (HH:MM)\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} placeholder - Trigger placeholder\n * @attr {boolean} disabled - Disable the picker\n * @attr {boolean} required - Required field\n * @attr {string} name - Form field name\n * @attr {string} error - Error message\n * @attr {boolean} success - Show an inline success check icon (suppressed while `error` is set)\n * @attr {string} helper - Helper text\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} time-format - '12' or '24' (default: '24')\n * @attr {number} minute-step - Minute increment (default: 5)\n *\n * @fires input - Fired when a time is selected.\n * @fires change - Fired when a time is selected.\n *\n * @slot label - Custom label for the trigger field\n * @slot prefix - Content before the trigger text (e.g. icon)\n * @slot helper - Custom helper text\n * @slot error - Custom error content\n *\n * @cssprop --time-picker-bg - Dropdown background\n * @cssprop --time-picker-border-color - Dropdown border color\n * @cssprop --time-picker-radius - Dropdown border radius\n * @cssprop --time-picker-shadow - Dropdown shadow\n * @cssprop --time-picker-selected-bg - Selected option background\n * @cssprop --time-picker-option-hover-bg - Option hover background\n *\n * @part field - The trigger field\n * @part dropdown - The floating time dropdown\n * @part column - A scrollable column (hours / minutes / period)\n * @part option - An individual time option cell\n *\n * @example\n * ```html\n * <!-- 24-hour clock, 15-minute steps -->\n * <ore-time-picker label=\"Meeting time\" value=\"09:30\" minute-step=\"15\" color=\"primary\"></ore-time-picker>\n *\n * <!-- 12-hour AM/PM format -->\n * <ore-time-picker label=\"Appointment\" time-format=\"12\" minute-step=\"30\"></ore-time-picker>\n *\n * <!-- Outside label, bordered variant -->\n * <ore-time-picker label=\"Start time\" label-placement=\"outside\" variant=\"bordered\"></ore-time-picker>\n * ```\n */\nexport const TIME_PICKER_TAG = 'ore-time-picker' as const;\n\ndefine<OreTimePickerProps>(TIME_PICKER_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...roundableBundle,\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n max: prop.string(),\n min: prop.string(),\n 'minute-step': prop.number(5),\n name: prop.string(),\n placeholder: prop.string(),\n required: prop.bool(false),\n success: prop.bool(false),\n 'time-format': prop.oneOf(['12', '24'] as const, '24'),\n value: prop.string(),\n variant: prop.oneOf(FIELD_VARIANTS),\n },\n\n setup(props) {\n const el = getHost();\n\n // ── Signals ──────────────────────────────────────────────────────────────\n\n const isOpen = signal(false);\n const selectedTime = signal<{ hours: number; minutes: number } | null>(parseTime(props.value.value));\n\n defineFieldValue(\n el,\n () => {\n const value = selectedTime.value;\n\n return value ? formatTime(value.hours, value.minutes) : '';\n },\n (value) => {\n selectedTime.value = parseTime(value);\n },\n );\n\n // ── Floating position ────────────────────────────────────────────────────\n // Same Orbit-powered positioner ore-date-picker's `.calendar` popup uses (see its own\n // comment for the full rationale): `fixed` + computed `top`/`left` instead of a static\n // `position: absolute; top: 100%`, so the dropdown flips/shifts to stay within the nearest\n // clipping ancestor instead of silently overflowing it.\n const triggerRef = ref<HTMLElement>();\n const dropdownRef = ref<HTMLElement>();\n\n const positioner = createDropdownPositioner({\n getFloating: () => dropdownRef.value ?? null,\n getOffsetPx: () => 4,\n getReference: () => triggerRef.value ?? null,\n matchWidth: false,\n });\n\n let stopAutoUpdate: (() => void) | null = null;\n\n const isDisabled = computed(() => Boolean(props.disabled.value));\n\n // ── Form value ────────────────────────────────────────────────────────────\n\n useField<string>({\n disabled: isDisabled,\n toFormValue: (v) => v || null,\n value: computed(() => {\n const t = selectedTime.value;\n\n return t ? formatTime(t.hours, t.minutes) : '';\n }),\n });\n\n // ── Computed helpers ──────────────────────────────────────────────────────\n\n const fmt = computed(() => props['time-format'].value ?? '24');\n const minuteStep = computed(() => Math.max(1, Math.min(59, props['minute-step'].value ?? 5)));\n\n const triggerText = computed(() => {\n if (!selectedTime.value) return props.placeholder.value || '';\n\n return formatDisplay(selectedTime.value.hours, selectedTime.value.minutes, fmt.value as '12' | '24');\n });\n\n /** All hour options for the column (0-23 in 24h; 1-12 in 12h) */\n const hourOptions = computed<number[]>(() => {\n if (fmt.value === '12') {\n return Array.from({ length: 12 }, (_, i) => i + 1);\n }\n\n return Array.from({ length: 24 }, (_, i) => i);\n });\n\n /** All minute options for the column, stepped */\n const minuteOptions = computed<number[]>(() => {\n const step = minuteStep.value;\n const opts: number[] = [];\n\n for (let m = 0; m < 60; m += step) opts.push(m);\n\n return opts;\n });\n\n const dialogId = `time-picker-${Math.random().toString(36).slice(2, 9)}-dropdown`;\n\n /** Convert 24h hour to display hour in current format */\n function toDisplayHour(h24: number): number {\n if (fmt.value === '24') return h24;\n\n const h12 = h24 % 12;\n\n return h12 === 0 ? 12 : h12;\n }\n\n /** Convert display hour + period → 24h hour */\n function to24Hour(displayHour: number, period: 'AM' | 'PM'): number {\n if (fmt.value === '24') return displayHour;\n\n if (period === 'AM') return displayHour === 12 ? 0 : displayHour;\n\n return displayHour === 12 ? 12 : displayHour + 12;\n }\n\n /** Whether a given HH:MM is out of [min, max] */\n function isTimeDisabled(h: number, m: number): boolean {\n const total = h * 60 + m;\n const minParsed = parseTime(props.min.value);\n const maxParsed = parseTime(props.max.value);\n\n if (minParsed && total < minParsed.hours * 60 + minParsed.minutes) return true;\n\n if (maxParsed && total > maxParsed.hours * 60 + maxParsed.minutes) return true;\n\n return false;\n }\n\n // ── State for focused/pending selection ───────────────────────────────────\n\n /** Pending hour (24h) while user is navigating columns */\n const pendingHour = signal<number>(selectedTime.value?.hours ?? 0);\n /** Pending minute */\n const pendingMinute = signal<number>(selectedTime.value?.minutes ?? 0);\n /** Pending period (12h mode) */\n const pendingPeriod = signal<'AM' | 'PM'>((selectedTime.value?.hours ?? 0) >= 12 ? 'PM' : 'AM');\n\n // ── Sync value prop externally ────────────────────────────────────────────\n\n let lastValueProp = props.value.value;\n\n onMounted(() => {\n const interval = setInterval(() => {\n const current = props.value.value;\n\n if (current !== lastValueProp) {\n lastValueProp = current;\n\n const parsed = parseTime(current);\n\n selectedTime.value = parsed;\n\n if (parsed) {\n pendingHour.value = parsed.hours;\n pendingMinute.value = parsed.minutes;\n pendingPeriod.value = parsed.hours >= 12 ? 'PM' : 'AM';\n }\n }\n }, 50);\n\n return () => clearInterval(interval);\n });\n\n // ── Commit ────────────────────────────────────────────────────────────────\n\n function commit(h24: number, m: number): void {\n const clamped = clampTime(h24, m, props.min.value, props.max.value);\n const snapped = Math.round(clamped.minutes / minuteStep.value) * minuteStep.value;\n const snappedMin = Math.min(snapped, 59);\n\n selectedTime.value = { hours: clamped.hours, minutes: snappedMin };\n setFieldValue(el, formatTime(clamped.hours, snappedMin));\n dispatchNativeFieldEvent(el, 'input');\n dispatchNativeFieldEvent(el, 'change');\n }\n\n function commitFromMinute(m: number): void {\n const h24 = fmt.value === '12' ? to24Hour(pendingHour.value, pendingPeriod.value) : pendingHour.value;\n\n pendingMinute.value = m;\n commit(h24, m);\n closePicker();\n }\n\n // ── Open / close ──────────────────────────────────────────────────────────\n\n function openPicker(): void {\n if (isDisabled.value) return;\n\n const current = selectedTime.value;\n\n if (current) {\n pendingHour.value = fmt.value === '12' ? toDisplayHour(current.hours) : current.hours;\n pendingMinute.value = current.minutes;\n pendingPeriod.value = current.hours >= 12 ? 'PM' : 'AM';\n } else {\n pendingHour.value = fmt.value === '12' ? 12 : 0;\n pendingMinute.value = 0;\n pendingPeriod.value = 'AM';\n }\n\n isOpen.value = true;\n positioner.update();\n stopAutoUpdate = positioner.startAutoUpdate?.() ?? null;\n }\n\n function closePicker(): void {\n isOpen.value = false;\n stopAutoUpdate?.();\n stopAutoUpdate = null;\n }\n\n function handleTriggerClick(): void {\n if (isOpen.value) closePicker();\n else openPicker();\n }\n\n // ── Outside click ─────────────────────────────────────────────────────────\n\n function handleOutsideClick(e: PointerEvent | MouseEvent): void {\n if (!isOpen.value) return;\n\n if (!e.composedPath().includes(el)) closePicker();\n }\n\n onMounted(() => {\n document.addEventListener('pointerdown', handleOutsideClick, { capture: true });\n\n return () => {\n document.removeEventListener('pointerdown', handleOutsideClick, { capture: true });\n stopAutoUpdate?.();\n stopAutoUpdate = null;\n };\n });\n\n // ── Keyboard ──────────────────────────────────────────────────────────────\n\n function handleTriggerKeydown(e: KeyboardEvent): void {\n if (isDisabled.value) return;\n\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleTriggerClick();\n } else if (e.key === 'Escape' && isOpen.value) {\n e.preventDefault();\n closePicker();\n } else if ((e.key === 'ArrowDown' || e.key === 'ArrowUp') && !isOpen.value) {\n e.preventDefault();\n openPicker();\n }\n }\n\n function handleDropdownKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n closePicker();\n }\n }\n\n function handleHourKeydown(e: KeyboardEvent): void {\n const opts = hourOptions.value;\n const idx = opts.indexOf(pendingHour.value);\n\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n pendingHour.value = opts[(idx - 1 + opts.length) % opts.length];\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n pendingHour.value = opts[(idx + 1) % opts.length];\n }\n }\n\n function handleMinuteKeydown(e: KeyboardEvent): void {\n const opts = minuteOptions.value;\n const idx = opts.indexOf(pendingMinute.value);\n\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n pendingMinute.value = opts[(idx - 1 + opts.length) % opts.length];\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n pendingMinute.value = opts[(idx + 1) % opts.length];\n }\n }\n\n function handlePeriodKeydown(e: KeyboardEvent): void {\n if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n e.preventDefault();\n pendingPeriod.value = pendingPeriod.value === 'AM' ? 'PM' : 'AM';\n }\n }\n\n // ── Host bindings ─────────────────────────────────────────────────────────\n\n bind({\n attr: {\n open: () => (isOpen.value ? true : undefined),\n },\n });\n\n // ── ore-input prop helpers ────────────────────────────────────────────────\n\n const inputValue = () => triggerText.value;\n const inputLabel = () => props.label.value ?? '';\n const inputPlaceholder = () => props.placeholder.value ?? '';\n const inputLabelPlacement = () => props['label-placement'].value ?? 'inset';\n const inputColor = () => props.color?.value ?? undefined;\n const inputSize = () => props.size?.value ?? undefined;\n const inputVariant = () => props.variant?.value ?? undefined;\n const inputRounded = () => props.rounded?.value ?? undefined;\n const inputHelper = () => props.helper.value ?? '';\n const inputError = () => props.error.value ?? '';\n const inputDisabled = () => (isDisabled.value ? true : undefined);\n const inputRequired = () => (props.required.value ? true : undefined);\n const inputFullwidth = () => (props.fullwidth.value ? true : undefined);\n\n return html`\n <!-- Trigger -->\n <ore-input\n class=\"trigger\"\n ref=\"${triggerRef}\"\n readonly\n tabindex=\"0\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-controls=\"${dialogId}\"\n aria-expanded=\"${() => String(isOpen.value)}\"\n aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n value=\"${inputValue}\"\n label=\"${inputLabel}\"\n placeholder=\"${inputPlaceholder}\"\n label-placement=\"${inputLabelPlacement}\"\n color=\"${inputColor}\"\n size=\"${inputSize}\"\n variant=\"${inputVariant}\"\n rounded=\"${inputRounded}\"\n helper=\"${inputHelper}\"\n error=\"${inputError}\"\n ?disabled=\"${inputDisabled}\"\n ?required=\"${inputRequired}\"\n ?fullwidth=\"${inputFullwidth}\"\n ?success=\"${() => props.success.value}\"\n @click=\"${handleTriggerClick}\"\n @keydown=\"${handleTriggerKeydown}\">\n <ore-icon slot=\"suffix\" name=\"clock\" size=\"16\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n </ore-input>\n\n <!-- Dropdown -->\n <div\n class=\"dropdown\"\n ref=\"${dropdownRef}\"\n id=\"${dialogId}\"\n role=\"listbox\"\n aria-label=\"${() => (props.label.value ? `${props.label.value}: select time` : 'Select time')}\"\n ?data-open=\"${isOpen}\"\n @keydown=\"${handleDropdownKeydown}\">\n <div class=\"cols-row\">\n <!-- Hours column -->\n <div class=\"col\" part=\"column\" role=\"group\" aria-label=\"Hours\">\n <div class=\"col-label\" aria-hidden=\"true\">HH</div>\n <div class=\"col-scroll\">\n ${() =>\n hourOptions.value.map((h) => {\n const h24 = fmt.value === '12' ? to24Hour(h, pendingPeriod.value) : h;\n const disabled = isTimeDisabled(h24, pendingMinute.value);\n\n return html`\n <div\n class=\"option\"\n part=\"option\"\n role=\"option\"\n tabindex=\"${() => (pendingHour.value === h && !disabled ? '0' : '-1')}\"\n aria-selected=\"${() => String(pendingHour.value === h)}\"\n aria-disabled=\"${() => String(disabled)}\"\n ?data-disabled=\"${() => disabled}\"\n @click=\"${() => {\n if (!disabled) pendingHour.value = h;\n }}\"\n @keydown=\"${handleHourKeydown}\">\n ${String(h).padStart(2, '0')}\n </div>\n `;\n })}\n </div>\n </div>\n\n <div class=\"col-sep\" aria-hidden=\"true\">:</div>\n\n <!-- Minutes column -->\n <div class=\"col\" part=\"column\" role=\"group\" aria-label=\"Minutes\">\n <div class=\"col-label\" aria-hidden=\"true\">MM</div>\n <div class=\"col-scroll\">\n ${() =>\n minuteOptions.value.map((m) => {\n const h24 = fmt.value === '12' ? to24Hour(pendingHour.value, pendingPeriod.value) : pendingHour.value;\n const disabled = isTimeDisabled(h24, m);\n\n return html`\n <div\n class=\"option\"\n part=\"option\"\n role=\"option\"\n tabindex=\"${() => (pendingMinute.value === m && !disabled ? '0' : '-1')}\"\n aria-selected=\"${() => String(pendingMinute.value === m)}\"\n aria-disabled=\"${() => String(disabled)}\"\n ?data-disabled=\"${() => disabled}\"\n @click=\"${() => {\n if (!disabled) commitFromMinute(m);\n }}\"\n @keydown=\"${handleMinuteKeydown}\">\n ${String(m).padStart(2, '0')}\n </div>\n `;\n })}\n </div>\n </div>\n\n <!-- AM/PM column (12h mode only) -->\n ${() =>\n fmt.value === '12'\n ? html`\n <div class=\"col col-period\" part=\"column\" role=\"group\" aria-label=\"Period\">\n <div class=\"col-label\" aria-hidden=\"true\">AM/PM</div>\n <div class=\"col-scroll\">\n ${(['AM', 'PM'] as const).map(\n (p) => html`\n <div\n class=\"option\"\n part=\"option\"\n role=\"option\"\n tabindex=\"${() => (pendingPeriod.value === p ? '0' : '-1')}\"\n aria-selected=\"${() => String(pendingPeriod.value === p)}\"\n @click=\"${() => {\n pendingPeriod.value = p;\n }}\"\n @keydown=\"${handlePeriodKeydown}\">\n ${p}\n </div>\n `,\n )}\n </div>\n </div>\n `\n : html``}\n </div>\n </div>\n `;\n },\n\n shadow: { delegatesFocus: true },\n styles: [colorThemeMixin, reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
package/dist/tooltip.cjs CHANGED
@@ -1,4 +1,4 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/theme.css.cjs"),t=require("./core/parse.cjs"),n=require("./shared/prop-bundles.cjs"),r=require("./overlay/shared/use-floating-trigger.cjs"),i=require("./overlay/tooltip/tooltip.css.cjs");let a=require("@vielzeug/ripple"),o=require("@vielzeug/ore");var s=4,c=[`hover`,`focus`],l=new Set([`click`,`focus`,`hover`]),parseDelayMs=e=>{if(e==null||e.trim()===``)return 0;let t=Number(e);return Number.isFinite(t)&&t>=0?t:0},normalizeTriggers=e=>t.parseStringTriggers(e,l,c),u=`ore-tooltip`;(0,o.define)(u,{props:{...n.sizableBundle,...n.disablableBundle,"close-delay":{default:0,parse:parseDelayMs},content:o.prop.string(),"default-open":o.prop.bool(!1),delay:{default:0,parse:parseDelayMs},open:{default:void 0,parse:t.parseOptionalBool},placement:o.prop.oneOf([`top`,`bottom`,`left`,`right`],`top`),trigger:o.prop.string(`hover,focus`),variant:o.prop.string()},setup(e){let t=(0,o.getHost)(),n=(0,o.useEmit)(),i=(0,o.useSlots)(),c=t.shadowRoot,l=(0,a.computed)(()=>!!e.disabled.value),u=(0,a.computed)(()=>e.open.value!==void 0),d=(0,a.signal)(`top`),f=(0,o.createId)(`tooltip`),p=(0,a.computed)(()=>normalizeTriggers(e.trigger.value)),m=null,h=null,g=null,clearShowTimer=()=>{h&&=(clearTimeout(h),null)},clearHideTimer=()=>{g&&=(clearTimeout(g),null)},_=r.useFloatingTrigger({bindTriggerAria:e=>(0,o.bind)({attr:{"aria-describedby":()=>f}},{target:e}),defaultOpen:e[`default-open`],disabled:l,getPanel:()=>m,onCleanup:o.onCleanup,onClose:e=>n(`open-change`,{open:!1,reason:e}),onOpen:e=>n(`open-change`,{open:!0,reason:e}),onPlacementChange:e=>{let t=e.split(`-`)[0];d.value=t,t===`left`&&m&&(m.style.left=`${parseFloat(m.style.left)-s}px`)},openProp:e.open,placement:(0,a.computed)(()=>e.placement.value),slot:()=>c?.querySelector(`slot:not([name])`)??null,slotElements:i.elements(),triggers:(0,a.computed)(()=>[])});function show(t=`hover`){if(u.value||l.value||!e.content.value&&!i.has(`content`).value)return;clearHideTimer(),clearShowTimer();let n=Number(e.delay.value)||0;n>0?h=setTimeout(()=>{h=null,_.open(t),_.updatePosition()},n):(_.open(t),_.updatePosition())}function hide(){if(u.value)return;clearShowTimer();let t=Number(e[`close-delay`].value)||0;t>0?(clearHideTimer(),g=setTimeout(()=>{g=null,_.close(`trigger`)},t)):_.close(`trigger`)}return(0,o.onMounted)(()=>{let e=c?.querySelector(`slot:not([name])`),t=[],addEvent=(e,n,r)=>{e.addEventListener(n,r),t.push(()=>e.removeEventListener(n,r))},bindTriggerEvents=()=>{for(let e of t)e();t=[];let n=e?.assignedElements({flatten:!0})[0];if(!n)return;let r=p.value;r.includes(`hover`)&&(addEvent(n,`pointerenter`,()=>show(`hover`)),addEvent(n,`pointerleave`,hide)),r.includes(`focus`)&&(addEvent(n,`focusin`,()=>show(`focus`)),addEvent(n,`focusout`,hide)),r.includes(`click`)&&addEvent(n,`click`,()=>_.visible.value?hide():show(`click`))};e?.addEventListener(`slotchange`,bindTriggerEvents),bindTriggerEvents();let n=_.mount();return()=>{e?.removeEventListener(`slotchange`,bindTriggerEvents);for(let e of t)e();clearShowTimer(),clearHideTimer(),n()}}),o.html`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/theme.css.cjs"),t=require("./core/parse.cjs"),n=require("./shared/prop-bundles.cjs"),r=require("./overlay/shared/use-floating-trigger.cjs"),i=require("./overlay/tooltip/tooltip.css.cjs");let a=require("@vielzeug/ripple"),o=require("@vielzeug/ore");var s=4,c=[`hover`,`focus`],l=new Set([`click`,`focus`,`hover`]),parseDelayMs=e=>{if(e==null||e.trim()===``)return 0;let t=Number(e);return Number.isFinite(t)&&t>=0?t:0},normalizeTriggers=e=>t.parseStringTriggers(e,l,c),u=`ore-tooltip`;(0,o.define)(u,{props:{...n.sizableBundle,...n.disablableBundle,"close-delay":{default:0,parse:parseDelayMs},content:o.prop.string(),"default-open":o.prop.bool(!1),delay:{default:0,parse:parseDelayMs},open:{default:void 0,parse:t.parseOptionalBool},placement:o.prop.oneOf([`top`,`bottom`,`left`,`right`],`top`),trigger:o.prop.string(`hover,focus`),variant:o.prop.oneOf([`dark`,`light`])},setup(e){let t=(0,o.getHost)(),n=(0,o.useEmit)(),i=(0,o.useSlots)(),c=t.shadowRoot,l=(0,a.computed)(()=>!!e.disabled.value),u=(0,a.computed)(()=>e.open.value!==void 0),d=(0,a.signal)(`top`),f=(0,o.createId)(`tooltip`),p=(0,a.computed)(()=>normalizeTriggers(e.trigger.value)),m=null,h=null,g=null,clearShowTimer=()=>{h&&=(clearTimeout(h),null)},clearHideTimer=()=>{g&&=(clearTimeout(g),null)},_=r.useFloatingTrigger({bindTriggerAria:e=>(0,o.bind)({attr:{"aria-describedby":()=>f}},{target:e}),defaultOpen:e[`default-open`],disabled:l,getPanel:()=>m,onCleanup:o.onCleanup,onClose:e=>n(`open-change`,{open:!1,reason:e}),onOpen:e=>n(`open-change`,{open:!0,reason:e}),onPlacementChange:e=>{let t=e.split(`-`)[0];d.value=t,t===`left`&&m&&(m.style.left=`${parseFloat(m.style.left)-s}px`)},openProp:e.open,placement:(0,a.computed)(()=>e.placement.value),slot:()=>c?.querySelector(`slot:not([name])`)??null,slotElements:i.elements(),triggers:(0,a.computed)(()=>[])});function show(t=`hover`){if(u.value||l.value||!e.content.value&&!i.has(`content`).value)return;clearHideTimer(),clearShowTimer();let n=Number(e.delay.value)||0;n>0?h=setTimeout(()=>{h=null,_.open(t),_.updatePosition()},n):(_.open(t),_.updatePosition())}function hide(){if(u.value)return;clearShowTimer();let t=Number(e[`close-delay`].value)||0;t>0?(clearHideTimer(),g=setTimeout(()=>{g=null,_.close(`trigger`)},t)):_.close(`trigger`)}return(0,o.onMounted)(()=>{let e=c?.querySelector(`slot:not([name])`),t=[],addEvent=(e,n,r)=>{e.addEventListener(n,r),t.push(()=>e.removeEventListener(n,r))},bindTriggerEvents=()=>{for(let e of t)e();t=[];let n=e?.assignedElements({flatten:!0})[0];if(!n)return;let r=p.value;r.includes(`hover`)&&(addEvent(n,`pointerenter`,()=>show(`hover`)),addEvent(n,`pointerleave`,hide)),r.includes(`focus`)&&(addEvent(n,`focusin`,()=>show(`focus`)),addEvent(n,`focusout`,hide)),r.includes(`click`)&&addEvent(n,`click`,()=>_.visible.value?hide():show(`click`))};e?.addEventListener(`slotchange`,bindTriggerEvents),bindTriggerEvents();let n=_.mount();return()=>{e?.removeEventListener(`slotchange`,bindTriggerEvents);for(let e of t)e();clearShowTimer(),clearHideTimer(),n()}}),o.html`
2
2
  <slot></slot>
3
3
  <div
4
4
  class="tooltip"
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.cjs","names":[],"sources":["../src/overlay/tooltip/tooltip.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\n\nimport { bind, createId, define, getHost, html, onCleanup, onMounted, prop, useEmit, useSlots } from '@vielzeug/ore';\nimport { computed, signal } from '@vielzeug/ripple';\nimport {\n type OverlayOpenChangeDetail,\n type OverlayOpenReason,\n parseOptionalBool,\n parseStringTriggers,\n} from '../../core';\nimport { disablableBundle, sizableBundle } from '../../shared';\nimport { forcedColorsMixin } from '../../styles';\nimport type { ComponentSize } from '../../types';\nimport { useFloatingTrigger } from '../shared/use-floating-trigger';\nimport styles from './tooltip.css?inline';\n\ntype TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';\ntype TooltipTrigger = 'click' | 'focus' | 'hover';\n\nconst LEFT_GAP_COMPENSATION = 4;\nconst DEFAULT_TOOLTIP_TRIGGERS: TooltipTrigger[] = ['hover', 'focus'];\nconst VALID_TOOLTIP_TRIGGERS = new Set<TooltipTrigger>(['click', 'focus', 'hover']);\n\nconst parseDelayMs = (value: string | null): number => {\n if (value == null || value.trim() === '') return 0;\n\n const parsed = Number(value);\n\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;\n};\n\nconst normalizeTriggers = (value: string | null | undefined): TooltipTrigger[] =>\n parseStringTriggers(value, VALID_TOOLTIP_TRIGGERS, DEFAULT_TOOLTIP_TRIGGERS);\n\n/** Tooltip component properties */\nexport type OreTooltipEvents = {\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreTooltipProps = {\n /** Hide delay in ms */\n 'close-delay'?: number;\n /** Tooltip text content */\n content?: string;\n /** Initial uncontrolled open state. Ignored when `open` is set. */\n 'default-open'?: boolean;\n /** Show delay in ms */\n delay?: number;\n /** Disable the tooltip */\n disabled?: boolean;\n /** Controlled open state */\n open?: boolean;\n /** Preferred placement relative to trigger */\n placement?: TooltipPlacement;\n /** Tooltip size */\n size?: ComponentSize;\n /** Which trigger(s) show/hide the tooltip */\n trigger?: string;\n /** Visual variant */\n variant?: 'dark' | 'light';\n};\n\n/**\n * A lightweight tooltip shown on hover/focus/click relative to the slotted trigger.\n *\n * @element ore-tooltip\n *\n * @attr {string} content - Tooltip text content\n * @attr {string} placement - 'top' | 'bottom' | 'left' | 'right' (default: 'top')\n * @attr {string} trigger - 'hover' | 'focus' | 'click' or comma-separated combination\n * @attr {number} delay - Show delay in milliseconds (default: 0)\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} variant - 'dark' (default) | 'light'\n * @attr {boolean} disabled - Disable the tooltip\n *\n * @slot - Trigger element that the tooltip is anchored to\n * @slot content - Complex tooltip content (overrides the `content` attribute)\n * @fires open-change - Fired when the tooltip state changes. detail: { open, reason }\n *\n * @cssprop --tooltip-max-width - Max width of the tooltip bubble\n *\n * @part tooltip - Tooltip container.\n * @example\n * ```html\n * <!-- Simple text tooltip -->\n * <ore-tooltip content=\"Copy to clipboard\">\n * <button>Copy</button>\n * </ore-tooltip>\n *\n * <!-- Placement and delay -->\n * <ore-tooltip content=\"Save your work\" placement=\"right\" delay=\"300\">\n * <ore-icon name=\"save\"></ore-icon>\n * </ore-tooltip>\n *\n * <!-- Hover + focus trigger -->\n * <ore-tooltip content=\"Required field\" trigger=\"hover,focus\" placement=\"top\">\n * <ore-input label=\"Email\" type=\"email\"></ore-input>\n * </ore-tooltip>\n *\n * <!-- Rich content slot -->\n * <ore-tooltip>\n * <ore-button>Help</ore-button>\n * <div slot=\"content\">\n * <strong>Keyboard shortcuts</strong>\n * <p>Press Ctrl+S to save.</p>\n * </div>\n * </ore-tooltip>\n * ```\n */\nexport const TOOLTIP_TAG = 'ore-tooltip' as const;\ndefine<OreTooltipProps>(TOOLTIP_TAG, {\n props: {\n ...sizableBundle,\n ...disablableBundle,\n 'close-delay': { default: 0, parse: parseDelayMs },\n content: prop.string(),\n 'default-open': prop.bool(false),\n delay: { default: 0, parse: parseDelayMs },\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n placement: prop.oneOf(['top', 'bottom', 'left', 'right'] as const, 'top'),\n trigger: prop.string('hover,focus'),\n variant: prop.string<'dark' | 'light'>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreTooltipEvents>();\n const slots = useSlots();\n\n const shadowRoot = el.shadowRoot;\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const isControlled = computed(() => props.open.value !== undefined);\n const activePlacement = signal<TooltipPlacement>('top');\n const tooltipId = createId('tooltip');\n const triggers = computed<TooltipTrigger[]>(() => normalizeTriggers(props.trigger.value));\n let tooltipEl: HTMLElement | null = null;\n let showTimer: ReturnType<typeof setTimeout> | null = null;\n let hideTimer: ReturnType<typeof setTimeout> | null = null;\n\n const clearShowTimer = (): void => {\n if (!showTimer) return;\n\n clearTimeout(showTimer);\n showTimer = null;\n };\n const clearHideTimer = (): void => {\n if (!hideTimer) return;\n\n clearTimeout(hideTimer);\n hideTimer = null;\n };\n\n const floating = useFloatingTrigger({\n bindTriggerAria: (triggerEl) => bind({ attr: { 'aria-describedby': () => tooltipId } }, { target: triggerEl }),\n defaultOpen: props['default-open'],\n disabled: isDisabled,\n getPanel: () => tooltipEl,\n onCleanup,\n onClose: (reason) => emit('open-change', { open: false, reason }),\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n onPlacementChange: (p) => {\n const side = p.split('-')[0] as TooltipPlacement;\n\n activePlacement.value = side;\n\n if (side === 'left' && tooltipEl) {\n tooltipEl.style.left = `${parseFloat(tooltipEl.style.left) - LEFT_GAP_COMPENSATION}px`;\n }\n },\n openProp: props.open as typeof props.open & { value: boolean | undefined },\n placement: computed(() => props.placement.value as Placement),\n slot: () => shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])') ?? null,\n slotElements: slots.elements(),\n triggers: computed(() => [] as TooltipTrigger[]),\n });\n\n function show(reason: OverlayOpenReason = 'hover'): void {\n if (isControlled.value) return;\n\n if (isDisabled.value || (!props.content.value && !slots.has('content').value)) return;\n\n clearHideTimer();\n clearShowTimer();\n\n const delay = Number(props.delay.value) || 0;\n\n if (delay > 0) {\n showTimer = setTimeout(() => {\n showTimer = null;\n floating.open(reason);\n floating.updatePosition();\n }, delay);\n } else {\n floating.open(reason);\n floating.updatePosition();\n }\n }\n\n function hide(): void {\n if (isControlled.value) return;\n\n clearShowTimer();\n\n const closeDelay = Number(props['close-delay'].value) || 0;\n\n if (closeDelay > 0) {\n clearHideTimer();\n hideTimer = setTimeout(() => {\n hideTimer = null;\n floating.close('trigger');\n }, closeDelay);\n } else {\n floating.close('trigger');\n }\n }\n\n onMounted(() => {\n // Manually bind trigger events so we can wrap show/hide with delay timers.\n // useFloatingTrigger handles ARIA, positioning, controlled mode, and keyboard dismiss.\n const triggerSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n let eventCleanups: Array<() => void> = [];\n\n const addEvent = (target: EventTarget, event: string, handler: EventListener): void => {\n target.addEventListener(event, handler);\n eventCleanups.push(() => target.removeEventListener(event, handler));\n };\n\n const bindTriggerEvents = (): void => {\n for (const cleanup of eventCleanups) cleanup();\n eventCleanups = [];\n\n const triggerEl = triggerSlot?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;\n\n if (!triggerEl) return;\n\n const t = triggers.value;\n\n if (t.includes('hover')) {\n addEvent(triggerEl, 'pointerenter', () => show('hover'));\n addEvent(triggerEl, 'pointerleave', hide);\n }\n\n if (t.includes('focus')) {\n addEvent(triggerEl, 'focusin', () => show('focus'));\n addEvent(triggerEl, 'focusout', hide);\n }\n\n if (t.includes('click')) {\n addEvent(triggerEl, 'click', () => (floating.visible.value ? hide() : show('click')));\n }\n };\n\n triggerSlot?.addEventListener('slotchange', bindTriggerEvents);\n bindTriggerEvents();\n\n const destroyFloating = floating.mount();\n\n return () => {\n triggerSlot?.removeEventListener('slotchange', bindTriggerEvents);\n for (const cleanup of eventCleanups) cleanup();\n clearShowTimer();\n clearHideTimer();\n destroyFloating();\n };\n });\n\n return html`\n <slot></slot>\n <div\n class=\"tooltip\"\n part=\"tooltip\"\n id=\"${tooltipId}\"\n role=\"tooltip\"\n popover=\"manual\"\n ref=${(ref: HTMLElement) => {\n tooltipEl = ref;\n }}\n data-placement=\"${activePlacement}\"\n aria-hidden=\"${() => String(!floating.visible.value)}\">\n <slot name=\"content\"><span class=\"tooltip-text\">${props.content}</span></slot>\n </div>\n `;\n },\n styles: [forcedColorsMixin, styles],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"tooltip.cjs","names":[],"sources":["../src/overlay/tooltip/tooltip.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\n\nimport { bind, createId, define, getHost, html, onCleanup, onMounted, prop, useEmit, useSlots } from '@vielzeug/ore';\nimport { computed, signal } from '@vielzeug/ripple';\nimport {\n type OverlayOpenChangeDetail,\n type OverlayOpenReason,\n parseOptionalBool,\n parseStringTriggers,\n} from '../../core';\nimport { disablableBundle, sizableBundle } from '../../shared';\nimport { forcedColorsMixin } from '../../styles';\nimport type { ComponentSize } from '../../types';\nimport { useFloatingTrigger } from '../shared/use-floating-trigger';\nimport styles from './tooltip.css?inline';\n\ntype TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';\ntype TooltipTrigger = 'click' | 'focus' | 'hover';\n\nconst LEFT_GAP_COMPENSATION = 4;\nconst DEFAULT_TOOLTIP_TRIGGERS: TooltipTrigger[] = ['hover', 'focus'];\nconst VALID_TOOLTIP_TRIGGERS = new Set<TooltipTrigger>(['click', 'focus', 'hover']);\n\nconst parseDelayMs = (value: string | null): number => {\n if (value == null || value.trim() === '') return 0;\n\n const parsed = Number(value);\n\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;\n};\n\nconst normalizeTriggers = (value: string | null | undefined): TooltipTrigger[] =>\n parseStringTriggers(value, VALID_TOOLTIP_TRIGGERS, DEFAULT_TOOLTIP_TRIGGERS);\n\n/** Tooltip component properties */\nexport type OreTooltipEvents = {\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreTooltipProps = {\n /** Hide delay in ms */\n 'close-delay'?: number;\n /** Tooltip text content */\n content?: string;\n /** Initial uncontrolled open state. Ignored when `open` is set. */\n 'default-open'?: boolean;\n /** Show delay in ms */\n delay?: number;\n /** Disable the tooltip */\n disabled?: boolean;\n /** Controlled open state */\n open?: boolean;\n /** Preferred placement relative to trigger */\n placement?: TooltipPlacement;\n /** Tooltip size */\n size?: ComponentSize;\n /** Which trigger(s) show/hide the tooltip */\n trigger?: string;\n /** Visual variant */\n variant?: 'dark' | 'light';\n};\n\n/**\n * A lightweight tooltip shown on hover/focus/click relative to the slotted trigger.\n *\n * @element ore-tooltip\n *\n * @attr {string} content - Tooltip text content\n * @attr {string} placement - 'top' | 'bottom' | 'left' | 'right' (default: 'top')\n * @attr {string} trigger - 'hover' | 'focus' | 'click' or comma-separated combination\n * @attr {number} delay - Show delay in milliseconds (default: 0)\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} variant - 'dark' (default) | 'light'\n * @attr {boolean} disabled - Disable the tooltip\n *\n * @slot - Trigger element that the tooltip is anchored to\n * @slot content - Complex tooltip content (overrides the `content` attribute)\n * @fires open-change - Fired when the tooltip state changes. detail: { open, reason }\n *\n * @cssprop --tooltip-max-width - Max width of the tooltip bubble\n *\n * @part tooltip - Tooltip container.\n * @example\n * ```html\n * <!-- Simple text tooltip -->\n * <ore-tooltip content=\"Copy to clipboard\">\n * <button>Copy</button>\n * </ore-tooltip>\n *\n * <!-- Placement and delay -->\n * <ore-tooltip content=\"Save your work\" placement=\"right\" delay=\"300\">\n * <ore-icon name=\"save\"></ore-icon>\n * </ore-tooltip>\n *\n * <!-- Hover + focus trigger -->\n * <ore-tooltip content=\"Required field\" trigger=\"hover,focus\" placement=\"top\">\n * <ore-input label=\"Email\" type=\"email\"></ore-input>\n * </ore-tooltip>\n *\n * <!-- Rich content slot -->\n * <ore-tooltip>\n * <ore-button>Help</ore-button>\n * <div slot=\"content\">\n * <strong>Keyboard shortcuts</strong>\n * <p>Press Ctrl+S to save.</p>\n * </div>\n * </ore-tooltip>\n * ```\n */\nexport const TOOLTIP_TAG = 'ore-tooltip' as const;\ndefine<OreTooltipProps>(TOOLTIP_TAG, {\n props: {\n ...sizableBundle,\n ...disablableBundle,\n 'close-delay': { default: 0, parse: parseDelayMs },\n content: prop.string(),\n 'default-open': prop.bool(false),\n delay: { default: 0, parse: parseDelayMs },\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n placement: prop.oneOf(['top', 'bottom', 'left', 'right'] as const, 'top'),\n trigger: prop.string('hover,focus'),\n variant: prop.oneOf(['dark', 'light'] as const),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreTooltipEvents>();\n const slots = useSlots();\n\n const shadowRoot = el.shadowRoot;\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const isControlled = computed(() => props.open.value !== undefined);\n const activePlacement = signal<TooltipPlacement>('top');\n const tooltipId = createId('tooltip');\n const triggers = computed<TooltipTrigger[]>(() => normalizeTriggers(props.trigger.value));\n let tooltipEl: HTMLElement | null = null;\n let showTimer: ReturnType<typeof setTimeout> | null = null;\n let hideTimer: ReturnType<typeof setTimeout> | null = null;\n\n const clearShowTimer = (): void => {\n if (!showTimer) return;\n\n clearTimeout(showTimer);\n showTimer = null;\n };\n const clearHideTimer = (): void => {\n if (!hideTimer) return;\n\n clearTimeout(hideTimer);\n hideTimer = null;\n };\n\n const floating = useFloatingTrigger({\n bindTriggerAria: (triggerEl) => bind({ attr: { 'aria-describedby': () => tooltipId } }, { target: triggerEl }),\n defaultOpen: props['default-open'],\n disabled: isDisabled,\n getPanel: () => tooltipEl,\n onCleanup,\n onClose: (reason) => emit('open-change', { open: false, reason }),\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n onPlacementChange: (p) => {\n const side = p.split('-')[0] as TooltipPlacement;\n\n activePlacement.value = side;\n\n if (side === 'left' && tooltipEl) {\n tooltipEl.style.left = `${parseFloat(tooltipEl.style.left) - LEFT_GAP_COMPENSATION}px`;\n }\n },\n openProp: props.open as typeof props.open & { value: boolean | undefined },\n placement: computed(() => props.placement.value as Placement),\n slot: () => shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])') ?? null,\n slotElements: slots.elements(),\n triggers: computed(() => [] as TooltipTrigger[]),\n });\n\n function show(reason: OverlayOpenReason = 'hover'): void {\n if (isControlled.value) return;\n\n if (isDisabled.value || (!props.content.value && !slots.has('content').value)) return;\n\n clearHideTimer();\n clearShowTimer();\n\n const delay = Number(props.delay.value) || 0;\n\n if (delay > 0) {\n showTimer = setTimeout(() => {\n showTimer = null;\n floating.open(reason);\n floating.updatePosition();\n }, delay);\n } else {\n floating.open(reason);\n floating.updatePosition();\n }\n }\n\n function hide(): void {\n if (isControlled.value) return;\n\n clearShowTimer();\n\n const closeDelay = Number(props['close-delay'].value) || 0;\n\n if (closeDelay > 0) {\n clearHideTimer();\n hideTimer = setTimeout(() => {\n hideTimer = null;\n floating.close('trigger');\n }, closeDelay);\n } else {\n floating.close('trigger');\n }\n }\n\n onMounted(() => {\n // Manually bind trigger events so we can wrap show/hide with delay timers.\n // useFloatingTrigger handles ARIA, positioning, controlled mode, and keyboard dismiss.\n const triggerSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n let eventCleanups: Array<() => void> = [];\n\n const addEvent = (target: EventTarget, event: string, handler: EventListener): void => {\n target.addEventListener(event, handler);\n eventCleanups.push(() => target.removeEventListener(event, handler));\n };\n\n const bindTriggerEvents = (): void => {\n for (const cleanup of eventCleanups) cleanup();\n eventCleanups = [];\n\n const triggerEl = triggerSlot?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;\n\n if (!triggerEl) return;\n\n const t = triggers.value;\n\n if (t.includes('hover')) {\n addEvent(triggerEl, 'pointerenter', () => show('hover'));\n addEvent(triggerEl, 'pointerleave', hide);\n }\n\n if (t.includes('focus')) {\n addEvent(triggerEl, 'focusin', () => show('focus'));\n addEvent(triggerEl, 'focusout', hide);\n }\n\n if (t.includes('click')) {\n addEvent(triggerEl, 'click', () => (floating.visible.value ? hide() : show('click')));\n }\n };\n\n triggerSlot?.addEventListener('slotchange', bindTriggerEvents);\n bindTriggerEvents();\n\n const destroyFloating = floating.mount();\n\n return () => {\n triggerSlot?.removeEventListener('slotchange', bindTriggerEvents);\n for (const cleanup of eventCleanups) cleanup();\n clearShowTimer();\n clearHideTimer();\n destroyFloating();\n };\n });\n\n return html`\n <slot></slot>\n <div\n class=\"tooltip\"\n part=\"tooltip\"\n id=\"${tooltipId}\"\n role=\"tooltip\"\n popover=\"manual\"\n ref=${(ref: HTMLElement) => {\n tooltipEl = ref;\n }}\n data-placement=\"${activePlacement}\"\n aria-hidden=\"${() => String(!floating.visible.value)}\">\n <slot name=\"content\"><span class=\"tooltip-text\">${props.content}</span></slot>\n </div>\n `;\n },\n styles: [forcedColorsMixin, styles],\n});\n"],"mappings":"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"}
package/dist/tooltip.js CHANGED
@@ -1,4 +1,4 @@
1
- import{forcedColorsMixin as e}from"./styles/mixins/theme.css.js";import{parseOptionalBool as t,parseStringTriggers as n}from"./core/parse.js";import{disablableBundle as r,sizableBundle as i}from"./shared/prop-bundles.js";import{useFloatingTrigger as a}from"./overlay/shared/use-floating-trigger.js";import o from"./overlay/tooltip/tooltip.css.js";import{computed as s,signal as c}from"@vielzeug/ripple";import{bind as l,createId as u,define as d,getHost as f,html as p,onCleanup as m,onMounted as h,prop as g,useEmit as _,useSlots as v}from"@vielzeug/ore";var y=4,b=[`hover`,`focus`],x=new Set([`click`,`focus`,`hover`]),parseDelayMs=e=>{if(e==null||e.trim()===``)return 0;let t=Number(e);return Number.isFinite(t)&&t>=0?t:0},normalizeTriggers=e=>n(e,x,b),S=`ore-tooltip`;d(S,{props:{...i,...r,"close-delay":{default:0,parse:parseDelayMs},content:g.string(),"default-open":g.bool(!1),delay:{default:0,parse:parseDelayMs},open:{default:void 0,parse:t},placement:g.oneOf([`top`,`bottom`,`left`,`right`],`top`),trigger:g.string(`hover,focus`),variant:g.string()},setup(e){let t=f(),n=_(),r=v(),i=t.shadowRoot,o=s(()=>!!e.disabled.value),d=s(()=>e.open.value!==void 0),g=c(`top`),b=u(`tooltip`),x=s(()=>normalizeTriggers(e.trigger.value)),S=null,C=null,w=null,clearShowTimer=()=>{C&&=(clearTimeout(C),null)},clearHideTimer=()=>{w&&=(clearTimeout(w),null)},T=a({bindTriggerAria:e=>l({attr:{"aria-describedby":()=>b}},{target:e}),defaultOpen:e[`default-open`],disabled:o,getPanel:()=>S,onCleanup:m,onClose:e=>n(`open-change`,{open:!1,reason:e}),onOpen:e=>n(`open-change`,{open:!0,reason:e}),onPlacementChange:e=>{let t=e.split(`-`)[0];g.value=t,t===`left`&&S&&(S.style.left=`${parseFloat(S.style.left)-y}px`)},openProp:e.open,placement:s(()=>e.placement.value),slot:()=>i?.querySelector(`slot:not([name])`)??null,slotElements:r.elements(),triggers:s(()=>[])});function show(t=`hover`){if(d.value||o.value||!e.content.value&&!r.has(`content`).value)return;clearHideTimer(),clearShowTimer();let n=Number(e.delay.value)||0;n>0?C=setTimeout(()=>{C=null,T.open(t),T.updatePosition()},n):(T.open(t),T.updatePosition())}function hide(){if(d.value)return;clearShowTimer();let t=Number(e[`close-delay`].value)||0;t>0?(clearHideTimer(),w=setTimeout(()=>{w=null,T.close(`trigger`)},t)):T.close(`trigger`)}return h(()=>{let e=i?.querySelector(`slot:not([name])`),t=[],addEvent=(e,n,r)=>{e.addEventListener(n,r),t.push(()=>e.removeEventListener(n,r))},bindTriggerEvents=()=>{for(let e of t)e();t=[];let n=e?.assignedElements({flatten:!0})[0];if(!n)return;let r=x.value;r.includes(`hover`)&&(addEvent(n,`pointerenter`,()=>show(`hover`)),addEvent(n,`pointerleave`,hide)),r.includes(`focus`)&&(addEvent(n,`focusin`,()=>show(`focus`)),addEvent(n,`focusout`,hide)),r.includes(`click`)&&addEvent(n,`click`,()=>T.visible.value?hide():show(`click`))};e?.addEventListener(`slotchange`,bindTriggerEvents),bindTriggerEvents();let n=T.mount();return()=>{e?.removeEventListener(`slotchange`,bindTriggerEvents);for(let e of t)e();clearShowTimer(),clearHideTimer(),n()}}),p`
1
+ import{forcedColorsMixin as e}from"./styles/mixins/theme.css.js";import{parseOptionalBool as t,parseStringTriggers as n}from"./core/parse.js";import{disablableBundle as r,sizableBundle as i}from"./shared/prop-bundles.js";import{useFloatingTrigger as a}from"./overlay/shared/use-floating-trigger.js";import o from"./overlay/tooltip/tooltip.css.js";import{computed as s,signal as c}from"@vielzeug/ripple";import{bind as l,createId as u,define as d,getHost as f,html as p,onCleanup as m,onMounted as h,prop as g,useEmit as _,useSlots as v}from"@vielzeug/ore";var y=4,b=[`hover`,`focus`],x=new Set([`click`,`focus`,`hover`]),parseDelayMs=e=>{if(e==null||e.trim()===``)return 0;let t=Number(e);return Number.isFinite(t)&&t>=0?t:0},normalizeTriggers=e=>n(e,x,b),S=`ore-tooltip`;d(S,{props:{...i,...r,"close-delay":{default:0,parse:parseDelayMs},content:g.string(),"default-open":g.bool(!1),delay:{default:0,parse:parseDelayMs},open:{default:void 0,parse:t},placement:g.oneOf([`top`,`bottom`,`left`,`right`],`top`),trigger:g.string(`hover,focus`),variant:g.oneOf([`dark`,`light`])},setup(e){let t=f(),n=_(),r=v(),i=t.shadowRoot,o=s(()=>!!e.disabled.value),d=s(()=>e.open.value!==void 0),g=c(`top`),b=u(`tooltip`),x=s(()=>normalizeTriggers(e.trigger.value)),S=null,C=null,w=null,clearShowTimer=()=>{C&&=(clearTimeout(C),null)},clearHideTimer=()=>{w&&=(clearTimeout(w),null)},T=a({bindTriggerAria:e=>l({attr:{"aria-describedby":()=>b}},{target:e}),defaultOpen:e[`default-open`],disabled:o,getPanel:()=>S,onCleanup:m,onClose:e=>n(`open-change`,{open:!1,reason:e}),onOpen:e=>n(`open-change`,{open:!0,reason:e}),onPlacementChange:e=>{let t=e.split(`-`)[0];g.value=t,t===`left`&&S&&(S.style.left=`${parseFloat(S.style.left)-y}px`)},openProp:e.open,placement:s(()=>e.placement.value),slot:()=>i?.querySelector(`slot:not([name])`)??null,slotElements:r.elements(),triggers:s(()=>[])});function show(t=`hover`){if(d.value||o.value||!e.content.value&&!r.has(`content`).value)return;clearHideTimer(),clearShowTimer();let n=Number(e.delay.value)||0;n>0?C=setTimeout(()=>{C=null,T.open(t),T.updatePosition()},n):(T.open(t),T.updatePosition())}function hide(){if(d.value)return;clearShowTimer();let t=Number(e[`close-delay`].value)||0;t>0?(clearHideTimer(),w=setTimeout(()=>{w=null,T.close(`trigger`)},t)):T.close(`trigger`)}return h(()=>{let e=i?.querySelector(`slot:not([name])`),t=[],addEvent=(e,n,r)=>{e.addEventListener(n,r),t.push(()=>e.removeEventListener(n,r))},bindTriggerEvents=()=>{for(let e of t)e();t=[];let n=e?.assignedElements({flatten:!0})[0];if(!n)return;let r=x.value;r.includes(`hover`)&&(addEvent(n,`pointerenter`,()=>show(`hover`)),addEvent(n,`pointerleave`,hide)),r.includes(`focus`)&&(addEvent(n,`focusin`,()=>show(`focus`)),addEvent(n,`focusout`,hide)),r.includes(`click`)&&addEvent(n,`click`,()=>T.visible.value?hide():show(`click`))};e?.addEventListener(`slotchange`,bindTriggerEvents),bindTriggerEvents();let n=T.mount();return()=>{e?.removeEventListener(`slotchange`,bindTriggerEvents);for(let e of t)e();clearShowTimer(),clearHideTimer(),n()}}),p`
2
2
  <slot></slot>
3
3
  <div
4
4
  class="tooltip"
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.js","names":[],"sources":["../src/overlay/tooltip/tooltip.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\n\nimport { bind, createId, define, getHost, html, onCleanup, onMounted, prop, useEmit, useSlots } from '@vielzeug/ore';\nimport { computed, signal } from '@vielzeug/ripple';\nimport {\n type OverlayOpenChangeDetail,\n type OverlayOpenReason,\n parseOptionalBool,\n parseStringTriggers,\n} from '../../core';\nimport { disablableBundle, sizableBundle } from '../../shared';\nimport { forcedColorsMixin } from '../../styles';\nimport type { ComponentSize } from '../../types';\nimport { useFloatingTrigger } from '../shared/use-floating-trigger';\nimport styles from './tooltip.css?inline';\n\ntype TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';\ntype TooltipTrigger = 'click' | 'focus' | 'hover';\n\nconst LEFT_GAP_COMPENSATION = 4;\nconst DEFAULT_TOOLTIP_TRIGGERS: TooltipTrigger[] = ['hover', 'focus'];\nconst VALID_TOOLTIP_TRIGGERS = new Set<TooltipTrigger>(['click', 'focus', 'hover']);\n\nconst parseDelayMs = (value: string | null): number => {\n if (value == null || value.trim() === '') return 0;\n\n const parsed = Number(value);\n\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;\n};\n\nconst normalizeTriggers = (value: string | null | undefined): TooltipTrigger[] =>\n parseStringTriggers(value, VALID_TOOLTIP_TRIGGERS, DEFAULT_TOOLTIP_TRIGGERS);\n\n/** Tooltip component properties */\nexport type OreTooltipEvents = {\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreTooltipProps = {\n /** Hide delay in ms */\n 'close-delay'?: number;\n /** Tooltip text content */\n content?: string;\n /** Initial uncontrolled open state. Ignored when `open` is set. */\n 'default-open'?: boolean;\n /** Show delay in ms */\n delay?: number;\n /** Disable the tooltip */\n disabled?: boolean;\n /** Controlled open state */\n open?: boolean;\n /** Preferred placement relative to trigger */\n placement?: TooltipPlacement;\n /** Tooltip size */\n size?: ComponentSize;\n /** Which trigger(s) show/hide the tooltip */\n trigger?: string;\n /** Visual variant */\n variant?: 'dark' | 'light';\n};\n\n/**\n * A lightweight tooltip shown on hover/focus/click relative to the slotted trigger.\n *\n * @element ore-tooltip\n *\n * @attr {string} content - Tooltip text content\n * @attr {string} placement - 'top' | 'bottom' | 'left' | 'right' (default: 'top')\n * @attr {string} trigger - 'hover' | 'focus' | 'click' or comma-separated combination\n * @attr {number} delay - Show delay in milliseconds (default: 0)\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} variant - 'dark' (default) | 'light'\n * @attr {boolean} disabled - Disable the tooltip\n *\n * @slot - Trigger element that the tooltip is anchored to\n * @slot content - Complex tooltip content (overrides the `content` attribute)\n * @fires open-change - Fired when the tooltip state changes. detail: { open, reason }\n *\n * @cssprop --tooltip-max-width - Max width of the tooltip bubble\n *\n * @part tooltip - Tooltip container.\n * @example\n * ```html\n * <!-- Simple text tooltip -->\n * <ore-tooltip content=\"Copy to clipboard\">\n * <button>Copy</button>\n * </ore-tooltip>\n *\n * <!-- Placement and delay -->\n * <ore-tooltip content=\"Save your work\" placement=\"right\" delay=\"300\">\n * <ore-icon name=\"save\"></ore-icon>\n * </ore-tooltip>\n *\n * <!-- Hover + focus trigger -->\n * <ore-tooltip content=\"Required field\" trigger=\"hover,focus\" placement=\"top\">\n * <ore-input label=\"Email\" type=\"email\"></ore-input>\n * </ore-tooltip>\n *\n * <!-- Rich content slot -->\n * <ore-tooltip>\n * <ore-button>Help</ore-button>\n * <div slot=\"content\">\n * <strong>Keyboard shortcuts</strong>\n * <p>Press Ctrl+S to save.</p>\n * </div>\n * </ore-tooltip>\n * ```\n */\nexport const TOOLTIP_TAG = 'ore-tooltip' as const;\ndefine<OreTooltipProps>(TOOLTIP_TAG, {\n props: {\n ...sizableBundle,\n ...disablableBundle,\n 'close-delay': { default: 0, parse: parseDelayMs },\n content: prop.string(),\n 'default-open': prop.bool(false),\n delay: { default: 0, parse: parseDelayMs },\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n placement: prop.oneOf(['top', 'bottom', 'left', 'right'] as const, 'top'),\n trigger: prop.string('hover,focus'),\n variant: prop.string<'dark' | 'light'>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreTooltipEvents>();\n const slots = useSlots();\n\n const shadowRoot = el.shadowRoot;\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const isControlled = computed(() => props.open.value !== undefined);\n const activePlacement = signal<TooltipPlacement>('top');\n const tooltipId = createId('tooltip');\n const triggers = computed<TooltipTrigger[]>(() => normalizeTriggers(props.trigger.value));\n let tooltipEl: HTMLElement | null = null;\n let showTimer: ReturnType<typeof setTimeout> | null = null;\n let hideTimer: ReturnType<typeof setTimeout> | null = null;\n\n const clearShowTimer = (): void => {\n if (!showTimer) return;\n\n clearTimeout(showTimer);\n showTimer = null;\n };\n const clearHideTimer = (): void => {\n if (!hideTimer) return;\n\n clearTimeout(hideTimer);\n hideTimer = null;\n };\n\n const floating = useFloatingTrigger({\n bindTriggerAria: (triggerEl) => bind({ attr: { 'aria-describedby': () => tooltipId } }, { target: triggerEl }),\n defaultOpen: props['default-open'],\n disabled: isDisabled,\n getPanel: () => tooltipEl,\n onCleanup,\n onClose: (reason) => emit('open-change', { open: false, reason }),\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n onPlacementChange: (p) => {\n const side = p.split('-')[0] as TooltipPlacement;\n\n activePlacement.value = side;\n\n if (side === 'left' && tooltipEl) {\n tooltipEl.style.left = `${parseFloat(tooltipEl.style.left) - LEFT_GAP_COMPENSATION}px`;\n }\n },\n openProp: props.open as typeof props.open & { value: boolean | undefined },\n placement: computed(() => props.placement.value as Placement),\n slot: () => shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])') ?? null,\n slotElements: slots.elements(),\n triggers: computed(() => [] as TooltipTrigger[]),\n });\n\n function show(reason: OverlayOpenReason = 'hover'): void {\n if (isControlled.value) return;\n\n if (isDisabled.value || (!props.content.value && !slots.has('content').value)) return;\n\n clearHideTimer();\n clearShowTimer();\n\n const delay = Number(props.delay.value) || 0;\n\n if (delay > 0) {\n showTimer = setTimeout(() => {\n showTimer = null;\n floating.open(reason);\n floating.updatePosition();\n }, delay);\n } else {\n floating.open(reason);\n floating.updatePosition();\n }\n }\n\n function hide(): void {\n if (isControlled.value) return;\n\n clearShowTimer();\n\n const closeDelay = Number(props['close-delay'].value) || 0;\n\n if (closeDelay > 0) {\n clearHideTimer();\n hideTimer = setTimeout(() => {\n hideTimer = null;\n floating.close('trigger');\n }, closeDelay);\n } else {\n floating.close('trigger');\n }\n }\n\n onMounted(() => {\n // Manually bind trigger events so we can wrap show/hide with delay timers.\n // useFloatingTrigger handles ARIA, positioning, controlled mode, and keyboard dismiss.\n const triggerSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n let eventCleanups: Array<() => void> = [];\n\n const addEvent = (target: EventTarget, event: string, handler: EventListener): void => {\n target.addEventListener(event, handler);\n eventCleanups.push(() => target.removeEventListener(event, handler));\n };\n\n const bindTriggerEvents = (): void => {\n for (const cleanup of eventCleanups) cleanup();\n eventCleanups = [];\n\n const triggerEl = triggerSlot?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;\n\n if (!triggerEl) return;\n\n const t = triggers.value;\n\n if (t.includes('hover')) {\n addEvent(triggerEl, 'pointerenter', () => show('hover'));\n addEvent(triggerEl, 'pointerleave', hide);\n }\n\n if (t.includes('focus')) {\n addEvent(triggerEl, 'focusin', () => show('focus'));\n addEvent(triggerEl, 'focusout', hide);\n }\n\n if (t.includes('click')) {\n addEvent(triggerEl, 'click', () => (floating.visible.value ? hide() : show('click')));\n }\n };\n\n triggerSlot?.addEventListener('slotchange', bindTriggerEvents);\n bindTriggerEvents();\n\n const destroyFloating = floating.mount();\n\n return () => {\n triggerSlot?.removeEventListener('slotchange', bindTriggerEvents);\n for (const cleanup of eventCleanups) cleanup();\n clearShowTimer();\n clearHideTimer();\n destroyFloating();\n };\n });\n\n return html`\n <slot></slot>\n <div\n class=\"tooltip\"\n part=\"tooltip\"\n id=\"${tooltipId}\"\n role=\"tooltip\"\n popover=\"manual\"\n ref=${(ref: HTMLElement) => {\n tooltipEl = ref;\n }}\n data-placement=\"${activePlacement}\"\n aria-hidden=\"${() => String(!floating.visible.value)}\">\n <slot name=\"content\"><span class=\"tooltip-text\">${props.content}</span></slot>\n </div>\n `;\n },\n styles: [forcedColorsMixin, styles],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"tooltip.js","names":[],"sources":["../src/overlay/tooltip/tooltip.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\n\nimport { bind, createId, define, getHost, html, onCleanup, onMounted, prop, useEmit, useSlots } from '@vielzeug/ore';\nimport { computed, signal } from '@vielzeug/ripple';\nimport {\n type OverlayOpenChangeDetail,\n type OverlayOpenReason,\n parseOptionalBool,\n parseStringTriggers,\n} from '../../core';\nimport { disablableBundle, sizableBundle } from '../../shared';\nimport { forcedColorsMixin } from '../../styles';\nimport type { ComponentSize } from '../../types';\nimport { useFloatingTrigger } from '../shared/use-floating-trigger';\nimport styles from './tooltip.css?inline';\n\ntype TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';\ntype TooltipTrigger = 'click' | 'focus' | 'hover';\n\nconst LEFT_GAP_COMPENSATION = 4;\nconst DEFAULT_TOOLTIP_TRIGGERS: TooltipTrigger[] = ['hover', 'focus'];\nconst VALID_TOOLTIP_TRIGGERS = new Set<TooltipTrigger>(['click', 'focus', 'hover']);\n\nconst parseDelayMs = (value: string | null): number => {\n if (value == null || value.trim() === '') return 0;\n\n const parsed = Number(value);\n\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;\n};\n\nconst normalizeTriggers = (value: string | null | undefined): TooltipTrigger[] =>\n parseStringTriggers(value, VALID_TOOLTIP_TRIGGERS, DEFAULT_TOOLTIP_TRIGGERS);\n\n/** Tooltip component properties */\nexport type OreTooltipEvents = {\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreTooltipProps = {\n /** Hide delay in ms */\n 'close-delay'?: number;\n /** Tooltip text content */\n content?: string;\n /** Initial uncontrolled open state. Ignored when `open` is set. */\n 'default-open'?: boolean;\n /** Show delay in ms */\n delay?: number;\n /** Disable the tooltip */\n disabled?: boolean;\n /** Controlled open state */\n open?: boolean;\n /** Preferred placement relative to trigger */\n placement?: TooltipPlacement;\n /** Tooltip size */\n size?: ComponentSize;\n /** Which trigger(s) show/hide the tooltip */\n trigger?: string;\n /** Visual variant */\n variant?: 'dark' | 'light';\n};\n\n/**\n * A lightweight tooltip shown on hover/focus/click relative to the slotted trigger.\n *\n * @element ore-tooltip\n *\n * @attr {string} content - Tooltip text content\n * @attr {string} placement - 'top' | 'bottom' | 'left' | 'right' (default: 'top')\n * @attr {string} trigger - 'hover' | 'focus' | 'click' or comma-separated combination\n * @attr {number} delay - Show delay in milliseconds (default: 0)\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} variant - 'dark' (default) | 'light'\n * @attr {boolean} disabled - Disable the tooltip\n *\n * @slot - Trigger element that the tooltip is anchored to\n * @slot content - Complex tooltip content (overrides the `content` attribute)\n * @fires open-change - Fired when the tooltip state changes. detail: { open, reason }\n *\n * @cssprop --tooltip-max-width - Max width of the tooltip bubble\n *\n * @part tooltip - Tooltip container.\n * @example\n * ```html\n * <!-- Simple text tooltip -->\n * <ore-tooltip content=\"Copy to clipboard\">\n * <button>Copy</button>\n * </ore-tooltip>\n *\n * <!-- Placement and delay -->\n * <ore-tooltip content=\"Save your work\" placement=\"right\" delay=\"300\">\n * <ore-icon name=\"save\"></ore-icon>\n * </ore-tooltip>\n *\n * <!-- Hover + focus trigger -->\n * <ore-tooltip content=\"Required field\" trigger=\"hover,focus\" placement=\"top\">\n * <ore-input label=\"Email\" type=\"email\"></ore-input>\n * </ore-tooltip>\n *\n * <!-- Rich content slot -->\n * <ore-tooltip>\n * <ore-button>Help</ore-button>\n * <div slot=\"content\">\n * <strong>Keyboard shortcuts</strong>\n * <p>Press Ctrl+S to save.</p>\n * </div>\n * </ore-tooltip>\n * ```\n */\nexport const TOOLTIP_TAG = 'ore-tooltip' as const;\ndefine<OreTooltipProps>(TOOLTIP_TAG, {\n props: {\n ...sizableBundle,\n ...disablableBundle,\n 'close-delay': { default: 0, parse: parseDelayMs },\n content: prop.string(),\n 'default-open': prop.bool(false),\n delay: { default: 0, parse: parseDelayMs },\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n placement: prop.oneOf(['top', 'bottom', 'left', 'right'] as const, 'top'),\n trigger: prop.string('hover,focus'),\n variant: prop.oneOf(['dark', 'light'] as const),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreTooltipEvents>();\n const slots = useSlots();\n\n const shadowRoot = el.shadowRoot;\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const isControlled = computed(() => props.open.value !== undefined);\n const activePlacement = signal<TooltipPlacement>('top');\n const tooltipId = createId('tooltip');\n const triggers = computed<TooltipTrigger[]>(() => normalizeTriggers(props.trigger.value));\n let tooltipEl: HTMLElement | null = null;\n let showTimer: ReturnType<typeof setTimeout> | null = null;\n let hideTimer: ReturnType<typeof setTimeout> | null = null;\n\n const clearShowTimer = (): void => {\n if (!showTimer) return;\n\n clearTimeout(showTimer);\n showTimer = null;\n };\n const clearHideTimer = (): void => {\n if (!hideTimer) return;\n\n clearTimeout(hideTimer);\n hideTimer = null;\n };\n\n const floating = useFloatingTrigger({\n bindTriggerAria: (triggerEl) => bind({ attr: { 'aria-describedby': () => tooltipId } }, { target: triggerEl }),\n defaultOpen: props['default-open'],\n disabled: isDisabled,\n getPanel: () => tooltipEl,\n onCleanup,\n onClose: (reason) => emit('open-change', { open: false, reason }),\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n onPlacementChange: (p) => {\n const side = p.split('-')[0] as TooltipPlacement;\n\n activePlacement.value = side;\n\n if (side === 'left' && tooltipEl) {\n tooltipEl.style.left = `${parseFloat(tooltipEl.style.left) - LEFT_GAP_COMPENSATION}px`;\n }\n },\n openProp: props.open as typeof props.open & { value: boolean | undefined },\n placement: computed(() => props.placement.value as Placement),\n slot: () => shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])') ?? null,\n slotElements: slots.elements(),\n triggers: computed(() => [] as TooltipTrigger[]),\n });\n\n function show(reason: OverlayOpenReason = 'hover'): void {\n if (isControlled.value) return;\n\n if (isDisabled.value || (!props.content.value && !slots.has('content').value)) return;\n\n clearHideTimer();\n clearShowTimer();\n\n const delay = Number(props.delay.value) || 0;\n\n if (delay > 0) {\n showTimer = setTimeout(() => {\n showTimer = null;\n floating.open(reason);\n floating.updatePosition();\n }, delay);\n } else {\n floating.open(reason);\n floating.updatePosition();\n }\n }\n\n function hide(): void {\n if (isControlled.value) return;\n\n clearShowTimer();\n\n const closeDelay = Number(props['close-delay'].value) || 0;\n\n if (closeDelay > 0) {\n clearHideTimer();\n hideTimer = setTimeout(() => {\n hideTimer = null;\n floating.close('trigger');\n }, closeDelay);\n } else {\n floating.close('trigger');\n }\n }\n\n onMounted(() => {\n // Manually bind trigger events so we can wrap show/hide with delay timers.\n // useFloatingTrigger handles ARIA, positioning, controlled mode, and keyboard dismiss.\n const triggerSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n let eventCleanups: Array<() => void> = [];\n\n const addEvent = (target: EventTarget, event: string, handler: EventListener): void => {\n target.addEventListener(event, handler);\n eventCleanups.push(() => target.removeEventListener(event, handler));\n };\n\n const bindTriggerEvents = (): void => {\n for (const cleanup of eventCleanups) cleanup();\n eventCleanups = [];\n\n const triggerEl = triggerSlot?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;\n\n if (!triggerEl) return;\n\n const t = triggers.value;\n\n if (t.includes('hover')) {\n addEvent(triggerEl, 'pointerenter', () => show('hover'));\n addEvent(triggerEl, 'pointerleave', hide);\n }\n\n if (t.includes('focus')) {\n addEvent(triggerEl, 'focusin', () => show('focus'));\n addEvent(triggerEl, 'focusout', hide);\n }\n\n if (t.includes('click')) {\n addEvent(triggerEl, 'click', () => (floating.visible.value ? hide() : show('click')));\n }\n };\n\n triggerSlot?.addEventListener('slotchange', bindTriggerEvents);\n bindTriggerEvents();\n\n const destroyFloating = floating.mount();\n\n return () => {\n triggerSlot?.removeEventListener('slotchange', bindTriggerEvents);\n for (const cleanup of eventCleanups) cleanup();\n clearShowTimer();\n clearHideTimer();\n destroyFloating();\n };\n });\n\n return html`\n <slot></slot>\n <div\n class=\"tooltip\"\n part=\"tooltip\"\n id=\"${tooltipId}\"\n role=\"tooltip\"\n popover=\"manual\"\n ref=${(ref: HTMLElement) => {\n tooltipEl = ref;\n }}\n data-placement=\"${activePlacement}\"\n aria-hidden=\"${() => String(!floating.visible.value)}\">\n <slot name=\"content\"><span class=\"tooltip-text\">${props.content}</span></slot>\n </div>\n `;\n },\n styles: [forcedColorsMixin, styles],\n});\n"],"mappings":"4iBAmBA,IAAM,EAAwB,EACxB,EAA6C,CAAC,QAAS,OAAO,EAC9D,EAAyB,IAAI,IAAoB,CAAC,QAAS,QAAS,OAAO,CAAC,EAE5E,aAAgB,GAAiC,CACrD,GAAI,GAAS,MAAQ,EAAM,KAAK,IAAM,GAAI,MAAO,GAEjD,IAAM,EAAS,OAAO,CAAK,EAE3B,OAAO,OAAO,SAAS,CAAM,GAAK,GAAU,EAAI,EAAS,CAC3D,EAEM,kBAAqB,GACzB,EAAoB,EAAO,EAAwB,CAAwB,EA+EhE,EAAc,cAC3B,EAAwB,EAAa,CACnC,MAAO,CACL,GAAG,EACH,GAAG,EACH,cAAe,CAAE,QAAS,EAAG,MAAO,YAAa,EACjD,QAAS,EAAK,OAAO,EACrB,eAAgB,EAAK,KAAK,EAAK,EAC/B,MAAO,CAAE,QAAS,EAAG,MAAO,YAAa,EACzC,KAAM,CAAE,QAAS,IAAA,GAAkC,MAAO,CAAkB,EAC5E,UAAW,EAAK,MAAM,CAAC,MAAO,SAAU,OAAQ,OAAO,EAAY,KAAK,EACxE,QAAS,EAAK,OAAO,aAAa,EAClC,QAAS,EAAK,MAAM,CAAC,OAAQ,OAAO,CAAU,CAChD,EACA,MAAM,EAAO,CACX,IAAM,EAAK,EAAQ,EACb,EAAO,EAA0B,EACjC,EAAQ,EAAS,EAEjB,EAAa,EAAG,WAChB,EAAa,MAAe,EAAQ,EAAM,SAAS,KAAM,EACzD,EAAe,MAAe,EAAM,KAAK,QAAU,IAAA,EAAS,EAC5D,EAAkB,EAAyB,KAAK,EAChD,EAAY,EAAS,SAAS,EAC9B,EAAW,MAAiC,kBAAkB,EAAM,QAAQ,KAAK,CAAC,EACpF,EAAgC,KAChC,EAAkD,KAClD,EAAkD,KAEhD,mBAA6B,CAC5B,AAGL,KADA,aAAa,CAAS,EACV,KACd,EACM,mBAA6B,CAC5B,AAGL,KADA,aAAa,CAAS,EACV,KACd,EAEM,EAAW,EAAmB,CAClC,gBAAkB,GAAc,EAAK,CAAE,KAAM,CAAE,uBAA0B,CAAU,CAAE,EAAG,CAAE,OAAQ,CAAU,CAAC,EAC7G,YAAa,EAAM,gBACnB,SAAU,EACV,aAAgB,EAChB,YACA,QAAU,GAAW,EAAK,cAAe,CAAE,KAAM,GAAO,QAAO,CAAC,EAChE,OAAS,GAAW,EAAK,cAAe,CAAE,KAAM,GAAM,QAAO,CAAC,EAC9D,kBAAoB,GAAM,CACxB,IAAM,EAAO,EAAE,MAAM,GAAG,CAAC,CAAC,GAE1B,EAAgB,MAAQ,EAEpB,IAAS,QAAU,IACrB,EAAU,MAAM,KAAO,GAAG,WAAW,EAAU,MAAM,IAAI,EAAI,EAAsB,IAEvF,EACA,SAAU,EAAM,KAChB,UAAW,MAAe,EAAM,UAAU,KAAkB,EAC5D,SAAY,GAAY,cAA+B,kBAAkB,GAAK,KAC9E,aAAc,EAAM,SAAS,EAC7B,SAAU,MAAe,CAAC,CAAqB,CACjD,CAAC,EAED,SAAS,KAAK,EAA4B,QAAe,CAGvD,GAFI,EAAa,OAEb,EAAW,OAAU,CAAC,EAAM,QAAQ,OAAS,CAAC,EAAM,IAAI,SAAS,CAAC,CAAC,MAAQ,OAE/E,eAAe,EACf,eAAe,EAEf,IAAM,EAAQ,OAAO,EAAM,MAAM,KAAK,GAAK,EAEvC,EAAQ,EACV,EAAY,eAAiB,CAC3B,EAAY,KACZ,EAAS,KAAK,CAAM,EACpB,EAAS,eAAe,CAC1B,EAAG,CAAK,GAER,EAAS,KAAK,CAAM,EACpB,EAAS,eAAe,EAE5B,CAEA,SAAS,MAAa,CACpB,GAAI,EAAa,MAAO,OAExB,eAAe,EAEf,IAAM,EAAa,OAAO,EAAM,cAAc,CAAC,KAAK,GAAK,EAErD,EAAa,GACf,eAAe,EACf,EAAY,eAAiB,CAC3B,EAAY,KACZ,EAAS,MAAM,SAAS,CAC1B,EAAG,CAAU,GAEb,EAAS,MAAM,SAAS,CAE5B,CAoDA,OAlDA,MAAgB,CAGd,IAAM,EAAc,GAAY,cAA+B,kBAAkB,EAC7E,EAAmC,CAAC,EAElC,UAAY,EAAqB,EAAe,IAAiC,CACrF,EAAO,iBAAiB,EAAO,CAAO,EACtC,EAAc,SAAW,EAAO,oBAAoB,EAAO,CAAO,CAAC,CACrE,EAEM,sBAAgC,CACpC,IAAK,IAAM,KAAW,EAAe,EAAQ,EAC7C,EAAgB,CAAC,EAEjB,IAAM,EAAY,GAAa,iBAAiB,CAAE,QAAS,EAAK,CAAC,CAAC,CAAC,GAEnE,GAAI,CAAC,EAAW,OAEhB,IAAM,EAAI,EAAS,MAEf,EAAE,SAAS,OAAO,IACpB,SAAS,EAAW,mBAAsB,KAAK,OAAO,CAAC,EACvD,SAAS,EAAW,eAAgB,IAAI,GAGtC,EAAE,SAAS,OAAO,IACpB,SAAS,EAAW,cAAiB,KAAK,OAAO,CAAC,EAClD,SAAS,EAAW,WAAY,IAAI,GAGlC,EAAE,SAAS,OAAO,GACpB,SAAS,EAAW,YAAgB,EAAS,QAAQ,MAAQ,KAAK,EAAI,KAAK,OAAO,CAAE,CAExF,EAEA,GAAa,iBAAiB,aAAc,iBAAiB,EAC7D,kBAAkB,EAElB,IAAM,EAAkB,EAAS,MAAM,EAEvC,UAAa,CACX,GAAa,oBAAoB,aAAc,iBAAiB,EAChE,IAAK,IAAM,KAAW,EAAe,EAAQ,EAC7C,eAAe,EACf,eAAe,EACf,EAAgB,CAClB,CACF,CAAC,EAEM,CAAI;;;;;cAKD,EAAU;;;cAGT,GAAqB,CAC1B,EAAY,CACd,EAAE;0BACgB,EAAgB;2BACb,OAAO,CAAC,EAAS,QAAQ,KAAK,EAAE;0DACH,EAAM,QAAQ;;KAGtE,EACA,OAAQ,CAAC,EAAmB,CAAM,CACpC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/refine",
3
- "version": "26.10.7",
3
+ "version": "26.10.10",
4
4
  "description": "Accessible, themeable web components built on ore",
5
5
  "keywords": [
6
6
  "web-components",
@@ -712,7 +712,8 @@
712
712
  "./dist/*.cjs",
713
713
  "./dist/core/*",
714
714
  "./dist/styles/**",
715
- "./src/core/icons.ts"
715
+ "./src/core/icons.ts",
716
+ "./src/icon-lucide.ts"
716
717
  ],
717
718
  "scripts": {
718
719
  "analyze": "custom-elements-manifest analyze --config scripts/refine-manifest.mjs",
@@ -736,17 +737,17 @@
736
737
  "test:e2e": "playwright test"
737
738
  },
738
739
  "dependencies": {
739
- "@vielzeug/arsenal": "26.10.7",
740
- "@vielzeug/dnd": "26.10.7",
741
- "@vielzeug/focus": "26.10.7",
742
- "@vielzeug/gesture": "26.10.7",
743
- "@vielzeug/keymap": "26.10.7",
744
- "@vielzeug/orbit": "26.10.7",
745
- "@vielzeug/ore": "26.10.7",
746
- "@vielzeug/ripple": "26.10.7",
747
- "@vielzeug/sentinel": "26.10.7",
748
- "@vielzeug/sigil": "26.10.7",
749
- "@vielzeug/tempo": "26.10.7",
740
+ "@vielzeug/arsenal": "26.10.10",
741
+ "@vielzeug/dnd": "26.10.10",
742
+ "@vielzeug/focus": "26.10.10",
743
+ "@vielzeug/gesture": "26.10.10",
744
+ "@vielzeug/keymap": "26.10.10",
745
+ "@vielzeug/orbit": "26.10.10",
746
+ "@vielzeug/ore": "26.10.10",
747
+ "@vielzeug/ripple": "26.10.10",
748
+ "@vielzeug/sentinel": "26.10.10",
749
+ "@vielzeug/sigil": "26.10.10",
750
+ "@vielzeug/tempo": "26.10.10",
750
751
  "lucide": "^1.48.0"
751
752
  },
752
753
  "devDependencies": {
@@ -756,7 +757,7 @@
756
757
  "@playwright/test": "^1.63.0",
757
758
  "@types/culori": "^4.0.1",
758
759
  "@types/node": "^26.6.2",
759
- "@vielzeug/assay": "26.10.7",
760
+ "@vielzeug/assay": "26.10.10",
760
761
  "axe-core": "^4.13.0",
761
762
  "browserslist": "^4.29.0",
762
763
  "culori": "^4.0.2",