@xyo-network/react-access-gate 7.5.8 → 7.5.12
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/components/AccessCodeGateFlexbox.d.ts.map +1 -1
- package/dist/browser/index.mjs +87 -97
- package/dist/browser/index.mjs.map +1 -1
- package/package.json +56 -28
- package/src/components/AccessCodeGateFlexbox.stories.tsx +0 -51
- package/src/components/AccessCodeGateFlexbox.tsx +0 -110
- package/src/components/CodeTextField.tsx +0 -47
- package/src/components/index.ts +0 -1
- package/src/hooks/index.ts +0 -1
- package/src/hooks/useAccessCodes.ts +0 -34
- package/src/index.ts +0 -2
- package/src/types/images.d.ts +0 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccessCodeGateFlexbox.d.ts","sourceRoot":"","sources":["../../../src/components/AccessCodeGateFlexbox.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAA;AAC9C,OAAO,KAEN,MAAM,OAAO,CAAA;AAId,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB,EAAE,YAAY;IAC5E,mBAAmB,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7C,iBAAiB,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,gBAAgB,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;CACnD;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,
|
|
1
|
+
{"version":3,"file":"AccessCodeGateFlexbox.d.ts","sourceRoot":"","sources":["../../../src/components/AccessCodeGateFlexbox.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAA;AAC9C,OAAO,KAEN,MAAM,OAAO,CAAA;AAId,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB,EAAE,YAAY;IAC5E,mBAAmB,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7C,iBAAiB,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,gBAAgB,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;CACnD;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA4FjE,CAAA"}
|
package/dist/browser/index.mjs
CHANGED
|
@@ -1,77 +1,81 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
-
|
|
4
1
|
// src/components/AccessCodeGateFlexbox.tsx
|
|
5
2
|
import { FormControl } from "@mui/material";
|
|
6
3
|
import { ButtonEx } from "@xylabs/react-button";
|
|
7
4
|
import { FlexGrowCol, FlexGrowRow } from "@xylabs/react-flexbox";
|
|
8
|
-
import
|
|
5
|
+
import {
|
|
6
|
+
useCallback,
|
|
7
|
+
useEffect,
|
|
8
|
+
useState
|
|
9
|
+
} from "react";
|
|
9
10
|
|
|
10
11
|
// src/components/CodeTextField.tsx
|
|
11
12
|
import { CheckCircleOutline, ErrorOutline } from "@mui/icons-material";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}), /* @__PURE__ */ React.createElement(CheckCircleOutline, {
|
|
25
|
-
color: "success",
|
|
26
|
-
fontSize: "medium",
|
|
27
|
-
sx: {
|
|
28
|
-
position: "absolute",
|
|
29
|
-
visibility: validCode === true ? "visible" : "hidden"
|
|
30
|
-
}
|
|
31
|
-
}), /* @__PURE__ */ React.createElement(ErrorOutline, {
|
|
32
|
-
color: "error",
|
|
33
|
-
fontSize: "medium",
|
|
34
|
-
sx: {
|
|
35
|
-
position: "absolute",
|
|
36
|
-
visibility: validCode === false ? "visible" : "hidden"
|
|
37
|
-
}
|
|
38
|
-
}))
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
onKeyUp: /* @__PURE__ */ __name((event) => event.key === "Enter" && !disabled ? onEnter?.() : null, "onKeyUp"),
|
|
42
|
-
autoFocus: true,
|
|
43
|
-
size: "small",
|
|
44
|
-
value: codeInput ?? "",
|
|
45
|
-
onChange: /* @__PURE__ */ __name((event) => setCodeInput?.(event.target.value), "onChange"),
|
|
13
|
+
import {
|
|
14
|
+
InputAdornment,
|
|
15
|
+
styled,
|
|
16
|
+
TextField
|
|
17
|
+
} from "@mui/material";
|
|
18
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
19
|
+
var CodeTextField = ({
|
|
20
|
+
codeInput,
|
|
21
|
+
disabled,
|
|
22
|
+
onEnter,
|
|
23
|
+
setCodeInput,
|
|
24
|
+
validCode,
|
|
46
25
|
...props
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
26
|
+
}) => /* @__PURE__ */ jsx(
|
|
27
|
+
StyledTextField,
|
|
28
|
+
{
|
|
29
|
+
slotProps: {
|
|
30
|
+
input: {
|
|
31
|
+
endAdornment: /* @__PURE__ */ jsxs(InputAdornment, { position: "start", children: [
|
|
32
|
+
/* @__PURE__ */ jsx(CheckCircleOutline, { sx: { display: validCode === null ? "block" : "hidden", visibility: "hidden" } }),
|
|
33
|
+
/* @__PURE__ */ jsx(
|
|
34
|
+
CheckCircleOutline,
|
|
35
|
+
{
|
|
36
|
+
color: "success",
|
|
37
|
+
fontSize: "medium",
|
|
38
|
+
sx: { position: "absolute", visibility: validCode === true ? "visible" : "hidden" }
|
|
39
|
+
}
|
|
40
|
+
),
|
|
41
|
+
/* @__PURE__ */ jsx(ErrorOutline, { color: "error", fontSize: "medium", sx: { position: "absolute", visibility: validCode === false ? "visible" : "hidden" } })
|
|
42
|
+
] })
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
onKeyUp: (event) => event.key === "Enter" && !disabled ? onEnter?.() : null,
|
|
46
|
+
autoFocus: true,
|
|
47
|
+
size: "small",
|
|
48
|
+
value: codeInput ?? "",
|
|
49
|
+
onChange: (event) => setCodeInput?.(event.target.value),
|
|
50
|
+
...props
|
|
53
51
|
}
|
|
54
|
-
|
|
52
|
+
);
|
|
53
|
+
var StyledTextField = styled(TextField, { name: "StyledTextField" })(() => ({ "& .MuiInputBase-root": { paddingRight: 0 } }));
|
|
55
54
|
|
|
56
55
|
// src/components/AccessCodeGateFlexbox.tsx
|
|
57
|
-
|
|
56
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
57
|
+
var AccessCodeGateFlexbox = ({
|
|
58
|
+
children,
|
|
59
|
+
onCodeInputChange,
|
|
60
|
+
onAccessCodeSuccess,
|
|
61
|
+
successRedirectDelay = 1500,
|
|
62
|
+
userAccessCodes,
|
|
63
|
+
validAccessCodes,
|
|
64
|
+
validateFunction,
|
|
65
|
+
...props
|
|
66
|
+
}) => {
|
|
58
67
|
const [initialized, setInitialized] = useState(false);
|
|
59
68
|
const [accessGranted, setAccessGranted] = useState(false);
|
|
60
69
|
const [codeInput, setCodeInput] = useState();
|
|
61
70
|
const [validCode, setValidCode] = useState(null);
|
|
62
71
|
const disabled = validateFunction ? !validateFunction(codeInput) : !codeInput;
|
|
63
|
-
const validateCode = useCallback((accessCode) => accessCode ? validAccessCodes?.includes(accessCode) : false, [
|
|
64
|
-
validAccessCodes
|
|
65
|
-
]);
|
|
72
|
+
const validateCode = useCallback((accessCode) => accessCode ? validAccessCodes?.includes(accessCode) : false, [validAccessCodes]);
|
|
66
73
|
useEffect(() => {
|
|
67
74
|
if (onCodeInputChange) {
|
|
68
75
|
onCodeInputChange(codeInput);
|
|
69
76
|
}
|
|
70
|
-
}, [
|
|
71
|
-
|
|
72
|
-
onCodeInputChange
|
|
73
|
-
]);
|
|
74
|
-
const onEnter = /* @__PURE__ */ __name(() => {
|
|
77
|
+
}, [codeInput, onCodeInputChange]);
|
|
78
|
+
const onEnter = () => {
|
|
75
79
|
onCodeInputChange?.(codeInput);
|
|
76
80
|
if (codeInput) {
|
|
77
81
|
const granted = validateCode(codeInput);
|
|
@@ -85,76 +89,62 @@ var AccessCodeGateFlexbox = /* @__PURE__ */ __name(({ children, onCodeInputChang
|
|
|
85
89
|
setValidCode(false);
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
|
-
}
|
|
92
|
+
};
|
|
89
93
|
useEffect(() => {
|
|
90
94
|
setValidCode(null);
|
|
91
|
-
}, [
|
|
92
|
-
codeInput
|
|
93
|
-
]);
|
|
95
|
+
}, [codeInput]);
|
|
94
96
|
useEffect(() => {
|
|
95
97
|
if (userAccessCodes) {
|
|
96
98
|
const granted = userAccessCodes.some((code) => validateCode(code));
|
|
97
|
-
setAccessGranted(granted);
|
|
98
99
|
if (granted) {
|
|
100
|
+
setAccessGranted(true);
|
|
99
101
|
onAccessCodeSuccess?.();
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
104
|
setInitialized(true);
|
|
103
|
-
}, [
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
disabled,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
validCode,
|
|
120
|
-
onEnter
|
|
121
|
-
})), /* @__PURE__ */ React2.createElement(FormControl, null, /* @__PURE__ */ React2.createElement(ButtonEx, {
|
|
122
|
-
disabled,
|
|
123
|
-
onClick: onEnter,
|
|
124
|
-
variant: "contained"
|
|
125
|
-
}, "Enter")))) : null);
|
|
126
|
-
}, "AccessCodeGateFlexbox");
|
|
105
|
+
}, [userAccessCodes, validateCode]);
|
|
106
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: initialized ? accessGranted ? children : /* @__PURE__ */ jsx2(FlexGrowCol, { gap: 2, ...props, children: /* @__PURE__ */ jsxs2(FlexGrowRow, { gap: 2, alignItems: "start", children: [
|
|
107
|
+
/* @__PURE__ */ jsx2(FormControl, { children: /* @__PURE__ */ jsx2(
|
|
108
|
+
CodeTextField,
|
|
109
|
+
{
|
|
110
|
+
codeInput,
|
|
111
|
+
disabled,
|
|
112
|
+
label: "Enter Access Code",
|
|
113
|
+
setCodeInput,
|
|
114
|
+
validCode,
|
|
115
|
+
onEnter
|
|
116
|
+
}
|
|
117
|
+
) }),
|
|
118
|
+
/* @__PURE__ */ jsx2(FormControl, { children: /* @__PURE__ */ jsx2(ButtonEx, { disabled, onClick: onEnter, variant: "contained", children: "Enter" }) })
|
|
119
|
+
] }) }) : null });
|
|
120
|
+
};
|
|
127
121
|
|
|
128
122
|
// src/hooks/useAccessCodes.ts
|
|
129
123
|
import { useMemo, useState as useState2 } from "react";
|
|
130
|
-
var useAccessCodes =
|
|
124
|
+
var useAccessCodes = (localStorageKey, validCodeLength = 6) => {
|
|
131
125
|
const [validated, setValidated] = useState2(false);
|
|
132
126
|
const [codeInput, setCodeInput] = useState2("");
|
|
133
|
-
const onAccessCodeSuccess =
|
|
127
|
+
const onAccessCodeSuccess = () => {
|
|
134
128
|
if (codeInput) {
|
|
135
|
-
localStorage.setItem(localStorageKey, JSON.stringify([
|
|
136
|
-
codeInput
|
|
137
|
-
]));
|
|
129
|
+
localStorage.setItem(localStorageKey, JSON.stringify([codeInput]));
|
|
138
130
|
setValidated(true);
|
|
139
131
|
} else {
|
|
140
132
|
setValidated(true);
|
|
141
133
|
}
|
|
142
|
-
}
|
|
143
|
-
const validateCodeInput =
|
|
134
|
+
};
|
|
135
|
+
const validateCodeInput = (code) => {
|
|
144
136
|
return code?.length === validCodeLength;
|
|
145
|
-
}
|
|
146
|
-
const onCodeInputChange =
|
|
137
|
+
};
|
|
138
|
+
const onCodeInputChange = (code) => {
|
|
147
139
|
setCodeInput(code ?? "");
|
|
148
|
-
}
|
|
140
|
+
};
|
|
149
141
|
const userAccessCodes = useMemo(() => {
|
|
150
142
|
const storedCodes = localStorage.getItem(localStorageKey);
|
|
151
143
|
if (storedCodes) {
|
|
152
144
|
const parsedResult = JSON.parse(storedCodes ?? "");
|
|
153
145
|
if (Array.isArray(parsedResult)) return parsedResult;
|
|
154
146
|
}
|
|
155
|
-
}, [
|
|
156
|
-
localStorageKey
|
|
157
|
-
]);
|
|
147
|
+
}, [localStorageKey]);
|
|
158
148
|
return {
|
|
159
149
|
codeInput,
|
|
160
150
|
validated,
|
|
@@ -163,7 +153,7 @@ var useAccessCodes = /* @__PURE__ */ __name((localStorageKey, validCodeLength =
|
|
|
163
153
|
onCodeInputChange,
|
|
164
154
|
validateCodeInput
|
|
165
155
|
};
|
|
166
|
-
}
|
|
156
|
+
};
|
|
167
157
|
export {
|
|
168
158
|
AccessCodeGateFlexbox,
|
|
169
159
|
useAccessCodes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/AccessCodeGateFlexbox.tsx","../../src/components/CodeTextField.tsx","../../src/hooks/useAccessCodes.ts"],"sourcesContent":["import { 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 { PropsWithChildren } from 'react'\nimport React, {\n useCallback, useEffect, useState,\n} from 'react'\n\nimport { CodeTextField } from './CodeTextField.tsx'\n\nexport interface AccessCodeGateFlexbox extends PropsWithChildren, FlexBoxProps {\n onAccessCodeSuccess?: (code?: string) => void\n onCodeInputChange?: (codeInput?: 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 onCodeInputChange,\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 // keep the parent informed of the code input\n useEffect(() => {\n if (onCodeInputChange) {\n onCodeInputChange(codeInput)\n }\n }, [codeInput, onCodeInputChange])\n\n const onEnter = () => {\n onCodeInputChange?.(codeInput)\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 { KeyboardEvent } from 'react'\nimport React from 'react'\n\nexport type CodeTextFieldProps = TextFieldProps & {\n codeInput?: string\n disabled?: boolean\n onEnter?: () => void\n setCodeInput?: (code?: string) => void\n validCode?: boolean | null\n}\n\nexport const CodeTextField: React.FC<CodeTextFieldProps> = ({\n codeInput, disabled, onEnter, setCodeInput, validCode, ...props\n}) => (\n <StyledTextField\n slotProps={{\n input: {\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 }}\n onKeyUp={(event: KeyboardEvent) => (event.key === 'Enter' && !disabled ? onEnter?.() : null)}\n autoFocus\n size=\"small\"\n value={codeInput ?? ''}\n onChange={(event: React.ChangeEvent<HTMLInputElement>) => 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, validCodeLength = 6) => {\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 validateCodeInput = (code?: string) => {\n return code?.length === validCodeLength\n }\n const onCodeInputChange = (code?: string) => {\n setCodeInput(code ?? '')\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 }, [localStorageKey])\n\n return {\n codeInput, validated, userAccessCodes, onAccessCodeSuccess, onCodeInputChange, validateCodeInput,\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","slotProps","input","endAdornment","InputAdornment","position","CheckCircleOutline","sx","display","visibility","color","fontSize","ErrorOutline","onKeyUp","event","key","autoFocus","size","value","onChange","target","styled","TextField","name","paddingRight","AccessCodeGateFlexbox","children","onCodeInputChange","onAccessCodeSuccess","successRedirectDelay","userAccessCodes","validAccessCodes","validateFunction","props","initialized","setInitialized","useState","accessGranted","setAccessGranted","codeInput","setCodeInput","validCode","setValidCode","disabled","validateCode","useCallback","accessCode","includes","useEffect","onEnter","granted","setTimeout","some","code","React","FlexGrowCol","gap","FlexGrowRow","alignItems","FormControl","CodeTextField","label","ButtonEx","onClick","variant","useMemo","useState","useAccessCodes","localStorageKey","validCodeLength","validated","setValidated","useState","codeInput","setCodeInput","onAccessCodeSuccess","localStorage","setItem","JSON","stringify","validateCodeInput","code","length","onCodeInputChange","userAccessCodes","useMemo","storedCodes","getItem","parsedResult","parse","Array","isArray"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/AccessCodeGateFlexbox.tsx","../../src/components/CodeTextField.tsx","../../src/hooks/useAccessCodes.ts"],"sourcesContent":["import { 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 { PropsWithChildren } from 'react'\nimport React, {\n useCallback, useEffect, useState,\n} from 'react'\n\nimport { CodeTextField } from './CodeTextField.tsx'\n\nexport interface AccessCodeGateFlexbox extends PropsWithChildren, FlexBoxProps {\n onAccessCodeSuccess?: (code?: string) => void\n onCodeInputChange?: (codeInput?: 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 onCodeInputChange,\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 // keep the parent informed of the code input\n useEffect(() => {\n if (onCodeInputChange) {\n onCodeInputChange(codeInput)\n }\n }, [codeInput, onCodeInputChange])\n\n const onEnter = () => {\n onCodeInputChange?.(codeInput)\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 // eslint-disable-next-line react-hooks/set-state-in-effect, react-x/set-state-in-effect\n setValidCode(null)\n }, [codeInput])\n\n useEffect(() => {\n if (userAccessCodes) {\n const granted = userAccessCodes.some(code => validateCode(code))\n if (granted) {\n // eslint-disable-next-line react-hooks/set-state-in-effect, react-x/set-state-in-effect\n setAccessGranted(true)\n onAccessCodeSuccess?.()\n }\n }\n // eslint-disable-next-line react-x/set-state-in-effect\n setInitialized(true)\n // eslint-disable-next-line react-hooks/exhaustive-deps, react-x/exhaustive-deps\n }, [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 { KeyboardEvent } from 'react'\nimport React from 'react'\n\nexport type CodeTextFieldProps = TextFieldProps & {\n codeInput?: string\n disabled?: boolean\n onEnter?: () => void\n setCodeInput?: (code?: string) => void\n validCode?: boolean | null\n}\n\nexport const CodeTextField: React.FC<CodeTextFieldProps> = ({\n codeInput, disabled, onEnter, setCodeInput, validCode, ...props\n}) => (\n <StyledTextField\n slotProps={{\n input: {\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 }}\n onKeyUp={(event: KeyboardEvent) => (event.key === 'Enter' && !disabled ? onEnter?.() : null)}\n autoFocus\n size=\"small\"\n value={codeInput ?? ''}\n onChange={(event: React.ChangeEvent<HTMLInputElement>) => 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, validCodeLength = 6) => {\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 validateCodeInput = (code?: string) => {\n return code?.length === validCodeLength\n }\n const onCodeInputChange = (code?: string) => {\n setCodeInput(code ?? '')\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 }, [localStorageKey])\n\n return {\n codeInput, validated, userAccessCodes, onAccessCodeSuccess, onCodeInputChange, validateCodeInput,\n }\n}\n"],"mappings":"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","names":["jsx","jsxs","useState"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xyo-network/react-access-gate",
|
|
3
|
-
"version": "7.5.
|
|
3
|
+
"version": "7.5.12",
|
|
4
4
|
"description": "Common React library for all XYO projects that use React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"xyo",
|
|
@@ -36,44 +36,72 @@
|
|
|
36
36
|
},
|
|
37
37
|
"./package.json": "./package.json"
|
|
38
38
|
},
|
|
39
|
-
"module": "dist/browser/index.mjs",
|
|
40
|
-
"types": "dist/browser/index.d.ts",
|
|
41
39
|
"files": [
|
|
42
40
|
"dist",
|
|
43
|
-
"
|
|
41
|
+
"README.md"
|
|
44
42
|
],
|
|
45
|
-
"dependencies": {
|
|
46
|
-
"@xylabs/react-button": "~7.1.17",
|
|
47
|
-
"@xylabs/react-flexbox": "~7.1.17"
|
|
48
|
-
},
|
|
49
43
|
"devDependencies": {
|
|
50
|
-
"@mui/icons-material": "
|
|
51
|
-
"@mui/material": "
|
|
52
|
-
"@
|
|
44
|
+
"@mui/icons-material": "^7.3.10",
|
|
45
|
+
"@mui/material": "^7.3.10",
|
|
46
|
+
"@opentelemetry/api": "^1.9.1",
|
|
47
|
+
"@opentelemetry/sdk-trace-base": "^2.7.0",
|
|
48
|
+
"@storybook/react-vite": "~10.3.5",
|
|
49
|
+
"@types/node": "~25.6.0",
|
|
53
50
|
"@types/react": "^19.2.14",
|
|
54
|
-
"@xylabs/
|
|
55
|
-
"@xylabs/
|
|
56
|
-
"@xylabs/
|
|
57
|
-
"@xylabs/
|
|
58
|
-
"@xylabs/
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"react
|
|
62
|
-
"
|
|
51
|
+
"@xylabs/react-async-effect": "~7.1.20",
|
|
52
|
+
"@xylabs/react-button": "~7.1.20",
|
|
53
|
+
"@xylabs/react-flexbox": "~7.1.20",
|
|
54
|
+
"@xylabs/react-promise": "~7.1.20",
|
|
55
|
+
"@xylabs/toolchain": "~7.11.9",
|
|
56
|
+
"@xylabs/tsconfig": "^7.11.9",
|
|
57
|
+
"@xylabs/tsconfig-dom": "^7.11.9",
|
|
58
|
+
"@xylabs/tsconfig-react": "~7.11.9",
|
|
59
|
+
"@xylabs/zod": "~5.0.100",
|
|
60
|
+
"async-mutex": "^0.5.0",
|
|
61
|
+
"axios": "^1.15.2",
|
|
62
|
+
"bn.js": "^5.2.3",
|
|
63
|
+
"bowser": "^2.14.1",
|
|
64
|
+
"buffer": "^6.0.3",
|
|
65
|
+
"chalk": "^5.6.2",
|
|
66
|
+
"esbuild": "~0.28.0",
|
|
67
|
+
"eslint": "^10.2.1",
|
|
68
|
+
"ethers": "^6.16.0",
|
|
69
|
+
"fast-deep-equal": "~3.1.3",
|
|
70
|
+
"js-cookie": "~3.0.5",
|
|
71
|
+
"pako": "^2.1.0",
|
|
72
|
+
"react": "^19.2.5",
|
|
73
|
+
"react-dom": "^19.2.5",
|
|
74
|
+
"spark-md5": "~3.0.2",
|
|
75
|
+
"storybook": "^10.3.5",
|
|
63
76
|
"typescript": "^5.9.3",
|
|
64
|
-
"vite": "
|
|
77
|
+
"vite": "^8.0.10",
|
|
65
78
|
"zod": "^4.3.6"
|
|
66
79
|
},
|
|
67
80
|
"peerDependencies": {
|
|
68
|
-
"@mui/icons-material": "
|
|
69
|
-
"@mui/material": "
|
|
70
|
-
"
|
|
71
|
-
"react
|
|
72
|
-
"react-
|
|
73
|
-
"
|
|
81
|
+
"@mui/icons-material": "^7.3.10",
|
|
82
|
+
"@mui/material": "^7.3.10",
|
|
83
|
+
"@opentelemetry/sdk-trace-base": "^2.7.0",
|
|
84
|
+
"@types/react": "^19.2.14",
|
|
85
|
+
"@xylabs/react-async-effect": "~7.1.20",
|
|
86
|
+
"@xylabs/react-button": "~7.1.20",
|
|
87
|
+
"@xylabs/react-flexbox": "~7.1.20",
|
|
88
|
+
"@xylabs/zod": "~5.0.100",
|
|
89
|
+
"async-mutex": "^0.5.0",
|
|
90
|
+
"bn.js": "^5.2.3",
|
|
91
|
+
"bowser": "^2.14.1",
|
|
92
|
+
"buffer": "^6.0.3",
|
|
93
|
+
"chalk": "^5.6.2",
|
|
94
|
+
"ethers": "^6.16.0",
|
|
95
|
+
"fast-deep-equal": "~3.1.3",
|
|
96
|
+
"js-cookie": "~3.0.5",
|
|
97
|
+
"pako": "^2.1.0",
|
|
98
|
+
"react": "^19.2.5",
|
|
99
|
+
"react-dom": "^19.2.5",
|
|
100
|
+
"spark-md5": "~3.0.2",
|
|
101
|
+
"zod": "^4.3.6"
|
|
74
102
|
},
|
|
75
103
|
"publishConfig": {
|
|
76
104
|
"access": "public"
|
|
77
105
|
},
|
|
78
106
|
"docs": "dist/docs.json"
|
|
79
|
-
}
|
|
107
|
+
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { Alert, Typography } from '@mui/material'
|
|
2
|
-
import type { Meta, StoryFn } from '@storybook/react-vite'
|
|
3
|
-
import { FlexCol } from '@xylabs/react-flexbox'
|
|
4
|
-
import React from 'react'
|
|
5
|
-
|
|
6
|
-
import { useAccessCodes } from '../hooks/index.ts'
|
|
7
|
-
import { AccessCodeGateFlexbox } from './AccessCodeGateFlexbox.tsx'
|
|
8
|
-
|
|
9
|
-
const ValidAccessCodes = ['100519']
|
|
10
|
-
|
|
11
|
-
export default {
|
|
12
|
-
component: AccessCodeGateFlexbox,
|
|
13
|
-
title: 'access/AccessCodeGateFlexbox',
|
|
14
|
-
} as Meta
|
|
15
|
-
|
|
16
|
-
const Template: StoryFn<typeof AccessCodeGateFlexbox> = args => (
|
|
17
|
-
<AccessCodeGateFlexbox {...args} />
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
const TemplateWithAccessCodes: StoryFn<typeof AccessCodeGateFlexbox> = (args) => {
|
|
21
|
-
const {
|
|
22
|
-
codeInput, validated, onAccessCodeSuccess, onCodeInputChange, validateCodeInput,
|
|
23
|
-
} = useAccessCodes('storybook-access-codes-test')
|
|
24
|
-
|
|
25
|
-
return validated
|
|
26
|
-
? <Alert severity="success">Success!</Alert>
|
|
27
|
-
: (
|
|
28
|
-
<FlexCol gap={2}>
|
|
29
|
-
<AccessCodeGateFlexbox
|
|
30
|
-
onAccessCodeSuccess={onAccessCodeSuccess}
|
|
31
|
-
onCodeInputChange={onCodeInputChange}
|
|
32
|
-
validAccessCodes={ValidAccessCodes}
|
|
33
|
-
validateFunction={validateCodeInput}
|
|
34
|
-
{...args}
|
|
35
|
-
/>
|
|
36
|
-
<Typography variant="caption" gutterBottom>
|
|
37
|
-
Hint:
|
|
38
|
-
{ValidAccessCodes[0]}
|
|
39
|
-
</Typography>
|
|
40
|
-
<Typography variant="caption">
|
|
41
|
-
Code Input in Parent:
|
|
42
|
-
{codeInput}
|
|
43
|
-
</Typography>
|
|
44
|
-
</FlexCol>
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const Default = Template.bind({})
|
|
49
|
-
const WithAccessCodes = TemplateWithAccessCodes.bind({})
|
|
50
|
-
|
|
51
|
-
export { Default, WithAccessCodes }
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { FormControl } from '@mui/material'
|
|
2
|
-
import { ButtonEx } from '@xylabs/react-button'
|
|
3
|
-
import type { FlexBoxProps } from '@xylabs/react-flexbox'
|
|
4
|
-
import { FlexGrowCol, FlexGrowRow } from '@xylabs/react-flexbox'
|
|
5
|
-
import type { PropsWithChildren } from 'react'
|
|
6
|
-
import React, {
|
|
7
|
-
useCallback, useEffect, useState,
|
|
8
|
-
} from 'react'
|
|
9
|
-
|
|
10
|
-
import { CodeTextField } from './CodeTextField.tsx'
|
|
11
|
-
|
|
12
|
-
export interface AccessCodeGateFlexbox extends PropsWithChildren, FlexBoxProps {
|
|
13
|
-
onAccessCodeSuccess?: (code?: string) => void
|
|
14
|
-
onCodeInputChange?: (codeInput?: string) => void
|
|
15
|
-
successRedirectDelay?: number
|
|
16
|
-
textFieldHelperText?: string
|
|
17
|
-
userAccessCodes?: string[]
|
|
18
|
-
validAccessCodes?: string[]
|
|
19
|
-
validateFunction?: (codeInput?: string) => boolean
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const AccessCodeGateFlexbox: React.FC<AccessCodeGateFlexbox> = ({
|
|
23
|
-
children,
|
|
24
|
-
onCodeInputChange,
|
|
25
|
-
onAccessCodeSuccess,
|
|
26
|
-
successRedirectDelay = 1500,
|
|
27
|
-
userAccessCodes,
|
|
28
|
-
validAccessCodes,
|
|
29
|
-
validateFunction,
|
|
30
|
-
...props
|
|
31
|
-
}) => {
|
|
32
|
-
const [initialized, setInitialized] = useState(false)
|
|
33
|
-
const [accessGranted, setAccessGranted] = useState(false)
|
|
34
|
-
const [codeInput, setCodeInput] = useState<string>()
|
|
35
|
-
const [validCode, setValidCode] = useState<boolean | null>(null)
|
|
36
|
-
|
|
37
|
-
const disabled = validateFunction ? !validateFunction(codeInput) : !codeInput
|
|
38
|
-
const validateCode = useCallback((accessCode: string) => (accessCode ? validAccessCodes?.includes(accessCode) : false), [validAccessCodes])
|
|
39
|
-
|
|
40
|
-
// keep the parent informed of the code input
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
if (onCodeInputChange) {
|
|
43
|
-
onCodeInputChange(codeInput)
|
|
44
|
-
}
|
|
45
|
-
}, [codeInput, onCodeInputChange])
|
|
46
|
-
|
|
47
|
-
const onEnter = () => {
|
|
48
|
-
onCodeInputChange?.(codeInput)
|
|
49
|
-
if (codeInput) {
|
|
50
|
-
const granted = validateCode(codeInput)
|
|
51
|
-
if (granted) {
|
|
52
|
-
setValidCode(true)
|
|
53
|
-
// delay success callback to ensure the ui shows success before next action
|
|
54
|
-
setTimeout(() => {
|
|
55
|
-
setAccessGranted(granted)
|
|
56
|
-
onAccessCodeSuccess?.(codeInput)
|
|
57
|
-
}, successRedirectDelay)
|
|
58
|
-
} else {
|
|
59
|
-
setValidCode(false)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
// whenever a code changes, reset the success/failure warning
|
|
66
|
-
setValidCode(null)
|
|
67
|
-
}, [codeInput])
|
|
68
|
-
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
if (userAccessCodes) {
|
|
71
|
-
const granted = userAccessCodes.some(code => validateCode(code))
|
|
72
|
-
setAccessGranted(granted)
|
|
73
|
-
if (granted) {
|
|
74
|
-
onAccessCodeSuccess?.()
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
setInitialized(true)
|
|
78
|
-
}, [onAccessCodeSuccess, userAccessCodes, validateCode])
|
|
79
|
-
|
|
80
|
-
return (
|
|
81
|
-
<>
|
|
82
|
-
{initialized
|
|
83
|
-
? accessGranted
|
|
84
|
-
? children
|
|
85
|
-
: (
|
|
86
|
-
<FlexGrowCol gap={2} {...props}>
|
|
87
|
-
<FlexGrowRow gap={2} alignItems="start">
|
|
88
|
-
<FormControl>
|
|
89
|
-
<CodeTextField
|
|
90
|
-
codeInput={codeInput}
|
|
91
|
-
disabled={disabled}
|
|
92
|
-
label="Enter Access Code"
|
|
93
|
-
setCodeInput={setCodeInput}
|
|
94
|
-
validCode={validCode}
|
|
95
|
-
onEnter={onEnter}
|
|
96
|
-
/>
|
|
97
|
-
</FormControl>
|
|
98
|
-
<FormControl>
|
|
99
|
-
<ButtonEx disabled={disabled} onClick={onEnter} variant="contained">
|
|
100
|
-
Enter
|
|
101
|
-
</ButtonEx>
|
|
102
|
-
</FormControl>
|
|
103
|
-
</FlexGrowRow>
|
|
104
|
-
</FlexGrowCol>
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
: null}
|
|
108
|
-
</>
|
|
109
|
-
)
|
|
110
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { CheckCircleOutline, ErrorOutline } from '@mui/icons-material'
|
|
2
|
-
import type { TextFieldProps } from '@mui/material'
|
|
3
|
-
import {
|
|
4
|
-
InputAdornment, styled, TextField,
|
|
5
|
-
} from '@mui/material'
|
|
6
|
-
import type { KeyboardEvent } from 'react'
|
|
7
|
-
import React from 'react'
|
|
8
|
-
|
|
9
|
-
export type CodeTextFieldProps = TextFieldProps & {
|
|
10
|
-
codeInput?: string
|
|
11
|
-
disabled?: boolean
|
|
12
|
-
onEnter?: () => void
|
|
13
|
-
setCodeInput?: (code?: string) => void
|
|
14
|
-
validCode?: boolean | null
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const CodeTextField: React.FC<CodeTextFieldProps> = ({
|
|
18
|
-
codeInput, disabled, onEnter, setCodeInput, validCode, ...props
|
|
19
|
-
}) => (
|
|
20
|
-
<StyledTextField
|
|
21
|
-
slotProps={{
|
|
22
|
-
input: {
|
|
23
|
-
endAdornment: (
|
|
24
|
-
<InputAdornment position="start">
|
|
25
|
-
{/* Having a display block element for all 3 states (null, false, true) means the icon coming in and out
|
|
26
|
-
does not affect the overall width */}
|
|
27
|
-
<CheckCircleOutline sx={{ display: validCode === null ? 'block' : 'hidden', visibility: 'hidden' }} />
|
|
28
|
-
<CheckCircleOutline
|
|
29
|
-
color="success"
|
|
30
|
-
fontSize="medium"
|
|
31
|
-
sx={{ position: 'absolute', visibility: validCode === true ? 'visible' : 'hidden' }}
|
|
32
|
-
/>
|
|
33
|
-
<ErrorOutline color="error" fontSize="medium" sx={{ position: 'absolute', visibility: validCode === false ? 'visible' : 'hidden' }} />
|
|
34
|
-
</InputAdornment>
|
|
35
|
-
),
|
|
36
|
-
},
|
|
37
|
-
}}
|
|
38
|
-
onKeyUp={(event: KeyboardEvent) => (event.key === 'Enter' && !disabled ? onEnter?.() : null)}
|
|
39
|
-
autoFocus
|
|
40
|
-
size="small"
|
|
41
|
-
value={codeInput ?? ''}
|
|
42
|
-
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setCodeInput?.(event.target.value)}
|
|
43
|
-
{...props}
|
|
44
|
-
/>
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
const StyledTextField = styled(TextField, { name: 'StyledTextField' })(() => ({ '& .MuiInputBase-root': { paddingRight: 0 } }))
|
package/src/components/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './AccessCodeGateFlexbox.tsx'
|
package/src/hooks/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './useAccessCodes.ts'
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { useMemo, useState } from 'react'
|
|
2
|
-
|
|
3
|
-
export const useAccessCodes = (localStorageKey: string, validCodeLength = 6) => {
|
|
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 validateCodeInput = (code?: string) => {
|
|
18
|
-
return code?.length === validCodeLength
|
|
19
|
-
}
|
|
20
|
-
const onCodeInputChange = (code?: string) => {
|
|
21
|
-
setCodeInput(code ?? '')
|
|
22
|
-
}
|
|
23
|
-
const userAccessCodes = useMemo(() => {
|
|
24
|
-
const storedCodes = localStorage.getItem(localStorageKey)
|
|
25
|
-
if (storedCodes) {
|
|
26
|
-
const parsedResult = JSON.parse(storedCodes ?? '')
|
|
27
|
-
if (Array.isArray(parsedResult)) return parsedResult
|
|
28
|
-
}
|
|
29
|
-
}, [localStorageKey])
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
codeInput, validated, userAccessCodes, onAccessCodeSuccess, onCodeInputChange, validateCodeInput,
|
|
33
|
-
}
|
|
34
|
-
}
|
package/src/index.ts
DELETED