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,66 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Button } from "../button";
|
|
3
|
+
import Tabs from "./Tabs";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: "Components/Tabs",
|
|
7
|
+
component: Tabs,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const Basic = () => {
|
|
11
|
+
const tabs = [
|
|
12
|
+
{
|
|
13
|
+
id: "bulbasaur",
|
|
14
|
+
title: "Bulbasaur",
|
|
15
|
+
content: (
|
|
16
|
+
<>
|
|
17
|
+
<p>
|
|
18
|
+
There is a plant seed on its back right from the day this Pokémon is born. The seed
|
|
19
|
+
slowly grows larger. There is a plant seed on its back right from the day this Pokémon
|
|
20
|
+
is born. The seed slowly grows larger. There is a plant seed on its back right from the
|
|
21
|
+
day this Pokémon is born. The seed slowly grows larger.
|
|
22
|
+
</p>
|
|
23
|
+
</>
|
|
24
|
+
),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "charmander",
|
|
28
|
+
title: "Charmander",
|
|
29
|
+
content: (
|
|
30
|
+
<>
|
|
31
|
+
<p>
|
|
32
|
+
It has a preference for hot things. When it rains, steam is said to spout from the tip
|
|
33
|
+
of its tail. It has a preference for hot things. When it rains, steam is said to spout
|
|
34
|
+
from the tip of its tail. It has a preference for hot things. When it rains, steam is
|
|
35
|
+
said to spout from the tip of its tail.
|
|
36
|
+
</p>
|
|
37
|
+
</>
|
|
38
|
+
),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "squirtle",
|
|
42
|
+
title: "Squirtle",
|
|
43
|
+
content: (
|
|
44
|
+
<>
|
|
45
|
+
<p>
|
|
46
|
+
When it retracts its long neck into its shell, it squirts out water with vigorous force.
|
|
47
|
+
When it retracts its long neck into its shell, it squirts out water with vigorous force.
|
|
48
|
+
When it retracts its long neck into its shell, it squirts out water with vigorous force.
|
|
49
|
+
</p>
|
|
50
|
+
</>
|
|
51
|
+
),
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
const [id, setId] = useState<string | number>("bulbasaur");
|
|
55
|
+
return (
|
|
56
|
+
<div className="flex flex-col gap-2">
|
|
57
|
+
<Tabs tabs={tabs} stickyTabs={true} value={id} onChange={setId}>
|
|
58
|
+
<Button icon variant="text" color="gray">
|
|
59
|
+
<i className="fe-sidebar-collapse"></i>
|
|
60
|
+
</Button>
|
|
61
|
+
</Tabs>
|
|
62
|
+
<Tabs tabs={tabs} value={id} onChange={setId} />
|
|
63
|
+
<Tabs tabs={tabs} fullWidth={true} value={id} onChange={setId} />
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
/* TODO: fix stickyTabs with overflow: hidden */
|
|
5
|
+
|
|
6
|
+
export interface Tab {
|
|
7
|
+
id: string | number;
|
|
8
|
+
title: ReactNode;
|
|
9
|
+
content?: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
tabs?: Tab[];
|
|
14
|
+
value: number | string;
|
|
15
|
+
/**
|
|
16
|
+
* Will make tabs stick to the top of the container when overflowing
|
|
17
|
+
*/
|
|
18
|
+
stickyTabs?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Tabs will take the maximum width and divide equally
|
|
21
|
+
*/
|
|
22
|
+
fullWidth?: boolean;
|
|
23
|
+
onChange: (id: number | string) => void;
|
|
24
|
+
|
|
25
|
+
children?: ReactNode;
|
|
26
|
+
|
|
27
|
+
className?: string;
|
|
28
|
+
contentClassName?: string;
|
|
29
|
+
listClassName?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default function Tabs({
|
|
33
|
+
className,
|
|
34
|
+
listClassName,
|
|
35
|
+
contentClassName,
|
|
36
|
+
tabs = [],
|
|
37
|
+
value,
|
|
38
|
+
onChange,
|
|
39
|
+
fullWidth = false,
|
|
40
|
+
stickyTabs = false,
|
|
41
|
+
children,
|
|
42
|
+
}: Props) {
|
|
43
|
+
const content = tabs.find((t) => t.id === value)?.content;
|
|
44
|
+
return (
|
|
45
|
+
<div className={clsx("ll-tabs md:rounded-sm shadow bg-gray-2 overflow-hidden", className)}>
|
|
46
|
+
<div
|
|
47
|
+
role="tablist"
|
|
48
|
+
className={clsx("tabs-list flex", stickyTabs && "sticky z-[1] top-0", listClassName)}
|
|
49
|
+
>
|
|
50
|
+
{tabs.map(({ title, id }) => {
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
key={id}
|
|
54
|
+
className={clsx(
|
|
55
|
+
"tabs-list-item border-t-4 hover:text-gray-7 transition-all",
|
|
56
|
+
fullWidth && "text-center flex-1",
|
|
57
|
+
value === id
|
|
58
|
+
? "border-t-yellow-3 bg-gray-0 text-gray-7"
|
|
59
|
+
: "border-t-transparent text-gray-6",
|
|
60
|
+
)}
|
|
61
|
+
>
|
|
62
|
+
<button
|
|
63
|
+
className="px-2 py-1 focus-visible:outline-offset-0 w-full"
|
|
64
|
+
onClick={(event) => {
|
|
65
|
+
event.stopPropagation();
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
onChange(id);
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
{title}
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
})}
|
|
75
|
+
{children && <div className="extra ml-auto mr-2 flex items-center">{children}</div>}
|
|
76
|
+
</div>
|
|
77
|
+
<div className={clsx("p-2 bg-gray-0 shadow", contentClassName)}>{content}</div>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ReactNode, isValidElement } from "react";
|
|
2
|
+
import { TooltipOptions } from "./interface";
|
|
3
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from ".";
|
|
4
|
+
|
|
5
|
+
interface Props extends TooltipOptions {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
content: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function SimpleTooltip({ content, children, contentClassName, ...options }: Props) {
|
|
11
|
+
return (
|
|
12
|
+
<Tooltip {...options}>
|
|
13
|
+
<TooltipContent className={contentClassName}>{content}</TooltipContent>
|
|
14
|
+
<TooltipTrigger asChild={isValidElement(children)}>{children}</TooltipTrigger>
|
|
15
|
+
</Tooltip>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { HTMLProps, forwardRef } from "react";
|
|
2
|
+
import { useDrag } from "@use-gesture/react";
|
|
3
|
+
import { useSpring, animated } from "@react-spring/web";
|
|
4
|
+
import Tooltip from "./Tooltip";
|
|
5
|
+
import SimpleTooltip from "./SimpleTooltip";
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
title: "Components/Tooltip",
|
|
9
|
+
component: Tooltip,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const Box = forwardRef<HTMLDivElement, HTMLProps<HTMLDivElement>>((props, ref) => (
|
|
13
|
+
<div
|
|
14
|
+
ref={ref}
|
|
15
|
+
className="shadow-lg w-[100px] h-[100px] bg-gray-2 flex-center"
|
|
16
|
+
style={{
|
|
17
|
+
margin: "100px",
|
|
18
|
+
}}
|
|
19
|
+
{...props}
|
|
20
|
+
></div>
|
|
21
|
+
));
|
|
22
|
+
|
|
23
|
+
export const Basic = () => {
|
|
24
|
+
return (
|
|
25
|
+
<div>
|
|
26
|
+
<p>
|
|
27
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Distinctio neque dolorum soluta
|
|
28
|
+
suscipit,
|
|
29
|
+
<SimpleTooltip content="infos" placement="bottom">
|
|
30
|
+
dessous
|
|
31
|
+
</SimpleTooltip>
|
|
32
|
+
aut unde provident? Optio ipsum provident unde! Nulla, dignissimos recusandae. Eveniet
|
|
33
|
+
ut quam voluptatum accusantium aspernatur?
|
|
34
|
+
</p>
|
|
35
|
+
<p>
|
|
36
|
+
Lorem ipsum dolor sit amet consectetur sit amet consectetur sit amet consectetur sit amet
|
|
37
|
+
consectetur sit amet consectetursit amet consectetur adipisicing elit. Distinctio neque
|
|
38
|
+
dolorum soluta suscipit,
|
|
39
|
+
<SimpleTooltip content="infos" placement="top">
|
|
40
|
+
top
|
|
41
|
+
</SimpleTooltip>
|
|
42
|
+
aut unde provident? Optio ipsum provident unde! Nulla, dignissimos recusandae.
|
|
43
|
+
Eveniet ut quam voluptatum accusantium aspernatur?
|
|
44
|
+
</p>
|
|
45
|
+
<p>
|
|
46
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Distinctio neque dolorum soluta
|
|
47
|
+
suscipit, sit amet consectetur
|
|
48
|
+
<SimpleTooltip content="infos" placement="left">
|
|
49
|
+
gauche
|
|
50
|
+
</SimpleTooltip>
|
|
51
|
+
aut unde provident? Optio ipsum provident unde! Nulla, dignissimos recusandae.
|
|
52
|
+
Eveniet ut quam voluptatum accusantium aspernatur?
|
|
53
|
+
</p>
|
|
54
|
+
<p>
|
|
55
|
+
Lorem ipsum dolor sit amet consectetur sit amet consectetur sit amet consectetur adipisicing
|
|
56
|
+
elit. Distinctio neque dolorum soluta suscipit,
|
|
57
|
+
<SimpleTooltip content="infos" placement="right">
|
|
58
|
+
droite
|
|
59
|
+
</SimpleTooltip>
|
|
60
|
+
aut unde provident? Optio ipsum provident unde! Nulla, dignissimos recusandae.
|
|
61
|
+
Eveniet ut quam voluptatum accusantium aspernatur?
|
|
62
|
+
</p>
|
|
63
|
+
|
|
64
|
+
<div className="grid grid-cols-repeat-fill-300 gap-8">
|
|
65
|
+
<SimpleTooltip color="yellow" open={true} placement="right-start" content="infos">
|
|
66
|
+
<Box />
|
|
67
|
+
</SimpleTooltip>
|
|
68
|
+
<SimpleTooltip open={true} placement="right" content="infos">
|
|
69
|
+
<Box />
|
|
70
|
+
</SimpleTooltip>
|
|
71
|
+
<SimpleTooltip open={true} placement="right-end" content="infos">
|
|
72
|
+
<Box />
|
|
73
|
+
</SimpleTooltip>
|
|
74
|
+
<SimpleTooltip color="yellow" open={true} placement="left-start" content="infos">
|
|
75
|
+
<Box />
|
|
76
|
+
</SimpleTooltip>
|
|
77
|
+
<SimpleTooltip color="gray" open={true} placement="left" content="infos">
|
|
78
|
+
<Box />
|
|
79
|
+
</SimpleTooltip>
|
|
80
|
+
<SimpleTooltip color="orange" open={true} placement="left-end" content="infos">
|
|
81
|
+
<Box />
|
|
82
|
+
</SimpleTooltip>
|
|
83
|
+
<SimpleTooltip color="red" open={true} placement="top-start" content="infos">
|
|
84
|
+
<Box />
|
|
85
|
+
</SimpleTooltip>
|
|
86
|
+
<SimpleTooltip color="blue" open={true} placement="top" content="infos">
|
|
87
|
+
<Box />
|
|
88
|
+
</SimpleTooltip>
|
|
89
|
+
<SimpleTooltip color="green" open={true} placement="top-end" content="infos">
|
|
90
|
+
<Box />
|
|
91
|
+
</SimpleTooltip>
|
|
92
|
+
<SimpleTooltip color="red" open={true} placement="bottom-start" content="infos">
|
|
93
|
+
<Box />
|
|
94
|
+
</SimpleTooltip>
|
|
95
|
+
<SimpleTooltip open={true} placement="bottom" content="infos">
|
|
96
|
+
<Box />
|
|
97
|
+
</SimpleTooltip>
|
|
98
|
+
<SimpleTooltip open={true} placement="bottom-end" content="infos">
|
|
99
|
+
<Box />
|
|
100
|
+
</SimpleTooltip>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const Draggable = () => {
|
|
107
|
+
const [{ x, y }, api] = useSpring(() => ({ x: 0, y: 0 }));
|
|
108
|
+
const bind = useDrag(({ event, offset: [x, y] }) => {
|
|
109
|
+
event.preventDefault();
|
|
110
|
+
api.start({ x, y, immediate: true });
|
|
111
|
+
});
|
|
112
|
+
return (
|
|
113
|
+
<div className="container">
|
|
114
|
+
<SimpleTooltip content="infos" placement="top" open={true} color="yellow">
|
|
115
|
+
<animated.div
|
|
116
|
+
{...bind()}
|
|
117
|
+
className="shadow-lg w-[100px] h-[100px] bg-gray-2 flex-center"
|
|
118
|
+
style={{
|
|
119
|
+
x,
|
|
120
|
+
y,
|
|
121
|
+
cursor: "pointer",
|
|
122
|
+
}}
|
|
123
|
+
>
|
|
124
|
+
<span>Box</span>
|
|
125
|
+
</animated.div>
|
|
126
|
+
</SimpleTooltip>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { cleanup, fireEvent, render } from "@testing-library/react";
|
|
2
|
+
import { afterEach, describe, it } from "vitest";
|
|
3
|
+
import { SimpleTooltip } from ".";
|
|
4
|
+
|
|
5
|
+
afterEach(cleanup);
|
|
6
|
+
|
|
7
|
+
describe("<Tooltip />", () => {
|
|
8
|
+
it("show the popup", async () => {
|
|
9
|
+
const { getByRole, findByText } = render(
|
|
10
|
+
<SimpleTooltip openDelay={1} content="details">
|
|
11
|
+
tooltip
|
|
12
|
+
</SimpleTooltip>,
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
fireEvent.mouseEnter(getByRole("button"));
|
|
16
|
+
findByText("details");
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { TooltipOptions } from "./interface";
|
|
3
|
+
import useTooltip from "./useTooltip";
|
|
4
|
+
import { TooltipContext } from "./useTooltipContext";
|
|
5
|
+
|
|
6
|
+
interface Props extends TooltipOptions {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function Tooltip({ children, ...options }: Props) {
|
|
11
|
+
const tooltip = useTooltip(options);
|
|
12
|
+
return <TooltipContext.Provider value={tooltip}>{children}</TooltipContext.Provider>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ComponentProps, forwardRef } from "react";
|
|
2
|
+
import useTooltipContext from "./useTooltipContext";
|
|
3
|
+
import { FloatingPortal, useMergeRefs } from "@floating-ui/react";
|
|
4
|
+
import clsx from "clsx";
|
|
5
|
+
import { computeArrowStyle } from "../dialog/util";
|
|
6
|
+
import { Dialog } from "../dialog";
|
|
7
|
+
|
|
8
|
+
const TooltipContent = forwardRef<HTMLDivElement, ComponentProps<"div">>(
|
|
9
|
+
({ style, children, className, ...props }, propRef) => {
|
|
10
|
+
const context = useTooltipContext();
|
|
11
|
+
const ref = useMergeRefs([context.refs.setFloating, propRef]);
|
|
12
|
+
if (!context.open) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return (
|
|
16
|
+
<FloatingPortal>
|
|
17
|
+
<div
|
|
18
|
+
ref={ref}
|
|
19
|
+
className={clsx("z-tooltip", context.middlewareData.hide?.referenceHidden && "invisible")}
|
|
20
|
+
style={{ ...context.floatingStyles, ...style }}
|
|
21
|
+
{...context.getFloatingProps(props)}
|
|
22
|
+
>
|
|
23
|
+
<Dialog
|
|
24
|
+
placement={context.placement}
|
|
25
|
+
className={clsx("animate-fade-in px-2 py-1 max-w-80 text-sm", className)}
|
|
26
|
+
color={context.color}
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
<div
|
|
30
|
+
ref={context.arrowRef}
|
|
31
|
+
style={computeArrowStyle(context)}
|
|
32
|
+
className="p8n-arrow"
|
|
33
|
+
></div>
|
|
34
|
+
</Dialog>
|
|
35
|
+
</div>
|
|
36
|
+
</FloatingPortal>
|
|
37
|
+
);
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
export default TooltipContent;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { HTMLProps, cloneElement, forwardRef, isValidElement } from "react";
|
|
2
|
+
import useTooltipContext from "./useTooltipContext";
|
|
3
|
+
import { useMergeRefs } from "@floating-ui/react";
|
|
4
|
+
|
|
5
|
+
interface Props extends HTMLProps<HTMLElement> {
|
|
6
|
+
/* allows the user to pass any element as the anchor instead of <button> */
|
|
7
|
+
asChild?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const TooltipTrigger = forwardRef<HTMLElement, Props>(
|
|
11
|
+
({ children, asChild = false, ...props }, propRef) => {
|
|
12
|
+
const context = useTooltipContext();
|
|
13
|
+
const childrenRef = (children as any).ref;
|
|
14
|
+
const ref = useMergeRefs([context.refs.setReference, propRef, childrenRef]);
|
|
15
|
+
|
|
16
|
+
if (asChild && isValidElement(children)) {
|
|
17
|
+
return cloneElement(
|
|
18
|
+
children,
|
|
19
|
+
context.getReferenceProps({
|
|
20
|
+
ref,
|
|
21
|
+
...props,
|
|
22
|
+
...children.props,
|
|
23
|
+
"data-state": context.open ? "open" : "closed",
|
|
24
|
+
}),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<abbr ref={ref} {...context.getReferenceProps(props)}>
|
|
30
|
+
{children}
|
|
31
|
+
</abbr>
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export default TooltipTrigger;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as Tooltip } from "./Tooltip";
|
|
2
|
+
export { default as TooltipContent } from "./TooltipContent";
|
|
3
|
+
export { default as TooltipTrigger } from "./TooltipTrigger";
|
|
4
|
+
export { default as useTooltip } from "./useTooltip";
|
|
5
|
+
export { default as useTooltipContext, TooltipContext } from "./useTooltipContext";
|
|
6
|
+
export { default as SimpleTooltip } from "./SimpleTooltip";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Placement } from "@floating-ui/react";
|
|
2
|
+
import { ThemeColor } from "~/types.d";
|
|
3
|
+
|
|
4
|
+
export interface TooltipOptions {
|
|
5
|
+
initialOpen?: boolean;
|
|
6
|
+
placement?: Placement;
|
|
7
|
+
open?: boolean;
|
|
8
|
+
onOpen?: (open: boolean) => void;
|
|
9
|
+
openDelay?: number;
|
|
10
|
+
closeDelay?: number;
|
|
11
|
+
color?: ThemeColor;
|
|
12
|
+
contentClassName?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { ThemeColor };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useMemo, useRef, useState } from "react";
|
|
2
|
+
import { TooltipOptions } from "./interface";
|
|
3
|
+
import {
|
|
4
|
+
arrow,
|
|
5
|
+
autoUpdate,
|
|
6
|
+
flip,
|
|
7
|
+
hide,
|
|
8
|
+
offset,
|
|
9
|
+
shift,
|
|
10
|
+
useDismiss,
|
|
11
|
+
useFloating,
|
|
12
|
+
useFocus,
|
|
13
|
+
useHover,
|
|
14
|
+
useInteractions,
|
|
15
|
+
useRole,
|
|
16
|
+
} from "@floating-ui/react";
|
|
17
|
+
|
|
18
|
+
const arrowWidth = 16;
|
|
19
|
+
|
|
20
|
+
export default function useTooltip({
|
|
21
|
+
initialOpen = false,
|
|
22
|
+
placement = "top",
|
|
23
|
+
open: controlledOpen,
|
|
24
|
+
onOpen: setControlledOpen,
|
|
25
|
+
openDelay = 500,
|
|
26
|
+
closeDelay = 500,
|
|
27
|
+
color = "yellow",
|
|
28
|
+
}: TooltipOptions = {}) {
|
|
29
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState(initialOpen);
|
|
30
|
+
|
|
31
|
+
const isUncontrolled = controlledOpen === null || controlledOpen === undefined;
|
|
32
|
+
|
|
33
|
+
const open = isUncontrolled ? uncontrolledOpen : controlledOpen;
|
|
34
|
+
const setOpen = isUncontrolled ? setUncontrolledOpen : setControlledOpen;
|
|
35
|
+
|
|
36
|
+
const arrowRef = useRef<HTMLDivElement>(null);
|
|
37
|
+
|
|
38
|
+
const offsetVal = arrowWidth / 2;
|
|
39
|
+
|
|
40
|
+
const data = useFloating({
|
|
41
|
+
placement,
|
|
42
|
+
open,
|
|
43
|
+
onOpenChange: setOpen,
|
|
44
|
+
whileElementsMounted: autoUpdate,
|
|
45
|
+
middleware: [
|
|
46
|
+
offset(offsetVal),
|
|
47
|
+
flip(),
|
|
48
|
+
shift({ padding: 5 }),
|
|
49
|
+
arrow({
|
|
50
|
+
element: arrowRef,
|
|
51
|
+
padding: 12,
|
|
52
|
+
}),
|
|
53
|
+
hide({
|
|
54
|
+
padding: 20,
|
|
55
|
+
}),
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
const { context } = data;
|
|
59
|
+
|
|
60
|
+
const hover = useHover(context, {
|
|
61
|
+
move: false,
|
|
62
|
+
enabled: isUncontrolled,
|
|
63
|
+
delay: {
|
|
64
|
+
open: openDelay || 1,
|
|
65
|
+
close: closeDelay,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
const focus = useFocus(context, {
|
|
69
|
+
enabled: isUncontrolled,
|
|
70
|
+
});
|
|
71
|
+
const dismiss = useDismiss(context);
|
|
72
|
+
const role = useRole(context, { role: "tooltip" });
|
|
73
|
+
|
|
74
|
+
const interactions = useInteractions([hover, focus, dismiss, role]);
|
|
75
|
+
|
|
76
|
+
return useMemo(
|
|
77
|
+
() => ({
|
|
78
|
+
open,
|
|
79
|
+
setOpen,
|
|
80
|
+
...interactions,
|
|
81
|
+
...data,
|
|
82
|
+
arrowRef,
|
|
83
|
+
color,
|
|
84
|
+
}),
|
|
85
|
+
[open, setOpen, color, interactions, data],
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
import useTooltip from "./useTooltip";
|
|
3
|
+
|
|
4
|
+
type ContextType = ReturnType<typeof useTooltip> | null;
|
|
5
|
+
|
|
6
|
+
export const TooltipContext = createContext<ContextType>(null);
|
|
7
|
+
|
|
8
|
+
export default function useTooltipContext() {
|
|
9
|
+
const context = useContext(TooltipContext);
|
|
10
|
+
|
|
11
|
+
if (context === null) {
|
|
12
|
+
throw new Error("Tooltip components must be wrapped in <Tooltip />");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return context;
|
|
16
|
+
}
|
package/src/env.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import "react";
|
|
2
|
+
|
|
3
|
+
type Merge<T, U> = Omit<T, keyof U> & U;
|
|
4
|
+
type PropsWithAs<P, T extends React.ElementType> = P & { as?: T };
|
|
5
|
+
export type PolymorphicPropsWithRef<P, T extends React.ElementType> = Merge<
|
|
6
|
+
T extends keyof JSX.IntrinsicElements
|
|
7
|
+
? React.PropsWithRef<JSX.IntrinsicElements[T]>
|
|
8
|
+
: React.ComponentPropsWithRef<T>,
|
|
9
|
+
PropsWithAs<P, T>
|
|
10
|
+
>;
|
|
11
|
+
|
|
12
|
+
declare global {
|
|
13
|
+
interface GlobalEventHandlersEventMap {
|
|
14
|
+
"maplibre-contextmenu": CustomEvent;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
declare module "react" {
|
|
19
|
+
interface CSSProperties {
|
|
20
|
+
[key: `--${string}`]: string | number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
24
|
+
function forwardRef<T, P = {}>(
|
|
25
|
+
render: (props: P, ref: React.Ref<T>) => React.ReactElement | null,
|
|
26
|
+
): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { useNotifications } from "./useNotifications";
|
|
2
|
+
export { useRipple } from "./useRipple";
|
|
3
|
+
export { useIsMounted } from "./useIsMounted";
|
|
4
|
+
export { useFetch } from "./useFetch";
|
|
5
|
+
export { useCombinedRefs } from "./useCombinedRefs";
|
|
6
|
+
export { useIsClosing } from "./useIsClosing";
|
|
7
|
+
export { usePrevious } from "./usePrevious";
|
|
8
|
+
export { useEventCallback } from "./useEventCallback";
|
|
9
|
+
export { useDebounce, useStateDebounce, useRefDebounce } from "./useDebounce";
|
|
10
|
+
export { useCopyToClipboard } from "./useCopyToClipboard";
|
|
11
|
+
export { useEventListener } from "./useEventListener";
|
|
12
|
+
export { useOnClickOutside } from "./useOnClickOutside";
|
|
13
|
+
export { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createElement, useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
import { render } from "@testing-library/react";
|
|
4
|
+
|
|
5
|
+
import { useCombinedRefs } from "./useCombinedRefs";
|
|
6
|
+
import { describe, expect, it, vi } from "vitest";
|
|
7
|
+
|
|
8
|
+
describe("useCombinedRefs", () => {
|
|
9
|
+
it("should correctly combine refs into one ref", async () => {
|
|
10
|
+
const cbRef = vi.fn();
|
|
11
|
+
const refs: { a: null | HTMLDivElement; b: null | HTMLDivElement } = { a: null, b: null };
|
|
12
|
+
|
|
13
|
+
const Test = () => {
|
|
14
|
+
const refA = useRef<HTMLDivElement>(null);
|
|
15
|
+
const refB = useRef<HTMLDivElement>(null);
|
|
16
|
+
const combinedRef = useCombinedRefs(refA, refB, cbRef, undefined);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
refs.a = refA.current;
|
|
19
|
+
refs.b = refB.current;
|
|
20
|
+
return () => {
|
|
21
|
+
refs.a = refA.current;
|
|
22
|
+
refs.b = refB.current;
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
return <div ref={combinedRef} data-testid="div"></div>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const { getByTestId, rerender } = render(createElement(Test));
|
|
29
|
+
|
|
30
|
+
const div = getByTestId("div");
|
|
31
|
+
|
|
32
|
+
expect(refs.a).toBe(div);
|
|
33
|
+
expect(refs.b).toBe(div);
|
|
34
|
+
expect(cbRef).toHaveBeenCalledWith(div);
|
|
35
|
+
|
|
36
|
+
rerender(<div />);
|
|
37
|
+
|
|
38
|
+
expect(cbRef).toHaveBeenCalledWith(null);
|
|
39
|
+
expect(refs.a).toBe(null);
|
|
40
|
+
expect(refs.b).toBe(null);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Ref, useCallback } from "react";
|
|
2
|
+
|
|
3
|
+
export const useCombinedRefs = <T extends any>(...refs: (Ref<T> | undefined)[]): Ref<T> =>
|
|
4
|
+
useCallback(
|
|
5
|
+
(element: T) =>
|
|
6
|
+
refs.forEach((ref) => {
|
|
7
|
+
if (!ref) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Ref can have two types - a function or an object. We treat each case.
|
|
12
|
+
if (typeof ref === "function") {
|
|
13
|
+
return ref(element);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// As per https://github.com/facebook/react/issues/13029
|
|
17
|
+
// it should be fine to set current this way.
|
|
18
|
+
(ref as any).current = element;
|
|
19
|
+
}),
|
|
20
|
+
refs,
|
|
21
|
+
);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
|
|
3
|
+
type CopiedValue = string | null;
|
|
4
|
+
|
|
5
|
+
type CopyFn = (text: string) => Promise<boolean>;
|
|
6
|
+
|
|
7
|
+
export function useCopyToClipboard(): [CopiedValue, CopyFn] {
|
|
8
|
+
const [copiedText, setCopiedText] = useState<CopiedValue>(null);
|
|
9
|
+
|
|
10
|
+
const copy: CopyFn = useCallback(async (text) => {
|
|
11
|
+
if (!navigator?.clipboard) {
|
|
12
|
+
console.warn("Clipboard not supported");
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
await navigator.clipboard.writeText(text);
|
|
18
|
+
setCopiedText(text);
|
|
19
|
+
return true;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.warn("Copy failed", error);
|
|
22
|
+
setCopiedText(null);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}, []);
|
|
26
|
+
|
|
27
|
+
return [copiedText, copy];
|
|
28
|
+
}
|