@proyecto-viviana/ui 0.1.7 → 0.2.0
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/README.md +192 -0
- package/dist/autocomplete/index.d.ts +89 -0
- package/dist/autocomplete/index.d.ts.map +1 -0
- package/dist/breadcrumbs/index.d.ts +38 -0
- package/dist/breadcrumbs/index.d.ts.map +1 -0
- package/dist/button/Button.d.ts.map +1 -1
- package/dist/calendar/DateField.d.ts +47 -0
- package/dist/calendar/DateField.d.ts.map +1 -0
- package/dist/calendar/DatePicker.d.ts +48 -0
- package/dist/calendar/DatePicker.d.ts.map +1 -0
- package/dist/calendar/RangeCalendar.d.ts +42 -0
- package/dist/calendar/RangeCalendar.d.ts.map +1 -0
- package/dist/calendar/TimeField.d.ts +44 -0
- package/dist/calendar/TimeField.d.ts.map +1 -0
- package/dist/calendar/index.d.ts +50 -0
- package/dist/calendar/index.d.ts.map +1 -0
- package/dist/checkbox/index.d.ts.map +1 -1
- package/dist/color/index.d.ts +228 -0
- package/dist/color/index.d.ts.map +1 -0
- package/dist/combobox/index.d.ts +81 -0
- package/dist/combobox/index.d.ts.map +1 -0
- package/dist/components.css +116 -14
- package/dist/custom/chip/index.d.ts +7 -2
- package/dist/custom/chip/index.d.ts.map +1 -1
- package/dist/custom/event-card/index.d.ts +5 -1
- package/dist/custom/event-card/index.d.ts.map +1 -1
- package/dist/custom/header/index.d.ts +16 -0
- package/dist/custom/header/index.d.ts.map +1 -0
- package/dist/custom/logo/index.d.ts +2 -0
- package/dist/custom/logo/index.d.ts.map +1 -1
- package/dist/custom/page-layout/index.d.ts +2 -0
- package/dist/custom/page-layout/index.d.ts.map +1 -1
- package/dist/custom/profile-card/index.d.ts +5 -1
- package/dist/custom/profile-card/index.d.ts.map +1 -1
- package/dist/custom/timeline-item/index.d.ts +12 -2
- package/dist/custom/timeline-item/index.d.ts.map +1 -1
- package/dist/dialog/Dialog.d.ts +67 -0
- package/dist/dialog/Dialog.d.ts.map +1 -0
- package/dist/dialog/index.d.ts +2 -17
- package/dist/dialog/index.d.ts.map +1 -1
- package/dist/disclosure/index.d.ts +84 -0
- package/dist/disclosure/index.d.ts.map +1 -0
- package/dist/gridlist/index.d.ts +92 -0
- package/dist/gridlist/index.d.ts.map +1 -0
- package/dist/index.d.ts +58 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6984 -783
- package/dist/index.js.map +1 -1
- package/dist/index.ssr.js +5905 -571
- package/dist/index.ssr.js.map +1 -1
- package/dist/landmark/index.d.ts +83 -0
- package/dist/landmark/index.d.ts.map +1 -0
- package/dist/link/index.d.ts.map +1 -1
- package/dist/listbox/index.d.ts +47 -0
- package/dist/listbox/index.d.ts.map +1 -0
- package/dist/menu/index.d.ts +74 -0
- package/dist/menu/index.d.ts.map +1 -0
- package/dist/meter/index.d.ts +49 -0
- package/dist/meter/index.d.ts.map +1 -0
- package/dist/numberfield/index.d.ts +50 -0
- package/dist/numberfield/index.d.ts.map +1 -0
- package/dist/popover/index.d.ts +85 -0
- package/dist/popover/index.d.ts.map +1 -0
- package/dist/radio/index.d.ts +7 -4
- package/dist/radio/index.d.ts.map +1 -1
- package/dist/searchfield/index.d.ts +44 -0
- package/dist/searchfield/index.d.ts.map +1 -0
- package/dist/select/index.d.ts +72 -0
- package/dist/select/index.d.ts.map +1 -0
- package/dist/slider/index.d.ts +53 -0
- package/dist/slider/index.d.ts.map +1 -0
- package/dist/switch/ToggleSwitch.d.ts.map +1 -1
- package/dist/table/index.d.ts +140 -0
- package/dist/table/index.d.ts.map +1 -0
- package/dist/tabs/index.d.ts +56 -0
- package/dist/tabs/index.d.ts.map +1 -0
- package/dist/tag-group/index.d.ts +80 -0
- package/dist/tag-group/index.d.ts.map +1 -0
- package/dist/toast/index.d.ts +101 -0
- package/dist/toast/index.d.ts.map +1 -0
- package/dist/toolbar/index.d.ts +42 -0
- package/dist/toolbar/index.d.ts.map +1 -0
- package/dist/tooltip/index.d.ts +66 -5
- package/dist/tooltip/index.d.ts.map +1 -1
- package/dist/tree/index.d.ts +99 -0
- package/dist/tree/index.d.ts.map +1 -0
- package/package.json +66 -58
- package/src/autocomplete/index.tsx +313 -0
- package/src/breadcrumbs/index.tsx +207 -0
- package/src/button/Button.tsx +74 -75
- 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 +3 -4
- package/src/color/index.tsx +687 -0
- package/src/combobox/index.tsx +383 -0
- package/src/components.css +116 -14
- package/src/custom/chip/index.tsx +17 -3
- package/src/custom/event-card/index.tsx +8 -2
- package/src/custom/header/index.tsx +33 -0
- package/src/custom/logo/index.tsx +7 -3
- package/src/custom/page-layout/index.tsx +12 -3
- package/src/custom/profile-card/index.tsx +8 -2
- package/src/custom/timeline-item/index.tsx +28 -4
- package/src/dialog/Dialog.tsx +260 -0
- package/src/dialog/index.tsx +3 -69
- package/src/disclosure/index.tsx +307 -0
- package/src/gridlist/index.tsx +403 -0
- package/src/index.ts +219 -4
- package/src/landmark/index.tsx +231 -0
- package/src/link/index.tsx +1 -2
- 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/radio/index.tsx +36 -82
- package/src/searchfield/index.tsx +453 -0
- package/src/select/index.tsx +349 -0
- package/src/slider/index.tsx +382 -0
- package/src/switch/ToggleSwitch.tsx +1 -2
- package/src/table/index.tsx +531 -0
- package/src/tabs/index.tsx +273 -0
- package/src/tag-group/index.tsx +240 -0
- package/src/toast/index.tsx +324 -0
- package/src/toolbar/index.tsx +108 -0
- package/src/tooltip/index.tsx +171 -5
- package/src/tree/index.tsx +494 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NumberField component for proyecto-viviana-ui
|
|
3
|
+
*
|
|
4
|
+
* A styled number field component with increment/decrement buttons.
|
|
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
|
+
createNumberField,
|
|
11
|
+
createFocusRing,
|
|
12
|
+
createPress,
|
|
13
|
+
createHover,
|
|
14
|
+
type AriaNumberFieldProps,
|
|
15
|
+
} from '@proyecto-viviana/solidaria'
|
|
16
|
+
import {
|
|
17
|
+
createNumberFieldState,
|
|
18
|
+
} from '@proyecto-viviana/solid-stately'
|
|
19
|
+
|
|
20
|
+
// ============================================
|
|
21
|
+
// TYPES
|
|
22
|
+
// ============================================
|
|
23
|
+
|
|
24
|
+
export type NumberFieldSize = 'sm' | 'md' | 'lg'
|
|
25
|
+
export type NumberFieldVariant = 'outline' | 'filled'
|
|
26
|
+
|
|
27
|
+
export interface NumberFieldProps extends Omit<AriaNumberFieldProps, 'label'> {
|
|
28
|
+
/** The size of the number field. */
|
|
29
|
+
size?: NumberFieldSize
|
|
30
|
+
/** The visual variant of the number field. */
|
|
31
|
+
variant?: NumberFieldVariant
|
|
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?: number
|
|
42
|
+
/** The default value (uncontrolled). */
|
|
43
|
+
defaultValue?: number
|
|
44
|
+
/** Handler called when the value changes. */
|
|
45
|
+
onChange?: (value: number) => void
|
|
46
|
+
/** The minimum value. */
|
|
47
|
+
minValue?: number
|
|
48
|
+
/** The maximum value. */
|
|
49
|
+
maxValue?: number
|
|
50
|
+
/** The step value for increment/decrement. */
|
|
51
|
+
step?: number
|
|
52
|
+
/** The locale for number formatting. */
|
|
53
|
+
locale?: string
|
|
54
|
+
/** Number format options. */
|
|
55
|
+
formatOptions?: Intl.NumberFormatOptions
|
|
56
|
+
/** Whether to hide the stepper buttons. */
|
|
57
|
+
hideStepper?: boolean
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ============================================
|
|
61
|
+
// STYLES
|
|
62
|
+
// ============================================
|
|
63
|
+
|
|
64
|
+
const sizeStyles = {
|
|
65
|
+
sm: {
|
|
66
|
+
input: 'h-8 px-2 text-sm',
|
|
67
|
+
label: 'text-sm',
|
|
68
|
+
description: 'text-xs',
|
|
69
|
+
button: 'w-6 h-6 text-sm',
|
|
70
|
+
buttonGap: 'gap-0.5',
|
|
71
|
+
},
|
|
72
|
+
md: {
|
|
73
|
+
input: 'h-10 px-3 text-base',
|
|
74
|
+
label: 'text-sm',
|
|
75
|
+
description: 'text-sm',
|
|
76
|
+
button: 'w-8 h-8 text-base',
|
|
77
|
+
buttonGap: 'gap-1',
|
|
78
|
+
},
|
|
79
|
+
lg: {
|
|
80
|
+
input: 'h-12 px-4 text-lg',
|
|
81
|
+
label: 'text-base',
|
|
82
|
+
description: 'text-sm',
|
|
83
|
+
button: 'w-10 h-10 text-lg',
|
|
84
|
+
buttonGap: 'gap-1',
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ============================================
|
|
89
|
+
// ICONS
|
|
90
|
+
// ============================================
|
|
91
|
+
|
|
92
|
+
function PlusIcon(props: { class?: string }) {
|
|
93
|
+
return (
|
|
94
|
+
<svg
|
|
95
|
+
class={props.class}
|
|
96
|
+
viewBox="0 0 16 16"
|
|
97
|
+
fill="none"
|
|
98
|
+
stroke="currentColor"
|
|
99
|
+
stroke-width="2"
|
|
100
|
+
>
|
|
101
|
+
<path d="M8 3v10M3 8h10" />
|
|
102
|
+
</svg>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function MinusIcon(props: { class?: string }) {
|
|
107
|
+
return (
|
|
108
|
+
<svg
|
|
109
|
+
class={props.class}
|
|
110
|
+
viewBox="0 0 16 16"
|
|
111
|
+
fill="none"
|
|
112
|
+
stroke="currentColor"
|
|
113
|
+
stroke-width="2"
|
|
114
|
+
>
|
|
115
|
+
<path d="M3 8h10" />
|
|
116
|
+
</svg>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ============================================
|
|
121
|
+
// COMPONENT
|
|
122
|
+
// ============================================
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A number field allows users to enter a numeric value with increment/decrement controls.
|
|
126
|
+
*
|
|
127
|
+
* Built directly on solidaria hooks for full accessibility support.
|
|
128
|
+
*/
|
|
129
|
+
export function NumberField(props: NumberFieldProps): JSX.Element {
|
|
130
|
+
const defaultProps: Partial<NumberFieldProps> = {
|
|
131
|
+
size: 'md',
|
|
132
|
+
variant: 'outline',
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const merged = solidMergeProps(defaultProps, props)
|
|
136
|
+
|
|
137
|
+
const [local, stateProps, ariaProps] = splitProps(merged, [
|
|
138
|
+
'size',
|
|
139
|
+
'variant',
|
|
140
|
+
'class',
|
|
141
|
+
'label',
|
|
142
|
+
'description',
|
|
143
|
+
'errorMessage',
|
|
144
|
+
'hideStepper',
|
|
145
|
+
], [
|
|
146
|
+
'value',
|
|
147
|
+
'defaultValue',
|
|
148
|
+
'onChange',
|
|
149
|
+
'minValue',
|
|
150
|
+
'maxValue',
|
|
151
|
+
'step',
|
|
152
|
+
'locale',
|
|
153
|
+
'formatOptions',
|
|
154
|
+
])
|
|
155
|
+
|
|
156
|
+
const size = () => sizeStyles[local.size!]
|
|
157
|
+
|
|
158
|
+
// Ref for input element
|
|
159
|
+
let inputRef: HTMLInputElement | undefined
|
|
160
|
+
|
|
161
|
+
// Create number field state
|
|
162
|
+
const state = createNumberFieldState({
|
|
163
|
+
get value() {
|
|
164
|
+
return stateProps.value
|
|
165
|
+
},
|
|
166
|
+
get defaultValue() {
|
|
167
|
+
return stateProps.defaultValue
|
|
168
|
+
},
|
|
169
|
+
get onChange() {
|
|
170
|
+
return stateProps.onChange
|
|
171
|
+
},
|
|
172
|
+
get minValue() {
|
|
173
|
+
return stateProps.minValue
|
|
174
|
+
},
|
|
175
|
+
get maxValue() {
|
|
176
|
+
return stateProps.maxValue
|
|
177
|
+
},
|
|
178
|
+
get step() {
|
|
179
|
+
return stateProps.step
|
|
180
|
+
},
|
|
181
|
+
get locale() {
|
|
182
|
+
return stateProps.locale
|
|
183
|
+
},
|
|
184
|
+
get formatOptions() {
|
|
185
|
+
return stateProps.formatOptions
|
|
186
|
+
},
|
|
187
|
+
get isDisabled() {
|
|
188
|
+
return ariaProps.isDisabled
|
|
189
|
+
},
|
|
190
|
+
get isReadOnly() {
|
|
191
|
+
return ariaProps.isReadOnly
|
|
192
|
+
},
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
// Create number field aria props
|
|
196
|
+
const numberFieldAria = createNumberField(
|
|
197
|
+
{
|
|
198
|
+
get label() {
|
|
199
|
+
return local.label
|
|
200
|
+
},
|
|
201
|
+
get 'aria-label'() {
|
|
202
|
+
return ariaProps['aria-label']
|
|
203
|
+
},
|
|
204
|
+
get 'aria-labelledby'() {
|
|
205
|
+
return ariaProps['aria-labelledby']
|
|
206
|
+
},
|
|
207
|
+
get 'aria-describedby'() {
|
|
208
|
+
return ariaProps['aria-describedby']
|
|
209
|
+
},
|
|
210
|
+
get isDisabled() {
|
|
211
|
+
return ariaProps.isDisabled
|
|
212
|
+
},
|
|
213
|
+
get isReadOnly() {
|
|
214
|
+
return ariaProps.isReadOnly
|
|
215
|
+
},
|
|
216
|
+
get isRequired() {
|
|
217
|
+
return ariaProps.isRequired
|
|
218
|
+
},
|
|
219
|
+
get isInvalid() {
|
|
220
|
+
return ariaProps.isInvalid
|
|
221
|
+
},
|
|
222
|
+
get description() {
|
|
223
|
+
return local.description
|
|
224
|
+
},
|
|
225
|
+
get errorMessage() {
|
|
226
|
+
return local.errorMessage
|
|
227
|
+
},
|
|
228
|
+
get id() {
|
|
229
|
+
return ariaProps.id
|
|
230
|
+
},
|
|
231
|
+
get autoFocus() {
|
|
232
|
+
return ariaProps.autoFocus
|
|
233
|
+
},
|
|
234
|
+
get name() {
|
|
235
|
+
return ariaProps.name
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
state,
|
|
239
|
+
() => inputRef ?? null
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
// Create focus ring for input
|
|
243
|
+
const { isFocused, isFocusVisible, focusProps } = createFocusRing()
|
|
244
|
+
|
|
245
|
+
// Increment button interactions
|
|
246
|
+
const { isPressed: incrementPressed, pressProps: incrementPressProps } = createPress({
|
|
247
|
+
get isDisabled() {
|
|
248
|
+
return ariaProps.isDisabled || !state.canIncrement()
|
|
249
|
+
},
|
|
250
|
+
onPress: () => {
|
|
251
|
+
state.increment()
|
|
252
|
+
inputRef?.focus()
|
|
253
|
+
},
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
const { isHovered: incrementHovered, hoverProps: incrementHoverProps } = createHover({
|
|
257
|
+
get isDisabled() {
|
|
258
|
+
return ariaProps.isDisabled || !state.canIncrement()
|
|
259
|
+
},
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
// Decrement button interactions
|
|
263
|
+
const { isPressed: decrementPressed, pressProps: decrementPressProps } = createPress({
|
|
264
|
+
get isDisabled() {
|
|
265
|
+
return ariaProps.isDisabled || !state.canDecrement()
|
|
266
|
+
},
|
|
267
|
+
onPress: () => {
|
|
268
|
+
state.decrement()
|
|
269
|
+
inputRef?.focus()
|
|
270
|
+
},
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
const { isHovered: decrementHovered, hoverProps: decrementHoverProps } = createHover({
|
|
274
|
+
get isDisabled() {
|
|
275
|
+
return ariaProps.isDisabled || !state.canDecrement()
|
|
276
|
+
},
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
// Compute classes
|
|
280
|
+
const containerClasses = () => {
|
|
281
|
+
const base = 'flex flex-col'
|
|
282
|
+
const disabledClass = ariaProps.isDisabled ? 'opacity-60' : ''
|
|
283
|
+
const custom = local.class || ''
|
|
284
|
+
return [base, disabledClass, custom].filter(Boolean).join(' ')
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const groupClasses = () => {
|
|
288
|
+
const base = 'flex items-center'
|
|
289
|
+
const gapClass = size().buttonGap
|
|
290
|
+
return [base, gapClass].filter(Boolean).join(' ')
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const inputClasses = () => {
|
|
294
|
+
const base = 'flex-1 rounded-md transition-all duration-200 outline-none text-center'
|
|
295
|
+
const sizeClass = size().input
|
|
296
|
+
|
|
297
|
+
let variantClass: string
|
|
298
|
+
if (local.variant === 'filled') {
|
|
299
|
+
variantClass = 'bg-bg-200 border border-transparent'
|
|
300
|
+
} else {
|
|
301
|
+
variantClass = 'bg-transparent border border-bg-400'
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
let stateClass: string
|
|
305
|
+
if (ariaProps.isDisabled) {
|
|
306
|
+
stateClass = 'bg-bg-200 text-primary-500 cursor-not-allowed'
|
|
307
|
+
} else if (ariaProps.isInvalid) {
|
|
308
|
+
stateClass = 'border-danger-500 focus:border-danger-400 focus:ring-2 focus:ring-danger-400/20'
|
|
309
|
+
} else {
|
|
310
|
+
stateClass = 'text-primary-100 placeholder:text-primary-500 focus:border-accent focus:ring-2 focus:ring-accent/20'
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const hoverClass = ariaProps.isDisabled ? '' : 'hover:border-accent-300'
|
|
314
|
+
|
|
315
|
+
return [base, sizeClass, variantClass, stateClass, hoverClass].filter(Boolean).join(' ')
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const buttonClasses = (isIncrement: boolean) => {
|
|
319
|
+
const base = 'flex items-center justify-center rounded-md transition-all duration-150 select-none'
|
|
320
|
+
const sizeClass = size().button
|
|
321
|
+
|
|
322
|
+
const isDisabled = ariaProps.isDisabled || (isIncrement ? !state.canIncrement() : !state.canDecrement())
|
|
323
|
+
const isPressed = isIncrement ? incrementPressed() : decrementPressed()
|
|
324
|
+
const isHovered = isIncrement ? incrementHovered() : decrementHovered()
|
|
325
|
+
|
|
326
|
+
let stateClass: string
|
|
327
|
+
if (isDisabled) {
|
|
328
|
+
stateClass = 'bg-bg-300 text-primary-600 cursor-not-allowed'
|
|
329
|
+
} else if (isPressed) {
|
|
330
|
+
stateClass = 'bg-accent-600 text-white scale-95'
|
|
331
|
+
} else if (isHovered) {
|
|
332
|
+
stateClass = 'bg-accent-500 text-white'
|
|
333
|
+
} else {
|
|
334
|
+
stateClass = 'bg-bg-300 text-primary-200 hover:bg-accent-500 hover:text-white'
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return [base, sizeClass, stateClass].filter(Boolean).join(' ')
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const labelClasses = () => {
|
|
341
|
+
const base = 'block font-medium text-primary-200 mb-1'
|
|
342
|
+
const sizeClass = size().label
|
|
343
|
+
return [base, sizeClass].filter(Boolean).join(' ')
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const descriptionClasses = () => {
|
|
347
|
+
const base = 'mt-1 text-primary-400'
|
|
348
|
+
const sizeClass = size().description
|
|
349
|
+
return [base, sizeClass].filter(Boolean).join(' ')
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const errorClasses = () => {
|
|
353
|
+
const base = 'mt-1 text-danger-500'
|
|
354
|
+
const sizeClass = size().description
|
|
355
|
+
return [base, sizeClass].filter(Boolean).join(' ')
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Clean props helpers
|
|
359
|
+
const cleanInputProps = () => {
|
|
360
|
+
const { ref: _ref, ...rest } = numberFieldAria.inputProps as Record<string, unknown>
|
|
361
|
+
return rest
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const cleanFocusProps = () => {
|
|
365
|
+
const { ref: _ref, ...rest } = focusProps as Record<string, unknown>
|
|
366
|
+
return rest
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const cleanGroupProps = () => {
|
|
370
|
+
const { ref: _ref, ...rest } = numberFieldAria.groupProps as Record<string, unknown>
|
|
371
|
+
return rest
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const cleanDecrementProps = () => {
|
|
375
|
+
const { ref: _ref, ...rest } = numberFieldAria.decrementButtonProps as Record<string, unknown>
|
|
376
|
+
return rest
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const cleanIncrementProps = () => {
|
|
380
|
+
const { ref: _ref, ...rest } = numberFieldAria.incrementButtonProps as Record<string, unknown>
|
|
381
|
+
return rest
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const cleanDecrementPressProps = () => {
|
|
385
|
+
const { ref: _ref, ...rest } = decrementPressProps as Record<string, unknown>
|
|
386
|
+
return rest
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const cleanDecrementHoverProps = () => {
|
|
390
|
+
const { ref: _ref, ...rest } = decrementHoverProps as Record<string, unknown>
|
|
391
|
+
return rest
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const cleanIncrementPressProps = () => {
|
|
395
|
+
const { ref: _ref, ...rest } = incrementPressProps as Record<string, unknown>
|
|
396
|
+
return rest
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const cleanIncrementHoverProps = () => {
|
|
400
|
+
const { ref: _ref, ...rest } = incrementHoverProps as Record<string, unknown>
|
|
401
|
+
return rest
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return (
|
|
405
|
+
<div
|
|
406
|
+
{...cleanGroupProps()}
|
|
407
|
+
class={containerClasses()}
|
|
408
|
+
data-disabled={ariaProps.isDisabled || undefined}
|
|
409
|
+
data-invalid={ariaProps.isInvalid || undefined}
|
|
410
|
+
>
|
|
411
|
+
{/* Label */}
|
|
412
|
+
<Show when={local.label}>
|
|
413
|
+
<span {...numberFieldAria.labelProps} class={labelClasses()}>
|
|
414
|
+
{local.label}
|
|
415
|
+
<Show when={ariaProps.isRequired}>
|
|
416
|
+
<span class="text-danger-500 ml-1">*</span>
|
|
417
|
+
</Show>
|
|
418
|
+
</span>
|
|
419
|
+
</Show>
|
|
420
|
+
|
|
421
|
+
{/* Input Group */}
|
|
422
|
+
<div class={groupClasses()}>
|
|
423
|
+
{/* Decrement Button */}
|
|
424
|
+
<Show when={!local.hideStepper}>
|
|
425
|
+
<button
|
|
426
|
+
{...cleanDecrementProps()}
|
|
427
|
+
{...cleanDecrementPressProps()}
|
|
428
|
+
{...cleanDecrementHoverProps()}
|
|
429
|
+
class={buttonClasses(false)}
|
|
430
|
+
data-pressed={decrementPressed() || undefined}
|
|
431
|
+
data-hovered={decrementHovered() || undefined}
|
|
432
|
+
data-disabled={ariaProps.isDisabled || !state.canDecrement() || undefined}
|
|
433
|
+
>
|
|
434
|
+
<MinusIcon class="w-4 h-4" />
|
|
435
|
+
</button>
|
|
436
|
+
</Show>
|
|
437
|
+
|
|
438
|
+
{/* Input */}
|
|
439
|
+
<input
|
|
440
|
+
ref={inputRef}
|
|
441
|
+
{...cleanInputProps()}
|
|
442
|
+
{...cleanFocusProps()}
|
|
443
|
+
class={inputClasses()}
|
|
444
|
+
data-focused={isFocused() || undefined}
|
|
445
|
+
data-focus-visible={isFocusVisible() || undefined}
|
|
446
|
+
/>
|
|
447
|
+
|
|
448
|
+
{/* Increment Button */}
|
|
449
|
+
<Show when={!local.hideStepper}>
|
|
450
|
+
<button
|
|
451
|
+
{...cleanIncrementProps()}
|
|
452
|
+
{...cleanIncrementPressProps()}
|
|
453
|
+
{...cleanIncrementHoverProps()}
|
|
454
|
+
class={buttonClasses(true)}
|
|
455
|
+
data-pressed={incrementPressed() || undefined}
|
|
456
|
+
data-hovered={incrementHovered() || undefined}
|
|
457
|
+
data-disabled={ariaProps.isDisabled || !state.canIncrement() || undefined}
|
|
458
|
+
>
|
|
459
|
+
<PlusIcon class="w-4 h-4" />
|
|
460
|
+
</button>
|
|
461
|
+
</Show>
|
|
462
|
+
</div>
|
|
463
|
+
|
|
464
|
+
{/* Description */}
|
|
465
|
+
<Show when={local.description && !ariaProps.isInvalid}>
|
|
466
|
+
<span {...numberFieldAria.descriptionProps} class={descriptionClasses()}>
|
|
467
|
+
{local.description}
|
|
468
|
+
</span>
|
|
469
|
+
</Show>
|
|
470
|
+
|
|
471
|
+
{/* Error Message */}
|
|
472
|
+
<Show when={ariaProps.isInvalid && local.errorMessage}>
|
|
473
|
+
<span {...numberFieldAria.errorMessageProps} class={errorClasses()}>
|
|
474
|
+
{local.errorMessage}
|
|
475
|
+
</span>
|
|
476
|
+
</Show>
|
|
477
|
+
</div>
|
|
478
|
+
)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Re-export types
|
|
482
|
+
export type { NumberFieldState } from '@proyecto-viviana/solid-stately'
|