From 015fc99dce2537fd2ad9dd5f0226910a5c356907 Mon Sep 17 00:00:00 2001 From: Danielle Huisman Date: Fri, 10 May 2024 15:16:08 +0200 Subject: [PATCH] Add start of radix-leptos-menu --- packages/primitives/core/number/src/lib.rs | 4 + packages/primitives/core/number/src/number.rs | 3 + packages/primitives/leptos/menu/Cargo.toml | 14 ++ packages/primitives/leptos/menu/README.md | 13 ++ .../primitives/leptos/menu/example/Cargo.toml | 19 ++ .../primitives/leptos/menu/example/Trunk.toml | 7 + .../primitives/leptos/menu/example/index.html | 7 + .../primitives/leptos/menu/example/src/app.rs | 127 +++++++++++ .../leptos/menu/example/src/main.rs | 10 + .../leptos/menu/example/style/tailwind.css | 3 + .../leptos/menu/example/tailwind.config.js | 8 + packages/primitives/leptos/menu/src/lib.rs | 9 + packages/primitives/leptos/menu/src/menu.rs | 203 ++++++++++++++++++ .../primitives/leptos/popper/src/popper.rs | 4 +- 14 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 packages/primitives/core/number/src/number.rs create mode 100644 packages/primitives/leptos/menu/Cargo.toml create mode 100644 packages/primitives/leptos/menu/README.md create mode 100644 packages/primitives/leptos/menu/example/Cargo.toml create mode 100644 packages/primitives/leptos/menu/example/Trunk.toml create mode 100644 packages/primitives/leptos/menu/example/index.html create mode 100644 packages/primitives/leptos/menu/example/src/app.rs create mode 100644 packages/primitives/leptos/menu/example/src/main.rs create mode 100644 packages/primitives/leptos/menu/example/style/tailwind.css create mode 100644 packages/primitives/leptos/menu/example/tailwind.config.js create mode 100644 packages/primitives/leptos/menu/src/lib.rs create mode 100644 packages/primitives/leptos/menu/src/menu.rs diff --git a/packages/primitives/core/number/src/lib.rs b/packages/primitives/core/number/src/lib.rs index 1eab5f86..f4e8b73f 100644 --- a/packages/primitives/core/number/src/lib.rs +++ b/packages/primitives/core/number/src/lib.rs @@ -3,3 +3,7 @@ //! This is an internal utility, not intended for public usage. //! //! See [`@radix-ui/number`](https://www.npmjs.com/package/@radix-ui/number) for the original package. + +mod number; + +pub use number::*; diff --git a/packages/primitives/core/number/src/number.rs b/packages/primitives/core/number/src/number.rs new file mode 100644 index 00000000..69bdb49f --- /dev/null +++ b/packages/primitives/core/number/src/number.rs @@ -0,0 +1,3 @@ +pub fn clamp(value: f64, [min, max]: [f64; 2]) -> f64 { + value.max(min).min(max) +} diff --git a/packages/primitives/leptos/menu/Cargo.toml b/packages/primitives/leptos/menu/Cargo.toml new file mode 100644 index 00000000..71a0894b --- /dev/null +++ b/packages/primitives/leptos/menu/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "radix-leptos-menu" +description = "Leptos port of Radix Menu." + +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +leptos.workspace = true +radix-leptos-direction = { path = "../direction", version = "0.0.1" } +radix-leptos-popper = { path = "../popper", version = "0.0.1" } diff --git a/packages/primitives/leptos/menu/README.md b/packages/primitives/leptos/menu/README.md new file mode 100644 index 00000000..b31044e4 --- /dev/null +++ b/packages/primitives/leptos/menu/README.md @@ -0,0 +1,13 @@ +

+ + + +

+ +

radix-leptos-menu

