1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-25 21:06:42 +07:00

added sidebar navigation

This commit is contained in:
Fedor Katurov 2022-08-05 18:45:11 +07:00
parent 1bb08f72e6
commit a03f80259d
6 changed files with 150 additions and 70 deletions

View file

@ -1,11 +1,21 @@
import React, { createContext, FC, PropsWithChildren, useCallback, useContext, useMemo, useState } from 'react'; import React, {
createContext,
FC,
PropsWithChildren,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from "react";
import { isNil } from '~/utils/ramda'; import { isNil } from "~/utils/ramda";
import styles from './styles.module.scss'; import styles from "./styles.module.scss";
interface SidebarStackProps extends PropsWithChildren<{}> { interface SidebarStackProps extends PropsWithChildren<{}> {
initialTab?: number; tab?: number;
onTabChange?: (index?: number) => void;
} }
interface SidebarStackContextValue { interface SidebarStackContextValue {
@ -38,12 +48,31 @@ const SidebarCards: FC = ({ children }) => {
return <div className={styles.card}>{nonEmptyChildren[activeTab]}</div>; return <div className={styles.card}>{nonEmptyChildren[activeTab]}</div>;
}; };
const SidebarStack = function({ children, initialTab }: SidebarStackProps) { const SidebarStack = function({
const [activeTab, setActiveTab] = useState<number | undefined>(initialTab); children,
const closeAllTabs = useCallback(() => setActiveTab(undefined), []); tab,
onTabChange,
}: SidebarStackProps) {
const [activeTab, setActiveTab] = useState<number | undefined>(tab);
const closeAllTabs = useCallback(() => {
setActiveTab(undefined);
onTabChange?.(undefined);
}, []);
const onChangeTab = useCallback(
(index: number) => {
onTabChange?.(index);
setActiveTab(index);
},
[onTabChange],
);
useEffect(() => setActiveTab(tab), [tab]);
return ( return (
<SidebarStackContext.Provider value={{ activeTab, setActiveTab, closeAllTabs }}> <SidebarStackContext.Provider
value={{ activeTab, setActiveTab: onChangeTab, closeAllTabs }}
>
<div className={styles.stack}>{children}</div> <div className={styles.stack}>{children}</div>
</SidebarStackContext.Provider> </SidebarStackContext.Provider>
); );

View file

@ -1,21 +1,21 @@
import { FC, useCallback } 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 { SidebarName } from '~/constants/sidebar'; import { SidebarName } from "~/constants/sidebar";
import { URLS } from '~/constants/urls'; import { URLS } from "~/constants/urls";
import { useSidebar } from '~/utils/providers/SidebarProvider'; 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 { open } = useSidebar(); const { open } = useSidebar();
const openProfileSidebar = useCallback(() => { const openProfileSidebar = useCallback(() => {
open(SidebarName.Settings); open(SidebarName.Settings, { page: "profile" });
}, [open]); }, [open]);
const { push } = useRouter(); const { push } = useRouter();

View file

@ -1,22 +1,48 @@
import { VFC } from 'react'; import { useCallback, useMemo, VFC } from "react";
import { ProfileSidebarNotes } from '~/components/profile/ProfileSidebarNotes'; import { isNil } from "ramda";
import { ProfileSidebarSettings } from '~/components/profile/ProfileSidebarSettings';
import { SidebarStack } from '~/components/sidebar/SidebarStack';
import { SidebarStackCard } from '~/components/sidebar/SidebarStackCard';
import { ProfileSidebarMenu } from '~/containers/profile/ProfileSidebarMenu';
import { SidebarWrapper } from '~/containers/sidebars/SidebarWrapper';
import { DialogComponentProps } from '~/types/modal';
interface ProfileSidebarProps extends DialogComponentProps { import { ProfileSidebarNotes } from "~/components/profile/ProfileSidebarNotes";
page: string; import { ProfileSidebarSettings } from "~/components/profile/ProfileSidebarSettings";
import { SidebarStack } from "~/components/sidebar/SidebarStack";
import { SidebarStackCard } from "~/components/sidebar/SidebarStackCard";
import { SidebarName } from "~/constants/sidebar";
import { ProfileSidebarMenu } from "~/containers/profile/ProfileSidebarMenu";
import { SidebarWrapper } from "~/containers/sidebars/SidebarWrapper";
import { SidebarComponentProps } from "~/types/sidebar";
import { useSidebar } from "~/utils/providers/SidebarProvider";
type TabName = "profile" | "bookmarks";
interface ProfileSidebarProps extends SidebarComponentProps {
page?: TabName;
} }
const ProfileSidebar: VFC<ProfileSidebarProps> = ({ onRequestClose }) => { const tabs: TabName[] = ["profile", "bookmarks"];
const ProfileSidebar: VFC<ProfileSidebarProps> = ({ onRequestClose, page }) => {
const initialTab = useMemo(
() => (page ? Math.min(tabs.indexOf(page), 0) : undefined),
[page],
);
const { open } = useSidebar();
const onTabChange = useCallback(
(val: number | undefined) => {
console.log({ val });
open(SidebarName.Settings, { page: !isNil(val) ? tabs[val] : undefined });
},
[open],
);
return ( return (
<SidebarWrapper onClose={onRequestClose}> <SidebarWrapper onClose={onRequestClose}>
<SidebarStack> <SidebarStack tab={initialTab} onTabChange={onTabChange}>
<SidebarStackCard headerFeature="close" title="Профиль" onBackPress={onRequestClose}> <SidebarStackCard
headerFeature="close"
title="Профиль"
onBackPress={onRequestClose}
>
<ProfileSidebarMenu onClose={onRequestClose} /> <ProfileSidebarMenu onClose={onRequestClose} />
</SidebarStackCard> </SidebarStackCard>

View file

@ -0,0 +1,3 @@
export interface SidebarComponentProps {
onRequestClose: () => void;
}

View file

@ -1,78 +1,100 @@
import { Context, createContext, createElement, FunctionComponent, PropsWithChildren, useCallback, useContext, useMemo } from 'react'; import {
Context,
createContext,
createElement,
FunctionComponent,
PropsWithChildren,
useCallback,
useContext,
useMemo,
} from "react";
import { useRouter } from 'next/router'; import { useRouter } from "next/router";
import { has } from 'ramda'; import { has, omit } from "ramda";
import { ModalWrapper } from '~/components/dialogs/ModalWrapper'; import { ModalWrapper } from "~/components/dialogs/ModalWrapper";
import { sidebarComponents, SidebarName } from '~/constants/sidebar'; import { sidebarComponents, SidebarName } from "~/constants/sidebar";
import { DialogComponentProps } from '~/types/modal'; import { DialogComponentProps } from "~/types/modal";
import { SidebarComponentProps } from "~/types/sidebar";
type ContextValue = typeof SidebarContext extends Context<infer U> ? U : never; type ContextValue = typeof SidebarContext extends Context<infer U> ? U : never;
type Name = keyof typeof sidebarComponents; type Name = keyof typeof sidebarComponents;
// TODO: use it to store props for sidebar // TODO: use it to store props for sidebar
type Props<T extends Name> = typeof sidebarComponents[T] extends FunctionComponent<infer U> type Props<
? U extends DialogComponentProps ? Omit<U, 'onRequestClose'> : U T extends Name
> = typeof sidebarComponents[T] extends FunctionComponent<infer U>
? U extends object
? U extends SidebarComponentProps
? Omit<U, "onRequestClose">
: U
: U
: {}; : {};
const SidebarContext = createContext({ const SidebarContext = createContext({
current: undefined as SidebarName | undefined, current: undefined as SidebarName | undefined,
open: <T extends Name>(name: T) => {}, open: <T extends Name>(name: T, props: Props<T>) => {},
close: () => {}, close: () => {},
}); });
export const SidebarProvider = <T extends Name>({ children }: PropsWithChildren<{}>) => { export const SidebarProvider = <T extends Name>({
children,
}: PropsWithChildren<{}>) => {
const router = useRouter(); const router = useRouter();
const current = useMemo(() => { const current = useMemo(() => {
const val = router.query.sidebar as SidebarName | undefined const val = router.query.sidebar as SidebarName | undefined;
return val && has(val, sidebarComponents) ? val : undefined; return val && has(val, sidebarComponents) ? val : undefined;
}, [router]); }, [router]);
const open = useCallback( const open = useCallback(
<T extends Name>(name: T) => { <T extends Name>(name: T, props: Props<T>) => {
const [path] = router.asPath.split('?'); const [path] = router.asPath.split("?");
void router.push(path + '?sidebar=' + name, path + '?sidebar=' + name, { const query = Object.entries(props as {})
.filter(([, val]) => val)
.map(([name, val]) => `${name}=${val}`)
.join("&");
const url = path + "?sidebar=" + name + (query && `&${query}`);
void router.push(url, url, {
shallow: true, shallow: true,
scroll: false, scroll: false,
}); });
}, },
[router] [router],
); );
const close = useCallback( const close = useCallback(() => {
() => { const [path] = router.asPath.split("?");
const [path] = router.asPath.split('?');
console.log('trying to close');
void router.replace(path, path, { void router.replace(path, path, {
shallow: true, shallow: true,
scroll: false, scroll: false,
}); });
}, }, [router]);
[router]
);
const value = useMemo<ContextValue>(() => ({ const value = useMemo<ContextValue>(
() => ({
current, current,
open, open,
close, close,
}), [current, open, close]); }),
[current, open, close],
);
return ( return (
<SidebarContext.Provider value={value}> <SidebarContext.Provider value={value}>
{children} {children}
{current && {current && (
<ModalWrapper onOverlayClick={close}> <ModalWrapper onOverlayClick={close}>
{createElement( {createElement(sidebarComponents[current], {
sidebarComponents[current], onRequestClose: close,
{ onRequestClose: close } as any ...omit(["sidebar"], router.query),
)} } as any)}
</ModalWrapper> </ModalWrapper>
} )}
</SidebarContext.Provider> </SidebarContext.Provider>
); );
} };
export const useSidebar = () => useContext(SidebarContext); export const useSidebar = () => useContext(SidebarContext);

File diff suppressed because one or more lines are too long