wip: search modal cursor

This commit is contained in:
Sonny
2023-05-16 19:37:02 +02:00
parent 304c1223ee
commit 73edba6d45
7 changed files with 167 additions and 76 deletions

View File

@@ -0,0 +1,15 @@
import { FcGoogle } from "react-icons/fc";
import styles from "./search.module.scss";
export default function LabelSearchWithGoogle() {
return (
<i className={styles["search-with-google"]}>
Recherche avec{" "}
<span>
<FcGoogle size={24} />
oogle
</span>
</i>
);
}

View File

@@ -0,0 +1,40 @@
import { ReactNode } from "react";
import { SearchItem } from "types";
import SearchListItem from "./SearchListItem";
import styles from "./search.module.scss";
export default function SearchList({
items,
noItem,
}: {
items: SearchItem[];
noItem?: ReactNode;
}) {
return (
<ul className={styles["search-list"]}>
{items.length > 0 ? (
items.map((item) => (
<SearchListItem
item={{
id: item.id,
name: item.name,
url: item.url,
type: item.type,
}}
key={item.type + "-" + item.id}
/>
))
) : noItem ? (
noItem
) : (
<LabelNoItem />
)}
</ul>
);
}
function LabelNoItem() {
return <i className={styles["no-item"]}>Aucun élément trouvé</i>;
}

View File

@@ -0,0 +1,23 @@
import LinkTag from "next/link";
import { AiOutlineFolder } from "react-icons/ai";
import LinkFavicon from "components/Links/LinkFavicon";
import { SearchItem } from "types";
import styles from "./search.module.scss";
export default function SearchListItem({ item }: { item: SearchItem }) {
const { name, type, url } = item;
return (
<li className={styles["search-item"]}>
<LinkTag href={url} target="_blank" rel="no-referrer">
{type === "link" ? (
<LinkFavicon url={item.url} noMargin />
) : (
<AiOutlineFolder size={32} />
)}
<span>{name}</span>
</LinkTag>
</li>
);
}

View File

