diff --git a/.fusion b/.fusion index bbadfbc141..588d0075ef 100644 --- a/.fusion +++ b/.fusion @@ -58,6 +58,7 @@ "line": "lib/core/style/_line.scss", "shadow": "lib/core/style/_shadow.scss", "icon": "lib/core/style/_icon.scss", + "motion": "lib/core/style/_motion.scss", "form-element": "lib/core/utility/_form-element.scss", "popup": "lib/core/utility/_popup.scss", "mask": "lib/core/utility/_mask.scss", @@ -79,6 +80,7 @@ "collapse": "lib/collapse/scss/variable.scss", "time-picker": "lib/time-picker/scss/variable.scss", "date-picker": "lib/date-picker/scss/variable.scss", + "drawer": "lib/drawer/scss/variable.scss", "message": "lib/message/scss/variable.scss", "dialog": "lib/dialog/scss/variable.scss", "grid": "lib/grid/scss/variable.scss", diff --git a/CHANGELOG.md b/CHANGELOG.md index 699937b419..fd24e0f686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,109 @@ # Change Log +## [1.17.6](https://github.com/alibaba-fusion/next/compare/1.17.5...1.17.6) (2019-08-15) + + +### Bug Fixes + +* **Menu:** value in selectedKeys doesn't exit in k2n ([fef80cd](https://github.com/alibaba-fusion/next/commit/fef80cd)) + + + + +## [1.17.5](https://github.com/alibaba-fusion/next/compare/1.17.4...1.17.5) (2019-08-14) + + +### Bug Fixes + +* **Field:** bug of resetToDefault withtou initValue. close [#1001](https://github.com/alibaba-fusion/next/issues/1001) ([a76b05c](https://github.com/alibaba-fusion/next/commit/a76b05c)) +* **Input:** support null to reset value ([0ecc5c2](https://github.com/alibaba-fusion/next/commit/0ecc5c2)) +* **Pagination:** react warning use setTimout event ([0e92ade](https://github.com/alibaba-fusion/next/commit/0e92ade)) + + +### Features + +* **Animate:** add slide animations ([407dc5b](https://github.com/alibaba-fusion/next/commit/407dc5b)) +* **Drawer:** make drawer configurable ([fbc4ad2](https://github.com/alibaba-fusion/next/commit/fbc4ad2)) +* **Locale:** add Drawer words ([1be3ea8](https://github.com/alibaba-fusion/next/commit/1be3ea8)) + + + + +## [1.17.4](https://github.com/alibaba-fusion/next/compare/1.17.3...1.17.4) (2019-08-13) + + +### Bug Fixes + +* **Menu:** child-selected should work in popup mode ([bd784e7](https://github.com/alibaba-fusion/next/commit/bd784e7)) +* **Tag:** closable can`t set `data-xx` prop ([d1e5507](https://github.com/alibaba-fusion/next/commit/d1e5507)) + + + + +## [1.17.3](https://github.com/alibaba-fusion/next/compare/1.17.2...1.17.3) (2019-08-09) + + +### Bug Fixes + +* **Select:** width should be in outer dom ([c8f072f](https://github.com/alibaba-fusion/next/commit/c8f072f)) + + + + +## [1.17.2](https://github.com/alibaba-fusion/next/compare/1.17.1...1.17.2) (2019-08-09) + + + + +## [1.17.1](https://github.com/alibaba-fusion/next/compare/1.17.0...1.17.1) (2019-08-08) + + + + +# [1.17.0](https://github.com/alibaba-fusion/next/compare/1.16.6...1.17.0) (2019-08-08) + + +### Bug Fixes + +* add name and value ([5beedd8](https://github.com/alibaba-fusion/next/commit/5beedd8)) +* **Input:** fix textarea ([ac270ea](https://github.com/alibaba-fusion/next/commit/ac270ea)) +* **Input:** fix ts property ([9ceb94a](https://github.com/alibaba-fusion/next/commit/9ceb94a)) +* **Menu:** paddingLeft should only be related to inline mode ([1115687](https://github.com/alibaba-fusion/next/commit/1115687)) +* **Menu:** string in Group/SubMenu causes error, close [#952](https://github.com/alibaba-fusion/next/issues/952) ([18a7f17](https://github.com/alibaba-fusion/next/commit/18a7f17)) +* **Nav:** subNav should be selected if it has selected child, close [#520](https://github.com/alibaba-fusion/next/issues/520) ([3eca52f](https://github.com/alibaba-fusion/next/commit/3eca52f)) +* **Range:** prevent arrow keyup event when disabled ([05f5a87](https://github.com/alibaba-fusion/next/commit/05f5a87)) +* **Search:** onSearch ts ([35d33dc](https://github.com/alibaba-fusion/next/commit/35d33dc)) +* **Select:** popupComponent do not need syncWidth ([71d7943](https://github.com/alibaba-fusion/next/commit/71d7943)) +* **Tab:** only focus after first render ([5feb398](https://github.com/alibaba-fusion/next/commit/5feb398)) +* **Transfer:** fix that Transfer component cannot be dragged in firefox ([0f72ef6](https://github.com/alibaba-fusion/next/commit/0f72ef6)) +* **Transfer:** pass test case ([632dea7](https://github.com/alibaba-fusion/next/commit/632dea7)) +* **Tree:** Still can check after disableChecked ([20ac774](https://github.com/alibaba-fusion/next/commit/20ac774)) +* **Typescript:** add a tags ts. ([443eecf](https://github.com/alibaba-fusion/next/commit/443eecf)) +* **TypeScript:** correct Upload Dragger props, close [#936](https://github.com/alibaba-fusion/next/issues/936) ([2c4ad4c](https://github.com/alibaba-fusion/next/commit/2c4ad4c)) + + +### Features + +* Add Next Adaptor ([0c7f49c](https://github.com/alibaba-fusion/next/commit/0c7f49c)) +* Add Next Adaptor ([#903](https://github.com/alibaba-fusion/next/issues/903)) ([807e258](https://github.com/alibaba-fusion/next/commit/807e258)) +* time-function & duration update ([f4535d3](https://github.com/alibaba-fusion/next/commit/f4535d3)) +* **Breadcrumb:** maxNode support `auto` ([9ce0323](https://github.com/alibaba-fusion/next/commit/9ce0323)) +* **ConfigProvider:** add device prop ([67161a1](https://github.com/alibaba-fusion/next/commit/67161a1)) +* **DatePicker:** support set Array defaultValue ([2f9ea90](https://github.com/alibaba-fusion/next/commit/2f9ea90)) +* **Dialog:** add max-width to fit phone ([66f463a](https://github.com/alibaba-fusion/next/commit/66f463a)) +* **Drawer:** add new component Drawer ([2ca6f17](https://github.com/alibaba-fusion/next/commit/2ca6f17)) +* **Form:** force set labelAlign=top while device=phone ([f07260f](https://github.com/alibaba-fusion/next/commit/f07260f)) +* **NumberPicker:** force set type=inline while device=phone ([0666869](https://github.com/alibaba-fusion/next/commit/0666869)) +* **Pagination:** support device ([52054e1](https://github.com/alibaba-fusion/next/commit/52054e1)) +* **Range:** add touch test case for Range ([6e2c85a](https://github.com/alibaba-fusion/next/commit/6e2c85a)) +* **Range:** range component support touch event ([ac03213](https://github.com/alibaba-fusion/next/commit/ac03213)) +* **Select:** add api popupComponent to custom Popup ([737f7e4](https://github.com/alibaba-fusion/next/commit/737f7e4)) +* **Tab:** support device touchable ([3c356bf](https://github.com/alibaba-fusion/next/commit/3c356bf)) +* **TimePicker:** add renderTimeMenuItems prop ([8c368f1](https://github.com/alibaba-fusion/next/commit/8c368f1)) + + + + ## [1.16.6](https://github.com/alibaba-fusion/next/compare/1.16.5...1.16.6) (2019-08-01) diff --git a/LATESTLOG.md b/LATESTLOG.md index a9610b59f7..19406b2af1 100644 --- a/LATESTLOG.md +++ b/LATESTLOG.md @@ -1,10 +1,10 @@ # Latest Log -## [1.16.6](https://github.com/alibaba-fusion/next/compare/1.16.5...1.16.6) (2019-08-01) +## [1.17.6](https://github.com/alibaba-fusion/next/compare/1.17.5...1.17.6) (2019-08-15) ### Bug Fixes -* **Typescript:** fix menu-button & split-button ([0cfe939](https://github.com/alibaba-fusion/next/commit/0cfe939)) +* **Menu:** value in selectedKeys doesn't exit in k2n ([fef80cd](https://github.com/alibaba-fusion/next/commit/fef80cd)) diff --git a/docs/adaptor.js b/docs/adaptor.js new file mode 100644 index 0000000000..4ac0c77350 --- /dev/null +++ b/docs/adaptor.js @@ -0,0 +1,83 @@ +import Badge from './badge/adaptor'; +import Balloon from './balloon/adaptor'; +import Breadcrumb from './breadcrumb/adaptor'; +import Button from './button/adaptor'; +import Calendar from './calendar/adaptor'; +import Card from './card/adaptor'; +import Cascader from './cascader/adaptor'; +import CascaderSelect from './cascader-select/adaptor'; +import Checkbox from './checkbox/adaptor'; +import Collapse from './collapse/adaptor'; +import DatePicker from './date-picker/adaptor'; +import Dialog from './dialog/adaptor'; +import Input from './input/adaptor'; +import Loading from './loading/adaptor'; +import Menu from './menu/adaptor'; +import MenuButton from './menu-button/adaptor'; +import Message from './message/adaptor'; +import Nav from './nav/adaptor'; +import NumberPicker from './number-picker/adaptor'; +import Pagination from './pagination/adaptor'; +import Paragraph from './paragraph/adaptor'; +import Progress from './progress/adaptor'; +import Radio from './radio/adaptor'; +import Range from './range/adaptor'; +import Rating from './rating/adaptor'; +import Search from './search/adaptor'; +import Select from './select/adaptor'; +import Slider from './slider/adaptor'; +import SplitButton from './split-button/adaptor'; +import Step from './step/adaptor'; +import Switch from './switch/adaptor'; +import Tab from './tab/adaptor'; +import Table from './table/adaptor'; +import Tag from './tag/adaptor'; +import TimePicker from './time-picker/adaptor'; +import Timeline from './timeline/adaptor'; +import Transfer from './transfer/adaptor'; +import Tree from './tree/adaptor'; +import TreeSelect from './tree-select/adaptor'; +import Upload from './upload/adaptor'; + +module.exports = { + Badge, + Balloon, + Breadcrumb, + Button, + Calendar, + Card, + Cascader, + CascaderSelect, + Checkbox, + Collapse, + DatePicker, + Dialog, + Input, + Loading, + Menu, + MenuButton, + Message, + Nav, + NumberPicker, + Pagination, + Paragraph, + Progress, + Radio, + Range, + Rating, + Search, + Select, + Slider, + SplitButton, + Step, + Switch, + Tab, + Table, + Tag, + TimePicker, + Timeline, + Transfer, + Tree, + TreeSelect, + Upload, +}; diff --git a/docs/affix/index.md b/docs/affix/index.md index 4daaf0380d..2dab72af7f 100644 --- a/docs/affix/index.md +++ b/docs/affix/index.md @@ -17,10 +17,10 @@ ### Affix -| 参数 | 说明 | 类型 | 默认值 | -| ------------ | ------------------------------------------------------------------------------------------------------------------- | -------- | ------------ | -| container | 设置 Affix 需要监听滚动事件的容器元素

**签名**:
Function() => ReactElement
**返回值**:
{ReactElement} 目标容器元素的实例
| Function | () => window | -| offsetTop | 距离窗口顶部达到指定偏移量后触发 | Number | - | -| offsetBottom | 距离窗口底部达到制定偏移量后触发 | Number | - | -| onAffix | 当元素的样式发生固钉样式变化时触发的回调函数

**签名**:
Function(元素是否被固钉: Boolean) => void
**参数**:
_元素是否被固钉_: {Boolean} null | Function | func.noop | -| useAbsolute | 是否启用绝对布局实现 affix | Boolean | - | +| 参数 | 说明 | 类型 | 默认值 | +| ------------ | ---------------------------------------------------------------------------------------------------------------------- | -------- | ------------ | +| container | 设置 Affix 需要监听滚动事件的容器元素

**签名**:
Function() => ReactElement
**返回值**:
{ReactElement} 目标容器元素的实例
| Function | () => window | +| offsetTop | 距离窗口顶部达到指定偏移量后触发 | Number | - | +| offsetBottom | 距离窗口底部达到制定偏移量后触发 | Number | - | +| onAffix | 当元素的样式发生固钉样式变化时触发的回调函数

**签名**:
Function(affixed: Boolean) => void
**参数**:
_affixed_: {Boolean} 元素是否被固钉 | Function | func.noop | +| useAbsolute | 是否启用绝对布局实现 affix | Boolean | - | diff --git a/docs/animate/index.md b/docs/animate/index.md index 0889fe7ae5..1e126572e7 100644 --- a/docs/animate/index.md +++ b/docs/animate/index.md @@ -23,7 +23,7 @@ | component | 包裹子元素的标签 | any | 'div' | | singleMode | 是否只有单个子元素,如果有多个子元素,请设置为 false | Boolean | true | | children | 子元素 | ReactElement/Array<ReactElement> | - | -| beforeAppear | 执行第一次挂载动画前触发的回调函数

