reshaped 3.7.0-canary.17 → 3.7.0-canary.19

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.
@@ -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 rootClassNames = classNames(s.root, elevated && s["--elevated"], position && s[`--position-${position}`], className);
8
- return (_jsx(View, { className: rootClassNames, attributes: attributes, paddingBlock: padding || paddingBlock, paddingInline: padding || paddingInline, children: children }));
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:relative}.--position-bottom{border-top:1px solid var(--rs-color-border-neutral-faded)}.--position-top{border-bottom:1px solid var(--rs-color-border-neutral-faded)}.--elevated{background-color:var(--rs-color-background-elevation-raised);border:none;overflow-x:clip}.--elevated:after{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)}
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)}.root[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 ){.root[style*="--rs-action-bar-offset-"]{--rs-action-bar-offset:var(--rs-action-bar-offset-m)}}@media (--rs-viewport-l ){.root[style*="--rs-action-bar-offset-"]{--rs-action-bar-offset:var(--rs-action-bar-offset-l)}}@media (--rs-viewport-xl ){.root[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
- position?: "top" | "bottom";
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 position: {
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
- export const position = {
13
- name: "position",
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
+ };
@@ -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" | "translate" | "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"> & {
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: children }), _jsx(HiddenVisually, { children: _jsx("input", { ...inputAttributes, type: "file", className: s.field, name: name, onChange: handleChange }) })] }) }));
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.--highlighted .triggerLayer{background:rgba(var(--rs-color-rgb-background-primary),8%);border-color:var(--rs-color-border-primary)}.triggerLayer{border:1px dashed var(--rs-color-border-neutral)}[data-rs-keyboard] .triggerLayer:focus-within{box-shadow:var(--rs-focus-shadow)}@media (hover:hover) and (pointer:fine){.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}
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: () => React.JSX.Element;
15
- export declare const height: () => React.JSX.Element;
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;
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { expect, userEvent, fn } from "storybook/test";
2
3
  import { Example } from "../../../utilities/storybook/index.js";
3
4
  import FileUpload from "../index.js";
4
5
  import View from "../../View/index.js";
@@ -6,6 +7,7 @@ import Image from "../../Image/index.js";
6
7
  import Link from "../../Link/index.js";
7
8
  import Icon from "../../Icon/index.js";
8
9
  import IconMic from "../../../icons/Mic.js";
10
+ import Button from "../../Button/index.js";
9
11
  export default {
10
12
  title: "Components/FileUpload",
11
13
  component: FileUpload,
@@ -34,28 +36,105 @@ const Demo = () => {
34
36
  </View>
35
37
  </View>);
36
38
  };
37
- export const base = () => (<Example>
38
- <Example.Item title="Base upload with previews">
39
- <Demo />
40
- </Example.Item>
41
- <Example.Item title="With trigger">
42
- <FileUpload name="file">
43
- <div>
44
- Drop files to attach, or{" "}
45
- <FileUpload.Trigger>
46
- <Link variant="plain">browse</Link>
47
- </FileUpload.Trigger>
48
- </div>
39
+ export const base = {
40
+ name: "base",
41
+ render: () => (<Example>
42
+ <Example.Item title="Base upload with previews">
43
+ <Demo />
44
+ </Example.Item>
45
+ <Example.Item title="With trigger">
46
+ <FileUpload name="file">
47
+ <div>
48
+ Drop files to attach, or{" "}
49
+ <FileUpload.Trigger>
50
+ <Link variant="plain">browse</Link>
51
+ </FileUpload.Trigger>
52
+ </div>
53
+ </FileUpload>
54
+ </Example.Item>
55
+ </Example>),
56
+ };
57
+ export const inline = {
58
+ name: "inline, variant, render props",
59
+ render: () => {
60
+ return (<Example>
61
+ <Example.Item title="inline">
62
+ <FileUpload name="file" inline onChange={console.log}>
63
+ <View padding={2} paddingInline={3}>
64
+ Upload
65
+ </View>
66
+ </FileUpload>
67
+ </Example.Item>
68
+ <Example.Item title="variant headless">
69
+ <FileUpload name="file2" variant="headless" onChange={console.log}>
70
+ <Button variant="outline" fullWidth>
71
+ Upload
72
+ </Button>
73
+ </FileUpload>
74
+ </Example.Item>
75
+ <Example.Item title="variant headless, inline">
76
+ <FileUpload name="file2" variant="headless" inline onChange={console.log}>
77
+ <Button>Upload</Button>
78
+ </FileUpload>
79
+ </Example.Item>
80
+ <Example.Item title="inline, render props">
81
+ <FileUpload name="file3" variant="headless" inline onChange={console.log}>
82
+ {(props) => <Button highlighted={props.highlighted}>Upload</Button>}
83
+ </FileUpload>
84
+ </Example.Item>
85
+ </Example>);
86
+ },
87
+ };
88
+ export const height = {
89
+ name: "height",
90
+ render: () => (<Example>
91
+ <Example.Item title="Custom height">
92
+ <FileUpload name="file" height="300px">
93
+ <View gap={3}>
94
+ <Icon svg={IconMic} size={8}/>
95
+ Drop files to attach
96
+ </View>
97
+ </FileUpload>
98
+ </Example.Item>
99
+ </Example>),
100
+ };
101
+ export const onChange = {
102
+ name: "name, onChange",
103
+ args: {
104
+ handleChange: fn(),
105
+ },
106
+ render: (args) => (<div data-testid="root">
107
+ <FileUpload name="test-name" onChange={args.handleChange}>
108
+ Content
49
109
  </FileUpload>
50
- </Example.Item>
51
- </Example>);
52
- export const height = () => (<Example>
53
- <Example.Item title="Custom height">
54
- <FileUpload name="file" height="300px">
55
- <View gap={3}>
56
- <Icon svg={IconMic} size={8}/>
57
- Drop files to attach
58
- </View>
110
+ </div>),
111
+ play: async ({ canvas, args }) => {
112
+ const file = new File(["hello"], "hello.png", { type: "image/png" });
113
+ const input = canvas.getByTestId("root").querySelector("input");
114
+ await userEvent.upload(input, file);
115
+ expect(input).toHaveAttribute("name", "test-name");
116
+ expect(input.files?.[0]).toBe(file);
117
+ expect(input.files).toHaveLength(1);
118
+ expect(args.handleChange).toHaveBeenCalledTimes(1);
119
+ expect(args.handleChange).toHaveBeenCalledWith({
120
+ name: "test-name",
121
+ value: [file],
122
+ event: expect.objectContaining({ target: input }),
123
+ });
124
+ },
125
+ };
126
+ export const className = {
127
+ name: "className, attributes",
128
+ render: () => (<div data-testid="root">
129
+ <FileUpload name="name" className="test-classname" attributes={{ id: "test-id" }} inputAttributes={{ id: "test-input-id" }}>
130
+ Content
59
131
  </FileUpload>
60
- </Example.Item>
61
- </Example>);
132
+ </div>),
133
+ play: async ({ canvas }) => {
134
+ const root = canvas.getByTestId("root").firstChild;
135
+ const input = canvas.getByTestId("root").querySelector("input");
136
+ expect(root).toHaveClass("test-classname");
137
+ expect(root).toHaveAttribute("id", "test-id");
138
+ expect(input).toHaveAttribute("id", "test-input-id");
139
+ },
140
+ };
@@ -1,4 +1,4 @@
1
- import { useState } from "react";
1
+ import { useState, useId } from "react";
2
2
  import { expect, fn, userEvent, within, waitFor } from "storybook/test";
