import { timed } from "@thi.ng/bench/timed" ; import { div } from "@thi.ng/hiccup-html/blocks" ; import { inputText } from "@thi.ng/hiccup-html/forms" ; import { anchor } from "@thi.ng/hiccup-html/inline" ; import type { IComponent } from "@thi.ng/rdom" ; import { $compile } from "@thi.ng/rdom/compile" ; import { Component } from "@thi.ng/rdom/component" ; import { $text } from "@thi.ng/rdom/dom" ; import { $list } from "@thi.ng/rdom/list" ; import type { ISubscription } from "@thi.ng/rstream" ; import { debounce } from "@thi.ng/rstream/debounce" ; import { reactive , Stream } from "@thi.ng/rstream/stream" ; import { map } from "@thi.ng/transducers/map" ; import msgpack from "@ygoe/msgpack" ; import { pageControls , Pagination } from "./pagination" ; import { search , type , SearchIndex } from "./search" ; const INDEX_URL = "https://docs.thi.ng/umbrella/search-index-latest.bin" ; const REPO_URL = "https://github.com/thi-ng/umbrella/" ; const BASE_URL = `${ REPO_URL }blob/develop/packages/` ; const SRC_URL = `${ REPO_URL }/tree/develop/examples/rdom-search-docs` ; const INITIAL_QUERY = "hdom" ; const PAGE_SIZE = 25 ; class DocSearch extends Component { wrapper ! : IComponent ; inner ! : IComponent ; pager ! : Pagination < string [ ] [ ] > ; query ! : Stream < string > ; queryResults ! : ISubscription < string , string [ ] [ ] > ; updateQuery ( e ) { const term = ( < HTMLInputElement > e . target ) . value ; if ( term . length > 0 ) { this . query . next ( term . toLowerCase ( ) ) ; this . pager . setPage ( 0 ) ; } } async mount ( parent ) { this . wrapper = $compile ( div ( { class : "ma2 measure-ns center-ns f7 f6-ns" } , [ "h1.mv0" , { } , "thi.ng/umbrella doc search" ] , div ( { class : "mb2 f7" } , anchor ( { class : "link blue" , href : SRC_URL } , "Source code" ) ) ) ) ; this . el = await this . wrapper . mount ( parent ) ; const loader = this . $el ( "div.ma2.measure-ns.center-ns.f7.f6-ns" , { } , "Loading search index..." , this . el ) ; try { const resp = await fetch ( INDEX_URL ) ; if ( resp . status >= 400 ) throw new Error ( "Failed to load search index" ) ; const buf = await resp . arrayBuffer ( ) ; const index = timed ( ( ) => msgpack . deserialize ( buf ) ) ; this . $remove ( loader ) ; this . query = reactive ( INITIAL_QUERY ) ; this . queryResults = this . query . subscribe ( debounce ( 100 ) ) . transform ( map ( ( q ) => search ( index , q , "" ) || [ ] ) ) ; this . pager = new Pagination ( this . queryResults , PAGE_SIZE ) ; this . inner = $compile ( div ( null , inputText ( { class : "w-100 mv2 pa2" , type : "text" , autofocus : true , oninput : this . updateQuery . bind ( this ) , value : this . query , } ) , div ( { class : "mv2" } , this . queryResults . transform ( map ( ( results ) => results . length ) ) , " results" , div ( null , `(total: ${ index . numVals }, keys: ${ index . numKeys } in ${ index . packages . length } packages, ${ index . numFiles } files)` ) ) , pageControls ( this . pager ) , $list ( this . pager . resultPage , "ul.list.pl0" , { } , ( [ suffix , file ] ) => [ "li" , { } , [ "span" , { } , [ "span.b.bg-washed-green" , { } , this . query . deref ( ) , ] , `${ suffix }: ` , ] , anchor ( { class : "link blue" , href : BASE_URL + file , target : "_new" , } , file ) , ] ) ) ) ; this . inner . mount ( this . el ) ; } catch ( e ) { $text ( < HTMLElement > loader , e ) ; } return this . el ; } async unmount ( ) { this . inner . unmount ( ) ; this . wrapper . unmount ( ) ; this . pager . release ( ) ; } } new DocSearch ( ) . mount ( document . getElementById ( "app" ) ! ) ;