Skip to content

Commit

Permalink
Desktop: Improved tag dialog to make it easier to add and remove tags (
Browse files Browse the repository at this point in the history
  • Loading branch information
CalebJohn authored and laurent22 committed Jun 7, 2019
1 parent 9f6b3cc commit 799ad5f
Show file tree
Hide file tree
Showing 4 changed files with 451 additions and 22 deletions.
11 changes: 7 additions & 4 deletions ElectronClient/app/gui/MainScreen.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,19 @@ class MainScreenComponent extends React.Component {
});
} else if (command.name === 'setTags') {
const tags = await Tag.tagsByNoteId(command.noteId);
const tagTitles = tags.map((a) => { return a.title }).sort();
const noteTags = tags.map((a) => { return {value: a.id, label: a.title } }).sort((a, b) => { return a.label.localeCompare(b.label); });
const allTags = await Tag.allWithNotes();
const tagSuggestions = allTags.map((a) => { return {value: a.id, label: a.title } });

this.setState({
promptOptions: {
label: _('Add or remove tags:'),
description: _('Separate each tag by a comma.'),
value: tagTitles.join(', '),
inputType: 'tags',
value: noteTags,
autocomplete: tagSuggestions,
onClose: async (answer) => {
if (answer !== null) {
const tagTitles = answer.split(',').map((a) => { return a.trim() });
const tagTitles = answer.map((a) => { return a.label.trim() });
await Tag.setNoteTagsByTitles(command.noteId, tagTitles);
}
this.setState({ promptOptions: null });
Expand Down
78 changes: 71 additions & 7 deletions ElectronClient/app/gui/PromptDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@ const moment = require('moment');
const { themeStyle } = require('../theme.js');
const { time } = require('lib/time-utils.js');
const Datetime = require('react-datetime');
const CreatableSelect = require('react-select/lib/Creatable').default;
const makeAnimated = require('react-select/lib/animated').default;

class PromptDialog extends React.Component {

constructor() {
super();

this.answerInput_ = React.createRef();
}

componentWillMount() {
this.setState({
visible: false,
Expand All @@ -28,7 +36,7 @@ class PromptDialog extends React.Component {
}

componentDidUpdate() {
if (this.focusInput_ && this.answerInput_) this.answerInput_.focus();
if (this.focusInput_ && this.answerInput_.current) this.answerInput_.current.focus();
this.focusInput_ = false;
}

Expand All @@ -53,15 +61,16 @@ class PromptDialog extends React.Component {
height: height - paddingTop,
backgroundColor: 'rgba(0,0,0,0.6)',
display: visible ? 'flex' : 'none',
alignItems: 'flex-start',
justifyContent: 'center',
paddingTop: paddingTop + 'px',
alignItems: 'flex-start',
justifyContent: 'center',
paddingTop: paddingTop + 'px',
};

this.styles_.promptDialog = {
backgroundColor: theme.backgroundColor,
padding: 16,
display: 'inline-block',
maxWidth: width * 0.5,
boxShadow: '6px 6px 20px rgba(0,0,0,0.5)',
};

Expand All @@ -80,7 +89,7 @@ class PromptDialog extends React.Component {
fontSize: theme.fontSize,
color: theme.color,
fontFamily: theme.fontFamily,
verticalAlign: 'top',
verticalAlign: 'middle',
};

this.styles_.input = {
Expand All @@ -92,6 +101,41 @@ class PromptDialog extends React.Component {
borderColor: theme.dividerColor,
};

this.styles_.tagList = {
control: (provided) => (Object.assign(provided, {
minWidth: width * 0.2,
maxWidth: width * 0.5,
})),
input: (provided) => (Object.assign(provided, {
minWidth: '20px',
color: theme.color,
})),
menu: (provided) => (Object.assign(provided, {
color: theme.color,
fontFamily: theme.fontFamily,
backgroundColor: theme.backgroundColor,
})),
multiValueLabel: (provided) => (Object.assign(provided, {
fontFamily: theme.fontFamily,
})),
multiValueRemove: (provided) => (Object.assign(provided, {
color: theme.color,
})),
};

this.styles_.tagListTheme = (tagTheme) => (Object.assign(tagTheme, {
borderRadius: 2,
colors: Object.assign(tagTheme.colors, {
primary: theme.raisedBackgroundColor,
primary25: theme.raisedBackgroundColor,
neutral0: theme.backgroundColor,
neutral10: theme.raisedBackgroundColor,
neutral80: theme.color,
danger: theme.backgroundColor,
dangerLight: theme.colorError2,
}),
}));

this.styles_.desc = Object.assign({}, theme.textStyle, {
marginTop: 10,
});
Expand Down Expand Up @@ -135,8 +179,13 @@ class PromptDialog extends React.Component {
this.setState({ answer: momentObject });
}

const onTagsChange = (newTags) => {
this.setState({ answer: newTags });
this.focusInput_ = true;
}

const onKeyDown = (event) => {
if (event.key === 'Enter') {
if (event.key === 'Enter' && this.props.inputType !== 'tags') {
onClose(true);
} else if (event.key === 'Escape') {
onClose(false);
Expand All @@ -155,10 +204,25 @@ class PromptDialog extends React.Component {
timeFormat={time.timeFormat()}
onChange={(momentObject) => onDateTimeChange(momentObject)}
/>
} else if (this.props.inputType === 'tags') {
inputComp = <CreatableSelect
styles={styles.tagList}
theme={styles.tagListTheme}
ref={this.answerInput_}
value={this.state.answer}
placeholder={this.props.label ? this.props.label.replace(':', '') : ''}
components={makeAnimated()}
isMulti={true}
isClearable={false}
backspaceRemovesValue={true}
options={this.props.autocomplete}
onChange={onTagsChange}
onKeyDown={(event) => onKeyDown(event)}
/>
} else {
inputComp = <input
style={styles.input}
ref={input => this.answerInput_ = input}
ref={this.answerInput_}
value={this.state.answer}
type="text"
onChange={(event) => onChange(event)}
Expand Down
Loading

0 comments on commit 799ad5f

Please sign in to comment.