@jsenv/dom 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/jsenv_dom.js +9653 -0
  2. package/index.js +101 -0
  3. package/package.json +47 -0
  4. package/src/attr/add_attribute_effect.js +93 -0
  5. package/src/attr/attributes.js +32 -0
  6. package/src/demos/3_columns_resize_demo.html +84 -0
  7. package/src/demos/3_rows_resize_demo.html +89 -0
  8. package/src/demos/aside_and_main_demo.html +93 -0
  9. package/src/demos/coordinates_demo.html +450 -0
  10. package/src/demos/document_autoscroll_demo.html +517 -0
  11. package/src/demos/drag_gesture_constraints_demo.html +701 -0
  12. package/src/demos/drag_gesture_demo.html +1047 -0
  13. package/src/demos/drag_gesture_element_to_impact_demo.html +445 -0
  14. package/src/demos/drag_reference_element_demo.html +480 -0
  15. package/src/demos/flex_details_set_demo.html +302 -0
  16. package/src/demos/flex_details_set_demo_2.html +315 -0
  17. package/src/demos/visible_rect_demo.html +525 -0
  18. package/src/interaction/drag/constraint_feedback_line.js +92 -0
  19. package/src/interaction/drag/drag_constraint.js +659 -0
  20. package/src/interaction/drag/drag_debug_markers.js +635 -0
  21. package/src/interaction/drag/drag_element_positioner.js +382 -0
  22. package/src/interaction/drag/drag_gesture.js +566 -0
  23. package/src/interaction/drag/drag_resize_demo.html +571 -0
  24. package/src/interaction/drag/drag_to_move.js +301 -0
  25. package/src/interaction/drag/drag_to_resize_gesture.js +68 -0
  26. package/src/interaction/drag/drop_target_detection.js +148 -0
  27. package/src/interaction/drag/sticky_frontiers.js +160 -0
  28. package/src/interaction/element_log.js +8 -0
  29. package/src/interaction/event_marker.js +14 -0
  30. package/src/interaction/focus/active_element.js +33 -0
  31. package/src/interaction/focus/arrow_navigation.js +599 -0
  32. package/src/interaction/focus/element_is_focusable.js +57 -0
  33. package/src/interaction/focus/element_is_visible.js +36 -0
  34. package/src/interaction/focus/find_focusable.js +21 -0
  35. package/src/interaction/focus/focus_group.js +91 -0
  36. package/src/interaction/focus/focus_group_registry.js +12 -0
  37. package/src/interaction/focus/focus_nav.js +12 -0
  38. package/src/interaction/focus/focus_nav_event_marker.js +14 -0
  39. package/src/interaction/focus/focus_trap.js +105 -0
  40. package/src/interaction/focus/tab_navigation.js +128 -0
  41. package/src/interaction/focus/tests/focus_group_skip_tab_test.html +206 -0
  42. package/src/interaction/focus/tests/tree_focus_test.html +304 -0
  43. package/src/interaction/focus/tests/tree_focus_test.jsx +261 -0
  44. package/src/interaction/focus/tests/tree_focus_test_preact.html +13 -0
  45. package/src/interaction/isolate_interactions.js +161 -0
  46. package/src/interaction/keyboard.js +26 -0
  47. package/src/interaction/scroll/capture_scroll.js +47 -0
  48. package/src/interaction/scroll/is_scrollable.js +159 -0
  49. package/src/interaction/scroll/scroll_container.js +110 -0
  50. package/src/interaction/scroll/scroll_trap.js +44 -0
  51. package/src/interaction/scroll/scrollbar_size.js +20 -0
  52. package/src/interaction/scroll/wheel_through.js +138 -0
  53. package/src/iterable_weak_set.js +66 -0
  54. package/src/position/dom_coords.js +340 -0
  55. package/src/position/offset_parent.js +15 -0
  56. package/src/position/position_fixed.js +15 -0
  57. package/src/position/position_sticky.js +213 -0
  58. package/src/position/sticky_rect.js +79 -0
  59. package/src/position/visible_rect.js +482 -0
  60. package/src/pub_sub.js +28 -0
  61. package/src/size/can_take_size.js +11 -0
  62. package/src/size/details_content_full_height.js +63 -0
  63. package/src/size/flex_details_set.js +974 -0
  64. package/src/size/get_available_height.js +22 -0
  65. package/src/size/get_available_width.js +22 -0
  66. package/src/size/get_border_sizes.js +14 -0
  67. package/src/size/get_height.js +4 -0
  68. package/src/size/get_inner_height.js +15 -0
  69. package/src/size/get_inner_width.js +15 -0
  70. package/src/size/get_margin_sizes.js +10 -0
  71. package/src/size/get_max_height.js +57 -0
  72. package/src/size/get_max_width.js +47 -0
  73. package/src/size/get_min_height.js +14 -0
  74. package/src/size/get_min_width.js +14 -0
  75. package/src/size/get_padding_sizes.js +10 -0
  76. package/src/size/get_width.js +4 -0
  77. package/src/size/hooks/use_available_height.js +27 -0
  78. package/src/size/hooks/use_available_width.js +27 -0
  79. package/src/size/hooks/use_max_height.js +10 -0
  80. package/src/size/hooks/use_max_width.js +10 -0
  81. package/src/size/hooks/use_resize_status.js +62 -0
  82. package/src/size/resize.js +695 -0
  83. package/src/size/resolve_css_size.js +32 -0
  84. package/src/style/dom_styles.js +97 -0
  85. package/src/style/style_composition.js +78 -0
  86. package/src/style/style_controller.js +345 -0
  87. package/src/style/style_parsing.js +317 -0
  88. package/src/transition/demos/animation_resumption_test.xhtml +500 -0
  89. package/src/transition/demos/height_toggle_test.xhtml +515 -0
  90. package/src/transition/dom_transition.js +254 -0
  91. package/src/transition/easing.js +48 -0
  92. package/src/transition/group_transition.js +261 -0
  93. package/src/transition/transform_style_parser.js +32 -0
  94. package/src/transition/transition_playback.js +366 -0
  95. package/src/transition/transition_timeline.js +79 -0
  96. package/src/traversal.js +247 -0
  97. package/src/ui_transition/demos/content_states_transition_demo.html +628 -0
  98. package/src/ui_transition/demos/smooth_height_transition_demo.html +149 -0
  99. package/src/ui_transition/demos/transition_testing.html +354 -0
  100. package/src/ui_transition/ui_transition.js +1492 -0
  101. package/src/utils.js +69 -0
