From ea44efa0f22e556033de3003cbb79fccfb7f1c3f Mon Sep 17 00:00:00 2001 From: Wahyu Kurniawan Date: Wed, 21 Feb 2024 16:13:16 +0700 Subject: [PATCH] [T-4867: feat] Horizontal and vertical tabs component (#84) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ⚡️ 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 --- packages/frontend/package.json | 5 +- .../shared/CustomIcon/GlobeIcon.tsx | 20 ++ .../src/components/shared/CustomIcon/index.ts | 1 + .../src/components/shared/Tabs/Tabs.theme.ts | 80 ++++++++ .../src/components/shared/Tabs/Tabs.tsx | 51 ++++++ .../shared/Tabs/TabsContent/TabsContent.tsx | 26 +++ .../shared/Tabs/TabsContent/index.ts | 3 + .../shared/Tabs/TabsList/TabsList.tsx | 25 +++ .../components/shared/Tabs/TabsList/index.ts | 3 + .../components/shared/Tabs/TabsProvider.tsx | 47 +++++ .../shared/Tabs/TabsTrigger/TabsTrigger.tsx | 59 ++++++ .../shared/Tabs/TabsTrigger/index.ts | 3 + .../src/components/shared/Tabs/index.ts | 1 + .../frontend/src/pages/components/index.tsx | 171 +++++------------- .../src/pages/components/renders/avatar.tsx | 19 ++ .../src/pages/components/renders/badge.tsx | 20 ++ .../src/pages/components/renders/button.tsx | 49 +++++ .../src/pages/components/renders/checkbox.tsx | 27 +++ .../src/pages/components/renders/tabs.tsx | 56 ++++++ yarn.lock | 147 +++++++++------ 20 files changed, 630 insertions(+), 183 deletions(-) create mode 100644 packages/frontend/src/components/shared/CustomIcon/GlobeIcon.tsx create mode 100644 packages/frontend/src/components/shared/Tabs/Tabs.theme.ts create mode 100644 packages/frontend/src/components/shared/Tabs/Tabs.tsx create mode 100644 packages/frontend/src/components/shared/Tabs/TabsContent/TabsContent.tsx create mode 100644 packages/frontend/src/components/shared/Tabs/TabsContent/index.ts create mode 100644 packages/frontend/src/components/shared/Tabs/TabsList/TabsList.tsx create mode 100644 packages/frontend/src/components/shared/Tabs/TabsList/index.ts create mode 100644 packages/frontend/src/components/shared/Tabs/TabsProvider.tsx create mode 100644 packages/frontend/src/components/shared/Tabs/TabsTrigger/TabsTrigger.tsx create mode 100644 packages/frontend/src/components/shared/Tabs/TabsTrigger/index.ts create mode 100644 packages/frontend/src/components/shared/Tabs/index.ts create mode 100644 packages/frontend/src/pages/components/renders/avatar.tsx create mode 100644 packages/frontend/src/pages/components/renders/badge.tsx create mode 100644 packages/frontend/src/pages/components/renders/button.tsx create mode 100644 packages/frontend/src/pages/components/renders/checkbox.tsx create mode 100644 packages/frontend/src/pages/components/renders/tabs.tsx diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 7e36902..a967442 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -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" } } diff --git a/packages/frontend/src/components/shared/CustomIcon/GlobeIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/GlobeIcon.tsx new file mode 100644 index 0000000..f2a23ca --- /dev/null +++ b/packages/frontend/src/components/shared/CustomIcon/GlobeIcon.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { CustomIcon, CustomIconProps } from './CustomIcon'; + +export const GlobeIcon = (props: CustomIconProps) => { + return ( + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/index.ts b/packages/frontend/src/components/shared/CustomIcon/index.ts index 34b7f48..c1ec641 100644 --- a/packages/frontend/src/components/shared/CustomIcon/index.ts +++ b/packages/frontend/src/components/shared/CustomIcon/index.ts @@ -4,3 +4,4 @@ export * from './CheckIcon'; export * from './ChevronGrabberHorizontal'; export * from './ChevronLeft'; export * from './ChevronRight'; +export * from './GlobeIcon'; diff --git a/packages/frontend/src/components/shared/Tabs/Tabs.theme.ts b/packages/frontend/src/components/shared/Tabs/Tabs.theme.ts new file mode 100644 index 0000000..4667fe8 --- /dev/null +++ b/packages/frontend/src/components/shared/Tabs/Tabs.theme.ts @@ -0,0 +1,80 @@ +import { tv, type VariantProps } from 'tailwind-variants'; + +export type TabsVariants = VariantProps; + +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, + }, +}); diff --git a/packages/frontend/src/components/shared/Tabs/Tabs.tsx b/packages/frontend/src/components/shared/Tabs/Tabs.tsx new file mode 100644 index 0000000..af1c65d --- /dev/null +++ b/packages/frontend/src/components/shared/Tabs/Tabs.tsx @@ -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 { + /** + * 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 ( + + + + ); +}; + +/** + * 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; diff --git a/packages/frontend/src/components/shared/Tabs/TabsContent/TabsContent.tsx b/packages/frontend/src/components/shared/Tabs/TabsContent/TabsContent.tsx new file mode 100644 index 0000000..2120099 --- /dev/null +++ b/packages/frontend/src/components/shared/Tabs/TabsContent/TabsContent.tsx @@ -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 {} + +/** + * A component that represents the content of the tabs component. + */ +const TabsContent = forwardRef, TabsContentProps>( + ({ className, ...props }, ref) => { + const { content } = tabsTheme(); + return ; + }, +); + +// Assigns the display name to the TabsContent component. +TabsContent.displayName = 'TabsContent'; + +export { TabsContent }; diff --git a/packages/frontend/src/components/shared/Tabs/TabsContent/index.ts b/packages/frontend/src/components/shared/Tabs/TabsContent/index.ts new file mode 100644 index 0000000..0e80ffb --- /dev/null +++ b/packages/frontend/src/components/shared/Tabs/TabsContent/index.ts @@ -0,0 +1,3 @@ +import { TabsContent } from './TabsContent'; + +export default TabsContent; diff --git a/packages/frontend/src/components/shared/Tabs/TabsList/TabsList.tsx b/packages/frontend/src/components/shared/Tabs/TabsList/TabsList.tsx new file mode 100644 index 0000000..f5eb149 --- /dev/null +++ b/packages/frontend/src/components/shared/Tabs/TabsList/TabsList.tsx @@ -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 {} + +/** + * A component that represents the list of tabs. + */ +const TabsList = forwardRef, TabsListProps>( + ({ className, ...props }, ref) => { + const { triggerList } = tabsTheme({ className }); + return ; + }, +); + +// Assigns the display name to the TabsList component. +TabsList.displayName = 'TabsList'; + +export { TabsList }; diff --git a/packages/frontend/src/components/shared/Tabs/TabsList/index.ts b/packages/frontend/src/components/shared/Tabs/TabsList/index.ts new file mode 100644 index 0000000..f02fd4e --- /dev/null +++ b/packages/frontend/src/components/shared/Tabs/TabsList/index.ts @@ -0,0 +1,3 @@ +import { TabsList } from './TabsList'; + +export default TabsList; diff --git a/packages/frontend/src/components/shared/Tabs/TabsProvider.tsx b/packages/frontend/src/components/shared/Tabs/TabsProvider.tsx new file mode 100644 index 0000000..b51c680 --- /dev/null +++ b/packages/frontend/src/components/shared/Tabs/TabsProvider.tsx @@ -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, + ComponentPropsWithoutRef {} + +type TabsProviderContext = ReturnType; + +const TabsContext = createContext>({}); + +// 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): JSX.Element => { + const values = useTabsValues(props); + return {children}; +}; + +/** + * 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; diff --git a/packages/frontend/src/components/shared/Tabs/TabsTrigger/TabsTrigger.tsx b/packages/frontend/src/components/shared/Tabs/TabsTrigger/TabsTrigger.tsx new file mode 100644 index 0000000..61093bf --- /dev/null +++ b/packages/frontend/src/components/shared/Tabs/TabsTrigger/TabsTrigger.tsx @@ -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 { + /** + * The icon to display in the trigger. + */ + icon?: ReactNode; +} + +/** + * A component that represents the trigger for the tabs component. + */ +const TabsTrigger = forwardRef< + ElementRef, + PropsWithChildren +>(({ className, icon, children, ...props }, ref) => { + const config = useTabs(); + const { triggerWrapper, trigger } = tabsTheme(config); + const orientation = config.orientation; + + return ( + + {/* Need to add button in the trigger children because there's focus state inside the children */} + + + ); +}); + +TabsTrigger.displayName = 'TabsTrigger'; + +export { TabsTrigger }; diff --git a/packages/frontend/src/components/shared/Tabs/TabsTrigger/index.ts b/packages/frontend/src/components/shared/Tabs/TabsTrigger/index.ts new file mode 100644 index 0000000..57b0b3e --- /dev/null +++ b/packages/frontend/src/components/shared/Tabs/TabsTrigger/index.ts @@ -0,0 +1,3 @@ +import { TabsTrigger } from './TabsTrigger'; + +export default TabsTrigger; diff --git a/packages/frontend/src/components/shared/Tabs/index.ts b/packages/frontend/src/components/shared/Tabs/index.ts new file mode 100644 index 0000000..856dbbb --- /dev/null +++ b/packages/frontend/src/components/shared/Tabs/index.ts @@ -0,0 +1 @@ +export * from './Tabs'; diff --git a/packages/frontend/src/pages/components/index.tsx b/packages/frontend/src/pages/components/index.tsx index 456f433..c1c46f6 100644 --- a/packages/frontend/src/pages/components/index.tsx +++ b/packages/frontend/src/pages/components/index.tsx @@ -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(); const [dateRange, setDateRange] = useState(); - const avatars = avatarSizes.map((size) => { - return ( - - ); - }); - - const avatarsFallback = avatarVariants.map((color) => { - return avatarSizes.map((size) => { - return ( - - ); - }); - }); - return (
@@ -51,114 +31,36 @@ const Page = () => {
- {/* Insert Components here */} + {/* Button */}

Button

- {['primary', 'secondary', 'tertiary', 'danger'].map( - (variant, index) => ( -
- {['lg', 'md', 'sm', 'xs', 'disabled'].map((size) => ( - - ))} -
- ), - )} - {[ - 'primary', - 'secondary', - 'tertiary', - 'ghost', - 'danger', - 'danger-ghost', - ].map((variant, index) => ( -
- {['lg', 'md', 'sm', 'xs', 'disabled'].map((size) => ( - - ))} -
- ))} + {renderButtons()} + {renderButtonIcons()}
+ {/* Badge */}

Badge

-
- {['primary', 'secondary', 'tertiary', 'inset'].map( - (variant, index) => ( -
- {['sm', 'xs'].map((size) => ( - - 1 - - ))} -
- ), - )} -
+
{renderBadges()}
+ {/* Checkbox */}

Checkbox

+
{renderCheckbox()}
- {Array.from({ length: 5 }).map((_, index) => ( - - ))} -
-
- {Array.from({ length: 2 }).map((_, index) => ( - - ))} + {renderCheckboxWithDescription()}
+ {/* Calendar */}

Calendar

@@ -185,18 +87,31 @@ const Page = () => { />
+
-
+
- {/* Avatar */} -
-

Avatar

-
- {avatars} - {avatarsFallback} -
+ {/* Avatar */} +
+

Avatar

+
+ {avatars} + {avatarsFallback}
+ +
+ + {/* Tabs */} +
+

Tabs

+
+ {renderTabs()} + {renderTabWithBadges()} +
+

Vertical Tabs

+ {renderVerticalTabs()} +
diff --git a/packages/frontend/src/pages/components/renders/avatar.tsx b/packages/frontend/src/pages/components/renders/avatar.tsx new file mode 100644 index 0000000..9ebaa03 --- /dev/null +++ b/packages/frontend/src/pages/components/renders/avatar.tsx @@ -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 ( + + ); +}); + +export const avatarsFallback = avatarVariants.map((color) => { + return avatarSizes.map((size) => { + return ( + + ); + }); +}); diff --git a/packages/frontend/src/pages/components/renders/badge.tsx b/packages/frontend/src/pages/components/renders/badge.tsx new file mode 100644 index 0000000..e4eb311 --- /dev/null +++ b/packages/frontend/src/pages/components/renders/badge.tsx @@ -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) => ( +
+ {['sm', 'xs'].map((size) => ( + + 1 + + ))} +
+ )); +}; diff --git a/packages/frontend/src/pages/components/renders/button.tsx b/packages/frontend/src/pages/components/renders/button.tsx new file mode 100644 index 0000000..c1c69c4 --- /dev/null +++ b/packages/frontend/src/pages/components/renders/button.tsx @@ -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) => ( +
+ {['lg', 'md', 'sm', 'xs', 'disabled'].map((size) => ( + + ))} +
+ ), + ); +}; + +export const renderButtonIcons = () => { + return [ + 'primary', + 'secondary', + 'tertiary', + 'ghost', + 'danger', + 'danger-ghost', + ].map((variant, index) => ( +
+ {['lg', 'md', 'sm', 'xs', 'disabled'].map((size) => ( + + ))} +
+ )); +}; diff --git a/packages/frontend/src/pages/components/renders/checkbox.tsx b/packages/frontend/src/pages/components/renders/checkbox.tsx new file mode 100644 index 0000000..66c9eef --- /dev/null +++ b/packages/frontend/src/pages/components/renders/checkbox.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Checkbox } from 'components/shared/Checkbox'; + +export const renderCheckbox = () => { + return Array.from({ length: 5 }).map((_, index) => ( + + )); +}; + +export const renderCheckboxWithDescription = () => { + return Array.from({ length: 2 }).map((_, index) => ( + + )); +}; diff --git a/packages/frontend/src/pages/components/renders/tabs.tsx b/packages/frontend/src/pages/components/renders/tabs.tsx new file mode 100644 index 0000000..d344317 --- /dev/null +++ b/packages/frontend/src/pages/components/renders/tabs.tsx @@ -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.map((_, index) => ( + + Tab item {index} + + ))} + + + ); +}; + +export const renderTabWithBadges = () => { + return ( + + + {tabs.map((_, index) => ( + {index}} + > + Tab item + + ))} + + + ); +}; + +export const renderVerticalTabs = () => { + return ( + + + {tabs.slice(0, 4).map((_, index) => ( + } + value={index.toString()} + > + Tab item {index} + + ))} + + + ); +}; diff --git a/yarn.lock b/yarn.lock index 87b3448..d24c5a8 100644 --- a/yarn.lock +++ b/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"