├── .eslintignore ├── .npmignore ├── tests └── jest │ ├── mock.js │ ├── config.js │ └── polyfill.js ├── src ├── tag │ ├── index.js │ ├── __test__ │ │ └── Tag_test.jsx │ └── Tag.jsx ├── tree │ ├── index.js │ └── model │ │ └── util.js ├── card │ ├── index.js │ ├── Card.jsx │ └── __test__ │ │ └── Card_test.jsx ├── icon │ ├── index.js │ └── Icon.jsx ├── rate │ ├── index.js │ └── __test__ │ │ └── rate_test.jsx ├── alert │ ├── index.js │ └── __test__ │ │ └── Alert_test.jsx ├── badge │ ├── index.js │ └── Badge.jsx ├── input │ ├── index.js │ └── __test__ │ │ └── Input_test.jsx ├── switch │ └── index.js ├── slider │ └── index.js ├── upload │ ├── index.js │ ├── Types.js │ ├── Cover.jsx │ └── ajax.js ├── loading │ └── index.js ├── message │ ├── index.js │ └── Message.jsx ├── popover │ └── index.js ├── tooltip │ └── index.js ├── cascader │ └── index.js ├── pagination │ └── index.js ├── progress │ └── index.js ├── transfer │ └── index.js ├── color-picker │ ├── index.js │ ├── draggable.js │ └── Types.js ├── input-number │ ├── index.js │ └── util.js ├── auto-complete │ └── index.js ├── notification │ ├── index.js │ └── NotificationCenter.jsx ├── layout │ ├── index.js │ └── Row.jsx ├── steps │ └── index.js ├── tabs │ ├── index.js │ └── TabPane.jsx ├── form │ └── index.js ├── button │ ├── index.js │ ├── ButtonGroup.jsx │ ├── Button.jsx │ └── __test__ │ │ └── Button_test.jsx ├── table │ ├── index.js │ └── TableColumn.js ├── carousel │ └── index.js ├── collapse │ ├── index.js │ ├── CollapseItem.jsx │ └── Collapse.jsx ├── date-picker │ ├── basic │ │ └── index.js │ ├── index.js │ ├── __test__ │ │ └── utils.js │ ├── panel │ │ └── PopperBase.js │ ├── MountBody.jsx │ ├── DatePicker.jsx │ ├── DateRangePicker.jsx │ ├── TimeRangePicker.jsx │ ├── TimeSelect.jsx │ └── TimePicker.jsx ├── breadcrumb │ ├── index.js │ ├── BreadcrumbItem.jsx │ ├── Breadcrumb.jsx │ └── __test__ │ │ └── Breadcrumb_test.jsx ├── radio │ ├── index.js │ ├── RadioGroup.jsx │ └── RadioButton.jsx ├── select │ ├── index.js │ └── OptionGroup.jsx ├── dialog │ ├── index.js │ ├── DialogBody.jsx │ └── DialogFooter.jsx ├── scrollbar │ ├── index.js │ ├── scrollbar-width.js │ └── util.js ├── dropdown │ ├── index.js │ ├── DropdownItem.jsx │ └── DropdownMenu.jsx ├── checkbox │ ├── index.js │ └── CheckBoxButton.jsx ├── menu │ ├── index.js │ ├── MixinComponent.jsx │ ├── MenuItem.jsx │ └── MenuItemGroup.jsx ├── locale │ ├── index.js │ └── format.js ├── message-box │ └── index.js └── index.js ├── libs ├── internal │ ├── index.js │ └── EventRegister.jsx ├── animate │ ├── index.js │ └── transition.jsx ├── utils │ ├── IDGenerator.js │ ├── react.js │ ├── assert.js │ ├── style.js │ ├── errors.js │ └── dom.js ├── props │ ├── index.js │ ├── regex.js │ └── range.js ├── index.js ├── component │ └── index.js ├── pureComponent │ └── index.js ├── view │ └── index.js ├── editor │ ├── style.scss │ └── index.jsx └── markdown │ └── index.jsx ├── site ├── pages │ ├── alert │ │ ├── style.scss │ │ └── index.jsx │ ├── select │ │ ├── style.scss │ │ └── index.jsx │ ├── card │ │ ├── hamburger.png │ │ ├── index.jsx │ │ └── style.scss │ ├── collapse │ │ ├── style.scss │ │ └── index.jsx │ ├── message │ │ ├── style.scss │ │ └── index.jsx │ ├── message-box │ │ ├── style.scss │ │ └── index.jsx │ ├── time-picker │ │ ├── style.scss │ │ └── index.jsx │ ├── notification │ │ ├── style.scss │ │ └── index.jsx │ ├── input-number │ │ ├── style.scss │ │ └── index.jsx │ ├── radio │ │ ├── style.scss │ │ └── index.jsx │ ├── i18n │ │ ├── style.scss │ │ └── index.jsx │ ├── checkbox │ │ ├── style.scss │ │ └── index.jsx │ ├── table │ │ ├── style.scss │ │ └── index.jsx │ ├── loading │ │ ├── style.scss │ │ └── index.jsx │ ├── progress │ │ ├── style.scss │ │ └── index.jsx │ ├── steps │ │ └── index.jsx │ ├── tabs │ │ └── index.jsx │ ├── transfer │ │ └── index.jsx │ ├── breadcrumb │ │ └── index.jsx │ ├── custom-theme │ │ └── index.jsx │ ├── popover │ │ ├── style.scss │ │ └── index.jsx │ ├── quick-start │ │ └── index.jsx │ ├── installation │ │ └── index.jsx │ ├── form │ │ ├── index.jsx │ │ └── style.scss │ ├── menu │ │ ├── index.jsx │ │ └── style.scss │ ├── rate │ │ ├── index.jsx │ │ └── style.scss │ ├── tag │ │ ├── index.jsx │ │ └── style.scss │ ├── tree │ │ ├── index.jsx │ │ └── style.scss │ ├── badge │ │ ├── index.jsx │ │ └── style.scss │ ├── button │ │ ├── index.jsx │ │ └── style.scss │ ├── color │ │ └── index.jsx │ ├── date-picker │ │ ├── index.jsx │ │ └── style.scss │ ├── dialog │ │ ├── index.jsx │ │ └── style.scss │ ├── layout │ │ ├── index.jsx │ │ └── style.scss │ ├── slider │ │ ├── index.jsx │ │ └── style.scss │ ├── switch │ │ ├── index.jsx │ │ └── style.scss │ ├── upload │ │ ├── index.jsx │ │ └── style.scss │ ├── tooltip │ │ ├── index.jsx │ │ └── style.scss │ ├── carousel │ │ └── index.jsx │ ├── cascader │ │ ├── index.jsx │ │ └── style.scss │ ├── datetime-picker │ │ ├── index.jsx │ │ └── style.scss │ ├── dropdown │ │ ├── index.jsx │ │ └── style.scss │ ├── pagination │ │ ├── index.jsx │ │ └── style.scss │ ├── color-picker │ │ ├── index.jsx │ │ └── style.scss │ ├── icon │ │ ├── index.jsx │ │ ├── iconList.js │ │ └── style.scss │ ├── input │ │ ├── index.jsx │ │ ├── custom-item.jsx │ │ └── style.scss │ ├── typography │ │ ├── index.jsx │ │ └── style.scss │ └── index.jsx ├── assets │ ├── favicon.ico │ └── github.png ├── locales │ ├── index.js │ ├── en-US.js │ └── zh-CN.js ├── index.html ├── docs │ ├── zh-CN │ │ ├── quick-start.md │ │ ├── icon.md │ │ ├── breadcrumb.md │ │ ├── i18n.md │ │ ├── color-picker.md │ │ ├── custom-theme.md │ │ ├── typography.md │ │ └── progress.md │ └── en-US │ │ ├── quick-start.md │ │ ├── icon.md │ │ ├── breadcrumb.md │ │ ├── color-picker.md │ │ ├── i18n.md │ │ └── typography.md ├── index.jsx └── run.js ├── .flowconfig ├── .gitignore ├── .editorconfig ├── typings └── typing-tests │ ├── tsconfig.json │ ├── Icon.tsx │ ├── Loading.tsx │ ├── ColorPicker.tsx │ ├── Card.tsx │ ├── Badge.tsx │ ├── InputNumber.tsx │ ├── Breadcrumb.tsx │ ├── Progress.tsx │ ├── Pagination.tsx │ ├── Timepicker.tsx │ ├── Tag.tsx │ ├── Slider.tsx │ ├── Switch.tsx │ ├── Collapse.tsx │ ├── AutoComplete.tsx │ ├── Alert.tsx │ ├── Input.tsx │ ├── Rate.tsx │ ├── Tabs.tsx │ ├── Upload.tsx │ ├── Select.tsx │ ├── Table.tsx │ ├── Checkbox.tsx │ ├── Dialog.tsx │ └── Transfer.tsx ├── .travis.yml ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── flow └── modules.js ├── .babelrc ├── CONTRIBUTING.md ├── LICENSE └── .eslintrc.json /.eslintignore: -------------------------------------------------------------------------------- 1 | build 2 | tests 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | build 2 | tests 3 | site 4 | .* 5 | -------------------------------------------------------------------------------- /tests/jest/mock.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = 'test-file-stub'; 3 | -------------------------------------------------------------------------------- /src/tag/index.js: -------------------------------------------------------------------------------- 1 | import Tag from './Tag' 2 | 3 | export default Tag 4 | -------------------------------------------------------------------------------- /src/tree/index.js: -------------------------------------------------------------------------------- 1 | import Tree from './Tree'; 2 | 3 | export default Tree; -------------------------------------------------------------------------------- /libs/internal/index.js: -------------------------------------------------------------------------------- 1 | export {default as EventRegister} from './EventRegister' -------------------------------------------------------------------------------- /src/card/index.js: -------------------------------------------------------------------------------- 1 | import Card from './Card'; 2 | 3 | export default Card; 4 | -------------------------------------------------------------------------------- /src/icon/index.js: -------------------------------------------------------------------------------- 1 | import Icon from './Icon'; 2 | 3 | export default Icon; 4 | -------------------------------------------------------------------------------- /src/rate/index.js: -------------------------------------------------------------------------------- 1 | import Rate from './Rate'; 2 | 3 | export default Rate; 4 | -------------------------------------------------------------------------------- /src/alert/index.js: -------------------------------------------------------------------------------- 1 | import Alert from './Alert'; 2 | 3 | export default Alert; 4 | -------------------------------------------------------------------------------- /src/badge/index.js: -------------------------------------------------------------------------------- 1 | import Badge from './Badge' 2 | 3 | export default Badge 4 | -------------------------------------------------------------------------------- /src/input/index.js: -------------------------------------------------------------------------------- 1 | import Input from './Input'; 2 | 3 | export default Input; 4 | -------------------------------------------------------------------------------- /src/switch/index.js: -------------------------------------------------------------------------------- 1 | import Switch from './Switch'; 2 | 3 | export default Switch; -------------------------------------------------------------------------------- /src/slider/index.js: -------------------------------------------------------------------------------- 1 | import Slider from './Slider'; 2 | 3 | export default Slider; 4 | -------------------------------------------------------------------------------- /src/upload/index.js: -------------------------------------------------------------------------------- 1 | import Upload from './Upload'; 2 | 3 | export default Upload; 4 | -------------------------------------------------------------------------------- /src/loading/index.js: -------------------------------------------------------------------------------- 1 | import Loading from './Loading'; 2 | 3 | export default Loading; 4 | -------------------------------------------------------------------------------- /src/message/index.js: -------------------------------------------------------------------------------- 1 | import Message from './Message'; 2 | 3 | export default Message; 4 | -------------------------------------------------------------------------------- /src/popover/index.js: -------------------------------------------------------------------------------- 1 | import Popover from './Popover'; 2 | 3 | export default Popover; 4 | -------------------------------------------------------------------------------- /src/tooltip/index.js: -------------------------------------------------------------------------------- 1 | import Tooltip from './Tooltip'; 2 | 3 | export default Tooltip; 4 | -------------------------------------------------------------------------------- /site/pages/alert/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-alert .el-alert { 2 | margin-bottom: 20px; 3 | } 4 | -------------------------------------------------------------------------------- /src/cascader/index.js: -------------------------------------------------------------------------------- 1 | import Cascader from './Cascader'; 2 | 3 | export default Cascader; 4 | -------------------------------------------------------------------------------- /src/pagination/index.js: -------------------------------------------------------------------------------- 1 | import Pagination from './Pagination'; 2 | 3 | export default Pagination; -------------------------------------------------------------------------------- /src/progress/index.js: -------------------------------------------------------------------------------- 1 | import Progress from './Progress'; 2 | 3 | export default Progress; 4 | -------------------------------------------------------------------------------- /src/transfer/index.js: -------------------------------------------------------------------------------- 1 | import Transfer from './Transfer'; 2 | 3 | export default Transfer; 4 | -------------------------------------------------------------------------------- /libs/animate/index.js: -------------------------------------------------------------------------------- 1 | import Transition from './transition' 2 | 3 | export default { Transition } 4 | -------------------------------------------------------------------------------- /site/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElemeFE/element-react/HEAD/site/assets/favicon.ico -------------------------------------------------------------------------------- /site/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElemeFE/element-react/HEAD/site/assets/github.png -------------------------------------------------------------------------------- /src/color-picker/index.js: -------------------------------------------------------------------------------- 1 | import ColorPicker from './ColorPicker'; 2 | 3 | export default ColorPicker; 4 | -------------------------------------------------------------------------------- /src/input-number/index.js: -------------------------------------------------------------------------------- 1 | import InputNumber from './InputNumber'; 2 | 3 | export default InputNumber; 4 | -------------------------------------------------------------------------------- /site/pages/select/style.scss: -------------------------------------------------------------------------------- 1 | .demo-select .el-select { 2 | display: inline-block; 3 | width: 240px; 4 | } 5 | -------------------------------------------------------------------------------- /src/auto-complete/index.js: -------------------------------------------------------------------------------- 1 | import AutoComplete from './AutoComplete'; 2 | 3 | export default AutoComplete; 4 | -------------------------------------------------------------------------------- /site/pages/card/hamburger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElemeFE/element-react/HEAD/site/pages/card/hamburger.png -------------------------------------------------------------------------------- /site/locales/index.js: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | 'zh-CN': require('./zh-CN'), 4 | 'en-US': require('./en-US') 5 | } 6 | -------------------------------------------------------------------------------- /site/pages/collapse/style.scss: -------------------------------------------------------------------------------- 1 | .demo-collapse .el-collapse-item__header .header-icon { 2 | margin-left: 5px; 3 | } 4 | -------------------------------------------------------------------------------- /src/notification/index.js: -------------------------------------------------------------------------------- 1 | import NotificationCenter from './NotificationCenter'; 2 | 3 | export default NotificationCenter; 4 | -------------------------------------------------------------------------------- /site/pages/message/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-message { 2 | .el-button + .el-button { 3 | margin-left: 10px; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/layout/index.js: -------------------------------------------------------------------------------- 1 | import Row from './Row'; 2 | import Col from './Col'; 3 | 4 | export default { 5 | Row, 6 | Col 7 | } 8 | -------------------------------------------------------------------------------- /site/pages/message-box/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-message { 2 | .el-button + .el-button { 3 | margin-left: 10px; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /site/pages/time-picker/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box { 2 | .el-date-editor + .el-date-editor { 3 | margin-left: 10px; 4 | } 5 | } -------------------------------------------------------------------------------- /site/pages/notification/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-notification { 2 | .el-button + .el-button { 3 | margin-left: 10px; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/steps/index.js: -------------------------------------------------------------------------------- 1 | import Steps from './Steps'; 2 | import Step from './Step'; 3 | 4 | Steps.Step = Step; 5 | 6 | export default Steps; 7 | -------------------------------------------------------------------------------- /src/tabs/index.js: -------------------------------------------------------------------------------- 1 | import Tabs from './Tabs' 2 | import TabPane from './TabPane' 3 | 4 | Tabs.Pane = TabPane; 5 | 6 | export default Tabs; 7 | -------------------------------------------------------------------------------- /src/form/index.js: -------------------------------------------------------------------------------- 1 | import Form from './Form'; 2 | import FormItem from './FormItem'; 3 | 4 | Form.Item = FormItem; 5 | 6 | export default Form; 7 | -------------------------------------------------------------------------------- /site/pages/input-number/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-inputnumber { 2 | .el-input-number+.el-input-number { 3 | margin-left: 10px; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /site/pages/radio/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-radio { 2 | .el-radio-group:nth-of-type(2) { 3 | display: block; 4 | margin: 15px 0; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/.* 3 | 4 | [include] 5 | 6 | [libs] 7 | flow 8 | 9 | [options] 10 | esproposal.class_static_fields=enable 11 | -------------------------------------------------------------------------------- /libs/utils/IDGenerator.js: -------------------------------------------------------------------------------- 1 | export class IDGenerator { 2 | constructor(){ 3 | this.id = 0 4 | } 5 | 6 | next(){ 7 | return this.id++ & 0xffff 8 | } 9 | } -------------------------------------------------------------------------------- /src/button/index.js: -------------------------------------------------------------------------------- 1 | import Button from './Button'; 2 | import ButtonGroup from './ButtonGroup'; 3 | 4 | Button.Group = ButtonGroup; 5 | 6 | export default Button; 7 | -------------------------------------------------------------------------------- /src/table/index.js: -------------------------------------------------------------------------------- 1 | import Table from './TableStore'; 2 | import TableColumn from './TableColumn'; 3 | 4 | Table.Column = TableColumn; 5 | 6 | export default Table; -------------------------------------------------------------------------------- /site/pages/i18n/style.scss: -------------------------------------------------------------------------------- 1 | ul.language-list { 2 | color: #5e6d82; 3 | font-size: 14px; 4 | padding-left: 20px; 5 | li { 6 | line-height: 1.8 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /site/pages/checkbox/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-checkbox { 2 | .checkbox { 3 | margin-right: 5px; 4 | & + .checkbox { 5 | margin-left: 10px; 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /src/carousel/index.js: -------------------------------------------------------------------------------- 1 | import Carousel from './Carousel'; 2 | import CarouselItem from './CarouselItem'; 3 | 4 | Carousel.Item = CarouselItem; 5 | 6 | export default Carousel; 7 | -------------------------------------------------------------------------------- /src/collapse/index.js: -------------------------------------------------------------------------------- 1 | import Collapse from './Collapse'; 2 | import CollapseItem from './CollapseItem'; 3 | 4 | Collapse.Item = CollapseItem; 5 | 6 | export default Collapse; 7 | -------------------------------------------------------------------------------- /src/date-picker/basic/index.js: -------------------------------------------------------------------------------- 1 | export {default as DateTable} from './DateTable' 2 | export {default as MonthTable} from './MonthTable' 3 | export {default as YearTable} from './YearTable' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | .idea 4 | 5 | npm-debug.log 6 | node_modules 7 | 8 | /dist 9 | /next.js 10 | 11 | .vscode 12 | Makefile 13 | yarn.lock 14 | package-lock.json 15 | -------------------------------------------------------------------------------- /src/breadcrumb/index.js: -------------------------------------------------------------------------------- 1 | import Breadcrumb from './Breadcrumb'; 2 | import BreadcrumbItem from './BreadcrumbItem'; 3 | 4 | Breadcrumb.Item = BreadcrumbItem; 5 | 6 | export default Breadcrumb; 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | 7 | [README.md] 8 | indent_style = space 9 | indent_size = 4 10 | 11 | [Makefile] 12 | indent_style = tab 13 | -------------------------------------------------------------------------------- /site/pages/table/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-table { 2 | .el-table .info-row { 3 | background: #c9e5f5; 4 | } 5 | 6 | .el-table .positive-row { 7 | background: #e2f0e4; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/loading/style.scss: -------------------------------------------------------------------------------- 1 | .demo-loading { 2 | .el-loading-demo { 3 | border-radius: 4px; 4 | overflow: hidden; 5 | } 6 | 7 | .el-loading-mask { 8 | height: 100px; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /site/pages/progress/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-progress { 2 | .el-progress--line { 3 | margin-bottom: 15px; 4 | width: 350px; 5 | } 6 | .el-progress--circle { 7 | margin-right: 15px; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/steps/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | export default class Steps extends Markdown { 4 | document(locale) { 5 | return require(`../../docs/${locale}/steps.md`); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /site/pages/tabs/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | export default class Tabs extends Markdown { 4 | document(locale) { 5 | return require(`../../docs/${locale}/tabs.md`); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /libs/utils/react.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | 4 | function firstChild(props) { 5 | const childrenArray = React.Children.toArray(props.children); 6 | return childrenArray[0] || null; 7 | } 8 | 9 | export {firstChild} -------------------------------------------------------------------------------- /site/pages/transfer/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | export default class Tooltip extends Markdown { 4 | document(locale) { 5 | return require(`../../docs/${locale}/transfer.md`); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/radio/index.js: -------------------------------------------------------------------------------- 1 | import Radio from './Radio'; 2 | import RadioButton from './RadioButton'; 3 | import RadioGroup from './RadioGroup'; 4 | 5 | Radio.Button = RadioButton; 6 | Radio.Group = RadioGroup; 7 | 8 | export default Radio; 9 | -------------------------------------------------------------------------------- /src/select/index.js: -------------------------------------------------------------------------------- 1 | import Select from './Select'; 2 | import Option from './Option'; 3 | import OptionGroup from './OptionGroup'; 4 | 5 | Select.Option = Option; 6 | Select.OptionGroup = OptionGroup; 7 | 8 | export default Select; 9 | -------------------------------------------------------------------------------- /site/pages/breadcrumb/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | export default class Breadcrumb extends Markdown { 4 | document(locale) { 5 | return require(`../../docs/${locale}/breadcrumb.md`); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /site/pages/custom-theme/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | export default class CustomTheme extends Markdown { 4 | document(locale) { 5 | return require(`../../docs/${locale}/custom-theme.md`); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /site/pages/popover/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-popover { 2 | .el-popover + .el-popover { 3 | margin-left: 10px; 4 | } 5 | .el-input { 6 | width: 360px; 7 | } 8 | .el-button { 9 | margin-left: 10px; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /site/pages/quick-start/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | export default class QuickStart extends Markdown { 4 | document(locale) { 5 | return require(`../../docs/${locale}/quick-start.md`); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/dialog/index.js: -------------------------------------------------------------------------------- 1 | import DialogBody from './DialogBody'; 2 | import DialogFooter from './DialogFooter'; 3 | import Dialog from './Dialog'; 4 | 5 | Dialog.Body = DialogBody; 6 | Dialog.Footer = DialogFooter; 7 | 8 | export default Dialog; 9 | -------------------------------------------------------------------------------- /site/pages/installation/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | export default class Installation extends Markdown { 4 | document(locale) { 5 | return require(`../../docs/${locale}/installation.md`); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/jest/config.js: -------------------------------------------------------------------------------- 1 | require('./polyfill'); 2 | 3 | const Enzyme = require('enzyme'); 4 | const EnzymeAdapter = require('enzyme-adapter-react-16'); 5 | 6 | // Setup enzyme's react adapter 7 | Enzyme.configure({ adapter: new EnzymeAdapter() }); 8 | -------------------------------------------------------------------------------- /libs/props/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | import rangeType from './range'; 4 | import regexType from './regex'; 5 | 6 | PropTypes.range = rangeType; 7 | PropTypes.regex = regexType; 8 | 9 | export default PropTypes; 10 | -------------------------------------------------------------------------------- /src/scrollbar/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | this is trimmed version compared to the version in vue-element lib. 3 | native functionality are leftout. 4 | 5 | revision version: 6 | 48996c6658300b7f9f01b9b4011a531f17e30b75 7 | */ 8 | export {Scrollbar} from './Scrollbar' -------------------------------------------------------------------------------- /src/dropdown/index.js: -------------------------------------------------------------------------------- 1 | import Dropdown from './Dropdown'; 2 | import DropdownMenu from './DropdownMenu'; 3 | import DropdownItem from './DropdownItem'; 4 | 5 | Dropdown.Item = DropdownItem; 6 | Dropdown.Menu = DropdownMenu; 7 | 8 | export default Dropdown; 9 | -------------------------------------------------------------------------------- /site/pages/form/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Form extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/form.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/i18n/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class i18n extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/i18n.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/menu/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Menu extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/menu.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/rate/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Rate extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/rate.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/tag/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Tag extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/tag.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/tree/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Tree extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/tree.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/tree/style.scss: -------------------------------------------------------------------------------- 1 | .demo-tree { 2 | .leaf { 3 | width: 20px; 4 | background: #ddd; 5 | } 6 | 7 | .folder { 8 | width: 20px; 9 | background: #888; 10 | } 11 | 12 | .buttons { 13 | margin-top: 20px; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /typings/typing-tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "module": "es2015", 5 | "noEmit": true, 6 | "jsx": "react" 7 | }, 8 | "include": [ 9 | "../index.d.ts", 10 | "." 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /site/pages/alert/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Alert extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/alert.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/badge/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Badge extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/badge.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/button/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Button extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/button.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/color/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Color extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/color.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/date-picker/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | import './style.scss'; 3 | 4 | export default class DatePicker extends Markdown { 5 | document(locale) { 6 | return require(`../../docs/${locale}/date-picker.md`); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /site/pages/dialog/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Dialog extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/dialog.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/layout/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Layout extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/layout.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/radio/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Radio extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/radio.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/select/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Select extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/select.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/slider/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Slider extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/slider.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/switch/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Switch extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/switch.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/table/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Table extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/table.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/upload/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Upload extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/upload.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/checkbox/index.js: -------------------------------------------------------------------------------- 1 | import Checkbox from './CheckBox'; 2 | import CheckboxGroup from './CheckBoxGroup'; 3 | import CheckboxButton from './CheckBoxButton'; 4 | 5 | Checkbox.Group = CheckboxGroup; 6 | Checkbox.Button = CheckboxButton; 7 | 8 | export default Checkbox; 9 | -------------------------------------------------------------------------------- /src/table/TableColumn.js: -------------------------------------------------------------------------------- 1 | // import * as React from 'react'; 2 | // import { Component, PropTypes } from '../../libs'; 3 | 4 | function TableColumn() { 5 | return null; 6 | } 7 | 8 | TableColumn.typeName = 'TableColumn'; 9 | 10 | export default TableColumn; -------------------------------------------------------------------------------- /site/pages/loading/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Loading extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/loading.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/message/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Message extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/message.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/popover/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Popover extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/popover.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/tooltip/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Tooltip extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/tooltip.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/carousel/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Carousel extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/carousel.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/cascader/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Cascader extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/cascader.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/checkbox/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Checkbox extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/checkbox.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/collapse/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Collapse extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/collapse.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/datetime-picker/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | import './style.scss'; 3 | 4 | export default class DatePicker extends Markdown { 5 | document(locale) { 6 | return require(`../../docs/${locale}/datetime-picker.md`); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /site/pages/dropdown/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Dropdown extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/dropdown.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/progress/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Progress extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/progress.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/message-box/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class MessageBox extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/message-box.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/pagination/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Pagination extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/pagination.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/time-picker/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class TimePicker extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/time-picker.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/color-picker/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class ColorPicker extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/color-picker.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/input-number/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class InputNumber extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/input-number.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/pages/notification/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Notification extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/notification.md`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/menu/index.js: -------------------------------------------------------------------------------- 1 | import Menu from './Menu'; 2 | import SubMenu from './SubMenu'; 3 | import MenuItem from './MenuItem'; 4 | import MenuItemGroup from './MenuItemGroup'; 5 | 6 | Menu.SubMenu = SubMenu; 7 | Menu.Item = MenuItem; 8 | Menu.ItemGroup = MenuItemGroup; 9 | 10 | export default Menu; 11 | -------------------------------------------------------------------------------- /tests/jest/polyfill.js: -------------------------------------------------------------------------------- 1 | global.document.createRange = () => ({ 2 | setStart: f => f, 3 | setEnd: f => f, 4 | commonAncestorContainer: { 5 | nodeName: "BODY", 6 | ownerDocument: document, 7 | }, 8 | }) 9 | 10 | global.requestAnimationFrame = (cb) => { 11 | setTimeout(cb, 0) 12 | } 13 | -------------------------------------------------------------------------------- /site/pages/icon/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Icon extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/icon.md`); 8 | } 9 | } 10 | 11 | Icon.defaultProps = { 12 | iconList: require('./iconList') 13 | }; 14 | -------------------------------------------------------------------------------- /libs/props/regex.js: -------------------------------------------------------------------------------- 1 | import { createPropType } from '../utils'; 2 | 3 | export default createPropType((props, propName, componentName) => { 4 | const value = props[propName]; 5 | 6 | if (!(value instanceof RegExp)) { 7 | return new Error(`Invalid prop ${propName} of ${componentName}, should be valid regex.`); 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /site/pages/card/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Card extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/card.md`); 8 | } 9 | } 10 | 11 | Card.defaultProps = { 12 | imgSrc: require('./hamburger.png') 13 | } 14 | -------------------------------------------------------------------------------- /site/pages/input/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Input extends Markdown { 6 | document(locale) { 7 | return require(`../../docs/${locale}/input.md`); 8 | } 9 | } 10 | 11 | Input.defaultProps = { 12 | customItem: require('./custom-item') 13 | } 14 | -------------------------------------------------------------------------------- /site/pages/typography/index.jsx: -------------------------------------------------------------------------------- 1 | import Markdown from '../../../libs/markdown'; 2 | 3 | import './style.scss'; 4 | 5 | export default class Typography extends Markdown { 6 | documentShouldTransform() { 7 | return false; 8 | } 9 | 10 | document(locale) { 11 | return require(`../../docs/${locale}/typography.md`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Element-React 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/icon/Icon.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Component, PropTypes } from '../../libs'; 3 | 4 | export default class Icon extends Component { 5 | render() { 6 | return ; 7 | } 8 | } 9 | 10 | Icon.propTypes = { 11 | name: PropTypes.string 12 | } 13 | -------------------------------------------------------------------------------- /libs/utils/assert.js: -------------------------------------------------------------------------------- 1 | import { ExtendableError } from './errors' 2 | 3 | class ErrorConditionFailed extends ExtendableError { 4 | constructor(...args) { 5 | super(args) 6 | } 7 | } 8 | 9 | export function require_condition(condition, msg = 'pre-condition failed') { 10 | if (!condition) { 11 | throw new ErrorConditionFailed(msg) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" 4 | 5 | env: 6 | - TRAVIS_CI=1 7 | 8 | cache: 9 | yarn: true 10 | directories: 11 | - node_modules 12 | 13 | install: 14 | npm i 15 | 16 | script: 17 | - npm run lint 18 | - npm run build 19 | - npm test 20 | - npm run typescript-test 21 | 22 | notifications: 23 | slack: eleme:3Nouwh5OgNugxccCsiFYpUIB 24 | -------------------------------------------------------------------------------- /libs/utils/style.js: -------------------------------------------------------------------------------- 1 | exports.reset = css => { 2 | const style = document.createElement('style'); 3 | 4 | style.type = 'text/css'; 5 | 6 | if (style.styleSheet){ 7 | style.styleSheet.cssText = css; 8 | } else { 9 | style.appendChild(document.createTextNode(css)); 10 | } 11 | 12 | (document.head || document.getElementsByTagName('head')[0]).appendChild(style); 13 | } 14 | -------------------------------------------------------------------------------- /src/button/ButtonGroup.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component } from '../../libs'; 5 | 6 | export default class ButtonGroup extends Component { 7 | render(): React.DOM { 8 | return ( 9 |
10 | {this.props.children} 11 |
12 | ) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/dialog/DialogBody.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component } from '../../libs'; 5 | 6 | export default class DialogBody extends Component { 7 | render(): React.DOM { 8 | return ( 9 |
10 | { this.props.children } 11 |
12 | ) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/date-picker/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | revision version: 3 | 48996c6658300b7f9f01b9b4011a531f17e30b75 4 | */ 5 | export { default as TimeSelect } from './TimeSelect' 6 | export { default as TimePicker } from './TimePicker' 7 | export { default as TimeRangePicker } from './TimeRangePicker' 8 | export { default as DatePicker } from './DatePicker' 9 | export { default as DateRangePicker } from './DateRangePicker' -------------------------------------------------------------------------------- /src/dialog/DialogFooter.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component } from '../../libs'; 5 | 6 | export default class DialogFooter extends Component { 7 | render(): React.DOM { 8 | return ( 9 |
10 | { this.props.children } 11 |
12 | ) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /libs/props/range.js: -------------------------------------------------------------------------------- 1 | import { createPropType } from '../utils'; 2 | 3 | export default function(min, max) { 4 | return createPropType((props, propName, componentName) => { 5 | const value = props[propName]; 6 | 7 | if (value < min || value > max) { 8 | return new Error(`Invalid prop ${propName} of ${componentName}, should between ${min} and ${max}.`); 9 | } 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /site/pages/dialog/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-dialog { 2 | .dialog-footer button:first-child { 3 | margin-right: 10px; 4 | } 5 | .full-image { 6 | width: 100%; 7 | } 8 | .el-dialog__wrapper { 9 | margin: 0; 10 | } 11 | .el-select { 12 | width: 300px; 13 | } 14 | .el-input { 15 | width: 300px; 16 | } 17 | .el-button--text { 18 | margin-right: 15px; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /site/pages/tag/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-tag { 2 | .el-tag { 3 | margin-right: 10px; 4 | } 5 | 6 | .button-new-tag { 7 | height: 24px; 8 | line-height: 22px; 9 | padding-top: 0; 10 | padding-bottom: 0; 11 | } 12 | 13 | .input-new-tag { 14 | width: 78px; 15 | vertical-align: bottom; 16 | 17 | .el-input__inner { 18 | height: 24px; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/tree/model/util.js: -------------------------------------------------------------------------------- 1 | export const NODE_KEY = '$treeNodeId'; 2 | 3 | export const markNodeData = function(node, data) { 4 | if (data[NODE_KEY]) return; 5 | Object.defineProperty(data, NODE_KEY, { 6 | value: node.id, 7 | enumerable: false, 8 | configurable: false, 9 | writable: false 10 | }); 11 | }; 12 | 13 | export const getNodeKey = function(key, data) { 14 | if (!key) return data[NODE_KEY]; 15 | return data[key]; 16 | }; 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please makes sure these boxes are checked before submitting your PR, thank you! 2 | 3 | * [ ] Make sure you are merging your commits to `master` branch. 4 | * [ ] Add some descriptions and refer relative issues for you PR. 5 | * [ ] Rebase your commits to make your pull request meaningful. 6 | * [ ] Make sure that your changes pass `npm test`, `npm run lint` and `npm run build`. 7 | 8 | Changes in this pull request 9 | 10 | - 11 | -------------------------------------------------------------------------------- /site/pages/date-picker/style.scss: -------------------------------------------------------------------------------- 1 | .demo-datepicker .source { 2 | padding: 0; 3 | display: flex; 4 | width: 100%; 5 | } 6 | 7 | .demo-datepicker .block { 8 | padding: 30px 0; 9 | text-align: center; 10 | border-right: solid 1px #EFF2F6; 11 | flex: 1; 12 | &:last-child { 13 | border-right: none; 14 | } 15 | } 16 | 17 | .demo-datepicker .demonstration { 18 | display: block; 19 | color: #8492a6; 20 | font-size: 14px; 21 | margin-bottom: 20px; 22 | } -------------------------------------------------------------------------------- /flow/modules.js: -------------------------------------------------------------------------------- 1 | declare module 'popper.js' { 2 | declare export default any; 3 | } 4 | 5 | declare module 'classnames' { 6 | declare var exports: { 7 | (): any 8 | } 9 | } 10 | 11 | declare module 'react-click-outside' { 12 | declare module.exports: any; 13 | } 14 | 15 | declare module 'throttle-debounce' { 16 | declare var debounce: any; 17 | declare var throttle: any; 18 | } 19 | 20 | declare module 'async-validator' { 21 | declare export default any; 22 | } 23 | -------------------------------------------------------------------------------- /site/pages/datetime-picker/style.scss: -------------------------------------------------------------------------------- 1 | .demo-datetimepicker .source { 2 | padding: 0; 3 | display: flex; 4 | width: 100%; 5 | } 6 | 7 | .demo-datetimepicker .block { 8 | padding: 30px 0; 9 | text-align: center; 10 | border-right: solid 1px #EFF2F6; 11 | flex: 1; 12 | &:last-child { 13 | border-right: none; 14 | } 15 | } 16 | 17 | .demo-datetimepicker .demonstration { 18 | display: block; 19 | color: #8492a6; 20 | font-size: 14px; 21 | margin-bottom: 20px; 22 | } -------------------------------------------------------------------------------- /site/pages/input/custom-item.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | class CustomItem extends React.Component { 5 | render() { 6 | return ( 7 |
8 |
{this.props.item.value}
9 | {this.props.item.address} 10 |
11 | ) 12 | } 13 | } 14 | 15 | CustomItem.propTypes = { 16 | item: PropTypes.object 17 | }; 18 | 19 | module.exports = CustomItem; 20 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "development": { 4 | "presets" : ["env", "stage-1", "react"], 5 | "plugins" : ["react-hot-loader/babel", ["transform-runtime", { "polyfill": false }]] 6 | }, 7 | "test": { 8 | "presets" : ["env", "stage-1", "react"] 9 | }, 10 | "production": { 11 | "presets" : [["env", { "modules": false, "loose": true }], "stage-1", "react"], 12 | "plugins" : [["transform-runtime", { "polyfill": false }]] 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /site/pages/badge/style.scss: -------------------------------------------------------------------------------- 1 | .demo-badge { 2 | .el-badge { 3 | margin-right: 40px; 4 | } 5 | 6 | .el-dropdown { 7 | vertical-align: middle !important; 8 | } 9 | 10 | .share-button { 11 | width: 36px; 12 | padding: 10px; 13 | } 14 | 15 | .mark { 16 | margin-right: 0; 17 | } 18 | 19 | .clearfix { 20 | display: flex; 21 | flex-direction: row; 22 | justify-content: space-between; 23 | align-items: center; 24 | line-height: 1; 25 | padding: 7px; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /site/pages/slider/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-slider .source { 2 | padding: 0; 3 | } 4 | 5 | .demo-box.demo-slider .block { 6 | padding: 30px 24px; 7 | border-bottom: solid 1px #EFF2F6; 8 | 9 | &:last-child { 10 | border-bottom: none; 11 | } 12 | } 13 | 14 | .demo-box.demo-slider .demonstration { 15 | font-size: 14px; 16 | color: #8492a6; 17 | line-height: 44px; 18 | } 19 | 20 | .demo-box.demo-slider .demonstration + .el-slider { 21 | float: right; 22 | width: 70%; 23 | margin-right: 20px; 24 | } 25 | -------------------------------------------------------------------------------- /libs/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Only exposing the libraries for the public components, 3 | * Internal components such as Markdown shouldn't be here. 4 | */ 5 | 6 | export { default as CollapseTransition } from './collapse'; 7 | export { default as Transition } from './transition'; 8 | export { default as Component } from './component'; 9 | export { default as PureComponent } from './pureComponent'; 10 | export { default as PropTypes } from './props'; 11 | export { default as View } from './view'; 12 | export { default as Animate } from './animate'; 13 | -------------------------------------------------------------------------------- /site/docs/zh-CN/quick-start.md: -------------------------------------------------------------------------------- 1 | ## 快速上手 2 | 3 | ### 安装 4 | 推荐使用 npm 的方式安装,它能更好地和`webpack`打包工具配合使用。 5 | 6 | ```shell 7 | npm i element-react --save 8 | ``` 9 | 10 | ### 主题 11 | 开始前, 你还需要一个主题包, 这里我们推荐使用`element-theme-default`. 12 | 13 | ```shell 14 | npm install element-theme-default --save 15 | ``` 16 | 17 | ### 使用 18 | 19 | ```js 20 | import React from 'react'; 21 | import ReactDOM from 'react-dom'; 22 | import { Button } from 'element-react'; 23 | 24 | import 'element-theme-default'; 25 | 26 | ReactDOM.render(, document.getElementById('app')); 27 | 28 | ``` 29 | -------------------------------------------------------------------------------- /site/pages/layout/style.scss: -------------------------------------------------------------------------------- 1 | .demo-layout { 2 | .el-row { 3 | margin-bottom: 20px; 4 | &:last-child { 5 | margin-bottom: 0; 6 | } 7 | } 8 | .el-col { 9 | border-radius: 4px; 10 | } 11 | .bg-purple-dark { 12 | background: #99a9bf; 13 | } 14 | .bg-purple { 15 | background: #d3dce6; 16 | } 17 | .bg-purple-light { 18 | background: #e5e9f2; 19 | } 20 | .grid-content { 21 | border-radius: 4px; 22 | min-height: 36px; 23 | } 24 | .row-bg { 25 | padding: 10px 0; 26 | background-color: #f9fafc; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /site/pages/menu/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-menu { 2 | .el-menu-demo { 3 | padding-left: 55px; 4 | } 5 | .el-menu-vertical-demo { 6 | width: 200px; 7 | min-height: 400px; 8 | } 9 | .line { 10 | height: 1px; 11 | background-color: #e0e6ed; 12 | margin: 35px -24px; 13 | } 14 | h5 { 15 | font-size: 14px; 16 | color: #8492a6; 17 | margin-top: 10px; 18 | margin-bottom: 15px; 19 | } 20 | .tac { 21 | text-align: center; 22 | 23 | .el-menu-vertical-demo { 24 | display: inline-block; 25 | text-align: left; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /typings/typing-tests/Icon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Icon } from 'element-react' 3 | import { Icon as IconNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | onClose = () => { } 7 | render() { 8 | return ( 9 |
10 | 11 | 12 | 13 | 14 | 15 |
16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /site/pages/card/style.scss: -------------------------------------------------------------------------------- 1 | .demo-card { 2 | .text { 3 | font-size: 14px; 4 | } 5 | 6 | .item { 7 | padding: 18px 0; 8 | } 9 | 10 | .box-card { 11 | width: 480px; 12 | } 13 | 14 | .image { 15 | width: 100%; 16 | display: block; 17 | } 18 | 19 | .bottom { 20 | margin-top: 13px; 21 | line-height: 12px; 22 | } 23 | 24 | .time { 25 | font-size: 13px; 26 | color: #999; 27 | } 28 | 29 | .clearfix:before, .clearfix:after { 30 | display: table; 31 | content: ""; 32 | } 33 | 34 | .button { 35 | padding: 0; 36 | float: right; 37 | } 38 | } -------------------------------------------------------------------------------- /src/breadcrumb/BreadcrumbItem.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | export default class BreadcrumbItem extends Component { 7 | render() { 8 | return ( 9 | 10 | {this.props.children} 11 | {this.context.separator} 12 | 13 | ) 14 | } 15 | } 16 | 17 | BreadcrumbItem.contextTypes = { 18 | separator: PropTypes.string 19 | }; 20 | -------------------------------------------------------------------------------- /src/date-picker/__test__/utils.js: -------------------------------------------------------------------------------- 1 | import createMockRaf from 'mock-raf' 2 | // import sinon from 'sinon' 3 | 4 | export function mockRAf(count = 10){ 5 | let mockRaf = createMockRaf(); 6 | // Stub out your `requestAnimationFrame` method 7 | global.requestAnimationFrame = mockRaf.raf; 8 | global.cancelAnimationFrame = ()=>{} 9 | // sinon.stub(global, 'requestAnimationFrame').callsFake(mockRaf.raf); 10 | // Take 10 `requestAnimationFrame` steps (your callback will fire 10 times) 11 | mockRaf.step({ count }); 12 | } 13 | 14 | export const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => { } } } 15 | -------------------------------------------------------------------------------- /src/locale/index.js: -------------------------------------------------------------------------------- 1 | import format from './format'; 2 | import defaultLang from './lang/zh-CN'; 3 | 4 | let _lang = defaultLang 5 | 6 | function use(lang) { 7 | _lang = lang; 8 | } 9 | 10 | function t(path, options) { 11 | const array = path.split('.'); 12 | let current = _lang; 13 | 14 | for (var i = 0, j = array.length; i < j; i++) { 15 | var property = array[i]; 16 | var value = current[property]; 17 | if (i === j - 1) return format(value, options); 18 | if (!value) return ''; 19 | current = value; 20 | } 21 | return ''; 22 | } 23 | 24 | export default { 25 | use, 26 | t 27 | } 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | Explain the issue you met, and describe what you expected to be. If you can write in English, we will be very grateful. 4 | 5 | ### Reproduce Steps 6 | 7 | 1. [First Step] 8 | 2. [Second Step] 9 | 3. [and so on...] 10 | 11 | ### Error Trace (if possible) 12 | 13 | It's better to post up the error stack for us to trace this issue. 14 | 15 | ### Solution 16 | 17 | What needs to be done to address this issue? Ideally, provide a pull request with a fix. 18 | 19 | ### Additional Information 20 | 21 | Any additional information, configuration or data that might be necessary to reproduce the issue. 22 | -------------------------------------------------------------------------------- /src/select/OptionGroup.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | export default class OptionGroup extends Component { 7 | render() { 8 | return ( 9 | 17 | ) 18 | } 19 | } 20 | 21 | OptionGroup.propTypes = { 22 | label: PropTypes.string, 23 | }; 24 | -------------------------------------------------------------------------------- /libs/component/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classnames from 'classnames'; 4 | 5 | export default class Component extends React.Component { 6 | classNames(...args) { 7 | return classnames(args); 8 | } 9 | 10 | className(...args) { 11 | const { className } = this.props; 12 | return this.classNames.apply(this, args.concat([className])); 13 | } 14 | 15 | style(args) { 16 | const { style } = this.props; 17 | return Object.assign({}, args, style) 18 | } 19 | } 20 | 21 | Component.propTypes = { 22 | className: PropTypes.string, 23 | style: PropTypes.object 24 | }; 25 | -------------------------------------------------------------------------------- /src/date-picker/panel/PopperBase.js: -------------------------------------------------------------------------------- 1 | import { PropTypes, Component } from '../../../libs'; 2 | import { PopperReactMixin } from '../../../libs/utils' 3 | 4 | export class PopperBase extends Component{ 5 | static get propTypes() { 6 | return { 7 | //()=>HtmlElement 8 | getPopperRefElement: PropTypes.func, 9 | popperMixinOption: PropTypes.object 10 | } 11 | } 12 | 13 | constructor(props) { 14 | super(props) 15 | 16 | PopperReactMixin.call(this, () => this.refs.root, props.getPopperRefElement, Object.assign({ 17 | boundariesPadding: 0, 18 | gpuAcceleration: false 19 | }, props.popperMixinOption)); 20 | } 21 | } -------------------------------------------------------------------------------- /libs/utils/errors.js: -------------------------------------------------------------------------------- 1 | //taken from : http://stackoverflow.com/questions/31089801/extending-error-in-javascript-with-es6-syntax 2 | export class ExtendableError extends Error { 3 | constructor(message) { 4 | super(message); 5 | this.name = this.constructor.name; 6 | this.message = message; 7 | if (typeof Error.captureStackTrace === 'function') { 8 | Error.captureStackTrace(this, this.constructor); 9 | } else { 10 | this.stack = (new Error(message)).stack; 11 | } 12 | } 13 | } 14 | 15 | export class MethodImplementationRequiredError extends ExtendableError { 16 | constructor(msg){ 17 | super(msg) 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /libs/pureComponent/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classnames from 'classnames'; 4 | 5 | export default class PureComponent extends React.PureComponent { 6 | classNames(...args) { 7 | return classnames(args); 8 | } 9 | 10 | className(...args) { 11 | const { className } = this.props; 12 | return this.classNames.apply(this, args.concat([className])); 13 | } 14 | 15 | style(args) { 16 | const { style } = this.props; 17 | return Object.assign({}, args, style) 18 | } 19 | } 20 | 21 | PureComponent.propTypes = { 22 | className: PropTypes.string, 23 | style: PropTypes.object 24 | }; 25 | -------------------------------------------------------------------------------- /site/pages/cascader/style.scss: -------------------------------------------------------------------------------- 1 | .demo-cascader { 2 | .el-cascader { 3 | width: 222px; 4 | text-align: left; 5 | } 6 | } 7 | 8 | .demo-cascader-size { 9 | .el-cascader { 10 | vertical-align: top; 11 | margin-right: 15px; 12 | } 13 | } 14 | 15 | .demo-cascader .block { 16 | padding: 30px 0; 17 | text-align: center; 18 | border-right: solid 1px #EFF2F6; 19 | display: inline-block; 20 | width: 50%; 21 | box-sizing: border-box; 22 | &:last-child { 23 | border-right: none; 24 | } 25 | } 26 | 27 | .demo-cascader .demonstration { 28 | display: block; 29 | color: #8492a6; 30 | font-size: 14px; 31 | margin-bottom: 20px; 32 | } 33 | -------------------------------------------------------------------------------- /src/upload/Types.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | // 自定义file类型 4 | export type _File = { 5 | status: string, 6 | name: string, 7 | size?: number, 8 | percentage?: number, 9 | uid: number, 10 | raw?: RawFile, 11 | url?: string, 12 | response?: Object 13 | }; 14 | 15 | export type UploadState = { 16 | fileList: Array<_File>, 17 | tempIndex: number 18 | }; 19 | 20 | export type IframeUploadState = { 21 | dragOver: boolean, 22 | file: ?File, 23 | disabled: false, 24 | frameName: string 25 | }; 26 | 27 | export class RawFile extends File { 28 | uid: number; 29 | } 30 | 31 | export class _ProgressEvent extends ProgressEvent { 32 | percent: number; 33 | } 34 | -------------------------------------------------------------------------------- /site/docs/en-US/quick-start.md: -------------------------------------------------------------------------------- 1 | ## Quick start 2 | 3 | ### Installation 4 | Installing with npm is recommended and it works seamlessly with `webpack`. 5 | 6 | ```shell 7 | npm i element-react --save 8 | ``` 9 | ### Theme 10 | Before the building, you need a style theme, here we recommend you to pick up `element-theme-default`. 11 | 12 | ```shell 13 | npm install element-theme-default --save 14 | ``` 15 | 16 | ### Usage 17 | 18 | ```js 19 | import React from 'react'; 20 | import ReactDOM from 'react-dom'; 21 | import { Button } from 'element-react'; 22 | 23 | import 'element-theme-default'; 24 | 25 | ReactDOM.render(, document.getElementById('app')); 26 | 27 | ``` 28 | -------------------------------------------------------------------------------- /site/docs/zh-CN/icon.md: -------------------------------------------------------------------------------- 1 | ## Icon 图标 2 | 3 | 提供了一套常用的图标集合。 4 | 5 | ### 使用方法 6 | 7 | 直接通过设置类名为 `el-icon-iconName` 来使用即可。例如: 8 | 9 | :::demo 10 | ```js 11 | render() { 12 | return ( 13 |
14 | 15 | 16 | 17 | 18 |
19 | ) 20 | } 21 | ``` 22 | ::: 23 | 24 | ### 图标集合 25 | 26 | :::demo 27 | ```js 28 | render() { 29 | return ( 30 | 35 | ) 36 | } 37 | ``` 38 | ::: 39 | -------------------------------------------------------------------------------- /site/pages/upload/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box { 2 | margin-bottom: 24px 3 | } 4 | 5 | .demo-box .upload-demo { 6 | width: 360px; 7 | } 8 | 9 | .demo-box .avatar-uploader .el-upload { 10 | border: 1px dashed #d9d9d9; 11 | border-radius: 6px; 12 | cursor: pointer; 13 | position: relative; 14 | overflow: hidden; 15 | } 16 | 17 | .demo-box .avatar-uploader .el-upload:hover { 18 | border-color: #20a0ff; 19 | } 20 | 21 | .demo-box .avatar-uploader .avatar-uploader-icon { 22 | font-size: 28px; 23 | color: #8c939d; 24 | width: 178px; 25 | height: 178px; 26 | line-height: 178px; 27 | text-align: center; 28 | } 29 | 30 | .demo-box .avatar-uploader .avatar { 31 | width: 178px; 32 | height: 178px; 33 | display: block 34 | } 35 | -------------------------------------------------------------------------------- /site/pages/icon/iconList.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | "arrow-down", 3 | "arrow-left", 4 | "arrow-right", 5 | "arrow-up", 6 | "caret-bottom", 7 | "caret-left", 8 | "caret-right", 9 | "caret-top", 10 | "check", 11 | "circle-check", 12 | "circle-close", 13 | "circle-cross", 14 | "close", 15 | "upload", 16 | "d-arrow-left", 17 | "d-arrow-right", 18 | "d-caret", 19 | "date", 20 | "delete", 21 | "document", 22 | "edit", 23 | "information", 24 | "loading", 25 | "menu", 26 | "message", 27 | "minus", 28 | "more", 29 | "picture", 30 | "plus", 31 | "search", 32 | "setting", 33 | "share", 34 | "star-off", 35 | "star-on", 36 | "time", 37 | "warning", 38 | "delete2", 39 | "upload2", 40 | "view" 41 | ]; 42 | -------------------------------------------------------------------------------- /typings/typing-tests/Loading.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Loading } from 'element-react' 3 | import { Loading as LoadingNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | render() { 7 | return ( 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /site/docs/en-US/icon.md: -------------------------------------------------------------------------------- 1 | ## Icon 2 | 3 | Element provides a set of common icons. 4 | 5 | ### Basic usage 6 | 7 | Just assign the class name to `el-icon-iconName`. 8 | 9 | :::demo 10 | 11 | ```js 12 | render() { 13 | return ( 14 |
15 | 16 | 17 | 18 | 19 |
20 | ) 21 | } 22 | ``` 23 | ::: 24 | 25 | ### Icons 26 | 27 | :::demo 28 | ```js 29 | render() { 30 | return ( 31 | 36 | ) 37 | } 38 | ``` 39 | ::: 40 | -------------------------------------------------------------------------------- /site/pages/dropdown/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box { 2 | .el-dropdown { 3 | vertical-align: top; 4 | 5 | & + .el-dropdown { 6 | margin-left: 15px; 7 | } 8 | } 9 | .el-dropdown-link { 10 | cursor: pointer; 11 | color: #20a0ff; 12 | } 13 | .el-icon-caret-bottom { 14 | font-size: 12px; 15 | } 16 | } 17 | 18 | .block-col-2 { 19 | margin: -24px; 20 | 21 | .el-col { 22 | padding: 30px 0; 23 | text-align: center; 24 | border-right: 1px solid #eff2f6; 25 | 26 | &:last-child { 27 | border-right: 0; 28 | } 29 | } 30 | } 31 | 32 | .demo-dropdown .demonstration { 33 | display: block; 34 | color: #8492a6; 35 | font-size: 14px; 36 | margin-bottom: 20px; 37 | } 38 | -------------------------------------------------------------------------------- /src/breadcrumb/Breadcrumb.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | type Context = { 7 | separator: string 8 | }; 9 | 10 | export default class Breadcrumb extends Component { 11 | getChildContext(): Context { 12 | return { 13 | separator: this.props.separator 14 | }; 15 | } 16 | 17 | render() { 18 | return ( 19 |
20 | {this.props.children} 21 |
22 | ) 23 | } 24 | } 25 | 26 | Breadcrumb.childContextTypes = { 27 | separator: PropTypes.string 28 | }; 29 | 30 | Breadcrumb.propTypes = { 31 | separator: PropTypes.string 32 | } 33 | 34 | Breadcrumb.defaultProps = { 35 | separator: '/' 36 | } 37 | -------------------------------------------------------------------------------- /typings/typing-tests/ColorPicker.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { ColorPicker } from 'element-react' 3 | import { ColorPicker as ColorPickerNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | format: 'hsl' | 'hsv' | 'hex' | 'rgb' 7 | onChange = (color) => { } 8 | render() { 9 | return ( 10 |
11 | 12 | 13 | 14 | 15 | 16 |
17 | ) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /libs/view/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | export default class View extends Component { 5 | render() { 6 | const classNames = []; 7 | const { show = true, className = '', children } = this.props; 8 | const mixed = { style: { ...children.props.style } }; 9 | if (!show) mixed.style.display = 'none'; 10 | if (children.props.className) classNames.push(children.props.className); 11 | if (className) classNames.push(className); 12 | mixed.className = classNames.join(' '); 13 | 14 | return React.cloneElement(React.Children.only(children), mixed); 15 | } 16 | } 17 | 18 | /* eslint-disable */ 19 | View.propTypes = { 20 | show: PropTypes.any, 21 | }; 22 | /* eslint-enable */ 23 | 24 | View._typeName = 'View'; 25 | -------------------------------------------------------------------------------- /src/card/Card.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | export default class Card extends Component { 7 | static defaultProps = { 8 | bodyStyle: { 9 | padding: '20px' 10 | } 11 | } 12 | 13 | render(): React.DOM { 14 | const { header, bodyStyle, children } = this.props; 15 | return ( 16 |
17 | { 18 | header &&
{ header }
19 | } 20 |
21 | { children } 22 |
23 |
24 | ) 25 | } 26 | } 27 | 28 | Card.propTypes = { 29 | header: PropTypes.node, 30 | bodyStyle: PropTypes.object 31 | }; 32 | -------------------------------------------------------------------------------- /site/docs/zh-CN/breadcrumb.md: -------------------------------------------------------------------------------- 1 | ## Breadcrumb 面包屑 2 | 显示当前页面的路径,快速返回之前的任意页面。 3 | 4 | ### 基础用法 5 | 6 | 适用广泛的基础用法。 7 | 8 | :::demo 在`Breadcrumb`中使用`Breadcrumb.Item`标签表示从首页开始的每一级。Element 提供了一个`separator`属性,在`Breadcrumb`标签中设置它来决定分隔符,它只能是字符串,默认为斜杠`/`。 9 | 10 | ```js 11 | render() { 12 | return ( 13 | 14 | 首页 15 | 活动管理 16 | 活动列表 17 | 活动详情 18 | 19 | ) 20 | } 21 | ``` 22 | ::: 23 | 24 | ### Breadcrumb Attributes 25 | | 参数 | 说明 | 类型 | 可选值 | 默认值 | 26 | |---------- |-------------- |---------- |-------------------------------- |-------- | 27 | | separator | 分隔符 | string | — | 斜杠'/' | 28 | -------------------------------------------------------------------------------- /site/pages/switch/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-switch { 2 | .el-switch { 3 | margin: 20px 20px 20px 0; 4 | } 5 | 6 | .el-tooltip { 7 | display: inline-block; 8 | } 9 | 10 | .el-tooltip + .el-tooltip { 11 | margin-left: 15px; 12 | } 13 | 14 | .box { 15 | width: 400px; 16 | 17 | .top { 18 | text-align: center; 19 | } 20 | 21 | .left { 22 | float: left; 23 | width: 60px; 24 | } 25 | 26 | .right { 27 | float: right; 28 | width: 60px; 29 | } 30 | 31 | .bottom { 32 | clear: both; 33 | text-align: center; 34 | } 35 | 36 | .item { 37 | margin: 4px; 38 | } 39 | 40 | .left .el-tooltip__popper, 41 | .right .el-tooltip__popper { 42 | padding: 8px 10px; 43 | } 44 | .el-tooltip { 45 | margin-left: 0; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /site/pages/tooltip/style.scss: -------------------------------------------------------------------------------- 1 | .demo-tooltip { 2 | .el-tooltip { 3 | display: inline-block; 4 | } 5 | 6 | .el-tooltip + .el-tooltip { 7 | margin-left: 15px; 8 | } 9 | 10 | .box { 11 | width: 400px; 12 | 13 | .top { 14 | text-align: center; 15 | } 16 | 17 | .left { 18 | float: left; 19 | width: 60px; 20 | } 21 | 22 | .right { 23 | float: right; 24 | width: 60px; 25 | } 26 | 27 | .bottom { 28 | clear: both; 29 | text-align: center; 30 | } 31 | 32 | .item { 33 | margin: 4px; 34 | } 35 | 36 | .left .el-tooltip__popper, 37 | .right .el-tooltip__popper { 38 | padding: 8px 10px; 39 | } 40 | .el-tooltip { 41 | margin-left: 0; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/scrollbar/scrollbar-width.js: -------------------------------------------------------------------------------- 1 | let scrollBarWidth; 2 | 3 | export function getScrollBarWidth() { 4 | if (scrollBarWidth !== undefined) return scrollBarWidth; 5 | 6 | const outer = document.createElement('div'); 7 | outer.className = 'el-scrollbar__wrap'; 8 | outer.style.visibility = 'hidden'; 9 | outer.style.width = '100px'; 10 | outer.style.position = 'absolute'; 11 | outer.style.top = '-9999px'; 12 | document.body.appendChild(outer); 13 | 14 | const widthNoScroll = outer.offsetWidth; 15 | outer.style.overflow = 'scroll'; 16 | 17 | const inner = document.createElement('div'); 18 | inner.style.width = '100%'; 19 | outer.appendChild(inner); 20 | 21 | const widthWithScroll = inner.offsetWidth; 22 | scrollBarWidth = widthNoScroll - widthWithScroll; 23 | outer.parentNode.removeChild(outer); 24 | 25 | return scrollBarWidth; 26 | } 27 | -------------------------------------------------------------------------------- /src/tabs/TabPane.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | type Props = { 7 | children: React.DOM, 8 | label: string | React.DOM, 9 | name: string, 10 | disabled: boolean, 11 | closable: boolean, 12 | } 13 | 14 | export default class TabPane extends Component { 15 | props: Props; 16 | 17 | render(): React.DOM { 18 | return ( 19 |
20 | { this.props.children } 21 |
22 | ); 23 | } 24 | } 25 | 26 | TabPane.propTypes = { 27 | label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), 28 | name: PropTypes.string, 29 | disabled: PropTypes.bool, 30 | closable: PropTypes.bool, 31 | } 32 | 33 | TabPane.defaultProps = { 34 | disabled: false, 35 | closable: false, 36 | } 37 | -------------------------------------------------------------------------------- /site/pages/button/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-button { 2 | .el-row { 3 | margin-bottom: 10px; 4 | } 5 | .el-button + .el-button { 6 | margin-left: 10px; 7 | } 8 | .el-button-group { 9 | margin-bottom: 20px; 10 | 11 | .el-button + .el-button { 12 | margin-left: 0; 13 | } 14 | 15 | & + .el-button-group { 16 | margin-left: 10px; 17 | } 18 | } 19 | } 20 | 21 | .demo-box.demo-button .intro-block { 22 | padding: 0; 23 | } 24 | 25 | .demo-button .intro-block .block { 26 | padding: 30px 24px; 27 | overflow: hidden; 28 | border-bottom: solid 1px #EFF2F6; 29 | &:last-child { 30 | border-bottom: none; 31 | } 32 | } 33 | 34 | .demo-button .intro-block .demonstration { 35 | font-size: 14px; 36 | color: #8492a6; 37 | line-height: 44px; 38 | } 39 | 40 | .demo-button .intro-block .wrapper { 41 | float: right; 42 | margin-right: 20px; 43 | } 44 | -------------------------------------------------------------------------------- /src/date-picker/MountBody.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | export class MountBody extends Component { 5 | componentWillMount() { 6 | let c = React.cloneElement(this.props.children) 7 | this.tnode = document.createElement('div') 8 | document.body.appendChild(this.tnode) 9 | ReactDOM.render(c, this.tnode) 10 | } 11 | 12 | componentWillUnmount() { 13 | ReactDOM.unmountComponentAtNode(this.tnode) 14 | document.body.removeChild(this.tnode) 15 | } 16 | 17 | contains(evt){ 18 | let parent = this.tnode.childNodes[0] 19 | let rect = parent.getBoundingClientRect() 20 | let isContain = (evt.clientX >= rect.left && evt.clientX <= rect.right) && (evt.clientY >= rect.top && evt.clientY <= rect.bottom ) 21 | return isContain 22 | } 23 | 24 | render(){ 25 | return null 26 | } 27 | } -------------------------------------------------------------------------------- /src/scrollbar/util.js: -------------------------------------------------------------------------------- 1 | export const BAR_MAP = { 2 | vertical: { 3 | offset: 'offsetHeight', 4 | scroll: 'scrollTop', 5 | scrollSize: 'scrollHeight', 6 | size: 'height', 7 | key: 'vertical', 8 | axis: 'Y', 9 | client: 'clientY', 10 | direction: 'top' 11 | }, 12 | horizontal: { 13 | offset: 'offsetWidth', 14 | scroll: 'scrollLeft', 15 | scrollSize: 'scrollWidth', 16 | size: 'width', 17 | key: 'horizontal', 18 | axis: 'X', 19 | client: 'clientX', 20 | direction: 'left' 21 | } 22 | } 23 | 24 | export function renderThumbStyle({ move, size, bar }) { 25 | const style = {}; 26 | const translate = `translate${bar.axis}(${ move }%)`; 27 | 28 | style[bar.size] = size; 29 | style.transform = translate; 30 | style.msTransform = translate; 31 | style.WebkitTransform = translate; 32 | 33 | return style; 34 | } 35 | -------------------------------------------------------------------------------- /src/menu/MixinComponent.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { Component, PropTypes } from '../../libs'; 4 | 5 | export default class MixinComponent extends Component { 6 | parent(): Component { 7 | return this.context.component; 8 | } 9 | 10 | indexPath(): Array { 11 | let path = [this.props.index]; 12 | let parent = this.parent(); 13 | 14 | while (parent.instanceType !== 'Menu') { 15 | if (parent.props.index) { 16 | path.unshift(parent.props.index); 17 | } 18 | 19 | parent = parent.parent(); 20 | } 21 | 22 | return path; 23 | } 24 | 25 | rootMenu(): Component { 26 | let parent = this.parent(); 27 | 28 | while (parent.instanceType !== 'Menu') { 29 | parent = parent.parent(); 30 | } 31 | 32 | return parent; 33 | } 34 | } 35 | 36 | MixinComponent.contextTypes = { 37 | component: PropTypes.any 38 | }; 39 | -------------------------------------------------------------------------------- /src/card/__test__/Card_test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow, mount } from 'enzyme'; 3 | import sinon from 'sinon'; 4 | 5 | import Card from '../'; 6 | 7 | describe('Card test', () => { 8 | it('render header', () => { 9 | const w = shallow( 10 | 11 | ); 12 | expect(w.find('.el-card__header').at(0).text()).toBe('HEADER'); 13 | }); 14 | 15 | it('render body', () => { 16 | const w = shallow( 17 | BODY 18 | ); 19 | expect(w.find('.el-card__body').at(0).text()).toBe('BODY'); 20 | }); 21 | 22 | it('use bodyStyle', () => { 23 | const bodyStyle = { 24 | padding: '5px', 25 | border: '1px solid blue', 26 | }; 27 | const w = shallow( 28 | 29 | ); 30 | expect(w.find('.el-card__body').at(0).prop('style')).toEqual(bodyStyle); 31 | }); 32 | }); -------------------------------------------------------------------------------- /typings/typing-tests/Card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Card, Button } from 'element-react' 3 | import { Card as CardNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | render() { 7 | return ( 8 |
9 | 10 | 11 | 13 | 14 |
15 | } /> 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | } /> 25 | 26 | 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /typings/typing-tests/Badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Badge } from 'element-react' 3 | import { Badge as BadgeNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | render() { 7 | return ( 8 |
9 | 10 |
badge
11 |
12 | 13 |
badge
14 |
15 | 16 |
badge
17 |
18 | 19 | 20 |
badge
21 |
22 | 23 |
badge
24 |
25 | 26 |
badge
27 |
28 |
29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /site/pages/rate/style.scss: -------------------------------------------------------------------------------- 1 | .demo-box.demo-rate { 2 | .el-row { 3 | margin-bottom: 10px; 4 | } 5 | .el-button + .el-rate { 6 | margin-left: 10px; 7 | } 8 | .el-rate-group { 9 | margin-bottom: 20px; 10 | 11 | .el-rate + .el-rate { 12 | margin-left: 0; 13 | } 14 | 15 | & + .el-rate-group { 16 | margin-left: 10px; 17 | } 18 | } 19 | } 20 | 21 | .demo-box.demo-rate .intro-block { 22 | overflow: hidden; 23 | margin: -24px; 24 | padding: 0; 25 | } 26 | 27 | .demo-rate .intro-block .block { 28 | padding: 30px 0; 29 | text-align: center; 30 | border-right: 1px solid #eff2f6; 31 | float: left; 32 | width: 50%; 33 | box-sizing: border-box; 34 | 35 | &:last-child { 36 | border-bottom: none; 37 | } 38 | } 39 | 40 | .demo-rate .intro-block .demonstration { 41 | font-size: 14px; 42 | color: #8492a6; 43 | line-height: 44px; 44 | } 45 | 46 | .demo-rate .intro-block .wrapper { 47 | margin-right: 20px; 48 | } 49 | -------------------------------------------------------------------------------- /typings/typing-tests/InputNumber.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { InputNumber } from 'element-react' 3 | import { InputNumber as InputNumberNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | onChange = (value) => { } 7 | render() { 8 | return ( 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/dropdown/DropdownItem.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | export default class DropdownItem extends Component { 7 | handleClick(): void { 8 | this.context.component.handleMenuItemClick(this.props.command, this); 9 | } 10 | 11 | render(): React.DOM { 12 | const { disabled, divided } = this.props; 13 | 14 | return ( 15 |
  • 22 | { this.props.children } 23 |
  • 24 | ) 25 | } 26 | } 27 | 28 | DropdownItem.contextTypes = { 29 | component: PropTypes.any 30 | }; 31 | 32 | DropdownItem.propTypes = { 33 | command: PropTypes.string, 34 | disabled: PropTypes.bool, 35 | divided: PropTypes.bool, 36 | }; 37 | -------------------------------------------------------------------------------- /typings/typing-tests/Breadcrumb.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Breadcrumb } from 'element-react' 3 | import { Breadcrumb as BreadcrumbNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | render() { 7 | return ( 8 |
    9 | 10 | 首页 11 | 12 | 13 | 首页 14 | 15 | 16 | 17 | 首页 18 | 19 | 20 | 首页 21 | 22 |
    23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /typings/typing-tests/Progress.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Progress } from 'element-react' 3 | import { Progress as ProgressNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | render() { 7 | return ( 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
    19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/locale/format.js: -------------------------------------------------------------------------------- 1 | /** 2 | * String format template 3 | * - Inspired: 4 | * https://github.com/Matt-Esch/string-template/index.js 5 | */ 6 | 7 | const RE_NARGS = /(%|)\{([0-9a-zA-Z_]+)\}/g; 8 | 9 | /** 10 | * format 11 | * 12 | * @param {String} string 13 | * @param {Array} ...args 14 | * @return {String} 15 | */ 16 | 17 | export default function(string, ...args) { 18 | if (args.length === 1 && typeof args[0] === 'object') { 19 | args = args[0]; 20 | } 21 | 22 | if (!args || !args.hasOwnProperty) { 23 | args = {}; 24 | } 25 | 26 | return string.replace(RE_NARGS, (match, prefix, i, index) => { 27 | let result; 28 | 29 | if (string[index - 1] === '{' && 30 | string[index + match.length] === '}') { 31 | return i; 32 | } else { 33 | result = Object.prototype.hasOwnProperty.call(args, i) ? args[i] : null; 34 | if (result === null || result === undefined) { 35 | return ''; 36 | } 37 | 38 | return result; 39 | } 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /typings/typing-tests/Pagination.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Pagination } from 'element-react' 3 | import { Pagination as PaginationNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | onCurrentChange = (currentPage) => { } 7 | onSizeChange = (size) => { } 8 | layout: 'total, prev, pager, next' 9 | render() { 10 | return ( 11 |
    12 | 13 | 14 | 15 | 16 | 17 |
    18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /site/docs/en-US/breadcrumb.md: -------------------------------------------------------------------------------- 1 | ## Breadcrumb 2 | 3 | Displays the location of the current page, making it easier to browser back. 4 | 5 | ### Basic usage 6 | 7 | 8 | :::demo In `Breadcrumb`, each `Breadcrumb.Item` is a tag that stands for every level starting from homepage. This component has a `String` attribute `separator`, and it determines the separator. Its default value is '/'. 9 | 10 | ```js 11 | render() { 12 | return ( 13 | 14 | Homepage 15 | Promotion Management 16 | Promotion List 17 | Promotion Detail 18 | 19 | ) 20 | } 21 | ``` 22 | ::: 23 | 24 | ### Breadcrumb Attributes 25 | | Attribute | Description | Type | Accepted Values | Default| 26 | |---------- |-------------- |---------- |-------------------------------- |-------- | 27 | | separator | separator character | string | — | / | 28 | -------------------------------------------------------------------------------- /src/color-picker/draggable.js: -------------------------------------------------------------------------------- 1 | let isDragging = false; 2 | 3 | export default function(element, options) { 4 | const moveFn = function(event) { 5 | if (options.drag) { 6 | options.drag(event); 7 | } 8 | }; 9 | const upFn = function(event) { 10 | document.removeEventListener('mousemove', moveFn); 11 | document.removeEventListener('mouseup', upFn); 12 | document.onselectstart = null; 13 | document.ondragstart = null; 14 | 15 | isDragging = false; 16 | 17 | if (options.end) { 18 | options.end(event); 19 | } 20 | }; 21 | element.addEventListener('mousedown', function(event) { 22 | if (isDragging) return; 23 | document.onselectstart = function() { 24 | return false; 25 | }; 26 | document.ondragstart = function() { 27 | return false; 28 | }; 29 | 30 | document.addEventListener('mousemove', moveFn); 31 | document.addEventListener('mouseup', upFn); 32 | isDragging = true; 33 | 34 | if (options.start) { 35 | options.start(event); 36 | } 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /libs/editor/style.scss: -------------------------------------------------------------------------------- 1 | .CodeMirror { 2 | font-family: 'source-code-pro', Menlo, 'Courier New', Consolas, monospace; 3 | font-size: 13px; 4 | line-height: 20px; 5 | color: #484848; 6 | padding: 15px 0; 7 | border-right: 1px solid #eaeefb; 8 | z-index: 0; 9 | height: auto; 10 | background: transparent; 11 | 12 | .CodeMirror-gutters { 13 | background: transparent; 14 | border-right: 0px; 15 | z-index: 0; 16 | } 17 | 18 | .CodeMirror-selected { 19 | background: #E6EFFB; 20 | } 21 | 22 | pre { 23 | padding: 0 25px; 24 | } 25 | 26 | span.cm-keyword { color: #1990B8; } 27 | span.cm-atom { color: #C92C2C; } 28 | span.cm-number { color: #C92C2C; } 29 | span.cm-variable { color: black; } 30 | span.cm-variable-2 { color: #0000C0; } 31 | span.cm-variable-3 { color: #0000C0; } 32 | span.cm-property { color: black; } 33 | span.cm-operator { color: black; } 34 | span.cm-comment { color: #7D8B99; } 35 | span.cm-string { color: #2F9C0A; } 36 | span.cm-string-2 { color: #2F9C0A; } 37 | span.cm-link { color: #C92C2C; } 38 | } 39 | -------------------------------------------------------------------------------- /typings/typing-tests/Timepicker.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { TimeSelect, TimePicker, TimeRangePicker } from 'element-react' 3 | 4 | class Component extends React.Component<{}, {}> { 5 | state = { 6 | value: new Date() 7 | } 8 | onChange = () => { } 9 | render() { 10 | return ( 11 |
    12 | 21 | 22 | 28 | 29 | 35 |
    36 | ) 37 | } 38 | } 39 | 40 | // TODO: 这里的测试没写完整 41 | -------------------------------------------------------------------------------- /site/pages/pagination/style.scss: -------------------------------------------------------------------------------- 1 | .demo-pagination .first { 2 | padding: 0; 3 | } 4 | 5 | .demo-pagination .first .block { 6 | padding: 30px 0; 7 | text-align: center; 8 | border-right: solid 1px #EFF2F6; 9 | display:inline-block; 10 | width: 50%; 11 | box-sizing: border-box; 12 | 13 | &:last-child { 14 | border-right: none; 15 | } 16 | } 17 | 18 | .demo-pagination .first .demonstration { 19 | display: block; 20 | color: #8492a6; 21 | font-size: 14px; 22 | margin-bottom: 20px; 23 | } 24 | 25 | .demo-pagination .last { 26 | padding: 0; 27 | } 28 | 29 | .demo-pagination .last .block { 30 | padding: 30px 24px; 31 | border-bottom: solid 1px #EFF2F6; 32 | &:last-child { 33 | border-bottom: none; 34 | } 35 | } 36 | 37 | .demo-pagination .last .demonstration { 38 | font-size: 14px; 39 | color: #8492a6; 40 | line-height: 44px; 41 | } 42 | 43 | .demo-pagination .last .demonstration + .el-pagination { 44 | float: right; 45 | width: 70%; 46 | margin: 5px 20px 0 0; 47 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Element React Contributing Guide 2 | 3 | We are very graceful that you are interested in contributing to `element-react`. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines. 4 | 5 | ## Issue Guidelines 6 | 7 | - Before submitting an issue, please check if similar problems have already been issued. 8 | 9 | - The title of the issue should be written as `[Component Name]: Issue caption.` (e.g. `Button: Fix xxx bug`) 10 | 11 | - Please specify which version of `element-react` and `element-theme-default` you are using, and other related information. 12 | 13 | ## Pull Request Guidelines 14 | 15 | - Fork this repository to your own account. Do not create branches here. 16 | 17 | - Make sure pull request will be merged into `master`. 18 | 19 | - Commit info should be formatted as `[Component Name]: Info about commit.` (e.g. `Button: Fix xxx bug`) 20 | 21 | - Make sure that your changes pass `npm test`, `npm run lint` and `npm run build`. 22 | 23 | - **Rebase** your commits to make your pull request meaningful. 24 | -------------------------------------------------------------------------------- /typings/typing-tests/Tag.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Tag } from 'element-react' 3 | import { Tag as TagNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | onClose = () => { } 7 | render() { 8 | return ( 9 |
    10 | tag 11 | tag 12 | tag 13 | tag 14 | tag 15 | tag 16 | 17 | TagNext 18 | TagNext 19 | TagNext 20 | TagNext 21 | TagNext 22 | TagNext 23 |
    24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/color-picker/Types.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export type ColorType = { 4 | _hue: number, 5 | _saturation: number, 6 | _value: number, 7 | _alpha: number, 8 | enableAlpha: boolean, 9 | format: string, 10 | value: string, 11 | set: (props: string, value: mixed) => void, 12 | get: (props: string) => mixed, 13 | toRgb: () => { r: number, g: number, b: number }, 14 | fromString: (value: string) => void, 15 | doOnChange: () => void 16 | }; 17 | 18 | export type ColorPickerState = { 19 | value: string, 20 | color: ColorType, 21 | showPicker: false, 22 | showPanelColor: false 23 | }; 24 | 25 | export type AlphaSliderState = { 26 | thumbLeft: number, 27 | thumbTop: number, 28 | background: ?string 29 | }; 30 | 31 | export type HueSliderState = { 32 | thumbLeft: number, 33 | thumbTop: number 34 | }; 35 | 36 | export type SvPanelState = { 37 | cursorTop: number, 38 | cursorLeft: number, 39 | background: string 40 | }; 41 | 42 | export type DragOptions = { 43 | drag: (event: SyntheticMouseEvent) => void, 44 | end: (event: SyntheticMouseEvent) => void 45 | }; 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 ELEME Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /site/pages/color-picker/style.scss: -------------------------------------------------------------------------------- 1 | .demo-colorpicker .block { 2 | padding: 30px 0; 3 | text-align: center; 4 | border-right: 1px solid #eff2f6; 5 | width: 50%; 6 | display: inline-block; 7 | box-sizing: border-box 8 | } 9 | .demo-colorpicker .block:after { 10 | clear: both; 11 | } 12 | 13 | .demo-colorpicker .block:last-child { 14 | border-right: none 15 | } 16 | 17 | .demo-colorpicker .source:nth-child(1) { 18 | padding: 0; 19 | } 20 | 21 | .demo-colorpicker .demonstration { 22 | display: block; 23 | color: #8492a6; 24 | font-size: 14px; 25 | margin-bottom: 20px 26 | } 27 | 28 | .demo-color-box { 29 | border-radius: 4px; 30 | padding: 20px; 31 | height: 74px; 32 | box-sizing: border-box; 33 | color: #fff; 34 | font-size: 14px 35 | } 36 | 37 | .demo-color-box .value { 38 | font-size: 12px; 39 | opacity: .69; 40 | line-height: 24px 41 | } 42 | 43 | .demo-color-box-group .demo-color-box { 44 | border-radius: 0 45 | } 46 | 47 | .demo-color-box-group .demo-color-box:first-child { 48 | border-radius: 4px 4px 0 0 49 | } 50 | 51 | .demo-color-box-group .demo-color-box:last-child { 52 | border-radius: 0 0 4px 4px 53 | } 54 | -------------------------------------------------------------------------------- /typings/typing-tests/Slider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Slider } from 'element-react' 3 | import { Slider as SliderNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | formatTooltip = () => { } 7 | onChange = (value) => { } 8 | render() { 9 | return ( 10 |
    11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
    19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /typings/typing-tests/Switch.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Switch } from 'element-react' 3 | import { Switch as SwitchNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | onChange = (value) => { } 7 | render() { 8 | return ( 9 |
    10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
    20 | ) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /site/pages/icon/style.scss: -------------------------------------------------------------------------------- 1 | .demo-icon .source div > i { 2 | font-size: 24px; 3 | color: #8492a6; 4 | margin: 0 20px; 5 | font-size: 1.5em; 6 | vertical-align: middle; 7 | } 8 | 9 | .demo-icon .source div > button { 10 | margin: 0 20px; 11 | } 12 | 13 | .icon-list { 14 | overflow: hidden; 15 | list-style: none; 16 | padding: 0; 17 | border: solid 1px #eaeefb; 18 | border-radius: 4px; 19 | } 20 | .icon-list li { 21 | float: left; 22 | width: 16.66%; 23 | text-align: center; 24 | height: 120px; 25 | line-height: 120px; 26 | color: #666; 27 | font-size: 13px; 28 | transition: color .15s linear; 29 | 30 | border-right: 1px solid #eee; 31 | border-bottom: 1px solid #eee; 32 | margin-right: -1px; 33 | margin-bottom: -1px; 34 | 35 | & span { 36 | display: inline-block; 37 | line-height: normal; 38 | vertical-align: middle; 39 | font-family: 'Helvetica Neue',Helvetica,'PingFang SC','Hiragino Sans GB','Microsoft YaHei',SimSun,sans-serif; 40 | color: #99a9bf; 41 | } 42 | & i { 43 | display: block; 44 | font-size: 24px; 45 | margin-bottom: 15px; 46 | color: #8492a6; 47 | } 48 | &:hover { 49 | color: rgb(92, 182, 255); 50 | } 51 | } -------------------------------------------------------------------------------- /libs/editor/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import CodeMirror from 'codemirror' 4 | 5 | import 'codemirror/mode/jsx/jsx' 6 | import 'codemirror/keymap/sublime' 7 | import 'codemirror/addon/comment/comment' 8 | 9 | import 'codemirror/lib/codemirror.css' 10 | import './style.scss' 11 | 12 | export default class Editor extends Component { 13 | componentDidMount() { 14 | const { onChange, value } = this.props 15 | 16 | this.cm = CodeMirror(this.editor, { 17 | mode: 'jsx', 18 | theme: 'react', 19 | keyMap: 'sublime', 20 | viewportMargin: Infinity, 21 | lineNumbers: false, 22 | dragDrop: false 23 | }); 24 | 25 | this.cm.setValue(value) 26 | 27 | this.cm.on('changes', cm => { 28 | if (onChange) { 29 | clearTimeout(this.timeout); 30 | 31 | this.timeout = setTimeout(() => { 32 | onChange(cm.getValue()); 33 | }, 300); 34 | } 35 | }) 36 | } 37 | 38 | render() { 39 | return
    (this.editor = ref)} /> 40 | } 41 | } 42 | 43 | Editor.propTypes = { 44 | onChange: PropTypes.func, 45 | value: PropTypes.string 46 | } 47 | -------------------------------------------------------------------------------- /src/collapse/CollapseItem.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes, CollapseTransition } from '../../libs'; 5 | 6 | export default class CollapseItem extends Component { 7 | constructor(props: Object) { 8 | super(props); 9 | } 10 | 11 | render(): React.DOM { 12 | const { title, isActive, onClick, name } = this.props; 13 | 14 | return ( 15 |
    21 |
    onClick(name)}> 22 | 23 | {title} 24 |
    25 | 26 |
    27 |
    28 | {this.props.children} 29 |
    30 |
    31 |
    32 |
    33 | ); 34 | } 35 | } 36 | 37 | CollapseItem.propTypes = { 38 | onClick: PropTypes.func, 39 | isActive: PropTypes.bool, 40 | title: PropTypes.node, 41 | name: PropTypes.string 42 | }; 43 | -------------------------------------------------------------------------------- /src/menu/MenuItem.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { PropTypes } from '../../libs'; 5 | 6 | import MixinComponent from './MixinComponent'; 7 | 8 | export default class MenuItem extends MixinComponent { 9 | instanceType: string; 10 | 11 | constructor(props: Object) { 12 | super(props); 13 | 14 | this.instanceType = 'MenuItem'; 15 | } 16 | 17 | componentDidMount() { 18 | this.rootMenu().state.menuItems[this.props.index] = this; 19 | } 20 | 21 | handleClick(): void { 22 | this.rootMenu().handleSelect( 23 | this.props.index, 24 | this.indexPath(), 25 | this 26 | ); 27 | } 28 | 29 | active(): boolean { 30 | return this.props.index === this.rootMenu().state.activeIndex; 31 | } 32 | 33 | render(): React.DOM { 34 | return ( 35 |
  • 43 | {this.props.children} 44 |
  • 45 | ) 46 | } 47 | } 48 | 49 | MenuItem.propTypes = { 50 | index: PropTypes.string.isRequired, 51 | disabled: PropTypes.bool 52 | }; 53 | -------------------------------------------------------------------------------- /site/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import { AppContainer } from 'react-hot-loader'; 4 | 5 | import 'core-js'; 6 | 7 | import 'element-theme-default'; 8 | 9 | import './styles/base.scss'; 10 | import './styles/prism.css'; 11 | 12 | import App from './page'; 13 | 14 | function inChinaConfirm() { 15 | import('../src/message-box').then(MessageBox => { 16 | MessageBox.default.confirm('建议大陆用户访问部署在国内的站点,是否跳转?', '提示').then(() => { 17 | location.href = 'https://element-react.faas.ele.me'; 18 | }); 19 | }); 20 | } 21 | 22 | function inChina() { 23 | if (window.fetch && document.domain !== 'element-react.faas.ele.me') { 24 | fetch('//restapi.amap.com/v3/ip?output=JSON&key=53a87f7c6a6d173be31d4123958ad5c2') 25 | .then(res => res.json()) 26 | .then(({ city }) => { 27 | if (city && typeof city === 'string') { 28 | inChinaConfirm(); 29 | } 30 | }) 31 | } 32 | } 33 | 34 | render(, document.getElementById('app'), inChina); 35 | 36 | if (module.hot) { 37 | module.hot.accept('./page', () => { 38 | const App = require('./page').default; 39 | 40 | render(, document.getElementById('app')); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /site/pages/form/style.scss: -------------------------------------------------------------------------------- 1 | .demo-form { 2 | .el-select .el-input { 3 | width: 420px; 4 | } 5 | .el-form { 6 | width: 500px; 7 | } 8 | .el-form.en-US { 9 | width: 540px; 10 | } 11 | 12 | .line { 13 | text-align: center; 14 | } 15 | 16 | .el-checkbox-group { 17 | width: 320px; 18 | margin: 0; 19 | padding: 0; 20 | list-style: none; 21 | 22 | &:after,&:before { 23 | content: ' '; 24 | display: table; 25 | } 26 | &:after { 27 | clear: both; 28 | visibility: hidden; 29 | font-size: 0; 30 | height: 0; 31 | } 32 | 33 | .el-checkbox { 34 | float: left; 35 | width: 160px; 36 | padding-right: 20px; 37 | margin: 0; 38 | padding: 0; 39 | 40 | + .el-checkbox { 41 | margin-left: 0; 42 | } 43 | } 44 | } 45 | .demo-form-normal { 46 | width: 440px; 47 | } 48 | .demo-form-inline { 49 | .el-input { 50 | width: 150px; 51 | } 52 | > * { 53 | margin-right: 10px; 54 | } 55 | } 56 | .demo-dynamic { 57 | .el-input { 58 | display: inline-block; 59 | margin-right: 9px; 60 | width: 270px; 61 | vertical-align: top; 62 | } 63 | } 64 | .fr { 65 | float: right; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /typings/typing-tests/Collapse.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Collapse } from 'element-react' 3 | import { Collapse as CollapseNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | onChange = (activeNames) => { } 7 | onClick = (item) => { } 8 | render() { 9 | return ( 10 |
    11 | 12 | item 13 | 14 | 15 | title
    )} name="name">item 16 | 17 | 18 | 19 | item 20 | 21 | 22 | title
    )} name="name">item 23 | 24 | 25 | ) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/badge/Badge.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | type Props = { 7 | children: React.DOM, 8 | value: number | string, 9 | max: number, 10 | isDot: boolean, 11 | } 12 | 13 | export default class Badge extends Component { 14 | props: Props; 15 | 16 | render(): React.DOM { 17 | const { children, value, max, isDot } = this.props; 18 | const className = this.classNames({ 19 | 'el-badge__content': true, 20 | 'is-fixed': !!children, 21 | 'is-dot': !!isDot, 22 | }); 23 | let content; 24 | 25 | if (isDot) { 26 | content = null; 27 | } else { 28 | if (typeof value === 'number' && typeof max === 'number') { 29 | content = max < value ? `${max}+` : value; 30 | } else { 31 | content = value; 32 | } 33 | } 34 | 35 | return ( 36 |
    37 | { children } 38 | { content } 39 |
    40 | ) 41 | } 42 | } 43 | 44 | Badge.propTypes = { 45 | value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), 46 | max: PropTypes.number, 47 | isDot: PropTypes.bool, 48 | } 49 | 50 | Badge.defaultProps = { 51 | isDot: false, 52 | } 53 | -------------------------------------------------------------------------------- /src/breadcrumb/__test__/Breadcrumb_test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow, mount } from 'enzyme'; 3 | 4 | import Breadcrumb from '../'; 5 | 6 | describe('Breadcrumb test', () => { 7 | it('basic usage', () => { 8 | const w = shallow( 9 | 10 | 首页 11 | 活动管理 12 | 13 | ); 14 | expect(w.is('.el-breadcrumb')).toBe(true); 15 | }); 16 | 17 | it('test children', () => { 18 | const w = mount( 19 | 20 | 首页 21 | 活动管理 22 | 活动列表 23 | 活动详情 24 | 25 | ); 26 | expect(w.find('.el-breadcrumb__item').length).toBe(4); 27 | }); 28 | 29 | it('test separator', () => { 30 | const w = mount( 31 | 32 | 首页 33 | 活动管理 34 | 活动列表 35 | 活动详情 36 | 37 | ); 38 | expect(w.find('.el-breadcrumb__separator').at(0).text()).toBe('/'); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/tag/__test__/Tag_test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow, mount } from 'enzyme'; 3 | import sinon from 'sinon'; 4 | 5 | import Tag from '../'; 6 | 7 | describe('Tag test', () => { 8 | it('type', () => { 9 | const w = mount( 10 | TEST 11 | ); 12 | expect(w.find('.el-tag--primary')).toHaveLength(1); 13 | expect(w.find('.el-tag--primary').text()).toBe('TEST'); 14 | }); 15 | 16 | it('closable', () => { 17 | const w = shallow( 18 | TEST 19 | ); 20 | expect(w.find('i.el-tag__close').exists()).toBe(true); 21 | }); 22 | 23 | // it('closeTransition', () => { 24 | // const w = shallow( 25 | // TEST 26 | // ); 27 | // expect(w.find('[name="el-zoom-in-center"]').exists()).toBe(true); 28 | // }); 29 | 30 | it('hit', () => { 31 | const w = mount( 32 | TEST 33 | ); 34 | expect(w.find('.el-tag').first().hasClass('is-hit')).toBeTruthy(); 35 | }); 36 | 37 | it('onClose', () => { 38 | const onClose = sinon.spy(); 39 | const w = shallow( 40 | TEST 41 | ); 42 | 43 | w.find('i.el-tag__close').simulate('click'); 44 | 45 | expect(onClose.calledOnce).toBe(true); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /src/input-number/util.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export function accSub(arg1: number, arg2: number): number { 4 | var r1, r2, m, n; 5 | try { 6 | r1 = arg1.toString().split('.')[1].length; 7 | } catch (e) { 8 | r1 = 0; 9 | } 10 | try { 11 | r2 = arg2.toString().split('.')[1].length; 12 | } catch (e) { 13 | r2 = 0; 14 | } 15 | m = Math.pow(10, Math.max(r1, r2)); 16 | n = (r1 >= r2) ? r1 : r2; 17 | return parseFloat(((arg1 * m - arg2 * m) / m).toFixed(n)); 18 | } 19 | 20 | export function accAdd(arg1: number, arg2: number): number { 21 | var r1, r2, m, c; 22 | try { 23 | r1 = arg1.toString().split('.')[1].length; 24 | } catch (e) { 25 | r1 = 0; 26 | } 27 | try { 28 | r2 = arg2.toString().split('.')[1].length; 29 | } catch (e) { 30 | r2 = 0; 31 | } 32 | c = Math.abs(r1 - r2); 33 | m = Math.pow(10, Math.max(r1, r2)); 34 | if (c > 0) { 35 | var cm = Math.pow(10, c); 36 | if (r1 > r2) { 37 | arg1 = Number(arg1.toString().replace('.', '')); 38 | arg2 = Number(arg2.toString().replace('.', '')) * cm; 39 | } else { 40 | arg1 = Number(arg1.toString().replace('.', '')) * cm; 41 | arg2 = Number(arg2.toString().replace('.', '')); 42 | } 43 | } else { 44 | arg1 = Number(arg1.toString().replace('.', '')); 45 | arg2 = Number(arg2.toString().replace('.', '')); 46 | } 47 | return (arg1 + arg2) / m; 48 | } 49 | -------------------------------------------------------------------------------- /site/docs/zh-CN/i18n.md: -------------------------------------------------------------------------------- 1 | ## 国际化 2 | 3 | Element 组件内部默认使用中文,若希望使用其他语言,则需要进行多语言设置。以英文为例,在 main.js 中: 4 | 5 | ```javascript 6 | import { i18n } from 'element-react' 7 | import locale from 'element-react/src/locale/lang/en' 8 | 9 | i18n.use(locale); 10 | ``` 11 | 12 | 如果使用其它语言,默认情况下中文语言包依旧是被引入的,可以使用 webpack 的 NormalModuleReplacementPlugin 替换默认语言包。 13 | 14 | **webpack.config.js** 15 | ```javascript 16 | { 17 | plugins: [ 18 | new webpack.NormalModuleReplacementPlugin(/element-react[\/\\]src[\/\\]locale[\/\\]lang[\/\\]zh-CN/, 'element-react/src/locale/lang/en') 19 | ] 20 | } 21 | ``` 22 | 23 | 目前 Element 内置了以下语言: 24 |
      25 |
    • 简体中文(zh-CN)
    • 26 |
    • 英语(en)
    • 27 |
    • 德语(de)
    • 28 |
    • 葡萄牙语(pt)
    • 29 |
    • 西班牙语(es)
    • 30 |
    • 丹麦语(da)
    • 31 |
    • 法语(fr)
    • 32 |
    • 挪威语(nb-NO)
    • 33 |
    • 繁体中文(zh-TW)
    • 34 |
    • 意大利语(it)
    • 35 |
    • 韩语(ko)
    • 36 |
    • 日语(ja)
    • 37 |
    • 荷兰语(nl)
    • 38 |
    • 越南语(vi)
    • 39 |
    • 俄语(ru-RU)
    • 40 |
    • 土耳其语(tr-TR)
    • 41 |
    • 巴西葡萄牙语(pt-br)
    • 42 |
    • 波斯语(fa)
    • 43 |
    • 泰语(th)
    • 44 |
    • 印尼语(id)
    • 45 |
    • 保加利亚语(bg)
    • 46 |
    • 波兰语(pl)
    • 47 |
    • 芬兰语(fi)
    • 48 |
    • 瑞典语(sv-SE)
    • 49 |
    • 希腊语(el)
    • 50 |
    • 斯洛伐克语(sk)
    • 51 |
    52 | 53 | 如果你需要使用其他的语言,欢迎贡献 PR:只需在 [这里](https://github.com/ElemeFE/element/tree/master/src/locale/lang) 添加一个语言配置文件即可。 54 | -------------------------------------------------------------------------------- /src/date-picker/DatePicker.jsx: -------------------------------------------------------------------------------- 1 | //@flow 2 | import React from 'react'; 3 | 4 | import { pick } from '../../libs/utils' 5 | import { SELECTION_MODES } from './utils' 6 | 7 | import BasePicker from './BasePicker' 8 | import DatePanel from './panel/DatePanel' 9 | import type { DatePickerProps } from './Types'; 10 | 11 | 12 | export default class DatePicker extends BasePicker { 13 | static get propTypes() { 14 | return Object.assign( 15 | {}, 16 | BasePicker.propTypes, 17 | pick(DatePanel.propTypes, 18 | ['value', 'shortcuts', 'selectionMode', 'disabledDate', 'showWeekNumber', 'firstDayOfWeek', 'isShowTime'])) 19 | } 20 | 21 | static get defaultProps() { 22 | let result: any = Object.assign({}, BasePicker.defaultProps) 23 | return result; 24 | } 25 | 26 | constructor(props: DatePickerProps) { 27 | let type = 'date' 28 | switch (props.selectionMode) { 29 | case SELECTION_MODES.YEAR: 30 | type = 'year'; break; 31 | case SELECTION_MODES.MONTH: 32 | type = 'month'; break; 33 | case SELECTION_MODES.WEEK: 34 | type = 'week'; break; 35 | } 36 | super(props, type, {}) 37 | } 38 | 39 | pickerPanel(state: any, props: DatePickerProps) { 40 | return ( 41 | 46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/date-picker/DateRangePicker.jsx: -------------------------------------------------------------------------------- 1 | //@flow 2 | import React from 'react'; 3 | 4 | import { pick } from '../../libs/utils' 5 | import { PropTypes } from '../../libs'; 6 | 7 | import BasePicker from './BasePicker' 8 | import DateRangePanel from './panel/DateRangePanel' 9 | import type { DateRangePickerProps } from './Types'; 10 | 11 | export default class DateRangePicker extends BasePicker { 12 | static get propTypes() { 13 | return Object.assign( 14 | {}, 15 | {rangeSeparator: PropTypes.string}, 16 | BasePicker.propTypes, 17 | // default value is been defined in ./constants file 18 | pick(DateRangePanel.propTypes, 19 | ['value', 'isShowTime', 'shortcuts', 'firstDayOfWeek'])) 20 | } 21 | 22 | static get defaultProps() { 23 | let result: any = Object.assign({}, BasePicker.defaultProps) 24 | return result; 25 | } 26 | 27 | constructor(props: DateRangePickerProps) { 28 | super(props, 'daterange', {}) 29 | } 30 | 31 | getFormatSeparator(){ 32 | return this.props.rangeSeparator 33 | } 34 | 35 | pickerPanel(state: any, props: DateRangePickerProps) { 36 | let value = state.value 37 | if (value instanceof Date) { 38 | value = [value, null] 39 | } 40 | return ( 41 | 46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /site/docs/zh-CN/color-picker.md: -------------------------------------------------------------------------------- 1 | ## ColorPicker 颜色选择器 2 | 3 | 用于颜色选择,支持多种格式。 4 | 5 | ### 基础用法 6 | 7 | :::demo 通过value属性控制当前显示的颜色。 8 | ```js 9 | render() { 10 | const color1 = '#20a0ff'; 11 | const color2 = null; 12 | return ( 13 |
    14 |
    15 | 有默认值 16 | 17 |
    18 |
    19 | 无默认值 20 | 21 |
    22 |
    23 | ) 24 | } 25 | ``` 26 | ::: 27 | 28 | ### 选择透明度 29 | 30 | :::demo ColorPicker 支持普通颜色,也支持带 Alpha 通道的颜色,通过`showAlpha`属性即可控制是否支持透明度的选择。 31 | ```js 32 | render() { 33 | const color3 = 'rgba(19, 206, 102, 0.8)'; 34 | return ( 35 |
    36 | 37 |
    38 | ) 39 | } 40 | ``` 41 | ::: 42 | 43 | ### Attributes 44 | | 参数 | 说明 | 类型 | 可选值 | 默认值 | 45 | |---------- |-------- |---------- |------------- |-------- | 46 | | showAlpha | 是否支持透明度选择 | boolean | — | false | 47 | | colorFormat | 写入 value 的颜色的格式 | string | hsl / hsv / hex / rgb | hex(show-alpha 为 false)/ rgb(show-alpha 为 true) | 48 | 49 | ### Events 50 | | 事件名称 | 说明 | 回调参数 | 51 | |---------- |-------- |---------- | 52 | | onChange | 当绑定值变化时触发 | 当前值 | 53 | -------------------------------------------------------------------------------- /src/checkbox/CheckBoxButton.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import CheckBox from './CheckBox'; 5 | 6 | export default class CheckboxButton extends CheckBox { 7 | static elementType = 'CheckboxButton'; 8 | 9 | render(): React.DOM { 10 | const group = this.context.ElCheckboxGroup; 11 | 12 | return ( 13 | 36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /site/pages/input/style.scss: -------------------------------------------------------------------------------- 1 | .demo-input { 2 | .el-select .el-input { 3 | width: 100px; 4 | } 5 | .text { 6 | font-size: 14px; 7 | color: #8492a6; 8 | } 9 | .el-input { 10 | width: 180px; 11 | 12 | & + .el-input, 13 | & + .el-textarea { 14 | margin-top: 15px; 15 | } 16 | } 17 | .el-textarea { 18 | width: 414px; 19 | } 20 | .el-input-group { 21 | width: 100%; 22 | } 23 | .el-input-group + .el-input-group { 24 | margin-top: 15px; 25 | } 26 | .el-autocomplete { 27 | display: inline-block; 28 | } 29 | .inline-input { 30 | .el-input { 31 | display: inline-block; 32 | vertical-align: top; 33 | margin: 10px 5px; 34 | } 35 | .el-autocomplete { 36 | margin: 10px 0 0; 37 | 38 | .el-input { 39 | margin: 0; 40 | } 41 | } 42 | } 43 | .tac { 44 | text-align: center; 45 | 46 | .el-autocomplete { 47 | text-align: left; 48 | } 49 | } 50 | .el-row.border-grid { 51 | .el-col:not(:last-child) { 52 | border-right: 1px solid rgba(224,230,237,0.50); 53 | } 54 | } 55 | .my-autocomplete { 56 | li { 57 | line-height: normal; 58 | padding: 7px; 59 | 60 | .name { 61 | text-overflow: ellipsis; 62 | overflow: hidden; 63 | } 64 | .addr { 65 | font-size: 12px; 66 | color: #b4b4b4; 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /typings/typing-tests/AutoComplete.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { AutoComplete, Icon } from 'element-react' 3 | import { AutoComplete as AutoCompleteNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | fetchSuggestions = (queryString, cb) => { } 7 | onSelect = (item) => { } 8 | onIconClick = () => { } 9 | render() { 10 | return ( 11 |
    12 | 13 | } size="large" /> 14 | 15 | s
    )} prepend={(
    s
    )} /> 16 | 17 | 18 | } size="large" /> 19 | 20 | s)} prepend={(
    s
    )} /> 21 | 22 | ) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/layout/Row.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | export default class Row extends Component { 7 | getChildContext(): { gutter: number | string } { 8 | return { 9 | gutter: this.props.gutter 10 | }; 11 | } 12 | 13 | getStyle(): { marginLeft: string, marginRight: string } { 14 | const style = {}; 15 | 16 | if (this.props.gutter) { 17 | style.marginLeft = `-${this.props.gutter / 2}px`; 18 | style.marginRight = style.marginLeft; 19 | } 20 | 21 | return style; 22 | } 23 | 24 | render(): React.DOM { 25 | return React.createElement(this.props.tag, { 26 | className: this.className('el-row', 27 | this.props.justify !== 'start' && `is-justify-${this.props.justify}`, 28 | this.props.align !== 'top' && `is-align-${this.props.align}`, { 29 | 'el-row--flex': this.props.type === 'flex' 30 | } 31 | ), 32 | style: this.style(this.getStyle()) 33 | }, this.props.children); 34 | } 35 | } 36 | 37 | Row.childContextTypes = { 38 | gutter: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) 39 | }; 40 | 41 | Row.propTypes = { 42 | gutter: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), 43 | type: PropTypes.string, 44 | justify: PropTypes.string, 45 | align: PropTypes.string, 46 | tag: PropTypes.string 47 | } 48 | 49 | Row.defaultProps = { 50 | justify: 'start', 51 | align: 'top', 52 | tag: 'div' 53 | }; 54 | -------------------------------------------------------------------------------- /site/pages/typography/style.scss: -------------------------------------------------------------------------------- 1 | .demo-typo-box { 2 | height: 200px; 3 | width: 200px; 4 | position: relative; 5 | border: 1px solid #eaeefb; 6 | font-size: 40px; 7 | color: #1f2d3d; 8 | text-align: center; 9 | line-height: 162px; 10 | padding-bottom: 36px; 11 | box-sizing: border-box; 12 | display: inline-block; 13 | margin-right: 17px; 14 | border-radius: 4px; 15 | 16 | .name { 17 | position: absolute; 18 | bottom: 0; 19 | width: 100%; 20 | height: 35px; 21 | border-top: 1px solid #eaeefb; 22 | font-size: 14px; 23 | color: #8492a6; 24 | line-height: 35px; 25 | text-align: left; 26 | text-indent: 10px; 27 | font-family: 'Helvetica Neue'; 28 | } 29 | } 30 | .demo-typo-size { 31 | .h1 { 32 | font-size: 20px; 33 | } 34 | .h2 { 35 | font-size: 18px; 36 | } 37 | .h3 { 38 | font-size: 16px; 39 | } 40 | .text-regular { 41 | font-size: 14px; 42 | } 43 | .text-small { 44 | font-size: 13px; 45 | } 46 | .text-smaller { 47 | font-size: 12px; 48 | } 49 | .color-dark-light { 50 | color: #99a9bf; 51 | } 52 | } 53 | .typo-PingFang { 54 | font-family: 'PingFang SC'; 55 | } 56 | .typo-Hiragino { 57 | font-family: 'Hiragino Sans GB'; 58 | } 59 | .typo-Microsoft { 60 | font-family: 'Microsoft YaHei'; 61 | } 62 | /* 英文 */ 63 | .typo-Helvetica-Neue { 64 | font-family: 'Helvetica Neue'; 65 | } 66 | .typo-Helvetica { 67 | font-family: 'Helvetica'; 68 | } 69 | .typo-Arial { 70 | font-family: 'Arial'; 71 | } 72 | -------------------------------------------------------------------------------- /src/message/Message.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import Toast from './Toast'; 5 | 6 | export default function Message(props = {}, type) { 7 | const div = document.createElement('div'); 8 | const messageBox = document.getElementsByClassName('el-message-content')[0]; 9 | if (messageBox) { 10 | messageBox.appendChild(div); 11 | document.body.appendChild(messageBox); 12 | } else { 13 | const messageBox = document.createElement('div'); 14 | messageBox.className = "el-message-content"; 15 | messageBox.appendChild(div); 16 | document.body.appendChild(messageBox); 17 | } 18 | 19 | if (typeof props === 'string' || React.isValidElement(props)) { 20 | props = { 21 | message: props 22 | }; 23 | } 24 | 25 | if (type) { 26 | props.type = type; 27 | } 28 | 29 | const component = React.createElement(Toast, Object.assign(props, { 30 | willUnmount: () => { 31 | const messageBox = document.getElementsByClassName('el-message-content')[0]; 32 | ReactDOM.unmountComponentAtNode(div); 33 | messageBox.removeChild(div); 34 | 35 | if (props.onClose instanceof Function) { 36 | props.onClose(); 37 | } 38 | } 39 | })); 40 | 41 | ReactDOM.render(component, div); 42 | } 43 | 44 | /* eslint-disable */ 45 | ['success', 'warning', 'info', 'error'].forEach(type => { 46 | Message[type] = (options = {}) => { 47 | return Message(options, type); 48 | }; 49 | }); 50 | /* eslint-enable */ 51 | -------------------------------------------------------------------------------- /src/upload/Cover.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | export default class Cover extends Component { 7 | static defaultProps = { 8 | onFile: Function 9 | }; 10 | 11 | constructor(props: Object) { 12 | super(props); 13 | this.state = { 14 | dragOver: false 15 | }; 16 | } 17 | 18 | handleDragover(e: SyntheticDragEvent): void { 19 | e.preventDefault(); 20 | if(!this.props.disabled){ 21 | this.setState({ dragOver: true }); 22 | } 23 | } 24 | 25 | handleDragleave(e: SyntheticDragEvent): void { 26 | e.preventDefault(); 27 | this.setState({ dragOver: false }); 28 | } 29 | 30 | onDrop(e: SyntheticDragEvent): void { 31 | if(this.props.disabled) return 32 | e.preventDefault(); 33 | this.setState({ dragOver: false }); 34 | this.props.onFile(e.dataTransfer.files); 35 | } 36 | 37 | render(): React.DOM { 38 | const { dragOver } = this.state; 39 | return ( 40 |
    this.onDrop(e)} 46 | onDragOver={e => this.handleDragover(e)} 47 | onDragLeave={e => this.handleDragleave(e)} 48 | > 49 | {this.props.children} 50 |
    51 | ); 52 | } 53 | } 54 | 55 | Cover.propTypes = { 56 | onFile: PropTypes.func, 57 | disabled: PropTypes.bool 58 | }; 59 | -------------------------------------------------------------------------------- /src/tag/Tag.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes, Transition, View } from '../../libs'; 5 | 6 | export default class Tag extends Component { 7 | constructor(props: Object) { 8 | super(props); 9 | 10 | this.state = { 11 | visible: true 12 | }; 13 | } 14 | 15 | handleClose() { 16 | this.setState({ 17 | visible: false 18 | }, () => { 19 | if (this.props.onClose) { 20 | this.props.onClose(); 21 | } 22 | }); 23 | } 24 | 25 | render() { 26 | const { type, hit, closable, closeTransition, color } = this.props; 27 | 28 | return( 29 | 30 | 31 | 39 | {this.props.children} 40 | { closable && } 41 | 42 | 43 | 44 | ) 45 | } 46 | } 47 | 48 | Tag.propTypes = { 49 | closable: PropTypes.bool, 50 | type: PropTypes.string, 51 | hit: PropTypes.bool, 52 | color: PropTypes.string, 53 | closeTransition: PropTypes.bool, 54 | onClose: PropTypes.func 55 | } 56 | -------------------------------------------------------------------------------- /src/button/Button.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | export default class Button extends Component { 7 | onClick(e: SyntheticEvent): void { 8 | if (!this.props.loading) { 9 | this.props.onClick && this.props.onClick(e); 10 | } 11 | } 12 | 13 | render(): React.DOM { 14 | return ( 15 | 24 | ) 25 | } 26 | } 27 | 28 | Button.propTypes = { 29 | onClick: PropTypes.func, 30 | type: PropTypes.string, 31 | size: PropTypes.string, 32 | icon: PropTypes.string, 33 | nativeType: PropTypes.string, 34 | loading: PropTypes.bool, 35 | disabled: PropTypes.bool, 36 | plain: PropTypes.bool 37 | } 38 | 39 | Button.defaultProps = { 40 | type: 'default', 41 | nativeType: 'button', 42 | loading: false, 43 | disabled: false, 44 | plain: false 45 | }; 46 | -------------------------------------------------------------------------------- /typings/typing-tests/Alert.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Alert } from 'element-react' 3 | import { Alert as AlertNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | onClose = () => { } 7 | render() { 8 | return ( 9 |
    10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
    33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/menu/MenuItemGroup.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { PropTypes } from '../../libs'; 5 | 6 | import MixinComponent from './MixinComponent'; 7 | 8 | type State = { 9 | paddingLeft: number 10 | }; 11 | 12 | export default class MenuItemGroup extends MixinComponent { 13 | state: State; 14 | instanceType: string; 15 | 16 | constructor(props: Object) { 17 | super(props); 18 | 19 | this.instanceType = 'MenuItemGroup'; 20 | 21 | this.state = { 22 | paddingLeft: 20 23 | } 24 | } 25 | 26 | componentDidMount() { 27 | this.initPadding(); 28 | } 29 | 30 | initPadding(): void { 31 | let level = 0, parent = this.parent(), component = parent.instanceType; 32 | 33 | while (component !== 'Menu') { 34 | if (component === 'SubMenu') { 35 | level++; 36 | } 37 | 38 | parent = parent.parent(); 39 | component = parent.instanceType; 40 | } 41 | 42 | this.setState({ 43 | paddingLeft: this.state.paddingLeft + level * 10 44 | }); 45 | } 46 | 47 | render(): React.DOM { 48 | return ( 49 |
  • 50 |
    {this.props.title}
    53 |
      54 | {this.props.children} 55 |
    56 |
  • 57 | ) 58 | } 59 | } 60 | 61 | MenuItemGroup.propTypes = { 62 | title: PropTypes.string.isRequired 63 | }; 64 | -------------------------------------------------------------------------------- /src/date-picker/TimeRangePicker.jsx: -------------------------------------------------------------------------------- 1 | //@flow 2 | import React from 'react'; 3 | import { debounce } from 'throttle-debounce'; 4 | import { PropTypes } from '../../libs'; 5 | 6 | import BasePicker from './BasePicker' 7 | import TimeRangePanel from './panel/TimeRangePanel' 8 | import type { TimeRangePickerProps } from './Types'; 9 | 10 | 11 | export default class TimeRangePicker extends BasePicker { 12 | static get propTypes() { 13 | let result: any = Object.assign({}, { rangeSeparator: PropTypes.string }, 14 | BasePicker.propTypes) 15 | return result; 16 | } 17 | 18 | static get defaultProps() { 19 | let result: any = Object.assign({}, BasePicker.defaultProps) 20 | return result; 21 | } 22 | 23 | constructor(props: TimeRangePickerProps) { 24 | super(props, 'timerange', {}) 25 | this._onSelectionChange = debounce(200, this.onSelectionChange.bind(this)) 26 | } 27 | 28 | onSelectionChange(start: number, end: number) { 29 | this.refs.inputRoot.refs.input.setSelectionRange(start, end); 30 | this.refs.inputRoot.refs.input.focus(); 31 | } 32 | 33 | getFormatSeparator() { 34 | return this.props.rangeSeparator 35 | } 36 | 37 | pickerPanel(state: any, props: TimeRangePickerProps) { 38 | return ( 39 | this.setState({ pickerVisible: false })} 43 | onPicked={this.onPicked.bind(this)} 44 | onSelectRangeChange={this._onSelectionChange} 45 | /> 46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/radio/RadioGroup.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | export default class RadioGroup extends Component { 7 | getChildContext(): { component: RadioGroup } { 8 | return { 9 | component: this 10 | }; 11 | } 12 | 13 | onChange(value: mixed) { 14 | if (this.props.onChange) { 15 | this.props.onChange(value) 16 | } 17 | } 18 | 19 | render() { 20 | return ( 21 |
    22 | { 23 | React.Children.map(this.props.children, element => { 24 | if (!element) { 25 | return null; 26 | } 27 | 28 | const { elementType } = element.type; 29 | if (elementType !== 'Radio' && elementType !== 'RadioButton') { 30 | return null; 31 | } 32 | 33 | return React.cloneElement(element, Object.assign({}, element.props, { 34 | onChange: this.onChange.bind(this), 35 | model: this.props.value, 36 | size: this.props.size 37 | })) 38 | }) 39 | } 40 |
    41 | ) 42 | } 43 | } 44 | 45 | RadioGroup.childContextTypes = { 46 | component: PropTypes.any 47 | }; 48 | 49 | RadioGroup.propTypes = { 50 | value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 51 | disabled: PropTypes.bool, 52 | size: PropTypes.string, 53 | textColor: PropTypes.string, 54 | fill: PropTypes.string, 55 | onChange: PropTypes.func 56 | } 57 | -------------------------------------------------------------------------------- /typings/typing-tests/Input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Input } from 'element-react' 3 | import { Input as InputNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | onClose = () => { } 7 | render() { 8 | return ( 9 |
    10 | 11 | icon
    )} size="mini" /> 12 | 13 | 29 | 30 | 31 | icon)} size="mini" /> 32 | 33 | 49 | 50 | ) 51 | } 52 | } 53 | 54 | // TODO: 这里的测试没写完整 55 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "babel-eslint", 4 | "extends": [ "airbnb", "eslint:recommended", "plugin:flowtype/recommended" ], 5 | "plugins": [ "react", "jsx-a11y", "flowtype" ], 6 | "env": { 7 | "browser": true, 8 | "commonjs": true, 9 | "es6": true, 10 | "jest": true 11 | }, 12 | "settings": { 13 | "flowtype": { 14 | "onlyFilesWithFlowAnnotation": true 15 | } 16 | }, 17 | "rules": { 18 | "jsx-a11y/no-static-element-interactions": 0, 19 | "react/self-closing-comp": 0, 20 | "react/jsx-no-bind": 0, 21 | "react/jsx-curly-spacing": 0, 22 | "react/no-string-refs": 0, 23 | "react/jsx-closing-bracket-location": 0, 24 | "react/jsx-boolean-value": 0, 25 | "react/no-did-mount-set-state": 1, 26 | "react/jsx-first-prop-new-line": 0, 27 | "react/jsx-max-props-per-line": 0, 28 | "react/require-default-props": 0, 29 | "jsx-a11y/img-has-alt": 0, 30 | "jsx-a11y/label-has-for": 0, 31 | "jsx-a11y/no-noninteractive-element-interactions": 0, 32 | "import/no-named-as-default-member": 0, 33 | "import/prefer-default-export": 0, 34 | "import/no-extraneous-dependencies": [1, {"devDependencies": ["**/__test__/**/*.js", "**/__test__/**/*.jsx", "**/*_test.jsx", "**/*_test.js"]}], 35 | "no-empty": 1, 36 | "no-console": 1, 37 | "react/sort-comp": 0, 38 | "react/prefer-stateless-function": 0, 39 | "react/prop-types": [1, { "ignore": ["children", "key"] }], 40 | "react/no-find-dom-node": 0, 41 | "react/no-unused-prop-types": 1, 42 | "react/forbid-prop-types": [1, { "forbid": ["array"] }], 43 | "react/no-array-index-key": 0 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /site/docs/en-US/color-picker.md: -------------------------------------------------------------------------------- 1 | ## ColorPicker 2 | 3 | ColorPicker is a color selector supporting multiple color formats. 4 | 5 | ### Basic usage 6 | 7 | :::demo ColorPicker requires a string typed variable to be bound to `value`. 8 | ```js 9 | render() { 10 | const color1 = '#20a0ff'; 11 | const color2 = null; 12 | return ( 13 |
    14 |
    15 | With default value 16 | 17 |
    18 |
    19 | With no default value 20 | 21 |
    22 |
    23 | ) 24 | } 25 | ``` 26 | ::: 27 | 28 | ### Alpha 29 | 30 | :::demo ColorPicker supports alpha channel selecting. To activate alpha selecting, just add the `showAlpha` attribute. 31 | ```js 32 | render() { 33 | const color3 = 'rgba(19, 206, 102, 0.8)'; 34 | return ( 35 |
    36 | 37 |
    38 | ) 39 | } 40 | ``` 41 | ::: 42 | 43 | ### Attributes 44 | | Attribute | Description | Type | Accepted Values | Default | 45 | |---------- |-------- |---------- |------------- |-------- | 46 | | showAlpha | whether to display the alpha slider | boolean | — | false | 47 | | colorFormat | color format of v-model | string | hsl / hsv / hex / rgb | hex (when show-alpha is false)/ rgb (when show-alpha is true) | 48 | 49 | ### Events 50 | | Event Name | Description | Parameters | 51 | |---------|--------|---------| 52 | | onChange | triggers when input value changes | color value | 53 | -------------------------------------------------------------------------------- /src/date-picker/TimeSelect.jsx: -------------------------------------------------------------------------------- 1 | //@flow 2 | import React from 'react'; 3 | 4 | import { PropTypes } from '../../libs'; 5 | import BasePicker from './BasePicker' 6 | 7 | import TimeSelectPanel from './panel/TimeSelectPanel' 8 | import type { TimeSelectProps, ValidDateType } from './Types'; 9 | 10 | export default class TimeSelect extends BasePicker { 11 | static get propTypes() { 12 | let result: any = Object.assign({}, { 13 | start: PropTypes.string, 14 | end: PropTypes.string, 15 | step: PropTypes.string, 16 | minTime: PropTypes.instanceOf(Date), 17 | }, 18 | BasePicker.propTypes) 19 | 20 | return result; 21 | } 22 | 23 | static get defaultProps() { 24 | let result: any = Object.assign({}, BasePicker.defaultProps) 25 | return result; 26 | } 27 | 28 | 29 | constructor(props: TimeSelectProps) { 30 | // props, type, state 31 | super(props, 'timeselect', {}); 32 | } 33 | 34 | isDateValid(value: ValidDateType) { 35 | return super.isDateValid(value) && TimeSelectPanel.isValid(this.dateToStr(value), this.panelProps()) 36 | } 37 | 38 | panelProps(props: ?TimeSelectProps){ 39 | const ps = props || this.props 40 | const minTime = this.dateToStr(this.props.minTime) 41 | return {...ps, minTime} 42 | } 43 | 44 | pickerPanel(state: any, props: TimeSelectProps) { 45 | const value = this.dateToStr(state.value) 46 | return ( 47 | { 52 | const r = this.parseDate(str) 53 | return r 54 | } } 55 | /> 56 | ) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/dropdown/DropdownMenu.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import Popper from 'popper.js'; 6 | import { Component, PropTypes, Transition, View } from '../../libs'; 7 | 8 | type State = { 9 | showPopper: boolean 10 | }; 11 | 12 | export default class DropdownMenu extends Component { 13 | state: State; 14 | 15 | constructor(props: Object) { 16 | super(props); 17 | 18 | this.state = { 19 | showPopper: false 20 | } 21 | } 22 | 23 | onVisibleChange(visible: boolean): void { 24 | this.setState({ 25 | showPopper: visible 26 | }) 27 | } 28 | 29 | onEnter(): void { 30 | const parent = ReactDOM.findDOMNode(this.parent()); 31 | 32 | this.popperJS = new Popper(parent, this.refs.popper, { 33 | placement: this.placement(), 34 | modifiers: { 35 | computeStyle: { 36 | gpuAcceleration: false 37 | } 38 | } 39 | }); 40 | } 41 | 42 | onAfterLeave(): void { 43 | this.popperJS.destroy(); 44 | } 45 | 46 | parent(): Component { 47 | return this.context.component; 48 | } 49 | 50 | placement(): string { 51 | return `bottom-${this.parent().props.menuAlign}`; 52 | } 53 | 54 | render(): React.DOM { 55 | return ( 56 | 57 | 58 |
      59 | {this.props.children} 60 |
    61 |
    62 |
    63 | ) 64 | } 65 | } 66 | 67 | DropdownMenu.contextTypes = { 68 | component: PropTypes.any 69 | }; 70 | -------------------------------------------------------------------------------- /typings/typing-tests/Rate.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Rate } from 'element-react' 3 | import { Rate as RateNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | onChange = () => { } 7 | render() { 8 | return ( 9 |
    10 | 11 | 30 | 31 | 32 | 51 |
    52 | ) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /typings/typing-tests/Tabs.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Tabs } from 'element-react' 3 | import { Tabs as TabsNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | onTabClick = (tab) => { } 7 | onTabRemove = (name) => { } 8 | onTabAdd = () => { } 9 | onTabEdit = (targetName, action) => { } 10 | render() { 11 | return ( 12 |
    13 | 14 | 管理 15 | 16 | 17 | 管理 18 | 19 | 20 | label
    } name="pane" disabled={true} closable={true}>管理 21 | 22 | 23 | 24 | 25 | 管理 26 | 27 | 28 | 管理 29 | 30 | 31 | label} name="pane" disabled={true} closable={true}>管理 32 | 33 | 34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /libs/animate/transition.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Transition as ReactTransition } from 'react-transition-group'; 3 | import View from '../view'; 4 | 5 | const noneFun = () => undefined; 6 | 7 | class Transition extends Component { 8 | 9 | render() { 10 | const { 11 | in: inProp, 12 | onEnter, 13 | onEntering, 14 | onEntered, 15 | onExit, 16 | onExiting, 17 | onExited, 18 | addEndListener, 19 | unmountOnExit, 20 | appear, 21 | duration, 22 | mountOnEnter, 23 | transitionClass 24 | } = this.props; 25 | return ( 26 | onEnter()} 28 | onEntering={() => onEntering()} 29 | onEntered={() => onEntered()} 30 | onExit={() => onExit()} 31 | onExiting={() => onExiting()} 32 | onExited={() => onExited()} 33 | addEndListener={() => addEndListener()} 34 | in={inProp} 35 | mountOnEnter={mountOnEnter} 36 | unmountOnExit={unmountOnExit} 37 | appear={appear} 38 | timeout={duration} 39 | > 40 | { 41 | state => ( 42 | 45 | {this.props.children} 46 | 47 | ) 48 | } 49 | 50 | ); 51 | } 52 | } 53 | 54 | Transition.defaultProps = { 55 | onEnter: noneFun, 56 | onEntering: noneFun, 57 | onEntered: noneFun, 58 | onExit: noneFun, 59 | onExiting: noneFun, 60 | onExited: noneFun, 61 | addEndListener: noneFun, 62 | mountOnEnter: false, 63 | unmountOnExit: false, 64 | appear: true, 65 | duration: 0 66 | } 67 | 68 | export default Transition; 69 | -------------------------------------------------------------------------------- /site/run.js: -------------------------------------------------------------------------------- 1 | /* eslint import/no-extraneous-dependencies: ["off"] */ 2 | 3 | const path = require('path'); 4 | const webpack = require('webpack'); 5 | const WebpackDevServer = require('webpack-dev-server'); 6 | 7 | new WebpackDevServer(webpack({ 8 | devtool: 'eval', 9 | entry: [ 10 | 'webpack-dev-server/client?http://localhost:3000', 11 | 'webpack/hot/only-dev-server', 12 | 'react-hot-loader/patch', 13 | './index' 14 | ], 15 | output: { 16 | path: path.join(__dirname, 'dist'), 17 | filename: 'bundle.js' 18 | }, 19 | plugins: [ 20 | new webpack.HotModuleReplacementPlugin() 21 | ], 22 | resolve: { 23 | extensions: ['.js', '.jsx'] 24 | }, 25 | module: { 26 | rules: [ 27 | { 28 | test: /\.jsx?$/, 29 | loader: 'babel-loader', 30 | include: [ 31 | path.join(__dirname, '../site'), 32 | path.join(__dirname, '../src'), 33 | path.join(__dirname, '../libs') 34 | ] 35 | }, 36 | { 37 | test: /\.css$/, 38 | use: ['style-loader', 'css-loader'] 39 | }, 40 | { 41 | test: /\.scss$/, 42 | use: ['style-loader', 'css-loader', 'sass-loader'] 43 | }, 44 | { 45 | test: /\.(eot|svg|ttf|woff|woff2)(\?.+)?$/, 46 | loader : 'file-loader' 47 | }, 48 | { 49 | test: /\.(jpe?g|png|gif)(\?.+)?$/, 50 | loader : 'url-loader' 51 | }, 52 | { 53 | test: /\.md$/, 54 | loader : 'raw-loader' 55 | } 56 | ] 57 | }, 58 | mode: 'development' 59 | }), { 60 | publicPath: '/', 61 | hot: true, 62 | historyApiFallback: true, 63 | stats: { colors: true } 64 | }).listen(3000, 'localhost', error => { 65 | if (error) { 66 | throw error; 67 | } 68 | }); 69 | -------------------------------------------------------------------------------- /src/notification/NotificationCenter.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import Notification from './Notification'; 5 | 6 | const className = '.el-notification'; 7 | 8 | export default function NotificationCenter(props = {}, type) { 9 | const div = document.createElement('div'); 10 | 11 | document.body.appendChild(div); 12 | 13 | if (typeof props === 'string' || React.isValidElement(props)) { 14 | props = { 15 | message: props 16 | }; 17 | } 18 | 19 | if (type) { 20 | props.type = type; 21 | } 22 | 23 | if (!props.offset) { 24 | props.offset = 0 25 | } 26 | 27 | const instances = document.querySelectorAll(className) 28 | 29 | const lastInstance = instances[instances.length - 1]; 30 | 31 | props.top = (lastInstance ? (parseInt(lastInstance.style.top) + lastInstance.offsetHeight) : props.offset) + 16; 32 | 33 | const element = React.createElement(Notification, Object.assign({}, props, { 34 | willUnmount(height, top) { 35 | setTimeout(() => document.body.removeChild(div)); 36 | requestAnimationFrame(() => { 37 | const instances = document.querySelectorAll(className); 38 | const len = instances.length; 39 | for (let i = 0; i < len; i++) { 40 | const element = instances[i]; 41 | const elementTop = parseInt(element.style.top); 42 | if (elementTop > top) { 43 | element.style.top = `${elementTop - height - 16}px`; 44 | } 45 | } 46 | }) 47 | } 48 | })); 49 | 50 | ReactDOM.render(element, div); 51 | } 52 | 53 | /* eslint-disable */ 54 | ['success', 'warning', 'info', 'error'].forEach(type => { 55 | NotificationCenter[type] = (options = {}) => NotificationCenter(options, type); 56 | }); 57 | /* eslint-enable */ 58 | -------------------------------------------------------------------------------- /libs/markdown/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import marked from 'marked'; 4 | import prism from 'prismjs'; 5 | 6 | import Canvas from './canvas'; 7 | 8 | export default class Markdown extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | 12 | this.components = new Map; 13 | 14 | this.renderer = new marked.Renderer(); 15 | this.renderer.table = (header, body) => { 16 | return `${header}${body}
    `; 17 | }; 18 | } 19 | 20 | componentDidMount() { 21 | this.renderDOM(); 22 | } 23 | 24 | componentDidUpdate() { 25 | this.renderDOM(); 26 | } 27 | 28 | renderDOM() { 29 | for (const [id, component] of this.components) { 30 | const div = document.getElementById(id); 31 | 32 | if (div instanceof HTMLElement) { 33 | ReactDOM.render(component, div); 34 | } 35 | } 36 | prism.highlightAll(); 37 | } 38 | 39 | render() { 40 | const document = this.document(localStorage.getItem('ELEMENT_LANGUAGE') || 'zh-CN'); 41 | 42 | if (typeof document === 'string') { 43 | this.components.clear(); 44 | 45 | const html = marked(document.replace(/:::\s?demo\s?([^]+?):::/g, (match, p1, offset) => { 46 | const id = offset.toString(36); 47 | 48 | this.components.set(id, React.createElement(Canvas, Object.assign({ 49 | name: this.constructor.name.toLowerCase() 50 | }, this.props), p1)); 51 | 52 | return `
    `; 53 | }), { renderer: this.renderer }); 54 | 55 | return ( 56 |
    59 | ) 60 | } else { 61 | return 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/button/__test__/Button_test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow, mount } from 'enzyme'; 3 | import sinon from 'sinon'; 4 | 5 | import Button from '../'; 6 | 7 | describe('Button test', () => { 8 | it('create', () => { 9 | const w = shallow( 10 | 11 | ); 12 | expect(w.hasClass('el-button--primary')).toBeTruthy(); 13 | expect(w.childAt(0).text()).toBe('TEST'); 14 | }); 15 | it('icon', () => { 16 | const w = shallow( 17 | 18 | ); 19 | expect(w.childAt(0).hasClass('el-icon-search')).toBeTruthy(); 20 | }); 21 | it('nativeType', () => { 22 | const w = shallow( 23 | 24 | ); 25 | expect(w.prop('type')).toBe('submit'); 26 | }); 27 | it('loading', () => { 28 | const w = shallow( 29 | 30 | ); 31 | expect(w.hasClass('is-loading')).toBeTruthy(); 32 | expect(w.childAt(0).hasClass('el-icon-loading')).toBeTruthy(); 33 | }); 34 | it('disabled', () => { 35 | const w = shallow( 36 | 37 | ); 38 | expect(w.hasClass('is-disabled')).toBeTruthy(); 39 | }); 40 | it('size', () => { 41 | const w = shallow( 42 | 43 | ); 44 | expect(w.hasClass('el-button--large')).toBeTruthy(); 45 | }); 46 | it('plain', () => { 47 | const w = shallow( 48 | 49 | ); 50 | expect(w.hasClass('is-plain')).toBeTruthy(); 51 | }); 52 | it('click', () => { 53 | const fn = sinon.spy(); 54 | const w = shallow( 55 | 56 | ); 57 | w.simulate('click'); 58 | expect(fn.callCount).toBe(1); 59 | }); 60 | }); -------------------------------------------------------------------------------- /site/locales/en-US.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | page: { 3 | 'quick-start': 'Quick Start', 4 | 'i18n': 'Internationalization', 5 | 'custom-theme': 'Custom Theme', 6 | 'layout': 'Layout', 7 | 'color': 'Color', 8 | 'typography': 'Typography', 9 | 'icon': 'Icon', 10 | 'button': 'Button', 11 | 'radio': 'Radio', 12 | 'checkbox': 'Checkbox', 13 | 'input': 'Input', 14 | 'input-number': 'Input Number', 15 | 'select': 'Select', 16 | 'cascader': 'Cascader', 17 | 'switch': 'Switch', 18 | 'slider': 'Slider', 19 | 'time-picker': 'Time Picker', 20 | 'date-picker': 'Date Picker', 21 | 'datetime-picker': 'DateTime Picker', 22 | 'upload': 'Upload', 23 | 'rate': 'Rate', 24 | 'color-picker': 'ColorPicker', 25 | 'transfer': 'Transfer', 26 | 'form': 'Form', 27 | 'table': 'Table', 28 | 'tag': 'Tag', 29 | 'progress': 'Progress', 30 | 'tree': 'Tree', 31 | 'pagination': 'Pagination', 32 | 'badge': 'Badge', 33 | 'alert': 'Alert', 34 | 'loading': 'Loading', 35 | 'message': 'Message', 36 | 'message-box': 'Message Box', 37 | 'notification': 'Notification', 38 | 'menu': 'NavMenu', 39 | 'tabs': 'Tabs', 40 | 'breadcrumb': 'Breadcrumb', 41 | 'dropdown': 'Dropdown', 42 | 'steps': 'Steps', 43 | 'dialog': 'Dialog', 44 | 'tooltip': 'Tooltip', 45 | 'popover': 'Popover', 46 | 'card': 'Card', 47 | 'carousel': 'Carousel', 48 | 'collapse': 'Collapse' 49 | }, 50 | misc: { 51 | 'guide': 'Guide', 52 | 'component': 'Component', 53 | 'resource': 'Resource', 54 | 'development': 'Development', 55 | 'components': 'Components', 56 | 'feedback': 'Feedback', 57 | 'contribution': 'Contribution' 58 | }, 59 | markdown: { 60 | 'show': 'Show', 61 | 'hide': 'Hide' 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /typings/typing-tests/Upload.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Upload } from 'element-react' 3 | import { Upload as UploadNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | state = { 7 | action: '//jsonplaceholder.typicode.com/posts/', 8 | fileList: [ 9 | { name: 'food.jpeg', url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg' } 10 | ] 11 | } 12 | beforeUpload = (file) => false 13 | onRemove = (file, fileList) => { } 14 | onPreview = (file) => { } 15 | onProgress = (event, file, fileList) => { } 16 | onSuccess = (response, file, fileList) => { } 17 | onError = (err, response, file) => { } 18 | onChange = (file, fileList) => { } 19 | render() { 20 | const { action, fileList } = this.state 21 | return ( 22 |
    23 | 24 | 25 | 26 | tip
    )} trigger={(
    trigger
    )} /> 27 | 28 | 29 | 30 | 31 | tip
    )} trigger={(
    trigger
    )} /> 32 | 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/radio/RadioButton.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | import Radio from './Radio'; 7 | 8 | export default class RadioButton extends Radio { 9 | static elementType = 'RadioButton'; 10 | 11 | parent(): Component { 12 | return this.context.component; 13 | } 14 | 15 | size(): string { 16 | return this.parent().props.size; 17 | } 18 | 19 | isDisabled(): boolean { 20 | return this.props.disabled || this.parent().props.disabled; 21 | } 22 | 23 | activeStyle(): { backgroundColor: string, borderColor: string, color: string } { 24 | return { 25 | backgroundColor: this.parent().props.fill || '', 26 | borderColor: this.parent().props.fill || '', 27 | color: this.parent().props.textColor || '' 28 | }; 29 | } 30 | 31 | render(): React.DOM { 32 | return ( 33 | 49 | ) 50 | } 51 | } 52 | 53 | RadioButton.contextTypes = { 54 | component: PropTypes.any 55 | }; 56 | 57 | RadioButton.propTypes = { 58 | value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 59 | disabled: PropTypes.bool, 60 | name: PropTypes.string 61 | }; 62 | -------------------------------------------------------------------------------- /src/input/__test__/Input_test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import sinon from 'sinon'; 4 | 5 | import Input from '../'; 6 | 7 | // const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout)); 8 | 9 | describe('Input test', () => { 10 | it('create', () => { 11 | const w = shallow( 12 | 13 | ); 14 | // to be tested: focus/placeholder/minlength/maxlength 15 | expect(w.hasClass('el-input')).toBeTruthy(); 16 | }); 17 | 18 | it('disabled', () => { 19 | const w = shallow( 20 | 21 | ); 22 | expect(w.find('.el-input input').prop('disabled')).toBe(true); 23 | }); 24 | 25 | it('icon', () => { 26 | const cb = sinon.spy(); 27 | const w = shallow( 28 | 33 | ); 34 | expect(w.find('.el-icon-time').exists()).toBeTruthy(); 35 | 36 | w.find('.el-icon-time').simulate('click'); 37 | expect(cb.callCount).toBe(1); 38 | }); 39 | 40 | it('size', () => { 41 | const w = shallow( 42 | 43 | ); 44 | expect(w.hasClass('el-input--large')).toBeTruthy(); 45 | }); 46 | 47 | it('type', () => { 48 | const w = shallow( 49 | 50 | ); 51 | expect(w.hasClass('el-textarea')).toBeTruthy(); 52 | }); 53 | 54 | it('rows', () => { 55 | const w = shallow( 56 | 57 | ); 58 | expect(w.find('.el-textarea__inner').prop('rows')).toBe(3); 59 | }); 60 | 61 | it('resize', () => { 62 | const w = shallow( 63 | 64 | ); 65 | expect(w.find('.el-textarea__inner').first().prop('style').resize).toBe('both'); 66 | }); 67 | 68 | }); 69 | -------------------------------------------------------------------------------- /libs/internal/EventRegister.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { Component } from 'react'; 3 | import { require_condition } from '../utils'; 4 | 5 | let windowKey = Symbol.for("er_register_map") 6 | const registerMap = window[windowKey] = window[windowKey] || { 7 | ids: {}, 8 | } 9 | 10 | const not_null = (t) => (t != null) 11 | 12 | const hasRegistered = ({ id }) => { 13 | return not_null(registerMap.ids[id]) 14 | } 15 | 16 | const cleanRegister = (props) => { 17 | const { target, eventName, func, isUseCapture, id } = props 18 | if (hasRegistered(props)) { 19 | target.removeEventListener(eventName, func, isUseCapture); 20 | delete registerMap.ids[id] 21 | } 22 | } 23 | 24 | const doRegister = (props) => { 25 | let { id, eventName, func, isUseCapture } = props 26 | registerMap.ids[id] = id 27 | document.addEventListener(eventName, func, isUseCapture) 28 | } 29 | 30 | /** 31 | * register events that hooked up react lifecycle 32 | */ 33 | export default class EventRegister extends Component { 34 | 35 | componentDidMount() { 36 | let { eventName, id } = this.props 37 | eventName = eventName.toLowerCase() 38 | eventName = /^on/.test(eventName) ? eventName.substring(2) : eventName 39 | this.cached = Object.assign({}, this.props, { eventName }) 40 | 41 | require_condition(typeof id === 'string', 'id prop is required') 42 | require_condition(!hasRegistered(this.cached), `id: ${id} has been registered`) 43 | 44 | doRegister(this.cached) 45 | } 46 | 47 | componentWillUnmount() { 48 | cleanRegister(this.cached) 49 | } 50 | 51 | render() { 52 | return null 53 | } 54 | } 55 | 56 | 57 | EventRegister.propTypes = { 58 | id: PropTypes.string.isRequired, 59 | target: PropTypes.object.isRequired, 60 | eventName: PropTypes.string.isRequired, 61 | func: PropTypes.func.isRequired, 62 | isUseCapture: PropTypes.bool 63 | }; 64 | -------------------------------------------------------------------------------- /site/docs/en-US/i18n.md: -------------------------------------------------------------------------------- 1 | ## Internationalization 2 | 3 | The default language of Element is Chinese. If you wish to use another language, you'll need to do some i18n configuration. In your entry file, if you are importing Element entirely: 4 | 5 | ```js 6 | import { i18n } from 'element-react' 7 | import locale from 'element-react/src/locale/lang/en' 8 | 9 | i18n.use(locale); 10 | ``` 11 | 12 | The Chinese language pack is imported by default, even if you're using another language. But with `NormalModuleReplacementPlugin` provided by webpack you can replace default locale: 13 | 14 | webpack.config.js 15 | ```js 16 | { 17 | plugins: [ 18 | new webpack.NormalModuleReplacementPlugin(/element-react[\/\\]src[\/\\]locale[\/\\]lang[\/\\]zh-CN/, 'element-react/src/locale/lang/en') 19 | ] 20 | } 21 | ``` 22 | 23 | Currently Element ships with the following languages: 24 |
      25 |
    • Simplified Chinese (zh-CN)
    • 26 |
    • English (en)
    • 27 |
    • German (de)
    • 28 |
    • Portuguese (pt)
    • 29 |
    • Spanish (es)
    • 30 |
    • Danish (da)
    • 31 |
    • French (fr)
    • 32 |
    • Norwegian (nb-NO)
    • 33 |
    • Traditional Chinese (zh-TW)
    • 34 |
    • Italian (it)
    • 35 |
    • Korean (ko)
    • 36 |
    • Japanese (ja)
    • 37 |
    • Dutch (nl)
    • 38 |
    • Vietnamese (vi)
    • 39 |
    • Russian (ru-RU)
    • 40 |
    • Turkish (tr-TR)
    • 41 |
    • Brazilian Portuguese (pt-br)
    • 42 |
    • Farsi (fa)
    • 43 |
    • Thai (th)
    • 44 |
    • Indonesian (id)
    • 45 |
    • Bulgarian (bg)
    • 46 |
    • Polish (pl)
    • 47 |
    • Finnish (fi)
    • 48 |
    • Swedish (sv-SE)
    • 49 |
    • Greek (el)
    • 50 |
    • Slovak (sk)
    • 51 |
    52 | 53 | If your target language is not included, you are more than welcome to contribute: just add another language config [here](https://github.com/ElemeFE/element/tree/master/src/locale/lang) and create a pull request. 54 | -------------------------------------------------------------------------------- /src/message-box/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import MessageBox from './MessageBox'; 5 | 6 | function alert(message, title, props) { 7 | if (typeof title === 'object') { 8 | props = title; 9 | } 10 | 11 | props = Object.assign({ title, message, 12 | modal: 'alert', 13 | closeOnPressEscape: false, 14 | closeOnClickModal: false 15 | }, props); 16 | 17 | return next(props); 18 | } 19 | 20 | function confirm(message, title, props) { 21 | if (typeof title === 'object') { 22 | props = title; 23 | } 24 | 25 | props = Object.assign({ title, message, 26 | modal: 'confirm', 27 | showCancelButton: true 28 | }, props); 29 | 30 | return next(props); 31 | } 32 | 33 | function prompt(message, title, props) { 34 | if (typeof title === 'object') { 35 | props = title; 36 | } 37 | 38 | props = Object.assign({ title, message, 39 | modal: 'prompt', 40 | showCancelButton: true, 41 | showInput: true 42 | }, props); 43 | 44 | return next(props); 45 | } 46 | 47 | function msgbox(props) { 48 | return next(props); 49 | } 50 | 51 | function next(props) { 52 | return new Promise((resolve, reject) => { 53 | const div = document.createElement('div'); 54 | 55 | document.body.appendChild(div); 56 | 57 | if (props.lockScroll != false) { 58 | document.body.style.setProperty('overflow', 'hidden'); 59 | } 60 | 61 | const component = React.createElement(MessageBox, Object.assign({}, props, { 62 | promise: { resolve, reject }, 63 | willUnmount: () => { 64 | ReactDOM.unmountComponentAtNode(div); 65 | document.body.removeChild(div); 66 | document.body.style.removeProperty('overflow'); 67 | } 68 | })); 69 | 70 | ReactDOM.render(component, div); 71 | }); 72 | } 73 | 74 | export default { 75 | alert, 76 | confirm, 77 | prompt, 78 | msgbox 79 | } 80 | -------------------------------------------------------------------------------- /site/locales/zh-CN.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | page: { 3 | 'quick-start': '快速上手', 4 | 'i18n': '国际化', 5 | 'custom-theme': '自定义主题', 6 | 'layout': 'Layout 布局', 7 | 'color': 'Color 色彩', 8 | 'typography': 'Typography 字体', 9 | 'icon': 'Icon 图标', 10 | 'button': 'Button 按钮', 11 | 'radio': 'Radio 单选框', 12 | 'checkbox': 'Checkbox 多选框', 13 | 'input': 'Input 输入框', 14 | 'input-number': 'Input Number 计数器', 15 | 'select': 'Select 选择器', 16 | 'cascader': 'Cascader 级联选择器', 17 | 'switch': 'Switch 开关', 18 | 'slider': 'Slider 滑块', 19 | 'time-picker': 'Time Picker 时间选择器', 20 | 'date-picker': 'Date Picker 日期选择器', 21 | 'datetime-picker': 'DateTimePicker 日期时间选择器', 22 | 'upload': 'Upload 上传', 23 | 'rate': 'Rate 评分', 24 | 'color-picker': 'ColorPicker 颜色选择器', 25 | 'transfer': 'Transfer 穿梭框', 26 | 'form': 'Form 表单', 27 | 'table': 'Table 表格组件', 28 | 'tag': 'Tag 标签', 29 | 'progress': 'Progress 进度条', 30 | 'tree': 'Tree 树形控件', 31 | 'pagination': 'Pagination 分页', 32 | 'badge': 'Badge 标记', 33 | 'alert': 'Alert 警告', 34 | 'loading': 'Loading 加载', 35 | 'message': 'Message 消息提示', 36 | 'message-box': 'Message Box 弹框', 37 | 'notification': 'Notification 通知', 38 | 'menu': 'NavMenu 导航菜单', 39 | 'tabs': 'Tabs 标签页', 40 | 'breadcrumb': 'Breadcrumb 面包屑', 41 | 'dropdown': 'Dropdown 下拉菜单', 42 | 'steps': 'Steps 步骤', 43 | 'dialog': 'Dialog 对话框', 44 | 'tooltip': 'Tooltip 文字提示', 45 | 'popover': 'Popover 弹出框', 46 | 'card': 'Card 卡片', 47 | 'carousel': 'Carousel 走马灯', 48 | 'collapse': 'Collapse 折叠面板' 49 | }, 50 | misc: { 51 | 'guide': '指南', 52 | 'component': '组件', 53 | 'resource': '资源', 54 | 'development': '开发指南', 55 | 'components': '基础组件', 56 | 'feedback': '反馈建议', 57 | 'contribution': '贡献指南' 58 | }, 59 | markdown: { 60 | 'show': '显示代码', 61 | 'hide': '隐藏代码' 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/collapse/Collapse.jsx: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import React from 'react'; 4 | import { Component, PropTypes } from '../../libs'; 5 | 6 | type State = { 7 | activeNames: Array 8 | }; 9 | 10 | export default class Collapse extends Component { 11 | state: State; 12 | 13 | static defaultProps = { 14 | value: [], 15 | onChange() {} 16 | }; 17 | 18 | constructor(props: Object) { 19 | super(props); 20 | this.state = { 21 | activeNames: [].concat(this.props.value) 22 | }; 23 | } 24 | 25 | componentWillReceiveProps (nextProps: Object) { 26 | this.setActiveNames(nextProps.value) 27 | } 28 | 29 | setActiveNames(activeNames: string | Array): void { 30 | activeNames = [].concat(activeNames); 31 | this.setState({ activeNames }, () => this.props.onChange(activeNames)); 32 | } 33 | 34 | handleItemClick(name: string): void { 35 | const { activeNames } = this.state; 36 | 37 | if (this.props.accordion) { 38 | this.setActiveNames( 39 | activeNames[0] && activeNames[0] === name ? '' : name 40 | ); 41 | } else { 42 | if (activeNames.includes(name)) { 43 | this.setActiveNames(activeNames.filter(item => item !== name)); 44 | } else { 45 | this.setActiveNames(activeNames.concat(name)); 46 | } 47 | } 48 | } 49 | 50 | render(): React.DOM { 51 | const content = React.Children.map(this.props.children, (child, idx) => { 52 | const name = child.props.name || idx.toString(); 53 | return React.cloneElement(child, { 54 | isActive: this.state.activeNames.includes(name), 55 | key: idx, 56 | name: name, 57 | onClick: item => this.handleItemClick(item) 58 | }); 59 | }); 60 | return ( 61 |
    62 | {content} 63 |
    64 | ); 65 | } 66 | } 67 | 68 | Collapse.propTypes = { 69 | accordion: PropTypes.bool, 70 | value: PropTypes.oneOfType([PropTypes.array, PropTypes.string]), 71 | onChange: PropTypes.func 72 | }; 73 | -------------------------------------------------------------------------------- /typings/typing-tests/Select.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Select } from 'element-react' 3 | import { Select as SelectNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | onChange = (value) => { } 7 | remoteMethod = () => { } 8 | filterMethod = () => { } 9 | render() { 10 | return ( 11 |
    12 | 17 | 22 | 23 | 24 | 25 | haha 26 | 27 | 28 | 29 | 30 | haha 31 | 32 | 33 |
    34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /typings/typing-tests/Table.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Table } from 'element-react' 3 | import { Table as TableNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | state = { 7 | columns: [ 8 | { 9 | label: "日期", 10 | prop: "date", 11 | width: 180 12 | }, 13 | { 14 | label: "姓名", 15 | prop: "name", 16 | width: 180 17 | }, 18 | { 19 | label: "地址", 20 | prop: "address" 21 | } 22 | ], 23 | data: [{ 24 | date: '2016-05-02', 25 | name: '王小虎', 26 | address: '上海市普陀区金沙江路 1518 弄' 27 | }, { 28 | date: '2016-05-04', 29 | name: '王小虎', 30 | address: '上海市普陀区金沙江路 1517 弄' 31 | }, { 32 | date: '2016-05-01', 33 | name: '王小虎', 34 | address: '上海市普陀区金沙江路 1519 弄' 35 | }, { 36 | date: '2016-05-03', 37 | name: '王小虎', 38 | address: '上海市普陀区金沙江路 1516 弄' 39 | }, { 40 | date: '2016-05-02', 41 | name: '王小虎', 42 | address: '上海市普陀区金沙江路 1518 弄' 43 | }, { 44 | date: '2016-05-04', 45 | name: '王小虎', 46 | address: '上海市普陀区金沙江路 1517 弄' 47 | }, { 48 | date: '2016-05-01', 49 | name: '王小虎', 50 | address: '上海市普陀区金沙江路 1519 弄' 51 | }, { 52 | date: '2016-05-03', 53 | name: '王小虎', 54 | address: '上海市普陀区金沙江路 1516 弄' 55 | }] 56 | } 57 | render() { 58 | const { columns, data } = this.state 59 | return ( 60 |
    61 | 62 |
    68 | 69 | 70 | 76 | 77 | ) 78 | } 79 | } 80 | 81 | // TODO: 完善 Table 的测试 82 | -------------------------------------------------------------------------------- /libs/utils/dom.js: -------------------------------------------------------------------------------- 1 | export const loadStyleString = (css, id = '') => { 2 | if (document.getElementById(id)) return; 3 | let style = document.createElement('style'); 4 | style.type = 'text/css'; 5 | style.id = id; 6 | try { 7 | style.appendChild(document.createTextNode(css)); 8 | } catch (ex) { 9 | style.styleSheet.cssText = css; 10 | } 11 | const head = document.getElementsByTagName('head')[0]; 12 | head.appendChild(style); 13 | } 14 | 15 | 16 | let isServer = false; 17 | /* istanbul ignore next */ 18 | export const on = (function() { 19 | if (!isServer && document.addEventListener) { 20 | return function(element, event, handler) { 21 | if (element && event && handler) { 22 | element.addEventListener(event, handler, false); 23 | } 24 | }; 25 | } else { 26 | return function(element, event, handler) { 27 | if (element && event && handler) { 28 | element.attachEvent('on' + event, handler); 29 | } 30 | }; 31 | } 32 | })(); 33 | 34 | /* istanbul ignore next */ 35 | export const off = (function() { 36 | if (!isServer && document.removeEventListener) { 37 | return function(element, event, handler) { 38 | if (element && event) { 39 | element.removeEventListener(event, handler, false); 40 | } 41 | }; 42 | } else { 43 | return function(element, event, handler) { 44 | if (element && event) { 45 | element.detachEvent('on' + event, handler); 46 | } 47 | }; 48 | } 49 | })(); 50 | 51 | export function scrollIntoView(container, selected) { 52 | if (isServer) return; 53 | 54 | if (!selected) { 55 | container.scrollTop = 0; 56 | return; 57 | } 58 | 59 | const top = selected.offsetTop; 60 | const bottom = selected.offsetTop + selected.offsetHeight; 61 | const viewRectTop = container.scrollTop; 62 | const viewRectBottom = viewRectTop + container.clientHeight; 63 | 64 | if (top < viewRectTop) { 65 | container.scrollTop = top; 66 | } else if (bottom > viewRectBottom) { 67 | container.scrollTop = bottom - container.clientHeight; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /site/docs/zh-CN/custom-theme.md: -------------------------------------------------------------------------------- 1 | ## 自定义主题 2 | Element 默认提供一套主题,CSS 命名采用 BEM 的风格方便使用者覆盖样式。如果你想完全替换主题色或者部分样式,可以使用下面方法。 3 | 4 | ### 安装工具 5 | 首先安装「主题生成工具」,可以全局安装或者安装在当前项目下,推荐安装在项目里,方便别人 clone 项目时能直接安装依赖并启动,这里以全局安装做演示。 6 | ```shell 7 | npm i element-theme -g 8 | ``` 9 | 10 | 安装默认主题,可以从 npm 安装或者从 GitHub 拉取最新代码。 11 | ```shell 12 | # 从 npm 13 | npm i element-theme-default -D 14 | 15 | # 从 GitHub 16 | npm i https://github.com/ElementUI/theme-default -D 17 | ``` 18 | 19 | ### 初始化变量文件 20 | 主题生成工具安装成功后,如果全局安装可以在命令行里通过 `et` 调用工具,如果安装在当前目录下,需要通过 `node_modules/.bin/et` 访问到命令。执行 `-i` 初始化变量文件。默认输出到 `element-variables.css`,当然你可以传参数指定文件输出目录。 21 | 22 | ```shell 23 | et -i [可以自定义变量文件] 24 | 25 | > ✔ Generator variables file 26 | ``` 27 | 28 | 如果使用默认配置,执行后当前目录会有一个 `element-variables.css` 文件。内部包含了主题所用到的所有变量,它们使用 CSS4 的风格定义。大致结构如下: 29 | ```css 30 | :root { 31 | 32 | /* Colors 33 | -------------------------- */ 34 | --color-primary: #20a0ff; 35 | --color-success: #13ce66; 36 | --color-warning: #f7ba2a; 37 | --color-danger: #ff4949; 38 | --color-info: #50BFFF; 39 | --color-blue: #2e90fe; 40 | --color-blue-light: #5da9ff; 41 | --color-blue-lighter: rgba(var(--color-blue), 0.12); 42 | --color-white: #fff; 43 | --color-black: #000; 44 | --color-grey: #C0CCDA; 45 | ``` 46 | 47 | ### 修改变量 48 | 直接编辑 `element-variables.css` 文件,例如修改主题色为红色。 49 | ```CSS 50 | --color-primary: red; 51 | ``` 52 | 53 | ### 编译主题 54 | 保存文件后,到命令行里执行 `et` 编译主题,如果你想启用 `watch` 模式,实时编译主题,增加 `-w` 参数;如果你在初始化时指定了自定义变量文件,则需要增加 `-c` 参数,并带上你的变量文件名 55 | ```shell 56 | et 57 | 58 | > ✔ build theme font 59 | > ✔ build element theme 60 | ``` 61 | 62 | ### 引入自定义主题 63 | 默认情况下编译的主题目录是放在 `./theme` 下,你可以通过 `-o` 参数指定打包目录。像引入默认主题一样,在代码里直接引用 `theme/index.css` 文件即可。 64 | 65 | ```javascript 66 | import '../theme/index.css' 67 | import 'element-ui' 68 | ``` 69 | 70 | ### 搭配插件按需引入组件主题 71 | 如果是搭配 `babel-plugin-component` 一起使用,只需要修改 `.babelrc` 的配置,指定 `styleLibraryName` 路径为自定义主题相对于 `.babelrc` 的路径,注意要加 `~`。 72 | ```json 73 | { 74 | "plugins": [["component", [ 75 | { 76 | "libraryName": "element-ui", 77 | "styleLibraryName": "~theme" 78 | } 79 | ]]] 80 | } 81 | ``` 82 | -------------------------------------------------------------------------------- /site/docs/zh-CN/typography.md: -------------------------------------------------------------------------------- 1 | ## Typography 字体 2 | 3 | 我们对字体进行统一规范,力求在各个操作系统下都有最佳展示效果。 4 | 5 | ### 中文字体 6 | 7 |
    8 | 和畅惠风 9 |
    PingFang SC
    10 |
    11 |
    12 | 和畅惠风 13 |
    Hiragino Sans GB
    14 |
    15 |
    16 | 和畅惠风 17 |
    Microsoft YaHei
    18 |
    19 | 20 | ### 英文/数字字体 21 | 22 |
    23 | RGag 24 |
    Helvetica Neue
    25 |
    26 |
    27 | RGag 28 |
    Helvetica
    29 |
    30 |
    31 | RGag 32 |
    Arial
    33 |
    34 | 35 | ### Font-family 代码 36 | 37 | ```css 38 | font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif; 39 | ``` 40 | 41 | ### 字体使用规范 42 | 43 |
    44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 |
    主标题用 Element 快速搭建页面20px Extra large
    标题用 Element 快速搭建页面18px large
    小标题用 Element 快速搭建页面16px Medium
    正文用 Element 快速搭建页面14px Small
    正文(小)用 Element 快速搭建页面13px Extra Small
    辅助文字用 Element 快速搭建页面12px Extra Extra Small
    77 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { default as i18n } from './locale'; 2 | 3 | export { default as Alert } from './alert'; 4 | export { default as Button } from './button'; 5 | export { default as Card } from './card'; 6 | export { default as Layout } from './layout'; 7 | export { default as Loading } from './loading'; 8 | export { default as Message } from './message'; 9 | export { default as MessageBox } from './message-box'; 10 | export { default as Notification } from './notification'; 11 | export { default as Radio } from './radio'; 12 | export { default as Dialog } from './dialog'; 13 | export { default as Rate } from './rate'; 14 | export { default as Progress } from './progress'; 15 | export { default as Badge } from './badge'; 16 | export { default as Tabs } from './tabs'; 17 | export { default as Tree } from './tree'; 18 | export { default as Input } from './input'; 19 | export { default as Icon } from './icon'; 20 | export { default as Menu } from './menu'; 21 | export { default as Steps } from './steps'; 22 | export { default as Breadcrumb } from './breadcrumb'; 23 | export { default as Tooltip } from './tooltip'; 24 | export { default as InputNumber } from './input-number'; 25 | export { default as Checkbox } from './checkbox'; 26 | export { default as Slider } from './slider'; 27 | export { default as Table } from './table' 28 | export { default as Switch } from './switch'; 29 | export { default as Form } from './form'; 30 | export { default as Upload } from './upload'; 31 | export { default as Tag } from './tag'; 32 | export { default as Select } from './select'; 33 | export { default as Dropdown } from './dropdown'; 34 | export { default as Popover } from './popover'; 35 | export { default as Pagination } from './pagination'; 36 | export { default as AutoComplete } from './auto-complete'; 37 | export { TimeSelect, TimePicker, TimeRangePicker, DatePicker, DateRangePicker } from './date-picker'; 38 | export { default as Carousel } from './carousel'; 39 | export { default as Collapse } from './collapse'; 40 | export { default as ColorPicker } from './color-picker'; 41 | export { default as Cascader } from './cascader'; 42 | export { default as Transfer } from './transfer'; 43 | -------------------------------------------------------------------------------- /typings/typing-tests/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Checkbox } from 'element-react' 3 | import { Checkbox as CheckboxNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | state = { 7 | value: [] 8 | } 9 | onChange = (value) => { } 10 | render() { 11 | return ( 12 |
    13 | 14 | 15 | Button 16 | 17 | 18 | 19 | Button 20 | 21 | 22 | 23 | Button 24 | 25 | 26 | 27 | 28 | Button 29 | 30 | 31 | 32 | Button 33 | 34 | 35 | 36 | Button 37 | 38 |
    39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/upload/ajax.js: -------------------------------------------------------------------------------- 1 | function getError(action, option, xhr) { 2 | let msg; 3 | if (xhr.response) { 4 | msg = `${xhr.status} ${xhr.response.error || xhr.response}`; 5 | } else if (xhr.responseText) { 6 | msg = `${xhr.status} ${xhr.responseText}`; 7 | } else { 8 | msg = `fail to post ${action} ${xhr.status}`; 9 | } 10 | 11 | const err = new Error(msg); 12 | err.status = xhr.status; 13 | err.method = 'post'; 14 | err.url = action; 15 | return err; 16 | } 17 | 18 | function getBody(xhr) { 19 | const text = xhr.responseText || xhr.response; 20 | if (!text) { 21 | return text; 22 | } 23 | 24 | try { 25 | return JSON.parse(text); 26 | } catch (e) { 27 | return text; 28 | } 29 | } 30 | 31 | export default function upload(option) { 32 | if (typeof XMLHttpRequest === 'undefined') { 33 | return; 34 | } 35 | 36 | const xhr = new XMLHttpRequest(); 37 | const action = option.action; 38 | 39 | if (xhr.upload) { 40 | xhr.upload.onprogress = function progress(e) { 41 | if (e.total > 0) { 42 | e.percent = e.loaded / e.total * 100; 43 | } 44 | option.onProgress(e); 45 | }; 46 | } 47 | 48 | const formData = new FormData(); 49 | 50 | if (option.data) { 51 | Object.keys(option.data).map(key => { 52 | formData.append(key, option.data[key]); 53 | }); 54 | } 55 | 56 | formData.append(option.filename, option.file); 57 | 58 | xhr.onerror = function error(e) { 59 | option.onError(e); 60 | }; 61 | 62 | xhr.onload = function onload() { 63 | if (xhr.status < 200 || xhr.status >= 300) { 64 | return option.onError(getError(action, option, xhr)); 65 | } 66 | 67 | option.onSuccess(getBody(xhr)); 68 | }; 69 | 70 | xhr.open('post', action, true); 71 | 72 | if (option.withCredentials && 'withCredentials' in xhr) { 73 | xhr.withCredentials = true; 74 | } 75 | 76 | const headers = option.headers || {}; 77 | 78 | for (let item in headers) { 79 | if (headers.hasOwnProperty(item) && headers[item] !== null) { 80 | xhr.setRequestHeader(item, headers[item]); 81 | } 82 | } 83 | xhr.send(formData); 84 | return xhr; 85 | } 86 | -------------------------------------------------------------------------------- /site/docs/zh-CN/progress.md: -------------------------------------------------------------------------------- 1 | ## Progress 进度条 2 | 3 | 用于展示操作进度,告知用户当前状态和预期。 4 | 5 | ### 线形进度条 — 百分比外显 6 | 7 | :::demo Progress 组件设置`percentage`属性即可,表示进度条对应的百分比,**必填**,必须在 0-100。 8 | 9 | ```js 10 | render() { 11 | return ( 12 |
    13 | 14 | 15 | 16 | 17 |
    18 | ) 19 | } 20 | ``` 21 | ::: 22 | 23 | ### 线形进度条 — 百分比内显 24 | 25 | 百分比不占用额外控件,适用于文件上传等场景。 26 | 27 | :::demo Progress 组件可通过 `strokeWidth` 属性更改进度条的高度,并可通过 `textInside` 属性来将进度条描述置于进度条内部。 28 | 29 | ```js 30 | render() { 31 | return ( 32 |
    33 | 34 | 35 | 36 | 37 |
    38 | ) 39 | } 40 | ``` 41 | ::: 42 | 43 | ### 环形进度条 44 | 45 | :::demo Progress 组件可通过 `type` 属性来指定使用环形进度条,在环形进度条中,还可以通过 `width` 属性来设置其大小。 46 | 47 | ```js 48 | render() { 49 | return ( 50 |
    51 | 52 | 53 | 54 | 55 |
    56 | ) 57 | } 58 | ``` 59 | ::: 60 | 61 | ### Attributes 62 | | 参数 | 说明 | 类型 | 可选值 | 默认值 | 63 | |------------- |---------------- |---------------- |---------------------- |-------- | 64 | | **percentage** | **百分比(必填)** | number | 0-100 | 0 | 65 | | type | 进度条类型 | string | line/circle | line | 66 | | strokeWidth | 进度条的宽度,单位 px | number | — | 6 | 67 | | textInside | 进度条显示文字内置在进度条内(只在 type=line 时可用) | Boolean | — | false | 68 | | status | 进度条当前状态 | string | success/exception | — | 69 | | width | 环形进度条画布宽度(只在 type=circle 时可用) | number | | 126 | 70 | | showText | 是否显示进度条文字内容 | boolean | — | true | 71 | -------------------------------------------------------------------------------- /typings/typing-tests/Dialog.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Dialog } from 'element-react' 3 | import { Dialog as DialogNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | onCancel = () => { } 7 | render() { 8 | return ( 9 |
    10 | 11 | 12 | 13 | 14 | 15 | 16 |
    body
    17 |
    18 | 19 |
    footer
    20 |
    21 |
    22 | 23 |
    body
    24 |
    25 | 26 |
    footer
    27 |
    28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
    body
    36 |
    37 | 38 |
    footer
    39 |
    40 |
    41 | 42 |
    body
    43 |
    44 | 45 |
    footer
    46 |
    47 |
    48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /typings/typing-tests/Transfer.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Transfer } from 'element-react' 3 | import { Transfer as TransferNext } from 'element-react/next' 4 | 5 | class Component extends React.Component<{}, {}> { 6 | state = { 7 | data: [{ 8 | value: 1, 9 | desc: `备选项 ${1}`, 10 | disabled: 1 % 4 === 0 11 | }] 12 | } 13 | onChange = (value, direction, movedKeys) => { } 14 | render() { 15 | const { data } = this.state 16 | return ( 17 |
    18 | 19 | { }} 25 | leftDefaultChecked={[]} 26 | rightDefaultChecked={[]} 27 | renderContent={(h, option) => { }} 28 | value={data} 29 | footerFormat={{ noChecked: '共 ${total} 项', hasChecked: '已选 ${checked}/${total} 项' }} 30 | filterable={true} 31 | propsAlias={{ 32 | key: 'value', 33 | label: 'desc' 34 | }} 35 | onChange={this.onChange} 36 | leftFooter={(
    footer
    )} 37 | rightFooter={(
    footer
    )} 38 | /> 39 | 40 | 41 | { }} 47 | leftDefaultChecked={[]} 48 | rightDefaultChecked={[]} 49 | renderContent={(h, option) => { }} 50 | value={data} 51 | footerFormat={{ noChecked: '共 ${total} 项', hasChecked: '已选 ${checked}/${total} 项' }} 52 | filterable={true} 53 | propsAlias={{ 54 | key: 'value', 55 | label: 'desc' 56 | }} 57 | onChange={this.onChange} 58 | leftFooter={(
    footer
    )} 59 | rightFooter={(
    footer
    )} 60 | /> 61 |
    62 | ) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/rate/__test__/rate_test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { mount } from 'enzyme'; 3 | 4 | import { Rate } from '../../../src'; 5 | 6 | test('Basic usage', () => { 7 | const state1 = { 8 | value: 0 9 | }; 10 | 11 | let testCallBack = (value)=> { 12 | state1.value = value; 13 | }; 14 | const component1 = mount( 15 | 16 | ); 17 | 18 | // 模拟component1的第二个星 mouseenter事件 19 | const rate1 = component1.find('.el-rate .el-rate__item').at(1); 20 | rate1.simulate('mouseenter'); 21 | setTimeout(() => { 22 | expect(rate1.find('i').at(0).hasClass('hover')).toEqual(true); 23 | }, 1000) 24 | 25 | // 模拟component1的第三个星 click事件 26 | const rate2 = component1.find('.el-rate .el-rate__item').at(2); 27 | rate2.simulate('click'); 28 | setTimeout(() => { 29 | expect(rate2.find('i').at(0).prop('style').color).toBe('#F7BA2A'); 30 | expect(state1.value).toEqual(3); 31 | }, 1000) 32 | 33 | 34 | const component2 = mount( 35 | 36 | ); 37 | 38 | // 模拟component2的第四个星 click事件 39 | const rate3 = component2.find('.el-rate .el-rate__item').at(3); 40 | rate3.simulate('click'); 41 | setTimeout(() => { 42 | expect(rate3.find('i').at(0).prop('style').color).toEqual('#FF9900'); 43 | }, 1000) 44 | }); 45 | 46 | test('With text', () => { 47 | const component = mount( 48 | 52 | ); 53 | 54 | // 模拟component2的第四个星 click事件 55 | const rate = component.find('.el-rate .el-rate__item').at(3); 56 | rate.simulate('click'); 57 | expect(component.find('.el-rate__text').at(0).text()).toEqual('good'); 58 | }); 59 | 60 | test('Read-only', () => { 61 | const component = mount( 62 | 63 | ); 64 | 65 | expect(component.find('.el-rate__text').at(0).text()).toEqual('3.9'); 66 | 67 | // 模拟component的第五个星 click事件 68 | const rate = component.find('.el-rate .el-rate__item').at(4); 69 | rate.simulate('click'); 70 | expect(rate.find('i').at(0).prop('style').color).toEqual('#EFF2F7'); // 点击后颜色不变 71 | }); 72 | -------------------------------------------------------------------------------- /site/pages/index.jsx: -------------------------------------------------------------------------------- 1 | export default { 2 | documents: { 3 | 'quick-start': require('./quick-start'), 4 | 'i18n': require('./i18n'), 5 | // 'custom-theme': require('./custom-theme') 6 | }, 7 | components: { 8 | 'Basic': { 9 | 'layout': require('./layout'), 10 | 'color': require('./color'), 11 | 'typography': require('./typography'), 12 | 'icon': require('./icon'), 13 | 'button': require('./button') 14 | }, 15 | 'Form': { 16 | 'radio': require('./radio'), 17 | 'checkbox': require('./checkbox'), 18 | 'input': require('./input'), 19 | 'input-number': require('./input-number'), 20 | 'select': require('./select'), 21 | 'cascader': require('./cascader'), 22 | 'switch': require('./switch'), 23 | 'slider': require('./slider'), 24 | 'time-picker': require('./time-picker'), 25 | 'date-picker': require('./date-picker'), 26 | 'datetime-picker': require('./datetime-picker'), 27 | 'upload': require('./upload'), 28 | 'rate': require('./rate'), 29 | 'color-picker': require('./color-picker'), 30 | 'transfer': require('./transfer'), 31 | 'form': require('./form') 32 | }, 33 | 'Data': { 34 | 'table': require('./table'), 35 | 'tag': require('./tag'), 36 | 'progress': require('./progress'), 37 | 'tree': require('./tree'), 38 | 'pagination': require('./pagination'), 39 | 'badge': require('./badge') 40 | }, 41 | 'Notice': { 42 | 'alert': require('./alert'), 43 | 'loading': require('./loading'), 44 | 'message': require('./message'), 45 | 'message-box': require('./message-box'), 46 | 'notification': require('./notification') 47 | }, 48 | 'Nav': { 49 | 'menu': require('./menu'), 50 | 'tabs': require('./tabs'), 51 | 'breadcrumb': require('./breadcrumb'), 52 | 'dropdown': require('./dropdown'), 53 | 'steps': require('./steps') 54 | }, 55 | 'Others': { 56 | 'dialog': require('./dialog'), 57 | 'tooltip': require('./tooltip'), 58 | 'popover': require('./popover'), 59 | 'card': require('./card'), 60 | 'carousel': require('./carousel'), 61 | 'collapse': require('./collapse') 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /site/docs/en-US/typography.md: -------------------------------------------------------------------------------- 1 | ## Typography 2 | 3 | We create a font convention to ensure the best presentation across different platforms. 4 | 5 | ### Chinese Font 6 | 7 |
    8 | 和畅惠风 9 |
    PingFang SC
    10 |
    11 |
    12 | 和畅惠风 13 |
    Hiragino Sans GB
    14 |
    15 |
    16 | 和畅惠风 17 |
    Microsoft YaHei
    18 |
    19 | 20 | ### English / Numberic Font 21 | 22 |
    23 | RGag 24 |
    Helvetica Neue
    25 |
    26 |
    27 | RGag 28 |
    Helvetica
    29 |
    30 |
    31 | RGag 32 |
    Arial
    33 |
    34 | 35 | ### Font-family 36 | 37 | ```css 38 | font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif; 39 | ``` 40 | 41 | ### Font Convention 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 |
    Main TitleBuild with Element20px Extra large
    TitleBuild with Element18px large
    Small TitleBuild with Element16px Medium
    BodyBuild with Element14px Small
    Body (small) Build with Element13px Extra Small
    Supplementary textBuild with Element12px Extra Extra Small
    77 | -------------------------------------------------------------------------------- /src/date-picker/TimePicker.jsx: -------------------------------------------------------------------------------- 1 | //@flow 2 | import React from 'react'; 3 | import { debounce } from 'throttle-debounce'; 4 | 5 | import { PropTypes } from '../../libs'; 6 | 7 | import BasePicker from './BasePicker' 8 | import TimePanel from './panel/TimePanel' 9 | 10 | import { TYPE_VALUE_RESOLVER_MAP, DEFAULT_FORMATS } from './constants' 11 | import type { TimePickerProps } from './Types'; 12 | 13 | function converSelectRange(props) { 14 | let selectableRange = [] 15 | if (props.selectableRange) { 16 | let ranges = props.selectableRange; 17 | const parser = TYPE_VALUE_RESOLVER_MAP.datetimerange.parser; 18 | const format = DEFAULT_FORMATS.timerange; 19 | 20 | ranges = Array.isArray(ranges) ? ranges : [ranges]; 21 | selectableRange = ranges.map(range => parser(range, format)); 22 | } 23 | return selectableRange 24 | } 25 | 26 | export default class TimePicker extends BasePicker { 27 | // why this is used, goto: http://exploringjs.com/es6/ch_classes.html 28 | static get propTypes() { 29 | let result: any = Object.assign({}, { 30 | // '18:30:00 - 20:30:00' 31 | // or ['09:30:00 - 12:00:00', '14:30:00 - 18:30:00'] 32 | selectableRange: PropTypes.oneOfType([ 33 | PropTypes.string, 34 | PropTypes.arrayOf(PropTypes.string) 35 | ]) 36 | }, BasePicker.propTypes) 37 | 38 | return result; 39 | } 40 | 41 | static get defaultProps() { 42 | let result: any = Object.assign({}, BasePicker.defaultProps) 43 | return result; 44 | } 45 | 46 | constructor(props: TimePickerProps) { 47 | super(props, 'time', {}) 48 | this._onSelectionChange = debounce(200, this.onSelectionChange.bind(this)) 49 | } 50 | 51 | onSelectionChange(start: number, end: number) { 52 | this.refs.inputRoot.refs.input.setSelectionRange(start, end); 53 | this.refs.inputRoot.refs.input.focus(); 54 | } 55 | 56 | pickerPanel(state: any, props: TimePickerProps) { 57 | return ( 58 | this.setState({pickerVisible: false})} 62 | onPicked={this.onPicked.bind(this)} 63 | onSelectRangeChange={this._onSelectionChange} 64 | selectableRange={converSelectRange(props)} 65 | /> 66 | ) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/alert/__test__/Alert_test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow, mount } from 'enzyme'; 3 | import sinon from 'sinon'; 4 | 5 | import Alert from '../'; 6 | 7 | describe('Alert test', () => { 8 | it('type', () => { 9 | const w = mount( 10 | 11 | ); 12 | expect(w.find('div.el-alert--success')).toBeTruthy(); 13 | expect(w.find('span.el-alert__title').exists()).toBeTruthy(); 14 | expect(w.find('span.el-alert__title').text()).toBe('TEST'); 15 | }); 16 | 17 | it('default closable', () => { 18 | const w = mount( 19 | 20 | ); 21 | expect(w.find('i.el-alert__closebtn').prop('style')).toEqual({}); 22 | }) 23 | 24 | it('disable close', () => { 25 | const w = mount( 26 | 27 | ); 28 | expect(w.find('i.el-alert__closebtn').prop('style').display).toBe('none'); 29 | }); 30 | 31 | it('closeText', () => { 32 | const w = mount( 33 | 34 | ); 35 | expect(w.find('i.is-customed').text()).toBe('testCloseText'); 36 | }); 37 | 38 | it('onClose', () => { 39 | const onClose = sinon.spy(); 40 | const w = mount( 41 | 42 | ); 43 | w.find('i.el-alert__closebtn').simulate('click'); 44 | setTimeout(() => { 45 | expect(onClose.calledOnce).toBe(true); 46 | }, 1000) 47 | }); 48 | 49 | it('showIcon', () => { 50 | const w = mount( 51 | 52 | ); 53 | expect(w.find('i.el-alert__icon').exists()).toBeTruthy(); 54 | }); 55 | 56 | it('description', () => { 57 | const w = mount( 58 | 61 | ); 62 | expect(w.find('p.el-alert__description').text()).toBe('testDescription'); 63 | }) 64 | 65 | it('showIcon and description', () => { 66 | const w = mount( 67 | 71 | ); 72 | expect(w.find('i.el-alert__icon').exists()).toBeTruthy(); 73 | expect(w.find('p.el-alert__description').text()).toBe('testDescription'); 74 | }); 75 | }); 76 | --------------------------------------------------------------------------------