From 1bf46db737a4b89bd47244967b5d92f19ad5a0e8 Mon Sep 17 00:00:00 2001 From: Danielle Huisman Date: Wed, 27 Mar 2024 19:54:26 +0100 Subject: [PATCH] Add start of visual Leptos tests --- Cargo.toml | 2 +- packages/leptos/test/visual/Cargo.toml | 20 + packages/leptos/test/visual/index.css | 229 ++++++++ packages/leptos/test/visual/index.html | 12 + packages/leptos/test/visual/src/app.rs | 72 +++ packages/leptos/test/visual/src/main.rs | 21 + packages/leptos/test/visual/src/spec.rs | 2 + .../leptos/test/visual/src/spec/placement.rs | 94 ++++ .../leptos/test/visual/src/spec/relative.rs | 12 + packages/leptos/test/visual/src/utils.rs | 2 + .../test/visual/src/utils/all_placements.rs | 16 + packages/leptos/test/visual/src/utils/new.rs | 9 + packages/utils/src/types.rs | 499 ------------------ 13 files changed, 490 insertions(+), 500 deletions(-) create mode 100644 packages/leptos/test/visual/Cargo.toml create mode 100644 packages/leptos/test/visual/index.css create mode 100644 packages/leptos/test/visual/index.html create mode 100644 packages/leptos/test/visual/src/app.rs create mode 100644 packages/leptos/test/visual/src/main.rs create mode 100644 packages/leptos/test/visual/src/spec.rs create mode 100644 packages/leptos/test/visual/src/spec/placement.rs create mode 100644 packages/leptos/test/visual/src/spec/relative.rs create mode 100644 packages/leptos/test/visual/src/utils.rs create mode 100644 packages/leptos/test/visual/src/utils/all_placements.rs create mode 100644 packages/leptos/test/visual/src/utils/new.rs delete mode 100644 packages/utils/src/types.rs diff --git a/Cargo.toml b/Cargo.toml index 072b05f..9b37e1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["packages/*", "packages/*/example"] +members = ["packages/*", "packages/*/example", "packages/*/test/*"] resolver = "2" [workspace.package] diff --git a/packages/leptos/test/visual/Cargo.toml b/packages/leptos/test/visual/Cargo.toml new file mode 100644 index 0000000..8062452 --- /dev/null +++ b/packages/leptos/test/visual/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "floating-ui-leptos-test-visual" +description = "Visual tests for Floating UI Leptos." +publish = false + +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +console_log = "1.0.0" +console_error_panic_hook = "0.1.7" +leptos = { version = "0.6.9", features = ["csr", "nightly"] } +log = "0.4.21" +floating-ui-leptos = { path = "../.." } +wasm-bindgen.workspace = true +web-sys.workspace = true +leptos_router = { version = "0.6.9", features = ["csr", "nightly"] } diff --git a/packages/leptos/test/visual/index.css b/packages/leptos/test/visual/index.css new file mode 100644 index 0000000..55f3805 --- /dev/null +++ b/packages/leptos/test/visual/index.css @@ -0,0 +1,229 @@ +*, +*::before, +*::after { + box-sizing: border-box; + font-family: sans-serif; +} + +body { + margin: 0; + padding-bottom: 200px; +} + +nav { + background: #edeff7; + position: fixed; + left: 0; + bottom: 0; + top: 0; + width: 15rem; + overflow-y: auto; + padding-bottom: 1rem; +} + +nav h2 { + margin-left: 1rem; +} + +nav ul { + list-style-type: none; + padding: 0; +} + +nav button { + width: 100%; + text-align: left; + font-size: 1rem; + border: none; + cursor: pointer; + padding: 0.25rem 0; + padding-left: 2rem; + background: none; +} + +.home-button { + font-size: 2rem; + font-weight: bold; + margin-top: 1rem; + text-decoration: none; + color: black; +} + +.new-button { + background: royalblue; + width: max-content; + text-decoration: none; + color: white; + font-size: 1.25rem; + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; +} + +.nav-top { + display: flex; + flex-direction: column; + padding: 0.5rem; + margin-left: 1.5rem; + gap: 0.5rem; +} + +.nav-link { + display: block; + text-decoration: none; + margin-left: 2rem; + margin-right: 1rem; + font-size: 1.125rem; + padding: 0.25rem 0; + color: #646870; + text-transform: capitalize; +} + +.nav-link[aria-current='page'] { + color: black; + font-weight: bold; +} + +.nav-link:visited { + color: #646870; +} + +h1 { + font-size: 3rem; +} + +main { + margin-left: 15rem; + padding-left: 2rem; + max-width: 800px; +} + +.reference { + display: grid; + place-items: center; + font: inherit; + width: 160px; + height: 160px; + background: #ed4f73; + color: white; +} + +.floating { + display: grid; + place-items: center; + background: turquoise; + width: 80px; + height: 80px; + outline: none; +} + +.arrow { + width: 15px; + height: 15px; + background: yellow; +} + +.container { + border: 1px solid black; + display: grid; + place-items: center; + width: 700px; + height: 500px; + margin-bottom: 1rem; +} + +.container[data-flexible] { + width: 100%; +} + +.controls { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + width: 100%; + background: #edeff7; + padding: 16px; +} + +.controls button { + all: unset; + text-align: center; + background: royalblue; + padding: 8px; + color: white; + font-size: 1.125rem; + border-radius: 4px; + width: 200px; +} + +.controls button:hover { + background: navy; +} + +.controls button:focus-visible { + outline: 2px solid black; +} + +.scroll { + display: grid; + place-items: center; + overflow: scroll; + background: #edeff7; + border: 1px solid; + width: 450px; + height: 450px; +} + +.scroll::before { + content: ''; + display: block; + width: 1px; + height: 750px; +} + +.scroll::after { + content: ''; + display: block; + width: 1px; + height: 750px; +} + +.scroll[data-x]::before, +.scroll[data-x]::after { + width: 1500px; +} + +.scroll-indicator { + background: #edeff726; + z-index: 10; + width: fit-content; + padding: 5px; + border-radius: 5px; + display: none; +} + +.prose { + font-size: 1.125rem; + color: #555; + line-height: 1.75; +} + +@keyframes scale { + from { + transform: scale(0.5); + } + + to { + transform: scale(1.25); + } +} + +@media (max-width: 600px) { + nav { + display: none; + } + + main { + margin: 0; + padding: 0 1rem; + } +} diff --git a/packages/leptos/test/visual/index.html b/packages/leptos/test/visual/index.html new file mode 100644 index 0000000..3c07d70 --- /dev/null +++ b/packages/leptos/test/visual/index.html @@ -0,0 +1,12 @@ + + + + Rust Floating UI Testing Grounds + + + + + +
+ + diff --git a/packages/leptos/test/visual/src/app.rs b/packages/leptos/test/visual/src/app.rs new file mode 100644 index 0000000..824c697 --- /dev/null +++ b/packages/leptos/test/visual/src/app.rs @@ -0,0 +1,72 @@ +use leptos::*; +use leptos_router::{Outlet, Route, Router, Routes, A}; + +use crate::spec::placement::Placement; +use crate::spec::relative::Relative; +use crate::utils::new::New; + +const ROUTES: [&str; 2] = ["placement", "relative"]; + +#[component] +pub fn AppWrapper() -> impl IntoView { + view! { +
+
+ +
+ +
+ } +} + +#[component] +pub fn Index() -> impl IntoView { + view! { +

