Add sort and query functions to file list

Added sort functionality and query methods to the file list, enabling users to sort file entries by name, size or modification date. The sorting can be in ascending or descending order. Also, updated element.js for element retrieval and added 'getElementsOrThrow' and 'getElements' methods.
This commit is contained in:
Ian Wijma 2024-01-06 19:30:09 +11:00
parent f45f23db1d
commit e501969807
6 changed files with 118 additions and 11 deletions

View File

@ -1,8 +1,10 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!! // Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::cmp::Ordering;
use std::time::{UNIX_EPOCH}; use std::time::{UNIX_EPOCH};
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use std::string::ToString;
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command] #[tauri::command]
@ -25,10 +27,29 @@ struct EntryMetaData {
accessed: u64, accessed: u64,
} }
enum EntryMetaDataFieldValues {
Str(String),
Int(u64),
Bool(bool),
}
impl EntryMetaData {
pub fn get_field(&self, field: &str) -> Option<EntryMetaDataFieldValues> {
match field {
"name" => Some(EntryMetaDataFieldValues::Str(self.name.clone())),
"size" => Some(EntryMetaDataFieldValues::Int(self.size)),
"modified" => Some(EntryMetaDataFieldValues::Int(self.modified)),
_ => None
}
}
}
#[tauri::command] #[tauri::command]
fn get_files( fn get_files(
_window: tauri::Window, _window: tauri::Window,
directory: &str, directory: &str,
sort: &str,
order: &str,
) -> Vec<EntryMetaData> { ) -> Vec<EntryMetaData> {
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
@ -91,6 +112,47 @@ fn get_files(
}); });
} }
if sort != "" && order != "" {
let order_asc = order != "desc";
data.sort_by(|a, b| {
if let Some(field_a) = a.get_field(sort) {
if let Some(field_b) = b.get_field(sort) {
return match field_a {
EntryMetaDataFieldValues::Str(a_string) => {
match field_b {
EntryMetaDataFieldValues::Str(b_string) => match order_asc {
true => a_string.cmp(&b_string),
false => b_string.cmp(&a_string)
},
_ => Ordering::Equal
}
}
EntryMetaDataFieldValues::Int(a_int) => {
match field_b {
EntryMetaDataFieldValues::Int(b_int) => match order_asc {
true => a_int.cmp(&b_int),
false => b_int.cmp(&a_int)
},
_ => Ordering::Equal
}
}
EntryMetaDataFieldValues::Bool(a_bool) => {
match field_b {
EntryMetaDataFieldValues::Bool(b_bool) => match order_asc {
true => a_bool.cmp(&b_bool),
false => b_bool.cmp(&a_bool)
},
_ => Ordering::Equal
}
}
}
}
}
return Ordering::Equal;
});
}
return data; return data;
} }

View File

@ -57,9 +57,15 @@
<table class="w-full"> <table class="w-full">
<thead> <thead>
<tr class="sticky top-0 bg-red-500"> <tr class="sticky top-0 bg-red-500">
<th>Name</th> <th data-el="sort" data-sort="name" data-order="">
<th>Size</th> Name
<th>Modified</th> </th>
<th data-el="sort" data-sort="size" data-order="">
Size
</th>
<th data-el="sort" data-sort="modified" data-order="">
Modified
</th>
</tr> </tr>
</thead> </thead>
<tbody data-el="body"></tbody> <tbody data-el="body"></tbody>

View File

@ -25,7 +25,7 @@ export const getElementOrThrow = (source, selector) => {
const element = getElement(source, selector); const element = getElement(source, selector);
if (!element) { if (!element) {
throw new Error(`Element ${selector} not found in ${source}`); throw new Error(`Element ${selector} not found in ${source.innerHTML}`);
} }
return element; return element;
@ -40,7 +40,7 @@ export const getElementsOrThrow = (source, selector) => {
const elements = getElements(source, selector); const elements = getElements(source, selector);
if (elements.length <= 0) { if (elements.length <= 0) {
throw new Error(`No elements found with ${selector} in ${source}`); throw new Error(`No elements found with ${selector} in ${source.innerHTML}`);
} }
return elements return elements

View File

@ -1,4 +1,4 @@
import {getElementOrThrow, getOneElementOrThrow} from "./element.js"; import {getElementOrThrow, getElements, getElementsOrThrow, getOneElementOrThrow} from "./element.js";
/** /**
* @typedef {Object} Template * @typedef {Object} Template
@ -22,10 +22,24 @@ export const getTemplate = (name) => {
*/ */
const el = (selector) => getElementOrThrow(element, `[data-el=${selector}]`); const el = (selector) => getElementOrThrow(element, `[data-el=${selector}]`);
/**
* @param {string} selector
* @returns {HTMLElement[]}
*/
const els = (selector) => getElementsOrThrow(element, `[data-el=${selector}]`);
/**
* @param {string} selector
* @returns {HTMLElement[]}
*/
const query = (selector) => getElements(element, selector);
const render = () => element; const render = () => element;
return { return {
el, el,
els,
query,
render render
} }
} }

View File

@ -56,10 +56,12 @@ export const renderFiles = async () => {
/** /**
* @param {string} directory * @param {string} directory
* @param {string} sort
* @param {string} order
* @returns {Promise<Entry[]>} * @returns {Promise<Entry[]>}
*/ */
export const getEntries = async (directory) => { export const getEntries = async (directory, sort = '', order = 'asc') => {
return await tauriInvoke('get_files', { directory }); return await tauriInvoke('get_files', { directory, sort, order });
} }
/** /**

View File

@ -6,9 +6,9 @@ import {sep} from "../../libs/path.js";
export const renderListFiles = () => { export const renderListFiles = () => {
const fileEl = getOneElementOrThrow(document, '[data-ui=files]'); const fileEl = getOneElementOrThrow(document, '[data-ui=files]');
const containerTemplate = getTemplate('files-list');
const body = containerTemplate.el('body'); let sort = '';
let order = '';
/** /**
* @callback ValueFormat * @callback ValueFormat
@ -31,8 +31,11 @@ export const renderListFiles = () => {
} }
const renderList = async () => { const renderList = async () => {
let containerTemplate = getTemplate('files-list');
const body = containerTemplate.el('body');
const currentDirectory = getCurrentDir(); const currentDirectory = getCurrentDir();
const entries = await getEntries(currentDirectory); const entries = await getEntries(currentDirectory, sort, order);
for (const entry of entries) { for (const entry of entries) {
const itemTemplate = getTemplate('files-list-item'); const itemTemplate = getTemplate('files-list-item');
@ -75,6 +78,26 @@ export const renderListFiles = () => {
body.appendChild(itemTemplate.render()); body.appendChild(itemTemplate.render());
} }
const sortEls = containerTemplate.els('sort');
const handleSort = (sortEl) => {
sort = sortEl.dataset.sort;
sortEls.forEach(sortEl => sortEl.dataset.order = '');
switch (order) {
case '': sortEl.dataset.order = 'asc'; break;
case 'asc': sortEl.dataset.order = 'desc'; break;
case 'desc': sortEl.dataset.order = ''; break;
default: sortEl.dataset.order = ''; break;
}
order = sortEl.dataset.order;
console.log({sort, order});
renderList()
};
sortEls.forEach(sortEl => sortEl.addEventListener('click', () => handleSort(sortEl)));
fileEl.innerHTML = ''; fileEl.innerHTML = '';
fileEl.appendChild(containerTemplate.render()); fileEl.appendChild(containerTemplate.render());
} }