@arolariu/components
Version:
🎨 70+ beautiful, accessible React components built on Base UI. TypeScript-first, CSS Modules styling, tree-shakeable, SSR-ready. Perfect for modern web apps, design systems & rapid prototyping. Zero config, maximum flexibility! ⚡
426 lines (425 loc) • 17.4 kB
JavaScript
"use client";
import { jsx, jsxs } from "react/jsx-runtime";
import { Dialog } from "@base-ui/react/dialog";
import { Separator } from "@base-ui/react/separator";
import { Search } from "lucide-react";
import { cn } from "../../lib/utilities.js";
import command_module from "./command.module.js";
import * as __rspack_external_react from "react";
const CommandContext = /*#__PURE__*/ __rspack_external_react.createContext(null);
const CommandGroupContext = /*#__PURE__*/ __rspack_external_react.createContext(null);
function assignRef(ref, value) {
if ("function" == typeof ref) return void ref(value);
if (ref) ref.current = value;
}
function normalizeCommandValue(value) {
return value.trim().toLowerCase();
}
function defaultCommandFilter(value, search, keywords = []) {
if (0 === search.length) return 1;
const normalizedSearch = normalizeCommandValue(search);
const normalizedValue = normalizeCommandValue([
value,
...keywords
].join(" "));
return normalizedValue.includes(normalizedSearch) ? 1 : 0;
}
function useCommandContext(componentName) {
const context = __rspack_external_react.useContext(CommandContext);
if (!context) throw new Error(`${componentName} must be used within Command.`);
return context;
}
const Command = /*#__PURE__*/ __rspack_external_react.forwardRef(({ children, className, defaultValue: _defaultValue, disablePointerSelection = false, filter, label, loop = false, onKeyDown, onValueChange: _onValueChange, shouldFilter = true, value: _value, vimBindings: _vimBindings, ...props }, ref)=>{
const [activeItemId, setActiveItemId] = __rspack_external_react.useState(null);
const [search, setSearch] = __rspack_external_react.useState("");
const itemOrderRef = __rspack_external_react.useRef(0);
const itemsRef = __rspack_external_react.useRef(new Map());
const [itemsVersion, setItemsVersion] = __rspack_external_react.useState(0);
const listId = __rspack_external_react.useId();
const registerItem = __rspack_external_react.useCallback((item)=>{
const existingItem = itemsRef.current.get(item.id);
const nextItem = {
...item,
order: existingItem?.order ?? itemOrderRef.current++
};
const hasChanged = !existingItem || existingItem.disabled !== nextItem.disabled || existingItem.forceMount !== nextItem.forceMount || existingItem.groupId !== nextItem.groupId || existingItem.keywords.join("\u0000") !== nextItem.keywords.join("\u0000") || existingItem.ref !== nextItem.ref || existingItem.textValue !== nextItem.textValue || existingItem.value !== nextItem.value;
if (!hasChanged) return;
itemsRef.current.set(item.id, nextItem);
setItemsVersion((currentVersion)=>currentVersion + 1);
}, []);
const unregisterItem = __rspack_external_react.useCallback((itemId)=>{
if (!itemsRef.current.delete(itemId)) return;
setItemsVersion((currentVersion)=>currentVersion + 1);
}, []);
const items = __rspack_external_react.useMemo(()=>[
...itemsRef.current.values()
].toSorted((firstItem, secondItem)=>firstItem.order - secondItem.order), [
itemsVersion
]);
const isFiltering = shouldFilter && search.trim().length > 0;
const isItemVisible = __rspack_external_react.useCallback((itemId)=>{
const item = itemsRef.current.get(itemId);
if (!item) return false;
if (item.forceMount || !shouldFilter || 0 === search.trim().length) return true;
const itemValue = item.value ?? item.textValue;
const itemFilter = filter ?? defaultCommandFilter;
return itemFilter(itemValue, search, item.keywords) > 0;
}, [
filter,
search,
shouldFilter
]);
const visibleItems = __rspack_external_react.useMemo(()=>items.filter((item)=>isItemVisible(item.id)), [
isItemVisible,
items
]);
const selectableItems = __rspack_external_react.useMemo(()=>visibleItems.filter((item)=>!item.disabled), [
visibleItems
]);
__rspack_external_react.useEffect(()=>{
if (0 === selectableItems.length) return void setActiveItemId(null);
if (!activeItemId || !selectableItems.some((item)=>item.id === activeItemId)) setActiveItemId(selectableItems[0].id);
}, [
activeItemId,
selectableItems
]);
__rspack_external_react.useEffect(()=>{
if (!activeItemId) return;
itemsRef.current.get(activeItemId)?.ref.current?.scrollIntoView({
block: "nearest"
});
}, [
activeItemId
]);
const selectSpecificItem = __rspack_external_react.useCallback((itemId)=>{
setActiveItemId(itemId);
}, []);
const selectNextItem = __rspack_external_react.useCallback(()=>{
if (0 === selectableItems.length) return;
const currentIndex = selectableItems.findIndex((item)=>item.id === activeItemId);
if (-1 === currentIndex) return void setActiveItemId(selectableItems[0].id);
const nextIndex = currentIndex + 1;
if (nextIndex >= selectableItems.length) return void setActiveItemId(loop ? selectableItems[0].id : selectableItems[currentIndex].id);
setActiveItemId(selectableItems[nextIndex].id);
}, [
activeItemId,
loop,
selectableItems
]);
const selectPreviousItem = __rspack_external_react.useCallback(()=>{
if (0 === selectableItems.length) return;
const currentIndex = selectableItems.findIndex((item)=>item.id === activeItemId);
if (-1 === currentIndex) return void setActiveItemId(selectableItems[0].id);
const previousIndex = currentIndex - 1;
if (previousIndex < 0) return void setActiveItemId(loop ? selectableItems.at(-1)?.id ?? selectableItems[0].id : selectableItems[currentIndex].id);
setActiveItemId(selectableItems[previousIndex].id);
}, [
activeItemId,
loop,
selectableItems
]);
const triggerActiveItem = __rspack_external_react.useCallback(()=>{
if (!activeItemId) return;
itemsRef.current.get(activeItemId)?.ref.current?.click();
}, [
activeItemId
]);
const hasVisibleItemsInGroup = __rspack_external_react.useCallback((groupId)=>visibleItems.some((item)=>item.groupId === groupId), [
visibleItems
]);
const getVisibleItemCount = __rspack_external_react.useCallback(()=>visibleItems.length, [
visibleItems.length
]);
const contextValue = __rspack_external_react.useMemo(()=>({
activeItemId,
disablePointerSelection,
getVisibleItemCount,
hasVisibleItemsInGroup,
isFiltering,
isItemVisible,
listId,
loop,
registerItem,
search,
setSearch,
selectNextItem,
selectPreviousItem,
selectSpecificItem,
shouldFilter,
triggerActiveItem,
unregisterItem
}), [
activeItemId,
disablePointerSelection,
getVisibleItemCount,
hasVisibleItemsInGroup,
isFiltering,
isItemVisible,
listId,
loop,
registerItem,
search,
setSearch,
selectNextItem,
selectPreviousItem,
selectSpecificItem,
shouldFilter,
triggerActiveItem,
unregisterItem
]);
return /*#__PURE__*/ jsx(CommandContext.Provider, {
value: contextValue,
children: /*#__PURE__*/ jsx("div", {
ref: ref,
"aria-label": label,
className: cn(command_module.command, className),
role: "toolbar",
onKeyDown: (event)=>{
onKeyDown?.(event);
if (event.defaultPrevented) return;
switch(event.key){
case "ArrowDown":
event.preventDefault();
selectNextItem();
break;
case "ArrowUp":
event.preventDefault();
selectPreviousItem();
break;
case "Home":
if (0 === selectableItems.length) return;
event.preventDefault();
setActiveItemId(selectableItems[0].id);
break;
case "End":
if (0 === selectableItems.length) return;
event.preventDefault();
setActiveItemId(selectableItems.at(-1)?.id ?? selectableItems[0].id);
break;
case "Enter":
if (event.nativeEvent.isComposing) return;
event.preventDefault();
triggerActiveItem();
break;
default:
break;
}
},
...props,
children: children
})
});
});
Command.displayName = "Command";
function CommandDialog({ children, open, onOpenChange, title = "Command menu", ...props }) {
return /*#__PURE__*/ jsx(Dialog.Root, {
open: open,
onOpenChange: onOpenChange,
...props,
children: /*#__PURE__*/ jsxs(Dialog.Portal, {
children: [
/*#__PURE__*/ jsx(Dialog.Backdrop, {
className: command_module.backdrop
}),
/*#__PURE__*/ jsxs(Dialog.Popup, {
className: command_module.dialogPopup,
children: [
/*#__PURE__*/ jsx(Dialog.Title, {
className: command_module.srOnly,
children: title
}),
/*#__PURE__*/ jsx(Command, {
children: children
})
]
})
]
})
});
}
CommandDialog.displayName = "CommandDialog";
const CommandInput = /*#__PURE__*/ __rspack_external_react.forwardRef(({ className, onChange, onValueChange, value, ...props }, ref)=>{
const { activeItemId, listId, search, selectSpecificItem, setSearch } = useCommandContext("CommandInput");
const isControlled = void 0 !== value;
const inputValue = isControlled ? value : search;
__rspack_external_react.useEffect(()=>{
if (!isControlled) return;
setSearch(value ?? "");
}, [
isControlled,
setSearch,
value
]);
return /*#__PURE__*/ jsxs("div", {
className: command_module.inputWrapper,
children: [
/*#__PURE__*/ jsx(Search, {
className: command_module.searchIcon
}),
/*#__PURE__*/ jsx("input", {
ref: ref,
"aria-activedescendant": activeItemId ?? void 0,
"aria-autocomplete": "list",
"aria-controls": listId,
"aria-expanded": "true",
className: cn(command_module.input, className),
onChange: (event)=>{
onChange?.(event);
const nextSearchValue = event.currentTarget.value;
setSearch(nextSearchValue);
onValueChange?.(nextSearchValue);
selectSpecificItem(null);
},
role: "combobox",
type: "text",
value: inputValue,
...props
})
]
});
});
CommandInput.displayName = "CommandInput";
const CommandList = /*#__PURE__*/ __rspack_external_react.forwardRef(({ className, label, ...props }, ref)=>{
const { listId } = useCommandContext("CommandList");
return /*#__PURE__*/ jsx("div", {
"aria-label": label,
ref: ref,
className: cn(command_module.list, className),
id: listId,
role: "listbox",
...props
});
});
CommandList.displayName = "CommandList";
const CommandEmpty = /*#__PURE__*/ __rspack_external_react.forwardRef(({ className, ...props }, ref)=>{
const { getVisibleItemCount } = useCommandContext("CommandEmpty");
if (getVisibleItemCount() > 0) return null;
return /*#__PURE__*/ jsx("div", {
ref: ref,
className: cn(command_module.empty, className),
role: "status",
...props
});
});
CommandEmpty.displayName = "CommandEmpty";
const CommandGroup = /*#__PURE__*/ __rspack_external_react.forwardRef(({ children, className, forceMount = false, heading, value: _value, ...props }, ref)=>{
const groupId = __rspack_external_react.useId();
const { hasVisibleItemsInGroup, isFiltering } = useCommandContext("CommandGroup");
if (!forceMount && isFiltering && !hasVisibleItemsInGroup(groupId)) return null;
return /*#__PURE__*/ jsx(CommandGroupContext.Provider, {
value: groupId,
children: /*#__PURE__*/ jsxs("div", {
ref: ref,
className: cn(command_module.group, className),
"data-command-group": "",
...props,
children: [
heading ? /*#__PURE__*/ jsx("div", {
className: command_module.groupHeading,
children: heading
}) : null,
children
]
})
});
});
CommandGroup.displayName = "CommandGroup";
const CommandSeparator = /*#__PURE__*/ __rspack_external_react.forwardRef(({ alwaysRender = false, className, orientation = "horizontal", ...props }, ref)=>{
const { isFiltering } = useCommandContext("CommandSeparator");
if (isFiltering && !alwaysRender) return null;
return /*#__PURE__*/ jsx(Separator, {
ref: ref,
className: cn(command_module.separator, className),
orientation: orientation,
...props
});
});
CommandSeparator.displayName = "CommandSeparator";
const CommandItem = /*#__PURE__*/ __rspack_external_react.forwardRef(({ children, className, disabled = false, forceMount = false, keywords = [], onClick, onMouseEnter, onSelect, value, ...props }, ref)=>{
const { activeItemId, disablePointerSelection, isFiltering, isItemVisible, registerItem, selectSpecificItem, unregisterItem } = useCommandContext("CommandItem");
const groupId = __rspack_external_react.useContext(CommandGroupContext);
const generatedId = __rspack_external_react.useId();
const itemRef = __rspack_external_react.useRef(null);
const keywordSignature = __rspack_external_react.useMemo(()=>keywords.join("\u0000"), [
keywords
]);
__rspack_external_react.useLayoutEffect(()=>{
const textValue = value ?? itemRef.current?.textContent?.trim() ?? "";
registerItem({
disabled,
forceMount,
groupId,
id: generatedId,
keywords,
ref: itemRef,
textValue,
value
});
}, [
children,
disabled,
forceMount,
generatedId,
groupId,
keywordSignature,
keywords,
registerItem,
value
]);
__rspack_external_react.useEffect(()=>()=>{
unregisterItem(generatedId);
}, [
generatedId,
unregisterItem
]);
const isVisible = forceMount || !isFiltering || isItemVisible(generatedId);
if (!isVisible) return null;
const isSelected = activeItemId === generatedId;
return /*#__PURE__*/ jsx("div", {
...props,
ref: (node)=>{
itemRef.current = node;
assignRef(ref, node);
},
"aria-disabled": disabled || void 0,
"aria-selected": isSelected,
className: cn(command_module.item, className),
"data-disabled": disabled ? "true" : void 0,
"data-selected": isSelected ? "true" : void 0,
id: generatedId,
onClick: (event)=>{
if (disabled) return void event.preventDefault();
selectSpecificItem(generatedId);
onSelect?.(value ?? itemRef.current?.textContent?.trim() ?? "");
onClick?.(event);
},
onKeyDown: (event)=>{
if (disabled) return void event.preventDefault();
if ("Enter" === event.key || " " === event.key) {
event.preventDefault();
selectSpecificItem(generatedId);
onSelect?.(value ?? itemRef.current?.textContent?.trim() ?? "");
}
},
onFocus: ()=>{
if (disabled) return;
selectSpecificItem(generatedId);
},
onMouseEnter: (event)=>{
onMouseEnter?.(event);
if (disabled || disablePointerSelection) return;
selectSpecificItem(generatedId);
},
role: "option",
tabIndex: disabled ? -1 : 0,
children: children
});
});
CommandItem.displayName = "CommandItem";
const CommandShortcut = ({ className, ...props })=>/*#__PURE__*/ jsx("span", {
className: cn(command_module.shortcut, className),
...props
});
CommandShortcut.displayName = "CommandShortcut";
export { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut };
//# sourceMappingURL=command.js.map