Floating UI Testing Grounds

+

+ Welcome! On the left is a navigation bar to browse through + different testing files. These files, and the control buttons, are + used by Playwright to take screenshots of the page for visual + snapshot testing. +

+ } +} + +#[component] +pub fn App() -> impl IntoView { + view! { + + + + + + + + + + + + } +} diff --git a/packages/leptos/test/visual/src/main.rs b/packages/leptos/test/visual/src/main.rs new file mode 100644 index 0000000..64730e4 --- /dev/null +++ b/packages/leptos/test/visual/src/main.rs @@ -0,0 +1,21 @@ +mod app; +mod spec; +mod utils; + +use wasm_bindgen::JsCast; +use web_sys::HtmlElement; + +use crate::app::App; + +pub fn main() { + _ = console_log::init_with_level(log::Level::Debug); + console_error_panic_hook::set_once(); + + leptos::mount_to( + leptos::document() + .get_element_by_id("root") + .unwrap() + .unchecked_into::(), + App, + ); +} diff --git a/packages/leptos/test/visual/src/spec.rs b/packages/leptos/test/visual/src/spec.rs new file mode 100644 index 0000000..c693fcc --- /dev/null +++ b/packages/leptos/test/visual/src/spec.rs @@ -0,0 +1,2 @@ +pub mod placement; +pub mod relative; diff --git a/packages/leptos/test/visual/src/spec/placement.rs b/packages/leptos/test/visual/src/spec/placement.rs new file mode 100644 index 0000000..92afff5 --- /dev/null +++ b/packages/leptos/test/visual/src/spec/placement.rs @@ -0,0 +1,94 @@ +use floating_ui_leptos::{use_floating, Placement, UseFloatingOptions, UseFloatingReturn}; +use leptos::{html::Div, *}; + +use crate::utils::all_placements::ALL_PLACEMENTS; + +#[component] +pub fn Placement() -> impl IntoView { + let reference = create_node_ref::
(); + let floating = create_node_ref::
(); + + let (rtl, set_rtl) = create_signal(false); + let (placement, set_placement) = create_signal(Placement::Bottom); + let UseFloatingReturn { + floating_styles, + update, + .. + } = use_floating( + reference, + floating, + UseFloatingOptions::default().placement(placement.into()), + ); + let (size, set_size) = create_signal(80); + + view! { +

Placement

+

+ The floating element should be correctly positioned when given each of the 12 placements. +

+
"direction: rtl;", + false => "direction: ltr;", + }> +
+ Reference +
+
+ Floating +
+ +
+ + +
+ +
+ "black", + false => "" + } + on:click=move |_| set_placement(local_placement) + > + {format!("{:?}", local_placement)} + + } + /> +
+ +

