@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.
- package/dist/jsenv_dom.js +9653 -0
- package/index.js +101 -0
- package/package.json +47 -0
- package/src/attr/add_attribute_effect.js +93 -0
- package/src/attr/attributes.js +32 -0
- package/src/demos/3_columns_resize_demo.html +84 -0
- package/src/demos/3_rows_resize_demo.html +89 -0
- package/src/demos/aside_and_main_demo.html +93 -0
- package/src/demos/coordinates_demo.html +450 -0
- package/src/demos/document_autoscroll_demo.html +517 -0
- package/src/demos/drag_gesture_constraints_demo.html +701 -0
- package/src/demos/drag_gesture_demo.html +1047 -0
- package/src/demos/drag_gesture_element_to_impact_demo.html +445 -0
- package/src/demos/drag_reference_element_demo.html +480 -0
- package/src/demos/flex_details_set_demo.html +302 -0
- package/src/demos/flex_details_set_demo_2.html +315 -0
- package/src/demos/visible_rect_demo.html +525 -0
- package/src/interaction/drag/constraint_feedback_line.js +92 -0
- package/src/interaction/drag/drag_constraint.js +659 -0
- package/src/interaction/drag/drag_debug_markers.js +635 -0
- package/src/interaction/drag/drag_element_positioner.js +382 -0
- package/src/interaction/drag/drag_gesture.js +566 -0
- package/src/interaction/drag/drag_resize_demo.html +571 -0
- package/src/interaction/drag/drag_to_move.js +301 -0
- package/src/interaction/drag/drag_to_resize_gesture.js +68 -0
- package/src/interaction/drag/drop_target_detection.js +148 -0
- package/src/interaction/drag/sticky_frontiers.js +160 -0
- package/src/interaction/element_log.js +8 -0
- package/src/interaction/event_marker.js +14 -0
- package/src/interaction/focus/active_element.js +33 -0
- package/src/interaction/focus/arrow_navigation.js +599 -0
- package/src/interaction/focus/element_is_focusable.js +57 -0
- package/src/interaction/focus/element_is_visible.js +36 -0
- package/src/interaction/focus/find_focusable.js +21 -0
- package/src/interaction/focus/focus_group.js +91 -0
- package/src/interaction/focus/focus_group_registry.js +12 -0
- package/src/interaction/focus/focus_nav.js +12 -0
- package/src/interaction/focus/focus_nav_event_marker.js +14 -0
- package/src/interaction/focus/focus_trap.js +105 -0
- package/src/interaction/focus/tab_navigation.js +128 -0
- package/src/interaction/focus/tests/focus_group_skip_tab_test.html +206 -0
- package/src/interaction/focus/tests/tree_focus_test.html +304 -0
- package/src/interaction/focus/tests/tree_focus_test.jsx +261 -0
- package/src/interaction/focus/tests/tree_focus_test_preact.html +13 -0
- package/src/interaction/isolate_interactions.js +161 -0
- package/src/interaction/keyboard.js +26 -0
- package/src/interaction/scroll/capture_scroll.js +47 -0
- package/src/interaction/scroll/is_scrollable.js +159 -0
- package/src/interaction/scroll/scroll_container.js +110 -0
- package/src/interaction/scroll/scroll_trap.js +44 -0
- package/src/interaction/scroll/scrollbar_size.js +20 -0
- package/src/interaction/scroll/wheel_through.js +138 -0
- package/src/iterable_weak_set.js +66 -0
- package/src/position/dom_coords.js +340 -0
- package/src/position/offset_parent.js +15 -0
- package/src/position/position_fixed.js +15 -0
- package/src/position/position_sticky.js +213 -0
- package/src/position/sticky_rect.js +79 -0
- package/src/position/visible_rect.js +482 -0
- package/src/pub_sub.js +28 -0
- package/src/size/can_take_size.js +11 -0
- package/src/size/details_content_full_height.js +63 -0
- package/src/size/flex_details_set.js +974 -0
- package/src/size/get_available_height.js +22 -0
- package/src/size/get_available_width.js +22 -0
- package/src/size/get_border_sizes.js +14 -0
- package/src/size/get_height.js +4 -0
- package/src/size/get_inner_height.js +15 -0
- package/src/size/get_inner_width.js +15 -0
- package/src/size/get_margin_sizes.js +10 -0
- package/src/size/get_max_height.js +57 -0
- package/src/size/get_max_width.js +47 -0
- package/src/size/get_min_height.js +14 -0
- package/src/size/get_min_width.js +14 -0
- package/src/size/get_padding_sizes.js +10 -0
- package/src/size/get_width.js +4 -0
- package/src/size/hooks/use_available_height.js +27 -0
- package/src/size/hooks/use_available_width.js +27 -0
- package/src/size/hooks/use_max_height.js +10 -0
- package/src/size/hooks/use_max_width.js +10 -0
- package/src/size/hooks/use_resize_status.js +62 -0
- package/src/size/resize.js +695 -0
- package/src/size/resolve_css_size.js +32 -0
- package/src/style/dom_styles.js +97 -0
- package/src/style/style_composition.js +78 -0
- package/src/style/style_controller.js +345 -0
- package/src/style/style_parsing.js +317 -0
- package/src/transition/demos/animation_resumption_test.xhtml +500 -0
- package/src/transition/demos/height_toggle_test.xhtml +515 -0
- package/src/transition/dom_transition.js +254 -0
- package/src/transition/easing.js +48 -0
- package/src/transition/group_transition.js +261 -0
- package/src/transition/transform_style_parser.js +32 -0
- package/src/transition/transition_playback.js +366 -0
- package/src/transition/transition_timeline.js +79 -0
- package/src/traversal.js +247 -0
- package/src/ui_transition/demos/content_states_transition_demo.html +628 -0
- package/src/ui_transition/demos/smooth_height_transition_demo.html +149 -0
- package/src/ui_transition/demos/transition_testing.html +354 -0
- package/src/ui_transition/ui_transition.js +1492 -0
- 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
|
+
`;
|