@proyecto-viviana/solidaria-components 0.2.9 → 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 (222) hide show
  1. package/README.md +39 -272
  2. package/dist/ActionBar.d.ts +21 -13
  3. package/dist/ActionBar.d.ts.map +1 -1
  4. package/dist/ActionGroup.d.ts +8 -8
  5. package/dist/ActionGroup.d.ts.map +1 -1
  6. package/dist/Alert.d.ts +5 -5
  7. package/dist/Alert.d.ts.map +1 -1
  8. package/dist/Autocomplete.d.ts +5 -5
  9. package/dist/Autocomplete.d.ts.map +1 -1
  10. package/dist/Breadcrumbs.d.ts +18 -7
  11. package/dist/Breadcrumbs.d.ts.map +1 -1
  12. package/dist/Button.d.ts +24 -5
  13. package/dist/Button.d.ts.map +1 -1
  14. package/dist/Calendar.d.ts +38 -7
  15. package/dist/Calendar.d.ts.map +1 -1
  16. package/dist/Checkbox.d.ts +32 -7
  17. package/dist/Checkbox.d.ts.map +1 -1
  18. package/dist/Collection.d.ts +19 -14
  19. package/dist/Collection.d.ts.map +1 -1
  20. package/dist/Color.d.ts +103 -14
  21. package/dist/Color.d.ts.map +1 -1
  22. package/dist/ColorEditor.d.ts +6 -6
  23. package/dist/ColorEditor.d.ts.map +1 -1
  24. package/dist/ComboBox.d.ts +85 -19
  25. package/dist/ComboBox.d.ts.map +1 -1
  26. package/dist/ContextualHelpTrigger.d.ts +2 -2
  27. package/dist/ContextualHelpTrigger.d.ts.map +1 -1
  28. package/dist/DateField.d.ts +8 -6
  29. package/dist/DateField.d.ts.map +1 -1
  30. package/dist/DatePicker.d.ts +53 -22
  31. package/dist/DatePicker.d.ts.map +1 -1
  32. package/dist/DateRangePickerContext.d.ts +30 -0
  33. package/dist/DateRangePickerContext.d.ts.map +1 -0
  34. package/dist/Dialog.d.ts +5 -5
  35. package/dist/Dialog.d.ts.map +1 -1
  36. package/dist/Disclosure.d.ts +23 -5
  37. package/dist/Disclosure.d.ts.map +1 -1
  38. package/dist/DragAndDrop.d.ts +6 -6
  39. package/dist/DragAndDrop.d.ts.map +1 -1
  40. package/dist/DragPreview.d.ts +2 -2
  41. package/dist/DragPreview.d.ts.map +1 -1
  42. package/dist/DropZone.d.ts +4 -4
  43. package/dist/DropZone.d.ts.map +1 -1
  44. package/dist/FieldError.d.ts +9 -5
  45. package/dist/FieldError.d.ts.map +1 -1
  46. package/dist/FileTrigger.d.ts +3 -3
  47. package/dist/FileTrigger.d.ts.map +1 -1
  48. package/dist/Focusable.d.ts +2 -2
  49. package/dist/Focusable.d.ts.map +1 -1
  50. package/dist/Form.d.ts +18 -4
  51. package/dist/Form.d.ts.map +1 -1
  52. package/dist/GridList.d.ts +32 -12
  53. package/dist/GridList.d.ts.map +1 -1
  54. package/dist/HiddenDateInput.d.ts +26 -0
  55. package/dist/HiddenDateInput.d.ts.map +1 -0
  56. package/dist/HiddenTimeInput.d.ts +25 -0
  57. package/dist/HiddenTimeInput.d.ts.map +1 -0
  58. package/dist/Icon.d.ts +5 -5
  59. package/dist/Icon.d.ts.map +1 -1
  60. package/dist/Keyboard.d.ts +1 -1
  61. package/dist/Landmark.d.ts +3 -3
  62. package/dist/Landmark.d.ts.map +1 -1
  63. package/dist/Link.d.ts +10 -4
  64. package/dist/Link.d.ts.map +1 -1
  65. package/dist/ListBox.d.ts +32 -12
  66. package/dist/ListBox.d.ts.map +1 -1
  67. package/dist/ListDropTargetDelegate.d.ts +6 -6
  68. package/dist/ListDropTargetDelegate.d.ts.map +1 -1
  69. package/dist/Menu.d.ts +65 -14
  70. package/dist/Menu.d.ts.map +1 -1
  71. package/dist/Meter.d.ts +3 -3
  72. package/dist/Meter.d.ts.map +1 -1
  73. package/dist/Modal.d.ts +5 -5
  74. package/dist/Modal.d.ts.map +1 -1
  75. package/dist/NumberField.d.ts +8 -12
  76. package/dist/NumberField.d.ts.map +1 -1
  77. package/dist/Popover.d.ts +28 -5
  78. package/dist/Popover.d.ts.map +1 -1
  79. package/dist/Pressable.d.ts +2 -2
  80. package/dist/Pressable.d.ts.map +1 -1
  81. package/dist/ProgressBar.d.ts +5 -3
  82. package/dist/ProgressBar.d.ts.map +1 -1
  83. package/dist/RadioGroup.d.ts +43 -9
  84. package/dist/RadioGroup.d.ts.map +1 -1
  85. package/dist/RangeCalendar.d.ts +34 -7
  86. package/dist/RangeCalendar.d.ts.map +1 -1
  87. package/dist/RouterProvider.d.ts +2 -2
  88. package/dist/RouterProvider.d.ts.map +1 -1
  89. package/dist/SearchField.d.ts +23 -20
  90. package/dist/SearchField.d.ts.map +1 -1
  91. package/dist/Select.d.ts +41 -11
  92. package/dist/Select.d.ts.map +1 -1
  93. package/dist/SelectionIndicator.d.ts +3 -3
  94. package/dist/SelectionIndicator.d.ts.map +1 -1
  95. package/dist/Separator.d.ts +9 -3
  96. package/dist/Separator.d.ts.map +1 -1
  97. package/dist/SharedElementTransition.d.ts +6 -4
  98. package/dist/SharedElementTransition.d.ts.map +1 -1
  99. package/dist/Slider.d.ts +12 -8
  100. package/dist/Slider.d.ts.map +1 -1
  101. package/dist/StepList.d.ts +90 -0
  102. package/dist/StepList.d.ts.map +1 -0
  103. package/dist/Switch.d.ts +11 -5
  104. package/dist/Switch.d.ts.map +1 -1
  105. package/dist/Table.d.ts +187 -23
  106. package/dist/Table.d.ts.map +1 -1
  107. package/dist/Tabs.d.ts +45 -9
  108. package/dist/Tabs.d.ts.map +1 -1
  109. package/dist/TagGroup.d.ts +12 -10
  110. package/dist/TagGroup.d.ts.map +1 -1
  111. package/dist/Text.d.ts +2 -2
  112. package/dist/TextField.d.ts +15 -11
  113. package/dist/TextField.d.ts.map +1 -1
  114. package/dist/TimeField.d.ts +6 -6
  115. package/dist/TimeField.d.ts.map +1 -1
  116. package/dist/Toast.d.ts +29 -14
  117. package/dist/Toast.d.ts.map +1 -1
  118. package/dist/ToggleButton.d.ts +11 -5
  119. package/dist/ToggleButton.d.ts.map +1 -1
  120. package/dist/ToggleButtonGroup.d.ts +7 -7
  121. package/dist/ToggleButtonGroup.d.ts.map +1 -1
  122. package/dist/Toolbar.d.ts +7 -3
  123. package/dist/Toolbar.d.ts.map +1 -1
  124. package/dist/Tooltip.d.ts +50 -8
  125. package/dist/Tooltip.d.ts.map +1 -1
  126. package/dist/Tree.d.ts +66 -17
  127. package/dist/Tree.d.ts.map +1 -1
  128. package/dist/Virtualizer.d.ts +12 -12
  129. package/dist/Virtualizer.d.ts.map +1 -1
  130. package/dist/VirtualizerLayouts.d.ts +2 -2
  131. package/dist/VirtualizerLayouts.d.ts.map +1 -1
  132. package/dist/VisuallyHidden.d.ts +1 -1
  133. package/dist/VisuallyHidden.d.ts.map +1 -1
  134. package/dist/contexts.d.ts +5 -1
  135. package/dist/contexts.d.ts.map +1 -1
  136. package/dist/index.d.ts +73 -71
  137. package/dist/index.d.ts.map +1 -1
  138. package/dist/index.js +23247 -18564
  139. package/dist/index.js.map +1 -1
  140. package/dist/index.jsx +18110 -0
  141. package/dist/index.jsx.map +1 -0
  142. package/dist/useDragAndDrop.d.ts +13 -13
  143. package/dist/useDragAndDrop.d.ts.map +1 -1
  144. package/dist/utils.d.ts +2 -2
  145. package/dist/utils.d.ts.map +1 -1
  146. package/dist/virtualizer/Layout.d.ts +1 -1
  147. package/dist/virtualizer/Layout.d.ts.map +1 -1
  148. package/package.json +31 -32
  149. package/src/ActionBar.tsx +75 -72
  150. package/src/ActionGroup.tsx +53 -61
  151. package/src/Alert.tsx +17 -42
  152. package/src/Autocomplete.tsx +39 -44
  153. package/src/Breadcrumbs.tsx +149 -80
  154. package/src/Button.tsx +267 -70
  155. package/src/Calendar.tsx +218 -138
  156. package/src/Checkbox.tsx +413 -121
  157. package/src/Collection.tsx +67 -58
  158. package/src/Color.tsx +803 -380
  159. package/src/ColorEditor.tsx +131 -149
  160. package/src/ComboBox.tsx +414 -249
  161. package/src/ContextualHelpTrigger.tsx +86 -74
  162. package/src/DateField.tsx +185 -91
  163. package/src/DatePicker.tsx +524 -213
  164. package/src/DateRangePickerContext.tsx +44 -0
  165. package/src/Dialog.tsx +156 -118
  166. package/src/Disclosure.tsx +127 -80
  167. package/src/DragAndDrop.tsx +60 -54
  168. package/src/DragPreview.tsx +13 -11
  169. package/src/DropZone.tsx +42 -22
  170. package/src/FieldError.tsx +45 -23
  171. package/src/FileTrigger.tsx +19 -19
  172. package/src/Focusable.tsx +21 -24
  173. package/src/Form.tsx +71 -16
  174. package/src/GridList.tsx +273 -197
  175. package/src/HiddenDateInput.tsx +153 -0
  176. package/src/HiddenTimeInput.tsx +133 -0
  177. package/src/Icon.tsx +22 -43
  178. package/src/Keyboard.tsx +3 -3
  179. package/src/Landmark.tsx +37 -63
  180. package/src/Link.tsx +125 -75
  181. package/src/ListBox.tsx +332 -233
  182. package/src/ListDropTargetDelegate.ts +81 -80
  183. package/src/Menu.tsx +1023 -274
  184. package/src/Meter.tsx +38 -56
  185. package/src/Modal.tsx +243 -175
  186. package/src/NumberField.tsx +139 -143
  187. package/src/Popover.tsx +386 -233
  188. package/src/Pressable.tsx +21 -21
  189. package/src/ProgressBar.tsx +48 -57
  190. package/src/RadioGroup.tsx +524 -122
  191. package/src/RangeCalendar.tsx +157 -90
  192. package/src/RouterProvider.tsx +30 -47
  193. package/src/SearchField.tsx +362 -143
  194. package/src/Select.tsx +656 -233
  195. package/src/SelectionIndicator.tsx +18 -15
  196. package/src/Separator.tsx +47 -49
  197. package/src/SharedElementTransition.tsx +103 -97
  198. package/src/Slider.tsx +138 -98
  199. package/src/StepList.tsx +272 -0
  200. package/src/Switch.tsx +93 -46
  201. package/src/Table.tsx +1308 -342
  202. package/src/Tabs.tsx +324 -103
  203. package/src/TagGroup.tsx +139 -126
  204. package/src/Text.tsx +3 -3
  205. package/src/TextField.tsx +389 -79
  206. package/src/TimeField.tsx +136 -76
  207. package/src/Toast.tsx +209 -157
  208. package/src/ToggleButton.tsx +47 -37
  209. package/src/ToggleButtonGroup.tsx +39 -34
  210. package/src/Toolbar.tsx +54 -69
  211. package/src/Tooltip.tsx +387 -119
  212. package/src/Tree.tsx +651 -368
  213. package/src/Virtualizer.tsx +208 -180
  214. package/src/VirtualizerLayouts.ts +45 -30
  215. package/src/VisuallyHidden.tsx +19 -19
  216. package/src/contexts.ts +29 -37
  217. package/src/index.ts +110 -195
  218. package/src/useDragAndDrop.ts +87 -71
  219. package/src/utils.tsx +40 -55
  220. package/src/virtualizer/Layout.ts +14 -22
  221. package/dist/index.ssr.js +0 -16996
  222. package/dist/index.ssr.js.map +0 -1
