Skip to content

Commit

Permalink
feat: implement sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
huang-xiao-jian committed May 31, 2020
1 parent 89cff52 commit 39d55ad
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 4 deletions.
13 changes: 9 additions & 4 deletions bin/remant.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ export default () => {
program
.version('0.1.0')
.command('create <package>')
.option('-i, --ignore-page', 'create component without page')
.description('setup package development grass')
.action((package) => {
.action((package, option) => {
const name = pascalCase(package);
const files = [
{
Expand All @@ -83,14 +84,18 @@ program
filename: path.resolve(__dirname, `../packages/${name}/${name}.css`),
content: '',
},
{
];

// several component without page preview
if (!option.ignorePage) {
files.push({
filename: path.resolve(
__dirname,
`../public/pages/${camelCase(package)}/index.tsx`
),
content: renderPage(name),
},
];
});
}

files.forEach((item) => {
fs.ensureFile(item.filename).then(() => {
Expand Down
4 changes: 4 additions & 0 deletions packages/Sidebar/Sidebar.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.van-sidebar {
width: 85px;
width: var(--sidebar-width, 85px);
}
76 changes: 76 additions & 0 deletions packages/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// packages
import React, {
FunctionComponent,
Children,
ComponentType,
cloneElement,
isValidElement,
useState,
useCallback,
PropsWithChildren,
} from 'react';
import clsx from 'clsx';
import { View } from 'remax/wechat';
// internal
import './Sidebar.css';

// 默认值填充属性
interface SidebarProps {
// 状态标记
activeKey: number;
// 事件回调
onChange: (event: { detail: number }) => void;
// 容器类名,用以覆盖内部
className?: string;
}

interface TransparentNeutralListenerSidebarProps {
// 状态标记
activeKey?: number;
// 默认受控组件
initialActiveKey?: number;
// 事件回调
onChange?: (event: { detail: number }) => void;
// 容器类名,用以覆盖内部
className?: string;
}

const Sidebar: FunctionComponent<SidebarProps> = (props) => {
const { className, activeKey, onChange, children } = props;
const classnames = {
container: clsx(className, 'van-sidebar'),
};
const elements = Children.map(children, (child, index) => {
return !isValidElement(child)
? child
: cloneElement(child, {
selected: index === activeKey,
onClick: () => onChange({ detail: index }),
});
});

return <View className={classnames.container}>{elements}</View>;
};

// TODO - support controlled component
const withNeutralListener = (Component: ComponentType<SidebarProps>) => (
props: PropsWithChildren<TransparentNeutralListenerSidebarProps>
) => {
const { initialActiveKey, className, children } = props;
const [activeKey, setActiveKey] = useState(initialActiveKey || 0);
const handleChangeEvent = useCallback((event: { detail: number }) => {
setActiveKey(event.detail);
}, []);

return (
<Component
className={className}
activeKey={activeKey}
onChange={handleChangeEvent}
>
{children}
</Component>
);
};

export default withNeutralListener(Sidebar);
4 changes: 4 additions & 0 deletions packages/Sidebar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* @description - nothing but export component
*/
export { default } from './Sidebar';
49 changes: 49 additions & 0 deletions packages/SidebarItem/SidebarItem.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.van-sidebar-item {
display: block;
box-sizing: border-box;
overflow: hidden;
word-wrap: break-word;
border-left: 3px solid transparent;
user-select: none;
padding: 20px 12px 20px 8px;
padding: var(--sidebar-padding, 20px 12px 20px 8px);
font-size: 14px;
font-size: var(--sidebar-font-size, 14px);
line-height: 20px;
line-height: var(--sidebar-line-height, 20px);
color: #323233;
color: var(--sidebar-text-color, #323233);
background-color: #f7f8fa;
background-color: var(--sidebar-background-color, #f7f8fa);
}
.van-sidebar-item__text {
position: relative;
display: inline-block;
}
.van-sidebar-item--hover:not(.van-sidebar-item--disabled) {
background-color: #f2f3f5;
background-color: var(--sidebar-active-color, #f2f3f5);
}
.van-sidebar-item::after {
border-bottom-width: 1px;
}
.van-sidebar-item--selected {
color: #323233;
color: var(--sidebar-selected-text-color, #323233);
font-weight: 500;
font-weight: var(--sidebar-selected-font-weight, 500);
border-color: #ee0a24;
border-color: var(--sidebar-selected-border-color, #ee0a24);
}
.van-sidebar-item--selected::after {
border-right-width: 1px;
}
.van-sidebar-item--selected,
.van-sidebar-item--selected.van-sidebar-item--hover {
background-color: #fff;
background-color: var(--sidebar-selected-background-color, #fff);
}
.van-sidebar-item--disabled {
color: #c8c9cc;
color: var(--sidebar-disabled-text-color, #c8c9cc);
}
71 changes: 71 additions & 0 deletions packages/SidebarItem/SidebarItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// packages
import React, { FunctionComponent } from 'react';
import clsx from 'clsx';
import { View } from 'remax/wechat';
// internal
import withDefaultProps from '../tools/with-default-props-advance';
import './SidebarItem.css';

// 默认值填充属性
interface NeutralSidebarItemProps {
disabled: boolean;
// Sidebar 显式传入
selected: boolean;
}

interface ExogenousSidebarItemProps {
// 暴露状态类
activeClassName?: string;
disabeldClassName?: string;
// Sidebar 显式传入
onClick?: (event: any) => void;
// 容器类名,用以覆盖内部
className?: string;
}

type SidebarItemProps = NeutralSidebarItemProps & ExogenousSidebarItemProps;

const DefaultSidebarProps: NeutralSidebarItemProps = {
disabled: false,
selected: false,
};
const SidebarItem: FunctionComponent<SidebarItemProps> = (props) => {
const {
className,
disabled,
selected,
activeClassName,
disabeldClassName,
onClick,
children,
} = props;
const classnames = {
container: clsx(
className,
// 链接状态类
selected && activeClassName,
disabled && disabeldClassName,
'van-sidebar-item',
{
'van-sidebar-item--selected': selected,
'van-sidebar-item--disabled': disabled,
}
),
};

return (
<View
className={classnames.container}
hoverClassName="van-sidebar-item--hover"
hoverStayTime={70}
onClick={onClick}
>
<View className="van-sidebar-item__text">{children}</View>
</View>
);
};

export default withDefaultProps<
ExogenousSidebarItemProps,
NeutralSidebarItemProps
>(DefaultSidebarProps)(SidebarItem);
4 changes: 4 additions & 0 deletions packages/SidebarItem/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* @description - nothing but export component
*/
export { default } from './SidebarItem';
1 change: 1 addition & 0 deletions public/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AppConfig } from 'remax/wechat';

const config: AppConfig = {
pages: [
'pages/sidebar/index',
'pages/panel/index',
'pages/steps/index',
'pages/progress/index',
Expand Down
29 changes: 29 additions & 0 deletions public/pages/sidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// packages
import * as React from 'react';
import { View } from 'remax/wechat';

// internal
import Sidebar from '../../../packages/Sidebar';
import SidebarItem from '../../../packages/SidebarItem';

export default () => {
return (
<View className="demo-block">
<View>
<Sidebar initialActiveKey={0}>
<SidebarItem>标签名</SidebarItem>
<SidebarItem>标签名</SidebarItem>
<SidebarItem>标签名</SidebarItem>
<SidebarItem>标签名</SidebarItem>
<SidebarItem>标签名</SidebarItem>
<SidebarItem>标签名</SidebarItem>
<SidebarItem>标签名</SidebarItem>
<SidebarItem>标签名</SidebarItem>
<SidebarItem>标签名</SidebarItem>
<SidebarItem>标签名</SidebarItem>
<SidebarItem>标签名</SidebarItem>
</Sidebar>
</View>
</View>
);
};

0 comments on commit 39d55ad

Please sign in to comment.