@human-kit/svelte-components 1.0.0-alpha.1 → 1.0.0-alpha.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 (92) hide show
  1. package/dist/calendar/README.md +65 -0
  2. package/dist/calendar/TODO.md +109 -0
  3. package/dist/calendar/body-cell/calendar-body-cell.svelte +155 -0
  4. package/dist/calendar/body-cell/calendar-body-cell.svelte.d.ts +9 -0
  5. package/dist/calendar/grid/calendar-grid-month-scope.svelte +16 -0
  6. package/dist/calendar/grid/calendar-grid-month-scope.svelte.d.ts +8 -0
  7. package/dist/calendar/grid/calendar-grid.svelte +45 -0
  8. package/dist/calendar/grid/calendar-grid.svelte.d.ts +8 -0
  9. package/dist/calendar/grid/month-scope.d.ts +2 -0
  10. package/dist/calendar/grid/month-scope.js +8 -0
  11. package/dist/calendar/grid-body/calendar-grid-body-custom-test.svelte +13 -0
  12. package/dist/calendar/grid-body/calendar-grid-body-custom-test.svelte.d.ts +18 -0
  13. package/dist/calendar/grid-body/calendar-grid-body.svelte +36 -0
  14. package/dist/calendar/grid-body/calendar-grid-body.svelte.d.ts +8 -0
  15. package/dist/calendar/grid-header/calendar-grid-header-custom-test.svelte +13 -0
  16. package/dist/calendar/grid-header/calendar-grid-header-custom-test.svelte.d.ts +18 -0
  17. package/dist/calendar/grid-header/calendar-grid-header.svelte +31 -0
  18. package/dist/calendar/grid-header/calendar-grid-header.svelte.d.ts +8 -0
  19. package/dist/calendar/header-cell/calendar-header-cell-test.svelte +11 -0
  20. package/dist/calendar/header-cell/calendar-header-cell-test.svelte.d.ts +18 -0
  21. package/dist/calendar/header-cell/calendar-header-cell.svelte +16 -0
  22. package/dist/calendar/header-cell/calendar-header-cell.svelte.d.ts +8 -0
  23. package/dist/calendar/heading/calendar-heading.svelte +17 -0
  24. package/dist/calendar/heading/calendar-heading.svelte.d.ts +5 -0
  25. package/dist/calendar/index.d.ts +13 -0
  26. package/dist/calendar/index.js +13 -0
  27. package/dist/calendar/index.parts.d.ts +9 -0
  28. package/dist/calendar/index.parts.js +9 -0
  29. package/dist/calendar/root/calendar-root-bind-value-test.svelte +14 -0
  30. package/dist/calendar/root/calendar-root-bind-value-test.svelte.d.ts +3 -0
  31. package/dist/calendar/root/calendar-root-controlled-clear-test.svelte +20 -0
  32. package/dist/calendar/root/calendar-root-controlled-clear-test.svelte.d.ts +3 -0
  33. package/dist/calendar/root/calendar-root-test.svelte +67 -0
  34. package/dist/calendar/root/calendar-root-test.svelte.d.ts +12 -0
  35. package/dist/calendar/root/calendar-root.svelte +140 -0
  36. package/dist/calendar/root/calendar-root.svelte.d.ts +30 -0
  37. package/dist/calendar/root/context.d.ts +62 -0
  38. package/dist/calendar/root/context.js +724 -0
  39. package/dist/calendar/root/date-utils.d.ts +17 -0
  40. package/dist/calendar/root/date-utils.js +104 -0
  41. package/dist/calendar/trigger-next/calendar-trigger-next.svelte +38 -0
  42. package/dist/calendar/trigger-next/calendar-trigger-next.svelte.d.ts +8 -0
  43. package/dist/calendar/trigger-previous/calendar-trigger-previous.svelte +38 -0
  44. package/dist/calendar/trigger-previous/calendar-trigger-previous.svelte.d.ts +8 -0
  45. package/dist/combobox/README.md +40 -0
  46. package/dist/combobox/TODO.md +92 -92
  47. package/dist/combobox/button/README.md +15 -0
  48. package/dist/combobox/input/README.md +16 -0
  49. package/dist/combobox/item/README.md +27 -0
  50. package/dist/combobox/item-indicator/README.md +15 -0
  51. package/dist/combobox/list/README.md +27 -0
  52. package/dist/combobox/popover/README.md +13 -0
  53. package/dist/combobox/root/README.md +44 -0
  54. package/dist/combobox/tag/README.md +37 -0
  55. package/dist/combobox/tag-remove/README.md +14 -0
  56. package/dist/combobox/tags/README.md +23 -0
  57. package/dist/dialog/README.md +35 -0
  58. package/dist/dialog/content/README.md +16 -0
  59. package/dist/dialog/overlay/README.md +13 -0
  60. package/dist/dialog/portal/README.md +12 -0
  61. package/dist/dialog/root/README.md +53 -0
  62. package/dist/dialog/trigger/README.md +12 -0
  63. package/dist/dialog/trigger/dialog-trigger-multi-button-test.svelte +19 -0
  64. package/dist/dialog/trigger/dialog-trigger-multi-button-test.svelte.d.ts +18 -0
  65. package/dist/dialog/trigger/dialog-trigger.svelte +18 -6
  66. package/dist/index.d.ts +4 -0
  67. package/dist/index.js +4 -0
  68. package/dist/listbox/README.md +26 -0
  69. package/dist/listbox/item/README.md +24 -0
  70. package/dist/listbox/root/README.md +40 -0
  71. package/dist/locale-provider/context.d.ts +8 -0
  72. package/dist/locale-provider/context.js +18 -0
  73. package/dist/locale-provider/index.d.ts +4 -0
  74. package/dist/locale-provider/index.js +4 -0
  75. package/dist/locale-provider/locale-provider-initial-value-test.svelte +15 -0
  76. package/dist/locale-provider/locale-provider-initial-value-test.svelte.d.ts +7 -0
  77. package/dist/locale-provider/locale-provider-test.svelte +20 -0
  78. package/dist/locale-provider/locale-provider-test.svelte.d.ts +6 -0
  79. package/dist/locale-provider/locale-provider-value-probe.svelte +22 -0
  80. package/dist/locale-provider/locale-provider-value-probe.svelte.d.ts +6 -0
  81. package/dist/locale-provider/locale-provider.svelte +23 -0
  82. package/dist/locale-provider/locale-provider.svelte.d.ts +8 -0
  83. package/dist/popover/README.md +32 -0
  84. package/dist/popover/content/README.md +25 -0
  85. package/dist/popover/root/README.md +30 -0
  86. package/dist/popover/trigger/README.md +23 -0
  87. package/dist/popover/trigger/popover-trigger-button.svelte +6 -3
  88. package/dist/popover/trigger/popover-trigger-button.svelte.d.ts +2 -3
  89. package/dist/popover/trigger/popover-trigger-multi-button-test.svelte +16 -0
  90. package/dist/popover/trigger/popover-trigger-multi-button-test.svelte.d.ts +18 -0
  91. package/dist/popover/trigger/popover-trigger.svelte +18 -6
  92. package/package.json +11 -1
@@ -0,0 +1,17 @@
1
+ export type CalendarDateValue = string;
2
+ export declare function isValidCalendarDateValue(value: string): boolean;
3
+ export declare function parseCalendarDate(value: CalendarDateValue): Date | null;
4
+ export declare function formatCalendarDate(date: Date): CalendarDateValue;
5
+ export declare function startOfMonth(date: Date): Date;
6
+ export declare function addMonths(date: Date, amount: number): Date;
7
+ export declare function addDays(date: Date, amount: number): Date;
8
+ export declare function compareDates(a: Date, b: Date): number;
9
+ export declare function getTodayUtcDate(): Date;
10
+ export declare function getFirstDayOfWeek(locale: string): number;
11
+ export declare function getWeekdayLabels(locale: string, firstDayOfWeek: number): string[];
12
+ export type CalendarDayCell = {
13
+ date: CalendarDateValue;
14
+ isOutsideMonth: boolean;
15
+ };
16
+ export declare function buildMonthGrid(monthStart: Date, firstDayOfWeek: number): CalendarDayCell[][];
17
+ export declare function formatMonthHeading(date: Date, locale: string): string;
@@ -0,0 +1,104 @@
1
+ const DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
2
+ export function isValidCalendarDateValue(value) {
3
+ const match = DATE_RE.exec(value);
4
+ if (!match)
5
+ return false;
6
+ const year = Number(match[1]);
7
+ const month = Number(match[2]);
8
+ const day = Number(match[3]);
9
+ if (month < 1 || month > 12 || day < 1 || day > 31)
10
+ return false;
11
+ const date = new Date(Date.UTC(year, month - 1, day));
12
+ return (date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day);
13
+ }
14
+ export function parseCalendarDate(value) {
15
+ if (!isValidCalendarDateValue(value))
16
+ return null;
17
+ const [year, month, day] = value.split('-').map(Number);
18
+ return new Date(Date.UTC(year, month - 1, day));
19
+ }
20
+ export function formatCalendarDate(date) {
21
+ const year = date.getUTCFullYear();
22
+ const month = String(date.getUTCMonth() + 1).padStart(2, '0');
23
+ const day = String(date.getUTCDate()).padStart(2, '0');
24
+ return `${year}-${month}-${day}`;
25
+ }
26
+ export function startOfMonth(date) {
27
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1));
28
+ }
29
+ function getDaysInMonthUtc(year, monthIndex) {
30
+ return new Date(Date.UTC(year, monthIndex + 1, 0)).getUTCDate();
31
+ }
32
+ export function addMonths(date, amount) {
33
+ const targetMonth = date.getUTCMonth() + amount;
34
+ const targetYear = date.getUTCFullYear() + Math.floor(targetMonth / 12);
35
+ const normalizedMonth = ((targetMonth % 12) + 12) % 12;
36
+ const targetDay = Math.min(date.getUTCDate(), getDaysInMonthUtc(targetYear, normalizedMonth));
37
+ return new Date(Date.UTC(targetYear, normalizedMonth, targetDay));
38
+ }
39
+ export function addDays(date, amount) {
40
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + amount));
41
+ }
42
+ export function compareDates(a, b) {
43
+ const at = a.getTime();
44
+ const bt = b.getTime();
45
+ if (at < bt)
46
+ return -1;
47
+ if (at > bt)
48
+ return 1;
49
+ return 0;
50
+ }
51
+ export function getTodayUtcDate() {
52
+ const now = new Date();
53
+ return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
54
+ }
55
+ export function getFirstDayOfWeek(locale) {
56
+ try {
57
+ const intlLocale = new Intl.Locale(locale);
58
+ const weekInfo = intlLocale.weekInfo;
59
+ if (weekInfo?.firstDay) {
60
+ return weekInfo.firstDay % 7;
61
+ }
62
+ }
63
+ catch {
64
+ // ignore and fallback to sunday
65
+ }
66
+ return 0;
67
+ }
68
+ export function getWeekdayLabels(locale, firstDayOfWeek) {
69
+ const formatter = new Intl.DateTimeFormat(locale, {
70
+ weekday: 'short',
71
+ timeZone: 'UTC'
72
+ });
73
+ const sunday = new Date(Date.UTC(2024, 0, 7));
74
+ return Array.from({ length: 7 }, (_, index) => {
75
+ const dayOffset = (firstDayOfWeek + index) % 7;
76
+ return formatter.format(addDays(sunday, dayOffset));
77
+ });
78
+ }
79
+ export function buildMonthGrid(monthStart, firstDayOfWeek) {
80
+ const firstOfMonth = startOfMonth(monthStart);
81
+ const firstWeekday = firstOfMonth.getUTCDay();
82
+ const startOffset = (firstWeekday - firstDayOfWeek + 7) % 7;
83
+ const gridStart = addDays(firstOfMonth, -startOffset);
84
+ const weeks = [];
85
+ for (let weekIndex = 0; weekIndex < 6; weekIndex++) {
86
+ const week = [];
87
+ for (let dayIndex = 0; dayIndex < 7; dayIndex++) {
88
+ const date = addDays(gridStart, weekIndex * 7 + dayIndex);
89
+ week.push({
90
+ date: formatCalendarDate(date),
91
+ isOutsideMonth: date.getUTCMonth() !== firstOfMonth.getUTCMonth()
92
+ });
93
+ }
94
+ weeks.push(week);
95
+ }
96
+ return weeks;
97
+ }
98
+ export function formatMonthHeading(date, locale) {
99
+ return new Intl.DateTimeFormat(locale, {
100
+ month: 'long',
101
+ year: 'numeric',
102
+ timeZone: 'UTC'
103
+ }).format(date);
104
+ }
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLButtonAttributes } from 'svelte/elements';
4
+ import { useCalendarContext } from '../root/context';
5
+
6
+ type CalendarTriggerNextProps = Omit<HTMLButtonAttributes, 'children'> & {
7
+ children?: Snippet;
8
+ };
9
+
10
+ let { children, class: className = '', ...restProps }: CalendarTriggerNextProps = $props();
11
+
12
+ const calendar = useCalendarContext();
13
+ const layoutVersion = calendar.layoutVersion;
14
+ const isDisabled = $derived.by(() => {
15
+ void $layoutVersion;
16
+ return calendar.isDisabled;
17
+ });
18
+
19
+ function handleClick() {
20
+ if (isDisabled) return;
21
+ calendar.goToNextPage();
22
+ }
23
+ </script>
24
+
25
+ <button
26
+ type="button"
27
+ class={className}
28
+ aria-label="Next page"
29
+ disabled={isDisabled}
30
+ onclick={handleClick}
31
+ {...restProps}
32
+ >
33
+ {#if children}
34
+ {@render children()}
35
+ {:else}
36
+ Next
37
+ {/if}
38
+ </button>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLButtonAttributes } from 'svelte/elements';
3
+ type CalendarTriggerNextProps = Omit<HTMLButtonAttributes, 'children'> & {
4
+ children?: Snippet;
5
+ };
6
+ declare const CalendarTriggerNext: import("svelte").Component<CalendarTriggerNextProps, {}, "">;
7
+ type CalendarTriggerNext = ReturnType<typeof CalendarTriggerNext>;
8
+ export default CalendarTriggerNext;
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLButtonAttributes } from 'svelte/elements';
4
+ import { useCalendarContext } from '../root/context';
5
+
6
+ type CalendarTriggerPreviousProps = Omit<HTMLButtonAttributes, 'children'> & {
7
+ children?: Snippet;
8
+ };
9
+
10
+ let { children, class: className = '', ...restProps }: CalendarTriggerPreviousProps = $props();
11
+
12
+ const calendar = useCalendarContext();
13
+ const layoutVersion = calendar.layoutVersion;
14
+ const isDisabled = $derived.by(() => {
15
+ void $layoutVersion;
16
+ return calendar.isDisabled;
17
+ });
18
+
19
+ function handleClick() {
20
+ if (isDisabled) return;
21
+ calendar.goToPreviousPage();
22
+ }
23
+ </script>
24
+
25
+ <button
26
+ type="button"
27
+ class={className}
28
+ aria-label="Previous page"
29
+ disabled={isDisabled}
30
+ onclick={handleClick}
31
+ {...restProps}
32
+ >
33
+ {#if children}
34
+ {@render children()}
35
+ {:else}
36
+ Prev
37
+ {/if}
38
+ </button>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLButtonAttributes } from 'svelte/elements';
3
+ type CalendarTriggerPreviousProps = Omit<HTMLButtonAttributes, 'children'> & {
4
+ children?: Snippet;
5
+ };
6
+ declare const CalendarTriggerPrevious: import("svelte").Component<CalendarTriggerPreviousProps, {}, "">;
7
+ type CalendarTriggerPrevious = ReturnType<typeof CalendarTriggerPrevious>;
8
+ export default CalendarTriggerPrevious;
@@ -0,0 +1,40 @@
1
+ # ComboBox
2
+
3
+ ## Description
4
+
5
+ `ComboBox` combines text input, popover, and listbox behavior into a single accessible selection pattern. It supports single and multiple selection, controlled and uncontrolled state, and keyboard-first interaction.
6
+
7
+ ## Usage guidelines
8
+
9
+ - Wrap all parts in `ComboBox.Root`.
10
+ - Use controlled props (`value`, `inputValue`, `isOpen`) only when external state management is needed.
11
+ - Provide a stable `id` in SSR environments to keep ARIA ids deterministic.
12
+ - Render `ComboBox.Tags`, `ComboBox.Tag`, and `ComboBox.TagRemove` in multiple mode to expose selected values.
13
+ - Choose `trigger="focus"`, `trigger="input"`, or `trigger="press"` based on your opening behavior requirements.
14
+
15
+ ## Anatomy
16
+
17
+ Import the component and compose its parts:
18
+
19
+ ```svelte
20
+ <ComboBox.Root>
21
+ <ComboBox.Input />
22
+ <ComboBox.Button />
23
+ <ComboBox.Popover>
24
+ <ComboBox.List>
25
+ <ComboBox.Item id="1">Option 1</ComboBox.Item>
26
+ </ComboBox.List>
27
+ </ComboBox.Popover>
28
+ </ComboBox.Root>
29
+ ```
30
+
31
+ - `ComboBox.Root`
32
+ - `ComboBox.Input`
33
+ - `ComboBox.Button`
34
+ - `ComboBox.Popover`
35
+ - `ComboBox.List`
36
+ - `ComboBox.Item`
37
+ - `ComboBox.ItemIndicator`
38
+ - `ComboBox.Tags`
39
+ - `ComboBox.Tag`
40
+ - `ComboBox.TagRemove`
@@ -4,134 +4,134 @@ Comprehensive review based on: **Accessibility**, **Scalability**, **Performance
4
4
 
5
5
  ---
6
6
 
7
- ## 🔊 Accesibilidad
7
+ ## Accessibility
8
8
 
9
- ### Completado
9
+ ### Completed (Accessibility)
10
10
 
11
- - [x] ARIA pattern: `aria-activedescendant` para virtual focus
12
- - [x] `aria-expanded`, `aria-haspopup`, `aria-controls` en input
13
- - [x] `aria-label` en ListBox
11
+ - [x] ARIA pattern: `aria-activedescendant` for virtual focus
12
+ - [x] `aria-expanded`, `aria-haspopup`, `aria-controls` on input
13
+ - [x] `aria-label` on ListBox
14
14
  - [x] `role="combobox"`, `role="listbox"`, `role="option"`
15
- - [x] `aria-selected` en items seleccionados
16
- - [x] `aria-disabled` en items/placeholder deshabilitados
17
- - [x] Input soporta `aria-label` y `aria-labelledby` props
18
- - [x] ListBox tiene ID para que `aria-controls` funcione correctamente
19
- - [x] Button tiene `aria-controls` apuntando al listbox
20
- - [x] Wrapper group soporta `aria-label` y `aria-labelledby`
21
- - [x] Input soporta `aria-describedby` para instrucciones de uso
15
+ - [x] `aria-selected` on selected items
16
+ - [x] `aria-disabled` on disabled items/placeholder
17
+ - [x] Input supports `aria-label` and `aria-labelledby` props
18
+ - [x] ListBox has an ID so `aria-controls` works correctly
19
+ - [x] Button has `aria-controls` pointing to the listbox
20
+ - [x] Wrapper group supports `aria-label` and `aria-labelledby`
21
+ - [x] Input supports `aria-describedby` for usage instructions
22
22
 
