pentatrion-design 0.0.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.
- package/.storybook/main.ts +33 -0
- package/.storybook/manager.ts +8 -0
- package/.storybook/preview-head.html +2 -0
- package/.storybook/preview.ts +26 -0
- package/.storybook/public/bg-map.jpg +0 -0
- package/.storybook/public/favicon-32x32.png +0 -0
- package/.storybook/public/favicon.svg +28 -0
- package/.storybook/public/movies.json +2153 -0
- package/.storybook/public/population.json +1178 -0
- package/.storybook/public/town-74.geojson +8995 -0
- package/.storybook/public/town-74.json +4062 -0
- package/.storybook/theme.ts +14 -0
- package/Makefile +14 -0
- package/README.md +40 -0
- package/TODO.md +5 -0
- package/dist/index.d.ts +1110 -0
- package/dist/index.js +3707 -0
- package/dist/redux/index.d.ts +43 -0
- package/dist/redux/index.js +583 -0
- package/dist/types.d-DlHa_IaW.d.ts +14 -0
- package/images/logo-shadow.svg +173 -0
- package/images/logo.svg +76 -0
- package/package.json +85 -0
- package/postcss.config.js +7 -0
- package/src/components/autocomplete/Autocomplete.stories.tsx +89 -0
- package/src/components/autocomplete/Autocomplete.tsx +316 -0
- package/src/components/autocomplete/AutocompleteOption.tsx +35 -0
- package/src/components/autocomplete/LazyAutocomplete.tsx +130 -0
- package/src/components/autocomplete/SimpleAutocomplete.tsx +74 -0
- package/src/components/autocomplete/index.ts +6 -0
- package/src/components/autocomplete/useAutocompleteContext.ts +20 -0
- package/src/components/autocomplete/util.ts +19 -0
- package/src/components/badge/Badge.stories.tsx +72 -0
- package/src/components/badge/Badge.tsx +70 -0
- package/src/components/button/Button.stories.tsx +189 -0
- package/src/components/button/Button.test.tsx +20 -0
- package/src/components/button/Button.tsx +200 -0
- package/src/components/button/ButtonGroup.stories.tsx +46 -0
- package/src/components/button/ButtonGroup.tsx +29 -0
- package/src/components/button/index.ts +2 -0
- package/src/components/card/Card.stories.tsx +21 -0
- package/src/components/card/config.ts +4 -0
- package/src/components/card/index.ts +1 -0
- package/src/components/code/Code.tsx +15 -0
- package/src/components/context-menu/ContextMenu.stories.tsx +22 -0
- package/src/components/context-menu/ContextMenu.tsx +175 -0
- package/src/components/context-menu/ContextMenuItem.tsx +34 -0
- package/src/components/context-menu/index.ts +2 -0
- package/src/components/dialog/Dialog.stories.tsx +270 -0
- package/src/components/dialog/Dialog.tsx +46 -0
- package/src/components/dialog/index.ts +1 -0
- package/src/components/dialog/util.ts +29 -0
- package/src/components/dropdown-menu/DropdownMenu.stories.tsx +19 -0
- package/src/components/dropdown-menu/DropdownMenu.tsx +12 -0
- package/src/components/dropdown-menu/DropdownMenuContent.tsx +46 -0
- package/src/components/dropdown-menu/DropdownMenuItem.tsx +33 -0
- package/src/components/dropdown-menu/DropdownMenuTrigger.tsx +42 -0
- package/src/components/dropdown-menu/index.ts +7 -0
- package/src/components/dropdown-menu/interface.d.ts +13 -0
- package/src/components/dropdown-menu/useDropdownMenu.tsx +115 -0
- package/src/components/dropdown-menu/useDropdownMenuContext.ts +16 -0
- package/src/components/dropdown-menu-nested/DropdownMenuNested.stories.tsx +33 -0
- package/src/components/dropdown-menu-nested/DropdownMenuNested.tsx +19 -0
- package/src/components/dropdown-menu-nested/MenuContext.ts +15 -0
- package/src/components/dropdown-menu-nested/MenuItem.tsx +43 -0
- package/src/components/dropdown-menu-nested/MenuItemWithChildren.tsx +204 -0
- package/src/components/dropdown-menu-nested/index.ts +3 -0
- package/src/components/flash/Flash.stories.tsx +34 -0
- package/src/components/flash/Flash.tsx +24 -0
- package/src/components/highlight/Highlight.stories.tsx +128 -0
- package/src/components/highlight/Highlight.tsx +29 -0
- package/src/components/href/Href.stories.tsx +55 -0
- package/src/components/href/Href.tsx +18 -0
- package/src/components/input/Checkbox.stories.tsx +52 -0
- package/src/components/input/Checkbox.tsx +41 -0
- package/src/components/input/Input.stories.tsx +103 -0
- package/src/components/input/Input.test.tsx +10 -0
- package/src/components/input/Input.tsx +60 -0
- package/src/components/input/RadioGroup.stories.tsx +76 -0
- package/src/components/input/RadioGroup.tsx +84 -0
- package/src/components/input/Toggle.stories.tsx +29 -0
- package/src/components/input/Toggle.tsx +35 -0
- package/src/components/input/index.ts +4 -0
- package/src/components/input-field/InputField.stories.tsx +227 -0
- package/src/components/input-field/InputField.tsx +62 -0
- package/src/components/loader/Loader.stories.tsx +37 -0
- package/src/components/loader/Loader.tsx +54 -0
- package/src/components/modal/Components.tsx +58 -0
- package/src/components/modal/Modal.stories.tsx +113 -0
- package/src/components/modal/Modal.tsx +13 -0
- package/src/components/modal/ModalContent.tsx +46 -0
- package/src/components/modal/ModalTrigger.tsx +41 -0
- package/src/components/modal/index.ts +7 -0
- package/src/components/modal/interface.d.ts +10 -0
- package/src/components/modal/useModal.ts +48 -0
- package/src/components/modal/useModalContext.ts +16 -0
- package/src/components/notification/Notification.stories.tsx +51 -0
- package/src/components/notification/NotificationsContext.ts +79 -0
- package/src/components/notification/NotificationsProvider.tsx +58 -0
- package/src/components/notification/index.ts +2 -0
- package/src/components/popover/Components.tsx +45 -0
- package/src/components/popover/Popover.stories.tsx +46 -0
- package/src/components/popover/Popover.test.tsx +21 -0
- package/src/components/popover/Popover.tsx +12 -0
- package/src/components/popover/PopoverContent.tsx +46 -0
- package/src/components/popover/PopoverTrigger.tsx +42 -0
- package/src/components/popover/index.ts +7 -0
- package/src/components/popover/interface.d.ts +13 -0
- package/src/components/popover/usePopover.tsx +80 -0
- package/src/components/popover/usePopoverContext.ts +16 -0
- package/src/components/portal/Portal.tsx +12 -0
- package/src/components/quick-settings/QuickSettings.stories.tsx +182 -0
- package/src/components/resize-area/ResizeArea.stories.css +40 -0
- package/src/components/resize-area/ResizeArea.stories.tsx +30 -0
- package/src/components/resize-area/ResizeArea.tsx +79 -0
- package/src/components/scroll/Scroll.stories.tsx +81 -0
- package/src/components/scroll/Scroll.tsx +111 -0
- package/src/components/select/Select.stories.tsx +248 -0
- package/src/components/select/Select.tsx +362 -0
- package/src/components/select/SelectOption.tsx +29 -0
- package/src/components/select/SelectSelection.tsx +13 -0
- package/src/components/select/index.ts +5 -0
- package/src/components/select/interface.d.ts +36 -0
- package/src/components/select/useSelectContext.ts +19 -0
- package/src/components/select/util.ts +12 -0
- package/src/components/snack/Snack.stories.tsx +33 -0
- package/src/components/snack/Snack.tsx +47 -0
- package/src/components/sortable/Sortable.stories.tsx +98 -0
- package/src/components/sortable/Sortable.tsx +37 -0
- package/src/components/sortable/index.ts +2 -0
- package/src/components/sortable/types.d.ts +12 -0
- package/src/components/steps/Step.tsx +41 -0
- package/src/components/steps/Steps.stories.tsx +141 -0
- package/src/components/steps/Steps.tsx +49 -0
- package/src/components/steps/index.ts +3 -0
- package/src/components/steps/util.ts +3 -0
- package/src/components/table/Table.stories.tsx +36 -0
- package/src/components/table/Table.tsx +13 -0
- package/src/components/table/TableBody.tsx +9 -0
- package/src/components/table/TableCell.tsx +16 -0
- package/src/components/table/TableFooter.tsx +9 -0
- package/src/components/table/TableHeader.tsx +14 -0
- package/src/components/table/TableHeaderCell.tsx +14 -0
- package/src/components/table/TableRow.tsx +14 -0
- package/src/components/table/index.ts +7 -0
- package/src/components/tabs/Tabs.stories.tsx +66 -0
- package/src/components/tabs/Tabs.tsx +80 -0
- package/src/components/tooltip/SimpleTooltip.tsx +17 -0
- package/src/components/tooltip/Tooltip.stories.tsx +129 -0
- package/src/components/tooltip/Tooltip.test.tsx +18 -0
- package/src/components/tooltip/Tooltip.tsx +13 -0
- package/src/components/tooltip/TooltipContent.tsx +41 -0
- package/src/components/tooltip/TooltipTrigger.tsx +36 -0
- package/src/components/tooltip/index.ts +6 -0
- package/src/components/tooltip/interface.d.ts +15 -0
- package/src/components/tooltip/useTooltip.ts +87 -0
- package/src/components/tooltip/useTooltipContext.ts +16 -0
- package/src/env.d.ts +27 -0
- package/src/hooks/index.ts +13 -0
- package/src/hooks/useCombinedRefs.test.tsx +42 -0
- package/src/hooks/useCombinedRefs.ts +21 -0
- package/src/hooks/useCopyToClipboard.ts +28 -0
- package/src/hooks/useDebounce.ts +59 -0
- package/src/hooks/useEventCallback.ts +13 -0
- package/src/hooks/useEventListener.ts +86 -0
- package/src/hooks/useFetch.tsx +21 -0
- package/src/hooks/useIsClosing.ts +25 -0
- package/src/hooks/useIsMounted.tsx +16 -0
- package/src/hooks/useIsomorphicLayoutEffect.ts +4 -0
- package/src/hooks/useNotifications.tsx +13 -0
- package/src/hooks/useOnClickOutside.ts +35 -0
- package/src/hooks/usePrevious.ts +9 -0
- package/src/hooks/useRipple.test.tsx +52 -0
- package/src/hooks/useRipple.tsx +79 -0
- package/src/index.css +14 -0
- package/src/index.ts +39 -0
- package/src/lib/arrUtil.ts +8 -0
- package/src/lib/error.ts +18 -0
- package/src/lib/fetch.test.ts +107 -0
- package/src/lib/fetch.ts +115 -0
- package/src/lib/index.ts +3 -0
- package/src/lib/tailwindVariants.ts +35 -0
- package/src/redux/index.ts +1 -0
- package/src/redux/notification/components/NotificationConsumer.tsx +52 -0
- package/src/redux/notification/errorCatcherMiddleware.ts +17 -0
- package/src/redux/notification/hooks/useNotification.ts +26 -0
- package/src/redux/notification/index.ts +5 -0
- package/src/redux/notification/notificationSlice.ts +68 -0
- package/src/styles/Animation.stories.tsx +43 -0
- package/src/styles/Shadow.stories.tsx +43 -0
- package/src/styles/Themes.stories.tsx +31 -0
- package/src/styles/Typography.stories.tsx +101 -0
- package/src/tailwind/base.css +188 -0
- package/src/tailwind/components-input-outline.css +62 -0
- package/src/tailwind/components-resize-area.css +112 -0
- package/src/tailwind/components-step.css +161 -0
- package/src/tailwind/components.css +51 -0
- package/src/tailwind/keyframes.ts +69 -0
- package/src/tailwind/pentatrionTw.ts +147 -0
- package/src/tailwind/utilities-dialog.css +103 -0
- package/src/tailwind/utilities.css +3 -0
- package/src/types.d.ts +12 -0
- package/tailwind.config.js +18 -0
- package/tests/README.md +32 -0
- package/tests/setup.ts +1 -0
- package/tsconfig.json +14 -0
- package/tsup.config.ts +23 -0
- package/vite.config.ts +24 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ComponentPropsWithRef, forwardRef, useRef } from "react";
|
|
2
|
+
import { type ThemeColor } from "~/types.d";
|
|
3
|
+
import { useCombinedRefs } from "~/hooks";
|
|
4
|
+
import clsx from "clsx";
|
|
5
|
+
|
|
6
|
+
interface Props extends ComponentPropsWithRef<"input"> {
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
color?: ThemeColor;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const Toggle = forwardRef<HTMLInputElement, Props>(
|
|
12
|
+
({ color = "yellow", disabled = false, checked, className, children, ...rest }, ref) => {
|
|
13
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
14
|
+
const combinedRef = useCombinedRefs(inputRef, ref);
|
|
15
|
+
return (
|
|
16
|
+
<label className={clsx("cursor-pointer flex items-center", disabled && "disabled")}>
|
|
17
|
+
<input
|
|
18
|
+
data-color={color}
|
|
19
|
+
ref={combinedRef}
|
|
20
|
+
disabled={disabled}
|
|
21
|
+
type="checkbox"
|
|
22
|
+
className={clsx(
|
|
23
|
+
"p8n-input-checkbox p8n-input-toggle ll-toggle w-[30px] h-[18px] box-border appearance-none rounded-xl inline-block relative mr-1 bg-gray-1 checked:bg-gray-0 after:absolute after:top-[3px] after:left-[3px] after:rounded-full after:w-[12px] after:h-[12px] after:bg-gray-2 after:outline after:outline-1 after:outline-gray-3 checked:after:bg-current checked:after:translate-x-[12px] checked:after:outline-current after:transition-all",
|
|
24
|
+
className,
|
|
25
|
+
)}
|
|
26
|
+
checked={checked}
|
|
27
|
+
{...rest}
|
|
28
|
+
/>
|
|
29
|
+
{children}
|
|
30
|
+
</label>
|
|
31
|
+
);
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export default Toggle;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { Meta } from "@storybook/react";
|
|
2
|
+
import { ChangeEvent, useState } from "react";
|
|
3
|
+
import InputField from "./InputField";
|
|
4
|
+
import { Toggle, Checkbox, RadioGroup } from "../input";
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: "Components/InputField",
|
|
8
|
+
component: InputField,
|
|
9
|
+
} satisfies Meta<typeof InputField>;
|
|
10
|
+
export default meta;
|
|
11
|
+
|
|
12
|
+
export const Playbook = () => {
|
|
13
|
+
const [value, setValue] = useState("");
|
|
14
|
+
const [label, setLabel] = useState("Your label");
|
|
15
|
+
const [hint, setHint] = useState("Any hint related to input field");
|
|
16
|
+
const [placeholder, setPlaceholder] = useState("Ex: Fernando");
|
|
17
|
+
const [help, setHelp] = useState("Help message into the bottom");
|
|
18
|
+
const [warning, setWarning] = useState("");
|
|
19
|
+
const [error, setError] = useState("");
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<div className="shadow-md rounded-2xl p-12 mb-12">
|
|
24
|
+
<InputField
|
|
25
|
+
label={label}
|
|
26
|
+
hint={hint}
|
|
27
|
+
placeholder={placeholder}
|
|
28
|
+
help={help}
|
|
29
|
+
value={value}
|
|
30
|
+
warning={warning}
|
|
31
|
+
error={error}
|
|
32
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setValue(e.target.value)}
|
|
33
|
+
/>
|
|
34
|
+
</div>
|
|
35
|
+
<div className="grid grid-cols-2 gap-4">
|
|
36
|
+
<InputField
|
|
37
|
+
label="Label"
|
|
38
|
+
placeholder="What is your name ?"
|
|
39
|
+
value={label}
|
|
40
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setLabel(e.target.value)}
|
|
41
|
+
/>
|
|
42
|
+
<InputField
|
|
43
|
+
label="Hint"
|
|
44
|
+
placeholder="One hint ?"
|
|
45
|
+
value={hint}
|
|
46
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setHint(e.target.value)}
|
|
47
|
+
/>
|
|
48
|
+
<InputField
|
|
49
|
+
label="Placeholder"
|
|
50
|
+
value={placeholder}
|
|
51
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setPlaceholder(e.target.value)}
|
|
52
|
+
/>
|
|
53
|
+
<InputField
|
|
54
|
+
label="Help"
|
|
55
|
+
value={help}
|
|
56
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setHelp(e.target.value)}
|
|
57
|
+
/>
|
|
58
|
+
<InputField
|
|
59
|
+
label="Warning"
|
|
60
|
+
placeholder="warning message"
|
|
61
|
+
value={warning}
|
|
62
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setWarning(e.target.value)}
|
|
63
|
+
/>
|
|
64
|
+
<InputField
|
|
65
|
+
label="Error"
|
|
66
|
+
placeholder="Error message"
|
|
67
|
+
value={error}
|
|
68
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setError(e.target.value)}
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const Context = () => {
|
|
76
|
+
const [name, setName] = useState("");
|
|
77
|
+
const [gender, setGender] = useState<string | null>("female");
|
|
78
|
+
const [isAgree, setIsAgree] = useState(true);
|
|
79
|
+
const [isEnabled, setIsEnabled] = useState(true);
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div className="flex flex-col gap-4">
|
|
83
|
+
<InputField
|
|
84
|
+
label="What is your name"
|
|
85
|
+
hint="Any hint related to input field"
|
|
86
|
+
placeholder="Ex: Fernando"
|
|
87
|
+
help="Help text for your input field"
|
|
88
|
+
value={name}
|
|
89
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setName(e.target.value)}
|
|
90
|
+
/>
|
|
91
|
+
<InputField
|
|
92
|
+
label="What is your name"
|
|
93
|
+
hint="Any hint related to input field"
|
|
94
|
+
placeholder="Ex: Fernando"
|
|
95
|
+
help="Help text for your input field"
|
|
96
|
+
error="Input is required"
|
|
97
|
+
value={name}
|
|
98
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setName(e.target.value)}
|
|
99
|
+
/>
|
|
100
|
+
<InputField
|
|
101
|
+
label="What is your name"
|
|
102
|
+
hint="Any hint related to input field"
|
|
103
|
+
placeholder="Ex: Fernando"
|
|
104
|
+
help="Help text for your input field"
|
|
105
|
+
warning="Only your firstname"
|
|
106
|
+
value={name}
|
|
107
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setName(e.target.value)}
|
|
108
|
+
/>
|
|
109
|
+
<InputField
|
|
110
|
+
value={gender}
|
|
111
|
+
onChange={(value: string | null) => {
|
|
112
|
+
setGender(value);
|
|
113
|
+
}}
|
|
114
|
+
label="What is your gender"
|
|
115
|
+
hint="You don't have to answer"
|
|
116
|
+
as={RadioGroup}
|
|
117
|
+
options={[
|
|
118
|
+
{ label: "Male", value: "male" },
|
|
119
|
+
{ label: "Female", value: "female" },
|
|
120
|
+
{ label: "I don't want to answer", value: "undefined" },
|
|
121
|
+
]}
|
|
122
|
+
help="You still have to check a box"
|
|
123
|
+
/>
|
|
124
|
+
<InputField
|
|
125
|
+
value={gender}
|
|
126
|
+
onChange={(value: string | null) => {
|
|
127
|
+
setGender(value);
|
|
128
|
+
}}
|
|
129
|
+
label="What is your gender"
|
|
130
|
+
hint="You don't have to answer"
|
|
131
|
+
as={RadioGroup}
|
|
132
|
+
error="This field is required"
|
|
133
|
+
options={[
|
|
134
|
+
{ label: "Male", value: "male" },
|
|
135
|
+
{ label: "Female", value: "female" },
|
|
136
|
+
{ label: "I don't want to answer", value: "undefined" },
|
|
137
|
+
]}
|
|
138
|
+
help="You still have to check a box"
|
|
139
|
+
/>
|
|
140
|
+
|
|
141
|
+
<InputField
|
|
142
|
+
value={gender}
|
|
143
|
+
onChange={(value: string | null) => {
|
|
144
|
+
setGender(value);
|
|
145
|
+
}}
|
|
146
|
+
label="What is your gender"
|
|
147
|
+
hint="You don't have to answer"
|
|
148
|
+
as={RadioGroup}
|
|
149
|
+
warning="Make your choice"
|
|
150
|
+
options={[
|
|
151
|
+
{ label: "Male", value: "male" },
|
|
152
|
+
{ label: "Female", value: "female" },
|
|
153
|
+
{ label: "I don't want to answer", value: "undefined" },
|
|
154
|
+
]}
|
|
155
|
+
help="You still have to check a box"
|
|
156
|
+
/>
|
|
157
|
+
<InputField
|
|
158
|
+
checked={isAgree}
|
|
159
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setIsAgree(e.target.checked)}
|
|
160
|
+
label="Gender"
|
|
161
|
+
hint="One hint"
|
|
162
|
+
as={Checkbox}
|
|
163
|
+
>
|
|
164
|
+
I agree
|
|
165
|
+
</InputField>
|
|
166
|
+
<InputField
|
|
167
|
+
checked={isAgree}
|
|
168
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setIsAgree(e.target.checked)}
|
|
169
|
+
label="Gender"
|
|
170
|
+
hint="One hint"
|
|
171
|
+
warning="Make your choice"
|
|
172
|
+
as={Checkbox}
|
|
173
|
+
>
|
|
174
|
+
I agree
|
|
175
|
+
</InputField>
|
|
176
|
+
<InputField
|
|
177
|
+
checked={isAgree}
|
|
178
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setIsAgree(e.target.checked)}
|
|
179
|
+
label="Gender"
|
|
180
|
+
hint="One hint"
|
|
181
|
+
error="This field is required"
|
|
182
|
+
as={Checkbox}
|
|
183
|
+
>
|
|
184
|
+
I agree
|
|
185
|
+
</InputField>
|
|
186
|
+
|
|
187
|
+
<InputField
|
|
188
|
+
checked={isEnabled}
|
|
189
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setIsEnabled(e.target.checked)}
|
|
190
|
+
label="Label"
|
|
191
|
+
hint=""
|
|
192
|
+
as={Toggle}
|
|
193
|
+
>
|
|
194
|
+
Enabled
|
|
195
|
+
</InputField>
|
|
196
|
+
<InputField
|
|
197
|
+
checked={isEnabled}
|
|
198
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setIsEnabled(e.target.checked)}
|
|
199
|
+
label="Label"
|
|
200
|
+
hint="One hint"
|
|
201
|
+
warning="Make your choice"
|
|
202
|
+
as={Toggle}
|
|
203
|
+
>
|
|
204
|
+
Enabled
|
|
205
|
+
</InputField>
|
|
206
|
+
<InputField
|
|
207
|
+
checked={isEnabled}
|
|
208
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => setIsEnabled(e.target.checked)}
|
|
209
|
+
label="Label"
|
|
210
|
+
hint="One hint"
|
|
211
|
+
error="This field is required"
|
|
212
|
+
as={Toggle}
|
|
213
|
+
>
|
|
214
|
+
Enabled
|
|
215
|
+
</InputField>
|
|
216
|
+
<pre>
|
|
217
|
+
name: {name}
|
|
218
|
+
<br />
|
|
219
|
+
gender: {gender}
|
|
220
|
+
<br />
|
|
221
|
+
agree: {isAgree ? "True" : "False"}
|
|
222
|
+
<br />
|
|
223
|
+
enabled: {isEnabled ? "True" : "False"}
|
|
224
|
+
</pre>
|
|
225
|
+
</div>
|
|
226
|
+
);
|
|
227
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ElementType, ForwardedRef, ReactNode, forwardRef, useId } from "react";
|
|
2
|
+
import { Input } from "../input";
|
|
3
|
+
import { type ThemeColor } from "~/types.d";
|
|
4
|
+
import { type PolymorphicPropsWithRef } from "~/env.d";
|
|
5
|
+
|
|
6
|
+
interface InputFieldOwnProps {
|
|
7
|
+
label?: ReactNode;
|
|
8
|
+
hint?: ReactNode;
|
|
9
|
+
help?: ReactNode;
|
|
10
|
+
error?: ReactNode | boolean;
|
|
11
|
+
warning?: ReactNode | boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const defaultElement = Input;
|
|
15
|
+
|
|
16
|
+
type Props<E extends ElementType> = PolymorphicPropsWithRef<InputFieldOwnProps, E>;
|
|
17
|
+
|
|
18
|
+
const InputFieldBase = <E extends ElementType = typeof defaultElement>(
|
|
19
|
+
{ label, hint, help, error, warning, as, ...rest }: Props<E>,
|
|
20
|
+
ref: ForwardedRef<Element>,
|
|
21
|
+
) => {
|
|
22
|
+
const id = useId();
|
|
23
|
+
const Element: ElementType = as || defaultElement;
|
|
24
|
+
|
|
25
|
+
const labelElement = label && <span className="font-bold">{label}</span>;
|
|
26
|
+
const hintElement = hint && <span className="ml-auto text-gray-6 text-sm">{hint}</span>;
|
|
27
|
+
const errorElement = error && typeof error !== "boolean" && (
|
|
28
|
+
<span className="text-red-4 font-medium">
|
|
29
|
+
<i className="fe-circle-exclamation"></i>
|
|
30
|
+
<span>{error}</span>
|
|
31
|
+
</span>
|
|
32
|
+
);
|
|
33
|
+
const warningElement = warning && typeof warning !== "boolean" && (
|
|
34
|
+
<span className="text-orange-4 font-medium">
|
|
35
|
+
<i className="fe-circle-exclamation"></i>
|
|
36
|
+
<span>{warning}</span>
|
|
37
|
+
</span>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const color: ThemeColor = error ? "red" : warning ? "orange" : "yellow";
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div>
|
|
44
|
+
{label || hint ? (
|
|
45
|
+
<label className="flex items-end mb-1" htmlFor={id}>
|
|
46
|
+
{labelElement}
|
|
47
|
+
{hintElement}
|
|
48
|
+
</label>
|
|
49
|
+
) : (
|
|
50
|
+
<label htmlFor={id} className="invisible"></label>
|
|
51
|
+
)}
|
|
52
|
+
<Element ref={ref} id={id} color={color} {...rest} />
|
|
53
|
+
<div className="text-gray-6 text-sm mt-1">{errorElement || warningElement || help}</div>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
InputFieldBase.displayName = "InputField";
|
|
58
|
+
|
|
59
|
+
const InputField: <E extends ElementType = typeof defaultElement>(props: Props<E>) => ReactNode =
|
|
60
|
+
forwardRef(InputFieldBase);
|
|
61
|
+
|
|
62
|
+
export default InputField;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import Loader from "./Loader";
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Components/Loader",
|
|
6
|
+
component: Loader,
|
|
7
|
+
argTypes: {
|
|
8
|
+
color: {
|
|
9
|
+
control: {
|
|
10
|
+
type: "select",
|
|
11
|
+
},
|
|
12
|
+
options: ["yellow", "gray", "red", "orange", "green", "blue"],
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
} satisfies Meta<typeof Loader>;
|
|
16
|
+
export default meta;
|
|
17
|
+
|
|
18
|
+
type Story = StoryObj<typeof meta>;
|
|
19
|
+
|
|
20
|
+
export const Playbook: Story = {
|
|
21
|
+
args: {
|
|
22
|
+
size: "medium",
|
|
23
|
+
color: "yellow",
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const Context = () => (
|
|
28
|
+
<div className="flex gap-2 flex-col">
|
|
29
|
+
<Loader size="small" />
|
|
30
|
+
<br />
|
|
31
|
+
<Loader size="medium" color="yellow" />
|
|
32
|
+
<br />
|
|
33
|
+
<Loader size="large" color="red" />
|
|
34
|
+
<br />
|
|
35
|
+
<Loader size="medium" color="gray" />
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { CSSProperties, ComponentPropsWithoutRef, useId } from "react";
|
|
2
|
+
import { ThemeColor } from "~/types.d";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { colorVariants } from "~/lib/tailwindVariants";
|
|
5
|
+
|
|
6
|
+
interface Props extends ComponentPropsWithoutRef<"svg"> {
|
|
7
|
+
size?: "small" | "medium" | "large";
|
|
8
|
+
color?: ThemeColor;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const sizeConfig = {
|
|
12
|
+
small: "w-6 h-6 text-base",
|
|
13
|
+
medium: "w-8 h-8 text-[2rem]",
|
|
14
|
+
large: "w-12 h-12 text-[3rem]",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const trackStyle: CSSProperties = {
|
|
18
|
+
strokeLinecap: "round",
|
|
19
|
+
strokeWidth: 2,
|
|
20
|
+
};
|
|
21
|
+
const circleStyle: CSSProperties = {
|
|
22
|
+
strokeLinecap: "round",
|
|
23
|
+
strokeWidth: 2,
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* $total-length: 43.699; // total length of path, calculated by getTotalLength() in JS
|
|
27
|
+
* $looping-percent: 40;
|
|
28
|
+
* $looping-length: math.div($looping-percent * $total-length, 100) = 17.464;
|
|
29
|
+
* stroke-dasharray: #{$looping-length * 1px}, #{($total-length - $looping-length) * 1px};
|
|
30
|
+
*/
|
|
31
|
+
strokeDasharray: "17.45px, 26.21px",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default function Loader({ size = "medium", color = "blue", className, ...rest }: Props) {
|
|
35
|
+
const id = useId();
|
|
36
|
+
return (
|
|
37
|
+
<svg
|
|
38
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
39
|
+
className={clsx("inline-block", sizeConfig[size], colorVariants[color].text, className)}
|
|
40
|
+
viewBox="0 0 16 16"
|
|
41
|
+
{...rest}
|
|
42
|
+
>
|
|
43
|
+
<defs>
|
|
44
|
+
<circle id={id} cx="8" cy="8" r="7" />
|
|
45
|
+
</defs>
|
|
46
|
+
<use href={`#${id}`} className="opacity-25 fill-none stroke-current" style={trackStyle} />
|
|
47
|
+
<use
|
|
48
|
+
href={`#${id}`}
|
|
49
|
+
className="fill-none stroke-current animate-loader-stroke"
|
|
50
|
+
style={circleStyle}
|
|
51
|
+
/>
|
|
52
|
+
</svg>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ComponentProps } from "react";
|
|
2
|
+
import { useModalContext } from ".";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { Button } from "../button";
|
|
5
|
+
import Scroll from "../scroll/Scroll";
|
|
6
|
+
|
|
7
|
+
interface ModalHeaderProps extends ComponentProps<"h2"> {
|
|
8
|
+
closeButton?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export function ModalHeader({
|
|
11
|
+
children,
|
|
12
|
+
className,
|
|
13
|
+
closeButton = true,
|
|
14
|
+
...props
|
|
15
|
+
}: ModalHeaderProps) {
|
|
16
|
+
const { setOpen } = useModalContext();
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<>
|
|
20
|
+
{closeButton && (
|
|
21
|
+
<div className="p-1 float-right">
|
|
22
|
+
<Button icon variant="text" color="gray" onClick={() => setOpen(false)}>
|
|
23
|
+
<i className="fe-cancel"></i>
|
|
24
|
+
</Button>
|
|
25
|
+
</div>
|
|
26
|
+
)}
|
|
27
|
+
<header className={clsx("flex items-center px-2 pt-2", className)} {...props}>
|
|
28
|
+
<h4>{children}</h4>
|
|
29
|
+
</header>
|
|
30
|
+
</>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface ModalDescriptionProps extends ComponentProps<"div"> {
|
|
35
|
+
height?: number;
|
|
36
|
+
}
|
|
37
|
+
export function ModalDescription({ children, className, height, ...props }: ModalDescriptionProps) {
|
|
38
|
+
if (!height) {
|
|
39
|
+
return (
|
|
40
|
+
<div className={clsx("p-2", className)} {...props}>
|
|
41
|
+
{children}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return (
|
|
46
|
+
<div className={clsx("py-2", className)} {...props}>
|
|
47
|
+
<Scroll style={{ height: `${height}px` }}>{children}</Scroll>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function ModalFooter({ children, className, ...props }: ComponentProps<"footer">) {
|
|
53
|
+
return (
|
|
54
|
+
<footer className={clsx("relative z-10 px-2 pb-2", className)} {...props}>
|
|
55
|
+
{children}
|
|
56
|
+
</footer>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Button } from "../button";
|
|
4
|
+
|
|
5
|
+
import { Modal, ModalTrigger, ModalContent, ModalDescription, ModalHeader, ModalFooter } from ".";
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
title: "Components/Modal",
|
|
9
|
+
component: Modal,
|
|
10
|
+
} satisfies Meta<typeof Modal>;
|
|
11
|
+
export default meta;
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof meta>;
|
|
14
|
+
|
|
15
|
+
export const Basic: Story = {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
17
|
+
render: ({ children, ...args }) => {
|
|
18
|
+
return (
|
|
19
|
+
<Modal {...args}>
|
|
20
|
+
<ModalTrigger>open Modal</ModalTrigger>
|
|
21
|
+
<ModalContent>
|
|
22
|
+
<ModalHeader>Header</ModalHeader>
|
|
23
|
+
<ModalDescription>Content</ModalDescription>
|
|
24
|
+
</ModalContent>
|
|
25
|
+
</Modal>
|
|
26
|
+
);
|
|
27
|
+
},
|
|
28
|
+
args: {
|
|
29
|
+
initialOpen: false,
|
|
30
|
+
color: "default",
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const Context = () => {
|
|
35
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
36
|
+
return (
|
|
37
|
+
<Modal
|
|
38
|
+
open={isOpen}
|
|
39
|
+
onOpen={(e) => {
|
|
40
|
+
console.log("setIsOpen", e);
|
|
41
|
+
setIsOpen(e);
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
<ModalTrigger>open Modal</ModalTrigger>
|
|
45
|
+
<ModalContent>
|
|
46
|
+
<ModalHeader>Header</ModalHeader>
|
|
47
|
+
<ModalDescription>Content</ModalDescription>
|
|
48
|
+
<ModalFooter>
|
|
49
|
+
<div className="flex justify-between">
|
|
50
|
+
<Button variant="text" color="gray" onClick={() => setIsOpen(false)}>
|
|
51
|
+
Cancel
|
|
52
|
+
</Button>
|
|
53
|
+
<Button color="yellow" onClick={() => setIsOpen(false)}>
|
|
54
|
+
Accept
|
|
55
|
+
</Button>
|
|
56
|
+
</div>
|
|
57
|
+
</ModalFooter>
|
|
58
|
+
</ModalContent>
|
|
59
|
+
</Modal>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const ScrollableModal = () => {
|
|
64
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
65
|
+
return (
|
|
66
|
+
<Modal
|
|
67
|
+
open={isOpen}
|
|
68
|
+
onOpen={(e) => {
|
|
69
|
+
console.log("setIsOpen", e);
|
|
70
|
+
setIsOpen(e);
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
<ModalTrigger>open scrollable modal</ModalTrigger>
|
|
74
|
+
<ModalContent>
|
|
75
|
+
<ModalHeader>Header</ModalHeader>
|
|
76
|
+
<ModalDescription height={160}>
|
|
77
|
+
<div className="px-4 text-justify">
|
|
78
|
+
<p>
|
|
79
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium unde, blanditiis
|
|
80
|
+
rem accusamus obcaecati enim amet, voluptatibus nemo facilis illum aut itaque in?
|
|
81
|
+
Deleniti iure amet qui vero, blanditiis quos?
|
|
82
|
+
</p>
|
|
83
|
+
<p>
|
|
84
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium unde, blanditiis
|
|
85
|
+
rem accusamus obcaecati enim amet, voluptatibus nemo facilis illum aut itaque in?
|
|
86
|
+
Deleniti iure amet qui vero, blanditiis quos?
|
|
87
|
+
</p>
|
|
88
|
+
<p>
|
|
89
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium unde, blanditiis
|
|
90
|
+
rem accusamus obcaecati enim amet, voluptatibus nemo facilis illum aut itaque in?
|
|
91
|
+
Deleniti iure amet qui vero, blanditiis quos?
|
|
92
|
+
</p>
|
|
93
|
+
<p>
|
|
94
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium unde, blanditiis
|
|
95
|
+
rem accusamus obcaecati enim amet, voluptatibus nemo facilis illum aut itaque in?
|
|
96
|
+
Deleniti iure amet qui vero, blanditiis quos?
|
|
97
|
+
</p>
|
|
98
|
+
</div>
|
|
99
|
+
</ModalDescription>
|
|
100
|
+
<ModalFooter>
|
|
101
|
+
<div className="flex justify-between">
|
|
102
|
+
<Button variant="text" color="gray" onClick={() => setIsOpen(false)}>
|
|
103
|
+
Cancel
|
|
104
|
+
</Button>
|
|
105
|
+
<Button color="yellow" onClick={() => setIsOpen(false)}>
|
|
106
|
+
Accept
|
|
107
|
+
</Button>
|
|
108
|
+
</div>
|
|
109
|
+
</ModalFooter>
|
|
110
|
+
</ModalContent>
|
|
111
|
+
</Modal>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { ModalOptions } from "./interface";
|
|
3
|
+
import useModal from "./useModal";
|
|
4
|
+
import { ModalContext } from "./useModalContext";
|
|
5
|
+
|
|
6
|
+
interface Props extends ModalOptions {
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function Modal({ children, ...options }: Props) {
|
|
11
|
+
const modal = useModal(options);
|
|
12
|
+
return <ModalContext.Provider value={modal}>{children}</ModalContext.Provider>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FloatingFocusManager,
|
|
3
|
+
FloatingOverlay,
|
|
4
|
+
FloatingPortal,
|
|
5
|
+
useMergeRefs,
|
|
6
|
+
} from "@floating-ui/react";
|
|
7
|
+
import { ComponentProps, forwardRef } from "react";
|
|
8
|
+
import { useModalContext } from ".";
|
|
9
|
+
import clsx from "clsx";
|
|
10
|
+
import { Dialog } from "../dialog";
|
|
11
|
+
|
|
12
|
+
const ModalContent = forwardRef<HTMLDivElement, ComponentProps<"div">>(
|
|
13
|
+
({ style, children, ...props }, propRef) => {
|
|
14
|
+
const context = useModalContext();
|
|
15
|
+
const floatingContext = context.context;
|
|
16
|
+
|
|
17
|
+
const ref = useMergeRefs([context.refs.setFloating, propRef]);
|
|
18
|
+
if (!context.open) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<FloatingPortal>
|
|
24
|
+
<FloatingOverlay
|
|
25
|
+
className={clsx(["flex-center bg-gray-7/40 z-overlay", "animate-fade-in-opacity"])}
|
|
26
|
+
lockScroll
|
|
27
|
+
>
|
|
28
|
+
<FloatingFocusManager context={floatingContext}>
|
|
29
|
+
<Dialog
|
|
30
|
+
className={clsx("w-96 max-w-full", `border-${context.color}-2`, "animate-fade-in")}
|
|
31
|
+
ref={ref}
|
|
32
|
+
aria-labelledby={context.labelId}
|
|
33
|
+
aria-describedby={context.descriptionId}
|
|
34
|
+
style={style}
|
|
35
|
+
{...context.getFloatingProps(props)}
|
|
36
|
+
>
|
|
37
|
+
{children}
|
|
38
|
+
</Dialog>
|
|
39
|
+
</FloatingFocusManager>
|
|
40
|
+
</FloatingOverlay>
|
|
41
|
+
</FloatingPortal>
|
|
42
|
+
);
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
export default ModalContent;
|