From 2afed7fc0a059c3404341c900418c73fe4550b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=ED=83=9C=EA=B7=9C=20Park=20Tae=20Kyu?= Date: Mon, 20 Jan 2025 03:18:47 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20dialog=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 304 ++++++++++++++++++++++++++++++- src/ui/dialog/dialog.css.ts | 102 +++++++++++ src/ui/dialog/dialog.stories.tsx | 111 +++++++++++ src/ui/dialog/dialog.tsx | 72 ++++++++ src/ui/dialog/index.ts | 1 + 6 files changed, 585 insertions(+), 6 deletions(-) create mode 100644 src/ui/dialog/dialog.css.ts create mode 100644 src/ui/dialog/dialog.stories.tsx create mode 100644 src/ui/dialog/dialog.tsx create mode 100644 src/ui/dialog/index.ts diff --git a/package.json b/package.json index bb05441cc..b53eef935 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@hookform/resolvers": "^3.9.1", + "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-switch": "^1.1.2", "@t3-oss/env-core": "^0.11.1", "@tanstack/react-query": "^5.62.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c7fde06b7..25d3c3e06 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@hookform/resolvers': specifier: ^3.9.1 version: 3.9.1(react-hook-form@7.54.2(react@18.3.1)) + '@radix-ui/react-dialog': + specifier: ^1.1.4 + version: 1.1.4(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-switch': specifier: ^1.1.2 version: 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -137,10 +140,10 @@ importers: version: 3.7.2(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(yaml@2.6.1)) '@vitest/coverage-v8': specifier: ^2.1.8 - version: 2.1.8(vitest@2.1.8(@types/node@22.10.5)(@vitest/ui@2.1.8)(happy-dom@16.3.0)(msw@2.7.0(@types/node@22.10.5)(typescript@5.6.3))) + version: 2.1.8(vitest@2.1.8) '@vitest/eslint-plugin': specifier: ^1.1.24 - version: 1.1.24(@typescript-eslint/utils@8.19.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3))(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3)(vitest@2.1.8(@types/node@22.10.5)(@vitest/ui@2.1.8)(happy-dom@16.3.0)(msw@2.7.0(@types/node@22.10.5)(typescript@5.6.3))) + version: 1.1.24(@typescript-eslint/utils@8.19.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3))(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3)(vitest@2.1.8) '@vitest/ui': specifier: ^2.1.8 version: 2.1.8(vitest@2.1.8) @@ -986,6 +989,89 @@ packages: '@types/react': optional: true + '@radix-ui/react-dialog@1.1.4': + resolution: {integrity: sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.3': + resolution: {integrity: sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.1': + resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.1': + resolution: {integrity: sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.0': + resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-portal@1.1.3': + resolution: {integrity: sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.2': + resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-primitive@2.0.1': resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==} peerDependencies: @@ -1039,6 +1125,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-escape-keydown@1.1.0': + resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-layout-effect@1.1.0': resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} peerDependencies: @@ -1847,6 +1942,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-hidden@1.2.4: + resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} + engines: {node: '>=10'} + aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} @@ -2201,6 +2300,9 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -2600,6 +2702,10 @@ packages: resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} engines: {node: '>= 0.4'} + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -3556,6 +3662,26 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.6.2: + resolution: {integrity: sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + react-router@7.1.1: resolution: {integrity: sha512-39sXJkftkKWRZ2oJtHhCxmoCrBCULr/HAH4IT5DHlgu/Q0FCPV0S4Lx+abjDTx/74xoZzNYDYbOZWlJjruyuDQ==} engines: {node: '>=20.0.0'} @@ -3566,6 +3692,16 @@ packages: react-dom: optional: true + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -4075,6 +4211,26 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + use-sync-external-store@1.4.0: resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} peerDependencies: @@ -5182,6 +5338,85 @@ snapshots: optionalDependencies: '@types/react': 18.3.18 + '@radix-ui/react-dialog@1.1.4(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.1(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) + aria-hidden: 1.2.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.6.2(@types/react@18.3.18)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.18 + '@types/react-dom': 18.3.5(@types/react@18.3.18) + + '@radix-ui/react-dismissable-layer@1.1.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.18)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.18 + '@types/react-dom': 18.3.5(@types/react@18.3.18) + + '@radix-ui/react-focus-guards@1.1.1(@types/react@18.3.18)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.18 + + '@radix-ui/react-focus-scope@1.1.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.18 + '@types/react-dom': 18.3.5(@types/react@18.3.18) + + '@radix-ui/react-id@1.1.0(@types/react@18.3.18)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.18 + + '@radix-ui/react-portal@1.1.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.18 + '@types/react-dom': 18.3.5(@types/react@18.3.18) + + '@radix-ui/react-presence@1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.18 + '@types/react-dom': 18.3.5(@types/react@18.3.18) + '@radix-ui/react-primitive@2.0.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-slot': 1.1.1(@types/react@18.3.18)(react@18.3.1) @@ -5226,6 +5461,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.18 + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.18)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.18 + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.18)(react@18.3.1)': dependencies: react: 18.3.1 @@ -5994,7 +6236,7 @@ snapshots: transitivePeerDependencies: - '@swc/helpers' - '@vitest/coverage-v8@2.1.8(vitest@2.1.8(@types/node@22.10.5)(@vitest/ui@2.1.8)(happy-dom@16.3.0)(msw@2.7.0(@types/node@22.10.5)(typescript@5.6.3)))': + '@vitest/coverage-v8@2.1.8(vitest@2.1.8)': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -6012,7 +6254,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/eslint-plugin@1.1.24(@typescript-eslint/utils@8.19.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3))(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3)(vitest@2.1.8(@types/node@22.10.5)(@vitest/ui@2.1.8)(happy-dom@16.3.0)(msw@2.7.0(@types/node@22.10.5)(typescript@5.6.3)))': + '@vitest/eslint-plugin@1.1.24(@typescript-eslint/utils@8.19.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3))(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3)(vitest@2.1.8)': dependencies: '@typescript-eslint/utils': 8.19.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3) eslint: 9.17.0(jiti@2.4.2) @@ -6148,6 +6390,10 @@ snapshots: argparse@2.0.1: {} + aria-hidden@1.2.4: + dependencies: + tslib: 2.8.1 + aria-query@5.3.0: dependencies: dequal: 2.0.3 @@ -6484,6 +6730,8 @@ snapshots: detect-indent@6.1.0: {} + detect-node-es@1.1.0: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -6738,7 +6986,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.17.0(jiti@2.4.2)))(eslint@9.17.0(jiti@2.4.2)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: @@ -6760,7 +7008,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.17.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.17.0(jiti@2.4.2)))(eslint@9.17.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -7055,6 +7303,8 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 + get-nonce@1.0.1: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -7990,6 +8240,25 @@ snapshots: react-is@17.0.2: {} + react-remove-scroll-bar@2.3.8(@types/react@18.3.18)(react@18.3.1): + dependencies: + react: 18.3.1 + react-style-singleton: 2.2.3(@types/react@18.3.18)(react@18.3.1) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.18 + + react-remove-scroll@2.6.2(@types/react@18.3.18)(react@18.3.1): + dependencies: + react: 18.3.1 + react-remove-scroll-bar: 2.3.8(@types/react@18.3.18)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@18.3.18)(react@18.3.1) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@18.3.18)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@18.3.18)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.18 + react-router@7.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@types/cookie': 0.6.0 @@ -8000,6 +8269,14 @@ snapshots: optionalDependencies: react-dom: 18.3.1(react@18.3.1) + react-style-singleton@2.2.3(@types/react@18.3.18)(react@18.3.1): + dependencies: + get-nonce: 1.0.1 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.18 + react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -8543,6 +8820,21 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + use-callback-ref@1.3.3(@types/react@18.3.18)(react@18.3.1): + dependencies: + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.18 + + use-sidecar@1.1.3(@types/react@18.3.18)(react@18.3.1): + dependencies: + detect-node-es: 1.1.0 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.18 + use-sync-external-store@1.4.0(react@18.3.1): dependencies: react: 18.3.1 diff --git a/src/ui/dialog/dialog.css.ts b/src/ui/dialog/dialog.css.ts new file mode 100644 index 000000000..14b8ea4ae --- /dev/null +++ b/src/ui/dialog/dialog.css.ts @@ -0,0 +1,102 @@ +import { style } from '@vanilla-extract/css'; +import { RecipeVariants, recipe } from '@vanilla-extract/recipes'; +import { globalVars } from '../theme.css.ts'; +import { typography } from '../typography.css.ts'; + +export const dialog = style({ + backgroundColor: globalVars.color.white, + borderRadius: '6px', + padding: '20px', + display: 'flex', + flexDirection: 'column', + gap: '24px', + width: '303px', + maxWidth: '324px', + textAlign: 'center', + boxSizing: 'border-box', +}); + +export const textContainer = style({ + display: 'flex', + flexDirection: 'column', + gap: '8px', + alignItems: 'flex-start', + flexShrink: '0', +}); + +export const title = style([ + { color: globalVars.color.grey800, margin: '0px' }, + typography('head_1_18_sb'), +]); + +export const description = style([ + { + color: globalVars.color.grey500, + textAlign: 'left', + maxHeight: '264px', + margin: '0px', + }, + typography('body_3_14_r'), +]); + +export const buttonContainer = recipe({ + base: { + display: 'flex', + height: '44px', + gap: '8px', + }, + variants: { + type: { + default: { justifyContent: 'space-between' }, + danger: { justifyContent: 'space-between' }, + single: { justifyContent: 'center', width: '100%' }, + }, + }, +}); + +export const button = recipe({ + base: { + padding: '14px 30px', + borderRadius: '40px', + cursor: 'pointer', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + width: '127px', + height: '44px', + fontWeight: 'bold', + }, + variants: { + type: { + defaultLeft: { + backgroundColor: globalVars.color.white, + color: globalVars.color.grey800, + border: `1px solid ${globalVars.color.grey300}`, + }, + defaultRight: { + backgroundColor: globalVars.color.grey800, + color: globalVars.color.white, + border: `1px solid ${globalVars.color.grey800}`, + }, + dangerLeft: { + backgroundColor: globalVars.color.white, + color: globalVars.color.grey800, + border: `1px solid ${globalVars.color.grey300}`, + }, + dangerRight: { + backgroundColor: globalVars.color.redDanger, + color: globalVars.color.white, + border: `1px solid ${globalVars.color.redDanger}`, + }, + single: { + backgroundColor: globalVars.color.grey800, + color: globalVars.color.white, + border: `1px solid ${globalVars.color.grey800}`, + width: '100%', + }, + }, + }, +}); + +export type ButtonVariants = RecipeVariants; +export type ButtonContainerVariants = RecipeVariants; diff --git a/src/ui/dialog/dialog.stories.tsx b/src/ui/dialog/dialog.stories.tsx new file mode 100644 index 000000000..45103061e --- /dev/null +++ b/src/ui/dialog/dialog.stories.tsx @@ -0,0 +1,111 @@ +import { useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { Dialog, DialogAction } from './dialog.tsx'; + +const meta: Meta = { + title: 'ui/Dialog', + component: Dialog, + parameters: { + layout: 'centered', + }, + argTypes: { + action: { + control: 'select', + options: ['single', 'danger', 'default'], + }, + open: { + control: 'boolean', + }, + }, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + title: '타이틀자리입니다', + description: '제품을 삭제할 시 복용 약 성분 분석에 반영돼요.', + action: 'default', + leftButtonText: 'button', + rightButtonText: 'button', + open: true, + }, + render: (args) => {}} />, +}; + +export const Danger: Story = { + args: { + title: '타이틀자리입니다', + description: '제품을 삭제할 시 복용 약 성분 분석에 반영돼요.', + action: 'danger', + leftButtonText: 'button', + rightButtonText: 'button', + open: true, + }, + render: (args) => {}} />, +}; + +export const Single: Story = { + args: { + title: '타이틀자리입니다', + description: '제품을 삭제할 시 복용 약 성분 분석에 반영돼요.', + action: 'single', + rightButtonText: 'button', + open: true, + }, + render: (args) => {}} />, +}; + +const WithStateDialog = () => { + const [open, setOpen] = useState(false); + const [action, setAction] = useState('default'); + + return ( +
+ + + + + { + alert('확인 버튼 클릭됨!'); + setOpen(false); + }} + /> +
+ ); +}; + +export const WithState: Story = { + render: () => , +}; diff --git a/src/ui/dialog/dialog.tsx b/src/ui/dialog/dialog.tsx new file mode 100644 index 000000000..7a9ebdfcb --- /dev/null +++ b/src/ui/dialog/dialog.tsx @@ -0,0 +1,72 @@ +import * as RadixDialog from '@radix-ui/react-dialog'; +import * as styles from './dialog.css.ts'; + +export type DialogAction = 'single' | 'danger' | 'default'; + +export interface DialogProps { + title: string; + description: string; + action: DialogAction; + leftButtonText?: string; + rightButtonText?: string; + open: boolean; + onOpenChange: (open: boolean) => void; + onConfirm?: () => void; +} + +export const Dialog = ({ + title, + description, + action, + leftButtonText = '취소', + rightButtonText = '확인', + open, + onOpenChange, + onConfirm, +}: DialogProps) => { + return ( + + + +
+ + {title} + + + {description} + +
+ +
+ {action !== 'single' && ( + + + + )} + + + +
+
+
+
+ ); +}; diff --git a/src/ui/dialog/index.ts b/src/ui/dialog/index.ts new file mode 100644 index 000000000..543bff826 --- /dev/null +++ b/src/ui/dialog/index.ts @@ -0,0 +1 @@ +export { Dialog, type DialogProps } from './dialog.tsx';