Added search component

This commit is contained in:
kvakazyambra 2022-11-18 21:33:31 +03:00
parent b64aa7eaa7
commit e80a9ec7e7
9 changed files with 131 additions and 22 deletions

View File

@ -71,6 +71,7 @@
color: #9fa1a7;
display: flex;
margin-bottom: 1em;
@include media-breakpoint-down(md) {
flex-wrap: wrap;

View File

@ -1,7 +1,7 @@
.topicHeader {
@include font-size(1.7rem);
padding-top: 2.8rem;
padding: 2.8rem $container-padding-x 0;
text-align: center;
h1 {
@ -24,7 +24,8 @@
color: #fff;
cursor: pointer;
font-size: 100%;
margin: 0 1.2rem;
margin: 0 1.2rem 1em;
padding: 0.8rem 1.6rem;
white-space: nowrap;
}
}

View File

@ -18,7 +18,7 @@ export const FullTopic = (props: Props) => {
return (
<div class={styles.topicFull}>
<Show when={!!props.topic?.slug}>
<div class={clsx(styles.topicHeader, 'col-md-8 offset-md-2')}>
<div class={clsx(styles.topicHeader, 'col-md-8 col-lg-6 offset-md-2 offset-lg-3')}>
<h1>#{props.topic.title}</h1>
<p>{props.topic.body}</p>
<div class={clsx(styles.topicActions)}>

View File

@ -1,6 +1,5 @@
import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
import type { Topic } from '../../graphql/types.gen'
import { Icon } from '../_shared/Icon'
import { t } from '../../utils/intl'
import { setTopicsSort, useTopicsStore } from '../../stores/zine/topics'
import { useRouter } from '../../stores/router'
@ -10,6 +9,7 @@ import { useSession } from '../../context/session'
import { locale } from '../../stores/ui'
import { translit } from '../../utils/ru2en'
import styles from '../../styles/AllTopics.module.scss'
import { SearchField } from '../_shared/SearchField'
type AllTopicsPageSearchParams = {
by: 'shouts' | 'authors' | 'title' | ''
@ -24,6 +24,42 @@ const PAGE_SIZE = 20
export const AllTopicsView = (props: AllTopicsViewProps) => {
const { searchParams, changeSearchParam } = useRouter<AllTopicsPageSearchParams>()
const [limit, setLimit] = createSignal(PAGE_SIZE)
const ALPHABET = [
'#',
'А',
'Б',
'В',
'Г',
'Д',
'Е',
'Ё',
'Ж',
'З',
'И',
'Й',
'К',
'Л',
'М',
'Н',
'О',
'П',
'Р',
'С',
'Т',
'У',
'Ф',
'Х',
'Ц',
'Ч',
'Ш',
'Щ',
'Ъ',
'Ы',
'Ь',
'Э',
'Ю',
'Я'
]
const { sortedTopics } = useTopicsStore({
topics: props.topics,
@ -56,12 +92,11 @@ export const AllTopicsView = (props: AllTopicsViewProps) => {
const subscribed = (s) => Boolean(session()?.news?.topics && session()?.news?.topics?.includes(s || ''))
const showMore = () => setLimit((oldLimit) => oldLimit + PAGE_SIZE)
let searchEl: HTMLInputElement
const [searchResults, setSearchResults] = createSignal<Topic[]>([])
// eslint-disable-next-line sonarjs/cognitive-complexity
const searchTopics = () => {
const searchTopics = (value) => {
/* very stupid search algorithm with no deps */
let q = searchEl.value.toLowerCase()
let q = value.toLowerCase()
if (q.length > 0) {
console.debug(q)
setSearchResults([])
@ -106,16 +141,8 @@ export const AllTopicsView = (props: AllTopicsViewProps) => {
<li classList={{ selected: searchParams().by === 'title' }}>
<a href="/topics?by=title">{t('By alphabet')}</a>
</li>
<li class="search-switcher">
<Icon name="search" />
<input
class="search-input"
ref={searchEl}
onChange={searchTopics}
onInput={searchTopics}
onFocus={() => (searchEl.innerHTML = '')}
placeholder={t('Search')}
/>
<li class="view-switcher__search">
<SearchField onChange={searchTopics} />
</li>
</ul>
</div>
@ -123,15 +150,32 @@ export const AllTopicsView = (props: AllTopicsViewProps) => {
)
return (
<div class={clsx(styles.allTopicsPage, 'container')}>
<AllTopicsHead />
<div class="shift-content">
<AllTopicsHead />
<Show when={sortedTopics().length > 0 || searchResults().length > 0}>
<Show when={searchParams().by === 'title'}>
<div class="row">
<div class="col-lg-10 col-xl-9">
<ul class={clsx('nodash', styles.alphabet)}>
<For each={ALPHABET}>
{(letter, index) => (
<li>
<Show when={sortedKeys().includes(letter)}>
<a href={`#letter-${index()}`}>{letter}</a>
</Show>
<Show when={!sortedKeys().includes(letter)}>{letter}</Show>
</li>
)}
</For>
</ul>
</div>
</div>
<For each={sortedKeys()}>
{(letter) => (
{(letter, index) => (
<div class={clsx(styles.group, 'group')}>
<h2>{letter}</h2>
<h2 id={`letter-${index()}`}>{letter}</h2>
<div class="container">
<div class="row">
<div class="col-lg-10">

View File

@ -0,0 +1,22 @@
.searchField {
display: flex;
justify-content: flex-end;
input {
border: none;
border-bottom: 1px solid #ccc;
font-family: inherit;
font-size: inherit;
width: 100%;
}
label {
@include media-breakpoint-up(md) {
flex: 1 60%;
}
}
.icon {
width: 2rem;
}
}

View File

@ -0,0 +1,26 @@
import styles from './SearchField.module.scss'
import { Icon } from './Icon'
import { t } from '../../utils/intl'
type SearchFieldProps = {
onChange: (value: string) => void
}
export const SearchField = (props: SearchFieldProps) => {
const handleInputChange = (event) => props.onChange(event.target.value)
return (
<div class={styles.searchField}>
<label for="search-field">
<Icon name="search" class={styles.icon} />
</label>
<input
id="search-field"
type="text"
class="search-input"
onInput={handleInputChange}
placeholder={t('Search')}
/>
</div>
)
}

View File

@ -52,3 +52,17 @@
}
}
}
.alphabet {
display: flex;
flex-wrap: wrap;
font-weight: 700;
margin: 1.5em -3% 0 0;
@include font-size(1.5rem);
li {
min-width: 1.5em;
margin-right: 3%;
}
}

View File

@ -2,7 +2,7 @@
.groupControls {
align-items: baseline;
margin-bottom: 4rem;
margin-top: 7rem;
margin-top: 0;
}
.floorImportant {

View File

@ -523,6 +523,7 @@ figcaption {
}
.view-switcher__search {
flex: 1 100%;
text-align: right;
white-space: nowrap;