@pmate/account-sdk 0.6.1 → 0.7.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/package.json +13 -12
- package/src/api/AccountService.ts +4 -2
- package/src/api/Api.ts +1 -1
- package/src/api/AppService.ts +8 -7
- package/src/api/EntityService.ts +1 -1
- package/src/api/ProfileService.ts +1 -1
- package/src/atoms/accountProfileAtom.ts +1 -1
- package/src/atoms/profileAtom.ts +1 -1
- package/src/atoms/profilesAtom.ts +1 -1
- package/src/atoms/switchProfileAtom.ts +1 -1
- package/src/atoms/userSettingsAtom.ts +4 -2
- package/src/components/{AuthProviderV2.tsx → AuthProvider.tsx} +29 -64
- package/src/components/index.ts +1 -1
- package/src/hooks/useAppBackgroundStyle.ts +2 -2
- package/src/hooks/useProfileStepFlow.ts +2 -1
- package/src/index.ts +1 -1
- package/src/types/app.ts +8 -2
- package/src/utils/AccountManagerV2.ts +1 -1
- package/src/utils/appTheme.ts +16 -0
- package/src/utils/index.ts +1 -0
- package/src/components/Button.tsx +0 -39
- package/src/components/Drawer.tsx +0 -80
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pmate/account-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -14,11 +14,6 @@
|
|
|
14
14
|
"publishConfig": {
|
|
15
15
|
"access": "public"
|
|
16
16
|
},
|
|
17
|
-
"scripts": {
|
|
18
|
-
"npm:publish": "npm publish --registry https://registry.npmjs.org/",
|
|
19
|
-
"test": "vitest run",
|
|
20
|
-
"test:watch": "vitest"
|
|
21
|
-
},
|
|
22
17
|
"peerDependencies": {
|
|
23
18
|
"jotai": "*",
|
|
24
19
|
"jotai-family": "*",
|
|
@@ -36,9 +31,10 @@
|
|
|
36
31
|
}
|
|
37
32
|
},
|
|
38
33
|
"dependencies": {
|
|
34
|
+
"@pmate/auth-widgets": "^0.1.1",
|
|
39
35
|
"@pmate/lang": "^1.0.2",
|
|
40
|
-
"@pmate/meta": "^1.1.
|
|
41
|
-
"@pmate/service-core": "^1.0.
|
|
36
|
+
"@pmate/meta": "^1.1.4",
|
|
37
|
+
"@pmate/service-core": "^1.0.1",
|
|
42
38
|
"@pmate/utils": "^1.0.3",
|
|
43
39
|
"browser-image-compression": "^2.0.2",
|
|
44
40
|
"elysia": "^1.4.19",
|
|
@@ -47,13 +43,18 @@
|
|
|
47
43
|
"react-use": "^17.6.0"
|
|
48
44
|
},
|
|
49
45
|
"devDependencies": {
|
|
50
|
-
"@types/react": "
|
|
51
|
-
"@types/react-dom": "
|
|
46
|
+
"@types/react": "^19.2.7",
|
|
47
|
+
"@types/react-dom": "^19.2.3",
|
|
52
48
|
"@vitejs/plugin-react": "^4.2.1",
|
|
53
49
|
"jsdom": "^26.1.0",
|
|
54
50
|
"react-dom": "*",
|
|
55
51
|
"typescript": "^5.2.2",
|
|
56
|
-
"vite": "
|
|
52
|
+
"vite": "^7.3.1",
|
|
57
53
|
"vitest": "^4.0.17"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"npm:publish": "npm publish --registry https://registry.npmjs.org/",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:watch": "vitest"
|
|
58
59
|
}
|
|
59
|
-
}
|
|
60
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
2
|
AuthLoginResponse,
|
|
3
3
|
AuthRequest,
|
|
4
4
|
AuthSession,
|
|
@@ -34,7 +34,9 @@ export class AccountService {
|
|
|
34
34
|
})
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
public static async login(
|
|
37
|
+
public static async login(
|
|
38
|
+
request: AuthRequest
|
|
39
|
+
): Promise<AuthLoginResponse> {
|
|
38
40
|
return AccountService.authRequest<AuthLoginResponse>("/login", {
|
|
39
41
|
method: "POST",
|
|
40
42
|
body: JSON.stringify(request),
|
package/src/api/Api.ts
CHANGED
package/src/api/AppService.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { lru } from "@pmate/utils"
|
|
2
|
-
import type { AppConfig, ProfileStep } from "../types/app"
|
|
2
|
+
import type { AppConfig, AppThemePreset, ProfileStep } from "../types/app"
|
|
3
3
|
|
|
4
|
-
const APPS_ENDPOINT =
|
|
5
|
-
|
|
4
|
+
const APPS_ENDPOINT =
|
|
5
|
+
import.meta.env.VITE_PUBLIC_APPS_SERVER_ENDPOINT ||
|
|
6
|
+
"https://apps-api.pmate.chat"
|
|
6
7
|
const DEFAULT_ICON = "https://parrot-static.pmate.chat/parrot-logo.png"
|
|
8
|
+
const DEFAULT_THEME_PRESET: AppThemePreset = "default"
|
|
7
9
|
|
|
8
10
|
type AppRegistryRecord = {
|
|
9
11
|
id: string
|
|
10
12
|
name: string
|
|
11
13
|
icon: string
|
|
12
14
|
theme?: {
|
|
13
|
-
|
|
14
|
-
themeColor?: string
|
|
15
|
+
preset?: AppThemePreset
|
|
15
16
|
welcomeText?: string
|
|
16
17
|
}
|
|
17
18
|
profileSchema?: ProfileStep[]
|
|
@@ -26,12 +27,12 @@ const buildDisplayNameFromAppId = (appId: string): string => {
|
|
|
26
27
|
|
|
27
28
|
const toAppConfig = (record: AppRegistryRecord): AppConfig => {
|
|
28
29
|
const fallbackName = buildDisplayNameFromAppId(record.id)
|
|
30
|
+
const themePreset = record.theme?.preset || DEFAULT_THEME_PRESET
|
|
29
31
|
return {
|
|
30
32
|
id: record.id,
|
|
31
33
|
name: record.name || fallbackName,
|
|
32
34
|
icon: record.icon || DEFAULT_ICON,
|
|
33
|
-
|
|
34
|
-
themeColor: record.theme?.themeColor,
|
|
35
|
+
themePreset,
|
|
35
36
|
welcomeText: record.theme?.welcomeText || `Welcome to ${fallbackName}`,
|
|
36
37
|
profiles: record.profileSchema ?? [],
|
|
37
38
|
}
|
package/src/api/EntityService.ts
CHANGED
package/src/atoms/profileAtom.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { VoiceList } from "@pmate/meta"
|
|
2
|
-
import { Difficulty
|
|
3
|
-
import {
|
|
2
|
+
import { Difficulty } from "@pmate/meta"
|
|
3
|
+
import type { UserSettings } from "@pmate/meta"
|
|
4
|
+
import { atom } from "jotai"
|
|
5
|
+
import type { WritableAtom } from "jotai"
|
|
4
6
|
import { LSKEYS, localStorageJsonAtom } from "./localStorageAtom"
|
|
5
7
|
|
|
6
8
|
const defaultSettings: UserSettings = {
|
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
AuthLoginPromptSheet,
|
|
3
|
+
AuthSessionErrorCard,
|
|
4
|
+
} from "@pmate/auth-widgets"
|
|
3
5
|
import { useSetAtom } from "jotai"
|
|
4
6
|
import {
|
|
5
7
|
Component,
|
|
6
|
-
PropsWithChildren,
|
|
7
8
|
useCallback,
|
|
8
9
|
useEffect,
|
|
9
10
|
useState,
|
|
10
11
|
} from "react"
|
|
11
|
-
import {
|
|
12
|
+
import type { PropsWithChildren } from "react"
|
|
12
13
|
import { accountAtom } from "../atoms/accountAtom"
|
|
13
|
-
import { AccountLifecycleState } from "../types/account.types"
|
|
14
14
|
import { userLogoutAtom } from "../atoms/userLogoutAtom"
|
|
15
|
-
import {
|
|
15
|
+
import { useAuthSnapshot } from "../hooks/useAuthSnapshot"
|
|
16
|
+
import { AccountLifecycleState } from "../types/account.types"
|
|
16
17
|
import { NotAuthenticatedError } from "../utils/errors"
|
|
17
18
|
import {
|
|
18
19
|
getWindowPathname,
|
|
19
20
|
subscribeToLocationChange,
|
|
20
21
|
} from "../utils/location"
|
|
22
|
+
import { Redirect } from "../utils/Redirect"
|
|
21
23
|
|
|
22
24
|
export type AuthRoute =
|
|
23
25
|
| string
|
|
@@ -69,12 +71,12 @@ const getAuthBehaviors = (
|
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
type
|
|
74
|
+
type AuthProviderProps = PropsWithChildren<{
|
|
73
75
|
app: string
|
|
74
76
|
authRoutes?: AuthRoute[]
|
|
75
77
|
onLoginSuccess?: () => void | Promise<void>
|
|
76
|
-
rtcProvider?: React.ComponentType<{ children: React.ReactNode }>
|
|
77
78
|
pathname?: string
|
|
79
|
+
rtcProvider?: React.ComponentType<{ children: React.ReactNode }>
|
|
78
80
|
}>
|
|
79
81
|
|
|
80
82
|
const useWindowPathname = () => {
|
|
@@ -87,13 +89,13 @@ const useWindowPathname = () => {
|
|
|
87
89
|
return pathname
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
export const
|
|
92
|
+
export const AuthProvider = ({
|
|
91
93
|
app,
|
|
92
94
|
authRoutes,
|
|
93
|
-
rtcProvider: RtcProvider,
|
|
94
|
-
pathname: pathnameProp,
|
|
95
95
|
children,
|
|
96
|
-
|
|
96
|
+
pathname: pathnameProp,
|
|
97
|
+
rtcProvider: RtcProvider,
|
|
98
|
+
}: AuthProviderProps) => {
|
|
97
99
|
const pathname = pathnameProp ?? useWindowPathname()
|
|
98
100
|
const [isLoginPromptOpen, setIsLoginPromptOpen] = useState(false)
|
|
99
101
|
const [isLoginErrorDismissed, setIsLoginErrorDismissed] = useState(false)
|
|
@@ -152,6 +154,7 @@ export const AuthProviderV2 = ({
|
|
|
152
154
|
requiresAuth && !snapshot.account && authBehavior === "prompt",
|
|
153
155
|
)
|
|
154
156
|
}, [authBehavior, loading, requiresAuth, snapshot.account])
|
|
157
|
+
|
|
155
158
|
useEffect(() => {
|
|
156
159
|
if (!loginError) {
|
|
157
160
|
setIsLoginErrorDismissed(false)
|
|
@@ -162,9 +165,7 @@ export const AuthProviderV2 = ({
|
|
|
162
165
|
setIsLoginPromptOpen(false)
|
|
163
166
|
if (window.history.length > 1) {
|
|
164
167
|
window.history.back()
|
|
165
|
-
return
|
|
166
168
|
}
|
|
167
|
-
// window.location.href = "/"
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
if (loading && requiresAuth) {
|
|
@@ -172,26 +173,12 @@ export const AuthProviderV2 = ({
|
|
|
172
173
|
}
|
|
173
174
|
if (loginError && requiresAuth && !isLoginErrorDismissed) {
|
|
174
175
|
return (
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
We could not restore your session. Please try again.
|
|
182
|
-
</p>
|
|
183
|
-
<div className="mt-3 text-xs text-rose-500">{loginError.message}</div>
|
|
184
|
-
<div className="mt-4 flex flex-wrap gap-3">
|
|
185
|
-
<button
|
|
186
|
-
className="rounded-md bg-rose-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-rose-700"
|
|
187
|
-
type="button"
|
|
188
|
-
onClick={() => window.location.reload()}
|
|
189
|
-
>
|
|
190
|
-
Reload
|
|
191
|
-
</button>
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
194
|
-
</div>
|
|
176
|
+
<AuthSessionErrorCard
|
|
177
|
+
title="Login failed"
|
|
178
|
+
description="We could not restore your session. Please try again."
|
|
179
|
+
message={loginError.message}
|
|
180
|
+
onAction={() => window.location.reload()}
|
|
181
|
+
/>
|
|
195
182
|
)
|
|
196
183
|
}
|
|
197
184
|
if (requiresAuth && hasAccount === null) {
|
|
@@ -199,36 +186,12 @@ export const AuthProviderV2 = ({
|
|
|
199
186
|
}
|
|
200
187
|
if (requiresAuth && hasAccount === false && authBehavior === "prompt") {
|
|
201
188
|
return (
|
|
202
|
-
<
|
|
189
|
+
<AuthLoginPromptSheet
|
|
203
190
|
open={isLoginPromptOpen}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
<div className="rounded-t-2xl px-6 pb-6 pt-4">
|
|
209
|
-
<div className="mx-auto mb-3 h-1.5 w-12 rounded-full bg-slate-200" />
|
|
210
|
-
<div className="text-lg font-semibold text-slate-900">
|
|
211
|
-
You need login to continue ?
|
|
212
|
-
</div>
|
|
213
|
-
<div className="mt-5 flex items-center justify-end gap-3">
|
|
214
|
-
<Button
|
|
215
|
-
type="button"
|
|
216
|
-
variant="plain"
|
|
217
|
-
className="min-w-[96px] justify-center"
|
|
218
|
-
onClick={handleBack}
|
|
219
|
-
>
|
|
220
|
-
Back
|
|
221
|
-
</Button>
|
|
222
|
-
<Button
|
|
223
|
-
type="button"
|
|
224
|
-
className="min-w-[96px] justify-center"
|
|
225
|
-
onClick={() => Redirect.toLogin(app)}
|
|
226
|
-
>
|
|
227
|
-
Login
|
|
228
|
-
</Button>
|
|
229
|
-
</div>
|
|
230
|
-
</div>
|
|
231
|
-
</Drawer>
|
|
191
|
+
onBack={handleBack}
|
|
192
|
+
onLogin={() => Redirect.toLogin(app)}
|
|
193
|
+
title="You need login to continue ?"
|
|
194
|
+
/>
|
|
232
195
|
)
|
|
233
196
|
}
|
|
234
197
|
if (!requiresAuth) {
|
|
@@ -242,6 +205,8 @@ export const AuthProviderV2 = ({
|
|
|
242
205
|
)
|
|
243
206
|
}
|
|
244
207
|
|
|
208
|
+
export const AuthProviderV2 = AuthProvider
|
|
209
|
+
|
|
245
210
|
interface AuthErrorBoundaryState {
|
|
246
211
|
hasError: boolean
|
|
247
212
|
}
|
|
@@ -281,8 +246,8 @@ class AuthErrorBoundaryInner extends Component<
|
|
|
281
246
|
}
|
|
282
247
|
|
|
283
248
|
const AuthErrorBoundary = ({
|
|
284
|
-
children,
|
|
285
249
|
app,
|
|
250
|
+
children,
|
|
286
251
|
}: PropsWithChildren<{
|
|
287
252
|
app: string
|
|
288
253
|
}>) => {
|
package/src/components/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./AuthProvider"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect, useMemo, useState } from "react"
|
|
2
2
|
import { getWindowSearch, subscribeToLocationChange } from "../utils/location"
|
|
3
|
+
import { getAppThemeBackground } from "../utils/appTheme"
|
|
3
4
|
import { useAppConfig } from "./useAppConfig"
|
|
4
5
|
|
|
5
6
|
export const useAppBackgroundStyle = () => {
|
|
@@ -17,8 +18,7 @@ export const useAppBackgroundStyle = () => {
|
|
|
17
18
|
|
|
18
19
|
return useMemo(
|
|
19
20
|
() => ({
|
|
20
|
-
background:
|
|
21
|
-
appConfig?.background || "linear-gradient(180deg, #9ca3af 0%, #6b7280 100%)",
|
|
21
|
+
background: getAppThemeBackground(appConfig?.themePreset),
|
|
22
22
|
}),
|
|
23
23
|
[appConfig]
|
|
24
24
|
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback, useMemo } from "react"
|
|
2
|
-
import { isProfileStepType
|
|
2
|
+
import { isProfileStepType } from "../utils/profileStep"
|
|
3
|
+
import type { ProfileStepType } from "../utils/profileStep"
|
|
3
4
|
import { useAppConfig } from "./useAppConfig"
|
|
4
5
|
|
|
5
6
|
type UseProfileStepFlowParams = {
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ export * from "./atoms"
|
|
|
2
2
|
export * from "./components"
|
|
3
3
|
export * from "./i18n"
|
|
4
4
|
export { DEFAULT_APP_ID } from "./constants"
|
|
5
|
-
export type { AppConfig, ProfileStep } from "./types/app"
|
|
5
|
+
export type { AppConfig, AppThemePreset, ProfileStep } from "./types/app"
|
|
6
6
|
export * from "./hooks"
|
|
7
7
|
export * from "./utils"
|
|
8
8
|
export * from "./api"
|
package/src/types/app.ts
CHANGED
|
@@ -11,12 +11,18 @@ export type ProfileStep = {
|
|
|
11
11
|
required: boolean
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export type AppThemePreset =
|
|
15
|
+
| "default"
|
|
16
|
+
| "chat"
|
|
17
|
+
| "parrotmate"
|
|
18
|
+
| "sunrise"
|
|
19
|
+
| "graphite"
|
|
20
|
+
|
|
14
21
|
export interface AppConfig {
|
|
15
22
|
id: string
|
|
16
23
|
name: string
|
|
17
24
|
icon: string
|
|
18
|
-
|
|
19
|
-
themeColor?: string
|
|
25
|
+
themePreset: AppThemePreset
|
|
20
26
|
welcomeText: string
|
|
21
27
|
profiles: ProfileStep[]
|
|
22
28
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AppThemePreset } from "../types/app"
|
|
2
|
+
|
|
3
|
+
const DEFAULT_THEME_PRESET: AppThemePreset = "default"
|
|
4
|
+
|
|
5
|
+
const THEME_BACKGROUNDS: Record<AppThemePreset, string> = {
|
|
6
|
+
default: "linear-gradient(180deg, #9ca3af 0%, #6b7280 100%)",
|
|
7
|
+
chat: "linear-gradient(180deg, #0f766e 0%, #115e59 100%)",
|
|
8
|
+
parrotmate: "linear-gradient(180deg, #5b4cf0 0%, #4537d2 100%)",
|
|
9
|
+
sunrise: "linear-gradient(180deg, #f59e0b 0%, #ea580c 100%)",
|
|
10
|
+
graphite: "linear-gradient(180deg, #111827 0%, #1f2937 100%)",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const getAppThemeBackground = (themePreset?: AppThemePreset) => {
|
|
14
|
+
const preset = themePreset ?? DEFAULT_THEME_PRESET
|
|
15
|
+
return THEME_BACKGROUNDS[preset] ?? THEME_BACKGROUNDS[DEFAULT_THEME_PRESET]
|
|
16
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import React from "react"
|
|
2
|
-
|
|
3
|
-
type ButtonVariant = "primary" | "plain"
|
|
4
|
-
|
|
5
|
-
export interface ButtonProps
|
|
6
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
7
|
-
variant?: ButtonVariant
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const joinClassNames = (...parts: Array<string | false | null | undefined>) =>
|
|
11
|
-
parts.filter(Boolean).join(" ")
|
|
12
|
-
|
|
13
|
-
const baseClassName =
|
|
14
|
-
"inline-flex items-center rounded-md px-3 py-1.5 text-sm font-medium transition-colors"
|
|
15
|
-
|
|
16
|
-
const variantClassName: Record<ButtonVariant, string> = {
|
|
17
|
-
primary: "bg-slate-900 text-white hover:bg-slate-800",
|
|
18
|
-
plain: "border border-slate-200 bg-white text-slate-700 hover:bg-slate-50",
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const Button = ({
|
|
22
|
-
variant = "primary",
|
|
23
|
-
className,
|
|
24
|
-
disabled,
|
|
25
|
-
...props
|
|
26
|
-
}: ButtonProps) => {
|
|
27
|
-
return (
|
|
28
|
-
<button
|
|
29
|
-
{...props}
|
|
30
|
-
disabled={disabled}
|
|
31
|
-
className={joinClassNames(
|
|
32
|
-
baseClassName,
|
|
33
|
-
variantClassName[variant],
|
|
34
|
-
disabled && "cursor-not-allowed opacity-60",
|
|
35
|
-
className,
|
|
36
|
-
)}
|
|
37
|
-
/>
|
|
38
|
-
)
|
|
39
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from "react"
|
|
2
|
-
|
|
3
|
-
type DrawerAnchor = "left" | "right" | "top" | "bottom"
|
|
4
|
-
|
|
5
|
-
export interface DrawerProps {
|
|
6
|
-
open: boolean
|
|
7
|
-
onClose: () => void
|
|
8
|
-
children: React.ReactNode
|
|
9
|
-
anchor?: DrawerAnchor
|
|
10
|
-
className?: string
|
|
11
|
-
overlayClassName?: string
|
|
12
|
-
id?: string
|
|
13
|
-
style?: React.CSSProperties
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const joinClassNames = (...parts: Array<string | false | null | undefined>) =>
|
|
17
|
-
parts.filter(Boolean).join(" ")
|
|
18
|
-
|
|
19
|
-
const positionClassMap: Record<DrawerAnchor, string> = {
|
|
20
|
-
left: "left-0 top-0 h-full",
|
|
21
|
-
right: "right-0 top-0 h-full",
|
|
22
|
-
top: "left-0 top-0 w-full",
|
|
23
|
-
bottom: "bottom-0 left-0 w-full",
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const hiddenTransformMap: Record<DrawerAnchor, string> = {
|
|
27
|
-
left: "-translate-x-full",
|
|
28
|
-
right: "translate-x-full",
|
|
29
|
-
top: "-translate-y-full",
|
|
30
|
-
bottom: "translate-y-full",
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const Drawer = ({
|
|
34
|
-
open,
|
|
35
|
-
onClose,
|
|
36
|
-
children,
|
|
37
|
-
anchor = "right",
|
|
38
|
-
className,
|
|
39
|
-
overlayClassName,
|
|
40
|
-
id,
|
|
41
|
-
style,
|
|
42
|
-
}: DrawerProps) => {
|
|
43
|
-
const [mounted, setMounted] = useState(open)
|
|
44
|
-
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
if (open) {
|
|
47
|
-
setMounted(true)
|
|
48
|
-
}
|
|
49
|
-
}, [open])
|
|
50
|
-
|
|
51
|
-
const handleTransitionEnd = () => {
|
|
52
|
-
if (!open) {
|
|
53
|
-
setMounted(false)
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (!mounted) return null
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<div
|
|
61
|
-
id={id}
|
|
62
|
-
className={joinClassNames("fixed inset-0 z-[1002]", overlayClassName)}
|
|
63
|
-
onClick={onClose}
|
|
64
|
-
>
|
|
65
|
-
<div
|
|
66
|
-
className={joinClassNames(
|
|
67
|
-
"absolute bg-white transition-transform duration-300",
|
|
68
|
-
positionClassMap[anchor],
|
|
69
|
-
open ? "translate-x-0 translate-y-0" : hiddenTransformMap[anchor],
|
|
70
|
-
className,
|
|
71
|
-
)}
|
|
72
|
-
style={style}
|
|
73
|
-
onClick={(event) => event.stopPropagation()}
|
|
74
|
-
onTransitionEnd={handleTransitionEnd}
|
|
75
|
-
>
|
|
76
|
-
{children}
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
)
|
|
80
|
-
}
|