@ciridae/ui 1.0.2 → 1.0.3
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/dist/app/auth.d.ts +10 -0
- package/dist/app/auth.js +60 -0
- package/dist/app/error-boundary.d.ts +18 -0
- package/dist/app/error-boundary.js +45 -0
- package/dist/app/feedback.d.ts +12 -0
- package/dist/app/feedback.js +128 -0
- package/dist/app/index.d.ts +8 -0
- package/dist/app/index.js +8 -0
- package/dist/app/request-errors.d.ts +63 -0
- package/dist/app/request-errors.js +172 -0
- package/dist/app/sentry.d.ts +18 -0
- package/dist/app/sentry.js +52 -0
- package/dist/app/telemetry.d.ts +7 -0
- package/dist/app/telemetry.js +28 -0
- package/dist/app/trail.d.ts +9 -0
- package/dist/app/trail.js +18 -0
- package/dist/chat/chat-query-keys.d.ts +3 -3
- package/dist/chat/contract.d.ts +15 -15
- package/dist/chat/conversation-items-query-options.d.ts +114 -114
- package/dist/chat/conversation-source.d.ts +75 -0
- package/dist/chat/conversation-source.js +1 -0
- package/dist/chat/message-text.d.ts +1 -1
- package/dist/chat/message-text.js +114 -46
- package/dist/chat/use-conversation-list.d.ts +6 -6
- package/dist/components/activity-feed.d.ts +2 -2
- package/dist/components/agent-work.d.ts +3 -3
- package/dist/components/app-frame.d.ts +79 -0
- package/dist/components/app-frame.js +92 -0
- package/dist/components/app-navigation.d.ts +3 -3
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/brand-icons.d.ts +3 -3
- package/dist/components/bulk-action-bar.d.ts +1 -1
- package/dist/components/button.d.ts +4 -4
- package/dist/components/calendar.d.ts +1 -1
- package/dist/components/centered-page.d.ts +1 -1
- package/dist/components/chart.d.ts +22 -2
- package/dist/components/chart.js +239 -44
- package/dist/components/chat-client-source.d.ts +16 -0
- package/dist/components/chat-client-source.js +256 -0
- package/dist/components/chat-page.d.ts +9 -8
- package/dist/components/chat-page.js +7 -11
- package/dist/components/chat-sheet-controller.d.ts +10 -14
- package/dist/components/chat-sheet-controller.js +2 -3
- package/dist/components/chat-sheet.d.ts +10 -11
- package/dist/components/chat-sheet.js +135 -273
- package/dist/components/chat.d.ts +22 -8
- package/dist/components/chat.js +128 -33
- package/dist/components/checkbox-item.d.ts +1 -1
- package/dist/components/checkbox-list.d.ts +1 -1
- package/dist/components/chip.d.ts +1 -1
- package/dist/components/collapsible.d.ts +1 -1
- package/dist/components/color-theme.d.ts +1 -1
- package/dist/components/column-settings.d.ts +1 -1
- package/dist/components/command-menu.d.ts +1 -1
- package/dist/components/command-palette.d.ts +3 -3
- package/dist/components/command-palette.js +1 -2
- package/dist/components/copyable-id.d.ts +22 -5
- package/dist/components/copyable-id.js +46 -6
- package/dist/components/data-display.d.ts +4 -4
- package/dist/components/data-grid.d.ts +10 -10
- package/dist/components/date-range-picker.d.ts +1 -1
- package/dist/components/date-time.d.ts +1 -1
- package/dist/components/dialog-height-context.d.ts +1 -1
- package/dist/components/document-viewer.d.ts +2 -2
- package/dist/components/drawer.d.ts +6 -6
- package/dist/components/dropzone.d.ts +4 -4
- package/dist/components/editable-field.d.ts +2 -2
- package/dist/components/email-input.d.ts +1 -1
- package/dist/components/environment-frame.d.ts +1 -1
- package/dist/components/expandable-text.d.ts +1 -1
- package/dist/components/feedback-screenshot.d.ts +1 -1
- package/dist/components/feedback-sheet.d.ts +18 -3
- package/dist/components/feedback-sheet.js +216 -55
- package/dist/components/field-context.d.ts +1 -1
- package/dist/components/filter-bar.d.ts +3 -3
- package/dist/components/filter-bar.js +4 -1
- package/dist/components/form-controls.d.ts +3 -3
- package/dist/components/form-layout.d.ts +7 -7
- package/dist/components/hotkeys.d.ts +2 -2
- package/dist/components/icon.d.ts +1 -1
- package/dist/components/input.d.ts +4 -4
- package/dist/components/input.js +3 -2
- package/dist/components/keyboard-shortcuts.d.ts +2 -2
- package/dist/components/keycap.d.ts +2 -2
- package/dist/components/layout.d.ts +13 -13
- package/dist/components/lightbox.d.ts +9 -3
- package/dist/components/lightbox.js +11 -8
- package/dist/components/link.d.ts +1 -1
- package/dist/components/locale.d.ts +32 -2
- package/dist/components/locale.js +25 -1
- package/dist/components/log-list.d.ts +3 -3
- package/dist/components/mention-input.d.ts +2 -2
- package/dist/components/menu.d.ts +2 -2
- package/dist/components/meter.d.ts +1 -1
- package/dist/components/navigation.d.ts +2 -2
- package/dist/components/notification-tray.d.ts +1 -1
- package/dist/components/overlays.d.ts +4 -4
- package/dist/components/page-frame.d.ts +4 -4
- package/dist/components/panel.d.ts +3 -3
- package/dist/components/profile-header.d.ts +1 -1
- package/dist/components/progress.d.ts +1 -1
- package/dist/components/project-avatar.d.ts +1 -1
- package/dist/components/radio-card.d.ts +2 -2
- package/dist/components/record.d.ts +4 -4
- package/dist/components/resolved-file-url.d.ts +19 -0
- package/dist/components/resolved-file-url.js +49 -0
- package/dist/components/scroll-area.d.ts +1 -1
- package/dist/components/secret-dialog.d.ts +1 -1
- package/dist/components/segmented-bar.d.ts +1 -1
- package/dist/components/select.d.ts +4 -4
- package/dist/components/settings.d.ts +4 -4
- package/dist/components/shell-skeleton.d.ts +1 -1
- package/dist/components/shell.d.ts +19 -19
- package/dist/components/shell.js +3 -1
- package/dist/components/side-panel.d.ts +3 -3
- package/dist/components/sidebar.d.ts +6 -6
- package/dist/components/site-banner.d.ts +1 -1
- package/dist/components/skeleton.d.ts +1 -1
- package/dist/components/skeletons.d.ts +5 -5
- package/dist/components/stages.d.ts +5 -5
- package/dist/components/stat-card.d.ts +3 -3
- package/dist/components/status-banner.d.ts +1 -1
- package/dist/components/status.d.ts +2 -2
- package/dist/components/steps.d.ts +1 -1
- package/dist/components/switch.d.ts +1 -1
- package/dist/components/tabs.d.ts +8 -8
- package/dist/components/theme-customizer.d.ts +2 -2
- package/dist/components/toast.d.ts +1 -1
- package/dist/components/use-new-version-available.d.ts +6 -0
- package/dist/components/use-new-version-available.js +29 -0
- package/dist/components/user-chip.d.ts +3 -3
- package/dist/components/wizard.d.ts +2 -2
- package/dist/feedback/contract.d.ts +136 -136
- package/dist/feedback/feedback-controller.d.ts +6 -6
- package/dist/feedback/generated/zod.gen.d.ts +28 -28
- package/dist/feedback/operator-contract.d.ts +11 -11
- package/dist/feedback/operator-controller.d.ts +32 -13
- package/dist/feedback/operator-controller.js +44 -11
- package/dist/styled-system/styles.css +203 -59
- package/package.json +32 -3
|
@@ -14,16 +14,16 @@ export type DrawerProps = Omit<ComponentProps<typeof BaseDrawer.Root>, "children
|
|
|
14
14
|
size?: "narrow" | "regular" | "large";
|
|
15
15
|
children: ReactNode;
|
|
16
16
|
};
|
|
17
|
-
export declare function Drawer({ title, icon: titleIcon, eyebrow, subtitle, titleAccessory, actions, size, children, ...rootProps }: DrawerProps): import("
|
|
17
|
+
export declare function Drawer({ title, icon: titleIcon, eyebrow, subtitle, titleAccessory, actions, size, children, ...rootProps }: DrawerProps): import("react").JSX.Element;
|
|
18
18
|
export declare function DrawerBody({ flush, className, ...props }: ComponentProps<"div"> & {
|
|
19
19
|
flush?: boolean;
|
|
20
|
-
}): import("
|
|
21
|
-
export declare function DrawerForm({ className, ...props }: ComponentProps<"form">): import("
|
|
20
|
+
}): import("react").JSX.Element;
|
|
21
|
+
export declare function DrawerForm({ className, ...props }: ComponentProps<"form">): import("react").JSX.Element;
|
|
22
22
|
export type DrawerFooterProps = ComponentProps<"footer"> & {
|
|
23
23
|
/** Muted text at the footer's leading edge saying what the actions do, before the actions. */
|
|
24
24
|
note?: ReactNode;
|
|
25
25
|
};
|
|
26
|
-
export declare function DrawerFooter({ note, className, children, ...props }: DrawerFooterProps): import("
|
|
26
|
+
export declare function DrawerFooter({ note, className, children, ...props }: DrawerFooterProps): import("react").JSX.Element;
|
|
27
27
|
export type DrawerFieldProps = {
|
|
28
28
|
label: ReactNode;
|
|
29
29
|
/** Muted after the label, such as `Optional` or where a value was filled in from. */
|
|
@@ -39,6 +39,6 @@ export type DrawerFieldProps = {
|
|
|
39
39
|
* A labelled form field. Its `<label>` points at the control inside, and its helper or error
|
|
40
40
|
* describes it: `Input`, `Textarea`, `Select` and `Combobox` take the ids from the field.
|
|
41
41
|
*/
|
|
42
|
-
export declare function DrawerField({ label, hint, helper, error, controlId, children }: DrawerFieldProps): import("
|
|
42
|
+
export declare function DrawerField({ label, hint, helper, error, controlId, children }: DrawerFieldProps): import("react").JSX.Element;
|
|
43
43
|
/** Two fields side by side. */
|
|
44
|
-
export declare function DrawerFieldRow({ className, ...props }: ComponentProps<"div">): import("
|
|
44
|
+
export declare function DrawerFieldRow({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
|
|
@@ -18,16 +18,16 @@ export type DropzoneProps = useRender.ComponentProps<"div"> & {
|
|
|
18
18
|
accept?: string;
|
|
19
19
|
};
|
|
20
20
|
/** Drop or paste files onto the element; while dragged over it carries `data-dragging`. */
|
|
21
|
-
export declare function Dropzone({ onFiles, dropLabel, disabled, accept, render, className, children, ...props }: DropzoneProps): import("
|
|
21
|
+
export declare function Dropzone({ onFiles, dropLabel, disabled, accept, render, className, children, ...props }: DropzoneProps): import("react").JSX.Element;
|
|
22
22
|
/** The enclosing `Dropzone`'s picker, for a trigger of the caller's own, such as an icon button. */
|
|
23
23
|
export declare function useDropzonePicker(): DropzoneContextValue;
|
|
24
24
|
export type DropzoneTriggerProps = Omit<ButtonProps, "onClick">;
|
|
25
25
|
/** Opens the enclosing `Dropzone`'s file picker; a paperclip unless it sets its own icon. */
|
|
26
|
-
export declare function DropzoneTrigger({ icon: triggerIcon, variant, disabled, ...props }: DropzoneTriggerProps): import("
|
|
26
|
+
export declare function DropzoneTrigger({ icon: triggerIcon, variant, disabled, ...props }: DropzoneTriggerProps): import("react").JSX.Element;
|
|
27
27
|
/** A dashed pad at rest inside a `Dropzone`: drop files on it, or browse for them. */
|
|
28
28
|
export declare function DropzonePad({ className }: {
|
|
29
29
|
className?: string;
|
|
30
|
-
}): import("
|
|
30
|
+
}): import("react").JSX.Element;
|
|
31
31
|
export type DropzoneFileListProps = {
|
|
32
32
|
files: readonly File[];
|
|
33
33
|
onRemove: (file: File) => void;
|
|
@@ -42,6 +42,6 @@ export type DropzoneFileListProps = {
|
|
|
42
42
|
className?: string;
|
|
43
43
|
};
|
|
44
44
|
/** One row per file: a thumbnail for an image, else its type's icon, its name and size, remove. */
|
|
45
|
-
export declare function DropzoneFileList({ files, onRemove, pending, disabled, variant, className, }: DropzoneFileListProps): import("
|
|
45
|
+
export declare function DropzoneFileList({ files, onRemove, pending, disabled, variant, className, }: DropzoneFileListProps): import("react").JSX.Element | null;
|
|
46
46
|
/** A file's type as an icon: PDF, archive, text, or a plain file. */
|
|
47
47
|
export declare function fileTypeIcon(file: Pick<File, "name" | "type">): ReactNode;
|
|
@@ -74,7 +74,7 @@ export type EditableFieldProps = Omit<ComponentProps<"div">, "children"> & {
|
|
|
74
74
|
edit?: FieldEdit;
|
|
75
75
|
labels?: Partial<CiridaeLabels["editableField"]>;
|
|
76
76
|
};
|
|
77
|
-
export declare function EditableField({ label, value, provenance, badge, note, action, edit, labels: ownLabels, className, ...props }: EditableFieldProps): import("
|
|
77
|
+
export declare function EditableField({ label, value, provenance, badge, note, action, edit, labels: ownLabels, className, ...props }: EditableFieldProps): import("react").JSX.Element;
|
|
78
78
|
export type ConflictingSource = {
|
|
79
79
|
id: string;
|
|
80
80
|
label: ReactNode;
|
|
@@ -96,4 +96,4 @@ export type SourceConflictProps = Omit<ComponentProps<"div">, "children"> & {
|
|
|
96
96
|
effectiveValue: string;
|
|
97
97
|
}>;
|
|
98
98
|
};
|
|
99
|
-
export declare function SourceConflict({ sources, effective, action, labels: ownLabels, className, ...props }: SourceConflictProps): import("
|
|
99
|
+
export declare function SourceConflict({ sources, effective, action, labels: ownLabels, className, ...props }: SourceConflictProps): import("react").JSX.Element;
|
|
@@ -9,4 +9,4 @@ export type EmailInputProps = Omit<InputProps, "value" | "defaultValue" | "onCha
|
|
|
9
9
|
invalidEmails?: readonly string[];
|
|
10
10
|
};
|
|
11
11
|
/** Comma, semicolon, whitespace, Enter, Tab, or blur commits addresses; duplicates are ignored. */
|
|
12
|
-
export declare function EmailInput({ value, onValueChange, inputValue, onInputValueChange, invalidEmails, disabled, className, onKeyDown, onBlur, onPaste, ...props }: EmailInputProps): import("
|
|
12
|
+
export declare function EmailInput({ value, onValueChange, inputValue, onInputValueChange, invalidEmails, disabled, className, onKeyDown, onBlur, onPaste, ...props }: EmailInputProps): import("react").JSX.Element;
|
|
@@ -10,4 +10,4 @@ export type EnvironmentFrameProps = ComponentProps<"div"> & {
|
|
|
10
10
|
tone?: SiteBannerProps["tone"];
|
|
11
11
|
};
|
|
12
12
|
/** A persistent environment strip and an inset AppShell viewport. */
|
|
13
|
-
export declare function EnvironmentFrame({ label, tone, children, className, ...props }: EnvironmentFrameProps): import("
|
|
13
|
+
export declare function EnvironmentFrame({ label, tone, children, className, ...props }: EnvironmentFrameProps): import("react").JSX.Element;
|
|
@@ -16,4 +16,4 @@ export declare function FeedbackScreenshot({ src, pointers, onPointersChange, di
|
|
|
16
16
|
pointers: readonly PlacedScreenshotPointer[];
|
|
17
17
|
onPointersChange: (pointers: readonly PlacedScreenshotPointer[]) => void;
|
|
18
18
|
disabled?: boolean;
|
|
19
|
-
}): import("
|
|
19
|
+
}): import("react").JSX.Element;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import { type FeedbackItem, type FeedbackSummary } from "../feedback/contract.js";
|
|
3
|
+
import { type FeedbackTriageStatus, type OperatorFeedbackItem, type OperatorFeedbackSummary } from "../feedback/operator-contract.js";
|
|
3
4
|
import { type FeedbackScreenshotPointer } from "./feedback-screenshot.js";
|
|
4
5
|
import { type CiridaeLabels } from "./locale.js";
|
|
5
6
|
export type FeedbackKind = "wrong" | "idea" | "question";
|
|
@@ -42,7 +43,7 @@ export type FeedbackFormProps = {
|
|
|
42
43
|
onClose: () => void;
|
|
43
44
|
};
|
|
44
45
|
/** The sheet's form, body and footer, for a `Drawer` that holds more than the form. */
|
|
45
|
-
export declare function FeedbackForm({ where: about, screenshot, onSubmit, errorDetail, onClose, }: FeedbackFormProps): import("
|
|
46
|
+
export declare function FeedbackForm({ where: about, screenshot, onSubmit, errorDetail, onClose, }: FeedbackFormProps): import("react").JSX.Element;
|
|
46
47
|
/** Something the caller loads for the sheet: in flight, failed with a way to try again, or here. */
|
|
47
48
|
export type FeedbackLoadState<Data> = {
|
|
48
49
|
status: "loading";
|
|
@@ -66,15 +67,29 @@ export type FeedbackSentProps = {
|
|
|
66
67
|
/** Resolves once `item` is withdrawn. */
|
|
67
68
|
onWithdraw: (feedbackId: string) => Promise<void>;
|
|
68
69
|
};
|
|
70
|
+
/** The All tab, for the people who answer feedback: everyone's items, triaged and replied to. */
|
|
71
|
+
export type FeedbackOperatorProps = {
|
|
72
|
+
/** Every reporter's feedback, in any order; the tab lists the latest change first. */
|
|
73
|
+
items: FeedbackLoadState<readonly OperatorFeedbackSummary[]>;
|
|
74
|
+
/** Loads the item into `item`; the sheet shows it once the ids match. */
|
|
75
|
+
onOpenItem: (feedbackId: string) => void;
|
|
76
|
+
item?: FeedbackLoadState<OperatorFeedbackItem>;
|
|
77
|
+
/** Resolves once `item` carries the reply; rejecting keeps the reply in the composer. */
|
|
78
|
+
onReply: (feedbackId: string, body: string) => Promise<void>;
|
|
79
|
+
/** Resolves once `item` carries the status; withdrawing stays the reporter's. */
|
|
80
|
+
onStatusChange: (feedbackId: string, status: FeedbackTriageStatus) => Promise<void>;
|
|
81
|
+
};
|
|
69
82
|
export type FeedbackSheetProps = Omit<FeedbackFormProps, "onClose"> & {
|
|
70
83
|
open: boolean;
|
|
71
84
|
onOpenChange: (open: boolean) => void;
|
|
72
85
|
/** Controls in the sheet's header, before the close button. */
|
|
73
86
|
actions?: ReactNode;
|
|
74
|
-
/** The
|
|
87
|
+
/** The person's own tickets; without it or `operator` the sheet is the form alone. */
|
|
75
88
|
sent?: FeedbackSentProps;
|
|
89
|
+
/** For an operator, the Tickets tab lists everyone's, with a filter for their own (`sent`). */
|
|
90
|
+
operator?: FeedbackOperatorProps;
|
|
76
91
|
/** Rewords the sheet's labels over the locale's. */
|
|
77
92
|
labels?: Partial<FeedbackLabels>;
|
|
78
93
|
};
|
|
79
94
|
/** The floating feedback sheet. Closing it drops the draft and the open item. */
|
|
80
|
-
export declare function FeedbackSheet({ labels, ...props }: FeedbackSheetProps): import("
|
|
95
|
+
export declare function FeedbackSheet({ labels, ...props }: FeedbackSheetProps): import("react").JSX.Element;
|
|
@@ -3,23 +3,28 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
* The feedback sheet. New: what it is about, where it was sent from, what happened, whether to
|
|
4
4
|
* attach a screenshot, with numbered pointers and notes on it, and files dropped on the form,
|
|
5
5
|
* picked or pasted; the form owns its validation and the sending, sent and failed states.
|
|
6
|
-
*
|
|
7
|
-
* thread they can reply in, with the item's actions, such as withdrawing it, behind its menu.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Tickets: everything the person filed, and each item's conversation with the Ciridae team as a
|
|
7
|
+
* thread they can reply in, with the item's actions, such as withdrawing it, behind its menu. For
|
|
8
|
+
* an operator it lists everyone's instead, filtered (their own among the filters) and searched,
|
|
9
|
+
* each item the same thread with a status to set. The sheet is presentational: the caller owns the requests behind
|
|
10
|
+
* `onSubmit`, `sent` and `operator`; `useFeedbackOperatorSheet` builds `operator` from the API.
|
|
10
11
|
*/
|
|
11
12
|
import { ArrowClockwiseIcon, ArrowLeftIcon, ChatCircleTextIcon, DotsThreeIcon, SquaresFourIcon, } from "@phosphor-icons/react";
|
|
12
13
|
import { createContext, useContext, useId, useState } from "react";
|
|
13
14
|
import { css, cx } from "#primitives/css";
|
|
14
|
-
import { feedbackAttachmentLimits, } from "../feedback/contract.js";
|
|
15
|
+
import { feedbackAttachmentLimits, feedbackStatusSchema, feedbackTypeSchema, } from "../feedback/contract.js";
|
|
16
|
+
import { feedbackTriageStatusSchema, } from "../feedback/operator-contract.js";
|
|
15
17
|
import { Button, IconButton } from "./button.js";
|
|
16
18
|
import { ChatComposer, ChatThread } from "./chat.js";
|
|
19
|
+
import { useCopy } from "./copyable-id.js";
|
|
17
20
|
import { DateTime } from "./date-time.js";
|
|
18
21
|
import { Drawer, DrawerBody, DrawerField, DrawerFooter, DrawerForm } from "./drawer.js";
|
|
19
22
|
import { Dropzone, DropzoneFileList, DropzonePad } from "./dropzone.js";
|
|
20
23
|
import { FeedbackScreenshot, } from "./feedback-screenshot.js";
|
|
24
|
+
import { FilterBar, matchesFilter } from "./filter-bar.js";
|
|
21
25
|
import { Checkbox, Textarea } from "./form-controls.js";
|
|
22
26
|
import { directional, icon, Spinner } from "./icon.js";
|
|
27
|
+
import { SearchInput } from "./input.js";
|
|
23
28
|
import { Eyebrow } from "./layout.js";
|
|
24
29
|
import { useCiridaeLocale } from "./locale.js";
|
|
25
30
|
import { Menu } from "./menu.js";
|
|
@@ -205,6 +210,11 @@ const sentEmpty = css({
|
|
|
205
210
|
textAlign: "center",
|
|
206
211
|
});
|
|
207
212
|
const padded = css({ padding: "panel.comfortable" });
|
|
213
|
+
const listToolbar = css({
|
|
214
|
+
paddingBlock: "panel.compact",
|
|
215
|
+
paddingInline: "panel.comfortable",
|
|
216
|
+
borderBottom: "1px solid token(colors.border.subtle)",
|
|
217
|
+
});
|
|
208
218
|
/* An item: its subject and status pinned over the thread, which scrolls; the reply below. */
|
|
209
219
|
const itemHeader = css({
|
|
210
220
|
position: "sticky",
|
|
@@ -347,25 +357,39 @@ function FailedNotice({ title, children, action, }) {
|
|
|
347
357
|
export function FeedbackSheet({ labels, ...props }) {
|
|
348
358
|
return (_jsx(FeedbackLabelsContext, { value: labels, children: _jsx(FeedbackSheetContent, { ...props }) }));
|
|
349
359
|
}
|
|
350
|
-
function FeedbackSheetContent({ open, onOpenChange, actions, sent, errorDetail, ...formProps }) {
|
|
360
|
+
function FeedbackSheetContent({ open, onOpenChange, actions, sent, operator, errorDetail, ...formProps }) {
|
|
351
361
|
const labels = useFeedbackLabels();
|
|
362
|
+
const operatorLabels = useCiridaeLocale().labels.feedbackOperator;
|
|
352
363
|
const [tab, setTab] = useState("new");
|
|
353
364
|
const [openItemId, setOpenItemId] = useState(null);
|
|
354
|
-
const
|
|
365
|
+
const showAll = tab === "tickets" && operator !== undefined;
|
|
366
|
+
const showSent = tab === "tickets" && !showAll && sent !== undefined;
|
|
355
367
|
const close = () => {
|
|
356
368
|
onOpenChange(false);
|
|
357
369
|
setOpenItemId(null);
|
|
358
370
|
};
|
|
359
|
-
|
|
371
|
+
const back = () => setOpenItemId(null);
|
|
372
|
+
return (_jsx(Drawer, { open: open, onOpenChange: (next) => (next ? onOpenChange(true) : close()), title: showAll ? operatorLabels.allTitle : showSent ? labels.sentTitle : labels.sendTitle, icon: _jsx(ChatCircleTextIcon, { className: icon({ size: "small" }), weight: "fill" }), actions: _jsxs(_Fragment, { children: [(sent || operator) && (_jsx(TabsRoot, { value: tab, onValueChange: (value) => {
|
|
360
373
|
setTab(value);
|
|
361
374
|
setOpenItemId(null);
|
|
362
|
-
}, children: _jsxs(TabsList, { variant: "contained", children: [_jsx(TabsTab, { value: "new", children: labels.newTab }), _jsx(TabsTab, { value: "
|
|
375
|
+
}, children: _jsxs(TabsList, { variant: "contained", children: [_jsx(TabsTab, { value: "new", children: labels.newTab }), _jsx(TabsTab, { value: "tickets", children: labels.ticketsTab })] }) })), actions] }), children: showAll ? (openItemId === null ? (_jsx(OperatorList, { operator: operator, ownIds: sent?.items.status === "loaded"
|
|
376
|
+
? new Set(sent.items.data.map((item) => item.id))
|
|
377
|
+
: undefined, errorDetail: errorDetail, onOpen: (feedbackId) => {
|
|
378
|
+
operator.onOpenItem(feedbackId);
|
|
379
|
+
setOpenItemId(feedbackId);
|
|
380
|
+
} })) : (_jsx(OperatorItem, { feedbackId: openItemId, operator: operator, errorDetail: errorDetail, onBack: back }, openItemId))) : !showSent ? (_jsx(FeedbackForm, { ...formProps, errorDetail: errorDetail, onClose: close })) : openItemId === null ? (_jsx(FeedbackList, { items: sent.items, rows: (items) => items.map((item) => ({
|
|
381
|
+
id: item.id,
|
|
382
|
+
subject: item.subject,
|
|
383
|
+
status: item.status,
|
|
384
|
+
meta: (_jsxs(_Fragment, { children: [typeLabel(item.type, labels), " \u00B7", " ", _jsx(DateTime, { value: item.updatedAt_epoch_milliseconds })] })),
|
|
385
|
+
})), empty: labels.noneSent, errorDetail: errorDetail, onOpen: (feedbackId) => {
|
|
363
386
|
sent.onOpenItem(feedbackId);
|
|
364
387
|
setOpenItemId(feedbackId);
|
|
365
|
-
} })) : (_jsx(SentItem, { feedbackId: openItemId, item: sent.item, errorDetail: errorDetail, onReply: sent.onReply, onWithdraw: sent.onWithdraw, onBack:
|
|
388
|
+
} })) : (_jsx(SentItem, { feedbackId: openItemId, item: sent.item, errorDetail: errorDetail, onReply: sent.onReply, onWithdraw: sent.onWithdraw, onBack: back }, openItemId)) }));
|
|
366
389
|
}
|
|
367
390
|
const placeholderWidths = ["58%", "42%", "50%"];
|
|
368
|
-
|
|
391
|
+
/** A list of feedback rows: loading, failed, empty, or each row's subject, meta and status. */
|
|
392
|
+
function FeedbackList({ items, rows, empty, toolbar, errorDetail, onOpen, }) {
|
|
369
393
|
const labels = useFeedbackLabels();
|
|
370
394
|
const { common } = useCiridaeLocale().labels;
|
|
371
395
|
if (items.status === "error") {
|
|
@@ -374,13 +398,68 @@ function SentList({ items, errorDetail, onOpen, }) {
|
|
|
374
398
|
if (items.status === "loading") {
|
|
375
399
|
return (_jsx(DrawerBody, { flush: true, children: _jsx("ul", { className: sentList, "aria-busy": "true", children: placeholderWidths.map((width) => (_jsxs("li", { className: sentRow, children: [_jsxs("span", { className: sentRowCopy, children: [_jsx(Skeleton, { shape: "text", style: { width } }), _jsx(Skeleton, { shape: "text", style: { width: "30%" } })] }), _jsx(Skeleton, { shape: "text", style: { width: 64 } })] }, width))) }) }));
|
|
376
400
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
401
|
+
const shown = rows(items.data);
|
|
402
|
+
return (_jsxs(DrawerBody, { flush: true, children: [toolbar, shown.length === 0 ? (_jsx("p", { className: sentEmpty, children: empty })) : (_jsx("ul", { className: sentList, children: shown.map((row) => {
|
|
403
|
+
const needsReply = row.status === "discussion_required";
|
|
404
|
+
const display = statusDisplay(row.status, labels);
|
|
405
|
+
return (_jsx("li", { children: _jsxs("button", { type: "button", className: sentRow, "data-needs-reply": needsReply ? "" : undefined, onClick: () => onOpen(row.id), children: [_jsxs("span", { className: sentRowCopy, children: [_jsxs("span", { className: sentRowSubject, children: [needsReply && _jsx("span", { className: needsReplyDot, "aria-hidden": "true" }), _jsx("span", { children: row.subject })] }), _jsx("span", { className: sentRowMeta, children: row.meta })] }), _jsx(Status, { variant: display.variant, children: display.label })] }) }, row.id));
|
|
406
|
+
}) }))] }));
|
|
407
|
+
}
|
|
408
|
+
/** Everyone's feedback, latest change first, narrowed by status, type and a search. */
|
|
409
|
+
function OperatorList({ operator, ownIds, errorDetail, onOpen, }) {
|
|
410
|
+
const labels = useFeedbackLabels();
|
|
411
|
+
const operatorLabels = useCiridaeLocale().labels.feedbackOperator;
|
|
412
|
+
const [filters, setFilters] = useState([]);
|
|
413
|
+
const [search, setSearch] = useState("");
|
|
414
|
+
const query = search.trim().toLowerCase();
|
|
415
|
+
const fields = [
|
|
416
|
+
...(ownIds
|
|
417
|
+
? [
|
|
418
|
+
{
|
|
419
|
+
key: "reporter",
|
|
420
|
+
label: operatorLabels.reporter,
|
|
421
|
+
kind: "multi",
|
|
422
|
+
options: [{ value: "mine", label: operatorLabels.mine }],
|
|
423
|
+
},
|
|
424
|
+
]
|
|
425
|
+
: []),
|
|
426
|
+
{
|
|
427
|
+
key: "status",
|
|
428
|
+
label: operatorLabels.status,
|
|
429
|
+
kind: "multi",
|
|
430
|
+
options: feedbackStatusSchema.options.map((value) => ({
|
|
431
|
+
value,
|
|
432
|
+
label: statusDisplay(value, labels).label,
|
|
433
|
+
})),
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
key: "type",
|
|
437
|
+
label: operatorLabels.type,
|
|
438
|
+
kind: "multi",
|
|
439
|
+
options: feedbackTypeSchema.options.map((value) => ({
|
|
440
|
+
value,
|
|
441
|
+
label: typeLabel(value, labels),
|
|
442
|
+
})),
|
|
443
|
+
},
|
|
444
|
+
];
|
|
445
|
+
return (_jsx(FeedbackList, { items: operator.items, rows: (items) => items
|
|
446
|
+
.filter((item) => filters.every((filter) => matchesFilter(filter.field === "reporter"
|
|
447
|
+
? ownIds?.has(item.id)
|
|
448
|
+
? "mine"
|
|
449
|
+
: "others"
|
|
450
|
+
: filter.field === "status"
|
|
451
|
+
? item.status
|
|
452
|
+
: item.type, filter)) &&
|
|
453
|
+
(query === "" ||
|
|
454
|
+
[item.subject, item.description, item.reporterEmail].some((value) => value?.toLowerCase().includes(query))))
|
|
455
|
+
// ISO timestamps in one format sort as text.
|
|
456
|
+
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))
|
|
457
|
+
.map((item) => ({
|
|
458
|
+
id: item.id,
|
|
459
|
+
subject: item.subject,
|
|
460
|
+
status: item.status,
|
|
461
|
+
meta: (_jsxs(_Fragment, { children: [item.reporterEmail ?? operatorLabels.reporter, " \u00B7 ", typeLabel(item.type, labels), " \u00B7", " ", _jsx(DateTime, { value: item.updatedAt })] })),
|
|
462
|
+
})), empty: filters.length > 0 || query !== "" ? operatorLabels.noMatches : operatorLabels.noneReceived, toolbar: _jsx(FilterBar, { className: listToolbar, fields: fields, value: filters, onValueChange: setFilters, children: _jsx(SearchInput, { variant: "ghost", "aria-label": operatorLabels.search, placeholder: operatorLabels.search, value: search, onValueCommit: setSearch }) }), errorDetail: errorDetail, onOpen: onOpen }));
|
|
384
463
|
}
|
|
385
464
|
/* What the thread shows in time order: the description, each status change, each message. */
|
|
386
465
|
function listThreadEntries(item, labels) {
|
|
@@ -413,37 +492,60 @@ function listThreadEntries(item, labels) {
|
|
|
413
492
|
];
|
|
414
493
|
return entries.sort((earlier, later) => earlier.at - later.at);
|
|
415
494
|
}
|
|
416
|
-
|
|
495
|
+
/**
|
|
496
|
+
* The operator's side of the same thread: the reporter's description and messages across from
|
|
497
|
+
* them, their own team's replies on their side.
|
|
498
|
+
*/
|
|
499
|
+
function listOperatorThreadEntries(item, reporter) {
|
|
500
|
+
const entries = [
|
|
501
|
+
{
|
|
502
|
+
kind: "message",
|
|
503
|
+
id: "description",
|
|
504
|
+
side: "assistant",
|
|
505
|
+
author: { name: reporter },
|
|
506
|
+
at: Date.parse(item.createdAt),
|
|
507
|
+
body: item.description,
|
|
508
|
+
attachments: item.attachments,
|
|
509
|
+
},
|
|
510
|
+
...item.messages.map((message) => {
|
|
511
|
+
const fromReporter = message.author.role === "reporter";
|
|
512
|
+
return {
|
|
513
|
+
kind: "message",
|
|
514
|
+
id: message.id,
|
|
515
|
+
side: fromReporter ? "assistant" : "user",
|
|
516
|
+
author: fromReporter ? { name: message.author.name } : undefined,
|
|
517
|
+
at: message.createdAt_epoch_milliseconds,
|
|
518
|
+
body: message.body,
|
|
519
|
+
};
|
|
520
|
+
}),
|
|
521
|
+
];
|
|
522
|
+
return entries.sort((earlier, later) => earlier.at - later.at);
|
|
523
|
+
}
|
|
524
|
+
/** Runs `action`, reporting it pending, then idle, or failed with its error. */
|
|
525
|
+
function usePendingAction(action) {
|
|
526
|
+
const [state, setState] = useState({ status: "idle" });
|
|
527
|
+
const run = async (input) => {
|
|
528
|
+
setState({ status: "pending" });
|
|
529
|
+
try {
|
|
530
|
+
await action(input);
|
|
531
|
+
setState({ status: "idle" });
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
setState({ status: "failed", error });
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
return { state, run: (input) => void run(input) };
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* One item as a thread: its header pinned over the conversation, then the failures and the reply
|
|
541
|
+
* below. Loading and failing to load keep the back button.
|
|
542
|
+
*/
|
|
543
|
+
function FeedbackThreadItem({ feedbackId, item, loaded, headerEnd, notices, replyDisabled, errorDetail, onReply, onBack, }) {
|
|
417
544
|
const labels = useFeedbackLabels();
|
|
418
545
|
const { common } = useCiridaeLocale().labels;
|
|
419
546
|
const [draft, setDraft] = useState("");
|
|
420
547
|
const [reply, setReply] = useState({ status: "idle" });
|
|
421
|
-
const [withdraw, setWithdraw] = useState({ status: "idle" });
|
|
422
|
-
// The item the caller last opened is this one only once its load carries this id.
|
|
423
|
-
const loaded = item?.status === "loaded" && item.data.id === feedbackId ? item.data : null;
|
|
424
|
-
const entries = loaded ? listThreadEntries(loaded, labels) : [];
|
|
425
|
-
if (reply.status === "sending") {
|
|
426
|
-
entries.push({
|
|
427
|
-
kind: "message",
|
|
428
|
-
id: "sending",
|
|
429
|
-
side: "user",
|
|
430
|
-
at: reply.at,
|
|
431
|
-
body: reply.body,
|
|
432
|
-
sending: true,
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
548
|
const back = (_jsx(IconButton, { "aria-label": labels.back, variant: "ghost", size: "small", onClick: onBack, children: _jsx(ArrowLeftIcon, { className: cx(icon({ size: "small" }), directional) }) }));
|
|
436
|
-
const actions = [
|
|
437
|
-
{ label: labels.copyId, onSelect: () => void navigator.clipboard.writeText(feedbackId) },
|
|
438
|
-
];
|
|
439
|
-
if (loaded?.canWithdraw) {
|
|
440
|
-
actions.push({
|
|
441
|
-
label: labels.withdraw,
|
|
442
|
-
variant: "danger",
|
|
443
|
-
disabled: reply.status === "sending",
|
|
444
|
-
onSelect: () => void sendWithdraw(),
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
549
|
const sendReply = async (body) => {
|
|
448
550
|
setReply({ status: "sending", body, at: Date.now() });
|
|
449
551
|
try {
|
|
@@ -455,21 +557,80 @@ function SentItem({ feedbackId, item, errorDetail, onReply, onWithdraw, onBack,
|
|
|
455
557
|
setReply({ status: "failed", error });
|
|
456
558
|
}
|
|
457
559
|
};
|
|
458
|
-
const sendWithdraw = async () => {
|
|
459
|
-
setWithdraw({ status: "pending" });
|
|
460
|
-
try {
|
|
461
|
-
await onWithdraw(feedbackId);
|
|
462
|
-
setWithdraw({ status: "idle" });
|
|
463
|
-
}
|
|
464
|
-
catch (error) {
|
|
465
|
-
setWithdraw({ status: "failed", error });
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
560
|
if (item?.status === "error") {
|
|
469
561
|
return (_jsxs(DrawerBody, { flush: true, children: [_jsx("div", { className: itemHeader, children: back }), _jsx("div", { className: padded, children: _jsx(FailedNotice, { title: labels.loadItemFailed, action: _jsx("button", { type: "button", onClick: item.retry, children: common.tryAgain }), children: errorDetail?.(item.error) }) })] }));
|
|
470
562
|
}
|
|
471
563
|
if (!loaded) {
|
|
472
564
|
return (_jsxs(DrawerBody, { flush: true, "aria-busy": "true", children: [_jsxs("div", { className: itemHeader, children: [back, _jsxs("span", { className: itemTitle, children: [_jsx(Skeleton, { shape: "text", style: { width: "60%" } }), _jsx(Skeleton, { shape: "text", style: { width: "30%" } })] })] }), _jsx("div", { className: padded, children: _jsxs("span", { className: placeholderLines, children: [_jsx(Skeleton, { shape: "text" }), _jsx(Skeleton, { shape: "text", style: { width: "80%" } }), _jsx(Skeleton, { shape: "text", style: { width: "50%" } })] }) })] }));
|
|
473
565
|
}
|
|
474
|
-
|
|
566
|
+
const entries = reply.status === "sending"
|
|
567
|
+
? [
|
|
568
|
+
...loaded.entries,
|
|
569
|
+
{
|
|
570
|
+
kind: "message",
|
|
571
|
+
id: "sending",
|
|
572
|
+
side: "user",
|
|
573
|
+
at: reply.at,
|
|
574
|
+
body: reply.body,
|
|
575
|
+
sending: true,
|
|
576
|
+
},
|
|
577
|
+
]
|
|
578
|
+
: loaded.entries;
|
|
579
|
+
return (_jsxs(_Fragment, { children: [_jsxs(DrawerBody, { flush: true, children: [_jsxs("div", { className: itemHeader, children: [back, _jsxs("span", { className: itemTitle, children: [_jsx("span", { className: itemSubject, children: loaded.subject }), _jsx("span", { className: sentRowMeta, children: loaded.meta })] }), headerEnd] }), _jsx(ChatThread, { entries: entries })] }), (loaded.canReply || reply.status === "failed" || notices) && (_jsxs("div", { className: itemFoot, children: [reply.status === "failed" && (_jsx(FailedNotice, { title: labels.replyFailed, children: errorDetail?.(reply.error) })), notices, loaded.canReply && (_jsx(ChatComposer, { variant: "sheet", placeholder: labels.reply, ariaLabel: labels.reply, submitLabel: labels.sendReply, value: draft, onValueChange: setDraft, sendDisabled: replyDisabled || reply.status === "sending", onSend: (body) => void sendReply(body.trim()) }))] }))] }));
|
|
580
|
+
}
|
|
581
|
+
function SentItem({ feedbackId, item, errorDetail, onReply, onWithdraw, onBack, }) {
|
|
582
|
+
const labels = useFeedbackLabels();
|
|
583
|
+
const withdraw = usePendingAction(() => onWithdraw(feedbackId));
|
|
584
|
+
const copyId = useCopy(feedbackId);
|
|
585
|
+
// The item the caller last opened is this one only once its load carries this id.
|
|
586
|
+
const loaded = item?.status === "loaded" && item.data.id === feedbackId ? item.data : null;
|
|
587
|
+
const actions = [{ label: labels.copyId, onSelect: copyId.copy }];
|
|
588
|
+
if (loaded?.canWithdraw) {
|
|
589
|
+
actions.push({
|
|
590
|
+
label: labels.withdraw,
|
|
591
|
+
variant: "danger",
|
|
592
|
+
onSelect: () => withdraw.run(undefined),
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
const display = loaded && statusDisplay(loaded.status, labels);
|
|
596
|
+
return (_jsx(FeedbackThreadItem, { feedbackId: feedbackId, item: item, loaded: loaded && {
|
|
597
|
+
subject: loaded.subject,
|
|
598
|
+
meta: (_jsxs(_Fragment, { children: [typeLabel(loaded.type, labels), " \u00B7", " ", _jsx(DateTime, { value: loaded.createdAt_epoch_milliseconds })] })),
|
|
599
|
+
entries: listThreadEntries(loaded, labels),
|
|
600
|
+
canReply: loaded.canReply,
|
|
601
|
+
}, headerEnd: _jsxs(_Fragment, { children: [display && (_jsx(Status, { className: itemStatus, variant: display.variant, children: display.label })), _jsx(Menu, { align: "end", items: actions, trigger: _jsx(IconButton, { "aria-label": labels.actions, variant: "ghost", size: "small", disabled: withdraw.state.status === "pending", children: withdraw.state.status === "pending" ? (_jsx(Spinner, { label: labels.withdrawing })) : (_jsx(DotsThreeIcon, { className: icon({ size: "small" }) })) }) })] }), notices: withdraw.state.status === "failed" && (_jsx(FailedNotice, { title: labels.withdrawFailed, children: errorDetail?.(withdraw.state.error) })), replyDisabled: withdraw.state.status === "pending", errorDetail: errorDetail, onReply: onReply, onBack: onBack }));
|
|
602
|
+
}
|
|
603
|
+
/** An item as its operator sees it: the reporter's thread, a status to set, and a reply. */
|
|
604
|
+
function OperatorItem({ feedbackId, operator, errorDetail, onBack, }) {
|
|
605
|
+
const labels = useFeedbackLabels();
|
|
606
|
+
const operatorLabels = useCiridaeLocale().labels.feedbackOperator;
|
|
607
|
+
const [pendingStatus, setPendingStatus] = useState(null);
|
|
608
|
+
const statusChange = usePendingAction(async (status) => {
|
|
609
|
+
setPendingStatus(status);
|
|
610
|
+
try {
|
|
611
|
+
await operator.onStatusChange(feedbackId, status);
|
|
612
|
+
}
|
|
613
|
+
finally {
|
|
614
|
+
setPendingStatus(null);
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
const { item } = operator;
|
|
618
|
+
const loaded = item?.status === "loaded" && item.data.id === feedbackId ? item.data : null;
|
|
619
|
+
const status = pendingStatus ?? loaded?.status;
|
|
620
|
+
const reporter = loaded?.reporterEmail ?? operatorLabels.reporter;
|
|
621
|
+
const statusOptions = [
|
|
622
|
+
// A withdrawn item is the reporter's call; it shows, but cannot be chosen.
|
|
623
|
+
...(status === "withdrawn" ? ["withdrawn"] : []),
|
|
624
|
+
...feedbackTriageStatusSchema.options,
|
|
625
|
+
].map((value) => ({ value, label: statusDisplay(value, labels).label }));
|
|
626
|
+
return (_jsx(FeedbackThreadItem, { feedbackId: feedbackId, item: item, loaded: loaded && {
|
|
627
|
+
subject: loaded.subject,
|
|
628
|
+
meta: (_jsxs(_Fragment, { children: [reporter, " \u00B7 ", typeLabel(loaded.type, labels), " \u00B7 ", _jsx(DateTime, { value: loaded.createdAt })] })),
|
|
629
|
+
entries: listOperatorThreadEntries(loaded, reporter),
|
|
630
|
+
canReply: loaded.canReply,
|
|
631
|
+
}, headerEnd: _jsx(Select, { label: operatorLabels.status, options: statusOptions, value: status, disabled: status === "withdrawn" || statusChange.state.status === "pending", onValueChange: (value) => {
|
|
632
|
+
const next = feedbackTriageStatusSchema.safeParse(value);
|
|
633
|
+
if (next.success && next.data !== status)
|
|
634
|
+
statusChange.run(next.data);
|
|
635
|
+
} }), notices: statusChange.state.status === "failed" && (_jsx(FailedNotice, { title: operatorLabels.statusFailed, children: errorDetail?.(statusChange.state.error) })), errorDetail: errorDetail, onReply: operator.onReply, onBack: onBack }));
|
|
475
636
|
}
|
|
@@ -8,7 +8,7 @@ export type FieldControl = {
|
|
|
8
8
|
descriptionId?: string;
|
|
9
9
|
invalid: boolean;
|
|
10
10
|
};
|
|
11
|
-
export declare const FieldControlContext: import("
|
|
11
|
+
export declare const FieldControlContext: import("react").Context<FieldControl | null>;
|
|
12
12
|
/** The ids and state a control takes from its field; its own props win. */
|
|
13
13
|
export declare function useFieldControl(own: {
|
|
14
14
|
id?: string;
|
|
@@ -49,7 +49,7 @@ export declare function FilterChip({ label, icon: fieldIcon, operator, values, o
|
|
|
49
49
|
values: ReactNode;
|
|
50
50
|
onEdit?: () => void;
|
|
51
51
|
onRemove: () => void;
|
|
52
|
-
}): import("
|
|
52
|
+
}): import("react").JSX.Element;
|
|
53
53
|
/**
|
|
54
54
|
* The set filters as chips, Clear all, and the Add filter builder: pick a field, then its operator
|
|
55
55
|
* and values, which apply as they are picked. Pressing a chip reopens the builder on its field.
|
|
@@ -63,7 +63,7 @@ export declare function FilterBar({ fields, value, defaultValue, onValueChange,
|
|
|
63
63
|
onValueChange?: (value: FilterValue[]) => void;
|
|
64
64
|
open?: boolean;
|
|
65
65
|
onOpenChange?: (open: boolean) => void;
|
|
66
|
-
}): import("
|
|
66
|
+
}): import("react").JSX.Element;
|
|
67
67
|
/**
|
|
68
68
|
* One field's operator and values, reporting every change so the list follows along. `onBack`
|
|
69
69
|
* adds the way back to the builder's field list; without it the field is fixed, as under a
|
|
@@ -74,4 +74,4 @@ export declare function FilterValueEditor({ field, value, onChange, onBack, }: {
|
|
|
74
74
|
value: FilterValue | undefined;
|
|
75
75
|
onChange: (filter: FilterValue) => void;
|
|
76
76
|
onBack?: () => void;
|
|
77
|
-
}): import("
|
|
77
|
+
}): import("react").JSX.Element;
|
|
@@ -158,14 +158,17 @@ const chips = css({
|
|
|
158
158
|
gap: "layout.controlGap",
|
|
159
159
|
minWidth: "0",
|
|
160
160
|
});
|
|
161
|
+
// One line: the buttons keep their size and the search after them takes what the row leaves.
|
|
161
162
|
const tools = css({
|
|
162
163
|
display: "flex",
|
|
164
|
+
flex: "1",
|
|
163
165
|
alignItems: "center",
|
|
164
166
|
justifyContent: "flex-end",
|
|
165
|
-
flexWrap: "wrap",
|
|
166
167
|
gap: "layout.controlGap",
|
|
167
168
|
minWidth: "0",
|
|
168
169
|
marginInlineStart: "auto",
|
|
170
|
+
"& > button": { flexShrink: "0" },
|
|
171
|
+
"& > :not(button)": { minWidth: "0" },
|
|
169
172
|
});
|
|
170
173
|
const builder = css({ display: "flex", flexDirection: "column", width: "300px", maxWidth: "100%" });
|
|
171
174
|
const fieldItem = css({
|
|
@@ -28,8 +28,8 @@ export declare function Checkbox({ children, className, wrapperClassName, ...pro
|
|
|
28
28
|
children?: ReactNode;
|
|
29
29
|
className?: string;
|
|
30
30
|
wrapperClassName?: string;
|
|
31
|
-
}): import("
|
|
32
|
-
export declare function Textarea({ className, ...props }: ComponentProps<"textarea">): import("
|
|
31
|
+
}): import("react").JSX.Element;
|
|
32
|
+
export declare function Textarea({ className, ...props }: ComponentProps<"textarea">): import("react").JSX.Element;
|
|
33
33
|
export declare const radioDot: string;
|
|
34
34
|
export type RadioGroupProps = Omit<ComponentProps<typeof BaseRadioGroup>, "children" | "defaultValue" | "name" | "onValueChange" | "value"> & {
|
|
35
35
|
name: string;
|
|
@@ -44,7 +44,7 @@ export type RadioGroupProps = Omit<ComponentProps<typeof BaseRadioGroup>, "child
|
|
|
44
44
|
value?: string;
|
|
45
45
|
onValueChange?: (value: string) => void;
|
|
46
46
|
};
|
|
47
|
-
export declare function RadioGroup({ name, options, label: groupLabel, defaultValue, value, onValueChange, layout, className, ...props }: RadioGroupProps): import("
|
|
47
|
+
export declare function RadioGroup({ name, options, label: groupLabel, defaultValue, value, onValueChange, layout, className, ...props }: RadioGroupProps): import("react").JSX.Element;
|
|
48
48
|
type SelectOption = {
|
|
49
49
|
label: string;
|
|
50
50
|
value: string;
|
|
@@ -4,8 +4,8 @@ import type { ComponentProps, ReactNode } from "react";
|
|
|
4
4
|
* Stacked form fields. Validation is the form library's, never the browser's, so this always
|
|
5
5
|
* carries `noValidate`.
|
|
6
6
|
*/
|
|
7
|
-
export declare function Form({ className, ...props }: ComponentProps<"form">): import("
|
|
8
|
-
export declare function FormActions({ className, ...props }: ComponentProps<"div">): import("
|
|
7
|
+
export declare function Form({ className, ...props }: ComponentProps<"form">): import("react").JSX.Element;
|
|
8
|
+
export declare function FormActions({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
|
|
9
9
|
/**
|
|
10
10
|
* A label round a `FieldLabel`, its control and any `FieldError`: the label names the control it
|
|
11
11
|
* holds, and while `invalid` the control is marked and described by the error, so `Input`,
|
|
@@ -13,20 +13,20 @@ export declare function FormActions({ className, ...props }: ComponentProps<"div
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function Field({ className, htmlFor, invalid, ...props }: ComponentProps<"label"> & {
|
|
15
15
|
invalid?: boolean;
|
|
16
|
-
}): import("
|
|
16
|
+
}): import("react").JSX.Element;
|
|
17
17
|
export declare function FieldLabel({ children }: {
|
|
18
18
|
children: ReactNode;
|
|
19
|
-
}): import("
|
|
19
|
+
}): import("react").JSX.Element;
|
|
20
20
|
/** The field's error; its id is the one the field's control is described by. */
|
|
21
21
|
export declare function FieldError({ id, children }: {
|
|
22
22
|
id?: string;
|
|
23
23
|
children: ReactNode;
|
|
24
|
-
}): import("
|
|
25
|
-
export declare function SegmentedControl({ className, ...props }: ComponentProps<"div">): import("
|
|
24
|
+
}): import("react").JSX.Element;
|
|
25
|
+
export declare function SegmentedControl({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
|
|
26
26
|
export type SegmentedOptionProps = Omit<ButtonProps, "variant" | "type" | "aria-pressed"> & {
|
|
27
27
|
selected: boolean;
|
|
28
28
|
/** Shown muted after the label, such as how many items the option lists. */
|
|
29
29
|
count?: number;
|
|
30
30
|
};
|
|
31
31
|
/** One option of a `SegmentedControl`. */
|
|
32
|
-
export declare function SegmentedOption({ selected, count, children, ...props }: SegmentedOptionProps): import("
|
|
32
|
+
export declare function SegmentedOption({ selected, count, children, ...props }: SegmentedOptionProps): import("react").JSX.Element;
|