overhaul tags

This commit is contained in:
jessop 2020-03-06 17:11:40 -05:00
parent f7d31bda33
commit c630482eab
6 changed files with 82 additions and 35 deletions

View file

@ -1010,5 +1010,10 @@
"Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.": "Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.", "Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.": "Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.",
"Add relevant tags...": "Add relevant tags...", "Add relevant tags...": "Add relevant tags...",
"Enter up to five (5) tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.": "Enter up to five (5) tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.", "Enter up to five (5) tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.": "Enter up to five (5) tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.",
"Sorry, your request timed out. Modify your options or %again%": "Sorry, your request timed out. Modify your options or %again%" "Sorry, your request timed out. Modify your options or %again%": "Sorry, your request timed out. Modify your options or %again%",
"gaming, crypto": "gaming, crypto",
"Autocomplete": "Autocomplete",
"Followed Tags": "Followed Tags",
"Add tags that are relevant to your content. If mature content, ensure it is tagged mature. Tag abuse and missing mature tags will not be tolerated.": "Add tags that are relevant to your content. If mature content, ensure it is tagged mature. Tag abuse and missing mature tags will not be tolerated.",
"%selectTagsLabel% (%number% left)": "%selectTagsLabel% (%number% left)"
} }

View file

@ -90,7 +90,6 @@ function PublishForm(props: Props) {
const TAGS_LIMIT = 5; const TAGS_LIMIT = 5;
const formDisabled = (!filePath && !editingURI) || publishing; const formDisabled = (!filePath && !editingURI) || publishing;
const isInProgress = filePath || editingURI || name || title; const isInProgress = filePath || editingURI || name || title;
const tagsCount = tags && tags.length;
// If they are editing, they don't need a new file chosen // If they are editing, they don't need a new file chosen
const formValidLessFile = const formValidLessFile =
@ -156,14 +155,14 @@ function PublishForm(props: Props) {
empty={__('No tags added')} empty={__('No tags added')}
limit={TAGS_LIMIT} limit={TAGS_LIMIT}
help={__( help={__(
'Enter up to five (5) tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.' 'Add tags that are relevant to your content. If mature content, ensure it is tagged mature. Tag abuse and missing mature tags will not be tolerated.'
)} )}
placeholder={__('Add relevant tags...')} placeholder={__('gaming, crypto')}
onSelect={newTags => { onSelect={newTags => {
const validatedTags = []; const validatedTags = [];
newTags.forEach(newTag => { newTags.forEach(newTag => {
if (!tags.some(tag => tag.name === newTag.name)) { if (!tags.some(tag => tag.name === newTag.name)) {
if (tagsCount + validatedTags.length < TAGS_LIMIT) validatedTags.push(newTag); validatedTags.push(newTag);
} }
}); });
updatePublishForm({ tags: [...tags, ...validatedTags] }); updatePublishForm({ tags: [...tags, ...validatedTags] });

View file

@ -31,6 +31,7 @@ export default function Tag(props: Props) {
disabled={disabled} disabled={disabled}
title={title} title={title}
className={classnames('tag', { className={classnames('tag', {
'tag--disabled': disabled === true,
'tag--large': type === 'large', 'tag--large': type === 'large',
'tag--remove': type === 'remove', 'tag--remove': type === 'remove',
// tag--add only adjusts the color, which causes issues with mature tag color clashing // tag--add only adjusts the color, which causes issues with mature tag color clashing

View file

@ -3,6 +3,7 @@ import React, { useState } from 'react';
import { Form, FormField } from 'component/common/form'; import { Form, FormField } from 'component/common/form';
import Tag from 'component/tag'; import Tag from 'component/tag';
import { setUnion, setDifference } from 'util/set-operations'; import { setUnion, setDifference } from 'util/set-operations';
import I18nMessage from '../i18nMessage';
type Props = { type Props = {
tagsPassedIn: Array<Tag>, tagsPassedIn: Array<Tag>,
@ -17,6 +18,7 @@ type Props = {
placeholder?: string, placeholder?: string,
label?: string, label?: string,
disabled?: boolean, disabled?: boolean,
limit?: number,
}; };
/* /*
@ -40,6 +42,7 @@ export default function TagsSearch(props: Props) {
placeholder, placeholder,
label, label,
disabled, disabled,
limit,
} = props; } = props;
const [newTag, setNewTag] = useState(''); const [newTag, setNewTag] = useState('');
const doesTagMatch = name => { const doesTagMatch = name => {
@ -56,6 +59,8 @@ export default function TagsSearch(props: Props) {
const remainingFollowedTagsSet = setDifference(followedTagsSet, selectedTagsSet); const remainingFollowedTagsSet = setDifference(followedTagsSet, selectedTagsSet);
const suggestedTagsSet = setUnion(remainingFollowedTagsSet, unfollowedTagsSet); const suggestedTagsSet = setUnion(remainingFollowedTagsSet, unfollowedTagsSet);
const countWithoutMature = selectedTagsSet.has('mature') ? selectedTagsSet.size - 1 : selectedTagsSet.size;
const maxed = Boolean(limit && countWithoutMature >= limit);
const suggestedTags = Array.from(suggestedTagsSet) const suggestedTags = Array.from(suggestedTagsSet)
.filter(doesTagMatch) .filter(doesTagMatch)
.slice(0, 5); .slice(0, 5);
@ -110,38 +115,70 @@ export default function TagsSearch(props: Props) {
return ( return (
<React.Fragment> <React.Fragment>
<Form className="tags__input-wrapper" onSubmit={handleSubmit}> <Form className="tags__input-wrapper" onSubmit={handleSubmit}>
<label>{label || __('Following')}</label> <label>
<ul className="tags--remove"> {limit ? (
{tagsPassedIn.map(tag => ( <I18nMessage
<Tag tokens={{
key={`passed${tag.name}`} number: 5 - countWithoutMature,
name={tag.name} selectTagsLabel: label,
type="remove"
onClick={() => {
onRemove(tag);
}} }}
/> >
))} %selectTagsLabel% (%number% left)
<li> </I18nMessage>
<FormField ) : (
autoFocus={!disableAutoFocus} label || __('Following')
className="tag__input" )}
onChange={onChange} </label>
placeholder={placeholder || __('Follow more tags')} <ul className="tags--remove">
type="text" {!tagsPassedIn.length && <Tag key={`placeholder-tag`} name={'example'} disabled type={'remove'} />}
value={newTag} {Boolean(tagsPassedIn.length) &&
disabled={disabled} tagsPassedIn.map(tag => (
/> <Tag
</li> key={`passed${tag.name}`}
name={tag.name}
type="remove"
onClick={() => {
onRemove(tag);
}}
/>
))}
</ul> </ul>
<FormField
autoFocus={!disableAutoFocus}
className="tag__input"
onChange={onChange}
placeholder={placeholder || __('gaming, crypto')}
type="text"
value={newTag}
disabled={disabled}
label={'Add Tags'}
/>
<section>
<label>{newTag.length ? __('Autocomplete') : __('Followed Tags')}</label>
<ul className="tags">
{Boolean(newTag.length) && !suggestedTags.includes(newTag) && (
<Tag
disabled={newTag !== 'mature' && maxed}
key={`entered${newTag}`}
name={newTag}
type="add"
onClick={newTag.includes('') ? e => handleSubmit(e) : e => handleTagClick(newTag)}
/>
)}
{suggestedTags.map(tag => (
<Tag
disabled={tag !== 'mature' && maxed}
key={`suggested${tag}`}
name={tag}
type="add"
onClick={() => handleTagClick(tag)}
/>
))}
{!suggestedTags.length && <p className="empty tags__empty-message">No suggested tags</p>}
</ul>
</section>
</Form> </Form>
<label>{__('Suggested')}</label>
<ul className="tags">
{suggestedTags.map(tag => (
<Tag disabled={disabled} key={`suggested${tag}`} name={tag} type="add" onClick={() => handleTagClick(tag)} />
))}
{!suggestedTags.length && <p className="empty tags__empty-message">No suggested tags</p>}
</ul>
</React.Fragment> </React.Fragment>
); );
} }

View file

@ -107,7 +107,7 @@ export default function TagsSelect(props: Props) {
disableAutoFocus={disableAutoFocus} disableAutoFocus={disableAutoFocus}
tagsPassedIn={tagsToDisplay} tagsPassedIn={tagsToDisplay}
placeholder={placeholder} placeholder={placeholder}
disabled={limit && tagCount >= limit} limit={limit}
/> />
</React.Fragment> </React.Fragment>
} }

View file

@ -43,6 +43,7 @@
height: var(--tag-height); height: var(--tag-height);
padding: calc(var(--spacing-miniscule) + 1px) var(--spacing-small); padding: calc(var(--spacing-miniscule) + 1px) var(--spacing-small);
background-color: var(--color-input-bg); background-color: var(--color-input-bg);
margin-bottom: var(--spacing-medium);
} }
} }
@ -55,6 +56,10 @@
max-width: 20rem; max-width: 20rem;
} }
.tag--disabled {
opacity: 0.3;
}
.tag--large { .tag--large {
height: var(--height-input); height: var(--height-input);
padding: 0 var(--spacing-s); padding: 0 var(--spacing-s);