1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-25 12:56:41 +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<{}> {
initialTab?: number;
tab?: number;
onTabChange?: (index?: number) => void;
}
interface SidebarStackContextValue {
@ -38,12 +48,31 @@ const SidebarCards: FC = ({ children }) => {
return <div className={styles.card}>{nonEmptyChildren[activeTab]}</div>;
};
const SidebarStack = function({ children, initialTab }: SidebarStackProps) {
const [activeTab, setActiveTab] = useState<number | undefined>(initialTab);
const closeAllTabs = useCallback(() => setActiveTab(undefined), []);
const SidebarStack = function({
children,
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 (
<SidebarStackContext.Provider value={{ activeTab, setActiveTab, closeAllTabs }}>
<SidebarStackContext.Provider
value={{ activeTab, setActiveTab: onChangeTab, closeAllTabs }}
>
<div className={styles.stack}>{children}</div>
</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 { Button } from '~/components/input/Button';
import { SidebarName } from '~/constants/sidebar';
import { URLS } from '~/constants/urls';
import { useSidebar } from '~/utils/providers/SidebarProvider';
import { Group } from "~/components/containers/Group";
import { Button } from "~/components/input/Button";
import { SidebarName } from "~/constants/sidebar";
import { URLS } from "~/constants/urls";
import { useSidebar } from "~/utils/providers/SidebarProvider";
import styles from './styles.module.scss';
import styles from "./styles.module.scss";
export interface BorisSuperpowersProps {}
const BorisSuperpowers: FC<BorisSuperpowersProps> = () => {
const { open } = useSidebar();
const openProfileSidebar = useCallback(() => {
open(SidebarName.Settings);
open(SidebarName.Settings, { page: "profile" });
}, [open]);
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 { 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';
import { isNil } from "ramda";
interface ProfileSidebarProps extends DialogComponentProps {
page: string;
import { ProfileSidebarNotes } from "~/components/profile/ProfileSidebarNotes";
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 (
<SidebarWrapper onClose={onRequestClose}>
<SidebarStack>
<SidebarStackCard headerFeature="close" title="Профиль" onBackPress={onRequestClose}>
<SidebarStack tab={initialTab} onTabChange={onTabChange}>
<SidebarStackCard
headerFeature="close"
title="Профиль"
onBackPress={onRequestClose}
>
<ProfileSidebarMenu onClose={onRequestClose} />
</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 { has } from 'ramda';
import { useRouter } from "next/router";
import { has, omit } from "ramda";
import { ModalWrapper } from '~/components/dialogs/ModalWrapper';
import { sidebarComponents, SidebarName } from '~/constants/sidebar';
import { DialogComponentProps } from '~/types/modal';
import { ModalWrapper } from "~/components/dialogs/ModalWrapper";
import { sidebarComponents, SidebarName } from "~/constants/sidebar";
import { DialogComponentProps } from "~/types/modal";
import { SidebarComponentProps } from "~/types/sidebar";
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
type Props<
T extends Name
> = typeof sidebarComponents[T] extends FunctionComponent<infer U>
? U extends object
? U extends SidebarComponentProps
? Omit<U, "onRequestClose">
: U
: U
: {};
const SidebarContext = createContext({
current: undefined as SidebarName | undefined,
open: <T extends Name>(name: T) => {},
close: () => {},
open: <T extends Name>(name: T, props: Props<T>) => {},
close: () => {},
});
export const SidebarProvider = <T extends Name>({ children }: PropsWithChildren<{}>) => {
export const SidebarProvider = <T extends Name>({
children,
}: PropsWithChildren<{}>) => {
const router = useRouter();
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;
}, [router]);
const open = useCallback(
<T extends Name>(name: T) => {
const [path] = router.asPath.split('?');
void router.push(path + '?sidebar=' + name, path + '?sidebar=' + name, {
<T extends Name>(name: T, props: Props<T>) => {
const [path] = router.asPath.split("?");
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,
scroll: false,
});
},
[router]
[router],
);
const close = useCallback(
() => {
const [path] = router.asPath.split('?');
const close = useCallback(() => {
const [path] = router.asPath.split("?");
console.log('trying to close');
void router.replace(path, path, {
shallow: true,
scroll: false,
});
}, [router]);
void router.replace(path, path, {
shallow: true,
scroll: false,
});
},
[router]
const value = useMemo<ContextValue>(
() => ({
current,
open,
close,
}),
[current, open, close],
);
const value = useMemo<ContextValue>(() => ({
current,
open,
close,
}), [current, open, close]);
return (
<SidebarContext.Provider value={value}>
{children}
{current &&
{current && (
<ModalWrapper onOverlayClick={close}>
{createElement(
sidebarComponents[current],
{ onRequestClose: close } as any
)}
{createElement(sidebarComponents[current], {
onRequestClose: close,
...omit(["sidebar"], router.query),
} as any)}
</ModalWrapper>
}
)}
</SidebarContext.Provider>
);
}
};
export const useSidebar = () => useContext(SidebarContext);