use leptos::*;
use leptos::logging::log;
use wasm_bindgen::prelude::*;
use wasm_bindgen::{JsValue, closure::Closure};
use js_sys::{Array,Object,Reflect};
use serde::{Serialize,Deserialize,de::DeserializeOwned};
use serde_wasm_bindgen::from_value;
use super::utils::convert;
#[allow(non_snake_case)]
#[derive(Clone, Debug, Serialize, Deserialize)]
struct CellRenderProps<T>{
cellData: T,
rowIndex: i32,
}
#[component]
pub fn TableV2<T>(
#[prop(into)] width: MaybeSignal<i32>,
#[prop(into)] height: MaybeSignal<i32>,
#[prop(into)] columns: MaybeSignal<Vec<Column>>,
#[prop(into)] data: MaybeSignal<Vec<T>>,
) -> impl IntoView
where T: Serialize + 'static + Clone + std::fmt::Debug
{
view! {
<el-table-v2
prop:columns=move || { columns_to_jsvalue(&columns.get()) }
prop:data=move || {
if let Ok(data) = serde_wasm_bindgen::to_value(&data) {
data
} else {
log!("TableV2: data is not serializable");
JsValue::UNDEFINED
}
}
width=width
height=height
fixed
></el-table-v2>
}
}
#[derive(Clone, Debug, Default)]
pub struct Column {
js_object: Object,
renderer: Option<JsValue>,
pub data_key: String,
pub title: String,
pub width: i32,
}
fn set_property<T: Into<JsValue>>(object: &Object, key: &str, value: T) {
let _ = Reflect::set(object, &JsValue::from_str(key), &value.into());
}
impl Column{
pub fn new(data_key: String, title: String, width: i32) -> Self {
let js_object = Object::new();
set_property(&js_object, "dataKey", data_key.clone());
set_property(&js_object, "title", title.clone());
set_property(&js_object, "width", width.clone());
Self {
js_object,
data_key,
title,
width,
..Default::default()
}
}
pub fn cell_renderer<T,F>(mut self, _cell_renderer: F) -> Self
where T: DeserializeOwned + 'static,
F: Fn(i32,T)->View + 'static {
let closure: Closure<dyn FnMut(JsValue)->JsValue> = Closure::new(move|cell_data: JsValue|{
let data: CellRenderProps<T>;
if let Ok(d) = from_value(cell_data) {
data = d;
}else{
log!("TableV2: cell_data is not deserializable");
return JsValue::UNDEFINED.into();
}
let view = _cell_renderer(data.rowIndex, data.cellData);
if let View::Element(v) = view {
return convert(v.into_html_element().into_any().get_root_node());
}else{
log!("cell_renderer must return View::Element");
return JsValue::UNDEFINED.into();
}
});
js_sys::Reflect::set(&self.js_object, &JsValue::from_str("cellRenderer"), &closure.as_ref().unchecked_ref()).unwrap();
self.renderer = Some(closure.into_js_value());
self
}
}
impl Into<JsValue> for Column {
fn into(self) -> JsValue {
self.js_object.into()
}
}
fn columns_to_jsvalue(columns: &Vec<Column>) -> JsValue {
let js_values: Vec<JsValue> = columns.iter().map(|column| {
column.clone().into()
}).collect();
let array = js_values.into_iter().collect::<Array>();
array.into()
}