@proyecto-viviana/ui 0.3.2 → 0.3.3
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/components.css +1077 -1077
- package/dist/index.js +236 -249
- package/dist/index.js.map +3 -3
- package/dist/index.ssr.js +78 -81
- package/dist/index.ssr.js.map +3 -3
- package/dist/radio/index.d.ts +12 -27
- package/dist/radio/index.d.ts.map +1 -1
- package/dist/test-utils/index.d.ts +2 -2
- package/dist/test-utils/index.d.ts.map +1 -1
- package/package.json +13 -12
- package/src/alert/index.tsx +48 -0
- package/src/assets/favicon.png +0 -0
- package/src/assets/fire.gif +0 -0
- package/src/autocomplete/index.tsx +313 -0
- package/src/avatar/index.tsx +75 -0
- package/src/badge/index.tsx +43 -0
- package/src/breadcrumbs/index.tsx +207 -0
- package/src/button/Button.tsx +74 -0
- package/src/button/index.ts +2 -0
- package/src/button/types.ts +24 -0
- package/src/calendar/DateField.tsx +200 -0
- package/src/calendar/DatePicker.tsx +298 -0
- package/src/calendar/RangeCalendar.tsx +236 -0
- package/src/calendar/TimeField.tsx +196 -0
- package/src/calendar/index.tsx +223 -0
- package/src/checkbox/index.tsx +257 -0
- package/src/color/index.tsx +687 -0
- package/src/combobox/index.tsx +383 -0
- package/src/components.css +1077 -0
- package/src/custom/calendar-card/index.tsx +66 -0
- package/src/custom/chip/index.tsx +46 -0
- package/src/custom/conversation/index.tsx +105 -0
- package/src/custom/event-card/index.tsx +132 -0
- package/src/custom/header/index.tsx +33 -0
- package/src/custom/lateral-nav/index.tsx +88 -0
- package/src/custom/logo/index.tsx +58 -0
- package/src/custom/nav-header/index.tsx +42 -0
- package/src/custom/page-layout/index.tsx +29 -0
- package/src/custom/profile-card/index.tsx +64 -0
- package/src/custom/project-card/index.tsx +59 -0
- package/src/custom/timeline-item/index.tsx +105 -0
- package/src/dialog/Dialog.tsx +260 -0
- package/src/dialog/index.tsx +3 -0
- package/src/disclosure/index.tsx +307 -0
- package/src/gridlist/index.tsx +403 -0
- package/src/icon/icons/GitHubIcon.tsx +20 -0
- package/src/icon/index.tsx +48 -0
- package/src/index.ts +322 -0
- package/src/landmark/index.tsx +231 -0
- package/src/link/index.tsx +130 -0
- package/src/listbox/index.tsx +231 -0
- package/src/menu/index.tsx +297 -0
- package/src/meter/index.tsx +163 -0
- package/src/numberfield/index.tsx +482 -0
- package/src/popover/index.tsx +260 -0
- package/src/progress-bar/index.tsx +169 -0
- package/src/radio/index.tsx +173 -0
- package/src/searchfield/index.tsx +453 -0
- package/src/select/index.tsx +349 -0
- package/src/separator/index.tsx +141 -0
- package/src/slider/index.tsx +382 -0
- package/src/styles.css +450 -0
- package/src/switch/ToggleSwitch.tsx +112 -0
- package/src/switch/index.tsx +90 -0
- package/src/table/index.tsx +531 -0
- package/src/tabs/index.tsx +273 -0
- package/src/tag-group/index.tsx +240 -0
- package/src/test-utils/index.ts +40 -0
- package/src/textfield/index.tsx +211 -0
- package/src/theme.css +101 -0
- package/src/toast/index.tsx +324 -0
- package/src/toolbar/index.tsx +108 -0
- package/src/tooltip/index.tsx +197 -0
- package/src/tree/index.tsx +494 -0
- package/dist/index.jsx +0 -6658
- package/dist/index.jsx.map +0 -7
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slider component for proyecto-viviana-ui
|
|
3
|
+
*
|
|
4
|
+
* A styled slider component with track, thumb, and value display.
|
|
5
|
+
* Built directly on solidaria hooks for full accessibility support.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type JSX, splitProps, mergeProps as solidMergeProps, Show } from 'solid-js'
|
|
9
|
+
import {
|
|
10
|
+
createSlider,
|
|
11
|
+
createFocusRing,
|
|
12
|
+
createHover,
|
|
13
|
+
type AriaSliderProps,
|
|
14
|
+
} from '@proyecto-viviana/solidaria'
|
|
15
|
+
import {
|
|
16
|
+
createSliderState,
|
|
17
|
+
type SliderOrientation,
|
|
18
|
+
} from '@proyecto-viviana/solid-stately'
|
|
19
|
+
|
|
20
|
+
// ============================================
|
|
21
|
+
// TYPES
|
|
22
|
+
// ============================================
|
|
23
|
+
|
|
24
|
+
export type SliderSize = 'sm' | 'md' | 'lg'
|
|
25
|
+
export type SliderVariant = 'default' | 'accent'
|
|
26
|
+
|
|
27
|
+
export interface SliderProps extends Omit<AriaSliderProps, 'label'> {
|
|
28
|
+
/** The size of the slider. */
|
|
29
|
+
size?: SliderSize
|
|
30
|
+
/** The visual variant of the slider. */
|
|
31
|
+
variant?: SliderVariant
|
|
32
|
+
/** Additional CSS class name. */
|
|
33
|
+
class?: string
|
|
34
|
+
/** Label text for the slider. */
|
|
35
|
+
label?: string
|
|
36
|
+
/** The current value (controlled). */
|
|
37
|
+
value?: number
|
|
38
|
+
/** The default value (uncontrolled). */
|
|
39
|
+
defaultValue?: number
|
|
40
|
+
/** Handler called when the value changes. */
|
|
41
|
+
onChange?: (value: number) => void
|
|
42
|
+
/** Handler called when dragging ends. */
|
|
43
|
+
onChangeEnd?: (value: number) => void
|
|
44
|
+
/** The minimum value. */
|
|
45
|
+
minValue?: number
|
|
46
|
+
/** The maximum value. */
|
|
47
|
+
maxValue?: number
|
|
48
|
+
/** The step value. */
|
|
49
|
+
step?: number
|
|
50
|
+
/** The orientation of the slider. */
|
|
51
|
+
orientation?: SliderOrientation
|
|
52
|
+
/** The locale for number formatting. */
|
|
53
|
+
locale?: string
|
|
54
|
+
/** Number format options. */
|
|
55
|
+
formatOptions?: Intl.NumberFormatOptions
|
|
56
|
+
/** Whether to show the value output. */
|
|
57
|
+
showOutput?: boolean
|
|
58
|
+
/** Whether to show min/max labels. */
|
|
59
|
+
showMinMax?: boolean
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ============================================
|
|
63
|
+
// STYLES
|
|
64
|
+
// ============================================
|
|
65
|
+
|
|
66
|
+
const sizeStyles = {
|
|
67
|
+
sm: {
|
|
68
|
+
track: 'h-1',
|
|
69
|
+
thumb: 'w-3 h-3 -mt-1',
|
|
70
|
+
label: 'text-sm',
|
|
71
|
+
output: 'text-xs',
|
|
72
|
+
},
|
|
73
|
+
md: {
|
|
74
|
+
track: 'h-2',
|
|
75
|
+
thumb: 'w-4 h-4 -mt-1',
|
|
76
|
+
label: 'text-sm',
|
|
77
|
+
output: 'text-sm',
|
|
78
|
+
},
|
|
79
|
+
lg: {
|
|
80
|
+
track: 'h-3',
|
|
81
|
+
thumb: 'w-5 h-5 -mt-1',
|
|
82
|
+
label: 'text-base',
|
|
83
|
+
output: 'text-base',
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ============================================
|
|
88
|
+
// COMPONENT
|
|
89
|
+
// ============================================
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A slider allows users to select a value from a range.
|
|
93
|
+
*
|
|
94
|
+
* Built directly on solidaria hooks for full accessibility support.
|
|
95
|
+
*/
|
|
96
|
+
export function Slider(props: SliderProps): JSX.Element {
|
|
97
|
+
const defaultProps: Partial<SliderProps> = {
|
|
98
|
+
size: 'md',
|
|
99
|
+
variant: 'default',
|
|
100
|
+
minValue: 0,
|
|
101
|
+
maxValue: 100,
|
|
102
|
+
step: 1,
|
|
103
|
+
orientation: 'horizontal',
|
|
104
|
+
showOutput: true,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const merged = solidMergeProps(defaultProps, props)
|
|
108
|
+
|
|
109
|
+
const [local, stateProps, ariaProps] = splitProps(merged, [
|
|
110
|
+
'size',
|
|
111
|
+
'variant',
|
|
112
|
+
'class',
|
|
113
|
+
'label',
|
|
114
|
+
'showOutput',
|
|
115
|
+
'showMinMax',
|
|
116
|
+
], [
|
|
117
|
+
'value',
|
|
118
|
+
'defaultValue',
|
|
119
|
+
'onChange',
|
|
120
|
+
'onChangeEnd',
|
|
121
|
+
'minValue',
|
|
122
|
+
'maxValue',
|
|
123
|
+
'step',
|
|
124
|
+
'orientation',
|
|
125
|
+
'locale',
|
|
126
|
+
'formatOptions',
|
|
127
|
+
])
|
|
128
|
+
|
|
129
|
+
const size = () => sizeStyles[local.size!]
|
|
130
|
+
|
|
131
|
+
// Track ref for pointer handling
|
|
132
|
+
let trackRef: HTMLDivElement | undefined
|
|
133
|
+
|
|
134
|
+
// Create slider state
|
|
135
|
+
const state = createSliderState({
|
|
136
|
+
get value() {
|
|
137
|
+
return stateProps.value
|
|
138
|
+
},
|
|
139
|
+
get defaultValue() {
|
|
140
|
+
return stateProps.defaultValue
|
|
141
|
+
},
|
|
142
|
+
get onChange() {
|
|
143
|
+
return stateProps.onChange
|
|
144
|
+
},
|
|
145
|
+
get onChangeEnd() {
|
|
146
|
+
return stateProps.onChangeEnd
|
|
147
|
+
},
|
|
148
|
+
get minValue() {
|
|
149
|
+
return stateProps.minValue
|
|
150
|
+
},
|
|
151
|
+
get maxValue() {
|
|
152
|
+
return stateProps.maxValue
|
|
153
|
+
},
|
|
154
|
+
get step() {
|
|
155
|
+
return stateProps.step
|
|
156
|
+
},
|
|
157
|
+
get orientation() {
|
|
158
|
+
return stateProps.orientation
|
|
159
|
+
},
|
|
160
|
+
get locale() {
|
|
161
|
+
return stateProps.locale
|
|
162
|
+
},
|
|
163
|
+
get formatOptions() {
|
|
164
|
+
return stateProps.formatOptions
|
|
165
|
+
},
|
|
166
|
+
get isDisabled() {
|
|
167
|
+
return ariaProps.isDisabled
|
|
168
|
+
},
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
// Create slider aria props
|
|
172
|
+
const sliderAria = createSlider(
|
|
173
|
+
{
|
|
174
|
+
get label() {
|
|
175
|
+
return local.label
|
|
176
|
+
},
|
|
177
|
+
get 'aria-label'() {
|
|
178
|
+
return ariaProps['aria-label']
|
|
179
|
+
},
|
|
180
|
+
get 'aria-labelledby'() {
|
|
181
|
+
return ariaProps['aria-labelledby']
|
|
182
|
+
},
|
|
183
|
+
get 'aria-describedby'() {
|
|
184
|
+
return ariaProps['aria-describedby']
|
|
185
|
+
},
|
|
186
|
+
get isDisabled() {
|
|
187
|
+
return ariaProps.isDisabled
|
|
188
|
+
},
|
|
189
|
+
get orientation() {
|
|
190
|
+
return stateProps.orientation
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
state,
|
|
194
|
+
() => trackRef ?? null
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
// Create focus ring
|
|
198
|
+
const { isFocused, isFocusVisible, focusProps } = createFocusRing()
|
|
199
|
+
|
|
200
|
+
// Create hover
|
|
201
|
+
const { isHovered, hoverProps } = createHover({
|
|
202
|
+
get isDisabled() {
|
|
203
|
+
return ariaProps.isDisabled
|
|
204
|
+
},
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
// Compute classes
|
|
208
|
+
const containerClasses = () => {
|
|
209
|
+
const base = 'flex flex-col w-full'
|
|
210
|
+
const disabledClass = ariaProps.isDisabled ? 'opacity-60' : ''
|
|
211
|
+
const custom = local.class || ''
|
|
212
|
+
return [base, disabledClass, custom].filter(Boolean).join(' ')
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const labelRowClasses = () => {
|
|
216
|
+
return 'flex justify-between items-center mb-2'
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const trackContainerClasses = () => {
|
|
220
|
+
const base = 'relative w-full cursor-pointer'
|
|
221
|
+
const disabledClass = ariaProps.isDisabled ? 'cursor-not-allowed' : ''
|
|
222
|
+
return [base, disabledClass].filter(Boolean).join(' ')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const trackClasses = () => {
|
|
226
|
+
const base = 'w-full rounded-full bg-bg-300'
|
|
227
|
+
const sizeClass = size().track
|
|
228
|
+
return [base, sizeClass].filter(Boolean).join(' ')
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const fillClasses = () => {
|
|
232
|
+
const base = 'absolute top-0 left-0 h-full rounded-full transition-all'
|
|
233
|
+
const variantClass = local.variant === 'accent' ? 'bg-accent' : 'bg-primary-400'
|
|
234
|
+
return [base, variantClass].filter(Boolean).join(' ')
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const thumbClasses = () => {
|
|
238
|
+
const base = 'absolute top-1/2 rounded-full shadow-md transition-all cursor-grab'
|
|
239
|
+
const sizeClass = size().thumb
|
|
240
|
+
|
|
241
|
+
let stateClass: string
|
|
242
|
+
if (ariaProps.isDisabled) {
|
|
243
|
+
stateClass = 'bg-primary-400 cursor-not-allowed'
|
|
244
|
+
} else if (state.isDragging()) {
|
|
245
|
+
stateClass = local.variant === 'accent' ? 'bg-accent-400 scale-110 cursor-grabbing' : 'bg-primary-200 scale-110 cursor-grabbing'
|
|
246
|
+
} else if (isHovered()) {
|
|
247
|
+
stateClass = local.variant === 'accent' ? 'bg-accent-400 scale-105' : 'bg-primary-200 scale-105'
|
|
248
|
+
} else {
|
|
249
|
+
stateClass = local.variant === 'accent' ? 'bg-accent' : 'bg-primary-100'
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const focusClass = isFocusVisible() ? 'ring-2 ring-accent ring-offset-2 ring-offset-bg-100' : ''
|
|
253
|
+
|
|
254
|
+
return [base, sizeClass, stateClass, focusClass].filter(Boolean).join(' ')
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const labelClasses = () => {
|
|
258
|
+
const base = 'font-medium text-primary-200'
|
|
259
|
+
const sizeClass = size().label
|
|
260
|
+
return [base, sizeClass].filter(Boolean).join(' ')
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const outputClasses = () => {
|
|
264
|
+
const base = 'font-medium text-primary-100'
|
|
265
|
+
const sizeClass = size().output
|
|
266
|
+
return [base, sizeClass].filter(Boolean).join(' ')
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const minMaxClasses = () => {
|
|
270
|
+
const base = 'text-xs text-primary-400'
|
|
271
|
+
return base
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Clean props helpers
|
|
275
|
+
const cleanGroupProps = () => {
|
|
276
|
+
const { ref: _ref, ...rest } = sliderAria.groupProps as Record<string, unknown>
|
|
277
|
+
return rest
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const cleanTrackProps = () => {
|
|
281
|
+
const { ref: _ref, style: _style, ...rest } = sliderAria.trackProps as Record<string, unknown>
|
|
282
|
+
return rest
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const cleanThumbProps = () => {
|
|
286
|
+
const { ref: _ref, style: thumbStyle, ...rest } = sliderAria.thumbProps as Record<string, unknown>
|
|
287
|
+
// Extract positioning from thumbStyle
|
|
288
|
+
const style = thumbStyle as Record<string, string> | undefined
|
|
289
|
+
return { rest, style }
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const cleanFocusProps = () => {
|
|
293
|
+
const { ref: _ref, ...rest } = focusProps as Record<string, unknown>
|
|
294
|
+
return rest
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const cleanHoverProps = () => {
|
|
298
|
+
const { ref: _ref, ...rest } = hoverProps as Record<string, unknown>
|
|
299
|
+
return rest
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const cleanOutputProps = () => {
|
|
303
|
+
const { ref: _ref, ...rest } = sliderAria.outputProps as Record<string, unknown>
|
|
304
|
+
return rest
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const thumbData = () => cleanThumbProps()
|
|
308
|
+
const percent = () => state.getValuePercent() * 100
|
|
309
|
+
|
|
310
|
+
return (
|
|
311
|
+
<div
|
|
312
|
+
{...cleanGroupProps()}
|
|
313
|
+
class={containerClasses()}
|
|
314
|
+
data-disabled={ariaProps.isDisabled || undefined}
|
|
315
|
+
data-orientation={state.orientation}
|
|
316
|
+
>
|
|
317
|
+
{/* Label and Output Row */}
|
|
318
|
+
<Show when={local.label || local.showOutput}>
|
|
319
|
+
<div class={labelRowClasses()}>
|
|
320
|
+
<Show when={local.label}>
|
|
321
|
+
<span {...sliderAria.labelProps} class={labelClasses()}>
|
|
322
|
+
{local.label}
|
|
323
|
+
</span>
|
|
324
|
+
</Show>
|
|
325
|
+
<Show when={local.showOutput}>
|
|
326
|
+
<output {...cleanOutputProps()} class={outputClasses()}>
|
|
327
|
+
{state.getFormattedValue()}
|
|
328
|
+
</output>
|
|
329
|
+
</Show>
|
|
330
|
+
</div>
|
|
331
|
+
</Show>
|
|
332
|
+
|
|
333
|
+
{/* Track Container */}
|
|
334
|
+
<div class={trackContainerClasses()}>
|
|
335
|
+
{/* Track */}
|
|
336
|
+
<div
|
|
337
|
+
ref={(el) => (trackRef = el)}
|
|
338
|
+
{...cleanTrackProps()}
|
|
339
|
+
class={trackClasses()}
|
|
340
|
+
style={{ 'touch-action': 'none' }}
|
|
341
|
+
>
|
|
342
|
+
{/* Fill */}
|
|
343
|
+
<div
|
|
344
|
+
class={fillClasses()}
|
|
345
|
+
style={{ width: `${percent()}%` }}
|
|
346
|
+
/>
|
|
347
|
+
</div>
|
|
348
|
+
|
|
349
|
+
{/* Thumb */}
|
|
350
|
+
<div
|
|
351
|
+
{...thumbData().rest}
|
|
352
|
+
{...cleanFocusProps()}
|
|
353
|
+
{...cleanHoverProps()}
|
|
354
|
+
class={thumbClasses()}
|
|
355
|
+
style={{
|
|
356
|
+
left: `${percent()}%`,
|
|
357
|
+
transform: 'translateX(-50%)',
|
|
358
|
+
...(thumbData().style || {}),
|
|
359
|
+
}}
|
|
360
|
+
data-dragging={state.isDragging() || undefined}
|
|
361
|
+
data-focused={isFocused() || undefined}
|
|
362
|
+
data-focus-visible={isFocusVisible() || undefined}
|
|
363
|
+
data-hovered={isHovered() || undefined}
|
|
364
|
+
/>
|
|
365
|
+
</div>
|
|
366
|
+
|
|
367
|
+
{/* Min/Max Labels */}
|
|
368
|
+
<Show when={local.showMinMax}>
|
|
369
|
+
<div class="flex justify-between mt-1">
|
|
370
|
+
<span class={minMaxClasses()}>{state.minValue}</span>
|
|
371
|
+
<span class={minMaxClasses()}>{state.maxValue}</span>
|
|
372
|
+
</div>
|
|
373
|
+
</Show>
|
|
374
|
+
|
|
375
|
+
{/* Hidden input for form submission */}
|
|
376
|
+
<input {...sliderAria.inputProps} />
|
|
377
|
+
</div>
|
|
378
|
+
)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Re-export types
|
|
382
|
+
export type { SliderState, SliderOrientation } from '@proyecto-viviana/solid-stately'
|