switch to nx monorepo
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
import { atomWithWebStorage } from '@/lib/jotai-helper';
|
||||
import { atom } from 'jotai';
|
||||
|
||||
export const desktopSidebarAtom = atomWithWebStorage('showSidebar', true);
|
||||
|
||||
export const desktopAsideAtom = atom(false);
|
||||
|
||||
type AsideStateType = {
|
||||
tab: string,
|
||||
isAsideOpen: boolean,
|
||||
}
|
||||
|
||||
export const asideStateAtom = atom<AsideStateType>({
|
||||
tab: '',
|
||||
isAsideOpen: false,
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
import { useAtom } from "jotai";
|
||||
|
||||
export function useToggleSidebar(sidebarAtom: any) {
|
||||
const [sidebarState, setSidebarState] = useAtom(sidebarAtom);
|
||||
return () => {
|
||||
setSidebarState(!sidebarState);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
.navbar {
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: var(--mantine-spacing-md);
|
||||
padding-top: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/*border-right: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));*/
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-left: calc(var(--mantine-spacing-md) * -1);
|
||||
margin-right: calc(var(--mantine-spacing-md) * -1);
|
||||
margin-bottom: var(--mantine-spacing-md);
|
||||
|
||||
&:not(:last-of-type) {
|
||||
border-bottom: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
}
|
||||
}
|
||||
|
||||
.searchCode {
|
||||
font-weight: 700;
|
||||
font-size: rem(10px);
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
|
||||
border: rem(1px) solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-7));
|
||||
}
|
||||
|
||||
.menuItems {
|
||||
padding-left: calc(var(--mantine-spacing-md) - var(--mantine-spacing-xs));
|
||||
padding-right: calc(var(--mantine-spacing-md) - var(--mantine-spacing-xs));
|
||||
padding-bottom: var(--mantine-spacing-md);
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
padding: rem(4px) var(--mantine-spacing-xs);
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
font-weight: 500;
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
|
||||
|
||||
&:hover {
|
||||
background-color: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6));
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
|
||||
}
|
||||
}
|
||||
|
||||
.menuItemInner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.menuItemIcon {
|
||||
margin-right: var(--mantine-spacing-sm);
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
}
|
||||
|
||||
.pages {
|
||||
padding-left: calc(var(--mantine-spacing-md) - rem(6px));
|
||||
padding-right: calc(var(--mantine-spacing-md) - rem(6px));
|
||||
padding-bottom: var(--mantine-spacing-md);
|
||||
}
|
||||
|
||||
.pagesHeader {
|
||||
padding-left: calc(var(--mantine-spacing-md) + rem(2px));
|
||||
padding-right: var(--mantine-spacing-md);
|
||||
margin-bottom: rem(5px);
|
||||
}
|
||||
|
||||
.pageLink {
|
||||
display: block;
|
||||
padding: rem(8px) var(--mantine-spacing-xs);
|
||||
text-decoration: none;
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
font-size: var(--mantine-font-size-xs);
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
|
||||
line-height: 1;
|
||||
font-weight: 500;
|
||||
|
||||
&:hover {
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import {
|
||||
UnstyledButton,
|
||||
Text,
|
||||
Group,
|
||||
ActionIcon,
|
||||
Tooltip,
|
||||
rem,
|
||||
} from '@mantine/core';
|
||||
import { spotlight } from '@mantine/spotlight';
|
||||
import {
|
||||
IconSearch,
|
||||
IconPlus,
|
||||
IconSettings,
|
||||
IconFilePlus,
|
||||
IconHome
|
||||
} from '@tabler/icons-react';
|
||||
|
||||
import classes from './navbar.module.css';
|
||||
import { UserButton } from './user-button';
|
||||
import React from 'react';
|
||||
import { useAtom } from 'jotai';
|
||||
import { settingsModalAtom } from '@/features/settings/modal/atoms/settings-modal-atom';
|
||||
import SettingsModal from '@/features/settings/modal/settings-modal';
|
||||
import { SearchSpotlight } from '@/features/search/search-spotlight';
|
||||
import { treeApiAtom } from '@/features/page/tree/atoms/tree-api-atom';
|
||||
import PageTree from '@/features/page/tree/page-tree';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface PrimaryMenuItem {
|
||||
icon: React.ElementType;
|
||||
label: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const primaryMenu: PrimaryMenuItem[] = [
|
||||
{ icon: IconHome, label: 'Home' },
|
||||
{ icon: IconSearch, label: 'Search' },
|
||||
{ icon: IconSettings, label: 'Settings' },
|
||||
// { icon: IconFilePlus, label: 'New Page' },
|
||||
];
|
||||
|
||||
export function Navbar() {
|
||||
const [, setSettingsModalOpen] = useAtom(settingsModalAtom);
|
||||
const [tree] = useAtom(treeApiAtom);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleMenuItemClick = (label: string) => {
|
||||
if (label === 'Home') {
|
||||
navigate('/home');
|
||||
}
|
||||
|
||||
if (label === 'Search') {
|
||||
spotlight.open();
|
||||
}
|
||||
|
||||
if (label === 'Settings') {
|
||||
setSettingsModalOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
function handleCreatePage() {
|
||||
tree?.create({ parentId: null, type: 'internal', index: 0 });
|
||||
}
|
||||
|
||||
const primaryMenuItems = primaryMenu.map((menuItem) => (
|
||||
<UnstyledButton
|
||||
key={menuItem.label}
|
||||
className={classes.menu}
|
||||
onClick={() => handleMenuItemClick(menuItem.label)}
|
||||
>
|
||||
<div className={classes.menuItemInner}>
|
||||
<menuItem.icon
|
||||
size={18}
|
||||
className={classes.menuItemIcon}
|
||||
stroke={2}
|
||||
/>
|
||||
<span>{menuItem.label}</span>
|
||||
</div>
|
||||
</UnstyledButton>
|
||||
));
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className={classes.navbar}>
|
||||
<div className={classes.section}>
|
||||
<UserButton />
|
||||
</div>
|
||||
|
||||
<div className={classes.section}>
|
||||
<div className={classes.menuItems}>{primaryMenuItems}</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.section}>
|
||||
<Group className={classes.pagesHeader} justify="space-between">
|
||||
<Text size="xs" fw={500} c="dimmed">
|
||||
Pages
|
||||
</Text>
|
||||
|
||||
<Tooltip label="Create page" withArrow position="right">
|
||||
<ActionIcon
|
||||
variant="default"
|
||||
size={18}
|
||||
onClick={handleCreatePage}
|
||||
>
|
||||
<IconPlus
|
||||
style={{ width: rem(12), height: rem(12) }}
|
||||
stroke={1.5}
|
||||
/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
|
||||
<div className={classes.pages}>
|
||||
<PageTree />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<SearchSpotlight />
|
||||
<SettingsModal />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.user {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: var(--mantine-spacing-md);
|
||||
color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
|
||||
|
||||
@mixin hover {
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-8));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { UnstyledButton, Group, Avatar, Text, rem } from '@mantine/core';
|
||||
import { IconChevronRight } from '@tabler/icons-react';
|
||||
import classes from './user-button.module.css';
|
||||
import { useAtom } from 'jotai/index';
|
||||
import { currentUserAtom } from '@/features/user/atoms/current-user-atom';
|
||||
|
||||
export function UserButton() {
|
||||
const [currentUser] = useAtom(currentUserAtom);
|
||||
|
||||
return (
|
||||
<UnstyledButton className={classes.user}>
|
||||
<Group>
|
||||
<Avatar
|
||||
src="https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-9.png"
|
||||
radius="xl"
|
||||
/>
|
||||
|
||||
<div style={{ flex: 1 }}>
|
||||
<Text size="sm" fw={500}>
|
||||
{currentUser?.user.name}
|
||||
</Text>
|
||||
|
||||
<Text c="dimmed" size="xs">
|
||||
{currentUser?.user.email}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<IconChevronRight style={{ width: rem(14), height: rem(14) }} stroke={1.5} />
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user