@proyecto-viviana/ui 0.2.5 → 0.3.2

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 (73) hide show
  1. package/dist/index.js +210 -557
  2. package/dist/index.js.map +7 -1
  3. package/dist/index.jsx +6658 -0
  4. package/dist/index.jsx.map +7 -0
  5. package/dist/index.ssr.js +42 -399
  6. package/dist/index.ssr.js.map +7 -1
  7. package/dist/radio/index.d.ts +27 -12
  8. package/dist/radio/index.d.ts.map +1 -1
  9. package/package.json +11 -12
  10. package/src/alert/index.tsx +0 -48
  11. package/src/assets/favicon.png +0 -0
  12. package/src/assets/fire.gif +0 -0
  13. package/src/autocomplete/index.tsx +0 -313
  14. package/src/avatar/index.tsx +0 -75
  15. package/src/badge/index.tsx +0 -43
  16. package/src/breadcrumbs/index.tsx +0 -207
  17. package/src/button/Button.tsx +0 -74
  18. package/src/button/index.ts +0 -2
  19. package/src/button/types.ts +0 -24
  20. package/src/calendar/DateField.tsx +0 -200
  21. package/src/calendar/DatePicker.tsx +0 -298
  22. package/src/calendar/RangeCalendar.tsx +0 -236
  23. package/src/calendar/TimeField.tsx +0 -196
  24. package/src/calendar/index.tsx +0 -223
  25. package/src/checkbox/index.tsx +0 -257
  26. package/src/color/index.tsx +0 -687
  27. package/src/combobox/index.tsx +0 -383
  28. package/src/components.css +0 -1077
  29. package/src/custom/calendar-card/index.tsx +0 -66
  30. package/src/custom/chip/index.tsx +0 -46
  31. package/src/custom/conversation/index.tsx +0 -105
  32. package/src/custom/event-card/index.tsx +0 -132
  33. package/src/custom/header/index.tsx +0 -33
  34. package/src/custom/lateral-nav/index.tsx +0 -88
  35. package/src/custom/logo/index.tsx +0 -58
  36. package/src/custom/nav-header/index.tsx +0 -42
  37. package/src/custom/page-layout/index.tsx +0 -29
  38. package/src/custom/profile-card/index.tsx +0 -64
  39. package/src/custom/project-card/index.tsx +0 -59
  40. package/src/custom/timeline-item/index.tsx +0 -105
  41. package/src/dialog/Dialog.tsx +0 -260
  42. package/src/dialog/index.tsx +0 -3
  43. package/src/disclosure/index.tsx +0 -307
  44. package/src/gridlist/index.tsx +0 -403
  45. package/src/icon/icons/GitHubIcon.tsx +0 -20
  46. package/src/icon/index.tsx +0 -48
  47. package/src/index.ts +0 -322
  48. package/src/landmark/index.tsx +0 -231
  49. package/src/link/index.tsx +0 -130
  50. package/src/listbox/index.tsx +0 -231
  51. package/src/menu/index.tsx +0 -297
  52. package/src/meter/index.tsx +0 -163
  53. package/src/numberfield/index.tsx +0 -482
  54. package/src/popover/index.tsx +0 -260
  55. package/src/progress-bar/index.tsx +0 -169
  56. package/src/radio/index.tsx +0 -173
  57. package/src/searchfield/index.tsx +0 -453
  58. package/src/select/index.tsx +0 -349
  59. package/src/separator/index.tsx +0 -141
  60. package/src/slider/index.tsx +0 -382
  61. package/src/styles.css +0 -450
  62. package/src/switch/ToggleSwitch.tsx +0 -112
  63. package/src/switch/index.tsx +0 -90
  64. package/src/table/index.tsx +0 -531
  65. package/src/tabs/index.tsx +0 -273
  66. package/src/tag-group/index.tsx +0 -240
  67. package/src/test-utils/index.ts +0 -32
  68. package/src/textfield/index.tsx +0 -211
  69. package/src/theme.css +0 -101
  70. package/src/toast/index.tsx +0 -324
  71. package/src/toolbar/index.tsx +0 -108
  72. package/src/tooltip/index.tsx +0 -197
  73. package/src/tree/index.tsx +0 -494
