@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,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Popover component for proyecto-viviana-ui
|
|
3
|
+
*
|
|
4
|
+
* A popover displays content in an overlay positioned relative to a trigger.
|
|
5
|
+
* Built on top of solidaria-components for accessibility.
|
|
6
|
+
* Follows Spectrum 2 design patterns.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { type JSX, Show, splitProps } from 'solid-js'
|
|
10
|
+
import {
|
|
11
|
+
Popover as HeadlessPopover,
|
|
12
|
+
PopoverTrigger as HeadlessPopoverTrigger,
|
|
13
|
+
OverlayArrow as HeadlessOverlayArrow,
|
|
14
|
+
type PopoverProps as HeadlessPopoverProps,
|
|
15
|
+
type PopoverTriggerProps as HeadlessPopoverTriggerProps,
|
|
16
|
+
type PopoverRenderProps,
|
|
17
|
+
} from '@proyecto-viviana/solidaria-components'
|
|
18
|
+
import type { Placement, PlacementAxis } from '@proyecto-viviana/solidaria'
|
|
19
|
+
|
|
20
|
+
// ============================================
|
|
21
|
+
// TYPES
|
|
22
|
+
// ============================================
|
|
23
|
+
|
|
24
|
+
export type PopoverPlacement = Placement
|
|
25
|
+
export type PopoverSize = 'sm' | 'md' | 'lg'
|
|
26
|
+
|
|
27
|
+
export interface PopoverTriggerProps extends HeadlessPopoverTriggerProps {
|
|
28
|
+
/** The children of the popover trigger (trigger element and popover). */
|
|
29
|
+
children: JSX.Element
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface PopoverProps extends Omit<HeadlessPopoverProps, 'class' | 'style' | 'children'> {
|
|
33
|
+
/** The content of the popover. */
|
|
34
|
+
children: JSX.Element
|
|
35
|
+
/** The position of the popover relative to the trigger. */
|
|
36
|
+
placement?: PopoverPlacement
|
|
37
|
+
/** Size variant of the popover. */
|
|
38
|
+
size?: PopoverSize
|
|
39
|
+
/** Additional CSS class name. */
|
|
40
|
+
class?: string
|
|
41
|
+
/** Whether to show an arrow pointing to the trigger. */
|
|
42
|
+
showArrow?: boolean
|
|
43
|
+
/** Custom padding inside the popover. */
|
|
44
|
+
padding?: 'none' | 'sm' | 'md' | 'lg'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ============================================
|
|
48
|
+
// STYLES
|
|
49
|
+
// ============================================
|
|
50
|
+
|
|
51
|
+
const baseStyles = [
|
|
52
|
+
'bg-bg-300',
|
|
53
|
+
'rounded-lg',
|
|
54
|
+
'shadow-xl',
|
|
55
|
+
'border border-primary-700',
|
|
56
|
+
'text-primary-200',
|
|
57
|
+
'outline-none',
|
|
58
|
+
// Animation
|
|
59
|
+
'animate-in fade-in-0 zoom-in-95',
|
|
60
|
+
'data-[placement=top]:slide-in-from-bottom-2',
|
|
61
|
+
'data-[placement=bottom]:slide-in-from-top-2',
|
|
62
|
+
'data-[placement=left]:slide-in-from-right-2',
|
|
63
|
+
'data-[placement=right]:slide-in-from-left-2',
|
|
64
|
+
'data-[exiting]:animate-out data-[exiting]:fade-out-0 data-[exiting]:zoom-out-95',
|
|
65
|
+
].join(' ')
|
|
66
|
+
|
|
67
|
+
const sizeStyles: Record<PopoverSize, string> = {
|
|
68
|
+
sm: 'max-w-xs',
|
|
69
|
+
md: 'max-w-sm',
|
|
70
|
+
lg: 'max-w-lg',
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const paddingStyles: Record<string, string> = {
|
|
74
|
+
none: '',
|
|
75
|
+
sm: 'p-2',
|
|
76
|
+
md: 'p-4',
|
|
77
|
+
lg: 'p-6',
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Arrow styles based on placement
|
|
81
|
+
const arrowBaseStyles = [
|
|
82
|
+
'fill-bg-300',
|
|
83
|
+
'stroke-primary-700',
|
|
84
|
+
'stroke-1',
|
|
85
|
+
].join(' ')
|
|
86
|
+
|
|
87
|
+
// Arrow positioning for each placement axis
|
|
88
|
+
const getArrowRotation = (placement: PlacementAxis | null): string => {
|
|
89
|
+
switch (placement) {
|
|
90
|
+
case 'top':
|
|
91
|
+
return 'rotate-180'
|
|
92
|
+
case 'bottom':
|
|
93
|
+
return ''
|
|
94
|
+
case 'left':
|
|
95
|
+
return 'rotate-90'
|
|
96
|
+
case 'right':
|
|
97
|
+
return '-rotate-90'
|
|
98
|
+
default:
|
|
99
|
+
return ''
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ============================================
|
|
104
|
+
// COMPONENTS
|
|
105
|
+
// ============================================
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* PopoverTrigger wraps around a trigger element and a Popover.
|
|
109
|
+
* It handles opening and closing the Popover when the user interacts
|
|
110
|
+
* with the trigger.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```tsx
|
|
114
|
+
* <PopoverTrigger>
|
|
115
|
+
* <Button>Open Popover</Button>
|
|
116
|
+
* <Popover>
|
|
117
|
+
* <p>Popover content here!</p>
|
|
118
|
+
* </Popover>
|
|
119
|
+
* </PopoverTrigger>
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export function PopoverTrigger(props: PopoverTriggerProps): JSX.Element {
|
|
123
|
+
return <HeadlessPopoverTrigger {...props} />
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Styled popover component that displays content in an overlay.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```tsx
|
|
131
|
+
* <PopoverTrigger>
|
|
132
|
+
* <Button>Settings</Button>
|
|
133
|
+
* <Popover placement="bottom" size="md">
|
|
134
|
+
* <h3>Settings</h3>
|
|
135
|
+
* <p>Configure your preferences here.</p>
|
|
136
|
+
* </Popover>
|
|
137
|
+
* </PopoverTrigger>
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
export function Popover(props: PopoverProps): JSX.Element {
|
|
141
|
+
const [local, rest] = splitProps(props, [
|
|
142
|
+
'placement',
|
|
143
|
+
'size',
|
|
144
|
+
'class',
|
|
145
|
+
'showArrow',
|
|
146
|
+
'padding',
|
|
147
|
+
])
|
|
148
|
+
|
|
149
|
+
const placement = () => local.placement ?? 'bottom'
|
|
150
|
+
const size = () => local.size ?? 'md'
|
|
151
|
+
const padding = () => local.padding ?? 'md'
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<HeadlessPopover
|
|
155
|
+
{...rest}
|
|
156
|
+
placement={placement()}
|
|
157
|
+
class={(_renderProps: PopoverRenderProps) => {
|
|
158
|
+
const classes = [
|
|
159
|
+
baseStyles,
|
|
160
|
+
sizeStyles[size()],
|
|
161
|
+
paddingStyles[padding()],
|
|
162
|
+
local.class ?? '',
|
|
163
|
+
].filter(Boolean).join(' ')
|
|
164
|
+
return classes
|
|
165
|
+
}}
|
|
166
|
+
>
|
|
167
|
+
{(renderProps: PopoverRenderProps) => (
|
|
168
|
+
<>
|
|
169
|
+
<Show when={local.showArrow}>
|
|
170
|
+
<PopoverArrow placement={renderProps.placement} />
|
|
171
|
+
</Show>
|
|
172
|
+
{props.children}
|
|
173
|
+
</>
|
|
174
|
+
)}
|
|
175
|
+
</HeadlessPopover>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Arrow component for the popover.
|
|
181
|
+
* Automatically positions itself based on the popover placement.
|
|
182
|
+
*/
|
|
183
|
+
interface PopoverArrowProps {
|
|
184
|
+
/** The current placement axis. */
|
|
185
|
+
placement: PlacementAxis | null
|
|
186
|
+
/** Additional CSS class. */
|
|
187
|
+
class?: string
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function PopoverArrow(props: PopoverArrowProps): JSX.Element {
|
|
191
|
+
return (
|
|
192
|
+
<HeadlessOverlayArrow
|
|
193
|
+
class="absolute block"
|
|
194
|
+
style={{
|
|
195
|
+
// Position based on placement
|
|
196
|
+
...(props.placement === 'top' && { bottom: '100%', left: '50%', transform: 'translateX(-50%)' }),
|
|
197
|
+
...(props.placement === 'bottom' && { top: '-8px', left: '50%', transform: 'translateX(-50%)' }),
|
|
198
|
+
...(props.placement === 'left' && { right: '100%', top: '50%', transform: 'translateY(-50%)' }),
|
|
199
|
+
...(props.placement === 'right' && { left: '-8px', top: '50%', transform: 'translateY(-50%)' }),
|
|
200
|
+
}}
|
|
201
|
+
>
|
|
202
|
+
<svg
|
|
203
|
+
width="12"
|
|
204
|
+
height="12"
|
|
205
|
+
viewBox="0 0 12 12"
|
|
206
|
+
class={`${arrowBaseStyles} ${getArrowRotation(props.placement)} ${props.class ?? ''}`}
|
|
207
|
+
>
|
|
208
|
+
<path d="M0 0 L6 6 L12 0" />
|
|
209
|
+
</svg>
|
|
210
|
+
</HeadlessOverlayArrow>
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ============================================
|
|
215
|
+
// POPOVER CONTENT SECTIONS
|
|
216
|
+
// ============================================
|
|
217
|
+
|
|
218
|
+
export interface PopoverHeaderProps {
|
|
219
|
+
/** The title of the popover. */
|
|
220
|
+
title: string
|
|
221
|
+
/** Optional description text. */
|
|
222
|
+
description?: string
|
|
223
|
+
/** Additional CSS class. */
|
|
224
|
+
class?: string
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Header section for popover with title and optional description.
|
|
229
|
+
*/
|
|
230
|
+
export function PopoverHeader(props: PopoverHeaderProps): JSX.Element {
|
|
231
|
+
return (
|
|
232
|
+
<div class={`mb-3 ${props.class ?? ''}`}>
|
|
233
|
+
<h3 class="text-lg font-semibold text-primary-100">{props.title}</h3>
|
|
234
|
+
<Show when={props.description}>
|
|
235
|
+
<p class="text-sm text-primary-400 mt-1">{props.description}</p>
|
|
236
|
+
</Show>
|
|
237
|
+
</div>
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export interface PopoverFooterProps {
|
|
242
|
+
/** Footer content, typically buttons. */
|
|
243
|
+
children: JSX.Element
|
|
244
|
+
/** Additional CSS class. */
|
|
245
|
+
class?: string
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Footer section for popover actions.
|
|
250
|
+
*/
|
|
251
|
+
export function PopoverFooter(props: PopoverFooterProps): JSX.Element {
|
|
252
|
+
return (
|
|
253
|
+
<div class={`flex gap-2 justify-end mt-4 pt-3 border-t border-primary-700 ${props.class ?? ''}`}>
|
|
254
|
+
{props.children}
|
|
255
|
+
</div>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Re-export types
|
|
260
|
+
export type { PopoverRenderProps, Placement, PlacementAxis }
|
package/src/radio/index.tsx
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
* RadioGroup and Radio components for proyecto-viviana-ui
|
|
3
3
|
*
|
|
4
4
|
* Styled radio components built on top of solidaria-components.
|
|
5
|
-
*
|
|
6
|
-
* IMPORTANT: Due to SolidJS's JSX evaluation order, we cannot wrap the headless
|
|
7
|
-
* components in a traditional way. Instead, we re-export them and provide
|
|
8
|
-
* styling through the class prop functions.
|
|
5
|
+
* SSR-compatible - renders children and UI elements directly without render props.
|
|
9
6
|
*/
|
|
10
7
|
|
|
11
8
|
import { type JSX, Show, createContext, useContext, splitProps } from 'solid-js'
|
|
@@ -88,7 +85,6 @@ export function RadioGroup(props: RadioGroupProps): JSX.Element {
|
|
|
88
85
|
'label',
|
|
89
86
|
'description',
|
|
90
87
|
'errorMessage',
|
|
91
|
-
'children',
|
|
92
88
|
])
|
|
93
89
|
|
|
94
90
|
const size = local.size ?? 'md'
|
|
@@ -102,22 +98,6 @@ export function RadioGroup(props: RadioGroupProps): JSX.Element {
|
|
|
102
98
|
return [base, orientationClass, disabledClass, customClass].filter(Boolean).join(' ')
|
|
103
99
|
}
|
|
104
100
|
|
|
105
|
-
// Create render children function
|
|
106
|
-
const renderChildren = (renderProps: RadioGroupRenderProps) => (
|
|
107
|
-
<>
|
|
108
|
-
<Show when={local.label}>
|
|
109
|
-
<span class="text-primary-200 font-medium mb-1">{local.label}</span>
|
|
110
|
-
</Show>
|
|
111
|
-
{typeof local.children === 'function' ? local.children(renderProps) : local.children}
|
|
112
|
-
<Show when={local.description && !renderProps.isInvalid}>
|
|
113
|
-
<span class="text-primary-400 text-sm">{local.description}</span>
|
|
114
|
-
</Show>
|
|
115
|
-
<Show when={local.errorMessage && renderProps.isInvalid}>
|
|
116
|
-
<span class="text-danger-400 text-sm">{local.errorMessage}</span>
|
|
117
|
-
</Show>
|
|
118
|
-
</>
|
|
119
|
-
)
|
|
120
|
-
|
|
121
101
|
// Pass remaining props through to headless component
|
|
122
102
|
// headlessProps maintains reactivity for controlled values like value/onChange
|
|
123
103
|
return (
|
|
@@ -126,8 +106,18 @@ export function RadioGroup(props: RadioGroupProps): JSX.Element {
|
|
|
126
106
|
{...headlessProps}
|
|
127
107
|
class={getClassName}
|
|
128
108
|
data-size={size}
|
|
129
|
-
|
|
130
|
-
|
|
109
|
+
>
|
|
110
|
+
<Show when={local.label}>
|
|
111
|
+
<span class="text-primary-200 font-medium mb-1">{local.label}</span>
|
|
112
|
+
</Show>
|
|
113
|
+
{props.children as JSX.Element}
|
|
114
|
+
<Show when={local.description}>
|
|
115
|
+
<span class="text-primary-400 text-sm [&:has(~[data-invalid])]:hidden">{local.description}</span>
|
|
116
|
+
</Show>
|
|
117
|
+
<Show when={local.errorMessage}>
|
|
118
|
+
<span class="text-danger-400 text-sm hidden [[data-invalid]_&]:block">{local.errorMessage}</span>
|
|
119
|
+
</Show>
|
|
120
|
+
</HeadlessRadioGroup>
|
|
131
121
|
</RadioSizeContext.Provider>
|
|
132
122
|
)
|
|
133
123
|
}
|
|
@@ -139,81 +129,45 @@ export function RadioGroup(props: RadioGroupProps): JSX.Element {
|
|
|
139
129
|
/**
|
|
140
130
|
* A radio button allows users to select a single option from a list.
|
|
141
131
|
* Must be used within a RadioGroup.
|
|
132
|
+
* SSR-compatible - renders static JSX without render prop children.
|
|
133
|
+
*
|
|
134
|
+
* Note: Unlike other styled components, Radio does not use render props for children.
|
|
135
|
+
* Instead, it relies on data attributes set by the headless Radio component for styling.
|
|
136
|
+
* However, since we need dynamic styling based on state, we accept that this component
|
|
137
|
+
* has some limitations compared to the render-props-based original implementation.
|
|
142
138
|
*
|
|
143
139
|
* Built on solidaria-components Radio for full accessibility support.
|
|
144
140
|
*/
|
|
145
141
|
export function Radio(props: RadioProps): JSX.Element {
|
|
142
|
+
const [local, headlessProps] = splitProps(props, ['class'])
|
|
146
143
|
const sizeFromContext = useContext(RadioSizeContext)
|
|
147
|
-
const
|
|
148
|
-
const customClass =
|
|
144
|
+
const sizeStyle = sizeStyles[sizeFromContext]
|
|
145
|
+
const customClass = local.class ?? ''
|
|
149
146
|
|
|
150
147
|
// Generate class based on render props
|
|
151
148
|
const getClassName = (renderProps: RadioRenderProps): string => {
|
|
152
|
-
const base = 'inline-flex items-center gap-2
|
|
153
|
-
const
|
|
154
|
-
|
|
149
|
+
const base = 'inline-flex items-center gap-2'
|
|
150
|
+
const cursorClass = renderProps.isDisabled ? 'cursor-not-allowed' : 'cursor-pointer'
|
|
151
|
+
const disabledClass = renderProps.isDisabled ? 'opacity-50' : ''
|
|
152
|
+
return [base, cursorClass, disabledClass, customClass].filter(Boolean).join(' ')
|
|
155
153
|
}
|
|
156
154
|
|
|
157
|
-
//
|
|
158
|
-
const
|
|
155
|
+
// Static classes - will use a simplified visual style since we can't dynamically style based on state without render props
|
|
156
|
+
const circleClass = `relative flex items-center justify-center rounded-full border-2 transition-all duration-200 ${sizeStyle.circle} border-primary-600 bg-transparent hover:border-accent-300`
|
|
157
|
+
const dotClass = `rounded-full bg-accent transition-all duration-200 ${sizeStyle.dot}`
|
|
158
|
+
const labelClass = `text-primary-200 ${sizeStyle.label}`
|
|
159
159
|
|
|
160
160
|
return (
|
|
161
161
|
<HeadlessRadio
|
|
162
162
|
{...headlessProps}
|
|
163
163
|
class={getClassName}
|
|
164
164
|
>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (renderProps.isDisabled) {
|
|
172
|
-
colorClass = 'border-bg-300 bg-bg-200'
|
|
173
|
-
} else if (renderProps.isSelected) {
|
|
174
|
-
colorClass = 'border-accent bg-transparent'
|
|
175
|
-
} else {
|
|
176
|
-
colorClass = 'border-primary-600 bg-transparent hover:border-accent-300'
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const focusClass = renderProps.isFocusVisible
|
|
180
|
-
? 'ring-2 ring-accent-300 ring-offset-2 ring-offset-bg-400'
|
|
181
|
-
: ''
|
|
182
|
-
const cursorClass = renderProps.isDisabled ? 'cursor-not-allowed' : 'cursor-pointer'
|
|
183
|
-
|
|
184
|
-
return [base, sizeClass, colorClass, focusClass, cursorClass].filter(Boolean).join(' ')
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const dotClasses = () => {
|
|
188
|
-
const base = 'rounded-full bg-accent transition-all duration-200'
|
|
189
|
-
const sizeClass = size().dot
|
|
190
|
-
const visibilityClass = renderProps.isSelected ? 'scale-100 opacity-100' : 'scale-0 opacity-0'
|
|
191
|
-
|
|
192
|
-
return [base, sizeClass, visibilityClass].filter(Boolean).join(' ')
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const labelClasses = () => {
|
|
196
|
-
const base = 'text-primary-200'
|
|
197
|
-
const sizeClass = size().label
|
|
198
|
-
const disabledClass = renderProps.isDisabled ? 'opacity-50' : ''
|
|
199
|
-
|
|
200
|
-
return [base, sizeClass, disabledClass].filter(Boolean).join(' ')
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Resolve children - could be a function or JSX element
|
|
204
|
-
const resolvedChildren = typeof children === 'function' ? children(renderProps) : children
|
|
205
|
-
|
|
206
|
-
return (
|
|
207
|
-
<>
|
|
208
|
-
<span class={circleClasses()}>
|
|
209
|
-
<span class={dotClasses()} />
|
|
210
|
-
</span>
|
|
211
|
-
<Show when={resolvedChildren}>
|
|
212
|
-
<span class={labelClasses()}>{resolvedChildren}</span>
|
|
213
|
-
</Show>
|
|
214
|
-
</>
|
|
215
|
-
)
|
|
216
|
-
}}
|
|
165
|
+
<span class={circleClass}>
|
|
166
|
+
<span class={dotClass} />
|
|
167
|
+
</span>
|
|
168
|
+
<Show when={props.children}>
|
|
169
|
+
<span class={labelClass}>{props.children as JSX.Element}</span>
|
|
170
|
+
</Show>
|
|
217
171
|
</HeadlessRadio>
|
|
218
172
|
)
|
|
219
173
|
}
|