@vielzeug/refine 26.10.1 → 26.10.6

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 (56) hide show
  1. package/dist/command-palette.cjs +9 -1
  2. package/dist/command-palette.cjs.map +1 -1
  3. package/dist/command-palette.js +9 -1
  4. package/dist/command-palette.js.map +1 -1
  5. package/dist/core/aria-reflection.cjs +1 -1
  6. package/dist/core/aria-reflection.cjs.map +1 -1
  7. package/dist/core/aria-reflection.d.ts +8 -8
  8. package/dist/core/aria-reflection.d.ts.map +1 -1
  9. package/dist/core/aria-reflection.js +1 -1
  10. package/dist/core/aria-reflection.js.map +1 -1
  11. package/dist/custom-elements.json +4881 -4569
  12. package/dist/frameworks/elements.d.ts +1 -1
  13. package/dist/inputs/index.d.ts +2 -0
  14. package/dist/inputs/index.d.ts.map +1 -1
  15. package/dist/inputs/query-bar/query-bar-grammar.cjs +2 -0
  16. package/dist/inputs/query-bar/query-bar-grammar.cjs.map +1 -0
  17. package/dist/inputs/query-bar/query-bar-grammar.d.ts +85 -0
  18. package/dist/inputs/query-bar/query-bar-grammar.d.ts.map +1 -0
  19. package/dist/inputs/query-bar/query-bar-grammar.js +2 -0
  20. package/dist/inputs/query-bar/query-bar-grammar.js.map +1 -0
  21. package/dist/inputs/query-bar/query-bar.css.cjs +2 -0
  22. package/dist/inputs/query-bar/query-bar.css.cjs.map +1 -0
  23. package/dist/inputs/query-bar/query-bar.css.js +2 -0
  24. package/dist/inputs/query-bar/query-bar.css.js.map +1 -0
  25. package/dist/inputs/query-bar/query-bar.d.ts +68 -0
  26. package/dist/inputs/query-bar/query-bar.d.ts.map +1 -0
  27. package/dist/inputs/query-bar/query-bar.types.d.ts +42 -0
  28. package/dist/inputs/query-bar/query-bar.types.d.ts.map +1 -0
  29. package/dist/navbar.cjs +2 -2
  30. package/dist/navbar.cjs.map +1 -1
  31. package/dist/navbar.js +2 -2
  32. package/dist/navbar.js.map +1 -1
  33. package/dist/overlay/command-palette/command-palette.css.cjs +1 -1
  34. package/dist/overlay/command-palette/command-palette.css.cjs.map +1 -1
  35. package/dist/overlay/command-palette/command-palette.css.js +1 -1
  36. package/dist/overlay/command-palette/command-palette.css.js.map +1 -1
  37. package/dist/overlay/command-palette/command-palette.d.ts +4 -0
  38. package/dist/overlay/command-palette/command-palette.d.ts.map +1 -1
  39. package/dist/overlay/command-palette/command-palette.types.d.ts +10 -0
  40. package/dist/overlay/command-palette/command-palette.types.d.ts.map +1 -1
  41. package/dist/query-bar.cjs +77 -0
  42. package/dist/query-bar.cjs.map +1 -0
  43. package/dist/query-bar.js +77 -0
  44. package/dist/query-bar.js.map +1 -0
  45. package/dist/refine.cjs +112 -29
  46. package/dist/refine.cjs.map +1 -1
  47. package/dist/refine.iife.js +112 -29
  48. package/dist/refine.iife.js.map +1 -1
  49. package/dist/refine.js +112 -29
  50. package/dist/refine.js.map +1 -1
  51. package/dist/tabs.cjs +1 -1
  52. package/dist/tabs.cjs.map +1 -1
  53. package/dist/tabs.js +1 -1
  54. package/dist/tabs.js.map +1 -1
  55. package/dist/types/elements.d.ts +2 -0
  56. package/package.json +25 -18
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/ripple"),c=require("@vielzeug/ore");var l=(0,c.createContext)(`TabsContext`),u=`ore-tabs`;(0,c.define)(u,{props:{...a.themableBundle,...a.sizableBundle,activation:c.prop.oneOf([`auto`,`manual`],`auto`),density:c.prop.oneOf([`compact`,`default`],`default`),label:c.prop.string(),orientation:c.prop.oneOf([`horizontal`,`vertical`],`horizontal`),value:c.prop.string(),variant:c.prop.string()},setup(e){let a=(0,c.getHost)(),o=(0,c.useEmit)(),u=a.shadowRoot,d=(0,c.ref)(),f=(0,c.ref)(),p=(0,s.signal)(e.value.value),m=(0,s.signal)(!1),isManualActivation=()=>e.activation.value===`manual`,isVertical=()=>e.orientation.value===`vertical`;(0,c.bind)({attr:{"data-has-panels":()=>m.value?``:null,value:()=>p.value??null}});let getTabs=()=>[...a.querySelectorAll(`:scope > ore-tab-item[slot="tabs"]`)],getEnabledTabs=()=>getTabs().filter(e=>!e.hasAttribute(`disabled`)),focusTab=e=>{e&&(e.shadowRoot?.querySelector(`[role="tab"]`)??e).focus()},setSelection=(e,t=!1)=>{p.value=e,t&&e&&o(`change`,{value:e})},ensureSelection=()=>{let e=getTabs();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;setSelection(n,!1)};(0,s.watch)(e.value,e=>{p.value=e,ensureSelection()});let h=i.createListControl({direction:()=>r.elementDirection(a),getItems:()=>getEnabledTabs(),loop:!0,onNavigate:({item:e})=>{if(focusTab(e),!isManualActivation()){let t=e.getAttribute(`value`);t&&setSelection(t,!0)}},orientation:()=>isVertical()?`vertical`:`both`,signal:n.lifecycleSignal(c.onCleanup)});(0,c.provide)(l,{color:e.color,density:(0,s.computed)(()=>e.density.value??`default`),orientation:(0,s.computed)(()=>e.orientation.value??`horizontal`),size:e.size,value:p,variant:e.variant});let moveIndicator=e=>{let t=f.value,n=d.value;if(!t||!n||!e)return;let r=e.getBoundingClientRect(),i=n.getBoundingClientRect();if(isVertical()){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)`}},updateIndicator=()=>{let e=p.value;if(!e)return;let t=getTabs().find(t=>t.getAttribute(`value`)===e);moveIndicator(t)};(0,s.watch)(p,()=>{requestAnimationFrame(updateIndicator)});let handleTabClick=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&&setSelection(n,!0)},activateFocusedTab=()=>{let e=getEnabledTabs().find(e=>e===document.activeElement||e.shadowRoot?.activeElement===document.activeElement)?.getAttribute(`value`);e&&e!==p.value&&setSelection(e,!0)},g=t.createInteraction({disabled:(0,s.computed)(()=>!isManualActivation()),onPress:activateFocusedTab}),handleKeydown=e=>{let t=getEnabledTabs();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&&h.set(r),!h.handleKeydown(e)&&g.handleKeydown(e)};return(0,c.bind)({on:{click:handleTabClick,keydown:handleKeydown}}),(0,c.onMounted)(()=>{let e=u?.querySelector(`slot[name="tabs"]`),t=u?.querySelector(`slot:not([name])`),sync=()=>{ensureSelection(),m.value=t?.assignedElements().some(e=>e.localName===`ore-tab-panel`)??!1,updateIndicator()};return e?.addEventListener(`slotchange`,sync),t?.addEventListener(`slotchange`,sync),sync(),requestAnimationFrame(sync),()=>{e?.removeEventListener(`slotchange`,sync),t?.removeEventListener(`slotchange`,sync)}}),c.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/ripple"),c=require("@vielzeug/ore");var l=(0,c.createContext)(`TabsContext`),u=`ore-tabs`;(0,c.define)(u,{props:{...a.themableBundle,...a.sizableBundle,activation:c.prop.oneOf([`auto`,`manual`],`auto`),density:c.prop.oneOf([`compact`,`default`],`default`),label:c.prop.string(),orientation:c.prop.oneOf([`horizontal`,`vertical`],`horizontal`),value:c.prop.string(),variant:c.prop.string()},setup(e){let a=(0,c.getHost)(),o=(0,c.useEmit)(),u=a.shadowRoot,d=(0,c.ref)(),f=(0,c.ref)(),p=(0,s.signal)(e.value.value),m=(0,s.signal)(!1),isManualActivation=()=>e.activation.value===`manual`,isVertical=()=>e.orientation.value===`vertical`;(0,c.bind)({attr:{"data-has-panels":()=>m.value?``:null,value:()=>p.value??null}});let getTabs=()=>[...a.querySelectorAll(`:scope > ore-tab-item[slot="tabs"]`)],getEnabledTabs=()=>getTabs().filter(e=>!e.hasAttribute(`disabled`)),focusTab=e=>{e&&(e.shadowRoot?.querySelector(`[role="tab"]`)??e).focus()},setSelection=(e,t=!1)=>{p.value=e,t&&e&&o(`change`,{value:e})},ensureSelection=()=>{let e=getTabs();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`)&&e.getAttribute(`value`));n&&setSelection(n.getAttribute(`value`)??void 0,!1)};(0,s.watch)(e.value,e=>{p.value=e,ensureSelection()});let h=i.createListControl({direction:()=>r.elementDirection(a),getItems:()=>getEnabledTabs(),loop:!0,onNavigate:({item:e})=>{if(focusTab(e),!isManualActivation()){let t=e.getAttribute(`value`);t&&setSelection(t,!0)}},orientation:()=>isVertical()?`vertical`:`both`,signal:n.lifecycleSignal(c.onCleanup)});(0,c.provide)(l,{color:e.color,density:(0,s.computed)(()=>e.density.value??`default`),orientation:(0,s.computed)(()=>e.orientation.value??`horizontal`),size:e.size,value:p,variant:e.variant});let moveIndicator=e=>{let t=f.value,n=d.value;if(!t||!n||!e)return;let r=e.getBoundingClientRect(),i=n.getBoundingClientRect();if(isVertical()){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)`}},updateIndicator=()=>{let e=p.value;if(!e)return;let t=getTabs().find(t=>t.getAttribute(`value`)===e);moveIndicator(t)};(0,s.watch)(p,()=>{requestAnimationFrame(updateIndicator)});let handleTabClick=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&&setSelection(n,!0)},activateFocusedTab=()=>{let e=getEnabledTabs().find(e=>e===document.activeElement||e.shadowRoot?.activeElement===document.activeElement)?.getAttribute(`value`);e&&e!==p.value&&setSelection(e,!0)},g=t.createInteraction({disabled:(0,s.computed)(()=>!isManualActivation()),onPress:activateFocusedTab}),handleKeydown=e=>{let t=getEnabledTabs();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&&h.set(r),!h.handleKeydown(e)&&g.handleKeydown(e)};return(0,c.bind)({on:{click:handleTabClick,keydown:handleKeydown}}),(0,c.onMounted)(()=>{let e=u?.querySelector(`slot[name="tabs"]`),t=u?.querySelector(`slot:not([name])`),sync=()=>{ensureSelection(),m.value=t?.assignedElements().some(e=>e.localName===`ore-tab-panel`)??!1,updateIndicator()};return e?.addEventListener(`slotchange`,sync),t?.addEventListener(`slotchange`,sync),sync(),requestAnimationFrame(sync),()=>{e?.removeEventListener(`slotchange`,sync),t?.removeEventListener(`slotchange`,sync)}}),c.html`
2
2
  <div class="tablist-wrapper">
3
3
  <div
4
4
  role="tablist"
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 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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"}
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 // Fall back only to a tab that carries a value: slot bindings land after\n // mount, and locking an empty first value here would later override a\n // bound host `value`. No valued tab yet means defer until a later sync.\n const firstEnabled = tabs.find((tab) => !tab.hasAttribute('disabled') && tab.getAttribute('value'));\n\n if (!firstEnabled) return;\n\n setSelection(firstEnabled.getAttribute('value') ?? undefined, 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":"+XA8BA,IAAa,GAAA,EAAW,EAAA,cAAA,CAA2B,aAAa,EAkEnD,EAAW,YACxB,EAAA,EAAA,OAAA,CAAqB,EAAU,CAC7B,MAAO,CACL,GAAG,EAAA,eACH,GAAG,EAAA,cACH,WAAY,EAAA,KAAK,MAAM,CAAC,OAAQ,QAAQ,EAAY,MAAM,EAC1D,QAAS,EAAA,KAAK,MAAM,CAAC,UAAW,SAAS,EAAY,SAAS,EAC9D,MAAO,EAAA,KAAK,OAAO,EACnB,YAAa,EAAA,KAAK,MAAM,CAAC,aAAc,UAAU,EAAY,YAAY,EACzE,MAAO,EAAA,KAAK,OAAO,EACnB,QAAS,EAAA,KAAK,OAAsB,CACtC,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAK,EAAA,QAAA,CAAQ,EACb,GAAA,EAAO,EAAA,QAAA,CAAuB,EAE9B,EAAa,EAAG,WAChB,GAAA,EAAa,EAAA,IAAA,CAAiB,EAC9B,GAAA,EAAe,EAAA,IAAA,CAAiB,EAChC,GAAA,EAAgB,EAAA,OAAA,CAA2B,EAAM,MAAM,KAAK,EAC5D,GAAA,EAAY,EAAA,OAAA,CAAO,EAAK,EACxB,uBAA2B,EAAM,WAAW,QAAU,SACtD,eAAmB,EAAM,YAAY,QAAU,YAErD,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,sBAA0B,EAAU,MAAQ,GAAK,KACjD,UAAa,EAAc,OAAS,IACtC,CACF,CAAC,EAED,IAAM,YAAgB,CAAC,GAAG,EAAG,iBAA8B,oCAAoC,CAAC,EAC1F,mBAAuB,QAAQ,CAAC,CAAC,OAAQ,GAAM,CAAC,EAAE,aAAa,UAAU,CAAC,EAC1E,SAAY,GAAiC,CAC5C,IAEa,EAAI,YAAY,cAA2B,cAAc,GAAK,EAAA,CAEtE,MAAM,CAClB,EAMM,cAAgB,EAA2B,EAAa,KAAU,CACtE,EAAc,MAAQ,EAElB,GAAc,GAAO,EAAK,SAAU,CAAE,OAAM,CAAC,CACnD,EAEM,oBAAwB,CAC5B,IAAM,EAAO,QAAQ,EAErB,GAAI,EAAK,SAAW,EAAG,OAEvB,IAAM,EAAU,EAAc,MAK9B,GAJmB,GACf,EAAK,KAAM,GAAQ,EAAI,aAAa,OAAO,IAAM,GAAW,CAAC,EAAI,aAAa,UAAU,CAAC,EAG7E,OAKhB,IAAM,EAAe,EAAK,KAAM,GAAQ,CAAC,EAAI,aAAa,UAAU,GAAK,EAAI,aAAa,OAAO,CAAC,EAE7F,GAEL,aAAa,EAAa,aAAa,OAAO,GAAK,IAAA,GAAW,EAAK,CACrE,GAEA,EAAA,EAAA,MAAA,CAAM,EAAM,MAAQ,GAAU,CAC5B,EAAc,MAAQ,EACtB,gBAAgB,CAClB,CAAC,EAMD,IAAM,EAAc,EAAA,kBAAkB,CACpC,cAAiB,EAAA,iBAAiB,CAAE,EACpC,aAAgB,eAAe,EAC/B,KAAM,GACN,YAAa,CAAE,UAAW,CAGxB,GAFA,SAAS,CAAI,EAET,CAAC,mBAAmB,EAAG,CACzB,IAAM,EAAQ,EAAK,aAAa,OAAO,EAEnC,GAAO,aAAa,EAAO,EAAI,CACrC,CACF,EACA,gBAAoB,WAAW,EAAI,WAAa,OAChD,OAAQ,EAAA,gBAAgB,EAAA,SAAS,CACnC,CAAC,GAMD,EAAA,EAAA,QAAA,CAAQ,EAAU,CAChB,MAAO,EAAM,MACb,SAAA,EAAS,EAAA,SAAA,KAAe,EAAM,QAAQ,OAAS,SAAS,EACxD,aAAA,EAAa,EAAA,SAAA,KAAe,EAAM,YAAY,OAAS,YAAY,EACnE,KAAM,EAAM,KACZ,MAAO,EACP,QAAS,EAAM,OACjB,CAAC,EAED,IAAM,cAAiB,GAAuC,CAC5D,IAAM,EAAY,EAAa,MACzB,EAAU,EAAW,MAE3B,GAAI,CAAC,GAAa,CAAC,GAAW,CAAC,EAAW,OAE1C,IAAM,EAAU,EAAU,sBAAsB,EAC1C,EAAW,EAAQ,sBAAsB,EAE/C,GAAI,WAAW,EAAG,CAChB,IAAM,EAAI,EAAQ,IAAM,EAAS,IAAM,EAAQ,UAE/C,EAAU,MAAM,WAAa,OAC7B,EAAU,MAAM,OAAS,GAAG,EAAQ,OAAO,IAC3C,EAAU,MAAM,MAAQ,GACxB,EAAU,MAAM,KAAO,IACvB,EAAU,MAAM,IAAM,IACtB,EAAe,YACf,EAAU,MAAM,WAAa,GAC7B,EAAU,MAAM,UAAY,cAAc,EAAE,IAC9C,KAAO,CACL,IAAM,EAAI,EAAQ,KAAO,EAAS,KAAO,EAAQ,WAEjD,EAAU,MAAM,WAAa,OAC7B,EAAU,MAAM,MAAQ,GAAG,EAAQ,MAAM,IACzC,EAAU,MAAM,OAAS,GACzB,EAAU,MAAM,IAAM,GACtB,EAAU,MAAM,KAAO,IACvB,EAAe,YACf,EAAU,MAAM,WAAa,GAC7B,EAAU,MAAM,UAAY,cAAc,EAAE,IAC9C,CACF,EAEM,oBAAwB,CAC5B,IAAM,EAAQ,EAAc,MAE5B,GAAI,CAAC,EAAO,OAEZ,IAAM,EAAY,QAAQ,CAAC,CAAC,KAAM,GAAM,EAAE,aAAa,OAAO,IAAM,CAAK,EAEzE,cAAc,CAAS,CACzB,GAEA,EAAA,EAAA,MAAA,CAAM,MAAqB,CACzB,sBAAsB,eAAe,CACvC,CAAC,EAMD,IAAM,eAAkB,GAAa,CACnC,IAAM,EAAM,EACT,aAAa,CAAC,CACd,KAAM,GAA8B,aAAgB,aAAe,EAAK,YAAc,cAAc,EAKvG,GAHI,CAAC,GAAO,EAAI,aAAa,UAAU,GAGnC,EAAI,QAAQ,UAAU,IAAM,EAAI,OAEpC,IAAM,EAAQ,EAAI,aAAa,OAAO,EAEjC,GAAS,IAAU,EAAc,OAEtC,aAAa,EAAO,EAAI,CAC1B,EAEM,uBAAiC,CAKrC,IAAM,EAJO,eACM,CAAA,CAAK,KACrB,GAAQ,IAAQ,SAAS,eAAiB,EAAI,YAAY,gBAAkB,SAAS,aAEnE,CAAA,EAAY,aAAa,OAAO,EAEjD,GAAgB,IAAiB,EAAc,OAAO,aAAa,EAAc,EAAI,CAC3F,EAEM,EAAwB,EAAA,kBAAkB,CAC9C,UAAA,EAAU,EAAA,SAAA,KAAe,CAAC,mBAAmB,CAAC,EAC9C,QAAS,kBACX,CAAC,EAEK,cAAiB,GAAqB,CAC1C,IAAM,EAAO,eAAe,EAE5B,GAAI,EAAK,SAAW,EAAG,OAOvB,IAAM,EALO,EAAE,aACY,CAAA,CAAK,KAC7B,GAA8B,aAAgB,aAAe,EAAK,YAAc,cAG9D,GAAsB,EAAK,KAAM,GAAM,EAAE,aAAa,OAAO,IAAM,EAAc,KAAK,EACrG,EAAU,EAAe,EAAK,QAAQ,CAAY,EAAI,GAExD,GAAW,GAAG,EAAY,IAAI,CAAO,EAErC,GAAY,cAAc,CAAC,GAE/B,EAAsB,cAAc,CAAC,CACvC,EAkCA,OAhCA,EAAA,EAAA,KAAA,CAAK,CACH,GAAI,CACF,MAAO,eACP,QAAS,aACX,CACF,CAAC,GAMD,EAAA,EAAA,UAAA,KAAgB,CACd,IAAM,EAAW,GAAY,cAA+B,mBAAmB,EACzE,EAAa,GAAY,cAA+B,kBAAkB,EAC1E,SAAa,CACjB,gBAAgB,EAChB,EAAU,MAAQ,GAAY,iBAAiB,CAAC,CAAC,KAAM,GAAU,EAAM,YAAc,eAAe,GAAK,GACzG,gBAAgB,CAClB,EAQA,OANA,GAAU,iBAAiB,aAAc,IAAI,EAC7C,GAAY,iBAAiB,aAAc,IAAI,EAE/C,KAAK,EACL,sBAAsB,IAAI,MAEb,CACX,GAAU,oBAAoB,aAAc,IAAI,EAChD,GAAY,oBAAoB,aAAc,IAAI,CACpD,CACF,CAAC,EAEM,EAAA,IAAI;;;;iBAIE,EAAW;;8BAEE,EAAM,YAAY;wBACxB,EAAM,MAAM;;;sCAGE,EAAa;;uDAEM,CAAC,EAAU,MAAM;;;KAIxE,EACA,OAAQ,CAAC,EAAA,gBAAiB,EAAA,OAAM,CAClC,CAAC"}
package/dist/tabs.js CHANGED
@@ -1,4 +1,4 @@
1
- import{colorThemeMixin as e}from"./styles/mixins/theme.css.js";import{createInteraction as t}from"./core/keyboard.js";import{lifecycleSignal as n}from"./core/ore.js";import{elementDirection as r}from"./core/direction.js";import{createListControl as i}from"./core/nav.js";import{sizableBundle as a,themableBundle as o}from"./shared/prop-bundles.js";import s from"./disclosure/tabs/tabs.css.js";import{computed as c,signal as l,watch as u}from"@vielzeug/ripple";import{bind as d,createContext as f,define as p,getHost as m,html as h,onCleanup as g,onMounted as _,prop as v,provide as y,ref as b,useEmit as x}from"@vielzeug/ore";var S=f(`TabsContext`),C=`ore-tabs`;p(C,{props:{...o,...a,activation:v.oneOf([`auto`,`manual`],`auto`),density:v.oneOf([`compact`,`default`],`default`),label:v.string(),orientation:v.oneOf([`horizontal`,`vertical`],`horizontal`),value:v.string(),variant:v.string()},setup(e){let a=m(),o=x(),s=a.shadowRoot,f=b(),p=b(),v=l(e.value.value),C=l(!1),isManualActivation=()=>e.activation.value===`manual`,isVertical=()=>e.orientation.value===`vertical`;d({attr:{"data-has-panels":()=>C.value?``:null,value:()=>v.value??null}});let getTabs=()=>[...a.querySelectorAll(`:scope > ore-tab-item[slot="tabs"]`)],getEnabledTabs=()=>getTabs().filter(e=>!e.hasAttribute(`disabled`)),focusTab=e=>{e&&(e.shadowRoot?.querySelector(`[role="tab"]`)??e).focus()},setSelection=(e,t=!1)=>{v.value=e,t&&e&&o(`change`,{value:e})},ensureSelection=()=>{let e=getTabs();if(e.length===0)return;let t=v.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;setSelection(n,!1)};u(e.value,e=>{v.value=e,ensureSelection()});let w=i({direction:()=>r(a),getItems:()=>getEnabledTabs(),loop:!0,onNavigate:({item:e})=>{if(focusTab(e),!isManualActivation()){let t=e.getAttribute(`value`);t&&setSelection(t,!0)}},orientation:()=>isVertical()?`vertical`:`both`,signal:n(g)});y(S,{color:e.color,density:c(()=>e.density.value??`default`),orientation:c(()=>e.orientation.value??`horizontal`),size:e.size,value:v,variant:e.variant});let moveIndicator=e=>{let t=p.value,n=f.value;if(!t||!n||!e)return;let r=e.getBoundingClientRect(),i=n.getBoundingClientRect();if(isVertical()){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)`}},updateIndicator=()=>{let e=v.value;if(!e)return;let t=getTabs().find(t=>t.getAttribute(`value`)===e);moveIndicator(t)};u(v,()=>{requestAnimationFrame(updateIndicator)});let handleTabClick=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!==v.value&&setSelection(n,!0)},activateFocusedTab=()=>{let e=getEnabledTabs().find(e=>e===document.activeElement||e.shadowRoot?.activeElement===document.activeElement)?.getAttribute(`value`);e&&e!==v.value&&setSelection(e,!0)},T=t({disabled:c(()=>!isManualActivation()),onPress:activateFocusedTab}),handleKeydown=e=>{let t=getEnabledTabs();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`)===v.value),r=n?t.indexOf(n):-1;r>=0&&w.set(r),!w.handleKeydown(e)&&T.handleKeydown(e)};return d({on:{click:handleTabClick,keydown:handleKeydown}}),_(()=>{let e=s?.querySelector(`slot[name="tabs"]`),t=s?.querySelector(`slot:not([name])`),sync=()=>{ensureSelection(),C.value=t?.assignedElements().some(e=>e.localName===`ore-tab-panel`)??!1,updateIndicator()};return e?.addEventListener(`slotchange`,sync),t?.addEventListener(`slotchange`,sync),sync(),requestAnimationFrame(sync),()=>{e?.removeEventListener(`slotchange`,sync),t?.removeEventListener(`slotchange`,sync)}}),h`
1
+ import{colorThemeMixin as e}from"./styles/mixins/theme.css.js";import{createInteraction as t}from"./core/keyboard.js";import{lifecycleSignal as n}from"./core/ore.js";import{elementDirection as r}from"./core/direction.js";import{createListControl as i}from"./core/nav.js";import{sizableBundle as a,themableBundle as o}from"./shared/prop-bundles.js";import s from"./disclosure/tabs/tabs.css.js";import{computed as c,signal as l,watch as u}from"@vielzeug/ripple";import{bind as d,createContext as f,define as p,getHost as m,html as h,onCleanup as g,onMounted as _,prop as v,provide as y,ref as b,useEmit as x}from"@vielzeug/ore";var S=f(`TabsContext`),C=`ore-tabs`;p(C,{props:{...o,...a,activation:v.oneOf([`auto`,`manual`],`auto`),density:v.oneOf([`compact`,`default`],`default`),label:v.string(),orientation:v.oneOf([`horizontal`,`vertical`],`horizontal`),value:v.string(),variant:v.string()},setup(e){let a=m(),o=x(),s=a.shadowRoot,f=b(),p=b(),v=l(e.value.value),C=l(!1),isManualActivation=()=>e.activation.value===`manual`,isVertical=()=>e.orientation.value===`vertical`;d({attr:{"data-has-panels":()=>C.value?``:null,value:()=>v.value??null}});let getTabs=()=>[...a.querySelectorAll(`:scope > ore-tab-item[slot="tabs"]`)],getEnabledTabs=()=>getTabs().filter(e=>!e.hasAttribute(`disabled`)),focusTab=e=>{e&&(e.shadowRoot?.querySelector(`[role="tab"]`)??e).focus()},setSelection=(e,t=!1)=>{v.value=e,t&&e&&o(`change`,{value:e})},ensureSelection=()=>{let e=getTabs();if(e.length===0)return;let t=v.value;if(t&&e.some(e=>e.getAttribute(`value`)===t&&!e.hasAttribute(`disabled`)))return;let n=e.find(e=>!e.hasAttribute(`disabled`)&&e.getAttribute(`value`));n&&setSelection(n.getAttribute(`value`)??void 0,!1)};u(e.value,e=>{v.value=e,ensureSelection()});let w=i({direction:()=>r(a),getItems:()=>getEnabledTabs(),loop:!0,onNavigate:({item:e})=>{if(focusTab(e),!isManualActivation()){let t=e.getAttribute(`value`);t&&setSelection(t,!0)}},orientation:()=>isVertical()?`vertical`:`both`,signal:n(g)});y(S,{color:e.color,density:c(()=>e.density.value??`default`),orientation:c(()=>e.orientation.value??`horizontal`),size:e.size,value:v,variant:e.variant});let moveIndicator=e=>{let t=p.value,n=f.value;if(!t||!n||!e)return;let r=e.getBoundingClientRect(),i=n.getBoundingClientRect();if(isVertical()){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)`}},updateIndicator=()=>{let e=v.value;if(!e)return;let t=getTabs().find(t=>t.getAttribute(`value`)===e);moveIndicator(t)};u(v,()=>{requestAnimationFrame(updateIndicator)});let handleTabClick=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!==v.value&&setSelection(n,!0)},activateFocusedTab=()=>{let e=getEnabledTabs().find(e=>e===document.activeElement||e.shadowRoot?.activeElement===document.activeElement)?.getAttribute(`value`);e&&e!==v.value&&setSelection(e,!0)},T=t({disabled:c(()=>!isManualActivation()),onPress:activateFocusedTab}),handleKeydown=e=>{let t=getEnabledTabs();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`)===v.value),r=n?t.indexOf(n):-1;r>=0&&w.set(r),!w.handleKeydown(e)&&T.handleKeydown(e)};return d({on:{click:handleTabClick,keydown:handleKeydown}}),_(()=>{let e=s?.querySelector(`slot[name="tabs"]`),t=s?.querySelector(`slot:not([name])`),sync=()=>{ensureSelection(),C.value=t?.assignedElements().some(e=>e.localName===`ore-tab-panel`)??!1,updateIndicator()};return e?.addEventListener(`slotchange`,sync),t?.addEventListener(`slotchange`,sync),sync(),requestAnimationFrame(sync),()=>{e?.removeEventListener(`slotchange`,sync),t?.removeEventListener(`slotchange`,sync)}}),h`
2
2
  <div class="tablist-wrapper">
3
3
  <div
4
4
  role="tablist"
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 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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"}
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 // Fall back only to a tab that carries a value: slot bindings land after\n // mount, and locking an empty first value here would later override a\n // bound host `value`. No valued tab yet means defer until a later sync.\n const firstEnabled = tabs.find((tab) => !tab.hasAttribute('disabled') && tab.getAttribute('value'));\n\n if (!firstEnabled) return;\n\n setSelection(firstEnabled.getAttribute('value') ?? undefined, 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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"}
@@ -71,6 +71,7 @@ import type { OreMessageComposerEvents, OreMessageComposerProps } from '../input
71
71
  import type { OreNumberInputEvents, OreNumberInputProps } from '../inputs/number-input/number-input.js';
72
72
  import type { OreOtpInputEvents, OreOtpInputProps } from '../inputs/otp-input/otp-input.js';
73
73
  import type { OreQrScannerEvents, OreQrScannerProps } from '../inputs/qr-scanner/qr-scanner.js';
74
+ import type { OreQueryBarEvents, OreQueryBarProps } from '../inputs/query-bar/query-bar.types.js';
74
75
  import type { OreRadioEvents, OreRadioProps } from '../inputs/radio/radio.js';
75
76
  import type { OreRadioGroupEvents, OreRadioGroupProps } from '../inputs/radio-group/radio-group.js';
76
77
  import type { OreRatingEvents, OreRatingProps } from '../inputs/rating/rating.js';
@@ -181,6 +182,7 @@ export interface RefineElementMap {
181
182
  'ore-progress': HTMLElement & OreProgressProps;
182
183
  'ore-qr-code': HTMLElement & OreQrCodeProps & AddEventListeners<OreQrCodeEvents>;
183
184
  'ore-qr-scanner': HTMLElement & OreQrScannerProps & AddEventListeners<OreQrScannerEvents>;
185
+ 'ore-query-bar': HTMLElement & OreQueryBarProps & AddEventListeners<OreQueryBarEvents>;
184
186
  'ore-radio': HTMLElement & OreRadioProps & FormValidityMethods & AddEventListeners<OreRadioEvents>;
185
187
  'ore-radio-group': HTMLElement & OreRadioGroupProps & FormValidityMethods & AddEventListeners<OreRadioGroupEvents>;
186
188
  'ore-rank-item': HTMLElement & OreRankItemProps;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/refine",
3
- "version": "26.10.1",
3
+ "version": "26.10.6",
4
4
  "description": "Accessible, themeable web components built on ore",
5
5
  "keywords": [
6
6
  "web-components",
@@ -329,6 +329,11 @@
329
329
  "require": "./dist/qr-scanner.cjs",
330
330
  "types": "./dist/inputs/qr-scanner/qr-scanner.d.ts"
331
331
  },
332
+ "./query-bar": {
333
+ "import": "./dist/query-bar.js",
334
+ "require": "./dist/query-bar.cjs",
335
+ "types": "./dist/inputs/query-bar/query-bar.d.ts"
336
+ },
332
337
  "./radio": {
333
338
  "import": "./dist/radio.js",
334
339
  "require": "./dist/radio.cjs",
@@ -622,6 +627,9 @@
622
627
  "qr-scanner": [
623
628
  "dist/inputs/qr-scanner/qr-scanner.d.ts"
624
629
  ],
630
+ "query-bar": [
631
+ "dist/inputs/query-bar/query-bar.d.ts"
632
+ ],
625
633
  "radio": [
626
634
  "dist/inputs/radio/radio.d.ts"
627
635
  ],
@@ -702,21 +710,20 @@
702
710
  "sideEffects": [
703
711
  "./dist/*.js",
704
712
  "./dist/*.cjs",
705
- "./dist/core/*.js",
706
- "./dist/core/*.cjs",
713
+ "./dist/core/*",
707
714
  "./dist/styles/**",
708
- "./src/core/icons.ts",
709
- "./src/icon-lucide.ts"
715
+ "./src/core/icons.ts"
710
716
  ],
711
717
  "scripts": {
712
718
  "analyze": "custom-elements-manifest analyze --config scripts/refine-manifest.mjs",
713
- "build": "pnpm --silent run check:theme && pnpm --silent run check:manifest && pnpm --silent run check:css-contract && vite build && pnpm --silent run check:cross-entry && pnpm --silent run build:bundle && cp -r src/styles/*.css dist/styles && pnpm --silent run build:types && pnpm --silent run analyze && pnpm --silent run gen:framework-types",
719
+ "build": "pnpm --silent run check:theme && pnpm --silent run check:manifest && pnpm --silent run check:css-contract && vite build && pnpm --silent run check:cross-entry && pnpm --silent run build:bundle && cp -r src/styles/*.css dist/styles && pnpm --silent run build:types && pnpm --silent run analyze && pnpm --silent run gen:framework-types && pnpm --silent run check:packed-icons",
714
720
  "build:bundle": "vite build --config vite.bundle.config.ts",
715
721
  "build:bundle:watch": "vite build --watch --config vite.bundle.config.ts",
716
722
  "build:types": "tsc -p tsconfig.declarations.json",
717
723
  "check:css-contract": "node ./scripts/check-css-contract.mjs",
718
724
  "check:cross-entry": "node ./scripts/refine-manifest.mjs check-cross-entry",
719
725
  "check:manifest": "node ./scripts/refine-manifest.mjs check-exports",
726
+ "check:packed-icons": "node ./scripts/check-packed-icons.mjs",
720
727
  "check:theme": "node ./scripts/theme-tokens.mjs check",
721
728
  "dev": "vite",
722
729
  "fix": "biome check --write src",
@@ -729,17 +736,17 @@
729
736
  "test:e2e": "playwright test"
730
737
  },
731
738
  "dependencies": {
732
- "@vielzeug/arsenal": "26.10.1",
733
- "@vielzeug/dnd": "26.10.1",
734
- "@vielzeug/focus": "26.10.1",
735
- "@vielzeug/gesture": "26.10.1",
736
- "@vielzeug/keymap": "26.10.1",
737
- "@vielzeug/orbit": "26.10.1",
738
- "@vielzeug/ore": "26.10.1",
739
- "@vielzeug/ripple": "26.10.1",
740
- "@vielzeug/sentinel": "26.10.1",
741
- "@vielzeug/sigil": "26.10.1",
742
- "@vielzeug/tempo": "26.10.1",
739
+ "@vielzeug/arsenal": "26.10.6",
740
+ "@vielzeug/dnd": "26.10.6",
741
+ "@vielzeug/focus": "26.10.6",
742
+ "@vielzeug/gesture": "26.10.6",
743
+ "@vielzeug/keymap": "26.10.6",
744
+ "@vielzeug/orbit": "26.10.6",
745
+ "@vielzeug/ore": "26.10.6",
746
+ "@vielzeug/ripple": "26.10.6",
747
+ "@vielzeug/sentinel": "26.10.6",
748
+ "@vielzeug/sigil": "26.10.6",
749
+ "@vielzeug/tempo": "26.10.6",
743
750
  "lucide": "^1.48.0"
744
751
  },
745
752
  "devDependencies": {
@@ -749,7 +756,7 @@
749
756
  "@playwright/test": "^1.63.0",
750
757
  "@types/culori": "^4.0.1",
751
758
  "@types/node": "^26.6.2",
752
- "@vielzeug/assay": "26.10.1",
759
+ "@vielzeug/assay": "26.10.6",
753
760
  "axe-core": "^4.13.0",
754
761
  "browserslist": "^4.29.0",
755
762
  "culori": "^4.0.2",