@proyecto-viviana/solidaria-components 0.1.3 → 0.2.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 (64) hide show
  1. package/dist/Color.d.ts +6 -2
  2. package/dist/Color.d.ts.map +1 -1
  3. package/dist/ComboBox.d.ts +3 -3
  4. package/dist/ComboBox.d.ts.map +1 -1
  5. package/dist/GridList.d.ts +2 -2
  6. package/dist/GridList.d.ts.map +1 -1
  7. package/dist/ListBox.d.ts +5 -5
  8. package/dist/ListBox.d.ts.map +1 -1
  9. package/dist/Menu.d.ts +3 -3
  10. package/dist/Menu.d.ts.map +1 -1
  11. package/dist/Select.d.ts +3 -3
  12. package/dist/Select.d.ts.map +1 -1
  13. package/dist/Table.d.ts +2 -2
  14. package/dist/Table.d.ts.map +1 -1
  15. package/dist/Tabs.d.ts +1 -1
  16. package/dist/Tabs.d.ts.map +1 -1
  17. package/dist/index.js +15 -15
  18. package/dist/index.js.map +2 -2
  19. package/dist/index.jsx +9056 -0
  20. package/dist/index.jsx.map +7 -0
  21. package/dist/index.ssr.js +15 -15
  22. package/dist/index.ssr.js.map +2 -2
  23. package/package.json +8 -10
  24. package/src/Autocomplete.tsx +0 -174
  25. package/src/Breadcrumbs.tsx +0 -264
  26. package/src/Button.tsx +0 -238
  27. package/src/Calendar.tsx +0 -471
  28. package/src/Checkbox.tsx +0 -387
  29. package/src/Color.tsx +0 -1370
  30. package/src/ComboBox.tsx +0 -824
  31. package/src/DateField.tsx +0 -337
  32. package/src/DatePicker.tsx +0 -367
  33. package/src/Dialog.tsx +0 -262
  34. package/src/Disclosure.tsx +0 -439
  35. package/src/GridList.tsx +0 -511
  36. package/src/Landmark.tsx +0 -203
  37. package/src/Link.tsx +0 -201
  38. package/src/ListBox.tsx +0 -346
  39. package/src/Menu.tsx +0 -544
  40. package/src/Meter.tsx +0 -157
  41. package/src/Modal.tsx +0 -433
  42. package/src/NumberField.tsx +0 -542
  43. package/src/Popover.tsx +0 -540
  44. package/src/ProgressBar.tsx +0 -162
  45. package/src/RadioGroup.tsx +0 -356
  46. package/src/RangeCalendar.tsx +0 -462
  47. package/src/SearchField.tsx +0 -479
  48. package/src/Select.tsx +0 -734
  49. package/src/Separator.tsx +0 -130
  50. package/src/Slider.tsx +0 -500
  51. package/src/Switch.tsx +0 -213
  52. package/src/Table.tsx +0 -857
  53. package/src/Tabs.tsx +0 -552
  54. package/src/TagGroup.tsx +0 -421
  55. package/src/TextField.tsx +0 -271
  56. package/src/TimeField.tsx +0 -455
  57. package/src/Toast.tsx +0 -503
  58. package/src/Toolbar.tsx +0 -160
  59. package/src/Tooltip.tsx +0 -423
  60. package/src/Tree.tsx +0 -551
  61. package/src/VisuallyHidden.tsx +0 -60
  62. package/src/contexts.ts +0 -74
  63. package/src/index.ts +0 -620
  64. package/src/utils.tsx +0 -329
