Last active
July 25, 2020 21:16
-
-
Save rebo/79618f540df17d6a90c27525fa1a8256 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(track_caller)] | |
// use rand::seq::SliceRandom; | |
use seed::prelude::*; | |
use seed::*; | |
use seed_hooks::*; | |
use seed_style::px; | |
use seed_style::*; | |
mod global_styles; | |
mod theme; | |
// mod fetch_example; | |
// mod multi_fetch_example; | |
use theme::*; | |
// | |
// Model, Msg, Update, init(), and start() | |
// --------------------------------------- | |
pub struct Model {} | |
// In aps that make use of conditional rendering on breakpoints we We just need one Msg | |
// in order to handle a WindowResized event. | |
#[derive(Clone,Debug)] | |
pub enum Msg { | |
PickedColorChanged(String), | |
PickedColorChanged2(String), | |
PickedColorChanged3(String), | |
PickedColorChanged4(String), | |
WindowResized, | |
} | |
fn update(msg: Msg, _model: &mut Model, _orders: &mut impl Orders<Msg>) { | |
log!(msg); | |
} | |
fn init(_url: Url, _orders: &mut impl Orders<Msg>) -> Model { | |
// allow app to access the user theme, plus seed style presets. | |
// app_themes().set(vec![user_theme(), default_color_theme()]); | |
load_app_themes(&[ user_theme, default_colors_theme]); | |
// themes values can now be used in the global style init | |
global_styles::global_init(); | |
Model {} | |
} | |
#[wasm_bindgen(start)] | |
pub fn start() { | |
let app = App::start("app", init, update, view); | |
my_app().set(Some(app)); | |
} | |
#[atom] | |
fn my_app() -> Atom<Option<App<Msg,Model,Node<Msg>>>>{ | |
None | |
} | |
#[atom] | |
fn hsl_widget_status(_local: Local) ->Atom<HslWidgetStatus> { | |
HslWidgetStatus::Unpicked | |
} | |
#[derive(Clone)] | |
pub enum HslWidgetStatus{ | |
Unpicked, | |
Open, | |
Picked((f64,f64,f64)) | |
} | |
#[topo::nested] | |
pub fn color_picker<Ms:'static>(ev_handler: EventHandler<Ms>) -> Node<Ms> { | |
picker_ui(Local::new(), Impure(ev_handler)).get() | |
} | |
#[reaction] | |
fn picker_ui<Ms: 'static>(local: Local, ev_handler: Impure<EventHandler<Ms>>) -> Reaction<Node<Ms>>{ | |
let status = hsl_widget_status(local).observe(); | |
match status { | |
HslWidgetStatus::Unpicked => picker_unpicked_ui(local), | |
HslWidgetStatus::Open => picker_open_ui(local,ev_handler).observe(), | |
HslWidgetStatus::Picked(hsl) => picker_picked_ui(local,hsl), | |
} | |
} | |
fn picker_unpicked_ui<Ms: 'static>(local: Local) -> Node<Ms> { | |
button![ | |
s().radius(px(4)).bg_color(hsluv(120,60,60)).color("white").p(px(16)), | |
s().hover().bg_color(hsluv(120,50,65)), | |
s().active().bg_color(hsluv(120,40,50)), | |
"Choose Color",hsl_widget_status(local).on_click(|s| *s = HslWidgetStatus::Open) | |
] | |
} | |
fn picker_picked_ui<Ms: 'static>(local:Local,hsl_value: (f64,f64,f64) ) -> Node<Ms> { | |
div![s().m(px(20)), | |
"picked color :", | |
div![ | |
s().w(px(30)).h(px(30)).bg_color(hsluv::hsluv_to_hex(hsl_value).as_str()).m(px(20)) | |
], | |
button![ | |
s().radius(px(4)).bg_color(hsluv(120,60,60)).color("white").p(px(16)), | |
s().hover().bg_color(hsluv(120,50,65)), | |
s().active().bg_color(hsluv(120,40,50)), | |
"Choose another Color",hsl_widget_status(local).on_click(|s| *s = HslWidgetStatus::Open) | |
] | |
] | |
} | |
#[atom] | |
fn hsl(_local: Local) -> Atom<(f64,f64,f64)> { | |
(0.,60.,60.) | |
} | |
#[reaction] | |
fn picker_open_ui<Ms: 'static>(local: Local, ev_handler: Impure<EventHandler<Ms>>) -> Reaction<Node<Ms>> { | |
let el_ref = use_state(||ElRef::<web_sys::HtmlElement>::new() ); | |
let hsl_value = hsl(local).observe(); | |
// log!(hsl_value); | |
Column![ | |
s().p(px(20)), | |
padding = px(8), | |
s().width(px(300)).height(px(300)) | |
.bg_color("white") | |
.radius(px(4)) | |
.b_color("black").b_style_solid().b_width(px(1)), | |
Item![ | |
align = ColumnAlign::MiddleCenter, | |
s().width(px(100)).height(px(100)).bg_color(hsluv::hsluv_to_hex(hsl_value).as_str()) | |
], | |
Item![ | |
align = ColumnAlign::MiddleRight, | |
label!("Hue:"), | |
input![ | |
s().width(px(120)).mr(px(50)).ml(px(20)), | |
attrs!{At::Value => hsl_value.0 ,At::Type => "range", At::Min => 0.0, At::Max => 360}, | |
hsl(local).on_input(|hsl,hue| hsl.0 = hue.parse::<f64>().unwrap()) | |
]] | |
, | |
Item![ | |
align = ColumnAlign::MiddleRight, | |
label!("Saturation:"), | |
input![ | |
s().width(px(120)).mr(px(50)).ml(px(20)), | |
attrs!{At::Value => hsl_value.1 ,At::Type => "range", At::Min => 0.0, At::Max => 100}, | |
hsl(local).on_input(|hsl,saturation| hsl.1 = saturation.parse::<f64>().unwrap()) | |
]], | |
Item![ | |
align = ColumnAlign::MiddleRight, | |
label!["Lightness:"], | |
input![ | |
s().width(px(120)).mr(px(50)).ml(px(20)), | |
attrs!{At::Value => hsl_value.2 ,At::Type => "range", At::Min => 0.0, At::Max => 100}, | |
hsl(local).on_input(|hsl,lightness| hsl.2 =lightness.parse::<f64>().unwrap()) | |
] | |
], | |
Item![ | |
align = ColumnAlign::MiddleRight, | |
button![ | |
s().radius(px(4)).bg_color(hsluv(120,60,60)).color("white").p(px(16)), | |
s().hover().bg_color(hsluv(120,50,65)), | |
s().active().bg_color(hsluv(120,40,50)), | |
"Ok", | |
hsl_widget_status(local).on_click(move |s| { | |
let el = el_ref.get().get().unwrap(); | |
let target = el.dyn_into::<web_sys::EventTarget>().unwrap(); | |
let _res = target.dispatch_event(&web_sys::Event::new("input").unwrap()); | |
*s = HslWidgetStatus::Picked(hsl_value);}) | |
], | |
input![ | |
s().display_none(), | |
el_ref.get(), | |
attrs!{At::Value=> hsluv::hsluv_to_hex(hsl_value)}, | |
ev_handler.0 | |
] | |
] | |
] | |
} | |
// View Entry Here, Sets up theme access, two themes are allowed access | |
// | |
// The first is the app defined theme, the second provides access to seed style presets. | |
// The content block also activates themed global_styles. | |
// --------------- | |
#[topo::nested] // Needed for Seed Hooks | |
pub fn view(_model: &Model) -> Node<Msg> { | |
div![ | |
color_picker( | |
input_ev( | |
Ev::Input,|color| Msg::PickedColorChanged(color) | |
) | |
), | |
color_picker( | |
input_ev( | |
Ev::Input,|color| Msg::PickedColorChanged2(color) | |
) | |
), | |
color_picker( | |
input_ev( | |
Ev::Input,|color| Msg::PickedColorChanged3(color) | |
) | |
), | |
color_picker( | |
input_ev( | |
Ev::Input,|color| Msg::PickedColorChanged(color) | |
) | |
), | |
] | |
} | |
// To be incorporated into atomic_hooks at some point to allow passing in | |
// arguments to an atom/reaction that are not memoized and therefore dont contribute to the "identify" of the observable. | |
#[derive(Clone)] | |
struct Impure<T>(T) where T : Clone; | |
impl <T>PartialEq for Impure<T> where T:Clone{ | |
fn eq(&self, _other: &Self) -> bool { | |
true | |
} | |
} | |
impl <T>std::hash::Hash for Impure<T> where T:Clone{ | |
fn hash<H: std::hash::Hasher>(&self, _state: &mut H) { | |
} | |
} | |
impl <T> std::cmp::Eq for Impure<T> where T:Clone{} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment