From b7fb128b5e15aa9d45180988749c7e26fce6f24d Mon Sep 17 00:00:00 2001
From: Noel De Martin
Date: Thu, 15 Feb 2018 18:09:40 +0100
Subject: [PATCH 1/2] Fix linter errors
---
src/Dialog.vue | 18 +++++++-------
src/Modal.vue | 66 ++++++++++++++++++++++++-------------------------
src/Resizer.vue | 14 +++++------
src/index.js | 8 +++---
4 files changed, 53 insertions(+), 53 deletions(-)
diff --git a/src/Dialog.vue b/src/Dialog.vue
index 75345fb..3d40c6e 100644
--- a/src/Dialog.vue
+++ b/src/Dialog.vue
@@ -1,5 +1,5 @@
-
- 0) {
const buttonIndex =
this.buttons.length === 1
diff --git a/src/Modal.vue b/src/Modal.vue
index d7692a4..64be4f2 100644
--- a/src/Modal.vue
+++ b/src/Modal.vue
@@ -78,14 +78,14 @@ export default {
minWidth: {
type: Number,
default: 0,
- validator(value) {
+ validator (value) {
return value >= 0
}
},
minHeight: {
type: Number,
default: 0,
- validator(value) {
+ validator (value) {
return value >= 0
}
},
@@ -100,7 +100,7 @@ export default {
width: {
type: [Number, String],
default: 600,
- validator(value) {
+ validator (value) {
if (typeof value === 'string') {
let width = parseNumber(value)
return (width.type === '%' || width.type === 'px') && width.value > 0
@@ -112,7 +112,7 @@ export default {
height: {
type: [Number, String],
default: 300,
- validator(value) {
+ validator (value) {
if (typeof value === 'string') {
if (value === 'auto') {
return true
@@ -130,14 +130,14 @@ export default {
pivotX: {
type: Number,
default: 0.5,
- validator(value) {
+ validator (value) {
return value >= 0 && value <= 1
}
},
pivotY: {
type: Number,
default: 0.5,
- validator(value) {
+ validator (value) {
return value >= 0 && value <= 1
}
}
@@ -145,7 +145,7 @@ export default {
components: {
Resizer
},
- data() {
+ data () {
return {
visible: false,
@@ -182,7 +182,7 @@ export default {
* inside `setTimeout` and `$nextTick`, after the DOM changes.
* This fixes `$refs.modal` `undefined` bug (fixes #15)
*/
- visible(value) {
+ visible (value) {
if (value) {
this.visibility.overlay = true
@@ -206,13 +206,13 @@ export default {
}
}
},
- created() {
+ created () {
this.setInitialSize()
},
/**
* Sets global listeners
*/
- beforeMount() {
+ beforeMount () {
Modal.event.$on('toggle', (name, state, params) => {
if (name === this.name) {
if (typeof state === 'undefined') {
@@ -247,7 +247,7 @@ export default {
* simply stay at its initial height (won't crash).
* (Provide polyfill to support IE < 11)
*/
- const MutationObserver = (function() {
+ const MutationObserver = (function () {
const prefixes = ['', 'WebKit', 'Moz', 'O', 'Ms']
for (let i = 0; i < prefixes.length; i++) {
@@ -274,7 +274,7 @@ export default {
/**
* Removes "resize" window listener
*/
- beforeDestroy() {
+ beforeDestroy () {
window.removeEventListener('resize', this.onWindowResize)
if (this.clickToClose) {
@@ -285,14 +285,14 @@ export default {
/**
* Returns true if height is set to "auto"
*/
- isAutoHeight() {
+ isAutoHeight () {
return this.modal.heightType === 'auto'
},
/**
* Calculates and returns modal position based on the
* pivots, window size and modal size
*/
- position() {
+ position () {
const {
window,
shift,
@@ -317,7 +317,7 @@ export default {
* Returns pixel width (if set with %) and makes sure that modal size
* fits the window
*/
- trueModalWidth() {
+ trueModalWidth () {
const { window, modal, adaptive, minWidth, maxWidth } = this
const value =
@@ -333,7 +333,7 @@ export default {
*
* Returns modal.renderedHeight if height set as "auto"
*/
- trueModalHeight() {
+ trueModalHeight () {
const { window, modal, isAutoHeight, adaptive, maxHeight } = this
const value =
@@ -353,7 +353,7 @@ export default {
/**
* Returns class list for screen overlay (modal background)
*/
- overlayClass() {
+ overlayClass () {
return {
'v--modal-overlay': true,
scrollable: this.scrollable && this.isAutoHeight
@@ -362,13 +362,13 @@ export default {
/**
* Returns class list for modal itself
*/
- modalClass() {
+ modalClass () {
return ['v--modal-box', this.classes]
},
/**
* CSS styles for position and size of the modal
*/
- modalStyle() {
+ modalStyle () {
return {
top: this.position.top + 'px',
left: this.position.left + 'px',
@@ -383,7 +383,7 @@ export default {
* if "reset" flag is set to true - this function will be called
* every time "beforeOpen" is triggered
*/
- setInitialSize() {
+ setInitialSize () {
const { modal } = this
const width = parseNumber(this.width)
const height = parseNumber(this.height)
@@ -394,13 +394,13 @@ export default {
modal.heightType = height.type
},
- onEscapeKeyUp(event) {
+ onEscapeKeyUp (event) {
if (event.which === 27 && this.visible) {
this.$modal.hide(this.name)
}
},
- onWindowResize() {
+ onWindowResize () {
this.window.width = window.innerWidth
this.window.height = window.innerHeight
},
@@ -408,7 +408,7 @@ export default {
/**
* Generates event object
*/
- genEventObject(params) {
+ genEventObject (params) {
const eventData = {
name: this.name,
timestamp: Date.now(),
@@ -421,7 +421,7 @@ export default {
/**
* Event handler which is triggered on modal resize
*/
- onModalResize(event) {
+ onModalResize (event) {
this.modal.widthType = 'px'
this.modal.width = event.size.width
@@ -438,7 +438,7 @@ export default {
* BeforeEvents: ('before-close' and 'before-open') are `$emit`ed here,
* but AfterEvents ('opened' and 'closed') are moved to `watch.visible`.
*/
- toggle(state, params) {
+ toggle (state, params) {
const { reset, scrollable, visible } = this
if (visible === state) return
const beforeEventName = visible ? 'before-close' : 'before-open'
@@ -484,7 +484,7 @@ export default {
}
},
- getDraggableElement() {
+ getDraggableElement () {
var selector =
typeof this.draggable !== 'string' ? '.v--modal-box' : this.draggable
@@ -499,13 +499,13 @@ export default {
/**
* Event handler that is triggered when background overlay is clicked
*/
- onBackgroundClick() {
+ onBackgroundClick () {
if (this.clickToClose) {
this.toggle(false)
}
},
- addDraggableListeners() {
+ addDraggableListeners () {
if (!this.draggable) {
return
}
@@ -570,7 +570,7 @@ export default {
}
},
- removeDraggableListeners() {
+ removeDraggableListeners () {
// console.log('removing draggable handlers')
},
@@ -581,7 +581,7 @@ export default {
* wrapping afterEvents in `$nextTick` fixes `$refs.modal` undefined bug.
* (fixes #15)
*/
- callAfterEvent(state) {
+ callAfterEvent (state) {
if (state) {
this.connectObserver()
} else {
@@ -600,7 +600,7 @@ export default {
* 1. modal opened
* 2. MutationObserver's observe callback
*/
- updateRenderedHeight() {
+ updateRenderedHeight () {
if (this.$refs.modal) {
this.modal.renderedHeight = this.$refs.modal.getBoundingClientRect().height
}
@@ -610,7 +610,7 @@ export default {
* Start observing modal's DOM, if childList or subtree changes,
* the callback (registered in beforeMount) will be called.
*/
- connectObserver() {
+ connectObserver () {
if (this.mutationObserver) {
this.mutationObserver.observe(this.$refs.modal, {
childList: true,
@@ -623,7 +623,7 @@ export default {
/**
* Disconnects MutationObserver
*/
- disconnectObserver() {
+ disconnectObserver () {
if (this.mutationObserver) {
this.mutationObserver.disconnect()
}
diff --git a/src/Resizer.vue b/src/Resizer.vue
index c56dc3f..a001d2f 100644
--- a/src/Resizer.vue
+++ b/src/Resizer.vue
@@ -16,22 +16,22 @@ export default {
default: 0
}
},
- data() {
+ data () {
return {
clicked: false,
size: {}
}
},
- mounted() {
+ mounted () {
this.$el.addEventListener('mousedown', this.start, false)
},
computed: {
- className() {
+ className () {
return { 'vue-modal-resizer': true, clicked: this.clicked }
}
},
methods: {
- start(event) {
+ start (event) {
this.clicked = true
window.addEventListener('mousemove', this.mousemove, false)
@@ -40,7 +40,7 @@ export default {
event.stopPropagation()
event.preventDefault()
},
- stop() {
+ stop () {
this.clicked = false
window.removeEventListener('mousemove', this.mousemove, false)
@@ -51,10 +51,10 @@ export default {
size: this.size
})
},
- mousemove(event) {
+ mousemove (event) {
this.resize(event)
},
- resize(event) {
+ resize (event) {
var el = this.$el.parentElement
if (el) {
diff --git a/src/index.js b/src/index.js
index 23d2032..1ed9163 100644
--- a/src/index.js
+++ b/src/index.js
@@ -4,7 +4,7 @@ import Dialog from './Dialog.vue'
const defaultComponentName = 'modal'
const Plugin = {
- install(Vue, options = {}) {
+ install (Vue, options = {}) {
/**
* Makes sure that plugin can be insstalled only once
*/
@@ -18,15 +18,15 @@ const Plugin = {
* Plugin API
*/
Vue.prototype.$modal = {
- show(name, params) {
+ show (name, params) {
Plugin.event.$emit('toggle', name, true, params)
},
- hide(name, params) {
+ hide (name, params) {
Plugin.event.$emit('toggle', name, false, params)
},
- toggle(name, params) {
+ toggle (name, params) {
Plugin.event.$emit('toggle', name, undefined, params)
}
}
From c36dfb3c3961edbe9c2d6d8b05b0d6915d086e5c Mon Sep 17 00:00:00 2001
From: Noel De Martin
Date: Thu, 15 Feb 2018 18:29:42 +0100
Subject: [PATCH 2/2] #173 Implement dynamic modals functionality
---
README.md | 48 +++++++++++++++++-
demo/client_side_rendering/src/App.vue | 49 ++++++++++++++++++
.../src/components/CustomComponentModal.vue | 12 +++++
demo/client_side_rendering/src/main.js | 3 +-
package.json | 1 +
src/ModalsContainer.vue | 50 +++++++++++++++++++
src/index.js | 25 ++++++++--
types/index.d.ts | 4 +-
8 files changed, 184 insertions(+), 8 deletions(-)
create mode 100644 demo/client_side_rendering/src/components/CustomComponentModal.vue
create mode 100644 src/ModalsContainer.vue
diff --git a/README.md b/README.md
index cda03f3..c767b0d 100644
--- a/README.md
+++ b/README.md
@@ -113,7 +113,7 @@ this.$modal.show('dialog', {
title: 'Alert!',
text: 'You are too awesome',
buttons: [
- {
+ {
title: 'Deal with it',
handler: () => { alert('Woot!') }
},
@@ -122,7 +122,7 @@ this.$modal.show('dialog', {
default: true, // Will be triggered by default if 'Enter' pressed.
handler: () => {} // Button click handler
},
- {
+ {
title: 'Close'
}
]
@@ -133,6 +133,50 @@ this.$modal.show('dialog', {
+### Dynamic Modals
+
+In order to instantiate modals at runtime (for lazy-loading or decluttering templates), it is possible to create modals dynamically.
+
+To start using this feature you must set `dynamic: true` in plugin configuration:
+
+```js
+Vue.use(VModal, { dynamic: true })
+```
+
+And include the ` ` component it in your project:
+
+```vue
+
+```
+
+Call it (the first argument is the component definition, the second are component properties, and the third modal parameters):
+
+```javascript
+this.$modal.show({
+ template: `
+
+
This is created inline
+
{{ text }}
+
+ `,
+ props: ['text']
+}, {
+ text: 'This text is passed as a property'
+})
+```
+
+It can also be used with `.vue` files:
+
+```javascript
+import MyComponent from './MyComponent.vue'
+
+this.$modal.show(MyComponent, {
+ text: 'This text is passed as a property'
+}, {
+ draggable: true
+})
+```
+
For more examples please take a look at [vue-js-modal.yev.io](http://vue-js-modal.yev.io).
### SSR
diff --git a/demo/client_side_rendering/src/App.vue b/demo/client_side_rendering/src/App.vue
index 5772188..b49fcc5 100644
--- a/demo/client_side_rendering/src/App.vue
+++ b/demo/client_side_rendering/src/App.vue
@@ -7,6 +7,8 @@
+
+
Dialog: buttons
+
+
+ Dynamic: Runtime Modal
+
+
+ Dynamic: Component Modal
+
+
+ Dynamic: Component Modal with modal params
+
@@ -118,6 +136,7 @@ import DemoLoginModal from './components/DemoLoginModal.vue'
import DemoDogProfileModal from './components/DogProfileModal.vue'
import DemoConditionalModal from './components/ConditionalModal.vue'
import DemoSizeModal from './components/SizeModal.vue'
+import CustomComponentModal from './components/CustomComponentModal.vue'
export default {
name: 'app',
@@ -203,6 +222,36 @@ export default {
})
},
+ showDynamicRuntimeModal () {
+ this.$modal.show({
+ template: `
+
+
This is created inline
+
{{ text }}
+
+ `,
+ props: ['text']
+ }, {
+ text: 'This text is passed as a property'
+ })
+ },
+
+ showDynamicComponentModal () {
+ this.$modal.show(CustomComponentModal, {
+ text: 'This text is passed as a property'
+ })
+ },
+
+ showDynamicComponentModalWithModalParams () {
+ this.$modal.show(CustomComponentModal, {
+ text: 'This text is passed as a property'
+ }, {
+ resizable: true,
+ adaptive: true,
+ draggable: true,
+ })
+ },
+
dialogEvent (eventName) {
console.log('Dialog event: ' + eventName)
}
diff --git a/demo/client_side_rendering/src/components/CustomComponentModal.vue b/demo/client_side_rendering/src/components/CustomComponentModal.vue
new file mode 100644
index 0000000..ac4b322
--- /dev/null
+++ b/demo/client_side_rendering/src/components/CustomComponentModal.vue
@@ -0,0 +1,12 @@
+
+
+
This is a custom component
+
{{ text }}
+
+
+
+
diff --git a/demo/client_side_rendering/src/main.js b/demo/client_side_rendering/src/main.js
index e88b15c..2a5aab7 100644
--- a/demo/client_side_rendering/src/main.js
+++ b/demo/client_side_rendering/src/main.js
@@ -3,7 +3,8 @@ import App from './App.vue'
import VueJsModal from 'plugin'
Vue.use(VueJsModal, {
- dialog: true
+ dialog: true,
+ dynamic: true,
})
new Vue({
diff --git a/package.json b/package.json
index 65ae70a..3fffe85 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"build:ssr-no-css": "webpack --config ./build/webpack.ssr-no-css.config.js --progress --hide-modules",
"lint": "eslint --ext .js,.vue src test/unit/specs",
"unit": "./node_modules/karma/bin/karma start test/unit/karma.conf.js",
+ "watch": "webpack --config ./build/webpack.client.config.js --progress --hide-modules --watch",
"build": "npm run build:client && npm run build:ssr && npm run build:ssr-no-css",
"test:types": "tsc -p types/test"
},
diff --git a/src/ModalsContainer.vue b/src/ModalsContainer.vue
new file mode 100644
index 0000000..479dedd
--- /dev/null
+++ b/src/ModalsContainer.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
diff --git a/src/index.js b/src/index.js
index 1ed9163..ef0a566 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,5 +1,6 @@
import Modal from './Modal.vue'
import Dialog from './Dialog.vue'
+import ModalsContainer from './ModalsContainer.vue'
const defaultComponentName = 'modal'
@@ -14,14 +15,26 @@ const Plugin = {
this.installed = true
this.event = new Vue()
+ this.dynamicContainer = null
+
/**
* Plugin API
*/
Vue.prototype.$modal = {
- show (name, params) {
- Plugin.event.$emit('toggle', name, true, params)
+ _setDynamicContainer (dynamicContainer) {
+ Plugin.dynamicContainer = dynamicContainer
+ },
+ show (modal, paramsOrProps, params) {
+ if (typeof modal === 'string') {
+ Plugin.event.$emit('toggle', modal, true, paramsOrProps)
+ } else {
+ if (Plugin.dynamicContainer === null) {
+ console.warn('[vue-js-modal] In order to render dynamic modals, a component must be present on the page')
+ } else {
+ Plugin.dynamicContainer.add(modal, paramsOrProps, params)
+ }
+ }
},
-
hide (name, params) {
Plugin.event.$emit('toggle', name, false, params)
},
@@ -41,6 +54,12 @@ const Plugin = {
if (options.dialog) {
Vue.component('v-dialog', Dialog)
}
+ /**
+ * Registration of component
+ */
+ if (options.dynamic) {
+ Vue.component('modals-container', ModalsContainer)
+ }
}
}
diff --git a/types/index.d.ts b/types/index.d.ts
index a2a7d12..0ca8d46 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -1,4 +1,4 @@
-import Vue, { PluginObject } from "vue";
+import Vue, { PluginObject, ComponentOptions } from "vue";
declare const VueJSModal: PluginObject;
export default VueJSModal;
@@ -9,7 +9,7 @@ export declare interface VueJSModalOptions {
}
declare interface VModal {
- show(name: string, params?: object): void;
+ show(modal: string | typeof Vue | ComponentOptions, paramsOrProps?: object, params?: object): void;
hide(name: string, params?: object): void;
toggle(name: string, params?: object): void;
}