@phreshos/react-ui 0.1.30 → 0.1.32

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.
Files changed (84) hide show
  1. package/README.md +162 -18
  2. package/dist/accordion.d.ts +45 -0
  3. package/dist/accordion.js +62 -0
  4. package/dist/alert-dialog.d.ts +0 -1
  5. package/dist/alert-dialog.js +0 -1
  6. package/dist/appearance.d.ts +6 -1
  7. package/dist/aria-direction.d.ts +9 -0
  8. package/dist/aria-direction.js +49 -0
  9. package/dist/calendar-layout.d.ts +8 -0
  10. package/dist/calendar-layout.js +103 -0
  11. package/dist/calendar-trigger.d.ts +5 -0
  12. package/dist/calendar-trigger.js +30 -0
  13. package/dist/calendar.d.ts +18 -0
  14. package/dist/calendar.js +13 -0
  15. package/dist/color.d.ts +16 -1
  16. package/dist/color.js +60 -11
  17. package/dist/combo-box.d.ts +20 -0
  18. package/dist/combo-box.js +68 -0
  19. package/dist/context-menu.d.ts +2 -4
  20. package/dist/context-menu.js +0 -1
  21. package/dist/control-surface.d.ts +2 -1
  22. package/dist/control-surface.js +2 -2
  23. package/dist/control.d.ts +1 -1
  24. package/dist/control.js +7 -8
  25. package/dist/date-control.d.ts +24 -0
  26. package/dist/date-control.js +69 -0
  27. package/dist/date-field.d.ts +19 -0
  28. package/dist/date-field.js +13 -0
  29. package/dist/date-picker.d.ts +19 -0
  30. package/dist/date-picker.js +16 -0
  31. package/dist/date-range-picker.d.ts +20 -0
  32. package/dist/date-range-picker.js +25 -0
  33. package/dist/date-range.d.ts +6 -0
  34. package/dist/date-range.js +0 -0
  35. package/dist/dialog.d.ts +4 -2
  36. package/dist/dialog.js +24 -7
  37. package/dist/disclosure.d.ts +42 -0
  38. package/dist/disclosure.js +97 -0
  39. package/dist/dropdown-menu.d.ts +0 -2
  40. package/dist/dropdown-menu.js +0 -1
  41. package/dist/list-box.d.ts +49 -0
  42. package/dist/list-box.js +129 -0
  43. package/dist/main.d.ts +27 -11
  44. package/dist/main.js +25 -9
  45. package/dist/material-paint.js +19 -1
  46. package/dist/menu.d.ts +22 -4
  47. package/dist/menu.js +25 -6
  48. package/dist/panel.d.ts +0 -1
  49. package/dist/panel.js +0 -1
  50. package/dist/popover.d.ts +0 -1
  51. package/dist/popover.js +13 -3
  52. package/dist/preferences.d.ts +8 -0
  53. package/dist/progress-bar.d.ts +22 -0
  54. package/dist/progress-bar.js +46 -0
  55. package/dist/radio.d.ts +5 -5
  56. package/dist/radio.js +8 -4
  57. package/dist/radius.js +1 -1
  58. package/dist/range-calendar.d.ts +19 -0
  59. package/dist/range-calendar.js +15 -0
  60. package/dist/scroll-area.d.ts +4 -1
  61. package/dist/scroll-area.js +3 -3
  62. package/dist/select.js +4 -14
  63. package/dist/selection.d.ts +18 -0
  64. package/dist/selection.js +28 -0
  65. package/dist/slider.js +1 -1
  66. package/dist/spacing.js +1 -1
  67. package/dist/table.d.ts +78 -0
  68. package/dist/table.js +148 -0
  69. package/dist/tabs.d.ts +45 -0
  70. package/dist/tabs.js +103 -0
  71. package/dist/time-field.d.ts +22 -0
  72. package/dist/time-field.js +13 -0
  73. package/dist/toggle-indicator.js +1 -1
  74. package/dist/toolbar.d.ts +46 -0
  75. package/dist/toolbar.js +121 -0
  76. package/dist/tooltip.d.ts +0 -1
  77. package/dist/tooltip.js +0 -1
  78. package/dist/tree.d.ts +56 -0
  79. package/dist/tree.js +134 -0
  80. package/dist/ui-provider.d.ts +4 -4
  81. package/dist/ui-provider.js +47 -8
  82. package/dist/window.d.ts +6 -3
  83. package/dist/window.js +10 -7
  84. package/package.json +4 -2
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({ className, color, size = "medium", style, ...properties }, ref) {
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: theme.foreground };
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.foreground, controlOpacity.separator),
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,
@@ -1,4 +1,12 @@
1
1
  import type { DesktopPreferences } from "@phreshos/core";
