mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-23 06:19:22 -05:00
✨ feat: add component
This commit is contained in:
parent
13eff74d96
commit
e4154d5c4b
11 changed files with 312 additions and 0 deletions
12
Cargo.toml
Normal file
12
Cargo.toml
Normal 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
21
src/button/button.css
Normal 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
17
src/button/mod.rs
Normal 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
30
src/card/card.css
Normal 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
76
src/card/mod.rs
Normal 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
9
src/lib.rs
Normal 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
39
src/modal/mod.rs
Normal 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
49
src/modal/modal.css
Normal 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
12
src/table/mod.rs
Normal 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
16
src/table/table.css
Normal 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
31
src/teleport/mod.rs
Normal 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,
|
||||||
|
<></>
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue