@jsenv/dom 0.6.1 → 0.7.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 (109) hide show
  1. package/dist/jsenv_dom.js +259 -314
  2. package/package.json +2 -4
  3. package/index.js +0 -124
  4. package/src/attr/add_attribute_effect.js +0 -93
  5. package/src/attr/attributes.js +0 -32
  6. package/src/color/color_constrast.js +0 -69
  7. package/src/color/color_parsing.js +0 -319
  8. package/src/color/color_scheme.js +0 -28
  9. package/src/color/pick_light_or_dark.js +0 -34
  10. package/src/color/resolve_css_color.js +0 -60
  11. package/src/demos/3_columns_resize_demo.html +0 -84
  12. package/src/demos/3_rows_resize_demo.html +0 -89
  13. package/src/demos/aside_and_main_demo.html +0 -93
  14. package/src/demos/coordinates_demo.html +0 -450
  15. package/src/demos/document_autoscroll_demo.html +0 -517
  16. package/src/demos/drag_gesture_constraints_demo.html +0 -701
  17. package/src/demos/drag_gesture_demo.html +0 -1047
  18. package/src/demos/drag_gesture_element_to_impact_demo.html +0 -445
  19. package/src/demos/drag_reference_element_demo.html +0 -480
  20. package/src/demos/flex_details_set_demo.html +0 -302
  21. package/src/demos/flex_details_set_demo_2.html +0 -315
  22. package/src/demos/visible_rect_demo.html +0 -525
  23. package/src/element_signature.js +0 -100
  24. package/src/interaction/drag/constraint_feedback_line.js +0 -92
  25. package/src/interaction/drag/drag_constraint.js +0 -659
  26. package/src/interaction/drag/drag_debug_markers.js +0 -635
  27. package/src/interaction/drag/drag_element_positioner.js +0 -382
  28. package/src/interaction/drag/drag_gesture.js +0 -566
  29. package/src/interaction/drag/drag_resize_demo.html +0 -571
  30. package/src/interaction/drag/drag_to_move.js +0 -301
  31. package/src/interaction/drag/drag_to_resize_gesture.js +0 -68
  32. package/src/interaction/drag/drop_target_detection.js +0 -148
  33. package/src/interaction/drag/sticky_frontiers.js +0 -160
  34. package/src/interaction/event_marker.js +0 -14
  35. package/src/interaction/focus/active_element.js +0 -33
  36. package/src/interaction/focus/arrow_navigation.js +0 -599
  37. package/src/interaction/focus/element_is_focusable.js +0 -57
  38. package/src/interaction/focus/element_visibility.js +0 -111
  39. package/src/interaction/focus/find_focusable.js +0 -21
  40. package/src/interaction/focus/focus_group.js +0 -91
  41. package/src/interaction/focus/focus_group_registry.js +0 -12
  42. package/src/interaction/focus/focus_nav.js +0 -12
  43. package/src/interaction/focus/focus_nav_event_marker.js +0 -14
  44. package/src/interaction/focus/focus_trap.js +0 -105
  45. package/src/interaction/focus/tab_navigation.js +0 -128
  46. package/src/interaction/focus/tests/focus_group_skip_tab_test.html +0 -206
  47. package/src/interaction/focus/tests/tree_focus_test.html +0 -304
  48. package/src/interaction/focus/tests/tree_focus_test.jsx +0 -261
  49. package/src/interaction/focus/tests/tree_focus_test_preact.html +0 -13
  50. package/src/interaction/isolate_interactions.js +0 -161
  51. package/src/interaction/keyboard.js +0 -26
  52. package/src/interaction/scroll/capture_scroll.js +0 -47
  53. package/src/interaction/scroll/is_scrollable.js +0 -159
  54. package/src/interaction/scroll/scroll_container.js +0 -110
  55. package/src/interaction/scroll/scroll_trap.js +0 -44
  56. package/src/interaction/scroll/scrollbar_size.js +0 -20
  57. package/src/interaction/scroll/wheel_through.js +0 -138
  58. package/src/iterable_weak_set.js +0 -66
  59. package/src/position/dom_coords.js +0 -340
  60. package/src/position/offset_parent.js +0 -15
  61. package/src/position/position_fixed.js +0 -15
  62. package/src/position/position_sticky.js +0 -213
  63. package/src/position/sticky_rect.js +0 -79
  64. package/src/position/visible_rect.js +0 -486
  65. package/src/pub_sub.js +0 -31
  66. package/src/size/can_take_size.js +0 -11
  67. package/src/size/details_content_full_height.js +0 -63
  68. package/src/size/flex_details_set.js +0 -974
  69. package/src/size/get_available_height.js +0 -22
  70. package/src/size/get_available_width.js +0 -22
  71. package/src/size/get_border_sizes.js +0 -14
  72. package/src/size/get_height.js +0 -4
  73. package/src/size/get_inner_height.js +0 -15
  74. package/src/size/get_inner_width.js +0 -15
  75. package/src/size/get_margin_sizes.js +0 -10
  76. package/src/size/get_max_height.js +0 -57
  77. package/src/size/get_max_width.js +0 -47
  78. package/src/size/get_min_height.js +0 -14
  79. package/src/size/get_min_width.js +0 -14
  80. package/src/size/get_padding_sizes.js +0 -10
  81. package/src/size/get_width.js +0 -4
  82. package/src/size/hooks/use_available_height.js +0 -27
  83. package/src/size/hooks/use_available_width.js +0 -27
  84. package/src/size/hooks/use_max_height.js +0 -10
  85. package/src/size/hooks/use_max_width.js +0 -10
  86. package/src/size/hooks/use_resize_status.js +0 -62
  87. package/src/size/resize.js +0 -695
  88. package/src/size/resolve_css_size.js +0 -32
  89. package/src/style/dom_styles.js +0 -97
  90. package/src/style/style_composition.js +0 -121
  91. package/src/style/style_controller.js +0 -345
  92. package/src/style/style_default.js +0 -153
  93. package/src/style/style_default_demo.html +0 -128
  94. package/src/style/style_parsing.js +0 -375
  95. package/src/transition/demos/animation_resumption_test.xhtml +0 -500
  96. package/src/transition/demos/height_toggle_test.xhtml +0 -515
  97. package/src/transition/dom_transition.js +0 -254
  98. package/src/transition/easing.js +0 -48
  99. package/src/transition/group_transition.js +0 -261
  100. package/src/transition/transform_style_parser.js +0 -32
  101. package/src/transition/transition_playback.js +0 -366
  102. package/src/transition/transition_timeline.js +0 -79
  103. package/src/traversal.js +0 -247
  104. package/src/ui_transition/demos/content_states_transition_demo.html +0 -628
  105. package/src/ui_transition/demos/smooth_height_transition_demo.html +0 -149
  106. package/src/ui_transition/demos/transition_testing.html +0 -354
  107. package/src/ui_transition/ui_transition.js +0 -1470
  108. package/src/utils.js +0 -69
  109. package/src/value_effect.js +0 -35
