@proyecto-viviana/solidaria-components 0.2.5 → 0.3.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.
Files changed (225) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -272
  3. package/dist/ActionBar.d.ts +79 -0
  4. package/dist/ActionBar.d.ts.map +1 -0
  5. package/dist/ActionGroup.d.ts +74 -0
  6. package/dist/ActionGroup.d.ts.map +1 -0
  7. package/dist/Alert.d.ts +70 -0
  8. package/dist/Alert.d.ts.map +1 -0
  9. package/dist/Autocomplete.d.ts +5 -5
  10. package/dist/Autocomplete.d.ts.map +1 -1
  11. package/dist/Breadcrumbs.d.ts +27 -8
  12. package/dist/Breadcrumbs.d.ts.map +1 -1
  13. package/dist/Button.d.ts +28 -5
  14. package/dist/Button.d.ts.map +1 -1
  15. package/dist/Calendar.d.ts +51 -7
  16. package/dist/Calendar.d.ts.map +1 -1
  17. package/dist/Checkbox.d.ts +33 -8
  18. package/dist/Checkbox.d.ts.map +1 -1
  19. package/dist/Collection.d.ts +130 -0
  20. package/dist/Collection.d.ts.map +1 -0
  21. package/dist/Color.d.ts +210 -9
  22. package/dist/Color.d.ts.map +1 -1
  23. package/dist/ColorEditor.d.ts +42 -0
  24. package/dist/ColorEditor.d.ts.map +1 -0
  25. package/dist/ComboBox.d.ts +146 -16
  26. package/dist/ComboBox.d.ts.map +1 -1
  27. package/dist/ContextualHelpTrigger.d.ts +40 -0
  28. package/dist/ContextualHelpTrigger.d.ts.map +1 -0
  29. package/dist/DateField.d.ts +35 -8
  30. package/dist/DateField.d.ts.map +1 -1
  31. package/dist/DatePicker.d.ts +101 -5
  32. package/dist/DatePicker.d.ts.map +1 -1
  33. package/dist/DateRangePickerContext.d.ts +30 -0
  34. package/dist/DateRangePickerContext.d.ts.map +1 -0
  35. package/dist/Dialog.d.ts +5 -5
  36. package/dist/Dialog.d.ts.map +1 -1
  37. package/dist/Disclosure.d.ts +25 -5
  38. package/dist/Disclosure.d.ts.map +1 -1
  39. package/dist/DragAndDrop.d.ts +80 -0
  40. package/dist/DragAndDrop.d.ts.map +1 -0
  41. package/dist/DragPreview.d.ts +14 -0
  42. package/dist/DragPreview.d.ts.map +1 -0
  43. package/dist/DropZone.d.ts +27 -0
  44. package/dist/DropZone.d.ts.map +1 -0
  45. package/dist/FieldError.d.ts +27 -0
  46. package/dist/FieldError.d.ts.map +1 -0
  47. package/dist/FileTrigger.d.ts +26 -0
  48. package/dist/FileTrigger.d.ts.map +1 -0
  49. package/dist/Focusable.d.ts +27 -0
  50. package/dist/Focusable.d.ts.map +1 -0
  51. package/dist/Form.d.ts +41 -0
  52. package/dist/Form.d.ts.map +1 -0
  53. package/dist/GridList.d.ts +69 -10
  54. package/dist/GridList.d.ts.map +1 -1
  55. package/dist/HiddenDateInput.d.ts +26 -0
  56. package/dist/HiddenDateInput.d.ts.map +1 -0
  57. package/dist/HiddenTimeInput.d.ts +25 -0
  58. package/dist/HiddenTimeInput.d.ts.map +1 -0
  59. package/dist/Icon.d.ts +57 -0
  60. package/dist/Icon.d.ts.map +1 -0
  61. package/dist/Keyboard.d.ts +13 -0
  62. package/dist/Keyboard.d.ts.map +1 -0
  63. package/dist/Landmark.d.ts +3 -3
  64. package/dist/Landmark.d.ts.map +1 -1
  65. package/dist/Link.d.ts +10 -4
  66. package/dist/Link.d.ts.map +1 -1
  67. package/dist/ListBox.d.ts +73 -11
  68. package/dist/ListBox.d.ts.map +1 -1
  69. package/dist/ListDropTargetDelegate.d.ts +38 -0
  70. package/dist/ListDropTargetDelegate.d.ts.map +1 -0
  71. package/dist/Menu.d.ts +79 -10
  72. package/dist/Menu.d.ts.map +1 -1
  73. package/dist/Meter.d.ts +4 -4
  74. package/dist/Meter.d.ts.map +1 -1
  75. package/dist/Modal.d.ts +6 -4
  76. package/dist/Modal.d.ts.map +1 -1
  77. package/dist/NumberField.d.ts +10 -12
  78. package/dist/NumberField.d.ts.map +1 -1
  79. package/dist/Popover.d.ts +32 -7
  80. package/dist/Popover.d.ts.map +1 -1
  81. package/dist/Pressable.d.ts +27 -0
  82. package/dist/Pressable.d.ts.map +1 -0
  83. package/dist/ProgressBar.d.ts +6 -4
  84. package/dist/ProgressBar.d.ts.map +1 -1
  85. package/dist/RadioGroup.d.ts +43 -9
  86. package/dist/RadioGroup.d.ts.map +1 -1
  87. package/dist/RangeCalendar.d.ts +39 -7
  88. package/dist/RangeCalendar.d.ts.map +1 -1
  89. package/dist/RouterProvider.d.ts +75 -0
  90. package/dist/RouterProvider.d.ts.map +1 -0
  91. package/dist/SearchField.d.ts +23 -21
  92. package/dist/SearchField.d.ts.map +1 -1
  93. package/dist/Select.d.ts +48 -7
  94. package/dist/Select.d.ts.map +1 -1
  95. package/dist/SelectionIndicator.d.ts +30 -0
  96. package/dist/SelectionIndicator.d.ts.map +1 -0
  97. package/dist/Separator.d.ts +9 -3
  98. package/dist/Separator.d.ts.map +1 -1
  99. package/dist/SharedElementTransition.d.ts +41 -0
  100. package/dist/SharedElementTransition.d.ts.map +1 -0
  101. package/dist/Slider.d.ts +15 -8
  102. package/dist/Slider.d.ts.map +1 -1
  103. package/dist/StepList.d.ts +90 -0
  104. package/dist/StepList.d.ts.map +1 -0
  105. package/dist/Switch.d.ts +11 -5
  106. package/dist/Switch.d.ts.map +1 -1
  107. package/dist/Table.d.ts +222 -19
  108. package/dist/Table.d.ts.map +1 -1
  109. package/dist/Tabs.d.ts +47 -10
  110. package/dist/Tabs.d.ts.map +1 -1
  111. package/dist/TagGroup.d.ts +22 -10
  112. package/dist/TagGroup.d.ts.map +1 -1
  113. package/dist/Text.d.ts +10 -0
  114. package/dist/Text.d.ts.map +1 -0
  115. package/dist/TextField.d.ts +19 -11
  116. package/dist/TextField.d.ts.map +1 -1
  117. package/dist/TimeField.d.ts +32 -7
  118. package/dist/TimeField.d.ts.map +1 -1
  119. package/dist/Toast.d.ts +29 -14
  120. package/dist/Toast.d.ts.map +1 -1
  121. package/dist/ToggleButton.d.ts +36 -0
  122. package/dist/ToggleButton.d.ts.map +1 -0
  123. package/dist/ToggleButtonGroup.d.ts +33 -0
  124. package/dist/ToggleButtonGroup.d.ts.map +1 -0
  125. package/dist/Toolbar.d.ts +7 -3
  126. package/dist/Toolbar.d.ts.map +1 -1
  127. package/dist/Tooltip.d.ts +58 -7
  128. package/dist/Tooltip.d.ts.map +1 -1
  129. package/dist/Tree.d.ts +102 -11
  130. package/dist/Tree.d.ts.map +1 -1
  131. package/dist/Virtualizer.d.ts +61 -0
  132. package/dist/Virtualizer.d.ts.map +1 -0
  133. package/dist/VirtualizerLayouts.d.ts +82 -0
  134. package/dist/VirtualizerLayouts.d.ts.map +1 -0
  135. package/dist/VisuallyHidden.d.ts +4 -2
  136. package/dist/VisuallyHidden.d.ts.map +1 -1
  137. package/dist/contexts.d.ts +6 -1
  138. package/dist/contexts.d.ts.map +1 -1
  139. package/dist/index.d.ts +73 -39
  140. package/dist/index.d.ts.map +1 -1
  141. package/dist/index.js +23342 -10644
  142. package/dist/index.js.map +1 -7
  143. package/dist/index.jsx +18110 -0
  144. package/dist/index.jsx.map +1 -0
  145. package/dist/useDragAndDrop.d.ts +93 -0
  146. package/dist/useDragAndDrop.d.ts.map +1 -0
  147. package/dist/utils.d.ts +8 -2
  148. package/dist/utils.d.ts.map +1 -1
  149. package/dist/virtualizer/Layout.d.ts +79 -0
  150. package/dist/virtualizer/Layout.d.ts.map +1 -0
  151. package/package.json +33 -32
  152. package/src/ActionBar.tsx +251 -0
  153. package/src/ActionGroup.tsx +277 -0
  154. package/src/Alert.tsx +152 -0
  155. package/src/Autocomplete.tsx +39 -44
  156. package/src/Breadcrumbs.tsx +227 -72
  157. package/src/Button.tsx +315 -74
  158. package/src/Calendar.tsx +347 -141
  159. package/src/Checkbox.tsx +414 -123
  160. package/src/Collection.tsx +350 -0
  161. package/src/Color.tsx +1325 -284
  162. package/src/ColorEditor.tsx +213 -0
  163. package/src/ComboBox.tsx +644 -245
  164. package/src/ContextualHelpTrigger.tsx +195 -0
  165. package/src/DateField.tsx +274 -106
  166. package/src/DatePicker.tsx +892 -111
  167. package/src/DateRangePickerContext.tsx +44 -0
  168. package/src/Dialog.tsx +173 -104
  169. package/src/Disclosure.tsx +158 -105
  170. package/src/DragAndDrop.tsx +340 -0
  171. package/src/DragPreview.tsx +47 -0
  172. package/src/DropZone.tsx +233 -0
  173. package/src/FieldError.tsx +89 -0
  174. package/src/FileTrigger.tsx +83 -0
  175. package/src/Focusable.tsx +103 -0
  176. package/src/Form.tsx +140 -0
  177. package/src/GridList.tsx +542 -128
  178. package/src/HiddenDateInput.tsx +153 -0
  179. package/src/HiddenTimeInput.tsx +133 -0
  180. package/src/Icon.tsx +133 -0
  181. package/src/Keyboard.tsx +26 -0
  182. package/src/Landmark.tsx +37 -63
  183. package/src/Link.tsx +132 -69
  184. package/src/ListBox.tsx +656 -106
  185. package/src/ListDropTargetDelegate.ts +283 -0
  186. package/src/Menu.tsx +1234 -132
  187. package/src/Meter.tsx +44 -58
  188. package/src/Modal.tsx +262 -166
  189. package/src/NumberField.tsx +267 -151
  190. package/src/Popover.tsx +452 -343
  191. package/src/Pressable.tsx +108 -0
  192. package/src/ProgressBar.tsx +54 -59
  193. package/src/RadioGroup.tsx +533 -121
  194. package/src/RangeCalendar.tsx +249 -150
  195. package/src/RouterProvider.tsx +223 -0
  196. package/src/SearchField.tsx +460 -133
  197. package/src/Select.tsx +804 -233
  198. package/src/SelectionIndicator.tsx +108 -0
  199. package/src/Separator.tsx +47 -49
  200. package/src/SharedElementTransition.tsx +264 -0
  201. package/src/Slider.tsx +148 -98
  202. package/src/StepList.tsx +272 -0
  203. package/src/Switch.tsx +93 -46
  204. package/src/Table.tsx +1551 -225
  205. package/src/Tabs.tsx +377 -123
  206. package/src/TagGroup.tsx +233 -135
  207. package/src/Text.tsx +18 -0
  208. package/src/TextField.tsx +413 -86
  209. package/src/TimeField.tsx +232 -222
  210. package/src/Toast.tsx +306 -160
  211. package/src/ToggleButton.tsx +169 -0
  212. package/src/ToggleButtonGroup.tsx +141 -0
  213. package/src/Toolbar.tsx +61 -70
  214. package/src/Tooltip.tsx +473 -116
  215. package/src/Tree.tsx +1514 -175
  216. package/src/Virtualizer.tsx +730 -0
  217. package/src/VirtualizerLayouts.ts +280 -0
  218. package/src/VisuallyHidden.tsx +32 -38
  219. package/src/contexts.ts +29 -36
  220. package/src/index.ts +972 -620
  221. package/src/useDragAndDrop.ts +367 -0
  222. package/src/utils.tsx +69 -50
  223. package/src/virtualizer/Layout.ts +192 -0
  224. package/dist/index.ssr.js +0 -9785
  225. package/dist/index.ssr.js.map +0 -7
