mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
added sidebar router
This commit is contained in:
parent
8a4709103b
commit
8a71d3d462
18 changed files with 166 additions and 108 deletions
|
@ -1,6 +1,6 @@
|
||||||
#NEXT_PUBLIC_API_HOST=https://pig.staging.vault48.org/
|
NEXT_PUBLIC_API_HOST=https://pig.staging.vault48.org/
|
||||||
#NEXT_PUBLIC_REMOTE_CURRENT=https://pig.staging.vault48.org/static/
|
NEXT_PUBLIC_REMOTE_CURRENT=https://pig.staging.vault48.org/static/
|
||||||
#NEXT_PUBLIC_API_HOST=http://localhost:8888/
|
#NEXT_PUBLIC_API_HOST=http://localhost:8888/
|
||||||
#NEXT_PUBLIC_REMOTE_CURRENT=http://localhost:8888/static/
|
#NEXT_PUBLIC_REMOTE_CURRENT=http://localhost:8888/static/
|
||||||
NEXT_PUBLIC_API_HOST=https://pig.vault48.org/
|
#NEXT_PUBLIC_API_HOST=https://pig.vault48.org/
|
||||||
NEXT_PUBLIC_REMOTE_CURRENT=https://pig.vault48.org/static/
|
#NEXT_PUBLIC_REMOTE_CURRENT=https://pig.vault48.org/static/
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,3 +5,4 @@
|
||||||
/build
|
/build
|
||||||
/.next
|
/.next
|
||||||
/.vscode
|
/.vscode
|
||||||
|
/.history
|
|
@ -1,11 +1,9 @@
|
||||||
import React, { FC, useState } from 'react';
|
import { FC, useState } from 'react';
|
||||||
|
|
||||||
import { Card } from '~/components/containers/Card';
|
import { Card } from '~/components/containers/Card';
|
||||||
import { Group } from '~/components/containers/Group';
|
import { Group } from '~/components/containers/Group';
|
||||||
import { Button } from '~/components/input/Button';
|
import { Button } from '~/components/input/Button';
|
||||||
import { InputText } from '~/components/input/InputText';
|
import { InputText } from '~/components/input/InputText';
|
||||||
import { Dialog } from '~/constants/modal';
|
|
||||||
import { useShowModal } from '~/hooks/modal/useShowModal';
|
|
||||||
import markdown from '~/styles/common/markdown.module.scss';
|
import markdown from '~/styles/common/markdown.module.scss';
|
||||||
|
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
|
@ -14,7 +12,6 @@ interface IProps {}
|
||||||
|
|
||||||
const BorisUIDemo: FC<IProps> = () => {
|
const BorisUIDemo: FC<IProps> = () => {
|
||||||
const [text, setText] = useState('');
|
const [text, setText] = useState('');
|
||||||
const openProfileSidebar = useShowModal(Dialog.ProfileSidebar);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={styles.card}>
|
<Card className={styles.card}>
|
||||||
|
@ -25,9 +22,6 @@ const BorisUIDemo: FC<IProps> = () => {
|
||||||
разработке
|
разработке
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Тестовые фичи</h2>
|
|
||||||
<Button onClick={() => openProfileSidebar({})}>Профиль в сайдбаре</Button>
|
|
||||||
|
|
||||||
<h2>Инпуты</h2>
|
<h2>Инпуты</h2>
|
||||||
|
|
||||||
<form autoComplete="off">
|
<form autoComplete="off">
|
||||||
|
|
|
@ -17,11 +17,11 @@ const NodeTags: FC<IProps> = memo(
|
||||||
return (
|
return (
|
||||||
<Tags
|
<Tags
|
||||||
tags={tags}
|
tags={tags}
|
||||||
is_editable={is_editable}
|
editable={is_editable}
|
||||||
onTagsChange={onChange}
|
onTagsChange={onChange}
|
||||||
onTagClick={onTagClick}
|
onTagClick={onTagClick}
|
||||||
onTagDelete={onTagDelete}
|
onTagDelete={onTagDelete}
|
||||||
is_deletable={is_deletable}
|
deletable={is_deletable}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodeTagsPlaceholder: FC<IProps> = memo(({ is_editable, tags, onChange }) => (
|
const NodeTagsPlaceholder: FC<IProps> = memo(({ is_editable, tags, onChange }) => (
|
||||||
<Tags tags={tags} is_editable={is_editable} onTagsChange={onChange} />
|
<Tags tags={tags} editable={is_editable} onTagsChange={onChange} />
|
||||||
));
|
));
|
||||||
|
|
||||||
export { NodeTagsPlaceholder };
|
export { NodeTagsPlaceholder };
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { VFC } from 'react';
|
import { VFC } from 'react';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import { Button } from '~/components/input/Button';
|
||||||
import { VerticalMenu } from '~/components/menu/VerticalMenu';
|
import { VerticalMenu } from '~/components/menu/VerticalMenu';
|
||||||
import { URLS } from '~/constants/urls';
|
import { URLS } from '~/constants/urls';
|
||||||
import { ProfileSidebarHead } from '~/containers/profile/ProfileSidebarHead';
|
import { ProfileSidebarHead } from '~/containers/profile/ProfileSidebarHead';
|
||||||
import { ProfileStats } from '~/containers/profile/ProfileStats';
|
|
||||||
|
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
|
|
||||||
|
@ -18,8 +17,6 @@ const SettingsMenu: VFC<SettingsMenuProps> = () => (
|
||||||
<Group>
|
<Group>
|
||||||
<ProfileSidebarHead />
|
<ProfileSidebarHead />
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<Group>
|
<Group>
|
||||||
<VerticalMenu className={styles.menu}>
|
<VerticalMenu className={styles.menu}>
|
||||||
<Link href={URLS.SETTINGS.BASE} passHref>
|
<Link href={URLS.SETTINGS.BASE} passHref>
|
||||||
|
@ -35,10 +32,6 @@ const SettingsMenu: VFC<SettingsMenuProps> = () => (
|
||||||
</Link>
|
</Link>
|
||||||
</VerticalMenu>
|
</VerticalMenu>
|
||||||
|
|
||||||
<div className={styles.stats}>
|
|
||||||
<ProfileStats />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Group horizontal>
|
<Group horizontal>
|
||||||
<Filler />
|
<Filler />
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,9 @@ interface IProps {
|
||||||
tag: Partial<ITag>;
|
tag: Partial<ITag>;
|
||||||
size?: 'normal' | 'big';
|
size?: 'normal' | 'big';
|
||||||
|
|
||||||
is_deletable?: boolean;
|
deletable?: boolean;
|
||||||
is_hoverable?: boolean;
|
hoverable?: boolean;
|
||||||
is_editing?: boolean;
|
editing?: boolean;
|
||||||
|
|
||||||
onBlur?: FocusEventHandler<HTMLInputElement>;
|
onBlur?: FocusEventHandler<HTMLInputElement>;
|
||||||
onClick?: (tag: Partial<ITag>) => void;
|
onClick?: (tag: Partial<ITag>) => void;
|
||||||
|
@ -24,9 +24,9 @@ interface IProps {
|
||||||
|
|
||||||
const Tag: FC<IProps> = ({
|
const Tag: FC<IProps> = ({
|
||||||
tag,
|
tag,
|
||||||
is_deletable,
|
deletable: deletable,
|
||||||
is_hoverable,
|
hoverable: hoverable,
|
||||||
is_editing,
|
editing: editing,
|
||||||
size = 'normal',
|
size = 'normal',
|
||||||
onClick,
|
onClick,
|
||||||
onDelete,
|
onDelete,
|
||||||
|
@ -48,9 +48,9 @@ const Tag: FC<IProps> = ({
|
||||||
<TagWrapper
|
<TagWrapper
|
||||||
feature={getTagFeature(tag)}
|
feature={getTagFeature(tag)}
|
||||||
size={size}
|
size={size}
|
||||||
is_deletable={is_deletable}
|
deletable={deletable}
|
||||||
is_hoverable={is_hoverable}
|
hoverable={hoverable}
|
||||||
is_editing={is_editing}
|
editing={editing}
|
||||||
onClick={onClick && onClickHandler}
|
onClick={onClick && onClickHandler}
|
||||||
onDelete={onDeleteHandler}
|
onDelete={onDeleteHandler}
|
||||||
title={tag.title}
|
title={tag.title}
|
||||||
|
|
|
@ -9,10 +9,10 @@ import styles from './styles.module.scss';
|
||||||
interface IProps {
|
interface IProps {
|
||||||
feature?: string;
|
feature?: string;
|
||||||
size?: string;
|
size?: string;
|
||||||
is_deletable?: boolean;
|
deletable?: boolean;
|
||||||
is_hoverable?: boolean;
|
hoverable?: boolean;
|
||||||
is_editing?: boolean;
|
editing?: boolean;
|
||||||
has_input?: boolean;
|
hasInput?: boolean;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
@ -22,15 +22,15 @@ const TagWrapper: FC<IProps> = ({
|
||||||
children,
|
children,
|
||||||
feature,
|
feature,
|
||||||
size,
|
size,
|
||||||
is_deletable,
|
deletable,
|
||||||
is_hoverable,
|
hoverable,
|
||||||
is_editing,
|
editing,
|
||||||
has_input,
|
hasInput,
|
||||||
onClick,
|
onClick,
|
||||||
onDelete,
|
onDelete,
|
||||||
title = '',
|
title = '',
|
||||||
}) => {
|
}) => {
|
||||||
const deletable = is_deletable && !is_editing && !has_input;
|
const canBeDeleted = deletable && !editing && !hasInput;
|
||||||
const onDeletePress = useCallback(
|
const onDeletePress = useCallback(
|
||||||
event => {
|
event => {
|
||||||
if (!onDelete) {
|
if (!onDelete) {
|
||||||
|
@ -46,10 +46,10 @@ const TagWrapper: FC<IProps> = ({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(styles.tag, feature, size, {
|
className={classNames(styles.tag, feature, size, {
|
||||||
is_hoverable,
|
is_hoverable: hoverable,
|
||||||
is_editing,
|
is_editing: editing,
|
||||||
deletable,
|
deletable: canBeDeleted,
|
||||||
input: has_input,
|
input: hasInput,
|
||||||
clickable: onClick,
|
clickable: onClick,
|
||||||
})}
|
})}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
|
11
src/constants/sidebar/index.ts
Normal file
11
src/constants/sidebar/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { FC, ReactNode } from "react";
|
||||||
|
|
||||||
|
import { ProfileSidebar } from "~/containers/sidebars/ProfileSidebar";
|
||||||
|
|
||||||
|
export enum SidebarName {
|
||||||
|
Settings = 'settings'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sidebarComponents = {
|
||||||
|
[SidebarName.Settings]: ProfileSidebar
|
||||||
|
}
|
|
@ -1,19 +1,22 @@
|
||||||
import { FC } from 'react';
|
import { FC, useCallback } from 'react';
|
||||||
|
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
import { Group } from '~/components/containers/Group';
|
import { Group } from '~/components/containers/Group';
|
||||||
import { Button } from '~/components/input/Button';
|
import { Button } from '~/components/input/Button';
|
||||||
import { Dialog } from '~/constants/modal';
|
import { SidebarName } from '~/constants/sidebar';
|
||||||
import { URLS } from '~/constants/urls';
|
import { URLS } from '~/constants/urls';
|
||||||
import { useShowModal } from '~/hooks/modal/useShowModal';
|
import { useSidebar } from '~/utils/providers/SidebarProvider';
|
||||||
|
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
|
|
||||||
export interface BorisSuperpowersProps {}
|
export interface BorisSuperpowersProps {}
|
||||||
|
|
||||||
const BorisSuperpowers: FC<BorisSuperpowersProps> = () => {
|
const BorisSuperpowers: FC<BorisSuperpowersProps> = () => {
|
||||||
const openProfileSidebar = useShowModal(Dialog.ProfileSidebar);
|
const { open } = useSidebar();
|
||||||
|
const openProfileSidebar = useCallback(() => {
|
||||||
|
open(SidebarName.Settings);
|
||||||
|
}, [open]);
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -21,7 +24,7 @@ const BorisSuperpowers: FC<BorisSuperpowersProps> = () => {
|
||||||
<h2>Штучи, находящиеся в разработке</h2>
|
<h2>Штучи, находящиеся в разработке</h2>
|
||||||
|
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
<Button size="mini" onClick={() => openProfileSidebar({})}>
|
<Button size="mini" onClick={() => openProfileSidebar()}>
|
||||||
Открыть
|
Открыть
|
||||||
</Button>
|
</Button>
|
||||||
<div className={styles.label}>Профиль в сайдбаре</div>
|
<div className={styles.label}>Профиль в сайдбаре</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { VFC } from 'react';
|
import { VFC } from 'react';
|
||||||
|
|
||||||
import { ProfileSidebarNotes } from '~/components/profile/ProfileSidebarNotes';
|
import { ProfileSidebarNotes } from '~/components/profile/ProfileSidebarNotes';
|
||||||
import { ProfileSidebarSettings } from '~/components/profile/ProfileSidebarSettings';
|
import { ProfileSidebarSettings } from '~/components/profile/ProfileSidebarSettings';
|
||||||
|
@ -8,7 +8,9 @@ import { ProfileSidebarMenu } from '~/containers/profile/ProfileSidebarMenu';
|
||||||
import { SidebarWrapper } from '~/containers/sidebars/SidebarWrapper';
|
import { SidebarWrapper } from '~/containers/sidebars/SidebarWrapper';
|
||||||
import { DialogComponentProps } from '~/types/modal';
|
import { DialogComponentProps } from '~/types/modal';
|
||||||
|
|
||||||
interface ProfileSidebarProps extends DialogComponentProps {}
|
interface ProfileSidebarProps extends DialogComponentProps {
|
||||||
|
page: string;
|
||||||
|
}
|
||||||
|
|
||||||
const ProfileSidebar: VFC<ProfileSidebarProps> = ({ onRequestClose }) => {
|
const ProfileSidebar: VFC<ProfileSidebarProps> = ({ onRequestClose }) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -126,7 +126,7 @@ const TagInput: FC<IProps> = ({ exclude, onAppend, onClearTag, onSubmit }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrap} ref={wrapper}>
|
<div className={styles.wrap} ref={wrapper}>
|
||||||
<TagWrapper title={input || placeholder} has_input={true} feature={feature}>
|
<TagWrapper title={input || placeholder} hasInput={true} feature={feature}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={input}
|
value={input}
|
||||||
|
|
|
@ -9,8 +9,8 @@ import { separateTags } from '~/utils/tag';
|
||||||
|
|
||||||
type IProps = HTMLAttributes<HTMLDivElement> & {
|
type IProps = HTMLAttributes<HTMLDivElement> & {
|
||||||
tags: Partial<ITag>[];
|
tags: Partial<ITag>[];
|
||||||
is_deletable?: boolean;
|
deletable?: boolean;
|
||||||
is_editable?: boolean;
|
editable?: boolean;
|
||||||
onTagsChange?: (tags: string[]) => void;
|
onTagsChange?: (tags: string[]) => void;
|
||||||
onTagClick?: (tag: Partial<ITag>) => void;
|
onTagClick?: (tag: Partial<ITag>) => void;
|
||||||
onTagDelete?: (id: ITag['ID']) => void;
|
onTagDelete?: (id: ITag['ID']) => void;
|
||||||
|
@ -18,8 +18,8 @@ type IProps = HTMLAttributes<HTMLDivElement> & {
|
||||||
|
|
||||||
export const Tags: FC<IProps> = ({
|
export const Tags: FC<IProps> = ({
|
||||||
tags,
|
tags,
|
||||||
is_deletable,
|
deletable,
|
||||||
is_editable,
|
editable,
|
||||||
onTagsChange,
|
onTagsChange,
|
||||||
onTagClick,
|
onTagClick,
|
||||||
onTagDelete,
|
onTagDelete,
|
||||||
|
@ -78,7 +78,7 @@ export const Tags: FC<IProps> = ({
|
||||||
key={tag.title}
|
key={tag.title}
|
||||||
tag={tag}
|
tag={tag}
|
||||||
onClick={onTagClick}
|
onClick={onTagClick}
|
||||||
is_deletable={is_deletable}
|
deletable={deletable}
|
||||||
onDelete={onTagDelete}
|
onDelete={onTagDelete}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
@ -88,16 +88,16 @@ export const Tags: FC<IProps> = ({
|
||||||
key={tag.title}
|
key={tag.title}
|
||||||
tag={tag}
|
tag={tag}
|
||||||
onClick={onTagClick}
|
onClick={onTagClick}
|
||||||
is_deletable={is_deletable}
|
deletable={deletable}
|
||||||
onDelete={onTagDelete}
|
onDelete={onTagDelete}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{data.map(title => (
|
{data.map(title => (
|
||||||
<Tag key={title} tag={{ title }} is_editing />
|
<Tag key={title} tag={{ title }} editing />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{is_editable && (
|
{editable && (
|
||||||
<TagInput
|
<TagInput
|
||||||
onAppend={onAppendTag}
|
onAppend={onAppendTag}
|
||||||
onClearTag={onClearTag}
|
onClearTag={onClearTag}
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
|
|
||||||
/** use this to preserve scrolling and handle back button behaviour on
|
|
||||||
* opening modal
|
|
||||||
*
|
|
||||||
* this will replace url with ?modal=modalName, next you should
|
|
||||||
* show modal for that name and pass params to it
|
|
||||||
*/
|
|
||||||
export const useModalRouting = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const openModal = useCallback(
|
|
||||||
(modalName: string) => {
|
|
||||||
const [path] = router.asPath.split('?');
|
|
||||||
|
|
||||||
void router.push(path + '?modal=' + modalName, path + '?modal=' + modalName, {
|
|
||||||
shallow: true,
|
|
||||||
scroll: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[router]
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentModal = useMemo(() => router.query.modal, [router]);
|
|
||||||
|
|
||||||
console.log(currentModal);
|
|
||||||
|
|
||||||
return { openModal };
|
|
||||||
};
|
|
|
@ -1,5 +1,9 @@
|
||||||
@import "src/styles/variables";
|
@import "src/styles/variables";
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
flex: 1 0;
|
flex: 1 0;
|
||||||
padding: $gap;
|
padding: $gap;
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { AuthProvider } from '~/utils/providers/AuthProvider';
|
||||||
import { MetadataProvider } from '~/utils/providers/MetadataProvider';
|
import { MetadataProvider } from '~/utils/providers/MetadataProvider';
|
||||||
import { SWRConfigProvider } from '~/utils/providers/SWRConfigProvider';
|
import { SWRConfigProvider } from '~/utils/providers/SWRConfigProvider';
|
||||||
import { SearchProvider } from '~/utils/providers/SearchProvider';
|
import { SearchProvider } from '~/utils/providers/SearchProvider';
|
||||||
|
import { SidebarProvider } from '~/utils/providers/SidebarProvider';
|
||||||
import { ToastProvider } from '~/utils/providers/ToastProvider';
|
import { ToastProvider } from '~/utils/providers/ToastProvider';
|
||||||
|
|
||||||
import '~/styles/main.scss';
|
import '~/styles/main.scss';
|
||||||
|
@ -41,22 +42,24 @@ export default class MyApp extends App {
|
||||||
<AudioPlayerProvider>
|
<AudioPlayerProvider>
|
||||||
<MetadataProvider>
|
<MetadataProvider>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<Head>
|
<SidebarProvider>
|
||||||
<meta
|
<Head>
|
||||||
name="viewport"
|
<meta
|
||||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=0"
|
name="viewport"
|
||||||
/>
|
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=0"
|
||||||
|
/>
|
||||||
|
|
||||||
{!!canonicalURL && <link rel="canonical" href={canonicalURL} />}
|
{!!canonicalURL && <link rel="canonical" href={canonicalURL} />}
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<ToastProvider />
|
<ToastProvider />
|
||||||
<Modal />
|
<Modal />
|
||||||
<Sprites />
|
<Sprites />
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
<BottomContainer />
|
<BottomContainer />
|
||||||
|
</SidebarProvider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</MetadataProvider>
|
</MetadataProvider>
|
||||||
</AudioPlayerProvider>
|
</AudioPlayerProvider>
|
||||||
|
|
78
src/utils/providers/SidebarProvider.tsx
Normal file
78
src/utils/providers/SidebarProvider.tsx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import { Context, createContext, createElement, FunctionComponent, PropsWithChildren, useCallback, useContext, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import { has } from 'ramda';
|
||||||
|
|
||||||
|
import { ModalWrapper } from '~/components/dialogs/ModalWrapper';
|
||||||
|
import { sidebarComponents, SidebarName } from '~/constants/sidebar';
|
||||||
|
import { DialogComponentProps } from '~/types/modal';
|
||||||
|
|
||||||
|
type ContextValue = typeof SidebarContext extends Context<infer U> ? U : never;
|
||||||
|
type Name = keyof typeof sidebarComponents;
|
||||||
|
|
||||||
|
// TODO: use it to store props for sidebar
|
||||||
|
type Props<T extends Name> = typeof sidebarComponents[T] extends FunctionComponent<infer U>
|
||||||
|
? U extends DialogComponentProps ? Omit<U, 'onRequestClose'> : U
|
||||||
|
: {};
|
||||||
|
|
||||||
|
const SidebarContext = createContext({
|
||||||
|
current: undefined as SidebarName | undefined,
|
||||||
|
open: <T extends Name>(name: T) => {},
|
||||||
|
close: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SidebarProvider = <T extends Name>({ children }: PropsWithChildren<{}>) => {
|
||||||
|
const router = useRouter();
|
||||||
|
const current = useMemo(() => {
|
||||||
|
const val = router.query.sidebar as SidebarName | undefined
|
||||||
|
|
||||||
|
return val && has(val, sidebarComponents) ? val : undefined;
|
||||||
|
}, [router]);
|
||||||
|
|
||||||
|
const open = useCallback(
|
||||||
|
<T extends Name>(name: T) => {
|
||||||
|
const [path] = router.asPath.split('?');
|
||||||
|
void router.push(path + '?sidebar=' + name, path + '?sidebar=' + name, {
|
||||||
|
shallow: true,
|
||||||
|
scroll: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[router]
|
||||||
|
);
|
||||||
|
|
||||||
|
const close = useCallback(
|
||||||
|
() => {
|
||||||
|
const [path] = router.asPath.split('?');
|
||||||
|
|
||||||
|
console.log('trying to close');
|
||||||
|
|
||||||
|
void router.replace(path, path, {
|
||||||
|
shallow: true,
|
||||||
|
scroll: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[router]
|
||||||
|
);
|
||||||
|
|
||||||
|
const value = useMemo<ContextValue>(() => ({
|
||||||
|
current,
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
}), [current, open, close]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SidebarContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
{current &&
|
||||||
|
<ModalWrapper onOverlayClick={close}>
|
||||||
|
{createElement(
|
||||||
|
sidebarComponents[current],
|
||||||
|
{ onRequestClose: close } as any
|
||||||
|
)}
|
||||||
|
</ModalWrapper>
|
||||||
|
}
|
||||||
|
</SidebarContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSidebar = () => useContext(SidebarContext);
|
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue