mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-26 07:57:36 +00:00
Temporarily patch FE-569 by keeping the affected portaled Reka dropdowns
and menus above their containing PrimeVue dialogs when PrimeVue auto
z-index state has been elevated.
- **What**: Added a small compatibility helper,
`usePrimeVueOverlayChildStyle`, that returns an anchor ref plus a
computed inline style for child popover content. The helper finds the
nearest PrimeVue dialog mask (`.p-dialog-mask` / `.p-overlay-mask`) from
the parent surface and, only when found, applies `parent z-index + 1` to
the affected Reka overlay content.
- **What**: Applied that helper at the exact PrimeVue parent surfaces
where the issue was found. This PR does not add a global overlay policy
and does not change every Reka select/dropdown in the app.
- **What**: Added optional `contentStyle`/`selectContentStyle` plumbing
only where needed so the style reaches the actual portaled Reka overlay
root.
- **What**: Added focused unit coverage for the helper contract: no
PrimeVue parent preserves existing stacking, PrimeVue dialog/overlay
masks render child content above the parent, low parent z-index values
respect the Reka floor, and invalid z-index values do not inject an
inline override.
- **Approach**: This is intentionally a minimal, parent-scoped band-aid.
It avoids a global PrimeVue overlay scanner because global sampling can
be polluted by unrelated persistent PrimeVue roots such as Toast and
would turn this fix into a broader layering policy.
- **Approach**: The patch targets the confirmed failure mode: a Reka
child overlay rendering below its owning PrimeVue dialog after PrimeVue
autoZIndex has been elevated. It does not attempt to solve PrimeVue
z-index globally.
- **Lifecycle**: This is temporary migration compatibility. PrimeVue
dialogs and controls are being incrementally migrated to Reka UI, so
`usePrimeVueOverlayChildStyle` and the optional style props added for
FE-569 should be removed once the affected parent surfaces move to Reka.
- **Breaking**: None. New props are optional and no public API contract
is changed.
- **Dependencies**: None.
This PR pinpoints the six affected user-facing surfaces below. Each
patch is applied from the PrimeVue dialog parent and passed only to the
Reka child overlay content that can render underneath that parent.
https://github.com/user-attachments/assets/d0d1522a-ffc7-4934-9e7a-06b83e20f809
1. **Workflow Template Library filters**
- **How to enter**: click the Templates button in the left sidebar, or
open the Comfy menu and choose **Browse Templates**.
- **Affected elements**: the template filter popovers in
`WorkflowTemplateSelectorDialog`: **Model**, **Use case**, **Runs on**,
and **Sort by**.
- **Patch point**: `WorkflowTemplateSelectorDialog.vue` anchors to the
template dialog content filter area and passes `selectContentStyle` to
the affected `MultiSelect` / `SingleSelect` controls.
https://github.com/user-attachments/assets/3641fa24-da51-4392-a904-9085f8a5a2f4
2. **Manager dialog header controls**
- **How to enter**: open Manager from the top/menu Manager entry when
the new Manager UI is available.
- **Affected elements**: the Manager header controls in `ManagerDialog`:
search mode `SingleSelect`, search autocomplete suggestions, and
**Sort** `SingleSelect`.
- **Patch point**: `ManagerDialog.vue` anchors to the dialog header and
passes `selectContentStyle` to those three Reka overlays.
https://github.com/user-attachments/assets/cf25cc06-f851-48ef-9d9c-9ec2da8afc06
3. **Asset Browser filter bar**
- **How to enter**: open the Asset Browser from an eligible model widget
browse action, the Model Library flow, or another
`useAssetBrowserDialog` caller.
- **Affected elements**: `AssetFilterBar` controls: **File formats**,
**Base models**, **Ownership**, and **Sort by**.
- **Patch point**: `AssetBrowserModal.vue` anchors to the PrimeVue
dialog header and passes the style through `AssetFilterBar` to its
`MultiSelect` / `SingleSelect` controls.
https://github.com/user-attachments/assets/e27bd805-10c0-4b3b-97f3-9e11faa47021
4. **Asset Browser model info panel**
- **How to enter**: open Asset Browser, select an asset, then use the
right-side model info panel.
- **Affected element**: the **Model type** select in `ModelInfoPanel`.
- **Patch point**: `AssetBrowserModal.vue` reuses the same parent-scoped
style and passes it to `ModelInfoPanel` as `selectContentStyle`.
https://github.com/user-attachments/assets/5e9f7ef0-ebd7-4987-ba1b-2137c034086f
5. **Upload Model confirmation step**
- **How to enter**: open Asset Browser, click **Upload**, enter/fetch
model metadata, then proceed to the confirmation step.
- **Affected element**: the **Model type** `SingleSelect` in
`UploadModelConfirmation`.
- **Patch point**: `UploadModelConfirmation.vue` anchors within the
upload dialog content and passes `selectContentStyle` to the model type
selector.
https://github.com/user-attachments/assets/ec145f26-8621-455b-915e-bedee47e1cbd
6. **Settings > Keybinding panel controls**
- **How to enter**: open Settings from the sidebar/menu, then select the
**Keybinding** panel.
- **Affected elements**: the keybinding preset select, the preset
overflow dropdown menu, and the row context menu inside
`KeybindingPanel`.
- **Patch point**: `KeybindingPanel.vue` anchors to the settings dialog
panel and passes `keybindingOverlayContentStyle` only to those Reka
overlay roots.
- Confirm the patch stays narrowly scoped to the six known PrimeVue
parent + Reka child overlay surfaces above.
- Confirm `contentStyle` reaches the actual portaled Reka overlay
content in each patched path.
- Confirm the fallback behavior preserves existing stacking when no
PrimeVue parent overlay is found; in that case the helper returns an
empty style object and leaves existing Tailwind z-index classes alone.
- Please avoid expanding this into a larger overlay refactor. The goal
is a clean, backport-friendly compatibility patch while the Reka
migration continues.
Validation performed:
- `pnpm exec vitest run src/composables/usePopoverSizing.test.ts`
- `pnpm typecheck`
- `pnpm lint` (passes with existing unrelated warnings only)
- `pnpm format:check`
- commit hook lint-staged checks (`oxfmt`, `stylelint`, `oxlint`,
`eslint --fix`, `pnpm typecheck`)
- pre-push `pnpm knip`
Linear: FE-569
https://github.com/user-attachments/assets/e73761af-9867-4c50-ab0d-4e32e59011e1
https://github.com/user-attachments/assets/145daf4d-3268-428b-9987-1e1afd0b866f
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12038-fix-keep-Reka-overlays-above-PrimeVue-dialogs-3596d73d365081e7af49dbc4d3905962)
by [Unito](https://www.unito.io)
(cherry picked from commit 1ab9752af8)
152 lines
3.8 KiB
Vue
152 lines
3.8 KiB
Vue
<template>
|
|
<SelectRoot v-model="selectedItem" v-model:open="isOpen" :disabled>
|
|
<SelectTrigger
|
|
v-bind="$attrs"
|
|
:aria-label="label || t('g.singleSelectDropdown')"
|
|
:aria-busy="loading || undefined"
|
|
:aria-invalid="invalid || undefined"
|
|
:class="
|
|
selectTriggerVariants({
|
|
size,
|
|
border: invalid ? 'invalid' : 'none'
|
|
})
|
|
"
|
|
>
|
|
<div
|
|
:class="
|
|
cn(
|
|
'flex flex-1 items-center gap-2 overflow-hidden py-2',
|
|
size === 'md' ? 'pl-3 text-xs' : 'pl-4 text-sm'
|
|
)
|
|
"
|
|
>
|
|
<i
|
|
v-if="loading"
|
|
class="icon-[lucide--loader-circle] shrink-0 animate-spin text-muted-foreground"
|
|
/>
|
|
<slot v-else name="icon" />
|
|
<SelectValue :placeholder="label" class="truncate" />
|
|
</div>
|
|
<div :class="selectDropdownClass">
|
|
<i class="icon-[lucide--chevron-down] text-muted-foreground" />
|
|
</div>
|
|
</SelectTrigger>
|
|
|
|
<SelectPortal>
|
|
<SelectContent
|
|
position="popper"
|
|
:side-offset="8"
|
|
align="start"
|
|
:style="[optionStyle, contentStyle]"
|
|
:class="cn(selectContentClass, 'min-w-(--reka-select-trigger-width)')"
|
|
@keydown="onContentKeydown"
|
|
>
|
|
<SelectViewport
|
|
:style="{ maxHeight: `min(${listMaxHeight}, 50vh)` }"
|
|
class="scrollbar-custom w-full"
|
|
>
|
|
<SelectItem
|
|
v-for="opt in options"
|
|
:key="opt.value"
|
|
:value="opt.value"
|
|
:class="selectItemVariants({ layout: 'single' })"
|
|
>
|
|
<SelectItemText class="truncate">
|
|
{{ opt.name }}
|
|
</SelectItemText>
|
|
<SelectItemIndicator
|
|
class="flex shrink-0 items-center justify-center"
|
|
>
|
|
<i
|
|
class="icon-[lucide--check] text-base-foreground"
|
|
aria-hidden="true"
|
|
/>
|
|
</SelectItemIndicator>
|
|
</SelectItem>
|
|
</SelectViewport>
|
|
</SelectContent>
|
|
</SelectPortal>
|
|
</SelectRoot>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import {
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectItemIndicator,
|
|
SelectItemText,
|
|
SelectPortal,
|
|
SelectRoot,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
SelectViewport
|
|
} from 'reka-ui'
|
|
import { ref } from 'vue'
|
|
import type { StyleValue } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
import { usePopoverSizing } from '@/composables/usePopoverSizing'
|
|
import { cn } from '@/utils/tailwindUtil'
|
|
|
|
import {
|
|
selectContentClass,
|
|
selectDropdownClass,
|
|
selectItemVariants,
|
|
selectTriggerVariants,
|
|
stopEscapeToDocument
|
|
} from './select.variants'
|
|
import type { SelectOption } from './types'
|
|
|
|
defineOptions({
|
|
inheritAttrs: false
|
|
})
|
|
|
|
const {
|
|
label,
|
|
options,
|
|
size = 'lg',
|
|
invalid = false,
|
|
loading = false,
|
|
disabled = false,
|
|
listMaxHeight = '28rem',
|
|
popoverMinWidth,
|
|
popoverMaxWidth,
|
|
contentStyle
|
|
} = defineProps<{
|
|
label?: string
|
|
options?: SelectOption[]
|
|
/** Trigger size: 'lg' (40px, Interface) or 'md' (32px, Node) */
|
|
size?: 'lg' | 'md'
|
|
/** Show invalid (destructive) border */
|
|
invalid?: boolean
|
|
/** Show loading spinner instead of chevron */
|
|
loading?: boolean
|
|
/** Disable the select */
|
|
disabled?: boolean
|
|
/** Maximum height of the dropdown panel (default: 28rem) */
|
|
listMaxHeight?: string
|
|
/** Minimum width of the popover (default: auto) */
|
|
popoverMinWidth?: string
|
|
/** Maximum width of the popover (default: auto) */
|
|
popoverMaxWidth?: string
|
|
contentStyle?: StyleValue
|
|
}>()
|
|
|
|
const selectedItem = defineModel<string | undefined>({ required: true })
|
|
|
|
const { t } = useI18n()
|
|
const isOpen = ref(false)
|
|
|
|
function onContentKeydown(event: KeyboardEvent) {
|
|
if (event.key === 'Escape') {
|
|
stopEscapeToDocument(event)
|
|
isOpen.value = false
|
|
}
|
|
}
|
|
|
|
const optionStyle = usePopoverSizing({
|
|
minWidth: popoverMinWidth,
|
|
maxWidth: popoverMaxWidth
|
|
})
|
|
</script>
|