23
- ### Pendiente
23
+ ### Pending (Accessibility)
24
24
 
25
- - [ ] **Live regions para conteo de resultados**
26
- - Agregar `<div aria-live="polite">` que anuncie "{N} resultados disponibles" al filtrar
27
- - Importante para screen readers que no ven el cambio visual
25
+ - [ ] **Live regions for result count**
26
+ - Add `<div aria-live="polite">` to announce "{N} results available" while filtering
27
+ - Important for screen readers that cannot see visual updates
28
28
 
29
- - [ ] **Anuncio de selección**
30
- - Anunciar "Item seleccionado: {label}" cuando se selecciona
31
- - Usar `aria-live="assertive"` para cambios importantes
29
+ - [ ] **Selection announcement**
30
+ - Announce "Selected item: {label}" when an item is selected
31
+ - Use `aria-live="assertive"` for important changes
32
32
 
33
- - [ ] **Soporte para grupos (sections)**
34
- - Implementar `role="group"` con `aria-labelledby` para secciones
35
- - Agregar `ComboBox.Section` component
33
+ - [ ] **Support for groups (sections)**
34
+ - Implement `role="group"` with `aria-labelledby` for sections
35
+ - Add `ComboBox.Section` component
36
36
 
37
37
  ---
38
38
 
