Skip to content

feat(Slider): add new component and composable#143

Merged
johnleider merged 41 commits intomasterfrom
feat/slider-component
Mar 10, 2026
Merged

feat(Slider): add new component and composable#143
johnleider merged 41 commits intomasterfrom
feat/slider-component

Conversation

@johnleider
Copy link
Copy Markdown
Member

Summary

  • Add createSlider composable with value math, step snapping, percent conversion, and multi-thumb support
  • Add headless Slider compound component (Root, Track, Range, Thumb, HiddenInput) with full WAI-ARIA compliance
  • Support single thumb, range (two thumbs), crossover mode, vertical orientation, and form integration via hidden inputs
  • Add documentation pages for both the composable and component with live examples

Changes

Composable (packages/0/src/composables/createSlider/)

  • Value clamping, step snapping, percent ↔ value conversion
  • Multi-thumb with configurable minStepsBetweenThumbs and crossover options

Components (packages/0/src/components/Slider/)

  • SliderRoot — context provider, v-model bridge, centralized drag handling via useDocumentEventListener
  • SliderTrack — click-to-position with nearest-thumb snapping
  • SliderRange — filled region with CSS custom properties for positioning
  • SliderThumb — keyboard nav (Arrow, Home, End, PageUp/Down), ARIA attributes, focus management
  • SliderHiddenInput — form integration with hidden <input> per thumb

Docs (apps/docs/)

  • Component page: pages/components/forms/slider.md
  • Composable page: pages/composables/forms/create-slider.md
  • Examples: basic single thumb, range with min-steps-between-thumbs

Test plan

  • 628 component integration tests (rendering, keyboard, ARIA, range, disabled)
  • 150 composable unit tests (value math, clamping, snapping, percent conversion)
  • Manual browser testing: drag, track click, vertical, crossover, range
  • Full suite passes (3072 tests)
  • Lint + typecheck clean

@johnleider johnleider changed the title feat(Slider): add headless slider component and createSlider composable feat(Slider): add new component and composable Mar 4, 2026
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 4, 2026

Open in StackBlitz

commit: 58387dc

- Fix infinite recursive watch by guarding model/values sync with array comparison
- Forward useAttrs() in Track, Range, Thumb for inheritAttrs:false compatibility
- Include onPointerdown/onKeydown in slotProps.attrs for renderless mode
- Centralize drag logic in SliderRoot using useDocumentEventListener
- Register track element via watchEffect instead of onMounted
- Add crossover prop to allow range thumbs to pass through each other
…ling

- Normalize range start/end after percent conversion for inverted mode
- Use useToggleScope for drag listeners to prevent leak on unmount
- Set per-thumb aria-valuemin/valuemax per WAI-ARIA multi-thumb pattern
- Recycle thumb indices on unmount/remount with lowest-first allocation
- Guard step=0 in snap() to prevent NaN corruption
- Rename onPointerDown to onPointerdown matching DOM event convention
- Add per-property JSDoc with @see and @example to barrel export
- readonly prop blocks mutations while keeping thumbs focusable
  (aria-readonly, data-readonly on all sub-components)
- Thumb-edge drag offset prevents value jumping when grabbing
  near the edge of a thumb element
- start/end emits replace valueCommit for drag lifecycle events
type="hidden" inputs are never rendered by the browser, making the
visually-hidden CSS and inert attribute redundant.
…ts to createSelection

createModel is now a single-value store with simplified select/unselect/apply.
Selection-specific concepts (mandatory, multiple, seek, mandate, onboard)
moved to createSelection where they belong.
apply() now detects ref-based ticket values and updates them directly
instead of browse resolution. selectedValues uses toValue() for automatic
reactivity tracking. Updated input example to single-ticket pattern and
color palette to multi-ticket composite with createSelection.
9-task TDD plan covering composable rewrite, test updates,
component changes, and validation.
Each thumb is now a model ticket with a shallowRef<number> value
instead of a standalone ref<number[]>. Values are derived from
ordered tickets via computed, enabling proper register/unregister
lifecycle. Adds registerThumb/unregisterThumb API, pending values
pattern for timing (useProxyModel fires apply before thumbs mount),
and range option.

BREAKING CHANGE: values is now ComputedRef<number[]> (was Ref<number[]>).
Direct assignment (slider.values.value = [...]) no longer works.
Use registerThumb/apply instead.
Replace direct values assignment with registerThumb/addThumb helper.
Add test coverage for registerThumb, unregisterThumb, apply with
pending values, and multi-thumb ordering.
Registration/unregistration now lives in createSlider via model tickets.
SliderRootContext no longer declares registerThumb/unregisterThumb —
they come through the SliderContext spread.
Register via root.registerThumb() which now returns a ModelTicket.
Index is reactive (ticket.index), value reads from ticket.value.
All index references updated to use .value since it's now a Ref.
- Rewrite slider.md: anatomy, architecture mermaid, accessibility,
  trimmed recipes (form integration, drag events, data attributes)
- Add audio equalizer example (useEqualizer.ts + Equalizer.vue +
  equalizer.vue) showcasing vertical sliders and composable state
- Add HSL color picker example (ColorSlider.vue + ColorPicker.vue +
  color-picker.vue) with reactive gradient tracks
- Add single-file examples: vertical, steps, states
- Update basic.vue to show both single and range modes
- Add section content rules to docs.md (usage, anatomy, examples,
  recipes conventions for component vs composable pages)
@johnleider johnleider force-pushed the feat/slider-component branch from 170470d to 90166d8 Compare March 10, 2026 02:32
- percent → fromValue
- setValue → set
- stepUp → up
- stepDown → down
- setToMin → floor
- setToMax → ceil
- Media scrubber: provider/consumer pattern with waveform visualization
- Theme comparison: clip-path reveal with data-theme scoping
- Update API references for renamed methods
- Remove old basic.vue example
- Fix isDragging warning in Equalizer example
Add comprehensive documentation to SliderOptions and SliderContext
interfaces matching the quality bar set by createRegistry and
createModel. Each method now has @param, @remarks, @see, and
@example tags.
Align register() with createRegistry/createModel convention by
accepting { value: N } in addition to the number shorthand.
createSlider needs all thumbs selected simultaneously, but select()
always cleared selectedIds first. Adding multiple: true lets select()
and apply() accumulate IDs instead of replacing, removing the manual
selectedIds.add() workaround in createSlider.
SelectionOptions extends ModelOptions and declares multiple as
MaybeRefOrGetter<boolean>. The narrower boolean type caused TS2430.
@johnleider johnleider self-assigned this Mar 10, 2026
@johnleider johnleider added enhancement New feature or request C: Slider E: createSlider and removed enhancement New feature or request labels Mar 10, 2026
@johnleider johnleider added this to the v0.2.0 milestone Mar 10, 2026
@johnleider johnleider merged commit 451e1f3 into master Mar 10, 2026
9 of 10 checks passed
@johnleider johnleider deleted the feat/slider-component branch March 10, 2026 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant