🐛 fix(vscode): 下拉按最长项加宽,避免 profile 名被截断
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"publisher": "clurdra",
|
"publisher": "clurdra",
|
||||||
"name": "superpowers-vscode-clurdra",
|
"name": "superpowers-vscode-clurdra",
|
||||||
"displayName": "Superpowers-clurdra",
|
"displayName": "Superpowers-clurdra",
|
||||||
"version": "0.2.97",
|
"version": "0.2.98",
|
||||||
"packageManager": "pnpm@10.27.0",
|
"packageManager": "pnpm@10.27.0",
|
||||||
"description": "Superpowers specs and plans Kanban explorer",
|
"description": "Superpowers specs and plans Kanban explorer",
|
||||||
"author": "clurdra",
|
"author": "clurdra",
|
||||||
|
|||||||
@@ -145,15 +145,16 @@ export function ManagedSessionsPanel({
|
|||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="w-12 shrink-0 text-xs text-[var(--vscode-descriptionForeground)]">Profile</span>
|
<span className="w-12 shrink-0 text-xs text-[var(--vscode-descriptionForeground)]">Profile</span>
|
||||||
<SelectMenu
|
<div className="min-w-0 flex-1">
|
||||||
value={selectedProfile}
|
<SelectMenu
|
||||||
onChange={setSelectedProfile}
|
value={selectedProfile}
|
||||||
options={profiles.length === 0
|
onChange={setSelectedProfile}
|
||||||
? [{ value: '', label: '(无可用 profile)' }]
|
options={profiles.length === 0
|
||||||
: profiles.map(p => ({ value: p.path, label: p.name }))}
|
? [{ value: '', label: '(无可用 profile)' }]
|
||||||
variant="input"
|
: profiles.map(p => ({ value: p.path, label: p.name }))}
|
||||||
className="min-w-0 flex-1"
|
variant="input"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="w-12 shrink-0 text-xs text-[var(--vscode-descriptionForeground)]">名字</span>
|
<span className="w-12 shrink-0 text-xs text-[var(--vscode-descriptionForeground)]">名字</span>
|
||||||
|
|||||||
@@ -22,6 +22,25 @@ export interface SelectMenuProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MAX_MENU_HEIGHT = 240
|
const MAX_MENU_HEIGHT = 240
|
||||||
|
/** 勾选图标 + gap + 水平 padding + 竖滚动条槽。少算会把最后几个字挤成省略号。 */
|
||||||
|
const MENU_CHROME_PX = 14 + 6 + 16 + 12
|
||||||
|
|
||||||
|
function measureOptionsWidth(options: SelectMenuOption[], font: string): number {
|
||||||
|
const probe = document.createElement('span')
|
||||||
|
probe.style.cssText = `position:absolute;left:-9999px;top:0;white-space:nowrap;font:${font}`
|
||||||
|
document.body.appendChild(probe)
|
||||||
|
let max = 0
|
||||||
|
for (const o of options) {
|
||||||
|
probe.textContent = o.label
|
||||||
|
max = Math.max(max, probe.offsetWidth)
|
||||||
|
if (o.hint) {
|
||||||
|
probe.textContent = o.hint
|
||||||
|
max = Math.max(max, probe.offsetWidth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
probe.remove()
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义下拉组件,替代原生 select。
|
* 自定义下拉组件,替代原生 select。
|
||||||
@@ -57,12 +76,21 @@ export function SelectMenu({
|
|||||||
const spaceBelow = window.innerHeight - rect.bottom
|
const spaceBelow = window.innerHeight - rect.bottom
|
||||||
const spaceAbove = rect.top
|
const spaceAbove = rect.top
|
||||||
const openUp = spaceBelow < menuHeight && spaceAbove > spaceBelow
|
const openUp = spaceBelow < menuHeight && spaceAbove > spaceBelow
|
||||||
|
// 跟触发器同宽会把长 profile 名截成省略号(ghost / 未撑满的 button 尤其明显)。
|
||||||
|
// 按最长项测量,下限触发器宽、上限视口,溢出时左移,不超出 webview。
|
||||||
|
const gutter = 8
|
||||||
|
const contentW = measureOptionsWidth(options, getComputedStyle(el).font) + MENU_CHROME_PX
|
||||||
|
const maxWidth = window.innerWidth - gutter * 2
|
||||||
|
const width = Math.min(maxWidth, Math.max(rect.width, contentW))
|
||||||
|
let left = rect.left
|
||||||
|
if (left + width > window.innerWidth - gutter)
|
||||||
|
left = Math.max(gutter, window.innerWidth - gutter - width)
|
||||||
setPos({
|
setPos({
|
||||||
top: openUp ? Math.max(8, rect.top - menuHeight - 4) : rect.bottom + 4,
|
top: openUp ? Math.max(8, rect.top - menuHeight - 4) : rect.bottom + 4,
|
||||||
left: rect.left,
|
left,
|
||||||
width: Math.max(rect.width, 120),
|
width,
|
||||||
})
|
})
|
||||||
}, [options.length])
|
}, [options])
|
||||||
|
|
||||||
const openMenu = useCallback((): void => {
|
const openMenu = useCallback((): void => {
|
||||||
computePos()
|
computePos()
|
||||||
@@ -181,7 +209,7 @@ export function SelectMenu({
|
|||||||
|
|
||||||
const triggerClasses
|
const triggerClasses
|
||||||
= variant === 'input'
|
= variant === 'input'
|
||||||
? 'rounded border border-[var(--vscode-input-border,var(--vscode-panel-border))] bg-[var(--vscode-input-background)] px-2 py-1 text-xs text-[var(--vscode-input-foreground)] focus:ring-1 focus:ring-inset focus:ring-[var(--vscode-focusBorder)]'
|
? 'w-full rounded border border-[var(--vscode-input-border,var(--vscode-panel-border))] bg-[var(--vscode-input-background)] px-2 py-1 text-xs text-[var(--vscode-input-foreground)] focus:ring-1 focus:ring-inset focus:ring-[var(--vscode-focusBorder)]'
|
||||||
: 'rounded bg-transparent px-1 py-0 text-[10px] text-[var(--vscode-descriptionForeground)] hover:bg-[var(--vscode-toolbar-hoverBackground)]'
|
: 'rounded bg-transparent px-1 py-0 text-[10px] text-[var(--vscode-descriptionForeground)] hover:bg-[var(--vscode-toolbar-hoverBackground)]'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -202,7 +230,7 @@ export function SelectMenu({
|
|||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className={cn('min-w-0 truncate', selected ? '' : 'opacity-50')}>
|
<span className={cn('min-w-0 flex-1 truncate', selected ? '' : 'opacity-50')}>
|
||||||
{selected?.label ?? placeholder}
|
{selected?.label ?? placeholder}
|
||||||
</span>
|
</span>
|
||||||
<ChevronDown className={cn('size-3.5 shrink-0', variant === 'ghost' && 'size-3')} />
|
<ChevronDown className={cn('size-3.5 shrink-0', variant === 'ghost' && 'size-3')} />
|
||||||
@@ -244,8 +272,8 @@ export function SelectMenu({
|
|||||||
<Check className="size-3.5" />
|
<Check className="size-3.5" />
|
||||||
</span>
|
</span>
|
||||||
<span className="flex min-w-0 flex-col">
|
<span className="flex min-w-0 flex-col">
|
||||||
<span className="truncate">{opt.label}</span>
|
<span className="truncate" title={opt.label}>{opt.label}</span>
|
||||||
{opt.hint && <span className="truncate text-[10px] opacity-70">{opt.hint}</span>}
|
{opt.hint && <span className="truncate text-[10px] opacity-70" title={opt.hint}>{opt.hint}</span>}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user