39
- ## 📈 Escalabilidad
39
+ ## Scalability
40
40
 
41
- ### Completado
41
+ ### Completed (Scalability)
42
42
 
43
- - [x] Hook `useVirtualFocus` reutilizable
43
+ - [x] Reusable `useVirtualFocus` hook
44
44
  - [x] Controlled/uncontrolled mode
45
- - [x] Filtrado automático en items
46
- - [x] `emptyPlaceholder` reactivo
45
+ - [x] Automatic item filtering
46
+ - [x] Reactive `emptyPlaceholder`
47
47
 
48
- ### Pendiente
48
+ ### Pending (Scalability)
49
49
 
50
- - [ ] **`filterFn` prop customizable**
51
- - Actualmente filtrado es case-insensitive includes
52
- - Permitir: fuzzy search, startsWith, exact match, async search
50
+ - [ ] **Customizable `filterFn` prop**
51
+ - Filtering is currently case-insensitive includes
52
+ - Allow: fuzzy search, startsWith, exact match, async search
53
53
 
54
54
  - [ ] **`allowCreate` prop**
55
- - Permitir crear nuevos items cuando no hay match
55
+ - Allow creating new items when there is no match
56
56
  - Callback `onCreate?: (value: string) => void`
57
57
 
58
58
  - [x] **Multiple selection UI**
59
- - Chips/tags para items seleccionados ✅ `ComboBox.Tags`, `ComboBox.Tag`, `ComboBox.TagRemove`
60
- - Clear all button (disponible via `clearSelection()` en context)
61
- - Contador de seleccionados (disponible via `selectedValue.size`)
62
- - Navegación de tags con teclado (ArrowLeft/Right, Delete/Backspace)
63
- - `ComboBox.ItemIndicator` para mostrar checks en items seleccionados
59
+ - Chips/tags for selected items: `ComboBox.Tags`, `ComboBox.Tag`, `ComboBox.TagRemove`
60
+ - Clear-all button (available via `clearSelection()` in context)
61
+ - Selected-count indicator (available via `selectedValue.size`)
62
+ - Keyboard tag navigation (ArrowLeft/Right, Delete/Backspace)
63
+ - `ComboBox.ItemIndicator` to show checks for selected items
64
64
 
65
65
  - [ ] **Form integration**
66
- - `name` prop para `<form>` nativo
67
- - Hidden input con valor serializado
68
- - Validación con `required`, `aria-invalid`
66
+ - `name` prop for native `<form>`
67
+ - Hidden input with serialized value
68
+ - Validation with `required`, `aria-invalid`
69
69
 