@@ -14,7 +14,7 @@ import {
14
14
  createSignal,
15
15
  splitProps,
16
16
  useContext,
17
- } from 'solid-js';
17
+ } from "solid-js";
18
18
  import {
19
19
  createDisclosureState,
20
20
  createDisclosureGroupState,
@@ -22,11 +22,13 @@ import {
22
22
  type DisclosureGroupState,
23
23
  type DisclosureStateProps,
24
24
  type DisclosureGroupStateProps,
25
- } from '@proyecto-viviana/solid-stately';
25
+ } from "@proyecto-viviana/solid-stately";
26
26
  import {
27
27
  createDisclosure,
28
28
  createDisclosureGroup,
29
- } from '@proyecto-viviana/solidaria';
29
+ createFocusRing,
30
+ mergeProps,
31
+ } from "@proyecto-viviana/solidaria";
30
32
  import {
31
33
  type RenderChildren,
32
34
  type ClassNameOrFunction,
@@ -34,17 +36,31 @@ import {
34
36
  useRenderProps,
35
37
  filterDOMProps,
36
38
  dataAttr,
37
- } from './utils';
39
+ } from "./utils";
38
40
 
39
- // ============================================
40
- // TYPES
41
- // ============================================
41
+ type RefLike<T> = T | ((el: T) => void) | { current?: T | null } | undefined;
42
+
43
+ function assignRef<T>(ref: RefLike<T>, el: T): void {
44
+ if (!ref) {
45
+ return;
46
+ }
47
+
48
+ if (typeof ref === "function") {
49
+ (ref as (el: T) => void)(el);
50
+ } else if (typeof ref === "object" && "current" in ref) {
51
+ ref.current = el;
52
+ }
53
+ }
42
54
 
