Browse Source

feat: modal component

yhhu 5 years ago
parent
commit
8d254beb26

+ 2 - 1
.eslintrc

@@ -1,7 +1,8 @@
 {
   "env": {
     "es6": true,
-    "node": true
+    "node": true,
+    "browser": true
   },
   "extends": "airbnb",
   "parser": "babel-eslint",

+ 2 - 1
package.json

@@ -15,7 +15,8 @@
     "classnames": "^2.2.6",
     "prop-types": "^15.6.2",
     "react": "^16.5.2",
-    "react-dom": "^16.5.2"
+    "react-dom": "^16.5.2",
+    "uuid": "^3.3.2"
   },
   "devDependencies": {
     "babel-core": "^6.26.3",

+ 28 - 0
src/components/delayUnmounting.js

@@ -0,0 +1,28 @@
+import React from 'react'
+
+export default function delayUnmounting(Component) {
+  return class extends React.Component {
+    constructor(props) {
+      super(props)
+      const { isMounted } = this.props
+      this.state = {
+        shouldRender: isMounted,
+      }
+    }
+
+    componentDidUpdate(prevProps) {
+      const { isMounted, delayTime } = this.props
+      if (prevProps.isMounted && !isMounted) {
+        setTimeout(() => this.setState({ shouldRender: false }), delayTime)
+      } else if (!prevProps.isMounted && isMounted) {
+        /* eslint-disable react/no-did-update-set-state */
+        this.setState({ shouldRender: true })
+      }
+    }
+
+    render() {
+      const { shouldRender } = this.state
+      return shouldRender ? <Component {...this.props} /> : null
+    }
+  }
+}

+ 41 - 26
src/components/index/DatePicker.js

@@ -1,53 +1,68 @@
 import React, { Component } from 'react'
 import PropTypes from 'prop-types'
-import classNames from 'classnames'
 import Styles from './picker.css'
 import { getDateFormatFromSepecificDate } from '../../utils'
+import Modal from './Modal'
 
 class DatePicker extends Component {
   constructor(props) {
     super(props)
+    const { defaultDate } = this.props
     this.state = {
-      inputFocus: false,
+      value: defaultDate,
+      showModal: false,
     }
 
-    this.onInputFocus = this.onInputFocus.bind(this)
-    this.onInputBlur = this.onInputBlur.bind(this)
+    this.onModalOpen = this.onModalOpen.bind(this)
     this.onInputChange = this.onInputChange.bind(this)
+    this.onInputClear = this.onInputClear.bind(this)
+    this.onModalClose = this.onModalClose.bind(this)
   }
 
-  onInputFocus() {
-    this.setState({ inputFocus: true })
+  onModalOpen() {
+    this.setState({ showModal: true })
+  }
+
+  onModalClose() {
+    this.setState({ showModal: false })
   }
 
   onInputChange() {
-    console.log('change')
+    console.log('onInputChange')
   }
 
-  onInputBlur() {
-    this.setState({ inputFocus: false })
+  onInputClear() {
+    this.setState({ value: '', showModal: false })
   }
 
   render() {
-    const { inline, defaultDate } = this.props
-    const { inputFocus } = this.state
+    const { inline } = this.props
+    const { value, showModal } = this.state
 
     return (
-      <div
-        className={Styles.container}
-        style={inline ? { display: 'inline-block' } : {}}
-      >
-        <input
-          type="text"
-          className={Styles.input}
-          value={defaultDate}
-          onFocus={this.onInputFocus}
-          onBlur={this.onInputBlur}
-          onChange={this.onInputChange}
-        />
-        <i className={Styles.calendar} />
-        <i className={Styles.close} />
-        <div className={classNames(Styles.line, { [Styles.inputFocus]: inputFocus })} />
+      <div className={Styles.wrapper}>
+        <div
+          className={Styles.container}
+          style={inline ? { display: 'inline-block' } : {}}
+        >
+          <input
+            type="text"
+            placeholder="选择日期"
+            className={Styles.input}
+            value={value}
+            onChange={this.onInputChange}
+            onFocus={this.onModalOpen}
+            onBlur={this.onModalClose}
+          />
+          <i className={Styles.calendar} />
+          <i
+            className={Styles.close}
+            onClick={this.onInputClear}
+            role="presentation"
+          />
+          <div className={Styles.line} />
+        </div>
+        <Modal isMounted={showModal} delayTime={200} />
       </div>
     )
   }

+ 27 - 0
src/components/index/Modal.js

@@ -0,0 +1,27 @@
+import React from 'react'
+import classNames from 'classnames'
+import PropTypes from 'prop-types'
+import Styles from './modal.css'
+import delayUnmounting from '../delayUnmounting'
+
+const Modal = ({ isMounted }) => (
+  <React.Fragment>
+    <div className={classNames(Styles.container, {
+      [Styles.in]: isMounted,
+      [Styles.out]: !isMounted,
+    })}
+    >
+      modal
+    </div>
+  </React.Fragment>
+)
+
+Modal.defaultProps = {
+  isMounted: false,
+}
+
+Modal.propTypes = {
+  isMounted: PropTypes.bool,
+}
+
+export default delayUnmounting(Modal)

+ 65 - 0
src/components/index/modal.css

@@ -0,0 +1,65 @@
+@keyframes bounceIn {
+  0% { 
+    transform: scale(0.1);
+    opacity: 0;
+  }
+  60% {
+    transform: scale(1.2);
+    opacity: 1;
+  }
+  100% {
+    transform: scale(1);
+  }
+}
+
+@keyframes bounceOut {
+  20% {
+    transform: scale(1.2);
+  }
+  100% {
+    transform: scale(0);
+    opacity: 0;
+  }
+}
+
+@keyframes slideDownIn {
+  0% {
+    opacity: 0;
+    transform-origin: 0% 0%;
+    transform: scaleY(.8);
+  }
+  100% {
+    opacity: 1;
+    transform-origin: 0% 0%;
+    transform: scaleY(1);
+  }
+}
+
+@keyframes slideDownOut {
+  0% {
+    opacity: 1;
+    transform-origin: 0% 0%;
+    transform: scaleY(1);
+  }
+  100% {
+    opacity: 0;
+    transform-origin: 0% 0%;
+    transform: scaleY(.8);
+  }
+}
+
+.container {
+  width: 280px;
+  height: 200px;
+  position: absolute;
+  top: 0;
+  left: 0;
+  background-color: aqua;
+  z-index: 1;
+}
+.in {
+  animation: slideDownIn 0.2s;
+}
+.out {
+  animation: slideDownOut 0.2s;
+}

+ 37 - 6
src/components/index/picker.css

@@ -1,12 +1,15 @@
-.container {
+.wrapper {
+  font-family: "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
   min-width: 170px;
-  cursor: text;
-  position: relative;
   font-size: 14px;
+  position: relative;
+}
+.container {
+  width: 100%;
+  cursor: text;
   overflow: hidden;
 }
 .input {
-  font-family: "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
   width: 100%;
   height: 32px;
   margin: 0;
@@ -26,10 +29,16 @@
   left: 0;
   border-bottom: 1px solid #1890ff;
   transform: translateX(-100%);
-  transition: transform .15s cubic-bezier(0.17, 1.05, 0.9,-0.15);
+  opacity: 0;
+  will-change: transfrom;
+  transition: transform .1s;
+}
+.container:hover {
+  border-bottom: none;
 }
-.input-focus {
+.container:hover > .line {
   transform: translateX(0);
+  opacity: 1;
 }
 .calendar {
   position: absolute;
@@ -41,4 +50,26 @@
   background-image: url('../../static/icon-calendar.svg');
   background-position: center; 
   background-size: 16px 16px;
+  opacity: 1;
+  transition: opacity .2s;
+}
+.close {
+  position: absolute;
+  margin-top: 8px;
+  right: 11px;
+  display: inline-block;
+  width: 16px;
+  height: 16px;
+  background-image: url('../../static/icon-close.svg');
+  background-position: center; 
+  background-size: 16px 16px;
+  cursor: pointer;
+  opacity: 0;
+  transition: opacity .2s;
+}
+.container:hover > .calendar {
+  opacity: 0;
+}
+.container:hover > .close {
+  opacity: 1;
 }

+ 0 - 2
src/index.js

@@ -5,6 +5,4 @@ import DatePicker from './components/index/DatePicker'
 import './styles/index.css'
 
 const App = () => <DatePicker />
-
-/* eslint-disable no-undef */
 ReactDOM.render(<App />, document.getElementById('container'))

File diff suppressed because it is too large
+ 1 - 0
src/static/icon-close.svg