Added search component
This commit is contained in:
parent
b64aa7eaa7
commit
e80a9ec7e7
|
@ -71,6 +71,7 @@
|
|||
|
||||
color: #9fa1a7;
|
||||
display: flex;
|
||||
margin-bottom: 1em;
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
flex-wrap: wrap;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)}>
|
||||
|
|
|
@ -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">
|
||||
|
|
22
src/components/_shared/SearchField.module.scss
Normal file
22
src/components/_shared/SearchField.module.scss
Normal 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;
|
||||
}
|
||||
}
|
26
src/components/_shared/SearchField.tsx
Normal file
26
src/components/_shared/SearchField.tsx
Normal 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>
|
||||
)
|
||||
}
|
|
@ -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%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
.groupControls {
|
||||
align-items: baseline;
|
||||
margin-bottom: 4rem;
|
||||
margin-top: 7rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.floorImportant {
|
||||
|
|
|
@ -523,6 +523,7 @@ figcaption {
|
|||
}
|
||||
|
||||
.view-switcher__search {
|
||||
flex: 1 100%;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user