mirror of
https://github.com/filecoin-project/slate.git
synced 2024-12-27 11:03:23 +03:00
feat: add input tag to data object
This commit is contained in:
parent
743fa736e9
commit
d692974fd7
@ -59,6 +59,8 @@ export const system = {
|
||||
bgBlurGrayBlack: "rgba(15, 14, 18, 0.8)",
|
||||
bgBlurBlack: "rgba(15, 14, 18, 0.9)",
|
||||
active: "#00BB00",
|
||||
blurBlack: "#262626",
|
||||
bgBlurGray: "#403F42",
|
||||
};
|
||||
|
||||
export const shadow = {
|
||||
|
@ -16,6 +16,7 @@ import { SlatePicker } from "~/components/core/SlatePicker";
|
||||
import { Input } from "~/components/system/components/Input";
|
||||
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
||||
import { Toggle } from "~/components/system/components/Toggle";
|
||||
import { Tag } from "~/components/system/components/Tag";
|
||||
|
||||
const DEFAULT_BOOK =
|
||||
"https://slate.textile.io/ipfs/bafkreibk32sw7arspy5kw3p5gkuidfcwjbwqyjdktd5wkqqxahvkm2qlyi";
|
||||
@ -260,6 +261,11 @@ class CarouselSidebarData extends React.Component {
|
||||
unsavedChanges: false,
|
||||
isEditing: false,
|
||||
isDownloading: false,
|
||||
tags:
|
||||
!Array.isArray(this.props.data.tags) || this.props.data.tags?.length === 0
|
||||
? []
|
||||
: this.props.data.tags,
|
||||
suggestions: [],
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
@ -279,6 +285,8 @@ class CarouselSidebarData extends React.Component {
|
||||
}
|
||||
this.setState({ selected, inPublicSlates, isPublic: this.props.data.public });
|
||||
}
|
||||
|
||||
this.fetchSuggestions();
|
||||
};
|
||||
|
||||
_handleDarkMode = async (e) => {
|
||||
@ -296,8 +304,8 @@ class CarouselSidebarData extends React.Component {
|
||||
};
|
||||
|
||||
_handleSave = async () => {
|
||||
let name = { name: this.state.name };
|
||||
this.props.onSave(name, this.props.index);
|
||||
let data = { name: this.state.name, tags: this.state.tags };
|
||||
this.props.onSave(data, this.props.index);
|
||||
await setTimeout(() => {
|
||||
this.setState({ unsavedChanges: false });
|
||||
}, 500);
|
||||
@ -458,6 +466,23 @@ class CarouselSidebarData extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
_handleTagDelete = async (tag) => {
|
||||
const response = await Actions.deleteTag({ tag });
|
||||
|
||||
if (response.success) {
|
||||
this.setState({ suggestions: response.tags });
|
||||
}
|
||||
|
||||
if (Events.hasError(response)) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
fetchSuggestions = async () => {
|
||||
const res = await Actions.getTagsByUserId();
|
||||
this.setState({ suggestions: res.tags });
|
||||
};
|
||||
|
||||
render() {
|
||||
const isVisible = this.state.inPublicSlates || this.state.isPublic;
|
||||
const { cid, file, name, coverImage, type, size, url, blurhash } = this.props.data;
|
||||
@ -547,6 +572,27 @@ class CarouselSidebarData extends React.Component {
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{this.props.isOwner ? (
|
||||
<React.Fragment>
|
||||
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
|
||||
Tags
|
||||
</div>
|
||||
<div css={STYLES_OPTIONS_SECTION}>
|
||||
<Tag
|
||||
type="dark"
|
||||
name="tags"
|
||||
placeholder={`Edit tags for ${this.state.name}`}
|
||||
tags={this.state.tags}
|
||||
suggestions={this.state.suggestions}
|
||||
style={{ margin: "0 0 16px" }}
|
||||
inputStyles={{ padding: "16px" }}
|
||||
dropdownStyles={{ top: "50px" }}
|
||||
onChange={this._handleChange}
|
||||
handleTagDelete={this._handleTagDelete}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
) : null}
|
||||
{this.props.external ? null : (
|
||||
<React.Fragment>
|
||||
<div css={STYLES_SECTION_HEADER}>Connected Slates</div>
|
||||
|
@ -12,6 +12,7 @@ import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
import { SlatePicker } from "~/components/core/SlatePicker";
|
||||
import { Input } from "~/components/system/components/Input";
|
||||
import { Textarea } from "~/components/system/components/Textarea";
|
||||
import { Tag } from "~/components/system/components/Tag";
|
||||
|
||||
import ProcessedText from "~/components/core/ProcessedText";
|
||||
|
||||
@ -186,6 +187,11 @@ export default class CarouselSidebarSlate extends React.Component {
|
||||
body: Strings.isEmpty(this.props.data.body) ? "" : this.props.data.body,
|
||||
source: Strings.isEmpty(this.props.data.source) ? "" : this.props.data.source,
|
||||
author: Strings.isEmpty(this.props.data.author) ? "" : this.props.data.author,
|
||||
tags:
|
||||
!Array.isArray(this.props.data.tags) || this.props.data.tags?.length === 0
|
||||
? []
|
||||
: this.props.data.tags,
|
||||
suggestions: [],
|
||||
selected: {},
|
||||
isPublic: false,
|
||||
copyValue: "",
|
||||
@ -213,6 +219,8 @@ export default class CarouselSidebarSlate extends React.Component {
|
||||
}
|
||||
this.setState({ selected, isPublic });
|
||||
}
|
||||
|
||||
this.fetchSuggestions();
|
||||
};
|
||||
|
||||
_handleClose = () => {
|
||||
@ -228,6 +236,7 @@ export default class CarouselSidebarSlate extends React.Component {
|
||||
body: this.state.body,
|
||||
source: this.state.source,
|
||||
author: this.state.author,
|
||||
tags: this.state.tags,
|
||||
};
|
||||
this.props.onSave(data, this.props.index);
|
||||
this.setState({ unsavedChanges: false });
|
||||
@ -321,6 +330,23 @@ export default class CarouselSidebarSlate extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
_handleTagDelete = async (tag) => {
|
||||
const response = await Actions.deleteTag({ tag });
|
||||
|
||||
if (response.success) {
|
||||
this.setState({ suggestions: response.tags });
|
||||
}
|
||||
|
||||
if (Events.hasError(response)) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
fetchSuggestions = async () => {
|
||||
const res = await Actions.getTagsByUserId();
|
||||
this.setState({ suggestions: res.tags });
|
||||
};
|
||||
|
||||
render() {
|
||||
let isUnityGame = false;
|
||||
if (this.props.data.type === "application/unity") {
|
||||
@ -350,6 +376,18 @@ export default class CarouselSidebarSlate extends React.Component {
|
||||
onChange={this._handleChange}
|
||||
style={STYLES_INPUT}
|
||||
/>
|
||||
<Tag
|
||||
type="dark"
|
||||
name="tags"
|
||||
placeholder={`Edit tags for ${this.state.title}`}
|
||||
tags={this.state.tags}
|
||||
suggestions={this.state.suggestions}
|
||||
style={{ margin: "0 0 16px" }}
|
||||
inputStyles={{ padding: "16px" }}
|
||||
dropdownStyles={{ top: "50px" }}
|
||||
onChange={this._handleChange}
|
||||
handleTagDelete={this._handleTagDelete}
|
||||
/>
|
||||
<Input
|
||||
full
|
||||
value={this.state.source}
|
||||
|
@ -53,9 +53,8 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
suggestions: [],
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
const res = await Actions.getTagsByUserId();
|
||||
this.setState({ suggestions: res.tags });
|
||||
componentDidMount() {
|
||||
this.fetchSuggestions();
|
||||
}
|
||||
|
||||
_handleSubmit = async () => {
|
||||
@ -131,6 +130,11 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
fetchSuggestions = async () => {
|
||||
const res = await Actions.getTagsByUserId();
|
||||
this.setState({ suggestions: res.tags });
|
||||
};
|
||||
|
||||
render() {
|
||||
const slug = Strings.createSlug(this.state.name);
|
||||
const url = `/${this.props.viewer.username}/${slug}`;
|
||||
@ -229,7 +233,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
</System.P>
|
||||
<System.Tag
|
||||
name="tags"
|
||||
placeholder={`Edit tag for ${this.state.slatename}`}
|
||||
placeholder={`Edit tags for ${this.state.slatename}`}
|
||||
tags={this.state.tags}
|
||||
suggestions={this.state.suggestions}
|
||||
style={{ marginTop: 12 }}
|
||||
|
@ -26,17 +26,50 @@ const STYLES_DROPDOWN = css`
|
||||
width: 100%;
|
||||
z-index: 30;
|
||||
box-shadow: 0px 12px 24px rgba(178, 178, 178, 0.3);
|
||||
|
||||
li[data-item-active="true"] {
|
||||
background: ${Constants.system.gray80};
|
||||
|
||||
span,
|
||||
svg {
|
||||
color: ${Constants.system.white};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_DROPDOWN_ITEM = css`
|
||||
const DROPDOWN_ITEM_STYLES = `
|
||||
list-style-type: none;
|
||||
padding: 8px 12px;
|
||||
background: ${Constants.system.white};
|
||||
border: 0.5px solid ${Constants.system.gray20};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: ${Constants.system.textGray};
|
||||
font-family: ${Constants.font.text};
|
||||
margin: 0 0 0 8px;
|
||||
}
|
||||
|
||||
|
||||
div.dismiss {
|
||||
opacity: 0;
|
||||
|
||||
&:hover {
|
||||
color: ${Constants.system.newBlack};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
`;
|
||||
|
||||
const STYLES_DROPDOWN_ITEM = css`
|
||||
${DROPDOWN_ITEM_STYLES};
|
||||
|
||||
background: ${Constants.system.white};
|
||||
border: 0.5px solid ${Constants.system.gray20};
|
||||
|
||||
&:hover {
|
||||
background: ${Constants.system.gray10};
|
||||
|
||||
@ -50,25 +83,40 @@ const STYLES_DROPDOWN_ITEM = css`
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: ${Constants.system.textGray};
|
||||
font-family: ${Constants.font.text};
|
||||
margin: 0 0 0 8px;
|
||||
div.active {
|
||||
background: linear-gradient(105.04deg, #e3e3e3 7.95%, #f3f3f3 92.61%);
|
||||
border-radius: 50px;
|
||||
}
|
||||
`;
|
||||
|
||||
div.dismiss {
|
||||
opacity: 0;
|
||||
const STYLES_DROPDOWN_ITEM_DARK = css`
|
||||
${DROPDOWN_ITEM_STYLES};
|
||||
|
||||
&:hover {
|
||||
color: ${Constants.system.newBlack};
|
||||
background: ${Constants.system.bgBlurGray};
|
||||
border: 0.5px solid ${Constants.system.bgBlurGray};
|
||||
|
||||
&:hover {
|
||||
background: ${Constants.system.gray80};
|
||||
|
||||
span,
|
||||
div:not(.dismiss) {
|
||||
color: ${Constants.system.white};
|
||||
}
|
||||
|
||||
div.dismiss {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
div.active {
|
||||
background: linear-gradient(105.04deg, #e3e3e3 7.95%, #f3f3f3 92.61%);
|
||||
background: linear-gradient(90deg, rgba(134, 134, 136, 1) 0%, rgba(134, 134, 136, 0.2) 80%);
|
||||
border-radius: 50px;
|
||||
|
||||
span {
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -88,11 +136,9 @@ const STYLES_DROPDOWN_ITEM_ICON = css`
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_DROPDOWN_ADD_ITEM = css`
|
||||
const DROPDOWN_ITEM_ADD_STYLES = `
|
||||
list-style-type: none;
|
||||
padding: 8px 12px;
|
||||
background: ${Constants.system.white};
|
||||
border: 0.5px solid ${Constants.system.gray20};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -100,9 +146,19 @@ const STYLES_DROPDOWN_ADD_ITEM = css`
|
||||
span {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: ${Constants.system.newBlack};
|
||||
font-family: ${Constants.font.text};
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_DROPDOWN_ADD_ITEM = css`
|
||||
${DROPDOWN_ITEM_ADD_STYLES};
|
||||
|
||||
background: ${Constants.system.white};
|
||||
border: 0.5px solid ${Constants.system.gray20};
|
||||
|
||||
span {
|
||||
color: ${Constants.system.newBlack};
|
||||
}
|
||||
|
||||
span.value {
|
||||
background: ${Constants.system.bgGray};
|
||||
@ -117,14 +173,39 @@ const STYLES_DROPDOWN_ADD_ITEM = css`
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_DROPDOWN_ADD_ITEM_DARK = css`
|
||||
${DROPDOWN_ITEM_ADD_STYLES};
|
||||
|
||||
background: ${Constants.system.bgBlurGray};
|
||||
border: 0.5px solid ${Constants.system.bgBlurGray};
|
||||
|
||||
span,
|
||||
svg {
|
||||
color: ${Constants.system.textGray};
|
||||
}
|
||||
|
||||
span.value {
|
||||
background: ${Constants.system.gray70};
|
||||
color: ${Constants.system.white};
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: ${Constants.system.gray80};
|
||||
|
||||
span,
|
||||
svg {
|
||||
color: ${Constants.system.white};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const INPUT_STYLES = `
|
||||
box-sizing: border-box;
|
||||
font-family: ${Constants.font.text};
|
||||
-webkit-appearance: none;
|
||||
background: ${Constants.system.white};
|
||||
color: ${Constants.system.black};
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
@ -138,10 +219,11 @@ const STYLES_INPUT = css`
|
||||
${INPUT_STYLES};
|
||||
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 0 0 1px ${Constants.system.gray30} inset;
|
||||
background: ${Constants.system.white};
|
||||
color: ${Constants.system.black};
|
||||
|
||||
:focus {
|
||||
outline: 0;
|
||||
@ -166,6 +248,17 @@ const STYLES_INPUT = css`
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_INPUT_DARK = css`
|
||||
${INPUT_STYLES};
|
||||
|
||||
width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 0 0 1px #3c3c3c inset;
|
||||
background: ${Constants.system.blurBlack};
|
||||
color: ${Constants.system.white};
|
||||
`;
|
||||
|
||||
const STYLES_LIST = css`
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
@ -173,11 +266,9 @@ const STYLES_LIST = css`
|
||||
border-radius: 4px;
|
||||
`;
|
||||
|
||||
const STYLES_TAG = css`
|
||||
const TAG_STYLES = `
|
||||
list-style-type: none;
|
||||
border-radius: 4px;
|
||||
background: ${Constants.system.bgGray};
|
||||
color: ${Constants.system.newBlack};
|
||||
font-family: ${Constants.font.text};
|
||||
padding: 2px 8px;
|
||||
margin: 8px 8px 0 0;
|
||||
@ -186,12 +277,30 @@ const STYLES_TAG = css`
|
||||
line-height: 1.5;
|
||||
font-size: 14px;
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_TAG = css`
|
||||
${TAG_STYLES};
|
||||
|
||||
background: ${Constants.system.bgGray};
|
||||
color: ${Constants.system.newBlack};
|
||||
|
||||
&:hover {
|
||||
background: ${Constants.system.gray30};
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_TAG_DARK = css`
|
||||
${TAG_STYLES};
|
||||
|
||||
background: ${Constants.system.gray80};
|
||||
color: ${Constants.system.textGray};
|
||||
|
||||
&:hover {
|
||||
background: ${Constants.system.gray80};
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_SHOW_MORE = css`
|
||||
font-family: ${Constants.font.text};
|
||||
color: ${Constants.system.textGray};
|
||||
@ -252,11 +361,13 @@ const DeleteConfirmation = ({ tag, handleDelete }) => {
|
||||
};
|
||||
|
||||
const Dropdown = ({
|
||||
type,
|
||||
open,
|
||||
setOpen,
|
||||
tags,
|
||||
value,
|
||||
suggestions,
|
||||
dropdownStyles,
|
||||
handleAdd,
|
||||
handleRemove,
|
||||
handleTagDelete,
|
||||
@ -293,9 +404,16 @@ const Dropdown = ({
|
||||
|
||||
return (
|
||||
<div ref={dropdownEl}>
|
||||
<ul css={STYLES_DROPDOWN} style={{ display: open ? "block" : "none" }}>
|
||||
<ul
|
||||
css={STYLES_DROPDOWN}
|
||||
style={{
|
||||
display: open ? "block" : "none",
|
||||
boxShadow: type === "dark" && "0px 12px 24px rgba(0, 0, 0, 0.2)",
|
||||
...dropdownStyles,
|
||||
}}
|
||||
>
|
||||
{!suggestions.length ? (
|
||||
<li css={STYLES_DROPDOWN_ITEM}>
|
||||
<li css={type === "dark" ? STYLES_DROPDOWN_ITEM_DARK : STYLES_DROPDOWN_ITEM}>
|
||||
<LoaderSpinner style={{ height: "24px", width: "24px", margin: "0 auto" }} />
|
||||
</li>
|
||||
) : (
|
||||
@ -303,8 +421,9 @@ const Dropdown = ({
|
||||
{(filteredTags || []).map((tag, index) => (
|
||||
<li
|
||||
key={tag}
|
||||
css={STYLES_DROPDOWN_ITEM}
|
||||
css={type === "dark" ? STYLES_DROPDOWN_ITEM_DARK : STYLES_DROPDOWN_ITEM}
|
||||
onClick={isActiveTag(index) ? () => handleRemove(tag) : () => handleAdd(tag)}
|
||||
data-item-active={isActiveTag(index)}
|
||||
>
|
||||
{isActiveTag(index) && (
|
||||
<div css={STYLES_DROPDOWN_ITEM_ICON}>
|
||||
@ -330,7 +449,7 @@ const Dropdown = ({
|
||||
</li>
|
||||
))}
|
||||
<li
|
||||
css={STYLES_DROPDOWN_ADD_ITEM}
|
||||
css={type === "dark" ? STYLES_DROPDOWN_ADD_ITEM_DARK : STYLES_DROPDOWN_ADD_ITEM}
|
||||
onClick={() => {
|
||||
if (value) {
|
||||
handleAdd(value);
|
||||
@ -341,7 +460,11 @@ const Dropdown = ({
|
||||
<SVG.Plus height="16px" />
|
||||
<span style={{ margin: "0 8px" }}>create new tag</span>
|
||||
{value && (
|
||||
<span css={STYLES_TAG} className="value" style={{ margin: 0 }}>
|
||||
<span
|
||||
css={type === "dark" ? STYLES_TAG_DARK : STYLES_TAG}
|
||||
className="value"
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
)}
|
||||
@ -354,10 +477,13 @@ const Dropdown = ({
|
||||
};
|
||||
|
||||
export const Tag = ({
|
||||
type,
|
||||
name,
|
||||
tags,
|
||||
suggestions = [],
|
||||
style,
|
||||
inputStyles,
|
||||
dropdownStyles,
|
||||
placeholder,
|
||||
handleTagDelete,
|
||||
onChange,
|
||||
@ -401,18 +527,21 @@ export const Tag = ({
|
||||
<input
|
||||
name={name}
|
||||
type="text"
|
||||
css={STYLES_INPUT}
|
||||
css={type === "dark" ? STYLES_INPUT_DARK : STYLES_INPUT}
|
||||
style={{ ...inputStyles }}
|
||||
placeholder={placeholder ? placeholder : null}
|
||||
value={value}
|
||||
onChange={_handleChange}
|
||||
onFocus={_handleFocus}
|
||||
/>
|
||||
<Dropdown
|
||||
type={type}
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
tags={tags}
|
||||
suggestions={suggestions}
|
||||
value={value}
|
||||
dropdownStyles={dropdownStyles}
|
||||
handleAdd={_handleAdd}
|
||||
handleRemove={_handleRemove}
|
||||
handleTagDelete={handleTagDelete}
|
||||
@ -422,7 +551,7 @@ export const Tag = ({
|
||||
<ul css={STYLES_LIST}>
|
||||
{tags &&
|
||||
tags.slice(0, numToDisplay).map((tag) => (
|
||||
<li key={tag} css={STYLES_TAG}>
|
||||
<li key={tag} css={type === "dark" ? STYLES_TAG_DARK : STYLES_TAG}>
|
||||
<span>{tag}</span>
|
||||
</li>
|
||||
))}
|
||||
|
Loading…
Reference in New Issue
Block a user