search articles

This commit is contained in:
Sergey Yarkov 2023-02-21 22:39:31 +03:00
parent 25c37af405
commit 87ece33516
No known key found for this signature in database
GPG Key ID: E91CE1FABC619B6F
8 changed files with 84 additions and 88 deletions

View File

@ -1,75 +1,72 @@
import type { Component } from "solid-js";
import { createSignal, createEffect, For } from "solid-js";
import { search, selectedTags } from "@stores/searchStore";
import SearchInput from "./SearchInput";
import TagsList from "./TagsList";
import ArticleItem from "@components/ArticleItem";
import "./ArticlesList.scss";
type UiStringsType = { "articles.empty": string };
type UiStringsType = { "articles.empty": string; "input.search": string };
type ArticleType = {
type ArticlesType = Array<{
id: string;
title: string;
tags: string[];
pubDate: Date;
};
}>;
type ArticlesType = Record<string, ArticleType[]>;
type ArticlesBlockType = Record<string, ArticlesType>;
type ArticlesListProps = {
data: { articles: ArticlesType; pageLang: string };
articles: ArticlesBlockType;
tags: string[];
pageLang: string;
i18n: UiStringsType;
};
const ArticlesList: Component<ArticlesListProps> = (props) => {
const [articles, setArticles] = createSignal<ArticlesType>(props.data.articles);
const [articles, setArticles] = createSignal<ArticlesBlockType>(props.articles);
/**
* Sort articles by year in descending order
*/
const sortYears = (articles: ArticlesType) => {
const isEmpty = () => Object.keys(articles()).length === 0;
const sortYears = (articles: ArticlesBlockType) => {
return Object.keys(articles)
.map(Number)
.sort((a, b) => b - a);
};
const isEmpty = () => Object.keys(articles()).length === 0;
const getLength = () => Object.values(articles()).flat().length;
const searchArticles = ({
params: { search, tags },
}: {
params: { search: string; tags?: string[] };
}) => {
const filter = (cb: (year: string) => ArticlesType) => {
return Object.fromEntries(
Object.keys(props.articles)
.map((year) => [year, cb(year)])
.filter((articles) => articles[1].length !== 0)
);
};
createEffect(() => {
// const filtered = Object.values(articles()).flat(1)
return filter((year) => {
let filtered = props.articles[year].filter((a) =>
a.title.toLocaleLowerCase().includes(search)
);
if (tags && tags.length > 0) {
filtered = filtered.filter((a) => tags.some((t) => a.tags.includes(t)));
}
return filtered;
});
};
createEffect(() => {
let filtered = props.data.articles;
/**
* Find articles by title
*/
if (search()) {
// filtered = findArticlesBySearch(search().trim(), filtered);
// setArticles(filtered);
console.log(search());
}
/**
* Find articles by selected tags
*/
if (selectedTags().length !== 0) {
// filtered = findArticlesByTags(selectedTags(), filtered);
// setArticles(filtered);
return;
}
/**
* Set filtered variable as default value
*/
if (!search() && selectedTags().length === 0) {
filtered = props.data.articles;
setArticles(filtered);
}
setArticles(searchArticles({ params: { search: search(), tags: selectedTags() } }));
});
return (
<>
<SearchInput i18n={{ "input.search": props.i18n["input.search"] }} />
<TagsList data={props.tags} />
<div itemscope itemtype="http://schema.org/Blog" class="articles-list">
{!isEmpty() ? (
<For each={sortYears(articles())}>
@ -77,12 +74,12 @@ const ArticlesList: Component<ArticlesListProps> = (props) => {
<div class="articles-list__wrapper">
<h2>{year}</h2>
<For each={articles()[year]}>
{(a) => (
{({ id, title, pubDate }) => (
<ArticleItem
id={a.id}
title={a.title}
pubDate={a.pubDate}
pageLang={props.data.pageLang}
id={id}
title={title}
pubDate={pubDate}
pageLang={props.pageLang}
/>
)}
</For>
@ -95,6 +92,7 @@ const ArticlesList: Component<ArticlesListProps> = (props) => {
</div>
)}
</div>
</>
);
};

View File

@ -21,7 +21,7 @@ const SearchInput: Component<SearchInputProps> = (props) => {
* Update state and set url param
*/
const onSearch: JSX.EventHandler<HTMLInputElement, InputEvent> = (e) => {
const value = e.currentTarget.value;
const value = e.currentTarget.value.trim().toLocaleLowerCase();
if (value.trim().length === 0) {
resetSearch();
@ -31,8 +31,6 @@ const SearchInput: Component<SearchInputProps> = (props) => {
const url = utils.setParamToUrl("search", value.trim());
history.replaceState(null, "", `${location.pathname}?${url}`);
setSearch(value);
// TODO: search articles
};
/**

View File

@ -6,7 +6,7 @@ import * as utils from "@utils/history";
type TagsListProps = { data: string[] };
const TagsList: Component<TagsListProps> = (props) => {
const isTagSelected = (tag: string) => selectedTags().includes(tag.toLocaleLowerCase());
const isTagSelected = (tag: string) => selectedTags().includes(tag);
/**
* Reset signal state and `tags` property in history state
@ -21,6 +21,8 @@ const TagsList: Component<TagsListProps> = (props) => {
const onSelect: JSX.EventHandler<HTMLLIElement, PointerEvent> = (e) => {
const tag = e.target.getAttribute("data-tag");
if (!tag) return;
/**
* Delete tag from selected tags if already selected
* and update param in url
@ -68,10 +70,10 @@ const TagsList: Component<TagsListProps> = (props) => {
const tags: string[] = [];
for (const tag of props.data) {
const isTagExist = param.includes(tag.toLocaleLowerCase());
const isTagExist = param.includes(tag);
if (isTagExist) {
tags.push(tag.toLocaleLowerCase());
tags.push(tag);
}
}
@ -83,12 +85,7 @@ const TagsList: Component<TagsListProps> = (props) => {
<For
each={props.data}
children={(tag) => (
<li
onClick={onSelect}
class="btn-link"
data-tag={tag.toLocaleLowerCase()}
data-active={isTagSelected(tag)}
>
<li onClick={onSelect} class="btn-link" data-tag={tag} data-active={isTagSelected(tag)}>
{tag}
</li>
)}

View File

@ -5,8 +5,6 @@ import * as utils from "@utils/index";
import PageLayout from "@layouts/default.astro";
import PageHeading from "@components/PageHeading.astro";
import ArticlesList from "@root/components/ArticlesList";
import SearchInput from "@components/SearchInput";
import TagsList from "@components/TagsList";
const t = useTranslation(Astro);
const pageLang = getLanguageFromURL(Astro.url.pathname);
@ -24,12 +22,12 @@ const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
>
<PageHeading heading={t("pageTitle.articles")} description={t("pageDescription.articles")} />
<section>
<SearchInput i18n={{ "input.search": t("input.search") }} client:load />
<TagsList data={tags} client:load />
<ArticlesList
client:load
i18n={{ "articles.empty": t("articles.empty") }}
data={{ articles: utils.formatToArticleBlocks(filtered), pageLang }}
i18n={{ "articles.empty": t("articles.empty"), "input.search": t("input.search") }}
articles={utils.formatToArticleBlocks(filtered)}
tags={tags}
pageLang={pageLang}
/>
</section>
</PageLayout>

View File

@ -49,7 +49,12 @@ export function formatToArticleBlocks(articles: CollectionEntry<"blog">[]) {
articles.forEach((a) => {
const year = a.data.pubDate.getFullYear();
if (!blocks[year]) blocks[year] = [];
blocks[year].push({ id: a.id, title: a.data.title, pubDate: a.data.pubDate });
blocks[year].push({
id: a.id,
title: a.data.title,
tags: a.data.tags,
pubDate: a.data.pubDate,
});
});
return blocks;
}