Tab fixes
This commit is contained in:
+33
-11
@@ -1,34 +1,56 @@
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
|
|
||||||
|
import { m } from 'framer-motion';
|
||||||
import type { Link } from 'type-route';
|
import type { Link } from 'type-route';
|
||||||
|
|
||||||
export interface TabProps {
|
export interface TabProps {
|
||||||
link?: Link;
|
link: Link;
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
title: string;
|
title: string;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
|
activeRight: boolean;
|
||||||
|
activeLeft: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Tab = ({ link, icon, title, active }: TabProps): JSX.Element => {
|
export const Tab = ({
|
||||||
|
link,
|
||||||
|
icon,
|
||||||
|
title,
|
||||||
|
active,
|
||||||
|
activeRight,
|
||||||
|
activeLeft,
|
||||||
|
}: TabProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div className="group relative flex-grow">
|
|
||||||
<div
|
<div
|
||||||
className={`peer flex cursor-pointer select-none py-1 dark:text-white ${
|
className={`max-w-[10rem] md:flex-grow ${
|
||||||
|
active ? 'bg-primaryDark' : 'bg-secondaryDark'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`group flex flex-grow cursor-pointer select-none py-2 hover:underline dark:text-white ${
|
||||||
active
|
active
|
||||||
? 'z-10 -mr-1 rounded-t-lg bg-gray-100 dark:bg-primaryDark'
|
? 'z-10 rounded-t-lg bg-gray-300 shadow-inner dark:bg-secondaryDark'
|
||||||
: 'dark:bg-secondaryDark'
|
: 'bg-gray-100 shadow-md dark:bg-primaryDark'
|
||||||
|
} ${activeRight ? 'rounded-br-lg' : ''} ${
|
||||||
|
activeLeft ? 'rounded-bl-lg' : ''
|
||||||
}`}
|
}`}
|
||||||
{...(link && link)}
|
{...(link && link)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`w-full px-2 ${
|
className={`my-auto w-full px-3 ${
|
||||||
active ? '' : 'border-l group-first:border-l-0 dark:border-gray-600'
|
active || activeLeft
|
||||||
|
? ''
|
||||||
|
: 'border-l border-gray-400 dark:border-gray-600'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex gap-2">
|
<m.div
|
||||||
|
className="flex gap-2"
|
||||||
|
whileHover={{ scale: 1.01 }}
|
||||||
|
whileTap={{ scale: 0.99 }}
|
||||||
|
>
|
||||||
<div className="my-auto">{icon}</div>
|
<div className="my-auto">{icon}</div>
|
||||||
{title}
|
<div className="hidden md:flex">{title}</div>
|
||||||
</div>
|
</m.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,29 +24,27 @@ export const IconButton = ({
|
|||||||
}: IconButtonProps): JSX.Element => {
|
}: IconButtonProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Tooltip disabled={!tooltip} content={tooltip}>
|
<Tooltip disabled={!tooltip} content={tooltip}>
|
||||||
<m.div
|
|
||||||
whileHover={{ scale: 1.01 }}
|
|
||||||
whileTap={{ scale: 0.97 }}
|
|
||||||
className="my-auto text-gray-500 dark:text-gray-400"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={`rounded-md p-2 hover:bg-gray-200 ${
|
className={`rounded-md p-2 hover:bg-gray-300 ${
|
||||||
active ? 'bg-gray-200 dark:bg-secondaryDark' : ''
|
active ? 'bg-gray-300 dark:bg-secondaryDark' : ''
|
||||||
} ${
|
} ${
|
||||||
nested ? 'dark:hover:bg-primaryDark' : 'dark:hover:bg-secondaryDark'
|
nested ? 'dark:hover:bg-primaryDark' : 'dark:hover:bg-secondaryDark'
|
||||||
} ${
|
} ${
|
||||||
disabled
|
disabled ? 'cursor-not-allowed text-gray-400 dark:text-gray-700' : ''
|
||||||
? 'cursor-not-allowed text-gray-400 dark:text-gray-700'
|
|
||||||
: ''
|
|
||||||
} ${className ?? ''}`}
|
} ${className ?? ''}`}
|
||||||
{...props}
|
{...props}
|
||||||
|
>
|
||||||
|
<m.div
|
||||||
|
whileHover={{ scale: 1.01 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
|
className="my-auto text-gray-600 dark:text-gray-400"
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
|
</m.div>
|
||||||
<span className="sr-only">Refresh</span>
|
<span className="sr-only">Refresh</span>
|
||||||
</button>
|
</button>
|
||||||
</m.div>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,6 +30,33 @@ export const Layout = ({
|
|||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
title: 'Messages',
|
||||||
|
icon: <FiMessageCircle />,
|
||||||
|
link: routes.messages().link,
|
||||||
|
active: route.name === 'messages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Nodes',
|
||||||
|
icon: <RiMindMap />,
|
||||||
|
link: routes.nodes().link,
|
||||||
|
active: route.name === 'nodes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Map',
|
||||||
|
icon: <RiRoadMapLine />,
|
||||||
|
link: routes.map().link,
|
||||||
|
active: route.name === 'map',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Extensions',
|
||||||
|
icon: <VscExtensions />,
|
||||||
|
link: routes.extensions().link,
|
||||||
|
active: route.name === 'extensions',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex w-full bg-gray-100 dark:bg-secondaryDark md:overflow-hidden md:shadow-xl">
|
<div className="relative flex w-full bg-gray-100 dark:bg-secondaryDark md:overflow-hidden md:shadow-xl">
|
||||||
<div className="flex flex-grow">
|
<div className="flex flex-grow">
|
||||||
@@ -45,42 +72,28 @@ export const Layout = ({
|
|||||||
</div>
|
</div>
|
||||||
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||||
<div className="flex h-full w-full flex-col bg-gray-300 dark:bg-secondaryDark">
|
<div className="flex h-full w-full flex-col bg-gray-300 dark:bg-secondaryDark">
|
||||||
<div className="flex w-full border-b border-gray-300 pr-2 pt-1 dark:border-gray-600">
|
<div className="flex w-full pt-1 dark:bg-primaryDark">
|
||||||
<div className="mx-1">
|
<div className="h-8">
|
||||||
<IconButton
|
<IconButton
|
||||||
className="rounded-b-none"
|
className="mx-1 rounded-b-none p-3"
|
||||||
|
icon={<FiSettings />}
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
setSettingsOpen(!settingsOpen);
|
setSettingsOpen(!settingsOpen);
|
||||||
}}
|
}}
|
||||||
nested
|
|
||||||
active={settingsOpen}
|
active={settingsOpen}
|
||||||
icon={<FiSettings />}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{tabs.map((tab, index) => (
|
||||||
<Tab
|
<Tab
|
||||||
title="Messages"
|
key={index}
|
||||||
icon={<FiMessageCircle />}
|
link={tab.link}
|
||||||
link={routes.messages().link}
|
title={tab.title}
|
||||||
active={route.name === 'messages'}
|
icon={tab.icon}
|
||||||
/>
|
active={tab.active}
|
||||||
<Tab
|
activeLeft={tabs[index - 1]?.active}
|
||||||
title="Nodes"
|
activeRight={tabs[index + 1]?.active}
|
||||||
icon={<RiMindMap />}
|
|
||||||
link={routes.nodes().link}
|
|
||||||
active={route.name === 'nodes'}
|
|
||||||
/>
|
|
||||||
<Tab
|
|
||||||
title="Map"
|
|
||||||
icon={<RiRoadMapLine />}
|
|
||||||
link={routes.map().link}
|
|
||||||
active={route.name === 'map'}
|
|
||||||
/>
|
|
||||||
<Tab
|
|
||||||
title="Extensions"
|
|
||||||
icon={<VscExtensions />}
|
|
||||||
link={routes.extensions().link}
|
|
||||||
active={route.name === 'extensions'}
|
|
||||||
/>
|
/>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-grow">{children}</div>
|
<div className="flex flex-grow">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user