Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Visually Hidden to Leptos 0.7 #429

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 84 additions & 22 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,34 +1,56 @@
[workspace]
# Temporarily disabled to upgrade individual packages to Leptos 0.7.
# members = [
# "book-examples/*/*",
# "packages/colors",
# "packages/icons/*",
# "packages/primitives/*/*",
# "packages/themes/*",
# "scripts",
# "stories/*",
# ]
# Temporarily disabled subcrates to be upgraded individually to Leptos 0.7.
# Once a crate is ready, uncomment it here.
members = [
"book-examples/*/*",
"packages/colors",
"packages/icons/*",
"packages/primitives/leptos/accessible-icon",
"packages/primitives/leptos/arrow",
"packages/primitives/leptos/aspect-ratio",
"packages/primitives/leptos/direction",
"packages/primitives/leptos/id",
"packages/primitives/leptos/label",
"packages/primitives/leptos/use-controllable-state",
"packages/primitives/leptos/use-escape-keydown",
"packages/primitives/leptos/use-previous",
"packages/primitives/leptos/use-size",
"packages/icons/dioxus",
"packages/icons/yew",

# -- Leptos Primitives (commented until they're upgraded) --
# "packages/primitives/leptos/accessible-icon",
# "packages/primitives/leptos/arrow",
# "packages/primitives/leptos/aspect-ratio",
# "packages/primitives/leptos/avatar",
# "packages/primitives/leptos/checkbox",
# "packages/primitives/leptos/collection",
# "packages/primitives/leptos/compose-refs",
# "packages/primitives/leptos/direction",
# "packages/primitives/leptos/dismissable-layer",
# "packages/primitives/leptos/dropdown-menu",
# "packages/primitives/leptos/focus-guards",
# "packages/primitives/leptos/focus-scope",
# "packages/primitives/leptos/id",
# "packages/primitives/leptos/label",
# "packages/primitives/leptos/menu",
# "packages/primitives/leptos/popover",
# "packages/primitives/leptos/popper",
# "packages/primitives/leptos/portal",
# "packages/primitives/leptos/presence",
"packages/primitives/leptos/primitive",
# "packages/primitives/leptos/progress",
# "packages/primitives/leptos/roving-focus",
# "packages/primitives/leptos/select",
# "packages/primitives/leptos/separator",
# "packages/primitives/leptos/slot",
# "packages/primitives/leptos/switch",
# "packages/primitives/leptos/tabs",
# "packages/primitives/leptos/toggle",
# "packages/primitives/leptos/use-controllable-state",
# "packages/primitives/leptos/use-escape-keydown",
# "packages/primitives/leptos/use-previous",
# "packages/primitives/leptos/use-size",
"packages/primitives/leptos/visually-hidden",

# -- Yew Primitives --
"packages/primitives/yew/*",

# -- Themes, Scripts, and Stories --
"packages/themes/yew",
"scripts",
"stories/*",
]

resolver = "2"

[workspace.package]
Expand All @@ -39,14 +61,17 @@ repository = "https://github.com/RustForWeb/radix"
version = "0.0.2"

[workspace.dependencies]
console_log = "1.0.0"
console_error_panic_hook = "0.1.7"
console_log = "1.0.0"
dioxus = "0.6.1"
leptos = "0.7.2"
leptos_dom = "0.7.2"
leptos_router = "0.7.2"
leptos-node-ref = "0.0.3"
leptos-maybe-callback = "0.0.3"
leptos-style = "0.0.3"
leptos-typed-fallback-show = "0.0.3"
leptos-use = "0.15.2"
log = "0.4.22"
send_wrapper = "0.6.0"
serde = "1.0.198"
Expand All @@ -58,6 +83,43 @@ yew-router = "0.18.0"
yew-struct-component = "0.1.4"
yew-style = "0.1.4"

