Adding emoji picker

This commit is contained in:
Jesús Espino 2020-10-28 10:45:15 +01:00
parent 8afac6781d
commit ec93778293
12 changed files with 113 additions and 1 deletions

View file

@ -16,6 +16,7 @@
"CardDetail.add-property": "+ Add a property", "CardDetail.add-property": "+ Add a property",
"CardDetail.image": "Image", "CardDetail.image": "Image",
"CardDetail.new-comment-placeholder": "Add a comment...", "CardDetail.new-comment-placeholder": "Add a comment...",
"CardDetail.pick-icon": "Pick Icon",
"CardDetail.random-icon": "Random", "CardDetail.random-icon": "Random",
"CardDetail.remove-icon": "Remove Icon", "CardDetail.remove-icon": "Remove Icon",
"CardDetail.text": "Text", "CardDetail.text": "Text",
@ -66,6 +67,7 @@
"ViewHeader.test-add-100-cards": "TEST: Add 100 cards", "ViewHeader.test-add-100-cards": "TEST: Add 100 cards",
"ViewHeader.test-add-1000-cards": "TEST: Add 1,000 cards", "ViewHeader.test-add-1000-cards": "TEST: Add 1,000 cards",
"ViewHeader.test-randomize-icons": "TEST: Randomize icons", "ViewHeader.test-randomize-icons": "TEST: Randomize icons",
"ViewTitle.pick-icon": "Pick Icon",
"ViewTitle.random-icon": "Random", "ViewTitle.random-icon": "Random",
"ViewTitle.remove-icon": "Remove Icon", "ViewTitle.remove-icon": "Remove Icon",
"ViewTitle.untitled-board": "Untitled Board" "ViewTitle.untitled-board": "Untitled Board"

View file

@ -16,6 +16,7 @@
"CardDetail.add-property": "+ Añadir propiedad", "CardDetail.add-property": "+ Añadir propiedad",
"CardDetail.image": "Imagen", "CardDetail.image": "Imagen",
"CardDetail.new-comment-placeholder": "Añadir un comentario...", "CardDetail.new-comment-placeholder": "Añadir un comentario...",
"CardDetail.pick-icon": "Escoger Icono",
"CardDetail.random-icon": "Aleatorio", "CardDetail.random-icon": "Aleatorio",
"CardDetail.remove-icon": "Quitar icono", "CardDetail.remove-icon": "Quitar icono",
"CardDetail.text": "Texto", "CardDetail.text": "Texto",
@ -66,6 +67,7 @@
"ViewHeader.test-add-100-cards": "TEST: Añadir 100 tarjetas", "ViewHeader.test-add-100-cards": "TEST: Añadir 100 tarjetas",
"ViewHeader.test-add-1000-cards": "TEST: Añadir 1,000 tarjetas", "ViewHeader.test-add-1000-cards": "TEST: Añadir 1,000 tarjetas",
"ViewHeader.test-randomize-icons": "TEST: Iconos aleatorios", "ViewHeader.test-randomize-icons": "TEST: Iconos aleatorios",
"ViewTitle.pick-icon": "Escoger Icono",
"ViewTitle.random-icon": "Aleatorio", "ViewTitle.random-icon": "Aleatorio",
"ViewTitle.remove-icon": "Quitar Icono", "ViewTitle.remove-icon": "Quitar Icono",
"ViewTitle.untitled-board": "Panel sin título" "ViewTitle.untitled-board": "Panel sin título"

View file