package/src/Button.tsx CHANGED
@@ -8,18 +8,23 @@
8
8
  import {
9
9
  type JSX,
10
10
  createContext,
11
+ createEffect,
11
12
  createMemo,
13
+ createSignal,
12
14
  splitProps,
13
15
  useContext,
14
- } from 'solid-js';
16
+ } from "solid-js";
15
17
  import {
18
+ announce,
16
19
  createButton,
17
20
  createFocusRing,
18
21
  createHover,
22
+ createId,
19
23
  mergeProps,
20
24
  type AriaButtonProps,
25
+ type HoverEvent,
21
26
  type PressEvent,
22
- } from '@proyecto-viviana/solidaria';
27
+ } from "@proyecto-viviana/solidaria";
23
28
  import {
24
29
  type RenderChildren,
25
30
  type ClassNameOrFunction,
@@ -27,12 +32,70 @@ import {
27
32
  type SlotProps,
28
33
  useRenderProps,
29
34
  filterDOMProps,
30
- } from './utils';
31
- import { DialogTriggerContext, PopoverTriggerContext } from './contexts';
35
+ } from "./utils";
36
+ import { DialogTriggerContext, PopoverTriggerContext } from "./contexts";
37
+ import { ProgressBarContext } from "./ProgressBar";
32
38
 
