feat: switcher

This commit is contained in:
2026-03-16 00:35:05 +01:00
parent 7c7d470f15
commit e64e29d773
3 changed files with 38 additions and 23 deletions

View File

@@ -9,7 +9,6 @@ import { RecollectionMenubarProvider } from './RecollectionMenubarContext'
import { RecollectionActionsProvider } from './RecollectionActionsContext' import { RecollectionActionsProvider } from './RecollectionActionsContext'
import { RecollectionTitleContent } from './RecollectionTitleContent' import { RecollectionTitleContent } from './RecollectionTitleContent'
import { RecollectionMenubar } from './RecollectionMenubar' import { RecollectionMenubar } from './RecollectionMenubar'
import { RecollectionViewSwitcher } from './RecollectionViewSwitcher'
import { FlippingCardView } from './FlippingCardView' import { FlippingCardView } from './FlippingCardView'
export function RecollectionLayout() { export function RecollectionLayout() {
@@ -38,12 +37,11 @@ export function RecollectionLayout() {
<RecollectionMenubarProvider> <RecollectionMenubarProvider>
<RecollectionActionsProvider> <RecollectionActionsProvider>
<RecollectionTitleContent /> <RecollectionTitleContent />
<div className="relative flex min-h-0 flex-1 flex-col"> <div className="flex min-h-0 flex-1 flex-col">
<RecollectionMenubar /> <RecollectionMenubar />
<div className="flex min-h-0 flex-1 flex-col overflow-hidden"> <div className="flex min-h-0 flex-1 flex-col overflow-hidden">
<FlippingCardView /> <FlippingCardView />
</div> </div>
<RecollectionViewSwitcher />
</div> </div>
</RecollectionActionsProvider> </RecollectionActionsProvider>
</RecollectionMenubarProvider> </RecollectionMenubarProvider>

View File

@@ -8,6 +8,7 @@ import { usePlatform } from '@/app/kosmos/KosmosContext'
import { ArrowLeft } from 'lucide-react' import { ArrowLeft } from 'lucide-react'
import { useRecollectionMenubar } from './RecollectionMenubarContext' import { useRecollectionMenubar } from './RecollectionMenubarContext'
import { RecollectionEditViewMenus } from './RecollectionEditViewMenus' import { RecollectionEditViewMenus } from './RecollectionEditViewMenus'
import { RecollectionViewSwitcher } from './RecollectionViewSwitcher'
export function RecollectionMenubar() { export function RecollectionMenubar() {
const { recollectionId } = useParams<{ recollectionId: string }>() const { recollectionId } = useParams<{ recollectionId: string }>()
@@ -34,6 +35,7 @@ export function RecollectionMenubar() {
<div className="flex min-w-0 flex-1 items-center gap-1 overflow-visible"> <div className="flex min-w-0 flex-1 items-center gap-1 overflow-visible">
<RecollectionEditViewMenus /> <RecollectionEditViewMenus />
</div> </div>
<RecollectionViewSwitcher />
</div> </div>
) )
} }

View File

@@ -1,14 +1,12 @@
/** /**
* Elevator-style view switcher: circular button moves between top (Logos) and bottom (Flux). * Circular view switcher: icon and title use the same vertical elevator-style animation.
* Click toggles; shortcut and tooltip on hover. * Click toggles; tooltip includes shortcut.
*/ */
import React, { useCallback, useEffect } from 'react' import React, { useCallback, useEffect } from 'react'
import { useLocation, useNavigate, useParams } from 'react-router-dom' import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { usePlatform } from '@/app/kosmos/KosmosContext'
import { FluxIcon, LogosIcon } from '@/lib/icons' import { FluxIcon, LogosIcon } from '@/lib/icons'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { Kbd } from '@/components/ui/kbd'
const VIEW_TOGGLE_KEYS = { key: 'v', shiftKey: true } const VIEW_TOGGLE_KEYS = { key: 'v', shiftKey: true }
@@ -21,7 +19,7 @@ function shortcutLabel() {
if (typeof navigator !== 'undefined' && /Mac|iPod|iPhone|iPad/.test(navigator.platform)) { if (typeof navigator !== 'undefined' && /Mac|iPod|iPhone|iPad/.test(navigator.platform)) {
return '⌘⇧V' return '⌘⇧V'
} }
return 'Ctrl++V' return 'Ctrl+Shift+V'
} }
export function RecollectionViewSwitcher() { export function RecollectionViewSwitcher() {
@@ -50,12 +48,13 @@ export function RecollectionViewSwitcher() {
if (!base) return null if (!base) return null
const shortcut = shortcutLabel()
const tooltipText = isFlux const tooltipText = isFlux
? `Switch to Logos` ? `Flux · Switch to Logos (${shortcut})`
: `Switch to Flux` : `Logos · Switch to Flux (${shortcut})`
return ( return (
<div className="absolute left-1/2 top-[18px] z-10 flex -translate-x-1/2 -translate-y-1/2 items-center gap-2"> <div className="flex shrink-0 items-center gap-2">
<TooltipProvider delayDuration={300}> <TooltipProvider delayDuration={300}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
@@ -63,26 +62,42 @@ export function RecollectionViewSwitcher() {
type="button" type="button"
onClick={toggle} onClick={toggle}
aria-label={isFlux ? 'Switch to Logos' : 'Switch to Flux'} aria-label={isFlux ? 'Switch to Logos' : 'Switch to Flux'}
className="flex items-center gap-2 rounded-full border border-border/60 bg-background shadow-md transition-colors hover:bg-accent hover:text-accent-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring" className="h-7 w-7 shrink-0 overflow-hidden rounded-full border border-border/60 bg-background shadow-md transition-colors hover:bg-accent hover:text-accent-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
> >
<div className="relative h-14 w-8 rounded-full bg-muted/50 p-0.5"> <div className="h-full w-full overflow-hidden">
<span <div
className="absolute left-1/2 flex h-6 w-6 -translate-x-1/2 items-center justify-center rounded-full border bg-background shadow-sm transition-[top] duration-200 ease-out" className="flex w-full flex-col transition-transform duration-200 ease-out"
style={{ top: isFlux ? 28 : 4 }} style={{
height: '200%',
transform: isFlux ? 'translateY(-50%)' : 'translateY(0)',
}}
> >
{isFlux ? ( <div className="flex h-9 w-full items-center justify-center">
<FluxIcon className="size-3.5 text-muted-foreground" /> <LogosIcon className="size-3.5 shrink-0 text-muted-foreground" />
) : ( </div>
<LogosIcon className="size-3.5 text-muted-foreground" /> <div className="flex h-9 w-full items-center justify-center">
)} <FluxIcon className="size-3.5 shrink-0 text-muted-foreground" />
</span> </div>
</div>
</div> </div>
</button> </button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="bottom">{tooltipText}</TooltipContent> <TooltipContent side="bottom">{tooltipText}</TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
<Kbd className="text-[10px]">{shortcutLabel()}</Kbd> <div className="h-9 min-w-[3.5rem] overflow-hidden">
<div
className="flex w-full flex-col transition-transform duration-200 ease-out"
style={{ transform: isFlux ? 'translateY(-50%)' : 'translateY(0)' }}
>
<span className="flex h-9 flex-shrink-0 items-center text-sm font-medium text-foreground">
Logos
</span>
<span className="flex h-9 flex-shrink-0 items-center text-sm font-medium text-foreground">
Flux
</span>
</div>
</div>
</div> </div>
) )
} }