| hoverTip?: string | hoverTip?: string | ||||
| textStyle?: { main?: string; extra?: string } | textStyle?: { main?: string; extra?: string } | ||||
| isExtraInLine?: boolean | isExtraInLine?: boolean | ||||
| mode?: 'expand' | 'collapse' | |||||
| mode?: string | |||||
| } | } | ||||
| const ApiSvg = <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"> | const ApiSvg = <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
| notion: <AppIcon innerIcon={NotionSvg} className='!border-[0.5px] !border-indigo-100 !bg-white' />, | notion: <AppIcon innerIcon={NotionSvg} className='!border-[0.5px] !border-indigo-100 !bg-white' />, | ||||
| } | } | ||||
| export default function AppBasic({ icon, icon_background, name, type, hoverTip, textStyle, mode = 'expand', iconType = 'app', isExtraInLine }: IAppBasicProps) { | |||||
| export default function AppBasic({ icon, icon_background, name, type, hoverTip, textStyle, mode = 'expand', iconType = 'app' }: IAppBasicProps) { | |||||
| return ( | return ( | ||||
| <div className="flex items-start"> | <div className="flex items-start"> | ||||
| {icon && icon_background && iconType === 'app' && ( | {icon && icon_background && iconType === 'app' && ( |
| import React from 'react' | |||||
| import React, { useCallback, useState } from 'react' | |||||
| import NavLink from './navLink' | import NavLink from './navLink' | ||||
| import type { NavIcon } from './navLink' | import type { NavIcon } from './navLink' | ||||
| import AppBasic from './basic' | import AppBasic from './basic' | ||||
| import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | ||||
| import { | |||||
| AlignLeft01, | |||||
| AlignRight01, | |||||
| } from '@/app/components/base/icons/src/vender/line/layout' | |||||
| export type IAppDetailNavProps = { | export type IAppDetailNavProps = { | ||||
| iconType?: 'app' | 'dataset' | 'notion' | iconType?: 'app' | 'dataset' | 'notion' | ||||
| } | } | ||||
| const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInfo, iconType = 'app' }: IAppDetailNavProps) => { | const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInfo, iconType = 'app' }: IAppDetailNavProps) => { | ||||
| const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand' | |||||
| const media = useBreakpoints() | const media = useBreakpoints() | ||||
| const isMobile = media === MediaType.mobile | const isMobile = media === MediaType.mobile | ||||
| const mode = isMobile ? 'collapse' : 'expand' | const mode = isMobile ? 'collapse' : 'expand' | ||||
| const [modeState, setModeState] = useState(isMobile ? mode : localeMode) | |||||
| const expand = modeState === 'expand' | |||||
| const handleToggle = useCallback(() => { | |||||
| setModeState((prev) => { | |||||
| const next = prev === 'expand' ? 'collapse' : 'expand' | |||||
| localStorage.setItem('app-detail-collapse-or-expand', next) | |||||
| return next | |||||
| }) | |||||
| }, []) | |||||
| return ( | return ( | ||||
| <div className="flex flex-col sm:w-56 w-16 overflow-y-auto bg-white border-r border-gray-200 shrink-0 mobile:h-screen"> | |||||
| <div className="flex flex-shrink-0 p-4"> | |||||
| <AppBasic mode={mode} iconType={iconType} icon={icon} icon_background={icon_background} name={title} type={desc} /> | |||||
| <div | |||||
| className={` | |||||
| shrink-0 flex flex-col bg-white border-r border-gray-200 transition-all | |||||
| ${expand ? 'w-[216px]' : 'w-14'} | |||||
| `} | |||||
| > | |||||
| <div | |||||
| className={` | |||||
| shrink-0 | |||||
| ${expand ? 'p-4' : 'p-2'} | |||||
| `} | |||||
| > | |||||
| <AppBasic | |||||
| mode={modeState} | |||||
| iconType={iconType} | |||||
| icon={icon} | |||||
| icon_background={icon_background} | |||||
| name={title} | |||||
| type={desc} | |||||
| /> | |||||
| </div> | </div> | ||||
| <nav className="flex-1 p-4 space-y-1 bg-white"> | |||||
| <nav | |||||
| className={` | |||||
| grow space-y-1 bg-white | |||||
| ${expand ? 'p-4' : 'px-2.5 py-4'} | |||||
| `} | |||||
| > | |||||
| {navigation.map((item, index) => { | {navigation.map((item, index) => { | ||||
| return ( | return ( | ||||
| <NavLink key={index} mode={mode} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} /> | |||||
| <NavLink key={index} mode={modeState} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} /> | |||||
| ) | ) | ||||
| })} | })} | ||||
| {extraInfo ?? null} | {extraInfo ?? null} | ||||
| </nav> | </nav> | ||||
| { | |||||
| !isMobile && ( | |||||
| <div | |||||
| className={` | |||||
| shrink-0 py-3 | |||||
| ${expand ? 'px-6' : 'px-4'} | |||||
| `} | |||||
| > | |||||
| <div | |||||
| className='flex items-center justify-center w-6 h-6 text-gray-500 cursor-pointer' | |||||
| onClick={handleToggle} | |||||
| > | |||||
| { | |||||
| expand | |||||
| ? <AlignLeft01 className='w-[14px] h-[14px]' /> | |||||
| : <AlignRight01 className='w-[14px] h-[14px]' /> | |||||
| } | |||||
| </div> | |||||
| </div> | |||||
| ) | |||||
| } | |||||
| </div> | </div> | ||||
| ) | ) | ||||
| } | } |
| selected: NavIcon | selected: NavIcon | ||||
| normal: NavIcon | normal: NavIcon | ||||
| } | } | ||||
| mode?: 'expand' | 'collapse' | |||||
| mode?: string | |||||
| } | } | ||||
| export default function NavLink({ | export default function NavLink({ | ||||
| href={href} | href={href} | ||||
| className={classNames( | className={classNames( | ||||
| isActive ? 'bg-primary-50 text-primary-600 font-semibold' : 'text-gray-700 hover:bg-gray-100 hover:text-gray-700', | isActive ? 'bg-primary-50 text-primary-600 font-semibold' : 'text-gray-700 hover:bg-gray-100 hover:text-gray-700', | ||||
| 'group flex items-center rounded-md px-2 py-2 text-sm font-normal', | |||||
| 'group flex items-center h-9 rounded-md py-2 text-sm font-normal', | |||||
| mode === 'expand' ? 'px-3' : 'px-2.5', | |||||
| )} | )} | ||||
| > | > | ||||
| <NavIcon | <NavIcon | ||||
| className={classNames( | className={classNames( | ||||
| 'mr-2 h-4 w-4 flex-shrink-0', | |||||
| 'h-4 w-4 flex-shrink-0', | |||||
| isActive ? 'text-primary-600' : 'text-gray-700', | isActive ? 'text-primary-600' : 'text-gray-700', | ||||
| mode === 'expand' ? 'mr-2' : 'mr-0', | |||||
| )} | )} | ||||
| aria-hidden="true" | aria-hidden="true" | ||||
| /> | /> |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||||
| <g id="align-left-01"> | |||||
| <path id="Icon" d="M3 3V21M21 12H7M7 12L14 19M7 12L14 5" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |||||
| </g> | |||||
| </svg> |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||||
| <g id="align-right-01"> | |||||
| <path id="Icon" d="M21 21V3M3 12H17M17 12L10 5M17 12L10 19" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |||||
| </g> | |||||
| </svg> |
| { | |||||
| "icon": { | |||||
| "type": "element", | |||||
| "isRootNode": true, | |||||
| "name": "svg", | |||||
| "attributes": { | |||||
| "width": "24", | |||||
| "height": "24", | |||||
| "viewBox": "0 0 24 24", | |||||
| "fill": "none", | |||||
| "xmlns": "http://www.w3.org/2000/svg" | |||||
| }, | |||||
| "children": [ | |||||
| { | |||||
| "type": "element", | |||||
| "name": "g", | |||||
| "attributes": { | |||||
| "id": "align-left-01" | |||||
| }, | |||||
| "children": [ | |||||
| { | |||||
| "type": "element", | |||||
| "name": "path", | |||||
| "attributes": { | |||||
| "id": "Icon", | |||||
| "d": "M3 3V21M21 12H7M7 12L14 19M7 12L14 5", | |||||
| "stroke": "currentColor", | |||||
| "stroke-width": "2", | |||||
| "stroke-linecap": "round", | |||||
| "stroke-linejoin": "round" | |||||
| }, | |||||
| "children": [] | |||||
| } | |||||
| ] | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "name": "AlignLeft01" | |||||
| } |
| // GENERATE BY script | |||||
| // DON NOT EDIT IT MANUALLY | |||||
| import * as React from 'react' | |||||
| import data from './AlignLeft01.json' | |||||
| import IconBase from '@/app/components/base/icons/IconBase' | |||||
| import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' | |||||
| const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>(( | |||||
| props, | |||||
| ref, | |||||
| ) => <IconBase {...props} ref={ref} data={data as IconData} />) | |||||
| Icon.displayName = 'AlignLeft01' | |||||
| export default Icon |
| { | |||||
| "icon": { | |||||
| "type": "element", | |||||
| "isRootNode": true, | |||||
| "name": "svg", | |||||
| "attributes": { | |||||
| "width": "24", | |||||
| "height": "24", | |||||
| "viewBox": "0 0 24 24", | |||||
| "fill": "none", | |||||
| "xmlns": "http://www.w3.org/2000/svg" | |||||
| }, | |||||
| "children": [ | |||||
| { | |||||
| "type": "element", | |||||
| "name": "g", | |||||
| "attributes": { | |||||
| "id": "align-right-01" | |||||
| }, | |||||
| "children": [ | |||||
| { | |||||
| "type": "element", | |||||
| "name": "path", | |||||
| "attributes": { | |||||
| "id": "Icon", | |||||
| "d": "M21 21V3M3 12H17M17 12L10 5M17 12L10 19", | |||||
| "stroke": "currentColor", | |||||
| "stroke-width": "2", | |||||
| "stroke-linecap": "round", | |||||
| "stroke-linejoin": "round" | |||||
| }, | |||||
| "children": [] | |||||
| } | |||||
| ] | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "name": "AlignRight01" | |||||
| } |
| // GENERATE BY script | |||||
| // DON NOT EDIT IT MANUALLY | |||||
| import * as React from 'react' | |||||
| import data from './AlignRight01.json' | |||||
| import IconBase from '@/app/components/base/icons/IconBase' | |||||
| import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' | |||||
| const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>(( | |||||
| props, | |||||
| ref, | |||||
| ) => <IconBase {...props} ref={ref} data={data as IconData} />) | |||||
| Icon.displayName = 'AlignRight01' | |||||
| export default Icon |
| export { default as AlignLeft01 } from './AlignLeft01' | |||||
| export { default as AlignRight01 } from './AlignRight01' | |||||
| export { default as Grid01 } from './Grid01' | export { default as Grid01 } from './Grid01' |