This repository has been archived on 2020-11-02. You can view files and clone it, but cannot push or open issues or pull requests.
TripSit_Suite/node_modules/textcomplete/lib/textcomplete.js.flow
2020-11-01 22:46:04 +00:00

203 lines
4.5 KiB
Plaintext

// @flow
import Completer from "./completer"
import Editor from "./editor"
import Dropdown, { type DropdownOptions } from "./dropdown"
import Strategy, { type StrategyProperties } from "./strategy"
import SearchResult from "./search_result"
import EventEmitter from "eventemitter3"
const CALLBACK_METHODS = [
"handleChange",
"handleEnter",
"handleEsc",
"handleHit",
"handleMove",
"handleSelect",
]
/** @typedef */
type TextcompleteOptions = {
dropdown?: DropdownOptions,
}
/**
* The core of textcomplete. It acts as a mediator.
*/
export default class Textcomplete extends EventEmitter {
dropdown: Dropdown
editor: Editor
options: TextcompleteOptions
completer: Completer
isQueryInFlight: boolean
nextPendingQuery: string | null
/**
* @param {Editor} editor - Where the textcomplete works on.
*/
constructor(editor: Editor, options: TextcompleteOptions = {}) {
super()
this.completer = new Completer()
this.isQueryInFlight = false
this.nextPendingQuery = null
this.dropdown = new Dropdown(options.dropdown || {})
this.editor = editor
this.options = options
CALLBACK_METHODS.forEach(method => {
;(this: any)[method] = (this: any)[method].bind(this)
})
this.startListening()
}
/**
* @return {this}
*/
destroy(destroyEditor: boolean = true) {
this.completer.destroy()
this.dropdown.destroy()
if (destroyEditor) {
this.editor.destroy()
}
this.stopListening()
return this
}
/**
* @return {this}
*/
hide() {
this.dropdown.deactivate()
return this
}
/**
* @return {this}
* @example
* textcomplete.register([{
* match: /(^|\s)(\w+)$/,
* search: function (term, callback) {
* $.ajax({ ... })
* .done(callback)
* .fail([]);
* },
* replace: function (value) {
* return '$1' + value + ' ';
* }
* }]);
*/
register(strategyPropsArray: StrategyProperties[]) {
strategyPropsArray.forEach(props => {
this.completer.registerStrategy(new Strategy(props))
})
return this
}
/**
* Start autocompleting.
*
* @param {string} text - Head to input cursor.
* @return {this}
*/
trigger(text: string) {
if (this.isQueryInFlight) {
this.nextPendingQuery = text
} else {
this.isQueryInFlight = true
this.nextPendingQuery = null
this.completer.run(text)
}
return this
}
/** @private */
handleHit({ searchResults }: { searchResults: SearchResult[] }) {
if (searchResults.length) {
this.dropdown.render(searchResults, this.editor.getCursorOffset())
} else {
this.dropdown.deactivate()
}
this.isQueryInFlight = false
if (this.nextPendingQuery !== null) {
this.trigger(this.nextPendingQuery)
}
}
/** @private */
handleMove(e: CustomEvent) {
e.detail.code === "UP" ? this.dropdown.up(e) : this.dropdown.down(e)
}
/** @private */
handleEnter(e: CustomEvent) {
const activeItem = this.dropdown.getActiveItem()
if (activeItem) {
this.dropdown.select(activeItem)
e.preventDefault()
} else {
this.dropdown.deactivate()
}
}
/** @private */
handleEsc(e: CustomEvent) {
if (this.dropdown.shown) {
this.dropdown.deactivate()
e.preventDefault()
}
}
/** @private */
handleChange(e: CustomEvent) {
if (e.detail.beforeCursor != null) {
this.trigger(e.detail.beforeCursor)
} else {
this.dropdown.deactivate()
}
}
/** @private */
handleSelect(selectEvent: CustomEvent) {
this.emit("select", selectEvent)
if (!selectEvent.defaultPrevented) {
this.editor.applySearchResult(selectEvent.detail.searchResult)
}
}
/** @private */
startListening() {
this.editor
.on("move", this.handleMove)
.on("enter", this.handleEnter)
.on("esc", this.handleEsc)
.on("change", this.handleChange)
this.dropdown.on("select", this.handleSelect)
;[
"show",
"shown",
"render",
"rendered",
"selected",
"hidden",
"hide",
].forEach(eventName => {
this.dropdown.on(eventName, () => this.emit(eventName))
})
this.completer.on("hit", this.handleHit)
}
/** @private */
stopListening() {
this.completer.removeAllListeners()
this.dropdown.removeAllListeners()
this.editor
.removeListener("move", this.handleMove)
.removeListener("enter", this.handleEnter)
.removeListener("esc", this.handleEsc)
.removeListener("change", this.handleChange)
}
}