]> Untitled Git - lemmy-ui.git/blob - src/shared/components/common/language-select.tsx
fix: Add triangle alert icon to language warning
[lemmy-ui.git] / src / shared / components / common / language-select.tsx
1 import { selectableLanguages } from "@utils/app";
2 import { randomStr } from "@utils/helpers";
3 import classNames from "classnames";
4 import { Component, linkEvent } from "inferno";
5 import { Language } from "lemmy-js-client";
6 import { I18NextService, UserService } from "../../services";
7 import { Icon } from "./icon";
8
9 interface LanguageSelectProps {
10   allLanguages: Language[];
11   siteLanguages: number[];
12   selectedLanguageIds?: number[];
13   multiple?: boolean;
14   onChange(val: number[]): any;
15   showAll?: boolean;
16   showSite?: boolean;
17   iconVersion?: boolean;
18   disabled?: boolean;
19   showLanguageWarning?: boolean;
20 }
21
22 export class LanguageSelect extends Component<LanguageSelectProps, any> {
23   private id = `language-select-${randomStr()}`;
24
25   constructor(props: any, context: any) {
26     super(props, context);
27   }
28
29   componentDidMount() {
30     this.setSelectedValues();
31   }
32
33   // Necessary because there is no HTML way to set selected for multiple in value=
34   setSelectedValues() {
35     const ids = this.props.selectedLanguageIds?.map(toString);
36     if (ids) {
37       const select = (document.getElementById(this.id) as HTMLSelectElement)
38         .options;
39       for (let i = 0; i < select.length; i++) {
40         const o = select[i];
41         if (ids.includes(o.value)) {
42           o.selected = true;
43         }
44       }
45     }
46   }
47
48   render() {
49     return this.props.iconVersion ? (
50       this.selectBtn
51     ) : (
52       <div className="language-select mb-3">
53         <label
54           className={classNames(
55             "col-form-label",
56             `col-sm-${this.props.multiple ? 3 : 2}`
57           )}
58           htmlFor={this.id}
59         >
60           {I18NextService.i18n.t(
61             this.props.multiple ? "language_plural" : "language"
62           )}
63         </label>
64         {this.props.multiple && this.props.showLanguageWarning && (
65           <div
66             id="lang-warning"
67             className="alert small alert-warning"
68             role="alert"
69           >
70             <Icon icon="alert-triangle" classes="icon-inline me-2" />
71             {I18NextService.i18n.t("undetermined_language_warning")}
72           </div>
73         )}
74         <div
75           className={classNames(`col-sm-${this.props.multiple ? 9 : 10}`, {
76             "input-group": this.props.multiple,
77           })}
78         >
79           {this.selectBtn}
80           {this.props.multiple && (
81             <button
82               className="btn btn-outline-secondary"
83               onClick={linkEvent(this, this.handleDeselectAll)}
84             >
85               <Icon icon="x" />
86             </button>
87           )}
88         </div>
89       </div>
90     );
91   }
92
93   get selectBtn() {
94     const selectedLangs = this.props.selectedLanguageIds;
95     const filteredLangs = selectableLanguages(
96       this.props.allLanguages,
97       this.props.siteLanguages,
98       this.props.showAll,
99       this.props.showSite,
100       UserService.Instance.myUserInfo
101     );
102
103     return (
104       <select
105         className={classNames("form-select w-auto", {
106           "d-inline-block": !this.props.iconVersion,
107         })}
108         id={this.id}
109         onChange={linkEvent(this, this.handleLanguageChange)}
110         aria-label={I18NextService.i18n.t("language_select_placeholder")}
111         aria-describedby={
112           this.props.multiple && this.props.showLanguageWarning
113             ? "lang-warning"
114             : ""
115         }
116         multiple={this.props.multiple}
117         disabled={this.props.disabled}
118       >
119         {!this.props.multiple && (
120           <option selected disabled hidden>
121             {I18NextService.i18n.t("language_select_placeholder")}
122           </option>
123         )}
124         {filteredLangs.map(l => (
125           <option
126             key={l.id}
127             value={l.id}
128             selected={selectedLangs?.includes(l.id)}
129           >
130             {l.name}
131           </option>
132         ))}
133       </select>
134     );
135   }
136
137   handleLanguageChange(i: LanguageSelect, event: any) {
138     const options: HTMLOptionElement[] = Array.from(event.target.options);
139     const selected: number[] = options
140       .filter(o => o.selected)
141       .map(o => Number(o.value));
142
143     i.props.onChange(selected);
144   }
145
146   handleDeselectAll(i: LanguageSelect, event: any) {
147     event.preventDefault();
148     i.props.onChange([]);
149   }
150 }