@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.
- package/dist/components/form/file-upload-field/index.d.ts +11 -2
- package/dist/components/form/file-upload-field/index.d.ts.map +1 -1
- package/dist/components/form/file-upload-field/index.js +8 -3
- package/dist/components/form/input-field/index.d.ts +41 -2
- package/dist/components/form/input-field/index.d.ts.map +1 -1
- package/dist/components/form/input-field/index.js +73 -15
- package/dist/components/form/select-field/index.d.ts +9 -1
- package/dist/components/form/select-field/index.d.ts.map +1 -1
- package/dist/components/form/select-field/index.js +2 -2
- package/dist/components/ui/button/styles.css +31 -1
- package/dist/components/ui/calendar/index.d.ts.map +1 -1
- package/dist/components/ui/calendar/index.js +7 -1
- package/dist/components/ui/file-upload/index.d.ts +235 -9
- package/dist/components/ui/file-upload/index.d.ts.map +1 -1
- package/dist/components/ui/file-upload/index.js +328 -13
- package/dist/components/ui/form.d.ts +4 -0
- package/dist/components/ui/form.d.ts.map +1 -1
- package/dist/components/ui/form.js +48 -6
- package/dist/components/ui/toggle-group/index.d.ts +1 -1
- package/dist/components/ui/toggle-group/index.d.ts.map +1 -1
- package/dist/components/ui/toggle-group/index.js +73 -4
- package/dist/esm/components/form/file-upload-field/index.d.ts +11 -2
- package/dist/esm/components/form/file-upload-field/index.d.ts.map +1 -1
- package/dist/esm/components/form/file-upload-field/index.js +8 -3
- package/dist/esm/components/form/input-field/index.d.ts +41 -2
- package/dist/esm/components/form/input-field/index.d.ts.map +1 -1
- package/dist/esm/components/form/input-field/index.js +73 -15
- package/dist/esm/components/form/select-field/index.d.ts +9 -1
- package/dist/esm/components/form/select-field/index.d.ts.map +1 -1
- package/dist/esm/components/form/select-field/index.js +2 -2
- package/dist/esm/components/ui/calendar/index.d.ts.map +1 -1
- package/dist/esm/components/ui/calendar/index.js +8 -2
- package/dist/esm/components/ui/file-upload/index.d.ts +235 -9
- package/dist/esm/components/ui/file-upload/index.d.ts.map +1 -1
- package/dist/esm/components/ui/file-upload/index.js +327 -13
- package/dist/esm/components/ui/form.d.ts +4 -0
- package/dist/esm/components/ui/form.d.ts.map +1 -1
- package/dist/esm/components/ui/form.js +48 -6
- package/dist/esm/components/ui/toggle-group/index.d.ts +1 -1
- package/dist/esm/components/ui/toggle-group/index.d.ts.map +1 -1
- package/dist/esm/components/ui/toggle-group/index.js +73 -4
- 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 };
|