Track HiveGrid Cloud planning reference
This commit is contained in:
1 parent
b3d5ea6764
commit
e341c5938a
5784 files changed
+1104929
-3
No files matched your search
@@ -0,0 +1,215 @@
|
||||
import React, { useState, useRef, useEffect, KeyboardEvent, ReactNode, RefObject, Ref, forwardRef, useImperativeHandle } from "react";
|
||||
import clsx from "clsx";
|
||||
import HintTooltip from "../tooltips/HintTooltip";
|
||||
import EntityIcon from "~/components/layouts/icons/EntityIcon";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { RowValueMultipleDto } from "~/application/dtos/entities/RowValueMultipleDto";
|
||||
import { SeparatorFormatType } from "~/utils/shared/SeparatorUtils";
|
||||
import { Input } from "../input";
|
||||
|
||||
export interface RefInputMultiText {
|
||||
input: RefObject<HTMLInputElement | null>;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
name?: string;
|
||||
title?: string;
|
||||
withLabel?: boolean;
|
||||
value?: RowValueMultipleDto[];
|
||||
className?: string;
|
||||
classNameBg?: string;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
readOnly?: boolean;
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
autoComplete?: string;
|
||||
translationParams?: string[];
|
||||
placeholder?: string;
|
||||
pattern?: string;
|
||||
rows?: number;
|
||||
button?: ReactNode;
|
||||
lowercase?: boolean;
|
||||
uppercase?: boolean;
|
||||
darkMode?: boolean;
|
||||
hint?: ReactNode;
|
||||
help?: string;
|
||||
icon?: string;
|
||||
onBlur?: () => void;
|
||||
borderless?: boolean;
|
||||
autoFocus?: boolean;
|
||||
separator?: SeparatorFormatType;
|
||||
}
|
||||
|
||||
const InputMultiText = (
|
||||
{
|
||||
name,
|
||||
title,
|
||||
withLabel = true,
|
||||
value,
|
||||
className,
|
||||
classNameBg,
|
||||
help,
|
||||
disabled = false,
|
||||
readOnly = false,
|
||||
required = false,
|
||||
minLength,
|
||||
maxLength,
|
||||
autoComplete,
|
||||
placeholder,
|
||||
pattern,
|
||||
hint,
|
||||
rows,
|
||||
button,
|
||||
lowercase,
|
||||
uppercase,
|
||||
darkMode,
|
||||
icon,
|
||||
onBlur,
|
||||
borderless,
|
||||
autoFocus,
|
||||
separator = ",",
|
||||
}: Props,
|
||||
ref: Ref<RefInputMultiText>
|
||||
) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
useImperativeHandle(ref, () => ({ input }));
|
||||
const input = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
|
||||
const [actualValue, setActualValue] = useState<(string | number)[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const selection = value?.map((f) => f.value) ?? [];
|
||||
if (selection.sort().join(",") !== actualValue.sort().join(",")) {
|
||||
setActualValue(selection);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [value]);
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (separator === e.key) {
|
||||
e.preventDefault();
|
||||
if (inputValue.trim() !== "") {
|
||||
setActualValue?.((prevTags) => [...prevTags, inputValue.trim()]);
|
||||
setInputValue("");
|
||||
}
|
||||
}
|
||||
|
||||
if (e.key === "Backspace" && inputValue === "") {
|
||||
setActualValue?.((prevTags) => prevTags.slice(0, prevTags.length - 1));
|
||||
}
|
||||
};
|
||||
|
||||
const removeTag = (index: number) => {
|
||||
setActualValue?.((prevTags) => prevTags.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={clsx(className, !darkMode && "")}>
|
||||
{withLabel && (
|
||||
<label htmlFor={name} className="mb-1 flex justify-between space-x-2 truncate text-xs font-medium">
|
||||
<div className="flex items-center space-x-1 truncate">
|
||||
<div className="flex space-x-1 truncate">
|
||||
<div className="truncate">{title}</div>
|
||||
{required && title && <div className="ml-1 text-red-600">*</div>}
|
||||
</div>
|
||||
<div className="">{help && <HintTooltip text={help} />}</div>
|
||||
</div>
|
||||
{hint}
|
||||
</label>
|
||||
)}
|
||||
|
||||
{actualValue.map((item, idx) => {
|
||||
return (
|
||||
<input
|
||||
key={idx}
|
||||
type="hidden"
|
||||
name={name + `[]`}
|
||||
value={JSON.stringify({
|
||||
value: item,
|
||||
order: idx,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
<div className={clsx("relative flex w-full rounded-md")}>
|
||||
{icon && (
|
||||
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<EntityIcon className="text-text-neutral-tertiary h-5 w-5" icon={icon} />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex w-full flex-wrap items-center">
|
||||
{actualValue?.map((tag, index) => (
|
||||
<div key={index} className="border-border-default bg-surface-neutral-secondary m-0.5 flex items-center rounded border px-2 py-2 text-sm">
|
||||
<span>{tag}</span>
|
||||
{!disabled && !readOnly && (
|
||||
<button
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
className="text-text-neutral-tertiary focus:text-text-neutral-primary ml-2 shrink-0"
|
||||
onClick={() => removeTag(index)}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{!disabled && !readOnly && (
|
||||
<Input
|
||||
type="text"
|
||||
ref={input}
|
||||
value={inputValue}
|
||||
onChange={(e) => {
|
||||
if (lowercase) {
|
||||
setInputValue(e.target.value.toLowerCase());
|
||||
} else if (uppercase) {
|
||||
setInputValue(e.target.value.toUpperCase());
|
||||
} else {
|
||||
setInputValue(e.target.value);
|
||||
}
|
||||
}}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={placeholder ?? t("shared.typeAndPressTo", { 0: separator, 1: t("shared.add").toLowerCase() })}
|
||||
id={name}
|
||||
autoComplete={autoComplete}
|
||||
// required={required}
|
||||
minLength={minLength}
|
||||
maxLength={maxLength}
|
||||
// defaultValue={value}
|
||||
onBlur={() => {
|
||||
if (inputValue.trim() !== "") {
|
||||
setActualValue?.((prevTags) => [...prevTags, inputValue.trim()]);
|
||||
setInputValue("");
|
||||
}
|
||||
if (onBlur) {
|
||||
onBlur();
|
||||
}
|
||||
}}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
pattern={pattern !== "" && pattern !== undefined ? pattern : undefined}
|
||||
autoFocus={autoFocus}
|
||||
className={clsx(
|
||||
"m-0.5 min-w-0 flex-1",
|
||||
icon && "rounded-md pl-10"
|
||||
// "focus:border-border focus:ring-ring block w-full min-w-0 flex-1 rounded-md border-border sm:text-sm",
|
||||
// className,
|
||||
// classNameBg,
|
||||
// disabled || readOnly ? "cursor-not-allowed bg-secondary/90" : "hover:bg-secondary focus:bg-secondary",
|
||||
// borderless && "border-transparent"
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{button}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default forwardRef(InputMultiText);
|
||||
Reference in new issue
Block a user