@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,525 @@
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>Visible Rect Demo</title>
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ padding: 20px;
11
+ font-family: Arial, sans-serif;
12
+ background: #f5f5f5;
13
+ min-height: 300vh; /* Make page tall enough to scroll */
14
+ min-width: 1200px; /* Force horizontal scrolling */
15
+ }
16
+
17
+ .demo-header {
18
+ background: white;
19
+ padding: 20px;
20
+ border-radius: 8px;
21
+ margin-bottom: 20px;
22
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
23
+ }
24
+
25
+ .demo-title {
26
+ margin: 0 0 10px 0;
27
+ font-size: 24px;
28
+ color: #333;
29
+ }
30
+
31
+ .demo-description {
32
+ margin: 0;
33
+ color: #666;
34
+ line-height: 1.5;
35
+ }
36
+
37
+ .scrollable-container {
38
+ padding: 20px;
39
+ /* width/height will be set dynamically when overflow: auto */
40
+ }
41
+
42
+ .scrollable-container.custom-mode {
43
+ max-width: 600px;
44
+ max-height: 400px;
45
+ }
46
+
47
+ .scrollable-content {
48
+ position: relative;
49
+ }
50
+
51
+ .target-row {
52
+ display: flex;
53
+ gap: 20px;
54
+ align-items: flex-start;
55
+ margin: 50px 0;
56
+ }
57
+
58
+ .side-content {
59
+ flex: 0 0 200px;
60
+ padding: 20px;
61
+ background: rgba(0, 0, 0, 0.05);
62
+ border-radius: 8px;
63
+ min-height: 150px;
64
+ }
65
+
66
+ .side-content h4 {
67
+ margin: 0 0 10px 0;
68
+ font-size: 14px;
69
+ color: #333;
70
+ }
71
+
72
+ .side-content p {
73
+ margin: 0;
74
+ font-size: 13px;
75
+ color: #666;
76
+ line-height: 1.4;
77
+ }
78
+
79
+ .target-element {
80
+ flex: 1;
81
+ width: 400px;
82
+ min-width: 400px;
83
+ height: 600px;
84
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
85
+ border: 15px solid #333;
86
+ display: flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ color: white;
90
+ font-weight: bold;
91
+ font-size: 18px;
92
+ text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
93
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
94
+ }
95
+
96
+ .overlay-ui {
97
+ position: fixed;
98
+ z-index: 1000;
99
+ overflow: hidden;
100
+ inset: 0;
101
+ pointer-events: none;
102
+ background: rgba(
103
+ 0,
104
+ 0,
105
+ 255,
106
+ 0.05
107
+ ); /* Discrete blue background to make it visible */
108
+ }
109
+
110
+ .overlay-container {
111
+ position: absolute;
112
+ background: rgba(255, 0, 0, 0.5);
113
+ border: 2px dashed white;
114
+ box-sizing: border-box; /* Ensure border is included in size */
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ color: white;
119
+ font-weight: bold;
120
+ text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
121
+ font-size: 12px;
122
+ /* Only transition visual properties, not positioning */
123
+ transition:
124
+ background-color 0.2s ease,
125
+ border-color 0.2s ease;
126
+ }
127
+
128
+ /* Dynamic styling based on visibility ratio */
129
+ .overlay-container.high-visibility {
130
+ background: rgba(0, 255, 0, 0.6);
131
+ border-color: #00ff00;
132
+ }
133
+
134
+ .overlay-container.medium-visibility {
135
+ background: rgba(255, 165, 0, 0.6);
136
+ border-color: #ffa500;
137
+ }
138
+
139
+ .overlay-container.low-visibility {
140
+ background: rgba(255, 0, 0, 0.6);
141
+ border-color: #ff0000;
142
+ }
143
+
144
+ .debug-info {
145
+ position: fixed;
146
+ top: 100px;
147
+ right: 10px;
148
+ background: rgba(0, 0, 0, 0.9);
149
+ color: white;
150
+ padding: 15px;
151
+ border-radius: 8px;
152
+ font-family: monospace;
153
+ font-size: 11px;
154
+ z-index: 2000;
155
+ max-width: 300px;
156
+ line-height: 1.4;
157
+ }
158
+
159
+ .debug-info h4 {
160
+ margin: 0 0 10px 0;
161
+ font-size: 12px;
162
+ color: #4caf50;
163
+ }
164
+
165
+ .debug-info .section {
166
+ margin-bottom: 10px;
167
+ padding-bottom: 8px;
168
+ border-bottom: 1px solid #333;
169
+ }
170
+
171
+ .debug-info .section:last-child {
172
+ border-bottom: none;
173
+ margin-bottom: 0;
174
+ }
175
+
176
+ .content-section {
177
+ background: white;
178
+ margin: 40px 0;
179
+ padding: 40px;
180
+ border-radius: 8px;
181
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
182
+ text-align: center;
183
+ min-width: 1000px; /* Force horizontal scrolling */
184
+ }
185
+
186
+ .content-section:nth-child(even) {
187
+ background: linear-gradient(135deg, #e3f2fd, #bbdefb);
188
+ color: #1976d2;
189
+ }
190
+
191
+ .content-section:nth-child(odd) {
192
+ background: linear-gradient(135deg, #f3e5f5, #ce93d8);
193
+ color: #7b1fa2;
194
+ }
195
+
196
+ .scroll-indicator {
197
+ position: fixed;
198
+ top: 10px;
199
+ right: 10px;
200
+ background: rgba(0, 0, 0, 0.8);
201
+ color: white;
202
+ padding: 10px;
203
+ border-radius: 4px;
204
+ font-family: monospace;
205
+ font-size: 12px;
206
+ z-index: 2000;
207
+ }
208
+
209
+ .controls {
210
+ background: white;
211
+ padding: 15px;
212
+ border-radius: 8px;
213
+ margin-bottom: 20px;
214
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
215
+ max-width: 400px;
216
+ }
217
+
218
+ .controls h3 {
219
+ margin: 0 0 15px 0;
220
+ font-size: 16px;
221
+ color: #333;
222
+ }
223
+
224
+ .controls button {
225
+ padding: 10px 15px;
226
+ background: #4caf50;
227
+ color: white;
228
+ border: none;
229
+ border-radius: 4px;
230
+ cursor: pointer;
231
+ font-size: 14px;
232
+ font-weight: bold;
233
+ margin-right: 10px;
234
+ }
235
+
236
+ .controls button:hover {
237
+ background: #45a049;
238
+ }
239
+
240
+ .controls button.active {
241
+ background: #2196f3;
242
+ }
243
+
244
+ .mode-status {
245
+ margin-top: 10px;
246
+ padding: 8px 12px;
247
+ background: #f0f0f0;
248
+ border-radius: 4px;
249
+ font-size: 12px;
250
+ color: #666;
251
+ font-style: italic;
252
+ }
253
+ </style>
254
+ </head>
255
+ <body>
256
+ <div class="demo-header">
257
+ <h1 class="demo-title">Visible Rect Demo</h1>
258
+ <p class="demo-description">
259
+ This demo shows how the <code>visibleRectEffect</code> API tracks the
260
+ visible portion of an element with two different visibility ratios:
261
+ <br />• <strong>Document Visibility:</strong> How much of the element is
262
+ visible in the browser viewport <br />•
263
+ <strong>Scroll Container Visibility:</strong> How much of the element is
264
+ visible within its scrollable parent <br /><strong
265
+ >Instructions:</strong
266
+ >
267
+ Scroll the page and watch how both ratios change. In custom container
268
+ mode, notice how scroll container visibility can be 100% while document
269
+ visibility is 0%.
270
+ </p>
271
+ </div>
272
+
273
+ <div class="controls">
274
+ <h3>Scrollable Container</h3>
275
+ <button id="documentBtn" class="active">Document</button>
276
+ <button id="customBtn">Custom Container</button>
277
+ <div class="mode-status" id="modeStatus">Current: Document scrolling</div>
278
+ </div>
279
+
280
+ <div class="scroll-indicator" id="scrollIndicator">Scroll: 0px</div>
281
+
282
+ <!-- Debug info panel -->
283
+ <div class="debug-info" id="debugInfo">
284
+ <h4>Visible Rect API Data</h4>
285
+ <div class="section">
286
+ <strong>Visible Rect:</strong><br />
287
+ <span id="visibleRect">No data</span>
288
+ </div>
289
+ <div class="section">
290
+ <strong>Element Full Size:</strong><br />
291
+ <span id="fullSize">No data</span>
292
+ </div>
293
+ <div class="section">
294
+ <strong>Document Visibility:</strong><br />
295
+ <span id="visibilityRatio">No data</span>
296
+ </div>
297
+ <div class="section">
298
+ <strong>Scroll Container Visibility:</strong><br />
299
+ <span id="scrollVisibilityRatio">No data</span>
300
+ </div>
301
+ <div class="section">
302
+ <strong>Visibility Status:</strong><br />
303
+ <span id="visibility">No data</span>
304
+ </div>
305
+ </div>
306
+
307
+ <!-- Single container that can switch between document and custom scrolling -->
308
+ <div class="scrollable-container" id="scrollableContainer">
309
+ <div class="scrollable-content">
310
+ <h3>Scrollable Content Area</h3>
311
+ <p>Content above the target element</p>
312
+
313
+ <div class="target-row">
314
+ <div class="side-content">
315
+ <h4>Left Content</h4>
316
+ <p>
317
+ This is some content on the left side of the target element. It
318
+ helps demonstrate how the overlay system works when the target is
319
+ part of a more complex layout.
320
+ </p>
321
+ <p>Additional content to make this section taller.</p>
322
+ </div>
323
+
324
+ <div class="target-element" id="targetElement">Target Element</div>
325
+
326
+ <div class="side-content">
327
+ <h4>Right Content</h4>
328
+ <p>
329
+ This is content on the right side of the target element. The
330
+ flexbox layout ensures proper alignment while maintaining the
331
+ overlay functionality.
332
+ </p>
333
+ <p>More content to balance the layout.</p>
334
+ </div>
335
+ </div>
336
+
337
+ <p>Content below target element</p>
338
+ </div>
339
+ </div>
340
+
341
+ <!-- Overlay UI showing the visible rect -->
342
+ <div class="overlay-ui" id="overlayUI" style="display: none">
343
+ <div class="overlay-container" id="overlayContainer">VISIBLE RECT</div>
344
+ </div>
345
+
346
+ <script type="module">
347
+ import { visibleRectEffect } from "../position/visible_rect.js";
348
+
349
+ const targetElement = document.getElementById("targetElement");
350
+ const scrollableContainer = document.getElementById(
351
+ "scrollableContainer",
352
+ );
353
+ const overlayUI = document.getElementById("overlayUI");
354
+ const overlayContainer = document.getElementById("overlayContainer");
355
+ const scrollIndicator = document.getElementById("scrollIndicator");
356
+ const documentBtn = document.getElementById("documentBtn");
357
+ const customBtn = document.getElementById("customBtn");
358
+ const modeStatus = document.getElementById("modeStatus");
359
+ const visibleRectSpan = document.getElementById("visibleRect");
360
+ const fullSizeSpan = document.getElementById("fullSize");
361
+ const visibilityRatioSpan = document.getElementById("visibilityRatio");
362
+ const scrollVisibilityRatioSpan = document.getElementById(
363
+ "scrollVisibilityRatio",
364
+ );
365
+ const visibilitySpan = document.getElementById("visibility");
366
+
367
+ let currentVisibleRectEffect = null;
368
+ const STORAGE_KEY = "overlay-demo-mode";
369
+
370
+ // Load saved mode from localStorage
371
+ const savedMode = localStorage.getItem(STORAGE_KEY) || "document";
372
+
373
+ // Update mode status display
374
+ function updateModeStatus(mode) {
375
+ if (mode === "document") {
376
+ modeStatus.textContent = "Current: Document scrolling";
377
+ } else {
378
+ modeStatus.textContent = "Current: Custom container scrolling";
379
+ }
380
+ }
381
+
382
+ // Update scroll indicator
383
+ const updateScrollIndicator = () => {
384
+ const scrollY = window.scrollY || document.documentElement.scrollTop;
385
+ const scrollX = window.scrollX || document.documentElement.scrollLeft;
386
+ scrollIndicator.textContent = `Scroll: ${Math.round(scrollX)}px, ${Math.round(scrollY)}px`;
387
+ };
388
+
389
+ // Initialize scroll indicator
390
+ updateScrollIndicator();
391
+ window.addEventListener("scroll", updateScrollIndicator);
392
+
393
+ // Initialize overlay
394
+ function initializeOverlay() {
395
+ if (currentVisibleRectEffect) {
396
+ currentVisibleRectEffect.disconnect();
397
+ }
398
+
399
+ overlayUI.style.display = "block";
400
+
401
+ currentVisibleRectEffect = visibleRectEffect(
402
+ targetElement,
403
+ (visibleRect, fullRect) => {
404
+ // Update overlay container position and size
405
+ overlayContainer.style.left = `${visibleRect.left}px`;
406
+ overlayContainer.style.top = `${visibleRect.top}px`;
407
+ overlayContainer.style.width = `${visibleRect.width}px`;
408
+ overlayContainer.style.height = `${visibleRect.height}px`;
409
+
410
+ // Hide overlay if element is not visible
411
+ if (visibleRect.width <= 0 || visibleRect.height <= 0) {
412
+ overlayContainer.style.display = "none";
413
+ } else {
414
+ overlayContainer.style.display = "flex";
415
+ }
416
+
417
+ // Update dynamic styling based on visibility ratio
418
+ overlayContainer.classList.remove(
419
+ "high-visibility",
420
+ "medium-visibility",
421
+ "low-visibility",
422
+ );
423
+ let visibilityClass = "low-visibility";
424
+ let overlayText = "VISIBLE RECT";
425
+
426
+ if (visibleRect.visibilityRatio >= 0.8) {
427
+ visibilityClass = "high-visibility";
428
+ overlayText = "FULLY VISIBLE";
429
+ } else if (visibleRect.visibilityRatio >= 0.3) {
430
+ visibilityClass = "medium-visibility";
431
+ overlayText = "PARTIALLY VISIBLE";
432
+ } else if (visibleRect.visibilityRatio > 0) {
433
+ visibilityClass = "low-visibility";
434
+ overlayText = "BARELY VISIBLE";
435
+ }
436
+
437
+ overlayContainer.classList.add(visibilityClass);
438
+ overlayContainer.textContent = overlayText;
439
+
440
+ // Update debug information
441
+ visibleRectSpan.innerHTML = `
442
+ left: ${Math.round(visibleRect.left)}px<br>
443
+ top: ${Math.round(visibleRect.top)}px<br>
444
+ width: ${Math.round(visibleRect.width)}px<br>
445
+ height: ${Math.round(visibleRect.height)}px
446
+ `;
447
+
448
+ fullSizeSpan.innerHTML = `
449
+ width: ${Math.round(fullRect.width)}px<br>
450
+ height: ${Math.round(fullRect.height)}px
451
+ `;
452
+
453
+ // Display the visibility ratios from the API
454
+ visibilityRatioSpan.innerHTML = `
455
+ ${(visibleRect.visibilityRatio * 100).toFixed(1)}%<br>
456
+ <small>Document Ratio: ${visibleRect.visibilityRatio.toFixed(3)}</small>
457
+ `;
458
+
459
+ scrollVisibilityRatioSpan.innerHTML = `
460
+ ${(visibleRect.scrollVisibilityRatio * 100).toFixed(1)}%<br>
461
+ <small>Scroll Ratio: ${visibleRect.scrollVisibilityRatio.toFixed(3)}</small>
462
+ `;
463
+
464
+ const isVisible = visibleRect.width > 0 && visibleRect.height > 0;
465
+
466
+ visibilitySpan.innerHTML = `
467
+ ${isVisible ? "Visible" : "Hidden"}<br>
468
+ <small>${visibilityClass.replace("-", " ").toUpperCase()}</small>
469
+ `;
470
+
471
+ console.log("Visible rect updated:", { visibleRect, fullRect });
472
+ },
473
+ );
474
+
475
+ console.log("Visible rect effect initialized");
476
+ }
477
+
478
+ // Set document scrolling mode
479
+ function setDocumentScrolling() {
480
+ scrollableContainer.style.overflow = "visible";
481
+ scrollableContainer.classList.remove("custom-mode");
482
+ localStorage.setItem(STORAGE_KEY, "document");
483
+ updateModeStatus("document");
484
+ initializeOverlay();
485
+ console.log("Document scrolling mode activated");
486
+ }
487
+
488
+ // Set custom container scrolling mode
489
+ function setCustomContainerScrolling() {
490
+ scrollableContainer.style.overflow = "auto";
491
+ scrollableContainer.classList.add("custom-mode");
492
+ localStorage.setItem(STORAGE_KEY, "custom");
493
+ updateModeStatus("custom");
494
+ initializeOverlay();
495
+ console.log("Custom container scrolling mode activated");
496
+ }
497
+
498
+ // Button handlers
499
+ documentBtn.addEventListener("click", () => {
500
+ documentBtn.classList.add("active");
501
+ customBtn.classList.remove("active");
502
+ setDocumentScrolling();
503
+ });
504
+
505
+ customBtn.addEventListener("click", () => {
506
+ customBtn.classList.add("active");
507
+ documentBtn.classList.remove("active");
508
+ setCustomContainerScrolling();
509
+ });
510
+
511
+ // Initialize with saved mode
512
+ if (savedMode === "custom") {
513
+ customBtn.classList.add("active");
514
+ documentBtn.classList.remove("active");
515
+ setCustomContainerScrolling();
516
+ } else {
517
+ documentBtn.classList.add("active");
518
+ customBtn.classList.remove("active");
519
+ setDocumentScrolling();
520
+ }
521
+
522
+ console.log("Visible rect demo initialized");
523
+ </script>
524
+ </body>
525
+ </html>
@@ -0,0 +1,92 @@
1
+ export const setupConstraintFeedbackLine = () => {
2
+ const constraintFeedbackLine = createConstraintFeedbackLine();
3
+
4
+ // Track last known mouse position for constraint feedback line during scroll
5
+ let lastMouseX = null;
6
+ let lastMouseY = null;
7
+
8
+ // Internal function to update constraint feedback line
9
+ const onDrag = (gestureInfo) => {
10
+ const { grabEvent, dragEvent } = gestureInfo;
11
+ if (
12
+ grabEvent.type === "programmatic" ||
13
+ dragEvent.type === "programmatic"
14
+ ) {
15
+ // programmatic drag
16
+ return;
17
+ }
18
+
19
+ const mouseX = dragEvent.clientX;
20
+ const mouseY = dragEvent.clientY;
21
+ // Use last known position if current position not available (e.g., during scroll)
22
+ const effectiveMouseX = mouseX !== null ? mouseX : lastMouseX;
23
+ const effectiveMouseY = mouseY !== null ? mouseY : lastMouseY;
24
+ if (effectiveMouseX === null || effectiveMouseY === null) {
25
+ return;
26
+ }
27
+
28
+ // Store current mouse position for potential use during scroll
29
+ lastMouseX = mouseX;
30
+ lastMouseY = mouseY;
31
+
32
+ const grabClientX = grabEvent.clientX;
33
+ const grabClientY = grabEvent.clientY;
34
+
35
+ // Calculate distance between mouse and current grab point
36
+ const deltaX = effectiveMouseX - grabClientX;
37
+ const deltaY = effectiveMouseY - grabClientY;
38
+ const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
39
+ // Show line only when distance is significant (> 20px threshold)
40
+ const threshold = 20;
41
+ if (distance <= threshold) {
42
+ constraintFeedbackLine.style.opacity = "";
43
+ constraintFeedbackLine.removeAttribute("data-visible");
44
+ return;
45
+ }
46
+
47
+ // Calculate angle and position
48
+ const angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
49
+ // Position line at current grab point (follows element movement)
50
+ constraintFeedbackLine.style.left = `${grabClientX}px`;
51
+ constraintFeedbackLine.style.top = `${grabClientY}px`;
52
+ constraintFeedbackLine.style.width = `${distance}px`;
53
+ constraintFeedbackLine.style.transform = `rotate(${angle}deg)`;
54
+ // Fade in based on distance (more visible as distance increases)
55
+ const maxOpacity = 0.8;
56
+ const opacityFactor = Math.min((distance - threshold) / 100, 1);
57
+ constraintFeedbackLine.style.opacity = `${maxOpacity * opacityFactor}`;
58
+ constraintFeedbackLine.setAttribute("data-visible", "");
59
+ };
60
+
61
+ return {
62
+ onDrag,
63
+ onRelease: () => {
64
+ constraintFeedbackLine.remove();
65
+ },
66
+ };
67
+ };
68
+
69
+ const createConstraintFeedbackLine = () => {
70
+ const line = document.createElement("div");
71
+ line.className = "navi_constraint_feedback_line";
72
+ line.title =
73
+ "Constraint feedback - shows distance between mouse and moving grab point";
74
+ document.body.appendChild(line);
75
+ return line;
76
+ };
77
+
78
+ import.meta.css = /* css */ `
79
+ .navi_constraint_feedback_line {
80
+ position: fixed;
81
+ pointer-events: none;
82
+ z-index: 9998;
83
+ visibility: hidden;
84
+ transition: opacity 0.15s ease;
85
+ transform-origin: left center;
86
+ border-top: 2px dotted rgba(59, 130, 246, 0.7);
87
+ }
88
+
89
+ .navi_constraint_feedback_line[data-visible] {
90
+ visibility: visible;
91
+ }
92
+ `;