import { filter as search } from "fuzzaldrin-plus" ; import { Observable , Subject , bufferCount , combineLatest , distinctUntilKeyChanged , filter , finalize , map , merge , of , switchMap , tap , withLatestFrom , zipWith } from "rxjs" ; import { getElement , watchElementBoundary } from "~/browser" ; import { round } from "~/utilities" ; import { Icon , renderIconSearchResult } from "_/templates" ; import { Component } from "../../_" ; import { IconSearchIndex } from "../_" ; import { IconSearchQuery } from "../query" ; /**
 * Icon search result
 */ export interface IconSearchResult { data : Icon [ ] ; } /**
 * Watch options
 */ interface WatchOptions { index$ : Observable < IconSearchIndex > ; query$ : Observable < IconSearchQuery > ; } /**
 * Mount options
 */ interface MountOptions { index$ : Observable < IconSearchIndex > ; query$ : Observable < IconSearchQuery > ; } param el param options returns export function watchIconSearchResult ( el , { index$ , query$ } ) { switch ( el . getAttribute ( "data-mdx-mode" ) ) { case "file" : return combineLatest ( [ query$ . pipe ( distinctUntilKeyChanged ( "value" ) ) , index$ . pipe ( map ( ( { icons } ) => Object . values ( icons . data ) . map ( icon => icon . replace ( /\.svg$/ , "" ) ) ) ) ] ) . pipe ( map ( ( [ { value } , data ] ) => search ( data , value ) ) , switchMap ( files => index$ . pipe ( map ( ( { icons } ) => ( { data : files . map < Icon > ( shortcode => { return { shortcode , url : [ icons . base , shortcode , ".svg" ] . join ( "" ) } ; } ) } ) ) ) ) ) ; default : return combineLatest ( [ query$ . pipe ( distinctUntilKeyChanged ( "value" ) ) , index$ . pipe ( map ( ( { icons , emojis } ) => [ ... Object . keys ( icons . data ) , ... Object . keys ( emojis . data ) ] ) ) ] ) . pipe ( map ( ( [ { value } , data ] ) => search ( data , value ) ) , switchMap ( shortcodes => index$ . pipe ( map ( ( { icons , emojis } ) => ( { data : shortcodes . map < Icon > ( shortcode => { const category = shortcode in icons . data ? icons : emojis ; return { shortcode , url : [ category . base , category . data [ shortcode ] ] . join ( "" ) } ; } ) } ) ) ) ) ) ; } } param el param options returns export function mountIconSearchResult ( el , { index$ , query$ } ) { const push$ = new Subject < IconSearchResult > ( ) ; const boundary$ = watchElementBoundary ( el ) . pipe ( filter ( Boolean ) ) ; const meta = getElement ( ":scope > :first-child" , el ) ; push$ . pipe ( withLatestFrom ( query$ ) ) . subscribe ( ( [ { data } , { value } ] ) => { if ( value ) { switch ( data . length ) { case 0 : meta . textContent = "No matches" ; break ; case 1 : meta . textContent = "1 match" ; break ; default : meta . textContent = `${ round ( data . length ) } matches` ; } } else { meta . textContent = "Type to start searching" ; } } ) ; const file = el . getAttribute ( "data-mdx-mode" ) === "file" ; const list = getElement ( ":scope > :last-child" , el ) ; push$ . pipe ( tap ( ( ) => list . innerHTML = "" ) , switchMap ( ( { data } ) => merge ( of ( ... data . slice ( 0 , 10 ) ) , of ( ... data . slice ( 10 ) ) . pipe ( bufferCount ( 10 ) , zipWith ( boundary$ ) , switchMap ( ( [ chunk ] ) => of ( ... chunk ) ) ) ) ) , withLatestFrom ( query$ ) ) . subscribe ( ( [ result , { value } ] ) => list . appendChild ( renderIconSearchResult ( result , value , file ) ) ) ; return watchIconSearchResult ( el , { query$ , index$ } ) . pipe ( tap ( state => push$ . next ( state ) ) , finalize ( ( ) => push$ . complete ( ) ) , map ( state => ( { ref : el , ... state } ) ) ) ; }