Switching to a proper emojii picker

This commit is contained in:
Dan 2026-02-21 19:57:47 +00:00
parent 3757454f3a
commit 15d98fe19a
4 changed files with 46 additions and 81 deletions

View file

@ -0,0 +1 @@
Emojii picker element - https://github.com/nolanlawson/emoji-picker-element

View file

@ -57,21 +57,11 @@
role="button" role="button"
tabindex="0" tabindex="0"
aria-label="Current emoji — click to change" aria-label="Current emoji — click to change"
aria-expanded="false"
> >
<span id="emoji-show">💬</span> <span id="emoji-show">💬</span>
</div> </div>
<input <emoji-picker id="emoji-picker"></emoji-picker>
type="text"
id="emoji-input"
class="emoji-picker-input"
placeholder="Type or paste an emoji…"
autocomplete="off"
autocorrect="off"
autocapitalize="none"
spellcheck="false"
aria-label="Emoji input"
/>
<span class="emoji-hint" id="emoji-hint">Click to change emoji</span>
</div> </div>
<!-- Status text --> <!-- Status text -->
@ -269,6 +259,7 @@
</div> </div>
</div> </div>
<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"></script>
<script src="script.js"></script> <script src="script.js"></script>
</body> </body>
</html> </html>

View file

@ -6,9 +6,7 @@ let settings = { username: "", apiKey: "", theme: "auto", mastodonPost: false };
const $ = (id) => document.getElementById(id); const $ = (id) => document.getElementById(id);
const emojiDisplay = $("emoji-display"); const emojiDisplay = $("emoji-display");
const emojiShow = $("emoji-show"); const emojiShow = $("emoji-show");
const emojiInput = $("emoji-input");
const emojiSection = emojiDisplay.closest(".emoji-section"); const emojiSection = emojiDisplay.closest(".emoji-section");
const emojiHint = $("emoji-hint");
const statusText = $("status-text"); const statusText = $("status-text");
const statusUrl = $("status-url"); const statusUrl = $("status-url");
const charCount = $("char-count"); const charCount = $("char-count");
@ -31,6 +29,11 @@ function applyTheme(theme) {
} else { } else {
document.documentElement.dataset.theme = theme; document.documentElement.dataset.theme = theme;
} }
const picker = document.getElementById("emoji-picker");
if (picker) {
picker.classList.remove("light", "dark");
if (theme !== "auto") picker.classList.add(theme);
}
} }
function updateThemeToggle(theme) { function updateThemeToggle(theme) {
@ -135,65 +138,43 @@ eyeBtn.addEventListener("click", () => {
}); });
// ── Emoji picker ───────────────────────────────────────────────────────────── // ── Emoji picker ─────────────────────────────────────────────────────────────
function extractFirstEmoji(str) { const emojiPicker = $("emoji-picker");
if (!str) return null;
if (typeof Intl?.Segmenter === "function") {
const seg = new Intl.Segmenter(undefined, { granularity: "grapheme" });
for (const { segment } of seg.segment(str)) {
if (/\p{Emoji}/u.test(segment) && segment.trim() !== "") return segment;
}
return null;
}
const m = str.match(/\p{Emoji_Presentation}[\p{Emoji}\u{FE0F}\u{20E3}]*/u);
return m ? m[0] : null;
}
// Detect platform for hint text
const isMac =
/Mac/.test(navigator.userAgent) && !/iPhone|iPad/.test(navigator.userAgent);
const osHint = isMac
? " · or ⌃⌘Space"
: navigator.userAgent.includes("Win")
? " · or Win+."
: "";
function openEmojiPicker() { function openEmojiPicker() {
emojiSection.classList.add("picking"); emojiSection.classList.add("picking");
emojiInput.value = ""; emojiDisplay.setAttribute("aria-expanded", "true");
emojiInput.focus();
emojiHint.textContent = `Type, paste, or use your emoji keyboard${osHint}`;
} }
function closeEmojiPicker() { function closeEmojiPicker() {
emojiSection.classList.remove("picking"); emojiSection.classList.remove("picking");
emojiHint.textContent = "Click to change emoji"; emojiDisplay.setAttribute("aria-expanded", "false");
} }
emojiDisplay.addEventListener("click", openEmojiPicker); emojiDisplay.addEventListener("click", () => {
if (emojiSection.classList.contains("picking")) closeEmojiPicker();
else openEmojiPicker();
});
emojiDisplay.addEventListener("keydown", (e) => { emojiDisplay.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") { if (e.key === "Enter" || e.key === " ") {
e.preventDefault(); e.preventDefault();
openEmojiPicker(); if (emojiSection.classList.contains("picking")) closeEmojiPicker();
else openEmojiPicker();
} }
}); });
emojiInput.addEventListener("input", () => { emojiPicker.addEventListener("emoji-click", (e) => {
const found = extractFirstEmoji(emojiInput.value); emoji = e.detail.unicode;
if (found) { emojiShow.textContent = emoji;
emoji = found; closeEmojiPicker();
emojiShow.textContent = found; });
document.addEventListener("click", (e) => {
if (emojiSection.classList.contains("picking") && !emojiSection.contains(e.target)) {
closeEmojiPicker(); closeEmojiPicker();
} }
}); });
emojiInput.addEventListener("blur", () => {
// Small delay so a click on the emoji-display doesn't flicker
setTimeout(() => {
if (document.activeElement !== emojiInput) closeEmojiPicker();
}, 150);
});
// ── Keyboard shortcuts ──────────────────────────────────────────────────────── // ── Keyboard shortcuts ────────────────────────────────────────────────────────
document.addEventListener("keydown", (e) => { document.addEventListener("keydown", (e) => {
if (e.key === "Escape") { if (e.key === "Escape") {

View file

@ -199,6 +199,7 @@ main {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
position: relative;
} }
.emoji-display { .emoji-display {
@ -229,39 +230,30 @@ main {
background: var(--accent-dim); background: var(--accent-dim);
} }
/* Visible emoji text input — slides in when picking */ emoji-picker {
.emoji-picker-input {
width: 200px;
text-align: center;
font-size: 1.5rem;
/* collapse when hidden */
max-height: 0;
padding-top: 0;
padding-bottom: 0;
overflow: hidden;
opacity: 0; opacity: 0;
visibility: hidden;
pointer-events: none; pointer-events: none;
border-color: transparent; position: absolute;
transition: top: calc(100% + 8px);
max-height 0.2s ease, left: 50%;
padding 0.18s ease, transform: translateX(-50%);
opacity 0.15s ease, z-index: 20;
border-color 0.15s ease; width: min(340px, calc(100vw - 40px));
border-radius: var(--r);
--background: var(--surface);
--border-color: var(--border);
--indicator-color: var(--accent);
--input-border-color: var(--border);
--input-font-color: var(--text);
--input-placeholder-color: var(--muted);
--outline-color: var(--accent);
} }
.emoji-section.picking .emoji-picker-input { .emoji-section.picking emoji-picker {
max-height: 60px;
padding-top: 9px;
padding-bottom: 9px;
opacity: 1; opacity: 1;
pointer-events: all; visibility: visible;
border-color: var(--accent); pointer-events: auto;
}
.emoji-hint {
font-size: 0.8rem;
color: var(--muted);
transition: opacity 0.15s;
} }
/* ── Form fields ───────────────────────────────────────────────────────── */ /* ── Form fields ───────────────────────────────────────────────────────── */