diff --git a/frontend/src/components/Gallery.vue b/frontend/src/components/Gallery.vue
index 0f89c62..e0b27af 100644
--- a/frontend/src/components/Gallery.vue
+++ b/frontend/src/components/Gallery.vue
@@ -2,12 +2,8 @@
@@ -55,10 +51,12 @@ const rename = (doc: Doc, newName: string) => {
doc.name = newName // We should get an update from watch but this is quicker
}
const gallery = ref()
-const columns = computed(() => {
- if (!gallery.value) return 1
- return getComputedStyle(gallery.value).gridTemplateColumns.split(' ').length
-})
+const columnCount = ref(1)
+const updateColumns = () => {
+ if (!gallery.value) return
+ columnCount.value = getComputedStyle(gallery.value).gridTemplateColumns.split(' ').length
+}
+const columns = computed(() => columnCount.value)
defineExpose({
newFolder() {
const now = Math.floor(Date.now() / 1000)
@@ -168,12 +166,21 @@ watchEffect(() => {
focusBreadcrumb()
}
})
+let resizeObserver: ResizeObserver | null = null
onMounted(() => {
const active = document.querySelector('.cursor') as HTMLElement | null
if (active) {
active.scrollIntoView({ block: 'center', behavior: 'instant' })
active.focus()
}
+ updateColumns()
+ if (gallery.value) {
+ resizeObserver = new ResizeObserver(updateColumns)
+ resizeObserver.observe(gallery.value)
+ }
+})
+onUnmounted(() => {
+ resizeObserver?.disconnect()
})
const mkdir = (doc: Doc, name: string) => {
const control = connect(controlUrl, {
@@ -205,6 +212,8 @@ const showFolderBreadcrumb = (i: number) => {
const docloc = docs[i].loc
return i === 0 ? docloc !== loc.value : docloc !== docs[i - 1].loc
}
+
+
const selectionIndeterminate = computed({
get: () => {
return (
@@ -254,13 +263,12 @@ const contextMenu = (ev: MouseEvent, doc: Doc) => {
display: grid;
gap: .5em;
grid-template-columns: repeat(auto-fill, minmax(15em, 1fr));
- grid-template-rows: repeat(minmax(auto, 15em));
align-items: end;
}
-.breadcrumb {
- border-radius: .5em 0 0 .5em;
+.folder-indicator {
+ grid-column: 1 / -1;
}
-.spacer {
- flex: 0 1000000000 4rem;
+.folder-start {
+ grid-column-start: 1;
}
diff --git a/frontend/src/utils/docsort.ts b/frontend/src/utils/docsort.ts
index 016db06..6c67158 100644
--- a/frontend/src/utils/docsort.ts
+++ b/frontend/src/utils/docsort.ts
@@ -13,3 +13,50 @@ export const sorted = (documents: Doc[], order: SortOrder) => {
sorted.sort(ordering[order])
return sorted
}
+
+/**
+ * Sort documents while keeping files grouped by their folder.
+ * - name: folders sorted by folder path, items within by name
+ * - modified: folders sorted by newest item within results, items within by mtime
+ * - size: folders sorted by largest file within results, items within by size
+ */
+export const sortedGrouped = (documents: Doc[], order: SortOrder) => {
+ if (!order) return documents
+
+ const compare = ordering[order]
+
+ // Group documents by their folder location
+ const byFolder = new Map()
+ for (const doc of documents) {
+ const folder = doc.loc
+ if (!byFolder.has(folder)) byFolder.set(folder, [])
+ byFolder.get(folder)!.push(doc)
+ }
+
+ // Sort items within each folder
+ for (const docs of byFolder.values()) {
+ docs.sort(compare)
+ }
+
+ // Find the "best" item in each folder (first after sorting = best according to criteria)
+ const folderBest = new Map()
+ for (const [folder, docs] of byFolder) {
+ folderBest.set(folder, docs[0])
+ }
+
+ // Sort folders: by path for name sort, by best item for modified/size
+ const sortedFolders = [...byFolder.keys()].sort((a, b) => {
+ if (order === 'name') {
+ return collator.compare(a, b)
+ }
+ return compare(folderBest.get(a)!, folderBest.get(b)!)
+ })
+
+ // Flatten back into a single array with folder grouping preserved
+ const result: Doc[] = []
+ for (const folder of sortedFolders) {
+ result.push(...byFolder.get(folder)!)
+ }
+
+ return result
+}
diff --git a/frontend/src/views/ExplorerView.vue b/frontend/src/views/ExplorerView.vue
index 8615168..f455f26 100644
--- a/frontend/src/views/ExplorerView.vue
+++ b/frontend/src/views/ExplorerView.vue
@@ -21,7 +21,7 @@ import { watchEffect, ref, computed, watch } from 'vue'
import { useMainStore } from '@/stores/main'
import Router from '@/router/index'
import { needleFormat, localeIncludes, collator } from '@/utils'
-import { sorted } from '@/utils/docsort'
+import { sorted, sortedGrouped } from '@/utils/docsort'
import FileExplorer from '@/components/FileExplorer.vue'
const store = useMainStore()
@@ -49,9 +49,9 @@ const documents = computed(() => {
}
}
const locsub = loc + '/'
- // Custom sort override in effect?
+ // Custom sort override in effect? Use grouped sorting to keep folders together
const order = store.prefs.sortFiltered
- if (order) return sorted(docs, order)
+ if (order) return sortedGrouped(docs, order)
// Sort by relevance - current folder, then subfolders, then others
docs.sort((a, b) => (
// @ts-ignore