@human-kit/svelte-components 1.0.0-alpha.13 → 1.0.0-alpha.15

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 (57) hide show
  1. package/dist/checkbox/root/checkbox-root.svelte +22 -2
  2. package/dist/checkbox/root/checkbox-root.svelte.d.ts +4 -1
  3. package/dist/combobox/root/combobox.svelte +1 -0
  4. package/dist/listbox/item/listbox-item.svelte +13 -0
  5. package/dist/listbox/root/listbox.svelte +7 -1
  6. package/dist/table/IMPLEMENTATION_NOTES.md +2 -1
  7. package/dist/table/PLAN.md +445 -33
  8. package/dist/table/README.md +11 -0
  9. package/dist/table/TODO.md +40 -2
  10. package/dist/table/body/README.md +2 -0
  11. package/dist/table/body/table-body.svelte +1 -7
  12. package/dist/table/body/table-body.svelte.d.ts +1 -6
  13. package/dist/table/cell/README.md +2 -0
  14. package/dist/table/cell/table-cell.svelte +87 -86
  15. package/dist/table/cell/table-cell.svelte.d.ts +1 -6
  16. package/dist/table/checkbox/README.md +2 -0
  17. package/dist/table/checkbox/table-checkbox-test.svelte +7 -0
  18. package/dist/table/checkbox/table-checkbox-test.svelte.d.ts +3 -1
  19. package/dist/table/checkbox/table-checkbox.svelte +56 -52
  20. package/dist/table/checkbox/table-checkbox.svelte.d.ts +1 -10
  21. package/dist/table/checkbox-indicator/README.md +2 -0
  22. package/dist/table/checkbox-indicator/table-checkbox-indicator.svelte +1 -8
  23. package/dist/table/checkbox-indicator/table-checkbox-indicator.svelte.d.ts +1 -7
  24. package/dist/table/column/README.md +17 -14
  25. package/dist/table/column/table-column.svelte +2 -26
  26. package/dist/table/column/table-column.svelte.d.ts +1 -15
  27. package/dist/table/column-header-cell/README.md +2 -0
  28. package/dist/table/column-header-cell/table-column-header-cell.svelte +1 -7
  29. package/dist/table/column-header-cell/table-column-header-cell.svelte.d.ts +1 -6
  30. package/dist/table/column-resizer/README.md +2 -1
  31. package/dist/table/column-resizer/table-column-resizer.svelte +1 -9
  32. package/dist/table/column-resizer/table-column-resizer.svelte.d.ts +1 -8
  33. package/dist/table/empty-state/README.md +2 -0
  34. package/dist/table/empty-state/table-empty-state.svelte +1 -6
  35. package/dist/table/empty-state/table-empty-state.svelte.d.ts +1 -5
  36. package/dist/table/footer/README.md +2 -0
  37. package/dist/table/footer/table-footer.svelte +1 -7
  38. package/dist/table/footer/table-footer.svelte.d.ts +1 -6
  39. package/dist/table/header/README.md +2 -0
  40. package/dist/table/header/table-header.svelte +1 -7
  41. package/dist/table/header/table-header.svelte.d.ts +1 -6
  42. package/dist/table/index.d.ts +2 -1
  43. package/dist/table/root/README.md +31 -21
  44. package/dist/table/root/context.d.ts +19 -6
  45. package/dist/table/root/context.js +203 -29
  46. package/dist/table/root/table-root.svelte +30 -33
  47. package/dist/table/root/table-root.svelte.d.ts +1 -26
  48. package/dist/table/root/table-test.svelte +29 -0
  49. package/dist/table/root/table-test.svelte.d.ts +5 -1
  50. package/dist/table/row/README.md +2 -0
  51. package/dist/table/row/table-row.svelte +46 -83
  52. package/dist/table/row/table-row.svelte.d.ts +1 -10
  53. package/dist/table/types.d.ts +90 -0
  54. package/dist/table/types.js +1 -0
  55. package/dist/table/utils/handle-body-keydown.d.ts +13 -0
  56. package/dist/table/utils/handle-body-keydown.js +67 -0
  57. package/package.json +1 -1
