dioxus_emoji_picker 0.2.0

Dioxus Emoji Picker
Documentation
use dioxus::prelude::*;
use emojis::{Group,SkinTone};
use lazy_static::lazy_static;

static SKIN_TONE: GlobalSignal<SkinTone> = Signal::global(|| SkinTone::Default);

mod emoji_indexer;

use emoji_indexer::*;

lazy_static! {
    static ref EMOJI_INDEXER: EmojiIndexer = EmojiIndexer::new();
}

#[component]
pub fn SearchIcon() -> Element {

	rsx! {
		svg {
			view_box:"0 0 24 24",
			fill:"none",
			xmlns:"http://www.w3.org/2000/svg",
			path {
				d:"M15.7955 15.8111L21 21M18 10.5C18 14.6421 14.6421 18 10.5 18C6.35786 18 3 14.6421 3 10.5C3 6.35786 6.35786 3 10.5 3C14.6421 3 18 6.35786 18 10.5Z",
				stroke:"#000000",
				stroke_width:"2",
				stroke_linecap:"round",
				stroke_linejoin:"round",
			}
		}
	}
}

fn group_as_str(group : Group) -> &'static str {

	match group {
		Group::SmileysAndEmotion => {
			"Smileys & Emotion"
		},
		Group::PeopleAndBody => {
			"People & Body"
		},
		Group::AnimalsAndNature => {
			"Animals & Nature"
		},
		Group::FoodAndDrink => {
			"Food & Drink"
		},
		Group::TravelAndPlaces => {
			"Travel & Places"
		},
		Group::Activities => {
			"Activities"
		},
		Group::Objects => {
			"Objects"
		},
		Group::Symbols => {
			"Symbols"
		},
		Group::Flags => {
			"Flags"
		},
	}
}

#[derive(Clone,PartialEq)]
enum PickerStatus {
	ByGroup(Group),
	Searching(String),
}

#[component]
fn EmojiSearch(
	picker_status : Signal<PickerStatus>,
) -> Element {

	let mut show_skin_tones = use_signal(|| false);
	let skin_tone_icon = use_memo(|| {
		let current_skin_tone = SKIN_TONE.read().clone();
		emojis::get("✌️").unwrap().with_skin_tone(current_skin_tone).unwrap().as_str()
	});

	rsx! {
		div {
			class: "emoji_search",
			section {
				class: "emoji_search_box",
				input {
					class: "emoji_search_box_input",
					oninput: move |evt| {
						let what = evt.value().clone();
						if what.is_empty() {
							picker_status
								.set(PickerStatus::ByGroup(Group::SmileysAndEmotion));
						} else {
							picker_status.set(PickerStatus::Searching(what));
						}
					},
					type: "text",
					placeholder: "Search...",
				}
				span {
					class: "emoji_search_icon",
					SearchIcon {}
				}
			}
			div {
				class: "emoji_skin_tone_dropdown",
				button {
					class : "emoji_button",
					onclick: move |_| {
						show_skin_tones.toggle();
					},
					"{skin_tone_icon}",
				}
				if show_skin_tones() {
					div {
						class: "emoji_skin_tone_list",
						{
							emojis::get(skin_tone_icon.read().clone()).unwrap()
								.skin_tones().unwrap()
								.map(|e| {
									let emoji_str = e.as_str();
									let shortcode = e.shortcode().unwrap_or_default();
									rsx! {
										button {
											onclick: move |_| {
												SKIN_TONE.with_mut(|s|
													*s = e.skin_tone().unwrap()
												);
												show_skin_tones.toggle();
											},
											class : "emoji_button",
											title : "{shortcode}",
											"{emoji_str}"
										}
									}
								})
						}
					}
				}
			}
		}
	}
}

#[component]
fn EmojiCategory(
	picker_status : Signal<PickerStatus>
) -> Element {

	match &*picker_status.read() {
		PickerStatus::ByGroup(group) => {
			let category = group_as_str(*group);
			rsx! {
				h2 {
					class : "emoji_category",
					"{category}"
				}
			}
		},
		_ => {
			rsx! {}
		}
	}
}

#[component]
fn EmojiGroups(
	picker_status : Signal<PickerStatus>
) -> Element {

    let groups = vec![
        (Group::SmileysAndEmotion, "😀","Smiley and Emotion"),
        (Group::PeopleAndBody, "👋","People and Body"),
        (Group::AnimalsAndNature, "🐶","Animals and Nature"),
        (Group::FoodAndDrink, "🍕","Foor and Drink"),
        (Group::TravelAndPlaces, "✈️","Travel and Places"),
        (Group::Activities, "","Activities"),
        (Group::Objects, "💡","Objects"),
        (Group::Symbols, "❤️","Symbols"),
        (Group::Flags, "🏁","Flags"),
    ];

	match &*picker_status.read() {
		PickerStatus::ByGroup(selected_group) => {
			rsx! {
				div {
					class: "emoji_groups",
					{
						groups.iter().map(|(group,group_emoji,group_name)| {
							let group = *group;
							let class_name = if group == *selected_group {
								"emoji_groups_button active"
							} else {
								"emoji_groups_button"
							};
							rsx! {
								button {
									onclick: move |_| {
										picker_status
											.set(PickerStatus::ByGroup(group));
									},
									key : "{group_emoji}",
									class : "{class_name}",
									title : "{group_name}",
									"{group_emoji}"
								}
							}
						})
					}
				}
			}
		},
		PickerStatus::Searching(_) => {
			rsx! {}
		}
	}
}		


#[component]
fn EmojiGrid(
	picker_status : Signal<PickerStatus>,
	content : Signal<String>,
) -> Element {

	match &*picker_status.read() {
		PickerStatus::ByGroup(group) => {
			rsx! {
				div {
					class: "emoji_grid",
					{
						group.emojis().map(|e| {
							let current_skin_tone = *SKIN_TONE.read();
							let e1 = e.with_skin_tone(current_skin_tone)
								.unwrap_or(e);

							let emoji_str = e1.as_str();
							let shortcode = e1.shortcode().unwrap_or_default();
							rsx! {
								button {
									onclick: move |_| {
										content.with_mut(|c|
											c.push_str(emoji_str)
										);
									},
									key : "{emoji_str}",
									class : "emoji_button",
									title : "{shortcode}",
									"{emoji_str}"
								}
							}
						})
					}
				}
			}
		},
		PickerStatus::Searching(what) => {
			let results = EMOJI_INDEXER.search(what, *SKIN_TONE.read());
			rsx! {
				div {
					class: "emoji_grid",
					{
						results.iter().map(|e| {
							let current_skin_tone = *SKIN_TONE.read();
							let e1 = e.with_skin_tone(current_skin_tone)
								.unwrap_or(e);

							let emoji_str = e1.as_str();
							let shortcode = e1.shortcode().unwrap_or_default();
							rsx! {
								button {
									onclick: move |_| {
										content.with_mut(|c|
											c.push_str(emoji_str)
										);
									},
									key : "{emoji_str}",
									class : "emoji_button",
									title : "{shortcode}",
									"{emoji_str}"
								}
							}
						})
					}
				}
			}
		}
	}
}


#[component]
pub fn EmojiPicker(
	content : Signal<String>
) -> Element {
	
	let picker_status = use_signal(||
		PickerStatus::ByGroup(Group::SmileysAndEmotion)
	);

	rsx! {
		div {
			class: "emoji_picker",
			EmojiSearch { picker_status : picker_status },
			EmojiGroups { picker_status : picker_status },
			EmojiCategory { picker_status : picker_status },
			EmojiGrid { picker_status : picker_status , content : content},
		}
	}
}