@parallelworks/ui 0.54.0 → 0.56.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/README.md +65 -26
- package/dist/access/AccessDrawer.d.ts +30 -19
- package/dist/access/AccessDrawer.js +92 -67
- package/dist/access/AccessList.d.ts +17 -16
- package/dist/access/AccessList.js +218 -114
- package/dist/access/GrantView.d.ts +9 -8
- package/dist/access/GrantView.js +34 -36
- package/dist/access/PrincipalPicker.d.ts +38 -0
- package/dist/access/{GroupPicker.js → PrincipalPicker.js} +173 -80
- package/dist/access/index.d.ts +1 -1
- package/dist/access/model.d.ts +67 -24
- package/dist/access/model.js +111 -47
- package/dist/access/strings.d.ts +63 -39
- package/dist/access/strings.js +114 -84
- package/dist/access/types.d.ts +32 -14
- package/dist/ai/adapter/mock/mockAdapter.js +1 -1
- package/dist/ai/core/useSSEStream.d.ts +1 -1
- package/dist/components/Checkbox.js +1 -1
- package/dist/components/Combobox.js +25 -20
- package/dist/components/DatePicker.js +62 -58
- package/dist/components/Drawer.js +3 -1
- package/dist/components/Field.d.ts +8 -3
- package/dist/components/Field.js +32 -12
- package/dist/components/HoverCard.js +15 -10
- package/dist/components/InfoTip.d.ts +3 -1
- package/dist/components/InfoTip.js +2 -1
- package/dist/components/Menu.js +3 -0
- package/dist/components/Modal.js +32 -27
- package/dist/components/Popover.d.ts +4 -1
- package/dist/components/Popover.js +31 -26
- package/dist/components/Radio.js +1 -1
- package/dist/components/Select.js +47 -42
- package/dist/components/Slider.js +3 -3
- package/dist/components/Switch.js +1 -1
- package/dist/components/Tooltip.js +16 -11
- package/dist/components/fieldBox.js +1 -1
- package/dist/components/fullscreen.d.ts +4 -0
- package/dist/components/fullscreen.js +18 -0
- package/dist/editor/EditorField.js +1 -2
- package/dist/editor/Monaco.js +26 -2
- package/dist/editor/themes.js +1 -1
- package/dist/graph/GraphEditorDialogs.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/styles/theme.css +10 -10
- package/dist/styles.css +1 -1
- package/dist/theme/derive.d.ts +2 -0
- package/dist/theme/derive.js +25 -14
- package/package.json +2 -2
- package/dist/access/GroupPicker.d.ts +0 -35
|
@@ -17,9 +17,14 @@ export declare const FieldRowContext: import("react").Context<boolean>;
|
|
|
17
17
|
/** The surrounding Field's control props, for a control drawn further down than the Field's
|
|
18
18
|
* own children. Null outside a Field. */
|
|
19
19
|
export declare function useFieldControl(): FieldControlProps | null;
|
|
20
|
-
/** The
|
|
21
|
-
*
|
|
22
|
-
|
|
20
|
+
/** The surrounding Field's control props for a control that isn't a Base UI one, such as a
|
|
21
|
+
* code editor, a button that opens a picker or an `<output>`, which the label's `for` can't
|
|
22
|
+
* name. The props name it by the label's id instead, and while it is mounted the label drops
|
|
23
|
+
* its `for`, which would point at no element, and focuses the control by its id when clicked.
|
|
24
|
+
* Null outside a Field. */
|
|
25
|
+
export declare function useFieldLabelledControl(): (FieldControlProps & {
|
|
26
|
+
'aria-labelledby': string;
|
|
27
|
+
}) | null;
|
|
23
28
|
export type FieldLayout = 'stacked' | 'inline';
|
|
24
29
|
export interface FieldProps {
|
|
25
30
|
label: ReactNode;
|
package/dist/components/Field.js
CHANGED
|
@@ -5,7 +5,7 @@ import { focusCameFromKeyboard, trackFocusModality } from "./focus.js";
|
|
|
5
5
|
import { InfoTip, formatInfoTipContent } from "./InfoTip.js";
|
|
6
6
|
import { RequiredMark } from "./RequiredMark.js";
|
|
7
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
-
import { createContext, use, useEffect, useId, useState } from "react";
|
|
8
|
+
import { createContext, use, useCallback, useEffect, useId, useLayoutEffect, useState } from "react";
|
|
9
9
|
import { Field } from "@base-ui/react/field";
|
|
10
10
|
import { Fieldset } from "@base-ui/react/fieldset";
|
|
11
11
|
//#region src/components/Field.tsx
|
|
@@ -19,10 +19,19 @@ var FieldRowContext = createContext(false);
|
|
|
19
19
|
function useFieldControl() {
|
|
20
20
|
return use(ControlContext)?.control ?? null;
|
|
21
21
|
}
|
|
22
|
-
/** The
|
|
23
|
-
*
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
/** The surrounding Field's control props for a control that isn't a Base UI one, such as a
|
|
23
|
+
* code editor, a button that opens a picker or an `<output>`, which the label's `for` can't
|
|
24
|
+
* name. The props name it by the label's id instead, and while it is mounted the label drops
|
|
25
|
+
* its `for`, which would point at no element, and focuses the control by its id when clicked.
|
|
26
|
+
* Null outside a Field. */
|
|
27
|
+
function useFieldLabelledControl() {
|
|
28
|
+
const context = use(ControlContext);
|
|
29
|
+
const claim = context?.claimLabel;
|
|
30
|
+
useLayoutEffect(() => claim?.(), [claim]);
|
|
31
|
+
return context ? {
|
|
32
|
+
...context.control,
|
|
33
|
+
"aria-labelledby": context.labelId
|
|
34
|
+
} : null;
|
|
26
35
|
}
|
|
27
36
|
var labelSizes = {
|
|
28
37
|
sm: {
|
|
@@ -75,23 +84,31 @@ function Field$1({ label, hint, help, actions, description, error, invalid, requ
|
|
|
75
84
|
...isInvalid && { "aria-invalid": true },
|
|
76
85
|
...required && { "aria-required": true }
|
|
77
86
|
};
|
|
87
|
+
const [labelledBy, setLabelledBy] = useState(0);
|
|
78
88
|
const controlContext = {
|
|
79
89
|
control,
|
|
80
|
-
labelId
|
|
90
|
+
labelId,
|
|
91
|
+
claimLabel: useCallback(() => {
|
|
92
|
+
setLabelledBy((n) => n + 1);
|
|
93
|
+
return () => setLabelledBy((n) => n - 1);
|
|
94
|
+
}, [])
|
|
81
95
|
};
|
|
82
96
|
const inline = layout === "inline";
|
|
83
97
|
const inRow = use(FieldRowContext) && !inline && !group;
|
|
84
98
|
const labelSize = labelSizes[size];
|
|
85
99
|
const [helpOpen, setHelpOpen] = useState(false);
|
|
100
|
+
const [controlBox, setControlBox] = useState(null);
|
|
86
101
|
useEffect(() => {
|
|
87
102
|
if (help) trackFocusModality();
|
|
88
103
|
}, [help]);
|
|
104
|
+
const besideControl = helpOpen === "focus";
|
|
89
105
|
const helpTip = help && /* @__PURE__ */ jsx(InfoTip, {
|
|
90
106
|
text: help,
|
|
91
107
|
describesControl: true,
|
|
92
108
|
open: Boolean(helpOpen),
|
|
93
109
|
onOpenChange: (open) => setHelpOpen(open && "pointer"),
|
|
94
|
-
|
|
110
|
+
side: besideControl ? "right" : "top",
|
|
111
|
+
anchor: besideControl ? controlBox : void 0
|
|
95
112
|
});
|
|
96
113
|
const helpFocus = help ? {
|
|
97
114
|
onFocus: () => {
|
|
@@ -119,6 +136,7 @@ function Field$1({ label, hint, help, actions, description, error, invalid, requ
|
|
|
119
136
|
] });
|
|
120
137
|
const placed = typeof children === "function" ? children(control) : children;
|
|
121
138
|
const body = /* @__PURE__ */ jsxs("div", {
|
|
139
|
+
ref: help ? setControlBox : void 0,
|
|
122
140
|
className: cx("flex w-full min-w-0 flex-col gap-1", inRow && "row-start-2"),
|
|
123
141
|
...helpFocus,
|
|
124
142
|
children: [
|
|
@@ -180,7 +198,10 @@ function Field$1({ label, hint, help, actions, description, error, invalid, requ
|
|
|
180
198
|
className: labelBox,
|
|
181
199
|
children: [
|
|
182
200
|
/* @__PURE__ */ jsxs(Field.Label, {
|
|
183
|
-
...
|
|
201
|
+
...labelledBy > 0 ? {
|
|
202
|
+
htmlFor: void 0,
|
|
203
|
+
onClick: () => document.getElementById(id)?.focus()
|
|
204
|
+
} : typeof children === "function" || ownId !== void 0 ? { htmlFor: id } : {},
|
|
184
205
|
id: labelId,
|
|
185
206
|
"data-field-label": true,
|
|
186
207
|
children: [label, required && /* @__PURE__ */ jsx(RequiredMark, {})]
|
|
@@ -229,11 +250,10 @@ function RevealToggle({ revealed, onRevealedChange, disabled }) {
|
|
|
229
250
|
* up with the fields around it. For a set of names and values on their own, use
|
|
230
251
|
* DescriptionList. */
|
|
231
252
|
function FieldValue({ size = "md", className, children, ...props }) {
|
|
232
|
-
const control =
|
|
233
|
-
const labelId = useFieldLabelId();
|
|
253
|
+
const control = useFieldLabelledControl();
|
|
234
254
|
return /* @__PURE__ */ jsx("output", {
|
|
235
255
|
id: control?.id,
|
|
236
|
-
"aria-labelledby":
|
|
256
|
+
"aria-labelledby": control?.["aria-labelledby"],
|
|
237
257
|
"aria-describedby": control?.["aria-describedby"],
|
|
238
258
|
...props,
|
|
239
259
|
className: cx("flex min-w-0 items-center break-words text-(--theme-app)", size === "sm" ? "min-h-7 text-xs" : "min-h-9 text-sm", className),
|
|
@@ -241,4 +261,4 @@ function FieldValue({ size = "md", className, children, ...props }) {
|
|
|
241
261
|
});
|
|
242
262
|
}
|
|
243
263
|
//#endregion
|
|
244
|
-
export { ErrorText, Field$1 as Field, FieldOption, FieldRowContext, FieldValue, RevealToggle, useFieldControl,
|
|
264
|
+
export { ErrorText, Field$1 as Field, FieldOption, FieldRowContext, FieldValue, RevealToggle, useFieldControl, useFieldLabelledControl };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cardClassName, positionerClassName } from "./floating.js";
|
|
2
|
+
import { useFullscreenContainer } from "./fullscreen.js";
|
|
2
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
4
|
import { PreviewCard } from "@base-ui/react/preview-card";
|
|
4
5
|
//#region src/components/HoverCard.tsx
|
|
@@ -7,23 +8,27 @@ import { PreviewCard } from "@base-ui/react/preview-card";
|
|
|
7
8
|
* reachable another way, such as the page the anchor links to. The card
|
|
8
9
|
* stays open while the pointer is over it. */
|
|
9
10
|
function HoverCard({ card, children, defaultOpen = false }) {
|
|
11
|
+
const fullscreen = useFullscreenContainer();
|
|
10
12
|
return /* @__PURE__ */ jsxs(PreviewCard.Root, {
|
|
11
13
|
defaultOpen,
|
|
12
14
|
children: [/* @__PURE__ */ jsx(PreviewCard.Trigger, {
|
|
13
15
|
delay: 350,
|
|
14
16
|
closeDelay: 150,
|
|
15
17
|
render: children
|
|
16
|
-
}), /* @__PURE__ */ jsx(PreviewCard.Portal, {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
className:
|
|
24
|
-
children:
|
|
18
|
+
}), /* @__PURE__ */ jsx(PreviewCard.Portal, {
|
|
19
|
+
container: fullscreen,
|
|
20
|
+
children: /* @__PURE__ */ jsx(PreviewCard.Positioner, {
|
|
21
|
+
side: "bottom",
|
|
22
|
+
align: "start",
|
|
23
|
+
sideOffset: 8,
|
|
24
|
+
collisionPadding: 8,
|
|
25
|
+
className: positionerClassName,
|
|
26
|
+
children: /* @__PURE__ */ jsx(PreviewCard.Popup, {
|
|
27
|
+
className: cardClassName,
|
|
28
|
+
children: card
|
|
29
|
+
})
|
|
25
30
|
})
|
|
26
|
-
})
|
|
31
|
+
})]
|
|
27
32
|
});
|
|
28
33
|
}
|
|
29
34
|
//#endregion
|
|
@@ -19,8 +19,10 @@ interface InfoTipProps {
|
|
|
19
19
|
/** Stays on `side` rather than flipping where it doesn't fit, as while the control it
|
|
20
20
|
* describes is typed in, which a flipped tip would cover. */
|
|
21
21
|
keepSide?: boolean | undefined;
|
|
22
|
+
/** Placed against this element rather than the ?, such as beside the control it describes. */
|
|
23
|
+
anchor?: Element | null | undefined;
|
|
22
24
|
}
|
|
23
25
|
/** Extra detail behind an info trigger. A popover that also opens on hover, so
|
|
24
26
|
* touch and screen reader users can reach it and its links. */
|
|
25
|
-
export declare function InfoTip({ text, side, className, children, describesControl, open, onOpenChange, keepSide, }: InfoTipProps): import("react").JSX.Element | null;
|
|
27
|
+
export declare function InfoTip({ text, side, className, children, describesControl, open, onOpenChange, keepSide, anchor, }: InfoTipProps): import("react").JSX.Element | null;
|
|
26
28
|
export {};
|
|
@@ -31,7 +31,7 @@ function formatInfoTipContent(content) {
|
|
|
31
31
|
var hasLink = (content) => new RegExp(linkRegex.source).test(content);
|
|
32
32
|
/** Extra detail behind an info trigger. A popover that also opens on hover, so
|
|
33
33
|
* touch and screen reader users can reach it and its links. */
|
|
34
|
-
function InfoTip({ text, side = "top", className, children, describesControl = false, open, onOpenChange, keepSide = false }) {
|
|
34
|
+
function InfoTip({ text, side = "top", className, children, describesControl = false, open, onOpenChange, keepSide = false, anchor }) {
|
|
35
35
|
const t = useStrings().common;
|
|
36
36
|
if (!text) return children ? /* @__PURE__ */ jsx("span", {
|
|
37
37
|
className,
|
|
@@ -43,6 +43,7 @@ function InfoTip({ text, side = "top", className, children, describesControl = f
|
|
|
43
43
|
openOnHover: true,
|
|
44
44
|
side,
|
|
45
45
|
keepSide,
|
|
46
|
+
anchor,
|
|
46
47
|
...open === void 0 ? {} : { open },
|
|
47
48
|
...onOpenChange ? { onOpenChange } : {},
|
|
48
49
|
...describesControl && { initialFocus: "none" },
|
package/dist/components/Menu.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { CheckIcon, ChevronRightIcon } from "../icons.js";
|
|
3
3
|
import { panelClassName, panelItemClassName, positionerClassName } from "./floating.js";
|
|
4
|
+
import { useFullscreenContainer } from "./fullscreen.js";
|
|
4
5
|
import { Menu } from "@base-ui/react/menu";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
//#region src/components/Menu.tsx
|
|
7
8
|
var itemClassName = panelItemClassName;
|
|
8
9
|
/** The portal, positioner and popup every menu needs. */
|
|
9
10
|
function MenuPopup({ children, className, side = "bottom", align = "start", anchor, keepMounted = false }) {
|
|
11
|
+
const fullscreen = useFullscreenContainer();
|
|
10
12
|
return /* @__PURE__ */ jsx(Menu.Portal, {
|
|
13
|
+
container: fullscreen,
|
|
11
14
|
keepMounted,
|
|
12
15
|
children: /* @__PURE__ */ jsx(Menu.Positioner, {
|
|
13
16
|
side,
|
package/dist/components/Modal.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
|
+
import { useFullscreenContainer } from "./fullscreen.js";
|
|
2
3
|
import { closeHandler } from "./dismiss.js";
|
|
3
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
import { AlertDialog } from "@base-ui/react/alert-dialog";
|
|
@@ -21,37 +22,41 @@ var sizeClasses = {
|
|
|
21
22
|
* and a `footer` for the standard layout, as a Drawer has, or lay out the whole
|
|
22
23
|
* panel with the children. */
|
|
23
24
|
function Modal({ open, onClose, title, description, footer, size, alert = false, dismissible = true, align = "top", panel = true, backdropClassName = "bg-black/40", className, children, ...popupProps }) {
|
|
25
|
+
const fullscreen = useFullscreenContainer();
|
|
24
26
|
const onOpenChange = closeHandler(dismissible, onClose);
|
|
25
27
|
const header = title !== void 0 || description !== void 0;
|
|
26
28
|
const body = children !== void 0 && children !== null && children !== false;
|
|
27
29
|
const laidOut = header || footer !== void 0;
|
|
28
|
-
const content = /* @__PURE__ */ jsxs(Dialog.Portal, {
|
|
29
|
-
|
|
30
|
-
children: /* @__PURE__ */ jsx(Dialog.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
className: "
|
|
38
|
-
children: title
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
30
|
+
const content = /* @__PURE__ */ jsxs(Dialog.Portal, {
|
|
31
|
+
container: fullscreen,
|
|
32
|
+
children: [/* @__PURE__ */ jsx(Dialog.Backdrop, { className: cx("fixed inset-0 z-(--layer-modal)", backdropClassName) }), /* @__PURE__ */ jsx(Dialog.Viewport, {
|
|
33
|
+
className: cx("fixed inset-0 z-(--layer-modal) flex justify-center px-4 pt-20 pb-8", align === "center" ? "items-center" : "items-start sm:pt-[max(14vh,5rem)]"),
|
|
34
|
+
children: /* @__PURE__ */ jsx(Dialog.Popup, {
|
|
35
|
+
...popupProps,
|
|
36
|
+
className: cx(panel && "rounded-xl border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-2xl", size && [sizeClasses[size], "max-w-full"], laidOut && "flex max-h-full flex-col", "outline-none", className),
|
|
37
|
+
children: laidOut ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38
|
+
header && /* @__PURE__ */ jsxs("div", {
|
|
39
|
+
className: cx("shrink-0 px-5 pt-5", !body && "pb-5"),
|
|
40
|
+
children: [title !== void 0 && /* @__PURE__ */ jsx(ModalTitle, {
|
|
41
|
+
className: "m-0 text-lg font-semibold",
|
|
42
|
+
children: title
|
|
43
|
+
}), description !== void 0 && /* @__PURE__ */ jsx(ModalDescription, {
|
|
44
|
+
className: "m-0 mt-1.5 text-sm text-(--theme-muted-text-color)",
|
|
45
|
+
children: description
|
|
46
|
+
})]
|
|
47
|
+
}),
|
|
48
|
+
body && /* @__PURE__ */ jsx("div", {
|
|
49
|
+
className: "min-h-0 flex-1 overflow-y-auto overscroll-contain px-5 py-4",
|
|
50
|
+
children
|
|
51
|
+
}),
|
|
52
|
+
footer !== void 0 && /* @__PURE__ */ jsx("div", {
|
|
53
|
+
className: "flex shrink-0 items-center justify-end gap-2 border-t border-(--theme-border) px-5 py-3",
|
|
54
|
+
children: footer
|
|
55
|
+
})
|
|
56
|
+
] }) : children
|
|
57
|
+
})
|
|
58
|
+
})]
|
|
59
|
+
});
|
|
55
60
|
return alert ? /* @__PURE__ */ jsx(AlertDialog.Root, {
|
|
56
61
|
open,
|
|
57
62
|
onOpenChange,
|
|
@@ -33,6 +33,9 @@ export interface PopoverOptions extends Omit<BasePopover.Popup.Props, 'className
|
|
|
33
33
|
* a list read before one item in it is picked, or nowhere, for a popover that
|
|
34
34
|
* offers an action on what the reader is doing, such as copying a selection. */
|
|
35
35
|
initialFocus?: 'first-control' | 'popup' | 'none';
|
|
36
|
+
/** Placed against this element rather than its trigger, such as a tip beside the field it
|
|
37
|
+
* describes. It still opens and closes from the trigger. */
|
|
38
|
+
anchor?: Element | null | undefined;
|
|
36
39
|
/** Stays on `side` where it doesn't fit rather than flipping to the other, for a popup that
|
|
37
40
|
* must not cover what is there, such as the control a tip describes while it is typed in. */
|
|
38
41
|
keepSide?: boolean | undefined;
|
|
@@ -46,5 +49,5 @@ export interface PopoverOptions extends Omit<BasePopover.Popup.Props, 'className
|
|
|
46
49
|
* flips and shifts to stay on screen, closes on Escape or a press outside, and
|
|
47
50
|
* returns focus to the trigger. Use a Menu for a list of actions, and a
|
|
48
51
|
* HoverCard for a preview a link already leads to. */
|
|
49
|
-
export declare function Popover({ trigger, at, title, open, defaultOpen, onOpenChange, side, align, openOnHover, initialFocus, keepSide, variant, className, children, ...popupProps }: PopoverProps): import("react").JSX.Element;
|
|
52
|
+
export declare function Popover({ trigger, at, title, open, defaultOpen, onOpenChange, side, align, openOnHover, initialFocus, anchor, keepSide, variant, className, children, ...popupProps }: PopoverProps): import("react").JSX.Element;
|
|
50
53
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { panelClassName, positionerClassName, tipClassName, toPositionerAnchor } from "./floating.js";
|
|
3
|
+
import { useFullscreenContainer } from "./fullscreen.js";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
import { createContext, use, useCallback, useRef } from "react";
|
|
5
6
|
import { Popover } from "@base-ui/react/popover";
|
|
@@ -16,7 +17,8 @@ function useClosePopover() {
|
|
|
16
17
|
* flips and shifts to stay on screen, closes on Escape or a press outside, and
|
|
17
18
|
* returns focus to the trigger. Use a Menu for a list of actions, and a
|
|
18
19
|
* HoverCard for a preview a link already leads to. */
|
|
19
|
-
function Popover$1({ trigger, at, title, open, defaultOpen, onOpenChange, side = "bottom", align = "center", openOnHover = false, initialFocus = "first-control", keepSide = false, variant = "panel", className, children, ...popupProps }) {
|
|
20
|
+
function Popover$1({ trigger, at, title, open, defaultOpen, onOpenChange, side = "bottom", align = "center", openOnHover = false, initialFocus = "first-control", anchor, keepSide = false, variant = "panel", className, children, ...popupProps }) {
|
|
21
|
+
const fullscreen = useFullscreenContainer();
|
|
20
22
|
const tip = variant === "tip";
|
|
21
23
|
const popup = useRef(null);
|
|
22
24
|
const actions = useRef(null);
|
|
@@ -33,32 +35,35 @@ function Popover$1({ trigger, at, title, open, defaultOpen, onOpenChange, side =
|
|
|
33
35
|
delay: 300,
|
|
34
36
|
closeDelay: 150
|
|
35
37
|
} : {}
|
|
36
|
-
}), /* @__PURE__ */ jsx(Popover.Portal, {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
tip
|
|
51
|
-
|
|
52
|
-
className: "
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
38
|
+
}), /* @__PURE__ */ jsx(Popover.Portal, {
|
|
39
|
+
container: fullscreen,
|
|
40
|
+
children: /* @__PURE__ */ jsx(Popover.Positioner, {
|
|
41
|
+
side,
|
|
42
|
+
align,
|
|
43
|
+
sideOffset: tip ? 8 : 6,
|
|
44
|
+
collisionPadding: 8,
|
|
45
|
+
...keepSide && { collisionAvoidance: { side: "none" } },
|
|
46
|
+
...at ? { anchor: toPositionerAnchor(at) } : anchor ? { anchor } : {},
|
|
47
|
+
className: positionerClassName,
|
|
48
|
+
children: /* @__PURE__ */ jsxs(Popover.Popup, {
|
|
49
|
+
...popupProps,
|
|
50
|
+
ref: popup,
|
|
51
|
+
initialFocus: initialFocus === "popup" ? popup : initialFocus === "first-control",
|
|
52
|
+
className: cx(tip ? cx("max-w-sm text-left", tipClassName) : cx(panelClassName, "max-h-(--available-height) max-w-[calc(100vw-16px)] overflow-y-auto p-3"), className),
|
|
53
|
+
children: [
|
|
54
|
+
tip && /* @__PURE__ */ jsx(Popover.Arrow, { className: "size-2 rotate-45 bg-(--theme-app) data-[side=top]:-bottom-1 data-[side=bottom]:-top-1 data-[side=left]:-right-1 data-[side=right]:-left-1" }),
|
|
55
|
+
title && /* @__PURE__ */ jsx(Popover.Title, {
|
|
56
|
+
className: "mb-2 text-sm font-semibold",
|
|
57
|
+
children: title
|
|
58
|
+
}),
|
|
59
|
+
/* @__PURE__ */ jsx(CloseContext, {
|
|
60
|
+
value: close,
|
|
61
|
+
children
|
|
62
|
+
})
|
|
63
|
+
]
|
|
64
|
+
})
|
|
60
65
|
})
|
|
61
|
-
})
|
|
66
|
+
})]
|
|
62
67
|
});
|
|
63
68
|
}
|
|
64
69
|
//#endregion
|
package/dist/components/Radio.js
CHANGED
|
@@ -9,7 +9,7 @@ import { RadioGroup } from "@base-ui/react/radio-group";
|
|
|
9
9
|
function Radio$1({ className, ...props }) {
|
|
10
10
|
return /* @__PURE__ */ jsx(Radio.Root, {
|
|
11
11
|
...props,
|
|
12
|
-
className: cx("flex size-4 shrink-0 items-center justify-center rounded-full border border-(--theme-border-strong) bg-(--theme-input-bg) cursor-pointer transition-colors motion-reduce:transition-none", focusOutline, "data-checked:border-(--theme-
|
|
12
|
+
className: cx("flex size-4 shrink-0 items-center justify-center rounded-full border border-(--theme-border-strong) bg-(--theme-input-bg) cursor-pointer transition-colors motion-reduce:transition-none", focusOutline, "data-checked:border-(--theme-focus) data-checked:bg-(--theme-accent)", "aria-invalid:border-(--theme-error)", "data-disabled:cursor-not-allowed data-disabled:opacity-50", className),
|
|
13
13
|
children: /* @__PURE__ */ jsx(Radio.Indicator, { className: "size-1.5 rounded-full bg-(--theme-accent-text)" })
|
|
14
14
|
});
|
|
15
15
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { CheckIcon, ChevronDownIcon } from "../icons.js";
|
|
3
3
|
import { panelClassName, positionerClassName } from "./floating.js";
|
|
4
|
+
import { useFullscreenContainer } from "./fullscreen.js";
|
|
4
5
|
import { Tooltip } from "./Tooltip.js";
|
|
5
6
|
import { fieldBox } from "./fieldBox.js";
|
|
6
7
|
import { optionNaming, pickerItemClassName } from "./comboboxItems.js";
|
|
@@ -13,6 +14,7 @@ var triggerClassName = "flex cursor-pointer items-center justify-between gap-2 p
|
|
|
13
14
|
/** Picks one value, or several with `multiple`, from a short list. Use
|
|
14
15
|
* Combobox when the list is long enough to need filtering. */
|
|
15
16
|
function Select$1({ items, placeholder, className, "aria-label": ariaLabel, "aria-labelledby": labelledBy, "aria-describedby": describedBy, "aria-invalid": invalid, "aria-required": required, trigger, triggerTooltip, size = "md", onItemHighlighted, ...rootProps }) {
|
|
17
|
+
const fullscreen = useFullscreenContainer();
|
|
16
18
|
const uid = useId();
|
|
17
19
|
const labelOf = (value) => items.find((item) => Object.is(item.value, value))?.label;
|
|
18
20
|
const triggerProps = {
|
|
@@ -46,51 +48,54 @@ function Select$1({ items, placeholder, className, "aria-label": ariaLabel, "ari
|
|
|
46
48
|
className: "shrink-0 text-(--theme-muted-text-color)",
|
|
47
49
|
children: /* @__PURE__ */ jsx(ChevronDownIcon, { "aria-hidden": "true" })
|
|
48
50
|
})]
|
|
49
|
-
}), /* @__PURE__ */ jsx(Select.Portal, {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
className:
|
|
55
|
-
children: /* @__PURE__ */ jsx(Select.
|
|
56
|
-
className: cx("max-
|
|
57
|
-
children:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
className: "flex min-w-0
|
|
72
|
-
children: [/* @__PURE__ */
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
"
|
|
86
|
-
|
|
51
|
+
}), /* @__PURE__ */ jsx(Select.Portal, {
|
|
52
|
+
container: fullscreen,
|
|
53
|
+
children: /* @__PURE__ */ jsx(Select.Positioner, {
|
|
54
|
+
alignItemWithTrigger: false,
|
|
55
|
+
sideOffset: 4,
|
|
56
|
+
className: positionerClassName,
|
|
57
|
+
children: /* @__PURE__ */ jsx(Select.Popup, {
|
|
58
|
+
className: cx("min-w-(--anchor-width) max-w-[min(20rem,var(--available-width))]", panelClassName),
|
|
59
|
+
children: /* @__PURE__ */ jsx(Select.List, {
|
|
60
|
+
className: cx("max-h-[min(15rem,var(--available-height))] overflow-y-auto", "p-1"),
|
|
61
|
+
children: items.map((item, index) => /* @__PURE__ */ jsxs(Select.Item, {
|
|
62
|
+
value: item.value,
|
|
63
|
+
disabled: item.disabled,
|
|
64
|
+
...optionNaming(`${uid}${index}`, item),
|
|
65
|
+
onFocus: onItemHighlighted && (() => onItemHighlighted(item.value)),
|
|
66
|
+
className: pickerItemClassName,
|
|
67
|
+
children: [
|
|
68
|
+
item.icon && /* @__PURE__ */ jsx("span", {
|
|
69
|
+
className: "flex shrink-0 items-center",
|
|
70
|
+
children: item.icon
|
|
71
|
+
}),
|
|
72
|
+
/* @__PURE__ */ jsxs("span", {
|
|
73
|
+
className: "flex min-w-0 flex-col",
|
|
74
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
75
|
+
className: "flex min-w-0 items-center gap-2",
|
|
76
|
+
children: [/* @__PURE__ */ jsx(Select.ItemText, {
|
|
77
|
+
id: `${uid}${index}label`,
|
|
78
|
+
className: "truncate",
|
|
79
|
+
children: item.label
|
|
80
|
+
}), item.badge]
|
|
81
|
+
}), /* @__PURE__ */ jsx(OptionDescription, {
|
|
82
|
+
id: `${uid}${index}description`,
|
|
83
|
+
item
|
|
84
|
+
})]
|
|
85
|
+
}),
|
|
86
|
+
/* @__PURE__ */ jsx(Select.ItemIndicator, {
|
|
87
|
+
className: "ml-auto flex shrink-0 items-center",
|
|
88
|
+
children: /* @__PURE__ */ jsx(CheckIcon, {
|
|
89
|
+
"aria-hidden": "true",
|
|
90
|
+
className: "size-3.5"
|
|
91
|
+
})
|
|
87
92
|
})
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
93
|
+
]
|
|
94
|
+
}, String(item.value)))
|
|
95
|
+
})
|
|
91
96
|
})
|
|
92
97
|
})
|
|
93
|
-
})
|
|
98
|
+
})]
|
|
94
99
|
});
|
|
95
100
|
}
|
|
96
101
|
//#endregion
|
|
@@ -17,10 +17,10 @@ function Slider$1({ value, onValueChange, min, max, step = 1, disabled = false,
|
|
|
17
17
|
children: /* @__PURE__ */ jsx(Slider.Control, {
|
|
18
18
|
className: "flex w-full touch-none select-none items-center py-2 data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
|
19
19
|
children: /* @__PURE__ */ jsxs(Slider.Track, {
|
|
20
|
-
className: "h-1 w-full rounded-full bg-(--theme-border)",
|
|
21
|
-
children: [/* @__PURE__ */ jsx(Slider.Indicator, { className: "rounded-full bg-(--theme-
|
|
20
|
+
className: "h-1 w-full rounded-full bg-(--theme-border-strong)",
|
|
21
|
+
children: [/* @__PURE__ */ jsx(Slider.Indicator, { className: "rounded-full bg-(--theme-focus)" }), /* @__PURE__ */ jsx(Slider.Thumb, {
|
|
22
22
|
"aria-label": ariaLabel,
|
|
23
|
-
className: "size-4 rounded-full border-2 border-(--theme-
|
|
23
|
+
className: "size-4 rounded-full border-2 border-(--theme-focus) bg-(--theme-panel-bg) shadow-sm has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-(--theme-focus)"
|
|
24
24
|
})]
|
|
25
25
|
})
|
|
26
26
|
})
|
|
@@ -21,7 +21,7 @@ var sizes = {
|
|
|
21
21
|
function Switch$1({ size = "md", className, onLabel, offLabel, ...props }) {
|
|
22
22
|
const control = /* @__PURE__ */ jsx(Switch.Root, {
|
|
23
23
|
...props,
|
|
24
|
-
className: cx("peer inline-flex shrink-0 items-center rounded-full p-0.5 bg-(--theme-border-strong) transition-colors duration-200 cursor-pointer motion-reduce:transition-none", "data-checked:bg-(--theme-
|
|
24
|
+
className: cx("peer inline-flex shrink-0 items-center rounded-full p-0.5 bg-(--theme-border-strong) transition-colors duration-200 cursor-pointer motion-reduce:transition-none", "data-checked:bg-(--theme-focus)", focusOutline, "aria-invalid:ring-2 aria-invalid:ring-(--theme-error)", "data-disabled:cursor-not-allowed data-disabled:opacity-50", sizes[size].root, className),
|
|
25
25
|
children: /* @__PURE__ */ jsx(Switch.Thumb, { className: cx("rounded-full bg-white shadow-sm transition-transform duration-200 motion-reduce:transition-none", sizes[size].thumb) })
|
|
26
26
|
});
|
|
27
27
|
if (onLabel === void 0 && offLabel === void 0) return control;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { positionerClassName, tipArrowClassName, tipClassName } from "./floating.js";
|
|
2
|
+
import { useFullscreenContainer } from "./fullscreen.js";
|
|
2
3
|
import { keyedByContent } from "./keys.js";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
import { Fragment as Fragment$1, createContext, use, useState } from "react";
|
|
@@ -19,18 +20,22 @@ function KbdShortcut({ keys }) {
|
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
22
|
function TooltipPopup({ content, shortcut, side }) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
const fullscreen = useFullscreenContainer();
|
|
24
|
+
return /* @__PURE__ */ jsx(Tooltip.Portal, {
|
|
25
|
+
container: fullscreen,
|
|
26
|
+
children: /* @__PURE__ */ jsx(Tooltip.Positioner, {
|
|
27
|
+
side,
|
|
28
|
+
sideOffset: 8,
|
|
29
|
+
className: positionerClassName,
|
|
30
|
+
children: /* @__PURE__ */ jsxs(Tooltip.Popup, {
|
|
31
|
+
className: `max-w-xs ${tipClassName}`,
|
|
32
|
+
children: [/* @__PURE__ */ jsx(Tooltip.Arrow, { className: tipArrowClassName }), shortcut ? /* @__PURE__ */ jsxs("span", {
|
|
33
|
+
className: "inline-flex items-center gap-2",
|
|
34
|
+
children: [content, /* @__PURE__ */ jsx(KbdShortcut, { keys: shortcut })]
|
|
35
|
+
}) : content]
|
|
36
|
+
})
|
|
32
37
|
})
|
|
33
|
-
})
|
|
38
|
+
});
|
|
34
39
|
}
|
|
35
40
|
/** One popup serves every Tooltip below it, so each anchor costs only a trigger.
|
|
36
41
|
* UIProvider mounts it; nothing else needs to. */
|
|
@@ -20,7 +20,7 @@ var sizes = {
|
|
|
20
20
|
* thumb: an outline in the theme's focus color, which is solved for 3:1 on every surface a
|
|
21
21
|
* control sits on, so it shows on a checked control as well as an empty one. */
|
|
22
22
|
var focusOutline = "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--theme-focus)";
|
|
23
|
-
var base = "w-full min-w-0 rounded-md border border-(--theme-border) bg-(--theme-input-bg) text-(--theme-app) outline-none transition-colors placeholder:text-(--theme-placeholder)";
|
|
23
|
+
var base = "w-full min-w-0 rounded-md border border-(--theme-border-strong) bg-(--theme-input-bg) text-(--theme-app) outline-none transition-colors placeholder:text-(--theme-placeholder)";
|
|
24
24
|
var own = cx("focus-visible:border-(--theme-focus) focus-visible:ring-1 focus-visible:ring-(--theme-focus)", "aria-invalid:border-(--theme-error) aria-invalid:focus-visible:ring-(--theme-error)", "disabled:cursor-not-allowed disabled:bg-(--theme-input-disabled-bg) disabled:text-(--theme-placeholder)", "data-disabled:cursor-not-allowed data-disabled:bg-(--theme-input-disabled-bg) data-disabled:text-(--theme-placeholder)");
|
|
25
25
|
var inside = cx("has-[:focus-visible]:border-(--theme-focus) has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-(--theme-focus)", "has-[[aria-invalid=true]]:border-(--theme-error) has-[[aria-invalid=true]:focus-visible]:ring-(--theme-error)", "has-[input:disabled]:cursor-not-allowed has-[input:disabled]:bg-(--theme-input-disabled-bg) has-[input:disabled]:text-(--theme-placeholder)", "data-disabled:cursor-not-allowed data-disabled:bg-(--theme-input-disabled-bg) data-disabled:text-(--theme-placeholder)");
|
|
26
26
|
/** The box every input-like control draws: text inputs, textareas, and the triggers of
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Where a popup portals while an element is fullscreen: that element. The browser
|
|
2
|
+
* shows only the fullscreen element's subtree, so a popup portaled to the body
|
|
3
|
+
* would open unseen behind it. Undefined otherwise, which keeps the body. */
|
|
4
|
+
export declare function useFullscreenContainer(): HTMLElement | undefined;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
//#region src/components/fullscreen.ts
|
|
3
|
+
function subscribe(onChange) {
|
|
4
|
+
document.addEventListener("fullscreenchange", onChange);
|
|
5
|
+
return () => document.removeEventListener("fullscreenchange", onChange);
|
|
6
|
+
}
|
|
7
|
+
function snapshot() {
|
|
8
|
+
const element = document.fullscreenElement;
|
|
9
|
+
return element instanceof HTMLElement ? element : void 0;
|
|
10
|
+
}
|
|
11
|
+
/** Where a popup portals while an element is fullscreen: that element. The browser
|
|
12
|
+
* shows only the fullscreen element's subtree, so a popup portaled to the body
|
|
13
|
+
* would open unseen behind it. Undefined otherwise, which keeps the body. */
|
|
14
|
+
function useFullscreenContainer() {
|
|
15
|
+
return useSyncExternalStore(subscribe, snapshot, () => void 0);
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { useFullscreenContainer };
|