mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 04:14:38 +00:00
feat: added new secret input component and updated toolbar key special prop to innerKey
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
/* eslint-disable react/no-danger */
|
||||
import { HTMLAttributes } from "react";
|
||||
import ContentEditable from "react-contenteditable";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
|
||||
import { useToggle } from "@app/hooks";
|
||||
|
||||
const REGEX = /\${([^}]+)}/g;
|
||||
const stripSpanTags = (str: string) => str.replace(/<\/?span[^>]*>/g, "");
|
||||
const replaceContentWithDot = (str: string) => {
|
||||
let finalStr = "";
|
||||
let isHtml = false;
|
||||
for (let i = 0; i < str.length; i += 1) {
|
||||
const char = str.at(i);
|
||||
|
||||
if (char === "<" || char === ">") {
|
||||
isHtml = char === "<";
|
||||
finalStr += char;
|
||||
} else if (!isHtml && char !== "\n") {
|
||||
finalStr += "•";
|
||||
} else {
|
||||
finalStr += char;
|
||||
}
|
||||
}
|
||||
return finalStr;
|
||||
};
|
||||
|
||||
const syntaxHighlight = (orgContent?: string | null, isVisible?: boolean) => {
|
||||
if (!orgContent) return "";
|
||||
if (!isVisible) return replaceContentWithDot(orgContent);
|
||||
const content = stripSpanTags(orgContent);
|
||||
const newContent = content.replace(
|
||||
REGEX,
|
||||
(_a, b) =>
|
||||
`<span class="ph-no-capture text-yellow">${<span class="ph-no-capture text-yello-200/80">${b}</span>}</span>`
|
||||
);
|
||||
|
||||
return newContent;
|
||||
};
|
||||
|
||||
const sanitizeConf = {
|
||||
allowedTags: ["div", "span", "br", "p"]
|
||||
};
|
||||
|
||||
type Props = Omit<HTMLAttributes<HTMLDivElement>, "onChange" | "onBlur"> & {
|
||||
value?: string | null;
|
||||
isVisible?: boolean;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (val: string, html: string) => void;
|
||||
onBlur?: (sanitizedHtml: string) => void;
|
||||
};
|
||||
|
||||
export const SecretInput = ({
|
||||
value,
|
||||
isVisible,
|
||||
onChange,
|
||||
onBlur,
|
||||
isDisabled,
|
||||
...props
|
||||
}: Props) => {
|
||||
const [isSecretFocused, setIsSecretFocused] = useToggle();
|
||||
|
||||
return (
|
||||
<div
|
||||
className="thin-scrollbar relative overflow-y-auto overflow-x-hidden"
|
||||
style={{ maxHeight: `${21 * 7}px` }}
|
||||
>
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: syntaxHighlight(value, isVisible || isSecretFocused)
|
||||
}}
|
||||
className="absolute top-0 left-0 z-0 h-full w-full text-ellipsis whitespace-pre-line break-all"
|
||||
/>
|
||||
<ContentEditable
|
||||
className="relative z-10 h-full w-full text-ellipsis whitespace-pre-line break-all text-transparent caret-white outline-none"
|
||||
role="textbox"
|
||||
onChange={(evt) => {
|
||||
if (onChange) onChange(evt.currentTarget.innerText.trim(), evt.currentTarget.innerHTML);
|
||||
}}
|
||||
onFocus={() => setIsSecretFocused.on()}
|
||||
disabled={isDisabled}
|
||||
spellCheck={false}
|
||||
onBlur={(evt) => {
|
||||
if (onBlur) onBlur(sanitizeHtml(evt.currentTarget.innerHTML || "", sanitizeConf));
|
||||
setIsSecretFocused.off();
|
||||
}}
|
||||
html={isVisible || isSecretFocused ? value || "" : syntaxHighlight(value, false)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { SecretInput } from "./SecretInput";
|
||||
@@ -52,7 +52,7 @@ export const Loading: Story = {
|
||||
</Tr>
|
||||
</THead>
|
||||
<TBody>
|
||||
<TableSkeleton columns={3} key="story-book-table" />
|
||||
<TableSkeleton columns={3} innerKey="story-book-table" />
|
||||
</TBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
@@ -33,10 +33,7 @@ export type TableProps = {
|
||||
|
||||
export const Table = ({ children, className }: TableProps): JSX.Element => (
|
||||
<table
|
||||
className={twMerge(
|
||||
"w-full bg-mineshaft-800 p-2 text-left text-sm text-gray-300",
|
||||
className
|
||||
)}
|
||||
className={twMerge("w-full bg-mineshaft-800 p-2 text-left text-sm text-gray-300", className)}
|
||||
>
|
||||
{children}
|
||||
</table>
|
||||
@@ -58,11 +55,24 @@ export const THead = ({ children, className }: THeadProps): JSX.Element => (
|
||||
export type TrProps = {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
isHoverable?: boolean;
|
||||
isSelectable?: boolean;
|
||||
} & HTMLAttributes<HTMLTableRowElement>;
|
||||
|
||||
export const Tr = ({ children, className, ...props }: TrProps): JSX.Element => (
|
||||
export const Tr = ({
|
||||
children,
|
||||
className,
|
||||
isHoverable,
|
||||
isSelectable,
|
||||
...props
|
||||
}: TrProps): JSX.Element => (
|
||||
<tr
|
||||
className={twMerge("border border-solid border-mineshaft-700 cursor-default", className)}
|
||||
className={twMerge(
|
||||
"cursor-default border border-solid border-mineshaft-700",
|
||||
isHoverable && "hover:bg-mineshaft-600",
|
||||
isSelectable && "cursor-pointer",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
@@ -76,7 +86,14 @@ export type ThProps = {
|
||||
};
|
||||
|
||||
export const Th = ({ children, className }: ThProps): JSX.Element => (
|
||||
<th className={twMerge("bg-mineshaft-800 px-5 pt-4 pb-3.5 font-semibold border-b-2 border-mineshaft-600", className)}>{children}</th>
|
||||
<th
|
||||
className={twMerge(
|
||||
"border-b-2 border-mineshaft-600 bg-mineshaft-800 px-5 pt-4 pb-3.5 font-semibold",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</th>
|
||||
);
|
||||
|
||||
// table body
|
||||
@@ -106,15 +123,15 @@ export type TBodyLoader = {
|
||||
columns: number;
|
||||
className?: string;
|
||||
// unique key for mapping
|
||||
key: string;
|
||||
innerKey: string;
|
||||
};
|
||||
|
||||
export const TableSkeleton = ({ rows = 3, columns, key, className }: TBodyLoader): JSX.Element => (
|
||||
export const TableSkeleton = ({ rows = 3, columns, innerKey, className }: TBodyLoader): JSX.Element => (
|
||||
<>
|
||||
{Array.apply(0, Array(rows)).map((_x, i) => (
|
||||
<Tr key={`${key}-skeleton-rows-${i + 1}`}>
|
||||
<Tr key={`${innerKey}-skeleton-rows-${i + 1}`}>
|
||||
{Array.apply(0, Array(columns)).map((_y, j) => (
|
||||
<Td key={`${key}-skeleton-rows-${i + 1}-column-${j + 1}`}>
|
||||
<Td key={`${innerKey}-skeleton-rows-${i + 1}-column-${j + 1}`}>
|
||||
<Skeleton className={className} />
|
||||
</Td>
|
||||
))}
|
||||
|
||||
@@ -4,7 +4,7 @@ export * from "./Checkbox";
|
||||
export * from "./DeleteActionModal";
|
||||
export * from "./Drawer";
|
||||
export * from "./Dropdown";
|
||||
export * from "./EmailServiceSetupModal"
|
||||
export * from "./EmailServiceSetupModal";
|
||||
export * from "./EmptyState";
|
||||
export * from "./FormControl";
|
||||
export * from "./HoverCardv2";
|
||||
@@ -13,6 +13,7 @@ export * from "./Input";
|
||||
export * from "./Menu";
|
||||
export * from "./Modal";
|
||||
export * from "./Popoverv2";
|
||||
export * from "./SecretInput";
|
||||
export * from "./Select";
|
||||
export * from "./Skeleton";
|
||||
export * from "./Spinner";
|
||||
|
||||
Reference in New Issue
Block a user