@phreshos/react-ui 0.1.4 → 0.1.5
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 +18 -8
- package/dist/surface-material.d.ts +6 -1
- package/dist/surface-material.js +39 -13
- package/dist/surface.d.ts +25 -1
- package/dist/surface.js +69 -17
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -83,9 +83,10 @@ Explicit values such as `4rem` are used directly — passing them through a
|
|
|
83
83
|
Theme hook would perform no additional work.
|
|
84
84
|
|
|
85
85
|
`Surface` keeps a native `<div>` as its public container and gives every
|
|
86
|
-
instance one locally owned pure-SVG material
|
|
87
|
-
64×64 micro-pattern derived
|
|
88
|
-
WebGL context, or shared
|
|
86
|
+
instance one locally owned pure-SVG material plus only the backdrop layers its
|
|
87
|
+
settings enable. The material uses a deterministic 64×64 micro-pattern derived
|
|
88
|
+
from the former shader grain; it creates no canvas, WebGL context, or shared
|
|
89
|
+
texture:
|
|
89
90
|
|
|
90
91
|
```tsx
|
|
91
92
|
<Surface className="grid rounded-xl shadow-lg">
|
|
@@ -94,16 +95,25 @@ WebGL context, or shared texture:
|
|
|
94
95
|
|
|
95
96
|
<Surface color="strong" grain="large">...</Surface>
|
|
96
97
|
<Surface color="#101114" grain={0.2} animation={8} backdrop={4} opacity={0.9}>...</Surface>
|
|
98
|
+
<Surface distortion={70} waves={8} ripples={4} saturation={1.4} brightness={1.04}>...</Surface>
|
|
97
99
|
```
|
|
98
100
|
|
|
99
101
|
`color` resolves from `Theme.background`; its semantic levels derive from that
|
|
100
102
|
same source and a direct color remains an explicit local override. `grain`,
|
|
101
|
-
`animation`, `backdrop`,
|
|
103
|
+
`grainAmount`, `animation`, `backdrop`, `opacity`, `distortion`, `waves`,
|
|
104
|
+
`ripples`, `saturation`, and `brightness` resolve from `Theme.surface`, accept
|
|
102
105
|
their semantic levels or direct values, and remain bounded by Core's Theme
|
|
103
|
-
limits. Radius and foreground remain ordinary Theme styles.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
limits. Radius and foreground remain ordinary Theme styles. Grain intensity
|
|
107
|
+
controls tonal distance while grain amount controls retained cell density.
|
|
108
|
+
When either is zero, Surface creates no grain pattern or paths. Refraction and
|
|
109
|
+
native frost use independent backdrop layers so blur does not soften the
|
|
110
|
+
displaced image. Enabled organic, wave, and ripple fields are combined
|
|
111
|
+
mathematically before one displacement pass; each zero-valued field is absent,
|
|
112
|
+
and the filter and refraction layer are absent when all three are zero.
|
|
113
|
+
Backdrop blur emits no CSS function at zero. Neutral saturation and brightness
|
|
114
|
+
at one are also omitted. Animation defaults to zero; only a Surface with
|
|
115
|
+
visible grain and a positive rate joins the internal document clock, while
|
|
116
|
+
every texture and seed remains local to its own Surface.
|
|
107
117
|
|
|
108
118
|
The Theme stores unrestricted CSS background, foreground, and accent sources.
|
|
109
119
|
Core derives the fixed `subtle`, `soft`, `base`, `strong`, and `intense`
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
interface SurfaceMaterialProps {
|
|
2
2
|
readonly animation: number;
|
|
3
3
|
readonly color: string;
|
|
4
|
+
readonly distortion: number;
|
|
4
5
|
readonly grain: number;
|
|
6
|
+
readonly grainAmount: number;
|
|
7
|
+
readonly identity: string;
|
|
5
8
|
readonly opacity: number;
|
|
9
|
+
readonly ripples: number;
|
|
10
|
+
readonly waves: number;
|
|
6
11
|
}
|
|
7
12
|
/** The locally owned SVG paint layer inside one Surface. */
|
|
8
|
-
export declare function SurfaceMaterial({ animation, color, grain, opacity }: SurfaceMaterialProps): import("react").JSX.Element;
|
|
13
|
+
export declare function SurfaceMaterial({ animation, color, distortion, grain, grainAmount, identity, opacity, ripples, waves }: SurfaceMaterialProps): import("react").JSX.Element | null;
|
|
9
14
|
export {};
|
package/dist/surface-material.js
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { Fragment, useEffect, useMemo, useRef } from "react";
|
|
3
3
|
/** The locally owned SVG paint layer inside one Surface. */
|
|
4
|
-
export function SurfaceMaterial({ animation, color, grain, opacity }) {
|
|
5
|
-
const identity = `phresh-surface-${useId().replaceAll(":", "")}`;
|
|
4
|
+
export function SurfaceMaterial({ animation, color, distortion, grain, grainAmount, identity, opacity, ripples, waves }) {
|
|
6
5
|
const seed = useMemo(() => seedFrom(identity), [identity]);
|
|
7
|
-
const
|
|
6
|
+
const hasPaint = opacity > 0;
|
|
7
|
+
const hasGrain = hasPaint && grain > 0 && grainAmount > 0;
|
|
8
|
+
const hasDistortion = distortion > 0 || waves > 0 || ripples > 0;
|
|
9
|
+
const initial = useMemo(() => hasGrain ? grainPaths(seed, 0, grainAmount) : [], [grainAmount, hasGrain, seed]);
|
|
8
10
|
const svg = useRef(null);
|
|
9
11
|
const paths = useRef([]);
|
|
10
12
|
useEffect(() => {
|
|
11
13
|
paths.current.forEach((path, tone) => path?.setAttribute("d", initial[tone] ?? ""));
|
|
12
|
-
if (animation === 0 || !svg.current)
|
|
14
|
+
if (animation === 0 || !hasGrain || !svg.current)
|
|
13
15
|
return;
|
|
14
16
|
const paint = (frame) => {
|
|
15
|
-
const values = grainPaths(seed, frame);
|
|
17
|
+
const values = grainPaths(seed, frame, grainAmount);
|
|
16
18
|
paths.current.forEach((path, tone) => path?.setAttribute("d", values[tone] ?? ""));
|
|
17
19
|
};
|
|
18
20
|
return animate(svg.current, animation, paint);
|
|
19
|
-
}, [animation, initial, seed]);
|
|
21
|
+
}, [animation, grainAmount, hasGrain, initial, seed]);
|
|
22
|
+
if (!hasPaint && !hasDistortion)
|
|
23
|
+
return null;
|
|
20
24
|
return _jsxs("svg", { ref: svg, "data-surface-material": "", "aria-hidden": "true", focusable: "false", style: {
|
|
21
25
|
position: "absolute",
|
|
22
26
|
zIndex: -1,
|
|
@@ -26,14 +30,33 @@ export function SurfaceMaterial({ animation, color, grain, opacity }) {
|
|
|
26
30
|
height: "100%",
|
|
27
31
|
overflow: "hidden",
|
|
28
32
|
borderRadius: "inherit",
|
|
29
|
-
border: "1px solid rgba(15, 17, 21, 0.08)",
|
|
30
33
|
boxSizing: "border-box",
|
|
31
34
|
opacity,
|
|
32
35
|
pointerEvents: "none"
|
|
33
|
-
}, children: [
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
}, children: [(hasGrain || hasDistortion) && _jsxs("defs", { children: [hasDistortion && _jsx(DistortionFilter, { distortion: distortion, identity: identity, ripples: ripples, seed: seed, waves: waves }), hasGrain && _jsx("pattern", { id: `${identity}-grain`, width: patternSize, height: patternSize, patternUnits: "userSpaceOnUse", children: initial.map((path, tone) => _jsx("path", { ref: node => { paths.current[tone] = node; }, "data-surface-grain-tone": tone, d: path, fill: grainTone(color, tone, grain), shapeRendering: "crispEdges" }, tone)) })] }), hasPaint && _jsx("rect", { "data-surface-base": "", width: "100%", height: "100%", fill: color }), hasGrain && _jsx("rect", { "data-surface-grain": "", width: "100%", height: "100%", fill: `url(#${identity}-grain)`, shapeRendering: "crispEdges" })] });
|
|
37
|
+
}
|
|
38
|
+
function DistortionFilter({ distortion, identity, ripples, seed, waves }) {
|
|
39
|
+
const fields = [
|
|
40
|
+
distortion > 0 && { name: "organic", strength: distortion, x: "R" },
|
|
41
|
+
waves > 0 && { name: "waves", strength: waves, x: "R" },
|
|
42
|
+
ripples > 0 && { name: "ripples", strength: ripples, x: "B" }
|
|
43
|
+
].filter((field) => Boolean(field));
|
|
44
|
+
const scale = fields.reduce((total, field) => total + field.strength, 0);
|
|
45
|
+
const weighted = fields.map(field => `${identity}-${field.name}-weighted`);
|
|
46
|
+
const map = `${identity}-distortion-map`;
|
|
47
|
+
return _jsxs("filter", { id: `${identity}-distortion`, "data-surface-distortion": "", x: "-20%", y: "-20%", width: "140%", height: "140%", colorInterpolationFilters: "sRGB", children: [distortion > 0 && _jsxs(Fragment, { children: [_jsx("feTurbulence", { "data-surface-distortion-noise": "", "data-surface-distortion-field": "organic", type: "fractalNoise", baseFrequency: "0.008 0.008", numOctaves: 2, seed: 92, result: `${identity}-organic-noise` }), _jsx("feGaussianBlur", { in: `${identity}-organic-noise`, stdDeviation: 2, result: `${identity}-organic-noise-blurred` })] }), waves > 0 && _jsx(Fragment, { children: _jsx("feTurbulence", { "data-surface-distortion-field": "waves", type: "turbulence", baseFrequency: "0.006 0.045", numOctaves: 1, seed: seed + 17, result: `${identity}-waves-noise` }) }), ripples > 0 && _jsx(Fragment, { children: _jsx("feTurbulence", { "data-surface-distortion-field": "ripples", type: "turbulence", baseFrequency: "0.055", numOctaves: 2, seed: seed + 31, result: `${identity}-ripples-noise` }) }), fields.map(field => _jsx("feColorMatrix", { "data-surface-distortion-weight": field.name, in: `${identity}-${field.name}-noise${field.name === "organic" ? "-blurred" : ""}`, type: "matrix", values: weightMatrix(field.x, field.strength / scale), result: `${identity}-${field.name}-weighted` }, field.name)), weighted.slice(1).map((field, index) => _jsx("feComposite", { "data-surface-distortion-combine": "", in: index === 0 ? weighted[0] : `${identity}-distortion-sum-${index}`, in2: field, operator: "arithmetic", k2: 1, k3: 1, result: index === weighted.length - 2 ? map : `${identity}-distortion-sum-${index + 1}` }, field)), _jsx("feDisplacementMap", { "data-surface-distortion-stage": "combined", in: "SourceGraphic", in2: weighted.length === 1 ? weighted[0] : map, scale: scale, xChannelSelector: "R", yChannelSelector: "G" })] });
|
|
48
|
+
}
|
|
49
|
+
function weightMatrix(x, weight) {
|
|
50
|
+
const red = x === "R" ? `${weight} 0 0 0 0` : `0 0 ${weight} 0 0`;
|
|
51
|
+
return `${red}
|
|
52
|
+
0 ${weight} 0 0 0
|
|
53
|
+
0 0 0 0 0
|
|
54
|
+
0 0 0 1 0`;
|
|
55
|
+
}
|
|
56
|
+
function grainTone(color, tone, intensity) {
|
|
57
|
+
const channel = Math.round(tone / (toneCount - 1) * 255);
|
|
58
|
+
const percentage = Math.round(intensity * 10_000) / 100;
|
|
59
|
+
return `color-mix(in srgb, ${color} ${100 - percentage}%, rgb(${channel} ${channel} ${channel}) ${percentage}%)`;
|
|
37
60
|
}
|
|
38
61
|
class AnimationClock {
|
|
39
62
|
#entries = new Set();
|
|
@@ -88,7 +111,7 @@ function animate(element, rate, paint) {
|
|
|
88
111
|
}
|
|
89
112
|
return clock.subscribe(rate, paint);
|
|
90
113
|
}
|
|
91
|
-
function grainPaths(seed, frame) {
|
|
114
|
+
function grainPaths(seed, frame, amount) {
|
|
92
115
|
const tones = Array.from({ length: toneCount }, () => []);
|
|
93
116
|
const frameX = frame * 19.17;
|
|
94
117
|
const frameY = frame * 7.31;
|
|
@@ -96,6 +119,9 @@ function grainPaths(seed, frame) {
|
|
|
96
119
|
for (let x = 0; x < patternSize; x += 1) {
|
|
97
120
|
const pointX = x + seed * 41;
|
|
98
121
|
const pointY = y + seed * 17;
|
|
122
|
+
const presence = shaderHash(pointX + frameX + 71.9, pointY + frameY + 13.7);
|
|
123
|
+
if (presence > amount)
|
|
124
|
+
continue;
|
|
99
125
|
const fine = shaderHash(Math.floor(pointX * 1.18) + frameX, Math.floor(pointY * 1.18) + frameY);
|
|
100
126
|
const clustered = shaderHash(Math.floor(pointX * 0.47) + frameX + 31.7, Math.floor(pointY * 0.47) + frameY + 31.7);
|
|
101
127
|
const value = clamp(fine * 0.8 + clustered * 0.2, 0, 1);
|
package/dist/surface.d.ts
CHANGED
|
@@ -8,23 +8,47 @@ export type SurfaceProps = Omit<ComponentPropsWithoutRef<"div">, "color" | "opac
|
|
|
8
8
|
color?: SurfaceColor;
|
|
9
9
|
/** Theme-derived level or direct grain intensity from zero to one. */
|
|
10
10
|
grain?: ScaleLevel | number;
|
|
11
|
+
/** Theme-derived level or direct retained grain amount from zero to one. */
|
|
12
|
+
grainAmount?: ScaleLevel | number;
|
|
11
13
|
/** Theme-derived level or direct grain changes per second from zero to 16. */
|
|
12
14
|
animation?: ScaleLevel | number;
|
|
13
15
|
/** Theme-derived level or direct backdrop blur from zero to 24 CSS pixels. */
|
|
14
16
|
backdrop?: ScaleLevel | number;
|
|
15
17
|
/** Theme-derived level or direct material opacity from zero to one. */
|
|
16
18
|
opacity?: ScaleLevel | number;
|
|
19
|
+
/** Theme-derived level or direct organic displacement from zero to 140 pixels. */
|
|
20
|
+
distortion?: ScaleLevel | number;
|
|
21
|
+
/** Theme-derived level or direct directional displacement from zero to 40 pixels. */
|
|
22
|
+
waves?: ScaleLevel | number;
|
|
23
|
+
/** Theme-derived level or direct ripple displacement from zero to 40 pixels. */
|
|
24
|
+
ripples?: ScaleLevel | number;
|
|
25
|
+
/** Theme-derived level or direct backdrop saturation multiplier. */
|
|
26
|
+
saturation?: ScaleLevel | number;
|
|
27
|
+
/** Theme-derived level or direct backdrop brightness multiplier. */
|
|
28
|
+
brightness?: ScaleLevel | number;
|
|
17
29
|
}>;
|
|
18
|
-
/** Contains content above
|
|
30
|
+
/** Contains content above locally owned Surface material layers. */
|
|
19
31
|
export declare const Surface: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "color" | "opacity"> & Readonly<{
|
|
20
32
|
/** Theme-derived treatment or direct CSS material color. */
|
|
21
33
|
color?: SurfaceColor;
|
|
22
34
|
/** Theme-derived level or direct grain intensity from zero to one. */
|
|
23
35
|
grain?: ScaleLevel | number;
|
|
36
|
+
/** Theme-derived level or direct retained grain amount from zero to one. */
|
|
37
|
+
grainAmount?: ScaleLevel | number;
|
|
24
38
|
/** Theme-derived level or direct grain changes per second from zero to 16. */
|
|
25
39
|
animation?: ScaleLevel | number;
|
|
26
40
|
/** Theme-derived level or direct backdrop blur from zero to 24 CSS pixels. */
|
|
27
41
|
backdrop?: ScaleLevel | number;
|
|
28
42
|
/** Theme-derived level or direct material opacity from zero to one. */
|
|
29
43
|
opacity?: ScaleLevel | number;
|
|
44
|
+
/** Theme-derived level or direct organic displacement from zero to 140 pixels. */
|
|
45
|
+
distortion?: ScaleLevel | number;
|
|
46
|
+
/** Theme-derived level or direct directional displacement from zero to 40 pixels. */
|
|
47
|
+
waves?: ScaleLevel | number;
|
|
48
|
+
/** Theme-derived level or direct ripple displacement from zero to 40 pixels. */
|
|
49
|
+
ripples?: ScaleLevel | number;
|
|
50
|
+
/** Theme-derived level or direct backdrop saturation multiplier. */
|
|
51
|
+
saturation?: ScaleLevel | number;
|
|
52
|
+
/** Theme-derived level or direct backdrop brightness multiplier. */
|
|
53
|
+
brightness?: ScaleLevel | number;
|
|
30
54
|
}> & import("react").RefAttributes<HTMLDivElement>>;
|
package/dist/surface.js
CHANGED
|
@@ -1,40 +1,87 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef, useCallback, useLayoutEffect, useRef } from "react";
|
|
3
|
-
import { color as deriveColor, isScaleLevel, scale, themeLimits } from "@phreshos/core";
|
|
2
|
+
import { forwardRef, useCallback, useId, useLayoutEffect, useRef } from "react";
|
|
3
|
+
import { color as deriveColor, isScaleLevel, scale, scaleMultiplier, themeLimits } from "@phreshos/core";
|
|
4
4
|
import { SurfaceMaterial } from "./surface-material.js";
|
|
5
5
|
import { useTheme } from "./theme-provider.js";
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const layerStyle = {
|
|
7
|
+
position: "absolute",
|
|
8
|
+
inset: 0,
|
|
9
|
+
borderRadius: "inherit",
|
|
10
|
+
pointerEvents: "none"
|
|
11
|
+
};
|
|
12
|
+
const borderOpacity = 0.3;
|
|
13
|
+
/** Contains content above locally owned Surface material layers. */
|
|
14
|
+
export const Surface = forwardRef(function Surface({ animation, backdrop, brightness, children, color, distortion, grain, grainAmount, opacity, ripples, saturation, style, waves, ...properties }, forwardedRef) {
|
|
8
15
|
const theme = useTheme();
|
|
16
|
+
const identity = `phresh-surface-${useId().replaceAll(":", "")}`;
|
|
9
17
|
const element = useRef(null);
|
|
10
|
-
const
|
|
18
|
+
const capture = useCallback((node) => {
|
|
11
19
|
element.current = node;
|
|
12
20
|
if (typeof forwardedRef === "function")
|
|
13
21
|
forwardedRef(node);
|
|
14
22
|
else if (forwardedRef)
|
|
15
23
|
forwardedRef.current = node;
|
|
16
24
|
}, [forwardedRef]);
|
|
17
|
-
const
|
|
18
|
-
const resolvedGrain = resolveScale(grain, theme.surface.grain, themeLimits.surface.grain);
|
|
19
|
-
const resolvedAnimation = resolveScale(animation, theme.surface.animation, themeLimits.surface.animation);
|
|
20
|
-
const resolvedBackdrop = resolveScale(backdrop, theme.surface.backdrop, themeLimits.surface.backdrop);
|
|
21
|
-
const resolvedOpacity = resolveScale(opacity, theme.surface.opacity, themeLimits.surface.opacity);
|
|
22
|
-
const blur = resolvedBackdrop === 0 ? {} : {
|
|
23
|
-
backdropFilter: `blur(${resolvedBackdrop}px)`,
|
|
24
|
-
WebkitBackdropFilter: `blur(${resolvedBackdrop}px)`
|
|
25
|
-
};
|
|
25
|
+
const resolved = resolveSurface({ animation, backdrop, brightness, color, distortion, grain, grainAmount, opacity, ripples, saturation, waves }, theme);
|
|
26
26
|
useLayoutEffect(() => {
|
|
27
27
|
const surface = element.current;
|
|
28
28
|
if (surface)
|
|
29
29
|
return prepareSurfaceLayout(surface);
|
|
30
30
|
});
|
|
31
|
-
return _jsxs("div", { ...properties, ref:
|
|
31
|
+
return _jsxs("div", { ...properties, ref: capture, style: {
|
|
32
32
|
borderRadius: theme.radius,
|
|
33
33
|
color: theme.foreground,
|
|
34
|
-
...blur,
|
|
35
34
|
...style
|
|
36
|
-
}, children: [_jsx(
|
|
35
|
+
}, children: [resolved.refracts && _jsx(BackdropLayer, { name: "refraction", filter: `url("#${identity}-distortion")`, zIndex: -3 }), resolved.frost && _jsx(BackdropLayer, { name: "frost", filter: resolved.frost, zIndex: -2 }), _jsx(SurfaceBorder, { color: resolved.material.color }), _jsx(SurfaceMaterial, { identity: identity, ...resolved.material }), children] });
|
|
37
36
|
});
|
|
37
|
+
/** Draws one continuous edge from the same color as the Surface material. */
|
|
38
|
+
function SurfaceBorder({ color }) {
|
|
39
|
+
const highlight = `color-mix(in oklch, ${color} 88%, white)`;
|
|
40
|
+
const shade = `color-mix(in oklch, ${color} 94%, black)`;
|
|
41
|
+
return _jsx("div", { "data-surface-border": "", "aria-hidden": "true", style: {
|
|
42
|
+
...layerStyle,
|
|
43
|
+
zIndex: 0,
|
|
44
|
+
borderStyle: "solid",
|
|
45
|
+
borderWidth: 1,
|
|
46
|
+
borderColor: `${highlight} ${shade} ${shade} ${highlight}`,
|
|
47
|
+
boxSizing: "border-box",
|
|
48
|
+
opacity: borderOpacity
|
|
49
|
+
} });
|
|
50
|
+
}
|
|
51
|
+
/** Keeps refraction and native frost in independent compositor passes. */
|
|
52
|
+
function BackdropLayer({ filter, name, zIndex }) {
|
|
53
|
+
return _jsx("div", { "data-surface-backdrop": name, "aria-hidden": "true", style: {
|
|
54
|
+
...layerStyle,
|
|
55
|
+
zIndex,
|
|
56
|
+
backdropFilter: filter,
|
|
57
|
+
WebkitBackdropFilter: filter
|
|
58
|
+
} });
|
|
59
|
+
}
|
|
60
|
+
function resolveSurface(values, theme) {
|
|
61
|
+
const material = {
|
|
62
|
+
animation: resolveScale(values.animation, theme.surface.animation, themeLimits.surface.animation),
|
|
63
|
+
color: resolveColor(values.color, theme.background),
|
|
64
|
+
distortion: resolveScale(values.distortion, theme.surface.distortion, themeLimits.surface.distortion),
|
|
65
|
+
grain: resolveScale(values.grain, theme.surface.grain, themeLimits.surface.grain),
|
|
66
|
+
grainAmount: resolveScale(values.grainAmount, theme.surface.grainAmount, themeLimits.surface.grainAmount),
|
|
67
|
+
opacity: resolveScale(values.opacity, theme.surface.opacity, themeLimits.surface.opacity),
|
|
68
|
+
ripples: resolveScale(values.ripples, theme.surface.ripples, themeLimits.surface.ripples),
|
|
69
|
+
waves: resolveScale(values.waves, theme.surface.waves, themeLimits.surface.waves)
|
|
70
|
+
};
|
|
71
|
+
const backdrop = resolveScale(values.backdrop, theme.surface.backdrop, themeLimits.surface.backdrop);
|
|
72
|
+
const saturation = resolveMultiplier(values.saturation, theme.surface.saturation, themeLimits.surface.saturation);
|
|
73
|
+
const brightness = resolveMultiplier(values.brightness, theme.surface.brightness, themeLimits.surface.brightness);
|
|
74
|
+
const frost = [
|
|
75
|
+
backdrop === 0 ? "" : `blur(${backdrop}px)`,
|
|
76
|
+
saturation === 1 ? "" : `saturate(${saturation})`,
|
|
77
|
+
brightness === 1 ? "" : `brightness(${brightness})`
|
|
78
|
+
].filter(Boolean).join(" ");
|
|
79
|
+
return {
|
|
80
|
+
material,
|
|
81
|
+
frost,
|
|
82
|
+
refracts: material.distortion > 0 || material.waves > 0 || material.ripples > 0
|
|
83
|
+
};
|
|
84
|
+
}
|
|
38
85
|
function resolveColor(value, base) {
|
|
39
86
|
if (value === undefined)
|
|
40
87
|
return base;
|
|
@@ -48,6 +95,11 @@ function resolveScale(value, base, range) {
|
|
|
48
95
|
const finite = Number.isFinite(resolved) ? resolved : base;
|
|
49
96
|
return Math.min(range.maximum, Math.max(range.minimum, finite));
|
|
50
97
|
}
|
|
98
|
+
function resolveMultiplier(value, base, range) {
|
|
99
|
+
const resolved = isScaleLevel(value) ? scaleMultiplier(base, value) : value ?? base;
|
|
100
|
+
const finite = Number.isFinite(resolved) ? resolved : base;
|
|
101
|
+
return Math.min(range.maximum, Math.max(range.minimum, finite));
|
|
102
|
+
}
|
|
51
103
|
function prepareSurfaceLayout(element) {
|
|
52
104
|
const view = element.ownerDocument.defaultView;
|
|
53
105
|
const computed = view?.getComputedStyle(element);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/react-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "React components for coherent PhreshOS Program interfaces.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"prepack": "node --run test && node --run build"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
|
-
"@phreshos/core": "^0.1.
|
|
55
|
+
"@phreshos/core": "^0.1.9",
|
|
56
56
|
"react": "^19.2.0",
|
|
57
57
|
"react-dom": "^19.2.0"
|
|
58
58
|
},
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@base-ui/react": "^1.7.0",
|
|
64
|
-
"@phreshos/core": "^0.1.
|
|
64
|
+
"@phreshos/core": "^0.1.9",
|
|
65
65
|
"@testing-library/dom": "^10.4.1",
|
|
66
66
|
"@testing-library/react": "^16.3.0",
|
|
67
67
|
"@testing-library/user-event": "^14.6.1",
|