Skip to content

Commit

Permalink
feat: improved commandmenu style and search, colorpicker input prop -…
Browse files Browse the repository at this point in the history
…> making it not mandatory
  • Loading branch information
mvriu5 committed Sep 17, 2024
1 parent 54e1391 commit d1677a3
Show file tree
Hide file tree
Showing 8 changed files with 870 additions and 2,554 deletions.
2 changes: 1 addition & 1 deletion components/colorpicker/ColorPicker.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type Story = StoryObj<typeof ColorPicker>

export const Default: Story = {
args: {
preColors: ["#4A90E2", "#FF6B6B", "#48D1CC", "#FFD700", "#9370DB", "#E17055", "#2ECC71", "#F39C12"],
colors: ["#4A90E2", "#FF6B6B", "#48D1CC", "#FFD700", "#9370DB", "#E17055", "#2ECC71", "#F39C12"],
title: "Pick a color",
},
};
35 changes: 19 additions & 16 deletions components/colorpicker/ColorPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { Seperator } from "../seperator/Seperator";
interface ColorPickerProps {
initialColor?: string;
onChange: (color: string) => void;
preColors?: string[];
colors: string[];
title?: string;
customColor?: boolean;
}

const ColorPicker: React.FC<ColorPickerProps> = ({ initialColor, preColors, onChange, title }) => {
const ColorPicker: React.FC<ColorPickerProps> = ({ initialColor, colors, onChange, title, customColor = "true" }) => {
const [color, setColor] = useState<string>(initialColor || "#FFFFFF");

const handleColorChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -26,24 +27,26 @@ const ColorPicker: React.FC<ColorPickerProps> = ({ initialColor, preColors, onCh
</div>
}
<div className={"grid grid-cols-4 gap-4 px-2"}>
{preColors?.map((preColor) => (
{colors?.map((color) => (
<ColorBox
key={preColor}
color={preColor}
onClick={() => setColor(preColor)}
key={color}
color={color}
onClick={() => setColor(color)}
/>
))}
</div>

<div className={"px-2"}>
<label className={"ml-0.5 block text-xs text-zinc-400 dark:text-marcador"}>Selected Color</label>
<input
type="color"
className={"w-full h-8 rounded-md bg-inherit appearance-none cursor-pointer"}
value={color}
onChange={e => handleColorChange(e)}
/>
</div>
{customColor &&
<div className={"px-2"}>
<label className={"ml-0.5 block text-xs text-zinc-400 dark:text-marcador"}>Selected Color</label>
<input
type="color"
className={"w-full h-8 rounded-md bg-inherit appearance-none cursor-pointer"}
value={color}
onChange={e => handleColorChange(e)}
/>
</div>
}
</div>
);
}
Expand All @@ -58,4 +61,4 @@ const ColorBox: React.FC<{ color: string, onClick: () => void }> = ({color, onCl
);
}

export { ColorPicker };
export {ColorPicker};
4 changes: 1 addition & 3 deletions components/commandmenu/CommandMenu.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,10 @@ export const Default = () => {
id: 1,
title: "Open File",
description: "Open an existing file from your system",
icon: <File size={20} />, // Lucide icon for file
icon: <File size={20}/>, // Lucide icon for file
onClick: (e) => console.log("Open File clicked"),
shortcut: "Ctrl+O",
disabled: false,
selected: false,
keywords: ["open", "file", "load"]
},
{
Expand All @@ -71,7 +70,6 @@ export const Default = () => {
title: "Delete Item",
icon: <Trash2 size={20} />, // Lucide icon for delete
onClick: (e) => console.log("Delete clicked"),
selected: true,
keywords: ["delete", "remove", "trash"],
disabled: false
},
Expand Down
64 changes: 38 additions & 26 deletions components/commandmenu/CommandMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import {cn} from "../../utils/cn";
import {DialogRef} from "@/components/dialog/Dialog";
import {Shortcut} from "@/components/shortcut/Shortcut";
import {Seperator} from "@/components/seperator/Seperator";
import {CornerDownLeft, MoveDown, MoveUp, Search} from "lucide-react";
import {CornerDownLeft, Info, MoveDown, MoveUp, Search} from "lucide-react";
import {Input} from "@/components/input/Input";
import {useHotkeys} from "react-hotkeys-hook";
import {useOutsideClick} from "../../hooks/useOutsideClick";
import {AnimatePresence, motion} from "framer-motion";
import {CloseButton} from "@/components/closebutton/CloseButton";

interface CommandMenuProps extends DialogHTMLAttributes<HTMLDialogElement> {
items: CommandMenuItemProps[];
Expand All @@ -31,7 +32,7 @@ interface CommandMenuItemProps {

const CommandMenu = forwardRef<DialogRef, CommandMenuProps>(({ isOpen, onClose, items, showItems, animation = "fade", ...props }, ref) => {
const [searchTerm, setSearchTerm] = useState<string>("");
const [filteredItems, setFilteredItems] = useState(items);
const [filteredItems, setFilteredItems] = useState(showItems);

const [selectedIndex, setSelectedIndex] = useState<number>(-1);
const [inputFocused, setInputFocused] = useState<boolean>(true);
Expand Down Expand Up @@ -59,13 +60,6 @@ const CommandMenu = forwardRef<DialogRef, CommandMenuProps>(({ isOpen, onClose,
}
}

useEffect(() => {
if (isOpen) {
setFilteredItems(showItems);
}
}, []);


useHotkeys('esc', () => {
handleClose();
})
Expand Down Expand Up @@ -120,24 +114,42 @@ const CommandMenu = forwardRef<DialogRef, CommandMenuProps>(({ isOpen, onClose,
onClick={(e) => e.stopPropagation()}
>
<div className={"flex flex-col"}>
<div className={"w-full flex flex-row items-center px-2 pt-2"}>
<Search size={18}
className={"text-zinc-400 dark:text-marcador ml-4 mr-2"}
/>
<Input
placeholder={"Search or type a command"}
border={"none"}
className={"w-full h-12 text-md m-0 mr-2 p-0 bg-zinc-100 dark:bg-black"}
value={searchTerm}
onChange={handleSearch}
ref={inputRef}
<div className={"w-full flex flex-row items-center justify-between pl-2 pr-4 pt-2 text-zinc-500 dark:text-gray"}>
<div className={"w-full flex flex-row items-center"}>
<Search size={18}
className={"ml-4 mr-2"}
/>
<Input
placeholder={"Search or type a command"}
border={"none"}
className={"w-full h-12 text-md m-0 mr-2 p-0 bg-zinc-100 dark:bg-black"}
value={searchTerm}
onChange={handleSearch}
ref={inputRef}
size={80}
/>
</div>

<CloseButton
className={"dark:bg-black"}
onClick={() => {
setSelectedIndex(-1);
setInputFocused(true);
setSearchTerm('');
setFilteredItems(showItems);
}}
/>
</div>
<Seperator/>
<div className={"flex flex-col space-y-2 p-2 max-h-[400px] overflow-y-auto"}>
{filteredItems.length === 0 &&
<div className={"flex flex-row items-center justify-center text-zinc-500 dark:text-gray"}>
{"No results found"}
<div
className={"py-4 flex flex-col space-y-2 items-center justify-center"}>
<div className={"flex flex-row space-x-1 items-center text-zinc-500 dark:text-gray"}>
<Info size={16}/>
<span>{"No results found"}</span>
</div>
<span className={"text-zinc-400 dark:text-marcador"}>{"Search something different..."}</span>
</div>
}
{filteredItems.length > 0 && filteredItems.map((item) => (
Expand All @@ -148,14 +160,14 @@ const CommandMenu = forwardRef<DialogRef, CommandMenuProps>(({ isOpen, onClose,
item.selected ? "bg-zinc-200 dark:bg-dark" : "",
item.disabled ? "opacity-50 cursor-not-allowed" : ""
)}
onMouseEnter={() => console.log(item.selected)}
onClick={item.onClick}
>
<div
className={"w-max flex flex-row items-center space-x-2 text-zinc-800 dark:text-white text-md"}>
<div className={"w-max flex flex-row items-center space-x-2 text-md text-zinc-800 dark:text-white"}>
{item.icon}
<span>{item.title}</span>
{item.description &&
<span className={"text-gray-500 dark:text-gray"}>{item.description}</span>
<span className={"text-sm text-zinc-500 dark:text-gray"}>{item.description}</span>
}
</div>
{item.shortcut &&
Expand All @@ -166,7 +178,7 @@ const CommandMenu = forwardRef<DialogRef, CommandMenuProps>(({ isOpen, onClose,
</div>
<Seperator/>
<div
className={"flex flex-row items-center rounded-b-lg space-x-8 px-4 h-12 bg-zinc-200 dark:bg-dark text-zinc-500 dark:text-gray text-sm"}>
className={"flex flex-row items-center rounded-b-lg space-x-8 px-6 h-12 bg-zinc-200 dark:bg-dark text-zinc-500 dark:text-gray text-sm"}>
<div className={"flex flex-row items-center space-x-2"}>
<span>{"Close"}</span>
<Shortcut text={"ESC"}/>
Expand Down
14 changes: 9 additions & 5 deletions hooks/useOutsideClick.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import {useEffect, useRef} from "react";

const useOutsideClick = (callback: (e: MouseEvent) => void) => {
const ref = useRef<HTMLDivElement>(null);
const useOutsideClick = (callback: (e: MouseEvent | TouchEvent) => void) => {
const ref = useRef<HTMLDivElement>(null)

useEffect(() => {
const handler = (e: MouseEvent) => {
if (!ref?.current?.contains(e.target as Node)) {
callback(e);
const handler = (e: MouseEvent | TouchEvent) => {
const el = ref?.current
if (!el || el.contains((e?.target as Node) || null)) {
return
}
handler(e)
}
document.addEventListener("mousedown", handler);
document.addEventListener("touchstart", handler)

return () => {
document.removeEventListener("mousedown", handler);
document.removeEventListener("touchstart", handler);
}
}, [callback]);

Expand Down
Loading

0 comments on commit d1677a3

Please sign in to comment.