@parallelworks/ui 0.50.0 → 0.51.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 +50 -5
- package/dist/ai/components/ModelSelector.js +1 -1
- package/dist/ai/components/agent/AgentMessageParts.js +3 -3
- package/dist/components/Callout.js +3 -7
- package/dist/components/CreateModal.js +1 -1
- package/dist/components/DescriptionList.d.ts +31 -17
- package/dist/components/DescriptionList.js +83 -31
- package/dist/components/Field.js +1 -1
- package/dist/components/MenuEntries.d.ts +39 -12
- package/dist/components/MenuEntries.js +24 -8
- package/dist/components/Meter.js +3 -3
- package/dist/components/PageHeader.js +1 -1
- package/dist/components/Provider.d.ts +2 -0
- package/dist/components/Provider.js +1 -0
- package/dist/components/RequiredMark.js +1 -1
- package/dist/components/RunStatusIcon.js +2 -2
- package/dist/components/StatusIcons.js +2 -2
- package/dist/components/ThemeEditor.js +6 -6
- package/dist/components/Toast.js +2 -2
- package/dist/components/buttonClasses.js +1 -1
- package/dist/components/tone.d.ts +5 -2
- package/dist/components/tone.js +15 -6
- package/dist/form/Wizard/WizardNavigation.js +1 -1
- package/dist/form/Wizard/WizardStepIndicator.js +1 -1
- package/dist/graph/DependencyGraph.js +1 -1
- package/dist/graph/GraphEditor.js +1 -1
- package/dist/graph/GraphEditorDialogs.js +1 -1
- package/dist/graph/ProblemsButton.js +2 -2
- package/dist/graph/ShortcutsButton.js +1 -1
- package/dist/graph/editorChrome.js +1 -1
- package/dist/graph/fieldProblems.js +1 -1
- package/dist/graph/stepWith.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/list/ActionToolbar.d.ts +2 -2
- package/dist/list/ActionToolbar.js +6 -3
- package/dist/list/ListPage.js +4 -2
- package/dist/list/ListViewControls.js +1 -1
- package/dist/list/rowMenu.d.ts +2 -2
- package/dist/logviewer/LogViewer.js +1 -1
- package/dist/styles/theme.css +24 -10
- package/dist/styles.css +1 -1
- package/dist/theme/derive.d.ts +1 -1
- package/dist/theme/derive.js +46 -21
- package/dist/theme/fit.js +10 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,8 @@ Each need has one component. Reach for it before styling markup by hand.
|
|
|
34
34
|
| A text field | `Input`, `Textarea`, or a control framed by `Field`, whose `actions` hold buttons that act on it |
|
|
35
35
|
| A number, a date, a color | `NumberInput`, `DatePicker`, `ColorInput` |
|
|
36
36
|
| A one-time code, such as a sign-in check | `OneTimeCodeInput` |
|
|
37
|
-
| A labeled value nobody edits, among fields | `FieldValue` in a `Field
|
|
37
|
+
| A labeled value nobody edits, among fields | `FieldValue` in a `Field` |
|
|
38
|
+
| A record's details, such as in a drawer or a card: names and values on their own | `DescriptionList` of `DescriptionListItem`s; `copy` on an ID, `mono` on a path, `variant="card"` to frame them |
|
|
38
39
|
| An error beside a control outside a `Field` | `ErrorText` |
|
|
39
40
|
| A dense field in a toolbar or a table cell | any field control with `size="sm"`; a width in `className` wins, and `cx` merges classes the same way |
|
|
40
41
|
| On or off | `Switch`; `Checkbox` in a list of things to tick, several of which may be on |
|
|
@@ -101,6 +102,30 @@ Some pairs look alike and are split on purpose:
|
|
|
101
102
|
A `Combobox` filters as you type, for a list long enough to search, and is the
|
|
102
103
|
one that takes several values, new ones, or free text (`Autocomplete`).
|
|
103
104
|
|
|
105
|
+
## Menu and row actions
|
|
106
|
+
|
|
107
|
+
A `MenuAction`, and the `RowAction` and `ToolbarAction` built on it, does
|
|
108
|
+
exactly one thing: runs `onClick`, navigates to `href`, or copies `copy`. An
|
|
109
|
+
action with none of them, or with two, doesn't compile, so a row that silently
|
|
110
|
+
does nothing when chosen can't ship. `checked` makes an `onClick` row an on/off
|
|
111
|
+
choice, and `reloadDocument` belongs to an `href`. A disabled action keeps its
|
|
112
|
+
behavior. In development, a menu row, pinned row button or toolbar button warns
|
|
113
|
+
in the console when it gets an action with no behavior anyway, from JavaScript
|
|
114
|
+
or a cast.
|
|
115
|
+
|
|
116
|
+
Moving to it:
|
|
117
|
+
|
|
118
|
+
- Rename a handler kept under another name, such as `onSelect`, to `onClick`.
|
|
119
|
+
An action built apart from its array, typed loosely or by another interface,
|
|
120
|
+
slipped past the old type; it now fails where it joins the array.
|
|
121
|
+
- Leave out an action that can't run, rather than passing
|
|
122
|
+
`onClick: cond ? run : undefined` or `href: url ?? undefined`:
|
|
123
|
+
`...(cond ? [{ key, label, onClick: run }] : [])`.
|
|
124
|
+
- Drop `onClick` from a `copy` or `href` action, and `checked` from an `href`
|
|
125
|
+
or `copy` one. To copy and also do something, call `useCopy` in an `onClick`.
|
|
126
|
+
- Write `RowAction` and `ToolbarAction` as types, not interfaces to extend:
|
|
127
|
+
`type MyAction = RowAction & { … }`.
|
|
128
|
+
|
|
104
129
|
## Themes
|
|
105
130
|
|
|
106
131
|
Every color a component may use is one of the closed set of `--theme-*` CSS
|
|
@@ -150,14 +175,29 @@ until it reaches its target against every surface it sits on.
|
|
|
150
175
|
| Pair | WCAG at 0 / 30 / 100 | APCA Lc |
|
|
151
176
|
| --- | --- | --- |
|
|
152
177
|
| Body text | 4.5 / 7 / 12 | 75 / 90 / 105 |
|
|
153
|
-
| Secondary text, links, status, syntax and terminal colors | 4.5 / 4.5 / 7 | 60 / 70 / 85 |
|
|
178
|
+
| Secondary text, links, status text, syntax and terminal colors | 4.5 / 4.5 / 7 | 60 / 70 / 85 |
|
|
154
179
|
| Placeholders | 3 / 3 / 4.5 | 45 / 50 / 65 |
|
|
155
|
-
| Focus ring | 3 / 3 / 4.5 | |
|
|
180
|
+
| Focus ring, status icons, dots, borders and bars | 3 / 3 / 4.5 | |
|
|
156
181
|
| Control borders | 1.6 / 2.2 / 3 | |
|
|
157
|
-
| Text on the accent | 4.5 | |
|
|
182
|
+
| Text on the accent, white text on a solid status fill | 4.5 | |
|
|
158
183
|
|
|
159
184
|
Status colors (success, error, warning, info, favorite) and terminal colors keep
|
|
160
|
-
fixed hues, so red means the same thing in every theme.
|
|
185
|
+
fixed hues, so red means the same thing in every theme.
|
|
186
|
+
|
|
187
|
+
Each status comes in four tokens, so a red icon stays bright while red words stay
|
|
188
|
+
readable:
|
|
189
|
+
|
|
190
|
+
| Token | For | Solved to |
|
|
191
|
+
| --- | --- | --- |
|
|
192
|
+
| `--theme-error` | Icons, dots, borders, rings, bars: anything but words | 3:1 on every surface and on the muted tint (WCAG 1.4.11) |
|
|
193
|
+
| `--theme-error-text` | Words in the status: error messages, a destructive menu item or text button, a badge's label | Secondary text's targets, on the surfaces and on the muted tint |
|
|
194
|
+
| `--theme-error-solid` | A fill under a white label, such as a destructive button | White on it at 4.5:1 |
|
|
195
|
+
| `--theme-error-muted` | A tinted background, such as a callout or badge | The vivid shade at 10% (15% in dark mode) |
|
|
196
|
+
|
|
197
|
+
The same four exist for `success`, `warning`, `info` and `favorite`. Words never
|
|
198
|
+
take the bare token: `--theme-error` meets only the 3:1 that graphics need. An
|
|
199
|
+
icon beside an error message may keep the message's text shade through
|
|
200
|
+
`currentColor`. Syntax colors turn away
|
|
161
201
|
from the accent's hue when they come within 30° of it, so a keyword never looks
|
|
162
202
|
like a link.
|
|
163
203
|
|
|
@@ -259,6 +299,11 @@ one is meant to differ.
|
|
|
259
299
|
| `--theme-bg` | `--theme-sidebar-bg` |
|
|
260
300
|
| `theme-element` (utility) | `theme-accent` |
|
|
261
301
|
|
|
302
|
+
`--theme-<status>` (`success`, `error`, `warning`, `info`, `favorite`) was once
|
|
303
|
+
solved as text. It is now the vivid graphic shade, so words that read it move to
|
|
304
|
+
`--theme-<status>-text`, and a fill under a white label to
|
|
305
|
+
`--theme-<status>-solid`.
|
|
306
|
+
|
|
262
307
|
## Buttons and spinners
|
|
263
308
|
|
|
264
309
|
Every pressable thing has one look, from `buttonClasses({ variant, size, iconOnly })`:
|
|
@@ -109,7 +109,7 @@ function ModelSelector({ targetSession }) {
|
|
|
109
109
|
children: t.loading
|
|
110
110
|
});
|
|
111
111
|
if (modelsError && models.length === 0) return /* @__PURE__ */ jsxs("span", {
|
|
112
|
-
className: cx(chipClassName, "cursor-default text-(--theme-error)"),
|
|
112
|
+
className: cx(chipClassName, "cursor-default text-(--theme-error-text)"),
|
|
113
113
|
children: [t.failed, /* @__PURE__ */ jsxs("button", {
|
|
114
114
|
type: "button",
|
|
115
115
|
onClick: () => refreshModels(),
|
|
@@ -220,7 +220,7 @@ function ToolCallBlock({ part }) {
|
|
|
220
220
|
}), /* @__PURE__ */ jsx(ResultBody, { part })]
|
|
221
221
|
}),
|
|
222
222
|
!isDiff && !expanded && /* @__PURE__ */ jsx("div", {
|
|
223
|
-
className: cx("ml-5 mt-0.5 text-[13px]", part.status === "error" ? "text-(--theme-error)" : "theme-muted-text"),
|
|
223
|
+
className: cx("ml-5 mt-0.5 text-[13px]", part.status === "error" ? "text-(--theme-error-text)" : "theme-muted-text"),
|
|
224
224
|
children: summary
|
|
225
225
|
}),
|
|
226
226
|
isDiff && /* @__PURE__ */ jsx("div", {
|
|
@@ -277,7 +277,7 @@ function SubagentCard({ part }) {
|
|
|
277
277
|
]
|
|
278
278
|
})]
|
|
279
279
|
})] }),
|
|
280
|
-
panelClassName: cx("ml-5 mt-0.5 space-y-1 text-[13px]", part.status === "failed" ? "text-(--theme-error)" : "theme-muted-text"),
|
|
280
|
+
panelClassName: cx("ml-5 mt-0.5 space-y-1 text-[13px]", part.status === "failed" ? "text-(--theme-error-text)" : "theme-muted-text"),
|
|
281
281
|
children: [actions.length > 0 && /* @__PURE__ */ jsxs("ul", {
|
|
282
282
|
className: "space-y-0.5",
|
|
283
283
|
children: [actions.map((a, i) => /* @__PURE__ */ jsx("li", { children: a }, i)), part.status !== "running" && /* @__PURE__ */ jsx("li", { children: outcome })]
|
|
@@ -286,7 +286,7 @@ function SubagentCard({ part }) {
|
|
|
286
286
|
children: /* @__PURE__ */ jsx(Markdown, { children: part.report })
|
|
287
287
|
})]
|
|
288
288
|
}), !expanded && /* @__PURE__ */ jsx("div", {
|
|
289
|
-
className: cx("ml-5 mt-0.5 text-[13px]", part.status === "failed" ? "text-(--theme-error)" : "theme-muted-text"),
|
|
289
|
+
className: cx("ml-5 mt-0.5 text-[13px]", part.status === "failed" ? "text-(--theme-error-text)" : "theme-muted-text"),
|
|
290
290
|
children: outcome
|
|
291
291
|
})]
|
|
292
292
|
});
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { InfoIcon, WarningIcon } from "../icons.js";
|
|
3
|
-
import { toneSurface } from "./tone.js";
|
|
3
|
+
import { toneIcon, toneSurface } from "./tone.js";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/Callout.tsx
|
|
6
6
|
function CalloutIcon({ type }) {
|
|
7
|
-
|
|
7
|
+
return /* @__PURE__ */ jsx(type === "info" ? InfoIcon : WarningIcon, {
|
|
8
8
|
"aria-hidden": "true",
|
|
9
|
-
className: "mr-3 h-5 w-5 shrink-0"
|
|
10
|
-
});
|
|
11
|
-
else return /* @__PURE__ */ jsx(InfoIcon, {
|
|
12
|
-
"aria-hidden": "true",
|
|
13
|
-
className: "mr-3 h-5 w-5 shrink-0"
|
|
9
|
+
className: cx("mr-3 h-5 w-5 shrink-0", toneIcon[type])
|
|
14
10
|
});
|
|
15
11
|
}
|
|
16
12
|
function Callout({ type, className = "", children }) {
|
|
@@ -127,7 +127,7 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
|
|
|
127
127
|
className: cx("mt-2 -mb-1 -mx-1.5 w-[calc(100%+0.75rem)] rounded-md border-0 px-1.5 py-1 bg-transparent shadow-none text-lg font-medium text-(--theme-app) placeholder:text-(--theme-placeholder) focus:outline-none", nameFlash && "animate-field-flash", nameMissing ? "ring-1 ring-(--theme-error)" : "focus:ring-0")
|
|
128
128
|
}),
|
|
129
129
|
nameError && /* @__PURE__ */ jsx("p", {
|
|
130
|
-
className: "mt-1 text-xs text-(--theme-error)",
|
|
130
|
+
className: "mt-1 text-xs text-(--theme-error-text)",
|
|
131
131
|
children: nameError
|
|
132
132
|
}),
|
|
133
133
|
hint && /* @__PURE__ */ jsx("p", {
|
|
@@ -1,18 +1,32 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
2
|
+
/** `plain` sets rows apart by space alone; `divided` rules a line between them; `card`
|
|
3
|
+
* also frames them in a panel. */
|
|
4
|
+
export type DescriptionListVariant = 'plain' | 'divided' | 'card';
|
|
5
|
+
export interface DescriptionListProps extends Omit<ComponentPropsWithoutRef<'dl'>, 'className'> {
|
|
6
|
+
variant?: DescriptionListVariant | undefined;
|
|
7
|
+
/** Puts each label over its value at any width. Without it they stack only when the
|
|
8
|
+
* list is too narrow for both side by side. */
|
|
9
|
+
stacked?: boolean | undefined;
|
|
10
|
+
/** Styles the box around the list, such as its margins or width. */
|
|
11
|
+
className?: string | undefined;
|
|
7
12
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
/** Names and their values, such as the details of a record in a drawer or a card. The
|
|
14
|
+
* label column is as wide as its longest label, up to two fifths of the list, so a
|
|
15
|
+
* long label wraps rather than squeezing the values; each label sits over its value
|
|
16
|
+
* when the list is narrow. */
|
|
17
|
+
export default function DescriptionList({ variant, stacked, className, children, ...props }: DescriptionListProps): import("react").JSX.Element;
|
|
18
|
+
export interface DescriptionListItemProps {
|
|
19
|
+
label: ReactNode;
|
|
20
|
+
/** The value. Nothing, an empty string or `false` shows the library's empty mark. */
|
|
21
|
+
children?: ReactNode;
|
|
22
|
+
/** Help behind an info button beside the label. */
|
|
23
|
+
tooltip?: string | undefined;
|
|
24
|
+
/** Sets the value in a monospace face, for an ID, a path or a hash. */
|
|
25
|
+
mono?: boolean | undefined;
|
|
26
|
+
/** Adds a button that copies the value: `true` copies its text as shown; a string
|
|
27
|
+
* copies that instead, such as a full ID behind a shortened one. */
|
|
28
|
+
copy?: boolean | string | undefined;
|
|
29
|
+
className?: string | undefined;
|
|
30
|
+
}
|
|
31
|
+
/** A label and its value in a DescriptionList. */
|
|
32
|
+
export declare function DescriptionListItem({ label, children, tooltip, mono, copy, className, }: DescriptionListItemProps): import("react").JSX.Element;
|
|
@@ -1,41 +1,93 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
|
+
import { CheckIcon, CopyIcon } from "../icons.js";
|
|
3
|
+
import { useStrings } from "./Provider.js";
|
|
4
|
+
import { useCopy } from "./useCopy.js";
|
|
5
|
+
import { IconButton } from "./IconButton.js";
|
|
2
6
|
import { InfoTip } from "./InfoTip.js";
|
|
3
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
-
import
|
|
8
|
+
import { useId, useRef } from "react";
|
|
5
9
|
//#region src/components/DescriptionList.tsx
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
var variantClasses = {
|
|
11
|
+
plain: "*:py-1",
|
|
12
|
+
divided: "divide-y divide-(--theme-border) *:py-2",
|
|
13
|
+
card: "panel divide-y divide-(--theme-border) px-4 *:py-2"
|
|
14
|
+
};
|
|
15
|
+
/** Names and their values, such as the details of a record in a drawer or a card. The
|
|
16
|
+
* label column is as wide as its longest label, up to two fifths of the list, so a
|
|
17
|
+
* long label wraps rather than squeezing the values; each label sits over its value
|
|
18
|
+
* when the list is narrow. */
|
|
19
|
+
function DescriptionList({ variant = "plain", stacked = false, className, children, ...props }) {
|
|
20
|
+
return /* @__PURE__ */ jsx("div", {
|
|
21
|
+
className: cx("@container/dl min-w-0", className),
|
|
22
|
+
children: /* @__PURE__ */ jsx("dl", {
|
|
23
|
+
...props,
|
|
24
|
+
className: cx("m-0 grid grid-cols-[minmax(0,1fr)] text-sm", !stacked && "@sm/dl:grid-cols-[fit-content(40%)_minmax(0,1fr)] @sm/dl:gap-x-6", variantClasses[variant]),
|
|
25
|
+
children
|
|
26
|
+
})
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
var isEmpty = (value) => value === null || value === void 0 || value === false || value === "";
|
|
30
|
+
/** A label and its value in a DescriptionList. */
|
|
31
|
+
function DescriptionListItem({ label, children, tooltip, mono = false, copy = false, className }) {
|
|
32
|
+
const labelId = useId();
|
|
33
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
34
|
+
className: cx("col-span-full grid grid-cols-subgrid gap-y-0.5", className),
|
|
35
|
+
children: [/* @__PURE__ */ jsxs("dt", {
|
|
36
|
+
className: "min-w-0 leading-5 wrap-break-word text-(--theme-muted-text-color)",
|
|
37
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
38
|
+
id: labelId,
|
|
39
|
+
children: label
|
|
40
|
+
}), tooltip && /* @__PURE__ */ jsx(InfoTip, {
|
|
41
|
+
className: "ml-1 align-[-2px]",
|
|
42
|
+
text: tooltip
|
|
43
|
+
})]
|
|
44
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
45
|
+
className: "m-0 flex min-w-0 items-start gap-1 text-(--theme-app)",
|
|
46
|
+
children: isEmpty(children) ? /* @__PURE__ */ jsx(EmptyValue, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
47
|
+
"data-value": true,
|
|
48
|
+
className: cx("min-w-0 leading-5 wrap-anywhere", mono && "font-mono text-[0.8125rem]"),
|
|
49
|
+
children
|
|
50
|
+
}), copy !== false && /* @__PURE__ */ jsx(CopyValue, {
|
|
51
|
+
text: copy === true ? void 0 : copy,
|
|
52
|
+
label,
|
|
53
|
+
labelId
|
|
54
|
+
})] })
|
|
55
|
+
})]
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
var EMPTY_MARK = "—";
|
|
59
|
+
function EmptyValue() {
|
|
60
|
+
const { common: t } = useStrings();
|
|
61
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
62
|
+
className: "leading-5 text-(--theme-muted-text-color)",
|
|
9
63
|
children: [/* @__PURE__ */ jsx("span", {
|
|
10
|
-
|
|
11
|
-
children:
|
|
12
|
-
}),
|
|
13
|
-
className: "
|
|
14
|
-
|
|
64
|
+
"aria-hidden": "true",
|
|
65
|
+
children: EMPTY_MARK
|
|
66
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
67
|
+
className: "sr-only",
|
|
68
|
+
children: t.noValue
|
|
15
69
|
})]
|
|
16
|
-
})
|
|
17
|
-
...props,
|
|
18
|
-
className: "flex text-[13px] leading-6 col-span-4 font-light items-center overflow-auto",
|
|
19
|
-
children
|
|
20
|
-
})] });
|
|
70
|
+
});
|
|
21
71
|
}
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
72
|
+
function CopyValue({ text, label, labelId }) {
|
|
73
|
+
const { common: t } = useStrings();
|
|
74
|
+
const { copy, copied } = useCopy();
|
|
75
|
+
const buttonId = useId();
|
|
76
|
+
const button = useRef(null);
|
|
77
|
+
const onClick = () => {
|
|
78
|
+
const value = button.current?.closest("dd")?.querySelector("[data-value]");
|
|
79
|
+
const shown = value?.innerText ?? value?.textContent;
|
|
80
|
+
copy(text ?? shown ?? "", typeof label === "string" ? { label } : void 0);
|
|
81
|
+
};
|
|
82
|
+
return /* @__PURE__ */ jsx(IconButton, {
|
|
83
|
+
ref: button,
|
|
84
|
+
id: buttonId,
|
|
85
|
+
size: "xs",
|
|
86
|
+
label: copied ? t.copied : t.copy,
|
|
87
|
+
"aria-labelledby": `${buttonId} ${labelId}`,
|
|
88
|
+
icon: copied ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(CopyIcon, {}),
|
|
89
|
+
onClick,
|
|
90
|
+
className: "-my-0.5"
|
|
39
91
|
});
|
|
40
92
|
}
|
|
41
93
|
//#endregion
|
package/dist/components/Field.js
CHANGED
|
@@ -31,7 +31,7 @@ var labelSizes = {
|
|
|
31
31
|
};
|
|
32
32
|
var labelClassName = "flex items-center gap-1 font-medium";
|
|
33
33
|
var actionsClassName = "ml-auto flex shrink-0 items-center gap-1";
|
|
34
|
-
var errorTextClassName = "flex items-center gap-1.5 text-xs text-(--theme-error)";
|
|
34
|
+
var errorTextClassName = "flex items-center gap-1.5 text-xs text-(--theme-error-text)";
|
|
35
35
|
/** An error in the look a Field gives its own, for a control outside a Field, or for a part of
|
|
36
36
|
* one rather than the whole of it, such as a row of a list editor. Give it an `id` and name it
|
|
37
37
|
* in the control's `aria-describedby` so the error is read with the control. */
|
|
@@ -1,27 +1,47 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
|
-
|
|
3
|
-
export interface MenuAction {
|
|
2
|
+
interface MenuActionBase {
|
|
4
3
|
label: string;
|
|
5
4
|
icon?: ReactNode;
|
|
6
|
-
/** Navigates instead of calling onClick. */
|
|
7
|
-
href?: string | undefined;
|
|
8
|
-
/** Loads the page instead of routing, for paths the host's router doesn't serve. */
|
|
9
|
-
reloadDocument?: boolean | undefined;
|
|
10
|
-
onClick?: (() => void) | undefined;
|
|
11
5
|
disabled?: boolean | undefined;
|
|
12
6
|
/** Why the action is disabled, shown on hover and read with its name. */
|
|
13
7
|
disabledReason?: string | undefined;
|
|
14
8
|
destructive?: boolean | undefined;
|
|
9
|
+
/** More text a menu's search matches, beside the label. */
|
|
10
|
+
keywords?: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
/** Runs `onClick`. */
|
|
13
|
+
interface MenuClickAction extends MenuActionBase {
|
|
14
|
+
onClick: () => void;
|
|
15
15
|
/** Makes the row an on/off choice, checked while true. */
|
|
16
16
|
checked?: boolean | undefined;
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
href?: never;
|
|
18
|
+
reloadDocument?: never;
|
|
19
|
+
copy?: never;
|
|
20
|
+
}
|
|
21
|
+
/** Navigates to `href`. */
|
|
22
|
+
interface MenuLinkAction extends MenuActionBase {
|
|
23
|
+
href: string;
|
|
24
|
+
/** Loads the page instead of routing, for paths the host's router doesn't serve. */
|
|
25
|
+
reloadDocument?: boolean | undefined;
|
|
26
|
+
onClick?: never;
|
|
27
|
+
checked?: never;
|
|
28
|
+
copy?: never;
|
|
29
|
+
}
|
|
30
|
+
/** Copies `copy.text` to the clipboard, confirmed with a toast naming `copy.label`. */
|
|
31
|
+
interface MenuCopyAction extends MenuActionBase {
|
|
32
|
+
copy: {
|
|
19
33
|
text: string;
|
|
20
34
|
label: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
};
|
|
36
|
+
onClick?: never;
|
|
37
|
+
checked?: never;
|
|
38
|
+
href?: never;
|
|
39
|
+
reloadDocument?: never;
|
|
24
40
|
}
|
|
41
|
+
/** Something a menu row does: runs `onClick`, navigates to `href`, or copies
|
|
42
|
+
* `copy`. Exactly one, so a row that does nothing when chosen doesn't compile.
|
|
43
|
+
* A disabled action keeps its behavior; one that has none belongs out of the menu. */
|
|
44
|
+
export type MenuAction = MenuClickAction | MenuLinkAction | MenuCopyAction;
|
|
25
45
|
/** A row that opens a menu of its own. */
|
|
26
46
|
export interface MenuSubmenu {
|
|
27
47
|
label: string;
|
|
@@ -54,3 +74,10 @@ export declare function MenuEntries({ entries, search, query, }: {
|
|
|
54
74
|
/** The search's text, from the MenuFilter around the menu's root. */
|
|
55
75
|
query?: string;
|
|
56
76
|
}): import("react").JSX.Element;
|
|
77
|
+
/** Warns in development when an action has no behavior. The types rule that
|
|
78
|
+
* out, but a JavaScript caller or a cast still can, and its row renders fine
|
|
79
|
+
* and silently does nothing. */
|
|
80
|
+
export declare function warnIfInert(action: MenuAction, where: string): void;
|
|
81
|
+
/** Runs an action that isn't a link: copies its text or calls its onClick. */
|
|
82
|
+
export declare function useRunAction(): (action: MenuAction) => void;
|
|
83
|
+
export {};
|
|
@@ -9,7 +9,7 @@ import { useQueryMatch } from "./search.js";
|
|
|
9
9
|
import { useCopy } from "./useCopy.js";
|
|
10
10
|
import { Menu } from "@base-ui/react/menu";
|
|
11
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
-
import { useState } from "react";
|
|
12
|
+
import { useCallback, useState } from "react";
|
|
13
13
|
//#region src/components/MenuEntries.tsx
|
|
14
14
|
var menuDivider = { divider: true };
|
|
15
15
|
var isMenuDivider = (entry) => "divider" in entry;
|
|
@@ -73,10 +73,29 @@ function SubmenuRow({ submenu }) {
|
|
|
73
73
|
})] })
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
|
-
var destructiveClassName = "text-(--theme-error) data-highlighted:text-(--theme-error)";
|
|
76
|
+
var destructiveClassName = "text-(--theme-error-text) data-highlighted:text-(--theme-error-text)";
|
|
77
|
+
var warnedInert = /* @__PURE__ */ new Set();
|
|
78
|
+
/** Warns in development when an action has no behavior. The types rule that
|
|
79
|
+
* out, but a JavaScript caller or a cast still can, and its row renders fine
|
|
80
|
+
* and silently does nothing. */
|
|
81
|
+
function warnIfInert(action, where) {
|
|
82
|
+
if (process.env.NODE_ENV === "production") return;
|
|
83
|
+
if (action.onClick || action.href || action.copy || warnedInert.has(action.label)) return;
|
|
84
|
+
warnedInert.add(action.label);
|
|
85
|
+
console.warn(`${where}: the action "${action.label}" has no onClick, href or copy, so choosing it does nothing.`);
|
|
86
|
+
}
|
|
87
|
+
/** Runs an action that isn't a link: copies its text or calls its onClick. */
|
|
88
|
+
function useRunAction() {
|
|
89
|
+
const { copy } = useCopy();
|
|
90
|
+
return useCallback((action) => {
|
|
91
|
+
if (action.copy) copy(action.copy.text, { label: action.copy.label });
|
|
92
|
+
else action.onClick?.();
|
|
93
|
+
}, [copy]);
|
|
94
|
+
}
|
|
77
95
|
function ActionRow({ action }) {
|
|
96
|
+
warnIfInert(action, "MenuEntries");
|
|
78
97
|
const Link = useLink();
|
|
79
|
-
const
|
|
98
|
+
const runAction = useRunAction();
|
|
80
99
|
const className = cx(action.destructive && destructiveClassName);
|
|
81
100
|
const content = /* @__PURE__ */ jsxs(Fragment, { children: [action.icon, action.label] });
|
|
82
101
|
if (action.href && !action.disabled) return action.reloadDocument ? /* @__PURE__ */ jsx(MenuLinkItem, {
|
|
@@ -88,10 +107,7 @@ function ActionRow({ action }) {
|
|
|
88
107
|
className,
|
|
89
108
|
children: content
|
|
90
109
|
});
|
|
91
|
-
const run = () =>
|
|
92
|
-
if (action.copy) copy(action.copy.text, { label: action.copy.label });
|
|
93
|
-
action.onClick?.();
|
|
94
|
-
};
|
|
110
|
+
const run = () => runAction(action);
|
|
95
111
|
const reason = action.disabled ? action.disabledReason : void 0;
|
|
96
112
|
const label = /* @__PURE__ */ jsxs(Fragment, { children: [content, reason && /* @__PURE__ */ jsxs("span", {
|
|
97
113
|
className: "sr-only",
|
|
@@ -115,4 +131,4 @@ function ActionRow({ action }) {
|
|
|
115
131
|
});
|
|
116
132
|
}
|
|
117
133
|
//#endregion
|
|
118
|
-
export { MenuEntries, MenuFilter, isMenuDivider, isMenuSubmenu, menuDivider };
|
|
134
|
+
export { MenuEntries, MenuFilter, isMenuDivider, isMenuSubmenu, menuDivider, useRunAction, warnIfInert };
|
package/dist/components/Meter.js
CHANGED
|
@@ -6,9 +6,9 @@ import { Progress } from "@base-ui/react/progress";
|
|
|
6
6
|
//#region src/components/Meter.tsx
|
|
7
7
|
var fillClassName = {
|
|
8
8
|
accent: "bg-(--theme-accent)",
|
|
9
|
-
success: "bg-
|
|
10
|
-
warning: "bg-
|
|
11
|
-
error: "bg-
|
|
9
|
+
success: "bg-(--theme-success)",
|
|
10
|
+
warning: "bg-(--theme-warning)",
|
|
11
|
+
error: "bg-(--theme-error)",
|
|
12
12
|
muted: "bg-(--theme-muted-text-color)"
|
|
13
13
|
};
|
|
14
14
|
var trackClassName = {
|
|
@@ -100,7 +100,7 @@ function BreadcrumbsRightAction({ children }) {
|
|
|
100
100
|
var breadcrumbsActionClasses = "flex cursor-pointer items-center gap-1 text-xs whitespace-nowrap hover:not-disabled:underline disabled:cursor-not-allowed disabled:opacity-50";
|
|
101
101
|
function BreadcrumbsActionButton({ icon: Icon, label, ariaLabel, href, onClick, disabled, tooltip, destructive = false }) {
|
|
102
102
|
const Link = useLink();
|
|
103
|
-
const className = cx(breadcrumbsActionClasses, destructive ? "text-(--theme-error)" : "text-(--theme-link)");
|
|
103
|
+
const className = cx(breadcrumbsActionClasses, destructive ? "text-(--theme-error-text)" : "text-(--theme-link)");
|
|
104
104
|
const content = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Icon, { className: "w-3 h-3" }), label] });
|
|
105
105
|
return /* @__PURE__ */ jsx(Tooltip, {
|
|
106
106
|
content: tooltip,
|
|
@@ -56,6 +56,8 @@ export interface UIStrings {
|
|
|
56
56
|
copyThing: (label: string) => string;
|
|
57
57
|
copiedThing: (label: string) => string;
|
|
58
58
|
copyThingFailed: (label: string) => string;
|
|
59
|
+
/** Read out where a value is missing, which shows as a dash. */
|
|
60
|
+
noValue: string;
|
|
59
61
|
/** A name field's rule: the characters a key or identifier allows. */
|
|
60
62
|
invalidName: string;
|
|
61
63
|
invalidNumber: string;
|
|
@@ -68,6 +68,7 @@ var DEFAULTS = {
|
|
|
68
68
|
copyThing: (label) => `Copy ${label}`,
|
|
69
69
|
copiedThing: (label) => `Copied ${label}`,
|
|
70
70
|
copyThingFailed: (label) => `Couldn't copy ${label}`,
|
|
71
|
+
noValue: "None",
|
|
71
72
|
invalidName: "Use letters, numbers, dashes, and underscores.",
|
|
72
73
|
invalidNumber: "Use a number.",
|
|
73
74
|
copy: "Copy",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { CancelIcon, FailIcon, NotRunningIcon, RunningIcon, SkipIcon, SuccessIcon } from "../icons.js";
|
|
3
|
-
import {
|
|
3
|
+
import { toneIcon } from "./tone.js";
|
|
4
4
|
import { useStrings } from "./Provider.js";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/RunStatusIcon.tsx
|
|
@@ -73,7 +73,7 @@ function RunStatusIcon({ status, className }) {
|
|
|
73
73
|
"aria-label": label
|
|
74
74
|
} : { "aria-hidden": true },
|
|
75
75
|
"data-status": status || "not-run",
|
|
76
|
-
className: cx("flex h-2 items-center",
|
|
76
|
+
className: cx("flex h-2 items-center", toneIcon[look.tone], className),
|
|
77
77
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
78
78
|
"aria-hidden": "true",
|
|
79
79
|
className: cx(look.spin && "animate-spin rounded-full")
|
|
@@ -3,10 +3,10 @@ import { ErrorIcon, SuccessIcon } from "../icons.js";
|
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
//#region src/components/StatusIcons.tsx
|
|
5
5
|
function SuccessCheckmark({ className, colored = true }) {
|
|
6
|
-
return /* @__PURE__ */ jsx(SuccessIcon, { className: cx(colored && "text-
|
|
6
|
+
return /* @__PURE__ */ jsx(SuccessIcon, { className: cx(colored && "text-(--theme-success)", className) });
|
|
7
7
|
}
|
|
8
8
|
function ErrorCheckmark({ className, colored = true }) {
|
|
9
|
-
return /* @__PURE__ */ jsx(ErrorIcon, { className: cx(colored && "text-
|
|
9
|
+
return /* @__PURE__ */ jsx(ErrorIcon, { className: cx(colored && "text-(--theme-error)", className) });
|
|
10
10
|
}
|
|
11
11
|
//#endregion
|
|
12
12
|
export { ErrorCheckmark, SuccessCheckmark };
|
|
@@ -318,13 +318,13 @@ function ThemePreview({ theme, mode }) {
|
|
|
318
318
|
/* @__PURE__ */ jsxs("div", {
|
|
319
319
|
className: "flex flex-wrap items-center gap-2",
|
|
320
320
|
children: [
|
|
321
|
-
/* @__PURE__ */
|
|
322
|
-
className: "rounded-full bg-(--theme-success-muted) px-2 py-0.5 text-xs text-(--theme-success)",
|
|
323
|
-
children: t.previewPassed
|
|
321
|
+
/* @__PURE__ */ jsxs("span", {
|
|
322
|
+
className: "inline-flex items-center gap-1.5 rounded-full bg-(--theme-success-muted) px-2 py-0.5 text-xs text-(--theme-success-text)",
|
|
323
|
+
children: [/* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-(--theme-success)" }), t.previewPassed]
|
|
324
324
|
}),
|
|
325
|
-
/* @__PURE__ */
|
|
326
|
-
className: "rounded-full bg-(--theme-error-muted) px-2 py-0.5 text-xs text-(--theme-error)",
|
|
327
|
-
children: t.previewFailed
|
|
325
|
+
/* @__PURE__ */ jsxs("span", {
|
|
326
|
+
className: "inline-flex items-center gap-1.5 rounded-full bg-(--theme-error-muted) px-2 py-0.5 text-xs text-(--theme-error-text)",
|
|
327
|
+
children: [/* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-(--theme-error)" }), t.previewFailed]
|
|
328
328
|
}),
|
|
329
329
|
/* @__PURE__ */ jsx("span", {
|
|
330
330
|
className: "text-(--theme-link) underline underline-offset-2",
|
package/dist/components/Toast.js
CHANGED
|
@@ -2,7 +2,7 @@ import { cx } from "../cx.js";
|
|
|
2
2
|
import { CloseIcon, ErrorIcon, InfoIcon, SuccessIcon, WarningIcon } from "../icons.js";
|
|
3
3
|
import { buttonClasses } from "./buttonClasses.js";
|
|
4
4
|
import { Loader } from "./Loader.js";
|
|
5
|
-
import {
|
|
5
|
+
import { toneIcon } from "./tone.js";
|
|
6
6
|
import { useStrings } from "./Provider.js";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { useEffect, useId, useSyncExternalStore } from "react";
|
|
@@ -149,7 +149,7 @@ function ToneIcon({ tone }) {
|
|
|
149
149
|
const Icon = ICONS[tone];
|
|
150
150
|
return /* @__PURE__ */ jsx(Icon, {
|
|
151
151
|
"aria-hidden": "true",
|
|
152
|
-
className: cx("mt-0.5 size-4 shrink-0",
|
|
152
|
+
className: cx("mt-0.5 size-4 shrink-0", toneIcon[tone])
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
function ToastList() {
|
|
@@ -6,7 +6,7 @@ var variants = {
|
|
|
6
6
|
secondary: "border border-(--theme-border) bg-(--theme-panel-bg) text-(--theme-app) hover:not-disabled:not-aria-disabled:bg-(--theme-hover)",
|
|
7
7
|
outline: "border border-(--theme-border) text-(--theme-muted-text-color) hover:not-disabled:not-aria-disabled:bg-(--theme-muted-panel-bg) hover:not-disabled:not-aria-disabled:text-(--theme-app) aria-pressed:border-(--theme-link) aria-pressed:text-(--theme-app) data-selected:border-(--theme-link) data-selected:text-(--theme-app) data-popup-open:bg-(--theme-muted-panel-bg) data-popup-open:text-(--theme-app)",
|
|
8
8
|
ghost: "text-(--theme-muted-text-color) hover:not-disabled:not-aria-disabled:bg-(--theme-muted-panel-bg) hover:not-disabled:not-aria-disabled:text-(--theme-app) aria-pressed:bg-(--theme-muted-panel-bg) aria-pressed:text-(--theme-link) data-selected:bg-(--theme-muted-panel-bg) data-selected:text-(--theme-link) data-popup-open:bg-(--theme-muted-panel-bg) data-popup-open:text-(--theme-app)",
|
|
9
|
-
danger: "bg-(--theme-error) text-white hover:not-disabled:not-aria-disabled:bg-(--theme-error)/90"
|
|
9
|
+
danger: "bg-(--theme-error-solid) text-white hover:not-disabled:not-aria-disabled:bg-(--theme-error-solid)/90"
|
|
10
10
|
};
|
|
11
11
|
var sizes = {
|
|
12
12
|
xs: "h-6 px-2 text-xs [&_svg]:size-3",
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/** How a status reads: its colors come from the theme's tone tokens, so every
|
|
2
|
-
* theme, dark ones included, sets them once.
|
|
2
|
+
* theme, dark ones included, sets them once. Words take a status's text shade,
|
|
3
|
+
* and icons and dots its vivid one, which only graphics' 3:1 holds. */
|
|
3
4
|
export type Tone = 'default' | 'success' | 'warning' | 'error' | 'info' | 'muted';
|
|
4
5
|
/** Text in the tone, for a status line beside a dot or icon. */
|
|
5
6
|
export declare const toneText: Record<Tone, string>;
|
|
7
|
+
/** An icon in the tone, through `currentColor`. */
|
|
8
|
+
export declare const toneIcon: Record<Tone, string>;
|
|
6
9
|
/** A tinted surface with a border and text in the tone, for a badge or a callout. */
|
|
7
10
|
export declare const toneSurface: Record<Tone, string>;
|
|
8
|
-
/** A solid fill in the tone, for a dot. */
|
|
11
|
+
/** A solid fill in the tone, for a dot or a bar. */
|
|
9
12
|
export declare const toneFill: Record<Tone, string>;
|