From ab56ad1171e3fda950299582dd72d03589ee32d1 Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 31 Jan 2023 09:47:46 -0700 Subject: [PATCH] Et2Select: Handle paste of CSV into selects with allowFreeEntries Special handling for Et2SelectEmail to handle email addresses that may have a name or comma --- api/js/etemplate/Et2Select/Et2SelectEmail.ts | 33 ++++++++++++++++++ api/js/etemplate/Et2Select/SearchMixin.ts | 36 ++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/api/js/etemplate/Et2Select/Et2SelectEmail.ts b/api/js/etemplate/Et2Select/Et2SelectEmail.ts index 9129fba0a1..915adfae7d 100644 --- a/api/js/etemplate/Et2Select/Et2SelectEmail.ts +++ b/api/js/etemplate/Et2Select/Et2SelectEmail.ts @@ -242,6 +242,39 @@ export class Et2SelectEmail extends Et2Select } super.set_value(val); } + + + /** + * Sometimes users paste multiple comma separated values at once. Split them then handle normally. + * Overridden here to handle email addresses that may have commas using the regex from the validator. + * + * @param {ClipboardEvent} event + * @protected + */ + protected _handlePaste(event : ClipboardEvent) + { + event.preventDefault(); + + let paste = event.clipboardData.getData('text'); + if(!paste) + { + return; + } + const selection = window.getSelection(); + if(selection.rangeCount) + { + selection.deleteFromDocument(); + } + + // Trim line start / end anchors off validation regex, make global + let regex = new RegExp(IsEmail.EMAIL_PREG.toString().substring(2, IsEmail.EMAIL_PREG.toString().length - 3), 'g'); + let values = paste.match(regex); + values.forEach(v => + { + this.createFreeEntry(v.trim()); + }); + this.dropdown.hide(); + } } // @ts-ignore TypeScript is not recognizing that this widget is a LitElement diff --git a/api/js/etemplate/Et2Select/SearchMixin.ts b/api/js/etemplate/Et2Select/SearchMixin.ts index 02c555c036..c56abbdee1 100644 --- a/api/js/etemplate/Et2Select/SearchMixin.ts +++ b/api/js/etemplate/Et2Select/SearchMixin.ts @@ -302,6 +302,7 @@ export const Et2WithSearchMixin = >(superclass this._handleSearchChange = this._handleSearchChange.bind(this); this._handleSearchKeyDown = this._handleSearchKeyDown.bind(this); this._handleEditKeyDown = this._handleEditKeyDown.bind(this); + this._handlePaste = this._handlePaste.bind(this); } connectedCallback() @@ -594,6 +595,11 @@ export const Et2WithSearchMixin = >(superclass // Need our own change to catch the change event from search input this.addEventListener("change", this._handleChange); + if(this.allowFreeEntries) + { + this.addEventListener("paste", this._handlePaste); + } + this.updateComplete.then(() => { // Search messes up event order. Since it throws its own bubbling change event, @@ -614,6 +620,7 @@ export const Et2WithSearchMixin = >(superclass this.removeEventListener("sl-after-show", this._handleAfterShow); this.removeEventListener("sl-clear", this._handleClear) this.removeEventListener("change", this._handleChange); + this.removeEventListener("paste", this._handlePaste); this._searchInputNode?.removeEventListener("change", this._handleSearchChange); } @@ -925,6 +932,35 @@ export const Et2WithSearchMixin = >(superclass } } + /** + * Sometimes users paste multiple comma separated values at once. Split them then handle normally. + * + * @param {ClipboardEvent} event + * @protected + */ + protected _handlePaste(event : ClipboardEvent) + { + event.preventDefault(); + + let paste = event.clipboardData.getData('text'); + if(!paste) + { + return; + } + const selection = window.getSelection(); + if(selection.rangeCount) + { + selection.deleteFromDocument(); + } + let values = paste.split(/,\t/); + + values.forEach(v => + { + this.createFreeEntry(v.trim()); + }); + this.dropdown.hide(); + } + /** * Start searching *