@@ -1,453 +0,0 @@
1
- /**
2
- * SearchField component for proyecto-viviana-ui
3
- *
4
- * A styled search field component with clear button and search icon.
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
- createSearchField,
11
- createFocusRing,
12
- createPress,
13
- createHover,
14
- type AriaSearchFieldProps,
15
- } from '@proyecto-viviana/solidaria'
16
- import {
17
- createSearchFieldState,
18
- } from '@proyecto-viviana/solid-stately'
19
-
20
- // ============================================
21
- // TYPES
22
- // ============================================
23
-
24
- export type SearchFieldSize = 'sm' | 'md' | 'lg'
25
- export type SearchFieldVariant = 'outline' | 'filled'
26
-
27
- export interface SearchFieldProps extends Omit<AriaSearchFieldProps, 'label'> {
28
- /** The size of the search field. */
29
- size?: SearchFieldSize
30
- /** The visual variant of the search field. */
31
- variant?: SearchFieldVariant
32
- /** Additional CSS class name. */
33
- class?: string
34
- /** Label text for the input. */
35
- label?: string
36
- /** Description text shown below the input. */
37
- description?: string
38
- /** Error message shown when invalid. */
39
- errorMessage?: string
40
- /** The current value (controlled). */
41
- value?: string
42
- /** The default value (uncontrolled). */
43
- defaultValue?: string
44
- /** Handler called when the value changes. */
45
- onChange?: (value: string) => void
46
- /** Handler called when the user submits the search. */
47
- onSubmit?: (value: string) => void
48
- /** Handler called when the field is cleared. */
49
- onClear?: () => void
50
- /** Whether to hide the search icon. */
51
- hideSearchIcon?: boolean
52
- }
53
-
54
- // ============================================
55
- // STYLES
56
- // ============================================
57
-
58
- const sizeStyles = {
59
- sm: {
60
- container: 'h-8',
61
- input: 'text-sm pl-8 pr-8',
62
- label: 'text-sm',
63
- description: 'text-xs',
64
- icon: 'w-4 h-4 left-2',
65
- clearButton: 'w-5 h-5 right-1.5',
66
- },
67
- md: {
68
- container: 'h-10',
69
- input: 'text-base pl-10 pr-10',
70
- label: 'text-sm',
71
- description: 'text-sm',
72
- icon: 'w-5 h-5 left-2.5',
73
- clearButton: 'w-6 h-6 right-2',
74
- },
75
- lg: {
76
- container: 'h-12',
77
- input: 'text-lg pl-12 pr-12',
78
- label: 'text-base',
79
- description: 'text-sm',
80
- icon: 'w-6 h-6 left-3',
81
- clearButton: 'w-7 h-7 right-2.5',
82
- },
83
- }
84
-
85
- // ============================================
86
- // ICONS
87
- // ============================================
88
-
89
- function SearchIcon(props: { class?: string }) {
90
- return (
91
- <svg
92
- class={props.class}
93
- viewBox="0 0 20 20"
94
- fill="none"
95
- stroke="currentColor"
96
- stroke-width="2"
97
- >
98
- <circle cx="8" cy="8" r="5" />
99
- <path d="M12 12L17 17" stroke-linecap="round" />
100
- </svg>
101
- )
102
- }
103
-
104
- function ClearIcon(props: { class?: string }) {
105
- return (
106
- <svg
107
- class={props.class}
108
- viewBox="0 0 16 16"
109
- fill="none"
110
- stroke="currentColor"
111
- stroke-width="2"
112
- >
113
- <path d="M4 4L12 12M12 4L4 12" stroke-linecap="round" />
114
- </svg>
115
- )
116
- }
117
-
118
- // ============================================
119
- // COMPONENT
120
- // ============================================
121
-
122
- /**
123
- * A search field allows users to enter and clear a search query.
124
- *
125
- * Built directly on solidaria hooks for full accessibility support.
126
- */
127
- export function SearchField(props: SearchFieldProps): JSX.Element {
128
- const defaultProps: Partial<SearchFieldProps> = {
129
- size: 'md',
130
- variant: 'outline',
131
- }
132
-
133
- const merged = solidMergeProps(defaultProps, props)
134
-
135
- const [local, stateProps, ariaProps] = splitProps(merged, [
136
- 'size',
137
- 'variant',
138
- 'class',
139
- 'label',
140
- 'description',
141
- 'errorMessage',
142
- 'hideSearchIcon',
143
- ], [
144
- 'value',
145
- 'defaultValue',
146
- 'onChange',
147
- 'onSubmit',
148
- 'onClear',
149
- ])
150
-
151
- const size = () => sizeStyles[local.size!]
152
-
153
- // Ref for input element
154
- let inputRef: HTMLInputElement | undefined
155
-
156
- // Create search field state
157
- const state = createSearchFieldState({
158
- get value() {
159
- return stateProps.value
160
- },
161
- get defaultValue() {
162
- return stateProps.defaultValue
163
- },
164
- get onChange() {
165
- return stateProps.onChange
166
- },
167
- })
168
-
169
- // Create search field aria props
170
- const searchFieldAria = createSearchField(
171
- {
172
- get label() {
173
- return local.label
174
- },
175
- get 'aria-label'() {
176
- return ariaProps['aria-label']
177
- },
178
- get 'aria-labelledby'() {
179
- return ariaProps['aria-labelledby']
180
- },
181
- get 'aria-describedby'() {
182
- return ariaProps['aria-describedby']
183
- },
184
- get isDisabled() {
185
- return ariaProps.isDisabled
186
- },
187
- get isReadOnly() {
188
- return ariaProps.isReadOnly
189
- },
190
- get isRequired() {
191
- return ariaProps.isRequired
192
- },
193
- get isInvalid() {
194
- return ariaProps.isInvalid
195
- },
196
- get description() {
197
- return local.description
198
- },
199
- get errorMessage() {
200
- return local.errorMessage
201
- },
202
- get placeholder() {
203
- return ariaProps.placeholder
204
- },
205
- get name() {
206
- return ariaProps.name
207
- },
208
- get autoFocus() {
209
- return ariaProps.autoFocus
210
- },
211
- get autoComplete() {
212
- return ariaProps.autoComplete
213
- },
214
- get maxLength() {
215
- return ariaProps.maxLength
216
- },
217
- get minLength() {
218
- return ariaProps.minLength
219
- },
220
- get pattern() {
221
- return ariaProps.pattern
222
- },
223
- get onSubmit() {
224
- return stateProps.onSubmit
225
- },
226
- get onClear() {
227
- return stateProps.onClear
228
- },
229
- },
230
- state,
231
- () => inputRef ?? null
232
- )
233
-
234
- // Create focus ring for input
235
- const { isFocused, isFocusVisible, focusProps } = createFocusRing()
236
-
237
- // Create hover for input
238
- const { isHovered, hoverProps } = createHover({
239
- get isDisabled() {
240
- return ariaProps.isDisabled
241
- },
242
- })
243
-
244
- // Clear button interactions
245
- const { isPressed: clearPressed, pressProps: clearPressProps } = createPress({
246
- get isDisabled() {
247
- return ariaProps.isDisabled || ariaProps.isReadOnly
248
- },
249
- onPress: () => {
250
- searchFieldAria.clearButtonProps.onClick()
251
- },
252
- })
253
-
254
- const { isHovered: clearHovered, hoverProps: clearHoverProps } = createHover({
255
- get isDisabled() {
256
- return ariaProps.isDisabled || ariaProps.isReadOnly
257
- },
258
- })
259
-
260
- // Compute classes
261
- const containerClasses = () => {
262
- const base = 'flex flex-col'
263
- const disabledClass = ariaProps.isDisabled ? 'opacity-60' : ''
264
- const custom = local.class || ''
265
- return [base, disabledClass, custom].filter(Boolean).join(' ')
266
- }
267
-
268
- const inputWrapperClasses = () => {
269
- const base = 'relative flex items-center'
270
- const sizeClass = size().container
271
- return [base, sizeClass].filter(Boolean).join(' ')
272
- }
273
-
274
- const inputClasses = () => {
275
- const base = 'w-full h-full rounded-md transition-all duration-200 outline-none'
276
- const sizeClass = size().input
277
-
278
- // Adjust padding based on search icon visibility
279
- const paddingClass = local.hideSearchIcon ? 'pl-3' : ''
280
-
281
- let variantClass: string
282
- if (local.variant === 'filled') {
283
- variantClass = 'bg-bg-200 border border-transparent'
284
- } else {
285
- variantClass = 'bg-transparent border border-bg-400'
286
- }
287
-
288
- let stateClass: string
289
- if (ariaProps.isDisabled) {
290
- stateClass = 'bg-bg-200 text-primary-500 cursor-not-allowed'
291
- } else if (ariaProps.isInvalid) {
292
- stateClass = 'border-danger-500 focus:border-danger-400 focus:ring-2 focus:ring-danger-400/20'
293
- } else {
294
- stateClass = 'text-primary-100 placeholder:text-primary-500 focus:border-accent focus:ring-2 focus:ring-accent/20'
295
- }
296
-
297
- const hoverClass = ariaProps.isDisabled ? '' : 'hover:border-accent-300'
298
-
299
- return [base, sizeClass, paddingClass, variantClass, stateClass, hoverClass].filter(Boolean).join(' ')
300
- }
301
-
302
- const searchIconClasses = () => {
303
- const base = 'absolute pointer-events-none text-primary-400'
304
- const sizeClass = size().icon
305
- const focusedClass = isFocused() ? 'text-accent' : ''
306
- return [base, sizeClass, focusedClass].filter(Boolean).join(' ')
307
- }
308
-
309
- const clearButtonClasses = () => {
310
- const base = 'absolute flex items-center justify-center rounded-md transition-all duration-150 select-none'
311
- const sizeClass = size().clearButton
312
-
313
- const isDisabled = ariaProps.isDisabled || ariaProps.isReadOnly
314
-
315
- let stateClass: string
316
- if (isDisabled) {
317
- stateClass = 'text-primary-600 cursor-not-allowed'
318
- } else if (clearPressed()) {
319
- stateClass = 'bg-bg-400 text-primary-100 scale-90'
320
- } else if (clearHovered()) {
321
- stateClass = 'bg-bg-300 text-primary-100'
322
- } else {
323
- stateClass = 'text-primary-400 hover:bg-bg-300 hover:text-primary-100'
324
- }
325
-
326
- return [base, sizeClass, stateClass].filter(Boolean).join(' ')
327
- }
328
-
329
- const labelClasses = () => {
330
- const base = 'block font-medium text-primary-200 mb-1'
331
- const sizeClass = size().label
332
- return [base, sizeClass].filter(Boolean).join(' ')
333
- }
334
-
335
- const descriptionClasses = () => {
336
- const base = 'mt-1 text-primary-400'
337
- const sizeClass = size().description
338
- return [base, sizeClass].filter(Boolean).join(' ')
339
- }
340
-
341
- const errorClasses = () => {
342
- const base = 'mt-1 text-danger-500'
343
- const sizeClass = size().description
344
- return [base, sizeClass].filter(Boolean).join(' ')
345
- }
346
-
347
- // Clean props helpers
348
- const cleanInputProps = () => {
349
- const { ref: _ref, ...rest } = searchFieldAria.inputProps as Record<string, unknown>
350
- return rest
351
- }
352
-
353
- const cleanFocusProps = () => {
354
- const { ref: _ref, ...rest } = focusProps as Record<string, unknown>
355
- return rest
356
- }
357
-
358
- const cleanHoverProps = () => {
359
- const { ref: _ref, ...rest } = hoverProps as Record<string, unknown>
360
- return rest
361
- }
362
-
363
- const cleanLabelProps = () => {
364
- const { ref: _ref, ...rest } = searchFieldAria.labelProps as Record<string, unknown>
365
- return rest
366
- }
367
-
368
- const cleanClearPressProps = () => {
369
- const { ref: _ref, ...rest } = clearPressProps as Record<string, unknown>
370
- return rest
371
- }
372
-
373
- const cleanClearHoverProps = () => {
374
- const { ref: _ref, ...rest } = clearHoverProps as Record<string, unknown>
375
- return rest
376
- }
377
-
378
- const isEmpty = () => state.value() === ''
379
-
380
- return (
381
- <div
382
- class={containerClasses()}
383
- data-empty={isEmpty() || undefined}
384
- data-disabled={ariaProps.isDisabled || undefined}
385
- data-invalid={ariaProps.isInvalid || undefined}
386
- >
387
- {/* Label */}
388
- <Show when={local.label}>
389
- <span {...cleanLabelProps()} class={labelClasses()}>
390
- {local.label}
391
- <Show when={ariaProps.isRequired}>
392
- <span class="text-danger-500 ml-1">*</span>
393
- </Show>
394
- </span>
395
- </Show>
396
-
397
- {/* Input Wrapper */}
398
- <div class={inputWrapperClasses()}>
399
- {/* Search Icon */}
400
- <Show when={!local.hideSearchIcon}>
401
- <SearchIcon class={searchIconClasses()} />
402
- </Show>
403
-
404
- {/* Input */}
405
- <input
406
- ref={inputRef}
407
- {...cleanInputProps()}
408
- {...cleanFocusProps()}
409
- {...cleanHoverProps()}
410
- class={inputClasses()}
411
- data-focused={isFocused() || undefined}
412
- data-focus-visible={isFocusVisible() || undefined}
413
- data-hovered={isHovered() || undefined}
414
- />
415
-
416
- {/* Clear Button */}
417
- <Show when={!isEmpty()}>
418
- <button
419
- type="button"
420
- aria-label={searchFieldAria.clearButtonProps['aria-label']}
421
- tabIndex={searchFieldAria.clearButtonProps.tabIndex}
422
- disabled={searchFieldAria.clearButtonProps.disabled}
423
- onMouseDown={searchFieldAria.clearButtonProps.onMouseDown}
424
- {...cleanClearPressProps()}
425
- {...cleanClearHoverProps()}
426
- class={clearButtonClasses()}
427
- data-pressed={clearPressed() || undefined}
428
- data-hovered={clearHovered() || undefined}
429
- >
430
- <ClearIcon class="w-3 h-3" />
431
- </button>
432
- </Show>
433
- </div>
434
-
435
- {/* Description */}
436
- <Show when={local.description && !ariaProps.isInvalid}>
437
- <span {...searchFieldAria.descriptionProps} class={descriptionClasses()}>
438
- {local.description}
439
- </span>
440
- </Show>
441
-
442
- {/* Error Message */}
443
- <Show when={ariaProps.isInvalid && local.errorMessage}>
444
- <span {...searchFieldAria.errorMessageProps} class={errorClasses()}>
445
- {local.errorMessage}
446
- </span>
447
- </Show>
448
- </div>
449
- )
450
- }
451
-
452
- // Re-export types
453
- export type { SearchFieldState } from '@proyecto-viviana/solid-stately'