freya/_docs/extending_components.rs
1//! # Extending Components
2//!
3//! A custom [`Component`](freya_core::prelude::Component) usually wraps a built-in
4//! element like `rect()`. By default, callers cannot tweak that inner element's
5//! layout, accessibility, styling, etc.
6//!
7//! The most common case is accepting children via [`ChildrenExt`](freya_core::elements::extensions::ChildrenExt),
8//! so callers can use `.child(..)` / `.children(..)` on your component. Going beyond
9//! that is not super common, but when you want your component to feel like a
10//! built-in element, Freya provides **Data** types like [`LayoutData`](freya_core::data::LayoutData)
11//! or [`AccessibilityData`](freya_core::data::AccessibilityData), and their matching
12//! **extension traits** like [`LayoutExt`](freya_core::elements::extensions::LayoutExt)
13//! or [`AccessibilityExt`](freya_core::elements::extensions::AccessibilityExt).
14//!
15//! Store the Data on your component, implement the trait, and forward it to the
16//! inner element at render time. The trait gives your component the same builder
17//! methods that built-in elements expose, for free.
18//!
19//! The built-in [`Card`](crate::components::Card) component already does this with
20//! `LayoutData` + `LayoutExt`, `AccessibilityData` + `AccessibilityExt`, and more.
21//!
22//! ## Example: a `Panel` with `LayoutData`
23//!
24//! ```rust, ignore
25//! # use freya::prelude::*;
26//! #[derive(Clone, PartialEq)]
27//! pub struct Panel {
28//! layout: LayoutData,
29//! elements: Vec<Element>,
30//! }
31//!
32//! impl LayoutExt for Panel {
33//! fn get_layout(&mut self) -> &mut LayoutData {
34//! &mut self.layout
35//! }
36//! }
37//!
38//! impl ChildrenExt for Panel {
39//! fn get_children(&mut self) -> &mut Vec<Element> {
40//! &mut self.elements
41//! }
42//! }
43//!
44//! impl Component for Panel {
45//! fn render(&self) -> impl IntoElement {
46//! rect()
47//! .layout(self.layout.clone())
48//! .children(self.elements.clone())
49//! }
50//! }
51//! ```
52//!
53//! Callers can now use any layout builder method directly on `Panel`:
54//!
55//! ```rust, ignore
56//! # use freya::prelude::*;
57//! Panel::new()
58//! .width(Size::percent(75.))
59//! .padding(8.)
60//! .child("Hello, World!")
61//! ```
62//!
63//! ## Pattern
64//!
65//! 1. Add a `*Data` field to your component.
66//! 2. Implement the matching `*Ext` trait returning `&mut` to that field.
67//! 3. Forward the Data into the inner element in `render`.
68//!
69//! Mix as many as you need: `LayoutData` + `LayoutExt`, `AccessibilityData` +
70//! `AccessibilityExt`, `Vec<Element>` + [`ChildrenExt`](freya_core::elements::extensions::ChildrenExt),
71//! and so on.