mirror of
https://github.com/flectar/mail.git
synced 2026-11-11 17:57:54 +00:00
136 lines
5.0 KiB
Plaintext
136 lines
5.0 KiB
Plaintext
import { LoadingSkeletonBlock } from "loading-skeleton.slint";
|
|
import { MotionSettings } from "../design-system.slint";
|
|
|
|
// Shared motion contract for retained item lists. Only semantic navigation
|
|
// (a new scope or a completed replacement load) restarts the reveal. Model
|
|
// revisions are deliberately kept out of this component: selection, metadata,
|
|
// pagination, and background sync may all revise a retained list without
|
|
// replacing what the user is looking at.
|
|
//
|
|
// A one-frame handoff gives Slint a real start and target value, while the
|
|
// critically damped 1.18 spring keeps fast scope changes smooth without making
|
|
// rows bounce.
|
|
export component WorkspaceListTransition inherits Rectangle {
|
|
in property <string> scope-key: "";
|
|
in property <bool> loading: false;
|
|
|
|
private property <float> animated-reveal-progress: 1;
|
|
private property <float> reveal-progress: MotionSettings.enabled ? root.animated-reveal-progress : 1;
|
|
private property <bool> reveal-pending: false;
|
|
|
|
clip: true;
|
|
|
|
function restart() {
|
|
if MotionSettings.enabled {
|
|
root.animated-reveal-progress = 0;
|
|
root.reveal-pending = true;
|
|
} else {
|
|
root.animated-reveal-progress = 1;
|
|
root.reveal-pending = false;
|
|
}
|
|
}
|
|
|
|
changed scope-key => { root.restart(); }
|
|
changed loading => { if !root.loading { root.restart(); } }
|
|
|
|
Timer {
|
|
interval: 16ms;
|
|
running: MotionSettings.enabled && root.reveal-pending;
|
|
triggered => {
|
|
root.reveal-pending = false;
|
|
root.animated-reveal-progress = 1;
|
|
}
|
|
}
|
|
|
|
content-stage := Rectangle {
|
|
x: (1 - root.reveal-progress) * 10px;
|
|
y: 0px;
|
|
width: parent.width;
|
|
height: parent.height;
|
|
opacity: 0.24 + root.reveal-progress * 0.76;
|
|
|
|
animate x {
|
|
duration: 220ms;
|
|
easing: spring(0);
|
|
enabled: MotionSettings.enabled && root.reveal-progress > 0;
|
|
}
|
|
animate opacity {
|
|
duration: 180ms;
|
|
easing: ease-out;
|
|
enabled: MotionSettings.enabled && root.reveal-progress > 0;
|
|
}
|
|
|
|
@children
|
|
}
|
|
}
|
|
|
|
// Mail, Contacts, and Files use the same virtualized row geometry but vary in
|
|
// density and line count. Keeping the placeholder here prevents loading states
|
|
// from drifting apart as those views evolve.
|
|
export component WorkspaceListLoadingRow inherits Rectangle {
|
|
in property <length> row-height: 82px;
|
|
in property <length> leading-size: 38px;
|
|
in property <length> leading-radius: root.leading-size / 2;
|
|
in property <bool> show-leading: true;
|
|
in property <int> line-count: 2;
|
|
in property <bool> show-trailing: true;
|
|
in property <color> base-color;
|
|
in property <color> highlight-color;
|
|
|
|
private property <bool> dense: root.row-height <= 52px;
|
|
private property <length> leading-x: 12px;
|
|
private property <length> content-x: root.show-leading
|
|
? root.leading-x + root.leading-size + 12px : 12px;
|
|
private property <length> trailing-width: root.dense ? 42px : 64px;
|
|
private property <length> trailing-x: max(root.content-x + 72px, root.width - root.trailing-width - 12px);
|
|
private property <length> content-width: max(0px, root.trailing-x - root.content-x - 12px);
|
|
|
|
height: root.row-height;
|
|
border-radius: 6px;
|
|
background: transparent;
|
|
|
|
if root.show-leading : LoadingSkeletonBlock {
|
|
x: root.leading-x;
|
|
y: (parent.height - self.height) / 2;
|
|
width: root.leading-size;
|
|
height: root.leading-size;
|
|
radius: root.leading-radius;
|
|
base-color: root.base-color;
|
|
highlight-color: root.highlight-color;
|
|
}
|
|
LoadingSkeletonBlock {
|
|
x: root.content-x;
|
|
y: root.dense ? (parent.height - self.height) / 2 : 13px;
|
|
width: min(154px, max(64px, root.content-width * 0.58));
|
|
height: 12px;
|
|
base-color: root.base-color;
|
|
highlight-color: root.highlight-color;
|
|
}
|
|
if root.line-count > 1 : LoadingSkeletonBlock {
|
|
x: root.dense ? min(root.content-x + 166px, root.trailing-x - 84px) : root.content-x;
|
|
y: root.dense ? (parent.height - self.height) / 2 : 36px;
|
|
width: root.dense
|
|
? min(118px, max(52px, root.trailing-x - self.x - 12px))
|
|
: min(218px, max(72px, root.content-width * 0.82));
|
|
height: root.dense ? 12px : 11px;
|
|
base-color: root.base-color;
|
|
highlight-color: root.highlight-color;
|
|
}
|
|
if root.line-count > 2 && !root.dense : LoadingSkeletonBlock {
|
|
x: root.content-x;
|
|
y: 58px;
|
|
width: min(178px, max(64px, root.content-width * 0.64));
|
|
height: 10px;
|
|
base-color: root.base-color;
|
|
highlight-color: root.highlight-color;
|
|
}
|
|
if root.show-trailing : LoadingSkeletonBlock {
|
|
x: root.trailing-x;
|
|
y: root.dense ? (parent.height - self.height) / 2 : 14px;
|
|
width: min(root.trailing-width, max(0px, parent.width - self.x - 12px));
|
|
height: 10px;
|
|
base-color: root.base-color;
|
|
highlight-color: root.highlight-color;
|
|
}
|
|
}
|