@xyo-network/react-access-gate 4.0.2 → 4.0.4
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/dist/browser/hooks/index.d.ts +2 -0
- package/dist/browser/hooks/index.d.ts.map +1 -0
- package/dist/browser/hooks/useAccessCodes.d.ts +7 -0
- package/dist/browser/hooks/useAccessCodes.d.ts.map +1 -0
- package/dist/browser/index.d.ts +1 -0
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.mjs +36 -1
- package/dist/browser/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/components/AccessCodeGateFlexbox.stories.tsx +13 -10
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAccessCodes.ts +32 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAccessCodes.d.ts","sourceRoot":"","sources":["../../../src/hooks/useAccessCodes.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,oBAAqB,MAAM;;;;+BAclB,MAAM;CAezC,CAAA"}
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA"}
|
package/dist/browser/index.mjs
CHANGED
|
@@ -113,7 +113,42 @@ var AccessCodeGateFlexbox = /* @__PURE__ */ __name(({ children, onAccessCodeSucc
|
|
|
113
113
|
variant: "contained"
|
|
114
114
|
}, "Enter")))) : null);
|
|
115
115
|
}, "AccessCodeGateFlexbox");
|
|
116
|
+
|
|
117
|
+
// src/hooks/useAccessCodes.ts
|
|
118
|
+
import { useMemo, useState as useState2 } from "react";
|
|
119
|
+
var useAccessCodes = /* @__PURE__ */ __name((localStorageKey) => {
|
|
120
|
+
const [validated, setValidated] = useState2(false);
|
|
121
|
+
const [codeInput, setCodeInput] = useState2("");
|
|
122
|
+
const onAccessCodeSuccess = /* @__PURE__ */ __name(() => {
|
|
123
|
+
if (codeInput) {
|
|
124
|
+
localStorage.setItem(localStorageKey, JSON.stringify([
|
|
125
|
+
codeInput
|
|
126
|
+
]));
|
|
127
|
+
setValidated(true);
|
|
128
|
+
} else {
|
|
129
|
+
setValidated(true);
|
|
130
|
+
}
|
|
131
|
+
}, "onAccessCodeSuccess");
|
|
132
|
+
const onCodeInputChange = /* @__PURE__ */ __name((code) => {
|
|
133
|
+
setCodeInput(code ?? "");
|
|
134
|
+
return code?.length === 6;
|
|
135
|
+
}, "onCodeInputChange");
|
|
136
|
+
const userAccessCodes = useMemo(() => {
|
|
137
|
+
const storedCodes = localStorage.getItem(localStorageKey);
|
|
138
|
+
if (storedCodes) {
|
|
139
|
+
const parsedResult = JSON.parse(storedCodes ?? "");
|
|
140
|
+
if (Array.isArray(parsedResult)) return parsedResult;
|
|
141
|
+
}
|
|
142
|
+
}, []);
|
|
143
|
+
return {
|
|
144
|
+
validated,
|
|
145
|
+
userAccessCodes,
|
|
146
|
+
onAccessCodeSuccess,
|
|
147
|
+
onCodeInputChange
|
|
148
|
+
};
|
|
149
|
+
}, "useAccessCodes");
|
|
116
150
|
export {
|
|
117
|
-
AccessCodeGateFlexbox
|
|
151
|
+
AccessCodeGateFlexbox,
|
|
152
|
+
useAccessCodes
|
|
118
153
|
};
|
|
119
154
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/AccessCodeGateFlexbox.tsx","../../src/components/CodeTextField.tsx"],"sourcesContent":["/* eslint-disable @eslint-react/hooks-extra/no-direct-set-state-in-use-effect */\nimport { FormControl } from '@mui/material'\nimport { ButtonEx } from '@xylabs/react-button'\nimport type { FlexBoxProps } from '@xylabs/react-flexbox'\nimport { FlexGrowCol, FlexGrowRow } from '@xylabs/react-flexbox'\nimport type { WithChildren } from '@xylabs/react-shared'\nimport React, {\n useCallback, useEffect, useState,\n} from 'react'\n\nimport { CodeTextField } from './CodeTextField.tsx'\n\nexport interface AccessCodeGateFlexbox extends WithChildren, FlexBoxProps {\n onAccessCodeSuccess?: (code?: string) => void\n successRedirectDelay?: number\n textFieldHelperText?: string\n userAccessCodes?: string[]\n validAccessCodes?: string[]\n validateFunction?: (codeInput?: string) => boolean\n}\n\nexport const AccessCodeGateFlexbox: React.FC<AccessCodeGateFlexbox> = ({\n children,\n onAccessCodeSuccess,\n successRedirectDelay = 1500,\n userAccessCodes,\n validAccessCodes,\n validateFunction,\n ...props\n}) => {\n const [initialized, setInitialized] = useState(false)\n const [accessGranted, setAccessGranted] = useState(false)\n const [codeInput, setCodeInput] = useState<string>()\n const [validCode, setValidCode] = useState<boolean | null>(null)\n\n const disabled = validateFunction ? !validateFunction(codeInput) : !codeInput\n const validateCode = useCallback((accessCode: string) => (accessCode ? validAccessCodes?.includes(accessCode) : false), [validAccessCodes])\n\n const onEnter = () => {\n if (codeInput) {\n const granted = validateCode(codeInput)\n if (granted) {\n setValidCode(true)\n // delay success callback to ensure the ui shows success before next action\n setTimeout(() => {\n setAccessGranted(granted)\n onAccessCodeSuccess?.(codeInput)\n }, successRedirectDelay)\n } else {\n setValidCode(false)\n }\n }\n }\n\n useEffect(() => {\n // whenever a code changes, reset the success/failure warning\n setValidCode(null)\n }, [codeInput])\n\n useEffect(() => {\n if (userAccessCodes) {\n const granted = userAccessCodes.some(code => validateCode(code))\n setAccessGranted(granted)\n if (granted) {\n onAccessCodeSuccess?.()\n }\n }\n setInitialized(true)\n }, [onAccessCodeSuccess, userAccessCodes, validateCode])\n\n return (\n <>\n {initialized\n ? accessGranted\n ? children\n : (\n <FlexGrowCol gap={2} {...props}>\n <FlexGrowRow gap={2} alignItems=\"start\">\n <FormControl>\n <CodeTextField\n codeInput={codeInput}\n disabled={disabled}\n label=\"Enter Access Code\"\n setCodeInput={setCodeInput}\n validCode={validCode}\n onEnter={onEnter}\n />\n </FormControl>\n <FormControl>\n <ButtonEx disabled={disabled} onClick={onEnter} variant=\"contained\">\n Enter\n </ButtonEx>\n </FormControl>\n </FlexGrowRow>\n </FlexGrowCol>\n )\n\n : null}\n </>\n )\n}\n","import { CheckCircleOutline, ErrorOutline } from '@mui/icons-material'\nimport type { TextFieldProps } from '@mui/material'\nimport {\n InputAdornment, styled, TextField,\n} from '@mui/material'\nimport type { Dispatch, SetStateAction } from 'react'\nimport React from 'react'\n\nexport type CodeTextFieldProps = TextFieldProps & {\n codeInput?: string\n disabled?: boolean\n onEnter?: () => void\n setCodeInput?: Dispatch<SetStateAction<string | undefined>>\n validCode?: boolean | null\n}\n\nexport const CodeTextField: React.FC<CodeTextFieldProps> = ({\n codeInput, disabled, onEnter, setCodeInput, validCode, ...props\n}) => (\n <StyledTextField\n InputProps={{\n endAdornment: (\n <InputAdornment position=\"start\">\n {/* Having a display block element for all 3 states (null, false, true) means the icon coming in and out\n does not affect the overall width */}\n <CheckCircleOutline sx={{ display: validCode === null ? 'block' : 'hidden', visibility: 'hidden' }} />\n <CheckCircleOutline\n color=\"success\"\n fontSize=\"medium\"\n sx={{ position: 'absolute', visibility: validCode === true ? 'visible' : 'hidden' }}\n />\n <ErrorOutline color=\"error\" fontSize=\"medium\" sx={{ position: 'absolute', visibility: validCode === false ? 'visible' : 'hidden' }} />\n </InputAdornment>\n ),\n }}\n onKeyUp={event => (event.key === 'Enter' && !disabled ? onEnter?.() : null)}\n autoFocus\n size=\"small\"\n value={codeInput ?? ''}\n onChange={event => setCodeInput?.(event.target.value)}\n {...props}\n />\n)\n\nconst StyledTextField = styled(TextField, { name: 'StyledTextField' })(() => ({ '& .MuiInputBase-root': { paddingRight: 0 } }))\n"],"mappings":"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","names":["FormControl","ButtonEx","FlexGrowCol","FlexGrowRow","React","useCallback","useEffect","useState","CheckCircleOutline","ErrorOutline","InputAdornment","styled","TextField","React","CodeTextField","codeInput","disabled","onEnter","setCodeInput","validCode","props","StyledTextField","InputProps","endAdornment","InputAdornment","position","CheckCircleOutline","sx","display","visibility","color","fontSize","ErrorOutline","onKeyUp","event","key","autoFocus","size","value","onChange","target","styled","TextField","name","paddingRight","AccessCodeGateFlexbox","children","onAccessCodeSuccess","successRedirectDelay","userAccessCodes","validAccessCodes","validateFunction","props","initialized","setInitialized","useState","accessGranted","setAccessGranted","codeInput","setCodeInput","validCode","setValidCode","disabled","validateCode","useCallback","accessCode","includes","onEnter","granted","setTimeout","useEffect","some","code","React","FlexGrowCol","gap","FlexGrowRow","alignItems","FormControl","CodeTextField","label","ButtonEx","onClick","variant"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/AccessCodeGateFlexbox.tsx","../../src/components/CodeTextField.tsx","../../src/hooks/useAccessCodes.ts"],"sourcesContent":["/* eslint-disable @eslint-react/hooks-extra/no-direct-set-state-in-use-effect */\nimport { FormControl } from '@mui/material'\nimport { ButtonEx } from '@xylabs/react-button'\nimport type { FlexBoxProps } from '@xylabs/react-flexbox'\nimport { FlexGrowCol, FlexGrowRow } from '@xylabs/react-flexbox'\nimport type { WithChildren } from '@xylabs/react-shared'\nimport React, {\n useCallback, useEffect, useState,\n} from 'react'\n\nimport { CodeTextField } from './CodeTextField.tsx'\n\nexport interface AccessCodeGateFlexbox extends WithChildren, FlexBoxProps {\n onAccessCodeSuccess?: (code?: string) => void\n successRedirectDelay?: number\n textFieldHelperText?: string\n userAccessCodes?: string[]\n validAccessCodes?: string[]\n validateFunction?: (codeInput?: string) => boolean\n}\n\nexport const AccessCodeGateFlexbox: React.FC<AccessCodeGateFlexbox> = ({\n children,\n onAccessCodeSuccess,\n successRedirectDelay = 1500,\n userAccessCodes,\n validAccessCodes,\n validateFunction,\n ...props\n}) => {\n const [initialized, setInitialized] = useState(false)\n const [accessGranted, setAccessGranted] = useState(false)\n const [codeInput, setCodeInput] = useState<string>()\n const [validCode, setValidCode] = useState<boolean | null>(null)\n\n const disabled = validateFunction ? !validateFunction(codeInput) : !codeInput\n const validateCode = useCallback((accessCode: string) => (accessCode ? validAccessCodes?.includes(accessCode) : false), [validAccessCodes])\n\n const onEnter = () => {\n if (codeInput) {\n const granted = validateCode(codeInput)\n if (granted) {\n setValidCode(true)\n // delay success callback to ensure the ui shows success before next action\n setTimeout(() => {\n setAccessGranted(granted)\n onAccessCodeSuccess?.(codeInput)\n }, successRedirectDelay)\n } else {\n setValidCode(false)\n }\n }\n }\n\n useEffect(() => {\n // whenever a code changes, reset the success/failure warning\n setValidCode(null)\n }, [codeInput])\n\n useEffect(() => {\n if (userAccessCodes) {\n const granted = userAccessCodes.some(code => validateCode(code))\n setAccessGranted(granted)\n if (granted) {\n onAccessCodeSuccess?.()\n }\n }\n setInitialized(true)\n }, [onAccessCodeSuccess, userAccessCodes, validateCode])\n\n return (\n <>\n {initialized\n ? accessGranted\n ? children\n : (\n <FlexGrowCol gap={2} {...props}>\n <FlexGrowRow gap={2} alignItems=\"start\">\n <FormControl>\n <CodeTextField\n codeInput={codeInput}\n disabled={disabled}\n label=\"Enter Access Code\"\n setCodeInput={setCodeInput}\n validCode={validCode}\n onEnter={onEnter}\n />\n </FormControl>\n <FormControl>\n <ButtonEx disabled={disabled} onClick={onEnter} variant=\"contained\">\n Enter\n </ButtonEx>\n </FormControl>\n </FlexGrowRow>\n </FlexGrowCol>\n )\n\n : null}\n </>\n )\n}\n","import { CheckCircleOutline, ErrorOutline } from '@mui/icons-material'\nimport type { TextFieldProps } from '@mui/material'\nimport {\n InputAdornment, styled, TextField,\n} from '@mui/material'\nimport type { Dispatch, SetStateAction } from 'react'\nimport React from 'react'\n\nexport type CodeTextFieldProps = TextFieldProps & {\n codeInput?: string\n disabled?: boolean\n onEnter?: () => void\n setCodeInput?: Dispatch<SetStateAction<string | undefined>>\n validCode?: boolean | null\n}\n\nexport const CodeTextField: React.FC<CodeTextFieldProps> = ({\n codeInput, disabled, onEnter, setCodeInput, validCode, ...props\n}) => (\n <StyledTextField\n InputProps={{\n endAdornment: (\n <InputAdornment position=\"start\">\n {/* Having a display block element for all 3 states (null, false, true) means the icon coming in and out\n does not affect the overall width */}\n <CheckCircleOutline sx={{ display: validCode === null ? 'block' : 'hidden', visibility: 'hidden' }} />\n <CheckCircleOutline\n color=\"success\"\n fontSize=\"medium\"\n sx={{ position: 'absolute', visibility: validCode === true ? 'visible' : 'hidden' }}\n />\n <ErrorOutline color=\"error\" fontSize=\"medium\" sx={{ position: 'absolute', visibility: validCode === false ? 'visible' : 'hidden' }} />\n </InputAdornment>\n ),\n }}\n onKeyUp={event => (event.key === 'Enter' && !disabled ? onEnter?.() : null)}\n autoFocus\n size=\"small\"\n value={codeInput ?? ''}\n onChange={event => setCodeInput?.(event.target.value)}\n {...props}\n />\n)\n\nconst StyledTextField = styled(TextField, { name: 'StyledTextField' })(() => ({ '& .MuiInputBase-root': { paddingRight: 0 } }))\n","import { useMemo, useState } from 'react'\n\nexport const useAccessCodes = (localStorageKey: string) => {\n const [validated, setValidated] = useState(false)\n const [codeInput, setCodeInput] = useState('')\n\n const onAccessCodeSuccess = () => {\n // Save the access code to local storage\n if (codeInput) {\n localStorage.setItem(localStorageKey, JSON.stringify([codeInput]))\n setValidated(true)\n } else {\n // If the codeInput is empty, but we have success, do nothing since the successful code is already saved\n setValidated(true)\n }\n }\n const onCodeInputChange = (code?: string) => {\n setCodeInput(code ?? '')\n return code?.length === 6\n }\n const userAccessCodes = useMemo(() => {\n const storedCodes = localStorage.getItem(localStorageKey)\n if (storedCodes) {\n const parsedResult = JSON.parse(storedCodes ?? '')\n if (Array.isArray(parsedResult)) return parsedResult\n }\n }, [])\n\n return {\n validated, userAccessCodes, onAccessCodeSuccess, onCodeInputChange,\n }\n}\n"],"mappings":"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","names":["FormControl","ButtonEx","FlexGrowCol","FlexGrowRow","React","useCallback","useEffect","useState","CheckCircleOutline","ErrorOutline","InputAdornment","styled","TextField","React","CodeTextField","codeInput","disabled","onEnter","setCodeInput","validCode","props","StyledTextField","InputProps","endAdornment","InputAdornment","position","CheckCircleOutline","sx","display","visibility","color","fontSize","ErrorOutline","onKeyUp","event","key","autoFocus","size","value","onChange","target","styled","TextField","name","paddingRight","AccessCodeGateFlexbox","children","onAccessCodeSuccess","successRedirectDelay","userAccessCodes","validAccessCodes","validateFunction","props","initialized","setInitialized","useState","accessGranted","setAccessGranted","codeInput","setCodeInput","validCode","setValidCode","disabled","validateCode","useCallback","accessCode","includes","onEnter","granted","setTimeout","useEffect","some","code","React","FlexGrowCol","gap","FlexGrowRow","alignItems","FormControl","CodeTextField","label","ButtonEx","onClick","variant","useMemo","useState","useAccessCodes","localStorageKey","validated","setValidated","useState","codeInput","setCodeInput","onAccessCodeSuccess","localStorage","setItem","JSON","stringify","onCodeInputChange","code","length","userAccessCodes","useMemo","storedCodes","getItem","parsedResult","parse","Array","isArray"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xyo-network/react-access-gate",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.4",
|
|
4
4
|
"description": "Common React library for all XYO projects that use React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"xyo",
|
|
@@ -43,20 +43,20 @@
|
|
|
43
43
|
"lint-pkg": "npmPkgJsonLint ."
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@mui/icons-material": "^6.1.
|
|
46
|
+
"@mui/icons-material": "^6.1.2",
|
|
47
47
|
"@xylabs/react-button": "^5.0.1",
|
|
48
48
|
"@xylabs/react-flexbox": "^5.0.1",
|
|
49
49
|
"@xylabs/react-shared": "^5.0.1"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@mui/material": "^6.1.
|
|
53
|
-
"@mui/styles": "^6.1.
|
|
54
|
-
"@storybook/react": "^8.3.
|
|
52
|
+
"@mui/material": "^6.1.2",
|
|
53
|
+
"@mui/styles": "^6.1.2",
|
|
54
|
+
"@storybook/react": "^8.3.5",
|
|
55
55
|
"@xylabs/ts-scripts-yarn3": "^4.0.7",
|
|
56
56
|
"@xylabs/tsconfig-react": "^4.0.7",
|
|
57
57
|
"react": "^18.3.1",
|
|
58
58
|
"react-dom": "^18.3.1",
|
|
59
|
-
"storybook": "^8.3.
|
|
59
|
+
"storybook": "^8.3.5",
|
|
60
60
|
"typescript": "^5.6.2"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { Alert, Typography } from '@mui/material'
|
|
2
2
|
import type { Meta, StoryFn } from '@storybook/react'
|
|
3
3
|
import { FlexCol } from '@xylabs/react-flexbox'
|
|
4
|
-
import React
|
|
4
|
+
import React from 'react'
|
|
5
5
|
|
|
6
|
+
import { useAccessCodes } from '../hooks/index.ts'
|
|
6
7
|
import { AccessCodeGateFlexbox } from './AccessCodeGateFlexbox.tsx'
|
|
7
8
|
|
|
9
|
+
const ValidAccessCodes = ['100519']
|
|
10
|
+
|
|
8
11
|
export default {
|
|
9
12
|
component: AccessCodeGateFlexbox,
|
|
10
13
|
title: 'access/AccessCodeGateFlexbox',
|
|
@@ -15,12 +18,9 @@ const Template: StoryFn<typeof AccessCodeGateFlexbox> = args => (
|
|
|
15
18
|
)
|
|
16
19
|
|
|
17
20
|
const TemplateWithAccessCodes: StoryFn<typeof AccessCodeGateFlexbox> = (args) => {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
setValidated(true)
|
|
22
|
-
}
|
|
23
|
-
const validateFunction = (code?: string) => code?.length === 6
|
|
21
|
+
const {
|
|
22
|
+
validated, onAccessCodeSuccess, onCodeInputChange,
|
|
23
|
+
} = useAccessCodes('storybook-access-codes-test')
|
|
24
24
|
|
|
25
25
|
return validated
|
|
26
26
|
? <Alert severity="success">Success!</Alert>
|
|
@@ -28,11 +28,14 @@ const TemplateWithAccessCodes: StoryFn<typeof AccessCodeGateFlexbox> = (args) =>
|
|
|
28
28
|
<FlexCol gap={2}>
|
|
29
29
|
<AccessCodeGateFlexbox
|
|
30
30
|
onAccessCodeSuccess={onAccessCodeSuccess}
|
|
31
|
-
validAccessCodes={
|
|
32
|
-
validateFunction={
|
|
31
|
+
validAccessCodes={ValidAccessCodes}
|
|
32
|
+
validateFunction={onCodeInputChange}
|
|
33
33
|
{...args}
|
|
34
34
|
/>
|
|
35
|
-
<Typography variant="caption">
|
|
35
|
+
<Typography variant="caption">
|
|
36
|
+
Hint:
|
|
37
|
+
{ValidAccessCodes[0]}
|
|
38
|
+
</Typography>
|
|
36
39
|
</FlexCol>
|
|
37
40
|
)
|
|
38
41
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useAccessCodes.ts'
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useMemo, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export const useAccessCodes = (localStorageKey: string) => {
|
|
4
|
+
const [validated, setValidated] = useState(false)
|
|
5
|
+
const [codeInput, setCodeInput] = useState('')
|
|
6
|
+
|
|
7
|
+
const onAccessCodeSuccess = () => {
|
|
8
|
+
// Save the access code to local storage
|
|
9
|
+
if (codeInput) {
|
|
10
|
+
localStorage.setItem(localStorageKey, JSON.stringify([codeInput]))
|
|
11
|
+
setValidated(true)
|
|
12
|
+
} else {
|
|
13
|
+
// If the codeInput is empty, but we have success, do nothing since the successful code is already saved
|
|
14
|
+
setValidated(true)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const onCodeInputChange = (code?: string) => {
|
|
18
|
+
setCodeInput(code ?? '')
|
|
19
|
+
return code?.length === 6
|
|
20
|
+
}
|
|
21
|
+
const userAccessCodes = useMemo(() => {
|
|
22
|
+
const storedCodes = localStorage.getItem(localStorageKey)
|
|
23
|
+
if (storedCodes) {
|
|
24
|
+
const parsedResult = JSON.parse(storedCodes ?? '')
|
|
25
|
+
if (Array.isArray(parsedResult)) return parsedResult
|
|
26
|
+
}
|
|
27
|
+
}, [])
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
validated, userAccessCodes, onAccessCodeSuccess, onCodeInputChange,
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.ts
CHANGED