Skip to content

fix(stack): recompute z-index for overlays outside global stack#22790

Closed
Luminet2023 wants to merge 2 commits intovuetifyjs:masterfrom
Luminet-org:master
Closed

fix(stack): recompute z-index for overlays outside global stack#22790
Luminet2023 wants to merge 2 commits intovuetifyjs:masterfrom
Luminet-org:master

Conversation

@Luminet2023
Copy link
Copy Markdown

Description

When using v-snackbar-queue, a snackbar triggered from inside a v-dialog can keep a stale z-index after the dialog is closed.

VSnackbar uses _disableGlobalStack, so it does not create its own entry in globalStack. However, its z-index is still computed when it becomes active. If a snackbar is opened while a dialog is active, it can capture a z-index derived from the dialog stack state.

After the dialog is closed, that snackbar keeps the stale z-index. When new snackbars are added later, queued items may conflict with each other visually, causing incorrect overlap or stacking order.

Reproduction steps:

  1. Open the dialog
  2. Trigger a snackbar from inside the dialog
  3. Close the dialog
  4. Trigger more snackbars from outside
  5. Observe queue item overlap / incorrect stacking

Problem

Overlays that opt out of globalStack can keep a stale z-index.

useStack() computes _zIndex when the overlay becomes active, and stackStyles always returns that cached value. This works for overlays that participate in globalStack, but overlays created with _disableGlobalStack do not create their own stack entry.

As a result, if the global stack changes after one of these overlays is shown, its z-index may no longer reflect the current top of the stack.

Fix

Extract the shared z-index calculation into getStackZIndex() and reuse it both when the overlay becomes active and when resolving stackStyles.

For overlays that participate in globalStack, behavior stays the same.

For overlays outside globalStack, stackStyles now derives z-index from the latest global stack state instead of always returning the cached _zIndex value.

Markup:

<template>
  <v-app>
    <v-container>
      <div class="d-flex ga-2">
        <v-btn color="success" @click="addMessage('success')">Success</v-btn>
        <v-btn color="info" @click="addMessage('info')">Info</v-btn>
        <v-btn color="error" @click="addMessage('error')">Error</v-btn>
        <v-btn color="surface-variant" @click="addMessage()">Default</v-btn>
        <v-btn prepend-icon="mdi-refresh" variant="outlined" @click="snackbarQueue?.clear()">Clear</v-btn>
      </div>
      <v-dialog max-width="500">
        <template v-slot:activator="{ props: activatorProps }">
          <v-btn v-bind="activatorProps" color="surface-variant" text="Open Dialog" variant="flat"></v-btn>
        </template>

        <template v-slot:default="{ isActive }">
          <v-card title="Dialog">
            <v-card-text>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua.
            </v-card-text>

            <v-card-actions>
              <v-spacer></v-spacer>
              <v-btn color="success" @click="addMessage('success')">Success</v-btn>
              <v-btn text="Close Dialog" @click="isActive.value = false"></v-btn>
            </v-card-actions>
          </v-card>
        </template>
      </v-dialog>
      <pre>{{ logs.join('\n') }}</pre>

      <v-snackbar-queue ref="snackbarQueue" v-model="messages" :total-visible="50" closable
        collapsed></v-snackbar-queue>
    </v-container>
  </v-app>
</template>

<script setup>
import { ref } from 'vue'


const snackbarQueue = ref()
const messages = ref([])
const logs = ref([])
let messageCount = 0

function addMessage(color) {
  const id = ++messageCount
  messages.value.push({
    text: `Message #${id}`,
    color,
    onDismiss(reason) {
      logs.value.unshift(`Message #${id}: Closed (${reason})`)
    },
  })
}

</script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant