mirror of
https://github.com/45Drives/cockpit-navigator.git
synced 2025-07-29 16:45:13 +02:00
fix HTML formatting and add GPL headers
This commit is contained in:
parent
116a422320
commit
ec57d3f5de
@ -1,17 +1,43 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="text-default bg-default h-full flex flex-col items-stretch">
|
||||
<router-view v-if="providesValid" />
|
||||
<div class="flex flex-row items-center px-4 py-2 gap-2">
|
||||
<div id="footer-text" class="flex flex-row flex-wrap gap-x-4 gap-y-0 text-xs grow basis-0"></div>
|
||||
<div
|
||||
id="footer-text"
|
||||
class="flex flex-row flex-wrap gap-x-4 gap-y-0 text-xs grow basis-0"
|
||||
></div>
|
||||
<div class="grow-0">
|
||||
45Drives
|
||||
</div>
|
||||
<div id="footer-buttons" class="flex flex-row-reverse gap-buttons grow basis-0">
|
||||
<div
|
||||
id="footer-buttons"
|
||||
class="flex flex-row-reverse gap-buttons grow basis-0"
|
||||
>
|
||||
<SettingsMenu />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Notifications :notificationFIFO="notificationFIFO" ref="notifications" />
|
||||
<Notifications
|
||||
:notificationFIFO="notificationFIFO"
|
||||
ref="notifications"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
@ -1,42 +1,102 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<transition enter-active-class="origin-top-left transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95"
|
||||
enter-to-class="transform opacity-100 scale-100" leave-active-class="origin-top-left transition ease-in duration-75"
|
||||
leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95">
|
||||
<div v-if="show" class="fixed inset-0 bg-transparent" @click="$emit('hide')">
|
||||
<div class="fixed z-20 max-w-sm flex flex-col items-stretch bg-default shadow-lg divide-y divide-default position-contextmenu">
|
||||
<transition
|
||||
enter-active-class="origin-top-left transition ease-out duration-100"
|
||||
enter-from-class="transform opacity-0 scale-95"
|
||||
enter-to-class="transform opacity-100 scale-100"
|
||||
leave-active-class="origin-top-left transition ease-in duration-75"
|
||||
leave-from-class="transform opacity-100 scale-100"
|
||||
leave-to-class="transform opacity-0 scale-95"
|
||||
>
|
||||
<div
|
||||
v-if="show"
|
||||
class="fixed inset-0 bg-transparent"
|
||||
@click="$emit('hide')"
|
||||
>
|
||||
<div
|
||||
class="fixed z-20 max-w-sm flex flex-col items-stretch bg-default shadow-lg divide-y divide-default position-contextmenu">
|
||||
<div class="flex items-stretch">
|
||||
<button :disabled="!pathHistory?.backAllowed()" @click="$emit('browserAction', 'back')" :class="{'grow flex items-center justify-center p-2': true, 'hover:bg-red-600/10': pathHistory?.backAllowed()}">
|
||||
<button
|
||||
:disabled="!pathHistory?.backAllowed()"
|
||||
:class="{ 'grow flex items-center justify-center p-2': true, 'hover:bg-red-600/10': pathHistory?.backAllowed() }"
|
||||
@click="$emit('browserAction', 'back')"
|
||||
>
|
||||
<ArrowLeftIcon class="size-icon icon-default" />
|
||||
</button>
|
||||
<button :disabled="!pathHistory?.forwardAllowed()" @click="$emit('browserAction', 'forward')" :class="{'grow flex items-center justify-center p-2': true, 'hover:bg-red-600/10': pathHistory?.forwardAllowed()}">
|
||||
<button
|
||||
:disabled="!pathHistory?.forwardAllowed()"
|
||||
:class="{ 'grow flex items-center justify-center p-2': true, 'hover:bg-red-600/10': pathHistory?.forwardAllowed() }"
|
||||
@click="$emit('browserAction', 'forward')"
|
||||
>
|
||||
<ArrowRightIcon class="size-icon icon-default" />
|
||||
</button>
|
||||
<button @click="$emit('browserAction', 'up')" class="grow hover:bg-red-600/10 flex items-center justify-center p-2">
|
||||
<button
|
||||
class="grow hover:bg-red-600/10 flex items-center justify-center p-2"
|
||||
@click="$emit('browserAction', 'up')"
|
||||
>
|
||||
<ArrowUpIcon class="size-icon icon-default" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col items-stretch">
|
||||
<!-- general actions -->
|
||||
<button v-if="entry?.path !== '/'" @click="$emit('browserAction', 'editPermissions', entry)" class="context-menu-button">
|
||||
<button
|
||||
v-if="entry?.path !== '/'"
|
||||
class="context-menu-button"
|
||||
@click="$emit('browserAction', 'editPermissions', entry)"
|
||||
>
|
||||
Edit permissions
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="entry?.resolvedType === 'f'" class="flex flex-col items-stretch">
|
||||
<div
|
||||
v-if="entry?.resolvedType === 'f'"
|
||||
class="flex flex-col items-stretch"
|
||||
>
|
||||
<!-- regular file actions -->
|
||||
<button @click="$emit('browserAction', 'edit', entry)" class="context-menu-button">
|
||||
<button
|
||||
class="context-menu-button"
|
||||
@click="$emit('browserAction', 'edit', entry)"
|
||||
>
|
||||
Edit contents
|
||||
</button>
|
||||
<button @click="$emit('browserAction', 'download', entry)" class="context-menu-button">
|
||||
<button
|
||||
class="context-menu-button"
|
||||
@click="$emit('browserAction', 'download', entry)"
|
||||
>
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
<div v-else-if="entry?.resolvedType === 'd'" class="flex flex-col items-stretch">
|
||||
<div
|
||||
v-else-if="entry?.resolvedType === 'd'"
|
||||
class="flex flex-col items-stretch"
|
||||
>
|
||||
<!-- directory actions -->
|
||||
<button @click="$emit('browserAction', 'cd', entry)" class="context-menu-button">
|
||||
<button
|
||||
class="context-menu-button"
|
||||
@click="$emit('browserAction', 'cd', entry)"
|
||||
>
|
||||
Open
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="entry?.type === 'l'" class="flex flex-col items-stretch">
|
||||
<div
|
||||
v-if="entry?.type === 'l'"
|
||||
class="flex flex-col items-stretch"
|
||||
>
|
||||
<!-- link actions -->
|
||||
</div>
|
||||
</div>
|
||||
@ -76,11 +136,15 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
div.position-contextmenu {
|
||||
top: v-bind(`${event?.clientY ?? 0}px`);
|
||||
left: v-bind(`${event?.clientX ?? 0}px`);
|
||||
top: v-bind("`${event?.clientY ?? 0}px`");
|
||||
left: v-bind("`${event?.clientX ?? 0}px`");
|
||||
}
|
||||
|
||||
button.context-menu-button {
|
||||
@apply text-default hover:bg-red-600/10 font-normal px-4 py-2 text-sm text-left;
|
||||
@apply text-default font-normal px-4 py-2 text-sm text-left;
|
||||
}
|
||||
|
||||
button.context-menu-button:hover {
|
||||
@apply bg-red-600/10;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,73 +1,173 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<template v-if="settings.directoryView?.view === 'list'">
|
||||
<tr
|
||||
:class="{ 'select-none dir-entry': true, '!bg-red-600/10': hover && !entry.selected, '!bg-red-600/20': hover && entry.selected, 'dir-entry-selected': entry.selected, 'suppress-border-t': suppressBorderT, 'suppress-border-b': suppressBorderB }">
|
||||
<td class="!pl-1 relative">
|
||||
<div :class="[entry.cut ? 'line-through' : '', 'flex items-center gap-1']">
|
||||
<div class="w-6" v-for="i in Array(level).fill(0).keys()" :key="i" v-memo="[level]"></div>
|
||||
<div class="relative w-6" :class="[entry.cut ? 'text-gray-500/50' : 'icon-default']">
|
||||
<FolderIcon v-if="entry.resolvedType === 'd'" class="size-icon" />
|
||||
<DocumentIcon v-else class="size-icon" />
|
||||
<LinkIcon v-if="entry.type === 'l'" class="w-2 h-2 absolute right-0 bottom-0 text-default" />
|
||||
<div class="indent-gap" />
|
||||
<div
|
||||
class="relative w-6"
|
||||
:class="[entry.cut ? 'text-gray-500/50' : 'icon-default']"
|
||||
>
|
||||
<FolderIcon
|
||||
v-if="entry.resolvedType === 'd'"
|
||||
class="size-icon"
|
||||
/>
|
||||
<DocumentIcon
|
||||
v-else
|
||||
class="size-icon"
|
||||
/>
|
||||
<LinkIcon
|
||||
v-if="entry.type === 'l'"
|
||||
class="w-2 h-2 absolute right-0 bottom-0 text-default"
|
||||
/>
|
||||
</div>
|
||||
<button class="z-10 icon-default" v-if="entry.resolvedType === 'd'" @click.stop="toggleShowEntries"
|
||||
@mouseenter="hover = true" @mouseleave="hover = false">
|
||||
<ChevronDownIcon v-if="!showEntries" class="size-icon" />
|
||||
<ChevronUpIcon v-else class="size-icon" />
|
||||
<button
|
||||
class="z-10 icon-default"
|
||||
v-if="entry.resolvedType === 'd'"
|
||||
@click.stop="toggleShowEntries"
|
||||
@mouseenter="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
>
|
||||
<ChevronDownIcon
|
||||
v-if="!showEntries"
|
||||
class="size-icon"
|
||||
/>
|
||||
<ChevronUpIcon
|
||||
v-else
|
||||
class="size-icon"
|
||||
/>
|
||||
</button>
|
||||
<div v-html="entry.nameHTML" :title="entry.name"></div>
|
||||
<div v-if="entry.type === 'l'" class="inline-flex gap-1 items-center">
|
||||
<div
|
||||
v-html="entry.nameHTML"
|
||||
:title="entry.name"
|
||||
></div>
|
||||
<div
|
||||
v-if="entry.type === 'l'"
|
||||
class="inline-flex gap-1 items-center"
|
||||
>
|
||||
<div class="inline relative">
|
||||
<ArrowNarrowRightIcon class="text-default size-icon-sm inline" />
|
||||
<XIcon v-if="entry.linkBroken"
|
||||
class="icon-danger size-icon-sm absolute inset-x-0 bottom-0" />
|
||||
<XIcon
|
||||
v-if="entry.linkBroken"
|
||||
class="icon-danger size-icon-sm absolute inset-x-0 bottom-0"
|
||||
/>
|
||||
</div>
|
||||
<div v-html="entry.linkRawPathHTML ?? ''" :title="entry.linkRawPath"></div>
|
||||
<div
|
||||
v-html="entry.linkRawPathHTML ?? ''"
|
||||
:title="entry.linkRawPath"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="absolute left-0 top-0 bottom-0 w-full max-w-[50vw]" @mouseup.stop
|
||||
<div
|
||||
class="absolute left-0 top-0 bottom-0 w-full max-w-[50vw]"
|
||||
@mouseup.stop
|
||||
@click.prevent="$emit('directoryViewAction', 'toggleSelected', entry, $event)"
|
||||
@contextmenu.prevent.stop="$emit('browserAction', 'contextMenu', entry, $event)"
|
||||
@dblclick="doubleClickCallback" @mouseenter="hover = true" @mouseleave="hover = false"
|
||||
ref="selectIntersectElement" />
|
||||
@dblclick="doubleClickCallback"
|
||||
@mouseenter="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
ref="selectIntersectElement"
|
||||
/>
|
||||
</td>
|
||||
<td v-if="settings?.directoryView?.cols?.mode" class="font-mono">{{ entry.modeStr }}</td>
|
||||
<td
|
||||
v-if="settings?.directoryView?.cols?.mode"
|
||||
class="font-mono"
|
||||
>{{ entry.modeStr }}</td>
|
||||
<td v-if="settings?.directoryView?.cols?.owner">{{ entry.owner }}</td>
|
||||
<td v-if="settings?.directoryView?.cols?.group">{{ entry.group }} </td>
|
||||
<td v-if="settings?.directoryView?.cols?.size" class="font-mono text-right">{{ entry.sizeHuman }}</td>
|
||||
<td
|
||||
v-if="settings?.directoryView?.cols?.size"
|
||||
class="font-mono text-right"
|
||||
>{{ entry.sizeHuman }}</td>
|
||||
<td v-if="settings?.directoryView?.cols?.btime">{{ entry.btimeStr }}</td>
|
||||
<td v-if="settings?.directoryView?.cols?.mtime">{{ entry.mtimeStr }}</td>
|
||||
<td v-if="settings?.directoryView?.cols?.atime">{{ entry.atimeStr }}</td>
|
||||
</tr>
|
||||
<component :is="DirectoryEntryList" v-if="entry.resolvedType === 'd' && showEntries" :host="host"
|
||||
:path="entry.path" :isChild="true" :sortCallback="inheritedSortCallback"
|
||||
:searchFilterRegExp="searchFilterRegExp" @startProcessing="(...args) => $emit('startProcessing', ...args)"
|
||||
@stopProcessing="(...args) => $emit('stopProcessing', ...args)" @cancelShowEntries="showEntries = false"
|
||||
ref="directoryEntryListRef" :level="level + 1" :selectedCount="selectedCount"
|
||||
<component
|
||||
:is="DirectoryEntryList"
|
||||
v-if="entry.resolvedType === 'd' && showEntries"
|
||||
:host="host"
|
||||
:path="entry.path"
|
||||
:isChild="true"
|
||||
:sortCallback="inheritedSortCallback"
|
||||
:searchFilterRegExp="searchFilterRegExp"
|
||||
:level="level + 1"
|
||||
:selectedCount="selectedCount"
|
||||
@browserAction="(...args) => $emit('browserAction', ...args)"
|
||||
@directoryViewAction="(...args) => $emit('directoryViewAction', ...args)" />
|
||||
@directoryViewAction="(...args) => $emit('directoryViewAction', ...args)"
|
||||
@startProcessing="(...args) => $emit('startProcessing', ...args)"
|
||||
@stopProcessing="(...args) => $emit('stopProcessing', ...args)"
|
||||
@cancelShowEntries="showEntries = false"
|
||||
ref="directoryEntryListRef"
|
||||
/>
|
||||
</template>
|
||||
<div v-else class="select-none dir-entry flex flex-col items-center overflow-hidden dir-entry-width p-2"
|
||||
:class="{ '!bg-red-600/10': hover && !entry.selected, '!bg-red-600/20': hover && entry.selected, 'dir-entry-selected': entry.selected, '!border-t-transparent': suppressBorderT, '!border-b-transparent': suppressBorderB, '!border-l-transparent': suppressBorderL, '!border-r-transparent': suppressBorderR }">
|
||||
<div class="w-full" @dblclick="doubleClickCallback"
|
||||
@click.prevent="$emit('directoryViewAction', 'toggleSelected', entry, $event)" @mouseup.stop
|
||||
<div
|
||||
v-else
|
||||
class="select-none dir-entry flex flex-col items-center overflow-hidden dir-entry-width p-2"
|
||||
:class="{ '!bg-red-600/10': hover && !entry.selected, '!bg-red-600/20': hover && entry.selected, 'dir-entry-selected': entry.selected, '!border-t-transparent': suppressBorderT, '!border-b-transparent': suppressBorderB, '!border-l-transparent': suppressBorderL, '!border-r-transparent': suppressBorderR }"
|
||||
>
|
||||
<div
|
||||
class="w-full"
|
||||
@dblclick="doubleClickCallback"
|
||||
@click.prevent="$emit('directoryViewAction', 'toggleSelected', entry, $event)"
|
||||
@mouseup.stop
|
||||
@contextmenu.prevent.stop="$emit('browserAction', 'contextMenu', entry, $event)"
|
||||
@mouseenter="hover = true" @mouseleave="hover = false" ref="selectIntersectElement">
|
||||
<div class="relative w-full" :class="[entry.cut ? 'text-gray-500/50' : 'icon-default']">
|
||||
<FolderIcon v-if="entry.resolvedType === 'd'" class="w-full h-auto" />
|
||||
<DocumentIcon v-else class="w-full h-auto" />
|
||||
<div :class="[entry.resolvedType === 'd' ? 'right-[15%] bottom-[25%]' : 'right-[25%] bottom-[15%]', 'inline absolute w-[20%]']"
|
||||
:title="`-> ${entry.linkRawPath ?? '?'}`">
|
||||
<LinkIcon v-if="entry.type === 'l'"
|
||||
:class="[entry.linkBroken ? 'text-red-300 dark:text-red-800' : 'text-gray-100 dark:text-gray-900']" />
|
||||
@mouseenter="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
ref="selectIntersectElement"
|
||||
>
|
||||
<div
|
||||
class="relative w-full"
|
||||
:class="[entry.cut ? 'text-gray-500/50' : 'icon-default']"
|
||||
>
|
||||
<FolderIcon
|
||||
v-if="entry.resolvedType === 'd'"
|
||||
class="w-full h-auto"
|
||||
/>
|
||||
<DocumentIcon
|
||||
v-else
|
||||
class="w-full h-auto"
|
||||
/>
|
||||
<div
|
||||
:class="[entry.resolvedType === 'd' ? 'right-[15%] bottom-[25%]' : 'right-[25%] bottom-[15%]', 'inline absolute w-[20%]']"
|
||||
:title="`-> ${entry.linkRawPath ?? '?'}`"
|
||||
>
|
||||
<LinkIcon
|
||||
v-if="entry.type === 'l'"
|
||||
:class="[entry.linkBroken ? 'text-red-300 dark:text-red-800' : 'text-gray-100 dark:text-gray-900']"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-full text-sm break-words"
|
||||
<div
|
||||
class="text-center w-full text-sm break-words"
|
||||
:class="{ 'multiline-ellipsis': entry.selected && selectedCount > 1, 'truncate': !entry.selected, 'line-through': entry.cut }"
|
||||
v-html="entry.nameHTML" :title="entry.name"></div>
|
||||
:title="entry.name"
|
||||
v-html="entry.nameHTML"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<Teleport to="#footer-text" v-if="entry.selected && selectedCount === 1">
|
||||
<Teleport
|
||||
to="#footer-text"
|
||||
v-if="entry.selected && selectedCount === 1"
|
||||
>
|
||||
<div>
|
||||
<span v-if="level > 0">{{ entry.path.split('/').slice(-1 * (level + 1)).join('/') }}:</span>
|
||||
<span v-else>{{ entry.name }}:</span>
|
||||
@ -78,7 +178,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, inject, watch, nextTick, onBeforeUnmount, onMounted, onActivated, onDeactivated, onUpdated } from 'vue';
|
||||
import { ref, inject, nextTick, onMounted, onUpdated } from 'vue';
|
||||
import { DocumentIcon, FolderIcon, LinkIcon, DocumentRemoveIcon, ArrowNarrowRightIcon, XIcon, ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/solid';
|
||||
import { notificationsInjectionKey, settingsInjectionKey } from '../keys';
|
||||
import DirectoryEntryList from './DirectoryEntryList.vue';
|
||||
@ -181,3 +281,9 @@ export default {
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.indent-gap {
|
||||
width: v-bind("`${24 * level}px`");
|
||||
}
|
||||
</style>
|
||||
|
@ -1,24 +1,59 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<DirectoryEntry v-for="entry, index in visibleEntries" :key="entry.path" :host="host" :entry="entry"
|
||||
:inheritedSortCallback="sortCallback" :searchFilterRegExp="searchFilterRegExp"
|
||||
@sortEntries="sortEntries"
|
||||
@startProcessing="(...args) => $emit('startProcessing', ...args)"
|
||||
@stopProcessing="(...args) => $emit('stopProcessing', ...args)" ref="entryRefs" :level="level" :selectedCount="selectedCount"
|
||||
@setEntryProp="(prop, value) => entry[prop] = value"
|
||||
@browserAction="(...args) => $emit('browserAction', ...args)"
|
||||
@directoryViewAction="(...args) => $emit('directoryViewAction', ...args)"
|
||||
<DirectoryEntry
|
||||
v-for="entry, index in visibleEntries"
|
||||
:key="entry.path"
|
||||
:host="host"
|
||||
:entry="entry"
|
||||
:inheritedSortCallback="sortCallback"
|
||||
:searchFilterRegExp="searchFilterRegExp"
|
||||
:level="level"
|
||||
:selectedCount="selectedCount"
|
||||
:suppressBorderT="visibleEntries[index - cols]?.selected && !(visibleEntries[index - cols]?.dirOpen)"
|
||||
:suppressBorderB="visibleEntries[index + cols]?.selected && !(entry.dirOpen)"
|
||||
:suppressBorderL="settings.directoryView.view !== 'list' && (visibleEntries[index - 1]?.selected && (index) % cols !== 0)"
|
||||
:suppressBorderR="settings.directoryView.view !== 'list' && (visibleEntries[index + 1]?.selected && (index + 1) % cols !== 0)" />
|
||||
:suppressBorderR="settings.directoryView.view !== 'list' && (visibleEntries[index + 1]?.selected && (index + 1) % cols !== 0)"
|
||||
@browserAction="(...args) => $emit('browserAction', ...args)"
|
||||
@directoryViewAction="(...args) => $emit('directoryViewAction', ...args)"
|
||||
@sortEntries="sortEntries"
|
||||
@startProcessing="(...args) => $emit('startProcessing', ...args)"
|
||||
@stopProcessing="(...args) => $emit('stopProcessing', ...args)"
|
||||
@setEntryProp="(prop, value) => entry[prop] = value"
|
||||
ref="entryRefs"
|
||||
/>
|
||||
<tr v-if="visibleEntries.length === 0">
|
||||
<td :colspan="Object.values(settings?.directoryView?.cols ?? {}).reduce((sum, current) => current ? sum + 1 : sum, 1) ?? 100"
|
||||
class="!pl-1 text-muted text-sm">
|
||||
<div class="w-6" v-for="i in Array(level).fill(0)" v-memo="[level]"></div>
|
||||
<td
|
||||
:colspan="Object.values(settings?.directoryView?.cols ?? {}).reduce((sum, current) => current ? sum + 1 : sum, 1) ?? 100"
|
||||
class="!pl-1 text-muted text-sm"
|
||||
>
|
||||
<div
|
||||
class="w-6"
|
||||
v-for="i in Array(level).fill(0)"
|
||||
v-memo="[level]"
|
||||
></div>
|
||||
<div class="inline-block">No entries.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<Teleport to="#footer-text" v-if="selectedCount === 0">
|
||||
<Teleport
|
||||
to="#footer-text"
|
||||
v-if="selectedCount === 0"
|
||||
>
|
||||
<div>
|
||||
<span v-if="level > 0">{{ path.split('/').slice(-1 * (level)).join('/') }}:</span>
|
||||
{{ stats }}
|
||||
@ -27,7 +62,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, reactive, computed, inject, watch, onBeforeUnmount, onMounted, nextTick, onUnmounted } from 'vue';
|
||||
import { ref, reactive, computed, inject, watch, onBeforeUnmount, nextTick } from 'vue';
|
||||
import { errorStringHTML } from '@45drives/cockpit-helpers';
|
||||
import { notificationsInjectionKey, settingsInjectionKey, clipboardInjectionKey } from '../keys';
|
||||
import DirectoryEntry from './DirectoryEntry.vue';
|
||||
|
@ -1,118 +1,235 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="h-full" @keydown="keyHandler($event)" tabindex="-1" :class="{ '!cursor-wait': processing }">
|
||||
<DragSelectArea class="h-full" @selectRectangle="selectRectangle" @mouseup.exact="deselectAll" @contextmenu.prevent="$emit('browserAction', 'contextMenu', { host, path, name: `Current directory (${path.split('/').pop() || '/'})` }, $event)">
|
||||
<Table :key="host + path" v-if="settings.directoryView?.view === 'list'" emptyText="No entries." noHeader stickyHeaders
|
||||
noShrink noShrinkHeight="h-full">
|
||||
<div
|
||||
class="h-full"
|
||||
@keydown="keyHandler($event)"
|
||||
tabindex="-1"
|
||||
:class="{ '!cursor-wait': processing }"
|
||||
>
|
||||
<DragSelectArea
|
||||
class="h-full"
|
||||
@selectRectangle="selectRectangle"
|
||||
@mouseup.exact="deselectAll"
|
||||
@contextmenu.prevent="$emit('browserAction', 'contextMenu', { host, path, name: `Current directory (${path.split('/').pop() || '/'})` }, $event)"
|
||||
>
|
||||
<Table
|
||||
:key="host + path"
|
||||
v-if="settings.directoryView?.view === 'list'"
|
||||
emptyText="No entries."
|
||||
noHeader
|
||||
stickyHeaders
|
||||
noShrink
|
||||
noShrinkHeight="h-full"
|
||||
>
|
||||
<template #thead>
|
||||
<tr>
|
||||
<th class="!pl-1 border-l-2 border-l-transparent last:border-r-2 last:border-r-transparent">
|
||||
<div class="flex flex-row flex-nowrap gap-1 items-center">
|
||||
<div class="flex items-center justify-center w-6">
|
||||
<LoadingSpinner v-if="processing" class="size-icon" />
|
||||
<LoadingSpinner
|
||||
v-if="processing"
|
||||
class="size-icon"
|
||||
/>
|
||||
</div>
|
||||
<div class="grow">Name</div>
|
||||
<SortCallbackButton initialFuncIsMine v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.name" />
|
||||
<SortCallbackButton
|
||||
initialFuncIsMine
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.name"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
<th class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.mode">
|
||||
<th
|
||||
class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.mode"
|
||||
>
|
||||
Mode</th>
|
||||
<th class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.owner">
|
||||
<th
|
||||
class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.owner"
|
||||
>
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div class="grow">Owner</div>
|
||||
<SortCallbackButton v-model="sortCallback" :compareFunc="sortCallbacks.owner" />
|
||||
<SortCallbackButton
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.owner"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
<th class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.group">
|
||||
<th
|
||||
class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.group"
|
||||
>
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div class="grow">Group</div>
|
||||
<SortCallbackButton v-model="sortCallback" :compareFunc="sortCallbacks.group" />
|
||||
<SortCallbackButton
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.group"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
<th class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.size">
|
||||
<th
|
||||
class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.size"
|
||||
>
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div class="grow text-right">Size</div>
|
||||
<SortCallbackButton v-model="sortCallback" :compareFunc="sortCallbacks.size" />
|
||||
<SortCallbackButton
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.size"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
<th class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.btime">
|
||||
<th
|
||||
class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.btime"
|
||||
>
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div class="grow">Created</div>
|
||||
<SortCallbackButton v-model="sortCallback" :compareFunc="sortCallbacks.btime" />
|
||||
<SortCallbackButton
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.btime"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
<th class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.mtime">
|
||||
<th
|
||||
class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.mtime"
|
||||
>
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div class="grow">Modified</div>
|
||||
<SortCallbackButton v-model="sortCallback" :compareFunc="sortCallbacks.mtime" />
|
||||
<SortCallbackButton
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.mtime"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
<th class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.atime">
|
||||
<th
|
||||
class="last:border-r-2 last:border-r-transparent"
|
||||
v-if="settings?.directoryView?.cols?.atime"
|
||||
>
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div class="grow">Accessed</div>
|
||||
<SortCallbackButton v-model="sortCallback" :compareFunc="sortCallbacks.atime" />
|
||||
<SortCallbackButton
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.atime"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</template>
|
||||
<template #tbody>
|
||||
<DirectoryEntryList :host="host" :path="path" :sortCallback="sortCallback"
|
||||
<DirectoryEntryList
|
||||
:host="host"
|
||||
:path="path"
|
||||
:sortCallback="sortCallback"
|
||||
:searchFilterRegExp="searchFilterRegExp"
|
||||
@startProcessing="processing++" @stopProcessing="processing--"
|
||||
@browserAction="(...args) => $emit('browserAction', ...args)"
|
||||
@directoryViewAction="handleAction" ref="directoryEntryListRef"
|
||||
:level="0"
|
||||
:cols="1"
|
||||
:selectedCount="selectedCount"
|
||||
@tallySelected="tallySelected"
|
||||
:level="0" :cols="1" :selectedCount="selectedCount" />
|
||||
@startProcessing="processing++"
|
||||
@stopProcessing="processing--"
|
||||
@browserAction="(...args) => $emit('browserAction', ...args)"
|
||||
@directoryViewAction="handleAction"
|
||||
ref="directoryEntryListRef"
|
||||
/>
|
||||
</template>
|
||||
</Table>
|
||||
<div v-else class="h-full flex flex-col items-stretch">
|
||||
<div class="grow-0 flex flex-row justify-start items-center px-6 py-2 gap-4 text-sm border-t border-default">
|
||||
<div
|
||||
v-else
|
||||
class="h-full flex flex-col items-stretch"
|
||||
>
|
||||
<div
|
||||
class="grow-0 flex flex-row justify-start items-center px-6 py-2 gap-4 text-sm border-t border-default">
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div>Name</div>
|
||||
<SortCallbackButton initialFuncIsMine v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.name" />
|
||||
<SortCallbackButton
|
||||
initialFuncIsMine
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.name"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div>Owner</div>
|
||||
<SortCallbackButton v-model="sortCallback" :compareFunc="sortCallbacks.owner" />
|
||||
<SortCallbackButton
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.owner"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div>Group</div>
|
||||
<SortCallbackButton v-model="sortCallback" :compareFunc="sortCallbacks.group" />
|
||||
<SortCallbackButton
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.group"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div>Size</div>
|
||||
<SortCallbackButton v-model="sortCallback" :compareFunc="sortCallbacks.size" />
|
||||
<SortCallbackButton
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.size"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div>Created</div>
|
||||
<SortCallbackButton v-model="sortCallback" :compareFunc="sortCallbacks.btime" />
|
||||
<SortCallbackButton
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.btime"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div>Modified</div>
|
||||
<SortCallbackButton v-model="sortCallback" :compareFunc="sortCallbacks.mtime" />
|
||||
<SortCallbackButton
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.mtime"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap gap-2 items-center">
|
||||
<div>Accessed</div>
|
||||
<SortCallbackButton v-model="sortCallback" :compareFunc="sortCallbacks.atime" />
|
||||
<SortCallbackButton
|
||||
v-model="sortCallback"
|
||||
:compareFunc="sortCallbacks.atime"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div :key="host + path" class="grow flex flex-wrap bg-well overflow-y-auto content-start" ref="gridRef"
|
||||
@wheel="scrollHandler">
|
||||
<DirectoryEntryList :host="host" :path="path" :sortCallback="sortCallback"
|
||||
<div
|
||||
:key="host + path"
|
||||
class="grow flex flex-wrap bg-well overflow-y-auto content-start"
|
||||
ref="gridRef"
|
||||
@wheel="scrollHandler"
|
||||
>
|
||||
<DirectoryEntryList
|
||||
:host="host"
|
||||
:path="path"
|
||||
:sortCallback="sortCallback"
|
||||
:searchFilterRegExp="searchFilterRegExp"
|
||||
@startProcessing="processing++" @stopProcessing="processing--"
|
||||
:level="0"
|
||||
:cols="cols"
|
||||
:selectedCount="selectedCount"
|
||||
@startProcessing="processing++"
|
||||
@stopProcessing="processing--"
|
||||
@browserAction="(...args) => $emit('browserAction', ...args)"
|
||||
@directoryViewAction="handleAction"
|
||||
@tallySelected="tallySelected" ref="directoryEntryListRef"
|
||||
:level="0" :cols="cols" :selectedCount="selectedCount" />
|
||||
@tallySelected="tallySelected"
|
||||
ref="directoryEntryListRef"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</DragSelectArea>
|
||||
@ -125,7 +242,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, inject, watch, onMounted, computed, onBeforeUnmount, onUpdated, nextTick } from 'vue';
|
||||
import { ref, inject, watch, onMounted, onBeforeUnmount, onUpdated } from 'vue';
|
||||
import Table from './Table.vue';
|
||||
import { clipboardInjectionKey, notificationsInjectionKey, settingsInjectionKey } from '../keys';
|
||||
import LoadingSpinner from './LoadingSpinner.vue';
|
||||
@ -279,7 +396,7 @@ export default {
|
||||
};
|
||||
});
|
||||
if (event.shiftKey)
|
||||
clipboard.content = [ ...newContent, ...clipboard.content ].filter((a, index, arr) => arr.findIndex(b => b.uniqueId === a.uniqueId) === index);
|
||||
clipboard.content = [...newContent, ...clipboard.content].filter((a, index, arr) => arr.findIndex(b => b.uniqueId === a.uniqueId) === index);
|
||||
else
|
||||
clipboard.content = newContent;
|
||||
const message = event.shiftKey
|
||||
@ -397,11 +514,11 @@ export default {
|
||||
|
||||
<style>
|
||||
tr.dir-entry>td {
|
||||
@apply border-solid border-y-red-600/50 border-y-0 first:border-l last:border-r first:border-l-transparent last:border-r-transparent;
|
||||
@apply border-solid border-y-red-600/50 border-y-0 first: border-l last:border-r first:border-l-transparent last:border-r-transparent;
|
||||
}
|
||||
|
||||
tr.dir-entry-selected>td {
|
||||
@apply border-y first:border-l-red-600/50 last:border-red-600/50 bg-red-600/10;
|
||||
@apply border-y first: border-l-red-600/50 last:border-red-600/50 bg-red-600/10;
|
||||
}
|
||||
|
||||
div.dir-entry {
|
||||
@ -413,19 +530,19 @@ div.dir-entry-selected {
|
||||
}
|
||||
|
||||
tr.dir-entry-selected.suppress-border-t>td {
|
||||
@apply !border-t-0;
|
||||
@apply !border-t-0;
|
||||
}
|
||||
|
||||
tr.dir-entry-selected.suppress-border-b>td {
|
||||
@apply !border-b-0;
|
||||
@apply !border-b-0;
|
||||
}
|
||||
|
||||
tr.dir-entry-selected.suppress-border-l>td {
|
||||
@apply !border-l-0;
|
||||
@apply !border-l-0;
|
||||
}
|
||||
|
||||
tr.dir-entry-selected.suppress-border-r>td {
|
||||
@apply !border-r-0;
|
||||
@apply !border-r-0;
|
||||
}
|
||||
|
||||
div.dir-entry-width {
|
||||
|
@ -1,12 +1,36 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div v-show="dragging" class="fixed border-red-600/50 border border-solid bg-red-600/10 rounded-lg" ref="selectionBox"></div>
|
||||
<div @mousedown="startDrag" v-bind="$attrs">
|
||||
<div
|
||||
v-show="dragging"
|
||||
class="fixed border-red-600/50 border border-solid bg-red-600/10 rounded-lg"
|
||||
ref="selectionBox"
|
||||
></div>
|
||||
<div
|
||||
v-bind="$attrs"
|
||||
@mousedown="startDrag"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, reactive, watch, onMounted, nextTick, onBeforeUnmount } from 'vue'
|
||||
import { ref, reactive, watch, onMounted } from 'vue'
|
||||
export default {
|
||||
props: {
|
||||
areaThreshold: {
|
||||
|
@ -1,17 +1,17 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit File Sharing.
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit File Sharing is free software: you can redistribute it and/or modify it under the terms
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit File Sharing is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit File Sharing.
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
@ -23,19 +23,55 @@ If not, see <https://www.gnu.org/licenses/>.
|
||||
<label class="text-label">Execute</label>
|
||||
|
||||
<label class="justify-self-start text-label">Owner</label>
|
||||
<input type="checkbox" class="input-checkbox" v-model="modeMatrix.owner.read" />
|
||||
<input type="checkbox" class="input-checkbox" v-model="modeMatrix.owner.write" />
|
||||
<input type="checkbox" class="input-checkbox" v-model="modeMatrix.owner.execute" />
|
||||
<input
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
v-model="modeMatrix.owner.read"
|
||||
/>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
v-model="modeMatrix.owner.write"
|
||||
/>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
v-model="modeMatrix.owner.execute"
|
||||
/>
|
||||
|
||||
<label class="justify-self-start text-label">Group</label>
|
||||
<input type="checkbox" class="input-checkbox" v-model="modeMatrix.group.read" />
|
||||
<input type="checkbox" class="input-checkbox" v-model="modeMatrix.group.write" />
|
||||
<input type="checkbox" class="input-checkbox" v-model="modeMatrix.group.execute" />
|
||||
<input
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
v-model="modeMatrix.group.read"
|
||||
/>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
v-model="modeMatrix.group.write"
|
||||
/>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
v-model="modeMatrix.group.execute"
|
||||
/>
|
||||
|
||||
<label class="justify-self-start text-label">Other</label>
|
||||
<input type="checkbox" class="input-checkbox" v-model="modeMatrix.other.read" />
|
||||
<input type="checkbox" class="input-checkbox" v-model="modeMatrix.other.write" />
|
||||
<input type="checkbox" class="input-checkbox" v-model="modeMatrix.other.execute" />
|
||||
<input
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
v-model="modeMatrix.other.read"
|
||||
/>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
v-model="modeMatrix.other.write"
|
||||
/>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
v-model="modeMatrix.other.execute"
|
||||
/>
|
||||
|
||||
<label class="justify-self-start text-label">Mode</label>
|
||||
<span class="col-span-3 font-mono font-medium whitespace-nowrap">{{ modeStr }}</span>
|
||||
|
@ -1,34 +1,53 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit File Sharing.
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit File Sharing is free software: you can redistribute it and/or modify it under the terms
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit File Sharing is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit File Sharing.
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<ModalPopup :showModal="show" :headerText="entry?.nameHTML ?? entry?.name" @apply="apply" @cancel="$emit('hide')">
|
||||
<ModalPopup
|
||||
:showModal="show"
|
||||
:headerText="entry?.nameHTML ?? entry?.name"
|
||||
@apply="apply"
|
||||
@cancel="$emit('hide')"
|
||||
>
|
||||
<div class="flex flex-col space-y-content items-start">
|
||||
<FileModeMatrix v-model="mode" />
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Owner</label>
|
||||
<select class="input-textlike" v-model="owner">
|
||||
<option v-for="user in users" :key="user.pretty" :value="user.user">{{ user.pretty }}</option>
|
||||
<select
|
||||
class="input-textlike"
|
||||
v-model="owner"
|
||||
>
|
||||
<option
|
||||
v-for="user in users"
|
||||
:key="user.pretty"
|
||||
:value="user.user"
|
||||
>{{ user.pretty }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Group</label>
|
||||
<select class="input-textlike" v-model="group">
|
||||
<option v-for="group in groups" :key="group.pretty" :value="group.group">{{ group.pretty }}</option>
|
||||
<select
|
||||
class="input-textlike"
|
||||
v-model="group"
|
||||
>
|
||||
<option
|
||||
v-for="group in groups"
|
||||
:key="group.pretty"
|
||||
:value="group.group"
|
||||
>{{ group.pretty }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,22 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<button @click="$emit('update:modelValue', modelValue === trueValue ? falseValue: trueValue)">
|
||||
<button @click="$emit('update:modelValue', modelValue === trueValue ? falseValue : trueValue)">
|
||||
<slot :value="modelValue === trueValue">
|
||||
|
||||
</slot>
|
||||
|
@ -16,19 +16,29 @@ If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<SwitchGroup as="div" :class="[labelRight ? 'flex-row-reverse' : 'flex-row', 'inline-flex items-center']">
|
||||
<SwitchGroup
|
||||
as="div"
|
||||
:class="[labelRight ? 'flex-row-reverse' : 'flex-row', 'inline-flex items-center']"
|
||||
>
|
||||
<span :class="[labelRight ? 'grow' : '', 'flex flex-col']">
|
||||
<SwitchLabel as="div" class="text-label"><slot /></SwitchLabel>
|
||||
<SwitchLabel
|
||||
as="div"
|
||||
class="text-label"
|
||||
>
|
||||
<slot />
|
||||
</SwitchLabel>
|
||||
<SwitchDescription
|
||||
as="div"
|
||||
class="text-sm text-muted"
|
||||
><slot name="description" /></SwitchDescription>
|
||||
>
|
||||
<slot name="description" />
|
||||
</SwitchDescription>
|
||||
</span>
|
||||
<div :class="[labelRight ? '' : 'grow', 'w-4']"></div> <!-- spacer -->
|
||||
<Switch
|
||||
:modelValue="modelValue"
|
||||
@update:modelValue="newValue => { $emit('update:modelValue', newValue); $emit('change', newValue); $emit('input', newValue); }"
|
||||
:class="[modelValue ? 'bg-45d' : 'bg-well', 'inline-flex shrink-0 h-6 w-11 p-[2px] rounded-full cursor-pointer shadow-inner']"
|
||||
@update:modelValue="newValue => { $emit('update:modelValue', newValue); $emit('change', newValue); $emit('input', newValue); }"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
@ -40,7 +50,6 @@ If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
<script>
|
||||
import { Switch, SwitchDescription, SwitchGroup, SwitchLabel } from '@headlessui/vue'
|
||||
import { watch, ref } from 'vue';
|
||||
export default {
|
||||
props: {
|
||||
modelValue: Boolean,
|
||||
|
@ -16,5 +16,6 @@ If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="aspect-square animate-spin border-neutral-300 border-t-neutral-500 dark:border-neutral-500 dark:border-t-neutral-200 rounded-full"></div>
|
||||
<div
|
||||
class="aspect-square animate-spin border-neutral-300 border-t-neutral-500 dark:border-neutral-500 dark:border-t-neutral-200 rounded-full" />
|
||||
</template>
|
||||
|
@ -16,25 +16,45 @@ If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<TransitionRoot as="div" class="fixed inset-0 z-10 overflow-visible" :show="showModal">
|
||||
<TransitionChild as="template" enter="ease-out duration-500" enter-from="opacity-0" enter-to="opacity-100"
|
||||
leave="ease-in duration-500" leave-from="opacity-100" leave-to="opacity-0">
|
||||
<TransitionRoot
|
||||
as="div"
|
||||
class="fixed inset-0 z-10 overflow-visible"
|
||||
:show="showModal"
|
||||
>
|
||||
<TransitionChild
|
||||
as="template"
|
||||
enter="ease-out duration-500"
|
||||
enter-from="opacity-0"
|
||||
enter-to="opacity-100"
|
||||
leave="ease-in duration-500"
|
||||
leave-from="opacity-100"
|
||||
leave-to="opacity-0"
|
||||
>
|
||||
<div class="fixed z-10 inset-0 bg-neutral-500/75 dark:bg-black/50 transition-opacity pointer" />
|
||||
</TransitionChild>
|
||||
<div @click.self="$emit('close')"
|
||||
class="fixed z-10 inset-0 overflow-hidden flex items-end sm:items-center justify-center px-4 pb-20 sm:p-0">
|
||||
<TransitionChild as="template" enter="ease-out duration-300"
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-hidden flex items-end sm:items-center justify-center px-4 pb-20 sm:p-0"
|
||||
@click.self="$emit('close')"
|
||||
>
|
||||
<TransitionChild
|
||||
as="template"
|
||||
enter="ease-out duration-300"
|
||||
enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-90"
|
||||
enter-to="opacity-100 translate-y-0 sm:scale-100" leave="ease-in duration-100"
|
||||
enter-to="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-100"
|
||||
leave-from="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-75">
|
||||
leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-75"
|
||||
>
|
||||
<div
|
||||
:class="[autoWidth ? 'sm:max-w-full' : 'sm:max-w-lg', 'inline-flex flex-col items-stretch overflow-hidden transform transition-all text-left z-10']">
|
||||
<div class="block w-[512px]" /> <!-- set min width of div -->
|
||||
<div class="card flex flex-col items-stretch overflow-hidden">
|
||||
<div class="card-header">
|
||||
<slot name="header">
|
||||
<h3 class="text-header" v-html="headerText" />
|
||||
<h3
|
||||
class="text-header"
|
||||
v-html="headerText"
|
||||
/>
|
||||
</slot>
|
||||
</div>
|
||||
<div class="card-body flex flex-row items-center gap-2">
|
||||
@ -45,12 +65,20 @@ If not, see <https://www.gnu.org/licenses/>.
|
||||
</div>
|
||||
<div class="card-footer button-group-row justify-end">
|
||||
<slot name="footer">
|
||||
<button v-if="!noCancel" type="button" class="btn btn-secondary"
|
||||
@click="$emit('cancel'); $emit('close')">
|
||||
<button
|
||||
v-if="!noCancel"
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
@click="$emit('cancel'); $emit('close')"
|
||||
>
|
||||
{{ cancelText }}
|
||||
</button>
|
||||
<button type="button" :class="['btn', applyDangerous ? 'btn-danger' : 'btn-primary']"
|
||||
@click="$emit('apply'); $emit('close')" :disabled="disableContinue">
|
||||
<button
|
||||
type="button"
|
||||
:class="['btn', applyDangerous ? 'btn-danger' : 'btn-primary']"
|
||||
:disabled="disableContinue"
|
||||
@click="$emit('apply'); $emit('close')"
|
||||
>
|
||||
{{ applyText }}
|
||||
</button>
|
||||
</slot>
|
||||
|
@ -16,49 +16,93 @@ If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div aria-live="assertive"
|
||||
class="fixed inset-0 flex items-end px-4 py-6 pointer-events-none sm:p-6 sm:items-start z-20 h-screen overflow-y-auto">
|
||||
<transition-group tag="div" class="w-full flex flex-col-reverse items-center sm:items-end sm:flex-col space-y-content"
|
||||
<div
|
||||
aria-live="assertive"
|
||||
class="fixed inset-0 flex items-end px-4 py-6 pointer-events-none sm:p-6 sm:items-start z-20 h-screen overflow-y-auto"
|
||||
>
|
||||
<transition-group
|
||||
tag="div"
|
||||
class="w-full flex flex-col-reverse items-center sm:items-end sm:flex-col space-y-content"
|
||||
enter-active-class="transition-all transform ease-out duration-300"
|
||||
enter-from-class="translate-y-8 opacity-0 scale-95 sm:translate-y-0 sm:translate-x-8"
|
||||
enter-to-class="translate-y-0 opacity-100 scale-100 sm:translate-x-0"
|
||||
leave-active-class="transition-all transform ease-in duration-100"
|
||||
leave-from-class="opacity-100 scale-100 sm:translate-x-0"
|
||||
leave-to-class="opacity-0 scale-95 sm:translate-x-8">
|
||||
<div v-for="notification in notificationList" :key="notification.id"
|
||||
leave-to-class="opacity-0 scale-95 sm:translate-x-8"
|
||||
>
|
||||
<div
|
||||
v-for="notification in notificationList"
|
||||
:key="notification.id"
|
||||
class="max-w-sm w-full shadow-lg pointer-events-auto overflow-hidden bg-default text-default"
|
||||
@mouseenter="notification.clearTimeouts?.()" @mouseleave="notification.setTimeouts?.()">
|
||||
@mouseenter="notification.clearTimeouts?.()"
|
||||
@mouseleave="notification.setTimeouts?.()"
|
||||
>
|
||||
<div class="p-4">
|
||||
<div class="flex items-start">
|
||||
<div class="flex-shrink-0" aria-hidden="true">
|
||||
<ExclamationCircleIcon v-if="notification.level === 'error'" class="icon-error size-icon-lg"
|
||||
aria-hidden="true" />
|
||||
<ExclamationCircleIcon v-else-if="notification.level === 'warning'"
|
||||
class="icon-warning size-icon-lg" aria-hidden="true" />
|
||||
<CheckCircleIcon v-else-if="notification.level === 'success'"
|
||||
class="icon-success size-icon-lg" aria-hidden="true" />
|
||||
<MinusCircleIcon v-else-if="notification.level === 'denied'" class="icon-error size-icon-lg"
|
||||
aria-hidden="true" />
|
||||
<InformationCircleIcon v-else class="icon-info size-icon-lg" />
|
||||
<div
|
||||
class="flex-shrink-0"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<ExclamationCircleIcon
|
||||
v-if="notification.level === 'error'"
|
||||
class="icon-error size-icon-lg"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ExclamationCircleIcon
|
||||
v-else-if="notification.level === 'warning'"
|
||||
class="icon-warning size-icon-lg"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckCircleIcon
|
||||
v-else-if="notification.level === 'success'"
|
||||
class="icon-success size-icon-lg"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<MinusCircleIcon
|
||||
v-else-if="notification.level === 'denied'"
|
||||
class="icon-error size-icon-lg"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<InformationCircleIcon
|
||||
v-else
|
||||
class="icon-info size-icon-lg"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3 w-0 flex-1 pt-0.5">
|
||||
<p class="text-sm font-medium">{{ notification.title }}</p>
|
||||
<p class="mt-1 text-sm text-muted whitespace-pre-wrap" v-html="notification.body">
|
||||
<p
|
||||
class="mt-1 text-sm text-muted whitespace-pre-wrap"
|
||||
v-html="notification.body"
|
||||
>
|
||||
</p>
|
||||
<div v-if="notification.actions?.length" class="mt-3 flex space-x-7">
|
||||
<button v-for="action in notification.actions" @click="action.callback"
|
||||
class="rounded-md text-sm font-medium text-primary">
|
||||
<div
|
||||
v-if="notification.actions?.length"
|
||||
class="mt-3 flex space-x-7"
|
||||
>
|
||||
<button
|
||||
v-for="action in notification.actions"
|
||||
class="rounded-md text-sm font-medium text-primary"
|
||||
@click="action.callback"
|
||||
>
|
||||
{{ action.text }}
|
||||
</button>
|
||||
<button @click="notification.show = false" type="button"
|
||||
class="rounded-md text-sm font-medium text-secondary">Dismiss</button>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-md text-sm font-medium text-secondary"
|
||||
@click="notification.show = false"
|
||||
>Dismiss</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-4 flex-shrink-0 flex">
|
||||
<button @click="notification.show = false"
|
||||
class="icon-default">
|
||||
<button
|
||||
class="icon-default"
|
||||
@click="notification.show = false"
|
||||
>
|
||||
<span class="sr-only">Close</span>
|
||||
<XIcon class="size-icon" aria-hidden="true" />
|
||||
<XIcon
|
||||
class="size-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -69,7 +113,7 @@ If not, see <https://www.gnu.org/licenses/>.
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, watch, reactive, onUnmounted } from 'vue';
|
||||
import { ref, watch, reactive } from 'vue';
|
||||
import { InformationCircleIcon, ExclamationCircleIcon, MinusCircleIcon, CheckCircleIcon } from '@heroicons/vue/outline';
|
||||
import { XIcon } from '@heroicons/vue/solid';
|
||||
import { FIFO } from '@45drives/cockpit-helpers';
|
||||
|
@ -1,26 +1,73 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="inline relative">
|
||||
<input type="text" class="input-textlike pr-10" v-model="hostInput" @change="hostChangeCallback"
|
||||
ref="hostInputRef" />
|
||||
<input
|
||||
type="text"
|
||||
class="input-textlike pr-10"
|
||||
v-model="hostInput"
|
||||
ref="hostInputRef"
|
||||
@change="hostChangeCallback"
|
||||
/>
|
||||
<div class="absolute right-0 inset-y-0 pr-3 flex items-center pointer-events-none">
|
||||
<ServerIcon class="size-icon icon-default" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center cursor-text h-10 grow" @click="typing = true">
|
||||
<div
|
||||
class="flex items-center cursor-text h-10 grow"
|
||||
@click="typing = true"
|
||||
>
|
||||
<template v-if="typing">
|
||||
<input v-model="pathInput" type="text" list="pathInputSuggestions" class="block w-full input-textlike"
|
||||
@change="pathChangeCallback" ref="pathInputRef" @focusout="typing = false" />
|
||||
<input
|
||||
v-model="pathInput"
|
||||
type="text"
|
||||
list="pathInputSuggestions"
|
||||
class="block w-full input-textlike"
|
||||
@focusout="typing = false"
|
||||
@change="pathChangeCallback"
|
||||
ref="pathInputRef"
|
||||
/>
|
||||
<datalist id="pathInputSuggestions">
|
||||
<option v-for="path in pathSuggestions" :value="path" />
|
||||
<option
|
||||
v-for="path in pathSuggestions"
|
||||
:value="path"
|
||||
/>
|
||||
</datalist>
|
||||
</template>
|
||||
<div v-else class="inline-flex items-center gap-1">
|
||||
<template v-for="segment, index in pathArr" :key="index">
|
||||
<ChevronRightIcon v-if="index > 0" class="size-icon icon-default" />
|
||||
<button @click.prevent.stop="$emit('cd', { path: `/${pathArr.slice(1, index + 1).join('/')}` })"
|
||||
class="p-2 hover:bg-accent rounded-lg cursor-pointer" v-html="escapeStringHTML(segment)"
|
||||
:title="escapeString(`/${pathArr.slice(1, index + 1).join('/')}`)"></button>
|
||||
<div
|
||||
v-else
|
||||
class="inline-flex items-center gap-1"
|
||||
>
|
||||
<template
|
||||
v-for="segment, index in pathArr"
|
||||
:key="index"
|
||||
>
|
||||
<ChevronRightIcon
|
||||
v-if="index > 0"
|
||||
class="size-icon icon-default"
|
||||
/>
|
||||
<button
|
||||
class="p-2 hover:bg-accent rounded-lg cursor-pointer"
|
||||
v-html="escapeStringHTML(segment)"
|
||||
:title="escapeString(`/${pathArr.slice(1, index + 1).join('/')}`)"
|
||||
@click.prevent.stop="$emit('cd', { path: `/${pathArr.slice(1, index + 1).join('/')}` })"
|
||||
></button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,3 +1,20 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<button @click="showMenu = true">
|
||||
<AdjustmentsIcon class="size-icon icon-default" />
|
||||
@ -16,15 +33,16 @@
|
||||
<LabelledSwitch v-model="darkMode">Dark mode</LabelledSwitch>
|
||||
<LabelledSwitch v-model="settings.directoryView.showHidden">Show hidden files</LabelledSwitch>
|
||||
<LabelledSwitch v-model="booleanAnalogs.directoryView.view.bool">List view</LabelledSwitch>
|
||||
<LabelledSwitch
|
||||
v-model="settings.directoryView.separateDirs"
|
||||
>Separate directories while sorting</LabelledSwitch>
|
||||
<LabelledSwitch v-model="settings.directoryView.separateDirs">Separate directories while sorting
|
||||
</LabelledSwitch>
|
||||
</div>
|
||||
<div v-if="booleanAnalogs.directoryView.view.bool" class="self-stretch">
|
||||
<div
|
||||
v-if="booleanAnalogs.directoryView.view.bool"
|
||||
class="self-stretch"
|
||||
>
|
||||
<div>List view columns</div>
|
||||
<div
|
||||
class="flex justify-start text-sm rounded-lg divide-x divide-default border border-default shadow"
|
||||
>
|
||||
class="flex justify-start text-sm rounded-lg divide-x divide-default border border-default shadow">
|
||||
<div class="flex flex-col grow items-stretch">
|
||||
<div class="font-semibold px-2">Visible</div>
|
||||
<div
|
||||
|
@ -1,13 +1,35 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<button @click="updateModel()">
|
||||
<SortDescendingIcon v-if="reverse" :class="[funcIsMine ? 'icon-45d' : 'icon-default', 'size-icon']" />
|
||||
<SortAscendingIcon v-else :class="[funcIsMine ? 'icon-45d' : 'icon-default', 'size-icon']" />
|
||||
<SortDescendingIcon
|
||||
v-if="reverse"
|
||||
:class="[funcIsMine ? 'icon-45d' : 'icon-default', 'size-icon']"
|
||||
/>
|
||||
<SortAscendingIcon
|
||||
v-else
|
||||
:class="[funcIsMine ? 'icon-45d' : 'icon-default', 'size-icon']"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
import { SortAscendingIcon, SortDescendingIcon } from "@heroicons/vue/solid";
|
||||
import { SortDescendingIcon } from "@heroicons/vue/solid";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
@ -40,7 +40,10 @@ If not, see <https://www.gnu.org/licenses/>.
|
||||
<tbody class="bg-default w-full">
|
||||
<slot name="tbody">
|
||||
<tr>
|
||||
<td colspan="100%" class="text-center align-middle text-muted text-sm">{{ emptyText }}</td>
|
||||
<td
|
||||
colspan="100%"
|
||||
class="text-center align-middle text-muted text-sm"
|
||||
>{{ emptyText }}</td>
|
||||
</tr>
|
||||
</slot>
|
||||
</tbody>
|
||||
@ -97,7 +100,7 @@ table.houston-table thead.use-sticky tr th {
|
||||
|
||||
table.houston-table th,
|
||||
table.houston-table td {
|
||||
@apply py-2 px-4 lg:px-6 whitespace-nowrap text-sm;
|
||||
@apply py-2 px-4 lg: px-6 whitespace-nowrap text-sm;
|
||||
}
|
||||
|
||||
table.houston-table th:not(.text-right):not(.text-center),
|
||||
@ -110,6 +113,6 @@ table.houston-table th {
|
||||
}
|
||||
|
||||
table.houston-table tr {
|
||||
@apply even:bg-accent;
|
||||
@apply even: bg-accent;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
*
|
||||
* This file is part of Cockpit Navigator.
|
||||
*
|
||||
* Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const isObject = (obj) => typeof obj === 'object' && !Array.isArray(obj) && obj !== null;
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
*
|
||||
* This file is part of Cockpit Navigator.
|
||||
*
|
||||
* Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Replaces all non-printable characters with an orange span containing the escaped representation of the character
|
||||
*
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
*
|
||||
* This file is part of Cockpit Navigator.
|
||||
*
|
||||
* Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import '../globalTypedefs';
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
*
|
||||
* This file is part of Cockpit Navigator.
|
||||
*
|
||||
* Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useSpawn, errorString } from "@45drives/cockpit-helpers";
|
||||
import { UNIT_SEPARATOR, RECORD_SEPARATOR } from "../constants";
|
||||
import { szudzikPair, hashString } from "./szudzikPair";
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
*
|
||||
* This file is part of Cockpit Navigator.
|
||||
*
|
||||
* Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useSpawn } from "@45drives/cockpit-helpers";
|
||||
|
||||
export async function getGroups() {
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
*
|
||||
* This file is part of Cockpit Navigator.
|
||||
*
|
||||
* Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useSpawn } from "@45drives/cockpit-helpers";
|
||||
|
||||
export async function getUsers() {
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
*
|
||||
* This file is part of Cockpit Navigator.
|
||||
*
|
||||
* Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Perform isqrt on BigInt
|
||||
*
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
*
|
||||
* This file is part of Cockpit Navigator.
|
||||
*
|
||||
* Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { createApp, reactive } from 'vue';
|
||||
import App from './App.vue';
|
||||
import { errorString, FIFO } from '@45drives/cockpit-helpers';
|
||||
|
@ -1,69 +0,0 @@
|
||||
import { useSpawn, errorString } from '@45drives/cockpit-helpers';
|
||||
|
||||
/** Run test with given expression and return boolean result. Throws on abnormal errors.
|
||||
*
|
||||
* @param {String} check - Argument(s) to test for checking path (man test(1))
|
||||
* @param {String} path - Path to check
|
||||
* @param {Object} opts - Options for cockpit.spawn()
|
||||
* @returns {Promise<Boolean>} Result of test
|
||||
*/
|
||||
export const test = async (check, path, opts = { superuser: 'try' }) => {
|
||||
try {
|
||||
await useSpawn(['test', ...check.split(/\s+/), path], opts).promise();
|
||||
return true;
|
||||
} catch (state) {
|
||||
if (state.status === 1)
|
||||
return false;
|
||||
throw new Error("Failed to check path: " + path + ": " + errorString(state));
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if path exists, false otherwise. Throws on abnormal errors.
|
||||
*
|
||||
* @param {String} path - Path to check
|
||||
* @param {Object} opts - Options for cockpit.spawn()
|
||||
* @returns {Promise<Boolean>} Result of test
|
||||
*/
|
||||
export const checkIfExists = (path, opts = { superuser: 'try' }) => {
|
||||
return test('-e', path, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} PermissionsResult
|
||||
* @property {Boolean} r - Read permission result
|
||||
* @property {Boolean} w - Write permission result
|
||||
* @property {Boolean} x - Execute/search permission result
|
||||
*/
|
||||
|
||||
/** Get read, write, and execute permissions for user on path. Throws on abnormal errors.
|
||||
*
|
||||
* @param {String} path - Path to check
|
||||
* @param {Object} opts - Options for cockpit.spawn()
|
||||
* @returns {Promise<PermissionsResult>} - result of test
|
||||
*/
|
||||
export const testPerimssions = async (path, opts = { superuser: 'try' }) => {
|
||||
return {
|
||||
r: await test('-r', path, opts),
|
||||
w: await test('-w', path, opts),
|
||||
x: await test('-x', path, opts),
|
||||
}
|
||||
}
|
||||
|
||||
/** Check for rw permissions if path is file or rwx permissions if path is dir.
|
||||
* Returns false if DNE. Throws on abnormal errors.
|
||||
*
|
||||
* @param {String} path - Path to check
|
||||
* @param {Boolean} readOnly - Set to true to ignore write permissions
|
||||
* @returns {Promise<Boolean>} Result of check
|
||||
*/
|
||||
export const checkIfAllowed = async (path, readOnly = false) => {
|
||||
if (!await checkIfExists(path))
|
||||
return false;
|
||||
const permissions = await testPerimssions(path);
|
||||
let result = permissions.r;
|
||||
if (!readOnly)
|
||||
result &= permissions.w;
|
||||
if (await test('-d', path))
|
||||
result &= permissions.x;
|
||||
return result;
|
||||
}
|
@ -4,17 +4,28 @@
|
||||
<div
|
||||
class="grid grid-cols-[auto_1fr] grid-rows-[1fr 1fr] md:grid-cols-[auto_3fr_1fr] md:grid-row-[1fr] items-stretch divide-x divide-y divide-default">
|
||||
<div class="button-group-row p-1 md:px-4 md:py-2 border-t border-default">
|
||||
<button class="p-2 rounded-lg hover:bg-accent relative" :disabled="!pathHistory.backAllowed()"
|
||||
@click="back()" @mouseenter="backHistoryDropdown.mouseEnter"
|
||||
@mouseleave="backHistoryDropdown.mouseLeave">
|
||||
<button
|
||||
class="p-2 rounded-lg hover:bg-accent relative"
|
||||
:disabled="!pathHistory.backAllowed()"
|
||||
@click="back()"
|
||||
@mouseenter="backHistoryDropdown.mouseEnter"
|
||||
@mouseleave="backHistoryDropdown.mouseLeave"
|
||||
>
|
||||
<ArrowLeftIcon class="size-icon icon-default" />
|
||||
<ChevronDownIcon class="w-3 h-3 icon-default absolute bottom-1 right-1"
|
||||
v-if="pathHistory.backAllowed()" />
|
||||
<div v-if="backHistoryDropdown.showDropdown"
|
||||
class="absolute top-full left-0 flex flex-col items-stretch z-50 bg-default shadow-lg rounded-lg overflow-y-auto max-h-80">
|
||||
<div v-for="item, index in pathHistory.stack.slice(0, pathHistory.index).reverse()"
|
||||
:key="index" @click="pathHistory.index = pathHistory.index - index"
|
||||
class="hover:text-white hover:bg-red-600 px-4 py-2 text-sm text-left whitespace-nowrap">
|
||||
<ChevronDownIcon
|
||||
class="w-3 h-3 icon-default absolute bottom-1 right-1"
|
||||
v-if="pathHistory.backAllowed()"
|
||||
/>
|
||||
<div
|
||||
v-if="backHistoryDropdown.showDropdown"
|
||||
class="absolute top-full left-0 flex flex-col items-stretch z-50 bg-default shadow-lg rounded-lg overflow-y-auto max-h-80"
|
||||
>
|
||||
<div
|
||||
v-for="item, index in pathHistory.stack.slice(0, pathHistory.index).reverse()"
|
||||
:key="index"
|
||||
class="hover:text-white hover:bg-red-600 px-4 py-2 text-sm text-left whitespace-nowrap"
|
||||
@click="pathHistory.index = pathHistory.index - index"
|
||||
>
|
||||
<span v-if="item.host !== pathHistory.current()?.host">
|
||||
{{ item.host ?? cockpit.transport.host }}:
|
||||
</span>
|
||||
@ -22,17 +33,28 @@
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="p-2 rounded-lg hover:bg-accent relative" :disabled="!pathHistory.forwardAllowed()"
|
||||
@click="forward()" @mouseenter="forwardHistoryDropdown.mouseEnter"
|
||||
@mouseleave="forwardHistoryDropdown.mouseLeave">
|
||||
<button
|
||||
class="p-2 rounded-lg hover:bg-accent relative"
|
||||
:disabled="!pathHistory.forwardAllowed()"
|
||||
@click="forward()"
|
||||
@mouseenter="forwardHistoryDropdown.mouseEnter"
|
||||
@mouseleave="forwardHistoryDropdown.mouseLeave"
|
||||
>
|
||||
<ArrowRightIcon class="size-icon icon-default" />
|
||||
<ChevronDownIcon class="w-3 h-3 icon-default absolute bottom-1 right-1"
|
||||
v-if="pathHistory.forwardAllowed()" />
|
||||
<div v-if="forwardHistoryDropdown.showDropdown"
|
||||
class="absolute top-full left-0 flex flex-col items-stretch z-50 bg-default shadow-lg rounded-lg overflow-y-auto max-h-80">
|
||||
<div v-for="item, index in pathHistory.stack.slice(pathHistory.index + 1)" :key="index"
|
||||
<ChevronDownIcon
|
||||
class="w-3 h-3 icon-default absolute bottom-1 right-1"
|
||||
v-if="pathHistory.forwardAllowed()"
|
||||
/>
|
||||
<div
|
||||
v-if="forwardHistoryDropdown.showDropdown"
|
||||
class="absolute top-full left-0 flex flex-col items-stretch z-50 bg-default shadow-lg rounded-lg overflow-y-auto max-h-80"
|
||||
>
|
||||
<div
|
||||
v-for="item, index in pathHistory.stack.slice(pathHistory.index + 1)"
|
||||
:key="index"
|
||||
class="hover:text-white hover:bg-red-600 px-4 py-2 text-sm text-left whitespace-nowrap"
|
||||
@click="pathHistory.index = pathHistory.index + index"
|
||||
class="hover:text-white hover:bg-red-600 px-4 py-2 text-sm text-left whitespace-nowrap">
|
||||
>
|
||||
<span v-if="item.host !== pathHistory.current()?.host">
|
||||
{{ item.host ?? cockpit.transport.host }}:
|
||||
</span>
|
||||
@ -40,72 +62,153 @@
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="p-2 rounded-lg hover:bg-accent" @click="up()"
|
||||
:disabled="pathHistory.current() === '/'">
|
||||
<button
|
||||
class="p-2 rounded-lg hover:bg-accent"
|
||||
:disabled="pathHistory.current() === '/'"
|
||||
@click="up()"
|
||||
>
|
||||
<ArrowUpIcon class="size-icon icon-default" />
|
||||
</button>
|
||||
<button class="p-2 rounded-lg hover:bg-accent" @click="directoryViewRef.refresh()">
|
||||
<button
|
||||
class="p-2 rounded-lg hover:bg-accent"
|
||||
@click="directoryViewRef.refresh()"
|
||||
>
|
||||
<RefreshIcon class="size-icon icon-default" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="p-1 md:px-4 md:py-2 col-start-1 col-end-3 row-start-2 row-end-3 md:col-start-auto md:col-end-auto md:row-start-auto md:row-end-auto">
|
||||
<PathBreadCrumbs :host="pathHistory.current()?.host" :path="pathHistory.current()?.path ?? '/'"
|
||||
@cd="cd" />
|
||||
<PathBreadCrumbs
|
||||
:host="pathHistory.current()?.host"
|
||||
:path="pathHistory.current()?.path ?? '/'"
|
||||
@cd="cd"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="p-1 md:px-4 md:py-2">
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<SearchIcon class="size-icon icon-default" aria-hidden="true" />
|
||||
<SearchIcon
|
||||
class="size-icon icon-default"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<input type="text" class="block input-textlike w-full pl-10" v-model="searchFilterStr"
|
||||
placeholder="Search in directory (foo*, b?r, *.jpg)" />
|
||||
<input
|
||||
type="text"
|
||||
class="block input-textlike w-full pl-10"
|
||||
v-model="searchFilterStr"
|
||||
placeholder="Search in directory (foo*, b?r, *.jpg)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grow overflow-hidden">
|
||||
<DirectoryView :host="pathHistory.current()?.host" :path="pathHistory.current()?.path"
|
||||
:searchFilterRegExp="searchFilterRegExp" @cd="path => cd({ path })" @edit="openEditor"
|
||||
@browserAction="handleAction" ref="directoryViewRef" />
|
||||
<DirectoryView
|
||||
:host="pathHistory.current()?.host"
|
||||
:path="pathHistory.current()?.path"
|
||||
:searchFilterRegExp="searchFilterRegExp"
|
||||
@cd="path => cd({ path })"
|
||||
@edit="openEditor"
|
||||
@browserAction="handleAction"
|
||||
ref="directoryViewRef"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ModalPopup :showModal="openFilePromptModal.show" :headerText="openFilePromptModal.entry?.name ?? 'NULL'"
|
||||
@close="() => openFilePromptModal.close()" autoWidth>
|
||||
<ModalPopup
|
||||
:showModal="openFilePromptModal.show"
|
||||
:headerText="openFilePromptModal.entry?.name ?? 'NULL'"
|
||||
autoWidth
|
||||
@close="() => openFilePromptModal.close()"
|
||||
>
|
||||
What would you like to do with this {{ openFilePromptModal.entry?.resolvedTypeHuman }}?
|
||||
<template #footer>
|
||||
<button type="button" class="btn btn-secondary" @click="() => openFilePromptModal.close()">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
@click="() => openFilePromptModal.close()"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" @click="() => openFilePromptModal.action('editPermissions')">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
@click="() => openFilePromptModal.action('editPermissions')"
|
||||
>
|
||||
Edit permissions
|
||||
</button>
|
||||
<button v-if="openFilePromptModal.entry?.resolvedType === 'f'" type="button" class="btn btn-primary"
|
||||
@click="() => openFilePromptModal.action('edit')">
|
||||
<button
|
||||
v-if="openFilePromptModal.entry?.resolvedType === 'f'"
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
@click="() => openFilePromptModal.action('edit')"
|
||||
>
|
||||
Open for editing
|
||||
</button>
|
||||
<button v-if="openFilePromptModal.entry?.resolvedType === 'f'" type="button" class="btn btn-primary"
|
||||
@click="() => openFilePromptModal.action('download')">
|
||||
<button
|
||||
v-if="openFilePromptModal.entry?.resolvedType === 'f'"
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
@click="() => openFilePromptModal.action('download')"
|
||||
>
|
||||
Download
|
||||
</button>
|
||||
</template>
|
||||
</ModalPopup>
|
||||
<FilePermissions :show="filePermissions.show" @hide="filePermissions.close" :entry="filePermissions.entry" />
|
||||
<ContextMenu @browserAction="handleAction" :show="contextMenu.show" @hide="contextMenu.close" :entry="contextMenu.entry" :event="contextMenu.event" />
|
||||
<FilePermissions
|
||||
:show="filePermissions.show"
|
||||
:entry="filePermissions.entry"
|
||||
@hide="filePermissions.close"
|
||||
/>
|
||||
<ContextMenu
|
||||
:show="contextMenu.show"
|
||||
:entry="contextMenu.entry"
|
||||
:event="contextMenu.event"
|
||||
@browserAction="handleAction"
|
||||
@hide="contextMenu.close"
|
||||
/>
|
||||
<Teleport to="#footer-buttons">
|
||||
<IconToggle v-model="darkMode" v-slot="{ value }">
|
||||
<MoonIcon v-if="value" class="size-icon icon-default" />
|
||||
<SunIcon v-else class="size-icon icon-default" />
|
||||
<IconToggle
|
||||
v-model="darkMode"
|
||||
v-slot="{ value }"
|
||||
>
|
||||
<MoonIcon
|
||||
v-if="value"
|
||||
class="size-icon icon-default"
|
||||
/>
|
||||
<SunIcon
|
||||
v-else
|
||||
class="size-icon icon-default"
|
||||
/>
|
||||
</IconToggle>
|
||||
<IconToggle v-model="settings.directoryView.showHidden" v-slot="{ value }">
|
||||
<EyeIcon v-if="value" class="size-icon icon-default" />
|
||||
<EyeOffIcon v-else class="size-icon icon-default" />
|
||||
<IconToggle
|
||||
v-model="settings.directoryView.showHidden"
|
||||
v-slot="{ value }"
|
||||
>
|
||||
<EyeIcon
|
||||
v-if="value"
|
||||
class="size-icon icon-default"
|
||||
/>
|
||||
<EyeOffIcon
|
||||
v-else
|
||||
class="size-icon icon-default"
|
||||
/>
|
||||
</IconToggle>
|
||||
<IconToggle v-model="settings.directoryView.view" trueValue="list" falseValue="grid" v-slot="{ value }">
|
||||
<ViewListIcon v-if="value" class="size-icon icon-default" />
|
||||
<ViewGridIcon v-else class="size-icon icon-default" />
|
||||
<IconToggle
|
||||
v-model="settings.directoryView.view"
|
||||
trueValue="list"
|
||||
falseValue="grid"
|
||||
v-slot="{ value }"
|
||||
>
|
||||
<ViewListIcon
|
||||
v-if="value"
|
||||
class="size-icon icon-default"
|
||||
/>
|
||||
<ViewGridIcon
|
||||
v-else
|
||||
class="size-icon icon-default"
|
||||
/>
|
||||
</IconToggle>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
@ -1,3 +1,20 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="grow">Editing {{ path }}</div>
|
||||
</template>
|
||||
|
@ -1,8 +1,25 @@
|
||||
<!--
|
||||
Copyright (C) 2022 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
Cockpit Navigator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Cockpit Navigator.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="grow flex flex-col items-center justify-center gap-10">
|
||||
<div class="text-header">{{title}}</div>
|
||||
<div class="text-muted">{{message}}</div>
|
||||
<div class="text-muted">Redirecting in {{counter}}...</div>
|
||||
<div class="text-header">{{ title }}</div>
|
||||
<div class="text-muted">{{ message }}</div>
|
||||
<div class="text-muted">Redirecting in {{ counter }}...</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user