ã¿ãªããããã«ã¡ã¯ïŒ
ç§ã®æåã®åºçç©ã¯äžå¿«ãªåŸå³ã§ããã ç§ã¯ãã®èª€è§£ãæ£ãããšãçŽæããããªãã®æ³å»·ã«VueJsã«é¢ããæåã®èšäºã®ã¬ãã¹ã³ãæäŸããŸãã 圹ã«ç«ã€ããšãé¡ã£ãŠããŸãã å€ãã®èããå€ãã®çµéšã ç§ã¯ä»ã®äººã®èšäºãæèšã«æ²¿ã£ãŠå匷ããŠããŸããã ç¥èãå
±æããæã§ãã
ãããŠãã¢ãŒãã«ãŠã£ã³ããŠãäœæããŸãã ã¯ãã圌ãã¯åã³ã§ãã ããããç§ã®æåã®ïŒç§ã®ãã®ã§ã¯ãªãïŒåºçç©ã§èª¬æãããŠããã»ã©åçŽã§ã¯ãããŸããã
Vueã®å€ãã¯ãã§ã«äœæãããŠããŸãã ã¿ããªäœ¿çšããŸããã ãããŠãæããã«ã楜åšã®æææš©ã®ç¹å®ã®ã¬ãã«ïŒãã®å Žåã¯
Vue ïŒã«éããããããã«èªè»¢è»ãäœããããšæãã§ãããã ãããŠãç§ã¯èŠåã®äŸå€ã§ããããŸããã§ããã
å©çšå¯èœãªãã¹ãŠã®ã¢ãŒãã«ã³ã³ããŒãã³ãã®ãã¡ãäž»ã«ããã䜿çšããŸãã
-Vuedals ã
ããããç§ã¯ãããã¢ããã°ã¬ãŒãããããšã«ããŸããã ååãšããŠã
EventBusãšãŠã£ã³ããŠã®ééã«é¢é£ããã€ãã³ãã®çžäºäœçšã®ã¿ãã³ã¢ããæ®ããŸããã ã¡ã€ã³ã³ã³ããŒãã³ããæžãçŽãããŠã©ãããŒã³ã³ãããŒã«ãªããæ°ããã³ã³ããŒãã³ãïŒã¢ãŒãã«ãŠã£ã³ããŠèªäœïŒãè¿œå ãããŸããã
ãããããŸãæåã«ã ãããŠãèšäºã¯éåžžã«å€§ãããªãã誰ããã¹ã¿ãŒããã§ãããããã®ãã³ãµã :)
åºæ¬çã«ããã¹ãŠã®äŸã®ã¢ãŒãã«ãŠã£ã³ããŠã¯ããã®ã¹ã¿ã€ã«ã§åŒã³åºãããŸãã
<template> <button @click="visible = true">Show modal</button> <modal :show="visible"> <div> </div> </modal> </template> <script> export default { data() { return { visible: false } } </script>
ãã¹ãŠãçŸããããã§ãã ãããïŒ
ãã®ã¢ãããŒãã®æ¬ ç¹ã¯äœã§ããïŒ
ãŸããã¢ãŒãã«ãŠã£ã³ããŠãã³ãã¬ãŒãã¯èŠªã³ã³ããŒãã³ãå
ã«ããã芪ã³ã³ããŒãã³ããšåŒã°ããŸãã ãŸãããŠã£ã³ããŠã³ã³ããã¹ãã¯èŠªããåé¢ãããŠããŸããã å¿
ããã䟿å©ã§å¿
èŠãªããã§ã¯ãããŸããã
次ã«ãåããŠã£ã³ããŠãè€æ°ã®å Žæã§äœ¿çšãããŠããå Žåãã³ãŒããè€è£œããå¿
èŠããããŸãã ã¶ãã¶ããããããªãïŒ
第äžã«ãããã¯ããããæãéèŠãªæ¬ ç¹ã§ã-ã¢ãŒãã«ãŠã£ã³ããŠã¯ãããŒãžãŸãã¯ä»ã®
Vueã³ã³ããŒãã³ãå
ã§ã®ã¿äœ¿çšã§ããŸããã
Vuex ã
Routerãªã©ã®å Žæã§ã¯äœ¿çšã§ããŸããã ããšãã°ãäœããã®ã€ãã³ãã§ã«ãŒã¿ãŒãŸãã¯ã¹ãã¢ããã¢ãŒãã«ãã°ã€ã³/ç»é²ãŠã£ã³ããŠãåŒã³åºãå¿
èŠããããŸãã äŸã¯ããªãªã³ã«ãã£ãŠäžããããšãã§ããŸãã
ãããã£ãŠããã©ã¡ãŒã¿ãŒã䜿çšããŠé¢æ°ãåŒã³åºãã次ã®åœ¢åŒã®ãçã®ãã³ã³ããŒãã³ããæž¡ãããšã§ãŠã£ã³ããŠãééãããšãã«ã
Vuedalsã䜿çšããã¢ãããŒã-
{ name: 'simple-modal', props: ['test'], template: "<div>{{test}}</div>" }
ãŸãã¯å€éšããã€ã³ããŒãããæ¬æ Œçãªãã®ã奜ãã§ããã
ããå€ãã®å¶åŸ¡ãåå©çšãªãã·ã§ã³ãããã³ãã®ãããªãŠã£ã³ããŠãã»ãŒã©ãããã§ãåŒã³åºãããšãã§ããŸãã
äžè¬çã«ã¯ãã®ããã«èŠããŸããã
ããšãã° ãã¢ããªã±ãŒã·ã§ã³ã«ã€ã³ããŒãããŠã«ãŒãAppã³ã³ããŒãã³ãã«è²Œãä»ãã
ModalWrapperã³ã³ããŒãã³ãããããŸãã äžã®ã©ããã
次ã«ãã©ãã§ãthisã$ Modals.openã¡ãœããïŒ{componentïŒSimpleModalãtitleïŒ 'Simple modal'}ïŒã
åŒã³åºããŸããããã§ã
ModalWrapperã§ã¬ã³ããªã³ã°ãããã¢ãŒãã«ãŠã£ã³ããŠã衚瀺ããã³è¡šç€ºãããŠã£ã³ããŠãšã³ã³ããŒãã³ãã®èšå®ã転éããŸãã
ãŠã£ã³ããŠã§ã®ãã¹ãŠã®æäœäžã«çºçããã€ãã³ãã®æãããããããã®ã€ãã³ãã¯
EventBusã䜿çšããŠå¶åŸ¡ãããããããèããŠäœããã®åœ¢ã§åå¿ããããšãã§ããŸãã
ããã¯ãæ
å ±ãåŠç¿ããããããããã®å
¥åã§ãã åºæ¬çã«ããã®èšäºã¯Vueã®åå¿è
åãã§ãã ããããç§ã¯ããã€ãã®èå³æ·±ãé
åçãªç¬éãããããšãé¡ã£ãŠããŸãã
éäžã§ããã€ãã®ã³ãŒããšã³ã¡ã³ããã¹ããŒããŸãã æåŸã«äŸãšãœãŒã¹ãžã®ãªã³ã¯ããããŸãã
ããŠãã¡ã€ã³ãã¡ã€ã«ããå§ããŸããã-
index.js import Bus from './utils/bus'; import ModalWrapper from './modal-wrapper'; import Modal from './modal' const VuModal = {} VuModal.install = (Vue) => { Vue.prototype.$modals = new Vue({ name: '$modals', created() { Bus.$on('opened', data => { this.$emit('modals:opened', data); }); Bus.$on('closed', data => { this.$emit('modals:closed', data); }); Bus.$on('destroyed', data => { this.$emit('modals:destroyed', data); }); this.$on('new', options => { this.open(options); }); this.$on('close', data => { this.close(data); }); this.$on('dismiss', index => { this.dismiss(index || null); }); }, methods: { open(options = null) { Bus.$emit('new', options); }, close(data = null) { Bus.$emit('close', data); }, dismiss(index = null) { Bus.$emit('dismiss', index); } } }); Vue.mixin({ created() { this.$on('modals:new', options => { Bus.$emit('new', options); }); this.$on('modals:close', data => { Bus.$emit('close', data); }); this.$on('modals:dismiss', index => { Bus.$emit('dismiss', index); }); } }); } if (typeof window !== 'undefined' && window.Vue) { window.Vue.use(VuModal); } export default VuModal; export { ModalWrapper, Modal, Bus }
ãã®äžã§ãã¢ãŒãã«ããªã¢ãŒãã«ãŠã£ã³ããŠã«äœ¿çšãããã³ã³ããŒãã³ããã€ã³ããŒãããŸãã
- ModalWrapper.js-ãŠã£ã³ããŠã衚瀺ããããã®äžè¬çãªã©ãããŒ
- Modal.js-ã¢ãŒãã«ãŠã£ã³ããŠèªäœã®ã³ã³ããŒãã³ãã ãªãªãžãã«ã®Vuedalã«ã¯ãããŸããã çŽæ¥äœ¿çšããå¿
èŠã¯ãããŸããã ãããã«ãããããã¯å
éšã§æ©èœããŸãã åã«æ²¿ã£ãŠãããªãã¯è³ã§ãã®æ°å³ãèŠãã§ãããããããŠããªãç§ããããå ããã®ããæããã«ãªãã§ãããã
- Bus.js-ã©ãããŒã³ã³ããŒãã³ãïŒ ModalWrapper ïŒãã¢ãŒãã«ãŠã£ã³ããŠïŒ Modal ïŒãããã³VueJsã¢ããªã±ãŒã·ã§ã³éã®éä¿¡çšã®EventBus ã
Bus.jsã³ãŒããããã«æäŸããããã§äœãèµ·ãããã説æããŸãã åè¿°ããããã«ã
EventBusã¯å
ã®
ç¶æ
ã®ãŸãŸã«ãªããŸããã
Bus.js let instance = null; class EventBus { constructor() { if (!instance) { this.events = {}; instance = this; } return instance; } $emit(event, message) { if (!this.events[event]) return; const callbacks = this.events[event]; for (let i = 0, l = callbacks.length; i < l; i++) { const callback = callbacks[i]; callback.call(this, message); } } $on(event, callback) { if (!this.events[event]) this.events[event] = []; this.events[event].push(callback); } } export default new EventBus();
ããã§ã¯ãã€ãã³ãïŒ
$ on ïŒããµãã¹ã¯ã©ã€ãããã€ãã³ããããªã¬ãŒïŒ
$ emit ïŒã§ãã
EventBusã®ã·ã³ã°ã«ãã³ã€ã³ã¹ã¿ã³ã¹ãäœæããŸãã 説æããç¹å¥ãªãã®ã¯ãªããšæããŸãã
EventBusã¯ã³ãŒã«ããã¯ãåéããå¿
èŠã«å¿ããŠããããåŒã³åºããŸãã ããã«ããã¹ãŠã®ã³ã³ããŒãã³ããã©ã®ããã«æ¥ç¶ããããæ確ã«ç解ã§ããããã«ãªããŸãã
ãããŠä»ãindex.js
ããã§ãããã©ã«ãã®ããã©ã«ãã®
ã€ã³ã¹ããŒã«é¢æ°ããšã¯ã¹ããŒãããŸã-ãŠã£ã³ããŠãã¢ããªã±ãŒã·ã§ã³ïŒ
Vue.useïŒïŒã䜿çšïŒãš
ModalWrapper ã
Modalããã³
Busã³ã³ããŒãã³ãã«æ¥ç¶ããŸãã ãã©ãŠã¶ã®ã¹ã¯ãªããã¿ã°ãä»ããŠ
VueUniversalModalãæ¥ç¶ããå ŽåãããŒãžäžã®ã°ããŒãã«
VueJãæ¥ç¶ãããŠããå Žåãã³ã³ããŒãã³ããã¢ã¯ãã£ãã«ããŸãã
ãããŠé çªã«ïŒ
$ã¢ãŒãã« Vue.prototype.$modals = new Vue({ name: '$modals', created() { Bus.$on('opened', data => { this.$emit('modals:opened', data); }); Bus.$on('closed', data => { this.$emit('modals:closed', data); }); Bus.$on('destroyed', data => { this.$emit('modals:destroyed', data); }); this.$on('new', options => { this.open(options); }); this.$on('close', data => { this.close(data); }); this.$on('dismiss', index => { this.dismiss(index || null); }); }, methods: { open(options = null) { Bus.$emit('new', options); }, close(data = null) { Bus.$emit('close', data); }, dismiss(index = null) { Bus.$emit('dismiss', index); } } });
ããã§ã¯ã
$ modalsãšåŒã°ãã
Vueã® ïŒ
ãããã¿ã€ããä»ã
ã ïŒã°ããŒãã«
VueJsã€ã³ã¹ã¿ã³ã¹ã«ããã¯ããŸãã
äœæãããã¡ãœããïŒã¢ããªã±ãŒã·ã§ã³ã®éå§çŽåŸã«éå§ïŒã§ã
EventBusãã
éãã ïŒãŠã£ã³ããŠãéãïŒã
éãã ïŒãŠã£ã³ããŠãéããïŒã
ç Žæ£ãã ïŒãŠã£ã³ããŠããªãã
ModalWrapperãåé€ããïŒã€ãã³ãã«çœ²åããŸãã ãããã®ã€ãã³ã
ãçºçãããšãEventBusã¯
modalsïŒ open ã
modalsïŒclosedãããã³
modalsïŒdestroyedã€ãã³ãã
$ modalsã³ã³ããŒãã³ãã«
åºåããŸãã
VueJsãå人çã«å©çšå¯èœãªå Žåã¯ãã€ã§ãããããã®ã€ãã³ããèãããšãã§ããŸãã
äžè¬ã«ãäžéšã¯å®å
šã«ãªãã·ã§ã³ã§ãã£ããããæåã¯ãããã®éä¿¡ã®ååãæšãŠãããšæã£ãŠããŸããããèããåŸããããæ®ããŸããã ããšãã°ãã¢ãŒãã«ãŠã£ã³ããŠã®çµ±èšæ
å ±ãåéããå Žåã«åœ¹ç«ã¡ãŸãã ãããŠåå¿è
ã¯ããã®äžèŠæ··ä¹±ãã
$ on ã
$ emit-åŒã³åºãã§ãããããèªåã§äœããç解ããã§ãããã
ããã«ããã$ On ...
ããã§ã¯ã
$ modalsã³ã³ããŒãã³ã
èªäœã«ããã€ãã³ããªã¹ããŒ
new ã
close ã
dismissã
æå¹ã«ã ãŸã ã ãããã®ã€ãã³ããçºçãããšã
$ modalsã³ã³ããŒãã³ãã®å¯Ÿå¿ããã¡ãœããã
åŒã³åºãããŸãã ãŠã£ã³ããŠãéãïŒ
open ïŒãéããïŒ
close ïŒããã£ã³ã»ã«ïŒ
dismiss ïŒããŸãã
ã芧ã®ããã«ããŠã£ã³ããŠãéããã«ã¯ã2ã€ã®æ¹æ³ããããŸã-åããªãã©ããéããïŒãã£ã³ã»ã«ãŸãã¯ãã«ãžã§ã¢-ãã£ã³ã»ã«-ïŒããã³
éãã ïŒéããïŒã éãã¯ã
closeãä»ããŠã¢ãŒãã«ãŠã£ã³ããŠã
éãããš ãæ°ããã¢ãŒãã«ãŠã£ã³ããŠã®ãªãã·ã§ã³ã«ããã¯ãã
onCloseã³ãŒã«ããã¯é¢æ°ã«ããŒã¿ã転éã§ããããšã§ãïŒåŸã§æ€èšããŸãïŒã
ãããŠãå®éã«ã¯
$ modalsã³ã³ããŒãã³ãã®
open ã
close ãããã³
dismissã¡ãœãã ã ãããã®äžã§ã
ModalWrapperã®
æ°ãã ã
close ã
dismissã€ãã³ãã§ãã
EventBusãå®è¡ããŸãã ããã§ããã¹ãŠã®éæ³ãèµ·ãããŸãã
ãããŠãindex.jsãã¡ã€ã«ã®
ã€ã³ã¹ããŒã«æ©èœã®æåŸã
Vue.mixin({ created() { this.$on('modals:new', options => { Bus.$emit('new', options); }); this.$on('modals:close', data => { Bus.$emit('close', data); }); this.$on('modals:dismiss', index => { Bus.$emit('dismiss', index); }); } });
ããã§ã¯ã
äœæãããã¡ãœããã
Vueããã¯ã¹ã€ã³ãä»ããŠãã¹ãŠã®
Vueã³ã³ããŒãã³ãã«æ¡åŒµããèµ·åæã«ã³ã³ããŒãã³ãã
ã¢ãŒãã«ïŒnew ã
modalsïŒcloseããã³
modalsïŒdismissã€ãã³ãã
ãªãã¹ã³ã§ããããã«ããEventBusãä»ããŠããããåŒã³åºããããšã察å¿ããã€ãã³ãã
ModalWrapperã§å床éå§
ããŸã
ã¢ãŒãã«ãŠã£ã³ããŠãå¶åŸ¡ããã«ã¯ãããããã¹ãŠã®å°çã®ãããªåŒã³åºããããã§å¿
èŠã§ãã ãŸãã
open ã
closeãããã³
dismissã€ãã³ããããªã¬ãŒããããã®4ã€ã®ãªãã·ã§ã³ãæäŸã
ãŸã ã
ã¢ããªã±ãŒã·ã§ã³ã§ã¢ãŒãã«ãŠã£ã³ããŠãåŒã³åºãæåã®æ¹æ³ïŒ
this.$modals.open(options)
2çªç®ã®æ¹æ³ïŒ
this.$modals.$emit('new', options)
第äžïŒ
this.$emit('modals:new', options)
4çªç®ïŒãã®ã¡ãœããã®å ŽåãBus.jsãã€ã³ããŒãããå¿
èŠããããŸãããããã«ããã
Vueã³ã³ããŒãã³ãããã§ã¯ãªããä»»æã®ã¹ã¯ãªãããããŠã£ã³ããŠãåŒã³åºãããšãã§ããŸãïŒïŒ
Bus.$emit('new', options)
ãŸãã
è¿ã ãã¢ãããžãŒã§
åŽäžããŸãã
ããã§ã圌ããèšãããã«-ãå³ãšè²ãžãã
次ã®æ£è
ã¯Modal.jsããç°¡åãªæ¹æ³ãæ¢ããŠããŸãã
ãã楜ããã³ãŒããè¿œå ãããŸãã æè¿ãã³ã³ããŒãã³ãïŒæšæºåœ¢åŒãš
jsx圢åŒã®äž¡æ¹ïŒã§
ã¬ã³ããªã³ã°é¢æ°ã䜿çšããŠã
ãŸã ã ããããã¬ã³ããªã³ã°ã®ããã®ããå€ãã®æ©äŒãæäŸããããšã«æ°ã¥ãããšããç§ã¯ã©ãã§ãããã䜿ãå§ããŸããã
ã¬ã³ããªã³ã°é¢æ°ã
䜿çšãããšã
Javascriptã®ãã¹ãŠã®æ©èœã«å ããŠã
vNodeãåããã¯ãŒã«ãªå
éš
VueJsãããã³ã
å
·äœçãªããŒãã¹ã
ãããããŸãã ããããç»å Žãããšããç§ã¯ããããã©ãããããã質åãã
ãã³ãã¬ãŒããã³ãã¬ãŒãã«ã³ã³ããŒãã³ããæç»ããäœã®ããã«æç»ãç¶ããŸããã ããããä»ãç§ã¯ç¬ãåè¬ãããŠããå Žæãç¥ã£ãŠããŸãã
ã¢ãŒãã«ã¯ãã¢ãŒãã«ãŠã£ã³ããŠèªäœãã¬ã³ããªã³ã°ããæ¬æ Œçãªã³ã³ããŒãã³ãã§ãã å
¥åãã©ã¡ãŒã¿ãŒã®æããããŸãã
圌ã¯ãã³ãŒãå
ã®ãã¹ãŠã®ãã©ã¡ãŒã¿ãŒã«ã€ããŠã³ã¡ã³ãããŠãããæ確ã«ããŸããã ãããŠããããã¯ãã¹ãæ©èœããªãããã«ãã³ãŒããã¹ãã€ã©ãŒã«ã¹ããŒããŸãã ããããã®ããã¹ãã
次ïŒ
import CloseIcon from './close-icon' export default { name: 'vu-modal', componentName: 'vu-modal', ... }
æåã«ã
SVG圢åŒã§ã¬ã³ããªã³ã°ããæ©èœã³ã³ããŒãã³ãã®åœ¢åŒã§ååã¢ã€ã³ã³ãã€ã³ããŒãããŸãã
close-icon.js export default { name: 'close-icon', functional: true, render(h) { return h('svg', { attrs: { width: '12px', height: '12px', viewBox: '0 0 12 12', xmlSpace: 'preserve' } }, [ h('line', { attrs: { x1: 1, y1: 11, x2: 11, y2: 1 }, style: { strokeLinecap: 'round', strokeLinejoin: 'round', } }), h('line', { attrs: { x1: 1, y1: 1, x2: 11, y2: 11 }, style: { strokeLinecap: 'round', strokeLinejoin: 'round', } }) ]) } }
ãªãç§ã¯åœŒå¥³ã«æ°ä»ããã®ããç§ãç¥ããªã
次ã«ã
nameãã©ã¡ãŒã¿ãŒ-ããã¯æšæºçãªåãã§ãã
ããããããã§ã¯
componentNameã¯
å¶ç¶ã§ã¯ãããŸããã ã¬ã³ããªã³ã°æã«
ModalWrapperã§ããã«å¿
èŠã«ãªããŸãã
ãããŠãèšç®ããããã©ã¡ãŒã¿ãŒ
propsData ïŒ
export default { ... computed: { propsData() { return (this.$parent.$vnode.data.props && this.$parent.$vnode.data.props.vModal) ? this.$parent.$vnode.data.props.vModal : this.$props } } }
ããã¯ãã£ãšè€éã§ãã
ããããåé¡ã¯ããã§ãã å
ã®
Vuedalã§ã¯ããã¹ãŠã®ãŠã£ã³ããŠã¯äžèšã®4ã€ã®ã¡ãœããã䜿çšããŠåŒã³åºãããŸãã ããããã«ããŠã£ã³ããŠã«è¡šç€ºããã³ã³ããŒãã³ããšãŠã£ã³ããŠãã©ã¡ãŒã¿ãŒãæž¡ãå¿
èŠããããŸãïŒãããã¯ãã¹ãŠãåä¿¡
ã¢ãŒãã«ãã©ã¡ãŒã¿ãŒã«å«ãŸããããã«ããã€ãã®æ°ãããã©ã¡ãŒã¿ãŒãè¿œå ãããŸãïŒã ãŸããã¢ããªã±ãŒã·ã§ã³ã®ç°ãªãéšåã§åããŠã£ã³ããŠãå®è¡ãããå Žåã¯ãæ¯åãŠã£ã³ããŠãã©ã¡ãŒã¿ãŒïŒãã£ã¡ã³ã·ã§ã³ããã®ä»ã®èšå®ïŒãæž¡ããŸãã ãããéè€ã§ãã ã¯ããé·ãéæ··ä¹±ããªãã§ãã ããã ãããŠãç§ãã¡ããã°ã©ããŒã¯ãçãç©ã¯ã³ã¢ã§éåžžã«æ ãè
ã§ãã ãããã£ãŠããã®
ã¢ãŒãã«ã³ã³ããŒãã³ããäœæãããŸããã
ããã§ã次ã®ãããªã¢ãŒãã«ãŠã£ã³ããŠã³ã³ããŒãã³ããäœæã§ããŸãã
simple-modal.js <template lang="html"> <vu-modal title="Test modal" :isScroll="true" size="p50" :center="true" :escapable="true"> <div slot="header" style="display: flex; justify-content: left; align-items: center;" v-if="header"> <div style="padding-left: 10px">Simple modal</div> </div> <div> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quia consequuntur minus sint quidem ut tenetur dicta sunt voluptates numquam. Eum totam ex maxime aut recusandae quae laborum fugit ab autem.</p> </div> <div slot="footer"> <button class="uk-button uk-button-smaller uk-button-primary" @click="close">Cancel</button> </div> </vu-modal> </template> <script> export default { name: 'simple-modal', props: { lorem: { type: Boolean, default: true } } }; </script>
ã€ãŸããæšæºã³ã³ããŒãã³ãã§ãã
Modal ïŒ
vu-modal ïŒã«ãã£ãŠã©ãããããŸãã ãã®
vu-modalã«ãå¿
èŠãªãã©ã¡ãŒã¿ãŒãæž¡ããŸãã ãããã¯ããã®ãŠã£ã³ããŠã®ããã©ã«ãå€ã«ãªããŸãã
ãããŠããã®ãŠã£ã³ããŠã次ã®ããã«åŒã³åºããŸãã
import SimpleModal from './modals/simple' ... this.$modals.open({ component: SimpleModal })
å¿
èŠãªãŠã£ã³ããŠèšå®ã®ããã©ã«ãå€ã¯ãã¹ãŠã
vu-modalã©ãããŒãã
ååŸããåã
SimpleModalã³ã³ããŒãã³ãããèªåçã«ååŸãããŸãã å¿
èŠãªèšå®ã§ãŠã£ã³ããŠã³ã³ããŒãã³ããäœæããåŸãèšå®ãæ°ã«ããã«ã©ãã§ã䜿çšã§ããŸãã ããã«ããããã®ããã©ã«ãå€ãåå²ãåœãŠããå¿
èŠãããå Žåããã®ãŠã£ã³ããŠãåŒã³åºããããšãã«å¿
èŠãªå€ãæå®ããŸãã
import SimpleModal from './modals/simple' ... this.$modals.open({ component: SimpleModal, center: false })
ããã§ã
äžå€®ã®ãã©ã¡ãŒã¿ãŒããŠã£ã³ããŠãã³ãã¬ãŒãã§æå®ãããããã©ã«ãã®ãã©ã¡ãŒã¿ãŒ
SimpleModalã«çœ®ãæãããŸãã
ã€ãŸãããã©ã¡ãŒã¿ãŒãããŒãžïŒããŒãžïŒãããšãã®åªå
é äœã¯æ¬¡ã®ãšããã§ãã
- props ïŒmodal.jsã®ããã©ã«ãå€ïŒ
- props ïŒvu-modalã§ã©ãããããã³ã³ããŒãã³ããã³ãã¬ãŒãå
ïŒ
- ãªãã·ã§ã³ ïŒãŠã£ã³ããŠãåŒã³åºããšãïŒ
äœãã»ã©éèŠã§ãã
ãã®ããã
vu-modalã³ã³ããŒãã³ãã®èšç®ããã
propsDataããããã£ã¯ã
vu-modalã®ãã®ã€ã³ã¹ã¿ã³ã¹ãäœããã®ã³ã³ããŒãã³ãã®ã©ãããŒïŒ
SimpleModal ïŒã§ãããã©ãããèæ
®ããŠãæ£ããå
¥åãã©ã¡ãŒã¿ãŒïŒ
props ïŒãè¿ããŸãã
ãããè¡ãã«ã¯ã
ModalWrapperã§ãŠã£ã³ããŠãã¬ã³ããªã³ã°ãããšãã«ããã®ãŠã£ã³ããŠã®ã³ã³ããŒãã³ãã
vu-modalã§ã©ãããããŠããå Žåãæããª
æ¯æ±ã
vModalãšããååã§è»¢éããŸããããã§ãªãå Žåã¯ãéåžžã®
æ¯æ±ã転éããŸãã
ãã ããã³ã³ããŒãã³ãã
vu-modalã«ã©ãããããŠããå Žåã
å°éå
·ãã¬ã³ããªã³ã°ãããšãã«ãã®èŠªã³ã³ããŒãã³ãïŒ
SimpleModal ïŒã«åé¡ãããããã芪ã³ã³ããŒãã³ãã«
vModalãšããååã®å
¥åãã©ã¡ãŒã¿ãŒããããã©ããã確èªããŸãã ååšããå Žåããããã®å€ãååŸããŸãããã以å€ã®å Žåã¯ãæšæº
propsãååŸããŸãã
ãããŠã
ããããã§ãã¯ããŸãã
$ Parentã$ Options.propsData ãããã
ããã$ Parentã$ Vnode.data.propsã¯ã芪ã³ã³ããŒãã³ãã®
propsã« vModalãã©ã¡ãŒã¿ãŒããªãå Žåããããã¬ã³ããªã³ã°ãããšãã«
ãããèŠãããšãã§ãã
vModalã¯
$ parentã$ vnode.data.propsã§ãã ããã«ã¯ãäŸå€ãªãæž¡ãããã¹ãŠã®ãã©ã¡ãŒã¿ãŒãå«ãŸããŸãã ãããŠããããã¯ãã£ã«ã¿ãªã³ã°ãããäœåãªéšåã¯ç Žæ£ãããŸãã
ãã®ã³ãŒããããäžåºŠæããŠãã ãããããã¯ãæèãšæ··åããªãããã«å°ãããã®ã§ãã
export default { ... computed: { propsData() { return (this.$parent.$vnode.data.props && this.$parent.$vnode.data.props.vModal) ? this.$parent.$vnode.data.props.vModal : this.$props } } }
ä»ããããããã¹ãŠãæ確ã§ã¯ãªãã å€ãã®æ
å ±ããããŸãããå€ãã®ã¬ãã¹ã³ã®ããã«ãã¹ãŠãæšæºã§ã¯ãããŸããã ç§ã¯ãã®çš®ã®èšäºãåããŠæžããŠããŸãããã©ã®ããã«æããã®ãæåãã¯ãŸã æ確ã§ã¯ãããŸããã ããããå€ãã®äººã
Vueã®å
éšãæãäžããŠããŸãããããã«ã€ããŠæžã人ã¯ã»ãšãã©ããŸããã é·ãéãç§ã¯ãã®ãããªç¬éã«é¢ããæ
å ±ãæ¢ããŸããã ç§ã¯äœããèŠã€ããæ®ããèªåã§éžã³ãŸããã ãããŠãç§ã¯ãã®ãããªããšã«ã€ããŠè©±ãããã§ãã
ãããã
ModalWrapperã
å解ãããšãããæ確ã«ãªããŸãã ããã§ãã¹ãã³ã
ãããããäœæããŠçªã«éããŸãã
ããŠã
ã¢ãŒãã«ã³ã³ããŒãã³ãã®
ã¬ã³ããªã³ã°æ©èœïŒ
vu-modal ïŒãæ®ã£ãŠã
ãŸã ã
ã¬ã³ããªã³ã°ïŒhïŒ " render(h) { const { dismissable, title, isScroll, fullscreen, isTop, isBottom, isLeft, isRight, center, size, className, bodyPadding } = this.propsData const closeBtn = dismissable ? h('div', { class: 'vu-modal__close-btn', on: { click: () => {this.$modals.dismiss()} } }, [h(CloseIcon)]) : null const headerContent = this.$slots.header ? this.$slots.header : title ? h('span', {class: ['vu-modal__cmp-header-title']}, title) : null const header = headerContent ? h('div', { class: ['vu-modal__cmp-header'] }, [ headerContent ]) : null const body = h('div', { class: ['vu-modal__cmp-body'], style: { overflowY: isScroll ? 'auto' : null, padding: bodyPadding ? '1em' : 0 } }, [ this.$slots.default ]) const footer = this.$slots.footer ? h('div', { class: ['vu-modal__cmp-footer'] }, [ this.$slots.footer ]) : null let style = {} let translateX = '-50%' let translateY = '0' if(center) { translateX = '-50%' translateY = '-50%' } if(isRight || isLeft) { translateX = '0%' } if((isTop || isBottom) && !isScroll && !center) { translateY = '0%' } style.transform = `translate(${translateX}, ${translateY})` return h('div', { style, class: ['vu-modal__cmp', { 'vu-modal__cmp--is-fullscreen': fullscreen, 'vu-modal__cmp--is-center': center, 'vu-modal__cmp--is-top': isTop && !isScroll && !center, 'vu-modal__cmp--is-bottom': isBottom && !isScroll && !center, 'vu-modal__cmp--is-left': isLeft, 'vu-modal__cmp--is-right': isRight }, isScroll && fullscreen && 'vu-modal__cmp--is-scroll-fullscreen', isScroll && !fullscreen && 'vu-modal__cmp--is-scroll', !fullscreen && `vu-modal__cmp--${size}`, className ], on: {click: (event) => {event.stopPropagation()}} }, [ closeBtn, header, body, footer ]) }
ããã§ã¯çããããšã¯äœããªãããã§ãã
æåã«ãåè¿°ã®
propsDataã®èšç®å€ãããã¹ãŠã®ãã©ã¡ãŒã¿ãŒãåŒãåºããŸãã
dismissableããããã£ã
trueã®å Žåã
dismissã€ãã³ãïŒãŠã£ã³ããŠã®ãã£ã³ã»ã«ïŒãããªã¬ãŒããååãã¿ã³ã衚瀺ã
ãŸã ã
ããããŒã圢æã
ãŸã-headerãšããååã®ã¹ãããïŒ
thisã$ Slots.header ïŒã
vu-modalã«æž¡ãããå Žåã
titleããããã£ãæž¡ãããå Žåããã®ã¹ããããæç»ããŸã-ãããå°å·ããŸããããã§ãªããã°ã
ããããŒããŸã£ãã衚瀺ããŸããã
ããã©ã«ãã®ã¹ãããïŒ
thisã$ Slots.default ïŒã®å
容ã§
æ¬äœãããã¯ã圢æããŸãã
ãããŠã
ããã¿ãŒ -
ããã¿ãŒã¹ãããïŒ
thisã$ Slots.footer ïŒã
æž¡ãããå Žå ã
次ã«ã
transformã®
css-ããããã£ã®æ£ããå€ã決å®ããŸã
ïŒãŠã£ã³ããŠã®
translateïŒxãyïŒ ã ã€ãŸãããŠã£ã³ããŠã«è»¢éãããããããã£ã«å¿ããŠããã©ã¡ãŒã¿ãŒ
Xããã³
Yã䜿çšãããŸãã ãããŠãã¬ã³ããªã³ã°æã®é©åãªé
眮ã®ããã«ããã®
å€æãã¡ã€ã³ãŠã£ã³ããŠ
divã«ã¬ã³ããªã³ã°ããŸãã
ããŠãå
šäœãã¬ã³ããªã³ã°ããåæã«å¿
èŠãªã¯ã©ã¹ãèšç®ããŸãã
ããã«ãã¡ã€ã³ã®
div.vu-modal__cmp onClick-ãã³ãã©ãŒã§
event.stopPropagationïŒïŒã䜿çšããŠ
é»è©±ã
åããšãåãŠã£ã³ããŠãã©ããããã¯ãªãã¯ã«å¿çãã
div ïŒãã¹ã¯ïŒã®ã¯ãªãã¯ãã¢ã¯ãã£ãã«ãªããªãããã«ããŠã£ã³ããŠã®ã¯ãªãã¯ãäžã«ãããã¢ããããŸãããããŠ
dismissãåŒã³åºããŸãã ããã§ãªãå Žåããã¹ã¯ã®ãã®
dismissã€ãã³ãã
çºçãããŠã£ã³ããŠãéããŸãã
ãããŒïŒ
æåŸã®ã³ã³ããŒãã³ãã¯ModalWrapperã§ã
modal-wrapper.jsãéå§ããŸã
ã€ã³ããŒã './style.scss'
'./utils/bus'ããã®ãã¹ã®ã€ã³ããŒã
ModalCmpãã./modalãããã€ã³ããŒãããŸã
ãšã¯ã¹ããŒãã®ããã©ã«ã{
ååïŒãvu-modal-wrapperãã
ããŒã¿ïŒïŒ{
return {
ã¢ãŒãã«ïŒ[]
}
}ã
ããŠã³ãæžã¿ïŒïŒ{
ifïŒtypeof documentïŒ== 'undefined'ïŒ{
document.body.addEventListenerïŒ 'keyup'ãthis.handleEscapeKeyïŒ
}
}ã
ç Žå£ãããïŒïŒ{
ifïŒtypeof documentïŒ== 'undefined'ïŒ{
document.body.removeEventListenerïŒ 'keyup'ãthis.handleEscapeKeyïŒ
}
}ã
ã¹ã¿ã€ã«ãæ¥ç¶ããŸãã
style.scss body.modals-open { overflow: hidden; } .vu-modal { &__wrapper { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 5000; overflow-x: hidden; overflow-y: auto; transition: opacity .4s ease; } &__mask { background-color: rgba(0, 0, 0, .5); position: absolute; width: 100%; height: 100%; overflow-y: scroll; &--disabled { background-color: rgba(0, 0, 0, 0); } } &__cmp { display: flex; flex-direction: column; border-radius: 0px; background: #FFF; box-shadow: 3px 5px 20px #333; margin: 30px auto; position: absolute; left: 50%; transform: translateX(-50%); width: 650px; &--is-center { margin: auto; top: 50%; } &--is-scroll { max-height: 90%; } &--is-scroll-fullscreen { max-height: 100%; } &--is-fullscreen { width: 100%; min-height: 100%; margin: 0 0; } &--is-bottom { bottom: 0; } &--is-top { top: 0; } &--is-right { right: 0; margin-right: 30px; } &--is-left { left: 0; margin-left: 30px; } &--xl { width: 1024px; } &--lg { width: 850px; } &--md { width: 650px; } &--sm { width: 550px; } &--xs { width: 350px; } &--p50 { width: 50%; } &--p70 { width: 70%; } &--p90 { width: 90%; } &-body { padding: 1em; &::-webkit-scrollbar { width: 6px; } &::-webkit-scrollbar-track { -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); } &::-webkit-scrollbar-thumb { background-color: darkgrey; outline: 1px solid slategrey; } } &-header { user-select: none; border-bottom: 1px solid #EEE; padding: 1em; text-align: left; &-title { font-size: 16px; font-weight: 800; } } &-footer { border-top: solid 1px #EEE; user-select: none; padding: 1em; text-align: right; } } &__close-btn { user-select: none; position: absolute; right: 12px; top: 5px; line { stroke: grey; stroke-width: 2; } &:hover { cursor: pointer; line { stroke: black; } } } }
ã¢ãŒãã«é
åã«ã¯ãçŸåšã¢ã¯ãã£ããªãŠã£ã³ããŠãä¿åããŸãã
ããŠã
ModalWrapperã³ã³ããŒãã³ããããŠã³ãããã³åé€ãããšãã
ããŒã¢ãããã³ãã©ãŒã
ãŠã£ã³ã㊠ïŒ
ãŠã£ã³ããŠãããå ŽåïŒã§
åæã ã
handleEscapeKeyã¡ãœãããèµ·åã
ãŸã ã
handleEscapeKey handleEscapeKey(e) { if (e.keyCode === 27 && this.modals.length) { if (!this.modals.length) return; if (this.current.options.escapable) this.dismiss(); } }
次ã«ã
EscããŒãæŒããããŠã£ã³ããŠããããçŸåšã®ïŒæåŸã«èµ·åãããïŒãŠã£ã³ããŠãããå Žåã
escapableããããã£ã¯
trueã«ãªã ããã®çŸåšã®ãŠã£ã³ããŠãéãã
dismissã¡ãœãããå®è¡ããŸãã
ããŠãããããæãèå³æ·±ãããšãå§ãŸã£ãŠããŸãã ã³ãŒãã§äœãèµ·ããŠããã®ãã説æããããšæããŸãããå€åããã¯æ¹åãããã§ãããã
ModalWrapperãäœæãããšãã
EventBusããã®ã€ãã³ããªã¹ãã³ã°
ãæå¹ã«ã
ãŸã ã åè¿°ã®
$ modalsã¡ãœããã§å®è¡ããããã®ïŒ
äœææžã¿ïŒïŒ created() { Bus.$on('new', options => {
ãã®ä»ã®ã€ãã³ãïŒéããŠåŽäž Bus.$on('close', data => {
次ã«ã¡ãœããïŒã¹ãã©ã€ã¹ methods: { splice(index = null) {
dismissã¡ãœããã§ã¯ããã¹ãŠãcloseã¡ãœããã«äŒŒãŠããŸãïŒåŽäžãã dismiss(index = null) { let localIndex = index; if (index && typeof index === 'function') localIndex = index(this.$last); if (typeof localIndex !== 'number') localIndex = this.$last; if (this.modals[localIndex].options.onDismiss() === false) return; Bus.$emit('dismissed', { index: localIndex, instance: this.modals[localIndex] }); this.doClose(localIndex); },
èšç®ãããããããã£ïŒããŠãæåŸã®æ©èœãä»ç§ã®ãæ°ã«å
¥ãïŒã¬ã³ããªã³ã°ïŒhïŒ ããã«ç©èªããããŸããé·ã話ã次åã¯å¿
èŠã«å¿ããŠçãããŠã¿ãŸããæåŸã«ãæ
å ±ãããããç解ããããã«ãŠã£ã³ããŠãéãããã®ã³ãŒãã®äŸã瀺ããŸãã this.$modals.open({ title: 'Modal with callbacks', component: Example, props: { lorem: true, test: true }, onDismiss() { console.log('Dismiss ok!') } onClose(data) { if(data.ended) return false console.log('Ok!') } })
ãããŠãããšãã°ããŠã£ã³ããŠã®ãã¿ã³ã䜿çšããŠãããã«ããŒã¿ã転éããcloseãéå§ããŸãã this.$modals.close({ ended: true })
ãã®å Žåãéããåã«ãonClose ã³ãŒã«ããã¯ãèµ·åãããŸããåæ§ã«ãonDismissã¯æ©èœããŸãããã®ã³ãŒã«ããã¯ã¯ãããšãã°ããã¿ãŒã®[ãã£ã³ã»ã«]ãã¿ã³ãã¯ãªãã¯ãããšãã«ãã¯ãã¹ãã¿ã³ããŠã£ã³ããŠãã¹ã¯ãã¯ãªãã¯ãããããŠã£ã³ããŠå
ã§çŽæ¥ã¯ãªãã¯ããããšã§ããªã¬ãŒãããŸãã this.$modals.dismiss()
ãã®ä»ãã¬ã³ããªã³ã°æ©èœã«ã€ããŠããããã¯ç¢ºãã«templateã®ã³ãŒãã»ã©èŠæ ãããããããŸããããããããããã§ã¯ããã³ãã¬ãŒãã§ã¯äžå¯èœãªããšãè¡ãããšãã§ããŸããããããããæŸèæãšã¬ã³ããªã³ã°é¢æ°ã§åŸããããããã¯ããã«å€ãã®ã³ãŒãã§ã§ããŸãããããŠãã§å¡ææåã§ããã°ãã¬ã³ããªã³ã°ã¯éåžžã«æ
éã«å€æŽãããé¢æ°props-ãšDATA-äŸåã¬ã³ããªã³ã°ããããããããããã£ããã¢ããããŒãã®ç¡éã®ãµã€ã¯ã«ïŒãžè¡ãã«ã¯ããã§ãªããªã¹ã¯æŽæ°ïŒã³ã³ããŒãã³ããããããä»ã®ãšãããã¹ãŠããããŠã圌ã¯æãæã¡åããŸãããããããç§ã¯å
šäœã®åãã説æãããã£ãã次ã®èšäºã¯çããªããŸããããããç§ã話ããããã¥ã¢ã³ã¹ããããŸãããããŠããã®ã©ã€ã³ã«äœãã§ããã¿ããªã«æè¬ããŸãïŒPS ããã«ãŠã£ã³ããŠã®äŸããããŸãããœãŒã¹ã³ãŒããå«ãGithubãžã®ãªã³ã¯ããããŸãããã·ã¢èªã§ãããã¥ã¡ã³ããè¿œå ããŸãã