Skip to content

Commit

Permalink
chore(lib): partially update Dialog
Browse files Browse the repository at this point in the history
- `Dialog` and `Modal` components are updated during the update of
  the documentation page of `Autocomplete`. Only changes sufficient
  to render the documentation page of `Autocomplete` are made. The
  documentation page of `Autocomplete` does not work yet.
- In `src/components/dialog/index.js`,
    - On Vue 3, `Vue.extend` no longer exists, and newing a component
      looks not supported. A dynamic Vue instance that renders
      `Dialog` is created and mounted instead. When the dialog is
      confirmed or cancelled, the Vue instance is unmounted. `Dialog`
      used to destroy itself, but `$destroy` no longer exists on Vue
      3. Thus, a Vue instance has to be unmounted from outside.
    - A component instance returned from `open` is not actually
      `Dialog` but exposes the `close` function so that a dialog may
      manually be closed. To access the `Dialog` instance,
      undocumented VNode structure is used.
    - A component instance no longer exposes `$on`, so event listeners
      `onConfirm` and `onCancel` have to be specified to the
      `createElement` function.
- In `src/components/dialog/Dialog.vue`,
    - On Vue 3, a `confirm` event can be received by binding
      `onConfirm`. This conflicts with the former definition of the
      `onConfirm` prop. To avoid this conflict, `onConfirm` is
      renamed to `confirmCallback`.
    - `inputAttrs` is no longer modified inside `Dialog`. It is copied
      as `safeInputAttrs` and `safeInputAttrs` is modified instead.
      `value` is deleted from `safeInputAttrs` because binding `value`
      by `v-bind` inhibits `v-model` binding of the input element.
- In `src/components/modal/Modal.vue`,
    - On Vue 3, a `cancel` event can be received by binding
      `onCancel`. This conflicts with the former definition of the
      `onCancel` prop. To avoid this conflict, `onCancel` is renamed
      to `cancelCallback`.
    - `beforeDestroy` --> `beforeUnmount`.
- Common,
    - `$destroy` is removed because it no longer exists on Vue 3.
    - Automatic ESLint fix is applied.
  • Loading branch information
kikuomax committed Jul 7, 2023
1 parent 07be36f commit a415f3a
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 40 deletions.
59 changes: 40 additions & 19 deletions src/components/dialog/Dialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,32 @@
:class="dialogClass"
v-trap-focus="trapFocus"
:role="ariaRole"
:aria-modal="ariaModal">
<div class="modal-background" @click="cancel('outside')"/>
:aria-modal="ariaModal"
>
<div class="modal-background" @click="cancel('outside')" />
<div class="modal-card animation-content">
<header class="modal-card-head" v-if="title">
<p class="modal-card-title">{{ title }}</p>
<p class="modal-card-title">
{{ title }}
</p>
</header>