@@ -0,0 +1,571 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Drag to Resize Demo</title>
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ padding: 20px;
11
+ font-family: system-ui, sans-serif;
12
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
13
+ min-height: 100vh;
14
+ }
15
+
16
+ .demo-container {
17
+ max-width: 1200px;
18
+ margin: 0 auto;
19
+ background: white;
20
+ border-radius: 12px;
21
+ padding: 30px;
22
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
23
+ }
24
+
25
+ h1 {
26
+ text-align: center;
27
+ color: #333;
28
+ margin-bottom: 10px;
29
+ }
30
+
31
+ .subtitle {
32
+ text-align: center;
33
+ color: #666;
34
+ margin-bottom: 40px;
35
+ }
36
+
37
+ .demo-section {
38
+ margin-bottom: 50px;
39
+ padding: 20px;
40
+ border: 2px dashed #e0e0e0;
41
+ border-radius: 8px;
42
+ position: relative;
43
+ }
44
+
45
+ .demo-section h2 {
46
+ margin-top: 0;
47
+ color: #444;
48
+ }
49
+
50
+ .demo-section p {
51
+ color: #666;
52
+ margin-bottom: 20px;
53
+ }
54
+
55
+ /* Resizable Elements */
56
+ .resizable {
57
+ position: relative;
58
+ background: linear-gradient(45deg, #4facfe 0%, #00f2fe 100%);
59
+ border: 2px solid #3498db;
60
+ border-radius: 8px;
61
+ color: white;
62
+ display: flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ font-weight: bold;
66
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
67
+ box-shadow: 0 4px 15px rgba(79, 172, 254, 0.3);
68
+ transition: box-shadow 0.3s ease;
69
+ user-select: none;
70
+ }
71
+
72
+ .resizable:hover {
73
+ box-shadow: 0 8px 25px rgba(79, 172, 254, 0.4);
74
+ }
75
+
76
+ .resizable.dragging {
77
+ box-shadow: 0 12px 30px rgba(79, 172, 254, 0.5);
78
+ z-index: 1000;
79
+ }
80
+
81
+ /* Resize Handles */
82
+ .resize-handle {
83
+ position: absolute;
84
+ background: #2980b9;
85
+ border: 2px solid white;
86
+ border-radius: 50%;
87
+ cursor: pointer;
88
+ opacity: 0.7;
89
+ transition: all 0.2s ease;
90
+ z-index: 10;
91
+ }
92
+
93
+ .resize-handle:hover {
94
+ opacity: 1;
95
+ transform: scale(1.2);
96
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
97
+ }
98
+
99
+ .resize-handle.dragging {
100
+ opacity: 1;
101
+ transform: scale(1.3);
102
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
103
+ }
104
+
105
+ /* Handle positions */
106
+ .resize-handle.right {
107
+ right: -8px;
108
+ top: 50%;
109
+ transform: translateY(-50%);
110
+ cursor: e-resize;
111
+ width: 16px;
112
+ height: 16px;
113
+ }
114
+
115
+ .resize-handle.bottom {
116
+ bottom: -8px;
117
+ left: 50%;
118
+ transform: translateX(-50%);
119
+ cursor: s-resize;
120
+ width: 16px;
121
+ height: 16px;
122
+ }
123
+
124
+ .resize-handle.corner {
125
+ bottom: -8px;
126
+ right: -8px;
127
+ cursor: se-resize;
128
+ width: 20px;
129
+ height: 20px;
130
+ background: #e74c3c;
131
+ }
132
+
133
+ /* Demo specific layouts */
134
+ .basic-resize {
135
+ width: 200px;
136
+ height: 150px;
137
+ margin: 20px;
138
+ }
139
+
140
+ .constrained-resize {
141
+ width: 180px;
142
+ height: 120px;
143
+ margin: 20px;
144
+ }
145
+
146
+ .grid-container {
147
+ display: grid;
148
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
149
+ gap: 20px;
150
+ margin: 20px 0;
151
+ }
152
+
153
+ .obstacle {
154
+ background: #e74c3c;
155
+ color: white;
156
+ padding: 20px;
157
+ border-radius: 8px;
158
+ text-align: center;
159
+ font-weight: bold;
160
+ box-shadow: 0 4px 15px rgba(231, 76, 60, 0.3);
161
+ }
162
+
163
+ /* Constraint feedback */
164
+ .constraint-feedback {
165
+ position: fixed;
166
+ pointer-events: none;
167
+ background: rgba(52, 152, 219, 0.8);
168
+ color: white;
169
+ padding: 4px 8px;
170
+ border-radius: 4px;
171
+ font-size: 12px;
172
+ font-weight: bold;
173
+ z-index: 9999;
174
+ opacity: 0;
175
+ transition: opacity 0.15s ease;
176
+ }
177
+
178
+ .constraint-feedback.visible {
179
+ opacity: 1;
180
+ }
181
+
182
+ /* Status display */
183
+ .status {
184
+ position: fixed;
185
+ top: 20px;
186
+ right: 20px;
187
+ background: rgba(0, 0, 0, 0.8);
188
+ color: white;
189
+ padding: 15px;
190
+ border-radius: 8px;
191
+ font-family: monospace;
192
+ font-size: 12px;
193
+ line-height: 1.4;
194
+ min-width: 200px;
195
+ z-index: 10000;
196
+ }
197
+
198
+ .controls {
199
+ margin: 20px 0;
200
+ text-align: center;
201
+ }
202
+
203
+ .controls button {
204
+ background: #3498db;
205
+ color: white;
206
+ border: none;
207
+ padding: 10px 20px;
208
+ border-radius: 5px;
209
+ margin: 0 5px;
210
+ cursor: pointer;
211
+ font-weight: bold;
212
+ transition: background 0.3s ease;
213
+ }
214
+
215
+ .controls button:hover {
216
+ background: #2980b9;
217
+ }
218
+
219
+ .controls button:active {
220
+ transform: translateY(1px);
221
+ }
222
+ </style>
223
+ </head>
224
+ <body>
225
+ <div class="demo-container">
226
+ <h1>🎯 Drag to Resize Demo</h1>
227
+ <p class="subtitle">
228
+ Interactive demonstration of constraint-based resizing with drag
229
+ gestures
230
+ </p>
231
+
232
+ <!-- Basic Resize -->
233
+ <div class="demo-section">
234
+ <h2>📐 Basic Resize</h2>
235
+ <p>
236
+ Resize elements by dragging the handles. Supports horizontal,
237
+ vertical, and corner resize.
238
+ </p>
239
+
240
+ <div class="resizable basic-resize" id="basic1">
241
+ <div>Basic Resize Box</div>
242
+ <div class="resize-handle right" data-direction="horizontal"></div>
243
+ <div class="resize-handle bottom" data-direction="vertical"></div>
244
+ <div class="resize-handle corner" data-direction="both"></div>
245
+ </div>
246
+
247
+ <div class="resizable basic-resize" id="basic2">
248
+ <div>Another Box</div>
249
+ <div class="resize-handle right" data-direction="horizontal"></div>
250
+ <div class="resize-handle bottom" data-direction="vertical"></div>
251
+ <div class="resize-handle corner" data-direction="both"></div>
252
+ </div>
253
+ </div>
254
+
255
+ <!-- Constrained Resize -->
256
+ <div class="demo-section">
257
+ <h2>🚧 Constrained Resize</h2>
258
+ <p>
259
+ Resize with minimum and maximum size constraints. Try resizing beyond
260
+ the limits!
261
+ </p>
262
+
263
+ <div
264
+ class="resizable constrained-resize"
265
+ id="constrained1"
266
+ data-min-width="120"
267
+ data-max-width="400"
268
+ data-min-height="80"
269
+ data-max-height="300"
270
+ >
271
+ <div>Min: 120×80<br />Max: 400×300</div>
272
+ <div class="resize-handle right" data-direction="horizontal"></div>
273
+ <div class="resize-handle bottom" data-direction="vertical"></div>
274
+ <div class="resize-handle corner" data-direction="both"></div>
275
+ </div>
276
+
277
+ <div
278
+ class="resizable constrained-resize"
279
+ id="constrained2"
280
+ data-min-width="100"
281
+ data-max-width="300"
282
+ data-min-height="100"
283
+ data-max-height="200"
284
+ >
285
+ <div>Min: 100×100<br />Max: 300×200</div>
286
+ <div class="resize-handle right" data-direction="horizontal"></div>
287
+ <div class="resize-handle bottom" data-direction="vertical"></div>
288
+ <div class="resize-handle corner" data-direction="both"></div>
289
+ </div>
290
+ </div>
291
+
292
+ <!-- Obstacle Resize -->
293
+ <div class="demo-section">
294
+ <h2>🚫 Obstacle Avoidance</h2>
295
+ <p>
296
+ Resize elements that cannot overlap with obstacles (red boxes). The
297
+ drag system prevents collision!
298
+ </p>
299
+
300
+ <div style="position: relative; height: 400px">
301
+ <div
302
+ class="obstacle"
303
+ style="
304
+ position: absolute;
305
+ top: 50px;
306
+ right: 50px;
307
+ width: 100px;
308
+ height: 80px;
309
+ "
310
+ >
311
+ Obstacle A
312
+ </div>
313
+
314
+ <div
315
+ class="obstacle"
316
+ style="
317
+ position: absolute;
318
+ bottom: 50px;
319
+ left: 200px;
320
+ width: 120px;
321
+ height: 60px;
322
+ "
323
+ >
324
+ Obstacle B
325
+ </div>
326
+
327
+ <div
328
+ class="resizable"
329
+ id="obstacle-resize"
330
+ style="
331
+ position: absolute;
332
+ top: 200px;
333
+ left: 50px;
334
+ width: 150px;
335
+ height: 100px;
336
+ "
337
+ >
338
+ <div>Try to resize me!</div>
339
+ <div class="resize-handle right" data-direction="horizontal"></div>
340
+ <div class="resize-handle bottom" data-direction="vertical"></div>
341
+ <div class="resize-handle corner" data-direction="both"></div>
342
+ </div>
343
+ </div>
344
+ </div>
345
+
346
+ <!-- Scroll Resize -->
347
+ <div class="demo-section">
348
+ <h2>🖱️ Scroll Resize</h2>
349
+ <p>
350
+ Hold and drag with scroll wheel for precise resizing. Great for
351
+ fine-tuning dimensions!
352
+ </p>
353
+
354
+ <div
355
+ class="resizable"
356
+ id="scroll-resize"
357
+ style="width: 200px; height: 150px; margin: 20px"
358
+ >
359
+ <div>Scroll + Drag Resize</div>
360
+ <div class="resize-handle right" data-direction="horizontal"></div>
361
+ <div class="resize-handle bottom" data-direction="vertical"></div>
362
+ <div class="resize-handle corner" data-direction="both"></div>
363
+ </div>
364
+ </div>
365
+
366
+ <!-- Controls -->
367
+ <div class="controls">
368
+ <button onclick="resetAllSizes()">🔄 Reset All Sizes</button>
369
+ <button onclick="toggleConstraints()">🚧 Toggle Constraints</button>
370
+ <button onclick="addObstacle()">➕ Add Obstacle</button>
371
+ <button onclick="toggleScrollMode()">🖱️ Toggle Scroll Mode</button>
372
+ </div>
373
+ </div>
374
+
375
+ <!-- Status Display -->
376
+ <div class="status" id="status">
377
+ <div><strong>Resize Status</strong></div>
378
+ <div id="statusContent">Ready to resize...</div>
379
+ </div>
380
+
381
+ <!-- Constraint Feedback -->
382
+ <div class="constraint-feedback" id="feedback"></div>
383
+
384
+ <script type="module">
385
+ import { createDragGestureController } from "./drag_gesture.js";
386
+
387
+ // Global state
388
+ let constraintsEnabled = true;
389
+ let scrollModeEnabled = true;
390
+ let activeResizes = new Map();
391
+
392
+ // Status update
393
+ function updateStatus(content) {
394
+ document.getElementById("statusContent").innerHTML = content;
395
+ }
396
+
397
+ // Initialize resize functionality
398
+ function initializeResize(element) {
399
+ const handles = element.querySelectorAll(".resize-handle");
400
+
401
+ handles.forEach((handle) => {
402
+ const direction = handle.dataset.direction;
403
+
404
+ // Setup drag for resize handle
405
+ const cleanup = createDragGestureController({
406
+ onGrab: () => {
407
+ // Handle can be grabbed
408
+ return {
409
+ element, // The element that will be visually impacted
410
+ elementToImpact: element, // The element that will receive position changes
411
+ };
412
+ },
413
+
414
+ onDragStart: () => {
415
+ handle.classList.add("dragging");
416
+ element.classList.add("dragging");
417
+
418
+ const rect = element.getBoundingClientRect();
419
+ activeResizes.set(element.id, {
420
+ startWidth: rect.width,
421
+ startHeight: rect.height,
422
+ startX: rect.left,
423
+ startY: rect.top,
424
+ direction,
425
+ });
426
+
427
+ updateStatus(
428
+ `Resizing ${element.id}<br>Direction: ${direction}<br>Start: ${Math.round(rect.width)}×${Math.round(rect.height)}`,
429
+ );
430
+ },
431
+
432
+ onDrag: (info) => {
433
+ const resizeInfo = activeResizes.get(element.id);
434
+ if (!resizeInfo) return;
435
+
436
+ let newWidth = resizeInfo.startWidth;
437
+ let newHeight = resizeInfo.startHeight;
438
+
439
+ // Calculate new dimensions based on direction
440
+ if (direction === "horizontal" || direction === "both") {
441
+ newWidth = Math.max(
442
+ parseInt(element.dataset.minWidth) || 50,
443
+ Math.min(
444
+ parseInt(element.dataset.maxWidth) || 800,
445
+ resizeInfo.startWidth + info.xFromStart,
446
+ ),
447
+ );
448
+ }
449
+
450
+ if (direction === "vertical" || direction === "both") {
451
+ newHeight = Math.max(
452
+ parseInt(element.dataset.minHeight) || 30,
453
+ Math.min(
454
+ parseInt(element.dataset.maxHeight) || 600,
455
+ resizeInfo.startHeight + info.yFromStart,
456
+ ),
457
+ );
458
+ }
459
+
460
+ // Apply new dimensions
461
+ element.style.width = `${newWidth}px`;
462
+ element.style.height = `${newHeight}px`;
463
+
464
+ updateStatus(
465
+ `Resizing ${element.id}<br>Direction: ${direction}<br>Size: ${Math.round(newWidth)}×${Math.round(newHeight)}<br>Delta: ${Math.round(info.xFromStart)}, ${Math.round(info.yFromStart)}`,
466
+ );
467
+ },
468
+
469
+ onRelease: () => {
470
+ handle.classList.remove("dragging");
471
+ element.classList.remove("dragging");
472
+
473
+ const resizeInfo = activeResizes.get(element.id);
474
+ if (resizeInfo) {
475
+ const rect = element.getBoundingClientRect();
476
+ updateStatus(
477
+ `Resize Complete!<br>Element: ${element.id}<br>Final Size: ${Math.round(rect.width)}×${Math.round(rect.height)}<br>Total Change: ${Math.round(rect.width - resizeInfo.startWidth)}×${Math.round(rect.height - resizeInfo.startHeight)}`,
478
+ );
479
+ activeResizes.delete(element.id);
480
+ }
481
+ },
482
+ });
483
+
484
+ // Add drag gesture to handle
485
+ handle.addEventListener("mousedown", cleanup);
486
+
487
+ // Store cleanup function
488
+ handle._dragCleanup = cleanup;
489
+ });
490
+ }
491
+
492
+ // Initialize all resizable elements
493
+ document.querySelectorAll(".resizable").forEach(initializeResize);
494
+
495
+ // Global functions for controls
496
+ window.resetAllSizes = function () {
497
+ document.querySelectorAll(".resizable").forEach((element) => {
498
+ if (element.classList.contains("basic-resize")) {
499
+ element.style.width = "200px";
500
+ element.style.height = "150px";
501
+ } else if (element.classList.contains("constrained-resize")) {
502
+ element.style.width = "180px";
503
+ element.style.height = "120px";
504
+ }
505
+ });
506
+ updateStatus("All sizes reset!");
507
+ };
508
+
509
+ window.toggleConstraints = function () {
510
+ constraintsEnabled = !constraintsEnabled;
511
+ updateStatus(
512
+ `Constraints ${constraintsEnabled ? "enabled" : "disabled"}`,
513
+ );
514
+
515
+ // Reinitialize all resizable elements with new constraint settings
516
+ document.querySelectorAll(".resize-handle").forEach((handle) => {
517
+ if (handle._dragCleanup) {
518
+ handle._dragCleanup();
519
+ }
520
+ });
521
+ document.querySelectorAll(".resizable").forEach(initializeResize);
522
+ };
523
+
524
+ window.toggleScrollMode = function () {
525
+ scrollModeEnabled = !scrollModeEnabled;
526
+ updateStatus(
527
+ `Scroll mode ${scrollModeEnabled ? "enabled" : "disabled"}`,
528
+ );
529
+
530
+ // Reinitialize all resizable elements
531
+ document.querySelectorAll(".resize-handle").forEach((handle) => {
532
+ if (handle._dragCleanup) {
533
+ handle._dragCleanup();
534
+ }
535
+ });
536
+ document.querySelectorAll(".resizable").forEach(initializeResize);
537
+ };
538
+
539
+ window.addObstacle = function () {
540
+ const section = document.querySelector(
541
+ ".demo-section:nth-of-type(3) > div",
542
+ );
543
+ const obstacle = document.createElement("div");
544
+ obstacle.className = "obstacle";
545
+ obstacle.style.cssText = `
546
+ position: absolute;
547
+ top: ${Math.random() * 200 + 50}px;
548
+ left: ${Math.random() * 300 + 100}px;
549
+ width: 80px;
550
+ height: 60px;
551
+ `;
552
+ obstacle.textContent = `Obstacle ${document.querySelectorAll(".obstacle").length + 1}`;
553
+ section.appendChild(obstacle);
554
+
555
+ updateStatus("New obstacle added!");
556
+
557
+ // Reinitialize obstacle-aware resize
558
+ const obstacleResize = document.getElementById("obstacle-resize");
559
+ const handles = obstacleResize.querySelectorAll(".resize-handle");
560
+ handles.forEach((handle) => {
561
+ if (handle._dragCleanup) {
562
+ handle._dragCleanup();
563
+ }
564
+ });
565
+ initializeResize(obstacleResize);
566
+ };
567
+
568
+ updateStatus("Drag resize demo loaded!<br>Try resizing the boxes above.");
569
+ </script>
570
+ </body>
571
+ </html>