@phreshos/react-ui 0.1.27 → 0.1.29
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 +40 -21
- package/dist/appearance-context.d.ts +47 -16
- package/dist/appearance-context.js +70 -20
- package/dist/appearance.d.ts +6 -0
- package/dist/appearance.js +11 -0
- package/dist/button.js +6 -10
- package/dist/color.d.ts +7 -5
- package/dist/color.js +37 -12
- package/dist/context-menu.d.ts +4 -1
- package/dist/context-menu.js +30 -3
- package/dist/control.d.ts +16 -2
- package/dist/control.js +34 -20
- package/dist/dialog.js +47 -40
- package/dist/direction.d.ts +9 -1
- package/dist/direction.js +34 -10
- package/dist/field-style.js +2 -1
- package/dist/layout.d.ts +1 -1
- package/dist/main.d.ts +4 -3
- package/dist/main.js +3 -2
- package/dist/material-options.d.ts +10 -3
- package/dist/material-options.js +3 -6
- package/dist/material-paint.d.ts +3 -1
- package/dist/material-paint.js +35 -11
- package/dist/menu.js +30 -18
- package/dist/motion-style.d.ts +6 -8
- package/dist/motion-style.js +15 -31
- package/dist/popover.js +13 -12
- package/dist/preferences.d.ts +4 -0
- package/dist/preferences.js +0 -0
- package/dist/radio.js +4 -3
- package/dist/radius.d.ts +1 -1
- package/dist/scroll-area.js +17 -10
- package/dist/select.js +24 -36
- package/dist/shadow-options.d.ts +9 -3
- package/dist/shadow-options.js +3 -6
- package/dist/slider.js +7 -6
- package/dist/spacing.d.ts +1 -1
- package/dist/surface-edge.d.ts +3 -1
- package/dist/surface-edge.js +3 -5
- package/dist/surface.js +24 -19
- package/dist/toggle-indicator.js +2 -5
- package/dist/tooltip.js +16 -15
- package/dist/ui-provider.d.ts +2 -1
- package/dist/ui-provider.js +9 -7
- package/dist/window-header.d.ts +42 -0
- package/dist/window-header.js +113 -0
- package/package.json +2 -2
package/dist/control.js
CHANGED
|
@@ -1,37 +1,50 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo } from "react";
|
|
3
3
|
import { FieldError, Label, Text } from "react-aria-components";
|
|
4
|
-
import {
|
|
4
|
+
import { useResolvedAppearance } from "./appearance-context.js";
|
|
5
5
|
import { resolveRadius } from "./radius.js";
|
|
6
6
|
import { scale } from "./scale.js";
|
|
7
7
|
import { colorOpacity, solidColors } from "./color.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { resolveSolidColor } from "./color.js";
|
|
9
|
+
import { controlTransition, visualTransition } from "./motion-style.js";
|
|
10
10
|
export const controlFontSizes = Object.freeze({
|
|
11
|
-
xsmall:
|
|
12
|
-
small:
|
|
13
|
-
medium:
|
|
14
|
-
large:
|
|
15
|
-
xlarge:
|
|
11
|
+
xsmall: "0.6875em",
|
|
12
|
+
small: "0.75em",
|
|
13
|
+
medium: "0.8125em",
|
|
14
|
+
large: "0.875em",
|
|
15
|
+
xlarge: "0.9375em"
|
|
16
16
|
});
|
|
17
|
+
/** Shared interaction attenuation, derived consistently across control families. */
|
|
18
|
+
export const controlOpacity = Object.freeze({
|
|
19
|
+
disabled: 0.46,
|
|
20
|
+
pending: 0.68,
|
|
21
|
+
secondary: 0.7,
|
|
22
|
+
placeholder: 0.6,
|
|
23
|
+
separator: 0.14
|
|
24
|
+
});
|
|
25
|
+
/** Shared emphasis used by labels and control-owned headings. */
|
|
26
|
+
export const controlFontWeight = 600;
|
|
17
27
|
export function useControlTheme({ size = "medium", color, radius = "medium" }) {
|
|
18
|
-
const
|
|
19
|
-
const
|
|
28
|
+
const resolved = useResolvedAppearance();
|
|
29
|
+
const { appearance, colors } = resolved;
|
|
30
|
+
const transition = useMemo(() => visualTransition(resolved.transaction, resolved.preferences.animations), [resolved.transaction, resolved.preferences.animations]);
|
|
31
|
+
const motionTransition = useMemo(() => controlTransition(resolved.transaction, resolved.preferences.animations), [resolved.transaction, resolved.preferences.animations]);
|
|
20
32
|
const spacing = scale(appearance.spacing, size);
|
|
21
|
-
const colors = useThemedValue(appearance.colors);
|
|
22
33
|
const foreground = colors.foreground;
|
|
23
34
|
const background = colors.background;
|
|
24
|
-
const tint =
|
|
25
|
-
const neutral =
|
|
26
|
-
const focus =
|
|
27
|
-
const danger =
|
|
35
|
+
const tint = resolveSolidColor(color ?? "default:base", colors);
|
|
36
|
+
const neutral = resolveSolidColor("default:base", colors);
|
|
37
|
+
const focus = resolveSolidColor("warning:base", colors);
|
|
38
|
+
const danger = resolveSolidColor("danger:base", colors);
|
|
28
39
|
const paints = useMemo(() => ({
|
|
29
40
|
palette: solidColors(tint, background, foreground),
|
|
30
41
|
neutral: solidColors(neutral, background, foreground),
|
|
31
42
|
danger: solidColors(danger, background, foreground)
|
|
32
43
|
}), [tint, neutral, background, foreground, danger]);
|
|
33
|
-
return {
|
|
44
|
+
return useMemo(() => ({
|
|
34
45
|
transition,
|
|
46
|
+
motionTransition,
|
|
47
|
+
animations: resolved.preferences.animations,
|
|
35
48
|
spacing,
|
|
36
49
|
foreground,
|
|
37
50
|
background,
|
|
@@ -41,9 +54,10 @@ export function useControlTheme({ size = "medium", color, radius = "medium" }) {
|
|
|
41
54
|
paints,
|
|
42
55
|
radius: resolveRadius(radius, appearance),
|
|
43
56
|
fontSize: controlFontSizes[size],
|
|
57
|
+
indicatorSize: Math.max(16, 16 + spacing / 4),
|
|
44
58
|
height: Math.max(24, 24 + spacing),
|
|
45
59
|
gap: Math.max(4, spacing / 2)
|
|
46
|
-
};
|
|
60
|
+
}), [appearance, colors, transition, motionTransition, resolved.preferences.animations, spacing, foreground, background, tint, focus, danger, paints, radius, size]);
|
|
47
61
|
}
|
|
48
62
|
export function fieldStyle(theme, disabled = false, style) {
|
|
49
63
|
return {
|
|
@@ -56,7 +70,7 @@ export function fieldStyle(theme, disabled = false, style) {
|
|
|
56
70
|
fontFamily: "inherit",
|
|
57
71
|
fontSize: theme.fontSize,
|
|
58
72
|
lineHeight: 1.5,
|
|
59
|
-
opacity: disabled ?
|
|
73
|
+
opacity: disabled ? controlOpacity.disabled : 1
|
|
60
74
|
};
|
|
61
75
|
}
|
|
62
76
|
export function controlPaint(theme, focused, invalid, hovered = false) {
|
|
@@ -85,8 +99,8 @@ export function controlStyle(theme, focused, invalid, hovered = false, focusVisi
|
|
|
85
99
|
};
|
|
86
100
|
}
|
|
87
101
|
export function FieldLabel({ label }) {
|
|
88
|
-
return label == null ? null : _jsx(Label, { style: { fontWeight:
|
|
102
|
+
return label == null ? null : _jsx(Label, { style: { fontWeight: controlFontWeight }, children: label });
|
|
89
103
|
}
|
|
90
104
|
export function FieldFeedback({ description, errorMessage, theme }) {
|
|
91
|
-
return _jsxs(_Fragment, { children: [description != null && _jsx(Text, { slot: "description", style: { fontSize: "0.92em", opacity:
|
|
105
|
+
return _jsxs(_Fragment, { children: [description != null && _jsx(Text, { slot: "description", style: { fontSize: "0.92em", opacity: controlOpacity.secondary }, children: description }), _jsx(FieldError, { style: { fontSize: "0.92em", color: theme.danger }, children: errorMessage })] });
|
|
92
106
|
}
|
package/dist/dialog.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef } from "react";
|
|
3
3
|
import { Dialog as AriaDialog, DialogTrigger as AriaDialogTrigger, Heading as AriaHeading, Modal as AriaModal, ModalOverlay as AriaModalOverlay, Text as AriaText } from "react-aria-components";
|
|
4
4
|
import { useAppearance } from "./ui-provider.js";
|
|
5
|
+
import { useResolvedAppearance } from "./appearance-context.js";
|
|
6
|
+
import { colorOpacity, orderColors } from "./color.js";
|
|
7
|
+
import { controlFontWeight, controlOpacity } from "./control.js";
|
|
5
8
|
import { Button } from "./button.js";
|
|
6
|
-
import { backdropMotionClass, overlayMotionClass,
|
|
9
|
+
import MotionStyle, { backdropMotionClass, overlayMotionClass, overlayTransition } from "./motion-style.js";
|
|
7
10
|
import { scale } from "./scale.js";
|
|
8
11
|
import { Surface } from "./surface.js";
|
|
9
12
|
import { resolveDirection, useDirection } from "./direction.js";
|
|
@@ -14,57 +17,61 @@ export const DialogTrigger = forwardRef(function DialogTrigger(properties, ref)
|
|
|
14
17
|
return _jsx(Button, { ...properties, ref: ref });
|
|
15
18
|
});
|
|
16
19
|
export const DialogBackdrop = forwardRef(function DialogBackdrop({ className, style, ...properties }, ref) {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
20
|
+
const resolved = useResolvedAppearance();
|
|
21
|
+
const inset = scale(resolved.appearance.spacing, "medium");
|
|
22
|
+
const colors = resolved.colors;
|
|
23
|
+
const backdrop = colorOpacity(orderColors(colors.background, colors.foreground).darker, 0.32);
|
|
24
|
+
const transition = overlayTransition(resolved.transaction, resolved.preferences.animations);
|
|
19
25
|
const direction = resolveDirection(properties.dir, useDirection());
|
|
20
|
-
return _jsx(AriaModalOverlay, { ...properties, ref: ref, dir: direction, className: [backdropMotionClass, className].filter(Boolean).join(" "), style: {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
return _jsxs(_Fragment, { children: [_jsx(MotionStyle, {}), _jsx(AriaModalOverlay, { ...properties, ref: ref, dir: direction, className: [backdropMotionClass, className].filter(Boolean).join(" "), style: {
|
|
27
|
+
...transition,
|
|
28
|
+
position: "fixed",
|
|
29
|
+
inset: 0,
|
|
30
|
+
zIndex: 1_000,
|
|
31
|
+
display: "grid",
|
|
32
|
+
placeItems: "center",
|
|
33
|
+
boxSizing: "border-box",
|
|
34
|
+
padding: inset,
|
|
35
|
+
overflow: "auto",
|
|
36
|
+
background: backdrop,
|
|
37
|
+
...style
|
|
38
|
+
} })] });
|
|
33
39
|
});
|
|
34
40
|
export const DialogContent = forwardRef(function DialogContent({ children, className, color, material, radius, shadow, style, ...properties }, ref) {
|
|
35
|
-
const
|
|
36
|
-
const
|
|
41
|
+
const resolved = useResolvedAppearance();
|
|
42
|
+
const inset = scale(resolved.appearance.spacing, "medium");
|
|
43
|
+
const transition = overlayTransition(resolved.transaction, resolved.preferences.animations);
|
|
37
44
|
const direction = resolveDirection(properties.dir, useDirection());
|
|
38
|
-
return _jsx(AriaModal, { dir: direction, className: overlayMotionClass, style: {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
outline: "none"
|
|
43
|
-
}, children: _jsx(Surface, { className: className, color: color, material: material, radius: radius, shadow: shadow, style: {
|
|
44
|
-
boxSizing: "border-box",
|
|
45
|
-
width: "100%",
|
|
46
|
-
maxHeight: "inherit",
|
|
47
|
-
padding: inset,
|
|
48
|
-
overflow: "hidden",
|
|
49
|
-
...style
|
|
50
|
-
}, children: _jsx(AriaDialog, { ...properties, ref: ref, style: {
|
|
51
|
-
display: "grid",
|
|
52
|
-
gap: inset,
|
|
53
|
-
minWidth: 0,
|
|
54
|
-
minHeight: 0,
|
|
55
|
-
maxHeight: "inherit",
|
|
45
|
+
return _jsxs(_Fragment, { children: [_jsx(MotionStyle, {}), _jsx(AriaModal, { dir: direction, className: overlayMotionClass, style: {
|
|
46
|
+
...transition,
|
|
47
|
+
width: `min(32rem, calc(100vw - ${inset * 2}px))`,
|
|
48
|
+
maxHeight: `calc(100vh - ${inset * 2}px)`,
|
|
56
49
|
outline: "none"
|
|
57
|
-
}, children:
|
|
50
|
+
}, children: _jsx(Surface, { className: className, color: color, material: material, radius: radius, shadow: shadow, style: {
|
|
51
|
+
boxSizing: "border-box",
|
|
52
|
+
width: "100%",
|
|
53
|
+
maxHeight: "inherit",
|
|
54
|
+
padding: inset,
|
|
55
|
+
overflow: "hidden",
|
|
56
|
+
...style
|
|
57
|
+
}, children: _jsx(AriaDialog, { ...properties, ref: ref, style: {
|
|
58
|
+
display: "grid",
|
|
59
|
+
gap: inset,
|
|
60
|
+
minWidth: 0,
|
|
61
|
+
minHeight: 0,
|
|
62
|
+
maxHeight: "inherit",
|
|
63
|
+
outline: "none"
|
|
64
|
+
}, children: children }) }) })] });
|
|
58
65
|
});
|
|
59
66
|
export const DialogHeader = forwardRef(function DialogHeader({ style, ...properties }, ref) {
|
|
60
67
|
const gap = scale(useAppearance().spacing, "small");
|
|
61
68
|
return _jsx("div", { ...properties, ref: ref, style: { display: "grid", gap, minWidth: 0, ...style } });
|
|
62
69
|
});
|
|
63
70
|
export const DialogTitle = forwardRef(function DialogTitle({ style, ...properties }, ref) {
|
|
64
|
-
return _jsx(AriaHeading, { ...properties, ref: ref, slot: "title", style: { margin: 0, font: "inherit", fontWeight:
|
|
71
|
+
return _jsx(AriaHeading, { ...properties, ref: ref, slot: "title", style: { margin: 0, font: "inherit", fontWeight: controlFontWeight, ...style } });
|
|
65
72
|
});
|
|
66
73
|
export const DialogDescription = forwardRef(function DialogDescription({ style, ...properties }, ref) {
|
|
67
|
-
return _jsx(AriaText, { ...properties, ref: ref, slot: "description", style: { margin: 0, opacity:
|
|
74
|
+
return _jsx(AriaText, { ...properties, ref: ref, slot: "description", style: { margin: 0, opacity: controlOpacity.secondary, ...style } });
|
|
68
75
|
});
|
|
69
76
|
export const DialogBody = forwardRef(function DialogBody({ style, ...properties }, ref) {
|
|
70
77
|
return _jsx("div", { ...properties, ref: ref, style: { minWidth: 0, minHeight: 0, ...style } });
|
package/dist/direction.d.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
/** The two concrete directions supported by React UI behavior. */
|
|
2
2
|
export type Direction = "ltr" | "rtl";
|
|
3
|
-
|
|
3
|
+
interface DirectionSource {
|
|
4
|
+
readonly getSnapshot: () => Direction;
|
|
5
|
+
readonly getServerSnapshot: () => Direction;
|
|
6
|
+
readonly subscribe: (change: () => void) => () => void;
|
|
7
|
+
}
|
|
4
8
|
/** Resolves a concrete element override before falling back to its React UI environment. */
|
|
5
9
|
export declare function resolveDirection(value: string | undefined, fallback: Direction): Direction;
|
|
6
10
|
/** Returns the nearest guaranteed React UI direction, or the document direction. */
|
|
7
11
|
export declare function useDirection(): Direction;
|
|
8
12
|
/** Reactively reads the explicit direction of the document's HTML element. */
|
|
9
13
|
export declare function useDocumentDirection(): Direction;
|
|
14
|
+
/** Creates a stable source for one explicitly established direction boundary. */
|
|
15
|
+
export declare function fixedDirectionSource(direction: Direction): DirectionSource;
|
|
16
|
+
export declare const DirectionContext: import("react").Context<DirectionSource>;
|
|
17
|
+
export {};
|
package/dist/direction.js
CHANGED
|
@@ -1,25 +1,43 @@
|
|
|
1
1
|
import { createContext, useContext, useSyncExternalStore } from "react";
|
|
2
|
-
export const DirectionContext = createContext(null);
|
|
3
2
|
/** Resolves a concrete element override before falling back to its React UI environment. */
|
|
4
3
|
export function resolveDirection(value, fallback) {
|
|
5
4
|
return value === "ltr" || value === "rtl" ? value : fallback;
|
|
6
5
|
}
|
|
7
6
|
/** Returns the nearest guaranteed React UI direction, or the document direction. */
|
|
8
7
|
export function useDirection() {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
return direction ?? documentDirection;
|
|
8
|
+
const source = useContext(DirectionContext);
|
|
9
|
+
return useSyncExternalStore(source.subscribe, source.getSnapshot, source.getServerSnapshot);
|
|
12
10
|
}
|
|
13
11
|
/** Reactively reads the explicit direction of the document's HTML element. */
|
|
14
12
|
export function useDocumentDirection() {
|
|
15
|
-
return useSyncExternalStore(
|
|
13
|
+
return useSyncExternalStore(documentDirectionSource.subscribe, documentDirectionSource.getSnapshot, documentDirectionSource.getServerSnapshot);
|
|
16
14
|
}
|
|
15
|
+
/** Creates a stable source for one explicitly established direction boundary. */
|
|
16
|
+
export function fixedDirectionSource(direction) {
|
|
17
|
+
return {
|
|
18
|
+
getSnapshot: () => direction,
|
|
19
|
+
getServerSnapshot: () => direction,
|
|
20
|
+
subscribe: () => () => undefined
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const subscribers = new Set();
|
|
24
|
+
let observer;
|
|
17
25
|
function subscribeDocumentDirection(change) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
subscribers.add(change);
|
|
27
|
+
if (subscribers.size === 1 && typeof document !== "undefined" && typeof MutationObserver !== "undefined") {
|
|
28
|
+
observer = new MutationObserver(() => {
|
|
29
|
+
for (const subscriber of subscribers)
|
|
30
|
+
subscriber();
|
|
31
|
+
});
|
|
32
|
+
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["dir"] });
|
|
33
|
+
}
|
|
34
|
+
return () => {
|
|
35
|
+
subscribers.delete(change);
|
|
36
|
+
if (subscribers.size === 0) {
|
|
37
|
+
observer?.disconnect();
|
|
38
|
+
observer = undefined;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
23
41
|
}
|
|
24
42
|
function documentDirection() {
|
|
25
43
|
if (typeof document === "undefined")
|
|
@@ -29,3 +47,9 @@ function documentDirection() {
|
|
|
29
47
|
function serverDirection() {
|
|
30
48
|
return "ltr";
|
|
31
49
|
}
|
|
50
|
+
const documentDirectionSource = {
|
|
51
|
+
getSnapshot: documentDirection,
|
|
52
|
+
getServerSnapshot: serverDirection,
|
|
53
|
+
subscribe: subscribeDocumentDirection
|
|
54
|
+
};
|
|
55
|
+
export const DirectionContext = createContext(documentDirectionSource);
|
package/dist/field-style.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { controlOpacity } from "./control.js";
|
|
2
3
|
export const textControlClass = "phreshos-ui-text-control";
|
|
3
4
|
/** Placeholder contrast follows the control's chosen text color, not browser gray. */
|
|
4
5
|
export default function FieldStyle() {
|
|
5
6
|
return _jsx("style", { href: "phreshos-react-ui-fields", precedence: "phreshos", children: `
|
|
6
7
|
.phreshos-ui-text-control::placeholder {
|
|
7
8
|
color: inherit;
|
|
8
|
-
opacity:
|
|
9
|
+
opacity: ${controlOpacity.placeholder};
|
|
9
10
|
}
|
|
10
11
|
` });
|
|
11
12
|
}
|
package/dist/layout.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CSSProperties } from "react";
|
|
2
|
-
import type { Appearance } from "
|
|
2
|
+
import type { Appearance } from "./appearance.js";
|
|
3
3
|
import { type Spacing } from "./spacing.js";
|
|
4
4
|
/** Cross-axis alignment shared by layout containers. */
|
|
5
5
|
export type LayoutAlignment = "start" | "center" | "end" | "stretch" | "baseline";
|
package/dist/main.d.ts
CHANGED
|
@@ -4,15 +4,16 @@
|
|
|
4
4
|
* Components enter this surface only after their implementation-independent
|
|
5
5
|
* behavior contract has been established by the package's tests.
|
|
6
6
|
*/
|
|
7
|
-
export { defaultAppearance } from "
|
|
7
|
+
export { defaultAppearance, type Appearance, type AppearanceColor, type AppearanceColors, type AppearanceMaterial, type AppearanceShadow, type AppearanceTransaction, type Easing, type ThemedValue } from "./appearance.js";
|
|
8
8
|
export { UIProvider, useAppearance, useBrowserPreferences, useDirection, useDocumentDirection, usePreferences, useThemedValue, type UIProviderProps } from "./ui-provider.js";
|
|
9
|
-
export type {
|
|
9
|
+
export type { Preferences, Theme } from "./preferences.js";
|
|
10
10
|
export type { Direction } from "./direction.js";
|
|
11
11
|
export { Flex, type FlexProps } from "./flex.js";
|
|
12
12
|
export { Grid, type GridProps } from "./grid.js";
|
|
13
13
|
export { Surface, type MaterialOptions, type ShadowOptions, type SurfaceComponent, type SurfaceHost, type SurfaceHostProps, type SurfaceOwnProps, type SurfaceProps } from "./surface.js";
|
|
14
14
|
export { defaultColor, type Color, type ColorLevel, type ColorScale } from "./color.js";
|
|
15
15
|
export { Panel, PanelRoot, PanelHeader, PanelContent, type PanelProps, type PanelRootProps, type PanelHeaderProps, type PanelContentProps } from "./panel.js";
|
|
16
|
+
export { WindowHeader, WindowHeaderRoot, WindowHeaderIdentity, WindowHeaderCenter, WindowHeaderActions, WindowHeaderAction, WindowHeaderMinimize, WindowHeaderMaximize, WindowHeaderClose, type WindowHeaderProps, type WindowHeaderRootProps, type WindowHeaderIdentityProps, type WindowHeaderCenterProps, type WindowHeaderActionsProps, type WindowHeaderActionProps, type WindowHeaderControlProps, type WindowHeaderMaximizeProps, type WindowHeaderCloseProps } from "./window-header.js";
|
|
16
17
|
export { Button, type ButtonColor, type ButtonProps } from "./button.js";
|
|
17
18
|
export { Input, type InputProps } from "./input.js";
|
|
18
19
|
export { Textarea, type TextareaProps } from "./textarea.js";
|
|
@@ -25,7 +26,7 @@ export { ScrollArea, type ScrollAreaAxis, type ScrollAreaProps } from "./scroll-
|
|
|
25
26
|
export { Popover, PopoverRoot, PopoverTrigger, PopoverContent, PopoverDialog, PopoverTitle, PopoverClose, type PopoverProps, type PopoverRootProps, type PopoverTriggerProps, type PopoverContentProps, type PopoverDialogProps, type PopoverTitleProps, type PopoverCloseProps } from "./popover.js";
|
|
26
27
|
export { Menu, MenuRoot, MenuItem, MenuSection, MenuHeader, MenuSeparator, type MenuProps, type MenuRootProps, type MenuItemProps, type MenuSectionProps, type MenuHeaderProps, type MenuSeparatorProps } from "./menu.js";
|
|
27
28
|
export { DropdownMenu, DropdownMenuRoot, DropdownMenuTrigger, type DropdownMenuProps, type DropdownMenuRootProps, type DropdownMenuTriggerProps } from "./dropdown-menu.js";
|
|
28
|
-
export { ContextMenu, ContextMenuRoot, ContextMenuTrigger, type ContextMenuProps, type ContextMenuRootProps, type ContextMenuTriggerProps } from "./context-menu.js";
|
|
29
|
+
export { ContextMenu, ContextMenuRoot, ContextMenuTrigger, ContextMenuContent, type ContextMenuProps, type ContextMenuRootProps, type ContextMenuTriggerProps, type ContextMenuContentProps } from "./context-menu.js";
|
|
29
30
|
export { Dialog, DialogRoot, DialogTrigger, DialogBackdrop, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, DialogFooter, DialogClose, type DialogProps, type DialogRootProps, type DialogTriggerProps, type DialogBackdropProps, type DialogContentProps, type DialogHeaderProps, type DialogTitleProps, type DialogDescriptionProps, type DialogBodyProps, type DialogFooterProps, type DialogCloseProps } from "./dialog.js";
|
|
30
31
|
export { AlertDialog, AlertDialogRoot, AlertDialogBackdrop, AlertDialogContent, type AlertDialogProps, type AlertDialogRootProps, type AlertDialogBackdropProps, type AlertDialogContentProps } from "./alert-dialog.js";
|
|
31
32
|
export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, type TooltipProps, type TooltipRootProps, type TooltipTriggerProps, type TooltipContentProps } from "./tooltip.js";
|
package/dist/main.js
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
* Components enter this surface only after their implementation-independent
|
|
5
5
|
* behavior contract has been established by the package's tests.
|
|
6
6
|
*/
|
|
7
|
-
export { defaultAppearance } from "
|
|
7
|
+
export { defaultAppearance } from "./appearance.js";
|
|
8
8
|
export { UIProvider, useAppearance, useBrowserPreferences, useDirection, useDocumentDirection, usePreferences, useThemedValue } from "./ui-provider.js";
|
|
9
9
|
export { Flex } from "./flex.js";
|
|
10
10
|
export { Grid } from "./grid.js";
|
|
11
11
|
export { Surface } from "./surface.js";
|
|
12
12
|
export { defaultColor } from "./color.js";
|
|
13
13
|
export { Panel, PanelRoot, PanelHeader, PanelContent } from "./panel.js";
|
|
14
|
+
export { WindowHeader, WindowHeaderRoot, WindowHeaderIdentity, WindowHeaderCenter, WindowHeaderActions, WindowHeaderAction, WindowHeaderMinimize, WindowHeaderMaximize, WindowHeaderClose } from "./window-header.js";
|
|
14
15
|
export { Button } from "./button.js";
|
|
15
16
|
export { Input } from "./input.js";
|
|
16
17
|
export { Textarea } from "./textarea.js";
|
|
@@ -23,7 +24,7 @@ export { ScrollArea } from "./scroll-area.js";
|
|
|
23
24
|
export { Popover, PopoverRoot, PopoverTrigger, PopoverContent, PopoverDialog, PopoverTitle, PopoverClose } from "./popover.js";
|
|
24
25
|
export { Menu, MenuRoot, MenuItem, MenuSection, MenuHeader, MenuSeparator } from "./menu.js";
|
|
25
26
|
export { DropdownMenu, DropdownMenuRoot, DropdownMenuTrigger } from "./dropdown-menu.js";
|
|
26
|
-
export { ContextMenu, ContextMenuRoot, ContextMenuTrigger } from "./context-menu.js";
|
|
27
|
+
export { ContextMenu, ContextMenuRoot, ContextMenuTrigger, ContextMenuContent } from "./context-menu.js";
|
|
27
28
|
export { Dialog, DialogRoot, DialogTrigger, DialogBackdrop, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, DialogFooter, DialogClose } from "./dialog.js";
|
|
28
29
|
export { AlertDialog, AlertDialogRoot, AlertDialogBackdrop, AlertDialogContent } from "./alert-dialog.js";
|
|
29
30
|
export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent } from "./tooltip.js";
|
|
@@ -13,8 +13,15 @@ export interface MaterialOverrides {
|
|
|
13
13
|
/** Omission or true uses Appearance defaults; false uses the resolved color as a normal background. */
|
|
14
14
|
readonly material?: boolean | MaterialOptions;
|
|
15
15
|
}
|
|
16
|
-
/** Resolves
|
|
17
|
-
export declare function
|
|
16
|
+
/** Resolves overrides against an already selected material branch. */
|
|
17
|
+
export declare function resolveMaterialOptions(values: MaterialOptions, defaults: Readonly<{
|
|
18
|
+
opacity: number;
|
|
19
|
+
backdrop: number;
|
|
20
|
+
grain: number;
|
|
21
|
+
grainAmount: number;
|
|
22
|
+
distortion: number;
|
|
23
|
+
saturation: number;
|
|
24
|
+
}>): {
|
|
18
25
|
opacity: number;
|
|
19
26
|
backdrop: number;
|
|
20
27
|
grain: number;
|
|
@@ -22,4 +29,4 @@ export declare function useMaterialOptions(values?: MaterialOptions): {
|
|
|
22
29
|
distortion: number;
|
|
23
30
|
saturation: number;
|
|
24
31
|
};
|
|
25
|
-
export type ResolvedMaterial = ReturnType<typeof
|
|
32
|
+
export type ResolvedMaterial = ReturnType<typeof resolveMaterialOptions>;
|
package/dist/material-options.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { appearanceLimits } from "
|
|
2
|
-
import { useAppearance, useThemedValue } from "./ui-provider.js";
|
|
1
|
+
import { appearanceLimits } from "./appearance.js";
|
|
3
2
|
import { isScaleLevel, scale, scaleMultiplier } from "./scale.js";
|
|
4
|
-
/** Resolves
|
|
5
|
-
export function
|
|
6
|
-
const appearance = useAppearance();
|
|
7
|
-
const defaults = useThemedValue(appearance.material);
|
|
3
|
+
/** Resolves overrides against an already selected material branch. */
|
|
4
|
+
export function resolveMaterialOptions(values, defaults) {
|
|
8
5
|
const limits = appearanceLimits.material;
|
|
9
6
|
return {
|
|
10
7
|
opacity: resolve(values.opacity, defaults.opacity, limits.opacity),
|
package/dist/material-paint.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
1
2
|
interface MaterialPaintProps {
|
|
2
3
|
readonly color: string;
|
|
3
4
|
readonly distortion: number;
|
|
@@ -5,7 +6,8 @@ interface MaterialPaintProps {
|
|
|
5
6
|
readonly grainAmount: number;
|
|
6
7
|
readonly identity: string;
|
|
7
8
|
readonly opacity: number;
|
|
9
|
+
readonly transition: CSSProperties;
|
|
8
10
|
}
|
|
9
11
|
/** The fill, refraction definition, and grain of one Surface. */
|
|
10
|
-
export declare function MaterialPaint({ color, distortion, grain, grainAmount, identity, opacity }: MaterialPaintProps): import("react").JSX.Element | null;
|
|
12
|
+
export declare function MaterialPaint({ color, distortion, grain, grainAmount, identity, opacity, transition }: MaterialPaintProps): import("react").JSX.Element | null;
|
|
11
13
|
export {};
|
package/dist/material-paint.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo } from "react";
|
|
3
|
-
import { usePaintTransition } from "./motion-style.js";
|
|
4
3
|
/** The fill, refraction definition, and grain of one Surface. */
|
|
5
|
-
export function MaterialPaint({ color, distortion, grain, grainAmount, identity, opacity }) {
|
|
6
|
-
const
|
|
7
|
-
const seed = useMemo(() => seedFrom(identity), [identity]);
|
|
4
|
+
export function MaterialPaint({ color, distortion, grain, grainAmount, identity, opacity, transition }) {
|
|
5
|
+
const grainSeed = useMemo(() => grainSeeds[seedFrom(identity) % grainSeeds.length] ?? grainSeeds[0], [identity]);
|
|
8
6
|
const hasPaint = opacity > 0;
|
|
9
7
|
const hasGrain = hasPaint && grain > 0 && grainAmount > 0;
|
|
10
8
|
const hasDistortion = distortion > 0;
|
|
11
|
-
const
|
|
9
|
+
const grainResource = useMemo(() => hasGrain ? grainImage(grainSeed, grainAmount) : null, [grainSeed, grainAmount, hasGrain]);
|
|
12
10
|
if (!hasPaint && !hasDistortion)
|
|
13
11
|
return null;
|
|
14
|
-
return _jsxs("
|
|
12
|
+
return _jsxs("span", { "data-material-paint": "", "aria-hidden": "true", style: {
|
|
15
13
|
position: "absolute",
|
|
16
14
|
zIndex: -1,
|
|
17
15
|
inset: 0,
|
|
@@ -21,16 +19,38 @@ export function MaterialPaint({ color, distortion, grain, grainAmount, identity,
|
|
|
21
19
|
overflow: "hidden",
|
|
22
20
|
borderRadius: "inherit",
|
|
23
21
|
pointerEvents: "none"
|
|
24
|
-
}, children: [
|
|
22
|
+
}, children: [hasDistortion && _jsx("svg", { width: "0", height: "0", focusable: "false", style: { position: "absolute" }, children: _jsx("defs", { children: _jsx(DistortionFilter, { distortion: distortion, identity: identity }) }) }), hasPaint && _jsx("span", { "data-material-fill": "", "data-material-base": "", style: {
|
|
23
|
+
position: "absolute",
|
|
24
|
+
inset: 0,
|
|
25
|
+
borderRadius: "inherit",
|
|
26
|
+
background: color,
|
|
27
|
+
opacity,
|
|
28
|
+
...transition,
|
|
29
|
+
transitionProperty: "background-color, opacity"
|
|
30
|
+
}, children: hasGrain && _jsx("span", { "data-material-grain": "", style: {
|
|
31
|
+
position: "absolute",
|
|
32
|
+
inset: 0,
|
|
33
|
+
borderRadius: "inherit",
|
|
34
|
+
backgroundImage: grainResource ?? undefined,
|
|
35
|
+
backgroundRepeat: "repeat",
|
|
36
|
+
backgroundSize: `${patternSize}px ${patternSize}px`,
|
|
37
|
+
opacity: grain,
|
|
38
|
+
...transition,
|
|
39
|
+
transitionProperty: "opacity"
|
|
40
|
+
} }) })] });
|
|
25
41
|
}
|
|
26
42
|
function DistortionFilter({ distortion, identity }) {
|
|
27
43
|
return _jsxs("filter", { id: `${identity}-distortion`, "data-material-distortion": "", x: "-20%", y: "-20%", width: "140%", height: "140%", colorInterpolationFilters: "sRGB", children: [_jsx("feTurbulence", { "data-material-distortion-noise": "", "data-material-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` }), _jsx("feDisplacementMap", { "data-material-distortion-stage": "organic", in: "SourceGraphic", in2: `${identity}-organic-noise-blurred`, scale: distortion, xChannelSelector: "R", yChannelSelector: "G" })] });
|
|
28
44
|
}
|
|
29
|
-
function
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
45
|
+
function grainImage(seed, amount) {
|
|
46
|
+
const paths = grainPaths(seed, amount)
|
|
47
|
+
.map((path, tone) => path
|
|
48
|
+
? `<path d="${path}" fill="rgb(${toneChannel(tone)} ${toneChannel(tone)} ${toneChannel(tone)})"/>`
|
|
49
|
+
: "")
|
|
50
|
+
.join("");
|
|
51
|
+
return `url("data:image/svg+xml,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" width="${patternSize}" height="${patternSize}" viewBox="0 0 ${patternSize} ${patternSize}">${paths}</svg>`)}")`;
|
|
33
52
|
}
|
|
53
|
+
/** Preserve the original contract: amount is the monotonic density of visible grain pixels. */
|
|
34
54
|
function grainPaths(seed, amount) {
|
|
35
55
|
const tones = Array.from({ length: toneCount }, () => []);
|
|
36
56
|
for (let y = 0; y < patternSize; y += 1) {
|
|
@@ -49,6 +69,9 @@ function grainPaths(seed, amount) {
|
|
|
49
69
|
}
|
|
50
70
|
return tones.map(tone => tone.join(""));
|
|
51
71
|
}
|
|
72
|
+
function toneChannel(tone) {
|
|
73
|
+
return Math.round(tone / (toneCount - 1) * 255);
|
|
74
|
+
}
|
|
52
75
|
function shaderHash(x, y) {
|
|
53
76
|
let red = fract(x * 0.1031);
|
|
54
77
|
let green = fract(y * 0.1031);
|
|
@@ -75,3 +98,4 @@ function clamp(value, minimum, maximum) {
|
|
|
75
98
|
}
|
|
76
99
|
const patternSize = 64;
|
|
77
100
|
const toneCount = 16;
|
|
101
|
+
const grainSeeds = [19, 47, 83, 131];
|
package/dist/menu.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, forwardRef, useContext } from "react";
|
|
2
|
+
import { createContext, forwardRef, useContext, useMemo } from "react";
|
|
3
3
|
import { Header as AriaHeader, Menu as AriaMenu, MenuItem as AriaMenuItem, MenuSection as AriaMenuSection, Separator as AriaSeparator } from "react-aria-components";
|
|
4
4
|
import { colorOpacity } from "./color.js";
|
|
5
|
-
import { useControlTheme } from "./control.js";
|
|
5
|
+
import { controlFontWeight, controlOpacity, useControlTheme } from "./control.js";
|
|
6
6
|
const MenuThemeContext = createContext(null);
|
|
7
7
|
const MenuRootImplementation = forwardRef(function MenuRoot({ className, color, size = "medium", style, ...properties }, ref) {
|
|
8
8
|
const theme = useControlTheme({ color, size });
|
|
9
|
-
|
|
9
|
+
const context = useMemo(() => ({ color, size, theme }), [color, size, theme]);
|
|
10
|
+
return _jsx(MenuThemeContext.Provider, { value: context, children: _jsx(AriaMenu, { ...properties, ref: ref, className: className, style: {
|
|
10
11
|
display: "grid",
|
|
11
12
|
gap: theme.gap,
|
|
12
|
-
minWidth:
|
|
13
|
+
minWidth: theme.height * 4,
|
|
13
14
|
padding: theme.gap,
|
|
14
15
|
boxSizing: "border-box",
|
|
15
16
|
outline: "none",
|
|
@@ -22,11 +23,19 @@ const MenuRootImplementation = forwardRef(function MenuRoot({ className, color,
|
|
|
22
23
|
export const MenuRoot = MenuRootImplementation;
|
|
23
24
|
const MenuItemImplementation = forwardRef(function MenuItem({ className, color, disabled = false, style, ...properties }, ref) {
|
|
24
25
|
const inherited = useContext(MenuThemeContext);
|
|
25
|
-
const
|
|
26
|
+
const item = { className, disabled, properties, ref, style };
|
|
27
|
+
return inherited != null && color === undefined
|
|
28
|
+
? _jsx(MenuItemView, { ...item, theme: inherited.theme })
|
|
29
|
+
: _jsx(ResolvedMenuItem, { ...item, color: color ?? inherited?.color, size: inherited?.size });
|
|
30
|
+
});
|
|
31
|
+
function ResolvedMenuItem({ color, size, ...properties }) {
|
|
32
|
+
return _jsx(MenuItemView, { ...properties, theme: useControlTheme({ color, size }) });
|
|
33
|
+
}
|
|
34
|
+
function MenuItemView({ className, disabled, properties, ref, style, theme }) {
|
|
26
35
|
return _jsx(AriaMenuItem, { ...properties, ref: ref, className: className, isDisabled: disabled, style: state => {
|
|
27
36
|
const paint = state.isPressed
|
|
28
37
|
? theme.paints.palette.pressed
|
|
29
|
-
: state.
|
|
38
|
+
: state.isHovered || state.isSelected
|
|
30
39
|
? theme.paints.palette.hover
|
|
31
40
|
: { background: "transparent", color: theme.foreground };
|
|
32
41
|
return {
|
|
@@ -40,41 +49,44 @@ const MenuItemImplementation = forwardRef(function MenuItem({ className, color,
|
|
|
40
49
|
paddingInline: Math.max(8, theme.spacing),
|
|
41
50
|
boxSizing: "border-box",
|
|
42
51
|
borderRadius: theme.radius,
|
|
43
|
-
outline: "none",
|
|
52
|
+
outline: state.isFocusVisible ? `1px solid ${theme.focusColor}` : "none",
|
|
53
|
+
outlineOffset: 1,
|
|
44
54
|
cursor: disabled ? "not-allowed" : "pointer",
|
|
45
|
-
opacity: disabled ?
|
|
55
|
+
opacity: disabled ? controlOpacity.disabled : 1,
|
|
46
56
|
userSelect: "none",
|
|
47
57
|
...paint,
|
|
48
58
|
...style
|
|
49
59
|
};
|
|
50
60
|
} });
|
|
51
|
-
}
|
|
61
|
+
}
|
|
52
62
|
export const MenuItem = MenuItemImplementation;
|
|
53
63
|
export const MenuSection = AriaMenuSection;
|
|
54
64
|
export const MenuHeader = forwardRef(function MenuHeader({ style, ...properties }, ref) {
|
|
55
|
-
const
|
|
56
|
-
const fallback = useControlTheme({});
|
|
57
|
-
const theme = inherited?.theme ?? fallback;
|
|
65
|
+
const theme = useMenuTheme();
|
|
58
66
|
return _jsx(AriaHeader, { ...properties, ref: ref, style: {
|
|
59
67
|
paddingInline: Math.max(8, theme.spacing),
|
|
60
68
|
paddingBlock: theme.gap,
|
|
61
|
-
fontWeight:
|
|
62
|
-
opacity:
|
|
69
|
+
fontWeight: controlFontWeight,
|
|
70
|
+
opacity: controlOpacity.secondary,
|
|
63
71
|
...style
|
|
64
72
|
} });
|
|
65
73
|
});
|
|
66
74
|
export const MenuSeparator = forwardRef(function MenuSeparator({ style, ...properties }, ref) {
|
|
67
|
-
const
|
|
68
|
-
const fallback = useControlTheme({});
|
|
69
|
-
const theme = inherited?.theme ?? fallback;
|
|
75
|
+
const theme = useMenuTheme();
|
|
70
76
|
return _jsx(AriaSeparator, { ...properties, ref: ref, style: {
|
|
71
77
|
height: 1,
|
|
72
78
|
marginBlock: theme.gap,
|
|
73
79
|
border: 0,
|
|
74
|
-
background: colorOpacity(theme.foreground,
|
|
80
|
+
background: colorOpacity(theme.foreground, controlOpacity.separator),
|
|
75
81
|
...style
|
|
76
82
|
} });
|
|
77
83
|
});
|
|
84
|
+
function useMenuTheme() {
|
|
85
|
+
const inherited = useContext(MenuThemeContext);
|
|
86
|
+
if (inherited == null)
|
|
87
|
+
throw new Error("Menu parts must be used inside Menu");
|
|
88
|
+
return inherited.theme;
|
|
89
|
+
}
|
|
78
90
|
/** A keyboard-navigable collection of commands or selectable options. */
|
|
79
91
|
export const Menu = Object.assign(MenuRoot, {
|
|
80
92
|
Root: MenuRoot,
|