mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 01:38:44 +00:00
search page and many component updates
This commit is contained in:
1 parent
ff6f7821a2
commit
cf3f79dc5f
31 files changed
+569
-71
No files matched your search
@@ -3,13 +3,14 @@
|
||||
.button {
|
||||
border-radius: 0.5rem;
|
||||
outline: none;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
font-size: 15px !important;
|
||||
font-weight: 500 !important;
|
||||
padding: 10px 20px;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: $white !important;
|
||||
}
|
||||
|
||||
.button-primary,
|
||||
@@ -19,19 +20,19 @@
|
||||
border: 2.5px solid;
|
||||
border-color: $primary;
|
||||
transition: 0.2s;
|
||||
color: $white;
|
||||
color: $white !important;
|
||||
}
|
||||
.button-primary:hover {
|
||||
background-color: lighten($primary, 5%);
|
||||
border-color: lighten($primary, 5%);
|
||||
color: $white;
|
||||
color: $white !important;
|
||||
}
|
||||
|
||||
.button-secondary,
|
||||
.button-secondary:focus,
|
||||
.button-secondary:active {
|
||||
background-color: $secondary;
|
||||
color: $white;
|
||||
color: $white !important;
|
||||
border: 2.5px solid;
|
||||
border-color: $secondary;
|
||||
transition: 0.2s;
|
||||
@@ -39,7 +40,7 @@
|
||||
|
||||
.button-secondary:hover {
|
||||
background-color: lighten($secondary, 5%);
|
||||
color: $white;
|
||||
color: $white !important;
|
||||
border: 2.5px solid;
|
||||
border-color: lighten($secondary, 5%);
|
||||
}
|
||||
@@ -48,7 +49,7 @@
|
||||
.button-destructive:focus,
|
||||
.button-destructive:active {
|
||||
background-color: $danger;
|
||||
color: $white;
|
||||
color: $white !important;
|
||||
border: 2.5px solid;
|
||||
border-color: $danger;
|
||||
transition: 0.2s;
|
||||
@@ -56,7 +57,7 @@
|
||||
|
||||
.button-destructive:hover {
|
||||
background-color: lighten($danger, 5%);
|
||||
color: $white;
|
||||
color: $white !important;
|
||||
border: 2.5px solid;
|
||||
border-color: lighten($danger, 5%);
|
||||
}
|
||||
@@ -65,7 +66,7 @@
|
||||
.button-small:focus,
|
||||
.button-small:active {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
font-size: 14px !important;
|
||||
padding: 8.25px 16px;
|
||||
transition: 0.2s;
|
||||
border: none;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { ButtonHTMLAttributes } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import "./Button.scss";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
@@ -7,29 +8,68 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
label?: string;
|
||||
icon?: IconDefinition;
|
||||
small?: boolean;
|
||||
secondary?: boolean;
|
||||
danger?: boolean;
|
||||
variant?: "primary" | "secondary" | "danger";
|
||||
to?: string; // for internal links
|
||||
href?: string; // for external links
|
||||
target?: "_blank" | "_self";
|
||||
}
|
||||
|
||||
export default function Button({
|
||||
label,
|
||||
icon,
|
||||
small,
|
||||
secondary,
|
||||
danger,
|
||||
variant = "primary",
|
||||
to,
|
||||
href,
|
||||
target = "_self",
|
||||
...rest
|
||||
}: ButtonProps) {
|
||||
const buttonClasses = `button ${small ? "button-small" : ""}
|
||||
${
|
||||
secondary
|
||||
? "button-secondary"
|
||||
: danger
|
||||
? "button-destructive"
|
||||
: "button-primary"
|
||||
} `;
|
||||
let buttonClass = `button`;
|
||||
|
||||
if (small) {
|
||||
buttonClass += ` button-small`;
|
||||
}
|
||||
|
||||
switch (variant) {
|
||||
case "primary":
|
||||
buttonClass += " button-primary";
|
||||
break;
|
||||
case "secondary":
|
||||
buttonClass += " button-secondary";
|
||||
break;
|
||||
case "danger":
|
||||
buttonClass += " button-destructive";
|
||||
break;
|
||||
default:
|
||||
buttonClass += " button-primary";
|
||||
break;
|
||||
}
|
||||
|
||||
if (to) {
|
||||
return (
|
||||
<Link to={to} className={buttonClass}>
|
||||
{icon && <FontAwesomeIcon icon={icon} className="me-2" />}
|
||||
{label}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
if (href) {
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
target={target}
|
||||
rel="noopener noreferrer"
|
||||
className={buttonClass}
|
||||
>
|
||||
{icon && <FontAwesomeIcon icon={icon} className="me-2" />}
|
||||
{label}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
// If neither 'to' nor 'href' are provided (regular button)
|
||||
return (
|
||||
<button className={buttonClasses} type="button" {...rest}>
|
||||
<button className={buttonClass} type="button" {...rest}>
|
||||
{icon && <FontAwesomeIcon icon={icon} className="me-2" />}
|
||||
{label}
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./Button";
|
||||
@@ -0,0 +1,39 @@
|
||||
@import "scss/colours.scss";
|
||||
|
||||
.checkable-tag {
|
||||
font-weight: 500;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 13px;
|
||||
padding: 9px 12px;
|
||||
background-color: $secondary;
|
||||
color: $white;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
border: 2px solid $secondary;
|
||||
|
||||
&.checked {
|
||||
background-color: lighten($secondary, 10%) !important;
|
||||
color: $white;
|
||||
border: 2px solid $primary !important;
|
||||
}
|
||||
|
||||
.remove-icon {
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($secondary, 5%);
|
||||
border: 2px solid lighten($secondary, 5%);
|
||||
}
|
||||
|
||||
&:hover > .remove-icon {
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import React from "react";
|
||||
import "./CheckableTag.scss";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faXmark } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
interface Props {
|
||||
tag: string;
|
||||
isSelected: boolean;
|
||||
onToggle: (tag: string) => void;
|
||||
}
|
||||
|
||||
export default function CheckableTag({ tag, isSelected, onToggle }: Props) {
|
||||
return (
|
||||
<div
|
||||
className={`d-flex align-items-center checkable-tag ${
|
||||
isSelected ? "checked" : ""
|
||||
}`}
|
||||
onClick={() => onToggle(tag)}
|
||||
>
|
||||
{tag}
|
||||
{isSelected && (
|
||||
<FontAwesomeIcon icon={faXmark} className="ms-2 remove-icon" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./CheckableTag";
|
||||
@@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
|
||||
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
icon?: IconDefinition;
|
||||
label?: string;
|
||||
textArea?: boolean;
|
||||
}
|
||||
|
||||
export default function Input({ label, icon, textArea, ...rest }: InputProps) {
|
||||
return (
|
||||
<div>
|
||||
{label && <label className="sub-title">{label}</label>}
|
||||
|
||||
<div className={`form-group ${icon ? "input-icon" : ""}`}>
|
||||
{icon && (
|
||||
<FontAwesomeIcon icon={icon} className="form-control-feedback" />
|
||||
)}
|
||||
<input className="form-control" {...rest} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./Input";
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
import Input from "../Input";
|
||||
import { faSearch } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
export default function ModelSearch({ value, onChange }: Props) {
|
||||
return (
|
||||
<Input
|
||||
icon={faSearch}
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder="Search..."
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export const dummyData = [
|
||||
{ id: 1, name: "Item 1", tags: ["tag1", "tag2"] },
|
||||
{ id: 2, name: "Item 2", tags: ["tag2", "tag3"] },
|
||||
];
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./ModelSearch";
|
||||
@@ -0,0 +1,29 @@
|
||||
@import "scss/colours.scss";
|
||||
|
||||
.model-search-results {
|
||||
background-color: lighten($panel, 6%);
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid lighten($panel, 12%);
|
||||
|
||||
.type-tag {
|
||||
font-weight: 600;
|
||||
border-radius: 0.4rem;
|
||||
font-size: 13px;
|
||||
padding: 4px 8px;
|
||||
background-color: $secondary;
|
||||
color: $white;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
height: 100%;
|
||||
|
||||
&.tt2 {
|
||||
color: #494ea9;
|
||||
background-color: #c6c7e1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from "react";
|
||||
import "./ModelSearchResults.scss";
|
||||
|
||||
interface Props {
|
||||
data: any[];
|
||||
}
|
||||
|
||||
export default function ModelSearchResults({ data }: Props) {
|
||||
return (
|
||||
<div className="row g-3">
|
||||
{data.map((item) => (
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="model-search-results p-3" key={item.id}>
|
||||
<h5 className="fw-semibold mb-0">{item.name}</h5>
|
||||
<p className="">by {item.creator}</p>
|
||||
<span
|
||||
className={`type-tag ${
|
||||
item.type === "Tacotron2" ? "tt2" : "tt2"
|
||||
}`}
|
||||
>
|
||||
{item.type}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./ModelSearchResults";
|
||||
@@ -0,0 +1,23 @@
|
||||
import React from "react";
|
||||
import CheckableTag from "../CheckableTag";
|
||||
|
||||
interface Props {
|
||||
tags: string[];
|
||||
selectedTags: string[];
|
||||
onSelectTag: (tag: string) => void;
|
||||
}
|
||||
|
||||
export default function ModelTags({ tags, selectedTags, onSelectTag }: Props) {
|
||||
return (
|
||||
<div className="d-flex gap-2 flex-wrap">
|
||||
{tags.map((tag) => (
|
||||
<CheckableTag
|
||||
key={tag}
|
||||
tag={tag}
|
||||
isSelected={selectedTags.includes(tag)}
|
||||
onToggle={onSelectTag}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./ModelTags";
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./Panel";
|
||||
@@ -0,0 +1,68 @@
|
||||
@import "scss/colours.scss";
|
||||
|
||||
.select__control {
|
||||
&.select {
|
||||
height: 48px;
|
||||
border-width: 2px;
|
||||
padding-left: 0.15rem;
|
||||
padding-right: 0.15rem;
|
||||
background-color: $input-dark;
|
||||
border-radius: 0.5rem;
|
||||
border-color: $input-dark !important;
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
|
||||
&.no-padding {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($input-dark, 5%);
|
||||
border-color: lighten($input-dark, 5%) !important;
|
||||
}
|
||||
|
||||
&.focused {
|
||||
border-color: $primary !important;
|
||||
}
|
||||
|
||||
&.with-icon {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
&.rounded-full {
|
||||
border-radius: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.select-option {
|
||||
background-color: $input-dark;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem 1rem;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($input-dark, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
.select-input,
|
||||
.select-value {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.select-container {
|
||||
background: $input-dark;
|
||||
margin-top: 6px;
|
||||
padding: 0;
|
||||
border-radius: 0.5rem;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.select-separator {
|
||||
width: 1px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.select-placeholder {
|
||||
color: rgba($white, 0.5) !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import "./Select.scss";
|
||||
import {
|
||||
default as ReactSelect,
|
||||
Props as ReactSelectProps,
|
||||
} from "react-select";
|
||||
|
||||
interface SelectProps extends ReactSelectProps {
|
||||
icon?: IconDefinition;
|
||||
label?: string;
|
||||
rounded?: boolean;
|
||||
}
|
||||
|
||||
export default function Select({ label, icon, rounded, ...rest }: SelectProps) {
|
||||
const SelectFieldClass = {
|
||||
control: (state: any) =>
|
||||
state.isFocused
|
||||
? `select focused ${icon && "with-icon"} ${rounded && "rounded-full"}`
|
||||
: `select ${icon && "with-icon"} ${rounded && "rounded-full"}`,
|
||||
option: (state: any) =>
|
||||
state.isFocused ? "select-option" : "select-option",
|
||||
input: (state: any) => (state.isFocused ? "select-input" : "select-input"),
|
||||
placeholder: (state: any) =>
|
||||
state.isFocused ? "select-placeholder" : "select-placeholder",
|
||||
singleValue: (state: any) =>
|
||||
state.isFocused ? "select-value" : "select-value",
|
||||
menu: (state: any) =>
|
||||
state.isFocused ? "select-container" : "select-container",
|
||||
indicatorSeparator: (state: any) =>
|
||||
state.isFocused ? "select-separator" : "select-separator",
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{label && <label className="sub-title">{label}</label>}
|
||||
|
||||
<div className={`form-group ${icon ? "input-icon" : ""}`}>
|
||||
{icon && (
|
||||
<FontAwesomeIcon icon={icon} className="form-control-feedback" />
|
||||
)}
|
||||
<div className="w-100">
|
||||
<ReactSelect
|
||||
classNamePrefix="select"
|
||||
classNames={SelectFieldClass}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./Select";
|
||||
@@ -35,8 +35,8 @@ export default function ShareButton(props: ShareButtonProps) {
|
||||
<Button
|
||||
label="Share Link"
|
||||
icon={faLink}
|
||||
secondary
|
||||
small
|
||||
variant="secondary"
|
||||
small={true}
|
||||
onClick={copyToClipboard}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
|
||||
interface TextAreaProps
|
||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||
label?: string;
|
||||
textArea?: boolean;
|
||||
}
|
||||
|
||||
export default function TextArea({ label, textArea, ...rest }: TextAreaProps) {
|
||||
return (
|
||||
<div>
|
||||
{label && <label className="sub-title">{label}</label>}
|
||||
|
||||
<div className="form-group">
|
||||
<textarea className="form-control" {...rest} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./TextArea";
|
||||
@@ -43,7 +43,7 @@ export default function MobileMenu(props: MobileMenuProps) {
|
||||
icon={faUser}
|
||||
label="Profile"
|
||||
small
|
||||
secondary
|
||||
variant="secondary"
|
||||
onClick={() => history.push(url)}
|
||||
/>
|
||||
</>
|
||||
@@ -58,7 +58,7 @@ export default function MobileMenu(props: MobileMenuProps) {
|
||||
<Button
|
||||
label="Menu"
|
||||
icon={faBars}
|
||||
secondary
|
||||
variant="secondary"
|
||||
small
|
||||
onClick={handleMenuButtonClick}
|
||||
/>
|
||||
|
||||
@@ -1,46 +1,59 @@
|
||||
import React from "react";
|
||||
import Panel from "../../common/Panel";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import Button from "components/common/Button";
|
||||
|
||||
interface Props {
|
||||
childProps?: any;
|
||||
titleIcon?: JSX.Element;
|
||||
titleComponent?: React.ComponentType;
|
||||
title?: string;
|
||||
subText: string;
|
||||
showButtons: boolean;
|
||||
actionButtons?: JSX.Element;
|
||||
interface PageHeaderProps {
|
||||
titleIcon?: IconDefinition;
|
||||
title: string;
|
||||
subText: string | React.ReactNode;
|
||||
full?: boolean;
|
||||
showButton?: boolean;
|
||||
extension?: React.ReactNode;
|
||||
buttonLabel?: string;
|
||||
buttonVariant?: "primary" | "secondary" | "danger";
|
||||
buttonTo?: string;
|
||||
}
|
||||
|
||||
export default function PageHeader({
|
||||
actionButtons,
|
||||
childProps,
|
||||
showButtons,
|
||||
subText,
|
||||
titleComponent: Title,
|
||||
title,
|
||||
titleIcon,
|
||||
}: Props) {
|
||||
title,
|
||||
subText,
|
||||
full,
|
||||
showButton,
|
||||
extension,
|
||||
buttonLabel,
|
||||
buttonVariant = "primary",
|
||||
buttonTo,
|
||||
}: PageHeaderProps) {
|
||||
const icon = (
|
||||
<>{titleIcon && <FontAwesomeIcon icon={titleIcon} className="me-3" />}</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="container-panel hero-section py-4">
|
||||
<div className="panel">
|
||||
<div className="p-3 py-4 p-md-4">
|
||||
{Title ? (
|
||||
<Title {...childProps} />
|
||||
) : (
|
||||
<h1 className="fw-bold text-center text-md-start">
|
||||
{titleIcon}
|
||||
{title}
|
||||
</h1>
|
||||
)}
|
||||
<p className="text-center text-md-start pt-1">{subText}</p>
|
||||
{showButtons && (
|
||||
<div className="d-flex flex-column flex-md-row gap-3 justify-content-center justify-content-md-start mt-4">
|
||||
{actionButtons}
|
||||
<div className="pt-3 pb-4 pt-lg-4">
|
||||
<Panel padding full={full}>
|
||||
<div className="d-flex flex-column gap-4">
|
||||
<div>
|
||||
<div className="d-flex">
|
||||
<h2 className="fw-bold flex-grow-1">
|
||||
{icon}
|
||||
{title}
|
||||
</h2>
|
||||
{showButton && (
|
||||
<Button
|
||||
variant={buttonVariant}
|
||||
label={buttonLabel}
|
||||
to={buttonTo}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<p>{subText}</p>
|
||||
</div>
|
||||
{extension && <div>{extension}</div>}
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// export default PageHeader;
|
||||
@@ -156,7 +156,7 @@ export default function SideNav(props: SideNavProps) {
|
||||
<Button
|
||||
label={t("loginButton")}
|
||||
small
|
||||
secondary
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
history.push("/login");
|
||||
handleNavLinkClick();
|
||||
@@ -194,7 +194,7 @@ export default function SideNav(props: SideNavProps) {
|
||||
icon={faUser}
|
||||
label={t("profileButton")}
|
||||
small
|
||||
secondary
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
history.push(url);
|
||||
handleNavLinkClick();
|
||||
@@ -209,7 +209,7 @@ export default function SideNav(props: SideNavProps) {
|
||||
icon={faSignOutAlt}
|
||||
label={t("logOutButton")}
|
||||
small
|
||||
danger
|
||||
variant="danger"
|
||||
onClick={async () => {
|
||||
await logoutHandler();
|
||||
handleNavLinkClick();
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import PageHeader from './PageHeader';
|
||||
|
||||
export {
|
||||
PageHeader,
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./useSearch";
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useState, useMemo } from "react";
|
||||
|
||||
export default function useSearch(initialData: any[]) {
|
||||
const [searchTerm, setSearchTerm] = useState<string>("");
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
let tempData = [...initialData];
|
||||
|
||||
if (searchTerm) {
|
||||
tempData = tempData.filter((item) =>
|
||||
item.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedTags.length) {
|
||||
tempData = tempData.filter((item) =>
|
||||
selectedTags.every((tag) => item.tags.includes(tag))
|
||||
);
|
||||
}
|
||||
|
||||
return tempData;
|
||||
}, [searchTerm, selectedTags, initialData]);
|
||||
|
||||
return {
|
||||
searchTerm,
|
||||
setSearchTerm,
|
||||
selectedTags,
|
||||
setSelectedTags,
|
||||
filteredData,
|
||||
};
|
||||
}
|
||||
@@ -782,7 +782,7 @@ styles with important tags:
|
||||
}
|
||||
|
||||
.select-placeholder {
|
||||
color: $white !important;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.select-container {
|
||||
|
||||
@@ -1,17 +1,91 @@
|
||||
import React from "react";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import Container from "components/common/Container";
|
||||
import Panel from "components/common/Panel/Panel";
|
||||
import PageHeader from "components/layout/PageHeader";
|
||||
import { faMessageDots } from "@fortawesome/pro-solid-svg-icons";
|
||||
import ModelSearch from "components/common/ModelSearch";
|
||||
import Panel from "components/common/Panel";
|
||||
import useSearch from "hooks/useSearch/useSearch";
|
||||
import ModelSearchResults from "components/common/ModelSearchResults";
|
||||
import ModelTags from "components/common/ModelTags";
|
||||
|
||||
interface TtsModelSearchPageProps {
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
const dummyData = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Spongebob",
|
||||
tags: ["English", "High-pitched", "Character"],
|
||||
type: "Tacotron2",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Mariano Closs",
|
||||
tags: ["Spanish", "Low-pitched"],
|
||||
type: "Tacotron2",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Cristiano Ronaldo",
|
||||
tags: ["Portuguese", "Low-pitched"],
|
||||
type: "Tacotron2",
|
||||
},
|
||||
];
|
||||
|
||||
const allTags = [
|
||||
"English",
|
||||
"Spanish",
|
||||
"Portuguese",
|
||||
"High-pitched",
|
||||
"Low-pitched",
|
||||
"Character",
|
||||
];
|
||||
|
||||
export default function TtsModelSearchPage(props: TtsModelSearchPageProps) {
|
||||
const {
|
||||
searchTerm,
|
||||
setSearchTerm,
|
||||
selectedTags,
|
||||
setSelectedTags,
|
||||
filteredData,
|
||||
} = useSearch(dummyData);
|
||||
|
||||
const handleSelectTag = (tag: string) => {
|
||||
setSelectedTags((prev) => {
|
||||
if (prev.includes(tag)) {
|
||||
return prev.filter((t) => t !== tag);
|
||||
}
|
||||
return [...prev, tag];
|
||||
});
|
||||
};
|
||||
|
||||
const searchTts = (
|
||||
<div className="d-flex flex-column gap-3">
|
||||
<ModelSearch value={searchTerm} onChange={setSearchTerm} />
|
||||
<ModelTags
|
||||
tags={allTags}
|
||||
selectedTags={selectedTags}
|
||||
onSelectTag={handleSelectTag}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Container type="panel">
|
||||
<PageHeader
|
||||
title="Text to Speech"
|
||||
titleIcon={faMessageDots}
|
||||
subText="Turn text into your favorite character's speaking voice."
|
||||
showButton={true}
|
||||
buttonLabel="Create a voice"
|
||||
buttonVariant="secondary"
|
||||
buttonTo="/voice-designer"
|
||||
extension={searchTts}
|
||||
/>
|
||||
<Panel padding={true}>
|
||||
<div>test</div>
|
||||
<ModelSearchResults data={filteredData} />
|
||||
</Panel>
|
||||
</Container>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user