@vielzeug/refine 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/chip.cjs +9 -9
  2. package/dist/chip.cjs.map +1 -1
  3. package/dist/chip.js +38 -35
  4. package/dist/chip.js.map +1 -1
  5. package/dist/command-palette.cjs +5 -5
  6. package/dist/command-palette.cjs.map +1 -1
  7. package/dist/command-palette.js +5 -5
  8. package/dist/command-palette.js.map +1 -1
  9. package/dist/content/card/card.css.cjs +1 -1
  10. package/dist/content/card/card.css.cjs.map +1 -1
  11. package/dist/content/card/card.css.js +1 -1
  12. package/dist/content/card/card.css.js.map +1 -1
  13. package/dist/content/index.d.ts +4 -0
  14. package/dist/content/index.d.ts.map +1 -1
  15. package/dist/content/keyboard-key/keyboard-key.css.cjs +2 -0
  16. package/dist/content/keyboard-key/keyboard-key.css.cjs.map +1 -0
  17. package/dist/content/keyboard-key/keyboard-key.css.js +6 -0
  18. package/dist/content/keyboard-key/keyboard-key.css.js.map +1 -0
  19. package/dist/content/keyboard-key/keyboard-key.d.ts +12 -0
  20. package/dist/content/keyboard-key/keyboard-key.d.ts.map +1 -0
  21. package/dist/content/keyboard-key/keyboard-shortcut.css.cjs +2 -0
  22. package/dist/content/keyboard-key/keyboard-shortcut.css.cjs.map +1 -0
  23. package/dist/content/keyboard-key/keyboard-shortcut.css.js +6 -0
  24. package/dist/content/keyboard-key/keyboard-shortcut.css.js.map +1 -0
  25. package/dist/content/qr-code/qr-code.css.cjs +2 -0
  26. package/dist/content/qr-code/qr-code.css.cjs.map +1 -0
  27. package/dist/content/qr-code/qr-code.css.js +6 -0
  28. package/dist/content/qr-code/qr-code.css.js.map +1 -0
  29. package/dist/content/qr-code/qr-code.d.ts +74 -0
  30. package/dist/content/qr-code/qr-code.d.ts.map +1 -0
  31. package/dist/content/stepper/step.css.cjs +1 -1
  32. package/dist/content/stepper/step.css.cjs.map +1 -1
  33. package/dist/content/stepper/step.css.js +1 -1
  34. package/dist/content/stepper/step.css.js.map +1 -1
  35. package/dist/counter.cjs +91 -0
  36. package/dist/counter.cjs.map +1 -0
  37. package/dist/counter.js +180 -0
  38. package/dist/counter.js.map +1 -0
  39. package/dist/custom-elements.json +3838 -3117
  40. package/dist/disclosure/tabs/tabs.css.cjs +1 -1
  41. package/dist/disclosure/tabs/tabs.css.cjs.map +1 -1
  42. package/dist/disclosure/tabs/tabs.css.js +1 -1
  43. package/dist/disclosure/tabs/tabs.css.js.map +1 -1
  44. package/dist/feedback/chip/chip.css.cjs +1 -1
  45. package/dist/feedback/chip/chip.css.cjs.map +1 -1
  46. package/dist/feedback/chip/chip.css.js +1 -1
  47. package/dist/feedback/chip/chip.css.js.map +1 -1
  48. package/dist/feedback/chip/chip.d.ts +6 -2
  49. package/dist/feedback/chip/chip.d.ts.map +1 -1
  50. package/dist/feedback/progress/progress.css.cjs +1 -1
  51. package/dist/feedback/progress/progress.css.cjs.map +1 -1
  52. package/dist/feedback/progress/progress.css.js +1 -1
  53. package/dist/feedback/progress/progress.css.js.map +1 -1
  54. package/dist/feedback/progress/progress.d.ts +6 -0
  55. package/dist/feedback/progress/progress.d.ts.map +1 -1
  56. package/dist/frameworks/elements.d.ts +1 -1
  57. package/dist/inputs/button/button.css.cjs +1 -1
  58. package/dist/inputs/button/button.css.cjs.map +1 -1
  59. package/dist/inputs/button/button.css.js +1 -1
  60. package/dist/inputs/button/button.css.js.map +1 -1
  61. package/dist/inputs/checkbox/checkbox.css.cjs +1 -1
  62. package/dist/inputs/checkbox/checkbox.css.cjs.map +1 -1
  63. package/dist/inputs/checkbox/checkbox.css.js +1 -1
  64. package/dist/inputs/checkbox/checkbox.css.js.map +1 -1
  65. package/dist/inputs/counter/counter.css.cjs +2 -0
  66. package/dist/inputs/counter/counter.css.cjs.map +1 -0
  67. package/dist/inputs/counter/counter.css.js +6 -0
  68. package/dist/inputs/counter/counter.css.js.map +1 -0
  69. package/dist/inputs/counter/counter.d.ts +100 -0
  70. package/dist/inputs/counter/counter.d.ts.map +1 -0
  71. package/dist/inputs/index.d.ts +4 -0
  72. package/dist/inputs/index.d.ts.map +1 -1
  73. package/dist/inputs/message-composer/message-composer.css.cjs +1 -1
  74. package/dist/inputs/message-composer/message-composer.css.cjs.map +1 -1
  75. package/dist/inputs/message-composer/message-composer.css.js +1 -1
  76. package/dist/inputs/message-composer/message-composer.css.js.map +1 -1
  77. package/dist/inputs/qr-scanner/qr-scanner.css.cjs +2 -0
  78. package/dist/inputs/qr-scanner/qr-scanner.css.cjs.map +1 -0
  79. package/dist/inputs/qr-scanner/qr-scanner.css.js +6 -0
  80. package/dist/inputs/qr-scanner/qr-scanner.css.js.map +1 -0
  81. package/dist/inputs/qr-scanner/qr-scanner.d.ts +87 -0
  82. package/dist/inputs/qr-scanner/qr-scanner.d.ts.map +1 -0
  83. package/dist/inputs/radio/radio.css.cjs +1 -1
  84. package/dist/inputs/radio/radio.css.cjs.map +1 -1
  85. package/dist/inputs/radio/radio.css.js +1 -1
  86. package/dist/inputs/radio/radio.css.js.map +1 -1
  87. package/dist/inputs/switch/switch.css.cjs +1 -1
  88. package/dist/inputs/switch/switch.css.cjs.map +1 -1
  89. package/dist/inputs/switch/switch.css.js +1 -1
  90. package/dist/inputs/switch/switch.css.js.map +1 -1
  91. package/dist/keyboard-key.cjs +9 -0
  92. package/dist/keyboard-key.cjs.map +1 -0
  93. package/dist/keyboard-key.js +36 -0
  94. package/dist/keyboard-key.js.map +1 -0
  95. package/dist/layout/navbar/navbar.css.cjs +1 -1
  96. package/dist/layout/navbar/navbar.css.cjs.map +1 -1
  97. package/dist/layout/navbar/navbar.css.js +1 -1
  98. package/dist/layout/navbar/navbar.css.js.map +1 -1
  99. package/dist/menu.cjs +1 -1
  100. package/dist/menu.cjs.map +1 -1
  101. package/dist/menu.js +2 -1
  102. package/dist/menu.js.map +1 -1
  103. package/dist/overlay/command-palette/command-palette.css.cjs +1 -1
  104. package/dist/overlay/command-palette/command-palette.css.cjs.map +1 -1
  105. package/dist/overlay/command-palette/command-palette.css.js +1 -1
  106. package/dist/overlay/command-palette/command-palette.css.js.map +1 -1
  107. package/dist/overlay/command-palette/command-palette.d.ts +1 -0
  108. package/dist/overlay/command-palette/command-palette.d.ts.map +1 -1
  109. package/dist/progress.cjs +14 -6
  110. package/dist/progress.cjs.map +1 -1
  111. package/dist/progress.js +19 -7
  112. package/dist/progress.js.map +1 -1
  113. package/dist/qr-code.cjs +15 -0
  114. package/dist/qr-code.cjs.map +1 -0
  115. package/dist/qr-code.js +70 -0
  116. package/dist/qr-code.js.map +1 -0
  117. package/dist/qr-scanner.cjs +46 -0
  118. package/dist/qr-scanner.cjs.map +1 -0
  119. package/dist/qr-scanner.js +129 -0
  120. package/dist/qr-scanner.js.map +1 -0
  121. package/dist/refine.cjs +327 -166
  122. package/dist/refine.cjs.map +1 -1
  123. package/dist/refine.iife.js +355 -194
  124. package/dist/refine.iife.js.map +1 -1
  125. package/dist/refine.js +298 -137
  126. package/dist/refine.js.map +1 -1
  127. package/dist/tabs.cjs +2 -2
  128. package/dist/tabs.cjs.map +1 -1
  129. package/dist/tabs.js +37 -34
  130. package/dist/tabs.js.map +1 -1
  131. package/dist/types/elements.d.ts +9 -0
  132. package/package.json +35 -2