70
70
  - [ ] **Async data support**
71
71
  - Props: `isLoading`, `loadingPlaceholder`
72
- - Callback: `onLoadMore` para infinite scroll
73
- - Debounce integrado para búsqueda async
72
+ - Callback: `onLoadMore` for infinite scroll
73
+ - Built-in debounce for async search
74
74
 
75
75
  - [ ] **Virtualization**
76
- - Para listas grandes (>100 items)
77
- - Integrar con `@tanstack/virtual` o similar
76
+ - For large lists (>100 items)
77
+ - Integrate with `@tanstack/virtual` or similar
78
78
 
79
79
  ---
80
80
 
81
- ## Performance
81
+ ## Performance
82
82
 
83
- ### Completado
83
+ ### Completed (Performance)
84
84
 
85
- - [x] Cache de DOM queries con invalidación (`cachedItemOrder`)
86
- - [x] `untrack()` para evitar loops infinitos en effects
87
- - [x] Subscription pattern para `itemCount` reactivo
85
+ - [x] DOM query cache with invalidation (`cachedItemOrder`)
86
+ - [x] `untrack()` to avoid infinite effect loops
87
+ - [x] Subscription pattern for reactive `itemCount`
88
88
  - [x] Scoped queries via `containerRef`
89
89
 
90
- ### Pendiente
90
+ ### Pending (Performance)
91
91
 
92
- - [ ] **Memoización de `isVisible` en ListBoxItem**
93
- - Actualmente se recalcula en cada render
94
- - Considerar memoizar con `$derived` más granular
92
+ - [ ] **`isVisible` memoization in ListBoxItem**
93
+ - It is currently recomputed on each render
94
+ - Consider more granular memoization with `$derived`
95
95
 
96
96
  - [ ] **Batch registration**
97
- - `registerItem` se llama por cada item individualmente
98
- - Para listas grandes, batch notifications
97
+ - `registerItem` runs once per individual item
98
+ - For large lists, consider batched notifications
99
99
 
100
- - [ ] **Lazy itemLabels**
101
- - El Map `itemLabels` crece con cada item
102
- - Limpiar en unmount está implementado, pero considerar WeakMap
100
+ - [ ] **Lazy `itemLabels`**
101
+ - The `itemLabels` map grows with each item
102
+ - Cleanup on unmount is implemented, but consider `WeakMap`
103
103
 
104
104
  - [ ] **Effect cleanup optimizations**
105
- - Revisar effects que podrían consolidarse
106
- - `combobox-listboxitem.svelte` tiene 2 effects que podrían ser 1
105
+ - Review effects that could be consolidated
106
+ - `combobox-listboxitem.svelte` has 2 effects that might become 1
107
107
 
108
108
  ---
109
109
 
110
- ## 🔧 Svelte 5 Runes Best Practices
110
+ ## Svelte 5 Runes Best Practices
111
111
 
112
- ### Completado
112
+ ### Completed (Runes)
113
113
 
114
- - [x] `$state` para estado reactivo
115
- - [x] `$derived` para valores computados
116
- - [x] `$effect` con cleanup functions
117
- - [x] `$bindable` para two-way binding
118
- - [x] `$props()` para destructuring
119
- - [x] `untrack()` para evitar re-runs innecesarios
120
- - [x] `$derived(expression)` en vez de `$derived(() => ...)` - Simplificado en `combobox-listboxitem.svelte`
121
- - [x] Effects consolidados - Usando 1 `$effect` + `onDestroy` en vez de 2 effects
114
+ - [x] `$state` for reactive state
115
+ - [x] `$derived` for computed values
116
+ - [x] `$effect` with cleanup functions
117
+ - [x] `$bindable` for two-way binding
118
+ - [x] `$props()` for destructuring
119
+ - [x] `untrack()` to avoid unnecessary re-runs
120
+ - [x] `$derived(expression)` instead of `$derived(() => ...)` - simplified in `combobox-listboxitem.svelte`
121
+ - [x] Consolidated effects - using 1 `$effect` + `onDestroy` instead of 2 effects
122
122
 
123
- ### Revisado - No requiere cambios
123
+ ### Reviewed - No Changes Required
124
124
 
125
- - [x] **`$effect.pre`**: Revisado - no hay race conditions que lo requieran
126
- - [x] **Context typing**: El type único es apropiado - tree-shaking no aplica a context objects
125
+ - [x] **`$effect.pre`**: Reviewed - no race conditions requiring it
126
+ - [x] **Context typing**: Single shared type is appropriate - tree-shaking does not apply to context objects
127
127
 
128
128
  ---
129
129
 
130
- ## 🧪 Testing
130
+ ## Testing
131
131
 
132
- ### Completado
132
+ ### Completed (Testing)
133
133
 
134
- - [x] 291 tests unitarios pasando
134
+ - [x] 291 passing unit tests
135
135
  - [x] Keyboard navigation tests
136
136
  - [x] Selection tests
137
137
  - [x] Filtering tests
@@ -143,33 +143,33 @@ Comprehensive review based on: **Accessibility**, **Scalability**, **Performance
143
143
  - [x] Selection behavior (Enter, click, Escape restoration)
144
144
  - [x] Multi-select tests (12 tests)
145
145
  - [x] Tags component tests (4 tests)
146
- - [x] Tag component tests (13 tests) - incluye navegación por teclado
146
+ - [x] Tag component tests (13 tests) - includes keyboard navigation
147
147
  - [x] TagRemove component tests (6 tests)
148
148
  - [x] ItemIndicator component tests (5 tests)
149
149
 
150
- ### Pendiente
150
+ ### Pending (Testing)
151
151
 
152
- - [ ] **Tests con muchos items (100+)** - performance tests
153
- - [ ] **Visual regression tests** - screenshots de estados
152
+ - [ ] **Tests with many items (100+)** - performance tests
153
+ - [ ] **Visual regression tests** - state screenshots
154
154
 
155
155
  ---
156
156
 
157
- ## 📝 Documentación
157
+ ## Documentation
158
158
 
159
- - [ ] **JSDoc completo**
160
- - Documentar todas las props públicas
161
- - Ejemplos de uso en comments
159
+ - [ ] **Complete JSDoc**
160
+ - Document all public props
161
+ - Add usage examples in comments
162
162
 
163
163
  - [ ] **Storybook/Demo page**
164
- - Ejemplos interactivos de todos los casos de uso
165
- - Estados: loading, error, disabled, readonly
164
+ - Interactive examples for all use cases
165
+ - States: loading, error, disabled, readonly
166
166
 
167
167
  ---
168
168
 
169
- ## 🎯 Próximos Pasos Priorizados
169
+ ## Prioritized Next Steps
170
170
 
