@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/motion-style.d.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import type { CSSProperties } from "react";
|
|
2
2
|
import type { Transition } from "motion/react";
|
|
3
|
-
|
|
4
|
-
export declare function
|
|
5
|
-
/** CSS timing for SVG paint owned by a Material. */
|
|
6
|
-
export declare function usePaintTransition(): CSSProperties;
|
|
7
|
-
/** Motion timing for interactive geometry owned by React UI. */
|
|
8
|
-
export declare function useControlTransition(): Transition;
|
|
3
|
+
import type { AppearanceTransaction } from "./appearance.js";
|
|
4
|
+
export declare function controlTransition(transaction: AppearanceTransaction, animations: boolean): Transition;
|
|
9
5
|
/** Per-overlay variables consumed by the shared entrance keyframes. */
|
|
10
|
-
export declare function
|
|
6
|
+
export declare function overlayTransition(transaction: AppearanceTransaction, animations: boolean): CSSProperties;
|
|
11
7
|
export declare const overlayMotionClass = "phreshos-ui-overlay";
|
|
12
8
|
export declare const backdropMotionClass = "phreshos-ui-backdrop";
|
|
13
|
-
export declare function
|
|
9
|
+
export declare function transitionTiming(transaction: AppearanceTransaction, animations: boolean): CSSProperties;
|
|
10
|
+
export declare function visualTransition(transaction: AppearanceTransaction, animations: boolean): CSSProperties;
|
|
11
|
+
export declare function paintTransition(transaction: AppearanceTransaction, animations: boolean): CSSProperties;
|
|
14
12
|
/** React hoists and deduplicates this stylesheet, including portals. */
|
|
15
13
|
export default function MotionStyle(): import("react").JSX.Element;
|
package/dist/motion-style.js
CHANGED
|
@@ -1,45 +1,34 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { defaultAppearance } from "@phreshos/core";
|
|
3
|
-
import { useAppearance, usePreferences } from "./appearance-context.js";
|
|
4
2
|
const visualProperties = "background-color, color, border-color, border-radius, box-shadow";
|
|
5
3
|
const paintProperties = "fill, stroke, opacity";
|
|
6
|
-
|
|
7
|
-
export function useVisualTransition() {
|
|
8
|
-
return { ...useTransitionTiming(), transitionProperty: visualProperties };
|
|
9
|
-
}
|
|
10
|
-
/** CSS timing for SVG paint owned by a Material. */
|
|
11
|
-
export function usePaintTransition() {
|
|
12
|
-
return { ...useTransitionTiming(), transitionProperty: paintProperties };
|
|
13
|
-
}
|
|
14
|
-
/** Motion timing for interactive geometry owned by React UI. */
|
|
15
|
-
export function useControlTransition() {
|
|
16
|
-
const { duration, easing } = useAppearance().transaction;
|
|
17
|
-
const { animations } = usePreferences();
|
|
4
|
+
export function controlTransition(transaction, animations) {
|
|
18
5
|
return {
|
|
19
6
|
type: "tween",
|
|
20
|
-
duration: animations ? duration / 1_000 : 0,
|
|
21
|
-
ease: motionEasing(easing)
|
|
7
|
+
duration: animations ? transaction.duration / 1_000 : 0,
|
|
8
|
+
ease: motionEasing(transaction.easing)
|
|
22
9
|
};
|
|
23
10
|
}
|
|
24
11
|
/** Per-overlay variables consumed by the shared entrance keyframes. */
|
|
25
|
-
export function
|
|
26
|
-
const { duration, easing } = useAppearance().transaction;
|
|
27
|
-
const { animations } = usePreferences();
|
|
12
|
+
export function overlayTransition(transaction, animations) {
|
|
28
13
|
return {
|
|
29
|
-
"--phreshos-ui-motion-duration": `${animations ? duration : 0}ms`,
|
|
30
|
-
"--phreshos-ui-motion-easing": cssEasing(easing)
|
|
14
|
+
"--phreshos-ui-motion-duration": `${animations ? transaction.duration : 0}ms`,
|
|
15
|
+
"--phreshos-ui-motion-easing": cssEasing(transaction.easing)
|
|
31
16
|
};
|
|
32
17
|
}
|
|
33
18
|
export const overlayMotionClass = "phreshos-ui-overlay";
|
|
34
19
|
export const backdropMotionClass = "phreshos-ui-backdrop";
|
|
35
|
-
export function
|
|
36
|
-
const { duration, easing } = useAppearance().transaction;
|
|
37
|
-
const { animations } = usePreferences();
|
|
20
|
+
export function transitionTiming(transaction, animations) {
|
|
38
21
|
return {
|
|
39
|
-
transitionDuration: `${animations ? duration : 0}ms`,
|
|
40
|
-
transitionTimingFunction: cssEasing(easing)
|
|
22
|
+
transitionDuration: `${animations ? transaction.duration : 0}ms`,
|
|
23
|
+
transitionTimingFunction: cssEasing(transaction.easing)
|
|
41
24
|
};
|
|
42
25
|
}
|
|
26
|
+
export function visualTransition(transaction, animations) {
|
|
27
|
+
return { ...transitionTiming(transaction, animations), transitionProperty: visualProperties };
|
|
28
|
+
}
|
|
29
|
+
export function paintTransition(transaction, animations) {
|
|
30
|
+
return { ...transitionTiming(transaction, animations), transitionProperty: paintProperties };
|
|
31
|
+
}
|
|
43
32
|
function cssEasing(easing) {
|
|
44
33
|
return typeof easing === "string" ? easing : `cubic-bezier(${easing.join(", ")})`;
|
|
45
34
|
}
|
|
@@ -57,11 +46,6 @@ function motionEasing(easing) {
|
|
|
57
46
|
return easing;
|
|
58
47
|
}
|
|
59
48
|
const stylesheet = `
|
|
60
|
-
:root {
|
|
61
|
-
--phreshos-ui-motion-duration: ${defaultAppearance.transaction.duration}ms;
|
|
62
|
-
--phreshos-ui-motion-easing: ${cssEasing(defaultAppearance.transaction.easing)};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
49
|
.phreshos-ui-overlay[data-entering] {
|
|
66
50
|
animation: phreshos-ui-overlay-enter var(--phreshos-ui-motion-duration) var(--phreshos-ui-motion-easing) both;
|
|
67
51
|
}
|
package/dist/popover.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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, Popover as AriaPopover } from "react-aria-components";
|
|
4
|
-
import {
|
|
4
|
+
import { useResolvedAppearance } from "./appearance-context.js";
|
|
5
5
|
import { Button } from "./button.js";
|
|
6
|
-
import { overlayMotionClass,
|
|
6
|
+
import MotionStyle, { overlayMotionClass, overlayTransition } from "./motion-style.js";
|
|
7
7
|
import { scale } from "./scale.js";
|
|
8
8
|
import { Surface } from "./surface.js";
|
|
9
9
|
import { resolveDirection, useDirection } from "./direction.js";
|
|
@@ -15,16 +15,17 @@ export const PopoverTrigger = forwardRef(function PopoverTrigger(properties, ref
|
|
|
15
15
|
return _jsx(Button, { ...properties, ref: ref });
|
|
16
16
|
});
|
|
17
17
|
export const PopoverContent = forwardRef(function PopoverContent({ children, className, color, material, radius, shadow, style, offset, placement = "bottom", ...properties }, ref) {
|
|
18
|
-
const
|
|
19
|
-
const
|
|
18
|
+
const resolved = useResolvedAppearance();
|
|
19
|
+
const inset = scale(resolved.appearance.spacing, "small");
|
|
20
|
+
const transition = overlayTransition(resolved.transaction, resolved.preferences.animations);
|
|
20
21
|
const direction = resolveDirection(properties.dir, useDirection());
|
|
21
|
-
return _jsx(AriaPopover, { ...properties, ref: ref, dir: direction, offset: offset ?? inset, placement: resolveDirectionalPlacement(placement, direction), className: overlayMotionClass, style: transition, children: _jsx(Surface, { dir: direction, className: className, color: color, material: material, radius: radius, shadow: shadow, style: {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
return _jsxs(_Fragment, { children: [_jsx(MotionStyle, {}), _jsx(AriaPopover, { ...properties, ref: ref, dir: direction, offset: offset ?? inset, placement: resolveDirectionalPlacement(placement, direction), className: overlayMotionClass, style: transition, children: _jsx(Surface, { dir: direction, className: className, color: color, material: material, radius: radius, shadow: shadow, style: {
|
|
23
|
+
boxSizing: "border-box",
|
|
24
|
+
maxWidth: `calc(100vw - ${inset * 2}px)`,
|
|
25
|
+
maxHeight: `calc(100vh - ${inset * 2}px)`,
|
|
26
|
+
outline: "none",
|
|
27
|
+
...style
|
|
28
|
+
}, children: children }) })] });
|
|
28
29
|
});
|
|
29
30
|
export const PopoverDialog = forwardRef(function PopoverDialog({ style, ...properties }, ref) {
|
|
30
31
|
return _jsx(AriaDialog, { ...properties, ref: ref, style: { minWidth: 0, outline: "none", ...style } });
|
|
File without changes
|
package/dist/radio.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Base UI accepts direction directly; React Aria derives these keys from locale.
|
|
2
3
|
import { Radio as BaseRadio } from "@base-ui/react/radio";
|
|
3
4
|
import { RadioGroup as BaseRadioGroup } from "@base-ui/react/radio-group";
|
|
4
5
|
import { DirectionProvider as BaseDirectionProvider } from "@base-ui/react/direction-provider";
|
|
5
6
|
import { createContext, forwardRef, useContext, useId, useState } from "react";
|
|
6
|
-
import { fieldStyle, useControlTheme } from "./control.js";
|
|
7
|
+
import { controlFontWeight, controlOpacity, fieldStyle, useControlTheme } from "./control.js";
|
|
7
8
|
import { ToggleIndicator, toggleStyle } from "./toggle-indicator.js";
|
|
8
9
|
import { resolveDirection, useDirection } from "./direction.js";
|
|
9
10
|
const RadioStyleContext = createContext(null);
|
|
@@ -18,7 +19,7 @@ export const RadioGroup = forwardRef(function RadioGroup({ label, description, e
|
|
|
18
19
|
const root = _jsxs(BaseRadioGroup, { ...properties, ref: ref, className: className, value: value, defaultValue: defaultValue, onValueChange: next => {
|
|
19
20
|
if (next != null)
|
|
20
21
|
onChange?.(next);
|
|
21
|
-
}, disabled: disabled, readOnly: readOnly, required: required, "aria-invalid": invalid || undefined, "aria-labelledby": ariaLabelledBy ?? (label != null ? labelId : undefined), "aria-describedby": describedBy, style: fieldStyle(theme, disabled, style), children: [label != null && _jsx("div", { id: labelId, style: { fontWeight:
|
|
22
|
+
}, disabled: disabled, readOnly: readOnly, required: required, "aria-invalid": invalid || undefined, "aria-labelledby": ariaLabelledBy ?? (label != null ? labelId : undefined), "aria-describedby": describedBy, style: fieldStyle(theme, disabled, style), children: [label != null && _jsx("div", { id: labelId, style: { fontWeight: controlFontWeight }, children: label }), _jsx(RadioStyleContext.Provider, { value: { size, color, disabled, invalid, readOnly, material, shadow }, children: _jsx("div", { style: { display: "flex", flexDirection: orientation === "vertical" ? "column" : "row", gap: theme.gap, flexWrap: "wrap" }, children: children }) }), description != null && _jsx("span", { id: descriptionId, style: { fontSize: "0.92em", opacity: controlOpacity.secondary }, children: description }), invalid && errorMessage != null && _jsx("span", { id: errorId, style: { fontSize: "0.92em", color: theme.danger }, children: errorMessage })] });
|
|
22
23
|
return _jsx(BaseDirectionProvider, { direction: direction, children: root });
|
|
23
24
|
});
|
|
24
25
|
/** An option in a RadioGroup. Selection and validation belong to the group. */
|
|
@@ -38,5 +39,5 @@ export const Radio = forwardRef(function Radio({ label, description, value, disa
|
|
|
38
39
|
}, onBlur: event => {
|
|
39
40
|
setFocusVisible(false);
|
|
40
41
|
onBlur?.(event);
|
|
41
|
-
}, 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:
|
|
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 })] });
|
|
42
43
|
});
|
package/dist/radius.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 ScaleLevel } from "./scale.js";
|
|
4
4
|
/** An Appearance-derived level, fully rounded shape, pixel value, or explicit CSS corner radius. */
|
|
5
5
|
export type Radius = ScaleLevel | "full" | number | (string & {});
|
package/dist/scroll-area.js
CHANGED
|
@@ -2,43 +2,50 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
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
|
-
import {
|
|
5
|
+
import { useResolvedAppearance } from "./appearance-context.js";
|
|
6
6
|
import { colorOpacity } from "./color.js";
|
|
7
|
-
import {
|
|
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
10
|
export const ScrollArea = forwardRef(function ScrollArea({ axis = "vertical", children, onScroll, style, viewportRef, ...properties }, ref) {
|
|
11
|
-
const
|
|
11
|
+
const resolved = useResolvedAppearance();
|
|
12
|
+
const { appearance } = resolved;
|
|
12
13
|
const direction = resolveDirection(properties.dir, useDirection());
|
|
13
|
-
const foreground =
|
|
14
|
+
const foreground = resolved.colors.foreground;
|
|
14
15
|
const radius = Math.min(appearance.radius, 8);
|
|
16
|
+
const thickness = Math.max(8, appearance.spacing);
|
|
17
|
+
const inset = Math.max(2, thickness / 3);
|
|
15
18
|
const vertical = axis === "vertical" || axis === "both";
|
|
16
19
|
const horizontal = axis === "horizontal" || axis === "both";
|
|
20
|
+
const transition = transitionTiming(resolved.transaction, resolved.preferences.animations);
|
|
17
21
|
const root = _jsxs(BaseScrollArea.Root, { ...properties, "data-phreshos-scroll-area": "", ref: ref, style: {
|
|
18
22
|
...style,
|
|
23
|
+
display: "flex",
|
|
24
|
+
flexDirection: "column",
|
|
19
25
|
position: style?.position ?? "relative",
|
|
20
26
|
minWidth: style?.minWidth ?? 0,
|
|
21
27
|
minHeight: style?.minHeight ?? 0,
|
|
22
28
|
overflow: "hidden"
|
|
23
29
|
}, children: [_jsx(BaseScrollArea.Viewport, { "data-phreshos-scroll-area-viewport": "", ref: viewportRef, onScroll: onScroll, style: {
|
|
30
|
+
flex: "1 1 auto",
|
|
31
|
+
minHeight: 0,
|
|
24
32
|
width: "100%",
|
|
25
33
|
height: "100%",
|
|
26
34
|
overflowX: horizontal ? "scroll" : "hidden",
|
|
27
35
|
overflowY: vertical ? "scroll" : "hidden"
|
|
28
|
-
}, children: _jsx(BaseScrollArea.Content, { "data-phreshos-scroll-area-content": "", style: vertical && !horizontal ? { minWidth: "100%", width: "100%" } : undefined, children: children }) }), vertical && _jsx(Scrollbar, { orientation: "vertical", foreground: foreground, radius: radius }), horizontal && _jsx(Scrollbar, { orientation: "horizontal", foreground: foreground, radius: radius }), vertical && horizontal && _jsx(BaseScrollArea.Corner, { style: { background: "transparent" } })] });
|
|
36
|
+
}, children: _jsx(BaseScrollArea.Content, { "data-phreshos-scroll-area-content": "", style: vertical && !horizontal ? { minWidth: "100%", width: "100%" } : undefined, children: children }) }), vertical && _jsx(Scrollbar, { orientation: "vertical", foreground: foreground, radius: radius, thickness: thickness, inset: inset, transition: transition }), horizontal && _jsx(Scrollbar, { orientation: "horizontal", foreground: foreground, radius: radius, thickness: thickness, inset: inset, transition: transition }), vertical && horizontal && _jsx(BaseScrollArea.Corner, { style: { background: "transparent" } })] });
|
|
29
37
|
return _jsx(BaseDirectionProvider, { direction: direction, children: root });
|
|
30
38
|
});
|
|
31
|
-
function Scrollbar({ orientation, foreground, radius }) {
|
|
39
|
+
function Scrollbar({ orientation, foreground, radius, thickness, inset, transition }) {
|
|
32
40
|
const [hovered, setHovered] = useState(false);
|
|
33
|
-
const transition = useTransitionTiming();
|
|
34
41
|
const vertical = orientation === "vertical";
|
|
35
42
|
return _jsx(BaseScrollArea.Scrollbar, { "data-phreshos-scroll-area-scrollbar": "", orientation: orientation, keepMounted: true, onPointerEnter: () => setHovered(true), onPointerLeave: () => setHovered(false), style: state => {
|
|
36
43
|
const present = vertical ? state.hasOverflowY : state.hasOverflowX;
|
|
37
44
|
return {
|
|
38
45
|
boxSizing: "border-box",
|
|
39
|
-
width: vertical ?
|
|
40
|
-
height: vertical ? undefined :
|
|
41
|
-
padding:
|
|
46
|
+
width: vertical ? thickness : undefined,
|
|
47
|
+
height: vertical ? undefined : thickness,
|
|
48
|
+
padding: inset,
|
|
42
49
|
opacity: present && (state.hovering || state.scrolling) ? 1 : 0,
|
|
43
50
|
pointerEvents: present && (state.hovering || state.scrolling) ? "auto" : "none",
|
|
44
51
|
transitionDuration: transition.transitionDuration,
|
package/dist/select.js
CHANGED
|
@@ -1,48 +1,36 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef } from "react";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { controlPaint, controlStyle, FieldFeedback, FieldLabel, fieldStyle, useControlTheme } from "./control.js";
|
|
2
|
+
import { forwardRef, useMemo } from "react";
|
|
3
|
+
import { Select as AriaSelect, Button, SelectValue, ListBox, ListBoxItem } from "react-aria-components";
|
|
4
|
+
import { controlOpacity, controlPaint, controlStyle, FieldFeedback, FieldLabel, fieldStyle, useControlTheme } from "./control.js";
|
|
6
5
|
import { SurfaceButton } from "./control-surface.js";
|
|
7
|
-
import { Surface } from "./surface.js";
|
|
8
6
|
import { ScrollArea } from "./scroll-area.js";
|
|
9
|
-
import {
|
|
7
|
+
import { PopoverContent } from "./popover.js";
|
|
10
8
|
import { resolveDirection, useDirection } from "./direction.js";
|
|
11
|
-
import { resolveDirectionalPlacement } from "./overlay-placement.js";
|
|
12
9
|
/** Single selection from string-valued options; keyboard navigation and typeahead stay native to React Aria. */
|
|
13
10
|
export const Select = forwardRef(function Select({ label, description, errorMessage, disabled, required, invalid, options, value, defaultValue, onChange, size, color, radius, style, material, shadow, ...properties }, ref) {
|
|
14
11
|
const theme = useControlTheme({ size, color, radius });
|
|
15
|
-
const transition = useControlTransition();
|
|
16
|
-
const overlayTransition = useOverlayTransition();
|
|
17
12
|
const direction = resolveDirection(properties.dir, useDirection());
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
theme.transition.transitionTimingFunction,
|
|
21
|
-
theme.transition.transitionProperty,
|
|
22
|
-
theme.gap,
|
|
23
|
-
theme.height,
|
|
24
|
-
theme.spacing,
|
|
25
|
-
theme.radius,
|
|
26
|
-
theme.focusColor,
|
|
27
|
-
theme.foreground,
|
|
28
|
-
theme.paints.palette.rest.background,
|
|
29
|
-
theme.paints.palette.rest.color,
|
|
30
|
-
theme.paints.palette.hover.background,
|
|
31
|
-
theme.paints.palette.hover.color
|
|
32
|
-
];
|
|
33
|
-
return _jsx(AriaSelect, { ...properties, ref: ref, value: value, defaultValue: defaultValue, onChange: key => onChange?.(key == null ? null : String(key)), disabledKeys: options.filter(option => option.disabled).map(option => option.value), isDisabled: disabled, isRequired: required, isInvalid: invalid, style: fieldStyle(theme, disabled, style), children: state => _jsxs(_Fragment, { children: [_jsx(FieldLabel, { label: label }), _jsxs(Button, { render: (native, button) => _jsx(SurfaceButton, { native: native, material: material, shadow: shadow, paint: controlPaint(theme, state.isOpen || button.isFocused, state.isInvalid, button.isHovered) }), style: button => ({
|
|
13
|
+
const disabledKeys = useMemo(() => options.filter(option => option.disabled).map(option => option.value), [options]);
|
|
14
|
+
return _jsx(AriaSelect, { ...properties, ref: ref, value: value, defaultValue: defaultValue, onChange: key => onChange?.(key == null ? null : String(key)), disabledKeys: disabledKeys, isDisabled: disabled, isRequired: required, isInvalid: invalid, style: fieldStyle(theme, disabled, style), children: state => _jsxs(_Fragment, { children: [_jsx(FieldLabel, { label: label }), _jsxs(Button, { render: (native, button) => _jsx(SurfaceButton, { native: native, material: material, shadow: shadow, paint: controlPaint(theme, state.isOpen || button.isFocused, state.isInvalid, button.isHovered) }), style: button => ({
|
|
34
15
|
...controlStyle(theme, state.isOpen || button.isFocused, state.isInvalid, button.isHovered, button.isFocusVisible),
|
|
35
16
|
display: "flex", alignItems: "center", justifyContent: "space-between", gap: theme.gap,
|
|
36
17
|
cursor: disabled ? "not-allowed" : "pointer", textAlign: "start"
|
|
37
|
-
}), children: [_jsx(SelectValue, { style: ({ isPlaceholder }) => ({ opacity: isPlaceholder ?
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
18
|
+
}), 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
|
+
width: "var(--trigger-width)",
|
|
20
|
+
maxHeight: `min(calc(100vh - ${theme.spacing * 2}px), ${theme.height * 8}px)`,
|
|
21
|
+
padding: theme.gap,
|
|
22
|
+
display: "flex",
|
|
23
|
+
flexDirection: "column"
|
|
24
|
+
}, children: _jsx(ScrollArea, { style: { flex: "1 1 auto", minHeight: 0 }, children: _jsx(ListBox, { shouldFocusOnHover: false, style: { display: "grid", gap: theme.gap, outline: "none", fontSize: theme.fontSize, color: theme.foreground }, children: options.map(option => _jsx(ListBoxItem, { id: option.value, textValue: option.label, style: item => ({
|
|
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 })] }) });
|
|
48
36
|
});
|
package/dist/shadow-options.d.ts
CHANGED
|
@@ -12,14 +12,20 @@ export interface ShadowOverrides {
|
|
|
12
12
|
/** Omission or true uses Appearance defaults; false removes the shadow. */
|
|
13
13
|
readonly shadow?: boolean | ShadowOptions;
|
|
14
14
|
}
|
|
15
|
-
/** Resolves
|
|
16
|
-
export declare function
|
|
15
|
+
/** Resolves overrides against an already selected shadow branch. */
|
|
16
|
+
export declare function resolveShadowOptions(values: ShadowOptions, defaults: Readonly<{
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
blur: number;
|
|
20
|
+
spread: number;
|
|
21
|
+
opacity: number;
|
|
22
|
+
}>): {
|
|
17
23
|
x: number;
|
|
18
24
|
y: number;
|
|
19
25
|
blur: number;
|
|
20
26
|
spread: number;
|
|
21
27
|
opacity: number;
|
|
22
28
|
};
|
|
23
|
-
export type ResolvedShadow = ReturnType<typeof
|
|
29
|
+
export type ResolvedShadow = ReturnType<typeof resolveShadowOptions>;
|
|
24
30
|
/** Serializes one resolved neutral-black outer shadow. */
|
|
25
31
|
export declare function shadowStyle({ x, y, blur, spread, opacity }: ResolvedShadow): string;
|
package/dist/shadow-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 } from "./scale.js";
|
|
4
|
-
/** Resolves
|
|
5
|
-
export function
|
|
6
|
-
const appearance = useAppearance();
|
|
7
|
-
const defaults = useThemedValue(appearance.shadow);
|
|
3
|
+
/** Resolves overrides against an already selected shadow branch. */
|
|
4
|
+
export function resolveShadowOptions(values, defaults) {
|
|
8
5
|
const limits = appearanceLimits.shadow;
|
|
9
6
|
return {
|
|
10
7
|
x: resolve(values.x, defaults.x, limits.x),
|
package/dist/slider.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Base UI accepts direction directly; React Aria derives these keys from locale.
|
|
2
3
|
import { Slider as BaseSlider } from "@base-ui/react/slider";
|
|
3
4
|
import { DirectionProvider as BaseDirectionProvider } from "@base-ui/react/direction-provider";
|
|
4
5
|
import { forwardRef, useEffect, useId, useRef, useState } from "react";
|
|
5
|
-
import { fieldStyle, useControlTheme } from "./control.js";
|
|
6
|
+
import { controlFontWeight, controlOpacity, fieldStyle, useControlTheme } from "./control.js";
|
|
6
7
|
import { resolveDirection, useDirection } from "./direction.js";
|
|
7
8
|
/** One numeric value with pointer, touch, keyboard, and form support. */
|
|
8
9
|
export const Slider = forwardRef(function Slider({ label, description, name, form, disabled, size, color, style, orientation = "horizontal", value, defaultValue, minValue = 0, maxValue = 100, step = 1, formatOptions, onChange, onChangeEnd, ...properties }, ref) {
|
|
@@ -14,7 +15,7 @@ export const Slider = forwardRef(function Slider({ label, description, name, for
|
|
|
14
15
|
const [uncontrolledValue, setUncontrolledValue] = useState(initialValue.current);
|
|
15
16
|
const [hovered, setHovered] = useState(false);
|
|
16
17
|
const vertical = orientation === "vertical";
|
|
17
|
-
const diameter = theme.
|
|
18
|
+
const diameter = theme.indicatorSize;
|
|
18
19
|
const rail = Math.max(4, Math.round(diameter / 3));
|
|
19
20
|
const describedBy = [properties["aria-describedby"], description != null ? descriptionId : null].filter(Boolean).join(" ") || undefined;
|
|
20
21
|
const controlled = value !== undefined;
|
|
@@ -31,7 +32,7 @@ export const Slider = forwardRef(function Slider({ label, description, name, for
|
|
|
31
32
|
if (!controlled)
|
|
32
33
|
setUncontrolledValue(next);
|
|
33
34
|
onChange?.(next);
|
|
34
|
-
}, onValueCommitted: next => onChangeEnd?.(next), style: fieldStyle(theme, disabled, style), children: [_jsxs("div", { style: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: theme.gap }, children: [label != null && _jsx(BaseSlider.Label, { style: { fontWeight:
|
|
35
|
+
}, onValueCommitted: next => onChangeEnd?.(next), style: fieldStyle(theme, disabled, style), children: [_jsxs("div", { style: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: theme.gap }, children: [label != null && _jsx(BaseSlider.Label, { style: { fontWeight: controlFontWeight }, children: label }), _jsx(BaseSlider.Value, { style: { fontVariantNumeric: "tabular-nums", opacity: controlOpacity.secondary } })] }), _jsx(BaseSlider.Control, { style: {
|
|
35
36
|
position: "relative",
|
|
36
37
|
display: "flex",
|
|
37
38
|
alignItems: "center",
|
|
@@ -39,7 +40,7 @@ export const Slider = forwardRef(function Slider({ label, description, name, for
|
|
|
39
40
|
touchAction: "none",
|
|
40
41
|
userSelect: "none",
|
|
41
42
|
width: vertical ? theme.height : "100%",
|
|
42
|
-
height: vertical ?
|
|
43
|
+
height: vertical ? theme.height * 4 : theme.height
|
|
43
44
|
}, children: _jsxs(BaseSlider.Track, { style: {
|
|
44
45
|
position: "relative",
|
|
45
46
|
width: vertical ? rail : "100%",
|
|
@@ -59,12 +60,12 @@ export const Slider = forwardRef(function Slider({ label, description, name, for
|
|
|
59
60
|
borderRadius: "50%",
|
|
60
61
|
boxSizing: "border-box",
|
|
61
62
|
background: paint.color,
|
|
62
|
-
border:
|
|
63
|
+
border: `${Math.max(1, diameter / 6)}px solid ${paint.background}`,
|
|
63
64
|
outline: state.focused ? `1px solid ${theme.focusColor}` : "none",
|
|
64
65
|
outlineOffset: 1,
|
|
65
66
|
cursor: state.disabled ? "not-allowed" : state.dragging ? "grabbing" : "grab",
|
|
66
67
|
transform: `scale(${state.dragging ? 0.92 : hovered ? 1.08 : 1})`
|
|
67
68
|
};
|
|
68
|
-
} })] }) }), description != null && _jsx("span", { id: descriptionId, style: { fontSize: "0.92em", opacity:
|
|
69
|
+
} })] }) }), description != null && _jsx("span", { id: descriptionId, style: { fontSize: "0.92em", opacity: controlOpacity.secondary }, children: description })] });
|
|
69
70
|
return _jsx(BaseDirectionProvider, { direction: direction, children: root });
|
|
70
71
|
});
|
package/dist/spacing.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 ScaleLevel } from "./scale.js";
|
|
4
4
|
/** An Appearance-derived level, pixel value, or explicit CSS spacing value. */
|
|
5
5
|
export type Spacing = ScaleLevel | number | (string & {});
|
package/dist/surface-edge.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { type CSSProperties } from "react";
|
|
1
2
|
type EdgeMaterial = Readonly<{
|
|
2
3
|
color: string;
|
|
3
4
|
opacity: number;
|
|
4
5
|
}>;
|
|
5
6
|
/** Paints the boundary owned by one Surface from the material it contains. */
|
|
6
|
-
export declare function SurfaceEdge({ material }: Readonly<{
|
|
7
|
+
export declare function SurfaceEdge({ material, transition }: Readonly<{
|
|
7
8
|
material: EdgeMaterial;
|
|
9
|
+
transition: CSSProperties;
|
|
8
10
|
}>): import("react").JSX.Element | null;
|
|
9
11
|
export {};
|
package/dist/surface-edge.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Fragment } from "react";
|
|
3
|
-
import { useTransitionTiming } from "./motion-style.js";
|
|
4
3
|
import { scale } from "./scale.js";
|
|
5
4
|
const edgeLayer = {
|
|
6
5
|
position: "absolute",
|
|
@@ -17,15 +16,14 @@ const illuminatedEdgeBoundary = Math.round((outerEdgeThickness + illuminatedEdge
|
|
|
17
16
|
const horizontalIllumination = "linear-gradient(90deg, var(--phreshos-surface-edge-light-minimum) 0%, var(--phreshos-surface-edge-light-minimum) 12%, var(--phreshos-surface-edge-light-soft) 34%, var(--phreshos-surface-edge-light-peak) 50%, var(--phreshos-surface-edge-light-soft) 66%, var(--phreshos-surface-edge-light-minimum) 88%, var(--phreshos-surface-edge-light-minimum) 100%)";
|
|
18
17
|
const edgeMask = "linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)";
|
|
19
18
|
/** Paints the boundary owned by one Surface from the material it contains. */
|
|
20
|
-
export function SurfaceEdge({ material }) {
|
|
21
|
-
const timing = useTransitionTiming();
|
|
19
|
+
export function SurfaceEdge({ material, transition }) {
|
|
22
20
|
const visible = material.opacity > 0;
|
|
23
21
|
if (!visible)
|
|
24
22
|
return null;
|
|
25
23
|
const opacity = Math.min(1, scale(material.opacity, "xlarge"));
|
|
26
24
|
const peak = illumination(material.color, 92);
|
|
27
25
|
return _jsxs(Fragment, { children: [_jsx("span", { "data-surface-edge-light": "", "aria-hidden": "true", style: {
|
|
28
|
-
...
|
|
26
|
+
...transition,
|
|
29
27
|
...edgeLayer,
|
|
30
28
|
transitionProperty: "opacity",
|
|
31
29
|
padding: illuminatedEdgeBoundary,
|
|
@@ -39,7 +37,7 @@ export function SurfaceEdge({ material }) {
|
|
|
39
37
|
"--phreshos-surface-edge-light-soft": illumination(material.color, 56),
|
|
40
38
|
"--phreshos-surface-edge-light-minimum": illumination(material.color, 24)
|
|
41
39
|
} }), _jsx("span", { "data-surface-edge": "", "aria-hidden": "true", style: {
|
|
42
|
-
...
|
|
40
|
+
...transition,
|
|
43
41
|
...edgeLayer,
|
|
44
42
|
transitionProperty: "border-color, opacity",
|
|
45
43
|
borderStyle: "solid",
|
package/dist/surface.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { createElement, forwardRef, useId } from "react";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { useResolvedAppearance } from "./appearance-context.js";
|
|
4
|
+
import { contrastingColor, defaultColor, resolveColor } from "./color.js";
|
|
5
|
+
import { resolveMaterialOptions } from "./material-options.js";
|
|
6
6
|
import { MaterialPaint } from "./material-paint.js";
|
|
7
|
-
import
|
|
7
|
+
import { paintTransition, transitionTiming, visualTransition } from "./motion-style.js";
|
|
8
8
|
import { resolveRadius } from "./radius.js";
|
|
9
|
-
import {
|
|
9
|
+
import { resolveShadowOptions, shadowStyle } from "./shadow-options.js";
|
|
10
10
|
import { SurfaceEdge } from "./surface-edge.js";
|
|
11
11
|
/** One material- and shadow-owning element. The host is a div unless `as` selects another element. */
|
|
12
12
|
const SurfaceRoot = forwardRef(function Surface({ as: Element = "div", color, material: options, shadow: shadowOptions, radius, children, style, ...properties }, ref) {
|
|
13
|
-
const
|
|
14
|
-
const transition =
|
|
15
|
-
const material =
|
|
16
|
-
const resolvedShadow =
|
|
13
|
+
const resolved = useResolvedAppearance();
|
|
14
|
+
const transition = visualTransition(resolved.transaction, resolved.preferences.animations);
|
|
15
|
+
const material = resolveSurface(color, options, resolved);
|
|
16
|
+
const resolvedShadow = resolveShadowOptions(typeof shadowOptions === "object" ? shadowOptions : {}, resolved.shadow);
|
|
17
17
|
const shadow = shadowOptions === false ? "none" : shadowStyle(resolvedShadow);
|
|
18
|
-
const resolvedRadius = resolveRadius(radius ?? "medium", appearance);
|
|
18
|
+
const resolvedRadius = resolveRadius(radius ?? "medium", resolved.appearance);
|
|
19
|
+
const timing = transitionTiming(resolved.transaction, resolved.preferences.animations);
|
|
20
|
+
const paintTiming = paintTransition(resolved.transaction, resolved.preferences.animations);
|
|
19
21
|
return createElement(Element, {
|
|
20
22
|
...properties,
|
|
21
23
|
ref,
|
|
@@ -29,17 +31,17 @@ const SurfaceRoot = forwardRef(function Surface({ as: Element = "div", color, ma
|
|
|
29
31
|
position: style?.position ?? "relative",
|
|
30
32
|
isolation: "isolate"
|
|
31
33
|
}
|
|
32
|
-
}, material.enabled && _jsx(SurfaceLayers, { material: material }), material.enabled && _jsx(SurfaceEdge, { material: material }), children);
|
|
34
|
+
}, material.enabled && _jsx(SurfaceLayers, { material: material, paintTransition: paintTiming }), material.enabled && _jsx(SurfaceEdge, { material: material, transition: timing }), children);
|
|
33
35
|
});
|
|
34
36
|
export const Surface = SurfaceRoot;
|
|
35
|
-
function
|
|
36
|
-
const
|
|
37
|
-
const
|
|
37
|
+
function resolveSurface(color, options, resolved) {
|
|
38
|
+
const material = resolveMaterialOptions(typeof options === "object" ? options : {}, resolved.material);
|
|
39
|
+
const fill = resolveColor(color, resolved.colors);
|
|
38
40
|
return {
|
|
39
41
|
...material,
|
|
40
42
|
enabled: options !== false,
|
|
41
|
-
color:
|
|
42
|
-
foreground:
|
|
43
|
+
color: fill,
|
|
44
|
+
foreground: contrastingColor(color ?? defaultColor, resolved.colors)
|
|
43
45
|
};
|
|
44
46
|
}
|
|
45
47
|
const layerStyle = {
|
|
@@ -48,14 +50,17 @@ const layerStyle = {
|
|
|
48
50
|
borderRadius: "inherit",
|
|
49
51
|
pointerEvents: "none"
|
|
50
52
|
};
|
|
51
|
-
function SurfaceLayers({ material }) {
|
|
52
|
-
const
|
|
53
|
+
function SurfaceLayers({ material, paintTransition }) {
|
|
54
|
+
const filtersVisible = material.opacity < 1;
|
|
55
|
+
const backdrop = filtersVisible ? material.backdrop : 0;
|
|
56
|
+
const saturation = filtersVisible ? material.saturation : 1;
|
|
57
|
+
const distortion = filtersVisible ? material.distortion : 0;
|
|
53
58
|
const frost = [
|
|
54
59
|
backdrop === 0 ? "" : `blur(${backdrop}px)`,
|
|
55
60
|
saturation === 1 ? "" : `saturate(${saturation})`
|
|
56
61
|
].filter(Boolean).join(" ");
|
|
57
62
|
const identity = `phresh-material-${useId().replaceAll(":", "")}`;
|
|
58
|
-
return _jsxs("span", { "data-material": "", "aria-hidden": "true", style: layerStyle, children: [
|
|
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.opacity, transition: paintTransition })] });
|
|
59
64
|
}
|
|
60
65
|
/** Keep refraction and native frost in independent compositor passes. */
|
|
61
66
|
function BackdropLayer({ filter, name, zIndex }) {
|
package/dist/toggle-indicator.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { motion } from "motion/react";
|
|
3
|
-
import { useControlTransition } from "./motion-style.js";
|
|
4
|
-
import { usePreferences } from "./ui-provider.js";
|
|
5
3
|
import { Surface } from "./surface.js";
|
|
6
4
|
/** Shared paint only. Selection, focus, validation, and native input behavior belong to React Aria. */
|
|
7
5
|
export function ToggleIndicator({ kind, direction = "ltr", selected, indeterminate = false, focused, invalid = false, hovered = false, pressed = false, theme, material: options, shadow }) {
|
|
8
|
-
const { animations } =
|
|
9
|
-
const
|
|
10
|
-
const diameter = theme.fontSize + 6;
|
|
6
|
+
const { animations, motionTransition: transition } = theme;
|
|
7
|
+
const diameter = theme.indicatorSize;
|
|
11
8
|
const active = selected || indeterminate;
|
|
12
9
|
const paints = invalid ? theme.paints.danger : active ? theme.paints.palette : theme.paints.neutral;
|
|
13
10
|
const paint = pressed ? paints.pressed : hovered ? paints.hover : paints.rest;
|