summaryrefslogtreecommitdiffstats
path: root/comm/mail/components/unifiedtoolbar/content/search-bar.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/components/unifiedtoolbar/content/search-bar.mjs')
-rw-r--r--comm/mail/components/unifiedtoolbar/content/search-bar.mjs121
1 files changed, 121 insertions, 0 deletions
diff --git a/comm/mail/components/unifiedtoolbar/content/search-bar.mjs b/comm/mail/components/unifiedtoolbar/content/search-bar.mjs
new file mode 100644
index 0000000000..a450f7349f
--- /dev/null
+++ b/comm/mail/components/unifiedtoolbar/content/search-bar.mjs
@@ -0,0 +1,121 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Search input with customizable search button and placeholder.
+ * Attributes:
+ * - label: Search field label for accessibility tree.
+ * - disabled: When present, disable the search field and button.
+ * Slots in template (#searchBarTemplate):
+ * - placeholder: Content displayed as placeholder. When not provided, the value
+ * of the label attribute is shown as placeholder.
+ * - button: Content displayed on the search button.
+ *
+ * @emits search: Event when a search should be executed. detail holds the
+ * search term.
+ * @emits autocomplte: Auto complete update. detail holds the current search
+ * term.
+ */
+export class SearchBar extends HTMLElement {
+ static get observedAttributes() {
+ return ["label", "disabled"];
+ }
+
+ /**
+ * Reference to the input field in the form.
+ *
+ * @type {?HTMLInputElement}
+ */
+ #input = null;
+
+ /**
+ * Reference to the search button in the form.
+ *
+ * @type {?HTMLButtonElement}
+ */
+ #button = null;
+
+ #onSubmit = event => {
+ event.preventDefault();
+ if (!this.#input.value) {
+ return;
+ }
+
+ const searchEvent = new CustomEvent("search", {
+ detail: this.#input.value,
+ cancelable: true,
+ });
+ if (this.dispatchEvent(searchEvent)) {
+ this.reset();
+ }
+ };
+
+ #onInput = () => {
+ const autocompleteEvent = new CustomEvent("autocomplete", {
+ detail: this.#input.value,
+ });
+ this.dispatchEvent(autocompleteEvent);
+ };
+
+ connectedCallback() {
+ if (this.shadowRoot) {
+ return;
+ }
+
+ const shadowRoot = this.attachShadow({ mode: "open" });
+
+ const template = document
+ .getElementById("searchBarTemplate")
+ .content.cloneNode(true);
+ this.#input = template.querySelector("input");
+ this.#button = template.querySelector("button");
+
+ template.querySelector("form").addEventListener("submit", this.#onSubmit, {
+ passive: false,
+ });
+
+ this.#input.setAttribute("aria-label", this.getAttribute("label"));
+ template.querySelector("slot[name=placeholder]").textContent =
+ this.getAttribute("label");
+ this.#input.addEventListener("input", this.#onInput);
+
+ const styles = document.createElement("link");
+ styles.setAttribute("rel", "stylesheet");
+ styles.setAttribute(
+ "href",
+ "chrome://messenger/skin/shared/search-bar.css"
+ );
+ shadowRoot.append(styles, template);
+ }
+
+ attributeChangedCallback(attributeName, oldValue, newValue) {
+ if (!this.#input) {
+ return;
+ }
+ switch (attributeName) {
+ case "label":
+ this.#input.setAttribute("aria-label", newValue);
+ this.shadowRoot.querySelector("slot[name=placeholder]").textContent =
+ newValue;
+ break;
+ case "disabled": {
+ const isDisabled = this.hasAttribute("disabled");
+ this.#input.disabled = isDisabled;
+ this.#button.disabled = isDisabled;
+ }
+ }
+ }
+
+ focus() {
+ this.#input.focus();
+ }
+
+ /**
+ * Reset the search bar to its empty state.
+ */
+ reset() {
+ this.#input.value = "";
+ }
+}
+customElements.define("search-bar", SearchBar);