Compare commits

..

No commits in common. "72e27dc20d8f8cbcd88e020368fb5f825288fa89" and "6d4e8462b8cd89253a812e8e930f63b4db5bede8" have entirely different histories.

3 changed files with 197 additions and 260 deletions

View File

@ -20,7 +20,6 @@
in pkgs.mkShell { in pkgs.mkShell {
buildInputs = with pkgs; [ buildInputs = with pkgs; [
cargo cargo
rustfmt
wasm-bindgen-cli wasm-bindgen-cli
trunk trunk
nodePackages.sass nodePackages.sass

View File

@ -1,21 +1,12 @@
body { body {
margin: 0;
}
body > div {
background-color: aliceblue; background-color: aliceblue;
display: flex; display: flex;
height: 100vh;
padding: 8px;
} }
section#les_queues { section#les_queues {
display: flex; display: grid;
} grid-template-columns: 1fr 1fr;
#les_queues > div {
flex-grow: 1;
} }
h1 { h1 {

View File

@ -1,323 +1,270 @@
use rand::distributions::Alphanumeric;
use rand::prelude::*;
use sycamore::builder::prelude::*;
use sycamore::prelude::*; use sycamore::prelude::*;
use sycamore::builder::prelude::*;
use sycamore::rt::JsCast; use sycamore::rt::JsCast;
use rand::prelude::*;
use rand::distributions::Alphanumeric;
enum NomQueue {
A, B
}
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
enum ModeQueue { enum ModeQueue {
Chrono, Chrono,
Aléatoire, Aléatoire
} }
impl ModeQueue { impl ModeQueue {
fn set_aléatoire(&mut self, b: bool) { fn set_aléatoire(&mut self, b: bool) {
match b { match b {
false => *self = ModeQueue::Chrono, false => { *self = ModeQueue::Chrono},
true => *self = ModeQueue::Aléatoire, true => { *self = ModeQueue::Aléatoire},
} }
} }
} }
impl std::fmt::Display for ModeQueue { impl std::fmt::Display for ModeQueue {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let s = match self { let s = match self {
ModeQueue::Chrono => "Chrono", ModeQueue::Chrono => "Chrono",
ModeQueue::Aléatoire => "Aléatoire", ModeQueue::Aléatoire => "Aléatoire"
}; };
write!(fmt, "{}", s) write!(fmt, "{}", s)
} }
} }
#[derive(Clone)] #[derive(Clone)]
struct ÉtatQueue<'a> { struct ÉtatQueue<'a> {
nom: String, nom: String,
id: usize,
mode: &'a Signal<ModeQueue>, mode: &'a Signal<ModeQueue>,
contenu: &'a Signal<Vec<String>>, contenu: &'a Signal<Vec<String>>,
nouvelleau: &'a Signal<String>, nouvelleau: &'a Signal<String>,
priorité: &'a Signal<u8>, priorité: &'a Signal<u8>,
crédit_parole: &'a Signal<f32>,
à_détruire: &'a Signal<bool>,
} }
impl<'a> PartialEq for ÉtatQueue<'a> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<'a> Eq for ÉtatQueue<'a> {}
impl<'a> ÉtatQueue<'a> { impl<'a> ÉtatQueue<'a> {
fn new<'b>(cx: BoundedScope<'a, 'b>, nom: &str) -> Self { fn new<'b>(cx: BoundedScope<'a, 'b>, nom: &str) -> Self {
ÉtatQueue { ÉtatQueue {
nom: nom.into(), nom: nom.into(),
mode: create_signal(cx, ModeQueue::Aléatoire), mode: create_signal(cx, ModeQueue::Aléatoire),
contenu: create_signal(cx, vec![]), contenu: create_signal(cx, vec![]),
nouvelleau: create_signal(cx, "".into()), nouvelleau: create_signal(cx, "".into()),
priorité: create_signal(cx, 1), priorité: create_signal(cx, 1.into()),
crédit_parole: create_signal(cx, 0.0), }
id: thread_rng().gen(),
à_détruire: create_signal(cx, false),
}
} }
fn len(&self) -> usize { fn len(&self) -> usize {
return self.contenu.get().len(); return self.contenu.get().len()
} }
fn enqueue(&mut self, rng: &mut ThreadRng) { fn enqueue(&mut self, rng: &mut ThreadRng) {
// //
let s = &self.nouvelleau; let s = &self.nouvelleau;
let n = self.len(); let n = self.len();
let k = match *self.mode.get() { let k = match *self.mode.get() {
ModeQueue::Chrono => n, ModeQueue::Chrono => n,
ModeQueue::Aléatoire => rng.gen_range(0, n + 1), ModeQueue::Aléatoire => {
}; rng.gen_range(0, n + 1)
self.contenu.modify().insert(k, s.to_string()) }
};
self.contenu.modify().insert(k, s.to_string())
} }
fn dequeue(&mut self) -> Option<String> { fn dequeue(&mut self) -> Option<String> {
*self.crédit_parole.modify() -= 1.0; if self.len() > 0 {
if self.len() > 0 { Some(self.contenu.modify().remove(0))
Some(self.contenu.modify().remove(0)) } else {
} else { None
None }
}
} }
} }
#[derive(Prop)] #[derive(Prop)]
struct QueueProp<'a> { struct QueueProp<'a>{
q: ÉtatQueue<'a>, q: ÉtatQueue<'a>
} }
fn validate_queue<'a>(mut queue: ÉtatQueue<'a>) -> impl FnMut(web_sys::Event) + 'a { fn validate_queue<'a>(mut queue: ÉtatQueue<'a>) -> impl FnMut(web_sys::Event) + 'a {
move |ev: web_sys::Event| { move |ev: web_sys::Event| {
let mut rng = thread_rng(); let mut rng = thread_rng();
let event: web_sys::KeyboardEvent = ev.unchecked_into(); let event : web_sys::KeyboardEvent = ev.unchecked_into();
if event.key_code() == 13 && !event.ctrl_key() { if event.key_code() == 13 {
queue.enqueue(&mut rng); queue.enqueue(&mut rng);
queue.nouvelleau.set(String::new()) queue.nouvelleau.set(String::new())
} }
} }
} }
// #[component]
// fn Queue<'a, G: Html>(cx: Scope<'a>, queue: QueueProp<'a>) -> View<G> {
// let queue_courante = use_context::<Signal<NomQueue>>(cx);
// }
#[component] #[component]
fn ConfigQueue<'a, G: Html>(cx: Scope<'a>, q: QueueProp<'a>) -> View<G> { fn ConfigQueue<'a, G: Html>(cx: Scope<'a>, queue: QueueProp<'a>) -> View<G> {
let queue: ÉtatQueue = q.q;
let prio_str = create_signal(cx, String::new()); let prio_str = create_signal(cx, String::new());
let est_aléatoire = create_signal(cx, true); //*queue.q.mode.get() == ModeQueue::Aléatoire); let est_aléatoire = create_signal(cx, true); //*queue.q.mode.get() == ModeQueue::Aléatoire);
let nom = queue.nom; let nom = queue.q.nom;
let suffix = rand::thread_rng() let suffix = rand::thread_rng()
.sample_iter(&Alphanumeric) .sample_iter(&Alphanumeric)
.take(15) .take(15)
.map(char::from) .map(char::from)
.collect::<String>(); .collect::<String>();
create_effect(cx, || { create_effect(cx, ||
if let Ok(p) = prio_str.get().parse::<u8>() { if let Ok(p) = prio_str.get().parse::<u8>() {
queue.priorité.set(p) queue.q.priorité.set(p)
} });
}); create_effect(cx, || prio_str.set(format!("{}", queue.q.priorité.get())));
create_effect(cx, || prio_str.set(format!("{}", queue.priorité.get())));
create_effect(cx, || { create_effect(cx, ||
queue.mode.modify().set_aléatoire(*est_aléatoire.get()) queue.q.mode.modify().set_aléatoire(*est_aléatoire.get()));
}); create_effect(cx, || est_aléatoire.set(*queue.q.mode.get() == ModeQueue::Aléatoire));
create_effect(cx, || {
est_aléatoire.set(*queue.mode.get() == ModeQueue::Aléatoire)
});
section() section()
.c(h3().dyn_t(move || nom.clone())) .c(h3().dyn_t(move || nom.clone()))
.c(input() .c(input()
.attr("type", "checkbox") .attr("type", "checkbox")
.dyn_attr("id", { .dyn_attr("id", { let suffix = suffix.clone();
let suffix = suffix.clone(); move || Some(format!("mode_queue_{}", &suffix.clone()))
move || Some(format!("mode_queue_{}", &suffix.clone())) })
}) .bind_checked(est_aléatoire)
.bind_checked(est_aléatoire)) )
.c(label() .c(label()
.dyn_attr("for", { .dyn_attr("for", {
let suffix = suffix.clone(); let suffix = suffix.clone();
move || Some(format!("mode_queue_{}", &suffix.clone())) move || Some(format!("mode_queue_{}", &suffix.clone()))
}) })
.t("Entrée aléatoire")) .t("Entrée aléatoire")
.c(br()) )
.c(input() .c(br())
.dyn_attr("id", { .c(input()
let suffix = suffix.clone(); .dyn_attr("id", { let suffix = suffix.clone();
move || Some(format!("prio_queue_{}", &suffix.clone())) move || Some(format!("prio_queue_{}", &suffix.clone()))
}) })
.attr("type", "range") .attr("type", "range")
.attr("min", "1") .attr("min", "1")
.attr("max", "7") .attr("max", "7")
.bind_value(prio_str)) .bind_value(prio_str)
.c(label() )
.dyn_attr("for", { .c(
let suffix = suffix.clone(); label()
move || Some(format!("prio_queue_{}", &suffix.clone())) .dyn_attr("for", { let suffix = suffix.clone();
}) move || Some(format!("prio_queue_{}", &suffix.clone()))
.dyn_t(|| format!("Priorité: {}", queue.priorité.get()))) })
.c(button() .dyn_t(|| format!("Priorité: {}", queue.q.priorité.get()))
.t("Supprimer") )
.on("click", move |ev| (queue.à_détruire.set(true)))) .view(cx)
.view(cx)
} }
#[component] #[component]
fn Queue<'a, G: Html>(cx: Scope<'a>, queue: QueueProp<'a>) -> View<G> { fn Queue<'a, G: Html>(cx: Scope<'a>, queue: QueueProp<'a>) -> View<G> {
let ok_queue = validate_queue(queue.q.clone()); let ok_queue = validate_queue(queue.q.clone());
view! { cx, view! { cx,
div { div {
section(class="queue") { section(class="queue") {
h3 {(format!("{} ({} - {})", queue.q.nom, queue.q.crédit_parole.get(), queue.q.priorité.get()))} h3 {(queue.q.nom)}
input(type="text", bind:value = queue.q.nouvelleau, on:keypress=ok_queue) {} input(type="text", bind:value = queue.q.nouvelleau, on:keypress=ok_queue) {}
ul { ul {
Keyed { Keyed {
iterable: queue.q.contenu, iterable: queue.q.contenu,
view: |cx, item: String| view! {cx, li { (item) }}, view: |cx, item: String| view! {cx, li { (item) }},
key: |x| x.clone() key: |x| x.clone()
} }
} }
} }
} }
} }
} }
fn main() { fn main() {
sycamore::render(|cx| { sycamore::render(|cx| {
let prochainə = create_signal(cx, String::from("personne encore")); let prochainə = create_signal(cx, String::from("personne encore"));
let conf_visible = create_signal(cx, false); let conf_visible = create_signal(cx, false);
let queues = create_signal( let queue_A = ÉtatQueue::new(cx, "Gauche");
cx, let queue_B = ÉtatQueue::new(cx, "Droite");
vec![ÉtatQueue::new(cx, "Gauche"), ÉtatQueue::new(cx, "Droite")], let queue_courante = create_signal(cx, NomQueue::A);
); provide_context_ref(cx, queue_courante);
let supprime_queue = let next = {
create_effect(cx, || queues.modify().retain(|q| !(*q.à_détruire.get()))); let mut my_queue_A = queue_A.clone();
let queues_suppression = queues.clone(); let mut my_queue_B = queue_B.clone();
let index_queues = create_memo(cx, { move |_ev| {
|| (0..queues.get().len()).into_iter().collect::<Vec<_>>() let (queue, next) = match *queue_courante.get() {
}); NomQueue::A => (&mut my_queue_A, NomQueue::B),
let total_priorités: &ReadSignal<usize> = create_memo(cx, { NomQueue::B => (&mut my_queue_B, NomQueue::A)
let queues = queues.clone(); };
move || { match queue.dequeue() {
queues Some(p) => prochainə.set(p),
.get() None => ()
.iter() }
.map(|q| *q.priorité.get() as usize) queue_courante.set(next);
.sum() }
}
});
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))| {
a.partial_cmp(b).expect("Tried to compare a NaN")
})
.expect("bla");
(idx, id)
}
});
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) => prochainə.set(p),
None => (),
}
for q in queues.get().iter() {
*q.crédit_parole.modify() +=
(*q.priorité.get() as f32) / (*total_priorités.get() as f32)
}
}
};
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();
}
};
view! { cx, let queue_A_pour_aff = queue_A.clone();
div(on:keypress= {let mut next = next.clone(); let queue_A_pour_conf = queue_A.clone();
move |ev: web_sys::Event| { let queue_B_pour_aff = queue_B.clone();
let event: web_sys::KeyboardEvent = ev.clone().unchecked_into(); let queue_B_pour_conf = queue_B.clone();
if event.key_code() == 13 && event.ctrl_key() {
next(ev)
}
}
}) {
main {
h1 { "Kikikoz" }
p(class="orateurice") {
"On écoute " (prochainə.get()) "."
br {}
button(on:click=next) { "Suivantə" }
}
section(id="les_queues") {
Keyed {
iterable: queues, //_pour_aff,
key: |q| q.id,
view: {
let queue_courante = queue_courante.clone();
move |cx, q|
view!{ cx,
div(class=(if queue_courante.get().1 == q.id {"active"} else {"attente"})) {
Queue {q}
}
}
}
}
}
}
aside {
button(on:click=|_| conf_visible.set(! *conf_visible.get())) {
(
if *conf_visible.get() {
"cacher"
} else {
"configuration"
}
)
}
div(class=if *conf_visible.get() { "visible" } else { "hidden" }) { view! { cx,
h2 { "Configuration" } main {
Keyed { h1 { "Kikikoz" }
iterable: queues, p(class="orateurice") {
key: |q| q.id, "On écoute " (prochainə.get()) "."
view: |cx, q| view!{cx, ConfigQueue{ q }} br {}
} button(on:click=next) { "Suivantə" }
section { }
h3 {"Nouvelle file" } section(id="les_queues") {
input(type="text", bind:value=nom_nouvelle_file, on:keypress=move |ev: web_sys::Event| { div(class=(match *queue_courante.get(){NomQueue::A=> "active", NomQueue::B=>"attente"})) {
let event : web_sys::KeyboardEvent = ev.clone().unchecked_into(); Queue {
if event.key_code() == 13 && !event.ctrl_key() { q: queue_A_pour_aff
nouvelle_file(ev) }
} }
}) {}
button(on:click=nouvelle_file) { "Créer" } div(class=(match *queue_courante.get(){NomQueue::B=>"active", NomQueue::A=>"attente"})) {
} Queue {
} q: queue_B_pour_aff
} }
} }
} }
}
aside {
button(on:click=|_| conf_visible.set(! *conf_visible.get())) {
(
if *conf_visible.get() {
"cacher"
} else {
"configuration"
}
)
}
div(class=if *conf_visible.get() { "visible" } else { "hidden" }) {
h2 { "Configuration" }
ConfigQueue {
q: queue_A_pour_conf
}
ConfigQueue {
q: queue_B_pour_conf
}
// section {
// h3 { "Queue B" }
// input(type="checkbox", id="mélanger_queue_B") {}
// label(for="enable_queue_B") {"Randomiser"}
// br {}
// input(type="range", id="prio_queue_B", min="1", max="7", bind:value=queue_B.priorité) {}
// label(for="prio_queue_B") {"Priorité "} // (queue_B.priorité.get())}
// }
}
}
}
}); });
} }