leptos_element_plus 0.0.2

A wrapper of element-plus for leptos
Documentation

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
{
    /*
     TODO: cleanup
    on_cleanup(move || {
        log!("TableV2: cleanup");
    });
    */
    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()
}