feat: add ability to change skin tone emoji
This commit is contained in:
parent
d5be361a49
commit
294d615e8a
29
README.md
29
README.md
|
@ -165,8 +165,9 @@ The `new Picker(options)` constructor supports several options:
|
|||
Name | Type | Default | Description |
|
||||
------ | ------ | ------ | ------ |
|
||||
`dataSource` | string | "https://cdn.jsdelivr.net/npm/emojibase-data@5/en/data.json" | URL to fetch the emojibase data from |
|
||||
`i18n` | I18n | - | i18n object (see below for details) |
|
||||
`i18n` | I18n | - | i18n object (see below for details) |
|
||||
`locale` | string | "en" | Locale string |
|
||||
`skinToneEmoji` | string | "🖐️" | the emoji to use for the skin tone picker |
|
||||
|
||||
**Returns:** *Picker*
|
||||
|
||||
|
@ -363,6 +364,16 @@ Name | Type | Description |
|
|||
|
||||
___
|
||||
|
||||
#### getPreferredSkinTone
|
||||
|
||||
▸ **getPreferredSkinTone**(): *Promise‹SkinTone›*
|
||||
|
||||
Get the user's preferred skin tone. Returns 0 if not found.
|
||||
|
||||
**Returns:** *Promise‹SkinTone›*
|
||||
|
||||
___
|
||||
|
||||
#### ready
|
||||
|
||||
▸ **ready**(): *Promise‹void›*
|
||||
|
@ -375,6 +386,22 @@ all wait for this promise to resolve before doing anything.
|
|||
|
||||
**Returns:** *Promise‹void›*
|
||||
|
||||
___
|
||||
|
||||
#### setPreferredSkinTone
|
||||
|
||||
▸ **setPreferredSkinTone**(`skinTone`: SkinTone): *Promise‹void›*
|
||||
|
||||
Set the user's preferred skin tone. Non-numbers throw an error.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
Name | Type | Description |
|
||||
------ | ------ | ------ |
|
||||
`skinTone` | SkinTone | preferred skin tone |
|
||||
|
||||
**Returns:** *Promise‹void›*
|
||||
|
||||
|
||||
<!-- database API end -->
|
||||
|
||||
|
|
|
@ -3,12 +3,14 @@ import Database from './ImportedDatabase'
|
|||
import { DEFAULT_DATA_SOURCE, DEFAULT_LOCALE } from '../database/constants'
|
||||
import enI18n from './i18n/en'
|
||||
import { mark } from '../shared/marks'
|
||||
import { DEFAULT_SKIN_TONE_EMOJI } from './constants'
|
||||
|
||||
export default class Picker extends SveltePicker {
|
||||
constructor ({
|
||||
locale = DEFAULT_LOCALE,
|
||||
dataSource = DEFAULT_DATA_SOURCE,
|
||||
i18n = enI18n
|
||||
i18n = enI18n,
|
||||
skinToneEmoji = DEFAULT_SKIN_TONE_EMOJI
|
||||
} = {}) {
|
||||
mark('initialLoad')
|
||||
// Make the API simpler, directly pass in the props
|
||||
|
@ -16,7 +18,8 @@ export default class Picker extends SveltePicker {
|
|||
super({
|
||||
props: {
|
||||
database: new Database({ dataSource, locale }),
|
||||
i18n
|
||||
i18n,
|
||||
skinToneEmoji
|
||||
}
|
||||
})
|
||||
this._locale = locale
|
||||
|
@ -25,7 +28,7 @@ export default class Picker extends SveltePicker {
|
|||
}
|
||||
|
||||
static get observedAttributes () {
|
||||
return ['locale', 'dataSource', 'i18n']
|
||||
return ['locale', 'dataSource', 'i18n', 'skinToneEmoji']
|
||||
}
|
||||
|
||||
get locale () {
|
||||
|
|
|
@ -4,7 +4,7 @@ import Database from '../../ImportedDatabase'
|
|||
import enI18n from '../../i18n/en'
|
||||
import { categories } from '../../categories'
|
||||
import { DEFAULT_LOCALE, DEFAULT_DATA_SOURCE } from '../../../database/constants'
|
||||
import { MIN_SEARCH_TEXT_LENGTH } from '../../../shared/constants'
|
||||
import { MIN_SEARCH_TEXT_LENGTH, NUM_SKIN_TONES } from '../../../shared/constants'
|
||||
import { requestIdleCallback } from '../../utils/requestIdleCallback'
|
||||
import { calculateTextWidth } from '../../utils/calculateTextWidth'
|
||||
import { hasZwj } from '../../utils/hasZwj'
|
||||
|
@ -16,14 +16,9 @@ import { applySkinTone } from '../../utils/applySkinTone'
|
|||
import { halt } from '../../utils/halt'
|
||||
import { incrementOrDecrement } from '../../utils/incrementOrDecrement'
|
||||
import { tick } from 'svelte'
|
||||
import { DEFAULT_SKIN_TONE_EMOJI, TIMEOUT_BEFORE_LOADING_MESSAGE } from '../../constants'
|
||||
|
||||
const TIMEOUT_BEFORE_LOADING_MESSAGE = 1000 // 1 second
|
||||
const SKIN_TONE_BASE_TEXT = '\u270c'
|
||||
|
||||
const numSkinTones = 6
|
||||
const skinToneTextForSkinTone = i => (i > 0 ? applySkinTone(SKIN_TONE_BASE_TEXT, i) : SKIN_TONE_BASE_TEXT)
|
||||
const skinTones = Array(numSkinTones).fill().map((_, i) => skinToneTextForSkinTone(i))
|
||||
|
||||
let skinToneEmoji = DEFAULT_SKIN_TONE_EMOJI
|
||||
let i18n = enI18n
|
||||
let initialLoad = true
|
||||
let database = null
|
||||
|
@ -45,6 +40,8 @@ let activeSkinTone = 0
|
|||
let skinToneText // eslint-disable-line no-unused-vars
|
||||
let style = '' // eslint-disable-line no-unused-vars
|
||||
let skinToneButtonLabel = '' // eslint-disable-line no-unused-vars
|
||||
let skinToneTextForSkinTone = ''
|
||||
let skinTones = []
|
||||
|
||||
const getBaselineEmojiWidth = thunk(() => calculateTextWidth(baselineEmoji))
|
||||
|
||||
|
@ -92,10 +89,12 @@ Promise.resolve().then(() => {
|
|||
$: style = `
|
||||
--num-categories: ${categories.length};
|
||||
--indicator-opacity: ${searchMode ? 0 : 1};
|
||||
--num-skintones: ${numSkinTones};`
|
||||
--num-skintones: ${NUM_SKIN_TONES};`
|
||||
|
||||
$: skinToneText = skinToneTextForSkinTone(currentSkinTone)
|
||||
$: skinToneButtonLabel = i18n.skinToneLabel.replace('{skinTone}', i18n.skinTones[currentSkinTone])
|
||||
$: skinToneTextForSkinTone = i => applySkinTone(skinToneEmoji, i)
|
||||
$: skinTones = Array(NUM_SKIN_TONES).fill().map((_, i) => skinToneTextForSkinTone(i))
|
||||
|
||||
// TODO: Chrome has an unfortunate bug where we can't use a simple percent-based transform
|
||||
// here, becuause it's janky. You can especially see this on a Nexus 5.
|
||||
|
@ -390,5 +389,6 @@ async function onSkinToneOptionsBlur () {
|
|||
|
||||
export {
|
||||
database,
|
||||
i18n
|
||||
i18n,
|
||||
skinToneEmoji
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
export const TIMEOUT_BEFORE_LOADING_MESSAGE = 1000 // 1 second
|
||||
export const DEFAULT_SKIN_TONE_EMOJI = '🖐️'
|
|
@ -1 +1,2 @@
|
|||
export const MIN_SEARCH_TEXT_LENGTH = 2
|
||||
export const NUM_SKIN_TONES = 6
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
import {I18n, PickerConstructorOptions, EmojiPickerEventMap, EmojiClickEvent, SkinToneChangeEvent} from "./shared";
|
||||
|
||||
export default class Picker extends HTMLElement {
|
||||
dataSource: string;
|
||||
locale: string;
|
||||
i18n: I18n;
|
||||
dataSource: string
|
||||
locale: string
|
||||
i18n: I18n
|
||||
skinToneEmoji: string
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dataSource - URL to fetch the emojibase data from
|
||||
* @param locale - Locale string
|
||||
* @param i18n - i18n object (see below for details)
|
||||
* @param skinToneEmoji - the emoji to use for the skin tone picker
|
||||
*/
|
||||
constructor({
|
||||
dataSource = 'https://cdn.jsdelivr.net/npm/emojibase-data@5/en/data.json',
|
||||
locale = 'en',
|
||||
i18n
|
||||
i18n,
|
||||
skinToneEmoji = '🖐️'
|
||||
}: PickerConstructorOptions = {}) {
|
||||
super()
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ export interface PickerConstructorOptions {
|
|||
dataSource?: string
|
||||
locale?: string
|
||||
i18n?: I18n
|
||||
skinToneEmoji?: string
|
||||
}
|
||||
|
||||
export interface I18n {
|
||||
|
|
|
@ -2,6 +2,7 @@ import { basicBeforeEach, basicAfterEach, ALL_EMOJI, truncatedEmoji, tick } from
|
|||
import * as testingLibrary from '@testing-library/dom'
|
||||
import Picker from '../../../src/picker/PickerElement.js'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { DEFAULT_SKIN_TONE_EMOJI } from '../../../src/picker/constants'
|
||||
|
||||
const { waitFor, fireEvent } = testingLibrary
|
||||
const { type } = userEvent
|
||||
|
@ -243,4 +244,11 @@ describe('Picker tests', () => {
|
|||
unicode: '🐒'
|
||||
}))
|
||||
})
|
||||
|
||||
test('Change default skin tone emoji', async () => {
|
||||
expect(getByRole('button', { name: /Choose a skin tone/ }).innerHTML).toContain(DEFAULT_SKIN_TONE_EMOJI)
|
||||
picker.skinToneEmoji = '👇'
|
||||
await waitFor(() => expect(getByRole('button', { name: /Choose a skin tone/ }).innerHTML)
|
||||
.toContain('👇'))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -20,7 +20,6 @@ describe('skintones tests', () => {
|
|||
const actualUnicode = applySkinTone(emoji.emoji, skin.tone)
|
||||
if (actualUnicode !== skin.emoji) {
|
||||
wrong.push({ emoji, actualUnicode, skin })
|
||||
applySkinTone(emoji.emoji, skin.tone)
|
||||
} else {
|
||||
right.push({ emoji, actualUnicode, skin })
|
||||
}
|
||||
|
@ -31,10 +30,24 @@ describe('skintones tests', () => {
|
|||
}
|
||||
console.log('wrong', wrong.length, 'right', right.length)
|
||||
for (const w of wrong) {
|
||||
console.log('\n' + debugIt(w.emoji.emoji).join(',') + '\n' +
|
||||
console.log('\n' + w.emoji.emoji + '\n' + debugIt(w.emoji.emoji).join(',') + '\n' +
|
||||
debugIt(w.skin.emoji).join(',') + '\n' +
|
||||
debugIt(w.actualUnicode).join(',') + '\n\n')
|
||||
}
|
||||
console.log('Right')
|
||||
for (const r of right) {
|
||||
console.log('\n' + r.emoji.emoji + '\n' + debugIt(r.emoji.emoji).join(',') + '\n' +
|
||||
debugIt(r.skin.emoji).join(',') + '\n' +
|
||||
debugIt(r.actualUnicode).join(',') + '\n\n')
|
||||
}
|
||||
})
|
||||
|
||||
test('can compute some correct unicode tones', () => {
|
||||
expect(applySkinTone('👍', 0)).toBe('👍')
|
||||
expect(applySkinTone('👍', 3)).toBe('👍🏽')
|
||||
expect(applySkinTone('🧘♀️', 3)).toBe('🧘🏽♀️')
|
||||
expect(applySkinTone('🤌', 2)).toBe('🤌🏼')
|
||||
expect(applySkinTone('🖐️', 5)).toBe('🖐🏿')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue