Next round of refactoring done
This commit is contained in:
parent
81a01fe9f3
commit
4a2d65f360
@ -16,6 +16,7 @@
|
||||
"BlockedIPs": "Blocked IPs",
|
||||
"Cancel": "Cancel",
|
||||
"Changes": "Changes",
|
||||
"Close": "Close",
|
||||
"Confirm": "Confirm",
|
||||
"ConfirmEmailResent": "Confirm e-mail resent",
|
||||
"ClientDomainManager": "Client Domain Manager",
|
||||
@ -68,6 +69,7 @@
|
||||
"PasswordIsRequired": "Password is required",
|
||||
"PasswordMinLength": "Password must be at least {{minPasswordLength}} characters",
|
||||
"PasswordsMustMatch": "You need to confirm by typing exactly the same as the new password",
|
||||
"PressAgainToDelete": "Press again to delete",
|
||||
"PressAgainToUnblock": "Press again to unblock",
|
||||
"ResetPassword": "Reset Password",
|
||||
"ResendConfirm": "Resend Confirm",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as React from "react";
|
||||
import React, { useState, useCallback, useEffect, useRef } from "react";
|
||||
import Option from "./option";
|
||||
|
||||
interface AutocompleteProps {
|
||||
@ -8,110 +8,99 @@ interface AutocompleteProps {
|
||||
onSelect: (item: Option) => void;
|
||||
}
|
||||
|
||||
interface AutocompleteState {
|
||||
filteredOptions: Option[];
|
||||
}
|
||||
const Autocomplete: React.FC<AutocompleteProps> = ({
|
||||
options,
|
||||
selectedOptions,
|
||||
placeholder,
|
||||
onSelect,
|
||||
}) => {
|
||||
const [filteredOptions, setFilteredOptions] = useState<Option[]>([]);
|
||||
const inputRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
export default class Autocomplete extends React.PureComponent<
|
||||
AutocompleteProps,
|
||||
AutocompleteState
|
||||
> {
|
||||
private inputRef;
|
||||
constructor(props: AutocompleteProps) {
|
||||
super(props);
|
||||
this.state = { filteredOptions: [] };
|
||||
this.inputRef = React.createRef<HTMLDivElement>();
|
||||
}
|
||||
|
||||
private filterOptions(filterTerm: string) {
|
||||
const filterOptions = useCallback(
|
||||
(filterTerm: string) => {
|
||||
if (filterTerm !== "") {
|
||||
let filtered =
|
||||
this.props.options?.filter((x) =>
|
||||
options?.filter((x) =>
|
||||
x.name.toLowerCase().includes(filterTerm.toLowerCase()),
|
||||
) ?? [];
|
||||
filtered = filtered.filter(
|
||||
(x) => !this.props.selectedOptions?.some((y) => x._id === y._id),
|
||||
(x) => !selectedOptions?.some((y) => x._id === y._id),
|
||||
);
|
||||
this.setState({
|
||||
filteredOptions: filtered,
|
||||
});
|
||||
setFilteredOptions(filtered);
|
||||
} else {
|
||||
this.setState({
|
||||
filteredOptions: [],
|
||||
});
|
||||
setFilteredOptions([]);
|
||||
}
|
||||
}
|
||||
|
||||
private showOptions() {
|
||||
let filtered = this.props.options?.filter((x) => x.name) ?? [];
|
||||
filtered = filtered.filter(
|
||||
(x) => !this.props.selectedOptions?.some((y) => x._id === y._id),
|
||||
},
|
||||
[options, selectedOptions],
|
||||
);
|
||||
this.setState({
|
||||
filteredOptions: filtered,
|
||||
});
|
||||
}
|
||||
|
||||
private hideAutocomplete = (event: any) => {
|
||||
const showOptions = useCallback(() => {
|
||||
let filtered = options?.filter((x) => x.name) ?? [];
|
||||
filtered = filtered.filter(
|
||||
(x) => !selectedOptions?.some((y) => x._id === y._id),
|
||||
);
|
||||
setFilteredOptions(filtered);
|
||||
}, [options, selectedOptions]);
|
||||
|
||||
const hideAutocomplete = useCallback((event: any) => {
|
||||
if (event.target.classList.contains("autocomplete-text-input")) return;
|
||||
setFilteredOptions([]);
|
||||
}, []);
|
||||
|
||||
this.setState({
|
||||
filteredOptions: [],
|
||||
});
|
||||
};
|
||||
|
||||
private handleBlur = (event: React.FocusEvent) => {
|
||||
const handleBlur = useCallback((event: React.FocusEvent) => {
|
||||
setTimeout(() => {
|
||||
if (
|
||||
this.inputRef.current &&
|
||||
this.inputRef.current.contains(document.activeElement)
|
||||
inputRef.current &&
|
||||
inputRef.current.contains(document.activeElement)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ filteredOptions: [] });
|
||||
setFilteredOptions([]);
|
||||
}, 0);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("click", hideAutocomplete);
|
||||
return () => {
|
||||
document.removeEventListener("click", hideAutocomplete);
|
||||
};
|
||||
}, [hideAutocomplete]);
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener("click", this.hideAutocomplete);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener("click", this.hideAutocomplete);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { placeholder, onSelect } = this.props;
|
||||
const { filteredOptions } = this.state;
|
||||
const handleSelect = useCallback(
|
||||
(item: Option) => {
|
||||
onSelect(item);
|
||||
setFilteredOptions([]);
|
||||
},
|
||||
[onSelect],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="autocomplete"
|
||||
ref={this.inputRef}
|
||||
onBlur={this.handleBlur}
|
||||
ref={inputRef}
|
||||
onBlur={handleBlur}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<input
|
||||
className="autocomplete-text-input"
|
||||
type="text"
|
||||
onChange={(e) => {
|
||||
this.filterOptions(e.target.value);
|
||||
filterOptions(e.target.value);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
this.showOptions();
|
||||
onFocus={() => {
|
||||
showOptions();
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
{filteredOptions.length > 0 && (
|
||||
<ul className="autocomplete-options">
|
||||
{filteredOptions.map((x, i) => (
|
||||
{filteredOptions.map((x) => (
|
||||
<li key={x._id} value={x._id} tabIndex={0}>
|
||||
<button
|
||||
className="autocomplete-option text-left"
|
||||
onClick={(e) => {
|
||||
onSelect(x);
|
||||
this.setState({ filteredOptions: [] });
|
||||
onClick={() => {
|
||||
handleSelect(x);
|
||||
}}
|
||||
>
|
||||
{x.name}
|
||||
@ -122,5 +111,6 @@ export default class Autocomplete extends React.PureComponent<
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default Autocomplete;
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import React, { useEffect, useState, useCallback, useMemo } from "react";
|
||||
import { Navigate } from "react-router-dom";
|
||||
import TabHeader from "./TabHeader";
|
||||
|
||||
interface HorizontalTabsProps {
|
||||
@ -8,7 +7,6 @@ interface HorizontalTabsProps {
|
||||
|
||||
const HorizontalTabs: React.FC<HorizontalTabsProps> = ({ children }) => {
|
||||
const [activeTab, setActiveTab] = useState<string>("");
|
||||
const [redirect, setRedirect] = useState<string>("");
|
||||
|
||||
// Set initial tab on mount
|
||||
useEffect(() => {
|
||||
@ -26,10 +24,6 @@ const HorizontalTabs: React.FC<HorizontalTabsProps> = ({ children }) => {
|
||||
return match ? match.props.children : <></>;
|
||||
}, [children, activeTab]);
|
||||
|
||||
if (redirect !== "") {
|
||||
return <Navigate to={redirect} />;
|
||||
}
|
||||
|
||||
// If only one tab, just render its content
|
||||
if (children.length === 1) {
|
||||
return <>{activeTabChildren}</>;
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Namespaces } from "../../i18n/i18n";
|
||||
|
||||
interface PillProps {
|
||||
pillKey: any;
|
||||
@ -19,19 +21,19 @@ export default function Pill({
|
||||
className,
|
||||
onClick,
|
||||
readOnly,
|
||||
flash = false
|
||||
flash = false,
|
||||
}: PillProps) {
|
||||
const { t } = useTranslation(Namespaces.Common);
|
||||
|
||||
const handleOnClick = () => {
|
||||
onClick(pillKey);
|
||||
};
|
||||
|
||||
let classNames = "pill";
|
||||
|
||||
if (className)
|
||||
classNames += " " + className;
|
||||
if (className) classNames += " " + className;
|
||||
|
||||
if (flash)
|
||||
classNames += " flash";
|
||||
if (flash) classNames += " flash";
|
||||
|
||||
return (
|
||||
<div className={classNames}>
|
||||
@ -39,9 +41,9 @@ export default function Pill({
|
||||
{!readOnly && (
|
||||
<button
|
||||
type="button"
|
||||
className={`close ${!enabled ? 'd-none': ''}`}
|
||||
className={`close ${!enabled ? "d-none" : ""}`}
|
||||
data-dismiss="alert"
|
||||
aria-label="Close"
|
||||
aria-label={t("Close") as string}
|
||||
onClick={handleOnClick}
|
||||
>
|
||||
<FontAwesomeIcon icon={faXmark} />
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import deepFind from "../../utils/deepfind";
|
||||
import Column from "./columns";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
@ -46,6 +47,7 @@ export default function TableBody<T>({
|
||||
onSelectRow,
|
||||
showSecondaryAudit,
|
||||
}: TableBodyProps<T>): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const resolvePath = (path: string, args: string[]) => {
|
||||
let modifiedPath = path;
|
||||
let index = 0;
|
||||
@ -137,7 +139,7 @@ export default function TableBody<T>({
|
||||
buttonType={ButtonType.primary}
|
||||
keyValue={item}
|
||||
onClick={onDelete}
|
||||
confirmMessage={"Press again to delete"}
|
||||
confirmMessage={t("PressAgainToDelete")}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</ConfirmButton>
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { GeneralIdRef } from "../../utils/GeneralIdRef";
|
||||
|
||||
export default interface Option {
|
||||
_id: string | number;
|
||||
name: string;
|
||||
|
||||
@ -6,7 +6,6 @@ import { determineInitialLocale } from "../modules/frame/services/lanugageServic
|
||||
|
||||
export const Namespaces = {
|
||||
Common: "common",
|
||||
Frame: "frame",
|
||||
} as const;
|
||||
|
||||
export type Namespace = (typeof Namespaces)[keyof typeof Namespaces];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user