@burtson-labs/ui 0.14.0 → 0.15.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/LICENSES/DEPENDENCIES.txt +1 -1
- package/README.md +15 -11
- package/dist/components/accordion.d.ts +3 -3
- package/dist/components/accordion.js +13 -10
- package/dist/components/accordion.js.map +1 -1
- package/dist/components/agent-run.d.ts +22 -0
- package/dist/components/agent-run.js +131 -0
- package/dist/components/agent-run.js.map +1 -0
- package/dist/components/alert-dialog.d.ts +12 -10
- package/dist/components/alert-dialog.js +29 -20
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/alert.d.ts +5 -3
- package/dist/components/alert.js +11 -7
- package/dist/components/alert.js.map +1 -1
- package/dist/components/app-shell.d.ts +7 -6
- package/dist/components/app-shell.js +21 -14
- package/dist/components/app-shell.js.map +1 -1
- package/dist/components/attachment.d.ts +3 -3
- package/dist/components/attachment.js +10 -7
- package/dist/components/attachment.js.map +1 -1
- package/dist/components/audio-player.d.ts +2 -2
- package/dist/components/audio-player.js +8 -6
- package/dist/components/audio-player.js.map +1 -1
- package/dist/components/avatar-upload.d.ts +19 -0
- package/dist/components/avatar-upload.js +359 -0
- package/dist/components/avatar-upload.js.map +1 -0
- package/dist/components/avatar.d.ts +3 -3
- package/dist/components/avatar.js +10 -7
- package/dist/components/avatar.js.map +1 -1
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/badge.js +12 -10
- package/dist/components/badge.js.map +1 -1
- package/dist/components/breadcrumb.d.ts +8 -8
- package/dist/components/breadcrumb.js +24 -17
- package/dist/components/breadcrumb.js.map +1 -1
- package/dist/components/button.d.ts +8 -1
- package/dist/components/button.js +12 -3
- package/dist/components/button.js.map +1 -1
- package/dist/components/card.d.ts +8 -8
- package/dist/components/card.js +32 -23
- package/dist/components/card.js.map +1 -1
- package/dist/components/chat-history.d.ts +1 -1
- package/dist/components/chat-history.js +10 -9
- package/dist/components/chat-history.js.map +1 -1
- package/dist/components/chat-layout.d.ts +1 -1
- package/dist/components/chat-layout.js +7 -3
- package/dist/components/chat-layout.js.map +1 -1
- package/dist/components/checkbox-card.d.ts +2 -2
- package/dist/components/checkbox-card.js +9 -7
- package/dist/components/checkbox-card.js.map +1 -1
- package/dist/components/checkbox.d.ts +23 -2
- package/dist/components/checkbox.js +48 -6
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/collapsible.d.ts +2 -2
- package/dist/components/collapsible.js +7 -5
- package/dist/components/collapsible.js.map +1 -1
- package/dist/components/combobox.d.ts +14 -4
- package/dist/components/combobox.js +18 -8
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/command.d.ts +68 -12
- package/dist/components/command.js +38 -25
- package/dist/components/command.js.map +1 -1
- package/dist/components/composer.d.ts +2 -2
- package/dist/components/composer.js +9 -7
- package/dist/components/composer.js.map +1 -1
- package/dist/components/connection-status.d.ts +3 -3
- package/dist/components/connection-status.js +18 -14
- package/dist/components/connection-status.js.map +1 -1
- package/dist/components/context-menu.d.ts +14 -13
- package/dist/components/context-menu.js +33 -23
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/conversation.d.ts +1 -1
- package/dist/components/conversation.js +6 -5
- package/dist/components/conversation.js.map +1 -1
- package/dist/components/copy-button.d.ts +1 -1
- package/dist/components/copy-button.js +3 -2
- package/dist/components/copy-button.js.map +1 -1
- package/dist/components/data-table.d.ts +7 -2
- package/dist/components/data-table.js +16 -42
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/dialog.d.ts +18 -9
- package/dist/components/dialog.js +38 -22
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/dropdown-menu.d.ts +14 -13
- package/dist/components/dropdown-menu.js +33 -23
- package/dist/components/dropdown-menu.js.map +1 -1
- package/dist/components/editor-tabs.d.ts +1 -1
- package/dist/components/editor-tabs.js +6 -5
- package/dist/components/editor-tabs.js.map +1 -1
- package/dist/components/empty-state.d.ts +5 -5
- package/dist/components/empty-state.js +16 -11
- package/dist/components/empty-state.js.map +1 -1
- package/dist/components/error-summary.d.ts +21 -0
- package/dist/components/error-summary.js +62 -0
- package/dist/components/error-summary.js.map +1 -0
- package/dist/components/field.d.ts +66 -7
- package/dist/components/field.js +151 -20
- package/dist/components/field.js.map +1 -1
- package/dist/components/form-actions.d.ts +18 -0
- package/dist/components/form-actions.js +30 -0
- package/dist/components/form-actions.js.map +1 -0
- package/dist/components/icon-button.d.ts +3 -1
- package/dist/components/icon-button.js +5 -3
- package/dist/components/icon-button.js.map +1 -1
- package/dist/components/input.d.ts +42 -7
- package/dist/components/input.js +53 -10
- package/dist/components/input.js.map +1 -1
- package/dist/components/kbd.d.ts +2 -2
- package/dist/components/kbd.js +7 -5
- package/dist/components/kbd.js.map +1 -1
- package/dist/components/label.d.ts +6 -1
- package/dist/components/label.js +10 -4
- package/dist/components/label.js.map +1 -1
- package/dist/components/markdown.d.ts +4 -3
- package/dist/components/markdown.js +7 -6
- package/dist/components/markdown.js.map +1 -1
- package/dist/components/menubar.d.ts +16 -14
- package/dist/components/menubar.js +39 -27
- package/dist/components/menubar.js.map +1 -1
- package/dist/components/message-actions.d.ts +3 -3
- package/dist/components/message-actions.js +15 -12
- package/dist/components/message-actions.js.map +1 -1
- package/dist/components/message.d.ts +1 -1
- package/dist/components/message.js +5 -3
- package/dist/components/message.js.map +1 -1
- package/dist/components/mobile-nav.d.ts +1 -1
- package/dist/components/mobile-nav.js +6 -5
- package/dist/components/mobile-nav.js.map +1 -1
- package/dist/components/native-select.d.ts +16 -0
- package/dist/components/native-select.js +33 -0
- package/dist/components/native-select.js.map +1 -0
- package/dist/components/navigation-menu.d.ts +12 -11
- package/dist/components/navigation-menu.js +32 -23
- package/dist/components/navigation-menu.js.map +1 -1
- package/dist/components/number-input.d.ts +23 -0
- package/dist/components/number-input.js +45 -0
- package/dist/components/number-input.js.map +1 -0
- package/dist/components/onboarding-checklist.d.ts +1 -1
- package/dist/components/onboarding-checklist.js +4 -3
- package/dist/components/onboarding-checklist.js.map +1 -1
- package/dist/components/page-header.d.ts +7 -6
- package/dist/components/page-header.js +22 -15
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/pagination.d.ts +1 -1
- package/dist/components/pagination.js +3 -2
- package/dist/components/pagination.js.map +1 -1
- package/dist/components/popover.d.ts +13 -10
- package/dist/components/popover.js +23 -17
- package/dist/components/popover.js.map +1 -1
- package/dist/components/progress.d.ts +1 -1
- package/dist/components/progress.js +6 -5
- package/dist/components/progress.js.map +1 -1
- package/dist/components/radio-group.d.ts +22 -3
- package/dist/components/radio-group.js +51 -10
- package/dist/components/radio-group.js.map +1 -1
- package/dist/components/reasoning.d.ts +3 -3
- package/dist/components/reasoning.js +9 -7
- package/dist/components/reasoning.js.map +1 -1
- package/dist/components/scroll-area.d.ts +3 -2
- package/dist/components/scroll-area.js +10 -7
- package/dist/components/scroll-area.js.map +1 -1
- package/dist/components/secret-input.d.ts +3 -2
- package/dist/components/secret-input.js +6 -4
- package/dist/components/secret-input.js.map +1 -1
- package/dist/components/select.d.ts +11 -6
- package/dist/components/select.js +24 -18
- package/dist/components/select.js.map +1 -1
- package/dist/components/separator.d.ts +1 -1
- package/dist/components/separator.js +4 -3
- package/dist/components/separator.js.map +1 -1
- package/dist/components/sheet.d.ts +10 -8
- package/dist/components/sheet.js +31 -20
- package/dist/components/sheet.js.map +1 -1
- package/dist/components/skeleton.d.ts +2 -1
- package/dist/components/skeleton.js +6 -4
- package/dist/components/skeleton.js.map +1 -1
- package/dist/components/slider.d.ts +10 -6
- package/dist/components/slider.js +15 -7
- package/dist/components/slider.js.map +1 -1
- package/dist/components/source.d.ts +2 -2
- package/dist/components/source.js +9 -7
- package/dist/components/source.js.map +1 -1
- package/dist/components/spinner.d.ts +4 -2
- package/dist/components/spinner.js +4 -3
- package/dist/components/spinner.js.map +1 -1
- package/dist/components/stat-card.d.ts +27 -2
- package/dist/components/stat-card.js +69 -9
- package/dist/components/stat-card.js.map +1 -1
- package/dist/components/status.d.ts +7 -6
- package/dist/components/status.js +11 -5
- package/dist/components/status.js.map +1 -1
- package/dist/components/steps.d.ts +1 -1
- package/dist/components/steps.js +4 -3
- package/dist/components/steps.js.map +1 -1
- package/dist/components/switch.d.ts +46 -2
- package/dist/components/switch.js +85 -8
- package/dist/components/switch.js.map +1 -1
- package/dist/components/table.d.ts +29 -12
- package/dist/components/table.js +79 -34
- package/dist/components/table.js.map +1 -1
- package/dist/components/tabs.d.ts +9 -4
- package/dist/components/tabs.js +22 -13
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/textarea.d.ts +6 -1
- package/dist/components/textarea.js +3 -3
- package/dist/components/textarea.js.map +1 -1
- package/dist/components/toast.d.ts +10 -6
- package/dist/components/toast.js +27 -18
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toaster.d.ts +4 -2
- package/dist/components/toaster.js +6 -2
- package/dist/components/toaster.js.map +1 -1
- package/dist/components/tool-call.d.ts +3 -3
- package/dist/components/tool-call.js +16 -12
- package/dist/components/tool-call.js.map +1 -1
- package/dist/components/toolbar.d.ts +9 -4
- package/dist/components/toolbar.js +14 -9
- package/dist/components/toolbar.js.map +1 -1
- package/dist/components/tooltip.d.ts +3 -2
- package/dist/components/tooltip.js +7 -4
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/tour.js +2 -2
- package/dist/components/tour.js.map +1 -1
- package/dist/components/tree-view.d.ts +1 -1
- package/dist/components/tree-view.js +9 -5
- package/dist/components/tree-view.js.map +1 -1
- package/dist/components/voice-recorder.d.ts +1 -1
- package/dist/components/voice-recorder.js +4 -2
- package/dist/components/voice-recorder.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +19 -12
- package/dist/lib/media.d.ts +18 -0
- package/dist/lib/media.js +34 -0
- package/dist/lib/media.js.map +1 -0
- package/dist/lib/utils.d.ts +32 -0
- package/dist/lib/utils.js +33 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/mui/index.js +20 -8
- package/dist/mui/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/theme.css +36 -17
- package/dist/tokens.d.ts +78 -36
- package/dist/tokens.js +63 -32
- package/dist/tokens.js.map +1 -1
- package/package.json +5 -3
|
@@ -38,5 +38,5 @@ export interface ChatLayoutProps extends Omit<React.ComponentProps<'div'>, 'titl
|
|
|
38
38
|
* details column, and a Full screen mode. Put a Conversation and a Composer
|
|
39
39
|
* in `children`; the layout stacks them and keeps the composer at the bottom.
|
|
40
40
|
*/
|
|
41
|
-
declare
|
|
41
|
+
declare const ChatLayout: React.ForwardRefExoticComponent<Omit<ChatLayoutProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
42
42
|
export { ChatLayout };
|
|
@@ -89,7 +89,7 @@ function useDesktop(query = "(min-width: 768px)") {
|
|
|
89
89
|
* details column, and a Full screen mode. Put a Conversation and a Composer
|
|
90
90
|
* in `children`; the layout stacks them and keeps the composer at the bottom.
|
|
91
91
|
*/
|
|
92
|
-
function ChatLayout({ sidebar, sidebarLabel = "Chats", details, detailsLabel = "Details", title, actions, sidebarOpen: controlledOpen, defaultSidebarOpen = true, onSidebarOpenChange, allowFullscreen = true, onFullscreenChange, sidebarSize = "24%", children, className, ...props }) {
|
|
92
|
+
var ChatLayout = React.forwardRef(function ChatLayout({ sidebar, sidebarLabel = "Chats", details, detailsLabel = "Details", title, actions, sidebarOpen: controlledOpen, defaultSidebarOpen = true, onSidebarOpenChange, allowFullscreen = true, onFullscreenChange, sidebarSize = "24%", children, className, ...props }, ref) {
|
|
93
93
|
const root = React.useRef(null);
|
|
94
94
|
const desktop = useDesktop();
|
|
95
95
|
const [ownOpen, setOwnOpen] = React.useState(defaultSidebarOpen);
|
|
@@ -122,7 +122,11 @@ function ChatLayout({ sidebar, sidebarLabel = "Chats", details, detailsLabel = "
|
|
|
122
122
|
children
|
|
123
123
|
});
|
|
124
124
|
return /* @__PURE__ */ jsxs("div", {
|
|
125
|
-
ref:
|
|
125
|
+
ref: (node) => {
|
|
126
|
+
root.current = node;
|
|
127
|
+
if (typeof ref === "function") ref(node);
|
|
128
|
+
else if (ref) ref.current = node;
|
|
129
|
+
},
|
|
126
130
|
"data-slot": "chat-layout",
|
|
127
131
|
"data-fullscreen": fullscreen || void 0,
|
|
128
132
|
className: cn("flex h-full min-h-0 flex-col overflow-hidden bg-background text-foreground", "data-[fullscreen]:fixed data-[fullscreen]:inset-0 data-[fullscreen]:z-50 data-[fullscreen]:h-dvh data-[fullscreen]:w-screen", className),
|
|
@@ -211,7 +215,7 @@ function ChatLayout({ sidebar, sidebarLabel = "Chats", details, detailsLabel = "
|
|
|
211
215
|
})
|
|
212
216
|
]
|
|
213
217
|
});
|
|
214
|
-
}
|
|
218
|
+
});
|
|
215
219
|
//#endregion
|
|
216
220
|
export { ChatLayout, useFullscreen };
|
|
217
221
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-layout.js","names":["_Fragment"],"sources":["../../src/components/chat-layout.tsx"],"sourcesContent":["import Maximize from '@burtson-labs/icons/react/maximize';\nimport Minimize from '@burtson-labs/icons/react/minimize';\nimport PanelLeft from '@burtson-labs/icons/react/panel-left';\nimport PanelLeftClose from '@burtson-labs/icons/react/panel-left-close';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport { IconButton } from './icon-button';\nimport { ResizablePanel, ResizablePanelGroup, ResizeHandle } from './resizable';\nimport { Sheet, SheetContent, SheetDescription, SheetTitle } from './sheet';\n\n/**\n * Full screen for one element: the Fullscreen API where the browser allows\n * it, otherwise the element is pinned over the page with CSS. Escape leaves\n * either way. Nothing remounts, so focus and scroll stay where they were.\n */\nexport function useFullscreen(\n ref: React.RefObject<HTMLElement | null>,\n onChange?: (fullscreen: boolean) => void,\n) {\n const [native, setNative] = React.useState(false);\n const [fallback, setFallback] = React.useState(false);\n const active = native || fallback;\n const changed = React.useRef(onChange);\n React.useEffect(() => {\n changed.current = onChange;\n }, [onChange]);\n\n React.useEffect(() => {\n const sync = () =>\n setNative(Boolean(ref.current && document.fullscreenElement === ref.current));\n document.addEventListener('fullscreenchange', sync);\n return () => document.removeEventListener('fullscreenchange', sync);\n }, [ref]);\n\n React.useEffect(() => {\n if (!fallback) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && !e.defaultPrevented) setFallback(false);\n };\n const root = document.documentElement;\n const previous = root.style.overflow;\n root.style.overflow = 'hidden';\n document.addEventListener('keydown', onKey);\n return () => {\n root.style.overflow = previous;\n document.removeEventListener('keydown', onKey);\n };\n }, [fallback]);\n\n const first = React.useRef(true);\n React.useEffect(() => {\n if (first.current) {\n first.current = false;\n return;\n }\n changed.current?.(active);\n }, [active]);\n\n const enter = React.useCallback(async () => {\n const el = ref.current;\n if (!el) return;\n const canNative = document.fullscreenEnabled && typeof el.requestFullscreen === 'function';\n // Denied (iframe without allowfullscreen, iOS) resolves false: use the CSS fallback.\n const ok = canNative\n ? await el.requestFullscreen().then(\n () => true,\n () => false,\n )\n : false;\n if (!ok) setFallback(true);\n }, [ref]);\n\n const exit = React.useCallback(async () => {\n setFallback(false);\n if (document.fullscreenElement && typeof document.exitFullscreen === 'function') {\n await document.exitFullscreen().catch(() => undefined);\n }\n }, []);\n\n const toggle = React.useCallback(() => (active ? exit() : enter()), [active, enter, exit]);\n return { fullscreen: active, native, enter, exit, toggle };\n}\n\nfunction useDesktop(query = '(min-width: 768px)') {\n const get = () => (typeof matchMedia === 'function' ? matchMedia(query).matches : true);\n const [desktop, setDesktop] = React.useState(get);\n React.useEffect(() => {\n if (typeof matchMedia !== 'function') return;\n const mq = matchMedia(query);\n const on = () => setDesktop(mq.matches);\n mq.addEventListener?.('change', on);\n return () => mq.removeEventListener?.('change', on);\n }, [query]);\n return desktop;\n}\n\nexport interface ChatLayoutProps extends Omit<React.ComponentProps<'div'>, 'title'> {\n /** The history column, usually a ChatHistory. A sheet from the left on phones. */\n sidebar?: React.ReactNode;\n sidebarLabel?: string;\n /** An optional right column: sources, files, run details. Hidden on phones. */\n details?: React.ReactNode;\n detailsLabel?: string;\n /** Shown in the header: the conversation title. */\n title?: React.ReactNode;\n /** Header controls right of the title (model picker, share). */\n actions?: React.ReactNode;\n /** Controlled sidebar state; leave out to let the layout keep its own. */\n sidebarOpen?: boolean;\n defaultSidebarOpen?: boolean;\n onSidebarOpenChange?: (open: boolean) => void;\n /** Show the Full screen button. Default true. */\n allowFullscreen?: boolean;\n onFullscreenChange?: (fullscreen: boolean) => void;\n /** Sidebar width, e.g. \"22%\" or 280 (px), as ResizablePanel sizes. */\n sidebarSize?: number | string;\n}\n\n/**\n * The frame for a chat app: header, a resizable history sidebar (a sheet on\n * phones that closes when a chat is picked), the conversation, an optional\n * details column, and a Full screen mode. Put a Conversation and a Composer\n * in `children`; the layout stacks them and keeps the composer at the bottom.\n */\nfunction ChatLayout({\n sidebar,\n sidebarLabel = 'Chats',\n details,\n detailsLabel = 'Details',\n title,\n actions,\n sidebarOpen: controlledOpen,\n defaultSidebarOpen = true,\n onSidebarOpenChange,\n allowFullscreen = true,\n onFullscreenChange,\n sidebarSize = '24%',\n children,\n className,\n ...props\n}: ChatLayoutProps) {\n const root = React.useRef<HTMLDivElement>(null);\n const desktop = useDesktop();\n const [ownOpen, setOwnOpen] = React.useState(defaultSidebarOpen);\n const [sheetOpen, setSheetOpen] = React.useState(false);\n const open = controlledOpen ?? ownOpen;\n const setOpen = (v: boolean) => {\n if (controlledOpen === undefined) setOwnOpen(v);\n onSidebarOpenChange?.(v);\n };\n const { fullscreen, toggle } = useFullscreen(root, onFullscreenChange);\n\n const sidebarToggle = sidebar ? (\n desktop ? (\n <IconButton\n variant=\"ghost\"\n size=\"icon-sm\"\n label={open ? `Hide ${sidebarLabel.toLowerCase()}` : `Show ${sidebarLabel.toLowerCase()}`}\n aria-expanded={open}\n onClick={() => setOpen(!open)}\n >\n {open ? <PanelLeftClose /> : <PanelLeft />}\n </IconButton>\n ) : (\n <IconButton\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"pointer-coarse:size-11\"\n label={`Open ${sidebarLabel.toLowerCase()}`}\n aria-expanded={sheetOpen}\n onClick={() => setSheetOpen(true)}\n >\n <PanelLeft />\n </IconButton>\n )\n ) : null;\n\n const main = (\n <div data-slot=\"chat-layout-main\" className=\"flex h-full min-h-0 min-w-0 flex-col\">\n {children}\n </div>\n );\n\n return (\n <div\n ref={root}\n data-slot=\"chat-layout\"\n data-fullscreen={fullscreen || undefined}\n className={cn(\n 'flex h-full min-h-0 flex-col overflow-hidden bg-background text-foreground',\n 'data-[fullscreen]:fixed data-[fullscreen]:inset-0 data-[fullscreen]:z-50 data-[fullscreen]:h-dvh data-[fullscreen]:w-screen',\n className,\n )}\n {...props}\n >\n <header className=\"flex h-12 shrink-0 items-center gap-1 border-b px-2\">\n {sidebarToggle}\n <div className=\"min-w-0 flex-1 truncate px-1 text-sm font-medium\">{title}</div>\n {actions}\n {allowFullscreen && (\n <IconButton\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"pointer-coarse:size-11\"\n label={fullscreen ? 'Exit full screen' : 'Full screen'}\n aria-pressed={fullscreen}\n onClick={() => void toggle()}\n >\n {fullscreen ? <Minimize /> : <Maximize />}\n </IconButton>\n )}\n </header>\n <div className=\"min-h-0 flex-1\">\n {desktop && (sidebar || details) ? (\n <ResizablePanelGroup orientation=\"horizontal\">\n {sidebar && open && (\n <>\n <ResizablePanel\n id=\"chat-sidebar\"\n defaultSize={sidebarSize}\n minSize={180}\n maxSize=\"45%\"\n className=\"bg-surface-muted/50\"\n >\n <aside aria-label={sidebarLabel} className=\"h-full\">\n {sidebar}\n </aside>\n </ResizablePanel>\n <ResizeHandle aria-label={`Resize ${sidebarLabel.toLowerCase()}`} />\n </>\n )}\n <ResizablePanel id=\"chat-main\" minSize=\"30%\">\n {main}\n </ResizablePanel>\n {details && (\n <>\n <ResizeHandle aria-label={`Resize ${detailsLabel.toLowerCase()}`} />\n <ResizablePanel id=\"chat-details\" defaultSize=\"26%\" minSize={200} maxSize=\"45%\">\n <aside aria-label={detailsLabel} className=\"h-full overflow-y-auto\">\n {details}\n </aside>\n </ResizablePanel>\n </>\n )}\n </ResizablePanelGroup>\n ) : (\n main\n )}\n </div>\n {sidebar && !desktop && (\n <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>\n <SheetContent side=\"left\" className=\"w-[min(20rem,88vw)] gap-0 p-0\">\n <SheetTitle className=\"border-b px-4 py-3.5 text-sm\">{sidebarLabel}</SheetTitle>\n <SheetDescription className=\"sr-only\">Pick a conversation.</SheetDescription>\n <div\n className=\"min-h-0 flex-1\"\n // Picking a chat (or New chat) closes the sheet, as on a phone it should.\n onClickCapture={(e) => {\n const t = e.target as HTMLElement;\n if (t.closest('[data-slot=\"chat-history-row\"], [data-close-sheet]'))\n setSheetOpen(false);\n }}\n >\n {sidebar}\n </div>\n </SheetContent>\n </Sheet>\n )}\n </div>\n );\n}\n\nexport { ChatLayout };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chat-layout.js","names":["_Fragment"],"sources":["../../src/components/chat-layout.tsx"],"sourcesContent":["import Maximize from '@burtson-labs/icons/react/maximize';\nimport Minimize from '@burtson-labs/icons/react/minimize';\nimport PanelLeft from '@burtson-labs/icons/react/panel-left';\nimport PanelLeftClose from '@burtson-labs/icons/react/panel-left-close';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport { IconButton } from './icon-button';\nimport { ResizablePanel, ResizablePanelGroup, ResizeHandle } from './resizable';\nimport { Sheet, SheetContent, SheetDescription, SheetTitle } from './sheet';\n\n/**\n * Full screen for one element: the Fullscreen API where the browser allows\n * it, otherwise the element is pinned over the page with CSS. Escape leaves\n * either way. Nothing remounts, so focus and scroll stay where they were.\n */\nexport function useFullscreen(\n ref: React.RefObject<HTMLElement | null>,\n onChange?: (fullscreen: boolean) => void,\n) {\n const [native, setNative] = React.useState(false);\n const [fallback, setFallback] = React.useState(false);\n const active = native || fallback;\n const changed = React.useRef(onChange);\n React.useEffect(() => {\n changed.current = onChange;\n }, [onChange]);\n\n React.useEffect(() => {\n const sync = () =>\n setNative(Boolean(ref.current && document.fullscreenElement === ref.current));\n document.addEventListener('fullscreenchange', sync);\n return () => document.removeEventListener('fullscreenchange', sync);\n }, [ref]);\n\n React.useEffect(() => {\n if (!fallback) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && !e.defaultPrevented) setFallback(false);\n };\n const root = document.documentElement;\n const previous = root.style.overflow;\n root.style.overflow = 'hidden';\n document.addEventListener('keydown', onKey);\n return () => {\n root.style.overflow = previous;\n document.removeEventListener('keydown', onKey);\n };\n }, [fallback]);\n\n const first = React.useRef(true);\n React.useEffect(() => {\n if (first.current) {\n first.current = false;\n return;\n }\n changed.current?.(active);\n }, [active]);\n\n const enter = React.useCallback(async () => {\n const el = ref.current;\n if (!el) return;\n const canNative = document.fullscreenEnabled && typeof el.requestFullscreen === 'function';\n // Denied (iframe without allowfullscreen, iOS) resolves false: use the CSS fallback.\n const ok = canNative\n ? await el.requestFullscreen().then(\n () => true,\n () => false,\n )\n : false;\n if (!ok) setFallback(true);\n }, [ref]);\n\n const exit = React.useCallback(async () => {\n setFallback(false);\n if (document.fullscreenElement && typeof document.exitFullscreen === 'function') {\n await document.exitFullscreen().catch(() => undefined);\n }\n }, []);\n\n const toggle = React.useCallback(() => (active ? exit() : enter()), [active, enter, exit]);\n return { fullscreen: active, native, enter, exit, toggle };\n}\n\nfunction useDesktop(query = '(min-width: 768px)') {\n const get = () => (typeof matchMedia === 'function' ? matchMedia(query).matches : true);\n const [desktop, setDesktop] = React.useState(get);\n React.useEffect(() => {\n if (typeof matchMedia !== 'function') return;\n const mq = matchMedia(query);\n const on = () => setDesktop(mq.matches);\n mq.addEventListener?.('change', on);\n return () => mq.removeEventListener?.('change', on);\n }, [query]);\n return desktop;\n}\n\nexport interface ChatLayoutProps extends Omit<React.ComponentProps<'div'>, 'title'> {\n /** The history column, usually a ChatHistory. A sheet from the left on phones. */\n sidebar?: React.ReactNode;\n sidebarLabel?: string;\n /** An optional right column: sources, files, run details. Hidden on phones. */\n details?: React.ReactNode;\n detailsLabel?: string;\n /** Shown in the header: the conversation title. */\n title?: React.ReactNode;\n /** Header controls right of the title (model picker, share). */\n actions?: React.ReactNode;\n /** Controlled sidebar state; leave out to let the layout keep its own. */\n sidebarOpen?: boolean;\n defaultSidebarOpen?: boolean;\n onSidebarOpenChange?: (open: boolean) => void;\n /** Show the Full screen button. Default true. */\n allowFullscreen?: boolean;\n onFullscreenChange?: (fullscreen: boolean) => void;\n /** Sidebar width, e.g. \"22%\" or 280 (px), as ResizablePanel sizes. */\n sidebarSize?: number | string;\n}\n\n/**\n * The frame for a chat app: header, a resizable history sidebar (a sheet on\n * phones that closes when a chat is picked), the conversation, an optional\n * details column, and a Full screen mode. Put a Conversation and a Composer\n * in `children`; the layout stacks them and keeps the composer at the bottom.\n */\nconst ChatLayout = React.forwardRef<HTMLDivElement, ChatLayoutProps>(function ChatLayout(\n {\n sidebar,\n sidebarLabel = 'Chats',\n details,\n detailsLabel = 'Details',\n title,\n actions,\n sidebarOpen: controlledOpen,\n defaultSidebarOpen = true,\n onSidebarOpenChange,\n allowFullscreen = true,\n onFullscreenChange,\n sidebarSize = '24%',\n children,\n className,\n ...props\n },\n ref,\n) {\n const root = React.useRef<HTMLDivElement>(null);\n const desktop = useDesktop();\n const [ownOpen, setOwnOpen] = React.useState(defaultSidebarOpen);\n const [sheetOpen, setSheetOpen] = React.useState(false);\n const open = controlledOpen ?? ownOpen;\n const setOpen = (v: boolean) => {\n if (controlledOpen === undefined) setOwnOpen(v);\n onSidebarOpenChange?.(v);\n };\n const { fullscreen, toggle } = useFullscreen(root, onFullscreenChange);\n\n const sidebarToggle = sidebar ? (\n desktop ? (\n <IconButton\n variant=\"ghost\"\n size=\"icon-sm\"\n label={open ? `Hide ${sidebarLabel.toLowerCase()}` : `Show ${sidebarLabel.toLowerCase()}`}\n aria-expanded={open}\n onClick={() => setOpen(!open)}\n >\n {open ? <PanelLeftClose /> : <PanelLeft />}\n </IconButton>\n ) : (\n <IconButton\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"pointer-coarse:size-11\"\n label={`Open ${sidebarLabel.toLowerCase()}`}\n aria-expanded={sheetOpen}\n onClick={() => setSheetOpen(true)}\n >\n <PanelLeft />\n </IconButton>\n )\n ) : null;\n\n const main = (\n <div data-slot=\"chat-layout-main\" className=\"flex h-full min-h-0 min-w-0 flex-col\">\n {children}\n </div>\n );\n\n return (\n <div\n ref={(node) => {\n root.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) ref.current = node;\n }}\n data-slot=\"chat-layout\"\n data-fullscreen={fullscreen || undefined}\n className={cn(\n 'flex h-full min-h-0 flex-col overflow-hidden bg-background text-foreground',\n 'data-[fullscreen]:fixed data-[fullscreen]:inset-0 data-[fullscreen]:z-50 data-[fullscreen]:h-dvh data-[fullscreen]:w-screen',\n className,\n )}\n {...props}\n >\n <header className=\"flex h-12 shrink-0 items-center gap-1 border-b px-2\">\n {sidebarToggle}\n <div className=\"min-w-0 flex-1 truncate px-1 text-sm font-medium\">{title}</div>\n {actions}\n {allowFullscreen && (\n <IconButton\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"pointer-coarse:size-11\"\n label={fullscreen ? 'Exit full screen' : 'Full screen'}\n aria-pressed={fullscreen}\n onClick={() => void toggle()}\n >\n {fullscreen ? <Minimize /> : <Maximize />}\n </IconButton>\n )}\n </header>\n <div className=\"min-h-0 flex-1\">\n {desktop && (sidebar || details) ? (\n <ResizablePanelGroup orientation=\"horizontal\">\n {sidebar && open && (\n <>\n <ResizablePanel\n id=\"chat-sidebar\"\n defaultSize={sidebarSize}\n minSize={180}\n maxSize=\"45%\"\n className=\"bg-surface-muted/50\"\n >\n <aside aria-label={sidebarLabel} className=\"h-full\">\n {sidebar}\n </aside>\n </ResizablePanel>\n <ResizeHandle aria-label={`Resize ${sidebarLabel.toLowerCase()}`} />\n </>\n )}\n <ResizablePanel id=\"chat-main\" minSize=\"30%\">\n {main}\n </ResizablePanel>\n {details && (\n <>\n <ResizeHandle aria-label={`Resize ${detailsLabel.toLowerCase()}`} />\n <ResizablePanel id=\"chat-details\" defaultSize=\"26%\" minSize={200} maxSize=\"45%\">\n <aside aria-label={detailsLabel} className=\"h-full overflow-y-auto\">\n {details}\n </aside>\n </ResizablePanel>\n </>\n )}\n </ResizablePanelGroup>\n ) : (\n main\n )}\n </div>\n {sidebar && !desktop && (\n <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>\n <SheetContent side=\"left\" className=\"w-[min(20rem,88vw)] gap-0 p-0\">\n <SheetTitle className=\"border-b px-4 py-3.5 text-sm\">{sidebarLabel}</SheetTitle>\n <SheetDescription className=\"sr-only\">Pick a conversation.</SheetDescription>\n <div\n className=\"min-h-0 flex-1\"\n // Picking a chat (or New chat) closes the sheet, as on a phone it should.\n onClickCapture={(e) => {\n const t = e.target as HTMLElement;\n if (t.closest('[data-slot=\"chat-history-row\"], [data-close-sheet]'))\n setSheetOpen(false);\n }}\n >\n {sidebar}\n </div>\n </SheetContent>\n </Sheet>\n )}\n </div>\n );\n});\n\nexport { ChatLayout };\n"],"mappings":"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"}
|
|
@@ -13,7 +13,7 @@ export interface CheckboxCardProps extends Omit<React.ComponentProps<typeof Chec
|
|
|
13
13
|
* area (44px or taller). For choices that need explaining: roles,
|
|
14
14
|
* permissions, notification types. One focus ring, on the card.
|
|
15
15
|
*/
|
|
16
|
-
declare
|
|
16
|
+
declare const CheckboxCard: React.ForwardRefExoticComponent<Omit<CheckboxCardProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
17
17
|
export interface CheckboxCardGroupProps extends React.ComponentProps<'fieldset'> {
|
|
18
18
|
/** The group's name, shown as its legend. */
|
|
19
19
|
label: React.ReactNode;
|
|
@@ -21,5 +21,5 @@ export interface CheckboxCardGroupProps extends React.ComponentProps<'fieldset'>
|
|
|
21
21
|
description?: React.ReactNode;
|
|
22
22
|
}
|
|
23
23
|
/** A fieldset of CheckboxCards with a visible legend. `disabled` disables every card. */
|
|
24
|
-
declare
|
|
24
|
+
declare const CheckboxCardGroup: React.ForwardRefExoticComponent<Omit<CheckboxCardGroupProps, "ref"> & React.RefAttributes<HTMLFieldSetElement>>;
|
|
25
25
|
export { CheckboxCard, CheckboxCardGroup };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { cn, noOutlineClasses } from "../lib/utils.js";
|
|
3
3
|
import { Checkbox } from "./checkbox.js";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -9,7 +9,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
9
9
|
* area (44px or taller). For choices that need explaining: roles,
|
|
10
10
|
* permissions, notification types. One focus ring, on the card.
|
|
11
11
|
*/
|
|
12
|
-
function CheckboxCard({ title, description, id: idProp, disabled, className, cardClassName, ...props }) {
|
|
12
|
+
var CheckboxCard = React.forwardRef(function CheckboxCard({ title, description, id: idProp, disabled, className, cardClassName, ...props }, ref) {
|
|
13
13
|
const auto = React.useId();
|
|
14
14
|
const id = idProp ?? auto;
|
|
15
15
|
return /* @__PURE__ */ jsxs("label", {
|
|
@@ -17,11 +17,12 @@ function CheckboxCard({ title, description, id: idProp, disabled, className, car
|
|
|
17
17
|
"data-slot": "checkbox-card",
|
|
18
18
|
className: cn("flex min-h-11 cursor-pointer items-start gap-3 rounded-md border border-border bg-surface px-3 py-2.5 transition-colors hover:border-border-strong", "has-[[data-state=checked]]:border-brand/40 has-[[data-state=checked]]:bg-brand-soft/50", "has-[:focus-visible]:border-ring has-[:focus-visible]:inset-ring-1 has-[:focus-visible]:inset-ring-ring", "has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-60 has-[:disabled]:hover:border-border", cardClassName),
|
|
19
19
|
children: [/* @__PURE__ */ jsx(Checkbox, {
|
|
20
|
+
ref,
|
|
20
21
|
id,
|
|
21
22
|
disabled,
|
|
22
23
|
"aria-labelledby": `${id}-title`,
|
|
23
24
|
"aria-describedby": description ? `${id}-description` : void 0,
|
|
24
|
-
className: cn("mt-0.5
|
|
25
|
+
className: cn("mt-0.5 focus-visible:outline-none", noOutlineClasses, className),
|
|
25
26
|
...props
|
|
26
27
|
}), /* @__PURE__ */ jsxs("span", {
|
|
27
28
|
className: "grid min-w-0 gap-0.5",
|
|
@@ -36,18 +37,19 @@ function CheckboxCard({ title, description, id: idProp, disabled, className, car
|
|
|
36
37
|
})]
|
|
37
38
|
})]
|
|
38
39
|
});
|
|
39
|
-
}
|
|
40
|
+
});
|
|
40
41
|
/** A fieldset of CheckboxCards with a visible legend. `disabled` disables every card. */
|
|
41
|
-
function CheckboxCardGroup({ label, description, className, children, ...props }) {
|
|
42
|
+
var CheckboxCardGroup = React.forwardRef(function CheckboxCardGroup({ label, description, className, children, ...props }, ref) {
|
|
42
43
|
const id = React.useId();
|
|
43
44
|
return /* @__PURE__ */ jsxs("fieldset", {
|
|
45
|
+
ref,
|
|
44
46
|
"data-slot": "checkbox-card-group",
|
|
45
47
|
"aria-describedby": description ? `${id}-description` : void 0,
|
|
46
48
|
className: cn("grid min-w-0 gap-2", className),
|
|
47
49
|
...props,
|
|
48
50
|
children: [
|
|
49
51
|
/* @__PURE__ */ jsx("legend", {
|
|
50
|
-
className: "mb-1.5 text-sm font-
|
|
52
|
+
className: "mb-1.5 text-sm font-semibold",
|
|
51
53
|
children: label
|
|
52
54
|
}),
|
|
53
55
|
description && /* @__PURE__ */ jsx("p", {
|
|
@@ -58,7 +60,7 @@ function CheckboxCardGroup({ label, description, className, children, ...props }
|
|
|
58
60
|
children
|
|
59
61
|
]
|
|
60
62
|
});
|
|
61
|
-
}
|
|
63
|
+
});
|
|
62
64
|
//#endregion
|
|
63
65
|
export { CheckboxCard, CheckboxCardGroup };
|
|
64
66
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkbox-card.js","names":[],"sources":["../../src/components/checkbox-card.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as CheckboxPrimitive from '../primitives/vendor/radix/react-checkbox';\n\nimport { Checkbox } from './checkbox';\n\nexport interface CheckboxCardProps extends Omit<\n React.ComponentProps<typeof CheckboxPrimitive.Root>,\n 'title' | 'children'\n> {\n /** The option's name. */\n title: React.ReactNode;\n /** A sentence under the name: what choosing it means. */\n description?: React.ReactNode;\n /** Extra classes for the card (the label); className goes to the checkbox. */\n cardClassName?: string;\n}\n\n/**\n * A checkbox with a title and a sentence, where the whole card is the hit\n * area (44px or taller). For choices that need explaining: roles,\n * permissions, notification types. One focus ring, on the card.\n */\
|
|
1
|
+
{"version":3,"file":"checkbox-card.js","names":[],"sources":["../../src/components/checkbox-card.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn, noOutlineClasses } from '../lib/utils';\nimport * as CheckboxPrimitive from '../primitives/vendor/radix/react-checkbox';\n\nimport { Checkbox } from './checkbox';\n\nexport interface CheckboxCardProps extends Omit<\n React.ComponentProps<typeof CheckboxPrimitive.Root>,\n 'title' | 'children'\n> {\n /** The option's name. */\n title: React.ReactNode;\n /** A sentence under the name: what choosing it means. */\n description?: React.ReactNode;\n /** Extra classes for the card (the label); className goes to the checkbox. */\n cardClassName?: string;\n}\n\n/**\n * A checkbox with a title and a sentence, where the whole card is the hit\n * area (44px or taller). For choices that need explaining: roles,\n * permissions, notification types. One focus ring, on the card.\n */\nconst CheckboxCard = React.forwardRef<HTMLButtonElement, CheckboxCardProps>(function CheckboxCard(\n { title, description, id: idProp, disabled, className, cardClassName, ...props },\n ref,\n) {\n const auto = React.useId();\n const id = idProp ?? auto;\n return (\n <label\n htmlFor={id}\n data-slot=\"checkbox-card\"\n className={cn(\n 'flex min-h-11 cursor-pointer items-start gap-3 rounded-md border border-border bg-surface px-3 py-2.5 transition-colors hover:border-border-strong',\n 'has-[[data-state=checked]]:border-brand/40 has-[[data-state=checked]]:bg-brand-soft/50',\n 'has-[:focus-visible]:border-ring has-[:focus-visible]:inset-ring-1 has-[:focus-visible]:inset-ring-ring',\n // :disabled also catches a disabled fieldset around the card.\n 'has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-60 has-[:disabled]:hover:border-border',\n cardClassName,\n )}\n >\n <Checkbox\n ref={ref}\n id={id}\n disabled={disabled}\n aria-labelledby={`${id}-title`}\n aria-describedby={description ? `${id}-description` : undefined}\n // The card draws the focus ring; the box keeps a system outline in forced colors.\n className={cn('mt-0.5 focus-visible:outline-none', noOutlineClasses, className)}\n {...props}\n />\n <span className=\"grid min-w-0 gap-0.5\">\n <span id={`${id}-title`} className=\"text-sm leading-5 font-medium\">\n {title}\n </span>\n {description && (\n <span id={`${id}-description`} className=\"text-[13px] leading-5 text-muted-foreground\">\n {description}\n </span>\n )}\n </span>\n </label>\n );\n});\n\nexport interface CheckboxCardGroupProps extends React.ComponentProps<'fieldset'> {\n /** The group's name, shown as its legend. */\n label: React.ReactNode;\n /** Help under the legend. */\n description?: React.ReactNode;\n}\n\n/** A fieldset of CheckboxCards with a visible legend. `disabled` disables every card. */\nconst CheckboxCardGroup = React.forwardRef<HTMLFieldSetElement, CheckboxCardGroupProps>(\n function CheckboxCardGroup({ label, description, className, children, ...props }, ref) {\n const id = React.useId();\n return (\n <fieldset\n ref={ref}\n data-slot=\"checkbox-card-group\"\n aria-describedby={description ? `${id}-description` : undefined}\n className={cn('grid min-w-0 gap-2', className)}\n {...props}\n >\n <legend className=\"mb-1.5 text-sm font-semibold\">{label}</legend>\n {description && (\n <p id={`${id}-description`} className=\"-mt-1.5 text-[13px] text-muted-foreground\">\n {description}\n </p>\n )}\n {children}\n </fieldset>\n );\n },\n);\n\nexport { CheckboxCard, CheckboxCardGroup };\n"],"mappings":";;;;;;;;;;;AAwBA,IAAM,eAAe,MAAM,WAAiD,SAAS,aACnF,EAAE,OAAO,aAAa,IAAI,QAAQ,UAAU,WAAW,eAAe,GAAG,SACzE,KACA;CACA,MAAM,OAAO,MAAM,MAAM;CACzB,MAAM,KAAK,UAAU;CACrB,OACE,qBAAC,SAAD;EACE,SAAS;EACT,aAAU;EACV,WAAW,GACT,sJACA,0FACA,2GAEA,qGACA,aACF;EAVF,UAAA,CAYE,oBAAC,UAAD;GACO;GACD;GACM;GACV,mBAAiB,GAAG,GAAG;GACvB,oBAAkB,cAAc,GAAG,GAAG,gBAAgB,KAAA;GAEtD,WAAW,GAAG,qCAAqC,kBAAkB,SAAS;GAC9E,GAAI;EACL,CAAA,GACD,qBAAC,QAAD;GAAM,WAAU;GAAhB,UAAA,CACE,oBAAC,QAAD;IAAM,IAAI,GAAG,GAAG;IAAS,WAAU;IAChC,UAAA;GACG,CAAA,GACL,eACC,oBAAC,QAAD;IAAM,IAAI,GAAG,GAAG;IAAe,WAAU;IACtC,UAAA;GACG,CAAA,CAEJ;EACD,CAAA,CAAA;;AAEX,CAAC;;AAUD,IAAM,oBAAoB,MAAM,WAC9B,SAAS,kBAAkB,EAAE,OAAO,aAAa,WAAW,UAAU,GAAG,SAAS,KAAK;CACrF,MAAM,KAAK,MAAM,MAAM;CACvB,OACE,qBAAC,YAAD;EACO;EACL,aAAU;EACV,oBAAkB,cAAc,GAAG,GAAG,gBAAgB,KAAA;EACtD,WAAW,GAAG,sBAAsB,SAAS;EAC7C,GAAI;EALN,UAAA;GAOE,oBAAC,UAAD;IAAQ,WAAU;IAAgC,UAAA;GAAc,CAAA;GAC/D,eACC,oBAAC,KAAD;IAAG,IAAI,GAAG,GAAG;IAAe,WAAU;IACnC,UAAA;GACA,CAAA;GAEJ;EACO;;AAEd,CACF"}
|
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import * as CheckboxPrimitive from '../primitives/vendor/radix/react-checkbox';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* A 16px box with a 44px hit area on touch screens. Its unchecked border is
|
|
5
|
+
* the field border tone (`input`, 3:1 on the page in both modes); checked it
|
|
6
|
+
* fills with primary in light mode and with brand (the ring colour, 4.5:1 on
|
|
7
|
+
* the page, where primary reaches 2.8:1) in dark mode. Focus is the shared
|
|
8
|
+
* 2px outline. Wrap it in a Label, or use CheckboxRow or CheckboxCard.
|
|
9
|
+
*/
|
|
10
|
+
declare const Checkbox: React.ForwardRefExoticComponent<Omit<CheckboxPrimitive.CheckboxProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
11
|
+
export interface CheckboxRowProps extends Omit<React.ComponentProps<typeof CheckboxPrimitive.Root>, 'children'> {
|
|
12
|
+
/** The option's name. */
|
|
13
|
+
label: React.ReactNode;
|
|
14
|
+
/** A line under the name. */
|
|
15
|
+
description?: React.ReactNode;
|
|
16
|
+
/** Extra classes for the row (the label); className goes to the checkbox. */
|
|
17
|
+
rowClassName?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A checkbox with its label to the right and an optional line under it, the
|
|
21
|
+
* whole row clickable. For a bordered option with a sentence of explanation
|
|
22
|
+
* use CheckboxCard.
|
|
23
|
+
*/
|
|
24
|
+
declare const CheckboxRow: React.ForwardRefExoticComponent<Omit<CheckboxRowProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
25
|
+
export { Checkbox, CheckboxRow };
|
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { cn, focusRingClasses, touchTargetClasses } from "../lib/utils.js";
|
|
3
3
|
import { Checkbox as Checkbox$1, CheckboxIndicator } from "../primitives/vendor/radix/react-checkbox/checkbox.js";
|
|
4
|
-
import "react";
|
|
4
|
+
import * as React from "react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import Check from "@burtson-labs/icons/react/check";
|
|
7
7
|
import Minus from "@burtson-labs/icons/react/minus";
|
|
8
8
|
//#region src/components/checkbox.tsx
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* A 16px box with a 44px hit area on touch screens. Its unchecked border is
|
|
11
|
+
* the field border tone (`input`, 3:1 on the page in both modes); checked it
|
|
12
|
+
* fills with primary in light mode and with brand (the ring colour, 4.5:1 on
|
|
13
|
+
* the page, where primary reaches 2.8:1) in dark mode. Focus is the shared
|
|
14
|
+
* 2px outline. Wrap it in a Label, or use CheckboxRow or CheckboxCard.
|
|
15
|
+
*/
|
|
16
|
+
var Checkbox = React.forwardRef(function Checkbox({ className, ...props }, ref) {
|
|
10
17
|
return /* @__PURE__ */ jsx(Checkbox$1, {
|
|
18
|
+
ref,
|
|
11
19
|
"data-slot": "checkbox",
|
|
12
|
-
className: cn("peer size-4 shrink-0 rounded-xs border border-
|
|
20
|
+
className: cn("peer size-4 shrink-0 rounded-xs border border-input bg-surface shadow-xs transition-[border-color,box-shadow,background-color] hover:border-brand/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground dark:data-[state=checked]:border-brand dark:data-[state=checked]:bg-brand dark:data-[state=checked]:text-background dark:data-[state=indeterminate]:border-brand dark:data-[state=indeterminate]:bg-brand dark:data-[state=indeterminate]:text-background", focusRingClasses, touchTargetClasses, className),
|
|
13
21
|
...props,
|
|
14
22
|
children: /* @__PURE__ */ jsxs(CheckboxIndicator, {
|
|
15
23
|
"data-slot": "checkbox-indicator",
|
|
@@ -25,8 +33,42 @@ function Checkbox({ className, ...props }) {
|
|
|
25
33
|
})]
|
|
26
34
|
})
|
|
27
35
|
});
|
|
28
|
-
}
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* A checkbox with its label to the right and an optional line under it, the
|
|
39
|
+
* whole row clickable. For a bordered option with a sentence of explanation
|
|
40
|
+
* use CheckboxCard.
|
|
41
|
+
*/
|
|
42
|
+
var CheckboxRow = React.forwardRef(function CheckboxRow({ label, description, id: idProp, className, rowClassName, disabled, ...props }, ref) {
|
|
43
|
+
const auto = React.useId();
|
|
44
|
+
const id = idProp ?? auto;
|
|
45
|
+
return /* @__PURE__ */ jsxs("label", {
|
|
46
|
+
htmlFor: id,
|
|
47
|
+
"data-slot": "checkbox-row",
|
|
48
|
+
className: cn("flex min-h-5 cursor-pointer items-start gap-2.5 text-sm leading-5 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-60", rowClassName),
|
|
49
|
+
children: [/* @__PURE__ */ jsx(Checkbox, {
|
|
50
|
+
ref,
|
|
51
|
+
id,
|
|
52
|
+
disabled,
|
|
53
|
+
"aria-labelledby": `${id}-label`,
|
|
54
|
+
"aria-describedby": description ? `${id}-description` : void 0,
|
|
55
|
+
className: cn("mt-0.5", className),
|
|
56
|
+
...props
|
|
57
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
58
|
+
className: "grid min-w-0 gap-0.5",
|
|
59
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
60
|
+
id: `${id}-label`,
|
|
61
|
+
className: "font-medium",
|
|
62
|
+
children: label
|
|
63
|
+
}), description && /* @__PURE__ */ jsx("span", {
|
|
64
|
+
id: `${id}-description`,
|
|
65
|
+
className: "text-xs leading-4 text-muted-foreground",
|
|
66
|
+
children: description
|
|
67
|
+
})]
|
|
68
|
+
})]
|
|
69
|
+
});
|
|
70
|
+
});
|
|
29
71
|
//#endregion
|
|
30
|
-
export { Checkbox };
|
|
72
|
+
export { Checkbox, CheckboxRow };
|
|
31
73
|
|
|
32
74
|
//# sourceMappingURL=checkbox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkbox.js","names":["CheckboxPrimitive.Root","CheckboxPrimitive.Indicator"],"sources":["../../src/components/checkbox.tsx"],"sourcesContent":["import Check from '@burtson-labs/icons/react/check';\nimport Minus from '@burtson-labs/icons/react/minus';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as CheckboxPrimitive from '../primitives/vendor/radix/react-checkbox';\n\
|
|
1
|
+
{"version":3,"file":"checkbox.js","names":["CheckboxPrimitive.Root","CheckboxPrimitive.Indicator"],"sources":["../../src/components/checkbox.tsx"],"sourcesContent":["import Check from '@burtson-labs/icons/react/check';\nimport Minus from '@burtson-labs/icons/react/minus';\nimport * as React from 'react';\n\nimport { cn, focusRingClasses, touchTargetClasses } from '../lib/utils';\nimport * as CheckboxPrimitive from '../primitives/vendor/radix/react-checkbox';\n\n/**\n * A 16px box with a 44px hit area on touch screens. Its unchecked border is\n * the field border tone (`input`, 3:1 on the page in both modes); checked it\n * fills with primary in light mode and with brand (the ring colour, 4.5:1 on\n * the page, where primary reaches 2.8:1) in dark mode. Focus is the shared\n * 2px outline. Wrap it in a Label, or use CheckboxRow or CheckboxCard.\n */\nconst Checkbox = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<typeof CheckboxPrimitive.Root>\n>(function Checkbox({ className, ...props }, ref) {\n return (\n <CheckboxPrimitive.Root\n ref={ref}\n data-slot=\"checkbox\"\n className={cn(\n 'peer size-4 shrink-0 rounded-xs border border-input bg-surface shadow-xs transition-[border-color,box-shadow,background-color] hover:border-brand/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground dark:data-[state=checked]:border-brand dark:data-[state=checked]:bg-brand dark:data-[state=checked]:text-background dark:data-[state=indeterminate]:border-brand dark:data-[state=indeterminate]:bg-brand dark:data-[state=indeterminate]:text-background',\n focusRingClasses,\n touchTargetClasses,\n className,\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"group/indicator grid place-content-center text-current\"\n >\n <Check\n className=\"size-3.5 group-data-[state=indeterminate]/indicator:hidden\"\n strokeWidth={2.5}\n aria-hidden\n />\n <Minus\n className=\"hidden size-3.5 group-data-[state=indeterminate]/indicator:block\"\n strokeWidth={2.5}\n aria-hidden\n />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n );\n});\n\nexport interface CheckboxRowProps extends Omit<\n React.ComponentProps<typeof CheckboxPrimitive.Root>,\n 'children'\n> {\n /** The option's name. */\n label: React.ReactNode;\n /** A line under the name. */\n description?: React.ReactNode;\n /** Extra classes for the row (the label); className goes to the checkbox. */\n rowClassName?: string;\n}\n\n/**\n * A checkbox with its label to the right and an optional line under it, the\n * whole row clickable. For a bordered option with a sentence of explanation\n * use CheckboxCard.\n */\nconst CheckboxRow = React.forwardRef<HTMLButtonElement, CheckboxRowProps>(function CheckboxRow(\n { label, description, id: idProp, className, rowClassName, disabled, ...props },\n ref,\n) {\n const auto = React.useId();\n const id = idProp ?? auto;\n return (\n <label\n htmlFor={id}\n data-slot=\"checkbox-row\"\n className={cn(\n 'flex min-h-5 cursor-pointer items-start gap-2.5 text-sm leading-5 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-60',\n rowClassName,\n )}\n >\n <Checkbox\n ref={ref}\n id={id}\n disabled={disabled}\n aria-labelledby={`${id}-label`}\n aria-describedby={description ? `${id}-description` : undefined}\n className={cn('mt-0.5', className)}\n {...props}\n />\n <span className=\"grid min-w-0 gap-0.5\">\n <span id={`${id}-label`} className=\"font-medium\">\n {label}\n </span>\n {description && (\n <span id={`${id}-description`} className=\"text-xs leading-4 text-muted-foreground\">\n {description}\n </span>\n )}\n </span>\n </label>\n );\n});\n\nexport { Checkbox, CheckboxRow };\n"],"mappings":";;;;;;;;;;;;;;;AAcA,IAAM,WAAW,MAAM,WAGrB,SAAS,SAAS,EAAE,WAAW,GAAG,SAAS,KAAK;CAChD,OACE,oBAACA,YAAD;EACO;EACL,aAAU;EACV,WAAW,GACT,stBACA,kBACA,oBACA,SACF;EACA,GAAI;EAEJ,UAAA,qBAACC,mBAAD;GACE,aAAU;GACV,WAAU;GAFZ,UAAA,CAIE,oBAAC,OAAD;IACE,WAAU;IACV,aAAa;IACb,eAAA;GACD,CAAA,GACD,oBAAC,OAAD;IACE,WAAU;IACV,aAAa;IACb,eAAA;GACD,CAAA,CAC0B;;CACP,CAAA;AAE5B,CAAC;;;;;;AAmBD,IAAM,cAAc,MAAM,WAAgD,SAAS,YACjF,EAAE,OAAO,aAAa,IAAI,QAAQ,WAAW,cAAc,UAAU,GAAG,SACxE,KACA;CACA,MAAM,OAAO,MAAM,MAAM;CACzB,MAAM,KAAK,UAAU;CACrB,OACE,qBAAC,SAAD;EACE,SAAS;EACT,aAAU;EACV,WAAW,GACT,mIACA,YACF;EANF,UAAA,CAQE,oBAAC,UAAD;GACO;GACD;GACM;GACV,mBAAiB,GAAG,GAAG;GACvB,oBAAkB,cAAc,GAAG,GAAG,gBAAgB,KAAA;GACtD,WAAW,GAAG,UAAU,SAAS;GACjC,GAAI;EACL,CAAA,GACD,qBAAC,QAAD;GAAM,WAAU;GAAhB,UAAA,CACE,oBAAC,QAAD;IAAM,IAAI,GAAG,GAAG;IAAS,WAAU;IAChC,UAAA;GACG,CAAA,GACL,eACC,oBAAC,QAAD;IAAM,IAAI,GAAG,GAAG;IAAe,WAAU;IACtC,UAAA;GACG,CAAA,CAEJ;EACD,CAAA,CAAA;;AAEX,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import * as CollapsiblePrimitive from '../primitives/vendor/radix/react-collapsible';
|
|
3
3
|
declare function Collapsible(props: React.ComponentProps<typeof CollapsiblePrimitive.Root>): React.JSX.Element;
|
|
4
|
-
declare
|
|
5
|
-
declare
|
|
4
|
+
declare const CollapsibleTrigger: React.ForwardRefExoticComponent<Omit<CollapsiblePrimitive.CollapsibleTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
5
|
+
declare const CollapsibleContent: React.ForwardRefExoticComponent<Omit<CollapsiblePrimitive.CollapsibleContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
6
6
|
export { Collapsible, CollapsibleContent, CollapsibleTrigger };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/utils.js";
|
|
3
3
|
import { Content, Root, Trigger } from "../primitives/vendor/radix/react-collapsible/collapsible.js";
|
|
4
|
-
import "react";
|
|
4
|
+
import * as React from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/collapsible.tsx
|
|
7
7
|
function Collapsible(props) {
|
|
@@ -10,19 +10,21 @@ function Collapsible(props) {
|
|
|
10
10
|
...props
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
|
-
function CollapsibleTrigger(props) {
|
|
13
|
+
var CollapsibleTrigger = React.forwardRef(function CollapsibleTrigger(props, ref) {
|
|
14
14
|
return /* @__PURE__ */ jsx(Trigger, {
|
|
15
|
+
ref,
|
|
15
16
|
"data-slot": "collapsible-trigger",
|
|
16
17
|
...props
|
|
17
18
|
});
|
|
18
|
-
}
|
|
19
|
-
function CollapsibleContent({ className, ...props }) {
|
|
19
|
+
});
|
|
20
|
+
var CollapsibleContent = React.forwardRef(function CollapsibleContent({ className, ...props }, ref) {
|
|
20
21
|
return /* @__PURE__ */ jsx(Content, {
|
|
22
|
+
ref,
|
|
21
23
|
"data-slot": "collapsible-content",
|
|
22
24
|
className: cn("overflow-hidden data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down", className),
|
|
23
25
|
...props
|
|
24
26
|
});
|
|
25
|
-
}
|
|
27
|
+
});
|
|
26
28
|
//#endregion
|
|
27
29
|
export { Collapsible, CollapsibleContent, CollapsibleTrigger };
|
|
28
30
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collapsible.js","names":["CollapsiblePrimitive.Root","CollapsiblePrimitive.Trigger","CollapsiblePrimitive.Content"],"sources":["../../src/components/collapsible.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as CollapsiblePrimitive from '../primitives/vendor/radix/react-collapsible';\n\nfunction Collapsible(props: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {\n return <CollapsiblePrimitive.Root data-slot=\"collapsible\" {...props} />;\n}\n\
|
|
1
|
+
{"version":3,"file":"collapsible.js","names":["CollapsiblePrimitive.Root","CollapsiblePrimitive.Trigger","CollapsiblePrimitive.Content"],"sources":["../../src/components/collapsible.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as CollapsiblePrimitive from '../primitives/vendor/radix/react-collapsible';\n\nfunction Collapsible(props: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {\n return <CollapsiblePrimitive.Root data-slot=\"collapsible\" {...props} />;\n}\n\nconst CollapsibleTrigger = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<typeof CollapsiblePrimitive.Trigger>\n>(function CollapsibleTrigger(props, ref) {\n return <CollapsiblePrimitive.Trigger ref={ref} data-slot=\"collapsible-trigger\" {...props} />;\n});\n\nconst CollapsibleContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof CollapsiblePrimitive.Content>\n>(function CollapsibleContent({ className, ...props }, ref) {\n return (\n <CollapsiblePrimitive.Content\n ref={ref}\n data-slot=\"collapsible-content\"\n className={cn(\n 'overflow-hidden data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down',\n className,\n )}\n {...props}\n />\n );\n});\n\nexport { Collapsible, CollapsibleContent, CollapsibleTrigger };\n"],"mappings":";;;;;;AAKA,SAAS,YAAY,OAA+D;CAClF,OAAO,oBAACA,MAAD;EAA2B,aAAU;EAAc,GAAI;CAAQ,CAAA;AACxE;AAEA,IAAM,qBAAqB,MAAM,WAG/B,SAAS,mBAAmB,OAAO,KAAK;CACxC,OAAO,oBAACC,SAAD;EAAmC;EAAK,aAAU;EAAsB,GAAI;CAAQ,CAAA;AAC7F,CAAC;AAED,IAAM,qBAAqB,MAAM,WAG/B,SAAS,mBAAmB,EAAE,WAAW,GAAG,SAAS,KAAK;CAC1D,OACE,oBAACC,SAAD;EACO;EACL,aAAU;EACV,WAAW,GACT,yGACA,SACF;EACA,GAAI;CACL,CAAA;AAEL,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { type FieldWidth } from './input';
|
|
2
3
|
export interface ComboboxOption {
|
|
3
4
|
value: string;
|
|
4
5
|
label: string;
|
|
@@ -8,8 +9,11 @@ export interface ComboboxOption {
|
|
|
8
9
|
}
|
|
9
10
|
export interface ComboboxProps {
|
|
10
11
|
options: ComboboxOption[];
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
/** Controlled: the chosen value, or null. Leave out for an uncontrolled combobox. */
|
|
13
|
+
value?: string | null;
|
|
14
|
+
/** The starting value when uncontrolled. */
|
|
15
|
+
defaultValue?: string | null;
|
|
16
|
+
onValueChange?: (value: string | null) => void;
|
|
13
17
|
placeholder?: string;
|
|
14
18
|
searchPlaceholder?: string;
|
|
15
19
|
emptyText?: string;
|
|
@@ -23,15 +27,21 @@ export interface ComboboxProps {
|
|
|
23
27
|
onSearchChange?: (search: string) => void;
|
|
24
28
|
/** Let the person clear the selection by picking it again. */
|
|
25
29
|
clearable?: boolean;
|
|
30
|
+
/** How wide the trigger is from 640px up; full width on phones. */
|
|
31
|
+
width?: FieldWidth;
|
|
26
32
|
id?: string;
|
|
27
33
|
className?: string;
|
|
28
34
|
'aria-invalid'?: boolean;
|
|
29
35
|
'aria-label'?: string;
|
|
30
36
|
'aria-labelledby'?: string;
|
|
31
37
|
'aria-describedby'?: string;
|
|
38
|
+
'aria-required'?: boolean;
|
|
32
39
|
name?: string;
|
|
33
40
|
required?: boolean;
|
|
34
41
|
}
|
|
35
|
-
/**
|
|
36
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Pick one value from a long or remote list by typing. The ref reaches the
|
|
44
|
+
* trigger button, so a form library or an ErrorSummary can focus it.
|
|
45
|
+
*/
|
|
46
|
+
declare const Combobox: React.ForwardRefExoticComponent<ComboboxProps & React.RefAttributes<HTMLButtonElement>>;
|
|
37
47
|
export { Combobox };
|
|
@@ -2,16 +2,25 @@
|
|
|
2
2
|
import { cn } from "../lib/utils.js";
|
|
3
3
|
import { Spinner } from "./spinner.js";
|
|
4
4
|
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
5
|
-
import { fieldClasses } from "./input.js";
|
|
5
|
+
import { fieldClasses, fieldWidthClasses } from "./input.js";
|
|
6
6
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "./command.js";
|
|
7
7
|
import * as React from "react";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import Check from "@burtson-labs/icons/react/check";
|
|
10
10
|
import ChevronsUpDown from "@burtson-labs/icons/react/chevrons-up-down";
|
|
11
11
|
//#region src/components/combobox.tsx
|
|
12
|
-
/**
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Pick one value from a long or remote list by typing. The ref reaches the
|
|
14
|
+
* trigger button, so a form library or an ErrorSummary can focus it.
|
|
15
|
+
*/
|
|
16
|
+
var Combobox = React.forwardRef(function Combobox({ options, value: valueProp, defaultValue = null, onValueChange, placeholder = "Select…", searchPlaceholder = "Search…", emptyText = "No matches.", disabled, loading, onSearchChange, clearable = true, width, id, className, name, required, ...aria }, ref) {
|
|
14
17
|
const [open, setOpen] = React.useState(false);
|
|
18
|
+
const [own, setOwn] = React.useState(defaultValue);
|
|
19
|
+
const value = valueProp === void 0 ? own : valueProp;
|
|
20
|
+
const setValue = (next) => {
|
|
21
|
+
if (valueProp === void 0) setOwn(next);
|
|
22
|
+
onValueChange?.(next);
|
|
23
|
+
};
|
|
15
24
|
const listId = React.useId();
|
|
16
25
|
const selected = options.find((o) => o.value === value);
|
|
17
26
|
return /* @__PURE__ */ jsxs(Fragment, { children: [name && /* @__PURE__ */ jsx("input", {
|
|
@@ -25,9 +34,10 @@ function Combobox({ options, value, onValueChange, placeholder = "Select…", se
|
|
|
25
34
|
children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
26
35
|
asChild: true,
|
|
27
36
|
children: /* @__PURE__ */ jsxs("button", {
|
|
37
|
+
ref,
|
|
28
38
|
type: "button",
|
|
29
39
|
role: "combobox",
|
|
30
|
-
"aria-required": required || void 0,
|
|
40
|
+
"aria-required": aria["aria-required"] ?? (required || void 0),
|
|
31
41
|
onKeyDown: (event) => {
|
|
32
42
|
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
33
43
|
event.preventDefault();
|
|
@@ -45,12 +55,12 @@ function Combobox({ options, value, onValueChange, placeholder = "Select…", se
|
|
|
45
55
|
"aria-invalid": aria["aria-invalid"],
|
|
46
56
|
disabled,
|
|
47
57
|
"data-slot": "combobox-trigger",
|
|
48
|
-
className: cn(fieldClasses, "flex h-9 items-center justify-between gap-2 text-left", !selected && "text-muted-foreground", className),
|
|
58
|
+
className: cn(fieldClasses, "flex h-9 items-center justify-between gap-2 text-left", !selected && "text-muted-foreground", width && fieldWidthClasses[width], className),
|
|
49
59
|
children: [/* @__PURE__ */ jsx("span", {
|
|
50
60
|
className: "truncate",
|
|
51
61
|
children: selected?.label ?? value ?? placeholder
|
|
52
62
|
}), /* @__PURE__ */ jsx(ChevronsUpDown, {
|
|
53
|
-
className: "size-4 shrink-0
|
|
63
|
+
className: "size-4 shrink-0 text-muted-foreground",
|
|
54
64
|
"aria-hidden": true
|
|
55
65
|
})]
|
|
56
66
|
})
|
|
@@ -72,7 +82,7 @@ function Combobox({ options, value, onValueChange, placeholder = "Select…", se
|
|
|
72
82
|
value: `${o.label} ${o.description ?? ""} ${o.value}`,
|
|
73
83
|
disabled: o.disabled,
|
|
74
84
|
onSelect: () => {
|
|
75
|
-
|
|
85
|
+
setValue(clearable && o.value === value ? null : o.value);
|
|
76
86
|
setOpen(false);
|
|
77
87
|
},
|
|
78
88
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
@@ -92,7 +102,7 @@ function Combobox({ options, value, onValueChange, placeholder = "Select…", se
|
|
|
92
102
|
})
|
|
93
103
|
})]
|
|
94
104
|
})] });
|
|
95
|
-
}
|
|
105
|
+
});
|
|
96
106
|
//#endregion
|
|
97
107
|
export { Combobox };
|
|
98
108
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.js","names":["_Fragment"],"sources":["../../src/components/combobox.tsx"],"sourcesContent":["import Check from '@burtson-labs/icons/react/check';\nimport ChevronsUpDown from '@burtson-labs/icons/react/chevrons-up-down';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from './command';\nimport { fieldClasses } from './input';\nimport { Popover, PopoverContent, PopoverTrigger } from './popover';\nimport { Spinner } from './spinner';\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n /** Second line, e.g. an email under a name. Also searched. */\n description?: string;\n disabled?: boolean;\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[];\n
|
|
1
|
+
{"version":3,"file":"combobox.js","names":["_Fragment"],"sources":["../../src/components/combobox.tsx"],"sourcesContent":["import Check from '@burtson-labs/icons/react/check';\nimport ChevronsUpDown from '@burtson-labs/icons/react/chevrons-up-down';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from './command';\nimport { fieldClasses, type FieldWidth, fieldWidthClasses } from './input';\nimport { Popover, PopoverContent, PopoverTrigger } from './popover';\nimport { Spinner } from './spinner';\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n /** Second line, e.g. an email under a name. Also searched. */\n description?: string;\n disabled?: boolean;\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[];\n /** Controlled: the chosen value, or null. Leave out for an uncontrolled combobox. */\n value?: string | null;\n /** The starting value when uncontrolled. */\n defaultValue?: string | null;\n onValueChange?: (value: string | null) => void;\n placeholder?: string;\n searchPlaceholder?: string;\n emptyText?: string;\n disabled?: boolean;\n /** Show a spinner in the list, e.g. while `onSearchChange` fetches. */\n loading?: boolean;\n /**\n * Called as the person types. Pass it to search on a server; the options\n * you pass back are shown as-is (no client filtering).\n */\n onSearchChange?: (search: string) => void;\n /** Let the person clear the selection by picking it again. */\n clearable?: boolean;\n /** How wide the trigger is from 640px up; full width on phones. */\n width?: FieldWidth;\n id?: string;\n className?: string;\n 'aria-invalid'?: boolean;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n 'aria-required'?: boolean;\n name?: string;\n required?: boolean;\n}\n\n/**\n * Pick one value from a long or remote list by typing. The ref reaches the\n * trigger button, so a form library or an ErrorSummary can focus it.\n */\nconst Combobox = React.forwardRef<HTMLButtonElement, ComboboxProps>(function Combobox(\n {\n options,\n value: valueProp,\n defaultValue = null,\n onValueChange,\n placeholder = 'Select…',\n searchPlaceholder = 'Search…',\n emptyText = 'No matches.',\n disabled,\n loading,\n onSearchChange,\n clearable = true,\n width,\n id,\n className,\n name,\n required,\n ...aria\n },\n ref,\n) {\n const [open, setOpen] = React.useState(false);\n const [own, setOwn] = React.useState<string | null>(defaultValue);\n const value = valueProp === undefined ? own : valueProp;\n const setValue = (next: string | null) => {\n if (valueProp === undefined) setOwn(next);\n onValueChange?.(next);\n };\n const listId = React.useId();\n const selected = options.find((o) => o.value === value);\n return (\n <>\n {name && <input type=\"hidden\" name={name} value={value ?? ''} disabled={disabled} />}\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n ref={ref}\n type=\"button\"\n role=\"combobox\"\n aria-required={aria['aria-required'] ?? (required || undefined)}\n onKeyDown={(event) => {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n setOpen(true);\n }\n }}\n id={id}\n aria-expanded={open}\n aria-haspopup=\"dialog\"\n aria-controls={open ? listId : undefined}\n aria-label={\n aria['aria-label'] ?? (aria['aria-labelledby'] || id ? undefined : placeholder)\n }\n aria-labelledby={aria['aria-labelledby']}\n aria-describedby={aria['aria-describedby']}\n data-required={required || undefined}\n aria-invalid={aria['aria-invalid']}\n disabled={disabled}\n data-slot=\"combobox-trigger\"\n className={cn(\n fieldClasses,\n 'flex h-9 items-center justify-between gap-2 text-left',\n !selected && 'text-muted-foreground',\n width && fieldWidthClasses[width],\n className,\n )}\n >\n <span className=\"truncate\">{selected?.label ?? value ?? placeholder}</span>\n <ChevronsUpDown className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden />\n </button>\n </PopoverTrigger>\n <PopoverContent\n id={listId}\n aria-label={aria['aria-label'] ?? placeholder}\n className=\"w-(--radix-popover-trigger-width) min-w-64 p-0\"\n align=\"start\"\n >\n <Command shouldFilter={!onSearchChange}>\n <CommandInput\n aria-label={searchPlaceholder}\n placeholder={searchPlaceholder}\n onValueChange={onSearchChange}\n />\n <CommandList>\n {loading ? (\n <div className=\"flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground\">\n <Spinner /> Searching…\n </div>\n ) : (\n <CommandEmpty>{emptyText}</CommandEmpty>\n )}\n {!loading && (\n <CommandGroup>\n {options.map((o) => (\n <CommandItem\n key={o.value}\n value={`${o.label} ${o.description ?? ''} ${o.value}`}\n disabled={o.disabled}\n onSelect={() => {\n setValue(clearable && o.value === value ? null : o.value);\n setOpen(false);\n }}\n >\n <span className=\"grid min-w-0 flex-1\">\n <span className=\"truncate\">{o.label}</span>\n {o.description && (\n <span className=\"truncate text-xs text-muted-foreground\">\n {o.description}\n </span>\n )}\n </span>\n <Check\n aria-hidden\n className={cn(\n 'ml-auto size-4 text-brand',\n o.value === value ? 'opacity-100' : 'opacity-0',\n )}\n />\n </CommandItem>\n ))}\n </CommandGroup>\n )}\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n </>\n );\n});\n\nexport { Combobox };\n"],"mappings":"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"}
|