**签名**:
Function() => void | Function | () => {} | +| beforeAppear | 执行第一次挂载动画前触发的回调函数

**签名**:
Function(node: HTMLElement) => void
**参数**:
_node_: {HTMLElement} 执行动画的 dom 元素 | Function | () => {} | | onAppear | 执行第一次挂载动画,添加 xxx-appear-active 类名后触发的回调函数

**签名**:
Function(node: HTMLElement) => void
**参数**:
_node_: {HTMLElement} 执行动画的 dom 元素 | Function | () => {} | | afterAppear | 执行完第一次挂载动画后触发的函数

**签名**:
Function(node: HTMLElement) => void
**参数**:
_node_: {HTMLElement} 执行动画的 dom 元素 | Function | () => {} | | beforeEnter | 执行进场动画前触发的回调函数

**签名**:
Function(node: HTMLElement) => void
**参数**:
_node_: {HTMLElement} 执行动画的 dom 元素 | Function | () => {} | @@ -42,6 +42,10 @@ | fadeInLeft | fadeOutLeft | | fadeInRight | fadeOutRight | | fadeInUp | fadeOutUp | +| slideInDown | slideOutUp | +| slideInLeft | slideOutLeft | +| slideInRight | slideOutRight | +| slideInUp | slideOutDown | | zoomIn | zoomOut | | expandInDown | expandOutUp | | expandInUp | expandOutDown | diff --git a/docs/badge/adaptor/index.jsx b/docs/badge/adaptor/index.jsx new file mode 100644 index 0000000000..6f86dd36a8 --- /dev/null +++ b/docs/badge/adaptor/index.jsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { Badge } from '@alifd/next'; +import { Types } from '@alifd/adaptor-helper'; + +export default { + name: 'Badge', + editor: () => ({ + props: [{ + name: 'level', + label: 'Type', + type: Types.enum, + options: ['dot', 'number'], + default: 'dot' + }, { + name: 'count', + type: Types.number, + default: 12, + }] + }), + adaptor: ({ level, count, ...others }) => { + return ; + }, + content: () => ({ + options: [{ + name: 'use', + options: ['independent', 'withOthers'], + default: 'withOthers' + }], + transform: (props, { use }) => { + if (use === 'withOthers') { + return { + ...props, + children: { + adaptor: 'div', + props: { + style: { + width: '42px', + height: '42px', + borderRadius: '50%', + background: '#eee', + display: 'inline-block' + } + } + } + }; + } + return props; + } + }) +}; diff --git a/docs/balloon/adaptor/index.jsx b/docs/balloon/adaptor/index.jsx new file mode 100644 index 0000000000..2d806f4146 --- /dev/null +++ b/docs/balloon/adaptor/index.jsx @@ -0,0 +1,87 @@ +import React from 'react'; +import { Balloon } from '@alifd/next'; +import { Types } from '@alifd/adaptor-helper'; + +const ALIGN_LIST = [ + { label: 'Top', value: 'b' }, // (上) + { label: 'Right', value: 'l' }, // (右) + { label: 'Bottom', value: 't' }, // (下) + { label: 'Left', value: 'r' }, // (左) + { label: 'Top Left', value: 'br' }, // (上左) + { label: 'Top Right', value: 'bl' }, // (上右) + { label: 'Bottom Left', value: 'tr' }, // (下左) + { label: 'Bottom Right', value: 'tl' }, // (下右) + { label: 'Left Top', value: 'rt' }, // (左上) + { label: 'Left Bottom', value: 'rb' }, // (左下) + { label: 'Right Top', value: 'lt' }, // (右上) + { label: 'Right Bottom', value: 'lb' }, // (右下 及其 两两组合) +]; + + +export default { + name: 'Balloon', + shape: [{ + label: 'Balloon', + value: 'balloon' + }, { + label: 'Tooltip', + value: 'tooltip' + }], + editor: (shape) => { + return { + props: [ + shape === 'balloon' && { + name: 'level', + type: Types.enum, + options: ['normal', 'primary'], + default: 'normal', + }, + { + name: 'direction', + label: 'Align', + type: Types.enum, + options: ALIGN_LIST, + default: 'b', + }, + shape === 'balloon' ? + { + name: 'closable', + type: Types.bool, + default: true + } : + null + ].filter(v => !!v), + data: { + default: `${shape.substring(0, 1).toUpperCase() + shape.substring(1)} content replace holder.` + } + }; + }, + adaptor: ({ shape, level, direction, closable, data, style, ...others }) => { + return ( + + {data} + + ); + }, + content: (shape) => ({ + options: [ + { + name: 'direction', + options: ALIGN_LIST, + default: 'b' + }, + shape === 'balloon' && { + name: 'closable', + options: ['yes', 'no'], + default: 'yes' + } + ].filter(v => !!v), + transform: (props, { direction, closable }) => { + return { + ...props, + direction, + closable: closable === 'yes', + } + } + }) +}; diff --git a/docs/breadcrumb/adaptor/index.jsx b/docs/breadcrumb/adaptor/index.jsx new file mode 100644 index 0000000000..1f5478ab02 --- /dev/null +++ b/docs/breadcrumb/adaptor/index.jsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Breadcrumb } from '@alifd/next'; +import { Types, parseData } from '@alifd/adaptor-helper'; + +export default { + name: 'Breadcrumb', + editor: () => ({ + props: [{ + name: 'ellipsis', + type: Types.bool, + default: false + }], + data: { + icon: true, + default: 'Home\nAll Categories\nWomen\'s Clothing\nBlouses & Shirts 78,999 T-shirts' + } + }), + adaptor: ({ ellipsis, data, ...others }) => { + const props = ellipsis ? { maxNode: 3 } : {}; + const list = parseData(data).filter((it) => it.type === 'node'); + return ( + + { + list.map((item, index) => {item.value}) + } + + ); + }, + content: () => ({ + options: [{ + name: 'ellipsis', + options: ['yes', 'no'], + default: 'no' + }], + transform: (props, { ellipsis }) => { + return { + ...props, + ellipsis: ellipsis === 'yes' + }; + } + }) + +}; diff --git a/docs/breadcrumb/index.en-us.md b/docs/breadcrumb/index.en-us.md index dd31aecb9b..a5d835f438 100644 --- a/docs/breadcrumb/index.en-us.md +++ b/docs/breadcrumb/index.en-us.md @@ -20,7 +20,7 @@ It is used to inform the user of the current position and the position of the cu | Param | Description | Type | Default Value | | --------- | -------------------------- | --------- | ------------------------------ | | children | Children components, hsould be an Breadcrumb.Item | custom | - | -| maxNode | The maximum number of breadcrumbs is displayed and the excess is hidden | Number | 100 | +| maxNode | The maximum number of breadcrumbs is displayed and the excess is hidden, can set auto compute maximum number | Number | 100, 'auto' | | separator | Separator, can be text or Icon | ReactNode | <Icon type="arrow-right" /> | | component | Set Element type | String/Function | 'nav' | diff --git a/docs/breadcrumb/index.md b/docs/breadcrumb/index.md index b51c6382f5..1c0da8b0ba 100644 --- a/docs/breadcrumb/index.md +++ b/docs/breadcrumb/index.md @@ -18,12 +18,12 @@ ### Breadcrumb -| 参数 | 说明 | 类型 | 默认值 | -| --------- | -------------------------- | --------------- | ------------------------------ | -| children | 面包屑子节点,需传入 Breadcrumb.Item | custom | - | -| maxNode | 面包屑最多显示个数,超出部分会被隐藏 | Number | 100 | -| separator | 分隔符,可以是文本或 Icon | ReactNode | <Icon type="arrow-right" /> | -| component | 设置标签类型 | String/Function | 'nav' | +| 参数 | 说明 | 类型 | 默认值 | +| --------- | ------------------------------------------- | --------------- | ------------------------------ | +| children | 面包屑子节点,需传入 Breadcrumb.Item | custom | - | +| maxNode | 面包屑最多显示个数,超出部分会被隐藏, 设置为 auto 会自动根据父元素的宽度适配。 | Number/Enum | 100 | +| separator | 分隔符,可以是文本或 Icon | ReactNode | <Icon type="arrow-right" /> | +| component | 设置标签类型 | String/Function | 'nav' | ### Breadcrumb.Item diff --git a/docs/button/adaptor/index.jsx b/docs/button/adaptor/index.jsx new file mode 100644 index 0000000000..fa300952c1 --- /dev/null +++ b/docs/button/adaptor/index.jsx @@ -0,0 +1,133 @@ +import React from 'react'; +import { Types, ContentType, parseData, STATE_MARK } from '@alifd/adaptor-helper'; +import { Button, Icon } from '@alifd/next'; + +const createContent = (list = []) => { + if (!Array.isArray(list)) return list; + return list.map(({ type, value }, index) => { + if (type === ContentType.icon) { + return ; + } + return value; + }); +}; + +export default { + name: 'Button', + shape: ['normal', 'text', 'warning', 'ghost', 'group'], + editor: (shape) => { + return { + props: [{ + name: 'level', + type: Types.enum, + options: { + normal: ['normal', 'primary', 'secondary'], + text: ['normal', 'primary', 'secondary'], + warning: ['normal', 'primary'], + ghost: ['light', 'dark'], + group: ['normal', 'primary', 'secondary'], + }[shape], + }, { + name: 'size', + type: Types.enum, + options: ['large', 'medium', 'small'], + default: 'medium' + }], + data: { + icon: true, + ...(shape === 'group' ? {} : { + disable: true, + hover: true, + }), + default: shape === 'group' ? 'Button\nButton\nButton' : 'Button' + } + }; + }, + adaptor: ({ shape, level, size, data, ...others }) => { + const list = parseData(data, { parseContent: true }); + + const buttonProps = { + type: shape === 'ghost' ? 'normal' : level, + warning: shape === 'warning', + text: shape === 'text', + ghost: shape === 'ghost' ? level : false, + }; + + if (list.length === 1) { + const className = (others.className || ''); + return + } + + return ( + + { + list.map((item, index) => ) + } + + ); + }, + content(shape) { + if (shape === 'group') { + return { + options: [{ + name: 'iconType', + options: ['none', 'arrow', 'onlyIcon'], + default: 'none' + }], + transform: (props, { iconType }) => { + if (iconType === 'arrow') { + return { + ...props, + data: ['[arrow-left]Go Back', 'Button', 'Go Forward[arrow-right]'].join('\n'), + }; + } + + if (iconType === 'onlyIcon') { + return { + ...props, + data: ['[set]', '[atm]', '[download]'].join('\n'), + }; + } + + return props; + } + }; + } + + return { + options: [{ + name: 'iconType', + options: ['none', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'atm'], + default: 'none' + }], + transform: (props, { iconType }) => { + if (iconType === 'none') return props; + let { data } = props; + const icon = `[${iconType}]`; + + if (['arrow-right', 'arrow-down', 'arrow-up'].indexOf(iconType) !== -1) { + data = data + icon; + } else { + data = Object.keys(STATE_MARK).filter(v => !!v).indexOf(data.substring(0, 1)) !== -1 ? + [data.substring(0, 1), icon, data.substring(1)].join('') : icon + data; + } + + return { + ...props, + data + }; + } + }; + }, + demoOptions(demo) { + const { node } = demo; + const { level } = node.props; + if (level === 'dark') { + return { + ...demo, + background: '#000', + }; + } + return demo; + } +}; diff --git a/docs/calendar/adaptor/index.jsx b/docs/calendar/adaptor/index.jsx new file mode 100644 index 0000000000..3bb8a30b22 --- /dev/null +++ b/docs/calendar/adaptor/index.jsx @@ -0,0 +1,72 @@ +import React from 'react'; +import moment from 'moment'; +import { Calendar } from '@alifd/next'; +import { Types } from '@alifd/adaptor-helper'; + +const now = new Date(); + +export default { + name: 'Calendar', + shape: ['fullscreen', 'card', 'panel', 'rangePanel'], + editor: (shape) => { + return { + props: [{ + name: 'level', + label: 'Type', + type: Types.enum, + options: ['day', 'month', 'year'].filter((level) => { + if (level === 'year') return shape === 'panel'; + if (shape === 'rangePanel') return level === 'day'; + + return true; + }), + default: 'day' + }, { + name: 'width', + type: Types.number, + default: shape === 'fullscreen' ? 600 : shape === 'rangePanel' ? 600 : 320, + }, { + name: 'date', + type: Types.string, + default: `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}` + }] + }; + }, + adaptor: ({ shape, level, width, date = '', style = {}, ...others }) => { + const arr = date.split('-').map(number => Number(number) || 0); + + const d = moment(); + arr.forEach((number, index) => { + if (!number) return; + switch (index) { + case 0: + d.year(number); + break; + case 1: + d.month(number - 1); + break; + case 2: + d.date(number); + break; + default: return; + } + }); + + if (shape === 'rangePanel') { + if (!Calendar.RangeCalendar) return null; + + return ( + + ); + } + return ( + + ); + } +}; diff --git a/docs/calendar/index.md b/docs/calendar/index.md index b09b2a88fa..fdcbd34de8 100644 --- a/docs/calendar/index.md +++ b/docs/calendar/index.md @@ -34,7 +34,7 @@ moment.locale('zh-cn'); | defaultValue | 默认选中的日期(moment 对象) | custom | - | | shape | 展现形态

**可选值**:
'card', 'fullscreen', 'panel' | Enum | 'fullscreen' | | value | 选中的日期值 (moment 对象) | custom | - | -| mode | 面板模式

**可选值**:
'date', 'month', 'year' | Enum | - | +| mode | 面板模式 | Enum | - | | showOtherMonth | 是否展示非本月的日期 | Boolean | true | | defaultVisibleMonth | 默认展示的月份

**签名**:
Function() => void | Function | - | | onSelect | 选择日期单元格时的回调

**签名**:
Function(value: Object) => void
**参数**:
_value_: {Object} 对应的日期值 (moment 对象) | Function | func.noop | diff --git a/docs/card/adaptor/index.jsx b/docs/card/adaptor/index.jsx new file mode 100644 index 0000000000..63c13709b6 --- /dev/null +++ b/docs/card/adaptor/index.jsx @@ -0,0 +1,93 @@ +import React from 'react'; +import { Card } from '@alifd/next'; +import { Types } from '@alifd/adaptor-helper'; + +let index = 0; +export default { + name: 'Card', + editor: () => ({ + props: [{ + name: 'bullet', + label: 'Label', + type: Types.bool, + default: false, + }, { + name: 'divider', + type: Types.bool, + default: true, + }, { + name: 'expand', + label: 'Expanded', + type: Types.bool, + default: false + }, { + name: 'width', + type: Types.number, + default: 300, + }, { + name: 'height', + label: 'Content Height', + type: Types.number, + default: 215, + }, { + name: 'title', + type: Types.string, + default: 'Title' + }, { + name: 'subTitle', + label: 'Subtitle', + type: Types.string, + default: '', + }, { + name: 'extra', + label: 'Extra Data', + type: Types.string, + default: '', + }], + }), + adaptor: ({ bullet, divider, expand, width, height, title, subTitle, extra, style, ...others }) => { + const cardStyle = { + width: width === 0 ? '' : width, + ...style, + }; + + return ( + +
+
+ ); + }, + content: () => ({ + options: [{ + name: 'bullet', + options: ['show', 'hide'], + default: 'hide' + }, { + name: 'divider', + options: ['show', 'hide'], + default: 'show' + }, { + name: 'expanded', + options: ['yes', 'no'], + default: 'no' + }, { + name: 'subTitle', + options: ['show', 'hide'], + default: 'hide' + }, { + name: 'link', + options: ['show', 'hide'], + default: 'hide' + }], + transform: (props, { bullet, divider, expanded, subTitle, link }) => { + return { + ...props, + bullet: bullet === 'show', + divider: divider === 'show', + expand: expanded === 'yes', + subTitle: subTitle === 'show' ? 'Sub Title' : '', + extra: link === 'show' ? 'Link' : '', + }; + } + }) +}; diff --git a/docs/cascader-select/adaptor/index.jsx b/docs/cascader-select/adaptor/index.jsx new file mode 100644 index 0000000000..5008b9103f --- /dev/null +++ b/docs/cascader-select/adaptor/index.jsx @@ -0,0 +1,106 @@ +import React from 'react'; +import { Types, parseData, NodeType } from '@alifd/adaptor-helper'; +import { CascaderSelect } from '@alifd/next'; + +let index = 1000; +const createDataSource = (list, map = {}) => { + if (!list) return []; + return list.filter((item) => item.type === NodeType.node).map(({ value, children, state }) => { + const key = String(index++); + if (state === 'active') { + if (!children || children.length === 0) { + map.selecteds.push(key); + } else { + map.expandeds.push(key); + } + } + + return { + value: key, + label: value, + disabled: state === 'disabled', + children: createDataSource(children, map), + }; + }); +}; +export default { + name: 'CascaderSelect', + editor: () => ({ + props: [{ + name: 'size', + type: Types.enum, + options: ['large', 'medium', 'small'], + default: 'medium' + }, { + name: 'state', + label: 'Status', + type: Types.enum, + options: ['normal', 'expanded', 'disabled'], + default: 'normal' + }, { + name: 'width', + type: Types.number, + default: 300, + }, { + name: 'border', + type: Types.bool, + default: true, + }, { + name: 'checkbox', + type: Types.bool, + default: false + }, { + name: 'label', + type: Types.string, + default: '' + }], + data: { + active: true, + disabled: true, + icon: true, + default: '*1\n\t*1-1\n\t\t1-1-1\n\t\t1-1-2\n\t\t1-1-3\n\t\t1-1-4\n\t\t*1-1-5\n\t1-2\n\t1-3\n\t1-4\n\t1-5\n2\n\t2-1\n\t2-2\n\t2-3\n\t2-4\n\t2-5\n3\n\t3-1\n\t3-2\n\t3-3\n\t3-4\n\t3-5\n4\n\t4-1\n\t4-2\n\t4-3\n\t4-4\n\t4-5\n5\n\t5-1\n\t5-2\n\t5-3\n\t5-4\n\t5-5' + } + }), + adaptor: ({ shape, size, state, width, border, checkbox, label, data, style = {}, ...others}) => { + const list = parseData(data); + const map = { selecteds: [], expandeds: [] }; + const dataSource = createDataSource(list, map); + const value = map.selecteds; + + return ( + node} hasBorder={border} size={size} multiple={checkbox} value={value} visible={state === 'expanded'} disabled={state=== 'disabled'} dataSource={dataSource}/> + ); + }, + content: () => ({ + options: [{ + name: 'checkbox', + options: ['yes', 'no'], + default: 'no' + }, { + name: 'border', + options: ['show', 'hide'], + default: 'show' + }, { + name: 'label', + options: ['yes', 'no'], + default: 'no' + }], + transform: (props, { checkbox, border, label }) => { + return { + ...props, + checkbox: checkbox === 'yes', + border: border === 'show', + label: label === 'yes' ? 'Label' : '' + }; + } + }), + demoOptions: (demo) => { + const { node } = demo; + const { props = {} } = node; + if (props.state === 'expanded') { + return { ...demo, height: 300 }; + } + + return demo; + } +}; diff --git a/docs/cascader/adaptor/index.jsx b/docs/cascader/adaptor/index.jsx new file mode 100644 index 0000000000..8a09f1fa53 --- /dev/null +++ b/docs/cascader/adaptor/index.jsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { Types, NodeType, parseData } from '@alifd/adaptor-helper'; +import { Cascader, Icon } from '@alifd/next'; + +let index = 1000; +const createDataSource = (list, map = {}) => { + if (!list) return []; + return list.filter((item) => item.type === NodeType.node).map(({ value, children, state }) => { + const key = String(index++); + if (state === 'active') { + if (!children || children.length === 0) { + map.selecteds.push(key); + } else { + map.expandeds.push(key); + } + } + + return { + value: key, + label: value, + disabled: state === 'disabled', + children: createDataSource(children, map), + }; + }); +} +export default { + name: 'Cascader', + editor: () => ({ + props: [{ + name: 'checkbox', + type: Types.bool, + default: false + }, { + name: 'width', + type: Types.number, + default: 120 + }], + data: { + active: true, + disable: true, + icon: true, + default: 'Option1\n\tOption1-1\n\tOption1-2\n\tOption1-3\n\tOption1-4\n\tOption1-5\n\tOption1-6\nOption2\n\tOption2-1\n\tOption2-2\n\tOption2-3\n\tOption2-4\n\tOption2-5\n\tOption2-6\nOption3\n\tOption3-1\n\tOption3-2\n\tOption3-3\n\tOption3-4\n\tOption3-5\n\tOption3-6\nOption4\n\tOption4-1\n\tOption4-2\n\tOption4-3\n\tOption4-4\n\tOption4-5\n\tOption4-6\nOption5\n\tOption5-1\n\tOption5-2\n\tOption5-3\n\tOption5-4\n\tOption5-5\n\tOption5-6\nOption6\n\tOption6-1\n\tOption6-2\n\tOption6-3\n\tOption6-4\n\tOption6-5\n\tOption6-6', + } + }), + adaptor: ({ checkbox, width, data, ...others }) => { + const list = parseData(data); + const map = { selecteds: [], expandeds: [] }; + const dataSource = createDataSource(list, map); + const value = map.selecteds; + const itemRender = ({ label = '' }) => { + return label.replace(/(\[.*?\])/g, '\n$1\n').split('\n').filter(v=> !!v) + .map((d, i) => { + let icon; + switch (true) { + case /^\[(.*)\]$/.test(d): + icon = RegExp.$1; + if (!icon) return ''; + return ; + default: + return d; + } + }); + }; + + return ; + } +}; diff --git a/docs/cascader/index.md b/docs/cascader/index.md index ef462f1c7d..940adf237c 100644 --- a/docs/cascader/index.md +++ b/docs/cascader/index.md @@ -38,7 +38,7 @@ | listStyle | 每列列表样式对象 | Object | - | | listClassName | 每列列表类名 | String | - | | itemRender | 每列列表项渲染函数