<section
class="modal-card-body"
:class="{ 'is-titleless': !title, 'is-flex': hasIcon }">
:class="{ 'is-titleless': !title, 'is-flex': hasIcon }"
>
<div class="media">
<div
class="media-left"
v-if="hasIcon && (icon || iconByType)">
v-if="hasIcon && (icon || iconByType)"
>
<b-icon
:icon="icon ? icon : iconByType"
:pack="iconPack"
:type="type"
:both="!icon"
size="is-large"/>
size="is-large"
/>
</div>
<div class="media-content">
<p>
Expand All @@ -44,12 +50,15 @@
class="input"
ref="input"
:class="{ 'is-danger': validationMessage }"
v-bind="inputAttrs"
v-bind="safeInputAttrs"
@compositionstart="isCompositing = true"
@compositionend="isCompositing = false"
@keydown.enter="confirm">
@keydown.enter="confirm"
>
</div>
<p class="help is-danger">{{ validationMessage }}</p>
<p class="help is-danger">
{{ validationMessage }}
</p>
</div>
</div>
</div>
Expand All @@ -59,11 +68,17 @@
<b-button
v-if="showCancel"
ref="cancelButton"
@click="cancel('button')">{{ cancelText }}</b-button>
@click="cancel('button')"
>
{{ cancelText }}
</b-button>
<b-button
:type="type"
ref="confirmButton"
@click="confirm">{{ confirmText }}</b-button>
@click="confirm"
>
{{ confirmText }}
</b-button>
</footer>
</div>
</div>
Expand Down Expand Up @@ -120,7 +135,7 @@ export default {
type: Object,
default: () => ({})
},
onConfirm: {
confirmCallback: {
type: Function,
default: () => {}
},
Expand Down Expand Up @@ -168,6 +183,17 @@ export default {
}
},
computed: {
// `safeInputAttrs` is a shallow copy of `inputAttrs` except for `value`
// `value` should not be specified to `v-bind` of the input element
// because it inhibits `v-model` of the input on Vue 3
safeInputAttrs() {
const attrs = { ...this.inputAttrs }
delete attrs.value
if (typeof attrs.required === 'undefined') {
attrs.required = true
}
return attrs
},
dialogClass() {
return [this.size, {
'has-custom-container': this.container !== null
Expand Down Expand Up @@ -197,7 +223,7 @@ export default {
methods: {
/**
* If it's a prompt Dialog, validate the input.
* Call the onConfirm prop (function) and close the Dialog.
* Call the confirmCallback prop (function) and close the Dialog.
*/
confirm() {
if (this.$refs.input !== undefined) {
Expand All @@ -209,7 +235,7 @@ export default {
}
}
this.$emit('confirm', this.prompt)
this.onConfirm(this.prompt, this)
this.confirmCallback(this.prompt, this)
if (this.closeOnConfirm) this.close()
},
Expand All @@ -220,7 +246,6 @@ export default {
this.isActive = false
// Timeout for the animation complete before destroying
setTimeout(() => {
this.$destroy()
removeElement(this.$el)
}, 150)
}
Expand All @@ -237,10 +262,6 @@ export default {
mounted() {
this.isActive = true
if (typeof this.inputAttrs.required === 'undefined') {
this.$set(this.inputAttrs, 'required', true)
}
this.$nextTick(() => {
// Handle which element receives focus
if (this.hasInput) {
Expand Down
64 changes: 48 additions & 16 deletions src/components/dialog/index.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,66 @@
import { createApp, h as createElement } from 'vue'

import Dialog from './Dialog.vue'

import config, { VueInstance } from '../../utils/config'
import config from '../../utils/config'
import { merge } from '../../utils/helpers'
import { use, registerComponent, registerComponentProgrammatic } from '../../utils/plugins'

let localVueInstance

function open(propsData) {
let slot
if (Array.isArray(propsData.message)) {
slot = propsData.message
delete propsData.message
}
const vm = typeof window !== 'undefined' && window.Vue ? window.Vue : localVueInstance || VueInstance
const DialogComponent = vm.extend(Dialog)
const component = new DialogComponent({
el: document.createElement('div'),
propsData
})
if (slot) {
component.$slots.default = slot
component.$forceUpdate()
function createDialog(onConfirm, onCancel) {
const container = document.createElement('div')
const vueInstance = createApp({
data() {
return {
dialogVNode: null
}
},
methods: {
close() {
// TODO: too much dependence on Vue's internal structure?
const dialog =
this.dialogVNode?.component?.expose ||
this.dialogVNode?.component?.proxy
dialog?.close()
}
},
render() {
this.dialogVNode = createElement(
Dialog,
{
...propsData,
onConfirm: (...args) => {
if (onConfirm != null) {
onConfirm(...args)
}
vueInstance.unmount()
},
onCancel: (...args) => {
if (onCancel != null) {
onCancel(...args)
}
vueInstance.unmount()
}
},
slot ? { default: () => slot } : undefined
)
return this.dialogVNode
}
})
return vueInstance.mount(container)
}
if (!config.defaultProgrammaticPromise) {
return component
return createDialog()
} else {
return new Promise((resolve) => {
component.$on('confirm', (event) => resolve({ result: event || true, dialog: component }))
component.$on('cancel', () => resolve({ result: false, dialog: component }))
const dialog = createDialog(
(event) => resolve({ result: event || true, dialog }),
() => resolve({ result: false, dialog }))
})
}
}
Expand Down Expand Up @@ -61,7 +94,6 @@ const DialogProgrammatic = {

const Plugin = {
install(Vue) {
localVueInstance = Vue
registerComponent(Vue, Dialog)
registerComponentProgrammatic(Vue, 'dialog', DialogProgrammatic)
}
Expand Down
9 changes: 4 additions & 5 deletions src/components/modal/Modal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default {
return config.defaultModalCanCancel
}
},
onCancel: {
cancelCallback: {
type: Function,
default: () => {}
},
Expand Down Expand Up @@ -220,17 +220,17 @@ export default {
},
/**
* Close the Modal if canCancel and call the onCancel prop (function).
* Close the Modal if canCancel and call the cancelCallback prop (function).
*/
cancel(method) {
if (this.cancelOptions.indexOf(method) < 0) return
this.$emit('cancel', arguments)
this.onCancel.apply(null, arguments)
this.cancelCallback.apply(null, arguments)
this.close()
},
/**
* Call the onCancel prop (function).
* Call the cancelCallback prop (function).
* Emit events, and destroy modal if it's programmatic.
*/
close() {
Expand All @@ -241,7 +241,6 @@ export default {
if (this.programmatic) {
this.isActive = false
setTimeout(() => {
this.$destroy()
removeElement(this.$el)
}, 150)
}
Expand Down

0 comments on commit a415f3a

Please sign in to comment.