283 lines
7.0 KiB
HTML
283 lines
7.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/picker.js">
|
|
<link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/database.js">
|
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.8em%22 font-size=%2280%22>😀</text></svg>">
|
|
<meta charset="UTF-8">
|
|
<title>emoji-picker-element</title>
|
|
<meta name="viewport" content="width=device-width">
|
|
<style>
|
|
html {
|
|
font-family: sans-serif;
|
|
}
|
|
|
|
body {
|
|
margin: 0 auto;
|
|
max-width: 1000px;
|
|
}
|
|
|
|
h1 {
|
|
padding: 0 10px;
|
|
text-align: center;
|
|
}
|
|
|
|
p {
|
|
padding: 20px;
|
|
}
|
|
|
|
.github-banner {
|
|
position: fixed;
|
|
top: 0;
|
|
right: 0;
|
|
z-index: 1000;
|
|
}
|
|
|
|
emoji-picker {
|
|
margin: 0 auto;
|
|
}
|
|
|
|
pre {
|
|
border: 1px solid #ededed;
|
|
padding: 10px;
|
|
white-space: pre-wrap;
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
a, a:visited {
|
|
color: royalblue;
|
|
}
|
|
|
|
.flex {
|
|
display: flex;
|
|
flex-direction: row;
|
|
}
|
|
|
|
.flex > * {
|
|
flex: 1;
|
|
}
|
|
|
|
.flex > :last-child {
|
|
margin-left: 20px;
|
|
}
|
|
|
|
@media (max-width: 767px) {
|
|
html {
|
|
padding-top: 50px;
|
|
}
|
|
.flex {
|
|
flex-direction: column;
|
|
}
|
|
.flex > :last-child {
|
|
margin-left: 0;
|
|
}
|
|
h1 {
|
|
padding: 0 50px;
|
|
}
|
|
}
|
|
|
|
/* iPhone 6/7/8/X */
|
|
@media screen and (max-width: 375px) {
|
|
h1 {
|
|
font-size: 1.125rem;
|
|
}
|
|
}
|
|
|
|
/* iPhone 6/7/8/X */
|
|
@media screen and (max-width: 375px) {
|
|
emoji-picker.has-custom {
|
|
--num-columns: 6;
|
|
--emoji-padding: 0.25rem;
|
|
--input-padding: 0.125rem;
|
|
}
|
|
}
|
|
|
|
/* Nexus 5 */
|
|
@media screen and (max-width: 360px) {
|
|
emoji-picker {
|
|
--num-columns: 6;
|
|
--emoji-padding: 0.25rem;
|
|
--input-padding: 0.125rem;
|
|
}
|
|
}
|
|
|
|
/* JioPhone 2 */
|
|
@media screen and (max-width: 240px) {
|
|
emoji-picker {
|
|
height: 80vh;
|
|
}
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
html {
|
|
background: #222;
|
|
color: #efefef;
|
|
}
|
|
a, a:visited {
|
|
color: #7294f5;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<section aria-label="GitHub link">
|
|
<a class="github-banner" href="https://github.com/nolanlawson/emoji-picker-element">
|
|
<img width="149" height="149"
|
|
src="https://github.blog/wp-content/uploads/2008/12/forkme_right_gray_6d6d6d.png?resize=149%2C149"
|
|
class="attachment-full size-full" alt="Fork me on GitHub"
|
|
data-recalc-dims="1">
|
|
</a>
|
|
</section>
|
|
<main>
|
|
<h1>emoji-picker-element</h1>
|
|
<p>
|
|
<code>emoji-picker-element</code> is a lightweight emoji picker for the modern web.
|
|
</p>
|
|
<div class="flex">
|
|
<div>
|
|
<emoji-picker></emoji-picker>
|
|
</div>
|
|
<div>
|
|
<div style="padding: 20px;">
|
|
<div style="margin-bottom:10px;">Dark mode:</div>
|
|
<label>
|
|
<input type="radio" value="auto" name="darkmode" checked>
|
|
Auto
|
|
</label>
|
|
<label>
|
|
<input type="radio" value="dark" name="darkmode">
|
|
Dark
|
|
</label>
|
|
<label>
|
|
<input type="radio" value="light" name="darkmode">
|
|
Light
|
|
</label>
|
|
</div>
|
|
<div style="padding: 20px">
|
|
<label>
|
|
<input type="checkbox" id="custom"> Custom emoji
|
|
</label>
|
|
</div>
|
|
<div style="padding: 20px;" role="alert" aria-live="polite">
|
|
<pre style="display:none;"></pre>
|
|
</div>
|
|
<div class="private-browsing-warning"
|
|
style="padding: 20px; display: none; border: 2px dashed crimson;"
|
|
role="alert">
|
|
Note that <code>emoji-picker-element</code> does not support environments without IndexedDB. For polyfills
|
|
and workarounds, see
|
|
<a href="https://github.com/nolanlawson/emoji-picker-element/blob/master/README.md#environments-without-indexeddb">
|
|
the README
|
|
</a>.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
<script type="module">
|
|
import 'https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/picker.js'
|
|
import 'https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/database.js' // avoid extra round-trip
|
|
|
|
const $ = document.querySelector.bind(document)
|
|
const $$ = _ => Array.from(document.querySelectorAll(_))
|
|
let customEmoji
|
|
|
|
// See https://stackoverflow.com/a/8533927
|
|
function supportsSelector (selector) {
|
|
const style = document.createElement('style')
|
|
document.head.appendChild(style)
|
|
try {
|
|
style.sheet.insertRule(selector + '{}', 0)
|
|
} catch (e) {
|
|
return false
|
|
} finally {
|
|
document.head.removeChild(style)
|
|
}
|
|
return true
|
|
}
|
|
|
|
// check for private browsing mode to show the warning
|
|
async function hasIDB() {
|
|
if (typeof indexedDB === 'undefined') {
|
|
return false
|
|
}
|
|
|
|
try {
|
|
const idbFailed = await new Promise(resolve => {
|
|
const db = indexedDB.open('test-idb')
|
|
db.onerror = () => resolve(true)
|
|
db.onsuccess = () => {
|
|
indexedDB.deleteDatabase('test-idb')
|
|
resolve(false)
|
|
}
|
|
})
|
|
if (idbFailed) {
|
|
return false
|
|
}
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
async function loadCustomEmoji () {
|
|
if (customEmoji) {
|
|
return
|
|
}
|
|
const categoriesToCustomEmoji = (await (await fetch('./custom.json')).json())
|
|
customEmoji = []
|
|
for (const [ category, names ] of Object.entries(categoriesToCustomEmoji)) {
|
|
for (const name of names) {
|
|
customEmoji.push({
|
|
category: category || undefined,
|
|
name,
|
|
shortcodes: [name],
|
|
url: `./custom/${name}.svg`
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
const focusVisiblePolyfillPromise = !supportsSelector(':focus-visible') &&
|
|
import('https://cdn.jsdelivr.net/npm/focus-visible@^5/dist/focus-visible.js')
|
|
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
const picker = $('emoji-picker')
|
|
const pre = $('pre')
|
|
const onEvent = e => {
|
|
console.log(e)
|
|
pre.style.display = 'block'
|
|
pre.innerHTML = `Event: ${JSON.stringify(e.type)}\n\nData:\n\n${JSON.stringify(e.detail, null, 2)}`
|
|
}
|
|
picker.addEventListener('emoji-click', onEvent)
|
|
picker.addEventListener('skin-tone-change', onEvent)
|
|
|
|
$$('input[name=darkmode]').forEach(input => {
|
|
input.addEventListener('change', e => {
|
|
picker.classList.toggle('dark', e.target.value ==='dark')
|
|
picker.classList.toggle('light', e.target.value ==='light')
|
|
})
|
|
})
|
|
$('#custom').addEventListener('change', async e => {
|
|
if (e.target.checked) {
|
|
await loadCustomEmoji()
|
|
picker.customEmoji = customEmoji
|
|
} else {
|
|
picker.customEmoji = []
|
|
}
|
|
picker.classList.toggle('has-custom', !!e.target.checked)
|
|
})
|
|
|
|
if (!(await hasIDB())) {
|
|
$('.private-browsing-warning').style.display = 'block';
|
|
}
|
|
|
|
if (focusVisiblePolyfillPromise) {
|
|
await focusVisiblePolyfillPromise
|
|
applyFocusVisiblePolyfill(picker.shadowRoot)
|
|
}
|
|
})
|
|
</script>
|
|
</body>
|
|
</html>
|