@polymarbot/nuxt-layer-shadcn-ui 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,6 +8,7 @@
8
8
  * Stacking order (low → high):
9
9
  * layout header z-100 (project convention)
10
10
  * dialog / sheet (Modal / Drawer) z-200 (cover the header)
11
+ * alert dialog (system interrupts) z-250 (always above other modals)
11
12
  * tooltip / popover / dropdown / select z-300 (above modal when nested)
12
13
  * ========================================================================= */
13
14
 
@@ -19,6 +20,17 @@
19
20
  z-index: 200;
20
21
  }
21
22
 
23
+ /* AlertDialog must always sit above regular Modals, regardless of teleport
24
+ * order. Vue's <Teleport> places content at a stable anchor based on the
25
+ * host component's mount time — so an AlertDialog declared in app.vue (early
26
+ * mount) ends up *behind* a page-level Modal opened later. The overlay is
27
+ * matched via :has() because reka-ui's DialogPortal renders it as the
28
+ * adjacent previous sibling of the content. */
29
+ [data-slot='dialog-content'][data-alert-dialog],
30
+ [data-slot='dialog-overlay']:has(+ [data-slot='dialog-content'][data-alert-dialog]) {
31
+ z-index: 250;
32
+ }
33
+
22
34
  [data-slot='tooltip-content'],
23
35
  [data-slot='popover-content'],
24
36
  [data-slot='dropdown-menu-content'],
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- const { current, isOpen, close, onClosed } = useDialogState()
2
+ const { current, isOpen, isActive, close, onClosed } = useDialogState()
3
3
 
4
4
  const isAlert = computed(() => current.value?.options.rejectLabel === '')
5
5
  const isDestructive = computed(() => {
@@ -10,6 +10,8 @@ const isDestructive = computed(() => {
10
10
 
11
11
  <template>
12
12
  <Modal
13
+ v-if="isActive"
14
+ data-alert-dialog
13
15
  :visible="isOpen"
14
16
  :title="current?.options.title"
15
17
  :type="current?.options.type"
@@ -11,6 +11,8 @@ import {
11
11
  } from '../../shadcn/sheet'
12
12
  import type { DrawerProps } from './types'
13
13
 
14
+ defineOptions({ inheritAttrs: false })
15
+
14
16
  const props = withDefaults(defineProps<DrawerProps>(), {
15
17
  showClose: true,
16
18
  closeOnClickOutside: false,
@@ -92,6 +94,7 @@ const contentClass = computed(() =>
92
94
  </SheetTrigger>
93
95
 
94
96
  <SheetContent
97
+ v-bind="$attrs"
95
98
  :side="side"
96
99
  :class="contentClass"
97
100
  @pointerDownOutside="onPointerDownOutside"
@@ -11,6 +11,8 @@ import {
11
11
  } from '../../shadcn/dialog'
12
12
  import type { ModalProps } from './types'
13
13
 
14
+ defineOptions({ inheritAttrs: false })
15
+
14
16
  const props = withDefaults(defineProps<ModalProps>(), {
15
17
  showClose: true,
16
18
  closeOnClickOutside: false,
@@ -97,6 +99,7 @@ const contentClass = computed(() =>
97
99
  </DialogTrigger>
98
100
 
99
101
  <DialogContent
102
+ v-bind="$attrs"
100
103
  :class="contentClass"
101
104
  :showCloseButton="false"
102
105
  @pointerDownOutside="onPointerDownOutside"
@@ -18,6 +18,7 @@ interface DialogQueueItem {
18
18
 
19
19
  const dialogQueue = reactive<DialogQueueItem[]>([])
20
20
  const isOpen = ref(false)
21
+ const mountedInstances = ref<symbol[]>([])
21
22
 
22
23
  function showDialog (options: DialogOptions): Promise<boolean> {
23
24
  return new Promise<boolean>(resolve => {
@@ -52,8 +53,22 @@ export function useDialog () {
52
53
 
53
54
  /**
54
55
  * Internal composable for the AlertDialog component to access dialog queue.
56
+ *
57
+ * Multiple `<AlertDialog />` instances can be mounted simultaneously (e.g. in
58
+ * Storybook docs view), but only the first one renders the modal — the rest
59
+ * stay inert via `isActive`. This prevents stacked overlays and duplicate
60
+ * `onClosed` events from over-shifting the shared queue.
55
61
  */
56
62
  export function useDialogState () {
63
+ const id = Symbol('AlertDialog')
64
+ mountedInstances.value.push(id)
65
+
66
+ onScopeDispose(() => {
67
+ const idx = mountedInstances.value.indexOf(id)
68
+ if (idx >= 0) mountedInstances.value.splice(idx, 1)
69
+ })
70
+
71
+ const isActive = computed(() => mountedInstances.value[0] === id)
57
72
  const current = computed(() => dialogQueue[0] ?? null)
58
73
 
59
74
  /** Resolve current dialog and trigger close animation */
@@ -72,5 +87,5 @@ export function useDialogState () {
72
87
  }
73
88
  }
74
89
 
75
- return { current, isOpen, close, onClosed }
90
+ return { current, isOpen, isActive, close, onClosed }
76
91
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polymarbot/nuxt-layer-shadcn-ui",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Nuxt layer providing shadcn-vue based UI components",
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
@@ -42,5 +42,5 @@
42
42
  "vue-i18n": "^11",
43
43
  "vue-router": "^4 || ^5"
44
44
  },
45
- "gitHead": "dbce6d8341e369cdd6ae0c7008b31325c339d200"
45
+ "gitHead": "6e895437229b0f15e709d90785903ea846399c36"
46
46
  }