feature (admin): reduce onboarding friction

This commit is contained in:
MickaelK 2025-10-16 01:37:09 +11:00
parent 95c878f671
commit a383322e1c
3 changed files with 113 additions and 2 deletions

View file

@ -42,6 +42,52 @@
font-weight: bold;
font-style: italic;
}
.component_setup #step2 #onboarding {
position: fixed;
right: 32px;
bottom: 32px;
border-radius: 16px;
padding: 12px 18px;
color: var(--light);
}
.component_setup #step2 #onboarding .dash-border {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.component_setup #step2 #onboarding .dash-border rect {
width: calc(100% - 4px);
height: calc(100% - 4px);
fill: none;
stroke: #57595A;
stroke-width: 3;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 8 6;
animation: dash-move 1.2s linear infinite;
}
@keyframes dash-move {
to { stroke-dashoffset: -14; }
}
.component_setup #step2 #onboarding svg {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.component_setup #step2 #onboarding svg path {
stroke-width: 3;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke: #57595A;
}
.component_setup #step2 #onboarding svg g {
fill: #57595A;
}
.component_setup .stepper-form-appear, .component_setup .stepper-form-enter {
transition-delay: 0.3s;
transform: scale(1.02);
@ -58,3 +104,12 @@
.component_setup .component_icon {
width: 30px;
}
.pulse {
animation: zoomPulse 1.2s ease-in-out infinite;
transform-origin: center left;
}
@keyframes zoomPulse {
0%, 100% { transform: scale(1.03); }
50% { transform: scale(1.07); }
}

View file

@ -117,6 +117,7 @@ function componentStep2(render) {
You're at the Helm now
</h4>
<div data-bind="dependencies"></div>
<div data-bind="onboarding"></div>
<style>${cssHideMenu}</style>
</div>
`);
@ -187,6 +188,8 @@ function componentStep2(render) {
};
return ret.toPromise();
};
// feature: modal
effect(getAdminConfig().pipe(
reshapeConfigBeforeSave,
rxjs.delay(300),
@ -197,7 +200,57 @@ function componentStep2(render) {
config["log"]["telemetry"] = enabled;
return config;
}),
rxjs.filter((config) => !!config),
saveConfig(),
rxjs.mergeMap((config) => {
if (config) return rxjs.of(config).pipe(saveConfig());
return rxjs.of(null);
}),
// feature: onboarding
rxjs.mapTo(createElement(`
<div id="onboarding">
<strong>Next step: Link your storage</strong>
<svg class="dash-border">
<rect x="2" y="2" rx="16" ry="16"></rect>
</svg>
<svg>
<path id="path" pathLength="1" stroke-dasharray="1" stroke-dashoffset="1">
<animate attributeName="stroke-dashoffset" begin="indefinite" dur="700ms" fill="freeze" values="1;0" calcMode="linear" />
</path>
<g>
<polygon points="4,0 -16,-8 -16,8"></polygon>
<animateMotion begin="indefinite" dur="700ms" fill="freeze" rotate="auto" calcMode="linear">
<mpath href="#path"></mpath>
</animateMotion>
</g>
</svg>
</div>
`)),
applyMutation(qs($page, "[data-bind=\"onboarding\"]"), "appendChild"),
rxjs.delay(500),
rxjs.map(($origin) => {
const $target = document.querySelector("a[href=\"/admin/storage\"]");
const $path = $origin.querySelector("svg path");
const $anims = [
$origin.querySelector("svg animate"),
$origin.querySelector("svg animateMotion"),
];
const arc = (start, end) => {
const r = Math.min(Math.abs(end.x - start.x), Math.abs(end.y - start.y)) * 0.9;
return `M ${start.x},${start.y} ` +
`L ${start.x},${end.y + r} ` +
`Q ${start.x},${end.y} ${start.x - r},${end.y} ` +
`L ${end.x+20},${end.y+2}`;
};
const o = $origin.getBoundingClientRect();
const t = $target.getBoundingClientRect();
$path.setAttribute("d", arc(
{ x: o.left + o.width/2, y: o.top },
{ x: t.right, y: t.top + t.height/2 }
));
$anims.forEach(($el) => $el.beginElement());
$target.classList.add("pulse");
return $origin;
}),
rxjs.switchMap(($node) => rxjs.fromEvent(window, "resize").pipe(rxjs.tap(() => $node.remove()))),
));
}

View file

@ -32,6 +32,9 @@
bottom: 0;
opacity: 0.25;
}
.component_menu_sidebar ul li a {
display: inline-block;
}
.component_menu_sidebar .header {
display: block;
height: 40px;