33
- // ============================================
34
- // TYPES
35
- // ============================================
39
+ type RefLike<T> = ((el: T) => void) | { current?: T | null } | undefined;
40
+
41
+ function assignRef<T>(ref: RefLike<T>, el: T): void {
42
+ if (!ref) return;
43
+ if (typeof ref === "function") {
44
+ ref(el);
45
+ } else {
46
+ ref.current = el;
47
+ }
48
+ }
49
+
50
+ function createLiveButtonRenderProps(values: () => ButtonRenderProps): ButtonRenderProps {
51
+ return {
52
+ get isHovered() {
53
+ return values().isHovered;
54
+ },
55
+ get isPressed() {
56
+ return values().isPressed;
57
+ },
58
+ get isFocused() {
59
+ return values().isFocused;
60
+ },
61
+ get isFocusVisible() {
62
+ return values().isFocusVisible;
63
+ },
64
+ get isDisabled() {
65
+ return values().isDisabled;
66
+ },
67
+ get isPending() {
68
+ return values().isPending;
69
+ },
70
+ };
71
+ }
72
+
73
+ function createLiveCustomRootProps(
74
+ getProps: () => JSX.ButtonHTMLAttributes<HTMLButtonElement>,
75
+ getChildren: () => JSX.Element,
76
+ ref: (el: HTMLButtonElement) => void,
77
+ ): JSX.ButtonHTMLAttributes<HTMLButtonElement> {
78
+ const props = {} as JSX.ButtonHTMLAttributes<HTMLButtonElement>;
79
+ const keys = new Set([...Object.keys(getProps()), "children", "ref"]);
80
+
81
+ for (const key of keys) {
82
+ Object.defineProperty(props, key, {
83
+ enumerable: true,
84
+ configurable: true,
85
+ get() {
86
+ if (key === "children") {
87
+ return getChildren();
88
+ }
89
+ if (key === "ref") {
90
+ return ref;
91
+ }
92
+ return (getProps() as Record<string, unknown>)[key];
93
+ },
94
+ });
95
+ }
96
+
97
+ return props;
98
+ }
36
99
 
