Skip to content

Commit 27b94fe

Browse files
committed
v2.51 - FIx Expandable @
1 parent c1f132e commit 27b94fe

1 file changed

Lines changed: 31 additions & 8 deletions

File tree

frontend/src/components/ui/ContentTypePicker.tsx

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client"
22

3-
import { memo, useState, useRef, useEffect, useCallback, useMemo } from "react"
3+
import { memo, useState, useRef, useEffect, useLayoutEffect, useCallback, useMemo } from "react"
4+
import { createPortal } from "react-dom"
45
import {
56
CONTENT_TYPE_OPTIONS,
67
CONTENT_TYPE_ICONS,
@@ -53,6 +54,7 @@ const ContentTypePicker = memo(function ContentTypePicker({
5354
const triggeredRef = useRef(false)
5455
const itemRefs = useRef<Map<number, HTMLButtonElement>>(new Map())
5556
const submenuTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
57+
const submenuAnchorRef = useRef<HTMLDivElement | null>(null)
5658

5759
const hasSelection = selected.length > 0
5860

@@ -377,6 +379,7 @@ const ContentTypePicker = memo(function ContentTypePicker({
377379
return (
378380
<div
379381
key={t.id}
382+
ref={activeSubmenu === t.id ? submenuAnchorRef : undefined}
380383
style={{ position: "relative" }}
381384
onMouseEnter={() => {
382385
setHighlightIdx(idx)
@@ -469,9 +472,10 @@ const ContentTypePicker = memo(function ContentTypePicker({
469472
)}
470473
</button>
471474

472-
{/* Sub-menu for variants */}
475+
{/* Sub-menu for variants (rendered in portal so it's not clipped by overflow) */}
473476
{hasVariants && activeSubmenu === t.id && (
474477
<VariantSubmenu
478+
anchorRef={submenuAnchorRef}
475479
parent={t}
476480
variants={getFilteredVariants(t)}
477481
selected={parentSelected}
@@ -503,9 +507,12 @@ const ContentTypePicker = memo(function ContentTypePicker({
503507
)
504508
})
505509

506-
/* ── Variant Sub-menu ── */
510+
/* ── Variant Sub-menu (portal so it isn't clipped by list overflow) ── */
511+
512+
const SUBMENU_OFFSET = 4
507513

508514
const VariantSubmenu = memo(function VariantSubmenu({
515+
anchorRef,
509516
parent,
510517
variants,
511518
selected,
@@ -520,6 +527,7 @@ const VariantSubmenu = memo(function VariantSubmenu({
520527
popupBorder,
521528
itemHoverBg,
522529
}: {
530+
anchorRef: React.RefObject<HTMLDivElement | null>
523531
parent: ContentTypeOption
524532
variants: { id: string; label: string }[]
525533
selected: string[]
@@ -535,14 +543,26 @@ const VariantSubmenu = memo(function VariantSubmenu({
535543
popupBorder: string
536544
itemHoverBg: string
537545
}) {
538-
return (
546+
const [position, setPosition] = useState({ top: 0, left: 0 })
547+
548+
useLayoutEffect(() => {
549+
const anchor = anchorRef.current
550+
if (!anchor) return
551+
const rect = anchor.getBoundingClientRect()
552+
setPosition({
553+
top: rect.top,
554+
left: rect.right + SUBMENU_OFFSET,
555+
})
556+
}, [anchorRef])
557+
558+
const submenuEl = (
539559
<div
540560
onMouseEnter={onMouseEnter}
541561
onMouseLeave={onMouseLeave}
542562
style={{
543-
position: "absolute",
544-
left: "calc(100% + 4px)",
545-
top: 0,
563+
position: "fixed",
564+
top: position.top,
565+
left: position.left,
546566
background: popupBg,
547567
border: `1px solid ${popupBorder}`,
548568
borderRadius: "12px",
@@ -554,7 +574,7 @@ const VariantSubmenu = memo(function VariantSubmenu({
554574
boxShadow: isDark
555575
? "0 8px 30px rgba(0,0,0,0.5)"
556576
: "0 6px 24px rgba(0,0,0,0.12)",
557-
zIndex: 101,
577+
zIndex: 10002,
558578
}}
559579
>
560580
{/* Sub-menu header */}
@@ -620,6 +640,9 @@ const VariantSubmenu = memo(function VariantSubmenu({
620640
})}
621641
</div>
622642
)
643+
644+
if (typeof document === "undefined") return null
645+
return createPortal(submenuEl, document.body)
623646
})
624647

625648
export default ContentTypePicker

0 commit comments

Comments
 (0)