fix: keep saved view reorder UI native

This commit is contained in:
lucaronin
2026-04-29 11:51:22 +02:00
parent 6ee7f219f5
commit 792b3009dd
8 changed files with 15 additions and 105 deletions

View File

@@ -1384,31 +1384,20 @@ describe('Sidebar', () => {
},
]
it('renders keyboard-accessible move buttons for saved views', () => {
const onMoveView = vi.fn()
it('does not add visible reorder controls to saved view rows', () => {
render(
<Sidebar
entries={mockEntries}
selection={defaultSelection}
onSelect={() => {}}
views={mockViews}
onMoveView={onMoveView}
onReorderViews={vi.fn()}
/>
)
const moveUpButtons = screen.getAllByTitle('Move view up')
const moveDownButtons = screen.getAllByTitle('Move view down')
expect(moveUpButtons[0]).toBeDisabled()
expect(moveUpButtons[1]).not.toBeDisabled()
expect(moveDownButtons[0]).not.toBeDisabled()
expect(moveDownButtons[1]).toBeDisabled()
fireEvent.click(moveUpButtons[1])
expect(onMoveView).toHaveBeenCalledWith('all-topics.yml', 'up')
fireEvent.click(moveDownButtons[0])
expect(onMoveView).toHaveBeenCalledWith('active-projects.yml', 'down')
expect(screen.queryByTitle('Move view up')).not.toBeInTheDocument()
expect(screen.queryByTitle('Move view down')).not.toBeInTheDocument()
expect(screen.queryByTitle('Reorder view')).not.toBeInTheDocument()
})
it('shows note count chip for each view matching the filter results', () => {

View File

@@ -24,7 +24,6 @@ import {
import { useSidebarTypeInteractions } from './sidebar/useSidebarTypeInteractions'
import type { AppLocale } from '../lib/i18n'
import type { FolderFileActions } from '../hooks/useFileActions'
import type { ViewMoveDirection } from '../utils/viewOrdering'
interface SidebarProps {
entries: VaultEntry[]
@@ -45,7 +44,6 @@ interface SidebarProps {
onEditView?: (filename: string) => void
onDeleteView?: (filename: string) => void
onReorderViews?: (orderedFilenames: string[]) => void
onMoveView?: (filename: string, direction: ViewMoveDirection) => void
folders?: FolderNode[]
onCreateFolder?: (name: string) => Promise<boolean> | boolean
onRenameFolder?: (folderPath: string, nextName: string) => Promise<boolean> | boolean
@@ -72,7 +70,6 @@ interface SidebarNavigationProps extends Pick<
| 'onEditView'
| 'onDeleteView'
| 'onReorderViews'
| 'onMoveView'
| 'folders'
| 'onCreateFolder'
| 'onRenameFolder'
@@ -112,7 +109,6 @@ function SidebarNavigation({
onEditView,
onDeleteView,
onReorderViews,
onMoveView,
folders = [],
onCreateFolder,
onRenameFolder,
@@ -178,7 +174,6 @@ function SidebarNavigation({
onEditView={onEditView}
onDeleteView={onDeleteView}
onReorderViews={onReorderViews}
onMoveView={onMoveView}
sensors={sensors}
entries={entries}
locale={locale}
@@ -243,7 +238,6 @@ export const Sidebar = memo(function Sidebar({
onEditView,
onDeleteView,
onReorderViews,
onMoveView,
folders = [],
onCreateFolder,
onRenameFolder,
@@ -280,7 +274,7 @@ export const Sidebar = memo(function Sidebar({
const reordered = computeReorder(sectionIds, active.id as string, over.id as string)
if (reordered) onReorderSections?.(reordered.map((typeName, order) => ({ typeName, order })))
}, [sectionIds, onReorderSections])
const viewActions = { onCreateView, onEditView, onDeleteView, onReorderViews, onMoveView }
const viewActions = { onCreateView, onEditView, onDeleteView, onReorderViews }
const sectionProps: SidebarSectionProps = {
entries,

View File

@@ -24,7 +24,6 @@ import { SidebarViewItem } from './SidebarViewItem'
import { computeReorder } from './sidebarHooks'
import { countByFilter } from '../../utils/noteListHelpers'
import { translate, type AppLocale } from '../../lib/i18n'
import { canMoveView, type ViewMoveDirection } from '../../utils/viewOrdering'
export { SidebarTopNav } from './SidebarTopNav'
export { FavoritesSection } from './FavoritesSection'
@@ -51,7 +50,6 @@ export function ViewsSection({
onEditView,
onDeleteView,
onReorderViews,
onMoveView,
sensors,
entries,
locale = 'en',
@@ -65,7 +63,6 @@ export function ViewsSection({
onEditView?: (filename: string) => void
onDeleteView?: (filename: string) => void
onReorderViews?: (orderedFilenames: string[]) => void
onMoveView?: (filename: string, direction: ViewMoveDirection) => void
sensors: ReturnType<typeof useSensors>
entries: VaultEntry[]
locale?: AppLocale
@@ -85,9 +82,6 @@ export function ViewsSection({
onSelect={() => onSelect({ kind: 'view', filename: view.filename })}
onEditView={onEditView}
onDeleteView={onDeleteView}
onMoveView={onMoveView}
canMoveUp={canMoveView(views, view.filename, 'up')}
canMoveDown={canMoveView(views, view.filename, 'down')}
entries={entries}
locale={locale}
/>
@@ -119,12 +113,10 @@ export function ViewsSection({
<SortableViewItem
key={view.filename}
view={view}
views={views}
selection={selection}
onSelect={onSelect}
onEditView={onEditView}
onDeleteView={onDeleteView}
onMoveView={onMoveView}
entries={entries}
locale={locale}
/>
@@ -140,22 +132,18 @@ export function ViewsSection({
function SortableViewItem({
view,
views,
selection,
onSelect,
onEditView,
onDeleteView,
onMoveView,
entries,
locale,
}: {
view: ViewFile
views: ViewFile[]
selection: SidebarSelection
onSelect: (selection: SidebarSelection) => void
onEditView?: (filename: string) => void
onDeleteView?: (filename: string) => void
onMoveView?: (filename: string, direction: ViewMoveDirection) => void
entries: VaultEntry[]
locale?: AppLocale
}) {
@@ -164,6 +152,7 @@ function SortableViewItem({
return (
<div
ref={setNodeRef}
{...attributes}
style={{
transform: CSS.Transform.toString(transform),
transition,
@@ -176,10 +165,7 @@ function SortableViewItem({
onSelect={() => onSelect({ kind: 'view', filename: view.filename })}
onEditView={onEditView}
onDeleteView={onDeleteView}
onMoveView={onMoveView}
canMoveUp={canMoveView(views, view.filename, 'up')}
canMoveDown={canMoveView(views, view.filename, 'down')}
dragHandleProps={{ ...attributes, ...listeners }}
dragHandleProps={listeners}
entries={entries}
locale={locale}
/>

View File

@@ -2,13 +2,11 @@ import { useMemo, type HTMLAttributes } from 'react'
import type { VaultEntry, ViewFile } from '../../types'
import { evaluateView } from '../../utils/viewFilters'
import { Funnel, PencilSimple, Trash } from '@phosphor-icons/react'
import { ArrowDown, ArrowUp, GripVertical } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { NoteTitleIcon } from '../NoteTitleIcon'
import { SidebarCountPill } from '../SidebarParts'
import { SIDEBAR_ITEM_PADDING } from './sidebarStyles'
import { translate, type AppLocale } from '../../lib/i18n'
import type { ViewMoveDirection } from '../../utils/viewOrdering'
import { ACCENT_COLORS } from '../../utils/typeColors'
interface ViewAccent {
@@ -22,10 +20,7 @@ interface SidebarViewItemProps {
onSelect: () => void
onEditView?: (filename: string) => void
onDeleteView?: (filename: string) => void
onMoveView?: (filename: string, direction: ViewMoveDirection) => void
canMoveUp?: boolean
canMoveDown?: boolean
dragHandleProps?: HTMLAttributes<HTMLButtonElement>
dragHandleProps?: HTMLAttributes<HTMLDivElement>
entries: VaultEntry[]
locale?: AppLocale
}
@@ -88,9 +83,6 @@ export function SidebarViewItem({
onSelect,
onEditView,
onDeleteView,
onMoveView,
canMoveUp = false,
canMoveDown = false,
dragHandleProps,
entries,
locale = 'en',
@@ -105,54 +97,13 @@ export function SidebarViewItem({
className={`flex cursor-pointer select-none items-center gap-2 rounded transition-colors ${isActive ? 'bg-primary/10 text-primary' : 'text-foreground hover:bg-accent'}`}
style={getViewRowStyle(showCount, isActive, accent)}
onClick={onSelect}
{...dragHandleProps}
>
{dragHandleProps && (
<Button
type="button"
variant="ghost"
size="icon-xs"
className="-ml-1 h-5 w-4 min-w-0 cursor-grab rounded p-0 text-muted-foreground hover:bg-transparent hover:text-foreground active:cursor-grabbing"
title={translate(locale, 'sidebar.action.reorderView')}
aria-label={translate(locale, 'sidebar.action.reorderView')}
onClick={(event) => event.stopPropagation()}
{...dragHandleProps}
>
<GripVertical size={12} />
</Button>
)}
<ViewIcon icon={view.definition.icon} isActive={isActive} accent={accent} />
<span className="min-w-0 flex-1 truncate text-[13px] font-medium">{view.definition.name}</span>
<ViewCountChip count={count} isActive={isActive} accent={accent} />
</div>
<div className="pointer-events-none absolute right-2 top-1/2 flex -translate-y-1/2 items-center gap-0.5 opacity-0 transition-opacity group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100">
{onMoveView && (
<>
<Button
type="button"
variant="ghost"
size="icon-xs"
className="h-5 w-5 min-w-0 rounded p-0 text-muted-foreground hover:bg-transparent hover:text-foreground disabled:opacity-30"
disabled={!canMoveUp}
onClick={(event) => { event.stopPropagation(); onMoveView(view.filename, 'up') }}
title={translate(locale, 'sidebar.action.moveViewUp')}
aria-label={translate(locale, 'sidebar.action.moveViewUp')}
>
<ArrowUp size={12} />
</Button>
<Button
type="button"
variant="ghost"
size="icon-xs"
className="h-5 w-5 min-w-0 rounded p-0 text-muted-foreground hover:bg-transparent hover:text-foreground disabled:opacity-30"
disabled={!canMoveDown}
onClick={(event) => { event.stopPropagation(); onMoveView(view.filename, 'down') }}
title={translate(locale, 'sidebar.action.moveViewDown')}
aria-label={translate(locale, 'sidebar.action.moveViewDown')}
>
<ArrowDown size={12} />
</Button>
</>
)}
{onEditView && (
<Button
type="button"