171
- 1. **Live regions** (accessibility - alto impacto)
172
- 2. **Form integration** (usabilidad - casos comunes)
173
- 3. **`filterFn` customizable** (escalabilidad)
174
- 4. **Consolidar effects** (performance/best practices)
175
- 5. **Async data support** (escalabilidad)
171
+ 1. **Live regions** (accessibility - high impact)
172
+ 2. **Form integration** (usability - common cases)
173
+ 3. **Customizable `filterFn`** (scalability)
174
+ 4. **Consolidate effects** (performance/best practices)
175
+ 5. **Async data support** (scalability)
@@ -0,0 +1,15 @@
1
+ # ComboBox Button
2
+
3
+ ## API reference
4
+
5
+ ### ComboBox.Button
6
+
7
+ Name: `ComboBox.Button`
8
+ Description: Optional trigger button that toggles the combobox popover without stealing focus from the input.
9
+
10
+ | Prop | Type | Default | Description |
11
+ | -------------- | ---------------------- | ----------- | --------------------------------------------------------------- |
12
+ | `class` | `string` | `undefined` | CSS class names for the button element. |
13
+ | `children` | `Snippet` | `undefined` | Custom trigger content. If omitted, a chevron icon is rendered. |
14
+ | `tabindex` | `number` | `-1` | Tab index applied to the button. |
15
+ | `...restProps` | `HTMLButtonAttributes` | `-` | Additional native button attributes. |
@@ -0,0 +1,16 @@
1
+ # ComboBox Input
2
+
3
+ ## API reference
4
+
5
+ ### ComboBox.Input
6
+
7
+ Name: `ComboBox.Input`
8
+ Description: Text input with `role="combobox"` that syncs typed value, active descendant, and keyboard handling with `ComboBox.Root`.
9
+
10
+ | Prop | Type | Default | Description |
11
+ | ------------------ | --------------------- | ----------- | ----------------------------------------------- |
12
+ | `aria-label` | `string` | `undefined` | Accessible label applied directly to the input. |
13
+ | `aria-labelledby` | `string` | `undefined` | Element id that labels the input. |
14
+ | `aria-describedby` | `string` | `undefined` | Element id that describes the input behavior. |
15
+ | `class` | `string` | `undefined` | CSS class names for the input element. |
16
+ | `...restProps` | `HTMLInputAttributes` | `-` | Additional native input attributes. |
@@ -0,0 +1,27 @@
1
+ # ComboBox Item
2
+
3
+ ## API reference
4
+
5
+ ### ComboBox.Item
6
+
7
+ Name: `ComboBox.Item`
8
+ Description: Selectable option item with combobox-specific filtering, virtual focus, and registration logic.
9
+
10
+ | Prop | Type | Default | Description |
11
+ | -------------- | -------------------------------- | -------------- | -------------------------------------------------------------- |
12
+ | `id` | `string \| number` | `required` | Unique item id used for selection and ARIA relationships. |
13
+ | `textValue` | `string` | `content text` | Text used for filtering and fallback label resolution. |
14
+ | `disabled` | `boolean` | `false` | Marks the item as disabled and non-selectable. |
15
+ | `class` | `string` | `undefined` | CSS class names for the item. |
16
+ | `children` | `Snippet` | `undefined` | Rendered item content. |
17
+ | `...restProps` | `HTMLAttributes<HTMLDivElement>` | `-` | Additional attributes passed to the underlying option element. |
18
+
19
+ ### Item context utility
20
+
21
+ Name: `COMBOBOX_ITEM_CONTEXT_KEY` / `ComboBoxItemContext`
22
+ Description: Internal context consumed by `ComboBox.ItemIndicator` to read the parent item id.
23
+
24
+ | Prop | Type | Default | Description |
25
+ | --------------------------- | ------------------ | ----------------------------- | ----------------------------------------- |
26
+ | `COMBOBOX_ITEM_CONTEXT_KEY` | `symbol` | `Symbol.for('combobox-item')` | Context key shared by item and indicator. |
27
+ | `id` | `string \| number` | `required` | Current item id exposed through context. |
@@ -0,0 +1,15 @@
1
+ # ComboBox Item Indicator
2
+
3
+ ## API reference
4
+
5
+ ### ComboBox.ItemIndicator
6
+
7
+ Name: `ComboBox.ItemIndicator`
8
+ Description: Visual selection indicator rendered inside `ComboBox.Item`.
9
+
10
+ | Prop | Type | Default | Description |
11
+ | -------------- | --------------------------------- | ------------ | ---------------------------------------------------- |
12
+ | `children` | `Snippet` | `check icon` | Custom indicator content. |
13
+ | `forceMount` | `boolean` | `false` | Forces rendering even when the item is not selected. |
14
+ | `class` | `string` | `undefined` | CSS class names for the indicator wrapper. |
15
+ | `...restProps` | `HTMLAttributes<HTMLSpanElement>` | `-` | Additional attributes passed to the wrapper span. |
@@ -0,0 +1,27 @@
1
+ # ComboBox List
2
+
3
+ ## API reference
4
+
5
+ ### ComboBox.List
6
+
7
+ Name: `ComboBox.List`
8
+ Description: Listbox bridge for combobox options. It delegates selection state and mode to `ComboBox.Root`.
9
+
10
+ | Prop | Type | Default | Description |
11
+ | ----------------- | --------------------------------------------------- | ----------- | ------------------------------------------------------------------------------- |
12
+ | `aria-label` | `string` | `'Options'` | Accessible label for the internal listbox. |
13
+ | `items` | `Iterable<T>` | `ctx.items` | Dynamic data source. If omitted, uses items from root context. |
14
+ | `children` | `Snippet<[T]> \| Snippet` | `undefined` | Dynamic item renderer or static list content. |
15
+ | `...listBoxProps` | `Omit<ComponentProps<ListBoxRoot>, internal props>` | `-` | Additional listbox props such as `class`, `emptyPlaceholder`, or `disabledIds`. |
16
+
17
+ ### Internally controlled props
18
+
19
+ Name: Controlled by `ComboBox.Root`
20
+ Description: The following values are set by `ComboBox.List` and should not be overridden directly.
21
+
22
+ | Prop | Type | Default | Description |
23
+ | --------------- | ------------------------ | -------------------------------- | ----------------------------------------------------- |
24
+ | `selectionMode` | `'single' \| 'multiple'` | `ctx.selectionMode` | Selection mode inherited from root. |
25
+ | `value` | `Set<string \| number>` | `ctx.selectedValue` | Current combobox selection state. |
26
+ | `onChange` | `(selection) => void` | `internal handler` | Delegates selected item handling to combobox context. |
27
+ | `id` | `string` | `combobox-listbox-${instanceId}` | Auto-generated listbox id for ARIA wiring. |
@@ -0,0 +1,13 @@
1
+ # ComboBox Popover
2
+
3
+ ## API reference
4
+
5
+ ### ComboBox.Popover
6
+
7
+ Name: `ComboBox.Popover`
8
+ Description: Floating container for combobox options. Internally composes `Popover.Root` and `Popover.Content` in non-modal mode.
9
+
10
+ | Prop | Type | Default | Description |
11
+ | ---------- | --------- | ----------- | ------------------------------------------- |
12
+ | `class` | `string` | `''` | CSS class names for the floating panel. |
13
+ | `children` | `Snippet` | `undefined` | Popover content, typically `ComboBox.List`. |