**签名**:
Function(data: Object) => ReactNode
**参数**:
_data_: {Object} 数据
**返回值**:
{ReactNode} 列表项内容
| Function | item => item.label | -| loadData | 异步加载数据函数

**签名**:
Function(data: Object, source: Object) => void
**参数**:
_data_: {Object} 当前点击异步加载的数据
_source_: {Object} 当前点击数据 | Function | - | +| loadData | 异步加载数据函数

**签名**:
Function(data: Object, source: Object) => void
**参数**:
_data_: {Object} 当前点击异步加载的数据
_source_: {Object} 当前点击数据,source是原始对象 | Function | - | diff --git a/docs/checkbox/adaptor/index.jsx b/docs/checkbox/adaptor/index.jsx new file mode 100644 index 0000000000..724b8c869f --- /dev/null +++ b/docs/checkbox/adaptor/index.jsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Checkbox } from '@alifd/next'; +import { Types } from '@alifd/adaptor-helper'; + +export default { + name: 'Checkbox', + editor: () => ({ + props: [{ + name: 'state', + label: 'Status', + type: Types.enum, + options: ['normal', 'hover', 'disabled', 'indeterminate', 'indeterminateHover', 'indeterminateDisabled', 'checked', 'checkedHover', 'checkedDisabled'], + default: 'normal' + }, { + name: 'label', + type: Types.string, + default: 'pear' + }] + }), + adaptor: ({ state = '', label, className = '', ...others }) => { + const isHovered = ['hover', 'indeterminateHover', 'checkedHover'].indexOf(state) !== -1; + const isChecked = state.indexOf('checked') !== -1; + const isDisabled = ['disabled', 'indeterminateDisabled', 'checkedDisabled'].indexOf(state) !== -1; + const isIndeterminated = state.indexOf('indeterminate') !== -1; + + return ( + {label} + ); + }, + content: () => ({ + options: [{ + name: 'label', + options: ['show', 'hide'], + default: 'show' + }], + transform: (props, { label }) => { + return { + ...props, + label: label === 'show' ? props.label : '' + }; + }, + }) +}; diff --git a/docs/checkbox/index.md b/docs/checkbox/index.md index e513b15b17..78ccfb5e78 100644 --- a/docs/checkbox/index.md +++ b/docs/checkbox/index.md @@ -19,30 +19,32 @@ ### Checkbox -| 参数 | 说明 | 类型 | 默认值 | -| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------- | --------- | -| id | checkbox id, 挂载在input上 | String | - | -| checked | 选中状态 | Boolean | - | -| defaultChecked | 默认选中状态 | Boolean | false | -| disabled | 禁用 | Boolean | - | -| label | 通过属性配置label, | ReactNode | - | -| indeterminate | Checkbox 的中间状态,只会影响到 Checkbox 的样式,并不影响其 checked 属性 | Boolean | - | -| defaultIndeterminate | Checkbox 的默认中间态,只会影响到 Checkbox 的样式,并不影响其 checked 属性 | Boolean | false | -| onChange | 状态变化时触发的事件

**签名**:
Function(checked: Boolean, e: Event) => void
**参数**:
_checked_: {Boolean} 是否选中
_e_: {Event} Dom 事件对象 | Function | func.noop | -| onMouseEnter | 鼠标进入enter事件

**签名**:
Function(e: Event) => void
**参数**:
_e_: {Event} Dom 事件对象 | Function | func.noop | -| onMouseLeave | 鼠标离开Leave事件

**签名**:
Function(e: Event) => void
**参数**:
_e_: {Event} Dom 事件对象 | Function | func.noop | +| 参数 | 说明 | 类型 | 默认值 | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | --------- | +| id | checkbox id, 挂载在input上 | String | - | +| checked | 选中状态 | Boolean | - | +| defaultChecked | 默认选中状态 | Boolean | false | +| disabled | 禁用 | Boolean | - | +| label | 通过属性配置label, | ReactNode | - | +| indeterminate | Checkbox 的中间状态,只会影响到 Checkbox 的样式,并不影响其 checked 属性 | Boolean | - | +| defaultIndeterminate | Checkbox 的默认中间态,只会影响到 Checkbox 的样式,并不影响其 checked 属性 | Boolean | false | +| onChange | 状态变化时触发的事件

**签名**:
Function(checked: Boolean, e: Event) => void
**参数**:
_checked_: {Boolean} 是否选中
_e_: {Event} Dom 事件对象 | Function | func.noop | +| onMouseEnter | 鼠标进入enter事件

**签名**:
Function(e: Event) => void
**参数**:
_e_: {Event} Dom 事件对象 | Function | func.noop | +| onMouseLeave | 鼠标离开Leave事件

