Skip to content

Commit

Permalink
style: pretiffy code
Browse files Browse the repository at this point in the history
  • Loading branch information
wewoor committed Nov 20, 2020
1 parent afab6dc commit ec2579d
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 71 deletions.
3 changes: 1 addition & 2 deletions src/common/css.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@

/**
* px = em * parentElementFontSize
* @param em em value
* TODO: Use Template Literal Types replace fontSize typing
*/
export function em2Px(em: number, fontSize: number): number {
return em * fontSize;
}
}
4 changes: 2 additions & 2 deletions src/common/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ export function getRelativePosition(
export function getEventPosition(e: React.MouseEvent) {
return {
x: e.clientX,
y: e.clientY
}
y: e.clientY,
};
}

export function findParentByClassName<T>(element, className): T | null {
Expand Down
5 changes: 4 additions & 1 deletion src/components/contextview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ export function useContextView(props?: IContextViewProps): IContextView {

if (!contextView) {
contextView = document.createElement('div');
contextView.className = classNames(claName, Utils.isMacOs() ? 'mac' : null);
contextView.className = classNames(
claName,
Utils.isMacOs() ? 'mac' : null
);
contextView.style.display = 'none';
const root = document.getElementById('molecule');
if (!root) {
Expand Down
13 changes: 10 additions & 3 deletions src/components/icon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ export interface IIcon {
export function Icon(props: IIcon) {
const { className, type, ...others } = props;
return (
<span className={classNames(className, 'codicon', prefixClaName(type, 'codicon'))} {...others}></span>
)
}
<span
className={classNames(
className,
'codicon',
prefixClaName(type, 'codicon')
)}
{...others}
></span>
);
}
32 changes: 25 additions & 7 deletions src/components/menu/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,43 @@ export interface IMenu extends ISubMenu {}
export const defaultMenuClassName = 'menu';

export function Menu(props: React.PropsWithChildren<IMenu>) {
const { className, mode = MenuMode.Vertical, data = [], children, ...others } = props;
const {
className,
mode = MenuMode.Vertical,
data = [],
children,
...others
} = props;
let content = children;
const claNames = classNames(prefixClaName(defaultMenuClassName), mode, className);
const claNames = classNames(
prefixClaName(defaultMenuClassName),
mode,
className
);

if (data.length > 0) {
const renderMenusByData = (menus: IMenu[]) => {
return menus.map((item: IMenu) => {
if (item.data && item.data.length > 0) {
return <SubMenu mode={mode} {...item}>{ renderMenusByData(item.data) }</SubMenu>
return (
<SubMenu mode={mode} {...item}>
{renderMenusByData(item.data)}
</SubMenu>
);
}
return <MenuItem key={item.id} {...item}>{item.name}</MenuItem>
})
}
return (
<MenuItem key={item.id} {...item}>
{item.name}
</MenuItem>
);
});
};
content = renderMenusByData(data);
}

return (
<ul className={claNames} {...others}>
{ content }
{content}
</ul>
);
}
35 changes: 25 additions & 10 deletions src/components/menu/menuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,44 @@ export interface IMenuItem extends HTMLElementProps {
*/
keybinding?: string;
/**
* Custom render
* Custom render
*/
render?: (data: IMenuItem) => ReactNode;
onClick?: (e: React.MouseEvent, item?: IMenuItem) => void;
sortIndex?: number;
}

