🐛 fix(vscode): 下拉按最长项加宽,避免 profile 名被截断
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
||||
"publisher": "clurdra",
|
||||
"name": "superpowers-vscode-clurdra",
|
||||
"displayName": "Superpowers-clurdra",
|
||||
"version": "0.2.97",
|
||||
"version": "0.2.98",
|
||||
"packageManager": "pnpm@10.27.0",
|
||||
"description": "Superpowers specs and plans Kanban explorer",
|
||||
"author": "clurdra",
|
||||
|
||||
@@ -145,15 +145,16 @@ export function ManagedSessionsPanel({
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-12 shrink-0 text-xs text-[var(--vscode-descriptionForeground)]">Profile</span>
|
||||
<SelectMenu
|
||||
value={selectedProfile}
|
||||
onChange={setSelectedProfile}
|
||||
options={profiles.length === 0
|
||||
? [{ value: '', label: '(无可用 profile)' }]
|
||||
: profiles.map(p => ({ value: p.path, label: p.name }))}
|
||||
variant="input"
|
||||
className="min-w-0 flex-1"
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<SelectMenu
|
||||
value={selectedProfile}
|
||||
onChange={setSelectedProfile}
|
||||
options={profiles.length === 0
|
||||
? [{ value: '', label: '(无可用 profile)' }]
|
||||
: profiles.map(p => ({ value: p.path, label: p.name }))}
|
||||
variant="input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<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
|
||||
/** 勾选图标 + 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。
|
||||
@@ -57,12 +76,21 @@ export function SelectMenu({
|
||||
const spaceBelow = window.innerHeight - rect.bottom
|
||||
const spaceAbove = rect.top
|
||||
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({
|
||||
top: openUp ? Math.max(8, rect.top - menuHeight - 4) : rect.bottom + 4,
|
||||
left: rect.left,
|
||||
width: Math.max(rect.width, 120),
|
||||
left,
|
||||
width,
|
||||
})
|
||||
}, [options.length])
|
||||
}, [options])
|
||||
|
||||
const openMenu = useCallback((): void => {
|
||||
computePos()
|
||||
@@ -181,7 +209,7 @@ export function SelectMenu({
|
||||
|
||||
const triggerClasses
|
||||
= 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)]'
|
||||
|
||||
return (
|
||||
@@ -202,7 +230,7 @@ export function SelectMenu({
|
||||
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}
|
||||
</span>
|
||||
<ChevronDown className={cn('size-3.5 shrink-0', variant === 'ghost' && 'size-3')} />
|
||||
@@ -244,8 +272,8 @@ export function SelectMenu({
|
||||
<Check className="size-3.5" />
|
||||
</span>
|
||||
<span className="flex min-w-0 flex-col">
|
||||
<span className="truncate">{opt.label}</span>
|
||||
{opt.hint && <span className="truncate text-[10px] opacity-70">{opt.hint}</span>}
|
||||
<span className="truncate" title={opt.label}>{opt.label}</span>
|
||||
{opt.hint && <span className="truncate text-[10px] opacity-70" title={opt.hint}>{opt.hint}</span>}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user