@parallelworks/ui 0.52.0 → 0.54.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 +97 -8
- 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/ai.css +5 -3
- package/dist/ai/components/SlashMenu.js +1 -1
- package/dist/ai/components/agent/AgentMessageParts.js +1 -1
- package/dist/ai/strings.js +0 -1
- package/dist/ai/ui/sidebar.js +1 -1
- package/dist/components/Combobox.d.ts +19 -0
- package/dist/components/Combobox.js +101 -50
- package/dist/components/Drawer.d.ts +6 -1
- package/dist/components/Drawer.js +25 -16
- package/dist/components/Field.d.ts +5 -4
- package/dist/components/Field.js +27 -4
- package/dist/components/InfoTip.d.ts +12 -4
- package/dist/components/InfoTip.js +13 -3
- package/dist/components/MarkdownImpl.d.ts +1 -0
- package/dist/components/MarkdownImpl.js +1 -0
- 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 -1
- package/dist/components/Provider.d.ts +9 -0
- package/dist/components/Provider.js +4 -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/components/focus.d.ts +5 -0
- package/dist/components/focus.js +21 -1
- package/dist/editor/Editor.js +13 -3
- package/dist/editor/Monaco.d.ts +8 -1
- package/dist/editor/Monaco.js +16 -3
- package/dist/editor/editorModel.d.ts +1 -1
- package/dist/editor/editorModel.js +1 -1
- package/dist/editor/index.d.ts +1 -0
- package/dist/editor/index.js +2 -1
- package/dist/editor/languages.d.ts +24 -0
- package/dist/editor/languages.js +31 -0
- package/dist/editor/monacoFeatures.d.ts +7 -0
- package/dist/editor/monacoFeatures.js +19 -0
- package/dist/editor/themes.d.ts +5 -0
- package/dist/editor/themes.js +24 -4
- package/dist/editor/workers.d.ts +3 -3
- package/dist/editor/workers.js +8 -4
- package/dist/editor/yamlLanguage.js +55 -38
- package/dist/file-explorer/FileExplorer.d.ts +18 -4
- package/dist/file-explorer/FileExplorer.js +64 -20
- package/dist/file-explorer/FilePreview/Viewer.js +12 -8
- package/dist/file-explorer/FileRow.d.ts +6 -2
- package/dist/file-explorer/FileRow.js +35 -17
- package/dist/file-explorer/FileRowsWindow.d.ts +2 -1
- package/dist/file-explorer/FileRowsWindow.js +2 -1
- package/dist/file-explorer/FileTable.d.ts +3 -1
- package/dist/file-explorer/FileTable.js +3 -2
- package/dist/file-explorer/TreeView.d.ts +5 -1
- package/dist/file-explorer/TreeView.js +49 -25
- package/dist/file-explorer/helpers.js +3 -3
- package/dist/file-explorer/lib/paging.d.ts +1 -0
- package/dist/file-explorer/lib/paging.js +5 -1
- package/dist/file-explorer/lib/utils.d.ts +3 -0
- package/dist/file-explorer/lib/utils.js +6 -1
- package/dist/form/Wizard/utils.js +2 -1
- package/dist/list/ActionToolbar.js +3 -2
- package/dist/list/ListPage.js +1 -1
- package/dist/list/ListTable.d.ts +5 -0
- package/dist/list/ListTable.js +6 -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 +61 -3
- package/package.json +9 -2
- package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +0 -6
- package/dist/file-explorer/FilePreview/CodePreviewEditor.js +0 -64
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { AddIcon, CheckIcon, ChevronDownIcon, RemoveIcon } from "../icons.js";
|
|
3
3
|
import { panelClassName, positionerClassName } from "./floating.js";
|
|
4
|
+
import { Loader } from "./Loader.js";
|
|
4
5
|
import { Tooltip } from "./Tooltip.js";
|
|
5
6
|
import { useStrings } from "./Provider.js";
|
|
6
7
|
import { useQueryMatch } from "./search.js";
|
|
7
8
|
import { fieldBox } from "./fieldBox.js";
|
|
8
|
-
import { isComboboxGroup, pickerItemClassName } from "./comboboxItems.js";
|
|
9
|
+
import { isComboboxGroup, optionNaming, pickerItemClassName } from "./comboboxItems.js";
|
|
10
|
+
import { OptionDescription } from "./OptionDescription.js";
|
|
9
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
12
|
import { useCallback, useId, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
11
13
|
import { dequal } from "dequal";
|
|
@@ -79,13 +81,14 @@ var triggerClassName = "flex shrink-0 items-center self-stretch px-2 text-(--the
|
|
|
79
81
|
var popupInputClassName = fieldBox({ size: "sm" });
|
|
80
82
|
/** A pick-from-the-list field: one value, or several with `multiple`. Typing filters the list. */
|
|
81
83
|
function Combobox$1(props) {
|
|
82
|
-
const { items, isItemEqualToValue = dequal, itemToStringLabel = String, className, placeholder, disabled, required, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, trigger, triggerTooltip, size = "md" } = props;
|
|
84
|
+
const { items, isItemEqualToValue = dequal, itemToStringLabel = String, className, placeholder, disabled, required, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, trigger, triggerTooltip, size = "md", details, detailsSummary, onItemHighlighted, onQueryChange, loading = false, collisionBoundary } = props;
|
|
83
85
|
const entries = useEntries(items);
|
|
84
86
|
const filter = useEntryFilter();
|
|
85
87
|
const t = useStrings().dropdown;
|
|
86
88
|
const list = useRef(null);
|
|
87
89
|
const uid = useId();
|
|
88
90
|
const [inputValue, setInputValue] = useState(null);
|
|
91
|
+
const [highlighted, setHighlighted] = useState(void 0);
|
|
89
92
|
const entryOf = (value) => entries.flat.find((entry) => isItemEqualToValue(entry.value, value)) ?? {
|
|
90
93
|
value,
|
|
91
94
|
label: itemToStringLabel(value)
|
|
@@ -125,15 +128,21 @@ function Combobox$1(props) {
|
|
|
125
128
|
autoComplete: "off",
|
|
126
129
|
className: trigger ? popupInputClassName : inputClassName(size)
|
|
127
130
|
});
|
|
131
|
+
const isHighlighted = (entry) => highlighted !== void 0 && !entry.created && !highlighted.created && isItemEqualToValue(entry.value, highlighted.value);
|
|
128
132
|
const itemRow = (entry, rowProps) => /* @__PURE__ */ jsxs(Combobox.Item, {
|
|
129
133
|
value: entry,
|
|
130
134
|
disabled: entry.disabled || full && !isChosen(entry),
|
|
131
135
|
...rowProps.props,
|
|
132
|
-
...optionNaming(uid
|
|
136
|
+
...optionNaming(`${uid}${rowProps.props.index}`, entry),
|
|
133
137
|
className: pickerItemClassName,
|
|
134
138
|
children: [/* @__PURE__ */ jsx(ItemContent, {
|
|
135
139
|
entry,
|
|
136
|
-
id: `${uid}${rowProps.props.index}
|
|
140
|
+
id: `${uid}${rowProps.props.index}`,
|
|
141
|
+
children: detailsSummary && isHighlighted(entry) && /* @__PURE__ */ jsx("span", {
|
|
142
|
+
"aria-hidden": "true",
|
|
143
|
+
className: "mt-0.5 text-xs leading-snug text-(--theme-muted-text-color) @min-[30rem]/picker:hidden",
|
|
144
|
+
children: detailsSummary(entry.value)
|
|
145
|
+
})
|
|
137
146
|
}), /* @__PURE__ */ jsx(Combobox.ItemIndicator, {
|
|
138
147
|
className: "ml-auto flex shrink-0 items-center",
|
|
139
148
|
children: /* @__PURE__ */ jsx(CheckIcon, {
|
|
@@ -162,7 +171,10 @@ function Combobox$1(props) {
|
|
|
162
171
|
className: "border-b border-(--theme-border) p-1.5",
|
|
163
172
|
children: input
|
|
164
173
|
}),
|
|
165
|
-
className: "w-max min-w-(--anchor-width) max-w-[min(28rem,var(--available-width))]",
|
|
174
|
+
className: details ? "w-[min(36rem,var(--available-width))]" : "w-max min-w-(--anchor-width) max-w-[min(28rem,var(--available-width))]",
|
|
175
|
+
loading: loading ? t.searching : void 0,
|
|
176
|
+
details: details?.(highlighted?.created ? void 0 : highlighted?.value),
|
|
177
|
+
collisionBoundary,
|
|
166
178
|
renderItem: itemRow
|
|
167
179
|
})] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Combobox.InputGroup, {
|
|
168
180
|
"data-testid": "combobox",
|
|
@@ -198,15 +210,25 @@ function Combobox$1(props) {
|
|
|
198
210
|
empty: t.noOptionsFound,
|
|
199
211
|
statusGroups: entries.statusGroups,
|
|
200
212
|
query: inputValue ?? "",
|
|
213
|
+
loading: loading ? t.searching : void 0,
|
|
214
|
+
details: details?.(highlighted?.created ? void 0 : highlighted?.value),
|
|
215
|
+
collisionBoundary,
|
|
201
216
|
renderItem: itemRow
|
|
202
217
|
})] });
|
|
203
218
|
const root = {
|
|
204
219
|
items: listItems,
|
|
205
|
-
filter,
|
|
206
220
|
isItemEqualToValue: (a, b) => isItemEqualToValue(a.value, b.value),
|
|
207
221
|
itemToStringLabel: entryLabel,
|
|
208
|
-
|
|
209
|
-
|
|
222
|
+
filter: onQueryChange ? null : filter,
|
|
223
|
+
onInputValueChange: (next) => {
|
|
224
|
+
setInputValue(next);
|
|
225
|
+
onQueryChange?.(next);
|
|
226
|
+
},
|
|
227
|
+
onItemHighlighted: (entry, { index, reason }) => {
|
|
228
|
+
if (details || detailsSummary) setHighlighted(entry);
|
|
229
|
+
onItemHighlighted?.(entry && !entry.created ? entry.value : void 0);
|
|
230
|
+
list.current?.reveal(index, reason);
|
|
231
|
+
},
|
|
210
232
|
disabled,
|
|
211
233
|
required,
|
|
212
234
|
virtualized: true,
|
|
@@ -218,7 +240,12 @@ function Combobox$1(props) {
|
|
|
218
240
|
multiple: true,
|
|
219
241
|
value: toEntries(props.value),
|
|
220
242
|
defaultValue: toEntries(props.defaultValue),
|
|
221
|
-
onValueChange: (next) => {
|
|
243
|
+
onValueChange: (next, details) => {
|
|
244
|
+
if (details.reason === "escape-key") {
|
|
245
|
+
details.cancel();
|
|
246
|
+
details.allowPropagation();
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
222
249
|
setUncontrolledCount(next.length);
|
|
223
250
|
props.onValueChange?.(next.map((entry) => entry.value));
|
|
224
251
|
}
|
|
@@ -275,7 +302,7 @@ function Autocomplete$1({ items, value, defaultValue, onValueChange, className,
|
|
|
275
302
|
value: entry,
|
|
276
303
|
disabled: entry.disabled,
|
|
277
304
|
...rowProps.props,
|
|
278
|
-
...optionNaming(uid
|
|
305
|
+
...optionNaming(`${uid}${rowProps.props.index}`, entry),
|
|
279
306
|
className: pickerItemClassName,
|
|
280
307
|
children: /* @__PURE__ */ jsx(ItemContent, {
|
|
281
308
|
entry,
|
|
@@ -285,33 +312,31 @@ function Autocomplete$1({ items, value, defaultValue, onValueChange, className,
|
|
|
285
312
|
})]
|
|
286
313
|
});
|
|
287
314
|
}
|
|
288
|
-
|
|
289
|
-
* its name. */
|
|
290
|
-
function optionNaming(uid, index, entry) {
|
|
291
|
-
return {
|
|
292
|
-
"aria-labelledby": `${uid}${index}label`,
|
|
293
|
-
"aria-describedby": entry.description ? `${uid}${index}description` : void 0
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
function ItemContent({ entry, id }) {
|
|
315
|
+
function ItemContent({ entry, id, children }) {
|
|
297
316
|
return /* @__PURE__ */ jsxs(Fragment, { children: [entry.icon && /* @__PURE__ */ jsx("span", {
|
|
298
317
|
className: "flex shrink-0 items-center",
|
|
299
318
|
children: entry.icon
|
|
300
319
|
}), /* @__PURE__ */ jsxs("span", {
|
|
301
320
|
className: "flex min-w-0 flex-col",
|
|
302
|
-
children: [
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
321
|
+
children: [
|
|
322
|
+
/* @__PURE__ */ jsxs("span", {
|
|
323
|
+
className: "flex min-w-0 items-center gap-2",
|
|
324
|
+
children: [/* @__PURE__ */ jsx(Tooltip, {
|
|
325
|
+
content: entry.label,
|
|
326
|
+
overflowOnly: true,
|
|
327
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
328
|
+
id: `${id}label`,
|
|
329
|
+
className: "truncate",
|
|
330
|
+
children: entry.label
|
|
331
|
+
})
|
|
332
|
+
}), entry.badge]
|
|
333
|
+
}),
|
|
334
|
+
/* @__PURE__ */ jsx(OptionDescription, {
|
|
335
|
+
id: `${id}description`,
|
|
336
|
+
item: entry
|
|
337
|
+
}),
|
|
338
|
+
children
|
|
339
|
+
]
|
|
315
340
|
})] });
|
|
316
341
|
}
|
|
317
342
|
function GroupHeading({ group }) {
|
|
@@ -335,26 +360,52 @@ function GroupHeading({ group }) {
|
|
|
335
360
|
] });
|
|
336
361
|
}
|
|
337
362
|
/** Portal, positioner and popup shared by Combobox and Autocomplete; both use the same parts. */
|
|
338
|
-
function ListPopup({ listRef, empty, statusGroups = [], query = "", header, className = "w-(--anchor-width) max-w-(--available-width)", renderItem }) {
|
|
363
|
+
function ListPopup({ listRef, empty, statusGroups = [], query = "", header, className = "w-(--anchor-width) max-w-(--available-width)", loading, details, collisionBoundary, renderItem }) {
|
|
364
|
+
const withDetails = details !== void 0;
|
|
365
|
+
const list = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
366
|
+
loading && /* @__PURE__ */ jsxs("div", {
|
|
367
|
+
role: "status",
|
|
368
|
+
className: "flex items-center gap-2 px-3 py-2 text-(--theme-muted-text-color)",
|
|
369
|
+
children: [/* @__PURE__ */ jsx(Loader, {
|
|
370
|
+
inline: true,
|
|
371
|
+
decorative: true,
|
|
372
|
+
size: "1em"
|
|
373
|
+
}), loading]
|
|
374
|
+
}),
|
|
375
|
+
empty && !loading && statusGroups.length === 0 && /* @__PURE__ */ jsx(Combobox.Empty, {
|
|
376
|
+
className: "px-3 py-2 text-(--theme-muted-text-color) empty:hidden",
|
|
377
|
+
children: empty
|
|
378
|
+
}),
|
|
379
|
+
/* @__PURE__ */ jsx(Combobox.List, {
|
|
380
|
+
className: cx(withDetails && "min-h-0 flex-1"),
|
|
381
|
+
children: /* @__PURE__ */ jsx(VirtualList, {
|
|
382
|
+
ref: listRef,
|
|
383
|
+
statusGroups,
|
|
384
|
+
query,
|
|
385
|
+
tall: withDetails,
|
|
386
|
+
renderItem
|
|
387
|
+
})
|
|
388
|
+
})
|
|
389
|
+
] });
|
|
339
390
|
return /* @__PURE__ */ jsx(Combobox.Portal, { children: /* @__PURE__ */ jsx(Combobox.Positioner, {
|
|
340
391
|
align: "start",
|
|
341
392
|
sideOffset: 4,
|
|
342
393
|
className: positionerClassName,
|
|
394
|
+
...collisionBoundary && { collisionBoundary },
|
|
343
395
|
children: /* @__PURE__ */ jsxs(Combobox.Popup, {
|
|
344
|
-
className: cx("overflow-hidden", className, panelClassName),
|
|
345
|
-
children: [
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
className: "
|
|
349
|
-
children:
|
|
350
|
-
}),
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
]
|
|
396
|
+
className: cx("overflow-hidden", withDetails && "@container/picker flex h-[min(40rem,var(--available-height))] flex-col", className, panelClassName),
|
|
397
|
+
children: [header, withDetails ? /* @__PURE__ */ jsxs("div", {
|
|
398
|
+
className: "flex min-h-0 flex-1",
|
|
399
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
400
|
+
className: "flex min-w-0 flex-1 flex-col",
|
|
401
|
+
children: list
|
|
402
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
403
|
+
"aria-hidden": "true",
|
|
404
|
+
"data-testid": "combobox-details",
|
|
405
|
+
className: "hidden w-1/2 shrink-0 overflow-y-auto overscroll-contain border-l border-(--theme-border) bg-(--theme-panel-bg) @min-[30rem]/picker:block",
|
|
406
|
+
children: [details, /* @__PURE__ */ jsx("div", { className: "pointer-events-none sticky bottom-0 -mt-6 h-6 bg-linear-to-t from-(--theme-panel-bg) to-transparent" })]
|
|
407
|
+
})]
|
|
408
|
+
}) : list]
|
|
358
409
|
})
|
|
359
410
|
}) });
|
|
360
411
|
}
|
|
@@ -382,7 +433,7 @@ function toRows(filtered, statusGroups) {
|
|
|
382
433
|
rowOfItem
|
|
383
434
|
};
|
|
384
435
|
}
|
|
385
|
-
function VirtualList({ ref, statusGroups, query, renderItem }) {
|
|
436
|
+
function VirtualList({ ref, statusGroups, query, tall, renderItem }) {
|
|
386
437
|
const filtered = Combobox.useFilteredItems();
|
|
387
438
|
const match = useQueryMatch();
|
|
388
439
|
const { rows, rowOfItem } = useMemo(() => {
|
|
@@ -420,7 +471,7 @@ function VirtualList({ ref, statusGroups, query, renderItem }) {
|
|
|
420
471
|
return /* @__PURE__ */ jsx("div", {
|
|
421
472
|
role: "presentation",
|
|
422
473
|
ref: scrollerRef,
|
|
423
|
-
className: "max-h-[min(15rem,var(--available-height))]
|
|
474
|
+
className: cx("overflow-auto overscroll-contain px-1", tall ? "max-h-full" : "max-h-[min(15rem,var(--available-height))]"),
|
|
424
475
|
style: { height: total },
|
|
425
476
|
children: /* @__PURE__ */ jsx("div", {
|
|
426
477
|
role: "presentation",
|
|
@@ -441,7 +492,7 @@ function VirtualList({ ref, statusGroups, query, renderItem }) {
|
|
|
441
492
|
"data-index": virtual.index,
|
|
442
493
|
ref: virtualizer.measureElement,
|
|
443
494
|
style,
|
|
444
|
-
className: "flex items-center gap-1.5 px-
|
|
495
|
+
className: "flex items-center gap-1.5 px-2 pt-2 pb-1 text-xs text-(--theme-muted-text-color)",
|
|
445
496
|
children: /* @__PURE__ */ jsx(GroupHeading, { group: row.group })
|
|
446
497
|
}, virtual.key);
|
|
447
498
|
return renderItem(row.entry, {
|
|
@@ -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),
|
|
@@ -26,10 +26,11 @@ export interface FieldProps {
|
|
|
26
26
|
/** Beside the label and outside it, so it stays out of the control's name, such as a count. For
|
|
27
27
|
* help text, use `help`. */
|
|
28
28
|
hint?: ReactNode;
|
|
29
|
-
/** More about the field, behind a ? beside the label
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* show as such
|
|
29
|
+
/** More about the field, behind a ? beside the label. It is read with the control as part of
|
|
30
|
+
* its description, so the ? is hidden from assistive technology and Tab passes over it. It
|
|
31
|
+
* opens on hover or press of the ?, and while the control has keyboard focus, until Escape.
|
|
32
|
+
* `[text](url)` links and line breaks show as such; a link keeps the ? in the Tab order, as
|
|
33
|
+
* the keyboard's way to it. */
|
|
33
34
|
help?: string | undefined;
|
|
34
35
|
/** Buttons at the far end of the label's row that act on the field, such as one that
|
|
35
36
|
* switches its control to a typed expression. */
|
package/dist/components/Field.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { EyeIcon, EyeOffIcon, WarningIcon } from "../icons.js";
|
|
3
3
|
import { useStrings } from "./Provider.js";
|
|
4
|
+
import { focusCameFromKeyboard, trackFocusModality } from "./focus.js";
|
|
4
5
|
import { InfoTip, formatInfoTipContent } from "./InfoTip.js";
|
|
5
6
|
import { RequiredMark } from "./RequiredMark.js";
|
|
6
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
-
import { createContext, use, useId } from "react";
|
|
8
|
+
import { createContext, use, useEffect, useId, useState } from "react";
|
|
8
9
|
import { Field } from "@base-ui/react/field";
|
|
9
10
|
import { Fieldset } from "@base-ui/react/fieldset";
|
|
10
11
|
//#region src/components/Field.tsx
|
|
@@ -81,10 +82,31 @@ function Field$1({ label, hint, help, actions, description, error, invalid, requ
|
|
|
81
82
|
const inline = layout === "inline";
|
|
82
83
|
const inRow = use(FieldRowContext) && !inline && !group;
|
|
83
84
|
const labelSize = labelSizes[size];
|
|
85
|
+
const [helpOpen, setHelpOpen] = useState(false);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (help) trackFocusModality();
|
|
88
|
+
}, [help]);
|
|
84
89
|
const helpTip = help && /* @__PURE__ */ jsx(InfoTip, {
|
|
85
90
|
text: help,
|
|
86
|
-
|
|
91
|
+
describesControl: true,
|
|
92
|
+
open: Boolean(helpOpen),
|
|
93
|
+
onOpenChange: (open) => setHelpOpen(open && "pointer"),
|
|
94
|
+
keepSide: helpOpen === "focus"
|
|
87
95
|
});
|
|
96
|
+
const helpFocus = help ? {
|
|
97
|
+
onFocus: () => {
|
|
98
|
+
if (focusCameFromKeyboard()) setHelpOpen("focus");
|
|
99
|
+
},
|
|
100
|
+
onBlur: (event) => {
|
|
101
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setHelpOpen(false);
|
|
102
|
+
},
|
|
103
|
+
onKeyDown: (event) => {
|
|
104
|
+
if (event.key === "Escape" && helpOpen) {
|
|
105
|
+
event.stopPropagation();
|
|
106
|
+
setHelpOpen(false);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} : {};
|
|
88
110
|
const labelBox = cx(labelClassName, labelSize.text, inline && cx(labelSize.inlineHeight, "shrink-0", compact ? "mr-2 w-fit" : "w-52 pr-3"), inRow && "row-start-1 self-end");
|
|
89
111
|
const labelContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
90
112
|
/* @__PURE__ */ jsxs("span", { children: [label, required && /* @__PURE__ */ jsx(RequiredMark, {})] }),
|
|
@@ -98,6 +120,7 @@ function Field$1({ label, hint, help, actions, description, error, invalid, requ
|
|
|
98
120
|
const placed = typeof children === "function" ? children(control) : children;
|
|
99
121
|
const body = /* @__PURE__ */ jsxs("div", {
|
|
100
122
|
className: cx("flex w-full min-w-0 flex-col gap-1", inRow && "row-start-2"),
|
|
123
|
+
...helpFocus,
|
|
101
124
|
children: [
|
|
102
125
|
/* @__PURE__ */ jsx(FieldRowContext, {
|
|
103
126
|
value: false,
|
|
@@ -116,7 +139,7 @@ function Field$1({ label, hint, help, actions, description, error, invalid, requ
|
|
|
116
139
|
}),
|
|
117
140
|
help && /* @__PURE__ */ jsx(Field.Description, {
|
|
118
141
|
id: helpId,
|
|
119
|
-
|
|
142
|
+
hidden: true,
|
|
120
143
|
children: formatInfoTipContent(help)
|
|
121
144
|
}),
|
|
122
145
|
error && /* @__PURE__ */ jsxs(Field.Error, {
|
|
@@ -131,7 +154,7 @@ function Field$1({ label, hint, help, actions, description, error, invalid, requ
|
|
|
131
154
|
})
|
|
132
155
|
]
|
|
133
156
|
});
|
|
134
|
-
const frame = inRow ? cx("
|
|
157
|
+
const frame = inRow ? cx("contents", className) : cx("flex w-full", inline ? "items-start" : cx("flex-col", labelSize.stackGap), className);
|
|
135
158
|
if (group) return /* @__PURE__ */ jsx(Field.Root, {
|
|
136
159
|
invalid: isInvalid,
|
|
137
160
|
disabled,
|
|
@@ -8,11 +8,19 @@ interface InfoTipProps {
|
|
|
8
8
|
className?: string | undefined;
|
|
9
9
|
/** Replaces the info icon as the trigger, such as a status label the text explains. */
|
|
10
10
|
children?: ReactNode;
|
|
11
|
-
/**
|
|
12
|
-
*
|
|
13
|
-
|
|
11
|
+
/** The text is also a control's description, which assistive technology reads with the
|
|
12
|
+
* control, so the ? is hidden from it and skipped by Tab rather than offering the text twice.
|
|
13
|
+
* Text with a link keeps the ? reachable, as the only way to the link from the keyboard. */
|
|
14
|
+
describesControl?: boolean | undefined;
|
|
15
|
+
/** Opens it from outside as well as on hover and press, such as while the described control
|
|
16
|
+
* has keyboard focus. Pair it with `onOpenChange`. */
|
|
17
|
+
open?: boolean | undefined;
|
|
18
|
+
onOpenChange?: ((open: boolean) => void) | undefined;
|
|
19
|
+
/** Stays on `side` rather than flipping where it doesn't fit, as while the control it
|
|
20
|
+
* describes is typed in, which a flipped tip would cover. */
|
|
21
|
+
keepSide?: boolean | undefined;
|
|
14
22
|
}
|
|
15
23
|
/** Extra detail behind an info trigger. A popover that also opens on hover, so
|
|
16
24
|
* touch and screen reader users can reach it and its links. */
|
|
17
|
-
export declare function InfoTip({ text, side, className, children,
|
|
25
|
+
export declare function InfoTip({ text, side, className, children, describesControl, open, onOpenChange, keepSide, }: InfoTipProps): import("react").JSX.Element | null;
|
|
18
26
|
export {};
|
|
@@ -28,22 +28,32 @@ function formatInfoTipContent(content) {
|
|
|
28
28
|
if (!content) return null;
|
|
29
29
|
return keyedByContent(content.split(/<br\s*\/?>|\n/i), (l) => l).map(({ key, item: line }, idx) => /* @__PURE__ */ jsxs(Fragment$1, { children: [idx > 0 && /* @__PURE__ */ jsx("br", {}), formatLine(line)] }, key));
|
|
30
30
|
}
|
|
31
|
+
var hasLink = (content) => new RegExp(linkRegex.source).test(content);
|
|
31
32
|
/** Extra detail behind an info trigger. A popover that also opens on hover, so
|
|
32
33
|
* touch and screen reader users can reach it and its links. */
|
|
33
|
-
function InfoTip({ text, side = "top", className, children,
|
|
34
|
+
function InfoTip({ text, side = "top", className, children, describesControl = false, open, onOpenChange, keepSide = false }) {
|
|
34
35
|
const t = useStrings().common;
|
|
35
36
|
if (!text) return children ? /* @__PURE__ */ jsx("span", {
|
|
36
37
|
className,
|
|
37
38
|
children
|
|
38
39
|
}) : null;
|
|
40
|
+
const hidden = describesControl && !hasLink(text);
|
|
39
41
|
return /* @__PURE__ */ jsx(Popover, {
|
|
40
42
|
variant: "tip",
|
|
41
43
|
openOnHover: true,
|
|
42
44
|
side,
|
|
45
|
+
keepSide,
|
|
46
|
+
...open === void 0 ? {} : { open },
|
|
47
|
+
...onOpenChange ? { onOpenChange } : {},
|
|
48
|
+
...describesControl && { initialFocus: "none" },
|
|
49
|
+
...hidden && { finalFocus: false },
|
|
43
50
|
trigger: /* @__PURE__ */ jsx("button", {
|
|
44
51
|
type: "button",
|
|
45
|
-
|
|
46
|
-
|
|
52
|
+
...hidden && {
|
|
53
|
+
tabIndex: -1,
|
|
54
|
+
"aria-hidden": true
|
|
55
|
+
},
|
|
56
|
+
"aria-label": children || hidden ? void 0 : t.moreInfo,
|
|
47
57
|
className: cx("inline-flex items-center cursor-help", !children && "tooltip", className),
|
|
48
58
|
children: children ?? /* @__PURE__ */ jsx(HelpIcon, { "aria-hidden": "true" })
|
|
49
59
|
}),
|
|
@@ -7,6 +7,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
7
7
|
import { useEffect, useState } from "react";
|
|
8
8
|
import { code } from "@streamdown/code";
|
|
9
9
|
import { math } from "@streamdown/math";
|
|
10
|
+
import "katex/dist/katex.min.css";
|
|
10
11
|
import { Streamdown, defaultRehypePlugins } from "streamdown";
|
|
11
12
|
//#region src/components/MarkdownImpl.tsx
|
|
12
13
|
var basePlugins = {
|
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
|
+
/** Stays on `side` where it doesn't fit rather than flipping to the other, for a popup that
|
|
37
|
+
* must not cover what is there, such as the control a tip describes while it is typed in. */
|
|
38
|
+
keepSide?: boolean | undefined;
|
|
36
39
|
/** `panel` holds controls or a list; `tip` is a tooltip-sized note with an arrow. */
|
|
37
40
|
variant?: 'panel' | 'tip';
|
|
38
41
|
/** On the popup: its width, padding or text size. */
|
|
@@ -43,5 +46,5 @@ export interface PopoverOptions extends Omit<BasePopover.Popup.Props, 'className
|
|
|
43
46
|
* flips and shifts to stay on screen, closes on Escape or a press outside, and
|
|
44
47
|
* returns focus to the trigger. Use a Menu for a list of actions, and a
|
|
45
48
|
* HoverCard for a preview a link already leads to. */
|
|
46
|
-
export declare function Popover({ trigger, at, title, open, defaultOpen, onOpenChange, side, align, openOnHover, initialFocus, variant, className, children, ...popupProps }: PopoverProps): import("react").JSX.Element;
|
|
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;
|
|
47
50
|
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", variant = "panel", className, children, ...popupProps }) {
|
|
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
20
|
const tip = variant === "tip";
|
|
21
21
|
const popup = useRef(null);
|
|
22
22
|
const actions = useRef(null);
|
|
@@ -38,6 +38,7 @@ function Popover$1({ trigger, at, title, open, defaultOpen, onOpenChange, side =
|
|
|
38
38
|
align,
|
|
39
39
|
sideOffset: tip ? 8 : 6,
|
|
40
40
|
collisionPadding: 8,
|
|
41
|
+
...keepSide && { collisionAvoidance: { side: "none" } },
|
|
41
42
|
...at ? { anchor: toPositionerAnchor(at) } : {},
|
|
42
43
|
className: positionerClassName,
|
|
43
44
|
children: /* @__PURE__ */ jsxs(Popover.Popup, {
|