mirror of
https://github.com/snowball-tools/snowballtools-base.git
synced 2024-12-22 16:37:44 +00:00
[T-4867: feat] Horizontal and vertical tabs component (#84)
* ⚡️ feat: create tabs component * ♻️ refactor: avoid big conflict on the example page * 🔧 chore: upgrade tailwindcss and install `@radix-ui/react-tabs` * ⚡️ feat: create globe icon component * 🎨 style: adjust vertical tab theme * 📝 docs: add vertical tabs to the example page
This commit is contained in:
parent
b5eef95d15
commit
ea44efa0f2
@ -5,8 +5,9 @@
|
||||
"dependencies": {
|
||||
"@fontsource/inter": "^5.0.16",
|
||||
"@material-tailwind/react": "^2.1.7",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-avatar": "^1.0.4",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
@ -76,6 +77,6 @@
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"prettier": "^3.1.0",
|
||||
"tailwindcss": "^3.3.6"
|
||||
"tailwindcss": "^3.4.1"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { CustomIcon, CustomIconProps } from './CustomIcon';
|
||||
|
||||
export const GlobeIcon = (props: CustomIconProps) => {
|
||||
return (
|
||||
<CustomIcon
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M10 17.9167C14.3723 17.9167 17.9167 14.3723 17.9167 10C17.9167 5.62776 14.3723 2.08334 10 2.08334M10 17.9167C5.62776 17.9167 2.08334 14.3723 2.08334 10C2.08334 5.62776 5.62776 2.08334 10 2.08334M10 17.9167C8.15906 17.9167 6.66668 14.3723 6.66668 10C6.66668 5.62776 8.15906 2.08334 10 2.08334M10 17.9167C11.841 17.9167 13.3333 14.3723 13.3333 10C13.3333 5.62776 11.841 2.08334 10 2.08334M17.5 10H2.50001"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="square"
|
||||
/>
|
||||
</CustomIcon>
|
||||
);
|
||||
};
|
@ -4,3 +4,4 @@ export * from './CheckIcon';
|
||||
export * from './ChevronGrabberHorizontal';
|
||||
export * from './ChevronLeft';
|
||||
export * from './ChevronRight';
|
||||
export * from './GlobeIcon';
|
||||
|
80
packages/frontend/src/components/shared/Tabs/Tabs.theme.ts
Normal file
80
packages/frontend/src/components/shared/Tabs/Tabs.theme.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import { tv, type VariantProps } from 'tailwind-variants';
|
||||
|
||||
export type TabsVariants = VariantProps<typeof tabsTheme>;
|
||||
|
||||
export const tabsTheme = tv({
|
||||
slots: {
|
||||
root: ['flex', 'data-[orientation=horizontal]:w-full'],
|
||||
triggerWrapper: [
|
||||
// Horizontal – default
|
||||
'px-1',
|
||||
'pb-5',
|
||||
'text-elements-low-em',
|
||||
'border-b-2',
|
||||
'border-transparent',
|
||||
'hover:border-border-interactive/10',
|
||||
'hover:text-elements-mid-em',
|
||||
'focus-within:border-border-interactive/10',
|
||||
'data-[state=active]:font-medium',
|
||||
'data-[state=active]:text-elements-high-em',
|
||||
'data-[state=active]:border-elements-high-em',
|
||||
// Vertical
|
||||
'data-[orientation=vertical]:px-3',
|
||||
'data-[orientation=vertical]:py-3',
|
||||
'data-[orientation=vertical]:min-w-[240px]',
|
||||
'data-[orientation=vertical]:focus-ring',
|
||||
'data-[orientation=vertical]:rounded-xl',
|
||||
'data-[orientation=vertical]:border-transparent',
|
||||
'data-[orientation=vertical]:hover:bg-base-bg-emphasized',
|
||||
'data-[orientation=vertical]:hover:text-elements-mid-em',
|
||||
'data-[orientation=vertical]:hover:border-transparent',
|
||||
'data-[orientation=vertical]:focus-visible:border-transparent',
|
||||
'data-[orientation=vertical]:focus-visible:bg-base-bg-emphasized',
|
||||
'data-[orientation=vertical]:focus-visible:text-elements-mid-em',
|
||||
'data-[orientation=vertical]:data-[state=active]:font-normal',
|
||||
'data-[orientation=vertical]:data-[state=active]:bg-base-bg-emphasized',
|
||||
'data-[orientation=vertical]:data-[state=active]:border-transparent',
|
||||
'data-[orientation=vertical]:data-[state=active]:hover:text-elements-high-em',
|
||||
'data-[orientation=vertical]:data-[state=active]:focus-visible:text-elements-high-em',
|
||||
],
|
||||
trigger: [
|
||||
'flex',
|
||||
'gap-1.5',
|
||||
'cursor-default',
|
||||
'select-none',
|
||||
'items-center',
|
||||
'justify-center',
|
||||
'outline-none',
|
||||
'leading-none',
|
||||
'tracking-[-0.006em]',
|
||||
'rounded-md',
|
||||
// Horizontal – default
|
||||
'data-[orientation=horizontal]:focus-ring',
|
||||
// Vertical
|
||||
'data-[orientation=vertical]:gap-2',
|
||||
],
|
||||
triggerList: [
|
||||
'flex',
|
||||
'shrink-0',
|
||||
'gap-5',
|
||||
'border-b',
|
||||
'border-transparent',
|
||||
// Horizontal – default
|
||||
'data-[orientation=horizontal]:border-border-interactive/10',
|
||||
// Vertical
|
||||
'data-[orientation=vertical]:flex-col',
|
||||
'data-[orientation=vertical]:gap-0.5',
|
||||
],
|
||||
content: ['text-elements-high-em', 'grow', 'outline-none', 'tab-content'],
|
||||
},
|
||||
variants: {
|
||||
fillWidth: {
|
||||
true: {
|
||||
trigger: ['flex-1'],
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
fillWidth: false,
|
||||
},
|
||||
});
|
51
packages/frontend/src/components/shared/Tabs/Tabs.tsx
Normal file
51
packages/frontend/src/components/shared/Tabs/Tabs.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React, { type ComponentPropsWithoutRef } from 'react';
|
||||
import { Root as TabsRoot } from '@radix-ui/react-tabs';
|
||||
|
||||
import { tabsTheme } from './Tabs.theme';
|
||||
import TabsContent from './TabsContent';
|
||||
import TabsList from './TabsList';
|
||||
import TabsTrigger from './TabsTrigger';
|
||||
import TabsProvider, { TabsProviderProps } from './TabsProvider';
|
||||
|
||||
export interface TabsProps extends ComponentPropsWithoutRef<typeof TabsRoot> {
|
||||
/**
|
||||
* The configuration for the tabs component.
|
||||
*/
|
||||
config?: TabsProviderProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* A component that allows users to switch between different tabs.
|
||||
* @returns JSX element representing the tabs component.
|
||||
*/
|
||||
export const Tabs = ({
|
||||
config,
|
||||
className,
|
||||
orientation = 'horizontal',
|
||||
...props
|
||||
}: TabsProps) => {
|
||||
const { root } = tabsTheme(config);
|
||||
|
||||
return (
|
||||
<TabsProvider {...config} orientation={orientation}>
|
||||
<TabsRoot
|
||||
{...props}
|
||||
orientation={orientation}
|
||||
className={root({ className })}
|
||||
/>
|
||||
</TabsProvider>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assigns the TabsTrigger class to the Trigger property of the Tabs object.
|
||||
*/
|
||||
Tabs.Trigger = TabsTrigger;
|
||||
/**
|
||||
* Assigns the TabsList object to the List property of the Tabs object.
|
||||
*/
|
||||
Tabs.List = TabsList;
|
||||
/**
|
||||
* Assigns the TabsContent component to the Content property of the Tabs component.
|
||||
*/
|
||||
Tabs.Content = TabsContent;
|
@ -0,0 +1,26 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
type ComponentPropsWithoutRef,
|
||||
type ElementRef,
|
||||
} from 'react';
|
||||
import { Content } from '@radix-ui/react-tabs';
|
||||
|
||||
import { tabsTheme } from '../Tabs.theme';
|
||||
|
||||
export interface TabsContentProps
|
||||
extends ComponentPropsWithoutRef<typeof Content> {}
|
||||
|
||||
/**
|
||||
* A component that represents the content of the tabs component.
|
||||
*/
|
||||
const TabsContent = forwardRef<ElementRef<typeof Content>, TabsContentProps>(
|
||||
({ className, ...props }, ref) => {
|
||||
const { content } = tabsTheme();
|
||||
return <Content ref={ref} className={content({ className })} {...props} />;
|
||||
},
|
||||
);
|
||||
|
||||
// Assigns the display name to the TabsContent component.
|
||||
TabsContent.displayName = 'TabsContent';
|
||||
|
||||
export { TabsContent };
|
@ -0,0 +1,3 @@
|
||||
import { TabsContent } from './TabsContent';
|
||||
|
||||
export default TabsContent;
|
@ -0,0 +1,25 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
type ComponentPropsWithoutRef,
|
||||
type ElementRef,
|
||||
} from 'react';
|
||||
import { List } from '@radix-ui/react-tabs';
|
||||
|
||||
import { tabsTheme } from 'components/shared/Tabs/Tabs.theme';
|
||||
|
||||
export interface TabsListProps extends ComponentPropsWithoutRef<typeof List> {}
|
||||
|
||||
/**
|
||||
* A component that represents the list of tabs.
|
||||
*/
|
||||
const TabsList = forwardRef<ElementRef<typeof List>, TabsListProps>(
|
||||
({ className, ...props }, ref) => {
|
||||
const { triggerList } = tabsTheme({ className });
|
||||
return <List ref={ref} className={triggerList({ className })} {...props} />;
|
||||
},
|
||||
);
|
||||
|
||||
// Assigns the display name to the TabsList component.
|
||||
TabsList.displayName = 'TabsList';
|
||||
|
||||
export { TabsList };
|
@ -0,0 +1,3 @@
|
||||
import { TabsList } from './TabsList';
|
||||
|
||||
export default TabsList;
|
@ -0,0 +1,47 @@
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
type PropsWithChildren,
|
||||
ComponentPropsWithoutRef,
|
||||
} from 'react';
|
||||
import { TabsVariants } from './Tabs.theme';
|
||||
import { Root as TabsRoot } from '@radix-ui/react-tabs';
|
||||
|
||||
export interface TabsProviderProps
|
||||
extends Partial<TabsVariants>,
|
||||
ComponentPropsWithoutRef<typeof TabsRoot> {}
|
||||
|
||||
type TabsProviderContext = ReturnType<typeof useTabsValues>;
|
||||
|
||||
const TabsContext = createContext<Partial<TabsProviderContext>>({});
|
||||
|
||||
// For inferring return type
|
||||
const useTabsValues = (props: TabsProviderProps) => {
|
||||
return props;
|
||||
};
|
||||
|
||||
/**
|
||||
* A provider component that allows users to switch between different tabs.
|
||||
* @returns JSX element representing the tabs provider component.
|
||||
*/
|
||||
export const TabsProvider = ({
|
||||
children,
|
||||
...props
|
||||
}: PropsWithChildren<TabsProviderProps>): JSX.Element => {
|
||||
const values = useTabsValues(props);
|
||||
return <TabsContext.Provider value={values}>{children}</TabsContext.Provider>;
|
||||
};
|
||||
|
||||
/**
|
||||
* A hook that returns the context of the tabs provider.
|
||||
* @returns The context of the tabs provider.
|
||||
*/
|
||||
export const useTabs = () => {
|
||||
const context = useContext(TabsContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useTabs was used outside of its Provider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export default TabsProvider;
|
@ -0,0 +1,59 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
type ComponentPropsWithoutRef,
|
||||
type ElementRef,
|
||||
type PropsWithChildren,
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
import { Trigger } from '@radix-ui/react-tabs';
|
||||
|
||||
import { tabsTheme } from 'components/shared/Tabs/Tabs.theme';
|
||||
import { useTabs } from 'components/shared/Tabs/TabsProvider';
|
||||
|
||||
export interface TabsTriggerProps
|
||||
extends ComponentPropsWithoutRef<typeof Trigger> {
|
||||
/**
|
||||
* The icon to display in the trigger.
|
||||
*/
|
||||
icon?: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* A component that represents the trigger for the tabs component.
|
||||
*/
|
||||
const TabsTrigger = forwardRef<
|
||||
ElementRef<typeof Trigger>,
|
||||
PropsWithChildren<TabsTriggerProps>
|
||||
>(({ className, icon, children, ...props }, ref) => {
|
||||
const config = useTabs();
|
||||
const { triggerWrapper, trigger } = tabsTheme(config);
|
||||
const orientation = config.orientation;
|
||||
|
||||
return (
|
||||
<Trigger
|
||||
ref={ref}
|
||||
// Disabled focus state for horizontal tabs
|
||||
tabIndex={orientation === 'horizontal' ? -1 : undefined}
|
||||
className={triggerWrapper({ className })}
|
||||
{...props}
|
||||
>
|
||||
{/* Need to add button in the trigger children because there's focus state inside the children */}
|
||||
<button
|
||||
data-orientation={orientation}
|
||||
// Disabled focus state for vertical tabs
|
||||
tabIndex={orientation === 'vertical' ? -1 : undefined}
|
||||
className={trigger()}
|
||||
>
|
||||
{/* Put the icon on the left of the text for veritcal tab */}
|
||||
{orientation === 'vertical' && icon}
|
||||
{children}
|
||||
{/* Put the icon on the right of the text for horizontal tab */}
|
||||
{orientation === 'horizontal' && icon}
|
||||
</button>
|
||||
</Trigger>
|
||||
);
|
||||
});
|
||||
|
||||
TabsTrigger.displayName = 'TabsTrigger';
|
||||
|
||||
export { TabsTrigger };
|
@ -0,0 +1,3 @@
|
||||
import { TabsTrigger } from './TabsTrigger';
|
||||
|
||||
export default TabsTrigger;
|
1
packages/frontend/src/components/shared/Tabs/index.ts
Normal file
1
packages/frontend/src/components/shared/Tabs/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './Tabs';
|
@ -1,43 +1,23 @@
|
||||
import { Avatar, AvatarVariants } from 'components/shared/Avatar';
|
||||
import { Badge, BadgeProps } from 'components/shared/Badge';
|
||||
import { Button, ButtonOrLinkProps } from 'components/shared/Button';
|
||||
import { Calendar } from 'components/shared/Calendar';
|
||||
import { Checkbox } from 'components/shared/Checkbox';
|
||||
import { PlusIcon } from 'components/shared/CustomIcon';
|
||||
import React, { useState } from 'react';
|
||||
import { Calendar } from 'components/shared/Calendar';
|
||||
import { Value } from 'react-calendar/dist/cjs/shared/types';
|
||||
|
||||
const avatarSizes: AvatarVariants['size'][] = [18, 20, 24, 28, 32, 36, 40, 44];
|
||||
const avatarVariants: AvatarVariants['type'][] = ['gray', 'orange', 'blue'];
|
||||
import {
|
||||
renderCheckbox,
|
||||
renderCheckboxWithDescription,
|
||||
} from './renders/checkbox';
|
||||
import { avatars, avatarsFallback } from './renders/avatar';
|
||||
import { renderBadges } from './renders/badge';
|
||||
import { renderButtonIcons, renderButtons } from './renders/button';
|
||||
import {
|
||||
renderTabWithBadges,
|
||||
renderTabs,
|
||||
renderVerticalTabs,
|
||||
} from './renders/tabs';
|
||||
|
||||
const Page = () => {
|
||||
const [singleDate, setSingleDate] = useState<Value>();
|
||||
const [dateRange, setDateRange] = useState<Value>();
|
||||
|
||||
const avatars = avatarSizes.map((size) => {
|
||||
return (
|
||||
<Avatar
|
||||
initials="SY"
|
||||
key={String(size)}
|
||||
size={size}
|
||||
imageSrc="/gray.png"
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const avatarsFallback = avatarVariants.map((color) => {
|
||||
return avatarSizes.map((size) => {
|
||||
return (
|
||||
<Avatar
|
||||
initials="SY"
|
||||
key={`${color}-${size}`}
|
||||
type={color}
|
||||
size={size}
|
||||
/>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="relative h-full min-h-full">
|
||||
<div className="flex flex-col items-center justify-center max-w-7xl mx-auto px-20 py-20">
|
||||
@ -51,114 +31,36 @@ const Page = () => {
|
||||
|
||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||
|
||||
{/* Insert Components here */}
|
||||
{/* Button */}
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Button</h1>
|
||||
<div className="flex flex-col gap-10">
|
||||
{['primary', 'secondary', 'tertiary', 'danger'].map(
|
||||
(variant, index) => (
|
||||
<div className="flex gap-5 flex-wrap" key={index}>
|
||||
{['lg', 'md', 'sm', 'xs', 'disabled'].map((size) => (
|
||||
<Button
|
||||
leftIcon={<PlusIcon />}
|
||||
rightIcon={<PlusIcon />}
|
||||
variant={variant as ButtonOrLinkProps['variant']}
|
||||
size={
|
||||
size !== 'disabled'
|
||||
? (size as ButtonOrLinkProps['size'])
|
||||
: 'md'
|
||||
}
|
||||
key={`${variant}-${size}`}
|
||||
disabled={size === 'disabled'}
|
||||
>
|
||||
Button
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
{[
|
||||
'primary',
|
||||
'secondary',
|
||||
'tertiary',
|
||||
'ghost',
|
||||
'danger',
|
||||
'danger-ghost',
|
||||
].map((variant, index) => (
|
||||
<div className="flex gap-5 flex-wrap" key={index}>
|
||||
{['lg', 'md', 'sm', 'xs', 'disabled'].map((size) => (
|
||||
<Button
|
||||
iconOnly
|
||||
variant={variant as ButtonOrLinkProps['variant']}
|
||||
size={
|
||||
size !== 'disabled'
|
||||
? (size as ButtonOrLinkProps['size'])
|
||||
: 'md'
|
||||
}
|
||||
key={`${variant}-${size}`}
|
||||
disabled={size === 'disabled'}
|
||||
>
|
||||
<PlusIcon />
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
{renderButtons()}
|
||||
{renderButtonIcons()}
|
||||
</div>
|
||||
|
||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||
|
||||
{/* Badge */}
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Badge</h1>
|
||||
<div className="space-y-5">
|
||||
{['primary', 'secondary', 'tertiary', 'inset'].map(
|
||||
(variant, index) => (
|
||||
<div className="flex gap-5" key={index}>
|
||||
{['sm', 'xs'].map((size) => (
|
||||
<Badge
|
||||
key={size}
|
||||
variant={variant as BadgeProps['variant']}
|
||||
size={size as BadgeProps['size']}
|
||||
>
|
||||
1
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-5">{renderBadges()}</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||
|
||||
{/* Checkbox */}
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Checkbox</h1>
|
||||
<div className="flex gap-10 flex-wrap">{renderCheckbox()}</div>
|
||||
<div className="flex gap-10 flex-wrap">
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Checkbox
|
||||
id={`checkbox-${index + 1}`}
|
||||
key={index}
|
||||
label={`Label ${index + 1}`}
|
||||
disabled={index === 2 || index === 4 ? true : false}
|
||||
checked={index === 4 ? true : undefined}
|
||||
value={`value-${index + 1}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-10 flex-wrap">
|
||||
{Array.from({ length: 2 }).map((_, index) => (
|
||||
<Checkbox
|
||||
id={`checkbox-description-${index + 1}`}
|
||||
key={index}
|
||||
label={`Label ${index + 1}`}
|
||||
description={`Description of the checkbox ${index + 1}`}
|
||||
value={`value-with-description-${index + 1}`}
|
||||
/>
|
||||
))}
|
||||
{renderCheckboxWithDescription()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||
|
||||
{/* Calendar */}
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Calendar</h1>
|
||||
<div className="flex flex-col gap-10">
|
||||
@ -185,18 +87,31 @@ const Page = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||
|
||||
{/* Avatar */}
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Avatar</h1>
|
||||
<div className="flex gap-10 flex-wrap max-w-[522px]">
|
||||
{avatars}
|
||||
{avatarsFallback}
|
||||
</div>
|
||||
{/* Avatar */}
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Avatar</h1>
|
||||
<div className="flex gap-10 flex-wrap max-w-[522px]">
|
||||
{avatars}
|
||||
{avatarsFallback}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Tabs</h1>
|
||||
<div className="flex flex-col gap-10 items-center justify-center">
|
||||
{renderTabs()}
|
||||
{renderTabWithBadges()}
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold">Vertical Tabs</h1>
|
||||
{renderVerticalTabs()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
19
packages/frontend/src/pages/components/renders/avatar.tsx
Normal file
19
packages/frontend/src/pages/components/renders/avatar.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { Avatar, AvatarVariants } from 'components/shared/Avatar';
|
||||
|
||||
const avatarSizes: AvatarVariants['size'][] = [18, 20, 24, 28, 32, 36, 40, 44];
|
||||
const avatarVariants: AvatarVariants['type'][] = ['gray', 'orange', 'blue'];
|
||||
|
||||
export const avatars = avatarSizes.map((size) => {
|
||||
return (
|
||||
<Avatar initials="SY" key={String(size)} size={size} imageSrc="/gray.png" />
|
||||
);
|
||||
});
|
||||
|
||||
export const avatarsFallback = avatarVariants.map((color) => {
|
||||
return avatarSizes.map((size) => {
|
||||
return (
|
||||
<Avatar initials="SY" key={`${color}-${size}`} type={color} size={size} />
|
||||
);
|
||||
});
|
||||
});
|
20
packages/frontend/src/pages/components/renders/badge.tsx
Normal file
20
packages/frontend/src/pages/components/renders/badge.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Badge } from 'components/shared/Badge';
|
||||
import { BadgeTheme } from 'components/shared/Badge/Badge.theme';
|
||||
|
||||
export const renderBadges = () => {
|
||||
return ['primary', 'secondary', 'tertiary', 'inset'].map((variant, index) => (
|
||||
<div className="flex gap-5" key={index}>
|
||||
{['sm', 'xs'].map((size) => (
|
||||
<Badge
|
||||
key={size}
|
||||
variant={variant as BadgeTheme['variant']}
|
||||
size={size as BadgeTheme['size']}
|
||||
>
|
||||
1
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
));
|
||||
};
|
49
packages/frontend/src/pages/components/renders/button.tsx
Normal file
49
packages/frontend/src/pages/components/renders/button.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { Button, ButtonTheme } from 'components/shared/Button';
|
||||
import { PlusIcon } from 'components/shared/CustomIcon';
|
||||
import React from 'react';
|
||||
|
||||
export const renderButtons = () => {
|
||||
return ['primary', 'secondary', 'tertiary', 'danger'].map(
|
||||
(variant, index) => (
|
||||
<div className="flex gap-5 flex-wrap" key={index}>
|
||||
{['lg', 'md', 'sm', 'xs', 'disabled'].map((size) => (
|
||||
<Button
|
||||
leftIcon={<PlusIcon />}
|
||||
rightIcon={<PlusIcon />}
|
||||
variant={variant as ButtonTheme['variant']}
|
||||
size={size !== 'disabled' ? (size as ButtonTheme['size']) : 'md'}
|
||||
key={`${variant}-${size}`}
|
||||
disabled={size === 'disabled'}
|
||||
>
|
||||
Button
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
export const renderButtonIcons = () => {
|
||||
return [
|
||||
'primary',
|
||||
'secondary',
|
||||
'tertiary',
|
||||
'ghost',
|
||||
'danger',
|
||||
'danger-ghost',
|
||||
].map((variant, index) => (
|
||||
<div className="flex gap-5 flex-wrap" key={index}>
|
||||
{['lg', 'md', 'sm', 'xs', 'disabled'].map((size) => (
|
||||
<Button
|
||||
iconOnly
|
||||
variant={variant as ButtonTheme['variant']}
|
||||
size={size !== 'disabled' ? (size as ButtonTheme['size']) : 'md'}
|
||||
key={`${variant}-${size}`}
|
||||
disabled={size === 'disabled'}
|
||||
>
|
||||
<PlusIcon />
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
));
|
||||
};
|
27
packages/frontend/src/pages/components/renders/checkbox.tsx
Normal file
27
packages/frontend/src/pages/components/renders/checkbox.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { Checkbox } from 'components/shared/Checkbox';
|
||||
|
||||
export const renderCheckbox = () => {
|
||||
return Array.from({ length: 5 }).map((_, index) => (
|
||||
<Checkbox
|
||||
id={`checkbox-${index + 1}`}
|
||||
key={index}
|
||||
label={`Label ${index + 1}`}
|
||||
disabled={index === 2 || index === 4 ? true : false}
|
||||
checked={index === 4 ? true : undefined}
|
||||
value={`value-${index + 1}`}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
export const renderCheckboxWithDescription = () => {
|
||||
return Array.from({ length: 2 }).map((_, index) => (
|
||||
<Checkbox
|
||||
id={`checkbox-description-${index + 1}`}
|
||||
key={index}
|
||||
label={`Label ${index + 1}`}
|
||||
description={`Description of the checkbox ${index + 1}`}
|
||||
value={`value-with-description-${index + 1}`}
|
||||
/>
|
||||
));
|
||||
};
|
56
packages/frontend/src/pages/components/renders/tabs.tsx
Normal file
56
packages/frontend/src/pages/components/renders/tabs.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
import { Tabs } from 'components/shared/Tabs';
|
||||
import { Badge } from 'components/shared/Badge';
|
||||
import { GlobeIcon } from 'components/shared/CustomIcon';
|
||||
|
||||
const tabs = Array.from({ length: 8 });
|
||||
|
||||
export const renderTabs = () => {
|
||||
return (
|
||||
<Tabs defaultValue="A">
|
||||
<Tabs.List>
|
||||
{tabs.map((_, index) => (
|
||||
<Tabs.Trigger key={index} value={index.toString()}>
|
||||
Tab item {index}
|
||||
</Tabs.Trigger>
|
||||
))}
|
||||
</Tabs.List>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
export const renderTabWithBadges = () => {
|
||||
return (
|
||||
<Tabs defaultValue="A">
|
||||
<Tabs.List>
|
||||
{tabs.map((_, index) => (
|
||||
<Tabs.Trigger
|
||||
key={index}
|
||||
value={index.toString()}
|
||||
icon={<Badge variant="tertiary">{index}</Badge>}
|
||||
>
|
||||
Tab item
|
||||
</Tabs.Trigger>
|
||||
))}
|
||||
</Tabs.List>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
export const renderVerticalTabs = () => {
|
||||
return (
|
||||
<Tabs defaultValue="A" orientation="vertical">
|
||||
<Tabs.List>
|
||||
{tabs.slice(0, 4).map((_, index) => (
|
||||
<Tabs.Trigger
|
||||
key={index}
|
||||
icon={<GlobeIcon />}
|
||||
value={index.toString()}
|
||||
>
|
||||
Tab item {index}
|
||||
</Tabs.Trigger>
|
||||
))}
|
||||
</Tabs.List>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
147
yarn.lock
147
yarn.lock
@ -1280,7 +1280,7 @@
|
||||
resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz"
|
||||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
||||
|
||||
"@babel/runtime@^7.10.4", "@babel/runtime@^7.13.10", "@babel/runtime@^7.23.7", "@babel/runtime@^7.13.10", "@babel/runtime@^7.23.7", "@babel/runtime@^7.3.1":
|
||||
"@babel/runtime@^7.10.4", "@babel/runtime@^7.13.10", "@babel/runtime@^7.23.7", "@babel/runtime@^7.3.1":
|
||||
version "7.23.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7"
|
||||
integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==
|
||||
@ -3277,6 +3277,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
|
||||
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
|
||||
|
||||
"@radix-ui/primitive@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd"
|
||||
integrity sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-avatar@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-avatar/-/react-avatar-1.0.4.tgz#de9a5349d9e3de7bbe990334c4d2011acbbb9623"
|
||||
@ -3288,57 +3295,6 @@
|
||||
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||
|
||||
"@radix-ui/react-compose-refs@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989"
|
||||
integrity sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-context@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c"
|
||||
integrity sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-primitive@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0"
|
||||
integrity sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-slot" "1.0.2"
|
||||
|
||||
"@radix-ui/react-slot@1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab"
|
||||
integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-compose-refs" "1.0.1"
|
||||
|
||||
"@radix-ui/react-use-callback-ref@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a"
|
||||
integrity sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-use-layout-effect@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399"
|
||||
integrity sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/primitive@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd"
|
||||
integrity sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-checkbox@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz#98f22c38d5010dd6df4c5744cac74087e3275f4b"
|
||||
@ -3354,6 +3310,17 @@
|
||||
"@radix-ui/react-use-previous" "1.0.1"
|
||||
"@radix-ui/react-use-size" "1.0.1"
|
||||
|
||||
"@radix-ui/react-collection@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.3.tgz#9595a66e09026187524a36c6e7e9c7d286469159"
|
||||
integrity sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-compose-refs" "1.0.1"
|
||||
"@radix-ui/react-context" "1.0.1"
|
||||
"@radix-ui/react-primitive" "1.0.3"
|
||||
"@radix-ui/react-slot" "1.0.2"
|
||||
|
||||
"@radix-ui/react-compose-refs@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989"
|
||||
@ -3368,6 +3335,21 @@
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-direction@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b"
|
||||
integrity sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-id@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.1.tgz#73cdc181f650e4df24f0b6a5b7aa426b912c88c0"
|
||||
integrity sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||
|
||||
"@radix-ui/react-presence@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.1.tgz#491990ba913b8e2a5db1b06b203cb24b5cdef9ba"
|
||||
@ -3385,6 +3367,22 @@
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-slot" "1.0.2"
|
||||
|
||||
"@radix-ui/react-roving-focus@1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz#e90c4a6a5f6ac09d3b8c1f5b5e81aab2f0db1974"
|
||||
integrity sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "1.0.1"
|
||||
"@radix-ui/react-collection" "1.0.3"
|
||||
"@radix-ui/react-compose-refs" "1.0.1"
|
||||
"@radix-ui/react-context" "1.0.1"
|
||||
"@radix-ui/react-direction" "1.0.1"
|
||||
"@radix-ui/react-id" "1.0.1"
|
||||
"@radix-ui/react-primitive" "1.0.3"
|
||||
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||
"@radix-ui/react-use-controllable-state" "1.0.1"
|
||||
|
||||
"@radix-ui/react-slot@1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab"
|
||||
@ -3393,6 +3391,21 @@
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-compose-refs" "1.0.1"
|
||||
|
||||
"@radix-ui/react-tabs@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz#993608eec55a5d1deddd446fa9978d2bc1053da2"
|
||||
integrity sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "1.0.1"
|
||||
"@radix-ui/react-context" "1.0.1"
|
||||
"@radix-ui/react-direction" "1.0.1"
|
||||
"@radix-ui/react-id" "1.0.1"
|
||||
"@radix-ui/react-presence" "1.0.1"
|
||||
"@radix-ui/react-primitive" "1.0.3"
|
||||
"@radix-ui/react-roving-focus" "1.0.4"
|
||||
"@radix-ui/react-use-controllable-state" "1.0.1"
|
||||
|
||||
"@radix-ui/react-use-callback-ref@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a"
|
||||
@ -15239,7 +15252,7 @@ tailwind-variants@^0.2.0:
|
||||
dependencies:
|
||||
tailwind-merge "^2.2.0"
|
||||
|
||||
tailwindcss@^3.0.2, tailwindcss@^3.3.6:
|
||||
tailwindcss@^3.0.2:
|
||||
version "3.3.6"
|
||||
resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.6.tgz"
|
||||
integrity sha512-AKjF7qbbLvLaPieoKeTjG1+FyNZT6KaJMJPFeQyLfIp7l82ggH1fbHJSsYIvnbTFQOlkh+gBYpyby5GT1LIdLw==
|
||||
@ -15267,6 +15280,34 @@ tailwindcss@^3.0.2, tailwindcss@^3.3.6:
|
||||
resolve "^1.22.2"
|
||||
sucrase "^3.32.0"
|
||||
|
||||
tailwindcss@^3.4.1:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.1.tgz#f512ca5d1dd4c9503c7d3d28a968f1ad8f5c839d"
|
||||
integrity sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==
|
||||
dependencies:
|
||||
"@alloc/quick-lru" "^5.2.0"
|
||||
arg "^5.0.2"
|
||||
chokidar "^3.5.3"
|
||||
didyoumean "^1.2.2"
|
||||
dlv "^1.1.3"
|
||||
fast-glob "^3.3.0"
|
||||
glob-parent "^6.0.2"
|
||||
is-glob "^4.0.3"
|
||||
jiti "^1.19.1"
|
||||
lilconfig "^2.1.0"
|
||||
micromatch "^4.0.5"
|
||||
normalize-path "^3.0.0"
|
||||
object-hash "^3.0.0"
|
||||
picocolors "^1.0.0"
|
||||
postcss "^8.4.23"
|
||||
postcss-import "^15.1.0"
|
||||
postcss-js "^4.0.1"
|
||||
postcss-load-config "^4.0.1"
|
||||
postcss-nested "^6.0.1"
|
||||
postcss-selector-parser "^6.0.11"
|
||||
resolve "^1.22.2"
|
||||
sucrase "^3.32.0"
|
||||
|
||||
tapable@^1.0.0:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz"
|
||||
|
Loading…
Reference in New Issue
Block a user