@phreshos/react-ui 0.1.52 → 0.2.0
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/dist/app-layout.d.ts +12 -0
- package/dist/app-layout.js +141 -28
- package/dist/color-field.d.ts +3 -1
- package/dist/color-field.js +21 -4
- package/dist/dialog.d.ts +0 -6
- package/dist/dialog.js +10 -8
- package/dist/drawer.d.ts +2 -1
- package/dist/drawer.js +7 -3
- package/dist/grid-list.d.ts +5 -0
- package/dist/grid-list.js +3 -2
- package/dist/main.d.ts +3 -3
- package/dist/main.js +2 -2
- package/dist/surface/surface.d.ts +9 -0
- package/dist/surface/surface.js +18 -1
- package/dist/table.d.ts +8 -1
- package/dist/table.js +33 -6
- package/dist/typography.d.ts +7 -0
- package/dist/typography.js +2 -1
- package/package.json +2 -2
package/dist/app-layout.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { CSSProperties, HTMLAttributes, ReactNode } from "react";
|
|
2
2
|
import { type SurfaceDepth } from "./surface/surface.js";
|
|
3
|
+
/** At this width or less, a sidebar beside the content would crowd it out. */
|
|
4
|
+
export declare const narrowAppLayoutWidth = 640;
|
|
3
5
|
export interface AppLayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
4
6
|
readonly children: ReactNode;
|
|
5
7
|
/** The sidebar's width; eighteen times the Appearance spacing by default. */
|
|
@@ -15,16 +17,26 @@ export type AppLayoutSidebarProps = AppLayoutRegionProps & Readonly<{
|
|
|
15
17
|
footer?: ReactNode;
|
|
16
18
|
}>;
|
|
17
19
|
export type AppLayoutTitleProps = HTMLAttributes<HTMLHeadingElement>;
|
|
20
|
+
/** In a narrow layout, the button that opens the sidebar's Drawer; otherwise nothing. */
|
|
21
|
+
declare function AppLayoutSidebarToggle(): import("react").JSX.Element | null;
|
|
22
|
+
/** Whether the nearest AppLayout is narrow, and closing its sidebar's Drawer, such as after a choice in it. */
|
|
23
|
+
export declare function useAppLayout(): {
|
|
24
|
+
narrow: boolean;
|
|
25
|
+
closeSidebar: () => void;
|
|
26
|
+
};
|
|
18
27
|
export declare const AppLayout: import("react").ForwardRefExoticComponent<AppLayoutProps & import("react").RefAttributes<HTMLDivElement>> & {
|
|
19
28
|
Title: import("react").ForwardRefExoticComponent<AppLayoutTitleProps & import("react").RefAttributes<HTMLHeadingElement>>;
|
|
20
29
|
Sidebar: import("react").ForwardRefExoticComponent<AppLayoutRegionProps & Readonly<{
|
|
21
30
|
/** What stays at the foot of the sidebar while the rest scrolls, such as work in progress. */
|
|
22
31
|
footer?: ReactNode;
|
|
23
32
|
}> & import("react").RefAttributes<HTMLElement>>;
|
|
33
|
+
SidebarToggle: typeof AppLayoutSidebarToggle;
|
|
24
34
|
Header: import("react").ForwardRefExoticComponent<AppLayoutRegionProps & import("react").RefAttributes<HTMLElement>>;
|
|
25
35
|
Content: import("react").ForwardRefExoticComponent<AppLayoutRegionProps & Readonly<{
|
|
26
36
|
/** How the content's Surface stands in the layout: recessed into it by default, or raised. */
|
|
27
37
|
depth?: SurfaceDepth;
|
|
28
38
|
}> & import("react").RefAttributes<HTMLElement>>;
|
|
39
|
+
Placeholder: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
|
|
29
40
|
Footer: import("react").ForwardRefExoticComponent<AppLayoutRegionProps & import("react").RefAttributes<HTMLElement>>;
|
|
30
41
|
};
|
|
42
|
+
export {};
|
package/dist/app-layout.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, forwardRef, useContext } from "react";
|
|
2
|
+
import { createContext, forwardRef, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useState } from "react";
|
|
3
|
+
import { PanelLeft } from "lucide-react";
|
|
4
|
+
import { Button } from "./button.js";
|
|
3
5
|
import { controlFontSizes, controlFontWeight } from "./control/control.js";
|
|
4
6
|
import { headerHeight } from "./foundation/layout.js";
|
|
5
7
|
import { resolveRadius } from "./foundation/radius.js";
|
|
@@ -8,39 +10,69 @@ import { useVisual } from "./foundation/visual.js";
|
|
|
8
10
|
import { ScrollArea } from "./scroll-area.js";
|
|
9
11
|
import { Surface } from "./surface/surface.js";
|
|
10
12
|
import { containerPadding } from "./foundation/spacing.js";
|
|
11
|
-
|
|
13
|
+
import { Drawer } from "./drawer.js";
|
|
14
|
+
/** At this width or less, a sidebar beside the content would crowd it out. */
|
|
15
|
+
export const narrowAppLayoutWidth = 640;
|
|
16
|
+
const AppLayoutContext = createContext(null);
|
|
17
|
+
// Kept apart from the state, so a part that names itself does not render again when it is read.
|
|
18
|
+
const AppLayoutRegistry = createContext(null);
|
|
12
19
|
/**
|
|
13
20
|
* The frame of a program's interface: a title above a sidebar, and a header,
|
|
14
21
|
* the content, and an optional footer beside them. Only the content is a
|
|
15
22
|
* Surface, the same inset region as a Panel's content; the other regions sit
|
|
16
23
|
* on whatever holds the layout, separated by spacing alone.
|
|
24
|
+
*
|
|
25
|
+
* At `narrowAppLayoutWidth` or less it gives the content the whole width: the title and sidebar
|
|
26
|
+
* wait in a Drawer, which `AppLayout.SidebarToggle` opens.
|
|
17
27
|
*/
|
|
18
28
|
const AppLayoutRoot = forwardRef(function AppLayout({ children, sidebarWidth, style, ...properties }, ref) {
|
|
19
29
|
const visual = useVisual();
|
|
20
30
|
const inset = scale(visual.spacing, "small");
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
const [element, setElement] = useState(null);
|
|
32
|
+
const narrow = useNarrow(element);
|
|
33
|
+
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
34
|
+
const [title, setTitle] = useState(null);
|
|
35
|
+
const [sidebarLabel, setSidebarLabel] = useState(undefined);
|
|
36
|
+
const attach = useCallback((value) => { setElement(value); assign(ref, value); }, [ref]);
|
|
37
|
+
// Widening puts the sidebar back beside the content, so its Drawer has nothing left to hold.
|
|
38
|
+
useEffect(() => { if (!narrow)
|
|
39
|
+
setSidebarOpen(false); }, [narrow]);
|
|
40
|
+
const state = useMemo(() => ({ narrow, sidebarOpen, setSidebarOpen, title, sidebarLabel }), [narrow, sidebarOpen, title, sidebarLabel]);
|
|
41
|
+
const registry = useMemo(() => ({ setTitle, setSidebarLabel }), []);
|
|
42
|
+
return _jsx(AppLayoutRegistry.Provider, { value: registry, children: _jsx(AppLayoutContext.Provider, { value: state, children: _jsx("div", { ...properties, ref: attach, style: {
|
|
43
|
+
position: "relative",
|
|
44
|
+
display: "grid",
|
|
45
|
+
gridTemplateColumns: narrow ? "minmax(0, 1fr)" : `${typeof sidebarWidth === "number" ? `${sidebarWidth}px` : sidebarWidth ?? `${visual.spacing * 18}px`} minmax(0, 1fr)`,
|
|
46
|
+
gridTemplateRows: "auto minmax(0, 1fr) auto",
|
|
47
|
+
gridTemplateAreas: narrow ? `"header" "content" "footer"` : `"title header" "sidebar content" "sidebar footer"`,
|
|
48
|
+
columnGap: narrow ? 0 : inset,
|
|
49
|
+
// The header row supplies the space above the content, as in a Panel.
|
|
50
|
+
padding: inset,
|
|
51
|
+
paddingTop: 0,
|
|
52
|
+
boxSizing: "border-box",
|
|
53
|
+
width: "100%",
|
|
54
|
+
height: "100%",
|
|
55
|
+
minWidth: 0,
|
|
56
|
+
minHeight: 0,
|
|
57
|
+
// The frame of a program: its text takes the Appearance text color.
|
|
58
|
+
color: visual.colors.foreground,
|
|
59
|
+
fontFamily: "inherit",
|
|
60
|
+
...style
|
|
61
|
+
}, children: children }) }) });
|
|
40
62
|
});
|
|
41
|
-
|
|
42
|
-
|
|
63
|
+
const visuallyHidden = {
|
|
64
|
+
position: "absolute", width: 1, height: 1, margin: -1, padding: 0, border: 0,
|
|
65
|
+
overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap"
|
|
66
|
+
};
|
|
67
|
+
/** The program's title above the sidebar, in the header row; in a narrow layout, atop its Drawer. */
|
|
68
|
+
const AppLayoutTitle = forwardRef(function AppLayoutTitle({ style, children, ...properties }, ref) {
|
|
43
69
|
const { spacing } = useLayout();
|
|
70
|
+
const { narrow } = useLayoutState();
|
|
71
|
+
const { setTitle } = useRegistry();
|
|
72
|
+
useLayoutEffect(() => { setTitle(children); return () => setTitle(null); }, [children, setTitle]);
|
|
73
|
+
// Out of sight while narrow, but still the page's heading, so what is named by it keeps its name.
|
|
74
|
+
if (narrow)
|
|
75
|
+
return _jsx("h1", { ...properties, ref: ref, style: visuallyHidden, children: children });
|
|
44
76
|
return _jsx("h1", { ...properties, ref: ref, style: {
|
|
45
77
|
gridArea: "title",
|
|
46
78
|
display: "flex",
|
|
@@ -52,12 +84,22 @@ const AppLayoutTitle = forwardRef(function AppLayoutTitle({ style, ...properties
|
|
|
52
84
|
fontSize: controlFontSizes.large,
|
|
53
85
|
fontWeight: controlFontWeight,
|
|
54
86
|
...style
|
|
55
|
-
} });
|
|
87
|
+
}, children: children });
|
|
56
88
|
});
|
|
57
|
-
/**
|
|
89
|
+
/**
|
|
90
|
+
* The sidebar, such as a program's navigation. It scrolls on its own, above its footer. In a
|
|
91
|
+
* narrow layout it waits in a Drawer under the title; choosing something in it is the program's to
|
|
92
|
+
* follow with `useAppLayout().closeSidebar()`.
|
|
93
|
+
*/
|
|
58
94
|
const AppLayoutSidebar = forwardRef(function AppLayoutSidebar({ children, footer, style, ...properties }, ref) {
|
|
59
95
|
const { spacing } = useLayout();
|
|
96
|
+
const { narrow, sidebarOpen, setSidebarOpen, title } = useLayoutState();
|
|
97
|
+
const { setSidebarLabel } = useRegistry();
|
|
60
98
|
const padding = scale(spacing, "small");
|
|
99
|
+
const label = properties["aria-label"];
|
|
100
|
+
useLayoutEffect(() => { setSidebarLabel(label); return () => setSidebarLabel(undefined); }, [label, setSidebarLabel]);
|
|
101
|
+
if (narrow)
|
|
102
|
+
return _jsxs(Drawer, { open: sidebarOpen, onClose: () => setSidebarOpen(false), title: title, "aria-label": label, children: [children, footer != null && _jsx("div", { style: { paddingTop: padding }, children: footer })] });
|
|
61
103
|
return _jsxs("aside", { ...properties, ref: ref, style: { gridArea: "sidebar", minHeight: 0, display: "flex", flexDirection: "column", ...style }, children: [_jsx(ScrollArea, { style: { flex: "1 1 auto", minHeight: 0 }, children: _jsx("div", { style: { padding }, children: children }) }), footer != null && _jsx("div", { style: { flex: "none", paddingInline: padding, paddingBottom: padding }, children: footer })] });
|
|
62
104
|
});
|
|
63
105
|
/** The header row above the content, as tall as a Window or Panel header. */
|
|
@@ -73,7 +115,10 @@ const AppLayoutHeader = forwardRef(function AppLayoutHeader({ style, ...properti
|
|
|
73
115
|
...style
|
|
74
116
|
} });
|
|
75
117
|
});
|
|
76
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* The content, on its Surface. It scrolls on its own, padded by the Appearance spacing. It is a size
|
|
120
|
+
* container, so what stands in for the content can fill what shows of it.
|
|
121
|
+
*/
|
|
77
122
|
const AppLayoutContent = forwardRef(function AppLayoutContent({ children, depth = "recessed", style, ...properties }, ref) {
|
|
78
123
|
const { spacing, radius } = useLayout();
|
|
79
124
|
return _jsx(Surface, { as: "main", ...properties, ref: ref, depth: depth, material: "extended", radius: resolveRadius("medium", radius), style: {
|
|
@@ -82,9 +127,26 @@ const AppLayoutContent = forwardRef(function AppLayoutContent({ children, depth
|
|
|
82
127
|
minHeight: 0,
|
|
83
128
|
// The Surface owns its shape: what scrolls inside stays within its corners.
|
|
84
129
|
overflow: "clip",
|
|
130
|
+
containerType: "size",
|
|
85
131
|
...style
|
|
86
132
|
}, children: _jsx(ScrollArea, { style: { height: "100%" }, children: _jsx("div", { style: { padding: containerPadding(spacing) }, children: children }) }) });
|
|
87
133
|
});
|
|
134
|
+
/**
|
|
135
|
+
* What stands in for the content while there is none to show: a wait, what went wrong, or that there
|
|
136
|
+
* is nothing. It stands in the middle of the content, wherever the content would have been.
|
|
137
|
+
*/
|
|
138
|
+
const AppLayoutPlaceholder = forwardRef(function AppLayoutPlaceholder({ style, ...properties }, ref) {
|
|
139
|
+
const { spacing } = useLayout();
|
|
140
|
+
return _jsx("div", { ...properties, ref: ref, style: {
|
|
141
|
+
display: "grid",
|
|
142
|
+
placeItems: "center",
|
|
143
|
+
alignContent: "center",
|
|
144
|
+
gap: scale(spacing, "medium"),
|
|
145
|
+
textAlign: "center",
|
|
146
|
+
minHeight: `calc(100cqh - ${containerPadding(spacing) * 2}px)`,
|
|
147
|
+
...style
|
|
148
|
+
} });
|
|
149
|
+
});
|
|
88
150
|
/** An optional row below the content, such as the actions that apply to it. */
|
|
89
151
|
const AppLayoutFooter = forwardRef(function AppLayoutFooter({ style, ...properties }, ref) {
|
|
90
152
|
const { spacing } = useLayout();
|
|
@@ -99,15 +161,66 @@ const AppLayoutFooter = forwardRef(function AppLayoutFooter({ style, ...properti
|
|
|
99
161
|
...style
|
|
100
162
|
} });
|
|
101
163
|
});
|
|
102
|
-
|
|
103
|
-
|
|
164
|
+
/** In a narrow layout, the button that opens the sidebar's Drawer; otherwise nothing. */
|
|
165
|
+
function AppLayoutSidebarToggle() {
|
|
166
|
+
const { narrow, sidebarOpen, setSidebarOpen, sidebarLabel } = useLayoutState();
|
|
167
|
+
if (!narrow)
|
|
168
|
+
return null;
|
|
169
|
+
return _jsx(Button, { iconOnly: true, depth: "flat", size: "small", "aria-label": sidebarLabel ?? "Sidebar", "aria-expanded": sidebarOpen, onPress: () => setSidebarOpen(!sidebarOpen), children: _jsx(PanelLeft, {}) });
|
|
170
|
+
}
|
|
171
|
+
/** Whether the nearest AppLayout is narrow, and closing its sidebar's Drawer, such as after a choice in it. */
|
|
172
|
+
export function useAppLayout() {
|
|
173
|
+
const { narrow, setSidebarOpen } = useLayoutState();
|
|
174
|
+
const closeSidebar = useCallback(() => setSidebarOpen(false), [setSidebarOpen]);
|
|
175
|
+
return useMemo(() => ({ narrow, closeSidebar }), [narrow, closeSidebar]);
|
|
176
|
+
}
|
|
177
|
+
function useLayoutState() {
|
|
178
|
+
const state = useContext(AppLayoutContext);
|
|
179
|
+
if (!state)
|
|
180
|
+
throw new Error("AppLayout parts must be used inside AppLayout");
|
|
181
|
+
return state;
|
|
182
|
+
}
|
|
183
|
+
function useRegistry() {
|
|
184
|
+
const registry = useContext(AppLayoutRegistry);
|
|
185
|
+
if (!registry)
|
|
104
186
|
throw new Error("AppLayout parts must be used inside AppLayout");
|
|
187
|
+
return registry;
|
|
188
|
+
}
|
|
189
|
+
function useLayout() {
|
|
190
|
+
useLayoutState();
|
|
105
191
|
return useVisual();
|
|
106
192
|
}
|
|
193
|
+
/** Follows whether the layout itself, not the window, is narrow. */
|
|
194
|
+
function useNarrow(element) {
|
|
195
|
+
const [narrow, setNarrow] = useState(false);
|
|
196
|
+
useLayoutEffect(() => {
|
|
197
|
+
if (!element)
|
|
198
|
+
return;
|
|
199
|
+
// A width of 0 is a layout not laid out yet, not a narrow one.
|
|
200
|
+
const follow = (width) => { if (width > 0)
|
|
201
|
+
setNarrow(width <= narrowAppLayoutWidth); };
|
|
202
|
+
follow(element.getBoundingClientRect().width);
|
|
203
|
+
if (typeof ResizeObserver !== "function")
|
|
204
|
+
return;
|
|
205
|
+
const observer = new ResizeObserver(([entry]) => { if (entry)
|
|
206
|
+
follow(entry.contentRect.width); });
|
|
207
|
+
observer.observe(element);
|
|
208
|
+
return () => observer.disconnect();
|
|
209
|
+
}, [element]);
|
|
210
|
+
return narrow;
|
|
211
|
+
}
|
|
212
|
+
function assign(ref, value) {
|
|
213
|
+
if (typeof ref === "function")
|
|
214
|
+
ref(value);
|
|
215
|
+
else if (ref)
|
|
216
|
+
ref.current = value;
|
|
217
|
+
}
|
|
107
218
|
export const AppLayout = Object.assign(AppLayoutRoot, {
|
|
108
219
|
Title: AppLayoutTitle,
|
|
109
220
|
Sidebar: AppLayoutSidebar,
|
|
221
|
+
SidebarToggle: AppLayoutSidebarToggle,
|
|
110
222
|
Header: AppLayoutHeader,
|
|
111
223
|
Content: AppLayoutContent,
|
|
224
|
+
Placeholder: AppLayoutPlaceholder,
|
|
112
225
|
Footer: AppLayoutFooter
|
|
113
226
|
});
|
package/dist/color-field.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface ColorFieldProps extends Omit<AriaColorFieldProps, ControlOverri
|
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* A color typed as text: a hex value, or one channel when `channel` is set.
|
|
15
|
-
* Inside a ColorPicker it edits the picker's color.
|
|
15
|
+
* Inside a ColorPicker it edits the picker's color. A hex value applies as soon
|
|
16
|
+
* as it is a whole color, as the picker's other parts apply while they move,
|
|
17
|
+
* not only when the field is left.
|
|
16
18
|
*/
|
|
17
19
|
export declare const ColorField: import("react").ForwardRefExoticComponent<ColorFieldProps & import("react").RefAttributes<HTMLInputElement>>;
|
package/dist/color-field.js
CHANGED
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef } from "react";
|
|
3
|
-
import { ColorField as AriaColorField, Input as AriaInput } from "react-aria-components";
|
|
2
|
+
import { forwardRef, useContext } from "react";
|
|
3
|
+
import { ColorField as AriaColorField, ColorPickerStateContext, Input as AriaInput, parseColor } from "react-aria-components";
|
|
4
4
|
import { colorValue } from "./control/color-value.js";
|
|
5
5
|
import { useControlMetrics } from "./control/control.js";
|
|
6
6
|
import { FieldFeedback, FieldLabel, fieldStyle } from "./control/field.js";
|
|
7
7
|
import { nativeTextStyle, TextWell } from "./control/text-control.js";
|
|
8
8
|
import MotionStyle, { textControlClass } from "./foundation/motion-style.js";
|
|
9
9
|
import { dimmedClass } from "./surface/surface.js";
|
|
10
|
+
/**
|
|
11
|
+
* A whole six-digit hex color, with or without its `#`. A three-digit one is
|
|
12
|
+
* also the start of a six-digit one, so it waits until the field is left.
|
|
13
|
+
*/
|
|
14
|
+
const completeHex = /^#?[\da-f]{6}$/i;
|
|
10
15
|
/**
|
|
11
16
|
* A color typed as text: a hex value, or one channel when `channel` is set.
|
|
12
|
-
* Inside a ColorPicker it edits the picker's color.
|
|
17
|
+
* Inside a ColorPicker it edits the picker's color. A hex value applies as soon
|
|
18
|
+
* as it is a whole color, as the picker's other parts apply while they move,
|
|
19
|
+
* not only when the field is left.
|
|
13
20
|
*/
|
|
14
21
|
export const ColorField = forwardRef(function ColorField({ label, description, errorMessage, disabled, readOnly, required, invalid, value, defaultValue, onChange, size, color = "background", radius, style, className, placeholder, material, ...properties }, ref) {
|
|
15
22
|
const metrics = useControlMetrics(size, radius);
|
|
16
|
-
|
|
23
|
+
const picker = useContext(ColorPickerStateContext);
|
|
24
|
+
function typed(text) {
|
|
25
|
+
if (properties.channel !== undefined || !completeHex.test(text.trim()))
|
|
26
|
+
return;
|
|
27
|
+
const next = parseColor(text.trim().startsWith("#") ? text.trim() : `#${text.trim()}`);
|
|
28
|
+
if (picker)
|
|
29
|
+
picker.setColor(next);
|
|
30
|
+
else
|
|
31
|
+
onChange?.(colorValue(next));
|
|
32
|
+
}
|
|
33
|
+
return _jsxs(AriaColorField, { ...properties, ...(value !== undefined ? { value } : {}), ...(defaultValue !== undefined ? { defaultValue } : {}), ...(onChange !== undefined ? { onChange: (next) => onChange(next === null ? null : colorValue(next)) } : {}), className: dimmedClass(disabled ?? false, className), isDisabled: disabled, isReadOnly: readOnly, isRequired: required, isInvalid: invalid, style: fieldStyle(metrics, style), children: [_jsx(MotionStyle, {}), _jsx(FieldLabel, { label: label }), _jsx(AriaInput, { ref: ref, placeholder: placeholder, className: textControlClass, render: (native, state) => _jsx(TextWell, { color: color, material: material, metrics: metrics, state: state, children: _jsx("input", { ...native, onChange: event => { native.onChange?.(event); typed(event.currentTarget.value); } }) }), style: { ...nativeTextStyle(metrics, false), fontVariantNumeric: "tabular-nums" } }), _jsx(FieldFeedback, { metrics: metrics, description: description, errorMessage: errorMessage })] });
|
|
17
34
|
});
|
package/dist/dialog.d.ts
CHANGED
|
@@ -21,13 +21,7 @@ export interface DialogBackdropProps extends Omit<AriaModalOverlayProps, AriaOve
|
|
|
21
21
|
/** Whether Escape closes the Dialog. */
|
|
22
22
|
readonly keyboardDismissable?: boolean;
|
|
23
23
|
readonly style?: CSSProperties;
|
|
24
|
-
/**
|
|
25
|
-
* How the interface behind the Dialog is held back: `dim` darkens it, and
|
|
26
|
-
* `blur` blurs it with the Appearance material while darkening it lightly.
|
|
27
|
-
*/
|
|
28
|
-
readonly variant?: DialogBackdropVariant;
|
|
29
24
|
}
|
|
30
|
-
export type DialogBackdropVariant = "dim" | "blur";
|
|
31
25
|
export declare const DialogBackdrop: import("react").ForwardRefExoticComponent<DialogBackdropProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
32
26
|
export interface DialogContentProps extends Omit<AriaDialogProps, "children" | "className" | "style">, SurfaceOwnProps {
|
|
33
27
|
readonly children?: AriaDialogProps["children"];
|
package/dist/dialog.js
CHANGED
|
@@ -18,21 +18,25 @@ export function DialogRoot({ children, ...state }) {
|
|
|
18
18
|
export const DialogTrigger = forwardRef(function DialogTrigger(properties, ref) {
|
|
19
19
|
return _jsx(Button, { ...properties, ref: ref });
|
|
20
20
|
});
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
/** How far every backdrop darkens the interface behind a Dialog. */
|
|
22
|
+
const backdropDimming = 0.5;
|
|
23
|
+
/** The share of the Appearance material's blur a backdrop takes: lighter than a frosted Surface. */
|
|
24
|
+
const backdropBlurShare = 0.5;
|
|
25
|
+
export const DialogBackdrop = forwardRef(function DialogBackdrop({ children, className, dir, dismissable = false, keyboardDismissable = true, style, ...attributes }, ref) {
|
|
25
26
|
const visual = useVisual();
|
|
26
27
|
const inset = scale(visual.spacing, "medium");
|
|
27
28
|
const colors = visual.colors;
|
|
28
29
|
// The backdrop dims toward whichever Appearance color is darker, in any Theme.
|
|
29
30
|
const darker = darkCanvas(colors) ? colors.background : colors.foreground;
|
|
30
|
-
const backdrop = colorOpacity(darker, backdropDimming
|
|
31
|
-
|
|
31
|
+
const backdrop = colorOpacity(darker, backdropDimming);
|
|
32
|
+
// Where the material blurs, the backdrop blurs too, more lightly; without it, it only dims.
|
|
33
|
+
const blur = visual.material.backdrop > 0 ? `blur(${visual.material.backdrop * backdropBlurShare}px)` : undefined;
|
|
32
34
|
const transition = overlayTransition(visual);
|
|
33
35
|
const direction = resolveDirection(dir, useDirection());
|
|
34
36
|
return _jsxs(_Fragment, { children: [_jsx(MotionStyle, {}), _jsx(AriaModalOverlay, { ...attributes, isDismissable: dismissable, isKeyboardDismissDisabled: !keyboardDismissable, ref: ref, dir: direction, className: [backdropMotionClass, className].filter(Boolean).join(" "), style: {
|
|
35
37
|
...transition,
|
|
38
|
+
backdropFilter: blur,
|
|
39
|
+
WebkitBackdropFilter: blur,
|
|
36
40
|
position: "fixed",
|
|
37
41
|
inset: 0,
|
|
38
42
|
zIndex: overlayLayer,
|
|
@@ -42,8 +46,6 @@ export const DialogBackdrop = forwardRef(function DialogBackdrop({ children, cla
|
|
|
42
46
|
padding: inset,
|
|
43
47
|
overflow: "auto",
|
|
44
48
|
background: backdrop,
|
|
45
|
-
backdropFilter: blur,
|
|
46
|
-
WebkitBackdropFilter: blur,
|
|
47
49
|
...style
|
|
48
50
|
}, children: children })] });
|
|
49
51
|
});
|
package/dist/drawer.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface DrawerProps {
|
|
|
16
16
|
* outside or Escape closes it. It slides in and back out along the same path, timed by the
|
|
17
17
|
* motion of a change in place, the way out its own animation so its end is seen; without motion it
|
|
18
18
|
* simply shows and goes. Only its position moves:
|
|
19
|
-
* fading it would switch off the blur it draws of what is behind it.
|
|
19
|
+
* fading it would switch off the blur it draws of what is behind it. The surface itself never
|
|
20
|
+
* scrolls, so its material stays behind all of it; what it holds scrolls inside it.
|
|
20
21
|
*/
|
|
21
22
|
export declare function Drawer({ open, onClose, title, className, style, children, ...properties }: DrawerProps): import("react").JSX.Element | null;
|
package/dist/drawer.js
CHANGED
|
@@ -4,6 +4,7 @@ import { controlFontSizes, controlFontWeight } from "./control/control.js";
|
|
|
4
4
|
import MotionStyle, { overlayTransition } from "./foundation/motion-style.js";
|
|
5
5
|
import { scale } from "./foundation/scale.js";
|
|
6
6
|
import { useVisual } from "./foundation/visual.js";
|
|
7
|
+
import { ScrollArea } from "./scroll-area.js";
|
|
7
8
|
import { Surface } from "./surface/surface.js";
|
|
8
9
|
/**
|
|
9
10
|
* A narrow layout's sidebar, over its content from the start side, such as the navigation a narrow
|
|
@@ -11,7 +12,8 @@ import { Surface } from "./surface/surface.js";
|
|
|
11
12
|
* outside or Escape closes it. It slides in and back out along the same path, timed by the
|
|
12
13
|
* motion of a change in place, the way out its own animation so its end is seen; without motion it
|
|
13
14
|
* simply shows and goes. Only its position moves:
|
|
14
|
-
* fading it would switch off the blur it draws of what is behind it.
|
|
15
|
+
* fading it would switch off the blur it draws of what is behind it. The surface itself never
|
|
16
|
+
* scrolls, so its material stays behind all of it; what it holds scrolls inside it.
|
|
15
17
|
*/
|
|
16
18
|
export function Drawer({ open, onClose, title, className, style, children, ...properties }) {
|
|
17
19
|
const visual = useVisual();
|
|
@@ -47,7 +49,9 @@ export function Drawer({ open, onClose, title, className, style, children, ...pr
|
|
|
47
49
|
boxSizing: "border-box",
|
|
48
50
|
width: `min(${visual.spacing * 20}px, calc(100% - ${visual.spacing * 4}px))`,
|
|
49
51
|
padding: inset,
|
|
50
|
-
|
|
52
|
+
display: "flex",
|
|
53
|
+
flexDirection: "column",
|
|
54
|
+
overflow: "hidden",
|
|
51
55
|
...style
|
|
52
|
-
}, children: [title != null && _jsx("div", { style: { padding: `${inset}px ${inset}px ${visual.spacing}px`, fontSize: controlFontSizes.xlarge, fontWeight: controlFontWeight }, children: title }), children] })] });
|
|
56
|
+
}, children: [title != null && _jsx("div", { style: { flex: "none", padding: `${inset}px ${inset}px ${visual.spacing}px`, fontSize: controlFontSizes.xlarge, fontWeight: controlFontWeight }, children: title }), _jsx(ScrollArea, { style: { flex: "1 1 auto", minHeight: 0 }, children: children })] })] });
|
|
53
57
|
}
|
package/dist/grid-list.d.ts
CHANGED
|
@@ -12,6 +12,11 @@ type GridListRootBaseProps<T extends object> = Omit<AriaGridListProps<T>, "class
|
|
|
12
12
|
restColor?: Color;
|
|
13
13
|
/** The narrowest an Item may be; the grid fits as many columns as that allows. Sixteen times the spacing by default. */
|
|
14
14
|
itemWidth?: CSSProperties["width"];
|
|
15
|
+
/**
|
|
16
|
+
* Whether fewer Items than fit share the whole width. Off by default, so an open collection keeps
|
|
17
|
+
* its Item width whatever it holds; on for a fixed set, such as a row of counts.
|
|
18
|
+
*/
|
|
19
|
+
stretch?: boolean;
|
|
15
20
|
size?: ScaleLevel;
|
|
16
21
|
style?: CSSProperties;
|
|
17
22
|
}>;
|
package/dist/grid-list.js
CHANGED
|
@@ -10,11 +10,12 @@ import { resolveDirection, useDirection } from "./foundation/direction.js";
|
|
|
10
10
|
import { collectionHeaderStyle } from "./list-box.js";
|
|
11
11
|
const GridListStyleContext = createContext(null);
|
|
12
12
|
const GridListRootImplementation = forwardRef(function GridList(properties, ref) {
|
|
13
|
-
const { className, color = "primary", restColor = "default", defaultValue: _defaultValue, itemWidth, onChange: _onChange, radius, selectionMode: _selectionMode, size, style, value: _value, ...native } = properties;
|
|
13
|
+
const { className, color = "primary", restColor = "default", defaultValue: _defaultValue, itemWidth, onChange: _onChange, radius, selectionMode: _selectionMode, size, stretch = false, style, value: _value, ...native } = properties;
|
|
14
14
|
const metrics = useControlMetrics(size, radius);
|
|
15
15
|
const width = itemWidth ?? metrics.visual.spacing * 16;
|
|
16
16
|
// As many columns as fit, each at least the Item width; a Section repeats the same columns.
|
|
17
|
-
|
|
17
|
+
// Stretched, the columns no Item fills fold away and the rest share their width.
|
|
18
|
+
const columns = `repeat(${stretch ? "auto-fit" : "auto-fill"}, minmax(min(${typeof width === "number" ? `${width}px` : width}, 100%), 1fr))`;
|
|
18
19
|
const context = useMemo(() => ({ color, restColor, metrics, columns }), [color, restColor, metrics, columns]);
|
|
19
20
|
const direction = resolveDirection(native.dir, useDirection());
|
|
20
21
|
return _jsx(GridListStyleContext.Provider, { value: context, children: _jsx(AriaDirectionBoundary, { direction: direction, children: _jsx(AriaGridList, { ...native, ...ariaSelection(properties, "single"), ref: ref, dir: direction, layout: "grid", className: className, style: state => ({
|
package/dist/main.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { useDirection, useDocumentDirection, type Direction } from "./foundation
|
|
|
13
13
|
export type { Preferences, PreferencesUpdate, Theme } from "./foundation/preferences.js";
|
|
14
14
|
export { Flex, type FlexProps } from "./flex.js";
|
|
15
15
|
export { Grid, type GridProps } from "./grid.js";
|
|
16
|
-
export { Surface, type MaterialMode, type MaterialOptions, type ShadowOptions, type SurfaceComponent, type SurfaceDepth, type SurfaceHost, type SurfaceHostProps, type SurfaceOwnProps, type SurfaceProps } from "./surface/surface.js";
|
|
16
|
+
export { Surface, useSurfaceColor, type MaterialMode, type MaterialOptions, type ShadowOptions, type SurfaceComponent, type SurfaceDepth, type SurfaceHost, type SurfaceHostProps, type SurfaceOwnProps, type SurfaceProps } from "./surface/surface.js";
|
|
17
17
|
export { resolveColor, useColor, useContrastingColor, type Color, type ColorLevel, type ColorScale } from "./foundation/color.js";
|
|
18
18
|
export { Panel, type PanelProps, type PanelHeaderProps, type PanelContentProps } from "./panel.js";
|
|
19
19
|
export { Window, type WindowProps, type WindowContentProps, type WindowHeaderProps, type WindowIdentityProps, type WindowCenterProps, type WindowActionsProps, type WindowActionProps, type WindowControlProps, type WindowMaximizeProps, type WindowCloseProps, type WindowMoveCaptureProps } from "./window.js";
|
|
@@ -62,7 +62,7 @@ export { Loading, type LoadingProps } from "./loading.js";
|
|
|
62
62
|
export { FileTrigger, type FileTriggerProps } from "./file-trigger.js";
|
|
63
63
|
export { DropZone, type DropZoneProps } from "./drop-zone.js";
|
|
64
64
|
export { Fieldset, type FieldsetProps } from "./fieldset.js";
|
|
65
|
-
export { AppLayout, type AppLayoutContentProps, type AppLayoutProps, type AppLayoutRegionProps, type AppLayoutSidebarProps, type AppLayoutTitleProps } from "./app-layout.js";
|
|
65
|
+
export { AppLayout, narrowAppLayoutWidth, useAppLayout, type AppLayoutContentProps, type AppLayoutProps, type AppLayoutRegionProps, type AppLayoutSidebarProps, type AppLayoutTitleProps } from "./app-layout.js";
|
|
66
66
|
export { ColorSwatch, type ColorSwatchProps } from "./color-swatch.js";
|
|
67
67
|
export { ColorSwatchPicker, type ColorSwatchPickerProps, type ColorSwatchPickerItemProps } from "./color-swatch-picker.js";
|
|
68
68
|
export { ColorSlider, type ColorSliderProps } from "./color-slider.js";
|
|
@@ -77,7 +77,7 @@ export { Popover, type PopoverProps, type PopoverTriggerProps, type PopoverConte
|
|
|
77
77
|
export { Menu, type MenuProps, type MenuNoSelectionProps, type MenuSingleSelectionProps, type MenuMultipleSelectionProps, type MenuMultipleValue, type MenuItemProps, type MenuSectionProps, type MenuHeaderProps, type MenuSeparatorProps, type MenuSubmenuProps, type MenuSubmenuContentProps } from "./menu.js";
|
|
78
78
|
export { DropdownMenu, type DropdownMenuProps, type DropdownMenuTriggerProps } from "./dropdown-menu.js";
|
|
79
79
|
export { ContextMenu, type ContextMenuProps, type ContextMenuTriggerProps, type ContextMenuContentProps } from "./context-menu.js";
|
|
80
|
-
export { Dialog, type DialogProps, type DialogTriggerProps, type DialogBackdropProps, type
|
|
80
|
+
export { Dialog, type DialogProps, type DialogTriggerProps, type DialogBackdropProps, type DialogContentProps, type DialogHeaderProps, type DialogTitleProps, type DialogDescriptionProps, type DialogBodyProps, type DialogFooterProps, type DialogCloseProps } from "./dialog.js";
|
|
81
81
|
export { AlertDialog, type AlertDialogProps, type AlertDialogBackdropProps, type AlertDialogContentProps } from "./alert-dialog.js";
|
|
82
82
|
export { Tooltip, type TooltipProps, type TooltipTriggerProps, type TooltipContentProps } from "./tooltip.js";
|
|
83
83
|
export type { LayoutAlignment, LayoutGap, LayoutJustification } from "./foundation/layout.js";
|
package/dist/main.js
CHANGED
|
@@ -12,7 +12,7 @@ export { timing } from "./foundation/timing.js";
|
|
|
12
12
|
export { useDirection, useDocumentDirection } from "./foundation/direction.js";
|
|
13
13
|
export { Flex } from "./flex.js";
|
|
14
14
|
export { Grid } from "./grid.js";
|
|
15
|
-
export { Surface } from "./surface/surface.js";
|
|
15
|
+
export { Surface, useSurfaceColor } from "./surface/surface.js";
|
|
16
16
|
export { resolveColor, useColor, useContrastingColor } from "./foundation/color.js";
|
|
17
17
|
// Context-dependent parts are public only through their owning component family.
|
|
18
18
|
export { Panel } from "./panel.js";
|
|
@@ -61,7 +61,7 @@ export { Loading } from "./loading.js";
|
|
|
61
61
|
export { FileTrigger } from "./file-trigger.js";
|
|
62
62
|
export { DropZone } from "./drop-zone.js";
|
|
63
63
|
export { Fieldset } from "./fieldset.js";
|
|
64
|
-
export { AppLayout } from "./app-layout.js";
|
|
64
|
+
export { AppLayout, narrowAppLayoutWidth, useAppLayout } from "./app-layout.js";
|
|
65
65
|
export { ColorSwatch } from "./color-swatch.js";
|
|
66
66
|
export { ColorSwatchPicker } from "./color-swatch-picker.js";
|
|
67
67
|
export { ColorSlider } from "./color-slider.js";
|
|
@@ -71,6 +71,15 @@ export declare const interactionShift: Readonly<{
|
|
|
71
71
|
}>;
|
|
72
72
|
/** A clear Surface has no paint to move, so it shows the same shift as a slightly stronger veil. */
|
|
73
73
|
export declare const clearVeil = 1.3;
|
|
74
|
+
/**
|
|
75
|
+
* The color a Surface of this color and depth is painted with at rest, before its material: a recess
|
|
76
|
+
* sinks below the color it is given; a clear Surface, with no depth or a transparent color, paints
|
|
77
|
+
* nothing. Whatever draws on a Surface and must match it, such as a terminal telling its programs
|
|
78
|
+
* what it stands on, takes this color.
|
|
79
|
+
*/
|
|
80
|
+
export declare function surfaceColor(color: Color, depth: SurfaceDepth, colors: Visual["colors"]): string;
|
|
81
|
+
/** The color a Surface of this color and depth is painted with at rest, in the active Appearance. */
|
|
82
|
+
export declare function useSurfaceColor(color?: Color, depth?: SurfaceDepth): string;
|
|
74
83
|
/** Resolves every visual value of one Surface. Pure over its inputs. */
|
|
75
84
|
export declare function surfacePaint(visual: Visual, color: Color, depth: SurfaceDepth, options: MaterialMode | MaterialOptions | undefined, shadowOptions: ShadowOverrides["shadow"], interaction: SurfaceInteraction | undefined): {
|
|
76
85
|
fill: string;
|
package/dist/surface/surface.js
CHANGED
|
@@ -67,6 +67,23 @@ const materialLevel = Object.freeze({ none: 0, basic: 1, extended: 2, full: 3 })
|
|
|
67
67
|
export const interactionShift = Object.freeze({ hovered: 0.05, pressed: 0.1 });
|
|
68
68
|
/** A clear Surface has no paint to move, so it shows the same shift as a slightly stronger veil. */
|
|
69
69
|
export const clearVeil = 1.3;
|
|
70
|
+
/**
|
|
71
|
+
* The color a Surface of this color and depth is painted with at rest, before its material: a recess
|
|
72
|
+
* sinks below the color it is given; a clear Surface, with no depth or a transparent color, paints
|
|
73
|
+
* nothing. Whatever draws on a Surface and must match it, such as a terminal telling its programs
|
|
74
|
+
* what it stands on, takes this color.
|
|
75
|
+
*/
|
|
76
|
+
export function surfaceColor(color, depth, colors) {
|
|
77
|
+
const base = resolveColor(color, colors);
|
|
78
|
+
if (depth === "none" || base === "transparent")
|
|
79
|
+
return "transparent";
|
|
80
|
+
return depth === "recessed" ? recessColor(base) : base;
|
|
81
|
+
}
|
|
82
|
+
/** The color a Surface of this color and depth is painted with at rest, in the active Appearance. */
|
|
83
|
+
export function useSurfaceColor(color = "background", depth = "raised") {
|
|
84
|
+
const { colors } = useVisual();
|
|
85
|
+
return surfaceColor(color, depth, colors);
|
|
86
|
+
}
|
|
70
87
|
/** Resolves every visual value of one Surface. Pure over its inputs. */
|
|
71
88
|
export function surfacePaint(visual, color, depth, options, shadowOptions, interaction) {
|
|
72
89
|
const { colors } = visual;
|
|
@@ -78,7 +95,7 @@ export function surfacePaint(visual, color, depth, options, shadowOptions, inter
|
|
|
78
95
|
const darkness = canvasDarkness(colors);
|
|
79
96
|
// A recess moves away from its surroundings toward whichever extreme has
|
|
80
97
|
// room, by one constant perceptual difference on any canvas.
|
|
81
|
-
const rest = depth === "recessed" ?
|
|
98
|
+
const rest = depth === "recessed" ? surfaceColor(color, depth, colors) : base;
|
|
82
99
|
// A clear Surface, with no depth or a transparent color, has no substance of
|
|
83
100
|
// its own: it paints nothing at rest, takes the content color of whatever it
|
|
84
101
|
// sits on, and only reveals interaction as a veil. With no depth, a color
|
package/dist/table.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface TableHeaderProps<T extends object = object> extends Omit<AriaTa
|
|
|
32
32
|
readonly style?: CSSProperties;
|
|
33
33
|
}
|
|
34
34
|
export type TableHeaderComponent = <T extends object = object>(properties: TableHeaderProps<T> & RefAttributes<HTMLTableSectionElement | HTMLDivElement>) => ReactElement | null;
|
|
35
|
-
export interface TableColumnProps extends Omit<AriaColumnProps, "allowsSorting" | "allowsArrowNavigation" | "className" | "id" | "isRowHeader" | "style"> {
|
|
35
|
+
export interface TableColumnProps extends Omit<AriaColumnProps, "allowsSorting" | "allowsArrowNavigation" | "className" | "id" | "isRowHeader" | "style" | "width" | "minWidth" | "maxWidth" | "defaultWidth"> {
|
|
36
36
|
/** Whether arrow keys move between cells while focus is inside this one. */
|
|
37
37
|
readonly arrowNavigation?: boolean;
|
|
38
38
|
/** Identity of this Column, reported in sort changes. */
|
|
@@ -41,6 +41,13 @@ export interface TableColumnProps extends Omit<AriaColumnProps, "allowsSorting"
|
|
|
41
41
|
readonly rowHeader?: boolean;
|
|
42
42
|
/** Whether pressing this Column requests a sort. */
|
|
43
43
|
readonly sortable?: boolean;
|
|
44
|
+
/** The Column's width, in pixels or any CSS length. */
|
|
45
|
+
readonly width?: number | string;
|
|
46
|
+
/**
|
|
47
|
+
* For a Column without a width, which takes the room the others leave: the least it keeps when
|
|
48
|
+
* that room runs out.
|
|
49
|
+
*/
|
|
50
|
+
readonly minWidth?: number | string;
|
|
44
51
|
readonly className?: string;
|
|
45
52
|
readonly style?: CSSProperties;
|
|
46
53
|
}
|
package/dist/table.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, forwardRef, useContext, useMemo } from "react";
|
|
2
|
+
import { createContext, forwardRef, useCallback, useContext, useLayoutEffect, useMemo, useState } from "react";
|
|
3
3
|
import { Cell as AriaCell, Column as AriaColumn, Row as AriaRow, Table as AriaTable, TableBody as AriaTableBody, TableHeader as AriaTableHeader } from "react-aria-components";
|
|
4
4
|
import { controlFontWeight, controlOpacity, transition, useControlMetrics, secondaryFontSize } from "./control/control.js";
|
|
5
5
|
import { separatorColor } from "./control/field.js";
|
|
@@ -16,11 +16,25 @@ const TableStyleContext = createContext(null);
|
|
|
16
16
|
const TableRoot = forwardRef(function Table(properties, ref) {
|
|
17
17
|
const { className, color = "primary", defaultValue: _defaultValue, onAction, onChange: _onChange, onSortChange, radius, selectOnPressUp, selectionMode = "none", size, sort, style, value: _value, ...native } = properties;
|
|
18
18
|
const metrics = useControlMetrics(size, radius);
|
|
19
|
-
|
|
19
|
+
// The least room each Column takes, from its width or its minimum.
|
|
20
|
+
const [least, setLeast] = useState({});
|
|
21
|
+
const measure = useCallback((column, room) => setLeast(current => {
|
|
22
|
+
if ((current[column] ?? null) === room)
|
|
23
|
+
return current;
|
|
24
|
+
const { [column]: _previous, ...rest } = current;
|
|
25
|
+
return room === null ? rest : { ...rest, [column]: room };
|
|
26
|
+
}), []);
|
|
27
|
+
const context = useMemo(() => ({ color, interactive: selectionMode !== "none" || onAction != null, metrics, measure }), [color, metrics, onAction, selectionMode, measure]);
|
|
28
|
+
const rooms = Object.values(least);
|
|
20
29
|
const direction = resolveDirection(native.dir, useDirection());
|
|
21
30
|
return _jsx(TableStyleContext.Provider, { value: context, children: _jsx(AriaDirectionBoundary, { direction: direction, children: _jsx(AriaTable, { ...native, shouldSelectOnPressUp: selectOnPressUp, ...ariaSelection(properties, "none"), ...(sort === undefined ? {} : { sortDescriptor: sort }), ref: ref, dir: direction, className: className, onRowAction: onAction == null ? undefined : key => onAction(stringKey(key)), onSortChange: onSortChange == null ? undefined : descriptor => onSortChange(tableSort(descriptor)), style: state => ({
|
|
22
31
|
width: "100%",
|
|
23
|
-
|
|
32
|
+
// Columns that state their room lay the Table out by it, and the Table never grows narrower
|
|
33
|
+
// than all of it together: past what holds it, it is scrolled, never crushed. Otherwise the
|
|
34
|
+
// Table is as wide as its content.
|
|
35
|
+
...rooms.length
|
|
36
|
+
? { tableLayout: "fixed", minWidth: `calc(${rooms.join(" + ")})` }
|
|
37
|
+
: { minWidth: "max-content" },
|
|
24
38
|
borderCollapse: "separate",
|
|
25
39
|
borderSpacing: 0,
|
|
26
40
|
boxSizing: "border-box",
|
|
@@ -40,9 +54,15 @@ const TableHeaderImplementation = forwardRef(function TableHeader({ className, s
|
|
|
40
54
|
useTableStyle();
|
|
41
55
|
return _jsx(AriaTableHeader, { ...properties, ref: ref, className: className, style: style });
|
|
42
56
|
});
|
|
43
|
-
const TableColumn = forwardRef(function TableColumn({ sortable = false, rowHeader, arrowNavigation, children, className, style, textValue, ...properties }, ref) {
|
|
44
|
-
const { metrics } = useTableStyle();
|
|
57
|
+
const TableColumn = forwardRef(function TableColumn({ sortable = false, rowHeader, arrowNavigation, children, className, style, textValue, width, minWidth, ...properties }, ref) {
|
|
58
|
+
const { metrics, measure } = useTableStyle();
|
|
45
59
|
const { colors } = metrics.visual;
|
|
60
|
+
const room = width ?? minWidth;
|
|
61
|
+
const least = room === undefined ? null : typeof room === "number" ? `${room}px` : room;
|
|
62
|
+
useLayoutEffect(() => {
|
|
63
|
+
measure(properties.id, least);
|
|
64
|
+
return () => measure(properties.id, null);
|
|
65
|
+
}, [measure, properties.id, least]);
|
|
46
66
|
return _jsx(AriaColumn, { ...properties, allowsArrowNavigation: arrowNavigation, ref: ref, isRowHeader: rowHeader, allowsSorting: sortable, className: className, textValue: textValue ?? (typeof children === "string" ? children : undefined), style: state => ({
|
|
47
67
|
...transition(metrics.visual, "color, outline-color"),
|
|
48
68
|
height: metrics.height,
|
|
@@ -56,8 +76,9 @@ const TableColumn = forwardRef(function TableColumn({ sortable = false, rowHeade
|
|
|
56
76
|
textAlign: "start",
|
|
57
77
|
userSelect: "none",
|
|
58
78
|
opacity: state.isHovered && sortable ? 1 : controlOpacity.secondary,
|
|
79
|
+
width,
|
|
59
80
|
...style
|
|
60
|
-
}), children: state => _jsxs("span", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: metrics.gap, minWidth: 0 }, children: [_jsx("span", { style: { minWidth: 0 }, children: typeof children === "function" ? children(state) : children }), sortable && state.sortDirection != null && _jsx(ChevronUp, { ...iconProps(14), style: { ...transition(metrics.visual, "rotate"), flexShrink: 0, rotate: state.sortDirection === "descending" ? "180deg" : "0deg" } })] }) });
|
|
81
|
+
}), children: state => _jsxs("span", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: metrics.gap, minWidth: 0 }, children: [_jsx("span", { style: { minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: typeof children === "function" ? children(state) : children }), sortable && state.sortDirection != null && _jsx(ChevronUp, { ...iconProps(14), style: { ...transition(metrics.visual, "rotate"), flexShrink: 0, rotate: state.sortDirection === "descending" ? "180deg" : "0deg" } })] }) });
|
|
61
82
|
});
|
|
62
83
|
const TableBodyImplementation = forwardRef(function TableBody({ className, style, ...properties }, ref) {
|
|
63
84
|
return _jsx(AriaTableBody, { ...properties, ref: ref, className: className, style: style });
|
|
@@ -93,6 +114,12 @@ const TableCell = forwardRef(function TableCell({ arrowNavigation, className, st
|
|
|
93
114
|
outline: `3px solid ${state.isFocusVisible ? colorOpacity(metrics.visual.colors.primary, 0.34) : "transparent"}`,
|
|
94
115
|
outlineOffset: -3,
|
|
95
116
|
textAlign: "start",
|
|
117
|
+
// A narrow column cuts a cell's text short instead of letting it spill into the next. What
|
|
118
|
+
// the cell holds still paints a focus ring's width past its edge.
|
|
119
|
+
overflow: "clip",
|
|
120
|
+
overflowClipMargin: 3,
|
|
121
|
+
textOverflow: "ellipsis",
|
|
122
|
+
whiteSpace: "nowrap",
|
|
96
123
|
...style
|
|
97
124
|
}) });
|
|
98
125
|
});
|
package/dist/typography.d.ts
CHANGED
|
@@ -19,6 +19,13 @@ export interface TextProps {
|
|
|
19
19
|
readonly tone?: "default" | "secondary";
|
|
20
20
|
/** The control text scale; omitted, the text keeps the size around it. */
|
|
21
21
|
readonly size?: ScaleLevel;
|
|
22
|
+
/**
|
|
23
|
+
* Keeps the text to one line, cut short with an ellipsis where its space ends: for a name beside
|
|
24
|
+
* other things, such as an icon or a badge, in a row too narrow for all of it.
|
|
25
|
+
*/
|
|
26
|
+
readonly truncate?: boolean;
|
|
27
|
+
/** What a pointer resting on it shows, such as the whole of a text cut short. */
|
|
28
|
+
readonly title?: string;
|
|
22
29
|
/** A slot name when the text describes a field or item, such as `description`. */
|
|
23
30
|
readonly slot?: string;
|
|
24
31
|
readonly elementType?: string;
|
package/dist/typography.js
CHANGED
|
@@ -25,11 +25,12 @@ export const Heading = forwardRef(function Heading({ level = 2, size, style, ...
|
|
|
25
25
|
} });
|
|
26
26
|
});
|
|
27
27
|
/** Running text in the tone and scale of the interface. */
|
|
28
|
-
export const Text = forwardRef(function Text({ tone = "default", size, style, ...properties }, ref) {
|
|
28
|
+
export const Text = forwardRef(function Text({ tone = "default", size, truncate = false, style, ...properties }, ref) {
|
|
29
29
|
return _jsx(AriaText, { ...properties, ref: ref, style: {
|
|
30
30
|
fontSize: size === undefined ? undefined : controlFontSizes[size],
|
|
31
31
|
lineHeight: controlLineHeight,
|
|
32
32
|
opacity: tone === "secondary" ? controlOpacity.secondary : undefined,
|
|
33
|
+
...truncate && { display: "block", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
33
34
|
...style
|
|
34
35
|
} });
|
|
35
36
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/react-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "React components for coherent PhreshOS Program interfaces.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@base-ui/react": "^1.7.0",
|
|
59
59
|
"@internationalized/date": "^3.12.3",
|
|
60
|
-
"@phreshos/core": "^0.
|
|
60
|
+
"@phreshos/core": "^0.2.0",
|
|
61
61
|
"colorjs.io": "^0.7.1",
|
|
62
62
|
"lucide-react": "^1.48.0",
|
|
63
63
|
"react-aria": "^3.51.0",
|