+ +This is an internal utility, not intended for public usage. + +## Rust Radix + +[Rust Radix](https://github.com/NixySoftware/radix) is a Rust port of [Radix](https://www.radix-ui.com/primitives). diff --git a/packages/primitives/leptos/menu/example/Cargo.toml b/packages/primitives/leptos/menu/example/Cargo.toml new file mode 100644 index 00000000..7f54fd01 --- /dev/null +++ b/packages/primitives/leptos/menu/example/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "radix-leptos-menu-example" +description = "Example for Radix Leptos Menu." +publish = false + +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +console_log.workspace = true +console_error_panic_hook.workspace = true +leptos.workspace = true +log.workspace = true +radix-leptos-menu = { path = ".." } +tailwind_fuse.workspace = true +web-sys.workspace = true diff --git a/packages/primitives/leptos/menu/example/Trunk.toml b/packages/primitives/leptos/menu/example/Trunk.toml new file mode 100644 index 00000000..0f4b0a37 --- /dev/null +++ b/packages/primitives/leptos/menu/example/Trunk.toml @@ -0,0 +1,7 @@ +[[hooks]] +stage = "pre_build" +command = "sh" +command_arguments = [ + "-c", + "npx tailwindcss -i style/tailwind.css -o style/tailwind.output.css", +] diff --git a/packages/primitives/leptos/menu/example/index.html b/packages/primitives/leptos/menu/example/index.html new file mode 100644 index 00000000..f0e51f86 --- /dev/null +++ b/packages/primitives/leptos/menu/example/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/primitives/leptos/menu/example/src/app.rs b/packages/primitives/leptos/menu/example/src/app.rs new file mode 100644 index 00000000..0ab3a4ca --- /dev/null +++ b/packages/primitives/leptos/menu/example/src/app.rs @@ -0,0 +1,127 @@ +use leptos::*; +use radix_leptos_menu::*; +use tailwind_fuse::*; + +// TODO: add router and separate pages for each component, similar to Storybook + +#[component] +pub fn App() -> impl IntoView { + view! { +

Styled

