Skip to content

Commit

Permalink
umputun#378 add emoji suggestion
Browse files Browse the repository at this point in the history
  • Loading branch information
Mavrin committed Jan 12, 2020
1 parent 3447aff commit 783f403
Show file tree
Hide file tree
Showing 13 changed files with 165 additions and 22 deletions.
1 change: 1 addition & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
4 changes: 4 additions & 0 deletions frontend/app/@types/preact/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ declare module 'preact/src/jsx' {
'md-link': any;
'md-unordered-list': any;
'md-ordered-list': any;
'text-expander': {
ref: any;
children: any;
};
}
}
}
1 change: 1 addition & 0 deletions frontend/app/common/static_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const StaticStore: StaticStoreType = {
simple_view: false,
anon_vote: false,
email_notifications: false,
emoji_enabled: false,
},
query: querySettings as QuerySettingsType,
};
1 change: 1 addition & 0 deletions frontend/app/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export interface Config {
simple_view: boolean;
anon_vote: boolean;
email_notifications: boolean;
emoji_enabled: boolean;
}

export interface RemarkConfig {
Expand Down
40 changes: 21 additions & 19 deletions frontend/app/components/comment-form/comment-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { SubscribeByRSS } from './__subscribe-by-rss';

import MarkdownToolbar from './markdown-toolbar';
import TextareaAutosize from './textarea-autosize';
import { TextExpander } from './text-expander';

let textareaId = 0;

Expand Down Expand Up @@ -144,7 +145,7 @@ export class CommentForm extends Component<Props, State> {
}

send(e: Event) {
const text = this.state.text;
const text = this.textAreaRef.current ? this.textAreaRef.current.getValue() : this.state.text;
const props = this.props;

if (e) e.preventDefault();
Expand All @@ -156,7 +157,7 @@ export class CommentForm extends Component<Props, State> {
this.setState({ preview: null, text: '' });
}

this.setState({ isDisabled: true, isErrorShown: false });
this.setState({ isDisabled: true, isErrorShown: false, text });

props
.onSubmit(text, pageTitle || document.title)
Expand All @@ -172,11 +173,11 @@ export class CommentForm extends Component<Props, State> {
}

getPreview() {
const text = this.state.text;
const text = this.textAreaRef.current ? this.textAreaRef.current.getValue() : this.state.text;

if (!text || !text.trim()) return;

this.setState({ isErrorShown: false, errorMessage: null });
this.setState({ isErrorShown: false, errorMessage: null, text });

this.props
.getPreview(text)
Expand Down Expand Up @@ -369,21 +370,22 @@ export class CommentForm extends Component<Props, State> {
</div>
)}
<div className="comment-form__field-wrapper">
<TextareaAutosize
id={this.textareaId}
onPaste={this.onPaste}
ref={this.textAreaRef}
className="comment-form__field"
placeholder="Your comment here"
value={text}
maxLength={maxLength}
onInput={this.onInput}
onKeyDown={this.onKeyDown}
disabled={isDisabled}
autofocus={!!props.autofocus}
spellcheck={true}
/>

<TextExpander>
<TextareaAutosize
id={this.textareaId}
onPaste={this.onPaste}
ref={this.textAreaRef}
className="comment-form__field"
placeholder="Your comment here"
value={text}
maxLength={maxLength}
onInput={this.onInput}
onKeyDown={this.onKeyDown}
disabled={isDisabled}
autofocus={!!props.autofocus}
spellcheck={true}
/>
</TextExpander>
{charactersLeft < 100 && <span className="comment-form__counter">{charactersLeft}</span>}
</div>

Expand Down
2 changes: 2 additions & 0 deletions frontend/app/components/comment-form/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ import './_theme/_dark/comment-form_theme_dark.scss';
import './_theme/_light/comment-form_theme_light.scss';

import './_simple/comment-form_simple.scss';

import './text-expander.scss';
39 changes: 39 additions & 0 deletions frontend/app/components/comment-form/text-expander.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.suggester-container {
position: absolute;
top: 0;
left: 0;
z-index: 30;
min-width: 180px;
padding: 0;
margin: 24px 0 0;
list-style: none;
cursor: pointer;
background: #fff;
border: 1px solid #e1e4e8;
border-radius: 3px;
box-shadow: 0 1px 5px rgba(27, 31, 35, 0.15);
}

.suggester li.navigation-focus,
.suggester li:hover,
.suggester li[aria-selected='true'] {
color: #fff;
text-decoration: none;
background: #0366d6;
}

.suggester li:first-child {
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
.suggester li:last-child {
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
}

.suggester li {
display: block;
padding: 4px 8px;
font-weight: 600;
border-bottom: 1px solid #e1e4e8;
}
49 changes: 49 additions & 0 deletions frontend/app/components/comment-form/text-expander.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/** @jsx createElement */
import { createElement, RenderableProps } from 'preact';
import { useEffect, useRef } from 'preact/hooks';
import nodeEmoji from 'node-emoji';
import '@github/text-expander-element';

export function TextExpander({ children }: RenderableProps<void>) {
const expanderRef = useRef<HTMLElement>();
useEffect(() => {
if (expanderRef.current) {
const expander = expanderRef.current;
expander.setAttribute(`keys`, ':');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
expander.addEventListener('text-expander-change', (event: any) => {
const { key, provide, text } = event.detail;
if (key === ':') {
const emojiList = nodeEmoji.search(text);
if (emojiList.length === 0) {
return;
}
const menu = document.createElement('ul');
menu.classList.add('suggester-container');
menu.classList.add('suggester');
menu.style.fontSize = `14px`;
for (let i = 0; i < 5; i++) {
const emoji = emojiList[i];
if (emoji) {
const item = document.createElement('li');
item.setAttribute('role', 'option');
item.dataset.emojiKey = emoji.key;
item.textContent = emoji.emoji + ` ` + emoji.key;
menu.append(item);
}
}

provide(Promise.resolve({ matched: true, fragment: menu }));
}
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
expander.addEventListener('text-expander-value', (event: any) => {
const { key, item } = event.detail;
if (key === ':') {
event.detail.value = `:${item.dataset.emojiKey}:`;
}
});
}
}, []);
return <text-expander ref={expanderRef}>{children}</text-expander>;
}
4 changes: 3 additions & 1 deletion frontend/app/components/comment-form/textarea-autosize.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ export default class TextareaAutosize extends Component<Props> {
onRef(node: HTMLTextAreaElement) {
this.textareaRef = node;
}

getValue() {
return this.textareaRef ? this.textareaRef.value : '';
}
autoResize() {
if (this.textareaRef) {
this.textareaRef.style.height = '';
Expand Down
1 change: 1 addition & 0 deletions frontend/app/testUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ beforeEach(() => {
simple_view: false,
anon_vote: false,
email_notifications: false,
emoji_enabled: true,
};
});
37 changes: 37 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^3.2.0",
"document-register-element": "^1.14.3",
"dotenv": "^8.2.0",
"enzyme": "^3.10.0",
"enzyme-adapter-preact-pure": "^2.1.0",
"es-check": "^5.1.0",
Expand Down Expand Up @@ -94,11 +95,14 @@
},
"dependencies": {
"@github/markdown-toolbar-element": "^1.1.0",
"@github/text-expander-element": "^1.0.1",
"@types/node-emoji": "^1.8.1",
"@webcomponents/custom-elements": "^1.3.0",
"bem-react-helper": "^1.1.2",
"core-js": "^3.2.1",
"focus-visible": "^5.0.2",
"intersection-observer": "^0.7.0",
"node-emoji": "^1.10.0",
"preact": "^10.0.1",
"react-redux": "^7.1.1",
"redux": "^4.0.4",
Expand Down
4 changes: 2 additions & 2 deletions frontend/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable no-console */

require('dotenv').config();
const path = require('path');

const webpack = require('webpack');
Expand Down Expand Up @@ -27,7 +27,7 @@ console.log(`REMARK_ENV = ${remarkUrl}`);
* so we have to exclude from ignore these modules
*/
function getExcluded() {
const modules = ['@github/markdown-toolbar-element'];
const modules = ['@github/markdown-toolbar-element', '@github/text-expander-element', '@github/combobox-nav'];
const exclude = new RegExp(`node_modules\\/(?!(${modules.map(m => m.replace(/\//g, '\\/')).join('|')})\\/).*`);

return {
Expand Down

0 comments on commit 783f403

Please sign in to comment.