item rendering, custom numput components
This commit is contained in:
parent
c38fe1e3bc
commit
6c7ba86018
@ -32,6 +32,9 @@ chrono = { version = "0.4.42", features = ["serde"] }
|
|||||||
console_log = "1.0.0"
|
console_log = "1.0.0"
|
||||||
num-traits = "0.2.19"
|
num-traits = "0.2.19"
|
||||||
num-format = "0.4.4"
|
num-format = "0.4.4"
|
||||||
|
js-sys = "=0.3.78"
|
||||||
|
seq-macro = "0.3.6"
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
csr = ["leptos/csr"]
|
csr = ["leptos/csr"]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use leptos::prelude::*;
|
use leptos::{logging, prelude::*};
|
||||||
use reactive_stores::Field;
|
use reactive_stores::Field;
|
||||||
use crate::{ components::numput::Numput, prelude::* };
|
use crate::{ components::{ item::Item as ItemRow, numput::Numput }, prelude::* };
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Inventory (
|
pub fn Inventory (
|
||||||
@ -8,8 +8,27 @@ pub fn Inventory (
|
|||||||
inventory: Field<Inventory>,
|
inventory: Field<Inventory>,
|
||||||
balance: Field<Balance>
|
balance: Field<Balance>
|
||||||
) -> impl IntoView {
|
) -> 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! {
|
view! {
|
||||||
<section class="inventory">
|
<section class=kind.class()>
|
||||||
<h3>{kind.to_string()}</h3>
|
<h3>{kind.to_string()}</h3>
|
||||||
<div class="balance">
|
<div class="balance">
|
||||||
<div title="PP (Platinum)" class="platinum">
|
<div title="PP (Platinum)" class="platinum">
|
||||||
@ -29,7 +48,18 @@ pub fn Inventory (
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="items">
|
<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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|||||||
64
src/components/item.rs
Normal file
64
src/components/item.rs
Normal 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>
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,6 +9,7 @@ pub mod sidebar;
|
|||||||
pub mod nav;
|
pub mod nav;
|
||||||
pub mod inventory;
|
pub mod inventory;
|
||||||
pub mod numput;
|
pub mod numput;
|
||||||
|
pub mod item;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Character () -> impl IntoView {
|
pub fn Character () -> impl IntoView {
|
||||||
@ -86,7 +87,7 @@ pub fn ItemSearchField () -> impl IntoView {
|
|||||||
|
|
||||||
|
|
||||||
#[component]
|
#[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()));
|
let card_class = format!("item-search-card {}", item.rarity.as_ref().unwrap_or(&String::new()));
|
||||||
view! {
|
view! {
|
||||||
<div class=card_class>
|
<div class=card_class>
|
||||||
|
|||||||
@ -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))} />
|
<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()} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@ use super::*;
|
|||||||
|
|
||||||
impl Dashboard {
|
impl Dashboard {
|
||||||
pub fn mock () -> Self {
|
pub fn mock () -> Self {
|
||||||
|
let (p_inv, c_inv) = generate_mock_inventory();
|
||||||
|
|
||||||
Dashboard {
|
Dashboard {
|
||||||
campaign: "В Поисках Дмитрия Шардалина".to_owned(),
|
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(),
|
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 },
|
hit_dice: HitDice { used: 1, kind: 20 },
|
||||||
death_save_throws: DeathSaveThrows { failed: 1, succeeded: 3 },
|
death_save_throws: DeathSaveThrows { failed: 1, succeeded: 3 },
|
||||||
prepared: generate_prepared_spells_list(),
|
prepared: generate_prepared_spells_list(),
|
||||||
inventory: generate_mock_inventory(),
|
inventory: p_inv,
|
||||||
},
|
},
|
||||||
common: CommonData {
|
common: CommonData {
|
||||||
balance: Balance { platinum: 1312, gold: 3211200, electrum: 231, silver: 1, copper: 21021 },
|
balance: Balance { platinum: 1312, gold: 3211200, electrum: 231, silver: 1, copper: 21021 },
|
||||||
inventory: generate_mock_inventory(),
|
inventory: c_inv,
|
||||||
},
|
},
|
||||||
quest_book: generate_mock_questbook(),
|
quest_book: generate_mock_questbook(),
|
||||||
notes: generate_mock_notebook(),
|
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()), ]), }), },
|
fn generate_mock_inventory() -> (Inventory, Inventory) {
|
||||||
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()), ]), }), },
|
let a = vec![
|
||||||
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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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()), ]), }, },
|
||||||
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()), ]), }), },
|
let b = vec![
|
||||||
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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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()), ]), }), },
|
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 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -8,14 +8,14 @@ use crate::prelude::*;
|
|||||||
mod mock;
|
mod mock;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Store)]
|
#[derive(Clone, Debug, Store)]
|
||||||
pub struct Item {
|
pub struct BookItem {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub weight: Option<f64>,
|
pub weight: Option<f64>,
|
||||||
pub rarity: Option<String>,
|
pub rarity: Option<String>,
|
||||||
pub rest: Map<String, Value>,
|
pub rest: Map<String, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Item {
|
impl BookItem {
|
||||||
pub fn from_json (t: Value) -> Option<Self> {
|
pub fn from_json (t: Value) -> Option<Self> {
|
||||||
let Value::String(name) = t["name"].clone() else { return None };
|
let Value::String(name) = t["name"].clone() else { return None };
|
||||||
let weight = match t["weight"].clone() {
|
let weight = match t["weight"].clone() {
|
||||||
@ -37,7 +37,7 @@ impl Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Store)]
|
#[derive(Clone, Debug, Store)]
|
||||||
pub struct ItemReferenceMap (pub HashMap<String, Item>);
|
pub struct ItemReferenceMap (pub HashMap<String, BookItem>);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Store)]
|
#[derive(Clone, Debug, Store)]
|
||||||
pub struct Dashboard {
|
pub struct Dashboard {
|
||||||
@ -90,24 +90,34 @@ pub struct Balance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Store)]
|
#[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 struct InventoryEntry {
|
||||||
|
pub id: u64,
|
||||||
pub quantity: u32,
|
pub quantity: u32,
|
||||||
pub item: InventoryItem,
|
pub item: InventoryItem,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Store)]
|
impl InventoryEntry {
|
||||||
pub enum InventoryItem {
|
pub fn key (&self) -> String {
|
||||||
Custom (CustomInventoryItem),
|
format!("{}-{}", self.quantity, self.item.name)
|
||||||
Book (Item),
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Store)]
|
// #[derive(Clone, Debug, Store)]
|
||||||
pub struct CustomInventoryItem {
|
// pub enum InventoryItem {
|
||||||
|
// Custom (CustomInventoryItem),
|
||||||
|
// Book (Item),
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Store, Default)]
|
||||||
|
pub struct InventoryItem {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub weight: Option<f32>,
|
pub weight: u32,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub tags: HashMap<String, 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 {
|
pub enum InventoryKind {
|
||||||
Personal,
|
Personal,
|
||||||
Common
|
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",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#![feature(let_chains)]
|
#![feature(type_alias_impl_trait)]
|
||||||
pub mod app;
|
pub mod app;
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
|
|||||||
@ -9,10 +9,11 @@ pub use anyhow::{ anyhow, bail };
|
|||||||
pub use reactive_stores::Store;
|
pub use reactive_stores::Store;
|
||||||
pub use leptos::{ web_sys::HtmlInputElement, ev::{ Event, Targeted }};
|
pub use leptos::{ web_sys::HtmlInputElement, ev::{ Event, Targeted }};
|
||||||
|
|
||||||
pub use crate::{
|
pub (crate) use crate::{
|
||||||
utils,
|
utils,
|
||||||
entities::*,
|
entities::{ self, * },
|
||||||
statics::*,
|
statics::*,
|
||||||
|
components::{ self, numput::{ Numput, NumputChange } },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Error = anyhow::Error;
|
pub type Error = anyhow::Error;
|
||||||
|
|||||||
@ -5,13 +5,13 @@ use leptos::web_sys::HtmlInputElement;
|
|||||||
use leptos::reactive::traits::{ Get, Set };
|
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 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 Value::Array(list) = json else { error!("Value isn't Array, skipping"); return 0 };
|
||||||
|
|
||||||
let start = map.len();
|
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) {
|
if map.contains_key(&t.name) {
|
||||||
let entry = map.get_mut(&t.name).unwrap();
|
let entry = map.get_mut(&t.name).unwrap();
|
||||||
entry.weight = entry.weight.or(t.weight);
|
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();
|
let max: f32 = max.into();
|
||||||
cur / max * 100.
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
section.inventory {
|
section.inventory {
|
||||||
width: 45%;
|
width: 50%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ section.inventory {
|
|||||||
h3 {
|
h3 {
|
||||||
text-transform: lowercase;
|
text-transform: lowercase;
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
margin-bottom: 10px;
|
margin: 0 20px 10px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
&::first-letter {
|
&::first-letter {
|
||||||
text-decoration: underline crimson;
|
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 {
|
input {
|
||||||
color: white;
|
color: white;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -104,4 +157,8 @@ section.inventory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user