mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 00:26:40 +00:00
Feat: Clear select option
This commit is contained in:
@@ -36,61 +36,73 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
|
|||||||
ref
|
ref
|
||||||
): JSX.Element => {
|
): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<SelectPrimitive.Root {...props} disabled={isDisabled}>
|
<div className="flex items-center space-x-2">
|
||||||
<SelectPrimitive.Trigger
|
<SelectPrimitive.Root
|
||||||
ref={ref}
|
{...props}
|
||||||
className={twMerge(
|
onValueChange={(value) => {
|
||||||
`inline-flex items-center justify-between rounded-md
|
if (!props.onValueChange) return;
|
||||||
bg-mineshaft-900 px-3 py-2 font-inter text-sm font-normal text-bunker-200 outline-none focus:bg-mineshaft-700/80 data-[placeholder]:text-mineshaft-200`,
|
|
||||||
className,
|
|
||||||
isDisabled && "cursor-not-allowed opacity-50"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<SelectPrimitive.Value placeholder={placeholder}>
|
|
||||||
{props.icon ? <FontAwesomeIcon icon={props.icon} /> : placeholder}
|
|
||||||
</SelectPrimitive.Value>
|
|
||||||
|
|
||||||
<SelectPrimitive.Icon className="ml-3">
|
const newValue = value === "EMPTY-VALUE" ? "" : value;
|
||||||
<FontAwesomeIcon
|
props.onValueChange(newValue);
|
||||||
icon={faCaretDown}
|
}}
|
||||||
size="sm"
|
disabled={isDisabled}
|
||||||
className={twMerge(isDisabled && "opacity-30")}
|
>
|
||||||
/>
|
<SelectPrimitive.Trigger
|
||||||
</SelectPrimitive.Icon>
|
ref={ref}
|
||||||
</SelectPrimitive.Trigger>
|
|
||||||
<SelectPrimitive.Portal>
|
|
||||||
<SelectPrimitive.Content
|
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"relative top-1 z-[100] overflow-hidden rounded-md border border-mineshaft-600 bg-mineshaft-900 font-inter text-bunker-100 shadow-md",
|
`inline-flex items-center justify-between rounded-md
|
||||||
position === "popper" && "max-h-72",
|
bg-mineshaft-900 px-3 py-2 font-inter text-sm font-normal text-bunker-200 outline-none focus:bg-mineshaft-700/80 data-[placeholder]:text-mineshaft-200`,
|
||||||
dropdownContainerClassName
|
className,
|
||||||
|
isDisabled && "cursor-not-allowed opacity-50"
|
||||||
)}
|
)}
|
||||||
position={position}
|
|
||||||
style={{ width: "var(--radix-select-trigger-width)" }}
|
|
||||||
>
|
>
|
||||||
<SelectPrimitive.ScrollUpButton>
|
<div className="flex items-center space-x-2">
|
||||||
<div className="flex items-center justify-center">
|
{props.icon && <FontAwesomeIcon icon={props.icon} />}
|
||||||
<FontAwesomeIcon icon={faCaretUp} size="sm" />
|
<SelectPrimitive.Value placeholder={placeholder} />
|
||||||
</div>
|
</div>
|
||||||
</SelectPrimitive.ScrollUpButton>
|
|
||||||
<SelectPrimitive.Viewport className="p-1">
|
<SelectPrimitive.Icon className="ml-3">
|
||||||
{isLoading ? (
|
<FontAwesomeIcon
|
||||||
<div className="flex items-center justify-center">
|
icon={faCaretDown}
|
||||||
<Spinner size="xs" />
|
size="sm"
|
||||||
<span className="ml-2 text-xs text-gray-500">Loading...</span>
|
className={twMerge(isDisabled && "opacity-30")}
|
||||||
</div>
|
/>
|
||||||
) : (
|
</SelectPrimitive.Icon>
|
||||||
children
|
</SelectPrimitive.Trigger>
|
||||||
|
<SelectPrimitive.Portal>
|
||||||
|
<SelectPrimitive.Content
|
||||||
|
className={twMerge(
|
||||||
|
"relative top-1 z-[100] overflow-hidden rounded-md border border-mineshaft-600 bg-mineshaft-900 font-inter text-bunker-100 shadow-md",
|
||||||
|
position === "popper" && "max-h-72",
|
||||||
|
dropdownContainerClassName
|
||||||
)}
|
)}
|
||||||
</SelectPrimitive.Viewport>
|
position={position}
|
||||||
<SelectPrimitive.ScrollDownButton>
|
style={{ width: "var(--radix-select-trigger-width)" }}
|
||||||
<div className="flex items-center justify-center">
|
>
|
||||||
<FontAwesomeIcon icon={faCaretDown} size="sm" />
|
<SelectPrimitive.ScrollUpButton>
|
||||||
</div>
|
<div className="flex items-center justify-center">
|
||||||
</SelectPrimitive.ScrollDownButton>
|
<FontAwesomeIcon icon={faCaretUp} size="sm" />
|
||||||
</SelectPrimitive.Content>
|
</div>
|
||||||
</SelectPrimitive.Portal>
|
</SelectPrimitive.ScrollUpButton>
|
||||||
</SelectPrimitive.Root>
|
<SelectPrimitive.Viewport className="p-1">
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<Spinner size="xs" />
|
||||||
|
<span className="ml-2 text-xs text-gray-500">Loading...</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
)}
|
||||||
|
</SelectPrimitive.Viewport>
|
||||||
|
<SelectPrimitive.ScrollDownButton>
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<FontAwesomeIcon icon={faCaretDown} size="sm" />
|
||||||
|
</div>
|
||||||
|
</SelectPrimitive.ScrollDownButton>
|
||||||
|
</SelectPrimitive.Content>
|
||||||
|
</SelectPrimitive.Portal>
|
||||||
|
</SelectPrimitive.Root>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -114,7 +126,7 @@ export const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
|
|||||||
outline-none transition-all hover:bg-mineshaft-500 data-[highlighted]:bg-mineshaft-700/80`,
|
outline-none transition-all hover:bg-mineshaft-500 data-[highlighted]:bg-mineshaft-700/80`,
|
||||||
isSelected && "bg-primary",
|
isSelected && "bg-primary",
|
||||||
isDisabled &&
|
isDisabled &&
|
||||||
"cursor-not-allowed text-gray-600 hover:bg-transparent hover:text-mineshaft-600",
|
"cursor-not-allowed text-gray-600 hover:bg-transparent hover:text-mineshaft-600",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
ref={forwardedRef}
|
ref={forwardedRef}
|
||||||
@@ -129,3 +141,45 @@ export const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
SelectItem.displayName = "SelectItem";
|
SelectItem.displayName = "SelectItem";
|
||||||
|
|
||||||
|
export type SelectClearProps = Omit<SelectItemProps, "disabled" | "value"> & {
|
||||||
|
onClear: () => void;
|
||||||
|
selectValue: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SelectClear = forwardRef<HTMLDivElement, SelectClearProps>(
|
||||||
|
(
|
||||||
|
{ children, className, isSelected, isDisabled, onClear, selectValue, ...props },
|
||||||
|
forwardedRef
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.Item
|
||||||
|
{...props}
|
||||||
|
value="EMPTY-VALUE"
|
||||||
|
onSelect={() => onClear()}
|
||||||
|
onClick={() => onClear()}
|
||||||
|
className={twMerge(
|
||||||
|
`relative mb-0.5 flex
|
||||||
|
cursor-pointer select-none items-center rounded-md py-2 pl-10 pr-4 text-sm
|
||||||
|
outline-none transition-all hover:bg-mineshaft-500 data-[highlighted]:bg-mineshaft-700/80`,
|
||||||
|
isSelected && "bg-primary",
|
||||||
|
isDisabled &&
|
||||||
|
"cursor-not-allowed text-gray-600 hover:bg-transparent hover:text-mineshaft-600",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
ref={forwardedRef}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={twMerge(
|
||||||
|
"absolute left-3.5 text-primary",
|
||||||
|
selectValue === "" ? "visible" : "hidden"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCheck} />
|
||||||
|
</div>
|
||||||
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||||
|
</SelectPrimitive.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
SelectClear.displayName = "SelectClear";
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export type { SelectItemProps, SelectProps } from "./Select";
|
export type { SelectItemProps, SelectProps } from "./Select";
|
||||||
export { Select, SelectItem } from "./Select";
|
export { Select, SelectClear, SelectItem } from "./Select";
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
|
SelectClear,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
Switch,
|
Switch,
|
||||||
Tab,
|
Tab,
|
||||||
@@ -68,7 +69,8 @@ export const AdminDashboardPage = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const signupMode = watch("signUpMode");
|
const signUpMode = watch("signUpMode");
|
||||||
|
const defaultAuthOrgId = watch("defaultAuthOrgId");
|
||||||
|
|
||||||
const { user, isLoading: isUserLoading } = useUser();
|
const { user, isLoading: isUserLoading } = useUser();
|
||||||
const { orgs } = useOrganization();
|
const { orgs } = useOrganization();
|
||||||
@@ -90,14 +92,7 @@ export const AdminDashboardPage = () => {
|
|||||||
|
|
||||||
const onFormSubmit = async (formData: TDashboardForm) => {
|
const onFormSubmit = async (formData: TDashboardForm) => {
|
||||||
try {
|
try {
|
||||||
const {
|
const { allowedSignUpDomain, trustSamlEmails, trustLdapEmails, trustOidcEmails } = formData;
|
||||||
signUpMode,
|
|
||||||
allowedSignUpDomain,
|
|
||||||
trustSamlEmails,
|
|
||||||
trustLdapEmails,
|
|
||||||
trustOidcEmails,
|
|
||||||
defaultAuthOrgId
|
|
||||||
} = formData;
|
|
||||||
|
|
||||||
await updateServerConfig({
|
await updateServerConfig({
|
||||||
defaultAuthOrgId: defaultAuthOrgId || null,
|
defaultAuthOrgId: defaultAuthOrgId || null,
|
||||||
@@ -175,7 +170,7 @@ export const AdminDashboardPage = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{signupMode === "anyone" && (
|
{signUpMode === "anyone" && (
|
||||||
<div className="flex flex-col justify-start">
|
<div className="flex flex-col justify-start">
|
||||||
<div className="mb-4 text-xl font-semibold text-mineshaft-100">
|
<div className="mb-4 text-xl font-semibold text-mineshaft-100">
|
||||||
Restrict signup by email domain(s)
|
Restrict signup by email domain(s)
|
||||||
@@ -222,19 +217,22 @@ export const AdminDashboardPage = () => {
|
|||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
|
placeholder="Allow all organizations"
|
||||||
className="w-full bg-mineshaft-700"
|
className="w-full bg-mineshaft-700"
|
||||||
dropdownContainerClassName="bg-mineshaft-800"
|
dropdownContainerClassName="bg-mineshaft-800"
|
||||||
defaultValue={field.value ?? " "}
|
defaultValue={field.value ?? " "}
|
||||||
onValueChange={(e) => {
|
onValueChange={(e) => onChange(e)}
|
||||||
if (e === "EMPTY") {
|
|
||||||
onChange("");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onChange(e);
|
|
||||||
}}
|
|
||||||
{...field}
|
{...field}
|
||||||
>
|
>
|
||||||
<SelectItem value="EMPTY">Select organization...</SelectItem>
|
<SelectClear
|
||||||
|
selectValue={defaultAuthOrgId}
|
||||||
|
onClear={() => {
|
||||||
|
console.log("clearing");
|
||||||
|
onChange("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Allow all organizations
|
||||||
|
</SelectClear>
|
||||||
{organizations.data?.map((org) => (
|
{organizations.data?.map((org) => (
|
||||||
<SelectItem key={org.id} value={org.id}>
|
<SelectItem key={org.id} value={org.id}>
|
||||||
{org.name}
|
{org.name}
|
||||||
|
|||||||
Reference in New Issue
Block a user