Skip to content

Commit 331c6af

Browse files
committed
feat: #10 dynamic modal
1 parent 986a432 commit 331c6af

10 files changed

+122
-25
lines changed

dist/VueFinalModal.esm.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/VueFinalModal.esm.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/VueFinalModal.umd.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/VueFinalModal.umd.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/ModalsContainer.vue

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<template>
2+
<div class="modals-container">
3+
<component
4+
v-for="(modal, index) in api.dynamicModals"
5+
:key="modal.id"
6+
:is="modal.component"
7+
v-model="modal.value"
8+
v-bind="modal.bind"
9+
v-on="modal.on"
10+
@closed="slice(index)"
11+
@before-open="e => beforeOpen(e, modal)"
12+
>
13+
<template v-for="(slot, key) in modal.slots" v-slot:[key]>
14+
<component :key="key" :is="slot.component" v-bind="slot.bind" @close="modal.value = false" />
15+
</template>
16+
</component>
17+
</div>
18+
</template>
19+
20+
<script>
21+
export default {
22+
props: {},
23+
computed: {
24+
api() {
25+
return this[this.$_options.key]
26+
}
27+
},
28+
methods: {
29+
slice(index) {
30+
this.api.dynamicModals.splice(index, 1)
31+
},
32+
beforeOpen(e, modal) {
33+
e.ref.params = modal.params
34+
}
35+
}
36+
}
37+
</script>

lib/Plugin.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { bindPrototype, registComponent } from './PluginCore'
2-
import { DUPLICATE_PLUGIN_COMPONENT, DUPLICATE_COMPONENT } from './utils/errors'
1+
import { bindPrototype, registModal, registContainer } from './PluginCore'
2+
import { DUPLICATE_PLUGIN_COMPONENT, DUPLICATE_COMPONENT, DUPLICATE_DYNAMIC_CONTAINER } from './utils/errors'
33
import mobile from 'is-mobile'
44

55
const defaultOptions = {
66
componentName: 'VueFinalModal',
7+
dynamicContainerName: 'ModalsContainer',
78
key: '$vfm',
89
isMobile: mobile()
910
}
@@ -13,16 +14,26 @@ const Plugin = () => ({
1314
const _options = Object.assign({}, defaultOptions, options)
1415
const isDuplicateKey = Vue.prototype[_options.key]
1516
const isDuplicateComponent = Vue.options.components[_options.componentName]
17+
const isDuplicateDynamicContainer = Vue.options.components[_options.dynamicContainerName]
1618

17-
if (isDuplicateComponent) {
19+
if (isDuplicateComponent || isDuplicateDynamicContainer) {
1820
if (typeof window !== 'undefined') {
19-
console.warn(isDuplicateKey ? DUPLICATE_PLUGIN_COMPONENT : DUPLICATE_COMPONENT)
21+
if (isDuplicateKey) {
22+
console.error(DUPLICATE_PLUGIN_COMPONENT)
23+
}
24+
if (isDuplicateComponent) {
25+
console.error(DUPLICATE_COMPONENT)
26+
}
27+
if (isDuplicateDynamicContainer) {
28+
console.error(DUPLICATE_DYNAMIC_CONTAINER)
29+
}
2030
}
2131
} else {
2232
if (!isDuplicateKey) {
2333
bindPrototype(Vue, _options)
2434
}
25-
registComponent(Vue, _options)
35+
registModal(Vue, _options)
36+
registContainer(Vue, _options)
2637
}
2738
}
2839
})

lib/PluginCore.js

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,59 @@
1-
import VueFinalModal from './VueFinalModal.vue'
21
import { setStyle, removeStyle } from './utils/dom.js'
2+
import { findRight } from './utils/array'
3+
import VueFinalModal from './VueFinalModal.vue'
4+
import ModalsContainer from './ModalsContainer.vue'
5+
6+
function assignOptions(component, options) {
7+
const _component = { ...component }
8+
Object.assign(_component.props, {
9+
$_options: { type: Object, default: () => options }
10+
})
11+
return _component
12+
}
313