export function MenuItem(props: React.PropsWithChildren<IMenuItem>) {
const { icon, className, onClick, keybinding, render, children, name } = props;
const {
icon,
className,
onClick,
keybinding,
render,
children,
name,
} = props;
const events = {
onClick: function(e: React.MouseEvent) {
onClick: function (e: React.MouseEvent) {
if (onClick) {
onClick(e, props)
onClick(e, props);
}
}
}
},
};
return (
<li className={classNames(defaultMenuItemClassName, className)} {...events}>
<li
className={classNames(defaultMenuItemClassName, className)}
{...events}
>
<a className="menu-item-container">
<Icon className="menu-item-check" type={icon || ''} />
<span className="menu-item-label" title={name as string}>{ render ? render(props) : children }</span>
{ keybinding ? <span className="keybinding">{keybinding}</span> : null }
<span className="menu-item-label" title={name as string}>
{render ? render(props) : children}
</span>
{keybinding ? (
<span className="keybinding">{keybinding}</span>
) : null}
</a>
</li>
)
);
}
2 changes: 1 addition & 1 deletion src/components/menu/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,6 @@ $menu: 'menu';
display: block;
font-size: 13px;
position: fixed;
// transition-delay: 0.2s; Bug
// transition-delay: 0.2s; Bug
z-index: 1;
}
146 changes: 103 additions & 43 deletions src/components/menu/subMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import { classNames, prefixClaName } from 'mo/common/className';
import { Icon } from '../icon';
import { Menu } from './menu';
import { useEffect } from 'react';
import { findParentByClassName, getRelativePosition, TriggerEvent } from 'mo/common/dom';
import {
findParentByClassName,
getRelativePosition,
TriggerEvent,
} from 'mo/common/dom';
import { defaultMenuItemClassName, IMenuItem } from './menuItem';
import { em2Px } from 'mo/common/css';

export enum MenuMode {
Vertical = 'vertical',
Horizontal = 'horizontal'
};
Horizontal = 'horizontal',
}

