Custom emoji for skin tone selector
Currently, the skin tone selector is a row of colored dots We propose creating a skinIcon field that uses the emoji-mart dots on default but otherwise uses the emoji passed in by the userrelease
parent
b1c499fc8d
commit
f1d5bc0095
|
@ -181,6 +181,11 @@
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.emoji-mart-preview-skins.custom {
|
||||||
|
right: 10px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
.emoji-mart-preview-name {
|
.emoji-mart-preview-name {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
@ -223,12 +228,18 @@
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji-mart-skin-swatches-opened .emoji-mart-skin-swatch {
|
.emoji-mart-skin-swatches.custom {
|
||||||
|
font-size: 0;
|
||||||
|
border: none;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-skin-swatches.opened .emoji-mart-skin-swatch {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
padding: 0 2px;
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji-mart-skin-swatches-opened .emoji-mart-skin-swatch-selected:after {
|
.emoji-mart-skin-swatches.opened .emoji-mart-skin-swatch.selected:after {
|
||||||
opacity: .75;
|
opacity: .75;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,12 +259,13 @@
|
||||||
.emoji-mart-skin-swatch:nth-child(5) { transition-delay: .12s }
|
.emoji-mart-skin-swatch:nth-child(5) { transition-delay: .12s }
|
||||||
.emoji-mart-skin-swatch:nth-child(6) { transition-delay: .15s }
|
.emoji-mart-skin-swatch:nth-child(6) { transition-delay: .15s }
|
||||||
|
|
||||||
.emoji-mart-skin-swatch-selected {
|
.emoji-mart-skin-swatch.selected {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
padding: 0 2px;
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
.emoji-mart-skin-swatch-selected:after {
|
|
||||||
|
.emoji-mart-skin-swatch.selected:after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%; left: 50%;
|
top: 50%; left: 50%;
|
||||||
|
@ -266,9 +278,63 @@
|
||||||
transition: opacity .2s ease-out;
|
transition: opacity .2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.emoji-mart-skin-swatch.custom {
|
||||||
|
display: inline-block;
|
||||||
|
width: 0;
|
||||||
|
height: 38px;
|
||||||
|
overflow: hidden;
|
||||||
|
vertical-align: middle;
|
||||||
|
transition-property: width, height;
|
||||||
|
transition-duration: .125s;
|
||||||
|
transition-timing-function: ease-out;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-skin-swatch.custom.selected {
|
||||||
|
position: relative;
|
||||||
|
width: 36px;
|
||||||
|
height: 38px;
|
||||||
|
padding: 0 2px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-skin-swatch.custom.selected:after {
|
||||||
|
content: "";
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-skin-swatches.custom .emoji-mart-skin-swatch.custom:hover {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
border-radius: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-skin-swatches.custom.opened .emoji-mart-skin-swatch.custom {
|
||||||
|
width: 36px;
|
||||||
|
height: 38px;
|
||||||
|
padding: 0 2px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-skin-swatches.custom.opened .emoji-mart-skin-swatch.custom.selected:after {
|
||||||
|
opacity: .75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-skin-text.opened {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: left;
|
||||||
|
color: #888;
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 5px 2px;
|
||||||
|
width: 30%;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 10%;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.emoji-mart-skin {
|
.emoji-mart-skin {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%; padding-top: 100%;
|
width: 100%;
|
||||||
|
padding-top: 100%;
|
||||||
max-width: 12px;
|
max-width: 12px;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ export { default as Category } from './category'
|
||||||
export { default as Preview } from './preview'
|
export { default as Preview } from './preview'
|
||||||
export { default as Search } from './search'
|
export { default as Search } from './search'
|
||||||
export { default as Skins } from './skins'
|
export { default as Skins } from './skins'
|
||||||
|
export { default as SkinsEmoji } from './skins-emoji'
|
||||||
|
export { default as SkinsDot } from './skins-dot'
|
||||||
|
|
||||||
export { default as Emoji } from './emoji/emoji'
|
export { default as Emoji } from './emoji/emoji'
|
||||||
export { default as NimbleEmoji } from './emoji/nimble-emoji'
|
export { default as NimbleEmoji } from './emoji/nimble-emoji'
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { Anchors, Category, Preview, Search } from '..'
|
||||||
const I18N = {
|
const I18N = {
|
||||||
search: 'Search',
|
search: 'Search',
|
||||||
notfound: 'No Emoji Found',
|
notfound: 'No Emoji Found',
|
||||||
|
skintext: 'Choose your default skin tone ',
|
||||||
categories: {
|
categories: {
|
||||||
search: 'Search Results',
|
search: 'Search Results',
|
||||||
recent: 'Frequently Used',
|
recent: 'Frequently Used',
|
||||||
|
@ -467,6 +468,7 @@ export default class NimblePicker extends React.PureComponent {
|
||||||
exclude,
|
exclude,
|
||||||
recent,
|
recent,
|
||||||
autoFocus,
|
autoFocus,
|
||||||
|
skinIcon,
|
||||||
} = this.props,
|
} = this.props,
|
||||||
{ skin } = this.state,
|
{ skin } = this.state,
|
||||||
width = perLine * (emojiSize + 12) + 12 + 2 + measureScrollbar()
|
width = perLine * (emojiSize + 12) + 12 + 2 + measureScrollbar()
|
||||||
|
@ -563,7 +565,9 @@ export default class NimblePicker extends React.PureComponent {
|
||||||
skinsProps={{
|
skinsProps={{
|
||||||
skin: skin,
|
skin: skin,
|
||||||
onChange: this.handleSkinChange,
|
onChange: this.handleSkinChange,
|
||||||
|
skinIcon: skinIcon,
|
||||||
}}
|
}}
|
||||||
|
i18n={this.i18n}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import { getData } from '../utils'
|
import { getData } from '../utils'
|
||||||
import { NimbleEmoji, Skins } from '.'
|
import { NimbleEmoji, SkinsEmoji, SkinsDot } from '.'
|
||||||
|
|
||||||
export default class Preview extends React.PureComponent {
|
export default class Preview extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -20,6 +20,7 @@ export default class Preview extends React.PureComponent {
|
||||||
showSkinTones,
|
showSkinTones,
|
||||||
title,
|
title,
|
||||||
emoji: idleEmoji,
|
emoji: idleEmoji,
|
||||||
|
i18n,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
if (emoji) {
|
if (emoji) {
|
||||||
|
@ -81,8 +82,27 @@ export default class Preview extends React.PureComponent {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showSkinTones && (
|
{showSkinTones && (
|
||||||
<div className="emoji-mart-preview-skins">
|
<div
|
||||||
<Skins {...skinsProps} />
|
className={`emoji-mart-preview-skins${
|
||||||
|
skinsProps.skinIcon ? ' custom' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{skinsProps.skinIcon ? (
|
||||||
|
<SkinsEmoji
|
||||||
|
skin={skinsProps.skin}
|
||||||
|
emojiProps={emojiProps}
|
||||||
|
data={this.data}
|
||||||
|
skinIcon={skinsProps.skinIcon}
|
||||||
|
i18n={i18n}
|
||||||
|
onChange={skinsProps.onChange}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<SkinsDot
|
||||||
|
skin={skinsProps.skin}
|
||||||
|
i18n={i18n}
|
||||||
|
onChange={skinsProps.onChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import { Skins } from '.'
|
||||||
|
|
||||||
|
export default class SkinsDot extends Skins {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.handleClick = this.handleClick.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { skin, i18n } = this.props
|
||||||
|
const { opened } = this.state
|
||||||
|
const skinToneNodes = []
|
||||||
|
|
||||||
|
for (let skinTone = 1; skinTone <= 6; skinTone++) {
|
||||||
|
const selected = skinTone === skin
|
||||||
|
skinToneNodes.push(
|
||||||
|
<span
|
||||||
|
key={`skin-tone-${skinTone}`}
|
||||||
|
className={`emoji-mart-skin-swatch${selected ? ' selected' : ''}`}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
onClick={this.handleClick}
|
||||||
|
data-skin={skinTone}
|
||||||
|
className={`emoji-mart-skin emoji-mart-skin-tone-${skinTone}`}
|
||||||
|
/>
|
||||||
|
</span>,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`emoji-mart-skin-swatches${opened ? ' opened' : ''}`}>
|
||||||
|
{skinToneNodes}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SkinsDot.propTypes = {
|
||||||
|
onChange: PropTypes.func,
|
||||||
|
skin: PropTypes.number.isRequired,
|
||||||
|
i18n: PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
|
SkinsDot.defaultProps = {
|
||||||
|
onChange: () => {},
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import { NimbleEmoji, Skins } from '.'
|
||||||
|
|
||||||
|
export default class SkinsEmoji extends Skins {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.handleClick = this.handleClick.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { skin, emojiProps, data, skinIcon, i18n } = this.props
|
||||||
|
const { opened } = this.state
|
||||||
|
const skinToneNodes = []
|
||||||
|
|
||||||
|
for (let skinTone = 1; skinTone <= 6; skinTone++) {
|
||||||
|
const selected = skinTone === skin
|
||||||
|
skinToneNodes.push(
|
||||||
|
<span
|
||||||
|
key={`skin-tone-${skinTone}`}
|
||||||
|
className={`emoji-mart-skin-swatch custom${
|
||||||
|
selected ? ' selected' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
onClick={this.handleClick}
|
||||||
|
data-skin={skinTone}
|
||||||
|
className={`emoji-mart-skin-tone-${skinTone}`}
|
||||||
|
>
|
||||||
|
{NimbleEmoji({
|
||||||
|
emoji: skinIcon,
|
||||||
|
data: data,
|
||||||
|
skin: skinTone,
|
||||||
|
backgroundImageFn: emojiProps.backgroundImageFn,
|
||||||
|
native: emojiProps.native,
|
||||||
|
set: emojiProps.set,
|
||||||
|
sheetSize: emojiProps.sheetSize,
|
||||||
|
size: 23,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</span>,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`emoji-mart-skin-swatches custom${opened ? ' opened' : ''}`}
|
||||||
|
>
|
||||||
|
<div className={`emoji-mart-skin-text${opened ? ' opened' : ''}`}>
|
||||||
|
{i18n.skintext}
|
||||||
|
</div>
|
||||||
|
{skinToneNodes}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SkinsEmoji.propTypes = {
|
||||||
|
onChange: PropTypes.func,
|
||||||
|
skin: PropTypes.number.isRequired,
|
||||||
|
emojiProps: PropTypes.object.isRequired,
|
||||||
|
skinTone: PropTypes.number,
|
||||||
|
skinIcon: PropTypes.string.isRequired,
|
||||||
|
i18n: PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
|
SkinsEmoji.defaultProps = {
|
||||||
|
onChange: () => {},
|
||||||
|
skinTone: null,
|
||||||
|
}
|
|
@ -1,15 +1,14 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import { NimbleEmoji } from '.'
|
||||||
|
|
||||||
export default class Skins extends React.PureComponent {
|
export default class Skins extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
opened: false,
|
opened: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleClick = this.handleClick.bind(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick(e) {
|
handleClick(e) {
|
||||||
|
@ -27,42 +26,7 @@ export default class Skins extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { skin } = this.props
|
return null
|
||||||
const { opened } = this.state
|
|
||||||
|
|
||||||
const skinToneNodes = []
|
|
||||||
|
|
||||||
for (let i = 0; i < 6; i++) {
|
|
||||||
const skinTone = i + 1
|
|
||||||
const selected = skinTone == skin
|
|
||||||
|
|
||||||
skinToneNodes.push(
|
|
||||||
<span
|
|
||||||
key={`skin-tone-${skinTone}`}
|
|
||||||
className={`emoji-mart-skin-swatch ${
|
|
||||||
selected ? 'emoji-mart-skin-swatch-selected' : ''
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
onClick={this.handleClick}
|
|
||||||
data-skin={skinTone}
|
|
||||||
className={`emoji-mart-skin emoji-mart-skin-tone-${skinTone}`}
|
|
||||||
/>
|
|
||||||
</span>,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
className={`emoji-mart-skin-swatches ${
|
|
||||||
opened ? 'emoji-mart-skin-swatches-opened' : ''
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{skinToneNodes}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,7 @@ const PickerPropTypes = {
|
||||||
imageUrl: PropTypes.string.isRequired,
|
imageUrl: PropTypes.string.isRequired,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
skinIcon: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const PickerDefaultProps = {
|
const PickerDefaultProps = {
|
||||||
|
@ -96,6 +97,7 @@ const PickerDefaultProps = {
|
||||||
emojiTooltip: EmojiDefaultProps.tooltip,
|
emojiTooltip: EmojiDefaultProps.tooltip,
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
custom: [],
|
custom: [],
|
||||||
|
skinIcon: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -41,6 +41,12 @@ storiesOf('Picker', module)
|
||||||
showPreview={boolean('Show preview', true)}
|
showPreview={boolean('Show preview', true)}
|
||||||
showSkinTones={boolean('Show skin tones', true)}
|
showSkinTones={boolean('Show skin tones', true)}
|
||||||
custom={CUSTOM_EMOJIS}
|
custom={CUSTOM_EMOJIS}
|
||||||
|
/>))
|
||||||
|
.add('custom-skin-icon', () => (
|
||||||
|
<Picker
|
||||||
|
native={boolean('Unicode', true)}
|
||||||
|
emojiSize={24}
|
||||||
|
skinIcon={text('Skin Preview Icon', 'v')}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue