mpc_valet/components/
layer_select_form.rs1use crate::components::{Icon, LayerSelect};
2use crate::model::LayerFile;
3use gloo_storage::{LocalStorage, Storage};
4use itertools::Itertools;
5use serde::{Deserialize, Serialize};
6use yew::{html, Callback, Component, Context, Html, Properties};
7
8use crate::model::SampleFile;
9
10#[derive(Properties, PartialEq)]
11pub struct LayerSelectFormProps {
12 #[prop_or_default]
13 pub files: Vec<SampleFile>,
14
15 pub on_selected: Callback<Vec<LayerFile>>,
16
17 pub on_cancel: Callback<()>,
18}
19
20pub enum LayerSelectFormMessages {
21 LayerChanged(usize, usize),
22 AllLayerChanged(usize),
23 Swap(usize, usize),
24 Done,
25 Reset,
26 Cancel,
27}
28
29#[derive(Default, Serialize, Deserialize)]
31pub struct LayerSelectForm {
32 pub layer_files: Vec<LayerFile>,
33}
34
35impl From<Vec<SampleFile>> for LayerSelectForm {
36 fn from(sample_files: Vec<SampleFile>) -> Self {
37 let layer_files: Vec<LayerFile> = sample_files
39 .iter()
40 .sorted_by(|a, b| a.root.cmp(&b.root))
42 .group_by(|f| f.root)
44 .into_iter()
45 .flat_map(|(_, group)| {
46 group
47 .sorted_by(|a, b| a.file.cmp(&b.file))
49 .enumerate()
50 .map(|(index, file)| LayerFile::from_sample_file(file.clone(), index % 4))
53 })
54 .sorted_by(|a, b| a.file.cmp(&b.file))
55 .collect();
56 Self { layer_files }
57 }
58}
59
60impl Component for LayerSelectForm {
61 type Message = LayerSelectFormMessages;
62 type Properties = LayerSelectFormProps;
63
64 fn create(ctx: &Context<Self>) -> Self {
65 LocalStorage::get("layer_select_form").unwrap_or_else(|_| ctx.props().files.clone().into())
66 }
67
68 fn destroy(&mut self, _ctx: &Context<Self>) {
69 LocalStorage::delete("layer_select_form");
70 }
71
72 fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
73 let redraw = match msg {
74 LayerSelectFormMessages::LayerChanged(index, layer) => {
75 self.layer_files[index].layer = layer;
76 true
77 }
78 LayerSelectFormMessages::AllLayerChanged(layer) => {
79 self.layer_files.iter_mut().for_each(|l| l.layer = layer);
80 true
81 }
82 LayerSelectFormMessages::Swap(layer1, layer2) => {
83 self.layer_files.iter_mut().for_each(|f| {
84 f.layer = if f.layer == layer1 {
85 layer2
86 } else if f.layer == layer2 {
87 layer1
88 } else {
89 f.layer
90 };
91 });
92 true
93 }
94 LayerSelectFormMessages::Done => {
95 ctx.props().on_selected.emit(self.layer_files.clone());
96 false
97 }
98
99 LayerSelectFormMessages::Cancel => {
100 ctx.props().on_cancel.emit(());
101 false
102 }
103 LayerSelectFormMessages::Reset => {
104 *self = ctx.props().files.clone().into();
105 true
106 }
107 };
108 LocalStorage::set("layer_select_form", self).unwrap_or_else(|e| {
109 log::error!("{e}");
110 });
111 redraw
112 }
113
114 fn view(&self, ctx: &Context<Self>) -> Html {
115 let samples: Vec<Html> = self
116 .layer_files
117 .iter()
118 .enumerate()
119 .map(|(index, sample)| {
120 html! {
121 <LayerSelect
122 label={sample.file.clone()}
123 initial={sample.layer}
124 selection_changed={ctx.link().callback(move |layer: usize| LayerSelectFormMessages::LayerChanged(index, layer))}
125 />
126 }
127 })
128 .collect();
129
130 let used_layers: Vec<usize> = self
131 .layer_files
132 .iter()
133 .map(|f| f.layer)
134 .unique()
135 .collect_vec();
136
137 let all_layers = match used_layers.len() {
138 1 => Some(used_layers[0]),
139 _ => None,
140 };
141
142 html! {
143 <div class="modal is-active">
144 <div class="modal-background"></div>
145 <div class="modal-card">
146 <header class="modal-card-head">
147 <Icon icon="layers" text="Select layers" text_class="modal-card-title" />
148 <button class="delete" aria-label="close" onclick={ctx.link().callback(|_| LayerSelectFormMessages::Cancel)}></button>
149 </header>
150 <section class="modal-card-body">
151 <LayerSelect
152 label={"All"}
153 initial={all_layers}
154 selection_changed={ctx.link().callback(LayerSelectFormMessages::AllLayerChanged)}
155 />
156 {samples}
157 <div class="columns">
158 <div class="column is-one-quarter">
159 {"Swap Layers"}
160 </div>
161 <div class="column">
162 <div class="buttons has-addons is-centered">
163 <button class="button" onclick={ctx.link().callback(|_| LayerSelectFormMessages::Swap(0,1))}>
164 <Icon icon="swap-horizontal-outline" text="Swap 1-2" />
165 </button>
166 <button class="button" onclick={ctx.link().callback(|_| LayerSelectFormMessages::Swap(1,2))}>
167 <Icon icon="swap-horizontal-outline" text="Swap 2-3" />
168 </button>
169 <button class="button" onclick={ctx.link().callback(|_| LayerSelectFormMessages::Swap(2,3))}>
170 <Icon icon="swap-horizontal-outline" text="Swap 3-4" />
171 </button>
172 </div>
173 </div>
174 </div>
175 </section>
176 <footer class="modal-card-foot">
177 <div class="buttons has-addons">
178 <button class="button" onclick={ctx.link().callback(|_| LayerSelectFormMessages::Cancel)}>
179 <Icon icon="trash" text ="Cancel" />
180 </button>
181 <button class="button" onclick={ctx.link().callback(|_| LayerSelectFormMessages::Reset)}>
182 <Icon icon="refresh" text="Reset" />
183 </button>
184 <button class="button is-success" onclick={ctx.link().callback(|_| LayerSelectFormMessages::Done)}>{"Ok"}</button>
185 </div>
186 </footer>
187 </div>
188 </div>
189 }
190 }
191
192 fn changed(&mut self, ctx: &Context<Self>) -> bool {
193 *self = ctx.props().files.clone().into();
194 true
195 }
196}