pinakes-ui: streamline sidebar design
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I0176fa480e5ba40eea5a39685a4f97896a6a6964
This commit is contained in:
parent
3e14bbe607
commit
278bcaa4b0
25 changed files with 1805 additions and 1686 deletions
|
|
@ -148,7 +148,11 @@ pub fn Import(
|
|||
}
|
||||
}
|
||||
},
|
||||
if is_importing { "Importing..." } else { "Import" }
|
||||
if is_importing {
|
||||
"Importing..."
|
||||
} else {
|
||||
"Import"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -157,9 +161,9 @@ pub fn Import(
|
|||
ImportOptions {
|
||||
tags: tags.clone(),
|
||||
collections: collections.clone(),
|
||||
selected_tags: selected_tags,
|
||||
new_tags_input: new_tags_input,
|
||||
selected_collection: selected_collection,
|
||||
selected_tags,
|
||||
new_tags_input,
|
||||
selected_collection,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -244,32 +248,48 @@ pub fn Import(
|
|||
let min = *filter_min_size.read();
|
||||
let max = *filter_max_size.read();
|
||||
|
||||
let filtered: Vec<&DirectoryPreviewFile> = preview_files.iter().filter(|f| {
|
||||
let type_idx = match type_badge_class(&f.media_type) {
|
||||
"type-audio" => 0,
|
||||
"type-video" => 1,
|
||||
"type-image" => 2,
|
||||
"type-document" => 3,
|
||||
"type-text" => 4,
|
||||
_ => 5,
|
||||
};
|
||||
if !types_snapshot[type_idx] { return false; }
|
||||
if min > 0 && f.file_size < min { return false; }
|
||||
if max > 0 && f.file_size > max { return false; }
|
||||
true
|
||||
}).collect();
|
||||
let filtered: Vec<&DirectoryPreviewFile> = preview_files
|
||||
|
||||
// Read selection once for display
|
||||
.iter()
|
||||
|
||||
// Filter bar
|
||||
|
||||
// Selection toolbar
|
||||
|
||||
// Deselect all filtered
|
||||
// Select all filtered
|
||||
.filter(|f| {
|
||||
let type_idx = match type_badge_class(&f.media_type) {
|
||||
"type-audio" => 0,
|
||||
"type-video" => 1,
|
||||
"type-image" => 2,
|
||||
"type-document" => 3,
|
||||
"type-text" => 4,
|
||||
_ => 5,
|
||||
};
|
||||
if !types_snapshot[type_idx] {
|
||||
return false;
|
||||
}
|
||||
if min > 0 && f.file_size < min {
|
||||
return false;
|
||||
}
|
||||
if max > 0 && f.file_size > max {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
})
|
||||
.collect();
|
||||
let filtered_count = filtered.len();
|
||||
let total_count = preview_files.len();
|
||||
|
||||
// Read selection once for display
|
||||
let selection = selected_file_paths.read().clone();
|
||||
let selected_count = selection.len();
|
||||
let all_filtered_selected = !filtered.is_empty()
|
||||
&& filtered.iter().all(|f| selection.contains(&f.path));
|
||||
|
||||
let filtered_paths: Vec<String> = filtered.iter().map(|f| f.path.clone()).collect();
|
||||
|
||||
let filtered_paths: Vec<String> = filtered
|
||||
.iter()
|
||||
.map(|f| f.path.clone())
|
||||
.collect();
|
||||
rsx! {
|
||||
div { class: "card mb-16",
|
||||
div { class: "card-header",
|
||||
|
|
@ -279,7 +299,6 @@ pub fn Import(
|
|||
}
|
||||
}
|
||||
|
||||
// Filter bar
|
||||
div { class: "filter-bar",
|
||||
div { class: "flex-row mb-8",
|
||||
label {
|
||||
|
|
@ -383,8 +402,9 @@ pub fn Import(
|
|||
}
|
||||
}
|
||||
|
||||
// Selection toolbar
|
||||
div { class: "flex-row mb-8", style: "gap: 8px; align-items: center; padding: 0 8px;",
|
||||
div {
|
||||
class: "flex-row mb-8",
|
||||
style: "gap: 8px; align-items: center; padding: 0 8px;",
|
||||
button {
|
||||
class: "btn btn-sm btn-secondary",
|
||||
onclick: {
|
||||
|
|
@ -407,9 +427,7 @@ pub fn Import(
|
|||
"Deselect All"
|
||||
}
|
||||
if selected_count > 0 {
|
||||
span { class: "text-muted text-sm",
|
||||
"{selected_count} files selected"
|
||||
}
|
||||
span { class: "text-muted text-sm", "{selected_count} files selected" }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -425,13 +443,17 @@ pub fn Import(
|
|||
let filtered_paths = filtered_paths.clone();
|
||||
move |_| {
|
||||
if all_filtered_selected {
|
||||
// Deselect all filtered
|
||||
let filtered_set: HashSet<String> = filtered_paths.iter().cloned().collect();
|
||||
let filtered_set: HashSet<String> = filtered_paths
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
let sel = selected_file_paths.read().clone();
|
||||
let new_sel: HashSet<String> = sel.difference(&filtered_set).cloned().collect();
|
||||
let new_sel: HashSet<String> = sel
|
||||
.difference(&filtered_set)
|
||||
.cloned()
|
||||
.collect();
|
||||
selected_file_paths.set(new_sel);
|
||||
} else {
|
||||
// Select all filtered
|
||||
let mut sel = selected_file_paths.read().clone();
|
||||
for p in &filtered_paths {
|
||||
sel.insert(p.clone());
|
||||
|
|
@ -455,9 +477,7 @@ pub fn Import(
|
|||
let is_selected = selection.contains(&file.path);
|
||||
let file_path_clone = file.path.clone();
|
||||
rsx! {
|
||||
tr {
|
||||
key: "{file.path}",
|
||||
class: if is_selected { "row-selected" } else { "" },
|
||||
tr { key: "{file.path}", class: if is_selected { "row-selected" } else { "" },
|
||||
td {
|
||||
input {
|
||||
r#type: "checkbox",
|
||||
|
|
@ -496,9 +516,9 @@ pub fn Import(
|
|||
ImportOptions {
|
||||
tags: tags.clone(),
|
||||
collections: collections.clone(),
|
||||
selected_tags: selected_tags,
|
||||
new_tags_input: new_tags_input,
|
||||
selected_collection: selected_collection,
|
||||
selected_tags,
|
||||
new_tags_input,
|
||||
selected_collection,
|
||||
}
|
||||
|
||||
div { class: "flex-row mb-16", style: "gap: 8px;",
|
||||
|
|
@ -516,7 +536,11 @@ pub fn Import(
|
|||
let mut new_tags_input = new_tags_input;
|
||||
let mut selected_collection = selected_collection;
|
||||
move |_| {
|
||||
let paths: Vec<String> = selected_file_paths.read().iter().cloned().collect();
|
||||
let paths: Vec<String> = selected_file_paths
|
||||
.read()
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
if !paths.is_empty() {
|
||||
let tag_ids = selected_tags.read().clone();
|
||||
let new_tags = parse_new_tags(&new_tags_input.read());
|
||||
|
|
@ -565,7 +589,11 @@ pub fn Import(
|
|||
}
|
||||
}
|
||||
},
|
||||
if is_importing { "Importing..." } else { "Import Entire Directory" }
|
||||
if is_importing {
|
||||
"Importing..."
|
||||
} else {
|
||||
"Import Entire Directory"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -589,36 +617,37 @@ pub fn Import(
|
|||
class: "btn btn-primary",
|
||||
disabled: is_importing,
|
||||
onclick: move |_| on_scan.call(()),
|
||||
if is_importing { "Scanning..." } else { "Scan All Roots" }
|
||||
if is_importing {
|
||||
"Scanning..."
|
||||
} else {
|
||||
"Scan All Roots"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref progress) = scan_progress {
|
||||
{
|
||||
let pct = (progress.files_processed * 100).checked_div(progress.files_found).unwrap_or(0);
|
||||
let pct = (progress.files_processed * 100)
|
||||
.checked_div(progress.files_found)
|
||||
.unwrap_or(0);
|
||||
rsx! {
|
||||
div { class: "mb-16",
|
||||
div { class: "progress-bar",
|
||||
div {
|
||||
class: "progress-fill",
|
||||
style: "width: {pct}%;",
|
||||
div { class: "mb-16",
|
||||
div { class: "progress-bar",
|
||||
div { class: "progress-fill", style: "width: {pct}%;" }
|
||||
}
|
||||
p { class: "text-muted text-sm",
|
||||
"{progress.files_processed} / {progress.files_found} files processed"
|
||||
}
|
||||
if progress.error_count > 0 {
|
||||
p { class: "text-muted text-sm", "{progress.error_count} errors" }
|
||||
}
|
||||
if progress.scanning {
|
||||
p { class: "text-muted text-sm", "Scanning..." }
|
||||
} else {
|
||||
p { class: "text-muted text-sm", "Scan complete" }
|
||||
}
|
||||
}
|
||||
}
|
||||
p { class: "text-muted text-sm",
|
||||
"{progress.files_processed} / {progress.files_found} files processed"
|
||||
}
|
||||
if progress.error_count > 0 {
|
||||
p { class: "text-muted text-sm",
|
||||
"{progress.error_count} errors"
|
||||
}
|
||||
}
|
||||
if progress.scanning {
|
||||
p { class: "text-muted text-sm", "Scanning..." }
|
||||
} else {
|
||||
p { class: "text-muted text-sm", "Scan complete" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -647,7 +676,9 @@ fn ImportOptions(
|
|||
div { class: "form-group",
|
||||
label { class: "form-label", "Tags" }
|
||||
if tags.is_empty() {
|
||||
p { class: "text-muted text-sm", "No tags available. Create tags from the Tags page." }
|
||||
p { class: "text-muted text-sm",
|
||||
"No tags available. Create tags from the Tags page."
|
||||
}
|
||||
} else {
|
||||
div { class: "tag-list",
|
||||
for tag in tags.iter() {
|
||||
|
|
@ -655,11 +686,7 @@ fn ImportOptions(
|
|||
let tag_id = tag.id.clone();
|
||||
let tag_name = tag.name.clone();
|
||||
let is_selected = selected_tags.read().contains(&tag_id);
|
||||
let badge_class = if is_selected {
|
||||
"tag-badge selected"
|
||||
} else {
|
||||
"tag-badge"
|
||||
};
|
||||
let badge_class = if is_selected { "tag-badge selected" } else { "tag-badge" };
|
||||
rsx! {
|
||||
span {
|
||||
class: "{badge_class}",
|
||||
|
|
@ -693,7 +720,9 @@ fn ImportOptions(
|
|||
value: "{new_tags_input}",
|
||||
oninput: move |e| new_tags_input.set(e.value()),
|
||||
}
|
||||
p { class: "text-muted text-sm", "Comma-separated. Will be created if they don't exist." }
|
||||
p { class: "text-muted text-sm",
|
||||
"Comma-separated. Will be created if they don't exist."
|
||||
}
|
||||
}
|
||||
|
||||
div { class: "form-group",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue