@proyecto-viviana/solidaria-components 0.1.3 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/Color.d.ts +6 -2
  2. package/dist/Color.d.ts.map +1 -1
  3. package/dist/ComboBox.d.ts +3 -3
  4. package/dist/ComboBox.d.ts.map +1 -1
  5. package/dist/GridList.d.ts +2 -2
  6. package/dist/GridList.d.ts.map +1 -1
  7. package/dist/ListBox.d.ts +5 -5
  8. package/dist/ListBox.d.ts.map +1 -1
  9. package/dist/Menu.d.ts +3 -3
  10. package/dist/Menu.d.ts.map +1 -1
  11. package/dist/Select.d.ts +3 -3
  12. package/dist/Select.d.ts.map +1 -1
  13. package/dist/Table.d.ts +2 -2
  14. package/dist/Table.d.ts.map +1 -1
  15. package/dist/Tabs.d.ts +1 -1
  16. package/dist/Tabs.d.ts.map +1 -1
  17. package/dist/index.js +15 -15
  18. package/dist/index.js.map +2 -2
  19. package/dist/index.jsx +9056 -0
  20. package/dist/index.jsx.map +7 -0
  21. package/dist/index.ssr.js +15 -15
  22. package/dist/index.ssr.js.map +2 -2
  23. package/package.json +8 -10
  24. package/src/Autocomplete.tsx +0 -174
  25. package/src/Breadcrumbs.tsx +0 -264
  26. package/src/Button.tsx +0 -238
  27. package/src/Calendar.tsx +0 -471
  28. package/src/Checkbox.tsx +0 -387
  29. package/src/Color.tsx +0 -1370
  30. package/src/ComboBox.tsx +0 -824
  31. package/src/DateField.tsx +0 -337
  32. package/src/DatePicker.tsx +0 -367
  33. package/src/Dialog.tsx +0 -262
  34. package/src/Disclosure.tsx +0 -439
  35. package/src/GridList.tsx +0 -511
  36. package/src/Landmark.tsx +0 -203
  37. package/src/Link.tsx +0 -201
  38. package/src/ListBox.tsx +0 -346
  39. package/src/Menu.tsx +0 -544
  40. package/src/Meter.tsx +0 -157
  41. package/src/Modal.tsx +0 -433
  42. package/src/NumberField.tsx +0 -542
  43. package/src/Popover.tsx +0 -540
  44. package/src/ProgressBar.tsx +0 -162
  45. package/src/RadioGroup.tsx +0 -356
  46. package/src/RangeCalendar.tsx +0 -462
  47. package/src/SearchField.tsx +0 -479
  48. package/src/Select.tsx +0 -734
  49. package/src/Separator.tsx +0 -130
  50. package/src/Slider.tsx +0 -500
  51. package/src/Switch.tsx +0 -213
  52. package/src/Table.tsx +0 -857
  53. package/src/Tabs.tsx +0 -552
  54. package/src/TagGroup.tsx +0 -421
  55. package/src/TextField.tsx +0 -271
  56. package/src/TimeField.tsx +0 -455
  57. package/src/Toast.tsx +0 -503
  58. package/src/Toolbar.tsx +0 -160
  59. package/src/Tooltip.tsx +0 -423
  60. package/src/Tree.tsx +0 -551
  61. package/src/VisuallyHidden.tsx +0 -60
  62. package/src/contexts.ts +0 -74
  63. package/src/index.ts +0 -620
  64. package/src/utils.tsx +0 -329
package/src/ComboBox.tsx DELETED
@@ -1,824 +0,0 @@
1
- /**
2
- * ComboBox component for solidaria-components
3
- *
4
- * A pre-wired headless combobox that combines state + aria hooks.
5
- * Port of react-aria-components/src/ComboBox.tsx
6
- */
7
-
8
- import {
9
- type JSX,
10
- type Accessor,
11
- createContext,
12
- createMemo,
13
- splitProps,
14
- useContext,
15
- For,
16
- Show,
17
- } from 'solid-js';
18
- import {
19
- createComboBox,
20
- createListBox,
21
- createOption,
22
- createHover,
23
- createInteractOutside,
24
- type AriaComboBoxProps,
25
- type AriaOptionProps,
26
- } from '@proyecto-viviana/solidaria';
27
- import {
28
- createComboBoxState,
29
- defaultContainsFilter,
30
- type ComboBoxState,
31
- type Key,
32
- type FilterFn,
33
- type MenuTriggerAction,
34
- } from '@proyecto-viviana/solid-stately';
35
- import {
36
- type RenderChildren,
37
- type ClassNameOrFunction,
38
- type StyleOrFunction,
39
- type SlotProps,
40
- useRenderProps,
41
- filterDOMProps,
42
- } from './utils';
43
-
44
- // ============================================
45
- // TYPES
46
- // ============================================
47
-
48
- export interface ComboBoxRenderProps {
49
- /** Whether the combobox listbox is open. */
50
- isOpen: boolean;
51
- /** Whether the combobox input is focused. */
52
- isFocused: boolean;
53
- /** Whether the combobox input has keyboard focus. */
54
- isFocusVisible: boolean;
55
- /** Whether the combobox is disabled. */
56
- isDisabled: boolean;
57
- /** Whether the combobox is required. */
58
- isRequired: boolean;
59
- /** Whether a value is selected. */
60
- isSelected: boolean;
61
- /** The current input value. */
62
- inputValue: string;
63
- }
64
-
65
- export interface ComboBoxProps<T>
66
- extends Omit<AriaComboBoxProps, 'children'>,
67
- SlotProps {
68
- /** The items to render in the combobox. */
69
- items: T[];
70
- /** Function to get the key from an item. */
71
- getKey?: (item: T) => Key;
72
- /** Function to get the text value from an item. */
73
- getTextValue?: (item: T) => string;
74
- /** Function to check if an item is disabled. */
75
- getDisabled?: (item: T) => boolean;
76
- /** Keys of disabled items. */
77
- disabledKeys?: Iterable<Key>;
78
- /** The currently selected key (controlled). */
79
- selectedKey?: Key | null;
80
- /** The default selected key (uncontrolled). */
81
- defaultSelectedKey?: Key | null;
82
- /** Handler called when selection changes. */
83
- onSelectionChange?: (key: Key | null) => void;
84
- /** The current input value (controlled). */
85
- inputValue?: string;
86
- /** The default input value (uncontrolled). */
87
- defaultInputValue?: string;
88
- /** Handler called when input value changes. */
89
- onInputChange?: (value: string) => void;
90
- /** Whether the combobox is open (controlled). */
91
- isOpen?: boolean;
92
- /** Whether the combobox is open by default (uncontrolled). */
93
- defaultOpen?: boolean;
94
- /** Handler called when the open state changes. */
95
- onOpenChange?: (isOpen: boolean, trigger?: MenuTriggerAction) => void;
96
- /** The filter function to use for filtering items. */
97
- defaultFilter?: FilterFn;
98
- /** Whether to allow custom values that don't match any item. */
99
- allowsCustomValue?: boolean;
100
- /** Whether to allow an empty collection (show listbox even with no matches). */
101
- allowsEmptyCollection?: boolean;
102
- /** The trigger mechanism for the combobox menu. */
103
- menuTrigger?: 'focus' | 'input' | 'manual';
104
- /** The name of the combobox, used when submitting an HTML form. */
105
- name?: string;
106
- /** The children of the component (compound components: ComboBoxInput, ComboBoxButton, ComboBoxListBox). */
107
- children: JSX.Element;
108
- /** The CSS className for the element. */
109
- class?: ClassNameOrFunction<ComboBoxRenderProps>;
110
- /** The inline style for the element. */
111
- style?: StyleOrFunction<ComboBoxRenderProps>;
112
- }
113
-
114
- export interface ComboBoxInputRenderProps {
115
- /** Whether the combobox is open. */
116
- isOpen: boolean;
117
- /** Whether the input is focused. */
118
- isFocused: boolean;
119
- /** Whether the input has keyboard focus. */
120
- isFocusVisible: boolean;
121
- /** Whether the input is hovered. */
122
- isHovered: boolean;
123
- /** Whether the input is disabled. */
124
- isDisabled: boolean;
125
- /** The current input value. */
126
- inputValue: string;
127
- }
128
-
129
- export interface ComboBoxInputProps extends SlotProps {
130
- /** The children of the input. */
131
- children?: RenderChildren<ComboBoxInputRenderProps>;
132
- /** The CSS className for the element. */
133
- class?: ClassNameOrFunction<ComboBoxInputRenderProps>;
134
- /** The inline style for the element. */
135
- style?: StyleOrFunction<ComboBoxInputRenderProps>;
136
- }
137
-
138
- export interface ComboBoxButtonRenderProps {
139
- /** Whether the combobox is open. */
140
- isOpen: boolean;
141
- /** Whether the button is focused. */
142
- isFocused: boolean;
143
- /** Whether the button is hovered. */
144
- isHovered: boolean;
145
- /** Whether the button is pressed. */
146
- isPressed: boolean;
147
- /** Whether the button is disabled. */
148
- isDisabled: boolean;
149
- }
150
-
151
- export interface ComboBoxButtonProps extends SlotProps {
152
- /** The children of the button. */
153
- children?: RenderChildren<ComboBoxButtonRenderProps>;
154
- /** The CSS className for the element. */
155
- class?: ClassNameOrFunction<ComboBoxButtonRenderProps>;
156
- /** The inline style for the element. */
157
- style?: StyleOrFunction<ComboBoxButtonRenderProps>;
158
- }
159
-
160
- export interface ComboBoxListBoxRenderProps {
161
- /** Whether the listbox is focused. */
162
- isFocused: boolean;
163
- }
164
-
165
- export interface ComboBoxListBoxProps<T> extends SlotProps {
166
- /** The children of the listbox. A function may be provided to render each item. */
167
- children?: (item: T) => JSX.Element;
168
- /** The CSS className for the element. */
169
- class?: ClassNameOrFunction<ComboBoxListBoxRenderProps>;
170
- /** The inline style for the element. */
171
- style?: StyleOrFunction<ComboBoxListBoxRenderProps>;
172
- }
173
-
174
- export interface ComboBoxOptionRenderProps {
175
- /** Whether the option is selected. */
176
- isSelected: boolean;
177
- /** Whether the option is focused. */
178
- isFocused: boolean;
179
- /** Whether the option has keyboard focus. */
180
- isFocusVisible: boolean;
181
- /** Whether the option is pressed. */
182
- isPressed: boolean;
183
- /** Whether the option is hovered. */
184
- isHovered: boolean;
185
- /** Whether the option is disabled. */
186
- isDisabled: boolean;
187
- }
188
-
189
- export interface ComboBoxOptionProps<T>
190
- extends Omit<AriaOptionProps, 'children' | 'key'>,
191
- SlotProps {
192
- /** The unique key for the option. */
193
- id: Key;
194
- /** The item value. */
195
- item?: T;
196
- /** The children of the option. A function may be provided to receive render props. */
197
- children?: RenderChildren<ComboBoxOptionRenderProps>;
198
- /** The CSS className for the element. */
199
- class?: ClassNameOrFunction<ComboBoxOptionRenderProps>;
200
- /** The inline style for the element. */
201
- style?: StyleOrFunction<ComboBoxOptionRenderProps>;
202
- /** The text value of the option (for typeahead). */
203
- textValue?: string;
204
- }
205
-
206
- // ============================================
207
- // CONTEXT
208
- // ============================================
209
-
210
- interface ComboBoxContextValue<T> {
211
- state: ComboBoxState<T>;
212
- inputProps: JSX.InputHTMLAttributes<HTMLInputElement>;
213
- buttonProps: JSX.HTMLAttributes<HTMLElement>;
214
- listBoxProps: JSX.HTMLAttributes<HTMLElement>;
215
- labelProps: JSX.HTMLAttributes<HTMLElement>;
216
- isOpen: Accessor<boolean>;
217
- isFocused: Accessor<boolean>;
218
- isFocusVisible: Accessor<boolean>;
219
- items: T[];
220
- inputRef: () => HTMLInputElement | null;
221
- setInputRef: (el: HTMLInputElement | null) => void;
222
- buttonRef: () => HTMLElement | null;
223
- setButtonRef: (el: HTMLElement | null) => void;
224
- }
225
-
226
- export const ComboBoxContext = createContext<ComboBoxContextValue<unknown> | null>(null);
227
- export const ComboBoxStateContext = createContext<ComboBoxState<unknown> | null>(null);
228
-
229
- // ============================================
230
- // COMPONENTS
231
- // ============================================
232
-
233
- /**
234
- * A combobox combines a text input with a listbox, allowing users to filter a list of options.
235
- */
236
- export function ComboBox<T>(props: ComboBoxProps<T>): JSX.Element {
237
- const [local, stateProps, ariaProps] = splitProps(
238
- props,
239
- ['class', 'style', 'slot'],
240
- [
241
- 'items',
242
- 'getKey',
243
- 'getTextValue',
244
- 'getDisabled',
245
- 'disabledKeys',
246
- 'selectedKey',
247
- 'defaultSelectedKey',
248
- 'onSelectionChange',
249
- 'inputValue',
250
- 'defaultInputValue',
251
- 'onInputChange',
252
- 'isOpen',
253
- 'defaultOpen',
254
- 'onOpenChange',
255
- 'defaultFilter',
256
- 'allowsCustomValue',
257
- 'allowsEmptyCollection',
258
- 'menuTrigger',
259
- 'name',
260
- ]
261
- );
262
-
263
- // Refs
264
- let inputRef: HTMLInputElement | null = null;
265
- let buttonRef: HTMLElement | null = null;
266
-
267
- // Create combobox state
268
- const state = createComboBoxState<T>({
269
- get items() {
270
- return stateProps.items;
271
- },
272
- get getKey() {
273
- return stateProps.getKey;
274
- },
275
- get getTextValue() {
276
- return stateProps.getTextValue;
277
- },
278
- get getDisabled() {
279
- return stateProps.getDisabled;
280
- },
281
- get disabledKeys() {
282
- return stateProps.disabledKeys;
283
- },
284
- get selectedKey() {
285
- return stateProps.selectedKey;
286
- },
287
- get defaultSelectedKey() {
288
- return stateProps.defaultSelectedKey;
289
- },
290
- get onSelectionChange() {
291
- return stateProps.onSelectionChange;
292
- },
293
- get inputValue() {
294
- return stateProps.inputValue;
295
- },
296
- get defaultInputValue() {
297
- return stateProps.defaultInputValue;
298
- },
299
- get onInputChange() {
300
- return stateProps.onInputChange;
301
- },
302
- get isOpen() {
303
- return stateProps.isOpen;
304
- },
305
- get defaultOpen() {
306
- return stateProps.defaultOpen;
307
- },
308
- get onOpenChange() {
309
- return stateProps.onOpenChange;
310
- },
311
- get defaultFilter() {
312
- return stateProps.defaultFilter;
313
- },
314
- get allowsCustomValue() {
315
- return stateProps.allowsCustomValue;
316
- },
317
- get allowsEmptyCollection() {
318
- return stateProps.allowsEmptyCollection;
319
- },
320
- get menuTrigger() {
321
- return stateProps.menuTrigger;
322
- },
323
- get isDisabled() {
324
- return ariaProps.isDisabled;
325
- },
326
- get isReadOnly() {
327
- return ariaProps.isReadOnly;
328
- },
329
- get isRequired() {
330
- return ariaProps.isRequired;
331
- },
332
- });
333
-
334
- // Create combobox aria props
335
- const comboBoxAria = createComboBox<T>(
336
- ariaProps,
337
- state,
338
- () => inputRef,
339
- () => buttonRef
340
- );
341
-
342
- // Create hover for wrapper
343
- const { isHovered, hoverProps } = createHover({
344
- get isDisabled() {
345
- return ariaProps.isDisabled;
346
- },
347
- });
348
-
349
- // Render props values
350
- const renderValues = createMemo<ComboBoxRenderProps>(() => ({
351
- isOpen: comboBoxAria.isOpen(),
352
- isFocused: comboBoxAria.isFocused(),
353
- isFocusVisible: comboBoxAria.isFocusVisible(),
354
- isDisabled: !!ariaProps.isDisabled,
355
- isRequired: !!ariaProps.isRequired,
356
- isSelected: state.selectedKey() != null,
357
- inputValue: state.inputValue(),
358
- }));
359
-
360
- // Resolve render props
361
- const renderProps = useRenderProps(
362
- {
363
- class: local.class,
364
- style: local.style,
365
- defaultClassName: 'solidaria-ComboBox',
366
- },
367
- renderValues
368
- );
369
-
370
- // Filter DOM props
371
- const domProps = createMemo(() => {
372
- const filtered = filterDOMProps(ariaProps as Record<string, unknown>, { global: true });
373
- return filtered;
374
- });
375
-
376
- // Remove ref from hover props
377
- const cleanHoverProps = () => {
378
- const { ref: _ref, ...rest } = hoverProps as Record<string, unknown>;
379
- return rest;
380
- };
381
-
382
- return (
383
- <ComboBoxContext.Provider
384
- value={{
385
- state,
386
- inputProps: comboBoxAria.inputProps,
387
- buttonProps: comboBoxAria.buttonProps,
388
- listBoxProps: comboBoxAria.listBoxProps,
389
- labelProps: comboBoxAria.labelProps,
390
- isOpen: comboBoxAria.isOpen,
391
- isFocused: comboBoxAria.isFocused,
392
- isFocusVisible: comboBoxAria.isFocusVisible,
393
- items: stateProps.items,
394
- inputRef: () => inputRef,
395
- setInputRef: (el) => { inputRef = el; },
396
- buttonRef: () => buttonRef,
397
- setButtonRef: (el) => { buttonRef = el; },
398
- }}
399
- >
400
- <ComboBoxStateContext.Provider value={state}>
401
- <div
402
- {...domProps()}
403
- {...cleanHoverProps()}
404
- class={renderProps.class()}
405
- style={renderProps.style()}
406
- data-open={comboBoxAria.isOpen() || undefined}
407
- data-focused={comboBoxAria.isFocused() || undefined}
408
- data-focus-visible={comboBoxAria.isFocusVisible() || undefined}
409
- data-disabled={ariaProps.isDisabled || undefined}
410
- data-required={ariaProps.isRequired || undefined}
411
- data-hovered={isHovered() || undefined}
412
- >
413
- {/* Hidden input for form submission */}
414
- <Show when={stateProps.name}>
415
- <input
416
- type="hidden"
417
- name={stateProps.name}
418
- value={state.selectedKey()?.toString() ?? ''}
419
- />
420
- </Show>
421
- {props.children}
422
- </div>
423
- </ComboBoxStateContext.Provider>
424
- </ComboBoxContext.Provider>
425
- );
426
- }
427
-
428
- /**
429
- * The text input for a combobox.
430
- */
431
- export function ComboBoxInput(props: ComboBoxInputProps): JSX.Element {
432
- const [local] = splitProps(props, ['class', 'style', 'slot']);
433
-
434
- // Get context
435
- const context = useContext(ComboBoxContext);
436
- if (!context) {
437
- throw new Error('ComboBoxInput must be used within a ComboBox');
438
- }
439
- const { inputProps, isOpen, isFocused, isFocusVisible, state, setInputRef } = context;
440
-
441
- // Create hover
442
- const { isHovered, hoverProps } = createHover({
443
- get isDisabled() {
444
- return state.isDisabled;
445
- },
446
- });
447
-
448
- // Render props values
449
- const renderValues = createMemo<ComboBoxInputRenderProps>(() => ({
450
- isOpen: isOpen(),
451
- isFocused: isFocused(),
452
- isFocusVisible: isFocusVisible(),
453
- isHovered: isHovered(),
454
- isDisabled: state.isDisabled,
455
- inputValue: state.inputValue(),
456
- }));
457
-
458
- // Resolve render props
459
- const renderProps = useRenderProps(
460
- {
461
- children: props.children,
462
- class: local.class,
463
- style: local.style,
464
- defaultClassName: 'solidaria-ComboBox-input',
465
- },
466
- renderValues
467
- );
468
-
469
- // Remove ref from spread props
470
- const cleanInputProps = () => {
471
- const { ref: _ref1, value: _value, ...rest } = inputProps as Record<string, unknown>;
472
- return rest;
473
- };
474
- const cleanHoverProps = () => {
475
- const { ref: _ref2, ...rest } = hoverProps as Record<string, unknown>;
476
- return rest;
477
- };
478
-
479
- return (
480
- <input
481
- ref={(el) => setInputRef(el)}
482
- {...cleanInputProps()}
483
- {...cleanHoverProps()}
484
- value={state.inputValue()}
485
- class={renderProps.class()}
486
- style={renderProps.style()}
487
- data-open={isOpen() || undefined}
488
- data-focused={isFocused() || undefined}
489
- data-focus-visible={isFocusVisible() || undefined}
490
- data-hovered={isHovered() || undefined}
491
- data-disabled={state.isDisabled || undefined}
492
- />
493
- );
494
- }
495
-
496
- /**
497
- * The trigger button for a combobox.
498
- */
499
- export function ComboBoxButton(props: ComboBoxButtonProps): JSX.Element {
500
- const [local] = splitProps(props, ['class', 'style', 'slot']);
501
-
502
- // Get context
503
- const context = useContext(ComboBoxContext);
504
- if (!context) {
505
- throw new Error('ComboBoxButton must be used within a ComboBox');
506
- }
507
- const { buttonProps, isOpen, isFocused, state, setButtonRef } = context;
508
-
509
- // Create hover
510
- const { isHovered, hoverProps } = createHover({
511
- get isDisabled() {
512
- return state.isDisabled;
513
- },
514
- });
515
-
516
- // Track pressed state
517
- let isPressed = false;
518
-
519
- // Render props values
520
- const renderValues = createMemo<ComboBoxButtonRenderProps>(() => ({
521
- isOpen: isOpen(),
522
- isFocused: isFocused(),
523
- isHovered: isHovered(),
524
- isPressed,
525
- isDisabled: state.isDisabled,
526
- }));
527
-
528
- // Resolve render props
529
- const renderProps = useRenderProps(
530
- {
531
- children: props.children,
532
- class: local.class,
533
- style: local.style,
534
- defaultClassName: 'solidaria-ComboBox-button',
535
- },
536
- renderValues
537
- );
538
-
539
- // Remove ref from spread props
540
- const cleanButtonProps = () => {
541
- const { ref: _ref1, ...rest } = buttonProps as Record<string, unknown>;
542
- return rest;
543
- };
544
- const cleanHoverProps = () => {
545
- const { ref: _ref2, ...rest } = hoverProps as Record<string, unknown>;
546
- return rest;
547
- };
548
-
549
- return (
550
- <button
551
- ref={(el) => setButtonRef(el)}
552
- {...cleanButtonProps()}
553
- {...cleanHoverProps()}
554
- class={renderProps.class()}
555
- style={renderProps.style()}
556
- data-open={isOpen() || undefined}
557
- data-focused={isFocused() || undefined}
558
- data-hovered={isHovered() || undefined}
559
- data-disabled={state.isDisabled || undefined}
560
- >
561
- {renderProps.renderChildren()}
562
- </button>
563
- );
564
- }
565
-
566
- /**
567
- * The listbox popup for a combobox.
568
- */
569
- export function ComboBoxListBox<T>(props: ComboBoxListBoxProps<T>): JSX.Element {
570
- const [local] = splitProps(props, ['class', 'style', 'slot']);
571
-
572
- // Get context
573
- const context = useContext(ComboBoxContext);
574
- if (!context) {
575
- throw new Error('ComboBoxListBox must be used within a ComboBox');
576
- }
577
- const { listBoxProps: contextListBoxProps, state: comboBoxState, isOpen, inputRef } = context;
578
- const state = comboBoxState as ComboBoxState<T>;
579
-
580
- // Ref for the listbox element (for click outside detection)
581
- let listBoxRef: HTMLUListElement | undefined;
582
-
583
- // Handle click outside to close combobox
584
- createInteractOutside({
585
- ref: () => listBoxRef ?? null,
586
- onInteractOutside: (e) => {
587
- // Don't close if clicking the input or button
588
- const target = e.target as HTMLElement;
589
- const input = inputRef();
590
- if (input?.contains(target)) {
591
- return;
592
- }
593
- if (isOpen()) {
594
- state.close();
595
- }
596
- },
597
- get isDisabled() {
598
- return !isOpen();
599
- },
600
- });
601
-
602
- // Create listbox aria props using ComboBoxState's ListState-compatible interface
603
- const { listBoxProps } = createListBox(
604
- {},
605
- {
606
- collection: state.collection,
607
- focusedKey: state.focusedKey,
608
- setFocusedKey: state.setFocusedKey,
609
- isFocused: state.isFocused,
610
- setFocused: state.setFocused,
611
- // Use state's built-in methods
612
- selectionMode: state.selectionMode,
613
- select: state.select,
614
- isSelected: state.isSelected,
615
- isDisabled: state.isKeyDisabled,
616
- // Additional ListState interface requirements
617
- selectedKeys: () => {
618
- const key = state.selectedKey();
619
- return key != null ? new Set([key]) : new Set();
620
- },
621
- disallowEmptySelection: () => true,
622
- toggleSelection: state.select,
623
- replaceSelection: state.select,
624
- extendSelection: () => {},
625
- selectAll: () => {},
626
- clearSelection: () => state.setSelectedKey(null),
627
- childFocusStrategy: () => null,
628
- } as any
629
- );
630
-
631
- // Render props values
632
- const renderValues = createMemo<ComboBoxListBoxRenderProps>(() => ({
633
- isFocused: state.isFocused(),
634
- }));
635
-
636
- // Resolve render props
637
- const renderProps = useRenderProps(
638
- {
639
- class: local.class,
640
- style: local.style,
641
- defaultClassName: 'solidaria-ComboBox-listbox',
642
- },
643
- renderValues
644
- );
645
-
646
- // Remove ref from spread props
647
- const cleanContextProps = () => {
648
- const { ref: _ref1, ...rest } = contextListBoxProps as Record<string, unknown>;
649
- return rest;
650
- };
651
- const cleanListBoxProps = () => {
652
- const { ref: _ref2, ...rest } = listBoxProps as Record<string, unknown>;
653
- return rest;
654
- };
655
-
656
- const items = () => Array.from(state.collection());
657
-
658
- // Prevent focus from being lost when clicking in the listbox
659
- // This is critical - if we don't prevent default, the input loses focus
660
- // and the blur handler closes the menu before the click can be processed
661
- // We need to attach this in the ref callback to use capture phase
662
- const setupMouseDownHandler = (el: HTMLUListElement) => {
663
- listBoxRef = el;
664
- if (el) {
665
- const mouseHandler = (e: MouseEvent) => {
666
- e.preventDefault();
667
- };
668
- const pointerHandler = (e: PointerEvent) => {
669
- e.preventDefault();
670
- };
671
- el.addEventListener('mousedown', mouseHandler, true); // capture phase
672
- el.addEventListener('pointerdown', pointerHandler, true); // capture phase
673
- }
674
- };
675
-
676
- return (
677
- <Show when={isOpen()}>
678
- <ul
679
- ref={setupMouseDownHandler}
680
- {...cleanContextProps()}
681
- {...cleanListBoxProps()}
682
- class={renderProps.class()}
683
- style={renderProps.style()}
684
- data-focused={state.isFocused() || undefined}
685
- >
686
- <Show when={props.children} fallback={
687
- <For each={items()}>
688
- {(node) => (
689
- <ComboBoxOption id={node.key}>
690
- {node.textValue}
691
- </ComboBoxOption>
692
- )}
693
- </For>
694
- }>
695
- <For each={items()}>
696
- {(node) => node.value != null ? props.children!(node.value) : null}
697
- </For>
698
- </Show>
699
- </ul>
700
- </Show>
701
- );
702
- }
703
-
704
- /**
705
- * An option in a combobox listbox.
706
- */
707
- export function ComboBoxOption<T>(props: ComboBoxOptionProps<T>): JSX.Element {
708
- const [local, ariaProps] = splitProps(props, [
709
- 'class',
710
- 'style',
711
- 'slot',
712
- 'id',
713
- 'item',
714
- 'textValue',
715
- ]);
716
-
717
- // Get state from context
718
- const context = useContext(ComboBoxStateContext);
719
- if (!context) {
720
- throw new Error('ComboBoxOption must be used within a ComboBox');
721
- }
722
- const state = context as ComboBoxState<T>;
723
-
724
- // Create option aria props using ComboBoxState's ListState-compatible interface
725
- const optionAria = createOption<T>(
726
- {
727
- key: local.id,
728
- get isDisabled() {
729
- return ariaProps.isDisabled;
730
- },
731
- get 'aria-label'() {
732
- return ariaProps['aria-label'];
733
- },
734
- },
735
- {
736
- collection: state.collection,
737
- focusedKey: state.focusedKey,
738
- setFocusedKey: state.setFocusedKey,
739
- isFocused: state.isFocused,
740
- setFocused: state.setFocused,
741
- // Use state's built-in methods
742
- selectionMode: state.selectionMode,
743
- select: state.select,
744
- isSelected: state.isSelected,
745
- isDisabled: state.isKeyDisabled,
746
- // Additional ListState interface requirements
747
- selectedKeys: () => {
748
- const key = state.selectedKey();
749
- return key != null ? new Set([key]) : new Set();
750
- },
751
- disallowEmptySelection: () => true,
752
- toggleSelection: state.select,
753
- replaceSelection: state.select,
754
- extendSelection: () => {},
755
- selectAll: () => {},
756
- clearSelection: () => state.setSelectedKey(null),
757
- childFocusStrategy: () => null,
758
- } as any
759
- );
760
-
761
- // Create hover
762
- const { isHovered, hoverProps } = createHover({
763
- get isDisabled() {
764
- return optionAria.isDisabled();
765
- },
766
- });
767
-
768
- // Render props values
769
- const renderValues = createMemo<ComboBoxOptionRenderProps>(() => ({
770
- isSelected: optionAria.isSelected(),
771
- isFocused: optionAria.isFocused(),
772
- isFocusVisible: optionAria.isFocusVisible(),
773
- isPressed: optionAria.isPressed(),
774
- isHovered: isHovered(),
775
- isDisabled: optionAria.isDisabled(),
776
- }));
777
-
778
- // Resolve render props
779
- const renderProps = useRenderProps(
780
- {
781
- children: props.children,
782
- class: local.class,
783
- style: local.style,
784
- defaultClassName: 'solidaria-ComboBox-option',
785
- },
786
- renderValues
787
- );
788
-
789
- // Remove ref from spread props
790
- const cleanOptionProps = () => {
791
- const { ref: _ref1, ...rest } = optionAria.optionProps as Record<string, unknown>;
792
- return rest;
793
- };
794
- const cleanHoverProps = () => {
795
- const { ref: _ref2, ...rest } = hoverProps as Record<string, unknown>;
796
- return rest;
797
- };
798
-
799
- return (
800
- <li
801
- {...cleanOptionProps()}
802
- {...cleanHoverProps()}
803
- class={renderProps.class()}
804
- style={renderProps.style()}
805
- data-selected={optionAria.isSelected() || undefined}
806
- data-focused={optionAria.isFocused() || undefined}
807
- data-focus-visible={optionAria.isFocusVisible() || undefined}
808
- data-pressed={optionAria.isPressed() || undefined}
809
- data-hovered={isHovered() || undefined}
810
- data-disabled={optionAria.isDisabled() || undefined}
811
- >
812
- {renderProps.renderChildren()}
813
- </li>
814
- );
815
- }
816
-
817
- // Attach sub-components
818
- ComboBox.Input = ComboBoxInput;
819
- ComboBox.Button = ComboBoxButton;
820
- ComboBox.ListBox = ComboBoxListBox;
821
- ComboBox.Option = ComboBoxOption;
822
-
823
- // Re-export filter function for convenience
824
- export { defaultContainsFilter };