mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-04 18:59:08 +01:00
put actual emojis
This commit is contained in:
parent
45b8470355
commit
2bc8295fdb
@ -4,6 +4,7 @@
|
||||
import clsx from "clsx";
|
||||
import React, { memo, useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { throttle } from "throttle-debounce";
|
||||
|
||||
import { useDimensionsWithExistingRef } from "@/app/hook/useDimensions";
|
||||
import "./contextmenu.less";
|
||||
@ -269,7 +270,7 @@ const ContextMenu = memo(
|
||||
parentRef: React.RefObject<HTMLDivElement>,
|
||||
label: string
|
||||
) => {
|
||||
setTimeout(() => {
|
||||
throttle(0, () => {
|
||||
const subContextMenuRef = subContextMenuRefs.current[key]?.current;
|
||||
if (!subContextMenuRef) return;
|
||||
|
||||
@ -296,7 +297,7 @@ const ContextMenu = memo(
|
||||
...prev,
|
||||
[key]: { top, left, label },
|
||||
}));
|
||||
}, 0);
|
||||
})();
|
||||
};
|
||||
|
||||
const handleMouseEnterItem = (
|
||||
|
43
frontend/app/element/emojipalette.less
Normal file
43
frontend/app/element/emojipalette.less
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
.emoji-palette-content {
|
||||
padding: 10px;
|
||||
max-height: 250px;
|
||||
width: 250px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
// > input {
|
||||
// margin-top: 10px;
|
||||
// }
|
||||
}
|
||||
|
||||
.emoji-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(30px, 1fr));
|
||||
gap: 5px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.emoji-button {
|
||||
font-size: 24px;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
transition: background-color 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.no-emojis {
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
}
|
37
frontend/app/element/emojipalette.stories.tsx
Normal file
37
frontend/app/element/emojipalette.stories.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { useRef } from "react";
|
||||
import { EmojiPalette } from "./emojipalette";
|
||||
|
||||
const meta: Meta<typeof EmojiPalette> = {
|
||||
title: "Elements/EmojiPalette",
|
||||
component: EmojiPalette,
|
||||
args: {
|
||||
className: "custom-emoji-palette-class",
|
||||
},
|
||||
argTypes: {
|
||||
scopeRef: {
|
||||
description: "Reference to the outer container element for positioning",
|
||||
},
|
||||
className: {
|
||||
description: "Custom class for emoji palette styling",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof EmojiPalette>;
|
||||
|
||||
export const DefaultEmojiPalette: Story = {
|
||||
render: (args) => {
|
||||
const scopeRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<div ref={scopeRef} style={{ padding: "20px", height: "300px", border: "2px solid black" }}>
|
||||
<EmojiPalette {...args} scopeRef={scopeRef} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
args: {
|
||||
className: "custom-emoji-palette-class",
|
||||
},
|
||||
};
|
@ -0,0 +1,280 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import clsx from "clsx";
|
||||
import React, { memo, useEffect, useRef, useState } from "react";
|
||||
import { Button } from "./button";
|
||||
import { Input } from "./input";
|
||||
import { Palette } from "./palette";
|
||||
|
||||
import "./emojiPalette.less";
|
||||
|
||||
interface EmojiPaletteProps {
|
||||
scopeRef: React.RefObject<HTMLElement>;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const emojiList = [
|
||||
// Smileys & Emotion
|
||||
{ emoji: "😀", name: "grinning face" },
|
||||
{ emoji: "😁", name: "beaming face with smiling eyes" },
|
||||
{ emoji: "😂", name: "face with tears of joy" },
|
||||
{ emoji: "🤣", name: "rolling on the floor laughing" },
|
||||
{ emoji: "😃", name: "grinning face with big eyes" },
|
||||
{ emoji: "😄", name: "grinning face with smiling eyes" },
|
||||
{ emoji: "😅", name: "grinning face with sweat" },
|
||||
{ emoji: "😆", name: "grinning squinting face" },
|
||||
{ emoji: "😉", name: "winking face" },
|
||||
{ emoji: "😊", name: "smiling face with smiling eyes" },
|
||||
{ emoji: "😋", name: "face savoring food" },
|
||||
{ emoji: "😎", name: "smiling face with sunglasses" },
|
||||
{ emoji: "😍", name: "smiling face with heart-eyes" },
|
||||
{ emoji: "😘", name: "face blowing a kiss" },
|
||||
{ emoji: "😗", name: "kissing face" },
|
||||
{ emoji: "😙", name: "kissing face with smiling eyes" },
|
||||
{ emoji: "😚", name: "kissing face with closed eyes" },
|
||||
{ emoji: "🙂", name: "slightly smiling face" },
|
||||
{ emoji: "🤗", name: "hugging face" },
|
||||
{ emoji: "🤔", name: "thinking face" },
|
||||
{ emoji: "😐", name: "neutral face" },
|
||||
{ emoji: "😑", name: "expressionless face" },
|
||||
{ emoji: "😶", name: "face without mouth" },
|
||||
{ emoji: "🙄", name: "face with rolling eyes" },
|
||||
{ emoji: "😏", name: "smirking face" },
|
||||
{ emoji: "😣", name: "persevering face" },
|
||||
{ emoji: "😥", name: "sad but relieved face" },
|
||||
{ emoji: "😮", name: "face with open mouth" },
|
||||
{ emoji: "🤐", name: "zipper-mouth face" },
|
||||
{ emoji: "😯", name: "hushed face" },
|
||||
{ emoji: "😪", name: "sleepy face" },
|
||||
{ emoji: "😫", name: "tired face" },
|
||||
{ emoji: "🥱", name: "yawning face" },
|
||||
{ emoji: "😴", name: "sleeping face" },
|
||||
{ emoji: "😌", name: "relieved face" },
|
||||
{ emoji: "😛", name: "face with tongue" },
|
||||
{ emoji: "😜", name: "winking face with tongue" },
|
||||
{ emoji: "😝", name: "squinting face with tongue" },
|
||||
{ emoji: "🤤", name: "drooling face" },
|
||||
{ emoji: "😒", name: "unamused face" },
|
||||
{ emoji: "😓", name: "downcast face with sweat" },
|
||||
{ emoji: "😔", name: "pensive face" },
|
||||
{ emoji: "😕", name: "confused face" },
|
||||
{ emoji: "🙃", name: "upside-down face" },
|
||||
{ emoji: "🫠", name: "melting face" },
|
||||
{ emoji: "😲", name: "astonished face" },
|
||||
{ emoji: "☹️", name: "frowning face" },
|
||||
{ emoji: "🙁", name: "slightly frowning face" },
|
||||
{ emoji: "😖", name: "confounded face" },
|
||||
{ emoji: "😞", name: "disappointed face" },
|
||||
{ emoji: "😟", name: "worried face" },
|
||||
{ emoji: "😤", name: "face with steam from nose" },
|
||||
{ emoji: "😢", name: "crying face" },
|
||||
{ emoji: "😭", name: "loudly crying face" },
|
||||
{ emoji: "😦", name: "frowning face with open mouth" },
|
||||
{ emoji: "😧", name: "anguished face" },
|
||||
{ emoji: "😨", name: "fearful face" },
|
||||
{ emoji: "😩", name: "weary face" },
|
||||
{ emoji: "🤯", name: "exploding head" },
|
||||
{ emoji: "😬", name: "grimacing face" },
|
||||
{ emoji: "😰", name: "anxious face with sweat" },
|
||||
{ emoji: "😱", name: "face screaming in fear" },
|
||||
{ emoji: "🥵", name: "hot face" },
|
||||
{ emoji: "🥶", name: "cold face" },
|
||||
{ emoji: "😳", name: "flushed face" },
|
||||
{ emoji: "🤪", name: "zany face" },
|
||||
{ emoji: "😵", name: "dizzy face" },
|
||||
{ emoji: "🥴", name: "woozy face" },
|
||||
{ emoji: "😠", name: "angry face" },
|
||||
{ emoji: "😡", name: "pouting face" },
|
||||
{ emoji: "🤬", name: "face with symbols on mouth" },
|
||||
{ emoji: "🤮", name: "face vomiting" },
|
||||
{ emoji: "🤢", name: "nauseated face" },
|
||||
{ emoji: "😷", name: "face with medical mask" },
|
||||
|
||||
// Gestures & Hand Signs
|
||||
{ emoji: "👋", name: "waving hand" },
|
||||
{ emoji: "🤚", name: "raised back of hand" },
|
||||
{ emoji: "🖐️", name: "hand with fingers splayed" },
|
||||
{ emoji: "✋", name: "raised hand" },
|
||||
{ emoji: "👌", name: "OK hand" },
|
||||
{ emoji: "✌️", name: "victory hand" },
|
||||
{ emoji: "🤞", name: "crossed fingers" },
|
||||
{ emoji: "🤟", name: "love-you gesture" },
|
||||
{ emoji: "🤘", name: "sign of the horns" },
|
||||
{ emoji: "🤙", name: "call me hand" },
|
||||
{ emoji: "👈", name: "backhand index pointing left" },
|
||||
{ emoji: "👉", name: "backhand index pointing right" },
|
||||
{ emoji: "👆", name: "backhand index pointing up" },
|
||||
{ emoji: "👇", name: "backhand index pointing down" },
|
||||
{ emoji: "👍", name: "thumbs up" },
|
||||
{ emoji: "👎", name: "thumbs down" },
|
||||
{ emoji: "👏", name: "clapping hands" },
|
||||
{ emoji: "🙌", name: "raising hands" },
|
||||
{ emoji: "👐", name: "open hands" },
|
||||
{ emoji: "🙏", name: "folded hands" },
|
||||
|
||||
// Animals & Nature
|
||||
{ emoji: "🐶", name: "dog face" },
|
||||
{ emoji: "🐱", name: "cat face" },
|
||||
{ emoji: "🐭", name: "mouse face" },
|
||||
{ emoji: "🐹", name: "hamster face" },
|
||||
{ emoji: "🐰", name: "rabbit face" },
|
||||
{ emoji: "🦊", name: "fox face" },
|
||||
{ emoji: "🐻", name: "bear face" },
|
||||
{ emoji: "🐼", name: "panda face" },
|
||||
{ emoji: "🐨", name: "koala" },
|
||||
{ emoji: "🐯", name: "tiger face" },
|
||||
{ emoji: "🦁", name: "lion" },
|
||||
{ emoji: "🐮", name: "cow face" },
|
||||
{ emoji: "🐷", name: "pig face" },
|
||||
{ emoji: "🐸", name: "frog face" },
|
||||
{ emoji: "🐵", name: "monkey face" },
|
||||
{ emoji: "🦄", name: "unicorn face" },
|
||||
{ emoji: "🐢", name: "turtle" },
|
||||
{ emoji: "🐍", name: "snake" },
|
||||
{ emoji: "🦋", name: "butterfly" },
|
||||
{ emoji: "🐝", name: "honeybee" },
|
||||
{ emoji: "🐞", name: "lady beetle" },
|
||||
{ emoji: "🦀", name: "crab" },
|
||||
{ emoji: "🐠", name: "tropical fish" },
|
||||
{ emoji: "🐟", name: "fish" },
|
||||
{ emoji: "🐬", name: "dolphin" },
|
||||
{ emoji: "🐳", name: "spouting whale" },
|
||||
{ emoji: "🐋", name: "whale" },
|
||||
{ emoji: "🦈", name: "shark" },
|
||||
|
||||
// Food & Drink
|
||||
{ emoji: "🍏", name: "green apple" },
|
||||
{ emoji: "🍎", name: "red apple" },
|
||||
{ emoji: "🍐", name: "pear" },
|
||||
{ emoji: "🍊", name: "tangerine" },
|
||||
{ emoji: "🍋", name: "lemon" },
|
||||
{ emoji: "🍌", name: "banana" },
|
||||
{ emoji: "🍉", name: "watermelon" },
|
||||
{ emoji: "🍇", name: "grapes" },
|
||||
{ emoji: "🍓", name: "strawberry" },
|
||||
{ emoji: "🫐", name: "blueberries" },
|
||||
{ emoji: "🍈", name: "melon" },
|
||||
{ emoji: "🍒", name: "cherries" },
|
||||
{ emoji: "🍑", name: "peach" },
|
||||
{ emoji: "🥭", name: "mango" },
|
||||
{ emoji: "🍍", name: "pineapple" },
|
||||
{ emoji: "🥥", name: "coconut" },
|
||||
{ emoji: "🥑", name: "avocado" },
|
||||
{ emoji: "🥦", name: "broccoli" },
|
||||
{ emoji: "🥕", name: "carrot" },
|
||||
{ emoji: "🌽", name: "corn" },
|
||||
{ emoji: "🌶️", name: "hot pepper" },
|
||||
{ emoji: "🍔", name: "hamburger" },
|
||||
{ emoji: "🍟", name: "french fries" },
|
||||
{ emoji: "🍕", name: "pizza" },
|
||||
{ emoji: "🌭", name: "hot dog" },
|
||||
{ emoji: "🥪", name: "sandwich" },
|
||||
{ emoji: "🍿", name: "popcorn" },
|
||||
{ emoji: "🥓", name: "bacon" },
|
||||
{ emoji: "🥚", name: "egg" },
|
||||
{ emoji: "🍰", name: "cake" },
|
||||
{ emoji: "🎂", name: "birthday cake" },
|
||||
{ emoji: "🍦", name: "ice cream" },
|
||||
{ emoji: "🍩", name: "doughnut" },
|
||||
{ emoji: "🍪", name: "cookie" },
|
||||
{ emoji: "🍫", name: "chocolate bar" },
|
||||
{ emoji: "🍬", name: "candy" },
|
||||
{ emoji: "🍭", name: "lollipop" },
|
||||
|
||||
// Activities
|
||||
{ emoji: "⚽", name: "soccer ball" },
|
||||
{ emoji: "🏀", name: "basketball" },
|
||||
{ emoji: "🏈", name: "american football" },
|
||||
{ emoji: "⚾", name: "baseball" },
|
||||
{ emoji: "🥎", name: "softball" },
|
||||
{ emoji: "🎾", name: "tennis" },
|
||||
{ emoji: "🏐", name: "volleyball" },
|
||||
{ emoji: "🎳", name: "bowling" },
|
||||
{ emoji: "⛳", name: "flag in hole" },
|
||||
{ emoji: "🚴", name: "person biking" },
|
||||
{ emoji: "🎮", name: "video game" },
|
||||
{ emoji: "🎲", name: "game die" },
|
||||
{ emoji: "🎸", name: "guitar" },
|
||||
{ emoji: "🎺", name: "trumpet" },
|
||||
|
||||
// Miscellaneous
|
||||
{ emoji: "🚀", name: "rocket" },
|
||||
{ emoji: "💖", name: "sparkling heart" },
|
||||
{ emoji: "🎉", name: "party popper" },
|
||||
{ emoji: "🔥", name: "fire" },
|
||||
{ emoji: "🎁", name: "gift" },
|
||||
{ emoji: "❤️", name: "red heart" },
|
||||
{ emoji: "🧡", name: "orange heart" },
|
||||
{ emoji: "💛", name: "yellow heart" },
|
||||
{ emoji: "💚", name: "green heart" },
|
||||
{ emoji: "💙", name: "blue heart" },
|
||||
{ emoji: "💜", name: "purple heart" },
|
||||
{ emoji: "🤍", name: "white heart" },
|
||||
{ emoji: "🤎", name: "brown heart" },
|
||||
{ emoji: "💔", name: "broken heart" },
|
||||
];
|
||||
|
||||
const EmojiPalette = memo(({ scopeRef, className }: EmojiPaletteProps) => {
|
||||
const anchorRef = useRef<HTMLButtonElement>(null);
|
||||
const [isPaletteVisible, setIsPaletteVisible] = useState(false);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (anchorRef.current && !anchorRef.current.contains(event.target as Node)) {
|
||||
setIsPaletteVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
scopeRef?.current?.addEventListener("mousedown", handleClickOutside);
|
||||
return () => {
|
||||
scopeRef?.current?.removeEventListener("mousedown", handleClickOutside);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleAnchorClick = () => {
|
||||
setIsPaletteVisible((prev) => !prev);
|
||||
};
|
||||
|
||||
const handleSearchChange = (val: string) => {
|
||||
setSearchTerm(val.toLowerCase());
|
||||
};
|
||||
|
||||
const filteredEmojis = emojiList.filter((item) => item.name.includes(searchTerm));
|
||||
|
||||
return (
|
||||
<div className={clsx("emoji-palette", className)}>
|
||||
<Button ref={anchorRef} className="ghost grey" onClick={handleAnchorClick}>
|
||||
<i className="fa-sharp fa-solid fa-face-smile"></i>
|
||||
</Button>
|
||||
{isPaletteVisible && (
|
||||
<Palette anchorRef={anchorRef} scopeRef={scopeRef} className="emoji-palette-content">
|
||||
<Input placeholder="Search emojis..." value={searchTerm} onChange={handleSearchChange} />
|
||||
<div className="emoji-grid">
|
||||
{filteredEmojis.length > 0 ? (
|
||||
filteredEmojis.map((item, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
className="ghost emoji-button"
|
||||
onClick={() => {
|
||||
console.log(`Emoji selected: ${item.emoji}`);
|
||||
setIsPaletteVisible(false);
|
||||
}}
|
||||
>
|
||||
{item.emoji}
|
||||
</Button>
|
||||
))
|
||||
) : (
|
||||
<div className="no-emojis">No emojis found</div>
|
||||
)}
|
||||
</div>
|
||||
</Palette>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
EmojiPalette.displayName = "EmojiPalette";
|
||||
|
||||
export { EmojiPalette };
|
@ -13,4 +13,5 @@
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
background: #212121;
|
||||
box-shadow: 0px 8px 24px 0px rgba(0, 0, 0, 0.3);
|
||||
visibility: hidden;
|
||||
}
|
||||
|
@ -33,13 +33,13 @@ export const DefaultPalette: Story = {
|
||||
setIsMenuVisible((prev) => !prev);
|
||||
};
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (anchorRef.current && !anchorRef.current.contains(event.target as Node)) {
|
||||
setIsMenuVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (anchorRef.current && !anchorRef.current.contains(event.target as Node)) {
|
||||
setIsMenuVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
scopeRef?.current?.addEventListener("mousedown", handleClickOutside);
|
||||
return () => {
|
||||
scopeRef?.current?.removeEventListener("mousedown", handleClickOutside);
|
||||
|
@ -32,7 +32,7 @@ const Palette = memo(({ children, className, anchorRef, scopeRef }: PaletteProps
|
||||
// Check if the palette goes beyond the right edge of the window
|
||||
const rightEdge = left + paletteEl.offsetWidth;
|
||||
if (rightEdge > window.innerWidth) {
|
||||
left = window.innerWidth - paletteEl.offsetWidth - 15;
|
||||
left = window.innerWidth - paletteEl.offsetWidth - 10;
|
||||
}
|
||||
|
||||
// Check if the palette goes beyond the bottom edge of the window
|
||||
@ -44,12 +44,14 @@ const Palette = memo(({ children, className, anchorRef, scopeRef }: PaletteProps
|
||||
}
|
||||
}, [anchorRef, scopeRef, width, height]);
|
||||
|
||||
useEffect(() => {
|
||||
if (position.top > 0 && paletteRef.current?.style.visibility !== "visible") {
|
||||
paletteRef.current.style.visibility = "visible";
|
||||
}
|
||||
}, [position.top]);
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
ref={paletteRef}
|
||||
style={{ top: `${position.top}px`, left: `${position.left}px` }}
|
||||
className={clsx("palette", className)}
|
||||
>
|
||||
<div ref={paletteRef} style={{ top: position.top, left: position.left }} className={clsx("palette", className)}>
|
||||
{children}
|
||||
</div>,
|
||||
document.body
|
||||
|
Loading…
Reference in New Issue
Block a user