mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 02:00:46 +00:00
input tags
This commit is contained in:
1 parent
954065b3e6
commit
b2a67f095a
6 files changed
+212
No files matched your search
@@ -0,0 +1,21 @@
|
||||
import MakeRequest from "../MakeRequest";
|
||||
|
||||
export interface EditTagsRequest {
|
||||
tags: string;
|
||||
}
|
||||
|
||||
export interface EditTagsResponse {
|
||||
success: boolean;
|
||||
tag_tokens: string[];
|
||||
}
|
||||
|
||||
export const EditTags = MakeRequest<
|
||||
string,
|
||||
EditTagsRequest,
|
||||
EditTagsResponse,
|
||||
{}
|
||||
>({
|
||||
method: "POST",
|
||||
routingFunction: (weight_token: string) =>
|
||||
`/v1/tags/edit/model_weight/${weight_token}`,
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import MakeRequest from "../MakeRequest";
|
||||
|
||||
export interface ListTagsRequest {}
|
||||
|
||||
export interface ListTagsResponse {
|
||||
success: boolean;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export const ListTags = MakeRequest<
|
||||
string,
|
||||
ListTagsRequest,
|
||||
ListTagsResponse,
|
||||
{}
|
||||
>({
|
||||
method: "GET",
|
||||
routingFunction: (weight_token: string) =>
|
||||
`/v1/tags/list/model_weight/${weight_token}`,
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
@import "scss/colours.scss";
|
||||
|
||||
.tags-input-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
|
||||
&:focus-within {
|
||||
border-color: $primary;
|
||||
outline: 0;
|
||||
box-shadow: 0 0 0 0.1rem $primary;
|
||||
}
|
||||
}
|
||||
|
||||
.tag {
|
||||
background-color: lighten($action, 10%);
|
||||
border-radius: 5px;
|
||||
padding: 0 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 15px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tag button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: rgba($white, 0.6);
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: lighten($primary, 20%);
|
||||
}
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
border: none;
|
||||
outline: none;
|
||||
flex: 1;
|
||||
background-color: transparent;
|
||||
color: $white;
|
||||
|
||||
&::placeholder {
|
||||
color: rgba($white, 0.5);
|
||||
opacity: 1; /* Firefox */
|
||||
}
|
||||
|
||||
&:-ms-input-placeholder {
|
||||
/* Internet Explorer 10-11 */
|
||||
color: rgba($white, 0.5);
|
||||
}
|
||||
|
||||
&::-ms-input-placeholder {
|
||||
/* Microsoft Edge */
|
||||
color: rgba($white, 0.5);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import React, { useState } from "react";
|
||||
import "./TagsInput.scss";
|
||||
import Label from "../Label";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faXmark } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
interface TagsInputProps {
|
||||
value: string[];
|
||||
label?: string;
|
||||
onChange: (tags: string[]) => void;
|
||||
className?: string;
|
||||
tagsLimit?: number;
|
||||
}
|
||||
|
||||
const TagsInput: React.FC<TagsInputProps> = ({
|
||||
value,
|
||||
label,
|
||||
onChange,
|
||||
className,
|
||||
tagsLimit = Infinity,
|
||||
}) => {
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if ((event.key === "Enter" || event.key === ",") && inputValue.trim()) {
|
||||
event.preventDefault();
|
||||
addTag(inputValue);
|
||||
} else if (event.key === "Backspace" && inputValue === "") {
|
||||
handleRemoveTag(value.length - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
if (inputValue.trim()) {
|
||||
addTag(inputValue);
|
||||
}
|
||||
};
|
||||
|
||||
const addTag = (tag: string) => {
|
||||
const lowerCaseValue = tag.trim().toLowerCase();
|
||||
if (value.length < tagsLimit) {
|
||||
onChange([...value, lowerCaseValue]);
|
||||
setInputValue("");
|
||||
} else {
|
||||
setInputValue("");
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveTag = (index: number) => {
|
||||
const newTags = value.filter((_, i) => i !== index);
|
||||
onChange(newTags);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{label && <Label label={label} />}
|
||||
<div className="tags-input-container form-control">
|
||||
{value.map((tag, index) => (
|
||||
<div className="tag" key={index}>
|
||||
{tag}
|
||||
<button type="button" onClick={() => handleRemoveTag(index)}>
|
||||
<FontAwesomeIcon icon={faXmark} />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<input
|
||||
type="text"
|
||||
value={inputValue}
|
||||
onChange={e => setInputValue(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={handleBlur}
|
||||
placeholder={
|
||||
value.length < tagsLimit
|
||||
? "Enter a tag and press Enter"
|
||||
: "Tags limit reached"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TagsInput;
|
||||
@@ -9,6 +9,8 @@ import { UpdateWeight } from "@storyteller/components/src/api/weights/UpdateWeig
|
||||
import { DeleteWeight } from "@storyteller/components/src/api/weights/DeleteWeight";
|
||||
import { useCoverImgUpload } from "hooks";
|
||||
import { LanguageTag } from "@storyteller/components/src/api/Languages";
|
||||
import { ListTags } from "@storyteller/components/src/api/tags/ListTags";
|
||||
import { EditTags } from "@storyteller/components/src/api/tags/EditTags";
|
||||
|
||||
interface Props {
|
||||
onSuccess?: (res: Weight) => any;
|
||||
@@ -32,6 +34,7 @@ export default function useWeightFetch({
|
||||
const [maybeUrlSlug, maybeUrlSlugSet] = useState(undefined);
|
||||
const [visibility, visibilitySet] = useState("public");
|
||||
const [languageTag, languageTagSet] = useState<LanguageTag>("en" || null);
|
||||
const [tags, tagsSet] = useState<string[]>([]);
|
||||
const [descriptionMD, descriptionMDSet] = useState("");
|
||||
const isLoading =
|
||||
status === FetchStatus.ready || status === FetchStatus.in_progress;
|
||||
@@ -64,6 +67,9 @@ export default function useWeightFetch({
|
||||
weight_type: data?.weight_type || "",
|
||||
})
|
||||
.then((res: any) => {
|
||||
EditTags(token, {
|
||||
tags: tags.join(","),
|
||||
});
|
||||
writeStatusSet(FetchStatus.success);
|
||||
history.replace(`/weight/${token}`);
|
||||
})
|
||||
@@ -93,6 +99,7 @@ export default function useWeightFetch({
|
||||
descriptionMDSet("");
|
||||
visibilitySet("public");
|
||||
languageTagSet("en");
|
||||
tagsSet([]);
|
||||
}
|
||||
}, [token, refetch]);
|
||||
|
||||
@@ -118,6 +125,12 @@ export default function useWeightFetch({
|
||||
languageTagSet(maybe_ietf_primary_language_subtag);
|
||||
onSuccess(res);
|
||||
setData(res);
|
||||
|
||||
ListTags(token, {}).then((res: any) => {
|
||||
if (res.success) {
|
||||
tagsSet(res.tags);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
statusSet(FetchStatus.error);
|
||||
}
|
||||
@@ -143,5 +156,7 @@ export default function useWeightFetch({
|
||||
visibility,
|
||||
writeStatus,
|
||||
languageTag,
|
||||
tags,
|
||||
tagsSet,
|
||||
};
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import { useSession, useWeightFetch } from "hooks";
|
||||
import "./WeightEditPage.scss";
|
||||
import { LanguageLabels } from "@storyteller/components/src/api/Languages";
|
||||
import { WeightCategory } from "@storyteller/components/src/api/_common/enums";
|
||||
import TagsInput from "components/common/TagsInput/TagsInput";
|
||||
|
||||
export default function WeightEditPage() {
|
||||
const { user, canEditTtsModel } = useSession();
|
||||
@@ -45,6 +46,8 @@ export default function WeightEditPage() {
|
||||
visibility,
|
||||
languageTag,
|
||||
writeStatus,
|
||||
tags,
|
||||
tagsSet,
|
||||
// status
|
||||
} = useWeightFetch({ token: weight_token });
|
||||
|
||||
@@ -180,6 +183,14 @@ export default function WeightEditPage() {
|
||||
/>
|
||||
)}
|
||||
|
||||
<TagsInput
|
||||
label="Tags"
|
||||
value={tags}
|
||||
onChange={tagsSet}
|
||||
className="mb-3"
|
||||
tagsLimit={10}
|
||||
/>
|
||||
|
||||
<TempSelect
|
||||
{...{
|
||||
icon: faEye,
|
||||
|
||||
Reference in new issue
Block a user