From 37f2232af17ff20698af08497d7568fc9360e916 Mon Sep 17 00:00:00 2001 From: YK Date: Fri, 12 Sep 2025 11:44:55 +0300 Subject: [PATCH] sidebar part 1: character image, name, class, level, xp, hp --- src/components/sidebar.rs | 103 +++++++++++++++++++++++++- src/utils/mod.rs | 25 +++++++ style/_sidebar.scss | 150 ++++++++++++++++++++++++++++++++++++++ style/main.scss | 36 ++++++--- 4 files changed, 300 insertions(+), 14 deletions(-) create mode 100644 style/_sidebar.scss diff --git a/src/components/sidebar.rs b/src/components/sidebar.rs index d42ee79..0dde8ed 100644 --- a/src/components/sidebar.rs +++ b/src/components/sidebar.rs @@ -1,12 +1,109 @@ -use crate::prelude::*; -use leptos::prelude::*; +use crate::{entities::{DashboardStoreFields, NameStoreFields, PlayerDataStoreFields}, prelude::*}; +use leptos::{attr::Target, ev::{Event, Targeted}, html, logging, prelude::*}; +use leptos::web_sys::HtmlInputElement; #[component] pub fn Sidebar () -> impl IntoView { + let state = expect_context::(); + + let player = state.player(); + let name = player.name(); + + let first = name.first(); + let last = name.last(); + let alias = name.alias(); + + let name_input_class = "header-input header-input-3"; + + let class = player.class(); + let level = player.level(); + let xp = player.xp(); + + let hp = player.hp(); + let max_hp = player.max_hp(); + let temp_hp = player.temp_hp(); + + let image = player.image(); + + let adjust_level = move |adjustment: i8| level.update(|l| { + if let Some(new) = l.checked_add_signed(adjustment) { + *l = new; + } + }); + + + let adjust_xp = move |ev: Targeted| { + utils::adjust_checked(ev, xp); + }; + + let adjust_hp = move |ev: Targeted| { + utils::adjust_checked(ev, hp); + }; + + let adjust_max_hp = move |ev: Targeted| { + utils::adjust_checked(ev, max_hp); + }; + + let adjust_temp_hp = move |ev: Targeted| { + utils::adjust_checked(ev, temp_hp); + }; + + + let pc_hp = move |a: u16, b: u16| { + format!("width: {}%", utils::filled_pc(a, b).min(100.)) + }; + view! { } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 4faee0d..f1e6392 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,4 +1,8 @@ use crate::prelude::*; +use std::str::FromStr; +use leptos::ev::{ Event, Targeted }; +use leptos::web_sys::HtmlInputElement; +use leptos::reactive::traits::{ Get, Set }; pub fn read_items (contents: &str, map: &mut HashMap) -> usize { @@ -19,3 +23,24 @@ pub fn read_items (contents: &str, map: &mut HashMap) -> usize { map.len() - start } + +pub fn adjust_checked (ev: Targeted, store: U) -> () + where + U: Set + Get, + T: FromStr + ToString +{ + let target = ev.target(); + let val = target.value(); + + if let Ok(val) = val.parse::() { + store.set(val); + } else { + target.set_value(&store.get().to_string()); + } +} + +pub fn filled_pc (cur: T, max: T) -> f32 where T: Into { + let cur: f32 = cur.into(); + let max: f32 = max.into(); + cur / max * 100. +} diff --git a/style/_sidebar.scss b/style/_sidebar.scss new file mode 100644 index 0000000..6fe29eb --- /dev/null +++ b/style/_sidebar.scss @@ -0,0 +1,150 @@ +@use 'mixins'; + +aside { + grid-row: 2/3; + grid-column: 1/2; + box-sizing: border-box; + + .character-image { + max-height: 32vh; + overflow: hidden; + display: flex; + justify-content: center; + align-items: center; + img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center bottom; + + } + $size: 30px; + clip-path: polygon(0 0, 100% 0, 100% calc(100% - $size), calc(100% - $size) 100%, 0 100%); + } + + // @FIXME: long names stretch the sidebar + .names { + margin-top: 10px; + margin-left: 10px; + display: flex; + font-size: .9em; + justify-content: flex-start; + flex-wrap: wrap; + box-sizing: border-box; + max-width: 100%; + overflow: hidden; + + transform: skewX(-3deg); + + .alias-field { + margin: 0 5px; + display: flex; + align-items: center; + justify-content: center; + input { + padding: 0; + } + } + } + + .about { + display: flex; + flex-wrap: wrap; + padding: 2px 10px; + justify-content: space-evenly; + + &, input.header-input, * { + font-size: 14px; + font-family: "Neusa Next Pro"; + font-weight: 300; + } + + .class { + input { + + } + } + + .level { + } + + button { + color: white; + } + + .level-adjust { + display: flex; + width: 100%; + justify-content: center; + button { + font-size: 1.5em; + width: 10%; + height: 20px; + margin: 4px 5px; + @include mixins.button; + } + } + } + + > h6 { + font-size: 0.7em; + text-align: right; + padding: 5px; + color: #666; + font-family: "Galderglynn Titling"; + } + + .hp { + display: flex; + background: black; + .perm, .temp { + position: relative; + display: flex; + align-items: center; + } + + + .perm { + width: 80%; + justify-content: flex-end; + + .bar { + background: rgba(crimson, 0.25); + } + #current_hp { + margin-right: -5px; + } + #max_hp { + margin-left: 5px; + } + } + .temp { + justify-content: flex-end; + text-align: right; + width: 20%; + .bar { + background: rgba(purple, 0.15); + } + } + + .bar { + z-index: 1; + height: 100%; + display: block; + content: ''; + position: absolute; + top: 0; + left: 0; + } + + .val { + z-index: 2; + } + } + + input { + max-width: 100%; + width: fit-content; + field-sizing: content; + } +} diff --git a/style/main.scss b/style/main.scss index d8a9e7f..603da7d 100644 --- a/style/main.scss +++ b/style/main.scss @@ -1,6 +1,7 @@ @use 'mixins'; @use 'reset'; @use 'header'; +@use 'sidebar'; html { background: black; @@ -33,27 +34,16 @@ h1, h2, h3, h4, h5, h6 { header, aside, main { background: rgba(12, 12, 12, 1); - filter: drop-shadow(1px 1px 0px hsl(345, 69%, 20%)) - drop-shadow(2px 1px 0px hsl(345, 69%, 15%)); position: relative; } - -aside { - grid-row: 2/3; - grid-column: 1/2; -} - main { grid-row: 2/3; grid-column: 2/3; } input { - border: 1px solid black; - height: 36px; - font-size: 28px; padding: 3px; } @@ -63,6 +53,7 @@ input { } input.header-input { + text-decoration: underline 1px rgba(255, 255, 255, 0.33); border: none; color: white; @@ -76,5 +67,28 @@ input.header-input { font-weight: 200; } + &-1 { + font-size: 2em; + } + &-2 { + font-size: 1.5em; + } + &-3 { + font-size: 1.25em; + } + &-4 { + font-size: 1em; + } + &-5 { + font-size: 0.9em; + } + &-6 { + font-size: 0.75em; + } + @include mixins.heading; } + +img { + overflow: hidden; +}