Skip to content

Commit e6c27a9

Browse files
committed
feat: quasarframework#40 Polished Modal: <quasar-modal> component
1 parent 73966fa commit e6c27a9

File tree

8 files changed

+138
-10
lines changed

8 files changed

+138
-10
lines changed

dev/app.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ router.map({
2121
'/card': { component: require('view/card.vue') },
2222
'/chat': { component: require('view/chat.vue') },
2323
'/collapsible': { component: require('view/collapsible.vue') },
24+
'/context-menu': { component: require('view/context-menu.vue') },
2425
'/css-elements': { component: require('view/css-elements.vue') },
2526
'/dialog': { component: require('view/dialog.vue') },
2627
'/fab': { component: require('view/fab.vue') },

dev/views/context-menu.vue

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<template>
2+
<div>
3+
<div class="layout-padding">
4+
<p class="caption">
5+
<span class="desktop-only">
6+
Right click anywhere on the Page below (not on header though).
7+
<br>
8+
On a real mobile device it works different by opening a minimized Modal triggered by a long tap.
9+
</span>
10+
<span class="mobile-only">
11+
Long Tap anywhere on the Page below (not on header though).
12+
<br>
13+
On a desktop it works different by opening a Popover.
14+
</span>
15+
</p>
16+
<blockquote>
17+
<small>
18+
Works with any elements you want. It doesn't have to be a List.
19+
</small>
20+
</blockquote>
21+
</div>
22+
23+
<quasar-context-menu v-ref:context>
24+
<div class="list item-delimiter highlight">
25+
<div
26+
class="item item-link"
27+
v-for="n in 30"
28+
@click="showToast(), $refs.context.close()"
29+
>
30+
<div class="item-content">
31+
<div class="item-label">Label</div>
32+
<div class="item-value">Value</div>
33+
</div>
34+
</div>
35+
</div>
36+
</quasar-context-menu>
37+
</div>
38+
</template>
39+
40+
<script>
41+
import { Platform, Toast } from 'quasar'
42+
43+
export default {
44+
methods: {
45+
showToast () {
46+
Toast.create((Platform.is.desktop ? 'Clicked' : 'Tapped') + ' on a context menu item.')
47+
}
48+
}
49+
}
50+
</script>

dev/views/index.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export default {
2020
'Card',
2121
'Chat',
2222
'Collapsible',
23+
'Context Menu',
2324
'CSS Elements',
2425
'Dialog',
2526
'Fab',

dev/views/modal.vue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,17 @@
99
</p>
1010
<p>
1111
<button class="primary" @click="toggleModal()">Toggle Modal</button>
12+
<button class="primary" @click="showInlineModal()">Show Inline Modal</button>
1213
</p>
14+
15+
<quasar-modal :config="{minimized: true}" v-ref:modal>
16+
<div style="padding: 50px">
17+
<div>Some simple Modal</div>
18+
<div>Variable from parent Vue scope: {{ modalVariable }}</div>
19+
<br><br>
20+
<button class="primary" @click="$refs.modal.close()">Close</button>
21+
</div>
22+
</quasar-modal>
1323
</template>
1424

1525
<script>
@@ -30,7 +40,15 @@ export default {
3040
beforeDestroy () {
3141
this.modal.destroy()
3242
},
43+
data () {
44+
return {
45+
modalVariable: 5
46+
}
47+
},
3348
methods: {
49+
showInlineModal () {
50+
this.$refs.modal.show()
51+
},
3452
toggleModal () {
3553
this.modal.show()
3654
setTimeout(() => {

src/components/modal/modal.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,23 @@ class Modal {
1111
if (!userVm) {
1212
throw new Error('Modal needs a VueModel.')
1313
}
14-
if (!userVm.el && !userVm.template) {
14+
15+
if (userVm.nodeType) {
16+
this.__customElement = userVm
17+
}
18+
else if (!userVm.template) {
19+
console.error('Error in Modal create object:', userVm)
1520
throw new Error('Modal needs a template.')
1621
}
22+
else if (userVm.el) {
23+
console.error('Error in Modal create object:', userVm)
24+
throw new Error('Specifying "el" property for VM in a Modal is forbidden')
25+
}
1726

1827
let vm
19-
this.__customElement = typeof userVm.el !== 'undefined'
2028

2129
if (this.__customElement) {
22-
vm = userVm
30+
vm = {el: this.__customElement}
2331
}
2432
else {
2533
vm = Utils.extend(true, userVm)
@@ -69,7 +77,7 @@ class Modal {
6977
this.$content.classList.add('maximized')
7078
}
7179

72-
if (this.__customElement) {
80+
if (this.closeWithBackdrop) {
7381
this.$backdrop.addEventListener('click', this.close)
7482
}
7583

@@ -155,7 +163,7 @@ class Modal {
155163
}
156164

157165
this.$backdrop.classList.remove('active')
158-
if (this.__customElement) {
166+
if (this.closeWithBackdrop) {
159167
this.$backdrop.removeEventListener('click', this.close)
160168
}
161169
if (!Platform.within.iframe) {

src/components/modal/modal.vue

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<template>
2+
<div class="modal hidden fullscreen flex items-center justify-center">
3+
<div class="modal-backdrop backdrop"></div>
4+
<div class="modal-content">
5+
<slot></slot>
6+
</div>
7+
</div>
8+
</template>
9+
10+
<script>
11+
import Modal from './modal'
12+
13+
export default {
14+
props: {
15+
config: {
16+
type: Object
17+
}
18+
},
19+
methods: {
20+
show (onShow) {
21+
if (this.modal) {
22+
console.warn('Trying to show modal while it is being displayed.')
23+
return
24+
}
25+
26+
this.modal = Modal.create(this.$el)
27+
.set(this.config || {})
28+
.onClose(() => {
29+
this.modal = null
30+
})
31+
.show(onShow)
32+
},
33+
close (onClose) {
34+
if (!this.modal) {
35+
console.warn('Trying to close modal but no modal exists.')
36+
}
37+
38+
this.modal.close(onClose)
39+
}
40+
},
41+
beforeDestroy () {
42+
if (this.modal) {
43+
this.modal.destroy()
44+
}
45+
}
46+
}
47+
</script>

src/install.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import GridTable from './vue-components/grid/grid-table.vue'
2727
import InfiniteScroll from './vue-components/infinite-scroll/infinite-scroll.vue'
2828
import Layout from './vue-components/layout/layout.vue'
2929
import ToolbarTitle from './vue-components/layout/toolbar-title.vue'
30+
import Modal from './components/modal/modal.vue'
3031
import Numeric from './vue-components/numeric/numeric.vue'
3132
import Pagination from './vue-components/pagination/pagination.vue'
3233
import Parallax from './vue-components/parallax/parallax.vue'
@@ -81,6 +82,7 @@ function registerComponents (_Vue) {
8182
_Vue.component('quasar-infinite-scroll', InfiniteScroll)
8283
_Vue.component('quasar-layout', Layout)
8384
_Vue.component('quasar-toolbar-title', ToolbarTitle)
85+
_Vue.component('quasar-modal', Modal)
8486
_Vue.component('quasar-numeric', Numeric)
8587
_Vue.component('quasar-pagination', Pagination)
8688
_Vue.component('quasar-parallax', Parallax)

src/vue-components/context-menu/context-menu-mobile.vue

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ export default {
2323
this.target = this.$el.parentNode
2424
2525
this.handler = () => {
26-
this.modal = Modal.create({
27-
el: this.$el
28-
}).set({
29-
minimized: true
30-
}).show()
26+
this.modal = Modal.create(this.$el)
27+
.set({
28+
minimized: true,
29+
closeWithBackdrop: true
30+
})
31+
.show()
3132
}
3233
3334
this.touchStartHandler = (event) => {

0 commit comments

Comments
 (0)