3
3
  import { Example } from "../../../utilities/storybook/index.js";
4
4
  import { sleep } from "../../../utilities/helpers.js";
@@ -19,13 +19,14 @@ export default {
19
19
  };
20
20
  const Demo = (props) => {
21
21
  const { position, ...rest } = props;
22
+ const id = useId();
22
23
  return (<Popover position={position} {...rest}>
23
24
  <Popover.Trigger>
24
25
  {(attributes) => <Button attributes={attributes}>{position || "Open"}</Button>}
25
26
  </Popover.Trigger>
26
- <Popover.Content>
27
+ <Popover.Content attributes={{ "aria-labelledby": id }}>
27
28
  <View gap={2} align="start">
28
- Popover content
29
+ <span id={id}>Popover content</span>
29
30
  <View direction="row" gap={2}>
30
31
  <Button onClick={() => { }}>Action 1</Button>
31
32
  <Button onClick={() => { }}>Action 2</Button>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "reshaped",
3
3
  "description": "Professionally crafted design system in React & Figma for building products of any scale and complexity",
4
- "version": "3.7.0-canary.17",
4
+ "version": "3.7.0-canary.19",
5
5
  "license": "MIT",
6
6
  "email": "hello@reshaped.so",
7
7
  "homepage": "https://reshaped.so",
@@ -1,15 +0,0 @@
1
- import { StoryObj } from "@storybook/react-vite";
2
- declare const _default: {
3
- title: string;
4
- component: import("react").FC<import("./..").ActionBarProps>;
5
- parameters: {
6
- iframe: {
7
- url: string;
8
- };
9
- chromatic: {
10
- disableSnapshot: boolean;
11
- };
12
- };
13
- };
14
- export default _default;
15
- export declare const className: StoryObj;
@@ -1,26 +0,0 @@
1
- import { expect } from "storybook/test";
2
- import ActionBar from "../index.js";
3
- import { Placeholder } from "../../../utilities/storybook/index.js";
4
- export default {
5
- title: "Components/ActionBar/tests",
6
- component: ActionBar,
7
- parameters: {
8
- iframe: {
9
- url: "https://reshaped.so/docs/components/action-bar",
10
- },
11
- chromatic: { disableSnapshot: true },
12
- },
13
- };
14
- export const className = {
15
- name: "className, attributes",
16
- render: () => (<div data-testid="root">
17
- <ActionBar className="test-classname" attributes={{ id: "test-id" }}>
18
- <Placeholder />
19
- </ActionBar>
20
- </div>),
21
- play: async ({ canvas }) => {
22
- const root = canvas.getByTestId("root").firstChild;
23
- expect(root).toHaveClass("test-classname");
24
- expect(root).toHaveAttribute("id", "test-id");
25
- },
26
- };