mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
75 lines
1.9 KiB
TypeScript
75 lines
1.9 KiB
TypeScript
import * as OneTimePasswordField from "@radix-ui/react-one-time-password-field";
|
|
import * as React from "react";
|
|
import styled from "styled-components";
|
|
import { s } from "@shared/styles";
|
|
|
|
type Props = React.ComponentProps<typeof OneTimePasswordRoot> & {
|
|
/** The length of the OTP */
|
|
length?: number;
|
|
/**
|
|
* Whether to accept uppercase letters in addition to digits. Lowercase input
|
|
* is normalized to uppercase. Defaults to numeric only.
|
|
*/
|
|
alphanumeric?: boolean;
|
|
};
|
|
|
|
const sanitizeAlphanumeric = (value: string) =>
|
|
value.replace(/[^a-zA-Z0-9]/g, "").toUpperCase();
|
|
|
|
export const OneTimePasswordInput = React.forwardRef(
|
|
function OneTimePasswordInput_(
|
|
{ length = 6, alphanumeric, ...rest }: Props,
|
|
ref: React.RefObject<HTMLInputElement>
|
|
) {
|
|
const alphanumericProps = alphanumeric
|
|
? {
|
|
validationType: "none" as const,
|
|
sanitizeValue: sanitizeAlphanumeric,
|
|
}
|
|
: undefined;
|
|
|
|
return (
|
|
<OneTimePasswordRoot {...alphanumericProps} {...rest}>
|
|
{Array.from({ length }, (_, i) => (
|
|
<OneTimePasswordInputField key={i} />
|
|
))}
|
|
<OneTimePasswordField.HiddenInput ref={ref} />
|
|
</OneTimePasswordRoot>
|
|
);
|
|
}
|
|
);
|
|
|
|
const OneTimePasswordRoot = styled(OneTimePasswordField.Root)`
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
flex-wrap: nowrap;
|
|
justify-content: space-between;
|
|
`;
|
|
|
|
const OneTimePasswordInputField = styled(OneTimePasswordField.Input)`
|
|
all: unset;
|
|
box-sizing: border-box;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
text-align: center;
|
|
border-radius: 4px;
|
|
font-size: 15px;
|
|
color: ${s("text")};
|
|
background: ${s("background")};
|
|
box-shadow: 0 0 0 1px ${s("inputBorder")};
|
|
padding: 0;
|
|
height: 38px;
|
|
width: 38px;
|
|
line-height: 1;
|
|
transition: box-shadow 0.1s ease-in-out;
|
|
|
|
&:focus {
|
|
box-shadow: 0 0 0 2px ${s("inputBorderFocused")};
|
|
}
|
|
&::selection {
|
|
background-color: ${s("background")};
|
|
color: ${s("text")};
|
|
}
|
|
`;
|