**签名**:
Function(e: Event) => void
**参数**:
_e_: {Event} Dom 事件对象 | Function | func.noop | +| value | checkbox 的value | String/Number | - | +| name | name | String | - | ### Checkbox.Group -| 参数 | 说明 | 类型 | 默认值 | -| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | -------- | -| disabled | 整体禁用 | Boolean | - | -| dataSource | 可选项列表, 数据项可为 String 或者 Object, 如 `['apple', 'pear', 'orange']` 或者 `[{value: 'apple', label: '苹果',}, {value: 'pear', label: '梨'}, {value: 'orange', label: '橙子'}]` | Array<any> | \[] | -| value | 被选中的值列表 | Array/String/Number | - | -| defaultValue | 默认被选中的值列表 | Array/String/Number | - | -| children | 通过子元素方式设置内部 checkbox | Array<ReactElement> | - | -| onChange | 选中值改变时的事件

**签名**:
Function(value: Array, e: Event) => void
**参数**:
_value_: {Array} 选中项列表
_e_: {Event} Dom 事件对象 | Function | () => {} | -| itemDirection | 子项目的排列方式
- hoz: 水平排列 (default)
- ver: 垂直排列

**可选值**:
'hoz', 'ver' | Enum | 'hoz' | +| 参数 | 说明 | 类型 | 默认值 | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | -------- | +| disabled | 整体禁用 | Boolean | - | +| dataSource | 可选项列表, 数据项可为 String 或者 Object, 如 `['apple', 'pear', 'orange']` 或者 `[{value: 'apple', label: '苹果',}, {value: 'pear', label: '梨'}, {value: 'orange', label: '橙子'}]` | Array<String>/Array<Object> | \[] | +| value | 被选中的值列表 | Array/String/Number | - | +| defaultValue | 默认被选中的值列表 | Array/String/Number | - | +| children | 通过子元素方式设置内部 checkbox | Array<ReactElement> | - | +| onChange | 选中值改变时的事件

**签名**:
Function(value: Array, e: Event) => void
**参数**:
_value_: {Array} 选中项列表
_e_: {Event} Dom 事件对象 | Function | () => {} | +| itemDirection | 子项目的排列方式
- hoz: 水平排列 (default)
- ver: 垂直排列

**可选值**:
'hoz', 'ver' | Enum | 'hoz' | ## ARIA and KeyBoard diff --git a/docs/collapse/adaptor/index.jsx b/docs/collapse/adaptor/index.jsx new file mode 100644 index 0000000000..e08d5473db --- /dev/null +++ b/docs/collapse/adaptor/index.jsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { Collapse } from '@alifd/next'; +import { Types, parseData, NodeType } from '@alifd/adaptor-helper'; + +export default { + name: 'Collapse', + editor: () => ({ + props: [{ + name: 'state', + label: 'Status', + type: Types.enum, + options: ['normal', 'disabled'], + default: 'normal' + }, { + name: 'width', + type: Types.number, + default: 400 + }], + data: { + active: true, + disable: true, + default: '*Panel Header 1\n\tPeople always make mistakes, frustrated, nerve-racking, but cannot remain stagnant.\nPanel Header 2\n\tPeople always make mistakes, frustrated, nerve-racking, but cannot remain stagnant.\nPanel Header 3\n\tPeople always make mistakes, frustrated, nerve-racking, but cannot remain stagnant.\n' + } + }), + adaptor: ({ state, width, data, style = {}, ...others }) => { + const list = parseData(data).filter(node => NodeType.node === node.type); + let expandedKeys = []; + const children = list.map(({ state, value, children }, index) => { + if (state === 'active') { + expandedKeys.push(`panel_${index}`); + } + + return ( + + {children && children.length > 0 ? children[0].value : ''} + + ); + }); + return ( + + { + children + } + + ); + } +}; diff --git a/docs/config-provider/index.en-us.md b/docs/config-provider/index.en-us.md index f346185d96..7d71a21d66 100644 --- a/docs/config-provider/index.en-us.md +++ b/docs/config-provider/index.en-us.md @@ -151,6 +151,7 @@ export default config(Component); | -------- | ----------------------------------- | ------------ | --- | | errorBoundary | turn errorBoundary on or not
If you pass object, properties:

fallbackUI `Function(error?: {}, errorInfo?: {}) => Element`
afterCatch `Function(error?: {}, errorInfo?: {})` after being catched, e.g. send data to server for data statistics | Boolean/Object | false | | pure | whether enable the Pure Render mode, it will improve performance, but it will also have side effects | Boolean | - | +| device | Responsive of device
Options:
`desktop`, `tablet`, `phone` | - | | warning | whether to display the warning prompt for component properties being deprecated in development mode | Boolean | true | | children | component tree | ReactElement | - | diff --git a/docs/config-provider/index.md b/docs/config-provider/index.md index f87b75fee2..7c91e9572c 100644 --- a/docs/config-provider/index.md +++ b/docs/config-provider/index.md @@ -220,6 +220,7 @@ export default config(Component); | pure | 是否开启 Pure Render 模式,会提高性能,但是也会带来副作用 | Boolean | - | | warning | 是否在开发模式下显示组件属性被废弃的 warning 提示 | Boolean | true | | rtl | 是否开启 rtl 模式 | Boolean | - | +| device | 设备类型,针对不同的设备类型组件做出对应的响应式变化

**可选值**:
'tablet', 'desktop', 'phone' | Enum | - | | children | 组件树 | ReactElement | - | diff --git a/docs/date-picker/adaptor/index.jsx b/docs/date-picker/adaptor/index.jsx new file mode 100644 index 0000000000..7cb96bbf8c --- /dev/null +++ b/docs/date-picker/adaptor/index.jsx @@ -0,0 +1,122 @@ +import React from 'react'; +import { DatePicker } from '@alifd/next'; +import { Types } from '@alifd/adaptor-helper'; +import moment from 'moment'; + +export default { + name: 'DatePicker', + shape: ['normal', { + value: 'range', + label: 'Range Picker' + }], + editor: (shape) => { + return { + props: [{ + name: 'state', + label: 'Status', + type: Types.enum, + options: ['normal', 'expanded', 'selected', 'disabled'], + default: 'normal' + }, { + name: 'size', + type: Types.enum, + options: ['large', 'medium', 'small'], + default: 'medium' + }, { + name: 'type', + type: Types.enum, + options: shape === 'range' ? ['dateRange', 'dateTimeRange'] : ['date', 'dateTime'], + default: shape === 'range' ? 'dateRange' : 'date' + }, { + name: 'width', + type: Types.number, + default: 288 + }, { + name: 'label', + type: Types.string, + default: '' + }, { + name: 'placeholder', + type: Types.string, + default: shape === 'range' ? 'Start Date - End Date' : 'Please Select Date' + }] + }; + }, + adaptor: ({ shape, state, size, type, width, label, placeholder = '', style = {}, ...others }) => { + const now = moment(); + style = { + minWidth: width, + ...style, + } + + if (shape === 'range') { + return ( + node} + style={style} + size={size} + showTime={'dateTimeRange' === type} + locale={{ + startPlaceholder: placeholder.split('-')[0], + endPlaceholder: placeholder.split('-')[1] + }} + label={label} + value={state !== 'normal' ? [now.clone(), now.clone().add(1, 'month')] : null} + /> + ); + } + + return ( + node} + style={style} + size={size} + showTime={'dateTime' === type} + placeholder={placeholder} + label={label} + value={state !== 'normal' ? now.clone() : null} + /> + ); + }, + content: (shape) => ({ + options: [{ + name: 'type', + options: shape === 'range' ? ['dateRange', 'dateTimeRange'] : ['date', 'dateTime'], + default: shape === 'range' ? 'dateRange' : 'date' + }, { + name: 'label', + options: ['show', 'hide'], + default: 'hide' + }], + transform: (props, { type , label }) => { + return { + ...props, + type, + label: label === 'show' ? 'Label' : '' + }; + } + }), + demoOptions: (demo) => { + const { node } = demo; + const { props = {} } = node; + if (props.state === 'expanded') { + return { ...demo, height: 300 }; + } + + return demo; + } +}; diff --git a/docs/date-picker/demo/show-time.md b/docs/date-picker/demo/show-time.md index 8df1fb1859..d209ed5da4 100644 --- a/docs/date-picker/demo/show-time.md +++ b/docs/date-picker/demo/show-time.md @@ -25,6 +25,7 @@ const onOk = (value) => console.log('onOK:', value.format('YYYY-MM-DD HH:mm:ss') const onRangeOk = (value) => console.log('onOk: [%s, %s]', value[0].format('YYYY-MM-DD HH:mm:ss'), value[1].format('YYYY-MM-DD HH:mm:ss')); const defaultTimeValue = moment('09:00:00', 'HH:mm:ss', true); +const defaultTimeValues = [moment('09:00:00', 'HH:mm:ss', true), moment('23:59:59', 'HH:mm:ss', true)]; ReactDOM.render(

DatePicker With Time

@@ -39,5 +40,7 @@ ReactDOM.render(

RangePicker with Time, with default time value, hide seconds

+

RangePicker with Time, with default start & end time value, hide seconds

+
, mountNode); ```` diff --git a/docs/dialog/adaptor/index.jsx b/docs/dialog/adaptor/index.jsx new file mode 100644 index 0000000000..bce05c90b5 --- /dev/null +++ b/docs/dialog/adaptor/index.jsx @@ -0,0 +1,141 @@ +import React from 'react'; +import { Types } from '@alifd/adaptor-helper'; +import { Dialog, Message } from '@alifd/next'; +import locale from '../../../src/locale/en-us'; + + +export default { + name: 'Dialog', + editor: () => ({ + props: [{ + name: 'level', + type: Types.enum, + options: ['normal', 'alert', 'confirm'], + default: 'normal' + }, { + name: 'footerAlign', + label: 'Button Position', + type: Types.enum, + options: ['left', 'right', 'center'], + default: 'right' + }, { + name: 'okButtonPosition', + label: 'Button Order', + type: Types.enum, + options: ['left', 'right'], + default: 'left' + }, { + name: 'mask', + type: Types.bool, + default: false + }, { + name: 'width', + type: Types.number, + default: 400 + }, { + name: 'height', + type: Types.number, + default: 160 + }, { + name: 'title', + type: Types.string, + default: 'Welcome to Alibaba.com' + }], + data: { + default: 'Start your business here by searching a popular product' + } + }), + adaptor: ({ level, footerAlign, okButtonPosition, mask, width, height, title, style, className, data, ...others}) => { + const dialogStyle = { + position: mask ? 'absolute' : 'relative', + width: width, + zIndex: 1, + ...(mask ? { + left: 20, + top: 20, + } : style), + }; + + const props = { + ...(mask ? {} : {...others }), + className: level === 'normal' ? className : `${className || ''} next-dialog-quick`, + style: dialogStyle, + footerAlign: footerAlign, + footerActions: okButtonPosition === 'left' ? ['ok', 'cancel'] : ['cancel', 'ok'], + locale: locale.Dialog, + height: `${height}px` + }; + + let dialog; + switch(level) { + case 'alert': + dialog = ( + + + {data} + + + ); + break; + case 'confirm': + dialog = ( + + + {data} + + + ); + break; + default: + dialog = {data} + break; + } + + return mask ? ( +
+
+ {dialog} +
+ ) : dialog; + }, + content: () => ({ + options: [{ + name: 'title', + options: ['show', 'hide'], + default: 'show', + }, { + name: 'overlay', + options: ['show', 'hide'], + default: 'hide', + }, { + name: 'footerAlign', + options: ['left', 'center', 'right'], + default: 'right' + }, { + name: 'okButtonPosition', + options: ['left', 'right'], + default: 'right' + }], + transform: (props, { title, overlay, footerAlign, okButtonPosition }) => { + return { + ...props, + title: title === 'hide' ? '' : title, + mask: overlay === 'show', + footerAlign, + okButtonPosition, + }; + } + }) +}; diff --git a/docs/drawer/demo/basic.md b/docs/drawer/demo/basic.md new file mode 100644 index 0000000000..c5cf4f7a2c --- /dev/null +++ b/docs/drawer/demo/basic.md @@ -0,0 +1,55 @@ +# 基本 + +- order: 0 + +第一个抽屉 + +:::lang=en-us +# Basic + +- order: 0 + +First drawer +::: +--- + +````jsx +import { Radio, Button, Drawer } from '@alifd/next'; + +class Demo extends React.Component { + state = { + visible: false, + }; + + onOpen = () => { + this.setState({ + visible: true + }); + }; + + onClose = (reason, e) => { + console.log(reason, e); + this.setState({ + visible: false + }); + } + + render() { + return ( +
+ + + Start your business here by searching a popular product + +
+ ); + } +} + +ReactDOM.render(, mountNode); +```` diff --git a/docs/drawer/demo/double.md b/docs/drawer/demo/double.md new file mode 100644 index 0000000000..461b0fb0f2 --- /dev/null +++ b/docs/drawer/demo/double.md @@ -0,0 +1,103 @@ +# 双层抽屉 + +- order: 2 + +双层抽屉,抽屉内打开新的抽屉 + +:::lang=en-us +# Basic + +- order: 2 + +Double drawer +::: +--- + +````jsx +import { Button, Drawer } from '@alifd/next'; + +class App extends React.Component { + state = { visible: false, childrenDrawer: false }; + + showDrawer = () => { + this.setState({ + visible: true, + }); + }; + + onClose = () => { + this.setState({ + visible: false, + }); + }; + + showChildrenDrawer = () => { + this.setState({ + childrenDrawer: true, + }); + }; + + onChildrenDrawerClose = () => { + this.setState({ + childrenDrawer: false, + }); + }; + + render() { + return ( +
+ + + + + This is two-level drawer + +
+ + +
+
+
+ ); + } +} + +ReactDOM.render(, mountNode); +```` diff --git a/docs/drawer/demo/placement.md b/docs/drawer/demo/placement.md new file mode 100644 index 0000000000..c9d5b70ace --- /dev/null +++ b/docs/drawer/demo/placement.md @@ -0,0 +1,66 @@ +# 自定义弹出方向 + +- order: 1 + +自定义弹出方向 + +:::lang=en-us +# Basic + +- order: 1 + +Diffrent placement +::: +--- + +````jsx +import { Radio, Button, Drawer } from '@alifd/next'; + +class Demo extends React.Component { + state = { + visible: false, + placement: 'right' + }; + + onOpen = () => { + this.setState({ + visible: true + }); + }; + + onClose = (reason) => { + + this.setState({ + visible: false + }); + }; + + onPlacementChange = dir => { + this.setState({ + placement: dir + }); + } + + render() { + return ( +
+      + + + Start your business here by searching a popular product + +
+ ); + } +} + +ReactDOM.render(, mountNode); +```` diff --git a/docs/drawer/demo/select.md b/docs/drawer/demo/select.md new file mode 100644 index 0000000000..3012da3681 --- /dev/null +++ b/docs/drawer/demo/select.md @@ -0,0 +1,68 @@ +# 抽屉式选择 + +- order: 1 + +将 Select 的弹出模式换成 Drawer + +:::lang=en-us +# Drawer Select + +- order: 1 + +Select width drawer +::: +--- + +````jsx +import { Radio, Drawer, Select } from '@alifd/next'; + +const Option = Select.Option; + +const onChange = function (value) { + console.log(value); +}; +const onBlur = function (e) { + console.log(/onblur/,e); +}; + +const onToggleHighlightItem = function (item, type) { + console.log(item, type); +}; + +class Demo extends React.Component { + state = { + placement: 'right' + }; + + onPlacementChange = dir => { + this.setState({ + placement: dir + }); + } + + render() { + const drawerProps = { + placement: this.state.placement, + closeable: 'mask', + bodyStyle: {padding: 0} + }; + return ( +
+      + + +
+ ); + } +} + +ReactDOM.render(, mountNode); +```` diff --git a/docs/drawer/index.en-us.md b/docs/drawer/index.en-us.md new file mode 100644 index 0000000000..142fa0c67c --- /dev/null +++ b/docs/drawer/index.en-us.md @@ -0,0 +1,37 @@ +# Drawer + +- chinese: 抽屉 +- family: Feedback +- category: Components +- type: 弹层 + +--- + +## Guide + +## When To Use + +The Drawer is used to provide the user with an auxiliary window for quickly performing a simple operation, confirming user information, or providing a feedback prompt without leaving the main path. + +## API + +### Drawer + +| Param | Descripiton | Type | Default Value | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | --------------------------------------------------------------------------------- | +| trigger | trigger the overlay to show or hide elements | ReactElement | - | +| triggerType | trigger the overlay to show or hide operations, either 'click', 'hover', 'focus', or an array of them, such as ['hover', 'focus'] | String/Array | 'hover' | +| visible | whether the overlay is visiible currently | Boolean | - | +| animation | configure animation, support the {in: 'enter-class', out: 'leave-class' } object parameters, if set to false, do not play the animation. Refer to `Animate` component documentation for available animations. | Object/Boolean | { in: 'expandInDown', out: 'expandOutUp' } | +| hasMask | whether to show the mask | Boolean | false | +| closeable | controls how the dialog is closed. The value can be either a String or Boolean, where the string consists of the following values:
**close** clicking the close button can close the dialog
**mask** clicking the mask can close the dialog
**esc** pressing the esc key can close the dialog
such as 'close' or 'close,esc,mask'
If set to true, all of the above close methods take effect
If set to false, all of the above close methods will fail | String/Boolean | 'esc,close' | +| onVisibleChange | callback function triggered when the ovlery is visible or hidden

