@jsenv/dom 0.5.3 → 0.6.1
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 -431
- 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 +3 -0
- package/src/ui_transition/ui_transition.js +28 -48
- 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
|
|
@@ -433,6 +538,9 @@ const normalizeNumber = (value, context, unit, propertyName) => {
|
|
|
433
538
|
|
|
434
539
|
// Normalize styles for DOM application
|
|
435
540
|
const normalizeStyles = (styles, context = "js") => {
|
|
541
|
+
if (!styles) {
|
|
542
|
+
return {};
|
|
543
|
+
}
|
|
436
544
|
if (typeof styles === "string") {
|
|
437
545
|
styles = parseStyleString(styles);
|
|
438
546
|
return styles;
|
|
@@ -681,6 +789,29 @@ const mergeStyles = (stylesA, stylesB, context = "js") => {
|
|
|
681
789
|
return result;
|
|
682
790
|
};
|
|
683
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
|
+
|
|
684
815
|
// Merge a single style property value with an existing value
|
|
685
816
|
const mergeOneStyle = (
|
|
686
817
|
existingValue,
|
|
@@ -4211,391 +4342,9 @@ const getDragCoordinates = (
|
|
|
4211
4342
|
return [leftRelativeToScrollContainer, topRelativeToScrollContainer];
|
|
4212
4343
|
};
|
|
4213
4344
|
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
// to keep in sync with https://github.com/calebdwilliams/construct-style-sheets
|
|
4217
|
-
// copy pasted into jsenv codebase to inject this code with more ease
|
|
4218
|
-
(function () {
|
|
4219
|
-
|
|
4220
|
-
if (typeof document === "undefined" || "adoptedStyleSheets" in document) {
|
|
4221
|
-
return;
|
|
4222
|
-
}
|
|
4223
|
-
|
|
4224
|
-
var hasShadyCss = "ShadyCSS" in window && !ShadyCSS.nativeShadow;
|
|
4225
|
-
var bootstrapper = document.implementation.createHTMLDocument("");
|
|
4226
|
-
var closedShadowRootRegistry = new WeakMap();
|
|
4227
|
-
var _DOMException = typeof DOMException === "object" ? Error : DOMException;
|
|
4228
|
-
var defineProperty = Object.defineProperty;
|
|
4229
|
-
var forEach = Array.prototype.forEach;
|
|
4230
|
-
|
|
4231
|
-
var importPattern = /@import.+?;?$/gm;
|
|
4232
|
-
function rejectImports(contents) {
|
|
4233
|
-
var _contents = contents.replace(importPattern, "");
|
|
4234
|
-
if (_contents !== contents) {
|
|
4235
|
-
console.warn(
|
|
4236
|
-
"@import rules are not allowed here. See https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-588352418",
|
|
4237
|
-
);
|
|
4238
|
-
}
|
|
4239
|
-
return _contents.trim();
|
|
4240
|
-
}
|
|
4241
|
-
function isElementConnected(element) {
|
|
4242
|
-
return "isConnected" in element
|
|
4243
|
-
? element.isConnected
|
|
4244
|
-
: document.contains(element);
|
|
4245
|
-
}
|
|
4246
|
-
function unique(arr) {
|
|
4247
|
-
return arr.filter(function (value, index) {
|
|
4248
|
-
return arr.indexOf(value) === index;
|
|
4249
|
-
});
|
|
4250
|
-
}
|
|
4251
|
-
function diff(arr1, arr2) {
|
|
4252
|
-
return arr1.filter(function (value) {
|
|
4253
|
-
return arr2.indexOf(value) === -1;
|
|
4254
|
-
});
|
|
4255
|
-
}
|
|
4256
|
-
function removeNode(node) {
|
|
4257
|
-
node.parentNode.removeChild(node);
|
|
4258
|
-
}
|
|
4259
|
-
function getShadowRoot(element) {
|
|
4260
|
-
return element.shadowRoot || closedShadowRootRegistry.get(element);
|
|
4261
|
-
}
|
|
4262
|
-
|
|
4263
|
-
var cssStyleSheetMethods = [
|
|
4264
|
-
"addRule",
|
|
4265
|
-
"deleteRule",
|
|
4266
|
-
"insertRule",
|
|
4267
|
-
"removeRule",
|
|
4268
|
-
];
|
|
4269
|
-
var NonConstructedStyleSheet = CSSStyleSheet;
|
|
4270
|
-
var nonConstructedProto = NonConstructedStyleSheet.prototype;
|
|
4271
|
-
nonConstructedProto.replace = function () {
|
|
4272
|
-
return Promise.reject(
|
|
4273
|
-
new _DOMException(
|
|
4274
|
-
"Can't call replace on non-constructed CSSStyleSheets.",
|
|
4275
|
-
),
|
|
4276
|
-
);
|
|
4277
|
-
};
|
|
4278
|
-
nonConstructedProto.replaceSync = function () {
|
|
4279
|
-
throw new _DOMException(
|
|
4280
|
-
"Failed to execute 'replaceSync' on 'CSSStyleSheet': Can't call replaceSync on non-constructed CSSStyleSheets.",
|
|
4281
|
-
);
|
|
4282
|
-
};
|
|
4283
|
-
function isCSSStyleSheetInstance(instance) {
|
|
4284
|
-
return typeof instance === "object"
|
|
4285
|
-
? proto$1.isPrototypeOf(instance) ||
|
|
4286
|
-
nonConstructedProto.isPrototypeOf(instance)
|
|
4287
|
-
: false;
|
|
4288
|
-
}
|
|
4289
|
-
function isNonConstructedStyleSheetInstance(instance) {
|
|
4290
|
-
return typeof instance === "object"
|
|
4291
|
-
? nonConstructedProto.isPrototypeOf(instance)
|
|
4292
|
-
: false;
|
|
4293
|
-
}
|
|
4294
|
-
var $basicStyleElement = new WeakMap();
|
|
4295
|
-
var $locations = new WeakMap();
|
|
4296
|
-
var $adoptersByLocation = new WeakMap();
|
|
4297
|
-
var $appliedMethods = new WeakMap();
|
|
4298
|
-
function addAdopterLocation(sheet, location) {
|
|
4299
|
-
var adopter = document.createElement("style");
|
|
4300
|
-
$adoptersByLocation.get(sheet).set(location, adopter);
|
|
4301
|
-
$locations.get(sheet).push(location);
|
|
4302
|
-
return adopter;
|
|
4303
|
-
}
|
|
4304
|
-
function getAdopterByLocation(sheet, location) {
|
|
4305
|
-
return $adoptersByLocation.get(sheet).get(location);
|
|
4306
|
-
}
|
|
4307
|
-
function removeAdopterLocation(sheet, location) {
|
|
4308
|
-
$adoptersByLocation.get(sheet).delete(location);
|
|
4309
|
-
$locations.set(
|
|
4310
|
-
sheet,
|
|
4311
|
-
$locations.get(sheet).filter(function (_location) {
|
|
4312
|
-
return _location !== location;
|
|
4313
|
-
}),
|
|
4314
|
-
);
|
|
4315
|
-
}
|
|
4316
|
-
function restyleAdopter(sheet, adopter) {
|
|
4317
|
-
requestAnimationFrame(function () {
|
|
4318
|
-
adopter.textContent = $basicStyleElement.get(sheet).textContent;
|
|
4319
|
-
$appliedMethods.get(sheet).forEach(function (command) {
|
|
4320
|
-
return adopter.sheet[command.method].apply(adopter.sheet, command.args);
|
|
4321
|
-
});
|
|
4322
|
-
});
|
|
4323
|
-
}
|
|
4324
|
-
function checkInvocationCorrectness(self) {
|
|
4325
|
-
if (!$basicStyleElement.has(self)) {
|
|
4326
|
-
throw new TypeError("Illegal invocation");
|
|
4327
|
-
}
|
|
4328
|
-
}
|
|
4329
|
-
function ConstructedStyleSheet() {
|
|
4330
|
-
var self = this;
|
|
4331
|
-
var style = document.createElement("style");
|
|
4332
|
-
bootstrapper.body.appendChild(style);
|
|
4333
|
-
$basicStyleElement.set(self, style);
|
|
4334
|
-
$locations.set(self, []);
|
|
4335
|
-
$adoptersByLocation.set(self, new WeakMap());
|
|
4336
|
-
$appliedMethods.set(self, []);
|
|
4337
|
-
}
|
|
4338
|
-
var proto$1 = ConstructedStyleSheet.prototype;
|
|
4339
|
-
proto$1.replace = function replace(contents) {
|
|
4340
|
-
try {
|
|
4341
|
-
this.replaceSync(contents);
|
|
4342
|
-
return Promise.resolve(this);
|
|
4343
|
-
} catch (e) {
|
|
4344
|
-
return Promise.reject(e);
|
|
4345
|
-
}
|
|
4346
|
-
};
|
|
4347
|
-
proto$1.replaceSync = function replaceSync(contents) {
|
|
4348
|
-
checkInvocationCorrectness(this);
|
|
4349
|
-
if (typeof contents === "string") {
|
|
4350
|
-
var self_1 = this;
|
|
4351
|
-
$basicStyleElement.get(self_1).textContent = rejectImports(contents);
|
|
4352
|
-
$appliedMethods.set(self_1, []);
|
|
4353
|
-
$locations.get(self_1).forEach(function (location) {
|
|
4354
|
-
if (location.isConnected()) {
|
|
4355
|
-
restyleAdopter(self_1, getAdopterByLocation(self_1, location));
|
|
4356
|
-
}
|
|
4357
|
-
});
|
|
4358
|
-
}
|
|
4359
|
-
};
|
|
4360
|
-
defineProperty(proto$1, "cssRules", {
|
|
4361
|
-
configurable: true,
|
|
4362
|
-
enumerable: true,
|
|
4363
|
-
get: function cssRules() {
|
|
4364
|
-
checkInvocationCorrectness(this);
|
|
4365
|
-
return $basicStyleElement.get(this).sheet.cssRules;
|
|
4366
|
-
},
|
|
4367
|
-
});
|
|
4368
|
-
defineProperty(proto$1, "media", {
|
|
4369
|
-
configurable: true,
|
|
4370
|
-
enumerable: true,
|
|
4371
|
-
get: function media() {
|
|
4372
|
-
checkInvocationCorrectness(this);
|
|
4373
|
-
return $basicStyleElement.get(this).sheet.media;
|
|
4374
|
-
},
|
|
4375
|
-
});
|
|
4376
|
-
cssStyleSheetMethods.forEach(function (method) {
|
|
4377
|
-
proto$1[method] = function () {
|
|
4378
|
-
var self = this;
|
|
4379
|
-
checkInvocationCorrectness(self);
|
|
4380
|
-
var args = arguments;
|
|
4381
|
-
$appliedMethods.get(self).push({ method: method, args: args });
|
|
4382
|
-
$locations.get(self).forEach(function (location) {
|
|
4383
|
-
if (location.isConnected()) {
|
|
4384
|
-
var sheet = getAdopterByLocation(self, location).sheet;
|
|
4385
|
-
sheet[method].apply(sheet, args);
|
|
4386
|
-
}
|
|
4387
|
-
});
|
|
4388
|
-
var basicSheet = $basicStyleElement.get(self).sheet;
|
|
4389
|
-
return basicSheet[method].apply(basicSheet, args);
|
|
4390
|
-
};
|
|
4391
|
-
});
|
|
4392
|
-
defineProperty(ConstructedStyleSheet, Symbol.hasInstance, {
|
|
4393
|
-
configurable: true,
|
|
4394
|
-
value: isCSSStyleSheetInstance,
|
|
4395
|
-
});
|
|
4396
|
-
|
|
4397
|
-
var defaultObserverOptions = {
|
|
4398
|
-
childList: true,
|
|
4399
|
-
subtree: true,
|
|
4400
|
-
};
|
|
4401
|
-
var locations = new WeakMap();
|
|
4402
|
-
function getAssociatedLocation(element) {
|
|
4403
|
-
var location = locations.get(element);
|
|
4404
|
-
if (!location) {
|
|
4405
|
-
location = new Location(element);
|
|
4406
|
-
locations.set(element, location);
|
|
4407
|
-
}
|
|
4408
|
-
return location;
|
|
4409
|
-
}
|
|
4410
|
-
function attachAdoptedStyleSheetProperty(constructor) {
|
|
4411
|
-
defineProperty(constructor.prototype, "adoptedStyleSheets", {
|
|
4412
|
-
configurable: true,
|
|
4413
|
-
enumerable: true,
|
|
4414
|
-
get: function () {
|
|
4415
|
-
return getAssociatedLocation(this).sheets;
|
|
4416
|
-
},
|
|
4417
|
-
set: function (sheets) {
|
|
4418
|
-
getAssociatedLocation(this).update(sheets);
|
|
4419
|
-
},
|
|
4420
|
-
});
|
|
4421
|
-
}
|
|
4422
|
-
function traverseWebComponents(node, callback) {
|
|
4423
|
-
var iter = document.createNodeIterator(
|
|
4424
|
-
node,
|
|
4425
|
-
NodeFilter.SHOW_ELEMENT,
|
|
4426
|
-
function (foundNode) {
|
|
4427
|
-
return getShadowRoot(foundNode)
|
|
4428
|
-
? NodeFilter.FILTER_ACCEPT
|
|
4429
|
-
: NodeFilter.FILTER_REJECT;
|
|
4430
|
-
},
|
|
4431
|
-
null,
|
|
4432
|
-
false,
|
|
4433
|
-
);
|
|
4434
|
-
for (var next = void 0; (next = iter.nextNode()); ) {
|
|
4435
|
-
callback(getShadowRoot(next));
|
|
4436
|
-
}
|
|
4437
|
-
}
|
|
4438
|
-
var $element = new WeakMap();
|
|
4439
|
-
var $uniqueSheets = new WeakMap();
|
|
4440
|
-
var $observer = new WeakMap();
|
|
4441
|
-
function isExistingAdopter(self, element) {
|
|
4442
|
-
return (
|
|
4443
|
-
element instanceof HTMLStyleElement &&
|
|
4444
|
-
$uniqueSheets.get(self).some(function (sheet) {
|
|
4445
|
-
return getAdopterByLocation(sheet, self);
|
|
4446
|
-
})
|
|
4447
|
-
);
|
|
4448
|
-
}
|
|
4449
|
-
function getAdopterContainer(self) {
|
|
4450
|
-
var element = $element.get(self);
|
|
4451
|
-
return element instanceof Document ? element.body : element;
|
|
4452
|
-
}
|
|
4453
|
-
function adopt(self) {
|
|
4454
|
-
var styleList = document.createDocumentFragment();
|
|
4455
|
-
var sheets = $uniqueSheets.get(self);
|
|
4456
|
-
var observer = $observer.get(self);
|
|
4457
|
-
var container = getAdopterContainer(self);
|
|
4458
|
-
observer.disconnect();
|
|
4459
|
-
sheets.forEach(function (sheet) {
|
|
4460
|
-
styleList.appendChild(
|
|
4461
|
-
getAdopterByLocation(sheet, self) || addAdopterLocation(sheet, self),
|
|
4462
|
-
);
|
|
4463
|
-
});
|
|
4464
|
-
container.insertBefore(styleList, null);
|
|
4465
|
-
observer.observe(container, defaultObserverOptions);
|
|
4466
|
-
sheets.forEach(function (sheet) {
|
|
4467
|
-
restyleAdopter(sheet, getAdopterByLocation(sheet, self));
|
|
4468
|
-
});
|
|
4469
|
-
}
|
|
4470
|
-
function Location(element) {
|
|
4471
|
-
var self = this;
|
|
4472
|
-
self.sheets = [];
|
|
4473
|
-
$element.set(self, element);
|
|
4474
|
-
$uniqueSheets.set(self, []);
|
|
4475
|
-
$observer.set(
|
|
4476
|
-
self,
|
|
4477
|
-
new MutationObserver(function (mutations, observer) {
|
|
4478
|
-
if (!document) {
|
|
4479
|
-
observer.disconnect();
|
|
4480
|
-
return;
|
|
4481
|
-
}
|
|
4482
|
-
mutations.forEach(function (mutation) {
|
|
4483
|
-
if (!hasShadyCss) {
|
|
4484
|
-
forEach.call(mutation.addedNodes, function (node) {
|
|
4485
|
-
if (!(node instanceof Element)) {
|
|
4486
|
-
return;
|
|
4487
|
-
}
|
|
4488
|
-
traverseWebComponents(node, function (root) {
|
|
4489
|
-
getAssociatedLocation(root).connect();
|
|
4490
|
-
});
|
|
4491
|
-
});
|
|
4492
|
-
}
|
|
4493
|
-
forEach.call(mutation.removedNodes, function (node) {
|
|
4494
|
-
if (!(node instanceof Element)) {
|
|
4495
|
-
return;
|
|
4496
|
-
}
|
|
4497
|
-
if (isExistingAdopter(self, node)) {
|
|
4498
|
-
adopt(self);
|
|
4499
|
-
}
|
|
4500
|
-
if (!hasShadyCss) {
|
|
4501
|
-
traverseWebComponents(node, function (root) {
|
|
4502
|
-
getAssociatedLocation(root).disconnect();
|
|
4503
|
-
});
|
|
4504
|
-
}
|
|
4505
|
-
});
|
|
4506
|
-
});
|
|
4507
|
-
}),
|
|
4508
|
-
);
|
|
4509
|
-
}
|
|
4510
|
-
Location.prototype = {
|
|
4511
|
-
isConnected: function () {
|
|
4512
|
-
var element = $element.get(this);
|
|
4513
|
-
return element instanceof Document
|
|
4514
|
-
? element.readyState !== "loading"
|
|
4515
|
-
: isElementConnected(element.host);
|
|
4516
|
-
},
|
|
4517
|
-
connect: function () {
|
|
4518
|
-
var container = getAdopterContainer(this);
|
|
4519
|
-
$observer.get(this).observe(container, defaultObserverOptions);
|
|
4520
|
-
if ($uniqueSheets.get(this).length > 0) {
|
|
4521
|
-
adopt(this);
|
|
4522
|
-
}
|
|
4523
|
-
traverseWebComponents(container, function (root) {
|
|
4524
|
-
getAssociatedLocation(root).connect();
|
|
4525
|
-
});
|
|
4526
|
-
},
|
|
4527
|
-
disconnect: function () {
|
|
4528
|
-
$observer.get(this).disconnect();
|
|
4529
|
-
},
|
|
4530
|
-
update: function (sheets) {
|
|
4531
|
-
var self = this;
|
|
4532
|
-
var locationType =
|
|
4533
|
-
$element.get(self) === document ? "Document" : "ShadowRoot";
|
|
4534
|
-
if (!Array.isArray(sheets)) {
|
|
4535
|
-
throw new TypeError(
|
|
4536
|
-
"Failed to set the 'adoptedStyleSheets' property on " +
|
|
4537
|
-
locationType +
|
|
4538
|
-
": Iterator getter is not callable.",
|
|
4539
|
-
);
|
|
4540
|
-
}
|
|
4541
|
-
if (!sheets.every(isCSSStyleSheetInstance)) {
|
|
4542
|
-
throw new TypeError(
|
|
4543
|
-
"Failed to set the 'adoptedStyleSheets' property on " +
|
|
4544
|
-
locationType +
|
|
4545
|
-
": Failed to convert value to 'CSSStyleSheet'",
|
|
4546
|
-
);
|
|
4547
|
-
}
|
|
4548
|
-
if (sheets.some(isNonConstructedStyleSheetInstance)) {
|
|
4549
|
-
throw new TypeError(
|
|
4550
|
-
"Failed to set the 'adoptedStyleSheets' property on " +
|
|
4551
|
-
locationType +
|
|
4552
|
-
": Can't adopt non-constructed stylesheets",
|
|
4553
|
-
);
|
|
4554
|
-
}
|
|
4555
|
-
self.sheets = sheets;
|
|
4556
|
-
var oldUniqueSheets = $uniqueSheets.get(self);
|
|
4557
|
-
var uniqueSheets = unique(sheets);
|
|
4558
|
-
var removedSheets = diff(oldUniqueSheets, uniqueSheets);
|
|
4559
|
-
removedSheets.forEach(function (sheet) {
|
|
4560
|
-
removeNode(getAdopterByLocation(sheet, self));
|
|
4561
|
-
removeAdopterLocation(sheet, self);
|
|
4562
|
-
});
|
|
4563
|
-
$uniqueSheets.set(self, uniqueSheets);
|
|
4564
|
-
if (self.isConnected() && uniqueSheets.length > 0) {
|
|
4565
|
-
adopt(self);
|
|
4566
|
-
}
|
|
4567
|
-
},
|
|
4568
|
-
};
|
|
4569
|
-
|
|
4570
|
-
window.CSSStyleSheet = ConstructedStyleSheet;
|
|
4571
|
-
attachAdoptedStyleSheetProperty(Document);
|
|
4572
|
-
if ("ShadowRoot" in window) {
|
|
4573
|
-
attachAdoptedStyleSheetProperty(ShadowRoot);
|
|
4574
|
-
var proto = Element.prototype;
|
|
4575
|
-
var attach_1 = proto.attachShadow;
|
|
4576
|
-
proto.attachShadow = function attachShadow(init) {
|
|
4577
|
-
var root = attach_1.call(this, init);
|
|
4578
|
-
if (init.mode === "closed") {
|
|
4579
|
-
closedShadowRootRegistry.set(this, root);
|
|
4580
|
-
}
|
|
4581
|
-
return root;
|
|
4582
|
-
};
|
|
4583
|
-
}
|
|
4584
|
-
var documentLocation = getAssociatedLocation(document);
|
|
4585
|
-
if (documentLocation.isConnected()) {
|
|
4586
|
-
documentLocation.connect();
|
|
4587
|
-
} else {
|
|
4588
|
-
document.addEventListener(
|
|
4589
|
-
"DOMContentLoaded",
|
|
4590
|
-
documentLocation.connect.bind(documentLocation),
|
|
4591
|
-
);
|
|
4592
|
-
}
|
|
4593
|
-
})();
|
|
4345
|
+
const installImportMetaCss = (importMeta) => {
|
|
4346
|
+
const stylesheet = new CSSStyleSheet({ baseUrl: importMeta.url });
|
|
4594
4347
|
|
|
4595
|
-
const installImportMetaCss = importMeta => {
|
|
4596
|
-
const stylesheet = new CSSStyleSheet({
|
|
4597
|
-
baseUrl: importMeta.url
|
|
4598
|
-
});
|
|
4599
4348
|
let called = false;
|
|
4600
4349
|
// eslint-disable-next-line accessor-pairs
|
|
4601
4350
|
Object.defineProperty(importMeta, "css", {
|
|
@@ -4606,8 +4355,11 @@ const installImportMetaCss = importMeta => {
|
|
|
4606
4355
|
}
|
|
4607
4356
|
called = true;
|
|
4608
4357
|
stylesheet.replaceSync(value);
|
|
4609
|
-
document.adoptedStyleSheets = [
|
|
4610
|
-
|
|
4358
|
+
document.adoptedStyleSheets = [
|
|
4359
|
+
...document.adoptedStyleSheets,
|
|
4360
|
+
stylesheet,
|
|
4361
|
+
];
|
|
4362
|
+
},
|
|
4611
4363
|
});
|
|
4612
4364
|
};
|
|
4613
4365
|
|
|
@@ -5666,15 +5418,6 @@ const getScrollport = (scrollBox, scrollContainer) => {
|
|
|
5666
5418
|
};
|
|
5667
5419
|
};
|
|
5668
5420
|
|
|
5669
|
-
const getElementSelector = (element) => {
|
|
5670
|
-
const tagName = element.tagName.toLowerCase();
|
|
5671
|
-
const id = element.id ? `#${element.id}` : "";
|
|
5672
|
-
const className = element.className
|
|
5673
|
-
? `.${element.className.split(" ").join(".")}`
|
|
5674
|
-
: "";
|
|
5675
|
-
return `${tagName}${id}${className}`;
|
|
5676
|
-
};
|
|
5677
|
-
|
|
5678
5421
|
installImportMetaCss(import.meta);const setupConstraintFeedbackLine = () => {
|
|
5679
5422
|
const constraintFeedbackLine = createConstraintFeedbackLine();
|
|
5680
5423
|
|
|
@@ -6700,7 +6443,7 @@ const createObstacleConstraintsFromQuerySelector = (
|
|
|
6700
6443
|
|
|
6701
6444
|
// obstacleBounds are already in scrollable-relative coordinates, no conversion needed
|
|
6702
6445
|
const obstacleObject = createObstacleContraint(obstacleBounds, {
|
|
6703
|
-
name: `${obstacleBounds.isSticky ? "sticky " : ""}obstacle (${
|
|
6446
|
+
name: `${obstacleBounds.isSticky ? "sticky " : ""}obstacle (${getElementSignature(obstacle)})`,
|
|
6704
6447
|
element: obstacle,
|
|
6705
6448
|
});
|
|
6706
6449
|
return obstacleObject;
|
|
@@ -6978,9 +6721,9 @@ const createStickyFrontierOnAxis = (
|
|
|
6978
6721
|
const hasOpposite = frontier.hasAttribute(oppositeAttrName);
|
|
6979
6722
|
// Check if element has both sides (invalid)
|
|
6980
6723
|
if (hasPrimary && hasOpposite) {
|
|
6981
|
-
const
|
|
6724
|
+
const elementSignature = getElementSignature(frontier);
|
|
6982
6725
|
console.warn(
|
|
6983
|
-
`Sticky frontier element (${
|
|
6726
|
+
`Sticky frontier element (${elementSignature}) has both ${primarySide} and ${oppositeSide} attributes.
|
|
6984
6727
|
A sticky frontier should only have one side attribute.`,
|
|
6985
6728
|
);
|
|
6986
6729
|
continue;
|
|
@@ -7003,7 +6746,7 @@ const createStickyFrontierOnAxis = (
|
|
|
7003
6746
|
element: frontier,
|
|
7004
6747
|
side: hasPrimary ? primarySide : oppositeSide,
|
|
7005
6748
|
bounds: frontierBounds,
|
|
7006
|
-
name: `sticky_frontier_${hasPrimary ? primarySide : oppositeSide} (${
|
|
6749
|
+
name: `sticky_frontier_${hasPrimary ? primarySide : oppositeSide} (${getElementSignature(frontier)})`,
|
|
7007
6750
|
};
|
|
7008
6751
|
matchingStickyFrontiers.push(stickyFrontierObject);
|
|
7009
6752
|
}
|
|
@@ -10544,34 +10287,21 @@ const useResizeStatus = (elementRef, { as = "number" } = {}) => {
|
|
|
10544
10287
|
|
|
10545
10288
|
installImportMetaCss(import.meta);
|
|
10546
10289
|
import.meta.css = /* css */ `
|
|
10547
|
-
.ui_transition_container
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
}
|
|
10552
|
-
|
|
10553
|
-
.ui_transition_outer_wrapper {
|
|
10554
|
-
display: inline-flex;
|
|
10555
|
-
flex: 1;
|
|
10556
|
-
}
|
|
10557
|
-
|
|
10558
|
-
.ui_transition_measure_wrapper {
|
|
10290
|
+
.ui_transition_container,
|
|
10291
|
+
.ui_transition_outer_wrapper,
|
|
10292
|
+
.ui_transition_measure_wrapper,
|
|
10293
|
+
.ui_transition_slot {
|
|
10559
10294
|
display: inline-flex;
|
|
10560
|
-
|
|
10295
|
+
width: fit-content;
|
|
10296
|
+
height: fit-content;
|
|
10561
10297
|
}
|
|
10562
10298
|
|
|
10299
|
+
.ui_transition_container,
|
|
10563
10300
|
.ui_transition_slot {
|
|
10564
10301
|
position: relative;
|
|
10565
|
-
display: inline-flex;
|
|
10566
|
-
flex: 1;
|
|
10567
|
-
}
|
|
10568
|
-
|
|
10569
|
-
.ui_transition_phase_overlay {
|
|
10570
|
-
position: absolute;
|
|
10571
|
-
inset: 0;
|
|
10572
|
-
pointer-events: none;
|
|
10573
10302
|
}
|
|
10574
10303
|
|
|
10304
|
+
.ui_transition_phase_overlay,
|
|
10575
10305
|
.ui_transition_content_overlay {
|
|
10576
10306
|
position: absolute;
|
|
10577
10307
|
inset: 0;
|
|
@@ -10948,7 +10678,7 @@ const initUITransition = (container) => {
|
|
|
10948
10678
|
debug(
|
|
10949
10679
|
"transition",
|
|
10950
10680
|
`Cloned previous child for ${isPhaseTransition ? "phase" : "content"} transition:`,
|
|
10951
|
-
previousChild
|
|
10681
|
+
getElementSignature(previousChild),
|
|
10952
10682
|
);
|
|
10953
10683
|
cleanup = () => oldChild.remove();
|
|
10954
10684
|
} else {
|
|
@@ -11000,7 +10730,7 @@ const initUITransition = (container) => {
|
|
|
11000
10730
|
if (localDebug.transition) {
|
|
11001
10731
|
const updateLabel =
|
|
11002
10732
|
childUIName ||
|
|
11003
|
-
(firstChild ?
|
|
10733
|
+
(firstChild ? getElementSignature(firstChild) : "cleared/empty");
|
|
11004
10734
|
console.group(`UI Update: ${updateLabel} (reason: ${reason})`);
|
|
11005
10735
|
}
|
|
11006
10736
|
|
|
@@ -11604,7 +11334,7 @@ const initUITransition = (container) => {
|
|
|
11604
11334
|
debug(
|
|
11605
11335
|
"transition",
|
|
11606
11336
|
`Attribute change detected: ${attributeName} on`,
|
|
11607
|
-
target
|
|
11337
|
+
getElementSignature(target),
|
|
11608
11338
|
);
|
|
11609
11339
|
}
|
|
11610
11340
|
}
|
|
@@ -11976,4 +11706,4 @@ const crossFade = {
|
|
|
11976
11706
|
},
|
|
11977
11707
|
};
|
|
11978
11708
|
|
|
11979
|
-
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 };
|
|
11709
|
+
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,
|
|
@@ -151,6 +151,9 @@ const normalizeNumber = (value, context, unit, propertyName) => {
|
|
|
151
151
|
|
|
152
152
|
// Normalize styles for DOM application
|
|
153
153
|
export const normalizeStyles = (styles, context = "js") => {
|
|
154
|
+
if (!styles) {
|
|
155
|
+
return {};
|
|
156
|
+
}
|
|
154
157
|
if (typeof styles === "string") {
|
|
155
158
|
styles = parseStyleString(styles);
|
|
156
159
|
return styles;
|
|
@@ -1,34 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Required HTML structure for UI transitions with smooth size and phase/content animations:
|
|
3
3
|
*
|
|
4
|
-
* <div
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
4
|
+
* <div
|
|
5
|
+
* class="ui_transition_container" <!-- Main container with relative positioning and overflow hidden -->
|
|
6
|
+
* data-size-transition <!-- Optional: enable size animations -->
|
|
7
|
+
* data-size-transition-duration <!-- Optional: size transition duration, default 300ms -->
|
|
8
|
+
* data-content-transition <!-- Content transition type: cross-fade, slide-left -->
|
|
9
|
+
* data-content-transition-duration <!-- Content transition duration -->
|
|
10
|
+
* data-phase-transition <!-- Phase transition type: cross-fade only -->
|
|
11
|
+
* data-phase-transition-duration <!-- Phase transition duration -->
|
|
11
12
|
* >
|
|
12
|
-
* <!--
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* <!-- Content measurement layer: ResizeObserver watches this to detect natural content size changes -->
|
|
19
|
-
*
|
|
20
|
-
* <div class="ui_transition_slot" data-content-key>
|
|
21
|
-
* <!-- Content slot: actual content is inserted here via children -->
|
|
22
|
-
* </div>
|
|
23
|
-
*
|
|
24
|
-
* <div class="ui_transition_phase_overlay">
|
|
25
|
-
* <!-- Phase transition overlay: clone old content phase is positioned here for content phase transitions (loading/error) -->
|
|
26
|
-
* </div>
|
|
13
|
+
* <div class="ui_transition_outer_wrapper"> <!-- Size animation target: width/height constraints are applied here during transitions -->
|
|
14
|
+
* <div class="ui_transition_measure_wrapper"> <!-- Content measurement layer: ResizeObserver watches this to detect natural content size changes -->
|
|
15
|
+
* <div class="ui_transition_slot" data-content-key></div> <!-- Content slot: actual content is here -->
|
|
16
|
+
* <div class="ui_transition_phase_overlay"> <!-- Used to transition to new phase: crossfade to new phase -->
|
|
17
|
+
* <!-- Clone of ".ui_transition_slot" children for phase transition -->
|
|
18
|
+
* </div>
|
|
27
19
|
* </div>
|
|
28
20
|
* </div>
|
|
29
21
|
*
|
|
30
|
-
* <div class="ui_transition_content_overlay">
|
|
31
|
-
* <!--
|
|
22
|
+
* <div class="ui_transition_content_overlay"> <!-- Used to transition to new content: crossfade/slide to new content -->
|
|
23
|
+
* <!-- Clone of ".ui_transition_slot" children for content transition -->
|
|
32
24
|
* </div>
|
|
33
25
|
* </div>
|
|
34
26
|
*
|
|
@@ -41,6 +33,7 @@
|
|
|
41
33
|
* - Independent content updates in the slot without affecting ongoing animations
|
|
42
34
|
*/
|
|
43
35
|
|
|
36
|
+
import { getElementSignature } from "../element_signature.js";
|
|
44
37
|
import { getHeight } from "../size/get_height.js";
|
|
45
38
|
import { getInnerWidth } from "../size/get_inner_width.js";
|
|
46
39
|
import { getWidth } from "../size/get_width.js";
|
|
@@ -57,34 +50,21 @@ import {
|
|
|
57
50
|
import { createGroupTransitionController } from "../transition/group_transition.js";
|
|
58
51
|
|
|
59
52
|
import.meta.css = /* css */ `
|
|
60
|
-
.ui_transition_container
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.ui_transition_outer_wrapper {
|
|
67
|
-
display: inline-flex;
|
|
68
|
-
flex: 1;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.ui_transition_measure_wrapper {
|
|
53
|
+
.ui_transition_container,
|
|
54
|
+
.ui_transition_outer_wrapper,
|
|
55
|
+
.ui_transition_measure_wrapper,
|
|
56
|
+
.ui_transition_slot {
|
|
72
57
|
display: inline-flex;
|
|
73
|
-
|
|
58
|
+
width: fit-content;
|
|
59
|
+
height: fit-content;
|
|
74
60
|
}
|
|
75
61
|
|
|
62
|
+
.ui_transition_container,
|
|
76
63
|
.ui_transition_slot {
|
|
77
64
|
position: relative;
|
|
78
|
-
display: inline-flex;
|
|
79
|
-
flex: 1;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.ui_transition_phase_overlay {
|
|
83
|
-
position: absolute;
|
|
84
|
-
inset: 0;
|
|
85
|
-
pointer-events: none;
|
|
86
65
|
}
|
|
87
66
|
|
|
67
|
+
.ui_transition_phase_overlay,
|
|
88
68
|
.ui_transition_content_overlay {
|
|
89
69
|
position: absolute;
|
|
90
70
|
inset: 0;
|
|
@@ -461,7 +441,7 @@ export const initUITransition = (container) => {
|
|
|
461
441
|
debug(
|
|
462
442
|
"transition",
|
|
463
443
|
`Cloned previous child for ${isPhaseTransition ? "phase" : "content"} transition:`,
|
|
464
|
-
previousChild
|
|
444
|
+
getElementSignature(previousChild),
|
|
465
445
|
);
|
|
466
446
|
cleanup = () => oldChild.remove();
|
|
467
447
|
} else {
|
|
@@ -513,7 +493,7 @@ export const initUITransition = (container) => {
|
|
|
513
493
|
if (localDebug.transition) {
|
|
514
494
|
const updateLabel =
|
|
515
495
|
childUIName ||
|
|
516
|
-
(firstChild ?
|
|
496
|
+
(firstChild ? getElementSignature(firstChild) : "cleared/empty");
|
|
517
497
|
console.group(`UI Update: ${updateLabel} (reason: ${reason})`);
|
|
518
498
|
}
|
|
519
499
|
|
|
@@ -1117,7 +1097,7 @@ export const initUITransition = (container) => {
|
|
|
1117
1097
|
debug(
|
|
1118
1098
|
"transition",
|
|
1119
1099
|
`Attribute change detected: ${attributeName} on`,
|
|
1120
|
-
target
|
|
1100
|
+
getElementSignature(target),
|
|
1121
1101
|
);
|
|
1122
1102
|
}
|
|
1123
1103
|
}
|
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
|
-
};
|