forked from didirus/AstralRinth
* chore: dedupe lockfile Signed-off-by: Evan Song <theevansong@gmail.com> * fix: incorrect spacing between editing and browsing state Signed-off-by: Evan Song <theevansong@gmail.com> * chore: improve files image viewer toolbar Signed-off-by: Evan Song <theevansong@gmail.com> * chore: image viewer cursor affordance Signed-off-by: Evan Song <theevansong@gmail.com> * chore: clean imports Signed-off-by: Evan Song <theevansong@gmail.com> * chore: add tooltips Signed-off-by: Evan Song <theevansong@gmail.com> * chore: use black background Signed-off-by: Evan Song <theevansong@gmail.com> * feat: show scale factor, handle large images, consolidate state Signed-off-by: Evan Song <theevansong@gmail.com> * chore: add types to fs operations Signed-off-by: Evan Song <theevansong@gmail.com> * feat: add date create sorting option Signed-off-by: Evan Song <theevansong@gmail.com> * fix: match name of folder creation modal Signed-off-by: Evan Song <theevansong@gmail.com> * fix: add it here too Signed-off-by: Evan Song <theevansong@gmail.com> * feat: add creation date to file item, file manager header Signed-off-by: Evan Song <theevansong@gmail.com> * chore: a11y Signed-off-by: Evan Song <theevansong@gmail.com> * fix: ensure move item modal always has leading slash Signed-off-by: Evan Song <theevansong@gmail.com> * fix: correct move input placeholder Signed-off-by: Evan Song <theevansong@gmail.com> * chore: correct design disparity Signed-off-by: Evan Song <theevansong@gmail.com> * chore: add better affordance on active file item state Signed-off-by: Evan Song <theevansong@gmail.com> * fix: correct instances where we dont sentence case Signed-off-by: Evan Song <theevansong@gmail.com> * chore: clean Signed-off-by: Evan Song <theevansong@gmail.com> * chore: notify that server restarted on saveandrestart Signed-off-by: Evan Song <theevansong@gmail.com> * fix: consolidate error state in file manager Signed-off-by: Evan Song <theevansong@gmail.com> * chore: adjust sizing Signed-off-by: Evan Song <theevansong@gmail.com> * feat: drag and drop file items to move them Signed-off-by: Evan Song <theevansong@gmail.com> * feat: enable ability to drag folders too Signed-off-by: Evan Song <theevansong@gmail.com> * feat: better file movement toasts Signed-off-by: Evan Song <theevansong@gmail.com> * just say u hate me Signed-off-by: Evan Song <theevansong@gmail.com> * feat: uploading indicator for file uploads Signed-off-by: Evan Song <theevansong@gmail.com> * chore: cleaner file item ghost when dragging Signed-off-by: Evan Song <theevansong@gmail.com> * fix: enforce max length and truncate on ghost Signed-off-by: Evan Song <theevansong@gmail.com> * chore: improve item rename toast Signed-off-by: Evan Song <theevansong@gmail.com> * chore: improve item create toast Signed-off-by: Evan Song <theevansong@gmail.com> * feat: undo and redo stack Signed-off-by: Evan Song <theevansong@gmail.com> * fix: confusing behavior where folders were not sorted alphabetically Signed-off-by: Evan Song <theevansong@gmail.com> * feat: find and replace in file editor Signed-off-by: Evan Song <theevansong@gmail.com> * feat: correctly set language mode of file editor Signed-off-by: Evan Song <theevansong@gmail.com> * chore: slop Signed-off-by: Evan Song <theevansong@gmail.com> * chore: actually handle case with multiple dots in file name before setting language Signed-off-by: Evan Song <theevansong@gmail.com> * fix: match move icons in file context/threedot Signed-off-by: Evan Song <theevansong@gmail.com> * feat: upload indicator Signed-off-by: Evan Song <theevansong@gmail.com> * chore: dedupe lockfile again Signed-off-by: Evan Song <theevansong@gmail.com> * lockfile Signed-off-by: Evan Song <theevansong@gmail.com> * fix: file undefinedness Signed-off-by: Evan Song <theevansong@gmail.com> * checkpoint Signed-off-by: Evan Song <theevansong@gmail.com> * checkpoint Signed-off-by: Evan Song <theevansong@gmail.com> * checkpoint Signed-off-by: Evan Song <theevansong@gmail.com> * remove shitty animation logic Signed-off-by: Evan Song <theevansong@gmail.com> * feat: file upload queuer Signed-off-by: Evan Song <theevansong@gmail.com> * chore: only allow editable files to have active affordance Signed-off-by: Evan Song <theevansong@gmail.com> * fix: properly throw pyrofetcherror when rename fails Signed-off-by: Evan Song <theevansong@gmail.com> * feat: cancel file uploads Signed-off-by: Evan Song <theevansong@gmail.com> * chore: clean Signed-off-by: Evan Song <theevansong@gmail.com> --------- Signed-off-by: Evan Song <theevansong@gmail.com>
123 lines
3.3 KiB
Vue
123 lines
3.3 KiB
Vue
<template>
|
|
<div ref="listContainer" data-pyro-files-virtual-list-root class="relative w-full">
|
|
<div
|
|
:style="{
|
|
position: 'relative',
|
|
minHeight: `${totalHeight}px`,
|
|
}"
|
|
data-pyro-files-virtual-height-watcher
|
|
>
|
|
<ul
|
|
class="list-none"
|
|
:style="{
|
|
position: 'absolute',
|
|
top: `${visibleTop}px`,
|
|
width: '100%',
|
|
margin: 0,
|
|
padding: 0,
|
|
}"
|
|
data-pyro-files-virtual-list
|
|
>
|
|
<UiServersFileItem
|
|
v-for="item in visibleItems"
|
|
:key="item.path"
|
|
:count="item.count"
|
|
:created="item.created"
|
|
:modified="item.modified"
|
|
:name="item.name"
|
|
:path="item.path"
|
|
:type="item.type"
|
|
:size="item.size"
|
|
@delete="$emit('delete', item)"
|
|
@rename="$emit('rename', item)"
|
|
@download="$emit('download', item)"
|
|
@move="$emit('move', item)"
|
|
@move-direct-to="$emit('moveDirectTo', $event)"
|
|
@edit="$emit('edit', item)"
|
|
@contextmenu="(x, y) => $emit('contextmenu', item, x, y)"
|
|
/>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, onMounted, onUnmounted } from "vue";
|
|
|
|
const props = defineProps<{
|
|
items: any[];
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(e: "delete", item: any): void;
|
|
(e: "rename", item: any): void;
|
|
(e: "download", item: any): void;
|
|
(e: "move", item: any): void;
|
|
(e: "edit", item: any): void;
|
|
(e: "contextmenu", item: any, x: number, y: number): void;
|
|
(e: "loadMore"): void;
|
|
(e: "moveDirectTo", item: any): void;
|
|
}>();
|
|
|
|
const ITEM_HEIGHT = 61;
|
|
const BUFFER_SIZE = 5;
|
|
|
|
const listContainer = ref<HTMLElement | null>(null);
|
|
const windowScrollY = ref(0);
|
|
const windowHeight = ref(0);
|
|
|
|
const totalHeight = computed(() => props.items.length * ITEM_HEIGHT);
|
|
|
|
const visibleRange = computed(() => {
|
|
if (!listContainer.value) return { start: 0, end: 0 };
|
|
|
|
const containerTop = listContainer.value.getBoundingClientRect().top + window.scrollY;
|
|
const relativeScrollTop = Math.max(0, windowScrollY.value - containerTop);
|
|
|
|
const start = Math.floor(relativeScrollTop / ITEM_HEIGHT);
|
|
const visibleCount = Math.ceil(windowHeight.value / ITEM_HEIGHT);
|
|
|
|
return {
|
|
start: Math.max(0, start - BUFFER_SIZE),
|
|
end: Math.min(props.items.length, start + visibleCount + BUFFER_SIZE * 2),
|
|
};
|
|
});
|
|
|
|
const visibleTop = computed(() => {
|
|
return visibleRange.value.start * ITEM_HEIGHT;
|
|
});
|
|
|
|
const visibleItems = computed(() => {
|
|
return props.items.slice(visibleRange.value.start, visibleRange.value.end);
|
|
});
|
|
|
|
const handleScroll = () => {
|
|
windowScrollY.value = window.scrollY;
|
|
|
|
if (!listContainer.value) return;
|
|
|
|
const containerBottom = listContainer.value.getBoundingClientRect().bottom;
|
|
const remainingScroll = containerBottom - window.innerHeight;
|
|
|
|
if (remainingScroll < windowHeight.value * 0.2) {
|
|
emit("loadMore");
|
|
}
|
|
};
|
|
|
|
const handleResize = () => {
|
|
windowHeight.value = window.innerHeight;
|
|
};
|
|
|
|
onMounted(() => {
|
|
windowHeight.value = window.innerHeight;
|
|
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
window.addEventListener("resize", handleResize, { passive: true });
|
|
handleScroll();
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener("scroll", handleScroll);
|
|
window.removeEventListener("resize", handleResize);
|
|
});
|
|
</script>
|