package/dist/tabs.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/keyboard.cjs"),n=require("./core/ore.cjs"),r=require("./core/direction.cjs"),i=require("./core/nav.cjs"),a=require("./shared/prop-bundles.cjs"),o=require("./disclosure/tabs/tabs.css.cjs");let s=require("@vielzeug/ore"),c=require("@vielzeug/ripple");var l=(0,s.createContext)(`TabsContext`),u=`ore-tabs`;(0,s.define)(u,{props:{...a.themableBundle,...a.sizableBundle,activation:s.prop.oneOf([`auto`,`manual`],`auto`),density:s.prop.oneOf([`compact`,`default`],`default`),label:s.prop.string(),orientation:s.prop.oneOf([`horizontal`,`vertical`],`horizontal`),value:s.prop.string(),variant:s.prop.string()},setup(e){let a=(0,s.getHost)(),o=(0,s.useEmit)(),u=a.shadowRoot,d=(0,s.ref)(),f=(0,s.ref)(),p=(0,c.signal)(e.value.value),m=()=>e.activation.value===`manual`,h=()=>e.orientation.value===`vertical`;(0,s.bind)({attr:{value:()=>p.value??null}});let g=()=>[...a.querySelectorAll(`:scope > ore-tab-item[slot="tabs"]`)],_=()=>g().filter(e=>!e.hasAttribute(`disabled`)),v=e=>{e&&(e.shadowRoot?.querySelector(`[role="tab"]`)??e).focus()},y=(e,t=!1)=>{p.value=e,t&&e&&o(`change`,{value:e})},b=()=>{let e=g();if(e.length===0)return;let t=p.value;if(t&&e.some(e=>e.getAttribute(`value`)===t&&!e.hasAttribute(`disabled`)))return;let n=e.find(e=>!e.hasAttribute(`disabled`))?.getAttribute(`value`)??void 0;y(n,!1)};(0,c.watch)(e.value,e=>{p.value=e,b()});let x=i.createListControl({direction:()=>r.elementDirection(a),getItems:()=>_(),loop:!0,onNavigate:({item:e})=>{if(v(e),!m()){let t=e.getAttribute(`value`);t&&y(t,!0)}},orientation:()=>h()?`vertical`:`both`,signal:n.lifecycleSignal(s.onCleanup)});(0,s.provide)(l,{color:e.color,density:(0,c.computed)(()=>e.density.value??`default`),orientation:(0,c.computed)(()=>e.orientation.value??`horizontal`),size:e.size,value:p,variant:e.variant});let S=e=>{let t=f.value,n=d.value;if(!t||!n||!e)return;let r=e.getBoundingClientRect(),i=n.getBoundingClientRect();if(h()){let e=r.top-i.top+n.scrollTop;t.style.transition=`none`,t.style.height=`${r.height}px`,t.style.width=``,t.style.left=`0`,t.style.top=`0`,t.offsetWidth,t.style.transition=``,t.style.transform=`translateY(${e}px)`}else{let e=r.left-i.left+n.scrollLeft;t.style.transition=`none`,t.style.width=`${r.width}px`,t.style.height=``,t.style.top=``,t.style.left=`0`,t.offsetWidth,t.style.transition=``,t.style.transform=`translateX(${e}px)`}},C=()=>{let e=p.value;if(!e)return;let t=g().find(t=>t.getAttribute(`value`)===e);S(t)};(0,c.watch)(p,()=>{requestAnimationFrame(C)});let w=e=>{let t=e.composedPath().find(e=>e instanceof HTMLElement&&e.localName===`ore-tab-item`);if(!t||t.hasAttribute(`disabled`)||t.closest(`ore-tabs`)!==a)return;let n=t.getAttribute(`value`);n&&n!==p.value&&y(n,!0)},T=t.createInteraction({disabled:(0,c.computed)(()=>!m()),onPress:()=>{let e=_().find(e=>e===document.activeElement||e.shadowRoot?.activeElement===document.activeElement)?.getAttribute(`value`);e&&e!==p.value&&y(e,!0)}});return(0,s.bind)({on:{click:w,keydown:e=>{let t=_();if(t.length===0)return;let n=e.composedPath().find(e=>e instanceof HTMLElement&&e.localName===`ore-tab-item`)??t.find(e=>e.getAttribute(`value`)===p.value),r=n?t.indexOf(n):-1;r>=0&&x.set(r),!x.handleKeydown(e)&&T.handleKeydown(e)}}}),(0,s.onMounted)(()=>{let e=()=>{b(),C()},t=u?.querySelector(`slot[name="tabs"]`);return t&&t.addEventListener(`slotchange`,e),e(),requestAnimationFrame(e),()=>{t&&t.removeEventListener(`slotchange`,e)}}),s.html`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/theme.css.cjs"),t=require("./core/keyboard.cjs"),n=require("./core/ore.cjs"),r=require("./core/direction.cjs"),i=require("./core/nav.cjs"),a=require("./shared/prop-bundles.cjs"),o=require("./disclosure/tabs/tabs.css.cjs");let s=require("@vielzeug/ore"),c=require("@vielzeug/ripple");var l=(0,s.createContext)(`TabsContext`),u=`ore-tabs`;(0,s.define)(u,{props:{...a.themableBundle,...a.sizableBundle,activation:s.prop.oneOf([`auto`,`manual`],`auto`),density:s.prop.oneOf([`compact`,`default`],`default`),label:s.prop.string(),orientation:s.prop.oneOf([`horizontal`,`vertical`],`horizontal`),value:s.prop.string(),variant:s.prop.string()},setup(e){let a=(0,s.getHost)(),o=(0,s.useEmit)(),u=a.shadowRoot,d=(0,s.ref)(),f=(0,s.ref)(),p=(0,c.signal)(e.value.value),m=(0,c.signal)(!1),h=()=>e.activation.value===`manual`,g=()=>e.orientation.value===`vertical`;(0,s.bind)({attr:{"data-has-panels":()=>m.value?``:null,value:()=>p.value??null}});let _=()=>[...a.querySelectorAll(`:scope > ore-tab-item[slot="tabs"]`)],v=()=>_().filter(e=>!e.hasAttribute(`disabled`)),y=e=>{e&&(e.shadowRoot?.querySelector(`[role="tab"]`)??e).focus()},b=(e,t=!1)=>{p.value=e,t&&e&&o(`change`,{value:e})},x=()=>{let e=_();if(e.length===0)return;let t=p.value;if(t&&e.some(e=>e.getAttribute(`value`)===t&&!e.hasAttribute(`disabled`)))return;let n=e.find(e=>!e.hasAttribute(`disabled`))?.getAttribute(`value`)??void 0;b(n,!1)};(0,c.watch)(e.value,e=>{p.value=e,x()});let S=i.createListControl({direction:()=>r.elementDirection(a),getItems:()=>v(),loop:!0,onNavigate:({item:e})=>{if(y(e),!h()){let t=e.getAttribute(`value`);t&&b(t,!0)}},orientation:()=>g()?`vertical`:`both`,signal:n.lifecycleSignal(s.onCleanup)});(0,s.provide)(l,{color:e.color,density:(0,c.computed)(()=>e.density.value??`default`),orientation:(0,c.computed)(()=>e.orientation.value??`horizontal`),size:e.size,value:p,variant:e.variant});let C=e=>{let t=f.value,n=d.value;if(!t||!n||!e)return;let r=e.getBoundingClientRect(),i=n.getBoundingClientRect();if(g()){let e=r.top-i.top+n.scrollTop;t.style.transition=`none`,t.style.height=`${r.height}px`,t.style.width=``,t.style.left=`0`,t.style.top=`0`,t.offsetWidth,t.style.transition=``,t.style.transform=`translateY(${e}px)`}else{let e=r.left-i.left+n.scrollLeft;t.style.transition=`none`,t.style.width=`${r.width}px`,t.style.height=``,t.style.top=``,t.style.left=`0`,t.offsetWidth,t.style.transition=``,t.style.transform=`translateX(${e}px)`}},w=()=>{let e=p.value;if(!e)return;let t=_().find(t=>t.getAttribute(`value`)===e);C(t)};(0,c.watch)(p,()=>{requestAnimationFrame(w)});let T=e=>{let t=e.composedPath().find(e=>e instanceof HTMLElement&&e.localName===`ore-tab-item`);if(!t||t.hasAttribute(`disabled`)||t.closest(`ore-tabs`)!==a)return;let n=t.getAttribute(`value`);n&&n!==p.value&&b(n,!0)},E=t.createInteraction({disabled:(0,c.computed)(()=>!h()),onPress:()=>{let e=v().find(e=>e===document.activeElement||e.shadowRoot?.activeElement===document.activeElement)?.getAttribute(`value`);e&&e!==p.value&&b(e,!0)}});return(0,s.bind)({on:{click:T,keydown:e=>{let t=v();if(t.length===0)return;let n=e.composedPath().find(e=>e instanceof HTMLElement&&e.localName===`ore-tab-item`)??t.find(e=>e.getAttribute(`value`)===p.value),r=n?t.indexOf(n):-1;r>=0&&S.set(r),!S.handleKeydown(e)&&E.handleKeydown(e)}}}),(0,s.onMounted)(()=>{let e=u?.querySelector(`slot[name="tabs"]`),t=u?.querySelector(`slot:not([name])`),n=()=>{x(),m.value=t?.assignedElements().some(e=>e.localName===`ore-tab-panel`)??!1,w()};return e?.addEventListener(`slotchange`,n),t?.addEventListener(`slotchange`,n),n(),requestAnimationFrame(n),()=>{e?.removeEventListener(`slotchange`,n),t?.removeEventListener(`slotchange`,n)}}),s.html`
2
2
  <div class="tablist-wrapper">
3
3
  <div
4
4
  role="tablist"
@@ -10,7 +10,7 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
10
10
  </div>
11
11
  <div class="indicator" ref="${f}" part="indicator"></div>
12
12
  </div>
13
- <div class="panels" part="panels">
13
+ <div class="panels" part="panels" ?hidden="${()=>!m.value}">
14
14
  <slot></slot>
15
15
  </div>
16
16
  `},styles:[e.colorThemeMixin,o.default]}),exports.TABS_CTX=l,exports.TABS_TAG=u;
