@vielzeug/refine 2.4.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -18
- package/dist/accordion-item.cjs.map +1 -1
- package/dist/accordion-item.js.map +1 -1
- package/dist/accordion.cjs.map +1 -1
- package/dist/accordion.js.map +1 -1
- package/dist/async.cjs +1 -1
- package/dist/async.cjs.map +1 -1
- package/dist/async.js +3 -3
- package/dist/async.js.map +1 -1
- package/dist/box.cjs.map +1 -1
- package/dist/box.js.map +1 -1
- package/dist/button.cjs +6 -4
- package/dist/button.cjs.map +1 -1
- package/dist/button.js +11 -9
- package/dist/button.js.map +1 -1
- package/dist/card.cjs +1 -1
- package/dist/card.cjs.map +1 -1
- package/dist/card.js +6 -4
- package/dist/card.js.map +1 -1
- package/dist/checkbox-group.cjs +1 -1
- package/dist/checkbox-group.cjs.map +1 -1
- package/dist/checkbox-group.js +1 -1
- package/dist/checkbox-group.js.map +1 -1
- package/dist/combobox.cjs +2 -2
- package/dist/combobox.cjs.map +1 -1
- package/dist/combobox.js +7 -3
- package/dist/combobox.js.map +1 -1
- package/dist/command-palette.cjs +1 -1
- package/dist/command-palette.cjs.map +1 -1
- package/dist/command-palette.js +6 -2
- package/dist/command-palette.js.map +1 -1
- package/dist/content/card/card.css.cjs +1 -1
- package/dist/content/card/card.css.cjs.map +1 -1
- package/dist/content/card/card.css.js +1 -1
- package/dist/content/card/card.css.js.map +1 -1
- package/dist/content/card/card.d.ts +2 -2
- package/dist/content/card/card.d.ts.map +1 -1
- package/dist/content/pagination/pagination.css.cjs +1 -1
- package/dist/content/pagination/pagination.css.cjs.map +1 -1
- package/dist/content/pagination/pagination.css.js +1 -1
- package/dist/content/pagination/pagination.css.js.map +1 -1
- package/dist/core/field-base.cjs +1 -1
- package/dist/core/field-base.cjs.map +1 -1
- package/dist/core/field-base.d.ts +1 -1
- package/dist/core/field-base.js +1 -1
- package/dist/core/field-base.js.map +1 -1
- package/dist/core/nav.cjs +1 -1
- package/dist/core/nav.cjs.map +1 -1
- package/dist/core/nav.d.ts +4 -1
- package/dist/core/nav.d.ts.map +1 -1
- package/dist/core/nav.js +41 -28
- 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.map +1 -1
- package/dist/core/option-list.js +30 -28
- package/dist/core/option-list.js.map +1 -1
- package/dist/core/slider.cjs +1 -1
- package/dist/core/slider.cjs.map +1 -1
- package/dist/core/slider.js +1 -1
- package/dist/core/slider.js.map +1 -1
- package/dist/core/spinner.cjs +1 -1
- package/dist/core/spinner.cjs.map +1 -1
- package/dist/core/spinner.js +1 -1
- package/dist/core/spinner.js.map +1 -1
- package/dist/custom-elements.json +231 -19
- package/dist/datagrid.cjs +73 -73
- package/dist/datagrid.cjs.map +1 -1
- package/dist/datagrid.js +331 -332
- package/dist/datagrid.js.map +1 -1
- package/dist/disclosure/accordion/accordion.css.cjs +1 -1
- package/dist/disclosure/accordion/accordion.css.cjs.map +1 -1
- package/dist/disclosure/accordion/accordion.css.js +1 -1
- package/dist/disclosure/accordion/accordion.css.js.map +1 -1
- package/dist/disclosure/accordion/accordion.d.ts +5 -5
- package/dist/disclosure/accordion/accordion.d.ts.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.cjs +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.cjs.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.js +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.js.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.d.ts +3 -3
- package/dist/disclosure/accordion-item/accordion-item.d.ts.map +1 -1
- package/dist/disclosure/tab-item/tab-item.css.cjs +1 -1
- package/dist/disclosure/tab-item/tab-item.css.cjs.map +1 -1
- package/dist/disclosure/tab-item/tab-item.css.js +1 -1
- package/dist/disclosure/tab-item/tab-item.css.js.map +1 -1
- package/dist/disclosure/tab-item/tab-item.d.ts +3 -3
- package/dist/disclosure/tab-item/tab-item.d.ts.map +1 -1
- package/dist/disclosure/tabs/tabs.css.cjs +1 -1
- package/dist/disclosure/tabs/tabs.css.cjs.map +1 -1
- package/dist/disclosure/tabs/tabs.css.js +1 -1
- package/dist/disclosure/tabs/tabs.css.js.map +1 -1
- package/dist/disclosure/tabs/tabs.d.ts +5 -5
- package/dist/disclosure/tabs/tabs.d.ts.map +1 -1
- package/dist/drawer.cjs +1 -1
- package/dist/drawer.cjs.map +1 -1
- package/dist/drawer.js +1 -1
- 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 +1 -1
- package/dist/feedback/toast/toast.d.ts.map +1 -1
- package/dist/file-input.cjs +1 -1
- package/dist/file-input.cjs.map +1 -1
- package/dist/file-input.js +2 -2
- package/dist/file-input.js.map +1 -1
- package/dist/frameworks/elements.d.ts +4 -86
- package/dist/frameworks/react.d.ts +5 -4
- package/dist/frameworks/vue.d.ts +4 -4
- package/dist/grid.cjs +2 -2
- package/dist/grid.cjs.map +1 -1
- package/dist/grid.js +21 -18
- package/dist/grid.js.map +1 -1
- package/dist/inputs/combobox/combobox.d.ts +12 -1
- package/dist/inputs/combobox/combobox.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid.d.ts +24 -32
- package/dist/inputs/datagrid/datagrid.d.ts.map +1 -1
- package/dist/inputs/date-picker/date-picker.css.cjs +1 -1
- package/dist/inputs/date-picker/date-picker.css.cjs.map +1 -1
- package/dist/inputs/date-picker/date-picker.css.js +1 -1
- package/dist/inputs/date-picker/date-picker.css.js.map +1 -1
- package/dist/inputs/file-input/file-input-upload.cjs +1 -1
- package/dist/inputs/file-input/file-input-upload.cjs.map +1 -1
- package/dist/inputs/file-input/file-input-upload.js +1 -1
- package/dist/inputs/file-input/file-input-upload.js.map +1 -1
- package/dist/inputs/index.d.ts +2 -3
- package/dist/inputs/index.d.ts.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/shared/field-binding.cjs +1 -1
- package/dist/inputs/shared/field-binding.cjs.map +1 -1
- package/dist/inputs/shared/field-binding.js +6 -6
- package/dist/inputs/shared/field-binding.js.map +1 -1
- package/dist/layout/box/box.css.cjs +1 -1
- package/dist/layout/box/box.css.cjs.map +1 -1
- package/dist/layout/box/box.css.js +1 -1
- package/dist/layout/box/box.css.js.map +1 -1
- package/dist/layout/box/box.d.ts +3 -3
- package/dist/layout/box/box.d.ts.map +1 -1
- package/dist/layout/grid/grid.d.ts.map +1 -1
- package/dist/layout/navbar/navbar.css.cjs +1 -1
- package/dist/layout/navbar/navbar.css.cjs.map +1 -1
- package/dist/layout/navbar/navbar.css.js +1 -1
- package/dist/layout/navbar/navbar.css.js.map +1 -1
- package/dist/layout/navbar/navbar.d.ts +7 -1
- package/dist/layout/navbar/navbar.d.ts.map +1 -1
- package/dist/layout/sidebar/sidebar-item.css.cjs +1 -1
- package/dist/layout/sidebar/sidebar-item.css.cjs.map +1 -1
- package/dist/layout/sidebar/sidebar-item.css.js +1 -1
- package/dist/layout/sidebar/sidebar-item.css.js.map +1 -1
- package/dist/layout/sidebar/sidebar.css.cjs +1 -1
- package/dist/layout/sidebar/sidebar.css.cjs.map +1 -1
- package/dist/layout/sidebar/sidebar.css.js +1 -1
- package/dist/layout/sidebar/sidebar.css.js.map +1 -1
- package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
- package/dist/menu.cjs +1 -1
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.js +6 -6
- package/dist/menu.js.map +1 -1
- package/dist/navbar.cjs +10 -10
- package/dist/navbar.cjs.map +1 -1
- package/dist/navbar.js +99 -95
- package/dist/navbar.js.map +1 -1
- package/dist/navigation-menu.cjs +1 -1
- package/dist/navigation-menu.cjs.map +1 -1
- package/dist/navigation-menu.js +1 -1
- package/dist/navigation-menu.js.map +1 -1
- package/dist/number-input.cjs +1 -1
- package/dist/number-input.cjs.map +1 -1
- package/dist/number-input.js +1 -1
- package/dist/number-input.js.map +1 -1
- package/dist/overlay/popover/popover.d.ts.map +1 -1
- package/dist/overlay/tooltip/tooltip.d.ts.map +1 -1
- package/dist/popover.cjs +1 -1
- package/dist/popover.cjs.map +1 -1
- package/dist/popover.js +6 -6
- package/dist/popover.js.map +1 -1
- package/dist/radio-group.cjs +1 -1
- package/dist/radio-group.cjs.map +1 -1
- package/dist/radio-group.js +1 -1
- 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 +5 -5
- 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 +241 -224
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +297 -280
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +251 -234
- package/dist/refine.js.map +1 -1
- package/dist/shared/types.d.ts +0 -2
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/sidebar.cjs +1 -1
- package/dist/sidebar.cjs.map +1 -1
- package/dist/sidebar.js +83 -83
- package/dist/sidebar.js.map +1 -1
- package/dist/skeleton.cjs +1 -1
- package/dist/skeleton.cjs.map +1 -1
- package/dist/skeleton.js +11 -9
- package/dist/skeleton.js.map +1 -1
- package/dist/slider.cjs +1 -1
- package/dist/slider.cjs.map +1 -1
- package/dist/slider.js +20 -20
- package/dist/slider.js.map +1 -1
- package/dist/stats.cjs +1 -1
- package/dist/stats.cjs.map +1 -1
- package/dist/stats.js +2 -2
- package/dist/stats.js.map +1 -1
- package/dist/styles/mixins/field-variants.css.cjs +15 -0
- package/dist/styles/mixins/field-variants.css.cjs.map +1 -1
- package/dist/styles/mixins/field-variants.css.d.ts.map +1 -1
- package/dist/styles/mixins/field-variants.css.js +15 -0
- package/dist/styles/mixins/field-variants.css.js.map +1 -1
- package/dist/styles/mixins/variants.css.cjs +4 -4
- package/dist/styles/mixins/variants.css.cjs.map +1 -1
- package/dist/styles/mixins/variants.css.js +4 -4
- package/dist/styles/mixins/variants.css.js.map +1 -1
- package/dist/tab-item.cjs.map +1 -1
- package/dist/tab-item.js.map +1 -1
- package/dist/tabs.cjs.map +1 -1
- package/dist/tabs.js.map +1 -1
- package/dist/toast.cjs +8 -8
- package/dist/toast.cjs.map +1 -1
- package/dist/toast.js +32 -33
- package/dist/toast.js.map +1 -1
- package/dist/tooltip.cjs +1 -1
- package/dist/tooltip.cjs.map +1 -1
- package/dist/tooltip.js +2 -2
- package/dist/tooltip.js.map +1 -1
- package/dist/types/elements.d.ts +207 -0
- package/package.json +13 -13
package/dist/stats.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats.js","names":[],"sources":["../src/content/stats/stats.ts"],"sourcesContent":["import { bind, define, html, prop, useSlots } from '@vielzeug/ore';\nimport { disablableBundle, loadableBundle, roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n colorThemeMixin,\n forcedColorsMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport type { ComponentSize, RoundedSize, ThemeColor } from '../../types';\nimport componentStyles from './stats.css?inline';\n\nexport type StatsTrendDirection = 'down' | 'neutral' | 'up';\nexport type StatsVariant = 'outlined' | 'plain' | 'solid';\n\nexport type OreStatsProps = {\n color?: ThemeColor;\n description?: string;\n disabled?: boolean;\n label?: string;\n loading?: boolean;\n rounded?: RoundedSize;\n size?: ComponentSize;\n trend?: string;\n 'trend-direction'?: StatsTrendDirection;\n value?: string;\n variant?: StatsVariant;\n};\n\n/**\n * A focused metric card for a label, primary value, supporting context, trend, icon, and optional visual.\n *\n * @element ore-stats\n *\n * @attr {string} label - Metric label\n * @attr {string} value - Primary metric value\n * @attr {string} description - Supporting context\n * @attr {string} trend - Trend text\n * @attr {string} trend-direction - Trend direction: 'up' | 'down' | 'neutral'\n * @attr {string} variant - Visual variant: 'outlined' | 'plain' | 'solid'\n * @attr {string} color - Semantic theme color\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius size\n * @attr {boolean} loading - Show loading state\n * @attr {boolean} disabled - Show unavailable state\n *\n * @slot icon - Leading metric icon\n * @slot label - Replaces label text\n * @slot value - Replaces value text\n * @slot visual - Compact chart, progress, or other metric visual\n * @slot trend - Replaces trend text\n * @slot description - Replaces supporting context\n *\n * @cssprop --stats-bg - Card background\n * @cssprop --stats-color - Card foreground color\n * @cssprop --stats-border-color - Card border color\n * @cssprop --stats-radius - Card border radius\n * @cssprop --stats-padding - Card padding\n * @cssprop --stats-min-height - Minimum card height\n * @cssprop --stats-value-size - Primary value font size\n * @cssprop --stats-icon-bg - Icon surface background\n * @cssprop --stats-trend-up - Positive trend color\n * @cssprop --stats-trend-down - Negative trend color\n *\n * @part card - Card surface\n * @part loading - Loading indicator\n * @part header - Label and icon row\n * @part icon - Icon container\n * @part label - Label container\n * @part body - Value and visual row\n * @part value - Primary value container\n * @part visual - Visual slot container\n * @part footer - Trend and description row\n * @part trend - Trend container\n * @part description - Description container\n *\n * @example\n * ```html\n * <ore-stats label=\"Revenue\" value=\"$42,800\" trend=\"+12%\" trend-direction=\"up\">\n * <ore-icon slot=\"icon\" name=\"chart-line\"></ore-icon>\n * </ore-stats>\n * ```\n */\nexport const STATS_TAG = 'ore-stats' as const;\ndefine<OreStatsProps>(STATS_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...loadableBundle,\n ...roundableBundle,\n description: prop.string(),\n label: prop.string(),\n trend: prop.string(),\n 'trend-direction': prop.oneOf<StatsTrendDirection>(['up', 'down', 'neutral'], 'neutral'),\n value: prop.string(),\n variant: prop.oneOf<StatsVariant>(['outlined', 'plain', 'solid'], 'outlined'),\n },\n setup(props) {\n const slots = useSlots<'description' | 'icon' | 'label' | 'trend' | 'value' | 'visual'>();\n const hasDescription = () => Boolean(props.description.value) || slots.has('description').value;\n const hasIcon = () => slots.has('icon').value;\n const hasLabel = () => Boolean(props.label.value) || slots.has('label').value;\n const hasTrend = () => Boolean(props.trend.value) || slots.has('trend').value;\n const hasValue = () => Boolean(props.value.value) || slots.has('value').value;\n const hasVisual = () => slots.has('visual').value;\n\n bind({\n attr: {\n
|
|
1
|
+
{"version":3,"file":"stats.js","names":[],"sources":["../src/content/stats/stats.ts"],"sourcesContent":["import { bind, define, html, prop, useSlots } from '@vielzeug/ore';\nimport { disablableBundle, loadableBundle, roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n colorThemeMixin,\n forcedColorsMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport type { ComponentSize, RoundedSize, ThemeColor } from '../../types';\nimport componentStyles from './stats.css?inline';\n\nexport type StatsTrendDirection = 'down' | 'neutral' | 'up';\nexport type StatsVariant = 'outlined' | 'plain' | 'solid';\n\nexport type OreStatsProps = {\n color?: ThemeColor;\n description?: string;\n disabled?: boolean;\n label?: string;\n loading?: boolean;\n rounded?: RoundedSize;\n size?: ComponentSize;\n trend?: string;\n 'trend-direction'?: StatsTrendDirection;\n value?: string;\n variant?: StatsVariant;\n};\n\n/**\n * A focused metric card for a label, primary value, supporting context, trend, icon, and optional visual.\n *\n * @element ore-stats\n *\n * @attr {string} label - Metric label\n * @attr {string} value - Primary metric value\n * @attr {string} description - Supporting context\n * @attr {string} trend - Trend text\n * @attr {string} trend-direction - Trend direction: 'up' | 'down' | 'neutral'\n * @attr {string} variant - Visual variant: 'outlined' | 'plain' | 'solid'\n * @attr {string} color - Semantic theme color\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius size\n * @attr {boolean} loading - Show loading state\n * @attr {boolean} disabled - Show unavailable state\n *\n * @slot icon - Leading metric icon\n * @slot label - Replaces label text\n * @slot value - Replaces value text\n * @slot visual - Compact chart, progress, or other metric visual\n * @slot trend - Replaces trend text\n * @slot description - Replaces supporting context\n *\n * @cssprop --stats-bg - Card background\n * @cssprop --stats-color - Card foreground color\n * @cssprop --stats-border-color - Card border color\n * @cssprop --stats-radius - Card border radius\n * @cssprop --stats-padding - Card padding\n * @cssprop --stats-min-height - Minimum card height\n * @cssprop --stats-value-size - Primary value font size\n * @cssprop --stats-icon-bg - Icon surface background\n * @cssprop --stats-trend-up - Positive trend color\n * @cssprop --stats-trend-down - Negative trend color\n *\n * @part card - Card surface\n * @part loading - Loading indicator\n * @part header - Label and icon row\n * @part icon - Icon container\n * @part label - Label container\n * @part body - Value and visual row\n * @part value - Primary value container\n * @part visual - Visual slot container\n * @part footer - Trend and description row\n * @part trend - Trend container\n * @part description - Description container\n *\n * @example\n * ```html\n * <ore-stats label=\"Revenue\" value=\"$42,800\" trend=\"+12%\" trend-direction=\"up\">\n * <ore-icon slot=\"icon\" name=\"chart-line\"></ore-icon>\n * </ore-stats>\n * ```\n */\nexport const STATS_TAG = 'ore-stats' as const;\ndefine<OreStatsProps>(STATS_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...loadableBundle,\n ...roundableBundle,\n description: prop.string(),\n label: prop.string(),\n trend: prop.string(),\n 'trend-direction': prop.oneOf<StatsTrendDirection>(['up', 'down', 'neutral'], 'neutral'),\n value: prop.string(),\n variant: prop.oneOf<StatsVariant>(['outlined', 'plain', 'solid'], 'outlined'),\n },\n setup(props) {\n const slots = useSlots<'description' | 'icon' | 'label' | 'trend' | 'value' | 'visual'>();\n const hasDescription = () => Boolean(props.description.value) || slots.has('description').value;\n const hasIcon = () => slots.has('icon').value;\n const hasLabel = () => Boolean(props.label.value) || slots.has('label').value;\n const hasTrend = () => Boolean(props.trend.value) || slots.has('trend').value;\n const hasValue = () => Boolean(props.value.value) || slots.has('value').value;\n const hasVisual = () => slots.has('visual').value;\n\n bind({\n attr: {\n 'aria-busy': () => String(Boolean(props.loading.value)),\n 'aria-disabled': () => String(Boolean(props.disabled.value)),\n },\n });\n\n return html`\n <article class=\"stats\" part=\"card\">\n <span class=\"loading\" part=\"loading\" aria-hidden=\"true\"></span>\n <header class=\"header\" part=\"header\" ?hidden=${() => !hasIcon() && !hasLabel()}>\n <span class=\"icon\" part=\"icon\" ?hidden=${() => !hasIcon()}><slot name=\"icon\"></slot></span>\n <span class=\"label\" part=\"label\" ?hidden=${() => !hasLabel()}><slot name=\"label\">${props.label}</slot></span>\n </header>\n <div class=\"body\" part=\"body\" ?hidden=${() => !hasValue() && !hasVisual()}>\n <strong class=\"value\" part=\"value\" ?hidden=${() => !hasValue()}><slot name=\"value\">${props.value}</slot></strong>\n <span class=\"visual\" part=\"visual\" ?hidden=${() => !hasVisual()}><slot name=\"visual\"></slot></span>\n </div>\n <footer class=\"footer\" part=\"footer\" ?hidden=${() => !hasTrend() && !hasDescription()}>\n <span class=\"trend\" part=\"trend\" data-direction=${props['trend-direction']} ?hidden=${() => !hasTrend()}><slot name=\"trend\">${props.trend}</slot></span>\n <span class=\"description\" part=\"description\" ?hidden=${() => !hasDescription()}><slot name=\"description\">${props.description}</slot></span>\n </footer>\n </article>\n `;\n },\n styles: [\n colorThemeMixin,\n roundedVariantMixin,\n sizeVariantMixin({\n lg: {\n '--_icon-size': 'var(--size-10)',\n '--_min-height': 'var(--stats-min-height, var(--size-36))',\n '--_value-size': 'var(--stats-value-size, var(--text-3xl))',\n gap: 'var(--size-4)',\n padding: 'var(--stats-padding, var(--size-5))',\n },\n md: {\n '--_icon-size': 'var(--size-9)',\n '--_min-height': 'var(--stats-min-height, var(--size-32))',\n '--_value-size': 'var(--stats-value-size, var(--text-2xl))',\n gap: 'var(--size-3)',\n padding: 'var(--stats-padding, var(--size-4))',\n },\n sm: {\n '--_icon-size': 'var(--size-8)',\n '--_min-height': 'var(--stats-min-height, var(--size-24))',\n '--_value-size': 'var(--stats-value-size, var(--text-xl))',\n gap: 'var(--size-2)',\n padding: 'var(--stats-padding, var(--size-3))',\n },\n }),\n reducedMotionMixin,\n forcedColorsMixin,\n componentStyles,\n ],\n});\n"],"mappings":";;;;;;;AAmFA,IAAa,IAAY;AACzB,EAAsB,GAAW;CAC/B,OAAO;EACL,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,aAAa,EAAK,OAAO;EACzB,OAAO,EAAK,OAAO;EACnB,OAAO,EAAK,OAAO;EACnB,mBAAmB,EAAK,MAA2B;GAAC;GAAM;GAAQ;EAAS,GAAG,SAAS;EACvF,OAAO,EAAK,OAAO;EACnB,SAAS,EAAK,MAAoB;GAAC;GAAY;GAAS;EAAO,GAAG,UAAU;CAC9E;CACA,MAAM,GAAO;EACX,IAAM,IAAQ,EAA0E,GAClF,UAAuB,EAAQ,EAAM,YAAY,SAAU,EAAM,IAAI,aAAa,CAAC,CAAC,OACpF,UAAgB,EAAM,IAAI,MAAM,CAAC,CAAC,OAClC,UAAiB,EAAQ,EAAM,MAAM,SAAU,EAAM,IAAI,OAAO,CAAC,CAAC,OAClE,UAAiB,EAAQ,EAAM,MAAM,SAAU,EAAM,IAAI,OAAO,CAAC,CAAC,OAClE,UAAiB,EAAQ,EAAM,MAAM,SAAU,EAAM,IAAI,OAAO,CAAC,CAAC,OAClE,UAAkB,EAAM,IAAI,QAAQ,CAAC,CAAC;EAS5C,OAPA,EAAK,EACH,MAAM;GACJ,mBAAmB,OAAO,EAAQ,EAAM,QAAQ,KAAM;GACtD,uBAAuB,OAAO,EAAQ,EAAM,SAAS,KAAM;EAC7D,EACF,CAAC,GAEM,CAAI;;;6DAG8C,CAAC,EAAQ,KAAK,CAAC,EAAS,EAAE;yDAC9B,CAAC,EAAQ,EAAE;2DACT,CAAC,EAAS,EAAE,sBAAsB,EAAM,MAAM;;sDAEnD,CAAC,EAAS,KAAK,CAAC,EAAU,EAAE;6DACrB,CAAC,EAAS,EAAE,sBAAsB,EAAM,MAAM;6DAC9C,CAAC,EAAU,EAAE;;6DAEb,CAAC,EAAS,KAAK,CAAC,EAAe,EAAE;4DAClC,EAAM,mBAAmB,iBAAiB,CAAC,EAAS,EAAE,sBAAsB,EAAM,MAAM;uEAC7E,CAAC,EAAe,EAAE,4BAA4B,EAAM,YAAY;;;;CAIrI;CACA,QAAQ;EACN;EACA;EACA,EAAiB;GACf,IAAI;IACF,gBAAgB;IAChB,iBAAiB;IACjB,iBAAiB;IACjB,KAAK;IACL,SAAS;GACX;GACA,IAAI;IACF,gBAAgB;IAChB,iBAAiB;IACjB,iBAAiB;IACjB,KAAK;IACL,SAAS;GACX;GACA,IAAI;IACF,gBAAgB;IAChB,iBAAiB;IACjB,iBAAiB;IACjB,KAAK;IACL,SAAS;GACX;EACF,CAAC;EACD;EACA;EACA;CACF;AACF,CAAC"}
|
|
@@ -80,6 +80,21 @@ let e=require("@vielzeug/ore");var t=({accentVar:t=`--_theme-focus`,container:n,
|
|
|
80
80
|
:host([variant='ghost']:not([disabled])) ${n}:hover {
|
|
81
81
|
background: var(--${i}-hover-bg, var(--color-contrast-100));
|
|
82
82
|
}
|
|
83
|
+
|
|
84
|
+
/* frost */
|
|
85
|
+
:host([variant='frost']) {
|
|
86
|
+
--_bg: var(--color-canvas);
|
|
87
|
+
--_border-color: var(--${i}-border-color, var(--color-contrast-300));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
:host([variant='frost']) ${n} {
|
|
91
|
+
box-shadow: none;
|
|
92
|
+
backdrop-filter: blur(var(--blur-md));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
:host([variant='frost']:not([disabled])) ${n}:hover {
|
|
96
|
+
background: var(--${i}-hover-bg, var(--color-contrast-100));
|
|
97
|
+
}
|
|
83
98
|
}
|
|
84
99
|
`;exports.fieldVariantMixin=t;
|
|
85
100
|
//# sourceMappingURL=field-variants.css.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-variants.css.cjs","names":[],"sources":["../../../src/styles/mixins/field-variants.css.ts"],"sourcesContent":["import { css } from '@vielzeug/ore';\n\nexport type FieldVariantMixinOptions = {\n /**\n * The theme accent custom property `flat`/`bordered` mix into their border color. Defaults to\n * `--_theme-focus` (from `colorThemeMixin`). Override for a component with its own distinct\n * focus/accent concept instead of the shared theme token (`ore-otp-input`'s `--_cell-focus-\n * border`, which isn't theme-color-driven the same way).\n */\n accentVar?: string;\n /** Selector for the element that gets the variant's background/border/shadow — the visual \"box\". */\n container: string;\n /**\n * Selector for the text-bearing element the `bordered` variant tints to the theme color.\n * Defaults to `container` for components where the box and the text-bearing element are the\n * same node (`ore-input`/`ore-textarea`'s `.field`). Pass a distinct selector when they're\n * split (`ore-message-composer`'s `.composer` card vs its bare `.field` textarea).\n */\n text?: string;\n /** CSS custom-property namespace, e.g. `'input'` → `--input-bg`, `--input-hover-bg`, ... */\n tokenPrefix: string;\n};\n\n/**\n * The `solid`/`flat`/`bordered`/`outline`/`ghost` variant switch shared by every text-entry\n * field component (`ore-input`, `ore-textarea`, `ore-message-composer`, `ore-otp-input`) —\n * previously hand-duplicated across them, mostly identically but not quite: this version also\n * uniformly adds a `:not([disabled])` guard to the `flat` variant's hover/focus rules (only\n * `ore-message-composer` had it) and the `bordered` variant's text/placeholder/caret tint\n * (`ore-input`/`ore-otp-input` were missing pieces of it) — clearly-unintentional per-component\n * gaps, not deliberate differences, so this mixin converges on the more complete behavior for\n * every consumer rather than picking one component's version arbitrarily.\n *\n * Deliberately does **not** touch `:focus-within` box-shadow for `solid`/`outline`/`ghost` —\n * every current consumer already covers that with its own single `:host(:not([disabled],\n * [variant='flat'], ...)) ${container}:focus-within` catch-all in its *own* base layer, so a\n * copy here would be pure redundancy for them, and actively wrong for `ore-otp-input`: its\n * `.cell` is the focusable element itself (not a wrapper), so `:focus-within` matches on every\n * keystroke, and it already renders its own bespoke ring (colored by `accentVar`, not\n * `--_theme-shadow`) uniformly across all variants in its base layer — this mixin adding a\n * second, generic one would fight it depending on layer/rule order.\n *\n * Each component keeps its own `:host(:not([variant], ...)) ${container}:hover/:focus-within`\n * base-state rules and any variant beyond this shared set (e.g. `ore-input`'s extra `'text'`\n * variant) in its own stylesheet — this mixin only covers the five variants every consumer has\n * in common.\n *\n * @example\n * ```ts\n * // ore-textarea / ore-input (box and text-bearing element are the same `.field`)\n * styles: [..., fieldVariantMixin({ container: '.field', tokenPrefix: 'textarea' }), componentStyles]\n *\n * // ore-message-composer (card `.composer` vs its own bare `.field` textarea)\n * styles: [\n * ...,\n * fieldVariantMixin({ container: '.composer', text: '.field', tokenPrefix: 'message-composer' }),\n * componentStyles,\n * ]\n *\n * // ore-otp-input (own per-cell accent instead of the shared theme-focus token)\n * styles: [\n * ...,\n * fieldVariantMixin({ accentVar: '--_cell-focus-border', container: '.cell', tokenPrefix: 'otp-cell' }),\n * componentStyles,\n * ]\n * ```\n */\nexport const fieldVariantMixin = ({\n accentVar = '--_theme-focus',\n container,\n text = container,\n tokenPrefix,\n}: FieldVariantMixinOptions) => css`\n @layer refine.variants {\n /* Solid (default) */\n :host(:not([variant])),\n :host([variant='solid']) {\n --_bg: var(--${tokenPrefix}-bg, var(--color-contrast-50));\n --_border-color: var(--${tokenPrefix}-border-color, var(--color-contrast-300));\n }\n\n :host(:not([variant])) ${container}, :host([variant='solid']) ${container} {\n box-shadow: var(--shadow-2xs);\n }\n\n /* Flat */\n :host([variant='flat']) {\n --_bg: var(--${tokenPrefix}-bg, var(--color-contrast-100));\n --_border-color: var(--${tokenPrefix}-border-color, color-mix(in oklch, var(${accentVar}) 20%, transparent));\n }\n\n :host([variant='flat']) ${container} {\n box-shadow: var(--inset-shadow-2xs);\n }\n\n :host([variant='flat']:not([disabled])) ${container}:hover {\n background: var(--${tokenPrefix}-hover-bg, color-mix(in oklch, var(--_theme-base) 6%, var(--color-contrast-100)));\n border-color: var(\n --${tokenPrefix}-hover-border-color,\n color-mix(in oklch, var(--_theme-base) 35%, var(--color-contrast-300))\n );\n }\n\n :host([variant='flat']:not([disabled])) ${container}:focus-within {\n background: var(--${tokenPrefix}-focus-bg, color-mix(in oklch, var(--_theme-base) 8%, var(--color-canvas)));\n border-color: var(--${tokenPrefix}-focus-border-color, color-mix(in oklch, var(${accentVar}) 60%, transparent));\n box-shadow: var(--_theme-shadow);\n }\n\n /* Bordered */\n :host([variant='bordered']) {\n --_bg: var(--${tokenPrefix}-bg, var(--_theme-backdrop));\n --_border-color: var(--${tokenPrefix}-border-color, color-mix(in oklch, var(${accentVar}) 70%, transparent));\n }\n\n :host([variant='bordered']:not([disabled])) ${container}:hover {\n border-color: var(--${tokenPrefix}-hover-border-color, var(${accentVar}));\n }\n\n :host([variant='bordered']) ${text} {\n color: var(--_theme-base);\n caret-color: var(--_theme-base);\n }\n\n :host([variant='bordered']) ${text}::placeholder {\n color: var(--${tokenPrefix}-placeholder-color, color-mix(in oklch, var(--_theme-base) 45%, transparent));\n }\n\n /* Outline — border always visible, so unlike the other variants it can't lean on a\n per-component \\`:host{}\\` base fallback for \\`--_border-color\\` that may or may not exist\n (\\`ore-message-composer\\` has none — every other rule here sets both custom properties, so\n its base layer omits a default on the assumption that always holds). Set explicitly. */\n :host([variant='outline']) {\n --_bg: var(--${tokenPrefix}-bg, transparent);\n --_border-color: var(--${tokenPrefix}-border-color, var(--color-contrast-300));\n }\n\n :host([variant='outline']) ${container} {\n box-shadow: none;\n }\n\n /* Ghost */\n :host([variant='ghost']) {\n --_bg: var(--${tokenPrefix}-bg, transparent);\n --_border-color: var(--${tokenPrefix}-border-color, transparent);\n }\n\n :host([variant='ghost']) ${container} {\n box-shadow: none;\n }\n\n :host([variant='ghost']:not([disabled])) ${container}:hover {\n background: var(--${tokenPrefix}-hover-bg, var(--color-contrast-100));\n }\n }\n`;\n"],"mappings":"+BAmEA,IAAa,GAAqB,CAChC,YAAY,iBACZ,YACA,OAAO,EACP,iBAC8B,EAAA,GAAG;;;;;qBAKd,EAAY;+BACF,EAAY;;;6BAGd,EAAU,6BAA6B,EAAU;;;;;;qBAMzD,EAAY;+BACF,EAAY,yCAAyC,EAAU;;;8BAGhE,EAAU;;;;8CAIM,EAAU;0BAC9B,EAAY;;YAE1B,EAAY;;;;;8CAKsB,EAAU;0BAC9B,EAAY;4BACV,EAAY,+CAA+C,EAAU;;;;;;qBAM5E,EAAY;+BACF,EAAY,yCAAyC,EAAU;;;kDAG5C,EAAU;4BAChC,EAAY,2BAA2B,EAAU;;;kCAG3C,EAAK;;;;;kCAKL,EAAK;qBAClB,EAAY;;;;;;;;qBAQZ,EAAY;+BACF,EAAY;;;iCAGV,EAAU;;;;;;qBAMtB,EAAY;+BACF,EAAY;;;+BAGZ,EAAU;;;;+CAIM,EAAU;0BAC/B,EAAY"}
|
|
1
|
+
{"version":3,"file":"field-variants.css.cjs","names":[],"sources":["../../../src/styles/mixins/field-variants.css.ts"],"sourcesContent":["import { css } from '@vielzeug/ore';\n\nexport type FieldVariantMixinOptions = {\n /**\n * The theme accent custom property `flat`/`bordered` mix into their border color. Defaults to\n * `--_theme-focus` (from `colorThemeMixin`). Override for a component with its own distinct\n * focus/accent concept instead of the shared theme token (`ore-otp-input`'s `--_cell-focus-\n * border`, which isn't theme-color-driven the same way).\n */\n accentVar?: string;\n /** Selector for the element that gets the variant's background/border/shadow — the visual \"box\". */\n container: string;\n /**\n * Selector for the text-bearing element the `bordered` variant tints to the theme color.\n * Defaults to `container` for components where the box and the text-bearing element are the\n * same node (`ore-input`/`ore-textarea`'s `.field`). Pass a distinct selector when they're\n * split (`ore-message-composer`'s `.composer` card vs its bare `.field` textarea).\n */\n text?: string;\n /** CSS custom-property namespace, e.g. `'input'` → `--input-bg`, `--input-hover-bg`, ... */\n tokenPrefix: string;\n};\n\n/**\n * The `solid`/`flat`/`bordered`/`outline`/`ghost` variant switch shared by every text-entry\n * field component (`ore-input`, `ore-textarea`, `ore-message-composer`, `ore-otp-input`) —\n * previously hand-duplicated across them, mostly identically but not quite: this version also\n * uniformly adds a `:not([disabled])` guard to the `flat` variant's hover/focus rules (only\n * `ore-message-composer` had it) and the `bordered` variant's text/placeholder/caret tint\n * (`ore-input`/`ore-otp-input` were missing pieces of it) — clearly-unintentional per-component\n * gaps, not deliberate differences, so this mixin converges on the more complete behavior for\n * every consumer rather than picking one component's version arbitrarily.\n *\n * Deliberately does **not** touch `:focus-within` box-shadow for `solid`/`outline`/`ghost` —\n * every current consumer already covers that with its own single `:host(:not([disabled],\n * [variant='flat'], ...)) ${container}:focus-within` catch-all in its *own* base layer, so a\n * copy here would be pure redundancy for them, and actively wrong for `ore-otp-input`: its\n * `.cell` is the focusable element itself (not a wrapper), so `:focus-within` matches on every\n * keystroke, and it already renders its own bespoke ring (colored by `accentVar`, not\n * `--_theme-shadow`) uniformly across all variants in its base layer — this mixin adding a\n * second, generic one would fight it depending on layer/rule order.\n *\n * Each component keeps its own `:host(:not([variant], ...)) ${container}:hover/:focus-within`\n * base-state rules and any variant beyond this shared set (e.g. `ore-input`'s extra `'text'`\n * variant) in its own stylesheet — this mixin only covers the five variants every consumer has\n * in common.\n *\n * @example\n * ```ts\n * // ore-textarea / ore-input (box and text-bearing element are the same `.field`)\n * styles: [..., fieldVariantMixin({ container: '.field', tokenPrefix: 'textarea' }), componentStyles]\n *\n * // ore-message-composer (card `.composer` vs its own bare `.field` textarea)\n * styles: [\n * ...,\n * fieldVariantMixin({ container: '.composer', text: '.field', tokenPrefix: 'message-composer' }),\n * componentStyles,\n * ]\n *\n * // ore-otp-input (own per-cell accent instead of the shared theme-focus token)\n * styles: [\n * ...,\n * fieldVariantMixin({ accentVar: '--_cell-focus-border', container: '.cell', tokenPrefix: 'otp-cell' }),\n * componentStyles,\n * ]\n * ```\n */\nexport const fieldVariantMixin = ({\n accentVar = '--_theme-focus',\n container,\n text = container,\n tokenPrefix,\n}: FieldVariantMixinOptions) => css`\n @layer refine.variants {\n /* Solid (default) */\n :host(:not([variant])),\n :host([variant='solid']) {\n --_bg: var(--${tokenPrefix}-bg, var(--color-contrast-50));\n --_border-color: var(--${tokenPrefix}-border-color, var(--color-contrast-300));\n }\n\n :host(:not([variant])) ${container}, :host([variant='solid']) ${container} {\n box-shadow: var(--shadow-2xs);\n }\n\n /* Flat */\n :host([variant='flat']) {\n --_bg: var(--${tokenPrefix}-bg, var(--color-contrast-100));\n --_border-color: var(--${tokenPrefix}-border-color, color-mix(in oklch, var(${accentVar}) 20%, transparent));\n }\n\n :host([variant='flat']) ${container} {\n box-shadow: var(--inset-shadow-2xs);\n }\n\n :host([variant='flat']:not([disabled])) ${container}:hover {\n background: var(--${tokenPrefix}-hover-bg, color-mix(in oklch, var(--_theme-base) 6%, var(--color-contrast-100)));\n border-color: var(\n --${tokenPrefix}-hover-border-color,\n color-mix(in oklch, var(--_theme-base) 35%, var(--color-contrast-300))\n );\n }\n\n :host([variant='flat']:not([disabled])) ${container}:focus-within {\n background: var(--${tokenPrefix}-focus-bg, color-mix(in oklch, var(--_theme-base) 8%, var(--color-canvas)));\n border-color: var(--${tokenPrefix}-focus-border-color, color-mix(in oklch, var(${accentVar}) 60%, transparent));\n box-shadow: var(--_theme-shadow);\n }\n\n /* Bordered */\n :host([variant='bordered']) {\n --_bg: var(--${tokenPrefix}-bg, var(--_theme-backdrop));\n --_border-color: var(--${tokenPrefix}-border-color, color-mix(in oklch, var(${accentVar}) 70%, transparent));\n }\n\n :host([variant='bordered']:not([disabled])) ${container}:hover {\n border-color: var(--${tokenPrefix}-hover-border-color, var(${accentVar}));\n }\n\n :host([variant='bordered']) ${text} {\n color: var(--_theme-base);\n caret-color: var(--_theme-base);\n }\n\n :host([variant='bordered']) ${text}::placeholder {\n color: var(--${tokenPrefix}-placeholder-color, color-mix(in oklch, var(--_theme-base) 45%, transparent));\n }\n\n /* Outline — border always visible, so unlike the other variants it can't lean on a\n per-component \\`:host{}\\` base fallback for \\`--_border-color\\` that may or may not exist\n (\\`ore-message-composer\\` has none — every other rule here sets both custom properties, so\n its base layer omits a default on the assumption that always holds). Set explicitly. */\n :host([variant='outline']) {\n --_bg: var(--${tokenPrefix}-bg, transparent);\n --_border-color: var(--${tokenPrefix}-border-color, var(--color-contrast-300));\n }\n\n :host([variant='outline']) ${container} {\n box-shadow: none;\n }\n\n /* Ghost */\n :host([variant='ghost']) {\n --_bg: var(--${tokenPrefix}-bg, transparent);\n --_border-color: var(--${tokenPrefix}-border-color, transparent);\n }\n\n :host([variant='ghost']) ${container} {\n box-shadow: none;\n }\n\n :host([variant='ghost']:not([disabled])) ${container}:hover {\n background: var(--${tokenPrefix}-hover-bg, var(--color-contrast-100));\n }\n\n /* frost */\n :host([variant='frost']) {\n --_bg: var(--color-canvas);\n --_border-color: var(--${tokenPrefix}-border-color, var(--color-contrast-300));\n }\n\n :host([variant='frost']) ${container} {\n box-shadow: none;\n backdrop-filter: blur(var(--blur-md));\n }\n\n :host([variant='frost']:not([disabled])) ${container}:hover {\n background: var(--${tokenPrefix}-hover-bg, var(--color-contrast-100));\n }\n }\n`;\n"],"mappings":"+BAmEA,IAAa,GAAqB,CAChC,YAAY,iBACZ,YACA,OAAO,EACP,iBAC8B,EAAA,GAAG;;;;;qBAKd,EAAY;+BACF,EAAY;;;6BAGd,EAAU,6BAA6B,EAAU;;;;;;qBAMzD,EAAY;+BACF,EAAY,yCAAyC,EAAU;;;8BAGhE,EAAU;;;;8CAIM,EAAU;0BAC9B,EAAY;;YAE1B,EAAY;;;;;8CAKsB,EAAU;0BAC9B,EAAY;4BACV,EAAY,+CAA+C,EAAU;;;;;;qBAM5E,EAAY;+BACF,EAAY,yCAAyC,EAAU;;;kDAG5C,EAAU;4BAChC,EAAY,2BAA2B,EAAU;;;kCAG3C,EAAK;;;;;kCAKL,EAAK;qBAClB,EAAY;;;;;;;;qBAQZ,EAAY;+BACF,EAAY;;;iCAGV,EAAU;;;;;;qBAMtB,EAAY;+BACF,EAAY;;;+BAGZ,EAAU;;;;+CAIM,EAAU;0BAC/B,EAAY;;;;;;+BAMP,EAAY;;;+BAGZ,EAAU;;;;;+CAKM,EAAU;0BAC/B,EAAY"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-variants.css.d.ts","sourceRoot":"","sources":["../../../src/styles/mixins/field-variants.css.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oGAAoG;IACpG,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4FAA4F;IAC5F,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,eAAO,MAAM,iBAAiB,GAAI,8CAK/B,wBAAwB,
|
|
1
|
+
{"version":3,"file":"field-variants.css.d.ts","sourceRoot":"","sources":["../../../src/styles/mixins/field-variants.css.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oGAAoG;IACpG,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4FAA4F;IAC5F,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,eAAO,MAAM,iBAAiB,GAAI,8CAK/B,wBAAwB,sCAkG1B,CAAC"}
|
|
@@ -82,6 +82,21 @@ var t = ({ accentVar: t = "--_theme-focus", container: n, text: r = n, tokenPref
|
|
|
82
82
|
:host([variant='ghost']:not([disabled])) ${n}:hover {
|
|
83
83
|
background: var(--${i}-hover-bg, var(--color-contrast-100));
|
|
84
84
|
}
|
|
85
|
+
|
|
86
|
+
/* frost */
|
|
87
|
+
:host([variant='frost']) {
|
|
88
|
+
--_bg: var(--color-canvas);
|
|
89
|
+
--_border-color: var(--${i}-border-color, var(--color-contrast-300));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
:host([variant='frost']) ${n} {
|
|
93
|
+
box-shadow: none;
|
|
94
|
+
backdrop-filter: blur(var(--blur-md));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
:host([variant='frost']:not([disabled])) ${n}:hover {
|
|
98
|
+
background: var(--${i}-hover-bg, var(--color-contrast-100));
|
|
99
|
+
}
|
|
85
100
|
}
|
|
86
101
|
`;
|
|
87
102
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-variants.css.js","names":[],"sources":["../../../src/styles/mixins/field-variants.css.ts"],"sourcesContent":["import { css } from '@vielzeug/ore';\n\nexport type FieldVariantMixinOptions = {\n /**\n * The theme accent custom property `flat`/`bordered` mix into their border color. Defaults to\n * `--_theme-focus` (from `colorThemeMixin`). Override for a component with its own distinct\n * focus/accent concept instead of the shared theme token (`ore-otp-input`'s `--_cell-focus-\n * border`, which isn't theme-color-driven the same way).\n */\n accentVar?: string;\n /** Selector for the element that gets the variant's background/border/shadow — the visual \"box\". */\n container: string;\n /**\n * Selector for the text-bearing element the `bordered` variant tints to the theme color.\n * Defaults to `container` for components where the box and the text-bearing element are the\n * same node (`ore-input`/`ore-textarea`'s `.field`). Pass a distinct selector when they're\n * split (`ore-message-composer`'s `.composer` card vs its bare `.field` textarea).\n */\n text?: string;\n /** CSS custom-property namespace, e.g. `'input'` → `--input-bg`, `--input-hover-bg`, ... */\n tokenPrefix: string;\n};\n\n/**\n * The `solid`/`flat`/`bordered`/`outline`/`ghost` variant switch shared by every text-entry\n * field component (`ore-input`, `ore-textarea`, `ore-message-composer`, `ore-otp-input`) —\n * previously hand-duplicated across them, mostly identically but not quite: this version also\n * uniformly adds a `:not([disabled])` guard to the `flat` variant's hover/focus rules (only\n * `ore-message-composer` had it) and the `bordered` variant's text/placeholder/caret tint\n * (`ore-input`/`ore-otp-input` were missing pieces of it) — clearly-unintentional per-component\n * gaps, not deliberate differences, so this mixin converges on the more complete behavior for\n * every consumer rather than picking one component's version arbitrarily.\n *\n * Deliberately does **not** touch `:focus-within` box-shadow for `solid`/`outline`/`ghost` —\n * every current consumer already covers that with its own single `:host(:not([disabled],\n * [variant='flat'], ...)) ${container}:focus-within` catch-all in its *own* base layer, so a\n * copy here would be pure redundancy for them, and actively wrong for `ore-otp-input`: its\n * `.cell` is the focusable element itself (not a wrapper), so `:focus-within` matches on every\n * keystroke, and it already renders its own bespoke ring (colored by `accentVar`, not\n * `--_theme-shadow`) uniformly across all variants in its base layer — this mixin adding a\n * second, generic one would fight it depending on layer/rule order.\n *\n * Each component keeps its own `:host(:not([variant], ...)) ${container}:hover/:focus-within`\n * base-state rules and any variant beyond this shared set (e.g. `ore-input`'s extra `'text'`\n * variant) in its own stylesheet — this mixin only covers the five variants every consumer has\n * in common.\n *\n * @example\n * ```ts\n * // ore-textarea / ore-input (box and text-bearing element are the same `.field`)\n * styles: [..., fieldVariantMixin({ container: '.field', tokenPrefix: 'textarea' }), componentStyles]\n *\n * // ore-message-composer (card `.composer` vs its own bare `.field` textarea)\n * styles: [\n * ...,\n * fieldVariantMixin({ container: '.composer', text: '.field', tokenPrefix: 'message-composer' }),\n * componentStyles,\n * ]\n *\n * // ore-otp-input (own per-cell accent instead of the shared theme-focus token)\n * styles: [\n * ...,\n * fieldVariantMixin({ accentVar: '--_cell-focus-border', container: '.cell', tokenPrefix: 'otp-cell' }),\n * componentStyles,\n * ]\n * ```\n */\nexport const fieldVariantMixin = ({\n accentVar = '--_theme-focus',\n container,\n text = container,\n tokenPrefix,\n}: FieldVariantMixinOptions) => css`\n @layer refine.variants {\n /* Solid (default) */\n :host(:not([variant])),\n :host([variant='solid']) {\n --_bg: var(--${tokenPrefix}-bg, var(--color-contrast-50));\n --_border-color: var(--${tokenPrefix}-border-color, var(--color-contrast-300));\n }\n\n :host(:not([variant])) ${container}, :host([variant='solid']) ${container} {\n box-shadow: var(--shadow-2xs);\n }\n\n /* Flat */\n :host([variant='flat']) {\n --_bg: var(--${tokenPrefix}-bg, var(--color-contrast-100));\n --_border-color: var(--${tokenPrefix}-border-color, color-mix(in oklch, var(${accentVar}) 20%, transparent));\n }\n\n :host([variant='flat']) ${container} {\n box-shadow: var(--inset-shadow-2xs);\n }\n\n :host([variant='flat']:not([disabled])) ${container}:hover {\n background: var(--${tokenPrefix}-hover-bg, color-mix(in oklch, var(--_theme-base) 6%, var(--color-contrast-100)));\n border-color: var(\n --${tokenPrefix}-hover-border-color,\n color-mix(in oklch, var(--_theme-base) 35%, var(--color-contrast-300))\n );\n }\n\n :host([variant='flat']:not([disabled])) ${container}:focus-within {\n background: var(--${tokenPrefix}-focus-bg, color-mix(in oklch, var(--_theme-base) 8%, var(--color-canvas)));\n border-color: var(--${tokenPrefix}-focus-border-color, color-mix(in oklch, var(${accentVar}) 60%, transparent));\n box-shadow: var(--_theme-shadow);\n }\n\n /* Bordered */\n :host([variant='bordered']) {\n --_bg: var(--${tokenPrefix}-bg, var(--_theme-backdrop));\n --_border-color: var(--${tokenPrefix}-border-color, color-mix(in oklch, var(${accentVar}) 70%, transparent));\n }\n\n :host([variant='bordered']:not([disabled])) ${container}:hover {\n border-color: var(--${tokenPrefix}-hover-border-color, var(${accentVar}));\n }\n\n :host([variant='bordered']) ${text} {\n color: var(--_theme-base);\n caret-color: var(--_theme-base);\n }\n\n :host([variant='bordered']) ${text}::placeholder {\n color: var(--${tokenPrefix}-placeholder-color, color-mix(in oklch, var(--_theme-base) 45%, transparent));\n }\n\n /* Outline — border always visible, so unlike the other variants it can't lean on a\n per-component \\`:host{}\\` base fallback for \\`--_border-color\\` that may or may not exist\n (\\`ore-message-composer\\` has none — every other rule here sets both custom properties, so\n its base layer omits a default on the assumption that always holds). Set explicitly. */\n :host([variant='outline']) {\n --_bg: var(--${tokenPrefix}-bg, transparent);\n --_border-color: var(--${tokenPrefix}-border-color, var(--color-contrast-300));\n }\n\n :host([variant='outline']) ${container} {\n box-shadow: none;\n }\n\n /* Ghost */\n :host([variant='ghost']) {\n --_bg: var(--${tokenPrefix}-bg, transparent);\n --_border-color: var(--${tokenPrefix}-border-color, transparent);\n }\n\n :host([variant='ghost']) ${container} {\n box-shadow: none;\n }\n\n :host([variant='ghost']:not([disabled])) ${container}:hover {\n background: var(--${tokenPrefix}-hover-bg, var(--color-contrast-100));\n }\n }\n`;\n"],"mappings":";;AAmEA,IAAa,KAAqB,EAChC,eAAY,kBACZ,cACA,UAAO,GACP,qBAC8B,CAAG;;;;;qBAKd,EAAY;+BACF,EAAY;;;6BAGd,EAAU,6BAA6B,EAAU;;;;;;qBAMzD,EAAY;+BACF,EAAY,yCAAyC,EAAU;;;8BAGhE,EAAU;;;;8CAIM,EAAU;0BAC9B,EAAY;;YAE1B,EAAY;;;;;8CAKsB,EAAU;0BAC9B,EAAY;4BACV,EAAY,+CAA+C,EAAU;;;;;;qBAM5E,EAAY;+BACF,EAAY,yCAAyC,EAAU;;;kDAG5C,EAAU;4BAChC,EAAY,2BAA2B,EAAU;;;kCAG3C,EAAK;;;;;kCAKL,EAAK;qBAClB,EAAY;;;;;;;;qBAQZ,EAAY;+BACF,EAAY;;;iCAGV,EAAU;;;;;;qBAMtB,EAAY;+BACF,EAAY;;;+BAGZ,EAAU;;;;+CAIM,EAAU;0BAC/B,EAAY"}
|
|
1
|
+
{"version":3,"file":"field-variants.css.js","names":[],"sources":["../../../src/styles/mixins/field-variants.css.ts"],"sourcesContent":["import { css } from '@vielzeug/ore';\n\nexport type FieldVariantMixinOptions = {\n /**\n * The theme accent custom property `flat`/`bordered` mix into their border color. Defaults to\n * `--_theme-focus` (from `colorThemeMixin`). Override for a component with its own distinct\n * focus/accent concept instead of the shared theme token (`ore-otp-input`'s `--_cell-focus-\n * border`, which isn't theme-color-driven the same way).\n */\n accentVar?: string;\n /** Selector for the element that gets the variant's background/border/shadow — the visual \"box\". */\n container: string;\n /**\n * Selector for the text-bearing element the `bordered` variant tints to the theme color.\n * Defaults to `container` for components where the box and the text-bearing element are the\n * same node (`ore-input`/`ore-textarea`'s `.field`). Pass a distinct selector when they're\n * split (`ore-message-composer`'s `.composer` card vs its bare `.field` textarea).\n */\n text?: string;\n /** CSS custom-property namespace, e.g. `'input'` → `--input-bg`, `--input-hover-bg`, ... */\n tokenPrefix: string;\n};\n\n/**\n * The `solid`/`flat`/`bordered`/`outline`/`ghost` variant switch shared by every text-entry\n * field component (`ore-input`, `ore-textarea`, `ore-message-composer`, `ore-otp-input`) —\n * previously hand-duplicated across them, mostly identically but not quite: this version also\n * uniformly adds a `:not([disabled])` guard to the `flat` variant's hover/focus rules (only\n * `ore-message-composer` had it) and the `bordered` variant's text/placeholder/caret tint\n * (`ore-input`/`ore-otp-input` were missing pieces of it) — clearly-unintentional per-component\n * gaps, not deliberate differences, so this mixin converges on the more complete behavior for\n * every consumer rather than picking one component's version arbitrarily.\n *\n * Deliberately does **not** touch `:focus-within` box-shadow for `solid`/`outline`/`ghost` —\n * every current consumer already covers that with its own single `:host(:not([disabled],\n * [variant='flat'], ...)) ${container}:focus-within` catch-all in its *own* base layer, so a\n * copy here would be pure redundancy for them, and actively wrong for `ore-otp-input`: its\n * `.cell` is the focusable element itself (not a wrapper), so `:focus-within` matches on every\n * keystroke, and it already renders its own bespoke ring (colored by `accentVar`, not\n * `--_theme-shadow`) uniformly across all variants in its base layer — this mixin adding a\n * second, generic one would fight it depending on layer/rule order.\n *\n * Each component keeps its own `:host(:not([variant], ...)) ${container}:hover/:focus-within`\n * base-state rules and any variant beyond this shared set (e.g. `ore-input`'s extra `'text'`\n * variant) in its own stylesheet — this mixin only covers the five variants every consumer has\n * in common.\n *\n * @example\n * ```ts\n * // ore-textarea / ore-input (box and text-bearing element are the same `.field`)\n * styles: [..., fieldVariantMixin({ container: '.field', tokenPrefix: 'textarea' }), componentStyles]\n *\n * // ore-message-composer (card `.composer` vs its own bare `.field` textarea)\n * styles: [\n * ...,\n * fieldVariantMixin({ container: '.composer', text: '.field', tokenPrefix: 'message-composer' }),\n * componentStyles,\n * ]\n *\n * // ore-otp-input (own per-cell accent instead of the shared theme-focus token)\n * styles: [\n * ...,\n * fieldVariantMixin({ accentVar: '--_cell-focus-border', container: '.cell', tokenPrefix: 'otp-cell' }),\n * componentStyles,\n * ]\n * ```\n */\nexport const fieldVariantMixin = ({\n accentVar = '--_theme-focus',\n container,\n text = container,\n tokenPrefix,\n}: FieldVariantMixinOptions) => css`\n @layer refine.variants {\n /* Solid (default) */\n :host(:not([variant])),\n :host([variant='solid']) {\n --_bg: var(--${tokenPrefix}-bg, var(--color-contrast-50));\n --_border-color: var(--${tokenPrefix}-border-color, var(--color-contrast-300));\n }\n\n :host(:not([variant])) ${container}, :host([variant='solid']) ${container} {\n box-shadow: var(--shadow-2xs);\n }\n\n /* Flat */\n :host([variant='flat']) {\n --_bg: var(--${tokenPrefix}-bg, var(--color-contrast-100));\n --_border-color: var(--${tokenPrefix}-border-color, color-mix(in oklch, var(${accentVar}) 20%, transparent));\n }\n\n :host([variant='flat']) ${container} {\n box-shadow: var(--inset-shadow-2xs);\n }\n\n :host([variant='flat']:not([disabled])) ${container}:hover {\n background: var(--${tokenPrefix}-hover-bg, color-mix(in oklch, var(--_theme-base) 6%, var(--color-contrast-100)));\n border-color: var(\n --${tokenPrefix}-hover-border-color,\n color-mix(in oklch, var(--_theme-base) 35%, var(--color-contrast-300))\n );\n }\n\n :host([variant='flat']:not([disabled])) ${container}:focus-within {\n background: var(--${tokenPrefix}-focus-bg, color-mix(in oklch, var(--_theme-base) 8%, var(--color-canvas)));\n border-color: var(--${tokenPrefix}-focus-border-color, color-mix(in oklch, var(${accentVar}) 60%, transparent));\n box-shadow: var(--_theme-shadow);\n }\n\n /* Bordered */\n :host([variant='bordered']) {\n --_bg: var(--${tokenPrefix}-bg, var(--_theme-backdrop));\n --_border-color: var(--${tokenPrefix}-border-color, color-mix(in oklch, var(${accentVar}) 70%, transparent));\n }\n\n :host([variant='bordered']:not([disabled])) ${container}:hover {\n border-color: var(--${tokenPrefix}-hover-border-color, var(${accentVar}));\n }\n\n :host([variant='bordered']) ${text} {\n color: var(--_theme-base);\n caret-color: var(--_theme-base);\n }\n\n :host([variant='bordered']) ${text}::placeholder {\n color: var(--${tokenPrefix}-placeholder-color, color-mix(in oklch, var(--_theme-base) 45%, transparent));\n }\n\n /* Outline — border always visible, so unlike the other variants it can't lean on a\n per-component \\`:host{}\\` base fallback for \\`--_border-color\\` that may or may not exist\n (\\`ore-message-composer\\` has none — every other rule here sets both custom properties, so\n its base layer omits a default on the assumption that always holds). Set explicitly. */\n :host([variant='outline']) {\n --_bg: var(--${tokenPrefix}-bg, transparent);\n --_border-color: var(--${tokenPrefix}-border-color, var(--color-contrast-300));\n }\n\n :host([variant='outline']) ${container} {\n box-shadow: none;\n }\n\n /* Ghost */\n :host([variant='ghost']) {\n --_bg: var(--${tokenPrefix}-bg, transparent);\n --_border-color: var(--${tokenPrefix}-border-color, transparent);\n }\n\n :host([variant='ghost']) ${container} {\n box-shadow: none;\n }\n\n :host([variant='ghost']:not([disabled])) ${container}:hover {\n background: var(--${tokenPrefix}-hover-bg, var(--color-contrast-100));\n }\n\n /* frost */\n :host([variant='frost']) {\n --_bg: var(--color-canvas);\n --_border-color: var(--${tokenPrefix}-border-color, var(--color-contrast-300));\n }\n\n :host([variant='frost']) ${container} {\n box-shadow: none;\n backdrop-filter: blur(var(--blur-md));\n }\n\n :host([variant='frost']:not([disabled])) ${container}:hover {\n background: var(--${tokenPrefix}-hover-bg, var(--color-contrast-100));\n }\n }\n`;\n"],"mappings":";;AAmEA,IAAa,KAAqB,EAChC,eAAY,kBACZ,cACA,UAAO,GACP,qBAC8B,CAAG;;;;;qBAKd,EAAY;+BACF,EAAY;;;6BAGd,EAAU,6BAA6B,EAAU;;;;;;qBAMzD,EAAY;+BACF,EAAY,yCAAyC,EAAU;;;8BAGhE,EAAU;;;;8CAIM,EAAU;0BAC9B,EAAY;;YAE1B,EAAY;;;;;8CAKsB,EAAU;0BAC9B,EAAY;4BACV,EAAY,+CAA+C,EAAU;;;;;;qBAM5E,EAAY;+BACF,EAAY,yCAAyC,EAAU;;;kDAG5C,EAAU;4BAChC,EAAY,2BAA2B,EAAU;;;kCAG3C,EAAK;;;;;kCAKL,EAAK;qBAClB,EAAY;;;;;;;;qBAQZ,EAAY;+BACF,EAAY;;;iCAGV,EAAU;;;;;;qBAMtB,EAAY;+BACF,EAAY;;;+BAGZ,EAAU;;;;+CAIM,EAAU;0BAC/B,EAAY;;;;;;+BAMP,EAAY;;;+BAGZ,EAAU;;;;;+CAKM,EAAU;0BAC/B,EAAY"}
|
|
@@ -16,9 +16,9 @@ let e=require("@vielzeug/ore");var t=t=>e.css`
|
|
|
16
16
|
box-shadow: var(--halo-shadow-neutral);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
/*
|
|
19
|
+
/* Explicit elevation replaces the default halo so each depth remains visually distinct. */
|
|
20
20
|
:host([variant='frost']:not([color])[elevation]) ${t} {
|
|
21
|
-
box-shadow: var(--
|
|
21
|
+
box-shadow: var(--_shadow);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
:host([variant='frost'][color]) ${t} {
|
|
@@ -37,9 +37,9 @@ let e=require("@vielzeug/ore");var t=t=>e.css`
|
|
|
37
37
|
box-shadow: var(--_theme-halo);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
/*
|
|
40
|
+
/* Explicit elevation replaces the default halo so each depth remains visually distinct. */
|
|
41
41
|
:host([variant='frost'][color][elevation]) ${t} {
|
|
42
|
-
box-shadow: var(--
|
|
42
|
+
box-shadow: var(--_shadow);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
:host([variant='frost']:not([color])) ${t}:hover {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"variants.css.cjs","names":[],"sources":["../../../src/styles/mixins/variants.css.ts"],"sourcesContent":["import { css } from '@vielzeug/ore';\n\n/**\n * Frost Variant Mixin\n *\n * Shared frost variant styles for components.\n * Provides consistent frosted glass effect with backdrop blur,\n * theme-aware colors, halo shadows, and elevation support.\n *\n * @param selector - CSS selector for the element, matched against the\n * component's actual shadow DOM markup (e.g., '.card', '.field') — a bare\n * word like 'button' is a TYPE selector for a literal `<button>` tag, not\n * a part/class selector. Use `'[part=\"button\"]'` for components whose root\n * is a `<span part=\"button\">`/`<a part=\"button\">` (e.g. ore-button, which\n * never renders a `<button>` tag).\n * @returns CSSResult with complete frost variant implementation\n *\n * @example\n * ```ts\n * import { frostVariantMixin } from '../../styles/mixins/frost.css';\n *\n * return {\n * styles: [frostVariantMixin('[part=\"button\"]'), componentStyles],\n * template: html`...`\n * };\n * ```\n */\nexport const frostVariantMixin = (selector: string) => css`\n :host([variant='frost']) ${selector} {\n backdrop-filter: blur(var(--blur-xl)) saturate(200%) brightness(1.1);\n -webkit-backdrop-filter: blur(var(--blur-xl)) saturate(200%) brightness(1.1);\n }\n\n :host([variant='frost']:not([color])) ${selector} {\n background: color-mix(in oklch, var(--color-canvas) 55%, transparent);\n color: var(--color-contrast);\n text-shadow: var(--text-shadow-xs);\n border: var(--border) solid color-mix(in oklch, var(--color-contrast-400) 45%, transparent);\n }\n\n /* Frost neutral without elevation: use halo shadow */\n :host([variant='frost']:not([color]):not([elevation])) ${selector} {\n box-shadow: var(--halo-shadow-neutral);\n }\n\n /*
|
|
1
|
+
{"version":3,"file":"variants.css.cjs","names":[],"sources":["../../../src/styles/mixins/variants.css.ts"],"sourcesContent":["import { css } from '@vielzeug/ore';\n\n/**\n * Frost Variant Mixin\n *\n * Shared frost variant styles for components.\n * Provides consistent frosted glass effect with backdrop blur,\n * theme-aware colors, halo shadows, and elevation support.\n *\n * @param selector - CSS selector for the element, matched against the\n * component's actual shadow DOM markup (e.g., '.card', '.field') — a bare\n * word like 'button' is a TYPE selector for a literal `<button>` tag, not\n * a part/class selector. Use `'[part=\"button\"]'` for components whose root\n * is a `<span part=\"button\">`/`<a part=\"button\">` (e.g. ore-button, which\n * never renders a `<button>` tag).\n * @returns CSSResult with complete frost variant implementation\n *\n * @example\n * ```ts\n * import { frostVariantMixin } from '../../styles/mixins/frost.css';\n *\n * return {\n * styles: [frostVariantMixin('[part=\"button\"]'), componentStyles],\n * template: html`...`\n * };\n * ```\n */\nexport const frostVariantMixin = (selector: string) => css`\n :host([variant='frost']) ${selector} {\n backdrop-filter: blur(var(--blur-xl)) saturate(200%) brightness(1.1);\n -webkit-backdrop-filter: blur(var(--blur-xl)) saturate(200%) brightness(1.1);\n }\n\n :host([variant='frost']:not([color])) ${selector} {\n background: color-mix(in oklch, var(--color-canvas) 55%, transparent);\n color: var(--color-contrast);\n text-shadow: var(--text-shadow-xs);\n border: var(--border) solid color-mix(in oklch, var(--color-contrast-400) 45%, transparent);\n }\n\n /* Frost neutral without elevation: use halo shadow */\n :host([variant='frost']:not([color]):not([elevation])) ${selector} {\n box-shadow: var(--halo-shadow-neutral);\n }\n\n /* Explicit elevation replaces the default halo so each depth remains visually distinct. */\n :host([variant='frost']:not([color])[elevation]) ${selector} {\n box-shadow: var(--_shadow);\n }\n\n :host([variant='frost'][color]) ${selector} {\n background: color-mix(in oklch, var(--_theme-base), transparent);\n border-color: color-mix(in oklch, var(--_theme-border) 45%, transparent);\n color: var(--_theme-content);\n border: var(--border) solid color-mix(in oklch, var(--_theme-border) 45%, transparent);\n backdrop-filter: blur(var(--blur-2xl)) saturate(220%) brightness(1.15);\n -webkit-backdrop-filter: blur(var(--blur-2xl)) saturate(220%) brightness(1.15);\n text-shadow: var(--text-shadow-xs);\n filter: brightness(1.05);\n }\n\n /* Frost color without elevation: use halo shadow */\n :host([variant='frost'][color]:not([elevation])) ${selector} {\n box-shadow: var(--_theme-halo);\n }\n\n /* Explicit elevation replaces the default halo so each depth remains visually distinct. */\n :host([variant='frost'][color][elevation]) ${selector} {\n box-shadow: var(--_shadow);\n }\n\n :host([variant='frost']:not([color])) ${selector}:hover {\n backdrop-filter: blur(var(--blur-2xl)) saturate(220%) brightness(1.15);\n -webkit-backdrop-filter: blur(var(--blur-2xl)) saturate(220%) brightness(1.15);\n }\n\n :host([variant='frost'][color]) ${selector}:hover {\n backdrop-filter: blur(var(--blur-3xl)) saturate(240%) brightness(1.2);\n -webkit-backdrop-filter: blur(var(--blur-3xl)) saturate(240%) brightness(1.2);\n }\n`;\n"],"mappings":"+BA2BA,IAAa,EAAqB,GAAqB,EAAA,GAAG;6BAC7B,EAAS;;;;;0CAKI,EAAS;;;;;;;;2DAQQ,EAAS;;;;;qDAKf,EAAS;;;;oCAI1B,EAAS;;;;;;;;;;;;qDAYQ,EAAS;;;;;+CAKf,EAAS;;;;0CAId,EAAS;;;;;oCAKf,EAAS"}
|
|
@@ -18,9 +18,9 @@ var t = (t) => e`
|
|
|
18
18
|
box-shadow: var(--halo-shadow-neutral);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/*
|
|
21
|
+
/* Explicit elevation replaces the default halo so each depth remains visually distinct. */
|
|
22
22
|
:host([variant='frost']:not([color])[elevation]) ${t} {
|
|
23
|
-
box-shadow: var(--
|
|
23
|
+
box-shadow: var(--_shadow);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
:host([variant='frost'][color]) ${t} {
|
|
@@ -39,9 +39,9 @@ var t = (t) => e`
|
|
|
39
39
|
box-shadow: var(--_theme-halo);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
/*
|
|
42
|
+
/* Explicit elevation replaces the default halo so each depth remains visually distinct. */
|
|
43
43
|
:host([variant='frost'][color][elevation]) ${t} {
|
|
44
|
-
box-shadow: var(--
|
|
44
|
+
box-shadow: var(--_shadow);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
:host([variant='frost']:not([color])) ${t}:hover {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"variants.css.js","names":[],"sources":["../../../src/styles/mixins/variants.css.ts"],"sourcesContent":["import { css } from '@vielzeug/ore';\n\n/**\n * Frost Variant Mixin\n *\n * Shared frost variant styles for components.\n * Provides consistent frosted glass effect with backdrop blur,\n * theme-aware colors, halo shadows, and elevation support.\n *\n * @param selector - CSS selector for the element, matched against the\n * component's actual shadow DOM markup (e.g., '.card', '.field') — a bare\n * word like 'button' is a TYPE selector for a literal `<button>` tag, not\n * a part/class selector. Use `'[part=\"button\"]'` for components whose root\n * is a `<span part=\"button\">`/`<a part=\"button\">` (e.g. ore-button, which\n * never renders a `<button>` tag).\n * @returns CSSResult with complete frost variant implementation\n *\n * @example\n * ```ts\n * import { frostVariantMixin } from '../../styles/mixins/frost.css';\n *\n * return {\n * styles: [frostVariantMixin('[part=\"button\"]'), componentStyles],\n * template: html`...`\n * };\n * ```\n */\nexport const frostVariantMixin = (selector: string) => css`\n :host([variant='frost']) ${selector} {\n backdrop-filter: blur(var(--blur-xl)) saturate(200%) brightness(1.1);\n -webkit-backdrop-filter: blur(var(--blur-xl)) saturate(200%) brightness(1.1);\n }\n\n :host([variant='frost']:not([color])) ${selector} {\n background: color-mix(in oklch, var(--color-canvas) 55%, transparent);\n color: var(--color-contrast);\n text-shadow: var(--text-shadow-xs);\n border: var(--border) solid color-mix(in oklch, var(--color-contrast-400) 45%, transparent);\n }\n\n /* Frost neutral without elevation: use halo shadow */\n :host([variant='frost']:not([color]):not([elevation])) ${selector} {\n box-shadow: var(--halo-shadow-neutral);\n }\n\n /*
|
|
1
|
+
{"version":3,"file":"variants.css.js","names":[],"sources":["../../../src/styles/mixins/variants.css.ts"],"sourcesContent":["import { css } from '@vielzeug/ore';\n\n/**\n * Frost Variant Mixin\n *\n * Shared frost variant styles for components.\n * Provides consistent frosted glass effect with backdrop blur,\n * theme-aware colors, halo shadows, and elevation support.\n *\n * @param selector - CSS selector for the element, matched against the\n * component's actual shadow DOM markup (e.g., '.card', '.field') — a bare\n * word like 'button' is a TYPE selector for a literal `<button>` tag, not\n * a part/class selector. Use `'[part=\"button\"]'` for components whose root\n * is a `<span part=\"button\">`/`<a part=\"button\">` (e.g. ore-button, which\n * never renders a `<button>` tag).\n * @returns CSSResult with complete frost variant implementation\n *\n * @example\n * ```ts\n * import { frostVariantMixin } from '../../styles/mixins/frost.css';\n *\n * return {\n * styles: [frostVariantMixin('[part=\"button\"]'), componentStyles],\n * template: html`...`\n * };\n * ```\n */\nexport const frostVariantMixin = (selector: string) => css`\n :host([variant='frost']) ${selector} {\n backdrop-filter: blur(var(--blur-xl)) saturate(200%) brightness(1.1);\n -webkit-backdrop-filter: blur(var(--blur-xl)) saturate(200%) brightness(1.1);\n }\n\n :host([variant='frost']:not([color])) ${selector} {\n background: color-mix(in oklch, var(--color-canvas) 55%, transparent);\n color: var(--color-contrast);\n text-shadow: var(--text-shadow-xs);\n border: var(--border) solid color-mix(in oklch, var(--color-contrast-400) 45%, transparent);\n }\n\n /* Frost neutral without elevation: use halo shadow */\n :host([variant='frost']:not([color]):not([elevation])) ${selector} {\n box-shadow: var(--halo-shadow-neutral);\n }\n\n /* Explicit elevation replaces the default halo so each depth remains visually distinct. */\n :host([variant='frost']:not([color])[elevation]) ${selector} {\n box-shadow: var(--_shadow);\n }\n\n :host([variant='frost'][color]) ${selector} {\n background: color-mix(in oklch, var(--_theme-base), transparent);\n border-color: color-mix(in oklch, var(--_theme-border) 45%, transparent);\n color: var(--_theme-content);\n border: var(--border) solid color-mix(in oklch, var(--_theme-border) 45%, transparent);\n backdrop-filter: blur(var(--blur-2xl)) saturate(220%) brightness(1.15);\n -webkit-backdrop-filter: blur(var(--blur-2xl)) saturate(220%) brightness(1.15);\n text-shadow: var(--text-shadow-xs);\n filter: brightness(1.05);\n }\n\n /* Frost color without elevation: use halo shadow */\n :host([variant='frost'][color]:not([elevation])) ${selector} {\n box-shadow: var(--_theme-halo);\n }\n\n /* Explicit elevation replaces the default halo so each depth remains visually distinct. */\n :host([variant='frost'][color][elevation]) ${selector} {\n box-shadow: var(--_shadow);\n }\n\n :host([variant='frost']:not([color])) ${selector}:hover {\n backdrop-filter: blur(var(--blur-2xl)) saturate(220%) brightness(1.15);\n -webkit-backdrop-filter: blur(var(--blur-2xl)) saturate(220%) brightness(1.15);\n }\n\n :host([variant='frost'][color]) ${selector}:hover {\n backdrop-filter: blur(var(--blur-3xl)) saturate(240%) brightness(1.2);\n -webkit-backdrop-filter: blur(var(--blur-3xl)) saturate(240%) brightness(1.2);\n }\n`;\n"],"mappings":";;AA2BA,IAAa,KAAqB,MAAqB,CAAG;6BAC7B,EAAS;;;;;0CAKI,EAAS;;;;;;;;2DAQQ,EAAS;;;;;qDAKf,EAAS;;;;oCAI1B,EAAS;;;;;;;;;;;;qDAYQ,EAAS;;;;;+CAKf,EAAS;;;;0CAId,EAAS;;;;;oCAKf,EAAS"}
|
package/dist/tab-item.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-item.cjs","names":[],"sources":["../src/disclosure/tab-item/tab-item.ts"],"sourcesContent":["import { bind, define, getHost, html, inject, onMounted, prop, ref, watchEffect } from '@vielzeug/ore';\nimport { computed } from '@vielzeug/ripple';\nimport { setAriaReflection } from '../../core';\nimport { disablableBundle } from '../../shared';\nimport { coarsePointerMixin, colorThemeMixin, forcedColorsFocusMixin } from '../../styles';\nimport type { ComponentSize,
|
|
1
|
+
{"version":3,"file":"tab-item.cjs","names":[],"sources":["../src/disclosure/tab-item/tab-item.ts"],"sourcesContent":["import { bind, define, getHost, html, inject, onMounted, prop, ref, watchEffect } from '@vielzeug/ore';\nimport { computed } from '@vielzeug/ripple';\nimport { setAriaReflection } from '../../core';\nimport { disablableBundle } from '../../shared';\nimport { coarsePointerMixin, colorThemeMixin, forcedColorsFocusMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor, VisualVariant } from '../../types';\nimport { TABS_CTX } from '../tabs/tabs';\nimport styles from './tab-item.css?inline';\n\nexport type OreTabItemProps = {\n /** Whether this tab is currently selected (set by ore-tabs) */\n active?: boolean;\n /** Theme color (inherited from ore-tabs) */\n color?: ThemeColor;\n /** Disable this tab */\n disabled?: boolean;\n /** Spatial density (inherited from ore-tabs) */\n density?: 'compact' | 'default';\n /** Size (inherited from ore-tabs) */\n size?: ComponentSize;\n /** Unique value identifier — must match a ore-tab-panel value */\n value: string;\n /** Visual variant (inherited from ore-tabs) */\n variant?: VisualVariant;\n};\n\n/**\n * Individual tab trigger. Must be placed in the `tabs` slot of `ore-tabs`.\n *\n * @element ore-tab-item\n *\n * @attr {string} value - Unique identifier, matches the corresponding ore-tab-panel value\n * @attr {boolean} active - Set by the parent ore-tabs when this tab is selected\n * @attr {boolean} disabled - Prevents selection\n * @attr {string} density - Inherited spatial density: 'default' | 'compact'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {string} variant - Inherited from ore-tabs: 'solid' | 'flat' | 'bordered' | 'ghost' | 'frost'\n * @attr {string} color - Inherited from ore-tabs: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n *\n * @slot prefix - Icon or content before the label\n * @slot - Tab label\n * @slot suffix - Badge or count after the label\n *\n * @cssprop --tab-item-radius - Button border radius\n * @cssprop --tab-item-transition - Transition duration/easing\n * @cssprop --tab-item-padding - Button padding\n * @cssprop --tab-item-font-size - Button font size\n * @cssprop --tab-item-color - Default text color\n * @cssprop --tab-item-hover-bg - Background on hover\n * @cssprop --tab-item-active-bg - Background when active/selected\n * @cssprop --tab-item-active-color - Text color when active/selected\n * @cssprop --tab-item-active-shadow - Box shadow when active/selected\n * @part tab - Tab trigger element.\n * @example\n * ```html\n * <ore-tab-item slot=\"tabs\" value=\"overview\">Overview</ore-tab-item>\n * <ore-tab-item slot=\"tabs\" value=\"settings\" disabled>Settings</ore-tab-item>\n * ```\n */\nexport const TAB_ITEM_TAG = 'ore-tab-item' as const;\ndefine<OreTabItemProps>(TAB_ITEM_TAG, {\n props: {\n ...disablableBundle,\n active: prop.bool(false),\n color: prop.string<ThemeColor>(),\n density: prop.oneOf(['compact', 'default'] as const, 'default'),\n size: prop.string<ComponentSize>(),\n value: prop.string(''),\n variant: prop.string<VisualVariant>(),\n },\n setup(props) {\n const el = getHost();\n const watch = watchEffect;\n\n const tabsCtx = inject(TABS_CTX);\n\n if (tabsCtx) {\n watch(() => {\n const color = tabsCtx.color.value;\n const density = tabsCtx.density.value;\n const size = tabsCtx.size.value;\n const variant = tabsCtx.variant.value;\n\n if (color !== undefined) el.setAttribute('color', color);\n\n el.setAttribute('density', density);\n\n if (size !== undefined) el.setAttribute('size', size);\n\n if (variant !== undefined) el.setAttribute('variant', variant);\n });\n }\n\n const isActive = computed(() =>\n tabsCtx ? !!tabsCtx.value.value && tabsCtx.value.value === props.value.value : props.active.value,\n );\n const isDisabled = () => Boolean(props.disabled.value);\n\n bind({\n attr: {\n active: () => (isActive.value ? true : undefined),\n },\n });\n\n const handleClick = (event: MouseEvent) => {\n event.stopPropagation();\n\n if (isDisabled()) {\n event.preventDefault();\n\n return;\n }\n\n el.dispatchEvent(new CustomEvent('click', { bubbles: true, detail: { value: props.value.value } }));\n };\n\n const tabId = () => `tab-${props.value.value}`;\n const buttonRef = ref<HTMLButtonElement>();\n\n // `aria-controls` as a plain IDREF attribute cannot resolve across shadow-tree boundaries\n // (the matching `id` lives inside <ore-tab-panel>'s own shadow root) — see\n // `setAriaReflection()`'s doc comment. Deferred to `onMounted()`: sibling custom elements\n // (including the peer `<ore-tab-panel>`) connect in tree order within the same synchronous\n // insertion, so a sibling's shadow content may not exist yet during this element's own\n // `setup()` — `onMounted()`'s microtask runs after the whole subtree has connected.\n onMounted(() => {\n watchEffect(() => {\n const button = buttonRef.value;\n const value = props.value.value;\n\n if (!button) return;\n\n const tabsEl = value ? el.closest('ore-tabs') : null;\n const panelEl = tabsEl?.querySelector<HTMLElement>(`:scope > ore-tab-panel[value=\"${value}\"]`);\n const panel = panelEl?.shadowRoot?.querySelector<HTMLElement>('.panel') ?? null;\n\n setAriaReflection(button, 'ariaControlsElements', panel ? [panel] : []);\n });\n });\n\n return html`\n <button\n role=\"tab\"\n type=\"button\"\n part=\"tab\"\n ref=\"${buttonRef}\"\n id=\"${tabId}\"\n aria-selected=\"${isActive}\"\n tabindex=\"${() => (isActive.value ? '0' : '-1')}\"\n aria-disabled=\"${isDisabled}\"\n @click=\"${handleClick}\">\n <slot name=\"prefix\"></slot>\n <slot></slot>\n <slot name=\"suffix\"></slot>\n </button>\n `;\n },\n styles: [colorThemeMixin, forcedColorsFocusMixin('button'), coarsePointerMixin, styles],\n});\n"],"mappings":"+XA2DA,IAAa,EAAe,gBAC5B,EAAA,EAAA,OAAA,CAAwB,EAAc,CACpC,MAAO,CACL,GAAG,EAAA,iBACH,OAAQ,EAAA,KAAK,KAAK,EAAK,EACvB,MAAO,EAAA,KAAK,OAAmB,EAC/B,QAAS,EAAA,KAAK,MAAM,CAAC,UAAW,SAAS,EAAY,SAAS,EAC9D,KAAM,EAAA,KAAK,OAAsB,EACjC,MAAO,EAAA,KAAK,OAAO,EAAE,EACrB,QAAS,EAAA,KAAK,OAAsB,CACtC,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAK,EAAA,QAAA,CAAQ,EACb,EAAQ,EAAA,YAER,GAAA,EAAU,EAAA,OAAA,CAAO,EAAA,QAAQ,EAE3B,GACF,MAAY,CACV,IAAM,EAAQ,EAAQ,MAAM,MACtB,EAAU,EAAQ,QAAQ,MAC1B,EAAO,EAAQ,KAAK,MACpB,EAAU,EAAQ,QAAQ,MAE5B,IAAU,IAAA,IAAW,EAAG,aAAa,QAAS,CAAK,EAEvD,EAAG,aAAa,UAAW,CAAO,EAE9B,IAAS,IAAA,IAAW,EAAG,aAAa,OAAQ,CAAI,EAEhD,IAAY,IAAA,IAAW,EAAG,aAAa,UAAW,CAAO,CAC/D,CAAC,EAGH,IAAM,GAAA,EAAW,EAAA,SAAA,KACf,EAAU,CAAC,CAAC,EAAQ,MAAM,OAAS,EAAQ,MAAM,QAAU,EAAM,MAAM,MAAQ,EAAM,OAAO,KAC9F,EACM,MAAmB,EAAQ,EAAM,SAAS,OAEhD,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,WAAe,EAAS,MAAQ,GAAO,IAAA,EACzC,CACF,CAAC,EAED,IAAM,EAAe,GAAsB,CAGzC,GAFA,EAAM,gBAAgB,EAElB,EAAW,EAAG,CAChB,EAAM,eAAe,EAErB,MACF,CAEA,EAAG,cAAc,IAAI,YAAY,QAAS,CAAE,QAAS,GAAM,OAAQ,CAAE,MAAO,EAAM,MAAM,KAAM,CAAE,CAAC,CAAC,CACpG,EAEM,MAAc,OAAO,EAAM,MAAM,QACjC,GAAA,EAAY,EAAA,IAAA,CAAuB,EAuBzC,OAfA,EAAA,EAAA,UAAA,KAAgB,EACd,EAAA,EAAA,YAAA,KAAkB,CAChB,IAAM,EAAS,EAAU,MACnB,EAAQ,EAAM,MAAM,MAE1B,GAAI,CAAC,EAAQ,OAIb,IAAM,IAFS,EAAQ,EAAG,QAAQ,UAAU,EAAI,KAAA,EACxB,cAA2B,iCAAiC,EAAM,GAAG,EAAA,EACtE,YAAY,cAA2B,QAAQ,GAAK,KAE3E,EAAA,kBAAkB,EAAQ,uBAAwB,EAAQ,CAAC,CAAK,EAAI,CAAC,CAAC,CACxE,CAAC,CACH,CAAC,EAEM,EAAA,IAAI;;;;;eAKA,EAAU;cACX,EAAM;yBACK,EAAS;wBACP,EAAS,MAAQ,IAAM,KAAM;yBAC/B,EAAW;kBAClB,EAAY;;;;;KAM5B,EACA,OAAQ,CAAC,EAAA,gBAAiB,EAAA,uBAAuB,QAAQ,EAAG,EAAA,mBAAoB,EAAA,OAAM,CACxF,CAAC"}
|
package/dist/tab-item.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-item.js","names":[],"sources":["../src/disclosure/tab-item/tab-item.ts"],"sourcesContent":["import { bind, define, getHost, html, inject, onMounted, prop, ref, watchEffect } from '@vielzeug/ore';\nimport { computed } from '@vielzeug/ripple';\nimport { setAriaReflection } from '../../core';\nimport { disablableBundle } from '../../shared';\nimport { coarsePointerMixin, colorThemeMixin, forcedColorsFocusMixin } from '../../styles';\nimport type { ComponentSize,
|
|
1
|
+
{"version":3,"file":"tab-item.js","names":[],"sources":["../src/disclosure/tab-item/tab-item.ts"],"sourcesContent":["import { bind, define, getHost, html, inject, onMounted, prop, ref, watchEffect } from '@vielzeug/ore';\nimport { computed } from '@vielzeug/ripple';\nimport { setAriaReflection } from '../../core';\nimport { disablableBundle } from '../../shared';\nimport { coarsePointerMixin, colorThemeMixin, forcedColorsFocusMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor, VisualVariant } from '../../types';\nimport { TABS_CTX } from '../tabs/tabs';\nimport styles from './tab-item.css?inline';\n\nexport type OreTabItemProps = {\n /** Whether this tab is currently selected (set by ore-tabs) */\n active?: boolean;\n /** Theme color (inherited from ore-tabs) */\n color?: ThemeColor;\n /** Disable this tab */\n disabled?: boolean;\n /** Spatial density (inherited from ore-tabs) */\n density?: 'compact' | 'default';\n /** Size (inherited from ore-tabs) */\n size?: ComponentSize;\n /** Unique value identifier — must match a ore-tab-panel value */\n value: string;\n /** Visual variant (inherited from ore-tabs) */\n variant?: VisualVariant;\n};\n\n/**\n * Individual tab trigger. Must be placed in the `tabs` slot of `ore-tabs`.\n *\n * @element ore-tab-item\n *\n * @attr {string} value - Unique identifier, matches the corresponding ore-tab-panel value\n * @attr {boolean} active - Set by the parent ore-tabs when this tab is selected\n * @attr {boolean} disabled - Prevents selection\n * @attr {string} density - Inherited spatial density: 'default' | 'compact'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {string} variant - Inherited from ore-tabs: 'solid' | 'flat' | 'bordered' | 'ghost' | 'frost'\n * @attr {string} color - Inherited from ore-tabs: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n *\n * @slot prefix - Icon or content before the label\n * @slot - Tab label\n * @slot suffix - Badge or count after the label\n *\n * @cssprop --tab-item-radius - Button border radius\n * @cssprop --tab-item-transition - Transition duration/easing\n * @cssprop --tab-item-padding - Button padding\n * @cssprop --tab-item-font-size - Button font size\n * @cssprop --tab-item-color - Default text color\n * @cssprop --tab-item-hover-bg - Background on hover\n * @cssprop --tab-item-active-bg - Background when active/selected\n * @cssprop --tab-item-active-color - Text color when active/selected\n * @cssprop --tab-item-active-shadow - Box shadow when active/selected\n * @part tab - Tab trigger element.\n * @example\n * ```html\n * <ore-tab-item slot=\"tabs\" value=\"overview\">Overview</ore-tab-item>\n * <ore-tab-item slot=\"tabs\" value=\"settings\" disabled>Settings</ore-tab-item>\n * ```\n */\nexport const TAB_ITEM_TAG = 'ore-tab-item' as const;\ndefine<OreTabItemProps>(TAB_ITEM_TAG, {\n props: {\n ...disablableBundle,\n active: prop.bool(false),\n color: prop.string<ThemeColor>(),\n density: prop.oneOf(['compact', 'default'] as const, 'default'),\n size: prop.string<ComponentSize>(),\n value: prop.string(''),\n variant: prop.string<VisualVariant>(),\n },\n setup(props) {\n const el = getHost();\n const watch = watchEffect;\n\n const tabsCtx = inject(TABS_CTX);\n\n if (tabsCtx) {\n watch(() => {\n const color = tabsCtx.color.value;\n const density = tabsCtx.density.value;\n const size = tabsCtx.size.value;\n const variant = tabsCtx.variant.value;\n\n if (color !== undefined) el.setAttribute('color', color);\n\n el.setAttribute('density', density);\n\n if (size !== undefined) el.setAttribute('size', size);\n\n if (variant !== undefined) el.setAttribute('variant', variant);\n });\n }\n\n const isActive = computed(() =>\n tabsCtx ? !!tabsCtx.value.value && tabsCtx.value.value === props.value.value : props.active.value,\n );\n const isDisabled = () => Boolean(props.disabled.value);\n\n bind({\n attr: {\n active: () => (isActive.value ? true : undefined),\n },\n });\n\n const handleClick = (event: MouseEvent) => {\n event.stopPropagation();\n\n if (isDisabled()) {\n event.preventDefault();\n\n return;\n }\n\n el.dispatchEvent(new CustomEvent('click', { bubbles: true, detail: { value: props.value.value } }));\n };\n\n const tabId = () => `tab-${props.value.value}`;\n const buttonRef = ref<HTMLButtonElement>();\n\n // `aria-controls` as a plain IDREF attribute cannot resolve across shadow-tree boundaries\n // (the matching `id` lives inside <ore-tab-panel>'s own shadow root) — see\n // `setAriaReflection()`'s doc comment. Deferred to `onMounted()`: sibling custom elements\n // (including the peer `<ore-tab-panel>`) connect in tree order within the same synchronous\n // insertion, so a sibling's shadow content may not exist yet during this element's own\n // `setup()` — `onMounted()`'s microtask runs after the whole subtree has connected.\n onMounted(() => {\n watchEffect(() => {\n const button = buttonRef.value;\n const value = props.value.value;\n\n if (!button) return;\n\n const tabsEl = value ? el.closest('ore-tabs') : null;\n const panelEl = tabsEl?.querySelector<HTMLElement>(`:scope > ore-tab-panel[value=\"${value}\"]`);\n const panel = panelEl?.shadowRoot?.querySelector<HTMLElement>('.panel') ?? null;\n\n setAriaReflection(button, 'ariaControlsElements', panel ? [panel] : []);\n });\n });\n\n return html`\n <button\n role=\"tab\"\n type=\"button\"\n part=\"tab\"\n ref=\"${buttonRef}\"\n id=\"${tabId}\"\n aria-selected=\"${isActive}\"\n tabindex=\"${() => (isActive.value ? '0' : '-1')}\"\n aria-disabled=\"${isDisabled}\"\n @click=\"${handleClick}\">\n <slot name=\"prefix\"></slot>\n <slot></slot>\n <slot name=\"suffix\"></slot>\n </button>\n `;\n },\n styles: [colorThemeMixin, forcedColorsFocusMixin('button'), coarsePointerMixin, styles],\n});\n"],"mappings":";;;;;;;;;AA2DA,IAAa,IAAe;AAC5B,EAAwB,GAAc;CACpC,OAAO;EACL,GAAG;EACH,QAAQ,EAAK,KAAK,EAAK;EACvB,OAAO,EAAK,OAAmB;EAC/B,SAAS,EAAK,MAAM,CAAC,WAAW,SAAS,GAAY,SAAS;EAC9D,MAAM,EAAK,OAAsB;EACjC,OAAO,EAAK,OAAO,EAAE;EACrB,SAAS,EAAK,OAAsB;CACtC;CACA,MAAM,GAAO;EACX,IAAM,IAAK,EAAQ,GACb,IAAQ,GAER,IAAU,EAAO,CAAQ;EAE/B,AAAI,KACF,QAAY;GACV,IAAM,IAAQ,EAAQ,MAAM,OACtB,IAAU,EAAQ,QAAQ,OAC1B,IAAO,EAAQ,KAAK,OACpB,IAAU,EAAQ,QAAQ;GAQhC,AANI,MAAU,KAAA,KAAW,EAAG,aAAa,SAAS,CAAK,GAEvD,EAAG,aAAa,WAAW,CAAO,GAE9B,MAAS,KAAA,KAAW,EAAG,aAAa,QAAQ,CAAI,GAEhD,MAAY,KAAA,KAAW,EAAG,aAAa,WAAW,CAAO;EAC/D,CAAC;EAGH,IAAM,IAAW,QACf,IAAU,CAAC,CAAC,EAAQ,MAAM,SAAS,EAAQ,MAAM,UAAU,EAAM,MAAM,QAAQ,EAAM,OAAO,KAC9F,GACM,UAAmB,EAAQ,EAAM,SAAS;EAEhD,EAAK,EACH,MAAM,EACJ,cAAe,EAAS,QAAQ,KAAO,KAAA,EACzC,EACF,CAAC;EAED,IAAM,KAAe,MAAsB;GAGzC,IAFA,EAAM,gBAAgB,GAElB,EAAW,GAAG;IAChB,EAAM,eAAe;IAErB;GACF;GAEA,EAAG,cAAc,IAAI,YAAY,SAAS;IAAE,SAAS;IAAM,QAAQ,EAAE,OAAO,EAAM,MAAM,MAAM;GAAE,CAAC,CAAC;EACpG,GAEM,UAAc,OAAO,EAAM,MAAM,SACjC,IAAY,EAAuB;EAuBzC,OAfA,QAAgB;GACd,QAAkB;IAChB,IAAM,IAAS,EAAU,OACnB,IAAQ,EAAM,MAAM;IAE1B,IAAI,CAAC,GAAQ;IAIb,IAAM,MAFS,IAAQ,EAAG,QAAQ,UAAU,IAAI,KAAA,EACxB,cAA2B,iCAAiC,EAAM,GAAG,EAAA,EACtE,YAAY,cAA2B,QAAQ,KAAK;IAE3E,EAAkB,GAAQ,wBAAwB,IAAQ,CAAC,CAAK,IAAI,CAAC,CAAC;GACxE,CAAC;EACH,CAAC,GAEM,CAAI;;;;;eAKA,EAAU;cACX,EAAM;yBACK,EAAS;0BACP,EAAS,QAAQ,MAAM,KAAM;yBAC/B,EAAW;kBAClB,EAAY;;;;;;CAM5B;CACA,QAAQ;EAAC;EAAiB,EAAuB,QAAQ;EAAG;EAAoB;CAAM;AACxF,CAAC"}
|
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 density: Readable<'compact' | 'default'>;\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 /** Spatial density; compact renders a 24px tab control for dense interfaces. */\n density?: 'compact' | 'default';\n /** Accessible label for the tablist (passed as aria-label). Use when there is no visible heading labelling the tabs. */\n label?: string;\n /** Tab list orientation */\n orientation?: 'horizontal' | 'vertical';\n /** Component size */\n size?: ComponentSize;\n /** Currently selected tab value */\n value?: string;\n /** Visual style variant */\n variant?: 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} density - Spatial density: 'default' | 'compact'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n *\n * @fires change - Emitted when the active tab changes with detail: { value: string }\n *\n * @slot tabs - Place `ore-tab-item` elements here\n * @slot - Place `ore-tab-panel` elements here\n *\n * @cssprop --tabs-radius - Border radius of the tablist container and panels\n * @cssprop --tabs-transition - Transition duration/easing for the active indicator\n * @cssprop --tabs-indicator-color - Color of the sliding active indicator line\n * @cssprop --tabs-bg - Background of the host element (flat variant)\n * @cssprop --tabs-tablist-bg - Tablist container background (solid/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 density: prop.oneOf(['compact', 'default'] as const, 'default'),\n label: prop.string(),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n variant: prop.string<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 density: computed(() => props.density.value ?? 'default'),\n orientation: computed(() => props.orientation.value ?? 'horizontal'),\n size: props.size,\n value: selectedValue,\n variant: props.variant,\n });\n\n const moveIndicator = (activeTab: HTMLElement | undefined) => {\n const indicator = indicatorRef.value;\n const tablist = tablistRef.value;\n\n if (!indicator || !tablist || !activeTab) return;\n\n const tabRect = activeTab.getBoundingClientRect();\n const listRect = tablist.getBoundingClientRect();\n\n if (isVertical()) {\n const y = tabRect.top - listRect.top + tablist.scrollTop;\n\n indicator.style.transition = 'none';\n indicator.style.height = `${tabRect.height}px`;\n indicator.style.width = '';\n indicator.style.left = '0';\n indicator.style.top = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateY(${y}px)`;\n } else {\n const x = tabRect.left - listRect.left + tablist.scrollLeft;\n\n indicator.style.transition = 'none';\n indicator.style.width = `${tabRect.width}px`;\n indicator.style.height = '';\n indicator.style.top = '';\n indicator.style.left = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateX(${x}px)`;\n }\n };\n\n const updateIndicator = () => {\n const value = selectedValue.value;\n\n if (!value) return;\n\n const activeTab = getTabs().find((t) => t.getAttribute('value') === value);\n\n moveIndicator(activeTab);\n };\n\n watch(selectedValue, () => {\n requestAnimationFrame(updateIndicator);\n });\n\n // ────────────────────────────────────────────────────────────────\n // Event Handlers\n // ────────────────────────────────────────────────────────────────\n\n const handleTabClick = (e: Event) => {\n const tab = e\n .composedPath()\n .find((node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item');\n\n if (!tab || tab.hasAttribute('disabled')) return;\n\n // Guard: only respond to tab-items that belong to THIS tabs instance\n if (tab.closest('ore-tabs') !== el) return;\n\n const value = tab.getAttribute('value');\n\n if (!value || value === selectedValue.value) return;\n\n setSelection(value, true);\n };\n\n const activateFocusedTab = (): void => {\n const tabs = getEnabledTabs();\n const focusedTab = tabs.find(\n (tab) => tab === document.activeElement || tab.shadowRoot?.activeElement === document.activeElement,\n );\n const focusedValue = focusedTab?.getAttribute('value');\n\n if (focusedValue && focusedValue !== selectedValue.value) setSelection(focusedValue, true);\n };\n\n const manualActivationPress = createInteraction({\n disabled: computed(() => !isManualActivation()),\n onPress: activateFocusedTab,\n });\n\n const handleKeydown = (e: KeyboardEvent) => {\n const tabs = getEnabledTabs();\n\n if (tabs.length === 0) return;\n\n const path = e.composedPath();\n const activeTabFromEvent = path.find(\n (node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item',\n );\n\n const tabFromEvent = activeTabFromEvent ?? tabs.find((t) => t.getAttribute('value') === selectedValue.value);\n const focused = tabFromEvent ? tabs.indexOf(tabFromEvent) : -1;\n\n if (focused >= 0) listControl.set(focused);\n\n if (listControl.handleKeydown(e)) return;\n\n manualActivationPress.handleKeydown(e);\n };\n\n bind({\n on: {\n click: handleTabClick,\n keydown: handleKeydown,\n },\n });\n\n // ────────────────────────────────────────────────────────────────\n // Lifecycle\n // ────────────────────────────────────────────────────────────────\n\n onMounted(() => {\n const syncSelection = () => {\n ensureSelection();\n updateIndicator();\n };\n\n const tabsSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"tabs\"]');\n\n if (tabsSlot) {\n tabsSlot.addEventListener('slotchange', syncSelection);\n }\n\n syncSelection();\n requestAnimationFrame(syncSelection);\n\n return () => {\n if (tabsSlot) {\n tabsSlot.removeEventListener('slotchange', syncSelection);\n }\n };\n });\n\n return html`\n <div class=\"tablist-wrapper\">\n <div\n role=\"tablist\"\n ref=\"${tablistRef}\"\n part=\"tablist\"\n aria-orientation=\"${props.orientation}\"\n aria-label=\"${props.label}\">\n <slot name=\"tabs\"></slot>\n </div>\n <div class=\"indicator\" ref=\"${indicatorRef}\" part=\"indicator\"></div>\n </div>\n <div class=\"panels\" part=\"panels\">\n <slot></slot>\n </div>\n `;\n },\n styles: [colorThemeMixin, styles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tabs.cjs","names":[],"sources":["../src/disclosure/tabs/tabs.ts"],"sourcesContent":["import {\n bind,\n createContext,\n define,\n getHost,\n html,\n onCleanup,\n onMounted,\n prop,\n provide,\n ref,\n useEmit,\n} from '@vielzeug/ore';\nimport { computed, type Readable, signal, watch } from '@vielzeug/ripple';\nimport { createInteraction, createListControl, elementDirection, lifecycleSignal } from '../../core';\nimport { sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor, VisualVariant } from '../../types';\nimport styles from './tabs.css?inline';\n\n/** Context provided by ore-tabs to its ore-tab-item and ore-tab-panel children. */\nexport type TabsContext = {\n color: Readable<ThemeColor | undefined>;\n density: Readable<'compact' | 'default'>;\n orientation: Readable<'horizontal' | 'vertical'>;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n variant: Readable<VisualVariant | undefined>;\n};\n/** Injection key for the tabs context. */\nexport const TABS_CTX = createContext<TabsContext>('TabsContext');\n\nexport type OreTabsEvents = {\n change: { value: string };\n};\n\nexport type OreTabsProps = {\n /**\n * Keyboard activation mode.\n * - `'auto'` (default): Selecting a tab on arrow-key focus immediately activates it (ARIA recommendation for most cases).\n * - `'manual'`: Arrow keys only move focus; the user must press Enter or Space to activate the focused tab.\n */\n activation?: 'auto' | 'manual';\n /** Theme color */\n color?: ThemeColor;\n /** Spatial density; compact renders a 24px tab control for dense interfaces. */\n density?: 'compact' | 'default';\n /** Accessible label for the tablist (passed as aria-label). Use when there is no visible heading labelling the tabs. */\n label?: string;\n /** Tab list orientation */\n orientation?: 'horizontal' | 'vertical';\n /** Component size */\n size?: ComponentSize;\n /** Currently selected tab value */\n value?: string;\n /** Visual style variant */\n variant?: VisualVariant;\n};\n\n/**\n * Tabs container. Manages tab selection and syncs state to child tab items and panels.\n *\n * @element ore-tabs\n * @element ore-tab-item - Child element for tab buttons (auto-discovered)\n * @element ore-tab-panel - Child element for tab content (auto-discovered)\n *\n * @attr {string} value - The value of the currently selected tab\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'ghost' | 'frost'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} density - Spatial density: 'default' | 'compact'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n *\n * @fires change - Emitted when the active tab changes with detail: { value: string }\n *\n * @slot tabs - Place `ore-tab-item` elements here\n * @slot - Place `ore-tab-panel` elements here\n *\n * @cssprop --tabs-radius - Border radius of the tablist container and panels\n * @cssprop --tabs-transition - Transition duration/easing for the active indicator\n * @cssprop --tabs-indicator-color - Color of the sliding active indicator line\n * @cssprop --tabs-bg - Background of the host element (flat variant)\n * @cssprop --tabs-tablist-bg - Tablist container background (solid/frost variants)\n * @cssprop --tabs-tablist-border-color - Tablist container border color\n * @part tablist - Container that holds the slotted tab items\n * @part indicator - Active tab indicator element\n * @part panels - Container that holds tab panel content\n * @example\n * ```html\n * <ore-tabs value=\"tab1\" variant=\"bordered\">\n * <ore-tab-item slot=\"tabs\" value=\"tab1\">Overview</ore-tab-item>\n * <ore-tab-item slot=\"tabs\" value=\"tab2\">Settings</ore-tab-item>\n * <ore-tab-panel value=\"tab1\"><p>Overview content</p></ore-tab-panel>\n * <ore-tab-panel value=\"tab2\"><p>Settings content</p></ore-tab-panel>\n * </ore-tabs>\n * ```\n */\nexport const TABS_TAG = 'ore-tabs' as const;\ndefine<OreTabsProps>(TABS_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n activation: prop.oneOf(['auto', 'manual'] as const, 'auto'),\n density: prop.oneOf(['compact', 'default'] as const, 'default'),\n label: prop.string(),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n variant: prop.string<VisualVariant>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreTabsEvents>();\n\n const shadowRoot = el.shadowRoot;\n const tablistRef = ref<HTMLElement>();\n const indicatorRef = ref<HTMLElement>();\n const selectedValue = signal<string | undefined>(props.value.value);\n const isManualActivation = () => props.activation.value === 'manual';\n const isVertical = () => props.orientation.value === 'vertical';\n\n bind({\n attr: {\n value: () => selectedValue.value ?? null,\n },\n });\n\n const getTabs = () => [...el.querySelectorAll<HTMLElement>(':scope > ore-tab-item[slot=\"tabs\"]')];\n const getEnabledTabs = () => getTabs().filter((t) => !t.hasAttribute('disabled'));\n const focusTab = (tab: HTMLElement | undefined) => {\n if (!tab) return;\n\n const focusable = tab.shadowRoot?.querySelector<HTMLElement>('[role=\"tab\"]') ?? tab;\n\n focusable.focus();\n };\n\n // ────────────────────────────────────────────────────────────────\n // Selection State Management\n // ────────────────────────────────────────────────────────────────\n\n const setSelection = (value: string | undefined, shouldEmit = false) => {\n selectedValue.value = value;\n\n if (shouldEmit && value) emit('change', { value });\n };\n\n const ensureSelection = () => {\n const tabs = getTabs();\n\n if (tabs.length === 0) return; // No tabs yet, keep current selection\n\n const current = selectedValue.value;\n const hasCurrent = current\n ? tabs.some((tab) => tab.getAttribute('value') === current && !tab.hasAttribute('disabled'))\n : false;\n\n if (hasCurrent) return;\n\n const firstEnabled = tabs.find((tab) => !tab.hasAttribute('disabled'))?.getAttribute('value') ?? undefined;\n\n setSelection(firstEnabled, false);\n };\n\n watch(props.value, (value) => {\n selectedValue.value = value;\n ensureSelection();\n });\n\n // ────────────────────────────────────────────────────────────────\n // List Control for Keyboard Navigation\n // ────────────────────────────────────────────────────────────────\n\n const listControl = createListControl({\n direction: () => elementDirection(el),\n getItems: () => getEnabledTabs(),\n loop: true,\n onNavigate: ({ item }) => {\n focusTab(item);\n\n if (!isManualActivation()) {\n const value = item.getAttribute('value');\n\n if (value) setSelection(value, true);\n }\n },\n orientation: () => (isVertical() ? 'vertical' : 'both'),\n signal: lifecycleSignal(onCleanup),\n });\n\n // ────────────────────────────────────────────────────────────────\n // Context & Indicator Management\n // ────────────────────────────────────────────────────────────────\n\n provide(TABS_CTX, {\n color: props.color,\n density: computed(() => props.density.value ?? 'default'),\n orientation: computed(() => props.orientation.value ?? 'horizontal'),\n size: props.size,\n value: selectedValue,\n variant: props.variant,\n });\n\n const moveIndicator = (activeTab: HTMLElement | undefined) => {\n const indicator = indicatorRef.value;\n const tablist = tablistRef.value;\n\n if (!indicator || !tablist || !activeTab) return;\n\n const tabRect = activeTab.getBoundingClientRect();\n const listRect = tablist.getBoundingClientRect();\n\n if (isVertical()) {\n const y = tabRect.top - listRect.top + tablist.scrollTop;\n\n indicator.style.transition = 'none';\n indicator.style.height = `${tabRect.height}px`;\n indicator.style.width = '';\n indicator.style.left = '0';\n indicator.style.top = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateY(${y}px)`;\n } else {\n const x = tabRect.left - listRect.left + tablist.scrollLeft;\n\n indicator.style.transition = 'none';\n indicator.style.width = `${tabRect.width}px`;\n indicator.style.height = '';\n indicator.style.top = '';\n indicator.style.left = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateX(${x}px)`;\n }\n };\n\n const updateIndicator = () => {\n const value = selectedValue.value;\n\n if (!value) return;\n\n const activeTab = getTabs().find((t) => t.getAttribute('value') === value);\n\n moveIndicator(activeTab);\n };\n\n watch(selectedValue, () => {\n requestAnimationFrame(updateIndicator);\n });\n\n // ────────────────────────────────────────────────────────────────\n // Event Handlers\n // ────────────────────────────────────────────────────────────────\n\n const handleTabClick = (e: Event) => {\n const tab = e\n .composedPath()\n .find((node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item');\n\n if (!tab || tab.hasAttribute('disabled')) return;\n\n // Guard: only respond to tab-items that belong to THIS tabs instance\n if (tab.closest('ore-tabs') !== el) return;\n\n const value = tab.getAttribute('value');\n\n if (!value || value === selectedValue.value) return;\n\n setSelection(value, true);\n };\n\n const activateFocusedTab = (): void => {\n const tabs = getEnabledTabs();\n const focusedTab = tabs.find(\n (tab) => tab === document.activeElement || tab.shadowRoot?.activeElement === document.activeElement,\n );\n const focusedValue = focusedTab?.getAttribute('value');\n\n if (focusedValue && focusedValue !== selectedValue.value) setSelection(focusedValue, true);\n };\n\n const manualActivationPress = createInteraction({\n disabled: computed(() => !isManualActivation()),\n onPress: activateFocusedTab,\n });\n\n const handleKeydown = (e: KeyboardEvent) => {\n const tabs = getEnabledTabs();\n\n if (tabs.length === 0) return;\n\n const path = e.composedPath();\n const activeTabFromEvent = path.find(\n (node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item',\n );\n\n const tabFromEvent = activeTabFromEvent ?? tabs.find((t) => t.getAttribute('value') === selectedValue.value);\n const focused = tabFromEvent ? tabs.indexOf(tabFromEvent) : -1;\n\n if (focused >= 0) listControl.set(focused);\n\n if (listControl.handleKeydown(e)) return;\n\n manualActivationPress.handleKeydown(e);\n };\n\n bind({\n on: {\n click: handleTabClick,\n keydown: handleKeydown,\n },\n });\n\n // ────────────────────────────────────────────────────────────────\n // Lifecycle\n // ────────────────────────────────────────────────────────────────\n\n onMounted(() => {\n const syncSelection = () => {\n ensureSelection();\n updateIndicator();\n };\n\n const tabsSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"tabs\"]');\n\n if (tabsSlot) {\n tabsSlot.addEventListener('slotchange', syncSelection);\n }\n\n syncSelection();\n requestAnimationFrame(syncSelection);\n\n return () => {\n if (tabsSlot) {\n tabsSlot.removeEventListener('slotchange', syncSelection);\n }\n };\n });\n\n return html`\n <div class=\"tablist-wrapper\">\n <div\n role=\"tablist\"\n ref=\"${tablistRef}\"\n part=\"tablist\"\n aria-orientation=\"${props.orientation}\"\n aria-label=\"${props.label}\">\n <slot name=\"tabs\"></slot>\n </div>\n <div class=\"indicator\" ref=\"${indicatorRef}\" part=\"indicator\"></div>\n </div>\n <div class=\"panels\" part=\"panels\">\n <slot></slot>\n </div>\n `;\n },\n styles: [colorThemeMixin, styles],\n});\n"],"mappings":"+XA8BA,IAAa,GAAA,EAAW,EAAA,cAAA,CAA2B,aAAa,EAkEnD,EAAW,YACxB,EAAA,EAAA,OAAA,CAAqB,EAAU,CAC7B,MAAO,CACL,GAAG,EAAA,eACH,GAAG,EAAA,cACH,WAAY,EAAA,KAAK,MAAM,CAAC,OAAQ,QAAQ,EAAY,MAAM,EAC1D,QAAS,EAAA,KAAK,MAAM,CAAC,UAAW,SAAS,EAAY,SAAS,EAC9D,MAAO,EAAA,KAAK,OAAO,EACnB,YAAa,EAAA,KAAK,MAAM,CAAC,aAAc,UAAU,EAAY,YAAY,EACzE,MAAO,EAAA,KAAK,OAAO,EACnB,QAAS,EAAA,KAAK,OAAsB,CACtC,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAK,EAAA,QAAA,CAAQ,EACb,GAAA,EAAO,EAAA,QAAA,CAAuB,EAE9B,EAAa,EAAG,WAChB,GAAA,EAAa,EAAA,IAAA,CAAiB,EAC9B,GAAA,EAAe,EAAA,IAAA,CAAiB,EAChC,GAAA,EAAgB,EAAA,OAAA,CAA2B,EAAM,MAAM,KAAK,EAC5D,MAA2B,EAAM,WAAW,QAAU,SACtD,MAAmB,EAAM,YAAY,QAAU,YAErD,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,UAAa,EAAc,OAAS,IACtC,CACF,CAAC,EAED,IAAM,MAAgB,CAAC,GAAG,EAAG,iBAA8B,oCAAoC,CAAC,EAC1F,MAAuB,EAAQ,CAAC,CAAC,OAAQ,GAAM,CAAC,EAAE,aAAa,UAAU,CAAC,EAC1E,EAAY,GAAiC,CAC5C,IAEa,EAAI,YAAY,cAA2B,cAAc,GAAK,EAAA,CAEtE,MAAM,CAClB,EAMM,GAAgB,EAA2B,EAAa,KAAU,CACtE,EAAc,MAAQ,EAElB,GAAc,GAAO,EAAK,SAAU,CAAE,OAAM,CAAC,CACnD,EAEM,MAAwB,CAC5B,IAAM,EAAO,EAAQ,EAErB,GAAI,EAAK,SAAW,EAAG,OAEvB,IAAM,EAAU,EAAc,MAK9B,GAJmB,GACf,EAAK,KAAM,GAAQ,EAAI,aAAa,OAAO,IAAM,GAAW,CAAC,EAAI,aAAa,UAAU,CAAC,EAG7E,OAEhB,IAAM,EAAe,EAAK,KAAM,GAAQ,CAAC,EAAI,aAAa,UAAU,CAAC,CAAC,EAAE,aAAa,OAAO,GAAK,IAAA,GAEjG,EAAa,EAAc,EAAK,CAClC,GAEA,EAAA,EAAA,MAAA,CAAM,EAAM,MAAQ,GAAU,CAC5B,EAAc,MAAQ,EACtB,EAAgB,CAClB,CAAC,EAMD,IAAM,EAAc,EAAA,kBAAkB,CACpC,cAAiB,EAAA,iBAAiB,CAAE,EACpC,aAAgB,EAAe,EAC/B,KAAM,GACN,YAAa,CAAE,UAAW,CAGxB,GAFA,EAAS,CAAI,EAET,CAAC,EAAmB,EAAG,CACzB,IAAM,EAAQ,EAAK,aAAa,OAAO,EAEnC,GAAO,EAAa,EAAO,EAAI,CACrC,CACF,EACA,gBAAoB,EAAW,EAAI,WAAa,OAChD,OAAQ,EAAA,gBAAgB,EAAA,SAAS,CACnC,CAAC,GAMD,EAAA,EAAA,QAAA,CAAQ,EAAU,CAChB,MAAO,EAAM,MACb,SAAA,EAAS,EAAA,SAAA,KAAe,EAAM,QAAQ,OAAS,SAAS,EACxD,aAAA,EAAa,EAAA,SAAA,KAAe,EAAM,YAAY,OAAS,YAAY,EACnE,KAAM,EAAM,KACZ,MAAO,EACP,QAAS,EAAM,OACjB,CAAC,EAED,IAAM,EAAiB,GAAuC,CAC5D,IAAM,EAAY,EAAa,MACzB,EAAU,EAAW,MAE3B,GAAI,CAAC,GAAa,CAAC,GAAW,CAAC,EAAW,OAE1C,IAAM,EAAU,EAAU,sBAAsB,EAC1C,EAAW,EAAQ,sBAAsB,EAE/C,GAAI,EAAW,EAAG,CAChB,IAAM,EAAI,EAAQ,IAAM,EAAS,IAAM,EAAQ,UAE/C,EAAU,MAAM,WAAa,OAC7B,EAAU,MAAM,OAAS,GAAG,EAAQ,OAAO,IAC3C,EAAU,MAAM,MAAQ,GACxB,EAAU,MAAM,KAAO,IACvB,EAAU,MAAM,IAAM,IACtB,EAAe,YACf,EAAU,MAAM,WAAa,GAC7B,EAAU,MAAM,UAAY,cAAc,EAAE,IAC9C,KAAO,CACL,IAAM,EAAI,EAAQ,KAAO,EAAS,KAAO,EAAQ,WAEjD,EAAU,MAAM,WAAa,OAC7B,EAAU,MAAM,MAAQ,GAAG,EAAQ,MAAM,IACzC,EAAU,MAAM,OAAS,GACzB,EAAU,MAAM,IAAM,GACtB,EAAU,MAAM,KAAO,IACvB,EAAe,YACf,EAAU,MAAM,WAAa,GAC7B,EAAU,MAAM,UAAY,cAAc,EAAE,IAC9C,CACF,EAEM,MAAwB,CAC5B,IAAM,EAAQ,EAAc,MAE5B,GAAI,CAAC,EAAO,OAEZ,IAAM,EAAY,EAAQ,CAAC,CAAC,KAAM,GAAM,EAAE,aAAa,OAAO,IAAM,CAAK,EAEzE,EAAc,CAAS,CACzB,GAEA,EAAA,EAAA,MAAA,CAAM,MAAqB,CACzB,sBAAsB,CAAe,CACvC,CAAC,EAMD,IAAM,EAAkB,GAAa,CACnC,IAAM,EAAM,EACT,aAAa,CAAC,CACd,KAAM,GAA8B,aAAgB,aAAe,EAAK,YAAc,cAAc,EAKvG,GAHI,CAAC,GAAO,EAAI,aAAa,UAAU,GAGnC,EAAI,QAAQ,UAAU,IAAM,EAAI,OAEpC,IAAM,EAAQ,EAAI,aAAa,OAAO,EAEjC,GAAS,IAAU,EAAc,OAEtC,EAAa,EAAO,EAAI,CAC1B,EAYM,EAAwB,EAAA,kBAAkB,CAC9C,UAAA,EAAU,EAAA,SAAA,KAAe,CAAC,EAAmB,CAAC,EAC9C,YAZqC,CAKrC,IAAM,EAJO,EACM,CAAA,CAAK,KACrB,GAAQ,IAAQ,SAAS,eAAiB,EAAI,YAAY,gBAAkB,SAAS,aAEnE,CAAA,EAAY,aAAa,OAAO,EAEjD,GAAgB,IAAiB,EAAc,OAAO,EAAa,EAAc,EAAI,CAC3F,CAKA,CAAC,EAuDD,OAjCA,EAAA,EAAA,KAAA,CAAK,CACH,GAAI,CACF,MAAO,EACP,QAvBmB,GAAqB,CAC1C,IAAM,EAAO,EAAe,EAE5B,GAAI,EAAK,SAAW,EAAG,OAOvB,IAAM,EALO,EAAE,aACY,CAAA,CAAK,KAC7B,GAA8B,aAAgB,aAAe,EAAK,YAAc,cAG9D,GAAsB,EAAK,KAAM,GAAM,EAAE,aAAa,OAAO,IAAM,EAAc,KAAK,EACrG,EAAU,EAAe,EAAK,QAAQ,CAAY,EAAI,GAExD,GAAW,GAAG,EAAY,IAAI,CAAO,EAErC,GAAY,cAAc,CAAC,GAE/B,EAAsB,cAAc,CAAC,CACvC,CAME,CACF,CAAC,GAMD,EAAA,EAAA,UAAA,KAAgB,CACd,IAAM,MAAsB,CAC1B,EAAgB,EAChB,EAAgB,CAClB,EAEM,EAAW,GAAY,cAA+B,mBAAmB,EAS/E,OAPI,GACF,EAAS,iBAAiB,aAAc,CAAa,EAGvD,EAAc,EACd,sBAAsB,CAAa,MAEtB,CACP,GACF,EAAS,oBAAoB,aAAc,CAAa,CAE5D,CACF,CAAC,EAEM,EAAA,IAAI;;;;iBAIE,EAAW;;8BAEE,EAAM,YAAY;wBACxB,EAAM,MAAM;;;sCAGE,EAAa;;;;;KAMjD,EACA,OAAQ,CAAC,EAAA,gBAAiB,EAAA,OAAM,CAClC,CAAC"}
|
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 density: Readable<'compact' | 'default'>;\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 /** Spatial density; compact renders a 24px tab control for dense interfaces. */\n density?: 'compact' | 'default';\n /** Accessible label for the tablist (passed as aria-label). Use when there is no visible heading labelling the tabs. */\n label?: string;\n /** Tab list orientation */\n orientation?: 'horizontal' | 'vertical';\n /** Component size */\n size?: ComponentSize;\n /** Currently selected tab value */\n value?: string;\n /** Visual style variant */\n variant?: 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} density - Spatial density: 'default' | 'compact'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n *\n * @fires change - Emitted when the active tab changes with detail: { value: string }\n *\n * @slot tabs - Place `ore-tab-item` elements here\n * @slot - Place `ore-tab-panel` elements here\n *\n * @cssprop --tabs-radius - Border radius of the tablist container and panels\n * @cssprop --tabs-transition - Transition duration/easing for the active indicator\n * @cssprop --tabs-indicator-color - Color of the sliding active indicator line\n * @cssprop --tabs-bg - Background of the host element (flat variant)\n * @cssprop --tabs-tablist-bg - Tablist container background (solid/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 density: prop.oneOf(['compact', 'default'] as const, 'default'),\n label: prop.string(),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n variant: prop.string<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 density: computed(() => props.density.value ?? 'default'),\n orientation: computed(() => props.orientation.value ?? 'horizontal'),\n size: props.size,\n value: selectedValue,\n variant: props.variant,\n });\n\n const moveIndicator = (activeTab: HTMLElement | undefined) => {\n const indicator = indicatorRef.value;\n const tablist = tablistRef.value;\n\n if (!indicator || !tablist || !activeTab) return;\n\n const tabRect = activeTab.getBoundingClientRect();\n const listRect = tablist.getBoundingClientRect();\n\n if (isVertical()) {\n const y = tabRect.top - listRect.top + tablist.scrollTop;\n\n indicator.style.transition = 'none';\n indicator.style.height = `${tabRect.height}px`;\n indicator.style.width = '';\n indicator.style.left = '0';\n indicator.style.top = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateY(${y}px)`;\n } else {\n const x = tabRect.left - listRect.left + tablist.scrollLeft;\n\n indicator.style.transition = 'none';\n indicator.style.width = `${tabRect.width}px`;\n indicator.style.height = '';\n indicator.style.top = '';\n indicator.style.left = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateX(${x}px)`;\n }\n };\n\n const updateIndicator = () => {\n const value = selectedValue.value;\n\n if (!value) return;\n\n const activeTab = getTabs().find((t) => t.getAttribute('value') === value);\n\n moveIndicator(activeTab);\n };\n\n watch(selectedValue, () => {\n requestAnimationFrame(updateIndicator);\n });\n\n // ────────────────────────────────────────────────────────────────\n // Event Handlers\n // ────────────────────────────────────────────────────────────────\n\n const handleTabClick = (e: Event) => {\n const tab = e\n .composedPath()\n .find((node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item');\n\n if (!tab || tab.hasAttribute('disabled')) return;\n\n // Guard: only respond to tab-items that belong to THIS tabs instance\n if (tab.closest('ore-tabs') !== el) return;\n\n const value = tab.getAttribute('value');\n\n if (!value || value === selectedValue.value) return;\n\n setSelection(value, true);\n };\n\n const activateFocusedTab = (): void => {\n const tabs = getEnabledTabs();\n const focusedTab = tabs.find(\n (tab) => tab === document.activeElement || tab.shadowRoot?.activeElement === document.activeElement,\n );\n const focusedValue = focusedTab?.getAttribute('value');\n\n if (focusedValue && focusedValue !== selectedValue.value) setSelection(focusedValue, true);\n };\n\n const manualActivationPress = createInteraction({\n disabled: computed(() => !isManualActivation()),\n onPress: activateFocusedTab,\n });\n\n const handleKeydown = (e: KeyboardEvent) => {\n const tabs = getEnabledTabs();\n\n if (tabs.length === 0) return;\n\n const path = e.composedPath();\n const activeTabFromEvent = path.find(\n (node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item',\n );\n\n const tabFromEvent = activeTabFromEvent ?? tabs.find((t) => t.getAttribute('value') === selectedValue.value);\n const focused = tabFromEvent ? tabs.indexOf(tabFromEvent) : -1;\n\n if (focused >= 0) listControl.set(focused);\n\n if (listControl.handleKeydown(e)) return;\n\n manualActivationPress.handleKeydown(e);\n };\n\n bind({\n on: {\n click: handleTabClick,\n keydown: handleKeydown,\n },\n });\n\n // ────────────────────────────────────────────────────────────────\n // Lifecycle\n // ────────────────────────────────────────────────────────────────\n\n onMounted(() => {\n const syncSelection = () => {\n ensureSelection();\n updateIndicator();\n };\n\n const tabsSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"tabs\"]');\n\n if (tabsSlot) {\n tabsSlot.addEventListener('slotchange', syncSelection);\n }\n\n syncSelection();\n requestAnimationFrame(syncSelection);\n\n return () => {\n if (tabsSlot) {\n tabsSlot.removeEventListener('slotchange', syncSelection);\n }\n };\n });\n\n return html`\n <div class=\"tablist-wrapper\">\n <div\n role=\"tablist\"\n ref=\"${tablistRef}\"\n part=\"tablist\"\n aria-orientation=\"${props.orientation}\"\n aria-label=\"${props.label}\">\n <slot name=\"tabs\"></slot>\n </div>\n <div class=\"indicator\" ref=\"${indicatorRef}\" part=\"indicator\"></div>\n </div>\n <div class=\"panels\" part=\"panels\">\n <slot></slot>\n </div>\n `;\n },\n styles: [colorThemeMixin, styles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tabs.js","names":[],"sources":["../src/disclosure/tabs/tabs.ts"],"sourcesContent":["import {\n bind,\n createContext,\n define,\n getHost,\n html,\n onCleanup,\n onMounted,\n prop,\n provide,\n ref,\n useEmit,\n} from '@vielzeug/ore';\nimport { computed, type Readable, signal, watch } from '@vielzeug/ripple';\nimport { createInteraction, createListControl, elementDirection, lifecycleSignal } from '../../core';\nimport { sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor, VisualVariant } from '../../types';\nimport styles from './tabs.css?inline';\n\n/** Context provided by ore-tabs to its ore-tab-item and ore-tab-panel children. */\nexport type TabsContext = {\n color: Readable<ThemeColor | undefined>;\n density: Readable<'compact' | 'default'>;\n orientation: Readable<'horizontal' | 'vertical'>;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n variant: Readable<VisualVariant | undefined>;\n};\n/** Injection key for the tabs context. */\nexport const TABS_CTX = createContext<TabsContext>('TabsContext');\n\nexport type OreTabsEvents = {\n change: { value: string };\n};\n\nexport type OreTabsProps = {\n /**\n * Keyboard activation mode.\n * - `'auto'` (default): Selecting a tab on arrow-key focus immediately activates it (ARIA recommendation for most cases).\n * - `'manual'`: Arrow keys only move focus; the user must press Enter or Space to activate the focused tab.\n */\n activation?: 'auto' | 'manual';\n /** Theme color */\n color?: ThemeColor;\n /** Spatial density; compact renders a 24px tab control for dense interfaces. */\n density?: 'compact' | 'default';\n /** Accessible label for the tablist (passed as aria-label). Use when there is no visible heading labelling the tabs. */\n label?: string;\n /** Tab list orientation */\n orientation?: 'horizontal' | 'vertical';\n /** Component size */\n size?: ComponentSize;\n /** Currently selected tab value */\n value?: string;\n /** Visual style variant */\n variant?: VisualVariant;\n};\n\n/**\n * Tabs container. Manages tab selection and syncs state to child tab items and panels.\n *\n * @element ore-tabs\n * @element ore-tab-item - Child element for tab buttons (auto-discovered)\n * @element ore-tab-panel - Child element for tab content (auto-discovered)\n *\n * @attr {string} value - The value of the currently selected tab\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'ghost' | 'frost'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} density - Spatial density: 'default' | 'compact'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n *\n * @fires change - Emitted when the active tab changes with detail: { value: string }\n *\n * @slot tabs - Place `ore-tab-item` elements here\n * @slot - Place `ore-tab-panel` elements here\n *\n * @cssprop --tabs-radius - Border radius of the tablist container and panels\n * @cssprop --tabs-transition - Transition duration/easing for the active indicator\n * @cssprop --tabs-indicator-color - Color of the sliding active indicator line\n * @cssprop --tabs-bg - Background of the host element (flat variant)\n * @cssprop --tabs-tablist-bg - Tablist container background (solid/frost variants)\n * @cssprop --tabs-tablist-border-color - Tablist container border color\n * @part tablist - Container that holds the slotted tab items\n * @part indicator - Active tab indicator element\n * @part panels - Container that holds tab panel content\n * @example\n * ```html\n * <ore-tabs value=\"tab1\" variant=\"bordered\">\n * <ore-tab-item slot=\"tabs\" value=\"tab1\">Overview</ore-tab-item>\n * <ore-tab-item slot=\"tabs\" value=\"tab2\">Settings</ore-tab-item>\n * <ore-tab-panel value=\"tab1\"><p>Overview content</p></ore-tab-panel>\n * <ore-tab-panel value=\"tab2\"><p>Settings content</p></ore-tab-panel>\n * </ore-tabs>\n * ```\n */\nexport const TABS_TAG = 'ore-tabs' as const;\ndefine<OreTabsProps>(TABS_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n activation: prop.oneOf(['auto', 'manual'] as const, 'auto'),\n density: prop.oneOf(['compact', 'default'] as const, 'default'),\n label: prop.string(),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n variant: prop.string<VisualVariant>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreTabsEvents>();\n\n const shadowRoot = el.shadowRoot;\n const tablistRef = ref<HTMLElement>();\n const indicatorRef = ref<HTMLElement>();\n const selectedValue = signal<string | undefined>(props.value.value);\n const isManualActivation = () => props.activation.value === 'manual';\n const isVertical = () => props.orientation.value === 'vertical';\n\n bind({\n attr: {\n value: () => selectedValue.value ?? null,\n },\n });\n\n const getTabs = () => [...el.querySelectorAll<HTMLElement>(':scope > ore-tab-item[slot=\"tabs\"]')];\n const getEnabledTabs = () => getTabs().filter((t) => !t.hasAttribute('disabled'));\n const focusTab = (tab: HTMLElement | undefined) => {\n if (!tab) return;\n\n const focusable = tab.shadowRoot?.querySelector<HTMLElement>('[role=\"tab\"]') ?? tab;\n\n focusable.focus();\n };\n\n // ────────────────────────────────────────────────────────────────\n // Selection State Management\n // ────────────────────────────────────────────────────────────────\n\n const setSelection = (value: string | undefined, shouldEmit = false) => {\n selectedValue.value = value;\n\n if (shouldEmit && value) emit('change', { value });\n };\n\n const ensureSelection = () => {\n const tabs = getTabs();\n\n if (tabs.length === 0) return; // No tabs yet, keep current selection\n\n const current = selectedValue.value;\n const hasCurrent = current\n ? tabs.some((tab) => tab.getAttribute('value') === current && !tab.hasAttribute('disabled'))\n : false;\n\n if (hasCurrent) return;\n\n const firstEnabled = tabs.find((tab) => !tab.hasAttribute('disabled'))?.getAttribute('value') ?? undefined;\n\n setSelection(firstEnabled, false);\n };\n\n watch(props.value, (value) => {\n selectedValue.value = value;\n ensureSelection();\n });\n\n // ────────────────────────────────────────────────────────────────\n // List Control for Keyboard Navigation\n // ────────────────────────────────────────────────────────────────\n\n const listControl = createListControl({\n direction: () => elementDirection(el),\n getItems: () => getEnabledTabs(),\n loop: true,\n onNavigate: ({ item }) => {\n focusTab(item);\n\n if (!isManualActivation()) {\n const value = item.getAttribute('value');\n\n if (value) setSelection(value, true);\n }\n },\n orientation: () => (isVertical() ? 'vertical' : 'both'),\n signal: lifecycleSignal(onCleanup),\n });\n\n // ────────────────────────────────────────────────────────────────\n // Context & Indicator Management\n // ────────────────────────────────────────────────────────────────\n\n provide(TABS_CTX, {\n color: props.color,\n density: computed(() => props.density.value ?? 'default'),\n orientation: computed(() => props.orientation.value ?? 'horizontal'),\n size: props.size,\n value: selectedValue,\n variant: props.variant,\n });\n\n const moveIndicator = (activeTab: HTMLElement | undefined) => {\n const indicator = indicatorRef.value;\n const tablist = tablistRef.value;\n\n if (!indicator || !tablist || !activeTab) return;\n\n const tabRect = activeTab.getBoundingClientRect();\n const listRect = tablist.getBoundingClientRect();\n\n if (isVertical()) {\n const y = tabRect.top - listRect.top + tablist.scrollTop;\n\n indicator.style.transition = 'none';\n indicator.style.height = `${tabRect.height}px`;\n indicator.style.width = '';\n indicator.style.left = '0';\n indicator.style.top = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateY(${y}px)`;\n } else {\n const x = tabRect.left - listRect.left + tablist.scrollLeft;\n\n indicator.style.transition = 'none';\n indicator.style.width = `${tabRect.width}px`;\n indicator.style.height = '';\n indicator.style.top = '';\n indicator.style.left = '0';\n void indicator.offsetWidth;\n indicator.style.transition = '';\n indicator.style.transform = `translateX(${x}px)`;\n }\n };\n\n const updateIndicator = () => {\n const value = selectedValue.value;\n\n if (!value) return;\n\n const activeTab = getTabs().find((t) => t.getAttribute('value') === value);\n\n moveIndicator(activeTab);\n };\n\n watch(selectedValue, () => {\n requestAnimationFrame(updateIndicator);\n });\n\n // ────────────────────────────────────────────────────────────────\n // Event Handlers\n // ────────────────────────────────────────────────────────────────\n\n const handleTabClick = (e: Event) => {\n const tab = e\n .composedPath()\n .find((node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item');\n\n if (!tab || tab.hasAttribute('disabled')) return;\n\n // Guard: only respond to tab-items that belong to THIS tabs instance\n if (tab.closest('ore-tabs') !== el) return;\n\n const value = tab.getAttribute('value');\n\n if (!value || value === selectedValue.value) return;\n\n setSelection(value, true);\n };\n\n const activateFocusedTab = (): void => {\n const tabs = getEnabledTabs();\n const focusedTab = tabs.find(\n (tab) => tab === document.activeElement || tab.shadowRoot?.activeElement === document.activeElement,\n );\n const focusedValue = focusedTab?.getAttribute('value');\n\n if (focusedValue && focusedValue !== selectedValue.value) setSelection(focusedValue, true);\n };\n\n const manualActivationPress = createInteraction({\n disabled: computed(() => !isManualActivation()),\n onPress: activateFocusedTab,\n });\n\n const handleKeydown = (e: KeyboardEvent) => {\n const tabs = getEnabledTabs();\n\n if (tabs.length === 0) return;\n\n const path = e.composedPath();\n const activeTabFromEvent = path.find(\n (node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-tab-item',\n );\n\n const tabFromEvent = activeTabFromEvent ?? tabs.find((t) => t.getAttribute('value') === selectedValue.value);\n const focused = tabFromEvent ? tabs.indexOf(tabFromEvent) : -1;\n\n if (focused >= 0) listControl.set(focused);\n\n if (listControl.handleKeydown(e)) return;\n\n manualActivationPress.handleKeydown(e);\n };\n\n bind({\n on: {\n click: handleTabClick,\n keydown: handleKeydown,\n },\n });\n\n // ────────────────────────────────────────────────────────────────\n // Lifecycle\n // ────────────────────────────────────────────────────────────────\n\n onMounted(() => {\n const syncSelection = () => {\n ensureSelection();\n updateIndicator();\n };\n\n const tabsSlot = shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"tabs\"]');\n\n if (tabsSlot) {\n tabsSlot.addEventListener('slotchange', syncSelection);\n }\n\n syncSelection();\n requestAnimationFrame(syncSelection);\n\n return () => {\n if (tabsSlot) {\n tabsSlot.removeEventListener('slotchange', syncSelection);\n }\n };\n });\n\n return html`\n <div class=\"tablist-wrapper\">\n <div\n role=\"tablist\"\n ref=\"${tablistRef}\"\n part=\"tablist\"\n aria-orientation=\"${props.orientation}\"\n aria-label=\"${props.label}\">\n <slot name=\"tabs\"></slot>\n </div>\n <div class=\"indicator\" ref=\"${indicatorRef}\" part=\"indicator\"></div>\n </div>\n <div class=\"panels\" part=\"panels\">\n <slot></slot>\n </div>\n `;\n },\n styles: [colorThemeMixin, styles],\n});\n"],"mappings":"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"}
|