@phreshos/react-ui 0.1.28 → 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 +30 -9
- package/dist/appearance-context.d.ts +43 -1
- package/dist/appearance-context.js +68 -18
- 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/main.d.ts +2 -1
- package/dist/main.js +2 -1
- package/dist/material-options.d.ts +10 -3
- package/dist/material-options.js +2 -5
- 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/radio.js +4 -3
- 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 +2 -5
- package/dist/slider.js +7 -6
- package/dist/surface-edge.d.ts +3 -1
- package/dist/surface-edge.js +3 -5
- package/dist/surface.js +20 -18
- package/dist/toggle-indicator.js +2 -5
- package/dist/tooltip.js +16 -15
- package/dist/ui-provider.js +9 -6
- package/dist/window-header.d.ts +42 -0
- package/dist/window-header.js +113 -0
- package/package.json +2 -2
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/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
1
|
import { appearanceLimits } from "./appearance.js";
|
|
2
|
-
import { useAppearance, useThemedValue } from "./ui-provider.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/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,7 +50,7 @@ const layerStyle = {
|
|
|
48
50
|
borderRadius: "inherit",
|
|
49
51
|
pointerEvents: "none"
|
|
50
52
|
};
|
|
51
|
-
function SurfaceLayers({ material }) {
|
|
53
|
+
function SurfaceLayers({ material, paintTransition }) {
|
|
52
54
|
const filtersVisible = material.opacity < 1;
|
|
53
55
|
const backdrop = filtersVisible ? material.backdrop : 0;
|
|
54
56
|
const saturation = filtersVisible ? material.saturation : 1;
|
|
@@ -58,7 +60,7 @@ function SurfaceLayers({ material }) {
|
|
|
58
60
|
saturation === 1 ? "" : `saturate(${saturation})`
|
|
59
61
|
].filter(Boolean).join(" ");
|
|
60
62
|
const identity = `phresh-material-${useId().replaceAll(":", "")}`;
|
|
61
|
-
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 })] });
|
|
62
64
|
}
|
|
63
65
|
/** Keep refraction and native frost in independent compositor passes. */
|
|
64
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;
|
package/dist/tooltip.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
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 { Tooltip as AriaTooltip, TooltipTrigger as AriaTooltipTrigger } from "react-aria-components";
|
|
4
|
-
import {
|
|
4
|
+
import { useResolvedAppearance } from "./appearance-context.js";
|
|
5
5
|
import { Button } from "./button.js";
|
|
6
6
|
import { controlFontSizes } from "./control.js";
|
|
7
|
-
import { overlayMotionClass,
|
|
7
|
+
import MotionStyle, { overlayMotionClass, overlayTransition } from "./motion-style.js";
|
|
8
8
|
import { scale } from "./scale.js";
|
|
9
9
|
import { Surface } from "./surface.js";
|
|
10
10
|
import { resolveDirection, useDirection } from "./direction.js";
|
|
@@ -16,19 +16,20 @@ export const TooltipTrigger = forwardRef(function TooltipTrigger(properties, ref
|
|
|
16
16
|
return _jsx(Button, { ...properties, ref: ref });
|
|
17
17
|
});
|
|
18
18
|
export const TooltipContent = forwardRef(function TooltipContent({ children, className, color, material, radius, shadow, style, offset, placement = "top", ...properties }, ref) {
|
|
19
|
-
const
|
|
20
|
-
const
|
|
19
|
+
const resolved = useResolvedAppearance();
|
|
20
|
+
const inset = scale(resolved.appearance.spacing, "small");
|
|
21
|
+
const transition = overlayTransition(resolved.transaction, resolved.preferences.animations);
|
|
21
22
|
const direction = resolveDirection(properties.dir, useDirection());
|
|
22
|
-
return _jsx(AriaTooltip, { ...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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
return _jsxs(_Fragment, { children: [_jsx(MotionStyle, {}), _jsx(AriaTooltip, { ...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: {
|
|
24
|
+
boxSizing: "border-box",
|
|
25
|
+
maxWidth: "28em",
|
|
26
|
+
paddingBlock: inset,
|
|
27
|
+
paddingInline: inset * 1.5,
|
|
28
|
+
outline: "none",
|
|
29
|
+
fontSize: controlFontSizes.small,
|
|
30
|
+
lineHeight: 1.4,
|
|
31
|
+
...style
|
|
32
|
+
}, children: children }) })] });
|
|
32
33
|
});
|
|
33
34
|
/** A description shown from the focus and hover state of its trigger. */
|
|
34
35
|
export const Tooltip = Object.assign(TooltipRoot, {
|
package/dist/ui-provider.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { AppearanceContext, fixedPreferencesSource, PreferencesContext } from "./appearance-context.js";
|
|
4
|
+
import { DirectionContext, fixedDirectionSource } from "./direction.js";
|
|
4
5
|
export { useAppearance, useBrowserPreferences, usePreferences, useThemedValue } from "./appearance-context.js";
|
|
5
6
|
export { useDirection, useDocumentDirection } from "./direction.js";
|
|
6
7
|
const directionBoundaryStyle = { display: "contents" };
|
|
7
8
|
/** Establishes only the explicitly supplied UI values for a React subtree. */
|
|
8
9
|
export function UIProvider({ appearance, children, direction, preferences }) {
|
|
9
10
|
let subtree = children;
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const preferencesSource = useMemo(() => preferences === undefined ? undefined : fixedPreferencesSource(preferences), [preferences]);
|
|
12
|
+
const directionSource = useMemo(() => direction === undefined ? undefined : fixedDirectionSource(direction), [direction]);
|
|
13
|
+
if (preferencesSource !== undefined) {
|
|
14
|
+
subtree = _jsx(PreferencesContext.Provider, { value: preferencesSource, children: subtree });
|
|
12
15
|
}
|
|
13
16
|
if (appearance !== undefined) {
|
|
14
17
|
subtree = _jsx(AppearanceContext.Provider, { value: appearance, children: subtree });
|
|
15
18
|
}
|
|
16
|
-
if (
|
|
17
|
-
subtree = _jsx(DirectionContext.Provider, { value:
|
|
19
|
+
if (directionSource !== undefined) {
|
|
20
|
+
subtree = _jsx(DirectionContext.Provider, { value: directionSource, children: _jsx("div", { dir: direction, style: directionBoundaryStyle, children: subtree }) });
|
|
18
21
|
}
|
|
19
22
|
return subtree;
|
|
20
23
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ComponentProps, HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import { type ButtonProps } from "./button.js";
|
|
3
|
+
export interface WindowHeaderRootProps extends HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
/** Attenuates the identity when this header is not active. */
|
|
5
|
+
readonly active?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** The composable top region of a window; its enclosing Surface owns the material. */
|
|
8
|
+
export declare const WindowHeaderRoot: import("react").ForwardRefExoticComponent<WindowHeaderRootProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
9
|
+
export interface WindowHeaderIdentityProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
10
|
+
readonly icon?: string;
|
|
11
|
+
readonly title?: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
/** The icon and truncating title share the window's active treatment. */
|
|
14
|
+
export declare const WindowHeaderIdentity: import("react").ForwardRefExoticComponent<WindowHeaderIdentityProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
15
|
+
export type WindowHeaderCenterProps = HTMLAttributes<HTMLDivElement>;
|
|
16
|
+
/** Optional flexible space for application-owned content. */
|
|
17
|
+
export declare const WindowHeaderCenter: import("react").ForwardRefExoticComponent<WindowHeaderCenterProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
18
|
+
export type WindowHeaderActionsProps = HTMLAttributes<HTMLDivElement>;
|
|
19
|
+
/** End-aligned controls that do not begin a header drag or double-click action. */
|
|
20
|
+
export declare const WindowHeaderActions: import("react").ForwardRefExoticComponent<WindowHeaderActionsProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
21
|
+
export type WindowHeaderActionProps = Omit<ButtonProps, "size">;
|
|
22
|
+
/** A compact header action; extra actions use the same treatment as the standard controls. */
|
|
23
|
+
export declare const WindowHeaderAction: import("react").ForwardRefExoticComponent<WindowHeaderActionProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
24
|
+
export type WindowHeaderControlProps = Omit<WindowHeaderActionProps, "children">;
|
|
25
|
+
export declare const WindowHeaderMinimize: import("react").ForwardRefExoticComponent<WindowHeaderControlProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
26
|
+
export interface WindowHeaderMaximizeProps extends WindowHeaderControlProps {
|
|
27
|
+
readonly maximized?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare const WindowHeaderMaximize: import("react").ForwardRefExoticComponent<WindowHeaderMaximizeProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
30
|
+
export type WindowHeaderCloseProps = Omit<WindowHeaderControlProps, "color">;
|
|
31
|
+
export declare const WindowHeaderClose: import("react").ForwardRefExoticComponent<WindowHeaderCloseProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
32
|
+
export declare const WindowHeader: import("react").ForwardRefExoticComponent<WindowHeaderRootProps & import("react").RefAttributes<HTMLDivElement>> & {
|
|
33
|
+
Root: import("react").ForwardRefExoticComponent<WindowHeaderRootProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
34
|
+
Identity: import("react").ForwardRefExoticComponent<WindowHeaderIdentityProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
35
|
+
Center: import("react").ForwardRefExoticComponent<WindowHeaderCenterProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
36
|
+
Actions: import("react").ForwardRefExoticComponent<WindowHeaderActionsProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
37
|
+
Action: import("react").ForwardRefExoticComponent<WindowHeaderActionProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
38
|
+
Minimize: import("react").ForwardRefExoticComponent<WindowHeaderControlProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
39
|
+
Maximize: import("react").ForwardRefExoticComponent<WindowHeaderMaximizeProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
40
|
+
Close: import("react").ForwardRefExoticComponent<WindowHeaderCloseProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
41
|
+
};
|
|
42
|
+
export type WindowHeaderProps = ComponentProps<typeof WindowHeaderRoot>;
|