Skip to content

Commit ff4b15a

Browse files
author
Pooya Parsa
committed
⚡ Modals
1 parent e130821 commit ff4b15a

File tree

5 files changed

+268
-115
lines changed

5 files changed

+268
-115
lines changed

components/modal.vue

Lines changed: 158 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,175 @@
11
<template>
2-
<div style="display: none">
3-
<div :id="id"
4-
:class="{ modal:true,fade: fade, show: animateModal || !fade }"
5-
style="display: block"
6-
@click="onClickOut($event)">
7-
<div :class="['modal-dialog','modal-'+size]" role="document" style="z-index: 9999">
8-
<div class="modal-content">
9-
<div class="modal-header">
10-
<slot name="modal-header"></slot>
11-
</div>
12-
<div class="modal-body">
13-
<slot name="modal-body"></slot>
14-
</div>
15-
<div class="modal-footer">
16-
<slot name="modal-footer"></slot>
17-
</div>
2+
<div v-if="visible">
3+
<div :id="id"
4+
:class="['modal',fade?'fade':null,visible?'show':null]"
5+
style="display: block"
6+
@click="onClickOut($event)">
7+
<div :class="['modal-dialog','modal-'+size]">
8+
<div class="modal-content">
9+
10+
<div class="modal-header" v-if="!hideHeader">
11+
<slot name="modal-header">
12+
<div class="modal-title">
13+
<slot name="modal-title">{{title}}</slot>
14+
</div>
15+
<button type="button" class="close" aria-label="Close" @click="hide">
16+
<span aria-hidden="true">&times;</span>
17+
</button>
18+
</slot>
19+
</div>
20+
21+
<div class="modal-body">
22+
<slot></slot>
23+
</div>
24+
25+
<div class="modal-footer" v-if="!hideFooter">
26+
<slot name="modal-footer">
27+
<b-btn variant="secondary" @click="$close">{{closeTitle}}</b-btn>
28+
<b-btn variant="primary" @click="$save">{{saveTitle}}</b-btn>
29+
</slot>
30+
</div>
31+
32+
</div>
33+
</div>
1834
</div>
19-
</div>
35+
<div :class="['modal-backdrop','show',fade?'fade':null]"></div>
2036
</div>
21-
<div class="modal-backdrop" :class="{ fade: fade, show: animateBackdrop || !fade }"></div>
22-
</div>
2337
</template>
2438

