feat(quick-260916-dyv): Bearbeiten-Schalter unten rechts (Rand oben 28 px), ganze Kachel als Griff mit Kopfleiste, cancel-Selektor, kein Ueberlappen beim Ablegen
- page.tsx: feste Aktionsleiste `fixed bottom-6 right-6 z-20` mit "Widget
hinzufuegen" (nur Bearbeitungsmodus) links neben dem Stift/Haekchen; Block
oben rechts und mt-8-Wrapper entfernt, Grid direkt im Container p-2
(12 + 8 + 8 = 28 px statt 60 px); page.test.tsx NEU mit 3 Tests
- widget-wrapper.tsx: Karte ist im Bearbeitungsmodus der Griff (cursor-grab),
20-px-Overlay-Kopfleiste mit Griff-Symbol und Tooltip dragHint, Loesch-Knopf
in der Kopfleiste mit data-no-drag; Rumpf h-full unveraendert (Hoehenkette)
- dashboard-grid.tsx: WIDGET_DRAG_HANDLE_SELECTOR, WIDGET_DRAG_CANCEL_SELECTOR
(input, textarea, select, button, a, [contenteditable], [data-no-drag],
.widgetNoDrag), threshold 3; FREE_PLACEMENT_COMPACTOR = noCompactor +
preventCollision: true (freie Platzierung bleibt, Commit c8f3361)
- dashboard-grid.test.tsx: Mock per importOriginal (echter noCompactor),
Tests 6-8 (dragConfig-Pin, Compactor-Pin, cancel/handle-Semantik im DOM)
- edit-mode-toggle.tsx: schwebend (shadow-lg, inaktiv border + bg-card)
- de.json/en.json: widgets.dragHint
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Responsive, noCompactor } from 'react-grid-layout';
|
||||
import type { ResponsiveLayouts } from 'react-grid-layout';
|
||||
import type { Compactor, ResponsiveLayouts } from 'react-grid-layout';
|
||||
import 'react-grid-layout/css/styles.css';
|
||||
import 'react-resizable/css/styles.css';
|
||||
import { useTranslations } from 'next-intl';
|
||||
@@ -17,6 +17,33 @@ import { TesseraLogo } from '@/components/brand/tessera-logo';
|
||||
const BREAKPOINTS = { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 };
|
||||
const COLS = { lg: 24, md: 20, sm: 12, xs: 8, xxs: 2 };
|
||||
|
||||
// quick-260916-dyv: Ziehen zuverlaessig.
|
||||
// - Griff ist die GANZE Karte (`widget-drag-handle` in widget-wrapper.tsx).
|
||||
// - `cancel` verhindert den Drag-Start an Eingabefeldern, Knoepfen, Links,
|
||||
// [contenteditable], [data-no-drag] und der Klasse `widgetNoDrag` (Favoriten/
|
||||
// Link-Widget, bisher nirgends verdrahtet). react-draggable 4.7.0 prueft
|
||||
// `cancel` NACH `handle` und vom Ereignisziel aufwaerts bis zum RGL-Element
|
||||
// (Draggable.js:417 + matchesSelectorAndParentsTo) — cancel gewinnt also auch
|
||||
// INNERHALB des Griffs. RGL haengt `.react-resizable-handle` selbst voran
|
||||
// (chunk-WGL5FSZH.mjs:526), der Groessen-Griff funktioniert weiter.
|
||||
// - `threshold: 3` = RGL-Standard (Klick vs. Ziehen).
|
||||
export const WIDGET_DRAG_HANDLE_SELECTOR = '.widget-drag-handle';
|
||||
export const WIDGET_DRAG_CANCEL_SELECTOR =
|
||||
'input, textarea, select, button, a, [contenteditable], [data-no-drag], .widgetNoDrag';
|
||||
|
||||
// quick-260916-dyv: Kein Ueberlappen beim Ablegen, freie Platzierung bleibt.
|
||||
// `noCompactor` (Commit c8f3361, "prevent auto-compaction on drag") ist gewollt:
|
||||
// Luecken werden nicht geschlossen. OHNE `preventCollision` springt beim Ziehen
|
||||
// auf ein belegtes Feld das gezogene Widget auf die Zeile des getroffenen und
|
||||
// das getroffene rutscht um seine EIGENE Hoehe nach unten — ohne Kaskade und
|
||||
// ohne Aufloesung, Ueberlappungen bleiben, weil `noCompactor.compact` die
|
||||
// Identitaet ist (gemessen chunk-76RTO6EO.mjs:279-328); beim Vergroessern in
|
||||
// einen Nachbarn entsteht die Ueberlappung stumm (chunk-WGL5FSZH.mjs:872-885).
|
||||
// MIT `preventCollision: true` bleibt das gezogene Widget am Ausgangsort
|
||||
// (`l.x = oldX; l.y = oldY`), und Vergroessern stoppt am Nachbarn.
|
||||
// `preventCollision` lebt am Compactor-Objekt (chunk-WGL5FSZH.mjs:666).
|
||||
export const FREE_PLACEMENT_COMPACTOR: Compactor = { ...noCompactor, preventCollision: true };
|
||||
|
||||
interface LayoutItemShape {
|
||||
i: string;
|
||||
x: number;
|
||||
@@ -147,12 +174,14 @@ export function DashboardGrid({
|
||||
// dann das margin (gemessen: effectiveContainerPadding = containerPadding ?? margin).
|
||||
dragConfig={{
|
||||
enabled: isEditMode,
|
||||
handle: '.widget-drag-handle',
|
||||
handle: WIDGET_DRAG_HANDLE_SELECTOR,
|
||||
cancel: WIDGET_DRAG_CANCEL_SELECTOR,
|
||||
threshold: 3,
|
||||
}}
|
||||
resizeConfig={{
|
||||
enabled: isEditMode,
|
||||
}}
|
||||
compactor={noCompactor}
|
||||
compactor={FREE_PLACEMENT_COMPACTOR}
|
||||
onLayoutChange={(_currentLayout, allLayouts) => onLayoutChange(allLayouts)}
|
||||
>
|
||||
{widgets.map((widget) => {
|
||||
|
||||
Reference in New Issue
Block a user