43
55
  export interface DisclosureRenderProps {
44
56
  /** Whether the disclosure is expanded. */
45
57
  isExpanded: boolean;
58
+ /** Whether the disclosure has keyboard focus within. */
59
+ isFocusVisibleWithin: boolean;
46
60
  /** Whether the disclosure is disabled. */
47
61
  isDisabled: boolean;
62
+ /** The disclosure state. */
63
+ state: DisclosureState;
48
64
  }
49
65
 
50
66
  export interface DisclosureGroupRenderProps {
@@ -63,6 +79,8 @@ export interface DisclosureProps extends DisclosureStateProps {
63
79
  isDisabled?: boolean;
64
80
  /** A unique identifier for the disclosure (used in groups). */
65
81
  id?: string;
82
+ /** A ref for the disclosure root element. */
83
+ ref?: RefLike<HTMLDivElement>;
66
84
  }
67
85
 
68
86
  export interface DisclosureGroupProps extends DisclosureGroupStateProps {
@@ -72,30 +90,40 @@ export interface DisclosureGroupProps extends DisclosureGroupStateProps {
72
90
  class?: ClassNameOrFunction<DisclosureGroupRenderProps>;
73
91
  /** The inline style for the element. */
74
92
  style?: StyleOrFunction<DisclosureGroupRenderProps>;
93
+ /** A ref for the disclosure group root element. */
94
+ ref?: RefLike<HTMLDivElement>;
75
95
  }
76
96
 
77
- export interface DisclosureTriggerProps {
97
+ export interface DisclosureTriggerProps extends Omit<
98
+ JSX.ButtonHTMLAttributes<HTMLButtonElement>,
99
+ "children" | "class" | "style" | "type" | "ref"
100
+ > {
78
101
  /** The children of the trigger. */
79
102
  children?: JSX.Element;
80
103
  /** The CSS className for the element. */
81
104
  class?: string;
82
105
  /** The inline style for the element. */
83
106
  style?: JSX.CSSProperties;
107
+ /** A ref for the trigger button element. */
108
+ ref?: RefLike<HTMLButtonElement>;
84
109
  }
85
110
 
86
- export interface DisclosurePanelProps {
111
+ export interface DisclosurePanelProps extends Omit<
112
+ JSX.HTMLAttributes<HTMLElement>,
113
+ "children" | "class" | "style" | "role" | "ref"
114
+ > {
87
115
  /** The children of the panel. */
88
116
  children?: RenderChildren<DisclosureRenderProps>;
89
117
  /** The CSS className for the element. */
90
118
  class?: ClassNameOrFunction<DisclosureRenderProps>;
91
119
  /** The inline style for the element. */
92
120
  style?: StyleOrFunction<DisclosureRenderProps>;
121
+ /** The accessibility role for the disclosure panel. */
122
+ role?: "group" | "region";
123
+ /** A ref for the panel element. */
124
+ ref?: RefLike<HTMLDivElement>;
93
125
  }
94
126
 
95
- // ============================================
96
- // CONTEXT
97
- // ============================================
98
-
99
127
  interface DisclosureContextValue {
100
128
  state: DisclosureState;
101
129
  isDisabled: () => boolean;
@@ -103,10 +131,12 @@ interface DisclosureContextValue {
103
131
  disclosureAria: {
104
132
  readonly buttonProps: JSX.ButtonHTMLAttributes<HTMLButtonElement>;
105
133
  readonly panelProps: JSX.HTMLAttributes<HTMLElement>;
134
+ readonly isPressed: () => boolean;
106
135
  };
107
136
  }
108
137
 
109
138
  export const DisclosureContext = createContext<DisclosureContextValue | null>(null);
139
+ export const DisclosureStateContext = createContext<DisclosureState | null>(null);
110
140
 
111
141
  export function useDisclosureContext(): DisclosureContextValue | null {
112
142
  return useContext(DisclosureContext);
@@ -117,15 +147,12 @@ interface DisclosureGroupContextValue {
117
147
  }
118
148
 
119
149
  export const DisclosureGroupContext = createContext<DisclosureGroupContextValue | null>(null);
150
+ export const DisclosureGroupStateContext = createContext<DisclosureGroupState | null>(null);
120
151
 
121
152
  export function useDisclosureGroupContext(): DisclosureGroupContextValue | null {
122
153
  return useContext(DisclosureGroupContext);
123
154
  }
124
155
 
125
- // ============================================
126
- // DISCLOSURE GROUP (Accordion)
127
- // ============================================
128
-
129
156
  /**
130
157
  * DisclosureGroup manages a group of Disclosure components.
131
158
  * Use this to create an accordion where only one item can be expanded at a time.
@@ -150,31 +177,28 @@ export function DisclosureGroup(props: DisclosureGroupProps): JSX.Element {
150
177
  // the context provider renders causes them to evaluate outside the context.
151
178
  // See: https://github.com/solidjs/solid/issues/182
152
179
  const [local, rest] = splitProps(props, [
153
- 'class',
154
- 'style',
155
- 'allowsMultipleExpanded',
156
- 'isDisabled',
157
- 'expandedKeys',
158
- 'defaultExpandedKeys',
159
- 'onExpandedChange',
180
+ "class",
181
+ "style",
182
+ "allowsMultipleExpanded",
183
+ "isDisabled",
184
+ "expandedKeys",
185
+ "defaultExpandedKeys",
186
+ "onExpandedChange",
187
+ "ref",
160
188
  ]);
161
189
 
162
190
  // Create group state
163
- const state = createDisclosureGroupState({
191
+ const state = createDisclosureGroupState(() => ({
164
192
  allowsMultipleExpanded: local.allowsMultipleExpanded,
165
193
  isDisabled: local.isDisabled,
166
194
  expandedKeys: local.expandedKeys,
167
195
  defaultExpandedKeys: local.defaultExpandedKeys,
168
196
  onExpandedChange: local.onExpandedChange,
169
- });
197
+ }));
170
198
 
171
199
  // Create group accessibility props
172
- const { groupProps } = createDisclosureGroup(
173
- { isDisabled: local.isDisabled },
174
- state
175
- );
200
+ const { groupProps } = createDisclosureGroup(() => ({ isDisabled: local.isDisabled }), state);
176
201
 
177
- // Render props values
178
202
  const renderValues = createMemo<DisclosureGroupRenderProps>(() => ({
179
203
  isDisabled: state.isDisabled,
180
204
  }));
@@ -184,39 +208,38 @@ export function DisclosureGroup(props: DisclosureGroupProps): JSX.Element {
184
208
  {
185
209
  class: local.class,
186
210
  style: local.style,
187
- defaultClassName: 'solidaria-DisclosureGroup',
211
+ defaultClassName: "solidaria-DisclosureGroup",
188
212
  },
189
- renderValues
213
+ renderValues,
190
214
  );
191
215
 
192
- // Filter DOM props
193
- const domProps = createMemo(() => filterDOMProps(rest as Record<string, unknown>, { global: true }));
216
+ const domProps = createMemo(() =>
217
+ filterDOMProps(rest as Record<string, unknown>, { global: true }),
218
+ );
194
219
 
195
- // Context value
196
220
  const contextValue: DisclosureGroupContextValue = { state };
197
221
 
198
222
  // Extract ref from groupProps to avoid type conflicts
199
223
  const { ref: _ref, ...cleanGroupProps } = groupProps as Record<string, unknown>;
200
224
 
201
225
  return (
202
- <DisclosureGroupContext.Provider value={contextValue}>
203
- <div
204
- {...domProps()}
205
- {...cleanGroupProps}
206
- class={renderProps.class()}
207
- style={renderProps.style()}
208
- data-disabled={dataAttr(state.isDisabled)}
209
- >
210
- {props.children}
211
- </div>
212
- </DisclosureGroupContext.Provider>
226
+ <DisclosureGroupStateContext.Provider value={state}>
227
+ <DisclosureGroupContext.Provider value={contextValue}>
228
+ <div
229
+ ref={(el) => assignRef(local.ref, el)}
230
+ {...domProps()}
231
+ {...cleanGroupProps}
232
+ class={renderProps.class()}
233
+ style={renderProps.style()}
234
+ data-disabled={dataAttr(state.isDisabled)}
235
+ >
236
+ {props.children}
237
+ </div>
238
+ </DisclosureGroupContext.Provider>
239
+ </DisclosureGroupStateContext.Provider>
213
240
  );
214
241
  }
215
242
 
216
- // ============================================
217
- // DISCLOSURE
218
- // ============================================
219
-
220
243
  /**
221
244
  * Disclosure is a widget that can be toggled to show or hide content.
222
245
  *
@@ -234,13 +257,14 @@ export function Disclosure(props: DisclosureProps): JSX.Element {
234
257
  // the context provider renders causes them to evaluate outside the context.
235
258
  // See: https://github.com/solidjs/solid/issues/182
236
259
  const [local, rest] = splitProps(props, [
237
- 'class',
238
- 'style',
239
- 'isDisabled',
240
- 'isExpanded',
241
- 'defaultExpanded',
242
- 'onExpandedChange',
243
- 'id',
260
+ "class",
261
+ "style",
262
+ "isDisabled",
263
+ "isExpanded",
264
+ "defaultExpanded",
265
+ "onExpandedChange",
266
+ "id",
267
+ "ref",
244
268
  ]);
245
269
 
246
270
  // Check if we're inside a DisclosureGroup
@@ -280,13 +304,17 @@ export function Disclosure(props: DisclosureProps): JSX.Element {
280
304
  const disclosureAria = createDisclosure(
281
305
  () => ({ isDisabled: isDisabled() }),
282
306
  state,
283
- panelRef // Pass the accessor directly
307
+ panelRef, // Pass the accessor directly
284
308
  );
309
+ const { isFocusVisible: isFocusVisibleWithin, focusProps: focusWithinProps } = createFocusRing({
310
+ within: true,
311
+ });
285
312
 
286
- // Render props values
287
313
  const renderValues = createMemo<DisclosureRenderProps>(() => ({
288
314
  isExpanded: state.isExpanded(),
315
+ isFocusVisibleWithin: isFocusVisibleWithin(),
289
316
  isDisabled: isDisabled(),
317
+ state,
290
318
  }));
291
319
 
292
320
  // Resolve render props - don't pass children, we'll render props.children directly
@@ -294,50 +322,53 @@ export function Disclosure(props: DisclosureProps): JSX.Element {
294
322
  {
295
323
  class: local.class,
296
324
  style: local.style,
297
- defaultClassName: 'solidaria-Disclosure',
325
+ defaultClassName: "solidaria-Disclosure",
298
326
  },
299
- renderValues
327
+ renderValues,
300
328
  );
301
329
 
302
- // Filter DOM props
303
- const domProps = createMemo(() => filterDOMProps(rest as Record<string, unknown>, { global: true }));
330
+ const domProps = createMemo(() =>
331
+ filterDOMProps(rest as Record<string, unknown>, { global: true }),
332
+ );
304
333
 
305
334
  // Context value - pass the disclosureAria object with getters intact
306
335
  const contextValue: DisclosureContextValue = {
307
336
  state,
308
- isDisabled, // Pass the accessor function, not the value
337
+ isDisabled, // Pass the accessor function, not the value
309
338
  disclosureAria,
310
339
  };
311
340
 
312
- // Setter for panel ref
313
341
  const setPanelRef = (el: HTMLElement | null) => {
314
342
  setPanelRefSignal(el);
315
343
  };
316
344
 
317
345
  return (
318
- <DisclosureContext.Provider value={contextValue}>
319
- <DisclosurePanelRefContext.Provider value={setPanelRef}>
320
- <div
321
- {...domProps()}
322
- class={renderProps.class()}
323
- style={renderProps.style()}
324
- data-expanded={dataAttr(state.isExpanded())}
325
- data-disabled={dataAttr(isDisabled())}
326
- >
327
- {props.children}
328
- </div>
329
- </DisclosurePanelRefContext.Provider>
330
- </DisclosureContext.Provider>
346
+ <DisclosureStateContext.Provider value={state}>
347
+ <DisclosureContext.Provider value={contextValue}>
348
+ <DisclosurePanelRefContext.Provider value={setPanelRef}>
349
+ <div
350
+ ref={(el) => assignRef(local.ref, el)}
351
+ {...mergeProps(
352
+ domProps() as Record<string, unknown>,
353
+ focusWithinProps as Record<string, unknown>,
354
+ )}
355
+ class={renderProps.class()}
356
+ style={renderProps.style()}
357
+ data-expanded={dataAttr(state.isExpanded())}
358
+ data-disabled={dataAttr(isDisabled())}
359
+ data-focus-visible-within={dataAttr(isFocusVisibleWithin())}
360
+ >
361
+ {props.children}
362
+ </div>
363
+ </DisclosurePanelRefContext.Provider>
364
+ </DisclosureContext.Provider>
365
+ </DisclosureStateContext.Provider>
331
366
  );
332
367
  }
333
368
 
334
369
  // Internal context to pass panel ref setter
335
370
  const DisclosurePanelRefContext = createContext<((el: HTMLElement | null) => void) | null>(null);
336
371
 
337
- // ============================================
338
- // DISCLOSURE TRIGGER
339
- // ============================================
340
-
341
372
  /**
342
373
  * DisclosureTrigger is the button that toggles the disclosure.
343
374
  * Pattern matches SelectTrigger for consistency.
@@ -346,7 +377,7 @@ export function DisclosureTrigger(props: DisclosureTriggerProps): JSX.Element {
346
377
  // Get context - now safe because parent uses lazy children evaluation
347
378
  const context = useContext(DisclosureContext);
348
379
  if (!context) {
349
- throw new Error('DisclosureTrigger must be used within a Disclosure');
380
+ throw new Error("DisclosureTrigger must be used within a Disclosure");
350
381
  }
351
382
 
352
383
  const { state, disclosureAria, isDisabled } = context;
@@ -360,80 +391,102 @@ export function DisclosureTrigger(props: DisclosureTriggerProps): JSX.Element {
360
391
  const { ref: _ref, ...rest } = disclosureAria.buttonProps as Record<string, unknown>;
361
392
  return rest;
362
393
  };
394
+ const { isFocused, isFocusVisible, focusProps } = createFocusRing();
395
+ const [local, rest] = splitProps(props, ["children", "class", "style", "ref"]);
396
+ const domProps = createMemo(() =>
397
+ filterDOMProps(rest as Record<string, unknown>, { global: true }),
398
+ );
399
+ const cleanFocusProps = () => {
400
+ const { ref: _ref, ...rest } = focusProps as Record<string, unknown>;
401
+ return rest;
402
+ };
363
403
 
364
404
  return (
365
405
  <button
366
- {...getButtonProps()}
406
+ {...mergeProps(domProps() as Record<string, unknown>, getButtonProps(), cleanFocusProps())}
407
+ ref={(el) => assignRef(local.ref, el)}
367
408
  type="button"
368
- class={props.class}
369
- style={props.style}
409
+ class={local.class}
410
+ style={local.style}
370
411
  data-expanded={dataAttr(isExpanded())}
371
412
  data-disabled={dataAttr(isDisabled())}
413
+ data-pressed={dataAttr(disclosureAria.isPressed())}
414
+ data-focused={dataAttr(isFocused())}
415
+ data-focus-visible={dataAttr(isFocusVisible())}
372
416
  >
373
- {props.children}
417
+ {local.children}
374
418
  </button>
375
419
  );
376
420
  }
377
421
 
378
- // ============================================
379
- // DISCLOSURE PANEL
380
- // ============================================
381
-
382
422
  /**
383
423
  * DisclosurePanel contains the content that is shown/hidden.
384
424
  */
385
425
  export function DisclosurePanel(props: DisclosurePanelProps): JSX.Element {
386
426
  // Get context - now safe because parent uses lazy children evaluation
387
427
  const context = useContext(DisclosureContext);
428
+ if (!context) {
429
+ throw new Error("DisclosurePanel must be used within a Disclosure");
430
+ }
388
431
  const panelRefSetter = useContext(DisclosurePanelRefContext);
389
432
 
390
- const [local, rest] = splitProps(props, ['class', 'style']);
433
+ const [local, rest] = splitProps(props, ["class", "style", "role", "ref"]);
434
+ const { isFocusVisible: isFocusVisibleWithin, focusProps: focusWithinProps } = createFocusRing({
435
+ within: true,
436
+ });
391
437
 
392
438
  // Reactive accessors
393
- const isExpanded = () => context?.state.isExpanded() ?? false;
394
- const isDisabled = () => context?.isDisabled() ?? false;
439
+ const isExpanded = () => context.state.isExpanded();
440
+ const isDisabled = () => context.isDisabled();
395
441
 
396
- // Render props values
397
442
  const renderValues = createMemo<DisclosureRenderProps>(() => ({
398
443
  isExpanded: isExpanded(),
444
+ isFocusVisibleWithin: isFocusVisibleWithin(),
399
445
  isDisabled: isDisabled(),
446
+ state: context.state,
400
447
  }));
401
448
 
402
- // Resolve render props
403
449
  const renderProps = useRenderProps(
404
450
  {
405
451
  children: props.children,
406
452
  class: local.class,
407
453
  style: local.style,
408
- defaultClassName: 'solidaria-DisclosurePanel',
454
+ defaultClassName: "solidaria-DisclosurePanel",
409
455
  },
410
- renderValues
456
+ renderValues,
411
457
  );
412
458
 
413
- // Filter DOM props
414
- const domProps = createMemo(() => filterDOMProps(rest as Record<string, unknown>, { global: true }));
459
+ const domProps = createMemo(() =>
460
+ filterDOMProps(rest as Record<string, unknown>, { global: true }),
461
+ );
415
462
 
416
463
  // Get panelProps from the getter each time - this ensures reactivity
417
464
  // IMPORTANT: Call the getter fresh each render to get updated hidden attribute, etc.
418
465
  const getPanelProps = () => {
419
- if (!context) return { id: undefined, role: 'region', 'aria-labelledby': undefined, hidden: true };
420
466
  const { ref: _ref, ...rest } = context.disclosureAria.panelProps as Record<string, unknown>;
421
467
  return rest;
422
468
  };
423
469
 
424
470
  return (
425
471
  <div
426
- {...domProps()}
472
+ {...mergeProps(
473
+ domProps() as Record<string, unknown>,
474
+ focusWithinProps as Record<string, unknown>,
475
+ )}
427
476
  {...getPanelProps()}
428
- ref={(el) => panelRefSetter?.(el)}
477
+ ref={(el) => {
478
+ panelRefSetter?.(el);
479
+ assignRef(local.ref, el);
480
+ }}
481
+ role={local.role ?? "group"}
429
482
  class={renderProps.class()}
430
483
  style={renderProps.style()}
431
484
  data-expanded={dataAttr(isExpanded())}
485
+ data-focus-visible-within={dataAttr(isFocusVisibleWithin())}
432
486
  >
433
487
  {renderProps.renderChildren()}
434
488
  </div>
435
489
  );
436
490
  }
437
491
 
438
- // Re-export state types for convenience
439
492
  export type { DisclosureState, DisclosureGroupState };