mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 18:27:19 +00:00
feat(multi-line): added support for multi-line in ui
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
export { useLeaveConfirm } from "./useLeaveConfirm";
|
export { useLeaveConfirm } from "./useLeaveConfirm";
|
||||||
export { usePersistentState } from "./usePersistentState";
|
export { usePersistentState } from "./usePersistentState";
|
||||||
export { usePopUp } from "./usePopUp";
|
export { usePopUp } from "./usePopUp";
|
||||||
|
export { useSyntaxHighlight } from "./useSyntaxHighlight";
|
||||||
export { useToggle } from "./useToggle";
|
export { useToggle } from "./useToggle";
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { faCircle } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
|
||||||
|
const REGEX = /([$]{.*?})/g;
|
||||||
|
|
||||||
|
export const useSyntaxHighlight = () => {
|
||||||
|
const syntaxHighlight = useCallback((text: string, isHidden?: boolean) => {
|
||||||
|
if (isHidden) {
|
||||||
|
return text
|
||||||
|
.split("")
|
||||||
|
.slice(0, 200)
|
||||||
|
.map((el, i) =>
|
||||||
|
el === "\n" ? (
|
||||||
|
el
|
||||||
|
) : (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
key={`${text}_${el}_${i + 1}`}
|
||||||
|
className="mr-0.5 text-xxs"
|
||||||
|
icon={faCircle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append a space on last new line this to show new line in ui for code component
|
||||||
|
const val = text.at(-1) === "\n" ? text.concat(" ") : text;
|
||||||
|
if (val?.length === 0) return <span className="font-mono text-bunker-400/80">EMPTY</span>;
|
||||||
|
return val?.split(REGEX).map((word, i) =>
|
||||||
|
word.match(REGEX) !== null ? (
|
||||||
|
<span className="ph-no-capture text-yellow" key={`${val}-${i + 1}`}>
|
||||||
|
${
|
||||||
|
<span className="ph-no-capture text-yellow-200/80">{word.slice(2, word.length - 1)}</span>
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span key={`${word}_${i + 1}`} className="ph-no-capture">
|
||||||
|
{word}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return syntaxHighlight;
|
||||||
|
};
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
/* eslint-disable react/jsx-no-useless-fragment */
|
/* eslint-disable react/jsx-no-useless-fragment */
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useRef, useState } from "react";
|
||||||
import { faCircle, faEye, faEyeSlash, faKey, faMinus } from "@fortawesome/free-solid-svg-icons";
|
import { faEye, faEyeSlash, faKey, faMinus } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { useToggle } from "~/hooks/useToggle";
|
||||||
|
import { useSyntaxHighlight } from "@app/hooks";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
secrets: any[] | undefined;
|
secrets: any[] | undefined;
|
||||||
@@ -12,7 +13,8 @@ type Props = {
|
|||||||
userAvailableEnvs?: any[];
|
userAvailableEnvs?: any[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const REGEX = /([$]{.*?})/g;
|
const SEC_VAL_LINE_HEIGHT = 21;
|
||||||
|
const MAX_MULTI_LINE = 6;
|
||||||
|
|
||||||
const DashboardInput = ({
|
const DashboardInput = ({
|
||||||
isOverridden,
|
isOverridden,
|
||||||
@@ -25,84 +27,54 @@ const DashboardInput = ({
|
|||||||
isReadOnly?: boolean;
|
isReadOnly?: boolean;
|
||||||
secret?: any;
|
secret?: any;
|
||||||
}): JSX.Element => {
|
}): JSX.Element => {
|
||||||
const syntaxHighlight = useCallback((val: string) => {
|
const ref = useRef<HTMLElement | null>(null);
|
||||||
if (val === undefined)
|
const [isFocused, setIsFocused] = useToggle();
|
||||||
return (
|
const syntaxHighlight = useSyntaxHighlight();
|
||||||
<span className="cursor-default font-sans text-xs italic text-red-500/80">
|
|
||||||
<FontAwesomeIcon icon={faMinus} className="mt-1" />
|
const value = isOverridden ? secret.valueOverride : secret?.value || "";
|
||||||
</span>
|
const multilineExpandUnit = ((value?.match(/\n/g)?.length || 0) + 1) * SEC_VAL_LINE_HEIGHT;
|
||||||
);
|
const maxMultilineHeight = Math.min(multilineExpandUnit, 21 * MAX_MULTI_LINE);
|
||||||
if (val?.length === 0)
|
|
||||||
return <span className="w-full font-sans text-bunker-400/80">EMPTY</span>;
|
|
||||||
return val?.split(REGEX).map((word, index) =>
|
|
||||||
word.match(REGEX) !== null ? (
|
|
||||||
<span className="ph-no-capture text-yellow" key={`${val}-${index + 1}`}>
|
|
||||||
{word.slice(0, 2)}
|
|
||||||
<span className="ph-no-capture text-yellow-200/80">{word.slice(2, word.length - 1)}</span>
|
|
||||||
{word.slice(word.length - 1, word.length) === "}" ? (
|
|
||||||
<span className="ph-no-capture text-yellow">
|
|
||||||
{word.slice(word.length - 1, word.length)}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span className="ph-no-capture text-yellow-400">
|
|
||||||
{word.slice(word.length - 1, word.length)}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span key={word} className="ph-no-capture">
|
|
||||||
{word}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<td
|
<td
|
||||||
key={`row-${secret?.key || ""}--`}
|
key={`row-${secret?.key || ""}--`}
|
||||||
className={`flex h-10 w-full cursor-default flex-row items-center justify-center ${
|
className={`flex w-full cursor-default flex-row ${
|
||||||
!(secret?.value || secret?.value === "") ? "bg-red-800/10" : "bg-mineshaft-900/30"
|
!(secret?.value || secret?.value === "") ? "bg-red-800/10" : "bg-mineshaft-900/30"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="group relative flex w-full cursor-default flex-col justify-center whitespace-pre">
|
<div className="group relative flex w-full flex-col whitespace-pre px-1.5 pt-1.5">
|
||||||
<input
|
<textarea
|
||||||
value={isOverridden ? secret.valueOverride : secret?.value || ""}
|
|
||||||
readOnly={isReadOnly}
|
readOnly={isReadOnly}
|
||||||
className={twMerge(
|
value={value}
|
||||||
"ph-no-capture no-scrollbar::-webkit-scrollbar duration-50 peer z-10 w-full cursor-default bg-transparent px-2 py-2 font-mono text-sm text-transparent caret-transparent outline-none no-scrollbar",
|
className="ph-no-capture min-w-16 duration-50 peer z-20 w-full resize-none overflow-auto text-ellipsis bg-transparent px-2 font-mono text-sm text-transparent caret-white outline-none no-scrollbar"
|
||||||
isSecretValueHidden && "text-transparent focus:text-transparent active:text-transparent"
|
style={{ height: `${maxMultilineHeight}px` }}
|
||||||
)}
|
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
|
onBlur={() => setIsFocused.off()}
|
||||||
|
onFocus={() => setIsFocused.on()}
|
||||||
|
onInput={(el) => {
|
||||||
|
if (ref.current) {
|
||||||
|
ref.current.scrollTop = el.currentTarget.scrollTop;
|
||||||
|
ref.current.scrollLeft = el.currentTarget.scrollLeft;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onScroll={(el) => {
|
||||||
|
if (ref.current) {
|
||||||
|
ref.current.scrollTop = el.currentTarget.scrollTop;
|
||||||
|
ref.current.scrollLeft = el.currentTarget.scrollLeft;
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<div
|
<pre className="whitespace-pre-wrap break-words">
|
||||||
className={twMerge(
|
<code
|
||||||
"ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar duration-50 absolute z-0 mt-0.5 flex h-10 w-full cursor-default flex-row overflow-x-scroll whitespace-pre bg-transparent px-2 py-2 font-mono text-sm outline-none no-scrollbar peer-focus:visible",
|
ref={ref}
|
||||||
isSecretValueHidden && secret?.value ? "invisible" : "visible",
|
className={`absolute top-1.5 left-3.5 z-10 overflow-auto font-mono text-sm transition-all no-scrollbar ${
|
||||||
isSecretValueHidden &&
|
isOverridden && "text-primary-300"
|
||||||
secret?.value &&
|
}`}
|
||||||
"duration-50 text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400",
|
style={{ height: `${maxMultilineHeight}px`, width: "calc(100% - 12px)" }}
|
||||||
!secret?.value && "justify-center text-bunker-400"
|
>
|
||||||
)}
|
{syntaxHighlight(value || "", isSecretValueHidden ? !isFocused : isSecretValueHidden)}
|
||||||
>
|
</code>
|
||||||
{syntaxHighlight(secret?.value)}
|
</pre>
|
||||||
</div>
|
|
||||||
{isSecretValueHidden && secret?.value && (
|
|
||||||
<div className="duration-50 peer absolute z-0 flex h-10 w-full flex-row items-center justify-between text-clip pr-2 text-bunker-400 group-hover:bg-white/[0.00] peer-focus:hidden peer-active:hidden">
|
|
||||||
<div className="no-scrollbar::-webkit-scrollbar flex flex-row items-center overflow-x-scroll px-2 no-scrollbar">
|
|
||||||
{(isOverridden ? secret.valueOverride : secret?.value || "")
|
|
||||||
?.split("")
|
|
||||||
.map((_a: string, index: number) => (
|
|
||||||
<FontAwesomeIcon
|
|
||||||
key={`${secret?.value}_${index + 1}`}
|
|
||||||
className="mr-0.5 text-xxs"
|
|
||||||
icon={faCircle}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{(isOverridden ? secret.valueOverride : secret?.value || "")?.split("").length ===
|
|
||||||
0 && <span className="text-sm text-bunker-400/80">EMPTY</span>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
@@ -122,14 +94,14 @@ export const EnvComparisonRow = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className="group flex min-w-full flex-row items-center hover:bg-mineshaft-800">
|
<tr className="group flex min-w-full flex-row hover:bg-mineshaft-800">
|
||||||
<td className="flex h-10 w-10 items-center justify-center border-none px-4">
|
<td className="flex w-10 justify-center border-none px-4">
|
||||||
<div className="w-10 text-center text-xs text-bunker-400">
|
<div className="flex h-8 w-10 items-center justify-center text-center text-xs text-bunker-400">
|
||||||
<FontAwesomeIcon icon={faKey} />
|
<FontAwesomeIcon icon={faKey} />
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="flex h-full min-w-[200px] flex-row items-center justify-between lg:min-w-[220px] xl:min-w-[250px]">
|
<td className="flex min-w-[200px] flex-row justify-between lg:min-w-[220px] xl:min-w-[250px]">
|
||||||
<div className="flex h-8 cursor-default flex-row items-center truncate">
|
<div className="flex h-8 cursor-default flex-row items-center justify-center truncate">
|
||||||
{secrets![0].key || ""}
|
{secrets![0].key || ""}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { useCallback } from "react";
|
import { useRef } from "react";
|
||||||
import { useFormContext, useWatch } from "react-hook-form";
|
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
||||||
import { faCircle } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
|
|
||||||
import { FormData } from "../../DashboardPage.utils";
|
import { FormData } from "../../DashboardPage.utils";
|
||||||
|
import { useSyntaxHighlight, useToggle } from "@app/hooks";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isReadOnly?: boolean;
|
isReadOnly?: boolean;
|
||||||
@@ -13,95 +11,94 @@ type Props = {
|
|||||||
index: number;
|
index: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const REGEX = /([$]{.*?})/g;
|
const SEC_VAL_LINE_HEIGHT = 21;
|
||||||
|
const MAX_MULTI_LINE = 6;
|
||||||
|
|
||||||
export const MaskedInput = ({ isReadOnly, isSecretValueHidden, index, isOverridden }: Props) => {
|
export const MaskedInput = ({ isReadOnly, isSecretValueHidden, index, isOverridden }: Props) => {
|
||||||
const { register, control } = useFormContext<FormData>();
|
const { control } = useFormContext<FormData>();
|
||||||
|
const ref = useRef<HTMLElement | null>(null);
|
||||||
|
const [isFocused, setIsFocused] = useToggle();
|
||||||
|
const syntaxHighlight = useSyntaxHighlight();
|
||||||
|
|
||||||
const secretValue = useWatch({ control, name: `secrets.${index}.value` });
|
const secretValue = useWatch({ control, name: `secrets.${index}.value` });
|
||||||
const secretValueOverride = useWatch({ control, name: `secrets.${index}.valueOverride` });
|
const secretValueOverride = useWatch({ control, name: `secrets.${index}.valueOverride` });
|
||||||
const value = isOverridden ? secretValueOverride : secretValue;
|
const value = isOverridden ? secretValueOverride : secretValue;
|
||||||
|
|
||||||
const syntaxHighlight = useCallback((val: string) => {
|
const multilineExpandUnit = ((value?.match(/\n/g)?.length || 0) + 1) * SEC_VAL_LINE_HEIGHT;
|
||||||
if (val?.length === 0) return <span className="font-sans text-bunker-400/80">EMPTY</span>;
|
const maxMultilineHeight = Math.min(multilineExpandUnit, 21 * MAX_MULTI_LINE);
|
||||||
return val?.split(REGEX).map((word, i) =>
|
|
||||||
word.match(REGEX) !== null ? (
|
|
||||||
<span className="ph-no-capture text-yellow" key={`${val}-${i + 1}`}>
|
|
||||||
{word.slice(0, 2)}
|
|
||||||
<span className="ph-no-capture text-yellow-200/80">{word.slice(2, word.length - 1)}</span>
|
|
||||||
{word.slice(word.length - 1, word.length) === "}" ? (
|
|
||||||
<span className="ph-no-capture text-yellow">
|
|
||||||
{word.slice(word.length - 1, word.length)}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span className="ph-no-capture text-yellow-400">
|
|
||||||
{word.slice(word.length - 1, word.length)}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span key={`${word}_${i + 1}`} className="ph-no-capture">
|
|
||||||
{word}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group relative flex w-full flex-col justify-center whitespace-pre px-1.5">
|
<div className="group relative flex w-full flex-col whitespace-pre px-1.5 pt-1.5">
|
||||||
{isOverridden ? (
|
{isOverridden ? (
|
||||||
<input
|
<Controller
|
||||||
{...register(`secrets.${index}.valueOverride`)}
|
control={control}
|
||||||
readOnly={isReadOnly}
|
name={`secrets.${index}.valueOverride`}
|
||||||
className={twMerge(
|
render={({ field }) => (
|
||||||
"ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar duration-50 peer z-10 w-full bg-transparent px-2 py-2 font-mono text-sm text-transparent caret-white outline-none no-scrollbar",
|
<textarea
|
||||||
!isSecretValueHidden &&
|
key={`secrets.${index}.valueOverride`}
|
||||||
"text-transparent focus:text-transparent active:text-transparent"
|
{...field}
|
||||||
|
readOnly={isReadOnly}
|
||||||
|
className="ph-no-capture min-w-16 duration-50 peer z-20 w-full resize-none overflow-auto text-ellipsis bg-transparent px-2 font-mono text-sm text-transparent caret-white outline-none no-scrollbar"
|
||||||
|
style={{ height: `${maxMultilineHeight}px` }}
|
||||||
|
spellCheck="false"
|
||||||
|
onBlur={() => setIsFocused.off()}
|
||||||
|
onFocus={() => setIsFocused.on()}
|
||||||
|
onInput={(el) => {
|
||||||
|
if (ref.current) {
|
||||||
|
ref.current.scrollTop = el.currentTarget.scrollTop;
|
||||||
|
ref.current.scrollLeft = el.currentTarget.scrollLeft;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onScroll={(el) => {
|
||||||
|
if (ref.current) {
|
||||||
|
ref.current.scrollTop = el.currentTarget.scrollTop;
|
||||||
|
ref.current.scrollLeft = el.currentTarget.scrollLeft;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
spellCheck="false"
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<input
|
<Controller
|
||||||
{...register(`secrets.${index}.value`)}
|
control={control}
|
||||||
readOnly={isReadOnly}
|
name={`secrets.${index}.value`}
|
||||||
className={twMerge(
|
key={`secrets.${index}.value`}
|
||||||
"ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar duration-50 peer z-10 w-full bg-transparent px-2 py-2 font-mono text-sm text-transparent caret-white outline-none no-scrollbar",
|
render={({ field }) => (
|
||||||
!isSecretValueHidden &&
|
<textarea
|
||||||
"text-transparent focus:text-transparent active:text-transparent"
|
{...field}
|
||||||
|
readOnly={isReadOnly}
|
||||||
|
className="ph-no-capture min-w-16 duration-50 peer z-20 w-full resize-none overflow-auto text-ellipsis bg-transparent px-2 font-mono text-sm text-transparent caret-white outline-none no-scrollbar"
|
||||||
|
style={{ height: `${maxMultilineHeight}px` }}
|
||||||
|
spellCheck="false"
|
||||||
|
onBlur={() => setIsFocused.off()}
|
||||||
|
onFocus={() => setIsFocused.on()}
|
||||||
|
onInput={(el) => {
|
||||||
|
if (ref.current) {
|
||||||
|
ref.current.scrollTop = el.currentTarget.scrollTop;
|
||||||
|
ref.current.scrollLeft = el.currentTarget.scrollLeft;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onScroll={(el) => {
|
||||||
|
if (ref.current) {
|
||||||
|
ref.current.scrollTop = el.currentTarget.scrollTop;
|
||||||
|
ref.current.scrollLeft = el.currentTarget.scrollLeft;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
spellCheck="false"
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div
|
<pre className="whitespace-pre-wrap break-words">
|
||||||
className={twMerge(
|
<code
|
||||||
"ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar duration-50 absolute z-0 mt-0.5 flex h-10 w-full flex-row overflow-x-scroll whitespace-pre bg-transparent px-2 py-2 font-mono text-sm outline-none no-scrollbar peer-focus:visible",
|
ref={ref}
|
||||||
isSecretValueHidden ? "invisible" : "visible",
|
className={`absolute top-1.5 left-3.5 z-10 w-full overflow-auto font-mono text-sm transition-all no-scrollbar ${
|
||||||
isOverridden
|
isOverridden && "text-primary-300"
|
||||||
? "text-primary-300"
|
}`}
|
||||||
: "duration-50 text-gray-400 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400"
|
style={{ height: `${maxMultilineHeight}px`, width: "calc(100% - 12px)" }}
|
||||||
)}
|
>
|
||||||
>
|
{syntaxHighlight(value || "", isSecretValueHidden ? !isFocused : isSecretValueHidden)}
|
||||||
{syntaxHighlight(value || "")}
|
</code>
|
||||||
</div>
|
</pre>
|
||||||
<div
|
|
||||||
className={twMerge(
|
|
||||||
"duration-50 peer absolute z-0 flex h-10 w-full flex-row items-center justify-between text-clip pr-2 text-bunker-400 group-hover:bg-white/[0.00] peer-focus:hidden peer-active:hidden",
|
|
||||||
!isSecretValueHidden ? "invisible" : "visible"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="no-scrollbar::-webkit-scrollbar flex flex-row items-center overflow-x-scroll px-2 no-scrollbar">
|
|
||||||
{value?.split("").map((val, i) => (
|
|
||||||
<FontAwesomeIcon
|
|
||||||
key={`${value}_${val}_${i + 1}`}
|
|
||||||
className="mr-0.5 text-xxs"
|
|
||||||
icon={faCircle}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{value?.split("").length === 0 && (
|
|
||||||
<span className="text-sm text-bunker-400/80">EMPTY</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import { WsTag } from "@app/hooks/api/types";
|
|||||||
|
|
||||||
import { FormData, SecretActionType } from "../../DashboardPage.utils";
|
import { FormData, SecretActionType } from "../../DashboardPage.utils";
|
||||||
import { MaskedInput } from "./MaskedInput";
|
import { MaskedInput } from "./MaskedInput";
|
||||||
|
import { useToggle } from "~/hooks/useToggle";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
index: number;
|
index: number;
|
||||||
@@ -157,7 +158,7 @@ export const SecretInputRow = memo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className="group flex flex-row items-center" key={index}>
|
<tr className="group flex flex-row" key={index}>
|
||||||
<td className="flex h-10 w-10 items-center justify-center border-none px-4">
|
<td className="flex h-10 w-10 items-center justify-center border-none px-4">
|
||||||
<div className="w-10 text-center text-xs text-bunker-400">{index + 1}</div>
|
<div className="w-10 text-center text-xs text-bunker-400">{index + 1}</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -198,7 +199,7 @@ export const SecretInputRow = memo(
|
|||||||
</HoverCard>
|
</HoverCard>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<td className="flex h-10 w-full flex-grow flex-row items-center justify-center border-r border-none border-red">
|
<td className="flex w-full flex-grow flex-row border-r border-none border-red">
|
||||||
<MaskedInput
|
<MaskedInput
|
||||||
isReadOnly={
|
isReadOnly={
|
||||||
isReadOnly || isRollbackMode || (isOverridden ? isAddOnly : shouldBeBlockedInAddOnly)
|
isReadOnly || isRollbackMode || (isOverridden ? isAddOnly : shouldBeBlockedInAddOnly)
|
||||||
@@ -208,8 +209,8 @@ export const SecretInputRow = memo(
|
|||||||
index={index}
|
index={index}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="min-w-sm flex h-10 items-center">
|
<td className="min-w-sm flex">
|
||||||
<div className="flex items-center pl-2">
|
<div className="flex h-8 items-center pl-2">
|
||||||
{secretTags.map(({ id, slug }, i) => (
|
{secretTags.map(({ id, slug }, i) => (
|
||||||
<Tag
|
<Tag
|
||||||
className={cx(
|
className={cx(
|
||||||
@@ -289,7 +290,7 @@ export const SecretInputRow = memo(
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-full flex-row items-center pr-2">
|
<div className="flex h-8 flex-row items-center pr-2">
|
||||||
{!isAddOnly && (
|
{!isAddOnly && (
|
||||||
<div>
|
<div>
|
||||||
<Tooltip content="Override with a personal value">
|
<Tooltip content="Override with a personal value">
|
||||||
@@ -346,35 +347,37 @@ export const SecretInputRow = memo(
|
|||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div className="duration-0 flex h-10 w-16 items-center justify-end space-x-2.5 overflow-hidden border-l border-mineshaft-600 transition-all">
|
<div className="duration-0 flex w-16 justify-center overflow-hidden border-l border-mineshaft-600 pl-2 transition-all">
|
||||||
{!isAddOnly && (
|
<div className="flex h-8 items-center space-x-2.5">
|
||||||
|
{!isAddOnly && (
|
||||||
|
<div className="opacity-0 group-hover:opacity-100">
|
||||||
|
<Tooltip content="Settings">
|
||||||
|
<IconButton
|
||||||
|
size="lg"
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="plain"
|
||||||
|
onClick={onRowExpand}
|
||||||
|
ariaLabel="expand"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faEllipsis} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="opacity-0 group-hover:opacity-100">
|
<div className="opacity-0 group-hover:opacity-100">
|
||||||
<Tooltip content="Settings">
|
<Tooltip content="Delete">
|
||||||
<IconButton
|
<IconButton
|
||||||
size="lg"
|
size="md"
|
||||||
colorSchema="primary"
|
|
||||||
variant="plain"
|
variant="plain"
|
||||||
onClick={onRowExpand}
|
colorSchema="danger"
|
||||||
ariaLabel="expand"
|
ariaLabel="delete"
|
||||||
|
isDisabled={isReadOnly || isRollbackMode}
|
||||||
|
onClick={() => onSecretDelete(index, secId, idOverride)}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faEllipsis} />
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
<div className="opacity-0 group-hover:opacity-100">
|
|
||||||
<Tooltip content="Delete">
|
|
||||||
<IconButton
|
|
||||||
size="md"
|
|
||||||
variant="plain"
|
|
||||||
colorSchema="danger"
|
|
||||||
ariaLabel="delete"
|
|
||||||
isDisabled={isReadOnly || isRollbackMode}
|
|
||||||
onClick={() => onSecretDelete(index, secId, idOverride)}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user