|
| 1 | +<!DOCTYPE html> |
| 2 | +<html lang="en"> |
| 3 | +<head> |
| 4 | + <title>T1330691 - Scheduler Popup Animation Debug</title> |
| 5 | + <meta charset="utf-8" /> |
| 6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 7 | + <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> |
| 8 | + <link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/25.1.3/css/dx.fluent.blue.light.css" /> |
| 9 | + <script src="https://cdn3.devexpress.com/jslib/25.1.3/js/dx.all.js"></script> |
| 10 | + <style> |
| 11 | + body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 20px; } |
| 12 | + h1 { font-size: 1.3em; margin-bottom: 8px; } |
| 13 | + .info { background: #f0f4ff; border: 1px solid #cce; border-radius: 6px; padding: 10px 14px; margin-bottom: 12px; font-size: 13px; } |
| 14 | + #scheduler { margin-top: 12px; } |
| 15 | + #log { position: fixed; bottom: 0; left: 0; right: 0; background: #1a1a2e; color: #0f0; font-family: monospace; font-size: 11px; padding: 6px; max-height: 200px; overflow-y: auto; z-index: 99999; } |
| 16 | + #log .entry { margin: 1px 0; } |
| 17 | + #log .warn { color: #ff0; } |
| 18 | + #log .err { color: #f55; } |
| 19 | + #log .ok { color: #5f5; } |
| 20 | + .controls { margin-bottom: 8px; } |
| 21 | + .controls button { margin-right: 8px; padding: 4px 12px; } |
| 22 | + </style> |
| 23 | +</head> |
| 24 | +<body class="dx-viewport"> |
| 25 | + <h1>T1330691 - Scheduler Popup Animation Debug</h1> |
| 26 | + <div class="info"> |
| 27 | + <strong>Steps:</strong> Double-click empty slot (animation shows) → close → click same slot again (no animation). |
| 28 | + <br>Check the debug log below for fx.animate / fx.stop calls and CSS transform values. |
| 29 | + </div> |
| 30 | + <div class="controls"> |
| 31 | + <button onclick="document.getElementById('log').innerHTML=''">Clear Log</button> |
| 32 | + </div> |
| 33 | + <div id="scheduler"></div> |
| 34 | + <div id="log"></div> |
| 35 | + |
| 36 | + <script> |
| 37 | + var logEl = document.getElementById('log'); |
| 38 | + function log(msg, cls) { |
| 39 | + var d = new Date(); |
| 40 | + var ts = d.toLocaleTimeString() + '.' + String(d.getMilliseconds()).padStart(3, '0'); |
| 41 | + var div = document.createElement('div'); |
| 42 | + div.className = 'entry ' + (cls || ''); |
| 43 | + div.textContent = '[' + ts + '] ' + msg; |
| 44 | + logEl.appendChild(div); |
| 45 | + logEl.scrollTop = logEl.scrollHeight; |
| 46 | + } |
| 47 | + |
| 48 | + function getStack() { |
| 49 | + try { throw new Error(); } catch(e) { |
| 50 | + var lines = (e.stack || '').split('\n').slice(2, 6); |
| 51 | + return lines.map(function(l) { return l.trim().replace(/^at\s+/, ''); }).join(' < '); |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + var popupShowCount = 0; |
| 56 | + |
| 57 | + // Hook fx.animate |
| 58 | + var origAnimate = DevExpress.fx.animate; |
| 59 | + DevExpress.fx.animate = function(element, config) { |
| 60 | + var $el = $(element); |
| 61 | + if ($el.hasClass('dx-overlay-content')) { |
| 62 | + var type = config ? config.type : '?'; |
| 63 | + var duration = config ? config.duration : '?'; |
| 64 | + var fromScale = config && config.from ? JSON.stringify(config.from) : '?'; |
| 65 | + var toStr = config && config.to ? JSON.stringify(config.to) : '?'; |
| 66 | + log('fx.animate: type=' + type + ' dur=' + duration + ' from=' + fromScale + ' to=' + toStr, 'ok'); |
| 67 | + lastAnimateTime = Date.now(); |
| 68 | + if (type === 'pop' && fromScale.indexOf('0.55') >= 0) { |
| 69 | + animateActive = true; |
| 70 | + setTimeout(function() { animateActive = false; }, 500); |
| 71 | + } |
| 72 | + |
| 73 | + var computedTransform = window.getComputedStyle(element).transform; |
| 74 | + var inlineTransform = element.style.transform; |
| 75 | + log(' CSS state: computed=' + computedTransform + ' inline=' + inlineTransform); |
| 76 | + } |
| 77 | + return origAnimate.apply(this, arguments); |
| 78 | + }; |
| 79 | + |
| 80 | + // Track when animate was last called |
| 81 | + var lastAnimateTime = 0; |
| 82 | + var animateActive = false; |
| 83 | + |
| 84 | + // Hook fx.stop |
| 85 | + var origStop = DevExpress.fx.stop; |
| 86 | + DevExpress.fx.stop = function(element, jumpToEnd) { |
| 87 | + var $el = $(element); |
| 88 | + if ($el.hasClass('dx-overlay-content') && $el.closest('.dx-scheduler-appointment-popup').length) { |
| 89 | + var timeSinceAnimate = Date.now() - lastAnimateTime; |
| 90 | + var stack; |
| 91 | + try { throw new Error(); } catch(e) { stack = e.stack || ''; } |
| 92 | + var lines = stack.split('\n').slice(2, 8).map(function(l) { return l.trim(); }).join('\n '); |
| 93 | + var isKiller = animateActive && timeSinceAnimate < 500; |
| 94 | + if (isKiller) { |
| 95 | + log('!! fx.stop ' + timeSinceAnimate + 'ms AFTER animate (KILLS ANIMATION):\n ' + lines, 'err'); |
| 96 | + } else { |
| 97 | + log('fx.stop(jumpToEnd=' + jumpToEnd + ') ' + timeSinceAnimate + 'ms after animate', 'warn'); |
| 98 | + } |
| 99 | + } |
| 100 | + return origStop.apply(this, arguments); |
| 101 | + }; |
| 102 | + |
| 103 | + var currentDate = new Date(); |
| 104 | + var appointments = [ |
| 105 | + { |
| 106 | + text: 'Meeting', |
| 107 | + startDate: new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate(), 9, 30), |
| 108 | + endDate: new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate(), 11, 0), |
| 109 | + }, |
| 110 | + ]; |
| 111 | + |
| 112 | + $('#scheduler').dxScheduler({ |
| 113 | + dataSource: appointments, |
| 114 | + currentDate: currentDate, |
| 115 | + startDayHour: 8, |
| 116 | + endDayHour: 18, |
| 117 | + height: 450, |
| 118 | + views: ['week'], |
| 119 | + currentView: 'week', |
| 120 | + }); |
| 121 | + |
| 122 | + log('Scheduler ready. Double-click empty slot, close popup, then click same slot again.'); |
| 123 | + log('Compare log output between first open (with animation) and second open (without).'); |
| 124 | + </script> |
| 125 | +</body> |
| 126 | +</html> |
0 commit comments