action-sheet.vue 3.63 KB
<template>
  <transition name="cube-action-sheet-fade" @after-leave="hidden" @after-enter="shown">
    <cube-popup
      type="action-sheet"
      :center="false"
      :mask="true"
      :z-index="100"
      :class="{'fake-viewport': position !== 'bottom'}"
      v-show="isVisible"
      @mask-click="maskClick">
      <transition :name="transition">
        <div class="cube-action-sheet-panel"
             :class="{'with-radius': hasBorderRadius, [`panel-${position}`]: true, full: full}"
             :style="panelStyle"
             v-show="isVisible"
             ref="panel"
             @click.stop="fakeMask">
          <slot></slot>
        </div>
      </transition>
    </cube-popup>
  </transition>
</template>

<script>
import { Popup } from 'cube-ui';

export default {
  name: 'ActionSheet',
  props: {
    position: {
      type: String,
      default: 'bottom',
    },
    panelStyle: {
      type: Object,
      default() {
        return {};
      },
    },
    hasBorderRadius: {
      type: Boolean,
      default: true,
    },
    emulateMask: {
      type: Boolean,
      default: false,
    },
    full: {
      type: Boolean,
      default: false,
    }
  },
  data() {
    return {
      isVisible: false,
    };
  },
  computed: {
    transition() {
      return `cube-action-sheet-${this.position}`;
    },
  },
  methods: {
    show(fn) {
      this.isVisible = true;
      if (typeof fn === 'function') {
        this._shownCallback = fn;
      }
    },
    hide() {
      this.$emit('hide');
      this.isVisible = false;
    },
    maskClick() {
      this.hide();
    },
    hidden() {
      this.$emit('hidden');
    },
    shown() {
      this.$emit('shown');
      if (this._shownCallback) {
        this.$nextTick(() => {
          this._shownCallback();
        });
      }
    },
    fakeMask(e) {
      if (this.emulateMask && e.target === this.$refs.panel) {
        this.hide();
      }
    },
  },
  components: {
    'cube-popup': Popup,
  }
};
</script>

<style lang="scss" scoped>
  .cube-popup.fake-viewport {
    position: absolute;

    /deep/ {
      .cube-popup-content {
        height: 100%;
      }

      .cube-popup-mask {
        background-color: rgba(0, 0, 0, 0.5);
        opacity: 1;
      }
    }
  }

  .cube-action-sheet-fade-enter,
  .cube-action-sheet-fade-leave-active {
    opacity: 0;
  }

  .cube-action-sheet-fade-enter-active {
    transition: all 0.4s cubic-bezier(0, 0.96, 0.14, 1.06);
  }

  .cube-action-sheet-fade-leave-active {
    transition: all 0.2s cubic-bezier(0.165, 0.84, 0.44, 1);
  }

  .cube-action-sheet-panel {
    background: #fff;
    border-top-left-radius: 20px 20px;
    border-top-right-radius: 20px 20px;

    &.full {
      height: 100%;
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
    }

    &:after {
      content: "";
      width: 100%;
      height: 200px;
      position: absolute;
      left: 0;
      bottom: -200px;
      z-index: -1;
      background: #fff;
    }

    &.panel-right:after {
      width: 100px;
      height: 100%;
      left: auto;
      bottom: auto;
      top: 0;
      right: -100px;
    }
  }

  .cube-action-sheet-bottom-enter, .cube-action-sheet-bottom-leave-active {
    transform: translate3d(0, 100%, 0);
  }

  .cube-action-sheet-right-enter, .cube-action-sheet-right-leave-active {
    transform: translate3d(100%, 0, 0);
  }

  .cube-action-sheet-bottom-enter-active,
  .cube-action-sheet-right-enter-active {
    transition: all 0.4s cubic-bezier(0, 0.96, 0.14, 1.06);
  }

  .cube-action-sheet-bottom-leave-active,
  .cube-action-sheet-right-leave-active {
    transition: all 0.2s cubic-bezier(0.165, 0.84, 0.44, 1);
  }
</style>