@phreshos/react-ui 0.1.35 → 0.1.37
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 +147 -167
- package/dist/accordion.d.ts +7 -6
- package/dist/accordion.js +5 -5
- package/dist/alert-dialog.js +3 -1
- package/dist/alert.d.ts +17 -0
- package/dist/alert.js +30 -0
- package/dist/app-layout.d.ts +13 -0
- package/dist/app-layout.js +84 -0
- package/dist/button.d.ts +12 -24
- package/dist/button.js +34 -39
- package/dist/calendar-layout.d.ts +9 -5
- package/dist/calendar-layout.js +67 -78
- package/dist/calendar-trigger.d.ts +4 -4
- package/dist/calendar-trigger.js +7 -29
- package/dist/calendar.d.ts +2 -2
- package/dist/calendar.js +6 -5
- package/dist/checkbox.d.ts +4 -4
- package/dist/checkbox.js +16 -6
- package/dist/color-area.d.ts +20 -0
- package/dist/color-area.js +30 -0
- package/dist/color-field.d.ts +17 -0
- package/dist/color-field.js +17 -0
- package/dist/color-picker.d.ts +24 -0
- package/dist/color-picker.js +30 -0
- package/dist/color-slider.d.ts +19 -0
- package/dist/color-slider.js +29 -0
- package/dist/color-swatch-picker.d.ts +23 -0
- package/dist/color-swatch-picker.js +36 -0
- package/dist/color-swatch.d.ts +18 -0
- package/dist/color-swatch.js +20 -0
- package/dist/combo-box.d.ts +18 -10
- package/dist/combo-box.js +43 -58
- package/dist/context-menu.d.ts +14 -10
- package/dist/context-menu.js +7 -6
- package/dist/control/color-thumb.d.ts +13 -0
- package/dist/control/color-thumb.js +33 -0
- package/dist/control/color-value.d.ts +6 -0
- package/dist/control/color-value.js +7 -0
- package/dist/control/control.d.ts +60 -0
- package/dist/control/control.js +71 -0
- package/dist/control/field-button.d.ts +13 -0
- package/dist/control/field-button.js +28 -0
- package/dist/control/field.d.ts +20 -0
- package/dist/control/field.js +34 -0
- package/dist/control/icon.d.ts +11 -0
- package/dist/control/icon.js +8 -0
- package/dist/control/item.d.ts +34 -0
- package/dist/control/item.js +53 -0
- package/dist/control/open-state.d.ts +25 -0
- package/dist/control/open-state.js +8 -0
- package/dist/control/selection-track.d.ts +39 -0
- package/dist/control/selection-track.js +122 -0
- package/dist/control/selection.d.ts +44 -0
- package/dist/{selection.js → control/selection.js} +16 -0
- package/dist/control/surface-render.d.ts +10 -0
- package/dist/control/surface-render.js +12 -0
- package/dist/control/text-control.d.ts +32 -0
- package/dist/control/text-control.js +31 -0
- package/dist/control/toggle.d.ts +32 -0
- package/dist/control/toggle.js +80 -0
- package/dist/date-control.d.ts +9 -9
- package/dist/date-control.js +31 -34
- package/dist/date-field.d.ts +7 -6
- package/dist/date-field.js +8 -6
- package/dist/date-picker.d.ts +13 -6
- package/dist/date-picker.js +9 -6
- package/dist/date-range-picker.d.ts +15 -6
- package/dist/date-range-picker.js +12 -9
- package/dist/dialog.d.ts +22 -8
- package/dist/dialog.js +37 -28
- package/dist/disclosure.d.ts +8 -7
- package/dist/disclosure.js +31 -52
- package/dist/drop-zone.d.ts +18 -0
- package/dist/drop-zone.js +32 -0
- package/dist/dropdown-menu.d.ts +7 -4
- package/dist/dropdown-menu.js +5 -4
- package/dist/fieldset.d.ts +13 -0
- package/dist/fieldset.js +26 -0
- package/dist/file-trigger.d.ts +13 -0
- package/dist/file-trigger.js +6 -0
- package/dist/flex.d.ts +1 -1
- package/dist/flex.js +5 -4
- package/dist/foundation/appearance.d.ts +83 -0
- package/dist/foundation/appearance.js +38 -0
- package/dist/{aria-direction.js → foundation/aria-direction.js} +3 -1
- package/dist/foundation/color.d.ts +61 -0
- package/dist/foundation/color.js +247 -0
- package/dist/{layout.d.ts → foundation/layout.d.ts} +6 -4
- package/dist/{layout.js → foundation/layout.js} +7 -5
- package/dist/foundation/motion-style.d.ts +9 -0
- package/dist/foundation/motion-style.js +50 -0
- package/dist/foundation/preferences.d.ts +15 -0
- package/dist/{ui-provider.d.ts → foundation/provider.d.ts} +3 -5
- package/dist/{ui-provider.js → foundation/provider.js} +14 -26
- package/dist/{radius.d.ts → foundation/radius.d.ts} +6 -3
- package/dist/foundation/radius.js +11 -0
- package/dist/foundation/spacing.d.ts +11 -0
- package/dist/foundation/spacing.js +4 -0
- package/dist/foundation/visual.d.ts +42 -0
- package/dist/{appearance-context.js → foundation/visual.js} +41 -27
- package/dist/grid.d.ts +1 -1
- package/dist/grid.js +5 -4
- package/dist/icons/main.d.ts +5 -3
- package/dist/icons/main.js +5 -3
- package/dist/input.d.ts +2 -2
- package/dist/input.js +9 -7
- package/dist/list-box.d.ts +24 -19
- package/dist/list-box.js +52 -109
- package/dist/main.d.ts +27 -16
- package/dist/main.js +22 -9
- package/dist/menu.d.ts +28 -32
- package/dist/menu.js +47 -93
- package/dist/number-field.d.ts +17 -0
- package/dist/number-field.js +28 -0
- package/dist/panel.d.ts +10 -5
- package/dist/panel.js +33 -12
- package/dist/popover.d.ts +19 -5
- package/dist/popover.js +22 -15
- package/dist/progress-bar.d.ts +8 -10
- package/dist/progress-bar.js +18 -34
- package/dist/radio.d.ts +15 -12
- package/dist/radio.js +27 -37
- package/dist/range-calendar.d.ts +5 -3
- package/dist/range-calendar.js +8 -7
- package/dist/scroll-area.d.ts +2 -2
- package/dist/scroll-area.js +10 -9
- package/dist/segmented-control.d.ts +23 -0
- package/dist/segmented-control.js +38 -0
- package/dist/select.d.ts +40 -13
- package/dist/select.js +63 -18
- package/dist/slider.d.ts +7 -3
- package/dist/slider.js +51 -50
- package/dist/spinner.d.ts +5 -8
- package/dist/spinner.js +12 -25
- package/dist/surface/grain.d.ts +12 -0
- package/dist/surface/grain.js +65 -0
- package/dist/{material-options.d.ts → surface/material-options.d.ts} +1 -2
- package/dist/{material-options.js → surface/material-options.js} +2 -2
- package/dist/surface/paint-class.d.ts +6 -0
- package/dist/surface/paint-class.js +36 -0
- package/dist/{shadow-options.d.ts → surface/shadow-options.d.ts} +13 -1
- package/dist/{shadow-options.js → surface/shadow-options.js} +17 -2
- package/dist/surface/surface.d.ts +73 -0
- package/dist/surface/surface.js +230 -0
- package/dist/switch.d.ts +4 -5
- package/dist/switch.js +17 -8
- package/dist/table.d.ts +29 -32
- package/dist/table.js +68 -106
- package/dist/tabs.d.ts +16 -15
- package/dist/tabs.js +44 -86
- package/dist/textarea.d.ts +2 -2
- package/dist/textarea.js +10 -14
- package/dist/time-field.d.ts +6 -6
- package/dist/time-field.js +8 -6
- package/dist/toolbar.d.ts +6 -4
- package/dist/toolbar.js +16 -15
- package/dist/tooltip.d.ts +13 -4
- package/dist/tooltip.js +17 -15
- package/dist/tree.d.ts +22 -27
- package/dist/tree.js +54 -99
- package/dist/window.d.ts +2 -2
- package/dist/window.js +18 -20
- package/package.json +3 -3
- package/dist/appearance-context.d.ts +0 -54
- package/dist/appearance.d.ts +0 -11
- package/dist/appearance.js +0 -11
- package/dist/color.d.ts +0 -69
- package/dist/color.js +0 -192
- package/dist/control-surface.d.ts +0 -20
- package/dist/control-surface.js +0 -11
- package/dist/control.d.ts +0 -70
- package/dist/control.js +0 -105
- package/dist/field-style.d.ts +0 -3
- package/dist/field-style.js +0 -12
- package/dist/material-paint.d.ts +0 -13
- package/dist/material-paint.js +0 -119
- package/dist/motion-style.d.ts +0 -13
- package/dist/motion-style.js +0 -75
- package/dist/preferences.d.ts +0 -12
- package/dist/radius.js +0 -11
- package/dist/readiness.d.ts +0 -37
- package/dist/readiness.js +0 -134
- package/dist/selection.d.ts +0 -18
- package/dist/spacing.d.ts +0 -8
- package/dist/spacing.js +0 -8
- package/dist/surface-edge.d.ts +0 -11
- package/dist/surface-edge.js +0 -55
- package/dist/surface.d.ts +0 -27
- package/dist/surface.js +0 -92
- package/dist/text-control.d.ts +0 -10
- package/dist/text-control.js +0 -0
- package/dist/toggle-indicator.d.ts +0 -18
- package/dist/toggle-indicator.js +0 -49
- /package/dist/{aria-direction.d.ts → foundation/aria-direction.d.ts} +0 -0
- /package/dist/{direction.d.ts → foundation/direction.d.ts} +0 -0
- /package/dist/{direction.js → foundation/direction.js} +0 -0
- /package/dist/{overlay-placement.d.ts → foundation/overlay-placement.d.ts} +0 -0
- /package/dist/{overlay-placement.js → foundation/overlay-placement.js} +0 -0
- /package/dist/{preferences.js → foundation/preferences.js} +0 -0
- /package/dist/{scale.d.ts → foundation/scale.d.ts} +0 -0
- /package/dist/{scale.js → foundation/scale.js} +0 -0
package/dist/spinner.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef } from "react";
|
|
3
3
|
import { ProgressBar as AriaProgressBar } from "react-aria-components";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import { scale } from "./scale.js";
|
|
4
|
+
import { useControlMetrics } from "./control/control.js";
|
|
5
|
+
import { colorOpacity, resolveColor } from "./foundation/color.js";
|
|
6
|
+
import MotionStyle from "./foundation/motion-style.js";
|
|
7
|
+
import { scale } from "./foundation/scale.js";
|
|
9
8
|
/** Compact indeterminate progress, either named or explicitly decorative. */
|
|
10
|
-
export const Spinner = forwardRef(function Spinner({ className, color = "
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
const
|
|
9
|
+
export const Spinner = forwardRef(function Spinner({ className, color = "primary", decorative = false, label, size = "medium", style, ...properties }, ref) {
|
|
10
|
+
const metrics = useControlMetrics(size);
|
|
11
|
+
const { colors } = metrics.visual;
|
|
12
|
+
// A standalone status stays visibly distinct from the compact control indicator.
|
|
13
|
+
const diameter = metrics.visual.spacing + scale(metrics.visual.spacing, size);
|
|
14
|
+
const arc = color === "currentColor" ? "currentColor" : resolveColor(color, colors);
|
|
16
15
|
const rootStyle = {
|
|
17
16
|
display: "inline-grid",
|
|
18
17
|
placeItems: "center",
|
|
@@ -23,20 +22,8 @@ export const Spinner = forwardRef(function Spinner({ className, color = "default
|
|
|
23
22
|
verticalAlign: "middle",
|
|
24
23
|
...style
|
|
25
24
|
};
|
|
26
|
-
const indicator = _jsx(
|
|
25
|
+
const indicator = _jsxs(_Fragment, { children: [_jsx(MotionStyle, {}), _jsxs("svg", { "data-spinner-indicator": "", "aria-hidden": "true", focusable: "false", viewBox: "0 0 24 24", width: "100%", height: "100%", fill: "none", style: { display: "block", animation: metrics.visual.duration > 0 ? `phreshos-ui-spin ${Math.max(600, metrics.visual.appearance.transaction.duration * 5)}ms linear infinite` : undefined }, children: [_jsx("circle", { "data-spinner-track": "", cx: "12", cy: "12", r: "9", stroke: colorOpacity(arc, 0.2), strokeWidth: "3" }), _jsx("circle", { "data-spinner-fill": "", cx: "12", cy: "12", r: "9", pathLength: "100", stroke: arc, strokeWidth: "3", strokeLinecap: "round", strokeDasharray: "30 70" })] })] });
|
|
27
26
|
if (decorative)
|
|
28
27
|
return _jsx("div", { ...properties, ref: ref, "aria-hidden": "true", className: className, style: rootStyle, children: indicator });
|
|
29
28
|
return _jsx(AriaProgressBar, { ...properties, ref: ref, "aria-label": label, className: className, isIndeterminate: true, style: rootStyle, children: indicator });
|
|
30
29
|
});
|
|
31
|
-
function SpinnerIndicator({ contextualColor, theme }) {
|
|
32
|
-
const duration = typeof theme.motionTransition.duration === "number"
|
|
33
|
-
? Math.max(0.6, theme.motionTransition.duration * 4)
|
|
34
|
-
: 0.8;
|
|
35
|
-
// currentColor belongs to the surrounding surface, whose canvas is not the
|
|
36
|
-
// Appearance canvas. Alpha preserves contrast against that actual owner.
|
|
37
|
-
const track = contextualColor
|
|
38
|
-
? colorOpacity("currentColor", 0.25)
|
|
39
|
-
: theme.paints.subtle.rest.background;
|
|
40
|
-
const fill = contextualColor ? "currentColor" : theme.paints.palette.rest.background;
|
|
41
|
-
return _jsxs(motion.svg, { "data-spinner-indicator": "", "aria-hidden": "true", focusable: "false", viewBox: "0 0 24 24", width: "100%", height: "100%", fill: "none", initial: false, animate: { rotate: theme.animations ? [0, 360] : 0 }, transition: theme.animations ? { duration, repeat: Infinity, ease: "linear" } : { duration: 0 }, style: { display: "block", transformOrigin: "center" }, children: [_jsx("circle", { "data-spinner-track": "", cx: "12", cy: "12", r: "9", stroke: track, strokeWidth: "3" }), _jsx("circle", { "data-spinner-fill": "", cx: "12", cy: "12", r: "9", pathLength: "100", stroke: fill, strokeWidth: "3", strokeLinecap: "round", strokeDasharray: "68 32" })] });
|
|
42
|
-
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the tiled grain texture for one seed, density, and opacity. The
|
|
3
|
+
* opacity is baked into the image so grain can share one layer with the paint
|
|
4
|
+
* without also fading that layer's backdrop.
|
|
5
|
+
*/
|
|
6
|
+
export declare function grainImage(seed: number, amount: number, opacity: number): string;
|
|
7
|
+
/**
|
|
8
|
+
* One texture serves every Surface. Each Surface tiles it from its own origin,
|
|
9
|
+
* so neighbours never align, and equal paints can share one generated class.
|
|
10
|
+
*/
|
|
11
|
+
export declare const grainSeed = 47;
|
|
12
|
+
export declare const grainSize = 64;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the tiled grain texture for one seed, density, and opacity. The
|
|
3
|
+
* opacity is baked into the image so grain can share one layer with the paint
|
|
4
|
+
* without also fading that layer's backdrop.
|
|
5
|
+
*/
|
|
6
|
+
export function grainImage(seed, amount, opacity) {
|
|
7
|
+
const key = `${seed}:${amount}:${Math.round(opacity * 1_000)}`;
|
|
8
|
+
const cached = resources.get(key);
|
|
9
|
+
if (cached !== undefined)
|
|
10
|
+
return cached;
|
|
11
|
+
const paths = grainPaths(seed, amount)
|
|
12
|
+
.map((path, tone) => path ? `<path d="${path}" fill="rgb(${channel(tone)} ${channel(tone)} ${channel(tone)})"/>` : "")
|
|
13
|
+
.join("");
|
|
14
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${patternSize}" height="${patternSize}" viewBox="0 0 ${patternSize} ${patternSize}"><g opacity="${opacity}">${paths}</g></svg>`;
|
|
15
|
+
const resource = `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
|
|
16
|
+
// The same few textures repeat across every Surface; the bound keeps live
|
|
17
|
+
// grain controls from growing the cache without limit.
|
|
18
|
+
if (resources.size >= resourceLimit)
|
|
19
|
+
resources.delete(resources.keys().next().value);
|
|
20
|
+
resources.set(key, resource);
|
|
21
|
+
return resource;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* One texture serves every Surface. Each Surface tiles it from its own origin,
|
|
25
|
+
* so neighbours never align, and equal paints can share one generated class.
|
|
26
|
+
*/
|
|
27
|
+
export const grainSeed = 47;
|
|
28
|
+
export const grainSize = 64;
|
|
29
|
+
/** `amount` is the monotonic density of visible grain pixels. */
|
|
30
|
+
function grainPaths(seed, amount) {
|
|
31
|
+
const tones = Array.from({ length: toneCount }, () => []);
|
|
32
|
+
for (let y = 0; y < patternSize; y += 1) {
|
|
33
|
+
for (let x = 0; x < patternSize; x += 1) {
|
|
34
|
+
const pointX = x + seed * 41;
|
|
35
|
+
const pointY = y + seed * 17;
|
|
36
|
+
if (hash(pointX + 71.9, pointY + 13.7) > amount)
|
|
37
|
+
continue;
|
|
38
|
+
const fine = hash(Math.floor(pointX * 1.18), Math.floor(pointY * 1.18));
|
|
39
|
+
const clustered = hash(Math.floor(pointX * 0.47) + 31.7, Math.floor(pointY * 0.47) + 31.7);
|
|
40
|
+
const value = Math.min(1, Math.max(0, fine * 0.8 + clustered * 0.2));
|
|
41
|
+
tones[Math.min(toneCount - 1, Math.floor(value * toneCount))]?.push(`M${x} ${y}h1v1h-1z`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return tones.map(tone => tone.join(""));
|
|
45
|
+
}
|
|
46
|
+
function channel(tone) {
|
|
47
|
+
return Math.round(tone / (toneCount - 1) * 255);
|
|
48
|
+
}
|
|
49
|
+
function hash(x, y) {
|
|
50
|
+
let red = fract(x * 0.1031);
|
|
51
|
+
let green = fract(y * 0.1031);
|
|
52
|
+
let blue = fract(x * 0.1031);
|
|
53
|
+
const product = red * (green + 33.33) + green * (blue + 33.33) + blue * (red + 33.33);
|
|
54
|
+
red += product;
|
|
55
|
+
green += product;
|
|
56
|
+
blue += product;
|
|
57
|
+
return fract((red + green) * blue);
|
|
58
|
+
}
|
|
59
|
+
function fract(value) {
|
|
60
|
+
return value - Math.floor(value);
|
|
61
|
+
}
|
|
62
|
+
const patternSize = grainSize;
|
|
63
|
+
const toneCount = 16;
|
|
64
|
+
const resourceLimit = 64;
|
|
65
|
+
const resources = new Map();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ScaleLevel } from "
|
|
1
|
+
import { type ScaleLevel } from "../foundation/scale.js";
|
|
2
2
|
/** Optional values of visual substance, expressed directly or relative to Appearance. */
|
|
3
3
|
export interface MaterialOptions {
|
|
4
4
|
readonly opacity?: ScaleLevel | number;
|
|
@@ -31,4 +31,3 @@ export declare function resolveMaterialOptions(values: MaterialOptions, defaults
|
|
|
31
31
|
distortion: number;
|
|
32
32
|
saturation: number;
|
|
33
33
|
};
|
|
34
|
-
export type ResolvedMaterial = ReturnType<typeof resolveMaterialOptions>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { appearanceLimits } from "
|
|
2
|
-
import { isScaleLevel, scale, scaleMultiplier } from "
|
|
1
|
+
import { appearanceLimits } from "../foundation/appearance.js";
|
|
2
|
+
import { isScaleLevel, scale, scaleMultiplier } from "../foundation/scale.js";
|
|
3
3
|
/** Resolves overrides against an already selected material branch. */
|
|
4
4
|
export function resolveMaterialOptions(values, defaults) {
|
|
5
5
|
const limits = appearanceLimits.material;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Returns the class for one declaration block, registering it on first use. */
|
|
2
|
+
export declare function paintClass(declarations: string): string;
|
|
3
|
+
/** Inserts every registered rule not yet in the document. Runs before layout effects. */
|
|
4
|
+
export declare function flushPaintRules(): void;
|
|
5
|
+
/** Whether generated rules can reach a document in this environment. */
|
|
6
|
+
export declare const paintClassesAvailable: boolean;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every distinct Surface paint becomes one shared class. Surfaces repeat the
|
|
3
|
+
* same few paints across a whole interface, so changing Theme or hovering a
|
|
4
|
+
* control swaps one class name instead of rewriting a dozen inline properties
|
|
5
|
+
* on every element, which dominated the cost of large Theme changes.
|
|
6
|
+
*
|
|
7
|
+
* Rules use zero specificity, so any consumer class or inline style still wins.
|
|
8
|
+
*/
|
|
9
|
+
const classes = new Map();
|
|
10
|
+
const pending = [];
|
|
11
|
+
let sheet = null;
|
|
12
|
+
/** Returns the class for one declaration block, registering it on first use. */
|
|
13
|
+
export function paintClass(declarations) {
|
|
14
|
+
const cached = classes.get(declarations);
|
|
15
|
+
if (cached !== undefined)
|
|
16
|
+
return cached;
|
|
17
|
+
const name = `phreshos-paint-${classes.size.toString(36)}`;
|
|
18
|
+
classes.set(declarations, name);
|
|
19
|
+
pending.push(`:where(.${name}) { ${declarations} }`);
|
|
20
|
+
return name;
|
|
21
|
+
}
|
|
22
|
+
/** Inserts every registered rule not yet in the document. Runs before layout effects. */
|
|
23
|
+
export function flushPaintRules() {
|
|
24
|
+
if (pending.length === 0 || typeof document === "undefined")
|
|
25
|
+
return;
|
|
26
|
+
if (sheet === null) {
|
|
27
|
+
const element = document.createElement("style");
|
|
28
|
+
element.setAttribute("data-phreshos-paint", "");
|
|
29
|
+
document.head.append(element);
|
|
30
|
+
sheet = element.sheet;
|
|
31
|
+
}
|
|
32
|
+
for (const rule of pending.splice(0))
|
|
33
|
+
sheet?.insertRule(rule, sheet.cssRules.length);
|
|
34
|
+
}
|
|
35
|
+
/** Whether generated rules can reach a document in this environment. */
|
|
36
|
+
export const paintClassesAvailable = typeof document !== "undefined";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ScaleLevel } from "
|
|
1
|
+
import { type ScaleLevel } from "../foundation/scale.js";
|
|
2
2
|
/** Optional outer-shadow values expressed directly or relative to Appearance. */
|
|
3
3
|
export interface ShadowOptions {
|
|
4
4
|
readonly x?: ScaleLevel | number;
|
|
@@ -29,3 +29,15 @@ export declare function resolveShadowOptions(values: ShadowOptions, defaults: Re
|
|
|
29
29
|
export type ResolvedShadow = ReturnType<typeof resolveShadowOptions>;
|
|
30
30
|
/** Serializes one resolved neutral-black outer shadow. */
|
|
31
31
|
export declare function shadowStyle({ x, y, blur, spread, opacity }: ResolvedShadow): string;
|
|
32
|
+
/**
|
|
33
|
+
* The outer shadow of something floating above the interface, such as an
|
|
34
|
+
* overlay: the same Appearance shadow, only wider and slightly denser. It keeps
|
|
35
|
+
* the Appearance offset, so a directionless Appearance stays directionless.
|
|
36
|
+
*/
|
|
37
|
+
export declare function floatingShadow(shadow: Readonly<{
|
|
38
|
+
x: number;
|
|
39
|
+
y: number;
|
|
40
|
+
blur: number;
|
|
41
|
+
spread: number;
|
|
42
|
+
opacity: number;
|
|
43
|
+
}>): ShadowOptions;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { appearanceLimits } from "
|
|
2
|
-
import { isScaleLevel, scale } from "
|
|
1
|
+
import { appearanceLimits } from "../foundation/appearance.js";
|
|
2
|
+
import { isScaleLevel, scale } from "../foundation/scale.js";
|
|
3
3
|
/** Resolves overrides against an already selected shadow branch. */
|
|
4
4
|
export function resolveShadowOptions(values, defaults) {
|
|
5
5
|
const limits = appearanceLimits.shadow;
|
|
@@ -20,3 +20,18 @@ function resolve(value, base, range) {
|
|
|
20
20
|
const finite = Number.isFinite(resolved) ? resolved : base;
|
|
21
21
|
return Math.min(range.maximum, Math.max(range.minimum, finite));
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* The outer shadow of something floating above the interface, such as an
|
|
25
|
+
* overlay: the same Appearance shadow, only wider and slightly denser. It keeps
|
|
26
|
+
* the Appearance offset, so a directionless Appearance stays directionless.
|
|
27
|
+
*/
|
|
28
|
+
export function floatingShadow(shadow) {
|
|
29
|
+
const limits = appearanceLimits.shadow;
|
|
30
|
+
return {
|
|
31
|
+
x: shadow.x,
|
|
32
|
+
y: shadow.y,
|
|
33
|
+
blur: Math.min(limits.blur.maximum, Math.max(24, shadow.blur * 3)),
|
|
34
|
+
spread: shadow.spread,
|
|
35
|
+
opacity: Math.min(limits.opacity.maximum, shadow.opacity * 1.6)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ComponentPropsWithRef, CSSProperties, ElementType, ReactElement, ReactNode } from "react";
|
|
2
|
+
import { type Color } from "../foundation/color.js";
|
|
3
|
+
import type { Direction } from "../foundation/direction.js";
|
|
4
|
+
import { type RadiusProps } from "../foundation/radius.js";
|
|
5
|
+
import { type Visual } from "../foundation/visual.js";
|
|
6
|
+
import { type MaterialMode, type MaterialOptions, type MaterialOverrides } from "./material-options.js";
|
|
7
|
+
import { type ShadowOverrides } from "./shadow-options.js";
|
|
8
|
+
export type { MaterialMode, MaterialOptions } from "./material-options.js";
|
|
9
|
+
export type { ShadowOptions } from "./shadow-options.js";
|
|
10
|
+
/**
|
|
11
|
+
* Where a Surface sits relative to its surroundings. Every depth is the same
|
|
12
|
+
* Material; only the direction of light changes.
|
|
13
|
+
* raised — something you act on: an outer shadow and a lit top edge.
|
|
14
|
+
* flat — a region level with its surroundings: only a boundary.
|
|
15
|
+
* recessed — something that holds content: a slightly deeper paint and an
|
|
16
|
+
* inner shadow instead of an outer one.
|
|
17
|
+
*/
|
|
18
|
+
export type SurfaceDepth = "raised" | "flat" | "recessed";
|
|
19
|
+
export interface SurfaceOwnProps extends MaterialOverrides, ShadowOverrides, RadiusProps {
|
|
20
|
+
readonly color?: Color;
|
|
21
|
+
readonly depth?: SurfaceDepth;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The structural properties a component must preserve when it hosts a Surface.
|
|
25
|
+
* It applies `className` and `style` and renders `children` on one element,
|
|
26
|
+
* and forwards its ref to that element.
|
|
27
|
+
*/
|
|
28
|
+
export interface SurfaceHostProps {
|
|
29
|
+
readonly children?: ReactNode;
|
|
30
|
+
readonly className?: string;
|
|
31
|
+
readonly dir?: Direction;
|
|
32
|
+
readonly style?: CSSProperties;
|
|
33
|
+
}
|
|
34
|
+
export type SurfaceHost = ElementType<SurfaceHostProps>;
|
|
35
|
+
export type SurfaceProps<As extends SurfaceHost = "div"> = SurfaceOwnProps & Readonly<{
|
|
36
|
+
as?: As;
|
|
37
|
+
}> & Omit<ComponentPropsWithRef<As>, keyof SurfaceOwnProps | "as" | "color">;
|
|
38
|
+
export type SurfaceComponent = <As extends SurfaceHost = "div">(properties: SurfaceProps<As>) => ReactElement | null;
|
|
39
|
+
/** Interaction state supplied by the components that own a Surface. */
|
|
40
|
+
export interface SurfaceInteraction {
|
|
41
|
+
readonly hovered?: boolean;
|
|
42
|
+
readonly pressed?: boolean;
|
|
43
|
+
readonly focusVisible?: boolean;
|
|
44
|
+
readonly disabled?: boolean;
|
|
45
|
+
/** Marks a value-holding Surface whose current value is rejected. */
|
|
46
|
+
readonly invalid?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The single visual primitive. Components express what they are through
|
|
50
|
+
* color, depth, and interaction; paint, grain, frost, edge, shadow, focus,
|
|
51
|
+
* and the text color are all derived here from Appearance.
|
|
52
|
+
*/
|
|
53
|
+
export declare const SurfaceView: import("react").ForwardRefExoticComponent<SurfaceOwnProps & Readonly<{
|
|
54
|
+
as?: ElementType;
|
|
55
|
+
interaction?: SurfaceInteraction;
|
|
56
|
+
}> & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, keyof SurfaceOwnProps | "as"> & import("react").RefAttributes<Element>>;
|
|
57
|
+
/** One material- and depth-owning element. The host is a div unless `as` selects another. */
|
|
58
|
+
export declare const Surface: SurfaceComponent;
|
|
59
|
+
/**
|
|
60
|
+
* Marks an element that is disabled. Disabling fades only the outermost
|
|
61
|
+
* disabled element: parts inside it, such as a field's well or a disabled
|
|
62
|
+
* calendar's days, are disabled with it and do not fade a second time.
|
|
63
|
+
*/
|
|
64
|
+
export declare function dimmedClass(disabled: boolean, className?: string): string | undefined;
|
|
65
|
+
/** Resolves every visual value of one Surface. Pure over its inputs. */
|
|
66
|
+
export declare function surfacePaint(visual: Visual, color: Color, depth: SurfaceDepth, options: MaterialMode | MaterialOptions | undefined, shadowOptions: ShadowOverrides["shadow"], interaction: SurfaceInteraction | undefined): {
|
|
67
|
+
fill: string;
|
|
68
|
+
text: string;
|
|
69
|
+
ring: string;
|
|
70
|
+
shadow: string;
|
|
71
|
+
distortion: number;
|
|
72
|
+
variables: CSSProperties;
|
|
73
|
+
};
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createElement, forwardRef, useId, useInsertionEffect } from "react";
|
|
3
|
+
import { canvasDarkness, colorOpacity, contrast, mixColor, readableColor, recessColor, resolveColor } from "../foundation/color.js";
|
|
4
|
+
import { resolveRadius } from "../foundation/radius.js";
|
|
5
|
+
import { useVisual } from "../foundation/visual.js";
|
|
6
|
+
import { grainImage, grainSeed, grainSize } from "./grain.js";
|
|
7
|
+
import { flushPaintRules, paintClass, paintClassesAvailable } from "./paint-class.js";
|
|
8
|
+
import { resolveMaterialOptions } from "./material-options.js";
|
|
9
|
+
import { resolveShadowOptions, shadowStyle } from "./shadow-options.js";
|
|
10
|
+
/**
|
|
11
|
+
* The single visual primitive. Components express what they are through
|
|
12
|
+
* color, depth, and interaction; paint, grain, frost, edge, shadow, focus,
|
|
13
|
+
* and the text color are all derived here from Appearance.
|
|
14
|
+
*/
|
|
15
|
+
export const SurfaceView = forwardRef(function Surface({ as: Element = "div", color = "background", depth = "raised", material, shadow, radius, interaction, className, children, style, ...properties }, ref) {
|
|
16
|
+
const visual = useVisual();
|
|
17
|
+
const identity = useId();
|
|
18
|
+
const paint = surfacePaint(visual, color, depth, material, shadow, interaction);
|
|
19
|
+
const borderRadius = radius === undefined && style?.borderRadius !== undefined
|
|
20
|
+
? style.borderRadius
|
|
21
|
+
: resolveRadius(radius ?? "medium", visual.radius);
|
|
22
|
+
const declarations = [
|
|
23
|
+
...Object.entries(paint.variables).map(([name, value]) => `${name}: ${value}`),
|
|
24
|
+
"transition-property: box-shadow, color, outline-color, opacity",
|
|
25
|
+
`transition-duration: ${visual.duration}ms`,
|
|
26
|
+
`transition-timing-function: ${visual.easing}`,
|
|
27
|
+
`box-shadow: ${paint.shadow}`,
|
|
28
|
+
`color: ${paint.text}`,
|
|
29
|
+
`outline: 3px solid ${interaction?.focusVisible ? paint.ring : "transparent"}`,
|
|
30
|
+
"outline-offset: 1px",
|
|
31
|
+
`border-radius: ${typeof borderRadius === "number" ? `${borderRadius}px` : borderRadius}`
|
|
32
|
+
].join("; ");
|
|
33
|
+
const painted = paintClassesAvailable ? paintClass(declarations) : undefined;
|
|
34
|
+
useInsertionEffect(flushPaintRules);
|
|
35
|
+
return createElement(Element, {
|
|
36
|
+
...properties,
|
|
37
|
+
ref,
|
|
38
|
+
className: [surfaceClass, painted, dimmedClass(interaction?.disabled ?? false), className].filter(Boolean).join(" "),
|
|
39
|
+
// Without a document (server rendering), the same declarations travel inline.
|
|
40
|
+
style: painted === undefined ? { ...inlineDeclarations(declarations), ...style } : style
|
|
41
|
+
}, _jsx(SurfaceStyle, {}), paint.distortion > 0 && _jsx(Refraction, { identity: identity, distortion: paint.distortion }), children);
|
|
42
|
+
});
|
|
43
|
+
function inlineDeclarations(declarations) {
|
|
44
|
+
const result = {};
|
|
45
|
+
for (const declaration of declarations.split("; ")) {
|
|
46
|
+
const separator = declaration.indexOf(": ");
|
|
47
|
+
const name = declaration.slice(0, separator);
|
|
48
|
+
result[name.startsWith("--") ? name : name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())] = declaration.slice(separator + 2);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
/** One material- and depth-owning element. The host is a div unless `as` selects another. */
|
|
53
|
+
export const Surface = SurfaceView;
|
|
54
|
+
/**
|
|
55
|
+
* Marks an element that is disabled. Disabling fades only the outermost
|
|
56
|
+
* disabled element: parts inside it, such as a field's well or a disabled
|
|
57
|
+
* calendar's days, are disabled with it and do not fade a second time.
|
|
58
|
+
*/
|
|
59
|
+
export function dimmedClass(disabled, className) {
|
|
60
|
+
return [disabled ? dimmed : undefined, className].filter(Boolean).join(" ") || undefined;
|
|
61
|
+
}
|
|
62
|
+
const disabledOpacity = 0.46;
|
|
63
|
+
const dimmed = "phreshos-dimmed";
|
|
64
|
+
const surfaceClass = "phreshos-surface";
|
|
65
|
+
const materialLevel = Object.freeze({ none: 0, basic: 1, extended: 2, full: 3 });
|
|
66
|
+
/** Resolves every visual value of one Surface. Pure over its inputs. */
|
|
67
|
+
export function surfacePaint(visual, color, depth, options, shadowOptions, interaction) {
|
|
68
|
+
const { colors } = visual;
|
|
69
|
+
const base = resolveColor(color, colors);
|
|
70
|
+
const active = interaction !== undefined && !interaction.disabled;
|
|
71
|
+
const shift = active ? interaction.pressed ? 0.1 : interaction.hovered ? 0.05 : 0 : 0;
|
|
72
|
+
// Recessed paint sinks slightly below its surroundings; interaction then
|
|
73
|
+
// moves the same base toward the content color so every state stays related.
|
|
74
|
+
const darkness = canvasDarkness(colors);
|
|
75
|
+
// A recess moves away from its surroundings toward whichever extreme has
|
|
76
|
+
// room, by one constant perceptual difference on any canvas.
|
|
77
|
+
const rest = depth === "recessed" ? recessColor(base) : base;
|
|
78
|
+
// A transparent Surface has no substance of its own: it takes the content
|
|
79
|
+
// color of whatever it sits on and only reveals interaction as a veil.
|
|
80
|
+
const clear = base === "transparent";
|
|
81
|
+
const fill = clear
|
|
82
|
+
? shift > 0 ? colorOpacity(colors.foreground, shift * 1.3) : "transparent"
|
|
83
|
+
: mixColor(rest, colors.foreground, shift);
|
|
84
|
+
const mode = clear ? "none" : typeof options === "object" ? "full" : options ?? "basic";
|
|
85
|
+
const level = materialLevel[mode];
|
|
86
|
+
const material = resolveMaterialOptions(typeof options === "object" ? options : {}, visual.material);
|
|
87
|
+
const opacity = level >= materialLevel.extended ? material.opacity : 1;
|
|
88
|
+
const translucent = opacity < 1;
|
|
89
|
+
const frost = level >= materialLevel.full && translucent
|
|
90
|
+
? [material.backdrop > 0 ? `blur(${material.backdrop}px)` : "", material.saturation !== 1 ? `saturate(${material.saturation})` : ""].filter(Boolean).join(" ")
|
|
91
|
+
: "";
|
|
92
|
+
const grain = level >= materialLevel.basic && material.grain > 0 && material.grainAmount > 0
|
|
93
|
+
? grainImage(grainSeed, material.grainAmount, level === materialLevel.basic ? material.grain * material.opacity : material.grain)
|
|
94
|
+
: "none";
|
|
95
|
+
const ringBase = interaction?.invalid
|
|
96
|
+
? colors.danger
|
|
97
|
+
: !clear && (contrast(base, colors.background) ?? 1) >= 1.6 ? base : colors.primary;
|
|
98
|
+
const edged = level >= materialLevel.basic;
|
|
99
|
+
const lit = edgeLight(depth, darkness);
|
|
100
|
+
const light = mixColor(fill, "#ffffff", 0.7);
|
|
101
|
+
// Light rises from the middle of the top and bottom edges and spreads along
|
|
102
|
+
// them, fading as it goes, so it leans toward the sides without reaching
|
|
103
|
+
// them; the same slight spill falls inward from both middles. A hairline
|
|
104
|
+
// outside separates the Surface from whatever it sits on. Neither edge
|
|
105
|
+
// dominates, so no Surface reads as a bevel. Depth changes only how much
|
|
106
|
+
// light the rim receives.
|
|
107
|
+
const rim = edged
|
|
108
|
+
? `linear-gradient(to right, transparent, ${colorOpacity(light, lit.side)} 18%, ${colorOpacity(light, lit.edge)} 50%, ${colorOpacity(light, lit.side)} 82%, transparent)`
|
|
109
|
+
: "none";
|
|
110
|
+
const spill = (edge) => edged && lit.spill > 0
|
|
111
|
+
? `radial-gradient(50% 6px at 50% ${edge === "top" ? "0" : "100%"}, ${colorOpacity(light, lit.spill)}, transparent)`
|
|
112
|
+
: "none";
|
|
113
|
+
// A recessed Surface holds a value, so focus and validity also claim its hairline.
|
|
114
|
+
const claimed = interaction?.invalid || (depth === "recessed" && interaction?.focusVisible);
|
|
115
|
+
const hairline = !edged
|
|
116
|
+
? null
|
|
117
|
+
: claimed
|
|
118
|
+
? `0 0 0 1px ${ringBase}`
|
|
119
|
+
: `0 0 0 0.5px ${colorOpacity("#000000", lit.hairline)}`;
|
|
120
|
+
const shadow = visual.shadow;
|
|
121
|
+
const outer = clear || depth !== "raised" || shadowOptions === false || (active && interaction.pressed)
|
|
122
|
+
? null
|
|
123
|
+
: shadowStyle(resolveShadowOptions(typeof shadowOptions === "object" ? shadowOptions : {}, shadow));
|
|
124
|
+
return {
|
|
125
|
+
fill,
|
|
126
|
+
// The label is chosen once for the resting paint: interaction shades stay
|
|
127
|
+
// close to it, and re-deciding per state would flip text on near-ties.
|
|
128
|
+
text: clear ? "inherit" : readableColor(rest, colors),
|
|
129
|
+
ring: colorOpacity(ringBase, 0.34),
|
|
130
|
+
shadow: [hairline, outer === "none" ? null : outer].filter(Boolean).join(", ") || "none",
|
|
131
|
+
distortion: level >= materialLevel.full && translucent ? material.distortion : 0,
|
|
132
|
+
variables: {
|
|
133
|
+
"--phreshos-surface-paint": translucent ? colorOpacity(fill, opacity) : fill,
|
|
134
|
+
"--phreshos-surface-grain": grain,
|
|
135
|
+
"--phreshos-surface-frost": frost || "none",
|
|
136
|
+
"--phreshos-surface-rim": rim,
|
|
137
|
+
"--phreshos-surface-spill-top": spill("top"),
|
|
138
|
+
"--phreshos-surface-spill-bottom": spill("bottom"),
|
|
139
|
+
"--phreshos-surface-duration": `${visual.duration}ms`,
|
|
140
|
+
"--phreshos-surface-easing": visual.easing
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** How much light each part of the rim receives, interpolated along the canvas darkness. */
|
|
145
|
+
function edgeLight(depth, darkness) {
|
|
146
|
+
const { light, dark } = edgeLightEnds[depth];
|
|
147
|
+
return {
|
|
148
|
+
edge: round(mix(light.edge, dark.edge, darkness)),
|
|
149
|
+
side: round(mix(light.side, dark.side, darkness)),
|
|
150
|
+
spill: round(mix(light.spill, dark.spill, darkness)),
|
|
151
|
+
hairline: round(mix(light.hairline, dark.hairline, darkness))
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function round(value) {
|
|
155
|
+
return Math.round(value * 1_000) / 1_000;
|
|
156
|
+
}
|
|
157
|
+
function mix(from, to, amount) {
|
|
158
|
+
return from + (to - from) * amount;
|
|
159
|
+
}
|
|
160
|
+
const edgeLightEnds = Object.freeze({
|
|
161
|
+
raised: {
|
|
162
|
+
light: { edge: 0.55, side: 0.22, spill: 0.15, hairline: 0.16 },
|
|
163
|
+
dark: { edge: 0.18, side: 0.08, spill: 0.035, hairline: 0.55 }
|
|
164
|
+
},
|
|
165
|
+
flat: {
|
|
166
|
+
light: { edge: 0.3, side: 0.14, spill: 0, hairline: 0.12 },
|
|
167
|
+
dark: { edge: 0.1, side: 0.05, spill: 0, hairline: 0.45 }
|
|
168
|
+
},
|
|
169
|
+
recessed: {
|
|
170
|
+
light: { edge: 0.45, side: 0.12, spill: 0, hairline: 0.12 },
|
|
171
|
+
dark: { edge: 0.12, side: 0.04, spill: 0, hairline: 0.45 }
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
/** A separate compositor pass for refraction, present only when it is visible. */
|
|
175
|
+
function Refraction({ identity, distortion }) {
|
|
176
|
+
const filter = `phreshos-refraction-${identity.replaceAll(":", "")}`;
|
|
177
|
+
return _jsx("span", { "aria-hidden": "true", "data-surface-refraction": "", style: {
|
|
178
|
+
position: "absolute",
|
|
179
|
+
inset: 0,
|
|
180
|
+
zIndex: -2,
|
|
181
|
+
borderRadius: "inherit",
|
|
182
|
+
pointerEvents: "none",
|
|
183
|
+
backdropFilter: `url("#${filter}")`,
|
|
184
|
+
WebkitBackdropFilter: `url("#${filter}")`
|
|
185
|
+
}, children: _jsx("svg", { width: "0", height: "0", focusable: "false", style: { position: "absolute" }, children: _jsxs("filter", { id: filter, x: "-20%", y: "-20%", width: "140%", height: "140%", colorInterpolationFilters: "sRGB", children: [_jsx("feTurbulence", { type: "fractalNoise", baseFrequency: "0.008 0.008", numOctaves: 2, seed: 92, result: "noise" }), _jsx("feGaussianBlur", { in: "noise", stdDeviation: 2, result: "soft" }), _jsx("feDisplacementMap", { in: "SourceGraphic", in2: "soft", scale: distortion, xChannelSelector: "R", yChannelSelector: "G" })] }) }) });
|
|
186
|
+
}
|
|
187
|
+
/*
|
|
188
|
+
* Paint and edge live on pseudo-elements driven by per-Surface custom
|
|
189
|
+
* properties, so a Surface is one DOM element regardless of its Material.
|
|
190
|
+
* Position and isolation use zero specificity so any consumer class or style
|
|
191
|
+
* can replace them.
|
|
192
|
+
*/
|
|
193
|
+
const stylesheet = `
|
|
194
|
+
:where(.${dimmed}:not(.${dimmed} .${dimmed})) {
|
|
195
|
+
opacity: ${disabledOpacity};
|
|
196
|
+
}
|
|
197
|
+
:where(.${surfaceClass}) {
|
|
198
|
+
position: relative;
|
|
199
|
+
isolation: isolate;
|
|
200
|
+
}
|
|
201
|
+
.${surfaceClass}::before,
|
|
202
|
+
.${surfaceClass}::after {
|
|
203
|
+
content: "";
|
|
204
|
+
position: absolute;
|
|
205
|
+
inset: 0;
|
|
206
|
+
border-radius: inherit;
|
|
207
|
+
pointer-events: none;
|
|
208
|
+
}
|
|
209
|
+
.${surfaceClass}::before {
|
|
210
|
+
z-index: -1;
|
|
211
|
+
background-color: var(--phreshos-surface-paint);
|
|
212
|
+
background-image: var(--phreshos-surface-spill-top), var(--phreshos-surface-spill-bottom), var(--phreshos-surface-grain);
|
|
213
|
+
background-size: auto, auto, ${grainSize}px ${grainSize}px;
|
|
214
|
+
-webkit-backdrop-filter: var(--phreshos-surface-frost);
|
|
215
|
+
backdrop-filter: var(--phreshos-surface-frost);
|
|
216
|
+
transition: background-color var(--phreshos-surface-duration) var(--phreshos-surface-easing);
|
|
217
|
+
}
|
|
218
|
+
.${surfaceClass}::after {
|
|
219
|
+
z-index: 1;
|
|
220
|
+
padding: 0.5px;
|
|
221
|
+
background: var(--phreshos-surface-rim);
|
|
222
|
+
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
|
223
|
+
-webkit-mask-composite: xor;
|
|
224
|
+
mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
|
|
225
|
+
}
|
|
226
|
+
`;
|
|
227
|
+
/** React hoists and deduplicates this stylesheet, including inside portals. */
|
|
228
|
+
function SurfaceStyle() {
|
|
229
|
+
return _jsx("style", { href: "phreshos-react-ui-surface", precedence: "phreshos", children: stylesheet });
|
|
230
|
+
}
|
package/dist/switch.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { SwitchFieldProps } from "react-aria-components";
|
|
2
|
-
import type
|
|
3
|
-
import type { MaterialOverrides } from "./material-options.js";
|
|
4
|
-
|
|
5
|
-
export interface SwitchProps extends Omit<SwitchFieldProps, ControlOverrides | "isReadOnly" | "isSelected" | "defaultSelected">, ControlProps, FieldProps, MaterialOverrides, ShadowOverrides {
|
|
2
|
+
import { type ControlOverrides, type ControlProps, type FieldProps } from "./control/control.js";
|
|
3
|
+
import type { MaterialOverrides } from "./surface/material-options.js";
|
|
4
|
+
export interface SwitchProps extends Omit<SwitchFieldProps, ControlOverrides | "isSelected" | "defaultSelected">, ControlProps, FieldProps, MaterialOverrides {
|
|
6
5
|
readonly checked?: boolean;
|
|
7
6
|
readonly defaultChecked?: boolean;
|
|
8
7
|
readonly readOnly?: boolean;
|
|
9
8
|
}
|
|
10
|
-
/** An on/off setting. onChange receives the next boolean value. */
|
|
9
|
+
/** An on/off setting. `onChange` receives the next boolean value. */
|
|
11
10
|
export declare const Switch: import("react").ForwardRefExoticComponent<SwitchProps & import("react").RefAttributes<HTMLDivElement>>;
|
package/dist/switch.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef } from "react";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
import { SwitchButton, SwitchField } from "react-aria-components";
|
|
4
|
+
import { useControlMetrics } from "./control/control.js";
|
|
5
|
+
import { FieldFeedback, fieldStyle } from "./control/field.js";
|
|
6
|
+
import { ToggleIndicator, toggleRowStyle } from "./control/toggle.js";
|
|
7
|
+
import { resolveDirection, useDirection } from "./foundation/direction.js";
|
|
8
|
+
import { dimmedClass } from "./surface/surface.js";
|
|
9
|
+
/** An on/off setting. `onChange` receives the next boolean value. */
|
|
10
|
+
export const Switch = forwardRef(function Switch({ label, description, errorMessage, disabled, required, invalid, readOnly, className, checked, defaultChecked, size, color = "primary", style, material, ...properties }, ref) {
|
|
11
|
+
const metrics = useControlMetrics(size);
|
|
10
12
|
const direction = resolveDirection(properties.dir, useDirection());
|
|
11
|
-
return _jsxs(SwitchField, { ...properties, ref: ref, isDisabled: disabled, isRequired: required, isInvalid: invalid, isReadOnly: readOnly, isSelected: checked, defaultSelected: defaultChecked,
|
|
13
|
+
return _jsxs(SwitchField, { ...properties, ref: ref, isDisabled: disabled, isRequired: required, isInvalid: invalid, isReadOnly: readOnly, isSelected: checked, defaultSelected: defaultChecked, className: state => dimmedClass(state.isDisabled, className ?? state.defaultClassName) ?? "", style: fieldStyle(metrics, style), children: [_jsx(SwitchButton, { style: state => toggleRowStyle(metrics, state.isDisabled, state.isReadOnly), children: state => _jsxs(_Fragment, { children: [_jsx(ToggleIndicator, { kind: "switch", direction: direction, color: color, material: material, metrics: metrics, state: {
|
|
14
|
+
selected: state.isSelected,
|
|
15
|
+
hovered: !state.isReadOnly && state.isHovered,
|
|
16
|
+
pressed: !state.isReadOnly && state.isPressed,
|
|
17
|
+
focusVisible: state.isFocusVisible,
|
|
18
|
+
invalid: state.isInvalid,
|
|
19
|
+
disabled: state.isDisabled
|
|
20
|
+
} }), label] }) }), _jsx(FieldFeedback, { metrics: metrics, description: description, errorMessage: errorMessage })] });
|
|
12
21
|
});
|