@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
@@ -0,0 +1,44 @@
1
+ import { createContext, useContext } from "solid-js";
2
+ import { type createDateRangePicker } from "@proyecto-viviana/solidaria";
3
+ import {
4
+ type DateFieldState,
5
+ type DateValue,
6
+ type RangeCalendarState,
7
+ } from "@proyecto-viviana/solid-stately";
8
+
9
+ export interface DateRangePickerFieldContextValue {
10
+ state: DateFieldState<DateValue>;
11
+ aria: {
12
+ labelProps: Record<string, unknown>;
13
+ inputProps: Record<string, unknown>;
14
+ descriptionProps: Record<string, unknown>;
15
+ errorMessageProps: Record<string, unknown>;
16
+ };
17
+ }
18
+
19
+ export interface DateRangePickerContextValue {
20
+ calendarState: RangeCalendarState<DateValue>;
21
+ startFieldState: DateFieldState<DateValue>;
22
+ endFieldState: DateFieldState<DateValue>;
23
+ startFieldContext: DateRangePickerFieldContextValue;
24
+ endFieldContext: DateRangePickerFieldContextValue;
25
+ overlayState: {
26
+ isOpen: boolean;
27
+ open: () => void;
28
+ close: () => void;
29
+ toggle: () => void;
30
+ };
31
+ triggerRef: () => HTMLElement | null;
32
+ setTriggerRef: (element: HTMLElement | null) => void;
33
+ pickerAria: ReturnType<typeof createDateRangePicker>;
34
+ }
35
+
36
+ export const DateRangePickerContext = createContext<DateRangePickerContextValue | null>(null);
37
+
38
+ export function useDateRangePickerContext(): DateRangePickerContextValue {
39
+ const context = useContext(DateRangePickerContext);
40
+ if (!context) {
41
+ throw new Error("DateRangePicker components must be used within a DateRangePicker");
42
+ }
43
+ return context;
44
+ }
package/src/Dialog.tsx CHANGED
@@ -8,20 +8,25 @@
8
8
  import {
9
9
  type JSX,
10
10
  createContext,
11
+ createEffect,
11
12
  createMemo,
12
13
  createUniqueId,
13
14
  splitProps,
14
15
  useContext,
15
16
  Switch,
16
17
  Match,
17
- } from 'solid-js'
18
+ } from "solid-js";
18
19
  import {
19
20
  createDialog,
20
21
  createOverlayTrigger,
22
+ focusSafely,
21
23
  type AriaDialogProps,
22
- } from '@proyecto-viviana/solidaria'
23
- import { createOverlayTriggerState } from '@proyecto-viviana/solid-stately'
24
- import { DialogTriggerContext, useOverlayTriggerState } from './contexts'
24
+ } from "@proyecto-viviana/solidaria";
25
+ import {
26
+ createOverlayTriggerState,
27
+ type OverlayTriggerState,
28
+ } from "@proyecto-viviana/solid-stately";
29
+ import { DialogTriggerContext, useOverlayTriggerState } from "./contexts";
25
30
  import {
26
31
  type RenderChildren,
27
32
  type ClassNameOrFunction,
@@ -29,171 +34,206 @@ import {
29
34
  type SlotProps,
30
35
  useRenderProps,
31
36
  filterDOMProps,
32
- } from './utils'
33
-
34
- // ============================================
35
- // TYPES
36
- // ============================================
37
+ } from "./utils";
37
38
 
38
39
  export interface DialogRenderProps {
39
40
  /** Function to close the dialog */
40
- close: () => void
41
+ close: () => void;
41
42
  }
42
43
 