@ -1314,6 +1314,15 @@
"@types/tern": "*" "@types/tern": "*"
} }
}, },
"@types/emoji-mart": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/emoji-mart/-/emoji-mart-3.0.3.tgz",
"integrity": "sha512-IAfTNRA6OoM7f/GZpuvdM47ktLGcHsnUuR+dQlwEEXSRbS0Z9iFoPA3WgBim6RBL4i7EVLtoRpREn5BgL3aMXA==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/estree": { "@types/estree": {
"version": "0.0.45", "version": "0.0.45",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz",
@ -3627,6 +3636,15 @@
"integrity": "sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ==", "integrity": "sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ==",
"dev": true "dev": true
}, },
"emoji-mart": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-3.0.0.tgz",
"integrity": "sha512-r5DXyzOLJttdwRYfJmPq/XL3W5tiAE/VsRnS0Hqyn27SqPA/GOYwVUSx50px/dXdJyDSnvmoPbuJ/zzhwSaU4A==",
"requires": {
"@babel/runtime": "^7.0.0",
"prop-types": "^15.6.0"
}
},
"emoji-regex": { "emoji-regex": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",

View file

@ -13,6 +13,7 @@
"i18n-extract": "formatjs extract src/**/*.tsx src/**/*.ts --out-file i18n/tmp.json; formatjs compile i18n/tmp.json --out-file i18n/en.json; rm i18n/tmp.json" "i18n-extract": "formatjs extract src/**/*.tsx src/**/*.ts --out-file i18n/tmp.json; formatjs compile i18n/tmp.json --out-file i18n/en.json; rm i18n/tmp.json"
}, },
"dependencies": { "dependencies": {
"emoji-mart": "^3.0.0",
"marked": "^1.1.1", "marked": "^1.1.1",
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
@ -30,6 +31,7 @@
"@formatjs/ts-transformer": "^2.11.3", "@formatjs/ts-transformer": "^2.11.3",
"@testing-library/jest-dom": "^5.11.4", "@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.0.4", "@testing-library/react": "^11.0.4",
"@types/emoji-mart": "^3.0.3",
"@types/jest": "^26.0.14", "@types/jest": "^26.0.14",
"@types/marked": "^1.1.0", "@types/marked": "^1.1.0",
"@types/react": "^16.9.49", "@types/react": "^16.9.49",

View file

@ -14,6 +14,7 @@ import {Utils} from '../utils'
import MenuWrapper from '../widgets/menuWrapper' import MenuWrapper from '../widgets/menuWrapper'
import Menu from '../widgets/menu' import Menu from '../widgets/menu'
import Editable from '../widgets/editable' import Editable from '../widgets/editable'
import EmojiPicker from '../widgets/emojiPicker'
import Button from '../widgets/buttons/button' import Button from '../widgets/buttons/button'
import {MarkdownEditor} from './markdownEditor' import {MarkdownEditor} from './markdownEditor'
@ -50,6 +51,13 @@ class CardDetail extends React.Component<Props, State> {
} }
} }
onSelectEmoji = (emoji: string) => {
mutator.changeIcon(this.state.cardTree.card, emoji)
// Close the menu
document.body.click()
}
componentDidMount() { componentDidMount() {
this.cardListener = new OctoListener() this.cardListener = new OctoListener()
this.cardListener.open([this.props.cardId], async (blockId) => { this.cardListener.open([this.props.cardId], async (blockId) => {
@ -128,6 +136,12 @@ class CardDetail extends React.Component<Props, State> {
name={intl.formatMessage({id: 'CardDetail.random-icon', defaultMessage: 'Random'})} name={intl.formatMessage({id: 'CardDetail.random-icon', defaultMessage: 'Random'})}
onClick={() => mutator.changeIcon(card, BlockIcons.shared.randomIcon())} onClick={() => mutator.changeIcon(card, BlockIcons.shared.randomIcon())}
/> />
<Menu.SubMenu
id='pick'
name={intl.formatMessage({id: 'CardDetail.pick-icon', defaultMessage: 'Pick Icon'})}
>
<EmojiPicker onSelect={this.onSelectEmoji}/>
</Menu.SubMenu>
<Menu.Text <Menu.Text
id='remove' id='remove'
name={intl.formatMessage({id: 'CardDetail.remove-icon', defaultMessage: 'Remove Icon'})} name={intl.formatMessage({id: 'CardDetail.remove-icon', defaultMessage: 'Remove Icon'})}

View file

@ -203,6 +203,7 @@ class Sidebar extends React.Component<Props, State> {
<Menu.SubMenu <Menu.SubMenu
id='lang' id='lang'
name={intl.formatMessage({id: 'Sidebar.set-language', defaultMessage: 'Set Language'})} name={intl.formatMessage({id: 'Sidebar.set-language', defaultMessage: 'Set Language'})}
position='top'
> >
<Menu.Text <Menu.Text
id='english-lang' id='english-lang'
@ -218,6 +219,7 @@ class Sidebar extends React.Component<Props, State> {
<Menu.SubMenu <Menu.SubMenu
id='theme' id='theme'
name={intl.formatMessage({id: 'Sidebar.set-theme', defaultMessage: 'Set Theme'})} name={intl.formatMessage({id: 'Sidebar.set-theme', defaultMessage: 'Set Theme'})}
position='top'
> >
<Menu.Text <Menu.Text
id='dark-theme' id='dark-theme'

View file

@ -9,6 +9,7 @@ import mutator from '../mutator'
import Menu from '../widgets/menu' import Menu from '../widgets/menu'
import MenuWrapper from '../widgets/menuWrapper' import MenuWrapper from '../widgets/menuWrapper'
import Editable from '../widgets/editable' import Editable from '../widgets/editable'
import EmojiPicker from '../widgets/emojiPicker'
import Button from '../widgets/buttons/button' import Button from '../widgets/buttons/button'
import './viewTitle.scss' import './viewTitle.scss'
@ -33,6 +34,13 @@ class ViewTitle extends React.Component<Props, State> {
this.state = {title: props.board.title} this.state = {title: props.board.title}
} }
onSelectEmoji = (emoji: string) => {
mutator.changeIcon(this.props.board, emoji)
// Close the menu
document.body.click()
}
render(): JSX.Element { render(): JSX.Element {
const {board, intl} = this.props const {board, intl} = this.props
@ -62,6 +70,12 @@ class ViewTitle extends React.Component<Props, State> {
name={intl.formatMessage({id: 'ViewTitle.random-icon', defaultMessage: 'Random'})} name={intl.formatMessage({id: 'ViewTitle.random-icon', defaultMessage: 'Random'})}
onClick={() => mutator.changeIcon(board, BlockIcons.shared.randomIcon())} onClick={() => mutator.changeIcon(board, BlockIcons.shared.randomIcon())}
/> />
<Menu.SubMenu
id='pick'
name={intl.formatMessage({id: 'ViewTitle.pick-icon', defaultMessage: 'Pick Icon'})}
>
<EmojiPicker onSelect={this.onSelectEmoji}/>
</Menu.SubMenu>
<Menu.Text <Menu.Text
id='remove' id='remove'
name={intl.formatMessage({id: 'ViewTitle.remove-icon', defaultMessage: 'Remove Icon'})} name={intl.formatMessage({id: 'ViewTitle.remove-icon', defaultMessage: 'Remove Icon'})}

View file

@ -0,0 +1,20 @@
.EmojiPicker {
.emoji-mart {
color: rgb(var(--main-fg));
background: rgb(var(--main-bg));
border-color: rgba(var(--main-fg), 0.16);
.emoji-mart-bar {
border: 0 solid rgba(var(--main-fg), 0.16);
border-bottom-width: 1px
}
.emoji-mart-search input {
border: 1px solid rgba(var(--main-fg), 0.16);
}
.emoji-mart-category-label span {
background: rgb(var(--main-bg));
}
.emoji-mart-search-icon svg {
fill: rgb(var(--main-fg));
}
}
}

View file

@ -0,0 +1,25 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {FC} from 'react'
import 'emoji-mart/css/emoji-mart.css'
import {Picker, BaseEmoji} from 'emoji-mart'
import './emojiPicker.scss'
type Props = {
onSelect: (emoji: string) => void
}
const EmojiPicker: FC<Props> = (props: Props): JSX.Element => (
<div
className='EmojiPicker'
onClick={(e) => e.stopPropagation()}
>
<Picker
onSelect={(emoji: BaseEmoji) => props.onSelect(emoji.native)}
/>
</div>
)
export default EmojiPicker

View file

@ -11,6 +11,12 @@
border-radius: 3px; border-radius: 3px;
box-shadow: rgba(var(--main-fg), 0.05) 0px 0px 0px 1px, rgba(var(--main-fg), 0.1) 0px 3px 6px, rgba(var(--main-fg), 0.2) 0px 9px 24px; box-shadow: rgba(var(--main-fg), 0.05) 0px 0px 0px 1px, rgba(var(--main-fg), 0.1) 0px 3px 6px, rgba(var(--main-fg), 0.2) 0px 9px 24px;
left: 100%; left: 100%;
&.top {
bottom: 0;
}
&.bottom {
top: 0;
}
} }
.SubmenuTriangleIcon { .SubmenuTriangleIcon {

View file

@ -9,7 +9,7 @@ import {MenuOptionProps} from './menuItem'
import './subMenuOption.scss' import './subMenuOption.scss'
type SubMenuOptionProps = MenuOptionProps & { type SubMenuOptionProps = MenuOptionProps & {
position?: 'bottom | top' position?: 'bottom' | 'top'
} }
type SubMenuState = { type SubMenuState = {

View file

@ -47,6 +47,13 @@ function makeCommonConfig() {
'sass-loader', 'sass-loader',
], ],
}, },
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader',
],
},
{ {
test: /\.(tsx?|js|jsx|html)$/, test: /\.(tsx?|js|jsx|html)$/,
use: [ use: [