DEV;Add MenuItem new option - onClick
This commit is contained in:
parent
4658595a62
commit
e3b89dabac
7 changed files with 46 additions and 37 deletions
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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[];
|
||||||
|
};
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue