mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-08 22:53:25 +00:00
wip: search modal cursor
This commit is contained in:
15
src/components/SearchModal/LabelSearchWithGoogle.tsx
Normal file
15
src/components/SearchModal/LabelSearchWithGoogle.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
40
src/components/SearchModal/SearchList.tsx
Normal file
40
src/components/SearchModal/SearchList.tsx
Normal 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>;
|
||||
}
|
||||
23
src/components/SearchModal/SearchListItem.tsx
Normal file
23
src/components/SearchModal/SearchListItem.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
10
src/constants/keys.ts
Normal 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";
|
||||
1
src/constants/search-urls.ts
Normal file
1
src/constants/search-urls.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const GOOGLE_SEARCH_URL = "https://google.com/search?q=";
|
||||
Reference in New Issue
Block a user