@noxlovette/material 0.8.1 → 0.8.3
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.
|
@@ -30,6 +30,7 @@ import { springTokens, springTransition } from './spring.js';
|
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
32
|
const FILL = '--md-container-transform-color';
|
|
33
|
+
const SHADOW = '--md-container-transform-shadow';
|
|
33
34
|
const resolve = (target) => typeof target === 'string'
|
|
34
35
|
? document.querySelector(target)
|
|
35
36
|
: target instanceof Element
|
|
@@ -46,16 +47,33 @@ const fillOf = (target) => {
|
|
|
46
47
|
const colour = target ? getComputedStyle(target).backgroundColor : '';
|
|
47
48
|
return colour && !TRANSPARENT.has(colour) ? colour : 'transparent';
|
|
48
49
|
};
|
|
50
|
+
/*
|
|
51
|
+
The container's elevation while it morphs. Motion clips the group (`overflow: clip`) whenever
|
|
52
|
+
the aspect ratio changes, which cuts the shadow out of both snapshots, so an elevated
|
|
53
|
+
destination (a floating pane) would only get its shadow when the live DOM takes over at the
|
|
54
|
+
end. The group's own `box-shadow` isn't clipped by its overflow, so it carries the shadow
|
|
55
|
+
instead: the destination's when it has one (opening into an elevated pane), otherwise the
|
|
56
|
+
source's (closing it back into a flat card), so the container stays elevated the whole way.
|
|
57
|
+
*/
|
|
58
|
+
const shadowOf = (target) => {
|
|
59
|
+
const shadow = target ? getComputedStyle(target).boxShadow : '';
|
|
60
|
+
return shadow && shadow !== 'none' ? shadow : undefined;
|
|
61
|
+
};
|
|
49
62
|
export const containerTransform = (update, { from, to, spring = springTokens.spatial }) => {
|
|
50
63
|
// The incoming snapshot is opaque wherever the destination is, and the outgoing one fades off
|
|
51
64
|
// it (motion.css layers them), so nothing behind shows through. The fill (motion.css) covers
|
|
52
65
|
// the rest of a destination with its own background, e.g. a full-screen view's area below the
|
|
53
|
-
// incoming snapshot's top as the bar grows
|
|
54
|
-
//
|
|
66
|
+
// incoming snapshot's top as the bar grows, and the shadow keeps its elevation (`shadowOf`).
|
|
67
|
+
// Only the transition's group reads the properties, and they're rewritten before each new
|
|
68
|
+
// snapshot, so they're left in place afterwards.
|
|
55
69
|
const root = document.documentElement;
|
|
56
70
|
const updateAndFill = async () => {
|
|
71
|
+
// Read before `update`, which may unmount it.
|
|
72
|
+
const fromShadow = shadowOf(resolve(from));
|
|
57
73
|
await update();
|
|
58
|
-
|
|
74
|
+
const target = resolve(to);
|
|
75
|
+
root.style.setProperty(FILL, fillOf(target));
|
|
76
|
+
root.style.setProperty(SHADOW, shadowOf(target) ?? fromShadow ?? 'none');
|
|
59
77
|
};
|
|
60
78
|
// The class lets motion.css keep both snapshots at their width, clipped by the container.
|
|
61
79
|
const builder = animateView(updateAndFill, springTransition(spring))
|
|
@@ -15,8 +15,24 @@ const CONTAINER_TOKENS = [
|
|
|
15
15
|
];
|
|
16
16
|
/** Used server-side, or if a token hasn't been painted yet (styles not loaded). */
|
|
17
17
|
const FALLBACK_PALETTE = ['e9ddff', 'e8def8', 'ffd9e3', 'ffdad6', 'e6e0e9'];
|
|
18
|
-
/**
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Converts a token's resolved value to a 6-digit hex. Dev serves the tokens as written in the
|
|
20
|
+
* theme CSS, `rgb(r g b)` (or `rgb(r, g, b)`), but a production build's CSS minifier rewrites
|
|
21
|
+
* them to hex (`#d1e5f3`, or the short `#fff`): pulling the numbers out of a hex string reads
|
|
22
|
+
* its digit runs as channels and yields a near-black colour, so hex is handled on its own.
|
|
23
|
+
*/
|
|
24
|
+
function colorToHex(value) {
|
|
25
|
+
const hex = value.match(/^#([0-9a-f]{3,8})$/i)?.[1];
|
|
26
|
+
if (hex) {
|
|
27
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
28
|
+
return [...hex.slice(0, 3)]
|
|
29
|
+
.map((c) => c + c)
|
|
30
|
+
.join('')
|
|
31
|
+
.toLowerCase();
|
|
32
|
+
}
|
|
33
|
+
// 6 digits, or 8 with alpha (dropped: DiceBear takes opaque backgrounds).
|
|
34
|
+
return hex.length === 6 || hex.length === 8 ? hex.slice(0, 6).toLowerCase() : undefined;
|
|
35
|
+
}
|
|
20
36
|
const channels = value.match(/\d+(?:\.\d+)?/g);
|
|
21
37
|
if (!channels || channels.length < 3)
|
|
22
38
|
return undefined;
|
|
@@ -38,7 +54,7 @@ export function getMaterialAvatarPalette() {
|
|
|
38
54
|
const styles = getComputedStyle(document.documentElement);
|
|
39
55
|
const palette = CONTAINER_TOKENS.map((token) => {
|
|
40
56
|
const raw = styles.getPropertyValue(token).trim();
|
|
41
|
-
return raw ?
|
|
57
|
+
return raw ? colorToHex(raw) : undefined;
|
|
42
58
|
}).filter((hex) => Boolean(hex));
|
|
43
59
|
return palette.length > 0 ? palette : FALLBACK_PALETTE;
|
|
44
60
|
}
|
package/dist/styles/motion.css
CHANGED
|
@@ -362,7 +362,12 @@
|
|
|
362
362
|
animation-name: none !important;
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
-
/*
|
|
365
|
+
/*
|
|
366
|
+
Filled with the destination's own background while it morphs, and elevated with its shadow
|
|
367
|
+
(or the source's), which Motion's crop clip would otherwise cut out of the snapshots. Both set
|
|
368
|
+
by containerTransform.
|
|
369
|
+
*/
|
|
366
370
|
::view-transition-group(.md-container-transform) {
|
|
367
371
|
background-color: var(--md-container-transform-color, transparent);
|
|
372
|
+
box-shadow: var(--md-container-transform-shadow, none);
|
|
368
373
|
}
|