@phreshos/react-ui 0.1.36 → 0.1.38
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.d.ts +2 -2
- 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 +31 -27
- package/dist/button.js +48 -40
- 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 +37 -0
- package/dist/control/item.js +57 -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 +29 -15
- 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 +11 -8
- 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} +9 -5
- package/dist/{ui-provider.js → foundation/provider.js} +18 -27
- 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 +28 -17
- 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 +26 -12
- 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 -27
- 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 +13 -0
- package/dist/surface/paint-class.js +47 -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 +80 -0
- package/dist/surface/surface.js +239 -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 +17 -8
- package/dist/tooltip.js +17 -15
- package/dist/tree.d.ts +22 -27
- package/dist/tree.js +54 -99
- package/dist/window.d.ts +4 -4
- 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/slider.js
CHANGED
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// Base UI accepts direction
|
|
2
|
+
// Base UI accepts an explicit direction; React Aria derives slider geometry
|
|
3
|
+
// from the locale, which cannot follow a React UI direction boundary.
|
|
3
4
|
import { Slider as BaseSlider } from "@base-ui/react/slider";
|
|
4
5
|
import { DirectionProvider as BaseDirectionProvider } from "@base-ui/react/direction-provider";
|
|
5
6
|
import { forwardRef, useEffect, useId, useRef, useState } from "react";
|
|
6
|
-
import { controlFontWeight, controlOpacity,
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
import { controlFontWeight, controlOpacity, transition, useControlMetrics } from "./control/control.js";
|
|
8
|
+
import { fieldStyle } from "./control/field.js";
|
|
9
|
+
import { lightColor } from "./foundation/color.js";
|
|
10
|
+
import { resolveDirection, useDirection } from "./foundation/direction.js";
|
|
11
|
+
import { SurfaceView, dimmedClass } from "./surface/surface.js";
|
|
12
|
+
/**
|
|
13
|
+
* One numeric value, drawn as a long Switch: a recessed bed, a raised fill in
|
|
14
|
+
* the owner's color up to the thumb, and the Switch thumb dragged along it.
|
|
15
|
+
*/
|
|
16
|
+
export const Slider = forwardRef(function Slider({ label, description, name, form, disabled, size, color = "primary", style, className, orientation = "horizontal", value, defaultValue, minValue = 0, maxValue = 100, step = 1, formatOptions, onChange, onChangeEnd, material, ...properties }, ref) {
|
|
17
|
+
const metrics = useControlMetrics(size);
|
|
18
|
+
const { colors } = metrics.visual;
|
|
11
19
|
const direction = resolveDirection(properties.dir, useDirection());
|
|
12
20
|
const descriptionId = useId();
|
|
13
21
|
const input = useRef(null);
|
|
14
22
|
const initialValue = useRef(defaultValue ?? minValue);
|
|
15
23
|
const [uncontrolledValue, setUncontrolledValue] = useState(initialValue.current);
|
|
16
|
-
const [hovered, setHovered] = useState(false);
|
|
17
24
|
const vertical = orientation === "vertical";
|
|
18
|
-
|
|
19
|
-
const
|
|
25
|
+
// The Switch's proportions: its bed height, and a thumb inset by the same gap.
|
|
26
|
+
const bed = metrics.indicator;
|
|
27
|
+
const inset = 3;
|
|
28
|
+
const thumb = bed - inset * 2;
|
|
20
29
|
const describedBy = [properties["aria-describedby"], description != null ? descriptionId : null].filter(Boolean).join(" ") || undefined;
|
|
21
30
|
const controlled = value !== undefined;
|
|
22
|
-
const resolvedValue = controlled ? value : uncontrolledValue;
|
|
23
31
|
useEffect(() => {
|
|
24
32
|
const formElement = input.current?.form;
|
|
25
33
|
if (controlled || formElement == null)
|
|
@@ -28,44 +36,37 @@ export const Slider = forwardRef(function Slider({ label, description, name, for
|
|
|
28
36
|
formElement.addEventListener("reset", reset);
|
|
29
37
|
return () => formElement.removeEventListener("reset", reset);
|
|
30
38
|
}, [controlled]);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
outline: state.focused ? `1px solid ${theme.focusColor}` : "none",
|
|
65
|
-
outlineOffset: 1,
|
|
66
|
-
cursor: state.disabled ? "not-allowed" : state.dragging ? "grabbing" : "grab",
|
|
67
|
-
transform: `scale(${state.dragging ? 0.92 : hovered ? 1.08 : 1})`
|
|
68
|
-
};
|
|
69
|
-
} })] }) }), description != null && _jsx("span", { id: descriptionId, style: { fontSize: "0.92em", opacity: controlOpacity.secondary }, children: description })] });
|
|
70
|
-
return _jsx(BaseDirectionProvider, { direction: direction, children: root });
|
|
39
|
+
return _jsx(BaseDirectionProvider, { direction: direction, children: _jsxs(BaseSlider.Root, { ...properties, ref: ref, className: dimmedClass(disabled ?? false, className), value: controlled ? value : uncontrolledValue, min: minValue, max: maxValue, step: step, name: name, form: form, disabled: disabled, orientation: orientation, format: formatOptions, onValueChange: next => {
|
|
40
|
+
if (!controlled)
|
|
41
|
+
setUncontrolledValue(next);
|
|
42
|
+
onChange?.(next);
|
|
43
|
+
}, onValueCommitted: next => onChangeEnd?.(next), style: fieldStyle(metrics, style), children: [_jsxs("div", { style: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: metrics.gap }, children: [label != null && _jsx(BaseSlider.Label, { style: { fontWeight: controlFontWeight }, children: label }), _jsx(BaseSlider.Value, { style: { fontVariantNumeric: "tabular-nums", opacity: controlOpacity.secondary } })] }), _jsx(SurfaceView, { as: "div", color: "background", depth: "recessed", material: material, radius: "full", style: {
|
|
44
|
+
display: "flex",
|
|
45
|
+
boxSizing: "border-box",
|
|
46
|
+
// The thumb center travels between the bed ends minus half a thumb,
|
|
47
|
+
// so the thumb keeps the Switch inset at both ends.
|
|
48
|
+
paddingBlock: vertical ? inset + thumb / 2 : inset,
|
|
49
|
+
paddingInline: vertical ? inset : inset + thumb / 2,
|
|
50
|
+
width: vertical ? bed : "100%",
|
|
51
|
+
height: vertical ? metrics.height * 4 : bed
|
|
52
|
+
}, children: _jsx(BaseSlider.Control, { style: { position: "relative", flex: 1, touchAction: "none", userSelect: "none" }, children: _jsxs(BaseSlider.Track, { style: { position: "relative", width: "100%", height: "100%" }, children: [_jsx(BaseSlider.Indicator, { render: (fill, _state) => _jsx(SurfaceView, { ...fill, ref: fill.ref, as: "div", color: color, material: material, shadow: false, radius: "full", style: {
|
|
53
|
+
...fill.style,
|
|
54
|
+
position: "absolute",
|
|
55
|
+
boxSizing: "content-box",
|
|
56
|
+
// The fill starts at the bed's end and wraps the thumb with the
|
|
57
|
+
// bed's inset, like an enabled Switch.
|
|
58
|
+
...(vertical
|
|
59
|
+
? { insetInlineStart: -inset, width: bed, marginBottom: -(inset + thumb / 2), paddingTop: thumb + inset * 2 }
|
|
60
|
+
: { top: -inset, height: bed, marginInlineStart: -(inset + thumb / 2), paddingInlineEnd: thumb + inset * 2 })
|
|
61
|
+
} }) }), _jsx(BaseSlider.Thumb, { inputRef: input, "aria-label": properties["aria-label"], "aria-labelledby": properties["aria-labelledby"], "aria-describedby": describedBy, render: (native, state) => _jsx(SurfaceView, { ...native, ref: native.ref, as: "div",
|
|
62
|
+
// A thumb is lit from above in every Theme.
|
|
63
|
+
color: lightColor(colors), radius: "full", interaction: { hovered: false, pressed: state.dragging, focusVisible: state.focused, disabled: state.disabled }, style: {
|
|
64
|
+
...native.style,
|
|
65
|
+
...transition(metrics.visual, "scale, outline-color, box-shadow"),
|
|
66
|
+
zIndex: 2,
|
|
67
|
+
width: thumb,
|
|
68
|
+
height: thumb,
|
|
69
|
+
cursor: state.disabled ? "not-allowed" : state.dragging ? "grabbing" : "grab",
|
|
70
|
+
scale: state.dragging ? "0.92" : "1"
|
|
71
|
+
} }) })] }) }) }), description != null && _jsx("span", { id: descriptionId, style: { fontSize: "0.92em", opacity: controlOpacity.secondary }, children: description })] }) });
|
|
71
72
|
});
|
package/dist/spinner.d.ts
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import type { CSSProperties } from "react";
|
|
2
2
|
import type { ProgressBarProps as AriaProgressBarProps } from "react-aria-components";
|
|
3
|
-
import type
|
|
4
|
-
import { type ScaleLevel } from "./scale.js";
|
|
3
|
+
import { type Color } from "./foundation/color.js";
|
|
4
|
+
import { type ScaleLevel } from "./foundation/scale.js";
|
|
5
5
|
type NativeSpinnerProps = Omit<AriaProgressBarProps, "aria-hidden" | "aria-label" | "aria-labelledby" | "children" | "className" | "formatOptions" | "isIndeterminate" | "maxValue" | "minValue" | "slot" | "style" | "value" | "valueLabel">;
|
|
6
6
|
interface SpinnerVisualProps extends NativeSpinnerProps {
|
|
7
|
-
/** Native class name applied without replacing the component contract. */
|
|
8
7
|
readonly className?: string;
|
|
9
|
-
/** Color of the moving
|
|
10
|
-
readonly color?:
|
|
11
|
-
/** Derives the indicator diameter from Appearance. */
|
|
8
|
+
/** Color of the moving arc. `currentColor` follows the surrounding content. */
|
|
9
|
+
readonly color?: Color;
|
|
12
10
|
readonly size?: ScaleLevel;
|
|
13
|
-
/** Native styles applied after Spinner defaults. */
|
|
14
11
|
readonly style?: CSSProperties;
|
|
15
12
|
}
|
|
16
13
|
type NamedSpinnerProps = Readonly<{
|
|
17
|
-
/** Accessible
|
|
14
|
+
/** Accessible name announced with indeterminate progress semantics. */
|
|
18
15
|
label: string;
|
|
19
16
|
decorative?: false;
|
|
20
17
|
}>;
|
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,22 +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",
|
|
42
|
-
// A repeating animation must start on mount; initial={false} renders its final keyframe instead.
|
|
43
|
-
initial: { rotate: 0 }, 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" })] });
|
|
44
|
-
}
|
|
@@ -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,13 @@
|
|
|
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;
|
|
7
|
+
/**
|
|
8
|
+
* Whether this render may use paint classes. Server rendering sends paints
|
|
9
|
+
* inline, so hydration renders inline too and matches the server HTML; React
|
|
10
|
+
* then renders again with classes. Without this, the server's inline paint
|
|
11
|
+
* would stay on the element, because hydration does not patch attributes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function usePaintClasses(): boolean;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Every distinct Surface paint becomes one shared class. Surfaces repeat the
|
|
4
|
+
* same few paints across a whole interface, so changing Theme or hovering a
|
|
5
|
+
* control swaps one class name instead of rewriting a dozen inline properties
|
|
6
|
+
* on every element, which dominated the cost of large Theme changes.
|
|
7
|
+
*
|
|
8
|
+
* Rules use zero specificity, so any consumer class or inline style still wins.
|
|
9
|
+
*/
|
|
10
|
+
const classes = new Map();
|
|
11
|
+
const pending = [];
|
|
12
|
+
let sheet = null;
|
|
13
|
+
/** Returns the class for one declaration block, registering it on first use. */
|
|
14
|
+
export function paintClass(declarations) {
|
|
15
|
+
const cached = classes.get(declarations);
|
|
16
|
+
if (cached !== undefined)
|
|
17
|
+
return cached;
|
|
18
|
+
const name = `phreshos-paint-${classes.size.toString(36)}`;
|
|
19
|
+
classes.set(declarations, name);
|
|
20
|
+
pending.push(`:where(.${name}) { ${declarations} }`);
|
|
21
|
+
return name;
|
|
22
|
+
}
|
|
23
|
+
/** Inserts every registered rule not yet in the document. Runs before layout effects. */
|
|
24
|
+
export function flushPaintRules() {
|
|
25
|
+
if (pending.length === 0 || typeof document === "undefined")
|
|
26
|
+
return;
|
|
27
|
+
if (sheet === null) {
|
|
28
|
+
const element = document.createElement("style");
|
|
29
|
+
element.setAttribute("data-phreshos-paint", "");
|
|
30
|
+
document.head.append(element);
|
|
31
|
+
sheet = element.sheet;
|
|
32
|
+
}
|
|
33
|
+
for (const rule of pending.splice(0))
|
|
34
|
+
sheet?.insertRule(rule, sheet.cssRules.length);
|
|
35
|
+
}
|
|
36
|
+
/** Whether generated rules can reach a document in this environment. */
|
|
37
|
+
export const paintClassesAvailable = typeof document !== "undefined";
|
|
38
|
+
const unchanging = () => () => undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Whether this render may use paint classes. Server rendering sends paints
|
|
41
|
+
* inline, so hydration renders inline too and matches the server HTML; React
|
|
42
|
+
* then renders again with classes. Without this, the server's inline paint
|
|
43
|
+
* would stay on the element, because hydration does not patch attributes.
|
|
44
|
+
*/
|
|
45
|
+
export function usePaintClasses() {
|
|
46
|
+
return useSyncExternalStore(unchanging, () => paintClassesAvailable, () => false);
|
|
47
|
+
}
|
|
@@ -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,80 @@
|
|
|
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
|
+
/** How far hover and press move a paint toward the text color. */
|
|
66
|
+
export declare const interactionShift: Readonly<{
|
|
67
|
+
hovered: 0.05;
|
|
68
|
+
pressed: 0.1;
|
|
69
|
+
}>;
|
|
70
|
+
/** A clear Surface has no paint to move, so it shows the same shift as a slightly stronger veil. */
|
|
71
|
+
export declare const clearVeil = 1.3;
|
|
72
|
+
/** Resolves every visual value of one Surface. Pure over its inputs. */
|
|
73
|
+
export declare function surfacePaint(visual: Visual, color: Color, depth: SurfaceDepth, options: MaterialMode | MaterialOptions | undefined, shadowOptions: ShadowOverrides["shadow"], interaction: SurfaceInteraction | undefined): {
|
|
74
|
+
fill: string;
|
|
75
|
+
text: string;
|
|
76
|
+
ring: string;
|
|
77
|
+
shadow: string;
|
|
78
|
+
distortion: number;
|
|
79
|
+
variables: CSSProperties;
|
|
80
|
+
};
|