1
0
Fork 0
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:
Fedor Katurov 2022-07-27 16:29:22 +07:00
parent 8a4709103b
commit 8a71d3d462
18 changed files with 166 additions and 108 deletions

View file

@ -1,6 +1,6 @@
#NEXT_PUBLIC_API_HOST=https://pig.staging.vault48.org/
#NEXT_PUBLIC_REMOTE_CURRENT=https://pig.staging.vault48.org/static/
NEXT_PUBLIC_API_HOST=https://pig.staging.vault48.org/
NEXT_PUBLIC_REMOTE_CURRENT=https://pig.staging.vault48.org/static/
#NEXT_PUBLIC_API_HOST=http://localhost:8888/
#NEXT_PUBLIC_REMOTE_CURRENT=http://localhost:8888/static/
NEXT_PUBLIC_API_HOST=https://pig.vault48.org/
NEXT_PUBLIC_REMOTE_CURRENT=https://pig.vault48.org/static/
#NEXT_PUBLIC_API_HOST=https://pig.vault48.org/
#NEXT_PUBLIC_REMOTE_CURRENT=https://pig.vault48.org/static/

1
.gitignore vendored
View file

@ -5,3 +5,4 @@
/build
/.next
/.vscode
/.history

View file

@ -1,11 +1,9 @@
import React, { FC, useState } from 'react';
import { FC, useState } from 'react';
import { Card } from '~/components/containers/Card';
import { Group } from '~/components/containers/Group';
import { Button } from '~/components/input/Button';
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 styles from './styles.module.scss';
@ -14,7 +12,6 @@ interface IProps {}
const BorisUIDemo: FC<IProps> = () => {
const [text, setText] = useState('');
const openProfileSidebar = useShowModal(Dialog.ProfileSidebar);
return (
<Card className={styles.card}>
@ -25,9 +22,6 @@ const BorisUIDemo: FC<IProps> = () => {
разработке
</p>
<h2>Тестовые фичи</h2>
<Button onClick={() => openProfileSidebar({})}>Профиль в сайдбаре</Button>
<h2>Инпуты</h2>
<form autoComplete="off">

View file

@ -17,11 +17,11 @@ const NodeTags: FC<IProps> = memo(
return (
<Tags
tags={tags}
is_editable={is_editable}
editable={is_editable}
onTagsChange={onChange}
onTagClick={onTagClick}
onTagDelete={onTagDelete}
is_deletable={is_deletable}
deletable={is_deletable}
/>
);
}

View file

@ -10,7 +10,7 @@ interface IProps {
}
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 };

View file

@ -1,4 +1,4 @@
import React, { VFC } from 'react';
import { VFC } from 'react';
import Link from 'next/link';
@ -8,7 +8,6 @@ import { Button } from '~/components/input/Button';
import { VerticalMenu } from '~/components/menu/VerticalMenu';
import { URLS } from '~/constants/urls';
import { ProfileSidebarHead } from '~/containers/profile/ProfileSidebarHead';
import { ProfileStats } from '~/containers/profile/ProfileStats';
import styles from './styles.module.scss';
@ -18,8 +17,6 @@ const SettingsMenu: VFC<SettingsMenuProps> = () => (
<Group>
<ProfileSidebarHead />
<br />
<Group>
<VerticalMenu className={styles.menu}>
<Link href={URLS.SETTINGS.BASE} passHref>
@ -35,10 +32,6 @@ const SettingsMenu: VFC<SettingsMenuProps> = () => (
</Link>
</VerticalMenu>
<div className={styles.stats}>
<ProfileStats />
</div>
<Group horizontal>
<Filler />

View file

@ -13,9 +13,9 @@ interface IProps {
tag: Partial<ITag>;
size?: 'normal' | 'big';
is_deletable?: boolean;
is_hoverable?: boolean;
is_editing?: boolean;
deletable?: boolean;
hoverable?: boolean;
editing?: boolean;
onBlur?: FocusEventHandler<HTMLInputElement>;
onClick?: (tag: Partial<ITag>) => void;
@ -24,9 +24,9 @@ interface IProps {
const Tag: FC<IProps> = ({
tag,
is_deletable,
is_hoverable,
is_editing,
deletable: deletable,
hoverable: hoverable,
editing: editing,
size = 'normal',
onClick,
onDelete,
@ -48,9 +48,9 @@ const Tag: FC<IProps> = ({
<TagWrapper
feature={getTagFeature(tag)}
size={size}
is_deletable={is_deletable}
is_hoverable={is_hoverable}
is_editing={is_editing}
deletable={deletable}
hoverable={hoverable}
editing={editing}
onClick={onClick && onClickHandler}
onDelete={onDeleteHandler}
title={tag.title}

View file

@ -9,10 +9,10 @@ import styles from './styles.module.scss';
interface IProps {
feature?: string;
size?: string;
is_deletable?: boolean;
is_hoverable?: boolean;
is_editing?: boolean;
has_input?: boolean;
deletable?: boolean;
hoverable?: boolean;
editing?: boolean;
hasInput?: boolean;
onClick?: () => void;
onDelete?: () => void;
title?: string;
@ -22,15 +22,15 @@ const TagWrapper: FC<IProps> = ({
children,
feature,
size,
is_deletable,
is_hoverable,
is_editing,
has_input,
deletable,
hoverable,
editing,
hasInput,
onClick,
onDelete,
title = '',
}) => {
const deletable = is_deletable && !is_editing && !has_input;
const canBeDeleted = deletable && !editing && !hasInput;
const onDeletePress = useCallback(
event => {
if (!onDelete) {
@ -46,10 +46,10 @@ const TagWrapper: FC<IProps> = ({
return (
<div
className={classNames(styles.tag, feature, size, {
is_hoverable,
is_editing,
deletable,
input: has_input,
is_hoverable: hoverable,
is_editing: editing,
deletable: canBeDeleted,
input: hasInput,
clickable: onClick,
})}
onClick={onClick}

View 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
}

View file

@ -1,19 +1,22 @@
import { FC } from 'react';
import { FC, useCallback } from 'react';
import { useRouter } from 'next/router';
import { Group } from '~/components/containers/Group';
import { Button } from '~/components/input/Button';
import { Dialog } from '~/constants/modal';
import { SidebarName } from '~/constants/sidebar';
import { URLS } from '~/constants/urls';
import { useShowModal } from '~/hooks/modal/useShowModal';
import { useSidebar } from '~/utils/providers/SidebarProvider';
import styles from './styles.module.scss';
export interface BorisSuperpowersProps {}
const BorisSuperpowers: FC<BorisSuperpowersProps> = () => {
const openProfileSidebar = useShowModal(Dialog.ProfileSidebar);
const { open } = useSidebar();
const openProfileSidebar = useCallback(() => {
open(SidebarName.Settings);
}, [open]);
const { push } = useRouter();
return (
@ -21,7 +24,7 @@ const BorisSuperpowers: FC<BorisSuperpowersProps> = () => {
<h2>Штучи, находящиеся в разработке</h2>
<div className={styles.grid}>
<Button size="mini" onClick={() => openProfileSidebar({})}>
<Button size="mini" onClick={() => openProfileSidebar()}>
Открыть
</Button>
<div className={styles.label}>Профиль в сайдбаре</div>

View file

@ -1,4 +1,4 @@
import React, { VFC } from 'react';
import { VFC } from 'react';
import { ProfileSidebarNotes } from '~/components/profile/ProfileSidebarNotes';
import { ProfileSidebarSettings } from '~/components/profile/ProfileSidebarSettings';
@ -8,7 +8,9 @@ import { ProfileSidebarMenu } from '~/containers/profile/ProfileSidebarMenu';
import { SidebarWrapper } from '~/containers/sidebars/SidebarWrapper';
import { DialogComponentProps } from '~/types/modal';
interface ProfileSidebarProps extends DialogComponentProps {}
interface ProfileSidebarProps extends DialogComponentProps {
page: string;
}
const ProfileSidebar: VFC<ProfileSidebarProps> = ({ onRequestClose }) => {
return (

View file

@ -126,7 +126,7 @@ const TagInput: FC<IProps> = ({ exclude, onAppend, onClearTag, onSubmit }) => {
return (
<div className={styles.wrap} ref={wrapper}>
<TagWrapper title={input || placeholder} has_input={true} feature={feature}>
<TagWrapper title={input || placeholder} hasInput={true} feature={feature}>
<input
type="text"
value={input}

View file

@ -9,8 +9,8 @@ import { separateTags } from '~/utils/tag';
type IProps = HTMLAttributes<HTMLDivElement> & {
tags: Partial<ITag>[];
is_deletable?: boolean;
is_editable?: boolean;
deletable?: boolean;
editable?: boolean;
onTagsChange?: (tags: string[]) => void;
onTagClick?: (tag: Partial<ITag>) => void;
onTagDelete?: (id: ITag['ID']) => void;
@ -18,8 +18,8 @@ type IProps = HTMLAttributes<HTMLDivElement> & {
export const Tags: FC<IProps> = ({
tags,
is_deletable,
is_editable,
deletable,
editable,
onTagsChange,
onTagClick,
onTagDelete,
@ -78,7 +78,7 @@ export const Tags: FC<IProps> = ({
key={tag.title}
tag={tag}
onClick={onTagClick}
is_deletable={is_deletable}
deletable={deletable}
onDelete={onTagDelete}
/>
))}
@ -88,16 +88,16 @@ export const Tags: FC<IProps> = ({
key={tag.title}
tag={tag}
onClick={onTagClick}
is_deletable={is_deletable}
deletable={deletable}
onDelete={onTagDelete}
/>
))}
{data.map(title => (
<Tag key={title} tag={{ title }} is_editing />
<Tag key={title} tag={{ title }} editing />
))}
{is_editable && (
{editable && (
<TagInput
onAppend={onAppendTag}
onClearTag={onClearTag}

View file

@ -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 };
};

View file

@ -1,5 +1,9 @@
@import "src/styles/variables";
.container {
width: 100vw;
}
.menu {
flex: 1 0;
padding: $gap;

View file

@ -18,6 +18,7 @@ import { AuthProvider } from '~/utils/providers/AuthProvider';
import { MetadataProvider } from '~/utils/providers/MetadataProvider';
import { SWRConfigProvider } from '~/utils/providers/SWRConfigProvider';
import { SearchProvider } from '~/utils/providers/SearchProvider';
import { SidebarProvider } from '~/utils/providers/SidebarProvider';
import { ToastProvider } from '~/utils/providers/ToastProvider';
import '~/styles/main.scss';
@ -41,6 +42,7 @@ export default class MyApp extends App {
<AudioPlayerProvider>
<MetadataProvider>
<AuthProvider>
<SidebarProvider>
<Head>
<meta
name="viewport"
@ -57,6 +59,7 @@ export default class MyApp extends App {
<Component {...pageProps} />
</MainLayout>
<BottomContainer />
</SidebarProvider>
</AuthProvider>
</MetadataProvider>
</AudioPlayerProvider>

View 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