@nocoo/basalt 2.0.2 → 2.1.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 +134 -15
- package/ai/COMPATIBILITY.md +180 -0
- package/ai/INTEGRATION.md +1760 -0
- package/ai/RECIPES.md +161 -0
- package/ai/USAGE.md +99 -0
- package/ai/registry.json +38099 -0
- package/ai/sources.json +10 -0
- package/dist/assets/brand-mark.d.ts +1 -0
- package/dist/assets/brand-mark.js +7 -0
- package/dist/assets/brand-mark.js.map +1 -0
- package/dist/charts/area.d.ts +58 -5
- package/dist/charts/area.js +20 -12
- package/dist/charts/area.js.map +1 -1
- package/dist/charts/bar.d.ts +54 -5
- package/dist/charts/bar.js +16 -10
- package/dist/charts/bar.js.map +1 -1
- package/dist/charts/bullet.d.ts +18 -1
- package/dist/charts/bullet.js +6 -3
- package/dist/charts/bullet.js.map +1 -1
- package/dist/charts/charts.d.ts +58 -5
- package/dist/charts/charts.js +13 -2
- package/dist/charts/charts.js.map +1 -1
- package/dist/charts/config.d.ts +7 -0
- package/dist/charts/config.js +5 -1
- package/dist/charts/config.js.map +1 -1
- package/dist/charts/custom-chart.d.ts +58 -5
- package/dist/charts/custom-chart.js +13 -2
- package/dist/charts/custom-chart.js.map +1 -1
- package/dist/charts/donut.d.ts +18 -1
- package/dist/charts/donut.js +6 -3
- package/dist/charts/donut.js.map +1 -1
- package/dist/charts/frame.d.ts +40 -3
- package/dist/charts/frame.js +55 -28
- package/dist/charts/frame.js.map +1 -1
- package/dist/charts/funnel.d.ts +18 -1
- package/dist/charts/funnel.js +5 -1
- package/dist/charts/funnel.js.map +1 -1
- package/dist/charts/gauge.d.ts +18 -1
- package/dist/charts/gauge.js +38 -20
- package/dist/charts/gauge.js.map +1 -1
- package/dist/charts/grouped-bar.d.ts +54 -5
- package/dist/charts/grouped-bar.js +16 -10
- package/dist/charts/grouped-bar.js.map +1 -1
- package/dist/charts/heatmap-calendar.d.ts +61 -0
- package/dist/charts/heatmap-calendar.js +287 -96
- package/dist/charts/heatmap-calendar.js.map +1 -1
- package/dist/charts/heatmap-matrix.d.ts +99 -0
- package/dist/charts/heatmap-matrix.js +249 -0
- package/dist/charts/heatmap-matrix.js.map +1 -0
- package/dist/charts/line.d.ts +66 -5
- package/dist/charts/line.js +37 -30
- package/dist/charts/line.js.map +1 -1
- package/dist/charts/palette.d.ts +1 -0
- package/dist/charts/palette.js +7 -1
- package/dist/charts/palette.js.map +1 -1
- package/dist/charts/radar.d.ts +18 -1
- package/dist/charts/radar.js +26 -19
- package/dist/charts/radar.js.map +1 -1
- package/dist/charts/sankey.d.ts +18 -1
- package/dist/charts/sankey.js +4 -1
- package/dist/charts/sankey.js.map +1 -1
- package/dist/charts/series.d.ts +5 -7
- package/dist/charts/series.js.map +1 -1
- package/dist/charts/slot-bar.d.ts +38 -6
- package/dist/charts/slot-bar.js +5 -2
- package/dist/charts/slot-bar.js.map +1 -1
- package/dist/charts/sparkline.d.ts +35 -5
- package/dist/charts/sparkline.js +22 -18
- package/dist/charts/sparkline.js.map +1 -1
- package/dist/charts/stacked-bar.d.ts +58 -5
- package/dist/charts/stacked-bar.js +19 -12
- package/dist/charts/stacked-bar.js.map +1 -1
- package/dist/charts/stat-card.d.ts +51 -1
- package/dist/charts/stat-card.js +66 -39
- package/dist/charts/stat-card.js.map +1 -1
- package/dist/charts/timeline.d.ts +3 -0
- package/dist/charts/timeline.js +4 -3
- package/dist/charts/timeline.js.map +1 -1
- package/dist/charts/timeseries.d.ts +58 -5
- package/dist/charts/timeseries.js +13 -2
- package/dist/charts/timeseries.js.map +1 -1
- package/dist/charts/tooltip.d.ts +57 -0
- package/dist/charts/tooltip.js +119 -2
- package/dist/charts/tooltip.js.map +1 -1
- package/dist/components/accordion.d.ts +117 -2
- package/dist/components/accordion.js +20 -13
- package/dist/components/accordion.js.map +1 -1
- package/dist/components/alert-dialog.d.ts +131 -13
- package/dist/components/alert-dialog.js +13 -10
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/app-header.js +18 -17
- package/dist/components/app-header.js.map +1 -1
- package/dist/components/app-shell.js +15 -14
- package/dist/components/app-shell.js.map +1 -1
- package/dist/components/autocomplete.d.ts +4 -0
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/avatar.d.ts +76 -3
- package/dist/components/avatar.js.map +1 -1
- package/dist/components/badge.d.ts +15 -1
- package/dist/components/badge.js +17 -16
- package/dist/components/badge.js.map +1 -1
- package/dist/components/banner.d.ts +88 -2
- package/dist/components/banner.js +41 -40
- package/dist/components/banner.js.map +1 -1
- package/dist/components/basalt-mark.js +14 -8
- package/dist/components/basalt-mark.js.map +1 -1
- package/dist/components/battery-meter.d.ts +10 -0
- package/dist/components/battery-meter.js +56 -0
- package/dist/components/battery-meter.js.map +1 -0
- package/dist/components/breadcrumbs.d.ts +27 -7
- package/dist/components/breadcrumbs.js +21 -20
- package/dist/components/breadcrumbs.js.map +1 -1
- package/dist/components/button.d.ts +48 -2
- package/dist/components/button.js +87 -36
- package/dist/components/button.js.map +1 -1
- package/dist/components/chat-bubble.d.ts +24 -0
- package/dist/components/chat-bubble.js +26 -0
- package/dist/components/chat-bubble.js.map +1 -0
- package/dist/components/chat-composer.d.ts +45 -0
- package/dist/components/chat-composer.js +74 -0
- package/dist/components/chat-composer.js.map +1 -0
- package/dist/components/chat-header.d.ts +25 -0
- package/dist/components/chat-header.js +34 -0
- package/dist/components/chat-header.js.map +1 -0
- package/dist/components/chat-inbox.d.ts +33 -0
- package/dist/components/chat-inbox.js +44 -0
- package/dist/components/chat-inbox.js.map +1 -0
- package/dist/components/checkbox.js +84 -53
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/clipboard-text.d.ts +13 -2
- package/dist/components/clipboard-text.js +4 -4
- package/dist/components/clipboard-text.js.map +1 -1
- package/dist/components/collapsible.d.ts +58 -15
- package/dist/components/collapsible.js.map +1 -1
- package/dist/components/command-palette.d.ts +151 -61
- package/dist/components/command-palette.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +1 -1
- package/dist/components/confirm-dialog.js +6 -4
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/context-menu.d.ts +210 -2
- package/dist/components/context-menu.js +7 -4
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/data-table.d.ts +40 -2
- package/dist/components/data-table.js +168 -120
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +53 -2
- package/dist/components/date-picker.js +387 -219
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/delete-resource.d.ts +7 -1
- package/dist/components/delete-resource.js +22 -18
- package/dist/components/delete-resource.js.map +1 -1
- package/dist/components/description-list.js +16 -15
- package/dist/components/description-list.js.map +1 -1
- package/dist/components/dialog.d.ts +141 -12
- package/dist/components/dialog.js +43 -39
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/dock.d.ts +39 -0
- package/dist/components/dock.js +97 -0
- package/dist/components/dock.js.map +1 -0
- package/dist/components/dropdown-menu.d.ts +229 -15
- package/dist/components/dropdown-menu.js +9 -6
- package/dist/components/dropdown-menu.js.map +1 -1
- package/dist/components/editable-nav-item.d.ts +33 -0
- package/dist/components/editable-nav-item.js +90 -0
- package/dist/components/editable-nav-item.js.map +1 -0
- package/dist/components/empty.d.ts +22 -2
- package/dist/components/empty.js +22 -13
- package/dist/components/empty.js.map +1 -1
- package/dist/components/fab.d.ts +23 -0
- package/dist/components/fab.js +25 -0
- package/dist/components/fab.js.map +1 -0
- package/dist/components/field.js +31 -30
- package/dist/components/field.js.map +1 -1
- package/dist/components/file-dropzone.d.ts +37 -0
- package/dist/components/file-dropzone.js +103 -0
- package/dist/components/file-dropzone.js.map +1 -0
- package/dist/components/filter-bar.d.ts +34 -0
- package/dist/components/filter-bar.js +52 -0
- package/dist/components/filter-bar.js.map +1 -0
- package/dist/components/flow.js +19 -18
- package/dist/components/flow.js.map +1 -1
- package/dist/components/grid.d.ts +3 -1
- package/dist/components/grid.js.map +1 -1
- package/dist/components/hover-card.d.ts +111 -1
- package/dist/components/hover-card.js +9 -6
- package/dist/components/hover-card.js.map +1 -1
- package/dist/components/icon-picker.d.ts +34 -0
- package/dist/components/icon-picker.js +77 -0
- package/dist/components/icon-picker.js.map +1 -0
- package/dist/components/inline-editable.d.ts +46 -0
- package/dist/components/inline-editable.js +124 -0
- package/dist/components/inline-editable.js.map +1 -0
- package/dist/components/input-area.js +3 -3
- package/dist/components/input-group.js +7 -7
- package/dist/components/input.js +3 -3
- package/dist/components/label.js +18 -17
- package/dist/components/label.js.map +1 -1
- package/dist/components/layer-card.d.ts +10 -2
- package/dist/components/layer-card.js +71 -68
- package/dist/components/layer-card.js.map +1 -1
- package/dist/components/loader.d.ts +10 -2
- package/dist/components/loader.js.map +1 -1
- package/dist/components/menu-bar.d.ts +174 -6
- package/dist/components/menu-bar.js +33 -29
- package/dist/components/menu-bar.js.map +1 -1
- package/dist/components/meter.d.ts +27 -2
- package/dist/components/meter.js.map +1 -1
- package/dist/components/multi-select.d.ts +64 -0
- package/dist/components/multi-select.js +218 -0
- package/dist/components/multi-select.js.map +1 -0
- package/dist/components/navigation-menu.d.ts +88 -5
- package/dist/components/navigation-menu.js.map +1 -1
- package/dist/components/overlay.js +6 -5
- package/dist/components/overlay.js.map +1 -1
- package/dist/components/page-header.js +23 -21
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/pagination.js +7 -7
- package/dist/components/popover.d.ts +136 -16
- package/dist/components/popover.js +32 -28
- package/dist/components/popover.js.map +1 -1
- package/dist/components/radio.js +38 -37
- package/dist/components/radio.js.map +1 -1
- package/dist/components/resource-list.d.ts +26 -1
- package/dist/components/resource-list.js +28 -15
- package/dist/components/resource-list.js.map +1 -1
- package/dist/components/responsive-master-detail.d.ts +27 -0
- package/dist/components/responsive-master-detail.js +85 -0
- package/dist/components/responsive-master-detail.js.map +1 -0
- package/dist/components/section-rule.js +23 -22
- package/dist/components/section-rule.js.map +1 -1
- package/dist/components/select.d.ts +27 -3
- package/dist/components/select.js +11 -11
- package/dist/components/select.js.map +1 -1
- package/dist/components/sheet.d.ts +115 -11
- package/dist/components/sheet.js +37 -30
- package/dist/components/sheet.js.map +1 -1
- package/dist/components/sidebar.d.ts +109 -17
- package/dist/components/sidebar.js +109 -108
- package/dist/components/sidebar.js.map +1 -1
- package/dist/components/skeleton-line.d.ts +22 -2
- package/dist/components/skeleton-line.js.map +1 -1
- package/dist/components/slider.d.ts +61 -1
- package/dist/components/slider.js +31 -15
- package/dist/components/slider.js.map +1 -1
- package/dist/components/stat-strip.js +16 -15
- package/dist/components/stat-strip.js.map +1 -1
- package/dist/components/switch.js +78 -47
- package/dist/components/switch.js.map +1 -1
- package/dist/components/table-of-contents.js +18 -17
- package/dist/components/table-of-contents.js.map +1 -1
- package/dist/components/table-pager.js +20 -19
- package/dist/components/table-pager.js.map +1 -1
- package/dist/components/table.d.ts +9 -3
- package/dist/components/table.js +19 -18
- package/dist/components/table.js.map +1 -1
- package/dist/components/tabs.js +29 -22
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/tag-badge.d.ts +58 -0
- package/dist/components/tag-badge.js +72 -0
- package/dist/components/tag-badge.js.map +1 -0
- package/dist/components/tag-color-picker.d.ts +20 -0
- package/dist/components/tag-color-picker.js +42 -0
- package/dist/components/tag-color-picker.js.map +1 -0
- package/dist/components/text.js +17 -16
- package/dist/components/text.js.map +1 -1
- package/dist/components/toast.d.ts +173 -3
- package/dist/components/toast.js +1 -1
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toggle-group.d.ts +88 -2
- package/dist/components/toggle-group.js +32 -31
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle.d.ts +35 -2
- package/dist/components/toggle.js +12 -11
- package/dist/components/toggle.js.map +1 -1
- package/dist/components/tooltip.d.ts +144 -6
- package/dist/components/tooltip.js +9 -6
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/typeahead-field.js +193 -132
- package/dist/components/typeahead-field.js.map +1 -1
- package/dist/components/upload-queue.d.ts +51 -0
- package/dist/components/upload-queue.js +116 -0
- package/dist/components/upload-queue.js.map +1 -0
- package/dist/providers/accent.d.ts +46 -2
- package/dist/providers/accent.js +358 -172
- package/dist/providers/accent.js.map +1 -1
- package/dist/providers/link.d.ts +14 -4
- package/dist/providers/link.js.map +1 -1
- package/dist/providers/theme.d.ts +38 -2
- package/dist/providers/theme.js +160 -27
- package/dist/providers/theme.js.map +1 -1
- package/dist/styles/base.css +83 -0
- package/dist/styles/standalone.css +820 -120
- package/dist/styles/tailwind.css +21 -7
- package/dist/styles/tokens.css +146 -91
- package/dist/utils/control-surface.d.ts +2 -1
- package/dist/utils/control-surface.js +4 -4
- package/dist/utils/control-surface.js.map +1 -1
- package/dist/utils/selection-indicator.js +2 -2
- package/dist/utils/selection-indicator.js.map +1 -1
- package/package.json +8 -4
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
export interface BreadcrumbItem {
|
|
3
|
+
/**
|
|
4
|
+
* Navigation target URL. When omitted, item renders as unlinked text (and only receives aria-current="page" if it is the final item).
|
|
5
|
+
*/
|
|
6
|
+
href?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Content label rendered inside the breadcrumb segment.
|
|
9
|
+
*/
|
|
10
|
+
label: ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* Optional leading icon rendered before the label.
|
|
13
|
+
*/
|
|
14
|
+
icon?: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
export interface BreadcrumbsProps {
|
|
17
|
+
/**
|
|
18
|
+
* List of breadcrumb segments ({ label: ReactNode, href?: string, icon?: ReactNode }[]) in hierarchical order from root to current page.
|
|
19
|
+
* Only the final item in the array without an href receives aria-current="page".
|
|
20
|
+
* Interactive items render via Link, delegating routing to LinkProvider when configured.
|
|
21
|
+
* Component only accepts items and className without native HTML rest attribute forwarding or forwarded ref.
|
|
22
|
+
*/
|
|
23
|
+
items: BreadcrumbItem[];
|
|
24
|
+
/**
|
|
25
|
+
* Optional class name applied to the outer nav element.
|
|
26
|
+
*/
|
|
8
27
|
className?: string;
|
|
9
|
-
}
|
|
28
|
+
}
|
|
29
|
+
export declare function Breadcrumbs({ items, className }: BreadcrumbsProps): import("react").JSX.Element;
|
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn as e } from "../utils/cn.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { BASALT_UI_CLASS as t } from "../utils/control-surface.js";
|
|
4
|
+
import { Link as n } from "./link.js";
|
|
5
|
+
import { ChevronRight as r } from "lucide-react";
|
|
6
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
6
7
|
//#region src/components/breadcrumbs.tsx
|
|
7
|
-
var
|
|
8
|
-
function
|
|
9
|
-
return /* @__PURE__ */
|
|
8
|
+
var o = "text-sm font-normal";
|
|
9
|
+
function s({ items: s, className: c }) {
|
|
10
|
+
return /* @__PURE__ */ i("nav", {
|
|
10
11
|
"aria-label": "Breadcrumb",
|
|
11
|
-
className: e("flex items-center gap-1 text-sm font-normal text-basalt-muted-foreground",
|
|
12
|
-
children:
|
|
13
|
-
let l = c ===
|
|
12
|
+
className: e(t, "flex items-center gap-1 text-sm font-normal text-basalt-muted-foreground", c),
|
|
13
|
+
children: s.map((t, c) => {
|
|
14
|
+
let l = c === s.length - 1 && !t.href, u = t.icon ? /* @__PURE__ */ a("span", {
|
|
14
15
|
className: "inline-flex items-center gap-1",
|
|
15
|
-
children: [
|
|
16
|
-
}) :
|
|
17
|
-
return /* @__PURE__ */
|
|
16
|
+
children: [t.icon, t.label]
|
|
17
|
+
}) : t.label;
|
|
18
|
+
return /* @__PURE__ */ a("span", {
|
|
18
19
|
className: "flex items-center gap-1",
|
|
19
|
-
children: [c > 0 ? /* @__PURE__ */ r
|
|
20
|
+
children: [c > 0 ? /* @__PURE__ */ i(r, {
|
|
20
21
|
className: "size-3",
|
|
21
22
|
"aria-hidden": "true"
|
|
22
|
-
}) : null,
|
|
23
|
-
href:
|
|
24
|
-
className: e(
|
|
23
|
+
}) : null, t.href ? /* @__PURE__ */ i(n, {
|
|
24
|
+
href: t.href,
|
|
25
|
+
className: e(o, "text-basalt-muted-foreground no-underline transition-colors hover:text-basalt-foreground"),
|
|
25
26
|
children: u
|
|
26
|
-
}) : /* @__PURE__ */
|
|
27
|
+
}) : /* @__PURE__ */ i("span", {
|
|
27
28
|
"aria-current": l ? "page" : void 0,
|
|
28
|
-
className: e(
|
|
29
|
+
className: e(o, l ? "text-basalt-foreground" : "text-basalt-muted-foreground"),
|
|
29
30
|
children: u
|
|
30
31
|
})]
|
|
31
|
-
}, `${String(
|
|
32
|
+
}, `${String(t.label)}-${c}`);
|
|
32
33
|
})
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
//#endregion
|
|
36
|
-
export {
|
|
37
|
+
export { s as Breadcrumbs };
|
|
37
38
|
|
|
38
39
|
//# sourceMappingURL=breadcrumbs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumbs.js","names":[],"sources":["../../src/components/breadcrumbs.tsx"],"sourcesContent":["import { ChevronRight } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Link } from \"./link\";\n\nconst crumbTypeClass = \"text-sm font-normal\";\n\nexport
|
|
1
|
+
{"version":3,"file":"breadcrumbs.js","names":[],"sources":["../../src/components/breadcrumbs.tsx"],"sourcesContent":["import { ChevronRight } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { Link } from \"./link\";\n\nconst crumbTypeClass = \"text-sm font-normal\";\n\nexport interface BreadcrumbItem {\n\t/**\n\t * Navigation target URL. When omitted, item renders as unlinked text (and only receives aria-current=\"page\" if it is the final item).\n\t */\n\thref?: string;\n\t/**\n\t * Content label rendered inside the breadcrumb segment.\n\t */\n\tlabel: ReactNode;\n\t/**\n\t * Optional leading icon rendered before the label.\n\t */\n\ticon?: ReactNode;\n}\n\nexport interface BreadcrumbsProps {\n\t/**\n\t * List of breadcrumb segments ({ label: ReactNode, href?: string, icon?: ReactNode }[]) in hierarchical order from root to current page.\n\t * Only the final item in the array without an href receives aria-current=\"page\".\n\t * Interactive items render via Link, delegating routing to LinkProvider when configured.\n\t * Component only accepts items and className without native HTML rest attribute forwarding or forwarded ref.\n\t */\n\titems: BreadcrumbItem[];\n\t/**\n\t * Optional class name applied to the outer nav element.\n\t */\n\tclassName?: string;\n}\n\nexport function Breadcrumbs({ items, className }: BreadcrumbsProps) {\n\treturn (\n\t\t<nav\n\t\t\taria-label=\"Breadcrumb\"\n\t\t\tclassName={cn(\n\t\t\t\tBASALT_UI_CLASS,\n\t\t\t\t\"flex items-center gap-1 text-sm font-normal text-basalt-muted-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t{items.map((item, index) => {\n\t\t\t\tconst current = index === items.length - 1 && !item.href;\n\t\t\t\tconst label = item.icon ? (\n\t\t\t\t\t<span className=\"inline-flex items-center gap-1\">\n\t\t\t\t\t\t{item.icon}\n\t\t\t\t\t\t{item.label}\n\t\t\t\t\t</span>\n\t\t\t\t) : (\n\t\t\t\t\titem.label\n\t\t\t\t);\n\t\t\t\treturn (\n\t\t\t\t\t<span key={`${String(item.label)}-${index}`} className=\"flex items-center gap-1\">\n\t\t\t\t\t\t{index > 0 ? <ChevronRight className=\"size-3\" aria-hidden=\"true\" /> : null}\n\t\t\t\t\t\t{item.href ? (\n\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\thref={item.href}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\tcrumbTypeClass,\n\t\t\t\t\t\t\t\t\t\"text-basalt-muted-foreground no-underline transition-colors hover:text-basalt-foreground\",\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\taria-current={current ? \"page\" : undefined}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\tcrumbTypeClass,\n\t\t\t\t\t\t\t\t\tcurrent ? \"text-basalt-foreground\" : \"text-basalt-muted-foreground\",\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</span>\n\t\t\t\t);\n\t\t\t})}\n\t\t</nav>\n\t);\n}\n"],"mappings":";;;;;;;AAMA,IAAM,IAAiB;AA+BvB,SAAgB,EAAY,EAAE,UAAO,gBAA+B;CACnE,OACC,kBAAC,OAAD;EACC,cAAW;EACX,WAAW,EACV,GACA,4EACA,CACD;EAEC,UAAA,EAAM,KAAK,GAAM,MAAU;GAC3B,IAAM,IAAU,MAAU,EAAM,SAAS,KAAK,CAAC,EAAK,MAC9C,IAAQ,EAAK,OAClB,kBAAC,QAAD;IAAM,WAAU;IAAhB,UAAA,CACE,EAAK,MACL,EAAK,KACD;GAEN,CAAA,IAAA,EAAK;GAEN,OACC,kBAAC,QAAD;IAA6C,WAAU;IAAvD,UAAA,CACE,IAAQ,IAAI,kBAAC,GAAD;KAAc,WAAU;KAAS,eAAY;IAAQ,CAAA,IAAI,MACrE,EAAK,OACL,kBAAC,GAAD;KACC,MAAM,EAAK;KACX,WAAW,EACV,GACA,0FACD;KAEC,UAAA;IACI,CAAA,IAEN,kBAAC,QAAD;KACC,gBAAc,IAAU,SAAS,KAAA;KACjC,WAAW,EACV,GACA,IAAU,2BAA2B,8BACtC;KAEC,UAAA;IACI,CAAA,CAEF;GAvBK,GAAA,GAAG,OAAO,EAAK,KAAK,EAAE,GAAG,GAuB9B;EAER,CAAC;CACG,CAAA;AAEP"}
|
|
@@ -4,13 +4,59 @@ declare const buttonVariants: (props?: ({
|
|
|
4
4
|
variant?: "default" | "destructive" | "ghost" | "link" | "outline" | "secondary" | null | undefined;
|
|
5
5
|
size?: "default" | "icon" | "lg" | "sm" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
|
-
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
7
|
+
export interface ButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "type">, Omit<VariantProps<typeof buttonVariants>, "variant" | "size"> {
|
|
8
|
+
/**
|
|
9
|
+
* Visual style variant.
|
|
10
|
+
* @default "default"
|
|
11
|
+
*/
|
|
12
|
+
variant?: VariantProps<typeof buttonVariants>["variant"];
|
|
13
|
+
/**
|
|
14
|
+
* Sizing preset.
|
|
15
|
+
* @default "default"
|
|
16
|
+
*/
|
|
17
|
+
size?: VariantProps<typeof buttonVariants>["size"];
|
|
18
|
+
/**
|
|
19
|
+
* Pass control to child element slot.
|
|
20
|
+
*
|
|
21
|
+
* Note: Child element must forward props and ref in all states. When disabled or loading,
|
|
22
|
+
* asChild suppresses click, keyboard activation (Enter/Space), and pointer handlers on both
|
|
23
|
+
* child and parent while retaining browser default Tab navigation without trapping focus.
|
|
24
|
+
* To preserve single DOM node layout and avoid unexpected child layout shifts, no internal
|
|
25
|
+
* spinner is inserted in asChild loading mode; visual disabled state is expressed via aria-disabled.
|
|
26
|
+
*
|
|
27
|
+
* @default false
|
|
28
|
+
*/
|
|
8
29
|
asChild?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Button type attribute.
|
|
32
|
+
* @default "button"
|
|
33
|
+
*/
|
|
34
|
+
type?: React.ButtonHTMLAttributes<HTMLButtonElement>["type"];
|
|
35
|
+
/**
|
|
36
|
+
* Display loading spinner and disable interactions.
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
9
39
|
loading?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Optional leading icon slot.
|
|
42
|
+
*/
|
|
10
43
|
icon?: React.ReactNode;
|
|
11
44
|
}
|
|
12
45
|
export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
13
|
-
export interface LinkButtonProps extends React.AnchorHTMLAttributes<HTMLAnchorElement>, VariantProps<typeof buttonVariants> {
|
|
46
|
+
export interface LinkButtonProps extends React.AnchorHTMLAttributes<HTMLAnchorElement>, Omit<VariantProps<typeof buttonVariants>, "variant" | "size"> {
|
|
47
|
+
/**
|
|
48
|
+
* Visual style variant.
|
|
49
|
+
* @default "default"
|
|
50
|
+
*/
|
|
51
|
+
variant?: VariantProps<typeof buttonVariants>["variant"];
|
|
52
|
+
/**
|
|
53
|
+
* Sizing preset.
|
|
54
|
+
* @default "default"
|
|
55
|
+
*/
|
|
56
|
+
size?: VariantProps<typeof buttonVariants>["size"];
|
|
57
|
+
/**
|
|
58
|
+
* Optional leading icon slot.
|
|
59
|
+
*/
|
|
14
60
|
icon?: React.ReactNode;
|
|
15
61
|
}
|
|
16
62
|
export declare const LinkButton: React.ForwardRefExoticComponent<LinkButtonProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn as e } from "../utils/cn.js";
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
3
|
+
import { BASALT_UI_CLASS as t } from "../utils/control-surface.js";
|
|
4
|
+
import { FOCUS_RING as n } from "./overlay.js";
|
|
5
|
+
import * as r from "react";
|
|
6
|
+
import { Slot as i } from "@radix-ui/react-slot";
|
|
7
|
+
import { cva as a } from "class-variance-authority";
|
|
8
|
+
import { Loader2 as o } from "lucide-react";
|
|
9
|
+
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
9
10
|
//#region src/components/button.tsx
|
|
10
|
-
var
|
|
11
|
+
var l = a(`${t} inline-flex items-center justify-center gap-2 rounded-basalt-md text-sm font-medium transition-colors ${n} disabled:pointer-events-none disabled:opacity-50 aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`, {
|
|
11
12
|
variants: {
|
|
12
13
|
variant: {
|
|
13
14
|
default: "bg-basalt-primary text-basalt-primary-foreground hover:bg-basalt-primary/90",
|
|
@@ -28,46 +29,96 @@ var c = i(`inline-flex items-center justify-center gap-2 rounded-basalt-md text-
|
|
|
28
29
|
variant: "default",
|
|
29
30
|
size: "default"
|
|
30
31
|
}
|
|
31
|
-
}),
|
|
32
|
-
let
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
}), u = r.forwardRef(({ className: t, variant: n, size: a, asChild: u = !1, type: d = "button", loading: f = !1, icon: p, children: m, disabled: h, ...g }, _) => {
|
|
33
|
+
let v = !!(h || f);
|
|
34
|
+
if (u) {
|
|
35
|
+
if (v && r.isValidElement(m)) {
|
|
36
|
+
let o = m, c = typeof o.type == "string" && o.type.toLowerCase() === "button", u = (e) => {
|
|
37
|
+
e.preventDefault(), e.stopPropagation(), "stopImmediatePropagation" in e && typeof e.stopImmediatePropagation == "function" && e.stopImmediatePropagation();
|
|
38
|
+
}, d = (e) => {
|
|
39
|
+
e.key === "Enter" || e.key === " " || e.key === "Spacebar" ? (e.preventDefault(), e.stopPropagation(), typeof e.nativeEvent?.stopImmediatePropagation == "function" && e.nativeEvent.stopImmediatePropagation()) : (e.stopPropagation(), typeof e.nativeEvent?.stopImmediatePropagation == "function" && e.nativeEvent.stopImmediatePropagation());
|
|
40
|
+
}, p = {
|
|
41
|
+
onClick: u,
|
|
42
|
+
onClickCapture: u,
|
|
43
|
+
onKeyDown: d,
|
|
44
|
+
onKeyDownCapture: d,
|
|
45
|
+
onKeyUp: d,
|
|
46
|
+
onKeyUpCapture: d,
|
|
47
|
+
onKeyPress: d,
|
|
48
|
+
onKeyPressCapture: d,
|
|
49
|
+
onMouseDown: u,
|
|
50
|
+
onMouseDownCapture: u,
|
|
51
|
+
onMouseUp: u,
|
|
52
|
+
onMouseUpCapture: u,
|
|
53
|
+
onPointerDown: u,
|
|
54
|
+
onPointerDownCapture: u,
|
|
55
|
+
onPointerUp: u,
|
|
56
|
+
onPointerUpCapture: u,
|
|
57
|
+
onAuxClick: u,
|
|
58
|
+
onAuxClickCapture: u
|
|
59
|
+
}, { disabled: h, ...v } = o.props, y = r.cloneElement(o, {
|
|
60
|
+
...v,
|
|
61
|
+
...p,
|
|
62
|
+
...c ? { disabled: !0 } : {},
|
|
63
|
+
"aria-disabled": !0,
|
|
64
|
+
"aria-busy": f ? !0 : void 0,
|
|
65
|
+
tabIndex: -1
|
|
66
|
+
}), b = {
|
|
67
|
+
...g,
|
|
68
|
+
...p,
|
|
69
|
+
"aria-disabled": !0,
|
|
70
|
+
"aria-busy": f ? "true" : void 0,
|
|
71
|
+
tabIndex: -1
|
|
72
|
+
};
|
|
73
|
+
return c && (b.disabled = !0), /* @__PURE__ */ s(i, {
|
|
74
|
+
className: e(l({
|
|
75
|
+
variant: n,
|
|
76
|
+
size: a
|
|
77
|
+
}), t),
|
|
78
|
+
ref: _,
|
|
79
|
+
...b,
|
|
80
|
+
children: y
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return /* @__PURE__ */ s(i, {
|
|
84
|
+
className: e(l({
|
|
85
|
+
variant: n,
|
|
86
|
+
size: a
|
|
87
|
+
}), t),
|
|
88
|
+
ref: _,
|
|
89
|
+
...g,
|
|
90
|
+
children: m
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
let y = f ? /* @__PURE__ */ s(o, {
|
|
94
|
+
className: "animate-basalt-spin motion-reduce:animate-none",
|
|
44
95
|
"aria-hidden": "true"
|
|
45
|
-
}) :
|
|
46
|
-
return /* @__PURE__ */
|
|
47
|
-
className: e(
|
|
96
|
+
}) : p;
|
|
97
|
+
return /* @__PURE__ */ c("button", {
|
|
98
|
+
className: e(l({
|
|
48
99
|
variant: n,
|
|
49
|
-
size:
|
|
100
|
+
size: a
|
|
50
101
|
}), t),
|
|
51
|
-
ref:
|
|
52
|
-
type:
|
|
53
|
-
disabled:
|
|
54
|
-
"aria-busy":
|
|
55
|
-
...
|
|
56
|
-
children: [
|
|
102
|
+
ref: _,
|
|
103
|
+
type: d,
|
|
104
|
+
disabled: v,
|
|
105
|
+
"aria-busy": f || void 0,
|
|
106
|
+
...g,
|
|
107
|
+
children: [y, m]
|
|
57
108
|
});
|
|
58
109
|
});
|
|
59
|
-
|
|
60
|
-
var
|
|
61
|
-
ref:
|
|
62
|
-
className: e(
|
|
110
|
+
u.displayName = "Button";
|
|
111
|
+
var d = r.forwardRef(({ className: t, variant: n, size: r, icon: i, children: a, ...o }, s) => /* @__PURE__ */ c("a", {
|
|
112
|
+
ref: s,
|
|
113
|
+
className: e(l({
|
|
63
114
|
variant: n,
|
|
64
115
|
size: r
|
|
65
116
|
}), t),
|
|
66
117
|
...o,
|
|
67
118
|
children: [i, a]
|
|
68
119
|
}));
|
|
69
|
-
|
|
120
|
+
d.displayName = "LinkButton";
|
|
70
121
|
//#endregion
|
|
71
|
-
export {
|
|
122
|
+
export { u as Button, d as LinkButton, l as buttonVariants };
|
|
72
123
|
|
|
73
124
|
//# sourceMappingURL=button.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","names":[],"sources":["../../src/components/button.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Loader2 } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { FOCUS_RING } from \"./overlay\";\n\nconst buttonVariants = cva(\n\t`inline-flex items-center justify-center gap-2 rounded-basalt-md text-sm font-medium transition-colors ${FOCUS_RING} disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`,\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"bg-basalt-primary text-basalt-primary-foreground hover:bg-basalt-primary/90\",\n\t\t\t\tsecondary: \"bg-basalt-control text-basalt-foreground hover:bg-basalt-accent\",\n\t\t\t\tdestructive:\n\t\t\t\t\t\"bg-basalt-destructive text-basalt-destructive-foreground hover:bg-basalt-destructive/90\",\n\t\t\t\toutline:\n\t\t\t\t\t\"border border-basalt-border bg-basalt-control hover:bg-basalt-accent hover:text-basalt-accent-foreground\",\n\t\t\t\tghost: \"hover:bg-basalt-accent hover:text-basalt-accent-foreground\",\n\t\t\t\tlink: \"text-basalt-primary underline-offset-4 hover:underline\",\n\t\t\t},\n\t\t\tsize: {\n\t\t\t\tdefault: \"h-9 px-4 py-2\",\n\t\t\t\tsm: \"h-8 rounded-basalt-md px-3 text-xs\",\n\t\t\t\tlg: \"h-10 rounded-basalt-md px-6\",\n\t\t\t\ticon: \"h-9 w-9\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: {\n\t\t\tvariant: \"default\",\n\t\t\tsize: \"default\",\n\t\t},\n\t},\n);\n\nexport interface ButtonProps\n\textends React.ButtonHTMLAttributes<HTMLButtonElement>,\n\t\tVariantProps<typeof buttonVariants> {\n\tasChild?: boolean;\n\tloading?: boolean;\n\ticon?: React.ReactNode;\n}\n\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n\t(\n\t\t{\n\t\t\tclassName,\n\t\t\tvariant,\n\t\t\tsize,\n\t\t\tasChild = false,\n\t\t\ttype = \"button\",\n\t\t\tloading = false,\n\t\t\ticon,\n\t\t\tchildren,\n\t\t\tdisabled,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst Comp = asChild ? Slot : \"button\";\n\t\tif (asChild) {\n\t\t\treturn (\n\t\t\t\t<Comp className={cn(buttonVariants({ variant, size }), className)} ref={ref} {...props}>\n\t\t\t\t\t{children}\n\t\t\t\t</Comp>\n\t\t\t);\n\t\t}\n\t\tconst iconNode = loading ? (\n\t\t\t<Loader2 className=\"animate-basalt-spin\" aria-hidden=\"true\" />\n\t\t) : (\n\t\t\ticon\n\t\t);\n\t\treturn (\n\t\t\t<button\n\t\t\t\tclassName={cn(buttonVariants({ variant, size }), className)}\n\t\t\t\tref={ref}\n\t\t\t\ttype={type}\n\t\t\t\tdisabled={disabled || loading}\n\t\t\t\taria-busy={loading || undefined}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{iconNode}\n\t\t\t\t{children}\n\t\t\t</button>\n\t\t);\n\t},\n);\nButton.displayName = \"Button\";\n\nexport interface LinkButtonProps\n\textends React.AnchorHTMLAttributes<HTMLAnchorElement>,\n\t\tVariantProps<typeof buttonVariants> {\n\ticon?: React.ReactNode;\n}\n\nexport const LinkButton = React.forwardRef<HTMLAnchorElement, LinkButtonProps>(\n\t({ className, variant, size, icon, children, ...props }, ref) => (\n\t\t<a ref={ref} className={cn(buttonVariants({ variant, size }), className)} {...props}>\n\t\t\t{icon}\n\t\t\t{children}\n\t\t</a>\n\t),\n);\nLinkButton.displayName = \"LinkButton\";\n\nexport { buttonVariants };\n"],"mappings":";;;;;;;;;AAOA,IAAM,IAAiB,EACtB,yGAAyG,EAAW,gHACpH;CACC,UAAU;EACT,SAAS;GACR,SAAS;GACT,WAAW;GACX,aACC;GACD,SACC;GACD,OAAO;GACP,MAAM;EACP;EACA,MAAM;GACL,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,MAAM;EACP;CACD;CACA,iBAAiB;EAChB,SAAS;EACT,MAAM;CACP;AACD,CACD,GAUa,IAAS,EAAM,YAE1B,EACC,cACA,YACA,SACA,aAAU,IACV,UAAO,UACP,aAAU,IACV,SACA,aACA,aACA,GAAG,KAEJ,MACI;CACJ,IAAM,IAAO,IAAU,IAAO;CAC9B,IAAI,GACH,OACC,kBAAC,GAAD;EAAM,WAAW,EAAG,EAAe;GAAE;GAAS;EAAK,CAAC,GAAG,CAAS;EAAQ;EAAK,GAAI;EAC/E;CACI,CAAA;CAGR,IAAM,IAAW,IAChB,kBAAC,GAAD;EAAS,WAAU;EAAsB,eAAY;CAAQ,CAAA,IAE7D;CAED,OACC,kBAAC,UAAD;EACC,WAAW,EAAG,EAAe;GAAE;GAAS;EAAK,CAAC,GAAG,CAAS;EACrD;EACC;EACN,UAAU,KAAY;EACtB,aAAW,KAAW,KAAA;EACtB,GAAI;EANL,UAAA,CAQE,GACA,CACM;;AAEV,CACD;AACA,EAAO,cAAc;AAQrB,IAAa,IAAa,EAAM,YAC9B,EAAE,cAAW,YAAS,SAAM,SAAM,aAAU,GAAG,KAAS,MACxD,kBAAC,KAAD;CAAQ;CAAK,WAAW,EAAG,EAAe;EAAE;EAAS;CAAK,CAAC,GAAG,CAAS;CAAG,GAAI;CAA9E,UAAA,CACE,GACA,CACC;AAEL,CAAA,CAAA;AACA,EAAW,cAAc"}
|
|
1
|
+
{"version":3,"file":"button.js","names":[],"sources":["../../src/components/button.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Loader2 } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { FOCUS_RING } from \"./overlay\";\n\nconst buttonVariants = cva(\n\t`${BASALT_UI_CLASS} inline-flex items-center justify-center gap-2 rounded-basalt-md text-sm font-medium transition-colors ${FOCUS_RING} disabled:pointer-events-none disabled:opacity-50 aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`,\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"bg-basalt-primary text-basalt-primary-foreground hover:bg-basalt-primary/90\",\n\t\t\t\tsecondary: \"bg-basalt-control text-basalt-foreground hover:bg-basalt-accent\",\n\t\t\t\tdestructive:\n\t\t\t\t\t\"bg-basalt-destructive text-basalt-destructive-foreground hover:bg-basalt-destructive/90\",\n\t\t\t\toutline:\n\t\t\t\t\t\"border border-basalt-border bg-basalt-control hover:bg-basalt-accent hover:text-basalt-accent-foreground\",\n\t\t\t\tghost: \"hover:bg-basalt-accent hover:text-basalt-accent-foreground\",\n\t\t\t\tlink: \"text-basalt-primary underline-offset-4 hover:underline\",\n\t\t\t},\n\t\t\tsize: {\n\t\t\t\tdefault: \"h-9 px-4 py-2\",\n\t\t\t\tsm: \"h-8 rounded-basalt-md px-3 text-xs\",\n\t\t\t\tlg: \"h-10 rounded-basalt-md px-6\",\n\t\t\t\ticon: \"h-9 w-9\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: {\n\t\t\tvariant: \"default\",\n\t\t\tsize: \"default\",\n\t\t},\n\t},\n);\n\nexport interface ButtonProps\n\textends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, \"type\">,\n\t\tOmit<VariantProps<typeof buttonVariants>, \"variant\" | \"size\"> {\n\t/**\n\t * Visual style variant.\n\t * @default \"default\"\n\t */\n\tvariant?: VariantProps<typeof buttonVariants>[\"variant\"];\n\t/**\n\t * Sizing preset.\n\t * @default \"default\"\n\t */\n\tsize?: VariantProps<typeof buttonVariants>[\"size\"];\n\t/**\n\t * Pass control to child element slot.\n\t *\n\t * Note: Child element must forward props and ref in all states. When disabled or loading,\n\t * asChild suppresses click, keyboard activation (Enter/Space), and pointer handlers on both\n\t * child and parent while retaining browser default Tab navigation without trapping focus.\n\t * To preserve single DOM node layout and avoid unexpected child layout shifts, no internal\n\t * spinner is inserted in asChild loading mode; visual disabled state is expressed via aria-disabled.\n\t *\n\t * @default false\n\t */\n\tasChild?: boolean;\n\t/**\n\t * Button type attribute.\n\t * @default \"button\"\n\t */\n\ttype?: React.ButtonHTMLAttributes<HTMLButtonElement>[\"type\"];\n\t/**\n\t * Display loading spinner and disable interactions.\n\t * @default false\n\t */\n\tloading?: boolean;\n\t/**\n\t * Optional leading icon slot.\n\t */\n\ticon?: React.ReactNode;\n}\n\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n\t(\n\t\t{\n\t\t\tclassName,\n\t\t\tvariant,\n\t\t\tsize,\n\t\t\tasChild = false,\n\t\t\ttype = \"button\",\n\t\t\tloading = false,\n\t\t\ticon,\n\t\t\tchildren,\n\t\t\tdisabled,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst isDisabled = Boolean(disabled || loading);\n\n\t\tif (asChild) {\n\t\t\tif (isDisabled && React.isValidElement(children)) {\n\t\t\t\tconst childElement = children as React.ReactElement<Record<string, unknown>>;\n\t\t\t\tconst isNativeButton =\n\t\t\t\t\ttypeof childElement.type === \"string\" && childElement.type.toLowerCase() === \"button\";\n\n\t\t\t\tconst blockActivationEvent = (event: React.SyntheticEvent | Event) => {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\tif (\n\t\t\t\t\t\t\"stopImmediatePropagation\" in event &&\n\t\t\t\t\t\ttypeof event.stopImmediatePropagation === \"function\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst handleKeyEvent = (event: React.KeyboardEvent) => {\n\t\t\t\t\tif (event.key === \"Enter\" || event.key === \" \" || event.key === \"Spacebar\") {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\tif (typeof event.nativeEvent?.stopImmediatePropagation === \"function\") {\n\t\t\t\t\t\t\tevent.nativeEvent.stopImmediatePropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// For non-activation keys like Tab, suppress user handlers from running while preserving browser default navigation\n\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\tif (typeof event.nativeEvent?.stopImmediatePropagation === \"function\") {\n\t\t\t\t\t\t\tevent.nativeEvent.stopImmediatePropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst blockedHandlers = {\n\t\t\t\t\tonClick: blockActivationEvent,\n\t\t\t\t\tonClickCapture: blockActivationEvent,\n\t\t\t\t\tonKeyDown: handleKeyEvent,\n\t\t\t\t\tonKeyDownCapture: handleKeyEvent,\n\t\t\t\t\tonKeyUp: handleKeyEvent,\n\t\t\t\t\tonKeyUpCapture: handleKeyEvent,\n\t\t\t\t\tonKeyPress: handleKeyEvent,\n\t\t\t\t\tonKeyPressCapture: handleKeyEvent,\n\t\t\t\t\tonMouseDown: blockActivationEvent,\n\t\t\t\t\tonMouseDownCapture: blockActivationEvent,\n\t\t\t\t\tonMouseUp: blockActivationEvent,\n\t\t\t\t\tonMouseUpCapture: blockActivationEvent,\n\t\t\t\t\tonPointerDown: blockActivationEvent,\n\t\t\t\t\tonPointerDownCapture: blockActivationEvent,\n\t\t\t\t\tonPointerUp: blockActivationEvent,\n\t\t\t\t\tonPointerUpCapture: blockActivationEvent,\n\t\t\t\t\tonAuxClick: blockActivationEvent,\n\t\t\t\t\tonAuxClickCapture: blockActivationEvent,\n\t\t\t\t};\n\n\t\t\t\tconst { disabled: _ignoredChildDisabled, ...sanitizedProps } = childElement.props;\n\n\t\t\t\tconst sanitizedChild = React.cloneElement(childElement, {\n\t\t\t\t\t...sanitizedProps,\n\t\t\t\t\t...blockedHandlers,\n\t\t\t\t\t...(isNativeButton ? { disabled: true } : {}),\n\t\t\t\t\t\"aria-disabled\": true,\n\t\t\t\t\t\"aria-busy\": loading ? true : undefined,\n\t\t\t\t\ttabIndex: -1,\n\t\t\t\t});\n\n\t\t\t\tconst slotProps: Record<string, unknown> = {\n\t\t\t\t\t...props,\n\t\t\t\t\t...blockedHandlers,\n\t\t\t\t\t\"aria-disabled\": true,\n\t\t\t\t\t\"aria-busy\": loading ? \"true\" : undefined,\n\t\t\t\t\ttabIndex: -1,\n\t\t\t\t};\n\t\t\t\tif (isNativeButton) {\n\t\t\t\t\tslotProps.disabled = true;\n\t\t\t\t}\n\n\t\t\t\treturn (\n\t\t\t\t\t<Slot\n\t\t\t\t\t\tclassName={cn(buttonVariants({ variant, size }), className)}\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\t{...slotProps}\n\t\t\t\t\t>\n\t\t\t\t\t\t{sanitizedChild}\n\t\t\t\t\t</Slot>\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn (\n\t\t\t\t<Slot className={cn(buttonVariants({ variant, size }), className)} ref={ref} {...props}>\n\t\t\t\t\t{children}\n\t\t\t\t</Slot>\n\t\t\t);\n\t\t}\n\n\t\tconst iconNode = loading ? (\n\t\t\t<Loader2 className=\"animate-basalt-spin motion-reduce:animate-none\" aria-hidden=\"true\" />\n\t\t) : (\n\t\t\ticon\n\t\t);\n\t\treturn (\n\t\t\t<button\n\t\t\t\tclassName={cn(buttonVariants({ variant, size }), className)}\n\t\t\t\tref={ref}\n\t\t\t\ttype={type}\n\t\t\t\tdisabled={isDisabled}\n\t\t\t\taria-busy={loading || undefined}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{iconNode}\n\t\t\t\t{children}\n\t\t\t</button>\n\t\t);\n\t},\n);\nButton.displayName = \"Button\";\n\nexport interface LinkButtonProps\n\textends React.AnchorHTMLAttributes<HTMLAnchorElement>,\n\t\tOmit<VariantProps<typeof buttonVariants>, \"variant\" | \"size\"> {\n\t/**\n\t * Visual style variant.\n\t * @default \"default\"\n\t */\n\tvariant?: VariantProps<typeof buttonVariants>[\"variant\"];\n\t/**\n\t * Sizing preset.\n\t * @default \"default\"\n\t */\n\tsize?: VariantProps<typeof buttonVariants>[\"size\"];\n\t/**\n\t * Optional leading icon slot.\n\t */\n\ticon?: React.ReactNode;\n}\n\nexport const LinkButton = React.forwardRef<HTMLAnchorElement, LinkButtonProps>(\n\t({ className, variant, size, icon, children, ...props }, ref) => (\n\t\t<a ref={ref} className={cn(buttonVariants({ variant, size }), className)} {...props}>\n\t\t\t{icon}\n\t\t\t{children}\n\t\t</a>\n\t),\n);\nLinkButton.displayName = \"LinkButton\";\n\nexport { buttonVariants };\n"],"mappings":";;;;;;;;;;AAQA,IAAM,IAAiB,EACtB,GAAG,EAAgB,yGAAyG,EAAW,yIACvI;CACC,UAAU;EACT,SAAS;GACR,SAAS;GACT,WAAW;GACX,aACC;GACD,SACC;GACD,OAAO;GACP,MAAM;EACP;EACA,MAAM;GACL,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,MAAM;EACP;CACD;CACA,iBAAiB;EAChB,SAAS;EACT,MAAM;CACP;AACD,CACD,GA2Ca,IAAS,EAAM,YAE1B,EACC,cACA,YACA,SACA,aAAU,IACV,UAAO,UACP,aAAU,IACV,SACA,aACA,aACA,GAAG,KAEJ,MACI;CACJ,IAAM,IAAa,GAAQ,KAAY;CAEvC,IAAI,GAAS;EACZ,IAAI,KAAc,EAAM,eAAe,CAAQ,GAAG;GACjD,IAAM,IAAe,GACf,IACL,OAAO,EAAa,QAAS,YAAY,EAAa,KAAK,YAAY,MAAM,UAExE,KAAwB,MAAwC;IAGrE,AAFA,EAAM,eAAe,GACrB,EAAM,gBAAgB,GAErB,8BAA8B,KAC9B,OAAO,EAAM,4BAA6B,cAE1C,EAAM,yBAAyB;GAEjC,GAEM,KAAkB,MAA+B;IACtD,AAAI,EAAM,QAAQ,WAAW,EAAM,QAAQ,OAAO,EAAM,QAAQ,cAC/D,EAAM,eAAe,GACrB,EAAM,gBAAgB,GAClB,OAAO,EAAM,aAAa,4BAA6B,cAC1D,EAAM,YAAY,yBAAyB,MAI5C,EAAM,gBAAgB,GAClB,OAAO,EAAM,aAAa,4BAA6B,cAC1D,EAAM,YAAY,yBAAyB;GAG9C,GAEM,IAAkB;IACvB,SAAS;IACT,gBAAgB;IAChB,WAAW;IACX,kBAAkB;IAClB,SAAS;IACT,gBAAgB;IAChB,YAAY;IACZ,mBAAmB;IACnB,aAAa;IACb,oBAAoB;IACpB,WAAW;IACX,kBAAkB;IAClB,eAAe;IACf,sBAAsB;IACtB,aAAa;IACb,oBAAoB;IACpB,YAAY;IACZ,mBAAmB;GACpB,GAEM,EAAE,UAAU,GAAuB,GAAG,MAAmB,EAAa,OAEtE,IAAiB,EAAM,aAAa,GAAc;IACvD,GAAG;IACH,GAAG;IACH,GAAI,IAAiB,EAAE,UAAU,GAAK,IAAI,CAAC;IAC3C,iBAAiB;IACjB,aAAa,IAAU,KAAO,KAAA;IAC9B,UAAU;GACX,CAAC,GAEK,IAAqC;IAC1C,GAAG;IACH,GAAG;IACH,iBAAiB;IACjB,aAAa,IAAU,SAAS,KAAA;IAChC,UAAU;GACX;GAKA,OAJI,MACH,EAAU,WAAW,KAIrB,kBAAC,GAAD;IACC,WAAW,EAAG,EAAe;KAAE;KAAS;IAAK,CAAC,GAAG,CAAS;IACrD;IACL,GAAI;IAEH,UAAA;GACI,CAAA;EAER;EAEA,OACC,kBAAC,GAAD;GAAM,WAAW,EAAG,EAAe;IAAE;IAAS;GAAK,CAAC,GAAG,CAAS;GAAQ;GAAK,GAAI;GAC/E;EACI,CAAA;CAER;CAEA,IAAM,IAAW,IAChB,kBAAC,GAAD;EAAS,WAAU;EAAiD,eAAY;CAAQ,CAAA,IAExF;CAED,OACC,kBAAC,UAAD;EACC,WAAW,EAAG,EAAe;GAAE;GAAS;EAAK,CAAC,GAAG,CAAS;EACrD;EACC;EACN,UAAU;EACV,aAAW,KAAW,KAAA;EACtB,GAAI;EANL,UAAA,CAQE,GACA,CACM;;AAEV,CACD;AACA,EAAO,cAAc;AAqBrB,IAAa,IAAa,EAAM,YAC9B,EAAE,cAAW,YAAS,SAAM,SAAM,aAAU,GAAG,KAAS,MACxD,kBAAC,KAAD;CAAQ;CAAK,WAAW,EAAG,EAAe;EAAE;EAAS;CAAK,CAAC,GAAG,CAAS;CAAG,GAAI;CAA9E,UAAA,CACE,GACA,CACC;AAEL,CAAA,CAAA;AACA,EAAW,cAAc"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type ChatBubbleVariant = "assistant" | "system" | "user";
|
|
3
|
+
export interface ChatBubbleProps {
|
|
4
|
+
/**
|
|
5
|
+
* Alignment and fill.
|
|
6
|
+
* @default "assistant"
|
|
7
|
+
*/
|
|
8
|
+
variant?: ChatBubbleVariant;
|
|
9
|
+
/**
|
|
10
|
+
* Show a streaming caret after the body.
|
|
11
|
+
* @default false
|
|
12
|
+
*/
|
|
13
|
+
streaming?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Optional class name applied to the message bubble.
|
|
16
|
+
* Component only accepts variant, streaming, className, and children without native HTML rest forwarding or forwarded ref.
|
|
17
|
+
*/
|
|
18
|
+
className?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Message content.
|
|
21
|
+
*/
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
}
|
|
24
|
+
export declare function ChatBubble({ variant, streaming, className, children, }: ChatBubbleProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../utils/cn.js";
|
|
3
|
+
import { BASALT_UI_CLASS as t } from "../utils/control-surface.js";
|
|
4
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/chat-bubble.tsx
|
|
6
|
+
function i({ variant: i = "assistant", streaming: a = !1, className: o, children: s }) {
|
|
7
|
+
if (i === "system") return /* @__PURE__ */ n("p", {
|
|
8
|
+
className: e(t, "px-1 text-center text-[11px] text-basalt-muted-foreground", o),
|
|
9
|
+
children: s
|
|
10
|
+
});
|
|
11
|
+
let c = i === "user";
|
|
12
|
+
return /* @__PURE__ */ n("div", {
|
|
13
|
+
className: e(t, "flex w-full", c ? "justify-end" : "justify-start"),
|
|
14
|
+
children: /* @__PURE__ */ r("div", {
|
|
15
|
+
className: e("max-w-[92%] px-3.5 py-2 text-sm leading-5 shadow-sm", c ? "rounded-2xl rounded-br-md bg-basalt-primary text-basalt-primary-foreground" : "rounded-2xl rounded-bl-md bg-basalt-secondary text-basalt-foreground ring-1 ring-basalt-border/50", o),
|
|
16
|
+
children: [s, a ? /* @__PURE__ */ n("span", {
|
|
17
|
+
className: "mt-1 inline-block h-3 w-1.5 animate-pulse rounded-sm bg-basalt-primary/70 align-middle motion-reduce:animate-none",
|
|
18
|
+
"aria-hidden": !0
|
|
19
|
+
}) : null]
|
|
20
|
+
})
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { i as ChatBubble };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=chat-bubble.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-bubble.js","names":[],"sources":["../../src/components/chat-bubble.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\n\nexport type ChatBubbleVariant = \"assistant\" | \"system\" | \"user\";\n\nexport interface ChatBubbleProps {\n\t/**\n\t * Alignment and fill.\n\t * @default \"assistant\"\n\t */\n\tvariant?: ChatBubbleVariant;\n\t/**\n\t * Show a streaming caret after the body.\n\t * @default false\n\t */\n\tstreaming?: boolean;\n\t/**\n\t * Optional class name applied to the message bubble.\n\t * Component only accepts variant, streaming, className, and children without native HTML rest forwarding or forwarded ref.\n\t */\n\tclassName?: string;\n\t/**\n\t * Message content.\n\t */\n\tchildren: ReactNode;\n}\n\nexport function ChatBubble({\n\tvariant = \"assistant\",\n\tstreaming = false,\n\tclassName,\n\tchildren,\n}: ChatBubbleProps) {\n\tif (variant === \"system\") {\n\t\treturn (\n\t\t\t<p\n\t\t\t\tclassName={cn(\n\t\t\t\t\tBASALT_UI_CLASS,\n\t\t\t\t\t\"px-1 text-center text-[11px] text-basalt-muted-foreground\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</p>\n\t\t);\n\t}\n\tconst user = variant === \"user\";\n\treturn (\n\t\t<div className={cn(BASALT_UI_CLASS, \"flex w-full\", user ? \"justify-end\" : \"justify-start\")}>\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"max-w-[92%] px-3.5 py-2 text-sm leading-5 shadow-sm\",\n\t\t\t\t\tuser\n\t\t\t\t\t\t? \"rounded-2xl rounded-br-md bg-basalt-primary text-basalt-primary-foreground\"\n\t\t\t\t\t\t: \"rounded-2xl rounded-bl-md bg-basalt-secondary text-basalt-foreground ring-1 ring-basalt-border/50\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t\t{streaming ? (\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName=\"mt-1 inline-block h-3 w-1.5 animate-pulse rounded-sm bg-basalt-primary/70 align-middle motion-reduce:animate-none\"\n\t\t\t\t\t\taria-hidden\n\t\t\t\t\t/>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;AA4BA,SAAgB,EAAW,EAC1B,aAAU,aACV,eAAY,IACZ,cACA,eACmB;CACnB,IAAI,MAAY,UACf,OACC,kBAAC,KAAD;EACC,WAAW,EACV,GACA,6DACA,CACD;EAEC;CACC,CAAA;CAGL,IAAM,IAAO,MAAY;CACzB,OACC,kBAAC,OAAD;EAAK,WAAW,EAAG,GAAiB,eAAe,IAAO,gBAAgB,eAAe;EACxF,UAAA,kBAAC,OAAD;GACC,WAAW,EACV,uDACA,IACG,+EACA,qGACH,CACD;GAPD,UAAA,CASE,GACA,IACA,kBAAC,QAAD;IACC,WAAU;IACV,eAAA;GACA,CAAA,IACE,IACA;;CACD,CAAA;AAEP"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface ChatComposerProps {
|
|
2
|
+
/**
|
|
3
|
+
* Disable the field and send.
|
|
4
|
+
* @default false
|
|
5
|
+
*/
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Replace send with stop. When streaming is true, the stop button renders and triggers onCancel.
|
|
9
|
+
* @default false
|
|
10
|
+
*/
|
|
11
|
+
streaming?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Field accessible name.
|
|
14
|
+
* @default "Message"
|
|
15
|
+
*/
|
|
16
|
+
label?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Placeholder when empty.
|
|
19
|
+
*/
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Called with trimmed text on send. Operates on internal draft state and does not wait for a Promise (caller manages asynchronous errors).
|
|
23
|
+
*/
|
|
24
|
+
onSend: (text: string) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Called when the stop control is pressed while streaming is active.
|
|
27
|
+
*/
|
|
28
|
+
onCancel?: () => void;
|
|
29
|
+
/**
|
|
30
|
+
* Send control accessible name.
|
|
31
|
+
* @default "Send message"
|
|
32
|
+
*/
|
|
33
|
+
sendLabel?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Stop control accessible name.
|
|
36
|
+
* @default "Stop generating"
|
|
37
|
+
*/
|
|
38
|
+
cancelLabel?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Optional class name applied to the form container.
|
|
41
|
+
* Component manages internal draft/height state and does not forward native form rest attributes or ref.
|
|
42
|
+
*/
|
|
43
|
+
className?: string;
|
|
44
|
+
}
|
|
45
|
+
export declare function ChatComposer({ disabled, streaming, label, placeholder, onSend, onCancel, sendLabel, cancelLabel, className, }: ChatComposerProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../utils/cn.js";
|
|
3
|
+
import { Button as t } from "./button.js";
|
|
4
|
+
import { InputArea as n } from "./input-area.js";
|
|
5
|
+
import { useRef as r, useState as i } from "react";
|
|
6
|
+
import { ArrowUp as a, Square as o } from "lucide-react";
|
|
7
|
+
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
8
|
+
//#region src/components/chat-composer.tsx
|
|
9
|
+
function l({ disabled: l = !1, streaming: u = !1, label: d = "Message", placeholder: f, onSend: p, onCancel: m, sendLabel: h = "Send message", cancelLabel: g = "Stop generating", className: _ }) {
|
|
10
|
+
let [v, y] = i(""), b = r(null), x = r(!1), S = () => {
|
|
11
|
+
let e = b.current;
|
|
12
|
+
e && (e.style.height = "0px", e.style.height = `${Math.min(e.scrollHeight, 160)}px`);
|
|
13
|
+
}, C = () => {
|
|
14
|
+
let e = v.trim();
|
|
15
|
+
!e || l || u || (p(e), y(""), requestAnimationFrame(() => {
|
|
16
|
+
let e = b.current;
|
|
17
|
+
e && (e.style.height = "0px", e.style.height = `${Math.min(e.scrollHeight, 160)}px`, e.focus());
|
|
18
|
+
}));
|
|
19
|
+
};
|
|
20
|
+
return /* @__PURE__ */ s("form", {
|
|
21
|
+
className: e("border-t border-basalt-border/50 bg-basalt-card p-3", _),
|
|
22
|
+
onSubmit: (e) => {
|
|
23
|
+
e.preventDefault(), C();
|
|
24
|
+
},
|
|
25
|
+
children: /* @__PURE__ */ c("div", {
|
|
26
|
+
className: "flex items-end gap-2 rounded-2xl bg-basalt-secondary p-2 ring-1 ring-basalt-border/50",
|
|
27
|
+
children: [/* @__PURE__ */ s(n, {
|
|
28
|
+
ref: b,
|
|
29
|
+
value: v,
|
|
30
|
+
onChange: (e) => {
|
|
31
|
+
y(e.target.value), requestAnimationFrame(S);
|
|
32
|
+
},
|
|
33
|
+
onCompositionStart: () => {
|
|
34
|
+
x.current = !0;
|
|
35
|
+
},
|
|
36
|
+
onCompositionEnd: () => {
|
|
37
|
+
x.current = !1;
|
|
38
|
+
},
|
|
39
|
+
onKeyDown: (e) => {
|
|
40
|
+
x.current || e.nativeEvent.isComposing || e.key !== "Enter" || e.shiftKey || (e.preventDefault(), C());
|
|
41
|
+
},
|
|
42
|
+
rows: 1,
|
|
43
|
+
placeholder: f,
|
|
44
|
+
disabled: l,
|
|
45
|
+
"aria-label": d,
|
|
46
|
+
className: "max-h-40 min-h-[40px] flex-1 resize-none border-0 bg-transparent p-2 shadow-none ring-0"
|
|
47
|
+
}), u ? /* @__PURE__ */ s(t, {
|
|
48
|
+
type: "button",
|
|
49
|
+
size: "icon",
|
|
50
|
+
variant: "secondary",
|
|
51
|
+
onClick: m,
|
|
52
|
+
disabled: !m,
|
|
53
|
+
"aria-label": g,
|
|
54
|
+
children: /* @__PURE__ */ s(o, {
|
|
55
|
+
className: "h-3.5 w-3.5 fill-current",
|
|
56
|
+
strokeWidth: 0
|
|
57
|
+
})
|
|
58
|
+
}) : /* @__PURE__ */ s(t, {
|
|
59
|
+
type: "submit",
|
|
60
|
+
size: "icon",
|
|
61
|
+
disabled: l || !v.trim(),
|
|
62
|
+
"aria-label": h,
|
|
63
|
+
children: /* @__PURE__ */ s(a, {
|
|
64
|
+
className: "h-4 w-4",
|
|
65
|
+
strokeWidth: 2.25
|
|
66
|
+
})
|
|
67
|
+
})]
|
|
68
|
+
})
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { l as ChatComposer };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=chat-composer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-composer.js","names":[],"sources":["../../src/components/chat-composer.tsx"],"sourcesContent":["import { ArrowUp, Square } from \"lucide-react\";\nimport { type FormEvent, type KeyboardEvent, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Button } from \"./button\";\nimport { InputArea } from \"./input-area\";\n\nexport interface ChatComposerProps {\n\t/**\n\t * Disable the field and send.\n\t * @default false\n\t */\n\tdisabled?: boolean;\n\t/**\n\t * Replace send with stop. When streaming is true, the stop button renders and triggers onCancel.\n\t * @default false\n\t */\n\tstreaming?: boolean;\n\t/**\n\t * Field accessible name.\n\t * @default \"Message\"\n\t */\n\tlabel?: string;\n\t/**\n\t * Placeholder when empty.\n\t */\n\tplaceholder?: string;\n\t/**\n\t * Called with trimmed text on send. Operates on internal draft state and does not wait for a Promise (caller manages asynchronous errors).\n\t */\n\tonSend: (text: string) => void;\n\t/**\n\t * Called when the stop control is pressed while streaming is active.\n\t */\n\tonCancel?: () => void;\n\t/**\n\t * Send control accessible name.\n\t * @default \"Send message\"\n\t */\n\tsendLabel?: string;\n\t/**\n\t * Stop control accessible name.\n\t * @default \"Stop generating\"\n\t */\n\tcancelLabel?: string;\n\t/**\n\t * Optional class name applied to the form container.\n\t * Component manages internal draft/height state and does not forward native form rest attributes or ref.\n\t */\n\tclassName?: string;\n}\n\nexport function ChatComposer({\n\tdisabled = false,\n\tstreaming = false,\n\tlabel = \"Message\",\n\tplaceholder,\n\tonSend,\n\tonCancel,\n\tsendLabel = \"Send message\",\n\tcancelLabel = \"Stop generating\",\n\tclassName,\n}: ChatComposerProps) {\n\tconst [value, setValue] = useState(\"\");\n\tconst ref = useRef<HTMLTextAreaElement>(null);\n\tconst composingRef = useRef(false);\n\n\tconst resize = () => {\n\t\tconst el = ref.current;\n\t\tif (!el) {\n\t\t\treturn;\n\t\t}\n\t\tel.style.height = \"0px\";\n\t\tel.style.height = `${Math.min(el.scrollHeight, 160)}px`;\n\t};\n\n\tconst submit = () => {\n\t\tconst text = value.trim();\n\t\tif (!text || disabled || streaming) {\n\t\t\treturn;\n\t\t}\n\t\tonSend(text);\n\t\tsetValue(\"\");\n\t\trequestAnimationFrame(() => {\n\t\t\tconst el = ref.current;\n\t\t\tif (el) {\n\t\t\t\tel.style.height = \"0px\";\n\t\t\t\tel.style.height = `${Math.min(el.scrollHeight, 160)}px`;\n\t\t\t\tel.focus();\n\t\t\t}\n\t\t});\n\t};\n\n\tconst onSubmit = (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tsubmit();\n\t};\n\n\tconst onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n\t\tif (composingRef.current || event.nativeEvent.isComposing) {\n\t\t\treturn;\n\t\t}\n\t\tif (event.key !== \"Enter\" || event.shiftKey) {\n\t\t\treturn;\n\t\t}\n\t\tevent.preventDefault();\n\t\tsubmit();\n\t};\n\n\treturn (\n\t\t<form\n\t\t\tclassName={cn(\"border-t border-basalt-border/50 bg-basalt-card p-3\", className)}\n\t\t\tonSubmit={onSubmit}\n\t\t>\n\t\t\t<div className=\"flex items-end gap-2 rounded-2xl bg-basalt-secondary p-2 ring-1 ring-basalt-border/50\">\n\t\t\t\t<InputArea\n\t\t\t\t\tref={ref}\n\t\t\t\t\tvalue={value}\n\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\tsetValue(event.target.value);\n\t\t\t\t\t\trequestAnimationFrame(resize);\n\t\t\t\t\t}}\n\t\t\t\t\tonCompositionStart={() => {\n\t\t\t\t\t\tcomposingRef.current = true;\n\t\t\t\t\t}}\n\t\t\t\t\tonCompositionEnd={() => {\n\t\t\t\t\t\tcomposingRef.current = false;\n\t\t\t\t\t}}\n\t\t\t\t\tonKeyDown={onKeyDown}\n\t\t\t\t\trows={1}\n\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\taria-label={label}\n\t\t\t\t\tclassName=\"max-h-40 min-h-[40px] flex-1 resize-none border-0 bg-transparent p-2 shadow-none ring-0\"\n\t\t\t\t/>\n\t\t\t\t{streaming ? (\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tonClick={onCancel}\n\t\t\t\t\t\tdisabled={!onCancel}\n\t\t\t\t\t\taria-label={cancelLabel}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Square className=\"h-3.5 w-3.5 fill-current\" strokeWidth={0} />\n\t\t\t\t\t</Button>\n\t\t\t\t) : (\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tdisabled={disabled || !value.trim()}\n\t\t\t\t\t\taria-label={sendLabel}\n\t\t\t\t\t>\n\t\t\t\t\t\t<ArrowUp className=\"h-4 w-4\" strokeWidth={2.25} />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</form>\n\t);\n}\n"],"mappings":";;;;;;;;AAmDA,SAAgB,EAAa,EAC5B,cAAW,IACX,eAAY,IACZ,WAAQ,WACR,gBACA,WACA,aACA,eAAY,gBACZ,iBAAc,mBACd,gBACqB;CACrB,IAAM,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,IAAM,EAA4B,IAAI,GACtC,IAAe,EAAO,EAAK,GAE3B,UAAe;EACpB,IAAM,IAAK,EAAI;EACV,MAGL,EAAG,MAAM,SAAS,OAClB,EAAG,MAAM,SAAS,GAAG,KAAK,IAAI,EAAG,cAAc,GAAG,EAAE;CACrD,GAEM,UAAe;EACpB,IAAM,IAAO,EAAM,KAAK;EACpB,CAAC,KAAQ,KAAY,MAGzB,EAAO,CAAI,GACX,EAAS,EAAE,GACX,4BAA4B;GAC3B,IAAM,IAAK,EAAI;GACf,AAAI,MACH,EAAG,MAAM,SAAS,OAClB,EAAG,MAAM,SAAS,GAAG,KAAK,IAAI,EAAG,cAAc,GAAG,EAAE,KACpD,EAAG,MAAM;EAEX,CAAC;CACF;CAkBA,OACC,kBAAC,QAAD;EACC,WAAW,EAAG,uDAAuD,CAAS;EACpE,WAnBM,MAAqB;GAEtC,AADA,EAAM,eAAe,GACrB,EAAO;EACR;EAkBE,UAAA,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACC,kBAAC,GAAD;IACM;IACE;IACP,WAAW,MAAU;KAEpB,AADA,EAAS,EAAM,OAAO,KAAK,GAC3B,sBAAsB,CAAM;IAC7B;IACA,0BAA0B;KACzB,EAAa,UAAU;IACxB;IACA,wBAAwB;KACvB,EAAa,UAAU;IACxB;IACW,YA9BI,MAA8C;KAC5D,EAAa,WAAW,EAAM,YAAY,eAG1C,EAAM,QAAQ,WAAW,EAAM,aAGnC,EAAM,eAAe,GACrB,EAAO;IACR;IAsBI,MAAM;IACO;IACH;IACV,cAAY;IACZ,WAAU;GACV,CAAA,GACA,IACA,kBAAC,GAAD;IACC,MAAK;IACL,MAAK;IACL,SAAQ;IACR,SAAS;IACT,UAAU,CAAC;IACX,cAAY;IAEZ,UAAA,kBAAC,GAAD;KAAQ,WAAU;KAA2B,aAAa;IAAI,CAAA;GACvD,CAAA,IAER,kBAAC,GAAD;IACC,MAAK;IACL,MAAK;IACL,UAAU,KAAY,CAAC,EAAM,KAAK;IAClC,cAAY;IAEZ,UAAA,kBAAC,GAAD;KAAS,WAAU;KAAU,aAAa;IAAO,CAAA;GAC1C,CAAA,CAEL;;CACA,CAAA;AAER"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
export interface ChatHeaderProps extends Omit<HTMLAttributes<HTMLElement>, "title" | "children" | "className"> {
|
|
3
|
+
/**
|
|
4
|
+
* Conversation title.
|
|
5
|
+
*/
|
|
6
|
+
title: ReactNode;
|
|
7
|
+
/**
|
|
8
|
+
* Secondary line under the title.
|
|
9
|
+
*/
|
|
10
|
+
subtitle?: ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* Leading mark, usually an icon.
|
|
13
|
+
*/
|
|
14
|
+
leading?: ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* Trailing actions.
|
|
17
|
+
*/
|
|
18
|
+
children?: ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* Optional class name applied to the header element.
|
|
21
|
+
* Component inherits native header HTMLAttributes on the root element but does not forward ref.
|
|
22
|
+
*/
|
|
23
|
+
className?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function ChatHeader({ title, subtitle, leading, children, className, ...props }: ChatHeaderProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../utils/cn.js";
|
|
3
|
+
import { BASALT_UI_CLASS as t } from "../utils/control-surface.js";
|
|
4
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/chat-header.tsx
|
|
6
|
+
function i({ title: i, subtitle: a, leading: o, children: s, className: c, ...l }) {
|
|
7
|
+
return /* @__PURE__ */ r("header", {
|
|
8
|
+
className: e(t, "flex items-center justify-between gap-2 border-b border-basalt-border/50 px-3 py-2.5", c),
|
|
9
|
+
...l,
|
|
10
|
+
children: [/* @__PURE__ */ r("div", {
|
|
11
|
+
className: "flex min-w-0 items-center gap-2",
|
|
12
|
+
children: [o ? /* @__PURE__ */ n("span", {
|
|
13
|
+
className: "flex h-8 w-8 items-center justify-center",
|
|
14
|
+
children: o
|
|
15
|
+
}) : null, /* @__PURE__ */ r("div", {
|
|
16
|
+
className: "min-w-0",
|
|
17
|
+
children: [/* @__PURE__ */ n("p", {
|
|
18
|
+
className: "truncate text-sm font-semibold tracking-tight",
|
|
19
|
+
children: i
|
|
20
|
+
}), a ? /* @__PURE__ */ n("p", {
|
|
21
|
+
className: "truncate text-[11px] text-basalt-muted-foreground",
|
|
22
|
+
children: a
|
|
23
|
+
}) : null]
|
|
24
|
+
})]
|
|
25
|
+
}), s ? /* @__PURE__ */ n("div", {
|
|
26
|
+
className: "flex items-center gap-1",
|
|
27
|
+
children: s
|
|
28
|
+
}) : null]
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { i as ChatHeader };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=chat-header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-header.js","names":[],"sources":["../../src/components/chat-header.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\n\nexport interface ChatHeaderProps\n\textends Omit<HTMLAttributes<HTMLElement>, \"title\" | \"children\" | \"className\"> {\n\t/**\n\t * Conversation title.\n\t */\n\ttitle: ReactNode;\n\t/**\n\t * Secondary line under the title.\n\t */\n\tsubtitle?: ReactNode;\n\t/**\n\t * Leading mark, usually an icon.\n\t */\n\tleading?: ReactNode;\n\t/**\n\t * Trailing actions.\n\t */\n\tchildren?: ReactNode;\n\t/**\n\t * Optional class name applied to the header element.\n\t * Component inherits native header HTMLAttributes on the root element but does not forward ref.\n\t */\n\tclassName?: string;\n}\n\nexport function ChatHeader({\n\ttitle,\n\tsubtitle,\n\tleading,\n\tchildren,\n\tclassName,\n\t...props\n}: ChatHeaderProps) {\n\treturn (\n\t\t<header\n\t\t\tclassName={cn(\n\t\t\t\tBASALT_UI_CLASS,\n\t\t\t\t\"flex items-center justify-between gap-2 border-b border-basalt-border/50 px-3 py-2.5\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<div className=\"flex min-w-0 items-center gap-2\">\n\t\t\t\t{leading ? (\n\t\t\t\t\t<span className=\"flex h-8 w-8 items-center justify-center\">{leading}</span>\n\t\t\t\t) : null}\n\t\t\t\t<div className=\"min-w-0\">\n\t\t\t\t\t<p className=\"truncate text-sm font-semibold tracking-tight\">{title}</p>\n\t\t\t\t\t{subtitle ? (\n\t\t\t\t\t\t<p className=\"truncate text-[11px] text-basalt-muted-foreground\">{subtitle}</p>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{children ? <div className=\"flex items-center gap-1\">{children}</div> : null}\n\t\t</header>\n\t);\n}\n"],"mappings":";;;;;AA6BA,SAAgB,EAAW,EAC1B,UACA,aACA,YACA,aACA,cACA,GAAG,KACgB;CACnB,OACC,kBAAC,UAAD;EACC,WAAW,EACV,GACA,wFACA,CACD;EACA,GAAI;EANL,UAAA,CAQC,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,IACA,kBAAC,QAAD;IAAM,WAAU;IAA4C,UAAA;GAAc,CAAA,IACvE,MACJ,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACC,kBAAC,KAAD;KAAG,WAAU;KAAiD,UAAA;IAAS,CAAA,GACtE,IACA,kBAAC,KAAD;KAAG,WAAU;KAAqD,UAAA;IAAY,CAAA,IAC3E,IACA;GACD,CAAA,CAAA;EACJ,CAAA,GAAA,IAAW,kBAAC,OAAD;GAAK,WAAU;GAA2B;EAAc,CAAA,IAAI,IACjE;;AAEV"}
|