From 6b09a15e1afff9e6633602d3463204c54827f65c Mon Sep 17 00:00:00 2001 From: Florent Becker Date: Fri, 29 Jul 2022 18:23:01 +0200 Subject: [PATCH] Ajout de l'aide --- kikikoz.css | 18 +++++- src/main.rs | 166 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 117 insertions(+), 67 deletions(-) diff --git a/kikikoz.css b/kikikoz.css index b5a05ba..7b32ade 100644 --- a/kikikoz.css +++ b/kikikoz.css @@ -2,10 +2,26 @@ body { margin: 0; } +details.aide[open] { + columns: 2; + background-color: rgba(255, 255, 255, 0.5); + border-radius: .5rem; +} + +details.aide > summary { + font-weight: 1000; +} + +details.configuration[open] { + background-color: rgba(0, 0, 0, 0.2); + padding: 1ex; + border-radius: .5rem; +} + body > div { background-color: aliceblue; display: flex; - height: 100vh; + min-height: 100vh; padding: 8px; } diff --git a/src/main.rs b/src/main.rs index 1527e5a..ba81ebe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -136,7 +136,9 @@ fn ConfigQueue<'a, G: Html>(cx: Scope<'a>, q: QueueProp<'a>) -> View { }); section() - .c(h3().dyn_t(move || nom.clone())) + .c(h3().dyn_t(move || format!("{} ", nom)).c(button() + .t("Supprimer") + .on("click", move |_ev| (queue.à_détruire.set(true))))) .c(input() .attr("type", "checkbox") .dyn_attr("id", { @@ -166,9 +168,6 @@ fn ConfigQueue<'a, G: Html>(cx: Scope<'a>, q: QueueProp<'a>) -> View { move || Some(format!("prio_queue_{}", &suffix.clone())) }) .dyn_t(|| format!("Priorité: {}", queue.priorité.get()))) - .c(button() - .t("Supprimer") - .on("click", move |ev| (queue.à_détruire.set(true)))) .view(cx) } @@ -192,6 +191,76 @@ fn Queue<'a, G: Html>(cx: Scope<'a>, queue: QueueProp<'a>) -> View { } } +#[derive(Prop, Clone, Copy)] +struct BarreCôtéProp<'a> { + queues: &'a Signal>>, +} + +fn classe_visible(b: bool) -> &'static str { + if b { + "visible" + } else { + "hidden" + } +} + +#[component] +fn Explications<'a, G: Html>(cx: Scope<'a>) -> View { + details() + .class("aide") + .c(summary().t("Comment utiliser Kikikoz")) + .c(p().t("Kikikoz est un outil qui vous aide à répartir la parole dans une réunion.")) + .c(p().t("Quand vous lancez Kikikoz, l'interface que vous voyez correspond à une réunion avec un tour de parole divisé en deux file. C'est une pratique que l'on observe souvent, notamment pour répartir équitablement la parole entre femmes et hommes.")) + .c(p().t("Quand une personne lève la main, vous allez inscrire son nom dans le champ de texte correspondant à la file d'attente idoine.")) + .c(p().t("Pour donner la parole à la personne suivante, il suffit de cliquer sur le bouton \"suivantə\", ou de taper CTRL+ENTRÉE. Son nom et son temps de parole apparaissent alors en gros caractères")) + .c(p().t("La parole alterne entre les deux files. Vous l'avez peut-être remarqué, quand une personne lève la main, elle ne va pas à la fin de la file, mais à une position aléatoire. C'est inhabituel, mais pas moins juste. Il y a des chances que ce qu'elle à a dire soit plus pertinent, puisqu'elle lève la main après avoir entendu plus de choses. Vous pouvez changer ce comportement pour chaque file.")) + .c(p().t("Pour faire une pause dans la réunion (quand personne n'a la parole), cliquez sur \"pause\".")) + .c(p().t("Vous pouvez configurer toutes sortes de détails via le panneau configaration. Vous pouvez y ajouter ou supprimer des files, décider si l'ajout de personnes dans chaque file doit se faire à une position aléatoire ou à la fin. Vous pouvez également attribuer une priorité différente aux files, si vous décidez que la parole doit revenir à l'une d'entre elles plus souvent qu'aux autres.")) + .view(cx) +} + +#[component] +fn BarreCôté<'a, G: Html>(cx: Scope<'a>, p: BarreCôtéProp<'a>) -> View { + let conf_visible = create_signal(cx, false); + let nom_nouvelle_file = create_signal(cx, String::new()); + let nouvelle_file = { + let queues = p.queues.clone(); + move |_ev| { + let q = ÉtatQueue::new(cx, &nom_nouvelle_file.get()); + queues.modify().push(q); + *nom_nouvelle_file.modify() = String::new(); + } + }; + let nouvelle_file_clav = move |ev: web_sys::Event| { + let event: web_sys::KeyboardEvent = ev.clone().unchecked_into(); + if event.key_code() == 13 && !event.ctrl_key() { + nouvelle_file(ev) + } + }; + + view! {cx, + aside { + + details(class="configuration") { + summary { "Configuration " } + + Keyed { + iterable: p.queues, + key: |q| q.id, + view: |cx, q| view!{cx, ConfigQueue{ q }} + } + + section { + h3 {"Nouvelle file" } + input(type="text", bind:value=nom_nouvelle_file, on:keypress=nouvelle_file_clav) {} + button(on:click=nouvelle_file) { "Créer" } + } + } + + } + } +} + fn main() { sycamore::render(|cx| { let heure_courante = create_signal(cx, 0.0); @@ -207,21 +276,17 @@ fn main() { let date_prise_parole = create_signal(cx, 0.0); let courantə = create_signal(cx, None); // let courantə2 = create_signal(cx, None).clone(); - let conf_visible = create_signal(cx, false); let queues = create_signal( cx, vec![ÉtatQueue::new(cx, "Gauche"), ÉtatQueue::new(cx, "Droite")], ); - let supprime_queue = create_effect(cx, || { + create_effect(cx, || { for q in queues.get().iter() { q.à_détruire.track() } queues.modify().retain(|q| !(*q.à_détruire.get())); }); - let index_queues = create_memo(cx, { - || (0..queues.get().len()).into_iter().collect::>() - }); let total_priorités: &ReadSignal = create_memo(cx, { let queues = queues.clone(); move || { @@ -233,14 +298,13 @@ fn main() { } }); let queue_courante: &ReadSignal<(usize, usize)> = create_memo(cx, { - let queues = queues.clone(); move || { let (idx, (id, _q)) = queues .get() .iter() .map(|q| (q.id, *q.crédit_parole.get())) .enumerate() - .max_by(|(ia, (id_a, a)), (ib, (id_b, b))| { + .max_by(|(_ia, (_id_a, a)), (_ib, (_id_b, b))| { a.partial_cmp(b).expect("Tried to compare a NaN") }) .expect("bla"); @@ -248,33 +312,24 @@ fn main() { } }); provide_context_ref(cx, queue_courante); - let next = { - let mut queues = queues.clone(); - let queue_courante = queue_courante.clone(); - move |_ev| { - //js_sys::alert("coucou"); - let mut queue: ÉtatQueue = queues.get()[queue_courante.get().0].clone(); - match queue.dequeue() { - Some(p) => { - courantə.set(Some(p)); - date_prise_parole.set(js_sys::Date::now()) - } - None => (), + let pause = move |_ev| { + courantə.set(None); + }; + let next = move |_ev| { + //js_sys::alert("coucou"); + let mut queue: ÉtatQueue = queues.get()[queue_courante.get().0].clone(); + match queue.dequeue() { + Some(p) => { + courantə.set(Some(p)); + date_prise_parole.set(js_sys::Date::now()) } - for q in queues.get().iter() { - *q.crédit_parole.modify() += - (*q.priorité.get() as f32) / (*total_priorités.get() as f32) + None => { + courantə.set(None); } } - }; - - let nom_nouvelle_file = create_signal(cx, String::new()); - let nouvelle_file = { - let mut queues = queues.clone(); - move |_ev| { - let q = ÉtatQueue::new(cx, &nom_nouvelle_file.get()); - queues.modify().push(q); - *nom_nouvelle_file.modify() = String::new(); + for q in queues.get().iter() { + *q.crédit_parole.modify() += + (*q.priorité.get() as f32) / (*total_priorités.get() as f32) } }; @@ -294,14 +349,16 @@ fn main() { match &*courantə.get() { None => "En attente".into(), Some(p) => { - let temps_parole = std::time::Duration::from_millis((*heure_courante.get() - *date_prise_parole.get()) as u64); - format!("On écoute {} depuis {:2}:{:02}", p, 0, temps_parole.as_secs()) + let temps_parole = std::time::Duration::from_millis((*heure_courante.get() - *date_prise_parole.get()) as u64).as_secs(); + format!("On écoute {} depuis {:2}:{:02}", p, temps_parole / 60, temps_parole % 60) } } ) br {} button(on:click=next) { "Suivantə" } + " " + button(on:click=pause) { "Pause" } } section(id="les_queues") { Keyed { @@ -319,37 +376,14 @@ fn main() { } } } - } - aside { - button(on:click=|_| conf_visible.set(! *conf_visible.get())) { - ( - if *conf_visible.get() { - "cacher" - } else { - "configuration" - } - ) - } + br {} + + Explications {} - div(class=if *conf_visible.get() { "visible" } else { "hidden" }) { - h2 { "Configuration" } - Keyed { - iterable: queues, - key: |q| q.id, - view: |cx, q| view!{cx, ConfigQueue{ q }} - } - section { - h3 {"Nouvelle file" } - input(type="text", bind:value=nom_nouvelle_file, on:keypress=move |ev: web_sys::Event| { - let event : web_sys::KeyboardEvent = ev.clone().unchecked_into(); - if event.key_code() == 13 && !event.ctrl_key() { - nouvelle_file(ev) - } - }) {} - button(on:click=nouvelle_file) { "Créer" } - } - } } + BarreCôté { + queues + } } } });