fix(app): move vertical tab update button to footer (#48030)

Co-authored-by: LukeParkerDev <10430890+Hona@users.noreply.github.com>
This commit is contained in:
David Hill
2026-09-08 17:41:10 -06:00
committed by GitHub
Unverified
parent d24f8b0810
commit 37b6fbc5bf
+37 -13
View File
@@ -679,8 +679,11 @@ export function Titlebar(props: {
onReorder={(keys) => tabsStoreActions.reorder(keys)}
/>
</div>
<div data-slot="vertical-tabs-footer" class="mt-2 flex w-full shrink-0 flex-col">
<div data-slot="vertical-tabs-footer" class="mt-2 flex w-full shrink-0 flex-col gap-2">
<TitlebarRightMount vertical />
<Show when={updateState().visible}>
<TitlebarUpdateIconButton state={updateState()} vertical />
</Show>
</div>
</Portal>
)}
@@ -691,7 +694,9 @@ export function Titlebar(props: {
<Show when={!mobile()}>
<div class="flex-1" />
</Show>
<TitlebarRight state={rightState()} mount={!props.verticalTabs} />
<Show when={!props.verticalTabs}>
<TitlebarRight state={rightState()} />
</Show>
</div>
)
}}
@@ -714,34 +719,52 @@ type TitlebarRightState = {
update: TitlebarUpdatePillState
}
function TitlebarRight(props: { state: TitlebarRightState; mount?: boolean }) {
function TitlebarRight(props: { state: TitlebarRightState }) {
return (
<div class="relative z-20 flex shrink-0 items-center justify-end gap-0 overflow-visible">
<Show when={props.state.update.visible}>
<TitlebarUpdateIconButton state={props.state.update} />
</Show>
<Show when={props.mount !== false}>
<TitlebarRightMount />
</Show>
<TitlebarRightMount />
</div>
)
}
function TitlebarUpdateIconButton(props: { state: TitlebarUpdatePillState }) {
function TitlebarUpdateIconButton(props: { state: TitlebarUpdatePillState; vertical?: boolean }) {
const label = () => (
<span
class="shrink-0 text-[11px] leading-4 text-v2-text-text-accent [font-weight:530] opacity-0 motion-safe:transition-all duration-150 ease-out group-hover:opacity-100 group-hover:translate-x-0 group-focus-within:opacity-100 group-focus-within:translate-x-0 motion-reduce:translate-x-0"
classList={{
"ms-px me-4 -translate-x-2 rtl:translate-x-2": props.vertical,
"ms-2 me-px translate-x-2 rtl:-translate-x-2": !props.vertical,
}}
>
{props.state.label}
</span>
)
return (
<div class="group relative mr-3 h-5 w-5 shrink-0 rounded-full bg-v2-background-bg-deep transition-[width] duration-150 ease-out hover:z-30 hover:w-[68px] focus-within:z-30 focus-within:w-[68px] motion-reduce:transition-none">
<div
data-slot="titlebar-update"
class="group relative shrink-0 rounded-full bg-v2-background-bg-deep transition-[width] duration-150 ease-out hover:z-30 focus-within:z-30 motion-reduce:transition-none"
classList={{
"h-7 w-7 self-start hover:w-[84px] focus-within:w-[84px]": props.vertical,
"me-3 h-5 w-5 hover:w-[68px] focus-within:w-[68px]": !props.vertical,
}}
>
<button
type="button"
class="absolute right-0 top-0 z-10 flex h-5 w-5 items-center justify-end overflow-hidden rounded-full bg-v2-icon-icon-accent/20 text-v2-icon-icon-accent transition-[width,background-color] duration-150 ease-out group-hover:w-[68px] group-hover:bg-[color-mix(in_srgb,var(--v2-icon-icon-accent)_20%,var(--v2-background-bg-deep))] group-focus-within:w-[68px] group-focus-within:bg-[color-mix(in_srgb,var(--v2-icon-icon-accent)_20%,var(--v2-background-bg-deep))] focus-visible:outline-none disabled:opacity-60 motion-reduce:transition-none"
class="absolute top-0 z-10 flex h-full w-full items-center overflow-hidden rounded-full bg-v2-icon-icon-accent/20 text-v2-icon-icon-accent transition-[background-color] duration-150 ease-out group-hover:bg-[color-mix(in_srgb,var(--v2-icon-icon-accent)_20%,var(--v2-background-bg-deep))] group-focus-within:bg-[color-mix(in_srgb,var(--v2-icon-icon-accent)_20%,var(--v2-background-bg-deep))] focus-visible:outline-none disabled:opacity-60 motion-reduce:transition-none [app-region:no-drag]"
classList={{ "start-0 justify-start": props.vertical, "end-0 justify-end": !props.vertical }}
onClick={props.state.onInstall}
disabled={props.state.installing}
aria-busy={props.state.installing}
aria-label={props.state.ariaLabel}
>
<span class="shrink-0 ml-[8px] mr-px text-[11px] text-v2-text-text-accent [font-weight:530] opacity-0 translate-x-2 motion-safe:transition-all duration-150 ease-out group-hover:opacity-100 group-hover:translate-x-0 group-focus-within:opacity-100 group-focus-within:translate-x-0 motion-reduce:translate-x-0">
{props.state.label}
</span>
<span class="flex size-5 shrink-0 items-center justify-center">
<Show when={!props.vertical}>{label()}</Show>
<span
class="flex shrink-0 items-center justify-center"
classList={{ "size-7": props.vertical, "size-5": !props.vertical }}
>
<Show
when={!props.state.installing}
fallback={<span data-slot="titlebar-update-loader" aria-hidden="true" />}
@@ -751,6 +774,7 @@ function TitlebarUpdateIconButton(props: { state: TitlebarUpdatePillState }) {
</svg>
</Show>
</span>
<Show when={props.vertical}>{label()}</Show>
</button>
</div>
)