export function isHorizontal(mode: MenuMode) {
return mode === MenuMode.Horizontal;
Expand All @@ -27,87 +31,128 @@ export interface ISubMenu extends IMenuItem {
*/
trigger?: TriggerEvent;
icon?: string;
data?: ISubMenu[];
data?: ISubMenu[];
mode?: MenuMode;
}

const defaultSubMenuClassName = prefixClaName('sub-menu');

function hideSubMenu(target?: HTMLElement) {
const container = target || document.body;
const all = container.querySelectorAll<HTMLMenuElement>('.'+defaultSubMenuClassName);
all?.forEach(ele => {
const all = container.querySelectorAll<HTMLMenuElement>(
'.' + defaultSubMenuClassName
);
all?.forEach((ele) => {
ele.style.visibility = 'hidden';
});
}

const hideAll = () => {
const hideAll = () => {
hideSubMenu();
};

const hideAfterLeftWindow = () => {
if (document.hidden) {
hideSubMenu();
}
}
};

let timer;

export function SubMenu(props: React.PropsWithChildren<ISubMenu>) {
const { className, name, render, data = [], mode = MenuMode.Vertical, icon, children, ...others } = props;
const {
className,
name,
render,
data = [],
mode = MenuMode.Vertical,
icon,
children,
...others
} = props;
const cNames = classNames(defaultSubMenuClassName, mode, className);
const isAlignHorizontal = isHorizontal(mode);

const events = {
onMouseOver: (event: React.MouseEvent<any, any>) => {
clearTimeout(timer);

const nextMenuItem = findParentByClassName<HTMLLIElement>(event.target, defaultMenuItemClassName);
const nextSubMenu = nextMenuItem?.querySelector<HTMLMenuElement>('.' + defaultSubMenuClassName);
const nextMenuItem = findParentByClassName<HTMLLIElement>(
event.target,
defaultMenuItemClassName
);
const nextSubMenu = nextMenuItem?.querySelector<HTMLMenuElement>(
'.' + defaultSubMenuClassName
);
if (!nextMenuItem || !nextSubMenu) return;

const prevMenuItem = findParentByClassName<HTMLLIElement>(event.relatedTarget, defaultMenuItemClassName);
const prevSubMenu = prevMenuItem?.querySelector<HTMLMenuElement>('.' + defaultSubMenuClassName);

if (prevMenuItem && prevSubMenu && !prevMenuItem.contains(nextMenuItem)) {
const prevMenuItem = findParentByClassName<HTMLLIElement>(
event.relatedTarget,
defaultMenuItemClassName
);
const prevSubMenu = prevMenuItem?.querySelector<HTMLMenuElement>(
'.' + defaultSubMenuClassName
);

if (
prevMenuItem &&
prevSubMenu &&
!prevMenuItem.contains(nextMenuItem)
) {
hideAll();
}

const domRect = nextMenuItem.getBoundingClientRect();
nextSubMenu.style.visibility = 'visible';
const pos = getRelativePosition(nextSubMenu, domRect);
if (isAlignHorizontal) pos.y = pos.y + domRect.height;

if (isAlignHorizontal) pos.y = pos.y + domRect.height;
else {
pos.x = pos.x + domRect.width;
// The vertical menu default has padding 0.5em so that need reduce the padding
const fontSize = getComputedStyle(nextSubMenu).getPropertyValue('font-size');
const paddingTop = em2Px(0.5, parseInt(fontSize.replace('px', ''), 10));
// The vertical menu default has padding 0.5em so that need reduce the padding
const fontSize = getComputedStyle(nextSubMenu).getPropertyValue(
'font-size'
);
const paddingTop = em2Px(
0.5,
parseInt(fontSize.replace('px', ''), 10)
);
pos.y = pos.y - paddingTop;
}

nextSubMenu.style.cssText = `
left: ${pos.x}px;
top: ${pos.y}px;
`;
},
onMouseOut: function(event: React.MouseEvent) {
const nextMenuItem = findParentByClassName<HTMLLIElement>(event.relatedTarget, defaultMenuItemClassName) ;
onMouseOut: function (event: React.MouseEvent) {
const nextMenuItem = findParentByClassName<HTMLLIElement>(
event.relatedTarget,
defaultMenuItemClassName
);
if (!nextMenuItem) return;

const prevMenuItem = event.currentTarget as HTMLLIElement;
const prevSubMenu = prevMenuItem?.querySelector('.' + defaultSubMenuClassName);
const nextSubMenu = nextMenuItem?.querySelector('.' + defaultSubMenuClassName);
// Hide the prev subMenu when the next menuItem hasn't subMenu and the prev MenuItem
const prevSubMenu = prevMenuItem?.querySelector(
'.' + defaultSubMenuClassName
);
const nextSubMenu = nextMenuItem?.querySelector(
'.' + defaultSubMenuClassName
);
// Hide the prev subMenu when the next menuItem hasn't subMenu and the prev MenuItem
// subMenu not contains it.
if (!nextSubMenu && prevSubMenu && !prevMenuItem.contains(nextMenuItem)) {
if (
!nextSubMenu &&
prevSubMenu &&
!prevMenuItem.contains(nextMenuItem)
) {
hideAll();
// delayHide(prevMenuItem);
}
},
onClick: function(event: React.MouseEvent) {
onClick: function (event: React.MouseEvent) {
event.stopPropagation();
}
},
};

useEffect(() => {
Expand All @@ -119,24 +164,39 @@ export function SubMenu(props: React.PropsWithChildren<ISubMenu>) {
window.removeEventListener('click', hideAll);
window.removeEventListener('visibilitychange', hideAfterLeftWindow);
clearTimeout(timer);
}
}, [])
};
}, []);

const chevronType = isAlignHorizontal ? 'down' : 'right';
const subMenuContent = data.length > 0 ?
<Menu className={cNames} style={{ visibility: 'hidden' }} data={data}/> :
<Menu className={cNames} style={{ visibility: 'hidden' }}> { children } </Menu>;

console.log('mode', mode, isAlignHorizontal)

return (
const subMenuContent =
data.length > 0 ? (
<Menu
className={cNames}
style={{ visibility: 'hidden' }}
data={data}
/>
) : (
<Menu className={cNames} style={{ visibility: 'hidden' }}>
{' '}
{children}{' '}
</Menu>
);

console.log('mode', mode, isAlignHorizontal);

return (
<li className={defaultMenuItemClassName} {...events} {...others}>
<a className="menu-item-container">
<Icon className="menu-item-check" type={icon || ''} />
<span className="menu-item-label">{ render ? render(props) : name }</span>
<Icon className="menu-item-indicator" type={`chevron-${chevronType}`}/>
<span className="menu-item-label">
{render ? render(props) : name}
</span>
<Icon
className="menu-item-indicator"
type={`chevron-${chevronType}`}
/>
</a>
{ subMenuContent }
{subMenuContent}
</li>
)
);
}
Loading

0 comments on commit ec2579d

Please sign in to comment.