4-
function createVfm(options) {
14+
function createVfm(Vue, options) {
515
let vfm
16+
const PREFIX = '_dynamic_modal_'
17+
const generateId = ((index = 0) => () => (index++).toString())()
618

719
return function() {
820
vfm = {
9-
show(name, ...args) {
10-
this.toggle(name, true, ...args)
21+
show(modal, ...args) {
22+
switch (typeof modal) {
23+
case 'string':
24+
this.toggle(modal, true, ...args)
25+
break
26+
case 'object':
27+
{
28+
const defaultModal = {
29+
component: options.componentName,
30+
bind: {},
31+
slots: {},
32+
on: {}
33+
}
34+
modal = Object.assign(defaultModal, modal)
35+
const id = generateId()
36+
const name = modal.bind.name || PREFIX + id
37+
this.dynamicModals.push({
38+
value: true,
39+
id,
40+
...modal,
41+
component: modal.component,
42+
slots: modal.slots,
43+
bind: { ...modal.bind, name },
44+
params: args[0]
45+
})
46+
}
47+
break
48+
}
1149
},
1250
hide(name) {
1351
this.toggle(name, false)
1452
},
1553
hideAll() {
16-
this.openedModals.forEach(modal => {
17-
modal.$emit('input', false)
18-
})
54+
for (let i = this.openedModals.length - 1; i >= 0; i--) {
55+
this.openedModals[i].$emit('input', false)
56+
}
1957
},
2058
toggle(name, ...args) {
2159
const modal = this.get(name)
@@ -45,29 +83,30 @@ function createVfm(options) {
4583
e.preventDefault()
4684
},
4785
get(name) {
48-
return this.modals.find(modal => modal.name === name)
86+
return findRight(this.modals, modal => modal.name === name)
4987
},
88+
dynamicModals: [],
5089
get openedModals() {
5190
return this.modals.filter(modal => modal.value)
5291
},
5392
modals: []
5493
}
55-
return vfm
94+
return Vue.observable(vfm)
5695
}
5796
}
5897

5998
export function bindPrototype(Vue, options) {
60-
const vfm = createVfm(options)()
99+
const vfm = createVfm(Vue, options)()
61100
Object.defineProperty(Vue.prototype, options.key, {
62101
get() {
63102
return vfm
64103
}
65104
})
66105
}
67106

68-
export function registComponent(Vue, options) {
69-
Vue.component(options.componentName, {
70-
...VueFinalModal,
71-
props: { ...VueFinalModal.props, $_options: { type: Object, default: () => options } }
72-
})
107+
export function registModal(Vue, options) {
108+
Vue.component(options.componentName, assignOptions(VueFinalModal, options))
109+
}
110+
export function registContainer(Vue, options) {
111+
Vue.component(options.dynamicContainerName, assignOptions(ModalsContainer, options))
73112
}

lib/VueFinalModal.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ const STYLE_PROP = {
7777
}
7878
7979
export default {
80-
name: 'VueFinalModal',
8180
props: {
8281
name: { type: String, default: null },
8382
value: { type: Boolean, default: false },

lib/utils/array.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function findRight(arr, callback) {
2+
for (let i = arr.length - 1; i >= 0; i--) {
3+
if (callback(arr[i], i)) {
4+
return arr[i]
5+
}
6+
}
7+
return undefined
8+
}

lib/utils/errors.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@ export const DUPLICATE_PLUGIN_COMPONENT =
22
'[vue-final-modal] Duplicate registration API key and componentName of VueFinalModal.'
33

44
export const DUPLICATE_COMPONENT = '[vue-final-modal] Duplicate registration componentName of VueFinalModal.'
5+
6+
export const DUPLICATE_DYNAMIC_CONTAINER =
7+
'[vue-final-modal] Duplicate registration dynamicContainerName of ModalsContainer.'

0 commit comments

Comments
 (0)