Browse Source

feat:增加popup组件

yangkaixuan 6 years ago
parent
commit
c2c63c2f3a

+ 11 - 1
src/config.json

@@ -492,6 +492,16 @@
       "sort": "3",
       "showDemo": true,
       "author": "zhenyulei"
+    },
+    {
+      "version": "1.0.0",
+      "name": "Popup",
+      "chnName": "弹出层",
+      "desc": "弹出层",
+      "type": "component",
+      "sort": "5",
+      "showDemo": true,
+      "author": "杨凯旋"
     }
   ]
-}
+}

+ 99 - 97
src/nutui.js

@@ -1,88 +1,88 @@
-import { version } from '../package.json';
-import { packages as pkgList } from './config.json';
-import { locale } from './locales';
-import Cell from './packages/cell/index.js';
-import './packages/cell/cell.scss';
-import Dialog from './packages/dialog/index.js';
-import './packages/dialog/dialog.scss';
-import Icon from './packages/icon/index.js';
-import './packages/icon/icon.scss';
-import Toast from './packages/toast/index.js';
-import './packages/toast/toast.scss';
-import ActionSheet from './packages/actionsheet/index.js';
-import './packages/actionsheet/actionsheet.scss';
-import Tab from './packages/tab/index.js';
-import './packages/tab/tab.scss';
-import TabPanel from './packages/tabpanel/index.js';
-import './packages/tabpanel/tabpanel.scss';
-import TabBar from './packages/tabbar/index.js';
-import './packages/tabbar/tabbar.scss';
-import Calendar from './packages/calendar/index.js';
-import './packages/calendar/calendar.scss';
-import DatePicker from './packages/datepicker/index.js';
-import './packages/datepicker/datepicker.scss';
-import NavBar from './packages/navbar/index.js';
-import './packages/navbar/navbar.scss';
-import NoticeBar from './packages/noticebar/index.js';
-import './packages/noticebar/noticebar.scss';
-import Switch from './packages/switch/index.js';
-import './packages/switch/switch.scss';
-import Slider from './packages/slider/index.js';
-import './packages/slider/slider.scss';
-import Range from './packages/range/index.js';
-import './packages/range/range.scss';
-import Picker from './packages/picker/index.js';
-import './packages/picker/picker.scss';
-import Progress from './packages/progress/index.js';
-import './packages/progress/progress.scss';
-import Price from './packages/price/index.js';
-import './packages/price/price.scss';
-import Flex from './packages/flex/index.js';
-import './packages/flex/flex.scss';
-import Col from './packages/col/index.js';
-import './packages/col/col.scss';
-import Row from './packages/row/index.js';
-import './packages/row/row.scss';
-import Steps from './packages/steps/index.js';
-import './packages/steps/steps.scss';
-import Button from './packages/button/index.js';
-import './packages/button/button.scss';
-import Badge from './packages/badge/index.js';
-import './packages/badge/badge.scss';
-import Rate from './packages/rate/index.js';
-import './packages/rate/rate.scss';
-import Swiper from './packages/swiper/index.js';
-import './packages/swiper/swiper.scss';
-import Menu from './packages/menu/index.js';
-import './packages/menu/menu.scss';
-import Stepper from './packages/stepper/index.js';
-import './packages/stepper/stepper.scss';
-import ButtonGroup from './packages/buttongroup/index.js';
-import './packages/buttongroup/buttongroup.scss';
-import SearchBar from './packages/searchbar/index.js';
-import './packages/searchbar/searchbar.scss';
-import ImagePicker from './packages/imagepicker/index.js';
-import './packages/imagepicker/imagepicker.scss';
-import Radio from './packages/radio/index.js';
-import './packages/radio/radio.scss';
-import RadioGroup from './packages/radiogroup/index.js';
-import './packages/radiogroup/radiogroup.scss';
-import CheckBox from './packages/checkbox/index.js';
-import './packages/checkbox/checkbox.scss';
-import CheckBoxGroup from './packages/checkboxgroup/index.js';
-import './packages/checkboxgroup/checkboxgroup.scss';
-import ShortPassword from './packages/shortpassword/index.js';
-import './packages/shortpassword/shortpassword.scss';
-import Skeleton from './packages/skeleton/index.js';
-import './packages/skeleton/skeleton.scss';
-import Scroller from './packages/scroller/index.js';
-import './packages/scroller/scroller.scss';
-import BackTop from './packages/backtop/index.js';
-import './packages/backtop/backtop.scss';
-import CountDown from './packages/countdown/index.js';
-import './packages/countdown/countdown.scss';
-import InfiniteLoading from './packages/infiniteloading/index.js';
-import './packages/infiniteloading/infiniteloading.scss';
+import { version } from "../package.json";
+import { packages as pkgList } from "./config.json";
+import { locale } from "./locales";
+import Cell from "./packages/cell/index.js";
+import "./packages/cell/cell.scss";
+import Dialog from "./packages/dialog/index.js";
+import "./packages/dialog/dialog.scss";
+import Icon from "./packages/icon/index.js";
+import "./packages/icon/icon.scss";
+import Toast from "./packages/toast/index.js";
+import "./packages/toast/toast.scss";
+import ActionSheet from "./packages/actionsheet/index.js";
+import "./packages/actionsheet/actionsheet.scss";
+import Tab from "./packages/tab/index.js";
+import "./packages/tab/tab.scss";
+import TabPanel from "./packages/tabpanel/index.js";
+import "./packages/tabpanel/tabpanel.scss";
+import TabBar from "./packages/tabbar/index.js";
+import "./packages/tabbar/tabbar.scss";
+import Calendar from "./packages/calendar/index.js";
+import "./packages/calendar/calendar.scss";
+import DatePicker from "./packages/datepicker/index.js";
+import "./packages/datepicker/datepicker.scss";
+import NavBar from "./packages/navbar/index.js";
+import "./packages/navbar/navbar.scss";
+import NoticeBar from "./packages/noticebar/index.js";
+import "./packages/noticebar/noticebar.scss";
+import Switch from "./packages/switch/index.js";
+import "./packages/switch/switch.scss";
+import Slider from "./packages/slider/index.js";
+import "./packages/slider/slider.scss";
+import Range from "./packages/range/index.js";
+import "./packages/range/range.scss";
+import Picker from "./packages/picker/index.js";
+import "./packages/picker/picker.scss";
+import Progress from "./packages/progress/index.js";
+import "./packages/progress/progress.scss";
+import Price from "./packages/price/index.js";
+import "./packages/price/price.scss";
+import Flex from "./packages/flex/index.js";
+import "./packages/flex/flex.scss";
+import Col from "./packages/col/index.js";
+import "./packages/col/col.scss";
+import Row from "./packages/row/index.js";
+import "./packages/row/row.scss";
+import Steps from "./packages/steps/index.js";
+import "./packages/steps/steps.scss";
+import Button from "./packages/button/index.js";
+import "./packages/button/button.scss";
+import Badge from "./packages/badge/index.js";
+import "./packages/badge/badge.scss";
+import Rate from "./packages/rate/index.js";
+import "./packages/rate/rate.scss";
+import Swiper from "./packages/swiper/index.js";
+import "./packages/swiper/swiper.scss";
+import Menu from "./packages/menu/index.js";
+import "./packages/menu/menu.scss";
+import Stepper from "./packages/stepper/index.js";
+import "./packages/stepper/stepper.scss";
+import ButtonGroup from "./packages/buttongroup/index.js";
+import "./packages/buttongroup/buttongroup.scss";
+import SearchBar from "./packages/searchbar/index.js";
+import "./packages/searchbar/searchbar.scss";
+import ImagePicker from "./packages/imagepicker/index.js";
+import "./packages/imagepicker/imagepicker.scss";
+import Radio from "./packages/radio/index.js";
+import "./packages/radio/radio.scss";
+import RadioGroup from "./packages/radiogroup/index.js";
+import "./packages/radiogroup/radiogroup.scss";
+import CheckBox from "./packages/checkbox/index.js";
+import "./packages/checkbox/checkbox.scss";
+import CheckBoxGroup from "./packages/checkboxgroup/index.js";
+import "./packages/checkboxgroup/checkboxgroup.scss";
+import ShortPassword from "./packages/shortpassword/index.js";
+import "./packages/shortpassword/shortpassword.scss";
+import Skeleton from "./packages/skeleton/index.js";
+import "./packages/skeleton/skeleton.scss";
+import Scroller from "./packages/scroller/index.js";
+import "./packages/scroller/scroller.scss";
+import BackTop from "./packages/backtop/index.js";
+import "./packages/backtop/backtop.scss";
+import CountDown from "./packages/countdown/index.js";
+import "./packages/countdown/countdown.scss";
+import InfiniteLoading from "./packages/infiniteloading/index.js";
+import "./packages/infiniteloading/infiniteloading.scss";
 import Uploader from "./packages/uploader/index.js";
 import "./packages/uploader/uploader.scss";
 import TextInput from "./packages/textinput/index.js";