RTL

+
+ "black", + false => "" + } + on:click=move |_| set_rtl(value) + > + {format!("{}", value)} + + } + /> +
+
+ } +} diff --git a/packages/leptos/test/visual/src/spec/relative.rs b/packages/leptos/test/visual/src/spec/relative.rs new file mode 100644 index 0000000..0355bdc --- /dev/null +++ b/packages/leptos/test/visual/src/spec/relative.rs @@ -0,0 +1,12 @@ +use leptos::*; + +#[component] +pub fn Relative() -> impl IntoView { + view! { +

Relative

+

+ The floating element should be positioned correctly on the bottom when a + certain parent node has position: relative applied. +

+ } +} diff --git a/packages/leptos/test/visual/src/utils.rs b/packages/leptos/test/visual/src/utils.rs new file mode 100644 index 0000000..7060c85 --- /dev/null +++ b/packages/leptos/test/visual/src/utils.rs @@ -0,0 +1,2 @@ +pub mod all_placements; +pub mod new; diff --git a/packages/leptos/test/visual/src/utils/all_placements.rs b/packages/leptos/test/visual/src/utils/all_placements.rs new file mode 100644 index 0000000..1f24348 --- /dev/null +++ b/packages/leptos/test/visual/src/utils/all_placements.rs @@ -0,0 +1,16 @@ +use floating_ui_leptos::Placement; + +pub const ALL_PLACEMENTS: [Placement; 12] = [ + Placement::Top, + Placement::TopStart, + Placement::TopEnd, + Placement::Right, + Placement::RightStart, + Placement::RightEnd, + Placement::Bottom, + Placement::BottomStart, + Placement::BottomEnd, + Placement::Left, + Placement::LeftStart, + Placement::LeftEnd, +]; diff --git a/packages/leptos/test/visual/src/utils/new.rs b/packages/leptos/test/visual/src/utils/new.rs new file mode 100644 index 0000000..e486bf5 --- /dev/null +++ b/packages/leptos/test/visual/src/utils/new.rs @@ -0,0 +1,9 @@ +use leptos::*; + +#[component] +pub fn New() -> impl IntoView { + view! { +

New

+

"This route lets you work on new features! Have fun :-)"