2539
<script>
26-
import {csstransitions} from '../utils/helpers';
27-
28-
// this is directly linked to the bootstrap animation timing in _modal.scss
29-
// // for browsers that do not support transitions like IE9 just change slide immediately
30-
const TRANSITION_DURATION = csstransitions() ? 300 : 0;
31-
32-
export default {
33-
replace: true,
34-
computed: {},
35-
data() {
36-
return {
37-
animateBackdrop: false,
38-
animateModal: false,
39-
visible: false
40-
};
41-
},
42-
props: {
43-
id: {
44-
type: String,
45-
default: 'default'
46-
},
47-
size: {
48-
type: String,
49-
default: 'md'
40+
export default {
41+
data() {
42+
return {
43+
visible: false
44+
};
5045
},
51-
fade: {
52-
type: Boolean,
53-
default: true
54-
},
55-
closeOnBackdrop: {
56-
type: Boolean,
57-
default: true
58-
}
59-
},
60-
methods: {
61-
show() {
62-
this.visible = true;
63-
this.$el.style.display = 'block';
64-
this._body = document.querySelector('body');
65-
const _this = this;
66-
// wait for the display block, and then add class "show" class on the modal
67-
this._modalAnimation = setTimeout(() => {
68-
_this.animateBackdrop = true;
69-
this._modalAnimation = setTimeout(() => {
70-
_this._body.classList.add('modal-open');
71-
_this.animateModal = true;
72-
_this.$root.$emit('shown::modal', this.id);
73-
}, (_this.fade) ? TRANSITION_DURATION : 0);
74-
}, 0);
46+
computed: {
47+
body() {
48+
if (typeof document !== 'undefined') {
49+
return document.querySelector('body');
50+
}
51+
}
7552
},
76-
hide() {
77-
this.visible = false;
78-
const _this = this;
79-
// first animate modal out
80-
this.animateModal = false;
81-
this._modalAnimation = setTimeout(() => {
82-
// wait for animation to complete and then hide the backdrop
83-
_this.animateBackdrop = false;
84-
this._modalAnimation = setTimeout(() => {
85-
_this._body.classList.remove('modal-open');
86-
// no hide the modal wrapper
87-
_this.$el.style.display = 'none';
88-
_this.$root.$emit('hidden::modal', this.id);
89-
}, (_this.fade) ? TRANSITION_DURATION : 0);
90-
}, (_this.fade) ? TRANSITION_DURATION : 0);
53+
props: {
54+
id: {
55+
type: String,
56+
default: null,
57+
},
58+
title: {
59+
type: String,
60+
default: 'Save',
61+
},
62+
size: {
63+
type: String,
64+
default: 'md'
65+
},
66+
fade: {
67+
type: Boolean,
68+
default: true
69+
},
70+
closeTitle: {
71+
type: String,
72+
default: 'Close'
73+
},
74+
onClose: {
75+
type: Function,
76+
default: null,
77+
},
78+
saveTitle: {
79+
type: String,
80+
default: 'OK'
81+
},
82+
onSave: {
83+
type: Function,
84+
default: null,
85+
},
86+
closeOnBackdrop: {
87+
type: Boolean,
88+
default: true
89+
},
90+
hideHeader: {
91+
type: Boolean,
92+
default: false
93+
},
94+
hideFooter: {
95+
type: Boolean,
96+
default: false
97+
},
9198
},
92-
onClickOut(e) {
93-
// if backdrop clicked, hide modal
94-
if (this.closeOnBackdrop && e.target.id && e.target.id === this.id) {
99+
methods: {
100+
show() {
101+
if (this.visible) {
102+
return;
103+
}
104+
this.visible = true;
105+
this.$root.$emit('shown::modal', this.id);
106+
this.body.classList.add('modal-open');
107+
},
108+
hide() {
109+
if (!this.visible) {
110+
return;
111+
}
112+
this.visible = false;
113+
this.$root.$emit('hide::modal', this.id);
114+
this.body.classList.remove('modal-open');
115+
},
116+
$save() {
117+
if (this.onSave) {
118+
if (this.onSave() === false) {
119+
// Cancel event
120+
return;
121+
}
122+
}
123+
this.hide();
124+
},
125+
$close() {
126+
if (this.onClose) {
127+
this.onClose();
128+
}
95129
this.hide();
130+
},
131+
onClickOut(e) {
132+
// If backdrop clicked, hide modal
133+
if (this.closeOnBackdrop && e.target.id && e.target.id === this.id) {
134+
this.hide();
135+
}
136+
},
137+
pressedButton(e) {
138+
// If not visible don't do anything
139+
if (!this.visible) {
140+
return;
141+
}
142+
143+
// Support for esc key press
144+
const key = e.which || e.keyCode;
145+
if (key === 27) { // 27 is esc
146+
this.hide();
147+
}
96148
}
97149
},
98-
pressedButton(e) {
99-
if (!this.visible) {
100-
// if not visible don't do anything
101-
return;
102-
}
150+
created() {
151+
this.$root.$on('show::modal', id => {
152+
if (id === this.id) {
153+
this.show();
154+
}
155+
});
103156
104-
// support for esc key press
105-
const key = e.which || e.keyCode;
106-
if (key === 27) { // 27 is esc
107-
this.hide();
157+
this.$root.$on('hide::modal', id => {
158+
if (id === this.id) {
159+
this.hide();
160+
}
161+
});
162+
},
163+
mounted() {
164+
if (typeof document !== 'undefined') {
165+
document.addEventListener('keydown', this.pressedButton);
166+
}
167+
},
168+
destroyed() {
169+
if (typeof document !== 'undefined') {
170+
document.removeEventListener('keydown', this.pressedButton);
108171
}
109172
}
110-
},
111-
created() {
112-
const hub = this.$root;
113-
hub.$on('show::modal', id => id === this.id && this.show()).bind(this);
114-
hub.$on('hide::modal', id => id === this.id && this.hide()).bind(this);
115-
},
116-
mounted() {
117-
document.addEventListener('keydown', this.pressedButton);
118-
},
119-
destroyed() {
120-
clearTimeout(this._modalAnimation);
121-
document.removeEventListener('keydown', this.pressedButton);
122-
}
123-
};
173+
};
124174
125175
</script>

docs/data/site.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ export default {
2424
{title: 'Form Radio'},
2525
{title: 'Form Checkbox'},
2626
{title: 'Form Select'},
27+
{title: 'Modals', experimental:true},
2728
{title: 'Nav'},
2829
{title: 'NavBar'},
2930
{title: 'Pagination'},
30-
{title: 'Popover', new: true},
31-
{title: 'Tables', new: true}
31+
{title: 'Popover'},
32+
{title: 'Tables'}
3233
]
3334
}
3435
]

docs/includes/sidebar.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
:key="group.slug"
1313
>
1414
{{ page.title }}
15-
<span class="badge badge-danger" v-if="page.new">NEW</span>
15+
<span class="badge badge-success" v-if="page.new">NEW</span>
16+
<span class="badge badge-danger" v-if="page.experimental">EXP</span>
1617
</b-nav-item>
1718
</b-nav>
1819

docs/layouts/components.vue

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
<slot name="demo"></slot>
2828
</div>
2929

30+
<h2>Usage</h2>
31+
<code v-code class="html">
32+
<slot name="usage"></slot>
33+
</code>
34+
3035
<template v-if="props_items && props_items.length > 0">
3136
<h2>Properties</h2>
3237
<br>
@@ -39,6 +44,15 @@
3944
</section>
4045
</template>
4146

47+
<template v-if="docs.slots && docs.slots.length > 0">
48+
<h2>Slots</h2>
49+
<br>
50+
<section>
51+
<b-table :items="docs.slots" :fields="slots_fields" striped>
52+
</b-table>
53+
</section>
54+
</template>
55+
4256
<template v-if="docs.events && docs.events.length > 0">
4357
<h2>Events</h2>
4458
<br>
@@ -54,10 +68,6 @@
5468
</section>
5569
</template>
5670

57-
<h2>Usage</h2>
58-
<code v-code class="html">
59-
<slot name="usage"></slot>
60-
</code>
6171

6272
</template>
6373
</layout>
@@ -102,6 +112,12 @@
102112
description: {label: 'Description'}
103113
};
104114
},
115+
slots_fields() {
116+
return {
117+
name: {label: 'Slot'},
118+
description: {label: 'Description'},
119+
};
120+
},
105121
props_items() {
106122
const component = Vue.options.components[this.docs.component];
107123
const props = component.options.props;

0 commit comments

Comments
 (0)