@parallelworks/ui 0.84.2 → 0.86.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 +1 -1
- package/dist/ai/components/ChatMessage.js +6 -0
- package/dist/ai/core/ChatProvider.js +16 -5
- package/dist/components/CopyButton.d.ts +7 -2
- package/dist/components/CopyButton.js +11 -52
- package/dist/components/PageHeaderAction.d.ts +12 -5
- package/dist/components/PageHeaderAction.js +10 -9
- package/dist/components/Pagination.d.ts +4 -2
- package/dist/components/Pagination.js +54 -17
- package/dist/components/Provider.d.ts +4 -0
- package/dist/components/Provider.js +5 -1
- package/dist/components/RunStatusIcon.js +14 -17
- package/dist/components/StatusBadge.d.ts +10 -1
- package/dist/components/StatusBadge.js +27 -15
- package/dist/components/StatusIcons.d.ts +2 -0
- package/dist/components/StatusIcons.js +5 -3
- package/dist/components/Tabs.js +31 -2
- package/dist/components/Tag.d.ts +2 -1
- package/dist/components/Tag.js +5 -4
- package/dist/components/chart.d.ts +57 -10
- package/dist/components/chart.js +117 -22
- package/dist/components/scrollFade.d.ts +13 -0
- package/dist/components/scrollFade.js +51 -0
- package/dist/file-explorer/FileExplorer.js +8 -0
- package/dist/icons.d.ts +14 -1
- package/dist/icons.js +104 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/list/ListPage.d.ts +4 -1
- package/dist/list/ListPage.js +96 -14
- package/dist/list/ListTable.d.ts +1 -0
- package/dist/list/ListTable.js +1 -1
- package/dist/list/ListViewControls.d.ts +1 -1
- package/dist/list/ListViewControls.js +2 -1
- package/dist/list/index.js +2 -2
- package/dist/list/listView.d.ts +15 -3
- package/dist/list/listView.js +11 -4
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -125,7 +125,7 @@ Each need has one component. Reach for it before styling markup by hand.
|
|
|
125
125
|
| A status in words | `StatusBadge`, or `StatusDot` beside text that says it |
|
|
126
126
|
| A thing's tags or labels | `Tag`; `TagList` for several, wrapped or on one line ending in "+N" |
|
|
127
127
|
| Keys to press | `Kbd`, with `Mod` for Command or Control; `ariaKeyShortcuts` for the control the keys press |
|
|
128
|
-
| A chart's colors, legend and tooltip | `seriesColors
|
|
128
|
+
| A chart's colors, legend and tooltip | `seriesColors` (by each series' place in a stable key list, eight at most) and `foldSeries` (the rest into "Other"), `ChartLegendList`, `ChartTooltipContent` placed beside the crosshair with `ChartTooltipBeside` or `chartTooltipPlacement`, and `useSeriesHighlight`, around a chart drawn by hand or by a library |
|
|
129
129
|
| A run's, job's or step's status beside its name | `RunStatusIcon`; `StatusBadge` with `runStatusTone` to say it in words |
|
|
130
130
|
| A copy action | `CopyButton`; `CopyCodeBlock` for a command or snippet, with `secret` for a value shown once; `SelectionCopy` to copy part of a block by selecting it; `useCopy` for a menu row or a control of your own |
|
|
131
131
|
| A live relative time or duration | `RelativeTime` (with a `fallback`, an `exact` style for its tooltip, or `show="absolute"`), `Elapsed` |
|
|
@@ -121,6 +121,12 @@ function ChatMessage({ message, allMessages, isStreaming = false, latest = false
|
|
|
121
121
|
isStreaming,
|
|
122
122
|
onApprovalAnswer: approvalAnswer
|
|
123
123
|
}),
|
|
124
|
+
hasError && /* @__PURE__ */ jsx(Callout, {
|
|
125
|
+
tone: "error",
|
|
126
|
+
size: "sm",
|
|
127
|
+
className: "mt-2",
|
|
128
|
+
children: message.error
|
|
129
|
+
}),
|
|
124
130
|
isStreaming && !isUser && /* @__PURE__ */ jsx("div", {
|
|
125
131
|
"aria-hidden": true,
|
|
126
132
|
className: "mt-2 h-7"
|
|
@@ -312,7 +312,7 @@ function ChatProvider({ adapter, currentUser, navigation, activeConversationId =
|
|
|
312
312
|
state.currentConversation,
|
|
313
313
|
navigation
|
|
314
314
|
]);
|
|
315
|
-
const streamReply = useCallback(async (targetConversationId, request, parentId, onFailure, routePart) => {
|
|
315
|
+
const streamReply = useCallback(async (targetConversationId, request, parentId, onFailure, routePart, onAccepted) => {
|
|
316
316
|
backgroundStreamsRef.current.set(targetConversationId, {
|
|
317
317
|
content: "",
|
|
318
318
|
reasoning: "",
|
|
@@ -335,7 +335,10 @@ function ChatProvider({ adapter, currentUser, navigation, activeConversationId =
|
|
|
335
335
|
let thinkingStart = null;
|
|
336
336
|
let thinkingEnd = null;
|
|
337
337
|
const markStreamStart = () => {
|
|
338
|
-
if (thinkingStart === null)
|
|
338
|
+
if (thinkingStart === null) {
|
|
339
|
+
thinkingStart = Date.now();
|
|
340
|
+
onAccepted?.();
|
|
341
|
+
}
|
|
339
342
|
};
|
|
340
343
|
let liveParts = [];
|
|
341
344
|
let liveText = "";
|
|
@@ -637,7 +640,8 @@ function ChatProvider({ adapter, currentUser, navigation, activeConversationId =
|
|
|
637
640
|
isSendingRef.current = true;
|
|
638
641
|
const targetConversationId = conversation.id;
|
|
639
642
|
const t = resolvedConfig.strings.agentTranscript;
|
|
640
|
-
|
|
643
|
+
const askedParts = paused.parts ?? [];
|
|
644
|
+
let pausedParts = askedParts;
|
|
641
645
|
const updatePaused = (updates) => dispatch({
|
|
642
646
|
type: "UPDATE_MESSAGE",
|
|
643
647
|
messageId: paused.id,
|
|
@@ -648,6 +652,7 @@ function ChatProvider({ adapter, currentUser, navigation, activeConversationId =
|
|
|
648
652
|
pausedParts = applyPartDelta(pausedParts, delta);
|
|
649
653
|
updatePaused({ parts: pausedParts });
|
|
650
654
|
};
|
|
655
|
+
if (paused.error) updatePaused({ error: null });
|
|
651
656
|
applyToPaused({
|
|
652
657
|
type: "approval_resolved",
|
|
653
658
|
id: approvalId,
|
|
@@ -677,8 +682,9 @@ function ChatProvider({ adapter, currentUser, navigation, activeConversationId =
|
|
|
677
682
|
}
|
|
678
683
|
streamingToConversationRef.current = targetConversationId;
|
|
679
684
|
startStreaming(targetConversationId);
|
|
685
|
+
let accepted = false;
|
|
680
686
|
try {
|
|
681
|
-
await streamReply(targetConversationId, {
|
|
687
|
+
if (await streamReply(targetConversationId, {
|
|
682
688
|
model: modelString,
|
|
683
689
|
messages: historyMessages(conversation.messages.slice(0, pausedAt + 1), state.selectedProvider),
|
|
684
690
|
conversationId: targetConversationId,
|
|
@@ -698,7 +704,12 @@ function ChatProvider({ adapter, currentUser, navigation, activeConversationId =
|
|
|
698
704
|
if (delta.type !== "tool_end" || delta.id !== approvalId) return false;
|
|
699
705
|
applyToPaused(delta);
|
|
700
706
|
return true;
|
|
701
|
-
})
|
|
707
|
+
}, () => {
|
|
708
|
+
accepted = true;
|
|
709
|
+
}) === "failed" && !accepted) {
|
|
710
|
+
pausedParts = askedParts;
|
|
711
|
+
updatePaused({ parts: askedParts });
|
|
712
|
+
}
|
|
702
713
|
} finally {
|
|
703
714
|
isSendingRef.current = false;
|
|
704
715
|
}
|
|
@@ -48,11 +48,16 @@ export interface CopyCodeBlockProps {
|
|
|
48
48
|
* surface reads on a panel and a tinted callout alike, and the buttons stay right after the
|
|
49
49
|
* value, always shown. The copy confirms with its check alone, as the change shows there. */
|
|
50
50
|
inline?: boolean | undefined;
|
|
51
|
+
/** False also says "Copied" in a toast. By default the copy confirms with the button's
|
|
52
|
+
* check alone, which stays in view until it turns back, as the button is where the reader
|
|
53
|
+
* pressed; a failure toasts either way. */
|
|
54
|
+
quiet?: boolean | undefined;
|
|
51
55
|
className?: string | undefined;
|
|
52
56
|
}
|
|
53
57
|
/** A block of code, such as a command to run, with a button that copies it. The
|
|
54
58
|
* buttons show while the pointer is over the block or focus is in it, and always
|
|
55
|
-
* where there is no hover to reveal them.
|
|
59
|
+
* where there is no hover to reveal them. A copy confirms with the button's check,
|
|
60
|
+
* not a toast; a failure still toasts. With `inline` it is a compact value in the line
|
|
56
61
|
* instead, such as a token shown once, its buttons always beside it. With `secret` it
|
|
57
62
|
* masks the value until the eye shows it, and copies it either way. */
|
|
58
|
-
export declare function CopyCodeBlock({ text, label, children, secret, revealed: revealedProp, defaultRevealed, onRevealedChange, inline, wrap, className, }: CopyCodeBlockProps): import("react").JSX.Element;
|
|
63
|
+
export declare function CopyCodeBlock({ text, label, children, secret, revealed: revealedProp, defaultRevealed, onRevealedChange, inline, wrap, quiet, className, }: CopyCodeBlockProps): import("react").JSX.Element;
|
|
@@ -4,7 +4,8 @@ import { CheckIcon, CopyIcon, EyeIcon, EyeOffIcon } from "../icons.js";
|
|
|
4
4
|
import { useStrings } from "./Provider.js";
|
|
5
5
|
import { useCopy } from "./useCopy.js";
|
|
6
6
|
import { IconButton } from "./IconButton.js";
|
|
7
|
-
import {
|
|
7
|
+
import { fadeMask, useHiddenEnds } from "./scrollFade.js";
|
|
8
|
+
import { useState } from "react";
|
|
8
9
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
10
|
//#region src/components/CopyButton.tsx
|
|
10
11
|
/** The one copy control: a button that copies its text, turns into a check for a
|
|
@@ -23,6 +24,7 @@ function CopyButton({ text, label, quiet = false, showLabel = false, variant = s
|
|
|
23
24
|
if (showLabel) return /* @__PURE__ */ jsxs("button", {
|
|
24
25
|
type: "button",
|
|
25
26
|
...props,
|
|
27
|
+
"data-copied": copied || void 0,
|
|
26
28
|
"aria-label": props["aria-label"] ?? name,
|
|
27
29
|
onClick,
|
|
28
30
|
className: cx(buttonClasses({
|
|
@@ -33,6 +35,7 @@ function CopyButton({ text, label, quiet = false, showLabel = false, variant = s
|
|
|
33
35
|
});
|
|
34
36
|
return /* @__PURE__ */ jsx(IconButton, {
|
|
35
37
|
...props,
|
|
38
|
+
"data-copied": copied || void 0,
|
|
36
39
|
variant,
|
|
37
40
|
size,
|
|
38
41
|
label: name,
|
|
@@ -40,60 +43,14 @@ function CopyButton({ text, label, quiet = false, showLabel = false, variant = s
|
|
|
40
43
|
onClick
|
|
41
44
|
});
|
|
42
45
|
}
|
|
43
|
-
var NO_ENDS = {
|
|
44
|
-
start: false,
|
|
45
|
-
end: false
|
|
46
|
-
};
|
|
47
|
-
function useHiddenEnds(active) {
|
|
48
|
-
const ref = useRef(null);
|
|
49
|
-
const [ends, setEnds] = useState(NO_ENDS);
|
|
50
|
-
const measureNow = () => {
|
|
51
|
-
const el = ref.current;
|
|
52
|
-
if (!el || !active) {
|
|
53
|
-
setEnds(NO_ENDS);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const scrolled = Math.abs(el.scrollLeft);
|
|
57
|
-
const start = scrolled > 1;
|
|
58
|
-
const end = scrolled + el.clientWidth < el.scrollWidth - 1;
|
|
59
|
-
setEnds((was) => was.start === start && was.end === end ? was : {
|
|
60
|
-
start,
|
|
61
|
-
end
|
|
62
|
-
});
|
|
63
|
-
};
|
|
64
|
-
const measure = useEffectEvent(measureNow);
|
|
65
|
-
useLayoutEffect(() => {
|
|
66
|
-
measure();
|
|
67
|
-
});
|
|
68
|
-
useLayoutEffect(() => {
|
|
69
|
-
const el = ref.current;
|
|
70
|
-
if (!el || !active || typeof ResizeObserver === "undefined") return;
|
|
71
|
-
const observer = new ResizeObserver(() => measure());
|
|
72
|
-
observer.observe(el);
|
|
73
|
-
return () => observer.disconnect();
|
|
74
|
-
}, [active]);
|
|
75
|
-
return {
|
|
76
|
-
ref,
|
|
77
|
-
ends,
|
|
78
|
-
onScroll: measureNow
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
var FADE = "1.25rem";
|
|
82
|
-
function fadeMask({ start, end }) {
|
|
83
|
-
if (!start && !end) return;
|
|
84
|
-
const image = `linear-gradient(to var(--fade-end, right), ${start ? `transparent, #000 ${FADE}` : "#000"}, ${end ? `#000 calc(100% - ${FADE}), transparent` : "#000"})`;
|
|
85
|
-
return {
|
|
86
|
-
maskImage: image,
|
|
87
|
-
WebkitMaskImage: image
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
46
|
var MASK = "•".repeat(24);
|
|
91
47
|
/** A block of code, such as a command to run, with a button that copies it. The
|
|
92
48
|
* buttons show while the pointer is over the block or focus is in it, and always
|
|
93
|
-
* where there is no hover to reveal them.
|
|
49
|
+
* where there is no hover to reveal them. A copy confirms with the button's check,
|
|
50
|
+
* not a toast; a failure still toasts. With `inline` it is a compact value in the line
|
|
94
51
|
* instead, such as a token shown once, its buttons always beside it. With `secret` it
|
|
95
52
|
* masks the value until the eye shows it, and copies it either way. */
|
|
96
|
-
function CopyCodeBlock({ text, label, children, secret = false, revealed: revealedProp, defaultRevealed = false, onRevealedChange, inline = false, wrap = false, className }) {
|
|
53
|
+
function CopyCodeBlock({ text, label, children, secret = false, revealed: revealedProp, defaultRevealed = false, onRevealedChange, inline = false, wrap = false, quiet = true, className }) {
|
|
97
54
|
const { common: t } = useStrings();
|
|
98
55
|
const [ownRevealed, setOwnRevealed] = useState(defaultRevealed);
|
|
99
56
|
const revealed = revealedProp ?? ownRevealed;
|
|
@@ -102,6 +59,7 @@ function CopyCodeBlock({ text, label, children, secret = false, revealed: reveal
|
|
|
102
59
|
onRevealedChange?.(next);
|
|
103
60
|
};
|
|
104
61
|
const reveal = "opacity-0 group-hover/code:opacity-100 group-focus-within/code:opacity-100 pointer-coarse:opacity-100";
|
|
62
|
+
const revealCopy = `${reveal} data-copied:opacity-100`;
|
|
105
63
|
let shown = children ?? text;
|
|
106
64
|
if (secret && !revealed && children === void 0) shown = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
107
65
|
"aria-hidden": "true",
|
|
@@ -140,7 +98,7 @@ function CopyCodeBlock({ text, label, children, secret = false, revealed: reveal
|
|
|
140
98
|
text,
|
|
141
99
|
label,
|
|
142
100
|
size: "xs",
|
|
143
|
-
quiet
|
|
101
|
+
quiet
|
|
144
102
|
}), revealButton]
|
|
145
103
|
})]
|
|
146
104
|
});
|
|
@@ -154,7 +112,8 @@ function CopyCodeBlock({ text, label, children, secret = false, revealed: reveal
|
|
|
154
112
|
children: [revealButton, /* @__PURE__ */ jsx(CopyButton, {
|
|
155
113
|
text,
|
|
156
114
|
label,
|
|
157
|
-
|
|
115
|
+
quiet,
|
|
116
|
+
className: cx("bg-(--theme-muted-panel-bg)", revealCopy)
|
|
158
117
|
})]
|
|
159
118
|
})]
|
|
160
119
|
});
|
|
@@ -35,8 +35,15 @@ export type PageHeaderActionProps = Target & {
|
|
|
35
35
|
disabledReason?: string | undefined;
|
|
36
36
|
/** A hint shown on hover and focus while it can be pressed. */
|
|
37
37
|
tooltip?: string | undefined;
|
|
38
|
-
/**
|
|
39
|
-
*
|
|
38
|
+
/** How a labeled action reads in the bar: `outline`, the page's small
|
|
39
|
+
* secondary button, as "+ Add" is; or `link`, text in the link color, for a
|
|
40
|
+
* page that keeps the older look. On a narrow bar the label folds into the
|
|
41
|
+
* tooltip and the outline stays, so the action is an outlined icon. An
|
|
42
|
+
* `iconOnly` action, such as a pencil beside the title, is always bare. */
|
|
43
|
+
variant?: 'outline' | 'link' | undefined;
|
|
44
|
+
/** For an action that removes or breaks something: in the error color where
|
|
45
|
+
* it is a menu entry, in the header's "…" menu. In the bar it reads as the
|
|
46
|
+
* others do; a page's destructive actions belong in its menu. */
|
|
40
47
|
destructive?: boolean | undefined;
|
|
41
48
|
/** A toggle's state, such as a favorite: set, it reads as pressed. Buttons only. */
|
|
42
49
|
pressed?: boolean | undefined;
|
|
@@ -47,10 +54,10 @@ export type PageHeaderActionProps = Target & {
|
|
|
47
54
|
/**
|
|
48
55
|
* An action in the page header: a link (`href`, `external` for a new tab,
|
|
49
56
|
* `reloadDocument` for a path the router doesn't serve), a
|
|
50
|
-
* button (`onClick`) or a menu (`menu`).
|
|
51
|
-
*
|
|
57
|
+
* button (`onClick`) or a menu (`menu`). A labeled action is a small outline
|
|
58
|
+
* button, as "+ Add" is, so the page's commands stand apart from the trail.
|
|
52
59
|
*/
|
|
53
|
-
export declare function PageHeaderAction({ icon: Icon, label, ariaLabel, iconOnly, disabled, disabledReason, tooltip, destructive, pressed, loading, ...target }: PageHeaderActionProps): import("react").JSX.Element;
|
|
60
|
+
export declare function PageHeaderAction({ icon: Icon, label, ariaLabel, iconOnly, variant, disabled, disabledReason, tooltip, destructive, pressed, loading, ...target }: PageHeaderActionProps): import("react").JSX.Element;
|
|
54
61
|
export type PageHeaderAddButtonProps = Target & {
|
|
55
62
|
/** The full action, such as "New cluster": the tooltip and accessible name. */
|
|
56
63
|
label: string;
|
|
@@ -55,11 +55,16 @@ function HeaderControl({ name, tooltip, disabled, disabledReason, loading = fals
|
|
|
55
55
|
/**
|
|
56
56
|
* An action in the page header: a link (`href`, `external` for a new tab,
|
|
57
57
|
* `reloadDocument` for a path the router doesn't serve), a
|
|
58
|
-
* button (`onClick`) or a menu (`menu`).
|
|
59
|
-
*
|
|
58
|
+
* button (`onClick`) or a menu (`menu`). A labeled action is a small outline
|
|
59
|
+
* button, as "+ Add" is, so the page's commands stand apart from the trail.
|
|
60
60
|
*/
|
|
61
|
-
function PageHeaderAction({ icon: Icon, label, ariaLabel, iconOnly = false, disabled = false, disabledReason, tooltip, destructive = false, pressed, loading = false, ...target }) {
|
|
62
|
-
const
|
|
61
|
+
function PageHeaderAction({ icon: Icon, label, ariaLabel, iconOnly = false, variant = "outline", disabled = false, disabledReason, tooltip, destructive = false, pressed, loading = false, ...target }) {
|
|
62
|
+
const link = variant === "link" && !iconOnly;
|
|
63
|
+
const className = cx(buttonClasses({
|
|
64
|
+
variant: link || iconOnly ? "ghost" : "outline",
|
|
65
|
+
size: "xs",
|
|
66
|
+
iconOnly
|
|
67
|
+
}), link && "*:text-(--theme-link)");
|
|
63
68
|
const icon = loading ? /* @__PURE__ */ jsx(Loader, {
|
|
64
69
|
inline: true,
|
|
65
70
|
decorative: true,
|
|
@@ -83,11 +88,7 @@ function PageHeaderAction({ icon: Icon, label, ariaLabel, iconOnly = false, disa
|
|
|
83
88
|
disabledReason,
|
|
84
89
|
loading,
|
|
85
90
|
pressed,
|
|
86
|
-
className
|
|
87
|
-
variant: "ghost",
|
|
88
|
-
size: "xs",
|
|
89
|
-
iconOnly
|
|
90
|
-
}), tone),
|
|
91
|
+
className,
|
|
91
92
|
children: [icon, !iconOnly && /* @__PURE__ */ jsx("span", {
|
|
92
93
|
className: "@max-md:sr-only",
|
|
93
94
|
children: label
|
|
@@ -23,8 +23,10 @@ export interface PaginationProps {
|
|
|
23
23
|
/** Numbers the pages between previous and next, for a list a reader jumps
|
|
24
24
|
* around in, such as a catalog: the first and last, and a window around
|
|
25
25
|
* the current one, with gaps between. `max` is how many steps show at
|
|
26
|
-
* most, gaps included (7 by default, 5 at least). Needs a `total
|
|
27
|
-
* on a phone
|
|
26
|
+
* most, gaps included (7 by default, 5 at least). Needs a `total`. Where
|
|
27
|
+
* the bar is narrow, as on a phone, the window narrows to the first, the
|
|
28
|
+
* current and the last page (5 steps), and where even those won't fit, to
|
|
29
|
+
* the current page and the count: "5 / 12". */
|
|
28
30
|
pageNumbers?: boolean | {
|
|
29
31
|
max?: number | undefined;
|
|
30
32
|
} | undefined;
|
|
@@ -6,7 +6,7 @@ import { AppLink } from "./AppLink.js";
|
|
|
6
6
|
import { IconButton } from "./IconButton.js";
|
|
7
7
|
import { formatInteger } from "../format.js";
|
|
8
8
|
import { Select } from "./Select.js";
|
|
9
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
//#region src/components/Pagination.tsx
|
|
11
11
|
/** Page sizes a list offers; the first is the default. */
|
|
12
12
|
var PAGE_SIZES = [
|
|
@@ -45,6 +45,7 @@ function pageSteps(page, count, max = 7) {
|
|
|
45
45
|
* nothing when everything fits on one page. */
|
|
46
46
|
function Pagination({ page, pageSize, total, hasNext, onPageChange, pageHref, pageSizes = PAGE_SIZES, onPageSizeChange, summary, pageNumbers = false, className }) {
|
|
47
47
|
const t = useStrings().list;
|
|
48
|
+
const locale = useLocale();
|
|
48
49
|
const start = page * pageSize + 1;
|
|
49
50
|
const pageEnd = (page + 1) * pageSize;
|
|
50
51
|
const known = total !== void 0;
|
|
@@ -59,7 +60,7 @@ function Pagination({ page, pageSize, total, hasNext, onPageChange, pageHref, pa
|
|
|
59
60
|
const position = summary ?? (known ? t.pager(start, end, pastCap ? end : total, pastCap) : null);
|
|
60
61
|
return /* @__PURE__ */ jsxs("nav", {
|
|
61
62
|
"aria-label": t.pagination,
|
|
62
|
-
className: cx("flex h-8 shrink-0 items-center justify-between gap-3 px-2.5", className),
|
|
63
|
+
className: cx("@container/pager flex h-8 shrink-0 items-center justify-between gap-3 px-2.5", className),
|
|
63
64
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
64
65
|
className: "flex min-w-0 flex-1 items-center gap-3 text-xs text-(--theme-muted-text-color)",
|
|
65
66
|
children: [position && /* @__PURE__ */ jsx("span", {
|
|
@@ -87,22 +88,39 @@ function Pagination({ page, pageSize, total, hasNext, onPageChange, pageHref, pa
|
|
|
87
88
|
onPageChange,
|
|
88
89
|
pageHref
|
|
89
90
|
}),
|
|
90
|
-
pageNumbers && pageCount !== void 0 && pageCount > 1 && /* @__PURE__ */
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
pageNumbers && pageCount !== void 0 && pageCount > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
92
|
+
/* @__PURE__ */ jsx(PageNumbers, {
|
|
93
|
+
steps: pageSteps(page, pageCount, typeof pageNumbers === "object" ? pageNumbers.max : void 0),
|
|
94
|
+
page,
|
|
95
|
+
className: "hidden @min-[34rem]/pager:flex",
|
|
96
|
+
onPageChange,
|
|
97
|
+
pageHref
|
|
98
|
+
}),
|
|
99
|
+
/* @__PURE__ */ jsx(PageNumbers, {
|
|
100
|
+
steps: pageSteps(page, pageCount, 5),
|
|
101
|
+
page,
|
|
102
|
+
className: "hidden @min-[19rem]/pager:flex @min-[34rem]/pager:hidden",
|
|
103
|
+
onPageChange,
|
|
104
|
+
pageHref
|
|
105
|
+
}),
|
|
106
|
+
/* @__PURE__ */ jsxs("span", {
|
|
107
|
+
className: "flex items-center px-1 text-xs whitespace-nowrap text-(--theme-muted-text-color) tabular-nums @min-[19rem]/pager:hidden",
|
|
108
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
109
|
+
className: "sr-only",
|
|
110
|
+
children: t.pageOfTotal(page + 1, pageCount)
|
|
111
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
95
112
|
"aria-hidden": "true",
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
113
|
+
children: [
|
|
114
|
+
/* @__PURE__ */ jsx("span", {
|
|
115
|
+
className: "text-(--theme-app)",
|
|
116
|
+
children: formatInteger(page + 1, locale)
|
|
117
|
+
}),
|
|
118
|
+
" / ",
|
|
119
|
+
formatInteger(pageCount, locale)
|
|
120
|
+
]
|
|
121
|
+
})]
|
|
122
|
+
})
|
|
123
|
+
] }),
|
|
106
124
|
/* @__PURE__ */ jsx(PageStep, {
|
|
107
125
|
to: page + 1,
|
|
108
126
|
enabled: hasMore,
|
|
@@ -115,6 +133,25 @@ function Pagination({ page, pageSize, total, hasNext, onPageChange, pageHref, pa
|
|
|
115
133
|
})]
|
|
116
134
|
});
|
|
117
135
|
}
|
|
136
|
+
function PageNumbers({ steps, page, className, onPageChange, pageHref }) {
|
|
137
|
+
const t = useStrings().list;
|
|
138
|
+
return /* @__PURE__ */ jsx("ol", {
|
|
139
|
+
className: cx("m-0 list-none items-center gap-1 p-0", className),
|
|
140
|
+
children: steps.map((step, i) => /* @__PURE__ */ jsx("li", {
|
|
141
|
+
className: "flex",
|
|
142
|
+
children: step === null ? /* @__PURE__ */ jsx(MoreIcon, {
|
|
143
|
+
"aria-hidden": "true",
|
|
144
|
+
className: "size-3.5 w-4 text-(--theme-muted-text-color)"
|
|
145
|
+
}) : /* @__PURE__ */ jsx(PageNumber, {
|
|
146
|
+
to: step,
|
|
147
|
+
current: step === page,
|
|
148
|
+
label: t.page(step + 1),
|
|
149
|
+
onPageChange,
|
|
150
|
+
pageHref
|
|
151
|
+
})
|
|
152
|
+
}, step ?? `gap-${i}`))
|
|
153
|
+
});
|
|
154
|
+
}
|
|
118
155
|
function PageStep({ to, enabled, label, icon, onPageChange, pageHref }) {
|
|
119
156
|
if (pageHref && enabled) return /* @__PURE__ */ jsx(IconButton, {
|
|
120
157
|
variant: "outline",
|
|
@@ -588,6 +588,8 @@ export interface UIStrings {
|
|
|
588
588
|
chart: {
|
|
589
589
|
/** The row under a chart's tooltip that sums its series. */
|
|
590
590
|
total: string;
|
|
591
|
+
/** The series that sums a chart's series past the eighth. */
|
|
592
|
+
other: string;
|
|
591
593
|
};
|
|
592
594
|
list: {
|
|
593
595
|
moreActions: string;
|
|
@@ -635,6 +637,8 @@ export interface UIStrings {
|
|
|
635
637
|
pagination: string;
|
|
636
638
|
/** A numbered page's name in `Pagination`, counting from 1. */
|
|
637
639
|
page: (page: number) => string;
|
|
640
|
+
/** Where the page sits, for `Pagination` too narrow for page numbers: it shows "5 / 12". */
|
|
641
|
+
pageOfTotal: (page: number, total: number) => string;
|
|
638
642
|
removeSearchTerm: (term: string) => string;
|
|
639
643
|
};
|
|
640
644
|
/** The chip row of a list's active filters, its Filter menu and its pickers. Field and option
|
|
@@ -580,7 +580,10 @@ var DEFAULTS = {
|
|
|
580
580
|
backspace: "Backspace",
|
|
581
581
|
delete: "Delete"
|
|
582
582
|
},
|
|
583
|
-
chart: {
|
|
583
|
+
chart: {
|
|
584
|
+
total: "Total",
|
|
585
|
+
other: "Other"
|
|
586
|
+
},
|
|
584
587
|
list: {
|
|
585
588
|
moreActions: "More actions",
|
|
586
589
|
labelName: "name",
|
|
@@ -619,6 +622,7 @@ var DEFAULTS = {
|
|
|
619
622
|
paginationNext: "Next",
|
|
620
623
|
pagination: "Pagination",
|
|
621
624
|
page: (page) => `Page ${page}`,
|
|
625
|
+
pageOfTotal: (page, total) => `Page ${page} of ${total}`,
|
|
622
626
|
removeSearchTerm: (term) => `Remove filter ${term}`
|
|
623
627
|
},
|
|
624
628
|
filters: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
|
-
import {
|
|
2
|
+
import { CancelFilledIcon, FailFilledIcon, NotRunningIcon, PendingRingIcon, RunningRingIcon, SkipIcon, SuccessFilledIcon, WarningFilledIcon } from "../icons.js";
|
|
3
3
|
import { toneIcon } from "./tone.js";
|
|
4
4
|
import { Tooltip } from "./Tooltip.js";
|
|
5
5
|
import { useStrings } from "./Provider.js";
|
|
@@ -8,33 +8,31 @@ import { jsx } from "react/jsx-runtime";
|
|
|
8
8
|
var looks = {
|
|
9
9
|
pending: {
|
|
10
10
|
tone: "muted",
|
|
11
|
-
icon:
|
|
11
|
+
icon: PendingRingIcon
|
|
12
12
|
},
|
|
13
13
|
started: {
|
|
14
14
|
tone: "info",
|
|
15
|
-
icon:
|
|
16
|
-
spin: true
|
|
15
|
+
icon: RunningRingIcon
|
|
17
16
|
},
|
|
18
17
|
running: {
|
|
19
18
|
tone: "info",
|
|
20
|
-
icon:
|
|
21
|
-
spin: true
|
|
19
|
+
icon: RunningRingIcon
|
|
22
20
|
},
|
|
23
21
|
completed: {
|
|
24
22
|
tone: "success",
|
|
25
|
-
icon:
|
|
23
|
+
icon: SuccessFilledIcon
|
|
26
24
|
},
|
|
27
25
|
warning: {
|
|
28
26
|
tone: "warning",
|
|
29
|
-
icon:
|
|
27
|
+
icon: WarningFilledIcon
|
|
30
28
|
},
|
|
31
29
|
failed: {
|
|
32
30
|
tone: "error",
|
|
33
|
-
icon:
|
|
31
|
+
icon: FailFilledIcon
|
|
34
32
|
},
|
|
35
33
|
error: {
|
|
36
34
|
tone: "error",
|
|
37
|
-
icon:
|
|
35
|
+
icon: FailFilledIcon
|
|
38
36
|
},
|
|
39
37
|
skipped: {
|
|
40
38
|
tone: "muted",
|
|
@@ -46,21 +44,20 @@ var looks = {
|
|
|
46
44
|
},
|
|
47
45
|
canceled: {
|
|
48
46
|
tone: "info",
|
|
49
|
-
icon:
|
|
47
|
+
icon: CancelFilledIcon
|
|
50
48
|
},
|
|
51
49
|
faulted: {
|
|
52
50
|
tone: "error",
|
|
53
|
-
icon:
|
|
54
|
-
spin: true
|
|
51
|
+
icon: RunningRingIcon
|
|
55
52
|
},
|
|
56
53
|
canceling: {
|
|
57
54
|
tone: "info",
|
|
58
|
-
icon:
|
|
59
|
-
spin: true
|
|
55
|
+
icon: RunningRingIcon
|
|
60
56
|
},
|
|
61
57
|
"": {
|
|
62
58
|
tone: "muted",
|
|
63
|
-
icon: NotRunningIcon
|
|
59
|
+
icon: NotRunningIcon,
|
|
60
|
+
faint: true
|
|
64
61
|
}
|
|
65
62
|
};
|
|
66
63
|
/** The tone a run status reads in, so a badge that says the status in words matches the
|
|
@@ -86,7 +83,7 @@ function RunStatusIcon({ status, label: ownLabel, tooltip = false, className, ..
|
|
|
86
83
|
className: cx("flex h-2 items-center", toneIcon[look.tone], className),
|
|
87
84
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
88
85
|
"aria-hidden": "true",
|
|
89
|
-
className: cx(look.
|
|
86
|
+
className: cx(look.faint && "opacity-55")
|
|
90
87
|
})
|
|
91
88
|
});
|
|
92
89
|
return tooltip && label ? /* @__PURE__ */ jsx(Tooltip, {
|
|
@@ -72,10 +72,19 @@ export declare function CountBadge({ count, max, variant, emphasis, size, label,
|
|
|
72
72
|
/** Any other span attribute and the ref reach its element, so it can be a Tooltip's or a popover's
|
|
73
73
|
* direct child. */
|
|
74
74
|
export interface StatusDotProps extends Omit<ComponentPropsWithRef<'span'>, 'children' | 'className'> {
|
|
75
|
+
/** The status's tone. */
|
|
75
76
|
variant?: BadgeVariant | undefined;
|
|
77
|
+
/** A ping that ripples out from the dot, for something under way; still for reduced motion. */
|
|
76
78
|
pulse?: boolean | undefined;
|
|
79
|
+
/** The dot's width and height in pixels, without its halo; 8 by default. */
|
|
77
80
|
size?: number | undefined;
|
|
81
|
+
/** Takes the text's color with no halo, for a dot on a solid fill, where the tone would
|
|
82
|
+
* vanish into it. */
|
|
83
|
+
current?: boolean | undefined;
|
|
78
84
|
className?: string | undefined;
|
|
79
85
|
}
|
|
80
|
-
|
|
86
|
+
/** A status's dot in its tone with a soft halo, for a status that stands beside its words
|
|
87
|
+
* without a badge, such as "running" in a list's cell. A badge's own dot is this one. Name
|
|
88
|
+
* the status in words beside it; the dot alone is only seen. */
|
|
89
|
+
export declare function StatusDot({ variant, pulse, size, current, className, style, ...rest }: StatusDotProps): import("react").JSX.Element;
|
|
81
90
|
export {};
|
|
@@ -33,6 +33,15 @@ var dotFill = {
|
|
|
33
33
|
...toneFill,
|
|
34
34
|
accent: "bg-(--theme-accent)"
|
|
35
35
|
};
|
|
36
|
+
var dotHalo = {
|
|
37
|
+
default: "ring-2 ring-(--theme-app)/22",
|
|
38
|
+
success: "ring-2 ring-(--theme-success)/22",
|
|
39
|
+
warning: "ring-2 ring-(--theme-warning)/22",
|
|
40
|
+
error: "ring-2 ring-(--theme-error)/22",
|
|
41
|
+
info: "ring-2 ring-(--theme-info)/22",
|
|
42
|
+
muted: "ring-2 ring-(--theme-muted-text-color)/22",
|
|
43
|
+
accent: "ring-2 ring-(--theme-accent)/22"
|
|
44
|
+
};
|
|
36
45
|
var sizeClasses = {
|
|
37
46
|
sm: "gap-1 py-px text-[10px] leading-3.5",
|
|
38
47
|
md: "gap-1.5 py-0.5 text-[11px] leading-4"
|
|
@@ -58,23 +67,21 @@ function statusTextClass(variant) {
|
|
|
58
67
|
}
|
|
59
68
|
/** A short status or label in a tone, such as "Running" or "Preview". */
|
|
60
69
|
function StatusBadge({ children, variant = "default", emphasis = "soft", shape = "pill", caps = false, truncate = false, pulse = false, dot = false, size = "sm", render, className, ref, ...rest }) {
|
|
61
|
-
const indicator = emphasis === "solid" ? "bg-current" : dotFill[variant];
|
|
62
70
|
return useRender({
|
|
63
71
|
render: render ?? /* @__PURE__ */ jsx("span", {}),
|
|
64
72
|
ref,
|
|
65
73
|
props: mergeProps(rest, {
|
|
66
74
|
className: cx("inline-flex items-center border font-medium no-underline", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--theme-focus)", sizeClasses[size], padClasses[shape][size], shapeClasses[shape], caps && "uppercase tracking-wider", truncate && "min-w-0 max-w-full", emphasisClasses[emphasis][variant], className),
|
|
67
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
] })
|
|
75
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [(pulse || dot) && /* @__PURE__ */ jsx(StatusDot, {
|
|
76
|
+
variant,
|
|
77
|
+
pulse,
|
|
78
|
+
size: pulse ? 8 : 6,
|
|
79
|
+
current: emphasis === "solid",
|
|
80
|
+
className: "shrink-0"
|
|
81
|
+
}), truncate ? /* @__PURE__ */ jsx("span", {
|
|
82
|
+
className: "min-w-0 truncate",
|
|
83
|
+
children
|
|
84
|
+
}) : children] })
|
|
78
85
|
})
|
|
79
86
|
});
|
|
80
87
|
}
|
|
@@ -99,8 +106,13 @@ function CountBadge({ count, max = 99, variant = "accent", emphasis = "solid", s
|
|
|
99
106
|
})]
|
|
100
107
|
});
|
|
101
108
|
}
|
|
102
|
-
|
|
109
|
+
/** A status's dot in its tone with a soft halo, for a status that stands beside its words
|
|
110
|
+
* without a badge, such as "running" in a list's cell. A badge's own dot is this one. Name
|
|
111
|
+
* the status in words beside it; the dot alone is only seen. */
|
|
112
|
+
function StatusDot({ variant = "default", pulse = false, size = 8, current = false, className, style, ...rest }) {
|
|
113
|
+
const fill = current ? "bg-current" : dotFill[variant];
|
|
103
114
|
return /* @__PURE__ */ jsxs("span", {
|
|
115
|
+
"aria-hidden": "true",
|
|
104
116
|
...rest,
|
|
105
117
|
className: cx("relative inline-flex", className),
|
|
106
118
|
style: {
|
|
@@ -108,8 +120,8 @@ function StatusDot({ variant = "default", pulse = false, size = 8, className, st
|
|
|
108
120
|
width: size,
|
|
109
121
|
height: size
|
|
110
122
|
},
|
|
111
|
-
children: [pulse && /* @__PURE__ */ jsx("span", { className: cx("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75 motion-reduce:hidden",
|
|
112
|
-
className: cx("relative inline-flex rounded-full",
|
|
123
|
+
children: [pulse && /* @__PURE__ */ jsx("span", { className: cx("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75 motion-reduce:hidden", fill) }), /* @__PURE__ */ jsx("span", {
|
|
124
|
+
className: cx("relative inline-flex rounded-full", fill, !current && dotHalo[variant]),
|
|
113
125
|
style: {
|
|
114
126
|
width: size,
|
|
115
127
|
height: size
|
|
@@ -2,6 +2,8 @@ interface IBaseCheckmarkProps {
|
|
|
2
2
|
className?: string;
|
|
3
3
|
colored?: boolean;
|
|
4
4
|
}
|
|
5
|
+
/** Something done: a solid disc in the success tone with a check cut out of it. */
|
|
5
6
|
export declare function SuccessCheckmark({ className, colored }: IBaseCheckmarkProps): import("react").JSX.Element;
|
|
7
|
+
/** Something failed: a solid disc in the error tone with an x cut out of it. */
|
|
6
8
|
export declare function ErrorCheckmark({ className, colored }: IBaseCheckmarkProps): import("react").JSX.Element;
|
|
7
9
|
export {};
|