|
@ -50,6 +50,7 @@ func setDefaults() Settings {
|
||||||
AdminUsername: "admin",
|
AdminUsername: "admin",
|
||||||
AdminPassword: "admin",
|
AdminPassword: "admin",
|
||||||
Server: Server{
|
Server: Server{
|
||||||
|
EnableThumbnails: true,
|
||||||
EnableExec: false,
|
EnableExec: false,
|
||||||
IndexingInterval: 5,
|
IndexingInterval: 5,
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
|
|
Before Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 843 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 190 KiB |
Before Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 4.9 KiB |
|
@ -33,13 +33,14 @@ header {
|
||||||
|
|
||||||
#search #input {
|
#search #input {
|
||||||
background: var(--surfaceSecondary);
|
background: var(--surfaceSecondary);
|
||||||
border-color: var(--surfacePrimary);
|
border-color: var(--surfaceSecondary);
|
||||||
}
|
}
|
||||||
#search #input input::placeholder {
|
#search #input input::placeholder {
|
||||||
color: var(--textSecondary);
|
color: var(--textSecondary);
|
||||||
}
|
}
|
||||||
#search.active #input {
|
#search.active #input {
|
||||||
background: var(--surfacePrimary);
|
background: var(--surfacePrimary);
|
||||||
|
border-color: white;
|
||||||
}
|
}
|
||||||
#search.active input {
|
#search.active input {
|
||||||
color: var(--textPrimary);
|
color: var(--textPrimary);
|
||||||
|
@ -131,8 +132,8 @@ nav > div {
|
||||||
.input {
|
.input {
|
||||||
background: var(--surfaceSecondary);
|
background: var(--surfaceSecondary);
|
||||||
color: var(--textPrimary);
|
color: var(--textPrimary);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.input:hover,
|
.input:hover,
|
||||||
.input:focus {
|
.input:focus {
|
||||||
border-color: rgba(255, 255, 255, 0.15);
|
border-color: rgba(255, 255, 255, 0.15);
|
||||||
|
@ -215,5 +216,5 @@ nav {
|
||||||
}
|
}
|
||||||
#result-desktop #result-list {
|
#result-desktop #result-list {
|
||||||
background: #2a3137;
|
background: #2a3137;
|
||||||
|
max-height: unset;
|
||||||
}
|
}
|
|
@ -1,27 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="search" @click="open" v-bind:class="{ active, ongoing }">
|
<div id="search" @click="open" v-bind:class="{ active, ongoing }">
|
||||||
<div id="input">
|
<div id="input">
|
||||||
<button
|
<button v-if="active" class="action" @click="close" :aria-label="$t('buttons.close')" :title="$t('buttons.close')">
|
||||||
v-if="active"
|
|
||||||
class="action"
|
|
||||||
@click="close"
|
|
||||||
:aria-label="$t('buttons.close')"
|
|
||||||
:title="$t('buttons.close')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">close</i>
|
<i class="material-icons">close</i>
|
||||||
</button>
|
</button>
|
||||||
<i v-else class="material-icons">search</i>
|
<i v-else class="material-icons">search</i>
|
||||||
<input
|
<input class="main-input" type="text" @keyup.exact="keyup" @input="submit" ref="input" :autofocus="active"
|
||||||
class="main-input"
|
v-model.trim="value" :aria-label="$t('search.search')" :placeholder="$t('search.search')" />
|
||||||
type="text"
|
|
||||||
@keyup.exact="keyup"
|
|
||||||
@input="submit"
|
|
||||||
ref="input"
|
|
||||||
:autofocus="active"
|
|
||||||
v-model.trim="value"
|
|
||||||
:aria-label="$t('search.search')"
|
|
||||||
:placeholder="$t('search.search')"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isMobile && active" id="result" :class="{ hidden: !active }" ref="result">
|
<div v-if="isMobile && active" id="result" :class="{ hidden: !active }" ref="result">
|
||||||
<div id="result-list">
|
<div id="result-list">
|
||||||
|
@ -29,12 +14,7 @@
|
||||||
Search Context: {{ getContext(this.$route.path) }}
|
Search Context: {{ getContext(this.$route.path) }}
|
||||||
</div>
|
</div>
|
||||||
<ul v-show="results.length > 0">
|
<ul v-show="results.length > 0">
|
||||||
<li
|
<li v-for="(s, k) in results" :key="k" @click.stop.prevent="navigateTo(s.url)" style="cursor: pointer">
|
||||||
v-for="(s, k) in results"
|
|
||||||
:key="k"
|
|
||||||
@click.stop.prevent="navigateTo(s.url)"
|
|
||||||
style="cursor: pointer"
|
|
||||||
>
|
|
||||||
<router-link to="#" event="">
|
<router-link to="#" event="">
|
||||||
<i v-if="s.dir" class="material-icons folder-icons"> folder </i>
|
<i v-if="s.dir" class="material-icons folder-icons"> folder </i>
|
||||||
<i v-else-if="s.audio" class="material-icons audio-icons"> volume_up </i>
|
<i v-else-if="s.audio" class="material-icons audio-icons"> volume_up </i>
|
||||||
|
@ -57,26 +37,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="isEmpty">
|
<template v-if="isEmpty">
|
||||||
<button
|
<button class="mobile-boxes" v-if="value.length === 0 && !showBoxes" @click="resetSearchFilters()">
|
||||||
class="mobile-boxes"
|
|
||||||
v-if="value.length === 0 && !showBoxes"
|
|
||||||
@click="resetSearchFilters()"
|
|
||||||
>
|
|
||||||
Reset filters
|
Reset filters
|
||||||
</button>
|
</button>
|
||||||
<template v-if="value.length === 0 && showBoxes">
|
<template v-if="value.length === 0 && showBoxes">
|
||||||
<div class="boxes">
|
<div class="boxes">
|
||||||
<h3>{{ $t("search.types") }}</h3>
|
<h3>{{ $t("search.types") }}</h3>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div class="mobile-boxes" tabindex="0" v-for="(v, k) in boxes" :key="k" role="button"
|
||||||
class="mobile-boxes"
|
@click="addToTypes('type:' + k)" :aria-label="v.label">
|
||||||
tabindex="0"
|
|
||||||
v-for="(v, k) in boxes"
|
|
||||||
:key="k"
|
|
||||||
role="button"
|
|
||||||
@click="addToTypes('type:' + k)"
|
|
||||||
:aria-label="v.label"
|
|
||||||
>
|
|
||||||
<i class="material-icons">{{ v.icon }}</i>
|
<i class="material-icons">{{ v.icon }}</i>
|
||||||
<p>{{ v.label }}</p>
|
<p>{{ v.label }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -121,52 +90,26 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<template>
|
<template>
|
||||||
<ButtonGroup
|
<ButtonGroup :buttons="folderSelect" @button-clicked="addToTypes" @remove-button-clicked="removeFromTypes"
|
||||||
:buttons="folderSelect"
|
@disableAll="folderSelectClicked()" @enableAll="resetButtonGroups()" />
|
||||||
@button-clicked="addToTypes"
|
<ButtonGroup :buttons="typeSelect" @button-clicked="addToTypes" @remove-button-clicked="removeFromTypes"
|
||||||
@remove-button-clicked="removeFromTypes"
|
:isDisabled="isTypeSelectDisabled" />
|
||||||
@disableAll="folderSelectClicked()"
|
|
||||||
@enableAll="resetButtonGroups()"
|
|
||||||
/>
|
|
||||||
<ButtonGroup
|
|
||||||
:buttons="typeSelect"
|
|
||||||
@button-clicked="addToTypes"
|
|
||||||
@remove-button-clicked="removeFromTypes"
|
|
||||||
:isDisabled="isTypeSelectDisabled"
|
|
||||||
/>
|
|
||||||
<div class="sizeConstraints">
|
<div class="sizeConstraints">
|
||||||
<div class="sizeInputWrapper">
|
<div class="sizeInputWrapper">
|
||||||
<p>Smaller Than:</p>
|
<p>Smaller Than:</p>
|
||||||
<input
|
<input class="sizeInput" v-model="smallerThan" type="number" min="0" placeholder="number" />
|
||||||
|
|
||||||
class="sizeInput"
|
|
||||||
v-model="smallerThan"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
placeholder="number"
|
|
||||||
/>
|
|
||||||
<p>MB</p>
|
<p>MB</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="sizeInputWrapper">
|
<div class="sizeInputWrapper">
|
||||||
<p>Larger Than:</p>
|
<p>Larger Than:</p>
|
||||||
<input
|
<input class="sizeInput" v-model="largerThan" type="number" placeholder="number" />
|
||||||
class="sizeInput"
|
|
||||||
v-model="largerThan"
|
|
||||||
type="number"
|
|
||||||
placeholder="number"
|
|
||||||
/>
|
|
||||||
<p>MB</p>
|
<p>MB</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<ul v-show="results.length > 0">
|
<ul v-show="results.length > 0">
|
||||||
<li
|
<li v-for="(s, k) in results" :key="k" @click.stop.prevent="navigateTo(s.url)" style="cursor: pointer">
|
||||||
v-for="(s, k) in results"
|
|
||||||
:key="k"
|
|
||||||
@click.stop.prevent="navigateTo(s.url)"
|
|
||||||
style="cursor: pointer"
|
|
||||||
>
|
|
||||||
<router-link to="#" event="">
|
<router-link to="#" event="">
|
||||||
<i v-if="s.dir" class="material-icons folder-icons"> folder </i>
|
<i v-if="s.dir" class="material-icons folder-icons"> folder </i>
|
||||||
<i v-else-if="s.audio" class="material-icons audio-icons"> volume_up </i>
|
<i v-else-if="s.audio" class="material-icons audio-icons"> volume_up </i>
|
||||||
|
@ -189,43 +132,56 @@
|
||||||
.main-input {
|
.main-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchContext {
|
.searchContext {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
background: var(--blue);
|
background: var(--blue);
|
||||||
color: white;
|
color: white;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
border-left: 1px solid gray;
|
||||||
|
border-right: 1px solid gray;
|
||||||
}
|
}
|
||||||
#result-desktop #result-list {
|
|
||||||
|
#result-desktop>#result-list {
|
||||||
|
max-height: 80vh;
|
||||||
|
width: 35em;
|
||||||
|
overflow: scroll;
|
||||||
|
padding-bottom: 1em;
|
||||||
|
-webkit-transition: width 0.3s ease 0s;
|
||||||
transition: width 0.3s ease 0s;
|
transition: width 0.3s ease 0s;
|
||||||
max-width: 100%;
|
}
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
#result-desktop {
|
||||||
border-color: gray;
|
-webkit-animation: SlideDown 0.5s forwards;
|
||||||
width: 30em;
|
animation: SlideDown 0.5s forwards;
|
||||||
padding-top: 0em;
|
border-radius: 1m;
|
||||||
border-color: #a7a7a7;
|
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
|
max-height: 100%;
|
||||||
border-top: none;
|
border-top: none;
|
||||||
|
border-top-width: initial;
|
||||||
|
border-top-style: none;
|
||||||
|
border-top-color: initial;
|
||||||
border-top-left-radius: 0px;
|
border-top-left-radius: 0px;
|
||||||
border-top-right-radius: 0px;
|
border-top-right-radius: 0px;
|
||||||
background: white;
|
|
||||||
max-height: 80vh;
|
|
||||||
left: 50%;
|
|
||||||
-webkit-transform: translateX(-50%);
|
-webkit-transform: translateX(-50%);
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
-webkit-box-shadow: 0px 2em 50px 10px rgba(0, 0, 0, 0.3);
|
-webkit-box-shadow: 0px 2em 50px 10px rgba(0, 0, 0, 0.3);
|
||||||
box-shadow: 0px 2em 50px 10px rgba(0, 0, 0, 0.3);
|
box-shadow: 0px 2em 50px 10px rgba(0, 0, 0, 0.3);
|
||||||
|
background-color: lightgray;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search.active #result-desktop ul li a {
|
#search.active #result-desktop ul li a {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: .3em 0;
|
padding: .3em 0;
|
||||||
margin-right: .3em;
|
margin-right: .3em;
|
||||||
}
|
}
|
||||||
#result-list.active {
|
|
||||||
|
#search #result-list.active {
|
||||||
width: 65em !important;
|
width: 65em !important;
|
||||||
max-width: 85vw !important;
|
max-width: 85vw !important;
|
||||||
}
|
}
|
||||||
|
@ -236,18 +192,22 @@
|
||||||
transform: translateY(-3em);
|
transform: translateY(-3em);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Search */
|
/* Search */
|
||||||
#search {
|
#search {
|
||||||
flex-basis: auto;
|
z-index:3;
|
||||||
|
position: fixed;
|
||||||
|
top: .5em;
|
||||||
min-width: 35em;
|
min-width: 35em;
|
||||||
height: 3em;
|
left: 50%;
|
||||||
display: flex;
|
-webkit-transform: translateX(-50%);
|
||||||
flex-direction: column;
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
#search #input {
|
#search #input {
|
||||||
|
@ -256,14 +216,11 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 0em 0.75em;
|
padding: 0em 0.75em;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-color: #ccc;
|
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
border-style: solid;
|
border-style: unset;
|
||||||
border-color: #a7a7a7;
|
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
transition: 1s ease all;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
z-index: 3;
|
height: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search input {
|
#search input {
|
||||||
|
@ -283,8 +240,10 @@
|
||||||
|
|
||||||
/* Hiding scrollbar for IE, Edge and Firefox */
|
/* Hiding scrollbar for IE, Edge and Firefox */
|
||||||
#result-list {
|
#result-list {
|
||||||
scrollbar-width: none; /* Firefox */
|
scrollbar-width: none;
|
||||||
-ms-overflow-style: none; /* IE and Edge */
|
/* Firefox */
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
/* IE and Edge */
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-container {
|
.text-container {
|
||||||
|
@ -309,6 +268,7 @@
|
||||||
color: rgba(0, 0, 0, 0.6);
|
color: rgba(0, 0, 0, 0.6);
|
||||||
height: 0;
|
height: 0;
|
||||||
transition: 2s ease height, 2s ease padding;
|
transition: 2s ease height, 2s ease padding;
|
||||||
|
transition: 2s ease width, 2s ease padding;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,7 +276,7 @@ body.rtl #search #result {
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search #result > div > *:first-child {
|
#search #result>div>*:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +286,7 @@ body.rtl #search #result {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Search Results */
|
/* Search Results */
|
||||||
body.rtl #search #result ul > * {
|
body.rtl #search #result ul>* {
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
@ -352,12 +312,14 @@ body.rtl #search #result ul > * {
|
||||||
#search.ongoing #renew {
|
#search.ongoing #renew {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search.active #input {
|
#search.active #input {
|
||||||
border-bottom-left-radius: 0px;
|
background-color: lightgray;
|
||||||
border-bottom-right-radius: 0px;
|
border-color: black;
|
||||||
|
border-style: solid;
|
||||||
border-bottom-style: none;
|
border-bottom-style: none;
|
||||||
border-bottom-left-radius: 0px;
|
border-bottom-right-radius: 0;
|
||||||
border-bottom-right-radius: 0px;
|
border-bottom-left-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Search Input Placeholder */
|
/* Search Input Placeholder */
|
||||||
|
@ -406,10 +368,6 @@ body.rtl #search .boxes h3 {
|
||||||
font-size: 3.5em;
|
font-size: 3.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#result-desktop {
|
|
||||||
animation: SlideDown 0.5s forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-boxes {
|
.mobile-boxes {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -420,6 +378,7 @@ body.rtl #search .boxes h3 {
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hiding scrollbar for Chrome, Safari and Opera */
|
/* Hiding scrollbar for Chrome, Safari and Opera */
|
||||||
.mobile-boxes::-webkit-scrollbar {
|
.mobile-boxes::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -427,9 +386,12 @@ body.rtl #search .boxes h3 {
|
||||||
|
|
||||||
/* Hiding scrollbar for IE, Edge and Firefox */
|
/* Hiding scrollbar for IE, Edge and Firefox */
|
||||||
.mobile-boxes {
|
.mobile-boxes {
|
||||||
scrollbar-width: none; /* Firefox */
|
scrollbar-width: none;
|
||||||
-ms-overflow-style: none; /* IE and Edge */
|
/* Firefox */
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
/* IE and Edge */
|
||||||
}
|
}
|
||||||
|
|
||||||
.helpText {
|
.helpText {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
@ -455,17 +417,17 @@ body.rtl #search .boxes h3 {
|
||||||
|
|
||||||
.sizeInputWrapper {
|
.sizeInputWrapper {
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
display: -ms-flexbox;
|
display: -ms-flexbox;
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: rgb(245, 245, 245);
|
background-color: rgb(245, 245, 245);
|
||||||
padding: 0.25em;
|
padding: 0.25em;
|
||||||
height: 3em;
|
height: 3em;
|
||||||
-webkit-box-align: center;
|
-webkit-box-align: center;
|
||||||
-ms-flex-align: center;
|
-ms-flex-align: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px solid #ccc
|
border: 1px solid #ccc
|
||||||
}
|
}
|
||||||
|
|
||||||
.helpButton {
|
.helpButton {
|
||||||
|
@ -545,7 +507,7 @@ export default {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resultList.classList.add("active");
|
resultList.classList.add("active");
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
},
|
},
|
||||||
show(val, old) {
|
show(val, old) {
|
||||||
this.active = val === "search";
|
this.active = val === "search";
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<i class="material-icons">note_add</i>
|
<i class="material-icons">note_add</i>
|
||||||
<span>{{ $t("sidebar.newFile") }}</span>
|
<span>{{ $t("sidebar.newFile") }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="upload-button" @click="upload($event)" class="action" :aria-label="$t('sidebar.upload')" >
|
<button id="upload-button" @click="upload($event)" class="action" >
|
||||||
<i class="material-icons">file_upload</i>
|
<i class="material-icons">file_upload</i>
|
||||||
<span>Upload file</span>
|
<span>Upload file</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -126,8 +126,13 @@ main {
|
||||||
padding-top: 4em;
|
padding-top: 4em;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
top: 0;
|
top: 0;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
main > div {
|
||||||
|
height: calc(100% - 3em);
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumbs {
|
.breadcrumbs {
|
||||||
|
|
|
@ -53,11 +53,6 @@ header .action span {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
header>div div {
|
|
||||||
vertical-align: middle;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Icon Colors */
|
/* Icon Colors */
|
||||||
.folder-icons {
|
.folder-icons {
|
||||||
color: var(--icon-blue);
|
color: var(--icon-blue);
|
||||||
|
|
|
@ -54,16 +54,20 @@
|
||||||
}
|
}
|
||||||
#search {
|
#search {
|
||||||
min-width: unset;
|
min-width: unset;
|
||||||
|
max-width: 60%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search.active {
|
#search.active {
|
||||||
display: block;
|
display: block;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 50%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
#search #input {
|
||||||
|
transition: 1s ease all;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search.active #input {
|
#search.active #input {
|
||||||
border-bottom: 3px solid rgba(0, 0, 0, 0.075);
|
border-bottom: 3px solid rgba(0, 0, 0, 0.075);
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
|
|
@ -280,15 +280,11 @@ main .spinner .bounce2 {
|
||||||
#editor-container {
|
#editor-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #fafafa;
|
background-color: none;
|
||||||
position: fixed;
|
|
||||||
padding-top: 4em;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 9999;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
#editor-container #editor {
|
#editor-container #editor {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<header-bar v-if="showHeader" showMenu showLogo />
|
|
||||||
|
|
||||||
<h2 class="message">
|
<h2 class="message">
|
||||||
<i class="material-icons">{{ info.icon }}</i>
|
<i class="material-icons">{{ info.icon }}</i>
|
||||||
<span>{{ $t(info.message) }}</span>
|
<span>{{ $t(info.message) }}</span>
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<header-bar v-if="error || req.type == null" showMenu showLogo />
|
|
||||||
|
|
||||||
<breadcrumbs base="/files" />
|
<breadcrumbs base="/files" />
|
||||||
|
|
||||||
<errors v-if="error" :errorCode="error.status" />
|
<errors v-if="error" :errorCode="error.status" />
|
||||||
|
@ -91,8 +89,12 @@ export default {
|
||||||
}
|
}
|
||||||
this.$store.commit("updateRequest", {});
|
this.$store.commit("updateRequest", {});
|
||||||
},
|
},
|
||||||
|
currentView(newView) {
|
||||||
|
// Commit the new value to the store
|
||||||
|
this.setCurrentValue(this.newValue);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(["setLoading"]),
|
...mapMutations(["setLoading","setCurrentView"]),
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
// Reset view information.
|
// Reset view information.
|
||||||
this.$store.commit("setReload", false);
|
this.$store.commit("setReload", false);
|
||||||
|
|
|
@ -3,9 +3,8 @@
|
||||||
<div v-if="progress" class="progress">
|
<div v-if="progress" class="progress">
|
||||||
<div v-bind:style="{ width: this.progress + '%' }"></div>
|
<div v-bind:style="{ width: this.progress + '%' }"></div>
|
||||||
</div>
|
</div>
|
||||||
<editorBar v-if="getCurrentView === 'editor'"></editorBar>
|
<listingBar v-if="currentView === 'listing'"></listingBar>
|
||||||
<listingBar v-else-if="getCurrentView === 'listing'"></listingBar>
|
<editorBar v-else-if="currentView === 'editor'"></editorBar>
|
||||||
<previewBar v-else-if="getCurrentView === 'preview'"></previewBar>
|
|
||||||
<defaultBar v-else></defaultBar>
|
<defaultBar v-else></defaultBar>
|
||||||
<sidebar></sidebar>
|
<sidebar></sidebar>
|
||||||
<main>
|
<main>
|
||||||
|
@ -17,24 +16,21 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import editorBar from "./files/Editor.vue"
|
import editorBar from "./bars/EditorBar.vue"
|
||||||
import defaultBar from "./files/Default.vue"
|
import defaultBar from "./bars/Default.vue"
|
||||||
import listingBar from"./files/Listing.vue"
|
import listingBar from "./bars/ListingBar.vue"
|
||||||
import previewBar from "./files/Preview.vue"
|
|
||||||
import Prompts from "@/components/prompts/Prompts";
|
import Prompts from "@/components/prompts/Prompts";
|
||||||
import Action from "@/components/header/Action";
|
import Action from "@/components/header/Action";
|
||||||
import { mapState, mapGetters } from "vuex";
|
import { mapState, mapGetters } from "vuex";
|
||||||
import Sidebar from "@/components/Sidebar.vue";
|
import Sidebar from "@/components/Sidebar.vue";
|
||||||
import UploadFiles from "../components/prompts/UploadFiles";
|
import UploadFiles from "../components/prompts/UploadFiles";
|
||||||
import { enableExec } from "@/utils/constants";
|
import { enableExec } from "@/utils/constants";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "layout",
|
name: "layout",
|
||||||
components: {
|
components: {
|
||||||
defaultBar,
|
defaultBar,
|
||||||
editorBar,
|
editorBar,
|
||||||
listingBar,
|
listingBar,
|
||||||
previewBar,
|
|
||||||
Action,
|
Action,
|
||||||
Sidebar,
|
Sidebar,
|
||||||
Prompts,
|
Prompts,
|
||||||
|
@ -49,18 +45,28 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(["isLogged", "progress"]),
|
...mapGetters(["isLogged", "progress", "isListing"]),
|
||||||
...mapState(["req", "user", "currentView"]),
|
...mapState(["req", "user", "state"]),
|
||||||
|
|
||||||
isExecEnabled: () => enableExec,
|
isExecEnabled: () => enableExec,
|
||||||
getCurrentView() {
|
currentView() {
|
||||||
return this.currentView;
|
if (this.req.type == undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.req.isDir) {
|
||||||
|
return "listing";
|
||||||
|
} else if (
|
||||||
|
this.req.type === "text" ||
|
||||||
|
this.req.type === "textImmutable"
|
||||||
|
) {
|
||||||
|
return "editor";
|
||||||
|
} else {
|
||||||
|
return "preview";
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
getCurrentView: function () {
|
|
||||||
console.log(this.currentView)
|
|
||||||
},
|
|
||||||
$route: function () {
|
$route: function () {
|
||||||
this.$store.commit("resetSelected");
|
this.$store.commit("resetSelected");
|
||||||
this.$store.commit("multiple", false);
|
this.$store.commit("multiple", false);
|
||||||
|
@ -70,7 +76,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
getTitle() {
|
getTitle() {
|
||||||
let title = "Title"
|
let title = "Title"
|
||||||
if (this.$route.path.startsWith('/settings/')){
|
if (this.$route.path.startsWith('/settings/')) {
|
||||||
title = "Settings"
|
title = "Settings"
|
||||||
}
|
}
|
||||||
return title
|
return title
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="dashboard">
|
<div class="dashboard">
|
||||||
<header-bar showMenu showLogo />
|
|
||||||
|
|
||||||
<div id="nav">
|
<div id="nav">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -52,15 +50,14 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
|
|
||||||
import HeaderBar from "@/components/header/HeaderBar";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "settings",
|
name: "settings",
|
||||||
components: {
|
mounted() {
|
||||||
HeaderBar,
|
// Update the req name property
|
||||||
|
this.$store.commit("updateRequest", { name: "Settings" });
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["user", "loading"]),
|
...mapState(["user", "loading","req"]),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,30 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<header-bar showMenu showLogo>
|
|
||||||
<title />
|
|
||||||
|
|
||||||
<action
|
|
||||||
v-if="selectedCount"
|
|
||||||
icon="file_download"
|
|
||||||
:label="$t('buttons.download')"
|
|
||||||
@action="download"
|
|
||||||
:counter="selectedCount"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
v-if="isSingleFile()"
|
|
||||||
class="action copy-clipboard"
|
|
||||||
:data-clipboard-text="linkSelected()"
|
|
||||||
:aria-label="$t('buttons.copyDownloadLinkToClipboard')"
|
|
||||||
:title="$t('buttons.copyDownloadLinkToClipboard')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">content_paste</i>
|
|
||||||
</button>
|
|
||||||
<action
|
|
||||||
icon="check_circle"
|
|
||||||
:label="$t('buttons.selectMultiple')"
|
|
||||||
@action="toggleMultipleSelection"
|
|
||||||
/>
|
|
||||||
</header-bar>
|
|
||||||
|
|
||||||
<breadcrumbs :base="'/share/' + hash" />
|
<breadcrumbs :base="'/share/' + hash" />
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<header-bar>
|
<header-bar>
|
||||||
<action
|
<action icon="close" :label="$t('buttons.close')" @action="close()" />
|
||||||
class="menu-button"
|
<title class="topTitle">{{ req.name }}</title>
|
||||||
icon="menu"
|
|
||||||
:label="$t('buttons.toggleSidebar')"
|
|
||||||
@action="toggleSidebar()"
|
|
||||||
/>
|
|
||||||
<search />
|
|
||||||
<action
|
|
||||||
class="menu-button"
|
|
||||||
icon="grid_view"
|
|
||||||
:label="$t('buttons.switchView')"
|
|
||||||
@action="switchView"
|
|
||||||
/>
|
|
||||||
</header-bar>
|
</header-bar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -29,6 +19,7 @@ import Vue from "vue";
|
||||||
import { mapState, mapGetters, mapMutations } from "vuex";
|
import { mapState, mapGetters, mapMutations } from "vuex";
|
||||||
import { users, files as api } from "@/api";
|
import { users, files as api } from "@/api";
|
||||||
import { enableExec } from "@/utils/constants";
|
import { enableExec } from "@/utils/constants";
|
||||||
|
import url from "@/utils/url";
|
||||||
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 * as upload from "@/utils/upload";
|
import * as upload from "@/utils/upload";
|
||||||
|
@ -58,6 +49,9 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["req", "selected", "user", "show", "multiple", "selected", "loading"]),
|
...mapState(["req", "selected", "user", "show", "multiple", "selected", "loading"]),
|
||||||
...mapGetters(["selectedCount"]),
|
...mapGetters(["selectedCount"]),
|
||||||
|
isSettings() {
|
||||||
|
return this.$route.path.includes("/settings/")
|
||||||
|
},
|
||||||
nameSorted() {
|
nameSorted() {
|
||||||
return this.req.sorting.by === "name";
|
return this.req.sorting.by === "name";
|
||||||
},
|
},
|
||||||
|
@ -587,7 +581,17 @@ export default {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
close() {
|
||||||
|
if (this.isSettings) { // Use this.isSettings to access the computed property
|
||||||
|
this.$router.push({ path: "/files/" }, () => { });
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$store.commit("updateRequest", {});
|
||||||
|
let uri = url.removeLastDir(this.$route.path) + "/";
|
||||||
|
console.log(url)
|
||||||
|
this.$router.push({ path: uri });
|
||||||
|
},
|
||||||
upload: function () {
|
upload: function () {
|
||||||
if (
|
if (
|
||||||
typeof window.DataTransferItem !== "undefined" &&
|
typeof window.DataTransferItem !== "undefined" &&
|
|
@ -0,0 +1,141 @@
|
||||||
|
<template>
|
||||||
|
<header-bar>
|
||||||
|
<action icon="close" :label="$t('buttons.close')" @action="close()" />
|
||||||
|
<title class="topTitle">{{ req.name }}</title>
|
||||||
|
|
||||||
|
<action v-if="user.perm.modify" id="save-button" icon="save" :label="$t('buttons.save')"
|
||||||
|
@action="save()" />
|
||||||
|
</header-bar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.flexbar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: block;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topTitle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from "vuex";
|
||||||
|
import { files as api } from "@/api";
|
||||||
|
import { theme } from "@/utils/constants";
|
||||||
|
import buttons from "@/utils/buttons";
|
||||||
|
import url from "@/utils/url";
|
||||||
|
|
||||||
|
import ace from "ace-builds/src-min-noconflict/ace.js";
|
||||||
|
import modelist from "ace-builds/src-min-noconflict/ext-modelist.js";
|
||||||
|
import "ace-builds/webpack-resolver";
|
||||||
|
|
||||||
|
import HeaderBar from "@/components/header/HeaderBar";
|
||||||
|
import Action from "@/components/header/Action";
|
||||||
|
import Breadcrumbs from "@/components/Breadcrumbs";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "editor",
|
||||||
|
components: {
|
||||||
|
HeaderBar,
|
||||||
|
Action,
|
||||||
|
Breadcrumbs,
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(["req", "user", "currentView"]),
|
||||||
|
breadcrumbs() {
|
||||||
|
let parts = this.$route.path.split("/");
|
||||||
|
|
||||||
|
if (parts[0] === "") {
|
||||||
|
parts.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts[parts.length - 1] === "") {
|
||||||
|
parts.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
let breadcrumbs = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < parts.length; i++) {
|
||||||
|
breadcrumbs.push({ name: decodeURIComponent(parts[i]) });
|
||||||
|
}
|
||||||
|
|
||||||
|
breadcrumbs.shift();
|
||||||
|
|
||||||
|
if (breadcrumbs.length > 3) {
|
||||||
|
while (breadcrumbs.length !== 4) {
|
||||||
|
breadcrumbs.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
breadcrumbs[0].name = "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
return breadcrumbs;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
window.addEventListener("keydown", this.keyEvent);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
window.removeEventListener("keydown", this.keyEvent);
|
||||||
|
this.editor.destroy();
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
const fileContent = this.req.content || "";
|
||||||
|
|
||||||
|
this.editor = ace.edit("editor", {
|
||||||
|
value: fileContent,
|
||||||
|
showPrintMargin: false,
|
||||||
|
readOnly: this.req.type === "textImmutable",
|
||||||
|
theme: "ace/theme/chrome",
|
||||||
|
mode: modelist.getModeForPath(this.req.name).mode,
|
||||||
|
wrap: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (theme == "dark") {
|
||||||
|
this.editor.setTheme("ace/theme/twilight");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
back() {
|
||||||
|
let uri = url.removeLastDir(this.$route.path) + "/";
|
||||||
|
this.$router.push({ path: uri });
|
||||||
|
},
|
||||||
|
keyEvent(event) {
|
||||||
|
if (!event.ctrlKey && !event.metaKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (String.fromCharCode(event.which).toLowerCase() !== "s") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
this.save();
|
||||||
|
},
|
||||||
|
async save() {
|
||||||
|
const button = "save";
|
||||||
|
buttons.loading("save");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.put(this.$route.path, this.editor.getValue());
|
||||||
|
buttons.success(button);
|
||||||
|
} catch (e) {
|
||||||
|
buttons.done(button);
|
||||||
|
this.$showError(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.$store.commit("updateRequest", {});
|
||||||
|
let uri = url.removeLastDir(this.$route.path) + "/";
|
||||||
|
this.$router.push({ path: uri });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,630 @@
|
||||||
|
<template>
|
||||||
|
<header-bar>
|
||||||
|
<action
|
||||||
|
class="menu-button"
|
||||||
|
icon="menu"
|
||||||
|
:label="$t('buttons.toggleSidebar')"
|
||||||
|
@action="toggleSidebar()"
|
||||||
|
/>
|
||||||
|
<search />
|
||||||
|
<action
|
||||||
|
class="menu-button"
|
||||||
|
icon="grid_view"
|
||||||
|
:label="$t('buttons.switchView')"
|
||||||
|
@action="switchView"
|
||||||
|
/>
|
||||||
|
</header-bar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.flexbar {
|
||||||
|
display:flex;
|
||||||
|
flex-direction:block;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Vue from "vue";
|
||||||
|
import { mapState, mapGetters, mapMutations } from "vuex";
|
||||||
|
import { users, files as api } from "@/api";
|
||||||
|
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||||
|
import Action from "@/components/header/Action.vue";
|
||||||
|
import url from "@/utils/url";
|
||||||
|
import * as upload from "@/utils/upload";
|
||||||
|
import css from "@/utils/css";
|
||||||
|
import throttle from "lodash.throttle";
|
||||||
|
import Search from "@/components/Search.vue";
|
||||||
|
|
||||||
|
import Item from "@/components/files/ListingItem.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "listing",
|
||||||
|
components: {
|
||||||
|
HeaderBar,
|
||||||
|
Action,
|
||||||
|
Search,
|
||||||
|
Item,
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
showLimit: 50,
|
||||||
|
columnWidth: 280,
|
||||||
|
dragCounter: 0,
|
||||||
|
width: window.innerWidth,
|
||||||
|
itemWeight: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(["req", "selected", "user", "show", "multiple", "selected", "loading"]),
|
||||||
|
...mapGetters(["selectedCount"]),
|
||||||
|
nameSorted() {
|
||||||
|
return this.req.sorting.by === "name";
|
||||||
|
},
|
||||||
|
sizeSorted() {
|
||||||
|
return this.req.sorting.by === "size";
|
||||||
|
},
|
||||||
|
modifiedSorted() {
|
||||||
|
return this.req.sorting.by === "modified";
|
||||||
|
},
|
||||||
|
ascOrdered() {
|
||||||
|
return this.req.sorting.asc;
|
||||||
|
},
|
||||||
|
items() {
|
||||||
|
const dirs = [];
|
||||||
|
const files = [];
|
||||||
|
|
||||||
|
this.req.items.forEach((item) => {
|
||||||
|
if (item.isDir) {
|
||||||
|
dirs.push(item);
|
||||||
|
} else {
|
||||||
|
files.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { dirs, files };
|
||||||
|
},
|
||||||
|
dirs() {
|
||||||
|
return this.items.dirs.slice(0, this.showLimit);
|
||||||
|
},
|
||||||
|
files() {
|
||||||
|
let showLimit = this.showLimit - this.items.dirs.length;
|
||||||
|
|
||||||
|
if (showLimit < 0) showLimit = 0;
|
||||||
|
|
||||||
|
return this.items.files.slice(0, showLimit);
|
||||||
|
},
|
||||||
|
nameIcon() {
|
||||||
|
if (this.nameSorted && !this.ascOrdered) {
|
||||||
|
return "arrow_upward";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "arrow_downward";
|
||||||
|
},
|
||||||
|
sizeIcon() {
|
||||||
|
if (this.sizeSorted && this.ascOrdered) {
|
||||||
|
return "arrow_downward";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "arrow_upward";
|
||||||
|
},
|
||||||
|
modifiedIcon() {
|
||||||
|
if (this.modifiedSorted && this.ascOrdered) {
|
||||||
|
return "arrow_downward";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "arrow_upward";
|
||||||
|
},
|
||||||
|
viewIcon() {
|
||||||
|
const icons = {
|
||||||
|
list: "view_module",
|
||||||
|
mosaic: "grid_view",
|
||||||
|
"mosaic gallery": "view_list",
|
||||||
|
};
|
||||||
|
return icons[this.user.viewMode];
|
||||||
|
},
|
||||||
|
headerButtons() {
|
||||||
|
return {
|
||||||
|
select: this.selectedCount > 0,
|
||||||
|
upload: this.user.perm.create && this.selectedCount > 0,
|
||||||
|
download: this.user.perm.download && this.selectedCount > 0,
|
||||||
|
delete: this.selectedCount > 0 && this.user.perm.delete,
|
||||||
|
rename: this.selectedCount === 1 && this.user.perm.rename,
|
||||||
|
share: this.selectedCount === 1 && this.user.perm.share,
|
||||||
|
move: this.selectedCount > 0 && this.user.perm.rename,
|
||||||
|
copy: this.selectedCount > 0 && this.user.perm.create,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
req: function () {
|
||||||
|
// Reset the show value
|
||||||
|
this.showLimit = 50;
|
||||||
|
|
||||||
|
// Ensures that the listing is displayed
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
// How much every listing item affects the window height
|
||||||
|
this.setItemWeight();
|
||||||
|
|
||||||
|
// Fill and fit the window with listing items
|
||||||
|
this.fillWindow(true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
// Check the columns size for the first time.
|
||||||
|
this.colunmsResize();
|
||||||
|
|
||||||
|
// How much every listing item affects the window height
|
||||||
|
this.setItemWeight();
|
||||||
|
|
||||||
|
// Fill and fit the window with listing items
|
||||||
|
this.fillWindow(true);
|
||||||
|
|
||||||
|
// Add the needed event listeners to the window and document.
|
||||||
|
window.addEventListener("keydown", this.keyEvent);
|
||||||
|
window.addEventListener("scroll", this.scrollEvent);
|
||||||
|
window.addEventListener("resize", this.windowsResize);
|
||||||
|
|
||||||
|
if (!this.user.perm.create) return;
|
||||||
|
document.addEventListener("dragover", this.preventDefault);
|
||||||
|
document.addEventListener("dragenter", this.dragEnter);
|
||||||
|
document.addEventListener("dragleave", this.dragLeave);
|
||||||
|
document.addEventListener("drop", this.drop);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
// Remove event listeners before destroying this page.
|
||||||
|
window.removeEventListener("keydown", this.keyEvent);
|
||||||
|
window.removeEventListener("scroll", this.scrollEvent);
|
||||||
|
window.removeEventListener("resize", this.windowsResize);
|
||||||
|
|
||||||
|
if (this.user && !this.user.perm.create) return;
|
||||||
|
document.removeEventListener("dragover", this.preventDefault);
|
||||||
|
document.removeEventListener("dragenter", this.dragEnter);
|
||||||
|
document.removeEventListener("dragleave", this.dragLeave);
|
||||||
|
document.removeEventListener("drop", this.drop);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
action: function () {
|
||||||
|
if (this.show) {
|
||||||
|
this.$store.commit("showHover", this.show);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit("action");
|
||||||
|
},
|
||||||
|
toggleSidebar() {
|
||||||
|
if (this.$store.state.show == "sidebar") {
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
} else {
|
||||||
|
this.$store.commit("showHover", "sidebar");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...mapMutations(["updateUser", "addSelected"]),
|
||||||
|
base64: function (name) {
|
||||||
|
return window.btoa(unescape(encodeURIComponent(name)));
|
||||||
|
},
|
||||||
|
keyEvent(event) {
|
||||||
|
// No prompts are shown
|
||||||
|
if (this.show !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Esc!
|
||||||
|
if (event.keyCode === 27) {
|
||||||
|
// Reset files selection.
|
||||||
|
this.$store.commit("resetSelected");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Del!
|
||||||
|
if (event.keyCode === 46) {
|
||||||
|
if (!this.user.perm.delete || this.selectedCount == 0) return;
|
||||||
|
|
||||||
|
// Show delete prompt.
|
||||||
|
this.$store.commit("showHover", "delete");
|
||||||
|
}
|
||||||
|
|
||||||
|
// F2!
|
||||||
|
if (event.keyCode === 113) {
|
||||||
|
if (!this.user.perm.rename || this.selectedCount !== 1) return;
|
||||||
|
|
||||||
|
// Show rename prompt.
|
||||||
|
this.$store.commit("showHover", "rename");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctrl is pressed
|
||||||
|
if (!event.ctrlKey && !event.metaKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = String.fromCharCode(event.which).toLowerCase();
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "f":
|
||||||
|
event.preventDefault();
|
||||||
|
this.$store.commit("showHover", "search");
|
||||||
|
break;
|
||||||
|
case "c":
|
||||||
|
case "x":
|
||||||
|
this.copyCut(event, key);
|
||||||
|
break;
|
||||||
|
case "v":
|
||||||
|
this.paste(event);
|
||||||
|
break;
|
||||||
|
case "a":
|
||||||
|
event.preventDefault();
|
||||||
|
for (let file of this.items.files) {
|
||||||
|
if (this.$store.state.selected.indexOf(file.index) === -1) {
|
||||||
|
this.addSelected(file.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let dir of this.items.dirs) {
|
||||||
|
if (this.$store.state.selected.indexOf(dir.index) === -1) {
|
||||||
|
this.addSelected(dir.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "s":
|
||||||
|
event.preventDefault();
|
||||||
|
document.getElementById("download-button").click();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
switchView: async function () {
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
const modes = {
|
||||||
|
list: "mosaic",
|
||||||
|
mosaic: "mosaic gallery",
|
||||||
|
"mosaic gallery": "list",
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
id: this.user.id,
|
||||||
|
viewMode: modes[this.user.viewMode] || "list",
|
||||||
|
};
|
||||||
|
//users.update(data, ["viewMode"]).catch(this.$showError);
|
||||||
|
this.$store.commit("updateUser", data);
|
||||||
|
|
||||||
|
//this.setItemWeight();
|
||||||
|
//this.fillWindow();
|
||||||
|
},
|
||||||
|
preventDefault(event) {
|
||||||
|
// Wrapper around prevent default.
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
copyCut(event, key) {
|
||||||
|
if (event.target.tagName.toLowerCase() === "input") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = [];
|
||||||
|
|
||||||
|
for (let i of this.selected) {
|
||||||
|
items.push({
|
||||||
|
from: this.req.items[i].url,
|
||||||
|
name: this.req.items[i].name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit("updateClipboard", {
|
||||||
|
key: key,
|
||||||
|
items: items,
|
||||||
|
path: this.$route.path,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
paste(event) {
|
||||||
|
if (event.target.tagName.toLowerCase() === "input") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = [];
|
||||||
|
|
||||||
|
for (let item of this.$store.state.clipboard.items) {
|
||||||
|
const from = item.from.endsWith("/") ? item.from.slice(0, -1) : item.from;
|
||||||
|
const to = this.$route.path + encodeURIComponent(item.name);
|
||||||
|
items.push({ from, to, name: item.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let action = (overwrite, rename) => {
|
||||||
|
api
|
||||||
|
.copy(items, overwrite, rename)
|
||||||
|
.then(() => {
|
||||||
|
this.$store.commit("setReload", true);
|
||||||
|
})
|
||||||
|
.catch(this.$showError);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.$store.state.clipboard.key === "x") {
|
||||||
|
action = (overwrite, rename) => {
|
||||||
|
api
|
||||||
|
.move(items, overwrite, rename)
|
||||||
|
.then(() => {
|
||||||
|
this.$store.commit("resetClipboard");
|
||||||
|
this.$store.commit("setReload", true);
|
||||||
|
})
|
||||||
|
.catch(this.$showError);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$store.state.clipboard.path == this.$route.path) {
|
||||||
|
action(false, true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let conflict = upload.checkConflict(items, this.req.items);
|
||||||
|
|
||||||
|
let overwrite = false;
|
||||||
|
let rename = false;
|
||||||
|
|
||||||
|
if (conflict) {
|
||||||
|
this.$store.commit("showHover", {
|
||||||
|
prompt: "replace-rename",
|
||||||
|
confirm: (event, option) => {
|
||||||
|
overwrite = option == "overwrite";
|
||||||
|
rename = option == "rename";
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
action(overwrite, rename);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
action(overwrite, rename);
|
||||||
|
},
|
||||||
|
colunmsResize() {
|
||||||
|
// Update the columns size based on the window width.
|
||||||
|
let columns = Math.floor(
|
||||||
|
document.querySelector("main").offsetWidth / this.columnWidth
|
||||||
|
);
|
||||||
|
let items = css(["#listing.mosaic .item", ".mosaic#listing .item"]);
|
||||||
|
if (columns === 0) columns = 1;
|
||||||
|
items.style.width = `calc(${100 / columns}% - 1em)`;
|
||||||
|
},
|
||||||
|
scrollEvent: throttle(function () {
|
||||||
|
const totalItems = this.req.numDirs + this.req.numFiles;
|
||||||
|
|
||||||
|
// All items are displayed
|
||||||
|
if (this.showLimit >= totalItems) return;
|
||||||
|
|
||||||
|
const currentPos = window.innerHeight + window.scrollY;
|
||||||
|
|
||||||
|
// Trigger at the 75% of the window height
|
||||||
|
const triggerPos = document.body.offsetHeight - window.innerHeight * 0.25;
|
||||||
|
|
||||||
|
if (currentPos > triggerPos) {
|
||||||
|
// Quantity of items needed to fill 2x of the window height
|
||||||
|
const showQuantity = Math.ceil((window.innerHeight * 2) / this.itemWeight);
|
||||||
|
|
||||||
|
// Increase the number of displayed items
|
||||||
|
this.showLimit += showQuantity;
|
||||||
|
}
|
||||||
|
}, 100),
|
||||||
|
dragEnter() {
|
||||||
|
this.dragCounter++;
|
||||||
|
|
||||||
|
// When the user starts dragging an item, put every
|
||||||
|
// file on the listing with 50% opacity.
|
||||||
|
let items = document.getElementsByClassName("item");
|
||||||
|
|
||||||
|
Array.from(items).forEach((file) => {
|
||||||
|
file.style.opacity = 0.5;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
dragLeave() {
|
||||||
|
this.dragCounter--;
|
||||||
|
|
||||||
|
if (this.dragCounter == 0) {
|
||||||
|
this.resetOpacity();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
drop: async function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.dragCounter = 0;
|
||||||
|
this.resetOpacity();
|
||||||
|
|
||||||
|
let dt = event.dataTransfer;
|
||||||
|
let el = event.target;
|
||||||
|
|
||||||
|
if (dt.files.length <= 0) return;
|
||||||
|
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
if (el !== null && !el.classList.contains("item")) {
|
||||||
|
el = el.parentElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let files = await upload.scanFiles(dt);
|
||||||
|
let items = this.req.items;
|
||||||
|
let path = this.$route.path.endsWith("/")
|
||||||
|
? this.$route.path
|
||||||
|
: this.$route.path + "/";
|
||||||
|
|
||||||
|
if (el !== null && el.classList.contains("item") && el.dataset.dir === "true") {
|
||||||
|
// Get url from ListingItem instance
|
||||||
|
path = el.__vue__.url;
|
||||||
|
|
||||||
|
try {
|
||||||
|
items = (await api.fetch(path)).items;
|
||||||
|
} catch (error) {
|
||||||
|
this.$showError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let conflict = upload.checkConflict(files, items);
|
||||||
|
|
||||||
|
if (conflict) {
|
||||||
|
this.$store.commit("showHover", {
|
||||||
|
prompt: "replace",
|
||||||
|
confirm: (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
upload.handleFiles(files, path, true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
upload.handleFiles(files, path);
|
||||||
|
},
|
||||||
|
uploadInput(event) {
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
|
||||||
|
let files = event.currentTarget.files;
|
||||||
|
let folder_upload =
|
||||||
|
files[0].webkitRelativePath !== undefined && files[0].webkitRelativePath !== "";
|
||||||
|
|
||||||
|
if (folder_upload) {
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
let file = files[i];
|
||||||
|
files[i].fullPath = file.webkitRelativePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = this.$route.path.endsWith("/")
|
||||||
|
? this.$route.path
|
||||||
|
: this.$route.path + "/";
|
||||||
|
let conflict = upload.checkConflict(files, this.req.items);
|
||||||
|
|
||||||
|
if (conflict) {
|
||||||
|
this.$store.commit("showHover", {
|
||||||
|
prompt: "replace",
|
||||||
|
confirm: (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
upload.handleFiles(files, path, true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
upload.handleFiles(files, path);
|
||||||
|
},
|
||||||
|
resetOpacity() {
|
||||||
|
let items = document.getElementsByClassName("item");
|
||||||
|
|
||||||
|
Array.from(items).forEach((file) => {
|
||||||
|
file.style.opacity = 1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async sort(by) {
|
||||||
|
let asc = false;
|
||||||
|
|
||||||
|
if (by === "name") {
|
||||||
|
if (this.nameIcon === "arrow_upward") {
|
||||||
|
asc = true;
|
||||||
|
}
|
||||||
|
} else if (by === "size") {
|
||||||
|
if (this.sizeIcon === "arrow_upward") {
|
||||||
|
asc = true;
|
||||||
|
}
|
||||||
|
} else if (by === "modified") {
|
||||||
|
if (this.modifiedIcon === "arrow_upward") {
|
||||||
|
asc = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await users.update({ id: this.user.id, sorting: { by, asc } }, ["sorting"]);
|
||||||
|
} catch (e) {
|
||||||
|
this.$showError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit("setReload", true);
|
||||||
|
},
|
||||||
|
openSearch() {
|
||||||
|
this.$store.commit("showHover", "search");
|
||||||
|
},
|
||||||
|
toggleMultipleSelection() {
|
||||||
|
this.$store.commit("multiple", !this.multiple);
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
},
|
||||||
|
windowsResize: throttle(function () {
|
||||||
|
this.colunmsResize();
|
||||||
|
this.width = window.innerWidth;
|
||||||
|
|
||||||
|
// Listing element is not displayed
|
||||||
|
if (this.$refs.listing == null) return;
|
||||||
|
|
||||||
|
// How much every listing item affects the window height
|
||||||
|
this.setItemWeight();
|
||||||
|
|
||||||
|
// Fill but not fit the window
|
||||||
|
this.fillWindow();
|
||||||
|
}, 100),
|
||||||
|
download() {
|
||||||
|
if (this.selectedCount === 1 && !this.req.items[this.selected[0]].isDir) {
|
||||||
|
api.download(null, this.req.items[this.selected[0]].url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit("showHover", {
|
||||||
|
prompt: "download",
|
||||||
|
confirm: (format) => {
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
let files = [];
|
||||||
|
if (this.selectedCount > 0) {
|
||||||
|
for (let i of this.selected) {
|
||||||
|
files.push(this.req.items[i].url);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
files.push(this.$route.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
api.download(format, ...files);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
upload: function () {
|
||||||
|
if (
|
||||||
|
typeof window.DataTransferItem !== "undefined" &&
|
||||||
|
typeof DataTransferItem.prototype.webkitGetAsEntry !== "undefined"
|
||||||
|
) {
|
||||||
|
this.$store.commit("showHover", "upload");
|
||||||
|
} else {
|
||||||
|
document.getElementById("upload-input").click();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setItemWeight() {
|
||||||
|
// Listing element is not displayed
|
||||||
|
if (this.$refs.listing == null) return;
|
||||||
|
|
||||||
|
let itemQuantity = this.req.numDirs + this.req.numFiles;
|
||||||
|
if (itemQuantity > this.showLimit) itemQuantity = this.showLimit;
|
||||||
|
|
||||||
|
// How much every listing item affects the window height
|
||||||
|
this.itemWeight = this.$refs.listing.offsetHeight / itemQuantity;
|
||||||
|
},
|
||||||
|
fillWindow(fit = false) {
|
||||||
|
const totalItems = this.req.numDirs + this.req.numFiles;
|
||||||
|
|
||||||
|
// More items are displayed than the total
|
||||||
|
if (this.showLimit >= totalItems && !fit) return;
|
||||||
|
|
||||||
|
const windowHeight = window.innerHeight;
|
||||||
|
|
||||||
|
// Quantity of items needed to fill 2x of the window height
|
||||||
|
const showQuantity = Math.ceil((windowHeight + windowHeight * 2) / this.itemWeight);
|
||||||
|
|
||||||
|
// Less items to display than current
|
||||||
|
if (this.showLimit > showQuantity && !fit) return;
|
||||||
|
|
||||||
|
// Set the number of displayed items
|
||||||
|
this.showLimit = showQuantity > totalItems ? totalItems : showQuantity;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -1,20 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="editor-container">
|
<div id="editor-container">
|
||||||
<header-bar>
|
|
||||||
<action icon="close" :label="$t('buttons.close')" @action="close()" />
|
|
||||||
<title>{{ req.name }}</title>
|
|
||||||
|
|
||||||
<action
|
|
||||||
v-if="user.perm.modify"
|
|
||||||
id="save-button"
|
|
||||||
icon="save"
|
|
||||||
:label="$t('buttons.save')"
|
|
||||||
@action="save()"
|
|
||||||
/>
|
|
||||||
</header-bar>
|
|
||||||
|
|
||||||
<breadcrumbs base="/files" noLink />
|
|
||||||
|
|
||||||
<form id="editor"></form>
|
<form id="editor"></form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -45,7 +30,7 @@ export default {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["req", "user"]),
|
...mapState(["req", "user","currentView"]),
|
||||||
breadcrumbs() {
|
breadcrumbs() {
|
||||||
let parts = this.$route.path.split("/");
|
let parts = this.$route.path.split("/");
|
||||||
|
|
||||||
|
|
|
@ -4,48 +4,6 @@
|
||||||
@mousemove="toggleNavigation"
|
@mousemove="toggleNavigation"
|
||||||
@touchstart="toggleNavigation"
|
@touchstart="toggleNavigation"
|
||||||
>
|
>
|
||||||
<header-bar>
|
|
||||||
<action icon="close" :label="$t('buttons.close')" @action="close()" />
|
|
||||||
<title>{{ name }}</title>
|
|
||||||
<action
|
|
||||||
:disabled="loading"
|
|
||||||
v-if="isResizeEnabled && req.type === 'image'"
|
|
||||||
:icon="fullSize ? 'photo_size_select_large' : 'hd'"
|
|
||||||
@action="toggleSize"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<template #actions>
|
|
||||||
<action
|
|
||||||
:disabled="loading"
|
|
||||||
v-if="user.perm.rename"
|
|
||||||
icon="mode_edit"
|
|
||||||
:label="$t('buttons.rename')"
|
|
||||||
show="rename"
|
|
||||||
/>
|
|
||||||
<action
|
|
||||||
:disabled="loading"
|
|
||||||
v-if="user.perm.delete"
|
|
||||||
icon="delete"
|
|
||||||
:label="$t('buttons.delete')"
|
|
||||||
@action="deleteFile"
|
|
||||||
id="delete-button"
|
|
||||||
/>
|
|
||||||
<action
|
|
||||||
:disabled="loading"
|
|
||||||
v-if="user.perm.download"
|
|
||||||
icon="file_download"
|
|
||||||
:label="$t('buttons.download')"
|
|
||||||
@action="download"
|
|
||||||
/>
|
|
||||||
<action
|
|
||||||
:disabled="loading"
|
|
||||||
icon="info"
|
|
||||||
:label="$t('buttons.info')"
|
|
||||||
show="info"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</header-bar>
|
|
||||||
|
|
||||||
<div class="loading delayed" v-if="loading">
|
<div class="loading delayed" v-if="loading">
|
||||||
<div class="spinner">
|
<div class="spinner">
|
||||||
<div class="bounce1"></div>
|
<div class="bounce1"></div>
|
||||||
|
|