├── .babelrc ├── .coveralls.yml ├── .editorconfig ├── .fecsignore ├── .fecsrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── bower.json ├── esdoc.json ├── example ├── App.js ├── BoxGroup.js ├── BoxGroup.styl ├── Breadcrumb.js ├── Breadcrumb.styl ├── Button.js ├── Button.styl ├── Chip.js ├── Chip.styl ├── Dialog.js ├── Dialog.styl ├── Drawer.js ├── Drawer.styl ├── Grid.js ├── Grid.styl ├── Icon.js ├── Icon.styl ├── Pager.js ├── Pager.styl ├── Progress.js ├── Progress.styl ├── Region.js ├── Region.styl ├── ScrollView.js ├── ScrollView.styl ├── Select.js ├── Select.styl ├── Slider.js ├── SnackBar.js ├── SnackBar.styl ├── Table.js ├── Table.styl ├── Tabs.js ├── Tabs.styl ├── TextBox.js ├── TextBox.styl ├── Tooltip.js ├── Tooltip.styl ├── Tree.js ├── Tree.styl ├── Uploader.js ├── Uploader.styl ├── Zippy.js ├── Zippy.styl ├── common │ ├── Nav.js │ ├── NavItem.js │ ├── component │ │ ├── Markdown.js │ │ └── View.js │ └── tpl │ │ └── base.swig ├── config.js ├── index.html ├── index.js ├── index.styl ├── metaform.html ├── metaform.js └── metaform.styl ├── gulpfile.js ├── package-lock.json ├── package.json ├── readme.md ├── src ├── Alert.js ├── BoxGroup.js ├── Breadcrumb.js ├── Button.js ├── Card.js ├── Chip.js ├── Confirm.js ├── Dialog.js ├── Divider.js ├── Drawer.js ├── DropDownMenu.js ├── Icon.js ├── IconMenu.js ├── Layer.js ├── Link.js ├── Mask.js ├── Menu.js ├── Navigation.js ├── Pager.js ├── Popover.js ├── Progress.js ├── Region.js ├── ScrollView.js ├── Select.js ├── SelectableTable.js ├── Slider.js ├── SnackBar.js ├── Table.js ├── Tabs.js ├── TextBox.js ├── Title.js ├── Toggle.js ├── ToolBar.js ├── Tooltip.js ├── Tree.js ├── Uploader.js ├── Zippy.js ├── boxgroup │ └── Option.js ├── breadcrumb │ └── Item.js ├── common │ └── util │ │ ├── dom.js │ │ ├── fn.js │ │ └── guid.js ├── css │ ├── BoxGroup.styl │ ├── Breadcrumb.styl │ ├── Button.styl │ ├── ButtonGroup.styl │ ├── Card.styl │ ├── Chip.styl │ ├── Dialog.styl │ ├── Divider.styl │ ├── Drawer.styl │ ├── Icon.styl │ ├── IconMenu.styl │ ├── Layer.styl │ ├── Link.styl │ ├── Mask.styl │ ├── Menu.styl │ ├── Navigation.styl │ ├── Pager.styl │ ├── Popover.styl │ ├── Progress.styl │ ├── Region.styl │ ├── Ripple.styl │ ├── ScrollView.styl │ ├── Select.styl │ ├── Slider.styl │ ├── SnackBar.styl │ ├── Table.styl │ ├── Tabs.styl │ ├── TextBox.styl │ ├── Title.styl │ ├── Toggle.styl │ ├── ToolBar.styl │ ├── Tooltip.styl │ ├── Tree.styl │ ├── Uploader.styl │ ├── Validity.styl │ ├── Zippy.styl │ ├── base.styl │ ├── colors.styl │ ├── grid.styl │ ├── mixin.styl │ ├── theme │ │ └── default │ │ │ ├── BoxGroup.variable.styl │ │ │ ├── Breadcrumb.variable.styl │ │ │ ├── Button.variable.styl │ │ │ ├── Chip.variable.styl │ │ │ ├── Link.variable.styl │ │ │ ├── Pager.variable.styl │ │ │ ├── Tabs.variable.styl │ │ │ ├── Toggle.variable.styl │ │ │ ├── ToolBar.variable.styl │ │ │ └── index.styl │ └── variable.styl ├── dialog │ ├── DialogWindow.js │ ├── commander.js │ └── windowScrollHelper.js ├── font │ ├── melon.eot │ ├── melon.svg │ ├── melon.ttf │ ├── melon.woff │ └── melon.woff2 ├── menu │ └── MenuItem.js ├── navigtaion │ ├── Header.js │ └── Item.js ├── region │ ├── Area.js │ ├── City.js │ ├── Province.js │ ├── Selector.js │ └── helper.js ├── ripples │ ├── CenterRipple.js │ ├── RippleCircle.js │ └── TouchRipple.js ├── scrollview │ └── Bar.js ├── select │ ├── Option.js │ └── OptionGroup.js ├── slider │ ├── Bar.js │ └── getNewValue.js ├── table │ ├── Cell.js │ ├── Column.js │ ├── Row.js │ ├── SelectorColumn.js │ ├── SelectorRow.js │ ├── TableBody.js │ └── TextEditor.js ├── tabs │ ├── Panel.js │ └── Tab.js ├── textbox │ ├── FloatLabel.js │ └── Input.js └── tree │ └── TreeNode.js ├── test ├── components │ ├── Alert.spec.js │ ├── BoxGroup.spec.js │ ├── Breadcrumb.spec.js │ ├── Button.spec.js │ ├── Card.spec.js │ ├── Chip.spec.js │ ├── Confirm.spec.js │ ├── Dialog.spec.js │ ├── Drawer.spec.js │ ├── Icon.spec.js │ ├── Layer.spec.js │ ├── Link.spec.js │ ├── Pager.spec.js │ ├── ScrollView.spec.js │ ├── Select.spec.js │ ├── SnackBar.spec.js │ ├── Tabs.spec.js │ ├── TextBox.spec.js │ ├── Title.spec.js │ ├── Toggle.spec.js │ ├── ToolBar.spec.js │ ├── Tooltip.spec.js │ ├── Tree.spec.js │ ├── Uploader.spec.js │ └── Zippy.spec.js ├── index.js ├── index.styl ├── karma.ci.conf.js ├── karma.conf.js ├── karma.local.conf.js ├── then.js └── waitFor.js └── tools ├── dll.json ├── webpack.common.js ├── webpack.dev.js ├── webpack.dll.js └── webpack.prod.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "dev": { 4 | "presets": [ 5 | "es2015", 6 | "stage-2", 7 | "react" 8 | ] 9 | }, 10 | "test": { 11 | "presets": [ 12 | "es2015", 13 | "stage-2", 14 | "react" 15 | ], 16 | "plugins": [ 17 | "istanbul" 18 | ] 19 | }, 20 | "production": { 21 | "presets": [ 22 | [ 23 | "es2015", 24 | { 25 | "loose": true, 26 | "modules": "umd" 27 | } 28 | ], 29 | "stage-2", 30 | "react" 31 | ] 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | repo_token: 1pRkyandErbPD52qlOmWkEnUHJFlNs6BC -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.fecsignore: -------------------------------------------------------------------------------- 1 | dist 2 | example/*.html -------------------------------------------------------------------------------- /.fecsrc: -------------------------------------------------------------------------------- 1 | { 2 | "eslint": { 3 | "env": { 4 | "es6": -1 5 | }, 6 | "rules": { 7 | "fecs-jsx-var": [ 8 | 2, 9 | { 10 | "pragma": "React" 11 | } 12 | ], 13 | "fecs-no-require": -1 14 | } 15 | }, 16 | "files": ["src"] 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dep 3 | coverage 4 | .edpproj 5 | npm-debug.log 6 | output 7 | coverage 8 | *.orig 9 | .DS_Store 10 | **/.DS_Store 11 | doc 12 | asset 13 | lib/package.json 14 | lib/readme.md 15 | browsers.json 16 | .idea 17 | lib 18 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dep 3 | coverage 4 | .edpproj 5 | src 6 | test 7 | npm-debug.log 8 | output 9 | coverage 10 | *.orig 11 | .bower.json 12 | asset 13 | package-lock.json 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 6 4 | install: 5 | | 6 | npm --version 7 | npm install --registry http://registry.npmjs.org 8 | script: 9 | - npm run test-ci 10 | after_script: 11 | - npm run coveralls 12 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "melon", 3 | "version": "0.5.13", 4 | "homepage": "http://react-melon.github.io/melon", 5 | "authors": [ 6 | "ludafa@outlook.com" 7 | ], 8 | "description": "react ui components", 9 | "main": "lib", 10 | "moduleType": [ 11 | "umd" 12 | ], 13 | "keywords": [ 14 | "react", 15 | "ui" 16 | ], 17 | "license": "MIT", 18 | "ignore": [ 19 | "**/.*", 20 | "node_modules", 21 | "bower_components", 22 | "dep", 23 | "test", 24 | "tests", 25 | "src", 26 | "doc", 27 | "example", 28 | "asset" 29 | ], 30 | "dependencies": { 31 | "melon-core": "^0.4", 32 | "react": "^15", 33 | "react-motion": "^0.4" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /esdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "./src", 3 | "destination": "./doc", 4 | "plugins": [ 5 | { 6 | "name": "esdoc-es7-plugin" 7 | } 8 | ], 9 | "package": "./package.json" 10 | } 11 | -------------------------------------------------------------------------------- /example/App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon example main 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import Nav from './common/Nav'; 8 | 9 | export default class App extends Component { 10 | render() { 11 | const {components, name, Component} = this.props; 12 | return ( 13 |
14 |
17 | ); 18 | } 19 | renderContent(Component) { 20 | return Component 21 | ? React.createElement(Component) 22 | : '加载中~'; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/BoxGroup.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | @require '../src/css/BoxGroup.styl' 3 | @require '../src/css/Button.styl' 4 | @require '../src/css/Icon.styl' 5 | @require '../src/css/Toggle.styl' 6 | 7 | .row p 8 | margin: 10px 0 -------------------------------------------------------------------------------- /example/Breadcrumb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon demo Tabs 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | 8 | import Title from '../src/Title'; 9 | import Breadcrumb from '../src/Breadcrumb'; 10 | import Icon from '../src/Icon'; 11 | 12 | class View extends React.Component { 13 | 14 | constructor() { 15 | super(); 16 | this.state = {href: ''}; 17 | } 18 | 19 | render() { 20 | 21 | const onClick = e => { 22 | e.preventDefault(); 23 | this.setState({ 24 | href: e.currentTarget.href 25 | }); 26 | }; 27 | 28 | return ( 29 |
30 | 面包屑 / Breadcrumb 31 | 文本 32 | 33 | 首页 36 | 应用中心 39 | 应用列表 42 | 44 | 我的应用 45 | 46 | 带 Icon 47 | 48 | 51 | 首页 52 | 53 | 56 | 应用中心 57 | 58 | 61 | 62 | 63 | 64 |
65 |
66 |
67 |
68 | {this.state.href} 69 |
70 | ); 71 | } 72 | 73 | } 74 | 75 | module.exports = View; 76 | -------------------------------------------------------------------------------- /example/Breadcrumb.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | @require '../src/css/Breadcrumb.styl' 3 | @require '../src/css/Icon.styl' 4 | -------------------------------------------------------------------------------- /example/Button.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | @require '../src/css/Button.styl' 3 | @require '../src/css/ButtonGroup.styl' 4 | @require '../src/css/Icon.styl' 5 | 6 | .melon-row 7 | margin: 20px 0 8 | 9 | .melon-column 10 | 11 | > .ui-button + .ui-button 12 | margin-left: 1em 13 | -------------------------------------------------------------------------------- /example/Chip.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Chip demo 3 | * @author Ma63d(chuck7liu@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | 8 | import Chip from '../src/Chip'; 9 | import Title from '../src/Title'; 10 | 11 | export default class View extends React.Component { 12 | 13 | state = { 14 | chipArr: [1,2,3,4] 15 | } 16 | 17 | handleChipRemove = i => { 18 | let chipArr = this.state.chipArr; 19 | chipArr.splice(i, 1); 20 | this.setState({ 21 | chipArr 22 | }); 23 | } 24 | 25 | render() { 26 | return ( 27 |
28 | Chip 纸片 29 | 默认 30 | 31 |
32 |
33 | 34 | hello world! 35 | 36 |
37 |
38 | 39 | 40 | 带头像的Chip 41 | 42 |
43 |
44 | }> 45 | hello world! 46 | 47 |
48 |
49 | 50 | 51 | 52 | 可点击的Chip 53 | 54 |
55 |
56 | } 57 | onClick={() => {alert('the chip is clicked')}}> 58 | hello world! 59 | 60 |
61 |
62 | 63 | 带删除图标的Chip 64 | 65 |
66 |
67 | { 68 | this.state.chipArr.map((item, index) => ( 69 | } 70 | key={index} 71 | onClick={() => { 72 | alert('the chip is clicked'); 73 | }} 74 | onRemove={() => { 75 | this.handleChipRemove(index); 76 | }} 77 | style={{margin: '4px'}}> 78 | {`我是小新${item}号`} 79 | 80 | )) 81 | } 82 | 83 |
84 |
85 |
86 | 87 | ); 88 | } 89 | } 90 | 91 | -------------------------------------------------------------------------------- /example/Chip.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Chip.styl' 2 | @require '../src/css/Title.styl' 3 | @require '../src/css/Icon.styl' 4 | -------------------------------------------------------------------------------- /example/Dialog.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | @require '../src/css/Mask.styl' 3 | @require '../src/css/Dialog.styl' 4 | @require '../src/css/Button.styl' -------------------------------------------------------------------------------- /example/Drawer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon demo SnackBar 3 | * @author cxtom(cxtom2010@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import Title from '../src/Title'; 8 | import Button from '../src/Button'; 9 | import Drawer from '../src/Drawer'; 10 | import Select from '../src/Select'; 11 | 12 | class View extends React.Component { 13 | 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | open: false, 18 | position: 'left', 19 | size: '300' 20 | }; 21 | } 22 | 23 | render() { 24 | 25 | const {open, position, size} = this.state; 26 | 27 | return ( 28 |
29 | Drawer 30 |
79 | ); 80 | } 81 | } 82 | 83 | module.exports = View; 84 | -------------------------------------------------------------------------------- /example/Drawer.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Drawer.styl' 2 | -------------------------------------------------------------------------------- /example/Grid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon demo button 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React from 'react'; 7 | 8 | import Title from '../src/Title'; 9 | 10 | const CELL_STYLE = { 11 | backgroundColor: '#eee', 12 | display: 'flex', 13 | alignItems: 'center', 14 | justifyContent: 'center', 15 | height: '60px' 16 | }; 17 | 18 | class View extends React.Component { 19 | 20 | render() { 21 | return ( 22 |
23 | 按钮 24 | 预定义样式 25 | {this.renderGrid(12)} 26 |
27 | ); 28 | } 29 | 30 | renderGrid(columns) { 31 | 32 | let grid = []; 33 | 34 | for (let i = 1; i < columns; ++i) { 35 | 36 | let prev = 'melon-column melon-column-' + i; 37 | let next = 'melon-column melon-column-' + (columns - i); 38 | 39 | grid[i] = ( 40 |
41 |
{i}
42 |
{columns - i}
43 |
44 | ); 45 | 46 | } 47 | 48 | return grid; 49 | } 50 | 51 | } 52 | 53 | module.exports = View; 54 | -------------------------------------------------------------------------------- /example/Grid.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | -------------------------------------------------------------------------------- /example/Icon.styl: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/Pager.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon demo Pager 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | 8 | import Title from '../src/Title'; 9 | import Pager from '../src/Pager'; 10 | 11 | class View extends React.Component { 12 | 13 | constructor() { 14 | super(); 15 | this.state = { 16 | page1: 1, 17 | page2: 1 18 | }; 19 | } 20 | 21 | render() { 22 | return ( 23 |
24 | 翻页器 25 | 26 | 图标 27 | 28 | 29 | 30 | {this.getCurrentPage(1)} 31 | 32 | 文字 33 | 34 | 35 | 36 | {this.getCurrentPage(2)} 37 | 38 | 39 | 40 |
41 | ); 42 | } 43 | 44 | getCurrentPage(index) { 45 | return ( 46 |
47 | 50 |
51 | ); 52 | } 53 | 54 | onChange(index, e) { 55 | this.setState({ 56 | [`page${index}`]: e.page 57 | }); 58 | } 59 | 60 | } 61 | 62 | module.exports = View; 63 | -------------------------------------------------------------------------------- /example/Pager.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | @require '../src/css/Pager.styl' 3 | @require '../src/css/Icon.styl' -------------------------------------------------------------------------------- /example/Progress.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon demo Progress 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | 8 | import Title from '../src/Title'; 9 | import Progress from '../src/Progress'; 10 | 11 | class View extends React.Component { 12 | 13 | constructor() { 14 | super(); 15 | this.state = { 16 | value: 20 17 | }; 18 | } 19 | 20 | componentDidMount() { 21 | const me = this; 22 | let value = this.state.value; 23 | this.timer = setTimeout(function tick() { 24 | value += 10; 25 | if (value < 100) { 26 | me.timer = setTimeout(tick, 800); 27 | me.setState({value}); 28 | } 29 | }, 800); 30 | } 31 | 32 | componentWillUnmount() { 33 | clearTimeout(this.timer); 34 | } 35 | 36 | render() { 37 | return ( 38 |
39 | Progress 40 | 41 |
42 | Linear 43 | determinate 44 | 45 | indeterminate 46 | 47 |
48 | 49 |
50 | Circle 51 | determinate 52 | 53 | 54 | 55 | 56 | 57 | indeterminate 58 | 59 | 60 | 61 |
62 |
63 | ); 64 | } 65 | 66 | } 67 | 68 | module.exports = View; 69 | -------------------------------------------------------------------------------- /example/Progress.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | @require '../src/css/Progress.styl' -------------------------------------------------------------------------------- /example/Region.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | @require '../src/css/Region.styl' 3 | 4 | .row 5 | max-width: 800px; -------------------------------------------------------------------------------- /example/ScrollView.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon demo ScrollView 3 | * @author cxtom(cxtom2010@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import Title from '../src/Title'; 8 | import ScrollView from '../src/ScrollView'; 9 | 10 | class View extends React.Component { 11 | 12 | render() { 13 | return ( 14 |
15 | ScrollView 16 | 17 |
18 |

如何让你遇见我

19 |

在我最美丽的时刻为这

20 |

我已在佛前求了五百年

21 |

求他让我们结一段尘缘

22 |

佛于是把我化作一棵树

23 |

长在你必经的路旁

24 |

阳光下慎重地开满了花

25 |

朵朵都是我前世的盼望

26 |

当你走近请你细听

27 |

那颤抖的叶是我等待的热情

28 |

而当你终于无视地走过

29 |

在你身后落了一地的

30 |

朋友啊那不是花瓣

31 |

是我凋零的心

32 |

只缘感君一回顾,使我思君暮与朝

33 |

——席慕容《古乐府》

34 |
35 |
36 |

垂柳里,兰舟当日曾系。

37 |

千帆过尽,只伊人不随书至。

38 |

怪管道着我侬心,一般思妇游子。

39 |

昨宵梦,分明记:几回飞度烟水。

40 |

西风水断伴灯花,摇摇欲坠。

41 |

宵深待到凤凰山,声声啼鴂催起。

42 |

锦书宛在怀袖底。

43 |

人迢迢、紫塞千里,算是不曾相忆。

44 |

倘有情、早合归来,休寄一纸无聊相思字!

45 |

——王国维《西河》

46 |
47 |
48 |
49 | ); 50 | } 51 | } 52 | 53 | module.exports = View; 54 | -------------------------------------------------------------------------------- /example/ScrollView.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | @require '../src/css/ScrollView.styl' 3 | 4 | .ui-scrollview 5 | line-height: 25px -------------------------------------------------------------------------------- /example/Select.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | @require '../src/css/Select.styl' 3 | -------------------------------------------------------------------------------- /example/Slider.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon demo Slider 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | 8 | import Title from '../src/Title'; 9 | import Slider from '../src/Slider'; 10 | 11 | class View extends React.Component { 12 | 13 | constructor() { 14 | super(); 15 | 16 | this.state = { 17 | value1: 12 18 | }; 19 | 20 | this.onChange = this.onChange.bind(this); 21 | } 22 | 23 | onChange({value}) { 24 | this.setState({value1: value}); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 数值选择器 31 | 36 | 41 |
42 | 数值:{this.state.value1} 43 |
44 |
45 | ); 46 | } 47 | 48 | } 49 | 50 | module.exports = View; 51 | -------------------------------------------------------------------------------- /example/SnackBar.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | @require '../src/css/SnackBar.styl' 3 | @require '../src/css/TextBox.styl' 4 | -------------------------------------------------------------------------------- /example/Table.styl: -------------------------------------------------------------------------------- 1 | 2 | @require 'nib' 3 | @require '../src/css/Title.styl' 4 | @require '../src/css/Table.styl' 5 | @require '../src/css/Button.styl' 6 | 7 | #app 8 | padding-bottom 200px 9 | 10 | .ui-table 11 | margin: 20px 0 12 | -------------------------------------------------------------------------------- /example/Tabs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon demo Tabs 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | 8 | import Title from '../src/Title'; 9 | import Tabs from '../src/Tabs'; 10 | 11 | /* eslint-disable no-console */ 12 | 13 | const Tab = Tabs.Tab; 14 | 15 | class View extends React.Component { 16 | 17 | constructor(props) { 18 | super(props); 19 | this.state = { 20 | selectedIndex: 2 21 | }; 22 | } 23 | 24 | renderPanel(content) { 25 | return ( 26 |
34 | {content} 35 |
36 | ); 37 | } 38 | 39 | render() { 40 | return ( 41 |
42 | Tabs 43 |

uncontrolled tabs

44 | 47 | 48 | {this.renderPanel('This is Tab A')} 49 | 50 | 51 | {this.renderPanel('This is Tab B')} 52 | 53 | 54 | {this.renderPanel()} 55 | 56 | 57 | 58 |

controlled tabs

59 |
60 | 当前选中的是第 {this.state.selectedIndex} 个 tab 61 |
62 | this.setState({selectedIndex})}> 65 | 66 | {this.renderPanel('This is Tab A')} 67 | 68 | 69 | {this.renderPanel('This is Tab B')} 70 | 71 | 72 | {this.renderPanel()} 73 | 74 | 75 | 76 |
77 | ); 78 | } 79 | 80 | } 81 | 82 | module.exports = View; 83 | -------------------------------------------------------------------------------- /example/Tabs.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | @require '../src/css/Tabs.styl' 3 | -------------------------------------------------------------------------------- /example/TextBox.styl: -------------------------------------------------------------------------------- 1 | @require 'nib' 2 | @require '../src/css/Title.styl' 3 | @require '../src/css/TextBox.styl' 4 | 5 | .melon-row 6 | margin: 2em 0 7 | -------------------------------------------------------------------------------- /example/Tooltip.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon demo Tabs 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | 8 | import Title from '../src/Title'; 9 | import Tooltip from '../src/Tooltip'; 10 | import Button from '../src/Button'; 11 | import Icon from '../src/Icon'; 12 | 13 | class View extends React.Component { 14 | 15 | render() { 16 | 17 | return ( 18 |
19 | Switches 20 | 21 | 22 | 23 | 方向 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 按钮组 37 |
38 | 39 | 42 | 43 | 44 | 47 | 48 | 49 | 52 | 53 |
54 |
55 | ); 56 | } 57 | 58 | } 59 | 60 | module.exports = View; 61 | -------------------------------------------------------------------------------- /example/Tooltip.styl: -------------------------------------------------------------------------------- 1 | @require "../src/css/Title.styl" 2 | @require "../src/css/Tooltip.styl" 3 | @require "../src/css/Button.styl" 4 | @require "../src/css/Icon.styl" 5 | 6 | 7 | .ui-tooltip + .ui-tooltip 8 | margin-left: 1em 9 | -------------------------------------------------------------------------------- /example/Tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon demo Tree 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | 8 | import Title from '../src/Title'; 9 | import Tree from '../src/Tree'; 10 | 11 | const TreeNode = Tree.TreeNode; 12 | 13 | class View extends React.Component { 14 | 15 | render() { 16 | 17 | const datasource = [{ 18 | id: '1', 19 | text: '百度', 20 | children: [ 21 | { 22 | id: '2', 23 | text: '联盟研发部', 24 | children: [ 25 | {id: '21', text: 'RD'}, 26 | {id: '22', text: 'FE'}, 27 | {id: '23', text: 'QA'}, 28 | {id: '24', text: 'PM'} 29 | ] 30 | }, 31 | { 32 | id: '3', 33 | text: '贴吧事业部', 34 | children: [ 35 | { 36 | id: '31', 37 | text: 'RD', 38 | children: [ 39 | {id: '311', text: 'UI'}, 40 | {id: '312', text: 'BS'} 41 | ] 42 | }, 43 | {id: '32', text: 'FE'}, 44 | {id: '33', text: 'QA'}, 45 | {id: '34', text: 'PM'} 46 | ] 47 | }, 48 | { 49 | id: '4', 50 | text: '百度音乐' 51 | } 52 | ] 53 | }]; 54 | 55 | return ( 56 |
57 | Tree 58 | 59 |
60 | 普通 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
79 | 80 |
81 | 使用Datasource 82 | 83 | {Tree.createTreeNodes(datasource)} 84 | 85 |
86 | 87 |
88 | defaultExpandAll 89 | 90 | {Tree.createTreeNodes(datasource)} 91 | 92 |
93 |
94 | ); 95 | } 96 | } 97 | 98 | module.exports = View; 99 | -------------------------------------------------------------------------------- /example/Tree.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Title.styl' 2 | @require '../src/css/Tree.styl' -------------------------------------------------------------------------------- /example/Uploader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon demo button 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import Title from '../src/Title'; 8 | import Uploader from '../src/Uploader'; 9 | 10 | /* eslint-disable */ 11 | const EXAMPLE_IMG_URL = [ 12 | 'https://assets.materialup.com/uploads/878dbfeb-7503-4d63-a4ab-1435ac533296/attachment.png', 13 | 'http://react-melon.github.io/melon/asset/common/img/melon-logo.ffb5dd37.png' 14 | ]; 15 | /* eslint-enable */ 16 | 17 | function randomImageURL() { 18 | const index = Math.floor(EXAMPLE_IMG_URL.length * Math.random()); 19 | return EXAMPLE_IMG_URL[index]; 20 | } 21 | 22 | class View extends React.Component { 23 | 24 | constructor(props) { 25 | super(props); 26 | this.upload = this.upload.bind(this); 27 | this.state = { 28 | imgURL: randomImageURL(), 29 | fileURL: 'http://boscdn.bpc.baidu.com/mms-res/voicefe/captain/widgets/Text.zip' 30 | }; 31 | } 32 | 33 | render() { 34 | return ( 35 |
36 | 文件上传 37 | 待选择 38 | 43 | 48 | 图片已选择 49 | { 52 | 53 | if (!files) { 54 | this.setState({imgURL: ''}); 55 | return; 56 | } 57 | 58 | }} 59 | value={this.state.imgURL} /> 60 | 文件已选择 61 | { 65 | 66 | if (!files) { 67 | this.setState({fileURL: ''}); 68 | return; 69 | } 70 | 71 | }} 72 | value={this.state.fileURL} /> 73 |
74 | ); 75 | } 76 | 77 | upload(files) { 78 | 79 | return new Promise(function (resolve, reject) { 80 | 81 | setTimeout( 82 | function () { 83 | resolve(randomImageURL()); 84 | }, 85 | 50000 86 | ); 87 | 88 | }); 89 | 90 | } 91 | 92 | } 93 | 94 | module.exports = View; 95 | -------------------------------------------------------------------------------- /example/Uploader.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/Uploader.styl' 2 | @require '../src/css/Title.styl' 3 | -------------------------------------------------------------------------------- /example/Zippy.styl: -------------------------------------------------------------------------------- 1 | 2 | .ui-zippy 3 | p 4 | padding: 0 5 | -------------------------------------------------------------------------------- /example/common/Nav.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file demo nav 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | let React = require('react'); 7 | let PropTypes = require('prop-types'); 8 | 9 | let NavItem = require('./NavItem'); 10 | 11 | class Nav extends React.Component { 12 | 13 | render() { 14 | 15 | let {components, name} = this.props; 16 | 17 | return ( 18 | 31 | ); 32 | } 33 | 34 | } 35 | 36 | Nav.propsTypes = { 37 | components: PropTypes.array.isRequired 38 | }; 39 | 40 | module.exports = Nav; 41 | -------------------------------------------------------------------------------- /example/common/NavItem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/example/NavItem 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | var React = require('react'); 7 | 8 | class NavItem extends React.Component { 9 | render() { 10 | var {name, current} = this.props; 11 | return ( 12 |
  • 13 | 16 | {name} 17 | 18 |
  • 19 | ); 20 | } 21 | } 22 | 23 | module.exports = NavItem; 24 | -------------------------------------------------------------------------------- /example/common/component/Markdown.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Markdown component 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | var React = require('react'); 7 | var Remarkable = require('remarkable'); 8 | 9 | class Markdown extends React.Component { 10 | 11 | render() { 12 | 13 | return ( 14 |
    {this.getContent()}
    15 | ); 16 | 17 | } 18 | 19 | getContent() { 20 | 21 | var markdown = new Remarkable({ 22 | html: true 23 | }); 24 | 25 | return React.Children.map(this.props.children, function (child) { 26 | 27 | 28 | if (typeof child !== 'string') { 29 | return child; 30 | } 31 | 32 | child = { 33 | __html: markdown.render(child) 34 | }; 35 | 36 | return
    ; 37 | 38 | }); 39 | } 40 | 41 | } 42 | 43 | module.exports = Markdown; 44 | -------------------------------------------------------------------------------- /example/common/component/View.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon demo view 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | var React = require('react'); 7 | var Nav = require('./Nav'); 8 | 9 | var View = React.createClass({ 10 | 11 | render: function() { 12 | 13 | console.log(this.props); 14 | 15 | return ( 16 |
    17 |
    20 | ); 21 | } 22 | 23 | }); 24 | 25 | module.exports = View; 26 | -------------------------------------------------------------------------------- /example/common/tpl/base.swig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}melon{% endblock %} 6 | 7 | 8 | 9 | 10 | 11 |
    12 | 24 |
    25 |
    26 | 27 | 28 | 46 | {% block script %}{% endblock %} 47 | 48 | 49 | -------------------------------------------------------------------------------- /example/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon example config 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | module.exports = { 7 | components: [ 8 | 'BoxGroup', 9 | 'Breadcrumb', 10 | 'Button', 11 | 'Chip', 12 | 'Dialog', 13 | 'Drawer', 14 | 'Grid', 15 | 'Icon', 16 | 'Pager', 17 | 'Progress', 18 | 'Region', 19 | 'ScrollView', 20 | 'Select', 21 | 'Slider', 22 | 'SnackBar', 23 | 'Table', 24 | 'Tabs', 25 | 'TextBox', 26 | 'Tree', 27 | 'Tooltip', 28 | 'Uploader', 29 | 'Zippy' 30 | ] 31 | }; 32 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | melon example 6 | 7 | 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon example index 3 | * @author leon 4 | */ 5 | 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import HashLocator from 'numen/HashLocator'; 9 | 10 | import BoxGroup from './BoxGroup'; 11 | import Breadcrumb from './Breadcrumb'; 12 | import Button from './Button'; 13 | import Chip from './Chip'; 14 | import Dialog from './Dialog'; 15 | import Drawer from './Drawer'; 16 | import Grid from './Grid'; 17 | import Icon from './Icon'; 18 | import Pager from './Pager'; 19 | import Progress from './Progress'; 20 | import Region from './Region'; 21 | import ScrollView from './ScrollView'; 22 | import Select from './Select'; 23 | import Slider from './Slider'; 24 | import SnackBar from './SnackBar'; 25 | import Table from './Table'; 26 | import Tabs from './Tabs'; 27 | import TextBox from './TextBox'; 28 | import Tooltip from './Tooltip'; 29 | import Tree from './Tree'; 30 | import Uploader from './Uploader'; 31 | import Zippy from './Zippy'; 32 | 33 | import App from './App'; 34 | import config from './config'; 35 | import './index.styl'; 36 | 37 | const routes = { 38 | BoxGroup, 39 | Breadcrumb, 40 | Button, 41 | Chip, 42 | Dialog, 43 | Drawer, 44 | Grid, 45 | Icon, 46 | Pager, 47 | Progress, 48 | Region, 49 | ScrollView, 50 | Select, 51 | Slider, 52 | SnackBar, 53 | Table, 54 | Tabs, 55 | TextBox, 56 | Tooltip, 57 | Tree, 58 | Uploader, 59 | Zippy 60 | }; 61 | 62 | /* eslint-disable no-console */ 63 | 64 | function bootstrap(account) { 65 | 66 | const locator = new HashLocator(); 67 | 68 | locator.on(function (location) { 69 | 70 | const name = location.pathname.slice(1) || 'Button'; 71 | const Component = routes[name]; 72 | 73 | ReactDOM.render( 74 | , 78 | document.querySelector('#app') 79 | ); 80 | 81 | }); 82 | 83 | locator.start(); 84 | } 85 | 86 | bootstrap(); 87 | -------------------------------------------------------------------------------- /example/index.styl: -------------------------------------------------------------------------------- 1 | @require 'nib' 2 | @require '../src/css/mixin.styl' 3 | @require '../src/css/theme/default' 4 | 5 | global-reset() 6 | 7 | body 8 | font-size: 16px 9 | font-family: 'Helvetica Neue', Helvetica, 'Microsoft YaHei', Arial, '宋体', SimSun, sans-serif 10 | 11 | melon-generate-grid() 12 | 13 | #app 14 | margin-left 200px 15 | padding 0 10px 16 | 17 | .nav 18 | fixed: top left bottom 19 | width: 200px 20 | background: #1b1c1d 21 | text-align: right 22 | overflow-y: auto 23 | 24 | > h1 25 | height: 2em 26 | color: rgb(0, 159, 147) 27 | font-size: 32px 28 | line-height: @height 29 | padding-right 10px 30 | 31 | > ul 32 | 33 | border: 1px solid rgba(255, 255, 255, .3) 34 | border-width: 1px 0 1px 0 35 | 36 | a 37 | display block 38 | height 3em 39 | padding-right 10px 40 | color #888 41 | line-height @height 42 | text-decoration none 43 | transition all .3s 44 | 45 | &.current, &:hover 46 | background-color: #1b1c1d + 10% 47 | color: #fff 48 | 49 | .row 50 | padding: 10px 0 51 | 52 | .line 53 | float: left 54 | box-sizing: border-box 55 | width: 25% 56 | padding: 10px 57 | text-align center 58 | 59 | .cell 60 | margin: 10px 0 61 | height: 120px 62 | font-size: 16px 63 | 64 | &:hover 65 | color: $-melon-colors.teal500 66 | 67 | >label 68 | display: block 69 | height: 30px 70 | line-height 30px 71 | 72 | .melon-row 73 | margin-top: 1em 74 | 75 | p 76 | font-size: 1.2rem 77 | color: #aaa 78 | line-height: 1.5 79 | padding: 1rem 0 80 | 81 | @require '../src/css/**' 82 | @require 'BoxGroup.styl' 83 | @require 'Breadcrumb.styl' 84 | @require 'Button.styl' 85 | @require 'Chip.styl' 86 | @require 'Dialog.styl' 87 | @require 'Drawer.styl' 88 | @require 'Pager.styl' 89 | @require 'Progress.styl' 90 | @require 'Region.styl' 91 | @require 'ScrollView.styl' 92 | @require 'Select.styl' 93 | @require 'SnackBar.styl' 94 | @require 'Table.styl' 95 | @require 'Tabs.styl' 96 | @require 'TextBox.styl' 97 | @require 'Tooltip.styl' 98 | @require 'Tree.styl' 99 | @require 'Uploader.styl' 100 | @require 'Zippy.styl' 101 | @require '../src/css/Slider.styl' 102 | @require '../src/css/Zippy.styl' 103 | -------------------------------------------------------------------------------- /example/metaform.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | melon meta form demo 6 | 7 | 8 | 9 |
    10 | 11 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/metaform.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file metaform 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | const React = require('react'); 7 | const ReactDOM = require('react-dom'); 8 | const Button = require('melon/Button'); 9 | const Icon = require('melon/Icon'); 10 | const Breadcrumb = require('melon/Breadcrumb'); 11 | const Title = require('melon/Title'); 12 | const Link = require('melon/Link'); 13 | 14 | ReactDOM.render( 15 |
    16 | button 17 | 18 | 19 | icon 20 | 21 | 22 | 首页 23 | 应用中心 24 | 应用列表 25 | 我的应用 26 | 27 | level 1 haha 28 | a link 29 | level 2 haha 30 | level 3 haha 31 | level 4 haha 32 | level 5 haha 33 | level 6 haha 34 |
    , 35 | document.querySelector('#app') 36 | ); 37 | -------------------------------------------------------------------------------- /example/metaform.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/theme/default' 2 | @require '../src/css/Button.styl' 3 | @require '../src/css/Icon.styl' 4 | @require '../src/css/Breadcrumb.styl' 5 | @require '../src/css/Title.styl' 6 | @require '../src/css/Link.styl' 7 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file gulpfile 3 | * @author leon 4 | */ 5 | 6 | const gulp = require('gulp'); 7 | const babel = require('gulp-babel'); 8 | const clean = require('gulp-clean'); 9 | 10 | const sourcemaps = require('gulp-sourcemaps'); 11 | 12 | gulp.task('babel', function () { 13 | return gulp.src('src/**/*.js') 14 | .pipe(sourcemaps.init()) 15 | .pipe(babel()) 16 | .pipe(sourcemaps.write('.')) 17 | .pipe(gulp.dest('lib')); 18 | }); 19 | 20 | gulp.task('stylus', function () { 21 | return gulp.src('src/**/*.styl').pipe(gulp.dest('lib')); 22 | }); 23 | 24 | gulp.task('font', function () { 25 | return gulp.src('src/font/*').pipe(gulp.dest('lib/font')); 26 | }); 27 | 28 | gulp.task('pkg', function () { 29 | return gulp.src([ 30 | 'package.json', 31 | 'readme.md' 32 | ]).pipe(gulp.dest('lib')); 33 | }); 34 | 35 | gulp.task('build', ['babel', 'stylus', 'font', 'pkg']); 36 | 37 | gulp.task('clean', function () { 38 | return gulp 39 | .src('dist', {read: false}) 40 | .pipe(clean()); 41 | }); 42 | 43 | gulp.task('rebuild', ['clean', 'build']); 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "melon", 3 | "version": "0.6.0-alpha.3", 4 | "description": "ui components for react", 5 | "main": "./src", 6 | "scripts": { 7 | "start": "NODE_ENV=dev webpack-dev-server --config tools/webpack.dev.js", 8 | "build": "rimraf lib && NODE_ENV=production gulp build", 9 | "lint": "fecs", 10 | "docs": "esdoc -c esdoc.json", 11 | "setup": "rimraf asset && NODE_ENV=production webpack --config=tools/webpack.dll.js", 12 | "test": "NODE_ENV=test karma start ./test/karma.local.conf.js", 13 | "test-ci": "NODE_ENV=test karma start ./test/karma.ci.conf.js", 14 | "coveralls": "cat ./test/coverage/lcov/lcov.info | ./node_modules/.bin/coveralls" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.26.0", 18 | "babel-istanbul": "^0.12.2", 19 | "babel-loader": "^6.2.5", 20 | "babel-plugin-istanbul": "^4.1.5", 21 | "babel-plugin-transform-es2015-modules-umd": "^6.12.0", 22 | "babel-plugin-transform-object-rest-spread": "^6.8.0", 23 | "babel-preset-es2015": "^6.14.0", 24 | "babel-preset-react": "^6.11.1", 25 | "babel-preset-stage-2": "^6.13.0", 26 | "connect-history-api-fallback": "^1.3.0", 27 | "coveralls": "^2.11.12", 28 | "css-loader": "^0.24.0", 29 | "enzyme": "^3.1.0", 30 | "enzyme-adapter-react-16": "^1.0.2", 31 | "esdoc": "^0.4.8", 32 | "esdoc-es7-plugin": "0.0.3", 33 | "extract-text-webpack-plugin": "^1.0.1", 34 | "fecs": "^0.8.6", 35 | "file-loader": "^0.9.0", 36 | "gulp": "^3.9.1", 37 | "gulp-babel": "^6.1.2", 38 | "gulp-clean": "^0.3.2", 39 | "gulp-sourcemaps": "^1.6.0", 40 | "html-webpack-harddisk-plugin": "0.0.2", 41 | "html-webpack-plugin": "^2.22.0", 42 | "jasmine": "^2.4.1", 43 | "json-loader": "^0.5.4", 44 | "karma": "^1.2.0", 45 | "karma-babel-preprocessor": "^6.0.1", 46 | "karma-browserstack-launcher": "^1.2.0", 47 | "karma-chrome-launcher": "^2.0.0", 48 | "karma-coverage": "^1.1.1", 49 | "karma-firefox-launcher": "^1.0.0", 50 | "karma-jasmine": "^1.0.2", 51 | "karma-jasmine-expect-jsx": "^1.0.2", 52 | "karma-sourcemap-loader": "^0.3.7", 53 | "karma-webpack": "^1.8.0", 54 | "nib": "^1.1.2", 55 | "numen": "^0.2.0", 56 | "react": "^16.0.0", 57 | "react-dom": "^16.0.0", 58 | "react-test-renderer": "^16.0.0", 59 | "rimraf": "^2.5.4", 60 | "style-loader": "^0.13.1", 61 | "stylus": "^0.54.5", 62 | "stylus-loader": "^2.3.1", 63 | "watchify": "^3.7.0", 64 | "webpack": "^1.13.1", 65 | "webpack-dev-server": "^1.16.5" 66 | }, 67 | "repository": { 68 | "type": "git", 69 | "url": "git+https://github.com/react-melon/melon.git" 70 | }, 71 | "peerDependencies": { 72 | "react": "^16.0.0", 73 | "react-dom": "^16.0.0" 74 | }, 75 | "keywords": [ 76 | "react", 77 | "ui", 78 | "components" 79 | ], 80 | "author": "ludafa@outlook.com", 81 | "license": "ISC", 82 | "bugs": { 83 | "url": "https://github.com/react-melon/melon/issues" 84 | }, 85 | "homepage": "https://github.com/react-melon/melon#readme", 86 | "dependencies": { 87 | "dom-align": "^1.5.3", 88 | "lodash.omit": "^4.5.0", 89 | "melon-core": "^0.5.2", 90 | "prop-types": "^15.5.10", 91 | "react-motion": "^0.5.2" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Alert.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/dialog/Alert 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import Dialog from './Dialog'; 9 | import Button from './Button'; 10 | import createCommand from './dialog/commander'; 11 | 12 | /* eslint-disable fecs-prefer-class */ 13 | 14 | /** 15 | * melon/Alert 16 | * 17 | * @class 18 | * @param {Object} props 属性 19 | * @return {ReactElement} 20 | */ 21 | export default function Alert(props) { 22 | 23 | const { 24 | variants = [], 25 | buttonVariants = [], 26 | size, 27 | onConfirm, 28 | ...rest 29 | } = props; 30 | 31 | const actions = ( 32 | 52 | ); 53 | 54 | } 55 | /* eslint-enable fecs-prefer-class */ 56 | 57 | Button.defaultProps = { 58 | hasRipple: true, 59 | disabled: false 60 | }; 61 | 62 | Button.propTypes = { 63 | hasRipple: PropTypes.bool, 64 | disabled: PropTypes.bool 65 | }; 66 | -------------------------------------------------------------------------------- /src/Card.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Card 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import {create} from 'melon-core/classname/cxBuilder'; 8 | 9 | const cx = create('Card'); 10 | 11 | /* eslint-disable fecs-prefer-class */ 12 | 13 | /** 14 | * melon/Card 15 | * 16 | * @class 17 | * @param {Object} props 属性 18 | * @return {ReactElement} 19 | */ 20 | export default function Card(props) { 21 | 22 | const children = props.children; 23 | 24 | return ( 25 |
    26 | {children} 27 |
    28 | ); 29 | 30 | } 31 | /* eslint-enable fecs-prefer-class */ 32 | 33 | Card.displayName = 'Card'; 34 | -------------------------------------------------------------------------------- /src/Confirm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/dialog/Confirm 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import Dialog from './Dialog'; 9 | import Button from './Button'; 10 | import createCommand from './dialog/commander'; 11 | 12 | /* eslint-disable fecs-prefer-class */ 13 | 14 | /** 15 | * melon/Confirm 16 | * 17 | * @extends {React.Component} 18 | * @class 19 | * @param {Object} props 属性 20 | */ 21 | export default function Confirm(props) { 22 | 23 | 24 | const { 25 | size, 26 | buttonVariants, 27 | variants = [], 28 | onConfirm, 29 | onCancel, 30 | cancelButtonText, 31 | confirmButtonText, 32 | ...rest 33 | } = props; 34 | 35 | const actions = [ 36 | 45 | 51 | 52 | {children} 53 | 54 | 55 | 56 | 57 | ); 58 | 59 | } 60 | 61 | } 62 | 63 | IconMenu.propTypes = { 64 | icon: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), 65 | children: PropTypes.arrayOf(PropTypes.element), 66 | maxHeight: PropTypes.number 67 | }; 68 | -------------------------------------------------------------------------------- /src/Link.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/Link 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React from 'react'; 7 | 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | import omit from 'lodash/omit'; 10 | 11 | const cx = create('Link'); 12 | 13 | /* eslint-disable fecs-prefer-class */ 14 | 15 | /** 16 | * melon/Link 17 | * 18 | * @class 19 | * @param {Object} props 属性 20 | * @return {ReactElement} 21 | */ 22 | export default function Link(props) { 23 | 24 | return ( 25 | 26 | ); 27 | 28 | } 29 | 30 | Link.displayName = 'Link'; 31 | 32 | /* eslint-enable fecs-prefer-class */ 33 | -------------------------------------------------------------------------------- /src/Mask.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/Mask 3 | * @author cxtom 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | import * as windowScrollHelper from './dialog/windowScrollHelper'; 10 | import omit from 'lodash/omit'; 11 | 12 | const cx = create('Mask'); 13 | 14 | /** 15 | * melon/Pager 16 | * 17 | * @extends {React.Component} 18 | * @class 19 | */ 20 | export default class Mask extends Component { 21 | 22 | /** 23 | * Mount时的处理 24 | * 25 | * @public 26 | * @override 27 | */ 28 | componentDidMount() { 29 | 30 | const {show, lockScrollingOnShow} = this.props; 31 | 32 | if (show && lockScrollingOnShow) { 33 | this.lockScroll(); 34 | } 35 | } 36 | 37 | /** 38 | * 是否更新组件 39 | * 40 | * @public 41 | * @override 42 | * @return {boolean} 43 | */ 44 | shouldComponentUpdate({show}) { 45 | return this.props.show !== show; 46 | } 47 | 48 | /** 49 | * 更新时处理 50 | * 51 | * @public 52 | * @override 53 | */ 54 | componentDidUpdate() { 55 | const {show, lockScrollingOnShow} = this.props; 56 | show && lockScrollingOnShow ? this.lockScroll() : this.unlockScroll(); 57 | } 58 | 59 | /** 60 | * unmount时处理 61 | * 62 | * @public 63 | * @override 64 | */ 65 | componentWillUnmount() { 66 | this.unlockScroll(); 67 | } 68 | 69 | /** 70 | * lock scrolling 71 | * 72 | * @public 73 | */ 74 | lockScroll() { 75 | windowScrollHelper.stop(); 76 | } 77 | 78 | /** 79 | * unlock scrolling 80 | * 81 | * @public 82 | */ 83 | unlockScroll() { 84 | windowScrollHelper.restore(); 85 | } 86 | 87 | /** 88 | * 渲染 89 | * 90 | * @public 91 | * @return {ReactElement} 92 | */ 93 | render() { 94 | 95 | const { 96 | show, 97 | variants, 98 | states, 99 | ...rest 100 | } = this.props; 101 | 102 | const className = cx() 103 | .addVariants(variants) 104 | .addStates({ 105 | ...states, 106 | show 107 | }) 108 | .build(); 109 | 110 | return ( 111 |
    114 | ); 115 | 116 | } 117 | 118 | } 119 | 120 | Mask.defaultProps = { 121 | lockScrollingOnShow: true, 122 | show: false 123 | }; 124 | 125 | 126 | Mask.propTypes = { 127 | lockScrollingOnShow: PropTypes.bool, 128 | show: PropTypes.bool 129 | }; 130 | -------------------------------------------------------------------------------- /src/Title.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/Title 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | 10 | const cx = create('Title'); 11 | 12 | /* eslint-disable fecs-prefer-class */ 13 | 14 | /** 15 | * melon/Title 16 | * 17 | * @class 18 | * @param {Object} props 属性 19 | * @param {Object} props.level 级别 20 | * @return {ReactElement} 21 | */ 22 | export default function Title(props) { 23 | 24 | /* eslint-disable fecs-min-vars-per-destructure */ 25 | let {level, variants, states, ...rest} = props; 26 | 27 | let Type = `h${level}`; 28 | let className = cx(props).addVariants(variants).addStates(states).build(); 29 | 30 | return ; 31 | 32 | } 33 | 34 | Title.propsTypes = { 35 | level: PropTypes.oneOf([1, 2, 3, 4, 5, 6]).isRequired 36 | }; 37 | 38 | Title.defaultProps = { 39 | level: 1 40 | }; 41 | -------------------------------------------------------------------------------- /src/Toggle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/Toggle 3 | * @author cxtom 4 | * @author leon 5 | */ 6 | 7 | 8 | import React from 'react'; 9 | import PropTypes from 'prop-types'; 10 | import InputComponent from 'melon-core/InputComponent'; 11 | import {create} from 'melon-core/classname/cxBuilder'; 12 | import CenterRipple from './ripples/CenterRipple'; 13 | 14 | const cx = create('Toggle'); 15 | 16 | /** 17 | * melon/Toggle 18 | * 19 | * @extends {melon-core/InputComponent} 20 | * @class 21 | */ 22 | export default class Toggle extends InputComponent { 23 | 24 | /** 25 | * 构造函数 26 | * 27 | * @public 28 | * @constructor 29 | * @param {*} props 属性 30 | * @param {*} context 上下文 31 | */ 32 | constructor(props, context) { 33 | 34 | super(props, context); 35 | 36 | this.onChange = this.onChange.bind(this); 37 | 38 | } 39 | 40 | /** 41 | * 值改变处理 42 | * 43 | * @protected 44 | * @param {Object} e 事件对象 45 | */ 46 | onChange(e) { 47 | 48 | const { 49 | disabled, 50 | readOnly, 51 | trueValue, 52 | falseValue 53 | } = this.props; 54 | 55 | if (disabled || readOnly) { 56 | return; 57 | } 58 | 59 | super.onChange({ 60 | type: 'change', 61 | target: this, 62 | value: e.target.checked ? trueValue : falseValue 63 | }); 64 | 65 | } 66 | 67 | /** 68 | * 渲染 69 | * 70 | * @public 71 | * @return {ReactElement} 72 | */ 73 | render() { 74 | 75 | const { 76 | props, 77 | state, 78 | onChange 79 | } = this; 80 | 81 | const value = state.value; 82 | 83 | const { 84 | name, 85 | trueValue, 86 | disabled 87 | } = props; 88 | 89 | const checked = value === trueValue; 90 | 91 | return ( 92 |
    100 |
    101 |
    102 | {disabled 103 | ? null 104 | : } 105 |
    106 |
    107 | 108 | ); 109 | 110 | } 111 | 112 | } 113 | 114 | Toggle.displayName = 'Toggle'; 115 | 116 | Toggle.defaultProps = { 117 | ...InputComponent.defaultProps, 118 | trueValue: 'on', 119 | falseValue: '' 120 | }; 121 | 122 | Toggle.propTypes = { 123 | ...InputComponent.propTypes, 124 | trueValue: PropTypes.any.isRequired 125 | }; 126 | 127 | Toggle.childContextTypes = InputComponent.childContextTypes; 128 | Toggle.contextTypes = InputComponent.contextTypes; 129 | -------------------------------------------------------------------------------- /src/ToolBar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ToolBar 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import {create} from 'melon-core/classname/cxBuilder'; 8 | 9 | const cx = create('ToolBar'); 10 | 11 | /** 12 | * melon/ToolBar 13 | * 14 | * @class 15 | * @param {Object} props 属性 16 | * @return {ReactElement} 17 | */ 18 | export default function ToolBar(props) { 19 | 20 | const children = props.children; 21 | 22 | return ( 23 |
    24 | {children} 25 |
    26 | ); 27 | 28 | } 29 | 30 | ToolBar.displayName = 'ToolBar'; 31 | -------------------------------------------------------------------------------- /src/Zippy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Zippy 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | 9 | 10 | import {create} from 'melon-core/classname/cxBuilder'; 11 | 12 | const cx = create('Zippy'); 13 | 14 | 15 | /** 16 | * melon/Zippy 17 | * 18 | * @extends {React.Component} 19 | * @class 20 | */ 21 | export default class Zippy extends Component { 22 | 23 | /** 24 | * 渲染 25 | * 26 | * @public 27 | * @return {ReactElement} 28 | */ 29 | render() { 30 | 31 | const props = this.props; 32 | 33 | const { 34 | expand, 35 | direction, 36 | variants, 37 | states, 38 | ...others 39 | } = props; 40 | 41 | const className = cx() 42 | .addVariants(variants) 43 | .addStates(states) 44 | .addVariants(direction) 45 | .addStates({close: !expand}) 46 | .build(); 47 | 48 | let style = this.props.style; 49 | let isVertical = direction === 'vertical'; 50 | let transform = `scale(${isVertical ? 1 : 0}, ${isVertical ? 0 : 1})`; 51 | 52 | if (!expand) { 53 | style = { 54 | ...style, 55 | WebkitTransform: transform, 56 | MozTransform: transform, 57 | msTransform: transform, 58 | transform 59 | }; 60 | } 61 | 62 | return ( 63 |
    64 | ); 65 | 66 | } 67 | 68 | } 69 | 70 | Zippy.displayName = 'Zippy'; 71 | 72 | Zippy.propTypes = { 73 | direction: PropTypes.oneOf(['vertical', 'horizontal']), 74 | expand: PropTypes.bool 75 | }; 76 | 77 | Zippy.defaultProps = { 78 | direction: 'vertical', 79 | expand: false 80 | }; 81 | -------------------------------------------------------------------------------- /src/boxgroup/Option.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/boxgroup/Option 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | import Icon from '../Icon'; 10 | import CenterRipple from '../ripples/CenterRipple'; 11 | 12 | const cx = create('BoxGroupOption'); 13 | 14 | export default class BoxGroupOption extends Component { 15 | 16 | constructor(props) { 17 | super(props); 18 | this.onClick = this.onClick.bind(this); 19 | } 20 | 21 | onClick() { 22 | this.refs.ripple && this.refs.ripple.animate(); 23 | } 24 | 25 | getIcon(boxModel, isChecked) { 26 | return BoxGroupOption.Icons[boxModel][isChecked ? 'checked' : 'unchecked']; 27 | } 28 | 29 | render() { 30 | 31 | const props = this.props; 32 | 33 | const { 34 | boxModel, 35 | checked, 36 | disabled, 37 | readOnly, 38 | onChange 39 | } = props; 40 | 41 | 42 | const className = cx(props).addStates({checked}).build(); 43 | const icon = this.getIcon(boxModel, checked); 44 | 45 | return ( 46 | 60 | ); 61 | 62 | } 63 | 64 | } 65 | 66 | BoxGroupOption.displayName = 'BoxGroupOption'; 67 | 68 | BoxGroupOption.propTypes = { 69 | boxModel: PropTypes.oneOf(['radio', 'checkbox']).isRequired, 70 | label: PropTypes.string, 71 | value: PropTypes.string, 72 | checked: PropTypes.bool, 73 | name: PropTypes.string, 74 | disabled: PropTypes.bool, 75 | onChange: PropTypes.func.isRequired 76 | }; 77 | 78 | BoxGroupOption.Icons = { 79 | radio: { 80 | checked: 'radio-button-checked', 81 | unchecked: 'radio-button-unchecked' 82 | }, 83 | checkbox: { 84 | checked: 'check-box', 85 | unchecked: 'check-box-outline-blank' 86 | } 87 | }; 88 | -------------------------------------------------------------------------------- /src/breadcrumb/Item.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/breadcrumb/item 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | 10 | const cx = create('BreadcrumbItem'); 11 | 12 | /** 13 | * 面包屑项 14 | * 15 | * @class 16 | * @param {*} props 属性 17 | */ 18 | export default function BreadcrumbItem(props) { 19 | 20 | const {level, ...rest} = props; 21 | 22 | return ( 23 | 27 | ); 28 | } 29 | 30 | BreadcrumbItem.propTypes = { 31 | href: PropTypes.string 32 | }; 33 | -------------------------------------------------------------------------------- /src/common/util/dom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon dom 相关的小工具 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | /** 7 | * 获取文档的兼容根节点 8 | * 9 | * @inner 10 | * @param {?HTMLElement=} el 节点引用,跨 frame 时需要 11 | * @return {HTMLElement} 兼容的有效根节点 12 | */ 13 | function getCompatElement(el) { 14 | let doc = el && el.ownerDocument || document; 15 | let compatMode = doc.compatMode; 16 | return !compatMode || compatMode === 'CSS1Compat' 17 | ? doc.documentElement 18 | : doc.body; 19 | } 20 | 21 | export function getClientHeight() { 22 | return getCompatElement().clientHeight; 23 | } 24 | 25 | export function getClientWidth() { 26 | return getCompatElement().clientWidth; 27 | } 28 | 29 | export function getPosition(element) { 30 | 31 | let bound = element.getBoundingClientRect(); 32 | 33 | let root = document.documentElement; 34 | let body = document.body; 35 | 36 | let clientTop = root.clientTop || body.clientTop || 0; 37 | let clientLeft = root.clientLeft || body.clientLeft || 0; 38 | let scrollTop = window.pageYOffset || root.scrollTop; 39 | let scrollLeft = window.pageXOffset || root.scrollLeft; 40 | 41 | return { 42 | left: parseFloat(bound.left) + scrollLeft - clientLeft, 43 | top: parseFloat(bound.top) + scrollTop - clientTop, 44 | width: bound.width, 45 | height: bound.height 46 | }; 47 | 48 | } 49 | 50 | export function hasClass(element, cls) { 51 | return element.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); 52 | } 53 | 54 | export function addClass(element, cls) { 55 | if (!this.hasClass(element, cls)) { 56 | element.className += ' ' + cls; 57 | } 58 | } 59 | 60 | export function removeClass(element, cls) { 61 | if (this.hasClass(element, cls)) { 62 | let reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); 63 | element.className = element.className.replace(reg, ' '); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/common/util/fn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Baidu Inc. All rights reserved. 3 | * 4 | * @file function 相关的小工具 5 | * @author leon 6 | */ 7 | 8 | 9 | /** 10 | * 限流 11 | * 12 | * @param {Function} func 被限流的原函数 13 | * @param {number} wait 限流时间阀 14 | * @param {Object} options 选项 15 | * @return {Function} 16 | */ 17 | export function throttle(func, wait, options = {}) { 18 | 19 | let timeout; 20 | let context; 21 | let args; 22 | let result; 23 | let previous = 0; 24 | 25 | const later = function () { 26 | 27 | previous = options.leading === false ? 0 : new Date().getTime(); 28 | timeout = null; 29 | result = func.apply(context, args); 30 | 31 | if (!timeout) { 32 | context = args = null; 33 | } 34 | 35 | }; 36 | 37 | const throttled = function (...argus) { 38 | 39 | const now = new Date(); 40 | 41 | if (!previous && options.leading === false) { 42 | previous = now; 43 | } 44 | 45 | const remaining = wait - (now - previous); 46 | 47 | context = this; 48 | args = argus; 49 | 50 | if (remaining <= 0 || remaining > wait) { 51 | if (timeout) { 52 | clearTimeout(timeout); 53 | timeout = null; 54 | } 55 | previous = now; 56 | result = func.apply(context, args); 57 | if (!timeout) { 58 | context = args = null; 59 | } 60 | } 61 | else if (!timeout && options.trailing !== false) { 62 | timeout = setTimeout(later, remaining); 63 | } 64 | 65 | return result; 66 | }; 67 | 68 | throttled.cancel = function () { 69 | clearTimeout(timeout); 70 | previous = 0; 71 | timeout = context = args = null; 72 | }; 73 | 74 | return throttled; 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/common/util/guid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file guid 3 | * @author leon 4 | */ 5 | 6 | export default function guid() { 7 | return (+(Math.random() + '').substr(2, 12)).toString(36); 8 | } 9 | -------------------------------------------------------------------------------- /src/css/BoxGroup.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | @require './Ripple.styl' 3 | @require './Icon.styl' 4 | 5 | .{$-melon-class-prefix}-box-group 6 | 7 | position: relative 8 | display: inline-block 9 | width: $-melon-boxgroup.width 10 | 11 | &-option 12 | display: block 13 | line-height: 2em 14 | color: $-melon-boxgroup-option.color 15 | cursor: pointer 16 | position: relative 17 | 18 | > input 19 | display: none 20 | 21 | > .{$-melon-class-prefix}-icon 22 | margin-right: 0.5em 23 | font-size: $-melon-boxgroup-option-icon.font-size 24 | color: $-melon-boxgroup-option-icon.color 25 | vertical-align: middle 26 | transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms 27 | position: relative 28 | z-index: 1 29 | 30 | .{$-melon-class-prefix}-center-ripple-circle 31 | height: 18px 32 | width: 18px 33 | top: 50% 34 | margin-top: -9px 35 | left: 50% 36 | margin-left: -9px 37 | 38 | > span 39 | vertical-align: middle 40 | 41 | &.state-checked 42 | 43 | > .{$-melon-class-prefix}-icon 44 | color: $-melon-boxgroup-option-icon-checked.color 45 | 46 | &.state-disabled 47 | color: $-melon-boxgroup-option-icon-disabled.color 48 | cursor: not-allowed 49 | 50 | &.state-invalid &-option .ui-icon 51 | color: $-melon-boxgroup-option-icon-invalid.color 52 | 53 | &.variant-horizontal 54 | display: inline-block 55 | margin-left: -1em 56 | width: auto 57 | 58 | .{$-melon-class-prefix}-box-group-option 59 | display: inline-block 60 | margin-left: 1em 61 | 62 | > .{$-melon-class-prefix}-icon 63 | margin-right: .5em 64 | 65 | .{$-melon-class-prefix}-validity 66 | margin-left: 1.5em 67 | -------------------------------------------------------------------------------- /src/css/Breadcrumb.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | 3 | .{$-melon-class-prefix}-breadcrumb 4 | 5 | display: inline-block 6 | 7 | &-item 8 | 9 | display: inline-block 10 | text-decoration: none 11 | color: $-melon-breadcrumb.item.color 12 | line-height: 2em 13 | 14 | &[href]:hover 15 | color: $-melon-breadcrumb.item.hover.color 16 | 17 | &:after 18 | display: inline-block 19 | content: '/' 20 | padding: 0 0.5em 21 | color: $-melon-breadcrumb.seperator.color 22 | 23 | &:last-child 24 | color: $-melon-breadcrumb.item.lastChild.color 25 | &:after 26 | display: none 27 | 28 | &>.ui-icon 29 | display: inline-block 30 | -------------------------------------------------------------------------------- /src/css/Button.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | @require './Ripple.styl' 3 | 4 | melon-button-swatch($swatches, $revert = false) 5 | 6 | color: $revert ? $-melon-colors.white : $swatches.color 7 | background-color: $revert ? $swatches.color : $swatches.background-color 8 | 9 | & .{$-melon-class-prefix}-touch-ripple-circle 10 | background-color: $swatches.color-ripple 11 | 12 | &:hover 13 | color: $revert ? $-melon-colors.white : $swatches.color-hover 14 | background-color: rgba($swatches.background-color-hover, $revert ? .6 : .2) 15 | 16 | &:active 17 | color: $revert ? $-melon-colors.white : $swatches.color-active 18 | background-color: rgba($swatches.background-color-active, $revert ? .8 : .4) 19 | 20 | 21 | .{$-melon-class-prefix}-button 22 | 23 | display: inline-block 24 | box-sizing: border-box 25 | position: relative 26 | 27 | padding: 0 1em 28 | height: 2em 29 | 30 | font-size: $-melon-medium-font-size 31 | line-height: 2em 32 | text-transform: uppercase 33 | 34 | text-align: center 35 | 36 | outline: 0 37 | vertical-align: middle 38 | 39 | 40 | border-radius: 2px 41 | border: none 42 | 43 | cursor: pointer 44 | user-select: none 45 | 46 | transition: all .3s 47 | 48 | // Gets rid of tap active state 49 | -webkit-tap-highlight-color: transparent 50 | 51 | &:disabled 52 | cursor: not-allowed 53 | color: rgba(#000, .26) !important 54 | background-color: $-melon-colors.white !important 55 | 56 | melon-button-swatch($-melon-button-default) 57 | 58 | for $color-theme, $value in $-melon-button-color-themes 59 | &.variant-{$color-theme} 60 | melon-button-swatch($value) 61 | &.variant-raised, 62 | &.variant-floating 63 | melon-button-swatch($value, true) 64 | 65 | &.variant-raised 66 | {$-melon-button-raised} 67 | &:disabled 68 | color: rgba(#000, .26) !important 69 | background-color: $-melon-colors.grey200 !important 70 | 71 | &.variant-floating 72 | {$-melon-button-floating} 73 | {$-melon-button-raised} 74 | &:disabled 75 | color: rgba(#000, .26) !important 76 | background-color: $-melon-colors.grey200 !important 77 | 78 | >.{$-melon-class-prefix}-touch-ripple 79 | border-radius: 50% 80 | 81 | >.{$-melon-class-prefix}-icon:first-child 82 | 83 | display: inline-block 84 | width: 2em 85 | height: 2em 86 | font-size: 1em 87 | line-height: 2em 88 | vertical-align: top 89 | margin-left: -1em 90 | 91 | &.variant-icon 92 | padding: 0 93 | min-width: 2em 94 | 95 | >.{$-melon-class-prefix}-icon 96 | margin-left: 0 97 | 98 | &.variant-ripple 99 | position: relative 100 | -------------------------------------------------------------------------------- /src/css/ButtonGroup.styl: -------------------------------------------------------------------------------- 1 | .{$-melon-class-prefix}-buttongroup 2 | display inline-block 3 | 4 | >.{$-melon-class-prefix}-button 5 | 6 | border-radius: 0 !important 7 | box-shadow: none !important 8 | border-left 1px solid #fff 9 | 10 | &:first-child 11 | border-bottom-left-radius: 2px !important 12 | border-top-left-radius: 2px !important 13 | border-left 0 14 | 15 | &:last-child 16 | border-top-right-radius: 2px !important 17 | border-bottom-right-radius: 2px !important 18 | -------------------------------------------------------------------------------- /src/css/Card.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | 3 | .{$-melon-class-prefix}-card 4 | box-sizing: border-box 5 | box-shadow: melon-get-z-depth('1') 6 | border-radius: 2px 7 | background-color: #fff 8 | 9 | &-title 10 | box-sizing: border-box 11 | padding: 1.5em 1em 1em 12 | 13 | &-content 14 | box-sizing: border-box 15 | padding: 0 1em 1em 16 | 17 | &-actions 18 | box-sizing: border-box 19 | padding: 0.5em 20 | -------------------------------------------------------------------------------- /src/css/Chip.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | 3 | .{$-melon-class-prefix}-chip 4 | display: inline-block 5 | color: $-melon-chip.color 6 | background-color: $-melon-chip.background-color 7 | line-height: 32px 8 | white-space: nowrap 9 | border-radius: 16px / 50% 10 | overflow: hidden 11 | &.state-active 12 | {$-melon-chip-shadow} 13 | cursor: pointer 14 | &:hover 15 | color: $-melon-chip.color-hover 16 | background-color: $-melon-chip.background-color-hover 17 | 18 | &-avatar 19 | display: table-cell 20 | vertical-align: middle 21 | width: 32px 22 | height: 32px 23 | border-radius: 50% 24 | &-label 25 | display: table-cell 26 | padding-left: 8px 27 | padding-right: 12px 28 | vertical-align: middle 29 | &-icon 30 | display: table-cell 31 | vertical-align: middle 32 | padding-left: 8px 33 | padding-right: 8px 34 | .{$-melon-class-prefix}-icon 35 | display: table-cell 36 | font-size: inherit 37 | color: $-melon-chip.icon.color 38 | background-color: $-melon-chip.icon.background-color 39 | border-radius: 50% 40 | &:hover 41 | color: $-melon-chip.icon.color-hover 42 | background-color: $-melon-chip.icon.background-color-hover 43 | 44 | -------------------------------------------------------------------------------- /src/css/Dialog.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | @require "nib" 3 | @require "./Layer.styl" 4 | 5 | .{$-melon-class-prefix}-dialog 6 | 7 | position: fixed 8 | box-sizing: border-box 9 | -webkit-tap-highlight-color: $-melon-colors.transparent 10 | z-index: $-melon-z-index.dialog 11 | top: 0 12 | left: 0 13 | width: 100% 14 | height: 100% 15 | overflow: auto 16 | opacity: 0 17 | visibility: hidden 18 | transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms 19 | 20 | &.state-open 21 | 22 | opacity: 1 23 | visibility: visible 24 | 25 | &-window 26 | box-sizing: border-box 27 | -webkit-tap-highlight-color: $-melon-colors.transparent 28 | position: absolute 29 | width: 75% 30 | max-width: 768px 31 | top: 50% 32 | left: 50% 33 | z-index: $-melon-z-index.dialog 34 | background-color: $-melon-colors.white 35 | box-shadow: 0 14px 45px rgba(0, 0, 0, 0.25), 0 10px 18px rgba(0, 0, 0, 0.22) 36 | border-radius: 2px 37 | padding: 1.5em 1.5em 0 1.5em 38 | margin-bottom: 16px 39 | 40 | &-window.variant-no-title &-body 41 | margin: 0 0 1.5em 42 | 43 | 44 | .variant-adaptive 45 | width: auto 46 | display: inline-block 47 | 48 | &-title 49 | font-size: $-melon-xx-large-font-size 50 | line-height: 2 51 | margin: -0.5em 0 0 52 | 53 | &-body 54 | margin: 1.25em 0 1.5em 55 | 56 | &-actions 57 | 58 | margin-right: -1em 59 | padding: 0.5em 0 60 | text-align: right 61 | clearfix() 62 | 63 | >.{$-melon-class-prefix}-button 64 | float: right 65 | margin-left: 0.5em 66 | -------------------------------------------------------------------------------- /src/css/Divider.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 分隔线样式 3 | * @author leon 4 | */ 5 | 6 | @require "./base.styl" 7 | 8 | .{$-melon-class-prefix}-divider 9 | border-style: solid 10 | border-color: rgba(0, 0, 0, .12) 11 | border-width: 0 0 1px 0 12 | -webkit-margin-before: 0 13 | -webkit-margin-after: 0 14 | margin: .5em 0 15 | -------------------------------------------------------------------------------- /src/css/Drawer.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | @require './Mask.styl' 3 | 4 | .{$-melon-class-prefix}-drawer 5 | 6 | &-window 7 | position: fixed 8 | background-color: #fff 9 | box-shadow: melon-get-z-depth('2') 10 | z-index: $-melon-z-index.drawer 11 | -------------------------------------------------------------------------------- /src/css/IconMenu.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | @require './Icon.styl' 3 | @require './Layer.styl' 4 | @require './Button.styl' 5 | @require './Menu.styl' 6 | 7 | .{$-melon-class-prefix}-icon-menu 8 | 9 | &-popup 10 | position: absolute; 11 | transform-origin: left top 0; 12 | z-index: $-melon-z-index.popover 13 | -------------------------------------------------------------------------------- /src/css/Layer.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | 3 | .ui-layer 4 | 5 | &-mask 6 | position: fixed 7 | top: 0 8 | left: 0 9 | right: 0 10 | bottom: 0 11 | z-index: $-melon-z-index.layer 12 | -------------------------------------------------------------------------------- /src/css/Link.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | @require "./Button.styl" 3 | 4 | .{$-melon-class-prefix}-link 5 | color: $-melon-link.color 6 | text-decoration: none 7 | 8 | &:hover 9 | color: $-melon-link.states.hover.color 10 | 11 | &.variant-button 12 | 13 | display: inline-block 14 | box-sizing: border-box 15 | position: relative 16 | 17 | padding: 0 1em 18 | height: 2em 19 | 20 | font-size: $-melon-medium-font-size 21 | line-height: 2em 22 | text-transform: uppercase 23 | 24 | text-align: center 25 | 26 | outline: 0 27 | vertical-align: middle 28 | 29 | 30 | border-radius: 2px 31 | border: none 32 | 33 | cursor: pointer 34 | user-select: none 35 | 36 | transition: all .3s 37 | 38 | // Gets rid of tap active state 39 | -webkit-tap-highlight-color: transparent 40 | 41 | &:disabled 42 | cursor: not-allowed 43 | color: rgba(#000, .26) !important 44 | background-color: $-melon-colors.white !important 45 | 46 | melon-button-swatch($-melon-button-default) 47 | 48 | for $color-theme, $value in $-melon-button-color-themes 49 | &.variant-{$color-theme} 50 | melon-button-swatch($value) 51 | &.variant-raised 52 | melon-button-swatch($value, true) 53 | 54 | &.variant-raised 55 | {$-melon-button-raised} 56 | &:disabled 57 | color: rgba(#000, .26) !important 58 | background-color: $-melon-colors.grey200 !important 59 | 60 | .{$-melon-class-prefix}-icon 61 | display: inline-block 62 | margin-right: 0.5em 63 | font-size: 1em 64 | line-height: 2em 65 | vertical-align: top 66 | 67 | & ~ span 68 | display: inline-block 69 | vertical-align: top 70 | -------------------------------------------------------------------------------- /src/css/Mask.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | @require "nib" 3 | 4 | .{$-melon-class-prefix}-mask 5 | 6 | position: fixed 7 | height: 100% 8 | width: 100% 9 | z-index: $-melon-z-index.dialog - 1 10 | top: 0 11 | left: -100% 12 | opacity: 0 13 | -webkit-tap-highlight-color: $-melon-colors.transparent 14 | will-change: opacity 15 | transform: translateZ(0) 16 | transition: left 0ms cubic-bezier(0.23, 1, 0.32, 1) 400ms, opacity 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms 17 | background-color: $-melon-colors.lightBlack 18 | 19 | &.state-show 20 | left: 0 21 | opacity: 1 22 | will-change: opacity 23 | transition: left 0ms cubic-bezier(0.23, 1, 0.32, 1) 0ms, opacity 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms 24 | -------------------------------------------------------------------------------- /src/css/Menu.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | @require './Icon.styl' 3 | @require './Button.styl' 4 | @require './Divider.styl' 5 | 6 | 7 | .{$-melon-class-prefix}-menu 8 | position: relative; 9 | 10 | &.variant-level-0 11 | padding: 0.5em 0 12 | 13 | &-cascading-icon 14 | position: absolute; 15 | top: 4px 16 | right: 0 17 | transform: rotate(270deg) 18 | color: rgba(0, 0, 0, .54) 19 | 20 | &-children-popover 21 | position: absolute; 22 | top: 0 23 | left: 101% 24 | padding: .5em 0 25 | border-radius: 2px 26 | background: #fff 27 | box-shadow: melon-get-z-depth('2') 28 | transform-origin: top left 29 | 30 | for $level in (0..10) 31 | &.variant-level-{$level} &-children-popover 32 | z-index: $-melon-z-index.popover + $level 33 | 34 | 35 | .{$-melon-class-prefix}-menu-item 36 | display: block 37 | position: relative; 38 | box-sizing: border-box; 39 | width: 100% 40 | height: 3em 41 | line-height: 3em 42 | padding: 0 1em 43 | text-align: left 44 | cursor: pointer 45 | color: rgba(0, 0, 0, 0.870588) 46 | white-space: nowrap; 47 | 48 | &.variant-cascading 49 | height: 2em 50 | line-height: 2em 51 | padding: 0 1.5em 52 | 53 | &:hover 54 | background-color: $-melon-colors.grey200 55 | 56 | &-label 57 | display: inline-block; 58 | height: 100% 59 | font-size: 15px 60 | vertical-align: middle; 61 | 62 | &:before 63 | content: '' 64 | display: inline-block; 65 | height: 100% 66 | width: 0 67 | vertical-align: middle; 68 | 69 | &.state-disabled 70 | color: $-melon-colors.grey400 71 | cursor: not-allowed; 72 | 73 | &:hover 74 | background: #fff 75 | 76 | &-icon-placeholder, 77 | &-left-icon 78 | display: inline-block; 79 | font-size: 18px 80 | width: 24px 81 | height: @width 82 | line-height: @width 83 | margin-right: 16px 84 | text-align: left 85 | vertical-align: middle; 86 | -------------------------------------------------------------------------------- /src/css/Navigation.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Navigation 样式 3 | * @author leon 4 | */ 5 | 6 | @require "nib" 7 | @require "./base.styl" 8 | @require "./Zippy.styl" 9 | 10 | .{$-melon-class-prefix}-navigation 11 | 12 | &-touchable 13 | display: block; 14 | position: relative; 15 | box-sizing: border-box; 16 | width: 100% 17 | color: rgba(0, 0, 0, .87) 18 | height: 3em 19 | line-height: 3em 20 | text-align: left 21 | text-decoration: none 22 | padding-left: 1em 23 | cursor: pointer 24 | font-size: 1em 25 | 26 | &:hover 27 | color: $-melon-colors.blue400 28 | 29 | &-label 30 | font-size: 0.875em 31 | 32 | for $level in (1..5) 33 | &.variant-level-{$level} &-touchable 34 | padding-left: unit($level * 2 - 1, 'em') 35 | 36 | .{$-melon-class-prefix}-navigation-item 37 | 38 | for $level in (1..5) 39 | &.variant-level-{$level} &-touchable 40 | padding-left: unit($level * 2 + 1, 'em') 41 | 42 | &-label 43 | font-size: 0.875em 44 | vertical-align: middle !important; 45 | 46 | &-touchable 47 | display: block; 48 | position: relative; 49 | box-sizing: border-box; 50 | width: 100% 51 | color: rgba(0, 0, 0, .87) 52 | height: 3em 53 | text-align: left 54 | text-decoration: none 55 | padding-left: 1em 56 | cursor: pointer 57 | 58 | &:visited 59 | color: rgba(0, 0, 0, .87) 60 | 61 | &:before 62 | display: inline-block; 63 | content: '' 64 | height: 100% 65 | width: 0 66 | vertical-align: middle; 67 | 68 | >.ui-icon 69 | font-size: 1.6em 70 | vertical-align: middle; 71 | color: rgba(0, 0, 0, .54) 72 | line-height: 1 73 | margin-right: 0.625em 74 | 75 | 76 | &.state-active &-touchable, 77 | &:hover &-touchable 78 | 79 | color: $-melon-colors.blue400 80 | 81 | >.ui-icon 82 | color: $-melon-colors.blue400 83 | 84 | .ui-title.variant-navigation-header 85 | color: rgba(0, 0, 0, .54) 86 | font-size: 14px 87 | padding: 0 16px 88 | margin: 0 89 | line-height: 48px 90 | 91 | .ui-button.variant-navigation 92 | width: 100% 93 | height: 3em 94 | text-align: left 95 | -------------------------------------------------------------------------------- /src/css/Pager.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | 3 | $-melon-pager-states = { 4 | 5 | disabled: { 6 | cursor: not-allowed 7 | color: $-melon-pager.item.states.disabled.color 8 | 9 | '&:hover': { 10 | background-color: $-melon-pager.item.states.disabledHover.color 11 | border-radius: 0 12 | } 13 | }, 14 | 15 | current: { 16 | background-color: $-melon-pager.item.states.current.background-color 17 | color: $-melon-pager.item.states.current.color 18 | border-radius: 2px 19 | }, 20 | 21 | prev: { 22 | width: auto 23 | font-size: 1.5em 24 | height: 1em 25 | line-height: 1em 26 | }, 27 | 28 | next: { 29 | width: auto 30 | font-size: 1.5em 31 | height: 1em 32 | line-height: 1em 33 | }, 34 | 35 | ellipsis: { 36 | width: auto 37 | font-size: 1.2em 38 | height: 1.5em 39 | line-height: 1.5em 40 | width: 1.5em 41 | } 42 | } 43 | 44 | 45 | .{$-melon-class-prefix}-pager 46 | 47 | display: block 48 | 49 | list-style: none 50 | 51 | outline: 0 52 | vertical-align: middle 53 | 54 | border: none 55 | 56 | // Gets rid of tap active state 57 | -webkit-tap-highlight-color: transparent 58 | 59 | font-size: $-melon-pager.font-size 60 | 61 | &.variant-lang .{$-melon-class-prefix}-pager-item 62 | &.state-prev, 63 | &.state-next, 64 | &.state-ellipsis 65 | font-size: 1em 66 | padding: 0 0.3em 67 | height: 2em 68 | line-height: 2em 69 | 70 | &-item 71 | 72 | display: inline-block 73 | 74 | padding: 0 75 | margin: 0 0.2em 76 | 77 | width: 2em 78 | height: 2em 79 | line-height: 2em 80 | 81 | 82 | outline: 0 83 | vertical-align: middle 84 | text-align: center 85 | 86 | border: none 87 | color: $-melon-pager.item.color 88 | 89 | cursor: pointer 90 | 91 | // Gets rid of tap active state 92 | -webkit-tap-highlight-color: transparent 93 | 94 | &:hover 95 | background-color: $-melon-pager.item.states.hover.background-color 96 | border-radius: 2px 97 | 98 | for $state, $hashes in $-melon-pager-states 99 | &.state-{$state} 100 | {$hashes} 101 | 102 | -------------------------------------------------------------------------------- /src/css/Popover.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Popup 样式 3 | * @author leon 4 | */ 5 | 6 | @require "nib" 7 | @require "./base.styl" 8 | @require "./Layer.styl" 9 | 10 | .{$-melon-class-prefix}-popover-layer 11 | position: absolute 12 | text-align: left 13 | background-color: #fff 14 | box-shadow: melon-get-z-depth('2') 15 | border-radius: 2px 16 | z-index: $-melon-z-index.popover 17 | -------------------------------------------------------------------------------- /src/css/Progress.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | 3 | 4 | .{$-melon-class-prefix}-progress.variant-linear 5 | 6 | position: relative 7 | height: 0.33em 8 | display: block 9 | width: 100% 10 | background-color: $-melon-colors.cyan100 11 | border-radius: 0.167em 12 | margin: 0 13 | overflow: hidden 14 | font-size: $-melon-medium-font-size 15 | 16 | &.variant-determinate .{$-melon-class-prefix}-progress-bar 17 | 18 | height: 100% 19 | transition: width 0.3s linear 0ms 20 | background-color: $-melon-colors.cyan500 21 | 22 | &.variant-indeterminate .{$-melon-class-prefix}-progress-bar 23 | 24 | height: 100% 25 | overflow: hidden 26 | 27 | .{$-melon-class-prefix}-progress-bar1, 28 | .{$-melon-class-prefix}-progress-bar2 29 | 30 | position: absolute 31 | background-color: $-melon-colors.cyan500 32 | top: 0 33 | left: 0 34 | bottom: 0 35 | 36 | .{$-melon-class-prefix}-progress-bar1 37 | 38 | transition: all 840ms cubic-bezier(0.650, 0.815, 0.735, 0.395) 39 | 40 | .{$-melon-class-prefix}-progress-bar2 41 | 42 | transition: all 840ms cubic-bezier(0.165, 0.840, 0.440, 1.000) 43 | 44 | 45 | .{$-melon-class-prefix}-progress.variant-circle 46 | 47 | position: relative 48 | display: inline-block 49 | width: 2em 50 | height: 2em 51 | font-size: $-melon-medium-font-size 52 | 53 | .{$-melon-class-prefix}-progress 54 | 55 | &-wapper 56 | width: 100% 57 | height: 100% 58 | display: inline-block 59 | transition: 20s transform linear 0ms 60 | 61 | &-svg 62 | width: 100% 63 | height: 100% 64 | position: relative 65 | 66 | &-path 67 | 68 | stroke: $-melon-colors.cyan500 69 | stroke-dashoffset: 0 70 | stroke-linecap: round 71 | transition: all 0.3s linear 0ms 72 | -------------------------------------------------------------------------------- /src/css/Region.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | 3 | 4 | .{$-melon-class-prefix}-region 5 | display: block 6 | 7 | &-selector 8 | cursor: pointer 9 | 10 | span 11 | vertical-align: middle 12 | 13 | .{$-melon-class-prefix}-icon 14 | margin-right: 0.5em 15 | vertical-align: middle 16 | transition: color .1s 17 | font-size: 1.2em 18 | 19 | &.state-checked .{$-melon-class-prefix}-icon 20 | color: $-melon-colors.blue500 21 | 22 | &-country 23 | h1 24 | background-color: $-melon-colors.grey300 25 | padding: 0.8em 1em 26 | line-height: 1em 27 | &-area 28 | line-height: 2.6em 29 | display: table 30 | width: 100% 31 | 32 | &.variant-even 33 | background-color: $-melon-colors.grey50 34 | 35 | &-selector 36 | display: table-cell 37 | padding-left: 1em 38 | width: 10em 39 | 40 | &-content 41 | display: table-cell 42 | padding-right: 1em 43 | clearfix() 44 | 45 | &-province 46 | float: left 47 | width: 7.5em 48 | position: relative 49 | padding-left: 0.5em 50 | white-space: nowrap 51 | 52 | &.state-expand 53 | background-color: $-melon-colors.blue50 54 | box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.1) 55 | 56 | .{$-melon-class-prefix}-region-selector 57 | position: relative 58 | z-index: 0 59 | 60 | &-popup 61 | visibility: hidden 62 | opacity: 0 63 | position: absolute 64 | left: 8em 65 | top: 0 66 | background-color: $-melon-colors.blue50 67 | z-index: 1 68 | box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.1) 69 | 70 | ul 71 | padding: 0 0.5em 72 | width: 12em 73 | clearfix() 74 | 75 | &.state-expand &-popup 76 | visibility: visible 77 | opacity: 1 78 | 79 | &-info 80 | display: inline-block 81 | margin-left: 4px 82 | font-size: 0.8em 83 | color: $-melon-colors.blue400 84 | 85 | &-city 86 | float: left 87 | min-width: 6em 88 | line-height: 2.6em 89 | -------------------------------------------------------------------------------- /src/css/Ripple.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | 3 | .{$-melon-class-prefix}-touch-ripple 4 | height: 100% 5 | width: 100% 6 | position: absolute 7 | top: 0 8 | left: 0 9 | overflow: hidden 10 | z-index: 1 11 | 12 | &-circle 13 | background-color: $-melon-colors.darkBlack 14 | position: absolute 15 | top: 0 16 | left: 0 17 | border-radius: 100em 18 | 19 | .{$-melon-class-prefix}-center-ripple 20 | position: absolute 21 | top: 0 22 | left: 0 23 | height: 100% 24 | width: 100% 25 | 26 | &-circle 27 | width: 100% 28 | height: 100% 29 | background-color: $-melon-colors.darkBlack 30 | position: absolute 31 | border-radius: 100em 32 | top: 0 33 | left: 0 34 | -------------------------------------------------------------------------------- /src/css/ScrollView.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | 3 | $-melon-scrollview-width = 100% 4 | $-melon-scrollview-height = 300px 5 | $-melon-scrollview-thumb-size = 8px 6 | $-melon-scrollview-thumb-color = $-melon-colors.minBlack; 7 | $-melon-scrollview-color = transparent 8 | $-melon-scrollview-border = 1px solid $-melon-colors.minBlack 9 | 10 | .{$-melon-class-prefix}-noselect 11 | user-select: none 12 | cursor: default 13 | 14 | .{$-melon-class-prefix}-scrollview 15 | 16 | position: relative 17 | display: block 18 | overflow: hidden 19 | width: $-melon-scrollview-width 20 | height: $-melon-scrollview-height 21 | 22 | &-main 23 | position: absolute 24 | left: 0 25 | top: 0 26 | overflow: visible 27 | box-sizing: border-box 28 | 29 | &.variant-vertical.variant-horizontal &-bar.variant-vertical 30 | height: calc(100% - 12px) 31 | 32 | &-bar 33 | position: absolute 34 | background-color: $-melon-scrollview-color 35 | padding: 2px 36 | z-index: 1 37 | transition: all .5s cubic-bezier(0.23, 1, 0.32, 1) 38 | 39 | &:hover, 40 | &.state-show 41 | background-color: rgba($-melon-colors.brown50, 0.5) 42 | 43 | &:hover > &-thumb, 44 | &.state-show > &-thumb 45 | background-color: $-melon-colors.lightBlack 46 | 47 | &.variant-vertical 48 | width: $-melon-scrollview-thumb-size 49 | height: 100% 50 | top: 0 51 | right: 0 52 | 53 | &.variant-horizontal 54 | width: 100% 55 | height: $-melon-scrollview-thumb-size 56 | left: 0 57 | bottom: 0 58 | 59 | &-thumb 60 | background-color: $-melon-scrollview-thumb-color 61 | border-radius: 4px 62 | cursor: default 63 | position: absolute 64 | 65 | &.variant-vertical > &-thumb 66 | width: $-melon-scrollview-thumb-size 67 | height: $-melon-scrollview-thumb-size * 3 68 | top: 0 69 | right: 2px 70 | 71 | &.variant-horizontal > &-thumb 72 | width: $-melon-scrollview-thumb-size * 3 73 | height: $-melon-scrollview-thumb-size 74 | left: 0 75 | bottom: 2px 76 | -------------------------------------------------------------------------------- /src/css/Select.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | @require './Icon.styl' 3 | @require './Popover.styl' 4 | 5 | .{$-melon-class-prefix}-select 6 | position: relative 7 | display: inline-block 8 | box-sizing: border-box 9 | padding: 0.5em 0 10 | padding-right: $-melon-medium-font-size 11 | 12 | min-width: 5em 13 | 14 | line-height: 1.5em 15 | font-size: $-melon-medium-font-size 16 | 17 | border-bottom: 1px solid $-melon-colors.grey300 18 | 19 | &-label 20 | display inline-block 21 | width: 100% 22 | font-size: 1em 23 | text-align: left 24 | cursor: pointer 25 | vertical-align: middle 26 | transition border-color 1s 27 | text-overflow: ellipsis; 28 | overflow: hidden; 29 | white-space: nowrap; 30 | 31 | &-placeholder 32 | color: $-melon-colors.grey500 33 | 34 | > .ui-icon 35 | display: inline-block 36 | font-size: $-melon-medium-font-size 37 | width: @font-size 38 | vertical-align middle 39 | margin-right: -@width 40 | 41 | &-popup 42 | padding: 1em 0 43 | 44 | &-option-group 45 | 46 | &-title 47 | box-sizing: border-box 48 | width: 100% 49 | line-height: 2em 50 | padding: 0 1em 51 | cursor: default 52 | color: $-melon-colors.grey700 53 | 54 | &-list .{$-melon-class-prefix}-select-option 55 | padding-left: 2em 56 | 57 | &-option 58 | box-sizing: border-box 59 | width: 100% 60 | line-height: 2em 61 | padding: 0 1em 62 | cursor: pointer 63 | 64 | &:hover 65 | background-color: $-melon-colors.grey300 66 | 67 | &.state-selected 68 | color: $-melon-colors.teal500 69 | 70 | &.state-disabled 71 | color: $-melon-colors.grey400 72 | cursor: not-allowed 73 | &:hover 74 | background-color: $-melon-colors.white 75 | 76 | &.state-invalid 77 | border-bottom-color: $-melon-colors.red500 78 | 79 | &.state-read-only &-label 80 | cursor: default; 81 | 82 | &.state-disabled &-label 83 | cursor: not-allowed; 84 | -------------------------------------------------------------------------------- /src/css/Slider.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | 3 | .{$-melon-class-prefix}-slider 4 | margin: 1em 0 5 | height: 2em 6 | position: relative 7 | user-select: none 8 | cursor: pointer 9 | 10 | 11 | .{$-melon-class-prefix}-slider-bar 12 | width: 100% 13 | background-color: $-melon-colors.grey200 14 | position: absolute 15 | top: 50% 16 | left: 0 17 | box-sizing: content-box 18 | cursor: pointer 19 | 20 | &-wrapper 21 | height: 100% 22 | position: relative 23 | 24 | &.state-active 25 | .{$-melon-class-prefix}-slider-bar-pointer 26 | transform: translate(-50%, 0) scale(1.6) !important 27 | .{$-melon-class-prefix}-slider-bar-pointer-outer 28 | transform: translate(-50%, 0) scale(0) !important 29 | 30 | &:hover 31 | .{$-melon-class-prefix}-slider-bar-pointer-outer 32 | transform: translate(-50%, 0) scale(1) 33 | 34 | &-active 35 | background-color: $-melon-colors.cyan500 36 | height: 100% 37 | position: absolute 38 | left: 0 39 | top: 0 40 | bottom: 0 41 | 42 | &-pointer 43 | size(0.8em) 44 | border-radius: 100em 45 | background-color: $-melon-colors.cyan500 46 | position: absolute 47 | top: 50% 48 | margin-top: -0.4em 49 | transform: translate(-50%, 0) 50 | z-index: 1 51 | transform-origin: 50% 50% 52 | transition: transform 0.3s ease 53 | 54 | &-outer 55 | size(1.6em) 56 | border-radius: 100em 57 | background-color: $-melon-colors.cyan100 58 | position: absolute 59 | opacity: 0.5 60 | top: 50% 61 | margin-top: -0.8em 62 | transform: translate(-50%, 0) scale(0) 63 | transition: transform 0.1s ease-out 64 | z-index: 0 65 | 66 | -------------------------------------------------------------------------------- /src/css/SnackBar.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | @require './Button.styl' 3 | 4 | 5 | $-melon-snackbar-variants = { 6 | 'bl': { 7 | bottom: -10px 8 | margin-left: 24px 9 | left: 0 10 | }, 11 | 'br': { 12 | bottom: -10px 13 | margin-right: 24px 14 | right: 0 15 | }, 16 | 'bc': { 17 | bottom: -10px 18 | left: 50% 19 | }, 20 | 'tl': { 21 | top: -10px 22 | margin-left: 24px 23 | left: 0 24 | }, 25 | 'tr': { 26 | top: -10px 27 | margin-right: 24px 28 | right: 0 29 | }, 30 | 'tc': { 31 | top: -10px 32 | left: 50% 33 | }, 34 | 'lt': { 35 | top: 24px 36 | left: 0 37 | }, 38 | 'lc': { 39 | top: 50% 40 | left: 0 41 | }, 42 | 'lb': { 43 | bottom: 24px 44 | left: 0 45 | }, 46 | 'rt': { 47 | top: 24px 48 | right: 0 49 | }, 50 | 'rc': { 51 | top: 50% 52 | right: 0 53 | }, 54 | 'rb': { 55 | bottom: 24px 56 | right: 0 57 | } 58 | } 59 | 60 | 61 | .{$-melon-class-prefix}-snack-bar 62 | color: $-melon-colors.white 63 | border-radius: 2px 64 | padding: 0.75em 0.5em 0.75em 1em 65 | line-height: 2em 66 | min-width: 288px 67 | max-width: 568px 68 | position: fixed 69 | z-index: $-melon-z-index.snackbar 70 | opacity: 0 71 | visibility: hidden 72 | transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms, bottom 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms, top 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms, right 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms, left 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms, visibility 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms 73 | background-color: $-melon-colors.darkBlack 74 | clearfix() 75 | 76 | &.state-open 77 | opacity: 1 78 | visibility: visible 79 | &.{$-melon-variant-prefix}-direction-bl, 80 | &.{$-melon-variant-prefix}-direction-bc, 81 | &.{$-melon-variant-prefix}-direction-br 82 | bottom: 24px 83 | &.{$-melon-variant-prefix}-direction-tl, 84 | &.{$-melon-variant-prefix}-direction-tc, 85 | &.{$-melon-variant-prefix}-direction-tr 86 | top: 24px 87 | &.{$-melon-variant-prefix}-direction-lt, 88 | &.{$-melon-variant-prefix}-direction-lc, 89 | &.{$-melon-variant-prefix}-direction-lb 90 | left: 24px 91 | &.{$-melon-variant-prefix}-direction-rt, 92 | &.{$-melon-variant-prefix}-direction-rc, 93 | &.{$-melon-variant-prefix}-direction-rb 94 | right: 24px 95 | 96 | &-message 97 | display: inline-block 98 | word-break: break-word 99 | 100 | &-action.{$-melon-class-prefix}-button 101 | color: $-melon-colors.pinkA200 102 | float: right 103 | 104 | &:hover, 105 | &:active 106 | color: $-melon-colors.pinkA200 107 | background-color: $-melon-colors.transparent 108 | 109 | for $variant, $hashes in $-melon-snackbar-variants 110 | &.{$-melon-variant-prefix}-direction-{$variant} 111 | {$hashes} 112 | -------------------------------------------------------------------------------- /src/css/Tabs.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | 3 | .{$-melon-class-prefix}-tabs 4 | 5 | display: block 6 | margin: 0 7 | padding: 0 8 | font-size: $-melon-tabs.font-size 9 | 10 | ul 11 | position: relative 12 | width: 100% 13 | 14 | background-color: $-melon-tabs.background-color 15 | white-space: nowrap 16 | 17 | list-style: none 18 | 19 | 20 | .{$-melon-class-prefix}-tabs-item 21 | 22 | display: inline-block 23 | cursor: pointer 24 | padding: 0.48em 0 25 | height: 2em 26 | line-height: 2em 27 | text-align: center 28 | color: $-melon-tabs.item.color 29 | 30 | &.state-selected 31 | color: $-melon-tabs.item.states.selected.color 32 | background-color: $-melon-tabs.item.states.selected.background-color 33 | 34 | &.state-disabled 35 | cursor: not-allowed 36 | color: $-melon-tabs.item.states.disabled.color 37 | 38 | 39 | .{$-melon-class-prefix}-tabs-panel 40 | display: none; 41 | &.state-active 42 | display: block!important 43 | 44 | .{$-melon-class-prefix}-tabs-inkbar 45 | 46 | display: inline-block 47 | 48 | position: absolute 49 | bottom: 0 50 | left: 0 51 | 52 | height: 2px 53 | background-color: $-melon-tabs.inkbar.background-color 54 | 55 | transition: left .8s cubic-bezier(0.23, 1, 0.32, 1) 0ms 56 | -------------------------------------------------------------------------------- /src/css/TextBox.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | @require "nib" 3 | 4 | .{$-melon-class-prefix}-text-box 5 | 6 | position: relative 7 | display: inline-block 8 | min-width: 5em 9 | padding: 1em 0 0.5em 10 | vertical-align: middle 11 | border-bottom: 1px solid $-melon-colors.grey300 12 | transition border-color 1s 13 | box-sizing: border-box 14 | 15 | &.variant-fluid &-input 16 | width: 100% 17 | 18 | &.state-focus 19 | border-bottom-color: $-melon-colors.blue500 20 | 21 | &.state-invalid 22 | border-bottom-color: $-melon-colors.red500 23 | 24 | &.state-disabled 25 | border-bottom-style: dotted 26 | .{$-melon-class-prefix}-textbox-input 27 | color: rgba($-melon-colors.black, 0.3) 28 | cursor not-allowed 29 | 30 | &-input 31 | display: block 32 | position: relative 33 | box-sizing border-box 34 | padding: 0 35 | height: 100% 36 | line-height: 1.5em 37 | z-index: 3 38 | width: 100% 39 | 40 | resize: none 41 | overflow: hidden 42 | 43 | font-size: inherit 44 | font-family: $-melon-font-family 45 | color: rgba($-melon-colors.black, 0.87) 46 | 47 | background-color transparent 48 | border: none 49 | outline: none 50 | 51 | transition height 1s 52 | 53 | &[readonly] 54 | color: rgba($-melon-colors.black, 0.4) 55 | &:focus 56 | border-bottom-color: $-melon-colors.grey300 57 | 58 | &::-webkit-input-placeholder 59 | color: $-melon-colors.grey500 60 | 61 | &:-moz-placeholder 62 | color: $-melon-colors.grey500 63 | 64 | &::-moz-placeholder 65 | color: $-melon-colors.grey500 66 | 67 | &:-ms-input-placeholder 68 | color: $-melon-colors.grey500 69 | 70 | &-floating-label 71 | position: absolute 72 | box-sizing: border-box 73 | left: 0 74 | top: 1em 75 | width: 100% 76 | line-height: 1.5em 77 | font-size: 1em 78 | color: $-melon-colors.grey500 79 | transform-origin: left top 0px 80 | white-space: nowrap 81 | transition: all .5s 82 | 83 | &.{$-melon-state-prefix}-floating 84 | transform: scale(0.8) translateY(-1.5em) 85 | 86 | &.{$-melon-state-prefix}-focus 87 | color: $-melon-colors.blue500 88 | 89 | &-prefix, 90 | &-suffix 91 | box-sizing: border-box 92 | display: inline-block 93 | padding: 1em 0 0.5em 94 | height: 2.5em 95 | line-height: 1em 96 | font-size: 1em 97 | color: $-melon-colors.grey600 98 | vertical-align: middle 99 | 100 | &-prefix 101 | margin-right: 0.5em 102 | 103 | &-suffix 104 | margin-left: 0.5em 105 | 106 | &-validate-message 107 | position: absolute 108 | left: 0 109 | top: 100% 110 | width: 100% 111 | font-size: 14px 112 | line-height: 24px 113 | 114 | &.state-invalid &-validate-message 115 | color: $-melon-colors.red500 116 | -------------------------------------------------------------------------------- /src/css/Title.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | 3 | $-melon-title = { 4 | margin: 0.5em 0 0.4em 5 | line-height: 110% 6 | } 7 | 8 | $-melon-title-levels = { 9 | 10 | '1': $-melon-xxxx-large-font-size, 11 | '2': $-melon-xx-large-font-size, 12 | '3': $-melon-x-large-font-size, 13 | '4': $-melon-medium-font-size, 14 | '5': $-melon-x-small-font-size, 15 | '6': $-melon-xx-small-font-size 16 | 17 | } 18 | 19 | .{$-melon-class-prefix}-title 20 | {$-melon-title} 21 | 22 | for level, value in $-melon-title-levels 23 | h{level}.{$-melon-class-prefix}-title 24 | font-size: value 25 | -------------------------------------------------------------------------------- /src/css/Toggle.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | 3 | .{$-melon-class-prefix}-toggle 4 | 5 | display: inline-block 6 | position: relative 7 | cursor: pointer 8 | 9 | melon-clearfix() 10 | 11 | &.state-disabled 12 | 13 | cursor: not-allowed 14 | 15 | .{$-melon-class-prefix}-toggle-circle 16 | background-color: $-melon-toggle-circle-disabled.background-color 17 | 18 | .{$-melon-class-prefix}-toggle-bar 19 | background-color: $-melon-toggle-bar-disabled.background-color 20 | 21 | &.state-checked 22 | 23 | .{$-melon-class-prefix}-toggle-circle 24 | left: 45% 25 | background-color: $-melon-toggle-circle.background-color 26 | 27 | .{$-melon-class-prefix}-toggle-bar 28 | background-color: $-melon-toggle-bar.background-color 29 | 30 | 31 | > input 32 | display: none 33 | 34 | &-bar-container 35 | 36 | float: left 37 | position: relative 38 | display: inline-block 39 | transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms 40 | width: 2em 41 | padding: 0.2em 0px 0.2em 0.1em 42 | 43 | 44 | &-circle 45 | transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms 46 | box-sizing: border-box 47 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0) 48 | box-shadow: rgba(0, 0, 0, 0.117647) 0px 1px 6px 49 | border-radius: 50% 50 | position: absolute 51 | top: 0 52 | left: 0 53 | left: 0 54 | width: 1.2em 55 | height: 1.2em 56 | line-height: 2em 57 | background-color: $-melon-colors.white 58 | 59 | &-bar 60 | transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms 61 | width: 100% 62 | height: 0.8em 63 | border-radius: 30px 64 | background-color: rgba(0, 0, 0, 0.258824) 65 | -------------------------------------------------------------------------------- /src/css/ToolBar.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | 3 | .{$-melon-class-prefix}-tool-bar 4 | padding: 0 1em 5 | font-size: $-melon-x-large-font-size 6 | height: 3.2em 7 | background-color: $-melon-tool-bar.background-color 8 | 9 | >.{$-melon-class-prefix}-button 10 | &:first-child 11 | margin-left: -0.5em 12 | 13 | &:last-child 14 | margin-right: -0.5em 15 | -------------------------------------------------------------------------------- /src/css/Tooltip.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | 3 | .{$-melon-class-prefix}-tooltip 4 | 5 | display: inline-block 6 | 7 | &-popover 8 | padding: 0 0.5em 9 | font-size: $-melon-x-small-font-size 10 | line-height: 1.6em 11 | color: $-melon-colors.white 12 | background-color: rgba($-melon-colors.grey700, .9) 13 | z-index: 11 14 | border-radius: 2px 15 | transition: opacity .3s 16 | white-space: nowrap 17 | -------------------------------------------------------------------------------- /src/css/Tree.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | @require './Icon.styl' 3 | 4 | 5 | .{$-melon-class-prefix}-tree 6 | 7 | list-style: none 8 | 9 | .{$-melon-class-prefix}-icon 10 | 11 | font-size: 1.2em 12 | color: $-melon-colors.grey500 13 | position: absolute 14 | top: 0.25em 15 | left: 0.25em 16 | 17 | .{$-melon-class-prefix}-tree-node 18 | 19 | cursor: pointer 20 | line-height: 1em 21 | transition: all 0.8s linear 0ms 22 | position: relative 23 | 24 | &-label 25 | 26 | display: block 27 | margin: 0.5em 28 | padding: 0.5em 1.2em 29 | border-radius: 0.2em 30 | 31 | &:hover 32 | background-color: $-melon-colors.cyan100 33 | 34 | 35 | .{$-melon-class-prefix}-tree-node-root 36 | 37 | overflow: hidden 38 | transition: all .3s ease 0s 39 | height: 0 40 | 41 | &.state-expand 42 | 43 | height: auto 44 | overflow: auto 45 | 46 | .{$-melon-class-prefix}-tree-node.state-selected .{$-melon-class-prefix}-tree-node-label 47 | font-weight: 700 48 | color: $-melon-colors.white 49 | background-color: $-melon-colors.cyan200 50 | -------------------------------------------------------------------------------- /src/css/Uploader.styl: -------------------------------------------------------------------------------- 1 | @require "./base.styl" 2 | @require "./Button.styl" 3 | @require "./Icon.styl" 4 | @require "./Progress.styl" 5 | @require "./Link.styl" 6 | 7 | .{$-melon-class-prefix}-uploader 8 | position: relative 9 | display: inline-block 10 | min-width: 5em 11 | 12 | 13 | >input[type=file] 14 | display: none 15 | 16 | &-content 17 | border-bottom: 1px solid $-melon-colors.grey300 18 | height: 2em 19 | line-height: 2em 20 | padding-bottom: 3px 21 | cursor: pointer; 22 | padding: 0 2em 0 1.5em; 23 | clearfix(); 24 | 25 | >.ui-link, 26 | .{$-melon-class-prefix}-uploader-placeholder 27 | display: block; 28 | float: left; 29 | width: 100%; 30 | line-height: 2em; 31 | text-transform: none; 32 | vertical-align: middle; 33 | text-overflow: ellipsis; 34 | overflow: hidden; 35 | white-space: nowrap; 36 | 37 | >.ui-icon 38 | float: left; 39 | display: inline-block; 40 | line-height: 2em; 41 | height: 100%; 42 | vertical-align: middle; 43 | margin-left: -1.5em 44 | font-size: 1em 45 | 46 | >.ui-button 47 | float: left; 48 | margin-right: -2em; 49 | 50 | 51 | &-uploading 52 | height: 2em; 53 | 54 | &:before 55 | content: ''; 56 | display: inline-block 57 | vertical-align: middle; 58 | height: 100%; 59 | 60 | >span, 61 | >.ui-progress, 62 | >.ui-button 63 | color: #aaa 64 | display: inline-block 65 | vertical-align: middle; 66 | margin-right: 0.5em 67 | 68 | 69 | &-label 70 | vertical-align: middle 71 | margin-right: 1em 72 | 73 | &-preview 74 | float: left; 75 | display: inline-block; 76 | line-height: 2em 77 | margin: 0.5em 0 0.5em -1.5em 78 | width: 1em; 79 | height: 1em; 80 | border: 1px solid #ddd; 81 | vertical-align: middle; 82 | 83 | &-placeholder 84 | color: $-melon-colors.grey500 85 | -------------------------------------------------------------------------------- /src/css/Validity.styl: -------------------------------------------------------------------------------- 1 | @require 'nib' 2 | @require './base.styl' 3 | 4 | .{$-melon-class-prefix}-validity 5 | position: absolute 6 | display: none 7 | top: 100% 8 | left: 0 9 | font-size: $-melon-xx-small-font-size 10 | line-height: 2em 11 | white-space: nowrap 12 | 13 | &.{$-melon-state-prefix}-invalid 14 | display: block 15 | color: $-melon-colors.red500 16 | -------------------------------------------------------------------------------- /src/css/Zippy.styl: -------------------------------------------------------------------------------- 1 | @require './base.styl' 2 | 3 | .{$-melon-class-prefix}-zippy 4 | transition: transform 0.5s cubic-bezier(0.23, 1, 0.32, 1) 0ms, height 0ms linear 0.5s, width 0ms linear 0.5s 5 | transform: scale(1, 1) 6 | transform-origin: top left 7 | 8 | &.variant-vertical.state-close 9 | height: 0 10 | 11 | &.variant-horizontal.state-close 12 | width: 0 13 | -------------------------------------------------------------------------------- /src/css/base.styl: -------------------------------------------------------------------------------- 1 | @require './colors.styl' 2 | @require './variable.styl' 3 | @require './mixin.styl' 4 | 5 | .{$-melon-state-prefix}-hidden 6 | display none !important 7 | 8 | .{$-melon-variant-prefix}-size-xxs 9 | font-size $-melon-xx-small-font-size !important 10 | 11 | .{$-melon-variant-prefix}-size-xs 12 | font-size $-melon-x-small-font-size !important 13 | 14 | .{$-melon-variant-prefix}-size-s 15 | font-size $-melon-small-font-size !important 16 | 17 | .{$-melon-variant-prefix}-size-m 18 | font-size $-melon-medium-font-size !important 19 | 20 | .{$-melon-variant-prefix}-size-l 21 | font-size $-melon-large-font-size !important 22 | 23 | .{$-melon-variant-prefix}-size-xl 24 | font-size $-melon-x-large-font-size !important 25 | 26 | .{$-melon-variant-prefix}-size-xxl 27 | font-size $-melon-xx-large-font-size !important 28 | 29 | .{$-melon-variant-prefix}-size-xxxl 30 | font-size $-melon-xxx-large-font-size !important 31 | 32 | .{$-melon-variant-prefix}-fluid 33 | display: block !important 34 | width: 100% !important 35 | -------------------------------------------------------------------------------- /src/css/grid.styl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-melon/melon/373bcaa0b0966351461b3a8fc2460aeb1cae5bc6/src/css/grid.styl -------------------------------------------------------------------------------- /src/css/mixin.styl: -------------------------------------------------------------------------------- 1 | @require "./variable.styl" 2 | 3 | melon-get-theme() 4 | return typeof($melon-theme) == 'ident' ? material : $melon-theme 5 | 6 | // make a circle 7 | melon-circular() 8 | border-radius 500em 9 | 10 | melon-clearfix() 11 | &:after 12 | display table 13 | clear both 14 | content "" 15 | 16 | melon-get-z-depth($level = '0') 17 | $shadow = $-melon-z-depth[$level] 18 | return $shadow ? unquote($shadow) : none 19 | 20 | melon-generate-grid($columns = 12, $gap = 0.3) 21 | 22 | $single-width = (1 / $columns) 23 | $single-column-width = $single-width * 1 / ( 1 + $gap ) 24 | $gap-width = $single-width * $gap / ( 1 + $gap ) 25 | 26 | .melon-row 27 | margin-left: unit(-1 * $gap-width * 100, '%') !important 28 | clearfix() 29 | 30 | .melon-column 31 | float: left 32 | margin-left: unit($gap-width * 100, '%') !important 33 | 34 | for $i in (1..$columns) 35 | .melon-column-{$i} 36 | width: unit(($i * $single-width - $gap-width) * 100, '%') !important 37 | -------------------------------------------------------------------------------- /src/css/theme/default/BoxGroup.variable.styl: -------------------------------------------------------------------------------- 1 | $-melon-boxgroup = { 2 | width: 100% 3 | }; 4 | 5 | $-melon-boxgroup-option = { 6 | color: rgba($-melon-colors.black, .54) 7 | }; 8 | 9 | $-melon-boxgroup-option-icon = { 10 | font-size: $-melon-large-font-size, 11 | color: $-melon-boxgroup-option.color 12 | }; 13 | 14 | $-melon-boxgroup-option-icon-checked = { 15 | color: $-melon-colors.teal500 16 | }; 17 | 18 | $-melon-boxgroup-option-icon-disabled = { 19 | color: rgba($-melon-colors.black, .26) 20 | }; 21 | 22 | $-melon-boxgroup-option-icon-invalid = { 23 | color: $-melon-colors.red500 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /src/css/theme/default/Breadcrumb.variable.styl: -------------------------------------------------------------------------------- 1 | $-melon-breadcrumb = { 2 | 3 | item: { 4 | color: $-melon-colors.blue500 5 | hover: { 6 | color: $-melon-colors.blue500 7 | }, 8 | lastChild: { 9 | color: $-melon-colors.grey600 10 | } 11 | }, 12 | 13 | seperator: { 14 | color: $-melon-colors.grey600 15 | } 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /src/css/theme/default/Chip.variable.styl: -------------------------------------------------------------------------------- 1 | 2 | $-melon-chip-shadow = { 3 | 4 | '&:active': { 5 | box-shadow: melon-get-z-depth('1') 6 | } 7 | 8 | } 9 | 10 | $-melon-chip = { 11 | 12 | color: $-melon-colors.darkBlack 13 | background-color: $-melon-colors.grey300 14 | 15 | color-hover: $-melon-colors.white 16 | background-color-hover: $-melon-colors.grey400 17 | 18 | icon: { 19 | color: $-melon-colors.grey300 20 | background-color: $-melon-colors.lightBlack 21 | 22 | color-hover: $-melon-colors.grey400 23 | background-color-hover: $-melon-colors.white 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/css/theme/default/Link.variable.styl: -------------------------------------------------------------------------------- 1 | $-melon-link = { 2 | 3 | color: rgba($-melon-colors.black, .9), 4 | 5 | states: { 6 | hover: { 7 | color: $-melon-colors.blue500 8 | } 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/css/theme/default/Pager.variable.styl: -------------------------------------------------------------------------------- 1 | $-melon-pager = { 2 | 3 | font-size: $-melon-medium-font-size 4 | 5 | item: { 6 | color: $-melon-colors.darkBlack, 7 | background-color: $-melon-colors.faintBlack 8 | 9 | states: { 10 | 11 | hover: { 12 | background-color: $-melon-colors.faintBlack 13 | } 14 | 15 | disabled: { 16 | color: $-melon-colors.lightBlack 17 | }, 18 | 19 | disabledHover: { 20 | background-color: transparent 21 | } 22 | 23 | current: { 24 | background-color: $-melon-colors.teal500 25 | color: $-melon-colors.white 26 | } 27 | } 28 | 29 | } 30 | 31 | }; 32 | -------------------------------------------------------------------------------- /src/css/theme/default/Tabs.variable.styl: -------------------------------------------------------------------------------- 1 | $-melon-tabs = { 2 | 3 | font-size: $-melon-medium-font-size, 4 | background-color: $-melon-colors.teal500, 5 | 6 | item: { 7 | color: $-melon-colors.darkWhite, 8 | states: { 9 | selected: { 10 | color: $-melon-colors.white, 11 | background-color: $-melon-colors.teal500 12 | }, 13 | disabled: { 14 | color: $-melon-colors.lightWhite 15 | } 16 | } 17 | } 18 | 19 | inkbar: { 20 | background-color: $-melon-colors.yellow200 21 | } 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /src/css/theme/default/Toggle.variable.styl: -------------------------------------------------------------------------------- 1 | $-melon-toggle-circle-disabled = { 2 | background-color: $-melon-colors.grey400 3 | } 4 | 5 | $-melon-toggle-bar-disabled = { 6 | background-color: $-melon-colors.faintBlack 7 | }; 8 | 9 | $-melon-toggle-circle = { 10 | background-color: rgb($-melon-colors.teal500) 11 | }; 12 | 13 | $-melon-toggle-bar = { 14 | background-color: rgba($-melon-colors.teal500, 0.498039) 15 | }; 16 | -------------------------------------------------------------------------------- /src/css/theme/default/ToolBar.variable.styl: -------------------------------------------------------------------------------- 1 | $-melon-tool-bar = { 2 | background-color: $-melon-colors.blue400 3 | }; 4 | -------------------------------------------------------------------------------- /src/css/theme/default/index.styl: -------------------------------------------------------------------------------- 1 | @require '../../colors.styl' 2 | @require './BoxGroup.variable.styl' 3 | @require './Toggle.variable.styl' 4 | @require './Button.variable.styl' 5 | @require './Link.variable.styl' 6 | @require './Pager.variable.styl' 7 | @require './Tabs.variable.styl' 8 | @require './Breadcrumb.variable.styl' 9 | @require './ToolBar.variable.styl' 10 | @require './Chip.variable.styl' 11 | -------------------------------------------------------------------------------- /src/css/variable.styl: -------------------------------------------------------------------------------- 1 | // font-size 2 | $-melon-medium-font-size = 16px 3 | $-melon-xx-small-font-size = ($-melon-medium-font-size * 0.75) // 12 4 | $-melon-x-small-font-size = ($-melon-medium-font-size * 0.875) // 14 5 | $-melon-small-font-size = ($-melon-medium-font-size * 0.9375) // 16 6 | $-melon-large-font-size = ($-melon-medium-font-size * 1.125) // 18 7 | $-melon-x-large-font-size = ($-melon-medium-font-size * 1.25) // 20 8 | $-melon-xx-large-font-size = ($-melon-medium-font-size * 1.375) // 22 9 | $-melon-xxx-large-font-size = ($-melon-medium-font-size * 1.5) // 24 10 | $-melon-xxxx-large-font-size = ($-melon-medium-font-size * 2.25) // 36 11 | 12 | $-melon-font-family = 'Helvetica Neue' Helvetica 'Microsoft YaHei' Arial SimSun sans-serif; 13 | 14 | // global transition switch 15 | $-melon-enable-transition-effect = true 16 | 17 | // all prefix definitions 18 | $-melon-class-prefix = ui 19 | $-melon-state-prefix = state 20 | $-melon-variant-prefix = variant 21 | 22 | $-melon-z-depth = { 23 | '1': '0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12)', 24 | '2': '0 5px 11px 0 rgba(0, 0, 0, 0.18), 0 4px 15px 0 rgba(0, 0, 0, 0.15)', 25 | '3': '0 12px 15px 0 rgba(0, 0, 0, 0.24), 0 17px 50px 0 rgba(0, 0, 0, 0.19)', 26 | '4': '0 16px 28px 0 rgba(0, 0, 0, 0.22), 0 25px 55px 0 rgba(0, 0, 0, 0.21)', 27 | '5': '0 27px 24px 0 rgba(0, 0, 0, 0.2), 0 40px 77px 0 rgba(0, 0, 0, 0.22)' 28 | }; 29 | 30 | $-melon-z-index = { 31 | layer: 10, 32 | dialog: 10, 33 | snackbar: 10, 34 | drawer: 10 35 | popover: 11 36 | }; 37 | -------------------------------------------------------------------------------- /src/dialog/DialogWindow.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/Dialog/DialogWindow 3 | * @author cxtom 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | import shallowEqual from 'melon-core/util/shallowEqual'; 10 | 11 | const cx = create('DialogWindow'); 12 | 13 | export default class DialogWindow extends Component { 14 | 15 | shouldComponentUpdate(nextProps) { 16 | return !shallowEqual(this.props, nextProps); 17 | } 18 | 19 | render() { 20 | 21 | const { 22 | children, 23 | title, 24 | footer, 25 | width, 26 | style, 27 | ...others 28 | } = this.props; 29 | 30 | return ( 31 |
    35 | {title}{children}{footer} 36 |
    37 | ); 38 | } 39 | 40 | } 41 | 42 | DialogWindow.propTypes = { 43 | footer: PropTypes.element, 44 | title: PropTypes.element 45 | }; 46 | -------------------------------------------------------------------------------- /src/dialog/commander.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 命令式窗口管理 3 | * @author leon 4 | */ 5 | 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | 9 | let container = null; 10 | 11 | /** 12 | * 创建一个对话框构造器 13 | * 14 | * @param {Function} Dialog ReactComponent 15 | * @param {Array} closeEvents 指定的事件会被包裹成高阶函数,获得一个 close 函数 16 | * @return {Function} 一个可以实时创建对话框的函数 17 | */ 18 | export default function createDialogCommand(Dialog, closeEvents = []) { 19 | 20 | return function (options) { 21 | 22 | if (!container) { 23 | container = document.createElement('div'); 24 | container.className = 'melon-seperate-dialog-container'; 25 | document.body.appendChild(container); 26 | } 27 | 28 | let element = document.createElement('div'); 29 | container.appendChild(element); 30 | 31 | options = closeEvents.reduce( 32 | (options, event) => { 33 | 34 | return { 35 | ...options, 36 | [event]: (...args) => options[event](close, ...args) 37 | }; 38 | 39 | }, 40 | options 41 | ); 42 | 43 | ReactDOM.render( 44 | , 45 | element 46 | ); 47 | 48 | function close() { 49 | 50 | ReactDOM.render( 51 | , 52 | element 53 | ); 54 | 55 | setTimeout(() => { 56 | 57 | ReactDOM.unmountComponentAtNode(element); 58 | container.removeChild(element); 59 | element = null; 60 | 61 | }, 1000); 62 | 63 | } 64 | 65 | return close; 66 | 67 | }; 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/dialog/windowScrollHelper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/dialog/windowScrollHelper 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | const originalHTMLBodySize = {}; 7 | 8 | /* eslint-disable fecs-valid-dom-style */ 9 | 10 | function stopWindowScrolling(name) { 11 | 12 | const element = document.getElementsByTagName(name)[0]; 13 | 14 | let lockNum = element.getAttribute('data-lock') || '0'; 15 | lockNum = parseInt(lockNum, 10); 16 | 17 | element.setAttribute('data-lock', lockNum + 1); 18 | 19 | if (lockNum === 0) { 20 | originalHTMLBodySize[name] = { 21 | width: element.style.width, 22 | height: element.style.height, 23 | overflow: element.style.overflow 24 | }; 25 | element.style.width = '100%'; 26 | element.style.height = '100%'; 27 | 28 | if (name !== 'html') { 29 | element.style.overflow = 'hidden'; 30 | } 31 | } 32 | 33 | return element; 34 | } 35 | 36 | function restoreWindowScrolling(name) { 37 | 38 | const element = document.getElementsByTagName(name)[0]; 39 | let lockNum = element.getAttribute('data-lock') || '0'; 40 | lockNum = parseInt(lockNum, 10); 41 | 42 | if (lockNum > 1) { 43 | element.setAttribute('data-lock', lockNum - 1); 44 | } 45 | else { 46 | element.removeAttribute('data-lock'); 47 | const size = originalHTMLBodySize[name]; 48 | element.style.width = size.width; 49 | element.style.height = size.height; 50 | 51 | if (name !== 'html') { 52 | element.style.overflow = size.overflow; 53 | } 54 | delete originalHTMLBodySize[name]; 55 | } 56 | 57 | return element; 58 | } 59 | 60 | export function stop() { 61 | stopWindowScrolling('body'); 62 | stopWindowScrolling('html'); 63 | } 64 | 65 | export function restore() { 66 | if (!originalHTMLBodySize.body || !originalHTMLBodySize.html) { 67 | return; 68 | } 69 | restoreWindowScrolling('body'); 70 | restoreWindowScrolling('html'); 71 | } 72 | 73 | export function update() { 74 | stop(); 75 | restore(); 76 | } 77 | -------------------------------------------------------------------------------- /src/font/melon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-melon/melon/373bcaa0b0966351461b3a8fc2460aeb1cae5bc6/src/font/melon.eot -------------------------------------------------------------------------------- /src/font/melon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-melon/melon/373bcaa0b0966351461b3a8fc2460aeb1cae5bc6/src/font/melon.ttf -------------------------------------------------------------------------------- /src/font/melon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-melon/melon/373bcaa0b0966351461b3a8fc2460aeb1cae5bc6/src/font/melon.woff -------------------------------------------------------------------------------- /src/font/melon.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-melon/melon/373bcaa0b0966351461b3a8fc2460aeb1cae5bc6/src/font/melon.woff2 -------------------------------------------------------------------------------- /src/navigtaion/Header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file NavigationHeader 3 | * @author leon 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import Title from '../Title'; 8 | 9 | export default class NavigationHeader extends Component { 10 | 11 | render() { 12 | 13 | let { 14 | children, 15 | variants = [], 16 | ...rest 17 | } = this.props; 18 | 19 | return ( 20 | 24 | {children} 25 | 26 | ); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/navigtaion/Item.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Navigation Item 3 | * @author leon 4 | */ 5 | 6 | import React, {Component, Children} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | import Icon from '../Icon'; 10 | import TouchRipple from '../ripples/TouchRipple'; 11 | 12 | const cx = create('NavigationItem'); 13 | 14 | export default class NavigationItem extends Component { 15 | 16 | renderIcon(icon) { 17 | return typeof icon === 'string' 18 | ? 19 | : icon; 20 | } 21 | 22 | render() { 23 | 24 | let { 25 | children, 26 | label, 27 | leftIcon, 28 | rightIcon, 29 | onClick, 30 | href, 31 | active, 32 | level 33 | } = this.props; 34 | 35 | children = Children.toArray(children); 36 | 37 | let className = cx(this.props) 38 | .addStates({active}) 39 | .addVariants([`level-${level}`]) 40 | .build(); 41 | 42 | if (children && children.length) { 43 | return ( 44 |
    47 | {children} 48 |
    49 | ); 50 | } 51 | 52 | leftIcon = this.renderIcon(leftIcon); 53 | rightIcon = this.renderIcon(rightIcon); 54 | label = {label}; 55 | let touchableClassName = cx.getPartClassName('touchable'); 56 | 57 | if (href) { 58 | return ( 59 |
    70 | ); 71 | } 72 | 73 | return ( 74 |
    75 |
    76 | {leftIcon} 77 | {label} 78 | {rightIcon} 79 | 80 |
    81 |
    82 | ); 83 | 84 | } 85 | 86 | } 87 | 88 | NavigationItem.propTypes = { 89 | label: PropTypes.string, 90 | leftIcon: PropTypes.node, 91 | rightIcon: PropTypes.node, 92 | onClick: PropTypes.func, 93 | level: PropTypes.number 94 | }; 95 | -------------------------------------------------------------------------------- /src/region/City.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Region/RegionCity 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | 9 | import {create} from 'melon-core/classname/cxBuilder'; 10 | import Selector from './Selector'; 11 | 12 | const cx = create('RegionCity'); 13 | 14 | export default class RegionCity extends Component { 15 | 16 | constructor(props) { 17 | 18 | super(props); 19 | 20 | this.onSelectorChange = this.onSelectorChange.bind(this); 21 | 22 | } 23 | 24 | onSelectorChange(e) { 25 | 26 | const value = e.value; 27 | 28 | const {datasource, onChange} = this.props; 29 | 30 | datasource.selected = value; 31 | 32 | onChange && onChange({ 33 | data: datasource 34 | }); 35 | 36 | } 37 | 38 | render() { 39 | 40 | const datasource = this.props.datasource; 41 | 42 | return ( 43 |
  • 44 | 49 |
  • 50 | ); 51 | 52 | } 53 | 54 | } 55 | 56 | RegionCity.displayName = 'RegionCity'; 57 | 58 | RegionCity.propTypes = { 59 | onChange: PropTypes.func, 60 | disabled: PropTypes.bool, 61 | datasource: PropTypes.object 62 | }; 63 | -------------------------------------------------------------------------------- /src/region/Selector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/region/Selector 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import Icon from '../Icon'; 9 | import {create} from 'melon-core/classname/cxBuilder'; 10 | 11 | const cx = create('RegionSelector'); 12 | 13 | export default class RegionSelector extends Component { 14 | 15 | constructor(props) { 16 | 17 | super(props); 18 | 19 | this.onClick = this.onClick.bind(this); 20 | 21 | } 22 | 23 | 24 | onClick(e) { 25 | let { 26 | onChange, 27 | checked 28 | } = this.props; 29 | 30 | onChange && onChange({ 31 | value: !checked, 32 | target: this 33 | }); 34 | } 35 | 36 | getIcon(isChecked) { 37 | let icons = RegionSelector.Icons; 38 | return icons[isChecked ? 'checked' : 'unchecked']; 39 | } 40 | 41 | render() { 42 | 43 | let { 44 | checked, 45 | disabled, 46 | hasInput, 47 | value, 48 | name, 49 | label, 50 | id 51 | } = this.props; 52 | 53 | let className = cx(this.props).addStates({checked}).build(); 54 | 55 | return ( 56 | 66 | ); 67 | 68 | } 69 | 70 | } 71 | 72 | RegionSelector.displayName = 'RegionSelector'; 73 | 74 | RegionSelector.defaultProps = { 75 | hasInput: false 76 | }; 77 | 78 | RegionSelector.propTypes = { 79 | label: PropTypes.string, 80 | value: PropTypes.string, 81 | checked: PropTypes.bool, 82 | name: PropTypes.string, 83 | disabled: PropTypes.bool, 84 | id: PropTypes.string, 85 | hasInput: PropTypes.bool, 86 | onChange: PropTypes.func 87 | }; 88 | 89 | RegionSelector.Icons = { 90 | checked: 'check-box', 91 | unchecked: 'check-box-outline-blank' 92 | }; 93 | -------------------------------------------------------------------------------- /src/region/helper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/region/mixin 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | export function selectAll(child) { 7 | 8 | child.selected = true; 9 | 10 | if (Array.isArray(child.children)) { 11 | child.children.forEach(selectAll); 12 | } 13 | 14 | } 15 | 16 | export function cancelAll(child) { 17 | 18 | child.selected = false; 19 | 20 | if (Array.isArray(child.children)) { 21 | child.children.forEach(cancelAll); 22 | } 23 | 24 | } 25 | 26 | export function parse(value, child, index) { 27 | 28 | if (value.indexOf(child.id) > -1) { 29 | child.selected = true; 30 | } 31 | 32 | if (Array.isArray(child.children)) { 33 | child.children = child.children.map(function (c, i) { 34 | return parse(value, c, i); 35 | }); 36 | } 37 | 38 | return child; 39 | } 40 | export function isAllSelected(data) { 41 | 42 | if (!Array.isArray(data.children) || !(data.children.length > 0)) { 43 | return; 44 | } 45 | 46 | data.selected = data.children.reduce( 47 | function (result, child, index) { 48 | return result && child.selected; 49 | }, 50 | true 51 | ); 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/ripples/CenterRipple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/CenterRipple 3 | * @author cxtom 4 | */ 5 | 6 | 7 | import React, {Component} from 'react'; 8 | import PropTypes from 'prop-types'; 9 | import {create} from 'melon-core/classname/cxBuilder'; 10 | import RippleCircle from './RippleCircle'; 11 | import {spring, TransitionMotion} from 'react-motion'; 12 | 13 | const cx = create('CenterRipple'); 14 | 15 | export default class CenterRipple extends Component { 16 | 17 | constructor(props) { 18 | 19 | super(props); 20 | 21 | this.state = { 22 | now: 't' + 0 23 | }; 24 | 25 | this.willLeave = this.willLeave.bind(this); 26 | } 27 | 28 | animate() { 29 | this.setState({ 30 | now: 't' + Date.now() 31 | }); 32 | } 33 | 34 | componentWillReceiveProps(nextProps) { 35 | if (nextProps.flag === !this.props.flag) { 36 | this.animate(); 37 | } 38 | } 39 | 40 | shouldCompoenntUpdate(nextProps, nextState) { 41 | return this.props.opacity !== nextProps.opacity 42 | || this.props.scale !== nextProps.scale 43 | || this.props.flag !== nextProps.flag 44 | || this.state.now !== nextState.now; 45 | } 46 | 47 | willLeave(key, valOfKey) { 48 | return { 49 | ...valOfKey, 50 | opacity: spring(0, {stiffness: 60, damping: 15}), 51 | scale: spring(this.props.scale, {stiffness: 60, damping: 15}) 52 | }; 53 | } 54 | 55 | render() { 56 | 57 | const {opacity, children} = this.props; 58 | const now = this.state.now; 59 | 60 | const styles = [{ 61 | key: now, 62 | style: { 63 | opacity: spring(opacity), 64 | scale: spring(0) 65 | } 66 | }]; 67 | 68 | const className = cx(this.props).build(); 69 | const circleClassName = cx().part('circle').build(); 70 | 71 | return ( 72 | 75 | {interpolatedStyles => 76 |
    77 | {interpolatedStyles.map(config => { 78 | let {opacity, scale} = config.style; 79 | return ( 80 | 85 | ); 86 | })} 87 | {children} 88 |
    89 | } 90 |
    91 | ); 92 | 93 | } 94 | 95 | } 96 | 97 | CenterRipple.defaultProps = { 98 | opacity: 0.5, 99 | scale: 2 100 | }; 101 | 102 | CenterRipple.propTypes = { 103 | opacity: PropTypes.number.isRequired, 104 | scale: PropTypes.number.isRequired, 105 | flag: PropTypes.bool 106 | }; 107 | 108 | CenterRipple.displayName = 'CenterRipple'; 109 | -------------------------------------------------------------------------------- /src/ripples/RippleCircle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/RippleCircle 3 | * @author cxtom 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | 9 | export default class RippleCircle extends Component { 10 | 11 | shouldComponentUpdate(nextProps) { 12 | 13 | const { 14 | opacity, 15 | scale 16 | } = this.props; 17 | 18 | return opacity !== nextProps.opacity || scale !== nextProps.scale; 19 | } 20 | 21 | render() { 22 | 23 | const { 24 | style, 25 | opacity, 26 | scale, 27 | ...other 28 | } = this.props; 29 | 30 | return ( 31 |
    41 | ); 42 | 43 | 44 | } 45 | 46 | } 47 | 48 | RippleCircle.displayName = 'RippleCircle'; 49 | 50 | RippleCircle.defaultProps = { 51 | opacity: 0.3, 52 | scale: 2 53 | }; 54 | 55 | RippleCircle.propTypes = { 56 | opacity: PropTypes.number.isRequired, 57 | scale: PropTypes.number.isRequired 58 | }; 59 | -------------------------------------------------------------------------------- /src/select/Option.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Select/Option 3 | * @author leon 4 | */ 5 | 6 | import React from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | 10 | const cx = create('SelectOption'); 11 | 12 | /** 13 | * SelectOption 14 | * 15 | * @class 16 | * @param {*} props 属性 17 | */ 18 | export default function SelectOption(props) { 19 | 20 | const { 21 | children, 22 | label, 23 | disabled, 24 | selected, 25 | value, 26 | onClick, 27 | style 28 | } = props; 29 | 30 | const className = cx(props) 31 | .addStates({ 32 | selected, 33 | disabled 34 | }) 35 | .build(); 36 | 37 | return ( 38 |
    onClick({value}) : null}> 45 | {children || label} 46 |
    47 | ); 48 | 49 | 50 | } 51 | 52 | 53 | SelectOption.displayName = 'SelectOption'; 54 | 55 | SelectOption.propTypes = { 56 | disabled: PropTypes.bool, 57 | value: PropTypes.string.isRequired, 58 | selected: PropTypes.bool, 59 | label: PropTypes.string 60 | }; 61 | 62 | SelectOption.defaultProps = { 63 | disabled: false, 64 | selected: false 65 | }; 66 | -------------------------------------------------------------------------------- /src/select/OptionGroup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 下拉框选项组 3 | * @author leon 4 | */ 5 | 6 | import React, {Children} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import Option from './Option'; 9 | import {create} from 'melon-core/classname/cxBuilder'; 10 | 11 | const cx = create('SelectOptionGroup'); 12 | 13 | /** 14 | * OptionGroup 15 | * 16 | * @class 17 | * @param {*} props 属性 18 | */ 19 | export default function SelectOptionGroup(props) { 20 | 21 | const { 22 | value, 23 | children, 24 | disabled, 25 | label, 26 | onClick 27 | } = props; 28 | 29 | return ( 30 |
    31 |

    {label}

    32 |
    33 | {Children.map(children, (child, index) => { 34 | 35 | if (child.type !== 'option') { 36 | return null; 37 | } 38 | 39 | return ( 40 |
    50 |
    51 | ); 52 | 53 | } 54 | 55 | SelectOptionGroup.propTypes = { 56 | disabled: PropTypes.bool, 57 | label: PropTypes.string.isRequired 58 | }; 59 | -------------------------------------------------------------------------------- /src/slider/Bar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Slider/SliderBar 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | 10 | import Tooltip from '../Tooltip'; 11 | 12 | const cx = create('SliderBar'); 13 | 14 | export default class SliderBar extends Component { 15 | 16 | render() { 17 | 18 | const { 19 | height, 20 | maximum, 21 | minimum, 22 | value, 23 | disableFocusRipple, 24 | pointerSize, 25 | active, 26 | states, 27 | variants, 28 | ...rest 29 | } = this.props; 30 | 31 | const percent = ((value - minimum) / (maximum - minimum) * 100) + '%'; 32 | 33 | const activeStyle = { 34 | width: percent 35 | }; 36 | 37 | let pointerStyle = {}; 38 | let outerPointerStyle = {}; 39 | 40 | if (pointerSize) { 41 | pointerStyle.width = pointerStyle.height = pointerSize; 42 | 43 | if (typeof pointerSize === 'string') { 44 | const unit = pointerSize.replace(/\d(\.)?\d*/, ''); 45 | const value = parseFloat(pointerSize); 46 | outerPointerStyle.width = outerPointerStyle.height = (value * 2) + unit; 47 | outerPointerStyle.marginTop = (-value) + unit; 48 | pointerStyle.marginTop = (-value / 2) + unit; 49 | } 50 | else { 51 | outerPointerStyle.width = outerPointerStyle.height = pointerSize * 2; 52 | outerPointerStyle.marginTop = -pointerSize; 53 | pointerStyle.marginTop = -pointerSize / 2; 54 | } 55 | } 56 | 57 | const className = cx() 58 | .part('wrapper') 59 | .addVariants(variants) 60 | .addStates({...states, active}) 61 | .build(); 62 | 63 | return ( 64 |
    65 |
    66 |
    67 | 71 | {disableFocusRipple 72 | ? null 73 | :
    76 | } 77 |
    78 |
    79 | ); 80 | } 81 | 82 | } 83 | 84 | SliderBar.displayName = 'SliderBar'; 85 | 86 | SliderBar.propTypes = { 87 | width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), 88 | height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), 89 | maximum: PropTypes.number, 90 | minimum: PropTypes.number, 91 | disableFocusRipple: PropTypes.bool 92 | }; 93 | 94 | SliderBar.defaultProps = { 95 | height: 2, 96 | width: '100%', 97 | disableFocusRipple: false 98 | }; 99 | -------------------------------------------------------------------------------- /src/slider/getNewValue.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 获取新值 3 | * @author cxtom 4 | */ 5 | 6 | import {getPosition} from '../common/util/dom'; 7 | 8 | export default function getNewValue(dom, clientX, max, min, step) { 9 | 10 | const position = getPosition(dom); 11 | 12 | const percent = (clientX - position.left) / position.width; 13 | let newValue = min + (max - min) * percent; 14 | 15 | return Math.round(newValue / step) * step; 16 | } 17 | -------------------------------------------------------------------------------- /src/table/Cell.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/TableCell 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | import shallowEqual from 'melon-core/util/shallowEqual'; 10 | 11 | const cx = create('TableCell'); 12 | 13 | const JUSTIFY_CONTENT_MAP = { 14 | left: 'flex-start', 15 | center: 'center', 16 | right: 'flex-end' 17 | }; 18 | 19 | export default class TableCell extends Component { 20 | 21 | shouldComponentUpdate(nextProps) { 22 | return !shallowEqual(nextProps, this.props); 23 | } 24 | 25 | render() { 26 | 27 | const { 28 | height, 29 | content, 30 | columnData 31 | } = this.props; 32 | 33 | let { 34 | align, 35 | width, 36 | grow, 37 | shrink, 38 | maxWidth, 39 | minWidth 40 | } = columnData; 41 | 42 | const style = { 43 | justifyContent: JUSTIFY_CONTENT_MAP[align], 44 | flexBasis: width, 45 | height: height, 46 | flexGrow: grow, 47 | flexShrink: shrink, 48 | maxWidth, 49 | minWidth 50 | }; 51 | 52 | return ( 53 |
    54 | {content} 55 |
    56 | ); 57 | 58 | } 59 | 60 | } 61 | 62 | TableCell.displayName = 'TableCell'; 63 | 64 | TableCell.propTypes = { 65 | 66 | part: PropTypes.oneOf(['header', 'body', 'footer']), 67 | 68 | columnData: PropTypes.shape({ 69 | width: PropTypes.number.isRequired, 70 | align: PropTypes.oneOf(['left', 'center', 'right']).isRequired, 71 | grow: PropTypes.number.isRequired, 72 | shrink: PropTypes.number.isRequired, 73 | maxWidth: PropTypes.number, 74 | minWidth: PropTypes.number 75 | }), 76 | 77 | rowData: PropTypes.any, 78 | columnIndex: PropTypes.number, 79 | rowIndex: PropTypes.number, 80 | cellData: PropTypes.any, 81 | cellKey: PropTypes.oneOfType([ 82 | PropTypes.string.isRequired, 83 | PropTypes.number.isRequired 84 | ]), 85 | 86 | height: PropTypes.number.isRequired, 87 | cellRenderer: PropTypes.func 88 | 89 | }; 90 | 91 | TableCell.defaultProps = { 92 | align: 'left' 93 | }; 94 | -------------------------------------------------------------------------------- /src/table/SelectorColumn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file TableSelectorColumn 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | 9 | import Icon from '../Icon'; 10 | import Column from './Column'; 11 | 12 | /** 13 | * 可选择的表格列 14 | * 15 | * @extends React.Component 16 | */ 17 | export default class TableSelectorColumn extends Component { 18 | 19 | /** 20 | * 渲染 21 | * 22 | * @public 23 | * @return {Element} 24 | */ 25 | render() { 26 | return null; 27 | } 28 | 29 | } 30 | 31 | TableSelectorColumn.displayName = 'TableSelectorColumn'; 32 | 33 | TableSelectorColumn.icons = { 34 | radio: { 35 | checked: 'radio-button-checked', 36 | unchecked: 'radio-button-unchecked' 37 | }, 38 | checkbox: { 39 | checked: 'check-box', 40 | unchecked: 'check-box-outline-blank' 41 | } 42 | }; 43 | 44 | TableSelectorColumn.getIcon = function (multiple, selected) { 45 | let icons = TableSelectorColumn.icons[multiple ? 'checkbox' : 'radio']; 46 | return icons[selected ? 'checked' : 'unchecked']; 47 | }; 48 | 49 | TableSelectorColumn.cellRenderer = function (props) { 50 | 51 | const {part, columnData, rowIndex} = props; 52 | const multiple = columnData.multiple; 53 | 54 | if (!multiple && part !== 'body') { 55 | return null; 56 | } 57 | 58 | const isSelected = part === 'body' 59 | ? columnData.isSelected(rowIndex) 60 | : columnData.isAllSelected(); 61 | 62 | return ( 63 | 68 | ); 69 | 70 | }; 71 | 72 | TableSelectorColumn.onCellClick = function (props) { 73 | 74 | let {part, rowIndex, columnData} = props; 75 | let handler = columnData[part === 'body' ? 'onSelect' : 'onSelectAll']; 76 | 77 | if (typeof handler === 'function') { 78 | handler(rowIndex); 79 | } 80 | 81 | }; 82 | 83 | TableSelectorColumn.propTypes = { 84 | 85 | ...Column.propTypes, 86 | 87 | isSelected: PropTypes.func.isRequired, 88 | isAllSelected: PropTypes.func.isRequired, 89 | onSelect: PropTypes.func, 90 | onSelectAll: PropTypes.func, 91 | name: PropTypes.string 92 | 93 | }; 94 | 95 | /** 96 | * 最关键的东西在这里 97 | */ 98 | TableSelectorColumn.defaultProps = { 99 | ...Column.defaultProps, 100 | width: 66, 101 | grow: 0, 102 | shrink: 0, 103 | cellRenderer: TableSelectorColumn.cellRenderer, 104 | headerRenderer: TableSelectorColumn.headerRenderer, 105 | footerRenderer: TableSelectorColumn.footerRenderer, 106 | align: 'center', 107 | dataKey: '', 108 | multiple: false 109 | }; 110 | 111 | TableSelectorColumn._TABLE_COMPONENT_ = 'COLUMN'; 112 | -------------------------------------------------------------------------------- /src/table/SelectorRow.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 可选择的表格行 3 | * @author leon 4 | */ 5 | 6 | import PropTypes from 'prop-types'; 7 | import Row from './Row'; 8 | 9 | /** 10 | * 可选择的表格行 11 | * 12 | * 做这个类,主要是为了解决性能问题 13 | * 14 | * @extends {TableRow} 15 | */ 16 | export default class SelectorRow extends Row { 17 | 18 | /** 19 | * 是否应该更新视图 20 | * 21 | * @public 22 | * @param {*} nextProps 下一个属性 23 | * @param {*} nextState 下一个状态 24 | * @return {boolean} 25 | */ 26 | shouldComponentUpdate(nextProps, nextState) { 27 | 28 | if (nextProps.selected !== this.props.selected) { 29 | return true; 30 | } 31 | 32 | return super.shouldComponentUpdate(nextProps, nextState); 33 | 34 | } 35 | 36 | } 37 | 38 | 39 | SelectorRow.propTypes = { 40 | ...Row.propTypes, 41 | selected: PropTypes.bool.isRequired 42 | }; 43 | 44 | SelectorRow.defaultProps = { 45 | ...Row.defaultProps, 46 | selected: false 47 | }; 48 | -------------------------------------------------------------------------------- /src/table/TableBody.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 表格 body 3 | * @author leon 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | import TableRow from './Row'; 10 | import shallowEqual from 'melon-core/util/shallowEqual'; 11 | 12 | const cx = create('TableBody'); 13 | 14 | export default class TableBody extends Component { 15 | 16 | shouldComponentUpdate(nextProps) { 17 | 18 | let {columns, dataSource} = this.props; 19 | 20 | return !shallowEqual(nextProps.columns, columns) 21 | || !shallowEqual(dataSource, nextProps.dataSource); 22 | 23 | } 24 | 25 | render() { 26 | 27 | let { 28 | dataSource, 29 | columns 30 | } = this.props; 31 | 32 | return ( 33 |
    34 | {dataSource.map((rowData, index) => ( 35 | 39 | ))} 40 |
    41 | ); 42 | 43 | } 44 | 45 | } 46 | 47 | 48 | TableBody.propTypes = { 49 | columns: PropTypes.arrayOf(PropTypes.object).isRequired, 50 | dataSource: PropTypes.arrayOf(PropTypes.object).isRequired 51 | }; 52 | -------------------------------------------------------------------------------- /src/tabs/Panel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon Tabs Panel 3 | * @author cxtom 4 | */ 5 | 6 | import React from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | 10 | const cx = create('TabsPanel'); 11 | 12 | /** 13 | * melon/Tabs/TabPanel 14 | * 15 | * @class 16 | * @param {Object} props 属性 17 | * @param {boolean} props.active 是否选中 18 | * @return {ReactElement} 19 | */ 20 | export default function TabsPanel(props) { 21 | 22 | /* eslint-disable fecs-min-vars-per-destructure */ 23 | const {active, ...others} = props; 24 | 25 | return ( 26 |
    27 | ); 28 | 29 | } 30 | 31 | TabsPanel.displayName = 'TabsPanel'; 32 | 33 | TabsPanel.propTypes = { 34 | active: PropTypes.bool 35 | }; 36 | 37 | TabsPanel.defaultProps = { 38 | active: false 39 | }; 40 | -------------------------------------------------------------------------------- /src/tabs/Tab.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon Tabs Tab 3 | * @author cxtom 4 | */ 5 | 6 | import React from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | 10 | const cx = create('TabsItem'); 11 | 12 | /* eslint-disable fecs-prefer-class */ 13 | 14 | /** 15 | * melon/Tabs/TabPanel 16 | * 17 | * @class 18 | * @param {Object} props 属性 19 | * @param {number} props.index 序号 20 | * @param {string|ReactElement} props.label Tab上显示的内容 21 | * @param {boolean} props.disabled 是否不可选 22 | * @param {boolean} props.selected 是否选中 23 | * @return {ReactElement} 24 | */ 25 | export default function Tab(props) { 26 | 27 | const { 28 | selected, 29 | disabled, 30 | label, 31 | index, 32 | onClick, 33 | ...others 34 | } = props; 35 | 36 | const className = cx(props).addStates({selected, disabled}).build(); 37 | 38 | return ( 39 |
  • disabled || onClick(index)}> 43 | {label} 44 |
  • 45 | ); 46 | 47 | } 48 | 49 | Tab.propTypes = { 50 | label: PropTypes.node, 51 | disabled: PropTypes.bool 52 | }; 53 | 54 | Tab.defaultProps = { 55 | disabled: false, 56 | selected: false 57 | }; 58 | -------------------------------------------------------------------------------- /src/textbox/FloatLabel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file melon/textbox/FloatingLabel 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | 10 | const cx = create('TextBoxFloatingLabel'); 11 | 12 | /* eslint-disable fecs-prefer-class */ 13 | export default function TextBoxFloatingLabel(props) { 14 | 15 | const { 16 | floating, 17 | focused, 18 | label 19 | } = props; 20 | 21 | const className = cx(props) 22 | .addStates({ 23 | focus: focused, 24 | floating 25 | }) 26 | .build(); 27 | 28 | return ( 29 | 32 | ); 33 | 34 | } 35 | 36 | TextBoxFloatingLabel.propTypes = { 37 | label: PropTypes.string.isRequired, 38 | floating: PropTypes.bool.isRequired 39 | }; 40 | -------------------------------------------------------------------------------- /src/textbox/Input.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file TextBox/Input 3 | * @author leon(ludafa@outlook.com) 4 | */ 5 | 6 | import React, {Component} from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import {create} from 'melon-core/classname/cxBuilder'; 9 | 10 | const cx = create('TextBoxInput'); 11 | 12 | export default class TextBoxInput extends Component { 13 | 14 | render() { 15 | 16 | const { 17 | multiline, 18 | rows, 19 | isFocus, 20 | value, 21 | variants, 22 | states, 23 | ...rest 24 | } = this.props; 25 | 26 | const Text = multiline ? 'textarea' : 'input'; 27 | const className = cx() 28 | .addVariants(variants) 29 | .addStates({ 30 | ...states, 31 | focus: isFocus 32 | }) 33 | .build(); 34 | 35 | const props = { 36 | ...rest, 37 | className, 38 | value, 39 | rows: multiline ? rows : null 40 | }; 41 | 42 | return ( 43 | 44 | ); 45 | 46 | } 47 | 48 | 49 | } 50 | 51 | TextBoxInput.displayName = 'TextBoxInput'; 52 | 53 | TextBoxInput.propTypes = { 54 | rows: PropTypes.number 55 | }; 56 | 57 | TextBoxInput.defaultProps = { 58 | rows: 2 59 | }; 60 | -------------------------------------------------------------------------------- /test/components/Alert.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Alert Spec 3 | * @author leon 4 | */ 5 | 6 | import React from 'react'; 7 | import {mount} from 'enzyme'; 8 | import Alert from '../../src/Alert'; 9 | 10 | describe('Alert', () => { 11 | 12 | it('as a component', done => { 13 | 14 | let spy = jasmine.createSpy('alert-confirm-spy'); 15 | 16 | let alert = mount( 17 | 21 | 报警! 22 | 23 | ); 24 | 25 | let dialog = document.querySelectorAll('.ui-dialog'); 26 | let button = document.querySelectorAll('.ui-button'); 27 | 28 | expect(button.length).toBe(1); 29 | expect(dialog.length).toBe(1); 30 | 31 | button[0].click(); 32 | 33 | setTimeout(() => { 34 | 35 | expect(spy).toHaveBeenCalled(); 36 | alert.unmount(); 37 | done(); 38 | 39 | }, 1000); 40 | 41 | 42 | }); 43 | 44 | it('as an API', done => { 45 | 46 | expect(typeof Alert.show).toBe('function'); 47 | 48 | const destroy = Alert.show({ 49 | children: 'haha' 50 | }); 51 | 52 | expect(typeof destroy === 'function').toBe(true); 53 | 54 | let elements = document.querySelectorAll( 55 | '.melon-seperate-dialog-container>div' 56 | ); 57 | 58 | expect(elements.length).toBe(1); 59 | 60 | destroy(); 61 | 62 | setTimeout(() => { 63 | elements = document.querySelectorAll( 64 | '.melon-seperate-dialog-container>div' 65 | ); 66 | expect(elements.length).toBe(0); 67 | done(); 68 | }, 1000); 69 | 70 | 71 | }); 72 | 73 | }); 74 | -------------------------------------------------------------------------------- /test/components/Breadcrumb.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Breadcrumb单测 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import {shallow} from 'enzyme'; 8 | import Breadcrumb from '../../src/Breadcrumb'; 9 | 10 | const Item = Breadcrumb.Item; 11 | 12 | describe('Breadcrumb', function () { 13 | 14 | it('work', function () { 15 | 16 | const crumb = shallow( 17 | 18 | crumb1 19 | crumb2 20 | 21 | ); 22 | 23 | expect(crumb.find(Item).length).toBe(2); 24 | 25 | }); 26 | 27 | 28 | it('Item', function () { 29 | const item = shallow( 30 | crumb1 31 | ); 32 | expect(item.hasClass('ui-breadcrumb-item')).toBe(true); 33 | expect(item.prop('data-level')).toBe(1); 34 | expect(item.prop('href')).toBe('#'); 35 | }); 36 | 37 | it('createCrumbs', function () { 38 | 39 | const crumbs = [{ 40 | href: '#1', 41 | text: 'crumb1' 42 | }, { 43 | href: '#2', 44 | text: 'crumb2' 45 | }]; 46 | 47 | const crumb = shallow( 48 | 49 | {Breadcrumb.createCrumbs(crumbs)} 50 | 51 | ); 52 | 53 | expect(crumb.find(Item).length).toBe(2); 54 | 55 | }); 56 | 57 | 58 | }); 59 | -------------------------------------------------------------------------------- /test/components/Button.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Button单测 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import {shallow} from 'enzyme'; 8 | import Button from '../../src/Button'; 9 | import Icon from '../../src/Icon'; 10 | import TouchRipple from '../../src/ripples/TouchRipple'; 11 | 12 | describe('Button', function () { 13 | 14 | 15 | it('label', function () { 16 | let wrapper = shallow( 17 | 25 | // ); 26 | // expect(actualElement).toEqualJSX(expectedElement); 27 | }); 28 | 29 | it('ripple', function () { 30 | 31 | let wrapper = shallow( 32 | 35 | ); 36 | 37 | expect(wrapper.hasClass('variant-ripple')).toBe(true); 38 | expect(wrapper.find(TouchRipple).length).toBe(1); 39 | 40 | // let actualElement = renderer.getRenderOutput(); 41 | // 42 | // let expectedElement = ( 43 | // 47 | // ); 48 | // 49 | // expect(actualElement).toEqualJSX(expectedElement); 50 | }); 51 | 52 | it('icon button', () => { 53 | let wrapper = shallow( 54 | 57 | ); 58 | expect(wrapper.hasClass('variant-icon')).toBe(true); 59 | expect(wrapper.find(Icon).length).toBe(1); 60 | 61 | // let actualElement = renderer.getRenderOutput(); 62 | // 63 | // let expectedElement = ( 64 | // 67 | // ); 68 | // 69 | // expect(actualElement).toEqualJSX(expectedElement); 70 | }); 71 | 72 | }); 73 | -------------------------------------------------------------------------------- /test/components/Card.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Card单测 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import {shallow} from 'enzyme'; 8 | import Card from '../../src/Card'; 9 | 10 | describe('Card', function () { 11 | 12 | it('work', function () { 13 | const wrapper = shallow(test); 14 | expect(wrapper.hasClass('ui-card')).toBe(true); 15 | expect(wrapper.text()).toBe('test'); 16 | // let actualElement = renderer.getRenderOutput(); 17 | // let expectedElement = ( 18 | //
    test
    19 | // ); 20 | // expect(actualElement).toEqualJSX(expectedElement); 21 | }); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /test/components/Confirm.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Confirm 3 | * @author leon 4 | */ 5 | 6 | import React from 'react'; 7 | import {mount} from 'enzyme'; 8 | import Confirm from '../../src/Confirm'; 9 | import then from '../then'; 10 | 11 | /* eslint-disable max-nested-callbacks */ 12 | 13 | describe('Confirm', () => { 14 | 15 | it('as a component', done => { 16 | 17 | let confirmSpy = jasmine.createSpy('confirm-confirm-spy'); 18 | let cancelSpy = jasmine.createSpy('confirm-cancel-spy'); 19 | 20 | let confirm = mount( 21 | 26 | 报警! 27 | 28 | ); 29 | 30 | let dialog = document.querySelectorAll('.ui-dialog'); 31 | let buttons = document.querySelectorAll('.ui-button'); 32 | 33 | expect(dialog.length).toBe(1); 34 | expect(buttons.length).toBe(2); 35 | 36 | buttons[1].click(); 37 | 38 | then(() => { 39 | expect(dialog[0].classList.contains('state-open')).toBe(true); 40 | expect(cancelSpy).toHaveBeenCalled(); 41 | buttons[0].click(); 42 | }) 43 | .then(() => { 44 | expect(dialog[0].classList.contains('state-open')).toBe(true); 45 | expect(confirmSpy).toHaveBeenCalled(); 46 | confirm.setProps({open: false}); 47 | 48 | setTimeout(() => { 49 | expect(dialog[0].parentNode == null).toBe(true); 50 | confirm.unmount(); 51 | done(); 52 | }, 1100); 53 | 54 | }); 55 | 56 | }); 57 | 58 | it('as an API', done => { 59 | 60 | expect(typeof Confirm.show).toBe('function'); 61 | 62 | const destroy = Confirm.show({ 63 | children: 'haha' 64 | }); 65 | 66 | expect(typeof destroy === 'function').toBe(true); 67 | 68 | let elements = document.querySelectorAll( 69 | '.melon-seperate-dialog-container>div' 70 | ); 71 | 72 | expect(elements.length).toBe(1); 73 | 74 | destroy(); 75 | 76 | setTimeout(() => { 77 | elements = document.querySelectorAll( 78 | '.melon-seperate-dialog-container>div' 79 | ); 80 | expect(elements.length).toBe(0); 81 | done(); 82 | }, 1000); 83 | 84 | }); 85 | 86 | }); 87 | -------------------------------------------------------------------------------- /test/components/Icon.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Icon单测 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import {shallow} from 'enzyme'; 8 | 9 | import Icon from '../../src/Icon'; 10 | 11 | describe('Icon', function () { 12 | 13 | it('work', function () { 14 | const wrapper = shallow(); 15 | expect(wrapper.prop('data-icon')).toBe('hello'); 16 | expect(wrapper.hasClass('ui-icon')).toBe(true); 17 | // let actualElement = renderer.getRenderOutput(); 18 | // let expectedElement = ( 19 | // 20 | // ); 21 | // expect(actualElement).toEqualJSX(expectedElement); 22 | }); 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /test/components/Link.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Link单测 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import {shallow} from 'enzyme'; 8 | import Link from '../../src/Link'; 9 | 10 | describe('Link', function () { 11 | 12 | it('work', function () { 13 | let wrapper = shallow(link); 14 | expect(wrapper.hasClass('ui-link')).toBe(true); 15 | expect(wrapper.text()).toBe('link'); 16 | }); 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /test/components/Select.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Select单测 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import {mount} from 'enzyme'; 8 | import Select from '../../src/Select'; 9 | 10 | import '../../src/css/theme/default/index.styl'; 11 | import '../../src/css/base.styl'; 12 | import '../../src/css/Select.styl'; 13 | 14 | /* eslint-disable max-nested-callbacks */ 15 | 16 | describe('Select', function () { 17 | 18 | const datasource = [ 19 | {value: '1', name: 'Never'}, 20 | {value: '2', name: 'Every Night'}, 21 | {value: '3', name: 'Weeknights'}, 22 | {value: '4', name: 'WeekendsWeekendsWeekendsWeekends'}, 23 | {value: '5', name: 'Weekly', disabled: true} 24 | ]; 25 | 26 | it('work', done => { 27 | 28 | let wrapper = mount( 29 | 35 | ); 36 | 37 | let select = wrapper.instance(); 38 | 39 | expect(select.getValue()).toEqual('1'); 40 | expect(select.isOpen()).toBeFalsy(); 41 | wrapper.find('.ui-select').simulate('click'); 42 | 43 | setTimeout(() => { 44 | 45 | expect(select.isOpen()).toBeTruthy(); 46 | 47 | let options = document.querySelectorAll('.ui-select-option'); 48 | 49 | // 有 6 个选项 50 | expect(options.length).toBe(6); 51 | 52 | // 第一项被选中 53 | expect(document.querySelectorAll('.ui-select-option.state-selected').length).toBe(1); 54 | 55 | options[1].click(); 56 | 57 | setTimeout(() => { 58 | 59 | expect(select.isOpen()).toBeFalsy(); 60 | expect(select.getValue()).toBe('2'); 61 | wrapper.unmount(); 62 | done(); 63 | 64 | }, 1000); 65 | 66 | }, 1000); 67 | 68 | }); 69 | 70 | }); 71 | -------------------------------------------------------------------------------- /test/components/Tabs.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Tabs单测 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import {mount, shallow} from 'enzyme'; 9 | import Tabs from '../../src/Tabs'; 10 | import TabsPanel from '../../src/tabs/Panel'; 11 | import then from '../then'; 12 | 13 | const Tab = Tabs.Tab; 14 | 15 | describe('Tabs', function () { 16 | 17 | it('Tab', function () { 18 | const tab = shallow( 19 | 20 | ); 21 | expect(tab.hasClass('ui-tabs-item')).toBe(true); 22 | expect(tab.hasClass('state-selected')).toBe(true); 23 | }); 24 | 25 | it('TabsPanel', function () { 26 | let wrapper = shallow(); 27 | expect(wrapper.hasClass('ui-tabs-panel')).toBe(true); 28 | expect(wrapper.hasClass('state-active')).toBe(true); 29 | // let actualElement = renderer.getRenderOutput(); 30 | // let expectedElement = (
    ); 31 | // expect(actualElement).toEqualJSX(expectedElement); 32 | }); 33 | 34 | it('Tabs functions', function (done) { 35 | 36 | const spy = jasmine.createSpy('tabs-change'); 37 | 38 | const tabs = mount( 39 | 40 | 41 | Tab1 Content 42 | 43 | 44 | Tab2 Content 45 | 46 | 47 | ); 48 | 49 | const labels = tabs.find('.ui-tabs-item'); 50 | 51 | expect(labels.length).toBe(2); 52 | expect(tabs.find('.ui-tabs-panel').length).toBe(2); 53 | expect(tabs.state('selectedIndex')).toBe(0); 54 | 55 | labels.at(1).simulate('click'); 56 | 57 | then(() => { 58 | expect(tabs.state('selectedIndex')).toBe(1); 59 | done(); 60 | }); 61 | 62 | }); 63 | 64 | }); 65 | -------------------------------------------------------------------------------- /test/components/Title.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Title单测 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import {shallow} from 'enzyme'; 8 | import Title from '../../src/Title'; 9 | 10 | describe('Title', function () { 11 | 12 | it('work h1', function () { 13 | let wrapper = shallow(title); 14 | expect(wrapper.is('h1')).toBe(true); 15 | expect(wrapper.hasClass('ui-title')).toBe(true); 16 | expect(wrapper.text()).toBe('title'); 17 | }); 18 | 19 | it('work h2', function () { 20 | let wrapper = shallow(title); 21 | expect(wrapper.is('h2')).toBe(true); 22 | }); 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /test/components/ToolBar.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ToolBar单测 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import {shallow} from 'enzyme'; 8 | import ToolBar from '../../src/ToolBar'; 9 | 10 | describe('ToolBar', function () { 11 | 12 | it('work', function () { 13 | const wrapper = shallow(test); 14 | expect(wrapper.hasClass('ui-tool-bar')).toBe(true); 15 | expect(wrapper.text()).toBe('test'); 16 | }); 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /test/components/Zippy.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Zippy单测 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import {shallow} from 'enzyme'; 9 | import then from '../then'; 10 | 11 | import Zippy from '../../src/Zippy'; 12 | 13 | describe('Zippy', () => { 14 | 15 | it('horizontal / vertical', () => { 16 | let wrapper = shallow(

    test

    ); 17 | expect(wrapper.hasClass('variant-vertical')).toBe(true); 18 | wrapper.setProps({direction: 'horizontal'}); 19 | expect(wrapper.hasClass('variant-horizontal')).toBe(true); 20 | }); 21 | 22 | it('expand', done => { 23 | 24 | let wrapper = shallow( 25 | 26 |

    test

    27 |
    28 | ); 29 | 30 | expect(wrapper.hasClass('state-close')).toBe(true); 31 | 32 | wrapper.setProps({expand: true}); 33 | 34 | then(() => { 35 | expect(wrapper.hasClass('state-close')).toBe(false); 36 | done(); 37 | }); 38 | 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 测试入口 3 | * @author leon 4 | */ 5 | 6 | import {configure} from 'enzyme'; 7 | import Adapter from 'enzyme-adapter-react-16'; 8 | 9 | configure({ 10 | adapter: new Adapter() 11 | }); 12 | 13 | import './index.styl'; 14 | 15 | // const WHITE_LIST = [ 16 | // // './Alert.spec', 17 | // // './BoxGroup.spec', 18 | // // './Breadcrumb.spec', 19 | // // './Button.spec', 20 | // // './Card.spec', 21 | // // './Chip.spec', 22 | // // './Confirm.spec', 23 | // './Dialog.spec', 24 | // // './Icon.spec', 25 | // // './Layer.spec', 26 | // // './Link.spec', 27 | // // './Pager.spec', 28 | // // './ScrollView.spec', 29 | // // './Select.spec', 30 | // // './SnackBar.spec', 31 | // // './Tabs.spec', 32 | // // './TextBox.spec', 33 | // // './Title.spec', 34 | // // './Toggle.spec', 35 | // // './ToolBar.spec', 36 | // // './Tooltip.spec', 37 | // // './Tree.spec', 38 | // // './Uploader.spec', 39 | // // './Zippy.spec', 40 | // ]; 41 | const BLACK_LIST = [ 42 | './Alert.spec', 43 | './Confirm.spec', 44 | './Dialog.spec', 45 | './Tooltip.spec', 46 | './SnackBar.spec', 47 | './Select.spec' 48 | ]; 49 | const specContext = require.context('./components', true) 50 | const specs = specContext 51 | .keys() 52 | .filter(spec => !/\.js$/.test(spec)) 53 | .filter(spec => BLACK_LIST.indexOf(spec) === -1) 54 | .forEach(specContext) 55 | -------------------------------------------------------------------------------- /test/index.styl: -------------------------------------------------------------------------------- 1 | @require '../src/css/theme/default/index.styl'; 2 | @require '../src/css/base.styl'; 3 | @require '../../src/css/Dialog.styl'; 4 | @require '../../src/css/Tooltip.styl'; 5 | @require '../../src/css/ScrollView.styl'; 6 | @require '../../src/css/Layer.styl' 7 | -------------------------------------------------------------------------------- /test/karma.ci.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file karma test config using travis 3 | * @author cxtom 4 | */ 5 | 6 | const karmaConfig = require('./karma.conf.js'); 7 | 8 | module.exports = function (config) { 9 | 10 | config.set( 11 | Object.assign( 12 | {}, 13 | karmaConfig, 14 | { 15 | 16 | browserStack: { 17 | username: 'leonlu2', 18 | accessKey: 'ps6dvCJdxhJGWWSTrWM4', 19 | retryLimit: 5, 20 | captureTimeout: 1800, 21 | timeout: 1800, 22 | concurrency: 2, 23 | browserNoActivityTimeout: 1800, 24 | browserDisconnectTimeout: 1800, 25 | browserDisconnectTolerance: 3, 26 | pollingTimeout: 30000 27 | }, 28 | 29 | /* eslint-disable fecs-camelcase */ 30 | // define browsers 31 | customLaunchers: { 32 | bs_chrome_mac: { 33 | base: 'BrowserStack', 34 | browser: 'chrome', 35 | browser_version: '55.0', 36 | os: 'OS X', 37 | os_version: 'Sierra' 38 | }, 39 | bs_firefix_mac: { 40 | base: 'BrowserStack', 41 | os: 'OS X', 42 | os_version: 'Sierra', 43 | browser: 'firefox', 44 | browser_version: '50.0' 45 | }, 46 | bs_ie9_windows: { 47 | base: 'BrowserStack', 48 | browser: 'ie', 49 | browser_version: '9.0', 50 | os: 'Windows', 51 | os_version: '7' 52 | } 53 | }, 54 | /* eslint-enable fecs-camelcase */ 55 | 56 | browsers: [ 57 | 'bs_chrome_mac', 58 | 'bs_firefix_mac' 59 | ], 60 | 61 | // if true, Karma captures browsers, runs the tests and exits 62 | singleRun: true 63 | } 64 | ) 65 | ); 66 | 67 | }; 68 | -------------------------------------------------------------------------------- /test/karma.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file karma test common config 3 | * @author ludafa 4 | */ 5 | 6 | const path = require('path'); 7 | 8 | module.exports = { 9 | 10 | basePath: path.join(__dirname, '../'), 11 | 12 | frameworks: [ 13 | 'jasmine' 14 | ], 15 | 16 | files: [ 17 | 'test/index.js' 18 | ], 19 | 20 | browsers: [ 21 | // 'Firefox', 22 | 'Chrome' 23 | ], 24 | 25 | preprocessors: { 26 | 'src/**/*.js': ['coverage', 'sourcemap'], 27 | 'test/**/*.js': ['webpack', 'sourcemap'] 28 | }, 29 | 30 | webpack: { 31 | module: { 32 | loaders: [ 33 | { 34 | test: /\.js$/, 35 | loader: 'babel', 36 | exclude: /node_modules/ 37 | }, 38 | { 39 | test: /\.json$/, 40 | loaders: ['json'] 41 | }, 42 | { 43 | test: /\.styl$/, 44 | loaders: [ 45 | 'style', 46 | 'css', 47 | 'stylus?paths=node_modules&resolve url' 48 | ] 49 | }, 50 | { 51 | test: /\.(svg|eot|ttf|woff|woff2|jpg|png)(\?.*)?$/, 52 | loader: 'file?name=asset/[name].[ext]' 53 | }, 54 | { 55 | test: /\.css$/, 56 | loader: 'style!css' 57 | } 58 | ] 59 | }, 60 | devtool: 'inline-source-map', 61 | externals: { 62 | 'react/lib/ExecutionEnvironment': true, 63 | 'react/lib/ReactContext': true, 64 | 'react/addons': true 65 | } 66 | }, 67 | 68 | webpackMiddleware: { 69 | stats: 'errors-only' 70 | }, 71 | 72 | autoWatch: true, 73 | 74 | // logLevel: config.LOG_DEBUG, 75 | reporters: ['progress', 'coverage'], 76 | 77 | coverageReporter: { 78 | dir: path.join(__dirname, './coverage'), 79 | reporters: [ 80 | // reporters not supporting the `file` property 81 | {type: 'html'}, 82 | {type: 'lcov', subdir: 'lcov'} 83 | ] 84 | } 85 | 86 | }; 87 | -------------------------------------------------------------------------------- /test/karma.local.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file karma test config using travis 3 | * @author cxtom 4 | */ 5 | 6 | const karmaConfig = require('./karma.conf.js'); 7 | 8 | module.exports = function (config) { 9 | config.set( 10 | Object.assign( 11 | {}, 12 | karmaConfig, 13 | { 14 | // if true, Karma captures browsers, runs the tests and exits 15 | singleRun: false 16 | } 17 | ) 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /test/then.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file waitFor from http://brandonokert.com/2015/08/04/TestingInReact/ 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | 7 | export default function then(callback, timeout) { 8 | setTimeout(callback, timeout > 0 ? timeout : 0); 9 | return {then}; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /test/waitFor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file waitFor from http://brandonokert.com/2015/08/04/TestingInReact/ 3 | * @author cxtom(cxtom2008@gmail.com) 4 | */ 5 | 6 | let waitsInProgress = []; 7 | 8 | export default function waitFor(test, message, done, timeLeft) { 9 | timeLeft = timeLeft === undefined ? 100 : timeLeft; 10 | waitsInProgress.push(setTimeout(() => { 11 | if (timeLeft <= 0) { 12 | throw new Error(message); 13 | } 14 | else if (test()) { 15 | done(); 16 | } 17 | else { 18 | waitFor(test, message, done, timeLeft - 10); 19 | } 20 | }, 10)); 21 | } 22 | 23 | waitFor.clear = () => waitsInProgress.map(clearTimeout); // optionally call this in the beforeEach to ensure rogue tests are not still waiting 24 | -------------------------------------------------------------------------------- /tools/dll.json: -------------------------------------------------------------------------------- 1 | { 2 | "melon": [ 3 | "melon/Button", 4 | "melon/Card", 5 | "melon/Select", 6 | "melon/Progress", 7 | "melon/Slider", 8 | "melon/Zippy", 9 | "melon/BoxGroup", 10 | "melon/Pager", 11 | "melon/SnackBar", 12 | "melon/Toggle", 13 | "melon/Alert", 14 | "melon/Confirm", 15 | "melon/Dialog", 16 | "melon/TextBox", 17 | "melon/Link", 18 | "melon/Title", 19 | "melon/Tooltip", 20 | "melon/Uploader", 21 | "melon/Table", 22 | "melon-core/Form" 23 | ], 24 | "melon-core": [ 25 | "melon-core/Form", 26 | "melon-core/InputComponent" 27 | ], 28 | "numen": [ 29 | "numen/HashLocator" 30 | ], 31 | "lodash": ["lodash.omit"], 32 | "melon-calendar": [ 33 | "melon-calendar/lib/RangeCalendar", 34 | "melon-calendar/lib/UnitCalendar", 35 | "melon-calendar/lib/Calendar" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /tools/webpack.common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file webpack 公共配置 3 | * @author chenxiao07 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const nib = require('nib'); 9 | 10 | module.exports = { 11 | 12 | resolve: { 13 | extensions: ['', '.js', '.styl'], 14 | modulesDirectories: ['node_modules', 'dep'] 15 | }, 16 | 17 | stylus: { 18 | 'use': [nib()], 19 | 'import': ['~nib/lib/nib/index.styl'] 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /tools/webpack.dev.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file webpack 开发配置 3 | * @author chenxiao07 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const webpack = require('webpack'); 9 | const path = require('path'); 10 | const fs = require('fs'); 11 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 12 | 13 | const config = Object.assign({}, require('./webpack.common'), { 14 | 15 | entry: { 16 | index: path.join(__dirname, '../example/index.js') 17 | }, 18 | 19 | module: { 20 | loaders: [{ 21 | test: /\.js?$/, 22 | loaders: [ 23 | 'babel' 24 | ], 25 | exclude: [ 26 | /node_modules/ 27 | ] 28 | }, { 29 | test: /\.styl$/, 30 | loaders: ['style', 'css', 'stylus?paths=node_modules&resolve url&include css'] 31 | }, { 32 | test: /\.(svg|eot|ttf|woff|woff2|jpg|png)(\?.*)?$/, 33 | loader: 'file?name=asset/[name].[ext]' 34 | }, { 35 | test: /\.json(\?.*)?$/, 36 | loader: 'json' 37 | }, { 38 | test: /\.css$/, 39 | loader: 'style!css' 40 | }] 41 | }, 42 | 43 | output: { 44 | path: path.resolve(__dirname, '../asset'), 45 | publicPath: '/', 46 | filename: '[name].js' 47 | }, 48 | 49 | cache: true, 50 | 51 | debug: true, 52 | 53 | devtool: 'eval-source-map', 54 | 55 | plugins: [ 56 | new webpack.DllReferencePlugin({ 57 | context: '.', 58 | manifest: require('../asset/inf-manifest.json') 59 | }), 60 | new webpack.NamedModulesPlugin(), 61 | new webpack.optimize.OccurenceOrderPlugin(), 62 | new webpack.HotModuleReplacementPlugin(), 63 | new HtmlWebpackPlugin({ 64 | inject: true, 65 | templateContent: (function () { 66 | return fs 67 | .readFileSync( 68 | path.join(__dirname, '../example/index.html'), 69 | 'utf8' 70 | ) 71 | .replace(//ig, function ($0, $1) { 72 | return ``; 73 | }); 74 | })() 75 | }), 76 | new webpack.IgnorePlugin(/regenerator|nodent|js\-beautify/, /ajv/), 77 | new webpack.IgnorePlugin(/locale/, /moment/) 78 | ], 79 | devServer: { 80 | host: 'localhost', 81 | port: 8080 82 | } 83 | 84 | }); 85 | 86 | 87 | module.exports = config; 88 | -------------------------------------------------------------------------------- /tools/webpack.dll.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 使用 dll 来创建 inf 包的依赖 3 | * @author leon 4 | */ 5 | 6 | const webpack = require('webpack'); 7 | 8 | module.exports = { 9 | 10 | entry: (function (pkg, mapper) { 11 | 12 | return { 13 | inf: Object 14 | .keys(pkg.dependencies) 15 | .reduce( 16 | function (dependencies, name) { 17 | return dependencies.concat( 18 | mapper[name] ? mapper[name] : name 19 | ); 20 | }, 21 | ['numen/HashLocator'] 22 | ) 23 | }; 24 | 25 | })(require('../package.json'), require('./dll.json')), 26 | 27 | output: { 28 | filename: '[name].dll.js', 29 | path: 'asset', 30 | library: '[name]' 31 | }, 32 | 33 | module: { 34 | loaders: [{ 35 | test: /\.js?$/, 36 | loaders: [ 37 | 'babel?presets[]=es2015,presets[]=react,presets[]=stage-1&cacheDirectory' 38 | ], 39 | exclude: [ 40 | /node_modules/ 41 | ] 42 | }, { 43 | test: /\.styl$/, 44 | loaders: ['style', 'css', 'stylus?paths=node_modules&resolve url'] 45 | }, { 46 | test: /\.(svg|eot|ttf|woff|jpg|png)(\?.*)?$/, 47 | loader: 'file?name=asset/[name].[ext]' 48 | }, { 49 | test: /\.json(\?.*)?$/, 50 | loader: 'json' 51 | }] 52 | }, 53 | 54 | plugins: [ 55 | new webpack.DllPlugin({ 56 | // The path to the manifest file which maps between 57 | // modules included in a bundle and the internal IDs 58 | // within that bundle 59 | path: 'asset/[name]-manifest.json', 60 | // The name of the global variable which the library's 61 | // require function has been assigned to. This must match the 62 | // output.library option above 63 | name: '[name]' 64 | }), 65 | new webpack.optimize.OccurenceOrderPlugin(), 66 | new webpack.IgnorePlugin(/regenerator|nodent|js\-beautify/, /ajv/), 67 | new webpack.IgnorePlugin(/locale/, /moment/) 68 | ] 69 | }; 70 | --------------------------------------------------------------------------------