2
2
  /** Complete environment preferences consumed by React UI. */
3
3
  export type Preferences = Pick<DesktopPreferences, "theme" | "animations">;
4
+ /** At least one preference merged with the nearest UIProvider. */
5
+ export type PreferencesUpdate = Readonly<{
6
+ theme: Preferences["theme"];
7
+ animations?: Preferences["animations"];
8
+ }> | Readonly<{
9
+ theme?: Preferences["theme"];
10
+ animations: Preferences["animations"];
11
+ }>;
4
12
  export type Theme = Preferences["theme"];
@@ -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 RadioProps extends Omit<BaseRadioProps<string>, "children" | "className" | "disabled" | "readOnly" | "required" | "style" | "value">, ControlProps, Pick<FieldProps, "label" | "description">, MaterialOverrides, ShadowOverrides {
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 string value selected from its Radio children, with direction-aware arrow navigation. */
19
- export declare const RadioGroup: import("react").ForwardRefExoticComponent<Omit<RadioGroupProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
20
- /** An option in a RadioGroup. Selection and validation belong to the group. */
21
- export declare const Radio: import("react").ForwardRefExoticComponent<Omit<RadioProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
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 Radio children, with direction-aware arrow navigation. */
12
- export const RadioGroup = forwardRef(function RadioGroup({ 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) {
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
- export const Radio = forwardRef(function Radio({ label, description, value, disabled = false, size, color, style, className, material, shadow, onFocus, onBlur, "aria-describedby": ariaDescribedBy, ...properties }, ref) {
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("Radio must be used inside RadioGroup");
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 a UIProvider");
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
+ });
@@ -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
  }
@@ -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.foreground;
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, ListBox, ListBoxItem } from "react-aria-components";
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: theme.gap,
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, { 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 })] }) });
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.neutral.rest.background
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 a UIProvider");
6
+ throw new Error("Semantic spacing requires an Appearance value");
7
7
  return scale(appearance.spacing, value);
8
8
  }
