Skip to content

Commit

Permalink
chore: Slider
Browse files Browse the repository at this point in the history
  • Loading branch information
lerte committed Oct 15, 2024
1 parent 05242b0 commit 93968a7
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 212 deletions.
5 changes: 4 additions & 1 deletion apps/docs/src/usages/sliders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { Slider } from 'actify'

export default () => {
return (
<Slider aria-label="Slider" className="w-full" labeled defaultValue={50} />
<>
<Slider label="Opacity" labeled defaultValue={50} className="w-full" />
<Slider label="Volume" labeled defaultValue={50} orientation="vertical" />
</>
)
}
101 changes: 32 additions & 69 deletions packages/actify/src/components/Sliders/Slider.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
'use client'

import { AriaSliderProps, useNumberFormatter, useSlider } from 'react-aria'
import React, { useId } from 'react'

import { Elevation } from './../Elevation'
import { FocusRing } from '../FocusRing'
import { Ripple } from './../Ripple'
import React from 'react'
import { Thumb } from './Thumb'
import clsx from 'clsx'
import styles from './slider.module.css'
Expand All @@ -19,21 +14,12 @@ type SliderProps = {
} & AriaSliderProps

const Slider = (props: SliderProps) => {
const {
id,
labeled,
className,
minValue = 0,
maxValue = 100,
isDisabled
} = props
const { labeled, minValue = 0, maxValue = 100 } = props

const sliderId = id || `actify-slider${useId()}`
const trackRef = React.useRef(null)
const numberFormatter = useNumberFormatter(props.formatOptions)
const state = useSliderState({ ...props, numberFormatter })

const { groupProps, trackProps, outputProps } = useSlider(
const { groupProps, trackProps, labelProps, outputProps } = useSlider(
props,
state,
trackRef
Expand All @@ -45,64 +31,41 @@ const Slider = (props: SliderProps) => {
return (
<div
{...groupProps}
role="presentation"
className={clsx(styles['slider'], className)}
style={
{
'--_tick-count': 100,
'--_start-fraction': 0,
'--_end-fraction': percent
} as React.CSSProperties
}
className={clsx(styles['slider'], styles[state.orientation])}
>
{/* Create a container for the label and output element. */}
{props.label && (
<div className={styles['label-container']}>
<label {...labelProps}>{props.label}</label>
<output {...outputProps}>{state.getThumbValueLabel(0)}</output>
</div>
)}
{/* The track element holds the visible track line and the thumb. */}
<div
style={
{
'--_tick-count': 100,
'--_start-fraction': 0,
'--_end-fraction': percent
} as React.CSSProperties
}
className={styles['container']}
{...trackProps}
ref={trackRef}
className={clsx(
styles['track'],
state.isDisabled && styles['disabled']
)}
>
<div
{...trackProps}
ref={trackRef}
style={{
position: 'absolute',
inset: 0
}}
className={clsx(state.isDisabled && 'disabled')}
>
<Thumb index={0} state={state} trackRef={trackRef} />
</div>
<div className={styles['track']}></div>
<div className={styles['handle-container-padded']}>
<div className={styles['handle-container-block']}>
<div className={styles['handle-container']}>
<div
style={{
zIndex: 1,
insetInlineEnd: 'calc(0px - var(--_state-layer-size) / 2)'
}}
className={styles['handle']}
>
<FocusRing />
<Ripple id={sliderId} disabled={isDisabled} />

<div className={styles['handle-nub']}>
<Elevation disabled={isDisabled} />
</div>
{/* labeled */}
{labeled && (
<output className={styles['label']} {...outputProps}>
<span style={{ zIndex: 1 }}>
{state.getThumbValueLabel(0)}
</span>
</output>
)}
</div>
</div>
</div>
</div>
<Thumb
index={0}
state={state}
labeled={labeled}
trackRef={trackRef}
outputProps={outputProps}
/>
</div>
</div>
)
}

Slider.displayName = 'Actify.Slider'

export { Slider }
45 changes: 34 additions & 11 deletions packages/actify/src/components/Sliders/Thumb.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
import {
AriaSliderThumbProps,
VisuallyHidden,
mergeProps,
useFocusRing,
useSliderThumb
} from 'react-aria'

import { Elevation } from '../Elevation/Elevation'
import { FocusRing } from '../FocusRing'
import React from 'react'
import { Ripple } from '../Ripple/Ripple'
import { SliderState } from 'react-stately'
import clsx from 'clsx'
import styles from './slider.module.css'

type ThumbProps = {
labeled?: boolean
state: SliderState
outputProps: React.OutputHTMLAttributes<HTMLOutputElement>
trackRef: React.RefObject<null>
} & AriaSliderThumbProps

const Thumb = (props: ThumbProps) => {
const { state, trackRef, index, name } = props
export function Thumb(props: ThumbProps) {
const { labeled, outputProps, state, trackRef, index, name } = props
const inputRef = React.useRef(null)
const { thumbProps, inputProps, isDragging } = useSliderThumb(
{
Expand All @@ -28,20 +34,37 @@ const Thumb = (props: ThumbProps) => {
state
)

const { focusProps, isFocusVisible } = useFocusRing()
const { focusProps, isFocused, isFocusVisible } = useFocusRing()

return (
<div
{...thumbProps}
className={clsx([
styles['input'],
isFocusVisible && 'focus',
isDragging && 'dragging'
])}
className={clsx(
styles['handle'],
isFocusVisible && styles['focus'],
isDragging && styles['dragging']
)}
>
<input ref={inputRef} {...mergeProps(inputProps, focusProps)} />
<div className={styles['thumb']}>
<VisuallyHidden>
<input ref={inputRef} {...mergeProps(inputProps, focusProps)} />
</VisuallyHidden>
{isFocusVisible && <FocusRing />}
<Ripple id={inputProps.id} disabled={state.isDisabled} />
<Elevation disabled={state.isDisabled} />
{/* labeled */}
{labeled && (
<output
{...outputProps}
className={styles['label']}
style={{
transform: `translateX(-50%) scale(${isFocused ? 1 : 0})`
}}
>
<span style={{ zIndex: 1 }}>{state.getThumbValueLabel(0)}</span>
</output>
)}
</div>
</div>
)
}

export { Thumb }
Loading

0 comments on commit 93968a7

Please sign in to comment.