@lerianstudio/sindarian-ui 2.0.0-beta.1 → 2.0.0-beta.3

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 (42) hide show
  1. package/dist/components/form/file-upload-field/index.d.ts +11 -2
  2. package/dist/components/form/file-upload-field/index.d.ts.map +1 -1
  3. package/dist/components/form/file-upload-field/index.js +8 -3
  4. package/dist/components/form/input-field/index.d.ts +41 -2
  5. package/dist/components/form/input-field/index.d.ts.map +1 -1
  6. package/dist/components/form/input-field/index.js +73 -15
  7. package/dist/components/form/select-field/index.d.ts +9 -1
  8. package/dist/components/form/select-field/index.d.ts.map +1 -1
  9. package/dist/components/form/select-field/index.js +2 -2
  10. package/dist/components/ui/button/styles.css +31 -1
  11. package/dist/components/ui/calendar/index.d.ts.map +1 -1
  12. package/dist/components/ui/calendar/index.js +7 -1
  13. package/dist/components/ui/file-upload/index.d.ts +235 -9
  14. package/dist/components/ui/file-upload/index.d.ts.map +1 -1
  15. package/dist/components/ui/file-upload/index.js +328 -13
  16. package/dist/components/ui/form.d.ts +4 -0
  17. package/dist/components/ui/form.d.ts.map +1 -1
  18. package/dist/components/ui/form.js +48 -6
  19. package/dist/components/ui/toggle-group/index.d.ts +1 -1
  20. package/dist/components/ui/toggle-group/index.d.ts.map +1 -1
  21. package/dist/components/ui/toggle-group/index.js +73 -4
  22. package/dist/esm/components/form/file-upload-field/index.d.ts +11 -2
  23. package/dist/esm/components/form/file-upload-field/index.d.ts.map +1 -1
  24. package/dist/esm/components/form/file-upload-field/index.js +8 -3
  25. package/dist/esm/components/form/input-field/index.d.ts +41 -2
  26. package/dist/esm/components/form/input-field/index.d.ts.map +1 -1
  27. package/dist/esm/components/form/input-field/index.js +73 -15
  28. package/dist/esm/components/form/select-field/index.d.ts +9 -1
  29. package/dist/esm/components/form/select-field/index.d.ts.map +1 -1
  30. package/dist/esm/components/form/select-field/index.js +2 -2
  31. package/dist/esm/components/ui/calendar/index.d.ts.map +1 -1
  32. package/dist/esm/components/ui/calendar/index.js +8 -2
  33. package/dist/esm/components/ui/file-upload/index.d.ts +235 -9
  34. package/dist/esm/components/ui/file-upload/index.d.ts.map +1 -1
  35. package/dist/esm/components/ui/file-upload/index.js +327 -13
  36. package/dist/esm/components/ui/form.d.ts +4 -0
  37. package/dist/esm/components/ui/form.d.ts.map +1 -1
  38. package/dist/esm/components/ui/form.js +48 -6
  39. package/dist/esm/components/ui/toggle-group/index.d.ts +1 -1
  40. package/dist/esm/components/ui/toggle-group/index.d.ts.map +1 -1
  41. package/dist/esm/components/ui/toggle-group/index.js +73 -4
  42. package/package.json +1 -1
@@ -26,12 +26,58 @@ const toggleVariants = cva("hover:bg-muted hover:text-muted-foreground focus-vis
26
26
  size: 'default'
27
27
  }
28
28
  });
29
+ const ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
30
+ /**
31
+ * Is an arrow key down right now?
32
+ *
33
+ * This is how an item tells keyboard navigation apart from a pointer press or a
34
+ * programmatic focus, and it is the seam Radix uses for the same job in its own
35
+ * `RadioGroup`. It works because `RovingFocusGroup` defers the focus move to a
36
+ * `setTimeout`: the keydown has already bubbled to `document` and set the flag
37
+ * by the time the item receives `focus`.
38
+ *
39
+ * The distinction is not cosmetic. A pointer press focuses BEFORE the click
40
+ * lands, so selecting on every focus would turn one click into
41
+ * select-then-deselect and break mouse selection outright.
42
+ *
43
+ * Returns null when disabled, so `type="multiple"` carries no flag to read.
44
+ */
45
+ function useArrowKeyPressed(enabled) {
46
+ const arrowKeyPressed = React.useRef(false);
47
+ React.useEffect(() => {
48
+ if (!enabled)
49
+ return;
50
+ const onKeyDown = (event) => {
51
+ if (ARROW_KEYS.includes(event.key))
52
+ arrowKeyPressed.current = true;
53
+ };
54
+ const onKeyUp = () => {
55
+ arrowKeyPressed.current = false;
56
+ };
57
+ document.addEventListener('keydown', onKeyDown);
58
+ document.addEventListener('keyup', onKeyUp);
59
+ window.addEventListener('blur', onKeyUp);
60
+ return () => {
61
+ document.removeEventListener('keydown', onKeyDown);
62
+ document.removeEventListener('keyup', onKeyUp);
63
+ window.removeEventListener('blur', onKeyUp);
64
+ // A group unmounted mid-keystroke never sees the keyup.
65
+ arrowKeyPressed.current = false;
66
+ };
67
+ }, [enabled]);
68
+ return enabled ? arrowKeyPressed : null;
69
+ }
29
70
  const ToggleGroupContext = React.createContext({
30
71
  size: 'default',
31
72
  variant: 'default',
32
- spacing: 0
73
+ spacing: 0,
74
+ arrowKeyPressed: null
33
75
  });
