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.
75 lines
2.0 KiB
TypeScript
75 lines
2.0 KiB
TypeScript
export type BurstPoint = {
|
|
x: number;
|
|
y: number;
|
|
maskRadius?: number;
|
|
};
|
|
|
|
export function getBurstPointFromElement(element: Element): BurstPoint {
|
|
const rect = element.getBoundingClientRect();
|
|
return {
|
|
x: rect.left + rect.width / 2,
|
|
y: rect.top + rect.height / 2,
|
|
maskRadius: Math.max(rect.width, rect.height) * 0.5,
|
|
};
|
|
}
|
|
|
|
export function getElementCenter(element: Element): BurstPoint {
|
|
return getBurstPointFromElement(element);
|
|
}
|
|
|
|
export function findEmojiOriginInMessage(
|
|
targetEventId: string,
|
|
emoji: string
|
|
): BurstPoint | undefined {
|
|
const message = document.querySelector(`[data-message-id="${CSS.escape(targetEventId)}"]`);
|
|
if (!message) return undefined;
|
|
|
|
const matches = message.querySelectorAll('[data-emoticon]');
|
|
for (const element of matches) {
|
|
if (element.getAttribute('data-emoticon') === emoji) {
|
|
return getBurstPointFromElement(element);
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
export function getMessageFallbackOrigin(targetEventId: string): BurstPoint | undefined {
|
|
const message = document.querySelector(`[data-message-id="${CSS.escape(targetEventId)}"]`);
|
|
if (!message) return undefined;
|
|
|
|
const jumboBody = message.querySelector('[data-jumbo-emoji]');
|
|
if (jumboBody) {
|
|
return getBurstPointFromElement(jumboBody);
|
|
}
|
|
|
|
const body = message.querySelector('[data-message-body]');
|
|
if (body) {
|
|
return getBurstPointFromElement(body);
|
|
}
|
|
|
|
return getBurstPointFromElement(message);
|
|
}
|
|
|
|
export function resolveBurstOrigin(
|
|
targetEventId: string | undefined,
|
|
emojis: string[],
|
|
explicitOrigin?: BurstPoint
|
|
): BurstPoint {
|
|
if (explicitOrigin) return explicitOrigin;
|
|
|
|
const primaryEmoji = emojis[0];
|
|
if (targetEventId && primaryEmoji) {
|
|
const emojiOrigin = findEmojiOriginInMessage(targetEventId, primaryEmoji);
|
|
if (emojiOrigin) return emojiOrigin;
|
|
|
|
const messageOrigin = getMessageFallbackOrigin(targetEventId);
|
|
if (messageOrigin) return messageOrigin;
|
|
}
|
|
|
|
return {
|
|
x: window.innerWidth / 2,
|
|
y: window.innerHeight * 0.35,
|
|
};
|
|
}
|