Add custom emoji lazyload
parent
fe28ab637b
commit
7edf171664
|
@ -137,7 +137,7 @@
|
|||
}
|
||||
|
||||
.emoji-mart-category .emoji-mart-emoji:hover:before {
|
||||
z-index: 0;
|
||||
z-index: -1;
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
|
|
177
package.json
177
package.json
|
@ -1,88 +1,89 @@
|
|||
{
|
||||
"name": "emoji-mart",
|
||||
"version": "3.0.1",
|
||||
"description": "Customizable Slack-like emoji picker for React",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist-es/index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:missive/emoji-mart.git"
|
||||
},
|
||||
"keywords": [
|
||||
"react",
|
||||
"emoji",
|
||||
"picker"
|
||||
],
|
||||
"author": "Etienne Lemay",
|
||||
"license": "BSD-3-Clause",
|
||||
"bugs": {
|
||||
"url": "https://github.com/missive/emoji-mart/issues"
|
||||
},
|
||||
"homepage": "https://github.com/missive/emoji-mart",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"prop-types": "^15.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^0.14.0 || ^15.0.0-0 || ^16.0.0 || ^17.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-react": "7.0.0",
|
||||
"@storybook/addon-actions": "^3.2.11",
|
||||
"@storybook/addon-knobs": "^3.2.10",
|
||||
"@storybook/addon-links": "^3.2.10",
|
||||
"@storybook/addon-options": "3.2.10",
|
||||
"@storybook/react": "^3.2.11",
|
||||
"babel-jest": "^24.9.0",
|
||||
"babel-loader": "^8.0.0",
|
||||
"babel-plugin-transform-define": "^2.0.0",
|
||||
"emoji-datasource": "5.0.1",
|
||||
"emojilib": "^2.2.1",
|
||||
"enzyme": "^3.9.0",
|
||||
"enzyme-adapter-react-16": "^1.11.2",
|
||||
"inflection": "1.10.0",
|
||||
"jest": "^24.9.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"prettier": "^1.16.4",
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0",
|
||||
"react-test-renderer": "^16.8.4",
|
||||
"rimraf": "2.5.2",
|
||||
"size-limit": "^0.11.4",
|
||||
"webpack": "^3.6.0"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist/ dist-es/ dist-modern/",
|
||||
"build:data": "node scripts/build-data",
|
||||
"build:dist": "npm run build:cjs && npm run build:es && npm run build:modern",
|
||||
"build:cjs": "BABEL_ENV=legacy-cjs babel src --out-dir dist --ignore '**/__tests__/*'",
|
||||
"build:es": "BABEL_ENV=legacy-es babel src --out-dir dist-es --ignore '**/__tests__/*'",
|
||||
"build:modern": "BABEL_ENV=modern babel src --out-dir dist-modern --ignore '**/__tests__/*'",
|
||||
"build:docs": "cp css/emoji-mart.css docs && webpack --config ./docs/webpack.config.js",
|
||||
"build": "npm run clean && npm run build:dist",
|
||||
"watch": "BABEL_ENV=legacy-cjs babel src --watch --out-dir dist --ignore '**/__tests__/*'",
|
||||
"start": "npm run watch",
|
||||
"react:clean": "rimraf node_modules/{react,react-dom,react-addons-test-utils}",
|
||||
"react:14": "npm run react:clean && npm i react@^0.14 react-dom@^0.14 react-addons-test-utils@^0.14 --save-dev",
|
||||
"react:15": "npm run react:clean && npm i react@^15 react-dom@^15 react-addons-test-utils@^15 --save-dev",
|
||||
"test": "npm run clean && jest",
|
||||
"test:size": "size-limit",
|
||||
"test:ssr": "node test/ssr.js",
|
||||
"prepublishOnly": "npm run build",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook",
|
||||
"prettier": "prettier --write \"{src,scripts,test,stories}/**/*.js\"",
|
||||
"prettier:check": "prettier --check \"{src,scripts,test,stories}/**/*.js\"",
|
||||
"prepare": "npm run build:dist"
|
||||
},
|
||||
"size-limit": [
|
||||
{
|
||||
"path": "dist-es/index.js",
|
||||
"limit": "82 KB"
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"name": "emoji-mart-lazyload",
|
||||
"version": "3.0.1h",
|
||||
"description": "Customizable Slack-like emoji picker for React",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist-es/index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:mashirozx/emoji-mart.git"
|
||||
},
|
||||
"keywords": [
|
||||
"react",
|
||||
"emoji",
|
||||
"picker"
|
||||
],
|
||||
"author": "Etienne Lemay",
|
||||
"license": "BSD-3-Clause",
|
||||
"bugs": {
|
||||
"url": "https://github.com/mashirozx/emoji-mart/issues"
|
||||
},
|
||||
"homepage": "https://github.com/mashirozx/emoji-mart",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"intersection-observer": "^0.12.0",
|
||||
"prop-types": "^15.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^0.14.0 || ^15.0.0-0 || ^16.0.0 || ^17.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-react": "7.0.0",
|
||||
"@storybook/addon-actions": "^3.2.11",
|
||||
"@storybook/addon-knobs": "^3.2.10",
|
||||
"@storybook/addon-links": "^3.2.10",
|
||||
"@storybook/addon-options": "3.2.10",
|
||||
"@storybook/react": "^3.2.11",
|
||||
"babel-jest": "^24.9.0",
|
||||
"babel-loader": "^8.0.0",
|
||||
"babel-plugin-transform-define": "^2.0.0",
|
||||
"emoji-datasource": "5.0.1",
|
||||
"emojilib": "^2.2.1",
|
||||
"enzyme": "^3.9.0",
|
||||
"enzyme-adapter-react-16": "^1.11.2",
|
||||
"inflection": "1.10.0",
|
||||
"jest": "^24.9.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"prettier": "^1.16.4",
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0",
|
||||
"react-test-renderer": "^16.8.4",
|
||||
"rimraf": "2.5.2",
|
||||
"size-limit": "^0.11.4",
|
||||
"webpack": "^3.6.0"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist/ dist-es/ dist-modern/",
|
||||
"build:data": "node scripts/build-data",
|
||||
"build:dist": "npm run build:cjs && npm run build:es && npm run build:modern",
|
||||
"build:cjs": "BABEL_ENV=legacy-cjs babel src --out-dir dist --ignore '**/__tests__/*'",
|
||||
"build:es": "BABEL_ENV=legacy-es babel src --out-dir dist-es --ignore '**/__tests__/*'",
|
||||
"build:modern": "BABEL_ENV=modern babel src --out-dir dist-modern --ignore '**/__tests__/*'",
|
||||
"build:docs": "cp css/emoji-mart.css docs && webpack --config ./docs/webpack.config.js",
|
||||
"build": "npm run clean && npm run build:dist",
|
||||
"watch": "BABEL_ENV=legacy-cjs babel src --watch --out-dir dist --ignore '**/__tests__/*'",
|
||||
"start": "npm run watch",
|
||||
"react:clean": "rimraf node_modules/{react,react-dom,react-addons-test-utils}",
|
||||
"react:14": "npm run react:clean && npm i react@^0.14 react-dom@^0.14 react-addons-test-utils@^0.14 --save-dev",
|
||||
"react:15": "npm run react:clean && npm i react@^15 react-dom@^15 react-addons-test-utils@^15 --save-dev",
|
||||
"test": "npm run clean && jest",
|
||||
"test:size": "size-limit",
|
||||
"test:ssr": "node test/ssr.js",
|
||||
"prepublishOnly": "npm run build",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook",
|
||||
"prettier": "prettier --write \"{src,scripts,test,stories}/**/*.js\"",
|
||||
"prettier:check": "prettier --check \"{src,scripts,test,stories}/**/*.js\"",
|
||||
"prepare": "npm run build:dist"
|
||||
},
|
||||
"size-limit": [
|
||||
{
|
||||
"path": "dist-es/index.js",
|
||||
"limit": "82 KB"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,248 +1,287 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import frequently from '../utils/frequently'
|
||||
import { getData } from '../utils'
|
||||
import NimbleEmoji from './emoji/nimble-emoji'
|
||||
import NotFound from './not-found'
|
||||
|
||||
export default class Category extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.data = props.data
|
||||
this.setContainerRef = this.setContainerRef.bind(this)
|
||||
this.setLabelRef = this.setLabelRef.bind(this)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.margin = 0
|
||||
this.minMargin = 0
|
||||
|
||||
this.memoizeSize()
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
var {
|
||||
name,
|
||||
perLine,
|
||||
native,
|
||||
hasStickyPosition,
|
||||
emojis,
|
||||
emojiProps,
|
||||
} = this.props,
|
||||
{ skin, size, set } = emojiProps,
|
||||
{
|
||||
perLine: nextPerLine,
|
||||
native: nextNative,
|
||||
hasStickyPosition: nextHasStickyPosition,
|
||||
emojis: nextEmojis,
|
||||
emojiProps: nextEmojiProps,
|
||||
} = nextProps,
|
||||
{ skin: nextSkin, size: nextSize, set: nextSet } = nextEmojiProps,
|
||||
shouldUpdate = false
|
||||
|
||||
if (name == 'Recent' && perLine != nextPerLine) {
|
||||
shouldUpdate = true
|
||||
}
|
||||
|
||||
if (name == 'Search') {
|
||||
shouldUpdate = !(emojis == nextEmojis)
|
||||
}
|
||||
|
||||
if (
|
||||
skin != nextSkin ||
|
||||
size != nextSize ||
|
||||
native != nextNative ||
|
||||
set != nextSet ||
|
||||
hasStickyPosition != nextHasStickyPosition
|
||||
) {
|
||||
shouldUpdate = true
|
||||
}
|
||||
|
||||
return shouldUpdate
|
||||
}
|
||||
|
||||
memoizeSize() {
|
||||
if (!this.container) {
|
||||
// probably this is a test environment, e.g. jest
|
||||
this.top = 0
|
||||
this.maxMargin = 0
|
||||
return
|
||||
}
|
||||
var parent = this.container.parentElement
|
||||
var { top, height } = this.container.getBoundingClientRect()
|
||||
var { top: parentTop } = parent.getBoundingClientRect()
|
||||
var { height: labelHeight } = this.label.getBoundingClientRect()
|
||||
|
||||
this.top = top - parentTop + parent.scrollTop
|
||||
|
||||
if (height == 0) {
|
||||
this.maxMargin = 0
|
||||
} else {
|
||||
this.maxMargin = height - labelHeight
|
||||
}
|
||||
}
|
||||
|
||||
handleScroll(scrollTop) {
|
||||
var margin = scrollTop - this.top
|
||||
margin = margin < this.minMargin ? this.minMargin : margin
|
||||
margin = margin > this.maxMargin ? this.maxMargin : margin
|
||||
|
||||
if (margin == this.margin) return
|
||||
|
||||
if (!this.props.hasStickyPosition) {
|
||||
this.label.style.top = `${margin}px`
|
||||
}
|
||||
|
||||
this.margin = margin
|
||||
return true
|
||||
}
|
||||
|
||||
getEmojis() {
|
||||
var { name, emojis, recent, perLine } = this.props
|
||||
|
||||
if (name == 'Recent') {
|
||||
let { custom } = this.props
|
||||
let frequentlyUsed = recent || frequently.get(perLine)
|
||||
|
||||
if (frequentlyUsed.length) {
|
||||
emojis = frequentlyUsed
|
||||
.map((id) => {
|
||||
const emoji = custom.filter((e) => e.id === id)[0]
|
||||
if (emoji) {
|
||||
return emoji
|
||||
}
|
||||
|
||||
return id
|
||||
})
|
||||
.filter((id) => !!getData(id, null, null, this.data))
|
||||
}
|
||||
|
||||
if (emojis.length === 0 && frequentlyUsed.length > 0) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
if (emojis) {
|
||||
emojis = emojis.slice(0)
|
||||
}
|
||||
|
||||
return emojis
|
||||
}
|
||||
|
||||
updateDisplay(display) {
|
||||
var emojis = this.getEmojis()
|
||||
|
||||
if (!emojis || !this.container) {
|
||||
return
|
||||
}
|
||||
|
||||
this.container.style.display = display
|
||||
}
|
||||
|
||||
setContainerRef(c) {
|
||||
this.container = c
|
||||
}
|
||||
|
||||
setLabelRef(c) {
|
||||
this.label = c
|
||||
}
|
||||
|
||||
render() {
|
||||
var {
|
||||
id,
|
||||
name,
|
||||
hasStickyPosition,
|
||||
emojiProps,
|
||||
i18n,
|
||||
notFound,
|
||||
notFoundEmoji,
|
||||
} = this.props,
|
||||
emojis = this.getEmojis(),
|
||||
labelStyles = {},
|
||||
labelSpanStyles = {},
|
||||
containerStyles = {}
|
||||
|
||||
if (!emojis) {
|
||||
containerStyles = {
|
||||
display: 'none',
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasStickyPosition) {
|
||||
labelStyles = {
|
||||
height: 28,
|
||||
}
|
||||
|
||||
labelSpanStyles = {
|
||||
position: 'absolute',
|
||||
}
|
||||
}
|
||||
|
||||
const label = i18n.categories[id] || name
|
||||
|
||||
return (
|
||||
<section
|
||||
ref={this.setContainerRef}
|
||||
className="emoji-mart-category"
|
||||
aria-label={label}
|
||||
style={containerStyles}
|
||||
>
|
||||
<div
|
||||
style={labelStyles}
|
||||
data-name={name}
|
||||
className="emoji-mart-category-label"
|
||||
>
|
||||
<span
|
||||
style={labelSpanStyles}
|
||||
ref={this.setLabelRef}
|
||||
aria-hidden={true /* already labeled by the section aria-label */}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ul className="emoji-mart-category-list">
|
||||
{emojis &&
|
||||
emojis.map((emoji) => (
|
||||
<li
|
||||
key={
|
||||
(emoji.short_names && emoji.short_names.join('_')) || emoji
|
||||
}
|
||||
>
|
||||
{NimbleEmoji({ emoji: emoji, data: this.data, ...emojiProps })}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{emojis && !emojis.length && (
|
||||
<NotFound
|
||||
i18n={i18n}
|
||||
notFound={notFound}
|
||||
notFoundEmoji={notFoundEmoji}
|
||||
data={this.data}
|
||||
emojiProps={emojiProps}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Category.propTypes /* remove-proptypes */ = {
|
||||
emojis: PropTypes.array,
|
||||
hasStickyPosition: PropTypes.bool,
|
||||
name: PropTypes.string.isRequired,
|
||||
native: PropTypes.bool.isRequired,
|
||||
perLine: PropTypes.number.isRequired,
|
||||
emojiProps: PropTypes.object.isRequired,
|
||||
recent: PropTypes.arrayOf(PropTypes.string),
|
||||
notFound: PropTypes.func,
|
||||
notFoundEmoji: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
Category.defaultProps = {
|
||||
emojis: [],
|
||||
hasStickyPosition: true,
|
||||
}
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import frequently from '../utils/frequently'
|
||||
import { getData } from '../utils'
|
||||
import NimbleEmoji from './emoji/nimble-emoji'
|
||||
import NotFound from './not-found'
|
||||
import 'intersection-observer'
|
||||
|
||||
export default class Category extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.data = props.data
|
||||
this.setContainerRef = this.setContainerRef.bind(this)
|
||||
this.setLabelRef = this.setLabelRef.bind(this)
|
||||
|
||||
this.imageObserver = new window.IntersectionObserver((entries, observer) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
const image = entry.target;
|
||||
image.src = image.dataset.src;
|
||||
image.classList.remove("lazy");
|
||||
this.imageObserver.unobserve(image);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.margin = 0
|
||||
this.minMargin = 0
|
||||
|
||||
this.memoizeSize()
|
||||
|
||||
this.lazyloadImages = []
|
||||
this.lazyload()
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.lazyload()
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.removeLazyloadObserver()
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
var {
|
||||
name,
|
||||
perLine,
|
||||
native,
|
||||
hasStickyPosition,
|
||||
emojis,
|
||||
emojiProps,
|
||||
} = this.props,
|
||||
{ skin, size, set } = emojiProps,
|
||||
{
|
||||
perLine: nextPerLine,
|
||||
native: nextNative,
|
||||
hasStickyPosition: nextHasStickyPosition,
|
||||
emojis: nextEmojis,
|
||||
emojiProps: nextEmojiProps,
|
||||
} = nextProps,
|
||||
{ skin: nextSkin, size: nextSize, set: nextSet } = nextEmojiProps,
|
||||
shouldUpdate = false
|
||||
|
||||
if (name == 'Recent' && perLine != nextPerLine) {
|
||||
shouldUpdate = true
|
||||
}
|
||||
|
||||
if (name == 'Search') {
|
||||
// shouldUpdate = !(emojis == nextEmojis)
|
||||
shouldUpdate = true
|
||||
}
|
||||
|
||||
if (
|
||||
skin != nextSkin ||
|
||||
size != nextSize ||
|
||||
native != nextNative ||
|
||||
set != nextSet ||
|
||||
hasStickyPosition != nextHasStickyPosition
|
||||
) {
|
||||
shouldUpdate = true
|
||||
}
|
||||
|
||||
return shouldUpdate
|
||||
}
|
||||
|
||||
memoizeSize() {
|
||||
if (!this.container) {
|
||||
// probably this is a test environment, e.g. jest
|
||||
this.top = 0
|
||||
this.maxMargin = 0
|
||||
return
|
||||
}
|
||||
var parent = this.container.parentElement
|
||||
var { top, height } = this.container.getBoundingClientRect()
|
||||
var { top: parentTop } = parent.getBoundingClientRect()
|
||||
var { height: labelHeight } = this.label.getBoundingClientRect()
|
||||
|
||||
this.top = top - parentTop + parent.scrollTop
|
||||
|
||||
if (height == 0) {
|
||||
this.maxMargin = 0
|
||||
} else {
|
||||
this.maxMargin = height - labelHeight
|
||||
}
|
||||
}
|
||||
|
||||
lazyload() {
|
||||
this.removeLazyloadObserver()
|
||||
this.lazyloadImages = this.container.querySelectorAll(".lazy");
|
||||
|
||||
this.lazyloadImages.forEach((image) => {
|
||||
this.imageObserver.observe(image);
|
||||
});
|
||||
}
|
||||
|
||||
removeLazyloadObserver() {
|
||||
this.lazyloadImages.forEach((image) => {
|
||||
this.imageObserver.unobserve(image);
|
||||
})
|
||||
}
|
||||
|
||||
handleScroll(scrollTop) {
|
||||
var margin = scrollTop - this.top
|
||||
margin = margin < this.minMargin ? this.minMargin : margin
|
||||
margin = margin > this.maxMargin ? this.maxMargin : margin
|
||||
|
||||
if (margin == this.margin) return
|
||||
|
||||
if (!this.props.hasStickyPosition) {
|
||||
this.label.style.top = `${margin}px`
|
||||
}
|
||||
|
||||
this.margin = margin
|
||||
return true
|
||||
}
|
||||
|
||||
getEmojis() {
|
||||
var { name, emojis, recent, perLine } = this.props
|
||||
|
||||
if (name == 'Recent') {
|
||||
let { custom } = this.props
|
||||
let frequentlyUsed = recent || frequently.get(perLine)
|
||||
|
||||
if (frequentlyUsed.length) {
|
||||
emojis = frequentlyUsed
|
||||
.map((id) => {
|
||||
const emoji = custom.filter((e) => e.id === id)[0]
|
||||
if (emoji) {
|
||||
return emoji
|
||||
}
|
||||
|
||||
return id
|
||||
})
|
||||
.filter((id) => !!getData(id, null, null, this.data))
|
||||
}
|
||||
|
||||
if (emojis.length === 0 && frequentlyUsed.length > 0) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
if (emojis) {
|
||||
emojis = emojis.slice(0)
|
||||
}
|
||||
|
||||
return emojis
|
||||
}
|
||||
|
||||
updateDisplay(display) {
|
||||
var emojis = this.getEmojis()
|
||||
|
||||
if (!emojis || !this.container) {
|
||||
return
|
||||
}
|
||||
|
||||
this.container.style.display = display
|
||||
}
|
||||
|
||||
setContainerRef(c) {
|
||||
this.container = c
|
||||
}
|
||||
|
||||
setLabelRef(c) {
|
||||
this.label = c
|
||||
}
|
||||
|
||||
render() {
|
||||
var {
|
||||
id,
|
||||
name,
|
||||
hasStickyPosition,
|
||||
emojiProps,
|
||||
i18n,
|
||||
notFound,
|
||||
notFoundEmoji,
|
||||
} = this.props,
|
||||
emojis = this.getEmojis(),
|
||||
labelStyles = {},
|
||||
labelSpanStyles = {},
|
||||
containerStyles = {}
|
||||
|
||||
if (!emojis) {
|
||||
containerStyles = {
|
||||
display: 'none',
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasStickyPosition) {
|
||||
labelStyles = {
|
||||
height: 28,
|
||||
}
|
||||
|
||||
labelSpanStyles = {
|
||||
position: 'absolute',
|
||||
}
|
||||
}
|
||||
|
||||
const label = i18n.categories[id] || name
|
||||
|
||||
return (
|
||||
<section
|
||||
ref={this.setContainerRef}
|
||||
className="emoji-mart-category"
|
||||
aria-label={label}
|
||||
style={containerStyles}
|
||||
>
|
||||
<div
|
||||
style={labelStyles}
|
||||
data-name={name}
|
||||
className="emoji-mart-category-label"
|
||||
>
|
||||
<span
|
||||
style={labelSpanStyles}
|
||||
ref={this.setLabelRef}
|
||||
aria-hidden={true /* already labeled by the section aria-label */}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ul className="emoji-mart-category-list">
|
||||
{emojis &&
|
||||
emojis.map((emoji) => (
|
||||
<li
|
||||
key={
|
||||
(emoji.short_names && emoji.short_names.join('_')) || emoji
|
||||
}
|
||||
>
|
||||
{NimbleEmoji({ emoji: emoji, data: this.data, ...emojiProps, lazy: true })}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{emojis && !emojis.length && (
|
||||
<NotFound
|
||||
i18n={i18n}
|
||||
notFound={notFound}
|
||||
notFoundEmoji={notFoundEmoji}
|
||||
data={this.data}
|
||||
emojiProps={emojiProps}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Category.propTypes /* remove-proptypes */ = {
|
||||
emojis: PropTypes.array,
|
||||
hasStickyPosition: PropTypes.bool,
|
||||
name: PropTypes.string.isRequired,
|
||||
native: PropTypes.bool.isRequired,
|
||||
perLine: PropTypes.number.isRequired,
|
||||
emojiProps: PropTypes.object.isRequired,
|
||||
recent: PropTypes.arrayOf(PropTypes.string),
|
||||
notFound: PropTypes.func,
|
||||
notFoundEmoji: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
Category.defaultProps = {
|
||||
emojis: [],
|
||||
hasStickyPosition: true,
|
||||
}
|
||||
|
|
|
@ -1,222 +1,240 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { getData, getSanitizedData, unifiedToNative } from '../../utils'
|
||||
import { uncompress } from '../../utils/data'
|
||||
import { EmojiPropTypes } from '../../utils/shared-props'
|
||||
import { EmojiDefaultProps } from '../../utils/shared-default-props'
|
||||
|
||||
const _getData = (props) => {
|
||||
var { emoji, skin, set, data } = props
|
||||
return getData(emoji, skin, set, data)
|
||||
}
|
||||
|
||||
const _getPosition = (props) => {
|
||||
var { sheet_x, sheet_y } = _getData(props),
|
||||
multiplyX = 100 / (props.sheetColumns - 1),
|
||||
multiplyY = 100 / (props.sheetRows - 1)
|
||||
|
||||
return `${multiplyX * sheet_x}% ${multiplyY * sheet_y}%`
|
||||
}
|
||||
|
||||
const _getSanitizedData = (props) => {
|
||||
var { emoji, skin, set, data } = props
|
||||
return getSanitizedData(emoji, skin, set, data)
|
||||
}
|
||||
|
||||
const _handleClick = (e, props) => {
|
||||
if (!props.onClick) {
|
||||
return
|
||||
}
|
||||
var { onClick } = props,
|
||||
emoji = _getSanitizedData(props)
|
||||
|
||||
onClick(emoji, e)
|
||||
}
|
||||
|
||||
const _handleOver = (e, props) => {
|
||||
if (!props.onOver) {
|
||||
return
|
||||
}
|
||||
var { onOver } = props,
|
||||
emoji = _getSanitizedData(props)
|
||||
|
||||
onOver(emoji, e)
|
||||
}
|
||||
|
||||
const _handleLeave = (e, props) => {
|
||||
if (!props.onLeave) {
|
||||
return
|
||||
}
|
||||
var { onLeave } = props,
|
||||
emoji = _getSanitizedData(props)
|
||||
|
||||
onLeave(emoji, e)
|
||||
}
|
||||
|
||||
const _isNumeric = (value) => {
|
||||
return !isNaN(value - parseFloat(value))
|
||||
}
|
||||
|
||||
const _convertStyleToCSS = (style) => {
|
||||
let div = document.createElement('div')
|
||||
|
||||
for (let key in style) {
|
||||
let value = style[key]
|
||||
|
||||
if (_isNumeric(value)) {
|
||||
value += 'px'
|
||||
}
|
||||
|
||||
div.style[key] = value
|
||||
}
|
||||
|
||||
return div.getAttribute('style')
|
||||
}
|
||||
|
||||
const NimbleEmoji = (props) => {
|
||||
if (props.data.compressed) {
|
||||
uncompress(props.data)
|
||||
}
|
||||
|
||||
for (let k in NimbleEmoji.defaultProps) {
|
||||
if (props[k] == undefined && NimbleEmoji.defaultProps[k] != undefined) {
|
||||
props[k] = NimbleEmoji.defaultProps[k]
|
||||
}
|
||||
}
|
||||
|
||||
let data = _getData(props)
|
||||
if (!data) {
|
||||
if (props.fallback) {
|
||||
return props.fallback(null, props)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
let { unified, custom, short_names, imageUrl } = data,
|
||||
style = {},
|
||||
children = props.children,
|
||||
className = 'emoji-mart-emoji',
|
||||
nativeEmoji = unified && unifiedToNative(unified),
|
||||
// combine the emoji itself and all shortcodes into an accessible label
|
||||
label = [nativeEmoji]
|
||||
.concat(short_names)
|
||||
.filter(Boolean)
|
||||
.join(', '),
|
||||
title = null
|
||||
|
||||
if (!unified && !custom) {
|
||||
if (props.fallback) {
|
||||
return props.fallback(data, props)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
if (props.tooltip) {
|
||||
title = short_names[0]
|
||||
}
|
||||
|
||||
if (props.native && unified) {
|
||||
className += ' emoji-mart-emoji-native'
|
||||
style = { fontSize: props.size }
|
||||
children = nativeEmoji
|
||||
|
||||
if (props.forceSize) {
|
||||
style.display = 'inline-block'
|
||||
style.width = props.size
|
||||
style.height = props.size
|
||||
style.wordBreak = 'keep-all'
|
||||
}
|
||||
} else if (custom) {
|
||||
className += ' emoji-mart-emoji-custom'
|
||||
style = {
|
||||
width: props.size,
|
||||
height: props.size,
|
||||
display: 'inline-block',
|
||||
}
|
||||
if (data.spriteUrl) {
|
||||
style = {
|
||||
...style,
|
||||
backgroundImage: `url(${data.spriteUrl})`,
|
||||
backgroundSize: `${100 * props.sheetColumns}% ${100 *
|
||||
props.sheetRows}%`,
|
||||
backgroundPosition: _getPosition(props),
|
||||
}
|
||||
} else {
|
||||
style = {
|
||||
...style,
|
||||
backgroundImage: `url(${imageUrl})`,
|
||||
backgroundSize: 'contain',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPosition: 'center',
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let setHasEmoji =
|
||||
data[`has_img_${props.set}`] == undefined || data[`has_img_${props.set}`]
|
||||
|
||||
if (!setHasEmoji) {
|
||||
if (props.fallback) {
|
||||
return props.fallback(data, props)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
} else {
|
||||
style = {
|
||||
width: props.size,
|
||||
height: props.size,
|
||||
display: 'inline-block',
|
||||
backgroundImage: `url(${props.backgroundImageFn(
|
||||
props.set,
|
||||
props.sheetSize,
|
||||
)})`,
|
||||
backgroundSize: `${100 * props.sheetColumns}% ${100 *
|
||||
props.sheetRows}%`,
|
||||
backgroundPosition: _getPosition(props),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var Tag = {
|
||||
name: 'span',
|
||||
props: {},
|
||||
}
|
||||
|
||||
if (props.onClick && props.useButton) {
|
||||
Tag.name = 'button'
|
||||
Tag.props = {
|
||||
type: 'button',
|
||||
}
|
||||
}
|
||||
|
||||
if (props.html) {
|
||||
style = _convertStyleToCSS(style)
|
||||
return `<${Tag.name} style='${style}' aria-label='${label}' ${
|
||||
title ? `title='${title}'` : ''
|
||||
} class='${className}'>${children || ''}</${Tag.name}>`
|
||||
} else {
|
||||
return (
|
||||
<Tag.name
|
||||
onClick={(e) => _handleClick(e, props)}
|
||||
onMouseEnter={(e) => _handleOver(e, props)}
|
||||
onMouseLeave={(e) => _handleLeave(e, props)}
|
||||
aria-label={label}
|
||||
title={title}
|
||||
className={className}
|
||||
{...Tag.props}
|
||||
>
|
||||
<span style={style}>{children}</span>
|
||||
</Tag.name>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
NimbleEmoji.propTypes /* remove-proptypes */ = {
|
||||
...EmojiPropTypes,
|
||||
data: PropTypes.object.isRequired,
|
||||
}
|
||||
NimbleEmoji.defaultProps = EmojiDefaultProps
|
||||
|
||||
export default NimbleEmoji
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { getData, getSanitizedData, unifiedToNative } from '../../utils'
|
||||
import { uncompress } from '../../utils/data'
|
||||
import { EmojiPropTypes } from '../../utils/shared-props'
|
||||
import { EmojiDefaultProps } from '../../utils/shared-default-props'
|
||||
|
||||
const _getData = (props) => {
|
||||
var { emoji, skin, set, data } = props
|
||||
return getData(emoji, skin, set, data)
|
||||
}
|
||||
|
||||
const _getPosition = (props) => {
|
||||
var { sheet_x, sheet_y } = _getData(props),
|
||||
multiplyX = 100 / (props.sheetColumns - 1),
|
||||
multiplyY = 100 / (props.sheetRows - 1)
|
||||
|
||||
return `${multiplyX * sheet_x}% ${multiplyY * sheet_y}%`
|
||||
}
|
||||
|
||||
const _getSanitizedData = (props) => {
|
||||
var { emoji, skin, set, data } = props
|
||||
return getSanitizedData(emoji, skin, set, data)
|
||||
}
|
||||
|
||||
const _handleClick = (e, props) => {
|
||||
if (!props.onClick) {
|
||||
return
|
||||
}
|
||||
var { onClick } = props,
|
||||
emoji = _getSanitizedData(props)
|
||||
|
||||
onClick(emoji, e)
|
||||
}
|
||||
|
||||
const _handleOver = (e, props) => {
|
||||
if (!props.onOver) {
|
||||
return
|
||||
}
|
||||
var { onOver } = props,
|
||||
emoji = _getSanitizedData(props)
|
||||
|
||||
onOver(emoji, e)
|
||||
}
|
||||
|
||||
const _handleLeave = (e, props) => {
|
||||
if (!props.onLeave) {
|
||||
return
|
||||
}
|
||||
var { onLeave } = props,
|
||||
emoji = _getSanitizedData(props)
|
||||
|
||||
onLeave(emoji, e)
|
||||
}
|
||||
|
||||
const _isNumeric = (value) => {
|
||||
return !isNaN(value - parseFloat(value))
|
||||
}
|
||||
|
||||
const _convertStyleToCSS = (style) => {
|
||||
let div = document.createElement('div')
|
||||
|
||||
for (let key in style) {
|
||||
let value = style[key]
|
||||
|
||||
if (_isNumeric(value)) {
|
||||
value += 'px'
|
||||
}
|
||||
|
||||
div.style[key] = value
|
||||
}
|
||||
|
||||
return div.getAttribute('style')
|
||||
}
|
||||
|
||||
const NimbleEmoji = (props) => {
|
||||
if (props.data.compressed) {
|
||||
uncompress(props.data)
|
||||
}
|
||||
|
||||
for (let k in NimbleEmoji.defaultProps) {
|
||||
if (props[k] == undefined && NimbleEmoji.defaultProps[k] != undefined) {
|
||||
props[k] = NimbleEmoji.defaultProps[k]
|
||||
}
|
||||
}
|
||||
|
||||
let data = _getData(props)
|
||||
if (!data) {
|
||||
if (props.fallback) {
|
||||
return props.fallback(null, props)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
let { unified, custom, short_names, imageUrl } = data,
|
||||
style = {},
|
||||
children = props.children,
|
||||
className = 'emoji-mart-emoji',
|
||||
nativeEmoji = unified && unifiedToNative(unified),
|
||||
// combine the emoji itself and all shortcodes into an accessible label
|
||||
label = [nativeEmoji]
|
||||
.concat(short_names)
|
||||
.filter(Boolean)
|
||||
.join(', '),
|
||||
title = null
|
||||
|
||||
if (!unified && !custom) {
|
||||
if (props.fallback) {
|
||||
return props.fallback(data, props)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
if (props.tooltip) {
|
||||
title = short_names[0]
|
||||
}
|
||||
|
||||
if (props.native && unified) {
|
||||
className += ' emoji-mart-emoji-native'
|
||||
style = { fontSize: props.size }
|
||||
children = nativeEmoji
|
||||
|
||||
if (props.forceSize) {
|
||||
style.display = 'inline-block'
|
||||
style.width = props.size
|
||||
style.height = props.size
|
||||
style.wordBreak = 'keep-all'
|
||||
}
|
||||
} else if (custom) {
|
||||
className += ' emoji-mart-emoji-custom'
|
||||
style = {
|
||||
width: props.size,
|
||||
height: props.size,
|
||||
display: 'inline-block',
|
||||
}
|
||||
if (data.spriteUrl) {
|
||||
style = {
|
||||
...style,
|
||||
backgroundImage: `url(${data.spriteUrl})`,
|
||||
backgroundSize: `${100 * props.sheetColumns}% ${100 *
|
||||
props.sheetRows}%`,
|
||||
backgroundPosition: _getPosition(props),
|
||||
}
|
||||
} else {
|
||||
style = {
|
||||
...style,
|
||||
backgroundImage: `url(${imageUrl})`,
|
||||
backgroundSize: 'contain',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPosition: 'center',
|
||||
}
|
||||
if(props.lazy){
|
||||
delete style.backgroundImage
|
||||
delete style.backgroundSize
|
||||
delete style.backgroundRepeat
|
||||
delete style.backgroundPosition
|
||||
style.objectFit = 'contain'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let setHasEmoji =
|
||||
data[`has_img_${props.set}`] == undefined || data[`has_img_${props.set}`]
|
||||
|
||||
if (!setHasEmoji) {
|
||||
if (props.fallback) {
|
||||
return props.fallback(data, props)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
} else {
|
||||
style = {
|
||||
width: props.size,
|
||||
height: props.size,
|
||||
display: 'inline-block',
|
||||
backgroundImage: `url(${props.backgroundImageFn(
|
||||
props.set,
|
||||
props.sheetSize,
|
||||
)})`,
|
||||
backgroundSize: `${100 * props.sheetColumns}% ${100 *
|
||||
props.sheetRows}%`,
|
||||
backgroundPosition: _getPosition(props),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var Tag = {
|
||||
name: 'span',
|
||||
props: {},
|
||||
}
|
||||
|
||||
if (props.onClick && props.useButton) {
|
||||
Tag.name = 'button'
|
||||
Tag.props = {
|
||||
type: 'button',
|
||||
}
|
||||
}
|
||||
|
||||
if (props.html) {
|
||||
style = _convertStyleToCSS(style)
|
||||
return `<${Tag.name} style='${style}' aria-label='${label}' ${
|
||||
title ? `title='${title}'` : ''
|
||||
} class='${className}'>${children || ''}</${Tag.name}>`
|
||||
} else {
|
||||
return (
|
||||
<Tag.name
|
||||
onClick={(e) => _handleClick(e, props)}
|
||||
onMouseEnter={(e) => _handleOver(e, props)}
|
||||
onMouseLeave={(e) => _handleLeave(e, props)}
|
||||
aria-label={label}
|
||||
title={title}
|
||||
className={className}
|
||||
{...Tag.props}
|
||||
>
|
||||
{
|
||||
custom && !data.spriteUrl && props.lazy
|
||||
?
|
||||
<img
|
||||
style={style}
|
||||
className="lazy"
|
||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP89B8AAukB8/71MdcAAAAASUVORK5CYII="
|
||||
data-src={imageUrl}
|
||||
/>
|
||||
:
|
||||
<span style={style}>{children}</span>
|
||||
}
|
||||
</Tag.name>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
NimbleEmoji.propTypes /* remove-proptypes */ = {
|
||||
...EmojiPropTypes,
|
||||
data: PropTypes.object.isRequired,
|
||||
}
|
||||
NimbleEmoji.defaultProps = EmojiDefaultProps
|
||||
|
||||
export default NimbleEmoji
|
||||
|
|
Loading…
Reference in New Issue