fix(widgets): harden minimize transitionend with fallback and property filter
- Filter transitionend by propertyName=opacity to prevent double-fire - Add 350ms fallback setTimeout for prefers-reduced-motion / zero-duration - Initialize _minimizing: false in create() for clean state - Dispatch events after save() for consistent state in listeners Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+22
-7
@@ -52,7 +52,7 @@ const WidgetManager = {
|
|||||||
const el = this._buildDOM(state);
|
const el = this._buildDOM(state);
|
||||||
document.body.appendChild(el);
|
document.body.appendChild(el);
|
||||||
|
|
||||||
this._widgets.set(id, { el, type, state });
|
this._widgets.set(id, { el, type, state, _minimizing: false });
|
||||||
this._initDrag(el);
|
this._initDrag(el);
|
||||||
this._initResize(el);
|
this._initResize(el);
|
||||||
this.bringToFront(id);
|
this.bringToFront(id);
|
||||||
@@ -170,7 +170,9 @@ const WidgetManager = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Widget minimieren (aus DOM verstecken, bleibt im Notebook)
|
* Widget minimieren (aus DOM verstecken, bleibt im Notebook).
|
||||||
|
* Nutzt transitionend statt setTimeout — _minimizing Flag verhindert Race Condition
|
||||||
|
* mit openWidget(). Fallback-Timer fuer prefers-reduced-motion / fehlende Transition.
|
||||||
* @param {string} id
|
* @param {string} id
|
||||||
*/
|
*/
|
||||||
async minimize(id) {
|
async minimize(id) {
|
||||||
@@ -180,17 +182,30 @@ const WidgetManager = {
|
|||||||
entry._minimizing = true;
|
entry._minimizing = true;
|
||||||
entry.el.classList.add('widget-minimized');
|
entry.el.classList.add('widget-minimized');
|
||||||
|
|
||||||
entry.el.addEventListener('transitionend', function onEnd(e) {
|
const MINIMIZE_FALLBACK_MS = 350;
|
||||||
if (e.target !== entry.el) return;
|
|
||||||
|
function onEnd(e) {
|
||||||
|
if (e.target !== entry.el || e.propertyName !== 'opacity') return;
|
||||||
|
clearTimeout(fallbackTimer);
|
||||||
entry.el.removeEventListener('transitionend', onEnd);
|
entry.el.removeEventListener('transitionend', onEnd);
|
||||||
if (entry._minimizing) {
|
if (entry._minimizing) {
|
||||||
entry.el.style.display = 'none';
|
entry.el.style.display = 'none';
|
||||||
}
|
}
|
||||||
entry._minimizing = false;
|
entry._minimizing = false;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
entry.el.addEventListener('transitionend', onEnd);
|
||||||
|
|
||||||
|
const fallbackTimer = setTimeout(() => {
|
||||||
|
entry.el.removeEventListener('transitionend', onEnd);
|
||||||
|
if (entry._minimizing) {
|
||||||
|
entry.el.style.display = 'none';
|
||||||
|
entry._minimizing = false;
|
||||||
|
}
|
||||||
|
}, MINIMIZE_FALLBACK_MS);
|
||||||
|
|
||||||
this._emitter.dispatchEvent(new CustomEvent('widget:minimize', { detail: { id } }));
|
|
||||||
await this.save();
|
await this.save();
|
||||||
|
this._emitter.dispatchEvent(new CustomEvent('widget:minimize', { detail: { id } }));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -207,8 +222,8 @@ const WidgetManager = {
|
|||||||
entry.el.classList.remove('widget-minimized');
|
entry.el.classList.remove('widget-minimized');
|
||||||
});
|
});
|
||||||
this.bringToFront(id);
|
this.bringToFront(id);
|
||||||
this._emitter.dispatchEvent(new CustomEvent('widget:open', { detail: { id } }));
|
|
||||||
await this.save();
|
await this.save();
|
||||||
|
this._emitter.dispatchEvent(new CustomEvent('widget:open', { detail: { id } }));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user