reshaped 3.7.0-canary.16 → 3.7.0-canary.18
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/CHANGELOG.md +8 -0
- package/dist/bundle.css +1 -1
- package/dist/bundle.js +11 -11
- package/dist/components/ActionBar/ActionBar.js +11 -4
- package/dist/components/ActionBar/ActionBar.module.css +1 -1
- package/dist/components/ActionBar/ActionBar.types.d.ts +5 -1
- package/dist/components/ActionBar/tests/ActionBar.stories.d.ts +23 -1
- package/dist/components/ActionBar/tests/ActionBar.stories.js +175 -3
- package/dist/components/Autocomplete/Autocomplete.js +64 -24
- package/dist/components/Autocomplete/Autocomplete.types.d.ts +2 -0
- package/dist/components/Autocomplete/tests/Autocomplete.stories.js +2 -2
- package/dist/components/Card/Card.d.ts +1 -1
- package/dist/components/FileUpload/FileUpload.js +5 -3
- package/dist/components/FileUpload/FileUpload.module.css +1 -1
- package/dist/components/FileUpload/FileUpload.types.d.ts +5 -1
- package/dist/components/FileUpload/tests/FileUpload.stories.d.ts +18 -2
- package/dist/components/FileUpload/tests/FileUpload.stories.js +102 -23
- package/dist/components/Flyout/FlyoutContent.js +1 -1
- package/dist/components/MenuItem/MenuItem.js +2 -2
- package/dist/components/MenuItem/MenuItem.module.css +1 -1
- package/dist/components/MenuItem/MenuItem.types.d.ts +1 -0
- package/dist/components/Popover/Popover.js +1 -1
- package/dist/components/Popover/tests/Popover.stories.js +4 -3
- package/package.json +1 -1
- package/dist/components/ActionBar/tests/ActionBar.test.stories.d.ts +0 -15
- package/dist/components/ActionBar/tests/ActionBar.test.stories.js +0 -26
- package/dist/components/FileUpload/tests/FileUpload.test.stories.d.ts +0 -21
- package/dist/components/FileUpload/tests/FileUpload.test.stories.js +0 -52
@@ -1,11 +1,18 @@
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
-
import { classNames } from "../../utilities/props.js";
|
2
|
+
import { classNames, responsiveVariables } from "../../utilities/props.js";
|
3
3
|
import View from "../View/index.js";
|
4
4
|
import s from "./ActionBar.module.css";
|
5
|
+
const fullWidthPositions = ["top", "bottom"];
|
5
6
|
const ActionBar = (props) => {
|
6
|
-
const { position = "bottom", padding, paddingBlock = 3, paddingInline = 4, children, elevated, className, attributes, } = props;
|
7
|
-
const
|
8
|
-
|
7
|
+
const { position = "bottom", positionType: passedPositionType, offset: passedOffset, padding, paddingBlock = 3, paddingInline = 4, children, blurred, elevated, active = true, className, attributes, } = props;
|
8
|
+
const positionType = passedPositionType ?? (fullWidthPositions.includes(position) ? "relative" : "absolute");
|
9
|
+
const offset = passedOffset ?? (positionType === "relative" ? undefined : 4);
|
10
|
+
const offsetVariables = offset && responsiveVariables("--rs-action-bar-offset", offset);
|
11
|
+
const rootClassNames = classNames(s.root, (elevated || !!offsetVariables) && s["--elevated"], position && s[`--position-${position}`], blurred && s["--blurred"], active && s["--active"], className);
|
12
|
+
return (_jsx(View, { className: rootClassNames, attributes: {
|
13
|
+
...attributes,
|
14
|
+
style: { ...attributes?.style, ...offsetVariables },
|
15
|
+
}, position: positionType, paddingBlock: padding || paddingBlock, paddingInline: padding || paddingInline, children: children }));
|
9
16
|
};
|
10
17
|
ActionBar.displayName = "ActionBar";
|
11
18
|
export default ActionBar;
|
@@ -1 +1 @@
|
|
1
|
-
.root{background:var(--rs-color-background-elevation-base);position:
|
1
|
+
.root{--rs-action-bar-background-rgb:var(--rs-color-rgb-background-elevation-base);--rs-action-bar-background-opacity:1;--rs-action-bar-translate-x:0;--rs-action-bar-translate-y:0;--rs-action-bar-radius:0;background:rgba(var(--rs-action-bar-background-rgb),var(--rs-action-bar-background-opacity));border:1px solid var(--rs-color-border-neutral-faded);border-radius:var(--rs-action-bar-radius);max-width:calc(100% - var(--rs-action-bar-offset) * 2);transform:translate(var(--rs-action-bar-translate-x),var(--rs-action-bar-translate-y));transition:transform var(--rs-duration-fast) var(--rs-easing-accelerate)}[style*="--rs-action-bar-offset-"]{--rs-action-bar-radius:var(--rs-radius-medium);margin:calc(var(--rs-action-bar-offset) * var(--rs-unit-x1));--rs-action-bar-offset-s:0;--rs-action-bar-offset-m:var(--rs-action-bar-offset-s);--rs-action-bar-offset-l:var(--rs-action-bar-offset-m);--rs-action-bar-offset-xl:var(--rs-action-bar-offset-l);--rs-action-bar-offset:var(--rs-action-bar-offset-s)}.--position-top,.--position-top-end,.--position-top-start{--rs-action-bar-translate-y:calc(-100% - var(--rs-action-bar-offset) * var(--rs-unit-x1) * 2);inset-block-start:0}.--position-bottom,.--position-bottom-end,.--position-bottom-start{--rs-action-bar-translate-y:calc(100% + var(--rs-action-bar-offset) * var(--rs-unit-x1) * 2);inset-block-end:0}.--position-bottom,.--position-top{--rs-action-bar-translate-x:-50%;inset-inline-start:50%}.--position-bottom-start,.--position-top-start{inset-inline-start:0}.--position-bottom-end,.--position-top-end{inset-inline-end:0}.--position-bottom:not([style*="--rs-action-bar-offset-"]),.--position-top:not([style*="--rs-action-bar-offset-"]){--rs-action-bar-border-radius:0px;border:0;overflow-x:clip;width:100%}.--position-bottom:not([style*="--rs-action-bar-offset-"]).--elevated,.--position-top:not([style*="--rs-action-bar-offset-"]).--elevated{border:0!important}.--position-top:not([style*="--rs-action-bar-offset-"]){border-bottom:1px solid var(--rs-color-border-neutral-faded)}.--position-bottom:not([style*="--rs-action-bar-offset-"]){border-top:1px solid var(--rs-color-border-neutral-faded)}.--active{--rs-action-bar-translate-y:0px;transition-timing-function:var(--rs-easing-decelerate)}.--blurred{--rs-action-bar-background-opacity:0.84;backdrop-filter:blur(10px)}.--elevated{--rs-action-bar-background-rgb:var(--rs-color-rgb-background-elevation-raised)}.--elevated:after{border-radius:var(--rs-action-bar-radius);bottom:0;box-shadow:var(--rs-shadow-raised);content:"";display:block;height:100%;left:0;pointer-events:none;position:absolute;right:0}.--position-bottom.--elevated:after{transform:rotateX(180deg)}@media (--rs-viewport-m ){[style*="--rs-action-bar-offset-"]{--rs-action-bar-offset:var(--rs-action-bar-offset-m)}}@media (--rs-viewport-l ){[style*="--rs-action-bar-offset-"]{--rs-action-bar-offset:var(--rs-action-bar-offset-l)}}@media (--rs-viewport-xl ){[style*="--rs-action-bar-offset-"]{--rs-action-bar-offset:var(--rs-action-bar-offset-xl)}}
|
@@ -2,7 +2,11 @@ import type React from "react";
|
|
2
2
|
import type { ViewProps } from "../View";
|
3
3
|
import type * as G from "../../types/global";
|
4
4
|
export type Props = Pick<ViewProps, "paddingBlock" | "paddingInline" | "padding"> & {
|
5
|
-
|
5
|
+
active?: boolean;
|
6
|
+
offset?: G.Responsive<number>;
|
7
|
+
position?: "top" | "top-end" | "top-start" | "bottom" | "bottom-start" | "bottom-end";
|
8
|
+
positionType?: G.Responsive<"relative" | "absolute" | "fixed">;
|
9
|
+
blurred?: boolean;
|
6
10
|
elevated?: boolean;
|
7
11
|
children?: React.ReactNode;
|
8
12
|
className?: G.ClassName;
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { StoryObj } from "@storybook/react-vite";
|
1
2
|
declare const _default: {
|
2
3
|
title: string;
|
3
4
|
component: import("react").FC<import("./..").ActionBarProps>;
|
@@ -8,7 +9,15 @@ declare const _default: {
|
|
8
9
|
};
|
9
10
|
};
|
10
11
|
export default _default;
|
11
|
-
export declare const
|
12
|
+
export declare const positionRelative: {
|
13
|
+
name: string;
|
14
|
+
render: () => import("react").JSX.Element;
|
15
|
+
};
|
16
|
+
export declare const positionAbsolute: {
|
17
|
+
name: string;
|
18
|
+
render: () => import("react").JSX.Element;
|
19
|
+
};
|
20
|
+
export declare const positionFixed: {
|
12
21
|
name: string;
|
13
22
|
render: () => import("react").JSX.Element;
|
14
23
|
};
|
@@ -16,7 +25,20 @@ export declare const elevated: {
|
|
16
25
|
name: string;
|
17
26
|
render: () => import("react").JSX.Element;
|
18
27
|
};
|
28
|
+
export declare const offset: {
|
29
|
+
name: string;
|
30
|
+
render: () => import("react").JSX.Element;
|
31
|
+
};
|
32
|
+
export declare const active: {
|
33
|
+
name: string;
|
34
|
+
render: () => import("react").JSX.Element;
|
35
|
+
};
|
36
|
+
export declare const blurred: {
|
37
|
+
name: string;
|
38
|
+
render: () => import("react").JSX.Element;
|
39
|
+
};
|
19
40
|
export declare const padding: {
|
20
41
|
name: string;
|
21
42
|
render: () => import("react").JSX.Element;
|
22
43
|
};
|
44
|
+
export declare const className: StoryObj;
|
@@ -1,5 +1,9 @@
|
|
1
|
+
import { expect } from "storybook/test";
|
1
2
|
import { Placeholder, Example } from "../../../utilities/storybook/index.js";
|
2
3
|
import ActionBar from "../index.js";
|
4
|
+
import View from "../../View/index.js";
|
5
|
+
import Button from "../../Button/index.js";
|
6
|
+
import useToggle from "../../../hooks/useToggle.js";
|
3
7
|
export default {
|
4
8
|
title: "Components/ActionBar",
|
5
9
|
component: ActionBar,
|
@@ -9,8 +13,19 @@ export default {
|
|
9
13
|
},
|
10
14
|
},
|
11
15
|
};
|
12
|
-
|
13
|
-
|
16
|
+
const Fixtures = {
|
17
|
+
Container: (props) => (<View backgroundColor="neutral-faded" height="160px" overflow="hidden" borderRadius="medium">
|
18
|
+
{props.children}
|
19
|
+
</View>),
|
20
|
+
Actions: () => (<View direction="row" gap={2}>
|
21
|
+
<Button onClick={() => { }}>Action</Button>
|
22
|
+
<Button onClick={() => { }} variant="outline">
|
23
|
+
Action
|
24
|
+
</Button>
|
25
|
+
</View>),
|
26
|
+
};
|
27
|
+
export const positionRelative = {
|
28
|
+
name: "position, positionType: relative",
|
14
29
|
render: () => (<Example>
|
15
30
|
<Example.Item title="position: top">
|
16
31
|
<ActionBar position="top">
|
@@ -19,12 +34,94 @@ export const position = {
|
|
19
34
|
</Example.Item>
|
20
35
|
|
21
36
|
<Example.Item title="position: bottom">
|
22
|
-
<ActionBar>
|
37
|
+
<ActionBar position="bottom">
|
23
38
|
<Placeholder />
|
24
39
|
</ActionBar>
|
25
40
|
</Example.Item>
|
26
41
|
</Example>),
|
27
42
|
};
|
43
|
+
export const positionAbsolute = {
|
44
|
+
name: "position, positionType: absolute",
|
45
|
+
render: () => (<Example>
|
46
|
+
<Example.Item title="position: top-start">
|
47
|
+
<Fixtures.Container>
|
48
|
+
<ActionBar padding={2} position="top-start" positionType="absolute">
|
49
|
+
<Fixtures.Actions />
|
50
|
+
</ActionBar>
|
51
|
+
</Fixtures.Container>
|
52
|
+
</Example.Item>
|
53
|
+
|
54
|
+
<Example.Item title="position: top">
|
55
|
+
<Fixtures.Container>
|
56
|
+
<ActionBar padding={2} position="top" positionType="absolute">
|
57
|
+
<Fixtures.Actions />
|
58
|
+
</ActionBar>
|
59
|
+
</Fixtures.Container>
|
60
|
+
</Example.Item>
|
61
|
+
|
62
|
+
<Example.Item title="position: top-end">
|
63
|
+
<Fixtures.Container>
|
64
|
+
<ActionBar padding={2} position="top-end" positionType="absolute">
|
65
|
+
<Fixtures.Actions />
|
66
|
+
</ActionBar>
|
67
|
+
</Fixtures.Container>
|
68
|
+
</Example.Item>
|
69
|
+
|
70
|
+
<Example.Item title="position: bottom-start">
|
71
|
+
<Fixtures.Container>
|
72
|
+
<ActionBar padding={2} position="bottom-start" positionType="absolute">
|
73
|
+
<Fixtures.Actions />
|
74
|
+
</ActionBar>
|
75
|
+
</Fixtures.Container>
|
76
|
+
</Example.Item>
|
77
|
+
|
78
|
+
<Example.Item title="position: bottom">
|
79
|
+
<Fixtures.Container>
|
80
|
+
<ActionBar padding={2} position="bottom" positionType="absolute">
|
81
|
+
<Fixtures.Actions />
|
82
|
+
</ActionBar>
|
83
|
+
</Fixtures.Container>
|
84
|
+
</Example.Item>
|
85
|
+
|
86
|
+
<Example.Item title="position: bottom-end">
|
87
|
+
<Fixtures.Container>
|
88
|
+
<ActionBar padding={2} position="bottom-end" positionType="absolute">
|
89
|
+
<Fixtures.Actions />
|
90
|
+
</ActionBar>
|
91
|
+
</Fixtures.Container>
|
92
|
+
</Example.Item>
|
93
|
+
</Example>),
|
94
|
+
};
|
95
|
+
export const positionFixed = {
|
96
|
+
name: "position, positionType: fixed",
|
97
|
+
render: () => (<>
|
98
|
+
<ActionBar padding={2} position="top-start" positionType="fixed">
|
99
|
+
<Fixtures.Actions />
|
100
|
+
</ActionBar>
|
101
|
+
|
102
|
+
<ActionBar padding={2} position="top" positionType="fixed">
|
103
|
+
<Fixtures.Actions />
|
104
|
+
</ActionBar>
|
105
|
+
|
106
|
+
<ActionBar padding={2} position="top-end" positionType="fixed">
|
107
|
+
<Fixtures.Actions />
|
108
|
+
</ActionBar>
|
109
|
+
|
110
|
+
<ActionBar padding={2} position="bottom-start" positionType="fixed">
|
111
|
+
<Fixtures.Actions />
|
112
|
+
</ActionBar>
|
113
|
+
|
114
|
+
<ActionBar padding={2} position="bottom" positionType="fixed">
|
115
|
+
<Fixtures.Actions />
|
116
|
+
</ActionBar>
|
117
|
+
|
118
|
+
<ActionBar padding={2} position="bottom-end" positionType="fixed">
|
119
|
+
<Fixtures.Actions />
|
120
|
+
</ActionBar>
|
121
|
+
|
122
|
+
<div style={{ height: 2000 }}/>
|
123
|
+
</>),
|
124
|
+
};
|
28
125
|
export const elevated = {
|
29
126
|
name: "elevated",
|
30
127
|
render: () => (<Example>
|
@@ -39,6 +136,68 @@ export const elevated = {
|
|
39
136
|
<Placeholder />
|
40
137
|
</ActionBar>
|
41
138
|
</Example.Item>
|
139
|
+
|
140
|
+
<Example.Item title="auto elevated, position: bottom">
|
141
|
+
<Fixtures.Container>
|
142
|
+
<ActionBar position="bottom-end">
|
143
|
+
<Fixtures.Actions />
|
144
|
+
</ActionBar>
|
145
|
+
</Fixtures.Container>
|
146
|
+
</Example.Item>
|
147
|
+
</Example>),
|
148
|
+
};
|
149
|
+
export const offset = {
|
150
|
+
name: "offset",
|
151
|
+
render: () => (<Example>
|
152
|
+
<Example.Item title="offset 2, position: top">
|
153
|
+
<Fixtures.Container>
|
154
|
+
<ActionBar position="top" positionType="absolute" offset={2}>
|
155
|
+
<Fixtures.Actions />
|
156
|
+
</ActionBar>
|
157
|
+
</Fixtures.Container>
|
158
|
+
</Example.Item>
|
159
|
+
|
160
|
+
<Example.Item title="offset 2, position: bottom-end">
|
161
|
+
<Fixtures.Container>
|
162
|
+
<ActionBar position="bottom-end" offset={2}>
|
163
|
+
<Fixtures.Actions />
|
164
|
+
</ActionBar>
|
165
|
+
</Fixtures.Container>
|
166
|
+
</Example.Item>
|
167
|
+
|
168
|
+
<Example.Item title="offset s: 2, m: 4, position: bottom-end">
|
169
|
+
<Fixtures.Container>
|
170
|
+
<ActionBar position="bottom-end" offset={{ s: 2, m: 4 }}>
|
171
|
+
<Fixtures.Actions />
|
172
|
+
</ActionBar>
|
173
|
+
</Fixtures.Container>
|
174
|
+
</Example.Item>
|
175
|
+
</Example>),
|
176
|
+
};
|
177
|
+
export const active = {
|
178
|
+
name: "active",
|
179
|
+
render: () => {
|
180
|
+
const barToggle = useToggle();
|
181
|
+
return (<>
|
182
|
+
<Button onClick={() => barToggle.toggle()}>Toggle</Button>
|
183
|
+
<ActionBar active={barToggle.active} positionType="fixed" position="top-end">
|
184
|
+
<Fixtures.Actions />
|
185
|
+
</ActionBar>
|
186
|
+
</>);
|
187
|
+
},
|
188
|
+
};
|
189
|
+
export const blurred = {
|
190
|
+
name: "blurred",
|
191
|
+
render: () => (<Example>
|
192
|
+
<Example.Item title="blurred">
|
193
|
+
<View backgroundColor="neutral-faded" height="200px" align="end" justify="end" padding={8}>
|
194
|
+
<Button color="primary">Action</Button>
|
195
|
+
|
196
|
+
<ActionBar position="bottom-end" blurred>
|
197
|
+
<View width={20} height={20}/>
|
198
|
+
</ActionBar>
|
199
|
+
</View>
|
200
|
+
</Example.Item>
|
42
201
|
</Example>),
|
43
202
|
};
|
44
203
|
export const padding = {
|
@@ -63,3 +222,16 @@ export const padding = {
|
|
63
222
|
</Example.Item>
|
64
223
|
</Example>),
|
65
224
|
};
|
225
|
+
export const className = {
|
226
|
+
name: "className, attributes",
|
227
|
+
render: () => (<div data-testid="root">
|
228
|
+
<ActionBar className="test-classname" attributes={{ id: "test-id" }}>
|
229
|
+
<Placeholder />
|
230
|
+
</ActionBar>
|
231
|
+
</div>),
|
232
|
+
play: async ({ canvas }) => {
|
233
|
+
const root = canvas.getByTestId("root").firstChild;
|
234
|
+
expect(root).toHaveClass("test-classname");
|
235
|
+
expect(root).toHaveAttribute("id", "test-id");
|
236
|
+
},
|
237
|
+
};
|
@@ -3,16 +3,18 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
4
4
|
import TextField from "../TextField/index.js";
|
5
5
|
import DropdownMenu from "../DropdownMenu/index.js";
|
6
|
-
import { getActiveElement } from "../../utilities/a11y/index.js";
|
7
6
|
import * as keys from "../../constants/keys.js";
|
8
7
|
import useHotkeys from "../../hooks/useHotkeys.js";
|
9
8
|
import useHandlerRef from "../../hooks/useHandlerRef.js";
|
10
9
|
import s from "./Autocomplete.module.css";
|
10
|
+
import useElementId from "../../hooks/useElementId.js";
|
11
|
+
import useIsomorphicLayoutEffect from "../../hooks/useIsomorphicLayoutEffect.js";
|
11
12
|
const AutocompleteContext = React.createContext({});
|
12
13
|
const Autocomplete = (props) => {
|
13
14
|
const { children, onChange, onInput, onItemSelect, name, containerRef, instanceRef, onBackspace, onEnter, active, onOpen, onClose, ...textFieldProps } = props;
|
15
|
+
const [highlightedId, setHighlightedId] = React.useState();
|
14
16
|
const onBackspaceRef = useHandlerRef(onBackspace);
|
15
|
-
const
|
17
|
+
const contentRef = React.useRef(null);
|
16
18
|
const internalInputRef = React.useRef(null);
|
17
19
|
const inputAttributesRef = textFieldProps.inputAttributes?.ref;
|
18
20
|
const inputRef = inputAttributesRef && typeof inputAttributesRef !== "string" && "current" in inputAttributesRef
|
@@ -23,15 +25,18 @@ const Autocomplete = (props) => {
|
|
23
25
|
const lockedRef = React.useRef(false);
|
24
26
|
const onOpenRef = useHandlerRef(onOpen);
|
25
27
|
const onCloseRef = useHandlerRef(onClose);
|
28
|
+
const onChangeRef = useHandlerRef(onChange);
|
29
|
+
const onItemSelectRef = useHandlerRef(onItemSelect);
|
30
|
+
const onEnterRef = useHandlerRef(onEnter);
|
26
31
|
const isDropdownActive = hasChildren && (active ?? internalActive);
|
27
|
-
const lockDropdown = () => {
|
32
|
+
const lockDropdown = React.useCallback(() => {
|
28
33
|
// Prevent dropdown from re-opening when clicked on item with mouse
|
29
34
|
// and focus moves to the item and back to the input
|
30
35
|
lockedRef.current = true;
|
31
36
|
setTimeout(() => {
|
32
37
|
lockedRef.current = false;
|
33
38
|
}, 100);
|
34
|
-
};
|
39
|
+
}, []);
|
35
40
|
const handleOpen = React.useCallback(() => {
|
36
41
|
if (lockedRef.current)
|
37
42
|
return;
|
@@ -42,11 +47,11 @@ const Autocomplete = (props) => {
|
|
42
47
|
setInternalActive(false);
|
43
48
|
onCloseRef.current?.(args);
|
44
49
|
};
|
45
|
-
const handleItemClick = (args) => {
|
46
|
-
|
47
|
-
|
50
|
+
const handleItemClick = React.useCallback((args) => {
|
51
|
+
onChangeRef.current?.({ value: args.value, name });
|
52
|
+
onItemSelectRef.current?.(args);
|
48
53
|
lockDropdown();
|
49
|
-
};
|
54
|
+
}, [lockDropdown, onChangeRef, onItemSelectRef, name]);
|
50
55
|
const handleChange = (args) => {
|
51
56
|
onChange?.(args);
|
52
57
|
handleOpen();
|
@@ -64,27 +69,56 @@ const Autocomplete = (props) => {
|
|
64
69
|
lockDropdown();
|
65
70
|
inputRef.current?.focus();
|
66
71
|
};
|
72
|
+
const getOptionElements = React.useCallback(() => {
|
73
|
+
const contentEl = contentRef.current;
|
74
|
+
if (!contentEl)
|
75
|
+
return [];
|
76
|
+
return Array.from(contentEl.querySelectorAll("[role=option]"));
|
77
|
+
}, []);
|
67
78
|
useHotkeys({
|
79
|
+
[keys.ENTER]: () => {
|
80
|
+
const options = getOptionElements();
|
81
|
+
const highlightedOption = options.find((el) => el.id === highlightedId);
|
82
|
+
highlightedOption?.click();
|
83
|
+
onEnterRef.current?.();
|
84
|
+
},
|
68
85
|
[keys.BACKSPACE]: () => {
|
69
86
|
onBackspaceRef.current?.();
|
70
87
|
},
|
71
|
-
[keys.
|
72
|
-
|
88
|
+
[keys.UP]: () => {
|
89
|
+
const options = getOptionElements();
|
90
|
+
if (options.length) {
|
91
|
+
const highlightedIndex = options.findIndex((el) => el.id === highlightedId);
|
92
|
+
const nextOption = options.at(highlightedIndex - 1) || options.at(-1);
|
93
|
+
setHighlightedId(nextOption.id);
|
94
|
+
}
|
73
95
|
},
|
74
|
-
}, [onBackspaceRef, onEnterRef], {
|
75
|
-
ref: inputRef,
|
76
|
-
disabled: !onBackspaceRef.current && !onEnterRef.current,
|
77
|
-
});
|
78
|
-
useHotkeys({
|
79
96
|
[keys.DOWN]: () => {
|
80
97
|
handleOpen();
|
98
|
+
const options = getOptionElements();
|
99
|
+
if (options.length) {
|
100
|
+
const highlightedIndex = options.findIndex((el) => el.id === highlightedId);
|
101
|
+
const nextOption = options.at(highlightedIndex + 1) || options.at(0);
|
102
|
+
setHighlightedId(nextOption.id);
|
103
|
+
}
|
81
104
|
},
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
105
|
+
}, [handleOpen, getOptionElements, highlightedId], { ref: inputRef, preventDefault: true });
|
106
|
+
useIsomorphicLayoutEffect(() => {
|
107
|
+
if (!isDropdownActive)
|
108
|
+
return;
|
109
|
+
requestAnimationFrame(() => {
|
110
|
+
const options = getOptionElements();
|
111
|
+
const firstId = options[0]?.id;
|
112
|
+
if (firstId)
|
113
|
+
setHighlightedId(firstId);
|
114
|
+
});
|
115
|
+
}, [isDropdownActive]);
|
116
|
+
const contextValue = React.useMemo(() => ({
|
117
|
+
onItemClick: handleItemClick,
|
118
|
+
highlightedId,
|
119
|
+
setHighlightedId,
|
120
|
+
}), [highlightedId, handleItemClick]);
|
121
|
+
return (_jsx(AutocompleteContext.Provider, { value: contextValue, children: _jsxs(DropdownMenu, { position: "bottom", width: "trigger", triggerType: "focus", trapFocusMode: false, active: isDropdownActive, onClose: handleClose, onOpen: handleOpen, containerRef: containerRef, disableHideAnimation: true, instanceRef: instanceRef, children: [_jsx(DropdownMenu.Trigger, { children: ({ ref, ...attributes }) => (_jsx(TextField, { ...textFieldProps, name: name, onChange: handleChange, focused: isDropdownActive, attributes: {
|
88
122
|
...textFieldProps.attributes,
|
89
123
|
// Ignoring the type check since TS can't infer the correct html element type
|
90
124
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
@@ -104,18 +138,24 @@ const Autocomplete = (props) => {
|
|
104
138
|
onClick: attributes.onFocus,
|
105
139
|
ref: inputRef,
|
106
140
|
role: "combobox",
|
107
|
-
|
141
|
+
"aria-activedescendant": highlightedId,
|
142
|
+
"aria-haspopup": "listbox",
|
143
|
+
"aria-autocomplete": "list",
|
144
|
+
} })) }), _jsx(DropdownMenu.Content, { attributes: { onClick: handleContentClick, role: "listbox", ref: contentRef }, children: children })] }) }));
|
108
145
|
};
|
109
146
|
const AutocompleteItem = (props) => {
|
110
147
|
const { value, data, onClick, ...menuItemProps } = props;
|
111
|
-
const { onItemClick } = React.useContext(AutocompleteContext);
|
148
|
+
const { onItemClick, highlightedId } = React.useContext(AutocompleteContext);
|
149
|
+
const id = useElementId();
|
112
150
|
const handleClick = (e) => {
|
113
151
|
onClick?.(e);
|
114
152
|
onItemClick({ value, data });
|
115
153
|
};
|
116
|
-
return (_jsx(DropdownMenu.Item, { ...menuItemProps, className: [menuItemProps.disabled && s["item--disabled"], menuItemProps.className], attributes: {
|
154
|
+
return (_jsx(DropdownMenu.Item, { ...menuItemProps, className: [menuItemProps.disabled && s["item--disabled"], menuItemProps.className], highlighted: highlightedId === id, attributes: {
|
117
155
|
...menuItemProps.attributes,
|
118
156
|
role: "option",
|
157
|
+
id,
|
158
|
+
tabIndex: -1,
|
119
159
|
}, onClick: handleClick }));
|
120
160
|
};
|
121
161
|
Autocomplete.Item = AutocompleteItem;
|
@@ -15,6 +15,8 @@ export type Props = TextFieldProps & Pick<DropdownMenuProps, "containerRef" | "i
|
|
15
15
|
export type ItemProps = MenuItemProps & SelectArgs;
|
16
16
|
export type Context = {
|
17
17
|
onItemClick: (args: SelectArgs) => void;
|
18
|
+
highlightedId?: string;
|
19
|
+
setHighlightedId: (value?: string) => void;
|
18
20
|
};
|
19
21
|
export type Instance = DropdownMenuInstance;
|
20
22
|
export {};
|
@@ -33,7 +33,7 @@ export const active = {
|
|
33
33
|
}} onClose={() => {
|
34
34
|
args.handleClose();
|
35
35
|
toggle.deactivate();
|
36
|
-
}}>
|
36
|
+
}} onChange={(args) => console.log(args)}>
|
37
37
|
{["Pizza", "Pie", "Ice-cream"].map((v, i) => {
|
38
38
|
return (<Autocomplete.Item key={v} value={v}>
|
39
39
|
{v}
|
@@ -46,7 +46,7 @@ export const active = {
|
|
46
46
|
},
|
47
47
|
play: async ({ canvasElement, args }) => {
|
48
48
|
const canvas = within(canvasElement.ownerDocument.body);
|
49
|
-
const input = canvas.getByRole("combobox");
|
49
|
+
// const input = canvas.getByRole("combobox");
|
50
50
|
const list = await canvas.findByRole("listbox");
|
51
51
|
expect(list).toBeInTheDocument();
|
52
52
|
expect(args.handleOpen).not.toHaveBeenCalled();
|
@@ -9,7 +9,7 @@ declare const Card: React.ForwardRefExoticComponent<{
|
|
9
9
|
href?: string;
|
10
10
|
as?: keyof React.JSX.IntrinsicElements | undefined;
|
11
11
|
className?: import("../../types/global").ClassName;
|
12
|
-
attributes?: (import("../..").Attributes<keyof React.JSX.IntrinsicElements> & ((import("../..").Attributes<"button"> & Omit<React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "form" | "slot" | "style" | "title" | "disabled" | "color" | "children" | "className" | "hidden" | "content" | "ref" | "aria-orientation" | "role" | "suppressHydrationWarning" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | "formTarget" | "value" | "dir" | "name" | "key" | "type" | "accessKey" | "autoCapitalize" | "autoFocus" | "contentEditable" | "contextMenu" | "draggable" | "enterKeyHint" | "id" | "lang" | "nonce" | "spellCheck" | "tabIndex" | "
|
12
|
+
attributes?: (import("../..").Attributes<keyof React.JSX.IntrinsicElements> & ((import("../..").Attributes<"button"> & Omit<React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "form" | "slot" | "style" | "title" | "disabled" | "color" | "children" | "className" | "hidden" | "content" | "ref" | "aria-orientation" | "role" | "suppressHydrationWarning" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | "formTarget" | "value" | "dir" | "name" | "key" | "type" | "translate" | "accessKey" | "autoCapitalize" | "autoFocus" | "contentEditable" | "contextMenu" | "draggable" | "enterKeyHint" | "id" | "lang" | "nonce" | "spellCheck" | "tabIndex" | "radioGroup" | "about" | "datatype" | "inlist" | "prefix" | "property" | "rel" | "resource" | "rev" | "typeof" | "vocab" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "popover" | "popoverTargetAction" | "popoverTarget" | "inert" | "inputMode" | "is" | "exportparts" | "part" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onScrollEnd" | "onScrollEndCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onToggle" | "onBeforeToggle" | "onTransitionCancel" | "onTransitionCancelCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "onTransitionRun" | "onTransitionRunCapture" | "onTransitionStart" | "onTransitionStartCapture"> & {
|
13
13
|
ref?: React.RefObject<HTMLButtonElement | HTMLAnchorElement | null>;
|
14
14
|
}) | undefined)) | undefined;
|
15
15
|
} & Pick<import("../View").ViewProps, "height"> & React.RefAttributes<HTMLElement>>;
|
@@ -10,9 +10,9 @@ const FileUploadTrigger = (props) => {
|
|
10
10
|
return _jsx("span", { className: s.trigger, children: children });
|
11
11
|
};
|
12
12
|
const FileUpload = (props) => {
|
13
|
-
const { name, children, height, className, attributes, inputAttributes, onChange } = props;
|
13
|
+
const { name, children, height, variant = "outline", inline, className, attributes, inputAttributes, onChange, } = props;
|
14
14
|
const highlightToggle = useToggle();
|
15
|
-
const rootClassNames = classNames(s.root, highlightToggle.active && s["--highlighted"], className);
|
15
|
+
const rootClassNames = classNames(s.root, variant && s[`--variant-${variant}`], inline && s[`--inline`], highlightToggle.active && s["--highlighted"], className);
|
16
16
|
const handleDragOver = (event) => {
|
17
17
|
event.preventDefault();
|
18
18
|
attributes?.onDragOver?.(event);
|
@@ -41,13 +41,15 @@ const FileUpload = (props) => {
|
|
41
41
|
onChange?.({ name, event, value: Array.from(nextValue) });
|
42
42
|
inputAttributes?.onChange?.(event);
|
43
43
|
};
|
44
|
+
const inputNode = (_jsx(HiddenVisually, { children: _jsx("input", { ...inputAttributes, type: "file", className: s.field, name: name, onChange: handleChange }) }));
|
45
|
+
const childrenNode = typeof children === "function" ? children({ highlighted: highlightToggle.active }) : children;
|
44
46
|
return (_jsx(View, { className: rootClassNames, height: height, attributes: {
|
45
47
|
...attributes,
|
46
48
|
onDragOver: handleDragOver,
|
47
49
|
onDragEnter: handleDragEnter,
|
48
50
|
onDragLeave: handleDragLeave,
|
49
51
|
onDrop: handleDrop,
|
50
|
-
}, children: _jsxs(View, { as: "label", className: s.triggerLayer, padding: 6, borderRadius: "medium", gap: 2, align: "center", justify: "center", textAlign: "center", animated: true, height: "100%", children: [_jsx(View.Item, { children:
|
52
|
+
}, children: variant === "outline" && !inline ? (_jsxs(View, { as: "label", className: s.triggerLayer, padding: 6, borderRadius: "medium", gap: 2, align: "center", justify: "center", textAlign: "center", animated: true, height: "100%", children: [inputNode, _jsx(View.Item, { children: childrenNode })] })) : (_jsxs("label", { className: s.triggerLayer, children: [inputNode, childrenNode] })) }));
|
51
53
|
};
|
52
54
|
FileUpload.Trigger = FileUploadTrigger;
|
53
55
|
FileUpload.displayName = "FileUpload";
|
@@ -1 +1 @@
|
|
1
|
-
.root
|
1
|
+
.root{--rs-file-upload-radius:var(--rs-radius-medium);display:block}[data-rs-keyboard] .root:focus-within{box-shadow:var(--rs-focus-shadow)}.--inline{--rs-file-upload-radius:var(--rs-radius-small)}.--inline,.--inline .triggerLayer{display:inline-block;vertical-align:top}[data-rs-keyboard] .--inline:focus-within{box-shadow:none}[data-rs-keyboard] .--inline:focus-within .triggerLayer>*{box-shadow:var(--rs-focus-shadow)}.--variant-outline .triggerLayer{border:1px dashed var(--rs-color-border-neutral);border-radius:var(--rs-file-upload-radius)}.--highlighted.--variant-outline .triggerLayer{background:rgba(var(--rs-color-rgb-background-primary),.08);border-color:var(--rs-color-border-primary)}@media (hover:hover) and (pointer:fine){.--variant-outline .triggerLayer:hover:not(:has(.trigger)){background:rgba(var(--rs-color-rgb-background-neutral),.16)}}.triggerLayer:has(.trigger){pointer-events:none}.triggerLayer:has(.trigger) .trigger{pointer-events:all}.trigger{display:contents}
|
@@ -3,9 +3,13 @@ import type { ViewProps } from "../View";
|
|
3
3
|
import type * as G from "../../types/global";
|
4
4
|
export type Props = {
|
5
5
|
name: string;
|
6
|
-
children?: React.ReactNode
|
6
|
+
children?: React.ReactNode | ((props: {
|
7
|
+
highlighted?: boolean;
|
8
|
+
}) => React.ReactNode);
|
7
9
|
onChange?: G.ChangeHandler<File[], React.DragEvent<HTMLDivElement> | React.ChangeEvent<HTMLInputElement>>;
|
8
10
|
height?: ViewProps["height"];
|
11
|
+
variant?: "outline" | "headless";
|
12
|
+
inline?: boolean;
|
9
13
|
className?: G.ClassName;
|
10
14
|
attributes?: G.Attributes<"div">;
|
11
15
|
inputAttributes?: G.Attributes<"input">;
|
@@ -1,4 +1,6 @@
|
|
1
1
|
import React from "react";
|
2
|
+
import { StoryObj } from "@storybook/react-vite";
|
3
|
+
import { fn } from "storybook/test";
|
2
4
|
declare const _default: {
|
3
5
|
title: string;
|
4
6
|
component: React.FC<import("./..").FileUploadProps> & {
|
@@ -11,5 +13,19 @@ declare const _default: {
|
|
11
13
|
};
|
12
14
|
};
|
13
15
|
export default _default;
|
14
|
-
export declare const base:
|
15
|
-
|
16
|
+
export declare const base: {
|
17
|
+
name: string;
|
18
|
+
render: () => React.JSX.Element;
|
19
|
+
};
|
20
|
+
export declare const inline: {
|
21
|
+
name: string;
|
22
|
+
render: () => React.JSX.Element;
|
23
|
+
};
|
24
|
+
export declare const height: {
|
25
|
+
name: string;
|
26
|
+
render: () => React.JSX.Element;
|
27
|
+
};
|
28
|
+
export declare const onChange: StoryObj<{
|
29
|
+
handleChange: ReturnType<typeof fn>;
|
30
|
+
}>;
|
31
|
+
export declare const className: StoryObj;
|