]> Untitled Git - lemmy-ui.git/blobdiff - src/shared/components/common/searchable-select.tsx
Merge remote-tracking branch 'lemmy/main' into fix/fix-long-words-in-titles-overflow
[lemmy-ui.git] / src / shared / components / common / searchable-select.tsx
index a5a75f2328507927fffc91725affc808f80efbba..ef59a8c247c286a5616b87e215c76793a97ce210 100644 (file)
@@ -1,3 +1,4 @@
+import { Choice } from "@utils/types";
 import classNames from "classnames";
 import {
   ChangeEvent,
@@ -6,8 +7,7 @@ import {
   linkEvent,
   RefObject,
 } from "inferno";
-import { i18n } from "../../i18next";
-import { Choice } from "../../utils";
+import { I18NextService } from "../../services";
 import { Icon, Spinner } from "./icon";
 
 interface SearchableSelectProps {
@@ -38,12 +38,38 @@ function handleSearch(i: SearchableSelect, e: ChangeEvent<HTMLInputElement>) {
   });
 }
 
+function focusSearch(i: SearchableSelect) {
+  if (i.toggleButtonRef.current?.ariaExpanded !== "true") {
+    i.searchInputRef.current?.focus();
+
+    if (i.props.onSearch) {
+      i.props.onSearch("");
+    }
+
+    i.setState({
+      searchText: "",
+    });
+  }
+}
+
+function handleChange({ option, i }: { option: Choice; i: SearchableSelect }) {
+  const { onChange, value } = i.props;
+
+  if (option.value !== value?.toString()) {
+    if (onChange) {
+      onChange(option);
+    }
+
+    i.setState({ searchText: "" });
+  }
+}
+
 export class SearchableSelect extends Component<
   SearchableSelectProps,
   SearchableSelectState
 > {
-  private searchInputRef: RefObject<HTMLInputElement> = createRef();
-  private toggleButtonRef: RefObject<HTMLButtonElement> = createRef();
+  searchInputRef: RefObject<HTMLInputElement> = createRef();
+  toggleButtonRef: RefObject<HTMLButtonElement> = createRef();
   private loadingEllipsesInterval?: NodeJS.Timer = undefined;
 
   state: SearchableSelectState = {
@@ -55,9 +81,6 @@ export class SearchableSelect extends Component<
   constructor(props: SearchableSelectProps, context: any) {
     super(props, context);
 
-    this.handleChange = this.handleChange.bind(this);
-    this.focusSearch = this.focusSearch.bind(this);
-
     if (props.value) {
       let selectedIndex = props.options.findIndex(
         ({ value }) => value === props.value?.toString()
@@ -79,35 +102,37 @@ export class SearchableSelect extends Component<
     const { searchText, selectedIndex, loadingEllipses } = this.state;
 
     return (
-      <div className="dropdown">
+      <div className="searchable-select dropdown col-12 col-sm-auto flex-grow-1">
         <button
           id={id}
           type="button"
-          className="custom-select text-start"
+          role="combobox"
+          className="form-select d-inline-block text-start"
           aria-haspopup="listbox"
+          aria-controls="searchable-select-input"
+          aria-activedescendant={options[selectedIndex].label}
+          aria-expanded={false}
           data-bs-toggle="dropdown"
-          onClick={this.focusSearch}
+          onClick={linkEvent(this, focusSearch)}
+          ref={this.toggleButtonRef}
         >
           {loading
-            ? `${i18n.t("loading")}${loadingEllipses}`
+            ? `${I18NextService.i18n.t("loading")}${loadingEllipses}`
             : options[selectedIndex].label}
         </button>
-        <div
-          role="combobox"
-          aria-activedescendant={options[selectedIndex].label}
-          className="modlog-choices-font-size dropdown-menu w-100 p-2"
-        >
+        <div className="modlog-choices-font-size dropdown-menu w-100 p-2">
           <div className="input-group">
             <span className="input-group-text">
               {loading ? <Spinner /> : <Icon icon="search" />}
             </span>
             <input
               type="text"
+              id="searchable-select-input"
               className="form-control"
               ref={this.searchInputRef}
               onInput={linkEvent(this, handleSearch)}
               value={searchText}
-              placeholder={`${i18n.t("search")}...`}
+              placeholder={`${I18NextService.i18n.t("search")}...`}
             />
           </div>
           {!loading &&
@@ -127,7 +152,7 @@ export class SearchableSelect extends Component<
                 aria-disabled={option.disabled}
                 disabled={option.disabled}
                 aria-selected={selectedIndex === index}
-                onClick={() => this.handleChange(option)}
+                onClick={linkEvent({ i: this, option }, handleChange)}
                 type="button"
               >
                 {option.label}
@@ -138,20 +163,6 @@ export class SearchableSelect extends Component<
     );
   }
 
-  focusSearch() {
-    if (this.toggleButtonRef.current?.ariaExpanded !== "true") {
-      this.searchInputRef.current?.focus();
-
-      if (this.props.onSearch) {
-        this.props.onSearch("");
-      }
-
-      this.setState({
-        searchText: "",
-      });
-    }
-  }
-
   static getDerivedStateFromProps({
     value,
     options,
@@ -189,16 +200,4 @@ export class SearchableSelect extends Component<
       clearInterval(this.loadingEllipsesInterval);
     }
   }
-
-  handleChange(option: Choice) {
-    const { onChange, value } = this.props;
-
-    if (option.value !== value?.toString()) {
-      if (onChange) {
-        onChange(option);
-      }
-
-      this.setState({ searchText: "" });
-    }
-  }
 }