Skip to content

Commit d34c243

Browse files
author
tigerros
authored
feat: Per-component themes (#425)
1 parent 708cdda commit d34c243

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1347
-697
lines changed

book/src/guides/theming.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ Freya has built-in support for Theming.
44

55
> ⚠️ Currently, extending the base theme is not supported.
66
7-
### Accessing the current theme
7+
## Accessing the current theme
8+
89
You can access the whole current theme via the `use_get_theme` hook.
910

1011
```rust, no_run
@@ -30,7 +31,7 @@ fn Component(cx: Scope) -> Element {
3031
}
3132
```
3233

33-
### Custom default theme
34+
## Custom default theme
3435
By default, the selected theme is `LIGHT_THEME`. You can use the alternative, `DARK_THEME`.
3536

3637
```rust, no_run
@@ -57,7 +58,8 @@ fn Component(cx: Scope) -> Element {
5758
}
5859
```
5960

60-
### Change theme
61+
## Change theme globally
62+
6163
Changing the selected theme at runtime is possible by using the `use_theme` hook.
6264

6365
```rust, no_run
@@ -88,12 +90,11 @@ fn Component(cx: Scope) -> Element {
8890
}
8991
```
9092

91-
### Custom theme
93+
## Custom theme
9294

9395
Themes can be built from scratch or extended from others, like here with `LIGHT_THEME`:
9496

9597
```rust, no_run
96-
9798
const CUSTOM_THEME: Theme = Theme {
9899
button: ButtonTheme {
99100
background: "rgb(230, 0, 0)",

crates/components/src/accordion.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use dioxus::prelude::*;
22
use freya_elements::elements as dioxus_elements;
33
use freya_elements::events::MouseEvent;
4+
45
use freya_hooks::{
5-
use_animation, use_get_theme, use_node, use_platform, AccordionTheme, Animation,
6+
use_animation, use_applied_theme, use_node, use_platform, AccordionTheme, AccordionThemeWith,
7+
Animation,
68
};
79
use winit::window::CursorIcon;
810

@@ -19,10 +21,13 @@ pub enum AccordionStatus {
1921
/// [`Accordion`] component properties.
2022
#[derive(Props)]
2123
pub struct AccordionProps<'a> {
24+
/// Theme override.
25+
#[props(optional)]
26+
pub theme: Option<AccordionThemeWith>,
2227
/// Inner children for the Accordion.
23-
children: Element<'a>,
28+
pub children: Element<'a>,
2429
/// Summary element.
25-
summary: Element<'a>,
30+
pub summary: Element<'a>,
2631
}
2732

2833
/// `Accordion` component.
@@ -35,7 +40,7 @@ pub struct AccordionProps<'a> {
3540
///
3641
#[allow(non_snake_case)]
3742
pub fn Accordion<'a>(cx: Scope<'a, AccordionProps<'a>>) -> Element<'a> {
38-
let theme = use_get_theme(cx);
43+
let theme = use_applied_theme!(cx, &cx.props.theme, accordion);
3944
let animation = use_animation(cx, || 0.0);
4045
let open = use_state(cx, || false);
4146
let (node_ref, size) = use_node(cx);
@@ -47,7 +52,7 @@ pub fn Accordion<'a>(cx: Scope<'a, AccordionProps<'a>>) -> Element<'a> {
4752
background,
4853
color,
4954
border_fill,
50-
} = theme.accordion;
55+
} = theme;
5156

5257
// Adapt the accordion if the body size changes
5358
let _ = use_memo(

crates/components/src/body.rs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
use dioxus::prelude::*;
22
use freya_elements::elements as dioxus_elements;
3-
use freya_hooks::{use_get_theme, BodyTheme};
3+
4+
use freya_hooks::{use_applied_theme, BodyTheme, BodyThemeWith};
45

56
/// [`Body`] component properties.
67
#[derive(Props)]
78
pub struct BodyProps<'a> {
9+
/// Theme override.
10+
pub theme: Option<BodyThemeWith>,
811
/// Inner children for the Body.
9-
children: Element<'a>,
10-
/// Padding for the Body.
11-
#[props(default = "none".to_string(), into)]
12-
padding: String,
12+
pub children: Element<'a>,
1313
}
1414

1515
/// `Body` component.
@@ -39,16 +39,21 @@ pub struct BodyProps<'a> {
3939
///
4040
#[allow(non_snake_case)]
4141
pub fn Body<'a>(cx: Scope<'a, BodyProps<'a>>) -> Element {
42-
let theme = use_get_theme(cx);
43-
let BodyTheme { background, color } = theme.body;
44-
let BodyProps { children, padding } = cx.props;
42+
let theme = use_applied_theme!(cx, &cx.props.theme, body);
43+
let BodyTheme {
44+
background,
45+
color,
46+
padding,
47+
} = theme;
4548

46-
render!(rect {
47-
width: "fill",
48-
height: "fill",
49-
color: "{color}",
50-
background: "{background}",
51-
padding: "{padding}",
52-
children
53-
})
49+
render!(
50+
rect {
51+
width: "fill",
52+
height: "fill",
53+
color: "{color}",
54+
background: "{background}",
55+
padding: "{padding}",
56+
&cx.props.children
57+
}
58+
)
5459
}

crates/components/src/button.rs

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,16 @@
11
use dioxus::prelude::*;
22
use freya_elements::elements as dioxus_elements;
33
use freya_elements::events::MouseEvent;
4-
use freya_hooks::{use_focus, use_get_theme, use_platform};
4+
5+
use freya_hooks::{use_applied_theme, use_focus, use_platform, ButtonTheme, ButtonThemeWith};
56
use winit::window::CursorIcon;
67

78
/// [`Button`] component properties.
89
#[derive(Props)]
910
pub struct ButtonProps<'a> {
10-
/// Padding for the Button.
11-
#[props(default = "8 16".to_string(), into)]
12-
pub padding: String,
13-
/// Margin for the Button.
14-
#[props(default = "4".to_string(), into)]
15-
pub margin: String,
16-
/// Corner radius for the Button.
17-
#[props(default = "8".to_string(), into)]
18-
pub corner_radius: String,
19-
/// Width size for the Button.
20-
#[props(default = "auto".to_string(), into)]
21-
pub width: String,
22-
/// Inner children for the Button.
23-
#[props(default = "auto".to_string(), into)]
24-
pub height: String,
11+
/// Theme override.
12+
#[props(optional)]
13+
pub theme: Option<ButtonThemeWith>,
2514
/// Inner children for the Button.
2615
pub children: Element<'a>,
2716
/// Handler for the `onclick` event.
@@ -66,12 +55,22 @@ pub enum ButtonStatus {
6655
#[allow(non_snake_case)]
6756
pub fn Button<'a>(cx: Scope<'a, ButtonProps<'a>>) -> Element {
6857
let focus = use_focus(cx);
69-
let theme = use_get_theme(cx);
7058
let status = use_state(cx, ButtonStatus::default);
7159
let platform = use_platform(cx);
72-
7360
let focus_id = focus.attribute(cx);
7461

62+
let ButtonTheme {
63+
background,
64+
hover_background,
65+
border_fill,
66+
padding,
67+
margin,
68+
corner_radius,
69+
width,
70+
height,
71+
font_theme,
72+
} = use_applied_theme!(cx, &cx.props.theme, button);
73+
7574
let onclick = move |ev| {
7675
focus.focus();
7776
if let Some(onclick) = &cx.props.onclick {
@@ -102,19 +101,9 @@ pub fn Button<'a>(cx: Scope<'a, ButtonProps<'a>>) -> Element {
102101
};
103102

104103
let background = match *status.get() {
105-
ButtonStatus::Hovering => theme.button.hover_background,
106-
ButtonStatus::Idle => theme.button.background,
104+
ButtonStatus::Hovering => hover_background,
105+
ButtonStatus::Idle => background,
107106
};
108-
let color = theme.button.font_theme.color;
109-
let border_fill = theme.button.border_fill;
110-
let ButtonProps {
111-
width,
112-
height,
113-
corner_radius,
114-
padding,
115-
margin,
116-
..
117-
} = &cx.props;
118107

119108
render!(
120109
rect {
@@ -129,7 +118,7 @@ pub fn Button<'a>(cx: Scope<'a, ButtonProps<'a>>) -> Element {
129118
focusable: "true",
130119
overflow: "clip",
131120
role: "button",
132-
color: "{color}",
121+
color: "{font_theme.color}",
133122
shadow: "0 4 5 0 rgb(0, 0, 0, 0.1)",
134123
border: "1 solid {border_fill}",
135124
corner_radius: "{corner_radius}",

crates/components/src/canvas.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
use dioxus::prelude::{render, Element, Props, Scope};
22
use freya_elements::elements as dioxus_elements;
3-
use freya_hooks::UseCanvas;
3+
4+
use freya_hooks::{use_applied_theme, CanvasTheme, CanvasThemeWith, UseCanvas};
45

56
/// [`Canvas`] component properties.
67
#[derive(Props, PartialEq)]
78
pub struct CanvasProps {
8-
/// Width of the canvas.
9-
#[props(default = "300".to_string(), into)]
10-
width: String,
11-
/// Height of the canvas.
12-
#[props(default = "150".to_string(), into)]
13-
height: String,
14-
/// Color of the canvas.
15-
#[props(default = "white".to_string(), into)]
16-
background: String,
9+
/// Theme override.
10+
pub theme: Option<CanvasThemeWith>,
1711
/// The Canvas reference.
18-
canvas: UseCanvas,
12+
pub canvas: UseCanvas,
1913
}
2014

2115
/// Draw anything inside of this canvas.
@@ -25,11 +19,17 @@ pub struct CanvasProps {
2519
///
2620
#[allow(non_snake_case)]
2721
pub fn Canvas(cx: Scope<CanvasProps>) -> Element {
22+
let CanvasTheme {
23+
width,
24+
height,
25+
background,
26+
} = use_applied_theme!(cx, &cx.props.theme, canvas);
27+
2828
render!(rect {
2929
overflow: "clip",
3030
canvas_reference: cx.props.canvas.attribute(cx),
31-
background: "{cx.props.background}",
32-
width: "{cx.props.width}",
33-
height: "{cx.props.height}",
31+
background: "{background}",
32+
width: "{width}",
33+
height: "{height}"
3434
})
3535
}

0 commit comments

Comments
 (0)