feat: SidebarView

pull/13/head
moonrailgun 4 years ago
parent e6e98e4dff
commit 06238c57a6

@ -0,0 +1,9 @@
import React, { Fragment } from 'react';
import { isDevelopment } from 'tailchat-shared';
export const DevContainer: React.FC = React.memo((props) => {
return isDevelopment ? <Fragment>{props.children}</Fragment> : null;
});
DevContainer.displayName = 'DevContainer';
export default DevContainer;

@ -0,0 +1,167 @@
import React, { useState, useContext } from 'react';
import _get from 'lodash/get';
import { DevContainer } from './DevContainer';
import clsx from 'clsx';
export interface SidebarViewMenuItemType {
type: 'item';
title: string;
content: React.ReactNode;
/**
*
*/
isDev?: boolean;
/**
*
*/
hidden?: boolean;
}
interface SidebarViewLinkType {
type: 'link';
title: string;
onClick: () => void;
isDanger?: boolean;
}
const SidebarViewMenuItemTitle: React.FC<{
active?: boolean;
isDanger?: boolean;
onClick: () => void;
}> = (props) => (
<div
className={clsx(
'rounded-sm px-1.5 py-2.5 mb-1 text-gray-300 cursor-pointer hover:bg-black hover:bg-opacity-10 hover:text-gray-200',
{
'bg-black bg-opacity-10 text-white': props.active,
'text-red-500': props.isDanger,
}
)}
style={{ width: 192, lineHeight: '20px' }}
onClick={props.onClick}
>
{props.children}
</div>
);
interface SidebarViewContextProps {
content: React.ReactNode;
setContent: (content: React.ReactNode) => void;
}
export const SidebarViewContext =
React.createContext<SidebarViewContextProps | null>(null);
SidebarViewContext.displayName = 'SidebarViewContext';
export type SidebarViewMenuItem = SidebarViewMenuItemType | SidebarViewLinkType;
export type SidebarViewMenuType =
| {
type: 'group';
title: string;
children: SidebarViewMenuItem[];
}
| SidebarViewMenuItem;
interface SidebarViewMenuProps {
menu: SidebarViewMenuType;
}
const SidebarViewMenuItem: React.FC<SidebarViewMenuProps> = React.memo(
(props) => {
const { menu } = props;
const context = useContext(SidebarViewContext);
if (!context) {
return null;
}
const { content, setContent } = context;
if (menu.type === 'group') {
return (
<div className="pb-2.5 mb-2.5 border-b last:border-0">
<div className="px-1.5 py-2.5 pt-0 text-xs font-bold uppercase">
{menu.title}
</div>
<div>
{menu.children.map((sub, i) => (
<SidebarViewMenuItem key={i} menu={sub} />
))}
</div>
</div>
);
} else if (menu.type === 'item') {
if (menu.hidden === true) {
return null;
}
const component = (
<SidebarViewMenuItemTitle
active={content === menu.content}
onClick={() => setContent(menu.content)}
>
{menu.title}
</SidebarViewMenuItemTitle>
);
if (menu.isDev === true) {
return <DevContainer>{component}</DevContainer>;
} else {
return <div>{component}</div>;
}
} else if (menu.type === 'link') {
return (
<div>
<SidebarViewMenuItemTitle
isDanger={menu.isDanger}
onClick={menu.onClick}
>
{menu.title}
</SidebarViewMenuItemTitle>
</div>
);
}
return null;
}
);
SidebarViewMenuItem.displayName = 'SidebarViewMenuItem';
interface SidebarViewProps {
menu: SidebarViewMenuType[];
/**
*
* @default "0.children.0.content"
*/
defaultContentPath: string;
}
export const SidebarView: React.FC<SidebarViewProps> = React.memo((props) => {
const { menu, defaultContentPath = '0.children.0.content' } = props;
const [content, setContent] = useState<React.ReactNode>(
_get(menu, defaultContentPath, null)
);
return (
<SidebarViewContext.Provider value={{ content, setContent }}>
<div className="flex w-full h-full">
<div
className="bg-black bg-opacity-10 flex flex-col justify-start items-end"
style={{ flex: '1 0 218px', padding: '90px 10px 80px' }}
>
{menu.map((item, i) => (
<SidebarViewMenuItem key={i} menu={item} />
))}
</div>
<div
className="overflow-auto"
style={{ flex: '1 1 800px', padding: '90px 40px 80px' }}
>
{content}
</div>
</div>
</SidebarViewContext.Provider>
);
});
SidebarView.displayName = 'SidebarView';

@ -0,0 +1,6 @@
import React from 'react';
export const SettingsAbout: React.FC = React.memo(() => {
return <div></div>;
});
SettingsAbout.displayName = 'SettingsAbout';

@ -0,0 +1,43 @@
import { FullModal } from '@/components/FullModal';
import { SidebarView, SidebarViewMenuType } from '@/components/SidebarView';
import React, { useCallback, useMemo } from 'react';
import { t } from 'tailchat-shared';
import { SettingsAbout } from './About';
interface SettingsViewProps {
onClose: () => void;
}
export const SettingsView: React.FC<SettingsViewProps> = React.memo((props) => {
const handleChangeVisible = useCallback(
(visible) => {
if (visible === false && typeof props.onClose === 'function') {
props.onClose();
}
},
[props.onClose]
);
const menu: SidebarViewMenuType[] = useMemo(
() => [
{
type: 'group',
title: t('通用'),
children: [
{
type: 'item',
title: t('关于'),
content: <SettingsAbout />,
},
],
},
],
[]
);
return (
<FullModal onChangeVisible={handleChangeVisible}>
<SidebarView menu={menu} defaultContentPath="0.children.0.content" />
</FullModal>
);
});
SettingsView.displayName = 'SettingsView';

@ -1,15 +1,11 @@
import { FullModal } from '@/components/FullModal';
import { closeModal, openModal } from '@/components/Modal';
import { SettingsView } from '@/components/modals/SettingsView';
import { Icon } from '@iconify/react';
import React, { useCallback } from 'react';
export const SettingBtn: React.FC = React.memo(() => {
const handleClick = useCallback(() => {
const key = openModal(
<FullModal onChangeVisible={() => closeModal(key)}>
Setting View
</FullModal>
);
const key = openModal(<SettingsView onClose={() => closeModal(key)} />);
}, []);
return (

@ -74,6 +74,7 @@ module.exports = {
opacity: ['disabled'],
display: ['group-hover'],
borderRadius: ['hover'],
borderWidth: ['last'],
},
},
plugins: [tailchat],

Loading…
Cancel
Save