@proyecto-viviana/ui 0.2.3 → 0.3.1

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.
Files changed (71) hide show
  1. package/dist/index.js +192 -179
  2. package/dist/index.js.map +3 -3
  3. package/dist/index.ssr.js +24 -21
  4. package/dist/index.ssr.js.map +3 -3
  5. package/dist/radio/index.d.ts +27 -12
  6. package/dist/radio/index.d.ts.map +1 -1
  7. package/package.json +12 -13
  8. package/src/alert/index.tsx +0 -48
  9. package/src/assets/favicon.png +0 -0
  10. package/src/assets/fire.gif +0 -0
  11. package/src/autocomplete/index.tsx +0 -313
  12. package/src/avatar/index.tsx +0 -75
  13. package/src/badge/index.tsx +0 -43
  14. package/src/breadcrumbs/index.tsx +0 -207
  15. package/src/button/Button.tsx +0 -74
  16. package/src/button/index.ts +0 -2
  17. package/src/button/types.ts +0 -24
  18. package/src/calendar/DateField.tsx +0 -200
  19. package/src/calendar/DatePicker.tsx +0 -298
  20. package/src/calendar/RangeCalendar.tsx +0 -236
  21. package/src/calendar/TimeField.tsx +0 -196
  22. package/src/calendar/index.tsx +0 -223
  23. package/src/checkbox/index.tsx +0 -257
  24. package/src/color/index.tsx +0 -687
  25. package/src/combobox/index.tsx +0 -383
  26. package/src/components.css +0 -1077
  27. package/src/custom/calendar-card/index.tsx +0 -66
  28. package/src/custom/chip/index.tsx +0 -46
  29. package/src/custom/conversation/index.tsx +0 -105
  30. package/src/custom/event-card/index.tsx +0 -132
  31. package/src/custom/header/index.tsx +0 -33
  32. package/src/custom/lateral-nav/index.tsx +0 -88
  33. package/src/custom/logo/index.tsx +0 -58
  34. package/src/custom/nav-header/index.tsx +0 -42
  35. package/src/custom/page-layout/index.tsx +0 -29
  36. package/src/custom/profile-card/index.tsx +0 -64
  37. package/src/custom/project-card/index.tsx +0 -59
  38. package/src/custom/timeline-item/index.tsx +0 -105
  39. package/src/dialog/Dialog.tsx +0 -260
  40. package/src/dialog/index.tsx +0 -3
  41. package/src/disclosure/index.tsx +0 -307
  42. package/src/gridlist/index.tsx +0 -403
  43. package/src/icon/icons/GitHubIcon.tsx +0 -20
  44. package/src/icon/index.tsx +0 -48
  45. package/src/index.ts +0 -322
  46. package/src/landmark/index.tsx +0 -231
  47. package/src/link/index.tsx +0 -130
  48. package/src/listbox/index.tsx +0 -231
  49. package/src/menu/index.tsx +0 -297
  50. package/src/meter/index.tsx +0 -163
  51. package/src/numberfield/index.tsx +0 -482
  52. package/src/popover/index.tsx +0 -260
  53. package/src/progress-bar/index.tsx +0 -169
  54. package/src/radio/index.tsx +0 -173
  55. package/src/searchfield/index.tsx +0 -453
  56. package/src/select/index.tsx +0 -349
  57. package/src/separator/index.tsx +0 -141
  58. package/src/slider/index.tsx +0 -382
  59. package/src/styles.css +0 -450
  60. package/src/switch/ToggleSwitch.tsx +0 -112
  61. package/src/switch/index.tsx +0 -90
  62. package/src/table/index.tsx +0 -531
  63. package/src/tabs/index.tsx +0 -273
  64. package/src/tag-group/index.tsx +0 -240
  65. package/src/test-utils/index.ts +0 -32
  66. package/src/textfield/index.tsx +0 -211
  67. package/src/theme.css +0 -101
  68. package/src/toast/index.tsx +0 -324
  69. package/src/toolbar/index.tsx +0 -108
  70. package/src/tooltip/index.tsx +0 -197
  71. package/src/tree/index.tsx +0 -494
