DEV;Add MenuItem new option - onClick

This commit is contained in:
Roman Jaroš 2022-07-24 20:45:03 +02:00
parent 4658595a62
commit e3b89dabac
7 changed files with 46 additions and 37 deletions

View file

@ -6,15 +6,17 @@ import ChevronDownIcon from '@treejs/components/Icons/ChevronDown';
import ChevronUpIcon from '@treejs/components/Icons/ChevronUp'; import ChevronUpIcon from '@treejs/components/Icons/ChevronUp';
import { isNilOrEmpty } from '@treejs/utils'; import { isNilOrEmpty } from '@treejs/utils';
import { IMenuItem } from '../types'; import { MenuItem } from '../types';
type IProps = { type IProps = {
isChild: boolean; isChild: boolean;
onClick: (href: string, external: boolean) => () => void; item: MenuItem;
} & IMenuItem; onClick: (menuItem: MenuItem) => void;
};
export const SidebarMenuItem = (props: IProps) => { export const SidebarMenuItem = (props: IProps) => {
const { defaultOpen, href, onClick, isChild, label, external, subMenu } = props; const { onClick, isChild, item } = props;
const { defaultOpen, href, label, subMenu } = item;
const [open, setOpen] = useState(defaultOpen); const [open, setOpen] = useState(defaultOpen);
@ -24,16 +26,16 @@ export const SidebarMenuItem = (props: IProps) => {
const handleOnClick = useCallback(() => { const handleOnClick = useCallback(() => {
if (!isNilOrEmpty(href)) { if (!isNilOrEmpty(href)) {
onClick(href, external)(); onClick(item);
} else { } else {
setOpen(!open); setOpen(!open);
} }
}, [open, href, external]); }, [open, item]);
const items = useMemo(() => { const items = useMemo(() => {
if (open && !isNilOrEmpty(subMenu)) { if (open && !isNilOrEmpty(subMenu)) {
return subMenu.map((menuItem, i) => ( return subMenu.map((subMenuItem, i) => (
<SidebarMenuItem key={i} isChild={true} onClick={onClick} {...menuItem} /> <SidebarMenuItem key={i} isChild={true} item={subMenuItem} onClick={onClick} />
)); ));
} else { } else {
return null; return null;

View file

@ -1,4 +1,5 @@
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import { isNil } from 'ramda';
import cx from 'classnames'; import cx from 'classnames';
import { History } from 'history'; import { History } from 'history';
@ -9,7 +10,7 @@ import { isNilOrEmpty } from '@treejs/utils';
import Modals from '../../Modal/components/Modals'; import Modals from '../../Modal/components/Modals';
import Toasters from '../../Toaster/components/Toasters'; import Toasters from '../../Toaster/components/Toasters';
import { IMenuItem } from '../types'; import { MenuItem } from '../types';
import SidebarMenuItem from './SidebarMenuItem'; import SidebarMenuItem from './SidebarMenuItem';
import UserMenu from './UserMenu'; import UserMenu from './UserMenu';
@ -27,9 +28,9 @@ type IProps = {
}; };
history: History; history: History;
menuItems?: { menuItems?: {
navigation?: IMenuItem[]; navigation?: MenuItem[];
sidebar?: IMenuItem[]; sidebar?: MenuItem[];
user?: IMenuItem[]; user?: MenuItem[];
}; };
}; };
@ -61,11 +62,15 @@ const Skeleton: React.FC<IProps> = (props) => {
}, [sidebarOpened]); }, [sidebarOpened]);
const openLink = useCallback( const openLink = useCallback(
(link: string, external: boolean) => () => { ({ href, external, onClick }: MenuItem) => {
if (external) { if (!isNil(onClick)) {
window.open(link, '_blank'); onClick();
} else { } else {
history.push(link); if (external) {
window.open(href, '_blank');
} else {
history.push(href);
}
} }
setSidebarOpened(false); setSidebarOpened(false);
}, },
@ -75,7 +80,7 @@ const Skeleton: React.FC<IProps> = (props) => {
const sidebarItems = useMemo(() => { const sidebarItems = useMemo(() => {
if (!isNilOrEmpty(menuItems.sidebar)) { if (!isNilOrEmpty(menuItems.sidebar)) {
return menuItems.sidebar.map((menuItem, i) => ( return menuItems.sidebar.map((menuItem, i) => (
<SidebarMenuItem key={i} isChild={false} onClick={openLink} {...menuItem} /> <SidebarMenuItem key={i} isChild={false} onClick={openLink} item={menuItem} />
)); ));
} else { } else {
return null; return null;
@ -84,9 +89,10 @@ const Skeleton: React.FC<IProps> = (props) => {
const navigationItems = useMemo(() => { const navigationItems = useMemo(() => {
if (!isNilOrEmpty(menuItems.navigation)) { if (!isNilOrEmpty(menuItems.navigation)) {
return menuItems.navigation.map(({ label, href, external }, i) => ( return menuItems.navigation.map((menuItem, i) => (
<div key={i} className="navigation-item" onClick={openLink(href, external)}> // eslint-disable-next-line react/jsx-no-bind
{label} <div key={i} className="navigation-item" onClick={() => openLink(menuItem)}>
{menuItem.label}
</div> </div>
)); ));
} else { } else {

View file

@ -2,24 +2,22 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { isNilOrEmpty } from '@treejs/utils'; import { isNilOrEmpty } from '@treejs/utils';
import { IMenuItem } from '../types'; import { MenuItem } from '../types';
type IProps = { type IProps = {
children: React.ReactElement; children: React.ReactElement;
items: IMenuItem[]; items: MenuItem[];
onClick: (href: string, external: boolean) => () => void; onClick: (menuItem: MenuItem) => void;
}; };
export const UserMenu = (props: IProps) => { export const UserMenu: React.FC<IProps> = ({ items, onClick, children }) => {
const { items, onClick, children } = props;
const divRef = useRef<HTMLDivElement>(); const divRef = useRef<HTMLDivElement>();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const handleClick = useCallback( const handleClick = useCallback(
(href: string, external: boolean) => () => { (item: MenuItem) => () => {
onClick(href, external)(); onClick(item);
setOpen(false); setOpen(false);
}, },
[onClick] [onClick]
@ -37,9 +35,9 @@ export const UserMenu = (props: IProps) => {
const userItems = useMemo(() => { const userItems = useMemo(() => {
if (!isNilOrEmpty(items)) { if (!isNilOrEmpty(items)) {
return items.map(({ label, href, external }, i) => ( return items.map((menuItem, i) => (
<div key={i} className="user-item" onClick={handleClick(href, external)}> <div key={i} className="user-item" onClick={handleClick(menuItem)}>
{label} {menuItem.label}
</div> </div>
)); ));
} else { } else {

View file

@ -1,7 +1,8 @@
export interface IMenuItem { export type MenuItem = {
defaultOpen?: boolean; defaultOpen?: boolean;
external?: boolean; external?: boolean;
href?: string; href?: string;
label: string; label: string;
subMenu?: IMenuItem[]; onClick?: () => void;
} subMenu?: MenuItem[];
};

View file

@ -41,10 +41,11 @@ export const Template = (args) => (
{ label: 'Menu External', href: 'https://treejs.romanjaros.dev', external: true }, { label: 'Menu External', href: 'https://treejs.romanjaros.dev', external: true },
], ],
sidebar: [{ label: 'Menu 1' }, { label: 'Menu 2' }], sidebar: [{ label: 'Menu 1' }, { label: 'Menu 2' }],
user: [{ label: 'Menu 1' }, { label: 'Menu 2', href: '/r3' }], user: [{ label: 'Menu 1', href: '/ru1' }, { label: 'Menu 2', href: '/ru2' }],
}, },
enabledMenu: { enabledMenu: {
user: true, user: true,
navigation: true,
sidebar: true, sidebar: true,
}, },
}} }}

View file

@ -7,7 +7,8 @@ const Router = () => (
<Route path="/" element={<div>Main</div>} /> <Route path="/" element={<div>Main</div>} />
<Route path="/r1" element={<div>Conten 1</div>} /> <Route path="/r1" element={<div>Conten 1</div>} />
<Route path="/r2" element={<div>Conten 2</div>} /> <Route path="/r2" element={<div>Conten 2</div>} />
<Route path="/r3" element={<div>User content 2</div>} /> <Route path="/ru1" element={<div>User content 1</div>} />
<Route path="/ru2" element={<div>User content 2</div>} />
</Routes> </Routes>
); );

View file

@ -24,6 +24,6 @@ const composeSetup = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
const store = createStore(mainReducer, composeSetup(applyMiddleware(thunk))); const store = createStore(mainReducer, composeSetup(applyMiddleware(thunk)));
// eslint-disable-next-line react/display-name // eslint-disable-next-line react/display-name
const StoreProvider: React.FC = ({ children }) => <Provider store={store}>{children}</Provider>; const StoreProvider: React.FC = ({ children }: any) => <Provider store={store}>{children}</Provider>;
export default StoreProvider; export default StoreProvider;