@proyecto-viviana/solidaria-components 0.2.2 → 0.2.4

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 +2 -6
  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 +56 -56
  18. package/dist/index.js.map +2 -2
  19. package/dist/index.ssr.js +56 -56
  20. package/dist/index.ssr.js.map +2 -2
  21. package/package.json +10 -8
  22. package/src/Autocomplete.tsx +174 -0
  23. package/src/Breadcrumbs.tsx +264 -0
  24. package/src/Button.tsx +238 -0
  25. package/src/Calendar.tsx +471 -0
  26. package/src/Checkbox.tsx +387 -0
  27. package/src/Color.tsx +1370 -0
  28. package/src/ComboBox.tsx +824 -0
  29. package/src/DateField.tsx +337 -0
  30. package/src/DatePicker.tsx +367 -0
  31. package/src/Dialog.tsx +262 -0
  32. package/src/Disclosure.tsx +439 -0
  33. package/src/GridList.tsx +511 -0
  34. package/src/Landmark.tsx +203 -0
  35. package/src/Link.tsx +201 -0
  36. package/src/ListBox.tsx +346 -0
  37. package/src/Menu.tsx +544 -0
  38. package/src/Meter.tsx +157 -0
  39. package/src/Modal.tsx +433 -0
  40. package/src/NumberField.tsx +542 -0
  41. package/src/Popover.tsx +540 -0
  42. package/src/ProgressBar.tsx +162 -0
  43. package/src/RadioGroup.tsx +356 -0
  44. package/src/RangeCalendar.tsx +462 -0
  45. package/src/SearchField.tsx +479 -0
  46. package/src/Select.tsx +734 -0
  47. package/src/Separator.tsx +130 -0
  48. package/src/Slider.tsx +500 -0
  49. package/src/Switch.tsx +213 -0
  50. package/src/Table.tsx +857 -0
  51. package/src/Tabs.tsx +552 -0
  52. package/src/TagGroup.tsx +421 -0
  53. package/src/TextField.tsx +271 -0
  54. package/src/TimeField.tsx +455 -0
  55. package/src/Toast.tsx +503 -0
  56. package/src/Toolbar.tsx +160 -0
  57. package/src/Tooltip.tsx +423 -0
  58. package/src/Tree.tsx +551 -0
  59. package/src/VisuallyHidden.tsx +60 -0
  60. package/src/contexts.ts +74 -0
  61. package/src/index.ts +620 -0
  62. package/src/utils.tsx +329 -0
  63. package/dist/index.jsx +0 -9056
  64. package/dist/index.jsx.map +0 -7
package/src/Toast.tsx ADDED
@@ -0,0 +1,503 @@
1
+ /**
2
+ * Toast components for solidaria-components
3
+ *
4
+ * Toast notifications with auto-dismiss, pause on hover/focus, and accessibility.
5
+ * Port of react-aria-components Toast.
6
+ */
7
+
8
+ import {
9
+ type JSX,
10
+ createContext,
11
+ createMemo,
12
+ splitProps,
13
+ Show,
14
+ useContext,
15
+ } from 'solid-js';
16
+ import { Portal, isServer } from 'solid-js/web';
17
+ import {
18
+ type ToastState,
19
+ type QueuedToast,
20
+ type ToastQueueOptions,
21
+ ToastQueue,
22
+ createToastState,
23
+ } from '@proyecto-viviana/solid-stately';
24
+ import {
25
+ createToast,
26
+ createToastRegion,
27
+ } from '@proyecto-viviana/solidaria';
28
+ import {
29
+ type RenderChildren,
30
+ type ClassNameOrFunction,
31
+ type StyleOrFunction,
32
+ useRenderProps,
33
+ filterDOMProps,
34
+ } from './utils';
35
+
36
+ // ============================================
37
+ // TYPES
38
+ // ============================================
39
+
40
+ export interface ToastContent {
41
+ /** The title of the toast. */
42
+ title?: string;
43
+ /** The description/body of the toast. */
44
+ description?: string;
45
+ /** The type/variant of the toast (info, success, warning, error). */
46
+ type?: 'info' | 'success' | 'warning' | 'error';
47
+ /** Custom action button. */
48
+ action?: {
49
+ label: string;
50
+ onAction: () => void;
51
+ };
52
+ }
53
+
54
+ export interface ToastRenderProps {
55
+ /** Whether the toast is currently animating in. */
56
+ isEntering: boolean;
57
+ /** Whether the toast is currently animating out. */
58
+ isExiting: boolean;
59
+ /** The animation state (entering, exiting, queued). */
60
+ animation: 'entering' | 'exiting' | 'queued' | undefined;
61
+ /** The toast data. */
62
+ toast: QueuedToast<ToastContent>;
63
+ }
64
+
65
+ export interface ToastRegionRenderProps {
66
+ /** The visible toasts. */
67
+ visibleToasts: QueuedToast<ToastContent>[];
68
+ }
69
+
70
+ export interface ToastRegionProps {
71
+ /** The children of the component - can be JSX or render function. */
72
+ children?: RenderChildren<ToastRegionRenderProps>;
73
+ /** The CSS className for the element. */
74
+ class?: ClassNameOrFunction<ToastRegionRenderProps>;
75
+ /** The inline style for the element. */
76
+ style?: StyleOrFunction<ToastRegionRenderProps>;
77
+ /** The toast state to display. If not provided, uses ToastContext. */
78
+ state?: ToastState<ToastContent>;
79
+ /** Accessible label for the region. */
80
+ 'aria-label'?: string;
81
+ /** Whether to render in a portal. */
82
+ portal?: boolean;
83
+ /** Placement of the toast region. */
84
+ placement?: 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end';
85
+ }
86
+
87
+ export interface ToastProps {
88
+ /** The toast data. */
89
+ toast: QueuedToast<ToastContent>;
90
+ /** The children of the component - can be JSX or render function. */
91
+ children?: RenderChildren<ToastRenderProps>;
92
+ /** The CSS className for the element. */
93
+ class?: ClassNameOrFunction<ToastRenderProps>;
94
+ /** The inline style for the element. */
95
+ style?: StyleOrFunction<ToastRenderProps>;
96
+ }
97
+
98
+ // ============================================
99
+ // CONTEXT
100
+ // ============================================
101
+
102
+ export const ToastContext = createContext<ToastState<ToastContent> | null>(null);
103
+
104
+ export function useToastContext(): ToastState<ToastContent> {
105
+ const context = useContext(ToastContext);
106
+ if (!context) {
107
+ throw new Error('Toast components must be used within a ToastProvider');
108
+ }
109
+ return context;
110
+ }
111
+
112
+ // ============================================
113
+ // GLOBAL TOAST QUEUE
114
+ // ============================================
115
+
116
+ /** Default global toast queue that can be used for app-wide toasts. */
117
+ export const globalToastQueue = new ToastQueue<ToastContent>({
118
+ maxVisibleToasts: 5,
119
+ hasExitAnimation: false, // TODO: Enable once exit animation handling is implemented
120
+ });
121
+
122
+ /**
123
+ * Add a toast to the global queue.
124
+ * Convenience function for adding toasts from anywhere in the app.
125
+ */
126
+ export function addToast(
127
+ content: ToastContent,
128
+ options?: { timeout?: number; priority?: number }
129
+ ): string {
130
+ return globalToastQueue.add(content, options);
131
+ }
132
+
133
+ // ============================================
134
+ // TOAST PROVIDER
135
+ // ============================================
136
+
137
+ export interface ToastProviderProps {
138
+ /** The children of the provider. */
139
+ children: JSX.Element;
140
+ /** Custom toast queue options. */
141
+ queueOptions?: ToastQueueOptions;
142
+ /** Use global queue instead of creating a new one. */
143
+ useGlobalQueue?: boolean;
144
+ }
145
+
146
+ /**
147
+ * ToastProvider creates a toast queue context for descendant components.
148
+ * Use this to wrap your app or a section that needs toast notifications.
149
+ *
150
+ * @example
151
+ * ```tsx
152
+ * <ToastProvider>
153
+ * <App />
154
+ * <ToastRegion />
155
+ * </ToastProvider>
156
+ * ```
157
+ */
158
+ export function ToastProvider(props: ToastProviderProps): JSX.Element {
159
+ const queue = props.useGlobalQueue
160
+ ? globalToastQueue
161
+ : new ToastQueue<ToastContent>(props.queueOptions);
162
+
163
+ const state = createToastState({ queue });
164
+
165
+ return (
166
+ <ToastContext.Provider value={state}>
167
+ {props.children}
168
+ </ToastContext.Provider>
169
+ );
170
+ }
171
+
172
+ // ============================================
173
+ // TOAST REGION COMPONENT
174
+ // ============================================
175
+
176
+ /**
177
+ * ToastRegion is a container that displays all visible toasts.
178
+ * It handles pause on hover/focus and provides the landmark region.
179
+ *
180
+ * @example
181
+ * ```tsx
182
+ * <ToastRegion placement="bottom-end">
183
+ * {(renderProps) => (
184
+ * <For each={renderProps.visibleToasts}>
185
+ * {(toast) => <Toast toast={toast} />}
186
+ * </For>
187
+ * )}
188
+ * </ToastRegion>
189
+ * ```
190
+ */
191
+ export function ToastRegion(props: ToastRegionProps): JSX.Element {
192
+ if (isServer) {
193
+ return null as unknown as JSX.Element;
194
+ }
195
+
196
+ const [local, rest] = splitProps(props, [
197
+ 'children',
198
+ 'class',
199
+ 'style',
200
+ 'state',
201
+ 'aria-label',
202
+ 'portal',
203
+ 'placement',
204
+ ]);
205
+
206
+ // Get state from context if not provided
207
+ const contextState = useContext(ToastContext);
208
+ const state = () => local.state ?? contextState;
209
+
210
+ // Create region accessibility props
211
+ const getRegionAria = () => {
212
+ const s = state();
213
+ if (!s) return null;
214
+ return createToastRegion({
215
+ state: s,
216
+ 'aria-label': local['aria-label'],
217
+ });
218
+ };
219
+
220
+ // Render props values
221
+ const renderValues = createMemo<ToastRegionRenderProps>(() => ({
222
+ visibleToasts: state()?.visibleToasts() ?? [],
223
+ }));
224
+
225
+ // Resolve render props
226
+ const renderProps = useRenderProps(
227
+ {
228
+ children: props.children,
229
+ class: local.class,
230
+ style: local.style,
231
+ defaultClassName: 'solidaria-ToastRegion',
232
+ },
233
+ renderValues
234
+ );
235
+
236
+ // Filter DOM props
237
+ const domProps = createMemo(() => filterDOMProps(rest as Record<string, unknown>, { global: true }));
238
+
239
+ // Placement styles
240
+ const placementStyles = createMemo<JSX.CSSProperties>(() => {
241
+ const placement = local.placement ?? 'bottom-end';
242
+ const base: JSX.CSSProperties = {
243
+ position: 'fixed',
244
+ 'z-index': 100001,
245
+ display: 'flex',
246
+ 'flex-direction': 'column',
247
+ gap: '8px',
248
+ padding: '16px',
249
+ 'pointer-events': 'none',
250
+ };
251
+
252
+ switch (placement) {
253
+ case 'top':
254
+ return { ...base, top: 0, left: '50%', transform: 'translateX(-50%)' } as JSX.CSSProperties;
255
+ case 'top-start':
256
+ return { ...base, top: 0, left: 0 } as JSX.CSSProperties;
257
+ case 'top-end':
258
+ return { ...base, top: 0, right: 0 } as JSX.CSSProperties;
259
+ case 'bottom':
260
+ return { ...base, bottom: 0, left: '50%', transform: 'translateX(-50%)' } as JSX.CSSProperties;
261
+ case 'bottom-start':
262
+ return { ...base, bottom: 0, left: 0 } as JSX.CSSProperties;
263
+ case 'bottom-end':
264
+ default:
265
+ return { ...base, bottom: 0, right: 0 } as JSX.CSSProperties;
266
+ }
267
+ });
268
+
269
+ const visibleToasts = () => state()?.visibleToasts() ?? [];
270
+ const hasToasts = () => visibleToasts().length > 0;
271
+
272
+ const regionContent = () => {
273
+ const regionAria = getRegionAria();
274
+ if (!regionAria || !state()) return null;
275
+
276
+ // Merge styles - placement styles are base, renderProps.style() overrides
277
+ const mergedStyle = () => {
278
+ const placement = placementStyles();
279
+ const custom = renderProps.style();
280
+ if (!custom) return placement;
281
+ return { ...placement, ...custom } as JSX.CSSProperties;
282
+ };
283
+
284
+ // Extract ref from regionProps to avoid type conflicts
285
+ const { ref: _ref, ...cleanRegionProps } = regionAria.regionProps as Record<string, unknown>;
286
+
287
+ return (
288
+ <div
289
+ {...domProps()}
290
+ {...cleanRegionProps}
291
+ class={renderProps.class()}
292
+ style={mergedStyle()}
293
+ data-placement={local.placement ?? 'bottom-end'}
294
+ >
295
+ {renderProps.renderChildren()}
296
+ </div>
297
+ );
298
+ };
299
+
300
+ // Only render when there are toasts
301
+ return (
302
+ <Show when={hasToasts()}>
303
+ <Show when={local.portal !== false} fallback={regionContent()}>
304
+ <Portal>{regionContent()}</Portal>
305
+ </Show>
306
+ </Show>
307
+ );
308
+ }
309
+
310
+ // ============================================
311
+ // TOAST COMPONENT
312
+ // ============================================
313
+
314
+ /**
315
+ * Toast is an individual notification component.
316
+ *
317
+ * @example
318
+ * ```tsx
319
+ * <Toast toast={toast}>
320
+ * {(renderProps) => (
321
+ * <div class={renderProps.isExiting ? 'fade-out' : 'fade-in'}>
322
+ * <h3>{renderProps.toast.content.title}</h3>
323
+ * <p>{renderProps.toast.content.description}</p>
324
+ * </div>
325
+ * )}
326
+ * </Toast>
327
+ * ```
328
+ */
329
+ export function Toast(props: ToastProps): JSX.Element {
330
+ const [local, rest] = splitProps(props, [
331
+ 'toast',
332
+ 'children',
333
+ 'class',
334
+ 'style',
335
+ ]);
336
+
337
+ // Get state from context
338
+ const state = useToastContext();
339
+
340
+ // Create toast accessibility props
341
+ const toastAria = createToast({
342
+ toast: local.toast,
343
+ state,
344
+ });
345
+
346
+ // Render props values
347
+ const renderValues = createMemo<ToastRenderProps>(() => ({
348
+ isEntering: local.toast.animation === 'entering',
349
+ isExiting: local.toast.animation === 'exiting',
350
+ animation: local.toast.animation,
351
+ toast: local.toast,
352
+ }));
353
+
354
+ // Resolve render props
355
+ const renderProps = useRenderProps(
356
+ {
357
+ children: props.children,
358
+ class: local.class,
359
+ style: local.style,
360
+ defaultClassName: 'solidaria-Toast',
361
+ },
362
+ renderValues
363
+ );
364
+
365
+ // Filter DOM props
366
+ const domProps = createMemo(() => filterDOMProps(rest as Record<string, unknown>, { global: true }));
367
+
368
+ // Merge styles
369
+ const mergedStyle = () => {
370
+ const custom = renderProps.style();
371
+ if (!custom) return { 'pointer-events': 'auto' as const };
372
+ return { 'pointer-events': 'auto' as const, ...custom } as JSX.CSSProperties;
373
+ };
374
+
375
+ // Extract ref from toastProps to avoid type conflicts
376
+ const { ref: _ref, ...cleanToastProps } = toastAria.toastProps as Record<string, unknown>;
377
+
378
+ return (
379
+ <div
380
+ {...domProps()}
381
+ {...cleanToastProps}
382
+ class={renderProps.class()}
383
+ style={mergedStyle()}
384
+ data-animation={local.toast.animation}
385
+ data-type={local.toast.content.type}
386
+ >
387
+ {renderProps.renderChildren()}
388
+ </div>
389
+ );
390
+ }
391
+
392
+ // ============================================
393
+ // TOAST SUB-COMPONENTS
394
+ // ============================================
395
+
396
+ export interface ToastTitleProps {
397
+ children: JSX.Element;
398
+ class?: string;
399
+ style?: JSX.CSSProperties;
400
+ }
401
+
402
+ /**
403
+ * ToastTitle renders the toast title with proper accessibility attributes.
404
+ */
405
+ export function ToastTitle(props: ToastTitleProps): JSX.Element {
406
+ return (
407
+ <div class={props.class} style={props.style}>
408
+ {props.children}
409
+ </div>
410
+ );
411
+ }
412
+
413
+ export interface ToastDescriptionProps {
414
+ children: JSX.Element;
415
+ class?: string;
416
+ style?: JSX.CSSProperties;
417
+ }
418
+
419
+ /**
420
+ * ToastDescription renders the toast description with proper accessibility attributes.
421
+ */
422
+ export function ToastDescription(props: ToastDescriptionProps): JSX.Element {
423
+ return (
424
+ <div class={props.class} style={props.style}>
425
+ {props.children}
426
+ </div>
427
+ );
428
+ }
429
+
430
+ export interface ToastCloseButtonProps {
431
+ /** The toast to close. */
432
+ toast: QueuedToast<ToastContent>;
433
+ children?: JSX.Element;
434
+ class?: string;
435
+ style?: JSX.CSSProperties;
436
+ 'aria-label'?: string;
437
+ }
438
+
439
+ /**
440
+ * ToastCloseButton is a button that closes the toast.
441
+ */
442
+ export function ToastCloseButton(props: ToastCloseButtonProps): JSX.Element {
443
+ const state = useToastContext();
444
+
445
+ const handleClose = () => {
446
+ state.close(props.toast.key);
447
+ };
448
+
449
+ return (
450
+ <button
451
+ type="button"
452
+ class={props.class}
453
+ style={props.style}
454
+ aria-label={props['aria-label'] ?? 'Close'}
455
+ onClick={handleClose}
456
+ >
457
+ {props.children ?? '×'}
458
+ </button>
459
+ );
460
+ }
461
+
462
+ // ============================================
463
+ // DEFAULT TOAST RENDERING
464
+ // ============================================
465
+
466
+ export interface DefaultToastProps {
467
+ toast: QueuedToast<ToastContent>;
468
+ }
469
+
470
+ /**
471
+ * DefaultToast provides a basic toast layout with title, description, and close button.
472
+ * Use this as a starting point or as-is for simple toast needs.
473
+ */
474
+ export function DefaultToast(props: DefaultToastProps): JSX.Element {
475
+ const content = () => props.toast.content;
476
+
477
+ return (
478
+ <Toast toast={props.toast}>
479
+ <div style={{ display: 'flex', 'align-items': 'flex-start', gap: '12px' }}>
480
+ <div style={{ flex: 1 }}>
481
+ <Show when={content().title}>
482
+ <ToastTitle style={{ 'font-weight': 'bold', 'margin-bottom': '4px' }}>
483
+ {content().title}
484
+ </ToastTitle>
485
+ </Show>
486
+ <Show when={content().description}>
487
+ <ToastDescription>{content().description}</ToastDescription>
488
+ </Show>
489
+ <Show when={content().action}>
490
+ <button
491
+ type="button"
492
+ style={{ 'margin-top': '8px' }}
493
+ onClick={content().action?.onAction}
494
+ >
495
+ {content().action?.label}
496
+ </button>
497
+ </Show>
498
+ </div>
499
+ <ToastCloseButton toast={props.toast} />
500
+ </div>
501
+ </Toast>
502
+ );
503
+ }
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Toolbar component for solidaria-components
3
+ *
4
+ * Pre-wired headless toolbar component that combines aria hooks.
5
+ * Port of react-aria-components/src/Toolbar.tsx
6
+ */
7
+
8
+ import {
9
+ type JSX,
10
+ type ParentProps,
11
+ createContext,
12
+ createMemo,
13
+ splitProps,
14
+ useContext,
15
+ } from 'solid-js'
16
+ import {
17
+ createToolbar,
18
+ type AriaToolbarProps,
19
+ type Orientation,
20
+ } from '@proyecto-viviana/solidaria'
21
+ import {
22
+ type SlotProps,
23
+ filterDOMProps,
24
+ } from './utils'
25
+
26
+ // ============================================
27
+ // TYPES
28
+ // ============================================
29
+
30
+ export interface ToolbarRenderProps {
31
+ /** The orientation of the toolbar. */
32
+ orientation: Orientation
33
+ }
34
+
35
+ export interface ToolbarProps
36
+ extends AriaToolbarProps,
37
+ ParentProps,
38
+ SlotProps {
39
+ /** The CSS className for the element. A function may be provided to receive render props. */
40
+ class?: string | ((renderProps: ToolbarRenderProps) => string)
41
+ /** The inline style for the element. A function may be provided to receive render props. */
42
+ style?: JSX.CSSProperties | ((renderProps: ToolbarRenderProps) => JSX.CSSProperties)
43
+ /** Additional data-* attributes. */
44
+ [key: `data-${string}`]: string | undefined
45
+ }
46
+
47
+ // ============================================
48
+ // CONTEXT
49
+ // ============================================
50
+
51
+ export interface ToolbarContextValue {
52
+ slots?: {
53
+ [name: string]: Record<string, unknown>
54
+ }
55
+ }
56
+
57
+ export const ToolbarContext = createContext<ToolbarContextValue | null>(null)
58
+
59
+ // ============================================
60
+ // TOOLBAR COMPONENT
61
+ // ============================================
62
+
63
+ /**
64
+ * A toolbar is a container for a set of interactive controls,
65
+ * such as buttons, checkboxes, or links, with arrow key navigation.
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * <Toolbar aria-label="Text formatting">
70
+ * <Button>Bold</Button>
71
+ * <Button>Italic</Button>
72
+ * <Button>Underline</Button>
73
+ * </Toolbar>
74
+ *
75
+ * // With render props
76
+ * <Toolbar orientation="vertical">
77
+ * {({ orientation }) => (
78
+ * <div data-orientation={orientation}>
79
+ * <Button>Cut</Button>
80
+ * <Button>Copy</Button>
81
+ * <Button>Paste</Button>
82
+ * </div>
83
+ * )}
84
+ * </Toolbar>
85
+ * ```
86
+ */
87
+ export function Toolbar(props: ToolbarProps): JSX.Element {
88
+ const [local, ariaProps, domProps] = splitProps(
89
+ props,
90
+ ['class', 'style', 'slot', 'children'],
91
+ ['orientation', 'aria-label', 'aria-labelledby']
92
+ )
93
+
94
+ // Get slot props from context if available
95
+ const ctx = useContext(ToolbarContext)
96
+ const slotProps = () => {
97
+ if (ctx?.slots && local.slot) {
98
+ return ctx.slots[local.slot] || {}
99
+ }
100
+ return {}
101
+ }
102
+
103
+ // Merge slot props with explicit props
104
+ const mergedAriaProps = createMemo(() => ({
105
+ orientation: ariaProps.orientation,
106
+ 'aria-label': ariaProps['aria-label'] ?? slotProps()['aria-label'] as string | undefined,
107
+ 'aria-labelledby': ariaProps['aria-labelledby'],
108
+ }))
109
+
110
+ // Create toolbar aria props
111
+ const { toolbarProps, orientation } = createToolbar(mergedAriaProps())
112
+
113
+ // Render props values
114
+ const renderValues = createMemo<ToolbarRenderProps>(() => ({
115
+ orientation: orientation(),
116
+ }))
117
+
118
+ // Resolve class
119
+ const resolvedClass = createMemo(() => {
120
+ const cls = local.class
121
+ if (typeof cls === 'function') {
122
+ return cls(renderValues())
123
+ }
124
+ return cls ?? 'solidaria-Toolbar'
125
+ })
126
+
127
+ // Resolve style
128
+ const resolvedStyle = createMemo(() => {
129
+ const style = local.style
130
+ if (typeof style === 'function') {
131
+ return style(renderValues())
132
+ }
133
+ return style
134
+ })
135
+
136
+ // Resolve children (support render props)
137
+ const resolvedChildren = createMemo(() => {
138
+ const children = props.children
139
+ if (typeof children === 'function') {
140
+ return (children as (props: ToolbarRenderProps) => JSX.Element)(renderValues())
141
+ }
142
+ return children
143
+ })
144
+
145
+ // Filter remaining DOM props
146
+ const filteredDOMProps = createMemo(() => filterDOMProps(domProps, { global: true }))
147
+
148
+ return (
149
+ <div
150
+ {...filteredDOMProps()}
151
+ {...toolbarProps}
152
+ class={resolvedClass()}
153
+ style={resolvedStyle()}
154
+ slot={local.slot}
155
+ data-orientation={orientation()}
156
+ >
157
+ {resolvedChildren()}
158
+ </div>
159
+ )
160
+ }