diff --git a/app/javascript/flavours/glitch/components/autosuggest_emoji.js b/app/javascript/flavours/glitch/components/autosuggest_emoji.js
new file mode 100644
index 0000000000..c8609e48f6
--- /dev/null
+++ b/app/javascript/flavours/glitch/components/autosuggest_emoji.js
@@ -0,0 +1,42 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import unicodeMapping from 'flavours/glitch/util/emoji/emoji_unicode_mapping_light';
+
+const assetHost = process.env.CDN_HOST || '';
+
+export default class AutosuggestEmoji extends React.PureComponent {
+
+ static propTypes = {
+ emoji: PropTypes.object.isRequired,
+ };
+
+ render () {
+ const { emoji } = this.props;
+ let url;
+
+ if (emoji.custom) {
+ url = emoji.imageUrl;
+ } else {
+ const mapping = unicodeMapping[emoji.native] || unicodeMapping[emoji.native.replace(/\uFE0F$/, '')];
+
+ if (!mapping) {
+ return null;
+ }
+
+ url = `${assetHost}/emoji/${mapping.filename}.svg`;
+ }
+
+ return (
+
+
+
+ {emoji.colons}
+
+ );
+ }
+
+}
diff --git a/app/javascript/flavours/glitch/components/autosuggest_textarea.js b/app/javascript/flavours/glitch/components/autosuggest_textarea.js
new file mode 100644
index 0000000000..af8fbe4065
--- /dev/null
+++ b/app/javascript/flavours/glitch/components/autosuggest_textarea.js
@@ -0,0 +1,224 @@
+import React from 'react';
+import AutosuggestAccountContainer from 'flavours/glitch/features/compose/containers/autosuggest_account_container';
+import AutosuggestEmoji from './autosuggest_emoji';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import PropTypes from 'prop-types';
+import { isRtl } from 'flavours/glitch/util/rtl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import Textarea from 'react-textarea-autosize';
+import classNames from 'classnames';
+
+const textAtCursorMatchesToken = (str, caretPosition) => {
+ let word;
+
+ let left = str.slice(0, caretPosition).search(/[^\s\u200B]+$/);
+ let right = str.slice(caretPosition).search(/[\s\u200B]/);
+
+ if (right < 0) {
+ word = str.slice(left);
+ } else {
+ word = str.slice(left, right + caretPosition);
+ }
+
+ if (!word || word.trim().length < 3 || ['@', ':', '#'].indexOf(word[0]) === -1) {
+ return [null, null];
+ }
+
+ word = word.trim().toLowerCase();
+
+ if (word.length > 0) {
+ return [left, word];
+ } else {
+ return [null, null];
+ }
+};
+
+export default class AutosuggestTextarea extends ImmutablePureComponent {
+
+ static propTypes = {
+ value: PropTypes.string,
+ suggestions: ImmutablePropTypes.list,
+ disabled: PropTypes.bool,
+ placeholder: PropTypes.string,
+ onSuggestionSelected: PropTypes.func.isRequired,
+ onSuggestionsClearRequested: PropTypes.func.isRequired,
+ onSuggestionsFetchRequested: PropTypes.func.isRequired,
+ onChange: PropTypes.func.isRequired,
+ onKeyUp: PropTypes.func,
+ onKeyDown: PropTypes.func,
+ onPaste: PropTypes.func.isRequired,
+ autoFocus: PropTypes.bool,
+ };
+
+ static defaultProps = {
+ autoFocus: true,
+ };
+
+ state = {
+ suggestionsHidden: false,
+ selectedSuggestion: 0,
+ lastToken: null,
+ tokenStart: 0,
+ };
+
+ onChange = (e) => {
+ const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart);
+
+ if (token !== null && this.state.lastToken !== token) {
+ this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart });
+ this.props.onSuggestionsFetchRequested(token);
+ } else if (token === null) {
+ this.setState({ lastToken: null });
+ this.props.onSuggestionsClearRequested();
+ }
+
+ this.props.onChange(e);
+ }
+
+ onKeyDown = (e) => {
+ const { suggestions, disabled } = this.props;
+ const { selectedSuggestion, suggestionsHidden } = this.state;
+
+ if (disabled) {
+ e.preventDefault();
+ return;
+ }
+
+ if (e.which === 229 || e.isComposing) {
+ // Ignore key events during text composition
+ // e.key may be a name of the physical key even in this case (e.x. Safari / Chrome on Mac)
+ return;
+ }
+
+ switch(e.key) {
+ case 'Escape':
+ if (suggestions.size === 0 || suggestionsHidden) {
+ document.querySelector('.ui').parentElement.focus();
+ } else {
+ e.preventDefault();
+ this.setState({ suggestionsHidden: true });
+ }
+
+ break;
+ case 'ArrowDown':
+ if (suggestions.size > 0 && !suggestionsHidden) {
+ e.preventDefault();
+ this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) });
+ }
+
+ break;
+ case 'ArrowUp':
+ if (suggestions.size > 0 && !suggestionsHidden) {
+ e.preventDefault();
+ this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) });
+ }
+
+ break;
+ case 'Enter':
+ case 'Tab':
+ // Select suggestion
+ if (this.state.lastToken !== null && suggestions.size > 0 && !suggestionsHidden) {
+ e.preventDefault();
+ e.stopPropagation();
+ this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
+ }
+
+ break;
+ }
+
+ if (e.defaultPrevented || !this.props.onKeyDown) {
+ return;
+ }
+
+ this.props.onKeyDown(e);
+ }
+
+ onBlur = () => {
+ this.setState({ suggestionsHidden: true });
+ }
+
+ onSuggestionClick = (e) => {
+ const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
+ e.preventDefault();
+ this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
+ this.textarea.focus();
+ }
+
+ componentWillReceiveProps (nextProps) {
+ if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden) {
+ this.setState({ suggestionsHidden: false });
+ }
+ }
+
+ setTextarea = (c) => {
+ this.textarea = c;
+ }
+
+ onPaste = (e) => {
+ if (e.clipboardData && e.clipboardData.files.length === 1) {
+ this.props.onPaste(e.clipboardData.files);
+ e.preventDefault();
+ }
+ }
+
+ renderSuggestion = (suggestion, i) => {
+ const { selectedSuggestion } = this.state;
+ let inner, key;
+
+ if (typeof suggestion === 'object') {
+ inner = ;
+ key = suggestion.id;
+ } else if (suggestion[0] === '#') {
+ inner = suggestion;
+ key = suggestion;
+ } else {
+ inner = ;
+ key = suggestion;
+ }
+
+ return (
+
+ {inner}
+
+ );
+ }
+
+ render () {
+ const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus } = this.props;
+ const { suggestionsHidden } = this.state;
+ const style = { direction: 'ltr' };
+
+ if (isRtl(value)) {
+ style.direction = 'rtl';
+ }
+
+ return (
+
+
+
+
+ {suggestions.map(this.renderSuggestion)}
+
+
+ );
+ }
+
+}
diff --git a/app/javascript/flavours/glitch/features/compose/components/autosuggest_account.js b/app/javascript/flavours/glitch/features/compose/components/autosuggest_account.js
new file mode 100644
index 0000000000..fb9bb50358
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/compose/components/autosuggest_account.js
@@ -0,0 +1,24 @@
+import React from 'react';
+import Avatar from 'flavours/glitch/components/avatar';
+import DisplayName from 'flavours/glitch/components/display_name';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+
+export default class AutosuggestAccount extends ImmutablePureComponent {
+
+ static propTypes = {
+ account: ImmutablePropTypes.map.isRequired,
+ };
+
+ render () {
+ const { account } = this.props;
+
+ return (
+
+ );
+ }
+
+}
diff --git a/app/javascript/flavours/glitch/features/compose/components/compose_form.js b/app/javascript/flavours/glitch/features/compose/components/compose_form.js
index ecd1aed695..2ea1379654 100644
--- a/app/javascript/flavours/glitch/features/compose/components/compose_form.js
+++ b/app/javascript/flavours/glitch/features/compose/components/compose_form.js
@@ -7,17 +7,20 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
// Components.
import ComposerOptions from '../../composer/options';
import ComposerPublisher from '../../composer/publisher';
-import ComposerTextarea from '../../composer/textarea';
+import ComposerTextareaIcons from '../../composer/textarea/icons';
import UploadFormContainer from '../containers/upload_form_container';
import PollFormContainer from '../containers/poll_form_container';
import WarningContainer from '../containers/warning_container';
import ReplyIndicatorContainer from '../containers/reply_indicator_container';
+import EmojiPicker from 'flavours/glitch/features/emoji_picker';
+import AutosuggestTextarea from '../../../components/autosuggest_textarea';
// Utils.
import { countableText } from 'flavours/glitch/util/counter';
import { isMobile } from 'flavours/glitch/util/is_mobile';
const messages = defineMessages({
+ placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
missingDescriptionMessage: { id: 'confirmations.missing_media_description.message',
defaultMessage: 'At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.' },
missingDescriptionConfirm: { id: 'confirmations.missing_media_description.confirm',
@@ -183,7 +186,7 @@ class ComposeForm extends ImmutablePureComponent {
}
// Sets a reference to the textarea.
- handleRefTextarea = (textareaComponent) => {
+ setAutosuggestTextarea = (textareaComponent) => {
if (textareaComponent) {
this.textarea = textareaComponent.textarea;
}
@@ -269,6 +272,10 @@ class ComposeForm extends ImmutablePureComponent {
}
}
+ handleChange = (e) => {
+ this.props.onChangeText(e.target.value);
+ }
+
render () {
const {
handleEmoji,
@@ -339,27 +346,35 @@ class ComposeForm extends ImmutablePureComponent {
-
+
+
+
0)}
spoiler={spoilersAlwaysOn ? (spoilerText && spoilerText.length > 0) : spoiler}
/>
+
{
+ const getAccount = makeGetAccount();
+
+ const mapStateToProps = (state, { id }) => ({
+ account: getAccount(state, id),
+ });
+
+ return mapStateToProps;
+};
+
+export default connect(makeMapStateToProps)(AutosuggestAccount);
diff --git a/app/javascript/flavours/glitch/features/composer/textarea/index.js b/app/javascript/flavours/glitch/features/composer/textarea/index.js
deleted file mode 100644
index 50e46fc784..0000000000
--- a/app/javascript/flavours/glitch/features/composer/textarea/index.js
+++ /dev/null
@@ -1,312 +0,0 @@
-// Package imports.
-import PropTypes from 'prop-types';
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import {
- defineMessages,
- FormattedMessage,
-} from 'react-intl';
-import Textarea from 'react-textarea-autosize';
-
-// Components.
-import EmojiPicker from 'flavours/glitch/features/emoji_picker';
-import ComposerTextareaIcons from './icons';
-import ComposerTextareaSuggestions from './suggestions';
-
-// Utils.
-import { isRtl } from 'flavours/glitch/util/rtl';
-import {
- assignHandlers,
- hiddenComponent,
-} from 'flavours/glitch/util/react_helpers';
-
-// Messages.
-const messages = defineMessages({
- placeholder: {
- defaultMessage: 'What is on your mind?',
- id: 'compose_form.placeholder',
- },
-});
-
-// Handlers.
-const handlers = {
-
- // When blurring the textarea, suggestions are hidden.
- handleBlur () {
- this.setState({ suggestionsHidden: true });
- },
-
- // When the contents of the textarea change, we have to pull up new
- // autosuggest suggestions if applicable, and also change the value
- // of the textarea in our store.
- handleChange ({
- target: {
- selectionStart,
- value,
- },
- }) {
- const {
- onChange,
- onSuggestionsFetchRequested,
- onSuggestionsClearRequested,
- } = this.props;
- const { lastToken } = this.state;
-
- // This gets the token at the caret location, if it begins with an
- // `@` (mentions) or `:` (shortcodes).
- const left = value.slice(0, selectionStart).search(/[^\s\u200B]+$/);
- const right = value.slice(selectionStart).search(/[\s\u200B]/);
- const token = function () {
- switch (true) {
- case left < 0 || !/[@:#]/.test(value[left]):
- return null;
- case right < 0:
- return value.slice(left);
- default:
- return value.slice(left, right + selectionStart).trim().toLowerCase();
- }
- }();
-
- // We only request suggestions for tokens which are at least 3
- // characters long.
- if (onSuggestionsFetchRequested && token && token.length >= 3) {
- if (lastToken !== token) {
- this.setState({
- lastToken: token,
- selectedSuggestion: 0,
- tokenStart: left,
- });
- onSuggestionsFetchRequested(token);
- }
- } else {
- this.setState({ lastToken: null });
- if (onSuggestionsClearRequested) {
- onSuggestionsClearRequested();
- }
- }
-
- // Updates the value of the textarea.
- if (onChange) {
- onChange(value);
- }
- },
-
- // Handles a click on an autosuggestion.
- handleClickSuggestion (index) {
- const { textarea } = this;
- const {
- onSuggestionSelected,
- suggestions,
- } = this.props;
- const {
- lastToken,
- tokenStart,
- } = this.state;
- onSuggestionSelected(tokenStart, lastToken, suggestions.get(index));
- textarea.focus();
- },
-
- // Handles a keypress. If the autosuggestions are visible, we need
- // to allow keypresses to navigate and sleect them.
- handleKeyDown (e) {
- const {
- disabled,
- onSubmit,
- onSecondarySubmit,
- onSuggestionSelected,
- suggestions,
- } = this.props;
- const {
- lastToken,
- suggestionsHidden,
- selectedSuggestion,
- tokenStart,
- } = this.state;
-
- // Keypresses do nothing if the composer is disabled.
- if (disabled) {
- e.preventDefault();
- return;
- }
-
- // We submit the status on control/meta + enter.
- if (onSubmit && e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
- onSubmit();
- }
-
- // Submit the status with secondary visibility on alt + enter.
- if (onSecondarySubmit && e.keyCode === 13 && e.altKey) {
- onSecondarySubmit();
- }
-
- // Switches over the pressed key.
- switch(e.key) {
-
- // On arrow down, we pick the next suggestion.
- case 'ArrowDown':
- if (suggestions && suggestions.size > 0 && !suggestionsHidden) {
- e.preventDefault();
- this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) });
- }
- return;
-
- // On arrow up, we pick the previous suggestion.
- case 'ArrowUp':
- if (suggestions && suggestions.size > 0 && !suggestionsHidden) {
- e.preventDefault();
- this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) });
- }
- return;
-
- // On enter or tab, we select the suggestion.
- case 'Enter':
- case 'Tab':
- if (onSuggestionSelected && lastToken !== null && suggestions && suggestions.size > 0 && !suggestionsHidden) {
- e.preventDefault();
- e.stopPropagation();
- onSuggestionSelected(tokenStart, lastToken, suggestions.get(selectedSuggestion));
- }
- return;
- }
- },
-
- // When the escape key is released, we either close the suggestions
- // window or focus the UI.
- handleKeyUp ({ key }) {
- const { suggestionsHidden } = this.state;
- if (key === 'Escape') {
- if (!suggestionsHidden) {
- this.setState({ suggestionsHidden: true });
- } else {
- document.querySelector('.ui').parentElement.focus();
- }
- }
- },
-
- // Handles the pasting of images into the composer.
- handlePaste (e) {
- const { onPaste } = this.props;
- let d;
- if (onPaste && (d = e.clipboardData) && (d = d.files).length === 1) {
- onPaste(d);
- e.preventDefault();
- }
- },
-
- // Saves a reference to the textarea.
- handleRefTextarea (textarea) {
- this.textarea = textarea;
- },
-};
-
-// The component.
-export default class ComposerTextarea extends React.Component {
-
- // Constructor.
- constructor (props) {
- super(props);
- assignHandlers(this, handlers);
- this.state = {
- suggestionsHidden: false,
- selectedSuggestion: 0,
- lastToken: null,
- tokenStart: 0,
- };
-
- // Instance variables.
- this.textarea = null;
- }
-
- // When we receive new suggestions, we unhide the suggestions window
- // if we didn't have any suggestions before.
- componentWillReceiveProps (nextProps) {
- const { suggestions } = this.props;
- const { suggestionsHidden } = this.state;
- if (nextProps.suggestions && nextProps.suggestions !== suggestions && nextProps.suggestions.size > 0 && suggestionsHidden) {
- this.setState({ suggestionsHidden: false });
- }
- }
-
- // Rendering.
- render () {
- const {
- handleBlur,
- handleChange,
- handleClickSuggestion,
- handleKeyDown,
- handleKeyUp,
- handlePaste,
- handleRefTextarea,
- } = this.handlers;
- const {
- advancedOptions,
- autoFocus,
- disabled,
- intl,
- onPickEmoji,
- suggestions,
- value,
- } = this.props;
- const {
- selectedSuggestion,
- suggestionsHidden,
- } = this.state;
-
- // The result.
- return (
-
-
-
-
-
- );
- }
-
-}
-
-// Props.
-ComposerTextarea.propTypes = {
- advancedOptions: ImmutablePropTypes.map,
- autoFocus: PropTypes.bool,
- disabled: PropTypes.bool,
- intl: PropTypes.object.isRequired,
- onChange: PropTypes.func,
- onPaste: PropTypes.func,
- onPickEmoji: PropTypes.func,
- onSubmit: PropTypes.func,
- onSecondarySubmit: PropTypes.func,
- onSuggestionsClearRequested: PropTypes.func,
- onSuggestionsFetchRequested: PropTypes.func,
- onSuggestionSelected: PropTypes.func,
- suggestions: ImmutablePropTypes.list,
- value: PropTypes.string,
-};
-
-// Default props.
-ComposerTextarea.defaultProps = { autoFocus: true };
diff --git a/app/javascript/flavours/glitch/features/composer/textarea/suggestions/index.js b/app/javascript/flavours/glitch/features/composer/textarea/suggestions/index.js
deleted file mode 100644
index dc72585f21..0000000000
--- a/app/javascript/flavours/glitch/features/composer/textarea/suggestions/index.js
+++ /dev/null
@@ -1,43 +0,0 @@
-// Package imports.
-import PropTypes from 'prop-types';
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-// Components.
-import ComposerTextareaSuggestionsItem from './item';
-
-// The component.
-export default function ComposerTextareaSuggestions ({
- hidden,
- onSuggestionClick,
- suggestions,
- value,
-}) {
-
- // The result.
- return (
-
- {!hidden && suggestions ? suggestions.map(
- (suggestion, index) => (
-
- )
- ) : null}
-
- );
-}
-
-ComposerTextareaSuggestions.propTypes = {
- hidden: PropTypes.bool,
- onSuggestionClick: PropTypes.func,
- suggestions: ImmutablePropTypes.list,
- value: PropTypes.number,
-};
diff --git a/app/javascript/flavours/glitch/features/composer/textarea/suggestions/item/index.js b/app/javascript/flavours/glitch/features/composer/textarea/suggestions/item/index.js
deleted file mode 100644
index 1b7ae89048..0000000000
--- a/app/javascript/flavours/glitch/features/composer/textarea/suggestions/item/index.js
+++ /dev/null
@@ -1,118 +0,0 @@
-// Package imports.
-import classNames from 'classnames';
-import PropTypes from 'prop-types';
-import React from 'react';
-
-// Components.
-import AccountContainer from 'flavours/glitch/containers/account_container';
-
-// Utils.
-import { unicodeMapping } from 'flavours/glitch/util/emoji';
-import { assignHandlers } from 'flavours/glitch/util/react_helpers';
-
-// Gets our asset host from the environment, if available.
-const assetHost = process.env.CDN_HOST || '';
-
-// Handlers.
-const handlers = {
-
- // Handles a click on a suggestion.
- handleClick (e) {
- const {
- index,
- onClick,
- } = this.props;
- if (onClick) {
- e.preventDefault();
- e.stopPropagation(); // Prevents following account links
- onClick(index);
- }
- },
-
- // This prevents the focus from changing, which would mess with
- // our suggestion code.
- handleMouseDown (e) {
- e.preventDefault();
- },
-};
-
-// The component.
-export default class ComposerTextareaSuggestionsItem extends React.Component {
-
- // Constructor.
- constructor (props) {
- super(props);
- assignHandlers(this, handlers);
- }
-
- // Rendering.
- render () {
- const {
- handleMouseDown,
- handleClick,
- } = this.handlers;
- const {
- selected,
- suggestion,
- } = this.props;
- const computedClass = classNames('composer--textarea--suggestions--item', { selected });
-
- // If the suggestion is an object, then we render an emoji.
- // Otherwise, we render a hashtag if it starts with #, or an account.
- let inner;
- if (typeof suggestion === 'object') {
- let url;
- if (suggestion.custom) {
- url = suggestion.imageUrl;
- } else {
- const mapping = unicodeMapping[suggestion.native] || unicodeMapping[suggestion.native.replace(/\uFE0F$/, '')];
- if (mapping) {
- url = `${assetHost}/emoji/${mapping.filename}.svg`;
- }
- }
- if (url) {
- inner = (
-
-
- {suggestion.colons}
-
- );
- }
- } else if (suggestion[0] === '#') {
- inner = suggestion;
- } else {
- inner = (
-
- );
- }
-
- // The result.
- return (
-
- { inner }
-
- );
- }
-
-}
-
-// Props.
-ComposerTextareaSuggestionsItem.propTypes = {
- index: PropTypes.number,
- onClick: PropTypes.func,
- selected: PropTypes.bool,
- suggestion: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
-};
diff --git a/app/javascript/flavours/glitch/styles/components/composer.scss b/app/javascript/flavours/glitch/styles/components/composer.scss
index f0729bedce..466b654de6 100644
--- a/app/javascript/flavours/glitch/styles/components/composer.scss
+++ b/app/javascript/flavours/glitch/styles/components/composer.scss
@@ -131,8 +131,8 @@
.composer--textarea {
position: relative;
- & > label {
- .textarea {
+ label {
+ .autosuggest-textarea__textarea {
display: block;
box-sizing: border-box;
margin: 0;
@@ -186,7 +186,7 @@
}
}
-.composer--textarea--suggestions {
+.autosuggest-textarea__suggestions {
display: block;
position: absolute;
box-sizing: border-box;
@@ -199,11 +199,14 @@
box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
font-size: 14px;
z-index: 99;
+ display: none;
+}
- &[hidden] { display: none }
+.autosuggest-textarea__suggestions--visible {
+ display: block;
}
-.composer--textarea--suggestions--item {
+.autosuggest-textarea__suggestions__item {
display: flex;
flex-direction: row;
align-items: center;
diff --git a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss
index 6f105d3fad..224272f245 100644
--- a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss
+++ b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss
@@ -196,11 +196,11 @@
border-color: $ui-base-color;
}
-.composer--textarea--suggestions {
+.autosuggest-textarea__suggestions {
background: lighten($ui-base-color, 10%)
}
-.composer--textarea--suggestions--item {
+.autosuggest-textarea__suggestions__item {
&:hover,
&:focus,
&:active,