**signatures**:
Function(visible: Boolean, type: String, e: Object) => void
**params**:
_visible_: {Boolean} whether the overlay is visible
_type_: {String} the reason that triggers the overlay to show or hide
_e_: {Object} DOM event | Function | func.noop | +| placement | placement of the drawer

**options**:
'top', 'right', 'bottom', 'left' | Enum | 'right' | + +## ARIA and Keyboard + +| Keyboard | Descripiton | +| :-------- | :--------------------------------------- | +| esc | pressing ESC will close dialog | +| tab | focus on any element that can be focused, the focus remains in the dialog when the dialog is displayed | +| shift+tab | back focus on any element that can be focused, the focus remains in the dialog when the dialog is displayed | diff --git a/docs/drawer/index.md b/docs/drawer/index.md new file mode 100644 index 0000000000..b3a1a21356 --- /dev/null +++ b/docs/drawer/index.md @@ -0,0 +1,42 @@ +# Drawer + +- chinese: 抽屉 +- family: Feedback +- category: Components +- type: 弹层 + +--- + +## 使用指南 + +抽屉 + +### 何时使用 + +抽屉是用于在不离开主路径的情况下,提供用户快速执行简单的操作、确认用户信息或反馈提示的辅助窗口。 + +## API + +### Drawer + +| 参数 | 说明 | 类型 | 默认值 | +| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | -------- | +| width | 宽度,仅在 placement是 left right 的时候生效 | Number/String | - | +| height | 高度,仅在 placement是 top bottom 的时候生效 | Number/String | - | +| visible | 是否显示 | Boolean | - | +| hasMask | 是否显示遮罩 | Boolean | true | +| closeable | 控制对话框关闭的方式,值可以为字符串或者布尔值,其中字符串是由以下值组成:
**close** 表示点击关闭按钮可以关闭对话框
**mask** 表示点击遮罩区域可以关闭对话框
**esc** 表示按下 esc 键可以关闭对话框
如 'close' 或 'close,esc,mask'
如果设置为 true,则以上关闭方式全部生效
如果设置为 false,则以上关闭方式全部失效 | String/Boolean | true | +| onClose | 对话框关闭时触发的回调函数

**签名**:
Function(trigger: String, event: Object) => void
**参数**:
_trigger_: {String} 关闭触发行为的描述字符串
_event_: {Object} 关闭时事件对象 | Function | () => {} | +| placement | 位于页面的位置

**可选值**:
'top', 'right', 'bottom', 'left' | Enum | 'right' | +| title | 标题 | ReactNode | - | +| onVisibleChange | 弹层显示或隐藏时触发的回调

**签名**:
Function(visible: Boolean, type: String) => void
**参数**:
_visible_: {Boolean} 弹层是否显示
_type_: {String} 触发弹层显示或隐藏的来源 fromContent 表示由Dropdown内容触发; fromTrigger 表示由trigger的点击触发; docClick 表示由document的点击触发 | Function | - | +| animation | 显示隐藏时动画的播放方式 | Object/Boolean | - | +| bodyStyle | body上的样式 | Object | - | + +## ARIA and Keyboard + +| 键盘 | 说明 | +| :-------- | :--------------------------------------- | +| esc | 按下ESC键将会关闭dialog而不触发任何的动作 | +| tab | 正向聚焦到任何可以被聚焦的元素, 在Dialog显示的时候,焦点始终保持在框体内 | +| shift+tab | 反向聚焦到任何可以被聚焦的元素,在Dialog显示的时候,焦点始终保持在框体内 | diff --git a/docs/drawer/theme/index.jsx b/docs/drawer/theme/index.jsx new file mode 100644 index 0000000000..f43a11f061 --- /dev/null +++ b/docs/drawer/theme/index.jsx @@ -0,0 +1,129 @@ +import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; +import '../../../src/demo-helper/style.js'; +import '../../../src/drawer/style.js'; +import { Demo, DemoGroup, initDemo } from '../../../src/demo-helper'; +import Drawer from '../../../src/drawer'; + +import zhCN from '../../../src/locale/zh-cn'; +import enUS from '../../../src/locale/en-us'; + +const i18nMaps = { + 'en-us': { + title: 'Title Here', + content: 'Start your business here by searching a popular product', + }, + + 'zh-cn': { + title: '这里是标题', + content: '开启您的贸易生活从 Alibaba.com 开始', + } +}; + +class FunctionDemo extends Component { + state = { + demoFunction: { + hasTitle: { + label: '标题', + value: 'true', + enum: [{ + label: '显示', + value: 'true' + }, { + label: '隐藏', + value: 'false' + }] + }, + hasCloseIcon: { + label: '有无关闭按钮', + value: 'true', + enum: [{ + label: '有', + value: 'true' + }, { + label: '无', + value: 'false' + }] + }, + placement: { + label: '方向', + value: 'right', + enum: [{ + label: '上', + value: 'top' + }, { + label: '下', + value: 'bottom' + }, { + label: '左', + value: 'left' + }, { + label: '右', + value: 'right' + }] + }, + } + } + onFunctionChange = demoFunction => { + this.setState({ + demoFunction + }); + } + + renderMask(hasMask, content) { + return hasMask ? ( +
+
+ {content} +
+ ) : content; + } + + render() { + // eslint-disable-next-line + const { lang, i18n } = this.props; + const locale = (lang === 'en-us' ? enUS : zhCN).Drawer; + const hasTitle = this.state.demoFunction.hasTitle.value === 'true'; + const hasCloseIcon = this.state.demoFunction.hasCloseIcon.value === 'true'; + + const placement = this.state.demoFunction.placement.value; + const style = { + position: 'absolute', + top: placement === 'bottom' ? 'auto' : 0, + [placement]: 0, + }; + + const normalContent = ( + + {i18n.content} + + ); + + return ( +
+ + + + {this.renderMask(true, normalContent)} + + + +
+ ); + } +} + + +const render = (lang = 'en-us') => { + const i18n = i18nMaps[lang]; + ReactDOM.render(, document.getElementById('container')); +}; + +window.renderDemo = render; +window.renderDemo('en-us'); +initDemo('drawer'); diff --git a/docs/field/index.en-us.md b/docs/field/index.en-us.md index cd2d8c0c7c..8d69278451 100644 --- a/docs/field/index.en-us.md +++ b/docs/field/index.en-us.md @@ -201,7 +201,7 @@ The api interface provided by the object after `new` (eg `myfield.getValues()`) | getValue | get the value of a single input control | Function(name: String) | | setValues ​​| Sets the value of a set of input controls (triggers render, follow the use of react time) | Function(obj: Object) | | setValue | Sets the value of a single input control (triggers render, follow the use of react time) | Function(name: String, value) | -| Validate | Validate and retrieve the values ​​of a set of input fields and Error | Function([names: String[]], [options: Object], callback: Function(errors, values)) | | | +| Validate | Validate and retrieve the values ​​of a set of input fields and Error | Function([names: String[]], callback: Function(errors, values)) | | | |getError | Get Error of a Single Input Control | Function(name: String) | | | |getErrors | Get Errors of a Group of Input Controls | Function([name: String]) | | | |setError | Set Error for a Single Input Control | Function(name: String, errors:String/Array[String]) | | | diff --git a/docs/field/index.md b/docs/field/index.md index 9f7add8150..87871f3797 100644 --- a/docs/field/index.md +++ b/docs/field/index.md @@ -205,7 +205,7 @@ let myfield = new Field(this [,options]); | getValue | 获取单个输入控件的值 | Function(name: String) | | | | setValues | 设置一组输入控件的值(会触发render,请遵循react时机使用) | Function(obj: Object) | | | | setValue | 设置单个输入控件的值 (会触发render,请遵循react时机使用)| Function(name: String, value) | | | -| validate | 校验并获取一组输入域的值与 Error | Function([names: String[]], [options: Object], callback: Function(errors, values)) | | | +| validate | 校验并获取一组输入域的值与 Error | Function([names: String[]], callback: Function(errors, values)) | | | | getError | 获取单个输入控件的 Error | Function(name: String) | | | | getErrors | 获取一组输入控件的 Error | Function([name: String]) | | | | setError | 设置单个输入控件的 Error | Function(name: String, errors:String/Array[String]) | | | diff --git a/docs/form/demo/mobile.md b/docs/form/demo/mobile.md new file mode 100644 index 0000000000..8fc98eafec --- /dev/null +++ b/docs/form/demo/mobile.md @@ -0,0 +1,78 @@ +# 移动端 + +- order: 17 + +device=phone 下会强制设置 labelAlign=top + +:::lang=en-us +# Basic Usage + +- order: 17 + +force set labelAlign=top while device=phone + +::: +--- + +````jsx +import { Form, Input, Checkbox, Switch, Radio } from '@alifd/next'; + + +const FormItem = Form.Item; + +const formItemLayout = { + labelCol: { + fixedSpan: 10 + }, + wrapperCol: { + span: 14 + } +}; + +class Demo extends React.Component { + state = { + device: 'desktop' + } + + handleDeviceChange = (device) => { + this.setState({ + device + }); + }; + + render() { + return ( +
+ + desktop + phone + +
+
+ +

Fixed Name

+
+ + + + + + + + Agree + + + Confirm + +
+
+ ); + } +} + +ReactDOM.render(, mountNode); +```` diff --git a/docs/form/index.md b/docs/form/index.md index 99a6d2229d..eece66b426 100644 --- a/docs/form/index.md +++ b/docs/form/index.md @@ -40,6 +40,7 @@ | value | 表单数值 | Object | - | | onChange | 表单变化回调

