Stagger a rectangular panel's new dashboard by rail order
Changing dashboard kept the grid mounted, so every new tile rose at once. The grid is drawn afresh per dashboard and its tiles come in row by row: from below, top row first, for one further down the rail, and from above, bottom row first, for one further up, as a round panel's dashboards slide.
This commit is contained in:
@@ -393,6 +393,7 @@ export function DashboardView({
|
||||
editing,
|
||||
onDashboard,
|
||||
around,
|
||||
turned,
|
||||
}: {
|
||||
dashboard: Dashboard
|
||||
renderWidget?: (widget: WidgetDef) => React.ReactNode
|
||||
@@ -407,6 +408,9 @@ export function DashboardView({
|
||||
* previous (-1), and their names. */
|
||||
onDashboard?: (by: number) => void
|
||||
around?: [string | undefined, string | undefined]
|
||||
/** Which way along a panel's rail it was reached: down (1) or up (-1).
|
||||
* Its tiles come in from that side. */
|
||||
turned?: number
|
||||
}) {
|
||||
const all = widgetsOf(dashboard)
|
||||
const round = isRound(dashboard)
|
||||
@@ -424,15 +428,15 @@ export function DashboardView({
|
||||
)
|
||||
}
|
||||
|
||||
// Stacked, the arrangement becomes a reading order, so it follows the rows
|
||||
// the panel shows rather than the order widgets happened to be added in.
|
||||
const widgets = stacked
|
||||
? [...all].sort((a, b) => {
|
||||
const left = placement(a)
|
||||
const right = placement(b)
|
||||
return (left.y ?? 0) - (right.y ?? 0) || (left.x ?? 0) - (right.x ?? 0)
|
||||
})
|
||||
: all
|
||||
// In the rows the panel shows rather than the order widgets happened to be
|
||||
// added in: the reading order a stack follows, and the order tiles arrive
|
||||
// in. A grid places each one itself, and one nobody arranged has them all
|
||||
// at 0,0, which the sort leaves as they were.
|
||||
const widgets = [...all].sort((a, b) => {
|
||||
const left = placement(a)
|
||||
const right = placement(b)
|
||||
return (left.y ?? 0) - (right.y ?? 0) || (left.x ?? 0) - (right.x ?? 0)
|
||||
})
|
||||
const columns = columnsOf(dashboard)
|
||||
|
||||
const grid = (
|
||||
@@ -449,11 +453,15 @@ export function DashboardView({
|
||||
/>
|
||||
) : (
|
||||
<WidgetGrid
|
||||
// Drawn afresh for another dashboard, so its tiles arrive as a page
|
||||
// rather than all at once under a grid already shown.
|
||||
key={dashboard.name}
|
||||
dashboard={dashboard}
|
||||
widgets={widgets}
|
||||
columns={columns}
|
||||
stacked={stacked}
|
||||
rail={rail}
|
||||
from={turned !== undefined && turned < 0 ? -1 : 1}
|
||||
renderWidget={renderWidget}
|
||||
/>
|
||||
)}
|
||||
@@ -480,6 +488,7 @@ function WidgetGrid({
|
||||
columns,
|
||||
stacked,
|
||||
rail,
|
||||
from,
|
||||
renderWidget,
|
||||
}: {
|
||||
dashboard: Dashboard
|
||||
@@ -487,6 +496,8 @@ function WidgetGrid({
|
||||
columns: number
|
||||
stacked?: boolean
|
||||
rail?: boolean
|
||||
/** The side the tiles come in from: below (1) or above (-1). */
|
||||
from: number
|
||||
renderWidget?: (widget: WidgetDef) => React.ReactNode
|
||||
}) {
|
||||
const { Frame } = useUi()
|
||||
@@ -496,6 +507,7 @@ function WidgetGrid({
|
||||
className={cn("widget-grid", stacked && "widget-stacked")}
|
||||
data-placed={isPlaced(widgets) || undefined}
|
||||
variants={gridStagger}
|
||||
custom={from}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
style={
|
||||
@@ -513,6 +525,7 @@ function WidgetGrid({
|
||||
style={widgetStyle(widget, columns)}
|
||||
className="widget-cell"
|
||||
variants={LOOK[look].enter}
|
||||
custom={from}
|
||||
transition={LOOK[look].spring}
|
||||
>
|
||||
{renderWidget ? (
|
||||
|
||||
@@ -30,6 +30,7 @@ export function PanelSurface({
|
||||
touch,
|
||||
onDashboard,
|
||||
around,
|
||||
turned,
|
||||
}: {
|
||||
dashboard: Dashboard
|
||||
/** A landscape arrangement scaled onto a phone comes out at about a fifth of
|
||||
@@ -45,6 +46,9 @@ export function PanelSurface({
|
||||
* others instead, whose names are `around` it. */
|
||||
onDashboard?: (by: number) => void
|
||||
around?: [string | undefined, string | undefined]
|
||||
/** Which way along the rail this dashboard was reached: down (1) or up
|
||||
* (-1). */
|
||||
turned?: number
|
||||
}) {
|
||||
const hasRail = Boolean(rail)
|
||||
const stack = stacked && !isRound(dashboard)
|
||||
@@ -57,9 +61,10 @@ export function PanelSurface({
|
||||
rail={hasRail}
|
||||
onDashboard={onDashboard}
|
||||
around={around}
|
||||
turned={turned}
|
||||
/>
|
||||
),
|
||||
[dashboard, hasRail, onDashboard, around],
|
||||
[dashboard, hasRail, onDashboard, around, turned],
|
||||
)
|
||||
return (
|
||||
<div className="relative size-full">
|
||||
@@ -76,7 +81,7 @@ export function PanelSurface({
|
||||
{/* Padded here rather than by the route: the stack overflows this
|
||||
box, so only padding inside the overflow clears the rail. */}
|
||||
<div className={cn("relative", rail && "pb-24")}>
|
||||
<DashboardView dashboard={dashboard} stacked />
|
||||
<DashboardView dashboard={dashboard} stacked turned={turned} />
|
||||
</div>
|
||||
{rail ? (
|
||||
// No column to spare on a phone, so the rail is a row floating
|
||||
|
||||
@@ -21,7 +21,8 @@ import type { Look } from "../../settings"
|
||||
|
||||
type LookMotion = {
|
||||
spring: Transition
|
||||
/** How a widget arrives on the canvas. */
|
||||
/** How a widget arrives on the canvas. `custom` is the side it comes from,
|
||||
* as `gridStagger` takes it. */
|
||||
enter: Variants
|
||||
/** Seconds for one round of the ambient ground; 0 draws none. */
|
||||
drift: number
|
||||
@@ -34,7 +35,7 @@ export const LOOK: Record<Look, LookMotion> = {
|
||||
// the way the rest of the product does.
|
||||
spring: { type: "spring", stiffness: 420, damping: 34, mass: 1 },
|
||||
enter: {
|
||||
hidden: { opacity: 0, y: 8 },
|
||||
hidden: (from = 1) => ({ opacity: 0, y: 8 * from }),
|
||||
visible: { opacity: 1, y: 0 },
|
||||
},
|
||||
drift: 0,
|
||||
@@ -42,7 +43,7 @@ export const LOOK: Record<Look, LookMotion> = {
|
||||
material: {
|
||||
spring: { type: "spring", stiffness: 550, damping: 38, mass: 1 },
|
||||
enter: {
|
||||
hidden: { opacity: 0, y: 8 },
|
||||
hidden: (from = 1) => ({ opacity: 0, y: 8 * from }),
|
||||
visible: { opacity: 1, y: 0 },
|
||||
},
|
||||
drift: 0,
|
||||
@@ -50,15 +51,23 @@ export const LOOK: Record<Look, LookMotion> = {
|
||||
glass: {
|
||||
spring: { type: "spring", stiffness: 320, damping: 22, mass: 0.8 },
|
||||
enter: {
|
||||
hidden: { opacity: 0, y: 12, scale: 0.96 },
|
||||
hidden: (from = 1) => ({ opacity: 0, y: 12 * from, scale: 0.96 }),
|
||||
visible: { opacity: 1, y: 0, scale: 1 },
|
||||
},
|
||||
drift: 46,
|
||||
},
|
||||
}
|
||||
|
||||
/** Widgets arrive as a page rather than all at once. */
|
||||
/**
|
||||
* Widgets arrive as a page rather than all at once, row by row.
|
||||
*
|
||||
* `custom` is the side they come from: below (1), as a round panel's next
|
||||
* dashboard rises, or above (-1) for one further up a panel's rail, which
|
||||
* brings the last row in first.
|
||||
*/
|
||||
export const gridStagger: Variants = {
|
||||
hidden: {},
|
||||
visible: { transition: { staggerChildren: 0.03 } },
|
||||
visible: (from = 1) => ({
|
||||
transition: { staggerChildren: 0.03, staggerDirection: from < 0 ? -1 : 1 },
|
||||
}),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user