summaryrefslogtreecommitdiffhomepage
path: root/frontend/src/components/inputs
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components/inputs')
-rw-r--r--frontend/src/components/inputs/Chips.tsx3
-rw-r--r--frontend/src/components/inputs/FileBrowser.tsx7
-rw-r--r--frontend/src/components/inputs/FileForm.tsx2
-rw-r--r--frontend/src/components/inputs/Selector.tsx83
-rw-r--r--frontend/src/components/inputs/Slider.tsx3
-rw-r--r--frontend/src/components/inputs/blacklist.tsx2
-rw-r--r--frontend/src/components/inputs/chip.scss43
-rw-r--r--frontend/src/components/inputs/selector.scss30
-rw-r--r--frontend/src/components/inputs/slider.scss49
9 files changed, 64 insertions, 158 deletions
diff --git a/frontend/src/components/inputs/Chips.tsx b/frontend/src/components/inputs/Chips.tsx
index 1be0050a0..e52f751d7 100644
--- a/frontend/src/components/inputs/Chips.tsx
+++ b/frontend/src/components/inputs/Chips.tsx
@@ -1,4 +1,4 @@
-import React, {
+import {
FocusEvent,
FunctionComponent,
KeyboardEvent,
@@ -8,7 +8,6 @@ import React, {
useRef,
useState,
} from "react";
-import "./chip.scss";
const SplitKeys = ["Tab", "Enter", " ", ",", ";"];
diff --git a/frontend/src/components/inputs/FileBrowser.tsx b/frontend/src/components/inputs/FileBrowser.tsx
index f3bc1d37d..5bf289de7 100644
--- a/frontend/src/components/inputs/FileBrowser.tsx
+++ b/frontend/src/components/inputs/FileBrowser.tsx
@@ -1,8 +1,9 @@
+import { useFileSystem } from "@/apis/hooks";
import { faFile, faFolder } from "@fortawesome/free-regular-svg-icons";
import { faReply } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useFileSystem } from "apis/hooks";
-import React, {
+import {
+ ChangeEvent,
FunctionComponent,
useEffect,
useMemo,
@@ -147,7 +148,7 @@ export const FileBrowser: FunctionComponent<FileBrowserProps> = ({
placeholder="Click to start"
type="text"
value={text}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+ onChange={(e: ChangeEvent<HTMLInputElement>) => {
setText(e.currentTarget.value);
}}
ref={input}
diff --git a/frontend/src/components/inputs/FileForm.tsx b/frontend/src/components/inputs/FileForm.tsx
index bfff960d6..af7a6a238 100644
--- a/frontend/src/components/inputs/FileForm.tsx
+++ b/frontend/src/components/inputs/FileForm.tsx
@@ -1,4 +1,4 @@
-import React, {
+import {
ChangeEvent,
FunctionComponent,
useEffect,
diff --git a/frontend/src/components/inputs/Selector.tsx b/frontend/src/components/inputs/Selector.tsx
index f3df67459..98ea9eeb3 100644
--- a/frontend/src/components/inputs/Selector.tsx
+++ b/frontend/src/components/inputs/Selector.tsx
@@ -1,8 +1,22 @@
-import { isArray } from "lodash";
-import React, { useCallback, useMemo } from "react";
-import Select from "react-select";
+import clsx from "clsx";
+import { FocusEvent, useCallback, useMemo, useRef } from "react";
+import Select, { GroupBase, OnChangeValue } from "react-select";
import { SelectComponents } from "react-select/dist/declarations/src/components";
-import "./selector.scss";
+
+export type SelectorOption<T> = {
+ label: string;
+ value: T;
+};
+
+export type SelectorComponents<T, M extends boolean> = SelectComponents<
+ SelectorOption<T>,
+ M,
+ GroupBase<SelectorOption<T>>
+>;
+
+export type SelectorValueType<T, M extends boolean> = M extends true
+ ? ReadonlyArray<T>
+ : Nullable<T>;
export interface SelectorProps<T, M extends boolean> {
className?: string;
@@ -13,11 +27,13 @@ export interface SelectorProps<T, M extends boolean> {
loading?: boolean;
multiple?: M;
onChange?: (k: SelectorValueType<T, M>) => void;
- onFocus?: (e: React.FocusEvent<HTMLElement>) => void;
+ onFocus?: (e: FocusEvent<HTMLElement>) => void;
label?: (item: T) => string;
defaultValue?: SelectorValueType<T, M>;
value?: SelectorValueType<T, M>;
- components?: Partial<SelectComponents<T, M, any>>;
+ components?: Partial<
+ SelectComponents<SelectorOption<T>, M, GroupBase<SelectorOption<T>>>
+ >;
}
export function Selector<T = string, M extends boolean = false>(
@@ -39,34 +55,45 @@ export function Selector<T = string, M extends boolean = false>(
value,
} = props;
- const nameFromItems = useCallback(
+ const labelRef = useRef(label);
+
+ const getName = useCallback(
(item: T) => {
- return options.find((v) => v.value === item)?.label;
+ if (labelRef.current) {
+ return labelRef.current(item);
+ }
+
+ return options.find((v) => v.value === item)?.label ?? "Unknown";
},
[options]
);
- // TODO: Force as any
const wrapper = useCallback(
- (value: SelectorValueType<T, M> | undefined | null): any => {
- if (value !== null && value !== undefined) {
- if (multiple) {
+ (
+ value: SelectorValueType<T, M> | undefined | null
+ ):
+ | SelectorOption<T>
+ | ReadonlyArray<SelectorOption<T>>
+ | null
+ | undefined => {
+ if (value === null || value === undefined) {
+ return value as null | undefined;
+ } else {
+ if (multiple === true) {
return (value as SelectorValueType<T, true>).map((v) => ({
- label: label ? label(v) : nameFromItems(v) ?? "Unknown",
+ label: getName(v),
value: v,
}));
} else {
const v = value as T;
return {
- label: label ? label(v) : nameFromItems(v) ?? "Unknown",
+ label: getName(v),
value: v,
};
}
}
-
- return value;
},
- [label, multiple, nameFromItems]
+ [multiple, getName]
);
const defaultWrapper = useMemo(
@@ -89,21 +116,23 @@ export function Selector<T = string, M extends boolean = false>(
isDisabled={disabled}
options={options}
components={components}
- className={`custom-selector w-100 ${className ?? ""}`}
+ className={clsx("custom-selector w-100", className)}
classNamePrefix="selector"
onFocus={onFocus}
- onChange={(v: SelectorOption<T>[]) => {
+ onChange={(newValue) => {
if (onChange) {
- let res: T | T[] | null = null;
- if (isArray(v)) {
- res = (v as ReadonlyArray<SelectorOption<T>>).map(
- (val) => val.value
- );
+ if (multiple === true) {
+ const values = (
+ newValue as OnChangeValue<SelectorOption<T>, true>
+ ).map((v) => v.value) as ReadonlyArray<T>;
+
+ onChange(values as SelectorValueType<T, M>);
} else {
- res = (v as SelectorOption<T>)?.value ?? null;
+ const value = (newValue as OnChangeValue<SelectorOption<T>, false>)
+ ?.value;
+
+ onChange(value as SelectorValueType<T, M>);
}
- // TODO: Force as any
- onChange(res as any);
}
}}
></Select>
diff --git a/frontend/src/components/inputs/Slider.tsx b/frontend/src/components/inputs/Slider.tsx
index e8a8c3c35..ab3fb0996 100644
--- a/frontend/src/components/inputs/Slider.tsx
+++ b/frontend/src/components/inputs/Slider.tsx
@@ -1,7 +1,6 @@
import RcSlider from "rc-slider";
import "rc-slider/assets/index.css";
-import React, { FunctionComponent, useMemo, useState } from "react";
-import "./slider.scss";
+import { FunctionComponent, useMemo, useState } from "react";
type TooltipsOptions = boolean | "Always";
diff --git a/frontend/src/components/inputs/blacklist.tsx b/frontend/src/components/inputs/blacklist.tsx
index fe079a925..d55e843ec 100644
--- a/frontend/src/components/inputs/blacklist.tsx
+++ b/frontend/src/components/inputs/blacklist.tsx
@@ -1,6 +1,6 @@
import { faFileExcel } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import React, { FunctionComponent } from "react";
+import { FunctionComponent } from "react";
import { AsyncButton } from "..";
interface Props {
diff --git a/frontend/src/components/inputs/chip.scss b/frontend/src/components/inputs/chip.scss
deleted file mode 100644
index 92e172541..000000000
--- a/frontend/src/components/inputs/chip.scss
+++ /dev/null
@@ -1,43 +0,0 @@
-.custom-chip-input {
- overflow: hidden;
- &:focus-within {
- border-color: var(--primary);
- }
- .main-input {
- border: hidden;
- outline: none;
- width: 100%;
- flex-grow: 2;
- }
-
- .chip-container {
- display: flex;
- flex-wrap: nowrap;
- flex-grow: 1;
- }
-
- .custom-chip {
- padding: 0 0.5rem;
- margin-right: 0.25rem;
- background-color: var(--light);
- border-radius: 0.25rem;
-
- max-width: 10rem;
-
- overflow-x: hidden;
- text-overflow: ellipsis;
-
- transition: {
- duration: 0.1s;
- timing-function: ease-in-out;
- }
-
- &.active {
- &:hover {
- cursor: pointer;
- background-color: var(--danger);
- color: white;
- }
- }
- }
-}
diff --git a/frontend/src/components/inputs/selector.scss b/frontend/src/components/inputs/selector.scss
deleted file mode 100644
index 5f4b139c5..000000000
--- a/frontend/src/components/inputs/selector.scss
+++ /dev/null
@@ -1,30 +0,0 @@
-@import "../../@scss/variable.scss";
-
-.custom-selector {
- .selector__control {
- outline: none !important;
- box-shadow: none !important;
- border-radius: 0.25rem;
- }
- .selector__control--is-focused {
- border-color: var(--primary) !important;
- }
- .selector__menu {
- z-index: 1000;
- }
-
- .selector__option--is-focused {
- background-color: $theme-color-transparent;
- &:focus,
- &:active {
- background-color: $theme-color-less-transparent;
- }
- }
-
- .selector__option--is-selected {
- background-color: var(--primary);
- &:active {
- background-color: $theme-color-darked;
- }
- }
-}
diff --git a/frontend/src/components/inputs/slider.scss b/frontend/src/components/inputs/slider.scss
deleted file mode 100644
index f0fe518dd..000000000
--- a/frontend/src/components/inputs/slider.scss
+++ /dev/null
@@ -1,49 +0,0 @@
-.custom-rc-slider {
- .rc-slider-track {
- background-color: var(--primary);
- }
- .rc-slider-step {
- cursor: pointer;
- }
- .rc-slider-handle {
- border: 3px solid var(--primary);
- margin-top: 0;
- top: 50%;
- transform: translate(-50%, -50%) !important;
- width: 18px;
- height: 18px;
- cursor: pointer;
-
- .rc-slider-handle-tips-always {
- display: block !important;
- }
-
- .rc-slider-handle-tips-hidden {
- display: none !important;
- }
-
- .rc-slider-handle-tips {
- font-size: medium;
- display: none;
- position: absolute;
- top: -1.1rem;
- left: 50%;
- transform: translate(-50%, -50%);
- }
-
- &:hover {
- border-color: var(--primary);
- .rc-slider-handle-tips {
- display: block;
- }
- }
-
- &:active {
- border-color: var(--primary);
- box-shadow: none;
- .rc-slider-handle-tips {
- display: block;
- }
- }
- }
-}