The ITags capability editor is now working.
This commit is contained in:
parent
3286de24e7
commit
0130a578ea
@ -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 {
|
||||
}
|
||||
}
|
||||
@ -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}
|
||||
|
||||
96
src/components/common/inputTags.tsx
Normal file
96
src/components/common/inputTags.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@ -17,7 +17,7 @@ export const TagsEditor: React.FC<CapabilityEditorProps> = (props) => {
|
||||
onChange,
|
||||
"tags",
|
||||
"Tags",
|
||||
InputType.text,
|
||||
InputType.inputTags,
|
||||
fieldErrors,
|
||||
)}
|
||||
</>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user