item rendering, custom numput components

This commit is contained in:
YK 2025-09-23 15:44:06 +03:00
parent c38fe1e3bc
commit 6c7ba86018
11 changed files with 267 additions and 49 deletions

View File

@ -32,6 +32,9 @@ chrono = { version = "0.4.42", features = ["serde"] }
console_log = "1.0.0"
num-traits = "0.2.19"
num-format = "0.4.4"
js-sys = "=0.3.78"
seq-macro = "0.3.6"
[features]
csr = ["leptos/csr"]

View File

@ -1,6 +1,6 @@
use leptos::prelude::*;
use leptos::{logging, prelude::*};
use reactive_stores::Field;
use crate::{ components::numput::Numput, prelude::* };
use crate::{ components::{ item::Item as ItemRow, numput::Numput }, prelude::* };
#[component]
pub fn Inventory (
@ -8,8 +8,27 @@ pub fn Inventory (
inventory: Field<Inventory>,
balance: Field<Balance>
) -> impl IntoView {
let state = expect_context::<Context>();
let rm = move |idx: usize| inventory.update(|v| {
v.rows.remove(idx);
});
let mv = move |idx: usize| {
let (from, to): (Field<Inventory>, Field<Inventory>) = match kind.dst() {
InventoryKind::Personal => (state.common().inventory().into(), state.player().inventory().into()),
InventoryKind::Common => (state.player().inventory().into(), state.common().inventory().into()),
};
let l = from.try_update(|f| f.rows.remove(idx));
if let Some(l) = l {
to.write().rows.push(l);
}
};
view! {
<section class="inventory">
<section class=kind.class()>
<h3>{kind.to_string()}</h3>
<div class="balance">
<div title="PP (Platinum)" class="platinum">
@ -29,7 +48,18 @@ pub fn Inventory (
</div>
</div>
<div class="items">
<ForEnumerate
each=move || inventory.rows()
key=|row| row.read().id
// let (idx, entry)
children = move |idx, child| {
view! {
<ItemRow idx=idx entry={child.into()} mv=mv rm=rm />
}
}
/>
//
</div>
</section>
}

64
src/components/item.rs Normal file
View File

@ -0,0 +1,64 @@
use crate::{prelude::*, utils::BasisPoints};
use leptos::prelude::*;
use reactive_stores::{Field, OptionStoreExt};
#[component]
pub fn Item (
idx: ReadSignal<usize>,
entry: Field<entities::InventoryEntry>,
mut mv: impl FnMut(usize) + 'static,
mut rm: impl FnMut(usize) + 'static,
) -> impl IntoView {
let qty = move || entry.quantity();
let item = move || entry.item();
let empty = String::new();
let class = move || format!("item {}", item().tags().get().get("weight").unwrap_or(&empty));
let weight_display = move || item().weight().with(|f| f.to_decimal_string());
let weight_change = move |ev: Targeted<Event, HtmlInputElement>| {
let old = item().weight().get_untracked();
let new = ev.target().value();
let f = item().weight();
if let Some(t) = new.strip_prefix("+") && let Ok(v) = t.parse::<f32>() {
let d = (v * 100.) as u32;
f.set(old.saturating_add(d))
} else if let Some(t) = new.strip_prefix("-") && let Ok(v) = t.parse::<f32>() {
let d = (v * 100.) as u32;
f.set(old.saturating_sub(d))
} else if let Ok(v) = new.parse::<f32>() {
let n = (v * 100.) as u32;
f.set(n);
} else {
ev.target().set_value(&weight_display());
}
};
view! {
<div class=class>
<div class="item-name">{move || entry.get().item.name}</div>
<div class="item-quantity">
<button on:click=move |_| entry.quantity().update(|q| *q = q.saturating_sub(1)) >"-"</button>
<Numput field={entry.quantity()} />
<button on:click=move |_| entry.quantity().update(|q| *q = q.saturating_add(1)) >"+"</button>
</div>
<div class="item-weight">
<button on:click=move |_| entry.item().weight().update(|q| *q = q.saturating_sub(1)) >"-"</button>
// <Numput<u32,_,QF,QP> field=entry.item().weight() display_with=move |f| format!("{}{}{}", f, f, f) />
<NumputChange<_, _> field_with={weight_display} change={weight_change} />
<button on:click=move |_| entry.item().weight().update(|q| *q = q.saturating_add(1)) >"+"</button>
</div>
<button class="item-more">""</button>
<button class="item-swap" on:click=move |_| mv(idx.get())>""</button>
<button class="item-remove" on:click=move |_| rm(idx.get())>"x"</button>
</div>
}
}

View File

@ -9,6 +9,7 @@ pub mod sidebar;
pub mod nav;
pub mod inventory;
pub mod numput;
pub mod item;
#[component]
pub fn Character () -> impl IntoView {
@ -86,7 +87,7 @@ pub fn ItemSearchField () -> impl IntoView {
#[component]
pub fn ItemSearchCard <'a> (item: &'a Item) -> impl IntoView {
pub fn ItemSearchCard <'a> (item: &'a BookItem) -> impl IntoView {
let card_class = format!("item-search-card {}", item.rarity.as_ref().unwrap_or(&String::new()));
view! {
<div class=card_class>

View File

@ -36,3 +36,20 @@ pub fn Numput <T, U, 'a> (
<input pattern="[0-9+=\\-]*" type="text" class=class on:change:target=change prop:value={move || field.with(|f| f.to_formatted_string(&Locale::en))} />
}
}
#[component]
pub fn NumputChange <U, G, 'a> (
mut field_with: U,
change: G,
#[prop(optional)]
class: &'a str,
) -> impl IntoView
where U: FnMut() -> String + IntoProperty + Copy + Clone + Send + Sync + 'static,
G: FnMut(Targeted<Event, HtmlInputElement>) + 'static
{
let class = format!("{} numput", class);
view! {
<input pattern="[0-9+=\\-.]*" type="text" class=class on:change:target=change prop:value={move || field_with()} />
}
}

View File

@ -3,6 +3,8 @@ use super::*;
impl Dashboard {
pub fn mock () -> Self {
let (p_inv, c_inv) = generate_mock_inventory();
Dashboard {
campaign: "В Поисках Дмитрия Шардалина".to_owned(),
campaign_image: "https://assetsio.gnwcdn.com/dnd-5e-journeys-through-the-radiant-citadel-salted-legacy-artwork.jpg?width=690&quality=80&format=jpg&auto=webp".to_owned(),
@ -27,11 +29,11 @@ impl Dashboard {
hit_dice: HitDice { used: 1, kind: 20 },
death_save_throws: DeathSaveThrows { failed: 1, succeeded: 3 },
prepared: generate_prepared_spells_list(),
inventory: generate_mock_inventory(),
inventory: p_inv,
},
common: CommonData {
balance: Balance { platinum: 1312, gold: 3211200, electrum: 231, silver: 1, copper: 21021 },
inventory: generate_mock_inventory(),
inventory: c_inv,
},
quest_book: generate_mock_questbook(),
notes: generate_mock_notebook(),
@ -43,31 +45,35 @@ impl Dashboard {
}
}
fn generate_mock_inventory() -> Inventory {
let items = vec![
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Vorpal Sword".to_string(), weight: Some(4.0), description: Some("A blade that severs heads with a single swing.".to_string()), tags: HashMap::from([ ("type".to_string(), "weapon".to_string()), ("rarity".to_string(), "legendary".to_string()), ]), }), },
InventoryEntry { quantity: 3, item: InventoryItem::Custom(CustomInventoryItem { name: "Potion of Healing".to_string(), weight: Some(0.5), description: Some("A vial of red liquid that restores vitality.".to_string()), tags: HashMap::from([ ("type".to_string(), "potion".to_string()), ("rarity".to_string(), "common".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Cloak of Elvenkind".to_string(), weight: Some(1.0), description: Some("A cloak that blends into shadows.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "uncommon".to_string()), ]), }), },
InventoryEntry { quantity: 2, item: InventoryItem::Custom(CustomInventoryItem { name: "Bag of Holding".to_string(), weight: Some(15.0), description: Some("A small bag with vast internal space.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "uncommon".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Mithril Chainmail".to_string(), weight: Some(20.0), description: Some("Lightweight armor that gleams like silver.".to_string()), tags: HashMap::from([ ("type".to_string(), "armor".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Wand of Fireballs".to_string(), weight: Some(0.2), description: Some("A wand that unleashes fiery explosions.".to_string()), tags: HashMap::from([ ("type".to_string(), "wand".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Ring of Invisibility".to_string(), weight: Some(0.1), description: Some("Grants the wearer invisibility.".to_string()), tags: HashMap::from([ ("type".to_string(), "ring".to_string()), ("rarity".to_string(), "legendary".to_string()), ]), }), },
InventoryEntry { quantity: 2, item: InventoryItem::Custom(CustomInventoryItem { name: "Boots of Speed".to_string(), weight: Some(1.5), description: Some("Boots that enhance the wearer's speed.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Dagger of Venom".to_string(), weight: Some(1.0), description: Some("A dagger coated with deadly poison.".to_string()), tags: HashMap::from([ ("type".to_string(), "weapon".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Staff of Power".to_string(), weight: Some(5.0), description: Some("A staff pulsing with arcane might.".to_string()), tags: HashMap::from([ ("type".to_string(), "staff".to_string()), ("rarity".to_string(), "very rare".to_string()), ]), }), },
InventoryEntry { quantity: 4, item: InventoryItem::Custom(CustomInventoryItem { name: "Torch".to_string(), weight: Some(1.0), description: Some("A simple torch for illumination.".to_string()), tags: HashMap::from([ ("type".to_string(), "tool".to_string()), ("rarity".to_string(), "common".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Amulet of Health".to_string(), weight: Some(0.3), description: Some("Boosts the wearer's vitality.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Flame Tongue".to_string(), weight: Some(3.0), description: Some("A sword that ignites on command.".to_string()), tags: HashMap::from([ ("type".to_string(), "weapon".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }), },
InventoryEntry { quantity: 5, item: InventoryItem::Custom(CustomInventoryItem { name: "Arrow of Slaying".to_string(), weight: Some(0.1), description: Some("An arrow designed to kill a specific foe.".to_string()), tags: HashMap::from([ ("type".to_string(), "ammunition".to_string()), ("rarity".to_string(), "very rare".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Cloak of Displacement".to_string(), weight: Some(1.0), description: Some("Makes the wearer hard to hit.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Helm of Teleportation".to_string(), weight: Some(3.0), description: Some("Allows teleportation to known locations.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Crystal Ball".to_string(), weight: Some(7.0), description: Some("A magical orb for scrying.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "very rare".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Gauntlets of Ogre Power".to_string(), weight: Some(2.0), description: Some("Grants the strength of an ogre.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "uncommon".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Robe of the Archmagi".to_string(), weight: Some(1.0), description: Some("Enhances a wizard's spellcasting.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "legendary".to_string()), ]), }), },
InventoryEntry { quantity: 1, item: InventoryItem::Custom(CustomInventoryItem { name: "Horn of Blasting".to_string(), weight: Some(2.0), description: Some("Emits a destructive sonic blast.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }), },
fn generate_mock_inventory() -> (Inventory, Inventory) {
let a = vec![
InventoryEntry { id: 1, quantity: 1, item: InventoryItem { name: "Vorpal Sword".to_string(), weight: 400, description: Some("A blade that severs heads with a single swing.".to_string()), tags: HashMap::from([ ("type".to_string(), "weapon".to_string()), ("rarity".to_string(), "legendary".to_string()), ]), }, },
InventoryEntry { id: 2, quantity: 3, item: InventoryItem { name: "Potion of Healing".to_string(), weight: 50, description: Some("A vial of red liquid that restores vitality.".to_string()), tags: HashMap::from([ ("type".to_string(), "potion".to_string()), ("rarity".to_string(), "common".to_string()), ]), }, },
InventoryEntry { id: 3, quantity: 1, item: InventoryItem { name: "Cloak of Elvenkind".to_string(), weight: 100, description: Some("A cloak that blends into shadows.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "uncommon".to_string()), ]), }, },
InventoryEntry { id: 4, quantity: 2, item: InventoryItem { name: "Bag of Holding".to_string(), weight: 1500, description: Some("A small bag with vast internal space.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "uncommon".to_string()), ]), }, },
InventoryEntry { id: 5, quantity: 1, item: InventoryItem { name: "Mithril Chainmail".to_string(), weight: 2000, description: Some("Lightweight armor that gleams like silver.".to_string()), tags: HashMap::from([ ("type".to_string(), "armor".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }, },
InventoryEntry { id: 6, quantity: 1, item: InventoryItem { name: "Wand of Fireballs".to_string(), weight: 20, description: Some("A wand that unleashes fiery explosions.".to_string()), tags: HashMap::from([ ("type".to_string(), "wand".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }, },
InventoryEntry { id: 7, quantity: 1, item: InventoryItem { name: "Ring of Invisibility".to_string(), weight: 10, description: Some("Grants the wearer invisibility.".to_string()), tags: HashMap::from([ ("type".to_string(), "ring".to_string()), ("rarity".to_string(), "legendary".to_string()), ]), }, },
InventoryEntry { id: 8, quantity: 2, item: InventoryItem { name: "Boots of Speed".to_string(), weight: 150, description: Some("Boots that enhance the wearer's speed.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }, },
InventoryEntry { id: 9, quantity: 1, item: InventoryItem { name: "Dagger of Venom".to_string(), weight: 100, description: Some("A dagger coated with deadly poison.".to_string()), tags: HashMap::from([ ("type".to_string(), "weapon".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }, },
InventoryEntry { id: 10, quantity: 1, item: InventoryItem { name: "Staff of Power".to_string(), weight: 500, description: Some("A staff pulsing with arcane might.".to_string()), tags: HashMap::from([ ("type".to_string(), "staff".to_string()), ("rarity".to_string(), "very rare".to_string()), ]), }, },
];
let b = vec![
InventoryEntry { id: 11, quantity: 4, item: InventoryItem { name: "Torch".to_string(), weight: 100, description: Some("A simple torch for illumination.".to_string()), tags: HashMap::from([ ("type".to_string(), "tool".to_string()), ("rarity".to_string(), "common".to_string()), ]), }, },
InventoryEntry { id: 12, quantity: 1, item: InventoryItem { name: "Amulet of Health".to_string(), weight: 30, description: Some("Boosts the wearer's vitality.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }, },
InventoryEntry { id: 13, quantity: 1, item: InventoryItem { name: "Flame Tongue".to_string(), weight: 300, description: Some("A sword that ignites on command.".to_string()), tags: HashMap::from([ ("type".to_string(), "weapon".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }, },
InventoryEntry { id: 14, quantity: 5, item: InventoryItem { name: "Arrow of Slaying".to_string(), weight: 10, description: Some("An arrow designed to kill a specific foe.".to_string()), tags: HashMap::from([ ("type".to_string(), "ammunition".to_string()), ("rarity".to_string(), "very rare".to_string()), ]), }, },
InventoryEntry { id: 15, quantity: 1, item: InventoryItem { name: "Cloak of Displacement".to_string(), weight: 100, description: Some("Makes the wearer hard to hit.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }, },
InventoryEntry { id: 16, quantity: 1, item: InventoryItem { name: "Helm of Teleportation".to_string(), weight: 300, description: Some("Allows teleportation to known locations.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }, },
InventoryEntry { id: 17, quantity: 1, item: InventoryItem { name: "Crystal Ball".to_string(), weight: 700, description: Some("A magical orb for scrying.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "very rare".to_string()), ]), }, },
InventoryEntry { id: 18, quantity: 1, item: InventoryItem { name: "Gauntlets of Ogre Power".to_string(), weight: 200, description: Some("Grants the strength of an ogre.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "uncommon".to_string()), ]), }, },
InventoryEntry { id: 19, quantity: 1, item: InventoryItem { name: "Robe of the Archmagi".to_string(), weight: 100, description: Some("Enhances a wizard's spellcasting.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "legendary".to_string()), ]), }, },
InventoryEntry { id: 20, quantity: 1, item: InventoryItem { name: "Horn of Blasting".to_string(), weight: 200, description: Some("Emits a destructive sonic blast.".to_string()), tags: HashMap::from([ ("type".to_string(), "wondrous".to_string()), ("rarity".to_string(), "rare".to_string()), ]), }, },
];
Inventory(items)
(Inventory { rows: a }, Inventory { rows: b })
}

View File

@ -8,14 +8,14 @@ use crate::prelude::*;
mod mock;
#[derive(Clone, Debug, Store)]
pub struct Item {
pub struct BookItem {
pub name: String,
pub weight: Option<f64>,
pub rarity: Option<String>,
pub rest: Map<String, Value>,
}
impl Item {
impl BookItem {
pub fn from_json (t: Value) -> Option<Self> {
let Value::String(name) = t["name"].clone() else { return None };
let weight = match t["weight"].clone() {
@ -37,7 +37,7 @@ impl Item {
}
#[derive(Clone, Debug, Store)]
pub struct ItemReferenceMap (pub HashMap<String, Item>);
pub struct ItemReferenceMap (pub HashMap<String, BookItem>);
#[derive(Clone, Debug, Store)]
pub struct Dashboard {
@ -90,24 +90,34 @@ pub struct Balance {
}
#[derive(Clone, Debug, Store)]
pub struct Inventory (pub Vec<InventoryEntry>);
pub struct Inventory {
#[store(key: u64 = |row| row.id)]
pub rows: Vec<InventoryEntry>
}
#[derive(Clone, Debug, Store)]
#[derive(Clone, Debug, Store, Default)]
pub struct InventoryEntry {
pub id: u64,
pub quantity: u32,
pub item: InventoryItem,
}
#[derive(Clone, Debug, Store)]
pub enum InventoryItem {
Custom (CustomInventoryItem),
Book (Item),
impl InventoryEntry {
pub fn key (&self) -> String {
format!("{}-{}", self.quantity, self.item.name)
}
}
#[derive(Clone, Debug, Store)]
pub struct CustomInventoryItem {
// #[derive(Clone, Debug, Store)]
// pub enum InventoryItem {
// Custom (CustomInventoryItem),
// Book (Item),
// }
#[derive(Clone, Debug, Store, Default)]
pub struct InventoryItem {
pub name: String,
pub weight: Option<f32>,
pub weight: u32,
pub description: Option<String>,
pub tags: HashMap<String, String>,
}
@ -230,7 +240,7 @@ pub struct Settings {
}
#[derive(Clone, Debug, Store)]
#[derive(Clone, Copy, Debug, Store)]
pub enum InventoryKind {
Personal,
Common
@ -244,3 +254,19 @@ impl Display for InventoryKind {
})
}
}
impl InventoryKind {
pub fn dst (&self) -> Self {
match self {
Self::Common => Self::Personal,
Self::Personal => Self::Common,
}
}
pub fn class (&self) -> &'static str {
match self {
Self::Common => "inventory inventory-common",
Self::Personal => "inventory inventory-personal",
}
}
}

View File

@ -1,4 +1,4 @@
#![feature(let_chains)]
#![feature(type_alias_impl_trait)]
pub mod app;
#[allow(unused_imports)]

View File

@ -9,10 +9,11 @@ pub use anyhow::{ anyhow, bail };
pub use reactive_stores::Store;
pub use leptos::{ web_sys::HtmlInputElement, ev::{ Event, Targeted }};
pub use crate::{
pub (crate) use crate::{
utils,
entities::*,
entities::{ self, * },
statics::*,
components::{ self, numput::{ Numput, NumputChange } },
};
pub type Error = anyhow::Error;

View File

@ -5,13 +5,13 @@ use leptos::web_sys::HtmlInputElement;
use leptos::reactive::traits::{ Get, Set };
pub fn read_items (contents: &str, map: &mut HashMap<String, Item>) -> usize {
pub fn read_items (contents: &str, map: &mut HashMap<String, BookItem>) -> usize {
let Ok(json): std::result::Result<Value, _> = serde_json::from_str(&contents) else { error!("Can't read Value from contents"); return 0 };
let Value::Array(list) = json else { error!("Value isn't Array, skipping"); return 0 };
let start = map.len();
for t in list.into_iter().map(Item::from_json).flatten() {
for t in list.into_iter().map(BookItem::from_json).flatten() {
if map.contains_key(&t.name) {
let entry = map.get_mut(&t.name).unwrap();
entry.weight = entry.weight.or(t.weight);
@ -44,3 +44,16 @@ pub fn filled_pc <T> (cur: T, max: T) -> f32 where T: Into<f32> {
let max: f32 = max.into();
cur / max * 100.
}
pub trait BasisPoints {
fn to_decimal_string (&self) -> String;
}
impl BasisPoints for u32 {
fn to_decimal_string (&self) -> String {
let int = self / 100;
let frac = self % 100;
format!("{}.{:0>2}", int, frac)
}
}

View File

@ -13,7 +13,7 @@
}
section.inventory {
width: 45%;
width: 50%;
display: flex;
flex-direction: column;
@ -29,7 +29,7 @@ section.inventory {
h3 {
text-transform: lowercase;
font-size: 1.3em;
margin-bottom: 10px;
margin: 0 20px 10px;
font-weight: 900;
&::first-letter {
text-decoration: underline crimson;
@ -84,6 +84,59 @@ section.inventory {
}
}
&-personal {
.item-swap {
&::before {
content: "";
}
grid-column: 7/8;
}
}
&-common {
.item-swap {
&::before {
content: "";
}
grid-column: 1/2;
}
}
.items {
margin-top: 20px;
.item {
&:nth-child(2n) {
background: rgba(white, 0.05);
}
font-weight: 300;
display: grid;
grid-template-columns: 20px 3fr 1fr 1fr 20px 20px 20px;
height: 40px;
padding: 3px 8px;
> * {
grid-row: 1/2;
display: flex;
align-items: center;
}
&-name {
grid-column: 2/3;
}
&-quantity {
grid-column: 3/4;
}
&-weight {
grid-column: 4/5;
}
&-more {
grid-column: 5/6;
}
&-remove {
grid-column: 6/7;
}
}
}
input {
color: white;
width: 100%;
@ -104,4 +157,8 @@ section.inventory {
}
}
button {
color: white;
}
}