**签名**:
Function(values: Object, item: Object) => void
**参数**:
_values_: {Object} 表单数据
_item_: {Object} 详细
_item.name_: {String} 变化的组件名
_item.value_: {String} 变化的数据
_item.field_: {Object} field 实例 | Function | func.noop | | component | 设置标签类型 | String/Function | 'form' | +| device | 预设屏幕宽度

**可选值**:
'phone', 'tablet', 'desktop' | Enum | 'desktop' | ### Form.Item @@ -82,6 +83,7 @@ | validator | [表单校验] 自定义校验函数

**签名**:
Function() => void | Function | - | | validatorTrigger | validator 自定义触发方式 | String/Array | - | | autoValidate | 是否修改数据时自动触发校验 | Boolean | - | +| device | 预设屏幕宽度

**可选值**:
'phone', 'tablet', 'desktop' | Enum | - | ### Form.Submit diff --git a/docs/input/adaptor/index.jsx b/docs/input/adaptor/index.jsx new file mode 100644 index 0000000000..20ef0f2969 --- /dev/null +++ b/docs/input/adaptor/index.jsx @@ -0,0 +1,172 @@ +import React from 'react'; +import { Types } from '@alifd/adaptor-helper'; +import { Input } from '@alifd/next'; + +export default { + name: 'Input', + shape: [{ + label: 'Textfield', + value: 'textfield' + }, { + label: 'Textarea', + value: 'textarea' + }, { + label: 'Addon', + value: 'addon' + }], + editor: (shape = 'textfield') => { + return { + props: [...( + shape === 'textarea' ? [] : [{ + name: 'size', + type: Types.enum, + options: ['large', 'medium', 'small'], + default: 'medium' + }] + ), ...( + shape === 'addon' ? [] : [{ + name: 'state', + label: 'Status', + type: Types.enum, + options: ['normal', 'focused', 'disabled', 'error', ...(shape === 'textfield' ? ['success', 'loading'] : [])], + default: 'normal' + }] + ), { + name: 'widget', + type: Types.enum, + options: shape === 'textarea' ? ['none', 'length'] : ['none', 'length', 'clear'], + default: 'none' + }, { + name: 'width', + type: Types.number, + default: 200 + }, + ...(shape === 'textarea' ? [{ + name: 'rows', + type: Types.number, + default: 4, + }] : []), + ...(shape !== 'addon' ? [{ + name: 'border', + type: Types.bool, + default: true, + }] : []), + { + name: 'label', + type: Types.string, + default: '' + }, { + name: 'placeholder', + type: Types.string, + default: 'Please Input' + }, + ...(shape === 'addon' ? [{ + name: 'prefix', + type: Types.string, + default: 'https://' + }, { + name: 'suffix', + type: Types.string, + default: '.com' + }] : [])], + data: { + default: shape === 'textarea' ? 'multiple line' : shape === 'addon' ? 'alibaba' : '' + } + }; + }, + adaptor: ({ + shape, + size, + state, + widget, + width, + rows, + border, + label, + placeholder, + prefix = '', + suffix = '', + style = {}, + className = '', + data, + ...others + }) => { + const props = { + ...others, + label, + hasBorder: border, + placeholder, + className, + style: { + width, + ...style + }, + value: data + }; + + if (widget === 'length') { + props.hasLimitHint = true; + props.maxLength = 15; + } else if (widget === 'clear') { + props.hasClear = true; + } + + if (size) { + props.size = size; + } + + switch(state) { + case 'focused': + props.className = `${className} next-focus`; + break; + case 'disabled': + props.disabled = true; + break; + case 'error': + case 'loading': + case 'success': + props.state = state; + break; + default: break; + } + + + if (rows && rows > 0) { + props.rows = rows; + } + + if (shape === 'addon') { + props.addonTextAfter = suffix; + props.addonTextBefore = prefix; + } + + if (shape === 'textarea') { + return ; + } + + return ; + }, + content: (shape) => { + if (shape === 'textfield') { + return { + options: [{ + name: 'clear', + options: ['show', 'hide'], + default: 'hide', + }], + transform: (props, { clear }) => { + return { + ...props, + widget: clear === 'show' ? 'clear' : 'none', + data: clear === 'show' ? 'Input ...' : props.value, + }; + } + }; + } + + return { + options: [], + transform: p => p + }; + } +}; diff --git a/docs/loading/adaptor/index.jsx b/docs/loading/adaptor/index.jsx new file mode 100644 index 0000000000..f81f8b148d --- /dev/null +++ b/docs/loading/adaptor/index.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Loading } from '@alifd/next'; + +export default { + name: 'Loading', + editor: () => ({ + props: [{ + name: 'size', + type: 'enum', + options: ['large', 'medium'], + default: 'medium' + }], + }), + adaptor: ({ size, ...others }) => { + return
; + } +}; diff --git a/docs/menu-button/adaptor/index.jsx b/docs/menu-button/adaptor/index.jsx new file mode 100644 index 0000000000..1c67e72b5d --- /dev/null +++ b/docs/menu-button/adaptor/index.jsx @@ -0,0 +1,195 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import { MenuButton, Menu, Icon } from '@alifd/next'; +import { Types, parseData, ContentType } from '@alifd/adaptor-helper'; + +const createDataSouce = (list, keys = { selected: [], expanded: {} }, level = 0, prefix='') => { + const array = []; + let group = []; + let grouping = false; + let index = 0; + + list.forEach((item) => { + switch(item.type) { + // eslint-disable-next-line no-case-declarations + case 'node': + const key = `${prefix || level }-${index++}`; + + if (item.children && item.children.length > 0) { + item.children = createDataSouce(item.children, keys, level + 1, key); + } + + if (grouping) { + group.push({ + ...item, + key, + }); + } else { + array.push({ + ...item, + key + }); + } + + if (item.state === 'active') { + if (item.children && item.children.length > 0) { + keys.expanded.push(key); + } else { + keys.selected.push(key); + } + } + + return; + case 'comment': + if (group.length > 0) { + array.push({ + type: 'group', + value: grouping, + children: group, + key: `${prefix || level }-${index++}` + }); + group = []; + } + grouping = item.value; + return; + case 'divider': + if (group.length > 0) { + array.push({ + type: 'group', + value: grouping, + children: group, + key: `${prefix || level }-${index++}` + }); + group = []; + } + grouping = false; + array.push({ + type: 'divider', + key: `${prefix || level }-${index++}`, + }); + return; + default: return; + } + }); + + if (group.length > 0) { + array.push({ + type: 'group', + value: grouping, + children: group, + key: `${prefix || level }-${index++}` + }); + group = []; + } + + return array; +}; + +const createMenuItem = (item) => { + if (item.children.length > 0) { + return ( + type === ContentType.text).map(({ value }) => value).join('') : ''}> + {createContents(item.children)} + + ); + } + + return type === 'icon' ? : value)} />; +}; + +const createContents = (array = []) => { + + return array.map((item) => { + if (item.type === 'group' && item.children.length > 0) { + return {item.children.map(it => createMenuItem(it))}; + } + + if (item.type === 'divider') { + return ; + } + + return createMenuItem(item); + }); +}; + +export default { + name: 'MenuButton', + shape: ['normal', 'text', 'ghost'], + editor: (shape = 'normal') => ({ + props: [{ + name: 'level', + type: Types.enum, + options: shape === 'text' ? ['normal', 'primary'] : shape === 'ghost' ? ['light', 'dark'] : ['normal', 'primary', 'secondary'], + default: shape === 'ghost' ? 'light' : 'normal', + }, { + name: 'size', + type: Types.enum, + options: ['large', 'medium', 'small'], + default: 'medium' + }], + data: { + icon: true, + active: true, + disable: true, + default: 'Edit Document\n\tUndo\n\t*Redo\n\tCut\n\tCopy\n\tPaste' + } + }), + adaptor: ({ shape, level, size, data, ...others}) => { + const list = parseData(data, { parseContent: true }); + const buttonItem = list[0] ? list[0] : { value: []}; + + if (buttonItem.type !== 'node') return null; + + const keys = { selected: [], expanded: [] }; + const dataSouce = createDataSouce(list[0] ? list[0].children : [], keys); + + const label = buttonItem.value.map(({ type, value}) => { + if (type === 'icon') return ; + return value; + }); + + return ( + node} + ghost={shape === 'ghost' ? level : false} + selectMode="multiple" + text={shape === 'text'} + menuProps={{ openKeys: keys.expanded, style: { textAlign: 'left' } }} + selectedKeys={keys.selected} + label={label} + > + {createContents(dataSouce)} + + ); + }, + demoOptions: (demo) => { + const { node = { props: {} } } = demo; + const { level, data } = node.props; + if (data.indexOf('*') === 0) { + demo = { + ...demo, + height: 250 + }; + } + + if (level === 'dark') { + demo = { + ...demo, + background: '#333' + }; + } else if(level === 'light') { + demo = { + ...demo, + background: 'rgb(235, 236, 240)', + }; + } + + return demo; + } +}; diff --git a/docs/menu/adaptor/index.jsx b/docs/menu/adaptor/index.jsx new file mode 100644 index 0000000000..df98fc1551 --- /dev/null +++ b/docs/menu/adaptor/index.jsx @@ -0,0 +1,192 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import { Types, parseData, ContentType } from '@alifd/adaptor-helper'; +import { Menu, Icon } from '@alifd/next'; + +const createDataSouce = (list, keys = { selected: [], expanded: [] }, level = 0, prefix='') => { + const array = []; + let group = []; + let grouping = false; + let index = 0; + + list.forEach((item) => { + switch(item.type) { + // eslint-disable-next-line no-case-declarations + case 'node': + const key = `${prefix || level }-${index++}`; + + if (item.children && item.children.length > 0) { + item.children = createDataSouce(item.children, keys, level + 1, key); + } + + if (grouping) { + group.push({ + ...item, + key, + }); + } else { + array.push({ + ...item, + key + }); + } + + if (item.state === 'active') { + if (item.children && item.children.length > 0) { + keys.expanded.push(key); + } else { + keys.selected.push(key); + } + } + + return; + case 'comment': + if (group.length > 0) { + array.push({ + type: 'group', + value: grouping, + children: group, + key: `${prefix || level }-${index++}` + }); + group = []; + } + grouping = item.value; + return; + case 'divider': + if (group.length > 0) { + array.push({ + type: 'group', + value: grouping, + children: group, + key: `${prefix || level }-${index++}` + }); + group = []; + } + grouping = false; + array.push({ + type: 'divider', + key: `${prefix || level }-${index++}`, + }); + return; + default: return; + } + }); + + if (group.length > 0) { + array.push({ + type: 'group', + value: grouping, + children: group, + key: `${prefix || level }-${index++}` + }); + group = []; + } + + return array; +}; + +const createMenuItem = (item, selectType) => { + if (item.children.length > 0) { + return ( + type === ContentType.text).map(({ value }) => value).join('') : ''}> + {createContents(item.children, selectType)} + + ); + } + + let Item = Menu.Item; + + if (selectType === 'checkbox') { + Item = Menu.CheckboxItem; + } else if (selectType === 'radio') { + Item = Menu.RadioItem; + } + + return type === 'icon' ? : value)} />; +}; + +const createContents = (array = [], selectType) => { + + return array.map((item) => { + if (item.type === 'group' && item.children.length > 0) { + return {item.children.map(it => createMenuItem(it, selectType))}; + } + + if (item.type === 'divider') { + return ; + } + + return createMenuItem(item, selectType); + }); +}; + +export default { + name: "Menu", + editor: () => ({ + props: [{ + name: 'selectType', + label: 'Selection Mode', + type: Types.enum, + options: ['checkbox', 'radio', 'checkLeft', 'checkRight'], + default: 'checkLeft' + }, { + name: 'nestMode', + label: 'Subset Structure', + type: Types.enum, + options: ['inline', 'popup'], + default: 'inline', + }, { + name: 'width', + type: Types.number, + default: 150 + }], + data: { + active: true, + disable: true, + group: true, + icon: true, + default: '#Group1\noption1\n*option2\n\tsub option3\n\t-sub option4\n\tsub option5\n---\n#Group2\noption1\n*option2' + } + }), + adaptor: ({ selectType, nestMode, width, style, data, ...others }) => { + const list = parseData(data, { parseContent: true }); + const keys = { selected: [], expanded: [] }; + const array = createDataSouce(list, keys); + + return ( + node }} isSelectIconRight={selectType === 'checkRight'} openKeys={keys.expanded} selectedKeys={keys.selected} style={{ width, ...style }} mode={nestMode === 'popup' ? 'popup' : 'inline'}> + { + createContents(array, selectType) + } + + ); + }, + content: () => ({ + options: [{ + name: 'group', + options: ['yes', 'no'], + default: 'no' + }, { + name: 'icon', + options: ['yes', 'no'], + default: 'no' + }, { + name: 'selectType', + options: ['checkbox', 'radio', 'checkLeft', 'checkRight'], + default: 'checkLeft', + }], + transform: (props, { group, icon, selectType }) => { + const iconStr = icon === 'yes' ? '[picture]' : ''; + const prefix = (props.data || '').indexOf('*') !== -1 ? '*' : (props.data || '').indexOf('-') !== -1 ? '-' : ''; + if (group === 'yes') { + props.data = `#group1\n${prefix}${iconStr}option1\n${iconStr}option2\n---\n#group2\n${iconStr}option3\n${iconStr}option4`; + } else if (icon === 'yes') { + props.data = `${iconStr}option1\n${iconStr}option2\n${iconStr}option3\n${iconStr}option4`; + } + return { + ...props, + selectType, + }; + } + }) +}; diff --git a/docs/message/adaptor/index.jsx b/docs/message/adaptor/index.jsx new file mode 100644 index 0000000000..4ddb0487e7 --- /dev/null +++ b/docs/message/adaptor/index.jsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { Message } from '@alifd/next'; +import { Types } from '@alifd/adaptor-helper'; + +export default { + name: 'Message', + editor: () => ({ + props: [{ + name: 'level', + label: 'Shape', + type: Types.enum, + options: ['inline', 'toast', 'addon'], + default: 'inline' + }, { + name: 'size', + type: Types.enum, + options: ['large', 'medium'], + default: 'medium' + }, { + name: 'state', + label: 'Status', + type: Types.enum, + options: ['success', 'warning', 'error', 'notice', 'help', 'loading'], + default: 'success', + }, { + name: 'closable', + label: 'Close Included', + type: Types.bool, + default: false + }, { + name: 'width', + type: Types.number, + default: 400 + }, { + name: 'title', + type: Types.string, + default: 'Title' + }], + data: { + default: 'This item already has the label "travel", you can add a new label.' + } + }), + adaptor: ({ level, size, state, closable, width, title, data, style, ...others}) => { + return ( + {data} + ); + } +}; diff --git a/docs/nav/adaptor/index.jsx b/docs/nav/adaptor/index.jsx new file mode 100644 index 0000000000..80873840fb --- /dev/null +++ b/docs/nav/adaptor/index.jsx @@ -0,0 +1,188 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import { Types, parseData, ContentType } from '@alifd/adaptor-helper'; +import { Nav } from '@alifd/next'; + +const createDataSouce = (list, keys = { selected: [], expanded: {} }, level = 0, prefix='') => { + const array = []; + let group = []; + let grouping = false; + let index = 0; + + list.forEach((item) => { + switch(item.type) { + // eslint-disable-next-line no-case-declarations + case 'node': + const key = `${prefix || level }-${index++}`; + + if (item.children && item.children.length > 0) { + item.children = createDataSouce(item.children, keys, level + 1, key); + } + + if (grouping) { + group.push({ + ...item, + key, + }); + } else { + array.push({ + ...item, + key + }); + } + + if (item.state === 'active') { + if (item.children && item.children.length > 0) { + keys.expanded.push(key); + } else { + keys.selected.push(key); + } + } + + return; + case 'comment': + if (group.length > 0) { + array.push({ + type: 'group', + value: grouping, + children: group, + key: `${prefix || level }-${index++}` + }); + group = []; + } + grouping = item.value; + return; + case 'divider': + if (group.length > 0) { + array.push({ + type: 'group', + value: grouping, + children: group, + key: `${prefix || level }-${index++}` + }); + group = []; + } + grouping = false; + array.push({ + type: 'divider', + key: `${prefix || level }-${index++}`, + }); + return; + default: return; + } + }); + + if (group.length > 0) { + array.push({ + type: 'group', + value: grouping, + children: group, + key: `${prefix || level }-${index++}` + }); + group = []; + } + + return array; +}; + +const createMenuItem = (item) => { + const { value } = (item.value || []).find(item => item.type === ContentType.icon) || {}; + if (item.children.length > 0) { + return ( + type === ContentType.text).map(({ value }) => value).join('') : ''}> + {createContents(item.children)} + + ); + } + + return type === 'icon' ? null : value)} />; +}; + +const createContents = (array = []) => { + + return array.map((item) => { + if (item.type === 'group' && item.children.length > 0) { + return {item.children.map(it => createMenuItem(it))}; + } + + if (item.type === 'divider') { + return ; + } + + return createMenuItem(item); + }); +}; + + +export default { + name: 'Nav', + shape: [{ + label: 'Horizontal', + value: 'hoz' + }, { + label: 'Vertical', + value: 'ver' + }], + editor: (shape = 'hoz') => ({ + props: [{ + name: 'level', + type: Types.enum, + options: ['normal', 'primary', 'secondary', 'line'], + default: 'normal' + }, { + name: 'selectLinePosition', + label: 'Label Position', + type: Types.enum, + options: shape === 'hoz' ? ['top', 'bottom', 'none'] : ['left', 'right', 'none'], + default: shape === 'hoz' ? 'bottom' : 'right' + }, { + name: 'width', + type: Types.number, + default: shape === 'hoz' ? 640 : 200 + }, + ...(shape === 'ver' ? [{ + name: 'height', + type: Types.number, + default: shape === 'hoz' ? '' : 600 + }, { + name: 'iconOnly', + type: Types.bool, + default: false, + }] : [])], + data: { + active: true, + hover: true, + group: true, + icon: true, + default: `[account]Nav Item 1\n[account]Nav Item 2\n[account]Nav Item 3\n${shape === 'ver' ? '*' : ''}[account]Nav Item 4\n\t[account]Option 1\n\t[account]Option 2\n\t[account]Option 3\n\t[account]Option 4` + } + }), + adaptor: ({ shape, level, selectLinePosition, width, height, data, style, iconOnly = false, ...others}) => { + const list = parseData(data, { parseContent: true }); + const keys = { selected: [], expanded: [] }; + const dataSouce = createDataSouce(list, keys); + + return ( + + ); + } +}; diff --git a/docs/number-picker/adaptor/index.jsx b/docs/number-picker/adaptor/index.jsx new file mode 100644 index 0000000000..64d541d2b9 --- /dev/null +++ b/docs/number-picker/adaptor/index.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { NumberPicker } from '@alifd/next'; +import { Types } from '@alifd/adaptor-helper'; + +export default { + name: 'NumberPicker', + editor: () => ({ + props: [{ + name: 'level', + type: Types.enum, + options: ['normal', 'inline'], + default: 'normal' + }, { + name: 'size', + type: Types.enum, + options: ['large', 'medium'], + default: 'medium' + }, { + name: 'state', + label: 'Status', + type: Types.enum, + options: ['normal', 'disabled'], + default: 'normal' + }, { + name: 'width', + type: Types.number, + default: 80 + }, { + name: 'value', + type: Types.number, + default: 1 + }] + }), + adaptor: ({ level, size, state, width, value, style, ...others }) => { + return + } +}; diff --git a/docs/number-picker/demo/mobile.md b/docs/number-picker/demo/mobile.md new file mode 100644 index 0000000000..ac340269c8 --- /dev/null +++ b/docs/number-picker/demo/mobile.md @@ -0,0 +1,50 @@ +# 移动端 + +- order: 9 + +device=phone 下会强制设置 type=normal + +:::lang=en-us +# Basic Usage + +- order: 9 + +force set type=normal while device=phone + +::: +--- + +````jsx +import { NumberPicker, Radio } from '@alifd/next'; + +class Demo extends React.Component { + state = { + device: 'desktop' + } + + handleDeviceChange = (device) => { + this.setState({ + device + }); + }; + + render() { + return ( +
+ + desktop + phone + +
+ +
+ ); + } +} + +ReactDOM.render(, mountNode); +```` diff --git a/docs/number-picker/index.md b/docs/number-picker/index.md index 9bb68d982e..02031ac686 100644 --- a/docs/number-picker/index.md +++ b/docs/number-picker/index.md @@ -51,6 +51,7 @@ | downBtnProps | 减少按钮的props | Object | - | | label | 内联 label | ReactNode | - | | innerAfter | inner after | ReactNode | - | +| device | 预设屏幕宽度

**可选值**:
'phone', 'tablet', 'desktop' | Enum | - | ## ARIA and KeyBoard diff --git a/docs/pagination/adaptor/index.jsx b/docs/pagination/adaptor/index.jsx new file mode 100644 index 0000000000..70e73d631a --- /dev/null +++ b/docs/pagination/adaptor/index.jsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { Pagination } from '@alifd/next'; +import { Types } from '@alifd/adaptor-helper'; +import enUS from '../../../src/locale/en-us'; + +export default { + name: 'Pagination', + shape: ['normal', 'simple', 'mini'], + editor: () => ({ + props: [{ + name: 'size', + type: Types.enum, + options: ['large', 'medium', 'small'], + default: 'medium' + }, { + name: 'sizeSelector', + label: 'Widget', + type: Types.enum, + options: ['none', 'filter', 'dropdown'], + default: 'none' + }, { + name: 'control', + label: 'Arrow Style', + type: Types.enum, + options: ['textAndIcon', 'onlyIcon', 'noBorder'], + default: 'textAndIcon' + }] + }), + adaptor: ({ shape, size, sizeSelector, control, ...others}) => { + return ( + + ); + }, + content: () => ({ + options: [{ + name: 'control', + options: ['textAndIcon', 'onlyIcon', 'noBorder'], + default: 'textAndIcon' + }], + transform: (props, { control }) => { + return { + ...props, + control + }; + } + }) +}; diff --git a/docs/paragraph/adaptor/index.jsx b/docs/paragraph/adaptor/index.jsx new file mode 100644 index 0000000000..2c4ade5c32 --- /dev/null +++ b/docs/paragraph/adaptor/index.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Paragraph } from '@alifd/next'; +import { Types } from '@alifd/adaptor-helper'; + +export default { + name: 'Paragraph', + editor: () => ({ + props: [{ + name: 'size', + type: Types.enum, + options: ['medium', 'small'], + default: 'medium' + }, { + name: 'width', + type: Types.number, + default: 400 + }], + data: { + default: `Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.` + } + }), + adaptor: ({ size, width, style, data, ...others }) => { + return ({data}); + } +}; diff --git a/docs/progress/adaptor/index.jsx b/docs/progress/adaptor/index.jsx new file mode 100644 index 0000000000..4fc510f8f3 --- /dev/null +++ b/docs/progress/adaptor/index.jsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { Types } from '@alifd/adaptor-helper'; +import { Progress } from '@alifd/next'; + +export default { + name: 'Progress', + shape: ['line', 'circle'], + editor: (shape = 'line') => { + return { + props: [{ + name: 'mode', + label: 'Type', + type: Types.enum, + options: ['basic', 'staging'], + default: 'basic' + }, { + name: 'level', + type: Types.enum, + options: ['normal', 'success', 'error'], + default: 'normal' + }, { + name: 'size', + type: Types.enum, + options: ['large', 'medium', 'small'], + default: 'medium' + }, { + name: 'text', + label: 'Data Included', + type: Types.bool, + default: false + }, + ...( + shape === 'line' ? + [{ + name: 'border', + type: Types.bool, + default: false + }, { + name: 'width', + type: Types.number, + default: 200 + }] : + [] + ), + { + name: 'percent', + type: Types.number, + default: 25 + }] + }; + }, + adaptor: ({ shape, level, size, mode, text, border, width, percent, style, ...others }) => { + return ( + text ? `${Math.floor(percent)}%` : false} percent={percent} state={level} progressive={mode === 'staging'} hasBorder={border} /> + ); + }, + content: (shape = 'line') => ({ + options: [{ + name: 'staging', + options: ['none', 'started', 'middle', 'finishing'], + default: 'none' + }, { + name: 'text', + options: ['show', 'hide'], + default: shape === 'cirlce' ? 'show' : 'hide' + }, { + name: 'border', + options: ['show', 'hide'], + default: 'hide' + }].filter(({ name }) => name !== 'border' || shape === 'line'), + transform: (props, { staging, text, border }) => { + switch (staging) { + case 'started': + props = { + ...props, + percent: 20, + mode: 'staging' + }; + break; + case 'middle': + props = { + ...props, + percent: 60, + mode: 'staging' + }; + break; + case 'finishing': + props = { + ...props, + percent: 85, + mode: 'staging' + }; + break; + default: break; + } + + return { + ...props, + text: text === 'show', + border: border === 'show' + }; + } + }) + +}; diff --git a/docs/radio/adaptor/index.jsx b/docs/radio/adaptor/index.jsx new file mode 100644 index 0000000000..2070c25bcd --- /dev/null +++ b/docs/radio/adaptor/index.jsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { Types, parseData, NodeType } from '@alifd/adaptor-helper'; +import { Radio } from '@alifd/next'; + +export default { + name: 'Radio', + shape: ['normal', {value: 'button', label: 'Radio Button'}], + editor: (shape = 'normal') => { + if (shape === 'button') { + return { + props: [{ + name: 'size', + type: Types.enum, + options: ['large', 'medium', 'small'], + default: 'medium' + }, { + name: 'state', + label: 'Status', + type: Types.enum, + options: ['normal', 'disabled'], + default: 'normal' + }], + data: { + hover: true, + disable: true, + active: true, + default: `Cell 1\n*Cell 2\nCell 3` + } + }; + } + return { + props: [{ + name: 'state', + label: 'Status', + type: Types.enum, + options: ['normal', 'hover', 'disabled', 'checked', 'checkedHover', 'checkedDisabled'], + default: 'normal' + }, { + name: 'label', + type: Types.string, + default: 'label' + }], + } + }, + adaptor: ({ shape, size, state = '', label, data, ...others }) => { + if (shape === 'normal') { + return ( + + ); + } + + const list = parseData(data).filter(({ type }) => type === NodeType.node); + + return ( + item.state === 'active')}> + { + list.map((item, index) => {item.value}) + } + + ); + + } + +}; + diff --git a/docs/radio/index.md b/docs/radio/index.md index ae9b6707b2..9b4b891353 100644 --- a/docs/radio/index.md +++ b/docs/radio/index.md @@ -44,7 +44,7 @@ | component | 设置标签类型 | String/Function | 'div' | | onChange | 选中值改变时的事件

**签名**:
Function(value: String/Number, e: Event) => void
**参数**:
_value_: {String/Number} 选中项的值
_e_: {Event} Dom 事件对象 | Function | () => {} | | disabled | 表示radio被禁用 | Boolean | - | -| dataSource | 可选项列表, 数据项可为 String 或者 Object, 如 `['apple', 'pear', 'orange']` | Array<any> | \[] | +| dataSource | 可选项列表, 数据项可为 String 或者 Object, 如 `['apple', 'pear', 'orange']` | Array<String>/Array<Object> | \[] | | children | 通过子元素方式设置内部radio | Array<ReactElement>/ReactElement | - | | itemDirection | 子项目的排列方式
- hoz: 水平排列 (default)
- ver: 垂直排列

**可选值**:
'hoz', 'ver' | Enum | 'hoz' | diff --git a/docs/range/adaptor/index.jsx b/docs/range/adaptor/index.jsx new file mode 100644 index 0000000000..913ef692fb --- /dev/null +++ b/docs/range/adaptor/index.jsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { Types } from '@alifd/adaptor-helper'; +import { Range } from '@alifd/next'; + +export default { + name: 'Range', + shape: [{ + label: 'Basic', + value: 'basic' + }, { + label: 'With Scale', + value: 'scale', + }], + editor: () => ({ + props: [{ + name: 'level', + label: 'Selection Mode', + type: Types.enum, + options: ['single', 'double'], + default: 'single' + }, { + name: 'type', + type: Types.enum, + options: ['basic', 'scale'], + default: 'basic' + }, { + name: 'scalePosition', + type: Types.enum, + options: ['above', 'below'], + default: 'above' + }, { + name: 'state', + label: 'Status', + type: Types.enum, + options: ['normal', 'hover', 'clicked', 'disabled'], + default: 'normal' + }, { + name: 'width', + type: Types.number, + default: 400 + }, { + name: 'start', + type: Types.number, + default: 0 + }, { + name: 'end', + type: Types.number, + default: 35 + }], + }), + adaptor: ({ shape, level, type, scalePosition, state, width, start, end, style, ...others }) => { + return ( +
+ +
+ ); + }, + demoOptions: (demo) => { + const { node } = demo; + const { props } = node; + if (props.level === 'double') { + props.start = 20; + props.end = 60; + } + + return demo; + } +}; diff --git a/docs/rating/adaptor/index.jsx b/docs/rating/adaptor/index.jsx new file mode 100644 index 0000000000..119a8ff49d --- /dev/null +++ b/docs/rating/adaptor/index.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Types } from '@alifd/adaptor-helper'; +import { Rating } from '@alifd/next'; + +export default { + name: 'Rating', + editor: () => ({ + props: [{ + name: 'type', + type: Types.enum, + options: ['normal', 'level'], + default: 'normal' + }, { + name: 'size', + type: Types.enum, + options: ['large', 'medium', 'small'], + default: 'medium' + }, { + name: 'value', + type: Types.number, + default: 3 + }] + }), + adaptor: ({ type, size, value, ...others }) => { + return ; + } +}; diff --git a/docs/search/adaptor/index.jsx b/docs/search/adaptor/index.jsx new file mode 100644 index 0000000000..0ce0008432 --- /dev/null +++ b/docs/search/adaptor/index.jsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { Types } from '@alifd/adaptor-helper'; +import { Search } from '@alifd/next'; + +export default { + name: 'Search', + shape: [{ + value: 'normal', + label: 'Basic' + }, { + value: 'simple', + label: 'Simple' + }], + editor: (shape = 'normal') => ({ + props: [{ + name: 'level', + type: Types.enum, + options: shape === 'simple' ? ['normal', 'dark'] : ['normal', 'primary', 'secondary', 'dark'], + default: 'normal' + }, { + name: 'size', + type: Types.enum, + options: ['large', 'medium'], + default: 'medium' + }, { + name: 'menu', + type: Types.bool, + default: true + }, { + name: 'placeholder', + type: Types.string, + default: 'Please Input' + }, + ...( + shape === 'normal' ? [{ + name: 'label', + type: Types.bool, + default: true + }] : [] + ), { + name: 'width', + type: Types.number, + default: shape === 'normal' ? 400 : 380 + }] + }), + adaptor: ({ shape, level, size, menu, label, width, style, ...others }) => { + return ( + + ); + }, + content: (shape) => ({ + options: [{ + name: 'menu', + options: ['show', 'hide'], + default: 'show' + }, { + name: 'buttonLabel', + options: ['show', 'hide'], + default: 'show' + }].filter(({ name }) => name !== 'buttonLabel' || shape === 'normal'), + transform: (props, { menu, buttonLabel }) => { + return { + ...props, + menu: menu === 'show', + label: buttonLabel === 'show' + }; + } + }), + demoOptions: (demo) => { + if (demo.node.props.level === 'dark') { + return { + ...demo, + background: '#333' + }; + } + return { + ...demo + }; + } +}; + diff --git a/docs/search/index.md b/docs/search/index.md index 45ffd9539d..7f97eee627 100644 --- a/docs/search/index.md +++ b/docs/search/index.md @@ -20,30 +20,30 @@ > 输入框部分继承 Select.AutoComplete 的能力,可以直接用AutoComplete 的 api -| 参数 | 说明 | 类型 | 默认值 | -| ------------------ | -------------------------------------------------------------------------------------------------------------------------- | ------------- | --------- | -| size | 大小