@@ -94,7 +94,7 @@ import "./packages/textbox/textbox.scss";
 import TextBox from "./packages/textbox/index.js";
 import Elevator from "./packages/elevator/index.js";
 import "./packages/elevator/elevator.scss";
-
+import Popup from "./packages/popup/index.js";
 const packages = {
   Cell,
   Dialog,
@@ -141,7 +141,8 @@ const packages = {
   TextInput,
   TextBox,
   Avatar,
-  Elevator: Elevator
+  Elevator,
+  Popup
 };
 
 const components = {};
@@ -152,7 +153,7 @@ pkgList.map(item => {
   const pkg = packages[item.name];
   if (!pkg) return;
 
-  if (item.type == 'component') {
+  if (item.type == "component") {
     if (pkg.name) {
       components[pkg.name] = pkg;
     } else {
@@ -160,16 +161,16 @@ pkgList.map(item => {
         components[n] = pkg[n];
       }
     }
-  } else if (item.type == 'method') {
+  } else if (item.type == "method") {
     methods[item.name] = pkg;
-  } else if (item.type == 'filter') {
+  } else if (item.type == "filter") {
     filters[item.name] = pkg;
-  } else if (item.type == 'directive') {
+  } else if (item.type == "directive") {
     directives[item.name] = pkg;
   }
 });
 
-const install = function (Vue, opts = {}) {
+const install = function(Vue, opts = {}) {
   if (install.installed) return;
 
   if (opts.locale) {
@@ -180,10 +181,10 @@ const install = function (Vue, opts = {}) {
 
   for (let cptName in methods) {
     if (Array.isArray(methods[cptName])) {
-      Vue.prototype['$' + cptName.toLowerCase()] = methods[cptName][0];
+      Vue.prototype["$" + cptName.toLowerCase()] = methods[cptName][0];
       Vue.component(methods[cptName][1].name, methods[cptName][1]);
     } else {
-      Vue.prototype['$' + cptName.toLowerCase()] = methods[cptName];
+      Vue.prototype["$" + cptName.toLowerCase()] = methods[cptName];
     }
   }
 
@@ -207,11 +208,12 @@ const install = function (Vue, opts = {}) {
 
   Vue.use(Lazyload, {
     lazyComponent: true,
-    loading: '//img12.360buyimg.com/imagetools/jfs/t1/73967/28/14561/916/5dc142e4E0666555b/bf33454553c6035e.png'
+    loading:
+      "//img12.360buyimg.com/imagetools/jfs/t1/73967/28/14561/916/5dc142e4E0666555b/bf33454553c6035e.png"
   });
 };
 
-if (typeof window !== 'undefined' && window.Vue) {
+if (typeof window !== "undefined" && window.Vue) {
   install(window.Vue);
 }
 
@@ -224,4 +226,4 @@ export default {
   ...filters,
   ...directives,
   ...methods
-};
+};

+ 113 - 0
src/packages/popup/demo.vue

@@ -0,0 +1,113 @@
+<template>
+  <div>
+    <h2 class="title">基本用法</h2>
+    <nut-cell
+      isLink
+      title="展示弹出层"
+      :showIcon="true"
+      @click.native="showBasic = true"
+    >
+    </nut-cell>
+    <nut-popup :style="{ padding: '30px 50px' }" v-model="showBasic" >正文</nut-popup>
+
+    <h2 class="title">弹出位置</h2>
+
+    <nut-cell
+      isLink
+      title="顶部弹出"
+      :showIcon="true"
+      @click.native="showTop = true"
+    >
+    </nut-cell>
+    <nut-popup position="top" v-model="showTop" :style="{ height: '20%' }">
+    </nut-popup>
+    <nut-cell
+      isLink
+      title="底部弹出"
+      :showIcon="true"
+      @click.native="showBottom = true"
+    >
+    </nut-cell>
+    <nut-popup
+      v-model="showBottom"
+      position="bottom"
+      :style="{ height: '20%' }"
+    >
+    </nut-popup>
+    <nut-cell
+      isLink
+      title="左侧弹出"
+      :showIcon="true"
+      @click.native="showLeft = true"
+    >
+    </nut-cell>
+    <nut-popup
+      :style="{ width: '20%', height: '100%' }"
+      v-model="showLeft"
+      position="left"
+    ></nut-popup>
+    <nut-cell
+      isLink
+      title="右侧弹出"
+      :showIcon="true"
+      @click.native="showRight = true"
+    >
+    </nut-cell>
+    <nut-popup
+      position="right"
+      v-model="showRight"
+      :style="{ width: '20%', height: '100%' }"
+    ></nut-popup>
+
+    <h2 class="title">圆角弹框</h2>
+    <nut-cell
+      isLink
+      title="圆角弹框"
+      :showIcon="true"
+      @click.native="showRound = true"
+    >
+    </nut-cell>
+    <nut-popup
+      round
+      v-model="showRound"
+      position="bottom"
+      :style="{ height: '20%' }"
+    ></nut-popup>
+  </div>
+</template>
+<script>
+export default {
+  props: {},
+  data() {
+    return {
+      showBasic: false,
+      showTop: false,
+      showBottom: false,
+      showLeft: false,
+      showRight: false,
+      showRound: false
+    };
+  },
+  methods: {
+    show() {
+      this.isShow = true;
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.title {
+  text-align: left;
+  margin: 0;
+  padding: 32px 16px 16px;
+  color: rgba(69, 90, 100, 0.6);
+  font-weight: normal;
+  font-size: 14px;
+  line-height: 16px;
+}
+.demo {
+  padding-left: 0;
+  padding-right: 0;
+  overflow: hidden;
+}
+</style>

+ 55 - 0
src/packages/popup/doc.md

@@ -0,0 +1,55 @@
+# Popup 弹出层
+
+介绍  
+弹出层容器,用于展示弹窗、信息提示等内容,支持多个弹出层叠加展示
+
+## 基本功能
+
+```html
+<nut-cell isLink title="展示弹出层" :showIcon="true" @click.native="show= true">
+</nut-cell>
+<nut-popup :style="{ padding: '30px 50px' }" v-model="show">正文</nut-popup>
+```
+
+```javascript
+export default {
+  data() {
+    return {
+      show: false
+    };
+  },
+
+  methods: {
+    showPopup() {
+      this.show = true;
+    }
+  }
+};
+```
+
+## 弹出位置
+
+通过 position 属性设置弹出位置,默认居中弹出,可以设置为 top、bottom、left、right
+
+```html
+<nut-popup v-model="show" position="top" :style="{ height: '20%' }" />
+```
+
+## 圆角弹窗
+
+设置 round 属性后,弹窗会根据弹出位置添加不同的圆角样式
+
+```html
+<van-popup v-model="show" round position="bottom" :style="{ height: '20%' }" />
+```
+
+## API
+
+| 字段       | 说明                                     | 类型    | 默认值 |
+| ---------- | ---------------------------------------- | ------- | ------ |
+| v-model    | 当前组件是否显示                         | boolean | -      |
+| overlay    | 是否显示遮罩层                           | boolean | true   |
+| position   | 弹出位置,可选值为 top bottom right left | string  | center |
+| duration   | 动画时长,单位秒                         | Number  | -      |
+| round      | 是否显示圆角                             | boolean | -      |
+| transition | 动画类名,等价于 transtion 的 name 属性  | string  | -      |

+ 7 - 0
src/packages/popup/index.js

@@ -0,0 +1,7 @@
+import Popup from "./popup.vue";
+
+Popup.install = function(Vue) {
+  Vue.component(Popup.name, Popup);
+};
+
+export default Popup;

+ 59 - 0
src/packages/popup/mask.vue

@@ -0,0 +1,59 @@
+<template>
+  <transition name="popup-fade">
+    <div v-show="show" class="bg nut-mask" @click="onClick"></div>
+  </transition>
+</template>
+<script>
+export default {
+  name: "nut-popup-mask",
+  props: {
+    show: { type: Boolean, default: true }
+  },
+  data() {
+    return {};
+  },
+
+  methods: {
+    onClick() {
+      this.$emit("input", false);
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.popup-fade-enter-active {
+  animation: 0.3s van-fade-in;
+}
+.popup-fade-leave-active {
+  animation: 0.3s van-fade-out;
+}
+.bg {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.7);
+  z-index: 99;
+}
+
+@keyframes van-fade-in {
+  from {
+    opacity: 0;
+  }
+
+  to {
+    opacity: 1;
+  }
+}
+
+@keyframes van-fade-out {
+  from {
+    opacity: 1;
+  }
+
+  to {
+    opacity: 0;
+  }
+}
+</style>

+ 196 - 0
src/packages/popup/popup.vue

@@ -0,0 +1,196 @@
+<template>
+  <transition :name="transitionName">
+    <div
+      ref="popupBox"
+      v-show="value"
+      class="popup-box"
+      :class="[`popup-${position}`, { round }]"
+    >
+      <slot></slot>
+    </div>
+  </transition>
+</template>
+<script>
+import Vue from "vue";
+import Mask from "./mask.vue";
+
+export default {
+  name: "nut-popup",
+  props: {
+    value: Boolean,
+    position: {
+      type: String,
+      default: "center"
+    },
+    duration: Number,
+    transition: String,
+    overlay: {
+      type: Boolean,
+      default: true
+    },
+    round: Boolean
+  },
+  created() {
+    if (this.transition) {
+      this.transitionName = this.transition;
+    } else if (this.position === "center") {
+      this.transitionName = "popup-fade";
+    } else {
+      this.transitionName = `popup-slide-${this.position}`;
+    }
+  },
+  mounted() {
+    if (this.duration) {
+      this.$refs.popupBox.style.transitionDuration = this.duration + "s";
+    }
+  },
+  watch: {
+    value(val) {
+      const type = val ? "open" : "close";
+      if (this.overlay) this[type]();
+    },
+    position(val) {
+      if (val === "center") {
+        this.transitionName = "popup-fade";
+      } else {
+        this.transitionName = `popup-slide-${this.position}`;
+      }
+    }
+  },
+  data() {
+    return {
+      transitionName: "popup-fade",
+      maskInstant: null
+    };
+  },
+
+  methods: {
+    mount(Component, data) {
+      const instance = new Vue({
+        el: document.createElement("div"),
+        props: Component.props,
+        render(h) {
+          return h(Component, {
+            props: this.$props,
+            ...data
+          });
+        }
+      });
+      document.body.appendChild(instance.$el);
+      return instance;
+    },
+
+    open() {
+      if (!this.maskInstant) {
+        this.maskInstant = this.mount(Mask, {
+          nativeOn: {
+            click: () => {
+              this.$emit("input", false);
+            }
+          }
+        });
+      } else {
+        this.maskInstant.show = true;
+      }
+    },
+    close() {
+      this.maskInstant.show = false;
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.popup-fade-enter-active {
+  animation: 0.2s van-fade-in;
+}
+.popup-fade-leave-active {
+  animation: 0.2s van-fade-out;
+}
+
+.popup-slide {
+  &-top-enter,
+  &-top-leave-active {
+    transform: translate(0, -100%);
+  }
+  &-right-enter,
+  &-right-leave-active {
+    transform: translate(100%, 0);
+  }
+
+  &-bottom-enter,
+  &-bottom-leave-active {
+    transform: translate(0, 100%);
+  }
+
+  &-left-enter,
+  &-left-leave-active {
+    transform: translate(-100%, 0);
+  }
+}
+
+.popup-center {
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+}
+
+.popup-bottom {
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  &.round {
+    border-radius: 25px 25px 0 0;
+  }
+}
+.popup-right {
+  top: 0;
+  right: 0;
+  &.round {
+    border-radius: 25px 0 0 25px;
+  }
+}
+
+.popup-left {
+  top: 0;
+  left: 0;
+  &.round {
+    border-radius: 0 25px 25px 0;
+  }
+}
+.popup-top {
+  top: 0;
+  left: 0;
+  width: 100%;
+  &.round {
+    border-radius: 0 0 25px 25px;
+  }
+}
+.popup-box {
+  position: fixed;
+  max-height: 100%;
+  overflow-y: auto;
+  background-color: #fff;
+  transition: transform 0.2s;
+  -webkit-overflow-scrolling: touch;
+  z-index: 2028;
+}
+@keyframes van-fade-in {
+  from {
+    opacity: 0;
+  }
+
+  to {
+    opacity: 1;
+  }
+}
+
+@keyframes van-fade-out {
+  from {
+    opacity: 1;
+  }
+
+  to {
+    opacity: 0;
+  }
+}
+</style>