Skip to content

Commit fdcf421

Browse files
authored
feat: added popover component (#16)
1 parent 42ece04 commit fdcf421

7 files changed

Lines changed: 204 additions & 1 deletion

File tree

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@
4343
},
4444
"dependencies": {
4545
"@headlessui/react": "^1.7.16",
46+
"@headlessui/tailwindcss": "^0.2.0",
4647
"@heroicons/react": "^2.0.18",
48+
"@radix-ui/react-popover": "^1.0.7",
4749
"@radix-ui/react-tooltip": "^1.0.7",
4850
"classnames": "^2.3.2",
4951
"react": "^18.2.0",
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
3+
import Popover from './Popover';
4+
5+
const meta: Meta<typeof Popover> = {
6+
component: Popover,
7+
parameters: {
8+
layout: 'centered',
9+
},
10+
tags: ['autodocs'],
11+
};
12+
13+
export default meta;
14+
15+
type Story = StoryObj<typeof Popover>;
16+
17+
export const Default: Story = {
18+
args: {
19+
trigger: <button>Click Me</button>,
20+
children: 'Hello, world!',
21+
side: 'top',
22+
},
23+
};
24+
25+
export const WithCloseButton: Story = {
26+
args: {
27+
trigger: <button>Click Me</button>,
28+
children: 'Hello, world!',
29+
showCloseButton: true,
30+
},
31+
};
32+
33+
export const WithArrow: Story = {
34+
args: {
35+
trigger: <button>Click Me</button>,
36+
children: 'Hello, world!',
37+
showArrow: true,
38+
},
39+
};
40+
41+
export const WithCustomArrow: Story = {
42+
args: {
43+
trigger: <button>Click Me</button>,
44+
children: 'Hello, world!',
45+
showArrow: true,
46+
arrowClassName: 'fill-red-500',
47+
},
48+
};

src/Components/Popover/Popover.tsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React from 'react';
2+
import * as PopoverPrimitive from '@radix-ui/react-popover';
3+
import { twMerge } from 'tailwind-merge';
4+
import { XMarkIcon } from '@heroicons/react/24/outline';
5+
6+
type Props = {
7+
trigger: React.ReactNode;
8+
className?: string;
9+
children: React.ReactNode;
10+
sideOffset?: number;
11+
showCloseButton?: boolean;
12+
showArrow?: boolean;
13+
arrowClassName?: string;
14+
side: 'top' | 'right' | 'bottom' | 'left';
15+
};
16+
17+
const Popover = ({
18+
trigger,
19+
className,
20+
children,
21+
sideOffset = 10,
22+
showCloseButton = false,
23+
showArrow = false,
24+
arrowClassName,
25+
side,
26+
...props
27+
}: Props) => {
28+
return (
29+
<PopoverPrimitive.Root>
30+
<PopoverPrimitive.Trigger asChild>{trigger}</PopoverPrimitive.Trigger>
31+
<PopoverPrimitive.Portal>
32+
<PopoverPrimitive.Content
33+
className={twMerge(
34+
'bg-white px-4 py-2 overflow-hidden rounded-lg shadow-lg ring-1 ring-black/5 will-change-transform data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade',
35+
className
36+
)}
37+
sideOffset={sideOffset}
38+
side={side}
39+
{...props}
40+
>
41+
{children}
42+
43+
{showArrow && (
44+
<PopoverPrimitive.Arrow className={twMerge('fill-white opacity-100', arrowClassName)} />
45+
)}
46+
47+
{showCloseButton && (
48+
<PopoverPrimitive.Close className="absolute top-1.5 right-1.5">
49+
<XMarkIcon className="h-4 w-4 text-gray-400 hover:text-gray-600" />
50+
</PopoverPrimitive.Close>
51+
)}
52+
</PopoverPrimitive.Content>
53+
</PopoverPrimitive.Portal>
54+
</PopoverPrimitive.Root>
55+
);
56+
};
57+
58+
export default Popover;

src/Components/Popover/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './Popover';

src/Components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export * from './Checkbox';
55
export * from './Dropdown';
66
export * from './Modal';
77
export * from './Notice';
8+
export * from './Popover';
89
export * from './RadioGroup';
910
export * from './SelectCard';
1011
export * from './SwitchInput';

tailwind.config.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,36 @@ export default {
55
"./src/**/*.{js,ts,jsx,tsx}",
66
],
77
theme: {
8-
extend: {},
8+
extend: {
9+
keyframes: {
10+
slideUpAndFade: {
11+
from: { opacity: 0, transform: 'translateY(2px)' },
12+
to: { opacity: 1, transform: 'translateY(0)' },
13+
},
14+
slideRightAndFade: {
15+
from: { opacity: 0, transform: 'translateX(-2px)' },
16+
to: { opacity: 1, transform: 'translateX(0)' },
17+
},
18+
slideDownAndFade: {
19+
from: { opacity: 0, transform: 'translateY(-2px)' },
20+
to: { opacity: 1, transform: 'translateY(0)' },
21+
},
22+
slideLeftAndFade: {
23+
from: { opacity: 0, transform: 'translateX(2px)' },
24+
to: { opacity: 1, transform: 'translateX(0)' },
25+
},
26+
},
27+
animation: {
28+
slideUpAndFade: 'slideUpAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)',
29+
slideRightAndFade: 'slideRightAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)',
30+
slideDownAndFade: 'slideDownAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)',
31+
slideLeftAndFade: 'slideLeftAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)',
32+
},
33+
},
934
},
1035
plugins: [
1136
require('@tailwindcss/forms'),
37+
require('@headlessui/tailwindcss')({ prefix: 'ui' })
1238
],
1339
}
1440

