From 079ece6dbe18c841dab7f1d9f03ddd3eb6511038 Mon Sep 17 00:00:00 2001 From: huaweidevcloud Date: Tue, 29 Aug 2023 09:47:33 +0800 Subject: [PATCH 1/3] refactor: modal --- packages/devui-vue/devui/modal/__tests__/modal.spec.tsx | 2 +- packages/devui-vue/devui/modal/src/components/body.tsx | 2 +- packages/devui-vue/devui/modal/src/components/footer.tsx | 2 +- packages/devui-vue/devui/modal/src/components/header.tsx | 2 +- packages/devui-vue/devui/modal/src/modal.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/devui-vue/devui/modal/__tests__/modal.spec.tsx b/packages/devui-vue/devui/modal/__tests__/modal.spec.tsx index 49f5780aae..54ea019199 100644 --- a/packages/devui-vue/devui/modal/__tests__/modal.spec.tsx +++ b/packages/devui-vue/devui/modal/__tests__/modal.spec.tsx @@ -4,7 +4,7 @@ import DModal from '../src/modal'; import DModalHeader from '../src/components/header'; import DModalFooter from '../src/components/footer'; import DIcon from '../../icon/src/icon'; -import { useNamespace } from '../../shared/hooks/use-namespace'; +import { useNamespace } from '@devui/shared'; import { wait } from '../../shared/utils/'; const ns = useNamespace('modal', true); diff --git a/packages/devui-vue/devui/modal/src/components/body.tsx b/packages/devui-vue/devui/modal/src/components/body.tsx index a095ae2233..785b501cde 100644 --- a/packages/devui-vue/devui/modal/src/components/body.tsx +++ b/packages/devui-vue/devui/modal/src/components/body.tsx @@ -1,5 +1,5 @@ import { defineComponent } from 'vue'; -import { useNamespace } from '../../../shared/hooks/use-namespace'; +import { useNamespace } from '@devui/shared'; export default defineComponent({ name: 'DModalBody', diff --git a/packages/devui-vue/devui/modal/src/components/footer.tsx b/packages/devui-vue/devui/modal/src/components/footer.tsx index f24d1e027d..50d77d1ee7 100644 --- a/packages/devui-vue/devui/modal/src/components/footer.tsx +++ b/packages/devui-vue/devui/modal/src/components/footer.tsx @@ -1,5 +1,5 @@ import { defineComponent } from 'vue'; -import { useNamespace } from '../../../shared/hooks/use-namespace'; +import { useNamespace } from '@devui/shared'; export default defineComponent({ name: 'DModalFooter', diff --git a/packages/devui-vue/devui/modal/src/components/header.tsx b/packages/devui-vue/devui/modal/src/components/header.tsx index 1ed9937dca..d15f67aa66 100644 --- a/packages/devui-vue/devui/modal/src/components/header.tsx +++ b/packages/devui-vue/devui/modal/src/components/header.tsx @@ -1,5 +1,5 @@ import { defineComponent } from 'vue'; -import { useNamespace } from '../../../shared/hooks/use-namespace'; +import { useNamespace } from '@devui/shared'; export default defineComponent({ name: 'DModalHeader', diff --git a/packages/devui-vue/devui/modal/src/modal.tsx b/packages/devui-vue/devui/modal/src/modal.tsx index f389a2f354..db4e2372b3 100644 --- a/packages/devui-vue/devui/modal/src/modal.tsx +++ b/packages/devui-vue/devui/modal/src/modal.tsx @@ -6,7 +6,7 @@ import { useModal, useModalRender } from './composables/use-modal'; import { useDraggable } from './composables/use-draggable'; import DModalHeader from './components/header'; import DModalBody from './components/body'; -import { useNamespace } from '../../shared/hooks/use-namespace'; +import { useNamespace } from '@devui/shared'; import './modal.scss'; interface TypeList { From abb8a16c640eaac470cde7e086b5f4635e85f1a4 Mon Sep 17 00:00:00 2001 From: huaweidevcloud Date: Tue, 29 Aug 2023 09:49:20 +0800 Subject: [PATCH 2/3] refactor: modal --- packages/devui-vue/devui/modal/src/const.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/devui-vue/devui/modal/src/const.ts diff --git a/packages/devui-vue/devui/modal/src/const.ts b/packages/devui-vue/devui/modal/src/const.ts new file mode 100644 index 0000000000..41698cb206 --- /dev/null +++ b/packages/devui-vue/devui/modal/src/const.ts @@ -0,0 +1 @@ +export const ZINDEX = 1; From f35fc1b6bbbe0ae727238a0c3b72f49e574ab44f Mon Sep 17 00:00:00 2001 From: huaweidevcloud Date: Tue, 29 Aug 2023 11:24:58 +0800 Subject: [PATCH 3/3] refactor: modal --- packages/devui-vue/devui/modal/index.ts | 2 +- .../modal/src/components/modal-icons.tsx | 19 ++++++ .../devui/modal/src/composables/use-modal.ts | 67 ++++++++++++------- .../devui-vue/devui/modal/src/modal-types.ts | 4 +- packages/devui-vue/devui/modal/src/modal.scss | 42 +++++------- packages/devui-vue/devui/modal/src/modal.tsx | 21 +++--- 6 files changed, 88 insertions(+), 67 deletions(-) create mode 100644 packages/devui-vue/devui/modal/src/components/modal-icons.tsx diff --git a/packages/devui-vue/devui/modal/index.ts b/packages/devui-vue/devui/modal/index.ts index fbc3467196..9c68b4bc9a 100644 --- a/packages/devui-vue/devui/modal/index.ts +++ b/packages/devui-vue/devui/modal/index.ts @@ -11,7 +11,7 @@ export * from './src/modal-types'; export { Modal, ModalHeader, ModalBody, ModalFooter }; export default { - title: 'Modal 弹窗', + title: 'Modal 模态框', category: '反馈', status: '100%', install(app: App): void { diff --git a/packages/devui-vue/devui/modal/src/components/modal-icons.tsx b/packages/devui-vue/devui/modal/src/components/modal-icons.tsx new file mode 100644 index 0000000000..fd6cfa8d7a --- /dev/null +++ b/packages/devui-vue/devui/modal/src/components/modal-icons.tsx @@ -0,0 +1,19 @@ +export function CloseIcon(): JSX.Element { + return ( + + + + + + + + ) +} \ No newline at end of file diff --git a/packages/devui-vue/devui/modal/src/composables/use-modal.ts b/packages/devui-vue/devui/modal/src/composables/use-modal.ts index b8cab8c255..7e33ddfbd5 100644 --- a/packages/devui-vue/devui/modal/src/composables/use-modal.ts +++ b/packages/devui-vue/devui/modal/src/composables/use-modal.ts @@ -1,8 +1,32 @@ -import { onMounted, onUnmounted, watch } from 'vue'; +import { onUnmounted, watch, ref } from 'vue'; import { ModalProps, EmitEventFn, UseModal } from '../modal-types'; import { lockScroll } from '../../../shared/utils/lock-scroll'; +import { useNamespace, focusStack } from '@devui/shared' +import type { FoucusLayer } from '@devui/shared' +import { ZINDEX } from '../const' export function useModal(props: ModalProps, emit: EmitEventFn): UseModal { + let lockScrollCb: () => void; + const ns = useNamespace('modal') + const overlayZIndex = ref('') + const modalZIndex = ref('') + const paused = ref(false) + const layer: FoucusLayer = { + pause() { + paused.value = true + }, + resume() { + paused.value = false + } + } + + const removeAdditions = () => { + lockScrollCb?.() + props.escapable && window.removeEventListener('keyup', onKeydown) + setTimeout(() => { + focusStack.remove(layer) + }); + } function close(): void { emit('update:modelValue', false); emit('close'); @@ -12,44 +36,37 @@ export function useModal(props: ModalProps, emit: EmitEventFn): UseModal { } function onKeydown(event: KeyboardEvent) { - if (event.code === 'Escape') { + if (event.code === 'Escape' && !paused.value) { execClose(); } } - onMounted(() => { - if (props.escapable) { - window.addEventListener('keydown', onKeydown); - } - }); - - onUnmounted(() => { - if (props.escapable) { - window.addEventListener('keydown', onKeydown); - } - }); - - return { execClose }; -} + function calculateZIndex() { + const modalDomsLength = document.querySelectorAll(`.${ns.b()}`).length + overlayZIndex.value = `calc(var(--devui-z-index-modal, 1050) + ${(modalDomsLength - 1) * ZINDEX} - 1)` + modalZIndex.value = `calc(var(--devui-z-index-modal, 1050) + ${(modalDomsLength - 1) * ZINDEX})` + } -export function useModalRender(props: ModalProps): void { - let lockScrollCb: () => void; - const removeBodyAdditions = () => { - lockScrollCb?.(); - }; watch( () => props.modelValue, - (val) => { + (val, oldVal) => { if (val) { + calculateZIndex() props.lockScroll && (lockScrollCb = lockScroll()); + props.escapable && window.addEventListener('keyup', onKeydown) + focusStack.push(layer) } else { - removeBodyAdditions(); + oldVal !== undefined && removeAdditions() } }, { immediate: true, + flush: 'post' } ); - onUnmounted(removeBodyAdditions); -} + onUnmounted(removeAdditions); + + return { execClose, overlayZIndex, modalZIndex }; + +} \ No newline at end of file diff --git a/packages/devui-vue/devui/modal/src/modal-types.ts b/packages/devui-vue/devui/modal/src/modal-types.ts index c961d743be..b8437bdce1 100644 --- a/packages/devui-vue/devui/modal/src/modal-types.ts +++ b/packages/devui-vue/devui/modal/src/modal-types.ts @@ -1,4 +1,4 @@ -import type { PropType, ExtractPropTypes } from 'vue'; +import type { PropType, ExtractPropTypes, Ref } from 'vue'; export type ModalType = 'success' | 'failed' | 'warning' | 'info' | ''; @@ -62,6 +62,8 @@ export type EmitEventFn = (event: EmitName, result?: boolean) => void; export interface UseModal { execClose: () => void; + overlayZIndex: Ref; + modalZIndex: Ref; } export type ModalProps = ExtractPropTypes; diff --git a/packages/devui-vue/devui/modal/src/modal.scss b/packages/devui-vue/devui/modal/src/modal.scss index 50644ac573..35a7b31951 100644 --- a/packages/devui-vue/devui/modal/src/modal.scss +++ b/packages/devui-vue/devui/modal/src/modal.scss @@ -5,7 +5,7 @@ top: 50%; left: 50%; width: 300px; - border-radius: $devui-border-radius; + border-radius: $devui-border-radius-card; border: none; opacity: 1; transform: translate(-50%, -50%); @@ -17,27 +17,10 @@ .btn-close { position: absolute; - right: 16px; - top: 20px; - width: 20px; - height: 20px; - line-height: 20px; - text-align: center; - cursor: pointer; - background: transparent; - border: 0; + right: 20px; + top: 18px; -webkit-appearance: none; - - &:hover { - color: $devui-icon-fill-active-hover; - background-color: $devui-list-item-hover-bg; - } - - & i { - position: absolute; - right: 0; - top: 0; - } + z-index: calc(var(--devui-z-index-model, 1050) + 1); } .type-content { @@ -70,12 +53,17 @@ .#{$devui-prefix}-modal__header { width: 100%; - height: 56px; + height: 46px; + line-height: 26px; padding: 20px 20px 0; - font-size: $devui-font-size-card-title; + font-size: $devui-font-size-modal-title; font-weight: bold; + letter-spacing: 0; box-sizing: border-box; - border: none; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + user-select: none; .header-alert-icon { display: inline-block; @@ -97,10 +85,10 @@ width: 100%; border-top: none; text-align: center; - padding: 0 32px 24px; + padding: 0 20px 20px 20px; box-sizing: border-box; - & > * { + &>* { margin: 0 4px; } } @@ -119,4 +107,4 @@ opacity: 0.2; top: calc(50% - 24px); } -} +} \ No newline at end of file diff --git a/packages/devui-vue/devui/modal/src/modal.tsx b/packages/devui-vue/devui/modal/src/modal.tsx index db4e2372b3..c1b27c72c0 100644 --- a/packages/devui-vue/devui/modal/src/modal.tsx +++ b/packages/devui-vue/devui/modal/src/modal.tsx @@ -1,11 +1,12 @@ import { computed, defineComponent, nextTick, ref, Teleport, toRefs, Transition, watch } from 'vue'; import { modalProps, ModalProps, ModalType } from './modal-types'; -import { Icon } from '../../icon'; +import { Icon } from '@devui/shared/components/icon'; import { FixedOverlay } from '../../overlay'; -import { useModal, useModalRender } from './composables/use-modal'; +import { useModal } from './composables/use-modal'; import { useDraggable } from './composables/use-draggable'; import DModalHeader from './components/header'; import DModalBody from './components/body'; +import { CloseIcon } from './components/modal-icons'; import { useNamespace } from '@devui/shared'; import './modal.scss'; @@ -24,8 +25,7 @@ export default defineComponent({ setup(props: ModalProps, { slots, attrs, emit }) { const ns = useNamespace('modal'); const { modelValue, title, showClose, showOverlay, appendToBody, closeOnClickOverlay, keepLast } = toRefs(props); - const { execClose } = useModal(props, emit); - useModalRender(props); + const { execClose, overlayZIndex, modalZIndex } = useModal(props, emit); const dialogRef = ref(); const headerRef = ref(); const draggable = computed(() => props.draggable); @@ -90,11 +90,11 @@ export default defineComponent({ {showOverlay.value && ( )} @@ -103,13 +103,8 @@ export default defineComponent({ ref={dialogRef} class={ns.b()} {...attrs} - onClick={(e: Event) => e.stopPropagation()} - style={{ transform: modalPosition.value }}> - {showClose.value && ( -
- -
- )} + style={{ transform: modalPosition.value, zIndex: modalZIndex.value }}> + {showClose.value && } {props.type ? ( renderType() ) : (