@@ -1,687 +0,0 @@
1
- /**
2
- * Color components for proyecto-viviana-ui
3
- *
4
- * Styled color picker components built on top of solidaria-components.
5
- * Inspired by Spectrum 2's color picker patterns.
6
- */
7
-
8
- import { type JSX, splitProps, createContext, useContext, Show } from 'solid-js'
9
- import {
10
- ColorSlider as HeadlessColorSlider,
11
- ColorSliderTrack as HeadlessColorSliderTrack,
12
- ColorSliderThumb as HeadlessColorSliderThumb,
13
- ColorArea as HeadlessColorArea,
14
- ColorAreaGradient as HeadlessColorAreaGradient,
15
- ColorAreaThumb as HeadlessColorAreaThumb,
16
- ColorWheel as HeadlessColorWheel,
17
- ColorWheelTrack as HeadlessColorWheelTrack,
18
- ColorWheelThumb as HeadlessColorWheelThumb,
19
- ColorField as HeadlessColorField,
20
- ColorFieldInput as HeadlessColorFieldInput,
21
- ColorSwatch as HeadlessColorSwatch,
22
- type ColorSliderProps as HeadlessColorSliderProps,
23
- type ColorAreaProps as HeadlessColorAreaProps,
24
- type ColorWheelProps as HeadlessColorWheelProps,
25
- type ColorFieldProps as HeadlessColorFieldProps,
26
- type ColorSwatchProps as HeadlessColorSwatchProps,
27
- type ColorSliderRenderProps,
28
- type ColorSliderTrackRenderProps,
29
- type ColorSliderThumbRenderProps,
30
- type ColorAreaRenderProps,
31
- type ColorAreaThumbRenderProps,
32
- type ColorWheelRenderProps,
33
- type ColorWheelThumbRenderProps,
34
- type ColorFieldRenderProps,
35
- type ColorSwatchRenderProps,
36
- } from '@proyecto-viviana/solidaria-components'
37
- import type { Color, ColorChannel, ColorFormat } from '@proyecto-viviana/solid-stately'
38
-
39
- // ============================================
40
- // SIZE CONTEXT
41
- // ============================================
42
-
43
- export type ColorSize = 'sm' | 'md' | 'lg'
44
-
45
- interface ColorContextValue {
46
- size: ColorSize
47
- }
48
-
49
- const ColorSizeContext = createContext<ColorContextValue>({ size: 'md' })
50
-
51
- // ============================================
52
- // STYLES
53
- // ============================================
54
-
55
- const sizeStyles = {
56
- sm: {
57
- slider: {
58
- track: 'h-4 rounded',
59
- thumb: 'w-4 h-4',
60
- label: 'text-sm',
61
- },
62
- area: {
63
- container: 'w-48 h-48',
64
- thumb: 'w-4 h-4',
65
- },
66
- wheel: {
67
- container: 'w-48 h-48',
68
- track: 'stroke-[16px]',
69
- thumb: 'w-4 h-4',
70
- },
71
- field: {
72
- input: 'h-8 text-sm px-2',
73
- label: 'text-sm',
74
- },
75
- swatch: 'w-8 h-8',
76
- },
77
- md: {
78
- slider: {
79
- track: 'h-6 rounded-md',
80
- thumb: 'w-5 h-5',
81
- label: 'text-base',
82
- },
83
- area: {
84
- container: 'w-64 h-64',
85
- thumb: 'w-5 h-5',
86
- },
87
- wheel: {
88
- container: 'w-64 h-64',
89
- track: 'stroke-[20px]',
90
- thumb: 'w-5 h-5',
91
- },
92
- field: {
93
- input: 'h-10 text-base px-3',
94
- label: 'text-base',
95
- },
96
- swatch: 'w-10 h-10',
97
- },
98
- lg: {
99
- slider: {
100
- track: 'h-8 rounded-lg',
101
- thumb: 'w-6 h-6',
102
- label: 'text-lg',
103
- },
104
- area: {
105
- container: 'w-80 h-80',
106
- thumb: 'w-6 h-6',
107
- },
108
- wheel: {
109
- container: 'w-80 h-80',
110
- track: 'stroke-[24px]',
111
- thumb: 'w-6 h-6',
112
- },
113
- field: {
114
- input: 'h-12 text-lg px-4',
115
- label: 'text-lg',
116
- },
117
- swatch: 'w-12 h-12',
118
- },
119
- }
120
-
121
- // ============================================
122
- // COLOR SLIDER
123
- // ============================================
124
-
125
- export interface ColorSliderProps extends Omit<HeadlessColorSliderProps, 'class' | 'style' | 'children'> {
126
- /** The size of the color slider. */
127
- size?: ColorSize
128
- /** Additional CSS class name. */
129
- class?: string
130
- /** Show the current value. */
131
- showValue?: boolean
132
- }
133
-
134
- /**
135
- * A color slider allows users to adjust a single color channel.
136
- *
137
- * @example
138
- * ```tsx
139
- * const [color, setColor] = createSignal(parseColor('hsl(0, 100%, 50%)'))
140
- *
141
- * <ColorSlider
142
- * channel="hue"
143
- * value={color()}
144
- * onChange={setColor}
145
- * label="Hue"
146
- * />
147
- * ```
148
- */
149
- export function ColorSlider(props: ColorSliderProps): JSX.Element {
150
- const [local, headlessProps] = splitProps(props, ['size', 'class', 'showValue'])
151
-
152
- const size = () => local.size ?? 'md'
153
- const styles = () => sizeStyles[size()]
154
- const customClass = local.class ?? ''
155
-
156
- const getClassName = (renderProps: ColorSliderRenderProps): string => {
157
- const base = 'flex flex-col gap-1.5'
158
- let stateClass = ''
159
- if (renderProps.isDisabled) {
160
- stateClass = 'opacity-50'
161
- }
162
- return [base, stateClass, customClass].filter(Boolean).join(' ')
163
- }
164
-
165
- const contextValue = () => ({ size: size() })
166
-
167
- return (
168
- <ColorSizeContext.Provider value={contextValue()}>
169
- <HeadlessColorSlider {...headlessProps} class={getClassName}>
170
- {(renderProps: ColorSliderRenderProps) => (
171
- <>
172
- <div class="flex items-center justify-between">
173
- <Show when={headlessProps.label}>
174
- <span class={`text-primary-200 font-medium ${styles().slider.label}`}>
175
- {headlessProps.label}
176
- </span>
177
- </Show>
178
- <Show when={local.showValue}>
179
- <span class={`text-primary-400 ${styles().slider.label}`}>
180
- {Math.round(renderProps.value)}
181
- </span>
182
- </Show>
183
- </div>
184
- <ColorSliderTrack>
185
- {() => <ColorSliderThumb />}
186
- </ColorSliderTrack>
187
- </>
188
- )}
189
- </HeadlessColorSlider>
190
- </ColorSizeContext.Provider>
191
- )
192
- }
193
-
194
- /**
195
- * The track component for a color slider.
196
- */
197
- export function ColorSliderTrack(props: { children?: JSX.Element | (() => JSX.Element); class?: string }): JSX.Element {
198
- const context = useContext(ColorSizeContext)
199
- const styles = sizeStyles[context.size]
200
- const customClass = props.class ?? ''
201
-
202
- const getClassName = (renderProps: ColorSliderTrackRenderProps): string => {
203
- const base = `relative ${styles.slider.track} shadow-inner border border-bg-300`
204
- const dragClass = renderProps.isDragging ? 'cursor-grabbing' : 'cursor-pointer'
205
- return [base, dragClass, customClass].filter(Boolean).join(' ')
206
- }
207
-
208
- return (
209
- <HeadlessColorSliderTrack class={getClassName}>
210
- {props.children}
211
- </HeadlessColorSliderTrack>
212
- )
213
- }
214
-
215
- /**
216
- * The thumb component for a color slider.
217
- */
218
- export function ColorSliderThumb(props: { class?: string }): JSX.Element {
219
- const context = useContext(ColorSizeContext)
220
- const styles = sizeStyles[context.size]
221
- const customClass = props.class ?? ''
222
-
223
- const getClassName = (renderProps: ColorSliderThumbRenderProps): string => {
224
- const base = `${styles.slider.thumb} rounded-full border-2 border-white shadow-md cursor-grab`
225
- const dragClass = renderProps.isDragging ? 'cursor-grabbing scale-110' : ''
226
- const focusClass = renderProps.isFocusVisible ? 'ring-2 ring-accent-300 ring-offset-2' : ''
227
- const disabledClass = renderProps.isDisabled ? 'cursor-not-allowed' : ''
228
- return [base, dragClass, focusClass, disabledClass, customClass].filter(Boolean).join(' ')
229
- }
230
-
231
- return <HeadlessColorSliderThumb class={getClassName} />
232
- }
233
-
234
- // ============================================
235
- // COLOR AREA
236
- // ============================================
237
-
238
- export interface ColorAreaProps extends Omit<HeadlessColorAreaProps, 'class' | 'style' | 'children'> {
239
- /** The size of the color area. */
240
- size?: ColorSize
241
- /** Additional CSS class name. */
242
- class?: string
243
- }
244
-
245
- /**
246
- * A color area allows users to select a color by dragging in a 2D gradient.
247
- *
248
- * @example
249
- * ```tsx
250
- * const [color, setColor] = createSignal(parseColor('hsl(0, 100%, 50%)'))
251
- *
252
- * <ColorArea
253
- * value={color()}
254
- * onChange={setColor}
255
- * xChannel="saturation"
256
- * yChannel="lightness"
257
- * />
258
- * ```
259
- */
260
- export function ColorArea(props: ColorAreaProps): JSX.Element {
261
- const [local, headlessProps] = splitProps(props, ['size', 'class'])
262
-
263
- const size = () => local.size ?? 'md'
264
- const styles = () => sizeStyles[size()]
265
- const customClass = local.class ?? ''
266
-
267
- const getClassName = (renderProps: ColorAreaRenderProps): string => {
268
- const base = `relative ${styles().area.container} rounded-lg overflow-hidden border border-bg-300 shadow-inner`
269
- let stateClass = ''
270
- if (renderProps.isDisabled) {
271
- stateClass = 'opacity-50 cursor-not-allowed'
272
- }
273
- return [base, stateClass, customClass].filter(Boolean).join(' ')
274
- }
275
-
276
- const contextValue = () => ({ size: size() })
277
-
278
- return (
279
- <ColorSizeContext.Provider value={contextValue()}>
280
- <HeadlessColorArea {...headlessProps} class={getClassName}>
281
- {() => (
282
- <>
283
- <ColorAreaGradient />
284
- <ColorAreaThumb />
285
- </>
286
- )}
287
- </HeadlessColorArea>
288
- </ColorSizeContext.Provider>
289
- )
290
- }
291
-
292
- /**
293
- * The gradient background for a color area.
294
- */
295
- export function ColorAreaGradient(props: { class?: string }): JSX.Element {
296
- const customClass = props.class ?? ''
297
- const className = `absolute inset-0 ${customClass}`
298
-
299
- return <HeadlessColorAreaGradient class={className} />
300
- }
301
-
302
- /**
303
- * The thumb component for a color area.
304
- */
305
- export function ColorAreaThumb(props: { class?: string }): JSX.Element {
306
- const context = useContext(ColorSizeContext)
307
- const styles = sizeStyles[context.size]
308
- const customClass = props.class ?? ''
309
-
310
- const getClassName = (renderProps: ColorAreaThumbRenderProps): string => {
311
- const base = `${styles.area.thumb} rounded-full border-2 border-white shadow-md cursor-grab`
312
- const dragClass = renderProps.isDragging ? 'cursor-grabbing scale-110' : ''
313
- const focusClass = renderProps.isFocusVisible ? 'ring-2 ring-accent-300 ring-offset-2' : ''
314
- const disabledClass = renderProps.isDisabled ? 'cursor-not-allowed' : ''
315
- return [base, dragClass, focusClass, disabledClass, customClass].filter(Boolean).join(' ')
316
- }
317
-
318
- return <HeadlessColorAreaThumb class={getClassName} />
319
- }
320
-
321
- // ============================================
322
- // COLOR WHEEL
323
- // ============================================
324
-
325
- export interface ColorWheelProps extends Omit<HeadlessColorWheelProps, 'class' | 'style' | 'children'> {
326
- /** The size of the color wheel. */
327
- size?: ColorSize
328
- /** Additional CSS class name. */
329
- class?: string
330
- }
331
-
332
- /**
333
- * A color wheel allows users to select a hue by dragging around a circular track.
334
- *
335
- * @example
336
- * ```tsx
337
- * const [color, setColor] = createSignal(parseColor('hsl(0, 100%, 50%)'))
338
- *
339
- * <ColorWheel
340
- * value={color()}
341
- * onChange={setColor}
342
- * />
343
- * ```
344
- */
345
- export function ColorWheel(props: ColorWheelProps): JSX.Element {
346
- const [local, headlessProps] = splitProps(props, ['size', 'class'])
347
-
348
- const size = () => local.size ?? 'md'
349
- const styles = () => sizeStyles[size()]
350
- const customClass = local.class ?? ''
351
-
352
- const getClassName = (renderProps: ColorWheelRenderProps): string => {
353
- const base = `relative ${styles().wheel.container}`
354
- let stateClass = ''
355
- if (renderProps.isDisabled) {
356
- stateClass = 'opacity-50 cursor-not-allowed'
357
- }
358
- return [base, stateClass, customClass].filter(Boolean).join(' ')
359
- }
360
-
361
- const contextValue = () => ({ size: size() })
362
-
363
- return (
364
- <ColorSizeContext.Provider value={contextValue()}>
365
- <HeadlessColorWheel {...headlessProps} class={getClassName}>
366
- {() => (
367
- <>
368
- <ColorWheelTrack />
369
- <ColorWheelThumb />
370
- </>
371
- )}
372
- </HeadlessColorWheel>
373
- </ColorSizeContext.Provider>
374
- )
375
- }
376
-
377
- /**
378
- * The circular track for a color wheel.
379
- */
380
- export function ColorWheelTrack(props: { class?: string }): JSX.Element {
381
- const context = useContext(ColorSizeContext)
382
- const styles = sizeStyles[context.size]
383
- const customClass = props.class ?? ''
384
-
385
- const className = `${styles.wheel.track} ${customClass}`
386
-
387
- return <HeadlessColorWheelTrack class={className} />
388
- }
389
-
390
- /**
391
- * The thumb component for a color wheel.
392
- */
393
- export function ColorWheelThumb(props: { class?: string }): JSX.Element {
394
- const context = useContext(ColorSizeContext)
395
- const styles = sizeStyles[context.size]
396
- const customClass = props.class ?? ''
397
-
398
- const getClassName = (renderProps: ColorWheelThumbRenderProps): string => {
399
- const base = `${styles.wheel.thumb} rounded-full border-2 border-white shadow-md cursor-grab`
400
- const dragClass = renderProps.isDragging ? 'cursor-grabbing scale-110' : ''
401
- const focusClass = renderProps.isFocusVisible ? 'ring-2 ring-accent-300 ring-offset-2' : ''
402
- const disabledClass = renderProps.isDisabled ? 'cursor-not-allowed' : ''
403
- return [base, dragClass, focusClass, disabledClass, customClass].filter(Boolean).join(' ')
404
- }
405
-
406
- return <HeadlessColorWheelThumb class={getClassName} />
407
- }
408
-
409
- // ============================================
410
- // COLOR FIELD
411
- // ============================================
412
-
413
- export interface ColorFieldProps extends Omit<HeadlessColorFieldProps, 'class' | 'style' | 'children'> {
414
- /** The size of the color field. */
415
- size?: ColorSize
416
- /** Additional CSS class name. */
417
- class?: string
418
- /** Description text below the input. */
419
- description?: string
420
- /** Error message to display. */
421
- errorMessage?: string
422
- }
423
-
424
- /**
425
- * A color field allows users to enter a color value as text.
426
- *
427
- * @example
428
- * ```tsx
429
- * const [color, setColor] = createSignal(parseColor('#ff0000'))
430
- *
431
- * <ColorField
432
- * value={color()}
433
- * onChange={setColor}
434
- * label="Color"
435
- * />
436
- * ```
437
- */
438
- export function ColorField(props: ColorFieldProps): JSX.Element {
439
- const [local, headlessProps] = splitProps(props, [
440
- 'size',
441
- 'class',
442
- 'description',
443
- 'errorMessage',
444
- ])
445
-
446
- const size = () => local.size ?? 'md'
447
- const styles = () => sizeStyles[size()]
448
- const customClass = local.class ?? ''
449
-
450
- const getClassName = (renderProps: ColorFieldRenderProps): string => {
451
- const base = 'flex flex-col gap-1.5'
452
- let stateClass = ''
453
- if (renderProps.isDisabled) {
454
- stateClass = 'opacity-50'
455
- }
456
- return [base, stateClass, customClass].filter(Boolean).join(' ')
457
- }
458
-
459
- const contextValue = () => ({ size: size() })
460
-
461
- return (
462
- <ColorSizeContext.Provider value={contextValue()}>
463
- <HeadlessColorField {...headlessProps} class={getClassName}>
464
- {() => (
465
- <>
466
- <Show when={headlessProps.label}>
467
- <span class={`text-primary-200 font-medium ${styles().field.label}`}>
468
- {headlessProps.label}
469
- </span>
470
- </Show>
471
- <ColorFieldInput isInvalid={!!local.errorMessage} />
472
- <Show when={local.description && !local.errorMessage}>
473
- <span class="text-primary-400 text-sm">{local.description}</span>
474
- </Show>
475
- <Show when={local.errorMessage}>
476
- <span class="text-danger-400 text-sm">{local.errorMessage}</span>
477
- </Show>
478
- </>
479
- )}
480
- </HeadlessColorField>
481
- </ColorSizeContext.Provider>
482
- )
483
- }
484
-
485
- /**
486
- * The input component for a color field.
487
- */
488
- export function ColorFieldInput(props: { class?: string; isInvalid?: boolean }): JSX.Element {
489
- const context = useContext(ColorSizeContext)
490
- const styles = sizeStyles[context.size]
491
- const customClass = props.class ?? ''
492
-
493
- const base = `${styles.field.input} w-full rounded-md border bg-bg-400 text-primary-200 placeholder:text-primary-500 focus:outline-none focus:ring-2 focus:ring-accent-300`
494
- const borderClass = props.isInvalid
495
- ? 'border-danger-400'
496
- : 'border-bg-300 focus:border-accent-300'
497
- const className = [base, borderClass, customClass].filter(Boolean).join(' ')
498
-
499
- return <HeadlessColorFieldInput class={className} />
500
- }
501
-
502
- // ============================================
503
- // COLOR SWATCH
504
- // ============================================
505
-
506
- export interface ColorSwatchProps extends Omit<HeadlessColorSwatchProps, 'class' | 'style'> {
507
- /** The size of the color swatch. */
508
- size?: ColorSize
509
- /** Additional CSS class name. */
510
- class?: string
511
- /** Whether the swatch is selectable. */
512
- isSelectable?: boolean
513
- /** Whether the swatch is selected. */
514
- isSelected?: boolean
515
- /** Handler called when the swatch is clicked. */
516
- onClick?: () => void
517
- }
518
-
519
- /**
520
- * A color swatch displays a color sample.
521
- *
522
- * @example
523
- * ```tsx
524
- * <ColorSwatch color={parseColor('#ff0000')} />
525
- *
526
- * // Selectable swatch
527
- * <ColorSwatch
528
- * color={parseColor('#00ff00')}
529
- * isSelectable
530
- * isSelected={selectedColor === '#00ff00'}
531
- * onClick={() => setSelectedColor('#00ff00')}
532
- * />
533
- * ```
534
- */
535
- export function ColorSwatch(props: ColorSwatchProps): JSX.Element {
536
- const [local, headlessProps] = splitProps(props, [
537
- 'size',
538
- 'class',
539
- 'isSelectable',
540
- 'isSelected',
541
- 'onClick',
542
- ])
543
-
544
- const size = () => local.size ?? 'md'
545
- const styles = () => sizeStyles[size()]
546
- const customClass = local.class ?? ''
547
-
548
- const getClassName = (_renderProps: ColorSwatchRenderProps): string => {
549
- const base = `${styles().swatch} rounded-md border border-bg-300 shadow-sm`
550
- const selectableClass = local.isSelectable
551
- ? 'cursor-pointer hover:scale-105 transition-transform'
552
- : ''
553
- const selectedClass = local.isSelected
554
- ? 'ring-2 ring-accent-300 ring-offset-2 ring-offset-bg-400'
555
- : ''
556
- return [base, selectableClass, selectedClass, customClass].filter(Boolean).join(' ')
557
- }
558
-
559
- const handleClick = () => {
560
- if (local.isSelectable && local.onClick) {
561
- local.onClick()
562
- }
563
- }
564
-
565
- return (
566
- <div onClick={handleClick}>
567
- <HeadlessColorSwatch {...headlessProps} class={getClassName} />
568
- </div>
569
- )
570
- }
571
-
572
- // ============================================
573
- // COLOR PICKER (Composite Component)
574
- // ============================================
575
-
576
- export interface ColorPickerProps {
577
- /** The current color value (controlled). */
578
- value?: Color | string
579
- /** The default color value (uncontrolled). */
580
- defaultValue?: Color | string
581
- /** Handler called when the color changes. */
582
- onChange?: (color: Color) => void
583
- /** The size of the picker. */
584
- size?: ColorSize
585
- /** Additional CSS class name. */
586
- class?: string
587
- /** Whether the picker is disabled. */
588
- isDisabled?: boolean
589
- /** A label for the picker. */
590
- label?: string
591
- /** Whether to show the hex input field. */
592
- showInput?: boolean
593
- /** Whether to show channel sliders. */
594
- showSliders?: boolean
595
- }
596
-
597
- /**
598
- * A complete color picker component with area, sliders, and input.
599
- *
600
- * @example
601
- * ```tsx
602
- * const [color, setColor] = createSignal(parseColor('hsl(0, 100%, 50%)'))
603
- *
604
- * <ColorPicker
605
- * value={color()}
606
- * onChange={setColor}
607
- * label="Pick a color"
608
- * showInput
609
- * showSliders
610
- * />
611
- * ```
612
- */
613
- export function ColorPicker(props: ColorPickerProps): JSX.Element {
614
- const size = () => props.size ?? 'md'
615
- const styles = () => sizeStyles[size()]
616
-
617
- return (
618
- <div class={`flex flex-col gap-4 ${props.class ?? ''}`}>
619
- <Show when={props.label}>
620
- <span class={`text-primary-200 font-medium ${styles().field.label}`}>
621
- {props.label}
622
- </span>
623
- </Show>
624
-
625
- <ColorArea
626
- value={props.value}
627
- defaultValue={props.defaultValue}
628
- onChange={props.onChange}
629
- xChannel="saturation"
630
- yChannel="lightness"
631
- size={size()}
632
- isDisabled={props.isDisabled}
633
- />
634
-
635
- <Show when={props.showSliders !== false}>
636
- <ColorSlider
637
- value={props.value}
638
- defaultValue={props.defaultValue}
639
- onChange={props.onChange}
640
- channel="hue"
641
- label="Hue"
642
- size={size()}
643
- showValue
644
- isDisabled={props.isDisabled}
645
- />
646
-
647
- <ColorSlider
648
- value={props.value}
649
- defaultValue={props.defaultValue}
650
- onChange={props.onChange}
651
- channel="alpha"
652
- label="Alpha"
653
- size={size()}
654
- showValue
655
- isDisabled={props.isDisabled}
656
- />
657
- </Show>
658
-
659
- <Show when={props.showInput}>
660
- <ColorField
661
- value={props.value}
662
- defaultValue={props.defaultValue}
663
- onChange={(color) => {
664
- if (color && props.onChange) {
665
- props.onChange(color)
666
- }
667
- }}
668
- label="Hex"
669
- size={size()}
670
- isDisabled={props.isDisabled}
671
- />
672
- </Show>
673
- </div>
674
- )
675
- }
676
-
677
- // Attach sub-components for convenience
678
- ColorSlider.Track = ColorSliderTrack
679
- ColorSlider.Thumb = ColorSliderThumb
680
- ColorArea.Gradient = ColorAreaGradient
681
- ColorArea.Thumb = ColorAreaThumb
682
- ColorWheel.Track = ColorWheelTrack
683
- ColorWheel.Thumb = ColorWheelThumb
684
- ColorField.Input = ColorFieldInput
685
-
686
- // Re-export types for convenience
687
- export type { Color, ColorChannel, ColorFormat }