Added type selection to icon and emoji search

This commit is contained in:
squidfunk
2024-05-03 18:50:37 +02:00
parent b0c5fe6aef
commit aef6175f04
12 changed files with 112 additions and 40 deletions

View File

@@ -33,6 +33,7 @@ export type ComponentType =
| "iconsearch" /* Icon search */
| "iconsearch-query" /* Icon search input */
| "iconsearch-result" /* Icon search results */
| "iconsearch-select" /* Icon search select */
| "sponsorship" /* Sponsorship */
| "sponsorship-count" /* Sponsorship count */
| "sponsorship-total" /* Sponsorship total */
@@ -62,6 +63,7 @@ interface ComponentTypeMap {
"iconsearch": HTMLElement /* Icon search */
"iconsearch-query": HTMLInputElement /* Icon search input */
"iconsearch-result": HTMLElement /* Icon search results */
"iconsearch-select": HTMLSelectElement
"sponsorship": HTMLElement /* Sponsorship */
"sponsorship-count": HTMLElement /* Sponsorship count */
"sponsorship-total": HTMLElement /* Sponsorship total */

View File

@@ -20,12 +20,16 @@
* IN THE SOFTWARE.
*/
import { Observable, merge } from "rxjs"
import { BehaviorSubject, Observable, fromEvent, map, merge } from "rxjs"
import { configuration } from "~/_"
import { requestJSON } from "~/browser"
import { Component, getComponentElement } from "../../_"
import {
Component,
getComponentElement,
getComponentElements
} from "../../_"
import {
IconSearchQuery,
mountIconSearchQuery
@@ -64,6 +68,14 @@ export type IconSearch =
| IconSearchQuery
| IconSearchResult
/**
* Icon search mode
*/
export type IconSearchMode =
| "all"
| "icons"
| "emojis"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
@@ -87,8 +99,18 @@ export function mountIconSearch(
const query = getComponentElement("iconsearch-query", el)
const result = getComponentElement("iconsearch-result", el)
/* Retrieve select component */
const mode$ = new BehaviorSubject<IconSearchMode>("all")
const selects = getComponentElements("iconsearch-select", el)
for (const select of selects) {
fromEvent(select, "change").pipe(
map(ev => (ev.target as HTMLSelectElement).value as IconSearchMode)
)
.subscribe(mode$)
}
/* Create and return component */
const query$ = mountIconSearchQuery(query)
const result$ = mountIconSearchResult(result, { index$, query$ })
const result$ = mountIconSearchResult(result, { index$, query$, mode$ })
return merge(query$, result$)
}

View File

@@ -26,6 +26,7 @@ import {
Subject,
bufferCount,
combineLatest,
combineLatestWith,
distinctUntilKeyChanged,
filter,
finalize,
@@ -47,7 +48,7 @@ import { round } from "~/utilities"
import { Icon, renderIconSearchResult } from "_/templates"
import { Component } from "../../_"
import { IconSearchIndex } from "../_"
import { IconSearchIndex, IconSearchMode } from "../_"
import { IconSearchQuery } from "../query"
/* ----------------------------------------------------------------------------
@@ -71,6 +72,7 @@ export interface IconSearchResult {
interface WatchOptions {
index$: Observable<IconSearchIndex> /* Search index observable */
query$: Observable<IconSearchQuery> /* Search query observable */
mode$: Observable<IconSearchMode> /* Search mode observable */
}
/**
@@ -79,6 +81,7 @@ interface WatchOptions {
interface MountOptions {
index$: Observable<IconSearchIndex> /* Search index observable */
query$: Observable<IconSearchQuery> /* Search query observable */
mode$: Observable<IconSearchMode> /* Search mode observable */
}
/* ----------------------------------------------------------------------------
@@ -94,7 +97,7 @@ interface MountOptions {
* @returns Icon search result observable
*/
export function watchIconSearchResult(
el: HTMLElement, { index$, query$ }: WatchOptions
el: HTMLElement, { index$, query$, mode$ }: WatchOptions
): Observable<IconSearchResult> {
switch (el.getAttribute("data-mdx-mode")) {
@@ -131,9 +134,14 @@ export function watchIconSearchResult(
query$.pipe(distinctUntilKeyChanged("value")),
index$
.pipe(
map(({ icons, emojis }) => [
...Object.keys(icons.data),
...Object.keys(emojis.data)
combineLatestWith(mode$),
map(([{ icons, emojis }, mode]) => [
...["all", "icons"].includes(mode)
? Object.keys(icons.data)
: [],
...["all", "emojis"].includes(mode)
? Object.keys(emojis.data)
: []
])
)
])
@@ -169,7 +177,7 @@ export function watchIconSearchResult(
* @returns Icon search result component observable
*/
export function mountIconSearchResult(
el: HTMLElement, { index$, query$ }: MountOptions
el: HTMLElement, { index$, query$, mode$ }: MountOptions
): Observable<Component<IconSearchResult, HTMLElement>> {
const push$ = new Subject<IconSearchResult>()
const boundary$ = watchElementBoundary(el)
@@ -178,7 +186,7 @@ export function mountIconSearchResult(
)
/* Update search result metadata */
const meta = getElement(":scope > :first-child", el)
const meta = getElement(".mdx-iconsearch-result__meta", el)
push$
.pipe(
withLatestFrom(query$)
@@ -228,7 +236,7 @@ export function mountIconSearchResult(
))
/* Create and return component */
return watchIconSearchResult(el, { query$, index$ })
return watchIconSearchResult(el, { query$, index$, mode$ })
.pipe(
tap(state => push$.next(state)),
finalize(() => push$.complete()),

View File

@@ -90,6 +90,38 @@
right: px2rem(12px);
font-size: px2rem(12.8px);
color: var(--md-default-fg-color--lighter);
// [mobile portrait -]: Hide meta
@include break-to-device(mobile portrait) {
display: none;
}
}
// Icon search result select
&__select {
position: absolute;
top: px2rem(8px);
right: px2rem(12px);
padding-block: 0.15em;
font-size: px2rem(12.8px);
color: var(--md-default-fg-color--light);
background-color: var(--md-default-fg-color--lightest);
border: none;
border-radius: px2rem(2px);
transition: color 125ms, background-color 125ms;
// Focused or hovered
&:focus,
&:hover {
color: var(--md-accent-bg-color);
background-color: var(--md-accent-fg-color);
outline: none;
}
// Adjust spacing
+ .mdx-iconsearch-result__meta {
right: px2rem(82px);
}
}
// Icon search result list