**可选值**:
'large'('大')
'medium'('小') | Enum | 'medium' | -| type | 类型 shape=normal: primary/secondary; shape=simple: normal/dark;

**可选值**:
'primary', 'secondary', 'normal', 'dark' | Enum | 'normal' | -| shape | 形状

**可选值**:
'normal', 'simple' | Enum | 'normal' | -| defaultValue | 搜索框默认值 | String | - | -| value | 搜索框数值 | String/Number | - | -| onChange | 输入关键字时的回掉

**签名**:
Function(value: Object) => void
**参数**:
_value_: {Object} 输入值 | Function | func.noop | -| onSearch | 点击搜索按钮触发的回调

**签名**:
Function(value: Object) => void
**参数**:
_value_: {Object} 输入值 | Function | func.noop | -| defaultFilterValue | 选择器默认值 | String | - | -| filter | 选择器 | Array | \[] | -| filterValue | 选择器值 | String | - | -| onFilterChange | 选择器发生变化时回调

**签名**:
Function(filter: Object) => void
**参数**:
_filter_: {Object} value | Function | func.noop | -| dataSource | 搜索框下拉联想列表 | Array | - | -| placeholder | 默认提示 | String | - | -| searchText | button 的内容 | ReactNode | - | -| filterProps | 选择器的props | Object | - | -| buttonProps | 按钮的额外属性 | Object | {} | -| popupContent | 自定义渲染的的下拉框 | ReactNode | - | -| followTrigger | 是否跟随滚动 | Boolean | - | -| visible | 自定义渲染的的下拉框 | Boolean | - | -| hasClear | 是否显示清除按钮 | Boolean | false | -| hasIcon | 是否显示搜索按钮 | Boolean | true | -| disabled | 是否禁用 | Boolean | false | +| 参数 | 说明 | 类型 | 默认值 | +| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | --------- | +| size | 大小

