added sorting option
This commit is contained in:
parent
7090e835b1
commit
1206e7e50f
|
|
@ -27,7 +27,7 @@
|
|||
### Organization
|
||||
- **Folder hierarchy** - Organize notes in nested folders
|
||||
- **Drag & drop** - Move notes and folders effortlessly
|
||||
- **Alphabetical sorting** - Find notes quickly
|
||||
- **Flexible sorting** - Sort notes by name (A-Z, Z-A), date (newest, oldest), or size (largest, smallest)
|
||||
- **Rename anything** - Files and folders, instantly
|
||||
- **Visual tree view** - Expandable/collapsible navigation
|
||||
- **Hide system folders** - Toggle to hide `_attachments`, `_templates` and other underscore-prefixed folders from sidebar
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ const LOCAL_SETTINGS = {
|
|||
tagsExpanded: { key: 'tagsExpanded', type: 'boolean', default: false },
|
||||
hideUnderscoreFolders: { key: 'hideUnderscoreFolders', type: 'boolean', default: false },
|
||||
tabInsertsTab: { key: 'tabInsertsTab', type: 'boolean', default: false },
|
||||
// String settings
|
||||
sortMode: { key: 'sortMode', type: 'string', default: 'a-z' },
|
||||
// Number settings with validation
|
||||
sidebarWidth: { key: 'sidebarWidth', type: 'number', default: CONFIG.DEFAULT_SIDEBAR_WIDTH, min: 200, max: 600 },
|
||||
editorWidth: { key: 'editorWidth', type: 'number', default: 50, min: 20, max: 80 },
|
||||
|
|
@ -219,6 +221,10 @@ function noteApp() {
|
|||
// Tab key inserts tab character instead of changing focus
|
||||
tabInsertsTab: localStorage.getItem('tabInsertsTab') === 'true',
|
||||
|
||||
// Note sorting mode (a-z, z-a, newest, oldest, largest, smallest)
|
||||
sortMode: localStorage.getItem('sortMode') || 'a-z',
|
||||
sortKey: 0, // Incremented to force tree re-render
|
||||
|
||||
// Icon rail / panel state
|
||||
activePanel: 'files', // 'files', 'search', 'tags', 'settings'
|
||||
|
||||
|
|
@ -852,6 +858,60 @@ function noteApp() {
|
|||
this.autoSave();
|
||||
},
|
||||
|
||||
// Sort mode configuration
|
||||
sortModes: ['a-z', 'z-a', 'newest', 'oldest', 'largest', 'smallest'],
|
||||
sortModeIcons: {
|
||||
'a-z': 'A↓',
|
||||
'z-a': 'Z↓',
|
||||
'newest': '🕐↓',
|
||||
'oldest': '🕐↑',
|
||||
'largest': '📄↓',
|
||||
'smallest': '📄↑'
|
||||
},
|
||||
|
||||
// Cycle through sort modes
|
||||
cycleSortMode() {
|
||||
const currentIndex = this.sortModes.indexOf(this.sortMode);
|
||||
const nextIndex = (currentIndex + 1) % this.sortModes.length;
|
||||
this.sortMode = this.sortModes[nextIndex];
|
||||
localStorage.setItem('sortMode', this.sortMode);
|
||||
// Rebuild tree to apply new sort order
|
||||
this.buildFolderTree();
|
||||
},
|
||||
|
||||
// Get current sort icon
|
||||
getSortIcon() {
|
||||
return this.sortModeIcons[this.sortMode] || 'A↓';
|
||||
},
|
||||
|
||||
// Get sort comparator based on current mode (for notes)
|
||||
getSortComparator() {
|
||||
switch (this.sortMode) {
|
||||
case 'z-a':
|
||||
return (a, b) => b.name.toLowerCase().localeCompare(a.name.toLowerCase());
|
||||
case 'newest':
|
||||
return (a, b) => (b.modified || '').localeCompare(a.modified || '');
|
||||
case 'oldest':
|
||||
return (a, b) => (a.modified || '').localeCompare(b.modified || '');
|
||||
case 'largest':
|
||||
return (a, b) => (b.size || 0) - (a.size || 0);
|
||||
case 'smallest':
|
||||
return (a, b) => (a.size || 0) - (b.size || 0);
|
||||
case 'a-z':
|
||||
default:
|
||||
return (a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||
}
|
||||
},
|
||||
|
||||
// Get sort comparator for folders (only A-Z/Z-A, others default to A-Z)
|
||||
getFolderSortComparator() {
|
||||
if (this.sortMode === 'z-a') {
|
||||
return (a, b) => b.name.toLowerCase().localeCompare(a.name.toLowerCase());
|
||||
}
|
||||
// Default: A-Z for all other modes
|
||||
return (a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||
},
|
||||
|
||||
// Update syntax highlight overlay (debounced, called on input)
|
||||
updateSyntaxHighlight() {
|
||||
if (!this.syntaxHighlightEnabled) return;
|
||||
|
|
@ -1757,7 +1817,7 @@ function noteApp() {
|
|||
const sortNotes = (obj) => {
|
||||
if (obj.notes && obj.notes.length > 0) {
|
||||
// Create a new sorted array instead of mutating for Alpine reactivity
|
||||
obj.notes = [...obj.notes].sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
||||
obj.notes = [...obj.notes].sort(this.getSortComparator());
|
||||
}
|
||||
if (obj.children && Object.keys(obj.children).length > 0) {
|
||||
Object.values(obj.children).forEach(child => sortNotes(child));
|
||||
|
|
@ -1766,7 +1826,7 @@ function noteApp() {
|
|||
|
||||
// Sort notes in root (create new array for reactivity)
|
||||
if (tree['__root__'] && tree['__root__'].notes) {
|
||||
tree['__root__'].notes = [...tree['__root__'].notes].sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
||||
tree['__root__'].notes = [...tree['__root__'].notes].sort(this.getSortComparator());
|
||||
}
|
||||
|
||||
// Sort notes in all folders
|
||||
|
|
@ -1886,7 +1946,8 @@ function noteApp() {
|
|||
|
||||
// Render folder recursively (helper for deep nesting)
|
||||
// Uses data-* attributes to store path/name, avoiding JS string escaping issues
|
||||
renderFolderRecursive(folder, level = 0, isTopLevel = false) {
|
||||
// sortKey parameter triggers Alpine re-render when sort mode changes
|
||||
renderFolderRecursive(folder, level = 0, isTopLevel = false, sortKey = 0) {
|
||||
if (!folder) return '';
|
||||
|
||||
let html = '';
|
||||
|
|
@ -1966,7 +2027,7 @@ function noteApp() {
|
|||
if (folder.children && Object.keys(folder.children).length > 0) {
|
||||
const children = Object.entries(folder.children)
|
||||
.filter(([k, v]) => !this.hideUnderscoreFolders || !v.name.startsWith('_'))
|
||||
.sort((a, b) => a[1].name.toLowerCase().localeCompare(b[1].name.toLowerCase()));
|
||||
.sort((a, b) => this.getFolderSortComparator()(a[1], b[1]));
|
||||
|
||||
children.forEach(([childKey, childFolder]) => {
|
||||
html += this.renderFolderRecursive(childFolder, 0, false);
|
||||
|
|
@ -2033,7 +2094,8 @@ function noteApp() {
|
|||
},
|
||||
|
||||
// Render root-level items (notes and media not in any folder)
|
||||
renderRootItems() {
|
||||
// sortKey parameter triggers Alpine re-render when sort mode changes
|
||||
renderRootItems(sortKey = 0) {
|
||||
const root = this.folderTree['__root__'];
|
||||
if (!root || !root.notes || root.notes.length === 0) {
|
||||
return '';
|
||||
|
|
|
|||
|
|
@ -1591,6 +1591,14 @@
|
|||
<div class="flex items-center justify-between mb-1">
|
||||
<span x-text="t('sidebar.folders_and_notes')"></span>
|
||||
<div class="flex gap-0.5">
|
||||
<button
|
||||
@click="cycleSortMode()"
|
||||
class="text-xs px-1.5 py-0.5 rounded transition-all hover:scale-110"
|
||||
:title="t('sidebar.sort_' + sortMode)"
|
||||
style="opacity: 0.7;"
|
||||
onmouseover="this.style.opacity='1'"
|
||||
onmouseout="this.style.opacity='0.7'"
|
||||
><span x-text="getSortIcon()"></span></button>
|
||||
<button
|
||||
@click="expandAllFolders()"
|
||||
class="text-xs px-1.5 py-0.5 rounded transition-all hover:scale-110"
|
||||
|
|
@ -1635,11 +1643,11 @@
|
|||
</div>
|
||||
|
||||
<!-- Folders (rendered recursively for unlimited nesting) -->
|
||||
<template x-for="[folderKey, folder] in Object.entries(folderTree).filter(([k, v]) => k !== '__root__' && (!hideUnderscoreFolders || !v.name.startsWith('_'))).sort((a, b) => a[1].name.toLowerCase().localeCompare(b[1].name.toLowerCase()))" :key="folderKey">
|
||||
<template x-for="[folderKey, folder] in Object.entries(folderTree).filter(([k, v]) => k !== '__root__' && (!hideUnderscoreFolders || !v.name.startsWith('_'))).sort((a, b) => getFolderSortComparator()(a[1], b[1]))" :key="folderKey">
|
||||
<div x-html="renderFolderRecursive(folder, 0, true)"></div>
|
||||
</template>
|
||||
|
||||
<!-- Root notes and images (no folder) - rendered via JS for consistency -->
|
||||
<!-- Root notes and images (no folder) -->
|
||||
<div x-html="renderRootItems()"></div>
|
||||
|
||||
<!-- Empty state -->
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@
|
|||
"no_results": "Keine Ergebnisse gefunden",
|
||||
"expand_all": "Alle Ordner aufklappen",
|
||||
"collapse_all": "Alle Ordner zuklappen",
|
||||
"sort_a-z": "Sortieren A-Z",
|
||||
"sort_z-a": "Sortieren Z-A",
|
||||
"sort_newest": "Nach neuesten sortieren",
|
||||
"sort_oldest": "Nach ältesten sortieren",
|
||||
"sort_largest": "Nach größten sortieren",
|
||||
"sort_smallest": "Nach kleinsten sortieren",
|
||||
"toggle_sidebar": "Seitenleiste umschalten",
|
||||
"go_to_homepage": "Zur Startseite",
|
||||
"files": "Dateien",
|
||||
|
|
|
|||
|
|
@ -48,6 +48,12 @@
|
|||
"no_results": "No results found",
|
||||
"expand_all": "Expand all folders",
|
||||
"collapse_all": "Collapse all folders",
|
||||
"sort_a-z": "Sort A to Z",
|
||||
"sort_z-a": "Sort Z to A",
|
||||
"sort_newest": "Sort by newest",
|
||||
"sort_oldest": "Sort by oldest",
|
||||
"sort_largest": "Sort by largest",
|
||||
"sort_smallest": "Sort by smallest",
|
||||
"toggle_sidebar": "Toggle sidebar",
|
||||
"go_to_homepage": "Go to homepage",
|
||||
"files": "Files",
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@
|
|||
"no_results": "No results found",
|
||||
"expand_all": "Expand all folders",
|
||||
"collapse_all": "Collapse all folders",
|
||||
"sort_a-z": "Sort A to Z",
|
||||
"sort_z-a": "Sort Z to A",
|
||||
"sort_newest": "Sort by newest",
|
||||
"sort_oldest": "Sort by oldest",
|
||||
"sort_largest": "Sort by largest",
|
||||
"sort_smallest": "Sort by smallest",
|
||||
"toggle_sidebar": "Toggle sidebar",
|
||||
"go_to_homepage": "Go to homepage",
|
||||
"files": "Files",
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@
|
|||
"no_results": "No se encontraron resultados",
|
||||
"expand_all": "Expandir todas las carpetas",
|
||||
"collapse_all": "Contraer todas las carpetas",
|
||||
"sort_a-z": "Ordenar A-Z",
|
||||
"sort_z-a": "Ordenar Z-A",
|
||||
"sort_newest": "Ordenar por más reciente",
|
||||
"sort_oldest": "Ordenar por más antiguo",
|
||||
"sort_largest": "Ordenar por más grande",
|
||||
"sort_smallest": "Ordenar por más pequeño",
|
||||
"toggle_sidebar": "Alternar barra lateral",
|
||||
"go_to_homepage": "Ir al inicio",
|
||||
"files": "Archivos",
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@
|
|||
"no_results": "Aucun résultat trouvé",
|
||||
"expand_all": "Développer tous les dossiers",
|
||||
"collapse_all": "Réduire tous les dossiers",
|
||||
"sort_a-z": "Trier A-Z",
|
||||
"sort_z-a": "Trier Z-A",
|
||||
"sort_newest": "Trier par plus récent",
|
||||
"sort_oldest": "Trier par plus ancien",
|
||||
"sort_largest": "Trier par plus grand",
|
||||
"sort_smallest": "Trier par plus petit",
|
||||
"toggle_sidebar": "Afficher/Masquer la barre latérale",
|
||||
"go_to_homepage": "Aller à l'accueil",
|
||||
"files": "Fichiers",
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@
|
|||
"no_results": "Nincs találat",
|
||||
"expand_all": "Mappák kibontása",
|
||||
"collapse_all": "Mappák összecsukása",
|
||||
"sort_a-z": "Rendezés A-Z",
|
||||
"sort_z-a": "Rendezés Z-A",
|
||||
"sort_newest": "Legújabb elöl",
|
||||
"sort_oldest": "Legrégebbi elöl",
|
||||
"sort_largest": "Legnagyobb elöl",
|
||||
"sort_smallest": "Legkisebb elöl",
|
||||
"toggle_sidebar": "Oldalsáv megjelenítése",
|
||||
"go_to_homepage": "Ugrás a kezdőlapra",
|
||||
"files": "Fájlok",
|
||||
|
|
|
|||
|
|
@ -48,6 +48,12 @@
|
|||
"no_results": "Nessun risultato trovato",
|
||||
"expand_all": "Espandi tutte le cartelle",
|
||||
"collapse_all": "Comprimi tutte le cartelle",
|
||||
"sort_a-z": "Ordina A-Z",
|
||||
"sort_z-a": "Ordina Z-A",
|
||||
"sort_newest": "Ordina per più recente",
|
||||
"sort_oldest": "Ordina per più vecchio",
|
||||
"sort_largest": "Ordina per più grande",
|
||||
"sort_smallest": "Ordina per più piccolo",
|
||||
"toggle_sidebar": "Mostra/nascondi barra laterale",
|
||||
"go_to_homepage": "Vai alla home",
|
||||
"files": "File",
|
||||
|
|
|
|||
|
|
@ -48,6 +48,12 @@
|
|||
"no_results": "結果が見つかりません",
|
||||
"expand_all": "すべて展開",
|
||||
"collapse_all": "すべて折りたたむ",
|
||||
"sort_a-z": "A-Z順",
|
||||
"sort_z-a": "Z-A順",
|
||||
"sort_newest": "新しい順",
|
||||
"sort_oldest": "古い順",
|
||||
"sort_largest": "大きい順",
|
||||
"sort_smallest": "小さい順",
|
||||
"toggle_sidebar": "サイドバー切替",
|
||||
"go_to_homepage": "ホームへ",
|
||||
"files": "ファイル",
|
||||
|
|
|
|||
|
|
@ -48,6 +48,12 @@
|
|||
"no_results": "Ничего не найдено",
|
||||
"expand_all": "Развернуть все папки",
|
||||
"collapse_all": "Свернуть все папки",
|
||||
"sort_a-z": "Сортировка А-Я",
|
||||
"sort_z-a": "Сортировка Я-А",
|
||||
"sort_newest": "Сначала новые",
|
||||
"sort_oldest": "Сначала старые",
|
||||
"sort_largest": "Сначала большие",
|
||||
"sort_smallest": "Сначала маленькие",
|
||||
"toggle_sidebar": "Показать/скрыть боковую панель",
|
||||
"go_to_homepage": "На главную",
|
||||
"files": "Файлы",
|
||||
|
|
|
|||
|
|
@ -48,6 +48,12 @@
|
|||
"no_results": "Ni najdenih rezultatov",
|
||||
"expand_all": "Razširi vse mape",
|
||||
"collapse_all": "Strni vse mape",
|
||||
"sort_a-z": "Razvrsti A-Ž",
|
||||
"sort_z-a": "Razvrsti Ž-A",
|
||||
"sort_newest": "Razvrsti po najnovejših",
|
||||
"sort_oldest": "Razvrsti po najstarejših",
|
||||
"sort_largest": "Razvrsti po največjih",
|
||||
"sort_smallest": "Razvrsti po najmanjših",
|
||||
"toggle_sidebar": "Preklopi stransko vrstico",
|
||||
"go_to_homepage": "Pojdi na začetno stran",
|
||||
"files": "Datoteke",
|
||||
|
|
|
|||
|
|
@ -48,6 +48,12 @@
|
|||
"no_results": "未找到结果",
|
||||
"expand_all": "展开所有文件夹",
|
||||
"collapse_all": "折叠所有文件夹",
|
||||
"sort_a-z": "按A-Z排序",
|
||||
"sort_z-a": "按Z-A排序",
|
||||
"sort_newest": "按最新排序",
|
||||
"sort_oldest": "按最旧排序",
|
||||
"sort_largest": "按最大排序",
|
||||
"sort_smallest": "按最小排序",
|
||||
"toggle_sidebar": "切换侧边栏",
|
||||
"go_to_homepage": "前往主页",
|
||||
"files": "文件",
|
||||
|
|
|
|||
Loading…
Reference in New Issue