43
- export interface DialogProps extends Omit<AriaDialogProps, 'class' | 'style'>, SlotProps {
44
+ export interface DialogProps extends Omit<AriaDialogProps, "class" | "style">, SlotProps {
44
45
  /** The children of the component - can be JSX or render function. */
45
- children?: RenderChildren<DialogRenderProps>
46
+ children?: RenderChildren<DialogRenderProps>;
46
47
  /** The CSS className for the element. */
47
- class?: ClassNameOrFunction<DialogRenderProps>
48
+ class?: ClassNameOrFunction<DialogRenderProps>;
48
49
  /** The inline style for the element. */
49
- style?: StyleOrFunction<DialogRenderProps>
50
+ style?: StyleOrFunction<DialogRenderProps>;
50
51
  /** Callback when dialog should close */
51
- onClose?: () => void
52
+ onClose?: () => void;
52
53
  }
53
54
 
54
55
  export interface DialogTriggerProps {
55
56
  /** The children - should include a trigger and modal/popover content. */
56
- children: JSX.Element
57
+ children: JSX.Element;
57
58
  /** Whether the dialog is open (controlled). */
58
- isOpen?: boolean
59
+ isOpen?: boolean;
59
60
  /** Whether the dialog is open by default (uncontrolled). */
60
- defaultOpen?: boolean
61
+ defaultOpen?: boolean;
61
62
  /** Callback when open state changes. */
62
- onOpenChange?: (isOpen: boolean) => void
63
+ onOpenChange?: (isOpen: boolean) => void;
63
64
  }
64
65
 
65
- // ============================================
66
- // CONTEXTS
67
- // ============================================
68
-
69
66
  interface DialogContextValue {
70
- close: () => void
71
- titleId?: string
67
+ close: () => void;
68
+ titleId?: string;
72
69
  }
73
70
 
74
- export const DialogContext = createContext<DialogContextValue | null>(null)
71
+ export const DialogContext = createContext<DialogContextValue | null>(null);
75
72
 
76
- // Re-export DialogTriggerContext from shared contexts (also imported above for local use)
77
- export { DialogTriggerContext, useDialogTrigger } from './contexts'
78
-
79
- // ============================================
80
- // DIALOG TRIGGER COMPONENT
81
- // ============================================
73
+ export { DialogTriggerContext, useDialogTrigger } from "./contexts";
82
74
 
83
75
  /**
84
76
  * A DialogTrigger opens a dialog when a trigger element is pressed.
85
77
  * Children should include a trigger element (e.g. Button) and the dialog content.
86
78
  */
87
79
  export function DialogTrigger(props: DialogTriggerProps): JSX.Element {
88
- const [local] = splitProps(props, ['isOpen', 'defaultOpen', 'onOpenChange'])
80
+ const [local] = splitProps(props, ["isOpen", "defaultOpen", "onOpenChange"]);
89
81
 
90
- // Create overlay trigger state
91
82
  const state = createOverlayTriggerState({
92
83
  get isOpen() {
93
- return local.isOpen
84
+ return local.isOpen;
94
85
  },
95
86
  get defaultOpen() {
96
- return local.defaultOpen
87
+ return local.defaultOpen;
97
88
  },
98
89
  onOpenChange: local.onOpenChange,
99
- })
100
-
101
- // Ref for the trigger element
102
- let triggerRef: HTMLElement | null = null
103
- const triggerId = createUniqueId()
90
+ });
91
+
92
+ let triggerRef: HTMLElement | null = null;
93
+ const triggerId = createUniqueId();
94
+
95
+ // Create overlay trigger props so registered trigger components can expose
96
+ // the same expanded/controls relationship as React Aria DialogTrigger.
97
+ const triggerAria = createOverlayTrigger({ type: "dialog" }, state, () => triggerRef);
98
+
99
+ const restoreFocusToTrigger = () => {
100
+ const trigger = triggerRef;
101
+ if (!trigger?.isConnected) return;
102
+
103
+ const win = trigger.ownerDocument.defaultView ?? window;
104
+ win.requestAnimationFrame(() => {
105
+ win.requestAnimationFrame(() => {
106
+ if (trigger.isConnected && !state.isOpen()) {
107
+ focusSafely(trigger);
108
+ }
109
+ });
110
+ });
111
+ };
112
+
113
+ const stateWithFocusRestore: OverlayTriggerState = {
114
+ isOpen: state.isOpen,
115
+ setOpen: (isOpen) => {
116
+ state.setOpen(isOpen);
117
+ if (!isOpen) {
118
+ restoreFocusToTrigger();
119
+ }
120
+ },
121
+ open: state.open,
122
+ close: () => {
123
+ state.close();
124
+ restoreFocusToTrigger();
125
+ },
126
+ toggle: () => {
127
+ const wasOpen = state.isOpen();
128
+ state.toggle();
129
+ if (wasOpen) {
130
+ restoreFocusToTrigger();
131
+ }
132
+ },
133
+ };
104
134
 
105
- // Create overlay trigger props (used via context, not directly applied)
106
- createOverlayTrigger(
107
- { type: 'dialog' },
108
- state,
109
- () => triggerRef
110
- )
135
+ const setTriggerRef = (el: HTMLElement | null) => {
136
+ if (!el) return;
137
+ if (!triggerRef || !triggerRef.isConnected) {
138
+ triggerRef = el;
139
+ }
140
+ };
111
141
 
112
142
  // Context value - memoized to avoid unnecessary re-renders
