diff --git a/bin/remant.js b/bin/remant.js index 7cfebb8..f29baf5 100755 --- a/bin/remant.js +++ b/bin/remant.js @@ -67,8 +67,9 @@ export default () => { program .version('0.1.0') .command('create ') + .option('-i, --ignore-page', 'create component without page') .description('setup package development grass') - .action((package) => { + .action((package, option) => { const name = pascalCase(package); const files = [ { @@ -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(() => { diff --git a/packages/Sidebar/Sidebar.css b/packages/Sidebar/Sidebar.css new file mode 100644 index 0000000..d680ec2 --- /dev/null +++ b/packages/Sidebar/Sidebar.css @@ -0,0 +1,4 @@ +.van-sidebar { + width: 85px; + width: var(--sidebar-width, 85px); +} diff --git a/packages/Sidebar/Sidebar.tsx b/packages/Sidebar/Sidebar.tsx new file mode 100644 index 0000000..b5cff31 --- /dev/null +++ b/packages/Sidebar/Sidebar.tsx @@ -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 = (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 {elements}; +}; + +// TODO - support controlled component +const withNeutralListener = (Component: ComponentType) => ( + props: PropsWithChildren +) => { + const { initialActiveKey, className, children } = props; + const [activeKey, setActiveKey] = useState(initialActiveKey || 0); + const handleChangeEvent = useCallback((event: { detail: number }) => { + setActiveKey(event.detail); + }, []); + + return ( + + {children} + + ); +}; + +export default withNeutralListener(Sidebar); diff --git a/packages/Sidebar/index.ts b/packages/Sidebar/index.ts new file mode 100644 index 0000000..a791e79 --- /dev/null +++ b/packages/Sidebar/index.ts @@ -0,0 +1,4 @@ +/** + * @description - nothing but export component + */ +export { default } from './Sidebar'; diff --git a/packages/SidebarItem/SidebarItem.css b/packages/SidebarItem/SidebarItem.css new file mode 100644 index 0000000..0057900 --- /dev/null +++ b/packages/SidebarItem/SidebarItem.css @@ -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); +} diff --git a/packages/SidebarItem/SidebarItem.tsx b/packages/SidebarItem/SidebarItem.tsx new file mode 100644 index 0000000..96ff140 --- /dev/null +++ b/packages/SidebarItem/SidebarItem.tsx @@ -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 = (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 ( + + {children} + + ); +}; + +export default withDefaultProps< + ExogenousSidebarItemProps, + NeutralSidebarItemProps +>(DefaultSidebarProps)(SidebarItem); diff --git a/packages/SidebarItem/index.ts b/packages/SidebarItem/index.ts new file mode 100644 index 0000000..7649f4f --- /dev/null +++ b/packages/SidebarItem/index.ts @@ -0,0 +1,4 @@ +/** + * @description - nothing but export component + */ +export { default } from './SidebarItem'; diff --git a/public/app.config.ts b/public/app.config.ts index d588a1d..b404507 100644 --- a/public/app.config.ts +++ b/public/app.config.ts @@ -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', diff --git a/public/pages/sidebar/index.tsx b/public/pages/sidebar/index.tsx new file mode 100644 index 0000000..e025afa --- /dev/null +++ b/public/pages/sidebar/index.tsx @@ -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 ( + + + + 标签名 + 标签名 + 标签名 + 标签名 + 标签名 + 标签名 + 标签名 + 标签名 + 标签名 + 标签名 + 标签名 + + + + ); +};