34
76
  function ToggleGroup({ className, variant, size, spacing = 0, children, style, ...props }) {
77
+ // Read, never destructured: `type` is the discriminant of the Radix props
78
+ // union, and pulling it out then spreading it back widens the union so the
79
+ // single/multiple props stop resolving.
80
+ const arrowKeyPressed = useArrowKeyPressed(props.type === 'single');
35
81
  return (_jsx(ToggleGroupPrimitive.Root, { "data-slot": "toggle-group", "data-variant": variant, "data-size": size, "data-spacing": spacing,
36
82
  // Caller style first: `--gap` is ours and must survive, but everything
37
83
  // else the caller passes has to reach the element.
@@ -39,13 +85,36 @@ function ToggleGroup({ className, variant, size, spacing = 0, children, style, .
39
85
  // Legacy carried a `data-[spacing=default]:...:shadow-xs` rule here that
40
86
  // never matched — `data-spacing` is always a number. Removed rather than
41
87
  // activated, so the rendered pixels stay identical to sindarian-x.
42
- 'group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md', className), ...props, children: _jsx(ToggleGroupContext.Provider, { value: { variant, size, spacing }, children: children }) }));
88
+ 'group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md', className), ...props, children: _jsx(ToggleGroupContext.Provider, { value: { variant, size, spacing, arrowKeyPressed }, children: children }) }));
43
89
  }
44
- function ToggleGroupItem({ className, children, variant, size, ...props }) {
90
+ function ToggleGroupItem({ className, children, variant, size, onFocus, ...props }) {
45
91
  const context = React.useContext(ToggleGroupContext);
92
+ /**
93
+ * Selection follows focus, which is what `role="radio"` promises.
94
+ *
95
+ * With `type="single"` Radix puts the group on the ARIA radio pattern: the
96
+ * root is a `radiogroup`, each item a `radio` with `aria-checked`. Under that
97
+ * pattern an arrow key both moves focus and checks the item it lands on.
98
+ * Radix only moves focus, so a screen-reader operator used to arrow across
99
+ * "radio, not checked, 2 of 3" with nothing ever selected.
100
+ *
101
+ * `type="multiple"` is a `toolbar` of `aria-pressed` buttons and keeps its
102
+ * current behavior, where moving focus without pressing is correct: the flag
103
+ * is null there.
104
+ *
105
+ * Clicking the element rather than writing the value keeps a single
106
+ * activation path, so Radix's own toggle and any caller `onClick` both fire
107
+ * exactly as they do for a real click.
108
+ */
109
+ const handleFocus = (event) => {
110
+ onFocus?.(event);
111
+ if (context.arrowKeyPressed?.current) {
112
+ event.currentTarget.click();
113
+ }
114
+ };
46
115
  return (_jsx(ToggleGroupPrimitive.Item, { "data-slot": "toggle-group-item", "data-variant": context.variant || variant, "data-size": context.size || size, "data-spacing": context.spacing, className: cn(toggleVariants({
47
116
  variant: context.variant || variant,
48
117
  size: context.size || size
49
- }), 'w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10', 'data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l', className), ...props, children: children }));
118
+ }), 'w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10', 'data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l', className), ...props, onFocus: handleFocus, children: children }));
50
119
  }
51
120
  export { ToggleGroup, ToggleGroupItem };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lerianstudio/sindarian-ui",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.0-beta.3",
4
4
  "description": "Sindarian UI - A UI library for Midaz Console",
5
5
  "license": "ISC",
6
6
  "author": {