1use hadrone_core::interaction::{InteractionSession, InteractionType};
2use hadrone_core::{
3 CollisionStrategy, CompactionType, FreePlacementCompactor, LayoutEngine, LayoutItem,
4 ResizeHandle, RisingTideCompactor,
5};
6use wasm_bindgen::prelude::*;
7
8#[wasm_bindgen]
9pub struct GridEngineWasm {
10 layout: Vec<LayoutItem>,
11 cols: i32,
12 compaction: CompactionType,
13 session: Option<InteractionSession>,
14}
15
16#[wasm_bindgen]
17impl GridEngineWasm {
18 #[wasm_bindgen(constructor)]
19 pub fn new(cols: i32, compaction_str: &str) -> Self {
20 let compaction = match compaction_str {
21 "FreePlacement" => CompactionType::FreePlacement,
22 _ => CompactionType::Gravity,
23 };
24 Self {
25 layout: Vec::new(),
26 cols,
27 compaction,
28 session: None,
29 }
30 }
31
32 #[wasm_bindgen]
33 pub fn set_layout(&mut self, items: JsValue) -> Result<(), JsValue> {
34 self.layout = serde_wasm_bindgen::from_value(items)?;
35 self.compact();
36 Ok(())
37 }
38
39 #[wasm_bindgen]
40 pub fn get_layout(&self) -> Result<JsValue, JsValue> {
41 serde_wasm_bindgen::to_value(&self.layout).map_err(|e| e.into())
42 }
43
44 fn compact(&mut self) {
45 let compactor: Box<dyn hadrone_core::Compactor> = match self.compaction {
46 CompactionType::Gravity => Box::new(RisingTideCompactor),
47 CompactionType::FreePlacement => Box::new(FreePlacementCompactor),
48 };
49 let engine = LayoutEngine::with_default_collision(compactor, self.cols);
50 engine.compact(&mut self.layout);
51 }
52
53 #[allow(clippy::too_many_arguments)]
54 #[wasm_bindgen]
55 pub fn start_interaction(
56 &mut self,
57 id: &str,
58 interaction_type: &str,
59 handle_str: &str,
60 mouse_x: f32,
61 mouse_y: f32,
62 col_width_px: f32,
63 row_height_px: f32,
64 margin_x: i32,
65 margin_y: i32,
66 padding_x: i32,
67 padding_y: i32,
68 ) {
69 if let Some(item) = self.layout.iter().find(|i| i.id == id) {
70 let i_type = match interaction_type {
71 "Resize" => InteractionType::Resize,
72 _ => InteractionType::Drag,
73 };
74
75 let handle = match handle_str {
76 "East" => ResizeHandle::East,
77 "West" => ResizeHandle::West,
78 "North" => ResizeHandle::North,
79 "South" => ResizeHandle::South,
80 "NorthEast" => ResizeHandle::NorthEast,
81 "NorthWest" => ResizeHandle::NorthWest,
82 "SouthEast" => ResizeHandle::SouthEast,
83 "SouthWest" => ResizeHandle::SouthWest,
84 _ => ResizeHandle::SouthEast,
85 };
86
87 self.session = Some(InteractionSession {
88 id: id.to_string(),
89 interaction_type: i_type,
90 start_mouse: (mouse_x, mouse_y),
91 start_rect: (item.x, item.y, item.w, item.h),
92 handle,
93 col_width_px,
94 row_height_px,
95 margin: (margin_x, margin_y),
96 container_padding: (padding_x, padding_y),
97 compaction: self.compaction,
98 collision: CollisionStrategy::PushDown,
99 });
100 }
101 }
102
103 #[wasm_bindgen]
104 pub fn update_interaction(&mut self, mouse_x: f32, mouse_y: f32) -> Result<JsValue, JsValue> {
105 if let Some(session) = &self.session {
106 let mut virtual_layout = self.layout.clone();
107 session.update((mouse_x, mouse_y), &mut virtual_layout, self.cols);
108
109 serde_wasm_bindgen::to_value(&virtual_layout).map_err(|e| e.into())
111 } else {
112 self.get_layout()
113 }
114 }
115
116 #[wasm_bindgen]
117 pub fn end_interaction(&mut self, mouse_x: f32, mouse_y: f32) {
118 if let Some(session) = self.session.take() {
119 session.update((mouse_x, mouse_y), &mut self.layout, self.cols);
120 }
121 }
122}