@filip.mazev/blocks 1.4.3 → 1.4.4
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/README.md +1 -1
- package/_index.scss +1 -1
- package/core/styles/_field-state.scss +16 -4
- package/core/styles/_index.scss +0 -1
- package/core/styles/_mixins.scss +2 -2
- package/core/styles/_motion.scss +1 -1
- package/core/styles/_palettes.scss +127 -132
- package/core/styles/_slider.scss +158 -40
- package/core/styles/_theme-engine.scss +43 -63
- package/core/styles/_utilities.scss +3 -4
- package/core/styles/_validation.scss +6 -1
- package/core/styles/_variables.scss +24 -14
- package/core/styles/themes/_cyberpunk-theme.scss +1 -1
- package/core/styles/themes/_green-theme.scss +1 -1
- package/core/styles/themes/_high-contrast-theme.scss +152 -133
- package/core/styles/themes/_orange-theme.scss +1 -1
- package/core/styles/themes/_purple-theme.scss +1 -1
- package/core/styles/themes/_red-theme.scss +1 -1
- package/core/styles/themes/_white-theme.scss +26 -20
- package/effects/README.md +5 -9
- package/effects/effects.css +200 -121
- package/effects/runtime/README.md +19 -11
- package/fesm2022/filip.mazev-blocks-core.mjs +27 -22
- package/fesm2022/filip.mazev-blocks-core.mjs.map +1 -1
- package/fesm2022/filip.mazev-blocks-effects-runtime.mjs +887 -67
- package/fesm2022/filip.mazev-blocks-effects-runtime.mjs.map +1 -1
- package/fesm2022/filip.mazev-blocks-effects.mjs +8 -48
- package/fesm2022/filip.mazev-blocks-effects.mjs.map +1 -1
- package/fesm2022/filip.mazev-blocks-forms.mjs +65 -61
- package/fesm2022/filip.mazev-blocks-forms.mjs.map +1 -1
- package/fesm2022/filip.mazev-blocks-modal.mjs +29 -32
- package/fesm2022/filip.mazev-blocks-modal.mjs.map +1 -1
- package/fesm2022/filip.mazev-blocks-navigation.mjs +35 -1
- package/fesm2022/filip.mazev-blocks-navigation.mjs.map +1 -1
- package/fesm2022/filip.mazev-blocks-primitives.mjs +106 -32
- package/fesm2022/filip.mazev-blocks-primitives.mjs.map +1 -1
- package/fesm2022/filip.mazev-blocks-toastr.mjs +5 -7
- package/fesm2022/filip.mazev-blocks-toastr.mjs.map +1 -1
- package/fesm2022/filip.mazev-blocks.mjs +1 -1
- package/fesm2022/filip.mazev-blocks.mjs.map +1 -1
- package/navigation/README.md +44 -0
- package/package.json +1 -1
- package/schematics/ng-add/index.js +4 -1
- package/types/filip.mazev-blocks-core.d.ts +3 -4
- package/types/filip.mazev-blocks-effects-runtime.d.ts +10 -8
- package/types/filip.mazev-blocks-effects.d.ts +5 -17
- package/types/filip.mazev-blocks-forms.d.ts +13 -11
- package/types/filip.mazev-blocks-modal.d.ts +1 -0
- package/types/filip.mazev-blocks-navigation.d.ts +21 -5
- package/types/filip.mazev-blocks-primitives.d.ts +10 -0
- package/types/filip.mazev-blocks-toastr.d.ts +4 -4
- package/types/filip.mazev-blocks.d.ts +1 -1
|
@@ -25,12 +25,11 @@ function smoothstep(low, high, value) {
|
|
|
25
25
|
}
|
|
26
26
|
const configurationEvent = "bx-glass-config-change";
|
|
27
27
|
const attributes = [
|
|
28
|
-
"data-bx-glass-material",
|
|
29
28
|
"data-bx-glass-quality",
|
|
30
29
|
"data-bx-reduced-motion",
|
|
31
30
|
"data-bx-reduced-transparency"
|
|
32
31
|
];
|
|
33
|
-
const properties = ["--bx-
|
|
32
|
+
const properties = ["--bx-glassiness"];
|
|
34
33
|
const configured = /* @__PURE__ */ new WeakSet();
|
|
35
34
|
function configureAppearance(root, initial = {}) {
|
|
36
35
|
if (configured.has(root)) throw new Error("A glass configuration is already mounted on this root.");
|
|
@@ -40,21 +39,19 @@ function configureAppearance(root, initial = {}) {
|
|
|
40
39
|
let options = { ...initial };
|
|
41
40
|
let disposed = false;
|
|
42
41
|
const notify = () => {
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
42
|
+
const viewEvent = root.ownerDocument.defaultView?.CustomEvent;
|
|
43
|
+
if (viewEvent) root.ownerDocument.dispatchEvent(new viewEvent(configurationEvent, { detail: root }));
|
|
45
44
|
};
|
|
46
45
|
const paint = () => {
|
|
47
46
|
restoreAttributes();
|
|
48
47
|
restoreStyles();
|
|
49
48
|
const values = [
|
|
50
|
-
["data-bx-glass-material", options.material],
|
|
51
49
|
["data-bx-glass-quality", options.quality],
|
|
52
50
|
["data-bx-reduced-motion", options.reducedMotion],
|
|
53
51
|
["data-bx-reduced-transparency", options.reducedTransparency]
|
|
54
52
|
];
|
|
55
53
|
for (const [name, value] of values) if (value !== void 0) root.setAttribute(name, String(value));
|
|
56
|
-
if (options.
|
|
57
|
-
if (options.tint !== void 0) root.style.setProperty("--bx-glass-opacity", String(finiteClamp(options.tint, 0, 1, .4)));
|
|
54
|
+
if (options.glassiness !== void 0) root.style.setProperty("--bx-glassiness", String(finiteClamp(options.glassiness, 0, 1, .5)));
|
|
58
55
|
notify();
|
|
59
56
|
};
|
|
60
57
|
paint();
|
|
@@ -125,10 +122,27 @@ function subscribeFrame(callback) {
|
|
|
125
122
|
}
|
|
126
123
|
};
|
|
127
124
|
}
|
|
125
|
+
const lightingOrigins = /* @__PURE__ */ new WeakMap();
|
|
126
|
+
function inheritGlassLighting(overlay, origin) {
|
|
127
|
+
if (overlay.contains(origin)) throw new Error("A lighting origin must be outside its overlay.");
|
|
128
|
+
lightingOrigins.set(overlay, origin);
|
|
129
|
+
return () => lightingOrigins.delete(overlay);
|
|
130
|
+
}
|
|
131
|
+
function lightingOrigin(element) {
|
|
132
|
+
let current = element;
|
|
133
|
+
while (current) {
|
|
134
|
+
const origin = lightingOrigins.get(current);
|
|
135
|
+
if (origin) return origin;
|
|
136
|
+
current = current.parentElement;
|
|
137
|
+
}
|
|
138
|
+
return element;
|
|
139
|
+
}
|
|
128
140
|
function lightingVisibility(doc, view) {
|
|
129
141
|
const styles = /* @__PURE__ */ new Map();
|
|
130
142
|
const boundaries = /* @__PURE__ */ new Map();
|
|
131
143
|
const layers = /* @__PURE__ */ new Map();
|
|
144
|
+
const ambientBoundaries = /* @__PURE__ */ new Map();
|
|
145
|
+
const hitStacks = /* @__PURE__ */ new Map();
|
|
132
146
|
const style = (element) => {
|
|
133
147
|
let value = styles.get(element);
|
|
134
148
|
if (!value) {
|
|
@@ -137,9 +151,55 @@ function lightingVisibility(doc, view) {
|
|
|
137
151
|
}
|
|
138
152
|
return value;
|
|
139
153
|
};
|
|
154
|
+
const elementsAt = (x, y) => {
|
|
155
|
+
const key = `${Math.round(x)},${Math.round(y)}`;
|
|
156
|
+
const cached = hitStacks.get(key);
|
|
157
|
+
if (cached) return cached;
|
|
158
|
+
const elements = [...doc.elementsFromPoint(x, y)];
|
|
159
|
+
for (let index = 0; index < elements.length; index++) {
|
|
160
|
+
const root = elements[index].shadowRoot;
|
|
161
|
+
if (!root) continue;
|
|
162
|
+
const shadowElements = root.elementsFromPoint(x, y);
|
|
163
|
+
if (!shadowElements.length) continue;
|
|
164
|
+
elements.splice(index, 0, ...shadowElements);
|
|
165
|
+
index += shadowElements.length;
|
|
166
|
+
}
|
|
167
|
+
hitStacks.set(key, elements);
|
|
168
|
+
return elements;
|
|
169
|
+
};
|
|
170
|
+
const ambientBoundary = (element) => {
|
|
171
|
+
const cached = ambientBoundaries.get(element);
|
|
172
|
+
if (cached) return cached;
|
|
173
|
+
const origin = lightingOrigin(element);
|
|
174
|
+
if (origin !== element) {
|
|
175
|
+
const result = ambientBoundary(origin);
|
|
176
|
+
ambientBoundaries.set(element, result);
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
let current = element;
|
|
180
|
+
const visited = [];
|
|
181
|
+
while (current) {
|
|
182
|
+
visited.push(current);
|
|
183
|
+
const boundary = ambientBoundaries.get(current) ?? current.closest("[data-bx-light-scope], dialog, [role=\"dialog\"], [role=\"alertdialog\"], [popover], .cdk-overlay-pane, .toast-container, bx-modal");
|
|
184
|
+
if (boundary) {
|
|
185
|
+
for (const ancestor of visited) ambientBoundaries.set(ancestor, boundary);
|
|
186
|
+
return boundary;
|
|
187
|
+
}
|
|
188
|
+
const root = current.getRootNode();
|
|
189
|
+
current = current.parentElement ?? root.host ?? null;
|
|
190
|
+
}
|
|
191
|
+
for (const ancestor of visited) ambientBoundaries.set(ancestor, doc.documentElement);
|
|
192
|
+
return doc.documentElement;
|
|
193
|
+
};
|
|
140
194
|
const boundary = (element) => {
|
|
141
195
|
const cached = boundaries.get(element);
|
|
142
196
|
if (cached) return cached;
|
|
197
|
+
const origin = lightingOrigin(element);
|
|
198
|
+
if (origin !== element) {
|
|
199
|
+
const result = boundary(origin);
|
|
200
|
+
boundaries.set(element, result);
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
143
203
|
const css = style(element);
|
|
144
204
|
const result = element.matches("[data-bx-light-scope], dialog, [role=\"dialog\"], [role=\"alertdialog\"], [popover], .cdk-overlay-pane, .toast-container, bx-modal") || css.position === "fixed" || css.position === "sticky" ? element : element.parentElement ? boundary(element.parentElement) : element;
|
|
145
205
|
boundaries.set(element, result);
|
|
@@ -167,17 +227,605 @@ function lightingVisibility(doc, view) {
|
|
|
167
227
|
const px = Math.max(bounds.left + inset, Math.min(x, bounds.right - inset));
|
|
168
228
|
const py = Math.max(bounds.top + inset, Math.min(y, bounds.bottom - inset));
|
|
169
229
|
if (px < 0 || py < 0 || px >= view.innerWidth || py >= view.innerHeight) return false;
|
|
230
|
+
if (css.pointerEvents === "none" && element.closest("bx-tooltip, [role=\"tooltip\"]")) {
|
|
231
|
+
if (css.visibility !== "visible" || css.display === "none" || Number(css.opacity) === 0) return false;
|
|
232
|
+
const origin = lightingOrigin(element);
|
|
233
|
+
if (origin !== element) {
|
|
234
|
+
const rect = origin.getBoundingClientRect();
|
|
235
|
+
return exposed(origin, rect.left + rect.width / 2, rect.top + rect.height / 2, rect);
|
|
236
|
+
}
|
|
237
|
+
const behind = doc.elementFromPoint(px, py);
|
|
238
|
+
return !!behind && ambientBoundary(behind) === ambientBoundary(element);
|
|
239
|
+
}
|
|
170
240
|
const hit = doc.elementFromPoint(px, py);
|
|
171
241
|
return !!hit && (hit === element || element.contains(hit));
|
|
172
242
|
};
|
|
173
243
|
return {
|
|
174
244
|
boundary,
|
|
175
245
|
layer,
|
|
176
|
-
exposed
|
|
246
|
+
exposed,
|
|
247
|
+
style,
|
|
248
|
+
elementsAt,
|
|
249
|
+
ambientBoundary
|
|
177
250
|
};
|
|
178
251
|
}
|
|
252
|
+
const sampleInterval = 180;
|
|
253
|
+
const liveInterval = 500;
|
|
254
|
+
const rasterSize = 32;
|
|
255
|
+
var AmbientSampler = class {
|
|
256
|
+
doc;
|
|
257
|
+
view;
|
|
258
|
+
changed;
|
|
259
|
+
observeRoot;
|
|
260
|
+
samples = /* @__PURE__ */ new Map();
|
|
261
|
+
dirty = /* @__PURE__ */ new Set();
|
|
262
|
+
pending = /* @__PURE__ */ new Set();
|
|
263
|
+
sources = /* @__PURE__ */ new Set();
|
|
264
|
+
initialized = false;
|
|
265
|
+
media = /* @__PURE__ */ new WeakMap();
|
|
266
|
+
dependencies;
|
|
267
|
+
sampleCanvas;
|
|
268
|
+
sampleContext;
|
|
269
|
+
timer;
|
|
270
|
+
liveTimer;
|
|
271
|
+
stopFrame;
|
|
272
|
+
nextSample = 0;
|
|
273
|
+
liveDelay = liveInterval;
|
|
274
|
+
motion;
|
|
275
|
+
constructor(doc, view, changed, observeRoot) {
|
|
276
|
+
this.doc = doc;
|
|
277
|
+
this.view = view;
|
|
278
|
+
this.changed = changed;
|
|
279
|
+
this.observeRoot = observeRoot;
|
|
280
|
+
this.sampleCanvas = doc.createElement("canvas");
|
|
281
|
+
this.sampleCanvas.width = this.sampleCanvas.height = rasterSize;
|
|
282
|
+
this.sampleContext = this.sampleCanvas.getContext("2d", { willReadFrequently: true });
|
|
283
|
+
this.motion = view.matchMedia("(prefers-reduced-motion: reduce)");
|
|
284
|
+
}
|
|
285
|
+
add(entry) {
|
|
286
|
+
if (!this.initialized) {
|
|
287
|
+
this.scan(this.doc);
|
|
288
|
+
this.initialized = true;
|
|
289
|
+
}
|
|
290
|
+
const root = entry.element.getRootNode();
|
|
291
|
+
if ("host" in root) this.scan(root);
|
|
292
|
+
this.samples.set(entry, {
|
|
293
|
+
lights: [],
|
|
294
|
+
media: /* @__PURE__ */ new Set(),
|
|
295
|
+
rects: /* @__PURE__ */ new Map()
|
|
296
|
+
});
|
|
297
|
+
this.invalidate(entry);
|
|
298
|
+
}
|
|
299
|
+
remove(entry) {
|
|
300
|
+
this.samples.delete(entry);
|
|
301
|
+
this.dirty.delete(entry);
|
|
302
|
+
this.pending.delete(entry);
|
|
303
|
+
this.syncLive();
|
|
304
|
+
}
|
|
305
|
+
lights(entry) {
|
|
306
|
+
return this.samples.get(entry)?.lights ?? [];
|
|
307
|
+
}
|
|
308
|
+
invalidate(entry) {
|
|
309
|
+
for (const receiver of entry ? [entry] : this.samples.keys()) if (receiver.visible && receiver.element.isConnected) this.dirty.add(receiver);
|
|
310
|
+
this.resume();
|
|
311
|
+
}
|
|
312
|
+
assetChanged(element) {
|
|
313
|
+
this.media.delete(element);
|
|
314
|
+
this.invalidate();
|
|
315
|
+
}
|
|
316
|
+
sourcesChanged(record) {
|
|
317
|
+
for (const node of record.addedNodes) if (node.nodeType === 1) this.scan(node);
|
|
318
|
+
for (const source of this.sources) if (!source.isConnected) this.sources.delete(source);
|
|
319
|
+
}
|
|
320
|
+
resume() {
|
|
321
|
+
if (this.doc.hidden) {
|
|
322
|
+
if (this.timer !== void 0) this.view.clearTimeout(this.timer);
|
|
323
|
+
if (this.liveTimer !== void 0) this.view.clearTimeout(this.liveTimer);
|
|
324
|
+
this.timer = this.liveTimer = void 0;
|
|
325
|
+
this.stopFrame?.();
|
|
326
|
+
this.stopFrame = void 0;
|
|
327
|
+
for (const entry of this.pending) this.dirty.add(entry);
|
|
328
|
+
this.pending.clear();
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (this.dirty.size && this.timer === void 0 && !this.stopFrame) this.timer = this.view.setTimeout(() => {
|
|
332
|
+
this.timer = void 0;
|
|
333
|
+
this.nextSample = this.view.performance.now() + sampleInterval;
|
|
334
|
+
this.pending = this.dirty;
|
|
335
|
+
this.dirty = /* @__PURE__ */ new Set();
|
|
336
|
+
this.stopFrame = subscribeFrame(() => this.sampleBatch());
|
|
337
|
+
}, Math.max(0, this.nextSample - this.view.performance.now()));
|
|
338
|
+
this.syncLive();
|
|
339
|
+
}
|
|
340
|
+
destroy() {
|
|
341
|
+
if (this.timer !== void 0) this.view.clearTimeout(this.timer);
|
|
342
|
+
if (this.liveTimer !== void 0) this.view.clearTimeout(this.liveTimer);
|
|
343
|
+
this.stopFrame?.();
|
|
344
|
+
this.stopFrame = void 0;
|
|
345
|
+
this.timer = this.liveTimer = void 0;
|
|
346
|
+
this.samples.clear();
|
|
347
|
+
this.dirty.clear();
|
|
348
|
+
this.pending.clear();
|
|
349
|
+
this.sources.clear();
|
|
350
|
+
this.media = /* @__PURE__ */ new WeakMap();
|
|
351
|
+
this.initialized = false;
|
|
352
|
+
this.nextSample = 0;
|
|
353
|
+
this.liveDelay = liveInterval;
|
|
354
|
+
}
|
|
355
|
+
scan(root) {
|
|
356
|
+
if ("host" in root) this.observeRoot(root);
|
|
357
|
+
if ("tagName" in root && /^(IMG|VIDEO|CANVAS)$/.test(String(root.tagName))) this.sources.add(root);
|
|
358
|
+
const shadow = "shadowRoot" in root ? root.shadowRoot : null;
|
|
359
|
+
if (shadow) this.scan(shadow);
|
|
360
|
+
for (const element of root.querySelectorAll("*")) {
|
|
361
|
+
if (/^(IMG|VIDEO|CANVAS)$/.test(element.tagName)) this.sources.add(element);
|
|
362
|
+
if (element.shadowRoot) this.scan(element.shadowRoot);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
sampleBatch() {
|
|
366
|
+
const start = this.view.performance.now();
|
|
367
|
+
const visibility = lightingVisibility(this.doc, this.view);
|
|
368
|
+
const rects = /* @__PURE__ */ new Map();
|
|
369
|
+
const rasterSamples = /* @__PURE__ */ new Map();
|
|
370
|
+
const sources = [...this.sources];
|
|
371
|
+
const completed = [];
|
|
372
|
+
for (const entry of this.pending) {
|
|
373
|
+
this.pending.delete(entry);
|
|
374
|
+
if (!entry.visible || !entry.element.isConnected || !this.samples.has(entry)) continue;
|
|
375
|
+
entry.bounds = entry.element.getBoundingClientRect();
|
|
376
|
+
this.dependencies = /* @__PURE__ */ new Set();
|
|
377
|
+
const lights = this.environmentLights(entry, sources, rasterSamples, rects, visibility);
|
|
378
|
+
const mediaRects = /* @__PURE__ */ new Map();
|
|
379
|
+
for (const source of this.dependencies) mediaRects.set(source, this.rectFor(source, rects));
|
|
380
|
+
this.samples.set(entry, {
|
|
381
|
+
lights,
|
|
382
|
+
media: this.dependencies,
|
|
383
|
+
rects: mediaRects
|
|
384
|
+
});
|
|
385
|
+
completed.push(entry);
|
|
386
|
+
if (completed.length >= 4 || this.view.performance.now() - start >= 4) break;
|
|
387
|
+
}
|
|
388
|
+
this.dependencies = void 0;
|
|
389
|
+
for (const entry of completed) this.changed(entry);
|
|
390
|
+
if (this.pending.size) return true;
|
|
391
|
+
this.stopFrame = void 0;
|
|
392
|
+
this.resume();
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
liveSources() {
|
|
396
|
+
const sources = /* @__PURE__ */ new Map();
|
|
397
|
+
for (const [entry, sample] of this.samples) {
|
|
398
|
+
const motion = inheritedAttribute(entry.element, "data-bx-reduced-motion");
|
|
399
|
+
if (!entry.visible || !entry.element.isConnected || (motion === null ? this.motion.matches : motion === "true")) continue;
|
|
400
|
+
for (const source of sample.media) {
|
|
401
|
+
if (!source.isConnected || !/^(CANVAS|VIDEO)$/.test(source.tagName) || this.media.get(source)?.data === null) continue;
|
|
402
|
+
if (source.tagName === "VIDEO" && source.paused) continue;
|
|
403
|
+
const entries = sources.get(source) ?? [];
|
|
404
|
+
entries.push(entry);
|
|
405
|
+
sources.set(source, entries);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return sources;
|
|
409
|
+
}
|
|
410
|
+
syncLive() {
|
|
411
|
+
const active = !this.doc.hidden && this.liveSources().size > 0;
|
|
412
|
+
if (!active && this.liveTimer !== void 0) {
|
|
413
|
+
this.view.clearTimeout(this.liveTimer);
|
|
414
|
+
this.liveTimer = void 0;
|
|
415
|
+
}
|
|
416
|
+
if (!active || this.liveTimer !== void 0) return;
|
|
417
|
+
this.liveTimer = this.view.setTimeout(() => {
|
|
418
|
+
this.liveTimer = void 0;
|
|
419
|
+
let changed = false;
|
|
420
|
+
const recolored = /* @__PURE__ */ new Set();
|
|
421
|
+
const visibility = lightingVisibility(this.doc, this.view);
|
|
422
|
+
for (const [source, entries] of this.liveSources()) {
|
|
423
|
+
const previous = this.media.get(source);
|
|
424
|
+
const next = this.readRaster(source, true);
|
|
425
|
+
if (previous?.data && next?.data && this.samePixels(previous.data, next.data)) continue;
|
|
426
|
+
if (previous?.data === next?.data) continue;
|
|
427
|
+
changed = true;
|
|
428
|
+
const coverageChanged = !previous?.data || !next?.data || !this.samePixels(previous.data, next.data, true);
|
|
429
|
+
for (const entry of entries) {
|
|
430
|
+
const sample = this.samples.get(entry);
|
|
431
|
+
if (!sample || coverageChanged || this.dirty.has(entry) || this.pending.has(entry)) {
|
|
432
|
+
this.dirty.add(entry);
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
const pixels = /* @__PURE__ */ new Map([[source, next?.data ?? null]]);
|
|
436
|
+
sample.lights = sample.lights.map((light) => {
|
|
437
|
+
if (light.source !== source) return light;
|
|
438
|
+
const color = this.rasterColorAt(source, light.x, light.y, pixels, sample.rects, visibility);
|
|
439
|
+
if (!color || color === light.color) return light;
|
|
440
|
+
recolored.add(entry);
|
|
441
|
+
return {
|
|
442
|
+
...light,
|
|
443
|
+
color
|
|
444
|
+
};
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
for (const entry of recolored) this.changed(entry);
|
|
449
|
+
this.liveDelay = changed ? liveInterval : Math.min(2e3, this.liveDelay * 2);
|
|
450
|
+
this.resume();
|
|
451
|
+
}, this.liveDelay);
|
|
452
|
+
}
|
|
453
|
+
samePixels(first, second, alphaOnly = false) {
|
|
454
|
+
for (let index = alphaOnly ? 3 : 0; index < first.data.length; index += alphaOnly ? 4 : 1) if (first.data[index] >> 3 !== second.data[index] >> 3) return false;
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
readRaster(element, refresh = false) {
|
|
458
|
+
const cached = this.media.get(element);
|
|
459
|
+
if (cached && !refresh) return cached;
|
|
460
|
+
const tag = element.tagName;
|
|
461
|
+
let source;
|
|
462
|
+
let stamp;
|
|
463
|
+
if (tag === "IMG") {
|
|
464
|
+
const image = element;
|
|
465
|
+
if (!image.complete || !image.naturalWidth) return void 0;
|
|
466
|
+
source = image;
|
|
467
|
+
} else if (tag === "VIDEO") {
|
|
468
|
+
const video = element;
|
|
469
|
+
if (video.readyState < 2 || !video.videoWidth) return void 0;
|
|
470
|
+
stamp = video.currentTime;
|
|
471
|
+
if (cached?.stamp === stamp) return cached;
|
|
472
|
+
source = video;
|
|
473
|
+
} else {
|
|
474
|
+
const canvas = element;
|
|
475
|
+
if (!canvas.width || !canvas.height) return void 0;
|
|
476
|
+
source = canvas;
|
|
477
|
+
}
|
|
478
|
+
if (!this.sampleContext) return void 0;
|
|
479
|
+
let data = null;
|
|
480
|
+
try {
|
|
481
|
+
this.sampleContext.clearRect(0, 0, rasterSize, rasterSize);
|
|
482
|
+
this.sampleContext.drawImage(source, 0, 0, rasterSize, rasterSize);
|
|
483
|
+
data = this.sampleContext.getImageData(0, 0, rasterSize, rasterSize);
|
|
484
|
+
} catch {
|
|
485
|
+
this.sampleCanvas.width = rasterSize;
|
|
486
|
+
this.sampleContext = this.sampleCanvas.getContext("2d", { willReadFrequently: true });
|
|
487
|
+
}
|
|
488
|
+
const result = {
|
|
489
|
+
data,
|
|
490
|
+
stamp
|
|
491
|
+
};
|
|
492
|
+
this.media.set(element, result);
|
|
493
|
+
return result;
|
|
494
|
+
}
|
|
495
|
+
environmentLights(entry, rasterSources, rasterSamples, rects, visibility) {
|
|
496
|
+
const bounds = entry.bounds;
|
|
497
|
+
if (!bounds || bounds.width <= 0 || bounds.height <= 0 || entry.element.getAttribute("data-bx-surface") === "solid" || entry.element.getAttribute("data-bx-reduced-transparency") === "true") return [];
|
|
498
|
+
const insetX = Math.min(3, bounds.width * .1);
|
|
499
|
+
const insetY = Math.min(3, bounds.height * .1);
|
|
500
|
+
const points = [
|
|
501
|
+
{
|
|
502
|
+
x: bounds.left + bounds.width * .24,
|
|
503
|
+
y: bounds.top + insetY,
|
|
504
|
+
side: "inside",
|
|
505
|
+
edge: 0
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
x: bounds.left + bounds.width * .76,
|
|
509
|
+
y: bounds.top + insetY,
|
|
510
|
+
side: "inside",
|
|
511
|
+
edge: 0
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
x: bounds.right - insetX,
|
|
515
|
+
y: bounds.top + bounds.height * .24,
|
|
516
|
+
side: "inside",
|
|
517
|
+
edge: 1
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
x: bounds.right - insetX,
|
|
521
|
+
y: bounds.top + bounds.height * .76,
|
|
522
|
+
side: "inside",
|
|
523
|
+
edge: 1
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
x: bounds.left + bounds.width * .76,
|
|
527
|
+
y: bounds.bottom - insetY,
|
|
528
|
+
side: "inside",
|
|
529
|
+
edge: 2
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
x: bounds.left + bounds.width * .24,
|
|
533
|
+
y: bounds.bottom - insetY,
|
|
534
|
+
side: "inside",
|
|
535
|
+
edge: 2
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
x: bounds.left + insetX,
|
|
539
|
+
y: bounds.top + bounds.height * .76,
|
|
540
|
+
side: "inside",
|
|
541
|
+
edge: 3
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
x: bounds.left + insetX,
|
|
545
|
+
y: bounds.top + bounds.height * .24,
|
|
546
|
+
side: "inside",
|
|
547
|
+
edge: 3
|
|
548
|
+
},
|
|
549
|
+
...[24, 96].flatMap((gap) => [
|
|
550
|
+
{
|
|
551
|
+
x: bounds.left + bounds.width / 2,
|
|
552
|
+
y: bounds.top - gap,
|
|
553
|
+
side: "outside",
|
|
554
|
+
edge: 0
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
x: bounds.right + gap,
|
|
558
|
+
y: bounds.top + bounds.height / 2,
|
|
559
|
+
side: "outside",
|
|
560
|
+
edge: 1
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
x: bounds.left + bounds.width / 2,
|
|
564
|
+
y: bounds.bottom + gap,
|
|
565
|
+
side: "outside",
|
|
566
|
+
edge: 2
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
x: bounds.left - gap,
|
|
570
|
+
y: bounds.top + bounds.height / 2,
|
|
571
|
+
side: "outside",
|
|
572
|
+
edge: 3
|
|
573
|
+
}
|
|
574
|
+
])
|
|
575
|
+
];
|
|
576
|
+
const visibleX = Math.max(1, Math.min(this.view.innerWidth - 1, bounds.left + bounds.width / 2));
|
|
577
|
+
const visibleY = Math.max(1, Math.min(this.view.innerHeight - 1, bounds.top + bounds.height / 2));
|
|
578
|
+
if (!visibility.exposed(entry.element, visibleX, visibleY, bounds)) return [];
|
|
579
|
+
const targetBoundary = visibility.ambientBoundary(entry.element);
|
|
580
|
+
const passiveTooltip = visibility.style(entry.element).pointerEvents === "none" && !!entry.element.closest("bx-tooltip, [role=\"tooltip\"]");
|
|
581
|
+
rasterSources = rasterSources.filter((source) => {
|
|
582
|
+
const rect = this.rectFor(source, rects);
|
|
583
|
+
return rect.width > 0 && rect.height > 0 && rect.right >= bounds.left - 96 && rect.left <= bounds.right + 96 && rect.bottom >= bounds.top - 96 && rect.top <= bounds.bottom + 96 && visibility.ambientBoundary(source) === targetBoundary;
|
|
584
|
+
});
|
|
585
|
+
const lights = [];
|
|
586
|
+
const sourcesByEdge = /* @__PURE__ */ new Map();
|
|
587
|
+
const colorsByEdge = /* @__PURE__ */ new Map();
|
|
588
|
+
points.forEach((point, index) => {
|
|
589
|
+
if (point.x < 0 || point.y < 0 || point.x >= this.view.innerWidth || point.y >= this.view.innerHeight) return;
|
|
590
|
+
const stack = visibility.elementsAt(point.x, point.y);
|
|
591
|
+
const receiverIndex = stack.findIndex((element) => this.isInside(element, entry.element));
|
|
592
|
+
let source;
|
|
593
|
+
let foreground = false;
|
|
594
|
+
if (point.side === "inside" && receiverIndex >= 0) {
|
|
595
|
+
source = this.findVisualSource(stack.slice(0, receiverIndex), entry, point.x, point.y, visibility, rasterSamples, rects, {
|
|
596
|
+
skipAncestors: true,
|
|
597
|
+
skipRoot: true
|
|
598
|
+
});
|
|
599
|
+
if (source) foreground = true;
|
|
600
|
+
source ??= this.findRasterSource(rasterSources, entry, point.x, point.y, visibility, rasterSamples, rects, stack, receiverIndex, true, targetBoundary);
|
|
601
|
+
source ??= this.findVisualSource(stack.slice(receiverIndex + 1), entry, point.x, point.y, visibility, rasterSamples, rects, {
|
|
602
|
+
skipAncestors: true,
|
|
603
|
+
skipRoot: true
|
|
604
|
+
});
|
|
605
|
+
source ??= this.findRasterSource(rasterSources, entry, point.x, point.y, visibility, rasterSamples, rects, stack, receiverIndex, false, targetBoundary);
|
|
606
|
+
source ??= this.findVisualSource(stack.slice(receiverIndex + 1), entry, point.x, point.y, visibility, rasterSamples, rects, {
|
|
607
|
+
skipAncestors: false,
|
|
608
|
+
skipRoot: true
|
|
609
|
+
});
|
|
610
|
+
source ??= this.findVisualSource(stack.slice(receiverIndex + 1), entry, point.x, point.y, visibility, rasterSamples, rects, {
|
|
611
|
+
skipAncestors: false,
|
|
612
|
+
skipRoot: false
|
|
613
|
+
});
|
|
614
|
+
} else if (point.side === "outside" || receiverIndex < 0 && passiveTooltip) {
|
|
615
|
+
source = this.findVisualSource(stack, entry, point.x, point.y, visibility, rasterSamples, rects, {
|
|
616
|
+
skipAncestors: true,
|
|
617
|
+
skipRoot: true
|
|
618
|
+
});
|
|
619
|
+
source ??= this.findRasterSource(rasterSources, entry, point.x, point.y, visibility, rasterSamples, rects, stack, -1, void 0, targetBoundary);
|
|
620
|
+
source ??= this.findVisualSource(stack, entry, point.x, point.y, visibility, rasterSamples, rects, {
|
|
621
|
+
skipAncestors: false,
|
|
622
|
+
skipRoot: true
|
|
623
|
+
});
|
|
624
|
+
source ??= this.findVisualSource(stack, entry, point.x, point.y, visibility, rasterSamples, rects, {
|
|
625
|
+
skipAncestors: false,
|
|
626
|
+
skipRoot: false
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
if (!source || visibility.ambientBoundary(source.element) !== targetBoundary) return;
|
|
630
|
+
const sourceRect = this.rectFor(source.element, rects);
|
|
631
|
+
const gapX = Math.max(bounds.left - sourceRect.right, sourceRect.left - bounds.right, 0);
|
|
632
|
+
const gapY = Math.max(bounds.top - sourceRect.bottom, sourceRect.top - bounds.bottom, 0);
|
|
633
|
+
if (point.side === "outside" && Math.hypot(gapX, gapY) > 160) return;
|
|
634
|
+
if (source.element === entry.element || this.isInside(source.element, entry.element)) return;
|
|
635
|
+
const edgeSources = sourcesByEdge.get(point.edge) ?? /* @__PURE__ */ new Set();
|
|
636
|
+
if (point.side === "outside" && edgeSources.has(source.element)) return;
|
|
637
|
+
const edgeColors = colorsByEdge.get(point.edge) ?? /* @__PURE__ */ new Map();
|
|
638
|
+
const sourceColors = edgeColors.get(source.element) ?? /* @__PURE__ */ new Set();
|
|
639
|
+
if (point.side === "outside" && sourceColors.has(source.color)) return;
|
|
640
|
+
sourceColors.add(source.color);
|
|
641
|
+
edgeColors.set(source.element, sourceColors);
|
|
642
|
+
colorsByEdge.set(point.edge, edgeColors);
|
|
643
|
+
if (point.side === "inside") {
|
|
644
|
+
edgeSources.add(source.element);
|
|
645
|
+
sourcesByEdge.set(point.edge, edgeSources);
|
|
646
|
+
}
|
|
647
|
+
if (point.side === "outside") foreground = this.isAmbientForeground(source.element, entry.element, stack, -1, visibility);
|
|
648
|
+
const depth = foreground ? 24 : 52;
|
|
649
|
+
const gap = Math.hypot(gapX, gapY);
|
|
650
|
+
const edgeLength = point.edge % 2 === 0 ? bounds.width : bounds.height;
|
|
651
|
+
lights.push({
|
|
652
|
+
key: `environment:${entry.id}:${index}`,
|
|
653
|
+
source: source.element.namespaceURI === "http://www.w3.org/1999/xhtml" ? source.element : void 0,
|
|
654
|
+
ambient: true,
|
|
655
|
+
x: point.x,
|
|
656
|
+
y: point.y,
|
|
657
|
+
z: depth,
|
|
658
|
+
color: source.color,
|
|
659
|
+
intensity: (point.side === "inside" ? .38 : .48) * Math.pow(Math.max(0, 1 - gap / 160), 2),
|
|
660
|
+
radius: 240,
|
|
661
|
+
spread: Math.max(48, Math.min(240, edgeLength * .65))
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
return lights;
|
|
665
|
+
}
|
|
666
|
+
findVisualSource(elements, entry, x, y, visibility, rasterSamples, rects, options) {
|
|
667
|
+
for (const element of elements) {
|
|
668
|
+
if (element === entry.element || this.isInside(element, entry.element)) continue;
|
|
669
|
+
if (options.skipAncestors && this.isInside(entry.element, element)) continue;
|
|
670
|
+
if (options.skipRoot && (element === this.doc.documentElement || element === this.doc.body)) continue;
|
|
671
|
+
if (visibility.ambientBoundary(element) !== visibility.ambientBoundary(entry.element)) continue;
|
|
672
|
+
const color = this.visualColorAt(element, x, y, visibility, rasterSamples, rects);
|
|
673
|
+
if (color) return {
|
|
674
|
+
element,
|
|
675
|
+
color
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
findRasterSource(sources, entry, x, y, visibility, rasterSamples, rects, stack, receiverIndex, foreground, targetBoundary) {
|
|
680
|
+
const matches = sources.filter((element) => {
|
|
681
|
+
if (this.isInside(element, entry.element) || visibility.ambientBoundary(element) !== targetBoundary) return false;
|
|
682
|
+
const bounds = this.rectFor(element, rects);
|
|
683
|
+
if (x < bounds.left || x >= bounds.right || y < bounds.top || y >= bounds.bottom) return false;
|
|
684
|
+
return foreground === void 0 || this.isAmbientForeground(element, entry.element, stack, receiverIndex, visibility) === foreground;
|
|
685
|
+
}).sort((first, second) => this.rasterOrder(second, stack, entry.element, visibility) - this.rasterOrder(first, stack, entry.element, visibility));
|
|
686
|
+
for (const element of matches) {
|
|
687
|
+
const color = this.rasterColorAt(element, x, y, rasterSamples, rects, visibility);
|
|
688
|
+
if (color) return {
|
|
689
|
+
element,
|
|
690
|
+
color
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
visualColorAt(element, x, y, visibility, rasterSamples, rects) {
|
|
695
|
+
const raster = this.rasterColorAt(element, x, y, rasterSamples, rects, visibility);
|
|
696
|
+
if (raster) return raster;
|
|
697
|
+
const style = visibility.style(element);
|
|
698
|
+
const gradient = this.gradientColorAt(style.backgroundImage, this.rectFor(element, rects), x, y);
|
|
699
|
+
if (gradient) return gradient;
|
|
700
|
+
if (!this.isTransparent(style.backgroundColor)) return style.backgroundColor;
|
|
701
|
+
for (const side of [
|
|
702
|
+
"top",
|
|
703
|
+
"right",
|
|
704
|
+
"bottom",
|
|
705
|
+
"left"
|
|
706
|
+
]) {
|
|
707
|
+
const color = style.getPropertyValue(`border-${side}-color`);
|
|
708
|
+
if (parseFloat(style.getPropertyValue(`border-${side}-width`)) > 0 && !this.isTransparent(color)) return color;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
rasterColorAt(element, x, y, cache, rects, visibility) {
|
|
712
|
+
const tag = element.tagName;
|
|
713
|
+
if (!/^(IMG|CANVAS|VIDEO)$/.test(tag)) return void 0;
|
|
714
|
+
const bounds = this.rectFor(element, rects);
|
|
715
|
+
if (bounds.width <= 0 || bounds.height <= 0) return void 0;
|
|
716
|
+
const css = visibility.style(element);
|
|
717
|
+
if (css.visibility !== "visible" || Number(css.opacity) === 0) return void 0;
|
|
718
|
+
this.dependencies?.add(element);
|
|
719
|
+
let data = cache.get(element);
|
|
720
|
+
if (data === void 0) {
|
|
721
|
+
data = this.readRaster(element)?.data ?? null;
|
|
722
|
+
cache.set(element, data);
|
|
723
|
+
}
|
|
724
|
+
if (!data) return void 0;
|
|
725
|
+
let u = Math.max(0, Math.min(1, (x - bounds.left) / bounds.width));
|
|
726
|
+
let v = Math.max(0, Math.min(1, (y - bounds.top) / bounds.height));
|
|
727
|
+
if ((tag === "IMG" || tag === "VIDEO") && css.objectFit !== "fill") {
|
|
728
|
+
const width = tag === "IMG" ? element.naturalWidth : element.videoWidth;
|
|
729
|
+
const height = tag === "IMG" ? element.naturalHeight : element.videoHeight;
|
|
730
|
+
if (!width || !height) return void 0;
|
|
731
|
+
const contain = Math.min(bounds.width / width, bounds.height / height);
|
|
732
|
+
const scale = css.objectFit === "cover" ? Math.max(bounds.width / width, bounds.height / height) : css.objectFit === "none" ? 1 : css.objectFit === "scale-down" ? Math.min(1, contain) : contain;
|
|
733
|
+
const [positionX = "50%", positionY = "50%"] = css.objectPosition.split(" ");
|
|
734
|
+
const offset = (position, space) => {
|
|
735
|
+
if (position.endsWith("%")) return parseFloat(position) / 100 * space;
|
|
736
|
+
if (position.endsWith("px")) return parseFloat(position);
|
|
737
|
+
return position === "left" || position === "top" ? 0 : position === "right" || position === "bottom" ? space : space / 2;
|
|
738
|
+
};
|
|
739
|
+
u = (x - bounds.left - offset(positionX, bounds.width - width * scale)) / (width * scale);
|
|
740
|
+
v = (y - bounds.top - offset(positionY, bounds.height - height * scale)) / (height * scale);
|
|
741
|
+
if (u < 0 || u > 1 || v < 0 || v > 1) return void 0;
|
|
742
|
+
}
|
|
743
|
+
const px = Math.max(0, Math.min(data.width - 1, Math.floor(u * data.width)));
|
|
744
|
+
const offset = (Math.max(0, Math.min(data.height - 1, Math.floor(v * data.height))) * data.width + px) * 4;
|
|
745
|
+
const alpha = data.data[offset + 3] / 255;
|
|
746
|
+
if (alpha < .08) return void 0;
|
|
747
|
+
return `rgb(${data.data[offset]} ${data.data[offset + 1]} ${data.data[offset + 2]} / ${alpha.toFixed(2)})`;
|
|
748
|
+
}
|
|
749
|
+
gradientColorAt(image, bounds, x, y) {
|
|
750
|
+
if (!image.includes("gradient(")) return void 0;
|
|
751
|
+
const colors = (image.match(/#[\da-f]{3,8}\b|(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color)\([^)]*\)|\b(?:transparent|black|white|red|green|blue|orange|yellow|purple|gray|grey|teal|cyan|magenta)\b/gi) ?? []).filter((color) => !this.isTransparent(color));
|
|
752
|
+
if (!colors.length) return void 0;
|
|
753
|
+
let amount = .5;
|
|
754
|
+
if (image.includes("linear-gradient(")) {
|
|
755
|
+
const prelude = image.match(/linear-gradient\(\s*([^,]+)/i)?.[1]?.trim().toLowerCase() ?? "";
|
|
756
|
+
let dx = 0, dy = 1;
|
|
757
|
+
const angle = prelude.match(/^(-?[\d.]+)deg$/);
|
|
758
|
+
if (angle) {
|
|
759
|
+
const radians = Number(angle[1]) * Math.PI / 180;
|
|
760
|
+
dx = Math.sin(radians);
|
|
761
|
+
dy = -Math.cos(radians);
|
|
762
|
+
} else if (prelude.startsWith("to ")) {
|
|
763
|
+
dx = prelude.includes("right") ? 1 : prelude.includes("left") ? -1 : 0;
|
|
764
|
+
dy = prelude.includes("bottom") ? 1 : prelude.includes("top") ? -1 : 0;
|
|
765
|
+
}
|
|
766
|
+
const range = Math.max(.01, Math.abs(dx) + Math.abs(dy));
|
|
767
|
+
const u = (x - bounds.left) / Math.max(1, bounds.width) - .5;
|
|
768
|
+
const v = (y - bounds.top) / Math.max(1, bounds.height) - .5;
|
|
769
|
+
amount = Math.max(0, Math.min(1, .5 + (u * dx + v * dy) / range));
|
|
770
|
+
} else {
|
|
771
|
+
const u = (x - (bounds.left + bounds.width / 2)) / Math.max(1, bounds.width / 2);
|
|
772
|
+
const v = (y - (bounds.top + bounds.height / 2)) / Math.max(1, bounds.height / 2);
|
|
773
|
+
amount = Math.max(0, Math.min(1, Math.hypot(u, v) / Math.SQRT2));
|
|
774
|
+
}
|
|
775
|
+
return colors[Math.min(colors.length - 1, Math.round(amount * (colors.length - 1)))];
|
|
776
|
+
}
|
|
777
|
+
isTransparent(color) {
|
|
778
|
+
const value = color.trim().toLowerCase();
|
|
779
|
+
if (!value || value === "transparent") return true;
|
|
780
|
+
const alpha = value.match(/\/\s*([\d.]+)%?\s*\)$/) ?? (/^rgba\(|^hsla\(/.test(value) ? value.match(/,\s*([\d.]+)\s*\)$/) : null);
|
|
781
|
+
return !!alpha && Number(alpha[1]) <= .02;
|
|
782
|
+
}
|
|
783
|
+
isInside(element, ancestor) {
|
|
784
|
+
let current = element;
|
|
785
|
+
while (current) {
|
|
786
|
+
if (current === ancestor) return true;
|
|
787
|
+
const root = current.getRootNode();
|
|
788
|
+
current = current.parentElement ?? root.host ?? null;
|
|
789
|
+
}
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
layerElement(element, visibility) {
|
|
793
|
+
let current = element;
|
|
794
|
+
while (current && current.namespaceURI !== "http://www.w3.org/1999/xhtml") {
|
|
795
|
+
const root = current.getRootNode();
|
|
796
|
+
current = current.parentElement ?? root.host ?? null;
|
|
797
|
+
}
|
|
798
|
+
return current ? visibility.layer(current) : this.doc.documentElement;
|
|
799
|
+
}
|
|
800
|
+
zIndex(element, visibility) {
|
|
801
|
+
const value = Number.parseFloat(visibility.style(element).zIndex);
|
|
802
|
+
return Number.isFinite(value) ? value : 0;
|
|
803
|
+
}
|
|
804
|
+
isAmbientForeground(source, receiver, stack, receiverIndex, visibility) {
|
|
805
|
+
const sourceIndex = stack.indexOf(source);
|
|
806
|
+
if (sourceIndex >= 0 && receiverIndex >= 0) return sourceIndex < receiverIndex;
|
|
807
|
+
const sourceLayer = this.layerElement(source, visibility);
|
|
808
|
+
const receiverLayer = visibility.layer(receiver);
|
|
809
|
+
const depth = this.zIndex(sourceLayer, visibility) - this.zIndex(receiverLayer, visibility);
|
|
810
|
+
if (depth !== 0) return depth > 0;
|
|
811
|
+
return !!(source.compareDocumentPosition(receiver) & 2);
|
|
812
|
+
}
|
|
813
|
+
rasterOrder(element, stack, receiver, visibility) {
|
|
814
|
+
const index = stack.indexOf(element);
|
|
815
|
+
if (index >= 0) return 1e4 - index;
|
|
816
|
+
return this.zIndex(this.layerElement(element, visibility), visibility) - this.zIndex(visibility.layer(receiver), visibility);
|
|
817
|
+
}
|
|
818
|
+
rectFor(element, rects) {
|
|
819
|
+
let rect = rects.get(element);
|
|
820
|
+
if (!rect) {
|
|
821
|
+
rect = element.getBoundingClientRect();
|
|
822
|
+
rects.set(element, rect);
|
|
823
|
+
}
|
|
824
|
+
return rect;
|
|
825
|
+
}
|
|
826
|
+
};
|
|
179
827
|
const runtimes = /* @__PURE__ */ new WeakMap();
|
|
180
|
-
const activeStateSelector = "[aria-pressed=\"true\"], [aria-selected=\"true\"], [aria-expanded=\"true\"], [aria-current]:not([aria-current=\"false\"]):not([aria-current=\"\"])";
|
|
828
|
+
const activeStateSelector = ":checked, [aria-checked=\"true\"], [aria-pressed=\"true\"], [aria-selected=\"true\"], [aria-expanded=\"true\"], [aria-current]:not([aria-current=\"false\"]):not([aria-current=\"\"])";
|
|
181
829
|
const disabledSelector = ":disabled, [aria-disabled=\"true\"], .is-disabled, [inert]";
|
|
182
830
|
function runtimeFor(doc) {
|
|
183
831
|
let runtime = runtimes.get(doc);
|
|
@@ -226,6 +874,16 @@ var SceneRuntime = class {
|
|
|
226
874
|
motion;
|
|
227
875
|
transparency;
|
|
228
876
|
view;
|
|
877
|
+
ambient;
|
|
878
|
+
renderedStyles = /* @__PURE__ */ new WeakMap();
|
|
879
|
+
assetLoad = (event) => {
|
|
880
|
+
const target = event.target;
|
|
881
|
+
if (target && /^(IMG|VIDEO)$/.test(target.tagName)) this.ambient.assetChanged(target);
|
|
882
|
+
};
|
|
883
|
+
visibilityChange = () => {
|
|
884
|
+
if (!this.doc.hidden) this.refresh();
|
|
885
|
+
this.ambient.resume();
|
|
886
|
+
};
|
|
229
887
|
down = (event) => {
|
|
230
888
|
this.pressed = this.find(event);
|
|
231
889
|
this.pressed?.hooks.press();
|
|
@@ -256,7 +914,8 @@ var SceneRuntime = class {
|
|
|
256
914
|
}
|
|
257
915
|
};
|
|
258
916
|
out = (event) => {
|
|
259
|
-
|
|
917
|
+
const target = this.hovered?.element.closest("label") ?? this.hovered?.element;
|
|
918
|
+
if (target && !target.contains(event.relatedTarget)) {
|
|
260
919
|
this.hovered = void 0;
|
|
261
920
|
this.updateEmitter();
|
|
262
921
|
}
|
|
@@ -273,6 +932,10 @@ var SceneRuntime = class {
|
|
|
273
932
|
this.focusedTarget = void 0;
|
|
274
933
|
this.updateEmitter();
|
|
275
934
|
};
|
|
935
|
+
change = () => {
|
|
936
|
+
for (const entry of this.entries.values()) this.updateSelected(entry);
|
|
937
|
+
this.updateEmitter();
|
|
938
|
+
};
|
|
276
939
|
sync = () => {
|
|
277
940
|
for (const entry of this.entries.values()) {
|
|
278
941
|
entry.hooks.sync();
|
|
@@ -282,8 +945,10 @@ var SceneRuntime = class {
|
|
|
282
945
|
this.refresh();
|
|
283
946
|
};
|
|
284
947
|
refresh = () => {
|
|
948
|
+
this.ambient.invalidate();
|
|
285
949
|
for (const entry of this.entries.values()) {
|
|
286
950
|
entry.bounds = void 0;
|
|
951
|
+
entry.lastLighting = void 0;
|
|
287
952
|
if (entry.visible) this.queue.add(entry);
|
|
288
953
|
}
|
|
289
954
|
this.schedule();
|
|
@@ -293,6 +958,11 @@ var SceneRuntime = class {
|
|
|
293
958
|
const view = doc.defaultView;
|
|
294
959
|
if (!view) throw new Error("Glass scenes require a browser document.");
|
|
295
960
|
this.view = view;
|
|
961
|
+
this.ambient = new AmbientSampler(doc, view, (entry) => {
|
|
962
|
+
if (this.entries.get(entry.element) !== entry) return;
|
|
963
|
+
this.queue.add(entry);
|
|
964
|
+
this.schedule();
|
|
965
|
+
}, (root) => this.observeState(root));
|
|
296
966
|
this.motion = view.matchMedia("(prefers-reduced-motion: reduce)");
|
|
297
967
|
this.transparency = view.matchMedia("(prefers-reduced-transparency: reduce)");
|
|
298
968
|
if (typeof view.IntersectionObserver === "function") this.observer = new view.IntersectionObserver((records) => {
|
|
@@ -300,8 +970,10 @@ var SceneRuntime = class {
|
|
|
300
970
|
const entry = this.entries.get(record.target);
|
|
301
971
|
if (!entry) continue;
|
|
302
972
|
entry.visible = record.isIntersecting;
|
|
973
|
+
this.ambient.invalidate(entry);
|
|
303
974
|
if (entry.visible) {
|
|
304
975
|
entry.bounds = void 0;
|
|
976
|
+
entry.lastLighting = void 0;
|
|
305
977
|
this.queue.add(entry);
|
|
306
978
|
}
|
|
307
979
|
}
|
|
@@ -309,15 +981,45 @@ var SceneRuntime = class {
|
|
|
309
981
|
}, { rootMargin: "64px" });
|
|
310
982
|
if (typeof view.MutationObserver === "function") this.stateObserver = new view.MutationObserver((records) => {
|
|
311
983
|
const changed = /* @__PURE__ */ new Set();
|
|
984
|
+
let visualChanged = false;
|
|
312
985
|
for (const record of records) {
|
|
313
|
-
|
|
986
|
+
if (record.type === "childList") {
|
|
987
|
+
if (record.target.nodeType === 1 && this.isLightingDecoration(record.target)) continue;
|
|
988
|
+
if ([...record.addedNodes, ...record.removedNodes].some((node) => !this.isLightingDecoration(node))) {
|
|
989
|
+
visualChanged = true;
|
|
990
|
+
this.ambient.sourcesChanged(record);
|
|
991
|
+
}
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
const target = record.target;
|
|
995
|
+
if (record.attributeName === "style" && this.renderedStyles.get(target) === target.getAttribute("style")) continue;
|
|
996
|
+
if (record.attributeName === "class") {
|
|
997
|
+
const entry = this.entries.get(target);
|
|
998
|
+
if (entry) changed.add(entry);
|
|
999
|
+
}
|
|
1000
|
+
if (record.attributeName === "class" || record.attributeName === "style" || record.attributeName === "hidden" || /^(role|data-bx-light-scope|data-bx-surface|data-bx-reduced-transparency|data-bx-reduced-motion|src|srcset|width|height)$/.test(record.attributeName ?? "")) {
|
|
1001
|
+
if (!this.isLightingDecoration(target)) {
|
|
1002
|
+
visualChanged = true;
|
|
1003
|
+
if (/^(src|srcset|width|height)$/.test(record.attributeName ?? "")) this.ambient.assetChanged(target);
|
|
1004
|
+
}
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
const visual = target.matches("input[type=\"checkbox\"], input[type=\"radio\"]") ? target.closest("label")?.querySelector("[data-bx-surface]") : void 0;
|
|
1008
|
+
const entry = this.entries.get(target) ?? (visual ? this.entries.get(visual) : void 0);
|
|
314
1009
|
if (entry) changed.add(entry);
|
|
315
|
-
if (record.attributeName === "data-bx-field-invalid") for (const surface of
|
|
1010
|
+
if (record.attributeName === "data-bx-field-invalid") for (const surface of target.querySelectorAll("[data-bx-surface]")) {
|
|
316
1011
|
const child = this.entries.get(surface);
|
|
317
1012
|
if (child) changed.add(child);
|
|
318
1013
|
}
|
|
319
1014
|
}
|
|
320
|
-
if (
|
|
1015
|
+
if (visualChanged) {
|
|
1016
|
+
for (const entry of this.entries.values()) {
|
|
1017
|
+
entry.bounds = void 0;
|
|
1018
|
+
entry.lastLighting = void 0;
|
|
1019
|
+
}
|
|
1020
|
+
this.ambient.invalidate();
|
|
1021
|
+
}
|
|
1022
|
+
if (!changed.size && !visualChanged) return;
|
|
321
1023
|
for (const entry of changed) this.updateSelected(entry);
|
|
322
1024
|
this.updateEmitter();
|
|
323
1025
|
});
|
|
@@ -332,6 +1034,7 @@ var SceneRuntime = class {
|
|
|
332
1034
|
};
|
|
333
1035
|
const overlay = lightingVisibility(this.doc, this.view).boundary(element) !== this.doc.documentElement;
|
|
334
1036
|
this.entries.set(element, entry);
|
|
1037
|
+
this.ambient.add(entry);
|
|
335
1038
|
this.observer?.observe(element);
|
|
336
1039
|
this.updateSelected(entry);
|
|
337
1040
|
this.updateEmitter();
|
|
@@ -341,6 +1044,7 @@ var SceneRuntime = class {
|
|
|
341
1044
|
return () => {
|
|
342
1045
|
this.observer?.unobserve(element);
|
|
343
1046
|
this.entries.delete(element);
|
|
1047
|
+
this.ambient.remove(entry);
|
|
344
1048
|
this.queue.delete(entry);
|
|
345
1049
|
this.selected.delete(entry);
|
|
346
1050
|
if (this.pressed === entry) this.pressed = void 0;
|
|
@@ -358,7 +1062,9 @@ var SceneRuntime = class {
|
|
|
358
1062
|
if (entry) this.updateSelected(entry);
|
|
359
1063
|
this.updateEmitter();
|
|
360
1064
|
if (entry) {
|
|
1065
|
+
this.ambient.invalidate(entry);
|
|
361
1066
|
entry.bounds = void 0;
|
|
1067
|
+
entry.lastLighting = void 0;
|
|
362
1068
|
this.queue.add(entry);
|
|
363
1069
|
this.schedule();
|
|
364
1070
|
}
|
|
@@ -377,11 +1083,14 @@ var SceneRuntime = class {
|
|
|
377
1083
|
update: (next) => {
|
|
378
1084
|
if (!disposed) {
|
|
379
1085
|
entry.options = next;
|
|
380
|
-
this.refreshRoot(root);
|
|
1086
|
+
this.refreshRoot(root, next.reuseGeometry !== true);
|
|
381
1087
|
}
|
|
382
1088
|
},
|
|
383
1089
|
refresh: () => {
|
|
384
|
-
if (!disposed)
|
|
1090
|
+
if (!disposed) {
|
|
1091
|
+
this.ambient.invalidate();
|
|
1092
|
+
this.refreshRoot(root);
|
|
1093
|
+
}
|
|
385
1094
|
},
|
|
386
1095
|
destroy: () => {
|
|
387
1096
|
if (disposed) return;
|
|
@@ -392,6 +1101,11 @@ var SceneRuntime = class {
|
|
|
392
1101
|
}
|
|
393
1102
|
};
|
|
394
1103
|
}
|
|
1104
|
+
isLightingDecoration(node) {
|
|
1105
|
+
if (node.nodeType !== node.ELEMENT_NODE) return true;
|
|
1106
|
+
const element = node;
|
|
1107
|
+
return element.matches(".bx-glass-decoration, .bx-glass-rim") || !!element.closest(".bx-glass-decoration, .bx-glass-rim");
|
|
1108
|
+
}
|
|
395
1109
|
find(event) {
|
|
396
1110
|
for (const node of event.composedPath()) {
|
|
397
1111
|
const found = this.entries.get(node);
|
|
@@ -399,13 +1113,17 @@ var SceneRuntime = class {
|
|
|
399
1113
|
if (found.element.closest(disabledSelector)) return void 0;
|
|
400
1114
|
if (found.hooks.interactive()) return found;
|
|
401
1115
|
}
|
|
1116
|
+
const label = event.target.closest("label");
|
|
1117
|
+
const visual = label?.control?.matches("input[type=\"checkbox\"], input[type=\"radio\"]") ? label.querySelector("[data-bx-surface]") : void 0;
|
|
1118
|
+
const found = visual ? this.entries.get(visual) : void 0;
|
|
1119
|
+
if (found && !found.element.closest(disabledSelector) && !label?.control?.matches(disabledSelector) && found.hooks.interactive()) return found;
|
|
402
1120
|
}
|
|
403
1121
|
scope(element) {
|
|
404
|
-
return element.closest(`[data-bx-light-scope], dialog, [role="dialog"], [role="alertdialog"], [popover], .cdk-overlay-pane, .toast-container, bx-modal, main, aside, nav`) ?? this.doc.body;
|
|
1122
|
+
return lightingOrigin(element).closest(`[data-bx-light-scope], dialog, [role="dialog"], [role="alertdialog"], [popover], .cdk-overlay-pane, .toast-container, bx-modal, main, aside, nav`) ?? this.doc.body;
|
|
405
1123
|
}
|
|
406
1124
|
updateEmitter() {
|
|
407
1125
|
const next = /* @__PURE__ */ new Map();
|
|
408
|
-
const enabled = (entry) => entry.hooks.interactive() && !entry.element.closest(disabledSelector);
|
|
1126
|
+
const enabled = (entry) => entry.hooks.interactive() && !entry.element.closest(disabledSelector) && !entry.element.closest("label")?.control?.matches(disabledSelector);
|
|
409
1127
|
for (const entry of [
|
|
410
1128
|
...this.selected,
|
|
411
1129
|
this.hovered,
|
|
@@ -426,45 +1144,69 @@ var SceneRuntime = class {
|
|
|
426
1144
|
if (!entry.visible) continue;
|
|
427
1145
|
if (sources.some((source) => this.scope(source.element) === this.scope(entry.element))) {
|
|
428
1146
|
entry.bounds = void 0;
|
|
1147
|
+
entry.lastLighting = void 0;
|
|
429
1148
|
this.queue.add(entry);
|
|
430
1149
|
}
|
|
431
1150
|
}
|
|
432
1151
|
this.schedule();
|
|
433
1152
|
}
|
|
434
1153
|
updateSelected(entry) {
|
|
435
|
-
|
|
1154
|
+
const checked = (entry.element.closest("label")?.control)?.matches("input[type=\"checkbox\"]:checked, input[type=\"radio\"]:checked");
|
|
1155
|
+
if (entry.hooks.emission?.() && (entry.element.matches(activeStateSelector) || checked)) this.selected.add(entry);
|
|
436
1156
|
else this.selected.delete(entry);
|
|
437
1157
|
}
|
|
438
|
-
refreshRoot(root) {
|
|
1158
|
+
refreshRoot(root, measure = true) {
|
|
439
1159
|
for (const entry of this.entries.values()) {
|
|
440
|
-
if (!root.contains(entry.element)) continue;
|
|
441
|
-
|
|
1160
|
+
if (!root.contains(lightingOrigin(entry.element))) continue;
|
|
1161
|
+
if (measure) {
|
|
1162
|
+
entry.bounds = void 0;
|
|
1163
|
+
entry.lastLighting = void 0;
|
|
1164
|
+
}
|
|
442
1165
|
if (entry.visible) this.queue.add(entry);
|
|
443
1166
|
}
|
|
444
1167
|
this.schedule();
|
|
445
1168
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
this.connected = true;
|
|
449
|
-
this.stateObserver?.observe(this.doc.documentElement, {
|
|
1169
|
+
observeState(root) {
|
|
1170
|
+
this.stateObserver?.observe(root, {
|
|
450
1171
|
subtree: true,
|
|
451
1172
|
attributes: true,
|
|
1173
|
+
childList: true,
|
|
452
1174
|
attributeFilter: [
|
|
453
1175
|
"aria-pressed",
|
|
1176
|
+
"aria-checked",
|
|
1177
|
+
"checked",
|
|
454
1178
|
"aria-selected",
|
|
455
1179
|
"aria-expanded",
|
|
456
1180
|
"aria-current",
|
|
457
1181
|
"disabled",
|
|
458
1182
|
"aria-disabled",
|
|
459
1183
|
"aria-invalid",
|
|
460
|
-
"data-bx-field-invalid"
|
|
1184
|
+
"data-bx-field-invalid",
|
|
1185
|
+
"class",
|
|
1186
|
+
"style",
|
|
1187
|
+
"hidden",
|
|
1188
|
+
"role",
|
|
1189
|
+
"data-bx-light-scope",
|
|
1190
|
+
"data-bx-surface",
|
|
1191
|
+
"data-bx-reduced-transparency",
|
|
1192
|
+
"data-bx-reduced-motion",
|
|
1193
|
+
"src",
|
|
1194
|
+
"srcset",
|
|
1195
|
+
"width",
|
|
1196
|
+
"height"
|
|
461
1197
|
]
|
|
462
1198
|
});
|
|
1199
|
+
}
|
|
1200
|
+
connect() {
|
|
1201
|
+
if (this.connected) return;
|
|
1202
|
+
this.connected = true;
|
|
1203
|
+
this.observeState(this.doc.documentElement);
|
|
463
1204
|
this.doc.addEventListener("pointerdown", this.down, { passive: true });
|
|
464
1205
|
this.doc.addEventListener("pointerover", this.over, { passive: true });
|
|
465
1206
|
this.doc.addEventListener("pointerout", this.out, { passive: true });
|
|
466
1207
|
this.doc.addEventListener("focusin", this.focus);
|
|
467
1208
|
this.doc.addEventListener("focusout", this.unfocus);
|
|
1209
|
+
this.doc.addEventListener("change", this.change);
|
|
468
1210
|
this.view.addEventListener("pointerup", this.up, { passive: true });
|
|
469
1211
|
this.view.addEventListener("pointercancel", this.up, { passive: true });
|
|
470
1212
|
this.doc.addEventListener("pointerleave", this.leave, { passive: true });
|
|
@@ -474,12 +1216,19 @@ var SceneRuntime = class {
|
|
|
474
1216
|
passive: true,
|
|
475
1217
|
capture: true
|
|
476
1218
|
});
|
|
1219
|
+
this.doc.addEventListener("load", this.assetLoad, true);
|
|
1220
|
+
this.doc.addEventListener("loadeddata", this.assetLoad, true);
|
|
1221
|
+
this.doc.addEventListener("play", this.assetLoad, true);
|
|
1222
|
+
this.doc.addEventListener("pause", this.assetLoad, true);
|
|
1223
|
+
this.doc.addEventListener("seeked", this.assetLoad, true);
|
|
1224
|
+
this.doc.addEventListener("visibilitychange", this.visibilityChange);
|
|
477
1225
|
this.doc.addEventListener(configurationEvent, this.sync);
|
|
478
1226
|
this.motion.addEventListener("change", this.sync);
|
|
479
1227
|
this.transparency.addEventListener("change", this.sync);
|
|
480
1228
|
}
|
|
481
1229
|
disconnectIfEmpty() {
|
|
482
1230
|
if (this.entries.size || this.scenes.size) return;
|
|
1231
|
+
this.ambient.destroy();
|
|
483
1232
|
this.stopFrame?.();
|
|
484
1233
|
this.stopFrame = void 0;
|
|
485
1234
|
this.queue.clear();
|
|
@@ -492,12 +1241,19 @@ var SceneRuntime = class {
|
|
|
492
1241
|
this.doc.removeEventListener("pointerout", this.out);
|
|
493
1242
|
this.doc.removeEventListener("focusin", this.focus);
|
|
494
1243
|
this.doc.removeEventListener("focusout", this.unfocus);
|
|
1244
|
+
this.doc.removeEventListener("change", this.change);
|
|
495
1245
|
this.view.removeEventListener("pointerup", this.up);
|
|
496
1246
|
this.view.removeEventListener("pointercancel", this.up);
|
|
497
1247
|
this.doc.removeEventListener("pointerleave", this.leave);
|
|
498
1248
|
this.view.removeEventListener("blur", this.blur);
|
|
499
1249
|
this.view.removeEventListener("resize", this.refresh);
|
|
500
1250
|
this.doc.removeEventListener("scroll", this.refresh, true);
|
|
1251
|
+
this.doc.removeEventListener("load", this.assetLoad, true);
|
|
1252
|
+
this.doc.removeEventListener("loadeddata", this.assetLoad, true);
|
|
1253
|
+
this.doc.removeEventListener("play", this.assetLoad, true);
|
|
1254
|
+
this.doc.removeEventListener("pause", this.assetLoad, true);
|
|
1255
|
+
this.doc.removeEventListener("seeked", this.assetLoad, true);
|
|
1256
|
+
this.doc.removeEventListener("visibilitychange", this.visibilityChange);
|
|
501
1257
|
this.doc.removeEventListener(configurationEvent, this.sync);
|
|
502
1258
|
this.motion.removeEventListener("change", this.sync);
|
|
503
1259
|
this.transparency.removeEventListener("change", this.sync);
|
|
@@ -529,26 +1285,46 @@ var SceneRuntime = class {
|
|
|
529
1285
|
const sceneData = /* @__PURE__ */ new Map();
|
|
530
1286
|
const projections = batch.filter((entry) => entry.visible && entry.element.isConnected).map((entry) => {
|
|
531
1287
|
entry.bounds ??= entry.element.getBoundingClientRect();
|
|
1288
|
+
const origin = lightingOrigin(entry.element);
|
|
532
1289
|
let nearest;
|
|
533
|
-
for (const candidate of this.scenes) if (candidate.root.contains(
|
|
534
|
-
if (nearest && !sceneData.has(nearest))
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
1290
|
+
for (const candidate of this.scenes) if (candidate.root.contains(origin) && (!nearest || nearest.root.contains(candidate.root))) nearest = candidate;
|
|
1291
|
+
if (nearest && !sceneData.has(nearest)) {
|
|
1292
|
+
const scene = nearest;
|
|
1293
|
+
sceneData.set(scene, {
|
|
1294
|
+
lights: (scene.options.lights ?? []).slice(0, 4).map((light, index) => this.lightPoint(light, `${scene.id}:${index}`)),
|
|
1295
|
+
reflections: (scene.options.reflectors ?? []).slice(0, 4).map((reflection) => this.reflectionPoint(reflection))
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
538
1298
|
const data = nearest ? sceneData.get(nearest) : void 0;
|
|
1299
|
+
const sceneRoot = nearest?.root ?? entry.element;
|
|
539
1300
|
const boundary = visibility.boundary(entry.element);
|
|
540
|
-
const lights = (data?.lights ?? []).filter((light) => visibility.boundary(light.source ??
|
|
541
|
-
for (const { emitter, light } of activeLights) if (this.scope(emitter.element) === this.scope(entry.element) && visibility.layer(emitter.element) === visibility.layer(
|
|
1301
|
+
const lights = (data?.lights ?? []).filter((light) => visibility.boundary(light.source ?? sceneRoot) === boundary);
|
|
1302
|
+
for (const { emitter, light } of activeLights) if (this.scope(emitter.element) === this.scope(entry.element) && visibility.layer(lightingOrigin(emitter.element)) === visibility.layer(origin) && !emitter.element.contains(entry.element) && (!entry.element.contains(emitter.element) || entry.element.matches("[role=\"menu\"]"))) lights.push(light);
|
|
1303
|
+
lights.push(...this.ambient.lights(entry));
|
|
542
1304
|
const reflections = (data?.reflections ?? []).filter((reflection) => visibility.boundary(reflection.source) === boundary);
|
|
543
1305
|
return [entry, this.project(entry, lights, reflections, visibility)];
|
|
544
1306
|
});
|
|
545
1307
|
for (const entry of batch) this.queue.delete(entry);
|
|
546
1308
|
for (const { emitter, color, exposed } of emissions) emitter.hooks.emit?.(exposed, color);
|
|
547
|
-
for (const [entry, lighting] of projections)
|
|
1309
|
+
for (const [entry, lighting] of projections) {
|
|
1310
|
+
if (this.sameLighting(entry.lastLighting, lighting)) continue;
|
|
1311
|
+
entry.lastLighting = lighting;
|
|
1312
|
+
const before = entry.element.getAttribute("style");
|
|
1313
|
+
entry.hooks.light(lighting);
|
|
1314
|
+
const after = entry.element.getAttribute("style");
|
|
1315
|
+
if (before !== after) this.renderedStyles.set(entry.element, after);
|
|
1316
|
+
}
|
|
548
1317
|
if (!this.queue.size) this.stopFrame = void 0;
|
|
549
1318
|
return this.queue.size > 0;
|
|
550
1319
|
});
|
|
551
1320
|
}
|
|
1321
|
+
sameLighting(previous, next) {
|
|
1322
|
+
if (!previous || previous.x !== next.x || previous.y !== next.y || previous.color !== next.color || previous.strength !== next.strength || previous.rim !== next.rim || previous.reflection !== next.reflection || previous.reflectionStrength !== next.reflectionStrength || previous.reflectionX !== next.reflectionX || previous.reflectionY !== next.reflectionY || previous.contributions.length !== next.contributions.length) return false;
|
|
1323
|
+
return previous.contributions.every((light, index) => {
|
|
1324
|
+
const other = next.contributions[index];
|
|
1325
|
+
return light.key === other.key && light.x === other.x && light.y === other.y && light.color === other.color && light.strength === other.strength && light.edgeStrength === other.edgeStrength && light.ambient === other.ambient && light.spread === other.spread;
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
552
1328
|
lightPoint(light, key, border = false) {
|
|
553
1329
|
const rect = light.element?.getBoundingClientRect();
|
|
554
1330
|
if (light.element && !border) {
|
|
@@ -582,7 +1358,7 @@ var SceneRuntime = class {
|
|
|
582
1358
|
};
|
|
583
1359
|
}
|
|
584
1360
|
project(entry, lights, reflectors, visibility) {
|
|
585
|
-
const b = entry.bounds;
|
|
1361
|
+
const b = entry.bounds ??= entry.element.getBoundingClientRect();
|
|
586
1362
|
const cx = b.left + b.width / 2, cy = b.top + b.height / 2;
|
|
587
1363
|
let x = 25, y = 0, strength = .04, color = "white";
|
|
588
1364
|
let strongest = 0, weightedX = 0, weightedY = 0, totalWeight = 0;
|
|
@@ -600,26 +1376,30 @@ var SceneRuntime = class {
|
|
|
600
1376
|
const edgeDistance = Math.hypot(Math.max(b.left - lx, 0, lx - b.right), Math.max(b.top - ly, 0, ly - b.bottom), light.z);
|
|
601
1377
|
const edgeWeight = Math.min(.85, light.intensity * Math.pow(Math.max(0, 1 - edgeDistance / light.radius), 2));
|
|
602
1378
|
if (edgeWeight <= .01 && weight <= .001) continue;
|
|
603
|
-
if (!visibility.exposed(entry.hooks.hitTarget ?? entry.element, lx, ly, b)) continue;
|
|
604
|
-
if (light.source) {
|
|
1379
|
+
if (!light.ambient && !visibility.exposed(entry.hooks.hitTarget ?? entry.element, lx, ly, b)) continue;
|
|
1380
|
+
if (!light.ambient && light.source) {
|
|
605
1381
|
const source = light.source.closest("[data-bx-surface]") ?? light.source;
|
|
606
1382
|
if (!visibility.exposed(source, lx, ly, source.getBoundingClientRect())) continue;
|
|
607
1383
|
}
|
|
608
|
-
|
|
609
|
-
weightedY += localY * weight;
|
|
610
|
-
if (edgeWeight > .01) rims.push(`radial-gradient(ellipse farthest-side at ${localX.toFixed(2)}% ${localY.toFixed(2)}%, color-mix(in srgb, ${light.color} ${(edgeWeight * 100).toFixed(2)}%, transparent), transparent 75%)`);
|
|
1384
|
+
if (edgeWeight > .01) rims.push(`radial-gradient(${light.spread ? `circle ${light.spread}px` : "ellipse farthest-side"} at ${localX.toFixed(2)}% ${localY.toFixed(2)}%, color-mix(in srgb, ${light.color} ${(edgeWeight * 100).toFixed(2)}%, transparent), transparent 75%)`);
|
|
611
1385
|
if (edgeWeight > .01 || weight > .001) contributions.push({
|
|
612
1386
|
key: light.key,
|
|
613
1387
|
x: localX,
|
|
614
1388
|
y: localY,
|
|
615
1389
|
color: light.color,
|
|
616
|
-
strength: Math.min(.25, weight * .4),
|
|
617
|
-
edgeStrength: edgeWeight
|
|
1390
|
+
strength: light.ambient ? 0 : Math.min(.25, weight * .4),
|
|
1391
|
+
edgeStrength: edgeWeight,
|
|
1392
|
+
ambient: light.ambient,
|
|
1393
|
+
spread: light.spread
|
|
618
1394
|
});
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
1395
|
+
if (!light.ambient) {
|
|
1396
|
+
weightedX += localX * weight;
|
|
1397
|
+
weightedY += localY * weight;
|
|
1398
|
+
totalWeight += weight;
|
|
1399
|
+
if (weight > strongest) {
|
|
1400
|
+
strongest = weight;
|
|
1401
|
+
color = light.color;
|
|
1402
|
+
}
|
|
623
1403
|
}
|
|
624
1404
|
}
|
|
625
1405
|
if (totalWeight > .001) {
|
|
@@ -726,8 +1506,9 @@ var Spring = class {
|
|
|
726
1506
|
};
|
|
727
1507
|
const mounted = /* @__PURE__ */ new WeakMap();
|
|
728
1508
|
function isActionSurface(element) {
|
|
729
|
-
if (element.matches("button, bx-button, [bx-button], a[href], summary, input:not([type=\"hidden\"]), select, textarea, [role=\"button\"], [role=\"link\"], [role=\"checkbox\"], [role=\"radio\"], [role=\"switch\"], [role=\"tab\"], [role=\"menuitem\"], [role=\"combobox\"], [onclick]") || typeof element.onclick === "function") return true;
|
|
1509
|
+
if (element.matches("button, bx-button, [bx-button], a[href], summary, input:not([type=\"hidden\"]), select, textarea, [role=\"button\"], [role=\"link\"], [role=\"checkbox\"], [role=\"radio\"], [role=\"switch\"], [role=\"tab\"], [role=\"menuitem\"], [role=\"combobox\"], [role=\"textbox\"], [role=\"searchbox\"], [role=\"slider\"], [role=\"spinbutton\"], [onclick]") || typeof element.onclick === "function") return true;
|
|
730
1510
|
if (element.closest("label")?.control) return true;
|
|
1511
|
+
if (!element.closest("bx-form-field")) return false;
|
|
731
1512
|
const controls = element.querySelectorAll("input:not([type=\"hidden\"]), select, textarea");
|
|
732
1513
|
return controls.length === 1 && controls[0].closest("[data-bx-surface]") === element;
|
|
733
1514
|
}
|
|
@@ -738,8 +1519,12 @@ function mountSurface(element, initial = {}) {
|
|
|
738
1519
|
if (!view) throw new Error("Glass surfaces must be mounted in a browser document.");
|
|
739
1520
|
const ownProperties = [
|
|
740
1521
|
"position",
|
|
741
|
-
"--bx-
|
|
742
|
-
"--bx-glass-
|
|
1522
|
+
"--bx-glassiness",
|
|
1523
|
+
"--bx-glass-lit-edge",
|
|
1524
|
+
"--bx-glass-lit-edge-top",
|
|
1525
|
+
"--bx-glass-lit-edge-right",
|
|
1526
|
+
"--bx-glass-lit-edge-bottom",
|
|
1527
|
+
"--bx-glass-lit-edge-left"
|
|
743
1528
|
];
|
|
744
1529
|
const restoreStyles = rememberStyles(element, ownProperties);
|
|
745
1530
|
const attributes = [
|
|
@@ -763,6 +1548,14 @@ function mountSurface(element, initial = {}) {
|
|
|
763
1548
|
rim.className = "bx-glass-rim";
|
|
764
1549
|
rim.setAttribute("aria-hidden", "true");
|
|
765
1550
|
element.append(rim);
|
|
1551
|
+
const environment = [doc.createElement("span"), doc.createElement("span")];
|
|
1552
|
+
for (const node of environment) {
|
|
1553
|
+
node.className = "bx-glass-contribution bx-glass-environment";
|
|
1554
|
+
node.style.opacity = "0";
|
|
1555
|
+
rim.append(node);
|
|
1556
|
+
}
|
|
1557
|
+
let environmentImage = "none";
|
|
1558
|
+
let environmentIndex = 0;
|
|
766
1559
|
const ambient = doc.createElement("span");
|
|
767
1560
|
ambient.className = "bx-glass-ambient";
|
|
768
1561
|
decoration.append(light);
|
|
@@ -774,6 +1567,7 @@ function mountSurface(element, initial = {}) {
|
|
|
774
1567
|
let disposed = false;
|
|
775
1568
|
let unsubscribe;
|
|
776
1569
|
let reducedMotion = false;
|
|
1570
|
+
let unifiedLighting = false;
|
|
777
1571
|
const interactive = () => options.interactive ?? isActionSurface(element);
|
|
778
1572
|
let emitting = false;
|
|
779
1573
|
let emittingColor;
|
|
@@ -896,8 +1690,36 @@ function mountSurface(element, initial = {}) {
|
|
|
896
1690
|
x.snap(value.x);
|
|
897
1691
|
y.snap(value.y);
|
|
898
1692
|
strength.target = value.strength;
|
|
899
|
-
|
|
900
|
-
|
|
1693
|
+
if (unifiedLighting) {
|
|
1694
|
+
const sources = [...value.contributions].sort((a, b) => a.edgeStrength - b.edgeStrength).slice(-4);
|
|
1695
|
+
const edgeAt = (x, y) => {
|
|
1696
|
+
let edge = "var(--bx-border-default, GrayText)";
|
|
1697
|
+
for (const source of sources) {
|
|
1698
|
+
const distance = Math.hypot((x - source.x) / Math.max(1, source.x, 100 - source.x), (y - source.y) / Math.max(1, source.y, 100 - source.y));
|
|
1699
|
+
const strength = finiteClamp(source.edgeStrength, 0, .85, 0) * .35 * Math.max(0, 1 - distance / .75);
|
|
1700
|
+
if (strength > .001) edge = `color-mix(in srgb, ${source.color} ${(strength * 100).toFixed(2)}%, ${edge})`;
|
|
1701
|
+
}
|
|
1702
|
+
return edge;
|
|
1703
|
+
};
|
|
1704
|
+
element.style.setProperty("--bx-glass-lit-edge", edgeAt(50, 50));
|
|
1705
|
+
element.style.setProperty("--bx-glass-lit-edge-top", edgeAt(50, 0));
|
|
1706
|
+
element.style.setProperty("--bx-glass-lit-edge-right", edgeAt(100, 50));
|
|
1707
|
+
element.style.setProperty("--bx-glass-lit-edge-bottom", edgeAt(50, 100));
|
|
1708
|
+
element.style.setProperty("--bx-glass-lit-edge-left", edgeAt(0, 50));
|
|
1709
|
+
}
|
|
1710
|
+
const ambientImage = value.contributions.filter((contribution) => contribution.ambient).map((contribution) => `radial-gradient(circle ${contribution.spread}px at ${contribution.x.toFixed(2)}% ${contribution.y.toFixed(2)}%, color-mix(in srgb, ${contribution.color} ${(contribution.edgeStrength * 100).toFixed(2)}%, transparent), transparent 75%)`).join(", ") || "none";
|
|
1711
|
+
if (ambientImage !== environmentImage) {
|
|
1712
|
+
environmentImage = ambientImage;
|
|
1713
|
+
const previous = environment[environmentIndex];
|
|
1714
|
+
environmentIndex = 1 - environmentIndex;
|
|
1715
|
+
const next = environment[environmentIndex];
|
|
1716
|
+
next.style.backgroundImage = ambientImage;
|
|
1717
|
+
next.style.opacity = "1";
|
|
1718
|
+
previous.style.opacity = "0";
|
|
1719
|
+
}
|
|
1720
|
+
const contributions = value.contributions.filter((contribution) => !contribution.ambient);
|
|
1721
|
+
const active = new Set(contributions.map((contribution) => contribution.key));
|
|
1722
|
+
for (const contribution of contributions) setLayer(contribution);
|
|
901
1723
|
for (const [key, layer] of layers) if (key !== "emitter" && !active.has(key) && !layer.leaving) fadeLayer(key, layer, false);
|
|
902
1724
|
decoration.style.setProperty("--bx-glass-light-color", value.color);
|
|
903
1725
|
decoration.style.setProperty("--bx-glass-reflection-color", value.reflection);
|
|
@@ -907,9 +1729,7 @@ function mountSurface(element, initial = {}) {
|
|
|
907
1729
|
animate();
|
|
908
1730
|
};
|
|
909
1731
|
const sync = () => {
|
|
910
|
-
|
|
911
|
-
const material = options.material ?? (inheritedMaterial === "frosted" || inheritedMaterial === "solid" ? inheritedMaterial : "glass");
|
|
912
|
-
element.setAttribute("data-bx-surface", material);
|
|
1732
|
+
element.setAttribute("data-bx-surface", "glass");
|
|
913
1733
|
const inheritedMotion = globalValue("data-bx-reduced-motion") ?? originalAttributes.get("data-bx-reduced-motion");
|
|
914
1734
|
const inheritedTransparency = globalValue("data-bx-reduced-transparency") ?? originalAttributes.get("data-bx-reduced-transparency");
|
|
915
1735
|
reducedMotion = options.reducedMotion ?? (inheritedMotion === null || inheritedMotion === void 0 ? motion.matches : inheritedMotion === "true");
|
|
@@ -921,11 +1741,10 @@ function mountSurface(element, initial = {}) {
|
|
|
921
1741
|
const economical = view.navigator.connection?.saveData || view.navigator.hardwareConcurrency > 0 && view.navigator.hardwareConcurrency <= 4 || view.matchMedia("(pointer: coarse)").matches;
|
|
922
1742
|
const enhanced = quality !== "baseline" && supported && (quality === "enhanced" || !economical);
|
|
923
1743
|
element.setAttribute("data-bx-glass-renderer", enhanced ? "enhanced" : "baseline");
|
|
924
|
-
if (options.
|
|
925
|
-
else restoreProperty("--bx-
|
|
926
|
-
if (options.tint !== void 0) element.style.setProperty("--bx-glass-opacity", String(finiteClamp(options.tint, 0, 1, .4)));
|
|
927
|
-
else restoreProperty("--bx-glass-opacity");
|
|
1744
|
+
if (options.glassiness !== void 0) element.style.setProperty("--bx-glassiness", String(finiteClamp(options.glassiness, 0, 1, .5)));
|
|
1745
|
+
else restoreProperty("--bx-glassiness");
|
|
928
1746
|
const style = view.getComputedStyle(element);
|
|
1747
|
+
unifiedLighting = style.getPropertyValue("--bx-glass-unified-lighting").trim() === "1";
|
|
929
1748
|
for (const side of [
|
|
930
1749
|
"top",
|
|
931
1750
|
"right",
|
|
@@ -1003,10 +1822,10 @@ function createGlassTransition(origin, target, options = {}) {
|
|
|
1003
1822
|
const view = doc.defaultView;
|
|
1004
1823
|
if (!view) throw new Error("Glass transitions must be mounted in a browser document.");
|
|
1005
1824
|
if (origin.ownerDocument !== doc) throw new Error("Glass transition endpoints must share a document.");
|
|
1006
|
-
const restoreStyles = rememberStyles(target, ["opacity", "visibility"]);
|
|
1007
1825
|
const skin = target.matches("[data-bx-surface]") ? target : target.querySelector("[data-bx-surface]") ?? target;
|
|
1008
|
-
const
|
|
1009
|
-
|
|
1826
|
+
const restoreOpacity = rememberStyles(skin, ["opacity"]);
|
|
1827
|
+
const restoreVisibility = rememberStyles(target, ["visibility"]);
|
|
1828
|
+
const opacity = Number(view.getComputedStyle(skin).opacity);
|
|
1010
1829
|
const preference = view.matchMedia("(prefers-reduced-motion: reduce)");
|
|
1011
1830
|
let disposed = false;
|
|
1012
1831
|
let active = false;
|
|
@@ -1018,7 +1837,7 @@ function createGlassTransition(origin, target, options = {}) {
|
|
|
1018
1837
|
return options.reducedMotion ?? (inherited === null ? preference.matches : inherited === "true");
|
|
1019
1838
|
};
|
|
1020
1839
|
const paint = () => {
|
|
1021
|
-
|
|
1840
|
+
skin.style.opacity = String(value * opacity);
|
|
1022
1841
|
options.onUpdate?.();
|
|
1023
1842
|
};
|
|
1024
1843
|
function settle() {
|
|
@@ -1026,7 +1845,8 @@ function createGlassTransition(origin, target, options = {}) {
|
|
|
1026
1845
|
value = destination;
|
|
1027
1846
|
unsubscribe?.();
|
|
1028
1847
|
unsubscribe = void 0;
|
|
1029
|
-
|
|
1848
|
+
restoreOpacity();
|
|
1849
|
+
restoreVisibility();
|
|
1030
1850
|
if (destination === 0) target.style.visibility = "hidden";
|
|
1031
1851
|
const resolve = resolvePending;
|
|
1032
1852
|
resolvePending = void 0;
|
|
@@ -1041,7 +1861,7 @@ function createGlassTransition(origin, target, options = {}) {
|
|
|
1041
1861
|
destination = open ? 1 : 0;
|
|
1042
1862
|
elapsed = 0;
|
|
1043
1863
|
active = true;
|
|
1044
|
-
|
|
1864
|
+
restoreVisibility();
|
|
1045
1865
|
const finished = new Promise((resolve) => {
|
|
1046
1866
|
resolvePending = resolve;
|
|
1047
1867
|
});
|
|
@@ -1082,12 +1902,12 @@ function createGlassTransition(origin, target, options = {}) {
|
|
|
1082
1902
|
resolvePending = void 0;
|
|
1083
1903
|
preference.removeEventListener("change", onPreference);
|
|
1084
1904
|
doc.removeEventListener(configurationEvent, onPreference);
|
|
1085
|
-
|
|
1086
|
-
|
|
1905
|
+
restoreOpacity();
|
|
1906
|
+
restoreVisibility();
|
|
1087
1907
|
}
|
|
1088
1908
|
};
|
|
1089
1909
|
}
|
|
1090
1910
|
const createLiquidTransition = createGlassTransition;
|
|
1091
|
-
export { Spring, configureAppearance, createGlassTransition, createLiquidTransition, mountGlassScene, mountSurface, observeGlassLighting, refreshGlassLighting };
|
|
1911
|
+
export { Spring, configureAppearance, createGlassTransition, createLiquidTransition, inheritGlassLighting, mountGlassScene, mountSurface, observeGlassLighting, refreshGlassLighting };
|
|
1092
1912
|
|
|
1093
1913
|
//# sourceMappingURL=filip.mazev-blocks-effects-runtime.mjs.map
|