113
143
  const contextValue = createMemo(() => ({
114
- state,
144
+ state: stateWithFocusRestore,
115
145
  triggerRef: () => triggerRef,
146
+ setTriggerRef,
116
147
  triggerId,
117
- }))
148
+ triggerProps: triggerAria.triggerProps,
149
+ overlayProps: triggerAria.overlayProps,
150
+ }));
118
151
 
119
152
  // In SolidJS, we simply render children directly within the provider
120
- // The children will have access to the context
121
153
  return (
122
154
  <DialogTriggerContext.Provider value={contextValue()}>
123
155
  {props.children}
124
156
  </DialogTriggerContext.Provider>
125
- )
157
+ );
126
158
  }
127
159
 
128
- // ============================================
129
- // DIALOG COMPONENT
130
- // ============================================
131
-
132
160
  /**
133
161
  * A dialog is an overlay shown above other content in an application.
134
162
  */
135
163
  export function Dialog(props: DialogProps): JSX.Element {
136
164
  const [local, ariaProps, rest] = splitProps(
137
165
  props,
138
- ['class', 'style', 'slot', 'onClose'],
139
- ['role', 'aria-label', 'aria-labelledby', 'aria-describedby']
140
- )
166
+ ["class", "style", "slot", "onClose"],
167
+ ["role", "aria-label", "aria-labelledby", "aria-describedby"],
168
+ );
141
169
 
142
- let dialogRef!: HTMLDivElement
170
+ let dialogRef!: HTMLDivElement;
143
171
 
144
172
  // Get trigger context for aria-labelledby fallback
145
- const triggerContext = useContext(DialogTriggerContext)
173
+ const triggerContext = useContext(DialogTriggerContext);
146
174
 
147
175
  // createDialog returns dialogProps AND titleProps (with the id for the Heading)
148
176
  const { dialogProps, titleProps } = createDialog(
149
177
  {
150
178
  get role() {
151
- return ariaProps.role
179
+ return ariaProps.role;
152
180
  },
153
- get 'aria-label'() {
154
- return ariaProps['aria-label']
181
+ get "aria-label"() {
182
+ return ariaProps["aria-label"];
155
183
  },
156
- get 'aria-labelledby'() {
157
- // Use provided labelledby, or fall back to trigger id if no title
158
- return ariaProps['aria-labelledby'] ?? triggerContext?.triggerId
184
+ get "aria-labelledby"() {
185
+ return ariaProps["aria-labelledby"];
159
186
  },
160
- get 'aria-describedby'() {
161
- return ariaProps['aria-describedby']
187
+ get "aria-describedby"() {
188
+ return ariaProps["aria-describedby"];
162
189
  },
163
190
  },
164
- () => dialogRef
165
- )
191
+ () => dialogRef,
192
+ );
166
193
 
167
194
  // Get titleId from titleProps - this links Dialog's aria-labelledby to Heading's id
168
- const titleId = () => titleProps()?.id
195
+ const titleId = () => titleProps()?.id as string | undefined;
169
196
 
170
197
  // Get close function from OverlayTriggerState context or onClose prop
171
- const overlayState = useOverlayTriggerState()
198
+ const overlayState = useOverlayTriggerState();
172
199
 
173
200
  const close = () => {
174
- local.onClose?.()
175
- overlayState?.close()
176
- triggerContext?.state.close()
177
- }
201
+ local.onClose?.();
202
+ if (overlayState) {
203
+ overlayState.close();
204
+ return;
205
+ }
206
+ triggerContext?.state.close();
207
+ };
208
+
209
+ createEffect(() => {
210
+ if (!dialogRef || ariaProps["aria-label"] || ariaProps["aria-labelledby"]) return;
211
+ const labelledBy = dialogRef.getAttribute("aria-labelledby");
212
+ if (labelledBy && dialogRef.ownerDocument.getElementById(labelledBy)) return;
213
+
214
+ const trigger = triggerContext?.triggerRef();
215
+ if (trigger?.id) {
216
+ dialogRef.setAttribute("aria-labelledby", trigger.id);
217
+ }
218
+ });
178
219
 
179
- // Render props values
180
220
  const renderValues = createMemo<DialogRenderProps>(() => ({
181
221
  close,
182
- }))
222
+ }));
183
223
 
184
- // Resolve render props
185
224
  const renderProps = useRenderProps(
186
225
  {
187
226
  children: props.children,
188
227
  class: local.class,
189
228
  style: local.style,
190
- defaultClassName: 'solidaria-Dialog',
229
+ defaultClassName: "solidaria-Dialog",
191
230
  },
192
- renderValues
193
- )
231
+ renderValues,
232
+ );
194
233
 
