UNPKG

lightswind

Version:

A professionally designed component library & templates market that brings together functionality, accessibility, and beautiful aesthetics for modern applications.

124 lines (123 loc) 8.34 kB
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; import * as React from "react"; import { cn } from "../lib/utils"; import { Dialog, DialogContent } from "./dialog"; import { Search, Loader2 } from "lucide-react"; const CommandContext = React.createContext(undefined); function useCommand() { const context = React.useContext(CommandContext); if (!context) { throw new Error("useCommand must be used within a Command component"); } return context; } const Command = React.forwardRef(({ className, isLoading: controlledLoading, emptyMessage = "No results found.", ...props }, ref) => { const [value, setValue] = React.useState(""); const [internalLoading, setInternalLoading] = React.useState(false); const isLoading = controlledLoading !== undefined ? controlledLoading : internalLoading; const filter = React.useCallback((items) => { if (!value) return items; return items.filter((item) => typeof item.label === "string" ? item.label.toLowerCase().includes(value.toLowerCase()) : item.value.toLowerCase().includes(value.toLowerCase())); }, [value]); return (_jsx(CommandContext.Provider, { value: { value, onValueChange: setValue, filter, isLoading, setIsLoading: setInternalLoading, emptyMessage }, children: _jsx("div", { ref: ref, className: cn("flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", className), ...props, "cmdk-root": "" }) })); }); Command.displayName = "Command"; const CommandDialog = ({ children, open, onOpenChange, className, }) => { // Prevent form submission causing page refresh const handleDialogClick = (e) => { // Prevent any click events from bubbling up to a form e.stopPropagation(); }; // Handle ESC key to close dialog React.useEffect(() => { if (!open) return; const handleKeyDown = (e) => { if (e.key === 'Escape') { e.preventDefault(); if (onOpenChange) { onOpenChange(false); } } }; document.addEventListener('keydown', handleKeyDown); return () => { document.removeEventListener('keydown', handleKeyDown); }; }, [open, onOpenChange]); // Add body class to enable blur effect on the entire page React.useEffect(() => { if (open) { document.body.classList.add('command-dialog-open'); } else { document.body.classList.remove('command-dialog-open'); } return () => { document.body.classList.remove('command-dialog-open'); }; }, [open]); return (_jsxs(_Fragment, { children: [open && (_jsx("div", { className: "fixed inset-0 z-40 bg-black/40 backdrop-blur-sm", "aria-hidden": "true" })), _jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsx(DialogContent, { className: cn("fixed overflow-hidden p-0 shadow-xl border-muted/50 bg-background/90 backdrop-blur-lg max-w-3xl z-50", "top-[10vh] max-h-[80vh]", // Position from top with max height className), onClick: handleDialogClick, children: _jsx(Command, { className: "[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5", children: children }) }) })] })); }; const CommandInput = React.forwardRef(({ className, onValueChange, isLoading: controlledLoading, ...props }, ref) => { const { value, onValueChange: contextOnValueChange, isLoading: contextLoading } = useCommand(); const isLoading = controlledLoading !== undefined ? controlledLoading : contextLoading; const handleChange = React.useCallback((e) => { e.preventDefault(); // Prevent form submission const newValue = e.target.value; if (onValueChange) { onValueChange(newValue); } else { contextOnValueChange(newValue); } }, [onValueChange, contextOnValueChange]); // Prevent form submission on key press const handleKeyDown = (e) => { if (e.key === 'Enter') { e.preventDefault(); } }; return (_jsxs("div", { className: "flex items-center border-b px-3", "cmdk-input-wrapper": "", children: [isLoading ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin opacity-70" })) : (_jsx(Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" })), _jsx("input", { ref: ref, value: props.value !== undefined ? props.value : value, onChange: handleChange, onKeyDown: handleKeyDown, className: cn("flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", className), placeholder: props.placeholder || "Type to search...", "cmdk-input": "", autoComplete: "off", autoCorrect: "off", spellCheck: "false", "aria-autocomplete": "list", ...props })] })); }); CommandInput.displayName = "CommandInput"; const CommandList = React.forwardRef(({ className, isLoading: controlledLoading, ...props }, ref) => { const { isLoading: contextLoading } = useCommand(); const isLoading = controlledLoading !== undefined ? controlledLoading : contextLoading; return (_jsxs("div", { ref: ref, className: cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className), ...props, children: [isLoading && props.children && (_jsx("div", { className: "flex items-center justify-center py-6", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }) })), !isLoading && props.children] })); }); CommandList.displayName = "CommandList"; const CommandEmpty = React.forwardRef((props, ref) => { const { emptyMessage } = useCommand(); return (_jsx("div", { ref: ref, className: "py-6 text-center text-sm text-muted-foreground", ...props, children: props.children || emptyMessage || "No results found." })); }); CommandEmpty.displayName = "CommandEmpty"; const CommandGroup = React.forwardRef(({ className, heading, ...props }, ref) => (_jsxs("div", { ref: ref, className: cn("overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground", className), ...props, children: [heading && _jsx("div", { "cmdk-group-heading": "", children: heading }), props.children] }))); CommandGroup.displayName = "CommandGroup"; const CommandSeparator = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("-mx-1 h-px bg-border", className), ...props }))); CommandSeparator.displayName = "CommandSeparator"; const CommandItem = React.forwardRef(({ className, disabled, onSelect, value, ...props }, ref) => { const [isSelected, setIsSelected] = React.useState(false); return (_jsx("div", { ref: ref, className: cn("relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50", isSelected && "bg-accent text-accent-foreground", className), "data-disabled": disabled ? "true" : undefined, "data-selected": isSelected ? "true" : undefined, "data-value": value, onMouseEnter: () => setIsSelected(true), onMouseLeave: () => setIsSelected(false), onClick: () => { if (!disabled && onSelect) { onSelect(); } }, ...props })); }); CommandItem.displayName = "CommandItem"; const CommandShortcut = ({ className, ...props }) => { return (_jsx("span", { className: cn("ml-auto text-xs tracking-widest text-muted-foreground", className), ...props })); }; CommandShortcut.displayName = "CommandShortcut"; export { Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator, };