import { state, property } from "lit/decorators.js"; import { msg, localized, str } from "@lit/localize"; import debounce from "lodash/fp/debounce"; import LiteElement, { html } from "../utils/LiteElement"; import { regexEscape } from "../utils/string"; export type Exclusion = { type: "text" | "regex"; value: string; }; export type ExclusionChangeEvent = CustomEvent<{ value: string; valid: boolean; }>; export type ExclusionAddEvent = CustomEvent<{ regex: string; onSuccess: () => void; }>; const MIN_LENGTH = 2; /** * Crawl queue exclusion form * * Usage example: * ```ts * * ``` * * @event on-change ExclusionChangeEvent * @event on-add ExclusionAddEvent */ @localized() export class QueueExclusionForm extends LiteElement { @property({ type: Boolean }) isSubmitting = false; @property({ type: String }) fieldErrorMessage = ""; @state() private selectValue: Exclusion["type"] = "text"; @state() private inputValue = ""; @state() private isRegexInvalid = false; async willUpdate(changedProperties: Map) { if ( changedProperties.get("selectValue") || (changedProperties.has("inputValue") && changedProperties.get("inputValue") !== undefined) ) { this.fieldErrorMessage = ""; this.checkInputValidity(); this.dispatchChangeEvent(); } } disconnectedCallback(): void { this.onInput.cancel(); super.disconnectedCallback(); } render() { return html` { this.selectValue = e.target.value; }} > ${msg("Matches Text")} ${msg("Regex")} ${this.fieldErrorMessage ? html` ${this.isRegexInvalid ? html` ${msg( html`Regular Expression syntax error: ${this.fieldErrorMessage}` )} ${msg( html`Please enter a valid constructor string pattern. See RegExp docs.` )} ` : html` ${this.fieldErrorMessage} `} ` : ""} `; } private onInput = debounce(200)((e: any) => { this.inputValue = e.target.value; }) as any; private onButtonClick() { this.handleAdd(); } private onKeyDown = (e: KeyboardEvent) => { e.stopPropagation(); if (e.key === "Enter") { this.handleAdd(); } }; private checkInputValidity(): void { let isValid = true; if (!this.inputValue || this.inputValue.length < MIN_LENGTH) { isValid = false; } else if (this.selectValue === "regex") { try { // Check if valid regex new RegExp(this.inputValue); } catch (err: any) { this.fieldErrorMessage = err.message; isValid = false; } } this.isRegexInvalid = !isValid; } private async dispatchChangeEvent() { await this.updateComplete; this.dispatchEvent( new CustomEvent("on-change", { detail: { value: this.selectValue === "text" ? regexEscape(this.inputValue) : this.inputValue, valid: !this.isRegexInvalid, }, }) as ExclusionChangeEvent ); } private async handleAdd() { this.onInput.flush(); await this.updateComplete; if (!this.inputValue) return; let regex = this.inputValue; if (this.selectValue === "text") { regex = regexEscape(this.inputValue); } this.dispatchEvent( new CustomEvent("on-add", { detail: { regex, onSuccess: () => { this.inputValue = ""; }, }, }) as ExclusionAddEvent ); } /** * Stop propgation of sl-select events. * Prevents bug where sl-dialog closes when dropdown closes * https://github.com/shoelace-style/shoelace/issues/170 */ private stopProp(e: CustomEvent) { e.stopPropagation(); } }
${msg( html`Regular Expression syntax error: ${this.fieldErrorMessage}` )}
${this.fieldErrorMessage}
${msg( html`Please enter a valid constructor string pattern. See RegExp docs.` )}
RegExp