feat: redesign task detail paper flow
ci / gitleaks (push) Successful in 8s
ci / docker (push) Successful in 3m37s

This commit is contained in:
2026-09-18 18:26:31 +08:00
parent 26f6ef31a6
commit 432a133a42
9 changed files with 263 additions and 45 deletions
+14
View File
@@ -132,6 +132,20 @@ describe('AppSheet', () => {
expect(host.getAttribute('aria-hidden')).toBeNull()
})
it('places a newly opened stacked modal above the existing modal', async () => {
const host = document.createElement('main'); document.body.append(host)
const second = ref(false)
const app = createApp({ setup: () => () => h('div', [
h(AppSheet, { open:true, titleId:'layer-lower' }, { default:() => h('h2',{id:'layer-lower'},'lower') }),
h(AppSheet, { open:second.value, titleId:'layer-upper' }, { default:() => h('h2',{id:'layer-upper'},'upper') }),
]) })
app.mount(host); cleanups.push(() => app.unmount()); await nextTick(); await nextTick()
second.value = true; await nextTick(); await nextTick()
const lower = document.querySelector<HTMLElement>('[aria-labelledby="layer-lower"]')!.parentElement!
const upper = document.querySelector<HTMLElement>('[aria-labelledby="layer-upper"]')!.parentElement!
expect(Number(upper.style.zIndex)).toBeGreaterThan(Number(lower.style.zIndex))
})
it('focuses prompt input and confirm dialog cancel action', async () => {
const host = document.createElement('div'); document.body.append(host)
const dialog = ref<InstanceType<typeof AppDialog> | null>(null)
+4 -2
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { nextTick, onBeforeUnmount, ref, watch } from 'vue'
import { isTopOverlay, overlayRoot, popOverlay, pushOverlay } from '../composables/useOverlayStack'
import { isTopOverlay, overlayRoot, overlayZIndex, popOverlay, pushOverlay } from '../composables/useOverlayStack'
const props = withDefaults(defineProps<{
open: boolean
@@ -17,6 +17,7 @@ const props = withDefaults(defineProps<{
defineOptions({ inheritAttrs: false })
const emit = defineEmits<{ close: [] }>()
const panel = ref<HTMLElement | null>(null)
const layerZIndex = ref(80)
let overlayId: symbol | null = null
function requestClose() {
@@ -54,6 +55,7 @@ function focusIntoPanel() {
async function activate() {
if (!props.open || !props.modal || overlayId) return
overlayId = pushOverlay(requestClose, () => props.busy, focusIntoPanel)
layerZIndex.value = overlayZIndex(overlayId)
await nextTick()
if (!props.open || !props.modal || !overlayId) return
focusIntoPanel()
@@ -71,7 +73,7 @@ onBeforeUnmount(deactivate)
<template>
<Teleport v-if="modal" :to="overlayRoot()">
<div v-if="open" class="app-overlay app-sheet-mask" :aria-busy="busy || undefined" @click="scrimClose">
<div v-if="open" class="app-overlay app-sheet-mask" :style="{ zIndex: layerZIndex }" :aria-busy="busy || undefined" @click="scrimClose">
<component :is="$attrs.onSubmit ? 'form' : 'section'" ref="panel" class="app-sheet" :class="[`app-sheet--${variant}`, panelClass]" role="dialog" aria-modal="true" :aria-labelledby="titleId" :aria-describedby="descriptionId" :aria-label="label" tabindex="-1" v-bind="$attrs" @keydown="keydown">
<slot />
</component>