@quiltt/react 2.1.0 → 2.1.2
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/CHANGELOG.md +482 -0
- package/README.md +110 -75
- package/dist/{QuilttSettingsProvider-6904aa95.d.ts → QuilttSettingsProvider-d1d44353.d.ts} +0 -1
- package/dist/components/index.cjs +1 -1
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/index.js.map +1 -1
- package/dist/hooks/index.cjs +1 -1
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/session/index.d.ts +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/index.cjs +1 -1
- package/dist/providers/index.cjs.map +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/{useRevokeSession-d988b7ad.d.ts → useRevokeSession-37aa05cb.d.ts} +1 -1
- package/package.json +13 -3
- package/src/components/QuilttButton.tsx +24 -0
- package/src/components/QuilttContainer.tsx +30 -0
- package/src/components/index.ts +2 -0
- package/src/hooks/helpers/index.ts +2 -0
- package/src/hooks/helpers/useEventListener.ts +84 -0
- package/src/hooks/helpers/useIsomorphicLayoutEffect.ts +11 -0
- package/src/hooks/index.ts +8 -0
- package/src/hooks/session/index.ts +4 -0
- package/src/hooks/session/useAuthenticateSession.ts +54 -0
- package/src/hooks/session/useIdentifySession.ts +55 -0
- package/src/hooks/session/useImportSession.ts +43 -0
- package/src/hooks/session/useRevokeSession.ts +27 -0
- package/src/hooks/useQuilttClient.ts +5 -0
- package/src/hooks/useQuilttConnector.ts +39 -0
- package/src/hooks/useQuilttSession.ts +45 -0
- package/src/hooks/useQuilttSettings.ts +17 -0
- package/src/hooks/useSession.ts +69 -0
- package/src/hooks/useStorage.ts +73 -0
- package/src/index.ts +4 -0
- package/src/providers/QuilttAuthProvider.tsx +43 -0
- package/src/providers/QuilttProvider.tsx +19 -0
- package/src/providers/QuilttSettingsProvider.tsx +20 -0
- package/src/providers/index.ts +3 -0
- package/src/types.ts +9 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useCallback } from 'react'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
AuthAPI,
|
|
5
|
+
SessionResponse,
|
|
6
|
+
UnprocessableData,
|
|
7
|
+
UnprocessableResponse,
|
|
8
|
+
UsernamePayload,
|
|
9
|
+
} from '@quiltt/core'
|
|
10
|
+
|
|
11
|
+
import type { SetSession } from '../useSession'
|
|
12
|
+
|
|
13
|
+
type IdentifySessionCallbacks = {
|
|
14
|
+
onSuccess?: () => unknown
|
|
15
|
+
onChallenged?: () => unknown
|
|
16
|
+
onError?: (errors: UnprocessableData) => unknown
|
|
17
|
+
}
|
|
18
|
+
type IdentifySession = (
|
|
19
|
+
payload: UsernamePayload,
|
|
20
|
+
callbacks: IdentifySessionCallbacks
|
|
21
|
+
) => Promise<unknown>
|
|
22
|
+
|
|
23
|
+
type UseIdentifySession = (auth: AuthAPI, setSession: SetSession) => IdentifySession
|
|
24
|
+
|
|
25
|
+
export const useIdentifySession: UseIdentifySession = (auth, setSession) => {
|
|
26
|
+
const identifySession = useCallback<IdentifySession>(
|
|
27
|
+
async (payload, callbacks) => {
|
|
28
|
+
const response = await auth.identify(payload)
|
|
29
|
+
const unprocessableResponse = response as UnprocessableResponse
|
|
30
|
+
|
|
31
|
+
switch (response.status) {
|
|
32
|
+
case 201:
|
|
33
|
+
setSession((response as SessionResponse).data.token)
|
|
34
|
+
if (callbacks.onSuccess) return callbacks.onSuccess()
|
|
35
|
+
break
|
|
36
|
+
|
|
37
|
+
case 202:
|
|
38
|
+
if (callbacks.onChallenged) return callbacks.onChallenged()
|
|
39
|
+
break
|
|
40
|
+
|
|
41
|
+
case 422:
|
|
42
|
+
if (callbacks.onError) return callbacks.onError(unprocessableResponse.data)
|
|
43
|
+
break
|
|
44
|
+
|
|
45
|
+
default:
|
|
46
|
+
throw new Error(`Unexpected auth identify response status: ${response.status}`)
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
[auth, setSession]
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return identifySession
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default useIdentifySession
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useCallback } from 'react'
|
|
2
|
+
|
|
3
|
+
import type { AuthAPI, Maybe, QuilttJWT } from '@quiltt/core'
|
|
4
|
+
|
|
5
|
+
import type { SetSession } from '../useSession'
|
|
6
|
+
|
|
7
|
+
type ImportSession = (token: string) => Promise<boolean>
|
|
8
|
+
|
|
9
|
+
type UseImportSession = (
|
|
10
|
+
auth: AuthAPI,
|
|
11
|
+
session: Maybe<QuilttJWT> | undefined,
|
|
12
|
+
setSession: SetSession
|
|
13
|
+
) => ImportSession
|
|
14
|
+
|
|
15
|
+
export const useImportSession: UseImportSession = (auth, session, setSession) => {
|
|
16
|
+
const importSession = useCallback<ImportSession>(
|
|
17
|
+
async (token) => {
|
|
18
|
+
if (!token) return !!session
|
|
19
|
+
if (session && session.token == token) return true
|
|
20
|
+
|
|
21
|
+
const response = await auth.ping(token)
|
|
22
|
+
|
|
23
|
+
switch (response.status) {
|
|
24
|
+
case 200:
|
|
25
|
+
setSession(token)
|
|
26
|
+
return true
|
|
27
|
+
break
|
|
28
|
+
|
|
29
|
+
case 401:
|
|
30
|
+
return false
|
|
31
|
+
break
|
|
32
|
+
|
|
33
|
+
default:
|
|
34
|
+
throw new Error(`Unexpected auth ping response status: ${response.status}`)
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
[auth, session, setSession]
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return importSession
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default useImportSession
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useCallback } from 'react'
|
|
2
|
+
|
|
3
|
+
import type { AuthAPI, Maybe, QuilttJWT } from '@quiltt/core'
|
|
4
|
+
|
|
5
|
+
import type { SetSession } from '../useSession'
|
|
6
|
+
|
|
7
|
+
type RevokeSession = () => Promise<void>
|
|
8
|
+
|
|
9
|
+
type UseRevokeSession = (
|
|
10
|
+
auth: AuthAPI,
|
|
11
|
+
session: Maybe<QuilttJWT> | undefined,
|
|
12
|
+
setSession: SetSession
|
|
13
|
+
) => RevokeSession
|
|
14
|
+
|
|
15
|
+
export const useRevokeSession: UseRevokeSession = (auth, session, setSession) => {
|
|
16
|
+
const revokeSession = useCallback<RevokeSession>(async () => {
|
|
17
|
+
if (!session) return
|
|
18
|
+
|
|
19
|
+
await auth.revoke(session.token)
|
|
20
|
+
|
|
21
|
+
setSession(null)
|
|
22
|
+
}, [auth, session, setSession])
|
|
23
|
+
|
|
24
|
+
return revokeSession
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default useRevokeSession
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect } from 'react'
|
|
4
|
+
import { useQuilttSession } from './useQuilttSession'
|
|
5
|
+
|
|
6
|
+
const QUILTT_CDN_BASE = process.env.QUILTT_CDN_BASE || 'https://cdn.quiltt.io'
|
|
7
|
+
|
|
8
|
+
// Script Element Singleton
|
|
9
|
+
let scriptElement: HTMLScriptElement
|
|
10
|
+
|
|
11
|
+
export const useQuilttConnector = () => {
|
|
12
|
+
const { session } = useQuilttSession()
|
|
13
|
+
|
|
14
|
+
// Create Script Element
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (scriptElement) return
|
|
17
|
+
|
|
18
|
+
scriptElement = document.createElement('script')
|
|
19
|
+
scriptElement.src = `${QUILTT_CDN_BASE}/v1/connector.js`
|
|
20
|
+
|
|
21
|
+
if (session?.token) {
|
|
22
|
+
scriptElement.setAttribute('quiltt-token', session.token)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
document.head.appendChild(scriptElement)
|
|
26
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
27
|
+
}, [])
|
|
28
|
+
|
|
29
|
+
// Update Script Element
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!scriptElement) return
|
|
32
|
+
|
|
33
|
+
if (session?.token && session.token !== scriptElement.getAttribute('quiltt-token')) {
|
|
34
|
+
scriptElement.setAttribute('quiltt-token', session.token)
|
|
35
|
+
} else if (!session?.token && scriptElement.getAttribute('quiltt-token')) {
|
|
36
|
+
scriptElement.removeAttribute('quiltt-token')
|
|
37
|
+
}
|
|
38
|
+
}, [session?.token])
|
|
39
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useCallback } from 'react'
|
|
2
|
+
|
|
3
|
+
import { useSession } from './useSession'
|
|
4
|
+
|
|
5
|
+
import { AuthAPI } from '@quiltt/core'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
useAuthenticateSession,
|
|
9
|
+
useIdentifySession,
|
|
10
|
+
useImportSession,
|
|
11
|
+
useRevokeSession,
|
|
12
|
+
} from './session'
|
|
13
|
+
import { useQuilttSettings } from './useQuilttSettings'
|
|
14
|
+
|
|
15
|
+
export const useQuilttSession = () => {
|
|
16
|
+
const { clientId } = useQuilttSettings()
|
|
17
|
+
const [session, setSession] = useSession()
|
|
18
|
+
|
|
19
|
+
const auth = new AuthAPI(clientId)
|
|
20
|
+
const importSession = useImportSession(auth, session, setSession)
|
|
21
|
+
const identifySession = useIdentifySession(auth, setSession)
|
|
22
|
+
const authenticateSession = useAuthenticateSession(auth, setSession)
|
|
23
|
+
const revokeSession = useRevokeSession(auth, session, setSession)
|
|
24
|
+
|
|
25
|
+
// Optionally takes a token, to help guard against async processes clearing the wrong session
|
|
26
|
+
const forgetSession = useCallback(
|
|
27
|
+
async (token?: string) => {
|
|
28
|
+
if (!token || (session && token && token == session.token)) {
|
|
29
|
+
setSession(null)
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
[session, setSession]
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
session,
|
|
37
|
+
importSession,
|
|
38
|
+
identifySession,
|
|
39
|
+
authenticateSession,
|
|
40
|
+
revokeSession,
|
|
41
|
+
forgetSession,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default useQuilttSession
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext } from 'react'
|
|
4
|
+
|
|
5
|
+
type QuilttSettingsContext = {
|
|
6
|
+
clientId?: string | undefined
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const QuilttSettings = createContext<QuilttSettingsContext>({})
|
|
10
|
+
|
|
11
|
+
export const useQuilttSettings = () => {
|
|
12
|
+
const settings = useContext(QuilttSettings)
|
|
13
|
+
|
|
14
|
+
return { ...settings }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default useQuilttSettings
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Dispatch, SetStateAction, useMemo } from 'react'
|
|
4
|
+
import { useEffect, useCallback } from 'react'
|
|
5
|
+
|
|
6
|
+
import type { Maybe, PrivateClaims, QuilttJWT } from '@quiltt/core'
|
|
7
|
+
import { JsonWebTokenParse, Timeoutable } from '@quiltt/core'
|
|
8
|
+
|
|
9
|
+
import { useStorage } from './useStorage'
|
|
10
|
+
|
|
11
|
+
export type SetSession = Dispatch<SetStateAction<Maybe<string> | undefined>>
|
|
12
|
+
|
|
13
|
+
const parse = JsonWebTokenParse<PrivateClaims>
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Singleton timeout, allows hooks to come and go, while ensuring that there is
|
|
17
|
+
* one notification being sent, preventing race conditions.
|
|
18
|
+
*/
|
|
19
|
+
const sessionTimer = new Timeoutable()
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* useSession uses useStorage to support a global singleton style of access. When
|
|
23
|
+
* updated, all components, and windows should also invalidate.
|
|
24
|
+
*
|
|
25
|
+
* TODO: Support Rotation before Expiry
|
|
26
|
+
*
|
|
27
|
+
* Dataflow can come from two directions:
|
|
28
|
+
* 1. Login - Bottom Up
|
|
29
|
+
* This happens on login, when a token is passed up through the setSession
|
|
30
|
+
* callback. From here it needs to be stored, and shared for usage.
|
|
31
|
+
* 2. Refresh - Top Down
|
|
32
|
+
* This happens when a page is reloaded or a person returns, and everything is
|
|
33
|
+
* reinitialized.
|
|
34
|
+
*/
|
|
35
|
+
export const useSession = (storageKey = 'session'): [Maybe<QuilttJWT> | undefined, SetSession] => {
|
|
36
|
+
const [token, setToken] = useStorage<string>(storageKey)
|
|
37
|
+
const session = useMemo(() => parse(token), [token])
|
|
38
|
+
|
|
39
|
+
// Clear session if/when it expires
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!session) return
|
|
42
|
+
|
|
43
|
+
const expirationMS = session.claims.exp * 1000
|
|
44
|
+
const expire = () => setToken(null)
|
|
45
|
+
|
|
46
|
+
if (Date.now() >= expirationMS) {
|
|
47
|
+
expire()
|
|
48
|
+
} else {
|
|
49
|
+
sessionTimer.set(expire, expirationMS - Date.now())
|
|
50
|
+
return () => sessionTimer.clear(expire)
|
|
51
|
+
}
|
|
52
|
+
}, [session, setToken])
|
|
53
|
+
|
|
54
|
+
// Bubbles up from Login
|
|
55
|
+
const setSession = useCallback(
|
|
56
|
+
(nextState: Maybe<string> | SetStateAction<Maybe<string> | undefined> | undefined) => {
|
|
57
|
+
const newState = nextState instanceof Function ? nextState(token) : nextState
|
|
58
|
+
|
|
59
|
+
if (token !== newState && (!newState || parse(newState))) {
|
|
60
|
+
setToken(newState)
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
[token, setToken]
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return [session, setSession]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default useSession
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { Dispatch, SetStateAction } from 'react'
|
|
4
|
+
import { useCallback, useEffect, useState } from 'react'
|
|
5
|
+
|
|
6
|
+
import type { Maybe } from '@quiltt/core'
|
|
7
|
+
import { GlobalStorage } from '@quiltt/core'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Attempt to persist state with local storage, so it remains after refresh and
|
|
11
|
+
* across open documents. Falls back to in memory storage when localStorage is
|
|
12
|
+
* unavailable.
|
|
13
|
+
*
|
|
14
|
+
* This hook is used in the same way as useState except that you must pass the
|
|
15
|
+
* storage key in the 1st parameter. If the window object is not present (as in SSR),
|
|
16
|
+
* useStorage() will return the default nextState.
|
|
17
|
+
*
|
|
18
|
+
* Expect values to remain in sync
|
|
19
|
+
* Across Hooks
|
|
20
|
+
* Across Reloads
|
|
21
|
+
* Across Windows (Documents)
|
|
22
|
+
*
|
|
23
|
+
* @param key
|
|
24
|
+
* @param initialState
|
|
25
|
+
* @returns {Array} [storage, setStorage]
|
|
26
|
+
*/
|
|
27
|
+
export const useStorage = <T>(
|
|
28
|
+
key: string,
|
|
29
|
+
initialState?: Maybe<T>
|
|
30
|
+
): [Maybe<T> | undefined, Dispatch<SetStateAction<Maybe<T> | undefined>>] => {
|
|
31
|
+
const getStorage = useCallback(() => {
|
|
32
|
+
let state
|
|
33
|
+
|
|
34
|
+
if ((state = GlobalStorage.get(key)) !== undefined) {
|
|
35
|
+
return state
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return initialState
|
|
39
|
+
}, [key, initialState])
|
|
40
|
+
|
|
41
|
+
const [hookState, setHookState] = useState<Maybe<T> | undefined>(getStorage())
|
|
42
|
+
|
|
43
|
+
const setStorage = useCallback(
|
|
44
|
+
(nextState: Maybe<T> | SetStateAction<Maybe<T> | undefined>) => {
|
|
45
|
+
const newState = nextState instanceof Function ? nextState(hookState) : nextState
|
|
46
|
+
|
|
47
|
+
if (hookState !== newState) {
|
|
48
|
+
GlobalStorage.set(key, newState)
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
[key, hookState]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The empty dependency array ensures that the effect runs only once when the component mounts
|
|
56
|
+
* and doesn't re-run unnecessarily on subsequent renders because it doesn't depend on any
|
|
57
|
+
* props or state variables that could change during the component's lifetime.
|
|
58
|
+
*
|
|
59
|
+
* Use an empty dependency array to avoid unnecessary re-renders.
|
|
60
|
+
*/
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
GlobalStorage.subscribe(key, setHookState)
|
|
63
|
+
|
|
64
|
+
setHookState(getStorage())
|
|
65
|
+
|
|
66
|
+
return () => GlobalStorage.unsubscribe(key, setHookState)
|
|
67
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
68
|
+
}, [])
|
|
69
|
+
|
|
70
|
+
return [hookState, setStorage]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default useStorage
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { FC, PropsWithChildren } from 'react'
|
|
4
|
+
import { useEffect } from 'react'
|
|
5
|
+
|
|
6
|
+
import { ApolloProvider } from '@apollo/client'
|
|
7
|
+
import { InMemoryCache, QuilttClient } from '@quiltt/core'
|
|
8
|
+
import { useQuilttSession } from '../hooks'
|
|
9
|
+
|
|
10
|
+
type QuilttAuthProviderProps = PropsWithChildren & {
|
|
11
|
+
token?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const Client = new QuilttClient({
|
|
15
|
+
cache: new InMemoryCache(),
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* If a token is provided, will validate the token against the api and then import
|
|
20
|
+
* it into trusted storage. While this process is happening, the component is put
|
|
21
|
+
* into a loading state and the children are not rendered to prevent race conditions
|
|
22
|
+
* from triggering within the transitionary state.
|
|
23
|
+
*
|
|
24
|
+
*/
|
|
25
|
+
export const QuilttAuthProvider: FC<QuilttAuthProviderProps> = ({ token, children }) => {
|
|
26
|
+
const { session, importSession } = useQuilttSession()
|
|
27
|
+
|
|
28
|
+
// Import Passed in Tokens
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (token) importSession(token)
|
|
31
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
32
|
+
}, [token])
|
|
33
|
+
|
|
34
|
+
// Reset Client Store when logging in or out
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
Client.resetStore()
|
|
37
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
38
|
+
}, [session])
|
|
39
|
+
|
|
40
|
+
return <ApolloProvider client={Client}>{children}</ApolloProvider>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default QuilttAuthProvider
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { FC, PropsWithChildren } from 'react'
|
|
2
|
+
|
|
3
|
+
import { QuilttAuthProvider } from './QuilttAuthProvider'
|
|
4
|
+
import { QuilttSettingsProvider } from './QuilttSettingsProvider'
|
|
5
|
+
|
|
6
|
+
type QuilttProviderProps = PropsWithChildren & {
|
|
7
|
+
clientId?: string
|
|
8
|
+
token?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const QuilttProvider: FC<QuilttProviderProps> = ({ clientId, token, children }) => {
|
|
12
|
+
return (
|
|
13
|
+
<QuilttSettingsProvider clientId={clientId}>
|
|
14
|
+
<QuilttAuthProvider token={token}>{children}</QuilttAuthProvider>
|
|
15
|
+
</QuilttSettingsProvider>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default QuilttProvider
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { FC, PropsWithChildren } from 'react'
|
|
4
|
+
import { useState } from 'react'
|
|
5
|
+
|
|
6
|
+
import { QuilttSettings } from '../hooks'
|
|
7
|
+
|
|
8
|
+
type QuilttSettingsProviderProps = PropsWithChildren & {
|
|
9
|
+
clientId?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const QuilttSettingsProvider: FC<QuilttSettingsProviderProps> = ({ clientId, children }) => {
|
|
13
|
+
const [_clientId] = useState(clientId)
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<QuilttSettings.Provider value={{ clientId: _clientId }}>{children}</QuilttSettings.Provider>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default QuilttSettingsProvider
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Component, ComponentType, FC } from 'react'
|
|
2
|
+
|
|
3
|
+
export type AnyTag = string | FC<any> | (new (props: any) => Component)
|
|
4
|
+
|
|
5
|
+
export type PropsOf<Tag> = Tag extends keyof JSX.IntrinsicElements
|
|
6
|
+
? JSX.IntrinsicElements[Tag]
|
|
7
|
+
: Tag extends ComponentType<infer Props>
|
|
8
|
+
? Props & JSX.IntrinsicAttributes
|
|
9
|
+
: never
|