diff options
Diffstat (limited to 'frontend/src/components/inputs')
-rw-r--r-- | frontend/src/components/inputs/Chips.tsx | 3 | ||||
-rw-r--r-- | frontend/src/components/inputs/FileBrowser.tsx | 7 | ||||
-rw-r--r-- | frontend/src/components/inputs/FileForm.tsx | 2 | ||||
-rw-r--r-- | frontend/src/components/inputs/Selector.tsx | 83 | ||||
-rw-r--r-- | frontend/src/components/inputs/Slider.tsx | 3 | ||||
-rw-r--r-- | frontend/src/components/inputs/blacklist.tsx | 2 | ||||
-rw-r--r-- | frontend/src/components/inputs/chip.scss | 43 | ||||
-rw-r--r-- | frontend/src/components/inputs/selector.scss | 30 | ||||
-rw-r--r-- | frontend/src/components/inputs/slider.scss | 49 |
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; - } - } - } -} |