feat: added right-click context-menu

This commit is contained in:
Omar Hussein 2023-12-09 13:25:40 -05:00
parent cfafefa35a
commit 924bac6667
6 changed files with 190 additions and 36 deletions

View File

@ -184,6 +184,12 @@ table th {
border-top: 1px solid var(--divider); border-top: 1px solid var(--divider);
} }
.context-menu {
background: var(--surfacePrimary);
border: 1px solid var(--divider);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
#editor-container { #editor-container {
background: var(--background); background: var(--background);
} }

View File

@ -0,0 +1,48 @@
<template>
<div
class="context-menu"
ref="contextMenu"
v-show="show"
:style="{
top: `${top}px`,
left: `${left}px`,
}"
>
<slot />
</div>
</template>
<script>
export default {
name: "context-menu",
props: ["show", "pos"],
computed: {
top() {
return Math.min(
this.pos.y,
window.innerHeight - this.$refs.contextMenu?.clientHeight ?? 0
);
},
left() {
return Math.min(
this.pos.x,
window.innerWidth - this.$refs.contextMenu?.clientWidth ?? 0
);
},
},
methods: {
hideContextMenu() {
this.$emit("hide");
},
},
watch: {
show: function (val) {
if (val) {
document.addEventListener("click", this.hideContextMenu);
} else {
document.removeEventListener("click", this.hideContextMenu);
}
},
},
};
</script>

View File