**可选值**:
'large'('大')
'medium'('小') | Enum | 'medium' | +| type | 类型 shape=normal: primary/secondary; shape=simple: normal/dark;

**可选值**:
'primary', 'secondary', 'normal', 'dark' | Enum | 'normal' | +| shape | 形状

**可选值**:
'normal', 'simple' | Enum | 'normal' | +| defaultValue | 搜索框默认值 | String | - | +| value | 搜索框数值 | String/Number | - | +| onChange | 输入关键字时的回掉

**签名**:
Function(value: Object) => void
**参数**:
_value_: {Object} 输入值 | Function | func.noop | +| onSearch | 点击搜索按钮触发的回调

**签名**:
Function(value: String, filterValue: String) => void
**参数**:
_value_: {String} 输入值
_filterValue_: {String} 选项值 | Function | func.noop | +| defaultFilterValue | 选择器默认值 | String | - | +| filter | 选择器 | Array | \[] | +| filterValue | 选择器值 | String | - | +| onFilterChange | 选择器发生变化时回调

**签名**:
Function(filter: Object) => void
**参数**:
_filter_: {Object} value | Function | func.noop | +| dataSource | 搜索框下拉联想列表 | Array | - | +| placeholder | 默认提示 | String | - | +| searchText | button 的内容 | ReactNode | - | +| filterProps | 选择器的props | Object | - | +| buttonProps | 按钮的额外属性 | Object | {} | +| popupContent | 自定义渲染的的下拉框 | ReactNode | - | +| followTrigger | 是否跟随滚动 | Boolean | - | +| visible | 自定义渲染的的下拉框 | Boolean | - | +| hasClear | 是否显示清除按钮 | Boolean | false | +| hasIcon | 是否显示搜索按钮 | Boolean | true | +| disabled | 是否禁用 | Boolean | false | ## ARIA and KeyBoard diff --git a/docs/select/adaptor/index.jsx b/docs/select/adaptor/index.jsx new file mode 100644 index 0000000000..85fb1a767b --- /dev/null +++ b/docs/select/adaptor/index.jsx @@ -0,0 +1,99 @@ +import React from 'react'; +import { Types, parseData, NodeType } from '@alifd/adaptor-helper'; +import { Select } from '@alifd/next'; + +export default { + name: 'Select', + editor: () => ({ + props: [{ + name: 'level', + label: 'Type', + type: Types.enum, + options: ['single', 'multiple'], + default: 'single' + }, { + name: 'size', + type: Types.enum, + options: ['large', 'medium', 'small'], + default: 'medium' + }, { + name: 'state', + label: 'Status', + type: Types.enum, + options: ['normal', 'expanded', 'disabled'], + default: 'normal' + }, { + name: 'border', + type: Types.bool, + default: true + }, { + name: 'width', + type: Types.number, + default: 200 + }, { + name: 'label', + type: Types.string, + default: '' + }, { + name: 'placeholder', + type: Types.string, + default: 'Please Select' + }], + data: { + disable: true, + active: true, + default: 'Option 1\n*Option 2\nOption 3\nOption 4\nOption 5' + } + }), + adaptor: ({ level, size, state, border, width, label, placeholder, data, style, ...others }) => { + const list = parseData(data).filter(({ type }) => NodeType.node === type); + const dataSource = []; + const value = []; + + list.forEach((item, index) => { + dataSource.push({ + label: item.value, + value: index, + disabled: item.state === 'disabled' + }); + if (item.state === 'active') { + value.push(index); + } + }); + + return ( + ); + + // value = null 时候清空数据 + wrapper.setProps({ value: null }); + assert(wrapper.find('input').prop('value') === ''); + done(); + }); }); }); diff --git a/test/input/textarea-spec.js b/test/input/textarea-spec.js index 4825882633..7b2b86daeb 100644 --- a/test/input/textarea-spec.js +++ b/test/input/textarea-spec.js @@ -288,5 +288,14 @@ describe('TextArea', () => { done(); }); + + it('support null to reset', done => { + let wrapper = mount(); + + // value = null 时候清空数据 + wrapper.setProps({ value: null }); + assert(wrapper.find('textarea[data-real]').prop('value') === ''); + done(); + }); }); }); diff --git a/test/menu/index-spec.js b/test/menu/index-spec.js index acde87880a..1e0e95cf96 100644 --- a/test/menu/index-spec.js +++ b/test/menu/index-spec.js @@ -84,6 +84,41 @@ describe('Menu', () => { assert(item.find('.next-menu-item-helper').text() === 'helper'); }); + it('Group/SubMenu should accepct string/number/node', () => { + wrapper = mount( + + + test-group-string + + Group option 1 + + + + test-submenu-string + + Sub option 1 + + + + ); + const innerHTML = wrapper.find('.next-menu').at(0).instance().innerHTML; + assert(innerHTML.match('test-group-string')); + assert(innerHTML.match('test-submenu-string')); + }); + + it('Group/SubMenu robotness', () => { + wrapper = mount( + + + + Sub option 1 + + + + ); + assert(wrapper); + }); + it('should filter duplicate keys', () => { wrapper = mount( @@ -163,6 +198,36 @@ describe('Menu', () => { assert(item.prop('aria-disabled')); }); + it('paddingleft should only be related to inline mode', () => { + wrapper = mount( + + + 1 + + 2 + + 3 + + + + + 11 + + 21 + + 31 + + + + + ); + const item1Level = wrapper.find('#sub2-item').at(0).props().inlineLevel; + const item2Level = wrapper.find('#suba2-item').at(0).props().inlineLevel; + + assert(item1Level === 3); + assert(item2Level === 2); + }); + it('should render menu divider', () => { wrapper = mount( diff --git a/test/nav/index-spec.js b/test/nav/index-spec.js index 5404b82291..cc72912ff4 100644 --- a/test/nav/index-spec.js +++ b/test/nav/index-spec.js @@ -209,6 +209,49 @@ describe('Nav', () => { assert(wrapper.find('li.next-nav-group-label').length === 1); }); + it('should support defaultSelectedKeys undefined/null', () => { + wrapper = mount( + + ); + + }); + + it('should support showChildSelected', () => { + wrapper = mount( +