diff --git a/src/frontend/apps/web/app/(main)/[stockSlug]/page.tsx b/src/frontend/apps/web/app/(main)/[stockSlug]/page.tsx index 5005c2a3..d5c88713 100644 --- a/src/frontend/apps/web/app/(main)/[stockSlug]/page.tsx +++ b/src/frontend/apps/web/app/(main)/[stockSlug]/page.tsx @@ -21,7 +21,7 @@ export default function StockDetailsPage({ params }) { return (
-
+
{ return ( -
- - -
+ + + -
-
+ + ); }; diff --git a/src/frontend/apps/web/src/features/chat/ui/chat-header.tsx b/src/frontend/apps/web/src/features/chat/ui/chat-header.tsx index b84cdc7b..a6f02f27 100644 --- a/src/frontend/apps/web/src/features/chat/ui/chat-header.tsx +++ b/src/frontend/apps/web/src/features/chat/ui/chat-header.tsx @@ -1,5 +1,12 @@ +import { SidebarTrigger } from '@workspace/ui/components'; + const ChatHeader = () => { - return
123
; + return ( +
+ + 123 +
+ ); }; export default ChatHeader; diff --git a/src/frontend/apps/web/src/shared/components/sidebar/index.ts b/src/frontend/apps/web/src/shared/components/sidebar/index.ts new file mode 100644 index 00000000..932a53ec --- /dev/null +++ b/src/frontend/apps/web/src/shared/components/sidebar/index.ts @@ -0,0 +1 @@ +export { default as SidebarContainer } from './ui/sidebar-container'; diff --git a/src/frontend/apps/web/src/shared/components/sidebar/ui/sidebar-container.tsx b/src/frontend/apps/web/src/shared/components/sidebar/ui/sidebar-container.tsx new file mode 100644 index 00000000..71c813ae --- /dev/null +++ b/src/frontend/apps/web/src/shared/components/sidebar/ui/sidebar-container.tsx @@ -0,0 +1,93 @@ +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, + Sidebar, + SidebarContent, + SidebarGroup, + SidebarGroupContent, + SidebarGroupLabel, + SidebarHeader, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from '@workspace/ui/components'; +import { ChevronRight } from 'lucide-react'; + +const SidebarContainer = () => { + const data = { + navMain: [ + { + title: '참여한 채널', + items: [ + { + title: 'one', + url: '#', + }, + { + title: 'two', + url: '#', + isActive: true, + }, + ], + }, + { + title: '전체 채널', + items: [ + { + title: 'three', + url: '#', + }, + { + title: 'four', + url: '#', + }, + ], + }, + ], + }; + return ( + + #WSName + + {data.navMain.map((item) => ( + + + + + {item.title} + + + + + + + {item.items.map((item) => ( + + + {item.title} + + + ))} + + + + + + ))} + + + ); +}; +export default SidebarContainer; diff --git a/src/frontend/packages/ui/src/components/Sidebar/sidebar.tsx b/src/frontend/packages/ui/src/components/Sidebar/sidebar.tsx index f5fe5f8d..e19b11d6 100644 --- a/src/frontend/packages/ui/src/components/Sidebar/sidebar.tsx +++ b/src/frontend/packages/ui/src/components/Sidebar/sidebar.tsx @@ -12,7 +12,12 @@ import { Input } from '../Input'; import { Separator } from '../Separate'; import { Sheet, SheetContent } from '../Sheet/sheet'; import { Skeleton } from '../Skeleton'; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../Tooltip'; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '../Tooltip'; const SIDEBAR_COOKIE_NAME = 'sidebar:state'; const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; @@ -49,85 +54,114 @@ const SidebarProvider = React.forwardRef< open?: boolean; onOpenChange?: (open: boolean) => void; } ->(({ defaultOpen = true, open: openProp, onOpenChange: setOpenProp, className, style, children, ...props }, ref) => { - const isMobile = useIsMobile(); - const [openMobile, setOpenMobile] = React.useState(false); - - // This is the internal state of the sidebar. - // We use openProp and setOpenProp for control from outside the component. - const [_open, _setOpen] = React.useState(defaultOpen); - const open = openProp ?? _open; - const setOpen = React.useCallback( - (value: boolean | ((value: boolean) => boolean)) => { - const openState = typeof value === 'function' ? value(open) : value; - if (setOpenProp) { - setOpenProp(openState); - } else { - _setOpen(openState); - } - - // This sets the cookie to keep the sidebar state. - document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`; +>( + ( + { + defaultOpen = true, + open: openProp, + onOpenChange: setOpenProp, + className, + style, + children, + ...props }, - [setOpenProp, open], - ); + ref, + ) => { + const isMobile = useIsMobile(); + const [openMobile, setOpenMobile] = React.useState(false); + + // This is the internal state of the sidebar. + // We use openProp and setOpenProp for control from outside the component. + const [_open, _setOpen] = React.useState(defaultOpen); + const open = openProp ?? _open; + const setOpen = React.useCallback( + (value: boolean | ((value: boolean) => boolean)) => { + const openState = typeof value === 'function' ? value(open) : value; + if (setOpenProp) { + setOpenProp(openState); + } else { + _setOpen(openState); + } - // Helper to toggle the sidebar. - const toggleSidebar = React.useCallback(() => { - return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open); - }, [isMobile, setOpen, setOpenMobile]); + // This sets the cookie to keep the sidebar state. + document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`; + }, + [setOpenProp, open], + ); - // Adds a keyboard shortcut to toggle the sidebar. - React.useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) { - event.preventDefault(); - toggleSidebar(); - } - }; - - window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); - }, [toggleSidebar]); - - // We add a state so that we can do data-state="expanded" or "collapsed". - // This makes it easier to style the sidebar with Tailwind classes. - const state = open ? 'expanded' : 'collapsed'; - - const contextValue = React.useMemo( - () => ({ - state, - open, - setOpen, - isMobile, - openMobile, - setOpenMobile, - toggleSidebar, - }), - [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar], - ); + // Helper to toggle the sidebar. + const toggleSidebar = React.useCallback(() => { + return isMobile + ? setOpenMobile((open) => !open) + : setOpen((open) => !open); + }, [isMobile, setOpen, setOpenMobile]); + + // Adds a keyboard shortcut to toggle the sidebar. + React.useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if ( + event.key === SIDEBAR_KEYBOARD_SHORTCUT && + (event.metaKey || event.ctrlKey) + ) { + event.preventDefault(); + toggleSidebar(); + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [toggleSidebar]); + + // We add a state so that we can do data-state="expanded" or "collapsed". + // This makes it easier to style the sidebar with Tailwind classes. + const state = open ? 'expanded' : 'collapsed'; + + const contextValue = React.useMemo( + () => ({ + state, + open, + setOpen, + isMobile, + openMobile, + setOpenMobile, + toggleSidebar, + }), + [ + state, + open, + setOpen, + isMobile, + openMobile, + setOpenMobile, + toggleSidebar, + ], + ); - return ( - - -
- {children} -
-
-
- ); -}); + return ( + + +
+ {children} +
+
+
+ ); + }, +); SidebarProvider.displayName = 'SidebarProvider'; const Sidebar = React.forwardRef< @@ -137,92 +171,108 @@ const Sidebar = React.forwardRef< variant?: 'sidebar' | 'floating' | 'inset'; collapsible?: 'offcanvas' | 'icon' | 'none'; } ->(({ side = 'left', variant = 'sidebar', collapsible = 'offcanvas', className, children, ...props }, ref) => { - const { isMobile, state, openMobile, setOpenMobile } = useSidebar(); - - if (collapsible === 'none') { +>( + ( + { + side = 'left', + variant = 'sidebar', + collapsible = 'offcanvas', + className, + children, + ...props + }, + ref, + ) => { + const { isMobile, state, openMobile, setOpenMobile } = useSidebar(); + if (collapsible === 'none') { + return ( +
+ {children} +
+ ); + } + + if (isMobile) { + return ( + + +
{children}
+
+
+ ); + } return (
- {children} -
- ); - } - - if (isMobile) { - return ( - - -
{children}
-
-
- ); - } - - return ( -
- {/* This is what handles the sidebar gap on desktop */} -
- - ); -}); + ); + }, +); Sidebar.displayName = 'Sidebar'; -const SidebarTrigger = React.forwardRef, React.ComponentProps>(({ className, onClick, ...props }, ref) => { +const SidebarTrigger = React.forwardRef< + React.ElementRef, + React.ComponentProps +>(({ className, onClick, ...props }, ref) => { const { toggleSidebar } = useSidebar(); return ( @@ -245,7 +295,10 @@ const SidebarTrigger = React.forwardRef, React.C }); SidebarTrigger.displayName = 'SidebarTrigger'; -const SidebarRail = React.forwardRef>(({ className, ...props }, ref) => { +const SidebarRail = React.forwardRef< + HTMLButtonElement, + React.ComponentProps<'button'> +>(({ className, ...props }, ref) => { const { toggleSidebar } = useSidebar(); return ( @@ -271,7 +324,10 @@ const SidebarRail = React.forwardRef>(({ className, ...props }, ref) => { +const SidebarInset = React.forwardRef< + HTMLDivElement, + React.ComponentProps<'main'> +>(({ className, ...props }, ref) => { return (
, React.ComponentProps>(({ className, ...props }, ref) => { +const SidebarInput = React.forwardRef< + React.ElementRef, + React.ComponentProps +>(({ className, ...props }, ref) => { return ( ); }); SidebarInput.displayName = 'SidebarInput'; -const SidebarHeader = React.forwardRef>(({ className, ...props }, ref) => { +const SidebarHeader = React.forwardRef< + HTMLDivElement, + React.ComponentProps<'div'> +>(({ className, ...props }, ref) => { return (
>(({ className, ...props }, ref) => { +const SidebarFooter = React.forwardRef< + HTMLDivElement, + React.ComponentProps<'div'> +>(({ className, ...props }, ref) => { return (
, React.ComponentProps>(({ className, ...props }, ref) => { +const SidebarSeparator = React.forwardRef< + React.ElementRef, + React.ComponentProps +>(({ className, ...props }, ref) => { return ( , Re }); SidebarSeparator.displayName = 'SidebarSeparator'; -const SidebarContent = React.forwardRef>(({ className, ...props }, ref) => { +const SidebarContent = React.forwardRef< + HTMLDivElement, + React.ComponentProps<'div'> +>(({ className, ...props }, ref) => { return (
); }); SidebarContent.displayName = 'SidebarContent'; -const SidebarGroup = React.forwardRef>(({ className, ...props }, ref) => { +const SidebarGroup = React.forwardRef< + HTMLDivElement, + React.ComponentProps<'div'> +>(({ className, ...props }, ref) => { return (
& { asChild?: boolean }>(({ className, asChild = false, ...props }, ref) => { +const SidebarGroupLabel = React.forwardRef< + HTMLDivElement, + React.ComponentProps<'div'> & { asChild?: boolean } +>(({ className, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : 'div'; return ( @@ -376,7 +459,10 @@ const SidebarGroupLabel = React.forwardRef & { asChild?: boolean }>(({ className, asChild = false, ...props }, ref) => { +const SidebarGroupAction = React.forwardRef< + HTMLButtonElement, + React.ComponentProps<'button'> & { asChild?: boolean } +>(({ className, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : 'button'; return ( @@ -396,7 +482,10 @@ const SidebarGroupAction = React.forwardRef>(({ className, ...props }, ref) => ( +const SidebarGroupContent = React.forwardRef< + HTMLDivElement, + React.ComponentProps<'div'> +>(({ className, ...props }, ref) => (
>(({ className, ...props }, ref) => ( +const SidebarMenu = React.forwardRef< + HTMLUListElement, + React.ComponentProps<'ul'> +>(({ className, ...props }, ref) => (
    >(({ className, ...props }, ref) => ( +const SidebarMenuItem = React.forwardRef< + HTMLLIElement, + React.ComponentProps<'li'> +>(({ className, ...props }, ref) => (
  • ; } & VariantProps ->(({ asChild = false, isActive = false, variant = 'default', size = 'default', tooltip, className, ...props }, ref) => { - const Comp = asChild ? Slot : 'button'; - const { isMobile, state } = useSidebar(); +>( + ( + { + asChild = false, + isActive = false, + variant = 'default', + size = 'default', + tooltip, + className, + ...props + }, + ref, + ) => { + const Comp = asChild ? Slot : 'button'; + const { isMobile, state } = useSidebar(); - const button = ( - - ); + const button = ( + + ); - if (!tooltip) { - return button; - } + if (!tooltip) { + return button; + } - if (typeof tooltip === 'string') { - tooltip = { - children: tooltip, - }; - } + if (typeof tooltip === 'string') { + tooltip = { + children: tooltip, + }; + } - return ( - - {button} - - ); -}); + return ( + + {button} + + ); + }, +); SidebarMenuButton.displayName = 'SidebarMenuButton'; const SidebarMenuAction = React.forwardRef< @@ -525,7 +633,10 @@ const SidebarMenuAction = React.forwardRef< }); SidebarMenuAction.displayName = 'SidebarMenuAction'; -const SidebarMenuBadge = React.forwardRef>(({ className, ...props }, ref) => ( +const SidebarMenuBadge = React.forwardRef< + HTMLDivElement, + React.ComponentProps<'div'> +>(({ className, ...props }, ref) => (
    >(({ className, ...props }, ref) => ( +const SidebarMenuSub = React.forwardRef< + HTMLUListElement, + React.ComponentProps<'ul'> +>(({ className, ...props }, ref) => (
      )); SidebarMenuSub.displayName = 'SidebarMenuSub'; -const SidebarMenuSubItem = React.forwardRef>(({ ...props }, ref) => ( +const SidebarMenuSubItem = React.forwardRef< + HTMLLIElement, + React.ComponentProps<'li'> +>(({ ...props }, ref) => (