import { Badge, createDisclosure, HStack, Icon, IconButton, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay, Text, VStack, hope, } from "@hope-ui/solid" import { BsSearch } from "solid-icons/bs" import { createSignal, For, Match, onCleanup, onMount, Show, Switch, } from "solid-js" import Mark from "mark.js" import { FullLoading, LinkWithBase, Paginator, SelectWrapper, } from "~/components" import { useFetch, usePath, useRouter, useT } from "~/hooks" import { getMainColor, me, password } from "~/store" import { SearchNode } from "~/types" import { bus, encodePath, fsSearch, getFileSize, handleResp, hoverColor, pathJoin, } from "~/utils" import { isMac } from "~/utils/compatibility" import { getIconByObj } from "~/utils/icon" // class MarkKeywords { // root // rootHTML // style // /** // * Keyword highlight // * @param root Find root elements // * @param style The default background is highly bright yellow // */ // constructor(config: { root: Element | null; style?: string }) { // if (!config.root) return // this.root = config.root // this.rootHTML = config.root.innerHTML // this.style = config.style ?? "background-color: #FF0" // } // /** mark keyword */ // light(keyword: string) { // const handler = (root: any) => { // if ( // root.nodeName === "#text" && // root.parentNode.childNodes.length === 1 // ) { // root.parentNode.innerHTML = root.parentNode.innerHTML.replace( // new RegExp(keyword, "g"), // `${keyword}`, // ) // } else { // for (const node of root.childNodes) { // handler(node) // } // } // } // if (!this.root) return // // reset HTML // this.root.innerHTML = this.rootHTML! // if (!keyword) return // handler(this.root) // } // } function NodeName(props: { keywords: string; name: string }) { let ref: HTMLSpanElement onMount(() => { // const highlighter = new MarkKeywords({ // root: ref!, // style: `background-color: var(--hope-colors-info5); border-radius: var(--hope-radii-md); margin: 0 1px; padding: 0 1px;`, // }) // highlighter.light(props.keywords) const mark = new Mark(ref!) mark.mark(props.keywords, { separateWordSearch: true, diacritics: true, }) }) return ( {props.name} ) } const SearchResult = (props: { node: SearchNode; keywords: string }) => { const { setPathAs } = usePath() return ( { setPathAs(props.node.path, props.node.is_dir) }} > 0 || !props.node.is_dir}> {getFileSize(props.node.size)} {props.node.parent} ) } const Search = () => { const pageSize = 100 const { isOpen, onOpen, onClose, onToggle } = createDisclosure() const t = useT() const handler = (name: string) => { if (name === "search") { onOpen() } } bus.on("tool", handler) const searchEvent = (e: KeyboardEvent) => { if ((e.ctrlKey || (isMac && e.metaKey)) && e.key.toLowerCase() === "k") { e.preventDefault() onToggle() } } document.addEventListener("keydown", searchEvent) onCleanup(() => { bus.off("tool", handler) document.removeEventListener("keydown", searchEvent) }) const [keywords, setKeywords] = createSignal("") const { pathname } = useRouter() const [loading, searchReq] = useFetch(fsSearch) const [data, setData] = createSignal({ content: [] as SearchNode[], total: 0, }) const [scope, setScope] = createSignal(0) const scopes = ["all", "folder", "file"] let resetPaginator: () => void const search = async (page = 1) => { page === 1 && resetPaginator?.() if (loading()) return setData({ content: [], total: 0, }) const resp = await searchReq( pathname(), keywords(), password(), scope(), page, pageSize, ) handleResp(resp, (data) => { const content = data.content if (!content) { return } content.forEach((node) => { if (me().base_path && node.parent.startsWith(me().base_path)) { const pattern = new RegExp("^" + me().base_path) node.parent = node.parent.replace(pattern, "") || "/" if (!node.parent.startsWith("/")) { node.parent = "/" + node.parent } } node.path = encodePath(pathJoin(node.parent, node.name)) }) setData(data) }) } return ( {t("home.search.search")} setScope(v)} options={scopes.map((v, i) => ({ value: i, label: t(`home.search.scopes.${v}`), }))} /> { setKeywords(e.currentTarget.value) }} onKeyDown={(e) => { if (e.key === "Enter" && keywords().length !== 0) { search() } }} /> } onClick={() => search()} loading={loading()} disabled={keywords().length === 0} /> {t("home.search.no_result")} {(item) => } { search(page) }} setResetCallback={(reset) => (resetPaginator = reset)} /> ) } export { Search }