# Subcrate packages (paths remain the same; you can comment out any subcrate not yet upgraded).
# We centralize shared dependencies in [workspace.dependencies] for a single source of truth,
# reducing duplication, preventing version drift, and keeping the Cargo.lock consistent.
#radix-leptos-arrow.path = "./packages/primitives/leptos/arrow"
#radix-leptos-aspect-ratio.path = "./packages/primitives/leptos/aspect-ratio"
#radix-leptos-accessible-icon.path = "./packages/primitives/leptos/accessible-icon"
#radix-leptos-avatar.path = "./packages/primitives/leptos/avatar"
#radix-leptos-checkbox.path = "./packages/primitives/leptos/checkbox"
#radix-leptos-collection.path = "./packages/primitives/leptos/collection"
#radix-leptos-compose-refs.path = "./packages/primitives/leptos/compose-refs"
#radix-leptos-direction.path = "./packages/primitives/leptos/direction"
#radix-leptos-dismissable-layer.path = "./packages/primitives/leptos/dismissable-layer"
#radix-leptos-dropdown-menu.path = "./packages/primitives/leptos/dropdown-menu"
#radix-leptos-focus-guards.path = "./packages/primitives/leptos/focus-guards"
#radix-leptos-focus-scope.path = "./packages/primitives/leptos/focus-scope"
#radix-leptos-id.path = "./packages/primitives/leptos/id"
#radix-leptos-label.path = "./packages/primitives/leptos/label"
#radix-leptos-menu.path = "./packages/primitives/leptos/menu"
#radix-leptos-popper.path = "./packages/primitives/leptos/popper"
#radix-leptos-portal.path = "./packages/primitives/leptos/portal"
#radix-leptos-presence.path = "./packages/primitives/leptos/presence"
radix-leptos-primitive.path = "./packages/primitives/leptos/primitive"
#radix-leptos-progress.path = "./packages/primitives/leptos/progress"
#radix-leptos-roving-focus.path = "./packages/primitives/leptos/roving-focus"
#radix-leptos-select.path = "./packages/primitives/leptos/select"
#radix-leptos-separator.path = "./packages/primitives/leptos/separator"
#radix-leptos-slot.path = "./packages/primitives/leptos/slot"
#radix-leptos-switch.path = "./packages/primitives/leptos/switch"
#radix-leptos-tabs.path = "./packages/primitives/leptos/tabs"
#radix-leptos-toggle.path = "./packages/primitives/leptos/toggle"
#radix-leptos-use-controllable-state.path = "./packages/primitives/leptos/use-controllable-state"
#radix-leptos-use-escape-keydown.path = "./packages/primitives/leptos/use-escape-keydown"
#radix-leptos-use-previous.path = "./packages/primitives/leptos/use-previous"
#radix-leptos-use-size.path = "./packages/primitives/leptos/use-size"
radix-leptos-visually-hidden.path = "./packages/primitives/leptos/visually-hidden"

[patch.crates-io]
yew = { git = "https://github.com/RustForWeb/yew.git", branch = "feature/use-composed-ref" }
yew-router = { git = "https://github.com/RustForWeb/yew.git", branch = "feature/use-composed-ref" }
leptos-node-ref = { git = "https://github.com/geoffreygarrett/leptos-utils", branch = "feature/any-node-ref" }
58 changes: 42 additions & 16 deletions book/src/primitives/utilities/visually-hidden.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Hides content from the screen in an accessible way.

## Features

- Visually hides content while preserving it for assistive technology.
- Visually hides content while preserving it for assistive technology.

## Installation

Expand All @@ -17,9 +17,9 @@ Install the component from your command line.
cargo add radix-leptos-visually-hidden
```

- [View on crates.io](https://crates.io/crates/radix-leptos-visually-hidden)
- [View on docs.rs](https://docs.rs/radix-leptos-visually-hidden/latest/radix_leptos_visually_hidden/)
- [View source](https://github.com/RustForWeb/radix/tree/main/packages/primitives/leptos/visually-hidden)
- [View on crates.io](https://crates.io/crates/radix-leptos-visually-hidden)
- [View on docs.rs](https://docs.rs/radix-leptos-visually-hidden/latest/radix_leptos_visually_hidden/)
- [View source](https://github.com/RustForWeb/radix/tree/main/packages/primitives/leptos/visually-hidden)

{{#endtab }}
{{#tab name="Yew" }}
Expand All @@ -28,9 +28,9 @@ cargo add radix-leptos-visually-hidden
cargo add radix-yew-visually-hidden
```

