@vielzeug/refine 2.1.3 → 2.2.4
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.
- package/dist/accordion.cjs +1 -1
- package/dist/accordion.cjs.map +1 -1
- package/dist/accordion.js +2 -2
- package/dist/accordion.js.map +1 -1
- package/dist/carousel.cjs +25 -24
- package/dist/carousel.cjs.map +1 -1
- package/dist/carousel.js +125 -135
- package/dist/carousel.js.map +1 -1
- package/dist/combobox.cjs +14 -14
- package/dist/combobox.cjs.map +1 -1
- package/dist/combobox.js +88 -98
- package/dist/combobox.js.map +1 -1
- package/dist/content/carousel/carousel.d.ts.map +1 -1
- package/dist/content/list/_list-item-reveal.cjs +1 -1
- package/dist/content/list/_list-item-reveal.cjs.map +1 -1
- package/dist/content/list/_list-item-reveal.d.ts +3 -3
- package/dist/content/list/_list-item-reveal.d.ts.map +1 -1
- package/dist/content/list/_list-item-reveal.js +22 -22
- package/dist/content/list/_list-item-reveal.js.map +1 -1
- package/dist/content/list/list-item.d.ts.map +1 -1
- package/dist/core/focus.cjs +1 -1
- package/dist/core/focus.cjs.map +1 -1
- package/dist/core/focus.d.ts.map +1 -1
- package/dist/core/focus.js +21 -20
- package/dist/core/focus.js.map +1 -1
- package/dist/core/index.d.ts +2 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/nav.cjs +1 -1
- package/dist/core/nav.cjs.map +1 -1
- package/dist/core/nav.d.ts +3 -62
- package/dist/core/nav.d.ts.map +1 -1
- package/dist/core/nav.js +41 -124
- package/dist/core/nav.js.map +1 -1
- package/dist/core/option-list.cjs +1 -1
- package/dist/core/option-list.cjs.map +1 -1
- package/dist/core/option-list.d.ts +5 -5
- package/dist/core/option-list.d.ts.map +1 -1
- package/dist/core/option-list.js +14 -13
- package/dist/core/option-list.js.map +1 -1
- package/dist/core/overlay.cjs +1 -1
- package/dist/core/overlay.cjs.map +1 -1
- package/dist/core/overlay.d.ts +0 -8
- package/dist/core/overlay.d.ts.map +1 -1
- package/dist/core/overlay.js +1 -3
- package/dist/core/overlay.js.map +1 -1
- package/dist/custom-elements.json +5636 -5683
- package/dist/drawer.cjs +6 -6
- package/dist/drawer.cjs.map +1 -1
- package/dist/drawer.js +65 -75
- package/dist/drawer.js.map +1 -1
- package/dist/feedback/skeleton/skeleton.css.cjs +1 -1
- package/dist/feedback/skeleton/skeleton.css.cjs.map +1 -1
- package/dist/feedback/skeleton/skeleton.css.js +1 -1
- package/dist/feedback/skeleton/skeleton.css.js.map +1 -1
- package/dist/feedback/skeleton/skeleton.d.ts.map +1 -1
- package/dist/feedback/toast/toast.d.ts.map +1 -1
- package/dist/grid.cjs +2 -2
- package/dist/grid.cjs.map +1 -1
- package/dist/grid.js +42 -41
- package/dist/grid.js.map +1 -1
- package/dist/input.cjs.map +1 -1
- package/dist/input.js.map +1 -1
- package/dist/inputs/input/input.css.cjs +1 -1
- package/dist/inputs/input/input.css.cjs.map +1 -1
- package/dist/inputs/input/input.css.js +1 -1
- package/dist/inputs/input/input.css.js.map +1 -1
- package/dist/inputs/input/input.d.ts +2 -0
- package/dist/inputs/input/input.d.ts.map +1 -1
- package/dist/inputs/select/select.d.ts +2 -2
- package/dist/inputs/select/select.d.ts.map +1 -1
- package/dist/layout/grid/grid.d.ts.map +1 -1
- package/dist/layout/navbar/navbar.d.ts.map +1 -1
- package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
- package/dist/list-item.cjs +2 -6
- package/dist/list-item.cjs.map +1 -1
- package/dist/list-item.js +40 -39
- package/dist/list-item.js.map +1 -1
- package/dist/list.cjs +1 -1
- package/dist/list.cjs.map +1 -1
- package/dist/list.js +2 -2
- package/dist/list.js.map +1 -1
- package/dist/marquee.cjs +1 -1
- package/dist/marquee.cjs.map +1 -1
- package/dist/marquee.js +3 -1
- package/dist/marquee.js.map +1 -1
- package/dist/menu.cjs +5 -5
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.js +59 -59
- package/dist/menu.js.map +1 -1
- package/dist/navbar.cjs +9 -9
- package/dist/navbar.cjs.map +1 -1
- package/dist/navbar.js +124 -116
- package/dist/navbar.js.map +1 -1
- package/dist/otp-input.cjs +1 -1
- package/dist/otp-input.cjs.map +1 -1
- package/dist/otp-input.js +2 -2
- package/dist/otp-input.js.map +1 -1
- package/dist/overlay/drawer/drawer.d.ts.map +1 -1
- package/dist/overlay/menu/menu.d.ts.map +1 -1
- package/dist/radio-group.cjs +1 -1
- package/dist/radio-group.cjs.map +1 -1
- package/dist/radio-group.js +2 -3
- package/dist/radio-group.js.map +1 -1
- package/dist/radio.cjs +1 -1
- package/dist/radio.cjs.map +1 -1
- package/dist/radio.js +12 -11
- package/dist/radio.js.map +1 -1
- package/dist/rating.cjs +1 -1
- package/dist/rating.cjs.map +1 -1
- package/dist/rating.js +1 -1
- package/dist/rating.js.map +1 -1
- package/dist/refine.cjs +188 -195
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +212 -219
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +392 -399
- package/dist/refine.js.map +1 -1
- package/dist/select.cjs +1 -1
- package/dist/select.cjs.map +1 -1
- package/dist/select.js +1 -0
- package/dist/select.js.map +1 -1
- package/dist/sidebar.cjs +16 -16
- package/dist/sidebar.cjs.map +1 -1
- package/dist/sidebar.js +106 -91
- package/dist/sidebar.js.map +1 -1
- package/dist/skeleton.cjs +3 -3
- package/dist/skeleton.cjs.map +1 -1
- package/dist/skeleton.js +8 -7
- package/dist/skeleton.js.map +1 -1
- package/dist/stepper.cjs +1 -1
- package/dist/stepper.cjs.map +1 -1
- package/dist/stepper.js +3 -3
- package/dist/stepper.js.map +1 -1
- package/dist/styles/fouc.css +22 -0
- package/dist/styles/preflight.css +2 -10
- package/dist/tabs.cjs +1 -1
- package/dist/tabs.cjs.map +1 -1
- package/dist/tabs.js +4 -5
- package/dist/tabs.js.map +1 -1
- package/dist/toast.cjs +13 -17
- package/dist/toast.cjs.map +1 -1
- package/dist/toast.js +86 -85
- package/dist/toast.js.map +1 -1
- package/package.json +18 -11
- package/dist/core/swipe.cjs +0 -2
- package/dist/core/swipe.cjs.map +0 -1
- package/dist/core/swipe.d.ts +0 -40
- package/dist/core/swipe.d.ts.map +0 -1
- package/dist/core/swipe.js +0 -63
- package/dist/core/swipe.js.map +0 -1
- package/dist/core/typeahead.cjs +0 -2
- package/dist/core/typeahead.cjs.map +0 -1
- package/dist/core/typeahead.d.ts +0 -53
- package/dist/core/typeahead.d.ts.map +0 -1
- package/dist/core/typeahead.js +0 -23
- package/dist/core/typeahead.js.map +0 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
@layer refine.reset {
|
|
2
|
+
/*
|
|
3
|
+
* Suppress flash of unstyled content (FOUC) for custom elements.
|
|
4
|
+
*
|
|
5
|
+
* :not(:defined) only matches custom elements that have not yet been
|
|
6
|
+
* upgraded — standard HTML elements are always :defined, so this rule is
|
|
7
|
+
* safe globally. Once customElements.define() runs and the element
|
|
8
|
+
* upgrades, :defined matches and this rule no longer applies.
|
|
9
|
+
*
|
|
10
|
+
* Import this file in your application's CSS bundle (not via JS injection)
|
|
11
|
+
* so the rule is available at first paint:
|
|
12
|
+
*
|
|
13
|
+
* import '@vielzeug/refine/fouc.css';
|
|
14
|
+
*
|
|
15
|
+
* `visibility: hidden` is used instead of `display: none` to preserve
|
|
16
|
+
* layout space — the element occupies its inline size while invisible,
|
|
17
|
+
* reducing layout shift when the shadow DOM attaches.
|
|
18
|
+
*/
|
|
19
|
+
:not(:defined) {
|
|
20
|
+
visibility: hidden;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
@
|
|
2
|
-
/*
|
|
3
|
-
* Suppress flash of unstyled content (FOUC) for custom elements.
|
|
4
|
-
* :not(:defined) only matches custom elements that have not yet been upgraded —
|
|
5
|
-
* standard HTML elements are always :defined, so this rule is safe globally.
|
|
6
|
-
* The rule is removed automatically once customElements.define() runs.
|
|
7
|
-
*/
|
|
8
|
-
:not(:defined) {
|
|
9
|
-
visibility: hidden;
|
|
10
|
-
}
|
|
1
|
+
@import url('./fouc.css');
|
|
11
2
|
|
|
3
|
+
@layer refine.reset {
|
|
12
4
|
*,
|
|
13
5
|
::after,
|
|
14
6
|
::before,
|
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`),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:(e
|
|
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`),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,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`
|
|
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, SurfaceVariant, ThemeColor } 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 orientation: Readable<'horizontal' | 'vertical'>;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n variant: Readable<SurfaceVariant | 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 /** 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?: SurfaceVariant;\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' | 'glass' | 'frost'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\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/glass/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 label: prop.string(),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n variant: prop.string<SurfaceVariant>(),\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: (_action, index) => {\n const tabs = getEnabledTabs();\n const nextTab = tabs[index];\n\n focusTab(nextTab);\n\n if (!isManualActivation()) {\n const value = nextTab?.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 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, SurfaceVariant, ThemeColor } 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 orientation: Readable<'horizontal' | 'vertical'>;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n variant: Readable<SurfaceVariant | 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 /** 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?: SurfaceVariant;\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' | 'glass' | 'frost'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\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/glass/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 label: prop.string(),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n variant: prop.string<SurfaceVariant>(),\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 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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"}
|
package/dist/tabs.js
CHANGED
|
@@ -41,11 +41,10 @@ u(C, {
|
|
|
41
41
|
direction: () => r(a),
|
|
42
42
|
getItems: () => E(),
|
|
43
43
|
loop: !0,
|
|
44
|
-
onNavigate: (e
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
e && O(e, !0);
|
|
44
|
+
onNavigate: ({ item: e }) => {
|
|
45
|
+
if (D(e), !C()) {
|
|
46
|
+
let t = e.getAttribute("value");
|
|
47
|
+
t && O(t, !0);
|
|
49
48
|
}
|
|
50
49
|
},
|
|
51
50
|
orientation: () => w() ? "vertical" : "both",
|
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, SurfaceVariant, ThemeColor } 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 orientation: Readable<'horizontal' | 'vertical'>;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n variant: Readable<SurfaceVariant | 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 /** 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?: SurfaceVariant;\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' | 'glass' | 'frost'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\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/glass/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 label: prop.string(),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n variant: prop.string<SurfaceVariant>(),\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: (_action, index) => {\n const tabs = getEnabledTabs();\n const nextTab = tabs[index];\n\n focusTab(nextTab);\n\n if (!isManualActivation()) {\n const value = nextTab?.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 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, SurfaceVariant, ThemeColor } 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 orientation: Readable<'horizontal' | 'vertical'>;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n variant: Readable<SurfaceVariant | 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 /** 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?: SurfaceVariant;\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' | 'glass' | 'frost'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\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/glass/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 label: prop.string(),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n variant: prop.string<SurfaceVariant>(),\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 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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"}
|
package/dist/toast.cjs
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs");require("./_dev.cjs");const t=require("./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs");require("./_dev.cjs");const t=require("./feedback/toast/toast.css.cjs");let n=require("@vielzeug/ore"),r=require("@vielzeug/ripple"),i=require("@vielzeug/gesture"),a=require("@vielzeug/arsenal/random");var o=300,s=class{#e=(0,r.signal)([]);#t=new Set;#n=new Set;#r=!1;#i=5;add(e){if(this.#r)return e.id??(0,a.uuid)();let t=e.id??(0,a.uuid)(),n=this.#e.value.filter(e=>e.phase!==`exiting`),r=n.length-(this.#i-1);if(r>0)for(let e of n.slice(0,r))this.dismiss(e.id);let i={dismissible:!0,duration:5e3,...e,exitTimeoutId:null,id:t,phase:`entering`,timer:null};return this.#l([...this.#e.value,i]),this.#o({id:t,type:`add`}),requestAnimationFrame(()=>{this.#s(t)?.phase===`entering`&&this.#u(t,{phase:`active`})}),i.duration>0&&this.#c(t,i.duration),t}clear(){for(let e of this.#e.value)e.phase!==`exiting`&&this.dismiss(e.id)}dispose(){if(!this.#r){this.#r=!0;for(let e of this.#e.value)e.timer&&clearTimeout(e.timer.timeoutId),e.exitTimeoutId&&clearTimeout(e.exitTimeoutId);this.#l([]),this.#t.clear(),this.#n.clear()}}dismiss(e){let t=this.#s(e);!t||t.phase===`exiting`||this.#r||(this.#u(e,{...this.#a(t),phase:`exiting`}),this.scheduleFinalization(e,350))}finalize(e){let t=this.#s(e);!t||this.#r||(t.timer&&clearTimeout(t.timer.timeoutId),t.exitTimeoutId&&clearTimeout(t.exitTimeoutId),this.#l(this.#e.value.filter(t=>t.id!==e)),t.onDismiss?.(),this.#o({id:e,type:`dismiss`}))}pauseTimers(){this.#r||this.#l(this.#e.value.map(e=>e.timer?(clearTimeout(e.timer.timeoutId),{...e,timer:{...e.timer,remaining:Math.max(0,e.timer.remaining-(Date.now()-e.timer.startedAt))}}):e))}resumeTimers(){if(!this.#r)for(let e of this.#e.value)e.phase===`active`&&e.timer&&e.timer.remaining>0&&this.#c(e.id,e.timer.remaining)}scheduleFinalization(e,t){let n=this.#s(e);if(n?.phase!==`exiting`||this.#r)return;n.exitTimeoutId&&clearTimeout(n.exitTimeoutId);let r=setTimeout(()=>this.finalize(e),t);this.#u(e,{exitTimeoutId:r})}setMax(e){e!=null&&(this.#i=Math.max(1,e))}subscribe(e){return e(this.#e.value),this.#t.add(e),()=>this.#t.delete(e)}subscribeEvents(e){return this.#n.add(e),()=>this.#n.delete(e)}update(e,t){let n=this.#s(e);if(!n||this.#r)return;let r=t.duration===void 0?n:this.#a(n);this.#u(e,{...r,...t,id:e}),t.duration!==void 0&&t.duration>0&&this.#c(e,t.duration)}#a(e){return e.timer&&clearTimeout(e.timer.timeoutId),{...e,timer:null}}#o(e){for(let t of this.#n)t(e)}#s(e){return this.#e.value.find(t=>t.id===e)}#c(e,t){if(t<=0||this.#r)return;let n=setTimeout(()=>this.dismiss(e),t);this.#u(e,{timer:{remaining:t,startedAt:Date.now(),timeoutId:n}})}#l(e){this.#e.value=e;for(let t of this.#t)t(e)}#u(e,t){this.#l(this.#e.value.map(n=>n.id===e?{...n,...t}:n))}},c=new WeakMap,l=new WeakMap,u=(e,t)=>{l.set(e,t),c.get(e)?.bind(t)};function d(e,t){return e.actions?.length?n.html`
|
|
2
2
|
<div slot="actions" class="toast-actions">
|
|
3
|
-
${e.actions.map(n
|
|
3
|
+
${e.actions.map(r=>n.html`
|
|
4
4
|
<ore-button
|
|
5
5
|
size="sm"
|
|
6
|
-
color=${
|
|
7
|
-
variant=${
|
|
8
|
-
@click=${()=>{
|
|
9
|
-
${
|
|
6
|
+
color=${r.color||e.color||`primary`}
|
|
7
|
+
variant=${r.variant||`solid`}
|
|
8
|
+
@click=${()=>{r.onClick?.(),t()}}>
|
|
9
|
+
${r.label}
|
|
10
10
|
</ore-button>
|
|
11
11
|
`)}
|
|
12
12
|
</div>
|
|
13
|
-
`:``}var f=`ore-toast`;(0,
|
|
13
|
+
`:``}var f=`ore-toast`;(0,n.define)(f,{props:{max:n.prop.number(5),position:n.prop.oneOf([`top-left`,`top-center`,`top-right`,`bottom-left`,`bottom-center`,`bottom-right`],`bottom-right`)},setup(){let e=(0,n.getHost)(),t=(0,n.useEmit)(),a=(0,n.ref)(),s=(0,r.signal)([]),u=(0,r.signal)(!1),f=(0,r.signal)(!1),p=(0,r.computed)(()=>u.value||f.value),m=new Map,h=new Map,g=new Set,_,v=null,y=()=>{},b=()=>{},x=e=>e.querySelector(`.toast-inner`),S=e=>{h.get(e)?.(),h.delete(e)},C=e=>{g.delete(e),v?.finalize(e)},w=(e,t)=>{let n=()=>s.value.find(t=>t.id===e)?.dismissible??!0,r=()=>{let e=x(t);e&&(e.style.transition=``,e.style.transform=``,e.style.opacity=``)};return(0,i.createPanGesture)(t,{axis:`x`,disabled:()=>!n(),onEnd:({distance:n,reason:i})=>{if(i!==`release`||Math.abs(n)<48){r();return}let a=x(t);if(!a||!v)return;g.add(e),v.dismiss(e);let o=()=>C(e);if(window.matchMedia(`(prefers-reduced-motion: reduce)`).matches){a.style.opacity=`0`,o();return}let s=n>=0?1:-1;a.style.transition=`transform 0.22s ease-out, opacity 0.22s ease-out`,a.offsetWidth,a.style.transform=`translateX(${s*120}%)`,a.style.opacity=`0`;let c=e=>{e.target===a&&e.propertyName===`transform`&&(a.removeEventListener(`transitionend`,c),o())};a.addEventListener(`transitionend`,c),v.scheduleFinalization(e,300)},onMove:({distance:e})=>{let n=x(t);n&&(n.style.transition=`none`,n.style.transform=`translateX(${e}px)`,n.style.opacity=String(Math.max(0,1-Math.abs(e)/200)))},pointerCapture:!1,shouldStart:e=>!e.composedPath().some(e=>e instanceof Element&&e.matches(`button, a, input, select, textarea, ore-button`))})},T=e=>{!v||h.has(e)||g.has(e)||requestAnimationFrame(()=>{if(!v||h.has(e)||g.has(e))return;let t=a.value?.querySelector(`[data-toast-id="${e}"] .toast-inner`);if(!t){v.scheduleFinalization(e,o);return}t.offsetHeight;let n=parseFloat(getComputedStyle(t).transitionDuration)*1e3,r=Number.isFinite(n)&&n>0?n:o,i=t=>{t.target?.closest?.(`[data-toast-id="${e}"]`)&&(S(e),v?.finalize(e))};h.set(e,()=>a.value?.removeEventListener(`transitionend`,i)),a.value?.addEventListener(`transitionend`,i),v.scheduleFinalization(e,r+50)})},E=()=>{let e=new Set(s.value.map(e=>e.id));for(let[t,n]of m)e.has(t)||(n.gesture.dispose(),m.delete(t),S(t),g.delete(t));for(let e of s.value){let t=a.value?.querySelector(`[data-toast-id="${e.id}"]`),n=m.get(e.id);t&&n?.element!==t&&(n?.gesture.dispose(),m.set(e.id,{element:t,gesture:w(e.id,t)})),e.phase===`exiting`&&T(e.id)}};(0,r.watch)(s,()=>{E(),_!==void 0&&cancelAnimationFrame(_),_=requestAnimationFrame(()=>{_=void 0,E()})}),(0,r.watch)(p,e=>{e?v?.pauseTimers():v?.resumeTimers()});let D=e=>{v!==e&&(y(),b(),v=e,y=v.subscribe(e=>{s.value=e}),b=v.subscribeEvents(e=>t(e.type,{id:e.id})),p.value&&v.pauseTimers())},O=e=>{let t=()=>v?.dismiss(e.id);return n.html`
|
|
14
14
|
<div
|
|
15
15
|
class="toast-wrapper"
|
|
16
16
|
data-toast-id=${e.id}
|
|
17
|
-
part="toast-wrapper"
|
|
18
|
-
@pointerdown=${t=>m.get(e.id)?.handlePointerDown(t)}
|
|
19
|
-
@pointermove=${t=>m.get(e.id)?.handlePointerMove(t)}
|
|
20
|
-
@pointerup=${t=>m.get(e.id)?.handlePointerUp(t)}
|
|
21
|
-
@pointercancel=${t=>m.get(e.id)?.handlePointerCancel(t)}>
|
|
17
|
+
part="toast-wrapper">
|
|
22
18
|
<div class="${()=>`toast-inner${e.phase===`active`?``:` ${e.phase}`}`}" part="toast-inner">
|
|
23
19
|
<ore-alert
|
|
24
20
|
color=${e.color||(e.urgency===`assertive`?`error`:`primary`)}
|
|
@@ -29,14 +25,14 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
29
25
|
heading=${e.heading||``}
|
|
30
26
|
?dismissible=${e.dismissible}
|
|
31
27
|
@dismiss=${t}>
|
|
32
|
-
${e.meta?
|
|
28
|
+
${e.meta?n.html`
|
|
33
29
|
<span slot="meta">${e.meta}</span>
|
|
34
30
|
`:``}
|
|
35
31
|
${e.message} ${d(e,t)}
|
|
36
32
|
</ore-alert>
|
|
37
33
|
</div>
|
|
38
34
|
</div>
|
|
39
|
-
`};(0,
|
|
35
|
+
`};(0,n.onMounted)(()=>{c.set(e,{bind:D,unbind(){y(),b(),v=null,s.value=[]}});let t=l.get(e);t&&D(t)}),(0,n.onCleanup)(()=>{c.get(e)?.unbind(),c.delete(e),_!==void 0&&cancelAnimationFrame(_);for(let e of m.values())e.gesture.dispose();m.clear();for(let e of h.keys())S(e)});let k=e=>e.urgency??(e.color===`error`?`assertive`:`polite`),A=(0,r.computed)(()=>s.value.filter(e=>k(e)===`polite`)),j=(0,r.computed)(()=>s.value.filter(e=>k(e)===`assertive`));return n.html`
|
|
40
36
|
<div
|
|
41
37
|
class="toast-container"
|
|
42
38
|
ref=${a}
|
|
@@ -52,7 +48,7 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
52
48
|
aria-atomic="false"
|
|
53
49
|
aria-label="Notifications"
|
|
54
50
|
class="toast-live-region">
|
|
55
|
-
${()=>
|
|
51
|
+
${()=>A.value.map(O)}
|
|
56
52
|
</div>
|
|
57
53
|
<div
|
|
58
54
|
role="region"
|
|
@@ -61,9 +57,9 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
61
57
|
aria-atomic="false"
|
|
62
58
|
aria-label="Critical notifications"
|
|
63
59
|
class="toast-live-region">
|
|
64
|
-
${()=>
|
|
60
|
+
${()=>j.value.map(O)}
|
|
65
61
|
</div>
|
|
66
62
|
<slot></slot>
|
|
67
63
|
</div>
|
|
68
|
-
`},styles:[e.reducedMotionMixin,
|
|
64
|
+
`},styles:[e.reducedMotionMixin,t.default]});var p=new WeakMap;function m(e=document.body){let t=p.get(e);if(t)return t;let n=new s,r=new AbortController,i=!1,o=!1,c=null,l=null,d=()=>!i,m=e,h=()=>{if(!d())return null;if(l?.isConnected)return l;if(l=m.querySelector(f),l||(l=document.createElement(f),c?.position&&l.setAttribute(`position`,c.position),c?.max!=null&&l.setAttribute(`max`,String(c.max)),m.appendChild(l)),c?.max!=null)n.setMax(c.max);else{let e=Number(l.getAttribute(`max`));Number.isFinite(e)&&e>0&&n.setMax(e)}return c=null,o=!0,u(l,n),l},g=()=>!!h(),_={add(e){return g()?n.add(e):e.id??(0,a.uuid)()},clear(){g()&&n.clear()},configure(e){o||(c={...c,...e})},dismiss(e){g()&&n.dismiss(e)},get disposalSignal(){return r.signal},dispose(){i||(i=!0,r.abort(),n.dispose(),p.get(e)===_&&p.delete(e))},get disposed(){return i},error(e,t){return _.add({color:`error`,...t,message:e})},info(e,t){return _.add({color:`info`,...t,message:e})},async promise(e,t){let n=_.add({color:`primary`,dismissible:!1,duration:0,message:t.loading});try{let r=await e;return _.update(n,{color:`success`,dismissible:!0,duration:5e3,message:typeof t.success==`function`?t.success(r):t.success}),r}catch(e){throw _.update(n,{color:`error`,dismissible:!0,duration:5e3,message:typeof t.error==`function`?t.error(e):t.error}),e}},success(e,t){return _.add({color:`success`,...t,message:e})},[Symbol.dispose](){_.dispose()},update(e,t){g()&&n.update(e,t)},warning(e,t){return _.add({color:`warning`,...t,message:e})}};return p.set(e,_),_}var h=m();exports.TOAST_TAG=f,exports.createToastService=m,exports.toast=h;
|
|
69
65
|
//# sourceMappingURL=toast.cjs.map
|
package/dist/toast.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.cjs","names":["#entries","#listeners","#eventListeners","#disposed","#max","#setEntries","#emit","#entry","#update","#scheduleTimer","#clearTimer"],"sources":["../src/feedback/toast/toast.ts"],"sourcesContent":["import '../alert/alert';\nimport { uuid } from '@vielzeug/arsenal/random';\nimport { define, getHost, html, onCleanup, onMounted, prop, ref, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { warn } from '../../_dev';\nimport type { SwipeControl } from '../../core';\nimport { createSwipeControl } from '../../core';\nimport { reducedMotionMixin } from '../../styles';\nimport type { ComponentSize, RoundedSize, ThemeColor } from '../../types';\nimport componentStyles from './toast.css?inline';\n\n/** Must match toast.css's default opacity transition duration. */\nconst TOAST_EXIT_MS = 300;\n\nexport type OreToastEvents = {\n add: { id: string };\n dismiss: { id: string };\n};\n\nexport type OreToastProps = {\n max?: number;\n position?: 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';\n};\n\n/** Individual toast notification. */\nexport type ToastItem = {\n actions?: Array<{\n color?: ThemeColor;\n label: string;\n onClick?: () => void;\n variant?: 'solid' | 'flat' | 'bordered';\n }>;\n color?: ThemeColor;\n dismissible?: boolean;\n /** Auto-dismiss delay in ms. Set to 0 for persistent toasts (default: 5000). */\n duration?: number;\n heading?: string;\n /** Show message and actions side-by-side (horizontal layout). */\n horizontal?: boolean;\n /** Auto-generated when omitted. */\n id?: string;\n message: string;\n /** Metadata text (for example, a timestamp) shown in the alert meta slot. */\n meta?: string;\n /** Called after the toast is fully dismissed and removed. */\n onDismiss?: () => void;\n rounded?: RoundedSize | '';\n size?: ComponentSize;\n /**\n * Screen-reader announcement urgency. Error-coloured toasts are assertive by\n * default; all other toasts are polite.\n */\n urgency?: 'polite' | 'assertive';\n variant?: 'solid' | 'flat' | 'bordered';\n};\n\ntype ToastPhase = 'entering' | 'active' | 'exiting';\n\ntype ToastTimer = {\n remaining: number;\n startedAt: number;\n timeoutId: ReturnType<typeof setTimeout>;\n};\n\ntype ToastEntry = Required<Pick<ToastItem, 'dismissible' | 'duration' | 'id'>> &\n Omit<ToastItem, 'dismissible' | 'duration' | 'id'> & {\n exitTimeoutId: ReturnType<typeof setTimeout> | null;\n id: string;\n phase: ToastPhase;\n timer: ToastTimer | null;\n };\n\ntype ToastStoreEvent = {\n id: string;\n type: 'add' | 'dismiss';\n};\n\n/**\n * Owns notification data and every timer. Renderers only subscribe to this\n * store; they never expose imperative mutation methods themselves.\n */\nclass ToastStore {\n readonly #entries = signal<ToastEntry[]>([]);\n readonly #listeners = new Set<(entries: ToastEntry[]) => void>();\n readonly #eventListeners = new Set<(event: ToastStoreEvent) => void>();\n #disposed = false;\n #max = 5;\n\n add(item: ToastItem): string {\n if (this.#disposed) return item.id ?? uuid();\n\n const id = item.id ?? uuid();\n const active = this.#entries.value.filter((entry) => entry.phase !== 'exiting');\n const overflow = active.length - (this.#max - 1);\n\n if (overflow > 0) {\n for (const entry of active.slice(0, overflow)) this.dismiss(entry.id);\n }\n\n const entry: ToastEntry = {\n dismissible: true,\n duration: 5000,\n ...item,\n exitTimeoutId: null,\n id,\n phase: 'entering',\n timer: null,\n };\n\n this.#setEntries([...this.#entries.value, entry]);\n this.#emit({ id, type: 'add' });\n\n requestAnimationFrame(() => {\n if (this.#entry(id)?.phase === 'entering') this.#update(id, { phase: 'active' });\n });\n\n if (entry.duration > 0) this.#scheduleTimer(id, entry.duration);\n\n return id;\n }\n\n clear(): void {\n for (const entry of this.#entries.value) {\n if (entry.phase !== 'exiting') this.dismiss(entry.id);\n }\n }\n\n dispose(): void {\n if (this.#disposed) return;\n\n this.#disposed = true;\n\n for (const entry of this.#entries.value) {\n if (entry.timer) clearTimeout(entry.timer.timeoutId);\n\n if (entry.exitTimeoutId) clearTimeout(entry.exitTimeoutId);\n }\n\n this.#setEntries([]);\n this.#listeners.clear();\n this.#eventListeners.clear();\n }\n\n dismiss(id: string): void {\n const entry = this.#entry(id);\n\n if (!entry || entry.phase === 'exiting' || this.#disposed) return;\n\n this.#update(id, { ...this.#clearTimer(entry), phase: 'exiting' });\n this.scheduleFinalization(id, TOAST_EXIT_MS + 50);\n }\n\n finalize(id: string): void {\n const entry = this.#entry(id);\n\n if (!entry || this.#disposed) return;\n\n if (entry.timer) clearTimeout(entry.timer.timeoutId);\n\n if (entry.exitTimeoutId) clearTimeout(entry.exitTimeoutId);\n\n this.#setEntries(this.#entries.value.filter((candidate) => candidate.id !== id));\n entry.onDismiss?.();\n this.#emit({ id, type: 'dismiss' });\n }\n\n pauseTimers(): void {\n if (this.#disposed) return;\n\n this.#setEntries(\n this.#entries.value.map((entry) => {\n if (!entry.timer) return entry;\n\n clearTimeout(entry.timer.timeoutId);\n\n return {\n ...entry,\n timer: {\n ...entry.timer,\n remaining: Math.max(0, entry.timer.remaining - (Date.now() - entry.timer.startedAt)),\n },\n };\n }),\n );\n }\n\n resumeTimers(): void {\n if (this.#disposed) return;\n\n for (const entry of this.#entries.value) {\n if (entry.phase === 'active' && entry.timer && entry.timer.remaining > 0) {\n this.#scheduleTimer(entry.id, entry.timer.remaining);\n }\n }\n }\n\n scheduleFinalization(id: string, delay: number): void {\n const entry = this.#entry(id);\n\n if (entry?.phase !== 'exiting' || this.#disposed) return;\n\n if (entry.exitTimeoutId) clearTimeout(entry.exitTimeoutId);\n\n const exitTimeoutId = setTimeout(() => this.finalize(id), delay);\n\n this.#update(id, { exitTimeoutId });\n }\n\n setMax(max: number | undefined): void {\n if (max != null) this.#max = Math.max(1, max);\n }\n\n subscribe(listener: (entries: ToastEntry[]) => void): () => void {\n listener(this.#entries.value);\n this.#listeners.add(listener);\n\n return () => this.#listeners.delete(listener);\n }\n\n subscribeEvents(listener: (event: ToastStoreEvent) => void): () => void {\n this.#eventListeners.add(listener);\n\n return () => this.#eventListeners.delete(listener);\n }\n\n update(id: string, updates: Partial<ToastItem>): void {\n const entry = this.#entry(id);\n\n if (!entry || this.#disposed) return;\n\n const cleared = updates.duration !== undefined ? this.#clearTimer(entry) : entry;\n\n this.#update(id, { ...cleared, ...updates, id });\n\n if (updates.duration !== undefined && updates.duration > 0) this.#scheduleTimer(id, updates.duration);\n }\n\n #clearTimer(entry: ToastEntry): ToastEntry {\n if (entry.timer) clearTimeout(entry.timer.timeoutId);\n\n return { ...entry, timer: null };\n }\n\n #emit(event: ToastStoreEvent): void {\n for (const listener of this.#eventListeners) listener(event);\n }\n\n #entry(id: string): ToastEntry | undefined {\n return this.#entries.value.find((entry) => entry.id === id);\n }\n\n #scheduleTimer(id: string, remaining: number): void {\n if (remaining <= 0 || this.#disposed) return;\n\n const timeoutId = setTimeout(() => this.dismiss(id), remaining);\n\n this.#update(id, { timer: { remaining, startedAt: Date.now(), timeoutId } });\n }\n\n #setEntries(entries: ToastEntry[]): void {\n this.#entries.value = entries;\n for (const listener of this.#listeners) listener(entries);\n }\n\n #update(id: string, patch: Partial<ToastEntry>): void {\n this.#setEntries(this.#entries.value.map((entry) => (entry.id === id ? { ...entry, ...patch } : entry)));\n }\n}\n\ntype ToastHostBinding = {\n bind: (store: ToastStore) => void;\n unbind: () => void;\n};\n\nconst hostBindings = new WeakMap<HTMLElement, ToastHostBinding>();\nconst hostStores = new WeakMap<HTMLElement, ToastStore>();\n\nconst bindToastHost = (host: HTMLElement, store: ToastStore): void => {\n hostStores.set(host, store);\n hostBindings.get(host)?.bind(store);\n};\n\n/** Renders the action buttons for a toast entry. */\nfunction renderToastActions(entry: ToastEntry, dismiss: () => void) {\n if (!entry.actions?.length) return '';\n\n return html`\n <div slot=\"actions\" class=\"toast-actions\">\n ${entry.actions.map(\n (action) => html`\n <ore-button\n size=\"sm\"\n color=${action.color || entry.color || 'primary'}\n variant=${action.variant || 'solid'}\n @click=${() => {\n action.onClick?.();\n dismiss();\n }}>\n ${action.label}\n </ore-button>\n `,\n )}\n </div>\n `;\n}\n\nexport const TOAST_TAG = 'ore-toast' as const;\n\n/**\n * Declarative toast host. It subscribes to the service for its scope and\n * renders notifications, but has no imperative mutation API of its own.\n *\n * @element ore-toast\n *\n * @attr {string} position - Stack placement.\n * @attr {number} max - Maximum live notifications for the scoped service.\n */\ndefine<OreToastProps>(TOAST_TAG, {\n props: {\n max: prop.number(5),\n position: prop.oneOf(\n ['top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right'] as const,\n 'bottom-right',\n ),\n },\n setup() {\n const el = getHost() as HTMLElement;\n const emit = useEmit<OreToastEvents>();\n const containerRef = ref<HTMLDivElement>();\n const entries = signal<ToastEntry[]>([]);\n const hoverPaused = signal(false);\n const focusPaused = signal(false);\n const paused = computed(() => hoverPaused.value || focusPaused.value);\n const swipeControls = new Map<string, SwipeControl>();\n const exiting = new Map<string, () => void>();\n const swiping = new Set<string>();\n let store: ToastStore | null = null;\n let unsubscribeEntries = () => {};\n let unsubscribeEvents = () => {};\n\n const getInner = (wrapperOrEvent: HTMLElement | Event): HTMLElement | null => {\n const wrapper = wrapperOrEvent instanceof Event ? (wrapperOrEvent.currentTarget as HTMLElement) : wrapperOrEvent;\n\n return wrapper.querySelector<HTMLElement>('.toast-inner');\n };\n\n const clearExitListener = (id: string): void => {\n exiting.get(id)?.();\n exiting.delete(id);\n };\n\n const finalizeSwipe = (id: string): void => {\n swiping.delete(id);\n store?.finalize(id);\n };\n\n const createToastSwipe = (id: string): SwipeControl => {\n const isDismissible = (): boolean => entries.value.find((entry) => entry.id === id)?.dismissible ?? true;\n\n return createSwipeControl({\n axis: () => 'x',\n captureTarget: () => null,\n disabled: {\n peek: () => !isDismissible(),\n subscribe: (listener) => entries.subscribe(listener),\n get value() {\n return !isDismissible();\n },\n },\n onCancel: ({ event }) => {\n const inner = getInner(event);\n\n if (!inner) return;\n\n inner.style.transition = '';\n inner.style.transform = '';\n inner.style.opacity = '';\n },\n onCommit: ({ distance, event }) => {\n const inner = getInner(event);\n\n if (!inner || !store) return;\n\n swiping.add(id);\n\n store.dismiss(id);\n\n const finish = () => finalizeSwipe(id);\n\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {\n inner.style.opacity = '0';\n finish();\n\n return;\n }\n\n const direction = distance >= 0 ? 1 : -1;\n\n inner.style.transition = 'transform 0.22s ease-out, opacity 0.22s ease-out';\n void inner.offsetWidth;\n inner.style.transform = `translateX(${direction * 120}%)`;\n inner.style.opacity = '0';\n\n const onTransitionEnd = (transition: TransitionEvent) => {\n if (transition.target !== inner || transition.propertyName !== 'transform') return;\n\n inner.removeEventListener('transitionend', onTransitionEnd);\n finish();\n };\n\n inner.addEventListener('transitionend', onTransitionEnd);\n store.scheduleFinalization(id, 300);\n },\n onMove: ({ distance, event }) => {\n const inner = getInner(event);\n\n if (!inner) return;\n\n inner.style.transition = 'none';\n inner.style.transform = `translateX(${distance}px)`;\n inner.style.opacity = String(Math.max(0, 1 - Math.abs(distance) / 200));\n },\n onRelease: ({ event }) => {\n const inner = getInner(event);\n\n if (!inner) return;\n\n inner.style.transition = '';\n inner.style.transform = '';\n inner.style.opacity = '';\n },\n });\n };\n\n const beginExit = (id: string): void => {\n if (!store || exiting.has(id) || swiping.has(id)) return;\n\n requestAnimationFrame(() => {\n if (!store || exiting.has(id) || swiping.has(id)) return;\n\n const inner = containerRef.value?.querySelector<HTMLElement>(`[data-toast-id=\"${id}\"] .toast-inner`);\n\n if (!inner) {\n store.scheduleFinalization(id, TOAST_EXIT_MS);\n\n return;\n }\n\n void inner.offsetHeight;\n\n const duration = parseFloat(getComputedStyle(inner).transitionDuration) * 1000;\n const exitMs = Number.isFinite(duration) && duration > 0 ? duration : TOAST_EXIT_MS;\n const onTransitionEnd = (event: TransitionEvent) => {\n const wrapper = (event.target as HTMLElement | null)?.closest?.(`[data-toast-id=\"${id}\"]`);\n\n if (!wrapper) return;\n\n clearExitListener(id);\n store?.finalize(id);\n };\n const remove = () => containerRef.value?.removeEventListener('transitionend', onTransitionEnd);\n\n exiting.set(id, remove);\n containerRef.value?.addEventListener('transitionend', onTransitionEnd);\n store.scheduleFinalization(id, exitMs + 50);\n });\n };\n\n const syncControls = (): void => {\n const currentIds = new Set(entries.value.map((entry) => entry.id));\n\n for (const [id, control] of swipeControls) {\n if (!currentIds.has(id)) {\n control.dispose();\n\n swipeControls.delete(id);\n clearExitListener(id);\n swiping.delete(id);\n }\n }\n\n for (const entry of entries.value) {\n if (!swipeControls.has(entry.id)) swipeControls.set(entry.id, createToastSwipe(entry.id));\n\n if (entry.phase === 'exiting') beginExit(entry.id);\n }\n };\n\n watch(entries, syncControls);\n watch(paused, (isPaused) => {\n if (isPaused) store?.pauseTimers();\n else store?.resumeTimers();\n });\n\n const bind = (nextStore: ToastStore): void => {\n if (store === nextStore) return;\n\n unsubscribeEntries();\n\n unsubscribeEvents();\n store = nextStore;\n unsubscribeEntries = store.subscribe((nextEntries) => {\n entries.value = nextEntries;\n });\n unsubscribeEvents = store.subscribeEvents((event) => emit(event.type, { id: event.id }));\n\n if (paused.value) store.pauseTimers();\n };\n\n const renderEntry = (entry: ToastEntry) => {\n const dismiss = () => store?.dismiss(entry.id);\n\n return html`\n <div\n class=\"toast-wrapper\"\n data-toast-id=${entry.id}\n part=\"toast-wrapper\"\n @pointerdown=${(event: PointerEvent) => swipeControls.get(entry.id)?.handlePointerDown(event)}\n @pointermove=${(event: PointerEvent) => swipeControls.get(entry.id)?.handlePointerMove(event)}\n @pointerup=${(event: PointerEvent) => swipeControls.get(entry.id)?.handlePointerUp(event)}\n @pointercancel=${(event: PointerEvent) => swipeControls.get(entry.id)?.handlePointerCancel(event)}>\n <div class=\"${() => `toast-inner${entry.phase !== 'active' ? ` ${entry.phase}` : ''}`}\" part=\"toast-inner\">\n <ore-alert\n color=${entry.color || (entry.urgency === 'assertive' ? 'error' : 'primary')}\n variant=${entry.variant || 'solid'}\n size=${entry.size || 'md'}\n rounded=${entry.rounded || 'md'}\n ?horizontal=${entry.horizontal}\n heading=${entry.heading || ''}\n ?dismissible=${entry.dismissible}\n @dismiss=${dismiss}>\n ${\n entry.meta\n ? html`\n <span slot=\"meta\">${entry.meta}</span>\n `\n : ''\n }\n ${entry.message} ${renderToastActions(entry, dismiss)}\n </ore-alert>\n </div>\n </div>\n `;\n };\n\n onMounted(() => {\n hostBindings.set(el, {\n bind,\n unbind() {\n unsubscribeEntries();\n unsubscribeEvents();\n store = null;\n entries.value = [];\n },\n });\n\n const boundStore = hostStores.get(el);\n\n if (boundStore) bind(boundStore);\n });\n\n onCleanup(() => {\n hostBindings.get(el)?.unbind();\n\n hostBindings.delete(el);\n\n for (const control of swipeControls.values()) control.dispose();\n for (const id of exiting.keys()) clearExitListener(id);\n });\n\n const urgencyOf = (entry: ToastEntry) => entry.urgency ?? (entry.color === 'error' ? 'assertive' : 'polite');\n const politeEntries = computed(() => entries.value.filter((entry) => urgencyOf(entry) === 'polite'));\n const assertiveEntries = computed(() => entries.value.filter((entry) => urgencyOf(entry) === 'assertive'));\n\n return html`\n <div\n class=\"toast-container\"\n ref=${containerRef}\n @pointerenter=${() => {\n hoverPaused.value = true;\n el.classList.add('hovered');\n }}\n @pointerleave=${() => {\n hoverPaused.value = false;\n el.classList.remove('hovered');\n }}\n @focusin=${() => {\n focusPaused.value = true;\n }}\n @focusout=${() => {\n focusPaused.value = false;\n }}\n part=\"container\">\n <div\n role=\"region\"\n aria-live=\"polite\"\n aria-relevant=\"additions removals\"\n aria-atomic=\"false\"\n aria-label=\"Notifications\"\n class=\"toast-live-region\">\n ${() => politeEntries.value.map(renderEntry)}\n </div>\n <div\n role=\"region\"\n aria-live=\"assertive\"\n aria-relevant=\"additions removals\"\n aria-atomic=\"false\"\n aria-label=\"Critical notifications\"\n class=\"toast-live-region\">\n ${() => assertiveEntries.value.map(renderEntry)}\n </div>\n <slot></slot>\n </div>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n\nexport type ToastServiceConfig = OreToastProps;\n\nexport interface ToastService {\n add(item: ToastItem): string;\n clear(): void;\n configure(config: ToastServiceConfig): void;\n dismiss(id: string): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n error(message: string, opts?: Partial<ToastItem>): string;\n info(message: string, opts?: Partial<ToastItem>): string;\n promise<T>(\n promise: Promise<T>,\n messages: {\n error: string | ((err: unknown) => string);\n loading: string;\n success: string | ((data: T) => string);\n },\n ): Promise<T>;\n success(message: string, opts?: Partial<ToastItem>): string;\n update(id: string, updates: Partial<ToastItem>): void;\n warning(message: string, opts?: Partial<ToastItem>): string;\n [Symbol.dispose](): void;\n}\n\nconst services = new WeakMap<ParentNode, ToastService>();\n\n/**\n * Creates a scoped toast service. The service owns its notification store and\n * binds it to the declarative `<ore-toast>` host inside `root`, creating that\n * host lazily when notifications are first requested.\n */\nexport function createToastService(root: ParentNode = document.body): ToastService {\n const existing = services.get(root);\n\n if (existing) return existing;\n\n const store = new ToastStore();\n const disposalController = new AbortController();\n let disposed = false;\n let configured = false;\n let pendingConfig: ToastServiceConfig | null = null;\n let host: HTMLElement | null = null;\n\n const assertActive = (): boolean => !disposed;\n const rootNode = root as Element | Document | ShadowRoot;\n const getHost = (): HTMLElement | null => {\n if (!assertActive()) return null;\n\n if (host?.isConnected) return host;\n\n host = rootNode.querySelector<HTMLElement>(TOAST_TAG);\n\n if (!host) {\n host = document.createElement(TOAST_TAG);\n\n if (pendingConfig?.position) host.setAttribute('position', pendingConfig.position);\n\n if (pendingConfig?.max != null) host.setAttribute('max', String(pendingConfig.max));\n\n rootNode.appendChild(host);\n }\n\n if (pendingConfig?.max != null) store.setMax(pendingConfig.max);\n else {\n const max = Number(host.getAttribute('max'));\n\n if (Number.isFinite(max) && max > 0) store.setMax(max);\n }\n\n pendingConfig = null;\n configured = true;\n bindToastHost(host, store);\n\n return host;\n };\n const ensureHost = (): boolean => Boolean(getHost());\n\n const service: ToastService = {\n add(item) {\n if (!ensureHost()) return item.id ?? uuid();\n\n return store.add(item);\n },\n\n clear() {\n if (ensureHost()) store.clear();\n },\n\n configure(config) {\n if (configured) {\n warn('toast.configure() called after the container was already created; options ignored.');\n\n return;\n }\n\n pendingConfig = { ...pendingConfig, ...config };\n },\n\n dismiss(id) {\n if (ensureHost()) store.dismiss(id);\n },\n\n get disposalSignal() {\n return disposalController.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n\n disposalController.abort();\n\n store.dispose();\n\n if (services.get(root) === service) services.delete(root);\n },\n\n get disposed() {\n return disposed;\n },\n\n error(message, opts) {\n return service.add({ color: 'error', ...opts, message });\n },\n\n info(message, opts) {\n return service.add({ color: 'info', ...opts, message });\n },\n\n async promise(promise, messages) {\n const id = service.add({ color: 'primary', dismissible: false, duration: 0, message: messages.loading });\n\n try {\n const data = await promise;\n\n service.update(id, {\n color: 'success',\n dismissible: true,\n duration: 5000,\n message: typeof messages.success === 'function' ? messages.success(data) : messages.success,\n });\n\n return data;\n } catch (err) {\n service.update(id, {\n color: 'error',\n dismissible: true,\n duration: 5000,\n message: typeof messages.error === 'function' ? messages.error(err) : messages.error,\n });\n throw err;\n }\n },\n\n success(message, opts) {\n return service.add({ color: 'success', ...opts, message });\n },\n\n [Symbol.dispose]() {\n service.dispose();\n },\n\n update(id, updates) {\n if (ensureHost()) store.update(id, updates);\n },\n\n warning(message, opts) {\n return service.add({ color: 'warning', ...opts, message });\n },\n };\n\n services.set(root, service);\n\n return service;\n}\n\n/** Singleton service backed by a lazily created host in `document.body`. */\nexport const toast: ToastService = createToastService();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"toast.cjs","names":["#entries","#listeners","#eventListeners","#disposed","#max","#setEntries","#emit","#entry","#update","#scheduleTimer","#clearTimer"],"sources":["../src/feedback/toast/toast.ts"],"sourcesContent":["import '../alert/alert';\nimport { uuid } from '@vielzeug/arsenal/random';\nimport { createPanGesture, type PanGesture } from '@vielzeug/gesture';\nimport { define, getHost, html, onCleanup, onMounted, prop, ref, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { warn } from '../../_dev';\nimport { reducedMotionMixin } from '../../styles';\nimport type { ComponentSize, RoundedSize, ThemeColor } from '../../types';\nimport componentStyles from './toast.css?inline';\n\n/** Must match toast.css's default opacity transition duration. */\nconst TOAST_EXIT_MS = 300;\n\nexport type OreToastEvents = {\n add: { id: string };\n dismiss: { id: string };\n};\n\nexport type OreToastProps = {\n max?: number;\n position?: 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';\n};\n\n/** Individual toast notification. */\nexport type ToastItem = {\n actions?: Array<{\n color?: ThemeColor;\n label: string;\n onClick?: () => void;\n variant?: 'solid' | 'flat' | 'bordered';\n }>;\n color?: ThemeColor;\n dismissible?: boolean;\n /** Auto-dismiss delay in ms. Set to 0 for persistent toasts (default: 5000). */\n duration?: number;\n heading?: string;\n /** Show message and actions side-by-side (horizontal layout). */\n horizontal?: boolean;\n /** Auto-generated when omitted. */\n id?: string;\n message: string;\n /** Metadata text (for example, a timestamp) shown in the alert meta slot. */\n meta?: string;\n /** Called after the toast is fully dismissed and removed. */\n onDismiss?: () => void;\n rounded?: RoundedSize | '';\n size?: ComponentSize;\n /**\n * Screen-reader announcement urgency. Error-coloured toasts are assertive by\n * default; all other toasts are polite.\n */\n urgency?: 'polite' | 'assertive';\n variant?: 'solid' | 'flat' | 'bordered';\n};\n\ntype ToastPhase = 'entering' | 'active' | 'exiting';\n\ntype ToastTimer = {\n remaining: number;\n startedAt: number;\n timeoutId: ReturnType<typeof setTimeout>;\n};\n\ntype ToastEntry = Required<Pick<ToastItem, 'dismissible' | 'duration' | 'id'>> &\n Omit<ToastItem, 'dismissible' | 'duration' | 'id'> & {\n exitTimeoutId: ReturnType<typeof setTimeout> | null;\n id: string;\n phase: ToastPhase;\n timer: ToastTimer | null;\n };\n\ntype ToastStoreEvent = {\n id: string;\n type: 'add' | 'dismiss';\n};\n\n/**\n * Owns notification data and every timer. Renderers only subscribe to this\n * store; they never expose imperative mutation methods themselves.\n */\nclass ToastStore {\n readonly #entries = signal<ToastEntry[]>([]);\n readonly #listeners = new Set<(entries: ToastEntry[]) => void>();\n readonly #eventListeners = new Set<(event: ToastStoreEvent) => void>();\n #disposed = false;\n #max = 5;\n\n add(item: ToastItem): string {\n if (this.#disposed) return item.id ?? uuid();\n\n const id = item.id ?? uuid();\n const active = this.#entries.value.filter((entry) => entry.phase !== 'exiting');\n const overflow = active.length - (this.#max - 1);\n\n if (overflow > 0) {\n for (const entry of active.slice(0, overflow)) this.dismiss(entry.id);\n }\n\n const entry: ToastEntry = {\n dismissible: true,\n duration: 5000,\n ...item,\n exitTimeoutId: null,\n id,\n phase: 'entering',\n timer: null,\n };\n\n this.#setEntries([...this.#entries.value, entry]);\n this.#emit({ id, type: 'add' });\n\n requestAnimationFrame(() => {\n if (this.#entry(id)?.phase === 'entering') this.#update(id, { phase: 'active' });\n });\n\n if (entry.duration > 0) this.#scheduleTimer(id, entry.duration);\n\n return id;\n }\n\n clear(): void {\n for (const entry of this.#entries.value) {\n if (entry.phase !== 'exiting') this.dismiss(entry.id);\n }\n }\n\n dispose(): void {\n if (this.#disposed) return;\n\n this.#disposed = true;\n\n for (const entry of this.#entries.value) {\n if (entry.timer) clearTimeout(entry.timer.timeoutId);\n\n if (entry.exitTimeoutId) clearTimeout(entry.exitTimeoutId);\n }\n\n this.#setEntries([]);\n this.#listeners.clear();\n this.#eventListeners.clear();\n }\n\n dismiss(id: string): void {\n const entry = this.#entry(id);\n\n if (!entry || entry.phase === 'exiting' || this.#disposed) return;\n\n this.#update(id, { ...this.#clearTimer(entry), phase: 'exiting' });\n this.scheduleFinalization(id, TOAST_EXIT_MS + 50);\n }\n\n finalize(id: string): void {\n const entry = this.#entry(id);\n\n if (!entry || this.#disposed) return;\n\n if (entry.timer) clearTimeout(entry.timer.timeoutId);\n\n if (entry.exitTimeoutId) clearTimeout(entry.exitTimeoutId);\n\n this.#setEntries(this.#entries.value.filter((candidate) => candidate.id !== id));\n entry.onDismiss?.();\n this.#emit({ id, type: 'dismiss' });\n }\n\n pauseTimers(): void {\n if (this.#disposed) return;\n\n this.#setEntries(\n this.#entries.value.map((entry) => {\n if (!entry.timer) return entry;\n\n clearTimeout(entry.timer.timeoutId);\n\n return {\n ...entry,\n timer: {\n ...entry.timer,\n remaining: Math.max(0, entry.timer.remaining - (Date.now() - entry.timer.startedAt)),\n },\n };\n }),\n );\n }\n\n resumeTimers(): void {\n if (this.#disposed) return;\n\n for (const entry of this.#entries.value) {\n if (entry.phase === 'active' && entry.timer && entry.timer.remaining > 0) {\n this.#scheduleTimer(entry.id, entry.timer.remaining);\n }\n }\n }\n\n scheduleFinalization(id: string, delay: number): void {\n const entry = this.#entry(id);\n\n if (entry?.phase !== 'exiting' || this.#disposed) return;\n\n if (entry.exitTimeoutId) clearTimeout(entry.exitTimeoutId);\n\n const exitTimeoutId = setTimeout(() => this.finalize(id), delay);\n\n this.#update(id, { exitTimeoutId });\n }\n\n setMax(max: number | undefined): void {\n if (max != null) this.#max = Math.max(1, max);\n }\n\n subscribe(listener: (entries: ToastEntry[]) => void): () => void {\n listener(this.#entries.value);\n this.#listeners.add(listener);\n\n return () => this.#listeners.delete(listener);\n }\n\n subscribeEvents(listener: (event: ToastStoreEvent) => void): () => void {\n this.#eventListeners.add(listener);\n\n return () => this.#eventListeners.delete(listener);\n }\n\n update(id: string, updates: Partial<ToastItem>): void {\n const entry = this.#entry(id);\n\n if (!entry || this.#disposed) return;\n\n const cleared = updates.duration !== undefined ? this.#clearTimer(entry) : entry;\n\n this.#update(id, { ...cleared, ...updates, id });\n\n if (updates.duration !== undefined && updates.duration > 0) this.#scheduleTimer(id, updates.duration);\n }\n\n #clearTimer(entry: ToastEntry): ToastEntry {\n if (entry.timer) clearTimeout(entry.timer.timeoutId);\n\n return { ...entry, timer: null };\n }\n\n #emit(event: ToastStoreEvent): void {\n for (const listener of this.#eventListeners) listener(event);\n }\n\n #entry(id: string): ToastEntry | undefined {\n return this.#entries.value.find((entry) => entry.id === id);\n }\n\n #scheduleTimer(id: string, remaining: number): void {\n if (remaining <= 0 || this.#disposed) return;\n\n const timeoutId = setTimeout(() => this.dismiss(id), remaining);\n\n this.#update(id, { timer: { remaining, startedAt: Date.now(), timeoutId } });\n }\n\n #setEntries(entries: ToastEntry[]): void {\n this.#entries.value = entries;\n for (const listener of this.#listeners) listener(entries);\n }\n\n #update(id: string, patch: Partial<ToastEntry>): void {\n this.#setEntries(this.#entries.value.map((entry) => (entry.id === id ? { ...entry, ...patch } : entry)));\n }\n}\n\ntype ToastHostBinding = {\n bind: (store: ToastStore) => void;\n unbind: () => void;\n};\n\nconst hostBindings = new WeakMap<HTMLElement, ToastHostBinding>();\nconst hostStores = new WeakMap<HTMLElement, ToastStore>();\n\nconst bindToastHost = (host: HTMLElement, store: ToastStore): void => {\n hostStores.set(host, store);\n hostBindings.get(host)?.bind(store);\n};\n\n/** Renders the action buttons for a toast entry. */\nfunction renderToastActions(entry: ToastEntry, dismiss: () => void) {\n if (!entry.actions?.length) return '';\n\n return html`\n <div slot=\"actions\" class=\"toast-actions\">\n ${entry.actions.map(\n (action) => html`\n <ore-button\n size=\"sm\"\n color=${action.color || entry.color || 'primary'}\n variant=${action.variant || 'solid'}\n @click=${() => {\n action.onClick?.();\n dismiss();\n }}>\n ${action.label}\n </ore-button>\n `,\n )}\n </div>\n `;\n}\n\nexport const TOAST_TAG = 'ore-toast' as const;\n\n/**\n * Declarative toast host. It subscribes to the service for its scope and\n * renders notifications, but has no imperative mutation API of its own.\n *\n * @element ore-toast\n *\n * @attr {string} position - Stack placement.\n * @attr {number} max - Maximum live notifications for the scoped service.\n */\ndefine<OreToastProps>(TOAST_TAG, {\n props: {\n max: prop.number(5),\n position: prop.oneOf(\n ['top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right'] as const,\n 'bottom-right',\n ),\n },\n setup() {\n const el = getHost() as HTMLElement;\n const emit = useEmit<OreToastEvents>();\n const containerRef = ref<HTMLDivElement>();\n const entries = signal<ToastEntry[]>([]);\n const hoverPaused = signal(false);\n const focusPaused = signal(false);\n const paused = computed(() => hoverPaused.value || focusPaused.value);\n const swipeControls = new Map<string, { element: HTMLElement; gesture: PanGesture }>();\n const exiting = new Map<string, () => void>();\n const swiping = new Set<string>();\n let pendingSyncFrame: number | undefined;\n let store: ToastStore | null = null;\n let unsubscribeEntries = () => {};\n let unsubscribeEvents = () => {};\n\n const getInner = (wrapper: HTMLElement): HTMLElement | null => {\n return wrapper.querySelector<HTMLElement>('.toast-inner');\n };\n\n const clearExitListener = (id: string): void => {\n exiting.get(id)?.();\n exiting.delete(id);\n };\n\n const finalizeSwipe = (id: string): void => {\n swiping.delete(id);\n store?.finalize(id);\n };\n\n const createToastSwipe = (id: string, wrapper: HTMLElement): PanGesture => {\n const isDismissible = (): boolean => entries.value.find((entry) => entry.id === id)?.dismissible ?? true;\n const reset = (): void => {\n const inner = getInner(wrapper);\n\n if (!inner) return;\n\n inner.style.transition = '';\n inner.style.transform = '';\n inner.style.opacity = '';\n };\n\n return createPanGesture(wrapper, {\n axis: 'x',\n disabled: () => !isDismissible(),\n onEnd: ({ distance, reason }) => {\n if (reason !== 'release' || Math.abs(distance) < 48) {\n reset();\n\n return;\n }\n\n const inner = getInner(wrapper);\n\n if (!inner || !store) return;\n\n swiping.add(id);\n\n store.dismiss(id);\n\n const finish = () => finalizeSwipe(id);\n\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {\n inner.style.opacity = '0';\n finish();\n\n return;\n }\n\n const direction = distance >= 0 ? 1 : -1;\n\n inner.style.transition = 'transform 0.22s ease-out, opacity 0.22s ease-out';\n void inner.offsetWidth;\n inner.style.transform = `translateX(${direction * 120}%)`;\n inner.style.opacity = '0';\n\n const onTransitionEnd = (transition: TransitionEvent) => {\n if (transition.target !== inner || transition.propertyName !== 'transform') return;\n\n inner.removeEventListener('transitionend', onTransitionEnd);\n finish();\n };\n\n inner.addEventListener('transitionend', onTransitionEnd);\n store.scheduleFinalization(id, 300);\n },\n onMove: ({ distance }) => {\n const inner = getInner(wrapper);\n\n if (!inner) return;\n\n inner.style.transition = 'none';\n inner.style.transform = `translateX(${distance}px)`;\n inner.style.opacity = String(Math.max(0, 1 - Math.abs(distance) / 200));\n },\n pointerCapture: false,\n shouldStart: (event) =>\n !event\n .composedPath()\n .some((node) => node instanceof Element && node.matches('button, a, input, select, textarea, ore-button')),\n });\n };\n\n const beginExit = (id: string): void => {\n if (!store || exiting.has(id) || swiping.has(id)) return;\n\n requestAnimationFrame(() => {\n if (!store || exiting.has(id) || swiping.has(id)) return;\n\n const inner = containerRef.value?.querySelector<HTMLElement>(`[data-toast-id=\"${id}\"] .toast-inner`);\n\n if (!inner) {\n store.scheduleFinalization(id, TOAST_EXIT_MS);\n\n return;\n }\n\n void inner.offsetHeight;\n\n const duration = parseFloat(getComputedStyle(inner).transitionDuration) * 1000;\n const exitMs = Number.isFinite(duration) && duration > 0 ? duration : TOAST_EXIT_MS;\n const onTransitionEnd = (event: TransitionEvent) => {\n const wrapper = (event.target as HTMLElement | null)?.closest?.(`[data-toast-id=\"${id}\"]`);\n\n if (!wrapper) return;\n\n clearExitListener(id);\n store?.finalize(id);\n };\n const remove = () => containerRef.value?.removeEventListener('transitionend', onTransitionEnd);\n\n exiting.set(id, remove);\n containerRef.value?.addEventListener('transitionend', onTransitionEnd);\n store.scheduleFinalization(id, exitMs + 50);\n });\n };\n\n const syncControls = (): void => {\n const currentIds = new Set(entries.value.map((entry) => entry.id));\n\n for (const [id, control] of swipeControls) {\n if (!currentIds.has(id)) {\n control.gesture.dispose();\n\n swipeControls.delete(id);\n clearExitListener(id);\n swiping.delete(id);\n }\n }\n\n for (const entry of entries.value) {\n const wrapper = containerRef.value?.querySelector<HTMLElement>(`[data-toast-id=\"${entry.id}\"]`);\n const control = swipeControls.get(entry.id);\n\n if (wrapper && control?.element !== wrapper) {\n control?.gesture.dispose();\n swipeControls.set(entry.id, { element: wrapper, gesture: createToastSwipe(entry.id, wrapper) });\n }\n\n if (entry.phase === 'exiting') beginExit(entry.id);\n }\n };\n\n const scheduleSyncControls = (): void => {\n syncControls();\n\n if (pendingSyncFrame !== undefined) cancelAnimationFrame(pendingSyncFrame);\n\n pendingSyncFrame = requestAnimationFrame(() => {\n pendingSyncFrame = undefined;\n syncControls();\n });\n };\n\n watch(entries, scheduleSyncControls);\n watch(paused, (isPaused) => {\n if (isPaused) store?.pauseTimers();\n else store?.resumeTimers();\n });\n\n const bind = (nextStore: ToastStore): void => {\n if (store === nextStore) return;\n\n unsubscribeEntries();\n\n unsubscribeEvents();\n store = nextStore;\n unsubscribeEntries = store.subscribe((nextEntries) => {\n entries.value = nextEntries;\n });\n unsubscribeEvents = store.subscribeEvents((event) => emit(event.type, { id: event.id }));\n\n if (paused.value) store.pauseTimers();\n };\n\n const renderEntry = (entry: ToastEntry) => {\n const dismiss = () => store?.dismiss(entry.id);\n\n return html`\n <div\n class=\"toast-wrapper\"\n data-toast-id=${entry.id}\n part=\"toast-wrapper\">\n <div class=\"${() => `toast-inner${entry.phase !== 'active' ? ` ${entry.phase}` : ''}`}\" part=\"toast-inner\">\n <ore-alert\n color=${entry.color || (entry.urgency === 'assertive' ? 'error' : 'primary')}\n variant=${entry.variant || 'solid'}\n size=${entry.size || 'md'}\n rounded=${entry.rounded || 'md'}\n ?horizontal=${entry.horizontal}\n heading=${entry.heading || ''}\n ?dismissible=${entry.dismissible}\n @dismiss=${dismiss}>\n ${\n entry.meta\n ? html`\n <span slot=\"meta\">${entry.meta}</span>\n `\n : ''\n }\n ${entry.message} ${renderToastActions(entry, dismiss)}\n </ore-alert>\n </div>\n </div>\n `;\n };\n\n onMounted(() => {\n hostBindings.set(el, {\n bind,\n unbind() {\n unsubscribeEntries();\n unsubscribeEvents();\n store = null;\n entries.value = [];\n },\n });\n\n const boundStore = hostStores.get(el);\n\n if (boundStore) bind(boundStore);\n });\n\n onCleanup(() => {\n hostBindings.get(el)?.unbind();\n\n hostBindings.delete(el);\n\n if (pendingSyncFrame !== undefined) cancelAnimationFrame(pendingSyncFrame);\n\n for (const control of swipeControls.values()) control.gesture.dispose();\n swipeControls.clear();\n for (const id of exiting.keys()) clearExitListener(id);\n });\n\n const urgencyOf = (entry: ToastEntry) => entry.urgency ?? (entry.color === 'error' ? 'assertive' : 'polite');\n const politeEntries = computed(() => entries.value.filter((entry) => urgencyOf(entry) === 'polite'));\n const assertiveEntries = computed(() => entries.value.filter((entry) => urgencyOf(entry) === 'assertive'));\n\n return html`\n <div\n class=\"toast-container\"\n ref=${containerRef}\n @pointerenter=${() => {\n hoverPaused.value = true;\n el.classList.add('hovered');\n }}\n @pointerleave=${() => {\n hoverPaused.value = false;\n el.classList.remove('hovered');\n }}\n @focusin=${() => {\n focusPaused.value = true;\n }}\n @focusout=${() => {\n focusPaused.value = false;\n }}\n part=\"container\">\n <div\n role=\"region\"\n aria-live=\"polite\"\n aria-relevant=\"additions removals\"\n aria-atomic=\"false\"\n aria-label=\"Notifications\"\n class=\"toast-live-region\">\n ${() => politeEntries.value.map(renderEntry)}\n </div>\n <div\n role=\"region\"\n aria-live=\"assertive\"\n aria-relevant=\"additions removals\"\n aria-atomic=\"false\"\n aria-label=\"Critical notifications\"\n class=\"toast-live-region\">\n ${() => assertiveEntries.value.map(renderEntry)}\n </div>\n <slot></slot>\n </div>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n\nexport type ToastServiceConfig = OreToastProps;\n\nexport interface ToastService {\n add(item: ToastItem): string;\n clear(): void;\n configure(config: ToastServiceConfig): void;\n dismiss(id: string): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n error(message: string, opts?: Partial<ToastItem>): string;\n info(message: string, opts?: Partial<ToastItem>): string;\n promise<T>(\n promise: Promise<T>,\n messages: {\n error: string | ((err: unknown) => string);\n loading: string;\n success: string | ((data: T) => string);\n },\n ): Promise<T>;\n success(message: string, opts?: Partial<ToastItem>): string;\n update(id: string, updates: Partial<ToastItem>): void;\n warning(message: string, opts?: Partial<ToastItem>): string;\n [Symbol.dispose](): void;\n}\n\nconst services = new WeakMap<ParentNode, ToastService>();\n\n/**\n * Creates a scoped toast service. The service owns its notification store and\n * binds it to the declarative `<ore-toast>` host inside `root`, creating that\n * host lazily when notifications are first requested.\n */\nexport function createToastService(root: ParentNode = document.body): ToastService {\n const existing = services.get(root);\n\n if (existing) return existing;\n\n const store = new ToastStore();\n const disposalController = new AbortController();\n let disposed = false;\n let configured = false;\n let pendingConfig: ToastServiceConfig | null = null;\n let host: HTMLElement | null = null;\n\n const assertActive = (): boolean => !disposed;\n const rootNode = root as Element | Document | ShadowRoot;\n const getHost = (): HTMLElement | null => {\n if (!assertActive()) return null;\n\n if (host?.isConnected) return host;\n\n host = rootNode.querySelector<HTMLElement>(TOAST_TAG);\n\n if (!host) {\n host = document.createElement(TOAST_TAG);\n\n if (pendingConfig?.position) host.setAttribute('position', pendingConfig.position);\n\n if (pendingConfig?.max != null) host.setAttribute('max', String(pendingConfig.max));\n\n rootNode.appendChild(host);\n }\n\n if (pendingConfig?.max != null) store.setMax(pendingConfig.max);\n else {\n const max = Number(host.getAttribute('max'));\n\n if (Number.isFinite(max) && max > 0) store.setMax(max);\n }\n\n pendingConfig = null;\n configured = true;\n bindToastHost(host, store);\n\n return host;\n };\n const ensureHost = (): boolean => Boolean(getHost());\n\n const service: ToastService = {\n add(item) {\n if (!ensureHost()) return item.id ?? uuid();\n\n return store.add(item);\n },\n\n clear() {\n if (ensureHost()) store.clear();\n },\n\n configure(config) {\n if (configured) {\n warn('toast.configure() called after the container was already created; options ignored.');\n\n return;\n }\n\n pendingConfig = { ...pendingConfig, ...config };\n },\n\n dismiss(id) {\n if (ensureHost()) store.dismiss(id);\n },\n\n get disposalSignal() {\n return disposalController.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n\n disposalController.abort();\n\n store.dispose();\n\n if (services.get(root) === service) services.delete(root);\n },\n\n get disposed() {\n return disposed;\n },\n\n error(message, opts) {\n return service.add({ color: 'error', ...opts, message });\n },\n\n info(message, opts) {\n return service.add({ color: 'info', ...opts, message });\n },\n\n async promise(promise, messages) {\n const id = service.add({ color: 'primary', dismissible: false, duration: 0, message: messages.loading });\n\n try {\n const data = await promise;\n\n service.update(id, {\n color: 'success',\n dismissible: true,\n duration: 5000,\n message: typeof messages.success === 'function' ? messages.success(data) : messages.success,\n });\n\n return data;\n } catch (err) {\n service.update(id, {\n color: 'error',\n dismissible: true,\n duration: 5000,\n message: typeof messages.error === 'function' ? messages.error(err) : messages.error,\n });\n throw err;\n }\n },\n\n success(message, opts) {\n return service.add({ color: 'success', ...opts, message });\n },\n\n [Symbol.dispose]() {\n service.dispose();\n },\n\n update(id, updates) {\n if (ensureHost()) store.update(id, updates);\n },\n\n warning(message, opts) {\n return service.add({ color: 'warning', ...opts, message });\n },\n };\n\n services.set(root, service);\n\n return service;\n}\n\n/** Singleton service backed by a lazily created host in `document.body`. */\nexport const toast: ToastService = createToastService();\n"],"mappings":"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"}
|