/* * The MIT License (MIT) * * Copyright (c) 2015 - present Instructure, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import { useState } from 'react' import { setSearchQuery, search, findNext, findPrevious, SearchQuery, closeSearchPanel } from '@codemirror/search' import { EditorView } from '@codemirror/view' import { TextInput } from '@instructure/ui-text-input/v11_6' import { IconButton } from '@instructure/ui-buttons/v11_6' import { IconArrowOpenDownLine, IconArrowOpenUpLine, IconSearchLine } from '@instructure/ui-icons' import { createRoot } from 'react-dom/client' export type SearchConfig = { placeholder: string nextResultLabel: string prevResultLabel: string } function SearchPanel({ view, searchConfig }: { view: EditorView searchConfig: SearchConfig }) { const [searchQueryStr, setSearchQueryStr] = useState('') const handleChange = ( _e: React.ChangeEvent, value: string ) => { setSearchQueryStr(value) handleHighlightSearch(value) } const handleHighlightSearch = (searchStr: string) => { view.dispatch({ effects: setSearchQuery.of( new SearchQuery({ search: searchStr }) ) }) } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key !== 'Enter') return if (!e.shiftKey) handleFindNext() else handleFindPrev() } const handleKeyUp = (e: React.KeyboardEvent) => { if (e.key !== 'Escape') return closeSearchPanel(view) } const handleFindNext = () => { handleHighlightSearch(searchQueryStr) findNext(view) } const handleFindPrev = () => { handleHighlightSearch(searchQueryStr) findPrevious(view) } return ( { setTimeout(() => r?.focus(), 0) }} size="small" display="inline-block" width="20rem" placeholder={searchConfig.placeholder} onChange={handleChange} onKeyDown={handleKeyDown} onKeyUp={handleKeyUp} renderBeforeInput={} renderAfterInput={ } /> ) } export default function customSearch(searchConfig: SearchConfig | undefined) { return searchConfig ? search({ createPanel: (view) => { const dom = document.createElement('div') dom.style.padding = '8px' const root = createRoot(dom) root.render() return { dom } } }) : [] }