The ITags capability editor is now working.

This commit is contained in:
Colin Dawson 2026-02-15 18:13:58 +00:00
parent 3286de24e7
commit 0130a578ea
4 changed files with 191 additions and 73 deletions

View File

@ -2,82 +2,88 @@
@import "../Sass/esuiteVariables";
.form-group {
padding: 10px 0;
padding: 10px 0;
}
.label {
}
input {
background: transparent;
background: transparent;
}
table {
white-space: pre-wrap;
white-space: pre-wrap;
}
.form-control {
}
.form-check {
}
.form-check-input {
}
.form-check-label {
}
.flex {
display: flex;
height: 38px;
display: flex;
height: 38px;
}
.passwordIcon {
vertical-align: middle;
position: relative;
top: 3px;
vertical-align: middle;
position: relative;
top: 3px;
}
.fullHeight {
height: 100%;
padding-left: 5px;
padding-right: 5px;
height: 100%;
padding-left: 5px;
padding-right: 5px;
}
.e-printWidget {
width: 100%;
margin-top: 20px;
min-height: 100px;
border: 1px solid $gray-600;
border-radius: 5px;
width: 100%;
margin-top: 20px;
min-height: 100px;
border: 1px solid $gray-600;
border-radius: 5px;
.thumbnail {
width: 80px;
min-height: 80px;
float: left;
background-color: $gray-600;
margin: 10px;
}
.thumbnail {
width: 80px;
min-height: 80px;
float: left;
background-color: $gray-600;
margin: 10px;
}
.label {
padding-top: 35px;
width: 200px;
height: 100px;
}
.label {
padding-top: 35px;
width: 200px;
height: 100px;
}
.e-print {
cursor: pointer;
width: 100px;
}
.e-print {
cursor: pointer;
width: 100px;
}
}
.allignedCheckBox .checkbox {
float: left;
margin-top: 10px;
}
float: left;
margin-top: 10px;
}
.tags-input-container {
.tags-input-field {
}
.tags-input {
}
.tags-suggestions {
}
}

View File

@ -4,19 +4,21 @@ import ErrorBlock from "./ErrorBlock";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons";
import { Checklist } from "./Checklist";
import { TagsInput } from "./inputTags";
export enum InputType {
button = "button",
checkbox = "checkbox",
checklist = "checklist",
color = "color",
date = "date",
datetimelocal = "datetime-local",
email = "email",
file = "file",
hidden = "hidden",
inputTags = "inputTags",
image = "image",
month = "month",
checklist = "checklist",
number = "number",
password = "password",
radio = "radio",
@ -88,16 +90,12 @@ function Input(props: InputProps) {
if (type === InputType.checkbox) {
checked = value === String(true);
showValue = undefined;
divClassName = "form-check";
divClassName = "form-check allignedCheckBox";
className = "form-check-input";
labelClassName += " form-check-label";
flexClassName += "checkbox";
}
if (type === InputType.checkbox) {
divClassName += " allignedCheckBox";
}
const renderType =
type === InputType.password && showPasswordIcon === faEye
? InputType.text
@ -111,6 +109,48 @@ function Input(props: InputProps) {
flexClassName += "flex";
}
const inputRenderers: Record<InputType, (props: InputProps) => JSX.Element> =
{
[InputType.textarea]: (props) => (
<textarea
id={props.name}
className={className}
name={props.name}
onChange={props.onChange}
disabled={props.readOnly}
value={props.value ?? ""}
autoComplete={props.autoComplete}
/>
),
[InputType.checklist]: (props) => (
<Checklist
value={(props.value as string[]) ?? []}
options={props.options ?? []}
onChange={(selectedOptions) => {
props.onChange?.({
target: { value: selectedOptions },
} as any);
}}
/>
),
[InputType.inputTags]: (props) => (
<TagsInput
value={(props.value as string[]) ?? []}
suggestions={props.options?.map((o) => o.label)}
onChange={(newTags) => {
props.onChange?.({
target: { value: newTags },
} as any);
}}
error={props.error}
/>
),
// future controls go here
};
const customRenderer = inputRenderers[type];
return (
<div className={divClassName} hidden={hidden}>
{(includeLabel === true || includeLabel === undefined) && (
@ -120,34 +160,10 @@ function Input(props: InputProps) {
)}
<div className={flexClassName}>
{type === InputType.textarea && (
<textarea
id={name}
className={className}
name={name}
onChange={onChange}
disabled={readOnly}
value={showValue ?? ""}
autoComplete={autoComplete}
/>
)}
{type === InputType.checklist && (
<Checklist
value={(value as string[]) ?? []}
options={options ?? []}
onChange={(selectedOptions) => {
onChange?.({
target: {
value: selectedOptions,
},
} as any);
}}
/>
)}
{customRenderer && customRenderer(props)}
{/* ALL OTHER INPUT TYPES */}
{type !== InputType.textarea && type !== InputType.checklist && (
{!customRenderer && (
<input
{...rest}
id={name}

View File

@ -0,0 +1,96 @@
import React, { useState, useCallback } from "react";
import Pill from "./Pill";
interface TagsInputProps {
value: string[];
suggestions?: string[];
onChange: (newTags: string[]) => void;
placeholder?: string;
error?: string;
}
export const TagsInput: React.FC<TagsInputProps> = ({
value,
suggestions = [],
onChange,
placeholder,
error,
}) => {
const [input, setInput] = useState("");
const addTag = useCallback(
(tag: string) => {
const trimmed = tag.trim();
if (!trimmed) return;
if (value.includes(trimmed)) return;
onChange([...value, trimmed]);
setInput("");
},
[value, onChange],
);
const removeTag = useCallback(
(tag: string) => {
onChange(value.filter((t) => t !== tag));
},
[value, onChange],
);
const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
e.preventDefault();
addTag(input);
}
if (e.key === "Backspace" && input === "") {
removeTag(value[value.length - 1]);
}
},
[input, value, addTag, removeTag],
);
const filteredSuggestions = suggestions
.filter((s) => s.toLowerCase().includes(input.toLowerCase()))
.filter((s) => !value.includes(s));
return (
<div className="tags-input-container">
<div className="tags-input-field">
{value.map((tag) => (
<Pill
key={tag}
pillKey={tag}
displayText={tag}
readOnly={false}
onClick={() => removeTag(tag)}
/>
))}
<input
type="text"
value={input}
placeholder={placeholder}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleKeyDown}
className="tags-input"
/>
</div>
{filteredSuggestions.length > 0 && (
<ul className="tags-suggestions">
{filteredSuggestions.map((s) => (
<li key={s}>
<button className="tags-suggestion" onClick={() => addTag(s)}>
{s}
</button>
</li>
))}
</ul>
)}
{error && <div className="alert alert-danger">{error}</div>}
</div>
);
};

View File

@ -17,7 +17,7 @@ export const TagsEditor: React.FC<CapabilityEditorProps> = (props) => {
onChange,
"tags",
"Tags",
InputType.text,
InputType.inputTags,
fieldErrors,
)}
</>