@@ -0,0 +1,78 @@
1
+ import type { CSSProperties, ReactElement, RefAttributes } from "react";
2
+ import type { CellProps as AriaCellProps, ColumnProps as AriaColumnProps, RowProps as AriaRowProps, TableBodyProps as AriaTableBodyProps, TableHeaderProps as AriaTableHeaderProps, TableProps as AriaTableProps } from "react-aria-components";
3
+ import type { ControlColor } from "./control.js";
4
+ import type { RadiusProps } from "./radius.js";
5
+ import type { ScaleLevel } from "./scale.js";
6
+ import { type MultipleStringSelection, type MultipleStringSelectionProps, type SingleStringSelectionProps } from "./selection.js";
7
+ export type TableSortDirection = "ascending" | "descending";
8
+ export interface TableSort {
9
+ readonly column: string;
10
+ readonly direction: TableSortDirection;
11
+ }
12
+ type TableRootBaseProps = Omit<AriaTableProps, "className" | "defaultSelectedKeys" | "disabledKeys" | "onRowAction" | "onSelectionChange" | "onSortChange" | "selectedKeys" | "selectionMode" | "sortDescriptor" | "style"> & RadiusProps & Readonly<{
13
+ className?: string;
14
+ color?: ControlColor;
15
+ disabledValues?: readonly string[];
16
+ onRowAction?: (value: string) => void;
17
+ onSortChange?: (sort: TableSort) => void;
18
+ size?: ScaleLevel;
19
+ sort?: TableSort;
20
+ style?: CSSProperties;
21
+ }>;
22
+ export type TableNoSelectionProps = Readonly<{
23
+ selectionMode?: "none";
24
+ value?: never;
25
+ defaultValue?: never;
26
+ onChange?: never;
27
+ }>;
28
+ export type TableSingleSelectionProps = SingleStringSelectionProps & Readonly<{
29
+ selectionMode: "single";
30
+ }>;
31
+ export type TableMultipleValue = MultipleStringSelection;
32
+ export type TableMultipleSelectionProps = MultipleStringSelectionProps & Readonly<{
33
+ selectionMode: "multiple";
34
+ }>;
35
+ export type TableRootProps = TableRootBaseProps & (TableNoSelectionProps | TableSingleSelectionProps | TableMultipleSelectionProps);
36
+ export declare const TableRoot: import("react").ForwardRefExoticComponent<TableRootProps & RefAttributes<HTMLDivElement | HTMLTableElement>>;
37
+ export interface TableHeaderProps<T extends object = object> extends Omit<AriaTableHeaderProps<T>, "className" | "style"> {
38
+ readonly className?: string;
39
+ readonly color?: ControlColor;
40
+ readonly style?: CSSProperties;
41
+ }
42
+ export type TableHeaderComponent = <T extends object = object>(properties: TableHeaderProps<T> & RefAttributes<HTMLTableSectionElement | HTMLDivElement>) => ReactElement | null;
43
+ export declare const TableHeader: TableHeaderComponent;
44
+ export interface TableColumnProps extends Omit<AriaColumnProps, "className" | "id" | "style"> {
45
+ readonly className?: string;
46
+ readonly id: string;
47
+ readonly style?: CSSProperties;
48
+ }
49
+ export declare const TableColumn: import("react").ForwardRefExoticComponent<TableColumnProps & RefAttributes<HTMLDivElement | HTMLTableCellElement>>;
50
+ export interface TableBodyProps<T extends object = object> extends Omit<AriaTableBodyProps<T>, "className" | "style"> {
51
+ readonly className?: string;
52
+ readonly style?: CSSProperties;
53
+ }
54
+ export type TableBodyComponent = <T extends object = object>(properties: TableBodyProps<T> & RefAttributes<HTMLTableSectionElement | HTMLDivElement>) => ReactElement | null;
55
+ export declare const TableBody: TableBodyComponent;
56
+ export interface TableRowProps<T extends object = object> extends Omit<AriaRowProps<T>, "className" | "id" | "isDisabled" | "style"> {
57
+ readonly className?: string;
58
+ readonly disabled?: boolean;
59
+ readonly id: string;
60
+ readonly style?: CSSProperties;
61
+ }
62
+ export type TableRowComponent = <T extends object = object>(properties: TableRowProps<T> & RefAttributes<HTMLTableRowElement | HTMLDivElement>) => ReactElement | null;
63
+ export declare const TableRow: TableRowComponent;
64
+ export interface TableCellProps extends Omit<AriaCellProps, "className" | "style"> {
65
+ readonly className?: string;
66
+ readonly style?: CSSProperties;
67
+ }
68
+ export declare const TableCell: import("react").ForwardRefExoticComponent<TableCellProps & RefAttributes<HTMLDivElement | HTMLTableCellElement>>;
69
+ /** Structured rows and columns with optional selection and consumer-owned sorting. */
70
+ export declare const Table: import("react").ForwardRefExoticComponent<TableRootProps & RefAttributes<HTMLDivElement | HTMLTableElement>> & {
71
+ Header: TableHeaderComponent;
72
+ Column: import("react").ForwardRefExoticComponent<TableColumnProps & RefAttributes<HTMLDivElement | HTMLTableCellElement>>;
73
+ Body: TableBodyComponent;
74
+ Row: TableRowComponent;
75
+ Cell: import("react").ForwardRefExoticComponent<TableCellProps & RefAttributes<HTMLDivElement | HTMLTableCellElement>>;
76
+ };
77
+ export type TableProps = TableRootProps;
78
+ export {};