Skip to content

Commit

Permalink
feat: #10 dynamic modal
Browse files Browse the repository at this point in the history
  • Loading branch information
hunterliu1003 committed Dec 11, 2020
1 parent 986a432 commit 331c6af
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 25 deletions.
2 changes: 1 addition & 1 deletion dist/VueFinalModal.esm.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/VueFinalModal.esm.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/VueFinalModal.umd.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/VueFinalModal.umd.js.map

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions lib/ModalsContainer.vue
@@ -0,0 +1,37 @@
<template>
<div class="modals-container">
<component
v-for="(modal, index) in api.dynamicModals"
:key="modal.id"
:is="modal.component"
v-model="modal.value"
v-bind="modal.bind"
v-on="modal.on"
@closed="slice(index)"
@before-open="e => beforeOpen(e, modal)"
>
<template v-for="(slot, key) in modal.slots" v-slot:[key]>
<component :key="key" :is="slot.component" v-bind="slot.bind" @close="modal.value = false" />
</template>
</component>
</div>
</template>

<script>
export default {
props: {},
computed: {
api() {
return this[this.$_options.key]
}
},
methods: {
slice(index) {
this.api.dynamicModals.splice(index, 1)
},
beforeOpen(e, modal) {
e.ref.params = modal.params
}
}
}
</script>
21 changes: 16 additions & 5 deletions lib/Plugin.js
@@ -1,9 +1,10 @@
import { bindPrototype, registComponent } from './PluginCore'
import { DUPLICATE_PLUGIN_COMPONENT, DUPLICATE_COMPONENT } from './utils/errors'
import { bindPrototype, registModal, registContainer } from './PluginCore'
import { DUPLICATE_PLUGIN_COMPONENT, DUPLICATE_COMPONENT, DUPLICATE_DYNAMIC_CONTAINER } from './utils/errors'
import mobile from 'is-mobile'

const defaultOptions = {
componentName: 'VueFinalModal',
dynamicContainerName: 'ModalsContainer',
key: '$vfm',
isMobile: mobile()
}
Expand All @@ -13,16 +14,26 @@ const Plugin = () => ({
const _options = Object.assign({}, defaultOptions, options)
const isDuplicateKey = Vue.prototype[_options.key]
const isDuplicateComponent = Vue.options.components[_options.componentName]
const isDuplicateDynamicContainer = Vue.options.components[_options.dynamicContainerName]

if (isDuplicateComponent) {
if (isDuplicateComponent || isDuplicateDynamicContainer) {
if (typeof window !== 'undefined') {
console.warn(isDuplicateKey ? DUPLICATE_PLUGIN_COMPONENT : DUPLICATE_COMPONENT)
if (isDuplicateKey) {
console.error(DUPLICATE_PLUGIN_COMPONENT)
}
if (isDuplicateComponent) {
console.error(DUPLICATE_COMPONENT)
}
if (isDuplicateDynamicContainer) {
console.error(DUPLICATE_DYNAMIC_CONTAINER)
}
}
} else {
if (!isDuplicateKey) {
bindPrototype(Vue, _options)
}
registComponent(Vue, _options)
registModal(Vue, _options)
registContainer(Vue, _options)
}
}
})
Expand Down
69 changes: 54 additions & 15 deletions lib/PluginCore.js
@@ -1,21 +1,59 @@
import VueFinalModal from './VueFinalModal.vue'
import { setStyle, removeStyle } from './utils/dom.js'
import { findRight } from './utils/array'
import VueFinalModal from './VueFinalModal.vue'
import ModalsContainer from './ModalsContainer.vue'

function assignOptions(component, options) {
const _component = { ...component }
Object.assign(_component.props, {
$_options: { type: Object, default: () => options }
})
return _component
}

function createVfm(options) {
function createVfm(Vue, options) {
let vfm
const PREFIX = '_dynamic_modal_'
const generateId = ((index = 0) => () => (index++).toString())()

return function() {
vfm = {
show(name, ...args) {
this.toggle(name, true, ...args)
show(modal, ...args) {
switch (typeof modal) {
case 'string':
this.toggle(modal, true, ...args)
break
case 'object':
{
const defaultModal = {
component: options.componentName,
bind: {},
slots: {},
on: {}
}
modal = Object.assign(defaultModal, modal)
const id = generateId()
const name = modal.bind.name || PREFIX + id
this.dynamicModals.push({
value: true,
id,
...modal,
component: modal.component,
slots: modal.slots,
bind: { ...modal.bind, name },
params: args[0]
})
}
break
}
},
hide(name) {
this.toggle(name, false)
},
hideAll() {
this.openedModals.forEach(modal => {
modal.$emit('input', false)
})
for (let i = this.openedModals.length - 1; i >= 0; i--) {
this.openedModals[i].$emit('input', false)
}
},
toggle(name, ...args) {
const modal = this.get(name)
Expand Down Expand Up @@ -45,29 +83,30 @@ function createVfm(options) {
e.preventDefault()
},
get(name) {
return this.modals.find(modal => modal.name === name)
return findRight(this.modals, modal => modal.name === name)
},
dynamicModals: [],
get openedModals() {
return this.modals.filter(modal => modal.value)
},
modals: []
}
return vfm
return Vue.observable(vfm)
}
}

export function bindPrototype(Vue, options) {
const vfm = createVfm(options)()
const vfm = createVfm(Vue, options)()
Object.defineProperty(Vue.prototype, options.key, {
get() {
return vfm
}
})
}

export function registComponent(Vue, options) {
Vue.component(options.componentName, {
...VueFinalModal,
props: { ...VueFinalModal.props, $_options: { type: Object, default: () => options } }
})
export function registModal(Vue, options) {
Vue.component(options.componentName, assignOptions(VueFinalModal, options))
}
export function registContainer(Vue, options) {
Vue.component(options.dynamicContainerName, assignOptions(ModalsContainer, options))
}
1 change: 0 additions & 1 deletion lib/VueFinalModal.vue
Expand Up @@ -77,7 +77,6 @@ const STYLE_PROP = {
}
export default {
name: 'VueFinalModal',
props: {
name: { type: String, default: null },
value: { type: Boolean, default: false },
Expand Down
8 changes: 8 additions & 0 deletions lib/utils/array.js
@@ -0,0 +1,8 @@
export function findRight(arr, callback) {
for (let i = arr.length - 1; i >= 0; i--) {
if (callback(arr[i], i)) {
return arr[i]
}
}
return undefined
}
3 changes: 3 additions & 0 deletions lib/utils/errors.js
Expand Up @@ -2,3 +2,6 @@ export const DUPLICATE_PLUGIN_COMPONENT =
'[vue-final-modal] Duplicate registration API key and componentName of VueFinalModal.'

export const DUPLICATE_COMPONENT = '[vue-final-modal] Duplicate registration componentName of VueFinalModal.'

export const DUPLICATE_DYNAMIC_CONTAINER =
'[vue-final-modal] Duplicate registration dynamicContainerName of ModalsContainer.'

0 comments on commit 331c6af

Please sign in to comment.