@ews-admin/global-design-system 1.30.0 → 1.31.0
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/components/LockScreen/LockScreen.d.ts +5 -4
- package/dist/components/LockScreen/LockScreen.d.ts.map +1 -1
- package/dist/components/LockScreen/LockScreen.types.d.ts +8 -0
- package/dist/components/LockScreen/LockScreen.types.d.ts.map +1 -1
- package/dist/components/PinCodeInput/PinCodeInput.d.ts +11 -0
- package/dist/components/PinCodeInput/PinCodeInput.d.ts.map +1 -0
- package/dist/components/PinCodeInput/PinCodeInput.types.d.ts +26 -0
- package/dist/components/PinCodeInput/PinCodeInput.types.d.ts.map +1 -0
- package/dist/components/PinCodeInput/index.d.ts +3 -0
- package/dist/components/PinCodeInput/index.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useMediaQuery.d.ts +7 -0
- package/dist/hooks/useMediaQuery.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +55 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +191 -12
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +192 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/LockScreen/LockScreen.tsx +215 -89
- package/src/components/LockScreen/LockScreen.types.ts +8 -0
- package/src/components/PinCodeInput/PinCodeInput.tsx +184 -0
- package/src/components/PinCodeInput/PinCodeInput.types.ts +25 -0
- package/src/components/PinCodeInput/index.ts +2 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useMediaQuery.ts +31 -0
- package/src/index.ts +4 -0
package/dist/index.js
CHANGED
|
@@ -33083,6 +33083,33 @@ function useDebouncedCallback(callback, delay) {
|
|
|
33083
33083
|
return debouncedCallback;
|
|
33084
33084
|
}
|
|
33085
33085
|
|
|
33086
|
+
const matches = (query, fallback) => typeof window !== "undefined" && typeof window.matchMedia === "function"
|
|
33087
|
+
? window.matchMedia(query).matches
|
|
33088
|
+
: fallback;
|
|
33089
|
+
/**
|
|
33090
|
+
* Whether a CSS media query matches, updated when it changes. Returns
|
|
33091
|
+
* `defaultValue` where `matchMedia` does not exist (server rendering, old
|
|
33092
|
+
* test environments).
|
|
33093
|
+
*/
|
|
33094
|
+
const useMediaQuery = (query, defaultValue = false) => {
|
|
33095
|
+
const [value, setValue] = React.useState(() => matches(query, defaultValue));
|
|
33096
|
+
React.useEffect(() => {
|
|
33097
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function")
|
|
33098
|
+
return;
|
|
33099
|
+
const list = window.matchMedia(query);
|
|
33100
|
+
const onChange = () => setValue(list.matches);
|
|
33101
|
+
onChange();
|
|
33102
|
+
// Safari < 14 only has addListener.
|
|
33103
|
+
if (list.addEventListener) {
|
|
33104
|
+
list.addEventListener("change", onChange);
|
|
33105
|
+
return () => list.removeEventListener("change", onChange);
|
|
33106
|
+
}
|
|
33107
|
+
list.addListener(onChange);
|
|
33108
|
+
return () => list.removeListener(onChange);
|
|
33109
|
+
}, [query]);
|
|
33110
|
+
return value;
|
|
33111
|
+
};
|
|
33112
|
+
|
|
33086
33113
|
const DEFAULT_TIMEOUT_MS = 180000;
|
|
33087
33114
|
const DEFAULT_STORAGE_KEY = "ews-idle-lock";
|
|
33088
33115
|
const ACTIVITY_WRITE_INTERVAL_MS = 5000;
|
|
@@ -35502,6 +35529,131 @@ const PinPad = ({ length = 4, onComplete, onChange, disabled = false, invalid =
|
|
|
35502
35529
|
: "bg-ews-primary-light", disabled && index < value.length && "animate-pulse") }, index))) }), jsxRuntime.jsx("p", { className: "sr-only", "aria-live": "polite", "aria-atomic": "true", children: progressLabel(value.length, length) }), jsxRuntime.jsxs("div", { dir: "ltr", role: "group", "aria-label": keypadLabel, className: "grid grid-cols-3 gap-x-8 gap-y-4", children: [DIGIT_ROWS.map((digit) => (jsxRuntime.jsx("button", { type: "button", className: keyClass, disabled: disabled, onClick: () => press(digit), children: digit }, digit))), jsxRuntime.jsx("span", { "aria-hidden": "true" }), jsxRuntime.jsx("button", { type: "button", className: keyClass, disabled: disabled, onClick: () => press("0"), children: "0" }), jsxRuntime.jsx("button", { type: "button", className: cn(keyClass, "text-ews-gray-700"), disabled: disabled || value.length === 0, "aria-label": eraseLabel, onClick: erase, children: jsxRuntime.jsx(Delete, { className: "h-7 w-7", strokeWidth: 1.5, "aria-hidden": "true" }) })] })] }));
|
|
35503
35530
|
};
|
|
35504
35531
|
|
|
35532
|
+
const MASK = "•";
|
|
35533
|
+
const onlyDigits = (text) => text.replace(/\D/g, "");
|
|
35534
|
+
/**
|
|
35535
|
+
* Row of one-digit boxes for a PIN or a one-time code (desktop keyboards):
|
|
35536
|
+
* typing moves to the next box, Backspace goes back, arrows move, pasting a
|
|
35537
|
+
* code fills the boxes and `onComplete` fires once every box is filled.
|
|
35538
|
+
*/
|
|
35539
|
+
const PinCodeInput = ({ length = 4, onComplete, onChange, masked = true, autoComplete = "off", autoFocus = true, disabled = false, invalid = false, resetKey, digitLabel, groupLabel, className, }) => {
|
|
35540
|
+
const empty = React.useCallback(() => Array.from({ length }, () => ""), [length]);
|
|
35541
|
+
const [digits, setDigits] = React.useState(empty);
|
|
35542
|
+
const digitsRef = React.useRef(digits);
|
|
35543
|
+
const inputs = React.useRef([]);
|
|
35544
|
+
const focusBox = (index) => {
|
|
35545
|
+
const input = inputs.current[Math.max(0, Math.min(index, length - 1))];
|
|
35546
|
+
input?.focus();
|
|
35547
|
+
input?.select();
|
|
35548
|
+
};
|
|
35549
|
+
const update = (next) => {
|
|
35550
|
+
digitsRef.current = next;
|
|
35551
|
+
setDigits(next);
|
|
35552
|
+
const value = next.join("");
|
|
35553
|
+
onChange?.(value);
|
|
35554
|
+
if (value.length === length && next.every(Boolean))
|
|
35555
|
+
onComplete(value);
|
|
35556
|
+
};
|
|
35557
|
+
/** Writes `chars` from box `start` on and focuses the box after them. */
|
|
35558
|
+
const fill = (start, chars) => {
|
|
35559
|
+
const next = [...digitsRef.current];
|
|
35560
|
+
const written = chars.slice(0, length - start).split("");
|
|
35561
|
+
written.forEach((char, offset) => {
|
|
35562
|
+
next[start + offset] = char;
|
|
35563
|
+
});
|
|
35564
|
+
focusBox(start + written.length);
|
|
35565
|
+
update(next);
|
|
35566
|
+
};
|
|
35567
|
+
// Clear the boxes when the caller asks for it.
|
|
35568
|
+
React.useEffect(() => {
|
|
35569
|
+
const cleared = empty();
|
|
35570
|
+
digitsRef.current = cleared;
|
|
35571
|
+
setDigits(cleared);
|
|
35572
|
+
}, [resetKey, empty]);
|
|
35573
|
+
// Focus the first empty box on mount, after a reset and once re-enabled.
|
|
35574
|
+
React.useEffect(() => {
|
|
35575
|
+
if (!autoFocus || disabled)
|
|
35576
|
+
return;
|
|
35577
|
+
const firstEmpty = digitsRef.current.findIndex((digit) => !digit);
|
|
35578
|
+
focusBox(firstEmpty === -1 ? length - 1 : firstEmpty);
|
|
35579
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35580
|
+
}, [autoFocus, disabled, resetKey, length]);
|
|
35581
|
+
const handleChange = (index, event) => {
|
|
35582
|
+
if (disabled)
|
|
35583
|
+
return;
|
|
35584
|
+
const { value, selectionStart } = event.target;
|
|
35585
|
+
const typed = onlyDigits(value);
|
|
35586
|
+
if (!typed) {
|
|
35587
|
+
const next = [...digitsRef.current];
|
|
35588
|
+
next[index] = "";
|
|
35589
|
+
update(next);
|
|
35590
|
+
return;
|
|
35591
|
+
}
|
|
35592
|
+
// A digit typed next to the one already in the box: keep the new one.
|
|
35593
|
+
if (digitsRef.current[index] && value.length === 2) {
|
|
35594
|
+
const inserted = value.charAt((selectionStart ?? value.length) - 1);
|
|
35595
|
+
if (/\d/.test(inserted)) {
|
|
35596
|
+
fill(index, inserted);
|
|
35597
|
+
return;
|
|
35598
|
+
}
|
|
35599
|
+
}
|
|
35600
|
+
// One digit, or a whole code (browser autofill of the one-time code).
|
|
35601
|
+
fill(typed.length >= length ? 0 : index, typed);
|
|
35602
|
+
};
|
|
35603
|
+
const handleKeyDown = (index, event) => {
|
|
35604
|
+
if (disabled)
|
|
35605
|
+
return;
|
|
35606
|
+
switch (event.key) {
|
|
35607
|
+
case "Backspace": {
|
|
35608
|
+
event.preventDefault();
|
|
35609
|
+
const next = [...digitsRef.current];
|
|
35610
|
+
const target = next[index] ? index : Math.max(0, index - 1);
|
|
35611
|
+
next[target] = "";
|
|
35612
|
+
focusBox(target);
|
|
35613
|
+
update(next);
|
|
35614
|
+
return;
|
|
35615
|
+
}
|
|
35616
|
+
case "Delete": {
|
|
35617
|
+
event.preventDefault();
|
|
35618
|
+
const next = [...digitsRef.current];
|
|
35619
|
+
next[index] = "";
|
|
35620
|
+
update(next);
|
|
35621
|
+
return;
|
|
35622
|
+
}
|
|
35623
|
+
case "ArrowLeft":
|
|
35624
|
+
event.preventDefault();
|
|
35625
|
+
focusBox(index - 1);
|
|
35626
|
+
return;
|
|
35627
|
+
case "ArrowRight":
|
|
35628
|
+
event.preventDefault();
|
|
35629
|
+
focusBox(index + 1);
|
|
35630
|
+
return;
|
|
35631
|
+
case "Home":
|
|
35632
|
+
event.preventDefault();
|
|
35633
|
+
focusBox(0);
|
|
35634
|
+
return;
|
|
35635
|
+
case "End":
|
|
35636
|
+
event.preventDefault();
|
|
35637
|
+
focusBox(length - 1);
|
|
35638
|
+
return;
|
|
35639
|
+
}
|
|
35640
|
+
};
|
|
35641
|
+
const handlePaste = (index, event) => {
|
|
35642
|
+
event.preventDefault();
|
|
35643
|
+
if (disabled)
|
|
35644
|
+
return;
|
|
35645
|
+
const pasted = onlyDigits(event.clipboardData.getData("text"));
|
|
35646
|
+
if (!pasted)
|
|
35647
|
+
return;
|
|
35648
|
+
fill(pasted.length >= length ? 0 : index, pasted);
|
|
35649
|
+
};
|
|
35650
|
+
return (
|
|
35651
|
+
// Codes stay left-to-right in RTL locales.
|
|
35652
|
+
jsxRuntime.jsx("div", { dir: "ltr", role: "group", "aria-label": groupLabel, "data-testid": "pin-code-input", className: cn("flex justify-center gap-3", invalid && "animate-pin-shake", className), children: digits.map((digit, index) => (jsxRuntime.jsx("input", { ref: (element) => {
|
|
35653
|
+
inputs.current[index] = element;
|
|
35654
|
+
}, type: "text", inputMode: "numeric", pattern: "[0-9]*", autoComplete: index === 0 ? autoComplete : "off", autoCorrect: "off", spellCheck: false, value: digit && masked ? MASK : digit, disabled: disabled, "aria-label": digitLabel(index + 1, length), "aria-invalid": invalid || undefined, "data-filled": !!digit, onChange: (event) => handleChange(index, event), onKeyDown: (event) => handleKeyDown(index, event), onPaste: (event) => handlePaste(index, event), onFocus: (event) => event.target.select(), className: cn("h-14 w-12 rounded-lg border bg-white text-center text-2xl font-medium text-ews-gray-900 outline-none", "transition-colors focus:border-ews-primary focus:ring-2 focus:ring-ews-primary", "disabled:cursor-not-allowed disabled:opacity-60", invalid ? "border-ews-error" : digit ? "border-ews-primary" : "border-ews-gray-300", disabled && digit && "animate-pulse") }, index))) }));
|
|
35655
|
+
};
|
|
35656
|
+
|
|
35505
35657
|
const FIRST_STEP = {
|
|
35506
35658
|
create: "new",
|
|
35507
35659
|
unlock: "unlock",
|
|
@@ -35522,14 +35674,19 @@ const titleOf = (mode, step, texts) => {
|
|
|
35522
35674
|
return texts.confirmTitle;
|
|
35523
35675
|
}
|
|
35524
35676
|
};
|
|
35677
|
+
/** Same breakpoint as Tailwind's `sm`. */
|
|
35678
|
+
const DESKTOP_QUERY = "(min-width: 640px)";
|
|
35679
|
+
/** Open lock screens, oldest first; only the last one traps focus. */
|
|
35680
|
+
const trapStack = [];
|
|
35525
35681
|
const FOCUSABLE = 'button:not([disabled]), input:not([disabled]), a[href], [tabindex]:not([tabindex="-1"])';
|
|
35526
35682
|
/**
|
|
35527
35683
|
* Blocking PIN screen: creates, checks, changes or resets (with a one-time
|
|
35528
|
-
* code) the user's secret code.
|
|
35529
|
-
*
|
|
35530
|
-
*
|
|
35684
|
+
* code) the user's secret code. Below 640px: full screen with an on-screen
|
|
35685
|
+
* keypad. From 640px: compact dialog on a grey backdrop with one box per digit,
|
|
35686
|
+
* typed on the keyboard. Submit handlers return promises: a rejection clears
|
|
35687
|
+
* the code and goes back to the first step, the app shows its message in `error`.
|
|
35531
35688
|
*/
|
|
35532
|
-
const LockScreen = ({ mode, texts, error, busy = false, onCreate, onUnlock, onChangePin, onReset, onForgot, onCancel, pinLength = 4, otpLength = 6, logo, className, }) => {
|
|
35689
|
+
const LockScreen = ({ mode, texts, error, busy = false, onCreate, onUnlock, onChangePin, onReset, onForgot, onCancel, onSignOut, subtitle, pinLength = 4, otpLength = 6, logo, className, }) => {
|
|
35533
35690
|
const [step, setStep] = React.useState(FIRST_STEP[mode]);
|
|
35534
35691
|
const [firstValue, setFirstValue] = React.useState(""); // current PIN or one-time code
|
|
35535
35692
|
const [newPin, setNewPin] = React.useState("");
|
|
@@ -35540,6 +35697,7 @@ const LockScreen = ({ mode, texts, error, busy = false, onCreate, onUnlock, onCh
|
|
|
35540
35697
|
const [resetKey, setResetKey] = React.useState(0);
|
|
35541
35698
|
const panelRef = React.useRef(null);
|
|
35542
35699
|
const mounted = React.useRef(true);
|
|
35700
|
+
const isDesktop = useMediaQuery(DESKTOP_QUERY);
|
|
35543
35701
|
const restart = React.useCallback((failed) => {
|
|
35544
35702
|
setStep(FIRST_STEP[mode]);
|
|
35545
35703
|
setFirstValue("");
|
|
@@ -35558,22 +35716,33 @@ const LockScreen = ({ mode, texts, error, busy = false, onCreate, onUnlock, onCh
|
|
|
35558
35716
|
mounted.current = false;
|
|
35559
35717
|
};
|
|
35560
35718
|
}, []);
|
|
35561
|
-
// Block page scroll and keep focus inside the screen.
|
|
35719
|
+
// Block page scroll and keep focus inside the screen. Only the most recently
|
|
35720
|
+
// mounted lock screen traps focus: two traps pulling focus back to their own
|
|
35721
|
+
// panel would loop forever (e.g. a Storybook docs page rendering every story).
|
|
35562
35722
|
React.useEffect(() => {
|
|
35723
|
+
const token = {};
|
|
35724
|
+
trapStack.push(token);
|
|
35563
35725
|
const previousOverflow = document.body.style.overflow;
|
|
35564
35726
|
document.body.style.overflow = "hidden";
|
|
35565
35727
|
const previousFocus = document.activeElement;
|
|
35566
|
-
|
|
35728
|
+
// The desktop code boxes focus themselves first.
|
|
35729
|
+
if (!panelRef.current?.contains(document.activeElement))
|
|
35730
|
+
panelRef.current?.focus();
|
|
35567
35731
|
const keepFocus = (event) => {
|
|
35732
|
+
if (trapStack[trapStack.length - 1] !== token)
|
|
35733
|
+
return;
|
|
35568
35734
|
const panel = panelRef.current;
|
|
35569
35735
|
if (panel && !panel.contains(event.target))
|
|
35570
35736
|
panel.focus();
|
|
35571
35737
|
};
|
|
35572
35738
|
document.addEventListener("focusin", keepFocus);
|
|
35573
35739
|
return () => {
|
|
35740
|
+
const wasTop = trapStack[trapStack.length - 1] === token;
|
|
35741
|
+
trapStack.splice(trapStack.indexOf(token), 1);
|
|
35574
35742
|
document.body.style.overflow = previousOverflow;
|
|
35575
35743
|
document.removeEventListener("focusin", keepFocus);
|
|
35576
|
-
|
|
35744
|
+
if (wasTop)
|
|
35745
|
+
previousFocus?.focus?.();
|
|
35577
35746
|
};
|
|
35578
35747
|
}, []);
|
|
35579
35748
|
const trapTab = (event) => {
|
|
@@ -35654,16 +35823,26 @@ const LockScreen = ({ mode, texts, error, busy = false, onCreate, onUnlock, onCh
|
|
|
35654
35823
|
setInvalid(false);
|
|
35655
35824
|
setStep("new");
|
|
35656
35825
|
};
|
|
35826
|
+
const completeOtp = (code) => {
|
|
35827
|
+
setLocalError(null);
|
|
35828
|
+
setFirstValue(code);
|
|
35829
|
+
setInvalid(false);
|
|
35830
|
+
setStep("new");
|
|
35831
|
+
};
|
|
35657
35832
|
const isBusy = busy || pending;
|
|
35658
35833
|
const shownError = localError ?? error;
|
|
35659
35834
|
const showForgot = !!onForgot &&
|
|
35660
35835
|
!!texts.forgot &&
|
|
35661
35836
|
(step === "unlock" || step === "current");
|
|
35662
35837
|
const title = titleOf(mode, step, texts);
|
|
35663
|
-
|
|
35664
|
-
|
|
35665
|
-
|
|
35666
|
-
|
|
35838
|
+
const digitLabel = texts.digit ?? texts.progress;
|
|
35839
|
+
const desktopContent = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "pb-5", children: logo ?? jsxRuntime.jsx(Logo, { size: "sm", variant: "normal" }) }), title && (jsxRuntime.jsx("h2", { className: "text-center text-xl font-semibold text-ews-gray-900", children: title })), subtitle && (jsxRuntime.jsx("p", { className: "mt-1 text-center text-sm text-ews-gray-600", children: subtitle })), step === "otp" && texts.otpDescription && (jsxRuntime.jsx("p", { className: "mt-2 text-center text-sm text-ews-gray-600", children: texts.otpDescription })), jsxRuntime.jsx("div", { className: "mt-6", children: step === "otp" ? (jsxRuntime.jsx(PinCodeInput, { length: otpLength, masked: false, autoComplete: "one-time-code", onComplete: completeOtp, onChange: () => setInvalid(false), disabled: isBusy, invalid: invalid, resetKey: resetKey, digitLabel: digitLabel, groupLabel: texts.otpLabel ?? title }, step)) : (jsxRuntime.jsx(PinCodeInput, { length: pinLength, onComplete: handlePin, onChange: () => setInvalid(false), disabled: isBusy, invalid: invalid, resetKey: resetKey, digitLabel: digitLabel, groupLabel: title }, step)) }), jsxRuntime.jsx("p", { role: "alert", className: "mt-3 min-h-[1.25rem] text-center text-sm text-ews-error", children: shownError }), (showForgot || (onCancel && texts.cancel) || (onSignOut && texts.signOut)) && (jsxRuntime.jsxs("div", { className: "mt-4 flex w-full flex-col items-center gap-2 border-t border-ews-gray-200 pt-4", children: [showForgot && (jsxRuntime.jsx("button", { type: "button", onClick: onForgot, disabled: isBusy, className: "text-sm font-medium text-ews-primary hover:underline focus-visible:underline", children: texts.forgot })), onCancel && texts.cancel && (jsxRuntime.jsx("button", { type: "button", onClick: onCancel, disabled: isBusy, className: "text-sm text-ews-gray-600 hover:underline focus-visible:underline", children: texts.cancel })), onSignOut && texts.signOut && (jsxRuntime.jsx("button", { type: "button", onClick: onSignOut, disabled: isBusy, className: "text-sm text-ews-gray-600 hover:underline focus-visible:underline", children: texts.signOut }))] }))] }));
|
|
35840
|
+
return (jsxRuntime.jsx("div", { className: cn("fixed inset-0 z-[1000] flex justify-center overflow-y-auto bg-white", "sm:items-center sm:bg-ews-gray-900/50 sm:backdrop-blur-sm", isDesktop && "p-4", className), "data-testid": "lock-screen", "data-layout": isDesktop ? "desktop" : "mobile", children: jsxRuntime.jsx("div", { ref: panelRef, role: "dialog", "aria-modal": "true", "aria-label": texts.dialogLabel, "aria-busy": isBusy, tabIndex: -1, onKeyDown: trapTab, className: isDesktop
|
|
35841
|
+
? "flex w-full max-w-[400px] flex-col items-center rounded-2xl bg-white px-8 py-8 shadow-xl outline-none"
|
|
35842
|
+
: cn("flex min-h-full w-full flex-col items-center px-6 pb-8 outline-none", "sm:min-h-0 sm:w-auto sm:min-w-[22rem] sm:rounded-2xl sm:bg-white sm:px-10 sm:py-10 sm:shadow-xl"), children: isDesktop ? (desktopContent) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "flex min-h-[33vh] shrink-0 items-center justify-center sm:min-h-0 sm:pb-8", children: logo ?? jsxRuntime.jsx(Logo, { size: "lg", variant: "normal" }) }), title && (jsxRuntime.jsx("h2", { className: "mb-8 text-center text-lg font-normal text-ews-gray-900", children: title })), step === "otp" ? (jsxRuntime.jsxs("form", { onSubmit: submitOtp, className: "flex w-full max-w-xs flex-col items-center gap-4", children: [texts.otpDescription && (jsxRuntime.jsx("p", { className: "text-center text-sm text-ews-gray-600", children: texts.otpDescription })), jsxRuntime.jsx("label", { className: "sr-only", htmlFor: "lock-screen-otp", children: texts.otpLabel }), jsxRuntime.jsx("input", { id: "lock-screen-otp", dir: "ltr", inputMode: "numeric", autoComplete: "one-time-code", maxLength: otpLength, value: otp, disabled: isBusy, "aria-label": texts.otpLabel, "aria-invalid": invalid || undefined, onChange: (event) => {
|
|
35843
|
+
setOtp(event.target.value.replace(/\D/g, "").slice(0, otpLength));
|
|
35844
|
+
setInvalid(false);
|
|
35845
|
+
}, className: cn("w-full rounded-lg border bg-white px-4 py-3 text-center text-2xl tracking-[0.5em]", "focus:border-ews-primary focus:ring-2 focus:ring-ews-primary", invalid ? "border-ews-error" : "border-ews-gray-300") }), jsxRuntime.jsx(Button, { type: "submit", variant: "ews-primary", fullWidth: true, disabled: otp.length !== otpLength || isBusy, children: texts.otpSubmit })] })) : (jsxRuntime.jsx(PinPad, { length: pinLength, onComplete: handlePin, onChange: () => setInvalid(false), disabled: isBusy, invalid: invalid, resetKey: resetKey, eraseLabel: texts.erase, progressLabel: texts.progress, keypadLabel: texts.keypad }, step)), jsxRuntime.jsx("p", { role: "alert", className: "mt-6 min-h-[1.25rem] text-center text-sm text-ews-error", children: shownError }), jsxRuntime.jsxs("div", { className: "mt-auto flex flex-col items-center gap-3 pt-6 sm:mt-4", children: [showForgot && (jsxRuntime.jsx("button", { type: "button", onClick: onForgot, disabled: isBusy, className: "text-base font-medium text-ews-primary hover:underline focus-visible:underline", children: texts.forgot })), onCancel && texts.cancel && (jsxRuntime.jsx("button", { type: "button", onClick: onCancel, disabled: isBusy, className: "text-sm text-ews-gray-600 hover:underline focus-visible:underline", children: texts.cancel }))] })] })) }) }));
|
|
35667
35846
|
};
|
|
35668
35847
|
|
|
35669
35848
|
const SpecialtySearchAutocomplete = ({ selectedSpecialties = [], onSpecialtiesChange, specialties: availableSpecialties, placeholder = "Search and select medical specialties...", className = "", disabled = false, maxSelections, showSelectedCount = true, title = "Medical Specialties", label = "Select Specialties", getSelectedCountText, removeSpecialtyLabel = "Remove specialty", }) => {
|
|
@@ -40163,6 +40342,7 @@ exports.PillBottle = PillBottle;
|
|
|
40163
40342
|
exports.PillBottleIcon = PillBottle;
|
|
40164
40343
|
exports.PillIcon = Pill;
|
|
40165
40344
|
exports.Pin = Pin;
|
|
40345
|
+
exports.PinCodeInput = PinCodeInput;
|
|
40166
40346
|
exports.PinIcon = Pin;
|
|
40167
40347
|
exports.PinOff = PinOff;
|
|
40168
40348
|
exports.PinOffIcon = PinOff;
|
|
@@ -41341,6 +41521,7 @@ exports.parseDateSafe = parseDateSafe;
|
|
|
41341
41521
|
exports.useDebounce = useDebounce;
|
|
41342
41522
|
exports.useDebouncedCallback = useDebouncedCallback;
|
|
41343
41523
|
exports.useIdleLock = useIdleLock;
|
|
41524
|
+
exports.useMediaQuery = useMediaQuery;
|
|
41344
41525
|
exports.useSelectField = useSelectField;
|
|
41345
41526
|
exports.useTheme = useTheme;
|
|
41346
41527
|
//# sourceMappingURL=index.js.map
|