mirror of
https://github.com/ever-co/ever-gauzy.git
synced 2026-10-02 01:54:50 +08:00
* fix(dashboard): assign both grid axes when re-flowing a canvas layout `packLayout` only ever corrected `y`, leaving `x` exactly where it was. The canvas derives reading order from `(y, x)`, so reordering two widgets that shared a row was a silent no-op: the sort read the untouched `x` values back and restored the original order. `flowLayout` assigns both coordinates from the order of the array, which is what the builder actually models -- widgets are appended, reordered by drag and resized from a menu, never placed at an arbitrary column. It is the sparse packing algorithm from CSS Grid 8.5 step 4, because the canvas now renders its cells with spans only and lets the browser place them; the two have to agree or the persisted `x`/`y` would describe a layout nobody sees. Verified against a real browser across four layouts, including ragged heights. Sparse rather than dense on purpose: dense back-fills earlier gaps, which would let a widget render before one that precedes it in the array, and the index CDK reports for a drop is a position in that array. Also adds `readingOrder`, and the pure drop-point geometry (`isPointInRect`, `dropIndexAtPoint`) the canvas uses to resolve a release that landed between cells. `packLayout` is untouched: `normalizeLayout` still depends on it. * test(dashboard): cover canvas flow layout and drop-point geometry Locks in the two contracts that are easy to break without noticing: - `flowLayout` must pack exactly like CSS grid auto-placement, because the browser is what actually positions the cells. The ragged-heights case is the one that catches drift -- `d` tucks in beside the tall `a` at column 4 rather than starting a fresh row. - A move between two widgets on the same row must survive the reading-order sort that follows it. That is the regression that made drag-and-drop look dead. Plus the drop-point geometry: below everything, in a gutter, on the next row, off either edge, and the half-open edge rule that stops two neighbouring cells both claiming a boundary pixel. * chore(i18n): add widget height labels and update the builder drag copy `HEIGHT` / `HEIGHT_ROWS` back the new height ladder in the widget menu. The drag copy no longer points at the grip: the whole card is the drag surface now, and the rail is the keyboard control, so the hint and the handle label say that instead. Only `en.json` carries these -- the rest of the builder strings are English-only too, and the other locales fall back. * fix(dashboard): make canvas drag-and-drop actually move widgets Dragging a widget did nothing at all, in any direction. `cdkDropListOrientation="mixed"` selects CDK's `MixedSortStrategy`, whose `sort()` shows the new position by MOVING THE PLACEHOLDER NODE between cells (`overlapElement.after(current)`) and does nothing else. Every cell was pinned with `grid-column: x+1 / span w`, so DOM order had no bearing on where anything rendered: CDK was reordering nodes inside a grid that ignored node order. Measured in a browser -- moving the first cell to the end left every widget's pixel position identical. Cells now declare spans only and the grid auto-places them in DOM order, so the sort is visible and the placeholder shows the real destination. Three further fixes: - `cdkDragHandle` is gone. With it, only the 16px rail could start a drag and grabbing the card -- what everyone tries -- did nothing. The whole cell is the drag surface; the kebab still opens on click because CDK needs 5px of movement before treating a press as a drag. The rail stays as the arrow-key control, which is the only way to rearrange without a mouse. - A release over the canvas' empty space is resolved from the drop point. CDK only re-sorts while the pointer is over another cell, so dropping into a gap, the ragged space beside a tall widget, or the run-off below the last row silently kept the index of the last cell crossed -- dragging a widget to the bottom put it back near where it started. - A release off the canvas changes nothing. CDK leaves a rejected or stray item at whatever index it sorted to on the way past, so flicking a widget towards the palette (which refuses it) quietly reordered the canvas. The canvas rect is measured in the handler rather than read from `isPointerOverContainer`, which CDK answers from a rect cached at drag start -- a layout shift mid-drag leaves it tens of pixels stale and rejects drops made well inside. Also drops the dead `_normalizePlacementHeight`, which was a no-op today but would have squashed any widget declaring `minSize.h === 1` to one row on load, and swaps CDK's default preview (a full clone of the cell -- a large translucent card covering the slot being aimed at, with a blank cloned canvas) for a chip. Verified with a real CDK harness driven by Playwright: 17 scenarios covering horizontal, vertical up and down, to the start and end, ragged rows, gaps, dead space, palette drops, rejected drops, no-op drags and a scrolled page. * fix(dashboard): stop canvas rows inflating and tighten the grid `align-content` behaves as `stretch` on a grid and `grid-auto-rows`' `auto` maximum is stretchable, so the rows expanded to fill `min-height`: a single two-row widget on a fresh canvas grew to the full 14rem. `align-content: start` keeps rows at their own size, and the space left over becomes the run-off the drop-point fallback reads as "put it at the end". `min-height` now applies only while editing, where it is that drop target. A read-only canvas has nothing to drop, and reserving 14rem under two small widgets was just empty page. Density: the row unit goes 64px -> 52px and the gap 1rem -> 0.75rem, so a six-row widget is 372px rather than 464px. Widget bodies scroll, so a tighter unit costs a little scrolling inside a big widget and buys the whole canvas fitting on one screen. The grab rail is visible from the moment edit mode starts -- at `opacity: 0` the only affordance for rearranging was hovering a strip nobody knew was there -- and the cell reserves a gutter for it so it sits beside the card instead of over the first 14px of its content. Drops the sibling-transform transition: the mixed sort strategy re-orders nodes and never transforms siblings, so it was dead. * fix(dashboard): auto-scroll the dashboard while dragging a widget This host element is the dashboard's scroll surface -- `dashboard.component.scss` gives `router-outlet ~ *` the `overflow-y: auto`, because the app layout's own scroller is clamped so the header and footer stay put. `CdkDropList` only auto-scrolls the viewport and ancestors registered with `ScrollDispatcher`, which is what `cdkScrollable` does. Without it, dragging a widget towards the bottom of a canvas taller than the window stopped at the fold: the page never scrolled, so the lower half was unreachable by drag. * style(dashboard): match the page spacing to the canvas grid The page stacked its heading, hint bar, tab strip and body on a 1rem rhythm wrapped around a 0.75rem grid, which read as a loose frame round a tight canvas. Same gap on both now, and the heading loses a quarter rem it was not using. * feat(dashboard): show a drop slot when dragging a widget from the palette CDK carries the placeholder into whichever list the pointer is over, so the default one -- a clone of the palette row -- was being dropped into the canvas GRID as a full-width list entry. The slot is styled in the palette's own sheet rather than the canvas': CDK renders it from this component's template, so it carries this component's style attribute and a canvas-scoped rule would never match it. Its grid spans are inert in the palette's own block list and match the most common widget footprint, since the real one is only known once the drop resolves the widget's `defaultSize`. * feat(dashboard): let a widget's height be resized from its menu The menu offered width only, so a widget arrived at whatever `defaultSize` its author picked and the only way to stop one eating half the canvas was to remove it. `resized` now carries either dimension and the menu shows two ladders. A ladder rather than every value in range -- a chart allowing 3 to 10 rows would put eight near-identical buttons in the menu -- but both of the widget's declared bounds are always included, so the shortest and tallest size it supports are always reachable. * style(dashboard): tighten widget card chrome The header and body paddings are the frame's fixed overhead: paid by every card on the canvas at every size, and they consumed around 2.4rem of a two-row widget before its content got a pixel. Tight here so the grid can be tight. Also renames the menu's width ladder classes to serve both axes. * fixed the AI comments * fixed the AI comments