├── CHANGELOG.md ├── .eslintignore ├── .gitignore ├── components ├── react │ ├── Button │ │ ├── FloatingActionButton │ │ │ ├── fab.css │ │ │ └── index.jsx │ │ ├── IconButton │ │ │ ├── button.css │ │ │ └── index.jsx │ │ ├── Button │ │ │ └── index.jsx │ │ ├── ButtonGroup │ │ │ ├── group.css │ │ │ └── index.jsx │ │ ├── ClassicButton │ │ │ ├── index.jsx │ │ │ └── button.css │ │ └── BaseButton │ │ │ ├── index.jsx │ │ │ └── button.css │ ├── Form │ │ ├── InputGroup │ │ │ ├── group.css │ │ │ └── index.jsx │ │ ├── SelectorV2 │ │ │ ├── Option.jsx │ │ │ ├── selector.css │ │ │ └── index.jsx │ │ ├── InputGroupV2 │ │ │ ├── index.jsx │ │ │ └── group.css │ │ ├── Textarea │ │ │ ├── textarea.css │ │ │ └── index.jsx │ │ ├── Selector │ │ │ ├── index.jsx │ │ │ └── selector.css │ │ └── Input │ │ │ ├── input.css │ │ │ └── index.jsx │ ├── Modal │ │ ├── ShortMessage │ │ │ ├── message.css │ │ │ └── index.jsx │ │ ├── BaseModal │ │ │ ├── modal.css │ │ │ └── index.jsx │ │ ├── Loading │ │ │ ├── animation.css │ │ │ ├── index.jsx │ │ │ └── loading.css │ │ ├── PortalModal │ │ │ └── index.jsx │ │ └── PortalModalWithState │ │ │ └── index.jsx │ ├── ClassicText │ │ ├── index.jsx │ │ └── text.css │ ├── Tipso │ │ ├── Tipso │ │ │ └── index.jsx │ │ └── BaseTipso │ │ │ ├── index.jsx │ │ │ └── tipso.css │ ├── Slider │ │ ├── ProgressBar.jsx │ │ ├── slider.css │ │ └── Dragger.jsx │ ├── Card │ │ ├── CardGroup │ │ │ ├── group.css │ │ │ └── index.jsx │ │ ├── ClassicCard │ │ │ ├── index.jsx │ │ │ └── card.css │ │ └── InfoCard │ │ │ ├── card.css │ │ │ └── index.jsx │ ├── Switcher │ │ ├── index.jsx │ │ └── switcher.css │ ├── Label │ │ ├── index.jsx │ │ └── label.css │ └── Dropdown │ │ ├── index.jsx │ │ └── dropdown.css ├── raw.js ├── shared │ ├── styles │ │ ├── border.css │ │ ├── z-index.css │ │ ├── font.css │ │ ├── shadow.css │ │ └── color.css │ ├── utils │ │ ├── darg.js │ │ ├── validator.js │ │ ├── icons.js │ │ └── helper.js │ └── components │ │ ├── AnimationComponent.jsx │ │ └── OutsideClickHandler.jsx ├── raw │ ├── Modal │ │ └── ShortMessage │ │ │ ├── message.css │ │ │ └── index.js │ └── Message │ │ ├── message.css │ │ └── index.js └── index.js ├── screenshots ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── button.gif ├── input.gif ├── range.gif ├── tipso.gif └── switcher.gif ├── .babelrc ├── .npmignore ├── postcss.config.js ├── stories ├── Card.js ├── Tipso.js ├── Slider.js ├── Message.js ├── ShortMessage.js ├── Form.js ├── Label.js ├── Button.js ├── Dropdown.js ├── Switcher.js ├── CustomSelector.js └── Loading.js ├── .storybook ├── preview-head.html ├── config.js └── webpack.config.js ├── LICENSE ├── examples ├── SwitcherWrapper.jsx ├── CustomSelectorWrapper.jsx ├── LoadingWrapper.jsx ├── SliderWrapper.jsx ├── MessageWrapper.jsx ├── LabelWrapper.jsx ├── ShortMessageWrapper.jsx ├── shared │ └── styles.css ├── DropdownWrapper.jsx ├── CardWrapper.jsx └── TipsoWrapper.jsx ├── README.md ├── .eslintrc.json └── package.json /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/** 2 | lib/** 3 | node_modules/** 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /lib 3 | /.out 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /components/react/Button/FloatingActionButton/fab.css: -------------------------------------------------------------------------------- 1 | 2 | .fab { 3 | margin: 0; 4 | } -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecmadao/light-ui/HEAD/screenshots/1.png -------------------------------------------------------------------------------- /screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecmadao/light-ui/HEAD/screenshots/2.png -------------------------------------------------------------------------------- /screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecmadao/light-ui/HEAD/screenshots/3.png -------------------------------------------------------------------------------- /screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecmadao/light-ui/HEAD/screenshots/4.png -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "stage-0", 5 | "react" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /screenshots/button.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecmadao/light-ui/HEAD/screenshots/button.gif -------------------------------------------------------------------------------- /screenshots/input.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecmadao/light-ui/HEAD/screenshots/input.gif -------------------------------------------------------------------------------- /screenshots/range.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecmadao/light-ui/HEAD/screenshots/range.gif -------------------------------------------------------------------------------- /screenshots/tipso.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecmadao/light-ui/HEAD/screenshots/tipso.gif -------------------------------------------------------------------------------- /screenshots/switcher.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecmadao/light-ui/HEAD/screenshots/switcher.gif -------------------------------------------------------------------------------- /components/react/Form/InputGroup/group.css: -------------------------------------------------------------------------------- 1 | .input { 2 | margin-left: 0; 3 | margin-right: 0; 4 | } 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /components/ 2 | /examples 3 | /stories 4 | /webpack 5 | /.storybook 6 | /CHANGELOG.md 7 | /screenshots 8 | -------------------------------------------------------------------------------- /components/raw.js: -------------------------------------------------------------------------------- 1 | 2 | export Message from './raw/Message'; 3 | export ShortMessage from './raw/Modal/ShortMessage'; 4 | -------------------------------------------------------------------------------- /components/shared/styles/border.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --radius-small: 2px; 3 | --radius-mid: 3px; 4 | --radius: 4px; 5 | --radius-half: 50%; 6 | --radius-all: 100%; 7 | } 8 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-import': {}, 4 | 'postcss-cssnext': { 5 | autoprefixer: { 6 | browsers: "ie >= 9" 7 | } 8 | } 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /stories/Card.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import CardWrapper from '../examples/CardWrapper'; 4 | 5 | storiesOf('Card', module) 6 | .add('basical', () => ( 7 | 8 | )); 9 | -------------------------------------------------------------------------------- /components/shared/styles/z-index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* ===== zIndex ===== */ 3 | --zIndexHidden: -9; 4 | --zIndex0: -1; 5 | --zIndex1: 1; 6 | --zIndex9: 9; 7 | --zIndex99: 99; 8 | --zIndex999: 999; 9 | --zIndex9999: 9999; 10 | } 11 | -------------------------------------------------------------------------------- /stories/Tipso.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import TipsoWrapper from '../examples/TipsoWrapper'; 4 | 5 | storiesOf('Tipso', module) 6 | .add('basical', () => ( 7 | 8 | )); 9 | -------------------------------------------------------------------------------- /stories/Slider.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import SliderWrapper from '../examples/SliderWrapper'; 4 | 5 | storiesOf('Slider', module) 6 | .add('basical', () => ( 7 | 8 | )); 9 | -------------------------------------------------------------------------------- /stories/Message.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import MessageWrapper from '../examples/MessageWrapper'; 4 | 5 | storiesOf('Message', module) 6 | .add('basical', () => ( 7 | 8 | )); 9 | -------------------------------------------------------------------------------- /stories/ShortMessage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import ShortMessageWrapper from '../examples/ShortMessageWrapper'; 4 | 5 | storiesOf('ShortMessage', module) 6 | .add('basical', () => ( 7 | 8 | )); 9 | -------------------------------------------------------------------------------- /stories/Form.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import FormWrapper from '../examples/FormWrapper'; 4 | 5 | storiesOf('Form', module) 6 | .add('basical', () => ( 7 | 8 | )) 9 | .add('disabled', () => ( 10 | 11 | )); 12 | -------------------------------------------------------------------------------- /stories/Label.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import LabelWrapper from '../examples/LabelWrapper'; 4 | 5 | storiesOf('Label', module) 6 | .add('basical', () => ( 7 | 8 | )) 9 | .add('disabled', () => ( 10 | 11 | )); 12 | -------------------------------------------------------------------------------- /stories/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import ButtonWrapper from '../examples/ButtonWrapper'; 4 | 5 | storiesOf('Button', module) 6 | .add('basical', () => ( 7 | 8 | )) 9 | .add('disabled', () => ( 10 | 11 | )); 12 | -------------------------------------------------------------------------------- /components/react/Button/IconButton/button.css: -------------------------------------------------------------------------------- 1 | @import "../../../shared/styles/font.css"; 2 | @import '../../../shared/styles/color.css'; 3 | 4 | .iconButton { 5 | width: 30px; 6 | height: 30px; 7 | padding: 0; 8 | transition: background-color 0.2s; 9 | composes: baseSubText; 10 | 11 | & i { 12 | color: var(--gray); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stories/Dropdown.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { storiesOf } from '@storybook/react'; 4 | import DropdownWrapper from '../examples/DropdownWrapper'; 5 | 6 | storiesOf('Dropdown', module) 7 | .add('basical', () => ( 8 | 9 | )) 10 | .add('disabled', () => ( 11 | 12 | )); 13 | -------------------------------------------------------------------------------- /stories/Switcher.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import SwitcherWrapper from '../examples/SwitcherWrapper'; 4 | 5 | storiesOf('Switcher', module) 6 | .add('basical', () => ( 7 | 8 | )) 9 | .add('disabled', () => ( 10 | 11 | )); 12 | -------------------------------------------------------------------------------- /stories/CustomSelector.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { storiesOf } from '@storybook/react'; 4 | import CustomSelectorWrapper from '../examples/CustomSelectorWrapper'; 5 | 6 | storiesOf('CustomSelector', module) 7 | .add('basical', () => ( 8 | 9 | )) 10 | .add('disabled', () => ( 11 | 12 | )); 13 | -------------------------------------------------------------------------------- /components/shared/styles/font.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* ===== font size ===== */ 3 | --baseTextSizeM: 12px; 4 | --baseTextSizeL: 14px; 5 | --baseTextSizeXL: 16px; 6 | } 7 | 8 | .baseText { 9 | font-size: var(--baseTextSizeL); 10 | } 11 | 12 | .baseSubText { 13 | font-size: var(--baseTextSizeM); 14 | } 15 | 16 | @media (max-width: 800px) { 17 | .baseText { 18 | font-size: var(--baseTextSizeM); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /components/raw/Modal/ShortMessage/message.css: -------------------------------------------------------------------------------- 1 | @import "../../../shared/styles/z-index.css"; 2 | @import '../../../shared/styles/border.css'; 3 | 4 | .messageComponent { 5 | position: fixed; 6 | top: 50%; 7 | left: 50%; 8 | transform: translate(-50%, -50%); 9 | background-color: rgba(0, 0, 0, 0.7); 10 | border-radius: var(--radius); 11 | padding: 23px 30px; 12 | color: #fff; 13 | font-size: 14px; 14 | z-index: var(--zIndex9999); 15 | } 16 | -------------------------------------------------------------------------------- /components/react/Modal/ShortMessage/message.css: -------------------------------------------------------------------------------- 1 | @import "../../../shared/styles/z-index.css"; 2 | @import "../../../shared/styles/border.css"; 3 | 4 | .messageModalWrapper { 5 | position: fixed; 6 | top: 50%; 7 | left: 50%; 8 | transform: translate(-50%, -50%); 9 | transition: opacity 0.3s; 10 | background-color: rgba(0, 0, 0, 0.7); 11 | border-radius: var(--radius); 12 | padding: 23px 30px; 13 | color: #fff; 14 | font-size: 16px; 15 | opacity: 0; 16 | z-index: var(--zIndex999); 17 | 18 | &.active { 19 | opacity: 1; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /stories/Loading.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import LoadingWrapper from '../examples/LoadingWrapper'; 4 | 5 | storiesOf('Loading', module) 6 | .add('No closeable', () => ( 7 | 8 | )) 9 | .add('Closeable, without callback', () => ( 10 | 11 | )) 12 | .add('Closeable, has callback', () => ( 13 | 14 | )) 15 | .add('Custom style', () => ( 16 | 17 | )); 18 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 26 | -------------------------------------------------------------------------------- /components/shared/styles/shadow.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* ===== shadow ===== */ 3 | --shadow1: 1px 1px 2px 0 rgba(0, 0, 0, 0.1); 4 | --shadow2: 1px 1px 4px 0 rgba(0, 0, 0, 0.1); 5 | --shadow3: 1px 1px 8px 0 rgba(0, 0, 0, 0.1); 6 | --shadow4: 2px 2px 8px 0 rgba(0, 0, 0, 0.1); 7 | --shadow5: 2px 2px 8px 0 rgba(0, 0, 0, 0.2); 8 | --shadow6: 2px 2px 8px 0 rgba(0, 0, 0, 0.3); 9 | --shadow7: 2px 2px 8px 0 rgba(0, 0, 0, 0.5); 10 | 11 | --shadowCard: 0 8px 10px 0 rgba(0, 0, 0, 0.12), 0 0 10px 0 rgba(0, 0, 0, 0.08); 12 | 13 | --shadowInput: 1px 1px 4px 0 rgba(0, 0, 0, 0.10), 0 0 4px 0 rgba(0, 0, 0, 0.08); 14 | --shadowInputHover: 2px 2px 6px 0 rgba(0, 0, 0, 0.10), 2px 2px 6px 0 rgba(0, 0, 0, 0.08); 15 | } 16 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import { configure, addDecorator } from '@storybook/react'; 3 | 4 | addDecorator((story) => { 5 | moment.locale('zh-cn'); 6 | return (story()); 7 | }); 8 | 9 | function loadStories() { 10 | require('../stories/Button'); 11 | require('../stories/Form'); 12 | require('../stories/ShortMessage'); 13 | require('../stories/Message'); 14 | require('../stories/Tipso'); 15 | require('../stories/Loading'); 16 | require('../stories/Card'); 17 | require('../stories/Slider'); 18 | require('../stories/Label'); 19 | require('../stories/Switcher'); 20 | require('../stories/CustomSelector'); 21 | require('../stories/Dropdown'); 22 | } 23 | 24 | configure(loadStories, module); 25 | -------------------------------------------------------------------------------- /components/react/ClassicText/index.jsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react' 3 | import cx from 'classnames' 4 | import styles from './text.css' 5 | 6 | const ClassicText = (props) => { 7 | const { 8 | className, 9 | theme = 'light', 10 | text = '', 11 | onTransitionEnd, 12 | onClick = Function.prototype 13 | } = props 14 | 15 | return ( 16 |
25 |
{text}
26 |
{text}
27 |
28 | ) 29 | } 30 | 31 | export default ClassicText 32 | -------------------------------------------------------------------------------- /components/react/Modal/BaseModal/modal.css: -------------------------------------------------------------------------------- 1 | @import "../../../shared/styles/z-index.css"; 2 | 3 | .modalComponent { 4 | position: fixed; 5 | background-color: transparent; 6 | opacity: 0; 7 | top: 0; 8 | left: 0; 9 | width: 100%; 10 | height: 100%; 11 | display: flex; 12 | flex-direction: column; 13 | justify-content: center; 14 | align-items: center; 15 | z-index: var(--zIndexHidden); 16 | transition: all 400ms cubic-bezier(0.165, 0.84, 0.44, 1); 17 | 18 | & .modalWrapper { 19 | position: absolute; 20 | top: 0; 21 | left: 0; 22 | width: 100%; 23 | height: 100%; 24 | z-index: -1; 25 | } 26 | 27 | &.active { 28 | opacity: 1; 29 | z-index: var(--zIndex999); 30 | background-color: rgba(0, 0, 0, 0.2); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /components/react/Tipso/Tipso/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import BaseTipso from '../BaseTipso'; 4 | 5 | const Tipso = (props) => { 6 | const { children } = props; 7 | const baseProps = Object.assign({}, props); 8 | 9 | delete baseProps.children; 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | }; 16 | 17 | Tipso.propTypes = { 18 | trigger: PropTypes.string, 19 | wrapperStyle: PropTypes.object, 20 | children: PropTypes.oneOfType([ 21 | PropTypes.node, 22 | PropTypes.element, 23 | PropTypes.string, 24 | PropTypes.array 25 | ]), 26 | }; 27 | 28 | Tipso.defaultProps = { 29 | trigger: 'hover', 30 | wrapperStyle: {}, 31 | children:
32 | }; 33 | 34 | export default Tipso; 35 | -------------------------------------------------------------------------------- /components/react/Slider/ProgressBar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | import PropTypes from 'prop-types'; 4 | import styles from './slider.css'; 5 | 6 | const ProgressBar = (props) => { 7 | const { left, right, color } = props; 8 | const barClass = cx( 9 | styles.progressBar, 10 | color && styles[`bar-${color}`] 11 | ); 12 | return ( 13 |
20 | ); 21 | }; 22 | 23 | ProgressBar.propTypes = { 24 | left: PropTypes.number, 25 | right: PropTypes.number, 26 | color: PropTypes.string, 27 | }; 28 | 29 | ProgressBar.defaultProps = { 30 | left: 0, 31 | right: 0, 32 | color: 'green' 33 | }; 34 | 35 | export default ProgressBar; 36 | -------------------------------------------------------------------------------- /components/shared/utils/darg.js: -------------------------------------------------------------------------------- 1 | 2 | const mousePosition = (e) => { 3 | e = e || window.event; 4 | const xPos = e.pageX 5 | ? e.pageX 6 | : e.clientX + document.body.scrollLeft - document.body.clientLeft; 7 | const yPos = e.pageY 8 | ? e.pageY 9 | : e.clientY + document.body.scrollTop - document.body.clientTop; 10 | return { 11 | x: xPos, 12 | y: yPos 13 | }; 14 | }; 15 | 16 | const disableMouseDown = (e) => { 17 | const event = e || window.event; 18 | event.preventDefault(); 19 | event.stopPropagation(); 20 | }; 21 | 22 | const getStandardAbsolutePosition = (position, minPosition, maxPosition) => 23 | Math.min( 24 | maxPosition, 25 | Math.max( 26 | position, 27 | minPosition 28 | ) 29 | ); 30 | 31 | export default { 32 | mousePosition, 33 | disableMouseDown, 34 | getStandardAbsolutePosition 35 | }; 36 | -------------------------------------------------------------------------------- /components/react/Modal/Loading/animation.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes bounce { 2 | 0% { 3 | transform: scale(0); 4 | } 5 | 6 | 50% { 7 | transform: scale(1); 8 | } 9 | 10 | 100% { 11 | transform: scale(0); 12 | } 13 | } 14 | 15 | @keyframes bounce { 16 | 0% { 17 | transform: scale(0); 18 | } 19 | 20 | 50% { 21 | transform: scale(1); 22 | } 23 | 24 | 100% { 25 | transform: scale(0); 26 | } 27 | } 28 | 29 | @keyframes rotate { 30 | 0% { 31 | transform: rotate(0deg) scale(1); 32 | } 33 | 50% { 34 | transform: rotate(180deg) scale(0.6); 35 | } 36 | 100% { 37 | transform: rotate(360deg) scale(1); 38 | } 39 | } 40 | 41 | @-webkit-keyframes rotate { 42 | 0% { 43 | transform: rotate(0deg) scale(1); 44 | } 45 | 50% { 46 | transform: rotate(180deg) scale(0.6); 47 | } 48 | 100% { 49 | transform: rotate(360deg) scale(1); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /components/react/Form/SelectorV2/Option.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | import styles from './selector.css'; 4 | 5 | class Option extends React.Component { 6 | onClick(e) { 7 | const { onClick, id, isActive, disabled } = this.props; 8 | !disabled && !isActive && onClick && onClick(id); 9 | e.stopPropagation(); 10 | return false; 11 | } 12 | 13 | render() { 14 | const { value, isActive, disabled, className } = this.props; 15 | const optionClass = cx( 16 | styles.option, 17 | isActive && styles.optionActive, 18 | disabled && styles.optionDisabled, 19 | className 20 | ); 21 | return ( 22 |
26 |
27 | {value} 28 |
29 |
30 | ); 31 | } 32 | } 33 | 34 | export default Option; 35 | -------------------------------------------------------------------------------- /components/react/Card/CardGroup/group.css: -------------------------------------------------------------------------------- 1 | @import "../../../shared/styles/shadow.css"; 2 | @import '../../../shared/styles/color.css'; 3 | @import '../../../shared/styles/border.css'; 4 | 5 | .cardsContainer { 6 | flex: 1; 7 | display: inline-flex; 8 | flex-direction: row; 9 | background-color: var(--white); 10 | border-bottom: 1px solid var(--gray-light); 11 | 12 | &:last-child { 13 | border-bottom: 1px solid transparent; 14 | margin: 10px 0; 15 | } 16 | } 17 | 18 | .cardsContainerWrapper { 19 | flex-direction: column; 20 | margin: 10px 0; 21 | 22 | &.material { 23 | border-radius: var(--radius-small); 24 | } 25 | } 26 | 27 | .cardsChild { 28 | display: flex; 29 | margin: 0; 30 | background-color: transparent; 31 | 32 | &:last-child { 33 | margin: 0; 34 | } 35 | } 36 | 37 | .material { 38 | border-bottom: 1px solid transparent; 39 | box-shadow: var(--shadow3); 40 | } 41 | 42 | .card { 43 | &:not(:last-child) { 44 | border-right: 1px solid var(--gray-light); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /components/shared/utils/validator.js: -------------------------------------------------------------------------------- 1 | 2 | import validator from 'validator'; 3 | 4 | const email = (value, options = {}) => validator.isEmail(value); 5 | 6 | const phone = (value, options = {}) => validator.isMobilePhone(value, options.local || 'zh-CN'); 7 | 8 | const empty = (value, options = {}) => validator.isEmpty(value); 9 | 10 | const number = (value, options = {}) => validator.isInt(value, { 11 | min: parseInt(options.min || 0, 10), 12 | max: parseInt(options.max || 99999, 10) 13 | }); 14 | 15 | const url = (value, options = {}) => validator.isURL(value); 16 | 17 | const string = (value, options = {}) => validator.isByteLength(value, { 18 | min: parseInt(options.min || 1, 10), 19 | max: parseInt(options.max || 999, 10), 20 | }); 21 | 22 | const textarea = (value, options = {}) => validator.isByteLength(value, { 23 | min: options.min || 0, 24 | max: options.max || 999 25 | }); 26 | 27 | export default { 28 | url, 29 | email, 30 | phone, 31 | empty, 32 | string, 33 | number, 34 | textarea, 35 | }; 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ecmadao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /components/shared/styles/color.css: -------------------------------------------------------------------------------- 1 | @import 'open-color/open-color.css'; 2 | 3 | :root { 4 | /* ===== colors ===== */ 5 | 6 | --white: var(--oc-white); 7 | --black: var(--oc-black); 8 | 9 | /* ====== gray ====== */ 10 | --gray-light-deep: var(--oc-gray-1); 11 | --gray-light: var(--oc-gray-3); 12 | --gray: var(--oc-gray-5); 13 | --gray-dark: var(--oc-gray-7); 14 | --gray-dark-deep: var(--oc-gray-9); 15 | 16 | /* ====== green ====== */ 17 | --green-light-deep: var(--oc-green-1); 18 | --green-light: var(--oc-green-3); 19 | --green: var(--oc-green-5); 20 | --green-dark: var(--oc-green-7); 21 | --green-dark-deep: var(--oc-green-9); 22 | 23 | /* ====== blue ====== */ 24 | --blue-light-deep: var(--oc-blue-1); 25 | --blue-light: var(--oc-blue-3); 26 | --blue: var(--oc-blue-5); 27 | --blue-dark: var(--oc-blue-7); 28 | --blue-dark-deep: var(--oc-blue-9); 29 | 30 | /* ====== red ====== */ 31 | --red-light-deep: var(--oc-red-1); 32 | --red-light: var(--oc-red-3); 33 | --red: var(--oc-red-5); 34 | --red-dark: var(--oc-red-7); 35 | --red-dark-deep: var(--oc-red-9); 36 | 37 | --bg: #F7F8F9; 38 | --bgDark: #252525; 39 | --bgDeep: #eaeaea; 40 | --flatCard: #F4F4F4; 41 | --flatCardBorder: #D5D5D5; 42 | } 43 | -------------------------------------------------------------------------------- /components/react/Modal/BaseModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | import PropTypes from 'prop-types'; 4 | import styles from './modal.css'; 5 | 6 | class BaseModal extends React.Component { 7 | onClose() { 8 | const {onClose} = this.props; 9 | onClose && onClose(); 10 | } 11 | 12 | render() { 13 | const { children, showModal, className } = this.props; 14 | const modalClass = cx( 15 | styles.modalComponent, 16 | showModal && styles.active, 17 | className 18 | ); 19 | return ( 20 |
21 |
25 | {children} 26 |
27 | ); 28 | } 29 | } 30 | 31 | BaseModal.propTypes = { 32 | showModal: PropTypes.bool, 33 | onClose: PropTypes.func, 34 | className: PropTypes.string, 35 | children: PropTypes.oneOfType([ 36 | PropTypes.node, 37 | PropTypes.element, 38 | PropTypes.string, 39 | PropTypes.array 40 | ]), 41 | }; 42 | 43 | BaseModal.defaultProps = { 44 | showModal: true, 45 | onClose: Function.prototype, 46 | className: '', 47 | children:
48 | }; 49 | 50 | export default BaseModal; 51 | -------------------------------------------------------------------------------- /components/react/Button/IconButton/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | import PropTypes from 'prop-types'; 4 | import BaseButton from '../BaseButton'; 5 | import styles from './button.css'; 6 | 7 | const IconButton = (props) => { 8 | const { icon, className } = props; 9 | const baseProps = Object.assign({}, props); 10 | 11 | const iconElement = typeof icon === 'string' 12 | ? (