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!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::cmp::Ordering;
use std::time::{UNIX_EPOCH};
use std::os::unix::fs::PermissionsExt;
use std::string::ToString;
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command]
@ -25,10 +27,29 @@ struct EntryMetaData {
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]
fn get_files(
_window: tauri::Window,
directory: &str,
sort: &str,
order: &str,
) -> Vec<EntryMetaData> {
use std::fs;
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;
}

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import {getElementOrThrow, getOneElementOrThrow} from "./element.js";
import {getElementOrThrow, getElements, getElementsOrThrow, getOneElementOrThrow} from "./element.js";
/**
* @typedef {Object} Template
@ -22,10 +22,24 @@ export const getTemplate = (name) => {
*/
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;
return {
el,
els,
query,
render
}
}

View File

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

View File

@ -6,9 +6,9 @@ import {sep} from "../../libs/path.js";
export const renderListFiles = () => {
const fileEl = getOneElementOrThrow(document, '[data-ui=files]');
const containerTemplate = getTemplate('files-list');
const body = containerTemplate.el('body');
let sort = '';
let order = '';
/**
* @callback ValueFormat
@ -31,8 +31,11 @@ export const renderListFiles = () => {
}
const renderList = async () => {
let containerTemplate = getTemplate('files-list');
const body = containerTemplate.el('body');
const currentDirectory = getCurrentDir();
const entries = await getEntries(currentDirectory);
const entries = await getEntries(currentDirectory, sort, order);
for (const entry of entries) {
const itemTemplate = getTemplate('files-list-item');
@ -75,6 +78,26 @@ export const renderListFiles = () => {
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.appendChild(containerTemplate.render());
}