package/src/Modal.tsx DELETED
@@ -1,433 +0,0 @@
1
- /**
2
- * Modal and ModalOverlay components for solidaria-components
3
- *
4
- * Headless modal components with overlay, focus trapping, and dismissal handling.
5
- * Port of react-aria-components Modal.
6
- */
7
-
8
- import {
9
- type JSX,
10
- createContext,
11
- createMemo,
12
- createSignal,
13
- createEffect,
14
- onCleanup,
15
- splitProps,
16
- Show,
17
- useContext,
18
- } from 'solid-js'
19
- import { Portal, isServer } from 'solid-js/web'
20
- import {
21
- createInteractOutside,
22
- ariaHideOutside,
23
- FocusScope,
24
- } from '@proyecto-viviana/solidaria'
25
- import {
26
- type RenderChildren,
27
- type ClassNameOrFunction,
28
- type StyleOrFunction,
29
- useRenderProps,
30
- filterDOMProps,
31
- dataAttr,
32
- } from './utils'
33
- import {
34
- DialogTriggerContext,
35
- OverlayTriggerStateContext,
36
- type OverlayTriggerState,
37
- } from './contexts'
38
-
39
- // ============================================
40
- // INTERNAL CONTEXT
41
- // ============================================
42
-
43
- /**
44
- * Internal context to signal that Modal is wrapped in ModalOverlay.
45
- * When present, Modal should not create its own Portal.
46
- */
47
- interface InternalModalContextValue {
48
- isDismissable?: boolean
49
- isKeyboardDismissDisabled?: boolean
50
- }
51
-
52
- const InternalModalContext = createContext<InternalModalContextValue | null>(null)
53
-
54
- // ============================================
55
- // TYPES
56
- // ============================================
57
-
58
- export interface ModalRenderProps {
59
- /** Whether the modal is currently entering (for animations). */
60
- isEntering: boolean
61
- /** Whether the modal is currently exiting (for animations). */
62
- isExiting: boolean
63
- }
64
-
65
- export interface ModalOverlayProps {
66
- /** The children of the component - can be JSX or render function. */
67
- children?: RenderChildren<ModalRenderProps>
68
- /** The CSS className for the element. */
69
- class?: ClassNameOrFunction<ModalRenderProps>
70
- /** The inline style for the element. */
71
- style?: StyleOrFunction<ModalRenderProps>
72
- /** Whether the modal is open (controlled). */
73
- isOpen?: boolean
74
- /** Whether the modal opens by default (uncontrolled). */
75
- defaultOpen?: boolean
76
- /** Handler called when the modal's open state changes. */
77
- onOpenChange?: (isOpen: boolean) => void
78
- /** Whether clicking outside the modal closes it. */
79
- isDismissable?: boolean
80
- /** Whether pressing Escape closes the modal. */
81
- isKeyboardDismissDisabled?: boolean
82
- /** Whether the modal is entering (for animations). */
83
- isEntering?: boolean
84
- /** Whether the modal is exiting (for animations). */
85
- isExiting?: boolean
86
- }
87
-
88
- export interface ModalProps extends ModalOverlayProps {}
89
-
90
- // Re-export from contexts for backwards compatibility
91
- export { OverlayTriggerStateContext, type OverlayTriggerState } from './contexts'
92
- export { useOverlayTriggerState } from './contexts'
93
-
94
- // ============================================
95
- // MODAL OVERLAY COMPONENT
96
- // ============================================
97
-
98
- /**
99
- * ModalOverlay is the backdrop/underlay behind a modal.
100
- * It handles click-outside dismissal and provides styling hooks.
101
- */
102
- export function ModalOverlay(props: ModalOverlayProps): JSX.Element {
103
- if (isServer) {
104
- return <>{props.children}</>
105
- }
106
-
107
- // IMPORTANT: Don't destructure or access props.children early!
108
- // In SolidJS, children are lazily evaluated. Accessing them before
109
- // the context provider renders causes them to evaluate outside the context.
110
- // See: https://github.com/solidjs/solid/issues/182
111
- const [local, rest] = splitProps(props, [
112
- 'class',
113
- 'style',
114
- 'isOpen',
115
- 'defaultOpen',
116
- 'onOpenChange',
117
- 'isDismissable',
118
- 'isKeyboardDismissDisabled',
119
- 'isEntering',
120
- 'isExiting',
121
- ])
122
-
123
- // Get state from DialogTrigger context if available
124
- const dialogTriggerContext = useContext(DialogTriggerContext)
125
-
126
- // Internal state for uncontrolled mode
127
- const [internalOpen, setInternalOpen] = createSignal(local.defaultOpen ?? false)
128
-
129
- // Determine if open (controlled > DialogTrigger context > uncontrolled)
130
- const isOpen = (): boolean => {
131
- if (local.isOpen !== undefined) return local.isOpen
132
- if (dialogTriggerContext) return dialogTriggerContext.state.isOpen()
133
- return internalOpen()
134
- }
135
-
136
- const close = () => {
137
- if (local.isOpen !== undefined) {
138
- local.onOpenChange?.(false)
139
- } else if (dialogTriggerContext) {
140
- dialogTriggerContext.state.close()
141
- local.onOpenChange?.(false)
142
- } else {
143
- setInternalOpen(false)
144
- local.onOpenChange?.(false)
145
- }
146
- }
147
-
148
- const open = () => {
149
- if (local.isOpen !== undefined) {
150
- local.onOpenChange?.(true)
151
- } else if (dialogTriggerContext) {
152
- dialogTriggerContext.state.open()
153
- local.onOpenChange?.(true)
154
- } else {
155
- setInternalOpen(true)
156
- local.onOpenChange?.(true)
157
- }
158
- }
159
-
160
- const toggle = () => {
161
- if (isOpen()) {
162
- close()
163
- } else {
164
- open()
165
- }
166
- }
167
-
168
- // Create overlay trigger state for context
169
- const state: OverlayTriggerState = {
170
- get isOpen() { return isOpen() },
171
- open,
172
- close,
173
- toggle,
174
- }
175
-
176
- // Render props values
177
- const renderValues = createMemo<ModalRenderProps>(() => ({
178
- isEntering: local.isEntering ?? false,
179
- isExiting: local.isExiting ?? false,
180
- }))
181
-
182
- // Resolve render props - don't pass children, we'll render props.children directly
183
- const renderProps = useRenderProps(
184
- {
185
- class: local.class,
186
- style: local.style,
187
- defaultClassName: 'solidaria-ModalOverlay',
188
- },
189
- renderValues
190
- )
191
-
192
- // Filter DOM props
193
- const domProps = createMemo(() => filterDOMProps(rest as Record<string, unknown>, { global: true }))
194
-
195
- // Internal context value to signal Modal that it's wrapped
196
- const internalModalContext: InternalModalContextValue = {
197
- isDismissable: local.isDismissable,
198
- isKeyboardDismissDisabled: local.isKeyboardDismissDisabled,
199
- }
200
-
201
- // Resolve children - handle both static JSX and render functions
202
- // IMPORTANT: We access props.children directly (not local.children) to preserve
203
- // lazy evaluation inside context providers
204
- const resolveChildren = () => {
205
- const children = props.children
206
- if (typeof children === 'function') {
207
- return (children as (props: ModalRenderProps) => JSX.Element)(renderValues())
208
- }
209
- return children
210
- }
211
-
212
- return (
213
- <Show when={isOpen() || local.isExiting}>
214
- <Portal>
215
- <OverlayTriggerStateContext.Provider value={state}>
216
- <InternalModalContext.Provider value={internalModalContext}>
217
- <div
218
- {...domProps()}
219
- class={renderProps.class()}
220
- style={renderProps.style()}
221
- data-entering={dataAttr(local.isEntering)}
222
- data-exiting={dataAttr(local.isExiting)}
223
- >
224
- {resolveChildren()}
225
- </div>
226
- </InternalModalContext.Provider>
227
- </OverlayTriggerStateContext.Provider>
228
- </Portal>
229
- </Show>
230
- )
231
- }
232
-
233
- // ============================================
234
- // MODAL COMPONENT
235
- // ============================================
236
-
237
- /**
238
- * Modal is a dialog container that manages focus trapping, scroll prevention,
239
- * aria-hiding of content outside, and dismissal.
240
- *
241
- * Usage patterns:
242
- * 1. Standalone: `<Modal isOpen>...</Modal>` - Creates its own overlay
243
- * 2. With custom overlay: `<ModalOverlay><Modal>...</Modal></ModalOverlay>`
244
- *
245
- * Note: Due to SolidJS's eager JSX evaluation, we cannot detect at render time
246
- * whether Modal is wrapped in ModalOverlay. So standalone Modal always creates
247
- * an overlay, and wrapped Modal renders directly (relying on InternalModalContext).
248
- */
249
- export function Modal(props: ModalProps): JSX.Element {
250
- // Check for InternalModalContext which signals we're inside a rendered ModalOverlay
251
- // This works because ModalContent is rendered INSIDE ModalOverlay's Show/Portal
252
- return <ModalContentWithAutoOverlay {...props} />
253
- }
254
-
255
- /**
256
- * Helper component that handles the overlay detection.
257
- * By being a separate component, we can use Show to defer rendering until
258
- * the parent context is available.
259
- */
260
- function ModalContentWithAutoOverlay(props: ModalProps): JSX.Element {
261
- const [overlayProps, modalProps] = splitProps(props, [
262
- 'isOpen',
263
- 'defaultOpen',
264
- 'onOpenChange',
265
- 'isDismissable',
266
- 'isKeyboardDismissDisabled',
267
- 'isEntering',
268
- 'isExiting',
269
- ])
270
-
271
- // Check for InternalModalContext - if present, we're inside a ModalOverlay
272
- const internalContext = useContext(InternalModalContext)
273
-
274
- // If wrapped in ModalOverlay, just render the content
275
- if (internalContext) {
276
- return (
277
- <ModalContent {...modalProps} internalContext={internalContext}>
278
- {props.children}
279
- </ModalContent>
280
- )
281
- }
282
-
283
- // For standalone usage, wrap in ModalOverlay
284
- const standaloneContext: InternalModalContextValue = {
285
- isDismissable: overlayProps.isDismissable,
286
- isKeyboardDismissDisabled: overlayProps.isKeyboardDismissDisabled,
287
- }
288
-
289
- return (
290
- <ModalOverlay {...overlayProps}>
291
- <ModalContent {...modalProps} internalContext={standaloneContext}>
292
- {props.children}
293
- </ModalContent>
294
- </ModalOverlay>
295
- )
296
- }
297
-
298
- /**
299
- * Internal component that renders the actual modal content.
300
- * Used by both standalone Modal and Modal wrapped in ModalOverlay.
301
- */
302
- function ModalContent(props: ModalProps & { internalContext: InternalModalContextValue }): JSX.Element {
303
- if (isServer) {
304
- return <>{props.children}</>
305
- }
306
-
307
- const [local, rest] = splitProps(props, [
308
- 'children',
309
- 'class',
310
- 'style',
311
- 'isOpen',
312
- 'defaultOpen',
313
- 'onOpenChange',
314
- 'isDismissable',
315
- 'isKeyboardDismissDisabled',
316
- 'isEntering',
317
- 'isExiting',
318
- 'internalContext',
319
- ])
320
-
321
- let modalRef!: HTMLDivElement
322
-
323
- // Get state from parent OverlayTriggerStateContext (provided by ModalOverlay)
324
- const parentState = useContext(OverlayTriggerStateContext)
325
-
326
- // Get dismissable settings from internal context (set by ModalOverlay)
327
- const isDismissable = () => local.internalContext?.isDismissable ?? local.isDismissable
328
- const isKeyboardDismissDisabled = () => local.internalContext?.isKeyboardDismissDisabled ?? local.isKeyboardDismissDisabled
329
-
330
- // Determine if open from parent state
331
- const isOpen = (): boolean => {
332
- if (local.isOpen !== undefined) return local.isOpen
333
- return parentState?.isOpen ?? false
334
- }
335
-
336
- const close = () => {
337
- if (local.isOpen !== undefined) {
338
- local.onOpenChange?.(false)
339
- } else {
340
- parentState?.close()
341
- }
342
- }
343
-
344
- // Prevent scroll when modal is open
345
- createEffect(() => {
346
- if (!isOpen()) return
347
-
348
- // Set overflow hidden on html element
349
- const html = document.documentElement
350
- const prevOverflow = html.style.overflow
351
- html.style.overflow = 'hidden'
352
-
353
- onCleanup(() => {
354
- html.style.overflow = prevOverflow
355
- })
356
- })
357
-
358
- // Click outside to close (if dismissable)
359
- createEffect(() => {
360
- if (!isOpen() || !isDismissable()) return
361
-
362
- createInteractOutside({
363
- ref: () => modalRef ?? null,
364
- onInteractOutside: () => {
365
- close()
366
- },
367
- isDisabled: false,
368
- })
369
- })
370
-
371
- // Escape key to close
372
- createEffect(() => {
373
- if (!isOpen() || isKeyboardDismissDisabled()) return
374
-
375
- const handleKeyDown = (e: KeyboardEvent) => {
376
- if (e.key === 'Escape') {
377
- e.preventDefault()
378
- e.stopPropagation()
379
- close()
380
- }
381
- }
382
-
383
- document.addEventListener('keydown', handleKeyDown, true)
384
- onCleanup(() => {
385
- document.removeEventListener('keydown', handleKeyDown, true)
386
- })
387
- })
388
-
389
- // Aria-hide outside content
390
- createEffect(() => {
391
- if (!isOpen() || !modalRef) return
392
-
393
- const cleanup = ariaHideOutside([modalRef])
394
- onCleanup(cleanup)
395
- })
396
-
397
- // Render props values
398
- const renderValues = createMemo<ModalRenderProps>(() => ({
399
- isEntering: local.isEntering ?? false,
400
- isExiting: local.isExiting ?? false,
401
- }))
402
-
403
- // Resolve render props
404
- const renderProps = useRenderProps(
405
- {
406
- children: props.children,
407
- class: local.class,
408
- style: local.style,
409
- defaultClassName: 'solidaria-Modal',
410
- },
411
- renderValues
412
- )
413
-
414
- // Filter DOM props
415
- const domProps = createMemo(() => filterDOMProps(rest as Record<string, unknown>, { global: true }))
416
-
417
- return (
418
- <FocusScope contain restoreFocus autoFocus>
419
- <div
420
- {...domProps()}
421
- ref={modalRef}
422
- class={renderProps.class()}
423
- style={renderProps.style()}
424
- data-entering={dataAttr(local.isEntering)}
425
- data-exiting={dataAttr(local.isExiting)}
426
- >
427
- {renderProps.renderChildren()}
428
- </div>
429
- </FocusScope>
430
- )
431
- }
432
-
433
- export default Modal