+ } +} diff --git a/packages/utils/src/types.rs b/packages/utils/src/types.rs deleted file mode 100644 index 733c8ff..0000000 --- a/packages/utils/src/types.rs +++ /dev/null @@ -1,499 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Alignment { - Start, - End, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Side { - Top, - Right, - Bottom, - Left, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum AlignedPlacement { - TopStart, - TopEnd, - RightStart, - RightEnd, - BottomStart, - BottomEnd, - LeftStart, - LeftEnd, -} - -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum Placement { - Top, - TopStart, - TopEnd, - Right, - RightStart, - RightEnd, - Bottom, - BottomStart, - BottomEnd, - Left, - LeftStart, - LeftEnd, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Strategy { - Absolute, - Fixed, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Axis { - X, - Y, -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Coords { - pub x: f64, - pub y: f64, -} - -impl Coords { - pub fn new(value: f64) -> Self { - Self { x: value, y: value } - } - - pub fn get_axis(&self, axis: Axis) -> f64 { - match axis { - Axis::X => self.x, - Axis::Y => self.y, - } - } - - pub fn update_axis(&mut self, axis: Axis, update: F) - where - F: Fn(f64) -> f64, - { - match axis { - Axis::X => { - self.x = update(self.x); - } - Axis::Y => { - self.y = update(self.y); - } - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Length { - Width, - Height, -} - -#[derive(Clone, Debug)] -pub struct Dimensions { - pub width: f64, - pub height: f64, -} - -impl Dimensions { - pub fn get_length(&self, length: Length) -> f64 { - match length { - Length::Width => self.width, - Length::Height => self.height, - } - } -} - -#[derive(Clone, Debug)] -pub struct SideObject { - pub top: f64, - pub right: f64, - pub bottom: f64, - pub left: f64, -} - -impl SideObject { - pub fn get_side(&self, side: Side) -> f64 { - match side { - Side::Top => self.top, - Side::Right => self.right, - Side::Bottom => self.bottom, - Side::Left => self.left, - } - } -} - -#[derive(Clone, Debug)] -pub struct PartialSideObject { - pub top: Option, - pub right: Option, - pub bottom: Option, - pub left: Option, -} - -#[derive(Clone, Debug)] -pub struct Rect { - pub x: f64, - pub y: f64, - pub width: f64, - pub height: f64, -} - -impl Rect { - pub fn get_axis(&self, axis: Axis) -> f64 { - match axis { - Axis::X => self.x, - Axis::Y => self.y, - } - } - - pub fn get_length(&self, length: Length) -> f64 { - match length { - Length::Width => self.width, - Length::Height => self.height, - } - } -} - -#[derive(Clone, Debug)] -pub enum Padding { - All(f64), - PerSide(PartialSideObject), -} - -#[derive(Clone, Debug)] -pub struct ClientRectObject { - pub x: f64, - pub y: f64, - pub width: f64, - pub height: f64, - pub top: f64, - pub right: f64, - pub bottom: f64, - pub left: f64, -} - -#[derive(Clone, Debug)] -pub struct ElementRects { - pub reference: Rect, - pub floating: Rect, -} - -pub enum ElementOrVirtual<'a, Element> { - Element(&'a Element), - VirtualElement(&'a dyn VirtualElement), -} - -impl<'a, Element> ElementOrVirtual<'a, Element> { - pub fn unwrap(&self) -> Option<&'a Element> { - match self { - ElementOrVirtual::Element(element) => Some(element), - ElementOrVirtual::VirtualElement(virtal_element) => virtal_element.context_element(), - } - } -} - -impl<'a, Element> From<&'a Element> for ElementOrVirtual<'a, Element> { - fn from(value: &'a Element) -> Self { - ElementOrVirtual::Element(value) - } -} - -/// Custom positioning reference element. -/// -/// See for the original documentation. -pub trait VirtualElement { - fn get_bounding_client_rect(&self) -> ClientRectObject; - - fn context_element(&self) -> Option<&Element>; -} - -#[derive(Clone, Debug)] -pub enum ElementOrWindow<'a, Element, Window> { - Element(&'a Element), - Window(&'a Window), -} - -impl<'a, Element, Window> From<&'a OwnedElementOrWindow> - for ElementOrWindow<'a, Element, Window> -{ - fn from(value: &'a OwnedElementOrWindow) -> Self { - match value { - OwnedElementOrWindow::Element(element) => ElementOrWindow::Element(element), - OwnedElementOrWindow::Window(window) => ElementOrWindow::Window(window), - } - } -} - -#[derive(Clone, Debug)] -pub enum OwnedElementOrWindow { - Element(Element), - Window(Window), -} - -pub const ALL_PLACEMENTS: [Placement; 12] = [ - Placement::Top, - Placement::TopStart, - Placement::TopEnd, - Placement::Right, - Placement::RightStart, - Placement::RightEnd, - Placement::Bottom, - Placement::BottomStart, - Placement::BottomEnd, - Placement::Left, - Placement::LeftStart, - Placement::LeftEnd, -]; - -pub fn clamp(start: f64, value: f64, end: f64) -> f64 { - value.min(end).max(start) -} - -pub fn get_side(placement: Placement) -> Side { - match placement { - Placement::Top => Side::Top, - Placement::TopStart => Side::Top, - Placement::TopEnd => Side::Top, - Placement::Right => Side::Right, - Placement::RightStart => Side::Right, - Placement::RightEnd => Side::Right, - Placement::Bottom => Side::Bottom, - Placement::BottomStart => Side::Bottom, - Placement::BottomEnd => Side::Bottom, - Placement::Left => Side::Left, - Placement::LeftStart => Side::Left, - Placement::LeftEnd => Side::Left, - } -} - -pub fn get_alignment(placement: Placement) -> Option { - match placement { - Placement::Top => None, - Placement::TopStart => Some(Alignment::Start), - Placement::TopEnd => Some(Alignment::End), - Placement::Right => None, - Placement::RightStart => Some(Alignment::Start), - Placement::RightEnd => Some(Alignment::End), - Placement::Bottom => None, - Placement::BottomStart => Some(Alignment::Start), - Placement::BottomEnd => Some(Alignment::End), - Placement::Left => None, - Placement::LeftStart => Some(Alignment::Start), - Placement::LeftEnd => Some(Alignment::End), - } -} - -pub fn get_placement(side: Side, alignment: Option) -> Placement { - match (side, alignment) { - (Side::Top, None) => Placement::Top, - (Side::Top, Some(Alignment::Start)) => Placement::TopStart, - (Side::Top, Some(Alignment::End)) => Placement::TopEnd, - (Side::Right, None) => Placement::Right, - (Side::Right, Some(Alignment::Start)) => Placement::RightStart, - (Side::Right, Some(Alignment::End)) => Placement::RightEnd, - (Side::Bottom, None) => Placement::Bottom, - (Side::Bottom, Some(Alignment::Start)) => Placement::BottomStart, - (Side::Bottom, Some(Alignment::End)) => Placement::BottomEnd, - (Side::Left, None) => Placement::Left, - (Side::Left, Some(Alignment::Start)) => Placement::LeftStart, - (Side::Left, Some(Alignment::End)) => Placement::LeftEnd, - } -} - -pub fn get_opposite_axis(axis: Axis) -> Axis { - match axis { - Axis::X => Axis::Y, - Axis::Y => Axis::X, - } -} - -pub fn get_axis_length(axis: Axis) -> Length { - match axis { - Axis::X => Length::Width, - Axis::Y => Length::Height, - } -} - -pub fn get_side_axis(placement: Placement) -> Axis { - match get_side(placement) { - Side::Top => Axis::Y, - Side::Right => Axis::X, - Side::Bottom => Axis::Y, - Side::Left => Axis::X, - } -} - -pub fn get_alignment_axis(placement: Placement) -> Axis { - get_opposite_axis(get_side_axis(placement)) -} - -pub fn get_alignment_sides( - placement: Placement, - rects: &ElementRects, - rtl: Option, -) -> (Side, Side) { - let alignment = get_alignment(placement); - let alignment_axis = get_alignment_axis(placement); - let length = get_axis_length(alignment_axis); - - let mut main_alignment_side = match (alignment_axis, alignment) { - (Axis::X, Some(Alignment::Start)) => match rtl { - Some(true) => Side::Left, - _ => Side::Right, - }, - (Axis::X, _) => match rtl { - Some(true) => Side::Right, - _ => Side::Left, - }, - (Axis::Y, Some(Alignment::Start)) => Side::Bottom, - (Axis::Y, _) => Side::Top, - }; - - if rects.reference.get_length(length) > rects.floating.get_length(length) { - main_alignment_side = get_opposite_side(main_alignment_side); - } - - (main_alignment_side, get_opposite_side(main_alignment_side)) -} - -pub fn get_expanded_placements(placement: Placement) -> Vec { - let opposite_placement = get_opposite_placement(placement); - - vec![ - get_opposite_alignment_placement(placement), - opposite_placement, - get_opposite_alignment_placement(opposite_placement), - ] -} - -pub fn get_opposite_alignment_placement(placement: Placement) -> Placement { - match placement { - Placement::Top => Placement::Top, - Placement::TopStart => Placement::TopEnd, - Placement::TopEnd => Placement::TopStart, - Placement::Right => Placement::Right, - Placement::RightStart => Placement::RightEnd, - Placement::RightEnd => Placement::RightStart, - Placement::Bottom => Placement::Bottom, - Placement::BottomStart => Placement::BottomEnd, - Placement::BottomEnd => Placement::BottomStart, - Placement::Left => Placement::Left, - Placement::LeftStart => Placement::LeftEnd, - Placement::LeftEnd => Placement::LeftStart, - } -} - -pub fn get_side_list(side: Side, is_start: bool, rtl: Option) -> Vec { - match side { - Side::Top | Side::Bottom => match rtl { - Some(true) => match is_start { - true => vec![Side::Right, Side::Left], - false => vec![Side::Left, Side::Right], - }, - _ => match is_start { - true => vec![Side::Left, Side::Right], - false => vec![Side::Right, Side::Left], - }, - }, - Side::Right | Side::Left => match is_start { - true => vec![Side::Top, Side::Bottom], - false => vec![Side::Bottom, Side::Top], - }, - } -} - -pub fn get_opposite_side(side: Side) -> Side { - match side { - Side::Top => Side::Bottom, - Side::Right => Side::Left, - Side::Bottom => Side::Top, - Side::Left => Side::Right, - } -} - -pub fn get_opposite_axis_placements( - placement: Placement, - flip_alignment: bool, - direction: Option, - rtl: Option, -) -> Vec { - let alignment = get_alignment(placement); - let side_list = get_side_list( - get_side(placement), - direction.is_some_and(|d| d == Alignment::Start), - rtl, - ); - - let mut list: Vec = side_list - .into_iter() - .map(|side| get_placement(side, alignment)) - .collect(); - - if flip_alignment { - let mut opposite_list: Vec = list - .clone() - .into_iter() - .map(get_opposite_alignment_placement) - .collect(); - - list.append(&mut opposite_list); - } - - list -} - -pub fn get_opposite_placement(placement: Placement) -> Placement { - match placement { - Placement::Top => Placement::Bottom, - Placement::TopStart => Placement::BottomStart, - Placement::TopEnd => Placement::BottomEnd, - Placement::Right => Placement::Left, - Placement::RightStart => Placement::LeftStart, - Placement::RightEnd => Placement::LeftEnd, - Placement::Bottom => Placement::Top, - Placement::BottomStart => Placement::TopStart, - Placement::BottomEnd => Placement::TopEnd, - Placement::Left => Placement::Right, - Placement::LeftStart => Placement::RightStart, - Placement::LeftEnd => Placement::RightEnd, - } -} - -pub fn expand_padding_object(padding: PartialSideObject) -> SideObject { - SideObject { - top: padding.top.unwrap_or(0.0), - right: padding.right.unwrap_or(0.0), - bottom: padding.bottom.unwrap_or(0.0), - left: padding.left.unwrap_or(0.0), - } -} - -pub fn get_padding_object(padding: Padding) -> SideObject { - match padding { - Padding::All(padding) => SideObject { - top: padding, - right: padding, - bottom: padding, - left: padding, - }, - Padding::PerSide(padding) => expand_padding_object(padding), - } -} - -pub fn rect_to_client_rect(rect: Rect) -> ClientRectObject { - ClientRectObject { - x: rect.x, - y: rect.y, - width: rect.width, - height: rect.height, - top: rect.y, - right: rect.x + rect.width, - bottom: rect.y + rect.height, - left: rect.x, - } -}