UNPKG

@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
"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