Support app.relay.emoji_confetti on jumbo emoji clicks with canvas physics, per-emoji particle profiles, and a fixed overlay that does not affect scroll. Also add a Vite playground for live component previews and theme hover preview.
55 lines
1.5 KiB
TypeScript
55 lines
1.5 KiB
TypeScript
import { KeyboardEvent, MouseEvent } from 'react';
|
|
import { extractJumboEmojis } from './sendEmojiConfetti';
|
|
import { BurstPoint, getBurstPointFromElement } from './burstOrigin';
|
|
|
|
export function resolveClickedEmoji(
|
|
event: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>,
|
|
fallbackBody: string
|
|
): { emoji: string; origin: BurstPoint } | null {
|
|
const currentTarget = event.currentTarget as HTMLElement;
|
|
const target = event.target as HTMLElement;
|
|
const emoticonEl = target.closest('[data-emoticon]') ?? currentTarget.querySelector('[data-emoticon]');
|
|
if (emoticonEl instanceof HTMLElement) {
|
|
const emoji = emoticonEl.getAttribute('data-emoticon');
|
|
if (emoji) {
|
|
return {
|
|
emoji,
|
|
origin: getBurstPointFromElement(emoticonEl),
|
|
};
|
|
}
|
|
}
|
|
|
|
if (!currentTarget.closest('[data-jumbo-emoji]') && !currentTarget.hasAttribute('data-jumbo-emoji')) {
|
|
return null;
|
|
}
|
|
|
|
const emojis = extractJumboEmojis(fallbackBody);
|
|
if (emojis.length === 0) return null;
|
|
|
|
if ('clientX' in event) {
|
|
return {
|
|
emoji: emojis[0],
|
|
origin: {
|
|
x: event.clientX,
|
|
y: event.clientY,
|
|
},
|
|
};
|
|
}
|
|
|
|
const firstEmoticon = currentTarget.querySelector('[data-emoticon]');
|
|
if (firstEmoticon instanceof HTMLElement) {
|
|
const emoji = firstEmoticon.getAttribute('data-emoticon');
|
|
if (emoji) {
|
|
return {
|
|
emoji,
|
|
origin: getBurstPointFromElement(firstEmoticon),
|
|
};
|
|
}
|
|
}
|
|
|
|
return {
|
|
emoji: emojis[0],
|
|
origin: getBurstPointFromElement(currentTarget),
|
|
};
|
|
}
|