diff --git a/CHANGELOG.md b/CHANGELOG.md index 00c66f4..81e9cc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Diese Liste beschreibt in einfachen Worten, was sich von Version zu Version an T ### Geändert - Dashboard: Die Widgets bleiben beim Darüberfahren mit der Maus ruhig stehen, sie heben sich nicht mehr an. +- Dashboard: Die Widgets stehen in der Ansicht genau dort, wo Sie sie beim Bearbeiten platziert haben. Bisher rückte Tessera sie nach dem Bearbeiten zur Seitenmitte, sodass etwa ein einzelnes Widget oben links plötzlich in die Mitte sprang. - Dashboard: Das Raster ist in der Breite doppelt so fein – Widgets lassen sich in kleineren Schritten breiter oder schmaler ziehen und genauer platzieren. Bestehende Anordnungen bleiben unverändert. - Dashboard: Das Kalender-Widget lässt sich deutlich schmaler ziehen als bisher. diff --git a/apps/web/src/components/dashboard/dashboard-grid.test.tsx b/apps/web/src/components/dashboard/dashboard-grid.test.tsx index 08de3e2..f25a73d 100644 --- a/apps/web/src/components/dashboard/dashboard-grid.test.tsx +++ b/apps/web/src/components/dashboard/dashboard-grid.test.tsx @@ -2,7 +2,7 @@ import { Children, isValidElement } from 'react'; import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { stubResizeObserver } from '@/test/fake-resize-observer'; -import { centeringOffset, RESIZE_AXIS_FALLBACK } from './dashboard-grid'; +import { RESIZE_AXIS_FALLBACK } from './dashboard-grid'; // Mock CSS imports that vitest cannot resolve vi.mock('react-grid-layout/css/styles.css', () => ({})); @@ -539,12 +539,3 @@ describe('RESIZE_AXIS_FALLBACK (Design „Mosaik“, Runde 3)', () => { expect(RESIZE_AXIS_FALLBACK.constrainSize?.(item, 3, 9, 'se', context)).toEqual({ w: 3, h: 9 }); }); }); - -describe('centeringOffset (Design „Mosaik“, Runde 3)', () => { - it('stellt belegte Spalten mittig und laesst eine volle Anordnung stehen', () => { - // 48 Spalten auf 1212 px: Spalte 13 px + 12 px Abstand = 25 px je Spalte. - expect(centeringOffset([{ x: 0, w: 24 }], 48, 1212)).toBe(300); - expect(centeringOffset([{ x: 24, w: 24 }], 48, 1212)).toBe(0); - expect(centeringOffset([], 48, 1212)).toBe(0); - }); -}); diff --git a/apps/web/src/components/dashboard/dashboard-grid.tsx b/apps/web/src/components/dashboard/dashboard-grid.tsx index 581769a..eeda6ae 100644 --- a/apps/web/src/components/dashboard/dashboard-grid.tsx +++ b/apps/web/src/components/dashboard/dashboard-grid.tsx @@ -93,33 +93,6 @@ const GRID_CONSTRAINTS: LayoutConstraint[] = [...defaultConstraints, RESIZE_AXIS const GRID_MARGIN = 12; -/** - * Seitlicher Versatz, um die belegten Spalten in der Ansicht mittig zu - * stellen (Design „Mosaik“, Runde 3): auf breiten Bildschirmen blieb rechts - * ein leerer Streifen, weil die Kacheln links beginnen. Belegt die Anordnung - * schon (fast) die ganze Breite, bleibt es bei 0. - */ -export function centeringOffset( - items: Array<{ x: number; w: number }> | undefined, - cols: number, - width: number, -): number { - if (!items || items.length === 0 || cols <= 0 || width <= 0) return 0; - const usedCols = Math.min(cols, Math.max(...items.map((l) => l.x + l.w))); - if (usedCols >= cols) return 0; - const colWidth = (width - GRID_MARGIN * (cols + 1)) / cols; - const freeWidth = (cols - usedCols) * (colWidth + GRID_MARGIN); - return Math.max(0, Math.floor(freeWidth / 2)); -} - -/** Breakpoint zur Breite — gleiche Regel wie react-grid-layout (strikt groesser). */ -function breakpointFor(width: number): keyof typeof COLS { - const order = (Object.keys(BREAKPOINTS) as Array).sort( - (a, b) => BREAKPOINTS[b] - BREAKPOINTS[a], - ); - return order.find((bp) => width > BREAKPOINTS[bp]) ?? 'xxs'; -} - interface LayoutItemShape { i: string; x: number; @@ -140,8 +113,6 @@ interface DashboardGridProps { onOpenCatalog?: () => void; /** Leerer Zustand: fuegt eine vorgeschlagene Kachel direkt hinzu. */ onQuickAdd?: (type: WidgetType) => void; - /** Meldet den seitlichen Versatz der zentrierten Ansicht (0 im Bearbeitungsmodus). */ - onInsetChange?: (px: number) => void; } /** Vorschlaege im leeren Zustand — Plattform-Kacheln, immer verfuegbar. */ @@ -210,7 +181,6 @@ export function DashboardGrid({ onRemoveWidget, onOpenCatalog, onQuickAdd, - onInsetChange, }: DashboardGridProps) { const t = useTranslations('widgets'); const [width, setWidth] = useState(1200); @@ -291,17 +261,6 @@ export function DashboardGrid({ // stabil bleibt. const effectiveLayouts = useMemo(() => applyConstraintMinima(layouts, widgets), [layouts, widgets]); - // Zentrieren nur in der Ansicht — im Bearbeitungsmodus braucht es das - // volle Raster, damit Kacheln auch in die freien Spalten rechts passen. - const breakpoint = breakpointFor(width); - const inset = isEditMode - ? 0 - : centeringOffset(effectiveLayouts[breakpoint] ?? effectiveLayouts.lg, COLS[breakpoint], width); - - useEffect(() => { - onInsetChange?.(widgets.length === 0 ? 0 : inset); - }, [inset, onInsetChange, widgets.length]); - // Leerer Zustand (D-02, Design „Mosaik“ Runde 2): freundlich ohne // Illustration — Titel, ein Satz, drei Vorschlaege zum direkten // Hinzufuegen und der Hauptknopf zum Katalog. @@ -347,14 +306,7 @@ export function DashboardGrid({ } return ( - // overflow-x-clip: die verschobene Rasterflaeche ragt rechts ueber den - // Rand; `clip` (nicht `hidden`) laesst die Schatten oben/unten stehen. -
-
0 ? { transform: `translateX(${inset}px)` } : undefined} - > +
-
); } diff --git a/docs/anleitung-anwender.md b/docs/anleitung-anwender.md index 43465b0..a959d30 100644 --- a/docs/anleitung-anwender.md +++ b/docs/anleitung-anwender.md @@ -58,7 +58,7 @@ Unten in der Seitenleiste begrüßt Tessera Sie je nach Tageszeit mit Ihrem Name ## Dashboard -Das Dashboard ist Ihre persönliche Startseite und öffnet sich automatisch nach der Anmeldung. Es zeigt ein Raster aus Kacheln — den **Widgets** —, das mittig auf der Seite steht. Jedes Widget trägt oben ein gelbes Symbol-Feld und blendet beim Laden sanft ein. Ist noch kein Widget platziert, sehen Sie den Hinweis „Ihr Dashboard ist noch leer" mit einigen Vorschlägen für erste Kacheln. +Das Dashboard ist Ihre persönliche Startseite und öffnet sich automatisch nach der Anmeldung. Es zeigt ein Raster aus Kacheln — den **Widgets** —, die genau dort stehen, wo Sie sie im Bearbeitungsmodus platziert haben. Jedes Widget trägt oben ein gelbes Symbol-Feld und blendet beim Laden sanft ein. Ist noch kein Widget platziert, sehen Sie den Hinweis „Ihr Dashboard ist noch leer" mit einigen Vorschlägen für erste Kacheln. **Mehrere Dashboards (Reiter):** Die Reiter stehen in der Mitte der App-Leiste — Sie können mehrere Dashboards anlegen, die dort nebeneinander stehen. Jeder Reiter trägt seine eigenen Kacheln und seine eigene Anordnung; was auf dem einen Reiter liegt, erscheint nicht auf dem anderen. Ein Klick auf einen Reiter wechselt dorthin. Beim Öffnen wird immer der ERSTE Reiter geladen — Sie legen ihn fest, indem Sie einen Reiter mit der Maus ganz nach vorn ziehen (das geht jederzeit, auch ohne den Bearbeitungsmodus). Im Bearbeitungsmodus können Sie außerdem: - Über den Knopf am Ende der Leiste einen neuen, leeren Reiter anlegen — er heißt automatisch „Dashboard 2", „Dashboard 3" und so weiter.