Skip to content

Instantly share code, notes, and snippets.

@rebo
Created July 25, 2020 21:28
Show Gist options
  • Save rebo/01a267867b2371920cc3e2fc8512a929 to your computer and use it in GitHub Desktop.
Save rebo/01a267867b2371920cc3e2fc8512a929 to your computer and use it in GitHub Desktop.
#![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
}
#[derive(Clone)]
pub struct HslWidgetState {
status: HslWidgetStatus,
hsl : (f64,f64,f64)
}
#[atom]
fn hsl_widget_state(_local: Local) ->Atom<HslWidgetState> {
HslWidgetState{
status: HslWidgetStatus::Unpicked,
hsl : (0.,60.,60.),
}
}
#[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 state = hsl_widget_state(local).observe();
match state.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_state(local).on_click(|s| s.status = 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_state(local).on_click(|s| s.status = HslWidgetStatus::Open)
]
]
}
#[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 widget_state = hsl_widget_state(local).observe();
let hsl = widget_state.hsl;
// 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(widget_state.hsl).as_str())
],
Item![
align = ColumnAlign::MiddleRight,
label!("Hue:"),
input![
s().width(px(120)).mr(px(50)).ml(px(20)),
attrs!{At::Value => widget_state.hsl.0 ,At::Type => "range", At::Min => 0.0, At::Max => 360},
hsl_widget_state(local).on_input(|s,hue| s.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 => widget_state.hsl.1 ,At::Type => "range", At::Min => 0.0, At::Max => 100},
hsl_widget_state(local).on_input(|s,saturation| s.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 => widget_state.hsl.2 ,At::Type => "range", At::Min => 0.0, At::Max => 100},
hsl_widget_state(local).on_input(|s,lightness| s.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_state(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.status = HslWidgetStatus::Picked(hsl);})
],
input![
s().display_none(),
el_ref.get(),
attrs!{At::Value=> hsluv::hsluv_to_hex(widget_state.hsl.clone())},
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