@parallelworks/ui 0.77.0 → 0.79.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 +42 -6
- package/dist/access/AccessDrawer.d.ts +49 -7
- package/dist/access/AccessDrawer.js +243 -38
- package/dist/access/AccessList.d.ts +19 -1
- package/dist/access/AccessList.js +220 -9
- package/dist/access/GrantView.d.ts +3 -1
- package/dist/access/GrantView.js +17 -4
- package/dist/access/PrincipalPicker.js +5 -5
- package/dist/access/focus.js +1 -1
- package/dist/access/index.d.ts +2 -2
- package/dist/access/index.js +2 -2
- package/dist/access/strings.d.ts +25 -1
- package/dist/access/strings.js +27 -3
- package/dist/access/types.d.ts +26 -0
- package/dist/ai/components/AllocationSelector.js +1 -1
- package/dist/ai/components/AttachmentManager.js +5 -5
- package/dist/ai/components/AttachmentUpload.js +2 -2
- package/dist/ai/components/BranchNavigator.js +1 -1
- package/dist/ai/components/ChatActivity.js +1 -1
- package/dist/ai/components/ChatEmptyState.js +3 -3
- package/dist/ai/components/ChatInput.js +3 -5
- package/dist/ai/components/ChatLayout.js +1 -1
- package/dist/ai/components/ChatMessage.js +1 -1
- package/dist/ai/components/ChatMessageList.js +23 -8
- package/dist/ai/components/ChatSidebar.js +3 -3
- package/dist/ai/components/ChatThread.js +4 -4
- package/dist/ai/components/ComposerAddMenu.js +9 -20
- package/dist/ai/components/ModelSelector.js +3 -3
- package/dist/ai/components/PastedText.js +5 -5
- package/dist/ai/components/SessionView.js +1 -1
- package/dist/ai/components/ShareDialog.js +3 -3
- package/dist/ai/components/SlashMenu.d.ts +31 -4
- package/dist/ai/components/SlashMenu.js +36 -63
- package/dist/ai/components/agent/ActivityLine.js +1 -1
- package/dist/ai/components/agent/AgentMessageParts.js +13 -13
- package/dist/ai/components/conversationActions.js +3 -3
- package/dist/ai/core/ChatProvider.js +1 -1
- package/dist/ai/core/config.js +1 -1
- package/dist/ai/ui/ConversationSidebar.js +3 -3
- package/dist/ai/ui/sidebar.js +3 -3
- package/dist/assets/yaml.worker-DOBV2lND.js +884 -0
- package/dist/components/ActionMenu.d.ts +38 -9
- package/dist/components/ActionMenu.js +48 -22
- package/dist/components/Avatar.d.ts +66 -6
- package/dist/components/Avatar.js +130 -18
- package/dist/components/Breadcrumbs.d.ts +4 -0
- package/dist/components/Breadcrumbs.js +26 -10
- package/dist/components/Button.d.ts +3 -2
- package/dist/components/Button.js +11 -1
- package/dist/components/Callout.d.ts +3 -2
- package/dist/components/Callout.js +3 -2
- package/dist/components/Card.d.ts +33 -9
- package/dist/components/Card.js +24 -8
- package/dist/components/Checkbox.d.ts +3 -1
- package/dist/components/Checkbox.js +4 -2
- package/dist/components/ColorInput.js +1 -1
- package/dist/components/Combobox.d.ts +23 -0
- package/dist/components/Combobox.js +252 -86
- package/dist/components/ConfirmModal.d.ts +28 -2
- package/dist/components/ConfirmModal.js +100 -15
- package/dist/components/ContextMenu.d.ts +29 -2
- package/dist/components/ContextMenu.js +60 -8
- package/dist/components/CopyButton.d.ts +21 -8
- package/dist/components/CopyButton.js +29 -9
- package/dist/components/CreateModal.d.ts +20 -4
- package/dist/components/CreateModal.js +28 -17
- package/dist/components/DatePicker.d.ts +31 -1
- package/dist/components/DatePicker.js +178 -47
- package/dist/components/DescriptionList.d.ts +13 -3
- package/dist/components/DescriptionList.js +43 -11
- package/dist/components/Disclosure.d.ts +10 -2
- package/dist/components/Disclosure.js +18 -7
- package/dist/components/Drawer.js +5 -5
- package/dist/components/EmptyState.d.ts +12 -4
- package/dist/components/EmptyState.js +18 -10
- package/dist/components/ErrorPage.d.ts +40 -0
- package/dist/components/ErrorPage.js +101 -0
- package/dist/components/ExternalLink.d.ts +4 -1
- package/dist/components/ExternalLink.js +4 -5
- package/dist/components/Field.d.ts +21 -6
- package/dist/components/Field.js +32 -16
- package/dist/components/FileButton.d.ts +19 -0
- package/dist/components/FileButton.js +32 -0
- package/dist/components/HoverCard.d.ts +26 -6
- package/dist/components/HoverCard.js +11 -5
- package/dist/components/IconButton.js +4 -2
- package/dist/components/InfoTip.js +3 -3
- package/dist/components/Input.d.ts +33 -3
- package/dist/components/Input.js +149 -14
- package/dist/components/Kbd.d.ts +40 -0
- package/dist/components/Kbd.js +168 -0
- package/dist/components/Listbox.d.ts +103 -0
- package/dist/components/Listbox.js +190 -0
- package/dist/components/Loader.d.ts +8 -2
- package/dist/components/Loader.js +16 -1
- package/dist/components/Markdown.js +1 -1
- package/dist/components/MarkdownImpl.js +1 -1
- package/dist/components/Menu.d.ts +20 -2
- package/dist/components/Menu.js +48 -19
- package/dist/components/MenuEntries.d.ts +41 -4
- package/dist/components/MenuEntries.js +266 -39
- package/dist/components/Meter.d.ts +50 -11
- package/dist/components/Meter.js +71 -21
- package/dist/components/Modal.d.ts +9 -3
- package/dist/components/Modal.js +68 -29
- package/dist/components/OneTimeCodeInput.js +1 -1
- package/dist/components/PageHeader.js +111 -7
- package/dist/components/PageHeaderAction.d.ts +8 -3
- package/dist/components/PageHeaderAction.js +122 -34
- package/dist/components/Pagination.d.ts +13 -1
- package/dist/components/Pagination.js +90 -19
- package/dist/components/Popover.d.ts +16 -2
- package/dist/components/Popover.js +20 -21
- package/dist/components/Provider.d.ts +172 -5
- package/dist/components/Provider.js +157 -14
- package/dist/components/RefreshButton.d.ts +13 -6
- package/dist/components/RefreshButton.js +17 -9
- package/dist/components/RunStatusIcon.d.ts +16 -7
- package/dist/components/RunStatusIcon.js +21 -8
- package/dist/components/Section.d.ts +11 -4
- package/dist/components/Section.js +33 -22
- package/dist/components/SegmentedControl.d.ts +16 -3
- package/dist/components/SegmentedControl.js +7 -3
- package/dist/components/Select.d.ts +19 -3
- package/dist/components/Select.js +114 -68
- package/dist/components/SelectionCopy.d.ts +10 -0
- package/dist/components/SelectionCopy.js +105 -0
- package/dist/components/SettingModeOption.js +1 -1
- package/dist/components/SettingsCard.js +1 -1
- package/dist/components/Skeleton.js +1 -1
- package/dist/components/StatusBadge.d.ts +55 -10
- package/dist/components/StatusBadge.js +91 -15
- package/dist/components/StickyBar.js +1 -1
- package/dist/components/Table.d.ts +22 -4
- package/dist/components/Table.js +46 -24
- package/dist/components/Tabs.d.ts +9 -4
- package/dist/components/Tabs.js +9 -4
- package/dist/components/Tag.d.ts +49 -0
- package/dist/components/Tag.js +199 -0
- package/dist/components/ThemeEditor.js +1 -1
- package/dist/components/Toast.js +1 -1
- package/dist/components/ToggleGroup.d.ts +4 -1
- package/dist/components/ToggleGroup.js +30 -13
- package/dist/components/Tooltip.d.ts +9 -3
- package/dist/components/Tooltip.js +55 -39
- package/dist/components/buttonClasses.d.ts +5 -2
- package/dist/components/buttonClasses.js +11 -10
- package/dist/components/chart.d.ts +69 -0
- package/dist/components/chart.js +145 -0
- package/dist/components/comboboxItems.d.ts +7 -1
- package/dist/components/comboboxItems.js +6 -2
- package/dist/components/dismiss.d.ts +5 -1
- package/dist/components/dismiss.js +6 -2
- package/dist/components/floating.d.ts +4 -4
- package/dist/components/floating.js +5 -5
- package/dist/components/headerActionsFit.d.ts +37 -0
- package/dist/components/headerActionsFit.js +81 -0
- package/dist/components/pageHeaderStore.d.ts +3 -0
- package/dist/components/pageHeaderStore.js +3 -0
- package/dist/components/pathBreaks.js +2 -2
- package/dist/components/popupContainer.d.ts +17 -0
- package/dist/components/popupContainer.js +69 -0
- package/dist/components/relativeTime.d.ts +21 -10
- package/dist/components/relativeTime.js +50 -22
- package/dist/components/segmented.d.ts +18 -4
- package/dist/components/segmented.js +48 -6
- package/dist/components/typedDate.d.ts +5 -0
- package/dist/components/typedDate.js +50 -0
- package/dist/components/useClosingValue.d.ts +4 -0
- package/dist/components/useClosingValue.js +12 -0
- package/dist/components/useCopy.d.ts +6 -0
- package/dist/components/useCopy.js +3 -3
- package/dist/components/useNarrow.d.ts +2 -0
- package/dist/components/useNarrow.js +16 -0
- package/dist/editor/Editor.js +1 -1
- package/dist/editor/FormCodePanel.js +1 -1
- package/dist/editor/Monaco.js +1 -1
- package/dist/editor/workers.js +1 -1
- package/dist/file-explorer/FileExplorer.js +16 -15
- package/dist/file-explorer/FileExplorerContext.js +2 -2
- package/dist/file-explorer/FilePreview/Viewer.js +3 -3
- package/dist/file-explorer/FilePreview/index.js +2 -2
- package/dist/file-explorer/FileRowsWindow.js +3 -3
- package/dist/file-explorer/FileTable.js +1 -1
- package/dist/file-explorer/LoadMoreRow.js +1 -1
- package/dist/file-explorer/TreeView.js +5 -5
- package/dist/file-explorer/helpers.js +2 -2
- package/dist/file-explorer/toasts.js +2 -2
- package/dist/form/Fields/CheckboxGroupField.js +1 -1
- package/dist/form/Fields/DropdownField.js +1 -1
- package/dist/form/Fields/DurationField.js +2 -2
- package/dist/form/Fields/MultiDropdownField.js +1 -1
- package/dist/form/Fields/NumberField.js +1 -1
- package/dist/form/Fields/ObjectField.js +1 -1
- package/dist/form/Fields/RangeField.js +1 -1
- package/dist/form/Fields/StringField.js +1 -1
- package/dist/form/Form.js +3 -3
- package/dist/form/FormLayout.js +1 -1
- package/dist/form/Wizard/WizardContainer.js +1 -1
- package/dist/form/Wizard/WizardNavigation.js +1 -1
- package/dist/form/Wizard/WizardStepContent.js +1 -1
- package/dist/form/fieldRegistry.js +1 -1
- package/dist/form/formEditing.js +1 -1
- package/dist/format.d.ts +17 -7
- package/dist/format.js +39 -16
- package/dist/graph/AnnotationBanner.js +2 -2
- package/dist/graph/Connectors.js +5 -5
- package/dist/graph/DependencyGraph.js +10 -9
- package/dist/graph/GraphEditor.js +4 -4
- package/dist/graph/GraphEditorDialogs.js +13 -12
- package/dist/graph/GraphNode.js +1 -1
- package/dist/graph/InputDialog.js +5 -5
- package/dist/graph/InputsEditor.js +10 -9
- package/dist/graph/MatrixGroup.js +2 -2
- package/dist/graph/ProblemPageLink.d.ts +9 -0
- package/dist/graph/ProblemPageLink.js +21 -0
- package/dist/graph/ProblemsButton.js +26 -16
- package/dist/graph/Reveal.js +1 -1
- package/dist/graph/RunWarningsList.js +21 -12
- package/dist/graph/ShortcutsButton.js +25 -8
- package/dist/graph/StepSummaries.js +1 -1
- package/dist/graph/TreeView.js +2 -2
- package/dist/graph/WorkflowSettingsDialog.js +3 -3
- package/dist/graph/editorApi.d.ts +10 -0
- package/dist/graph/editorFields.js +3 -3
- package/dist/graph/editorPrimitives.js +1 -1
- package/dist/graph/editorRows.js +8 -6
- package/dist/graph/editorStrings.d.ts +1 -0
- package/dist/graph/editorStrings.js +1 -0
- package/dist/graph/fieldProblems.js +2 -2
- package/dist/graph/inputRefs.js +7 -6
- package/dist/graph/runWarnings.d.ts +12 -1
- package/dist/graph/stepWith.js +2 -2
- package/dist/index.d.ts +30 -21
- package/dist/index.js +20 -12
- package/dist/list/ActionToolbar.d.ts +4 -0
- package/dist/list/ActionToolbar.js +35 -12
- package/dist/list/ListFilterBar.d.ts +42 -0
- package/dist/list/ListFilterBar.js +789 -0
- package/dist/list/ListPage.d.ts +42 -4
- package/dist/list/ListPage.js +56 -22
- package/dist/list/ListTable.d.ts +27 -3
- package/dist/list/ListTable.js +48 -23
- package/dist/list/ListToolbar.d.ts +31 -0
- package/dist/list/ListToolbar.js +43 -0
- package/dist/list/ListViewControls.d.ts +8 -4
- package/dist/list/ListViewControls.js +22 -14
- package/dist/list/UserHoverCard.d.ts +8 -2
- package/dist/list/UserHoverCard.js +26 -13
- package/dist/list/filterModel.d.ts +122 -0
- package/dist/list/filterModel.js +308 -0
- package/dist/list/index.d.ts +6 -2
- package/dist/list/index.js +6 -2
- package/dist/list/listSearch.d.ts +1 -1
- package/dist/list/listSearch.js +8 -4
- package/dist/list/listView.d.ts +24 -2
- package/dist/list/listView.js +47 -45
- package/dist/list/rowMenu.d.ts +10 -5
- package/dist/list/rowMenu.js +17 -7
- package/dist/list/useListFilters.d.ts +68 -0
- package/dist/list/useListFilters.js +252 -0
- package/dist/logviewer/LogViewer.js +4 -4
- package/dist/styles/theme.css +94 -0
- package/dist/styles.css +1 -1
- package/package.json +11 -11
- package/dist/assets/yaml.worker-k9AdOqzf.js +0 -1441
- package/dist/components/fullscreen.d.ts +0 -4
- package/dist/components/fullscreen.js +0 -18
|
@@ -1,23 +1,99 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { toneFill, toneSurface, toneText } from "./tone.js";
|
|
3
|
-
import {
|
|
3
|
+
import { useStrings } from "./Provider.js";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
4
6
|
//#region src/components/StatusBadge.tsx
|
|
7
|
+
var emphasisClasses = {
|
|
8
|
+
soft: {
|
|
9
|
+
...toneSurface,
|
|
10
|
+
accent: "bg-(--theme-accent)/10 text-(--theme-link) border-(--theme-accent)/25"
|
|
11
|
+
},
|
|
12
|
+
solid: {
|
|
13
|
+
default: "bg-(--theme-app) text-(--theme-app-bg) border-transparent",
|
|
14
|
+
success: "bg-(--theme-success-text) text-(--theme-app-bg) border-transparent",
|
|
15
|
+
warning: "bg-(--theme-warning-text) text-(--theme-app-bg) border-transparent",
|
|
16
|
+
error: "bg-(--theme-error-text) text-(--theme-app-bg) border-transparent",
|
|
17
|
+
info: "bg-(--theme-info-text) text-(--theme-app-bg) border-transparent",
|
|
18
|
+
muted: "bg-(--theme-muted-text-color) text-(--theme-app-bg) border-transparent",
|
|
19
|
+
accent: "bg-(--theme-accent) text-(--theme-accent-text) border-transparent"
|
|
20
|
+
},
|
|
21
|
+
outline: {
|
|
22
|
+
default: "bg-transparent text-(--theme-app) border-(--theme-border)",
|
|
23
|
+
success: "bg-transparent text-(--theme-success-text) border-(--theme-success)/40",
|
|
24
|
+
warning: "bg-transparent text-(--theme-warning-text) border-(--theme-warning)/40",
|
|
25
|
+
error: "bg-transparent text-(--theme-error-text) border-(--theme-error)/40",
|
|
26
|
+
info: "bg-transparent text-(--theme-info-text) border-(--theme-info)/40",
|
|
27
|
+
muted: "bg-transparent text-(--theme-muted-text-color) border-(--theme-border)",
|
|
28
|
+
accent: "bg-transparent text-(--theme-link) border-(--theme-accent)/40"
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var dotFill = {
|
|
32
|
+
...toneFill,
|
|
33
|
+
accent: "bg-(--theme-accent)"
|
|
34
|
+
};
|
|
35
|
+
var sizeClasses = {
|
|
36
|
+
sm: "gap-1 py-px text-[10px] leading-3.5",
|
|
37
|
+
md: "gap-1.5 py-0.5 text-[11px] leading-4"
|
|
38
|
+
};
|
|
39
|
+
var padClasses = {
|
|
40
|
+
pill: {
|
|
41
|
+
sm: "px-1.5",
|
|
42
|
+
md: "px-2"
|
|
43
|
+
},
|
|
44
|
+
tag: {
|
|
45
|
+
sm: "px-1",
|
|
46
|
+
md: "px-1.5"
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var shapeClasses = {
|
|
50
|
+
pill: "rounded-full",
|
|
51
|
+
tag: "rounded-[4px]"
|
|
52
|
+
};
|
|
5
53
|
/** A line of text in a variant's color, for a status that stands beside a
|
|
6
54
|
* dot rather than inside a badge. */
|
|
7
55
|
function statusTextClass(variant) {
|
|
8
|
-
return toneText[variant];
|
|
56
|
+
return variant === "accent" ? "text-(--theme-link)" : toneText[variant];
|
|
9
57
|
}
|
|
10
|
-
|
|
58
|
+
/** A short status or label in a tone, such as "Running" or "Preview". */
|
|
59
|
+
function StatusBadge({ children, variant = "default", emphasis = "soft", shape = "pill", caps = false, truncate = false, pulse = false, dot = false, size = "sm", render, className }) {
|
|
60
|
+
const indicator = emphasis === "solid" ? "bg-current" : dotFill[variant];
|
|
61
|
+
return useRender({
|
|
62
|
+
render: render ?? /* @__PURE__ */ jsx("span", {}),
|
|
63
|
+
props: {
|
|
64
|
+
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),
|
|
65
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
66
|
+
pulse && /* @__PURE__ */ jsxs("span", {
|
|
67
|
+
className: "relative flex h-2 w-2 shrink-0",
|
|
68
|
+
children: [/* @__PURE__ */ jsx("span", { className: cx("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75 motion-reduce:hidden", indicator) }), /* @__PURE__ */ jsx("span", { className: cx("relative inline-flex h-2 w-2 rounded-full", indicator) })]
|
|
69
|
+
}),
|
|
70
|
+
dot && !pulse && /* @__PURE__ */ jsx("span", { className: cx("inline-flex h-1.5 w-1.5 shrink-0 rounded-full", indicator) }),
|
|
71
|
+
truncate ? /* @__PURE__ */ jsx("span", {
|
|
72
|
+
className: "min-w-0 truncate",
|
|
73
|
+
children
|
|
74
|
+
}) : children
|
|
75
|
+
] })
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
var countSizeClasses = {
|
|
80
|
+
sm: "min-w-3.5 px-1 text-[10px] leading-3.5",
|
|
81
|
+
md: "min-w-4 px-1.5 text-[11px] leading-4"
|
|
82
|
+
};
|
|
83
|
+
/** A number beside a link or tab, such as unread messages or items waiting,
|
|
84
|
+
* capped at `max` so a big count doesn't widen what it sits in. */
|
|
85
|
+
function CountBadge({ count, max = 99, variant = "accent", emphasis = "solid", size = "md", label, showZero = false, locale, className }) {
|
|
86
|
+
const { common } = useStrings();
|
|
87
|
+
if (count <= 0 && !showZero) return null;
|
|
88
|
+
const number = new Intl.NumberFormat(locale);
|
|
89
|
+
const text = count > max ? common.countOverflow(number.format(max)) : number.format(count);
|
|
11
90
|
return /* @__PURE__ */ jsxs("span", {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
dot && !pulse && /* @__PURE__ */ jsx("span", { className: cx("inline-flex h-1.5 w-1.5 rounded-full", toneFill[variant]) }),
|
|
19
|
-
children
|
|
20
|
-
]
|
|
91
|
+
"data-count": count,
|
|
92
|
+
className: cx("inline-flex shrink-0 items-center justify-center rounded-full font-semibold tabular-nums", countSizeClasses[size], emphasisClasses[emphasis][variant], className),
|
|
93
|
+
children: [text, label && /* @__PURE__ */ jsxs("span", {
|
|
94
|
+
className: "sr-only",
|
|
95
|
+
children: [" ", label]
|
|
96
|
+
})]
|
|
21
97
|
});
|
|
22
98
|
}
|
|
23
99
|
function StatusDot({ variant = "default", pulse = false, size = 8, className }) {
|
|
@@ -27,8 +103,8 @@ function StatusDot({ variant = "default", pulse = false, size = 8, className })
|
|
|
27
103
|
width: size,
|
|
28
104
|
height: size
|
|
29
105
|
},
|
|
30
|
-
children: [pulse && /* @__PURE__ */ jsx("span", { className: cx("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75 motion-reduce:hidden",
|
|
31
|
-
className: cx("relative inline-flex rounded-full",
|
|
106
|
+
children: [pulse && /* @__PURE__ */ jsx("span", { className: cx("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75 motion-reduce:hidden", dotFill[variant]) }), /* @__PURE__ */ jsx("span", {
|
|
107
|
+
className: cx("relative inline-flex rounded-full", dotFill[variant]),
|
|
32
108
|
style: {
|
|
33
109
|
width: size,
|
|
34
110
|
height: size
|
|
@@ -37,4 +113,4 @@ function StatusDot({ variant = "default", pulse = false, size = 8, className })
|
|
|
37
113
|
});
|
|
38
114
|
}
|
|
39
115
|
//#endregion
|
|
40
|
-
export { StatusBadge, StatusDot, statusTextClass };
|
|
116
|
+
export { CountBadge, StatusBadge, StatusDot, statusTextClass };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { stickyClasses, useStickyTop } from "./stickyTop.js";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
3
|
import { useState } from "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/StickyBar.tsx
|
|
6
6
|
/** A strip of a page's own controls, such as a row of filters or a page's
|
|
7
7
|
* actions, pinned as the page scrolls. It stops just below whatever its
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
/** Which way a column sorts, as `aria-sort` names it. */
|
|
3
3
|
export type SortDirection = 'ascending' | 'descending' | 'none';
|
|
4
|
-
/** `comfortable` gives each row room; `compact` fits a short key and value
|
|
5
|
-
|
|
4
|
+
/** `comfortable` gives each row room; `compact` fits a short key and value
|
|
5
|
+
* list; `dense` sets small type in tight rows, for a table inside a card or
|
|
6
|
+
* a panel of figures. */
|
|
7
|
+
export type TableDensity = 'comfortable' | 'compact' | 'dense';
|
|
6
8
|
interface ItemProps extends React.TdHTMLAttributes<HTMLTableCellElement> {
|
|
7
9
|
/** Lets the text wrap; by default a cell keeps to one line. */
|
|
8
10
|
wrap?: boolean;
|
|
@@ -18,13 +20,29 @@ interface HeaderProps {
|
|
|
18
20
|
sort?: SortDirection | undefined;
|
|
19
21
|
/** Sorts by this column, or flips its direction. */
|
|
20
22
|
onSort?: (() => void) | undefined;
|
|
21
|
-
/** False sets the title in sentence case instead of small capitals.
|
|
23
|
+
/** False sets the title in sentence case instead of small capitals. The
|
|
24
|
+
* table's `caps` by default. */
|
|
22
25
|
caps?: boolean | undefined;
|
|
23
26
|
}
|
|
24
27
|
declare function Header({ children, className, style, colSpan, sort, onSort, caps }: HeaderProps): React.JSX.Element;
|
|
25
28
|
interface TableProps {
|
|
26
29
|
children?: React.ReactNode;
|
|
27
30
|
density?: TableDensity | undefined;
|
|
31
|
+
/** Each column's width, in order, such as `['20%', '8rem', undefined]`; a
|
|
32
|
+
* column left undefined takes what the others leave. With
|
|
33
|
+
* `table-fixed` in `className`, the widths hold whatever the cells hold. */
|
|
34
|
+
columns?: readonly (string | undefined)[] | undefined;
|
|
35
|
+
/** False sets every header in sentence case, as a small table in a card
|
|
36
|
+
* reads best; a header's own `caps` still wins. */
|
|
37
|
+
caps?: boolean | undefined;
|
|
38
|
+
/** Drops the outer side padding of the first and last columns and the
|
|
39
|
+
* header's fill, for a table inside something already padded, such as a
|
|
40
|
+
* card: it reads as part of the card, its titles in line with the card's
|
|
41
|
+
* other content and set apart by the rule under them. */
|
|
42
|
+
flush?: boolean | undefined;
|
|
43
|
+
/** Rules a line between columns as well as rows, for a grid of figures to
|
|
44
|
+
* read across and down. */
|
|
45
|
+
columnDividers?: boolean | undefined;
|
|
28
46
|
borderless?: boolean;
|
|
29
47
|
/** When false, drops the theme-panel background. */
|
|
30
48
|
panel?: boolean;
|
|
@@ -56,7 +74,7 @@ interface TableProps {
|
|
|
56
74
|
}
|
|
57
75
|
/** Rows of values under a row of column titles. Put `Table.Header`s first among
|
|
58
76
|
* the children, then `<tr>`s of `Table.Item`s. */
|
|
59
|
-
export declare function Table({ density, borderless, panel, className, wrapperClassName, tbodyClassName, theadClassName, children, roundedTop, rounded, roundedBottom, isLoading, stickyHeader, tableProps, }: TableProps): React.JSX.Element;
|
|
77
|
+
export declare function Table({ density, columns, caps, flush, columnDividers, borderless, panel, className, wrapperClassName, tbodyClassName, theadClassName, children, roundedTop, rounded, roundedBottom, isLoading, stickyHeader, tableProps, }: TableProps): React.JSX.Element;
|
|
60
78
|
export declare namespace Table {
|
|
61
79
|
export { Header };
|
|
62
80
|
export { Item };
|
package/dist/components/Table.js
CHANGED
|
@@ -2,16 +2,27 @@ import { cx } from "../cx.js";
|
|
|
2
2
|
import { SortDownIcon, SortUnsortedIcon, SortUpIcon } from "../icons.js";
|
|
3
3
|
import { Loader } from "./Loader.js";
|
|
4
4
|
import { useStickyTop } from "./stickyTop.js";
|
|
5
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
5
|
import React, { createContext, useContext, useState } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/Table.tsx
|
|
8
8
|
var DensityContext = createContext("comfortable");
|
|
9
|
+
var CapsContext = createContext(true);
|
|
10
|
+
var cellY = {
|
|
11
|
+
comfortable: "py-4",
|
|
12
|
+
compact: "py-1.5 text-sm",
|
|
13
|
+
dense: "py-1 text-xs"
|
|
14
|
+
};
|
|
15
|
+
var headerY = {
|
|
16
|
+
comfortable: "py-3",
|
|
17
|
+
compact: "py-2",
|
|
18
|
+
dense: "py-1.5"
|
|
19
|
+
};
|
|
9
20
|
var cellX = "px-3";
|
|
10
21
|
function Item({ wrap = false, children, className, ...tdProps }) {
|
|
11
22
|
const density = useContext(DensityContext);
|
|
12
23
|
return /* @__PURE__ */ jsx("td", {
|
|
13
24
|
...tdProps,
|
|
14
|
-
className: cx(cellX, density
|
|
25
|
+
className: cx(cellX, cellY[density], !wrap && "whitespace-nowrap", className),
|
|
15
26
|
children
|
|
16
27
|
});
|
|
17
28
|
}
|
|
@@ -20,15 +31,16 @@ var sortIcons = {
|
|
|
20
31
|
descending: SortDownIcon,
|
|
21
32
|
none: SortUnsortedIcon
|
|
22
33
|
};
|
|
23
|
-
function Header({ children, className, style, colSpan, sort, onSort, caps
|
|
34
|
+
function Header({ children, className, style, colSpan, sort, onSort, caps }) {
|
|
24
35
|
const density = useContext(DensityContext);
|
|
36
|
+
const tableCaps = useContext(CapsContext);
|
|
25
37
|
const Icon = sortIcons[sort ?? "none"];
|
|
26
38
|
return /* @__PURE__ */ jsx("th", {
|
|
27
39
|
scope: "col",
|
|
28
40
|
colSpan,
|
|
29
41
|
style,
|
|
30
42
|
"aria-sort": onSort ? sort ?? "none" : void 0,
|
|
31
|
-
className: cx(cellX, density
|
|
43
|
+
className: cx(cellX, headerY[density], "text-left text-xs font-medium", (caps ?? tableCaps) && "uppercase tracking-wider", className),
|
|
32
44
|
children: onSort ? /* @__PURE__ */ jsxs("button", {
|
|
33
45
|
type: "button",
|
|
34
46
|
onClick: onSort,
|
|
@@ -46,32 +58,42 @@ var isHeaderChild = (child) => {
|
|
|
46
58
|
};
|
|
47
59
|
/** Rows of values under a row of column titles. Put `Table.Header`s first among
|
|
48
60
|
* the children, then `<tr>`s of `Table.Item`s. */
|
|
49
|
-
function Table({ density = "comfortable", borderless = false, panel = true, className, wrapperClassName = "", tbodyClassName = "divide-y", theadClassName
|
|
61
|
+
function Table({ density = "comfortable", columns, caps = true, flush = false, columnDividers = false, borderless = false, panel = true, className, wrapperClassName = "", tbodyClassName = "divide-y", theadClassName, children, roundedTop = false, rounded = false, roundedBottom = false, isLoading = false, stickyHeader = false, tableProps }) {
|
|
50
62
|
const childList = React.Children.toArray(children);
|
|
51
63
|
const [thead, setThead] = useState(null);
|
|
52
64
|
useStickyTop(thead, stickyHeader && !isLoading);
|
|
53
65
|
const hasOverflow = wrapperClassName.split(" ").some((cls) => cls.startsWith("overflow-"));
|
|
54
66
|
return /* @__PURE__ */ jsx(DensityContext, {
|
|
55
67
|
value: density,
|
|
56
|
-
children: /* @__PURE__ */ jsx(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
className: "flex
|
|
61
|
-
children: /* @__PURE__ */ jsx(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
className: cx("min-w-full
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
children: /* @__PURE__ */ jsx(CapsContext, {
|
|
69
|
+
value: caps,
|
|
70
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
71
|
+
"aria-busy": isLoading,
|
|
72
|
+
className: cx("flex w-full flex-col", !hasOverflow && !stickyHeader && "overflow-hidden", wrapperClassName),
|
|
73
|
+
children: isLoading ? /* @__PURE__ */ jsx("div", {
|
|
74
|
+
className: "flex h-32 items-center justify-center",
|
|
75
|
+
children: /* @__PURE__ */ jsx(Loader, {})
|
|
76
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
77
|
+
className: cx("inline-block min-w-full align-middle", !stickyHeader && "overflow-x-auto", panel && "theme-panel", !borderless && "border theme-border", rounded && "rounded-xl", roundedBottom && "rounded-b-xl", roundedTop && "rounded-t-xl"),
|
|
78
|
+
children: /* @__PURE__ */ jsxs("table", {
|
|
79
|
+
className: cx("min-w-full divide-y theme-border", flush && "[&_td:first-child]:pl-0 [&_th:first-child]:pl-0 [&_td:last-child]:pr-0 [&_th:last-child]:pr-0", columnDividers && "[&_tr]:divide-x [&_tr]:divide-(--theme-border)", className),
|
|
80
|
+
...tableProps,
|
|
81
|
+
children: [
|
|
82
|
+
columns && /* @__PURE__ */ jsx("colgroup", {
|
|
83
|
+
className: "border-0",
|
|
84
|
+
children: columns.map((width, i) => /* @__PURE__ */ jsx("col", { style: width ? { width } : void 0 }, i))
|
|
85
|
+
}),
|
|
86
|
+
/* @__PURE__ */ jsx("thead", {
|
|
87
|
+
ref: setThead,
|
|
88
|
+
className: cx(stickyHeader && "sticky top-0 z-(--layer-sticky) border-b-0 shadow-[inset_0_-1px_0_var(--theme-border)]", theadClassName ?? (flush ? "text-(--theme-muted-text-color)" : "theme-muted-panel")),
|
|
89
|
+
children: /* @__PURE__ */ jsx("tr", { children: childList.filter(isHeaderChild) })
|
|
90
|
+
}),
|
|
91
|
+
/* @__PURE__ */ jsx("tbody", {
|
|
92
|
+
className: tbodyClassName,
|
|
93
|
+
children: childList.filter((c) => !isHeaderChild(c))
|
|
94
|
+
})
|
|
95
|
+
]
|
|
96
|
+
})
|
|
75
97
|
})
|
|
76
98
|
})
|
|
77
99
|
})
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
|
-
import { type SegmentLayout } from './segmented.js';
|
|
2
|
+
import { type SegmentLayout, type SegmentTone } from './segmented.js';
|
|
3
3
|
export interface TabItem<Value extends string = string> {
|
|
4
4
|
value: Value;
|
|
5
5
|
label: string;
|
|
6
6
|
icon?: ReactNode;
|
|
7
7
|
/** Beside the label, or in the corner when only the icon shows. */
|
|
8
8
|
badge?: ReactNode;
|
|
9
|
+
/** How many things the view holds, as a pill after the label. */
|
|
10
|
+
count?: number | undefined;
|
|
11
|
+
/** Colors the label and the count, for a view that needs attention, such
|
|
12
|
+
* as what failed or what has been left behind. */
|
|
13
|
+
tone?: SegmentTone | undefined;
|
|
9
14
|
/** Navigates instead of showing a panel, for tabs that are pages. */
|
|
10
15
|
href?: string;
|
|
11
16
|
disabled?: boolean;
|
|
@@ -20,8 +25,8 @@ export interface TabsProps<Value extends string> extends SegmentLayout {
|
|
|
20
25
|
defaultValue?: Value | undefined;
|
|
21
26
|
onValueChange?: ((value: Value) => void) | undefined;
|
|
22
27
|
/** `line` underlines the current tab across a page; `segmented` matches a
|
|
23
|
-
* SegmentedControl, for a few views in a small space. `fill
|
|
24
|
-
* apply to `segmented`. */
|
|
28
|
+
* SegmentedControl, for a few views in a small space. `fill`, `iconOnly`
|
|
29
|
+
* and `size` apply to `segmented`. */
|
|
25
30
|
variant?: 'line' | 'segmented' | undefined;
|
|
26
31
|
/** At the tab row's far end, such as a picker for what the tabs show. */
|
|
27
32
|
end?: ReactNode;
|
|
@@ -37,7 +42,7 @@ export interface TabsProps<Value extends string> extends SegmentLayout {
|
|
|
37
42
|
/** Views of one thing, one shown at a time: panels below the tabs, or pages
|
|
38
43
|
* the tabs link to. A lone tab would only repeat the page's title, so the row
|
|
39
44
|
* hides unless there are two or more, or something at its end. */
|
|
40
|
-
export declare function Tabs<Value extends string>({ label, items, value, defaultValue, onValueChange, variant, fill, iconOnly, end, sticky, className, testId, children, }: TabsProps<Value>): import("react").JSX.Element;
|
|
45
|
+
export declare function Tabs<Value extends string>({ label, items, value, defaultValue, onValueChange, variant, fill, iconOnly, size, end, sticky, className, testId, children, }: TabsProps<Value>): import("react").JSX.Element;
|
|
41
46
|
/** One tab's content, shown while its tab is current. */
|
|
42
47
|
export declare function TabPanel({ value, keepMounted, className, children, }: {
|
|
43
48
|
value: string;
|
package/dist/components/Tabs.js
CHANGED
|
@@ -3,18 +3,23 @@ import { Tooltip } from "./Tooltip.js";
|
|
|
3
3
|
import { AppLink } from "./AppLink.js";
|
|
4
4
|
import { SegmentContent, segmentClassName, segmentGroupClassName } from "./segmented.js";
|
|
5
5
|
import { useStickyTop } from "./stickyTop.js";
|
|
6
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
6
|
import { useState } from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { Tabs } from "@base-ui/react/tabs";
|
|
9
9
|
//#region src/components/Tabs.tsx
|
|
10
|
+
var lineTone = {
|
|
11
|
+
warning: "text-(--theme-warning)",
|
|
12
|
+
danger: "text-(--theme-error-text)"
|
|
13
|
+
};
|
|
10
14
|
/** Views of one thing, one shown at a time: panels below the tabs, or pages
|
|
11
15
|
* the tabs link to. A lone tab would only repeat the page's title, so the row
|
|
12
16
|
* hides unless there are two or more, or something at its end. */
|
|
13
|
-
function Tabs$1({ label, items, value, defaultValue, onValueChange, variant = "line", fill = false, iconOnly = false, end, sticky, className, testId, children }) {
|
|
17
|
+
function Tabs$1({ label, items, value, defaultValue, onValueChange, variant = "line", fill = false, iconOnly = false, size = "md", end, sticky, className, testId, children }) {
|
|
14
18
|
const segmented = variant === "segmented";
|
|
15
19
|
const layout = {
|
|
16
20
|
fill,
|
|
17
|
-
iconOnly: segmented && iconOnly
|
|
21
|
+
iconOnly: segmented && iconOnly,
|
|
22
|
+
size
|
|
18
23
|
};
|
|
19
24
|
const [own, setOwn] = useState(defaultValue ?? items[0]?.value);
|
|
20
25
|
const current = value ?? own;
|
|
@@ -30,7 +35,7 @@ function Tabs$1({ label, items, value, defaultValue, onValueChange, variant = "l
|
|
|
30
35
|
nativeButton: false,
|
|
31
36
|
render: /* @__PURE__ */ jsx(AppLink, { href: item.href })
|
|
32
37
|
} : {},
|
|
33
|
-
className: segmented ? segmentClassName(item.value === current, layout) : "flex cursor-pointer select-none items-center gap-1.5 whitespace-nowrap px-2 py-2 text-sm font-semibold
|
|
38
|
+
className: segmented ? segmentClassName(item.value === current, layout, item.tone) : cx("flex cursor-pointer select-none items-center gap-1.5 whitespace-nowrap px-2 py-2 text-sm font-semibold transition-colors focus-visible:rounded-sm focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--theme-focus) data-disabled:cursor-not-allowed data-disabled:opacity-50", item.tone ? lineTone[item.tone] : "theme-muted-text hover:theme-text data-active:text-(--theme-link)"),
|
|
34
39
|
children: /* @__PURE__ */ jsx(SegmentContent, {
|
|
35
40
|
...item,
|
|
36
41
|
iconOnly: layout.iconOnly
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface TagStyle {
|
|
3
|
+
/** Monospace text, for a value read as code, such as a key=value label. */
|
|
4
|
+
mono?: boolean | undefined;
|
|
5
|
+
/** Small capitals with wider spacing, for a short code such as a region. */
|
|
6
|
+
caps?: boolean | undefined;
|
|
7
|
+
}
|
|
8
|
+
/** The one tag look: a pill with a hairline border and 11px text. Another component's chip,
|
|
9
|
+
* such as a picker's chosen value, uses it to look the same. */
|
|
10
|
+
export declare function tagClasses({ mono, caps }?: TagStyle): string;
|
|
11
|
+
export interface TagProps extends TagStyle {
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
/** A dot before the text in this color, any CSS color, such as a label's own. */
|
|
14
|
+
color?: string | undefined;
|
|
15
|
+
/** Makes the tag a link, through the host's router. */
|
|
16
|
+
href?: string | undefined;
|
|
17
|
+
/** Adds an × that removes the tag, named by `removeLabel` ("Remove gpu" by default). */
|
|
18
|
+
onRemove?: (() => void) | undefined;
|
|
19
|
+
removeLabel?: string | undefined;
|
|
20
|
+
/** A hint on hover, such as the full text of a long tag. */
|
|
21
|
+
title?: string | undefined;
|
|
22
|
+
className?: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
/** A tag or label on a thing: a pill with an optional color dot, a link with `href`, and an ×
|
|
25
|
+
* with `onRemove`. */
|
|
26
|
+
export declare function Tag({ children, color, href, onRemove, removeLabel, title, mono, caps, className, }: TagProps): import("react").JSX.Element;
|
|
27
|
+
/** One tag of a `TagList`: a string is its own key and label. */
|
|
28
|
+
export type TagItem = string | {
|
|
29
|
+
key: string;
|
|
30
|
+
label: ReactNode;
|
|
31
|
+
color?: string | undefined;
|
|
32
|
+
href?: string | undefined;
|
|
33
|
+
};
|
|
34
|
+
export interface TagListProps extends TagStyle {
|
|
35
|
+
tags: readonly TagItem[];
|
|
36
|
+
/** `wrap`, the default, runs onto more lines; `clip` keeps one line and ends with a "+N"
|
|
37
|
+
* chip for the tags that don't fit, which lists them on hover or focus. */
|
|
38
|
+
overflow?: 'wrap' | 'clip' | undefined;
|
|
39
|
+
/** Shows at most this many, the rest in the "+N" chip. */
|
|
40
|
+
max?: number | undefined;
|
|
41
|
+
/** Adds an × to each tag; called with the tag's key. */
|
|
42
|
+
onRemove?: ((key: string) => void) | undefined;
|
|
43
|
+
className?: string | undefined;
|
|
44
|
+
}
|
|
45
|
+
/** A thing's tags as a list: wrapped, or on one line ending in "+N" for the rest. */
|
|
46
|
+
export declare function TagList({ tags, overflow, max, mono, caps, onRemove, className, }: TagListProps): import("react").JSX.Element;
|
|
47
|
+
/** Tags as the strings they hold, from an array or a comma-separated string: trimmed, empty
|
|
48
|
+
* ones dropped, each once. Anything else is no tags. */
|
|
49
|
+
export declare function normalizeTags(tags: unknown): string[];
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { cx } from "../cx.js";
|
|
2
|
+
import { RemoveIcon } from "../icons.js";
|
|
3
|
+
import { useLocale, useStrings } from "./Provider.js";
|
|
4
|
+
import { AppLink } from "./AppLink.js";
|
|
5
|
+
import { Popover } from "./Popover.js";
|
|
6
|
+
import { useLayoutEffect, useRef, useState } from "react";
|
|
7
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
//#region src/components/Tag.tsx
|
|
9
|
+
/** The one tag look: a pill with a hairline border and 11px text. Another component's chip,
|
|
10
|
+
* such as a picker's chosen value, uses it to look the same. */
|
|
11
|
+
function tagClasses({ mono = false, caps = false } = {}) {
|
|
12
|
+
return cx("inline-flex h-5 max-w-full min-w-0 shrink-0 items-center gap-1 rounded-full border border-(--theme-border) px-2 text-[11px] leading-none whitespace-nowrap text-(--theme-app)", mono ? "font-mono" : "font-sans", caps && "text-[10px] font-medium tracking-wide uppercase");
|
|
13
|
+
}
|
|
14
|
+
/** A tag or label on a thing: a pill with an optional color dot, a link with `href`, and an ×
|
|
15
|
+
* with `onRemove`. */
|
|
16
|
+
function Tag({ children, color, href, onRemove, removeLabel, title, mono, caps, className }) {
|
|
17
|
+
const { dropdown } = useStrings();
|
|
18
|
+
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [color && /* @__PURE__ */ jsx("span", {
|
|
19
|
+
"aria-hidden": "true",
|
|
20
|
+
className: "size-2 shrink-0 rounded-full",
|
|
21
|
+
style: { background: color }
|
|
22
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
23
|
+
className: "min-w-0 truncate py-px",
|
|
24
|
+
children
|
|
25
|
+
})] });
|
|
26
|
+
const classes = cx(tagClasses({
|
|
27
|
+
mono,
|
|
28
|
+
caps
|
|
29
|
+
}), className);
|
|
30
|
+
const hover = "transition-colors hover:bg-(--theme-hover)";
|
|
31
|
+
if (!onRemove) return href ? /* @__PURE__ */ jsx(AppLink, {
|
|
32
|
+
href,
|
|
33
|
+
title,
|
|
34
|
+
className: cx(classes, hover, "no-underline"),
|
|
35
|
+
children: content
|
|
36
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
37
|
+
title,
|
|
38
|
+
className: classes,
|
|
39
|
+
children: content
|
|
40
|
+
});
|
|
41
|
+
const name = removeLabel ?? dropdown.removeValue(typeof children === "string" ? children : "");
|
|
42
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
43
|
+
title,
|
|
44
|
+
className: cx(classes, "pr-0.5"),
|
|
45
|
+
children: [href ? /* @__PURE__ */ jsx(AppLink, {
|
|
46
|
+
href,
|
|
47
|
+
className: "inline-flex min-w-0 items-center gap-1 text-inherit no-underline hover:underline",
|
|
48
|
+
children: content
|
|
49
|
+
}) : content, /* @__PURE__ */ jsx("button", {
|
|
50
|
+
type: "button",
|
|
51
|
+
onClick: onRemove,
|
|
52
|
+
"aria-label": name.trim(),
|
|
53
|
+
className: "flex size-4 shrink-0 cursor-pointer items-center justify-center rounded-full text-(--theme-muted-text-color) hover:bg-(--theme-hover) hover:text-(--theme-app) focus-visible:outline-2 focus-visible:outline-(--theme-focus)",
|
|
54
|
+
children: /* @__PURE__ */ jsx(RemoveIcon, {
|
|
55
|
+
"aria-hidden": "true",
|
|
56
|
+
className: "size-2.5"
|
|
57
|
+
})
|
|
58
|
+
})]
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
var normal = (tag) => typeof tag === "string" ? {
|
|
62
|
+
key: tag,
|
|
63
|
+
label: tag
|
|
64
|
+
} : tag;
|
|
65
|
+
var text = (tag) => typeof tag.label === "string" ? tag.label : tag.key;
|
|
66
|
+
/** A thing's tags as a list: wrapped, or on one line ending in "+N" for the rest. */
|
|
67
|
+
function TagList({ tags, overflow = "wrap", max, mono, caps, onRemove, className }) {
|
|
68
|
+
const items = tags.map(normal);
|
|
69
|
+
const clip = overflow === "clip";
|
|
70
|
+
const capped = max === void 0 ? items.length : Math.max(0, Math.min(max, items.length));
|
|
71
|
+
const [fit, setFit] = useState(capped);
|
|
72
|
+
const list = useRef(null);
|
|
73
|
+
const measure = useRef(null);
|
|
74
|
+
const signature = items.map((item) => item.key).join("\n");
|
|
75
|
+
useLayoutEffect(() => {
|
|
76
|
+
const row = list.current;
|
|
77
|
+
const copy = measure.current;
|
|
78
|
+
if (!clip || !row || !copy) return;
|
|
79
|
+
const place = () => {
|
|
80
|
+
const width = row.clientWidth;
|
|
81
|
+
const chips = [...copy.children];
|
|
82
|
+
const more = chips.pop();
|
|
83
|
+
const gap = 4;
|
|
84
|
+
const widths = chips.slice(0, capped).map((chip) => chip.offsetWidth);
|
|
85
|
+
const total = widths.reduce((sum, w, i) => sum + w + (i ? gap : 0), 0);
|
|
86
|
+
if (capped === items.length && total <= width) {
|
|
87
|
+
setFit(capped);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const room = width - (more?.offsetWidth ?? 0) - gap;
|
|
91
|
+
let used = 0;
|
|
92
|
+
let count = 0;
|
|
93
|
+
for (const w of widths) {
|
|
94
|
+
const next = used + (count ? gap : 0) + w;
|
|
95
|
+
if (next > room) break;
|
|
96
|
+
used = next;
|
|
97
|
+
count++;
|
|
98
|
+
}
|
|
99
|
+
setFit(count);
|
|
100
|
+
};
|
|
101
|
+
place();
|
|
102
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
103
|
+
const observer = new ResizeObserver(place);
|
|
104
|
+
observer.observe(row);
|
|
105
|
+
return () => observer.disconnect();
|
|
106
|
+
}, [
|
|
107
|
+
clip,
|
|
108
|
+
capped,
|
|
109
|
+
signature,
|
|
110
|
+
items.length
|
|
111
|
+
]);
|
|
112
|
+
const shown = items.slice(0, clip ? fit : capped);
|
|
113
|
+
const hidden = items.slice(shown.length);
|
|
114
|
+
const style = {
|
|
115
|
+
mono,
|
|
116
|
+
caps
|
|
117
|
+
};
|
|
118
|
+
const tag = (item) => /* @__PURE__ */ jsx(Tag, {
|
|
119
|
+
...style,
|
|
120
|
+
color: item.color,
|
|
121
|
+
href: item.href,
|
|
122
|
+
onRemove: onRemove ? () => onRemove(item.key) : void 0,
|
|
123
|
+
children: item.label
|
|
124
|
+
});
|
|
125
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
126
|
+
className: cx("relative min-w-0", clip && "overflow-hidden", className),
|
|
127
|
+
children: [/* @__PURE__ */ jsxs("ul", {
|
|
128
|
+
ref: list,
|
|
129
|
+
className: cx("m-0 flex min-w-0 list-none items-center gap-1 p-0", !clip && "flex-wrap"),
|
|
130
|
+
children: [shown.map((item) => /* @__PURE__ */ jsx("li", {
|
|
131
|
+
className: "flex min-w-0",
|
|
132
|
+
children: tag(item)
|
|
133
|
+
}, item.key)), hidden.length > 0 && /* @__PURE__ */ jsx("li", {
|
|
134
|
+
className: "flex shrink-0",
|
|
135
|
+
children: /* @__PURE__ */ jsx(MoreTags, {
|
|
136
|
+
tags: hidden,
|
|
137
|
+
...style
|
|
138
|
+
})
|
|
139
|
+
})]
|
|
140
|
+
}), clip && /* @__PURE__ */ jsxs("div", {
|
|
141
|
+
ref: measure,
|
|
142
|
+
"aria-hidden": "true",
|
|
143
|
+
inert: true,
|
|
144
|
+
className: "pointer-events-none invisible absolute top-0 left-0 flex h-0 items-center gap-1 overflow-hidden",
|
|
145
|
+
children: [items.slice(0, capped).map((item) => /* @__PURE__ */ jsx("span", {
|
|
146
|
+
className: "flex shrink-0",
|
|
147
|
+
children: tag(item)
|
|
148
|
+
}, item.key)), /* @__PURE__ */ jsxs("span", {
|
|
149
|
+
className: tagClasses(style),
|
|
150
|
+
children: ["+", items.length]
|
|
151
|
+
})]
|
|
152
|
+
})]
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/** "+N" for the tags a list leaves out: a button that shows them in a popover, which also
|
|
156
|
+
* opens while the pointer rests on it, and whose name lists them. */
|
|
157
|
+
function MoreTags({ tags, ...style }) {
|
|
158
|
+
const { common } = useStrings();
|
|
159
|
+
const locale = useLocale();
|
|
160
|
+
const names = tags.map(text);
|
|
161
|
+
const count = new Intl.NumberFormat(locale).format(tags.length);
|
|
162
|
+
return /* @__PURE__ */ jsx(Popover, {
|
|
163
|
+
openOnHover: true,
|
|
164
|
+
initialFocus: "popup",
|
|
165
|
+
"aria-label": common.moreCount(tags.length),
|
|
166
|
+
className: "max-w-64 p-2",
|
|
167
|
+
trigger: /* @__PURE__ */ jsxs("button", {
|
|
168
|
+
type: "button",
|
|
169
|
+
"aria-label": `${common.moreCount(tags.length)}: ${names.join(", ")}`,
|
|
170
|
+
className: cx(tagClasses(style), "cursor-pointer text-(--theme-muted-text-color) tabular-nums hover:bg-(--theme-hover) hover:text-(--theme-app) focus-visible:outline-2 focus-visible:outline-(--theme-focus) data-popup-open:bg-(--theme-hover)"),
|
|
171
|
+
children: ["+", count]
|
|
172
|
+
}),
|
|
173
|
+
children: /* @__PURE__ */ jsx("ul", {
|
|
174
|
+
className: "m-0 flex list-none flex-wrap gap-1 p-0",
|
|
175
|
+
children: tags.map((tag) => /* @__PURE__ */ jsx("li", {
|
|
176
|
+
className: "flex min-w-0",
|
|
177
|
+
children: /* @__PURE__ */ jsx(Tag, {
|
|
178
|
+
...style,
|
|
179
|
+
color: tag.color,
|
|
180
|
+
href: tag.href,
|
|
181
|
+
children: tag.label
|
|
182
|
+
})
|
|
183
|
+
}, tag.key))
|
|
184
|
+
})
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/** Tags as the strings they hold, from an array or a comma-separated string: trimmed, empty
|
|
188
|
+
* ones dropped, each once. Anything else is no tags. */
|
|
189
|
+
function normalizeTags(tags) {
|
|
190
|
+
const list = Array.isArray(tags) ? tags : typeof tags === "string" ? tags.split(",") : [];
|
|
191
|
+
const seen = /* @__PURE__ */ new Set();
|
|
192
|
+
for (const tag of list) if (typeof tag === "string" || typeof tag === "number") {
|
|
193
|
+
const value = String(tag).trim();
|
|
194
|
+
if (value) seen.add(value);
|
|
195
|
+
}
|
|
196
|
+
return [...seen];
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
export { Tag, TagList, normalizeTags, tagClasses };
|
|
@@ -17,8 +17,8 @@ import { Switch } from "./Switch.js";
|
|
|
17
17
|
import { Select } from "./Select.js";
|
|
18
18
|
import { SegmentedControl } from "./SegmentedControl.js";
|
|
19
19
|
import { Slider } from "./Slider.js";
|
|
20
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
20
|
import { useId, useMemo, useState } from "react";
|
|
21
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
22
22
|
//#region src/components/ThemeEditor.tsx
|
|
23
23
|
/**
|
|
24
24
|
* Edits a theme: a background, an accent and a contrast for light and for
|
package/dist/components/Toast.js
CHANGED
|
@@ -5,8 +5,8 @@ import { Loader } from "./Loader.js";
|
|
|
5
5
|
import { toneIcon } from "./tone.js";
|
|
6
6
|
import { useStrings } from "./Provider.js";
|
|
7
7
|
import { AppLink } from "./AppLink.js";
|
|
8
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
8
|
import { useEffect, useId, useSyncExternalStore } from "react";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
import { Toast } from "@base-ui/react/toast";
|
|
11
11
|
//#region src/components/Toast.tsx
|
|
12
12
|
function actionFields(action) {
|