Modrinth/apps/frontend/src/components/ui/servers/FilesBrowseNavbar.vue
Evan Song a75538c093
Modrinth Servers Mega Features & Bug Fix-a-thon (#3222)
* fix(content): changing mod versions works again

* chore(assets): update pyro logo

* fix(properties): deprecate fetchconfigfile

* Revert "fix(content): changing mod versions works again"

This reverts commit d7c0d1196f8c1850fd7ccbc1644941c6db4dc306.

* feat(files): ability to sort via column click

* chore(startup): update clunky wording

* feat(serverListing): server icons SSR friendly

* fix(servers): if archon fails, display err in listing

* chore(serverlisting): use pyroserver hook to init icon

* chore(servers): much more graceful reinstall

* fix(servers): tw warn

* fix(platform): correctly react when pack reinstalled

* fix(serversroot): explicitly import navigateTo

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(serverlabels): show skeleton instead of hiding

Signed-off-by: Evan Song <theevansong@gmail.com>

* feat(platform): install-aware controls

Signed-off-by: Evan Song <theevansong@gmail.com>

* refactor!(platform): rewrite platform page

* fix(platform): regression in autoselecting loader

* chore(platform): prefer version over project modification date

* fix(platform): permanent hang after initial mount

* chore(platform): do not silently fail and hang if modpack fails loading

* oops: remove hardcoded error causer

* fix(platform): switch modpack btn while installing doesnt need class

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(platform): adjust styling in version modal

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(platform): prevent changing project card style

Signed-off-by: Evan Song <theevansong@gmail.com>

* refactor(pyrodropdown): rewrite

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(pyrodropdown): do nopt use deprecated substr

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore: clean

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(network): sentence case

Signed-off-by: Evan Song <theevansong@gmail.com>

* refactor(terminal): initial batch

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(terminal): fulllog over fullscreen

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(terminal): fullscreen conflict with body scroll

Signed-off-by: Evan Song <theevansong@gmail.com>

* feat(terminal): init drag select

* feat(terminal): shift click support

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(terminal): double lines limit

Signed-off-by: Evan Song <theevansong@gmail.com>

* feat(terminal): copy button

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(terminal): protip style

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(terminal): improve styles

Signed-off-by: Evan Song <theevansong@gmail.com>

* feat(terminal): regex search

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(terminal): move icons to icons dir

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(terminal): improve drag select autoscroll inertia

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(terminal): cancel selection on right click

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(terminal): progblur and stb btn disappearing

Signed-off-by: Evan Song <theevansong@gmail.com>

* refactor(serverstats): power efficiency

* fix(subdomainlabel): correct tooltip terminology

Signed-off-by: Evan Song <theevansong@gmail.com>

* feat(preferences): users hide subdomain label

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(servers): clean

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(terminal): deselect lines on escape

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(serversidebar): type err

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(fileitem): vue server render type

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(terminal): disable pointer events on lines if scrolling

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(terminal): search result counts style

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(terminal): plural

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(terminal): clean

Signed-off-by: Evan Song <theevansong@gmail.com>

* feat(terminal): view selection

Signed-off-by: Evan Song <theevansong@gmail.com>

* feat(terminal): show actively selected lines in scrollbar

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(terminallog): btn color

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore: clean

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(gamelabel): align to text

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(gamelabel): align to text

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(listing): remove deadcode

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(serverlisting): deprecated process.server

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(platform): correctly disable button

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(backups): do not allow backup creation during server installation

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(platform): flush stale currentversion data on successful install

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(gamelabel): fix gap

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(network): vaporize uppercase

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(info): vaporize uppercase

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(backups): style unification

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(backups): finalize style change

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(servers): catch pyro servers fetch errors during ssr

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(serverstats): ram as bytes graph now works

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(platform): unify attempts and refresh interval

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(terminal): input

Signed-off-by: Evan Song <theevansong@gmail.com>

* feat(servers): installing ticket + update available notice back in platform

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(terminal): dont add bg to scroll track

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(terminal): preserve whitespace

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(serversroot): unnest blurred icon query

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(serverstats): clamp memory usage to 100% no matter what

Signed-off-by: Evan Song <theevansong@gmail.com>

* feat(terminal): allow copy of single lines, show btn

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(terminal): animate copy>view transition

Signed-off-by: Evan Song <theevansong@gmail.com>

* init: search improvements

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: lint

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore: change log modal title

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: hide fullscreen when selecting and cancel selection on clickout

Signed-off-by: Evan Song <theevansong@gmail.com>

* refactor(terminal): more reliable jumpToLine

Signed-off-by: Evan Song <theevansong@gmail.com>

* feat: search results separator

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore: remove buggy isScrollable check

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: style

Signed-off-by: Evan Song <theevansong@gmail.com>

* refactor: correctly store pos to make jump reliable

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: disparity between search/log dragselect

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: prevent propagation of click events when clicking on jump btn

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: switch selection strategies depending on terminal mode

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore: smarter esc handling

Signed-off-by: Evan Song <theevansong@gmail.com>

* finalize

Signed-off-by: Evan Song <theevansong@gmail.com>

* run fix

* fix: ensure lines between cannot be selected

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: increase initial log batch to 256

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(terminal): click on scroll track should take user to new scroll position

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(terminal): update aria label for view selected logs btn

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>
2025-02-10 07:39:13 -08:00

230 lines
7.4 KiB
Vue

<template>
<div ref="pyroFilesSentinel" class="sentinel" data-pyro-files-sentinel />
<header
:class="[
'duration-20 top-0 flex select-none flex-col justify-between gap-2 bg-table-alternateRow p-3 transition-[border-radius] sm:h-12 sm:flex-row',
!isStuck ? 'rounded-t-2xl' : 'sticky top-0 z-20',
]"
data-pyro-files-state="browsing"
aria-label="File navigation"
>
<nav
aria-label="Breadcrumb navigation"
class="m-0 flex min-w-0 flex-shrink items-center p-0 text-contrast"
>
<ol class="m-0 flex min-w-0 flex-shrink list-none items-center p-0">
<li class="-ml-1 flex-shrink-0">
<ButtonStyled type="transparent">
<button
v-tooltip="'Back to home'"
type="button"
class="mr-2 grid h-12 w-10 place-content-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand"
@click="$emit('navigate', -1)"
>
<span
class="grid size-8 place-content-center rounded-full bg-button-bg p-[6px] group-hover:bg-brand-highlight group-hover:text-brand"
>
<HomeIcon class="h-5 w-5" />
<span class="sr-only">Home</span>
</span>
</button>
</ButtonStyled>
</li>
<li class="m-0 -ml-2 min-w-0 flex-shrink p-0">
<ol class="m-0 flex min-w-0 flex-shrink items-center overflow-hidden p-0">
<TransitionGroup
name="breadcrumb"
tag="span"
class="relative flex min-w-0 flex-shrink items-center"
>
<li
v-for="(segment, index) in breadcrumbSegments"
:key="`${segment || index}-group`"
class="relative flex min-w-0 flex-shrink items-center text-sm"
>
<div class="flex min-w-0 flex-shrink items-center">
<ButtonStyled type="transparent">
<button
class="cursor-pointer truncate focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand"
:aria-current="
index === breadcrumbSegments.length - 1 ? 'location' : undefined
"
:class="{
'!text-contrast': index === breadcrumbSegments.length - 1,
}"
@click="$emit('navigate', index)"
>
{{ segment || "" }}
</button>
</ButtonStyled>
<ChevronRightIcon
v-if="index < breadcrumbSegments.length - 1"
class="size-4 flex-shrink-0 text-secondary"
aria-hidden="true"
/>
</div>
</li>
</TransitionGroup>
</ol>
</li>
</ol>
</nav>
<div class="flex flex-shrink-0 items-center gap-1">
<div class="flex w-full flex-row-reverse sm:flex-row">
<ButtonStyled type="transparent">
<UiServersTeleportOverflowMenu
position="bottom"
direction="left"
aria-label="Filter view"
:options="[
{ id: 'all', action: () => $emit('filter', 'all') },
{ id: 'filesOnly', action: () => $emit('filter', 'filesOnly') },
{ id: 'foldersOnly', action: () => $emit('filter', 'foldersOnly') },
]"
>
<div class="flex items-center gap-1">
<FilterIcon aria-hidden="true" class="h-5 w-5" />
<span class="hidden text-sm font-medium sm:block">
{{ filterLabel }}
</span>
</div>
<DropdownIcon aria-hidden="true" class="h-5 w-5 text-secondary" />
<template #all>Show all</template>
<template #filesOnly>Files only</template>
<template #foldersOnly>Folders only</template>
</UiServersTeleportOverflowMenu>
</ButtonStyled>
<div class="mx-1 w-full text-sm sm:w-48">
<label for="search-folder" class="sr-only">Search folder</label>
<div class="relative">
<SearchIcon
class="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2"
aria-hidden="true"
/>
<input
id="search-folder"
:value="searchQuery"
type="search"
name="search"
autocomplete="off"
class="h-8 min-h-[unset] w-full border-[1px] border-solid border-divider bg-transparent py-2 pl-9"
placeholder="Search..."
@input="$emit('update:searchQuery', ($event.target as HTMLInputElement).value)"
/>
</div>
</div>
</div>
<ButtonStyled type="transparent">
<UiServersTeleportOverflowMenu
position="bottom"
direction="left"
aria-label="Create new..."
:options="[
{ id: 'file', action: () => $emit('create', 'file') },
{ id: 'directory', action: () => $emit('create', 'directory') },
{ id: 'upload', action: () => $emit('upload') },
]"
>
<PlusIcon aria-hidden="true" />
<DropdownIcon aria-hidden="true" class="h-5 w-5 text-secondary" />
<template #file> <BoxIcon aria-hidden="true" /> New file </template>
<template #directory> <FolderOpenIcon aria-hidden="true" /> New folder </template>
<template #upload> <UploadIcon aria-hidden="true" /> Upload file </template>
</UiServersTeleportOverflowMenu>
</ButtonStyled>
</div>
</header>
</template>
<script setup lang="ts">
import {
BoxIcon,
PlusIcon,
UploadIcon,
DropdownIcon,
FolderOpenIcon,
SearchIcon,
HomeIcon,
ChevronRightIcon,
FilterIcon,
} from "@modrinth/assets";
import { ButtonStyled } from "@modrinth/ui";
import { ref, computed } from "vue";
import { useIntersectionObserver } from "@vueuse/core";
const props = defineProps<{
breadcrumbSegments: string[];
searchQuery: string;
currentFilter: string;
}>();
defineEmits<{
(e: "navigate", index: number): void;
(e: "create", type: "file" | "directory"): void;
(e: "upload"): void;
(e: "update:searchQuery", value: string): void;
(e: "filter", type: string): void;
}>();
const pyroFilesSentinel = ref<HTMLElement | null>(null);
const isStuck = ref(false);
useIntersectionObserver(
pyroFilesSentinel,
([{ isIntersecting }]) => {
isStuck.value = !isIntersecting;
},
{ threshold: [0, 1] },
);
const filterLabel = computed(() => {
switch (props.currentFilter) {
case "filesOnly":
return "Files only";
case "foldersOnly":
return "Folders only";
default:
return "Show all";
}
});
</script>
<style scoped>
.sentinel {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
visibility: hidden;
}
.breadcrumb-move,
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all 0.2s ease;
}
.breadcrumb-enter-from {
opacity: 0;
transform: translateX(-10px) scale(0.9);
}
.breadcrumb-leave-to {
opacity: 0;
transform: translateX(-10px) scale(0.8);
filter: blur(4px);
}
.breadcrumb-leave-active {
position: relative;
pointer-events: none;
}
.breadcrumb-move {
z-index: 1;
}
</style>