diff --git a/src/action-sheet/index.js b/src/action-sheet/index.js index c6b70bcde..5b124e9dc 100644 --- a/src/action-sheet/index.js +++ b/src/action-sheet/index.js @@ -1,19 +1,16 @@ // Utils import { createNamespace } from '../utils'; -// Mixins -import { popupMixinProps } from '../mixins/popup'; - // Components import Icon from '../icon'; -import Popup from '../popup'; +import Popup, { popupSharedProps } from '../popup'; import Loading from '../loading'; const [createComponent, bem] = createNamespace('action-sheet'); export default createComponent({ props: { - ...popupMixinProps, + ...popupSharedProps, title: String, actions: Array, duration: [Number, String], diff --git a/src/mixins/popup/context.ts b/src/mixins/popup/context.ts deleted file mode 100644 index 25b501a28..000000000 --- a/src/mixins/popup/context.ts +++ /dev/null @@ -1,13 +0,0 @@ -export type StackItem = { - vm: any; - overlay: any; -}; - -export const context = { - zIndex: 2000, - lockCount: 0, - stack: [] as StackItem[], - find(vm: any): StackItem | undefined { - return this.stack.filter((item) => item.vm === vm)[0]; - }, -}; diff --git a/src/mixins/popup/index.js b/src/mixins/popup/index.js deleted file mode 100644 index 803161d37..000000000 --- a/src/mixins/popup/index.js +++ /dev/null @@ -1,204 +0,0 @@ -// Context -import { context } from './context'; - -// Utils -import { on, off, preventDefault } from '../../utils/dom/event'; -import { getScroller } from '../../utils/dom/scroll'; - -// Mixins -import { TouchMixin } from '../touch'; -import { CloseOnPopstateMixin } from '../close-on-popstate'; - -export const popupMixinProps = { - // whether to show popup - show: Boolean, - // whether to show overlay - overlay: Boolean, - // overlay custom style - overlayStyle: Object, - // overlay custom class name - overlayClass: String, - // teleport - getContainer: [String, Function], - // whether to close popup when click overlay - closeOnClickOverlay: Boolean, - // z-index - zIndex: [Number, String], - // prevent body scroll - lockScroll: { - type: Boolean, - default: true, - }, - // whether to lazy render - lazyRender: { - type: Boolean, - default: true, - }, -}; - -export function PopupMixin(options = {}) { - return { - mixins: [TouchMixin, CloseOnPopstateMixin], - - props: popupMixinProps, - - data() { - return { - inited: this.show, - currentZIndex: null, - }; - }, - - computed: { - shouldRender() { - return this.inited || !this.lazyRender; - }, - }, - - watch: { - show(val) { - const type = val ? 'open' : 'close'; - this.inited = this.inited || this.show; - this[type](); - - if (!options.skipToggleEvent) { - this.$emit(type); - } - }, - - overlay: 'renderOverlay', - }, - - mounted() { - if (this.show) { - this.open(); - } - }, - - /* istanbul ignore next */ - activated() { - if (this.shouldReopen) { - this.$emit('update:show', true); - this.shouldReopen = false; - } - }, - - beforeUnmount() { - if (this.opened) { - this.removeLock(); - } - }, - - /* istanbul ignore next */ - deactivated() { - if (this.show) { - this.close(); - this.shouldReopen = true; - } - }, - - methods: { - open() { - /* istanbul ignore next */ - if (this.$isServer || this.opened) { - return; - } - - // cover default zIndex - if (this.zIndex !== undefined) { - context.zIndex = this.zIndex; - } - - this.opened = true; - this.renderOverlay(); - this.addLock(); - }, - - addLock() { - if (this.lockScroll) { - on(document, 'touchstart', this.touchStart); - on(document, 'touchmove', this.onTouchMove); - - if (!context.lockCount) { - document.body.classList.add('van-overflow-hidden'); - } - context.lockCount++; - } - }, - - removeLock() { - if (this.lockScroll && context.lockCount) { - context.lockCount--; - off(document, 'touchstart', this.touchStart); - off(document, 'touchmove', this.onTouchMove); - - if (!context.lockCount) { - document.body.classList.remove('van-overflow-hidden'); - } - } - }, - - close() { - if (!this.opened) { - return; - } - - this.opened = false; - this.removeLock(); - this.$emit('update:show', false); - }, - - onTouchMove(event) { - this.touchMove(event); - const direction = this.deltaY > 0 ? '10' : '01'; - const el = getScroller(event.target, this.$refs.root); - const { scrollHeight, offsetHeight, scrollTop } = el; - let status = '11'; - - /* istanbul ignore next */ - if (scrollTop === 0) { - status = offsetHeight >= scrollHeight ? '00' : '01'; - } else if (scrollTop + offsetHeight >= scrollHeight) { - status = '10'; - } - - /* istanbul ignore next */ - if ( - status !== '11' && - this.direction === 'vertical' && - !(parseInt(status, 2) & parseInt(direction, 2)) - ) { - preventDefault(event, true); - } - }, - - onClickOverlay() { - this.$emit('click-overlay'); - - if (this.closeOnClickOverlay) { - // TODO - // if (this.onClickOverlay) { - // this.onClickOverlay(); - // } else { - // this.close(); - // } - this.close(); - } - }, - - renderOverlay() { - if (this.$isServer || !this.show) { - return; - } - - this.$nextTick(() => { - this.updateZIndex(this.overlay ? 1 : 0); - }); - }, - - updateZIndex(value = 0) { - this.currentZIndex = ++context.zIndex + value; - }, - }, - }; -} diff --git a/src/mixins/popup/type.ts b/src/mixins/popup/type.ts deleted file mode 100644 index 5944f8932..000000000 --- a/src/mixins/popup/type.ts +++ /dev/null @@ -1,13 +0,0 @@ -export type GetContainer = () => Element; - -export type PopupMixinProps = { - value: boolean; - zIndex: string | number; - overlay?: boolean; - lockScroll: boolean; - lazyRender: boolean; - overlayClass?: any; - overlayStyle?: object | object[]; - getContainer?: string | GetContainer; - closeOnClickOverlay?: boolean; -}; diff --git a/src/notify/Notify.js b/src/notify/Notify.js index 806878c30..13c63ecb1 100644 --- a/src/notify/Notify.js +++ b/src/notify/Notify.js @@ -1,12 +1,11 @@ import { createNamespace } from '../utils'; -import { popupMixinProps } from '../mixins/popup'; -import Popup from '../popup'; +import Popup, { popupSharedProps } from '../popup'; const [createComponent, bem] = createNamespace('notify'); export default createComponent({ props: { - ...popupMixinProps, + ...popupSharedProps, color: String, message: [Number, String], duration: [Number, String], diff --git a/src/popup/index.js b/src/popup/index.js index b117e3cf5..03b9b94a2 100644 --- a/src/popup/index.js +++ b/src/popup/index.js @@ -1,17 +1,62 @@ +// Utils import { Teleport, Transition } from 'vue'; import { createNamespace, isDef, isFunction } from '../utils'; -import { PopupMixin } from '../mixins/popup'; +import { on, off, preventDefault } from '../utils/dom/event'; +import { getScroller } from '../utils/dom/scroll'; + +// Mixins +import { TouchMixin } from '../mixins/touch'; +import { CloseOnPopstateMixin } from '../mixins/close-on-popstate'; + +// Components import Icon from '../icon'; import Overlay from '../overlay'; const [createComponent, bem] = createNamespace('popup'); +const context = { + zIndex: 2000, + lockCount: 0, + stack: [], + find(vm) { + return this.stack.filter((item) => item.vm === vm)[0]; + }, +}; + +export const popupSharedProps = { + // whether to show popup + show: Boolean, + // whether to show overlay + overlay: Boolean, + // overlay custom style + overlayStyle: Object, + // overlay custom class name + overlayClass: String, + // teleport + getContainer: [String, Function], + // whether to close popup when click overlay + closeOnClickOverlay: Boolean, + // z-index + zIndex: [Number, String], + // prevent body scroll + lockScroll: { + type: Boolean, + default: true, + }, + // whether to lazy render + lazyRender: { + type: Boolean, + default: true, + }, +}; + export default createComponent({ - mixins: [PopupMixin()], + mixins: [TouchMixin, CloseOnPopstateMixin], inheritAttrs: false, props: { + ...popupSharedProps, round: Boolean, duration: [Number, String], closeable: Boolean, @@ -49,6 +94,30 @@ export default createComponent({ 'click-overlay', ], + data() { + return { + inited: this.show, + currentZIndex: null, + }; + }, + + computed: { + shouldRender() { + return this.inited || !this.lazyRender; + }, + }, + + watch: { + show(val) { + const type = val ? 'open' : 'close'; + this.inited = this.inited || this.show; + this[type](); + this.$emit(type); + }, + + overlay: 'renderOverlay', + }, + beforeCreate() { const createEmitter = (eventName) => (event) => this.$emit(eventName, event); @@ -58,6 +127,34 @@ export default createComponent({ this.onClosed = createEmitter('closed'); }, + mounted() { + if (this.show) { + this.open(); + } + }, + + /* istanbul ignore next */ + activated() { + if (this.shouldReopen) { + this.$emit('update:show', true); + this.shouldReopen = false; + } + }, + + beforeUnmount() { + if (this.opened) { + this.removeLock(); + } + }, + + /* istanbul ignore next */ + deactivated() { + if (this.show) { + this.close(); + this.shouldReopen = true; + } + }, + methods: { genOverlay() { if (this.overlay) { @@ -123,6 +220,108 @@ export default createComponent({ ); }, + + open() { + /* istanbul ignore next */ + if (this.$isServer || this.opened) { + return; + } + + // cover default zIndex + if (this.zIndex !== undefined) { + context.zIndex = this.zIndex; + } + + this.opened = true; + this.renderOverlay(); + this.addLock(); + }, + + addLock() { + if (this.lockScroll) { + on(document, 'touchstart', this.touchStart); + on(document, 'touchmove', this.onTouchMove); + + if (!context.lockCount) { + document.body.classList.add('van-overflow-hidden'); + } + context.lockCount++; + } + }, + + removeLock() { + if (this.lockScroll && context.lockCount) { + context.lockCount--; + off(document, 'touchstart', this.touchStart); + off(document, 'touchmove', this.onTouchMove); + + if (!context.lockCount) { + document.body.classList.remove('van-overflow-hidden'); + } + } + }, + + close() { + if (!this.opened) { + return; + } + + this.opened = false; + this.removeLock(); + this.$emit('update:show', false); + }, + + onTouchMove(event) { + this.touchMove(event); + const direction = this.deltaY > 0 ? '10' : '01'; + const el = getScroller(event.target, this.$refs.root); + const { scrollHeight, offsetHeight, scrollTop } = el; + let status = '11'; + + /* istanbul ignore next */ + if (scrollTop === 0) { + status = offsetHeight >= scrollHeight ? '00' : '01'; + } else if (scrollTop + offsetHeight >= scrollHeight) { + status = '10'; + } + + /* istanbul ignore next */ + if ( + status !== '11' && + this.direction === 'vertical' && + !(parseInt(status, 2) & parseInt(direction, 2)) + ) { + preventDefault(event, true); + } + }, + + onClickOverlay() { + this.$emit('click-overlay'); + + if (this.closeOnClickOverlay) { + // TODO + // if (this.onClickOverlay) { + // this.onClickOverlay(); + // } else { + // this.close(); + // } + this.close(); + } + }, + + renderOverlay() { + if (this.$isServer || !this.show) { + return; + } + + this.$nextTick(() => { + this.updateZIndex(this.overlay ? 1 : 0); + }); + }, + + updateZIndex(value = 0) { + this.currentZIndex = ++context.zIndex + value; + }, }, render() { diff --git a/src/share-sheet/index.js b/src/share-sheet/index.js index 2e37cc4d7..ab5c723aa 100644 --- a/src/share-sheet/index.js +++ b/src/share-sheet/index.js @@ -1,11 +1,8 @@ // Utils import { createNamespace, isDef } from '../utils'; -// Mixins -import { popupMixinProps } from '../mixins/popup'; - // Components -import Popup from '../popup'; +import Popup, { popupSharedProps } from '../popup'; const PRESET_ICONS = ['qq', 'weibo', 'wechat', 'link', 'qrcode', 'poster']; @@ -13,7 +10,7 @@ const [createComponent, bem, t] = createNamespace('share-sheet'); export default createComponent({ props: { - ...popupMixinProps, + ...popupSharedProps, title: String, duration: [Number, String], cancelText: String,