Add per-tab tutorials, profile expand/collapse, and fix card animation
- Add sub-tutorials for Dashboard, Targets, Sources, and Profiles tabs with ? trigger buttons, en/ru/zh translations, and hidden-ancestor skip via offsetParent check - Add expand/collapse all buttons to Profiles tab toolbar - Move dashboard poll slider from section header to toolbar - Fix cardEnter animation forcing opacity:1 on disabled profile cards - Use data-card-section selectors instead of data-cs-toggle to avoid z-index misalignment during tutorial spotlight - Add tutorial sync convention to CLAUDE.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,44 @@ const gettingStartedSteps = [
|
||||
{ selector: '#locale-select', textKey: 'tour.language', position: 'bottom' }
|
||||
];
|
||||
|
||||
const dashboardTutorialSteps = [
|
||||
{ selector: '[data-dashboard-section="perf"]', textKey: 'tour.dash.perf', position: 'bottom' },
|
||||
{ selector: '[data-dashboard-section="running"]', textKey: 'tour.dash.running', position: 'bottom' },
|
||||
{ selector: '[data-dashboard-section="stopped"]', textKey: 'tour.dash.stopped', position: 'bottom' },
|
||||
{ selector: '[data-dashboard-section="profiles"]', textKey: 'tour.dash.profiles', position: 'bottom' }
|
||||
];
|
||||
|
||||
const targetsTutorialSteps = [
|
||||
{ selector: '[data-target-sub-tab="led"]', textKey: 'tour.tgt.led_tab', position: 'bottom' },
|
||||
{ selector: '[data-card-section="led-devices"]', textKey: 'tour.tgt.devices', position: 'bottom' },
|
||||
{ selector: '[data-card-section="led-css"]', textKey: 'tour.tgt.css', position: 'bottom' },
|
||||
{ selector: '[data-card-section="led-targets"]', textKey: 'tour.tgt.targets', position: 'bottom' },
|
||||
{ selector: '[data-target-sub-tab="key_colors"]', textKey: 'tour.tgt.kc_tab', position: 'bottom' }
|
||||
];
|
||||
|
||||
const sourcesTourSteps = [
|
||||
{ selector: '[data-stream-tab="raw"]', textKey: 'tour.src.raw', position: 'bottom' },
|
||||
{ selector: '[data-card-section="raw-templates"]', textKey: 'tour.src.templates', position: 'bottom' },
|
||||
{ selector: '[data-stream-tab="static_image"]', textKey: 'tour.src.static', position: 'bottom' },
|
||||
{ selector: '[data-stream-tab="processed"]', textKey: 'tour.src.processed', position: 'bottom' },
|
||||
{ selector: '[data-stream-tab="audio"]', textKey: 'tour.src.audio', position: 'bottom' },
|
||||
{ selector: '[data-stream-tab="value"]', textKey: 'tour.src.value', position: 'bottom' }
|
||||
];
|
||||
|
||||
const profilesTutorialSteps = [
|
||||
{ selector: '[data-card-section="profiles"]', textKey: 'tour.prof.list', position: 'bottom' },
|
||||
{ selector: '[data-cs-add="profiles"]', textKey: 'tour.prof.add', position: 'bottom' },
|
||||
{ selector: '.card[data-profile-id]', textKey: 'tour.prof.card', position: 'bottom' }
|
||||
];
|
||||
|
||||
const _fixedResolve = (step) => {
|
||||
const el = document.querySelector(step.selector);
|
||||
if (!el) return null;
|
||||
// offsetParent is null when element or any ancestor has display:none
|
||||
if (!el.offsetParent) return null;
|
||||
return el;
|
||||
};
|
||||
|
||||
const deviceTutorialSteps = [
|
||||
{ selector: '.card-subtitle', textKey: 'device.tip.metadata', position: 'bottom' },
|
||||
{ selector: '.brightness-control', textKey: 'device.tip.brightness', position: 'bottom' },
|
||||
@@ -112,6 +150,46 @@ export function startGettingStartedTutorial() {
|
||||
});
|
||||
}
|
||||
|
||||
export function startDashboardTutorial() {
|
||||
startTutorial({
|
||||
steps: dashboardTutorialSteps,
|
||||
overlayId: 'getting-started-overlay',
|
||||
mode: 'fixed',
|
||||
container: null,
|
||||
resolveTarget: _fixedResolve
|
||||
});
|
||||
}
|
||||
|
||||
export function startTargetsTutorial() {
|
||||
startTutorial({
|
||||
steps: targetsTutorialSteps,
|
||||
overlayId: 'getting-started-overlay',
|
||||
mode: 'fixed',
|
||||
container: null,
|
||||
resolveTarget: _fixedResolve
|
||||
});
|
||||
}
|
||||
|
||||
export function startSourcesTutorial() {
|
||||
startTutorial({
|
||||
steps: sourcesTourSteps,
|
||||
overlayId: 'getting-started-overlay',
|
||||
mode: 'fixed',
|
||||
container: null,
|
||||
resolveTarget: _fixedResolve
|
||||
});
|
||||
}
|
||||
|
||||
export function startProfilesTutorial() {
|
||||
startTutorial({
|
||||
steps: profilesTutorialSteps,
|
||||
overlayId: 'getting-started-overlay',
|
||||
mode: 'fixed',
|
||||
container: null,
|
||||
resolveTarget: _fixedResolve
|
||||
});
|
||||
}
|
||||
|
||||
export function closeTutorial() {
|
||||
if (!activeTutorial) return;
|
||||
const onClose = activeTutorial.onClose;
|
||||
|
||||
Reference in New Issue
Block a user