@parallelworks/ui 0.53.0 → 0.55.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 +68 -9
- package/dist/access/AccessDrawer.d.ts +55 -0
- package/dist/access/AccessDrawer.js +445 -0
- package/dist/access/AccessList.d.ts +58 -0
- package/dist/access/AccessList.js +382 -0
- package/dist/access/GrantView.d.ts +35 -0
- package/dist/access/GrantView.js +188 -0
- package/dist/access/GroupPicker.d.ts +35 -0
- package/dist/access/GroupPicker.js +336 -0
- package/dist/access/focus.d.ts +10 -0
- package/dist/access/focus.js +27 -0
- package/dist/access/index.d.ts +3 -0
- package/dist/access/index.js +3 -0
- package/dist/access/model.d.ts +49 -0
- package/dist/access/model.js +129 -0
- package/dist/access/strings.d.ts +122 -0
- package/dist/access/strings.js +151 -0
- package/dist/access/types.d.ts +56 -0
- package/dist/ai/components/SlashMenu.js +1 -1
- package/dist/components/Combobox.d.ts +19 -0
- package/dist/components/Combobox.js +101 -50
- package/dist/components/DatePicker.js +4 -5
- package/dist/components/Drawer.d.ts +6 -1
- package/dist/components/Drawer.js +25 -16
- package/dist/components/Field.d.ts +8 -3
- package/dist/components/Field.js +32 -12
- package/dist/components/InfoTip.d.ts +3 -1
- package/dist/components/InfoTip.js +2 -1
- package/dist/components/Menu.js +4 -4
- package/dist/components/MenuEntries.js +1 -1
- package/dist/components/OptionDescription.d.ts +7 -0
- package/dist/components/OptionDescription.js +19 -0
- package/dist/components/Popover.d.ts +4 -1
- package/dist/components/Popover.js +2 -2
- package/dist/components/Provider.d.ts +7 -0
- package/dist/components/Provider.js +3 -0
- package/dist/components/Select.js +13 -11
- package/dist/components/comboboxItems.d.ts +10 -0
- package/dist/components/comboboxItems.js +8 -1
- package/dist/components/floating.d.ts +5 -2
- package/dist/components/floating.js +2 -2
- package/dist/editor/EditorField.js +1 -2
- package/dist/editor/Monaco.js +26 -2
- package/dist/editor/themes.d.ts +5 -0
- package/dist/editor/themes.js +18 -3
- package/dist/file-explorer/FileExplorer.js +2 -1
- package/dist/file-explorer/lib/paging.d.ts +1 -0
- package/dist/file-explorer/lib/paging.js +5 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/styles/theme.css +82 -62
- package/dist/styles.css +1 -1
- package/dist/theme/derive.d.ts +5 -3
- package/dist/theme/derive.js +74 -4
- package/package.json +6 -2
|
@@ -4,7 +4,7 @@ import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from "../icons.js";
|
|
|
4
4
|
import { panelClassName, positionerClassName } from "./floating.js";
|
|
5
5
|
import { useStrings } from "./Provider.js";
|
|
6
6
|
import { fieldBox } from "./fieldBox.js";
|
|
7
|
-
import {
|
|
7
|
+
import { useFieldLabelledControl } from "./Field.js";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { useState } from "react";
|
|
10
10
|
import { Popover } from "@base-ui/react/popover";
|
|
@@ -76,8 +76,7 @@ var triggerClassName = (size) => cx(fieldBox({ size }), "flex flex-1 cursor-poin
|
|
|
76
76
|
function DatePicker({ value, onValueChange, withTime = false, min, max, timeStep = 60, required = false, disabled = false, placeholder, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, size = "md", className }) {
|
|
77
77
|
const t = useStrings().datePicker;
|
|
78
78
|
const { common } = useStrings();
|
|
79
|
-
const field =
|
|
80
|
-
const fieldLabelId = useFieldLabelId();
|
|
79
|
+
const field = useFieldLabelledControl();
|
|
81
80
|
const [open, setOpen] = useState(false);
|
|
82
81
|
const [weekStartsOn] = useState(firstWeekday);
|
|
83
82
|
const day = parseDay(value);
|
|
@@ -104,10 +103,10 @@ function DatePicker({ value, onValueChange, withTime = false, min, max, timeStep
|
|
|
104
103
|
open,
|
|
105
104
|
onOpenChange: setOpen,
|
|
106
105
|
children: [/* @__PURE__ */ jsxs(Popover.Trigger, {
|
|
107
|
-
id,
|
|
106
|
+
id: id ?? field?.id,
|
|
108
107
|
disabled,
|
|
109
108
|
"aria-label": ariaLabel,
|
|
110
|
-
"aria-labelledby": ariaLabelledBy ?? (ariaLabel ? void 0 :
|
|
109
|
+
"aria-labelledby": ariaLabelledBy ?? (ariaLabel ? void 0 : field?.["aria-labelledby"]),
|
|
111
110
|
"aria-describedby": ariaDescribedBy ?? field?.["aria-describedby"],
|
|
112
111
|
"aria-invalid": ariaInvalid || field?.["aria-invalid"],
|
|
113
112
|
"aria-required": ariaRequired || field?.["aria-required"],
|
|
@@ -7,6 +7,11 @@ export interface DrawerProps extends Omit<BaseDrawer.Popup.Props, 'className' |
|
|
|
7
7
|
title?: ReactNode;
|
|
8
8
|
/** A line under the title. */
|
|
9
9
|
description?: ReactNode;
|
|
10
|
+
/** Adds a back arrow before the title, for a view the drawer opened over its first, such as
|
|
11
|
+
* a form; the drawer stays one drawer rather than another stacking on it. */
|
|
12
|
+
onBack?: (() => void) | undefined;
|
|
13
|
+
/** Names the back arrow; defaults to the provider's `back`. */
|
|
14
|
+
backLabel?: string | undefined;
|
|
10
15
|
/** Actions along the bottom edge, such as Cancel and Save; they stay in view as the body scrolls. */
|
|
11
16
|
footer?: ReactNode;
|
|
12
17
|
/** The edge it slides in from, and the way a swipe dismisses it. */
|
|
@@ -44,4 +49,4 @@ export interface DrawerProps extends Omit<BaseDrawer.Popup.Props, 'className' |
|
|
|
44
49
|
* `--app-inset-top` (a top bar's height), and at its widest leaves the strip
|
|
45
50
|
* `--app-inset-start` (a start-side sidebar's width, at least 2.5rem). Set both
|
|
46
51
|
* on the document root. */
|
|
47
|
-
export declare function Drawer({ open, onClose, title, description, footer, side, width, resizable, minWidth, dismissible, modal, closeButton, contained, keepMounted, padded, className, children, ...popupProps }: DrawerProps): import("react").JSX.Element;
|
|
52
|
+
export declare function Drawer({ open, onClose, title, description, onBack, backLabel, footer, side, width, resizable, minWidth, dismissible, modal, closeButton, contained, keepMounted, padded, className, children, ...popupProps }: DrawerProps): import("react").JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
|
-
import { CloseIcon } from "../icons.js";
|
|
2
|
+
import { ArrowLeftIcon, CloseIcon } from "../icons.js";
|
|
3
3
|
import { useStrings } from "./Provider.js";
|
|
4
4
|
import { IconButton } from "./IconButton.js";
|
|
5
5
|
import { closeHandler } from "./dismiss.js";
|
|
@@ -15,7 +15,7 @@ import { Drawer } from "@base-ui/react/drawer";
|
|
|
15
15
|
* `--app-inset-top` (a top bar's height), and at its widest leaves the strip
|
|
16
16
|
* `--app-inset-start` (a start-side sidebar's width, at least 2.5rem). Set both
|
|
17
17
|
* on the document root. */
|
|
18
|
-
function Drawer$1({ open, onClose, title, description, footer, side = "right", width = 480, resizable = false, minWidth = 320, dismissible = true, modal = true, closeButton = true, contained = false, keepMounted = false, padded = true, className, children, ...popupProps }) {
|
|
18
|
+
function Drawer$1({ open, onClose, title, description, onBack, backLabel, footer, side = "right", width = 480, resizable = false, minWidth = 320, dismissible = true, modal = true, closeButton = true, contained = false, keepMounted = false, padded = true, className, children, ...popupProps }) {
|
|
19
19
|
const { common } = useStrings();
|
|
20
20
|
const container = useRef(null);
|
|
21
21
|
const viewport = useRef(null);
|
|
@@ -61,20 +61,29 @@ function Drawer$1({ open, onClose, title, description, footer, side = "right", w
|
|
|
61
61
|
children: [
|
|
62
62
|
header && /* @__PURE__ */ jsxs("div", {
|
|
63
63
|
className: "flex shrink-0 items-start gap-3 border-b border-(--theme-border) px-4 py-3",
|
|
64
|
-
children: [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
64
|
+
children: [
|
|
65
|
+
onBack && /* @__PURE__ */ jsx(IconButton, {
|
|
66
|
+
icon: /* @__PURE__ */ jsx(ArrowLeftIcon, {}),
|
|
67
|
+
label: backLabel ?? common.back,
|
|
68
|
+
onClick: onBack,
|
|
69
|
+
className: "-my-1 -ms-1.5"
|
|
70
|
+
}),
|
|
71
|
+
/* @__PURE__ */ jsxs("div", {
|
|
72
|
+
className: "min-w-0 flex-1",
|
|
73
|
+
children: [title !== void 0 && /* @__PURE__ */ jsx(Drawer.Title, {
|
|
74
|
+
className: "m-0 text-sm font-semibold wrap-break-word",
|
|
75
|
+
children: title
|
|
76
|
+
}), description !== void 0 && /* @__PURE__ */ jsx(Drawer.Description, {
|
|
77
|
+
className: "mt-0.5 text-xs text-(--theme-muted-text-color)",
|
|
78
|
+
children: description
|
|
79
|
+
})]
|
|
80
|
+
}),
|
|
81
|
+
closeButton && /* @__PURE__ */ jsx(Drawer.Close, { render: /* @__PURE__ */ jsx(IconButton, {
|
|
82
|
+
icon: /* @__PURE__ */ jsx(CloseIcon, {}),
|
|
83
|
+
label: common.close,
|
|
84
|
+
className: "-my-1 -me-1.5"
|
|
85
|
+
}) })
|
|
86
|
+
]
|
|
78
87
|
}),
|
|
79
88
|
/* @__PURE__ */ jsx("div", {
|
|
80
89
|
className: cx("relative flex min-h-0 flex-1 flex-col overflow-y-auto", padded && "p-4", className),
|
|
@@ -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 };
|
|
@@ -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
|
@@ -16,7 +16,7 @@ function MenuPopup({ children, className, side = "bottom", align = "start", anch
|
|
|
16
16
|
sideOffset: 4,
|
|
17
17
|
className: positionerClassName,
|
|
18
18
|
children: /* @__PURE__ */ jsx(Menu.Popup, {
|
|
19
|
-
className: cx("min-w-44 max-h-(--available-height) overflow-y-auto
|
|
19
|
+
className: cx("min-w-44 max-h-(--available-height) overflow-y-auto", "p-1", panelClassName, className),
|
|
20
20
|
children
|
|
21
21
|
})
|
|
22
22
|
})
|
|
@@ -41,7 +41,7 @@ function MenuRadioItem({ className, children, ...props }) {
|
|
|
41
41
|
return /* @__PURE__ */ jsxs(Menu.RadioItem, {
|
|
42
42
|
closeOnClick: true,
|
|
43
43
|
...props,
|
|
44
|
-
className: cx(itemClassName, "pl-
|
|
44
|
+
className: cx(itemClassName, "pl-1", className),
|
|
45
45
|
children: [/* @__PURE__ */ jsx("span", {
|
|
46
46
|
className: "flex w-3.5 shrink-0 justify-center",
|
|
47
47
|
children: /* @__PURE__ */ jsx(Menu.RadioItemIndicator, { children: /* @__PURE__ */ jsx(CheckIcon, { className: "size-3" }) })
|
|
@@ -72,11 +72,11 @@ function MenuSubmenuTrigger({ className, children, ...props }) {
|
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
74
|
function MenuSeparator({ className }) {
|
|
75
|
-
return /* @__PURE__ */ jsx(Menu.Separator, { className: cx("my-1 h-px bg-(--theme-border)", className) });
|
|
75
|
+
return /* @__PURE__ */ jsx(Menu.Separator, { className: cx("-mx-1 my-1 h-px bg-(--theme-border)", className) });
|
|
76
76
|
}
|
|
77
77
|
function MenuGroupLabel({ children }) {
|
|
78
78
|
return /* @__PURE__ */ jsx(Menu.GroupLabel, {
|
|
79
|
-
className: "px-
|
|
79
|
+
className: "px-2 pt-2 pb-1 text-[10px] font-semibold uppercase tracking-wide text-(--theme-muted-text-color)",
|
|
80
80
|
children
|
|
81
81
|
});
|
|
82
82
|
}
|
|
@@ -34,7 +34,7 @@ function MenuEntries({ entries, search, query = "" }) {
|
|
|
34
34
|
const shown = needle ? searchMatches(entries, (a) => matches(needle, a.label, a.keywords)) : entries;
|
|
35
35
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
36
36
|
/* @__PURE__ */ jsx("div", {
|
|
37
|
-
className: "sticky top-0 z-[1] bg-(--theme-app-bg) px-
|
|
37
|
+
className: "sticky top-0 z-[1] bg-(--theme-app-bg) px-1 pt-0.5 pb-1",
|
|
38
38
|
children: /* @__PURE__ */ jsx(Menu.Input, {
|
|
39
39
|
"aria-label": search.placeholder,
|
|
40
40
|
placeholder: search.placeholder,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ComboboxItem } from './comboboxItems.js';
|
|
2
|
+
/** An option's description under its label. An `accessibleDescription` is read in its place,
|
|
3
|
+
* so the shown one is then hidden from assistive technology rather than read twice. */
|
|
4
|
+
export declare function OptionDescription({ id, item, }: {
|
|
5
|
+
id: string;
|
|
6
|
+
item: Pick<ComboboxItem<unknown>, 'description' | 'accessibleDescription'>;
|
|
7
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
//#region src/components/OptionDescription.tsx
|
|
3
|
+
/** An option's description under its label. An `accessibleDescription` is read in its place,
|
|
4
|
+
* so the shown one is then hidden from assistive technology rather than read twice. */
|
|
5
|
+
function OptionDescription({ id, item }) {
|
|
6
|
+
const spoken = item.accessibleDescription;
|
|
7
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [item.description && /* @__PURE__ */ jsx("span", {
|
|
8
|
+
id: spoken ? void 0 : id,
|
|
9
|
+
"aria-hidden": spoken ? true : void 0,
|
|
10
|
+
className: "text-xs leading-snug text-(--theme-muted-text-color)",
|
|
11
|
+
children: item.description
|
|
12
|
+
}), spoken && /* @__PURE__ */ jsx("span", {
|
|
13
|
+
id,
|
|
14
|
+
hidden: true,
|
|
15
|
+
children: spoken
|
|
16
|
+
})] });
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { OptionDescription };
|
|
@@ -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 {};
|
|
@@ -16,7 +16,7 @@ function useClosePopover() {
|
|
|
16
16
|
* flips and shifts to stay on screen, closes on Escape or a press outside, and
|
|
17
17
|
* returns focus to the trigger. Use a Menu for a list of actions, and a
|
|
18
18
|
* 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 }) {
|
|
19
|
+
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 }) {
|
|
20
20
|
const tip = variant === "tip";
|
|
21
21
|
const popup = useRef(null);
|
|
22
22
|
const actions = useRef(null);
|
|
@@ -39,7 +39,7 @@ function Popover$1({ trigger, at, title, open, defaultOpen, onOpenChange, side =
|
|
|
39
39
|
sideOffset: tip ? 8 : 6,
|
|
40
40
|
collisionPadding: 8,
|
|
41
41
|
...keepSide && { collisionAvoidance: { side: "none" } },
|
|
42
|
-
...at ? { anchor: toPositionerAnchor(at) } : {},
|
|
42
|
+
...at ? { anchor: toPositionerAnchor(at) } : anchor ? { anchor } : {},
|
|
43
43
|
className: positionerClassName,
|
|
44
44
|
children: /* @__PURE__ */ jsxs(Popover.Popup, {
|
|
45
45
|
...popupProps,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Dispatch, ReactNode, SetStateAction } from 'react';
|
|
2
|
+
import type { AccessText } from '../access/strings.js';
|
|
2
3
|
import type { WorkflowAction, WorkflowEditing } from '../editing.js';
|
|
3
4
|
import type { WorkflowEngine } from '../engine.js';
|
|
4
5
|
import type { GraphEditorText, InputsEditorText } from '../graph/editorStrings.js';
|
|
@@ -14,6 +15,8 @@ export interface UIStrings {
|
|
|
14
15
|
favoriteHintOff: string;
|
|
15
16
|
};
|
|
16
17
|
common: {
|
|
18
|
+
/** Returns to the view a drawer or panel showed before. */
|
|
19
|
+
back: string;
|
|
17
20
|
cancel: string;
|
|
18
21
|
close: string;
|
|
19
22
|
save: string;
|
|
@@ -110,6 +113,8 @@ export interface UIStrings {
|
|
|
110
113
|
dropdown: {
|
|
111
114
|
select: (type: string) => string;
|
|
112
115
|
noOptionsFound: string;
|
|
116
|
+
/** While a search for options is under way. */
|
|
117
|
+
searching: string;
|
|
113
118
|
showOptions: string;
|
|
114
119
|
/** The row that adds typed text as a new value. */
|
|
115
120
|
add: (text: string) => string;
|
|
@@ -259,6 +264,8 @@ export interface UIStrings {
|
|
|
259
264
|
};
|
|
260
265
|
/** The visual editor's text a host translates; the editor fills in the rest in English. */
|
|
261
266
|
graphEditor: Partial<GraphEditorText>;
|
|
267
|
+
/** The access drawer's text, over its English defaults. */
|
|
268
|
+
access: Partial<AccessText>;
|
|
262
269
|
inputsEditor: Partial<InputsEditorText>;
|
|
263
270
|
fileExplorer: {
|
|
264
271
|
preview: {
|
|
@@ -41,6 +41,7 @@ var DEFAULTS = {
|
|
|
41
41
|
favoriteHintOff: "Favorite your item when it's created"
|
|
42
42
|
},
|
|
43
43
|
common: {
|
|
44
|
+
back: "Back",
|
|
44
45
|
cancel: "Cancel",
|
|
45
46
|
close: "Close",
|
|
46
47
|
save: "Save",
|
|
@@ -125,6 +126,7 @@ var DEFAULTS = {
|
|
|
125
126
|
dropdown: {
|
|
126
127
|
select: (type) => `Select ${type}`,
|
|
127
128
|
noOptionsFound: "No options found",
|
|
129
|
+
searching: "Searching…",
|
|
128
130
|
showOptions: "Show options",
|
|
129
131
|
add: (text) => `Add “${text}”`,
|
|
130
132
|
limitReached: (max) => `Up to ${max}`,
|
|
@@ -276,6 +278,7 @@ var DEFAULTS = {
|
|
|
276
278
|
openInNewGraph: "Open in new graph"
|
|
277
279
|
},
|
|
278
280
|
graphEditor: {},
|
|
281
|
+
access: {},
|
|
279
282
|
inputsEditor: {},
|
|
280
283
|
fileExplorer: {
|
|
281
284
|
preview: {
|
|
@@ -3,7 +3,8 @@ import { CheckIcon, ChevronDownIcon } from "../icons.js";
|
|
|
3
3
|
import { panelClassName, positionerClassName } from "./floating.js";
|
|
4
4
|
import { Tooltip } from "./Tooltip.js";
|
|
5
5
|
import { fieldBox } from "./fieldBox.js";
|
|
6
|
-
import { pickerItemClassName } from "./comboboxItems.js";
|
|
6
|
+
import { optionNaming, pickerItemClassName } from "./comboboxItems.js";
|
|
7
|
+
import { OptionDescription } from "./OptionDescription.js";
|
|
7
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
import { useId } from "react";
|
|
9
10
|
import { Select } from "@base-ui/react/select";
|
|
@@ -52,12 +53,11 @@ function Select$1({ items, placeholder, className, "aria-label": ariaLabel, "ari
|
|
|
52
53
|
children: /* @__PURE__ */ jsx(Select.Popup, {
|
|
53
54
|
className: cx("min-w-(--anchor-width) max-w-[min(20rem,var(--available-width))]", panelClassName),
|
|
54
55
|
children: /* @__PURE__ */ jsx(Select.List, {
|
|
55
|
-
className: "max-h-[min(15rem,var(--available-height))] overflow-y-auto
|
|
56
|
+
className: cx("max-h-[min(15rem,var(--available-height))] overflow-y-auto", "p-1"),
|
|
56
57
|
children: items.map((item, index) => /* @__PURE__ */ jsxs(Select.Item, {
|
|
57
58
|
value: item.value,
|
|
58
59
|
disabled: item.disabled,
|
|
59
|
-
|
|
60
|
-
"aria-describedby": item.description && `${uid}${index}description`,
|
|
60
|
+
...optionNaming(`${uid}${index}`, item),
|
|
61
61
|
onFocus: onItemHighlighted && (() => onItemHighlighted(item.value)),
|
|
62
62
|
className: pickerItemClassName,
|
|
63
63
|
children: [
|
|
@@ -67,14 +67,16 @@ function Select$1({ items, placeholder, className, "aria-label": ariaLabel, "ari
|
|
|
67
67
|
}),
|
|
68
68
|
/* @__PURE__ */ jsxs("span", {
|
|
69
69
|
className: "flex min-w-0 flex-col",
|
|
70
|
-
children: [/* @__PURE__ */
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
71
|
+
className: "flex min-w-0 items-center gap-2",
|
|
72
|
+
children: [/* @__PURE__ */ jsx(Select.ItemText, {
|
|
73
|
+
id: `${uid}${index}label`,
|
|
74
|
+
className: "truncate",
|
|
75
|
+
children: item.label
|
|
76
|
+
}), item.badge]
|
|
77
|
+
}), /* @__PURE__ */ jsx(OptionDescription, {
|
|
75
78
|
id: `${uid}${index}description`,
|
|
76
|
-
|
|
77
|
-
children: item.description
|
|
79
|
+
item
|
|
78
80
|
})]
|
|
79
81
|
}),
|
|
80
82
|
/* @__PURE__ */ jsx(Select.ItemIndicator, {
|
|
@@ -7,7 +7,12 @@ export interface ComboboxItem<Value = string> {
|
|
|
7
7
|
value: Value;
|
|
8
8
|
label: string;
|
|
9
9
|
description?: string | undefined;
|
|
10
|
+
/** Read after the label in place of `description`, for what the row shows in more than
|
|
11
|
+
* words, such as a count and a status: "24 members, synced". */
|
|
12
|
+
accessibleDescription?: string | undefined;
|
|
10
13
|
icon?: ReactNode;
|
|
14
|
+
/** Beside the label, such as a tag that marks the option out. */
|
|
15
|
+
badge?: ReactNode;
|
|
11
16
|
disabled?: boolean | undefined;
|
|
12
17
|
}
|
|
13
18
|
export interface ComboboxGroup<Value = string> {
|
|
@@ -23,6 +28,11 @@ export interface ComboboxGroup<Value = string> {
|
|
|
23
28
|
export type ComboboxLeaf<Value = string> = ComboboxItem<Value> | (Value & string);
|
|
24
29
|
/** Items, groups under headings, or both: items between groups list where they sit, without a heading. */
|
|
25
30
|
export type ComboboxItems<Value = string> = readonly (ComboboxLeaf<Value> | ComboboxGroup<Value>)[];
|
|
31
|
+
/** How an option is named and described: by its label, with its description read after it. */
|
|
32
|
+
export declare function optionNaming(id: string, item: Pick<ComboboxItem<unknown>, 'description' | 'accessibleDescription'>): {
|
|
33
|
+
'aria-labelledby': string;
|
|
34
|
+
'aria-describedby': string | undefined;
|
|
35
|
+
};
|
|
26
36
|
export declare function isComboboxGroup<Value>(item: ComboboxLeaf<Value> | ComboboxGroup<Value>): item is ComboboxGroup<Value>;
|
|
27
37
|
/** Every item, out of their groups. */
|
|
28
38
|
export declare function comboboxLeaves<Value>(items: ComboboxItems<Value>): ComboboxLeaf<Value>[];
|
|
@@ -4,6 +4,13 @@ import { panelItemClassName } from "./floating.js";
|
|
|
4
4
|
/** An option's row in a Select's or a Combobox's list, so both lists look alike: the icon, the
|
|
5
5
|
* label over its description, and the check at the end. */
|
|
6
6
|
var pickerItemClassName = cx(panelItemClassName, "data-selected:font-medium");
|
|
7
|
+
/** How an option is named and described: by its label, with its description read after it. */
|
|
8
|
+
function optionNaming(id, item) {
|
|
9
|
+
return {
|
|
10
|
+
"aria-labelledby": `${id}label`,
|
|
11
|
+
"aria-describedby": item.accessibleDescription || item.description ? `${id}description` : void 0
|
|
12
|
+
};
|
|
13
|
+
}
|
|
7
14
|
function isComboboxGroup(item) {
|
|
8
15
|
return typeof item === "object" && "items" in item;
|
|
9
16
|
}
|
|
@@ -12,4 +19,4 @@ function comboboxLeaves(items) {
|
|
|
12
19
|
return items.flatMap((item) => isComboboxGroup(item) ? item.items : [item]);
|
|
13
20
|
}
|
|
14
21
|
//#endregion
|
|
15
|
-
export { comboboxLeaves, isComboboxGroup, pickerItemClassName };
|
|
22
|
+
export { comboboxLeaves, isComboboxGroup, optionNaming, pickerItemClassName };
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
export declare const positionerClassName = "z-(--layer-popover) outline-none data-anchor-hidden:invisible";
|
|
3
3
|
/** A panel of choices: a menu, a select's list, a combobox's list. */
|
|
4
4
|
export declare const panelClassName = "rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-sm text-(--theme-app) shadow-(--theme-shadow-lg) outline-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
|
|
5
|
-
/** A
|
|
6
|
-
|
|
5
|
+
/** A panel's rows inset by the same gutter on every side, so a highlighted first or last row
|
|
6
|
+
* keeps the margin it has at its sides rather than leaving a strip against the border. */
|
|
7
|
+
export declare const panelListClassName = "p-1";
|
|
8
|
+
/** A row in a panel, inside its `panelListClassName` gutter; its text lines up with a field's. */
|
|
9
|
+
export declare const panelItemClassName = "flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-2 py-1.5 text-left outline-none data-highlighted:bg-(--theme-hover) data-highlighted:text-(--theme-app) data-disabled:cursor-not-allowed data-disabled:opacity-50";
|
|
7
10
|
/** A tooltip's or infotip's small inverted surface. */
|
|
8
11
|
export declare const tipClassName = "rounded-md bg-(--theme-app) px-2 py-1 text-xs text-(--theme-app-bg) shadow-(--theme-shadow) whitespace-pre-line break-words data-instant:transition-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
|
|
9
12
|
export declare const tipArrowClassName = "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";
|
|
@@ -4,8 +4,8 @@ var positionerClassName = "z-(--layer-popover) outline-none data-anchor-hidden:i
|
|
|
4
4
|
var enterExit = "origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
|
|
5
5
|
/** A panel of choices: a menu, a select's list, a combobox's list. */
|
|
6
6
|
var panelClassName = `rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-sm text-(--theme-app) shadow-(--theme-shadow-lg) outline-none ${enterExit}`;
|
|
7
|
-
/** A row in a panel. */
|
|
8
|
-
var panelItemClassName = "flex w-full cursor-pointer select-none items-center gap-2 px-
|
|
7
|
+
/** A row in a panel, inside its `panelListClassName` gutter; its text lines up with a field's. */
|
|
8
|
+
var panelItemClassName = "flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-2 py-1.5 text-left outline-none data-highlighted:bg-(--theme-hover) data-highlighted:text-(--theme-app) data-disabled:cursor-not-allowed data-disabled:opacity-50";
|
|
9
9
|
/** A tooltip's or infotip's small inverted surface. */
|
|
10
10
|
var tipClassName = `rounded-md bg-(--theme-app) px-2 py-1 text-xs text-(--theme-app-bg) shadow-(--theme-shadow) whitespace-pre-line break-words data-instant:transition-none ${enterExit}`;
|
|
11
11
|
var tipArrowClassName = "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";
|
|
@@ -6,12 +6,11 @@ import { jsx } from "react/jsx-runtime";
|
|
|
6
6
|
//#region src/editor/EditorField.tsx
|
|
7
7
|
/** A form field that edits its value as code, in the language its `options` name (shell by default). */
|
|
8
8
|
function EditorField(props) {
|
|
9
|
-
const { field,
|
|
9
|
+
const { field, disabled, onChange } = props;
|
|
10
10
|
const [value, change] = useFieldValue(field.name ?? "");
|
|
11
11
|
return /* @__PURE__ */ jsx(Field, {
|
|
12
12
|
...fieldFrame(props),
|
|
13
13
|
children: /* @__PURE__ */ jsx(Editor, {
|
|
14
|
-
ariaLabel: label,
|
|
15
14
|
height: "200px",
|
|
16
15
|
value: value ?? "",
|
|
17
16
|
disabled,
|
package/dist/editor/Monaco.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { useStrings } from "../components/Provider.js";
|
|
3
|
+
import { useFieldLabelledControl } from "../components/Field.js";
|
|
3
4
|
import { useCssIsDark } from "../components/useCssIsDark.js";
|
|
4
5
|
import { HOST_MARKER_OWNER, LINT_OWNER } from "./lintOwner.js";
|
|
5
6
|
import { lintContext, useLintEditing, useLintSources } from "./lintContext.js";
|
|
@@ -62,6 +63,7 @@ function minimalEdit(model, from, to) {
|
|
|
62
63
|
}
|
|
63
64
|
function MonacoEditor({ className, disabled, onChange = noop, onValidate = noop, width = "100%", height = "100%", path: pathProp, preserveUndo = false, lint = false, markers, nested, value, language, ...options }) {
|
|
64
65
|
const { common: t } = useStrings();
|
|
66
|
+
const field = useFieldLabelledControl();
|
|
65
67
|
const ownPath = `file:///editor/${useId().replace(/[^\w-]/g, "")}`;
|
|
66
68
|
const path = useRef(pathProp ?? ownPath).current;
|
|
67
69
|
const isDark = useCssIsDark();
|
|
@@ -86,6 +88,7 @@ function MonacoEditor({ className, disabled, onChange = noop, onValidate = noop,
|
|
|
86
88
|
markersRef.current = markers;
|
|
87
89
|
const editorOptions = {
|
|
88
90
|
ariaLabel: t.codeEditor,
|
|
91
|
+
...field?.["aria-required"] && { ariaRequired: true },
|
|
89
92
|
readOnlyMessage: { value: t.readOnlyText },
|
|
90
93
|
domReadOnly: readOnly,
|
|
91
94
|
hideCursorInOverviewRuler: readOnly,
|
|
@@ -201,9 +204,30 @@ function MonacoEditor({ className, disabled, onChange = noop, onValidate = noop,
|
|
|
201
204
|
for (const name of DISABLED_BACKGROUNDS) if (disabled) node?.style.setProperty(name, "var(--theme-input-disabled-bg)");
|
|
202
205
|
else node?.style.removeProperty(name);
|
|
203
206
|
}, [disabled]);
|
|
207
|
+
const fieldId = field?.id;
|
|
208
|
+
const labelledBy = field?.["aria-labelledby"];
|
|
209
|
+
const describedBy = field?.["aria-describedby"];
|
|
210
|
+
const invalid = field?.["aria-invalid"];
|
|
204
211
|
useEffect(() => {
|
|
205
|
-
monacoRef.current?.getDomNode()?.querySelector("textarea.inputarea, .native-edit-context")
|
|
206
|
-
|
|
212
|
+
const input = monacoRef.current?.getDomNode()?.querySelector("textarea.inputarea, .native-edit-context");
|
|
213
|
+
if (!input) return;
|
|
214
|
+
input.setAttribute("aria-readonly", String(readOnly));
|
|
215
|
+
const attributes = {
|
|
216
|
+
id: fieldId,
|
|
217
|
+
"aria-labelledby": labelledBy,
|
|
218
|
+
"aria-describedby": describedBy,
|
|
219
|
+
"aria-invalid": invalid && "true"
|
|
220
|
+
};
|
|
221
|
+
for (const [name, value] of Object.entries(attributes)) if (value) input.setAttribute(name, value);
|
|
222
|
+
else input.removeAttribute(name);
|
|
223
|
+
}, [
|
|
224
|
+
readOnly,
|
|
225
|
+
path,
|
|
226
|
+
fieldId,
|
|
227
|
+
labelledBy,
|
|
228
|
+
describedBy,
|
|
229
|
+
invalid
|
|
230
|
+
]);
|
|
207
231
|
useEffect(() => {
|
|
208
232
|
monacoRef.current?.updateOptions(optionsRef.current);
|
|
209
233
|
}, [optionsKey]);
|