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);
}
.context-menu {
background: var(--surfacePrimary);
border: 1px solid var(--divider);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
#editor-container {
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"
@drop="drop"
@click="itemClick"
@contextmenu="contextMenu"
:data-dir="isDir"
:data-type="type"
:aria-label="name"
@ -194,6 +195,25 @@ export default {
if (this.singleClick && !this.$store.state.multiple) this.open();
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) {
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 "./login.css";
@import "./mobile.css";
@import "./context-menu.css";
.link {
color: var(--blue);
@ -434,4 +435,4 @@ body.rtl .card-content .small + input {
body.rtl .card.floating .card-content .file-list {
direction: ltr;
text-align: left;
}
}

View File

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