Compare commits
No commits in common. "v2" and "main" have entirely different histories.
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "heroicons"]
|
||||
path = heroicons
|
||||
url = git@github.com:tailwindlabs/heroicons.git
|
||||
21
Cargo.toml
21
Cargo.toml
@ -9,22 +9,13 @@ leptos = { version = "0.7.1", features = ["csr"] }
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
js-sys = "0.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde-wasm-bindgen = "0.6"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
src-common = { path = "./src-common", features = ["store"] }
|
||||
reactive_stores = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
getrandom = { version = "*", features = ["js"] }
|
||||
|
||||
[workspace]
|
||||
members = ["src-common", "src-tauri"]
|
||||
|
||||
[workspace.dependencies]
|
||||
reactive_stores = "0.1.1"
|
||||
strum = "0.26.3"
|
||||
strum_macros = "0.26.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
reactive_stores = "0.1.2"
|
||||
uuid = { version = "1.11.0", features = ["v4", "serde"] }
|
||||
itertools = "0.13.0"
|
||||
serde_json = "1.0.133"
|
||||
|
||||
[workspace]
|
||||
members = ["src-tauri"]
|
||||
|
||||
12
Trunk.toml
12
Trunk.toml
@ -8,15 +8,3 @@ ignore = ["./src-tauri"]
|
||||
port = 1420
|
||||
open = false
|
||||
ws_protocol = "ws"
|
||||
|
||||
[[hooks]]
|
||||
stage = "build"
|
||||
command = "bun"
|
||||
command_arguments = [
|
||||
"x",
|
||||
"tailwindcss",
|
||||
"-i",
|
||||
"tailwind.css",
|
||||
"-o",
|
||||
"dist/.stage/tailwind.css",
|
||||
]
|
||||
|
||||
@ -1,60 +0,0 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
const inputFile: string = 'icon-list';
|
||||
const outputFile: string = 'src/components/icons.rs';
|
||||
const svgDir: string = 'heroicons/optimized';
|
||||
|
||||
function toComponentName(filePath: string): string {
|
||||
const parts = filePath.split('/');
|
||||
const size = parts[0];
|
||||
const style = parts[1];
|
||||
const fileName = parts[2].replace('.svg', '');
|
||||
|
||||
return fileName
|
||||
.split('-')
|
||||
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
||||
.join('')
|
||||
+ size.charAt(0).toUpperCase() + size.slice(1)
|
||||
+ style.charAt(0).toUpperCase() + style.slice(1);
|
||||
}
|
||||
|
||||
function getSizeClass(size: string): string {
|
||||
switch (size) {
|
||||
case '16': return 'size-4';
|
||||
case '20': return 'size-5';
|
||||
case '24': return 'size-6';
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
function addClassToSvg(svgContent: string, sizeClass: string): string {
|
||||
|
||||
// Add the size class
|
||||
svgContent = svgContent.replace('<svg', `<svg class="${sizeClass}"`);
|
||||
|
||||
return svgContent;
|
||||
}
|
||||
|
||||
function generateComponents(): void {
|
||||
const svgFiles: string[] = fs.readFileSync(inputFile, 'utf8').split('\n').filter(Boolean);
|
||||
let output: string = 'use leptos::prelude::*;\n\n';
|
||||
|
||||
svgFiles.forEach((file: string) => {
|
||||
const svgPath = `${file}.svg`;
|
||||
const parts = file.split('/');
|
||||
const size = parts[0];
|
||||
let svgContent: string = fs.readFileSync(path.join(svgDir, svgPath), 'utf8');
|
||||
const componentName: string = toComponentName(svgPath);
|
||||
const sizeClass = getSizeClass(size);
|
||||
|
||||
svgContent = addClassToSvg(svgContent, sizeClass);
|
||||
|
||||
output += `#[component]\npub fn ${componentName}() -> impl IntoView {\n`;
|
||||
output += ` view! { ${svgContent} }\n}\n\n`;
|
||||
});
|
||||
|
||||
fs.writeFileSync(outputFile, output);
|
||||
console.log('Components generated successfully.');
|
||||
}
|
||||
|
||||
generateComponents();
|
||||
@ -1 +0,0 @@
|
||||
Subproject commit fa902f44d071eac776758cfd6e0522cb7c37b1c3
|
||||
@ -1,8 +0,0 @@
|
||||
16/solid/plus
|
||||
16/solid/folder-plus
|
||||
16/solid/arrow-up
|
||||
16/solid/arrow-down
|
||||
16/solid/eye
|
||||
16/solid/eye-slash
|
||||
16/solid/chevron-down
|
||||
16/solid/chevron-right
|
||||
20
index.html
20
index.html
@ -1,13 +1,11 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Tauri + Leptos App</title>
|
||||
<link rel="stylesheet" href="tailwind.css" />
|
||||
<link data-trunk rel="rust" data-wasm-opt="z" />
|
||||
</head>
|
||||
|
||||
<body></body>
|
||||
|
||||
</html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Tauri + Leptos App</title>
|
||||
<link data-trunk rel="tailwind-css" href="tailwind.css" />
|
||||
<link data-trunk rel="copy-dir" href="public" />
|
||||
<link data-trunk rel="rust" data-wasm-opt="z" />
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
|
||||
@ -4,8 +4,7 @@
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@types/bun": "latest",
|
||||
"daisyui": "^4.12.23",
|
||||
"tailwindcss": "^3.4.17"
|
||||
"daisyui": "^4.12.23"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
[package]
|
||||
name = "src-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
uuid = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
reactive_stores = { workspace = true }
|
||||
|
||||
[features]
|
||||
store = []
|
||||
@ -1 +0,0 @@
|
||||
pub mod models;
|
||||
@ -1,19 +0,0 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RangedNumber<const MIN: u32, const MAX: u32>(u32);
|
||||
|
||||
impl<const MIN: u32, const MAX: u32> RangedNumber<MIN, MAX> {
|
||||
pub fn new(value: u32) -> Result<Self, String> {
|
||||
if value < MIN || value > MAX {
|
||||
Err(format!("Value {} is out of range ({} - {})", value, MIN, MAX))
|
||||
} else {
|
||||
Ok(RangedNumber(value))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min() -> u32 {MIN}
|
||||
pub fn max() -> u32 {MAX}
|
||||
|
||||
pub fn value(&self) -> u32 {self.0}
|
||||
}
|
||||
@ -1,217 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use itertools::Itertools;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::base::RangedNumber;
|
||||
|
||||
impl Filter {
|
||||
pub fn default_leaf() -> Self {
|
||||
Self {
|
||||
id: Uuid::new_v4(),
|
||||
enabled: true,
|
||||
name: "".to_string(),
|
||||
rule: Default::default(),
|
||||
remain: FilterRemain::Leaf(Default::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_group() -> Self {
|
||||
Self {
|
||||
id: Uuid::new_v4(),
|
||||
enabled: true,
|
||||
name: "".to_string(),
|
||||
rule: Default::default(),
|
||||
remain: FilterRemain::Group(Default::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "store", derive(reactive_stores::Store))]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Filter {
|
||||
pub id: uuid::Uuid,
|
||||
pub enabled: bool,
|
||||
pub name: String,
|
||||
pub rule: FilterRule,
|
||||
pub remain: FilterRemain,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "store", derive(reactive_stores::Store))]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum FilterRemain {
|
||||
Leaf(FilterLeaf),
|
||||
Group(FilterGroup),
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "store", derive(reactive_stores::Store))]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct FilterLeaf {
|
||||
pub show: bool,
|
||||
}
|
||||
impl Default for FilterLeaf {
|
||||
fn default() -> Self {
|
||||
Self { show: true }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "store", derive(reactive_stores::Store))]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct FilterGroup {
|
||||
#[cfg_attr(feature = "store", store(key: String = |row| row.id.to_string()))]
|
||||
pub filters: Vec<Filter>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Color {
|
||||
pub r: u8,
|
||||
pub g: u8,
|
||||
pub b: u8,
|
||||
pub a: u8,
|
||||
}
|
||||
|
||||
pub type Level = RangedNumber<1, 100>;
|
||||
|
||||
#[cfg_attr(feature = "store", derive(reactive_stores::Store))]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct FilterRule {
|
||||
pub class: Option<Vec<ItemClass>>,
|
||||
pub base_type: Option<Vec<ItemBaseType>>,
|
||||
pub area_level: Option<(Op, Level)>,
|
||||
pub drop_level: Option<(Op, Level)>,
|
||||
pub item_level: Option<(Op, Level)>,
|
||||
pub rarity: Option<(Op, ItemRarity)>,
|
||||
pub sockets: Option<(Op, u32)>,
|
||||
pub quality: Option<(Op, u32)>,
|
||||
pub stack_size: Option<(Op, u32)>,
|
||||
|
||||
// waystones
|
||||
pub waystone_tier: Option<(Op, RangedNumber<1, 16>)>,
|
||||
|
||||
// effects
|
||||
pub set_font_size: Option<RangedNumber<1, 45>>,
|
||||
pub set_text_color: Option<Color>,
|
||||
pub set_border_color: Option<Color>,
|
||||
pub set_background_color: Option<Color>,
|
||||
pub play_alert_sound: Option<(RangedNumber<1, 16>, RangedNumber<0, 300>)>,
|
||||
pub play_effect: Option<GameColor>,
|
||||
pub minimap_icon: Option<(RangedNumber<0, 2>, GameColor, MinimapIconShape)>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Settings {
|
||||
base_types: Vec<String>,
|
||||
classes: Vec<String>,
|
||||
rarities: Vec<String>,
|
||||
minimap_icon_shapes: Vec<String>,
|
||||
game_colors: Vec<String>,
|
||||
ops: Vec<String>,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
fn format_err(value: &str, category: &str, options: &Vec<String>) -> String {
|
||||
format!(
|
||||
"{} is not a valid {}, options are: {}",
|
||||
value,
|
||||
category,
|
||||
options.iter().join(" ")
|
||||
)
|
||||
}
|
||||
pub fn base_type(&self, s: &String) -> Result<ItemBaseType, String> {
|
||||
if !self.base_types.contains(s) {
|
||||
Err(Self::format_err(s, "BaseType", &self.base_types))
|
||||
} else {
|
||||
Ok(ItemBaseType(s.to_string()))
|
||||
}
|
||||
}
|
||||
pub fn class(&self, s: &String) -> Result<ItemClass, String> {
|
||||
if !self.classes.contains(s) {
|
||||
Err(Self::format_err(s, "Class", &self.classes))
|
||||
} else {
|
||||
Ok(ItemClass(s.to_string()))
|
||||
}
|
||||
}
|
||||
pub fn rarity(&self, s: &String) -> Result<ItemRarity, String> {
|
||||
if !self.rarities.contains(s) {
|
||||
Err(Self::format_err(s, "Rarity", &self.rarities))
|
||||
} else {
|
||||
Ok(ItemRarity(s.to_string()))
|
||||
}
|
||||
}
|
||||
pub fn op(&self, s: &String) -> Result<Op, String> {
|
||||
if !self.ops.contains(s) {
|
||||
Err(Self::format_err(s, "Operator", &self.ops))
|
||||
} else {
|
||||
Ok(Op(s.to_string()))
|
||||
}
|
||||
}
|
||||
pub fn minimap_icon_shape(&self, s: &String) -> Result<MinimapIconShape, String> {
|
||||
if !self.minimap_icon_shapes.contains(s) {
|
||||
Err(Self::format_err(
|
||||
s,
|
||||
"MinimapIconShape",
|
||||
&self.minimap_icon_shapes,
|
||||
))
|
||||
} else {
|
||||
Ok(MinimapIconShape(s.to_string()))
|
||||
}
|
||||
}
|
||||
pub fn game_color(&self, s: &String) -> Result<GameColor, String> {
|
||||
if !self.game_colors.contains(s) {
|
||||
Err(Self::format_err(s, "GameColor", &self.game_colors))
|
||||
} else {
|
||||
Ok(GameColor(s.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ItemBaseType(String);
|
||||
impl ItemBaseType {
|
||||
pub fn value(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ItemClass(String);
|
||||
impl ItemClass {
|
||||
pub fn value(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct ItemRarity(String);
|
||||
impl ItemRarity {
|
||||
pub fn value(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct MinimapIconShape(String);
|
||||
impl MinimapIconShape {
|
||||
pub fn value(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct GameColor(String);
|
||||
impl GameColor {
|
||||
pub fn value(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Op(String);
|
||||
|
||||
impl Op {
|
||||
pub fn value(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
pub mod loot_filter;
|
||||
pub mod base;
|
||||
@ -20,13 +20,12 @@ tauri-build = { version = "2", features = [] }
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri-plugin-opener = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
strum_macros = "0.26.4"
|
||||
strum = "0.26.3"
|
||||
reqwest = { version = "0.12.9", features = ["blocking"] }
|
||||
serde_json = "1.0.133"
|
||||
thiserror = "2.0.8"
|
||||
directories = "5.0.1"
|
||||
indoc = "2.0.5"
|
||||
open = "5.3.1"
|
||||
src-common = { path = "../src-common" }
|
||||
serde = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
|
||||
403
src/app.rs
403
src/app.rs
@ -1,14 +1,11 @@
|
||||
use leptos::{logging::log, prelude::*, tachys::reactive_graph::bind::IntoSplitSignal};
|
||||
use reactive_stores::{Field, Store, StoreField};
|
||||
use leptos::prelude::{Read as Reads, *};
|
||||
use reactive_stores::Store;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_wasm_bindgen::from_value;
|
||||
use src_common::models::loot_filter::{Filter, FilterStoreFields};
|
||||
use strum_macros::EnumIter;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
|
||||
use crate::components::{self, filter_detail::FilterDetail, filter_root::FilterRoot, icons};
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
// invoke without arguments
|
||||
@ -21,108 +18,326 @@ extern "C" {
|
||||
|
||||
// They need to have different names!
|
||||
}
|
||||
#[derive(Store, Debug, Clone, Serialize, Deserialize)]
|
||||
struct POE2FilterConfig {
|
||||
#[store(key: String = |row| row.kind.to_string())]
|
||||
armours: Vec<ArmourFilter>,
|
||||
#[store(key: String = |row| row.kind.to_string())]
|
||||
weapons: Vec<WeaponFilter>,
|
||||
#[store(key: String = |row| row.kind.to_string())]
|
||||
accessories: Vec<AccessoryFiler>,
|
||||
settings: FilterSettings,
|
||||
}
|
||||
|
||||
#[derive(Store, Clone)]
|
||||
struct AppStore {
|
||||
root: Filter,
|
||||
selected_filter: Option<Vec<Field<Filter>>>,
|
||||
#[derive(Store, Debug, Clone, Serialize, Deserialize, Default)]
|
||||
struct FilterSettings {
|
||||
max_level: u32,
|
||||
}
|
||||
|
||||
#[derive(Store, Debug, Clone, strum_macros::Display, EnumIter, Serialize, Deserialize)]
|
||||
#[strum(serialize_all = "title_case")]
|
||||
enum ArmourKind {
|
||||
BodyArmours,
|
||||
Helmets,
|
||||
Boots,
|
||||
Gloves,
|
||||
}
|
||||
|
||||
#[derive(Store, Debug, Clone, strum_macros::Display, EnumIter, Serialize, Deserialize)]
|
||||
#[strum(serialize_all = "title_case")]
|
||||
enum WeaponKind {
|
||||
OneHandMaces,
|
||||
TwoHandMaces,
|
||||
Crossbows,
|
||||
Bows,
|
||||
Quivers,
|
||||
Sceptres,
|
||||
Wands,
|
||||
Staves,
|
||||
Quarterstaves,
|
||||
Shields,
|
||||
Foci,
|
||||
}
|
||||
|
||||
#[derive(Store, Debug, Clone, strum_macros::Display, EnumIter, Serialize, Deserialize)]
|
||||
#[strum(serialize_all = "title_case")]
|
||||
enum AccessoryKind {}
|
||||
|
||||
#[derive(Store, Debug, Clone, Serialize, Deserialize)]
|
||||
struct ArmourFilter {
|
||||
kind: ArmourKind,
|
||||
armour: bool,
|
||||
evasion: bool,
|
||||
energy_shield: bool,
|
||||
armour_evasion: bool,
|
||||
armour_energy_shield: bool,
|
||||
evasion_energy_shield: bool,
|
||||
}
|
||||
#[derive(Store, Debug, Clone, Serialize, Deserialize)]
|
||||
struct WeaponFilter {
|
||||
kind: WeaponKind,
|
||||
show: bool,
|
||||
}
|
||||
|
||||
#[derive(Store, Debug, Clone, Serialize, Deserialize)]
|
||||
struct AccessoryFiler {
|
||||
kind: AccessoryKind,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct UpdateArgs {
|
||||
config: POE2FilterConfig,
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Main() -> impl IntoView {
|
||||
let store = Store::new(AppStore {
|
||||
root: Filter::default_group(),
|
||||
selected_filter: None,
|
||||
fn Main(config: POE2FilterConfig) -> impl IntoView {
|
||||
let store = Store::new(config);
|
||||
Effect::new(move |_| {
|
||||
let config = store.get();
|
||||
spawn_local(async move {
|
||||
let args = serde_wasm_bindgen::to_value(&UpdateArgs { config: config }).unwrap();
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
invoke("update", args).await;
|
||||
});
|
||||
});
|
||||
let filter = store.root();
|
||||
// Effect::new(move |_| {
|
||||
// let config = store.get();
|
||||
// spawn_local(async move {
|
||||
// let args = serde_wasm_bindgen::to_value(&UpdateArgs { config: config }).unwrap();
|
||||
// // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
// invoke("update", args).await;
|
||||
// });
|
||||
// });
|
||||
|
||||
let right = move || {
|
||||
let selected = store.selected_filter().get();
|
||||
match selected {
|
||||
Some(selected) => {
|
||||
if selected[0].id().get() == store.root().id().get() {
|
||||
FilterRoot().into_any()
|
||||
} else {
|
||||
view! { <FilterDetail filter=selected /> }.into_any()
|
||||
}
|
||||
}
|
||||
None => FilterRoot().into_any(),
|
||||
}
|
||||
};
|
||||
|
||||
let max_level = store.settings().max_level();
|
||||
view! {
|
||||
<main class="container p-4 max-w-none h-screen">
|
||||
<article class="prose">
|
||||
<h2>POE2 Loot Filter Config</h2>
|
||||
</article>
|
||||
<div class="flex">
|
||||
<div class="flex-1">
|
||||
<ul class="menu menu-xs">
|
||||
<components::filter::Filter
|
||||
filter=filter
|
||||
selected=store.selected_filter()
|
||||
on_action=move |action| {
|
||||
log!("action");
|
||||
if let components::filter::Action::Select(f) = action {
|
||||
log!("select");
|
||||
store.selected_filter().set(Some(f));
|
||||
}
|
||||
}
|
||||
root=true
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex-1">{right}</div>
|
||||
<main class="container p-4 prose max-w-none">
|
||||
<ul>
|
||||
<li class="not-prose">
|
||||
"Config folder: "
|
||||
<button
|
||||
class="btn btn-xs btn-link"
|
||||
on:click=|_| {
|
||||
spawn_local(async {
|
||||
invoke_without_args("open_config_folder").await;
|
||||
});
|
||||
}
|
||||
>
|
||||
"%appdata%\\LeonLiu\\POE2 Loot Filter\\config"
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
"Changes on the UI will immediately write the updated filter file into POE2's config folder. Reload the filter in-game after changes on the UI."
|
||||
</li>
|
||||
<li>"The output filter is named " <strong>Leon</strong>.</li>
|
||||
<li>
|
||||
"Based on "
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://github.com/NeverSinkDev/NeverSink-PoE2litefilter"
|
||||
>
|
||||
NeverSink-PoE2litefilter
|
||||
</a>, will download on every app launch, saved as
|
||||
" base_filter.filter in the config folder".
|
||||
</li>
|
||||
<li class="not-prose">
|
||||
"In areas lower than level"
|
||||
<input
|
||||
class="input input-xs w-12 input-bordered"
|
||||
type="number"
|
||||
min="60"
|
||||
max="100"
|
||||
prop:value=max_level.get()
|
||||
on:change:target=move |ev| {
|
||||
*store.settings().max_level().write() = ev
|
||||
.target()
|
||||
.value()
|
||||
.parse::<u32>()
|
||||
.unwrap_or(70);
|
||||
}
|
||||
/> ",hide normal or magic items, unless turned on in configurations below."
|
||||
</li>
|
||||
<li>
|
||||
{move || {
|
||||
format!(
|
||||
"From area level 11 to {}, hide normal or magic items of which the drop level is 10 level lower than the area level.",
|
||||
max_level.get(),
|
||||
)
|
||||
}}
|
||||
</li>
|
||||
</ul>
|
||||
<h2>Weapons</h2>
|
||||
<p>
|
||||
{move || {
|
||||
format!(
|
||||
"In areas lower than level {}, only show normal or magic weapons selected.",
|
||||
max_level.get(),
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<div class="flex flex-wrap">
|
||||
<For
|
||||
each=move || store.weapons()
|
||||
key=|row| row.read().kind.to_string()
|
||||
children=|child| {
|
||||
let kind = child.clone().kind().get().to_string();
|
||||
let show = child.show();
|
||||
view! {
|
||||
<label class="label cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
prop:checked=show.get()
|
||||
on:change:target=move |ev| {
|
||||
*show.write() = event_target_checked(&ev);
|
||||
}
|
||||
/>
|
||||
<span class="ml-1 label-text">{kind.clone()}</span>
|
||||
</label>
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h2>Armours</h2>
|
||||
<p>
|
||||
{move || {
|
||||
format!(
|
||||
"In areas lower than level {}, only show normal or magic armours with selected base defence types.",
|
||||
max_level.get(),
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<div>
|
||||
<For
|
||||
each=move || store.armours()
|
||||
key=|row| row.read().kind.to_string()
|
||||
children=|child| {
|
||||
let kind = child.clone().kind().get().to_string();
|
||||
let armour = child.clone().armour();
|
||||
let evasion = child.clone().evasion();
|
||||
let energy_shield = child.clone().energy_shield();
|
||||
let armour_evasion = child.clone().armour_evasion();
|
||||
let armour_energy_shield = child.clone().armour_energy_shield();
|
||||
let evasion_energy_shield = child.clone().evasion_energy_shield();
|
||||
view! {
|
||||
<h3>{kind.clone()}</h3>
|
||||
<div class="flex flex-wrap">
|
||||
<label class="label cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
id=kind.clone() + "Armour"
|
||||
name=kind.clone() + "Armour"
|
||||
prop:checked=armour.get()
|
||||
on:change:target=move |ev| {
|
||||
*armour.write() = event_target_checked(&ev);
|
||||
}
|
||||
/>
|
||||
<span class="ml-1 label-text">"Armour"</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
id=kind.clone() + "Evasion"
|
||||
name=kind.clone() + "Evasion"
|
||||
prop:checked=evasion.get()
|
||||
on:change:target=move |ev| {
|
||||
*evasion.write() = event_target_checked(&ev);
|
||||
}
|
||||
/>
|
||||
<span class="ml-1 label-text">"Evasion"</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
id=kind.clone() + "EnergyShield"
|
||||
name=kind.clone() + "EnergyShield"
|
||||
prop:checked=energy_shield.get()
|
||||
on:change:target=move |ev| {
|
||||
*energy_shield.write() = event_target_checked(&ev);
|
||||
}
|
||||
/>
|
||||
<span class="ml-1 label-text">"Energy Shield"</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
id=kind.clone() + "ArmourEvasion"
|
||||
name=kind.clone() + "ArmourEvasion"
|
||||
prop:checked=armour_evasion.get()
|
||||
on:change:target=move |ev| {
|
||||
*armour_evasion.write() = event_target_checked(&ev);
|
||||
}
|
||||
/>
|
||||
<span class="ml-1 label-text">"Armour + Evasion"</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
id=kind.clone() + "ArmourEnergyShield"
|
||||
name=kind.clone() + "ArmourEnergyShield"
|
||||
prop:checked=armour_energy_shield.get()
|
||||
on:change:target=move |ev| {
|
||||
*armour_energy_shield.write() = event_target_checked(&ev);
|
||||
}
|
||||
/>
|
||||
<span class="ml-1 label-text">"Armour + Energy Shield"</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
id=kind.clone() + "EvasionEnergyShield"
|
||||
name=kind.clone() + "EvasionEnergyShield"
|
||||
prop:checked=evasion_energy_shield.get()
|
||||
on:change:target=move |ev| {
|
||||
*evasion_energy_shield.write() = event_target_checked(&ev);
|
||||
}
|
||||
/>
|
||||
<span class="ml-1 label-text">"Evasion + Energy Shield"</span>
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
}
|
||||
}
|
||||
|
||||
// async fn load_data() -> POE2FilterConfig {
|
||||
// from_value(invoke_without_args("get_config").await).unwrap()
|
||||
// }
|
||||
async fn load_data() -> POE2FilterConfig {
|
||||
from_value(invoke_without_args("get_config").await).unwrap()
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn App() -> impl IntoView {
|
||||
view! { <Main /> }
|
||||
// let config = LocalResource::new(move || load_data());
|
||||
// view! {
|
||||
// <Suspense fallback=move || {
|
||||
// view! {
|
||||
// <main class="flex items-center justify-center h-screen">
|
||||
// <div class="inline-flex items-center justify-start gap-1">
|
||||
// <svg
|
||||
// role="status"
|
||||
// class="mr-2 h-8 w-8 animate-spin fill-green-600 text-gray-100 dark:text-gray-600"
|
||||
// viewBox="0 0 100 101"
|
||||
// fill="none"
|
||||
// xmlns="http://www.w3.org/2000/svg"
|
||||
// >
|
||||
// <path
|
||||
// d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||
// fill="currentColor"
|
||||
// ></path>
|
||||
// <path
|
||||
// d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||
// fill="currentFill"
|
||||
// ></path>
|
||||
// </svg>
|
||||
// <span>Loading...</span>
|
||||
// </div>
|
||||
// </main>
|
||||
// }
|
||||
// }>
|
||||
// {move || {
|
||||
// config.get().as_deref().map(|config| view! { <Main config=config.clone() /> })
|
||||
// }}
|
||||
// </Suspense>
|
||||
// }
|
||||
let config = LocalResource::new(move || load_data());
|
||||
view! {
|
||||
<Suspense fallback=move || {
|
||||
view! {
|
||||
<main class="flex items-center justify-center h-screen">
|
||||
<div class="inline-flex items-center justify-start gap-1">
|
||||
<svg
|
||||
role="status"
|
||||
class="mr-2 h-8 w-8 animate-spin fill-green-600 text-gray-100 dark:text-gray-600"
|
||||
viewBox="0 0 100 101"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
<path
|
||||
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||
fill="currentFill"
|
||||
></path>
|
||||
</svg>
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
</main>
|
||||
}
|
||||
}>
|
||||
{move || {
|
||||
config.get().as_deref().map(|config| view! { <Main config=config.clone() /> })
|
||||
}}
|
||||
</Suspense>
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,232 +0,0 @@
|
||||
use leptos::{either::Either, ev::MouseEvent, prelude::*};
|
||||
use reactive_stores::Field;
|
||||
use src_common::models::loot_filter::{
|
||||
Filter, FilterGroup, FilterGroupStoreFields, FilterLeaf, FilterLeafStoreFields, FilterRemain,
|
||||
FilterRemainStoreFields, FilterStoreFields,
|
||||
};
|
||||
|
||||
use crate::components::icons;
|
||||
|
||||
fn group(
|
||||
filter: Field<Filter>,
|
||||
group: Field<FilterGroup>,
|
||||
on_action: Callback<(Action,)>,
|
||||
selected: Field<Option<Vec<Field<Filter>>>>,
|
||||
root: bool,
|
||||
) -> impl IntoView {
|
||||
let header = move || {
|
||||
if root {
|
||||
"Root".to_string()
|
||||
} else {
|
||||
filter.id().get().to_string()
|
||||
}
|
||||
};
|
||||
let (expanded, set_expanded) = signal(false);
|
||||
|
||||
let new_rule = move |ev: MouseEvent| {
|
||||
ev.stop_propagation();
|
||||
group.filters().write().push(Filter::default_leaf());
|
||||
set_expanded.set(true);
|
||||
};
|
||||
let new_group = move |ev: MouseEvent| {
|
||||
ev.stop_propagation();
|
||||
group.filters().write().push(Filter::default_group());
|
||||
set_expanded.set(true);
|
||||
};
|
||||
let nav_icon = move || {
|
||||
view! {
|
||||
<button on:click=move |ev| {
|
||||
ev.stop_propagation();
|
||||
set_expanded
|
||||
.update(|v| {
|
||||
*v = !*v;
|
||||
});
|
||||
}>
|
||||
{if expanded.get() {
|
||||
Either::Left(view! { <icons::ChevronDown16Solid /> })
|
||||
} else {
|
||||
Either::Right(view! { <icons::ChevronRight16Solid /> })
|
||||
}}
|
||||
</button>
|
||||
}
|
||||
};
|
||||
let active = move || match selected.get() {
|
||||
Some(f) => filter.id().get() == f[0].id().get(),
|
||||
None => false,
|
||||
};
|
||||
view! {
|
||||
<li>
|
||||
<a
|
||||
class=("active", active)
|
||||
on:click=move |_| {
|
||||
on_action.run((Action::Select(vec![filter]),));
|
||||
}
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
{nav_icon}
|
||||
<button on:click=move |ev| {
|
||||
ev.stop_propagation();
|
||||
on_action.run((Action::Up,));
|
||||
}>
|
||||
<icons::ArrowUp16Solid />
|
||||
</button>
|
||||
<button on:click=move |ev| {
|
||||
ev.stop_propagation();
|
||||
on_action.run((Action::Down,));
|
||||
}>
|
||||
<icons::ArrowDown16Solid />
|
||||
</button>
|
||||
</div>
|
||||
{header}
|
||||
<div class="flex gap-2">
|
||||
<button class="tooltip" data-tip="New Rule">
|
||||
<icons::Plus16Solid on:click=new_rule />
|
||||
</button>
|
||||
<button class="tooltip" data-tip="New Group">
|
||||
<icons::FolderPlus16Solid on:click=new_group />
|
||||
</button>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle toggle-xs"
|
||||
disabled=root
|
||||
prop:checked=move || filter.enabled().get()
|
||||
on:click=move |ev| {
|
||||
ev.stop_propagation();
|
||||
}
|
||||
on:change:target=move |ev| {
|
||||
filter.enabled().set(event_target_checked(&ev));
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
<ul class=("hidden", move || !expanded.get())>
|
||||
<For
|
||||
each=move || group.filters()
|
||||
key=|row| row.read().id.to_string()
|
||||
children=move |child| {
|
||||
let id = child.clone().id().get();
|
||||
view! {
|
||||
<Filter
|
||||
filter=child
|
||||
selected
|
||||
on_action=move |action| {
|
||||
let i = group
|
||||
.filters()
|
||||
.into_iter()
|
||||
.position(|f| f.id().get() == id)
|
||||
.unwrap();
|
||||
match action {
|
||||
Action::Up => {
|
||||
if i > 0 {
|
||||
group.filters().write().swap(i, i - 1);
|
||||
}
|
||||
}
|
||||
Action::Down => {
|
||||
if i < group.filters().get().len() - 1 {
|
||||
group.filters().write().swap(i, i + 1);
|
||||
}
|
||||
}
|
||||
Action::Select(mut v) => {
|
||||
v.push(filter);
|
||||
on_action.run((Action::Select(v),));
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
}
|
||||
}
|
||||
/>
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
|
||||
fn leaf(
|
||||
filter: Field<Filter>,
|
||||
leaf: Field<FilterLeaf>,
|
||||
on_action: Callback<(Action,)>,
|
||||
selected: Field<Option<Vec<Field<Filter>>>>,
|
||||
) -> impl IntoView {
|
||||
let header = move || filter.id().get().to_string();
|
||||
|
||||
let icon = move || {
|
||||
if leaf.show().get() {
|
||||
Either::Left(view! { <icons::Eye16Solid /> })
|
||||
} else {
|
||||
Either::Right(view! { <icons::EyeSlash16Solid /> })
|
||||
}
|
||||
};
|
||||
let active = move || match selected.get() {
|
||||
Some(f) => filter.id().get() == f[0].id().get(),
|
||||
None => false,
|
||||
};
|
||||
view! {
|
||||
<li>
|
||||
<a
|
||||
class=("active", active)
|
||||
on:click=move |_| {
|
||||
on_action.run((Action::Select(vec![filter]),));
|
||||
}
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
{icon}
|
||||
<button on:click=move |ev| {
|
||||
ev.stop_propagation();
|
||||
on_action.run((Action::Up,));
|
||||
}>
|
||||
<icons::ArrowUp16Solid />
|
||||
</button>
|
||||
<button on:click=move |ev| {
|
||||
ev.stop_propagation();
|
||||
on_action.run((Action::Down,));
|
||||
}>
|
||||
<icons::ArrowDown16Solid />
|
||||
</button>
|
||||
</div>
|
||||
{header}
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle toggle-xs"
|
||||
prop:checked=move || filter.enabled().get()
|
||||
on:click=move |ev| {
|
||||
ev.stop_propagation();
|
||||
}
|
||||
on:change:target=move |ev| {
|
||||
filter.enabled().set(event_target_checked(&ev));
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Action {
|
||||
Up,
|
||||
Down,
|
||||
Select(Vec<Field<Filter>>),
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Filter(
|
||||
#[prop(into)] filter: Field<Filter>,
|
||||
#[prop(into)] on_action: Callback<(Action,)>,
|
||||
#[prop(into)] selected: Field<Option<Vec<Field<Filter>>>>,
|
||||
#[prop(optional)] root: bool,
|
||||
) -> impl IntoView {
|
||||
let remain = filter.remain();
|
||||
match remain.get_untracked() {
|
||||
FilterRemain::Leaf(_) => {
|
||||
leaf(filter, remain.leaf_0().unwrap().into(), on_action, selected).into_any()
|
||||
}
|
||||
FilterRemain::Group(_) => group(
|
||||
filter,
|
||||
remain.group_0().unwrap().into(),
|
||||
on_action,
|
||||
selected,
|
||||
root,
|
||||
)
|
||||
.into_any(),
|
||||
}
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
use leptos::prelude::*;
|
||||
use reactive_stores::{Field, OptionStoreExt};
|
||||
use src_common::models::loot_filter::{
|
||||
Filter, FilterLeafStoreFields, FilterRemain, FilterRemainStoreFields, FilterRule,
|
||||
FilterRuleStoreFields, FilterStoreFields,
|
||||
};
|
||||
|
||||
#[component]
|
||||
pub fn FilterDetail(#[prop(into)] filter: Vec<Field<Filter>>) -> impl IntoView {
|
||||
let (first, rest) = filter.split_first().unwrap();
|
||||
view! {
|
||||
<article class="prose">
|
||||
<h3>Filter details:</h3>
|
||||
</article>
|
||||
{match first.remain().get_untracked() {
|
||||
FilterRemain::Leaf(_) => {
|
||||
let leaf = first.remain().leaf_0().unwrap();
|
||||
Some(
|
||||
view! {
|
||||
<div class="flex gap-2 items-center">
|
||||
<label class="label">Hide</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle"
|
||||
prop:checked=move || leaf.show().get()
|
||||
on:change:target=move |ev| {
|
||||
leaf.show().set(event_target_checked(&ev));
|
||||
}
|
||||
/>
|
||||
<label class="label">Show</label>
|
||||
</div>
|
||||
},
|
||||
)
|
||||
}
|
||||
FilterRemain::Group(_) => None,
|
||||
}}
|
||||
<RuleDetail rule=first.rule() />
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn RuleDetail(#[prop(into)] rule: Field<FilterRule>) -> impl IntoView {
|
||||
let class = view! {
|
||||
<tr class="flex items-center gap-2">
|
||||
<td>
|
||||
<input type="checkbox" class="checkbox" />
|
||||
</td>
|
||||
<td>
|
||||
<label class="label">Class</label>
|
||||
</td>
|
||||
<td>
|
||||
<label class="label">Class</label>
|
||||
</td>
|
||||
</tr>
|
||||
};
|
||||
view! {
|
||||
<table class="table">
|
||||
<tbody>{class}</tbody>
|
||||
</table>
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
use leptos::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn FilterRoot() -> impl IntoView {
|
||||
view! {
|
||||
<article class="prose">
|
||||
<h3>How to use:</h3>
|
||||
</article>
|
||||
}
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
use leptos::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn Plus16Solid() -> impl IntoView {
|
||||
view! { <svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"/>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn FolderPlus16Solid() -> impl IntoView {
|
||||
view! { <svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path fill-rule="evenodd" d="M3.5 2A1.5 1.5 0 0 0 2 3.5v9A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 12.5 4H9.621a1.5 1.5 0 0 1-1.06-.44L7.439 2.44A1.5 1.5 0 0 0 6.38 2H3.5ZM8 6a.75.75 0 0 1 .75.75v1.5h1.5a.75.75 0 0 1 0 1.5h-1.5v1.5a.75.75 0 0 1-1.5 0v-1.5h-1.5a.75.75 0 0 1 0-1.5h1.5v-1.5A.75.75 0 0 1 8 6Z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn ArrowUp16Solid() -> impl IntoView {
|
||||
view! { <svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path fill-rule="evenodd" d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn ArrowDown16Solid() -> impl IntoView {
|
||||
view! { <svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path fill-rule="evenodd" d="M8 2a.75.75 0 0 1 .75.75v8.69l3.22-3.22a.75.75 0 1 1 1.06 1.06l-4.5 4.5a.75.75 0 0 1-1.06 0l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.22 3.22V2.75A.75.75 0 0 1 8 2Z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Eye16Solid() -> impl IntoView {
|
||||
view! { <svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"/>
|
||||
<path fill-rule="evenodd" d="M1.38 8.28a.87.87 0 0 1 0-.566 7.003 7.003 0 0 1 13.238.006.87.87 0 0 1 0 .566A7.003 7.003 0 0 1 1.379 8.28ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn EyeSlash16Solid() -> impl IntoView {
|
||||
view! { <svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path fill-rule="evenodd" d="M3.28 2.22a.75.75 0 0 0-1.06 1.06l10.5 10.5a.75.75 0 1 0 1.06-1.06l-1.322-1.323a7.012 7.012 0 0 0 2.16-3.11.87.87 0 0 0 0-.567A7.003 7.003 0 0 0 4.82 3.76l-1.54-1.54Zm3.196 3.195 1.135 1.136A1.502 1.502 0 0 1 9.45 8.389l1.136 1.135a3 3 0 0 0-4.109-4.109Z" clip-rule="evenodd"/>
|
||||
<path d="m7.812 10.994 1.816 1.816A7.003 7.003 0 0 1 1.38 8.28a.87.87 0 0 1 0-.566 6.985 6.985 0 0 1 1.113-2.039l2.513 2.513a3 3 0 0 0 2.806 2.806Z"/>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn ChevronDown16Solid() -> impl IntoView {
|
||||
view! { <svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path fill-rule="evenodd" d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn ChevronRight16Solid() -> impl IntoView {
|
||||
view! { <svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path fill-rule="evenodd" d="M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
pub mod filter;
|
||||
pub mod filter_detail;
|
||||
pub mod filter_root;
|
||||
pub mod icons;
|
||||
@ -1,5 +1,4 @@
|
||||
mod app;
|
||||
mod components;
|
||||
|
||||
use app::*;
|
||||
use leptos::prelude::*;
|
||||
@ -7,6 +6,8 @@ use leptos::prelude::*;
|
||||
fn main() {
|
||||
console_error_panic_hook::set_once();
|
||||
mount_to_body(|| {
|
||||
view! { <App /> }
|
||||
view! {
|
||||
<App/>
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user