@xyo-network/react-access-gate 4.1.1 → 4.1.3

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.
@@ -3,6 +3,7 @@ import type { WithChildren } from '@xylabs/react-shared';
3
3
  import React from 'react';
4
4
  export interface AccessCodeGateFlexbox extends WithChildren, FlexBoxProps {
5
5
  onAccessCodeSuccess?: (code?: string) => void;
6
+ onCodeInputChange?: (codeInput?: string) => void;
6
7
  successRedirectDelay?: number;
7
8
  textFieldHelperText?: string;
8
9
  userAccessCodes?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"AccessCodeGateFlexbox.d.ts","sourceRoot":"","sources":["../../../src/components/AccessCodeGateFlexbox.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,KAEN,MAAM,OAAO,CAAA;AAId,MAAM,WAAW,qBAAsB,SAAQ,YAAY,EAAE,YAAY;IACvE,mBAAmB,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7C,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,CA+EjE,CAAA"}
1
+ {"version":3,"file":"AccessCodeGateFlexbox.d.ts","sourceRoot":"","sources":["../../../src/components/AccessCodeGateFlexbox.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,KAEN,MAAM,OAAO,CAAA;AAId,MAAM,WAAW,qBAAsB,SAAQ,YAAY,EAAE,YAAY;IACvE,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,CAwFjE,CAAA"}
@@ -1,7 +1,9 @@
1
- export declare const useAccessCodes: (localStorageKey: string) => {
1
+ export declare const useAccessCodes: (localStorageKey: string, validCodeLength?: number) => {
2
+ codeInput: string;
2
3
  validated: boolean;
3
4
  userAccessCodes: any[] | undefined;
4
5
  onAccessCodeSuccess: () => void;
5
- onCodeInputChange: (code?: string) => boolean;
6
+ onCodeInputChange: (code?: string) => void;
7
+ validateCodeInput: (code?: string) => boolean;
6
8
  };
7
9
  //# sourceMappingURL=useAccessCodes.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"useAccessCodes.d.ts","sourceRoot":"","sources":["../../../src/hooks/useAccessCodes.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,oBAAqB,MAAM;;;;;+BAiBlB,MAAM;+BAHN,MAAM;CAiBzC,CAAA"}
@@ -52,7 +52,7 @@ var StyledTextField = styled(TextField, {
52
52
  }));
53
53
 
54
54
  // src/components/AccessCodeGateFlexbox.tsx
55
- var AccessCodeGateFlexbox = /* @__PURE__ */ __name(({ children, onAccessCodeSuccess, successRedirectDelay = 1500, userAccessCodes, validAccessCodes, validateFunction, ...props }) => {
55
+ var AccessCodeGateFlexbox = /* @__PURE__ */ __name(({ children, onCodeInputChange, onAccessCodeSuccess, successRedirectDelay = 1500, userAccessCodes, validAccessCodes, validateFunction, ...props }) => {
56
56
  const [initialized, setInitialized] = useState(false);
57
57
  const [accessGranted, setAccessGranted] = useState(false);
58
58
  const [codeInput, setCodeInput] = useState();
@@ -61,7 +61,16 @@ var AccessCodeGateFlexbox = /* @__PURE__ */ __name(({ children, onAccessCodeSucc
61
61
  const validateCode = useCallback((accessCode) => accessCode ? validAccessCodes?.includes(accessCode) : false, [
62
62
  validAccessCodes
63
63
  ]);
64
+ useEffect(() => {
65
+ if (onCodeInputChange) {
66
+ onCodeInputChange(codeInput);
67
+ }
68
+ }, [
69
+ codeInput,
70
+ onCodeInputChange
71
+ ]);
64
72
  const onEnter = /* @__PURE__ */ __name(() => {
73
+ onCodeInputChange?.(codeInput);
65
74
  if (codeInput) {
66
75
  const granted = validateCode(codeInput);
67
76
  if (granted) {
@@ -116,7 +125,7 @@ var AccessCodeGateFlexbox = /* @__PURE__ */ __name(({ children, onAccessCodeSucc
116
125
 
117
126
  // src/hooks/useAccessCodes.ts
118
127
  import { useMemo, useState as useState2 } from "react";
119
- var useAccessCodes = /* @__PURE__ */ __name((localStorageKey) => {
128
+ var useAccessCodes = /* @__PURE__ */ __name((localStorageKey, validCodeLength = 6) => {
120
129
  const [validated, setValidated] = useState2(false);
121
130
  const [codeInput, setCodeInput] = useState2("");
122
131
  const onAccessCodeSuccess = /* @__PURE__ */ __name(() => {
@@ -129,9 +138,11 @@ var useAccessCodes = /* @__PURE__ */ __name((localStorageKey) => {
129
138
  setValidated(true);
130
139
  }
131
140
  }, "onAccessCodeSuccess");
141
+ const validateCodeInput = /* @__PURE__ */ __name((code) => {
142
+ return code?.length === validCodeLength;
143
+ }, "validateCodeInput");
132
144
  const onCodeInputChange = /* @__PURE__ */ __name((code) => {
133
145
  setCodeInput(code ?? "");
134
- return code?.length === 6;
135
146
  }, "onCodeInputChange");
136
147
  const userAccessCodes = useMemo(() => {
137
148
  const storedCodes = localStorage.getItem(localStorageKey);
@@ -141,10 +152,12 @@ var useAccessCodes = /* @__PURE__ */ __name((localStorageKey) => {
141
152
  }
142
153
  }, []);
143
154
  return {
155
+ codeInput,
144
156
  validated,
145
157
  userAccessCodes,
146
158
  onAccessCodeSuccess,
147
- onCodeInputChange
159
+ onCodeInputChange,
160
+ validateCodeInput
148
161
  };
149
162
  }, "useAccessCodes");
150
163
  export {
@@ -1 +1 @@
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"]}
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 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 { 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, 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 }, [])\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","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","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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/react-access-gate",
3
- "version": "4.1.1",
3
+ "version": "4.1.3",
4
4
  "description": "Common React library for all XYO projects that use React",
5
5
  "keywords": [
6
6
  "xyo",
@@ -44,19 +44,19 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@mui/icons-material": "^6.1.4",
47
- "@xylabs/react-button": "^5.2.1",
48
- "@xylabs/react-flexbox": "^5.2.1",
49
- "@xylabs/react-shared": "^5.2.1"
47
+ "@xylabs/react-button": "^5.2.2",
48
+ "@xylabs/react-flexbox": "^5.2.2",
49
+ "@xylabs/react-shared": "^5.2.2"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@mui/material": "^6.1.4",
53
53
  "@mui/styles": "^6.1.4",
54
- "@storybook/react": "^8.3.5",
54
+ "@storybook/react": "^8.3.6",
55
55
  "@xylabs/ts-scripts-yarn3": "^4.2.1",
56
56
  "@xylabs/tsconfig-react": "^4.2.1",
57
57
  "react": "^18.3.1",
58
58
  "react-dom": "^18.3.1",
59
- "storybook": "^8.3.5",
59
+ "storybook": "^8.3.6",
60
60
  "typescript": "^5.6.3"
61
61
  },
62
62
  "peerDependencies": {
@@ -19,7 +19,7 @@ const Template: StoryFn<typeof AccessCodeGateFlexbox> = args => (
19
19
 
20
20
  const TemplateWithAccessCodes: StoryFn<typeof AccessCodeGateFlexbox> = (args) => {
21
21
  const {
22
- validated, onAccessCodeSuccess, onCodeInputChange,
22
+ codeInput, validated, onAccessCodeSuccess, onCodeInputChange, validateCodeInput,
23
23
  } = useAccessCodes('storybook-access-codes-test')
24
24
 
25
25
  return validated
@@ -28,14 +28,19 @@ const TemplateWithAccessCodes: StoryFn<typeof AccessCodeGateFlexbox> = (args) =>
28
28
  <FlexCol gap={2}>
29
29
  <AccessCodeGateFlexbox
30
30
  onAccessCodeSuccess={onAccessCodeSuccess}
31
+ onCodeInputChange={onCodeInputChange}
31
32
  validAccessCodes={ValidAccessCodes}
32
- validateFunction={onCodeInputChange}
33
+ validateFunction={validateCodeInput}
33
34
  {...args}
34
35
  />
35
- <Typography variant="caption">
36
+ <Typography variant="caption" gutterBottom>
36
37
  Hint:
37
38
  {ValidAccessCodes[0]}
38
39
  </Typography>
40
+ <Typography variant="caption">
41
+ Code Input in Parent:
42
+ {codeInput}
43
+ </Typography>
39
44
  </FlexCol>
40
45
  )
41
46
  }
@@ -12,6 +12,7 @@ import { CodeTextField } from './CodeTextField.tsx'
12
12
 
13
13
  export interface AccessCodeGateFlexbox extends WithChildren, FlexBoxProps {
14
14
  onAccessCodeSuccess?: (code?: string) => void
15
+ onCodeInputChange?: (codeInput?: string) => void
15
16
  successRedirectDelay?: number
16
17
  textFieldHelperText?: string
17
18
  userAccessCodes?: string[]
@@ -21,6 +22,7 @@ export interface AccessCodeGateFlexbox extends WithChildren, FlexBoxProps {
21
22
 
22
23
  export const AccessCodeGateFlexbox: React.FC<AccessCodeGateFlexbox> = ({
23
24
  children,
25
+ onCodeInputChange,
24
26
  onAccessCodeSuccess,
25
27
  successRedirectDelay = 1500,
26
28
  userAccessCodes,
@@ -36,7 +38,15 @@ export const AccessCodeGateFlexbox: React.FC<AccessCodeGateFlexbox> = ({
36
38
  const disabled = validateFunction ? !validateFunction(codeInput) : !codeInput
37
39
  const validateCode = useCallback((accessCode: string) => (accessCode ? validAccessCodes?.includes(accessCode) : false), [validAccessCodes])
38
40
 
41
+ // keep the parent informed of the code input
42
+ useEffect(() => {
43
+ if (onCodeInputChange) {
44
+ onCodeInputChange(codeInput)
45
+ }
46
+ }, [codeInput, onCodeInputChange])
47
+
39
48
  const onEnter = () => {
49
+ onCodeInputChange?.(codeInput)
40
50
  if (codeInput) {
41
51
  const granted = validateCode(codeInput)
42
52
  if (granted) {
@@ -1,6 +1,6 @@
1
1
  import { useMemo, useState } from 'react'
2
2
 
3
- export const useAccessCodes = (localStorageKey: string) => {
3
+ export const useAccessCodes = (localStorageKey: string, validCodeLength = 6) => {
4
4
  const [validated, setValidated] = useState(false)
5
5
  const [codeInput, setCodeInput] = useState('')
6
6
 
@@ -14,9 +14,11 @@ export const useAccessCodes = (localStorageKey: string) => {
14
14
  setValidated(true)
15
15
  }
16
16
  }
17
+ const validateCodeInput = (code?: string) => {
18
+ return code?.length === validCodeLength
19
+ }
17
20
  const onCodeInputChange = (code?: string) => {
18
21
  setCodeInput(code ?? '')
19
- return code?.length === 6
20
22
  }
21
23
  const userAccessCodes = useMemo(() => {
22
24
  const storedCodes = localStorage.getItem(localStorageKey)
@@ -27,6 +29,6 @@ export const useAccessCodes = (localStorageKey: string) => {
27
29
  }, [])
28
30
 
29
31
  return {
30
- validated, userAccessCodes, onAccessCodeSuccess, onCodeInputChange,
32
+ codeInput, validated, userAccessCodes, onAccessCodeSuccess, onCodeInputChange, validateCodeInput,
31
33
  }
32
34
  }