@ -8,6 +8,7 @@
@dragover="dragOver" @dragover="dragOver"
@drop="drop" @drop="drop"
@click="itemClick" @click="itemClick"
@contextmenu="contextMenu"
:data-dir="isDir" :data-dir="isDir"
:data-type="type" :data-type="type"
:aria-label="name" :aria-label="name"
@ -194,6 +195,25 @@ export default {
if (this.singleClick && !this.$store.state.multiple) this.open(); if (this.singleClick && !this.$store.state.multiple) this.open();
else this.click(event); else this.click(event);
}, },
contextMenu: function (event) {
const to = setTimeout(() => {
this.touches = 0;
}, 300);
this.touches++;
if (this.touches > 1) return;
event.preventDefault();
if (
this.selected.length < 2 ||
event.ctrlKey ||
this.$store.state.selected.indexOf(this.index) === -1
) {
this.touches--;
clearTimeout(to);
this.click(event);
}
},
click: function (event) { click: function (event) {
if (!this.singleClick && this.selectedCount !== 0) event.preventDefault(); if (!this.singleClick && this.selectedCount !== 0) event.preventDefault();

View File

@ -0,0 +1,16 @@
.context-menu {
position: fixed;
background: #ffffff;
min-width: 180px;
border: 1px solid rgba(0, 0, 0, 0.05);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
z-index: 1000;
}
.context-menu .action {
display: block;
width: 100%;
border-radius: 0;
display: flex;
align-items: center;
}

View File

@ -15,6 +15,7 @@
@import "./dashboard.css"; @import "./dashboard.css";
@import "./login.css"; @import "./login.css";
@import "./mobile.css"; @import "./mobile.css";
@import "./context-menu.css";
.link { .link {
color: var(--blue); color: var(--blue);
@ -434,4 +435,4 @@ body.rtl .card-content .small + input {
body.rtl .card.floating .card-content .file-list { body.rtl .card.floating .card-content .file-list {
direction: ltr; direction: ltr;
text-align: left; text-align: left;
} }

View File

@ -198,39 +198,88 @@
</div> </div>
</div> </div>
<h2 v-if="req.numDirs > 0">{{ $t("files.folders") }}</h2> <span @contextmenu="showContextMenu">
<div v-if="req.numDirs > 0"> <h2 v-if="req.numDirs > 0">{{ $t("files.folders") }}</h2>
<item <div v-if="req.numDirs > 0">
v-for="item in dirs" <item
:key="base64(item.name)" v-for="item in dirs"
v-bind:index="item.index" :key="base64(item.name)"
v-bind:name="item.name" v-bind:index="item.index"
v-bind:isDir="item.isDir" v-bind:name="item.name"
v-bind:url="item.url" v-bind:isDir="item.isDir"
v-bind:modified="item.modified" v-bind:url="item.url"
v-bind:type="item.type" v-bind:modified="item.modified"
v-bind:size="item.size" v-bind:type="item.type"
v-bind:path="item.path" v-bind:size="item.size"
> v-bind:path="item.path"
</item> >
</div> </item>
</div>
<h2 v-if="req.numFiles > 0">{{ $t("files.files") }}</h2> <h2 v-if="req.numFiles > 0">{{ $t("files.files") }}</h2>
<div v-if="req.numFiles > 0"> <div v-if="req.numFiles > 0">
<item <item
v-for="item in files" v-for="item in files"
:key="base64(item.name)" :key="base64(item.name)"
v-bind:index="item.index" v-bind:index="item.index"
v-bind:name="item.name" v-bind:name="item.name"
v-bind:isDir="item.isDir" v-bind:isDir="item.isDir"
v-bind:url="item.url" v-bind:url="item.url"
v-bind:modified="item.modified" v-bind:modified="item.modified"
v-bind:type="item.type" v-bind:type="item.type"
v-bind:size="item.size" v-bind:size="item.size"
v-bind:path="item.path" v-bind:path="item.path"
>
</item>
</div>
<context-menu
:show="isContextMenuVisible"
:pos="contextMenuPos"
@hide="hideContextMenu"
> >
</item> <action
</div> v-if="headerButtons.share"
icon="share"
:label="$t('buttons.share')"
show="share"
/>
<action
v-if="headerButtons.rename"
icon="mode_edit"
:label="$t('buttons.rename')"
show="rename"
/>
<action
v-if="headerButtons.copy"
id="copy-button"
icon="content_copy"
:label="$t('buttons.copyFile')"
show="copy"
/>
<action
v-if="headerButtons.move"
id="move-button"
icon="forward"
:label="$t('buttons.moveFile')"
show="move"
/>
<action
v-if="headerButtons.delete"
id="delete-button"
icon="delete"
:label="$t('buttons.delete')"
show="delete"
/>
<action
v-if="headerButtons.download"
icon="file_download"
:label="$t('buttons.download')"
@action="download"
:counter="selectedCount"
/>
<action icon="info" :label="$t('buttons.info')" show="info" />
</context-menu>
</span>
<input <input
style="display: none" style="display: none"
@ -278,6 +327,7 @@ import throttle from "lodash.throttle";
import HeaderBar from "@/components/header/HeaderBar.vue"; import HeaderBar from "@/components/header/HeaderBar.vue";
import Action from "@/components/header/Action.vue"; import Action from "@/components/header/Action.vue";
import Search from "@/components/Search.vue"; import Search from "@/components/Search.vue";
import ContextMenu from "@/components/ContextMenu.vue";
import Item from "@/components/files/ListingItem.vue"; import Item from "@/components/files/ListingItem.vue";
export default { export default {
@ -287,6 +337,7 @@ export default {
Action, Action,
Search, Search,
Item, Item,
ContextMenu,
}, },
data: function () { data: function () {
return { return {
@ -295,10 +346,12 @@ export default {
dragCounter: 0, dragCounter: 0,
width: window.innerWidth, width: window.innerWidth,
itemWeight: 0, itemWeight: 0,
isContextMenuVisible: false,
contextMenuPos: { x: 0, y: 0 },
}; };
}, },
computed: { computed: {
...mapState(["req", "selected", "user", "multiple", "selected", "loading"]), ...mapState(["req", "user", "multiple", "selected", "loading"]),
...mapGetters(["selectedCount", "currentPrompt"]), ...mapGetters(["selectedCount", "currentPrompt"]),
nameSorted() { nameSorted() {
return this.req.sorting.by === "name"; return this.req.sorting.by === "name";
@ -398,7 +451,7 @@ export default {
}, },
mounted: function () { mounted: function () {
// Check the columns size for the first time. // Check the columns size for the first time.
this.colunmsResize(); this.columnsResize();
// How much every listing item affects the window height // How much every listing item affects the window height
this.setItemWeight(); this.setItemWeight();
@ -597,7 +650,7 @@ export default {
action(overwrite, rename); action(overwrite, rename);
}, },
colunmsResize() { columnsResize() {
// Update the columns size based on the window width. // Update the columns size based on the window width.
let items = css(["#listing.mosaic .item", ".mosaic#listing .item"]); let items = css(["#listing.mosaic .item", ".mosaic#listing .item"]);
if (!items) return; if (!items) return;
@ -788,7 +841,7 @@ export default {
this.$store.commit("closeHovers"); this.$store.commit("closeHovers");
}, },
windowsResize: throttle(function () { windowsResize: throttle(function () {
this.colunmsResize(); this.columnsResize();
this.width = window.innerWidth; this.width = window.innerWidth;
// Listing element is not displayed // Listing element is not displayed
@ -886,6 +939,16 @@ export default {
// Set the number of displayed items // Set the number of displayed items
this.showLimit = showQuantity > totalItems ? totalItems : showQuantity; this.showLimit = showQuantity > totalItems ? totalItems : showQuantity;
}, },
showContextMenu(event) {
this.isContextMenuVisible = true;
this.contextMenuPos = {
x: event.clientX,
y: event.clientY,
};
},
hideContextMenu() {
this.isContextMenuVisible = false;
},
}, },
}; };
</script> </script>