Skip to content

Commit

Permalink
feat(ui): outside click (#648)
Browse files Browse the repository at this point in the history
Co-authored-by: Darius Clark <dariusc93@users.noreply.github.com>
  • Loading branch information
molimauro and dariusc93 authored Dec 27, 2022
1 parent 8370db8 commit 4c7867f
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 54 deletions.
17 changes: 6 additions & 11 deletions extensions/native/audio-factory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use sir::css;

use ui_kit::{
button::{self, Button},
outside::OutsideClick,
select::*,
switch::Switch,
};
Expand Down Expand Up @@ -189,6 +190,7 @@ pub fn ExtAudioFactory(cx: Scope<Props>) -> Element {

cx.render(rsx! {
div {
id: "ext-audio-factory",
class: "{styles}",
div {
class: "row",
Expand Down Expand Up @@ -405,25 +407,18 @@ impl BasicExtension for AudioFactory {
);

// TODO: Icon should be a record icon, it should turn red and become a ovular shape like a normal button which includes the duration of the recording and turns the icon red
let factory_visible = use_state(&cx, || false);

cx.render(rsx! {
div {
id: "audio-factory",
class: "{styles}",
(factory_visible).then(|| rsx! {
OutsideClick {
ExtAudioFactory {
debug: false
}
}),
Button {
icon: Shape::ViewfinderCircle,
state: if **factory_visible {
button::State::Primary
} else {
button::State::Secondary
Button {
icon: Shape::ViewfinderCircle,
on_pressed: move |_| {}
}
on_pressed: move |_| factory_visible.set(!factory_visible)
}
}
})
Expand Down
85 changes: 42 additions & 43 deletions extensions/native/emoji-selector/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use dioxus::prelude::*;
use dioxus_heroicons::outline::Shape;
use emojis::{Group, UnicodeVersion};
use sir::css;
use ui_kit::button::{self, Button};
use ui_kit::{
button::{self, Button},
outside::OutsideClick,
};
use utils::extensions::{BasicExtension, ExtensionInfo, ExtensionType};

static MAX_UNICODE_VER: UnicodeVersion = UnicodeVersion::new(11, 0);
Expand Down Expand Up @@ -72,7 +75,6 @@ impl BasicExtension for EmojiSelector {
"
);

let is_opened = use_state(&cx, || false);
let eval = use_eval(&cx);
let insert = move |val: &str| {
eval(format!(
Expand All @@ -86,50 +88,47 @@ impl BasicExtension for EmojiSelector {

let groups = Group::iter();
cx.render(rsx! {
div {
class: "ext-emoji-selector",
(is_opened).then(|| rsx! {
div {
onblur: |_| println!("blur"),
class: "{styles}",
groups.map(|group| {
let name = get_group_name(group);
rsx!(
div {
class: "category",
div {
class: "ext-emoji-selector",
rsx! {
OutsideClick {
div {
onblur: |_| println!("blur"),
class: "{styles}",
groups.map(|group| {
let name = get_group_name(group);
rsx!(
div {
class: "name",
label { "{name}" }
},
div {
class: "items",
group.emojis()
.filter(|v| v.unicode_version() <= MAX_UNICODE_VER)
.map(|v| {
let name = v.name();
let emoji = v.as_str();
rsx!(button {
onclick: move |_| insert(emoji),
class: "item",
title: "{name}",
"{v}"
class: "category",
div {
class: "name",
label { "{name}" }
},
div {
class: "items",
group.emojis()
.filter(|v| v.unicode_version() <= MAX_UNICODE_VER)
.map(|v| {
let name = v.name();
let emoji = v.as_str();
rsx!(button {
onclick: move |_| insert(emoji),
class: "item",
title: "{name}",
"{v}"
})
})
})
},
}
)
})
}
})
Button {
icon: Shape::FaceSmile,
state: if **is_opened {
button::State::Primary
} else {
button::State::Secondary
},
}
)
})
}
Button {
icon: Shape::FaceSmile,
on_pressed: move |_| {}
}
}
on_pressed: move |_| is_opened.set(!is_opened)
}
}
}
})
}
Expand Down
1 change: 1 addition & 0 deletions src/ui_kit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod input_add_friend;
pub mod loader;
pub mod new_folder;
pub mod numeric_indicator;
pub mod outside;
pub mod photo_picker;
pub mod pin;
pub mod popup;
Expand Down
28 changes: 28 additions & 0 deletions src/ui_kit/src/outside/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use dioxus::prelude::*;
use warp::crypto::rand::{self, distributions::Alphanumeric, Rng};

#[derive(Props)]
pub struct Props<'a> {
children: Element<'a>,
}

#[allow(non_snake_case)]
pub fn OutsideClick<'a>(cx: Scope<'a, Props<'a>>) -> Element<'a> {
let rand_s: &String = cx.use_hook(|_| {
rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(7)
.map(char::from)
.collect()
});

let script = include_str!("outside.js").replace("ID", rand_s);

cx.render(rsx! {
span {
id: "outside-container-{rand_s}",
&cx.props.children,
script { "{script}" },
}
})
}
53 changes: 53 additions & 0 deletions src/ui_kit/src/outside/outside.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
function hideOnClickOutside(element, trigger) {
const outsideClickListener = (event) => {
if (
!element.contains(event.target) &&
!trigger.contains(event.target) &&
isVisible(element)
) {
element.style.display = "none"
trigger.style.backgroundColor = "var(--theme-secondary)"
removeClickListener()
}
}

const removeClickListener = () => {
document.removeEventListener("click", outsideClickListener)
}

document.addEventListener("click", outsideClickListener)
}

function isVisible(elem) {
return (
!!elem &&
!!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length)
)
}

function initClick() {
const container = document.getElementById("outside-container-ID")
if (container.children.length < 2) {
throw new Error(
"Outside click needs a container (the hiding container) as first child and a trigger button as second child",
)
}

const el = container.firstChild
el.style.display = "none"

const trigger = container.children[1]
trigger.style.backgroundColor = "var(--theme-secondary)"

trigger.addEventListener("click", () => {
el.style.display = el.style.display === "none" ? "" : "none"
trigger.style.backgroundColor =
el.style.display === "none"
? "var(--theme-secondary)"
: "var(--theme-primary)"

hideOnClickOutside(el, trigger)
})
}

initClick()

0 comments on commit 4c7867f

Please sign in to comment.