mirror of
https://github.com/yewprint/yewprint
synced 2024-11-22 03:23:03 +00:00
Implement Dialog and Alert (#172)
This commit is contained in:
parent
993b8cc8bd
commit
ad55431263
16 changed files with 1051 additions and 114 deletions
|
@ -55,7 +55,7 @@ Roadmap
|
|||
- [ ] [Breadcrumbs](https://blueprintjs.com/docs/#core/components/breadcrumbs)
|
||||
- [x] [Button](https://blueprintjs.com/docs/#core/components/button)
|
||||
- [ ] Complete Button API
|
||||
- [ ] AnchorButton
|
||||
- [x] AnchorButton
|
||||
- [x] [ButtonGroup](https://blueprintjs.com/docs/#core/components/button-group)
|
||||
- depends on: Button
|
||||
- [x] [Callout](https://blueprintjs.com/docs/#core/components/callout)
|
||||
|
@ -103,11 +103,11 @@ Roadmap
|
|||
- [x] [Overlay](https://blueprintjs.com/docs/#core/components/overlay)
|
||||
- depends on: Portal
|
||||
- [x] [Portal](https://blueprintjs.com/docs/#core/components/portal)
|
||||
- [ ] [Alert](https://blueprintjs.com/docs/#core/components/alert)
|
||||
- [x] [Alert](https://blueprintjs.com/docs/#core/components/alert)
|
||||
- depends on: Button, Dialog
|
||||
- [ ] [Context menu](https://blueprintjs.com/docs/#core/components/context-menu)
|
||||
- depends on: Popover
|
||||
- [ ] [Dialog](https://blueprintjs.com/docs/#core/components/dialog)
|
||||
- [x] [Dialog](https://blueprintjs.com/docs/#core/components/dialog)
|
||||
- depends on: Icon, Overlay, Button
|
||||
- [ ] [Drawer](https://blueprintjs.com/docs/#core/components/drawer)
|
||||
- depends on: Icon, Overlay, Button
|
||||
|
|
131
src/alert.rs
Normal file
131
src/alert.rs
Normal file
|
@ -0,0 +1,131 @@
|
|||
use crate::{Button, Dialog, Icon, Intent};
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Alert {
|
||||
cb: MsgCallbacks<Self>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Properties)]
|
||||
pub struct AlertProps {
|
||||
#[prop_or_default]
|
||||
pub dark: Option<bool>,
|
||||
#[prop_or_default]
|
||||
pub class: Classes,
|
||||
#[prop_or_default]
|
||||
pub style: Option<AttrValue>,
|
||||
#[prop_or_default]
|
||||
pub open: bool,
|
||||
#[prop_or_default]
|
||||
pub icon: Option<Icon>,
|
||||
#[prop_or_default]
|
||||
pub intent: Option<Intent>,
|
||||
#[prop_or_default]
|
||||
pub loading: bool,
|
||||
#[prop_or(html!("OK"))]
|
||||
pub confirm_button: Html,
|
||||
#[prop_or_default]
|
||||
pub cancel_button: Option<Html>,
|
||||
#[prop_or_default]
|
||||
pub onclose: Callback<bool>,
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
#[derive(yew_callbacks::Callbacks)]
|
||||
pub enum Msg {
|
||||
OnCancel,
|
||||
OnConfirmClick(MouseEvent),
|
||||
OnCancelClick(MouseEvent),
|
||||
}
|
||||
|
||||
impl Component for Alert {
|
||||
type Properties = AlertProps;
|
||||
type Message = Msg;
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
Self {
|
||||
cb: ctx.link().into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
let Self::Properties { onclose, .. } = ctx.props();
|
||||
let Self::Properties { loading, .. } = ctx.props();
|
||||
|
||||
match msg {
|
||||
Msg::OnCancel => {
|
||||
if !loading {
|
||||
onclose.emit(false);
|
||||
}
|
||||
false
|
||||
}
|
||||
Msg::OnConfirmClick(_event) => {
|
||||
if !loading {
|
||||
onclose.emit(true);
|
||||
}
|
||||
false
|
||||
}
|
||||
Msg::OnCancelClick(_event) => {
|
||||
if !loading {
|
||||
onclose.emit(false);
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let Self::Properties {
|
||||
dark,
|
||||
class,
|
||||
style,
|
||||
open,
|
||||
icon,
|
||||
intent,
|
||||
loading,
|
||||
confirm_button,
|
||||
cancel_button,
|
||||
onclose: _,
|
||||
children,
|
||||
} = ctx.props();
|
||||
|
||||
let cancel_button_html = cancel_button.clone().map(|x| {
|
||||
html! {
|
||||
<Button
|
||||
disabled={loading}
|
||||
onclick={self.cb.on_cancel_click()}
|
||||
>
|
||||
{x}
|
||||
</Button>
|
||||
}
|
||||
});
|
||||
|
||||
html! {
|
||||
<Dialog
|
||||
{dark}
|
||||
class={classes!("bp3-alert", class.clone())}
|
||||
{style}
|
||||
{open}
|
||||
onclose={self.cb.on_cancel()}
|
||||
>
|
||||
<div class={classes!("bp3-alert-body")}>
|
||||
<Icon {icon} size={40} {intent} />
|
||||
<div class={classes!("bp3-alert-contents")}>
|
||||
{for children.iter()}
|
||||
</div>
|
||||
</div>
|
||||
<div class={classes!("bp3-alert-footer")}>
|
||||
<Button
|
||||
{loading}
|
||||
{intent}
|
||||
onclick={self.cb.on_confirm_click()}
|
||||
>
|
||||
{confirm_button.clone()}
|
||||
</Button>
|
||||
{cancel_button_html}
|
||||
</div>
|
||||
</Dialog>
|
||||
}
|
||||
}
|
||||
}
|
242
src/buttons.rs
242
src/buttons.rs
|
@ -2,105 +2,187 @@ use crate::{Icon, IconSize, Intent, Spinner};
|
|||
use yew::prelude::*;
|
||||
use yew::virtual_dom::AttrValue;
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct ButtonProps {
|
||||
#[prop_or_default]
|
||||
pub fill: bool,
|
||||
#[prop_or_default]
|
||||
pub minimal: bool,
|
||||
#[prop_or_default]
|
||||
pub small: bool,
|
||||
#[prop_or_default]
|
||||
pub outlined: bool,
|
||||
#[prop_or_default]
|
||||
pub loading: bool,
|
||||
#[prop_or_default]
|
||||
pub large: bool,
|
||||
#[prop_or_default]
|
||||
pub active: bool,
|
||||
#[prop_or_default]
|
||||
pub disabled: bool,
|
||||
#[prop_or_default]
|
||||
pub icon: Option<Icon>,
|
||||
#[prop_or_default]
|
||||
pub right_icon: Option<Icon>,
|
||||
#[prop_or_default]
|
||||
pub intent: Option<Intent>,
|
||||
#[prop_or_default]
|
||||
pub title: Option<AttrValue>,
|
||||
#[prop_or_default]
|
||||
pub onclick: Callback<MouseEvent>,
|
||||
#[prop_or_default]
|
||||
pub class: Classes,
|
||||
#[prop_or_default]
|
||||
pub style: Option<AttrValue>,
|
||||
#[prop_or_default]
|
||||
pub button_ref: NodeRef,
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
macro_rules! generate_with_common_props {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
$vis:vis struct $name:ident {
|
||||
$(
|
||||
$(#[$field_attr:meta])*
|
||||
$field_vis:vis $field_name:ident : $field_ty:ty,
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
$(#[$attr])*
|
||||
$vis struct $name {
|
||||
#[prop_or_default]
|
||||
pub fill: bool,
|
||||
#[prop_or_default]
|
||||
pub minimal: bool,
|
||||
#[prop_or_default]
|
||||
pub small: bool,
|
||||
#[prop_or_default]
|
||||
pub outlined: bool,
|
||||
#[prop_or_default]
|
||||
pub loading: bool,
|
||||
#[prop_or_default]
|
||||
pub large: bool,
|
||||
#[prop_or_default]
|
||||
pub active: bool,
|
||||
#[prop_or_default]
|
||||
pub disabled: bool,
|
||||
#[prop_or_default]
|
||||
pub icon: Option<Icon>,
|
||||
#[prop_or_default]
|
||||
pub right_icon: Option<Icon>,
|
||||
#[prop_or_default]
|
||||
pub intent: Option<Intent>,
|
||||
#[prop_or_default]
|
||||
pub title: Option<AttrValue>,
|
||||
#[prop_or_default]
|
||||
pub class: Classes,
|
||||
#[prop_or_default]
|
||||
pub style: Option<AttrValue>,
|
||||
#[prop_or_default]
|
||||
pub button_ref: NodeRef,
|
||||
#[prop_or_default]
|
||||
pub left_element: Option<Html>,
|
||||
#[prop_or_default]
|
||||
pub right_element: Option<Html>,
|
||||
#[prop_or_default]
|
||||
pub aria_label: Option<AttrValue>,
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
|
||||
$(
|
||||
$(#[$field_attr])*
|
||||
$field_vis $field_name: $field_ty,
|
||||
)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
fn common_classes(&self) -> Classes {
|
||||
let disabled = self.disabled || self.loading;
|
||||
|
||||
classes!(
|
||||
"bp3-button",
|
||||
self.fill.then_some("bp3-fill"),
|
||||
self.minimal.then_some("bp3-minimal"),
|
||||
self.small.then_some("bp3-small"),
|
||||
self.outlined.then_some("bp3-outlined"),
|
||||
self.loading.then_some("bp3-loading"),
|
||||
self.large.then_some("bp3-large"),
|
||||
(self.active && !disabled).then_some("bp3-active"),
|
||||
disabled.then_some("bp3-disabled"),
|
||||
self.intent,
|
||||
self.class.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_children(&self) -> Html {
|
||||
html! {
|
||||
<>
|
||||
{
|
||||
self.loading
|
||||
.then(|| html! {
|
||||
<Spinner
|
||||
class={classes!("bp3-button-spinner")}
|
||||
size={IconSize::LARGE}
|
||||
/>
|
||||
})
|
||||
}
|
||||
<Icon icon={self.icon.clone()} />
|
||||
{self.left_element.clone()}
|
||||
{
|
||||
(!self.children.is_empty())
|
||||
.then(|| html! {
|
||||
<span class="bp3-button-text">
|
||||
{for self.children.iter()}
|
||||
</span>
|
||||
})
|
||||
}
|
||||
<Icon icon={self.right_icon.clone()} />
|
||||
{self.right_element.clone()}
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
generate_with_common_props! {
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct ButtonProps {
|
||||
#[prop_or_default]
|
||||
pub onclick: Callback<MouseEvent>,
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(Button)]
|
||||
pub fn button(props: &ButtonProps) -> Html {
|
||||
let ButtonProps {
|
||||
fill,
|
||||
minimal,
|
||||
small,
|
||||
outlined,
|
||||
loading,
|
||||
large,
|
||||
active,
|
||||
disabled,
|
||||
icon,
|
||||
right_icon,
|
||||
intent,
|
||||
title,
|
||||
onclick,
|
||||
class,
|
||||
style,
|
||||
button_ref,
|
||||
children,
|
||||
aria_label,
|
||||
onclick,
|
||||
..
|
||||
} = props;
|
||||
|
||||
let disabled = *disabled || *loading;
|
||||
|
||||
html! {
|
||||
<button
|
||||
class={classes!(
|
||||
"bp3-button",
|
||||
fill.then_some("bp3-fill"),
|
||||
minimal.then_some("bp3-minimal"),
|
||||
small.then_some("bp3-small"),
|
||||
outlined.then_some("bp3-outlined"),
|
||||
loading.then_some("bp3-loading"),
|
||||
large.then_some("bp3-large"),
|
||||
(*active && !disabled).then_some("bp3-active"),
|
||||
disabled.then_some("bp3-disabled"),
|
||||
intent,
|
||||
class.clone(),
|
||||
)}
|
||||
class={props.common_classes()}
|
||||
{style}
|
||||
{title}
|
||||
aria-label={aria_label}
|
||||
onclick={(!disabled).then_some(onclick.clone())}
|
||||
ref={button_ref.clone()}
|
||||
>
|
||||
{
|
||||
loading
|
||||
.then(|| html! {
|
||||
<Spinner
|
||||
class={classes!("bp3-button-spinner")}
|
||||
size={IconSize::LARGE}
|
||||
/>
|
||||
})
|
||||
}
|
||||
<Icon {icon} />
|
||||
{
|
||||
(!children.is_empty())
|
||||
.then(|| html! {
|
||||
<span class="bp3-button-text">
|
||||
{for children.iter()}
|
||||
</span>
|
||||
})
|
||||
}
|
||||
<Icon icon={right_icon} />
|
||||
{props.render_children()}
|
||||
</button>
|
||||
}
|
||||
}
|
||||
|
||||
generate_with_common_props! {
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct AnchorButtonProps {
|
||||
#[prop_or_default]
|
||||
pub href: AttrValue,
|
||||
#[prop_or_default]
|
||||
pub target: Option<AttrValue>,
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(AnchorButton)]
|
||||
pub fn anchor_button(props: &AnchorButtonProps) -> Html {
|
||||
let AnchorButtonProps {
|
||||
loading,
|
||||
disabled,
|
||||
title,
|
||||
style,
|
||||
button_ref,
|
||||
aria_label,
|
||||
href,
|
||||
target,
|
||||
..
|
||||
} = props;
|
||||
|
||||
let disabled = *disabled || *loading;
|
||||
|
||||
html! {
|
||||
<a
|
||||
class={props.common_classes()}
|
||||
{style}
|
||||
{title}
|
||||
aria-label={aria_label}
|
||||
href={(!disabled).then_some(href.clone())}
|
||||
{target}
|
||||
ref={button_ref.clone()}
|
||||
>
|
||||
{props.render_children()}
|
||||
</a>
|
||||
}
|
||||
}
|
||||
|
|
221
src/dialog.rs
Normal file
221
src/dialog.rs
Normal file
|
@ -0,0 +1,221 @@
|
|||
use crate::{Button, Icon, IconSize, Overlay, H4};
|
||||
use std::cell::Cell;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Dialog {
|
||||
title_id: AttrValue,
|
||||
cb: MsgCallbacks<Self>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Properties)]
|
||||
pub struct DialogProps {
|
||||
#[prop_or_default]
|
||||
pub dark: Option<bool>,
|
||||
#[prop_or_default]
|
||||
pub class: Classes,
|
||||
#[prop_or_default]
|
||||
pub style: Option<AttrValue>,
|
||||
#[prop_or_default]
|
||||
pub open: bool,
|
||||
#[prop_or_default]
|
||||
pub onclose: Callback<()>,
|
||||
#[prop_or(true)]
|
||||
pub show_close_button: bool,
|
||||
#[prop_or_default]
|
||||
pub icon: Option<Icon>,
|
||||
#[prop_or_default]
|
||||
pub title: Option<Html>,
|
||||
#[prop_or_default]
|
||||
pub container_ref: NodeRef,
|
||||
#[prop_or_default]
|
||||
pub aria_labelledby: Option<AttrValue>,
|
||||
#[prop_or_default]
|
||||
pub aria_describedby: Option<AttrValue>,
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
#[derive(yew_callbacks::Callbacks)]
|
||||
pub enum Msg {
|
||||
OnClose(MouseEvent),
|
||||
}
|
||||
|
||||
impl Component for Dialog {
|
||||
type Properties = DialogProps;
|
||||
type Message = Msg;
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
thread_local! {
|
||||
static ID: Cell<usize> = Default::default();
|
||||
}
|
||||
let id = ID.with(|x| {
|
||||
let next = x.get().wrapping_add(1);
|
||||
x.replace(next)
|
||||
});
|
||||
let title_id = AttrValue::from(format!("title-bp-dialog-{id}"));
|
||||
|
||||
Self {
|
||||
title_id,
|
||||
cb: ctx.link().into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::OnClose(_event) => {
|
||||
ctx.props().onclose.emit(());
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let Self::Properties {
|
||||
dark,
|
||||
class,
|
||||
style,
|
||||
open,
|
||||
onclose,
|
||||
show_close_button,
|
||||
icon,
|
||||
title,
|
||||
container_ref,
|
||||
aria_labelledby,
|
||||
aria_describedby,
|
||||
children,
|
||||
} = ctx.props();
|
||||
|
||||
let aria_labelledby = aria_labelledby
|
||||
.clone()
|
||||
.or(title.is_some().then(|| self.title_id.clone()));
|
||||
|
||||
let close_button = show_close_button.then(|| {
|
||||
html! {
|
||||
<Button
|
||||
aria_label="Close"
|
||||
class={classes!("bp3-dialog-close-button")}
|
||||
left_element={html!(<Icon icon={Icon::SmallCross} size={IconSize::LARGE} />)}
|
||||
minimal=true
|
||||
onclick={self.cb.on_close()}
|
||||
/>
|
||||
}
|
||||
});
|
||||
|
||||
let header = title.clone().map(|title| {
|
||||
html! {
|
||||
<div class={classes!("bp3-dialog-header")}>
|
||||
<Icon {icon} size={IconSize::LARGE} aria_hidden=true tab_index={-1} />
|
||||
<H4 id={&self.title_id}>{title}</H4>
|
||||
{close_button}
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
html! {
|
||||
<Overlay
|
||||
{container_ref}
|
||||
{dark}
|
||||
class={classes!("bp3-dialog-container")}
|
||||
{open}
|
||||
{onclose}
|
||||
scrollable=true
|
||||
backdrop=true
|
||||
>
|
||||
<div
|
||||
class={classes!("bp3-dialog", class.clone())}
|
||||
role="dialog"
|
||||
aria-labelledby={aria_labelledby}
|
||||
aria-describedby={aria_describedby}
|
||||
{style}
|
||||
>
|
||||
{header}
|
||||
{for children.iter()}
|
||||
</div>
|
||||
</Overlay>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct DialogFooterProps {
|
||||
#[prop_or_default]
|
||||
pub class: Classes,
|
||||
#[prop_or_default]
|
||||
pub style: Option<AttrValue>,
|
||||
#[prop_or(false)]
|
||||
pub minimal: bool,
|
||||
#[prop_or_default]
|
||||
pub actions: Option<Html>,
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
#[function_component(DialogFooter)]
|
||||
pub fn dialog_footer(props: &DialogFooterProps) -> Html {
|
||||
let DialogFooterProps {
|
||||
class,
|
||||
style,
|
||||
minimal,
|
||||
actions,
|
||||
children,
|
||||
} = props;
|
||||
|
||||
let actions_html = actions.clone().map(|html| {
|
||||
html! {
|
||||
<div class="bp3-dialog-footer-actions">{html}</div>
|
||||
}
|
||||
});
|
||||
|
||||
html! {
|
||||
<div
|
||||
class={classes!(
|
||||
"bp3-dialog-footer",
|
||||
(!minimal).then_some("bp3-dialog-footer-fixed"),
|
||||
class.clone(),
|
||||
)}
|
||||
{style}
|
||||
>
|
||||
<div class="bp3-dialog-footer-main-section">
|
||||
{for children.iter()}
|
||||
</div>
|
||||
{actions_html}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct DialogBodyProps {
|
||||
#[prop_or_default]
|
||||
pub class: Classes,
|
||||
#[prop_or_default]
|
||||
pub style: Option<AttrValue>,
|
||||
#[prop_or(true)]
|
||||
pub use_overflow_scroll_container: bool,
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
#[function_component(DialogBody)]
|
||||
pub fn dialog_body(props: &DialogBodyProps) -> Html {
|
||||
let DialogBodyProps {
|
||||
class,
|
||||
style,
|
||||
use_overflow_scroll_container,
|
||||
children,
|
||||
} = props;
|
||||
|
||||
html! {
|
||||
<div
|
||||
role="dialogbody"
|
||||
class={classes!(
|
||||
"bp3-dialog-body",
|
||||
use_overflow_scroll_container.then_some("bp3-dialog-body-scroll-container"),
|
||||
class.clone(),
|
||||
)}
|
||||
{style}
|
||||
>
|
||||
{for children.iter()}
|
||||
</div>
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@ use yew::prelude::*;
|
|||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ChildrenOnlyProps {
|
||||
#[prop_or_default]
|
||||
pub id: Option<AttrValue>,
|
||||
#[prop_or_default]
|
||||
pub class: Classes,
|
||||
#[prop_or_default]
|
||||
|
@ -11,9 +13,15 @@ pub struct ChildrenOnlyProps {
|
|||
macro_rules! build_component {
|
||||
($name:ident, $tag:tt, $class:literal) => {
|
||||
#[function_component($name)]
|
||||
pub fn $tag(ChildrenOnlyProps { class, children }: &ChildrenOnlyProps) -> Html {
|
||||
pub fn $tag(
|
||||
ChildrenOnlyProps {
|
||||
id,
|
||||
class,
|
||||
children,
|
||||
}: &ChildrenOnlyProps,
|
||||
) -> Html {
|
||||
html! {
|
||||
<$tag class={classes!($class, class.clone())}>
|
||||
<$tag {id} class={classes!($class, class.clone())}>
|
||||
{children.clone()}
|
||||
</$tag>
|
||||
}
|
||||
|
|
16
src/icon.rs
16
src/icon.rs
|
@ -73,6 +73,12 @@ impl IntoPropValue<f64> for IconSize {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoPropValue<IconSize> for Option<IconSize> {
|
||||
fn into_prop_value(self) -> IconSize {
|
||||
self.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Icon {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
|
@ -97,6 +103,8 @@ impl Icon {
|
|||
intent,
|
||||
size,
|
||||
onclick,
|
||||
aria_hidden,
|
||||
tab_index,
|
||||
}: &IconProps,
|
||||
) -> Html {
|
||||
if let Icon::Custom(html) = icon {
|
||||
|
@ -117,10 +125,14 @@ impl Icon {
|
|||
let width = AttrValue::from(format!("{size}"));
|
||||
let height = width.clone();
|
||||
|
||||
let aria_hidden = aria_hidden.or(title.is_some().then_some(true));
|
||||
|
||||
html! {
|
||||
<span
|
||||
class={classes!("bp3-icon", class.clone(), intent)}
|
||||
{onclick}
|
||||
aria-hidden={aria_hidden.and_then(|x| x.then_some(AttrValue::Static("true")))}
|
||||
tabIndex={tab_index.map(|x| x.to_string())}
|
||||
>
|
||||
<svg
|
||||
{fill}
|
||||
|
@ -159,6 +171,10 @@ pub struct IconProps {
|
|||
pub size: IconSize,
|
||||
#[prop_or_default]
|
||||
pub onclick: Callback<MouseEvent>,
|
||||
#[prop_or_default]
|
||||
pub aria_hidden: Option<bool>,
|
||||
#[prop_or_default]
|
||||
pub tab_index: Option<isize>,
|
||||
}
|
||||
|
||||
impl Component for Icon {
|
||||
|
|
63
src/lib.rs
63
src/lib.rs
|
@ -5,9 +5,11 @@
|
|||
clippy::type_complexity,
|
||||
clippy::derive_partial_eq_without_eq,
|
||||
clippy::uninlined_format_args,
|
||||
clippy::derivable_impls
|
||||
clippy::derivable_impls,
|
||||
clippy::enum_variant_names
|
||||
)]
|
||||
|
||||
mod alert;
|
||||
mod button_group;
|
||||
mod buttons;
|
||||
mod callout;
|
||||
|
@ -15,6 +17,7 @@ mod card;
|
|||
mod checkbox;
|
||||
mod collapse;
|
||||
mod control_group;
|
||||
mod dialog;
|
||||
mod divider;
|
||||
mod html_elements;
|
||||
mod html_select;
|
||||
|
@ -38,6 +41,7 @@ mod text_area;
|
|||
#[cfg(feature = "tree")]
|
||||
mod tree;
|
||||
|
||||
pub use alert::*;
|
||||
pub use button_group::*;
|
||||
pub use buttons::*;
|
||||
pub use callout::*;
|
||||
|
@ -45,6 +49,7 @@ pub use card::*;
|
|||
pub use checkbox::*;
|
||||
pub use collapse::*;
|
||||
pub use control_group::*;
|
||||
pub use dialog::*;
|
||||
pub use divider::*;
|
||||
pub use html_elements::*;
|
||||
pub use html_select::*;
|
||||
|
@ -71,6 +76,8 @@ pub use text_area::*;
|
|||
pub use tree::*;
|
||||
|
||||
use implicit_clone::ImplicitClone;
|
||||
use std::cell::Cell;
|
||||
use yew::classes;
|
||||
use yew::Classes;
|
||||
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
|
||||
|
@ -180,3 +187,57 @@ impl From<&Elevation> for Classes {
|
|||
Self::from(*elevation)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dark;
|
||||
|
||||
impl Dark {
|
||||
pub fn with<T>(&self, f: impl FnOnce(&Cell<bool>) -> T) -> T {
|
||||
thread_local! {
|
||||
static DARK: Cell<bool> = {
|
||||
Cell::new(web_sys::window()
|
||||
.and_then(|x| x.match_media("(prefers-color-scheme: dark)").ok().flatten())
|
||||
.map(|x| x.matches())
|
||||
.unwrap_or(true))
|
||||
}
|
||||
}
|
||||
DARK.with(f)
|
||||
}
|
||||
|
||||
pub fn get(&self) -> bool {
|
||||
self.with(|x| x.get())
|
||||
}
|
||||
|
||||
pub fn set(&self, value: bool) {
|
||||
self.with(|x| x.set(value))
|
||||
}
|
||||
|
||||
pub fn replace(&self, value: bool) -> bool {
|
||||
self.with(|x| x.replace(value))
|
||||
}
|
||||
|
||||
pub fn toggle(&self) -> bool {
|
||||
self.with(|x| {
|
||||
let value = x.get();
|
||||
x.set(!value);
|
||||
value
|
||||
})
|
||||
}
|
||||
|
||||
pub fn classes(&self) -> Classes {
|
||||
self.classes_with_override(None)
|
||||
}
|
||||
|
||||
pub fn classes_with_override(&self, force: impl Into<Option<bool>>) -> Classes {
|
||||
if force.into().unwrap_or(self.get()) {
|
||||
thread_local! {
|
||||
static CLASSES: Classes = classes!("bp3-dark");
|
||||
}
|
||||
CLASSES.with(|x| x.clone())
|
||||
} else {
|
||||
thread_local! {
|
||||
static CLASSES: Classes = classes!();
|
||||
}
|
||||
CLASSES.with(|x| x.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::Portal;
|
||||
use crate::{Dark, Portal};
|
||||
use gloo::timers::callback::Timeout;
|
||||
use wasm_bindgen::closure::Closure;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
@ -25,10 +25,15 @@ pub struct Overlay {
|
|||
|
||||
#[derive(Debug, PartialEq, Properties)]
|
||||
pub struct OverlayProps {
|
||||
#[prop_or_default]
|
||||
pub dark: Option<bool>,
|
||||
#[prop_or_default]
|
||||
pub class: Classes,
|
||||
#[prop_or_default]
|
||||
pub style: Option<AttrValue>,
|
||||
// NOTE: this should have been false by default
|
||||
#[prop_or(true)]
|
||||
pub scrollable: bool,
|
||||
#[prop_or_default]
|
||||
pub open: bool,
|
||||
#[prop_or(true)]
|
||||
|
@ -36,6 +41,8 @@ pub struct OverlayProps {
|
|||
#[prop_or_default]
|
||||
pub onclose: Callback<()>,
|
||||
#[prop_or_default]
|
||||
pub container_ref: NodeRef,
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
|
@ -183,11 +190,14 @@ impl Component for Overlay {
|
|||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let Self::Properties {
|
||||
dark,
|
||||
class,
|
||||
style,
|
||||
scrollable,
|
||||
open,
|
||||
backdrop,
|
||||
onclose: _,
|
||||
container_ref,
|
||||
children,
|
||||
} = ctx.props();
|
||||
|
||||
|
@ -230,10 +240,12 @@ impl Component for Overlay {
|
|||
html! {
|
||||
<Portal>
|
||||
<div
|
||||
ref={container_ref}
|
||||
class={classes!(
|
||||
"bp3-overlay",
|
||||
"bp3-overlay-scroll-container",
|
||||
scrollable.then_some("bp3-overlay-scroll-container"),
|
||||
open.then_some("bp3-overlay-open"),
|
||||
Dark.classes_with_override(*dark),
|
||||
)}
|
||||
aria-live="polite"
|
||||
onkeydown={ctx.link().callback(Msg::OnKeyDown)}
|
||||
|
|
106
yewprint-doc/src/alert/example.rs
Normal file
106
yewprint-doc/src/alert/example.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use gloo::timers::callback::Timeout;
|
||||
use yew::prelude::*;
|
||||
use yewprint::{Alert, Button, Icon, Intent};
|
||||
|
||||
pub struct Example {
|
||||
open_error: bool,
|
||||
open_deletion: bool,
|
||||
loading: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct ExampleProps {
|
||||
pub will_load: bool,
|
||||
}
|
||||
|
||||
pub enum Msg {
|
||||
OpenFileError,
|
||||
OpenFileDeletion,
|
||||
Close(bool),
|
||||
FinishClosing,
|
||||
}
|
||||
|
||||
impl Component for Example {
|
||||
type Message = Msg;
|
||||
type Properties = ExampleProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Example {
|
||||
open_error: false,
|
||||
open_deletion: false,
|
||||
loading: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
let Self::Properties { will_load, .. } = *ctx.props();
|
||||
|
||||
match msg {
|
||||
Msg::OpenFileError => {
|
||||
self.loading = false;
|
||||
self.open_error = true;
|
||||
}
|
||||
Msg::OpenFileDeletion => {
|
||||
self.loading = false;
|
||||
self.open_deletion = true;
|
||||
}
|
||||
Msg::Close(res) => {
|
||||
if res {
|
||||
if will_load {
|
||||
self.loading = true;
|
||||
let callback = ctx.link().callback(|()| Msg::FinishClosing);
|
||||
Timeout::new(2000, move || callback.emit(())).forget();
|
||||
} else {
|
||||
self.open_error = false;
|
||||
self.open_deletion = false;
|
||||
}
|
||||
} else {
|
||||
self.open_error = false;
|
||||
self.open_deletion = false;
|
||||
}
|
||||
}
|
||||
Msg::FinishClosing => {
|
||||
self.open_error = false;
|
||||
self.open_deletion = false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<>
|
||||
<Button
|
||||
onclick={ctx.link().callback(|_| Msg::OpenFileError)}
|
||||
>
|
||||
{"Open file error alert"}
|
||||
</Button>
|
||||
<Alert
|
||||
open={self.open_error}
|
||||
onclose={ctx.link().callback(|res| Msg::Close(res))}
|
||||
loading={self.loading}
|
||||
>
|
||||
<p>{"Couldn't create the file because the containing folder doesn't \
|
||||
exist anymore. You will be assimilated."}</p>
|
||||
</Alert>
|
||||
<Button
|
||||
onclick={ctx.link().callback(|_| Msg::OpenFileDeletion)}
|
||||
>
|
||||
{"Open file deletion alert"}
|
||||
</Button>
|
||||
<Alert
|
||||
open={self.open_deletion}
|
||||
onclose={ctx.link().callback(|res| Msg::Close(res))}
|
||||
loading={self.loading}
|
||||
icon={Icon::Trash}
|
||||
intent={Intent::Danger}
|
||||
confirm_button={html!("Move to Trash")}
|
||||
cancel_button={html!("Cancel")}
|
||||
>
|
||||
<p>{"Are you sure you want to move "}<b>{"filename"}</b>{" to Trash? \
|
||||
You will be able to restore it later, but it will become Borg."}</p>
|
||||
</Alert>
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
79
yewprint-doc/src/alert/mod.rs
Normal file
79
yewprint-doc/src/alert/mod.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
mod example;
|
||||
|
||||
use crate::ExampleContainer;
|
||||
use example::*;
|
||||
use yew::prelude::*;
|
||||
use yewprint::{Switch, H1, H5};
|
||||
|
||||
pub struct AlertDoc {
|
||||
callback: Callback<ExampleProps>,
|
||||
state: ExampleProps,
|
||||
}
|
||||
|
||||
impl Component for AlertDoc {
|
||||
type Message = ExampleProps;
|
||||
type Properties = ();
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
AlertDoc {
|
||||
callback: ctx.link().callback(|x| x),
|
||||
state: ExampleProps { will_load: true },
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
self.state = msg;
|
||||
true
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
let example_props = self.state.clone();
|
||||
let source = crate::include_raw_html!(
|
||||
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
|
||||
"bp3-code-block"
|
||||
);
|
||||
|
||||
let props_component = html! {
|
||||
<AlertProps
|
||||
callback={self.callback.clone()}
|
||||
example_props={example_props.clone()}
|
||||
/>
|
||||
};
|
||||
|
||||
html! {
|
||||
<div>
|
||||
<H1 class={classes!("docs-title")}>{"Alert"}</H1>
|
||||
<SourceCodeUrl />
|
||||
<div>
|
||||
<ExampleContainer
|
||||
source={source}
|
||||
props={Some(props_component)}
|
||||
>
|
||||
<Example ..example_props />
|
||||
</ExampleContainer>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::build_example_prop_component! {
|
||||
AlertProps for ExampleProps =>
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<H5>{"Props"}</H5>
|
||||
<Switch
|
||||
onclick={self.update_props(ctx, |props, _| ExampleProps {
|
||||
will_load: !props.will_load,
|
||||
..props
|
||||
})}
|
||||
checked={ctx.props().example_props.will_load}
|
||||
label={html!("Does alert use loading state")}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::build_source_code_component!();
|
|
@ -1,3 +1,4 @@
|
|||
use crate::alert::*;
|
||||
use crate::button_group::*;
|
||||
use crate::buttons::*;
|
||||
use crate::callout::*;
|
||||
|
@ -5,6 +6,7 @@ use crate::card::*;
|
|||
use crate::checkbox::*;
|
||||
use crate::collapse::*;
|
||||
use crate::control_group::*;
|
||||
use crate::dialog::*;
|
||||
use crate::divider::*;
|
||||
use crate::html_select::*;
|
||||
use crate::icon::*;
|
||||
|
@ -23,10 +25,9 @@ use crate::tag::*;
|
|||
use crate::text::*;
|
||||
use crate::text_area::*;
|
||||
use crate::tree::*;
|
||||
use crate::DARK;
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
use yewprint::{Icon, Menu, MenuItem};
|
||||
use yewprint::{Dark, Icon, Menu, MenuItem};
|
||||
|
||||
#[function_component(AppRoot)]
|
||||
pub fn app_root() -> Html {
|
||||
|
@ -55,7 +56,7 @@ impl Component for App {
|
|||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::ToggleLight => {
|
||||
DARK.with(|x| x.replace(!x.get()));
|
||||
Dark.toggle();
|
||||
}
|
||||
Msg::GoToMenu(event, doc_menu) => {
|
||||
event.prevent_default();
|
||||
|
@ -70,7 +71,7 @@ impl Component for App {
|
|||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let dark = DARK.with(|x| x.get());
|
||||
let dark = Dark.get();
|
||||
|
||||
let netlify_badge = if dark {
|
||||
"https://www.netlify.com/img/global/badges/netlify-color-accent.svg"
|
||||
|
@ -88,6 +89,12 @@ impl Component for App {
|
|||
.callback(|_| Msg::ToggleLight)}
|
||||
icon={go_to_theme_icon}
|
||||
/>
|
||||
<MenuItem
|
||||
text={html!("Alert")}
|
||||
href="/alert"
|
||||
onclick={ctx.link()
|
||||
.callback(|e| Msg::GoToMenu(e, DocMenu::Alert))}
|
||||
/>
|
||||
<MenuItem
|
||||
text={html!("Button")}
|
||||
href="/button"
|
||||
|
@ -130,6 +137,12 @@ impl Component for App {
|
|||
onclick={ctx.link()
|
||||
.callback(|e| Msg::GoToMenu(e, DocMenu::ControlGroup))}
|
||||
/>
|
||||
<MenuItem
|
||||
text={html!("Dialog")}
|
||||
href="/dialog"
|
||||
onclick={ctx.link()
|
||||
.callback(|e| Msg::GoToMenu(e, DocMenu::Dialog))}
|
||||
/>
|
||||
<MenuItem
|
||||
text={html!("Divider")}
|
||||
href="/divider"
|
||||
|
@ -276,7 +289,7 @@ impl Component for App {
|
|||
};
|
||||
|
||||
html! {
|
||||
<div class={classes!("docs-root", dark.then_some("bp3-dark"))}>
|
||||
<div class={classes!("docs-root", Dark.classes())}>
|
||||
<div class={classes!("docs-app")}>
|
||||
{{ navigation }}
|
||||
<main class={classes!("docs-content-wrapper")} role="main">
|
||||
|
@ -292,6 +305,7 @@ impl Component for App {
|
|||
|
||||
fn switch(route: DocMenu) -> Html {
|
||||
match route {
|
||||
DocMenu::Alert => html! (<AlertDoc />),
|
||||
DocMenu::Button | DocMenu::Home => html! (<ButtonDoc />),
|
||||
DocMenu::ButtonGroup => html! (<ButtonGroupDoc />),
|
||||
DocMenu::Callout => html!(<CalloutDoc />),
|
||||
|
@ -299,6 +313,7 @@ fn switch(route: DocMenu) -> Html {
|
|||
DocMenu::Checkbox => html!(<CheckboxDoc />),
|
||||
DocMenu::Collapse => html!(<CollapseDoc />),
|
||||
DocMenu::ControlGroup => html!(<ControlGroupDoc />),
|
||||
DocMenu::Dialog => html! (<DialogDoc />),
|
||||
DocMenu::Divider => html!(<DividerDoc />),
|
||||
DocMenu::HtmlSelect => html!(<HtmlSelectDoc />),
|
||||
DocMenu::Icon => html!(<IconDoc />),
|
||||
|
@ -322,6 +337,8 @@ fn switch(route: DocMenu) -> Html {
|
|||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Copy, Routable)]
|
||||
pub enum DocMenu {
|
||||
#[at("/alert")]
|
||||
Alert,
|
||||
#[at("/button-group")]
|
||||
ButtonGroup,
|
||||
#[at("/button")]
|
||||
|
@ -336,10 +353,12 @@ pub enum DocMenu {
|
|||
Collapse,
|
||||
#[at("/control-group")]
|
||||
ControlGroup,
|
||||
#[at("/html-select")]
|
||||
HtmlSelect,
|
||||
#[at("/dialog")]
|
||||
Dialog,
|
||||
#[at("/divider")]
|
||||
Divider,
|
||||
#[at("/html-select")]
|
||||
HtmlSelect,
|
||||
#[at("/icon")]
|
||||
Icon,
|
||||
#[at("/input-group")]
|
||||
|
|
177
yewprint-doc/src/dialog/example.rs
Normal file
177
yewprint-doc/src/dialog/example.rs
Normal file
|
@ -0,0 +1,177 @@
|
|||
use yew::prelude::*;
|
||||
use yewprint::{AnchorButton, Button, Dialog, DialogBody, DialogFooter, Icon, Intent};
|
||||
|
||||
pub struct Example {
|
||||
show_dialog: bool,
|
||||
show_dialog_with_title: bool,
|
||||
show_dialog_with_title_and_footer: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct ExampleProps {}
|
||||
|
||||
pub enum Msg {
|
||||
ShowDialog,
|
||||
ShowDialogWithTitle,
|
||||
ShowDialogWithTitleAndFooter,
|
||||
Close,
|
||||
}
|
||||
|
||||
impl Component for Example {
|
||||
type Message = Msg;
|
||||
type Properties = ExampleProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Example {
|
||||
show_dialog: false,
|
||||
show_dialog_with_title: false,
|
||||
show_dialog_with_title_and_footer: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::ShowDialog => {
|
||||
self.show_dialog = true;
|
||||
}
|
||||
Msg::ShowDialogWithTitle => {
|
||||
self.show_dialog_with_title = true;
|
||||
}
|
||||
Msg::ShowDialogWithTitleAndFooter => {
|
||||
self.show_dialog_with_title_and_footer = true;
|
||||
}
|
||||
Msg::Close => {
|
||||
self.show_dialog = false;
|
||||
self.show_dialog_with_title = false;
|
||||
self.show_dialog_with_title_and_footer = false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<>
|
||||
<Button
|
||||
onclick={ctx.link().callback(|_| Msg::ShowDialog)}
|
||||
>
|
||||
{"Show dialog"}
|
||||
</Button>
|
||||
<Dialog
|
||||
open={self.show_dialog}
|
||||
onclose={ctx.link().callback(|_| Msg::Close)}
|
||||
>
|
||||
<div style="margin: 0px 20px;">
|
||||
<div class="bp3-popover2-target" aria-haspopup="true">
|
||||
<InterestingDialogBody />
|
||||
<VisitChatGptWebsiteButton />
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
<Button
|
||||
onclick={ctx.link().callback(|_| Msg::ShowDialogWithTitle)}
|
||||
>
|
||||
{"Show dialog with title"}
|
||||
</Button>
|
||||
<Dialog
|
||||
open={self.show_dialog_with_title}
|
||||
onclose={ctx.link().callback(|_| Msg::Close)}
|
||||
title={html!("Embracing disruptive approach")}
|
||||
icon={Icon::InfoSign}
|
||||
>
|
||||
<div style="margin: 0px 20px;">
|
||||
<div class="bp3-popover2-target" aria-haspopup="true">
|
||||
<InterestingDialogBody />
|
||||
<VisitChatGptWebsiteButton />
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
<Button
|
||||
onclick={ctx.link().callback(|_| Msg::ShowDialogWithTitleAndFooter)}
|
||||
>
|
||||
{"Show dialog with title and footer"}
|
||||
</Button>
|
||||
<Dialog
|
||||
open={self.show_dialog_with_title_and_footer}
|
||||
onclose={ctx.link().callback(|_| Msg::Close)}
|
||||
title={html!("Embracing disruptive approach")}
|
||||
icon={Icon::InfoSign}
|
||||
>
|
||||
<InterestingDialogBody />
|
||||
<DialogFooter
|
||||
actions={html! {
|
||||
<>
|
||||
<Button onclick={ctx.link().callback(|_| Msg::Close)}>
|
||||
{"Close"}
|
||||
</Button>
|
||||
<VisitChatGptWebsiteButton fill=false />
|
||||
</>
|
||||
}}
|
||||
/>
|
||||
</Dialog>
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(InterestingDialogBody)]
|
||||
pub fn interesting_content(_: &()) -> Html {
|
||||
html! {
|
||||
<DialogBody>
|
||||
<p><strong>{"Our transformative solution optimizes paperclip management \
|
||||
through advanced technology, revolutionizing office supply logistics and \
|
||||
driving operational efficiency."}</strong></p>
|
||||
<p>{"Leveraging synergistic ideation and cutting-edge paradigm shifts,
|
||||
our groundbreaking solution addresses the pervasive issue of inefficient \
|
||||
paperclip utilization. We recognize the urgent need to optimize paperclip
|
||||
distribution and deployment, revolutionizing the way organizations engage
|
||||
with this crucial office asset. By seamlessly integrating blockchain
|
||||
technology, artificial intelligence algorithms, and quantum-inspired
|
||||
computing, our transformative platform offers a paradigmatic shift in the
|
||||
paperclip management landscape."}</p>
|
||||
<p>{"Our innovative solution streamlines paperclip logistics through a \
|
||||
gamified interface that incentivizes employees to achieve maximum paperclip
|
||||
productivity. Leveraging the power of machine learning, our algorithm
|
||||
predicts demand patterns, ensuring optimal inventory levels and eliminating
|
||||
wasteful overstocking. Furthermore, our blockchain-based tracking system
|
||||
guarantees immutable traceability, empowering organizations to perform
|
||||
data-driven analysis on paperclip utilization, leading to unprecedented
|
||||
insights and cost-saving opportunities."}</p>
|
||||
<p>{"By embracing this disruptive approach, organizations can transcend \
|
||||
traditional paperclip constraints and unlock a new era of operational
|
||||
efficiency. Our solution not only resolves the current paperclip dilemma
|
||||
but also paves the way for a sustainable future, reducing environmental
|
||||
impact through our proprietary green paperclip technology. Join us in the
|
||||
paperclip revolution and embrace the future of office supplies where
|
||||
mundane tasks are transformed into innovative experiences that drive
|
||||
productivity and fuel growth."}</p>
|
||||
<p>{"Unlock the power of seamless paperclip management with our innovative
|
||||
solution - experience unparalleled efficiency and cost-saving opportunities
|
||||
for your organization. Try it today and revolutionize your office supply
|
||||
logistics!"}</p>
|
||||
</DialogBody>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct VisitChatGptWebsiteButtonProps {
|
||||
#[prop_or(true)]
|
||||
pub fill: bool,
|
||||
}
|
||||
|
||||
#[function_component(VisitChatGptWebsiteButton)]
|
||||
pub fn visit_chat_gpt_website_button(
|
||||
VisitChatGptWebsiteButtonProps { fill }: &VisitChatGptWebsiteButtonProps,
|
||||
) -> Html {
|
||||
html! {
|
||||
<AnchorButton
|
||||
href="https://chat.openai.com/"
|
||||
target="_blank"
|
||||
{fill}
|
||||
intent={Intent::Primary}
|
||||
icon={Icon::Share}
|
||||
>
|
||||
{"Generate all sorts of bs by visiting ChatGPT"}
|
||||
</AnchorButton>
|
||||
}
|
||||
}
|
42
yewprint-doc/src/dialog/mod.rs
Normal file
42
yewprint-doc/src/dialog/mod.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
mod example;
|
||||
|
||||
use crate::ExampleContainer;
|
||||
use example::*;
|
||||
use yew::prelude::*;
|
||||
use yewprint::H1;
|
||||
|
||||
pub struct DialogDoc {
|
||||
callback: Callback<ExampleProps>,
|
||||
}
|
||||
|
||||
impl Component for DialogDoc {
|
||||
type Message = ExampleProps;
|
||||
type Properties = ();
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
DialogDoc {
|
||||
callback: ctx.link().callback(|x| x),
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
let source = crate::include_raw_html!(
|
||||
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
|
||||
"bp3-code-block"
|
||||
);
|
||||
|
||||
html! {
|
||||
<div>
|
||||
<H1 class={classes!("docs-title")}>{"Dialog"}</H1>
|
||||
<SourceCodeUrl />
|
||||
<div>
|
||||
<ExampleContainer source={source}>
|
||||
<Example />
|
||||
</ExampleContainer>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::build_source_code_component!();
|
|
@ -8,6 +8,7 @@
|
|||
clippy::uninlined_format_args
|
||||
)]
|
||||
|
||||
mod alert;
|
||||
mod app;
|
||||
mod button_group;
|
||||
mod buttons;
|
||||
|
@ -16,6 +17,7 @@ mod card;
|
|||
mod checkbox;
|
||||
mod collapse;
|
||||
mod control_group;
|
||||
mod dialog;
|
||||
mod divider;
|
||||
mod example;
|
||||
mod html_select;
|
||||
|
@ -40,16 +42,6 @@ mod tree;
|
|||
pub use app::*;
|
||||
pub use example::*;
|
||||
pub use logo::*;
|
||||
use std::cell::Cell;
|
||||
|
||||
thread_local! {
|
||||
pub static DARK: Cell<bool> = {
|
||||
Cell::new(web_sys::window()
|
||||
.and_then(|x| x.match_media("(prefers-color-scheme: dark)").ok().flatten())
|
||||
.map(|x| x.matches())
|
||||
.unwrap_or(true))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! include_raw_html {
|
||||
|
|
|
@ -15,10 +15,6 @@ impl Component for MenuDoc {
|
|||
MenuDoc
|
||||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
let source = crate::include_raw_html!(
|
||||
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use crate::DARK;
|
||||
use yew::prelude::*;
|
||||
use yewprint::{Button, Card, Elevation, Icon, Intent, Overlay, H3};
|
||||
|
||||
pub struct Example {
|
||||
open: bool,
|
||||
tall: bool,
|
||||
show_button_ref: NodeRef,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
|
@ -27,7 +25,6 @@ impl Component for Example {
|
|||
Example {
|
||||
open: false,
|
||||
tall: false,
|
||||
show_button_ref: NodeRef::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +51,6 @@ impl Component for Example {
|
|||
<div>
|
||||
<Button
|
||||
onclick={ctx.link().callback(|_| Msg::Open)}
|
||||
button_ref={self.show_button_ref.clone()}
|
||||
>
|
||||
{"Show overlay"}
|
||||
</Button>
|
||||
|
@ -63,7 +59,6 @@ impl Component for Example {
|
|||
onclose={ctx.link().callback(|_| Msg::Close)}
|
||||
{backdrop}
|
||||
class={classes!(
|
||||
DARK.with(|x| x.get().then_some("bp3-dark")),
|
||||
self.tall.then_some("docs-overlay-example-tall"),
|
||||
)}
|
||||
style="left: calc(50vw - 200px); margin: 10vh 0; top: 0; width: 400px;"
|
||||
|
|
Loading…
Reference in a new issue