@usefui/components 1.5.1

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 (104) hide show
  1. package/CHANGELOG.md +233 -0
  2. package/LICENSE +21 -0
  3. package/README.md +0 -0
  4. package/babel.config.js +12 -0
  5. package/dist/index.d.mts +1299 -0
  6. package/dist/index.d.ts +1299 -0
  7. package/dist/index.js +3701 -0
  8. package/dist/index.mjs +3586 -0
  9. package/package.json +44 -0
  10. package/src/__tests__/Accordion.test.tsx +106 -0
  11. package/src/__tests__/Avatar.test.tsx +89 -0
  12. package/src/__tests__/Badge.test.tsx +58 -0
  13. package/src/__tests__/Button.test.tsx +88 -0
  14. package/src/__tests__/Checkbox.test.tsx +106 -0
  15. package/src/__tests__/Collapsible.test.tsx +79 -0
  16. package/src/__tests__/Dialog.test.tsx +109 -0
  17. package/src/__tests__/Dropdown.test.tsx +159 -0
  18. package/src/__tests__/Field.test.tsx +100 -0
  19. package/src/__tests__/OTPField.test.tsx +199 -0
  20. package/src/__tests__/Overlay.test.tsx +70 -0
  21. package/src/__tests__/Page.test.tsx +98 -0
  22. package/src/__tests__/Portal.test.tsx +28 -0
  23. package/src/__tests__/Sheet.test.tsx +125 -0
  24. package/src/__tests__/Switch.test.tsx +90 -0
  25. package/src/__tests__/Tabs.test.tsx +129 -0
  26. package/src/__tests__/Toggle.test.tsx +67 -0
  27. package/src/__tests__/Toolbar.test.tsx +147 -0
  28. package/src/__tests__/Tooltip.test.tsx +88 -0
  29. package/src/accordion/Accordion.stories.tsx +89 -0
  30. package/src/accordion/hooks/index.tsx +39 -0
  31. package/src/accordion/index.tsx +170 -0
  32. package/src/avatar/Avatar.stories.tsx +62 -0
  33. package/src/avatar/index.tsx +90 -0
  34. package/src/avatar/styles/index.ts +79 -0
  35. package/src/badge/Badge.stories.tsx +60 -0
  36. package/src/badge/index.tsx +58 -0
  37. package/src/badge/styles/index.ts +109 -0
  38. package/src/button/Button.stories.tsx +47 -0
  39. package/src/button/index.tsx +79 -0
  40. package/src/button/styles/index.ts +180 -0
  41. package/src/checkbox/Checkbox.stories.tsx +100 -0
  42. package/src/checkbox/hooks/index.tsx +40 -0
  43. package/src/checkbox/index.tsx +147 -0
  44. package/src/checkbox/styles/index.ts +139 -0
  45. package/src/collapsible/Collapsible.stories.tsx +95 -0
  46. package/src/collapsible/hooks/index.tsx +50 -0
  47. package/src/collapsible/index.tsx +137 -0
  48. package/src/dialog/Dialog.stories.tsx +73 -0
  49. package/src/dialog/hooks/index.tsx +35 -0
  50. package/src/dialog/index.tsx +221 -0
  51. package/src/dialog/styles/index.ts +72 -0
  52. package/src/divider/index.ts +10 -0
  53. package/src/dropdown/Dropdown.stories.tsx +100 -0
  54. package/src/dropdown/hooks/index.tsx +64 -0
  55. package/src/dropdown/index.tsx +316 -0
  56. package/src/dropdown/styles/index.ts +90 -0
  57. package/src/field/Field.stories.tsx +146 -0
  58. package/src/field/hooks/index.tsx +28 -0
  59. package/src/field/index.tsx +183 -0
  60. package/src/field/styles/index.ts +166 -0
  61. package/src/index.ts +33 -0
  62. package/src/otp-field/OTPField.stories.tsx +50 -0
  63. package/src/otp-field/hooks/index.tsx +13 -0
  64. package/src/otp-field/index.tsx +234 -0
  65. package/src/otp-field/styles/index.ts +33 -0
  66. package/src/otp-field/types/index.ts +23 -0
  67. package/src/overlay/Overlay.stories.tsx +59 -0
  68. package/src/overlay/index.tsx +58 -0
  69. package/src/overlay/styles/index.ts +26 -0
  70. package/src/page/Page.stories.tsx +85 -0
  71. package/src/page/index.tsx +265 -0
  72. package/src/page/styles/index.ts +59 -0
  73. package/src/portal/Portal.stories.tsx +27 -0
  74. package/src/portal/index.tsx +36 -0
  75. package/src/scrollarea/Scrollarea.stories.tsx +99 -0
  76. package/src/scrollarea/index.tsx +27 -0
  77. package/src/scrollarea/styles/index.ts +71 -0
  78. package/src/sheet/Sheet.stories.tsx +86 -0
  79. package/src/sheet/hooks/index.tsx +47 -0
  80. package/src/sheet/index.tsx +190 -0
  81. package/src/sheet/styles/index.ts +69 -0
  82. package/src/switch/Switch.stories.tsx +96 -0
  83. package/src/switch/hooks/index.tsx +33 -0
  84. package/src/switch/index.tsx +122 -0
  85. package/src/switch/styles/index.ts +118 -0
  86. package/src/table/index.tsx +138 -0
  87. package/src/table/styles/index.ts +48 -0
  88. package/src/tabs/Tabs.stories.tsx +87 -0
  89. package/src/tabs/hooks/index.tsx +35 -0
  90. package/src/tabs/index.tsx +161 -0
  91. package/src/tabs/styles/index.ts +9 -0
  92. package/src/toggle/Toggle.stories.tsx +118 -0
  93. package/src/toggle/index.tsx +55 -0
  94. package/src/toggle/styles/index.ts +0 -0
  95. package/src/toolbar/Toolbar.stories.tsx +89 -0
  96. package/src/toolbar/hooks/index.tsx +35 -0
  97. package/src/toolbar/index.tsx +243 -0
  98. package/src/toolbar/styles/index.ts +129 -0
  99. package/src/tooltip/Tooltip.stories.tsx +60 -0
  100. package/src/tooltip/index.tsx +177 -0
  101. package/src/tooltip/styles/index.ts +38 -0
  102. package/src/utils/index.ts +2 -0
  103. package/tsconfig.json +18 -0
  104. package/vitest.config.ts +16 -0
@@ -0,0 +1,99 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import { ScrollArea } from "..";
4
+
5
+ // Duplicated doc: The JSDoc content isn't rendering on Storybook.
6
+
7
+ /**
8
+ * Add native scroll functionality to custom components.
9
+ */
10
+ const meta = {
11
+ title: "Components/ScrollArea",
12
+ component: ScrollArea,
13
+ tags: ["autodocs"],
14
+ } satisfies Meta<typeof ScrollArea>;
15
+ export default meta;
16
+
17
+ type Story = StoryObj<typeof meta>;
18
+ export const Default: Story = {
19
+ args: {
20
+ scrollbar: false,
21
+ },
22
+ render: ({ ...args }) => {
23
+ return (
24
+ <ScrollArea
25
+ className="h-100 w-100 flex align-center justify-center p-large-10"
26
+ style={{ maxWidth: 1024, margin: "0 auto" }}
27
+ {...args}
28
+ >
29
+ Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dicta autem
30
+ incidunt voluptatum dignissimos, aperiam nostrum, explicabo adipisci
31
+ ipsum magnam iste id accusamus, sed inventore ex eaque? Vitae iusto odit
32
+ temporibus. Lorem ipsum dolor sit, amet consectetur adipisicing elit.
33
+ Dicta autem incidunt voluptatum dignissimos, aperiam nostrum, explicabo
34
+ adipisci ipsum magnam iste id accusamus, sed inventore ex eaque? Vitae
35
+ iusto odit temporibus. Lorem ipsum dolor sit, amet consectetur
36
+ adipisicing elit. Dicta autem incidunt voluptatum dignissimos, aperiam
37
+ nostrum, explicabo adipisci ipsum magnam iste id accusamus, sed
38
+ inventore ex eaque? Vitae iusto odit temporibus. Lorem ipsum dolor sit,
39
+ amet consectetur adipisicing elit. Dicta autem incidunt voluptatum
40
+ dignissimos, aperiam nostrum, explicabo adipisci ipsum magnam iste id
41
+ accusamus, sed inventore ex eaque? Vitae iusto odit temporibus. Lorem
42
+ ipsum dolor sit, amet consectetur adipisicing elit. Dicta autem incidunt
43
+ voluptatum dignissimos, aperiam nostrum, explicabo adipisci ipsum magnam
44
+ iste id accusamus, sed inventore ex eaque? Vitae iusto odit temporibus.
45
+ Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dicta autem
46
+ incidunt voluptatum dignissimos, aperiam nostrum, explicabo adipisci
47
+ ipsum magnam iste id accusamus, sed inventore ex eaque? Vitae iusto odit
48
+ temporibus. Lorem ipsum dolor sit, amet consectetur adipisicing elit.
49
+ Dicta autem incidunt voluptatum dignissimos, aperiam nostrum, explicabo
50
+ adipisci ipsum magnam iste id accusamus, sed inventore ex eaque? Vitae
51
+ iusto odit temporibus. Lorem ipsum dolor sit, amet consectetur
52
+ adipisicing elit. Dicta autem incidunt voluptatum dignissimos, aperiam
53
+ nostrum, explicabo adipisci ipsum magnam iste id accusamus, sed
54
+ inventore ex eaque? Vitae iusto odit temporibus.
55
+ </ScrollArea>
56
+ );
57
+ },
58
+ };
59
+ export const Scrollbar: Story = {
60
+ args: {
61
+ scrollbar: true,
62
+ },
63
+ render: ({ ...args }) => {
64
+ return (
65
+ <ScrollArea
66
+ className="h-100 w-100 flex align-center justify-center p-large-10"
67
+ style={{ maxWidth: 1024, margin: "0 auto" }}
68
+ {...args}
69
+ >
70
+ Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dicta autem
71
+ incidunt voluptatum dignissimos, aperiam nostrum, explicabo adipisci
72
+ ipsum magnam iste id accusamus, sed inventore ex eaque? Vitae iusto odit
73
+ temporibus. Lorem ipsum dolor sit, amet consectetur adipisicing elit.
74
+ Dicta autem incidunt voluptatum dignissimos, aperiam nostrum, explicabo
75
+ adipisci ipsum magnam iste id accusamus, sed inventore ex eaque? Vitae
76
+ iusto odit temporibus. Lorem ipsum dolor sit, amet consectetur
77
+ adipisicing elit. Dicta autem incidunt voluptatum dignissimos, aperiam
78
+ nostrum, explicabo adipisci ipsum magnam iste id accusamus, sed
79
+ inventore ex eaque? Vitae iusto odit temporibus. Lorem ipsum dolor sit,
80
+ amet consectetur adipisicing elit. Dicta autem incidunt voluptatum
81
+ dignissimos, aperiam nostrum, explicabo adipisci ipsum magnam iste id
82
+ accusamus, sed inventore ex eaque? Vitae iusto odit temporibus. Lorem
83
+ ipsum dolor sit, amet consectetur adipisicing elit. Dicta autem incidunt
84
+ voluptatum dignissimos, aperiam nostrum, explicabo adipisci ipsum magnam
85
+ iste id accusamus, sed inventore ex eaque? Vitae iusto odit temporibus.
86
+ Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dicta autem
87
+ incidunt voluptatum dignissimos, aperiam nostrum, explicabo adipisci
88
+ ipsum magnam iste id accusamus, sed inventore ex eaque? Vitae iusto odit
89
+ temporibus. Lorem ipsum dolor sit, amet consectetur adipisicing elit.
90
+ Dicta autem incidunt voluptatum dignissimos, aperiam nostrum, explicabo
91
+ adipisci ipsum magnam iste id accusamus, sed inventore ex eaque? Vitae
92
+ iusto odit temporibus. Lorem ipsum dolor sit, amet consectetur
93
+ adipisicing elit. Dicta autem incidunt voluptatum dignissimos, aperiam
94
+ nostrum, explicabo adipisci ipsum magnam iste id accusamus, sed
95
+ inventore ex eaque? Vitae iusto odit temporibus.
96
+ </ScrollArea>
97
+ );
98
+ },
99
+ };
@@ -0,0 +1,27 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { ScrollAreaWrapper } from "./styles";
5
+
6
+ export interface IScrollAreaProperties extends React.ComponentProps<any> {
7
+ scrollbar?: boolean;
8
+ }
9
+
10
+ /**
11
+ * Add native scroll functionality to custom components.
12
+ */
13
+ export const ScrollArea = ({
14
+ scrollbar = false,
15
+ children,
16
+ ...restProps
17
+ }: IScrollAreaProperties) => {
18
+ return (
19
+ <ScrollAreaWrapper
20
+ aria-hidden="true"
21
+ data-scrollbar={scrollbar}
22
+ {...restProps}
23
+ >
24
+ {children}
25
+ </ScrollAreaWrapper>
26
+ );
27
+ };
@@ -0,0 +1,71 @@
1
+ import styled, { css } from "styled-components";
2
+
3
+ export const HiddenScrollbar = css`
4
+ scrollbar-width: none;
5
+
6
+ &::-webkit-scrollbar {
7
+ display: none;
8
+ width: 0;
9
+ height: 0;
10
+ }
11
+ &::-moz-scrollbar {
12
+ display: none;
13
+ }
14
+ `;
15
+
16
+ interface ScrollContainerProps {
17
+ $height?: string;
18
+ $width?: string;
19
+ $thumbColor?: string;
20
+ $trackColor?: string;
21
+ $thumbHoverColor?: string;
22
+ }
23
+
24
+ export const CustomScrollbar = css<ScrollContainerProps>`
25
+ height: ${({ $height }) => $height || "100%"};
26
+ width: ${({ $width }) => $width || "100%"};
27
+ overflow-y: auto;
28
+ overflow-x: hidden;
29
+
30
+ &::-webkit-scrollbar {
31
+ cursor: pointer;
32
+
33
+ width: var(--measurement-medium-10);
34
+ }
35
+
36
+ &::-webkit-scrollbar-track {
37
+ background: ${({ $trackColor }) => $trackColor || "transparent"};
38
+ border-radius: var(--measurement-medium-30);
39
+ border: none;
40
+ }
41
+
42
+ &::-webkit-scrollbar-thumb {
43
+ background: ${({ $thumbColor }) =>
44
+ $thumbColor || "var(--font-color-alpha-10)"};
45
+ border-radius: var(--measurement-medium-30);
46
+ transition: background-color 0.2s ease;
47
+ }
48
+
49
+ &::-webkit-scrollbar-thumb:hover {
50
+ background: ${({ $thumbHoverColor, $thumbColor }) =>
51
+ $thumbHoverColor || $thumbColor || "var(--font-color-alpha-20)"};
52
+ }
53
+
54
+ // Firefox
55
+ scrollbar-width: thin;
56
+ scrollbar-color: ${({ $thumbColor, $trackColor }) =>
57
+ `${$thumbColor || "var(--font-color-alpha-10)"} ${
58
+ $trackColor || "transparent"
59
+ }`};
60
+ `;
61
+
62
+ export const ScrollAreaWrapper = styled.div<any>`
63
+ overflow: scroll;
64
+
65
+ &[data-scrollbar="true"] {
66
+ ${CustomScrollbar}
67
+ }
68
+ &[data-scrollbar="false"] {
69
+ ${HiddenScrollbar}
70
+ }
71
+ `;
@@ -0,0 +1,86 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+
4
+ import { Sheet } from "..";
5
+ import { ComponentSizeEnum, ComponentSideEnum } from "../../../../types";
6
+
7
+ // Duplicated doc: The JSDoc content isn't rendering on Storybook.
8
+
9
+ /**
10
+ * Sheets are component that provides additional information in a top layer.
11
+ *
12
+ * **Best practices:**
13
+ *
14
+ * - Use semantic HTML elements to structure the content of the sheet.
15
+ * - Ensure that the sheet is visible and accessible to all users, including those using assistive technologies.
16
+ * - Use keyboard shortcuts to provide an alternative way of interacting with the sheet.
17
+ * - Ensure that the sheet is responsive and adapts to different screen sizes and orientations.
18
+ *
19
+ */
20
+ const meta = {
21
+ title: "Components/Sheet",
22
+ component: Sheet,
23
+ tags: ["autodocs"],
24
+ } satisfies Meta<typeof Sheet>;
25
+ export default meta;
26
+
27
+ type Story = StoryObj<typeof meta>;
28
+ export const Default = {
29
+ args: {
30
+ raw: false,
31
+ open: false,
32
+ lock: false,
33
+ overlay: false,
34
+ closeOnInteract: false,
35
+ shortcut: false,
36
+ sizing: "small",
37
+ side: "right",
38
+ hotkey: "k",
39
+ bindkey: null,
40
+ },
41
+ argTypes: {
42
+ side: {
43
+ options: [ComponentSideEnum.Right, ComponentSideEnum.Left],
44
+ control: { type: "radio" },
45
+ },
46
+ sizing: {
47
+ options: [
48
+ ComponentSizeEnum.Small,
49
+ ComponentSizeEnum.Medium,
50
+ ComponentSizeEnum.Large,
51
+ ],
52
+ control: { type: "radio" },
53
+ },
54
+ },
55
+ render: ({ ...args }) => (
56
+ <Sheet.Root>
57
+ <Sheet.Trigger>&hArr;</Sheet.Trigger>
58
+ <Sheet>
59
+ 🐻‍❄️
60
+ <Sheet.Trigger>&hArr;</Sheet.Trigger>
61
+ </Sheet>
62
+ </Sheet.Root>
63
+ ),
64
+ };
65
+ export const DefaultOpen: Story = {
66
+ render: ({ ...args }) => (
67
+ <Sheet.Root>
68
+ <Sheet.Trigger>&hArr;</Sheet.Trigger>
69
+ <Sheet open lock={false}>
70
+ 🐻‍❄️
71
+ <Sheet.Trigger>&hArr;</Sheet.Trigger>
72
+ </Sheet>
73
+ </Sheet.Root>
74
+ ),
75
+ };
76
+ export const Shortcut: Story = {
77
+ render: ({ ...args }) => (
78
+ <Sheet.Root>
79
+ <Sheet.Trigger>&hArr;</Sheet.Trigger>
80
+ <Sheet shortcut bindkey="ctrlKey" hotkey="t">
81
+ 🐻‍❄️
82
+ <Sheet.Trigger>&hArr;</Sheet.Trigger>
83
+ </Sheet>
84
+ </Sheet.Root>
85
+ ),
86
+ };
@@ -0,0 +1,47 @@
1
+ import React from "react";
2
+ import { IReactChildren } from "../../../../../types";
3
+
4
+ type MethodsType = React.Dispatch<React.SetStateAction<boolean>> | any;
5
+ interface IContextProperties {
6
+ id: Record<string, string>;
7
+ states: Record<string, string | boolean>;
8
+ methods: Record<string, MethodsType>;
9
+ }
10
+
11
+ const SheetContext = React.createContext<IContextProperties>({
12
+ id: {},
13
+ states: {},
14
+ methods: {},
15
+ });
16
+ export const useSheet = () => React.useContext(SheetContext);
17
+
18
+ export const SheetProvider = ({ children }: IReactChildren): JSX.Element => {
19
+ const context = useSheetProvider();
20
+
21
+ return (
22
+ <SheetContext.Provider value={context}>{children}</SheetContext.Provider>
23
+ );
24
+ };
25
+
26
+ function useSheetProvider(): IContextProperties {
27
+ const containerId = React.useId();
28
+ const triggerId = React.useId();
29
+ const controlId = React.useId();
30
+
31
+ const [open, setOpen] = React.useState<boolean>(false);
32
+
33
+ return {
34
+ id: {
35
+ containerId,
36
+ triggerId,
37
+ controlId,
38
+ },
39
+ states: {
40
+ open,
41
+ },
42
+ methods: {
43
+ setOpen,
44
+ toggle: () => setOpen(!open),
45
+ },
46
+ };
47
+ }
@@ -0,0 +1,190 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+
5
+ import { useKeyPress, useDisabledScroll } from "@usefui/hooks";
6
+ import { SheetProvider, useSheet } from "./hooks";
7
+
8
+ import { Button, Overlay } from "../";
9
+ import { SheetWrapper } from "./styles";
10
+
11
+ import { applyDataState } from "../utils";
12
+
13
+ import { IButtonProperties } from "../button";
14
+ import {
15
+ ComponentVariantEnum,
16
+ ComponentSizeEnum,
17
+ ComponentSideEnum,
18
+ IComponentStyling,
19
+ IComponentSize,
20
+ IComponentControlProperties,
21
+ IReactChildren,
22
+ KeyBindingEnum,
23
+ } from "../../../../types";
24
+
25
+ export interface ISheetProperties
26
+ extends IComponentStyling,
27
+ IComponentSize,
28
+ IComponentControlProperties,
29
+ React.ComponentProps<"dialog"> {
30
+ side?: "left" | "right";
31
+ lock?: boolean;
32
+ overlay?: boolean;
33
+ closeOnInteract?: boolean;
34
+ open?: boolean;
35
+ }
36
+
37
+ export interface ISheetComposition {
38
+ Root: typeof SheetRoot;
39
+ Trigger: typeof SheetTrigger;
40
+ }
41
+
42
+ const SheetRoot = ({ children }: IReactChildren) => {
43
+ return <SheetProvider>{children}</SheetProvider>;
44
+ };
45
+ SheetRoot.displayName = "Sheet.Root";
46
+
47
+ /**
48
+ * Sheets are component that provides additional information in a top layer.
49
+ *
50
+ * **Best practices:**
51
+ *
52
+ * - Use semantic HTML elements to structure the content of the sheet.
53
+ * - Ensure that the sheet is visible and accessible to all users, including those using assistive technologies.
54
+ * - Use keyboard shortcuts to provide an alternative way of interacting with the sheet.
55
+ * - Ensure that the sheet is responsive and adapts to different screen sizes and orientations.
56
+ *
57
+ * @param {ISheetProperties} props - The props for the Sheet component.
58
+ * @param {boolean} props.raw - Define whether the component is styled or not.
59
+ * @param {string} props.shortcut - The key combination used as keyboard shortcuts to trigger the sheet.
60
+ * @param {string} props.hotkey - The key to use in the key combination for the keyboard shortcuts.
61
+ * @param {KeyBindingEnum} props.bindkey - The modifier key to use in the key combination.
62
+ * @param {TComponentSide} props.sizing - The size of the sheet.
63
+ * @param {ComponentSideEnum} props.side - The side of the sheet.
64
+ * @param {boolean} props.open - Whether the sheet should be open by default.
65
+ * @param {boolean} props.lock - Whether the sheet blocks the scroll once opened. True by default.
66
+ * @param {boolean} props.overlay - Whether the sheet has an overlay between the page and the sheet itself.
67
+ * @param {boolean} props.closeOnInteract - Whether the over should be closed if interacted with.
68
+ * @param {ReactNode} props.children - The content to be rendered inside the sheet.
69
+ * @returns {ReactElement} The Sheet component.
70
+ */
71
+ const Sheet = (props: ISheetProperties) => {
72
+ const {
73
+ raw,
74
+ sizing = ComponentSizeEnum.Medium,
75
+ side = ComponentSideEnum.Right,
76
+ lock = true,
77
+ overlay = true,
78
+ closeOnInteract = true,
79
+ open,
80
+ shortcut,
81
+ bindkey = KeyBindingEnum.Ctrl,
82
+ hotkey,
83
+ children,
84
+ ...restProps
85
+ } = props;
86
+ const { id, states, methods } = useSheet();
87
+ const { toggle } = methods;
88
+ const shortcutControls = useKeyPress(String(hotkey), true, bindkey);
89
+
90
+ React.useEffect(() => {
91
+ if (open && toggle) return toggle();
92
+ }, [open]);
93
+
94
+ React.useEffect(() => {
95
+ if (shortcut && shortcutControls && toggle) {
96
+ return toggle();
97
+ }
98
+ }, [shortcutControls]);
99
+
100
+ useDisabledScroll(lock && Boolean(states.open));
101
+ return (
102
+ <React.Fragment>
103
+ {states.open && (
104
+ <React.Fragment>
105
+ <SheetWrapper
106
+ role="dialog"
107
+ tabIndex={-1}
108
+ id={String(id.containerId)}
109
+ aria-label={props["aria-label"] ?? `${id.containerId}-sheet`}
110
+ aria-labelledby={String(id.containerId)}
111
+ open={Boolean(states.open)}
112
+ data-expanded={Boolean(states.open)}
113
+ data-state={applyDataState(Boolean(states.open))}
114
+ data-size={sizing}
115
+ data-side={side}
116
+ data-raw={Boolean(raw)}
117
+ {...restProps}
118
+ >
119
+ {children}
120
+ </SheetWrapper>
121
+ {overlay && (
122
+ <Overlay
123
+ onClick={() => toggle && toggle(!states.open)}
124
+ visible={Boolean(states.open)}
125
+ closeOnInteract={closeOnInteract}
126
+ aria-label={
127
+ props["aria-label"]
128
+ ? `${props["aria-label"]}-overlay`
129
+ : `${id.containerId}-sheet-overlay`
130
+ }
131
+ />
132
+ )}
133
+ </React.Fragment>
134
+ )}
135
+ </React.Fragment>
136
+ );
137
+ };
138
+ Sheet.displayName = "Sheet";
139
+
140
+ /**
141
+ * Sheet.Trigger are used to triggers the expansion and collapse of the associated Sheet component.
142
+ *
143
+ * **Best practices:**
144
+ *
145
+ * - Use a clear and descriptive title for the trigger that accurately conveys the content of the associated sheet section.
146
+ * - Ensure that the trigger can be operated using only the keyboard.
147
+ * - Ensure that the focus is properly managed when the trigger is activated.
148
+ *
149
+ * @param {IButtonProperties} props - The props for the Sheet.Trigger component.
150
+ * @param {ReactNode} props.children - The content to be rendered inside the Sheet trigger.
151
+ * @returns {ReactElement} The Sheet.Trigger component.
152
+ */
153
+ const SheetTrigger = (props: IButtonProperties) => {
154
+ const {
155
+ raw,
156
+ onClick,
157
+ variant = ComponentVariantEnum.Ghost,
158
+ sizing = ComponentSizeEnum.Small,
159
+ children,
160
+ ...restProps
161
+ } = props;
162
+ const { id, methods, states } = useSheet();
163
+ const { toggle } = methods;
164
+
165
+ const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
166
+ if (onClick) onClick(event);
167
+ if (toggle) toggle(!states.open);
168
+ };
169
+
170
+ return (
171
+ <Button
172
+ id={id.triggerId}
173
+ aria-controls={String(id.containerId)}
174
+ data-state={applyDataState(Boolean(states.open))}
175
+ variant={variant}
176
+ sizing={sizing}
177
+ raw={Boolean(raw)}
178
+ onClick={handleClick}
179
+ {...restProps}
180
+ >
181
+ {children}
182
+ </Button>
183
+ );
184
+ };
185
+ SheetTrigger.displayName = "Sheet.Trigger";
186
+
187
+ Sheet.Root = SheetRoot;
188
+ Sheet.Trigger = SheetTrigger;
189
+
190
+ export { Sheet, SheetRoot, SheetTrigger };
@@ -0,0 +1,69 @@
1
+ import styled, { css } from "styled-components";
2
+
3
+ const SheetStyles = css`
4
+ all: unset;
5
+ position: fixed;
6
+ background-color: var(--body-color);
7
+ border: var(--measurement-small-10) solid transparent;
8
+ padding: var(--measurement-medium-60);
9
+ height: 100%;
10
+ z-index: var(--depth-default-100);
11
+
12
+ @keyframes slide-right {
13
+ 0% {
14
+ transform: translateX(calc(var(--measurement-large-90) * 2));
15
+ }
16
+ 100% {
17
+ transform: translateX(0);
18
+ }
19
+ }
20
+ @keyframes slide-left {
21
+ 0% {
22
+ transform: translateX(calc(var(--measurement-large-90) * -2));
23
+ }
24
+ 100% {
25
+ transform: translateX(0);
26
+ }
27
+ }
28
+
29
+ animation-delay: 0.2s;
30
+ animation-duration: 0.2s;
31
+ animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1);
32
+ animation-fill-mode: backwards;
33
+ `;
34
+ const SheetSizeStyles = css`
35
+ &[data-size="small"] {
36
+ width: var(--measurement-large-80);
37
+ }
38
+
39
+ &[data-size="medium"] {
40
+ width: var(--measurement-large-90);
41
+ }
42
+
43
+ &[data-size="large"] {
44
+ width: calc(var(--measurement-large-90) * 1.5);
45
+ }
46
+ `;
47
+ const SheetSideStyles = css`
48
+ top: 0;
49
+
50
+ &[data-side="right"] {
51
+ right: 0;
52
+ border-left-color: var(--font-color-alpha-10);
53
+ animation-name: slide-right;
54
+ }
55
+
56
+ &[data-side="left"] {
57
+ left: 0;
58
+ border-right-color: var(--font-color-alpha-10);
59
+ animation-name: slide-left;
60
+ }
61
+ `;
62
+
63
+ export const SheetWrapper = styled.dialog<any>`
64
+ &[data-raw="false"] {
65
+ ${SheetStyles}
66
+ ${SheetSideStyles}
67
+ ${SheetSizeStyles}
68
+ }
69
+ `;
@@ -0,0 +1,96 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+
4
+ import { Switch } from "..";
5
+ import { ComponentSizeEnum, ComponentVariantEnum } from "../../../../types";
6
+
7
+ // Duplicated doc: The JSDoc content isn't rendering on Storybook.
8
+
9
+ /**
10
+ * Switch are toggle components that allows the user to turn a setting on or off.
11
+ *
12
+ * **Best practices:**
13
+ *
14
+ * - Use a clear and descriptive label for each switch.
15
+ * - The interaction must have predictable behavior.
16
+ */
17
+ const meta = {
18
+ title: "Components/Switch",
19
+ component: Switch,
20
+ tags: ["autodocs"],
21
+ decorators: [
22
+ (Story) => (
23
+ <div className="m-medium-30">
24
+ <Story />
25
+ </div>
26
+ ),
27
+ ],
28
+ } satisfies Meta<typeof Switch>;
29
+ export default meta;
30
+
31
+ type Story = StoryObj<typeof meta>;
32
+ export const Default: Story = {
33
+ args: {
34
+ raw: false,
35
+ defaultChecked: false,
36
+ },
37
+ argTypes: {
38
+ variant: {
39
+ options: [ComponentVariantEnum.Primary, ComponentVariantEnum.Ghost],
40
+ control: { type: "radio" },
41
+ },
42
+ sizing: {
43
+ options: [
44
+ ComponentSizeEnum.Small,
45
+ ComponentSizeEnum.Medium,
46
+ ComponentSizeEnum.Large,
47
+ ],
48
+ control: { type: "radio" },
49
+ },
50
+ },
51
+ render: ({ ...args }) => (
52
+ <Switch.Root>
53
+ <Switch>
54
+ <Switch.Thumb />
55
+ </Switch>
56
+ </Switch.Root>
57
+ ),
58
+ };
59
+ export const DefaultChecked: Story = {
60
+ render: ({ ...args }) => (
61
+ <Switch.Root>
62
+ <Switch defaultChecked>
63
+ <Switch.Thumb />
64
+ </Switch>
65
+ </Switch.Root>
66
+ ),
67
+ };
68
+ export const Sizes: Story = {
69
+ render: ({ ...args }) => (
70
+ <div className="flex g-medium-30">
71
+ {["large", "medium", "small"].map((item) => (
72
+ <Switch.Root key={item}>
73
+ <Switch sizing={item}>
74
+ <Switch.Thumb />
75
+ </Switch>
76
+ </Switch.Root>
77
+ ))}
78
+ </div>
79
+ ),
80
+ };
81
+ export const Variants: Story = {
82
+ render: ({ ...args }) => (
83
+ <div className="flex g-medium-30">
84
+ {["primary", "ghost"].map((item) => (
85
+ <Switch.Root key={item}>
86
+ <Switch name={item} variant={item}>
87
+ <Switch.Thumb />
88
+ </Switch>
89
+ <label htmlFor={item}>
90
+ <small>{item}</small>
91
+ </label>
92
+ </Switch.Root>
93
+ ))}
94
+ </div>
95
+ ),
96
+ };