@phreshos/react-ui 0.1.29 → 0.1.31
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 +197 -39
- package/dist/accordion.d.ts +45 -0
- package/dist/accordion.js +62 -0
- package/dist/alert-dialog.d.ts +0 -1
- package/dist/alert-dialog.js +0 -1
- package/dist/aria-direction.d.ts +9 -0
- package/dist/aria-direction.js +49 -0
- package/dist/calendar-layout.d.ts +8 -0
- package/dist/calendar-layout.js +103 -0
- package/dist/calendar-trigger.d.ts +5 -0
- package/dist/calendar-trigger.js +30 -0
- package/dist/calendar.d.ts +18 -0
- package/dist/calendar.js +13 -0
- package/dist/color.d.ts +16 -1
- package/dist/color.js +60 -11
- package/dist/combo-box.d.ts +20 -0
- package/dist/combo-box.js +68 -0
- package/dist/context-menu.d.ts +2 -4
- package/dist/context-menu.js +0 -1
- package/dist/control-surface.d.ts +2 -1
- package/dist/control-surface.js +2 -2
- package/dist/control.d.ts +1 -1
- package/dist/control.js +7 -8
- package/dist/date-control.d.ts +24 -0
- package/dist/date-control.js +69 -0
- package/dist/date-field.d.ts +19 -0
- package/dist/date-field.js +13 -0
- package/dist/date-picker.d.ts +19 -0
- package/dist/date-picker.js +16 -0
- package/dist/date-range-picker.d.ts +20 -0
- package/dist/date-range-picker.js +25 -0
- package/dist/date-range.d.ts +6 -0
- package/dist/date-range.js +0 -0
- package/dist/dialog.d.ts +4 -2
- package/dist/dialog.js +24 -7
- package/dist/disclosure.d.ts +42 -0
- package/dist/disclosure.js +97 -0
- package/dist/dropdown-menu.d.ts +0 -2
- package/dist/dropdown-menu.js +0 -1
- package/dist/list-box.d.ts +49 -0
- package/dist/list-box.js +129 -0
- package/dist/main.d.ts +27 -11
- package/dist/main.js +26 -10
- package/dist/material-options.d.ts +4 -2
- package/dist/material-paint.js +19 -1
- package/dist/menu.d.ts +22 -4
- package/dist/menu.js +25 -6
- package/dist/panel.d.ts +0 -1
- package/dist/panel.js +0 -1
- package/dist/popover.d.ts +0 -1
- package/dist/popover.js +13 -3
- package/dist/progress-bar.d.ts +22 -0
- package/dist/progress-bar.js +46 -0
- package/dist/radio.d.ts +5 -5
- package/dist/radio.js +8 -4
- package/dist/radius.js +1 -1
- package/dist/range-calendar.d.ts +19 -0
- package/dist/range-calendar.js +15 -0
- package/dist/scroll-area.d.ts +4 -1
- package/dist/scroll-area.js +3 -3
- package/dist/select.js +4 -14
- package/dist/selection.d.ts +18 -0
- package/dist/selection.js +28 -0
- package/dist/slider.js +1 -1
- package/dist/spacing.js +1 -1
- package/dist/surface.d.ts +1 -1
- package/dist/surface.js +22 -3
- package/dist/table.d.ts +78 -0
- package/dist/table.js +148 -0
- package/dist/tabs.d.ts +45 -0
- package/dist/tabs.js +103 -0
- package/dist/time-field.d.ts +22 -0
- package/dist/time-field.js +13 -0
- package/dist/toggle-indicator.js +1 -1
- package/dist/toolbar.d.ts +46 -0
- package/dist/toolbar.js +121 -0
- package/dist/tooltip.d.ts +0 -1
- package/dist/tooltip.js +0 -1
- package/dist/tree.d.ts +56 -0
- package/dist/tree.js +134 -0
- package/dist/window.d.ts +39 -0
- package/dist/{window-header.js → window.js} +57 -31
- package/package.json +4 -2
- package/dist/window-header.d.ts +0 -42
package/dist/material-paint.js
CHANGED
|
@@ -43,12 +43,28 @@ function DistortionFilter({ distortion, identity }) {
|
|
|
43
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" })] });
|
|
44
44
|
}
|
|
45
45
|
function grainImage(seed, amount) {
|
|
46
|
+
const key = `${seed}:${amount}`;
|
|
47
|
+
const cached = grainResources.get(key);
|
|
48
|
+
if (cached !== undefined) {
|
|
49
|
+
grainResources.delete(key);
|
|
50
|
+
grainResources.set(key, cached);
|
|
51
|
+
return cached;
|
|
52
|
+
}
|
|
46
53
|
const paths = grainPaths(seed, amount)
|
|
47
54
|
.map((path, tone) => path
|
|
48
55
|
? `<path d="${path}" fill="rgb(${toneChannel(tone)} ${toneChannel(tone)} ${toneChannel(tone)})"/>`
|
|
49
56
|
: "")
|
|
50
57
|
.join("");
|
|
51
|
-
|
|
58
|
+
const resource = `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>`)}")`;
|
|
59
|
+
// Preserve the exact texture while avoiding the same expensive SVG generation
|
|
60
|
+
// for every Surface. The bound prevents live grain controls from growing it forever.
|
|
61
|
+
if (grainResources.size >= grainResourceLimit) {
|
|
62
|
+
const oldest = grainResources.keys().next().value;
|
|
63
|
+
if (oldest !== undefined)
|
|
64
|
+
grainResources.delete(oldest);
|
|
65
|
+
}
|
|
66
|
+
grainResources.set(key, resource);
|
|
67
|
+
return resource;
|
|
52
68
|
}
|
|
53
69
|
/** Preserve the original contract: amount is the monotonic density of visible grain pixels. */
|
|
54
70
|
function grainPaths(seed, amount) {
|
|
@@ -99,3 +115,5 @@ function clamp(value, minimum, maximum) {
|
|
|
99
115
|
const patternSize = 64;
|
|
100
116
|
const toneCount = 16;
|
|
101
117
|
const grainSeeds = [19, 47, 83, 131];
|
|
118
|
+
const grainResourceLimit = 64;
|
|
119
|
+
const grainResources = new Map();
|
package/dist/menu.d.ts
CHANGED
|
@@ -2,18 +2,36 @@ import type { CSSProperties, ReactElement, RefAttributes } from "react";
|
|
|
2
2
|
import type { HeaderProps as AriaHeaderProps, MenuItemProps as AriaMenuItemProps, MenuProps as AriaMenuProps, MenuSectionProps as AriaMenuSectionProps, SeparatorProps as AriaSeparatorProps } from "react-aria-components";
|
|
3
3
|
import { type ControlColor } from "./control.js";
|
|
4
4
|
import type { ScaleLevel } from "./scale.js";
|
|
5
|
-
|
|
5
|
+
import { type MultipleStringSelection, type MultipleStringSelectionProps, type SingleStringSelectionProps } from "./selection.js";
|
|
6
|
+
type MenuRootBaseProps<T extends object> = Omit<AriaMenuProps<T>, "className" | "defaultSelectedKeys" | "disabledKeys" | "onAction" | "onSelectionChange" | "selectedKeys" | "selectionMode" | "style"> & Readonly<{
|
|
6
7
|
readonly className?: string;
|
|
7
8
|
readonly color?: ControlColor;
|
|
9
|
+
readonly disabledValues?: readonly string[];
|
|
10
|
+
readonly onItemAction?: (value: string) => void;
|
|
8
11
|
readonly size?: ScaleLevel;
|
|
9
12
|
readonly style?: CSSProperties;
|
|
10
|
-
}
|
|
13
|
+
}>;
|
|
14
|
+
export type MenuNoSelectionProps = Readonly<{
|
|
15
|
+
selectionMode?: "none";
|
|
16
|
+
value?: never;
|
|
17
|
+
defaultValue?: never;
|
|
18
|
+
onChange?: never;
|
|
19
|
+
}>;
|
|
20
|
+
export type MenuSingleSelectionProps = SingleStringSelectionProps & Readonly<{
|
|
21
|
+
selectionMode: "single";
|
|
22
|
+
}>;
|
|
23
|
+
export type MenuMultipleValue = MultipleStringSelection;
|
|
24
|
+
export type MenuMultipleSelectionProps = MultipleStringSelectionProps & Readonly<{
|
|
25
|
+
selectionMode: "multiple";
|
|
26
|
+
}>;
|
|
27
|
+
export type MenuRootProps<T extends object = object> = MenuRootBaseProps<T> & (MenuNoSelectionProps | MenuSingleSelectionProps | MenuMultipleSelectionProps);
|
|
11
28
|
export type MenuRootComponent = <T extends object = object>(properties: MenuRootProps<T> & RefAttributes<HTMLDivElement>) => ReactElement | null;
|
|
12
29
|
export declare const MenuRoot: MenuRootComponent;
|
|
13
|
-
export interface MenuItemProps<T = object> extends Omit<AriaMenuItemProps<T>, "className" | "isDisabled" | "style"> {
|
|
30
|
+
export interface MenuItemProps<T = object> extends Omit<AriaMenuItemProps<T>, "className" | "id" | "isDisabled" | "style"> {
|
|
14
31
|
readonly className?: string;
|
|
15
32
|
readonly color?: ControlColor;
|
|
16
33
|
readonly disabled?: boolean;
|
|
34
|
+
readonly id?: string;
|
|
17
35
|
readonly style?: CSSProperties;
|
|
18
36
|
}
|
|
19
37
|
export type MenuItemComponent = <T = object>(properties: MenuItemProps<T> & RefAttributes<HTMLDivElement>) => ReactElement | null;
|
|
@@ -26,10 +44,10 @@ export type MenuSeparatorProps = AriaSeparatorProps;
|
|
|
26
44
|
export declare const MenuSeparator: import("react").ForwardRefExoticComponent<AriaSeparatorProps & RefAttributes<HTMLElement>>;
|
|
27
45
|
/** A keyboard-navigable collection of commands or selectable options. */
|
|
28
46
|
export declare const Menu: MenuRootComponent & {
|
|
29
|
-
Root: MenuRootComponent;
|
|
30
47
|
Item: MenuItemComponent;
|
|
31
48
|
Section: <T>(props: AriaMenuSectionProps<T> & React.RefAttributes<HTMLElement>) => ReactElement<unknown, string | React.JSXElementConstructor<any>> | null;
|
|
32
49
|
Header: import("react").ForwardRefExoticComponent<AriaHeaderProps & RefAttributes<HTMLElement>>;
|
|
33
50
|
Separator: import("react").ForwardRefExoticComponent<AriaSeparatorProps & RefAttributes<HTMLElement>>;
|
|
34
51
|
};
|
|
35
52
|
export type MenuProps<T extends object = object> = MenuRootProps<T>;
|
|
53
|
+
export {};
|
package/dist/menu.js
CHANGED
|
@@ -3,23 +3,43 @@ 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
5
|
import { controlFontWeight, controlOpacity, useControlTheme } from "./control.js";
|
|
6
|
+
import { multipleSelection, singleSelection, stringKey, toAriaSelection } from "./selection.js";
|
|
6
7
|
const MenuThemeContext = createContext(null);
|
|
7
|
-
const MenuRootImplementation = forwardRef(function MenuRoot(
|
|
8
|
+
const MenuRootImplementation = forwardRef(function MenuRoot(properties, ref) {
|
|
9
|
+
const { className, color, defaultValue, disabledValues, onChange, onItemAction, selectionMode = "none", size = "medium", style, value, ...native } = properties;
|
|
8
10
|
const theme = useControlTheme({ color, size });
|
|
9
11
|
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: {
|
|
12
|
+
return _jsx(MenuThemeContext.Provider, { value: context, children: _jsx(AriaMenu, { ...native, ...selectionProperties(properties), ref: ref, className: className, disabledKeys: disabledValues, selectionMode: selectionMode === "none" ? undefined : selectionMode, onAction: onItemAction == null ? undefined : key => onItemAction(stringKey(key)), style: {
|
|
11
13
|
display: "grid",
|
|
12
14
|
gap: theme.gap,
|
|
13
15
|
minWidth: theme.height * 4,
|
|
14
16
|
padding: theme.gap,
|
|
15
17
|
boxSizing: "border-box",
|
|
16
18
|
outline: "none",
|
|
17
|
-
color: theme.foreground,
|
|
18
19
|
fontFamily: "inherit",
|
|
19
20
|
fontSize: theme.fontSize,
|
|
20
21
|
...style
|
|
21
22
|
} }) });
|
|
22
23
|
});
|
|
24
|
+
function selectionProperties(properties) {
|
|
25
|
+
const selectedKeys = toAriaSelection(properties.value);
|
|
26
|
+
const defaultSelectedKeys = toAriaSelection(properties.defaultValue);
|
|
27
|
+
const onSelectionChange = properties.onChange == null
|
|
28
|
+
? undefined
|
|
29
|
+
: (selection) => {
|
|
30
|
+
if (properties.selectionMode === "multiple") {
|
|
31
|
+
properties.onChange?.(multipleSelection(selection));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (properties.selectionMode === "single")
|
|
35
|
+
properties.onChange?.(singleSelection(selection));
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
...(selectedKeys !== undefined ? { selectedKeys } : {}),
|
|
39
|
+
...(defaultSelectedKeys !== undefined ? { defaultSelectedKeys } : {}),
|
|
40
|
+
...(onSelectionChange !== undefined ? { onSelectionChange } : {})
|
|
41
|
+
};
|
|
42
|
+
}
|
|
23
43
|
export const MenuRoot = MenuRootImplementation;
|
|
24
44
|
const MenuItemImplementation = forwardRef(function MenuItem({ className, color, disabled = false, style, ...properties }, ref) {
|
|
25
45
|
const inherited = useContext(MenuThemeContext);
|
|
@@ -37,7 +57,7 @@ function MenuItemView({ className, disabled, properties, ref, style, theme }) {
|
|
|
37
57
|
? theme.paints.palette.pressed
|
|
38
58
|
: state.isHovered || state.isSelected
|
|
39
59
|
? theme.paints.palette.hover
|
|
40
|
-
: { background: "transparent", color:
|
|
60
|
+
: { background: "transparent", color: "inherit" };
|
|
41
61
|
return {
|
|
42
62
|
...theme.transition,
|
|
43
63
|
display: "flex",
|
|
@@ -77,7 +97,7 @@ export const MenuSeparator = forwardRef(function MenuSeparator({ style, ...prope
|
|
|
77
97
|
height: 1,
|
|
78
98
|
marginBlock: theme.gap,
|
|
79
99
|
border: 0,
|
|
80
|
-
background: colorOpacity(theme.
|
|
100
|
+
background: colorOpacity(theme.tint, controlOpacity.separator),
|
|
81
101
|
...style
|
|
82
102
|
} });
|
|
83
103
|
});
|
|
@@ -89,7 +109,6 @@ function useMenuTheme() {
|
|
|
89
109
|
}
|
|
90
110
|
/** A keyboard-navigable collection of commands or selectable options. */
|
|
91
111
|
export const Menu = Object.assign(MenuRoot, {
|
|
92
|
-
Root: MenuRoot,
|
|
93
112
|
Item: MenuItem,
|
|
94
113
|
Section: MenuSection,
|
|
95
114
|
Header: MenuHeader,
|
package/dist/panel.d.ts
CHANGED
|
@@ -11,7 +11,6 @@ export type PanelContentProps = SurfaceProps;
|
|
|
11
11
|
export declare const PanelContent: import("react").ForwardRefExoticComponent<Omit<PanelContentProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
12
12
|
/** A material shell whose named parts remain independently composable. */
|
|
13
13
|
export declare const Panel: import("react").ForwardRefExoticComponent<Omit<PanelRootProps, "ref"> & import("react").RefAttributes<HTMLDivElement>> & {
|
|
14
|
-
Root: import("react").ForwardRefExoticComponent<Omit<PanelRootProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
15
14
|
Header: import("react").ForwardRefExoticComponent<PanelHeaderProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
16
15
|
Content: import("react").ForwardRefExoticComponent<Omit<PanelContentProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
17
16
|
};
|
package/dist/panel.js
CHANGED
|
@@ -36,7 +36,6 @@ export const PanelContent = forwardRef(function PanelContent({ style, ...propert
|
|
|
36
36
|
});
|
|
37
37
|
/** A material shell whose named parts remain independently composable. */
|
|
38
38
|
export const Panel = Object.assign(PanelRoot, {
|
|
39
|
-
Root: PanelRoot,
|
|
40
39
|
Header: PanelHeader,
|
|
41
40
|
Content: PanelContent
|
|
42
41
|
});
|
package/dist/popover.d.ts
CHANGED
|
@@ -20,7 +20,6 @@ export type PopoverCloseProps = ButtonProps;
|
|
|
20
20
|
export declare const PopoverClose: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
21
21
|
/** An anchored non-modal overlay with explicit trigger, surface, and dialog roles. */
|
|
22
22
|
export declare const Popover: typeof PopoverRoot & {
|
|
23
|
-
Root: typeof PopoverRoot;
|
|
24
23
|
Trigger: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
25
24
|
Content: import("react").ForwardRefExoticComponent<PopoverContentProps & import("react").RefAttributes<HTMLElement>>;
|
|
26
25
|
Dialog: import("react").ForwardRefExoticComponent<AriaDialogProps & import("react").RefAttributes<HTMLElement>>;
|
package/dist/popover.js
CHANGED
|
@@ -3,6 +3,7 @@ import { forwardRef } from "react";
|
|
|
3
3
|
import { Dialog as AriaDialog, DialogTrigger as AriaDialogTrigger, Heading as AriaHeading, Popover as AriaPopover } from "react-aria-components";
|
|
4
4
|
import { useResolvedAppearance } from "./appearance-context.js";
|
|
5
5
|
import { Button } from "./button.js";
|
|
6
|
+
import { controlFontSizes, controlFontWeight } from "./control.js";
|
|
6
7
|
import MotionStyle, { overlayMotionClass, overlayTransition } from "./motion-style.js";
|
|
7
8
|
import { scale } from "./scale.js";
|
|
8
9
|
import { Surface } from "./surface.js";
|
|
@@ -30,15 +31,24 @@ export const PopoverContent = forwardRef(function PopoverContent({ children, cla
|
|
|
30
31
|
export const PopoverDialog = forwardRef(function PopoverDialog({ style, ...properties }, ref) {
|
|
31
32
|
return _jsx(AriaDialog, { ...properties, ref: ref, style: { minWidth: 0, outline: "none", ...style } });
|
|
32
33
|
});
|
|
33
|
-
export const PopoverTitle = forwardRef(function PopoverTitle(properties, ref) {
|
|
34
|
-
return _jsx(AriaHeading, { ...properties, ref: ref, slot: "title"
|
|
34
|
+
export const PopoverTitle = forwardRef(function PopoverTitle({ style, ...properties }, ref) {
|
|
35
|
+
return _jsx(AriaHeading, { ...properties, ref: ref, slot: "title",
|
|
36
|
+
// Portalled headings cannot rely on the trigger subtree to neutralize
|
|
37
|
+
// browser heading defaults; this part owns its complete text treatment.
|
|
38
|
+
style: {
|
|
39
|
+
margin: 0,
|
|
40
|
+
fontFamily: "inherit",
|
|
41
|
+
fontSize: controlFontSizes.medium,
|
|
42
|
+
fontWeight: controlFontWeight,
|
|
43
|
+
lineHeight: 1.5,
|
|
44
|
+
...style
|
|
45
|
+
} });
|
|
35
46
|
});
|
|
36
47
|
export const PopoverClose = forwardRef(function PopoverClose(properties, ref) {
|
|
37
48
|
return _jsx(Button, { ...properties, ref: ref, slot: "close" });
|
|
38
49
|
});
|
|
39
50
|
/** An anchored non-modal overlay with explicit trigger, surface, and dialog roles. */
|
|
40
51
|
export const Popover = Object.assign(PopoverRoot, {
|
|
41
|
-
Root: PopoverRoot,
|
|
42
52
|
Trigger: PopoverTrigger,
|
|
43
53
|
Content: PopoverContent,
|
|
44
54
|
Dialog: PopoverDialog,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from "react";
|
|
2
|
+
import type { ProgressBarProps as AriaProgressBarProps } from "react-aria-components";
|
|
3
|
+
import type { ControlColor } from "./control.js";
|
|
4
|
+
import type { ScaleLevel } from "./scale.js";
|
|
5
|
+
type NativeProgressBarProps = Omit<AriaProgressBarProps, "children" | "className" | "isIndeterminate" | "style">;
|
|
6
|
+
export interface ProgressBarProps extends NativeProgressBarProps {
|
|
7
|
+
/** Native class name applied without replacing the component contract. */
|
|
8
|
+
readonly className?: string;
|
|
9
|
+
/** Color used for the completed or moving portion. */
|
|
10
|
+
readonly color?: ControlColor;
|
|
11
|
+
/** Displays progress without asserting a current value. */
|
|
12
|
+
readonly indeterminate?: boolean;
|
|
13
|
+
/** Visible label associated with the progress value. */
|
|
14
|
+
readonly label?: ReactNode;
|
|
15
|
+
/** Derives typography and track thickness from Appearance. */
|
|
16
|
+
readonly size?: ScaleLevel;
|
|
17
|
+
/** Native styles applied after ProgressBar defaults. */
|
|
18
|
+
readonly style?: CSSProperties;
|
|
19
|
+
}
|
|
20
|
+
/** Determinate or indeterminate progress for an operation over time. */
|
|
21
|
+
export declare const ProgressBar: import("react").ForwardRefExoticComponent<ProgressBarProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { Label, ProgressBar as AriaProgressBar } from "react-aria-components";
|
|
4
|
+
import { motion } from "motion/react";
|
|
5
|
+
import { controlFontWeight, controlOpacity, fieldStyle, useControlTheme } from "./control.js";
|
|
6
|
+
import { resolveDirection, useDirection } from "./direction.js";
|
|
7
|
+
/** Determinate or indeterminate progress for an operation over time. */
|
|
8
|
+
export const ProgressBar = forwardRef(function ProgressBar({ className, color = "default:base", indeterminate = false, label, size = "medium", style, value, minValue = 0, maxValue = 100, ...properties }, ref) {
|
|
9
|
+
const theme = useControlTheme({ color, size });
|
|
10
|
+
const direction = resolveDirection(properties.dir, useDirection());
|
|
11
|
+
const thickness = Math.max(2, Math.round(theme.spacing / 2));
|
|
12
|
+
const sweep = direction === "rtl" ? ["100%", "-350%"] : ["-100%", "350%"];
|
|
13
|
+
const duration = typeof theme.motionTransition.duration === "number"
|
|
14
|
+
? theme.motionTransition.duration * 10
|
|
15
|
+
: 1.2;
|
|
16
|
+
return _jsx(AriaProgressBar, { ...properties, ref: ref, className: className, value: value, minValue: minValue, maxValue: maxValue, isIndeterminate: indeterminate, style: {
|
|
17
|
+
...fieldStyle(theme, false),
|
|
18
|
+
width: "100%",
|
|
19
|
+
...style
|
|
20
|
+
}, children: state => {
|
|
21
|
+
const output = properties.valueLabel ?? state.valueText;
|
|
22
|
+
const showHeading = label != null || (!state.isIndeterminate && output != null);
|
|
23
|
+
const moving = state.isIndeterminate && theme.animations;
|
|
24
|
+
return _jsxs(_Fragment, { children: [showHeading && _jsxs("span", { style: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: theme.gap }, children: [label != null && _jsx(Label, { style: { fontWeight: controlFontWeight }, children: label }), !state.isIndeterminate && output != null && _jsx("span", { style: { fontVariantNumeric: "tabular-nums", opacity: controlOpacity.secondary }, children: output })] }), _jsx("span", { "data-progress-track": "", "aria-hidden": "true", style: {
|
|
25
|
+
position: "relative",
|
|
26
|
+
display: "block",
|
|
27
|
+
width: "100%",
|
|
28
|
+
height: thickness,
|
|
29
|
+
overflow: "hidden",
|
|
30
|
+
borderRadius: thickness,
|
|
31
|
+
background: theme.paints.subtle.rest.background
|
|
32
|
+
}, children: _jsx(motion.span, { "data-progress-fill": "", initial: false, animate: { x: moving ? sweep : "0%" }, transition: moving
|
|
33
|
+
? { ...theme.motionTransition, duration, repeat: Infinity }
|
|
34
|
+
: theme.motionTransition, style: {
|
|
35
|
+
...theme.transition,
|
|
36
|
+
position: "absolute",
|
|
37
|
+
insetBlock: 0,
|
|
38
|
+
insetInlineStart: state.isIndeterminate && !moving ? "30%" : 0,
|
|
39
|
+
display: "block",
|
|
40
|
+
width: state.isIndeterminate ? "40%" : `${state.percentage ?? 0}%`,
|
|
41
|
+
borderRadius: "inherit",
|
|
42
|
+
background: theme.paints.palette.rest.background,
|
|
43
|
+
transitionProperty: "width"
|
|
44
|
+
} }) })] });
|
|
45
|
+
} });
|
|
46
|
+
});
|
package/dist/radio.d.ts
CHANGED
|
@@ -12,10 +12,10 @@ export interface RadioGroupProps extends Omit<BaseRadioGroupProps<string | null>
|
|
|
12
12
|
readonly readOnly?: boolean;
|
|
13
13
|
readonly orientation?: "horizontal" | "vertical";
|
|
14
14
|
}
|
|
15
|
-
export interface
|
|
15
|
+
export interface RadioGroupItemProps extends Omit<BaseRadioProps<string>, "children" | "className" | "disabled" | "readOnly" | "required" | "style" | "value">, ControlProps, Pick<FieldProps, "label" | "description">, MaterialOverrides, ShadowOverrides {
|
|
16
16
|
readonly value: string;
|
|
17
17
|
}
|
|
18
|
-
/** One
|
|
19
|
-
export declare const RadioGroup: import("react").ForwardRefExoticComponent<Omit<RadioGroupProps, "ref"> & import("react").RefAttributes<HTMLDivElement
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
/** One exclusive-choice field whose context-dependent options are grouped as Items. */
|
|
19
|
+
export declare const RadioGroup: import("react").ForwardRefExoticComponent<Omit<RadioGroupProps, "ref"> & import("react").RefAttributes<HTMLDivElement>> & {
|
|
20
|
+
Item: import("react").ForwardRefExoticComponent<Omit<RadioGroupItemProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
21
|
+
};
|
package/dist/radio.js
CHANGED
|
@@ -8,8 +8,8 @@ import { controlFontWeight, controlOpacity, fieldStyle, useControlTheme } from "
|
|
|
8
8
|
import { ToggleIndicator, toggleStyle } from "./toggle-indicator.js";
|
|
9
9
|
import { resolveDirection, useDirection } from "./direction.js";
|
|
10
10
|
const RadioStyleContext = createContext(null);
|
|
11
|
-
/** One string value selected from its
|
|
12
|
-
|
|
11
|
+
/** One string value selected from its Items, with direction-aware arrow navigation. */
|
|
12
|
+
const RadioGroupRoot = forwardRef(function RadioGroupRoot({ label, description, errorMessage, children, disabled = false, readOnly = false, required = false, invalid = false, value, defaultValue, onChange, orientation = "vertical", size, color, style, className, material, shadow, "aria-describedby": ariaDescribedBy, "aria-labelledby": ariaLabelledBy, ...properties }, ref) {
|
|
13
13
|
const theme = useControlTheme({ size, color });
|
|
14
14
|
const direction = resolveDirection(properties.dir, useDirection());
|
|
15
15
|
const labelId = useId();
|
|
@@ -23,10 +23,10 @@ export const RadioGroup = forwardRef(function RadioGroup({ label, description, e
|
|
|
23
23
|
return _jsx(BaseDirectionProvider, { direction: direction, children: root });
|
|
24
24
|
});
|
|
25
25
|
/** An option in a RadioGroup. Selection and validation belong to the group. */
|
|
26
|
-
|
|
26
|
+
const RadioGroupItem = forwardRef(function RadioGroupItem({ label, description, value, disabled = false, size, color, style, className, material, shadow, onFocus, onBlur, "aria-describedby": ariaDescribedBy, ...properties }, ref) {
|
|
27
27
|
const inherited = useContext(RadioStyleContext);
|
|
28
28
|
if (inherited == null)
|
|
29
|
-
throw new Error("
|
|
29
|
+
throw new Error("RadioGroup.Item must be used inside RadioGroup");
|
|
30
30
|
const theme = useControlTheme({ size: size ?? inherited.size, color: color ?? inherited.color });
|
|
31
31
|
const descriptionId = useId();
|
|
32
32
|
const [hovered, setHovered] = useState(false);
|
|
@@ -41,3 +41,7 @@ export const Radio = forwardRef(function Radio({ label, description, value, disa
|
|
|
41
41
|
onBlur?.(event);
|
|
42
42
|
}, style: state => toggleStyle(theme, state.disabled, state.readOnly), render: (native, state) => _jsxs("span", { ...native, children: [_jsx(ToggleIndicator, { kind: "radio", material: material ?? inherited.material, shadow: shadow ?? inherited.shadow, theme: theme, selected: state.checked, focused: focusVisible, invalid: inherited.invalid, hovered: !state.disabled && !state.readOnly && hovered, pressed: !state.disabled && !state.readOnly && pressed }), label] }) }), description != null && _jsx("span", { id: descriptionId, style: { fontSize: "0.92em", opacity: controlOpacity.secondary }, children: description })] });
|
|
43
43
|
});
|
|
44
|
+
/** One exclusive-choice field whose context-dependent options are grouped as Items. */
|
|
45
|
+
export const RadioGroup = Object.assign(RadioGroupRoot, {
|
|
46
|
+
Item: RadioGroupItem
|
|
47
|
+
});
|
package/dist/radius.js
CHANGED
|
@@ -6,6 +6,6 @@ export function resolveRadius(value, appearance) {
|
|
|
6
6
|
if (!isScaleLevel(value))
|
|
7
7
|
return value;
|
|
8
8
|
if (!appearance)
|
|
9
|
-
throw new Error("A semantic radius requires
|
|
9
|
+
throw new Error("A semantic radius requires an Appearance value");
|
|
10
10
|
return scale(appearance.radius, value);
|
|
11
11
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CalendarDate } from "@internationalized/date";
|
|
2
|
+
import type { RangeCalendarProps as AriaRangeCalendarProps } from "react-aria-components";
|
|
3
|
+
import { type ControlOverrides, type ControlProps } from "./control.js";
|
|
4
|
+
import type { DateRange } from "./date-range.js";
|
|
5
|
+
import type { RadiusProps } from "./radius.js";
|
|
6
|
+
type RangeCalendarOverrides = ControlOverrides | "children" | "isReadOnly" | "isInvalid" | "value" | "defaultValue" | "onChange" | "minValue" | "maxValue" | "isDateUnavailable" | "visibleDuration" | "createCalendar" | "errorMessage" | "validationState";
|
|
7
|
+
export interface RangeCalendarProps extends Omit<AriaRangeCalendarProps<CalendarDate>, RangeCalendarOverrides>, ControlProps, RadiusProps {
|
|
8
|
+
readonly value?: DateRange | null;
|
|
9
|
+
readonly defaultValue?: DateRange | null;
|
|
10
|
+
readonly onChange?: (value: DateRange) => void;
|
|
11
|
+
readonly minValue?: CalendarDate | null;
|
|
12
|
+
readonly maxValue?: CalendarDate | null;
|
|
13
|
+
readonly isDateUnavailable?: (date: CalendarDate, anchorDate: CalendarDate | null) => boolean;
|
|
14
|
+
readonly readOnly?: boolean;
|
|
15
|
+
readonly invalid?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** A locale-aware month view for selecting an inclusive date range. */
|
|
18
|
+
export declare const RangeCalendar: import("react").ForwardRefExoticComponent<RangeCalendarProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { RangeCalendar as AriaRangeCalendar } from "react-aria-components";
|
|
4
|
+
import { AriaDirectionBoundary } from "./aria-direction.js";
|
|
5
|
+
import { CalendarLayout, calendarRootStyle } from "./calendar-layout.js";
|
|
6
|
+
import { useControlTheme } from "./control.js";
|
|
7
|
+
import { resolveDirection, useDirection } from "./direction.js";
|
|
8
|
+
/** A locale-aware month view for selecting an inclusive date range. */
|
|
9
|
+
export const RangeCalendar = forwardRef(function RangeCalendar({ disabled, readOnly, invalid, size, color, radius, style, className, isDateUnavailable, ...properties }, ref) {
|
|
10
|
+
const theme = useControlTheme({ size, color, radius });
|
|
11
|
+
const direction = resolveDirection(properties.dir, useDirection());
|
|
12
|
+
return _jsx(AriaDirectionBoundary, { direction: direction, children: _jsx(AriaRangeCalendar, { ...properties, ref: ref, dir: direction, className: className, isDisabled: disabled, isReadOnly: readOnly, isInvalid: invalid, isDateUnavailable: isDateUnavailable == null
|
|
13
|
+
? undefined
|
|
14
|
+
: (date, anchorDate) => isDateUnavailable(date, anchorDate), style: calendarRootStyle(theme, disabled, style), children: _jsx(CalendarLayout, { color: color, range: true, size: size, theme: theme }) }) });
|
|
15
|
+
});
|
package/dist/scroll-area.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { ComponentPropsWithoutRef, Ref, ReactNode, UIEventHandler } from "react";
|
|
2
|
+
import type { ControlColor } from "./control.js";
|
|
2
3
|
export type ScrollAreaAxis = "vertical" | "horizontal" | "both";
|
|
3
|
-
export interface ScrollAreaProps extends Omit<ComponentPropsWithoutRef<"div">, "children" | "onScroll"> {
|
|
4
|
+
export interface ScrollAreaProps extends Omit<ComponentPropsWithoutRef<"div">, "children" | "color" | "onScroll"> {
|
|
4
5
|
readonly axis?: ScrollAreaAxis;
|
|
5
6
|
readonly children?: ReactNode;
|
|
7
|
+
/** Base color from which scrollbar states are derived. */
|
|
8
|
+
readonly color?: ControlColor;
|
|
6
9
|
readonly onScroll?: UIEventHandler<HTMLDivElement>;
|
|
7
10
|
readonly viewportRef?: Ref<HTMLDivElement>;
|
|
8
11
|
}
|
package/dist/scroll-area.js
CHANGED
|
@@ -3,15 +3,15 @@ import { ScrollArea as BaseScrollArea } from "@base-ui/react/scroll-area";
|
|
|
3
3
|
import { DirectionProvider as BaseDirectionProvider } from "@base-ui/react/direction-provider";
|
|
4
4
|
import { forwardRef, useState } from "react";
|
|
5
5
|
import { useResolvedAppearance } from "./appearance-context.js";
|
|
6
|
-
import { colorOpacity } from "./color.js";
|
|
6
|
+
import { colorOpacity, resolveColor } from "./color.js";
|
|
7
7
|
import { transitionTiming } from "./motion-style.js";
|
|
8
8
|
import { resolveDirection, useDirection } from "./direction.js";
|
|
9
9
|
/** A native scrolling viewport with locally owned React UI scrollbars. */
|
|
10
|
-
export const ScrollArea = forwardRef(function ScrollArea({ axis = "vertical", children, onScroll, style, viewportRef, ...properties }, ref) {
|
|
10
|
+
export const ScrollArea = forwardRef(function ScrollArea({ axis = "vertical", children, color, onScroll, style, viewportRef, ...properties }, ref) {
|
|
11
11
|
const resolved = useResolvedAppearance();
|
|
12
12
|
const { appearance } = resolved;
|
|
13
13
|
const direction = resolveDirection(properties.dir, useDirection());
|
|
14
|
-
const foreground = resolved.colors
|
|
14
|
+
const foreground = resolveColor(color ?? "foreground:base", resolved.colors);
|
|
15
15
|
const radius = Math.min(appearance.radius, 8);
|
|
16
16
|
const thickness = Math.max(8, appearance.spacing);
|
|
17
17
|
const inset = Math.max(2, thickness / 3);
|
package/dist/select.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useMemo } from "react";
|
|
3
|
-
import { Select as AriaSelect, Button, SelectValue
|
|
3
|
+
import { Select as AriaSelect, Button, SelectValue } from "react-aria-components";
|
|
4
4
|
import { controlOpacity, controlPaint, controlStyle, FieldFeedback, FieldLabel, fieldStyle, useControlTheme } from "./control.js";
|
|
5
5
|
import { SurfaceButton } from "./control-surface.js";
|
|
6
6
|
import { ScrollArea } from "./scroll-area.js";
|
|
7
7
|
import { PopoverContent } from "./popover.js";
|
|
8
8
|
import { resolveDirection, useDirection } from "./direction.js";
|
|
9
|
+
import { ListBox } from "./list-box.js";
|
|
9
10
|
/** Single selection from string-valued options; keyboard navigation and typeahead stay native to React Aria. */
|
|
10
11
|
export const Select = forwardRef(function Select({ label, description, errorMessage, disabled, required, invalid, options, value, defaultValue, onChange, size, color, radius, style, material, shadow, ...properties }, ref) {
|
|
11
12
|
const theme = useControlTheme({ size, color, radius });
|
|
@@ -18,19 +19,8 @@ export const Select = forwardRef(function Select({ label, description, errorMess
|
|
|
18
19
|
}), children: [_jsx(SelectValue, { style: ({ isPlaceholder }) => ({ opacity: isPlaceholder ? controlOpacity.placeholder : 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }) }), _jsx("svg", { "aria-hidden": "true", width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", style: { ...theme.transition, transitionProperty: "transform", flexShrink: 0, transform: `rotate(${state.isOpen ? 180 : 0}deg)` }, children: _jsx("path", { d: "m2 4 4 4 4-4" }) })] }), _jsx(PopoverContent, { dir: direction, placement: "bottom start", offset: theme.gap, style: {
|
|
19
20
|
width: "var(--trigger-width)",
|
|
20
21
|
maxHeight: `min(calc(100vh - ${theme.spacing * 2}px), ${theme.height * 8}px)`,
|
|
21
|
-
padding:
|
|
22
|
+
padding: 0,
|
|
22
23
|
display: "flex",
|
|
23
24
|
flexDirection: "column"
|
|
24
|
-
}, children: _jsx(ScrollArea, { style: { flex: "1 1 auto", minHeight: 0 }, children: _jsx(ListBox, {
|
|
25
|
-
...theme.transition,
|
|
26
|
-
display: "flex", alignItems: "center", justifyContent: "space-between", gap: theme.gap,
|
|
27
|
-
minHeight: theme.height, paddingInline: Math.max(8, theme.spacing), boxSizing: "border-box",
|
|
28
|
-
borderRadius: theme.radius,
|
|
29
|
-
outline: item.isFocusVisible && !item.isSelected ? `1px solid ${theme.focusColor}` : "none", outlineOffset: 1,
|
|
30
|
-
cursor: item.isDisabled ? "not-allowed" : "pointer",
|
|
31
|
-
opacity: item.isDisabled ? controlOpacity.disabled : 1,
|
|
32
|
-
...(item.isSelected
|
|
33
|
-
? item.isPressed ? theme.paints.palette.pressed : item.isHovered ? theme.paints.palette.hover : theme.paints.palette.rest
|
|
34
|
-
: item.isHovered ? theme.paints.neutral.hover : { background: "transparent", color: theme.foreground })
|
|
35
|
-
}), children: item => _jsxs(_Fragment, { children: [option.label, _jsx("span", { "aria-hidden": "true", children: item.isSelected ? "✓" : null })] }) }, option.value)) }) }) }), _jsx(FieldFeedback, { theme: theme, description: description, errorMessage: errorMessage })] }) });
|
|
25
|
+
}, children: _jsx(ScrollArea, { style: { flex: "1 1 auto", minHeight: 0 }, children: _jsx(ListBox, { color: color, radius: radius, size: size, children: options.map(option => _jsx(ListBox.Item, { id: option.value, textValue: option.label, disabled: option.disabled, children: option.label }, option.value)) }) }) }), _jsx(FieldFeedback, { theme: theme, description: description, errorMessage: errorMessage })] }) });
|
|
36
26
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Key, Selection } from "react-aria-components";
|
|
2
|
+
export type MultipleStringSelection = readonly string[] | "all";
|
|
3
|
+
export type SingleStringSelectionProps = Readonly<{
|
|
4
|
+
value?: string | null;
|
|
5
|
+
defaultValue?: string | null;
|
|
6
|
+
onChange?: (value: string | null) => void;
|
|
7
|
+
}>;
|
|
8
|
+
export type MultipleStringSelectionProps = Readonly<{
|
|
9
|
+
value?: MultipleStringSelection;
|
|
10
|
+
defaultValue?: MultipleStringSelection;
|
|
11
|
+
onChange?: (value: MultipleStringSelection) => void;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function toAriaSelection(value: string | null | MultipleStringSelection | undefined): "all" | Set<string> | undefined;
|
|
14
|
+
export declare function stringKey(value: Key): string;
|
|
15
|
+
export declare function optionalStringKey(value: Key | null): string | null;
|
|
16
|
+
export declare function singleSelection(selection: Selection): string | null;
|
|
17
|
+
export declare function multipleSelection(selection: Selection): MultipleStringSelection;
|
|
18
|
+
export declare function stringKeys(values: Iterable<Key>): readonly string[];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function toAriaSelection(value) {
|
|
2
|
+
if (value === undefined)
|
|
3
|
+
return undefined;
|
|
4
|
+
if (value === "all")
|
|
5
|
+
return "all";
|
|
6
|
+
if (value === null)
|
|
7
|
+
return new Set();
|
|
8
|
+
if (typeof value === "string")
|
|
9
|
+
return new Set([value]);
|
|
10
|
+
return new Set(value);
|
|
11
|
+
}
|
|
12
|
+
export function stringKey(value) {
|
|
13
|
+
if (typeof value !== "string")
|
|
14
|
+
throw new Error("Collection identities must be strings");
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
export function optionalStringKey(value) {
|
|
18
|
+
return value === null ? null : stringKey(value);
|
|
19
|
+
}
|
|
20
|
+
export function singleSelection(selection) {
|
|
21
|
+
return selection === "all" ? null : optionalStringKey(selection.values().next().value ?? null);
|
|
22
|
+
}
|
|
23
|
+
export function multipleSelection(selection) {
|
|
24
|
+
return selection === "all" ? "all" : [...selection].map(stringKey);
|
|
25
|
+
}
|
|
26
|
+
export function stringKeys(values) {
|
|
27
|
+
return [...values].map(stringKey);
|
|
28
|
+
}
|
package/dist/slider.js
CHANGED
|
@@ -46,7 +46,7 @@ export const Slider = forwardRef(function Slider({ label, description, name, for
|
|
|
46
46
|
width: vertical ? rail : "100%",
|
|
47
47
|
height: vertical ? "100%" : rail,
|
|
48
48
|
borderRadius: rail,
|
|
49
|
-
background: theme.paints.
|
|
49
|
+
background: theme.paints.subtle.rest.background
|
|
50
50
|
}, children: [_jsx(BaseSlider.Indicator, { style: {
|
|
51
51
|
...theme.transition,
|
|
52
52
|
borderRadius: rail,
|
package/dist/spacing.js
CHANGED
|
@@ -3,6 +3,6 @@ export function resolveSpacing(value, appearance) {
|
|
|
3
3
|
if (!isScaleLevel(value))
|
|
4
4
|
return value;
|
|
5
5
|
if (!appearance)
|
|
6
|
-
throw new Error("Semantic spacing requires
|
|
6
|
+
throw new Error("Semantic spacing requires an Appearance value");
|
|
7
7
|
return scale(appearance.spacing, value);
|
|
8
8
|
}
|
package/dist/surface.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { type MaterialOverrides } from "./material-options.js";
|
|
|
4
4
|
import { type RadiusProps } from "./radius.js";
|
|
5
5
|
import { type ShadowOverrides } from "./shadow-options.js";
|
|
6
6
|
import type { Direction } from "./direction.js";
|
|
7
|
-
export type { MaterialOptions } from "./material-options.js";
|
|
7
|
+
export type { MaterialMode, MaterialOptions } from "./material-options.js";
|
|
8
8
|
export type { ShadowOptions } from "./shadow-options.js";
|
|
9
9
|
export interface SurfaceOwnProps extends MaterialOverrides, ShadowOverrides, RadiusProps {
|
|
10
10
|
readonly color?: Color;
|
package/dist/surface.js
CHANGED
|
@@ -35,15 +35,34 @@ const SurfaceRoot = forwardRef(function Surface({ as: Element = "div", color, ma
|
|
|
35
35
|
});
|
|
36
36
|
export const Surface = SurfaceRoot;
|
|
37
37
|
function resolveSurface(color, options, resolved) {
|
|
38
|
+
const mode = typeof options === "object" ? "full" : options ?? "basic";
|
|
39
|
+
const level = materialLevel[mode];
|
|
38
40
|
const material = resolveMaterialOptions(typeof options === "object" ? options : {}, resolved.material);
|
|
39
41
|
const fill = resolveColor(color, resolved.colors);
|
|
42
|
+
const opacity = level >= materialLevel.extended ? material.opacity : 1;
|
|
43
|
+
const backdrop = level >= materialLevel.full;
|
|
44
|
+
const grain = level === materialLevel.basic
|
|
45
|
+
? material.grain * material.opacity
|
|
46
|
+
: material.grain;
|
|
40
47
|
return {
|
|
41
48
|
...material,
|
|
42
|
-
enabled:
|
|
49
|
+
enabled: level >= materialLevel.basic,
|
|
50
|
+
opacity,
|
|
51
|
+
fillOpacity: opacity,
|
|
52
|
+
grain,
|
|
53
|
+
backdrop: backdrop ? material.backdrop : 0,
|
|
54
|
+
distortion: backdrop ? material.distortion : 0,
|
|
55
|
+
saturation: backdrop ? material.saturation : 1,
|
|
43
56
|
color: fill,
|
|
44
57
|
foreground: contrastingColor(color ?? defaultColor, resolved.colors)
|
|
45
58
|
};
|
|
46
59
|
}
|
|
60
|
+
const materialLevel = Object.freeze({
|
|
61
|
+
none: 0,
|
|
62
|
+
basic: 1,
|
|
63
|
+
extended: 2,
|
|
64
|
+
full: 3
|
|
65
|
+
});
|
|
47
66
|
const layerStyle = {
|
|
48
67
|
position: "absolute",
|
|
49
68
|
inset: 0,
|
|
@@ -51,7 +70,7 @@ const layerStyle = {
|
|
|
51
70
|
pointerEvents: "none"
|
|
52
71
|
};
|
|
53
72
|
function SurfaceLayers({ material, paintTransition }) {
|
|
54
|
-
const filtersVisible = material.
|
|
73
|
+
const filtersVisible = material.fillOpacity < 1;
|
|
55
74
|
const backdrop = filtersVisible ? material.backdrop : 0;
|
|
56
75
|
const saturation = filtersVisible ? material.saturation : 1;
|
|
57
76
|
const distortion = filtersVisible ? material.distortion : 0;
|
|
@@ -60,7 +79,7 @@ function SurfaceLayers({ material, paintTransition }) {
|
|
|
60
79
|
saturation === 1 ? "" : `saturate(${saturation})`
|
|
61
80
|
].filter(Boolean).join(" ");
|
|
62
81
|
const identity = `phresh-material-${useId().replaceAll(":", "")}`;
|
|
63
|
-
return _jsxs("span", { "data-material": "", "aria-hidden": "true", style: layerStyle, children: [distortion > 0 && _jsx(BackdropLayer, { name: "refraction", filter: `url("#${identity}-distortion")`, zIndex: -3 }), frost && _jsx(BackdropLayer, { name: "frost", filter: frost, zIndex: -2 }), _jsx(MaterialPaint, { identity: identity, distortion: distortion, color: material.color, grain: material.grain, grainAmount: material.grainAmount, opacity: material.
|
|
82
|
+
return _jsxs("span", { "data-material": "", "aria-hidden": "true", style: layerStyle, children: [distortion > 0 && _jsx(BackdropLayer, { name: "refraction", filter: `url("#${identity}-distortion")`, zIndex: -3 }), frost && _jsx(BackdropLayer, { name: "frost", filter: frost, zIndex: -2 }), _jsx(MaterialPaint, { identity: identity, distortion: distortion, color: material.color, grain: material.grain, grainAmount: material.grainAmount, opacity: material.fillOpacity, transition: paintTransition })] });
|
|
64
83
|
}
|
|
65
84
|
/** Keep refraction and native frost in independent compositor passes. */
|
|
66
85
|
function BackdropLayer({ filter, name, zIndex }) {
|