- [View on crates.io](https://crates.io/crates/radix-yew-visually-hidden)
- [View on docs.rs](https://docs.rs/radix-yew-visually-hidden/latest/radix_yew_visually_hidden/)
- [View source](https://github.com/RustForWeb/radix/tree/main/packages/primitives/yew/visually-hidden)
- [View on crates.io](https://crates.io/crates/radix-yew-visually-hidden)
- [View on docs.rs](https://docs.rs/radix-yew-visually-hidden/latest/radix_yew_visually_hidden/)
- [View source](https://github.com/RustForWeb/radix/tree/main/packages/primitives/yew/visually-hidden)

{{#endtab }}
{{#endtabs }}
Expand All @@ -49,7 +49,7 @@ use radix_leptos_visually_hidden::*;
#[component]
fn Anatomy() -> impl IntoView {
view! {
<VisuallyHidden />
<VisuallyHidden>"Your hidden content here"</VisuallyHidden>
}
}
```
Expand Down Expand Up @@ -81,14 +81,19 @@ Anything you put inside this component will be hidden from the screen but will b
{{#tabs global="framework" }}
{{#tab name="Leptos" }}

<i>No props.</i>
| Prop | Type | Default | Description |
|------------|--------------------------------------------|---------|---------------------------------------------------------------------------------|
| `children` | `TypedChildrenFn<impl IntoView + 'static>` | - | The content to be visually hidden but still accessible to screen readers |
| `as_child` | `MaybeProp<bool>` | `false` | If `true`, the `Primitive` is rendered as the child element rather than wrapped |
| `node_ref` | `AnyNodeRef` | - | A reference to the underlying DOM node |

{{#endtab }}
{{#tab name="Yew" }}

| Prop | Type | Default |
| ---------- | -------------------------------------------------- | ------- |
| `as_child` | `Option<Callback<VisuallyHiddenChildProps, Html>>` | - |
| Prop | Type | Default | Description |
|------------|----------------------------------------------------|---------|-------------|
| `as_child` | `Option<Callback<VisuallyHiddenChildProps, Html>>` | - | - |
| `children` | `TODO` | - | TODO |

{{#endtab }}
{{#endtabs }}
Expand Down Expand Up @@ -124,9 +129,9 @@ use radix_yew_icons::GearIcon;
use radix_yew_visually_hidden::*;
use yew::prelude::*;

#[component]
fn Example() -> impl IntoView {
view! {
#[function_component]
fn Example() -> Html {
html! {
<button>
<GearIcon />
<VisuallyHidden>{"Settings"}</VisuallyHidden>
Expand All @@ -142,6 +147,27 @@ fn Example() -> impl IntoView {

This is useful in certain scenarios as an alternative to traditional labelling with `aria-label` or `aria-labelledby`.

## Implementation Details

The component uses the following CSS properties to hide content visually while keeping it accessible to screen readers:

```css
.visually-hidden {
position: absolute;
border: 0;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
word-wrap: normal;
}
```

These styles are based on Bootstrap's visually-hidden implementation.

## See Also

- [Radix documentation](https://www.radix-ui.com/primitives/docs/utilities/visually-hidden)
- [Radix documentation](https://www.radix-ui.com/primitives/docs/utilities/visually-hidden)
14 changes: 14 additions & 0 deletions packages/primitives/leptos/primitive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "radix-leptos-primitive"
description = "Leptos port of Radix Primitive."

authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
version.workspace = true

[dependencies]
leptos.workspace = true
leptos-node-ref.workspace = true
leptos-typed-fallback-show.workspace = true
98 changes: 98 additions & 0 deletions packages/primitives/leptos/primitive/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@

<p align="center">
<a href="../../../../logo.svg">
<img src="../../../../logo.svg" width="300" height="200" alt="Rust Radix Logo">
</a>
</p>

<h1 align="center">radix-leptos-primitive</h1>

This is an internal utility, not intended for public usage.

[Rust Radix](https://github.com/RustForWeb/radix) is a Rust port of [Radix](https://www.radix-ui.com/primitives).

## Overview

```rust
use leptos::*;
use leptos_node_ref::AnyNodeRef;
use leptos_typed_fallback_show::TypedFallbackShow;

/// A generic Primitive component. Renders `element()` by default, or its
/// children directly if `as_child` is `true`. We rely on `TypedChildrenFn`
/// so that attributes can pass through at runtime—critical in Leptos v0.7
/// because `Children`-based types block such passthrough.
#[component]
#[allow(non_snake_case)]
pub fn Primitive<E, C>(
element: fn() -> HtmlElement<E, (), ()>,
children: TypedChildrenFn<C>,
#[prop(optional, into)] as_child: MaybeProp<bool>,
#[prop(optional, into)] node_ref: AnyNodeRef,
) -> impl IntoView
where
E: ElementType + 'static,
C: IntoView + 'static,
{
let children = StoredValue::new(children.into_inner());
view! {
<TypedFallbackShow
when=move || as_child.get().unwrap_or_default()
fallback=move || {
element()
.child(children.with_value(|c| c()))
.add_any_attr(leptos_node_ref::any_node_ref(node_ref))
}
>
{children.with_value(|c| c())
.add_any_attr(leptos_node_ref::any_node_ref(node_ref))}
</TypedFallbackShow>
}
}

/// Same idea, but for elements that do not take children (e.g. `img`, `input`).
#[component]
#[allow(non_snake_case)]
pub fn VoidPrimitive<E, C>(
element: fn() -> HtmlElement<E, (), ()>,
children: TypedChildrenFn<C>,
#[prop(optional, into)] as_child: MaybeProp<bool>,
#[prop(optional, into)] node_ref: AnyNodeRef,
) -> impl IntoView
where
E: ElementType + 'static,
{
let children = StoredValue::new(children.into_inner());
view! {
<TypedFallbackShow
when=move || as_child.get().unwrap_or_default()
fallback=move || {
element().add_any_attr(leptos_node_ref::any_node_ref(node_ref))
}
>
{children.with_value(|c| c())
.add_any_attr(leptos_node_ref::any_node_ref(node_ref))}
</TypedFallbackShow>
}
}

// (Compose callbacks is an internal piece from Radix Core; omitted for brevity.)
```

## Notes

- **Why `TypedChildrenFn`?**: Leptos attribute passthrough only works if a component doesn't rely on `AnyView` or `Children`. Using typed children ensures classes, events, etc. from the parent can flow to the rendered DOM node.
- **`as_child`**: Mimics `asChild` in Radix’s React version, but we skip an explicit `<Slot>`: Leptos’s approach to typed fallback rendering covers “slot-like” logic.
- **Class Handling**: Static classes from a parent can overwrite child-defined classes. No built-in merging exists.
- **Attribute System Limitations**: Leptos limits you to 26 dynamic attributes. Past that, nest components or try a custom approach.
- **Parity with React**: In React, `...props` merges everything automatically. In Leptos, we rely on typed props/attributes and can intercept unknown ones with `AttributeInterceptor`.

## Documentation

See [the Rust Radix book](https://radix.rustforweb.org/) for documentation.

## Rust For Web

The Rust Radix project is part of the [Rust For Web](https://github.com/RustForWeb).

[Rust For Web](https://github.com/RustForWeb) creates and ports web UI libraries for Rust. All projects are free and open source.
9 changes: 9 additions & 0 deletions packages/primitives/leptos/primitive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//! Leptos port of [Radix Primitive](https://www.radix-ui.com/primitives).
//!
//! This is an internal utility, not intended for public usage.

//! See [`@radix-ui/react-primitive`](https://www.npmjs.com/package/@radix-ui/react-primitive) for the original package.

mod primitive;

pub use primitive::*;
Loading