feat: add search item cursor

This commit is contained in:
Sonny
2023-05-17 12:55:59 +02:00
parent e19bffb792
commit 261254576b
6 changed files with 79 additions and 26 deletions

View File

@@ -33,14 +33,14 @@ export default function Modal({
onClick={handleWrapperClick}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0, transition: { duration: 0.1 } }}
exit={{ opacity: 0, transition: { duration: 0.1, delay: 0.1 } }}
>
<motion.div
className={styles["modal-container"]}
style={{ padding }}
initial={{ opacity: 0, y: -15 }}
initial={{ opacity: 0, y: "-6em" }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -15, transition: { duration: 0.1 } }}
exit={{ opacity: 0, y: "-6em", transition: { duration: 0.1 } }}
>
{!noHeader && (
<div className={styles["modal-header"]}>

View File

@@ -1,4 +1,4 @@
import { ReactNode } from "react";
import { ReactNode, useMemo } from "react";
import { SearchItem } from "types";
import SearchListItem from "./SearchListItem";
@@ -8,14 +8,18 @@ import styles from "./search.module.scss";
export default function SearchList({
items,
noItem,
cursor,
setCursor,
}: {
items: SearchItem[];
noItem?: ReactNode;
cursor: number;
setCursor: (cursor: number) => void;
}) {
return (
<ul className={styles["search-list"]}>
{items.length > 0 ? (
items.map((item) => (
items.map((item, index) => (
<SearchListItem
item={{
id: item.id,
@@ -23,6 +27,9 @@ export default function SearchList({
url: item.url,
type: item.type,
}}
setCursor={setCursor}
selected={index === cursor}
index={index}
key={item.type + "-" + item.id}
/>
))

View File

@@ -1,23 +1,66 @@
import { motion } from "framer-motion";
import LinkTag from "next/link";
import { AiOutlineFolder } from "react-icons/ai";
import LinkFavicon from "components/Links/LinkFavicon";
import { SearchItem } from "types";
import { useEffect, useId, useRef, useState } from "react";
import styles from "./search.module.scss";
export default function SearchListItem({ item }: { item: SearchItem }) {
export default function SearchListItem({
item,
index,
selected,
setCursor,
}: {
item: SearchItem;
index?: number;
selected: boolean;
setCursor: (cursor: number) => void;
}) {
const id = useId();
const ref = useRef<HTMLLIElement>(null);
const [isHover, setHover] = useState<boolean>(false);
const { name, type, url } = item;
useEffect(() => {
if (selected && !isHover) {
console.log(selected, ref.current);
ref.current?.scrollIntoView({ behavior: "smooth", block: "center" });
}
}, [isHover, selected]);
return (
<li className={styles["search-item"]}>
<motion.li
className={styles["search-item"]}
initial={{ y: -15, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{
type: "spring",
stiffness: 260,
damping: 20,
delay: index * 0.025,
}}
ref={ref}
onMouseEnter={() => {
setCursor(index);
setHover(true);
}}
onMouseLeave={() => setHover(false)}
key={id}
>
<LinkTag href={url} target="_blank" rel="no-referrer">
{type === "link" ? (
<LinkFavicon url={item.url} noMargin />
<LinkFavicon url={item.url} noMargin size={24} />
) : (
<AiOutlineFolder size={32} />
<AiOutlineFolder size={24} />
)}
<span>{name}</span>
{selected && "selected"}
</LinkTag>
</li>
</motion.li>
);
}

View File

@@ -11,7 +11,7 @@ import SearchList from "./SearchList";
import * as Keys from "constants/keys";
import { GOOGLE_SEARCH_URL } from "constants/search-urls";
import { Category, Link, SearchItem } from "types";
import { Category, SearchItem } from "types";
import styles from "./search.module.scss";
@@ -19,13 +19,11 @@ export default function SearchModal({
close,
handleSelectCategory,
categories,
favorites,
items,
}: {
close: any;
handleSelectCategory: (category: Category) => void;
categories: Category[];
favorites: Link[];
items: SearchItem[];
}) {
const autoFocusRef = useAutoFocus();
@@ -51,12 +49,16 @@ export default function SearchModal({
[items, search]
);
useHotkeys(Keys.ARROW_LEFT, () => {
console.log("left");
useHotkeys(Keys.ARROW_UP, () => setCursor((cursor) => (cursor -= 1)), {
enableOnFormTags: ["INPUT"],
enabled: itemsCompletion.length > 1 && cursor !== 0,
preventDefault: true,
});
useHotkeys(Keys.ARROW_RIGHT, () => {
console.log("right");
useHotkeys(Keys.ARROW_DOWN, () => setCursor((cursor) => (cursor += 1)), {
enableOnFormTags: ["INPUT"],
enabled:
itemsCompletion.length > 1 && cursor !== itemsCompletion.length - 1,
preventDefault: true,
});
const handleSearchInputChange = useCallback((value) => {
@@ -74,19 +76,17 @@ export default function SearchModal({
return close();
}
// TODO: replace "firstItem" by a "cursor"
const firstItem = itemsCompletion[0];
const category = categories.find((c) => c.id === firstItem.id);
if (firstItem.type === "category" && category) {
const selectedItem = itemsCompletion[cursor];
const category = categories.find((c) => c.id === selectedItem.id);
if (selectedItem.type === "category" && category) {
handleSelectCategory(category);
return close();
}
window.open(firstItem.url);
window.open(selectedItem.url);
close();
},
[categories, close, handleSelectCategory, itemsCompletion, search]
[categories, close, cursor, handleSelectCategory, itemsCompletion, search]
);
return (
@@ -121,6 +121,8 @@ export default function SearchModal({
type: item.type,
}))}
noItem={<LabelSearchWithGoogle />}
cursor={cursor}
setCursor={setCursor}
/>
)}
<button type="submit" disabled={!canSubmit} style={{ display: "none" }}>

View File

@@ -48,6 +48,7 @@ function Home(props: HomeProps) {
name: item.name,
url: type === "link" ? (item as Link).url : `/?categoryId=${item.id}`,
type,
category: type === "link" ? (item as Link).category : undefined,
});
const itemsSearch = useMemo<SearchItem[]>(() => {
@@ -179,7 +180,6 @@ function Home(props: HomeProps) {
<SearchModal
close={modal.close}
categories={categories}
favorites={favorites}
items={itemsSearch}
handleSelectCategory={handleSelectCategory}
/>

1
src/types.d.ts vendored
View File

@@ -32,4 +32,5 @@ export interface SearchItem {
name: string;
url: string;
type: "category" | "link";
category?: undefined | Link["category"];
}