@jsenv/dom 0.5.2 → 0.6.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 +161 -423
- package/index.js +3 -1
- package/package.json +1 -1
- package/src/element_signature.js +100 -0
- package/src/interaction/drag/drag_constraint.js +2 -2
- package/src/interaction/drag/sticky_frontiers.js +4 -4
- package/src/style/style_composition.js +23 -0
- package/src/style/style_parsing.js +11 -13
- package/src/ui_transition/ui_transition.js +4 -3
- package/src/value_effect.js +11 -7
- package/src/interaction/element_log.js +0 -8
package/dist/jsenv_dom.js
CHANGED
|
@@ -1,6 +1,107 @@
|
|
|
1
1
|
import { signal, effect } from "@preact/signals";
|
|
2
2
|
import { useState, useLayoutEffect } from "preact/hooks";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Generates a unique signature for various types of elements that can be used for identification in logs.
|
|
6
|
+
*
|
|
7
|
+
* This function handles different types of elements and returns an appropriate identifier:
|
|
8
|
+
* - For DOM elements: Creates a CSS selector using tag name, data-ui-name, ID, classes, or parent hierarchy
|
|
9
|
+
* - For React/Preact elements (JSX): Returns JSX-like representation with type and props
|
|
10
|
+
* - For functions: Returns function name and optional underlying element reference in brackets
|
|
11
|
+
* - For null/undefined: Returns the string representation
|
|
12
|
+
*
|
|
13
|
+
* The returned signature for DOM elements is a valid CSS selector that can be copy-pasted
|
|
14
|
+
* into browser dev tools to locate the element in the DOM.
|
|
15
|
+
*
|
|
16
|
+
* @param {HTMLElement|Object|Function|null|undefined} element - The element to generate a signature for
|
|
17
|
+
* @returns {string} A unique identifier string in various formats depending on element type
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // For DOM element with data-ui-name
|
|
21
|
+
* // <div data-ui-name="header">
|
|
22
|
+
* getElementSignature(element) // Returns: `div[data-ui-name="header"]`
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // For DOM element with ID
|
|
26
|
+
* // <div id="main" class="container active">
|
|
27
|
+
* getElementSignature(element) // Returns: "div#main"
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* // For DOM element with classes only
|
|
31
|
+
* // <button class="btn primary">
|
|
32
|
+
* getElementSignature(element) // Returns: "button.btn.primary"
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // For DOM element without distinguishing features (uses parent hierarchy)
|
|
36
|
+
* // <p> inside <section id="content">
|
|
37
|
+
* getElementSignature(element) // Returns: "section#content > p"
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // For React/Preact element with props
|
|
41
|
+
* // <MyComponent id="widget" />
|
|
42
|
+
* getElementSignature(element) // Returns: `<MyComponent id="widget" />`
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // For named function with underlying element reference
|
|
46
|
+
* const MyComponent = () => {}; MyComponent.underlyingElementId = "div#main";
|
|
47
|
+
* getElementSignature(MyComponent) // Returns: "[function MyComponent for div#main]"
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* // For anonymous function without underlying element
|
|
51
|
+
* const anonymousFunc = () => {};
|
|
52
|
+
* getElementSignature(anonymousFunc) // Returns: "[function]"
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // For named function without underlying element
|
|
56
|
+
* function namedHandler() {}
|
|
57
|
+
* getElementSignature(namedHandler) // Returns: "[function namedHandler]"
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* // For null/undefined
|
|
61
|
+
* getElementSignature(null) // Returns: "null"
|
|
62
|
+
*/
|
|
63
|
+
const getElementSignature = (element) => {
|
|
64
|
+
if (!element) {
|
|
65
|
+
return String(element);
|
|
66
|
+
}
|
|
67
|
+
if (typeof element === "function") {
|
|
68
|
+
const functionName = element.name;
|
|
69
|
+
const functionLabel = functionName
|
|
70
|
+
? `function ${functionName}`
|
|
71
|
+
: "function";
|
|
72
|
+
const underlyingElementId = element.underlyingElementId;
|
|
73
|
+
if (underlyingElementId) {
|
|
74
|
+
return `[${functionLabel} for ${underlyingElementId}]`;
|
|
75
|
+
}
|
|
76
|
+
return `[${functionLabel}]`;
|
|
77
|
+
}
|
|
78
|
+
if (element.props) {
|
|
79
|
+
const type = element.type;
|
|
80
|
+
const id = element.props.id;
|
|
81
|
+
if (id) {
|
|
82
|
+
return `<${type} id="${id}" />`;
|
|
83
|
+
}
|
|
84
|
+
return `<${type} />`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const tagName = element.tagName.toLowerCase();
|
|
88
|
+
const dataUIName = element.getAttribute("data-ui-name");
|
|
89
|
+
if (dataUIName) {
|
|
90
|
+
return `${tagName}[data-ui-name="${dataUIName}"]`;
|
|
91
|
+
}
|
|
92
|
+
const elementId = element.id;
|
|
93
|
+
if (elementId) {
|
|
94
|
+
return `${tagName}#${elementId}`;
|
|
95
|
+
}
|
|
96
|
+
const className = element.className;
|
|
97
|
+
if (className) {
|
|
98
|
+
return `${tagName}.${className.split(" ").join(".")}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const parentSignature = getElementSignature(element.parentElement);
|
|
102
|
+
return `${parentSignature} > ${tagName}`;
|
|
103
|
+
};
|
|
104
|
+
|
|
4
105
|
const createIterableWeakSet = () => {
|
|
5
106
|
const objectWeakRefSet = new Set();
|
|
6
107
|
|
|
@@ -102,22 +203,26 @@ const createPubSub = (clearOnPublish = false) => {
|
|
|
102
203
|
|
|
103
204
|
const createValueEffect = (value) => {
|
|
104
205
|
const callbackSet = new Set();
|
|
105
|
-
const
|
|
206
|
+
const valueCleanupSet = new Set();
|
|
207
|
+
|
|
208
|
+
const cleanup = () => {
|
|
209
|
+
for (const valueCleanup of valueCleanupSet) {
|
|
210
|
+
valueCleanup();
|
|
211
|
+
}
|
|
212
|
+
valueCleanupSet.clear();
|
|
213
|
+
};
|
|
106
214
|
|
|
107
215
|
const updateValue = (newValue) => {
|
|
108
216
|
if (newValue === value) {
|
|
109
217
|
return;
|
|
110
218
|
}
|
|
111
|
-
|
|
112
|
-
cleanup();
|
|
113
|
-
}
|
|
114
|
-
previousValueCleanupSet.clear();
|
|
219
|
+
cleanup();
|
|
115
220
|
const oldValue = value;
|
|
116
221
|
value = newValue;
|
|
117
222
|
for (const callback of callbackSet) {
|
|
118
223
|
const returnValue = callback(newValue, oldValue);
|
|
119
224
|
if (typeof returnValue === "function") {
|
|
120
|
-
|
|
225
|
+
valueCleanupSet.add(returnValue);
|
|
121
226
|
}
|
|
122
227
|
}
|
|
123
228
|
};
|
|
@@ -129,7 +234,7 @@ const createValueEffect = (value) => {
|
|
|
129
234
|
};
|
|
130
235
|
};
|
|
131
236
|
|
|
132
|
-
return [updateValue, addEffect];
|
|
237
|
+
return [updateValue, addEffect, cleanup];
|
|
133
238
|
};
|
|
134
239
|
|
|
135
240
|
// https://github.com/davidtheclark/tabbable/blob/master/index.js
|
|
@@ -418,26 +523,24 @@ const normalizeNumber = (value, context, unit, propertyName) => {
|
|
|
418
523
|
return value;
|
|
419
524
|
}
|
|
420
525
|
if (typeof value === "string") {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const numericValue = parseFloat(value);
|
|
428
|
-
if (isNaN(numericValue)) {
|
|
429
|
-
console.warn(
|
|
430
|
-
`"${propertyName}": ${value} cannot be converted to number, returning value as-is.`,
|
|
431
|
-
);
|
|
432
|
-
return value;
|
|
526
|
+
// For js context, only convert px values to numbers
|
|
527
|
+
if (unit === "px" && value.endsWith("px")) {
|
|
528
|
+
const numericValue = parseFloat(value);
|
|
529
|
+
if (!isNaN(numericValue)) {
|
|
530
|
+
return numericValue;
|
|
531
|
+
}
|
|
433
532
|
}
|
|
434
|
-
|
|
533
|
+
// Keep all other strings as-is (including %, em, rem, auto, none, etc.)
|
|
534
|
+
return value;
|
|
435
535
|
}
|
|
436
536
|
return value;
|
|
437
537
|
};
|
|
438
538
|
|
|
439
539
|
// Normalize styles for DOM application
|
|
440
540
|
const normalizeStyles = (styles, context = "js") => {
|
|
541
|
+
if (!styles) {
|
|
542
|
+
return {};
|
|
543
|
+
}
|
|
441
544
|
if (typeof styles === "string") {
|
|
442
545
|
styles = parseStyleString(styles);
|
|
443
546
|
return styles;
|
|
@@ -686,6 +789,29 @@ const mergeStyles = (stylesA, stylesB, context = "js") => {
|
|
|
686
789
|
return result;
|
|
687
790
|
};
|
|
688
791
|
|
|
792
|
+
const appendStyles = (
|
|
793
|
+
stylesAObject,
|
|
794
|
+
stylesBNormalized,
|
|
795
|
+
context = "js",
|
|
796
|
+
) => {
|
|
797
|
+
const aKeys = Object.keys(stylesAObject);
|
|
798
|
+
const bKeys = Object.keys(stylesBNormalized);
|
|
799
|
+
for (const bKey of bKeys) {
|
|
800
|
+
const aHasKey = aKeys.includes(bKey);
|
|
801
|
+
if (aHasKey) {
|
|
802
|
+
stylesAObject[bKey] = mergeOneStyle(
|
|
803
|
+
stylesAObject[bKey],
|
|
804
|
+
stylesBNormalized[bKey],
|
|
805
|
+
bKey,
|
|
806
|
+
context,
|
|
807
|
+
);
|
|
808
|
+
} else {
|
|
809
|
+
stylesAObject[bKey] = stylesBNormalized[bKey];
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return stylesAObject;
|
|
813
|
+
};
|
|
814
|
+
|
|
689
815
|
// Merge a single style property value with an existing value
|
|
690
816
|
const mergeOneStyle = (
|
|
691
817
|
existingValue,
|
|
@@ -4216,391 +4342,9 @@ const getDragCoordinates = (
|
|
|
4216
4342
|
return [leftRelativeToScrollContainer, topRelativeToScrollContainer];
|
|
4217
4343
|
};
|
|
4218
4344
|
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
// to keep in sync with https://github.com/calebdwilliams/construct-style-sheets
|
|
4222
|
-
// copy pasted into jsenv codebase to inject this code with more ease
|
|
4223
|
-
(function () {
|
|
4224
|
-
|
|
4225
|
-
if (typeof document === "undefined" || "adoptedStyleSheets" in document) {
|
|
4226
|
-
return;
|
|
4227
|
-
}
|
|
4228
|
-
|
|
4229
|
-
var hasShadyCss = "ShadyCSS" in window && !ShadyCSS.nativeShadow;
|
|
4230
|
-
var bootstrapper = document.implementation.createHTMLDocument("");
|
|
4231
|
-
var closedShadowRootRegistry = new WeakMap();
|
|
4232
|
-
var _DOMException = typeof DOMException === "object" ? Error : DOMException;
|
|
4233
|
-
var defineProperty = Object.defineProperty;
|
|
4234
|
-
var forEach = Array.prototype.forEach;
|
|
4235
|
-
|
|
4236
|
-
var importPattern = /@import.+?;?$/gm;
|
|
4237
|
-
function rejectImports(contents) {
|
|
4238
|
-
var _contents = contents.replace(importPattern, "");
|
|
4239
|
-
if (_contents !== contents) {
|
|
4240
|
-
console.warn(
|
|
4241
|
-
"@import rules are not allowed here. See https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-588352418",
|
|
4242
|
-
);
|
|
4243
|
-
}
|
|
4244
|
-
return _contents.trim();
|
|
4245
|
-
}
|
|
4246
|
-
function isElementConnected(element) {
|
|
4247
|
-
return "isConnected" in element
|
|
4248
|
-
? element.isConnected
|
|
4249
|
-
: document.contains(element);
|
|
4250
|
-
}
|
|
4251
|
-
function unique(arr) {
|
|
4252
|
-
return arr.filter(function (value, index) {
|
|
4253
|
-
return arr.indexOf(value) === index;
|
|
4254
|
-
});
|
|
4255
|
-
}
|
|
4256
|
-
function diff(arr1, arr2) {
|
|
4257
|
-
return arr1.filter(function (value) {
|
|
4258
|
-
return arr2.indexOf(value) === -1;
|
|
4259
|
-
});
|
|
4260
|
-
}
|
|
4261
|
-
function removeNode(node) {
|
|
4262
|
-
node.parentNode.removeChild(node);
|
|
4263
|
-
}
|
|
4264
|
-
function getShadowRoot(element) {
|
|
4265
|
-
return element.shadowRoot || closedShadowRootRegistry.get(element);
|
|
4266
|
-
}
|
|
4345
|
+
const installImportMetaCss = (importMeta) => {
|
|
4346
|
+
const stylesheet = new CSSStyleSheet({ baseUrl: importMeta.url });
|
|
4267
4347
|
|
|
4268
|
-
var cssStyleSheetMethods = [
|
|
4269
|
-
"addRule",
|
|
4270
|
-
"deleteRule",
|
|
4271
|
-
"insertRule",
|
|
4272
|
-
"removeRule",
|
|
4273
|
-
];
|
|
4274
|
-
var NonConstructedStyleSheet = CSSStyleSheet;
|
|
4275
|
-
var nonConstructedProto = NonConstructedStyleSheet.prototype;
|
|
4276
|
-
nonConstructedProto.replace = function () {
|
|
4277
|
-
return Promise.reject(
|
|
4278
|
-
new _DOMException(
|
|
4279
|
-
"Can't call replace on non-constructed CSSStyleSheets.",
|
|
4280
|
-
),
|
|
4281
|
-
);
|
|
4282
|
-
};
|
|
4283
|
-
nonConstructedProto.replaceSync = function () {
|
|
4284
|
-
throw new _DOMException(
|
|
4285
|
-
"Failed to execute 'replaceSync' on 'CSSStyleSheet': Can't call replaceSync on non-constructed CSSStyleSheets.",
|
|
4286
|
-
);
|
|
4287
|
-
};
|
|
4288
|
-
function isCSSStyleSheetInstance(instance) {
|
|
4289
|
-
return typeof instance === "object"
|
|
4290
|
-
? proto$1.isPrototypeOf(instance) ||
|
|
4291
|
-
nonConstructedProto.isPrototypeOf(instance)
|
|
4292
|
-
: false;
|
|
4293
|
-
}
|
|
4294
|
-
function isNonConstructedStyleSheetInstance(instance) {
|
|
4295
|
-
return typeof instance === "object"
|
|
4296
|
-
? nonConstructedProto.isPrototypeOf(instance)
|
|
4297
|
-
: false;
|
|
4298
|
-
}
|
|
4299
|
-
var $basicStyleElement = new WeakMap();
|
|
4300
|
-
var $locations = new WeakMap();
|
|
4301
|
-
var $adoptersByLocation = new WeakMap();
|
|
4302
|
-
var $appliedMethods = new WeakMap();
|
|
4303
|
-
function addAdopterLocation(sheet, location) {
|
|
4304
|
-
var adopter = document.createElement("style");
|
|
4305
|
-
$adoptersByLocation.get(sheet).set(location, adopter);
|
|
4306
|
-
$locations.get(sheet).push(location);
|
|
4307
|
-
return adopter;
|
|
4308
|
-
}
|
|
4309
|
-
function getAdopterByLocation(sheet, location) {
|
|
4310
|
-
return $adoptersByLocation.get(sheet).get(location);
|
|
4311
|
-
}
|
|
4312
|
-
function removeAdopterLocation(sheet, location) {
|
|
4313
|
-
$adoptersByLocation.get(sheet).delete(location);
|
|
4314
|
-
$locations.set(
|
|
4315
|
-
sheet,
|
|
4316
|
-
$locations.get(sheet).filter(function (_location) {
|
|
4317
|
-
return _location !== location;
|
|
4318
|
-
}),
|
|
4319
|
-
);
|
|
4320
|
-
}
|
|
4321
|
-
function restyleAdopter(sheet, adopter) {
|
|
4322
|
-
requestAnimationFrame(function () {
|
|
4323
|
-
adopter.textContent = $basicStyleElement.get(sheet).textContent;
|
|
4324
|
-
$appliedMethods.get(sheet).forEach(function (command) {
|
|
4325
|
-
return adopter.sheet[command.method].apply(adopter.sheet, command.args);
|
|
4326
|
-
});
|
|
4327
|
-
});
|
|
4328
|
-
}
|
|
4329
|
-
function checkInvocationCorrectness(self) {
|
|
4330
|
-
if (!$basicStyleElement.has(self)) {
|
|
4331
|
-
throw new TypeError("Illegal invocation");
|
|
4332
|
-
}
|
|
4333
|
-
}
|
|
4334
|
-
function ConstructedStyleSheet() {
|
|
4335
|
-
var self = this;
|
|
4336
|
-
var style = document.createElement("style");
|
|
4337
|
-
bootstrapper.body.appendChild(style);
|
|
4338
|
-
$basicStyleElement.set(self, style);
|
|
4339
|
-
$locations.set(self, []);
|
|
4340
|
-
$adoptersByLocation.set(self, new WeakMap());
|
|
4341
|
-
$appliedMethods.set(self, []);
|
|
4342
|
-
}
|
|
4343
|
-
var proto$1 = ConstructedStyleSheet.prototype;
|
|
4344
|
-
proto$1.replace = function replace(contents) {
|
|
4345
|
-
try {
|
|
4346
|
-
this.replaceSync(contents);
|
|
4347
|
-
return Promise.resolve(this);
|
|
4348
|
-
} catch (e) {
|
|
4349
|
-
return Promise.reject(e);
|
|
4350
|
-
}
|
|
4351
|
-
};
|
|
4352
|
-
proto$1.replaceSync = function replaceSync(contents) {
|
|
4353
|
-
checkInvocationCorrectness(this);
|
|
4354
|
-
if (typeof contents === "string") {
|
|
4355
|
-
var self_1 = this;
|
|
4356
|
-
$basicStyleElement.get(self_1).textContent = rejectImports(contents);
|
|
4357
|
-
$appliedMethods.set(self_1, []);
|
|
4358
|
-
$locations.get(self_1).forEach(function (location) {
|
|
4359
|
-
if (location.isConnected()) {
|
|
4360
|
-
restyleAdopter(self_1, getAdopterByLocation(self_1, location));
|
|
4361
|
-
}
|
|
4362
|
-
});
|
|
4363
|
-
}
|
|
4364
|
-
};
|
|
4365
|
-
defineProperty(proto$1, "cssRules", {
|
|
4366
|
-
configurable: true,
|
|
4367
|
-
enumerable: true,
|
|
4368
|
-
get: function cssRules() {
|
|
4369
|
-
checkInvocationCorrectness(this);
|
|
4370
|
-
return $basicStyleElement.get(this).sheet.cssRules;
|
|
4371
|
-
},
|
|
4372
|
-
});
|
|
4373
|
-
defineProperty(proto$1, "media", {
|
|
4374
|
-
configurable: true,
|
|
4375
|
-
enumerable: true,
|
|
4376
|
-
get: function media() {
|
|
4377
|
-
checkInvocationCorrectness(this);
|
|
4378
|
-
return $basicStyleElement.get(this).sheet.media;
|
|
4379
|
-
},
|
|
4380
|
-
});
|
|
4381
|
-
cssStyleSheetMethods.forEach(function (method) {
|
|
4382
|
-
proto$1[method] = function () {
|
|
4383
|
-
var self = this;
|
|
4384
|
-
checkInvocationCorrectness(self);
|
|
4385
|
-
var args = arguments;
|
|
4386
|
-
$appliedMethods.get(self).push({ method: method, args: args });
|
|
4387
|
-
$locations.get(self).forEach(function (location) {
|
|
4388
|
-
if (location.isConnected()) {
|
|
4389
|
-
var sheet = getAdopterByLocation(self, location).sheet;
|
|
4390
|
-
sheet[method].apply(sheet, args);
|
|
4391
|
-
}
|
|
4392
|
-
});
|
|
4393
|
-
var basicSheet = $basicStyleElement.get(self).sheet;
|
|
4394
|
-
return basicSheet[method].apply(basicSheet, args);
|
|
4395
|
-
};
|
|
4396
|
-
});
|
|
4397
|
-
defineProperty(ConstructedStyleSheet, Symbol.hasInstance, {
|
|
4398
|
-
configurable: true,
|
|
4399
|
-
value: isCSSStyleSheetInstance,
|
|
4400
|
-
});
|
|
4401
|
-
|
|
4402
|
-
var defaultObserverOptions = {
|
|
4403
|
-
childList: true,
|
|
4404
|
-
subtree: true,
|
|
4405
|
-
};
|
|
4406
|
-
var locations = new WeakMap();
|
|
4407
|
-
function getAssociatedLocation(element) {
|
|
4408
|
-
var location = locations.get(element);
|
|
4409
|
-
if (!location) {
|
|
4410
|
-
location = new Location(element);
|
|
4411
|
-
locations.set(element, location);
|
|
4412
|
-
}
|
|
4413
|
-
return location;
|
|
4414
|
-
}
|
|
4415
|
-
function attachAdoptedStyleSheetProperty(constructor) {
|
|
4416
|
-
defineProperty(constructor.prototype, "adoptedStyleSheets", {
|
|
4417
|
-
configurable: true,
|
|
4418
|
-
enumerable: true,
|
|
4419
|
-
get: function () {
|
|
4420
|
-
return getAssociatedLocation(this).sheets;
|
|
4421
|
-
},
|
|
4422
|
-
set: function (sheets) {
|
|
4423
|
-
getAssociatedLocation(this).update(sheets);
|
|
4424
|
-
},
|
|
4425
|
-
});
|
|
4426
|
-
}
|
|
4427
|
-
function traverseWebComponents(node, callback) {
|
|
4428
|
-
var iter = document.createNodeIterator(
|
|
4429
|
-
node,
|
|
4430
|
-
NodeFilter.SHOW_ELEMENT,
|
|
4431
|
-
function (foundNode) {
|
|
4432
|
-
return getShadowRoot(foundNode)
|
|
4433
|
-
? NodeFilter.FILTER_ACCEPT
|
|
4434
|
-
: NodeFilter.FILTER_REJECT;
|
|
4435
|
-
},
|
|
4436
|
-
null,
|
|
4437
|
-
false,
|
|
4438
|
-
);
|
|
4439
|
-
for (var next = void 0; (next = iter.nextNode()); ) {
|
|
4440
|
-
callback(getShadowRoot(next));
|
|
4441
|
-
}
|
|
4442
|
-
}
|
|
4443
|
-
var $element = new WeakMap();
|
|
4444
|
-
var $uniqueSheets = new WeakMap();
|
|
4445
|
-
var $observer = new WeakMap();
|
|
4446
|
-
function isExistingAdopter(self, element) {
|
|
4447
|
-
return (
|
|
4448
|
-
element instanceof HTMLStyleElement &&
|
|
4449
|
-
$uniqueSheets.get(self).some(function (sheet) {
|
|
4450
|
-
return getAdopterByLocation(sheet, self);
|
|
4451
|
-
})
|
|
4452
|
-
);
|
|
4453
|
-
}
|
|
4454
|
-
function getAdopterContainer(self) {
|
|
4455
|
-
var element = $element.get(self);
|
|
4456
|
-
return element instanceof Document ? element.body : element;
|
|
4457
|
-
}
|
|
4458
|
-
function adopt(self) {
|
|
4459
|
-
var styleList = document.createDocumentFragment();
|
|
4460
|
-
var sheets = $uniqueSheets.get(self);
|
|
4461
|
-
var observer = $observer.get(self);
|
|
4462
|
-
var container = getAdopterContainer(self);
|
|
4463
|
-
observer.disconnect();
|
|
4464
|
-
sheets.forEach(function (sheet) {
|
|
4465
|
-
styleList.appendChild(
|
|
4466
|
-
getAdopterByLocation(sheet, self) || addAdopterLocation(sheet, self),
|
|
4467
|
-
);
|
|
4468
|
-
});
|
|
4469
|
-
container.insertBefore(styleList, null);
|
|
4470
|
-
observer.observe(container, defaultObserverOptions);
|
|
4471
|
-
sheets.forEach(function (sheet) {
|
|
4472
|
-
restyleAdopter(sheet, getAdopterByLocation(sheet, self));
|
|
4473
|
-
});
|
|
4474
|
-
}
|
|
4475
|
-
function Location(element) {
|
|
4476
|
-
var self = this;
|
|
4477
|
-
self.sheets = [];
|
|
4478
|
-
$element.set(self, element);
|
|
4479
|
-
$uniqueSheets.set(self, []);
|
|
4480
|
-
$observer.set(
|
|
4481
|
-
self,
|
|
4482
|
-
new MutationObserver(function (mutations, observer) {
|
|
4483
|
-
if (!document) {
|
|
4484
|
-
observer.disconnect();
|
|
4485
|
-
return;
|
|
4486
|
-
}
|
|
4487
|
-
mutations.forEach(function (mutation) {
|
|
4488
|
-
if (!hasShadyCss) {
|
|
4489
|
-
forEach.call(mutation.addedNodes, function (node) {
|
|
4490
|
-
if (!(node instanceof Element)) {
|
|
4491
|
-
return;
|
|
4492
|
-
}
|
|
4493
|
-
traverseWebComponents(node, function (root) {
|
|
4494
|
-
getAssociatedLocation(root).connect();
|
|
4495
|
-
});
|
|
4496
|
-
});
|
|
4497
|
-
}
|
|
4498
|
-
forEach.call(mutation.removedNodes, function (node) {
|
|
4499
|
-
if (!(node instanceof Element)) {
|
|
4500
|
-
return;
|
|
4501
|
-
}
|
|
4502
|
-
if (isExistingAdopter(self, node)) {
|
|
4503
|
-
adopt(self);
|
|
4504
|
-
}
|
|
4505
|
-
if (!hasShadyCss) {
|
|
4506
|
-
traverseWebComponents(node, function (root) {
|
|
4507
|
-
getAssociatedLocation(root).disconnect();
|
|
4508
|
-
});
|
|
4509
|
-
}
|
|
4510
|
-
});
|
|
4511
|
-
});
|
|
4512
|
-
}),
|
|
4513
|
-
);
|
|
4514
|
-
}
|
|
4515
|
-
Location.prototype = {
|
|
4516
|
-
isConnected: function () {
|
|
4517
|
-
var element = $element.get(this);
|
|
4518
|
-
return element instanceof Document
|
|
4519
|
-
? element.readyState !== "loading"
|
|
4520
|
-
: isElementConnected(element.host);
|
|
4521
|
-
},
|
|
4522
|
-
connect: function () {
|
|
4523
|
-
var container = getAdopterContainer(this);
|
|
4524
|
-
$observer.get(this).observe(container, defaultObserverOptions);
|
|
4525
|
-
if ($uniqueSheets.get(this).length > 0) {
|
|
4526
|
-
adopt(this);
|
|
4527
|
-
}
|
|
4528
|
-
traverseWebComponents(container, function (root) {
|
|
4529
|
-
getAssociatedLocation(root).connect();
|
|
4530
|
-
});
|
|
4531
|
-
},
|
|
4532
|
-
disconnect: function () {
|
|
4533
|
-
$observer.get(this).disconnect();
|
|
4534
|
-
},
|
|
4535
|
-
update: function (sheets) {
|
|
4536
|
-
var self = this;
|
|
4537
|
-
var locationType =
|
|
4538
|
-
$element.get(self) === document ? "Document" : "ShadowRoot";
|
|
4539
|
-
if (!Array.isArray(sheets)) {
|
|
4540
|
-
throw new TypeError(
|
|
4541
|
-
"Failed to set the 'adoptedStyleSheets' property on " +
|
|
4542
|
-
locationType +
|
|
4543
|
-
": Iterator getter is not callable.",
|
|
4544
|
-
);
|
|
4545
|
-
}
|
|
4546
|
-
if (!sheets.every(isCSSStyleSheetInstance)) {
|
|
4547
|
-
throw new TypeError(
|
|
4548
|
-
"Failed to set the 'adoptedStyleSheets' property on " +
|
|
4549
|
-
locationType +
|
|
4550
|
-
": Failed to convert value to 'CSSStyleSheet'",
|
|
4551
|
-
);
|
|
4552
|
-
}
|
|
4553
|
-
if (sheets.some(isNonConstructedStyleSheetInstance)) {
|
|
4554
|
-
throw new TypeError(
|
|
4555
|
-
"Failed to set the 'adoptedStyleSheets' property on " +
|
|
4556
|
-
locationType +
|
|
4557
|
-
": Can't adopt non-constructed stylesheets",
|
|
4558
|
-
);
|
|
4559
|
-
}
|
|
4560
|
-
self.sheets = sheets;
|
|
4561
|
-
var oldUniqueSheets = $uniqueSheets.get(self);
|
|
4562
|
-
var uniqueSheets = unique(sheets);
|
|
4563
|
-
var removedSheets = diff(oldUniqueSheets, uniqueSheets);
|
|
4564
|
-
removedSheets.forEach(function (sheet) {
|
|
4565
|
-
removeNode(getAdopterByLocation(sheet, self));
|
|
4566
|
-
removeAdopterLocation(sheet, self);
|
|
4567
|
-
});
|
|
4568
|
-
$uniqueSheets.set(self, uniqueSheets);
|
|
4569
|
-
if (self.isConnected() && uniqueSheets.length > 0) {
|
|
4570
|
-
adopt(self);
|
|
4571
|
-
}
|
|
4572
|
-
},
|
|
4573
|
-
};
|
|
4574
|
-
|
|
4575
|
-
window.CSSStyleSheet = ConstructedStyleSheet;
|
|
4576
|
-
attachAdoptedStyleSheetProperty(Document);
|
|
4577
|
-
if ("ShadowRoot" in window) {
|
|
4578
|
-
attachAdoptedStyleSheetProperty(ShadowRoot);
|
|
4579
|
-
var proto = Element.prototype;
|
|
4580
|
-
var attach_1 = proto.attachShadow;
|
|
4581
|
-
proto.attachShadow = function attachShadow(init) {
|
|
4582
|
-
var root = attach_1.call(this, init);
|
|
4583
|
-
if (init.mode === "closed") {
|
|
4584
|
-
closedShadowRootRegistry.set(this, root);
|
|
4585
|
-
}
|
|
4586
|
-
return root;
|
|
4587
|
-
};
|
|
4588
|
-
}
|
|
4589
|
-
var documentLocation = getAssociatedLocation(document);
|
|
4590
|
-
if (documentLocation.isConnected()) {
|
|
4591
|
-
documentLocation.connect();
|
|
4592
|
-
} else {
|
|
4593
|
-
document.addEventListener(
|
|
4594
|
-
"DOMContentLoaded",
|
|
4595
|
-
documentLocation.connect.bind(documentLocation),
|
|
4596
|
-
);
|
|
4597
|
-
}
|
|
4598
|
-
})();
|
|
4599
|
-
|
|
4600
|
-
const installImportMetaCss = importMeta => {
|
|
4601
|
-
const stylesheet = new CSSStyleSheet({
|
|
4602
|
-
baseUrl: importMeta.url
|
|
4603
|
-
});
|
|
4604
4348
|
let called = false;
|
|
4605
4349
|
// eslint-disable-next-line accessor-pairs
|
|
4606
4350
|
Object.defineProperty(importMeta, "css", {
|
|
@@ -4611,8 +4355,11 @@ const installImportMetaCss = importMeta => {
|
|
|
4611
4355
|
}
|
|
4612
4356
|
called = true;
|
|
4613
4357
|
stylesheet.replaceSync(value);
|
|
4614
|
-
document.adoptedStyleSheets = [
|
|
4615
|
-
|
|
4358
|
+
document.adoptedStyleSheets = [
|
|
4359
|
+
...document.adoptedStyleSheets,
|
|
4360
|
+
stylesheet,
|
|
4361
|
+
];
|
|
4362
|
+
},
|
|
4616
4363
|
});
|
|
4617
4364
|
};
|
|
4618
4365
|
|
|
@@ -5671,15 +5418,6 @@ const getScrollport = (scrollBox, scrollContainer) => {
|
|
|
5671
5418
|
};
|
|
5672
5419
|
};
|
|
5673
5420
|
|
|
5674
|
-
const getElementSelector = (element) => {
|
|
5675
|
-
const tagName = element.tagName.toLowerCase();
|
|
5676
|
-
const id = element.id ? `#${element.id}` : "";
|
|
5677
|
-
const className = element.className
|
|
5678
|
-
? `.${element.className.split(" ").join(".")}`
|
|
5679
|
-
: "";
|
|
5680
|
-
return `${tagName}${id}${className}`;
|
|
5681
|
-
};
|
|
5682
|
-
|
|
5683
5421
|
installImportMetaCss(import.meta);const setupConstraintFeedbackLine = () => {
|
|
5684
5422
|
const constraintFeedbackLine = createConstraintFeedbackLine();
|
|
5685
5423
|
|
|
@@ -6705,7 +6443,7 @@ const createObstacleConstraintsFromQuerySelector = (
|
|
|
6705
6443
|
|
|
6706
6444
|
// obstacleBounds are already in scrollable-relative coordinates, no conversion needed
|
|
6707
6445
|
const obstacleObject = createObstacleContraint(obstacleBounds, {
|
|
6708
|
-
name: `${obstacleBounds.isSticky ? "sticky " : ""}obstacle (${
|
|
6446
|
+
name: `${obstacleBounds.isSticky ? "sticky " : ""}obstacle (${getElementSignature(obstacle)})`,
|
|
6709
6447
|
element: obstacle,
|
|
6710
6448
|
});
|
|
6711
6449
|
return obstacleObject;
|
|
@@ -6983,9 +6721,9 @@ const createStickyFrontierOnAxis = (
|
|
|
6983
6721
|
const hasOpposite = frontier.hasAttribute(oppositeAttrName);
|
|
6984
6722
|
// Check if element has both sides (invalid)
|
|
6985
6723
|
if (hasPrimary && hasOpposite) {
|
|
6986
|
-
const
|
|
6724
|
+
const elementSignature = getElementSignature(frontier);
|
|
6987
6725
|
console.warn(
|
|
6988
|
-
`Sticky frontier element (${
|
|
6726
|
+
`Sticky frontier element (${elementSignature}) has both ${primarySide} and ${oppositeSide} attributes.
|
|
6989
6727
|
A sticky frontier should only have one side attribute.`,
|
|
6990
6728
|
);
|
|
6991
6729
|
continue;
|
|
@@ -7008,7 +6746,7 @@ const createStickyFrontierOnAxis = (
|
|
|
7008
6746
|
element: frontier,
|
|
7009
6747
|
side: hasPrimary ? primarySide : oppositeSide,
|
|
7010
6748
|
bounds: frontierBounds,
|
|
7011
|
-
name: `sticky_frontier_${hasPrimary ? primarySide : oppositeSide} (${
|
|
6749
|
+
name: `sticky_frontier_${hasPrimary ? primarySide : oppositeSide} (${getElementSignature(frontier)})`,
|
|
7012
6750
|
};
|
|
7013
6751
|
matchingStickyFrontiers.push(stickyFrontierObject);
|
|
7014
6752
|
}
|
|
@@ -10953,7 +10691,7 @@ const initUITransition = (container) => {
|
|
|
10953
10691
|
debug(
|
|
10954
10692
|
"transition",
|
|
10955
10693
|
`Cloned previous child for ${isPhaseTransition ? "phase" : "content"} transition:`,
|
|
10956
|
-
previousChild
|
|
10694
|
+
getElementSignature(previousChild),
|
|
10957
10695
|
);
|
|
10958
10696
|
cleanup = () => oldChild.remove();
|
|
10959
10697
|
} else {
|
|
@@ -11005,7 +10743,7 @@ const initUITransition = (container) => {
|
|
|
11005
10743
|
if (localDebug.transition) {
|
|
11006
10744
|
const updateLabel =
|
|
11007
10745
|
childUIName ||
|
|
11008
|
-
(firstChild ?
|
|
10746
|
+
(firstChild ? getElementSignature(firstChild) : "cleared/empty");
|
|
11009
10747
|
console.group(`UI Update: ${updateLabel} (reason: ${reason})`);
|
|
11010
10748
|
}
|
|
11011
10749
|
|
|
@@ -11609,7 +11347,7 @@ const initUITransition = (container) => {
|
|
|
11609
11347
|
debug(
|
|
11610
11348
|
"transition",
|
|
11611
11349
|
`Attribute change detected: ${attributeName} on`,
|
|
11612
|
-
target
|
|
11350
|
+
getElementSignature(target),
|
|
11613
11351
|
);
|
|
11614
11352
|
}
|
|
11615
11353
|
}
|
|
@@ -11981,4 +11719,4 @@ const crossFade = {
|
|
|
11981
11719
|
},
|
|
11982
11720
|
};
|
|
11983
11721
|
|
|
11984
|
-
export { EASING, activeElementSignal, addActiveElementEffect, addAttributeEffect, addWillChange, allowWheelThrough, canInterceptKeys, captureScrollState, createDragGestureController, createDragToMoveGestureController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dragAfterThreshold, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findFocusable, getAvailableHeight, getAvailableWidth, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getInnerHeight, getInnerWidth, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getPaddingSizes, getPositionedParent, getPreferedColorScheme, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getVisuallyVisibleInfo, getWidth, initFlexDetailsSet, initFocusGroup, initPositionSticky, initUITransition, isScrollable, mergeStyles, normalizeStyles, parseCSSColor, pickLightOrDark, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, resolveCSSColor, resolveCSSSize, setAttribute, setAttributes, setStyles, startDragToResizeGesture, stickyAsRelativeCoords, stringifyCSSColor, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect };
|
|
11722
|
+
export { EASING, activeElementSignal, addActiveElementEffect, addAttributeEffect, addWillChange, allowWheelThrough, appendStyles, canInterceptKeys, captureScrollState, createDragGestureController, createDragToMoveGestureController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dragAfterThreshold, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findFocusable, getAvailableHeight, getAvailableWidth, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getInnerHeight, getInnerWidth, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getPaddingSizes, getPositionedParent, getPreferedColorScheme, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getVisuallyVisibleInfo, getWidth, initFlexDetailsSet, initFocusGroup, initPositionSticky, initUITransition, isScrollable, mergeStyles, normalizeStyles, parseCSSColor, pickLightOrDark, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, resolveCSSColor, resolveCSSSize, setAttribute, setAttributes, setStyles, startDragToResizeGesture, stickyAsRelativeCoords, stringifyCSSColor, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect };
|
package/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { getElementSignature } from "./src/element_signature.js";
|
|
2
|
+
|
|
1
3
|
// state management
|
|
2
4
|
export { createIterableWeakSet } from "./src/iterable_weak_set.js";
|
|
3
5
|
export { createPubSub } from "./src/pub_sub.js";
|
|
@@ -5,7 +7,7 @@ export { createValueEffect } from "./src/value_effect.js";
|
|
|
5
7
|
|
|
6
8
|
// style
|
|
7
9
|
export { addWillChange, getStyle, setStyles } from "./src/style/dom_styles.js";
|
|
8
|
-
export { mergeStyles } from "./src/style/style_composition.js";
|
|
10
|
+
export { appendStyles, mergeStyles } from "./src/style/style_composition.js";
|
|
9
11
|
export { createStyleController } from "./src/style/style_controller.js";
|
|
10
12
|
export { getDefaultStyles } from "./src/style/style_default.js";
|
|
11
13
|
export { normalizeStyles } from "./src/style/style_parsing.js";
|
package/package.json
CHANGED
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { getElementSignature } from "../../element_signature.js";
|
|
1
2
|
import {
|
|
2
3
|
addScrollToRect,
|
|
3
4
|
getScrollRelativeRect,
|
|
4
5
|
} from "../../position/dom_coords.js";
|
|
5
|
-
import { getElementSelector } from "../element_log.js";
|
|
6
6
|
import { setupConstraintFeedbackLine } from "./constraint_feedback_line.js";
|
|
7
7
|
import { setupDragDebugMarkers } from "./drag_debug_markers.js";
|
|
8
8
|
|
|
@@ -333,7 +333,7 @@ const createObstacleConstraintsFromQuerySelector = (
|
|
|
333
333
|
|
|
334
334
|
// obstacleBounds are already in scrollable-relative coordinates, no conversion needed
|
|
335
335
|
const obstacleObject = createObstacleContraint(obstacleBounds, {
|
|
336
|
-
name: `${obstacleBounds.isSticky ? "sticky " : ""}obstacle (${
|
|
336
|
+
name: `${obstacleBounds.isSticky ? "sticky " : ""}obstacle (${getElementSignature(obstacle)})`,
|
|
337
337
|
element: obstacle,
|
|
338
338
|
});
|
|
339
339
|
return obstacleObject;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { getElementSignature } from "../../element_signature.js";
|
|
1
2
|
import { getScrollRelativeRect } from "../../position/dom_coords.js";
|
|
2
|
-
import { getElementSelector } from "../element_log.js";
|
|
3
3
|
|
|
4
4
|
export const applyStickyFrontiersToAutoScrollArea = (
|
|
5
5
|
autoScrollArea,
|
|
@@ -127,9 +127,9 @@ const createStickyFrontierOnAxis = (
|
|
|
127
127
|
const hasOpposite = frontier.hasAttribute(oppositeAttrName);
|
|
128
128
|
// Check if element has both sides (invalid)
|
|
129
129
|
if (hasPrimary && hasOpposite) {
|
|
130
|
-
const
|
|
130
|
+
const elementSignature = getElementSignature(frontier);
|
|
131
131
|
console.warn(
|
|
132
|
-
`Sticky frontier element (${
|
|
132
|
+
`Sticky frontier element (${elementSignature}) has both ${primarySide} and ${oppositeSide} attributes.
|
|
133
133
|
A sticky frontier should only have one side attribute.`,
|
|
134
134
|
);
|
|
135
135
|
continue;
|
|
@@ -152,7 +152,7 @@ const createStickyFrontierOnAxis = (
|
|
|
152
152
|
element: frontier,
|
|
153
153
|
side: hasPrimary ? primarySide : oppositeSide,
|
|
154
154
|
bounds: frontierBounds,
|
|
155
|
-
name: `sticky_frontier_${hasPrimary ? primarySide : oppositeSide} (${
|
|
155
|
+
name: `sticky_frontier_${hasPrimary ? primarySide : oppositeSide} (${getElementSignature(frontier)})`,
|
|
156
156
|
};
|
|
157
157
|
matchingStickyFrontiers.push(stickyFrontierObject);
|
|
158
158
|
}
|
|
@@ -33,6 +33,29 @@ export const mergeStyles = (stylesA, stylesB, context = "js") => {
|
|
|
33
33
|
return result;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
export const appendStyles = (
|
|
37
|
+
stylesAObject,
|
|
38
|
+
stylesBNormalized,
|
|
39
|
+
context = "js",
|
|
40
|
+
) => {
|
|
41
|
+
const aKeys = Object.keys(stylesAObject);
|
|
42
|
+
const bKeys = Object.keys(stylesBNormalized);
|
|
43
|
+
for (const bKey of bKeys) {
|
|
44
|
+
const aHasKey = aKeys.includes(bKey);
|
|
45
|
+
if (aHasKey) {
|
|
46
|
+
stylesAObject[bKey] = mergeOneStyle(
|
|
47
|
+
stylesAObject[bKey],
|
|
48
|
+
stylesBNormalized[bKey],
|
|
49
|
+
bKey,
|
|
50
|
+
context,
|
|
51
|
+
);
|
|
52
|
+
} else {
|
|
53
|
+
stylesAObject[bKey] = stylesBNormalized[bKey];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return stylesAObject;
|
|
57
|
+
};
|
|
58
|
+
|
|
36
59
|
// Merge a single style property value with an existing value
|
|
37
60
|
export const mergeOneStyle = (
|
|
38
61
|
existingValue,
|
|
@@ -136,26 +136,24 @@ const normalizeNumber = (value, context, unit, propertyName) => {
|
|
|
136
136
|
return value;
|
|
137
137
|
}
|
|
138
138
|
if (typeof value === "string") {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const numericValue = parseFloat(value);
|
|
146
|
-
if (isNaN(numericValue)) {
|
|
147
|
-
console.warn(
|
|
148
|
-
`"${propertyName}": ${value} cannot be converted to number, returning value as-is.`,
|
|
149
|
-
);
|
|
150
|
-
return value;
|
|
139
|
+
// For js context, only convert px values to numbers
|
|
140
|
+
if (unit === "px" && value.endsWith("px")) {
|
|
141
|
+
const numericValue = parseFloat(value);
|
|
142
|
+
if (!isNaN(numericValue)) {
|
|
143
|
+
return numericValue;
|
|
144
|
+
}
|
|
151
145
|
}
|
|
152
|
-
|
|
146
|
+
// Keep all other strings as-is (including %, em, rem, auto, none, etc.)
|
|
147
|
+
return value;
|
|
153
148
|
}
|
|
154
149
|
return value;
|
|
155
150
|
};
|
|
156
151
|
|
|
157
152
|
// Normalize styles for DOM application
|
|
158
153
|
export const normalizeStyles = (styles, context = "js") => {
|
|
154
|
+
if (!styles) {
|
|
155
|
+
return {};
|
|
156
|
+
}
|
|
159
157
|
if (typeof styles === "string") {
|
|
160
158
|
styles = parseStyleString(styles);
|
|
161
159
|
return styles;
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
* - Independent content updates in the slot without affecting ongoing animations
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
|
+
import { getElementSignature } from "../element_signature.js";
|
|
44
45
|
import { getHeight } from "../size/get_height.js";
|
|
45
46
|
import { getInnerWidth } from "../size/get_inner_width.js";
|
|
46
47
|
import { getWidth } from "../size/get_width.js";
|
|
@@ -461,7 +462,7 @@ export const initUITransition = (container) => {
|
|
|
461
462
|
debug(
|
|
462
463
|
"transition",
|
|
463
464
|
`Cloned previous child for ${isPhaseTransition ? "phase" : "content"} transition:`,
|
|
464
|
-
previousChild
|
|
465
|
+
getElementSignature(previousChild),
|
|
465
466
|
);
|
|
466
467
|
cleanup = () => oldChild.remove();
|
|
467
468
|
} else {
|
|
@@ -513,7 +514,7 @@ export const initUITransition = (container) => {
|
|
|
513
514
|
if (localDebug.transition) {
|
|
514
515
|
const updateLabel =
|
|
515
516
|
childUIName ||
|
|
516
|
-
(firstChild ?
|
|
517
|
+
(firstChild ? getElementSignature(firstChild) : "cleared/empty");
|
|
517
518
|
console.group(`UI Update: ${updateLabel} (reason: ${reason})`);
|
|
518
519
|
}
|
|
519
520
|
|
|
@@ -1117,7 +1118,7 @@ export const initUITransition = (container) => {
|
|
|
1117
1118
|
debug(
|
|
1118
1119
|
"transition",
|
|
1119
1120
|
`Attribute change detected: ${attributeName} on`,
|
|
1120
|
-
target
|
|
1121
|
+
getElementSignature(target),
|
|
1121
1122
|
);
|
|
1122
1123
|
}
|
|
1123
1124
|
}
|
package/src/value_effect.js
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
export const createValueEffect = (value) => {
|
|
2
2
|
const callbackSet = new Set();
|
|
3
|
-
const
|
|
3
|
+
const valueCleanupSet = new Set();
|
|
4
|
+
|
|
5
|
+
const cleanup = () => {
|
|
6
|
+
for (const valueCleanup of valueCleanupSet) {
|
|
7
|
+
valueCleanup();
|
|
8
|
+
}
|
|
9
|
+
valueCleanupSet.clear();
|
|
10
|
+
};
|
|
4
11
|
|
|
5
12
|
const updateValue = (newValue) => {
|
|
6
13
|
if (newValue === value) {
|
|
7
14
|
return;
|
|
8
15
|
}
|
|
9
|
-
|
|
10
|
-
cleanup();
|
|
11
|
-
}
|
|
12
|
-
previousValueCleanupSet.clear();
|
|
16
|
+
cleanup();
|
|
13
17
|
const oldValue = value;
|
|
14
18
|
value = newValue;
|
|
15
19
|
for (const callback of callbackSet) {
|
|
16
20
|
const returnValue = callback(newValue, oldValue);
|
|
17
21
|
if (typeof returnValue === "function") {
|
|
18
|
-
|
|
22
|
+
valueCleanupSet.add(returnValue);
|
|
19
23
|
}
|
|
20
24
|
}
|
|
21
25
|
};
|
|
@@ -27,5 +31,5 @@ export const createValueEffect = (value) => {
|
|
|
27
31
|
};
|
|
28
32
|
};
|
|
29
33
|
|
|
30
|
-
return [updateValue, addEffect];
|
|
34
|
+
return [updateValue, addEffect, cleanup];
|
|
31
35
|
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export const getElementSelector = (element) => {
|
|
2
|
-
const tagName = element.tagName.toLowerCase();
|
|
3
|
-
const id = element.id ? `#${element.id}` : "";
|
|
4
|
-
const className = element.className
|
|
5
|
-
? `.${element.className.split(" ").join(".")}`
|
|
6
|
-
: "";
|
|
7
|
-
return `${tagName}${id}${className}`;
|
|
8
|
-
};
|