yarn.lock

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,6 +1752,15 @@ __metadata:
17521752
languageName: node
17531753
linkType: hard
17541754

1755+
"@headlessui/tailwindcss@npm:^0.2.0":
1756+
version: 0.2.0
1757+
resolution: "@headlessui/tailwindcss@npm:0.2.0"
1758+
peerDependencies:
1759+
tailwindcss: ^3.0
1760+
checksum: 10c0/b641bef150e4ee18afd068c1af8e83c582a3131903ed1a310abbdb463b6150428a9b4c38e42cb296469fb20857f5542ae34f681cbff62f47d52332fbc5117479
1761+
languageName: node
1762+
linkType: hard
1763+
17551764
"@heroicons/react@npm:^2.0.18":
17561765
version: 2.1.1
17571766
resolution: "@heroicons/react@npm:2.1.1"
@@ -2268,6 +2277,28 @@ __metadata:
22682277
languageName: node
22692278
linkType: hard
22702279

2280+
"@radix-ui/react-focus-scope@npm:1.0.4":
2281+
version: 1.0.4
2282+
resolution: "@radix-ui/react-focus-scope@npm:1.0.4"
2283+
dependencies:
2284+
"@babel/runtime": "npm:^7.13.10"
2285+
"@radix-ui/react-compose-refs": "npm:1.0.1"
2286+
"@radix-ui/react-primitive": "npm:1.0.3"
2287+
"@radix-ui/react-use-callback-ref": "npm:1.0.1"
2288+
peerDependencies:
2289+
"@types/react": "*"
2290+
"@types/react-dom": "*"
2291+
react: ^16.8 || ^17.0 || ^18.0
2292+
react-dom: ^16.8 || ^17.0 || ^18.0
2293+
peerDependenciesMeta:
2294+
"@types/react":
2295+
optional: true
2296+
"@types/react-dom":
2297+
optional: true
2298+
checksum: 10c0/2fce0bafcab4e16cf4ed7560bda40654223f3d0add6b231e1c607433030c14e6249818b444b7b58ee7a6ff6bbf8e192c9c81d22c3a5c88c2daade9d1f881b5be
2299+
languageName: node
2300+
linkType: hard
2301+
22712302
"@radix-ui/react-id@npm:1.0.1":
22722303
version: 1.0.1
22732304
resolution: "@radix-ui/react-id@npm:1.0.1"
@@ -2284,6 +2315,40 @@ __metadata:
22842315
languageName: node
22852316
linkType: hard
22862317

2318+
"@radix-ui/react-popover@npm:^1.0.7":
2319+
version: 1.0.7
2320+
resolution: "@radix-ui/react-popover@npm:1.0.7"
2321+
dependencies:
2322+
"@babel/runtime": "npm:^7.13.10"
2323+
"@radix-ui/primitive": "npm:1.0.1"
2324+
"@radix-ui/react-compose-refs": "npm:1.0.1"
2325+
"@radix-ui/react-context": "npm:1.0.1"
2326+
"@radix-ui/react-dismissable-layer": "npm:1.0.5"
2327+
"@radix-ui/react-focus-guards": "npm:1.0.1"
2328+
"@radix-ui/react-focus-scope": "npm:1.0.4"
2329+
"@radix-ui/react-id": "npm:1.0.1"
2330+
"@radix-ui/react-popper": "npm:1.1.3"
2331+
"@radix-ui/react-portal": "npm:1.0.4"
2332+
"@radix-ui/react-presence": "npm:1.0.1"
2333+
"@radix-ui/react-primitive": "npm:1.0.3"
2334+
"@radix-ui/react-slot": "npm:1.0.2"
2335+
"@radix-ui/react-use-controllable-state": "npm:1.0.1"
2336+
aria-hidden: "npm:^1.1.1"
2337+
react-remove-scroll: "npm:2.5.5"
2338+
peerDependencies:
2339+
"@types/react": "*"
2340+
"@types/react-dom": "*"
2341+
react: ^16.8 || ^17.0 || ^18.0
2342+
react-dom: ^16.8 || ^17.0 || ^18.0
2343+
peerDependenciesMeta:
2344+
"@types/react":
2345+
optional: true
2346+
"@types/react-dom":
2347+
optional: true
2348+
checksum: 10c0/ed7abbd61df1e15d62072e214fafbdc4e31942e0ce49665f2045d8279944a0a37762bcd70a36389ed9e43c95797d5acb57f6f5ca5a15b688b1928cfc2b9ce196
2349+
languageName: node
2350+
linkType: hard
2351+
22872352
"@radix-ui/react-popper@npm:1.1.2":
22882353
version: 1.1.2
22892354
resolution: "@radix-ui/react-popper@npm:1.1.2"
@@ -4403,7 +4468,9 @@ __metadata:
44034468
resolution: "@wedevs/tail-react@workspace:."
44044469
dependencies:
44054470
"@headlessui/react": "npm:^1.7.16"
4471+
"@headlessui/tailwindcss": "npm:^0.2.0"
44064472
"@heroicons/react": "npm:^2.0.18"
4473+
"@radix-ui/react-popover": "npm:^1.0.7"
44074474
"@radix-ui/react-tooltip": "npm:^1.0.7"
44084475
"@storybook/addon-essentials": "npm:^7.2.1"
44094476
"@storybook/addon-interactions": "npm:^7.2.1"

0 commit comments

Comments
 (0)