@flanksource/clicky-ui 0.2.4 → 0.2.5
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/components/Combobox.cjs +291 -0
- package/dist/components/Combobox.cjs.map +1 -0
- package/dist/components/Combobox.d.ts +59 -0
- package/dist/components/Combobox.d.ts.map +1 -0
- package/dist/components/Combobox.js +291 -0
- package/dist/components/Combobox.js.map +1 -0
- package/dist/components/FilterBar.cjs +153 -124
- package/dist/components/FilterBar.cjs.map +1 -1
- package/dist/components/FilterBar.d.ts +25 -1
- package/dist/components/FilterBar.d.ts.map +1 -1
- package/dist/components/FilterBar.js +153 -124
- package/dist/components/FilterBar.js.map +1 -1
- package/dist/components/FormatOptionsDropdown.cjs +62 -0
- package/dist/components/FormatOptionsDropdown.cjs.map +1 -0
- package/dist/components/FormatOptionsDropdown.d.ts +37 -0
- package/dist/components/FormatOptionsDropdown.d.ts.map +1 -0
- package/dist/components/FormatOptionsDropdown.js +62 -0
- package/dist/components/FormatOptionsDropdown.js.map +1 -0
- package/dist/components/SplitButton.cjs +87 -0
- package/dist/components/SplitButton.cjs.map +1 -0
- package/dist/components/SplitButton.d.ts +32 -0
- package/dist/components/SplitButton.d.ts.map +1 -0
- package/dist/components/SplitButton.js +87 -0
- package/dist/components/SplitButton.js.map +1 -0
- package/dist/components/icon-menu-picker.cjs +1 -1
- package/dist/components/icon-menu-picker.cjs.map +1 -1
- package/dist/components/icon-menu-picker.js +1 -1
- package/dist/components/icon-menu-picker.js.map +1 -1
- package/dist/components.cjs +7 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.ts +3 -0
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +7 -0
- package/dist/components.js.map +1 -1
- package/dist/data/Badge.d.ts +1 -1
- package/dist/data/ProgressBar.cjs +4 -2
- package/dist/data/ProgressBar.cjs.map +1 -1
- package/dist/data/ProgressBar.d.ts +1 -1
- package/dist/data/ProgressBar.d.ts.map +1 -1
- package/dist/data/ProgressBar.js +4 -2
- package/dist/data/ProgressBar.js.map +1 -1
- package/dist/data/TaskManager.cjs +162 -0
- package/dist/data/TaskManager.cjs.map +1 -0
- package/dist/data/TaskManager.d.ts +21 -0
- package/dist/data/TaskManager.d.ts.map +1 -0
- package/dist/data/TaskManager.js +162 -0
- package/dist/data/TaskManager.js.map +1 -0
- package/dist/data/TaskProgress.cjs +125 -0
- package/dist/data/TaskProgress.cjs.map +1 -0
- package/dist/data/TaskProgress.d.ts +20 -0
- package/dist/data/TaskProgress.d.ts.map +1 -0
- package/dist/data/TaskProgress.js +125 -0
- package/dist/data/TaskProgress.js.map +1 -0
- package/dist/data/TaskSnapshot.cjs +23 -0
- package/dist/data/TaskSnapshot.cjs.map +1 -0
- package/dist/data/TaskSnapshot.d.ts +49 -0
- package/dist/data/TaskSnapshot.d.ts.map +1 -0
- package/dist/data/TaskSnapshot.js +23 -0
- package/dist/data/TaskSnapshot.js.map +1 -0
- package/dist/data/TimeseriesPanel.axes.cjs +39 -0
- package/dist/data/TimeseriesPanel.axes.cjs.map +1 -0
- package/dist/data/TimeseriesPanel.axes.d.ts +28 -0
- package/dist/data/TimeseriesPanel.axes.d.ts.map +1 -0
- package/dist/data/TimeseriesPanel.axes.js +39 -0
- package/dist/data/TimeseriesPanel.axes.js.map +1 -0
- package/dist/data/TimeseriesPanel.cjs +324 -0
- package/dist/data/TimeseriesPanel.cjs.map +1 -0
- package/dist/data/TimeseriesPanel.d.ts +141 -0
- package/dist/data/TimeseriesPanel.d.ts.map +1 -0
- package/dist/data/TimeseriesPanel.js +324 -0
- package/dist/data/TimeseriesPanel.js.map +1 -0
- package/dist/data/Tree.cjs.map +1 -1
- package/dist/data/Tree.d.ts.map +1 -1
- package/dist/data/Tree.js.map +1 -1
- package/dist/data/TreeNode.cjs +44 -7
- package/dist/data/TreeNode.cjs.map +1 -1
- package/dist/data/TreeNode.d.ts +19 -1
- package/dist/data/TreeNode.d.ts.map +1 -1
- package/dist/data/TreeNode.js +44 -7
- package/dist/data/TreeNode.js.map +1 -1
- package/dist/data/diagnostics/DiagnosticsDetailPanel.cjs +66 -44
- package/dist/data/diagnostics/DiagnosticsDetailPanel.cjs.map +1 -1
- package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts +21 -1
- package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts.map +1 -1
- package/dist/data/diagnostics/DiagnosticsDetailPanel.js +65 -43
- package/dist/data/diagnostics/DiagnosticsDetailPanel.js.map +1 -1
- package/dist/data/diagnostics/DiagnosticsTree.cjs +2 -1
- package/dist/data/diagnostics/DiagnosticsTree.cjs.map +1 -1
- package/dist/data/diagnostics/DiagnosticsTree.js +2 -1
- package/dist/data/diagnostics/DiagnosticsTree.js.map +1 -1
- package/dist/data/diagnostics/FrameSourceWindow.cjs +37 -0
- package/dist/data/diagnostics/FrameSourceWindow.cjs.map +1 -0
- package/dist/data/diagnostics/FrameSourceWindow.d.ts +15 -0
- package/dist/data/diagnostics/FrameSourceWindow.d.ts.map +1 -0
- package/dist/data/diagnostics/FrameSourceWindow.js +37 -0
- package/dist/data/diagnostics/FrameSourceWindow.js.map +1 -0
- package/dist/data/diagnostics/JvmStackTrace.cjs +25 -12
- package/dist/data/diagnostics/JvmStackTrace.cjs.map +1 -1
- package/dist/data/diagnostics/JvmStackTrace.d.ts +8 -1
- package/dist/data/diagnostics/JvmStackTrace.d.ts.map +1 -1
- package/dist/data/diagnostics/JvmStackTrace.js +26 -13
- package/dist/data/diagnostics/JvmStackTrace.js.map +1 -1
- package/dist/data/diagnostics/ThreadCard.cjs +9 -2
- package/dist/data/diagnostics/ThreadCard.cjs.map +1 -1
- package/dist/data/diagnostics/ThreadCard.d.ts +3 -1
- package/dist/data/diagnostics/ThreadCard.d.ts.map +1 -1
- package/dist/data/diagnostics/ThreadCard.js +9 -2
- package/dist/data/diagnostics/ThreadCard.js.map +1 -1
- package/dist/data/diagnostics/jvm-stacktrace.cjs +10 -1
- package/dist/data/diagnostics/jvm-stacktrace.cjs.map +1 -1
- package/dist/data/diagnostics/jvm-stacktrace.d.ts.map +1 -1
- package/dist/data/diagnostics/jvm-stacktrace.js +10 -1
- package/dist/data/diagnostics/jvm-stacktrace.js.map +1 -1
- package/dist/data/diagnostics/utils.cjs +0 -12
- package/dist/data/diagnostics/utils.cjs.map +1 -1
- package/dist/data/diagnostics/utils.d.ts +1 -1
- package/dist/data/diagnostics/utils.d.ts.map +1 -1
- package/dist/data/diagnostics/utils.js +0 -12
- package/dist/data/diagnostics/utils.js.map +1 -1
- package/dist/data/task-status.cjs +108 -0
- package/dist/data/task-status.cjs.map +1 -0
- package/dist/data/task-status.d.ts +21 -0
- package/dist/data/task-status.d.ts.map +1 -0
- package/dist/data/task-status.js +108 -0
- package/dist/data/task-status.js.map +1 -0
- package/dist/data.cjs +21 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +7 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +21 -0
- package/dist/data.js.map +1 -1
- package/dist/hooks/use-task-run.cjs +137 -0
- package/dist/hooks/use-task-run.cjs.map +1 -0
- package/dist/hooks/use-task-run.d.ts +35 -0
- package/dist/hooks/use-task-run.d.ts.map +1 -0
- package/dist/hooks/use-task-run.js +137 -0
- package/dist/hooks/use-task-run.js.map +1 -0
- package/dist/hooks.cjs +3 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +1 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +3 -0
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +37 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/color.cjs +33 -0
- package/dist/lib/color.cjs.map +1 -0
- package/dist/lib/color.d.ts +10 -0
- package/dist/lib/color.d.ts.map +1 -0
- package/dist/lib/color.js +33 -0
- package/dist/lib/color.js.map +1 -0
- package/dist/lib/format.cjs +77 -0
- package/dist/lib/format.cjs.map +1 -0
- package/dist/lib/format.d.ts +30 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +77 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/overlay/DropdownMenu.cjs +18 -2
- package/dist/overlay/DropdownMenu.cjs.map +1 -1
- package/dist/overlay/DropdownMenu.d.ts +3 -1
- package/dist/overlay/DropdownMenu.d.ts.map +1 -1
- package/dist/overlay/DropdownMenu.js +18 -2
- package/dist/overlay/DropdownMenu.js.map +1 -1
- package/dist/rpc/types.cjs.map +1 -1
- package/dist/rpc/types.d.ts +4 -0
- package/dist/rpc/types.d.ts.map +1 -1
- package/dist/rpc/types.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/utils.cjs +6 -0
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +6 -0
- package/dist/utils.js.map +1 -1
- package/package.json +4 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
require("react");
|
|
5
|
+
const Icon = require("../data/Icon.cjs");
|
|
6
|
+
const SplitButton = require("./SplitButton.cjs");
|
|
7
|
+
const UiEye = require("../icons/components/UiEye.cjs");
|
|
8
|
+
const UiFileCode = require("../icons/components/UiFileCode.cjs");
|
|
9
|
+
const UiTable = require("../icons/components/UiTable.cjs");
|
|
10
|
+
const UiComment = require("../icons/components/UiComment.cjs");
|
|
11
|
+
const UiCheck = require("../icons/components/UiCheck.cjs");
|
|
12
|
+
const CLICKY_FORMAT_OPTIONS = [
|
|
13
|
+
{ value: "clicky", label: "Clicky", description: "Rendered Clicky JSON with the rich Clicky viewer", icon: UiEye.UiEye },
|
|
14
|
+
{ value: "json", label: "JSON", description: "Plain JSON for inspecting the raw response body", icon: UiFileCode.UiFileCode },
|
|
15
|
+
{ value: "yaml", label: "YAML", description: "YAML for config-friendly inspection and export", icon: UiFileCode.UiFileCode },
|
|
16
|
+
{ value: "csv", label: "CSV", description: "Comma-separated values for spreadsheets and imports", icon: UiTable.UiTable },
|
|
17
|
+
{ value: "markdown", label: "Markdown", description: "Markdown formatted for docs, comments, and chat", icon: UiFileCode.UiFileCode },
|
|
18
|
+
{ value: "html", label: "HTML", description: "Browser-ready HTML preview of the formatted output", icon: UiFileCode.UiFileCode },
|
|
19
|
+
{ value: "pdf", label: "PDF", description: "Portable document for sharing and printing", icon: UiFileCode.UiFileCode },
|
|
20
|
+
{ value: "pretty", label: "Pretty", description: "Human-readable plain text output from the formatter", icon: UiFileCode.UiFileCode },
|
|
21
|
+
{ value: "excel", label: "Excel", description: "Spreadsheet workbook for offline analysis", icon: UiTable.UiTable },
|
|
22
|
+
{ value: "slack", label: "Slack", description: "Slack Block Kit JSON for chat-native output", icon: UiComment.UiComment }
|
|
23
|
+
];
|
|
24
|
+
function FormatOptionsDropdown({
|
|
25
|
+
value,
|
|
26
|
+
onChange,
|
|
27
|
+
options = CLICKY_FORMAT_OPTIONS,
|
|
28
|
+
label,
|
|
29
|
+
variant = "outline",
|
|
30
|
+
size = "default",
|
|
31
|
+
className
|
|
32
|
+
}) {
|
|
33
|
+
const active = options.find((option) => option.value === value);
|
|
34
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
35
|
+
SplitButton.SplitButton,
|
|
36
|
+
{
|
|
37
|
+
label: label ?? (active == null ? void 0 : active.label) ?? value,
|
|
38
|
+
...(active == null ? void 0 : active.icon) ? { icon: active.icon } : {},
|
|
39
|
+
onClick: () => onChange(value),
|
|
40
|
+
variant,
|
|
41
|
+
size,
|
|
42
|
+
title: "Choose format",
|
|
43
|
+
...className ? { className } : {},
|
|
44
|
+
menuClassName: "min-w-[18rem]",
|
|
45
|
+
items: options.map((option) => ({
|
|
46
|
+
...option.icon ? { icon: option.icon } : {},
|
|
47
|
+
...option.description ? { title: option.description } : {},
|
|
48
|
+
label: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col", children: [
|
|
49
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2 font-medium", children: [
|
|
50
|
+
option.label,
|
|
51
|
+
option.value === value && /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiCheck.UiCheck, className: "text-xs text-muted-foreground" })
|
|
52
|
+
] }),
|
|
53
|
+
option.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: option.description })
|
|
54
|
+
] }),
|
|
55
|
+
onSelect: () => onChange(option.value)
|
|
56
|
+
}))
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
exports.CLICKY_FORMAT_OPTIONS = CLICKY_FORMAT_OPTIONS;
|
|
61
|
+
exports.FormatOptionsDropdown = FormatOptionsDropdown;
|
|
62
|
+
//# sourceMappingURL=FormatOptionsDropdown.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormatOptionsDropdown.cjs","sources":["../../src/components/FormatOptionsDropdown.tsx"],"sourcesContent":["import { type ReactNode } from \"react\";\nimport { type ButtonProps } from \"./button\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiCheck, UiComment, UiEye, UiFileCode, UiTable } from \"../icons\";\nimport { SplitButton } from \"./SplitButton\";\n\nexport type FormatOption = {\n /** Stable identifier emitted by `onChange`. */\n value: string;\n /** Visible label. */\n label: string;\n /** Secondary description shown in the menu. */\n description?: string;\n /** Icon rendered before the label. */\n icon?: StaticIconComponent;\n};\n\n/**\n * Clicky output formats with their label, description, and icon. Mirrors the\n * metadata in `Clicky.tsx`'s `getRemoteFormatMeta` so the format picker can live\n * as a standalone component.\n */\nexport const CLICKY_FORMAT_OPTIONS: FormatOption[] = [\n { value: \"clicky\", label: \"Clicky\", description: \"Rendered Clicky JSON with the rich Clicky viewer\", icon: UiEye },\n { value: \"json\", label: \"JSON\", description: \"Plain JSON for inspecting the raw response body\", icon: UiFileCode },\n { value: \"yaml\", label: \"YAML\", description: \"YAML for config-friendly inspection and export\", icon: UiFileCode },\n { value: \"csv\", label: \"CSV\", description: \"Comma-separated values for spreadsheets and imports\", icon: UiTable },\n { value: \"markdown\", label: \"Markdown\", description: \"Markdown formatted for docs, comments, and chat\", icon: UiFileCode },\n { value: \"html\", label: \"HTML\", description: \"Browser-ready HTML preview of the formatted output\", icon: UiFileCode },\n { value: \"pdf\", label: \"PDF\", description: \"Portable document for sharing and printing\", icon: UiFileCode },\n { value: \"pretty\", label: \"Pretty\", description: \"Human-readable plain text output from the formatter\", icon: UiFileCode },\n { value: \"excel\", label: \"Excel\", description: \"Spreadsheet workbook for offline analysis\", icon: UiTable },\n { value: \"slack\", label: \"Slack\", description: \"Slack Block Kit JSON for chat-native output\", icon: UiComment },\n];\n\nexport type FormatOptionsDropdownProps = {\n /** Active format value. */\n value: string;\n /** Called with the chosen format value. */\n onChange: (value: string) => void;\n /** Available formats. Defaults to the clicky output formats. */\n options?: FormatOption[];\n /** Override the primary button label. Defaults to the active option's label. */\n label?: ReactNode;\n /** Variant forwarded to the split button. */\n variant?: ButtonProps[\"variant\"];\n /** Size forwarded to the split button. */\n size?: ButtonProps[\"size\"];\n /** Classes applied to the wrapper. */\n className?: string;\n};\n\nexport function FormatOptionsDropdown({\n value,\n onChange,\n options = CLICKY_FORMAT_OPTIONS,\n label,\n variant = \"outline\",\n size = \"default\",\n className,\n}: FormatOptionsDropdownProps) {\n const active = options.find((option) => option.value === value);\n\n return (\n <SplitButton\n label={label ?? active?.label ?? value}\n {...(active?.icon ? { icon: active.icon } : {})}\n onClick={() => onChange(value)}\n variant={variant}\n size={size}\n title=\"Choose format\"\n {...(className ? { className } : {})}\n menuClassName=\"min-w-[18rem]\"\n items={options.map((option) => ({\n ...(option.icon ? { icon: option.icon } : {}),\n ...(option.description ? { title: option.description } : {}),\n label: (\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"flex items-center gap-2 font-medium\">\n {option.label}\n {option.value === value && <Icon icon={UiCheck} className=\"text-xs text-muted-foreground\" />}\n </span>\n {option.description && (\n <span className=\"text-xs text-muted-foreground\">{option.description}</span>\n )}\n </span>\n ),\n onSelect: () => onChange(option.value),\n }))}\n />\n );\n}\n"],"names":["UiEye","UiFileCode","UiTable","UiComment","jsx","SplitButton","jsxs","Icon","UiCheck"],"mappings":";;;;;;;;;;;AAsBO,MAAM,wBAAwC;AAAA,EACnD,EAAE,OAAO,UAAU,OAAO,UAAU,aAAa,oDAAoD,MAAMA,YAAA;AAAA,EAC3G,EAAE,OAAO,QAAQ,OAAO,QAAQ,aAAa,mDAAmD,MAAMC,sBAAA;AAAA,EACtG,EAAE,OAAO,QAAQ,OAAO,QAAQ,aAAa,kDAAkD,MAAMA,sBAAA;AAAA,EACrG,EAAE,OAAO,OAAO,OAAO,OAAO,aAAa,uDAAuD,MAAMC,gBAAA;AAAA,EACxG,EAAE,OAAO,YAAY,OAAO,YAAY,aAAa,mDAAmD,MAAMD,sBAAA;AAAA,EAC9G,EAAE,OAAO,QAAQ,OAAO,QAAQ,aAAa,sDAAsD,MAAMA,sBAAA;AAAA,EACzG,EAAE,OAAO,OAAO,OAAO,OAAO,aAAa,8CAA8C,MAAMA,sBAAA;AAAA,EAC/F,EAAE,OAAO,UAAU,OAAO,UAAU,aAAa,uDAAuD,MAAMA,sBAAA;AAAA,EAC9G,EAAE,OAAO,SAAS,OAAO,SAAS,aAAa,6CAA6C,MAAMC,gBAAA;AAAA,EAClG,EAAE,OAAO,SAAS,OAAO,SAAS,aAAa,+CAA+C,MAAMC,UAAAA,UAAA;AACtG;AAmBO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP;AACF,GAA+B;AAC7B,QAAM,SAAS,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,KAAK;AAE9D,SACEC,2BAAAA;AAAAA,IAACC,YAAAA;AAAAA,IAAA;AAAA,MACC,OAAO,UAAS,iCAAQ,UAAS;AAAA,MAChC,IAAI,iCAAQ,QAAO,EAAE,MAAM,OAAO,KAAA,IAAS,CAAA;AAAA,MAC5C,SAAS,MAAM,SAAS,KAAK;AAAA,MAC7B;AAAA,MACA;AAAA,MACA,OAAM;AAAA,MACL,GAAI,YAAY,EAAE,UAAA,IAAc,CAAA;AAAA,MACjC,eAAc;AAAA,MACd,OAAO,QAAQ,IAAI,CAAC,YAAY;AAAA,QAC9B,GAAI,OAAO,OAAO,EAAE,MAAM,OAAO,KAAA,IAAS,CAAA;AAAA,QAC1C,GAAI,OAAO,cAAc,EAAE,OAAO,OAAO,YAAA,IAAgB,CAAA;AAAA,QACzD,OACEC,2BAAAA,KAAC,QAAA,EAAK,WAAU,yBACd,UAAA;AAAA,UAAAA,2BAAAA,KAAC,QAAA,EAAK,WAAU,uCACb,UAAA;AAAA,YAAA,OAAO;AAAA,YACP,OAAO,UAAU,SAASF,+BAACG,KAAAA,QAAK,MAAMC,QAAAA,SAAS,WAAU,gCAAA,CAAgC;AAAA,UAAA,GAC5F;AAAA,UACC,OAAO,eACNJ,2BAAAA,IAAC,UAAK,WAAU,iCAAiC,iBAAO,YAAA,CAAY;AAAA,QAAA,GAExE;AAAA,QAEF,UAAU,MAAM,SAAS,OAAO,KAAK;AAAA,MAAA,EACrC;AAAA,IAAA;AAAA,EAAA;AAGR;;;"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ButtonProps } from './button';
|
|
3
|
+
import { StaticIconComponent } from '../data/Icon';
|
|
4
|
+
export type FormatOption = {
|
|
5
|
+
/** Stable identifier emitted by `onChange`. */
|
|
6
|
+
value: string;
|
|
7
|
+
/** Visible label. */
|
|
8
|
+
label: string;
|
|
9
|
+
/** Secondary description shown in the menu. */
|
|
10
|
+
description?: string;
|
|
11
|
+
/** Icon rendered before the label. */
|
|
12
|
+
icon?: StaticIconComponent;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Clicky output formats with their label, description, and icon. Mirrors the
|
|
16
|
+
* metadata in `Clicky.tsx`'s `getRemoteFormatMeta` so the format picker can live
|
|
17
|
+
* as a standalone component.
|
|
18
|
+
*/
|
|
19
|
+
export declare const CLICKY_FORMAT_OPTIONS: FormatOption[];
|
|
20
|
+
export type FormatOptionsDropdownProps = {
|
|
21
|
+
/** Active format value. */
|
|
22
|
+
value: string;
|
|
23
|
+
/** Called with the chosen format value. */
|
|
24
|
+
onChange: (value: string) => void;
|
|
25
|
+
/** Available formats. Defaults to the clicky output formats. */
|
|
26
|
+
options?: FormatOption[];
|
|
27
|
+
/** Override the primary button label. Defaults to the active option's label. */
|
|
28
|
+
label?: ReactNode;
|
|
29
|
+
/** Variant forwarded to the split button. */
|
|
30
|
+
variant?: ButtonProps["variant"];
|
|
31
|
+
/** Size forwarded to the split button. */
|
|
32
|
+
size?: ButtonProps["size"];
|
|
33
|
+
/** Classes applied to the wrapper. */
|
|
34
|
+
className?: string;
|
|
35
|
+
};
|
|
36
|
+
export declare function FormatOptionsDropdown({ value, onChange, options, label, variant, size, className, }: FormatOptionsDropdownProps): import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
//# sourceMappingURL=FormatOptionsDropdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormatOptionsDropdown.d.ts","sourceRoot":"","sources":["../../src/components/FormatOptionsDropdown.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAI9D,MAAM,MAAM,YAAY,GAAG;IACzB,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,IAAI,CAAC,EAAE,mBAAmB,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,YAAY,EAW/C,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,gEAAgE;IAChE,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,gFAAgF;IAChF,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACjC,0CAA0C;IAC1C,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,QAAQ,EACR,OAA+B,EAC/B,KAAK,EACL,OAAmB,EACnB,IAAgB,EAChB,SAAS,GACV,EAAE,0BAA0B,2CA+B5B"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import { Icon } from "../data/Icon.js";
|
|
4
|
+
import { SplitButton } from "./SplitButton.js";
|
|
5
|
+
import { UiEye } from "../icons/components/UiEye.js";
|
|
6
|
+
import { UiFileCode } from "../icons/components/UiFileCode.js";
|
|
7
|
+
import { UiTable } from "../icons/components/UiTable.js";
|
|
8
|
+
import { UiComment } from "../icons/components/UiComment.js";
|
|
9
|
+
import { UiCheck } from "../icons/components/UiCheck.js";
|
|
10
|
+
const CLICKY_FORMAT_OPTIONS = [
|
|
11
|
+
{ value: "clicky", label: "Clicky", description: "Rendered Clicky JSON with the rich Clicky viewer", icon: UiEye },
|
|
12
|
+
{ value: "json", label: "JSON", description: "Plain JSON for inspecting the raw response body", icon: UiFileCode },
|
|
13
|
+
{ value: "yaml", label: "YAML", description: "YAML for config-friendly inspection and export", icon: UiFileCode },
|
|
14
|
+
{ value: "csv", label: "CSV", description: "Comma-separated values for spreadsheets and imports", icon: UiTable },
|
|
15
|
+
{ value: "markdown", label: "Markdown", description: "Markdown formatted for docs, comments, and chat", icon: UiFileCode },
|
|
16
|
+
{ value: "html", label: "HTML", description: "Browser-ready HTML preview of the formatted output", icon: UiFileCode },
|
|
17
|
+
{ value: "pdf", label: "PDF", description: "Portable document for sharing and printing", icon: UiFileCode },
|
|
18
|
+
{ value: "pretty", label: "Pretty", description: "Human-readable plain text output from the formatter", icon: UiFileCode },
|
|
19
|
+
{ value: "excel", label: "Excel", description: "Spreadsheet workbook for offline analysis", icon: UiTable },
|
|
20
|
+
{ value: "slack", label: "Slack", description: "Slack Block Kit JSON for chat-native output", icon: UiComment }
|
|
21
|
+
];
|
|
22
|
+
function FormatOptionsDropdown({
|
|
23
|
+
value,
|
|
24
|
+
onChange,
|
|
25
|
+
options = CLICKY_FORMAT_OPTIONS,
|
|
26
|
+
label,
|
|
27
|
+
variant = "outline",
|
|
28
|
+
size = "default",
|
|
29
|
+
className
|
|
30
|
+
}) {
|
|
31
|
+
const active = options.find((option) => option.value === value);
|
|
32
|
+
return /* @__PURE__ */ jsx(
|
|
33
|
+
SplitButton,
|
|
34
|
+
{
|
|
35
|
+
label: label ?? (active == null ? void 0 : active.label) ?? value,
|
|
36
|
+
...(active == null ? void 0 : active.icon) ? { icon: active.icon } : {},
|
|
37
|
+
onClick: () => onChange(value),
|
|
38
|
+
variant,
|
|
39
|
+
size,
|
|
40
|
+
title: "Choose format",
|
|
41
|
+
...className ? { className } : {},
|
|
42
|
+
menuClassName: "min-w-[18rem]",
|
|
43
|
+
items: options.map((option) => ({
|
|
44
|
+
...option.icon ? { icon: option.icon } : {},
|
|
45
|
+
...option.description ? { title: option.description } : {},
|
|
46
|
+
label: /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col", children: [
|
|
47
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 font-medium", children: [
|
|
48
|
+
option.label,
|
|
49
|
+
option.value === value && /* @__PURE__ */ jsx(Icon, { icon: UiCheck, className: "text-xs text-muted-foreground" })
|
|
50
|
+
] }),
|
|
51
|
+
option.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: option.description })
|
|
52
|
+
] }),
|
|
53
|
+
onSelect: () => onChange(option.value)
|
|
54
|
+
}))
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
CLICKY_FORMAT_OPTIONS,
|
|
60
|
+
FormatOptionsDropdown
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=FormatOptionsDropdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormatOptionsDropdown.js","sources":["../../src/components/FormatOptionsDropdown.tsx"],"sourcesContent":["import { type ReactNode } from \"react\";\nimport { type ButtonProps } from \"./button\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiCheck, UiComment, UiEye, UiFileCode, UiTable } from \"../icons\";\nimport { SplitButton } from \"./SplitButton\";\n\nexport type FormatOption = {\n /** Stable identifier emitted by `onChange`. */\n value: string;\n /** Visible label. */\n label: string;\n /** Secondary description shown in the menu. */\n description?: string;\n /** Icon rendered before the label. */\n icon?: StaticIconComponent;\n};\n\n/**\n * Clicky output formats with their label, description, and icon. Mirrors the\n * metadata in `Clicky.tsx`'s `getRemoteFormatMeta` so the format picker can live\n * as a standalone component.\n */\nexport const CLICKY_FORMAT_OPTIONS: FormatOption[] = [\n { value: \"clicky\", label: \"Clicky\", description: \"Rendered Clicky JSON with the rich Clicky viewer\", icon: UiEye },\n { value: \"json\", label: \"JSON\", description: \"Plain JSON for inspecting the raw response body\", icon: UiFileCode },\n { value: \"yaml\", label: \"YAML\", description: \"YAML for config-friendly inspection and export\", icon: UiFileCode },\n { value: \"csv\", label: \"CSV\", description: \"Comma-separated values for spreadsheets and imports\", icon: UiTable },\n { value: \"markdown\", label: \"Markdown\", description: \"Markdown formatted for docs, comments, and chat\", icon: UiFileCode },\n { value: \"html\", label: \"HTML\", description: \"Browser-ready HTML preview of the formatted output\", icon: UiFileCode },\n { value: \"pdf\", label: \"PDF\", description: \"Portable document for sharing and printing\", icon: UiFileCode },\n { value: \"pretty\", label: \"Pretty\", description: \"Human-readable plain text output from the formatter\", icon: UiFileCode },\n { value: \"excel\", label: \"Excel\", description: \"Spreadsheet workbook for offline analysis\", icon: UiTable },\n { value: \"slack\", label: \"Slack\", description: \"Slack Block Kit JSON for chat-native output\", icon: UiComment },\n];\n\nexport type FormatOptionsDropdownProps = {\n /** Active format value. */\n value: string;\n /** Called with the chosen format value. */\n onChange: (value: string) => void;\n /** Available formats. Defaults to the clicky output formats. */\n options?: FormatOption[];\n /** Override the primary button label. Defaults to the active option's label. */\n label?: ReactNode;\n /** Variant forwarded to the split button. */\n variant?: ButtonProps[\"variant\"];\n /** Size forwarded to the split button. */\n size?: ButtonProps[\"size\"];\n /** Classes applied to the wrapper. */\n className?: string;\n};\n\nexport function FormatOptionsDropdown({\n value,\n onChange,\n options = CLICKY_FORMAT_OPTIONS,\n label,\n variant = \"outline\",\n size = \"default\",\n className,\n}: FormatOptionsDropdownProps) {\n const active = options.find((option) => option.value === value);\n\n return (\n <SplitButton\n label={label ?? active?.label ?? value}\n {...(active?.icon ? { icon: active.icon } : {})}\n onClick={() => onChange(value)}\n variant={variant}\n size={size}\n title=\"Choose format\"\n {...(className ? { className } : {})}\n menuClassName=\"min-w-[18rem]\"\n items={options.map((option) => ({\n ...(option.icon ? { icon: option.icon } : {}),\n ...(option.description ? { title: option.description } : {}),\n label: (\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"flex items-center gap-2 font-medium\">\n {option.label}\n {option.value === value && <Icon icon={UiCheck} className=\"text-xs text-muted-foreground\" />}\n </span>\n {option.description && (\n <span className=\"text-xs text-muted-foreground\">{option.description}</span>\n )}\n </span>\n ),\n onSelect: () => onChange(option.value),\n }))}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AAsBO,MAAM,wBAAwC;AAAA,EACnD,EAAE,OAAO,UAAU,OAAO,UAAU,aAAa,oDAAoD,MAAM,MAAA;AAAA,EAC3G,EAAE,OAAO,QAAQ,OAAO,QAAQ,aAAa,mDAAmD,MAAM,WAAA;AAAA,EACtG,EAAE,OAAO,QAAQ,OAAO,QAAQ,aAAa,kDAAkD,MAAM,WAAA;AAAA,EACrG,EAAE,OAAO,OAAO,OAAO,OAAO,aAAa,uDAAuD,MAAM,QAAA;AAAA,EACxG,EAAE,OAAO,YAAY,OAAO,YAAY,aAAa,mDAAmD,MAAM,WAAA;AAAA,EAC9G,EAAE,OAAO,QAAQ,OAAO,QAAQ,aAAa,sDAAsD,MAAM,WAAA;AAAA,EACzG,EAAE,OAAO,OAAO,OAAO,OAAO,aAAa,8CAA8C,MAAM,WAAA;AAAA,EAC/F,EAAE,OAAO,UAAU,OAAO,UAAU,aAAa,uDAAuD,MAAM,WAAA;AAAA,EAC9G,EAAE,OAAO,SAAS,OAAO,SAAS,aAAa,6CAA6C,MAAM,QAAA;AAAA,EAClG,EAAE,OAAO,SAAS,OAAO,SAAS,aAAa,+CAA+C,MAAM,UAAA;AACtG;AAmBO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP;AACF,GAA+B;AAC7B,QAAM,SAAS,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,KAAK;AAE9D,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAO,UAAS,iCAAQ,UAAS;AAAA,MAChC,IAAI,iCAAQ,QAAO,EAAE,MAAM,OAAO,KAAA,IAAS,CAAA;AAAA,MAC5C,SAAS,MAAM,SAAS,KAAK;AAAA,MAC7B;AAAA,MACA;AAAA,MACA,OAAM;AAAA,MACL,GAAI,YAAY,EAAE,UAAA,IAAc,CAAA;AAAA,MACjC,eAAc;AAAA,MACd,OAAO,QAAQ,IAAI,CAAC,YAAY;AAAA,QAC9B,GAAI,OAAO,OAAO,EAAE,MAAM,OAAO,KAAA,IAAS,CAAA;AAAA,QAC1C,GAAI,OAAO,cAAc,EAAE,OAAO,OAAO,YAAA,IAAgB,CAAA;AAAA,QACzD,OACE,qBAAC,QAAA,EAAK,WAAU,yBACd,UAAA;AAAA,UAAA,qBAAC,QAAA,EAAK,WAAU,uCACb,UAAA;AAAA,YAAA,OAAO;AAAA,YACP,OAAO,UAAU,SAAS,oBAAC,QAAK,MAAM,SAAS,WAAU,gCAAA,CAAgC;AAAA,UAAA,GAC5F;AAAA,UACC,OAAO,eACN,oBAAC,UAAK,WAAU,iCAAiC,iBAAO,YAAA,CAAY;AAAA,QAAA,GAExE;AAAA,QAEF,UAAU,MAAM,SAAS,OAAO,KAAK;AAAA,MAAA,EACrC;AAAA,IAAA;AAAA,EAAA;AAGR;"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const react = require("react");
|
|
5
|
+
const utils = require("../lib/utils.cjs");
|
|
6
|
+
const button = require("./button.cjs");
|
|
7
|
+
const Icon = require("../data/Icon.cjs");
|
|
8
|
+
const DropdownMenu = require("../overlay/DropdownMenu.cjs");
|
|
9
|
+
const UiChevronUp = require("../icons/components/UiChevronUp.cjs");
|
|
10
|
+
const UiChevronDown = require("../icons/components/UiChevronDown.cjs");
|
|
11
|
+
function SplitButton({
|
|
12
|
+
label,
|
|
13
|
+
onClick,
|
|
14
|
+
items,
|
|
15
|
+
icon,
|
|
16
|
+
variant = "default",
|
|
17
|
+
size = "default",
|
|
18
|
+
loading = false,
|
|
19
|
+
disabled = false,
|
|
20
|
+
align = "right",
|
|
21
|
+
title,
|
|
22
|
+
className,
|
|
23
|
+
menuClassName
|
|
24
|
+
}) {
|
|
25
|
+
const [open, setOpen] = react.useState(false);
|
|
26
|
+
if (items.length === 0) {
|
|
27
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
28
|
+
button.Button,
|
|
29
|
+
{
|
|
30
|
+
variant,
|
|
31
|
+
size,
|
|
32
|
+
loading,
|
|
33
|
+
disabled,
|
|
34
|
+
onClick,
|
|
35
|
+
className,
|
|
36
|
+
children: [
|
|
37
|
+
icon && /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { ...typeof icon === "string" ? { name: icon } : { icon } }),
|
|
38
|
+
label
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("inline-flex items-stretch", className), children: [
|
|
44
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
45
|
+
button.Button,
|
|
46
|
+
{
|
|
47
|
+
variant,
|
|
48
|
+
size,
|
|
49
|
+
loading,
|
|
50
|
+
disabled,
|
|
51
|
+
onClick,
|
|
52
|
+
className: "rounded-r-none",
|
|
53
|
+
children: [
|
|
54
|
+
icon && /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { ...typeof icon === "string" ? { name: icon } : { icon } }),
|
|
55
|
+
label
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
),
|
|
59
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
60
|
+
DropdownMenu.DropdownMenu,
|
|
61
|
+
{
|
|
62
|
+
items,
|
|
63
|
+
align,
|
|
64
|
+
className: "items-stretch",
|
|
65
|
+
...menuClassName ? { menuClassName } : {},
|
|
66
|
+
onOpenChange: setOpen,
|
|
67
|
+
trigger: /* @__PURE__ */ jsxRuntime.jsx(
|
|
68
|
+
button.Button,
|
|
69
|
+
{
|
|
70
|
+
variant,
|
|
71
|
+
size: size === "default" ? "icon" : size,
|
|
72
|
+
disabled,
|
|
73
|
+
title,
|
|
74
|
+
"aria-label": title ?? "Open menu",
|
|
75
|
+
className: utils.cn(
|
|
76
|
+
"h-auto self-stretch rounded-l-none border-l border-l-current/25 px-2",
|
|
77
|
+
size !== "icon" && "w-8"
|
|
78
|
+
),
|
|
79
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: open ? UiChevronUp.UiChevronUp : UiChevronDown.UiChevronDown })
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
] });
|
|
85
|
+
}
|
|
86
|
+
exports.SplitButton = SplitButton;
|
|
87
|
+
//# sourceMappingURL=SplitButton.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SplitButton.cjs","sources":["../../src/components/SplitButton.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button, type ButtonProps } from \"./button\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiChevronDown, UiChevronUp } from \"../icons\";\nimport { DropdownMenu, type DropdownMenuItem } from \"../overlay/DropdownMenu\";\n\nexport type SplitButtonProps = {\n /** Content of the primary action button. */\n label: ReactNode;\n /** Primary action handler. */\n onClick: () => void;\n /** Secondary actions listed in the dropdown menu. */\n items: DropdownMenuItem[];\n /** Icon shown before the primary label. */\n icon?: string | StaticIconComponent;\n /** Variant forwarded to both halves. */\n variant?: ButtonProps[\"variant\"];\n /** Size forwarded to both halves. */\n size?: ButtonProps[\"size\"];\n /** Show a spinner and disable the primary half while pending. */\n loading?: boolean;\n /** Disable both halves. */\n disabled?: boolean;\n /** Horizontal alignment of the menu relative to the trigger. */\n align?: \"left\" | \"right\";\n /** Browser tooltip / accessible label for the chevron trigger. */\n title?: string;\n /** Classes applied to the wrapper. */\n className?: string;\n /** Classes applied to the floating menu. */\n menuClassName?: string;\n};\n\nexport function SplitButton({\n label,\n onClick,\n items,\n icon,\n variant = \"default\",\n size = \"default\",\n loading = false,\n disabled = false,\n align = \"right\",\n title,\n className,\n menuClassName,\n}: SplitButtonProps) {\n const [open, setOpen] = useState(false);\n\n if (items.length === 0) {\n return (\n <Button\n variant={variant}\n size={size}\n loading={loading}\n disabled={disabled}\n onClick={onClick}\n className={className}\n >\n {icon && <Icon {...(typeof icon === \"string\" ? { name: icon } : { icon })} />}\n {label}\n </Button>\n );\n }\n\n return (\n <div className={cn(\"inline-flex items-stretch\", className)}>\n <Button\n variant={variant}\n size={size}\n loading={loading}\n disabled={disabled}\n onClick={onClick}\n className=\"rounded-r-none\"\n >\n {icon && <Icon {...(typeof icon === \"string\" ? { name: icon } : { icon })} />}\n {label}\n </Button>\n <DropdownMenu\n items={items}\n align={align}\n className=\"items-stretch\"\n {...(menuClassName ? { menuClassName } : {})}\n onOpenChange={setOpen}\n trigger={\n <Button\n variant={variant}\n size={size === \"default\" ? \"icon\" : size}\n disabled={disabled}\n title={title}\n aria-label={title ?? \"Open menu\"}\n className={cn(\n \"h-auto self-stretch rounded-l-none border-l border-l-current/25 px-2\",\n size !== \"icon\" && \"w-8\",\n )}\n >\n <Icon icon={open ? UiChevronUp : UiChevronDown} />\n </Button>\n }\n />\n </div>\n );\n}\n"],"names":["useState","jsxs","Button","jsx","Icon","cn","DropdownMenu","UiChevronUp","UiChevronDown"],"mappings":";;;;;;;;;;AAkCO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP,UAAU;AAAA,EACV,WAAW;AAAA,EACX,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,CAAC,MAAM,OAAO,IAAIA,MAAAA,SAAS,KAAK;AAEtC,MAAI,MAAM,WAAW,GAAG;AACtB,WACEC,2BAAAA;AAAAA,MAACC,OAAAA;AAAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QAEC,UAAA;AAAA,UAAA,QAAQC,2BAAAA,IAACC,WAAA,EAAM,GAAI,OAAO,SAAS,WAAW,EAAE,MAAM,KAAA,IAAS,EAAE,KAAA,EAAK,CAAI;AAAA,UAC1E;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AAEA,yCACG,OAAA,EAAI,WAAWC,MAAAA,GAAG,6BAA6B,SAAS,GACvD,UAAA;AAAA,IAAAJ,2BAAAA;AAAAA,MAACC,OAAAA;AAAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAU;AAAA,QAET,UAAA;AAAA,UAAA,QAAQC,2BAAAA,IAACC,WAAA,EAAM,GAAI,OAAO,SAAS,WAAW,EAAE,MAAM,KAAA,IAAS,EAAE,KAAA,EAAK,CAAI;AAAA,UAC1E;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAEHD,2BAAAA;AAAAA,MAACG,aAAAA;AAAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,WAAU;AAAA,QACT,GAAI,gBAAgB,EAAE,cAAA,IAAkB,CAAA;AAAA,QACzC,cAAc;AAAA,QACd,SACEH,2BAAAA;AAAAA,UAACD,OAAAA;AAAAA,UAAA;AAAA,YACC;AAAA,YACA,MAAM,SAAS,YAAY,SAAS;AAAA,YACpC;AAAA,YACA;AAAA,YACA,cAAY,SAAS;AAAA,YACrB,WAAWG,MAAAA;AAAAA,cACT;AAAA,cACA,SAAS,UAAU;AAAA,YAAA;AAAA,YAGrB,UAAAF,2BAAAA,IAACC,WAAA,EAAK,MAAM,OAAOG,YAAAA,cAAcC,4BAAA,CAAe;AAAA,UAAA;AAAA,QAAA;AAAA,MAClD;AAAA,IAAA;AAAA,EAEJ,GACF;AAEJ;;"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ButtonProps } from './button';
|
|
3
|
+
import { StaticIconComponent } from '../data/Icon';
|
|
4
|
+
import { DropdownMenuItem } from '../overlay/DropdownMenu';
|
|
5
|
+
export type SplitButtonProps = {
|
|
6
|
+
/** Content of the primary action button. */
|
|
7
|
+
label: ReactNode;
|
|
8
|
+
/** Primary action handler. */
|
|
9
|
+
onClick: () => void;
|
|
10
|
+
/** Secondary actions listed in the dropdown menu. */
|
|
11
|
+
items: DropdownMenuItem[];
|
|
12
|
+
/** Icon shown before the primary label. */
|
|
13
|
+
icon?: string | StaticIconComponent;
|
|
14
|
+
/** Variant forwarded to both halves. */
|
|
15
|
+
variant?: ButtonProps["variant"];
|
|
16
|
+
/** Size forwarded to both halves. */
|
|
17
|
+
size?: ButtonProps["size"];
|
|
18
|
+
/** Show a spinner and disable the primary half while pending. */
|
|
19
|
+
loading?: boolean;
|
|
20
|
+
/** Disable both halves. */
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
/** Horizontal alignment of the menu relative to the trigger. */
|
|
23
|
+
align?: "left" | "right";
|
|
24
|
+
/** Browser tooltip / accessible label for the chevron trigger. */
|
|
25
|
+
title?: string;
|
|
26
|
+
/** Classes applied to the wrapper. */
|
|
27
|
+
className?: string;
|
|
28
|
+
/** Classes applied to the floating menu. */
|
|
29
|
+
menuClassName?: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function SplitButton({ label, onClick, items, icon, variant, size, loading, disabled, align, title, className, menuClassName, }: SplitButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
//# sourceMappingURL=SplitButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SplitButton.d.ts","sourceRoot":"","sources":["../../src/components/SplitButton.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAE9D,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE9E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,4CAA4C;IAC5C,KAAK,EAAE,SAAS,CAAC;IACjB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,qDAAqD;IACrD,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,wCAAwC;IACxC,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACjC,qCAAqC;IACrC,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,OAAO,EACP,KAAK,EACL,IAAI,EACJ,OAAmB,EACnB,IAAgB,EAChB,OAAe,EACf,QAAgB,EAChB,KAAe,EACf,KAAK,EACL,SAAS,EACT,aAAa,GACd,EAAE,gBAAgB,2CAwDlB"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
import { Button } from "./button.js";
|
|
5
|
+
import { Icon } from "../data/Icon.js";
|
|
6
|
+
import { DropdownMenu } from "../overlay/DropdownMenu.js";
|
|
7
|
+
import { UiChevronUp } from "../icons/components/UiChevronUp.js";
|
|
8
|
+
import { UiChevronDown } from "../icons/components/UiChevronDown.js";
|
|
9
|
+
function SplitButton({
|
|
10
|
+
label,
|
|
11
|
+
onClick,
|
|
12
|
+
items,
|
|
13
|
+
icon,
|
|
14
|
+
variant = "default",
|
|
15
|
+
size = "default",
|
|
16
|
+
loading = false,
|
|
17
|
+
disabled = false,
|
|
18
|
+
align = "right",
|
|
19
|
+
title,
|
|
20
|
+
className,
|
|
21
|
+
menuClassName
|
|
22
|
+
}) {
|
|
23
|
+
const [open, setOpen] = useState(false);
|
|
24
|
+
if (items.length === 0) {
|
|
25
|
+
return /* @__PURE__ */ jsxs(
|
|
26
|
+
Button,
|
|
27
|
+
{
|
|
28
|
+
variant,
|
|
29
|
+
size,
|
|
30
|
+
loading,
|
|
31
|
+
disabled,
|
|
32
|
+
onClick,
|
|
33
|
+
className,
|
|
34
|
+
children: [
|
|
35
|
+
icon && /* @__PURE__ */ jsx(Icon, { ...typeof icon === "string" ? { name: icon } : { icon } }),
|
|
36
|
+
label
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("inline-flex items-stretch", className), children: [
|
|
42
|
+
/* @__PURE__ */ jsxs(
|
|
43
|
+
Button,
|
|
44
|
+
{
|
|
45
|
+
variant,
|
|
46
|
+
size,
|
|
47
|
+
loading,
|
|
48
|
+
disabled,
|
|
49
|
+
onClick,
|
|
50
|
+
className: "rounded-r-none",
|
|
51
|
+
children: [
|
|
52
|
+
icon && /* @__PURE__ */ jsx(Icon, { ...typeof icon === "string" ? { name: icon } : { icon } }),
|
|
53
|
+
label
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
),
|
|
57
|
+
/* @__PURE__ */ jsx(
|
|
58
|
+
DropdownMenu,
|
|
59
|
+
{
|
|
60
|
+
items,
|
|
61
|
+
align,
|
|
62
|
+
className: "items-stretch",
|
|
63
|
+
...menuClassName ? { menuClassName } : {},
|
|
64
|
+
onOpenChange: setOpen,
|
|
65
|
+
trigger: /* @__PURE__ */ jsx(
|
|
66
|
+
Button,
|
|
67
|
+
{
|
|
68
|
+
variant,
|
|
69
|
+
size: size === "default" ? "icon" : size,
|
|
70
|
+
disabled,
|
|
71
|
+
title,
|
|
72
|
+
"aria-label": title ?? "Open menu",
|
|
73
|
+
className: cn(
|
|
74
|
+
"h-auto self-stretch rounded-l-none border-l border-l-current/25 px-2",
|
|
75
|
+
size !== "icon" && "w-8"
|
|
76
|
+
),
|
|
77
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: open ? UiChevronUp : UiChevronDown })
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
] });
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
SplitButton
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=SplitButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SplitButton.js","sources":["../../src/components/SplitButton.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button, type ButtonProps } from \"./button\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiChevronDown, UiChevronUp } from \"../icons\";\nimport { DropdownMenu, type DropdownMenuItem } from \"../overlay/DropdownMenu\";\n\nexport type SplitButtonProps = {\n /** Content of the primary action button. */\n label: ReactNode;\n /** Primary action handler. */\n onClick: () => void;\n /** Secondary actions listed in the dropdown menu. */\n items: DropdownMenuItem[];\n /** Icon shown before the primary label. */\n icon?: string | StaticIconComponent;\n /** Variant forwarded to both halves. */\n variant?: ButtonProps[\"variant\"];\n /** Size forwarded to both halves. */\n size?: ButtonProps[\"size\"];\n /** Show a spinner and disable the primary half while pending. */\n loading?: boolean;\n /** Disable both halves. */\n disabled?: boolean;\n /** Horizontal alignment of the menu relative to the trigger. */\n align?: \"left\" | \"right\";\n /** Browser tooltip / accessible label for the chevron trigger. */\n title?: string;\n /** Classes applied to the wrapper. */\n className?: string;\n /** Classes applied to the floating menu. */\n menuClassName?: string;\n};\n\nexport function SplitButton({\n label,\n onClick,\n items,\n icon,\n variant = \"default\",\n size = \"default\",\n loading = false,\n disabled = false,\n align = \"right\",\n title,\n className,\n menuClassName,\n}: SplitButtonProps) {\n const [open, setOpen] = useState(false);\n\n if (items.length === 0) {\n return (\n <Button\n variant={variant}\n size={size}\n loading={loading}\n disabled={disabled}\n onClick={onClick}\n className={className}\n >\n {icon && <Icon {...(typeof icon === \"string\" ? { name: icon } : { icon })} />}\n {label}\n </Button>\n );\n }\n\n return (\n <div className={cn(\"inline-flex items-stretch\", className)}>\n <Button\n variant={variant}\n size={size}\n loading={loading}\n disabled={disabled}\n onClick={onClick}\n className=\"rounded-r-none\"\n >\n {icon && <Icon {...(typeof icon === \"string\" ? { name: icon } : { icon })} />}\n {label}\n </Button>\n <DropdownMenu\n items={items}\n align={align}\n className=\"items-stretch\"\n {...(menuClassName ? { menuClassName } : {})}\n onOpenChange={setOpen}\n trigger={\n <Button\n variant={variant}\n size={size === \"default\" ? \"icon\" : size}\n disabled={disabled}\n title={title}\n aria-label={title ?? \"Open menu\"}\n className={cn(\n \"h-auto self-stretch rounded-l-none border-l border-l-current/25 px-2\",\n size !== \"icon\" && \"w-8\",\n )}\n >\n <Icon icon={open ? UiChevronUp : UiChevronDown} />\n </Button>\n }\n />\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAkCO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP,UAAU;AAAA,EACV,WAAW;AAAA,EACX,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AAEtC,MAAI,MAAM,WAAW,GAAG;AACtB,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QAEC,UAAA;AAAA,UAAA,QAAQ,oBAAC,MAAA,EAAM,GAAI,OAAO,SAAS,WAAW,EAAE,MAAM,KAAA,IAAS,EAAE,KAAA,EAAK,CAAI;AAAA,UAC1E;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AAEA,8BACG,OAAA,EAAI,WAAW,GAAG,6BAA6B,SAAS,GACvD,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAU;AAAA,QAET,UAAA;AAAA,UAAA,QAAQ,oBAAC,MAAA,EAAM,GAAI,OAAO,SAAS,WAAW,EAAE,MAAM,KAAA,IAAS,EAAE,KAAA,EAAK,CAAI;AAAA,UAC1E;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAEH;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,WAAU;AAAA,QACT,GAAI,gBAAgB,EAAE,cAAA,IAAkB,CAAA;AAAA,QACzC,cAAc;AAAA,QACd,SACE;AAAA,UAAC;AAAA,UAAA;AAAA,YACC;AAAA,YACA,MAAM,SAAS,YAAY,SAAS;AAAA,YACpC;AAAA,YACA;AAAA,YACA,cAAY,SAAS;AAAA,YACrB,WAAW;AAAA,cACT;AAAA,cACA,SAAS,UAAU;AAAA,YAAA;AAAA,YAGrB,UAAA,oBAAC,MAAA,EAAK,MAAM,OAAO,cAAc,cAAA,CAAe;AAAA,UAAA;AAAA,QAAA;AAAA,MAClD;AAAA,IAAA;AAAA,EAEJ,GACF;AAEJ;"}
|
|
@@ -131,7 +131,7 @@ function IconMenuPickerInner({
|
|
|
131
131
|
triggerClassName
|
|
132
132
|
),
|
|
133
133
|
children: [
|
|
134
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: selected.icon,
|
|
134
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: selected.icon, ...showLabel ? { className: "shrink-0 text-foreground" } : {} }),
|
|
135
135
|
showLabel && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
136
136
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-left capitalize", children: `${ariaLabel}: ${selected.label}` }),
|
|
137
137
|
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiChevronDown.UiChevronDown, className: "shrink-0 text-muted-foreground" })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icon-menu-picker.cjs","sources":["../../src/components/icon-menu-picker.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiCheck, UiChevronDown } from \"../icons\";\nimport { Button } from \"./button\";\n\nexport type IconMenuOption<T extends string> = {\n value: T;\n icon: StaticIconComponent;\n label: string;\n description?: string;\n};\n\nexport type IconMenuPickerProps<T extends string> = {\n value: T;\n onChange: (next: T) => void;\n options: IconMenuOption<T>[];\n ariaLabel: string;\n triggerTitle?: string | undefined;\n footer?: ReactNode | undefined;\n showLabel?: boolean | undefined;\n className?: string | undefined;\n triggerClassName?: string | undefined;\n menuClassName?: string | undefined;\n};\n\nfunction IconMenuPickerInner<T extends string>(\n {\n value,\n onChange,\n options,\n ariaLabel,\n triggerTitle,\n footer,\n showLabel,\n className,\n triggerClassName,\n menuClassName,\n }: IconMenuPickerProps<T>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n const [open, setOpen] = useState(false);\n\n useImperativeHandle(ref, () => rootRef.current as HTMLDivElement);\n\n const activeIndex = useMemo(\n () =>\n Math.max(\n 0,\n options.findIndex((option) => option.value === value),\n ),\n [options, value],\n );\n\n const close = useCallback((returnFocus: boolean) => {\n setOpen(false);\n if (returnFocus) triggerRef.current?.focus();\n }, []);\n\n const select = useCallback(\n (next: T) => {\n onChange(next);\n close(true);\n },\n [close, onChange],\n );\n\n useEffect(() => {\n if (!open) return;\n\n const onPointerDown = (event: PointerEvent) => {\n if (event.button !== 0) return;\n if (!rootRef.current?.contains(event.target as Node)) {\n close(false);\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n close(true);\n }\n };\n\n document.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [close, open]);\n\n useEffect(() => {\n if (!open) return;\n const node = itemRefs.current[activeIndex];\n node?.focus();\n }, [activeIndex, open]);\n\n const focusItem = (index: number) => {\n const wrapped = (index + options.length) % options.length;\n itemRefs.current[wrapped]?.focus();\n };\n\n const onMenuKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>) => {\n const focusedIndex = itemRefs.current.findIndex((node) => node === document.activeElement);\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) + 1);\n return;\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) - 1);\n return;\n }\n if (event.key === \"Home\") {\n event.preventDefault();\n focusItem(0);\n return;\n }\n if (event.key === \"End\") {\n event.preventDefault();\n focusItem(options.length - 1);\n return;\n }\n if (event.key === \"Tab\") {\n close(false);\n }\n };\n\n const onTriggerKeyDown = (event: ReactKeyboardEvent<HTMLButtonElement>) => {\n if (event.key === \"ArrowDown\" || event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n setOpen(true);\n }\n };\n\n const selected = options[activeIndex];\n if (!selected) {\n throw new Error(`IconMenuPicker: value \"${value}\" not found in options for ${ariaLabel}`);\n }\n\n return (\n <div ref={rootRef} className={cn(\"relative inline-flex\", showLabel && \"w-full\", className)}>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant=\"ghost\"\n size={showLabel ? \"default\" : \"icon\"}\n aria-label={ariaLabel}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n title={triggerTitle ?? `${ariaLabel}: ${selected.label}`}\n onClick={() => setOpen((current) => !current)}\n onKeyDown={onTriggerKeyDown}\n className={cn(\n \"text-muted-foreground hover:text-foreground\",\n showLabel && \"w-full justify-start\",\n triggerClassName,\n )}\n >\n <Icon icon={selected.icon} className={showLabel ? \"shrink-0 text-foreground\" : undefined} />\n {showLabel && (\n <>\n <span className=\"min-w-0 flex-1 truncate text-left capitalize\">\n {`${ariaLabel}: ${selected.label}`}\n </span>\n <Icon icon={UiChevronDown} className=\"shrink-0 text-muted-foreground\" />\n </>\n )}\n </Button>\n {open && (\n <div\n role=\"menu\"\n aria-label={ariaLabel}\n onKeyDown={onMenuKeyDown}\n className={cn(\n \"absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n >\n {options.map((option, index) => {\n const active = option.value === value;\n return (\n <button\n key={option.value}\n ref={(node) => {\n itemRefs.current[index] = node;\n }}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={active}\n tabIndex={active ? 0 : -1}\n onClick={() => select(option.value)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n select(option.value);\n }\n }}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors\",\n \"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none\",\n active && \"text-foreground\",\n )}\n >\n <Icon icon={option.icon} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate capitalize\">{option.label}</span>\n {active ? (\n <Icon icon={UiCheck} className=\"shrink-0 text-foreground\" />\n ) : (\n <span className=\"inline-block size-4 shrink-0\" aria-hidden />\n )}\n </button>\n );\n })}\n {footer ? (\n <div className=\"mt-1 border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground\">\n {footer}\n </div>\n ) : null}\n </div>\n )}\n </div>\n );\n}\n\nexport const IconMenuPicker = forwardRef(IconMenuPickerInner) as <T extends string>(\n props: IconMenuPickerProps<T> & { ref?: React.Ref<HTMLDivElement> },\n) => JSX.Element;\n"],"names":["useRef","useState","useImperativeHandle","useMemo","useCallback","useEffect","jsxs","cn","Button","jsx","Icon","Fragment","UiChevronDown","UiCheck","forwardRef"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"icon-menu-picker.cjs","sources":["../../src/components/icon-menu-picker.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiCheck, UiChevronDown } from \"../icons\";\nimport { Button } from \"./button\";\n\nexport type IconMenuOption<T extends string> = {\n value: T;\n icon: StaticIconComponent;\n label: string;\n description?: string;\n};\n\nexport type IconMenuPickerProps<T extends string> = {\n value: T;\n onChange: (next: T) => void;\n options: IconMenuOption<T>[];\n ariaLabel: string;\n triggerTitle?: string | undefined;\n footer?: ReactNode | undefined;\n showLabel?: boolean | undefined;\n className?: string | undefined;\n triggerClassName?: string | undefined;\n menuClassName?: string | undefined;\n};\n\nfunction IconMenuPickerInner<T extends string>(\n {\n value,\n onChange,\n options,\n ariaLabel,\n triggerTitle,\n footer,\n showLabel,\n className,\n triggerClassName,\n menuClassName,\n }: IconMenuPickerProps<T>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n const [open, setOpen] = useState(false);\n\n useImperativeHandle(ref, () => rootRef.current as HTMLDivElement);\n\n const activeIndex = useMemo(\n () =>\n Math.max(\n 0,\n options.findIndex((option) => option.value === value),\n ),\n [options, value],\n );\n\n const close = useCallback((returnFocus: boolean) => {\n setOpen(false);\n if (returnFocus) triggerRef.current?.focus();\n }, []);\n\n const select = useCallback(\n (next: T) => {\n onChange(next);\n close(true);\n },\n [close, onChange],\n );\n\n useEffect(() => {\n if (!open) return;\n\n const onPointerDown = (event: PointerEvent) => {\n if (event.button !== 0) return;\n if (!rootRef.current?.contains(event.target as Node)) {\n close(false);\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n close(true);\n }\n };\n\n document.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [close, open]);\n\n useEffect(() => {\n if (!open) return;\n const node = itemRefs.current[activeIndex];\n node?.focus();\n }, [activeIndex, open]);\n\n const focusItem = (index: number) => {\n const wrapped = (index + options.length) % options.length;\n itemRefs.current[wrapped]?.focus();\n };\n\n const onMenuKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>) => {\n const focusedIndex = itemRefs.current.findIndex((node) => node === document.activeElement);\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) + 1);\n return;\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) - 1);\n return;\n }\n if (event.key === \"Home\") {\n event.preventDefault();\n focusItem(0);\n return;\n }\n if (event.key === \"End\") {\n event.preventDefault();\n focusItem(options.length - 1);\n return;\n }\n if (event.key === \"Tab\") {\n close(false);\n }\n };\n\n const onTriggerKeyDown = (event: ReactKeyboardEvent<HTMLButtonElement>) => {\n if (event.key === \"ArrowDown\" || event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n setOpen(true);\n }\n };\n\n const selected = options[activeIndex];\n if (!selected) {\n throw new Error(`IconMenuPicker: value \"${value}\" not found in options for ${ariaLabel}`);\n }\n\n return (\n <div ref={rootRef} className={cn(\"relative inline-flex\", showLabel && \"w-full\", className)}>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant=\"ghost\"\n size={showLabel ? \"default\" : \"icon\"}\n aria-label={ariaLabel}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n title={triggerTitle ?? `${ariaLabel}: ${selected.label}`}\n onClick={() => setOpen((current) => !current)}\n onKeyDown={onTriggerKeyDown}\n className={cn(\n \"text-muted-foreground hover:text-foreground\",\n showLabel && \"w-full justify-start\",\n triggerClassName,\n )}\n >\n <Icon icon={selected.icon} {...(showLabel ? { className: \"shrink-0 text-foreground\" } : {})} />\n {showLabel && (\n <>\n <span className=\"min-w-0 flex-1 truncate text-left capitalize\">\n {`${ariaLabel}: ${selected.label}`}\n </span>\n <Icon icon={UiChevronDown} className=\"shrink-0 text-muted-foreground\" />\n </>\n )}\n </Button>\n {open && (\n <div\n role=\"menu\"\n aria-label={ariaLabel}\n onKeyDown={onMenuKeyDown}\n className={cn(\n \"absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n >\n {options.map((option, index) => {\n const active = option.value === value;\n return (\n <button\n key={option.value}\n ref={(node) => {\n itemRefs.current[index] = node;\n }}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={active}\n tabIndex={active ? 0 : -1}\n onClick={() => select(option.value)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n select(option.value);\n }\n }}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors\",\n \"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none\",\n active && \"text-foreground\",\n )}\n >\n <Icon icon={option.icon} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate capitalize\">{option.label}</span>\n {active ? (\n <Icon icon={UiCheck} className=\"shrink-0 text-foreground\" />\n ) : (\n <span className=\"inline-block size-4 shrink-0\" aria-hidden />\n )}\n </button>\n );\n })}\n {footer ? (\n <div className=\"mt-1 border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground\">\n {footer}\n </div>\n ) : null}\n </div>\n )}\n </div>\n );\n}\n\nexport const IconMenuPicker = forwardRef(IconMenuPickerInner) as <T extends string>(\n props: IconMenuPickerProps<T> & { ref?: React.Ref<HTMLDivElement> },\n) => JSX.Element;\n"],"names":["useRef","useState","useImperativeHandle","useMemo","useCallback","useEffect","jsxs","cn","Button","jsx","Icon","Fragment","UiChevronDown","UiCheck","forwardRef"],"mappings":"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"}
|
|
@@ -129,7 +129,7 @@ function IconMenuPickerInner({
|
|
|
129
129
|
triggerClassName
|
|
130
130
|
),
|
|
131
131
|
children: [
|
|
132
|
-
/* @__PURE__ */ jsx(Icon, { icon: selected.icon,
|
|
132
|
+
/* @__PURE__ */ jsx(Icon, { icon: selected.icon, ...showLabel ? { className: "shrink-0 text-foreground" } : {} }),
|
|
133
133
|
showLabel && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
134
134
|
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left capitalize", children: `${ariaLabel}: ${selected.label}` }),
|
|
135
135
|
/* @__PURE__ */ jsx(Icon, { icon: UiChevronDown, className: "shrink-0 text-muted-foreground" })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icon-menu-picker.js","sources":["../../src/components/icon-menu-picker.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiCheck, UiChevronDown } from \"../icons\";\nimport { Button } from \"./button\";\n\nexport type IconMenuOption<T extends string> = {\n value: T;\n icon: StaticIconComponent;\n label: string;\n description?: string;\n};\n\nexport type IconMenuPickerProps<T extends string> = {\n value: T;\n onChange: (next: T) => void;\n options: IconMenuOption<T>[];\n ariaLabel: string;\n triggerTitle?: string | undefined;\n footer?: ReactNode | undefined;\n showLabel?: boolean | undefined;\n className?: string | undefined;\n triggerClassName?: string | undefined;\n menuClassName?: string | undefined;\n};\n\nfunction IconMenuPickerInner<T extends string>(\n {\n value,\n onChange,\n options,\n ariaLabel,\n triggerTitle,\n footer,\n showLabel,\n className,\n triggerClassName,\n menuClassName,\n }: IconMenuPickerProps<T>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n const [open, setOpen] = useState(false);\n\n useImperativeHandle(ref, () => rootRef.current as HTMLDivElement);\n\n const activeIndex = useMemo(\n () =>\n Math.max(\n 0,\n options.findIndex((option) => option.value === value),\n ),\n [options, value],\n );\n\n const close = useCallback((returnFocus: boolean) => {\n setOpen(false);\n if (returnFocus) triggerRef.current?.focus();\n }, []);\n\n const select = useCallback(\n (next: T) => {\n onChange(next);\n close(true);\n },\n [close, onChange],\n );\n\n useEffect(() => {\n if (!open) return;\n\n const onPointerDown = (event: PointerEvent) => {\n if (event.button !== 0) return;\n if (!rootRef.current?.contains(event.target as Node)) {\n close(false);\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n close(true);\n }\n };\n\n document.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [close, open]);\n\n useEffect(() => {\n if (!open) return;\n const node = itemRefs.current[activeIndex];\n node?.focus();\n }, [activeIndex, open]);\n\n const focusItem = (index: number) => {\n const wrapped = (index + options.length) % options.length;\n itemRefs.current[wrapped]?.focus();\n };\n\n const onMenuKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>) => {\n const focusedIndex = itemRefs.current.findIndex((node) => node === document.activeElement);\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) + 1);\n return;\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) - 1);\n return;\n }\n if (event.key === \"Home\") {\n event.preventDefault();\n focusItem(0);\n return;\n }\n if (event.key === \"End\") {\n event.preventDefault();\n focusItem(options.length - 1);\n return;\n }\n if (event.key === \"Tab\") {\n close(false);\n }\n };\n\n const onTriggerKeyDown = (event: ReactKeyboardEvent<HTMLButtonElement>) => {\n if (event.key === \"ArrowDown\" || event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n setOpen(true);\n }\n };\n\n const selected = options[activeIndex];\n if (!selected) {\n throw new Error(`IconMenuPicker: value \"${value}\" not found in options for ${ariaLabel}`);\n }\n\n return (\n <div ref={rootRef} className={cn(\"relative inline-flex\", showLabel && \"w-full\", className)}>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant=\"ghost\"\n size={showLabel ? \"default\" : \"icon\"}\n aria-label={ariaLabel}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n title={triggerTitle ?? `${ariaLabel}: ${selected.label}`}\n onClick={() => setOpen((current) => !current)}\n onKeyDown={onTriggerKeyDown}\n className={cn(\n \"text-muted-foreground hover:text-foreground\",\n showLabel && \"w-full justify-start\",\n triggerClassName,\n )}\n >\n <Icon icon={selected.icon} className={showLabel ? \"shrink-0 text-foreground\" : undefined} />\n {showLabel && (\n <>\n <span className=\"min-w-0 flex-1 truncate text-left capitalize\">\n {`${ariaLabel}: ${selected.label}`}\n </span>\n <Icon icon={UiChevronDown} className=\"shrink-0 text-muted-foreground\" />\n </>\n )}\n </Button>\n {open && (\n <div\n role=\"menu\"\n aria-label={ariaLabel}\n onKeyDown={onMenuKeyDown}\n className={cn(\n \"absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n >\n {options.map((option, index) => {\n const active = option.value === value;\n return (\n <button\n key={option.value}\n ref={(node) => {\n itemRefs.current[index] = node;\n }}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={active}\n tabIndex={active ? 0 : -1}\n onClick={() => select(option.value)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n select(option.value);\n }\n }}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors\",\n \"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none\",\n active && \"text-foreground\",\n )}\n >\n <Icon icon={option.icon} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate capitalize\">{option.label}</span>\n {active ? (\n <Icon icon={UiCheck} className=\"shrink-0 text-foreground\" />\n ) : (\n <span className=\"inline-block size-4 shrink-0\" aria-hidden />\n )}\n </button>\n );\n })}\n {footer ? (\n <div className=\"mt-1 border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground\">\n {footer}\n </div>\n ) : null}\n </div>\n )}\n </div>\n );\n}\n\nexport const IconMenuPicker = forwardRef(IconMenuPickerInner) as <T extends string>(\n props: IconMenuPickerProps<T> & { ref?: React.Ref<HTMLDivElement> },\n) => JSX.Element;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"icon-menu-picker.js","sources":["../../src/components/icon-menu-picker.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiCheck, UiChevronDown } from \"../icons\";\nimport { Button } from \"./button\";\n\nexport type IconMenuOption<T extends string> = {\n value: T;\n icon: StaticIconComponent;\n label: string;\n description?: string;\n};\n\nexport type IconMenuPickerProps<T extends string> = {\n value: T;\n onChange: (next: T) => void;\n options: IconMenuOption<T>[];\n ariaLabel: string;\n triggerTitle?: string | undefined;\n footer?: ReactNode | undefined;\n showLabel?: boolean | undefined;\n className?: string | undefined;\n triggerClassName?: string | undefined;\n menuClassName?: string | undefined;\n};\n\nfunction IconMenuPickerInner<T extends string>(\n {\n value,\n onChange,\n options,\n ariaLabel,\n triggerTitle,\n footer,\n showLabel,\n className,\n triggerClassName,\n menuClassName,\n }: IconMenuPickerProps<T>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n const [open, setOpen] = useState(false);\n\n useImperativeHandle(ref, () => rootRef.current as HTMLDivElement);\n\n const activeIndex = useMemo(\n () =>\n Math.max(\n 0,\n options.findIndex((option) => option.value === value),\n ),\n [options, value],\n );\n\n const close = useCallback((returnFocus: boolean) => {\n setOpen(false);\n if (returnFocus) triggerRef.current?.focus();\n }, []);\n\n const select = useCallback(\n (next: T) => {\n onChange(next);\n close(true);\n },\n [close, onChange],\n );\n\n useEffect(() => {\n if (!open) return;\n\n const onPointerDown = (event: PointerEvent) => {\n if (event.button !== 0) return;\n if (!rootRef.current?.contains(event.target as Node)) {\n close(false);\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n close(true);\n }\n };\n\n document.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [close, open]);\n\n useEffect(() => {\n if (!open) return;\n const node = itemRefs.current[activeIndex];\n node?.focus();\n }, [activeIndex, open]);\n\n const focusItem = (index: number) => {\n const wrapped = (index + options.length) % options.length;\n itemRefs.current[wrapped]?.focus();\n };\n\n const onMenuKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>) => {\n const focusedIndex = itemRefs.current.findIndex((node) => node === document.activeElement);\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) + 1);\n return;\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) - 1);\n return;\n }\n if (event.key === \"Home\") {\n event.preventDefault();\n focusItem(0);\n return;\n }\n if (event.key === \"End\") {\n event.preventDefault();\n focusItem(options.length - 1);\n return;\n }\n if (event.key === \"Tab\") {\n close(false);\n }\n };\n\n const onTriggerKeyDown = (event: ReactKeyboardEvent<HTMLButtonElement>) => {\n if (event.key === \"ArrowDown\" || event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n setOpen(true);\n }\n };\n\n const selected = options[activeIndex];\n if (!selected) {\n throw new Error(`IconMenuPicker: value \"${value}\" not found in options for ${ariaLabel}`);\n }\n\n return (\n <div ref={rootRef} className={cn(\"relative inline-flex\", showLabel && \"w-full\", className)}>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant=\"ghost\"\n size={showLabel ? \"default\" : \"icon\"}\n aria-label={ariaLabel}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n title={triggerTitle ?? `${ariaLabel}: ${selected.label}`}\n onClick={() => setOpen((current) => !current)}\n onKeyDown={onTriggerKeyDown}\n className={cn(\n \"text-muted-foreground hover:text-foreground\",\n showLabel && \"w-full justify-start\",\n triggerClassName,\n )}\n >\n <Icon icon={selected.icon} {...(showLabel ? { className: \"shrink-0 text-foreground\" } : {})} />\n {showLabel && (\n <>\n <span className=\"min-w-0 flex-1 truncate text-left capitalize\">\n {`${ariaLabel}: ${selected.label}`}\n </span>\n <Icon icon={UiChevronDown} className=\"shrink-0 text-muted-foreground\" />\n </>\n )}\n </Button>\n {open && (\n <div\n role=\"menu\"\n aria-label={ariaLabel}\n onKeyDown={onMenuKeyDown}\n className={cn(\n \"absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n >\n {options.map((option, index) => {\n const active = option.value === value;\n return (\n <button\n key={option.value}\n ref={(node) => {\n itemRefs.current[index] = node;\n }}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={active}\n tabIndex={active ? 0 : -1}\n onClick={() => select(option.value)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n select(option.value);\n }\n }}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors\",\n \"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none\",\n active && \"text-foreground\",\n )}\n >\n <Icon icon={option.icon} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate capitalize\">{option.label}</span>\n {active ? (\n <Icon icon={UiCheck} className=\"shrink-0 text-foreground\" />\n ) : (\n <span className=\"inline-block size-4 shrink-0\" aria-hidden />\n )}\n </button>\n );\n })}\n {footer ? (\n <div className=\"mt-1 border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground\">\n {footer}\n </div>\n ) : null}\n </div>\n )}\n </div>\n );\n}\n\nexport const IconMenuPicker = forwardRef(IconMenuPickerInner) as <T extends string>(\n props: IconMenuPickerProps<T> & { ref?: React.Ref<HTMLDivElement> },\n) => JSX.Element;\n"],"names":[],"mappings":"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"}
|
package/dist/components.cjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const button = require("./components/button.cjs");
|
|
4
|
+
const SplitButton = require("./components/SplitButton.cjs");
|
|
5
|
+
const FormatOptionsDropdown = require("./components/FormatOptionsDropdown.cjs");
|
|
4
6
|
const DateField = require("./components/DateField.cjs");
|
|
5
7
|
const DatePicker = require("./components/DatePicker.cjs");
|
|
6
8
|
const DateTimePicker = require("./components/DateTimePicker.cjs");
|
|
@@ -9,6 +11,7 @@ const RangeSlider = require("./components/RangeSlider.cjs");
|
|
|
9
11
|
const FilterBar = require("./components/FilterBar.cjs");
|
|
10
12
|
const MultiSelect = require("./components/MultiSelect.cjs");
|
|
11
13
|
const select = require("./components/select.cjs");
|
|
14
|
+
const Combobox = require("./components/Combobox.cjs");
|
|
12
15
|
const themeSwitcher = require("./components/theme-switcher.cjs");
|
|
13
16
|
const densitySwitcher = require("./components/density-switcher.cjs");
|
|
14
17
|
const iconMenuPicker = require("./components/icon-menu-picker.cjs");
|
|
@@ -19,6 +22,9 @@ const HoverCard = require("./overlay/HoverCard.cjs");
|
|
|
19
22
|
const Modal = require("./overlay/Modal.cjs");
|
|
20
23
|
exports.Button = button.Button;
|
|
21
24
|
exports.buttonVariants = button.buttonVariants;
|
|
25
|
+
exports.SplitButton = SplitButton.SplitButton;
|
|
26
|
+
exports.CLICKY_FORMAT_OPTIONS = FormatOptionsDropdown.CLICKY_FORMAT_OPTIONS;
|
|
27
|
+
exports.FormatOptionsDropdown = FormatOptionsDropdown.FormatOptionsDropdown;
|
|
22
28
|
exports.DateField = DateField.DateField;
|
|
23
29
|
exports.DatePicker = DatePicker.DatePicker;
|
|
24
30
|
exports.DateTimePicker = DateTimePicker.DateTimePicker;
|
|
@@ -27,6 +33,7 @@ exports.RangeSlider = RangeSlider.RangeSlider;
|
|
|
27
33
|
exports.FilterBar = FilterBar.FilterBar;
|
|
28
34
|
exports.MultiSelect = MultiSelect.MultiSelect;
|
|
29
35
|
exports.Select = select.Select;
|
|
36
|
+
exports.Combobox = Combobox.Combobox;
|
|
30
37
|
exports.ThemeSwitcher = themeSwitcher.ThemeSwitcher;
|
|
31
38
|
exports.DensitySwitcher = densitySwitcher.DensitySwitcher;
|
|
32
39
|
exports.IconMenuPicker = iconMenuPicker.IconMenuPicker;
|
package/dist/components.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"components.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|