feat: add component

This commit is contained in:
luoxiao 2023-03-28 12:37:24 +08:00
parent 13eff74d96
commit e4154d5c4b
11 changed files with 312 additions and 0 deletions

12
Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "melt-ui"
version = "0.0.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
leptos = { version = "0.2.4", features = ["stable"] }
stylers = "0.3.0"
web-sys = "0.3.61"
leptos_dom = { version = "0.2.4" }

21
src/button/button.css Normal file
View file

@ -0,0 +1,21 @@
.melt-button {
height: 34px;
padding: 0 16px;
background-color: #0000;
border: 1px solid #555a;
border-radius: 5px;
cursor: pointer;
}
.melt-button:hover {
transition: all 0.3s;
border-color: #555;
}
.melt-button--text {
border: none;
}
.melt-button--text:hover {
color: #4af;
}

17
src/button/mod.rs Normal file
View file

@ -0,0 +1,17 @@
use leptos::*;
use stylers::style_sheet;
#[component]
pub fn Button(cx: Scope, #[prop(default = false)] text: bool, children: Children) -> impl IntoView {
let class_name = style_sheet!("./src/button/button.css");
let class = move || if text {
"melt-button melt-button--text"
} else {
"melt-button"
};
view! {cx, class=class_name,
<button class=class>
{children(cx)}
</button>
}
}

30
src/card/card.css Normal file
View file

@ -0,0 +1,30 @@
.melt-card {
display: flex;
flex-direction: column;
overflow: hidden;
}
.melt-card__header {
font-weight: 600;
display: flex;
align-items: center;
}
.melt-card__header-extra {
display: flex;
align-items: center;
}
.melt-card__header-content {
flex: 1;
}
.melt-card__header,
.melt-card__content,
.melt-card__footer {
padding: 12px 28px;
background-color: #fff;
}
.melt-card__header {
padding: 20px 28px;
}
.melt-card__header + .melt-card__content,
.melt-card__footer {
padding: 0 28px 20px;
}

76
src/card/mod.rs Normal file
View file

@ -0,0 +1,76 @@
use leptos::*;
use stylers::style_sheet;
#[component]
pub fn Card(
cx: Scope,
#[prop(default = None)] title: Option<String>,
#[prop(default = None)] header: Option<Children>,
#[prop(default = None)] header_extra: Option<Children>,
children: Children,
#[prop(default = None)] footer: Option<Children>,
) -> impl IntoView {
let class_name = style_sheet!("./src/card/card.css");
view! {
cx, class=class_name,
<div class="melt-card">
{
if header.is_some() || title.is_some() {
view! {
cx, class=class_name,
<div class="melt-card__header">
<div class="melt-card__header-content">
{
if let Some(header) = header {
view! {
cx,
<>
{ header(cx) }
</>
}
} else {
view! {
cx,
<>
{ title }
</>
}
}
}
</div>
{
if let Some(header_extra) = header_extra {
view! {
cx, class=class_name,
<div class="melt-card__header-extra">
{ header_extra(cx)}
</div>
}.into()
} else {
None
}
}
</div>
}.into()
} else {
None
}
}
<div class="melt-card__content">
{ children(cx) }
</div>
{
if let Some(footer) = footer {
view! {
cx, class=class_name,
<div class="melt-card__footer">
{ footer(cx) }
</div>
}.into()
} else {
None
}
}
</div>
}
}

9
src/lib.rs Normal file
View file

@ -0,0 +1,9 @@
mod button;
mod card;
mod modal;
mod table;
mod teleport;
pub use button::*;
pub use modal::*;
pub use table::*;

39
src/modal/mod.rs Normal file
View file

@ -0,0 +1,39 @@
use crate::card::*;
use crate::teleport::*;
use leptos::*;
use stylers::style_sheet;
#[component]
pub fn Modal(
cx: Scope,
#[prop(default = None)] title: Option<String>,
children: Children,
#[prop(default = None)] footer: Option<Children>,
open: ReadSignal<bool>,
#[prop(default = None)] on_cancel: Option<Box<dyn Fn() + 'static>>,
) -> impl IntoView {
let class_name = style_sheet!("./src/modal/modal.css");
let header_extra = |cx| {
view! {
cx,
<>
<span style="cursor: pointer;" on:click=move |_| if let Some(on_cancel) = &on_cancel { on_cancel()}>
{ "x" }
</span>
</>
}
};
view! {
cx, class=class_name,
<Teleport>
<div class="melt-modal-container" style=move || if open.get() { "" } else { "display: none" }>
<div class="melt-modal-mask"></div>
<div class="melt-modal-body">
<Card title=title header_extra=Some(Box::new(header_extra)) footer=footer>
{children(cx)}
</Card>
</div>
</div>
</Teleport>
}
}

49
src/modal/modal.css Normal file
View file

@ -0,0 +1,49 @@
.melt-modal-container {
z-index: 2001;
}
.melt-modal-mask {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
background-color: #0007;
z-index: 2000;
}
.melt-modal-body {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
min-height: 100%;
z-index: 2002;
}
.melt-model-card {
width: 600px;
margin: auto;
}
.melt-model-card__header-close {
cursor: pointer;
}
.melt-model-card__header-title {
font-size: 16px;
}
.melt-modal-body {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
min-height: 100%;
z-index: 2002;
}
.melt-modal-body .melt-card {
width: 480px;
margin: auto;
}
.melt-modal-body .melt-card__header-content {
font-size: 16px;
}

12
src/table/mod.rs Normal file
View file

@ -0,0 +1,12 @@
use leptos::*;
use stylers::style_sheet;
#[component]
pub fn Table(cx: Scope, children: Children) -> impl IntoView {
let class_name = style_sheet!("./src/table/table.css");
view! {cx, class=class_name,
<table class="melt-table">
{children(cx)}
</table>
}
}

16
src/table/table.css Normal file
View file

@ -0,0 +1,16 @@
.melt-table {
width: 100%;
}
.melt-table th {
text-align: inherit;
}
.melt-table td,
.melt-table th {
padding: 10px 12px;
}
.melt-table thead {
border-bottom: 1px solid #888;
}

31
src/teleport/mod.rs Normal file
View file

@ -0,0 +1,31 @@
use leptos::*;
use web_sys::Element;
/// https://github.com/solidjs/solid/blob/main/packages/solid/web/src/index.ts#L56
#[component]
pub fn Teleport(cx: Scope, #[prop(optional)] to: Option<String>, children: Children) -> impl IntoView {
let parent = if let Some(to) = to {
document().query_selector(to.as_str()).expect("element not to exist").expect("element not to exist")
} else {
Element::from(document().body().expect("body element not to exist"))
};
#[cfg(all(target_arch = "wasm32"))]
{
use leptos_dom::Mountable;
let node = children(cx).into_view(cx);
parent.append_child(&node.get_mountable_node()).unwrap();
}
#[cfg(not(target_arch = "wasm32"))]
{
_ = cx;
_ = parent;
_ = children;
}
view! {
cx,
<></>
}
}