37
100
  export interface ButtonRenderProps {
38
101
  /** Whether the button is currently hovered with a mouse. */
@@ -49,28 +112,37 @@ export interface ButtonRenderProps {
49
112
  isPending: boolean;
50
113
  }
51
114
 
52
- export interface ButtonProps
53
- extends Omit<AriaButtonProps, 'children'>,
54
- SlotProps {
115
+ export interface ButtonProps extends Omit<AriaButtonProps, "children">, SlotProps {
55
116
  /** The children of the component. A function may be provided to receive render props. */
56
117
  children?: RenderChildren<ButtonRenderProps>;
57
118
  /** The CSS className for the element. */
58
119
  class?: ClassNameOrFunction<ButtonRenderProps>;
59
120
  /** The inline style for the element. */
60
121
  style?: StyleOrFunction<ButtonRenderProps>;
122
+ /** Custom renderer for the outer button element. */
123
+ render?: (
124
+ props: JSX.ButtonHTMLAttributes<HTMLButtonElement>,
125
+ renderProps: ButtonRenderProps,
126
+ ) => JSX.Element;
127
+ /** Ref for the underlying button element. */
128
+ ref?: RefLike<HTMLButtonElement>;
61
129
  /** Whether the button is in a pending state. */
62
130
  isPending?: boolean;
131
+ /** Keeps pending buttons focusable by using aria-disabled without a native disabled attribute. */
132
+ isPendingFocusable?: boolean;
133
+ /** Handler called when hover starts. */
134
+ onHoverStart?: (e: HoverEvent) => void;
135
+ /** Handler called when hover ends. */
136
+ onHoverEnd?: (e: HoverEvent) => void;
137
+ /** Handler called when hover state changes. */
138
+ onHoverChange?: (isHovered: boolean) => void;
63
139
  }
64
140
 
65
- // ============================================
66
- // CONTEXT
67
- // ============================================
68
-
69
- export const ButtonContext = createContext<ButtonProps | null>(null);
141
+ export interface ButtonContextValue extends ButtonProps {
142
+ slots?: Record<string, ButtonProps>;
143
+ }
70
144
 
71
- // ============================================
72
- // COMPONENT
73
- // ============================================
145
+ export const ButtonContext = createContext<ButtonContextValue | null>(null);
74
146
 
75
147
  /**
76
148
  * A button allows a user to perform an action.
@@ -91,15 +163,28 @@ export const ButtonContext = createContext<ButtonProps | null>(null);
91
163
  */
92
164
  export function Button(props: ButtonProps): JSX.Element {
93
165
  const contextProps = useContext(ButtonContext);
94
- const mergedProps = (contextProps ? mergeProps(contextProps, props) : props) as ButtonProps;
166
+ const contextSlotProps = contextProps?.slots?.[props.slot ?? "default"];
167
+ const contextBaseProps = createMemo<ButtonProps>(() => {
168
+ if (!contextProps) return {};
169
+ const { slots: _slots, ...rest } = contextProps;
170
+ return rest;
171
+ });
172
+ const mergedProps = (
173
+ contextProps ? mergeProps(contextBaseProps(), contextSlotProps ?? {}, props) : props
174
+ ) as ButtonProps;
95
175
 
96
- // Split props
97
176
  const [local, ariaProps] = splitProps(mergedProps, [
98
- 'children',
99
- 'class',
100
- 'style',
101
- 'slot',
102
- 'isPending',
177
+ "children",
178
+ "class",
179
+ "style",
180
+ "render",
181
+ "ref",
182
+ "slot",
183
+ "isPending",
184
+ "isPendingFocusable",
185
+ "onHoverStart",
186
+ "onHoverEnd",
187
+ "onHoverChange",
103
188
  ]);
104
189
 
105
190
  // Check if inside a DialogTrigger or PopoverTrigger - if so, toggle on press
@@ -113,30 +198,35 @@ export function Button(props: ButtonProps): JSX.Element {
113
198
  // Helper to resolve isDisabled (handles both boolean and Accessor<boolean>)
114
199
  const resolveDisabled = (): boolean => {
115
200
  const disabled = ariaProps.isDisabled;
116
- if (typeof disabled === 'function') {
201
+ if (typeof disabled === "function") {
117
202
  return disabled();
118
203
  }
119
204
  return !!disabled;
120
205
  };
121
206
 
122
207
  const resolvePending = (): boolean => !!local.isPending;
208
+ const isPendingFocusable = () => local.isPendingFocusable !== false;
123
209
 
124
- let buttonEl: HTMLButtonElement | undefined;
210
+ const [resolvedButtonEl, setResolvedButtonEl] = createSignal<HTMLButtonElement | null>(null);
211
+ const buttonId = createId((ariaProps as Record<string, unknown>).id as string | undefined);
212
+ const progressId = createId();
125
213
 
126
214
  // Explicit trigger ownership: a button toggles overlays only when it is the
127
215
  // registered trigger element for the surrounding trigger context.
128
216
  const isDialogTrigger = () =>
129
- !!dialogTriggerContext && !!buttonEl && dialogTriggerContext.triggerRef() === buttonEl;
217
+ !!dialogTriggerContext &&
218
+ !!resolvedButtonEl() &&
219
+ dialogTriggerContext.triggerRef() === resolvedButtonEl();
130
220
  const isPopoverTrigger = () =>
131
- !!popoverTriggerContext && !!buttonEl && popoverTriggerContext.triggerRef() === buttonEl;
221
+ !!popoverTriggerContext &&
222
+ !!resolvedButtonEl() &&
223
+ popoverTriggerContext.triggerRef() === resolvedButtonEl();
132
224
 
133
- // Wrap onPress to also toggle dialog/popover if this is a trigger button
134
225
  const handlePress = (e: PressEvent) => {
135
226
  if (resolvePending()) {
136
227
  return;
137
228
  }
138
- // Call original onPress if provided
139
- if (typeof ariaProps.onPress === 'function') {
229
+ if (typeof ariaProps.onPress === "function") {
140
230
  ariaProps.onPress(e);
141
231
  }
142
232
  // Toggle only when this exact button is the registered trigger element.
@@ -148,7 +238,6 @@ export function Button(props: ButtonProps): JSX.Element {
148
238
  }
149
239
  };
150
240
 
151
- // Create button aria props
152
241
  const buttonAria = createButton({
153
242
  ...ariaProps,
154
243
  onPress: handlePress,
@@ -172,17 +261,17 @@ export function Button(props: ButtonProps): JSX.Element {
172
261
  },
173
262
  });
174
263
 
175
- // Create focus ring
176
264
  const { isFocused, isFocusVisible, focusProps } = createFocusRing();
177
265
 
178
- // Create hover
179
266
  const { isHovered, hoverProps } = createHover({
180
267
  get isDisabled() {
181
268
  return resolveDisabled() || resolvePending();
182
269
  },
270
+ onHoverStart: local.onHoverStart,
271
+ onHoverEnd: local.onHoverEnd,
272
+ onHoverChange: local.onHoverChange,
183
273
  });
184
274
 
185
- // Render props values
186
275
  const renderValues = createMemo<ButtonRenderProps>(() => ({
187
276
  isHovered: isHovered(),
188
277
  isPressed: buttonAria.isPressed() && !resolvePending(),
@@ -192,19 +281,19 @@ export function Button(props: ButtonProps): JSX.Element {
192
281
  isPending: resolvePending(),
193
282
  }));
194
283
 
195
- // Resolve render props
196
284
  const renderProps = useRenderProps(
197
285
  {
198
286
  // Use merged children so ButtonContext can supply slot/default content.
199
- children: local.children,
287
+ get children() {
288
+ return local.children;
289
+ },
200
290
  class: local.class,
201
291
  style: local.style,
202
- defaultClassName: 'solidaria-Button',
292
+ defaultClassName: "solidaria-Button",
203
293
  },
204
- renderValues
294
+ renderValues,
205
295
  );
206
296
 
207
- // Filter DOM props
208
297
  // Remove onClick from DOM props - it's already handled by createPress
209
298
  // This matches React Aria Components behavior (Button.tsx line 144: delete DOMProps.onClick)
210
299
  const domProps = createMemo(() => {
@@ -214,12 +303,16 @@ export function Button(props: ButtonProps): JSX.Element {
214
303
  return filtered;
215
304
  });
216
305
 
217
- // Extract refs from props to combine them manually
218
- const buttonPropsRef = (buttonAria.buttonProps as Record<string, unknown>).ref as ((el: HTMLElement) => void) | undefined;
219
- const focusPropsRef = (focusProps as Record<string, unknown>).ref as ((el: HTMLElement) => void) | undefined;
220
- const hoverPropsRef = (hoverProps as Record<string, unknown>).ref as ((el: HTMLElement) => void) | undefined;
306
+ const buttonPropsRef = (buttonAria.buttonProps as Record<string, unknown>).ref as
307
+ | ((el: HTMLElement) => void)
308
+ | undefined;
309
+ const focusPropsRef = (focusProps as Record<string, unknown>).ref as
310
+ | ((el: HTMLElement) => void)
311
+ | undefined;
312
+ const hoverPropsRef = (hoverProps as Record<string, unknown>).ref as
313
+ | ((el: HTMLElement) => void)
314
+ | undefined;
221
315
 
222
- // Remove ref from spread props to avoid type conflicts
223
316
  const cleanButtonProps = () => {
224
317
  const { ref: _ref1, ...rest } = buttonAria.buttonProps as Record<string, unknown>;
225
318
  return rest;
@@ -232,51 +325,155 @@ export function Button(props: ButtonProps): JSX.Element {
232
325
  const { ref: _ref3, ...rest } = hoverProps as Record<string, unknown>;
233
326
  return rest;
234
327
  };
328
+ const ariaLabelledBy = createMemo(() => {
329
+ const labelledBy = cleanButtonProps()["aria-labelledby"] as string | undefined;
330
+ if (!resolvePending()) {
331
+ return labelledBy;
332
+ }
333
+
334
+ if (labelledBy) {
335
+ return `${labelledBy} ${progressId}`;
336
+ }
337
+
338
+ if (cleanButtonProps()["aria-label"]) {
339
+ return `${buttonId} ${progressId}`;
340
+ }
341
+
342
+ return labelledBy;
343
+ });
235
344
 
236
- // Ref callback that combines all refs
237
345
  const handleRef = (el: HTMLButtonElement) => {
238
- buttonEl = el;
346
+ setResolvedButtonEl(el);
347
+ assignRef(local.ref, el);
239
348
 
240
- // Call the focusable ref for autoFocus support
241
349
  buttonPropsRef?.(el);
242
350
  focusPropsRef?.(el);
243
351
  hoverPropsRef?.(el);
244
352
 
245
353
  // Register trigger ownership for surrounding trigger contexts.
246
354
  if (dialogTriggerContext?.setTriggerRef) {
355
+ if (!el.id) {
356
+ el.id = dialogTriggerContext.triggerId;
357
+ }
247
358
  dialogTriggerContext.setTriggerRef(el);
248
359
  }
249
360
  if (popoverTriggerContext?.setTriggerRef) {
250
361
  popoverTriggerContext.setTriggerRef(el);
251
362
  }
252
363
  };
364
+ const buttonType = () =>
365
+ (buttonAria.buttonProps as Record<string, unknown>).type === "submit" && resolvePending()
366
+ ? "button"
367
+ : ((buttonAria.buttonProps as Record<string, unknown>).type as
368
+ | "button"
369
+ | "submit"
370
+ | "reset"
371
+ | undefined);
372
+ const triggerAriaProps = () => {
373
+ const dialogTriggerProps = dialogTriggerContext?.triggerProps;
374
+ if (dialogTriggerProps && isDialogTrigger()) {
375
+ const next: Record<string, unknown> = {};
376
+ for (const name of ["aria-haspopup", "aria-expanded", "aria-controls"]) {
377
+ if (dialogTriggerProps[name] != null) {
378
+ next[name] = dialogTriggerProps[name];
379
+ }
380
+ }
381
+ return next;
382
+ }
383
+
384
+ const triggerProps = popoverTriggerContext?.triggerProps;
385
+ if (!triggerProps || !isPopoverTrigger()) {
386
+ return {};
387
+ }
388
+
389
+ const next: Record<string, unknown> = {};
390
+ for (const name of ["aria-haspopup", "aria-expanded", "aria-controls"]) {
391
+ if (triggerProps[name] != null) {
392
+ next[name] = triggerProps[name];
393
+ }
394
+ }
395
+ return next;
396
+ };
397
+ const disablePendingInteractions = (props: Record<string, unknown>) => {
398
+ if (!resolvePending()) {
399
+ return props;
400
+ }
401
+
402
+ const next = { ...props };
403
+ for (const key of Object.keys(next)) {
404
+ if (key.startsWith("on") && !key.includes("Focus") && !key.includes("Blur")) {
405
+ next[key] = undefined;
406
+ }
407
+ }
408
+ next.href = undefined;
409
+ next.target = undefined;
410
+ return next;
411
+ };
412
+ const dataState = (value: boolean) => (value ? "true" : undefined);
413
+ const buttonChildren = () => renderProps.renderChildren();
414
+ const buttonContent = () => (
415
+ <ProgressBarContext.Provider value={{ id: progressId }}>
416
+ {buttonChildren()}
417
+ </ProgressBarContext.Provider>
418
+ );
419
+ let wasPending = resolvePending();
420
+ createEffect(() => {
421
+ const pending = resolvePending();
422
+ const message = { "aria-labelledby": ariaLabelledBy() || buttonId };
253
423
 
254
- return (
424
+ if (!wasPending && isFocused() && pending) {
425
+ announce(message, "assertive");
426
+ } else if (wasPending && isFocused() && !pending) {
427
+ announce(message, "assertive");
428
+ }
429
+
430
+ wasPending = pending;
431
+ });
432
+ const rootProps = () =>
433
+ ({
434
+ ...domProps(),
435
+ ...disablePendingInteractions(cleanButtonProps()),
436
+ ...triggerAriaProps(),
437
+ ...cleanFocusProps(),
438
+ ...cleanHoverProps(),
439
+ type: buttonType(),
440
+ id: buttonId,
441
+ class: renderProps.class(),
442
+ style: renderProps.style(),
443
+ slot: local.slot,
444
+ disabled: resolvePending() && isPendingFocusable() ? undefined : cleanButtonProps().disabled,
445
+ "aria-labelledby": ariaLabelledBy(),
446
+ "aria-disabled": resolvePending()
447
+ ? "true"
448
+ : (cleanButtonProps()["aria-disabled"] ?? ariaProps["aria-disabled"]),
449
+ "data-pressed": dataState(buttonAria.isPressed() && !resolvePending()),
450
+ "data-hovered": dataState(isHovered()),
451
+ "data-focused": dataState(isFocused()),
452
+ "data-focus-visible": dataState(isFocusVisible()),
453
+ "data-disabled": dataState(resolveDisabled()),
454
+ "data-pending": dataState(resolvePending()),
455
+ }) as JSX.ButtonHTMLAttributes<HTMLButtonElement>;
456
+ const customRootProps = createLiveCustomRootProps(rootProps, buttonContent, handleRef);
457
+ const customRenderValues = createLiveButtonRenderProps(renderValues);
458
+
459
+ return local.render ? (
460
+ local.render(customRootProps, customRenderValues)
461
+ ) : (
255
462
  <button
256
463
  ref={handleRef}
257
- {...domProps()}
258
- {...cleanButtonProps()}
259
- {...cleanFocusProps()}
260
- {...cleanHoverProps()}
261
- type={
262
- (buttonAria.buttonProps as Record<string, unknown>).type === 'submit' && resolvePending()
263
- ? 'button'
264
- : (buttonAria.buttonProps as Record<string, unknown>).type as
265
- | 'button'
266
- | 'submit'
267
- | 'reset'
268
- | undefined
269
- }
464
+ {...rootProps()}
270
465
  class={renderProps.class()}
271
466
  style={renderProps.style()}
272
- data-pressed={(buttonAria.isPressed() && !resolvePending()) || undefined}
273
- data-hovered={isHovered() || undefined}
274
- data-focused={isFocused() || undefined}
275
- data-focus-visible={isFocusVisible() || undefined}
276
- data-disabled={resolveDisabled() || undefined}
277
- data-pending={resolvePending() || undefined}
467
+ attr:data-pressed={(rootProps() as Record<string, unknown>)["data-pressed"] as string}
468
+ attr:data-hovered={(rootProps() as Record<string, unknown>)["data-hovered"] as string}
469
+ attr:data-focused={(rootProps() as Record<string, unknown>)["data-focused"] as string}
470
+ attr:data-focus-visible={
471
+ (rootProps() as Record<string, unknown>)["data-focus-visible"] as string
472
+ }
473
+ attr:data-disabled={(rootProps() as Record<string, unknown>)["data-disabled"] as string}
474
+ attr:data-pending={(rootProps() as Record<string, unknown>)["data-pending"] as string}
278
475
  >
279
- {renderProps.renderChildren()}
476
+ {buttonContent()}
280
477
  </button>
281
478
  );
282
479
  }