@parallelworks/ui 0.80.0 → 0.82.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 +7 -1
- package/dist/ai/adapter/types.d.ts +1 -0
- package/dist/ai/ai.css +3 -92
- package/dist/ai/components/agent/AgentMessageParts.js +1 -0
- package/dist/ai/core/ChatProvider.js +10 -5
- package/dist/ai/strings.d.ts +3 -0
- package/dist/ai/strings.js +2 -0
- package/dist/ai/types.d.ts +5 -0
- package/dist/components/Avatar.d.ts +5 -3
- package/dist/components/Avatar.js +6 -2
- package/dist/components/Card.d.ts +16 -1
- package/dist/components/Card.js +13 -2
- package/dist/components/CodeBlock.d.ts +40 -0
- package/dist/components/CodeBlock.js +159 -0
- package/dist/components/CopyButton.d.ts +11 -4
- package/dist/components/CopyButton.js +86 -11
- package/dist/components/CreateModal.d.ts +3 -1
- package/dist/components/CreateModal.js +31 -11
- package/dist/components/DescriptionList.js +1 -1
- package/dist/components/Drawer.js +1 -1
- package/dist/components/Input.d.ts +5 -0
- package/dist/components/Input.js +4 -3
- package/dist/components/Listbox.d.ts +12 -3
- package/dist/components/Listbox.js +38 -15
- package/dist/components/MarkdownImpl.js +89 -2
- package/dist/components/MenuEntries.d.ts +3 -0
- package/dist/components/MenuEntries.js +19 -6
- package/dist/components/Modal.d.ts +1 -1
- package/dist/components/Modal.js +6 -5
- package/dist/components/PageHeader.d.ts +7 -1
- package/dist/components/PageHeader.js +11 -5
- package/dist/components/Provider.d.ts +3 -0
- package/dist/components/Provider.js +1 -0
- package/dist/components/Select.js +1 -1
- package/dist/components/SettingsCard.d.ts +7 -2
- package/dist/components/SettingsCard.js +42 -7
- package/dist/components/ToggleGroup.d.ts +3 -1
- package/dist/components/ToggleGroup.js +2 -2
- package/dist/components/Tooltip.js +35 -2
- package/dist/components/codeHighlight.d.ts +9 -0
- package/dist/components/codeHighlight.js +29 -0
- package/dist/components/pageHeaderStore.d.ts +4 -0
- package/dist/components/pageHeaderStore.js +5 -2
- package/dist/file-explorer/FileExplorer.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -2
- package/dist/list/ListFilterBar.js +6 -5
- package/dist/list/filterModel.d.ts +4 -0
- package/dist/list/filterModel.js +7 -1
- package/dist/styles/theme.css +1 -0
- package/dist/styles.css +1 -1
- package/dist/theme/derive.d.ts +1 -1
- package/dist/theme/derive.js +3 -1
- package/package.json +1 -1
package/dist/components/Modal.js
CHANGED
|
@@ -10,7 +10,8 @@ import { AlertDialog } from "@base-ui/react/alert-dialog";
|
|
|
10
10
|
import { Dialog } from "@base-ui/react/dialog";
|
|
11
11
|
//#region src/components/Modal.tsx
|
|
12
12
|
/** The panel a modal draws by default; pass `panel={false}` to paint your own. */
|
|
13
|
-
var modalPanelClassName = "rounded-xl border border-(--theme-border) bg-(--theme-
|
|
13
|
+
var modalPanelClassName = "rounded-xl border border-(--theme-border) bg-(--theme-panel-bg) text-(--theme-app) shadow-2xl";
|
|
14
|
+
var phoneFooterClassName = "max-sm:flex-wrap max-sm:[&>:is(button,a)]:h-10 max-sm:[&>:is(button,a)]:flex-1 max-sm:[&>:not(button,a)]:basis-full";
|
|
14
15
|
/** Names the modal for assistive technology; renders an `<h2>`. */
|
|
15
16
|
var ModalTitle = Dialog.Title;
|
|
16
17
|
/** Describes the modal for assistive technology; renders a `<p>`. */
|
|
@@ -32,7 +33,7 @@ function firstControlIn(panel) {
|
|
|
32
33
|
* and focus returns to where it was on close. Give it a `title`, a `description`
|
|
33
34
|
* and a `footer` for the standard layout, as a Drawer has, or lay out the whole
|
|
34
35
|
* panel with the children. */
|
|
35
|
-
function Modal({ open, onClose, title, description, footer, size, alert = false, dismissible = true, align = "top", panel = true, backdropClassName = "bg-
|
|
36
|
+
function Modal({ open, onClose, title, description, footer, size, alert = false, dismissible = true, align = "top", panel = true, backdropClassName = "bg-(--theme-backdrop)", closeButton = false, onSubmit, className, children, initialFocus, ...popupProps }) {
|
|
36
37
|
const portal = usePopupContainer();
|
|
37
38
|
const { common } = useStrings();
|
|
38
39
|
const panelElement = useRef(null);
|
|
@@ -67,14 +68,14 @@ function Modal({ open, onClose, title, description, footer, size, alert = false,
|
|
|
67
68
|
children
|
|
68
69
|
}),
|
|
69
70
|
footer !== void 0 && /* @__PURE__ */ jsx("div", {
|
|
70
|
-
className: "flex shrink-0 items-center justify-end gap-2 border-t border-(--theme-border) px-4 py-3",
|
|
71
|
+
className: cx("flex shrink-0 items-center justify-end gap-2 border-t border-(--theme-border) px-4 py-3", phoneFooterClassName),
|
|
71
72
|
children: footer
|
|
72
73
|
})
|
|
73
74
|
] });
|
|
74
75
|
const content = /* @__PURE__ */ jsxs(Dialog.Portal, {
|
|
75
76
|
...portal,
|
|
76
77
|
children: [/* @__PURE__ */ jsx(Dialog.Backdrop, { className: cx("fixed inset-0 z-(--layer-modal)", backdropMotion, backdropClassName) }), /* @__PURE__ */ jsx(Dialog.Viewport, {
|
|
77
|
-
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)]"),
|
|
78
|
+
className: cx("fixed inset-0 z-(--layer-modal) flex justify-center px-4 pt-20 pb-8 max-sm:px-2 max-sm:pb-[max(8px,env(safe-area-inset-bottom))]", align === "center" ? "items-center" : "items-start sm:pt-[max(14vh,5rem)]"),
|
|
78
79
|
children: /* @__PURE__ */ jsx(Dialog.Popup, {
|
|
79
80
|
...popupProps,
|
|
80
81
|
ref: (element) => {
|
|
@@ -84,7 +85,7 @@ function Modal({ open, onClose, title, description, footer, size, alert = false,
|
|
|
84
85
|
else if (forward) forward.current = element;
|
|
85
86
|
},
|
|
86
87
|
initialFocus: initialFocus ?? (closeButton ? () => firstControlIn(panelElement.current) : void 0),
|
|
87
|
-
className: cx(panel && "rounded-xl border border-(--theme-border) bg-(--theme-
|
|
88
|
+
className: cx(panel && "rounded-xl border border-(--theme-border) bg-(--theme-panel-bg) text-(--theme-app) shadow-2xl", size && [sizeClasses[size], "max-w-full"], panel && (size || laidOut) && "max-sm:w-full", laidOut && "flex max-h-full flex-col", "outline-none", popupMotion, className),
|
|
88
89
|
children: !laidOut ? children : onSubmit ? /* @__PURE__ */ jsx("form", {
|
|
89
90
|
className: "flex min-h-0 flex-1 flex-col",
|
|
90
91
|
onSubmit: (event) => {
|
|
@@ -13,6 +13,10 @@ export interface PageHeaderProps {
|
|
|
13
13
|
* `h1`. A crumb with an empty label, such as a name still loading, shows a
|
|
14
14
|
* placeholder. */
|
|
15
15
|
breadcrumbs?: readonly Breadcrumb[] | undefined;
|
|
16
|
+
/** False keeps the last crumb out of the page's outline, for a page whose `h1` is in its
|
|
17
|
+
* body, such as a title field or a section's heading: the crumb still says it is the
|
|
18
|
+
* current page, and the page has one `h1`. */
|
|
19
|
+
heading?: boolean | undefined;
|
|
16
20
|
/** Beside the title: actions on the page's subject, such as edit, rename or
|
|
17
21
|
* favorite, or its `PageHeaderMenu`. */
|
|
18
22
|
titleActions?: ReactNode;
|
|
@@ -29,7 +33,9 @@ export interface PageHeaderProps {
|
|
|
29
33
|
* toolbar. Under a `PageHeaderProvider` it renders nothing in place: the
|
|
30
34
|
* shell's `PageHeaderBar` shows it, while the actions stay in the page's React
|
|
31
35
|
* tree, with its context and state. Without a provider it renders the bar
|
|
32
|
-
* itself, for a page with no shell
|
|
36
|
+
* itself, for a page with no shell, with no fill of its own, so it shows the
|
|
37
|
+
* surface it sits on. A host that pins it gives its pinned wrapper the page's
|
|
38
|
+
* surface, so content scrolling under it is covered.
|
|
33
39
|
*
|
|
34
40
|
* Any component may render one, such as a form adding its Save button: the bar
|
|
35
41
|
* shows the actions of all of them, outermost first, and the trail of the last
|
|
@@ -42,7 +42,9 @@ function createRegionNodes() {
|
|
|
42
42
|
* toolbar. Under a `PageHeaderProvider` it renders nothing in place: the
|
|
43
43
|
* shell's `PageHeaderBar` shows it, while the actions stay in the page's React
|
|
44
44
|
* tree, with its context and state. Without a provider it renders the bar
|
|
45
|
-
* itself, for a page with no shell
|
|
45
|
+
* itself, for a page with no shell, with no fill of its own, so it shows the
|
|
46
|
+
* surface it sits on. A host that pins it gives its pinned wrapper the page's
|
|
47
|
+
* surface, so content scrolling under it is covered.
|
|
46
48
|
*
|
|
47
49
|
* Any component may render one, such as a form adding its Save button: the bar
|
|
48
50
|
* shows the actions of all of them, outermost first, and the trail of the last
|
|
@@ -51,7 +53,9 @@ function createRegionNodes() {
|
|
|
51
53
|
function PageHeader(props) {
|
|
52
54
|
const store = use(StoreContext);
|
|
53
55
|
if (!store) return props.hidden ? null : /* @__PURE__ */ jsx(HeaderLayout, {
|
|
56
|
+
inPlace: true,
|
|
54
57
|
breadcrumbs: props.breadcrumbs ?? [],
|
|
58
|
+
heading: props.heading ?? true,
|
|
55
59
|
titleActions: props.titleActions,
|
|
56
60
|
actions: props.actions,
|
|
57
61
|
toolbar: props.toolbar
|
|
@@ -61,7 +65,7 @@ function PageHeader(props) {
|
|
|
61
65
|
...props
|
|
62
66
|
});
|
|
63
67
|
}
|
|
64
|
-
function DeclaredHeader({ store, breadcrumbs, titleActions, actions, toolbar, hidden = false }) {
|
|
68
|
+
function DeclaredHeader({ store, breadcrumbs, heading = true, titleActions, actions, toolbar, hidden = false }) {
|
|
65
69
|
const [order] = useState(() => ++renderOrder);
|
|
66
70
|
const [nodes] = useState(createRegionNodes);
|
|
67
71
|
const client = useSyncExternalStore(subscribeNever, () => true, () => false);
|
|
@@ -73,6 +77,7 @@ function DeclaredHeader({ store, breadcrumbs, titleActions, actions, toolbar, hi
|
|
|
73
77
|
useLayoutEffect(() => {
|
|
74
78
|
store.set(order, {
|
|
75
79
|
breadcrumbs,
|
|
80
|
+
heading,
|
|
76
81
|
hidden,
|
|
77
82
|
nodes,
|
|
78
83
|
fills
|
|
@@ -105,6 +110,7 @@ function PageHeaderBar({ compact = false, persistent = false, className }) {
|
|
|
105
110
|
if (header.hidden || empty && !persistent) return null;
|
|
106
111
|
return /* @__PURE__ */ jsx(HeaderLayout, {
|
|
107
112
|
breadcrumbs: header.breadcrumbs,
|
|
113
|
+
heading: header.heading,
|
|
108
114
|
titleActions: header.titleActions.length > 0 && /* @__PURE__ */ jsx(Mounts, { nodes: header.titleActions }),
|
|
109
115
|
actions: header.actions.length > 0 && /* @__PURE__ */ jsx(Mounts, { nodes: header.actions }),
|
|
110
116
|
toolbar: header.toolbar.length > 0 && /* @__PURE__ */ jsx(Mounts, { nodes: header.toolbar }),
|
|
@@ -137,7 +143,7 @@ function Mount({ node }) {
|
|
|
137
143
|
className: "contents"
|
|
138
144
|
});
|
|
139
145
|
}
|
|
140
|
-
function HeaderLayout({ breadcrumbs, titleActions, actions, toolbar, fit: sharedFit, compact = false, className }) {
|
|
146
|
+
function HeaderLayout({ breadcrumbs, heading, titleActions, actions, toolbar, fit: sharedFit, compact = false, inPlace = false, className }) {
|
|
141
147
|
const { breadcrumbPin, navToggle } = useSlots();
|
|
142
148
|
const { list: t } = useStrings();
|
|
143
149
|
const [ownFit] = useState(() => new HeaderActionsFit());
|
|
@@ -150,7 +156,7 @@ function HeaderLayout({ breadcrumbs, titleActions, actions, toolbar, fit: shared
|
|
|
150
156
|
const moved = fit.menu();
|
|
151
157
|
const hasTrail = breadcrumbs.length > 0;
|
|
152
158
|
return /* @__PURE__ */ jsxs("div", {
|
|
153
|
-
className: cx("@container w-full shrink-0 bg-(--theme-app-bg)", className),
|
|
159
|
+
className: cx("@container w-full shrink-0", !inPlace && "bg-(--theme-app-bg)", className),
|
|
154
160
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
155
161
|
ref: setRow,
|
|
156
162
|
className: cx("flex items-center justify-between gap-4 border-b border-(--theme-border) px-4", compact ? "h-8" : "h-12"),
|
|
@@ -161,7 +167,7 @@ function HeaderLayout({ breadcrumbs, titleActions, actions, toolbar, fit: shared
|
|
|
161
167
|
navToggle?.(),
|
|
162
168
|
hasTrail && /* @__PURE__ */ jsx(Breadcrumbs, {
|
|
163
169
|
crumbs: breadcrumbs,
|
|
164
|
-
heading
|
|
170
|
+
heading
|
|
165
171
|
}),
|
|
166
172
|
present(titleActions) && /* @__PURE__ */ jsx("div", {
|
|
167
173
|
className: "flex shrink-0 items-center gap-0.5",
|
|
@@ -102,6 +102,9 @@ export interface UIStrings {
|
|
|
102
102
|
opensInNewTab: string;
|
|
103
103
|
/** Names a code editor that has no label of its own. */
|
|
104
104
|
codeEditor: string;
|
|
105
|
+
/** Names a block of code that has no title of its own, by its language,
|
|
106
|
+
* such as "yaml code"; `language` is empty when it has none. */
|
|
107
|
+
codeOf: (language: string) => string;
|
|
105
108
|
/** Shown when someone types in a read-only code editor. */
|
|
106
109
|
readOnlyText: string;
|
|
107
110
|
/** Names the handle that resizes a panel. */
|
|
@@ -148,6 +148,7 @@ var DEFAULTS = {
|
|
|
148
148
|
no: "No",
|
|
149
149
|
opensInNewTab: "(opens in a new tab)",
|
|
150
150
|
codeEditor: "Code editor",
|
|
151
|
+
codeOf: (language) => language ? `${language} code` : "Code",
|
|
151
152
|
readOnlyText: "This text is read-only.",
|
|
152
153
|
resizePanel: "Resize panel",
|
|
153
154
|
panelWidth: (px) => `${px} pixels wide`,
|
|
@@ -59,7 +59,7 @@ function Select$1({ items, placeholder, className, "aria-label": ariaLabel, "ari
|
|
|
59
59
|
content: tip,
|
|
60
60
|
children: /* @__PURE__ */ jsxs(Select.Trigger, {
|
|
61
61
|
...triggerProps,
|
|
62
|
-
className: cx(fieldBox({ size }), triggerClassName, content && "w-fit max-w-full", explained && explainedDisabledClassName, className),
|
|
62
|
+
className: cx(fieldBox({ size }), triggerClassName, content && "w-fit max-w-full shrink-0", explained && explainedDisabledClassName, className),
|
|
63
63
|
children: [/* @__PURE__ */ jsx(Select.Value, {
|
|
64
64
|
placeholder,
|
|
65
65
|
className: cx("truncate data-placeholder:text-(--theme-placeholder)", content && !rootProps.multiple && "grid"),
|
|
@@ -17,7 +17,9 @@ export interface SettingsCardProps {
|
|
|
17
17
|
children: ReactNode;
|
|
18
18
|
}
|
|
19
19
|
/** A page's settings, grouped: a titled panel of SettingRows, each ruled off from
|
|
20
|
-
* the next. Cards stack with room between them.
|
|
20
|
+
* the next. Cards stack with room between them. An error about one setting, such as its
|
|
21
|
+
* Save failing, is its row's `error`; one about the whole card goes outside it, such as
|
|
22
|
+
* an ErrorText under the card, since every child of the card is ruled off as a row. */
|
|
21
23
|
export declare function SettingsCard({ title, description, action, actionAlign, headingLevel, overflowVisible, className, children, }: SettingsCardProps): import("react").JSX.Element;
|
|
22
24
|
/** A heading between groups of rows inside a SettingsCard, a level below the card's title. */
|
|
23
25
|
export declare function SettingsHeading({ children }: {
|
|
@@ -25,7 +27,7 @@ export declare function SettingsHeading({ children }: {
|
|
|
25
27
|
}): import("react").JSX.Element;
|
|
26
28
|
/** One setting in a SettingsCard: its name and help on the left, its control on
|
|
27
29
|
* the right, or under the name across the row with `fill`. */
|
|
28
|
-
export declare function SettingRow({ label, htmlFor, helpText, fill, onClick, children, }: {
|
|
30
|
+
export declare function SettingRow({ label, htmlFor, helpText, fill, onClick, error, children, }: {
|
|
29
31
|
label: string;
|
|
30
32
|
/** The control's id, so the name labels it. */
|
|
31
33
|
htmlFor?: string | undefined;
|
|
@@ -35,5 +37,8 @@ export declare function SettingRow({ label, htmlFor, helpText, fill, onClick, ch
|
|
|
35
37
|
/** Makes the whole row press its control, such as a switch. The control still
|
|
36
38
|
* handles its own press; stop it propagating there so it doesn't toggle twice. */
|
|
37
39
|
onClick?: (() => void) | undefined;
|
|
40
|
+
/** An error about this setting, such as its Save failing: under the control, at its start,
|
|
41
|
+
* announced, and describing the control that `htmlFor` names. */
|
|
42
|
+
error?: ReactNode;
|
|
38
43
|
children: ReactNode;
|
|
39
44
|
}): import("react").JSX.Element;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ErrorText } from "./Field.js";
|
|
3
|
+
import { createContext, useContext, useId, useLayoutEffect } from "react";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
//#region src/components/SettingsCard.tsx
|
|
5
6
|
var HeadingLevel = createContext(3);
|
|
6
7
|
/** A page's settings, grouped: a titled panel of SettingRows, each ruled off from
|
|
7
|
-
* the next. Cards stack with room between them.
|
|
8
|
+
* the next. Cards stack with room between them. An error about one setting, such as its
|
|
9
|
+
* Save failing, is its row's `error`; one about the whole card goes outside it, such as
|
|
10
|
+
* an ErrorText under the card, since every child of the card is ruled off as a row. */
|
|
8
11
|
function SettingsCard({ title, description, action, actionAlign = "beside", headingLevel = 2, overflowVisible = false, className, children }) {
|
|
9
12
|
const Heading = `h${headingLevel}`;
|
|
10
13
|
return /* @__PURE__ */ jsx(HeadingLevel, {
|
|
@@ -40,7 +43,34 @@ function SettingsHeading({ children }) {
|
|
|
40
43
|
}
|
|
41
44
|
/** One setting in a SettingsCard: its name and help on the left, its control on
|
|
42
45
|
* the right, or under the name across the row with `fill`. */
|
|
43
|
-
function SettingRow({ label, htmlFor, helpText, fill = false, onClick, children }) {
|
|
46
|
+
function SettingRow({ label, htmlFor, helpText, fill = false, onClick, error, children }) {
|
|
47
|
+
const errorId = useId();
|
|
48
|
+
const hasError = error !== void 0 && error !== null && error !== false && error !== "";
|
|
49
|
+
useLayoutEffect(() => {
|
|
50
|
+
const control = hasError && htmlFor ? document.getElementById(htmlFor) : null;
|
|
51
|
+
if (!control) return;
|
|
52
|
+
const ids = (control.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(Boolean);
|
|
53
|
+
if (!ids.includes(errorId)) control.setAttribute("aria-describedby", [...ids, errorId].join(" "));
|
|
54
|
+
return () => {
|
|
55
|
+
const rest = (control.getAttribute("aria-describedby") ?? "").split(/\s+/).filter((id) => id && id !== errorId);
|
|
56
|
+
if (rest.length > 0) control.setAttribute("aria-describedby", rest.join(" "));
|
|
57
|
+
else control.removeAttribute("aria-describedby");
|
|
58
|
+
};
|
|
59
|
+
}, [
|
|
60
|
+
hasError,
|
|
61
|
+
htmlFor,
|
|
62
|
+
errorId
|
|
63
|
+
]);
|
|
64
|
+
const control = /* @__PURE__ */ jsx("div", {
|
|
65
|
+
className: cx("min-w-0", !fill && [
|
|
66
|
+
"flex items-center justify-end",
|
|
67
|
+
"max-md:has-[input:not([type=checkbox],[type=radio],[type=hidden],[type=range]),textarea,[role=combobox]]:justify-start",
|
|
68
|
+
"max-md:has-[input:not([type=checkbox],[type=radio],[type=hidden],[type=range]),textarea,[role=combobox]]:*:grow",
|
|
69
|
+
"max-md:[&_:is(input:not([type=checkbox],[type=radio],[type=hidden],[type=range]),textarea,[role=combobox])]:min-w-0",
|
|
70
|
+
"max-md:[&_:is(input:not([type=checkbox],[type=radio],[type=hidden],[type=range]),textarea,[role=combobox])]:flex-1"
|
|
71
|
+
]),
|
|
72
|
+
children
|
|
73
|
+
});
|
|
44
74
|
return /* @__PURE__ */ jsxs("div", {
|
|
45
75
|
role: "none",
|
|
46
76
|
onClick,
|
|
@@ -55,10 +85,15 @@ function SettingRow({ label, htmlFor, helpText, fill = false, onClick, children
|
|
|
55
85
|
className: "text-xs leading-snug theme-muted-text",
|
|
56
86
|
children: helpText
|
|
57
87
|
})]
|
|
58
|
-
}), /* @__PURE__ */
|
|
59
|
-
className:
|
|
60
|
-
children
|
|
61
|
-
|
|
88
|
+
}), hasError ? /* @__PURE__ */ jsxs("div", {
|
|
89
|
+
className: "flex min-w-0 flex-col gap-y-2",
|
|
90
|
+
children: [control, /* @__PURE__ */ jsx(ErrorText, {
|
|
91
|
+
id: errorId,
|
|
92
|
+
size: "md",
|
|
93
|
+
className: "md:w-0 md:min-w-full",
|
|
94
|
+
children: error
|
|
95
|
+
})]
|
|
96
|
+
}) : control]
|
|
62
97
|
});
|
|
63
98
|
}
|
|
64
99
|
//#endregion
|
|
@@ -25,6 +25,8 @@ export interface ToggleGroupProps<T extends string> {
|
|
|
25
25
|
all?: boolean | {
|
|
26
26
|
count?: number | undefined;
|
|
27
27
|
} | undefined;
|
|
28
|
+
/** The All option's words, for one that names what it covers, such as "All types". */
|
|
29
|
+
allLabel?: ReactNode;
|
|
28
30
|
size?: 'sm' | 'md' | undefined;
|
|
29
31
|
disabled?: boolean | undefined;
|
|
30
32
|
/** Names an element that says more about the group, such as a live summary
|
|
@@ -42,4 +44,4 @@ export interface ToggleGroupProps<T extends string> {
|
|
|
42
44
|
* value, such as a time range, what to group by, or which view of the same
|
|
43
45
|
* data to show. A filter that may be off, or takes several values, is a
|
|
44
46
|
* `ToggleGroup`. */
|
|
45
|
-
export declare function ToggleGroup<T extends string>({ label, options, value, onValueChange, multiple, all, size, disabled, 'aria-describedby': describedBy, testId, }: ToggleGroupProps<T>): import("react").JSX.Element;
|
|
47
|
+
export declare function ToggleGroup<T extends string>({ label, options, value, onValueChange, multiple, all, allLabel, size, disabled, 'aria-describedby': describedBy, testId, }: ToggleGroupProps<T>): import("react").JSX.Element;
|
|
@@ -18,7 +18,7 @@ var ALL = "\0all";
|
|
|
18
18
|
* value, such as a time range, what to group by, or which view of the same
|
|
19
19
|
* data to show. A filter that may be off, or takes several values, is a
|
|
20
20
|
* `ToggleGroup`. */
|
|
21
|
-
function ToggleGroup$1({ label, options, value, onValueChange, multiple = true, all = false, size = "sm", disabled = false, "aria-describedby": describedBy, testId }) {
|
|
21
|
+
function ToggleGroup$1({ label, options, value, onValueChange, multiple = true, all = false, allLabel, size = "sm", disabled = false, "aria-describedby": describedBy, testId }) {
|
|
22
22
|
const strings = useStrings();
|
|
23
23
|
const allCount = typeof all === "object" ? all.count : void 0;
|
|
24
24
|
const pressed = all && value.length === 0 ? [ALL] : value;
|
|
@@ -40,7 +40,7 @@ function ToggleGroup$1({ label, options, value, onValueChange, multiple = true,
|
|
|
40
40
|
children: [
|
|
41
41
|
all && /* @__PURE__ */ jsx(Chip, {
|
|
42
42
|
value: ALL,
|
|
43
|
-
label: strings.common.all,
|
|
43
|
+
label: allLabel ?? strings.common.all,
|
|
44
44
|
count: allCount,
|
|
45
45
|
size
|
|
46
46
|
}),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { positionerClassName, tipClassName } from "./floating.js";
|
|
2
2
|
import { usePopupContainer } from "./popupContainer.js";
|
|
3
3
|
import { Kbd } from "./Kbd.js";
|
|
4
|
-
import { cloneElement, createContext, use, useId, useState } from "react";
|
|
4
|
+
import { cloneElement, createContext, use, useEffect, useId, useState } from "react";
|
|
5
5
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { Tooltip } from "@base-ui/react/tooltip";
|
|
7
7
|
//#region src/components/Tooltip.tsx
|
|
@@ -52,12 +52,44 @@ function skipUnclipped(open, details) {
|
|
|
52
52
|
const anchor = details.trigger;
|
|
53
53
|
if (open && anchor instanceof HTMLElement && anchor.hasAttribute("data-overflow-only") && anchor.scrollWidth <= anchor.clientWidth) details.cancel();
|
|
54
54
|
}
|
|
55
|
+
/** True from when the anchor's own popup, such as its menu, closes until the pointer leaves the
|
|
56
|
+
* anchor or focus does. Closing the popup hands focus back to the anchor, and a tip opening
|
|
57
|
+
* then would take the next Escape, meant for the dialog around it, and repeat a label the
|
|
58
|
+
* reader has just seen. */
|
|
59
|
+
function useRestAfterPopup(anchor) {
|
|
60
|
+
const [resting, setResting] = useState(false);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!anchor || typeof MutationObserver === "undefined") return;
|
|
63
|
+
let expanded = anchor.getAttribute("aria-expanded") === "true";
|
|
64
|
+
const observer = new MutationObserver(() => {
|
|
65
|
+
const now = anchor.getAttribute("aria-expanded") === "true";
|
|
66
|
+
if (expanded && !now) setResting(true);
|
|
67
|
+
else if (now) setResting(false);
|
|
68
|
+
expanded = now;
|
|
69
|
+
});
|
|
70
|
+
observer.observe(anchor, {
|
|
71
|
+
attributes: true,
|
|
72
|
+
attributeFilter: ["aria-expanded"]
|
|
73
|
+
});
|
|
74
|
+
const wake = () => setResting(false);
|
|
75
|
+
anchor.addEventListener("blur", wake);
|
|
76
|
+
anchor.addEventListener("pointerleave", wake);
|
|
77
|
+
return () => {
|
|
78
|
+
observer.disconnect();
|
|
79
|
+
anchor.removeEventListener("blur", wake);
|
|
80
|
+
anchor.removeEventListener("pointerleave", wake);
|
|
81
|
+
};
|
|
82
|
+
}, [anchor]);
|
|
83
|
+
return resting;
|
|
84
|
+
}
|
|
55
85
|
/** A visual hint for sighted pointer and keyboard users. The anchor still needs
|
|
56
86
|
* its own accessible name; `describesAnchor` also gives assistive technology the
|
|
57
87
|
* hint. Use InfoTip for content everyone must reach, such as on a touch screen. */
|
|
58
88
|
function Tooltip$1({ content, children, side = "top", shortcut, disabled = false, overflowOnly = false, describesAnchor = false, ...anchorProps }) {
|
|
59
89
|
const handle = use(TooltipHandleContext);
|
|
60
90
|
const descriptionId = useId();
|
|
91
|
+
const [anchorElement, setAnchorElement] = useState(null);
|
|
92
|
+
const resting = useRestAfterPopup(anchorElement);
|
|
61
93
|
const payload = {
|
|
62
94
|
content,
|
|
63
95
|
shortcut,
|
|
@@ -75,7 +107,8 @@ function Tooltip$1({ content, children, side = "top", shortcut, disabled = false
|
|
|
75
107
|
...!handle && anchorProps.delay === void 0 && { delay: OPEN_DELAY },
|
|
76
108
|
handle: handle ?? void 0,
|
|
77
109
|
payload,
|
|
78
|
-
|
|
110
|
+
ref: setAnchorElement,
|
|
111
|
+
disabled: disabled || !content || resting,
|
|
79
112
|
"data-overflow-only": overflowOnly || void 0,
|
|
80
113
|
render: described ? cloneElement(anchor, { "aria-describedby": describedBy }) : children
|
|
81
114
|
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface CodeToken {
|
|
2
|
+
content: string;
|
|
3
|
+
color?: string;
|
|
4
|
+
fontStyle?: 'italic';
|
|
5
|
+
}
|
|
6
|
+
/** Tokens for each line of `source`, at once when they are cached, otherwise
|
|
7
|
+
* null now and `onReady` once the language has loaded. A language the
|
|
8
|
+
* highlighter doesn't know reads as plain text. */
|
|
9
|
+
export declare function highlightCode(source: string, language: string, incomplete: boolean, onReady: (lines: CodeToken[][]) => void): CodeToken[][] | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CODE_THEMES } from "./codeTheme.js";
|
|
2
|
+
import { code } from "@streamdown/code";
|
|
3
|
+
//#region src/components/codeHighlight.ts
|
|
4
|
+
var ITALIC = 1;
|
|
5
|
+
function toLines(tokens) {
|
|
6
|
+
return tokens.map((line) => line.map((token) => {
|
|
7
|
+
const color = token.htmlStyle?.["color"] ?? token.color;
|
|
8
|
+
const italic = token.htmlStyle?.["font-style"] === "italic" || ((token.fontStyle ?? 0) & ITALIC) === ITALIC;
|
|
9
|
+
return {
|
|
10
|
+
content: token.content,
|
|
11
|
+
...color ? { color } : {},
|
|
12
|
+
...italic ? { fontStyle: "italic" } : {}
|
|
13
|
+
};
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
/** Tokens for each line of `source`, at once when they are cached, otherwise
|
|
17
|
+
* null now and `onReady` once the language has loaded. A language the
|
|
18
|
+
* highlighter doesn't know reads as plain text. */
|
|
19
|
+
function highlightCode(source, language, incomplete, onReady) {
|
|
20
|
+
const result = code.highlight({
|
|
21
|
+
code: source,
|
|
22
|
+
language: language || "text",
|
|
23
|
+
themes: CODE_THEMES,
|
|
24
|
+
isIncomplete: incomplete
|
|
25
|
+
}, (later) => onReady(toLines(later.tokens)));
|
|
26
|
+
return result ? toLines(result.tokens) : null;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { highlightCode };
|
|
@@ -7,12 +7,16 @@ export type PageHeaderRegion = 'titleActions' | 'actions' | 'toolbar';
|
|
|
7
7
|
export type RegionNodes = Readonly<Record<PageHeaderRegion, HTMLElement>>;
|
|
8
8
|
export interface Declaration {
|
|
9
9
|
breadcrumbs: readonly Breadcrumb[] | undefined;
|
|
10
|
+
/** Whether the trail's last crumb is the page's `h1`. */
|
|
11
|
+
heading: boolean;
|
|
10
12
|
hidden: boolean;
|
|
11
13
|
nodes: RegionNodes | null;
|
|
12
14
|
fills: Readonly<Record<PageHeaderRegion, boolean>>;
|
|
13
15
|
}
|
|
14
16
|
export interface PageHeaderSnapshot {
|
|
15
17
|
breadcrumbs: readonly Breadcrumb[];
|
|
18
|
+
/** Whether the trail's last crumb is the page's `h1`, as the page whose trail it is says. */
|
|
19
|
+
heading: boolean;
|
|
16
20
|
hidden: boolean;
|
|
17
21
|
titleActions: readonly HTMLElement[];
|
|
18
22
|
actions: readonly HTMLElement[];
|
|
@@ -4,6 +4,7 @@ var NO_NODES = [];
|
|
|
4
4
|
var NO_CRUMBS = [];
|
|
5
5
|
var EMPTY = {
|
|
6
6
|
breadcrumbs: NO_CRUMBS,
|
|
7
|
+
heading: true,
|
|
7
8
|
hidden: false,
|
|
8
9
|
titleActions: NO_NODES,
|
|
9
10
|
actions: NO_NODES,
|
|
@@ -15,7 +16,7 @@ function sameCrumbs(a, b) {
|
|
|
15
16
|
return a.every((crumb, i) => crumb.label === b[i]?.label && crumb.href === b[i]?.href);
|
|
16
17
|
}
|
|
17
18
|
function sameDeclaration(a, b) {
|
|
18
|
-
return sameCrumbs(a.breadcrumbs, b.breadcrumbs) && a.hidden === b.hidden && a.nodes === b.nodes && a.fills.titleActions === b.fills.titleActions && a.fills.actions === b.fills.actions && a.fills.toolbar === b.fills.toolbar;
|
|
19
|
+
return sameCrumbs(a.breadcrumbs, b.breadcrumbs) && a.heading === b.heading && a.hidden === b.hidden && a.nodes === b.nodes && a.fills.titleActions === b.fills.titleActions && a.fills.actions === b.fills.actions && a.fills.toolbar === b.fills.toolbar;
|
|
19
20
|
}
|
|
20
21
|
function sameNodes(a, b) {
|
|
21
22
|
return a.length === b.length && a.every((node, i) => node === b[i]);
|
|
@@ -54,10 +55,12 @@ var PageHeaderStore = class {
|
|
|
54
55
|
const next = ordered.flatMap((d) => d.nodes && d.fills[region] ? [d.nodes[region]] : []);
|
|
55
56
|
return sameNodes(next, prev) ? prev : next;
|
|
56
57
|
};
|
|
57
|
-
const
|
|
58
|
+
const owner = ordered.findLast((d) => d.breadcrumbs !== void 0);
|
|
59
|
+
const trail = owner?.breadcrumbs ?? NO_CRUMBS;
|
|
58
60
|
const prev = this.snapshot;
|
|
59
61
|
this.snapshot = {
|
|
60
62
|
breadcrumbs: sameCrumbs(trail, prev.breadcrumbs) ? prev.breadcrumbs : trail,
|
|
63
|
+
heading: owner?.heading ?? true,
|
|
61
64
|
hidden: ordered.some((d) => d.hidden),
|
|
62
65
|
titleActions: regionNodes("titleActions", prev.titleActions),
|
|
63
66
|
actions: regionNodes("actions", prev.actions),
|
|
@@ -13,9 +13,9 @@ import { useCopy } from "../components/useCopy.js";
|
|
|
13
13
|
import { Breadcrumbs } from "../components/Breadcrumbs.js";
|
|
14
14
|
import { Button } from "../components/Button.js";
|
|
15
15
|
import { Callout } from "../components/Callout.js";
|
|
16
|
+
import { CopyButton, CopyCodeBlock } from "../components/CopyButton.js";
|
|
16
17
|
import { InfoTip } from "../components/InfoTip.js";
|
|
17
18
|
import { ConfirmModal } from "../components/ConfirmModal.js";
|
|
18
|
-
import { CopyButton, CopyCodeBlock } from "../components/CopyButton.js";
|
|
19
19
|
import { CreateModal } from "../components/CreateModal.js";
|
|
20
20
|
import { useSplitter } from "../components/splitter.js";
|
|
21
21
|
import { formatBytes, formatDateTime } from "../format.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -5,8 +5,9 @@ export { type Breadcrumb, Breadcrumbs, type BreadcrumbsProps } from './component
|
|
|
5
5
|
export { Button, type ButtonProps, MethodButton } from './components/Button.js';
|
|
6
6
|
export { type ButtonSize, type ButtonStyle, type ButtonVariant, buttonClasses, } from './components/buttonClasses.js';
|
|
7
7
|
export { Callout, type CalloutProps, type CalloutTone } from './components/Callout.js';
|
|
8
|
-
export { Card, CardHeader, type CardHeaderProps, type CardPadding, type CardProps, type CardSize, } from './components/Card.js';
|
|
8
|
+
export { Card, CardHeader, type CardHeaderProps, type CardPadding, type CardProps, type CardSize, type CardTone, } from './components/Card.js';
|
|
9
9
|
export { Checkbox, CheckboxGroup, type CheckboxProps } from './components/Checkbox.js';
|
|
10
|
+
export { CodeBlock, type CodeBlockProps } from './components/CodeBlock.js';
|
|
10
11
|
export { ColorInput, type ColorInputProps } from './components/ColorInput.js';
|
|
11
12
|
export { Autocomplete, type AutocompleteProps, Combobox, type ComboboxGroup, type ComboboxItem, type ComboboxItems, type ComboboxProps, } from './components/Combobox.js';
|
|
12
13
|
export { ConfirmModal, type ConfirmModalProps, type ConfirmRequest, useConfirm, } from './components/ConfirmModal.js';
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,8 @@ import { Callout } from "./components/Callout.js";
|
|
|
23
23
|
import { Card, CardHeader } from "./components/Card.js";
|
|
24
24
|
import { fieldBox } from "./components/fieldBox.js";
|
|
25
25
|
import { Checkbox, CheckboxGroup } from "./components/Checkbox.js";
|
|
26
|
+
import { CopyButton, CopyCodeBlock } from "./components/CopyButton.js";
|
|
27
|
+
import { CodeBlock } from "./components/CodeBlock.js";
|
|
26
28
|
import { JND, composite, deltaE, parseColor, toHex, toP3 } from "./theme/color.js";
|
|
27
29
|
import { apcaContrast, contrastRatio, isDarkColor } from "./theme/contrast.js";
|
|
28
30
|
import { CODE_STYLES, DEFAULT_CONTRAST, DEFAULT_PRESET, DEFAULT_THEME, THEME_PRESETS, modeOf, parseTheme, resolveMode, schemeFor, serializeTheme } from "./theme/model.js";
|
|
@@ -40,7 +42,6 @@ import { Tag, TagList, normalizeTags, tagClasses } from "./components/Tag.js";
|
|
|
40
42
|
import { Autocomplete, Combobox } from "./components/Combobox.js";
|
|
41
43
|
import { Modal, ModalDescription, ModalTitle, modalPanelClassName } from "./components/Modal.js";
|
|
42
44
|
import { ConfirmModal, useConfirm } from "./components/ConfirmModal.js";
|
|
43
|
-
import { CopyButton, CopyCodeBlock } from "./components/CopyButton.js";
|
|
44
45
|
import { Disclosure, DisclosurePanel, collapsePanelClassName } from "./components/Disclosure.js";
|
|
45
46
|
import { Switch } from "./components/Switch.js";
|
|
46
47
|
import { CreateModal, CreateModalSelect, CreateModalSwitch } from "./components/CreateModal.js";
|
|
@@ -83,4 +84,4 @@ import { ToggleGroup } from "./components/ToggleGroup.js";
|
|
|
83
84
|
import { useClosingValue } from "./components/useClosingValue.js";
|
|
84
85
|
import { useCssIsDark } from "./components/useCssIsDark.js";
|
|
85
86
|
import { useApplyTheme, useColorMode } from "./components/useTheme.js";
|
|
86
|
-
export { ActionMenu, AppLink, Autocomplete, Avatar, AvatarGroup, Breadcrumbs, Button, CODE_STYLES, Calendar, Callout, Card, CardHeader, ChartLegendList, ChartTooltipContent, Checkbox, CheckboxGroup, ColorInput, Combobox, ConfirmModal, ContextMenuProvider, CopyButton, CopyCodeBlock, CountBadge, CreateModal, CreateModalSelect, CreateModalSwitch, DEFAULT_CONTRAST, DEFAULT_PRESET, DEFAULT_THEME, DatePicker, DescriptionList, DescriptionListGroup, DescriptionListItem, Disclosure, DisclosurePanel, Drawer, Elapsed, EmptyState, ErrorPage, ErrorText, ExternalLink, Field, FieldOption, FieldValue, FileButton, HoverCard, IconButton, InfoTip, Input, JND, Kbd, LetterBadge, ListboxGroup, ListboxOption, ListboxPanel, Loader, LoadingRegion, Markdown, MenuCheckboxItem, MenuEntries, MenuFilter, MenuGroupLabel, MenuItem, MenuLinkItem, MenuPopup, MenuRadioItem, MenuSeparator, MenuSubmenuTrigger, Meter, MethodButton, Modal, ModalDescription, ModalTitle, NumberInput, OneTimeCodeInput, Overview, PAGE_SIZES, PageHeader, PageHeaderAction, PageHeaderAddButton, PageHeaderBar, PageHeaderMenu, PageHeaderProvider, Pagination, Popover, PopoverClose, ProgressBar, Radio, RadioGroup, RefreshButton, RelativeTime, RequiredMark, RevealToggle, RunStatusIcon, SIDEBAR_TOKENS, SearchInput, Section, SegmentedControl, Select, SelectionCopy, SeriesSwatch, SettingModeOption, SettingRow, SettingsCard, SettingsHeading, Skeleton, Slider, StatusBadge, StatusDot, StickyBar, Switch, THEME_PRESETS, THEME_TOKENS, TabPanel, Table, Tabs, Tag, TagList, Textarea, ThemeEditor, ThemePreview, TitleInput, ToggleGroup, Tooltip, TooltipProvider, UIProvider, apcaContrast, applyTheme, ariaKeyShortcuts, avatarInitials, buttonClasses, categoryClasses, categoryOf, collapsePanelClassName, composite, contrastRatio, cx, deltaE, deriveTheme, errorPageKind, fieldBox, fitSeed, formatBytes, formatCount, formatDateTime, formatDuration, formatInfoTipContent, formatShortcut, formatTimeLeft, generateTheme, isApplePlatform, isDarkColor, isMenuDivider, isMenuGroup, isMenuSubmenu, isPlainClick, isTextEntryContext, levelTone, menuDivider, modalPanelClassName, modeOf, normalizeTags, parseColor, parseTheme, prefersWideGamut, resolveMode, runStatusTone, schemeFor, serializeTheme, seriesColor, seriesColors, seriesOpacity, seriesSlotColor, shortcutText, statusTextClass, tagClasses, themeStylesheet, toHex, toP3, toast, useApplyTheme, useAvatarColoring, useClock, useCloseMenu, useClosePopover, useClosingValue, useColorMode, useConfirm, useContextMenu, useCopy, useCssIsDark, useElapsed, useFieldControl, useFieldLabelledControl, useListboxNavigation, useLocale, useMenuOpenedBy, useNavigation, useOpenMenu, useOptionalWorkflowEngine, usePersistedState, useProblemText, useProvisionCorsRules, useRelativeTime, useRunFile, useSeriesHighlight, useShortcutFormat, useShortcutKey, useSlots, useStrings, useWorkflowActions, useWorkflowEditing, useWorkflowEngine, useWorkflowEngineLoader };
|
|
87
|
+
export { ActionMenu, AppLink, Autocomplete, Avatar, AvatarGroup, Breadcrumbs, Button, CODE_STYLES, Calendar, Callout, Card, CardHeader, ChartLegendList, ChartTooltipContent, Checkbox, CheckboxGroup, CodeBlock, ColorInput, Combobox, ConfirmModal, ContextMenuProvider, CopyButton, CopyCodeBlock, CountBadge, CreateModal, CreateModalSelect, CreateModalSwitch, DEFAULT_CONTRAST, DEFAULT_PRESET, DEFAULT_THEME, DatePicker, DescriptionList, DescriptionListGroup, DescriptionListItem, Disclosure, DisclosurePanel, Drawer, Elapsed, EmptyState, ErrorPage, ErrorText, ExternalLink, Field, FieldOption, FieldValue, FileButton, HoverCard, IconButton, InfoTip, Input, JND, Kbd, LetterBadge, ListboxGroup, ListboxOption, ListboxPanel, Loader, LoadingRegion, Markdown, MenuCheckboxItem, MenuEntries, MenuFilter, MenuGroupLabel, MenuItem, MenuLinkItem, MenuPopup, MenuRadioItem, MenuSeparator, MenuSubmenuTrigger, Meter, MethodButton, Modal, ModalDescription, ModalTitle, NumberInput, OneTimeCodeInput, Overview, PAGE_SIZES, PageHeader, PageHeaderAction, PageHeaderAddButton, PageHeaderBar, PageHeaderMenu, PageHeaderProvider, Pagination, Popover, PopoverClose, ProgressBar, Radio, RadioGroup, RefreshButton, RelativeTime, RequiredMark, RevealToggle, RunStatusIcon, SIDEBAR_TOKENS, SearchInput, Section, SegmentedControl, Select, SelectionCopy, SeriesSwatch, SettingModeOption, SettingRow, SettingsCard, SettingsHeading, Skeleton, Slider, StatusBadge, StatusDot, StickyBar, Switch, THEME_PRESETS, THEME_TOKENS, TabPanel, Table, Tabs, Tag, TagList, Textarea, ThemeEditor, ThemePreview, TitleInput, ToggleGroup, Tooltip, TooltipProvider, UIProvider, apcaContrast, applyTheme, ariaKeyShortcuts, avatarInitials, buttonClasses, categoryClasses, categoryOf, collapsePanelClassName, composite, contrastRatio, cx, deltaE, deriveTheme, errorPageKind, fieldBox, fitSeed, formatBytes, formatCount, formatDateTime, formatDuration, formatInfoTipContent, formatShortcut, formatTimeLeft, generateTheme, isApplePlatform, isDarkColor, isMenuDivider, isMenuGroup, isMenuSubmenu, isPlainClick, isTextEntryContext, levelTone, menuDivider, modalPanelClassName, modeOf, normalizeTags, parseColor, parseTheme, prefersWideGamut, resolveMode, runStatusTone, schemeFor, serializeTheme, seriesColor, seriesColors, seriesOpacity, seriesSlotColor, shortcutText, statusTextClass, tagClasses, themeStylesheet, toHex, toP3, toast, useApplyTheme, useAvatarColoring, useClock, useCloseMenu, useClosePopover, useClosingValue, useColorMode, useConfirm, useContextMenu, useCopy, useCssIsDark, useElapsed, useFieldControl, useFieldLabelledControl, useListboxNavigation, useLocale, useMenuOpenedBy, useNavigation, useOpenMenu, useOptionalWorkflowEngine, usePersistedState, useProblemText, useProvisionCorsRules, useRelativeTime, useRunFile, useSeriesHighlight, useShortcutFormat, useShortcutKey, useSlots, useStrings, useWorkflowActions, useWorkflowEditing, useWorkflowEngine, useWorkflowEngineLoader };
|
|
@@ -9,7 +9,7 @@ import { Input } from "../components/Input.js";
|
|
|
9
9
|
import { Combobox } from "../components/Combobox.js";
|
|
10
10
|
import { Calendar } from "../components/DatePicker.js";
|
|
11
11
|
import { Drawer } from "../components/Drawer.js";
|
|
12
|
-
import { DATE_PRESETS, fieldOperators, isDatePreset, isMultiple, parseDay, toDay } from "./filterModel.js";
|
|
12
|
+
import { DATE_PRESETS, fieldOperators, isDatePreset, isMultiple, operatorCount, parseDay, toDay } from "./filterModel.js";
|
|
13
13
|
import { Menu } from "@base-ui/react/menu";
|
|
14
14
|
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
15
15
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -81,7 +81,7 @@ function FilterChip({ filters, filter, field, stretch = false, answers, onRemove
|
|
|
81
81
|
const shown = all.slice(0, 2);
|
|
82
82
|
const hidden = all.length - shown.length;
|
|
83
83
|
const valueText = unset ? showsAny ? t.any : t.pending : t.list(shown);
|
|
84
|
-
const operatorText = t.operator(filter.op, filter
|
|
84
|
+
const operatorText = t.operator(filter.op, operatorCount(field, filter));
|
|
85
85
|
const noValuePart = filter.op === "isSet" || filter.op === "isNotSet" || field.kind === "boolean" && filter.values[0] !== "false";
|
|
86
86
|
const noOperatorPart = showsAny || field.kind === "boolean" && filter.values[0] !== "false";
|
|
87
87
|
const readAll = unset ? valueText : t.list(all);
|
|
@@ -121,6 +121,7 @@ function FilterChip({ filters, filter, field, stretch = false, answers, onRemove
|
|
|
121
121
|
}),
|
|
122
122
|
!noOperatorPart && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Divider, {}), operators.length > 1 ? /* @__PURE__ */ jsx(OperatorMenu, {
|
|
123
123
|
filters,
|
|
124
|
+
field,
|
|
124
125
|
filter,
|
|
125
126
|
operators,
|
|
126
127
|
open: open === "operator",
|
|
@@ -218,9 +219,9 @@ function FieldMenu({ filters, field, open, onOpenChange }) {
|
|
|
218
219
|
})]
|
|
219
220
|
});
|
|
220
221
|
}
|
|
221
|
-
function OperatorMenu({ filters, filter, operators, open, onOpenChange }) {
|
|
222
|
+
function OperatorMenu({ filters, field, filter, operators, open, onOpenChange }) {
|
|
222
223
|
const t = useStrings().filters;
|
|
223
|
-
const count = filter
|
|
224
|
+
const count = operatorCount(field, filter);
|
|
224
225
|
const text = t.operator(filter.op, count);
|
|
225
226
|
return /* @__PURE__ */ jsxs(Menu.Root, {
|
|
226
227
|
open,
|
|
@@ -246,7 +247,7 @@ function OperatorMenu({ filters, filter, operators, open, onOpenChange }) {
|
|
|
246
247
|
},
|
|
247
248
|
children: operators.map((op) => /* @__PURE__ */ jsx(MenuRadioItem, {
|
|
248
249
|
value: op,
|
|
249
|
-
children: t.operator(op,
|
|
250
|
+
children: t.operator(op, count)
|
|
250
251
|
}, op))
|
|
251
252
|
}) })]
|
|
252
253
|
});
|
|
@@ -79,6 +79,10 @@ export type DatePreset = (typeof DATE_PRESETS)[number];
|
|
|
79
79
|
type AnyField = FilterField<any>;
|
|
80
80
|
export declare function fieldOperators(field: AnyField): readonly FilterOperator[];
|
|
81
81
|
export declare function isMultiple(field: AnyField): boolean;
|
|
82
|
+
/** How many values a condition's words are for: the values chosen, and one while none is, so a
|
|
83
|
+
* chip just added reads "is" rather than "is any of"; never more than one where only one can be
|
|
84
|
+
* chosen. Hosts that word the condition by count, as a plural rule does, get the right form. */
|
|
85
|
+
export declare function operatorCount(field: AnyField, filter: ActiveFilter): number;
|
|
82
86
|
/** Whether a filter narrows anything yet: a value chosen, or a condition that needs none. */
|
|
83
87
|
export declare function isComplete(filter: ActiveFilter): boolean;
|
|
84
88
|
/** Whether two sets of filters say the same, ignoring order, as Save or Update buttons ask. */
|
package/dist/list/filterModel.js
CHANGED
|
@@ -25,6 +25,12 @@ function fieldOperators(field) {
|
|
|
25
25
|
function isMultiple(field) {
|
|
26
26
|
return (field.kind === "enum" || field.kind === "people") && field.multiple !== false;
|
|
27
27
|
}
|
|
28
|
+
/** How many values a condition's words are for: the values chosen, and one while none is, so a
|
|
29
|
+
* chip just added reads "is" rather than "is any of"; never more than one where only one can be
|
|
30
|
+
* chosen. Hosts that word the condition by count, as a plural rule does, get the right form. */
|
|
31
|
+
function operatorCount(field, filter) {
|
|
32
|
+
return isMultiple(field) ? Math.max(filter.values.length, 1) : 1;
|
|
33
|
+
}
|
|
28
34
|
/** Whether a filter narrows anything yet: a value chosen, or a condition that needs none. */
|
|
29
35
|
function isComplete(filter) {
|
|
30
36
|
if (filter.op === "isSet" || filter.op === "isNotSet") return true;
|
|
@@ -305,4 +311,4 @@ function applyFilterList(rows, filters, fields, now = /* @__PURE__ */ new Date()
|
|
|
305
311
|
return rows.filter((row) => checks.every(({ field, filter }) => matchFilter(field, filter, row, now)));
|
|
306
312
|
}
|
|
307
313
|
//#endregion
|
|
308
|
-
export { DATE_PRESETS, applyFilterList, dateFilterRange, decodeFilters, encodeFilters, fieldOperators, filterParamKeys, fromStored, isComplete, isDatePreset, isMultiple, matchFilter, parseDay, sameFilters, toDay, toStored, withFilterParams };
|
|
314
|
+
export { DATE_PRESETS, applyFilterList, dateFilterRange, decodeFilters, encodeFilters, fieldOperators, filterParamKeys, fromStored, isComplete, isDatePreset, isMultiple, matchFilter, operatorCount, parseDay, sameFilters, toDay, toStored, withFilterParams };
|
package/dist/styles/theme.css
CHANGED
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
--theme-shadow-xl:
|
|
62
62
|
0 25px 80px -20px light-dark(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.6)),
|
|
63
63
|
0 10px 30px -10px light-dark(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.4));
|
|
64
|
+
--theme-backdrop: light-dark(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.65));
|
|
64
65
|
--theme-code-bg: light-dark(#e7eaee, #1c242c);
|
|
65
66
|
--theme-code-text: light-dark(#1c2330, #e1e9f2);
|
|
66
67
|
--theme-code-comment: light-dark(#555d6c, #c0c8d1);
|