fix(message-box): fix incompatible migration (#1671)

* fix(message-box): fix incompatible migration

* docs(message-box): add docs of buttonSize
This commit is contained in:
Ryan2128 2021-03-27 19:11:38 +08:00 committed by GitHub
parent 99451a1ef6
commit 25868c2446
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 137 additions and 136 deletions

View File

@ -2,7 +2,7 @@
<transition name="fade-in-linear" @after-leave="$emit('vanish')">
<el-overlay
v-show="visible"
:z-index="state.zIndex"
:z-index="zIndex"
:overlay-class="['is-message-box', modalClass]"
:mask="modal"
@click.self="handleWrapperClick"
@ -60,46 +60,46 @@
<div v-show="showInput" class="el-message-box__input">
<el-input
ref="inputRef"
v-model="state.inputValue"
v-model="inputValue"
:type="inputType"
:placeholder="inputPlaceholder"
:class="{ invalid: state.validateError }"
:class="{ invalid: validateError }"
@keydown.prevent.enter="handleInputEnter"
/>
<div
class="el-message-box__errormsg"
:style="{
visibility: !!state.editorErrorMessage ? 'visible' : 'hidden',
visibility: !!editorErrorMessage ? 'visible' : 'hidden',
}"
>
{{ state.editorErrorMessage }}
{{ editorErrorMessage }}
</div>
</div>
</div>
<div class="el-message-box__btns">
<el-button
v-if="showCancelButton"
:loading="state.cancelButtonLoading"
:loading="cancelButtonLoading"
:class="[cancelButtonClass]"
:round="roundButton"
size="small"
:size="buttonSize || 'small'"
@click="handleAction('cancel')"
@keydown.enter="handleAction('cancel')"
>
{{ state.cancelButtonText || t('el.messagebox.cancel') }}
{{ cancelButtonText || t('el.messagebox.cancel') }}
</el-button>
<el-button
v-show="showConfirmButton"
ref="confirmRef"
:loading="state.confirmButtonLoading"
:loading="confirmButtonLoading"
:class="[confirmButtonClasses]"
:round="roundButton"
:disabled="state.confirmButtonDisabled"
size="small"
:disabled="confirmButtonDisabled"
:size="buttonSize || 'small'"
@click="handleAction('confirm')"
@keydown.enter="handleAction('confirm')"
>
{{ state.confirmButtonText || t('el.messagebox.confirm') }}
{{ confirmButtonText || t('el.messagebox.confirm') }}
</el-button>
</div>
</div>
@ -116,7 +116,8 @@ import {
watch,
reactive,
ref,
isVNode,
toRefs,
PropType,
} from 'vue'
import ElButton from '@element-plus/button'
import ElInput from '@element-plus/input'
@ -126,11 +127,11 @@ import { useModal, useLockScreen, useRestoreActive, usePreventGlobal } from '@el
import { TrapFocus } from '@element-plus/directives'
import PopupManager from '@element-plus/utils/popup-manager'
import { on, off } from '@element-plus/utils/dom'
import { isString } from '@element-plus/utils/util'
import { EVENT_CODE } from '@element-plus/utils/aria'
import { isValidComponentSize } from '@element-plus/utils/validators'
import type { ComponentPublicInstance, PropType, VNode } from 'vue'
import type { Action, MessageBoxState } from './message-box.type'
import type { ComponentPublicInstance } from 'vue'
import type { Action, MessageBoxState, MessageBoxType } from './message-box.type'
const TypeMap: Indexable<string> = {
success: 'success',
@ -141,25 +142,32 @@ const TypeMap: Indexable<string> = {
export default defineComponent({
name: 'ElMessageBox',
directives: {
TrapFocus,
},
components: {
ElButton,
ElInput,
ElOverlay,
},
directives: {
TrapFocus,
},
inheritAttrs: false,
props: {
beforeClose: {
type: Function as PropType<(action: Action, state: MessageBoxState, doClose: () => void) => any>,
default: undefined,
buttonSize: {
type: String as PropType<ComponentSize>,
validator: isValidComponentSize,
},
callback: Function,
cancelButtonText: {
type: String,
modal: {
type: Boolean,
default: true,
},
lockScroll: {
type: Boolean,
default: true,
},
showClose: {
type: Boolean,
default: true,
},
cancelButtonClass: String,
center: Boolean,
closeOnClickModal: {
type: Boolean,
default: true,
@ -172,85 +180,54 @@ export default defineComponent({
type: Boolean,
default: true,
},
confirmButtonText: {
type: String,
center: Boolean,
roundButton: {
default: false,
type: Boolean,
},
confirmButtonClass: String,
container: {
type: String, // default append to body
default: 'body',
},
customClass: String,
dangerouslyUseHTMLString: Boolean,
distinguishCancelAndClose: Boolean,
iconClass: String,
inputPattern: {
type: Object as PropType<RegExp>,
default: () => undefined,
validator: (val: unknown) => (val instanceof RegExp || val === 'undefined'),
boxType: {
type: String as PropType<MessageBoxType>,
default: '',
},
inputPlaceholder: {
type: String,
},
inputType: {
type: String,
default: 'text',
},
inputValue: {
type: String,
},
inputValidator: {
type: Function as PropType<(...args: any[]) => boolean | string>,
default: null,
},
inputErrorMessage: String,
lockScroll: {
type: Boolean,
default: true,
},
message: {
type: [String, Object] as PropType<string | VNode>,
validator: (val: unknown) => {
return isString(val) || isVNode(val)
},
},
modalFade: { // implement this feature
type: Boolean,
default: true,
},
modalClass: String,
modal: {
type: Boolean,
default: true,
},
roundButton: Boolean,
showCancelButton: Boolean,
showConfirmButton: {
type: Boolean,
default: true,
},
showClose: {
type: Boolean,
default: true,
},
type: String,
title: String,
showInput: Boolean,
zIndex: Number,
},
emits: ['vanish', 'action'],
setup(props, { emit }) {
// const popup = usePopup(props, doClose)
const visible = ref(false)
// s represents state
const state = reactive({
const state = reactive<MessageBoxState>({
beforeClose: null,
callback: null,
cancelButtonText: '',
cancelButtonClass: '',
confirmButtonText: '',
confirmButtonClass: '',
customClass: '',
dangerouslyUseHTMLString: false,
distinguishCancelAndClose: false,
iconClass: '',
inputPattern: null,
inputPlaceholder: '',
inputType: 'text',
inputValue: null,
inputValidator: null,
inputErrorMessage: '',
message: null,
modalFade: true,
modalClass: '',
showCancelButton: false,
showConfirmButton: true,
type: '',
title: undefined,
showInput: false,
action: '' as Action,
inputValue: props.inputValue,
confirmButtonLoading: false,
cancelButtonLoading: false,
cancelButtonText: props.cancelButtonText,
confirmButtonDisabled: false,
confirmButtonText: props.confirmButtonText,
editorErrorMessage: '',
// refer to: https://github.com/ElemeFE/element/commit/2999279ae34ef10c373ca795c87b020ed6753eed
// seemed ok for now without this state.
@ -258,28 +235,28 @@ export default defineComponent({
validateError: false,
zIndex: PopupManager.nextZIndex(),
})
const icon = computed(() => props.iconClass || (props.type && TypeMap[props.type] ? `el-icon-${TypeMap[props.type]}` : ''))
const hasMessage = computed(() => !!props.message)
const icon = computed(() => state.iconClass || (state.type && TypeMap[state.type] ? `el-icon-${TypeMap[state.type]}` : ''))
const hasMessage = computed(() => !!state.message)
const inputRef = ref<ComponentPublicInstance>(null)
const confirmRef = ref<ComponentPublicInstance>(null)
const confirmButtonClasses = computed(() => `el-button--primary ${props.confirmButtonClass}`)
const confirmButtonClasses = computed(() => `el-button--primary ${state.confirmButtonClass}`)
watch(() => state.inputValue, async val => {
await nextTick()
if (props.type === 'prompt' && val !== null) {
if (props.boxType === 'prompt' && val !== null) {
validate()
}
}, { immediate: true })
watch(() => visible.value, val => {
if (val) {
if (props.type === 'alert' || props.type === 'confirm') {
if (props.boxType === 'alert' || props.boxType === 'confirm') {
nextTick().then(() => { confirmRef.value?.$el?.focus?.() })
}
state.zIndex = PopupManager.nextZIndex()
}
if (props.type !== 'prompt') return
if (props.boxType !== 'prompt') return
if (val) {
nextTick().then(() => {
if (inputRef.value && inputRef.value.$el) {
@ -315,43 +292,43 @@ export default defineComponent({
const handleWrapperClick = () => {
if (props.closeOnClickModal) {
handleAction(props.distinguishCancelAndClose ? 'close' : 'cancel')
handleAction(state.distinguishCancelAndClose ? 'close' : 'cancel')
}
}
const handleInputEnter = () => {
if (props.inputType !== 'textarea') {
if (state.inputType !== 'textarea') {
return handleAction('confirm')
}
}
const handleAction = (action: Action) => {
if (props.type === 'prompt' && action === 'confirm' && !validate()) {
if (props.boxType === 'prompt' && action === 'confirm' && !validate()) {
return
}
state.action = action
if (props.beforeClose) {
props.beforeClose?.(action, state, doClose)
if (state.beforeClose) {
state.beforeClose?.(action, state, doClose)
} else {
doClose()
}
}
const validate = () => {
if (props.type === 'prompt') {
const inputPattern = props.inputPattern
if (props.boxType === 'prompt') {
const inputPattern = state.inputPattern
if (inputPattern && !inputPattern.test(state.inputValue || '')) {
state.editorErrorMessage = props.inputErrorMessage || t('el.messagebox.error')
state.editorErrorMessage = state.inputErrorMessage || t('el.messagebox.error')
state.validateError = true
return false
}
const inputValidator = props.inputValidator
const inputValidator = state.inputValidator
if (typeof inputValidator === 'function') {
const validateResult = inputValidator(state.inputValue)
if (validateResult === false) {
state.editorErrorMessage = props.inputErrorMessage || t('el.messagebox.error')
state.editorErrorMessage = state.inputErrorMessage || t('el.messagebox.error')
state.validateError = true
return false
}
@ -399,7 +376,7 @@ export default defineComponent({
useRestoreActive(visible)
return {
state,
...toRefs(state),
visible,
hasMessage,
icon,

View File

@ -1,7 +1,8 @@
import type { VNode } from 'vue'
export type Action = 'confirm' | 'close' | 'cancel'
export type MessageType = 'success' | 'warning' | 'info' | 'error'
export type MessageType = '' | 'success' | 'warning' | 'info' | 'error'
export type MessageBoxType = '' | 'prompt' | 'alert' | 'confirm'
export type MessageBoxData = MessageBoxInputData & Action
export interface MessageBoxInputData {
value: string
@ -12,28 +13,13 @@ export interface MessageBoxInputValidator {
(value: string): boolean | string
}
export interface MessageBoxState {
action: Action
cancelButtonLoading: boolean
cancelButtonText: string
confirmButtonLoading: boolean
confirmButtonDisabled: boolean
confirmButtonText: string
editorErrorMessage: string
// isOnComposition: boolean temporary commented
inputValue: string
validateError: boolean
zIndex: number
}
export declare class ElMessageBoxComponent {
export declare interface MessageBoxState {
title: string
message: string
type: MessageType
iconClass: string
customClass: string
showInput: boolean
showClose: boolean
inputValue: string
inputPlaceholder: string
inputType: string
@ -53,7 +39,16 @@ export declare class ElMessageBoxComponent {
cancelButtonClass: string
editorErrorMessage: string
close(): any
beforeClose: null | ((action: Action, instance: MessageBoxState, done: () => void) => void)
callback: null | Callback
distinguishCancelAndClose: boolean
modalFade: boolean
modalClass: string
// refer to: https://github.com/ElemeFE/element/commit/2999279ae34ef10c373ca795c87b020ed6753eed
// seemed ok for now without this state.
// isOnComposition: false, // temporary remove
validateError: boolean
zIndex: number
}
export type Callback =
@ -66,7 +61,7 @@ export interface ElMessageBoxOptions {
/** Callback before MessageBox closes, and it will prevent MessageBox from closing */
beforeClose?: (
action: Action,
instance: ElMessageBoxComponent,
instance: MessageBoxState,
done: () => void,
) => void
@ -100,6 +95,9 @@ export interface ElMessageBoxOptions {
/** Message type, used for icon display */
type?: MessageType
/** Message box type */
boxType?: MessageBoxType
/** Custom icon's class */
iconClass?: string

View File

@ -1,4 +1,4 @@
import { h, render } from 'vue'
import { h, watch, render } from 'vue'
import MessageBoxConstructor from './index.vue'
import isServer from '@element-plus/utils/isServer'
import { isVNode, isString } from '@element-plus/utils/util'
@ -55,7 +55,7 @@ const showMessage = (options: any) => {
const currentMsg = messageInstance.get(vm)
let resolve: Action | { value: string; action: Action; }
if (options.showInput) {
resolve = { value: vm.state.inputValue, action }
resolve = { value: vm.inputValue, action }
} else {
resolve = action
}
@ -81,14 +81,26 @@ const showMessage = (options: any) => {
// get component instance like v2.
const vm = instance.proxy as ComponentPublicInstance<{
visible: boolean
state: MessageBoxState
doClose: () => void
}>
} & MessageBoxState>
if (isVNode(options.message)) {
// Override slots since message is vnode type.
instance.slots.default = () => [options.message]
for (const prop in options) {
if (options.hasOwnProperty(prop) && !vm.$props.hasOwnProperty(prop)) {
vm[prop] = options[prop]
}
}
watch(() => vm.message, (newVal, oldVal) => {
if (isVNode(newVal)) {
// Override slots since message is vnode type.
instance.slots.default = () => [newVal]
} else if(isVNode(oldVal) && !isVNode(newVal)){
delete instance.slots.default
}
}, {
immediate: true,
})
// change visibility after everything is settled
vm.visible = true
return vm
@ -137,11 +149,14 @@ MessageBox.alert = (
{
title: title,
message: message,
type: 'alert',
type: '',
closeOnPressEscape: false,
closeOnClickModal: false,
},
options,
{
boxType: 'alert',
},
),
)
}
@ -162,10 +177,13 @@ MessageBox.confirm = (
{
title: title,
message: message,
type: 'confirm',
type: '',
showCancelButton: true,
},
options,
{
boxType: 'confirm',
},
),
)
}
@ -188,9 +206,12 @@ MessageBox.prompt = (
message: message,
showCancelButton: true,
showInput: true,
type: 'prompt',
type: '',
},
options,
{
boxType: 'prompt',
},
),
)
}

View File

@ -327,3 +327,4 @@ The corresponding methods are: `ElMessageBox`, `ElMessageBox.alert`, `ElMessageB
| inputErrorMessage | error message when validation fails | string | — | Illegal input |
| center | whether to align the content in center | boolean | — | false |
| roundButton | whether to use round button | boolean | — | false |
| buttonSize | custom size of confirm and cancel buttons | string | mini / small / medium / large | small |

View File

@ -330,3 +330,4 @@ Los métodos correspondientes: `ElMessageBox`, `ElMessageBox.alert`, `ElMessageB
| inputErrorMessage | mensaje de error cuando la validación falla | string | — | Illegal input |
| center | utilizado para alinear el contenido al centro | boolean | — | false |
| roundButton | utilizado para redondear el botón | boolean | — | false |
| buttonSize | custom size of confirm and cancel buttons | string | mini / small / medium / large | small |

View File

@ -329,3 +329,4 @@ Les méthodes correspondantes sont: `ElMessageBox`, `ElMessageBox.alert`, `ElMes
| inputErrorMessage | Message d'erreur lorsque la validation échoue. | string | — | Illegal input |
| center | Si le contenu doit être centré. | boolean | — | false |
| roundButton | Si le bouton doit être rond. | boolean | — | false |
| buttonSize | custom size of confirm and cancel buttons | string | mini / small / medium / large | small |

View File

@ -326,3 +326,4 @@ import { ElMessageBox } from 'element-plus';
| inputErrorMessage | バリデーション失敗時のエラーメッセージ | string | — | Illegal input |
| center | コンテンツを中央に配置するかどうか | boolean | — | false |
| roundButton | 丸いボタンを使うかどうか | boolean | — | false |
| buttonSize | custom size of confirm and cancel buttons | string | mini / small / medium / large | small |

View File

@ -325,3 +325,4 @@ import { ElMessageBox } from 'element-plus';
| inputErrorMessage | 校验未通过时的提示文本 | string | — | 输入的数据不合法! |
| center | 是否居中布局 | boolean | — | false |
| roundButton | 是否使用圆角按钮 | boolean | — | false |
| buttonSize | 自定义确认按钮及取消按钮的大小 | string | mini / small / medium / large | small |