@@ -24,6 +24,7 @@
24
24
  | 'value'
25
25
  > & {
26
26
  id?: string;
27
+ element?: HTMLSpanElement | null;
27
28
  name?: string;
28
29
  value?: string;
29
30
  isChecked?: boolean;
@@ -39,8 +40,20 @@
39
40
  class?: string;
40
41
  'aria-label'?: string;
41
42
  'aria-labelledby'?: string;
43
+ onclick?: HTMLAttributes<HTMLSpanElement>['onclick'];
44
+ onkeydown?: HTMLAttributes<HTMLSpanElement>['onkeydown'];
42
45
  };
43
46
 
47
+ function composeEventHandlers<TEvent extends Event>(
48
+ internalHandler: ((event: TEvent) => void) | undefined,
49
+ externalHandler: ((event: TEvent) => void) | undefined
50
+ ): (event: TEvent) => void {
51
+ return (event: TEvent) => {
52
+ internalHandler?.(event);
53
+ externalHandler?.(event);
54
+ };
55
+ }
56
+
44
57
  function resolveState(isChecked: boolean, isIndeterminate: boolean): CheckboxState {
45
58
  if (isIndeterminate) return 'indeterminate';
46
59
  return isChecked ? 'checked' : 'unchecked';
@@ -56,6 +69,7 @@
56
69
 
57
70
  let {
58
71
  id,
72
+ element = $bindable(),
59
73
  name,
60
74
  value = 'on',
61
75
  isChecked = $bindable(),
@@ -71,6 +85,8 @@
71
85
  class: className = '',
72
86
  'aria-label': ariaLabel,
73
87
  'aria-labelledby': ariaLabelledby,
88
+ onclick: onClickExternal,
89
+ onkeydown: onKeyDownExternal,
74
90
  ...restProps
75
91
  }: CheckboxRootProps = $props();
76
92
 
@@ -91,6 +107,10 @@
91
107
  let rootRef: HTMLSpanElement | null = $state(null);
92
108
  let inputRef: HTMLInputElement | null = $state(null);
93
109
 
110
+ $effect(() => {
111
+ element = rootRef;
112
+ });
113
+
94
114
  if (untrack(() => isChecked) === undefined) {
95
115
  isChecked = initialState === 'checked';
96
116
  }
@@ -326,8 +346,8 @@
326
346
  data-required={required || undefined}
327
347
  data-focused={focused || undefined}
328
348
  data-focus-visible={focusVisible || undefined}
329
- onclick={handleClick}
330
- onkeydown={handleKeyDown}
349
+ onclick={composeEventHandlers(onClickExternal ?? undefined, handleClick)}
350
+ onkeydown={composeEventHandlers(handleKeyDown, onKeyDownExternal ?? undefined)}
331
351
  onkeyup={handleKeyUp}
332
352
  onpointerdown={handlePointerDown}
333
353
  onmousedown={handlePointerDown}
@@ -2,6 +2,7 @@ import { type Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
3
  type CheckboxRootProps = Omit<HTMLAttributes<HTMLSpanElement>, 'children' | 'class' | 'id' | 'role' | 'tabindex' | 'aria-checked' | 'aria-disabled' | 'aria-readonly' | 'aria-required' | 'onclick' | 'onkeydown' | 'value'> & {
4
4
  id?: string;
5
+ element?: HTMLSpanElement | null;
5
6
  name?: string;
6
7
  value?: string;
7
8
  isChecked?: boolean;
@@ -17,7 +18,9 @@ type CheckboxRootProps = Omit<HTMLAttributes<HTMLSpanElement>, 'children' | 'cla
17
18
  class?: string;
18
19
  'aria-label'?: string;
19
20
  'aria-labelledby'?: string;
21
+ onclick?: HTMLAttributes<HTMLSpanElement>['onclick'];
22
+ onkeydown?: HTMLAttributes<HTMLSpanElement>['onkeydown'];
20
23
  };
21
- declare const CheckboxRoot: import("svelte").Component<CheckboxRootProps, {}, "isChecked" | "isIndeterminate">;
24
+ declare const CheckboxRoot: import("svelte").Component<CheckboxRootProps, {}, "element" | "isChecked" | "isIndeterminate">;
22
25
  type CheckboxRoot = ReturnType<typeof CheckboxRoot>;
23
26
  export default CheckboxRoot;
@@ -225,6 +225,7 @@
225
225
 
226
226
  if (selectionMode === 'single') {
227
227
  newSelection = new Set([id]);
228
+ shouldFilter = false;
228
229
  // Save the label persistently for restore on blur/escape
229
230
  selectedLabel = label;
230
231
  // Keep the selected label visible in the input without re-triggering
@@ -159,6 +159,19 @@
159
159
  isFocusVisible = false;
160
160
  });
161
161
 
162
+ $effect(() => {
163
+ if (!isFocusedComputed) {
164
+ if (pressedKey !== null) {
165
+ clearPressedState();
166
+ }
167
+ return;
168
+ }
169
+
170
+ if (listFocusVisible || isFocusVisibleComputed) {
171
+ isHovered = false;
172
+ }
173
+ });
174
+
162
175
  function clearPressedState() {
163
176
  isPressed = false;
164
177
  pressedKey = null;
@@ -1,5 +1,6 @@
1
1
  <script lang="ts" generics="T extends object = object">
2
2
  import type { Snippet } from 'svelte';
3
+ import { onMount } from 'svelte';
3
4
  import { createListBoxContext, type ListBoxContext } from './context';
4
5
  import { trackInteractionModality } from '../../primitives/input-modality';
5
6
 
@@ -118,9 +119,14 @@
118
119
 
119
120
  const itemsArray = $derived(items ? Array.from(items) : []);
120
121
  const hasItems = $derived(itemsArray.length > 0 || itemCount > 0);
122
+ let hasMounted = $state(false);
121
123
 
122
124
  let focusWithin = $state(false);
123
125
 
126
+ onMount(() => {
127
+ hasMounted = true;
128
+ });
129
+
124
130
  function syncFocusWithin() {
125
131
  focusWithin =
126
132
  !!listboxElement &&
@@ -175,7 +181,7 @@
175
181
  {@render (children as Snippet)()}
176
182
  {/if}
177
183
 
178
- {#if !hasItems && itemCount === 0}
184
+ {#if hasMounted && !hasItems && itemCount === 0}
179
185
  {#if typeof emptyPlaceholder === 'string'}
180
186
  <div role="option" aria-selected="false" aria-disabled="true" data-empty-placeholder>
181
187
  {emptyPlaceholder}
@@ -2,7 +2,8 @@
2
2
 
3
3
  ## Open questions
4
4
 
5
- - Disabled body rows are currently rendered and keyboard-focusable, but they cannot be selected. We should validate whether this matches the desired UX.
5
+ - Disabled body rows are currently rendered and treated with an all-or-nothing disabled model. The planned `disabledBehavior` API (`'selection' | 'all'`) will require splitting focus/action disabling from selection disabling.
6
6
  - `Table.Column` is implemented as a logical wrapper and currently assumes the intended child is a single `Table.ColumnHeaderCell`.
7
7
  - `Table.Footer` renders semantic table cells but is intentionally excluded from the roving-focus model in v1.
8
8
  - Interactive controls nested inside `Table.Cell` are still intentionally out of scope for v1.
9
+ - `pressRow()` is currently selection-oriented. The planned `onRowAction` feature will require a clearer interaction pipeline so pointer click, double click, `Enter`, and `Space` can route to action and selection independently.