195
- // Filter DOM props
196
- const domProps = createMemo(() => filterDOMProps(rest as Record<string, unknown>, { global: true }))
234
+ const domProps = createMemo(() =>
235
+ filterDOMProps(rest as Record<string, unknown>, { global: true }),
236
+ );
197
237
 
198
238
  return (
199
239
  <DialogContext.Provider value={{ close, titleId: titleId() }}>
@@ -203,26 +243,23 @@ export function Dialog(props: DialogProps): JSX.Element {
203
243
  ref={dialogRef}
204
244
  class={renderProps.class()}
205
245
  style={renderProps.style()}
246
+ slot={local.slot}
206
247
  >
207
248
  {renderProps.renderChildren()}
208
249
  </div>
209
250
  </DialogContext.Provider>
210
- )
251
+ );
211
252
  }
212
253
 
213
- // ============================================
214
- // HEADING COMPONENT
215
- // ============================================
216
-
217
254
  export interface HeadingProps {
218
255
  /** The children of the heading. */
219
- children: JSX.Element
256
+ children: JSX.Element;
220
257
  /** The CSS className. */
221
- class?: string
258
+ class?: string;
222
259
  /** The heading level (1-6). Defaults to 2. */
223
- level?: 1 | 2 | 3 | 4 | 5 | 6
260
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
224
261
  /** The slot to render into. */
225
- slot?: string
262
+ slot?: string;
226
263
  }
227
264
 
228
265
  /**
@@ -230,33 +267,65 @@ export interface HeadingProps {
230
267
  * When rendered inside a Dialog, automatically gets the titleProps.
231
268
  */
232
269
  export function Heading(props: HeadingProps): JSX.Element {
233
- const dialogContext = useContext(DialogContext)
234
- const level = () => props.level ?? 2
235
- const id = () => dialogContext?.titleId
270
+ const dialogContext = useContext(DialogContext);
271
+ const level = () => props.level ?? 2;
272
+ const id = () => dialogContext?.titleId;
273
+ let headingRef: HTMLHeadingElement | undefined;
274
+
275
+ createEffect(() => {
276
+ const el = headingRef;
277
+ if (!el) return;
278
+
279
+ const contextId = id();
280
+ if (contextId) {
281
+ el.id = contextId;
282
+ return;
283
+ }
284
+
285
+ if (!el.id) {
286
+ const dialog = el.closest('[role="dialog"],[role="alertdialog"]');
287
+ const labelledBy = dialog?.getAttribute("aria-labelledby");
288
+ if (labelledBy && !el.ownerDocument.getElementById(labelledBy)) {
289
+ el.id = labelledBy;
290
+ }
291
+ }
292
+ });
236
293
 
237
294
  return (
238
295
  <Switch>
239
296
  <Match when={level() === 1}>
240
- <h1 id={id()} class={props.class}>{props.children}</h1>
297
+ <h1 ref={headingRef} id={id()} class={props.class}>
298
+ {props.children}
299
+ </h1>
241
300
  </Match>
242
301
  <Match when={level() === 2}>
243
- <h2 id={id()} class={props.class}>{props.children}</h2>
302
+ <h2 ref={headingRef} id={id()} class={props.class}>
303
+ {props.children}
304
+ </h2>
244
305
  </Match>
245
306
  <Match when={level() === 3}>
246
- <h3 id={id()} class={props.class}>{props.children}</h3>
307
+ <h3 ref={headingRef} id={id()} class={props.class}>
308
+ {props.children}
309
+ </h3>
247
310
  </Match>
248
311
  <Match when={level() === 4}>
249
- <h4 id={id()} class={props.class}>{props.children}</h4>
312
+ <h4 ref={headingRef} id={id()} class={props.class}>
313
+ {props.children}
314
+ </h4>
250
315
  </Match>
251
316
  <Match when={level() === 5}>
252
- <h5 id={id()} class={props.class}>{props.children}</h5>
317
+ <h5 ref={headingRef} id={id()} class={props.class}>
318
+ {props.children}
319
+ </h5>
253
320
  </Match>
254
321
  <Match when={level() === 6}>
255
- <h6 id={id()} class={props.class}>{props.children}</h6>
322
+ <h6 ref={headingRef} id={id()} class={props.class}>
323
+ {props.children}
324
+ </h6>
256
325
  </Match>
257
326
  </Switch>
258
- )
327
+ );
259
328
  }
260
329
 
261
330
  // Keep backward compatibility
262
- export { Heading as DialogHeading }
331
+ export { Heading as DialogHeading };