@@ -1,525 +0,0 @@
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>
@@ -1,100 +0,0 @@
1
- /**
2
- * Generates a unique signature for various types of elements that can be used for identification in logs.
3
- *
4
- * This function handles different types of elements and returns an appropriate identifier:
5
- * - For DOM elements: Creates a CSS selector using tag name, data-ui-name, ID, classes, or parent hierarchy
6
- * - For React/Preact elements (JSX): Returns JSX-like representation with type and props
7
- * - For functions: Returns function name and optional underlying element reference in brackets
8
- * - For null/undefined: Returns the string representation
9
- *
10
- * The returned signature for DOM elements is a valid CSS selector that can be copy-pasted
11
- * into browser dev tools to locate the element in the DOM.
12
- *
13
- * @param {HTMLElement|Object|Function|null|undefined} element - The element to generate a signature for
14
- * @returns {string} A unique identifier string in various formats depending on element type
15
- *
16
- * @example
17
- * // For DOM element with data-ui-name
18
- * // <div data-ui-name="header">
19
- * getElementSignature(element) // Returns: `div[data-ui-name="header"]`
20
- *
21
- * @example
22
- * // For DOM element with ID
23
- * // <div id="main" class="container active">
24
- * getElementSignature(element) // Returns: "div#main"
25
- *
26
- * @example
27
- * // For DOM element with classes only
28
- * // <button class="btn primary">
29
- * getElementSignature(element) // Returns: "button.btn.primary"
30
- *
31
- * @example
32
- * // For DOM element without distinguishing features (uses parent hierarchy)
33
- * // <p> inside <section id="content">
34
- * getElementSignature(element) // Returns: "section#content > p"
35
- *
36
- * @example
37
- * // For React/Preact element with props
38
- * // <MyComponent id="widget" />
39
- * getElementSignature(element) // Returns: `<MyComponent id="widget" />`
40
- *
41
- * @example
42
- * // For named function with underlying element reference
43
- * const MyComponent = () => {}; MyComponent.underlyingElementId = "div#main";
44
- * getElementSignature(MyComponent) // Returns: "[function MyComponent for div#main]"
45
- *
46
- * @example
47
- * // For anonymous function without underlying element
48
- * const anonymousFunc = () => {};
49
- * getElementSignature(anonymousFunc) // Returns: "[function]"
50
- *
51
- * @example
52
- * // For named function without underlying element
53
- * function namedHandler() {}
54
- * getElementSignature(namedHandler) // Returns: "[function namedHandler]"
55
- *
56
- * @example
57
- * // For null/undefined
58
- * getElementSignature(null) // Returns: "null"
59
- */
60
- export const getElementSignature = (element) => {
61
- if (!element) {
62
- return String(element);
63
- }
64
- if (typeof element === "function") {
65
- const functionName = element.name;
66
- const functionLabel = functionName
67
- ? `function ${functionName}`
68
- : "function";
69
- const underlyingElementId = element.underlyingElementId;
70
- if (underlyingElementId) {
71
- return `[${functionLabel} for ${underlyingElementId}]`;
72
- }
73
- return `[${functionLabel}]`;
74
- }
75
- if (element.props) {
76
- const type = element.type;
77
- const id = element.props.id;
78
- if (id) {
79
- return `<${type} id="${id}" />`;
80
- }
81
- return `<${type} />`;
82
- }
83
-
84
- const tagName = element.tagName.toLowerCase();
85
- const dataUIName = element.getAttribute("data-ui-name");
86
- if (dataUIName) {
87
- return `${tagName}[data-ui-name="${dataUIName}"]`;
88
- }
89
- const elementId = element.id;
90
- if (elementId) {
91
- return `${tagName}#${elementId}`;
92
- }
93
- const className = element.className;
94
- if (className) {
95
- return `${tagName}.${className.split(" ").join(".")}`;
96
- }
97
-
98
- const parentSignature = getElementSignature(element.parentElement);
99
- return `${parentSignature} > ${tagName}`;
100
- };