mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 15:40:47 +00:00
feat: renamed old frontend and fixed some minor bugs
This commit is contained in:
@@ -1,30 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./Accordion";
|
||||
|
||||
const meta: Meta<typeof Accordion> = {
|
||||
title: "Components/Accordion",
|
||||
component: Accordion,
|
||||
tags: ["v2"],
|
||||
argTypes: {}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Accordion>;
|
||||
|
||||
export const Basic: Story = {
|
||||
render: (args) => (
|
||||
<div className="flex w-full justify-center">
|
||||
<Accordion {...args}>
|
||||
<AccordionItem value="section-1">
|
||||
<AccordionTrigger>Section 1</AccordionTrigger>
|
||||
<AccordionContent>Description of Section 1</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="section-2">
|
||||
<AccordionTrigger>Section 2</AccordionTrigger>
|
||||
<AccordionContent>Description of Section 2</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
@@ -27,7 +27,7 @@ export const AccordionTrigger = forwardRef<
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
className={twMerge(
|
||||
"group flex h-11 flex-1 items-center justify-between py-2 px-4 outline-none hover:text-primary data-[state=open]:text-primary",
|
||||
"group flex h-11 flex-1 items-center justify-between px-4 py-2 outline-none hover:text-primary data-[state=open]:text-primary",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -51,13 +51,13 @@ export const AccordionContent = forwardRef<
|
||||
>(({ children, className, ...props }, forwardedRef) => (
|
||||
<AccordionPrimitive.Content
|
||||
className={twMerge(
|
||||
"overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp",
|
||||
"overflow-hidden data-[state=closed]:animate-slideUp data-[state=open]:animate-slideDown",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={forwardedRef}
|
||||
>
|
||||
<div className="py-2 px-4 text-sm">{children}</div>
|
||||
<div className="px-4 py-2 text-sm">{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
));
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Alert, AlertDescription } from "./Alert";
|
||||
|
||||
const meta: Meta<typeof Alert> = {
|
||||
title: "Components/Alert",
|
||||
component: Alert,
|
||||
tags: ["v2"]
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof Alert>;
|
||||
|
||||
const ExampleComponent = () => <AlertDescription>this is a description</AlertDescription>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: <ExampleComponent />
|
||||
}
|
||||
};
|
||||
|
||||
export const Warning: Story = {
|
||||
args: {
|
||||
children: <ExampleComponent />,
|
||||
variant: "warning"
|
||||
}
|
||||
};
|
||||
|
||||
export const Danger: Story = {
|
||||
args: {
|
||||
children: <ExampleComponent />,
|
||||
variant: "danger"
|
||||
}
|
||||
};
|
||||
|
||||
export const WithCustomIcon: Story = {
|
||||
args: {
|
||||
children: <ExampleComponent />,
|
||||
variant: "warning",
|
||||
icon: <FontAwesomeIcon icon={faPlus} />
|
||||
}
|
||||
};
|
||||
|
||||
export const WithOutIcon: Story = {
|
||||
args: {
|
||||
children: <ExampleComponent />,
|
||||
variant: "warning",
|
||||
icon: null
|
||||
}
|
||||
};
|
||||
|
||||
export const WithOutTitle: Story = {
|
||||
args: {
|
||||
children: <ExampleComponent />,
|
||||
variant: "warning",
|
||||
hideTitle: true
|
||||
}
|
||||
};
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
faInfoCircle
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { type VariantProps, cva } from "cva";
|
||||
import { cva, type VariantProps } from "cva";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
const alertVariants = cva(
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Button } from "./Button";
|
||||
|
||||
const meta: Meta<typeof Button> = {
|
||||
title: "Components/Button",
|
||||
component: Button,
|
||||
tags: ["v2"],
|
||||
argTypes: {
|
||||
isRounded: {
|
||||
defaultValue: true,
|
||||
type: "boolean"
|
||||
},
|
||||
isFullWidth: {
|
||||
defaultValue: false,
|
||||
type: "boolean"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Button>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
children: "Hello Infisical"
|
||||
}
|
||||
};
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
children: "Hello Infisical",
|
||||
colorSchema: "secondary",
|
||||
variant: "outline"
|
||||
}
|
||||
};
|
||||
|
||||
export const Star: Story = {
|
||||
args: {
|
||||
children: "Hello Infisical",
|
||||
variant: "star"
|
||||
}
|
||||
};
|
||||
|
||||
export const Danger: Story = {
|
||||
args: {
|
||||
children: "Hello Infisical",
|
||||
colorSchema: "danger",
|
||||
variant: "solid"
|
||||
}
|
||||
};
|
||||
|
||||
export const Plain: Story = {
|
||||
args: {
|
||||
children: "Hello Infisical",
|
||||
variant: "plain"
|
||||
}
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
children: "Hello Infisical",
|
||||
disabled: true
|
||||
}
|
||||
};
|
||||
|
||||
export const FullWidth: Story = {
|
||||
args: {
|
||||
children: "Hello Infisical",
|
||||
isFullWidth: true
|
||||
}
|
||||
};
|
||||
|
||||
export const Loading: Story = {
|
||||
args: {
|
||||
children: "Hello Infisical",
|
||||
isLoading: true
|
||||
}
|
||||
};
|
||||
|
||||
export const LeftIcon: Story = {
|
||||
args: {
|
||||
children: "Hello Infisical",
|
||||
leftIcon: <FontAwesomeIcon icon={faPlus} className="pr-0.5" />
|
||||
}
|
||||
};
|
||||
|
||||
export const RightIcon: Story = {
|
||||
args: {
|
||||
children: "Hello Infisical",
|
||||
rightIcon: <FontAwesomeIcon icon={faPlus} className="pr-0.5" />
|
||||
}
|
||||
};
|
||||
@@ -1,40 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Card, CardBody, CardFooter, CardProps, CardTitle } from "./Card";
|
||||
|
||||
const meta: Meta<typeof Card> = {
|
||||
title: "Components/Card",
|
||||
component: Card,
|
||||
tags: ["v2"],
|
||||
argTypes: {
|
||||
isRounded: {
|
||||
type: "boolean",
|
||||
defaultValue: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Card>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
const Template = (args: CardProps) => (
|
||||
<div className="w-96">
|
||||
<Card {...args}>
|
||||
<CardTitle subTitle="Please add your subtitle here">Title</CardTitle>
|
||||
<CardBody>Content</CardBody>
|
||||
<CardFooter>Footer</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const Basic: Story = {
|
||||
render: (args) => <Template {...args} />
|
||||
};
|
||||
|
||||
export const Hoverable: Story = {
|
||||
render: (args) => <Template {...args} />,
|
||||
args: {
|
||||
isHoverable: true
|
||||
}
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Checkbox } from "./Checkbox";
|
||||
|
||||
const meta: Meta<typeof Checkbox> = {
|
||||
title: "Components/Checkbox",
|
||||
component: Checkbox,
|
||||
tags: ["v2"],
|
||||
argTypes: {}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Checkbox>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Simple: Story = {
|
||||
args: {
|
||||
children: "Accept the condition"
|
||||
}
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
children: "Accept the condition",
|
||||
isDisabled: true
|
||||
}
|
||||
};
|
||||
|
||||
export const Required: Story = {
|
||||
args: {
|
||||
children: "Accept the condition",
|
||||
isRequired: true
|
||||
}
|
||||
};
|
||||
@@ -1,83 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import { faCaretDown, faCheck } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Combobox, Transition } from "@headlessui/react";
|
||||
import { ByComparator } from "@headlessui/react/dist/types";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
type ComboBoxProps<T extends object> = {
|
||||
value?: T;
|
||||
className?: string;
|
||||
items: {
|
||||
value: T;
|
||||
key: string;
|
||||
label: string;
|
||||
}[];
|
||||
by: ByComparator<T>;
|
||||
defaultValue?: T;
|
||||
displayValue: (value: T) => string;
|
||||
onSelectChange: (value: T) => void;
|
||||
onFilter: (value: { value: T }, filterQuery: string) => boolean;
|
||||
};
|
||||
|
||||
// TODO(akhilmhdh): This is a very temporary one due to limitation of present situation
|
||||
// don't mind the api for now will be switched to aria later
|
||||
export const ComboBox = <T extends object>({
|
||||
onSelectChange,
|
||||
onFilter,
|
||||
displayValue,
|
||||
by,
|
||||
items,
|
||||
...props
|
||||
}: ComboBoxProps<T>) => {
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
const filteredResult =
|
||||
query === "" ? items.slice(0, 20) : items.filter((el) => onFilter(el, query)).slice(0, 20);
|
||||
|
||||
return (
|
||||
<Combobox by={by} {...props} onChange={onSelectChange}>
|
||||
<div className="relative">
|
||||
<Combobox.Input
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
displayValue={displayValue}
|
||||
className=" inline-flex w-full items-center justify-between rounded-md 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"
|
||||
/>
|
||||
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<FontAwesomeIcon icon={faCaretDown} size="sm" aria-hidden="true" />
|
||||
</Combobox.Button>
|
||||
<Transition
|
||||
leave="transition ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
afterLeave={() => setQuery("")}
|
||||
>
|
||||
<Combobox.Options className="absolute z-50 mt-1 max-h-60 w-full overflow-auto rounded-md bg-mineshaft-900 py-1 text-base shadow-lg ring-1 ring-black/5 focus:outline-none sm:text-sm">
|
||||
{filteredResult.map(({ value, key, label }) => (
|
||||
<Combobox.Option
|
||||
key={key}
|
||||
value={value}
|
||||
className={({ active }) =>
|
||||
`relative cursor-pointer select-none py-2 pl-10 pr-4 transition-all hover:bg-mineshaft-500 ${
|
||||
active ? "text-primary" : "text-white"
|
||||
}`
|
||||
}
|
||||
>
|
||||
{({ selected }) => (
|
||||
<>
|
||||
{label}
|
||||
{selected ? (
|
||||
<div className={twMerge("absolute top-2 left-3 text-primary")}>
|
||||
<FontAwesomeIcon icon={faCheck} />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</Combobox.Option>
|
||||
))}
|
||||
</Combobox.Options>
|
||||
</Transition>
|
||||
</div>
|
||||
</Combobox>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export { ComboBox } from "./ComboBox";
|
||||
@@ -40,7 +40,7 @@ export const ContentLoader = ({ text, frequency = 2000, className }: Props) => {
|
||||
loading="lazy"
|
||||
/>
|
||||
{text && isTextArray && (
|
||||
<AnimatePresence exitBeforeEnter>
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
className="text-primary"
|
||||
key={`content-loader-${pos}`}
|
||||
|
||||
@@ -38,7 +38,7 @@ export const CreatableSelect = <T,>({
|
||||
control: ({ isFocused }) =>
|
||||
twMerge(
|
||||
isFocused ? "border-primary-400/50" : "border-mineshaft-600 hover:border-gray-400",
|
||||
"border w-full p-0.5 rounded-md text-mineshaft-200 font-inter bg-mineshaft-900 hover:cursor-pointer"
|
||||
"w-full rounded-md border bg-mineshaft-900 p-0.5 font-inter text-mineshaft-200 hover:cursor-pointer"
|
||||
),
|
||||
placeholder: () => "text-mineshaft-400 text-sm pl-1 py-0.5",
|
||||
input: () => "pl-1 py-0.5",
|
||||
@@ -58,7 +58,7 @@ export const CreatableSelect = <T,>({
|
||||
twMerge(
|
||||
isFocused && "bg-mineshaft-700 active:bg-mineshaft-600",
|
||||
isSelected && "text-mineshaft-200",
|
||||
"hover:cursor-pointer text-xs px-3 py-2"
|
||||
"px-3 py-2 text-xs hover:cursor-pointer"
|
||||
),
|
||||
noOptionsMessage: () => "text-mineshaft-400 p-2 rounded-md"
|
||||
}}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { DatePicker } from "./DatePicker";
|
||||
|
||||
const meta: Meta<typeof DatePicker> = {
|
||||
title: "Components/DatePicker",
|
||||
component: DatePicker,
|
||||
tags: ["v2"],
|
||||
argTypes: {}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof DatePicker>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Primary: Story = {
|
||||
args: {}
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Button } from "../Button";
|
||||
import { Drawer, DrawerContent, DrawerContentProps, DrawerTrigger } from "./Drawer";
|
||||
|
||||
const meta: Meta<typeof Drawer> = {
|
||||
title: "Components/Drawer",
|
||||
component: Drawer,
|
||||
tags: ["v2"],
|
||||
argTypes: {}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof DrawerContent>;
|
||||
|
||||
const Template = (args: DrawerContentProps) => (
|
||||
<Drawer>
|
||||
<DrawerTrigger asChild>
|
||||
<Button>Open</Button>
|
||||
</DrawerTrigger>
|
||||
<DrawerContent {...args}>Hello world</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
|
||||
export const Basic: Story = {
|
||||
render: (args) => <Template {...args} />,
|
||||
args: {
|
||||
title: "Title",
|
||||
subTitle: "Something as subtitle",
|
||||
footerContent: "footer content"
|
||||
}
|
||||
};
|
||||
@@ -59,7 +59,7 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
|
||||
<IconButton
|
||||
variant="plain"
|
||||
ariaLabel="close"
|
||||
className="absolute top-4 right-6 rounded text-bunker-400 hover:text-bunker-50"
|
||||
className="absolute right-6 top-4 rounded text-bunker-400 hover:text-bunker-50"
|
||||
>
|
||||
<FontAwesomeIcon icon={faTimes} size="lg" className="cursor-pointer" />
|
||||
</IconButton>
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
import { faPlus, faTrash, faUndo } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { IconButton } from "../IconButton";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from "./Dropdown";
|
||||
|
||||
const meta: Meta<typeof DropdownMenu> = {
|
||||
title: "Components/DropdownMenu",
|
||||
component: DropdownMenu,
|
||||
tags: ["v2"],
|
||||
argTypes: {}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof DropdownMenuContent>;
|
||||
|
||||
export const Basic: Story = {
|
||||
render: (args) => (
|
||||
<div className="flex w-full justify-center">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton ariaLabel="add">
|
||||
<FontAwesomeIcon icon={faPlus} />
|
||||
</IconButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" {...args}>
|
||||
<DropdownMenuItem>Delete</DropdownMenuItem>
|
||||
<DropdownMenuItem>Undo</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export const Icons: Story = {
|
||||
render: (args) => (
|
||||
<div className="flex w-full justify-center">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton ariaLabel="add">
|
||||
<FontAwesomeIcon icon={faPlus} />
|
||||
</IconButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" {...args}>
|
||||
<DropdownMenuItem icon={<FontAwesomeIcon icon={faTrash} size="sm" />}>
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem icon={<FontAwesomeIcon icon={faUndo} size="sm" />}>
|
||||
Undo
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export const WithDivider: Story = {
|
||||
render: (args) => (
|
||||
<div className="flex w-full justify-center">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton ariaLabel="add">
|
||||
<FontAwesomeIcon icon={faPlus} />
|
||||
</IconButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" {...args}>
|
||||
<DropdownMenuItem>Delete</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>Undo</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>Redo</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export const Group: Story = {
|
||||
render: (args) => (
|
||||
<div className="flex w-full justify-center">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton ariaLabel="add">
|
||||
<FontAwesomeIcon icon={faPlus} />
|
||||
</IconButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" {...args}>
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>Group</DropdownMenuLabel>
|
||||
<DropdownMenuItem>Undo</DropdownMenuItem>
|
||||
<DropdownMenuItem>Delete</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>Group#2</DropdownMenuLabel>
|
||||
<DropdownMenuItem>Undo</DropdownMenuItem>
|
||||
<DropdownMenuItem>Delete</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
@@ -14,7 +14,7 @@ export type DropdownMenuTriggerProps = DropdownMenuPrimitive.DropdownMenuTrigger
|
||||
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
||||
|
||||
// item container
|
||||
export type DropdownMenuContentProps = DropdownMenuPrimitive.MenuContentProps;
|
||||
export type DropdownMenuContentProps = DropdownMenuPrimitive.DropdownMenuContentProps;
|
||||
export const DropdownMenuContent = forwardRef<HTMLDivElement, DropdownMenuContentProps>(
|
||||
({ children, className, ...props }, forwardedRef) => {
|
||||
return (
|
||||
@@ -24,7 +24,7 @@ export const DropdownMenuContent = forwardRef<HTMLDivElement, DropdownMenuConten
|
||||
{...props}
|
||||
ref={forwardedRef}
|
||||
className={twMerge(
|
||||
"z-30 min-w-[220px] overflow-y-auto rounded-md border border-mineshaft-600 bg-mineshaft-900 text-bunker-300 shadow will-change-auto data-[side=top]:animate-slideDownAndFade data-[side=left]:animate-slideRightAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade",
|
||||
"z-30 min-w-[220px] overflow-y-auto rounded-md border border-mineshaft-600 bg-mineshaft-900 text-bunker-300 shadow will-change-auto data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade data-[side=right]:animate-slideLeftAndFade data-[side=top]:animate-slideDownAndFade",
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -38,7 +38,7 @@ export const DropdownMenuContent = forwardRef<HTMLDivElement, DropdownMenuConten
|
||||
DropdownMenuContent.displayName = "DropdownMenuContent";
|
||||
|
||||
// item container
|
||||
export type DropdownSubMenuContentProps = DropdownMenuPrimitive.MenuSubContentProps;
|
||||
export type DropdownSubMenuContentProps = DropdownMenuPrimitive.DropdownMenuSubContentProps;
|
||||
export const DropdownSubMenuContent = forwardRef<HTMLDivElement, DropdownSubMenuContentProps>(
|
||||
({ children, className, ...props }, forwardedRef) => {
|
||||
return (
|
||||
@@ -48,7 +48,7 @@ export const DropdownSubMenuContent = forwardRef<HTMLDivElement, DropdownSubMenu
|
||||
{...props}
|
||||
ref={forwardedRef}
|
||||
className={twMerge(
|
||||
"z-30 min-w-[220px] rounded-md border border-mineshaft-600 bg-mineshaft-900 text-bunker-300 shadow will-change-auto data-[side=top]:animate-slideDownAndFade data-[side=left]:animate-slideRightAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade",
|
||||
"z-30 min-w-[220px] rounded-md border border-mineshaft-600 bg-mineshaft-900 text-bunker-300 shadow will-change-auto data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade data-[side=right]:animate-slideLeftAndFade data-[side=top]:animate-slideDownAndFade",
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -62,17 +62,17 @@ export const DropdownSubMenuContent = forwardRef<HTMLDivElement, DropdownSubMenu
|
||||
DropdownSubMenuContent.displayName = "DropdownMenuContent";
|
||||
|
||||
// item label component
|
||||
export type DropdownLabelProps = DropdownMenuPrimitive.MenuLabelProps;
|
||||
export type DropdownLabelProps = DropdownMenuPrimitive.DropdownMenuLabelProps;
|
||||
export const DropdownMenuLabel = ({ className, ...props }: DropdownLabelProps) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
{...props}
|
||||
className={twMerge("px-4 pt-2 pb-1 text-xs text-bunker-400", className)}
|
||||
className={twMerge("px-4 pb-1 pt-2 text-xs text-bunker-400", className)}
|
||||
/>
|
||||
);
|
||||
|
||||
// dropdown items
|
||||
export type DropdownMenuItemProps<T extends ElementType> =
|
||||
DropdownMenuPrimitive.MenuContentProps & {
|
||||
DropdownMenuPrimitive.DropdownMenuContentProps & {
|
||||
icon?: ReactNode;
|
||||
as?: T;
|
||||
inputRef?: Ref<T>;
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { EmptyState } from "./EmptyState";
|
||||
|
||||
const meta: Meta<typeof EmptyState> = {
|
||||
title: "Components/EmptyState",
|
||||
component: EmptyState,
|
||||
tags: ["v2"],
|
||||
argTypes: {},
|
||||
args: {
|
||||
title: "No members found"
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof EmptyState>;
|
||||
|
||||
export const Basic: Story = {
|
||||
render: (args) => <EmptyState {...args} />
|
||||
};
|
||||
@@ -41,12 +41,12 @@ export const FilterableSelect = <T,>({
|
||||
}}
|
||||
classNames={{
|
||||
container: ({ isDisabled }) =>
|
||||
twMerge("w-full text-sm font-inter", isDisabled && "!pointer-events-auto opacity-50"),
|
||||
twMerge("w-full font-inter text-sm", isDisabled && "!pointer-events-auto opacity-50"),
|
||||
control: ({ isFocused, isDisabled }) =>
|
||||
twMerge(
|
||||
isFocused ? "border-primary-400/50" : "border-mineshaft-600 ",
|
||||
`border w-full p-0.5 rounded-md text-mineshaft-200 font-inter bg-mineshaft-900 ${
|
||||
isDisabled ? "!cursor-not-allowed" : "hover:border-gray-400 hover:cursor-pointer"
|
||||
isFocused ? "border-primary-400/50" : "border-mineshaft-600",
|
||||
`w-full rounded-md border bg-mineshaft-900 p-0.5 font-inter text-mineshaft-200 ${
|
||||
isDisabled ? "!cursor-not-allowed" : "hover:cursor-pointer hover:border-gray-400"
|
||||
} `
|
||||
),
|
||||
placeholder: () =>
|
||||
@@ -72,7 +72,7 @@ export const FilterableSelect = <T,>({
|
||||
twMerge(
|
||||
isFocused && "bg-mineshaft-700 active:bg-mineshaft-600",
|
||||
isSelected && "text-mineshaft-200",
|
||||
"hover:cursor-pointer rounded text-xs px-3 py-2"
|
||||
"rounded px-3 py-2 text-xs hover:cursor-pointer"
|
||||
),
|
||||
noOptionsMessage: () => "text-mineshaft-400 p-2 rounded-md"
|
||||
}}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
// Be careful on dep cycle
|
||||
import { Input } from "../Input/Input";
|
||||
import { FormControl } from "./FormControl";
|
||||
|
||||
const meta: Meta<typeof FormControl> = {
|
||||
title: "Components/FormControl",
|
||||
component: FormControl,
|
||||
tags: ["v2"],
|
||||
argTypes: {}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof FormControl>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Basic: Story = {
|
||||
args: {
|
||||
children: <Input />,
|
||||
label: "Email",
|
||||
id: "email",
|
||||
helperText: "Type something.."
|
||||
}
|
||||
};
|
||||
|
||||
export const RequiredInput: Story = {
|
||||
args: {
|
||||
...Basic.args,
|
||||
isRequired: true
|
||||
}
|
||||
};
|
||||
|
||||
export const ErrorInput: Story = {
|
||||
args: {
|
||||
...Basic.args,
|
||||
errorText: "Some random error",
|
||||
isError: true
|
||||
}
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Button } from "../Button";
|
||||
import {
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardContentProps,
|
||||
HoverCardTrigger
|
||||
} from "./HoverCardv2";
|
||||
|
||||
const meta: Meta<typeof HoverCardContent> = {
|
||||
title: "Components/HoverCard",
|
||||
component: HoverCard,
|
||||
tags: ["v2"]
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof HoverCardContent>;
|
||||
|
||||
const Template = (args: HoverCardContentProps) => (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger>
|
||||
<Button>Hello world</Button>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent {...args} sideOffset={10}>
|
||||
<div className="bg-mineshaft-600 p-4 text-gray-400">
|
||||
<h1>This is hover card</h1>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
);
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Primary: Story = {
|
||||
render: (args) => <Template {...args} />
|
||||
};
|
||||
@@ -1,60 +0,0 @@
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { IconButton } from "./IconButton";
|
||||
|
||||
const meta: Meta<typeof IconButton> = {
|
||||
title: "Components/IconButton",
|
||||
component: IconButton,
|
||||
tags: ["v2"],
|
||||
argTypes: {
|
||||
isRounded: {
|
||||
defaultValue: true,
|
||||
type: "boolean"
|
||||
},
|
||||
ariaLabel: {
|
||||
defaultValue: "Some buttons..."
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof IconButton>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
children: <FontAwesomeIcon icon={faPlus} />
|
||||
}
|
||||
};
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
children: <FontAwesomeIcon icon={faPlus} />,
|
||||
colorSchema: "secondary",
|
||||
variant: "outline"
|
||||
}
|
||||
};
|
||||
|
||||
export const Danger: Story = {
|
||||
args: {
|
||||
children: <FontAwesomeIcon icon={faPlus} />,
|
||||
colorSchema: "danger",
|
||||
variant: "solid"
|
||||
}
|
||||
};
|
||||
|
||||
export const Plain: Story = {
|
||||
args: {
|
||||
children: <FontAwesomeIcon icon={faPlus} />,
|
||||
variant: "plain"
|
||||
}
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
children: <FontAwesomeIcon icon={faPlus} />,
|
||||
disabled: true
|
||||
}
|
||||
};
|
||||
@@ -294,7 +294,7 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="max-w-60 h-full w-full flex-col items-center justify-center rounded-md text-white"
|
||||
className="h-full w-full max-w-60 flex-col items-center justify-center rounded-md text-white"
|
||||
ref={popoverContentRef}
|
||||
>
|
||||
{suggestions.map((item, i) => {
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import { faEye, faMailBulk } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Input } from "./Input";
|
||||
|
||||
const meta: Meta<typeof Input> = {
|
||||
title: "Components/Input",
|
||||
component: Input,
|
||||
tags: ["v2"],
|
||||
argTypes: {
|
||||
isRounded: {
|
||||
defaultValue: true,
|
||||
type: "boolean"
|
||||
},
|
||||
placeholder: {
|
||||
defaultValue: "Type anything",
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Input>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Filled: Story = {
|
||||
args: {}
|
||||
};
|
||||
|
||||
export const Outline: Story = {
|
||||
args: {
|
||||
variant: "outline"
|
||||
}
|
||||
};
|
||||
|
||||
export const Plain: Story = {
|
||||
args: {
|
||||
variant: "plain"
|
||||
}
|
||||
};
|
||||
|
||||
export const Error: Story = {
|
||||
args: {
|
||||
isError: true
|
||||
}
|
||||
};
|
||||
|
||||
export const AutoWidth: Story = {
|
||||
args: {
|
||||
isFullWidth: false,
|
||||
className: "w-auto"
|
||||
}
|
||||
};
|
||||
|
||||
export const RightIcon: Story = {
|
||||
args: {
|
||||
rightIcon: <FontAwesomeIcon icon={faEye} />
|
||||
}
|
||||
};
|
||||
|
||||
export const LeftIcon: Story = {
|
||||
args: {
|
||||
leftIcon: <FontAwesomeIcon icon={faMailBulk} />
|
||||
}
|
||||
};
|
||||
@@ -1,104 +0,0 @@
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Menu, MenuGroup, MenuItem } from "./Menu";
|
||||
|
||||
const meta: Meta<typeof Menu> = {
|
||||
title: "Components/Menu",
|
||||
component: Menu,
|
||||
tags: ["v2"],
|
||||
argTypes: {}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Menu>;
|
||||
|
||||
export const Basic: Story = {
|
||||
render: (args) => (
|
||||
<Menu {...args}>
|
||||
<MenuItem>Secrets</MenuItem>
|
||||
<MenuItem>Members</MenuItem>
|
||||
<MenuItem>Integrations</MenuItem>
|
||||
</Menu>
|
||||
),
|
||||
args: {}
|
||||
};
|
||||
|
||||
export const SelectedItem: Story = {
|
||||
render: (args) => (
|
||||
<Menu {...args}>
|
||||
<MenuItem isSelected>Secrets</MenuItem>
|
||||
<MenuItem>Members</MenuItem>
|
||||
<MenuItem>Integrations</MenuItem>
|
||||
</Menu>
|
||||
),
|
||||
args: {}
|
||||
};
|
||||
|
||||
export const GroupedItem: Story = {
|
||||
render: (args) => (
|
||||
<Menu {...args}>
|
||||
<MenuGroup title="Group 1">
|
||||
<MenuItem>Secrets</MenuItem>
|
||||
<MenuItem>Members</MenuItem>
|
||||
</MenuGroup>
|
||||
<MenuGroup title="Group 2">
|
||||
<MenuItem>Secrets</MenuItem>
|
||||
<MenuItem>Members</MenuItem>
|
||||
</MenuGroup>
|
||||
</Menu>
|
||||
),
|
||||
args: {}
|
||||
};
|
||||
|
||||
export const DisabledItem: Story = {
|
||||
render: (args) => (
|
||||
<Menu {...args}>
|
||||
<MenuGroup title="Group 1">
|
||||
<MenuItem isDisabled>Secrets</MenuItem>
|
||||
<MenuItem>Members</MenuItem>
|
||||
</MenuGroup>
|
||||
<MenuGroup title="Group 2">
|
||||
<MenuItem>Secrets</MenuItem>
|
||||
<MenuItem>Members</MenuItem>
|
||||
</MenuGroup>
|
||||
</Menu>
|
||||
),
|
||||
args: {}
|
||||
};
|
||||
|
||||
export const WithIcons: Story = {
|
||||
render: (args) => (
|
||||
<Menu {...args}>
|
||||
<MenuGroup title="Group 1">
|
||||
<MenuItem isDisabled icon="system-outline-90-lock-closed">
|
||||
Secrets
|
||||
</MenuItem>
|
||||
<MenuItem icon="system-outline-96-groups">Members</MenuItem>
|
||||
</MenuGroup>
|
||||
<MenuGroup title="Group 2">
|
||||
<MenuItem icon="system-outline-90-lock-closed">Secrets</MenuItem>
|
||||
<MenuItem icon="system-outline-96-groups">Members</MenuItem>
|
||||
</MenuGroup>
|
||||
</Menu>
|
||||
),
|
||||
args: {}
|
||||
};
|
||||
|
||||
export const WithDescription: Story = {
|
||||
render: (args) => (
|
||||
<Menu {...args}>
|
||||
<MenuItem
|
||||
isDisabled
|
||||
icon="system-outline-90-lock-closed"
|
||||
description="Some random description"
|
||||
>
|
||||
Secrets
|
||||
</MenuItem>
|
||||
<MenuItem icon="system-outline-96-groups" description="Some random description">
|
||||
Members
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
),
|
||||
args: {}
|
||||
};
|
||||
@@ -1,10 +1,6 @@
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-nocheck
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
/* eslint-disable global-require */
|
||||
import { ComponentPropsWithRef, ElementType, ReactNode, Ref, useRef } from "react";
|
||||
import { DotLottie, DotLottieReact } from "@lottiefiles/dotlottie-react";
|
||||
import { motion } from "framer-motion";
|
||||
import Lottie from "lottie-react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export type MenuProps = {
|
||||
@@ -40,8 +36,7 @@ export const MenuItem = <T extends ElementType = "button">({
|
||||
inputRef,
|
||||
...props
|
||||
}: MenuItemProps<T> & ComponentPropsWithRef<T>): JSX.Element => {
|
||||
const iconRef = useRef();
|
||||
|
||||
const iconRef = useRef<DotLottie | null>(null);
|
||||
return (
|
||||
<div onMouseEnter={() => iconRef.current?.play()} onMouseLeave={() => iconRef.current?.stop()}>
|
||||
<li
|
||||
@@ -65,17 +60,17 @@ export const MenuItem = <T extends ElementType = "button">({
|
||||
isSelected ? "visisble" : "invisible"
|
||||
} absolute -left-[0.28rem] h-5 w-[0.07rem] rounded-md bg-primary`}
|
||||
/>
|
||||
{/* {icon && <span className="mr-3 ml-4 w-5 block group-hover:hidden">{icon}</span>} */}
|
||||
{icon && (
|
||||
<Lottie
|
||||
lottieRef={iconRef}
|
||||
style={{ width: 22, height: 22 }}
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
animationData={require(`../../../../public/lotties/${icon}.json`)}
|
||||
loop={false}
|
||||
autoplay={false}
|
||||
className="my-auto ml-[0.1rem] mr-3"
|
||||
/>
|
||||
<div style={{ width: "22px", height: "22px" }} className="my-auto ml-1 mr-3">
|
||||
<DotLottieReact
|
||||
dotLottieRefCallback={(el) => {
|
||||
iconRef.current = el;
|
||||
}}
|
||||
src={`/lotties/${icon}.json`}
|
||||
loop
|
||||
className="h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<span className="flex-grow text-left">{children}</span>
|
||||
</Item>
|
||||
@@ -86,55 +81,6 @@ export const MenuItem = <T extends ElementType = "button">({
|
||||
);
|
||||
};
|
||||
|
||||
export const SubMenuItem = <T extends ElementType = "button">({
|
||||
children,
|
||||
icon,
|
||||
className,
|
||||
isDisabled,
|
||||
isSelected,
|
||||
as: Item = "button",
|
||||
description,
|
||||
// wrapping in forward ref with generic component causes the loss of ts definitions on props
|
||||
inputRef,
|
||||
...props
|
||||
}: MenuItemProps<T> & ComponentPropsWithRef<T>): JSX.Element => {
|
||||
const iconRef = useRef();
|
||||
|
||||
return (
|
||||
<a onMouseEnter={() => iconRef.current?.play()} onMouseLeave={() => iconRef.current?.stop()}>
|
||||
<li
|
||||
className={twMerge(
|
||||
"duration-50 group mt-0.5 flex cursor-pointer flex-col rounded px-1 py-1 font-inter text-sm text-mineshaft-300 transition-all hover:bg-mineshaft-700 hover:text-mineshaft-100",
|
||||
isDisabled && "cursor-not-allowed hover:bg-transparent",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<motion.span className="flex w-full flex-row items-center justify-start rounded-sm pl-6">
|
||||
<Item
|
||||
type="button"
|
||||
role="menuitem"
|
||||
className="relative flex items-center"
|
||||
ref={inputRef}
|
||||
{...props}
|
||||
>
|
||||
<Lottie
|
||||
lottieRef={iconRef}
|
||||
style={{ width: 16, height: 16 }}
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
animationData={require(`../../../../public/lotties/${icon}.json`)}
|
||||
loop={false}
|
||||
autoplay={false}
|
||||
className="my-auto ml-[0.1rem] mr-3"
|
||||
/>
|
||||
<span className="flex-grow text-left text-sm">{children}</span>
|
||||
</Item>
|
||||
{description && <span className="mt-2 text-xs">{description}</span>}
|
||||
</motion.span>
|
||||
</li>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
MenuItem.displayName = "MenuItem";
|
||||
|
||||
export type MenuGroupProps = {
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Button } from "../Button";
|
||||
import { Modal, ModalContent, ModalContentProps, ModalTrigger } from "./Modal";
|
||||
|
||||
const meta: Meta<typeof Modal> = {
|
||||
title: "Components/Modal",
|
||||
component: Modal,
|
||||
tags: ["v2"],
|
||||
argTypes: {}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ModalContent>;
|
||||
|
||||
const Template = (args: ModalContentProps) => (
|
||||
<Modal>
|
||||
<ModalTrigger asChild>
|
||||
<Button>Open</Button>
|
||||
</ModalTrigger>
|
||||
<ModalContent {...args}>Hello world</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
export const Basic: Story = {
|
||||
render: (args) => <Template {...args} />,
|
||||
args: {
|
||||
title: "Title",
|
||||
subTitle: "Something as subtitle",
|
||||
footerContent: "footer content"
|
||||
}
|
||||
};
|
||||
@@ -40,7 +40,7 @@ export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
|
||||
<Card
|
||||
isRounded
|
||||
className={twMerge(
|
||||
"thin-scrollbar fixed top-1/2 left-1/2 z-30 max-w-xl -translate-y-2/4 -translate-x-2/4 animate-popIn border border-mineshaft-600 drop-shadow-2xl dark:[color-scheme:dark]",
|
||||
"thin-scrollbar fixed left-1/2 top-1/2 z-30 max-w-xl -translate-x-2/4 -translate-y-2/4 animate-popIn border border-mineshaft-600 drop-shadow-2xl dark:[color-scheme:dark]",
|
||||
className
|
||||
)}
|
||||
style={{ maxHeight: "90%" }}
|
||||
@@ -57,7 +57,7 @@ export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
|
||||
<IconButton
|
||||
variant="plain"
|
||||
ariaLabel="close"
|
||||
className="absolute top-4 right-6 rounded text-bunker-400 hover:text-bunker-50"
|
||||
className="absolute right-6 top-4 rounded text-bunker-400 hover:text-bunker-50"
|
||||
>
|
||||
<FontAwesomeIcon icon={faTimes} size="lg" className="cursor-pointer" />
|
||||
</IconButton>
|
||||
@@ -72,7 +72,7 @@ ModalContent.displayName = "ModalContent";
|
||||
|
||||
export type ModalProps = Omit<DialogPrimitive.DialogProps, "open"> & { isOpen?: boolean };
|
||||
export const Modal = ({ isOpen, ...props }: ModalProps) => (
|
||||
<DialogPrimitive.Root open={isOpen} {...props} modal/>
|
||||
<DialogPrimitive.Root open={isOpen} {...props} />
|
||||
);
|
||||
|
||||
export const ModalTrigger = DialogPrimitive.Trigger;
|
||||
|
||||
@@ -4,23 +4,23 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
type Props = {
|
||||
icon?: IconDefinition;
|
||||
title: string;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
icon?: IconDefinition;
|
||||
title: string;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const NoticeBanner = ({ icon = faWarning, title, children, className }: Props) => (
|
||||
<div
|
||||
className={twMerge(
|
||||
"flex w-full flex-row items-center rounded-md border border-primary-600/70 bg-primary/[.07] p-4 text-base text-white",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<FontAwesomeIcon icon={icon} className="pr-6 text-4xl text-white/80" />
|
||||
<div className="flex w-full flex-col text-sm">
|
||||
<div className="mb-2 text-lg font-semibold">{title}</div>
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={twMerge(
|
||||
"flex w-full flex-row items-center rounded-md border border-primary-600/70 bg-primary/[.07] p-4 text-base text-white",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<FontAwesomeIcon icon={icon} className="pr-6 text-4xl text-white/80" />
|
||||
<div className="flex w-full flex-col text-sm">
|
||||
<div className="mb-2 text-lg font-semibold">{title}</div>
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
// eslint-disable-next-line
|
||||
import { useArgs } from "@storybook/client-api";
|
||||
import type { Meta } from "@storybook/react";
|
||||
|
||||
import { Pagination, PaginationProps } from "./Pagination";
|
||||
|
||||
const meta: Meta<typeof Pagination> = {
|
||||
title: "Components/Pagination",
|
||||
component: Pagination,
|
||||
tags: ["v2"],
|
||||
args: {
|
||||
count: 50
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
// type Story = StoryObj<typeof Pagination>;
|
||||
|
||||
export const Primary = (args: PaginationProps) => {
|
||||
const [, updateArgs] = useArgs();
|
||||
|
||||
return (
|
||||
<Pagination
|
||||
{...args}
|
||||
onChangePage={(page) => updateArgs({ page })}
|
||||
onChangePerPage={(perPage) => updateArgs({ perPage, page: 1 })}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -49,7 +49,7 @@ export const Pagination = ({
|
||||
return (
|
||||
<div
|
||||
className={twMerge(
|
||||
"flex w-full items-center justify-end border-t border-mineshaft-600 bg-mineshaft-800 py-3 px-4 text-white",
|
||||
"flex w-full items-center justify-end border-t border-mineshaft-600 bg-mineshaft-800 px-4 py-3 text-white",
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -21,7 +21,7 @@ export const PopoverObject = ({ children, text, onChangeHandler, id }: Props) =>
|
||||
</Popover.Trigger>
|
||||
<Popover.Portal>
|
||||
<Popover.Content
|
||||
className="z-[100] min-h-fit w-[460px] rounded border border-chicago-700 bg-mineshaft-600 p-3 shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2)] will-change-[transform,opacity] focus:shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2),0_0_0_2px_theme(colors.violet7)] data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade"
|
||||
className="z-[100] min-h-fit w-[460px] rounded border border-chicago-700 bg-mineshaft-600 p-3 shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2)] will-change-[transform,opacity] focus:shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2),0_0_0_2px_theme(colors.violet7)] data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=top]:animate-slideDownAndFade"
|
||||
sideOffset={5}
|
||||
hideWhenDetached
|
||||
side="left"
|
||||
@@ -32,13 +32,13 @@ export const PopoverObject = ({ children, text, onChangeHandler, id }: Props) =>
|
||||
onChange={(e) => onChangeHandler(e.target.value, id)}
|
||||
// type={type}
|
||||
value={text}
|
||||
className="ph-no-capture placeholder peer z-10 h-[20rem] w-full rounded-md border border-mineshaft-500 bg-bunker-600 py-2.5 px-2 text-sm text-bunker-300 caret-bunker-200 outline-none duration-200 placeholder:text-bunker-400 focus:text-bunker-100 placeholder:focus:text-transparent dark:[color-scheme:dark]"
|
||||
className="ph-no-capture placeholder peer z-10 h-[20rem] w-full rounded-md border border-mineshaft-500 bg-bunker-600 px-2 py-2.5 text-sm text-bunker-300 caret-bunker-200 outline-none duration-200 placeholder:text-bunker-400 focus:text-bunker-100 placeholder:focus:text-transparent dark:[color-scheme:dark]"
|
||||
spellCheck="false"
|
||||
placeholder="–"
|
||||
/>
|
||||
</div>
|
||||
<Popover.Close
|
||||
className="hover:bg-violet4 focus:shadow-violet7 absolute top-[5px] right-[5px] inline-flex h-[25px] w-[25px] cursor-default items-center justify-center rounded-full text-bunker-300 outline-none hover:text-white focus:shadow-[0_0_0_2px]"
|
||||
className="hover:bg-violet4 focus:shadow-violet7 absolute right-[5px] top-[5px] inline-flex h-[25px] w-[25px] cursor-default items-center justify-center rounded-full text-bunker-300 outline-none hover:text-white focus:shadow-[0_0_0_2px]"
|
||||
aria-label="Close"
|
||||
>
|
||||
<FontAwesomeIcon icon={faXmark} />
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Button } from "../Button";
|
||||
import { Popover, PopoverContent, PopoverContentProps, PopoverTrigger } from "./Popoverv2";
|
||||
|
||||
const meta: Meta<typeof PopoverContent> = {
|
||||
title: "Components/Popover",
|
||||
component: Popover,
|
||||
tags: ["v2"]
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof PopoverContent>;
|
||||
|
||||
const Template = (args: PopoverContentProps) => (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button>Hello world</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent {...args} side="bottom" sideOffset={10}>
|
||||
<h1>This is popper</h1>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Primary: Story = {
|
||||
render: (args) => <Template {...args} />
|
||||
};
|
||||
@@ -44,7 +44,7 @@ export const PopoverContent = ({
|
||||
<IconButton
|
||||
variant="plain"
|
||||
ariaLabel="close"
|
||||
className="absolute top-0 right-1 rounded text-bunker-400 hover:text-bunker-50"
|
||||
className="absolute right-1 top-0 rounded text-bunker-400 hover:text-bunker-50"
|
||||
>
|
||||
<FontAwesomeIcon icon={faTimes} size="lg" className="cursor-pointer" />
|
||||
</IconButton>
|
||||
|
||||
@@ -78,8 +78,8 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
style={{ maxHeight: `${21 * 7}px` }}
|
||||
>
|
||||
<div className="relative overflow-hidden">
|
||||
<pre aria-hidden className="m-0 ">
|
||||
<code className={`inline-block w-full ${commonClassName}`}>
|
||||
<pre aria-hidden className="m-0">
|
||||
<code className={`inline-block w-full ${commonClassName}`}>
|
||||
<span style={{ whiteSpace: "break-spaces" }}>
|
||||
{syntaxHighlight(value, isVisible || isSecretFocused, isImport)}
|
||||
</span>
|
||||
|
||||
@@ -39,7 +39,7 @@ export const SecretPathInput = ({
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
const { folderNames: folders } = useGetFoldersByEnv({
|
||||
path: secretPath,
|
||||
environments: [environment || currentWorkspace?.environments?.[0].slug!],
|
||||
environments: [environment || currentWorkspace?.environments?.[0].slug || ""],
|
||||
projectId: workspaceId
|
||||
});
|
||||
|
||||
@@ -141,7 +141,7 @@ export const SecretPathInput = ({
|
||||
maxHeight: "var(--radix-select-content-available-height)"
|
||||
}}
|
||||
>
|
||||
<div className="max-w-60 h-full w-full flex-col items-center justify-center rounded-md text-white">
|
||||
<div className="h-full w-full max-w-60 flex-col items-center justify-center rounded-md text-white">
|
||||
{suggestions.map((suggestion, i) => (
|
||||
<div
|
||||
tabIndex={0}
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { SelectProps } from "@radix-ui/react-select";
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Select, SelectItem } from "./Select";
|
||||
|
||||
const meta: Meta<typeof Select> = {
|
||||
title: "Components/Select",
|
||||
component: Select,
|
||||
tags: ["v2"],
|
||||
argTypes: {
|
||||
placeholder: {
|
||||
defaultValue: "Type something..."
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Select>;
|
||||
|
||||
export const Basic: Story = {
|
||||
render: (args) => (
|
||||
<div className="">
|
||||
<Select placeholder="Type anything..." className="w-72" {...args}>
|
||||
<SelectItem value="1">John</SelectItem>
|
||||
<SelectItem value="2">Peter</SelectItem>
|
||||
<SelectItem value="3">Suzy</SelectItem>
|
||||
</Select>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
const Controlled = (args: SelectProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selected, setSelected] = useState("");
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
<Select
|
||||
defaultValue="1"
|
||||
className="w-72"
|
||||
open={isOpen}
|
||||
onValueChange={(val) => setSelected(val)}
|
||||
value={selected}
|
||||
onOpenChange={(open) => setIsOpen(open)}
|
||||
{...args}
|
||||
>
|
||||
<SelectItem value="1">John</SelectItem>
|
||||
<SelectItem value="2">Peter</SelectItem>
|
||||
<SelectItem value="3">Suzy</SelectItem>
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Control: Story = {
|
||||
render: (args) => <Controlled {...args} />
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
render: (args) => (
|
||||
<div className="">
|
||||
<Select defaultValue="1" className="w-72" {...args}>
|
||||
<SelectItem value="1">John</SelectItem>
|
||||
<SelectItem value="2" isDisabled>
|
||||
Peter
|
||||
</SelectItem>
|
||||
<SelectItem value="3">Suzy</SelectItem>
|
||||
</Select>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export const Loading: Story = {
|
||||
render: (args) => (
|
||||
<div className="">
|
||||
<Select defaultValue="1" className="w-72" isLoading {...args}>
|
||||
<SelectItem value="1">John</SelectItem>
|
||||
<SelectItem value="2">Peter</SelectItem>
|
||||
<SelectItem value="3">Suzy</SelectItem>
|
||||
</Select>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
const AsyncSelectOptions = () => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line no-new
|
||||
new Promise<void>((resolve): void => {
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
resolve();
|
||||
}, 1000);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
<Select placeholder="Hello" className="w-72" isLoading={isLoading}>
|
||||
<SelectItem value="1">John</SelectItem>
|
||||
<SelectItem value="2">Peter</SelectItem>
|
||||
<SelectItem value="3">Suzy</SelectItem>
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Async: Story = {
|
||||
render: (args) => <AsyncSelectOptions {...args} />
|
||||
};
|
||||
@@ -52,8 +52,7 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={twMerge(
|
||||
`inline-flex items-center justify-between rounded-md border border-mineshaft-600
|
||||
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-400`,
|
||||
"inline-flex items-center justify-between rounded-md border border-mineshaft-600 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-400",
|
||||
className,
|
||||
isDisabled && "cursor-not-allowed opacity-50"
|
||||
)}
|
||||
@@ -123,9 +122,7 @@ export const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
|
||||
<SelectPrimitive.Item
|
||||
{...props}
|
||||
className={twMerge(
|
||||
`relative mb-0.5 flex
|
||||
cursor-pointer select-none items-center overflow-hidden text-ellipsis whitespace-nowrap rounded-md py-2
|
||||
pl-10 pr-4 text-sm outline-none transition-all hover:bg-mineshaft-500 data-[highlighted]:bg-mineshaft-700/80`,
|
||||
"relative mb-0.5 flex cursor-pointer select-none items-center overflow-hidden text-ellipsis whitespace-nowrap 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 opacity-80 hover:!bg-transparent",
|
||||
className
|
||||
@@ -160,9 +157,7 @@ export const SelectClear = forwardRef<HTMLDivElement, SelectClearProps>(
|
||||
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`,
|
||||
"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",
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Skeleton } from "./Skeleton";
|
||||
|
||||
const meta: Meta<typeof Skeleton> = {
|
||||
title: "Components/Skeleton",
|
||||
component: Skeleton,
|
||||
tags: ["v2"],
|
||||
argTypes: {}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Skeleton>;
|
||||
|
||||
export const Basic: Story = {
|
||||
render: (args) => <Skeleton {...args} />
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Switch } from "./Switch";
|
||||
|
||||
const meta: Meta<typeof Switch> = {
|
||||
title: "Components/Switch",
|
||||
component: Switch,
|
||||
tags: ["v2"],
|
||||
argTypes: {}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Switch>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Simple: Story = {
|
||||
args: {
|
||||
children: "Dark mode"
|
||||
}
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
children: "Dark mode",
|
||||
isDisabled: true
|
||||
}
|
||||
};
|
||||
|
||||
export const Required: Story = {
|
||||
args: {
|
||||
children: "Dark mode",
|
||||
isRequired: true
|
||||
}
|
||||
};
|
||||
@@ -1,60 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Table, TableContainer, TableSkeleton, TBody, Td, Th, THead, Tr } from "./Table";
|
||||
|
||||
const meta: Meta<typeof Table> = {
|
||||
title: "Components/Table",
|
||||
component: Table,
|
||||
tags: ["v2"],
|
||||
argTypes: {}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Table>;
|
||||
|
||||
export const Basic: Story = {
|
||||
render: (args) => (
|
||||
<TableContainer>
|
||||
<Table {...args}>
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th>Head#1</Th>
|
||||
<Th>Head#2</Th>
|
||||
<Th>Head#3</Th>
|
||||
</Tr>
|
||||
</THead>
|
||||
<TBody>
|
||||
<Tr>
|
||||
<Td>Row#1</Td>
|
||||
<Td>Row#2</Td>
|
||||
<Td>Row#3</Td>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<Td>Row#1</Td>
|
||||
<Td>Row#2</Td>
|
||||
<Td>Row#3</Td>
|
||||
</Tr>
|
||||
</TBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)
|
||||
};
|
||||
|
||||
export const Loading: Story = {
|
||||
render: (args) => (
|
||||
<TableContainer>
|
||||
<Table {...args}>
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th>Head#1</Th>
|
||||
<Th>Head#2</Th>
|
||||
<Th>Head#3</Th>
|
||||
</Tr>
|
||||
</THead>
|
||||
<TBody>
|
||||
<TableSkeleton columns={3} innerKey="story-book-table" />
|
||||
</TBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)
|
||||
};
|
||||
@@ -101,7 +101,7 @@ export type ThProps = {
|
||||
export const Th = ({ children, className }: ThProps): JSX.Element => (
|
||||
<th
|
||||
className={twMerge(
|
||||
"border-b-2 border-mineshaft-600 bg-mineshaft-800 px-5 pt-4 pb-3.5 font-semibold",
|
||||
"border-b-2 border-mineshaft-600 bg-mineshaft-800 px-5 pb-3.5 pt-4 font-semibold",
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -13,7 +13,7 @@ export type TabListProps = TabsPrimitive.TabsListProps;
|
||||
|
||||
export const TabList = ({ className, children, ...props }: TabListProps) => (
|
||||
<TabsPrimitive.List
|
||||
className={twMerge("flex flex-shrink-0 border-b-2 border-mineshaft-800", className)}
|
||||
className={twMerge("flex flex-shrink-0 border-b-2 border-mineshaft-800", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { TextArea } from "./TextArea";
|
||||
|
||||
const meta: Meta<typeof TextArea> = {
|
||||
title: "Components/TextArea",
|
||||
component: TextArea,
|
||||
tags: ["v2"],
|
||||
argTypes: {
|
||||
placeholder: {
|
||||
defaultValue: "Type anything",
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof TextArea>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Filled: Story = {
|
||||
args: {}
|
||||
};
|
||||
|
||||
export const Outline: Story = {
|
||||
args: {
|
||||
variant: "outline"
|
||||
}
|
||||
};
|
||||
|
||||
export const Plain: Story = {
|
||||
args: {
|
||||
variant: "plain"
|
||||
}
|
||||
};
|
||||
|
||||
export const Error: Story = {
|
||||
args: {
|
||||
isError: true
|
||||
}
|
||||
};
|
||||
|
||||
export const AutoWidth: Story = {
|
||||
args: {
|
||||
isFullWidth: false,
|
||||
className: "w-auto"
|
||||
}
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Tooltip, TooltipProps, TooltipProvider } from "./Tooltip";
|
||||
|
||||
const meta: Meta<typeof Tooltip> = {
|
||||
title: "Components/Tooltip",
|
||||
component: Tooltip,
|
||||
tags: ["v2"],
|
||||
args: {
|
||||
content: "Hi"
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Tooltip>;
|
||||
|
||||
const Template = (args: TooltipProps) => (
|
||||
<TooltipProvider>
|
||||
<Tooltip {...args}>
|
||||
<div className="text-white">Hello infisical</div>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Primary: Story = {
|
||||
render: (args) => <Template {...args} />
|
||||
};
|
||||
@@ -45,16 +45,11 @@ export const Tooltip = ({
|
||||
sideOffset={5}
|
||||
{...props}
|
||||
className={twMerge(
|
||||
`z-50 max-w-[15rem] select-none border border-mineshaft-600 bg-mineshaft-800 font-light text-bunker-200 shadow-md
|
||||
data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade
|
||||
data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade
|
||||
data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade
|
||||
data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade
|
||||
`,
|
||||
"z-50 max-w-[15rem] select-none border border-mineshaft-600 bg-mineshaft-800 font-light text-bunker-200 shadow-md data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade",
|
||||
isDisabled && "!hidden",
|
||||
center && "text-center",
|
||||
size === "sm" && "rounded-sm py-1 px-2 text-xs",
|
||||
size === "md" && "rounded-md py-2 px-4 text-sm",
|
||||
size === "sm" && "rounded-sm px-2 py-1 text-xs",
|
||||
size === "md" && "rounded-md px-4 py-2 text-sm",
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import { useOrganization, useSubscription } from "@app/context";
|
||||
import { useGetOrgTrialUrl } from "@app/hooks/api";
|
||||
|
||||
import { Button } from "../Button";
|
||||
import { Modal, ModalContent } from "../Modal";
|
||||
|
||||
type Props = {
|
||||
isOpen?: boolean;
|
||||
onOpenChange?: (isOpen: boolean) => void;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export const UpgradePlanModal = ({ text, isOpen, onOpenChange }: Props): JSX.Element => {
|
||||
const { subscription } = useSubscription();
|
||||
const { currentOrg } = useOrganization();
|
||||
const { mutateAsync, isLoading } = useGetOrgTrialUrl();
|
||||
const link =
|
||||
subscription && subscription.slug !== null
|
||||
? `/org/${currentOrg?.id}/billing`
|
||||
: "https://infisical.com/scheduledemo";
|
||||
|
||||
const handleUpgradeBtnClick = async () => {
|
||||
try {
|
||||
if (!subscription || !currentOrg) return;
|
||||
|
||||
if (!subscription.has_used_trial) {
|
||||
// direct user to start pro trial
|
||||
|
||||
const url = await mutateAsync({
|
||||
orgId: currentOrg.id,
|
||||
success_url: window.location.href
|
||||
});
|
||||
|
||||
window.location.href = url;
|
||||
} else {
|
||||
// direct user to upgrade their plan
|
||||
window.location.href = link;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||
<ModalContent title="Unleash Infisical's Full Power">
|
||||
<p className="mb-2 text-bunker-300">{text}</p>
|
||||
<p className="text-bunker-300">
|
||||
Upgrade and get access to this, as well as to other powerful enhancements.
|
||||
</p>
|
||||
<div className="mt-8 flex items-center">
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
colorSchema="primary"
|
||||
onClick={handleUpgradeBtnClick}
|
||||
className="mr-4"
|
||||
>
|
||||
{subscription && !subscription.has_used_trial ? "Start Pro Free Trial" : "Upgrade Plan"}
|
||||
</Button>
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
variant="plain"
|
||||
onClick={() => onOpenChange && onOpenChange(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export { UpgradePlanModal } from "./UpgradePlanModal";
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
export * from "./Accordion";
|
||||
export * from "./Alert";
|
||||
export * from "./Badge";
|
||||
@@ -33,4 +34,3 @@ export * from "./Tabs";
|
||||
export * from "./Tag";
|
||||
export * from "./TextArea";
|
||||
export * from "./Tooltip";
|
||||
export * from "./UpgradePlanModal";
|
||||
|
||||
@@ -1,337 +0,0 @@
|
||||
import { FC, useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useRouter } from "next/router";
|
||||
import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import z from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Button,
|
||||
Checkbox,
|
||||
FormControl,
|
||||
Input,
|
||||
Modal,
|
||||
ModalClose,
|
||||
ModalContent,
|
||||
Select,
|
||||
SelectItem,
|
||||
TextArea
|
||||
} from "@app/components/v2";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
useOrganization,
|
||||
useOrgPermission,
|
||||
useSubscription,
|
||||
useUser
|
||||
} from "@app/context";
|
||||
import { getProjectHomePage } from "@app/helpers/project";
|
||||
import {
|
||||
fetchOrgUsers,
|
||||
useAddUserToWsNonE2EE,
|
||||
useCreateWorkspace,
|
||||
useGetExternalKmsList,
|
||||
useGetUserWorkspaces
|
||||
} from "@app/hooks/api";
|
||||
import { INTERNAL_KMS_KEY_ID } from "@app/hooks/api/kms/types";
|
||||
import { InfisicalProjectTemplate, useListProjectTemplates } from "@app/hooks/api/projectTemplates";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string().trim().min(1, "Required").max(64, "Too long, maximum length is 64 characters"),
|
||||
description: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(256, "Description too long, max length is 256 characters")
|
||||
.optional(),
|
||||
addMembers: z.boolean(),
|
||||
kmsKeyId: z.string(),
|
||||
template: z.string()
|
||||
});
|
||||
|
||||
type TAddProjectFormData = z.infer<typeof formSchema>;
|
||||
|
||||
interface NewProjectModalProps {
|
||||
isOpen: boolean;
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
projectType: ProjectType;
|
||||
}
|
||||
|
||||
type NewProjectFormProps = Pick<NewProjectModalProps, "onOpenChange" | "projectType">;
|
||||
|
||||
const NewProjectForm = ({ onOpenChange, projectType }: NewProjectFormProps) => {
|
||||
const router = useRouter();
|
||||
const { currentOrg } = useOrganization();
|
||||
const { permission } = useOrgPermission();
|
||||
const { user } = useUser();
|
||||
const createWs = useCreateWorkspace();
|
||||
const { refetch: refetchWorkspaces } = useGetUserWorkspaces();
|
||||
const addUsersToProject = useAddUserToWsNonE2EE();
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const canReadProjectTemplates = permission.can(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.ProjectTemplates
|
||||
);
|
||||
|
||||
const { data: projectTemplates = [] } = useListProjectTemplates({
|
||||
enabled: Boolean(canReadProjectTemplates && subscription?.projectTemplates)
|
||||
});
|
||||
|
||||
const { data: externalKmsList } = useGetExternalKmsList(currentOrg?.id!, {
|
||||
enabled: permission.can(OrgPermissionActions.Read, OrgPermissionSubjects.Kms)
|
||||
});
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
reset,
|
||||
formState: { isSubmitting, errors }
|
||||
} = useForm<TAddProjectFormData>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
kmsKeyId: INTERNAL_KMS_KEY_ID,
|
||||
template: InfisicalProjectTemplate.Default
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(errors).length > 0) {
|
||||
console.log("Current form errors:", errors);
|
||||
}
|
||||
}, [errors]);
|
||||
|
||||
const onCreateProject = async ({
|
||||
name,
|
||||
description,
|
||||
addMembers,
|
||||
kmsKeyId,
|
||||
template
|
||||
}: TAddProjectFormData) => {
|
||||
// type check
|
||||
if (!currentOrg) return;
|
||||
if (!user) return;
|
||||
try {
|
||||
const {
|
||||
data: { project }
|
||||
} = await createWs.mutateAsync({
|
||||
projectName: name,
|
||||
projectDescription: description,
|
||||
kmsKeyId: kmsKeyId !== INTERNAL_KMS_KEY_ID ? kmsKeyId : undefined,
|
||||
template,
|
||||
type: projectType
|
||||
});
|
||||
const { id: newProjectId } = project;
|
||||
|
||||
if (addMembers) {
|
||||
const orgUsers = await fetchOrgUsers(currentOrg.id);
|
||||
await addUsersToProject.mutateAsync({
|
||||
usernames: orgUsers
|
||||
.filter(
|
||||
(member) => member.user.username !== user.username && member.status === "accepted"
|
||||
)
|
||||
.map((member) => member.user.username),
|
||||
projectId: newProjectId,
|
||||
orgId: currentOrg.id
|
||||
});
|
||||
}
|
||||
|
||||
await refetchWorkspaces();
|
||||
|
||||
createNotification({ text: "Project created", type: "success" });
|
||||
reset();
|
||||
onOpenChange(false);
|
||||
router.push(getProjectHomePage(project));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({ text: "Failed to create project", type: "error" });
|
||||
}
|
||||
};
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
return onCreateProject(data);
|
||||
});
|
||||
return (
|
||||
<form onSubmit={onSubmit}>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Controller
|
||||
control={control}
|
||||
name="name"
|
||||
defaultValue=""
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Project Name"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
className="flex-1"
|
||||
>
|
||||
<Input {...field} placeholder="Type your project name" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
defaultValue=""
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Project Description"
|
||||
isError={Boolean(error)}
|
||||
isOptional
|
||||
errorText={error?.message}
|
||||
className="flex-1"
|
||||
>
|
||||
<TextArea
|
||||
placeholder="Project description"
|
||||
{...field}
|
||||
rows={3}
|
||||
className="thin-scrollbar w-full !resize-none bg-mineshaft-900"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="template"
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Read}
|
||||
a={OrgPermissionSubjects.ProjectTemplates}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<FormControl
|
||||
label="Project Template"
|
||||
icon={<FontAwesomeIcon icon={faInfoCircle} size="sm" />}
|
||||
tooltipText={
|
||||
<>
|
||||
<p>
|
||||
Create this project from a template to provision it with custom environments
|
||||
and roles.
|
||||
</p>
|
||||
{subscription && !subscription.projectTemplates && (
|
||||
<p className="pt-2">Project templates are a paid feature.</p>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Select
|
||||
defaultValue={InfisicalProjectTemplate.Default}
|
||||
placeholder={InfisicalProjectTemplate.Default}
|
||||
isDisabled={!isAllowed || !subscription?.projectTemplates}
|
||||
value={value}
|
||||
onValueChange={onChange}
|
||||
className="w-full"
|
||||
>
|
||||
{projectTemplates.length
|
||||
? projectTemplates.map((template) => (
|
||||
<SelectItem key={template.id} value={template.name}>
|
||||
{template.name}
|
||||
</SelectItem>
|
||||
))
|
||||
: Object.values(InfisicalProjectTemplate).map((template) => (
|
||||
<SelectItem key={template} value={template}>
|
||||
{template}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 pl-1">
|
||||
<Controller
|
||||
control={control}
|
||||
name="addMembers"
|
||||
defaultValue={false}
|
||||
render={({ field: { onBlur, value, onChange } }) => (
|
||||
<OrgPermissionCan I={OrgPermissionActions.Read} a={OrgPermissionSubjects.Member}>
|
||||
{(isAllowed) => (
|
||||
<div>
|
||||
<Checkbox
|
||||
id="add-project-layout"
|
||||
isChecked={value}
|
||||
onCheckedChange={onChange}
|
||||
isDisabled={!isAllowed}
|
||||
onBlur={onBlur}
|
||||
>
|
||||
Add all members of my organization to this project
|
||||
</Checkbox>
|
||||
</div>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-14 flex">
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
<AccordionItem value="advance-settings" className="data-[state=open]:border-none">
|
||||
<AccordionTrigger className="h-fit flex-none pl-1 text-sm">
|
||||
<div className="order-1 ml-3">Advanced Settings</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<Controller
|
||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||
<FormControl errorText={error?.message} isError={Boolean(error)} label="KMS">
|
||||
<Select
|
||||
{...field}
|
||||
onValueChange={(e) => {
|
||||
onChange(e);
|
||||
}}
|
||||
className="mb-12 w-full bg-mineshaft-600"
|
||||
>
|
||||
<SelectItem value={INTERNAL_KMS_KEY_ID} key="kms-internal">
|
||||
Default Infisical KMS
|
||||
</SelectItem>
|
||||
{externalKmsList?.map((kms) => (
|
||||
<SelectItem value={kms.id} key={`kms-${kms.id}`}>
|
||||
{kms.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
control={control}
|
||||
name="kmsKeyId"
|
||||
/>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
<div className="absolute right-0 bottom-0 mr-6 mb-6 flex items-start justify-end">
|
||||
<ModalClose>
|
||||
<Button colorSchema="secondary" variant="plain" className="py-2">
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalClose>
|
||||
<Button isDisabled={isSubmitting} isLoading={isSubmitting} className="ml-4" type="submit">
|
||||
Create Project
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export const NewProjectModal: FC<NewProjectModalProps> = ({
|
||||
isOpen,
|
||||
onOpenChange,
|
||||
projectType
|
||||
}) => {
|
||||
return (
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||
<ModalContent
|
||||
title="Create a new project"
|
||||
subTitle="This project will contain your secrets and configurations."
|
||||
>
|
||||
<NewProjectForm onOpenChange={onOpenChange} projectType={projectType} />
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export { NewProjectModal } from "./NewProjectModal";
|
||||
Reference in New Issue
Block a user