+ +
+ +
+ } +} + +#[component] +fn Styled() -> impl IntoView { + let item_class = create_memo(move |_| ItemClass::default().to_class()); + + view! { + + + Undo + + + Redo + + + + Cut + + + Copy + + + Paste + + + } +} + +#[component] +fn Submenus() -> impl IntoView { + view! {} +} + +#[component] +fn WithLabels() -> impl IntoView { + view! {} +} + +#[component] +fn Typeahead() -> impl IntoView { + view! {} +} + +#[component] +fn CheckboxItems() -> impl IntoView { + view! {} +} + +#[component] +fn RadioItems() -> impl IntoView { + view! {} +} + +#[component] +fn Animated() -> impl IntoView { + view! {} +} + +#[component] +fn MenuWithAnchor( + #[prop(into, optional)] open: MaybeProp, + children: Children, +) -> impl IntoView { + let open = Signal::derive(move || open().unwrap_or(true)); + + let content_class = create_memo(move |_| ContentClass::default().to_class()); + + // TODO: add missing props + view! { + + {""} + + + {children()} + + + + } +} + +#[component] +fn Submenu() -> impl IntoView { + view! {} +} + +#[derive(TwClass, Default, Clone, Copy)] +#[tw( + class = "inline-block box-border min-w-[130px] bg-[#fff] border border-solid border-[#ccc] rounded-[6px] p-[5px] shadow-[0px_5px_10px_0px_rgba(0,0,0,0.1)] font-['apple-system, BlinkMacSystemFont, helvetica, arial, sans-serif'] text-[13px] focus-within:border-[#111]" +)] +pub struct ContentClass {} + +#[derive(TwClass, Default, Clone, Copy)] +#[tw( + class = "flex items-center justify-between leading-none cursor-default select-none whitespace-nowrap h-[25px] p-[0px_10px] text-[#ccc] rounded-[3px]" +)] +pub struct LabelClass {} + +#[derive(TwClass, Default, Clone, Copy)] +#[tw( + class = "flex items-center justify-between leading-none cursor-default select-none whitespace-nowrap h-[25px] p-[0px_10px] text-[#111] rounded-[3px] outline-none data-highlighted:bg-[#111] data-highlighted:text-[#fff] data-disabled:text-[#ccc]" +)] +pub struct ItemClass {} + +#[derive(TwClass, Default, Clone, Copy)] +#[tw( + class = "flex items-center justify-between leading-none cursor-default select-none whitespace-nowrap h-[25px] p-[0px_10px] text-[#111] rounded-[3px] outline-none data-highlighted:bg-[#111] data-highlighted:text-[#fff] data-disabled:text-[#ccc] [&:not([data-highlighted])[data-state=\"open\"]]:bg-[#ccc] [&:not([data-highlighted])[data-state=\"open\"]]:text-[#111]" +)] +pub struct SubTriggerClass {} + +#[derive(TwClass, Default, Clone, Copy)] +#[tw(class = "h-[1px] m-[5px_10px] bg-[#ccc]")] +pub struct SeparatorClass {} diff --git a/packages/primitives/leptos/menu/example/src/main.rs b/packages/primitives/leptos/menu/example/src/main.rs new file mode 100644 index 00000000..90613b00 --- /dev/null +++ b/packages/primitives/leptos/menu/example/src/main.rs @@ -0,0 +1,10 @@ +mod app; + +use crate::app::App; + +pub fn main() { + _ = console_log::init_with_level(log::Level::Debug); + console_error_panic_hook::set_once(); + + leptos::mount_to_body(App); +} diff --git a/packages/primitives/leptos/menu/example/style/tailwind.css b/packages/primitives/leptos/menu/example/style/tailwind.css new file mode 100644 index 00000000..b5c61c95 --- /dev/null +++ b/packages/primitives/leptos/menu/example/style/tailwind.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/packages/primitives/leptos/menu/example/tailwind.config.js b/packages/primitives/leptos/menu/example/tailwind.config.js new file mode 100644 index 00000000..acc8afcf --- /dev/null +++ b/packages/primitives/leptos/menu/example/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['*.html', './src/**/*.rs'], + theme: { + extend: {} + }, + plugins: [] +}; diff --git a/packages/primitives/leptos/menu/src/lib.rs b/packages/primitives/leptos/menu/src/lib.rs new file mode 100644 index 00000000..f6dd9180 --- /dev/null +++ b/packages/primitives/leptos/menu/src/lib.rs @@ -0,0 +1,9 @@ +//! Leptos port of [Radix Menu](https://www.radix-ui.com/primitives). +//! +//! This is an internal utility, not intended for public usage. +//! +//! See [`@radix-ui/react-menu`](https://www.npmjs.com/package/@radix-ui/react-menu) for the original package. + +mod menu; + +pub use menu::*; diff --git a/packages/primitives/leptos/menu/src/menu.rs b/packages/primitives/leptos/menu/src/menu.rs new file mode 100644 index 00000000..e575c140 --- /dev/null +++ b/packages/primitives/leptos/menu/src/menu.rs @@ -0,0 +1,203 @@ +// TODO: remove +#![allow(dead_code, unused_variables)] + +use leptos::{html::AnyElement, *}; +use radix_leptos_direction::{use_direction, Direction}; +use radix_leptos_popper::{Popper, PopperAnchor}; + +#[derive(Clone)] +struct MenuContextValue { + open: Signal, + content_ref: NodeRef, + // TODO: onOpenChange +} + +#[derive(Clone)] +struct MenuRootContextValue { + is_using_keyboard: Signal, + dir: Signal, + modal: Signal, + // TODO: onClose +} + +#[component] +pub fn Menu( + #[prop(into, optional)] open: MaybeProp, + #[prop(into, optional)] dir: MaybeProp, + #[prop(into, optional)] modal: MaybeProp, + // TODO: onOpenChange + children: Children, +) -> impl IntoView { + let open = Signal::derive(move || open().unwrap_or(false)); + let modal = Signal::derive(move || modal().unwrap_or(true)); + + // TODO: popper scope + let content_ref = create_node_ref::(); + let is_using_keyboard = create_rw_signal(false); + let direction = use_direction(dir); + + let context_value = MenuContextValue { open, content_ref }; + let root_context_value = MenuRootContextValue { + is_using_keyboard: is_using_keyboard.into(), + dir: direction, + modal, + }; + + create_effect(move |_| { + // TODO: event handlers + }); + + on_cleanup(move || { + // TODO: cleanup event handlers + }); + + view! { + + + + {children()} + + + + } +} + +#[component] +pub fn MenuAnchor( + // #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>, + children: Children, +) -> impl IntoView { + // TODO: popper scope + view! { + // {..attributes} + + {children()} + + } +} + +#[component] +pub fn MenuPortal(children: Children) -> impl IntoView { + // TODO: portal + // view! {} + children() +} + +#[component] +pub fn MenuContent( + #[prop(into, optional)] class: MaybeProp, + #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>, + children: Children, +) -> impl IntoView { + let root_context = expect_context::(); + + view! { + // TODO: wrapper components + // TODO: fix this + //
+ // {move || match (root_context.modal)() { + // true => view! { + // + // {children()} + // + // }, + // false => view! { + // + // {children()} + // + // } + // }} + //
+ } +} + +#[component] +fn MenuRootContentModal( + #[prop(into, optional)] class: MaybeProp, + #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>, + children: Children, +) -> impl IntoView { + view! {} +} + +#[component] +fn MenuRootContentNonModal( + #[prop(into, optional)] class: MaybeProp, + #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>, + children: Children, +) -> impl IntoView { + view! {} +} + +#[component] +fn MenuContentImpl() -> impl IntoView { + view! {} +} + +#[component] +pub fn MenuGroup() -> impl IntoView { + view! {} +} + +#[component] +pub fn MenuLabel() -> impl IntoView { + view! {} +} + +#[component] +pub fn MenuItem( + #[prop(into, optional)] class: MaybeProp, + children: Children, +) -> impl IntoView { + view! {} +} + +#[component] +fn MenuItemImpl() -> impl IntoView { + view! {} +} + +#[component] +pub fn MenuCheckboxItem() -> impl IntoView { + view! {} +} + +#[component] +pub fn MenuRadioGroup() -> impl IntoView { + view! {} +} + +#[component] +pub fn MenuRadioItem() -> impl IntoView { + view! {} +} + +#[component] +pub fn MenuItemIndicator() -> impl IntoView { + view! {} +} + +#[component] +pub fn MenuSeparator() -> impl IntoView { + view! {} +} + +#[component] +pub fn MenuArrow() -> impl IntoView { + view! {} +} + +#[component] +pub fn MenuSub() -> impl IntoView { + view! {} +} + +#[component] +pub fn MenuSubTrigger() -> impl IntoView { + view! {} +} + +#[component] +pub fn MenuSubContent() -> impl IntoView { + view! {} +} diff --git a/packages/primitives/leptos/popper/src/popper.rs b/packages/primitives/leptos/popper/src/popper.rs index 3d0a30c4..980379a1 100644 --- a/packages/primitives/leptos/popper/src/popper.rs +++ b/packages/primitives/leptos/popper/src/popper.rs @@ -74,7 +74,7 @@ pub fn Popper(children: Children) -> impl IntoView { #[component] pub fn PopperAnchor( - #[prop(into, optional)] class: MaybeSignal, + #[prop(into, optional)] class: MaybeProp, #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>, children: Children, ) -> impl IntoView { @@ -110,7 +110,7 @@ pub fn PopperContent( #[prop(into, optional)] sticky: MaybeProp, #[prop(into, optional)] hide_when_detached: MaybeProp, #[prop(into, optional)] update_position_strategy: MaybeProp, - #[prop(into, optional)] class: MaybeSignal, + #[prop(into, optional)] class: MaybeProp, #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>, children: Children, ) -> impl IntoView {