@vielzeug/refine 2.3.0 → 2.4.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/dist/accordion-item.cjs +1 -1
- package/dist/accordion-item.cjs.map +1 -1
- package/dist/accordion-item.js +1 -1
- package/dist/accordion-item.js.map +1 -1
- package/dist/card.cjs +1 -1
- package/dist/card.cjs.map +1 -1
- package/dist/card.js +1 -1
- package/dist/card.js.map +1 -1
- package/dist/chat-message.cjs +1 -1
- package/dist/chat-message.cjs.map +1 -1
- package/dist/chat-message.js +1 -1
- package/dist/chat-message.js.map +1 -1
- package/dist/combobox.cjs +9 -7
- package/dist/combobox.cjs.map +1 -1
- package/dist/combobox.js +55 -48
- package/dist/combobox.js.map +1 -1
- package/dist/content/index.d.ts +2 -0
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/list/list-item.css.cjs +1 -1
- package/dist/content/list/list-item.css.cjs.map +1 -1
- package/dist/content/list/list-item.css.js +1 -1
- package/dist/content/list/list-item.css.js.map +1 -1
- package/dist/content/list/list-item.d.ts +9 -0
- package/dist/content/list/list-item.d.ts.map +1 -1
- package/dist/content/stats/stats.css.cjs +2 -0
- package/dist/content/stats/stats.css.cjs.map +1 -0
- package/dist/content/stats/stats.css.js +6 -0
- package/dist/content/stats/stats.css.js.map +1 -0
- package/dist/content/stats/stats.d.ts +72 -0
- package/dist/content/stats/stats.d.ts.map +1 -0
- package/dist/core/auto-resize.cjs +1 -1
- package/dist/core/auto-resize.cjs.map +1 -1
- package/dist/core/auto-resize.js +1 -1
- package/dist/core/auto-resize.js.map +1 -1
- package/dist/custom-elements.json +5087 -4791
- package/dist/datagrid.cjs +199 -179
- package/dist/datagrid.cjs.map +1 -1
- package/dist/datagrid.js +435 -321
- package/dist/datagrid.js.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 -0
- 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 +4 -0
- package/dist/disclosure/tabs/tabs.d.ts.map +1 -1
- package/dist/frameworks/elements.d.ts +2 -1
- package/dist/icon.cjs +1 -1
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.js +1 -1
- package/dist/icon.js.map +1 -1
- package/dist/input.cjs +5 -4
- package/dist/input.cjs.map +1 -1
- package/dist/input.js +14 -8
- package/dist/input.js.map +1 -1
- package/dist/inputs/combobox/combobox-options.cjs +1 -1
- package/dist/inputs/combobox/combobox-options.cjs.map +1 -1
- package/dist/inputs/combobox/combobox-options.d.ts.map +1 -1
- package/dist/inputs/combobox/combobox-options.js +1 -0
- package/dist/inputs/combobox/combobox-options.js.map +1 -1
- package/dist/inputs/combobox/combobox.css.cjs +1 -1
- package/dist/inputs/combobox/combobox.css.cjs.map +1 -1
- package/dist/inputs/combobox/combobox.css.js +1 -1
- package/dist/inputs/combobox/combobox.css.js.map +1 -1
- package/dist/inputs/combobox/combobox.d.ts +1 -0
- package/dist/inputs/combobox/combobox.d.ts.map +1 -1
- package/dist/inputs/combobox/combobox.types.d.ts +7 -0
- package/dist/inputs/combobox/combobox.types.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.cjs +1 -1
- package/dist/inputs/datagrid/datagrid-model.cjs.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.d.ts +17 -3
- package/dist/inputs/datagrid/datagrid-model.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.js +93 -70
- package/dist/inputs/datagrid/datagrid-model.js.map +1 -1
- package/dist/inputs/datagrid/datagrid.css.cjs +1 -1
- package/dist/inputs/datagrid/datagrid.css.cjs.map +1 -1
- package/dist/inputs/datagrid/datagrid.css.js +1 -1
- package/dist/inputs/datagrid/datagrid.css.js.map +1 -1
- package/dist/inputs/datagrid/datagrid.d.ts +54 -4
- package/dist/inputs/datagrid/datagrid.d.ts.map +1 -1
- package/dist/inputs/index.d.ts +1 -1
- 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/input/input.d.ts +3 -0
- package/dist/inputs/input/input.d.ts.map +1 -1
- package/dist/inputs/select/select.css.cjs +1 -1
- package/dist/inputs/select/select.css.cjs.map +1 -1
- package/dist/inputs/select/select.css.js +1 -1
- package/dist/inputs/select/select.css.js.map +1 -1
- package/dist/inputs/select/select.d.ts +5 -0
- package/dist/inputs/select/select.d.ts.map +1 -1
- package/dist/layout/navbar/navbar.d.ts +2 -0
- package/dist/layout/navbar/navbar.d.ts.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 +8 -0
- package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
- package/dist/list-item.cjs +6 -5
- package/dist/list-item.cjs.map +1 -1
- package/dist/list-item.js +23 -17
- package/dist/list-item.js.map +1 -1
- package/dist/list.cjs +1 -1
- package/dist/list.cjs.map +1 -1
- package/dist/list.js +1 -1
- package/dist/list.js.map +1 -1
- package/dist/menu.cjs +1 -1
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.js +1 -1
- package/dist/menu.js.map +1 -1
- package/dist/navbar.cjs +2 -2
- package/dist/navbar.cjs.map +1 -1
- package/dist/navbar.js +7 -5
- 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/overlay/popover/popover.css.cjs +1 -1
- package/dist/overlay/popover/popover.css.cjs.map +1 -1
- package/dist/overlay/popover/popover.css.js +1 -1
- package/dist/overlay/popover/popover.css.js.map +1 -1
- package/dist/overlay/popover/popover.d.ts +1 -0
- package/dist/overlay/popover/popover.d.ts.map +1 -1
- package/dist/overlay/shared/use-dialog.cjs +1 -1
- package/dist/overlay/shared/use-dialog.cjs.map +1 -1
- package/dist/overlay/shared/use-dialog.js +1 -1
- package/dist/overlay/shared/use-dialog.js.map +1 -1
- package/dist/overlay/shared/use-floating-trigger.cjs +1 -1
- package/dist/overlay/shared/use-floating-trigger.cjs.map +1 -1
- package/dist/overlay/shared/use-floating-trigger.js +1 -1
- package/dist/overlay/shared/use-floating-trigger.js.map +1 -1
- package/dist/popover.cjs.map +1 -1
- package/dist/popover.js.map +1 -1
- package/dist/radio.cjs +1 -1
- package/dist/radio.cjs.map +1 -1
- package/dist/radio.js +10 -8
- 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 +372 -323
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +372 -323
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +402 -353
- package/dist/refine.js.map +1 -1
- package/dist/select.cjs +16 -14
- package/dist/select.cjs.map +1 -1
- package/dist/select.js +118 -112
- package/dist/select.js.map +1 -1
- package/dist/sidebar.cjs +28 -23
- package/dist/sidebar.cjs.map +1 -1
- package/dist/sidebar.js +112 -77
- package/dist/sidebar.js.map +1 -1
- package/dist/slider.cjs +1 -1
- package/dist/slider.cjs.map +1 -1
- package/dist/slider.js +1 -1
- package/dist/slider.js.map +1 -1
- package/dist/stats.cjs +18 -0
- package/dist/stats.cjs.map +1 -0
- package/dist/stats.js +88 -0
- package/dist/stats.js.map +1 -0
- package/dist/stepper.cjs +1 -1
- package/dist/stepper.cjs.map +1 -1
- package/dist/stepper.js +1 -1
- package/dist/stepper.js.map +1 -1
- package/dist/styles/mixins/animation.css.cjs +2 -0
- package/dist/styles/mixins/animation.css.cjs.map +1 -1
- package/dist/styles/mixins/animation.css.d.ts.map +1 -1
- package/dist/styles/mixins/animation.css.js +2 -0
- package/dist/styles/mixins/animation.css.js.map +1 -1
- package/dist/tab-item.cjs +1 -1
- package/dist/tab-item.cjs.map +1 -1
- package/dist/tab-item.js +3 -2
- package/dist/tab-item.js.map +1 -1
- package/dist/tabs.cjs +1 -1
- package/dist/tabs.cjs.map +1 -1
- package/dist/tabs.js +3 -1
- package/dist/tabs.js.map +1 -1
- package/dist/toast.cjs.map +1 -1
- package/dist/toast.js.map +1 -1
- package/package.json +64 -56
package/dist/slider.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slider.cjs","names":[],"sources":["../src/inputs/slider/slider.ts"],"sourcesContent":["import {\n bind,\n createStableId,\n define,\n getHost,\n html,\n onCleanup,\n onEvent,\n onMounted,\n prop,\n ref,\n useField,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { createErrorHelperState, createSliderControl } from '../../core';\nimport { disablableBundle, SLIDER_SIZE_PRESET, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, disabledStateMixin, reducedMotionMixin, sizeVariantMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport { defineFieldValue, dispatchNativeFieldEvent, setFieldValue } from '../shared/native-field-event';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './slider.css?inline';\n\nconst guard =\n <E extends Event = Event>(condition: () => unknown, handler: (e: E) => void): ((e: E) => void) =>\n (e) => {\n if (condition()) handler(e);\n };\n\n/** Slider component properties */\n\nexport type OreSliderEvents = {\n change: Event;\n input: Event;\n};\n\nexport type OreSliderProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Error message — marks the field as invalid (fallback when the `error` slot is empty) */\n error?: string;\n /** Range mode: lower bound */\n from?: number | string;\n /** Range mode a11y label for the start thumb (e.g. \"$20\") */\n 'from-value-text'?: string;\n /** Stretch to full width of container */\n fullwidth?: boolean;\n /** Helper text displayed below the slider (fallback when the `helper` slot is empty) */\n helper?: string;\n /** Maximum value */\n max?: number | string;\n /** Minimum value */\n min?: number | string;\n /** Single-value mode: form field name */\n name?: string;\n /** Activate two-thumb range selection */\n range?: boolean;\n /** Component size */\n size?: ComponentSize;\n /** Step increment */\n step?: number | string;\n /** Range mode: upper bound */\n to?: number | string;\n /** Range mode a11y label for the end thumb (e.g. \"$80\") */\n 'to-value-text'?: string;\n /** Single-value mode: current value */\n value?: number | string;\n /** Single-value mode a11y label override (e.g. \"75%\"). Overrides raw aria-valuenow. */\n 'value-text'?: string;\n};\n\n/**\n * A slider for selecting a single numeric value or a numeric range.\n *\n * Add the boolean `range` attribute to activate two-thumb range mode.\n *\n * @element ore-slider\n *\n * @attr {number} min - Minimum value (default: 0)\n * @attr {number} max - Maximum value (default: 100)\n * @attr {number} step - Step increment (default: 1)\n * @attr {number} value - Current value (single mode)\n * @attr {number} from - Lower bound (range mode)\n * @attr {number} to - Upper bound (range mode)\n * @attr {boolean} range - Activate range mode\n * @attr {boolean} disabled - Disable interaction\n * @attr {string} name - Form field name (single mode)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {string} helper - Helper text below the slider\n * @attr {string} error - Error message below the slider\n * @attr {boolean} fullwidth - Stretch to full width of container\n *\n * @fires input - Emitted when the slider value changes.\n * @fires change - Emitted when the slider value changes.\n *\n * @slot - Slider label text\n *\n * @part slider - Slider container\n * @part track - Track element\n * @part fill - Fill element\n * @part thumb - Single-value thumb\n * @part thumb-start - Range start thumb\n * @part thumb-end - Range end thumb\n * @part label - Label element\n * @part helper-text - The helper/error text element\n *\n * @cssprop --slider-height - Track height\n * @cssprop --slider-size - Thumb dimensions\n * @cssprop --slider-track-bg - Track background color\n * @cssprop --slider-fill - Active fill color\n * @cssprop --slider-thumb-bg - Thumb background color\n *\n * @example\n * ```html\n * <ore-slider value=\"50\" name=\"volume\">Volume</ore-slider>\n * <ore-slider range from=\"20\" to=\"80\" color=\"primary\">Price range</ore-slider>\n * ```\n */\nexport const SLIDER_TAG = 'ore-slider' as const;\ndefine<OreSliderProps>(SLIDER_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n error: prop.string(),\n from: prop.number(0),\n 'from-value-text': prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n max: prop.number(100),\n min: prop.number(0),\n name: prop.string(),\n range: prop.bool(false),\n step: prop.number(1),\n to: prop.number(100),\n 'to-value-text': prop.string(),\n value: prop.number(0),\n 'value-text': prop.string(),\n },\n setup(props) {\n const el = getHost();\n const slots = useSlots();\n\n // Treat `range` as static — determined at first render\n const isRange = props.range.value;\n // ── Shared helpers ────────────────────────────────────────────\n const sliderControl = createSliderControl({\n max: props.max,\n min: props.min,\n step: props.step,\n });\n // ── Single-value state ────────────────────────────────────────\n const isDragging = signal(false);\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const labelledById = signal<string | undefined>(undefined);\n const assistiveId = createStableId('helper');\n const assistive = createErrorHelperState({ error: props.error, helper: props.helper });\n const errorText = computed(() => assistive.value.errorText);\n const helperText = computed(() => assistive.value.helperText);\n const ariaDescribedBy = computed(() => (errorText.value || helperText.value ? assistiveId : null));\n\n bind({\n attr: {\n 'data-dragging': () => (isDragging.value ? true : undefined),\n size: props.size,\n },\n });\n\n const valueSignal = signal('0');\n\n if (!isRange) {\n useField({ disabled: isDisabled, value: valueSignal });\n watch(\n props.value,\n (v) => {\n valueSignal.value = String(v);\n },\n { immediate: true },\n );\n bind({\n attr: {\n ariaDescribedby: () => ariaDescribedBy.value ?? null,\n ariaDisabled: () => (isDisabled.value ? 'true' : null),\n ariaLabelledby: () => labelledById.value ?? null,\n ariaValuemax: () => sliderControl.max(),\n ariaValuemin: () => sliderControl.min(),\n ariaValuenow: () => Number(valueSignal.value || 0),\n ariaValuetext: () => props['value-text'].value ?? null,\n role: () => 'slider',\n tabindex: () => (isDisabled.value ? null : '0'),\n },\n });\n }\n\n // ── Range state ───────────────────────────────────────────────\n const startVal = signal(0);\n const endVal = signal(100);\n\n defineFieldValue(\n el,\n () => (isRange ? `${startVal.value},${endVal.value}` : valueSignal.value),\n (value) => {\n if (isRange) {\n const [from, to] = value.split(',').map(Number);\n\n if (Number.isFinite(from)) startVal.value = sliderControl.snap(from);\n\n if (Number.isFinite(to)) endVal.value = sliderControl.snap(to);\n\n return;\n }\n\n const next = Number(value);\n\n if (Number.isFinite(next)) valueSignal.value = String(sliderControl.snap(next));\n },\n );\n\n if (isRange) {\n useField<{\n from: number;\n to: number;\n }>({\n disabled: isDisabled,\n toFormValue: ({ from, to }) => {\n const name = props.name.value;\n\n if (!name) return null;\n\n const fd = new FormData();\n\n fd.append(`${name}[from]`, String(from));\n fd.append(`${name}[to]`, String(to));\n\n return fd;\n },\n value: computed(() => ({ from: startVal.value, to: endVal.value })),\n });\n watch(\n props.from,\n (v) => {\n startVal.value = sliderControl.snap(Number(v));\n },\n { immediate: true },\n );\n watch(\n props.to,\n (v) => {\n endVal.value = sliderControl.snap(Number(v));\n },\n { immediate: true },\n );\n }\n\n // ── Refs ──────────────────────────────────────────────────────\n const containerRef = ref<HTMLDivElement>();\n const labelRef = ref<HTMLSpanElement>();\n const trackRef = ref<HTMLDivElement>();\n const thumbStartRef = ref<HTMLDivElement>();\n const thumbEndRef = ref<HTMLDivElement>();\n const startId = createStableId('slider-start');\n const endId = createStableId('slider-end');\n // ── CSS update helpers ────────────────────────────────────────\n const updateSingleCSS = (value: number) => {\n const pct = sliderControl.toPercent(value);\n\n el.style.setProperty('--_thumb-pos', `${pct}%`);\n el.style.setProperty('--_fill-start', '0%');\n el.style.setProperty('--_fill-width', `${pct}%`);\n };\n const updateRangeCSS = () => {\n const s = sliderControl.toPercent(startVal.value);\n const e = sliderControl.toPercent(endVal.value);\n\n el.style.setProperty('--_thumb-start', `${s}%`);\n el.style.setProperty('--_thumb-end', `${e}%`);\n el.style.setProperty('--_fill-start', `${s}%`);\n el.style.setProperty('--_fill-width', `${e - s}%`);\n };\n const emitValueChange = () => {\n const value = isRange ? `${startVal.value},${endVal.value}` : valueSignal.value;\n\n setFieldValue(el, value);\n dispatchNativeFieldEvent(el, 'input');\n dispatchNativeFieldEvent(el, 'change');\n };\n\n // ── Range mode setup ──────────────────────────────────────────\n const setupRangeMode = (container: HTMLDivElement, track: HTMLDivElement) => {\n updateRangeCSS();\n\n const clientToValue = (clientX: number) => {\n const rect = track.getBoundingClientRect();\n\n return sliderControl.fromClientX(clientX, rect);\n };\n let dragging: 'start' | 'end' | null = null;\n const applyDrag = (val: number) => {\n if (dragging === 'start') startVal.value = Math.min(sliderControl.snap(val), endVal.value);\n else if (dragging === 'end') endVal.value = Math.max(sliderControl.snap(val), startVal.value);\n\n startVal.value = sliderControl.clamp(startVal.value);\n endVal.value = sliderControl.clamp(endVal.value);\n updateRangeCSS();\n emitValueChange();\n };\n\n onEvent(\n container,\n 'pointerdown',\n guard(\n () => !isDisabled.value,\n (e: PointerEvent) => {\n e.preventDefault();\n\n const val = clientToValue(e.clientX);\n\n dragging = Math.abs(val - startVal.value) <= Math.abs(val - endVal.value) ? 'start' : 'end';\n (e.target as Element).setPointerCapture(e.pointerId);\n applyDrag(val);\n },\n ),\n );\n onEvent(\n container,\n 'pointermove',\n guard(\n () => !!dragging,\n (e: PointerEvent) => {\n e.preventDefault();\n\n if (!isDragging.value) isDragging.value = true;\n\n applyDrag(clientToValue(e.clientX));\n },\n ),\n );\n onEvent(\n container,\n 'pointerup',\n guard(\n () => !!dragging,\n (e: PointerEvent) => {\n e.preventDefault();\n dragging = null;\n isDragging.value = false;\n (e.target as Element).releasePointerCapture(e.pointerId);\n },\n ),\n );\n\n const makeThumbKeydown = (getVal: () => number, setVal: (v: number) => void) => (e: KeyboardEvent) => {\n if (isDisabled.value) return;\n\n const next = sliderControl.nextFromKey(e.key, getVal());\n\n if (next === null) return;\n\n e.preventDefault();\n setVal(sliderControl.snap(next));\n updateRangeCSS();\n emitValueChange();\n };\n const thumbStartEl = thumbStartRef.value;\n const thumbEndEl = thumbEndRef.value;\n\n if (thumbStartEl) {\n onEvent(\n thumbStartEl,\n 'keydown',\n makeThumbKeydown(\n () => startVal.value,\n (v) => {\n startVal.value = Math.min(v, endVal.value);\n },\n ),\n );\n onCleanup(\n bind(\n {\n aria: {\n label: 'Range start',\n valuemax: () => endVal.value,\n valuemin: () => sliderControl.min(),\n valuenow: () => startVal.value,\n valuetext: () => props['from-value-text'].value ?? null,\n },\n },\n { target: thumbStartEl },\n ),\n );\n }\n\n if (thumbEndEl) {\n onEvent(\n thumbEndEl,\n 'keydown',\n makeThumbKeydown(\n () => endVal.value,\n (v) => {\n endVal.value = Math.max(v, startVal.value);\n },\n ),\n );\n onCleanup(\n bind(\n {\n aria: {\n label: 'Range end',\n valuemax: () => sliderControl.max(),\n valuemin: () => startVal.value,\n valuenow: () => endVal.value,\n valuetext: () => props['to-value-text'].value ?? null,\n },\n },\n { target: thumbEndEl },\n ),\n );\n }\n };\n // ── Single-value mode setup ───────────────────────────────────\n const setupSingleMode = (container: HTMLDivElement, track: HTMLDivElement) => {\n updateSingleCSS(Number(valueSignal.value));\n\n const updateValue = (clientX: number) => {\n if (isDisabled.value) return;\n\n const rect = track.getBoundingClientRect();\n const newValue = sliderControl.fromClientX(clientX, rect);\n\n if (Number(valueSignal.value) !== newValue) {\n valueSignal.value = newValue.toString();\n updateSingleCSS(newValue);\n emitValueChange();\n }\n };\n let isPointerDragging = false;\n\n onEvent(\n container,\n 'pointerdown',\n guard(\n () => !isDisabled.value,\n (e: PointerEvent) => {\n e.preventDefault();\n isPointerDragging = true;\n updateValue(e.clientX);\n (e.target as Element).setPointerCapture(e.pointerId);\n },\n ),\n );\n onEvent(\n container,\n 'pointermove',\n guard(\n () => isPointerDragging,\n (e: PointerEvent) => {\n e.preventDefault();\n\n if (!isDragging.value) isDragging.value = true;\n\n updateValue(e.clientX);\n },\n ),\n );\n onEvent(\n container,\n 'pointerup',\n guard(\n () => isPointerDragging,\n (e: PointerEvent) => {\n e.preventDefault();\n isPointerDragging = false;\n isDragging.value = false;\n (e.target as Element).releasePointerCapture(e.pointerId);\n },\n ),\n );\n onEvent(\n el,\n 'keydown',\n guard(\n () => !isDisabled.value,\n (e: KeyboardEvent) => {\n const val = Number(valueSignal.value || 0);\n const next = sliderControl.nextFromKey(e.key, val);\n\n if (next === null) return;\n\n e.preventDefault();\n\n const newValue = sliderControl.clamp(next);\n\n if (newValue !== val) {\n valueSignal.value = newValue.toString();\n updateSingleCSS(newValue);\n emitValueChange();\n }\n },\n ),\n );\n };\n\n onMounted(() => {\n const container = containerRef.value;\n const track = trackRef.value;\n\n if (!container || !track) return;\n\n if (slots.has().value && labelRef.value) {\n const labelId = createStableId('slider-label');\n\n labelRef.value.id = labelId;\n\n if (!isRange) labelledById.value = labelId;\n }\n\n if (isRange) setupRangeMode(container, track);\n else setupSingleMode(container, track);\n });\n\n return html`\n <div class=\"slider-container\" part=\"slider\" ref=${containerRef}>\n <div class=\"slider-track\" part=\"track\" ref=${trackRef}>\n <div class=\"slider-fill\" part=\"fill\"></div>\n <div class=\"slider-thumb slider-thumb-sole\" part=\"thumb\"></div>\n <div\n class=\"slider-thumb slider-thumb-start\"\n part=\"thumb-start\"\n ref=${thumbStartRef}\n role=\"slider\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\"\n aria-describedby=\"${ariaDescribedBy}\"\n id=\"${startId}\"></div>\n <div\n class=\"slider-thumb slider-thumb-end\"\n part=\"thumb-end\"\n ref=${thumbEndRef}\n role=\"slider\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\"\n aria-describedby=\"${ariaDescribedBy}\"\n id=\"${endId}\"></div>\n </div>\n </div>\n <span class=\"label\" part=\"label\" ref=${labelRef}><slot></slot></span>\n ${renderHelperRegion(assistiveId, errorText, helperText)}\n `;\n },\n styles: [\n disabledStateMixin,\n colorThemeMixin,\n sizeVariantMixin(SLIDER_SIZE_PRESET),\n componentStyles,\n reducedMotionMixin,\n ],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"slider.cjs","names":[],"sources":["../src/inputs/slider/slider.ts"],"sourcesContent":["import {\n bind,\n createStableId,\n define,\n getHost,\n html,\n onCleanup,\n onEvent,\n onMounted,\n prop,\n ref,\n useField,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { createErrorHelperState, createSliderControl } from '../../core';\nimport { disablableBundle, SLIDER_SIZE_PRESET, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, disabledStateMixin, reducedMotionMixin, sizeVariantMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport { defineFieldValue, dispatchNativeFieldEvent, setFieldValue } from '../shared/native-field-event';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './slider.css?inline';\n\nconst guard =\n <E extends Event = Event>(condition: () => unknown, handler: (e: E) => void): ((e: E) => void) =>\n (e) => {\n if (condition()) handler(e);\n };\n\n/** Slider component properties */\n\nexport type OreSliderEvents = {\n change: Event;\n input: Event;\n};\n\nexport type OreSliderProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Error message — marks the field as invalid (fallback when the `error` slot is empty) */\n error?: string;\n /** Range mode: lower bound */\n from?: number | string;\n /** Range mode a11y label for the start thumb (e.g. \"$20\") */\n 'from-value-text'?: string;\n /** Stretch to full width of container */\n fullwidth?: boolean;\n /** Helper text displayed below the slider (fallback when the `helper` slot is empty) */\n helper?: string;\n /** Maximum value */\n max?: number | string;\n /** Minimum value */\n min?: number | string;\n /** Single-value mode: form field name */\n name?: string;\n /** Activate two-thumb range selection */\n range?: boolean;\n /** Component size */\n size?: ComponentSize;\n /** Step increment */\n step?: number | string;\n /** Range mode: upper bound */\n to?: number | string;\n /** Range mode a11y label for the end thumb (e.g. \"$80\") */\n 'to-value-text'?: string;\n /** Single-value mode: current value */\n value?: number | string;\n /** Single-value mode a11y label override (e.g. \"75%\"). Overrides raw aria-valuenow. */\n 'value-text'?: string;\n};\n\n/**\n * A slider for selecting a single numeric value or a numeric range.\n *\n * Add the boolean `range` attribute to activate two-thumb range mode.\n *\n * @element ore-slider\n *\n * @attr {number} min - Minimum value (default: 0)\n * @attr {number} max - Maximum value (default: 100)\n * @attr {number} step - Step increment (default: 1)\n * @attr {number} value - Current value (single mode)\n * @attr {number} from - Lower bound (range mode)\n * @attr {number} to - Upper bound (range mode)\n * @attr {boolean} range - Activate range mode\n * @attr {boolean} disabled - Disable interaction\n * @attr {string} name - Form field name (single mode)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {string} helper - Helper text below the slider\n * @attr {string} error - Error message below the slider\n * @attr {boolean} fullwidth - Stretch to full width of container\n *\n * @fires input - Emitted when the slider value changes.\n * @fires change - Emitted when the slider value changes.\n *\n * @slot - Slider label text\n *\n * @part slider - Slider container\n * @part track - Track element\n * @part fill - Fill element\n * @part thumb - Single-value thumb\n * @part thumb-start - Range start thumb\n * @part thumb-end - Range end thumb\n * @part label - Label element\n * @part helper-text - The helper/error text element\n *\n * @cssprop --slider-height - Track height\n * @cssprop --slider-size - Thumb dimensions\n * @cssprop --slider-track-bg - Track background color\n * @cssprop --slider-fill - Active fill color\n * @cssprop --slider-thumb-bg - Thumb background color\n *\n * @example\n * ```html\n * <ore-slider value=\"50\" name=\"volume\">Volume</ore-slider>\n * <ore-slider range from=\"20\" to=\"80\" color=\"primary\">Price range</ore-slider>\n * ```\n */\nexport const SLIDER_TAG = 'ore-slider' as const;\ndefine<OreSliderProps>(SLIDER_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n error: prop.string(),\n from: prop.number(0),\n 'from-value-text': prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n max: prop.number(100),\n min: prop.number(0),\n name: prop.string(),\n range: prop.bool(false),\n step: prop.number(1),\n to: prop.number(100),\n 'to-value-text': prop.string(),\n value: prop.number(0),\n 'value-text': prop.string(),\n },\n setup(props) {\n const el = getHost();\n const slots = useSlots();\n\n // Treat `range` as static — determined at first render\n const isRange = props.range.value;\n // ── Shared helpers ────────────────────────────────────────────\n const sliderControl = createSliderControl({\n max: props.max,\n min: props.min,\n step: props.step,\n });\n // ── Single-value state ────────────────────────────────────────\n const isDragging = signal(false);\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const labelledById = signal<string | undefined>(undefined);\n const assistiveId = createStableId('helper');\n const assistive = createErrorHelperState({ error: props.error, helper: props.helper });\n const errorText = computed(() => assistive.value.errorText);\n const helperText = computed(() => assistive.value.helperText);\n const ariaDescribedBy = computed(() => (errorText.value || helperText.value ? assistiveId : null));\n\n bind({\n attr: {\n 'data-dragging': () => (isDragging.value ? true : undefined),\n size: props.size,\n },\n });\n\n const valueSignal = signal('0');\n\n if (!isRange) {\n useField({ disabled: isDisabled, value: valueSignal });\n watch(\n props.value,\n (v) => {\n valueSignal.value = String(v);\n },\n { immediate: true },\n );\n bind({\n attr: {\n ariaDescribedby: () => ariaDescribedBy.value ?? null,\n ariaDisabled: () => (isDisabled.value ? 'true' : null),\n ariaLabelledby: () => labelledById.value ?? null,\n ariaValuemax: () => sliderControl.max(),\n ariaValuemin: () => sliderControl.min(),\n ariaValuenow: () => Number(valueSignal.value || 0),\n ariaValuetext: () => props['value-text'].value ?? null,\n role: () => 'slider',\n tabindex: () => (isDisabled.value ? null : '0'),\n },\n });\n }\n\n // ── Range state ───────────────────────────────────────────────\n const startVal = signal(0);\n const endVal = signal(100);\n\n defineFieldValue(\n el,\n () => (isRange ? `${startVal.value},${endVal.value}` : valueSignal.value),\n (value) => {\n if (isRange) {\n const [from, to] = value.split(',').map(Number);\n\n if (Number.isFinite(from)) startVal.value = sliderControl.snap(from);\n\n if (Number.isFinite(to)) endVal.value = sliderControl.snap(to);\n\n return;\n }\n\n const next = Number(value);\n\n if (Number.isFinite(next)) valueSignal.value = String(sliderControl.snap(next));\n },\n );\n\n if (isRange) {\n useField<{\n from: number;\n to: number;\n }>({\n disabled: isDisabled,\n toFormValue: ({ from, to }) => {\n const name = props.name.value;\n\n if (!name) return null;\n\n const fd = new FormData();\n\n fd.append(`${name}[from]`, String(from));\n fd.append(`${name}[to]`, String(to));\n\n return fd;\n },\n value: computed(() => ({ from: startVal.value, to: endVal.value })),\n });\n watch(\n props.from,\n (v) => {\n startVal.value = sliderControl.snap(Number(v));\n },\n { immediate: true },\n );\n watch(\n props.to,\n (v) => {\n endVal.value = sliderControl.snap(Number(v));\n },\n { immediate: true },\n );\n }\n\n // ── Refs ──────────────────────────────────────────────────────\n const containerRef = ref<HTMLDivElement>();\n const labelRef = ref<HTMLSpanElement>();\n const trackRef = ref<HTMLDivElement>();\n const thumbStartRef = ref<HTMLDivElement>();\n const thumbEndRef = ref<HTMLDivElement>();\n const startId = createStableId('slider-start');\n const endId = createStableId('slider-end');\n // ── CSS update helpers ────────────────────────────────────────\n const updateSingleCSS = (value: number) => {\n const pct = sliderControl.toPercent(value);\n\n el.style.setProperty('--_thumb-pos', `${pct}%`);\n el.style.setProperty('--_fill-start', '0%');\n el.style.setProperty('--_fill-width', `${pct}%`);\n };\n const updateRangeCSS = () => {\n const s = sliderControl.toPercent(startVal.value);\n const e = sliderControl.toPercent(endVal.value);\n\n el.style.setProperty('--_thumb-start', `${s}%`);\n el.style.setProperty('--_thumb-end', `${e}%`);\n el.style.setProperty('--_fill-start', `${s}%`);\n el.style.setProperty('--_fill-width', `${e - s}%`);\n };\n const emitValueChange = () => {\n const value = isRange ? `${startVal.value},${endVal.value}` : valueSignal.value;\n\n setFieldValue(el, value);\n dispatchNativeFieldEvent(el, 'input');\n dispatchNativeFieldEvent(el, 'change');\n };\n\n // ── Range mode setup ──────────────────────────────────────────\n const setupRangeMode = (container: HTMLDivElement, track: HTMLDivElement) => {\n updateRangeCSS();\n\n const clientToValue = (clientX: number) => {\n const rect = track.getBoundingClientRect();\n\n return sliderControl.fromClientX(clientX, rect);\n };\n let dragging: 'start' | 'end' | null = null;\n const applyDrag = (val: number) => {\n if (dragging === 'start') startVal.value = Math.min(sliderControl.snap(val), endVal.value);\n else if (dragging === 'end') endVal.value = Math.max(sliderControl.snap(val), startVal.value);\n\n startVal.value = sliderControl.clamp(startVal.value);\n endVal.value = sliderControl.clamp(endVal.value);\n updateRangeCSS();\n emitValueChange();\n };\n\n onEvent(\n container,\n 'pointerdown',\n guard(\n () => !isDisabled.value,\n (e: PointerEvent) => {\n e.preventDefault();\n\n const val = clientToValue(e.clientX);\n\n dragging = Math.abs(val - startVal.value) <= Math.abs(val - endVal.value) ? 'start' : 'end';\n (e.target as Element).setPointerCapture(e.pointerId);\n applyDrag(val);\n },\n ),\n );\n onEvent(\n container,\n 'pointermove',\n guard(\n () => !!dragging,\n (e: PointerEvent) => {\n e.preventDefault();\n\n if (!isDragging.value) isDragging.value = true;\n\n applyDrag(clientToValue(e.clientX));\n },\n ),\n );\n onEvent(\n container,\n 'pointerup',\n guard(\n () => !!dragging,\n (e: PointerEvent) => {\n e.preventDefault();\n dragging = null;\n isDragging.value = false;\n (e.target as Element).releasePointerCapture(e.pointerId);\n },\n ),\n );\n\n const makeThumbKeydown = (getVal: () => number, setVal: (v: number) => void) => (e: KeyboardEvent) => {\n if (isDisabled.value) return;\n\n const next = sliderControl.nextFromKey(e.key, getVal());\n\n if (next === null) return;\n\n e.preventDefault();\n setVal(sliderControl.snap(next));\n updateRangeCSS();\n emitValueChange();\n };\n const thumbStartEl = thumbStartRef.value;\n const thumbEndEl = thumbEndRef.value;\n\n if (thumbStartEl) {\n onEvent(\n thumbStartEl,\n 'keydown',\n makeThumbKeydown(\n () => startVal.value,\n (v) => {\n startVal.value = Math.min(v, endVal.value);\n },\n ),\n );\n onCleanup(\n bind(\n {\n aria: {\n label: 'Range start',\n valuemax: () => endVal.value,\n valuemin: () => sliderControl.min(),\n valuenow: () => startVal.value,\n valuetext: () => props['from-value-text'].value ?? null,\n },\n },\n { target: thumbStartEl },\n ),\n );\n }\n\n if (thumbEndEl) {\n onEvent(\n thumbEndEl,\n 'keydown',\n makeThumbKeydown(\n () => endVal.value,\n (v) => {\n endVal.value = Math.max(v, startVal.value);\n },\n ),\n );\n onCleanup(\n bind(\n {\n aria: {\n label: 'Range end',\n valuemax: () => sliderControl.max(),\n valuemin: () => startVal.value,\n valuenow: () => endVal.value,\n valuetext: () => props['to-value-text'].value ?? null,\n },\n },\n { target: thumbEndEl },\n ),\n );\n }\n };\n // ── Single-value mode setup ───────────────────────────────────\n const setupSingleMode = (container: HTMLDivElement, track: HTMLDivElement) => {\n updateSingleCSS(Number(valueSignal.value));\n\n const updateValue = (clientX: number) => {\n if (isDisabled.value) return;\n\n const rect = track.getBoundingClientRect();\n const newValue = sliderControl.fromClientX(clientX, rect);\n\n if (Number(valueSignal.value) !== newValue) {\n valueSignal.value = newValue.toString();\n updateSingleCSS(newValue);\n emitValueChange();\n }\n };\n let isPointerDragging = false;\n\n onEvent(\n container,\n 'pointerdown',\n guard(\n () => !isDisabled.value,\n (e: PointerEvent) => {\n e.preventDefault();\n isPointerDragging = true;\n updateValue(e.clientX);\n (e.target as Element).setPointerCapture(e.pointerId);\n },\n ),\n );\n onEvent(\n container,\n 'pointermove',\n guard(\n () => isPointerDragging,\n (e: PointerEvent) => {\n e.preventDefault();\n\n if (!isDragging.value) isDragging.value = true;\n\n updateValue(e.clientX);\n },\n ),\n );\n onEvent(\n container,\n 'pointerup',\n guard(\n () => isPointerDragging,\n (e: PointerEvent) => {\n e.preventDefault();\n isPointerDragging = false;\n isDragging.value = false;\n (e.target as Element).releasePointerCapture(e.pointerId);\n },\n ),\n );\n onEvent(\n el,\n 'keydown',\n guard(\n () => !isDisabled.value,\n (e: KeyboardEvent) => {\n const val = Number(valueSignal.value || 0);\n const next = sliderControl.nextFromKey(e.key, val);\n\n if (next === null) return;\n\n e.preventDefault();\n\n const newValue = sliderControl.clamp(next);\n\n if (newValue !== val) {\n valueSignal.value = newValue.toString();\n updateSingleCSS(newValue);\n emitValueChange();\n }\n },\n ),\n );\n };\n\n onMounted(() => {\n const container = containerRef.value;\n const track = trackRef.value;\n\n if (!container || !track) return;\n\n if (slots.has().value && labelRef.value) {\n const labelId = createStableId('slider-label');\n\n labelRef.value.id = labelId;\n\n if (!isRange) labelledById.value = labelId;\n }\n\n if (isRange) setupRangeMode(container, track);\n else setupSingleMode(container, track);\n });\n\n return html`\n <div class=\"slider-container\" part=\"slider\" ref=${containerRef}>\n <div class=\"slider-track\" part=\"track\" ref=${trackRef}>\n <div class=\"slider-fill\" part=\"fill\"></div>\n <div class=\"slider-thumb slider-thumb-sole\" part=\"thumb\"></div>\n <div\n class=\"slider-thumb slider-thumb-start\"\n part=\"thumb-start\"\n ref=${thumbStartRef}\n role=\"slider\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\"\n aria-describedby=\"${ariaDescribedBy}\"\n id=\"${startId}\"></div>\n <div\n class=\"slider-thumb slider-thumb-end\"\n part=\"thumb-end\"\n ref=${thumbEndRef}\n role=\"slider\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\"\n aria-describedby=\"${ariaDescribedBy}\"\n id=\"${endId}\"></div>\n </div>\n </div>\n <span class=\"label\" part=\"label\" ref=${labelRef}><slot></slot></span>\n ${renderHelperRegion(assistiveId, errorText, helperText)}\n `;\n },\n styles: [\n disabledStateMixin,\n colorThemeMixin,\n sizeVariantMixin(SLIDER_SIZE_PRESET),\n componentStyles,\n reducedMotionMixin,\n ],\n});\n"],"mappings":"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"}
|
package/dist/slider.js
CHANGED
package/dist/slider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slider.js","names":[],"sources":["../src/inputs/slider/slider.ts"],"sourcesContent":["import {\n bind,\n createStableId,\n define,\n getHost,\n html,\n onCleanup,\n onEvent,\n onMounted,\n prop,\n ref,\n useField,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { createErrorHelperState, createSliderControl } from '../../core';\nimport { disablableBundle, SLIDER_SIZE_PRESET, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, disabledStateMixin, reducedMotionMixin, sizeVariantMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport { defineFieldValue, dispatchNativeFieldEvent, setFieldValue } from '../shared/native-field-event';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './slider.css?inline';\n\nconst guard =\n <E extends Event = Event>(condition: () => unknown, handler: (e: E) => void): ((e: E) => void) =>\n (e) => {\n if (condition()) handler(e);\n };\n\n/** Slider component properties */\n\nexport type OreSliderEvents = {\n change: Event;\n input: Event;\n};\n\nexport type OreSliderProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Error message — marks the field as invalid (fallback when the `error` slot is empty) */\n error?: string;\n /** Range mode: lower bound */\n from?: number | string;\n /** Range mode a11y label for the start thumb (e.g. \"$20\") */\n 'from-value-text'?: string;\n /** Stretch to full width of container */\n fullwidth?: boolean;\n /** Helper text displayed below the slider (fallback when the `helper` slot is empty) */\n helper?: string;\n /** Maximum value */\n max?: number | string;\n /** Minimum value */\n min?: number | string;\n /** Single-value mode: form field name */\n name?: string;\n /** Activate two-thumb range selection */\n range?: boolean;\n /** Component size */\n size?: ComponentSize;\n /** Step increment */\n step?: number | string;\n /** Range mode: upper bound */\n to?: number | string;\n /** Range mode a11y label for the end thumb (e.g. \"$80\") */\n 'to-value-text'?: string;\n /** Single-value mode: current value */\n value?: number | string;\n /** Single-value mode a11y label override (e.g. \"75%\"). Overrides raw aria-valuenow. */\n 'value-text'?: string;\n};\n\n/**\n * A slider for selecting a single numeric value or a numeric range.\n *\n * Add the boolean `range` attribute to activate two-thumb range mode.\n *\n * @element ore-slider\n *\n * @attr {number} min - Minimum value (default: 0)\n * @attr {number} max - Maximum value (default: 100)\n * @attr {number} step - Step increment (default: 1)\n * @attr {number} value - Current value (single mode)\n * @attr {number} from - Lower bound (range mode)\n * @attr {number} to - Upper bound (range mode)\n * @attr {boolean} range - Activate range mode\n * @attr {boolean} disabled - Disable interaction\n * @attr {string} name - Form field name (single mode)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {string} helper - Helper text below the slider\n * @attr {string} error - Error message below the slider\n * @attr {boolean} fullwidth - Stretch to full width of container\n *\n * @fires input - Emitted when the slider value changes.\n * @fires change - Emitted when the slider value changes.\n *\n * @slot - Slider label text\n *\n * @part slider - Slider container\n * @part track - Track element\n * @part fill - Fill element\n * @part thumb - Single-value thumb\n * @part thumb-start - Range start thumb\n * @part thumb-end - Range end thumb\n * @part label - Label element\n * @part helper-text - The helper/error text element\n *\n * @cssprop --slider-height - Track height\n * @cssprop --slider-size - Thumb dimensions\n * @cssprop --slider-track-bg - Track background color\n * @cssprop --slider-fill - Active fill color\n * @cssprop --slider-thumb-bg - Thumb background color\n *\n * @example\n * ```html\n * <ore-slider value=\"50\" name=\"volume\">Volume</ore-slider>\n * <ore-slider range from=\"20\" to=\"80\" color=\"primary\">Price range</ore-slider>\n * ```\n */\nexport const SLIDER_TAG = 'ore-slider' as const;\ndefine<OreSliderProps>(SLIDER_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n error: prop.string(),\n from: prop.number(0),\n 'from-value-text': prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n max: prop.number(100),\n min: prop.number(0),\n name: prop.string(),\n range: prop.bool(false),\n step: prop.number(1),\n to: prop.number(100),\n 'to-value-text': prop.string(),\n value: prop.number(0),\n 'value-text': prop.string(),\n },\n setup(props) {\n const el = getHost();\n const slots = useSlots();\n\n // Treat `range` as static — determined at first render\n const isRange = props.range.value;\n // ── Shared helpers ────────────────────────────────────────────\n const sliderControl = createSliderControl({\n max: props.max,\n min: props.min,\n step: props.step,\n });\n // ── Single-value state ────────────────────────────────────────\n const isDragging = signal(false);\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const labelledById = signal<string | undefined>(undefined);\n const assistiveId = createStableId('helper');\n const assistive = createErrorHelperState({ error: props.error, helper: props.helper });\n const errorText = computed(() => assistive.value.errorText);\n const helperText = computed(() => assistive.value.helperText);\n const ariaDescribedBy = computed(() => (errorText.value || helperText.value ? assistiveId : null));\n\n bind({\n attr: {\n 'data-dragging': () => (isDragging.value ? true : undefined),\n size: props.size,\n },\n });\n\n const valueSignal = signal('0');\n\n if (!isRange) {\n useField({ disabled: isDisabled, value: valueSignal });\n watch(\n props.value,\n (v) => {\n valueSignal.value = String(v);\n },\n { immediate: true },\n );\n bind({\n attr: {\n ariaDescribedby: () => ariaDescribedBy.value ?? null,\n ariaDisabled: () => (isDisabled.value ? 'true' : null),\n ariaLabelledby: () => labelledById.value ?? null,\n ariaValuemax: () => sliderControl.max(),\n ariaValuemin: () => sliderControl.min(),\n ariaValuenow: () => Number(valueSignal.value || 0),\n ariaValuetext: () => props['value-text'].value ?? null,\n role: () => 'slider',\n tabindex: () => (isDisabled.value ? null : '0'),\n },\n });\n }\n\n // ── Range state ───────────────────────────────────────────────\n const startVal = signal(0);\n const endVal = signal(100);\n\n defineFieldValue(\n el,\n () => (isRange ? `${startVal.value},${endVal.value}` : valueSignal.value),\n (value) => {\n if (isRange) {\n const [from, to] = value.split(',').map(Number);\n\n if (Number.isFinite(from)) startVal.value = sliderControl.snap(from);\n\n if (Number.isFinite(to)) endVal.value = sliderControl.snap(to);\n\n return;\n }\n\n const next = Number(value);\n\n if (Number.isFinite(next)) valueSignal.value = String(sliderControl.snap(next));\n },\n );\n\n if (isRange) {\n useField<{\n from: number;\n to: number;\n }>({\n disabled: isDisabled,\n toFormValue: ({ from, to }) => {\n const name = props.name.value;\n\n if (!name) return null;\n\n const fd = new FormData();\n\n fd.append(`${name}[from]`, String(from));\n fd.append(`${name}[to]`, String(to));\n\n return fd;\n },\n value: computed(() => ({ from: startVal.value, to: endVal.value })),\n });\n watch(\n props.from,\n (v) => {\n startVal.value = sliderControl.snap(Number(v));\n },\n { immediate: true },\n );\n watch(\n props.to,\n (v) => {\n endVal.value = sliderControl.snap(Number(v));\n },\n { immediate: true },\n );\n }\n\n // ── Refs ──────────────────────────────────────────────────────\n const containerRef = ref<HTMLDivElement>();\n const labelRef = ref<HTMLSpanElement>();\n const trackRef = ref<HTMLDivElement>();\n const thumbStartRef = ref<HTMLDivElement>();\n const thumbEndRef = ref<HTMLDivElement>();\n const startId = createStableId('slider-start');\n const endId = createStableId('slider-end');\n // ── CSS update helpers ────────────────────────────────────────\n const updateSingleCSS = (value: number) => {\n const pct = sliderControl.toPercent(value);\n\n el.style.setProperty('--_thumb-pos', `${pct}%`);\n el.style.setProperty('--_fill-start', '0%');\n el.style.setProperty('--_fill-width', `${pct}%`);\n };\n const updateRangeCSS = () => {\n const s = sliderControl.toPercent(startVal.value);\n const e = sliderControl.toPercent(endVal.value);\n\n el.style.setProperty('--_thumb-start', `${s}%`);\n el.style.setProperty('--_thumb-end', `${e}%`);\n el.style.setProperty('--_fill-start', `${s}%`);\n el.style.setProperty('--_fill-width', `${e - s}%`);\n };\n const emitValueChange = () => {\n const value = isRange ? `${startVal.value},${endVal.value}` : valueSignal.value;\n\n setFieldValue(el, value);\n dispatchNativeFieldEvent(el, 'input');\n dispatchNativeFieldEvent(el, 'change');\n };\n\n // ── Range mode setup ──────────────────────────────────────────\n const setupRangeMode = (container: HTMLDivElement, track: HTMLDivElement) => {\n updateRangeCSS();\n\n const clientToValue = (clientX: number) => {\n const rect = track.getBoundingClientRect();\n\n return sliderControl.fromClientX(clientX, rect);\n };\n let dragging: 'start' | 'end' | null = null;\n const applyDrag = (val: number) => {\n if (dragging === 'start') startVal.value = Math.min(sliderControl.snap(val), endVal.value);\n else if (dragging === 'end') endVal.value = Math.max(sliderControl.snap(val), startVal.value);\n\n startVal.value = sliderControl.clamp(startVal.value);\n endVal.value = sliderControl.clamp(endVal.value);\n updateRangeCSS();\n emitValueChange();\n };\n\n onEvent(\n container,\n 'pointerdown',\n guard(\n () => !isDisabled.value,\n (e: PointerEvent) => {\n e.preventDefault();\n\n const val = clientToValue(e.clientX);\n\n dragging = Math.abs(val - startVal.value) <= Math.abs(val - endVal.value) ? 'start' : 'end';\n (e.target as Element).setPointerCapture(e.pointerId);\n applyDrag(val);\n },\n ),\n );\n onEvent(\n container,\n 'pointermove',\n guard(\n () => !!dragging,\n (e: PointerEvent) => {\n e.preventDefault();\n\n if (!isDragging.value) isDragging.value = true;\n\n applyDrag(clientToValue(e.clientX));\n },\n ),\n );\n onEvent(\n container,\n 'pointerup',\n guard(\n () => !!dragging,\n (e: PointerEvent) => {\n e.preventDefault();\n dragging = null;\n isDragging.value = false;\n (e.target as Element).releasePointerCapture(e.pointerId);\n },\n ),\n );\n\n const makeThumbKeydown = (getVal: () => number, setVal: (v: number) => void) => (e: KeyboardEvent) => {\n if (isDisabled.value) return;\n\n const next = sliderControl.nextFromKey(e.key, getVal());\n\n if (next === null) return;\n\n e.preventDefault();\n setVal(sliderControl.snap(next));\n updateRangeCSS();\n emitValueChange();\n };\n const thumbStartEl = thumbStartRef.value;\n const thumbEndEl = thumbEndRef.value;\n\n if (thumbStartEl) {\n onEvent(\n thumbStartEl,\n 'keydown',\n makeThumbKeydown(\n () => startVal.value,\n (v) => {\n startVal.value = Math.min(v, endVal.value);\n },\n ),\n );\n onCleanup(\n bind(\n {\n aria: {\n label: 'Range start',\n valuemax: () => endVal.value,\n valuemin: () => sliderControl.min(),\n valuenow: () => startVal.value,\n valuetext: () => props['from-value-text'].value ?? null,\n },\n },\n { target: thumbStartEl },\n ),\n );\n }\n\n if (thumbEndEl) {\n onEvent(\n thumbEndEl,\n 'keydown',\n makeThumbKeydown(\n () => endVal.value,\n (v) => {\n endVal.value = Math.max(v, startVal.value);\n },\n ),\n );\n onCleanup(\n bind(\n {\n aria: {\n label: 'Range end',\n valuemax: () => sliderControl.max(),\n valuemin: () => startVal.value,\n valuenow: () => endVal.value,\n valuetext: () => props['to-value-text'].value ?? null,\n },\n },\n { target: thumbEndEl },\n ),\n );\n }\n };\n // ── Single-value mode setup ───────────────────────────────────\n const setupSingleMode = (container: HTMLDivElement, track: HTMLDivElement) => {\n updateSingleCSS(Number(valueSignal.value));\n\n const updateValue = (clientX: number) => {\n if (isDisabled.value) return;\n\n const rect = track.getBoundingClientRect();\n const newValue = sliderControl.fromClientX(clientX, rect);\n\n if (Number(valueSignal.value) !== newValue) {\n valueSignal.value = newValue.toString();\n updateSingleCSS(newValue);\n emitValueChange();\n }\n };\n let isPointerDragging = false;\n\n onEvent(\n container,\n 'pointerdown',\n guard(\n () => !isDisabled.value,\n (e: PointerEvent) => {\n e.preventDefault();\n isPointerDragging = true;\n updateValue(e.clientX);\n (e.target as Element).setPointerCapture(e.pointerId);\n },\n ),\n );\n onEvent(\n container,\n 'pointermove',\n guard(\n () => isPointerDragging,\n (e: PointerEvent) => {\n e.preventDefault();\n\n if (!isDragging.value) isDragging.value = true;\n\n updateValue(e.clientX);\n },\n ),\n );\n onEvent(\n container,\n 'pointerup',\n guard(\n () => isPointerDragging,\n (e: PointerEvent) => {\n e.preventDefault();\n isPointerDragging = false;\n isDragging.value = false;\n (e.target as Element).releasePointerCapture(e.pointerId);\n },\n ),\n );\n onEvent(\n el,\n 'keydown',\n guard(\n () => !isDisabled.value,\n (e: KeyboardEvent) => {\n const val = Number(valueSignal.value || 0);\n const next = sliderControl.nextFromKey(e.key, val);\n\n if (next === null) return;\n\n e.preventDefault();\n\n const newValue = sliderControl.clamp(next);\n\n if (newValue !== val) {\n valueSignal.value = newValue.toString();\n updateSingleCSS(newValue);\n emitValueChange();\n }\n },\n ),\n );\n };\n\n onMounted(() => {\n const container = containerRef.value;\n const track = trackRef.value;\n\n if (!container || !track) return;\n\n if (slots.has().value && labelRef.value) {\n const labelId = createStableId('slider-label');\n\n labelRef.value.id = labelId;\n\n if (!isRange) labelledById.value = labelId;\n }\n\n if (isRange) setupRangeMode(container, track);\n else setupSingleMode(container, track);\n });\n\n return html`\n <div class=\"slider-container\" part=\"slider\" ref=${containerRef}>\n <div class=\"slider-track\" part=\"track\" ref=${trackRef}>\n <div class=\"slider-fill\" part=\"fill\"></div>\n <div class=\"slider-thumb slider-thumb-sole\" part=\"thumb\"></div>\n <div\n class=\"slider-thumb slider-thumb-start\"\n part=\"thumb-start\"\n ref=${thumbStartRef}\n role=\"slider\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\"\n aria-describedby=\"${ariaDescribedBy}\"\n id=\"${startId}\"></div>\n <div\n class=\"slider-thumb slider-thumb-end\"\n part=\"thumb-end\"\n ref=${thumbEndRef}\n role=\"slider\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\"\n aria-describedby=\"${ariaDescribedBy}\"\n id=\"${endId}\"></div>\n </div>\n </div>\n <span class=\"label\" part=\"label\" ref=${labelRef}><slot></slot></span>\n ${renderHelperRegion(assistiveId, errorText, helperText)}\n `;\n },\n styles: [\n disabledStateMixin,\n colorThemeMixin,\n sizeVariantMixin(SLIDER_SIZE_PRESET),\n componentStyles,\n reducedMotionMixin,\n ],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"slider.js","names":[],"sources":["../src/inputs/slider/slider.ts"],"sourcesContent":["import {\n bind,\n createStableId,\n define,\n getHost,\n html,\n onCleanup,\n onEvent,\n onMounted,\n prop,\n ref,\n useField,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { createErrorHelperState, createSliderControl } from '../../core';\nimport { disablableBundle, SLIDER_SIZE_PRESET, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, disabledStateMixin, reducedMotionMixin, sizeVariantMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport { defineFieldValue, dispatchNativeFieldEvent, setFieldValue } from '../shared/native-field-event';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './slider.css?inline';\n\nconst guard =\n <E extends Event = Event>(condition: () => unknown, handler: (e: E) => void): ((e: E) => void) =>\n (e) => {\n if (condition()) handler(e);\n };\n\n/** Slider component properties */\n\nexport type OreSliderEvents = {\n change: Event;\n input: Event;\n};\n\nexport type OreSliderProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Error message — marks the field as invalid (fallback when the `error` slot is empty) */\n error?: string;\n /** Range mode: lower bound */\n from?: number | string;\n /** Range mode a11y label for the start thumb (e.g. \"$20\") */\n 'from-value-text'?: string;\n /** Stretch to full width of container */\n fullwidth?: boolean;\n /** Helper text displayed below the slider (fallback when the `helper` slot is empty) */\n helper?: string;\n /** Maximum value */\n max?: number | string;\n /** Minimum value */\n min?: number | string;\n /** Single-value mode: form field name */\n name?: string;\n /** Activate two-thumb range selection */\n range?: boolean;\n /** Component size */\n size?: ComponentSize;\n /** Step increment */\n step?: number | string;\n /** Range mode: upper bound */\n to?: number | string;\n /** Range mode a11y label for the end thumb (e.g. \"$80\") */\n 'to-value-text'?: string;\n /** Single-value mode: current value */\n value?: number | string;\n /** Single-value mode a11y label override (e.g. \"75%\"). Overrides raw aria-valuenow. */\n 'value-text'?: string;\n};\n\n/**\n * A slider for selecting a single numeric value or a numeric range.\n *\n * Add the boolean `range` attribute to activate two-thumb range mode.\n *\n * @element ore-slider\n *\n * @attr {number} min - Minimum value (default: 0)\n * @attr {number} max - Maximum value (default: 100)\n * @attr {number} step - Step increment (default: 1)\n * @attr {number} value - Current value (single mode)\n * @attr {number} from - Lower bound (range mode)\n * @attr {number} to - Upper bound (range mode)\n * @attr {boolean} range - Activate range mode\n * @attr {boolean} disabled - Disable interaction\n * @attr {string} name - Form field name (single mode)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {string} helper - Helper text below the slider\n * @attr {string} error - Error message below the slider\n * @attr {boolean} fullwidth - Stretch to full width of container\n *\n * @fires input - Emitted when the slider value changes.\n * @fires change - Emitted when the slider value changes.\n *\n * @slot - Slider label text\n *\n * @part slider - Slider container\n * @part track - Track element\n * @part fill - Fill element\n * @part thumb - Single-value thumb\n * @part thumb-start - Range start thumb\n * @part thumb-end - Range end thumb\n * @part label - Label element\n * @part helper-text - The helper/error text element\n *\n * @cssprop --slider-height - Track height\n * @cssprop --slider-size - Thumb dimensions\n * @cssprop --slider-track-bg - Track background color\n * @cssprop --slider-fill - Active fill color\n * @cssprop --slider-thumb-bg - Thumb background color\n *\n * @example\n * ```html\n * <ore-slider value=\"50\" name=\"volume\">Volume</ore-slider>\n * <ore-slider range from=\"20\" to=\"80\" color=\"primary\">Price range</ore-slider>\n * ```\n */\nexport const SLIDER_TAG = 'ore-slider' as const;\ndefine<OreSliderProps>(SLIDER_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n error: prop.string(),\n from: prop.number(0),\n 'from-value-text': prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n max: prop.number(100),\n min: prop.number(0),\n name: prop.string(),\n range: prop.bool(false),\n step: prop.number(1),\n to: prop.number(100),\n 'to-value-text': prop.string(),\n value: prop.number(0),\n 'value-text': prop.string(),\n },\n setup(props) {\n const el = getHost();\n const slots = useSlots();\n\n // Treat `range` as static — determined at first render\n const isRange = props.range.value;\n // ── Shared helpers ────────────────────────────────────────────\n const sliderControl = createSliderControl({\n max: props.max,\n min: props.min,\n step: props.step,\n });\n // ── Single-value state ────────────────────────────────────────\n const isDragging = signal(false);\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const labelledById = signal<string | undefined>(undefined);\n const assistiveId = createStableId('helper');\n const assistive = createErrorHelperState({ error: props.error, helper: props.helper });\n const errorText = computed(() => assistive.value.errorText);\n const helperText = computed(() => assistive.value.helperText);\n const ariaDescribedBy = computed(() => (errorText.value || helperText.value ? assistiveId : null));\n\n bind({\n attr: {\n 'data-dragging': () => (isDragging.value ? true : undefined),\n size: props.size,\n },\n });\n\n const valueSignal = signal('0');\n\n if (!isRange) {\n useField({ disabled: isDisabled, value: valueSignal });\n watch(\n props.value,\n (v) => {\n valueSignal.value = String(v);\n },\n { immediate: true },\n );\n bind({\n attr: {\n ariaDescribedby: () => ariaDescribedBy.value ?? null,\n ariaDisabled: () => (isDisabled.value ? 'true' : null),\n ariaLabelledby: () => labelledById.value ?? null,\n ariaValuemax: () => sliderControl.max(),\n ariaValuemin: () => sliderControl.min(),\n ariaValuenow: () => Number(valueSignal.value || 0),\n ariaValuetext: () => props['value-text'].value ?? null,\n role: () => 'slider',\n tabindex: () => (isDisabled.value ? null : '0'),\n },\n });\n }\n\n // ── Range state ───────────────────────────────────────────────\n const startVal = signal(0);\n const endVal = signal(100);\n\n defineFieldValue(\n el,\n () => (isRange ? `${startVal.value},${endVal.value}` : valueSignal.value),\n (value) => {\n if (isRange) {\n const [from, to] = value.split(',').map(Number);\n\n if (Number.isFinite(from)) startVal.value = sliderControl.snap(from);\n\n if (Number.isFinite(to)) endVal.value = sliderControl.snap(to);\n\n return;\n }\n\n const next = Number(value);\n\n if (Number.isFinite(next)) valueSignal.value = String(sliderControl.snap(next));\n },\n );\n\n if (isRange) {\n useField<{\n from: number;\n to: number;\n }>({\n disabled: isDisabled,\n toFormValue: ({ from, to }) => {\n const name = props.name.value;\n\n if (!name) return null;\n\n const fd = new FormData();\n\n fd.append(`${name}[from]`, String(from));\n fd.append(`${name}[to]`, String(to));\n\n return fd;\n },\n value: computed(() => ({ from: startVal.value, to: endVal.value })),\n });\n watch(\n props.from,\n (v) => {\n startVal.value = sliderControl.snap(Number(v));\n },\n { immediate: true },\n );\n watch(\n props.to,\n (v) => {\n endVal.value = sliderControl.snap(Number(v));\n },\n { immediate: true },\n );\n }\n\n // ── Refs ──────────────────────────────────────────────────────\n const containerRef = ref<HTMLDivElement>();\n const labelRef = ref<HTMLSpanElement>();\n const trackRef = ref<HTMLDivElement>();\n const thumbStartRef = ref<HTMLDivElement>();\n const thumbEndRef = ref<HTMLDivElement>();\n const startId = createStableId('slider-start');\n const endId = createStableId('slider-end');\n // ── CSS update helpers ────────────────────────────────────────\n const updateSingleCSS = (value: number) => {\n const pct = sliderControl.toPercent(value);\n\n el.style.setProperty('--_thumb-pos', `${pct}%`);\n el.style.setProperty('--_fill-start', '0%');\n el.style.setProperty('--_fill-width', `${pct}%`);\n };\n const updateRangeCSS = () => {\n const s = sliderControl.toPercent(startVal.value);\n const e = sliderControl.toPercent(endVal.value);\n\n el.style.setProperty('--_thumb-start', `${s}%`);\n el.style.setProperty('--_thumb-end', `${e}%`);\n el.style.setProperty('--_fill-start', `${s}%`);\n el.style.setProperty('--_fill-width', `${e - s}%`);\n };\n const emitValueChange = () => {\n const value = isRange ? `${startVal.value},${endVal.value}` : valueSignal.value;\n\n setFieldValue(el, value);\n dispatchNativeFieldEvent(el, 'input');\n dispatchNativeFieldEvent(el, 'change');\n };\n\n // ── Range mode setup ──────────────────────────────────────────\n const setupRangeMode = (container: HTMLDivElement, track: HTMLDivElement) => {\n updateRangeCSS();\n\n const clientToValue = (clientX: number) => {\n const rect = track.getBoundingClientRect();\n\n return sliderControl.fromClientX(clientX, rect);\n };\n let dragging: 'start' | 'end' | null = null;\n const applyDrag = (val: number) => {\n if (dragging === 'start') startVal.value = Math.min(sliderControl.snap(val), endVal.value);\n else if (dragging === 'end') endVal.value = Math.max(sliderControl.snap(val), startVal.value);\n\n startVal.value = sliderControl.clamp(startVal.value);\n endVal.value = sliderControl.clamp(endVal.value);\n updateRangeCSS();\n emitValueChange();\n };\n\n onEvent(\n container,\n 'pointerdown',\n guard(\n () => !isDisabled.value,\n (e: PointerEvent) => {\n e.preventDefault();\n\n const val = clientToValue(e.clientX);\n\n dragging = Math.abs(val - startVal.value) <= Math.abs(val - endVal.value) ? 'start' : 'end';\n (e.target as Element).setPointerCapture(e.pointerId);\n applyDrag(val);\n },\n ),\n );\n onEvent(\n container,\n 'pointermove',\n guard(\n () => !!dragging,\n (e: PointerEvent) => {\n e.preventDefault();\n\n if (!isDragging.value) isDragging.value = true;\n\n applyDrag(clientToValue(e.clientX));\n },\n ),\n );\n onEvent(\n container,\n 'pointerup',\n guard(\n () => !!dragging,\n (e: PointerEvent) => {\n e.preventDefault();\n dragging = null;\n isDragging.value = false;\n (e.target as Element).releasePointerCapture(e.pointerId);\n },\n ),\n );\n\n const makeThumbKeydown = (getVal: () => number, setVal: (v: number) => void) => (e: KeyboardEvent) => {\n if (isDisabled.value) return;\n\n const next = sliderControl.nextFromKey(e.key, getVal());\n\n if (next === null) return;\n\n e.preventDefault();\n setVal(sliderControl.snap(next));\n updateRangeCSS();\n emitValueChange();\n };\n const thumbStartEl = thumbStartRef.value;\n const thumbEndEl = thumbEndRef.value;\n\n if (thumbStartEl) {\n onEvent(\n thumbStartEl,\n 'keydown',\n makeThumbKeydown(\n () => startVal.value,\n (v) => {\n startVal.value = Math.min(v, endVal.value);\n },\n ),\n );\n onCleanup(\n bind(\n {\n aria: {\n label: 'Range start',\n valuemax: () => endVal.value,\n valuemin: () => sliderControl.min(),\n valuenow: () => startVal.value,\n valuetext: () => props['from-value-text'].value ?? null,\n },\n },\n { target: thumbStartEl },\n ),\n );\n }\n\n if (thumbEndEl) {\n onEvent(\n thumbEndEl,\n 'keydown',\n makeThumbKeydown(\n () => endVal.value,\n (v) => {\n endVal.value = Math.max(v, startVal.value);\n },\n ),\n );\n onCleanup(\n bind(\n {\n aria: {\n label: 'Range end',\n valuemax: () => sliderControl.max(),\n valuemin: () => startVal.value,\n valuenow: () => endVal.value,\n valuetext: () => props['to-value-text'].value ?? null,\n },\n },\n { target: thumbEndEl },\n ),\n );\n }\n };\n // ── Single-value mode setup ───────────────────────────────────\n const setupSingleMode = (container: HTMLDivElement, track: HTMLDivElement) => {\n updateSingleCSS(Number(valueSignal.value));\n\n const updateValue = (clientX: number) => {\n if (isDisabled.value) return;\n\n const rect = track.getBoundingClientRect();\n const newValue = sliderControl.fromClientX(clientX, rect);\n\n if (Number(valueSignal.value) !== newValue) {\n valueSignal.value = newValue.toString();\n updateSingleCSS(newValue);\n emitValueChange();\n }\n };\n let isPointerDragging = false;\n\n onEvent(\n container,\n 'pointerdown',\n guard(\n () => !isDisabled.value,\n (e: PointerEvent) => {\n e.preventDefault();\n isPointerDragging = true;\n updateValue(e.clientX);\n (e.target as Element).setPointerCapture(e.pointerId);\n },\n ),\n );\n onEvent(\n container,\n 'pointermove',\n guard(\n () => isPointerDragging,\n (e: PointerEvent) => {\n e.preventDefault();\n\n if (!isDragging.value) isDragging.value = true;\n\n updateValue(e.clientX);\n },\n ),\n );\n onEvent(\n container,\n 'pointerup',\n guard(\n () => isPointerDragging,\n (e: PointerEvent) => {\n e.preventDefault();\n isPointerDragging = false;\n isDragging.value = false;\n (e.target as Element).releasePointerCapture(e.pointerId);\n },\n ),\n );\n onEvent(\n el,\n 'keydown',\n guard(\n () => !isDisabled.value,\n (e: KeyboardEvent) => {\n const val = Number(valueSignal.value || 0);\n const next = sliderControl.nextFromKey(e.key, val);\n\n if (next === null) return;\n\n e.preventDefault();\n\n const newValue = sliderControl.clamp(next);\n\n if (newValue !== val) {\n valueSignal.value = newValue.toString();\n updateSingleCSS(newValue);\n emitValueChange();\n }\n },\n ),\n );\n };\n\n onMounted(() => {\n const container = containerRef.value;\n const track = trackRef.value;\n\n if (!container || !track) return;\n\n if (slots.has().value && labelRef.value) {\n const labelId = createStableId('slider-label');\n\n labelRef.value.id = labelId;\n\n if (!isRange) labelledById.value = labelId;\n }\n\n if (isRange) setupRangeMode(container, track);\n else setupSingleMode(container, track);\n });\n\n return html`\n <div class=\"slider-container\" part=\"slider\" ref=${containerRef}>\n <div class=\"slider-track\" part=\"track\" ref=${trackRef}>\n <div class=\"slider-fill\" part=\"fill\"></div>\n <div class=\"slider-thumb slider-thumb-sole\" part=\"thumb\"></div>\n <div\n class=\"slider-thumb slider-thumb-start\"\n part=\"thumb-start\"\n ref=${thumbStartRef}\n role=\"slider\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\"\n aria-describedby=\"${ariaDescribedBy}\"\n id=\"${startId}\"></div>\n <div\n class=\"slider-thumb slider-thumb-end\"\n part=\"thumb-end\"\n ref=${thumbEndRef}\n role=\"slider\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\"\n aria-describedby=\"${ariaDescribedBy}\"\n id=\"${endId}\"></div>\n </div>\n </div>\n <span class=\"label\" part=\"label\" ref=${labelRef}><slot></slot></span>\n ${renderHelperRegion(assistiveId, errorText, helperText)}\n `;\n },\n styles: [\n disabledStateMixin,\n colorThemeMixin,\n sizeVariantMixin(SLIDER_SIZE_PRESET),\n componentStyles,\n reducedMotionMixin,\n ],\n});\n"],"mappings":";;;;;;;;;;;;;;AAuBA,IAAM,KACsB,GAA0B,OACnD,MAAM;CACL,AAAI,EAAU,KAAG,EAAQ,CAAC;AAC5B,GA8FW,IAAa;AAC1B,EAAuB,GAAY;CACjC,gBAAgB;CAChB,OAAO;EACL,GAAG;EACH,GAAG;EACH,GAAG;EACH,OAAO,EAAK,OAAO;EACnB,MAAM,EAAK,OAAO,CAAC;EACnB,mBAAmB,EAAK,OAAO;EAC/B,WAAW,EAAK,KAAK,EAAK;EAC1B,QAAQ,EAAK,OAAO;EACpB,KAAK,EAAK,OAAO,GAAG;EACpB,KAAK,EAAK,OAAO,CAAC;EAClB,MAAM,EAAK,OAAO;EAClB,OAAO,EAAK,KAAK,EAAK;EACtB,MAAM,EAAK,OAAO,CAAC;EACnB,IAAI,EAAK,OAAO,GAAG;EACnB,iBAAiB,EAAK,OAAO;EAC7B,OAAO,EAAK,OAAO,CAAC;EACpB,cAAc,EAAK,OAAO;CAC5B;CACA,MAAM,GAAO;EACX,IAAM,IAAK,EAAQ,GACb,IAAQ,EAAS,GAGjB,IAAU,EAAM,MAAM,OAEtB,IAAgB,EAAoB;GACxC,KAAK,EAAM;GACX,KAAK,EAAM;GACX,MAAM,EAAM;EACd,CAAC,GAEK,IAAa,EAAO,EAAK,GACzB,IAAa,QAAe,EAAQ,EAAM,SAAS,KAAM,GACzD,IAAe,EAA2B,KAAA,CAAS,GACnD,IAAc,EAAe,QAAQ,GACrC,IAAY,EAAuB;GAAE,OAAO,EAAM;GAAO,QAAQ,EAAM;EAAO,CAAC,GAC/E,IAAY,QAAe,EAAU,MAAM,SAAS,GACpD,IAAa,QAAe,EAAU,MAAM,UAAU,GACtD,IAAkB,QAAgB,EAAU,SAAS,EAAW,QAAQ,IAAc,IAAK;EAEjG,EAAK,EACH,MAAM;GACJ,uBAAwB,EAAW,QAAQ,KAAO,KAAA;GAClD,MAAM,EAAM;EACd,EACF,CAAC;EAED,IAAM,IAAc,EAAO,GAAG;EAE9B,AAAK,MACH,EAAS;GAAE,UAAU;GAAY,OAAO;EAAY,CAAC,GACrD,EACE,EAAM,QACL,MAAM;GACL,EAAY,QAAQ,OAAO,CAAC;EAC9B,GACA,EAAE,WAAW,GAAK,CACpB,GACA,EAAK,EACH,MAAM;GACJ,uBAAuB,EAAgB,SAAS;GAChD,oBAAqB,EAAW,QAAQ,SAAS;GACjD,sBAAsB,EAAa,SAAS;GAC5C,oBAAoB,EAAc,IAAI;GACtC,oBAAoB,EAAc,IAAI;GACtC,oBAAoB,OAAO,EAAY,SAAS,CAAC;GACjD,qBAAqB,EAAM,aAAa,CAAC,SAAS;GAClD,YAAY;GACZ,gBAAiB,EAAW,QAAQ,OAAO;EAC7C,EACF,CAAC;EAIH,IAAM,IAAW,EAAO,CAAC,GACnB,IAAS,EAAO,GAAG;EAsBzB,AApBA,EACE,SACO,IAAU,GAAG,EAAS,MAAM,GAAG,EAAO,UAAU,EAAY,QAClE,MAAU;GACT,IAAI,GAAS;IACX,IAAM,CAAC,GAAM,KAAM,EAAM,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;IAI9C,AAFI,OAAO,SAAS,CAAI,MAAG,EAAS,QAAQ,EAAc,KAAK,CAAI,IAE/D,OAAO,SAAS,CAAE,MAAG,EAAO,QAAQ,EAAc,KAAK,CAAE;IAE7D;GACF;GAEA,IAAM,IAAO,OAAO,CAAK;GAEzB,AAAI,OAAO,SAAS,CAAI,MAAG,EAAY,QAAQ,OAAO,EAAc,KAAK,CAAI,CAAC;EAChF,CACF,GAEI,MACF,EAGG;GACD,UAAU;GACV,cAAc,EAAE,SAAM,YAAS;IAC7B,IAAM,IAAO,EAAM,KAAK;IAExB,IAAI,CAAC,GAAM,OAAO;IAElB,IAAM,IAAK,IAAI,SAAS;IAKxB,OAHA,EAAG,OAAO,GAAG,EAAK,SAAS,OAAO,CAAI,CAAC,GACvC,EAAG,OAAO,GAAG,EAAK,OAAO,OAAO,CAAE,CAAC,GAE5B;GACT;GACA,OAAO,SAAgB;IAAE,MAAM,EAAS;IAAO,IAAI,EAAO;GAAM,EAAE;EACpE,CAAC,GACD,EACE,EAAM,OACL,MAAM;GACL,EAAS,QAAQ,EAAc,KAAK,OAAO,CAAC,CAAC;EAC/C,GACA,EAAE,WAAW,GAAK,CACpB,GACA,EACE,EAAM,KACL,MAAM;GACL,EAAO,QAAQ,EAAc,KAAK,OAAO,CAAC,CAAC;EAC7C,GACA,EAAE,WAAW,GAAK,CACpB;EAIF,IAAM,IAAe,EAAoB,GACnC,IAAW,EAAqB,GAChC,IAAW,EAAoB,GAC/B,IAAgB,EAAoB,GACpC,IAAc,EAAoB,GAClC,IAAU,EAAe,cAAc,GACvC,IAAQ,EAAe,YAAY,GAEnC,KAAmB,MAAkB;GACzC,IAAM,IAAM,EAAc,UAAU,CAAK;GAIzC,AAFA,EAAG,MAAM,YAAY,gBAAgB,GAAG,EAAI,EAAE,GAC9C,EAAG,MAAM,YAAY,iBAAiB,IAAI,GAC1C,EAAG,MAAM,YAAY,iBAAiB,GAAG,EAAI,EAAE;EACjD,GACM,UAAuB;GAC3B,IAAM,IAAI,EAAc,UAAU,EAAS,KAAK,GAC1C,IAAI,EAAc,UAAU,EAAO,KAAK;GAK9C,AAHA,EAAG,MAAM,YAAY,kBAAkB,GAAG,EAAE,EAAE,GAC9C,EAAG,MAAM,YAAY,gBAAgB,GAAG,EAAE,EAAE,GAC5C,EAAG,MAAM,YAAY,iBAAiB,GAAG,EAAE,EAAE,GAC7C,EAAG,MAAM,YAAY,iBAAiB,GAAG,IAAI,EAAE,EAAE;EACnD,GACM,UAAwB;GAC5B,IAAM,IAAQ,IAAU,GAAG,EAAS,MAAM,GAAG,EAAO,UAAU,EAAY;GAI1E,AAFA,EAAc,GAAI,CAAK,GACvB,EAAyB,GAAI,OAAO,GACpC,EAAyB,GAAI,QAAQ;EACvC,GAGM,KAAkB,GAA2B,MAA0B;GAC3E,EAAe;GAEf,IAAM,KAAiB,MAAoB;IACzC,IAAM,IAAO,EAAM,sBAAsB;IAEzC,OAAO,EAAc,YAAY,GAAS,CAAI;GAChD,GACI,IAAmC,MACjC,KAAa,MAAgB;IAOjC,AANI,MAAa,UAAS,EAAS,QAAQ,KAAK,IAAI,EAAc,KAAK,CAAG,GAAG,EAAO,KAAK,IAChF,MAAa,UAAO,EAAO,QAAQ,KAAK,IAAI,EAAc,KAAK,CAAG,GAAG,EAAS,KAAK,IAE5F,EAAS,QAAQ,EAAc,MAAM,EAAS,KAAK,GACnD,EAAO,QAAQ,EAAc,MAAM,EAAO,KAAK,GAC/C,EAAe,GACf,EAAgB;GAClB;GAgCA,AA9BA,EACE,GACA,eACA,QACQ,CAAC,EAAW,QACjB,MAAoB;IACnB,EAAE,eAAe;IAEjB,IAAM,IAAM,EAAc,EAAE,OAAO;IAInC,AAFA,IAAW,KAAK,IAAI,IAAM,EAAS,KAAK,KAAK,KAAK,IAAI,IAAM,EAAO,KAAK,IAAI,UAAU,OACtF,EAAG,OAAmB,kBAAkB,EAAE,SAAS,GACnD,EAAU,CAAG;GACf,CACF,CACF,GACA,EACE,GACA,eACA,QACQ,CAAC,CAAC,IACP,MAAoB;IAKnB,AAJA,EAAE,eAAe,GAEjB,AAAuB,EAAW,UAAQ,IAE1C,EAAU,EAAc,EAAE,OAAO,CAAC;GACpC,CACF,CACF,GACA,EACE,GACA,aACA,QACQ,CAAC,CAAC,IACP,MAAoB;IAInB,AAHA,EAAE,eAAe,GACjB,IAAW,MACX,EAAW,QAAQ,IACnB,EAAG,OAAmB,sBAAsB,EAAE,SAAS;GACzD,CACF,CACF;GAEA,IAAM,KAAoB,GAAsB,OAAiC,MAAqB;IACpG,IAAI,EAAW,OAAO;IAEtB,IAAM,IAAO,EAAc,YAAY,EAAE,KAAK,EAAO,CAAC;IAElD,MAAS,SAEb,EAAE,eAAe,GACjB,EAAO,EAAc,KAAK,CAAI,CAAC,GAC/B,EAAe,GACf,EAAgB;GAClB,GACM,IAAe,EAAc,OAC7B,IAAa,EAAY;GA6B/B,AA3BI,MACF,EACE,GACA,WACA,QACQ,EAAS,QACd,MAAM;IACL,EAAS,QAAQ,KAAK,IAAI,GAAG,EAAO,KAAK;GAC3C,CACF,CACF,GACA,EACE,EACE,EACE,MAAM;IACJ,OAAO;IACP,gBAAgB,EAAO;IACvB,gBAAgB,EAAc,IAAI;IAClC,gBAAgB,EAAS;IACzB,iBAAiB,EAAM,kBAAkB,CAAC,SAAS;GACrD,EACF,GACA,EAAE,QAAQ,EAAa,CACzB,CACF,IAGE,MACF,EACE,GACA,WACA,QACQ,EAAO,QACZ,MAAM;IACL,EAAO,QAAQ,KAAK,IAAI,GAAG,EAAS,KAAK;GAC3C,CACF,CACF,GACA,EACE,EACE,EACE,MAAM;IACJ,OAAO;IACP,gBAAgB,EAAc,IAAI;IAClC,gBAAgB,EAAS;IACzB,gBAAgB,EAAO;IACvB,iBAAiB,EAAM,gBAAgB,CAAC,SAAS;GACnD,EACF,GACA,EAAE,QAAQ,EAAW,CACvB,CACF;EAEJ,GAEM,KAAmB,GAA2B,MAA0B;GAC5E,EAAgB,OAAO,EAAY,KAAK,CAAC;GAEzC,IAAM,KAAe,MAAoB;IACvC,IAAI,EAAW,OAAO;IAEtB,IAAM,IAAO,EAAM,sBAAsB,GACnC,IAAW,EAAc,YAAY,GAAS,CAAI;IAExD,AAAI,OAAO,EAAY,KAAK,MAAM,MAChC,EAAY,QAAQ,EAAS,SAAS,GACtC,EAAgB,CAAQ,GACxB,EAAgB;GAEpB,GACI,IAAoB;GA0CxB,AAxCA,EACE,GACA,eACA,QACQ,CAAC,EAAW,QACjB,MAAoB;IAInB,AAHA,EAAE,eAAe,GACjB,IAAoB,IACpB,EAAY,EAAE,OAAO,GACrB,EAAG,OAAmB,kBAAkB,EAAE,SAAS;GACrD,CACF,CACF,GACA,EACE,GACA,eACA,QACQ,IACL,MAAoB;IAKnB,AAJA,EAAE,eAAe,GAEjB,AAAuB,EAAW,UAAQ,IAE1C,EAAY,EAAE,OAAO;GACvB,CACF,CACF,GACA,EACE,GACA,aACA,QACQ,IACL,MAAoB;IAInB,AAHA,EAAE,eAAe,GACjB,IAAoB,IACpB,EAAW,QAAQ,IACnB,EAAG,OAAmB,sBAAsB,EAAE,SAAS;GACzD,CACF,CACF,GACA,EACE,GACA,WACA,QACQ,CAAC,EAAW,QACjB,MAAqB;IACpB,IAAM,IAAM,OAAO,EAAY,SAAS,CAAC,GACnC,IAAO,EAAc,YAAY,EAAE,KAAK,CAAG;IAEjD,IAAI,MAAS,MAAM;IAEnB,EAAE,eAAe;IAEjB,IAAM,IAAW,EAAc,MAAM,CAAI;IAEzC,AAAI,MAAa,MACf,EAAY,QAAQ,EAAS,SAAS,GACtC,EAAgB,CAAQ,GACxB,EAAgB;GAEpB,CACF,CACF;EACF;EAoBA,OAlBA,QAAgB;GACd,IAAM,IAAY,EAAa,OACzB,IAAQ,EAAS;GAEnB,IAAC,KAAc,GAEnB;QAAI,EAAM,IAAI,CAAC,CAAC,SAAS,EAAS,OAAO;KACvC,IAAM,IAAU,EAAe,cAAc;KAI7C,AAFA,EAAS,MAAM,KAAK,GAEf,MAAS,EAAa,QAAQ;IACrC;IAEA,AAAI,IAAS,EAAe,GAAW,CAAK,IACvC,EAAgB,GAAW,CAAK;GAHrC;EAIF,CAAC,GAEM,CAAI;wDACyC,EAAa;qDAChB,EAAS;;;;;;kBAM5C,EAAc;;8BAED,EAAW,QAAQ,OAAO,IAAK;gCAC9B,EAAgB;kBAC9B,EAAQ;;;;kBAIR,EAAY;;8BAEC,EAAW,QAAQ,OAAO,IAAK;gCAC9B,EAAgB;kBAC9B,EAAM;;;6CAGqB,EAAS;QAC9C,EAAmB,GAAa,GAAW,CAAU,EAAE;;CAE7D;CACA,QAAQ;EACN;EACA;EACA,EAAiB,CAAkB;EACnC;EACA;CACF;AACF,CAAC"}
|
package/dist/stats.cjs
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/shape.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./shared/prop-bundles.cjs"),i=require("./content/stats/stats.css.cjs");let a=require("@vielzeug/ore");var o=`ore-stats`;(0,a.define)(o,{props:{...r.themableBundle,...r.sizableBundle,...r.disablableBundle,...r.loadableBundle,...r.roundableBundle,description:a.prop.string(),label:a.prop.string(),trend:a.prop.string(),"trend-direction":a.prop.oneOf([`up`,`down`,`neutral`],`neutral`),value:a.prop.string(),variant:a.prop.oneOf([`outlined`,`plain`,`solid`],`outlined`)},setup(e){let t=(0,a.useSlots)(),n=()=>!!e.description.value||t.has(`description`).value,r=()=>t.has(`icon`).value,i=()=>!!e.label.value||t.has(`label`).value,o=()=>!!e.trend.value||t.has(`trend`).value,s=()=>!!e.value.value||t.has(`value`).value,c=()=>t.has(`visual`).value;return(0,a.bind)({attr:{ariaBusy:()=>String(!!e.loading.value),ariaDisabled:()=>String(!!e.disabled.value)}}),a.html`
|
|
2
|
+
<article class="stats" part="card">
|
|
3
|
+
<span class="loading" part="loading" aria-hidden="true"></span>
|
|
4
|
+
<header class="header" part="header" ?hidden=${()=>!r()&&!i()}>
|
|
5
|
+
<span class="icon" part="icon" ?hidden=${()=>!r()}><slot name="icon"></slot></span>
|
|
6
|
+
<span class="label" part="label" ?hidden=${()=>!i()}><slot name="label">${e.label}</slot></span>
|
|
7
|
+
</header>
|
|
8
|
+
<div class="body" part="body" ?hidden=${()=>!s()&&!c()}>
|
|
9
|
+
<strong class="value" part="value" ?hidden=${()=>!s()}><slot name="value">${e.value}</slot></strong>
|
|
10
|
+
<span class="visual" part="visual" ?hidden=${()=>!c()}><slot name="visual"></slot></span>
|
|
11
|
+
</div>
|
|
12
|
+
<footer class="footer" part="footer" ?hidden=${()=>!o()&&!n()}>
|
|
13
|
+
<span class="trend" part="trend" data-direction=${e[`trend-direction`]} ?hidden=${()=>!o()}><slot name="trend">${e.trend}</slot></span>
|
|
14
|
+
<span class="description" part="description" ?hidden=${()=>!n()}><slot name="description">${e.description}</slot></span>
|
|
15
|
+
</footer>
|
|
16
|
+
</article>
|
|
17
|
+
`},styles:[n.colorThemeMixin,t.roundedVariantMixin,t.sizeVariantMixin({lg:{"--_icon-size":`var(--size-10)`,"--_min-height":`var(--stats-min-height, var(--size-36))`,"--_value-size":`var(--stats-value-size, var(--text-3xl))`,gap:`var(--size-4)`,padding:`var(--stats-padding, var(--size-5))`},md:{"--_icon-size":`var(--size-9)`,"--_min-height":`var(--stats-min-height, var(--size-32))`,"--_value-size":`var(--stats-value-size, var(--text-2xl))`,gap:`var(--size-3)`,padding:`var(--stats-padding, var(--size-4))`},sm:{"--_icon-size":`var(--size-8)`,"--_min-height":`var(--stats-min-height, var(--size-24))`,"--_value-size":`var(--stats-value-size, var(--text-xl))`,gap:`var(--size-2)`,padding:`var(--stats-padding, var(--size-3))`}}),e.reducedMotionMixin,n.forcedColorsMixin,i.default]}),exports.STATS_TAG=o;
|
|
18
|
+
//# sourceMappingURL=stats.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.cjs","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 ariaBusy: () => String(Boolean(props.loading.value)),\n ariaDisabled: () => 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":"mUAmFA,IAAa,EAAY,aACzB,EAAA,EAAA,OAAA,CAAsB,EAAW,CAC/B,MAAO,CACL,GAAG,EAAA,eACH,GAAG,EAAA,cACH,GAAG,EAAA,iBACH,GAAG,EAAA,eACH,GAAG,EAAA,gBACH,YAAa,EAAA,KAAK,OAAO,EACzB,MAAO,EAAA,KAAK,OAAO,EACnB,MAAO,EAAA,KAAK,OAAO,EACnB,kBAAmB,EAAA,KAAK,MAA2B,CAAC,KAAM,OAAQ,SAAS,EAAG,SAAS,EACvF,MAAO,EAAA,KAAK,OAAO,EACnB,QAAS,EAAA,KAAK,MAAoB,CAAC,WAAY,QAAS,OAAO,EAAG,UAAU,CAC9E,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAQ,EAAA,SAAA,CAA0E,EAClF,MAAuB,EAAQ,EAAM,YAAY,OAAU,EAAM,IAAI,aAAa,CAAC,CAAC,MACpF,MAAgB,EAAM,IAAI,MAAM,CAAC,CAAC,MAClC,MAAiB,EAAQ,EAAM,MAAM,OAAU,EAAM,IAAI,OAAO,CAAC,CAAC,MAClE,MAAiB,EAAQ,EAAM,MAAM,OAAU,EAAM,IAAI,OAAO,CAAC,CAAC,MAClE,MAAiB,EAAQ,EAAM,MAAM,OAAU,EAAM,IAAI,OAAO,CAAC,CAAC,MAClE,MAAkB,EAAM,IAAI,QAAQ,CAAC,CAAC,MAS5C,OAPA,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,aAAgB,OAAO,EAAQ,EAAM,QAAQ,KAAM,EACnD,iBAAoB,OAAO,EAAQ,EAAM,SAAS,KAAM,CAC1D,CACF,CAAC,EAEM,EAAA,IAAI;;;2DAG8C,CAAC,EAAQ,GAAK,CAAC,EAAS,EAAE;uDAC9B,CAAC,EAAQ,EAAE;yDACT,CAAC,EAAS,EAAE,sBAAsB,EAAM,MAAM;;oDAEnD,CAAC,EAAS,GAAK,CAAC,EAAU,EAAE;2DACrB,CAAC,EAAS,EAAE,sBAAsB,EAAM,MAAM;2DAC9C,CAAC,EAAU,EAAE;;2DAEb,CAAC,EAAS,GAAK,CAAC,EAAe,EAAE;4DAClC,EAAM,mBAAmB,eAAiB,CAAC,EAAS,EAAE,sBAAsB,EAAM,MAAM;qEAC7E,CAAC,EAAe,EAAE,4BAA4B,EAAM,YAAY;;;KAIrI,EACA,OAAQ,CACN,EAAA,gBACA,EAAA,oBACA,EAAA,iBAAiB,CACf,GAAI,CACF,eAAgB,iBAChB,gBAAiB,0CACjB,gBAAiB,2CACjB,IAAK,gBACL,QAAS,qCACX,EACA,GAAI,CACF,eAAgB,gBAChB,gBAAiB,0CACjB,gBAAiB,2CACjB,IAAK,gBACL,QAAS,qCACX,EACA,GAAI,CACF,eAAgB,gBAChB,gBAAiB,0CACjB,gBAAiB,0CACjB,IAAK,gBACL,QAAS,qCACX,CACF,CAAC,EACD,EAAA,mBACA,EAAA,kBACA,EAAA,OACF,CACF,CAAC"}
|
package/dist/stats.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { reducedMotionMixin as e } from "./styles/mixins/accessibility.css.js";
|
|
2
|
+
import { roundedVariantMixin as t, sizeVariantMixin as n } from "./styles/mixins/shape.css.js";
|
|
3
|
+
import { colorThemeMixin as r, forcedColorsMixin as i } from "./styles/mixins/theme.css.js";
|
|
4
|
+
import { disablableBundle as a, loadableBundle as o, roundableBundle as s, sizableBundle as c, themableBundle as l } from "./shared/prop-bundles.js";
|
|
5
|
+
import u from "./content/stats/stats.css.js";
|
|
6
|
+
import { bind as d, define as f, html as p, prop as m, useSlots as h } from "@vielzeug/ore";
|
|
7
|
+
//#region src/content/stats/stats.ts
|
|
8
|
+
var g = "ore-stats";
|
|
9
|
+
f(g, {
|
|
10
|
+
props: {
|
|
11
|
+
...l,
|
|
12
|
+
...c,
|
|
13
|
+
...a,
|
|
14
|
+
...o,
|
|
15
|
+
...s,
|
|
16
|
+
description: m.string(),
|
|
17
|
+
label: m.string(),
|
|
18
|
+
trend: m.string(),
|
|
19
|
+
"trend-direction": m.oneOf([
|
|
20
|
+
"up",
|
|
21
|
+
"down",
|
|
22
|
+
"neutral"
|
|
23
|
+
], "neutral"),
|
|
24
|
+
value: m.string(),
|
|
25
|
+
variant: m.oneOf([
|
|
26
|
+
"outlined",
|
|
27
|
+
"plain",
|
|
28
|
+
"solid"
|
|
29
|
+
], "outlined")
|
|
30
|
+
},
|
|
31
|
+
setup(e) {
|
|
32
|
+
let t = h(), n = () => !!e.description.value || t.has("description").value, r = () => t.has("icon").value, i = () => !!e.label.value || t.has("label").value, a = () => !!e.trend.value || t.has("trend").value, o = () => !!e.value.value || t.has("value").value, s = () => t.has("visual").value;
|
|
33
|
+
return d({ attr: {
|
|
34
|
+
ariaBusy: () => String(!!e.loading.value),
|
|
35
|
+
ariaDisabled: () => String(!!e.disabled.value)
|
|
36
|
+
} }), p`
|
|
37
|
+
<article class="stats" part="card">
|
|
38
|
+
<span class="loading" part="loading" aria-hidden="true"></span>
|
|
39
|
+
<header class="header" part="header" ?hidden=${() => !r() && !i()}>
|
|
40
|
+
<span class="icon" part="icon" ?hidden=${() => !r()}><slot name="icon"></slot></span>
|
|
41
|
+
<span class="label" part="label" ?hidden=${() => !i()}><slot name="label">${e.label}</slot></span>
|
|
42
|
+
</header>
|
|
43
|
+
<div class="body" part="body" ?hidden=${() => !o() && !s()}>
|
|
44
|
+
<strong class="value" part="value" ?hidden=${() => !o()}><slot name="value">${e.value}</slot></strong>
|
|
45
|
+
<span class="visual" part="visual" ?hidden=${() => !s()}><slot name="visual"></slot></span>
|
|
46
|
+
</div>
|
|
47
|
+
<footer class="footer" part="footer" ?hidden=${() => !a() && !n()}>
|
|
48
|
+
<span class="trend" part="trend" data-direction=${e["trend-direction"]} ?hidden=${() => !a()}><slot name="trend">${e.trend}</slot></span>
|
|
49
|
+
<span class="description" part="description" ?hidden=${() => !n()}><slot name="description">${e.description}</slot></span>
|
|
50
|
+
</footer>
|
|
51
|
+
</article>
|
|
52
|
+
`;
|
|
53
|
+
},
|
|
54
|
+
styles: [
|
|
55
|
+
r,
|
|
56
|
+
t,
|
|
57
|
+
n({
|
|
58
|
+
lg: {
|
|
59
|
+
"--_icon-size": "var(--size-10)",
|
|
60
|
+
"--_min-height": "var(--stats-min-height, var(--size-36))",
|
|
61
|
+
"--_value-size": "var(--stats-value-size, var(--text-3xl))",
|
|
62
|
+
gap: "var(--size-4)",
|
|
63
|
+
padding: "var(--stats-padding, var(--size-5))"
|
|
64
|
+
},
|
|
65
|
+
md: {
|
|
66
|
+
"--_icon-size": "var(--size-9)",
|
|
67
|
+
"--_min-height": "var(--stats-min-height, var(--size-32))",
|
|
68
|
+
"--_value-size": "var(--stats-value-size, var(--text-2xl))",
|
|
69
|
+
gap: "var(--size-3)",
|
|
70
|
+
padding: "var(--stats-padding, var(--size-4))"
|
|
71
|
+
},
|
|
72
|
+
sm: {
|
|
73
|
+
"--_icon-size": "var(--size-8)",
|
|
74
|
+
"--_min-height": "var(--stats-min-height, var(--size-24))",
|
|
75
|
+
"--_value-size": "var(--stats-value-size, var(--text-xl))",
|
|
76
|
+
gap: "var(--size-2)",
|
|
77
|
+
padding: "var(--stats-padding, var(--size-3))"
|
|
78
|
+
}
|
|
79
|
+
}),
|
|
80
|
+
e,
|
|
81
|
+
i,
|
|
82
|
+
u
|
|
83
|
+
]
|
|
84
|
+
});
|
|
85
|
+
//#endregion
|
|
86
|
+
export { g as STATS_TAG };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +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 ariaBusy: () => String(Boolean(props.loading.value)),\n ariaDisabled: () => 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,gBAAgB,OAAO,EAAQ,EAAM,QAAQ,KAAM;GACnD,oBAAoB,OAAO,EAAQ,EAAM,SAAS,KAAM;EAC1D,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"}
|
package/dist/stepper.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./core/keyboard.cjs"),t=require("./core/ore.cjs"),n=require("./core/direction.cjs"),r=require("./core/nav.cjs"),i=require("./shared/prop-bundles.cjs"),a=require("./content/stepper/_is-step-navigable.cjs"),o=require("./content/stepper/stepper.css.cjs");let s=require("@vielzeug/ore"),c=require("@vielzeug/ripple");var l=(0,s.createContext)(`StepperContext`),u=`ore-stepper`;(0,s.define)(u,{props:{...i.themableBundle,...i.sizableBundle,...i.disablableBundle,clickable:s.prop.bool(!1),label:s.prop.string(`Progress`),linear:s.prop.bool(!1),orientation:s.prop.oneOf([`horizontal`,`vertical`],`horizontal`),value:s.prop.string()},setup(i){let o=(0,s.getHost)(),u=(0,s.useEmit)(),d=(0,s.useSlots)(),f=()=>Array.from(o.querySelectorAll(`:scope > ore-step`)),p=e=>{e?.shadowRoot?.querySelector(`button.control`)?.focus()},m=(0,c.signal)(i.value.value);(0,s.bind)({attr:{value:()=>m.value??null}});let h=(e,t=!1)=>{
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./core/keyboard.cjs"),t=require("./core/ore.cjs"),n=require("./core/direction.cjs"),r=require("./core/nav.cjs"),i=require("./shared/prop-bundles.cjs"),a=require("./content/stepper/_is-step-navigable.cjs"),o=require("./content/stepper/stepper.css.cjs");let s=require("@vielzeug/ore"),c=require("@vielzeug/ripple");var l=(0,s.createContext)(`StepperContext`),u=`ore-stepper`;(0,s.define)(u,{props:{...i.themableBundle,...i.sizableBundle,...i.disablableBundle,clickable:s.prop.bool(!1),label:s.prop.string(`Progress`),linear:s.prop.bool(!1),orientation:s.prop.oneOf([`horizontal`,`vertical`],`horizontal`),value:s.prop.string()},setup(i){let o=(0,s.getHost)(),u=(0,s.useEmit)(),d=(0,s.useSlots)(),f=()=>Array.from(o.querySelectorAll(`:scope > ore-step`)),p=e=>{e?.shadowRoot?.querySelector(`button.control`)?.focus()},m=(0,c.signal)(i.value.value);(0,s.bind)({attr:{value:()=>m.value??null}});let h=(e,t=!1)=>{e&&e!==m.value&&(m.value=e,t&&u(`change`,{value:e}))},g=()=>{let e=f();if(e.length===0)return;let t=m.value;if(t&&e.some(e=>e.getAttribute(`value`)===t))return;let n=e.find(e=>!e.hasAttribute(`disabled`))?.getAttribute(`value`)??void 0;n&&h(n)};(0,c.watch)(i.value,e=>{m.value=e,g()});let _=(0,c.computed)(()=>(d.elements().value,f().map(e=>e.getAttribute(`value`)??``)));(0,s.provide)(l,{clickable:(0,c.computed)(()=>!!i.clickable.value&&!i.disabled.value),color:i.color,currentValue:m,linear:(0,c.computed)(()=>!!i.linear.value),orientation:(0,c.computed)(()=>i.orientation.value??`horizontal`),size:i.size,stepValues:_}),(0,s.onMounted)(()=>{g()});let v=()=>{let e=f(),t=e.map(e=>e.getAttribute(`value`)??``).indexOf(m.value??``),n=!!i.clickable.value&&!i.disabled.value,r=!!i.linear.value;return e.filter((e,i)=>a.isStepNavigable({disabled:e.hasAttribute(`disabled`),index:i,linear:r,stepperClickable:n,stepperCurrentIndex:t}))},y=r.createListControl({direction:()=>n.elementDirection(o),getItems:v,loop:!1,onNavigate:({item:e})=>{let t=e.getAttribute(`value`);t&&h(t,!0),p(e)},orientation:()=>i.orientation.value===`vertical`?`vertical`:`horizontal`,signal:t.lifecycleSignal(s.onCleanup)}),b=e=>{let t=e.composedPath().find(e=>e instanceof HTMLElement&&e.localName===`ore-step`);if(!t||t.closest(`ore-stepper`)!==o||!v().includes(t))return;let n=t.getAttribute(`value`);h(n??void 0,!0),p(t)},x=e.createInteraction({onPress:()=>{let e=v().find(e=>e===document.activeElement||e.shadowRoot?.activeElement===document.activeElement),t=e?.getAttribute(`value`);t&&(h(t,!0),p(e))}});return(0,s.bind)({on:{click:b,keydown:e=>{let t=v();if(t.length===0)return;let n=e.composedPath().find(e=>e instanceof HTMLElement&&e.localName===`ore-step`),r=n?t.indexOf(n):-1;r>=0&&y.set(r),!y.handleKeydown(e)&&x.handleKeydown(e)}}}),s.html`
|
|
2
2
|
<nav part="nav" aria-label="${i.label}">
|
|
3
3
|
<ol class="steps" role="list" part="list">
|
|
4
4
|
<slot></slot>
|
package/dist/stepper.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stepper.cjs","names":[],"sources":["../src/content/stepper/stepper.ts"],"sourcesContent":["import {\n bind,\n createContext,\n define,\n getHost,\n html,\n onCleanup,\n onMounted,\n prop,\n provide,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed, type Readable, signal, watch } from '@vielzeug/ripple';\nimport { createInteraction, createListControl, elementDirection, lifecycleSignal } from '../../core';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport { isStepNavigable } from './_is-step-navigable';\nimport componentStyles from './stepper.css?inline';\n\n// ── Context ─────────────────────────────────────────────────────────────────\n//\n// ore-step derives all of its parent-relative state (current/completed/navigable/\n// index/total/color/size/orientation) from this context instead of ore-stepper\n// pushing 8 attributes onto every child on every change. Matches the\n// provide()/inject() coordination already used by ore-tabs/ore-tab-item,\n// ore-radio-group/ore-radio, and ore-list/ore-list-item — ore-stepper was\n// previously the one outlier doing manual `querySelectorAll` + `setAttribute`\n// fan-out, which meant every reactive change re-walked and re-wrote every step\n// regardless of whether that step's own state actually changed.\n\n/** Context provided by ore-stepper to its ore-step children. */\nexport type StepperContext = {\n clickable: Readable<boolean>;\n color: Readable<ThemeColor | undefined>;\n currentValue: Readable<string | undefined>;\n linear: Readable<boolean>;\n orientation: Readable<'horizontal' | 'vertical'>;\n size: Readable<ComponentSize | undefined>;\n /** Ordered `value`s of every sibling `ore-step`, used to derive index/total/completed. */\n stepValues: Readable<string[]>;\n};\n/** Injection key for the stepper context. */\nexport const STEPPER_CTX = createContext<StepperContext>('StepperContext');\n\nexport type OreStepperEvents = {\n change: { value: string };\n};\n\nexport type OreStepperProps = {\n /** When true, steps are clickable/keyboard-focusable for navigation. Default: display-only progress. */\n clickable?: boolean;\n /** Theme color for the current/completed step indicators */\n color?: ThemeColor;\n /** Disables the whole stepper — no step is navigable regardless of `clickable` */\n disabled?: boolean;\n /** Accessible label for the nav landmark */\n label?: string;\n /**\n * Restricts navigation to completed steps and the current step — steps ahead of the\n * current one cannot be clicked or focused, even when `clickable` is set.\n */\n linear?: boolean;\n /** Layout orientation — 'horizontal' (default, desktop) or 'vertical' (compact/mobile-friendly) */\n orientation?: 'horizontal' | 'vertical';\n /** Component size */\n size?: ComponentSize;\n /** The `value` of the currently active `ore-step` */\n value?: string;\n};\n\n/**\n * Displays progress through a sequence of numbered steps. Manages step selection and provides\n * shared state (current value, clickability, theming) to its `ore-step` children via context.\n * Can be purely informational (progress display) or interactive navigation.\n *\n * @element ore-stepper\n * @element ore-step - Child element for each step (auto-discovered)\n *\n * @attr {string} value - The value of the currently active step\n * @attr {boolean} clickable - Allow clicking/focusing steps to navigate\n * @attr {boolean} linear - Restrict navigation to completed + current steps only\n * @attr {boolean} disabled - Disables the whole stepper\n * @attr {string} orientation - 'horizontal' (default) | 'vertical'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} label - Accessible nav landmark label (default: 'Progress')\n *\n * @fires change - Emitted when the active step changes via click/keyboard. detail: { value: string }\n *\n * @slot - `ore-step` elements\n *\n * @cssprop --stepper-connector-color - Color of the connector line between steps\n * @cssprop --stepper-connector-size - Thickness of the connector line\n * @cssprop --stepper-gap - Gap between steps\n *\n * @part nav - Navigation landmark element\n * @part list - Ordered list container holding the slotted steps\n *\n * @example\n * ```html\n * <!-- Display-only progress -->\n * <ore-stepper value=\"shipping\" color=\"primary\">\n * <ore-step value=\"cart\">Cart</ore-step>\n * <ore-step value=\"shipping\">Shipping</ore-step>\n * <ore-step value=\"payment\">Payment</ore-step>\n * </ore-stepper>\n *\n * <!-- Clickable navigation, mobile-friendly vertical layout -->\n * <ore-stepper value=\"shipping\" clickable linear orientation=\"vertical\">\n * <ore-step value=\"cart\">Cart</ore-step>\n * <ore-step value=\"shipping\">Shipping</ore-step>\n * <ore-step value=\"payment\" disabled>Payment</ore-step>\n * </ore-stepper>\n * ```\n */\nexport const STEPPER_TAG = 'ore-stepper' as const;\ndefine<OreStepperProps>(STEPPER_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n clickable: prop.bool(false),\n label: prop.string('Progress'),\n linear: prop.bool(false),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreStepperEvents>();\n const slots = useSlots();\n\n const getSteps = (): HTMLElement[] => Array.from(el.querySelectorAll(':scope > ore-step')) as HTMLElement[];\n\n const focusStep = (step: HTMLElement | undefined) => {\n step?.shadowRoot?.querySelector<HTMLElement>('button.control')?.focus();\n };\n\n // ────────────────────────────────────────────────────────────────\n // Selection State — mirrors ore-tabs' `selectedValue` signal + `ensureSelection()` pattern\n // ────────────────────────────────────────────────────────────────\n\n const currentValue = signal<string | undefined>(props.value.value);\n\n bind({ attr: { value: () => currentValue.value ?? null } });\n\n const setSelection = (value: string | undefined, shouldEmit = false) => {\n if (!value || value === currentValue.value) return;\n\n currentValue.value = value;\n\n if (shouldEmit) emit('change', { value });\n };\n\n const ensureSelection = () => {\n const steps = getSteps();\n\n if (steps.length === 0) return;\n\n const current = currentValue.value;\n const hasCurrent = current ? steps.some((s) => s.getAttribute('value') === current) : false;\n\n if (hasCurrent) return;\n\n const firstEnabled = steps.find((s) => !s.hasAttribute('disabled'))?.getAttribute('value') ?? undefined;\n\n if (firstEnabled) setSelection(firstEnabled);\n };\n\n watch(props.value, (value) => {\n currentValue.value = value;\n ensureSelection();\n });\n\n // ────────────────────────────────────────────────────────────────\n // Context provided to ore-step children\n // ────────────────────────────────────────────────────────────────\n\n const stepValues = computed(() => {\n void slots.elements().value;\n\n return getSteps().map((s) => s.getAttribute('value') ?? '');\n });\n\n provide(STEPPER_CTX, {\n clickable: computed(() => Boolean(props.clickable.value) && !props.disabled.value),\n color: props.color,\n currentValue,\n linear: computed(() => Boolean(props.linear.value)),\n orientation: computed(() => props.orientation.value ?? 'horizontal'),\n size: props.size,\n stepValues,\n });\n\n // Deferred to `onMounted()` (mirrors ore-tabs' `ensureSelection()` timing) rather than an\n // immediate `watch(stepValues, ...)` — `getSteps()` walks the *live* light DOM, and while the\n // browser is still parsing this element's `ore-step` children (synchronously upgrading each\n // one as its own tag is reached, which happens whenever `customElements.define()` already\n // ran before this markup was parsed — exactly what a sandboxed live-preview iframe does by\n // design), `stepValues` observes that child list mid-populate. Reading it eagerly here used\n // to call `ensureSelection()` — and therefore write `currentValue` — once per step as each\n // one was discovered, interleaved with that *same* step's own first render effect further\n // down the reactive graph. That reentrant write during a child's not-yet-finished initial\n // render corrupted its rendered output (the step's clickable/static control silently failed\n // to mount at all). Onmounted's callback runs on a microtask, strictly after the whole\n // synchronous parse (and thus every child) has completed, so `getSteps()` sees the final list.\n onMounted(() => {\n ensureSelection();\n return undefined;\n });\n\n // ────────────────────────────────────────────────────────────────\n // Keyboard Navigation (roving tabindex over navigable steps)\n // ────────────────────────────────────────────────────────────────\n\n const getNavigableSteps = (): HTMLElement[] => {\n const steps = getSteps();\n const values = steps.map((s) => s.getAttribute('value') ?? '');\n const currentIndex = values.indexOf(currentValue.value ?? '');\n const clickable = Boolean(props.clickable.value) && !props.disabled.value;\n const linear = Boolean(props.linear.value);\n\n return steps.filter((step, index) =>\n isStepNavigable({\n disabled: step.hasAttribute('disabled'),\n index,\n linear,\n stepperClickable: clickable,\n stepperCurrentIndex: currentIndex,\n }),\n );\n };\n\n const listControl = createListControl({\n direction: () => elementDirection(el),\n getItems: getNavigableSteps,\n loop: false,\n onNavigate: ({ item }) => {\n // Select BEFORE focusing: changing `currentValue` re-renders each step's\n // control (the step's navigable/button node-slot depends on the current\n // index), which destroys the previously focused button. Ripple flushes\n // effects synchronously, so by the time focusStep() runs the replacement\n // button exists.\n const value = item.getAttribute('value');\n\n if (value) setSelection(value, true);\n\n focusStep(item);\n },\n orientation: () => (props.orientation.value === 'vertical' ? 'vertical' : 'horizontal'),\n signal: lifecycleSignal(onCleanup),\n });\n\n const handleStepClick = (e: Event) => {\n const step = e\n .composedPath()\n .find((node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-step');\n\n if (!step || step.closest(STEPPER_TAG) !== el || !getNavigableSteps().includes(step)) return;\n\n const value = step.getAttribute('value');\n\n setSelection(value ?? undefined, true);\n // The click focused the step's old control; the selection re-render replaced\n // it — restore focus onto the new one (see onNavigate's ordering note).\n focusStep(step);\n };\n\n const activateFocusedStep = (): void => {\n const steps = getNavigableSteps();\n const focusedStep = steps.find(\n (step) => step === document.activeElement || step.shadowRoot?.activeElement === document.activeElement,\n );\n const value = focusedStep?.getAttribute('value');\n\n if (value) {\n setSelection(value, true);\n focusStep(focusedStep);\n }\n };\n\n const activationPress = createInteraction({ onPress: activateFocusedStep });\n\n const handleKeydown = (e: KeyboardEvent) => {\n const steps = getNavigableSteps();\n\n if (steps.length === 0) return;\n\n const path = e.composedPath();\n const stepFromEvent = path.find(\n (node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-step',\n );\n const focused = stepFromEvent ? steps.indexOf(stepFromEvent) : -1;\n\n if (focused >= 0) listControl.set(focused);\n\n if (listControl.handleKeydown(e)) return;\n\n activationPress.handleKeydown(e);\n };\n\n bind({\n on: {\n click: handleStepClick,\n keydown: handleKeydown,\n },\n });\n\n return html`\n <nav part=\"nav\" aria-label=\"${props.label}\">\n <ol class=\"steps\" role=\"list\" part=\"list\">\n <slot></slot>\n </ol>\n </nav>\n `;\n },\n styles: [componentStyles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stepper.cjs","names":[],"sources":["../src/content/stepper/stepper.ts"],"sourcesContent":["import {\n bind,\n createContext,\n define,\n getHost,\n html,\n onCleanup,\n onMounted,\n prop,\n provide,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed, type Readable, signal, watch } from '@vielzeug/ripple';\nimport { createInteraction, createListControl, elementDirection, lifecycleSignal } from '../../core';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport { isStepNavigable } from './_is-step-navigable';\nimport componentStyles from './stepper.css?inline';\n\n// ── Context ─────────────────────────────────────────────────────────────────\n//\n// ore-step derives all of its parent-relative state (current/completed/navigable/\n// index/total/color/size/orientation) from this context instead of ore-stepper\n// pushing 8 attributes onto every child on every change. Matches the\n// provide()/inject() coordination already used by ore-tabs/ore-tab-item,\n// ore-radio-group/ore-radio, and ore-list/ore-list-item — ore-stepper was\n// previously the one outlier doing manual `querySelectorAll` + `setAttribute`\n// fan-out, which meant every reactive change re-walked and re-wrote every step\n// regardless of whether that step's own state actually changed.\n\n/** Context provided by ore-stepper to its ore-step children. */\nexport type StepperContext = {\n clickable: Readable<boolean>;\n color: Readable<ThemeColor | undefined>;\n currentValue: Readable<string | undefined>;\n linear: Readable<boolean>;\n orientation: Readable<'horizontal' | 'vertical'>;\n size: Readable<ComponentSize | undefined>;\n /** Ordered `value`s of every sibling `ore-step`, used to derive index/total/completed. */\n stepValues: Readable<string[]>;\n};\n/** Injection key for the stepper context. */\nexport const STEPPER_CTX = createContext<StepperContext>('StepperContext');\n\nexport type OreStepperEvents = {\n change: { value: string };\n};\n\nexport type OreStepperProps = {\n /** When true, steps are clickable/keyboard-focusable for navigation. Default: display-only progress. */\n clickable?: boolean;\n /** Theme color for the current/completed step indicators */\n color?: ThemeColor;\n /** Disables the whole stepper — no step is navigable regardless of `clickable` */\n disabled?: boolean;\n /** Accessible label for the nav landmark */\n label?: string;\n /**\n * Restricts navigation to completed steps and the current step — steps ahead of the\n * current one cannot be clicked or focused, even when `clickable` is set.\n */\n linear?: boolean;\n /** Layout orientation — 'horizontal' (default, desktop) or 'vertical' (compact/mobile-friendly) */\n orientation?: 'horizontal' | 'vertical';\n /** Component size */\n size?: ComponentSize;\n /** The `value` of the currently active `ore-step` */\n value?: string;\n};\n\n/**\n * Displays progress through a sequence of numbered steps. Manages step selection and provides\n * shared state (current value, clickability, theming) to its `ore-step` children via context.\n * Can be purely informational (progress display) or interactive navigation.\n *\n * @element ore-stepper\n * @element ore-step - Child element for each step (auto-discovered)\n *\n * @attr {string} value - The value of the currently active step\n * @attr {boolean} clickable - Allow clicking/focusing steps to navigate\n * @attr {boolean} linear - Restrict navigation to completed + current steps only\n * @attr {boolean} disabled - Disables the whole stepper\n * @attr {string} orientation - 'horizontal' (default) | 'vertical'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} label - Accessible nav landmark label (default: 'Progress')\n *\n * @fires change - Emitted when the active step changes via click/keyboard. detail: { value: string }\n *\n * @slot - `ore-step` elements\n *\n * @cssprop --stepper-connector-color - Color of the connector line between steps\n * @cssprop --stepper-connector-size - Thickness of the connector line\n * @cssprop --stepper-gap - Gap between steps\n *\n * @part nav - Navigation landmark element\n * @part list - Ordered list container holding the slotted steps\n *\n * @example\n * ```html\n * <!-- Display-only progress -->\n * <ore-stepper value=\"shipping\" color=\"primary\">\n * <ore-step value=\"cart\">Cart</ore-step>\n * <ore-step value=\"shipping\">Shipping</ore-step>\n * <ore-step value=\"payment\">Payment</ore-step>\n * </ore-stepper>\n *\n * <!-- Clickable navigation, mobile-friendly vertical layout -->\n * <ore-stepper value=\"shipping\" clickable linear orientation=\"vertical\">\n * <ore-step value=\"cart\">Cart</ore-step>\n * <ore-step value=\"shipping\">Shipping</ore-step>\n * <ore-step value=\"payment\" disabled>Payment</ore-step>\n * </ore-stepper>\n * ```\n */\nexport const STEPPER_TAG = 'ore-stepper' as const;\ndefine<OreStepperProps>(STEPPER_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n clickable: prop.bool(false),\n label: prop.string('Progress'),\n linear: prop.bool(false),\n orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n value: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreStepperEvents>();\n const slots = useSlots();\n\n const getSteps = (): HTMLElement[] => Array.from(el.querySelectorAll(':scope > ore-step')) as HTMLElement[];\n\n const focusStep = (step: HTMLElement | undefined) => {\n step?.shadowRoot?.querySelector<HTMLElement>('button.control')?.focus();\n };\n\n // ────────────────────────────────────────────────────────────────\n // Selection State — mirrors ore-tabs' `selectedValue` signal + `ensureSelection()` pattern\n // ────────────────────────────────────────────────────────────────\n\n const currentValue = signal<string | undefined>(props.value.value);\n\n bind({ attr: { value: () => currentValue.value ?? null } });\n\n const setSelection = (value: string | undefined, shouldEmit = false) => {\n if (!value || value === currentValue.value) return;\n\n currentValue.value = value;\n\n if (shouldEmit) emit('change', { value });\n };\n\n const ensureSelection = () => {\n const steps = getSteps();\n\n if (steps.length === 0) return;\n\n const current = currentValue.value;\n const hasCurrent = current ? steps.some((s) => s.getAttribute('value') === current) : false;\n\n if (hasCurrent) return;\n\n const firstEnabled = steps.find((s) => !s.hasAttribute('disabled'))?.getAttribute('value') ?? undefined;\n\n if (firstEnabled) setSelection(firstEnabled);\n };\n\n watch(props.value, (value) => {\n currentValue.value = value;\n ensureSelection();\n });\n\n // ────────────────────────────────────────────────────────────────\n // Context provided to ore-step children\n // ────────────────────────────────────────────────────────────────\n\n const stepValues = computed(() => {\n void slots.elements().value;\n\n return getSteps().map((s) => s.getAttribute('value') ?? '');\n });\n\n provide(STEPPER_CTX, {\n clickable: computed(() => Boolean(props.clickable.value) && !props.disabled.value),\n color: props.color,\n currentValue,\n linear: computed(() => Boolean(props.linear.value)),\n orientation: computed(() => props.orientation.value ?? 'horizontal'),\n size: props.size,\n stepValues,\n });\n\n // Deferred to `onMounted()` (mirrors ore-tabs' `ensureSelection()` timing) rather than an\n // immediate `watch(stepValues, ...)` — `getSteps()` walks the *live* light DOM, and while the\n // browser is still parsing this element's `ore-step` children (synchronously upgrading each\n // one as its own tag is reached, which happens whenever `customElements.define()` already\n // ran before this markup was parsed — exactly what a sandboxed live-preview iframe does by\n // design), `stepValues` observes that child list mid-populate. Reading it eagerly here used\n // to call `ensureSelection()` — and therefore write `currentValue` — once per step as each\n // one was discovered, interleaved with that *same* step's own first render effect further\n // down the reactive graph. That reentrant write during a child's not-yet-finished initial\n // render corrupted its rendered output (the step's clickable/static control silently failed\n // to mount at all). Onmounted's callback runs on a microtask, strictly after the whole\n // synchronous parse (and thus every child) has completed, so `getSteps()` sees the final list.\n onMounted(() => {\n ensureSelection();\n return undefined;\n });\n\n // ────────────────────────────────────────────────────────────────\n // Keyboard Navigation (roving tabindex over navigable steps)\n // ────────────────────────────────────────────────────────────────\n\n const getNavigableSteps = (): HTMLElement[] => {\n const steps = getSteps();\n const values = steps.map((s) => s.getAttribute('value') ?? '');\n const currentIndex = values.indexOf(currentValue.value ?? '');\n const clickable = Boolean(props.clickable.value) && !props.disabled.value;\n const linear = Boolean(props.linear.value);\n\n return steps.filter((step, index) =>\n isStepNavigable({\n disabled: step.hasAttribute('disabled'),\n index,\n linear,\n stepperClickable: clickable,\n stepperCurrentIndex: currentIndex,\n }),\n );\n };\n\n const listControl = createListControl({\n direction: () => elementDirection(el),\n getItems: getNavigableSteps,\n loop: false,\n onNavigate: ({ item }) => {\n // Select BEFORE focusing: changing `currentValue` re-renders each step's\n // control (the step's navigable/button node-slot depends on the current\n // index), which destroys the previously focused button. Ripple flushes\n // effects synchronously, so by the time focusStep() runs the replacement\n // button exists.\n const value = item.getAttribute('value');\n\n if (value) setSelection(value, true);\n\n focusStep(item);\n },\n orientation: () => (props.orientation.value === 'vertical' ? 'vertical' : 'horizontal'),\n signal: lifecycleSignal(onCleanup),\n });\n\n const handleStepClick = (e: Event) => {\n const step = e\n .composedPath()\n .find((node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-step');\n\n if (!step || step.closest(STEPPER_TAG) !== el || !getNavigableSteps().includes(step)) return;\n\n const value = step.getAttribute('value');\n\n setSelection(value ?? undefined, true);\n // The click focused the step's old control; the selection re-render replaced\n // it — restore focus onto the new one (see onNavigate's ordering note).\n focusStep(step);\n };\n\n const activateFocusedStep = (): void => {\n const steps = getNavigableSteps();\n const focusedStep = steps.find(\n (step) => step === document.activeElement || step.shadowRoot?.activeElement === document.activeElement,\n );\n const value = focusedStep?.getAttribute('value');\n\n if (value) {\n setSelection(value, true);\n focusStep(focusedStep);\n }\n };\n\n const activationPress = createInteraction({ onPress: activateFocusedStep });\n\n const handleKeydown = (e: KeyboardEvent) => {\n const steps = getNavigableSteps();\n\n if (steps.length === 0) return;\n\n const path = e.composedPath();\n const stepFromEvent = path.find(\n (node): node is HTMLElement => node instanceof HTMLElement && node.localName === 'ore-step',\n );\n const focused = stepFromEvent ? steps.indexOf(stepFromEvent) : -1;\n\n if (focused >= 0) listControl.set(focused);\n\n if (listControl.handleKeydown(e)) return;\n\n activationPress.handleKeydown(e);\n };\n\n bind({\n on: {\n click: handleStepClick,\n keydown: handleKeydown,\n },\n });\n\n return html`\n <nav part=\"nav\" aria-label=\"${props.label}\">\n <ol class=\"steps\" role=\"list\" part=\"list\">\n <slot></slot>\n </ol>\n </nav>\n `;\n },\n styles: [componentStyles],\n});\n"],"mappings":"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"}
|
package/dist/stepper.js
CHANGED
|
@@ -26,7 +26,7 @@ d(w, {
|
|
|
26
26
|
}, g = x(i.value.value);
|
|
27
27
|
l({ attr: { value: () => g.value ?? null } });
|
|
28
28
|
let w = (e, t = !1) => {
|
|
29
|
-
|
|
29
|
+
e && e !== g.value && (g.value = e, t && o("change", { value: e }));
|
|
30
30
|
}, T = () => {
|
|
31
31
|
let e = u();
|
|
32
32
|
if (e.length === 0) return;
|