Tutorial: Alle Demos mit _runDemo() absichern gegen haengenbleibende Navigation

Neuer Helfer _runDemo(fn): Fuehrt async Demo-Methoden aus und faengt
alle Fehler ab. Bei Fehler wird _demoRunning zurueckgesetzt und
_enableNavAfterDemo aufgerufen, sodass der Weiter-Button immer erscheint.

Alle 12 Demo-Aufrufe (FormTitleDesc, TypeSwitch, FormSources,
FormVisibility, FormRefresh, FormRetention, FormNotifications,
MapDemo, Drag, Resize, SourcesInfoIcon, SourcesActions) verwenden
jetzt _runDemo statt direktem Aufruf.

Zusaetzlich:
- _cursorToElement gibt sichere Fallback-Koordinaten zurueck wenn
  Element nicht sichtbar (getBoundingClientRect width/height = 0)
- _simulateFormTitleDesc wartet 600ms auf Modal-Rendering

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
Claude Dev
2026-03-16 17:22:30 +01:00
Ursprung a0f0315768
Commit 0c9ee1c144
2 geänderte Dateien mit 28 neuen und 14 gelöschten Zeilen

Datei anzeigen

@@ -493,6 +493,17 @@ const Tutorial = {
this._stepTimers = [];
},
// Sichere Demo-Ausfuehrung: Faengt Fehler ab und stellt Navigation sicher
_runDemo(fn) {
var self = this;
fn.call(this).catch(function(e) {
self._hideCursor();
self._clearSubHighlights();
self._demoRunning = false;
self._enableNavAfterDemo();
});
},
// -----------------------------------------------------------------------
// Scroll-Helfer: Element in den sichtbaren Bereich scrollen
// -----------------------------------------------------------------------
@@ -575,7 +586,7 @@ const Tutorial = {
if (modalBody) modalBody.scrollTop = 0;
var t = document.getElementById('inc-title'); if (t) t.value = '';
var d = document.getElementById('inc-description'); if (d) d.value = '';
Tutorial._simulateFormTitleDesc();
Tutorial._runDemo(Tutorial._simulateFormTitleDesc);
},
onExit: function() {
Tutorial._clearSubHighlights();
@@ -600,7 +611,7 @@ const Tutorial = {
if (modalBody) modalBody.scrollTo({ top: 0, behavior: 'smooth' });
Tutorial._stepTimeout(function() {
Tutorial._highlightSub('#inc-type');
Tutorial._simulateTypeSwitch();
Tutorial._runDemo(Tutorial._simulateTypeSwitch);
}, 500);
},
onExit: function() {
@@ -624,7 +635,7 @@ const Tutorial = {
var overlay = document.getElementById('modal-new');
if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active');
if (overlay) overlay.style.zIndex = '9002';
Tutorial._simulateFormSources();
Tutorial._runDemo(Tutorial._simulateFormSources);
},
onExit: function() {
Tutorial._clearSubHighlights();
@@ -645,7 +656,7 @@ const Tutorial = {
var overlay = document.getElementById('modal-new');
if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active');
if (overlay) overlay.style.zIndex = '9002';
Tutorial._simulateFormVisibility();
Tutorial._runDemo(Tutorial._simulateFormVisibility);
},
onExit: function() {
Tutorial._clearSubHighlights();
@@ -666,7 +677,7 @@ const Tutorial = {
var overlay = document.getElementById('modal-new');
if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active');
if (overlay) overlay.style.zIndex = '9002';
Tutorial._simulateFormRefresh();
Tutorial._runDemo(Tutorial._simulateFormRefresh);
},
onExit: function() {
Tutorial._clearSubHighlights();
@@ -687,7 +698,7 @@ const Tutorial = {
var overlay = document.getElementById('modal-new');
if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active');
if (overlay) overlay.style.zIndex = '9002';
Tutorial._simulateFormRetention();
Tutorial._runDemo(Tutorial._simulateFormRetention);
},
onExit: function() {
Tutorial._clearSubHighlights();
@@ -709,7 +720,7 @@ const Tutorial = {
var overlay = document.getElementById('modal-new');
if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active');
if (overlay) overlay.style.zIndex = '9002';
Tutorial._simulateFormNotifications();
Tutorial._runDemo(Tutorial._simulateFormNotifications);
},
onExit: function() {
var overlay = document.getElementById('modal-new');
@@ -970,7 +981,7 @@ const Tutorial = {
position: 'right',
disableNav: true,
onEnter: function() {
Tutorial._simulateDrag();
Tutorial._runDemo(Tutorial._simulateDrag);
},
},
// 19 - Resize Demo
@@ -984,7 +995,7 @@ const Tutorial = {
position: 'left',
disableNav: true,
onEnter: function() {
Tutorial._simulateResize();
Tutorial._runDemo(Tutorial._simulateResize);
},
},
// 20 - Theme
@@ -1044,7 +1055,7 @@ const Tutorial = {
}
if (overlay) overlay.style.zIndex = '9002';
Tutorial._stepTimeout(function() {
Tutorial._simulateSourcesInfoIcon();
Tutorial._runDemo(Tutorial._simulateSourcesInfoIcon);
}, 1500);
},
onExit: function() {
@@ -1075,7 +1086,7 @@ const Tutorial = {
}
}
if (overlay) overlay.style.zIndex = '9002';
Tutorial._simulateSourcesActions();
Tutorial._runDemo(Tutorial._simulateSourcesActions);
},
onExit: function() {
var overlay = document.getElementById('modal-sources');
@@ -1594,7 +1605,7 @@ const Tutorial = {
}
// Nach Zoom: Demo starten
setTimeout(function() {
self._simulateMapDemo();
self._runDemo(self._simulateMapDemo);
}, 3200);
}, 1200);
},
@@ -1680,8 +1691,9 @@ const Tutorial = {
// Helfer: Cursor zu einem Element bewegen
async _cursorToElement(selector, fromX, fromY) {
var el = document.querySelector(selector);
if (!el) return { x: fromX, y: fromY };
if (!el) return { x: fromX || 400, y: fromY || 300 };
var rect = el.getBoundingClientRect();
if (!rect.width && !rect.height) return { x: fromX || 400, y: fromY || 300 };
var tx = rect.left + Math.min(rect.width / 2, 60);
var ty = rect.top + rect.height / 2;
if (fromX !== undefined && fromY !== undefined) {
@@ -1707,6 +1719,8 @@ const Tutorial = {
// -----------------------------------------------------------------------
async _simulateFormTitleDesc() {
this._demoRunning = true;
// Warten bis Modal vollstaendig gerendert
await this._wait(600);
var titleInput = document.getElementById('inc-title');
var descInput = document.getElementById('inc-description');
if (!titleInput) { this._demoRunning = false; this._enableNavAfterDemo(); return; }