@@ -1,19 +1,19 @@
import LinkTag from "next/link";
import { ReactNode, useCallback, useMemo, useState } from "react";
import { FcGoogle } from "react-icons/fc";
import { FormEvent, useCallback, useMemo, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import useAutoFocus from "hooks/useAutoFocus";
import LinkFavicon from "components/Links/LinkFavicon";
import Modal from "components/Modal/Modal";
import TextBox from "components/TextBox";
import LabelSearchWithGoogle from "./LabelSearchWithGoogle";
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 styles from "./search.module.scss";
const GOOGLE_SEARCH_URL = "https://google.com/search?q=";
export default function SearchModal({
close,
handleSelectCategory,
@@ -29,7 +29,13 @@ export default function SearchModal({
}) {
const autoFocusRef = useAutoFocus();
// TODO: peristance
const [canSearchLink, setCanSearchLink] = useState<boolean>(true);
const [canSearchCategory, setCanSearchCategory] = useState<boolean>(true);
const [search, setSearch] = useState<string>("");
const [cursor, setCursor] = useState<number>(0);
const canSubmit = useMemo<boolean>(() => search.length > 0, [search]);
const itemsCompletion = useMemo(
@@ -44,8 +50,21 @@ export default function SearchModal({
[items, search]
);
useHotkeys(Keys.ARROW_LEFT, () => {
console.log("left");
});
useHotkeys(Keys.ARROW_RIGHT, () => {
console.log("right");
});
const handleSearchInputChange = useCallback((value) => {
setSearch(value);
setCursor(0);
}, []);
const handleSubmit = useCallback(
(event) => {
(event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
setSearch("");
@@ -72,16 +91,22 @@ export default function SearchModal({
return (
<Modal title="Rechercher" close={close}>
<form onSubmit={handleSubmit} className={styles["search-form"]}>
<SearchFilter
canSearchLink={canSearchLink}
setCanSearchLink={setCanSearchLink}
canSearchCategory={canSearchCategory}
setCanSearchCategory={setCanSearchCategory}
/>
<TextBox
name="search"
onChangeCallback={(value) => setSearch(value)}
onChangeCallback={handleSearchInputChange}
value={search}
placeholder="Rechercher"
innerRef={autoFocusRef}
fieldClass={styles["search-input-field"]}
/>
{search.length === 0 && favorites.length > 0 && (
<ListItemComponent
<SearchList
items={favorites.map((favorite) => ({
id: favorite.id,
name: favorite.name,
@@ -92,7 +117,7 @@ export default function SearchModal({
/>
)}
{search.length > 0 && (
<ListItemComponent
<SearchList
items={itemsCompletion.map((item) => ({
id: item.id,
name: item.name,
@@ -110,72 +135,48 @@ export default function SearchModal({
);
}
function LabelSearchWithGoogle() {
return (
<i className={styles["search-with-google"]}>
Recherche avec{" "}
<span>
<FcGoogle size={24} />
oogle
</span>
</i>
);
}
function LabelNoItem() {
return <i className={styles["no-item"]}>Aucun élément trouvé</i>;
}
function ListItemComponent({
items,
noItem,
function SearchFilter({
canSearchLink,
setCanSearchLink,
canSearchCategory,
setCanSearchCategory,
}: {
items: SearchItem[];
noItem?: ReactNode;
canSearchLink: boolean;
setCanSearchLink: (value: boolean) => void;
canSearchCategory: boolean;
setCanSearchCategory: (value: boolean) => void;
}) {
return (
<ul className={styles["list-item"]}>
{items.length > 0 ? (
items.map((item) => (
<ItemComponent
item={{
id: item.id,
name: item.name,
url: item.url,
type: item.type,
}}
key={item.type + "-" + item.id}
/>
))
) : noItem ? (
noItem
) : (
<LabelNoItem />
)}
</ul>
);
}
function ItemComponent({ item }: { item: SearchItem }) {
const { name, type, url } = item;
return (
<li className={styles["item"]}>
<LinkTag
href={url}
style={{
display: "flex",
flexDirection: "column",
}}
target="_blank"
rel="no-referrer"
>
{type === "link" ? (
<LinkFavicon url={item.url} noMargin />
) : (
<span>category</span>
)}
<span>{name}</span>
</LinkTag>
</li>
<div
style={{
display: "flex",
gap: "1em",
alignItems: "center",
justifyContent: "center",
marginBottom: "1em",
}}
>
<p>Rechercher</p>
<div style={{ display: "flex", gap: ".25em" }}>
<input
type="checkbox"
name="filter-link"
id="filter-link"
onChange={({ target }) => setCanSearchLink(target.checked)}
checked={canSearchLink}
/>
<label htmlFor="filter-link">liens</label>
</div>
<div style={{ display: "flex", gap: ".25em" }}>
<input
type="checkbox"
name="filter-category"
id="filter-category"
onChange={({ target }) => setCanSearchCategory(target.checked)}
checked={canSearchCategory}
/>
<label htmlFor="filter-category">categories</label>
</div>
</div>
);
}

View File

@@ -25,7 +25,7 @@ form.search-form {
}
}
.list-item {
.search-list {
margin: 1em 0;
display: flex;
gap: 1em;
@@ -34,9 +34,10 @@ form.search-form {
flex-wrap: wrap;
}
.item {
.search-item {
& > a {
display: flex;
align-items: center;
flex-direction: column;
}
}

10
src/constants/keys.ts Normal file
View File

@@ -0,0 +1,10 @@
export const OPEN_SEARCH_KEY = "s";
export const CLOSE_SEARCH_KEY = "escape";
export const OPEN_CREATE_LINK_KEY = "l";
export const OPEN_CREATE_CATEGORY_KEY = "c";
export const ARROW_UP = "ArrowUp";
export const ARROW_DOWN = "ArrowDown";
export const ARROW_LEFT = "ArrowLeft";
export const ARROW_RIGHT = "ArrowRight";

View File

@@ -0,0 +1 @@
export const GOOGLE_SEARCH_URL = "https://google.com/search?q=";