package/dist/tabs.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tabs.cjs","names":[],"sources":["../src/disclosure/tabs/tabs.ts"],"sourcesContent":["import {\n bind,\n createContext,\n define,\n getHost,\n html,\n onCleanup,\n onMounted,\n prop,\n provide,\n ref,\n useEmit,\n} from '@vielzeug/ore';\nimport { computed, type Readable, signal, watch } from '@vielzeug/ripple';\nimport { createInteraction, createListControl, elementDirection, lifecycleSignal } from '../../core';\nimport { sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor, VisualVariant } from '../../types';\nimport styles from './tabs.css?inline';\n\n/** Context provided by ore-tabs to its ore-tab-item and ore-tab-panel children. */\nexport type TabsContext = {\n color: Readable<ThemeColor | undefined>;\n density: Readable<'compact' | 'default'>;\n orientation: Readable<'horizontal' | 'vertical'>;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n variant: Readable<VisualVariant | undefined>;\n};\n/** Injection key for the tabs context. */\nexport const TABS_CTX = createContext<TabsContext>('TabsContext');\n\nexport type OreTabsEvents = {\n change: { value: string };\n};\n\nexport type OreTabsProps = {\n /**\n * Keyboard activation mode.\n * - `'auto'` (default): Selecting a tab on arrow-key focus immediately activates it (ARIA recommendation for most cases).\n * - `'manual'`: Arrow keys only move focus; the user must press Enter or Space to activate the focused tab.\n */\n activation?: 'auto' | 'manual';\n /** Theme color */\n color?: ThemeColor;\n /** Spatial density; compact renders a 24px tab control for dense interfaces. */\n density?: 'compact' | 'default';\n /** Accessible label for the tablist (passed as aria-label). Use when there is no visible heading labelling the tabs. */\n label?: string;\n /** Tab list orientation */\n orientation?: 'horizontal' | 'vertical';\n /** Component size */\n size?: ComponentSize;\n /** Currently selected tab value */\n value?: string;\n /** Visual style variant */\n variant?: VisualVariant;\n};\n\n/**\n * Tabs container. Manages tab selection and syncs state to child tab items and panels.\n *\n * @element ore-tabs\n * @element ore-tab-item - Child element for tab buttons (auto-discovered)\n * @element ore-tab-panel - Child element for tab content (auto-discovered)\n *\n * @attr {string} value - The value of the currently selected tab\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'ghost' | 'frost'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} density - Spatial density: 'default' | 'compact'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n *\n * @fires change - Emitted when the active tab changes with detail: { value: string }\n *\n * @slot tabs - Place `ore-tab-item` elements here\n * @slot - Place `ore-tab-panel` elements here\n *\n * @cssprop --tabs-radius - Border radius of the tablist container and panels\n * @cssprop --tabs-transition - Transition duration/easing for the active indicator\n * @cssprop --tabs-indicator-color - Color of the sliding active indicator line\n * @cssprop --tabs-bg - Background of the host element (flat variant)\n * @cssprop --tabs-tablist-bg - Tablist container background (solid/frost variants)\n * @cssprop --tabs-tablist-border-color - Tablist container border color\n * @part tablist - Container that holds the slotted tab items\n * @part indicator - Active tab indicator element\n * @part panels - Container that holds tab panel content\n * @example\n * ```html\n * <ore-tabs value=\"tab1\" variant=\"bordered\">\n * <ore-tab-item slot=\"tabs\" value=\"tab1\">Overview</ore-tab-item>\n * <ore-tab-item slot=\"tabs\" value=\"tab2\">Settings</ore-tab-item>\n * <ore-tab-panel value=\"tab1\"><p>Overview content</p></ore-tab-panel>\n * <ore-tab-panel value=\"tab2\"><p>Settings content</p></ore-tab-panel>\n * </ore-tabs>\n * ```\n */\nexport const TABS_TAG = 'ore-tabs' as const;\ndefine<OreTabsProps>(TABS_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n activation: prop.oneOf(['auto', 'manual'] as const, 'auto'),\n density: prop.oneOf(['compact', 'default'] as const, 'default'),\n label: prop.string(),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n variant: prop.string<VisualVariant>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreTabsEvents>();\n\n const shadowRoot = el.shadowRoot;\n const tablistRef = ref<HTMLElement>();\n const indicatorRef = ref<HTMLElement>();\n const selectedValue = signal<string | undefined>(props.value.value);\n const isManualActivation = () => props.activation.value === 'manual';\n const isVertical = () => props.orientation.value === 'vertical';\n\n bind({\n attr: {\n value: () => selectedValue.value ?? null,\n },\n });\n\n const getTabs = () => [...el.querySelectorAll<HTMLElement>(':scope > ore-tab-item[slot=\"tabs\"]')];\n const getEnabledTabs = () => getTabs().filter((t) => !t.hasAttribute('disabled'));\n const focusTab = (tab: HTMLElement | undefined) => {\n if (!tab) return;\n\n const focusable = tab.shadowRoot?.querySelector<HTMLElement>('[role=\"tab\"]') ?? tab;\n\n focusable.focus();\n };\n\n // ────────────────────────────────────────────────────────────────\n // Selection State Management\n // ────────────────────────────────────────────────────────────────\n\n const setSelection = (value: string | undefined, shouldEmit = false) => {\n selectedValue.value = value;\n\n if (shouldEmit && value) emit('change', { value });\n };\n\n const ensureSelection = () => {\n const tabs = getTabs();\n\n if (tabs.length === 0) return; // No tabs yet, keep current selection\n\n const current = selectedValue.value;\n const hasCurrent = current\n ? tabs.some((tab) => tab.getAttribute('value') === current && !tab.hasAttribute('disabled'))\n : false;\n\n if (hasCurrent) return;\n\n const firstEnabled = tabs.find((tab) => !tab.hasAttribute('disabled'))?.getAttribute('value') ?? undefined;\n\n setSelection(firstEnabled, false);\n };\n\n watch(props.value, (value) => {\n selectedValue.value = value;\n ensureSelection();\n });\n\n // ────────────────────────────────────────────────────────────────\n // List Control for Keyboard Navigation\n // ────────────────────────────────────────────────────────────────\n\n const listControl = createListControl({\n direction: () => elementDirection(el),\n getItems: () => getEnabledTabs(),\n loop: true,\n onNavigate: ({ item }) => {\n focusTab(item);\n\n if (!isManualActivation()) {\n const value = item.getAttribute('value');\n\n if (value) setSelection(value, true);\n }\n },\n orientation: () => (isVertical() ? 'vertical' : 'both'),\n signal: lifecycleSignal(onCleanup),\n });\n\n // ────────────────────────────────────────────────────────────────\n // Context & Indicator Management\n // ────────────────────────────────────────────────────────────────\n\n provide(TABS_CTX, {\n color: props.color,\n density: computed(() => props.density.value ?? 'default'),\n orientation: computed(() => props.orientation.value ?? 'horizontal'),\n size: props.size,\n value: selectedValue,\n variant: props.variant,\n });\n\n const moveIndicator = (activeTab: HTMLElement | undefined) => {\n const indicator = indicatorRef.value;\n const tablist = tablistRef.value;\n\n if (!indicator || !tablist || !activeTab) return;\n\n const tabRect = activeTab.getBoundingClientRect();\n const listRect = tablist.getBoundingClientRect();\n\n if (isVertical()) {\n const y = tabRect.top - listRect.top + tablist.scrollTop;\n\n indicator.style.transition = 'none';\n indicator.style.height = `${tabRect.height}px`;\n indicator.style.width = '';\n indicator.style.left = '0';\n indicator.style.top = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateY(${y}px)`;\n } else {\n const x = tabRect.left - listRect.left + tablist.scrollLeft;\n\n indicator.style.transition = 'none';\n indicator.style.width = `${tabRect.width}px`;\n indicator.style.height = '';\n indicator.style.top = '';\n indicator.style.left = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateX(${x}px)`;\n }\n };\n\n const updateIndicator = () => {\n const value = selectedValue.value;\n\n if (!value) return;\n\n const activeTab = getTabs().find((t) => t.getAttribute('value') === value);\n\n moveIndicator(activeTab);\n };\n\n watch(selectedValue, () => {\n requestAnimationFrame(updateIndicator);\n });\n\n // ────────────────────────────────────────────────────────────────\n // Event Handlers\n // ────────────────────────────────────────────────────────────────\n\n const handleTabClick = (e: Event) => {\n const tab = e\n .composedPath()\n .find((node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item');\n\n if (!tab || tab.hasAttribute('disabled')) return;\n\n // Guard: only respond to tab-items that belong to THIS tabs instance\n if (tab.closest('ore-tabs') !== el) return;\n\n const value = tab.getAttribute('value');\n\n if (!value || value === selectedValue.value) return;\n\n setSelection(value, true);\n };\n\n const activateFocusedTab = (): void => {\n const tabs = getEnabledTabs();\n const focusedTab = tabs.find(\n (tab) => tab === document.activeElement || tab.shadowRoot?.activeElement === document.activeElement,\n );\n const focusedValue = focusedTab?.getAttribute('value');\n\n if (focusedValue && focusedValue !== selectedValue.value) setSelection(focusedValue, true);\n };\n\n const manualActivationPress = createInteraction({\n disabled: computed(() => !isManualActivation()),\n onPress: activateFocusedTab,\n });\n\n const handleKeydown = (e: KeyboardEvent) => {\n const tabs = getEnabledTabs();\n\n if (tabs.length === 0) return;\n\n const path = e.composedPath();\n const activeTabFromEvent = path.find(\n (node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item',\n );\n\n const tabFromEvent = activeTabFromEvent ?? tabs.find((t) => t.getAttribute('value') === selectedValue.value);\n const focused = tabFromEvent ? tabs.indexOf(tabFromEvent) : -1;\n\n if (focused >= 0) listControl.set(focused);\n\n if (listControl.handleKeydown(e)) return;\n\n manualActivationPress.handleKeydown(e);\n };\n\n bind({\n on: {\n click: handleTabClick,\n keydown: handleKeydown,\n },\n });\n\n // ────────────────────────────────────────────────────────────────\n // Lifecycle\n // ────────────────────────────────────────────────────────────────\n\n onMounted(() => {\n const syncSelection = () => {\n ensureSelection();\n updateIndicator();\n };\n\n const tabsSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"tabs\"]');\n\n if (tabsSlot) {\n tabsSlot.addEventListener('slotchange', syncSelection);\n }\n\n syncSelection();\n requestAnimationFrame(syncSelection);\n\n return () => {\n if (tabsSlot) {\n tabsSlot.removeEventListener('slotchange', syncSelection);\n }\n };\n });\n\n return html`\n <div class=\"tablist-wrapper\">\n <div\n role=\"tablist\"\n ref=\"${tablistRef}\"\n part=\"tablist\"\n aria-orientation=\"${props.orientation}\"\n aria-label=\"${props.label}\">\n <slot name=\"tabs\"></slot>\n </div>\n <div class=\"indicator\" ref=\"${indicatorRef}\" part=\"indicator\"></div>\n </div>\n <div class=\"panels\" part=\"panels\">\n <slot></slot>\n </div>\n `;\n },\n styles: [colorThemeMixin, styles],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"tabs.cjs","names":[],"sources":["../src/disclosure/tabs/tabs.ts"],"sourcesContent":["import {\n bind,\n createContext,\n define,\n getHost,\n html,\n onCleanup,\n onMounted,\n prop,\n provide,\n ref,\n useEmit,\n} from '@vielzeug/ore';\nimport { computed, type Readable, signal, watch } from '@vielzeug/ripple';\nimport { createInteraction, createListControl, elementDirection, lifecycleSignal } from '../../core';\nimport { sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor, VisualVariant } from '../../types';\nimport styles from './tabs.css?inline';\n\n/** Context provided by ore-tabs to its ore-tab-item and ore-tab-panel children. */\nexport type TabsContext = {\n color: Readable<ThemeColor | undefined>;\n density: Readable<'compact' | 'default'>;\n orientation: Readable<'horizontal' | 'vertical'>;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n variant: Readable<VisualVariant | undefined>;\n};\n/** Injection key for the tabs context. */\nexport const TABS_CTX = createContext<TabsContext>('TabsContext');\n\nexport type OreTabsEvents = {\n change: { value: string };\n};\n\nexport type OreTabsProps = {\n /**\n * Keyboard activation mode.\n * - `'auto'` (default): Selecting a tab on arrow-key focus immediately activates it (ARIA recommendation for most cases).\n * - `'manual'`: Arrow keys only move focus; the user must press Enter or Space to activate the focused tab.\n */\n activation?: 'auto' | 'manual';\n /** Theme color */\n color?: ThemeColor;\n /** Spatial density; compact renders a 24px tab control for dense interfaces. */\n density?: 'compact' | 'default';\n /** Accessible label for the tablist (passed as aria-label). Use when there is no visible heading labelling the tabs. */\n label?: string;\n /** Tab list orientation */\n orientation?: 'horizontal' | 'vertical';\n /** Component size */\n size?: ComponentSize;\n /** Currently selected tab value */\n value?: string;\n /** Visual style variant */\n variant?: VisualVariant;\n};\n\n/**\n * Tabs container. Manages tab selection and syncs state to child tab items and panels.\n *\n * @element ore-tabs\n * @element ore-tab-item - Child element for tab buttons (auto-discovered)\n * @element ore-tab-panel - Child element for tab content (auto-discovered)\n *\n * @attr {string} value - The value of the currently selected tab\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'ghost' | 'frost'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} density - Spatial density: 'default' | 'compact'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n *\n * @fires change - Emitted when the active tab changes with detail: { value: string }\n *\n * @slot tabs - Place `ore-tab-item` elements here\n * @slot - Place `ore-tab-panel` elements here\n *\n * @cssprop --tabs-radius - Border radius of the tablist container and panels\n * @cssprop --tabs-transition - Transition duration/easing for the active indicator\n * @cssprop --tabs-indicator-color - Color of the sliding active indicator line\n * @cssprop --tabs-bg - Background of the host element (flat variant)\n * @cssprop --tabs-tablist-bg - Tablist container background (solid/frost variants)\n * @cssprop --tabs-tablist-border-color - Tablist container border color\n * @part tablist - Container that holds the slotted tab items\n * @part indicator - Active tab indicator element\n * @part panels - Container that holds tab panel content\n * @example\n * ```html\n * <ore-tabs value=\"tab1\" variant=\"bordered\">\n * <ore-tab-item slot=\"tabs\" value=\"tab1\">Overview</ore-tab-item>\n * <ore-tab-item slot=\"tabs\" value=\"tab2\">Settings</ore-tab-item>\n * <ore-tab-panel value=\"tab1\"><p>Overview content</p></ore-tab-panel>\n * <ore-tab-panel value=\"tab2\"><p>Settings content</p></ore-tab-panel>\n * </ore-tabs>\n * ```\n */\nexport const TABS_TAG = 'ore-tabs' as const;\ndefine<OreTabsProps>(TABS_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n activation: prop.oneOf(['auto', 'manual'] as const, 'auto'),\n density: prop.oneOf(['compact', 'default'] as const, 'default'),\n label: prop.string(),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n variant: prop.string<VisualVariant>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreTabsEvents>();\n\n const shadowRoot = el.shadowRoot;\n const tablistRef = ref<HTMLElement>();\n const indicatorRef = ref<HTMLElement>();\n const selectedValue = signal<string | undefined>(props.value.value);\n const hasPanels = signal(false);\n const isManualActivation = () => props.activation.value === 'manual';\n const isVertical = () => props.orientation.value === 'vertical';\n\n bind({\n attr: {\n 'data-has-panels': () => (hasPanels.value ? '' : null),\n value: () => selectedValue.value ?? null,\n },\n });\n\n const getTabs = () => [...el.querySelectorAll<HTMLElement>(':scope > ore-tab-item[slot=\"tabs\"]')];\n const getEnabledTabs = () => getTabs().filter((t) => !t.hasAttribute('disabled'));\n const focusTab = (tab: HTMLElement | undefined) => {\n if (!tab) return;\n\n const focusable = tab.shadowRoot?.querySelector<HTMLElement>('[role=\"tab\"]') ?? tab;\n\n focusable.focus();\n };\n\n // ────────────────────────────────────────────────────────────────\n // Selection State Management\n // ────────────────────────────────────────────────────────────────\n\n const setSelection = (value: string | undefined, shouldEmit = false) => {\n selectedValue.value = value;\n\n if (shouldEmit && value) emit('change', { value });\n };\n\n const ensureSelection = () => {\n const tabs = getTabs();\n\n if (tabs.length === 0) return; // No tabs yet, keep current selection\n\n const current = selectedValue.value;\n const hasCurrent = current\n ? tabs.some((tab) => tab.getAttribute('value') === current && !tab.hasAttribute('disabled'))\n : false;\n\n if (hasCurrent) return;\n\n const firstEnabled = tabs.find((tab) => !tab.hasAttribute('disabled'))?.getAttribute('value') ?? undefined;\n\n setSelection(firstEnabled, false);\n };\n\n watch(props.value, (value) => {\n selectedValue.value = value;\n ensureSelection();\n });\n\n // ────────────────────────────────────────────────────────────────\n // List Control for Keyboard Navigation\n // ────────────────────────────────────────────────────────────────\n\n const listControl = createListControl({\n direction: () => elementDirection(el),\n getItems: () => getEnabledTabs(),\n loop: true,\n onNavigate: ({ item }) => {\n focusTab(item);\n\n if (!isManualActivation()) {\n const value = item.getAttribute('value');\n\n if (value) setSelection(value, true);\n }\n },\n orientation: () => (isVertical() ? 'vertical' : 'both'),\n signal: lifecycleSignal(onCleanup),\n });\n\n // ────────────────────────────────────────────────────────────────\n // Context & Indicator Management\n // ────────────────────────────────────────────────────────────────\n\n provide(TABS_CTX, {\n color: props.color,\n density: computed(() => props.density.value ?? 'default'),\n orientation: computed(() => props.orientation.value ?? 'horizontal'),\n size: props.size,\n value: selectedValue,\n variant: props.variant,\n });\n\n const moveIndicator = (activeTab: HTMLElement | undefined) => {\n const indicator = indicatorRef.value;\n const tablist = tablistRef.value;\n\n if (!indicator || !tablist || !activeTab) return;\n\n const tabRect = activeTab.getBoundingClientRect();\n const listRect = tablist.getBoundingClientRect();\n\n if (isVertical()) {\n const y = tabRect.top - listRect.top + tablist.scrollTop;\n\n indicator.style.transition = 'none';\n indicator.style.height = `${tabRect.height}px`;\n indicator.style.width = '';\n indicator.style.left = '0';\n indicator.style.top = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateY(${y}px)`;\n } else {\n const x = tabRect.left - listRect.left + tablist.scrollLeft;\n\n indicator.style.transition = 'none';\n indicator.style.width = `${tabRect.width}px`;\n indicator.style.height = '';\n indicator.style.top = '';\n indicator.style.left = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateX(${x}px)`;\n }\n };\n\n const updateIndicator = () => {\n const value = selectedValue.value;\n\n if (!value) return;\n\n const activeTab = getTabs().find((t) => t.getAttribute('value') === value);\n\n moveIndicator(activeTab);\n };\n\n watch(selectedValue, () => {\n requestAnimationFrame(updateIndicator);\n });\n\n // ────────────────────────────────────────────────────────────────\n // Event Handlers\n // ────────────────────────────────────────────────────────────────\n\n const handleTabClick = (e: Event) => {\n const tab = e\n .composedPath()\n .find((node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item');\n\n if (!tab || tab.hasAttribute('disabled')) return;\n\n // Guard: only respond to tab-items that belong to THIS tabs instance\n if (tab.closest('ore-tabs') !== el) return;\n\n const value = tab.getAttribute('value');\n\n if (!value || value === selectedValue.value) return;\n\n setSelection(value, true);\n };\n\n const activateFocusedTab = (): void => {\n const tabs = getEnabledTabs();\n const focusedTab = tabs.find(\n (tab) => tab === document.activeElement || tab.shadowRoot?.activeElement === document.activeElement,\n );\n const focusedValue = focusedTab?.getAttribute('value');\n\n if (focusedValue && focusedValue !== selectedValue.value) setSelection(focusedValue, true);\n };\n\n const manualActivationPress = createInteraction({\n disabled: computed(() => !isManualActivation()),\n onPress: activateFocusedTab,\n });\n\n const handleKeydown = (e: KeyboardEvent) => {\n const tabs = getEnabledTabs();\n\n if (tabs.length === 0) return;\n\n const path = e.composedPath();\n const activeTabFromEvent = path.find(\n (node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item',\n );\n\n const tabFromEvent = activeTabFromEvent ?? tabs.find((t) => t.getAttribute('value') === selectedValue.value);\n const focused = tabFromEvent ? tabs.indexOf(tabFromEvent) : -1;\n\n if (focused >= 0) listControl.set(focused);\n\n if (listControl.handleKeydown(e)) return;\n\n manualActivationPress.handleKeydown(e);\n };\n\n bind({\n on: {\n click: handleTabClick,\n keydown: handleKeydown,\n },\n });\n\n // ────────────────────────────────────────────────────────────────\n // Lifecycle\n // ────────────────────────────────────────────────────────────────\n\n onMounted(() => {\n const tabsSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"tabs\"]');\n const panelsSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n const sync = () => {\n ensureSelection();\n hasPanels.value = panelsSlot?.assignedElements().some((child) => child.localName === 'ore-tab-panel') ?? false;\n updateIndicator();\n };\n\n tabsSlot?.addEventListener('slotchange', sync);\n panelsSlot?.addEventListener('slotchange', sync);\n\n sync();\n requestAnimationFrame(sync);\n\n return () => {\n tabsSlot?.removeEventListener('slotchange', sync);\n panelsSlot?.removeEventListener('slotchange', sync);\n };\n });\n\n return html`\n <div class=\"tablist-wrapper\">\n <div\n role=\"tablist\"\n ref=\"${tablistRef}\"\n part=\"tablist\"\n aria-orientation=\"${props.orientation}\"\n aria-label=\"${props.label}\">\n <slot name=\"tabs\"></slot>\n </div>\n <div class=\"indicator\" ref=\"${indicatorRef}\" part=\"indicator\"></div>\n </div>\n <div class=\"panels\" part=\"panels\" ?hidden=\"${() => !hasPanels.value}\">\n <slot></slot>\n </div>\n `;\n },\n styles: [colorThemeMixin, styles],\n});\n"],"mappings":"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"}
package/dist/tabs.js CHANGED
@@ -21,34 +21,37 @@ u(C, {
21
21
  variant: h.string()
22
22
  },
23
23
  setup(e) {
24
- let a = d(), o = v(), s = a.shadowRoot, l = _(), u = _(), h = b(e.value.value), C = () => e.activation.value === "manual", w = () => e.orientation.value === "vertical";
25
- c({ attr: { value: () => h.value ?? null } });
26
- let T = () => [...a.querySelectorAll(":scope > ore-tab-item[slot=\"tabs\"]")], E = () => T().filter((e) => !e.hasAttribute("disabled")), D = (e) => {
24
+ let a = d(), o = v(), s = a.shadowRoot, l = _(), u = _(), h = b(e.value.value), C = b(!1), w = () => e.activation.value === "manual", T = () => e.orientation.value === "vertical";
25
+ c({ attr: {
26
+ "data-has-panels": () => C.value ? "" : null,
27
+ value: () => h.value ?? null
28
+ } });
29
+ let E = () => [...a.querySelectorAll(":scope > ore-tab-item[slot=\"tabs\"]")], D = () => E().filter((e) => !e.hasAttribute("disabled")), O = (e) => {
27
30
  e && (e.shadowRoot?.querySelector("[role=\"tab\"]") ?? e).focus();
28
- }, O = (e, t = !1) => {
31
+ }, k = (e, t = !1) => {
29
32
  h.value = e, t && e && o("change", { value: e });
30
- }, k = () => {
31
- let e = T();
33
+ }, A = () => {
34
+ let e = E();
32
35
  if (e.length === 0) return;
33
36
  let t = h.value;
34
37
  if (t && e.some((e) => e.getAttribute("value") === t && !e.hasAttribute("disabled"))) return;
35
38
  let n = e.find((e) => !e.hasAttribute("disabled"))?.getAttribute("value") ?? void 0;
36
- O(n, !1);
39
+ k(n, !1);
37
40
  };
38
41
  x(e.value, (e) => {
39
- h.value = e, k();
42
+ h.value = e, A();
40
43
  });
41
- let A = i({
44
+ let j = i({
42
45
  direction: () => r(a),
43
- getItems: () => E(),
46
+ getItems: () => D(),
44
47
  loop: !0,
45
48
  onNavigate: ({ item: e }) => {
46
- if (D(e), !C()) {
49
+ if (O(e), !w()) {
47
50
  let t = e.getAttribute("value");
48
- t && O(t, !0);
51
+ t && k(t, !0);
49
52
  }
50
53
  },
51
- orientation: () => w() ? "vertical" : "both",
54
+ orientation: () => T() ? "vertical" : "both",
52
55
  signal: n(p)
53
56
  });
54
57
  g(S, {
@@ -59,52 +62,52 @@ u(C, {
59
62
  value: h,
60
63
  variant: e.variant
61
64
  });
62
- let j = (e) => {
65
+ let M = (e) => {
63
66
  let t = u.value, n = l.value;
64
67
  if (!t || !n || !e) return;
65
68
  let r = e.getBoundingClientRect(), i = n.getBoundingClientRect();
66
- if (w()) {
69
+ if (T()) {
67
70
  let e = r.top - i.top + n.scrollTop;
68
71
  t.style.transition = "none", t.style.height = `${r.height}px`, t.style.width = "", t.style.left = "0", t.style.top = "0", t.offsetWidth, t.style.transition = "", t.style.transform = `translateY(${e}px)`;
69
72
  } else {
70
73
  let e = r.left - i.left + n.scrollLeft;
71
74
  t.style.transition = "none", t.style.width = `${r.width}px`, t.style.height = "", t.style.top = "", t.style.left = "0", t.offsetWidth, t.style.transition = "", t.style.transform = `translateX(${e}px)`;
72
75
  }
73
- }, M = () => {
76
+ }, N = () => {
74
77
  let e = h.value;
75
78
  if (!e) return;
76
- let t = T().find((t) => t.getAttribute("value") === e);
77
- j(t);
79
+ let t = E().find((t) => t.getAttribute("value") === e);
80
+ M(t);
78
81
  };
79
82
  x(h, () => {
80
- requestAnimationFrame(M);
83
+ requestAnimationFrame(N);
81
84
  });
82
- let N = (e) => {
85
+ let P = (e) => {
83
86
  let t = e.composedPath().find((e) => e instanceof HTMLElement && e.localName === "ore-tab-item");
84
87
  if (!t || t.hasAttribute("disabled") || t.closest("ore-tabs") !== a) return;
85
88
  let n = t.getAttribute("value");
86
- n && n !== h.value && O(n, !0);
87
- }, P = t({
88
- disabled: y(() => !C()),
89
+ n && n !== h.value && k(n, !0);
90
+ }, F = t({
91
+ disabled: y(() => !w()),
89
92
  onPress: () => {
90
- let e = E().find((e) => e === document.activeElement || e.shadowRoot?.activeElement === document.activeElement)?.getAttribute("value");
91
- e && e !== h.value && O(e, !0);
93
+ let e = D().find((e) => e === document.activeElement || e.shadowRoot?.activeElement === document.activeElement)?.getAttribute("value");
94
+ e && e !== h.value && k(e, !0);
92
95
  }
93
96
  });
94
97
  return c({ on: {
95
- click: N,
98
+ click: P,
96
99
  keydown: (e) => {
97
- let t = E();
100
+ let t = D();
98
101
  if (t.length === 0) return;
99
102
  let n = e.composedPath().find((e) => e instanceof HTMLElement && e.localName === "ore-tab-item") ?? t.find((e) => e.getAttribute("value") === h.value), r = n ? t.indexOf(n) : -1;
100
- r >= 0 && A.set(r), !A.handleKeydown(e) && P.handleKeydown(e);
103
+ r >= 0 && j.set(r), !j.handleKeydown(e) && F.handleKeydown(e);
101
104
  }
102
105
  } }), m(() => {
103
- let e = () => {
104
- k(), M();
105
- }, t = s?.querySelector("slot[name=\"tabs\"]");
106
- return t && t.addEventListener("slotchange", e), e(), requestAnimationFrame(e), () => {
107
- t && t.removeEventListener("slotchange", e);
106
+ let e = s?.querySelector("slot[name=\"tabs\"]"), t = s?.querySelector("slot:not([name])"), n = () => {
107
+ A(), C.value = t?.assignedElements().some((e) => e.localName === "ore-tab-panel") ?? !1, N();
108
+ };
109
+ return e?.addEventListener("slotchange", n), t?.addEventListener("slotchange", n), n(), requestAnimationFrame(n), () => {
110
+ e?.removeEventListener("slotchange", n), t?.removeEventListener("slotchange", n);
108
111
  };
109
112
  }), f`
110
113
  <div class="tablist-wrapper">
@@ -118,7 +121,7 @@ u(C, {
118
121
  </div>
119
122
  <div class="indicator" ref="${u}" part="indicator"></div>
120
123
  </div>
121
- <div class="panels" part="panels">
124
+ <div class="panels" part="panels" ?hidden="${() => !C.value}">
122
125
  <slot></slot>
123
126
  </div>
124
127
  `;
package/dist/tabs.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tabs.js","names":[],"sources":["../src/disclosure/tabs/tabs.ts"],"sourcesContent":["import {\n bind,\n createContext,\n define,\n getHost,\n html,\n onCleanup,\n onMounted,\n prop,\n provide,\n ref,\n useEmit,\n} from '@vielzeug/ore';\nimport { computed, type Readable, signal, watch } from '@vielzeug/ripple';\nimport { createInteraction, createListControl, elementDirection, lifecycleSignal } from '../../core';\nimport { sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor, VisualVariant } from '../../types';\nimport styles from './tabs.css?inline';\n\n/** Context provided by ore-tabs to its ore-tab-item and ore-tab-panel children. */\nexport type TabsContext = {\n color: Readable<ThemeColor | undefined>;\n density: Readable<'compact' | 'default'>;\n orientation: Readable<'horizontal' | 'vertical'>;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n variant: Readable<VisualVariant | undefined>;\n};\n/** Injection key for the tabs context. */\nexport const TABS_CTX = createContext<TabsContext>('TabsContext');\n\nexport type OreTabsEvents = {\n change: { value: string };\n};\n\nexport type OreTabsProps = {\n /**\n * Keyboard activation mode.\n * - `'auto'` (default): Selecting a tab on arrow-key focus immediately activates it (ARIA recommendation for most cases).\n * - `'manual'`: Arrow keys only move focus; the user must press Enter or Space to activate the focused tab.\n */\n activation?: 'auto' | 'manual';\n /** Theme color */\n color?: ThemeColor;\n /** Spatial density; compact renders a 24px tab control for dense interfaces. */\n density?: 'compact' | 'default';\n /** Accessible label for the tablist (passed as aria-label). Use when there is no visible heading labelling the tabs. */\n label?: string;\n /** Tab list orientation */\n orientation?: 'horizontal' | 'vertical';\n /** Component size */\n size?: ComponentSize;\n /** Currently selected tab value */\n value?: string;\n /** Visual style variant */\n variant?: VisualVariant;\n};\n\n/**\n * Tabs container. Manages tab selection and syncs state to child tab items and panels.\n *\n * @element ore-tabs\n * @element ore-tab-item - Child element for tab buttons (auto-discovered)\n * @element ore-tab-panel - Child element for tab content (auto-discovered)\n *\n * @attr {string} value - The value of the currently selected tab\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'ghost' | 'frost'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} density - Spatial density: 'default' | 'compact'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n *\n * @fires change - Emitted when the active tab changes with detail: { value: string }\n *\n * @slot tabs - Place `ore-tab-item` elements here\n * @slot - Place `ore-tab-panel` elements here\n *\n * @cssprop --tabs-radius - Border radius of the tablist container and panels\n * @cssprop --tabs-transition - Transition duration/easing for the active indicator\n * @cssprop --tabs-indicator-color - Color of the sliding active indicator line\n * @cssprop --tabs-bg - Background of the host element (flat variant)\n * @cssprop --tabs-tablist-bg - Tablist container background (solid/frost variants)\n * @cssprop --tabs-tablist-border-color - Tablist container border color\n * @part tablist - Container that holds the slotted tab items\n * @part indicator - Active tab indicator element\n * @part panels - Container that holds tab panel content\n * @example\n * ```html\n * <ore-tabs value=\"tab1\" variant=\"bordered\">\n * <ore-tab-item slot=\"tabs\" value=\"tab1\">Overview</ore-tab-item>\n * <ore-tab-item slot=\"tabs\" value=\"tab2\">Settings</ore-tab-item>\n * <ore-tab-panel value=\"tab1\"><p>Overview content</p></ore-tab-panel>\n * <ore-tab-panel value=\"tab2\"><p>Settings content</p></ore-tab-panel>\n * </ore-tabs>\n * ```\n */\nexport const TABS_TAG = 'ore-tabs' as const;\ndefine<OreTabsProps>(TABS_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n activation: prop.oneOf(['auto', 'manual'] as const, 'auto'),\n density: prop.oneOf(['compact', 'default'] as const, 'default'),\n label: prop.string(),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n variant: prop.string<VisualVariant>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreTabsEvents>();\n\n const shadowRoot = el.shadowRoot;\n const tablistRef = ref<HTMLElement>();\n const indicatorRef = ref<HTMLElement>();\n const selectedValue = signal<string | undefined>(props.value.value);\n const isManualActivation = () => props.activation.value === 'manual';\n const isVertical = () => props.orientation.value === 'vertical';\n\n bind({\n attr: {\n value: () => selectedValue.value ?? null,\n },\n });\n\n const getTabs = () => [...el.querySelectorAll<HTMLElement>(':scope > ore-tab-item[slot=\"tabs\"]')];\n const getEnabledTabs = () => getTabs().filter((t) => !t.hasAttribute('disabled'));\n const focusTab = (tab: HTMLElement | undefined) => {\n if (!tab) return;\n\n const focusable = tab.shadowRoot?.querySelector<HTMLElement>('[role=\"tab\"]') ?? tab;\n\n focusable.focus();\n };\n\n // ────────────────────────────────────────────────────────────────\n // Selection State Management\n // ────────────────────────────────────────────────────────────────\n\n const setSelection = (value: string | undefined, shouldEmit = false) => {\n selectedValue.value = value;\n\n if (shouldEmit && value) emit('change', { value });\n };\n\n const ensureSelection = () => {\n const tabs = getTabs();\n\n if (tabs.length === 0) return; // No tabs yet, keep current selection\n\n const current = selectedValue.value;\n const hasCurrent = current\n ? tabs.some((tab) => tab.getAttribute('value') === current && !tab.hasAttribute('disabled'))\n : false;\n\n if (hasCurrent) return;\n\n const firstEnabled = tabs.find((tab) => !tab.hasAttribute('disabled'))?.getAttribute('value') ?? undefined;\n\n setSelection(firstEnabled, false);\n };\n\n watch(props.value, (value) => {\n selectedValue.value = value;\n ensureSelection();\n });\n\n // ────────────────────────────────────────────────────────────────\n // List Control for Keyboard Navigation\n // ────────────────────────────────────────────────────────────────\n\n const listControl = createListControl({\n direction: () => elementDirection(el),\n getItems: () => getEnabledTabs(),\n loop: true,\n onNavigate: ({ item }) => {\n focusTab(item);\n\n if (!isManualActivation()) {\n const value = item.getAttribute('value');\n\n if (value) setSelection(value, true);\n }\n },\n orientation: () => (isVertical() ? 'vertical' : 'both'),\n signal: lifecycleSignal(onCleanup),\n });\n\n // ────────────────────────────────────────────────────────────────\n // Context & Indicator Management\n // ────────────────────────────────────────────────────────────────\n\n provide(TABS_CTX, {\n color: props.color,\n density: computed(() => props.density.value ?? 'default'),\n orientation: computed(() => props.orientation.value ?? 'horizontal'),\n size: props.size,\n value: selectedValue,\n variant: props.variant,\n });\n\n const moveIndicator = (activeTab: HTMLElement | undefined) => {\n const indicator = indicatorRef.value;\n const tablist = tablistRef.value;\n\n if (!indicator || !tablist || !activeTab) return;\n\n const tabRect = activeTab.getBoundingClientRect();\n const listRect = tablist.getBoundingClientRect();\n\n if (isVertical()) {\n const y = tabRect.top - listRect.top + tablist.scrollTop;\n\n indicator.style.transition = 'none';\n indicator.style.height = `${tabRect.height}px`;\n indicator.style.width = '';\n indicator.style.left = '0';\n indicator.style.top = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateY(${y}px)`;\n } else {\n const x = tabRect.left - listRect.left + tablist.scrollLeft;\n\n indicator.style.transition = 'none';\n indicator.style.width = `${tabRect.width}px`;\n indicator.style.height = '';\n indicator.style.top = '';\n indicator.style.left = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateX(${x}px)`;\n }\n };\n\n const updateIndicator = () => {\n const value = selectedValue.value;\n\n if (!value) return;\n\n const activeTab = getTabs().find((t) => t.getAttribute('value') === value);\n\n moveIndicator(activeTab);\n };\n\n watch(selectedValue, () => {\n requestAnimationFrame(updateIndicator);\n });\n\n // ────────────────────────────────────────────────────────────────\n // Event Handlers\n // ────────────────────────────────────────────────────────────────\n\n const handleTabClick = (e: Event) => {\n const tab = e\n .composedPath()\n .find((node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item');\n\n if (!tab || tab.hasAttribute('disabled')) return;\n\n // Guard: only respond to tab-items that belong to THIS tabs instance\n if (tab.closest('ore-tabs') !== el) return;\n\n const value = tab.getAttribute('value');\n\n if (!value || value === selectedValue.value) return;\n\n setSelection(value, true);\n };\n\n const activateFocusedTab = (): void => {\n const tabs = getEnabledTabs();\n const focusedTab = tabs.find(\n (tab) => tab === document.activeElement || tab.shadowRoot?.activeElement === document.activeElement,\n );\n const focusedValue = focusedTab?.getAttribute('value');\n\n if (focusedValue && focusedValue !== selectedValue.value) setSelection(focusedValue, true);\n };\n\n const manualActivationPress = createInteraction({\n disabled: computed(() => !isManualActivation()),\n onPress: activateFocusedTab,\n });\n\n const handleKeydown = (e: KeyboardEvent) => {\n const tabs = getEnabledTabs();\n\n if (tabs.length === 0) return;\n\n const path = e.composedPath();\n const activeTabFromEvent = path.find(\n (node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item',\n );\n\n const tabFromEvent = activeTabFromEvent ?? tabs.find((t) => t.getAttribute('value') === selectedValue.value);\n const focused = tabFromEvent ? tabs.indexOf(tabFromEvent) : -1;\n\n if (focused >= 0) listControl.set(focused);\n\n if (listControl.handleKeydown(e)) return;\n\n manualActivationPress.handleKeydown(e);\n };\n\n bind({\n on: {\n click: handleTabClick,\n keydown: handleKeydown,\n },\n });\n\n // ────────────────────────────────────────────────────────────────\n // Lifecycle\n // ────────────────────────────────────────────────────────────────\n\n onMounted(() => {\n const syncSelection = () => {\n ensureSelection();\n updateIndicator();\n };\n\n const tabsSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"tabs\"]');\n\n if (tabsSlot) {\n tabsSlot.addEventListener('slotchange', syncSelection);\n }\n\n syncSelection();\n requestAnimationFrame(syncSelection);\n\n return () => {\n if (tabsSlot) {\n tabsSlot.removeEventListener('slotchange', syncSelection);\n }\n };\n });\n\n return html`\n <div class=\"tablist-wrapper\">\n <div\n role=\"tablist\"\n ref=\"${tablistRef}\"\n part=\"tablist\"\n aria-orientation=\"${props.orientation}\"\n aria-label=\"${props.label}\">\n <slot name=\"tabs\"></slot>\n </div>\n <div class=\"indicator\" ref=\"${indicatorRef}\" part=\"indicator\"></div>\n </div>\n <div class=\"panels\" part=\"panels\">\n <slot></slot>\n </div>\n `;\n },\n styles: [colorThemeMixin, styles],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"tabs.js","names":[],"sources":["../src/disclosure/tabs/tabs.ts"],"sourcesContent":["import {\n bind,\n createContext,\n define,\n getHost,\n html,\n onCleanup,\n onMounted,\n prop,\n provide,\n ref,\n useEmit,\n} from '@vielzeug/ore';\nimport { computed, type Readable, signal, watch } from '@vielzeug/ripple';\nimport { createInteraction, createListControl, elementDirection, lifecycleSignal } from '../../core';\nimport { sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor, VisualVariant } from '../../types';\nimport styles from './tabs.css?inline';\n\n/** Context provided by ore-tabs to its ore-tab-item and ore-tab-panel children. */\nexport type TabsContext = {\n color: Readable<ThemeColor | undefined>;\n density: Readable<'compact' | 'default'>;\n orientation: Readable<'horizontal' | 'vertical'>;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n variant: Readable<VisualVariant | undefined>;\n};\n/** Injection key for the tabs context. */\nexport const TABS_CTX = createContext<TabsContext>('TabsContext');\n\nexport type OreTabsEvents = {\n change: { value: string };\n};\n\nexport type OreTabsProps = {\n /**\n * Keyboard activation mode.\n * - `'auto'` (default): Selecting a tab on arrow-key focus immediately activates it (ARIA recommendation for most cases).\n * - `'manual'`: Arrow keys only move focus; the user must press Enter or Space to activate the focused tab.\n */\n activation?: 'auto' | 'manual';\n /** Theme color */\n color?: ThemeColor;\n /** Spatial density; compact renders a 24px tab control for dense interfaces. */\n density?: 'compact' | 'default';\n /** Accessible label for the tablist (passed as aria-label). Use when there is no visible heading labelling the tabs. */\n label?: string;\n /** Tab list orientation */\n orientation?: 'horizontal' | 'vertical';\n /** Component size */\n size?: ComponentSize;\n /** Currently selected tab value */\n value?: string;\n /** Visual style variant */\n variant?: VisualVariant;\n};\n\n/**\n * Tabs container. Manages tab selection and syncs state to child tab items and panels.\n *\n * @element ore-tabs\n * @element ore-tab-item - Child element for tab buttons (auto-discovered)\n * @element ore-tab-panel - Child element for tab content (auto-discovered)\n *\n * @attr {string} value - The value of the currently selected tab\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'ghost' | 'frost'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} density - Spatial density: 'default' | 'compact'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n *\n * @fires change - Emitted when the active tab changes with detail: { value: string }\n *\n * @slot tabs - Place `ore-tab-item` elements here\n * @slot - Place `ore-tab-panel` elements here\n *\n * @cssprop --tabs-radius - Border radius of the tablist container and panels\n * @cssprop --tabs-transition - Transition duration/easing for the active indicator\n * @cssprop --tabs-indicator-color - Color of the sliding active indicator line\n * @cssprop --tabs-bg - Background of the host element (flat variant)\n * @cssprop --tabs-tablist-bg - Tablist container background (solid/frost variants)\n * @cssprop --tabs-tablist-border-color - Tablist container border color\n * @part tablist - Container that holds the slotted tab items\n * @part indicator - Active tab indicator element\n * @part panels - Container that holds tab panel content\n * @example\n * ```html\n * <ore-tabs value=\"tab1\" variant=\"bordered\">\n * <ore-tab-item slot=\"tabs\" value=\"tab1\">Overview</ore-tab-item>\n * <ore-tab-item slot=\"tabs\" value=\"tab2\">Settings</ore-tab-item>\n * <ore-tab-panel value=\"tab1\"><p>Overview content</p></ore-tab-panel>\n * <ore-tab-panel value=\"tab2\"><p>Settings content</p></ore-tab-panel>\n * </ore-tabs>\n * ```\n */\nexport const TABS_TAG = 'ore-tabs' as const;\ndefine<OreTabsProps>(TABS_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n activation: prop.oneOf(['auto', 'manual'] as const, 'auto'),\n density: prop.oneOf(['compact', 'default'] as const, 'default'),\n label: prop.string(),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n variant: prop.string<VisualVariant>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreTabsEvents>();\n\n const shadowRoot = el.shadowRoot;\n const tablistRef = ref<HTMLElement>();\n const indicatorRef = ref<HTMLElement>();\n const selectedValue = signal<string | undefined>(props.value.value);\n const hasPanels = signal(false);\n const isManualActivation = () => props.activation.value === 'manual';\n const isVertical = () => props.orientation.value === 'vertical';\n\n bind({\n attr: {\n 'data-has-panels': () => (hasPanels.value ? '' : null),\n value: () => selectedValue.value ?? null,\n },\n });\n\n const getTabs = () => [...el.querySelectorAll<HTMLElement>(':scope > ore-tab-item[slot=\"tabs\"]')];\n const getEnabledTabs = () => getTabs().filter((t) => !t.hasAttribute('disabled'));\n const focusTab = (tab: HTMLElement | undefined) => {\n if (!tab) return;\n\n const focusable = tab.shadowRoot?.querySelector<HTMLElement>('[role=\"tab\"]') ?? tab;\n\n focusable.focus();\n };\n\n // ────────────────────────────────────────────────────────────────\n // Selection State Management\n // ────────────────────────────────────────────────────────────────\n\n const setSelection = (value: string | undefined, shouldEmit = false) => {\n selectedValue.value = value;\n\n if (shouldEmit && value) emit('change', { value });\n };\n\n const ensureSelection = () => {\n const tabs = getTabs();\n\n if (tabs.length === 0) return; // No tabs yet, keep current selection\n\n const current = selectedValue.value;\n const hasCurrent = current\n ? tabs.some((tab) => tab.getAttribute('value') === current && !tab.hasAttribute('disabled'))\n : false;\n\n if (hasCurrent) return;\n\n const firstEnabled = tabs.find((tab) => !tab.hasAttribute('disabled'))?.getAttribute('value') ?? undefined;\n\n setSelection(firstEnabled, false);\n };\n\n watch(props.value, (value) => {\n selectedValue.value = value;\n ensureSelection();\n });\n\n // ────────────────────────────────────────────────────────────────\n // List Control for Keyboard Navigation\n // ────────────────────────────────────────────────────────────────\n\n const listControl = createListControl({\n direction: () => elementDirection(el),\n getItems: () => getEnabledTabs(),\n loop: true,\n onNavigate: ({ item }) => {\n focusTab(item);\n\n if (!isManualActivation()) {\n const value = item.getAttribute('value');\n\n if (value) setSelection(value, true);\n }\n },\n orientation: () => (isVertical() ? 'vertical' : 'both'),\n signal: lifecycleSignal(onCleanup),\n });\n\n // ────────────────────────────────────────────────────────────────\n // Context & Indicator Management\n // ────────────────────────────────────────────────────────────────\n\n provide(TABS_CTX, {\n color: props.color,\n density: computed(() => props.density.value ?? 'default'),\n orientation: computed(() => props.orientation.value ?? 'horizontal'),\n size: props.size,\n value: selectedValue,\n variant: props.variant,\n });\n\n const moveIndicator = (activeTab: HTMLElement | undefined) => {\n const indicator = indicatorRef.value;\n const tablist = tablistRef.value;\n\n if (!indicator || !tablist || !activeTab) return;\n\n const tabRect = activeTab.getBoundingClientRect();\n const listRect = tablist.getBoundingClientRect();\n\n if (isVertical()) {\n const y = tabRect.top - listRect.top + tablist.scrollTop;\n\n indicator.style.transition = 'none';\n indicator.style.height = `${tabRect.height}px`;\n indicator.style.width = '';\n indicator.style.left = '0';\n indicator.style.top = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateY(${y}px)`;\n } else {\n const x = tabRect.left - listRect.left + tablist.scrollLeft;\n\n indicator.style.transition = 'none';\n indicator.style.width = `${tabRect.width}px`;\n indicator.style.height = '';\n indicator.style.top = '';\n indicator.style.left = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateX(${x}px)`;\n }\n };\n\n const updateIndicator = () => {\n const value = selectedValue.value;\n\n if (!value) return;\n\n const activeTab = getTabs().find((t) => t.getAttribute('value') === value);\n\n moveIndicator(activeTab);\n };\n\n watch(selectedValue, () => {\n requestAnimationFrame(updateIndicator);\n });\n\n // ────────────────────────────────────────────────────────────────\n // Event Handlers\n // ────────────────────────────────────────────────────────────────\n\n const handleTabClick = (e: Event) => {\n const tab = e\n .composedPath()\n .find((node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item');\n\n if (!tab || tab.hasAttribute('disabled')) return;\n\n // Guard: only respond to tab-items that belong to THIS tabs instance\n if (tab.closest('ore-tabs') !== el) return;\n\n const value = tab.getAttribute('value');\n\n if (!value || value === selectedValue.value) return;\n\n setSelection(value, true);\n };\n\n const activateFocusedTab = (): void => {\n const tabs = getEnabledTabs();\n const focusedTab = tabs.find(\n (tab) => tab === document.activeElement || tab.shadowRoot?.activeElement === document.activeElement,\n );\n const focusedValue = focusedTab?.getAttribute('value');\n\n if (focusedValue && focusedValue !== selectedValue.value) setSelection(focusedValue, true);\n };\n\n const manualActivationPress = createInteraction({\n disabled: computed(() => !isManualActivation()),\n onPress: activateFocusedTab,\n });\n\n const handleKeydown = (e: KeyboardEvent) => {\n const tabs = getEnabledTabs();\n\n if (tabs.length === 0) return;\n\n const path = e.composedPath();\n const activeTabFromEvent = path.find(\n (node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item',\n );\n\n const tabFromEvent = activeTabFromEvent ?? tabs.find((t) => t.getAttribute('value') === selectedValue.value);\n const focused = tabFromEvent ? tabs.indexOf(tabFromEvent) : -1;\n\n if (focused >= 0) listControl.set(focused);\n\n if (listControl.handleKeydown(e)) return;\n\n manualActivationPress.handleKeydown(e);\n };\n\n bind({\n on: {\n click: handleTabClick,\n keydown: handleKeydown,\n },\n });\n\n // ────────────────────────────────────────────────────────────────\n // Lifecycle\n // ────────────────────────────────────────────────────────────────\n\n onMounted(() => {\n const tabsSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"tabs\"]');\n const panelsSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n const sync = () => {\n ensureSelection();\n hasPanels.value = panelsSlot?.assignedElements().some((child) => child.localName === 'ore-tab-panel') ?? false;\n updateIndicator();\n };\n\n tabsSlot?.addEventListener('slotchange', sync);\n panelsSlot?.addEventListener('slotchange', sync);\n\n sync();\n requestAnimationFrame(sync);\n\n return () => {\n tabsSlot?.removeEventListener('slotchange', sync);\n panelsSlot?.removeEventListener('slotchange', sync);\n };\n });\n\n return html`\n <div class=\"tablist-wrapper\">\n <div\n role=\"tablist\"\n ref=\"${tablistRef}\"\n part=\"tablist\"\n aria-orientation=\"${props.orientation}\"\n aria-label=\"${props.label}\">\n <slot name=\"tabs\"></slot>\n </div>\n <div class=\"indicator\" ref=\"${indicatorRef}\" part=\"indicator\"></div>\n </div>\n <div class=\"panels\" part=\"panels\" ?hidden=\"${() => !hasPanels.value}\">\n <slot></slot>\n </div>\n `;\n },\n styles: [colorThemeMixin, styles],\n});\n"],"mappings":"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"}
@@ -21,10 +21,12 @@ import type { OreChatMessageEvents, OreChatMessageProps } from '../content/chat-
21
21
  import type { OreCodeWindowProps } from '../content/code-window/code-window.js';
22
22
  import type { OreCopyCommandEvents, OreCopyCommandProps } from '../content/copy-command/copy-command.js';
23
23
  import type { OreIconProps } from '../content/icon/icon.js';
24
+ import type { OreKeyboardKeyProps, OreKeyboardShortcutProps } from '../content/keyboard-key/keyboard-key.js';
24
25
  import type { OreListEvents, OreListProps } from '../content/list/list.js';
25
26
  import type { OreListItemEvents, OreListItemProps } from '../content/list/list-item.js';
26
27
  import type { OreMarqueeProps } from '../content/marquee/marquee.js';
27
28
  import type { OrePaginationEvents, OrePaginationProps } from '../content/pagination/pagination.js';
29
+ import type { OreQrCodeEvents, OreQrCodeProps } from '../content/qr-code/qr-code.js';
28
30
  import type { OreSeparatorProps } from '../content/separator/separator.js';
29
31
  import type { OreStatsProps } from '../content/stats/stats.js';
30
32
  import type { OreStepProps } from '../content/stepper/step.js';
@@ -51,6 +53,7 @@ import type { OreCalendarEvents, OreCalendarProps } from '../inputs/calendar/cal
51
53
  import type { OreCheckboxEvents, OreCheckboxProps } from '../inputs/checkbox/checkbox.js';
52
54
  import type { OreCheckboxGroupEvents, OreCheckboxGroupProps } from '../inputs/checkbox-group/checkbox-group.js';
53
55
  import type { OreComboboxEvents, OreComboboxOptionProps, OreComboboxProps } from '../inputs/combobox/combobox.types.js';
56
+ import type { OreCounterEvents, OreCounterProps } from '../inputs/counter/counter.js';
54
57
  import type { OreDataGridEvents, OreDataGridProps } from '../inputs/datagrid/datagrid.js';
55
58
  import type { OreDatePickerEvents, OreDatePickerProps } from '../inputs/date-picker/date-picker.js';
56
59
  import type { OreFileInputEvents, OreFileInputProps } from '../inputs/file-input/file-input.js';
@@ -58,6 +61,7 @@ import type { OreInputEvents, OreInputProps } from '../inputs/input/input.js';
58
61
  import type { OreMessageComposerEvents, OreMessageComposerProps } from '../inputs/message-composer/message-composer.js';
59
62
  import type { OreNumberInputEvents, OreNumberInputProps } from '../inputs/number-input/number-input.js';
60
63
  import type { OreOtpInputEvents, OreOtpInputProps } from '../inputs/otp-input/otp-input.js';
64
+ import type { OreQrScannerEvents, OreQrScannerProps } from '../inputs/qr-scanner/qr-scanner.js';
61
65
  import type { OreRadioEvents, OreRadioProps } from '../inputs/radio/radio.js';
62
66
  import type { OreRadioGroupEvents, OreRadioGroupProps } from '../inputs/radio-group/radio-group.js';
63
67
  import type { OreRatingEvents, OreRatingProps } from '../inputs/rating/rating.js';
@@ -130,6 +134,7 @@ export interface RefineElementMap {
130
134
  'ore-command-palette': HTMLElement & OreCommandPaletteProps & AddEventListeners<OreCommandPaletteEvents>;
131
135
  'ore-command-palette-item': HTMLElement & CommandPaletteItemInput;
132
136
  'ore-copy-command': HTMLElement & OreCopyCommandProps & AddEventListeners<OreCopyCommandEvents>;
137
+ 'ore-counter': HTMLElement & OreCounterProps & AddEventListeners<OreCounterEvents>;
133
138
  'ore-datagrid': HTMLElement & OreDataGridProps & AddEventListeners<OreDataGridEvents>;
134
139
  'ore-date-picker': HTMLElement & OreDatePickerProps & AddEventListeners<OreDatePickerEvents>;
135
140
  // overlay
@@ -140,6 +145,8 @@ export interface RefineElementMap {
140
145
  'ore-grid-item': HTMLElement & OreGridItemProps;
141
146
  'ore-icon': HTMLElement & OreIconProps;
142
147
  'ore-input': HTMLElement & OreInputProps & FormValidityMethods & AddEventListeners<OreInputEvents>;
148
+ 'ore-keyboard-key': HTMLElement & OreKeyboardKeyProps;
149
+ 'ore-keyboard-shortcut': HTMLElement & OreKeyboardShortcutProps;
143
150
  'ore-list': HTMLElement & OreListProps & AddEventListeners<OreListEvents>;
144
151
  'ore-list-item': HTMLElement & OreListItemProps & AddEventListeners<OreListItemEvents>;
145
152
  'ore-marquee': HTMLElement & OreMarqueeProps;
@@ -161,6 +168,8 @@ export interface RefineElementMap {
161
168
  'ore-password-strength': HTMLElement & OrePasswordStrengthProps;
162
169
  'ore-popover': HTMLElement & OrePopoverProps & AddEventListeners<OrePopoverEvents>;
163
170
  'ore-progress': HTMLElement & OreProgressProps;
171
+ 'ore-qr-code': HTMLElement & OreQrCodeProps & AddEventListeners<OreQrCodeEvents>;
172
+ 'ore-qr-scanner': HTMLElement & OreQrScannerProps & AddEventListeners<OreQrScannerEvents>;
164
173
  'ore-radio': HTMLElement & OreRadioProps & FormValidityMethods & AddEventListeners<OreRadioEvents>;
165
174
  'ore-radio-group': HTMLElement & OreRadioGroupProps & FormValidityMethods & AddEventListeners<OreRadioGroupEvents>;
166
175
  'ore-rating': HTMLElement & OreRatingProps & FormValidityMethods & AddEventListeners<OreRatingEvents>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vielzeug/refine",
3
- "version": "3.2.0",
4
- "description": "Accessible, themeable web components built on Ore custom element primitives",
3
+ "version": "3.3.0",
4
+ "description": "Accessible, themeable web components built on ore",
5
5
  "keywords": [
6
6
  "web-components",
7
7
  "custom-elements",
@@ -179,6 +179,11 @@
179
179
  "require": "./dist/copy-command.cjs",
180
180
  "types": "./dist/content/copy-command/copy-command.d.ts"
181
181
  },
182
+ "./counter": {
183
+ "import": "./dist/counter.js",
184
+ "require": "./dist/counter.cjs",
185
+ "types": "./dist/inputs/counter/counter.d.ts"
186
+ },
182
187
  "./datagrid": {
183
188
  "import": "./dist/datagrid.js",
184
189
  "require": "./dist/datagrid.cjs",
@@ -224,6 +229,11 @@
224
229
  "require": "./dist/input.cjs",
225
230
  "types": "./dist/inputs/input/input.d.ts"
226
231
  },
232
+ "./keyboard-key": {
233
+ "import": "./dist/keyboard-key.js",
234
+ "require": "./dist/keyboard-key.cjs",
235
+ "types": "./dist/content/keyboard-key/keyboard-key.d.ts"
236
+ },
227
237
  "./list": {
228
238
  "import": "./dist/list.js",
229
239
  "require": "./dist/list.cjs",
@@ -289,6 +299,16 @@
289
299
  "require": "./dist/progress.cjs",
290
300
  "types": "./dist/feedback/progress/progress.d.ts"
291
301
  },
302
+ "./qr-code": {
303
+ "import": "./dist/qr-code.js",
304
+ "require": "./dist/qr-code.cjs",
305
+ "types": "./dist/content/qr-code/qr-code.d.ts"
306
+ },
307
+ "./qr-scanner": {
308
+ "import": "./dist/qr-scanner.js",
309
+ "require": "./dist/qr-scanner.cjs",
310
+ "types": "./dist/inputs/qr-scanner/qr-scanner.d.ts"
311
+ },
292
312
  "./radio": {
293
313
  "import": "./dist/radio.js",
294
314
  "require": "./dist/radio.cjs",
@@ -477,6 +497,9 @@
477
497
  "copy-command": [
478
498
  "dist/content/copy-command/copy-command.d.ts"
479
499
  ],
500
+ "counter": [
501
+ "dist/inputs/counter/counter.d.ts"
502
+ ],
480
503
  "datagrid": [
481
504
  "dist/inputs/datagrid/datagrid.d.ts"
482
505
  ],
@@ -504,6 +527,9 @@
504
527
  "input": [
505
528
  "dist/inputs/input/input.d.ts"
506
529
  ],
530
+ "keyboard-key": [
531
+ "dist/content/keyboard-key/keyboard-key.d.ts"
532
+ ],
507
533
  "list": [
508
534
  "dist/content/list/list.d.ts"
509
535
  ],
@@ -543,6 +569,12 @@
543
569
  "progress": [
544
570
  "dist/feedback/progress/progress.d.ts"
545
571
  ],
572
+ "qr-code": [
573
+ "dist/content/qr-code/qr-code.d.ts"
574
+ ],
575
+ "qr-scanner": [
576
+ "dist/inputs/qr-scanner/qr-scanner.d.ts"
577
+ ],
546
578
  "radio": [
547
579
  "dist/inputs/radio/radio.d.ts"
548
580
  ],
@@ -645,6 +677,7 @@
645
677
  "@vielzeug/ore": "3.0.0",
646
678
  "@vielzeug/ripple": "3.0.0",
647
679
  "@vielzeug/sentinel": "3.0.0",
680
+ "@vielzeug/sigil": "3.1.0",
648
681
  "@vielzeug/tempo": "3.0.0",
649
682
  "lucide": "^1.43.0"
650
683
  },