added prettier

This commit is contained in:
Čarodej
2022-02-01 12:21:38 +01:00
parent 5ae875233b
commit b38b532cbe
284 changed files with 25410 additions and 25338 deletions

5
.prettierrc.yaml Normal file
View File

@@ -0,0 +1,5 @@
trailingComma: "es5"
tabWidth: 4
semi: false
singleQuote: true
printWidth: 120

222
package-lock.json generated
View File

@@ -1968,6 +1968,15 @@
"integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==",
"dev": true
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"requires": {
"sprintf-js": "~1.0.2"
}
},
"array-find-index": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
@@ -2449,6 +2458,12 @@
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
"dev": true
},
"builtin-modules": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
"integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==",
"dev": true
},
"builtin-status-codes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
@@ -2471,6 +2486,32 @@
"get-intrinsic": "^1.0.2"
}
},
"caller-callsite": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
"integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
"dev": true,
"requires": {
"callsites": "^2.0.0"
},
"dependencies": {
"callsites": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
"integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
"dev": true
}
}
},
"caller-path": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
"integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
"dev": true,
"requires": {
"caller-callsite": "^2.0.0"
}
},
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -3431,6 +3472,12 @@
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
"dev": true
},
"detect-newline": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
"integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
"dev": true
},
"detect-node": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
@@ -3710,6 +3757,12 @@
"estraverse": "^4.1.1"
}
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true
},
"esrecurse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
@@ -4049,6 +4102,18 @@
"pkg-dir": "^4.1.0"
}
},
"find-line-column": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/find-line-column/-/find-line-column-0.5.2.tgz",
"integrity": "sha1-2wAjj/hoVRoYLnShA0FtKVqYyMo=",
"dev": true
},
"find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
"dev": true
},
"find-up": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
@@ -4713,6 +4778,107 @@
"resolve-cwd": "^3.0.0"
}
},
"import-sort": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/import-sort/-/import-sort-6.0.0.tgz",
"integrity": "sha512-XUwSQMGAGmcW/wfshFE0gXgb1NPF6ibbQD6wDr3KRDykZf/lZj0jf58Bwa02xNb8EE59oz7etFe9OHnJocUW5Q==",
"dev": true,
"requires": {
"detect-newline": "^2.1.0",
"import-sort-parser": "^6.0.0",
"import-sort-style": "^6.0.0",
"is-builtin-module": "^3.0.0",
"resolve": "^1.8.1"
}
},
"import-sort-config": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/import-sort-config/-/import-sort-config-6.0.0.tgz",
"integrity": "sha512-FJpF2F3+30JXqH1rJKeajxoSCHCueai3/0ntDN4y3GJL5pjnLDt/VjCy5FzjH7u0NHnllL/zVEf1wfmsVxJlPQ==",
"dev": true,
"requires": {
"cosmiconfig": "^5.0.5",
"find-root": "^1.0.0",
"minimatch": "^3.0.4",
"resolve-from": "^4.0.0"
},
"dependencies": {
"cosmiconfig": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
"integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
"dev": true,
"requires": {
"import-fresh": "^2.0.0",
"is-directory": "^0.3.1",
"js-yaml": "^3.13.1",
"parse-json": "^4.0.0"
}
},
"import-fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
"integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
"dev": true,
"requires": {
"caller-path": "^2.0.0",
"resolve-from": "^3.0.0"
},
"dependencies": {
"resolve-from": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
"dev": true
}
}
},
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
}
}
},
"import-sort-parser": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/import-sort-parser/-/import-sort-parser-6.0.0.tgz",
"integrity": "sha512-H5L+d6HnqHvThB0GmAA3/43Sv74oCwL0iMk3/ixOv0LRJ69rCyHXeG/+UadMHrD2FefEmgPIWboEPAG7gsQrkA==",
"dev": true
},
"import-sort-parser-babylon": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/import-sort-parser-babylon/-/import-sort-parser-babylon-6.0.0.tgz",
"integrity": "sha512-NyShTiNhTh4Vy7kJUVe6CuvOaQAzzfSIT72wtp3CzGjz8bHjNj59DCAjncuviicmDOgVAgmLuSh1WMcLYAMWGg==",
"dev": true,
"requires": {
"@babel/core": "^7.2.2",
"@babel/parser": "^7.0.0-beta.54",
"@babel/traverse": "^7.0.0-beta.54",
"@babel/types": "^7.0.0-beta.54",
"find-line-column": "^0.5.2"
}
},
"import-sort-parser-typescript": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/import-sort-parser-typescript/-/import-sort-parser-typescript-6.0.0.tgz",
"integrity": "sha512-pgxnr3I156DonupQriNsgDb2zJN9TxrqCCIN1rwT/6SDO1rkJb+a0fjqshCjlgacTSA92oPAp1eAwmQUeZi3dw==",
"dev": true,
"requires": {
"typescript": "^3.2.4"
}
},
"import-sort-style": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/import-sort-style/-/import-sort-style-6.0.0.tgz",
"integrity": "sha512-z0H5PKs7YoDeKxNYXv2AA1mjjZFY07fjeNCXUdTM3ymJtWeeEoTm8CQkFm2l+KPZoMczIvdwzJpWkkOamBnsPw==",
"dev": true
},
"in-publish": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz",
@@ -4788,6 +4954,15 @@
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-builtin-module": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.1.0.tgz",
"integrity": "sha512-OV7JjAgOTfAFJmHZLvpSTb4qi0nIILDV1gWPYDnDJUTNFM5aGlRAhk4QcT8i7TuAleeEV5Fdkqn3t4mS+Q11fg==",
"dev": true,
"requires": {
"builtin-modules": "^3.0.0"
}
},
"is-core-module": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
@@ -4805,6 +4980,12 @@
"has-tostringtag": "^1.0.0"
}
},
"is-directory": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
"dev": true
},
"is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
@@ -4967,6 +5148,16 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
@@ -6812,8 +7003,25 @@
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
"integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
"dev": true
},
"prettier-plugin-import-sort": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/prettier-plugin-import-sort/-/prettier-plugin-import-sort-0.0.7.tgz",
"integrity": "sha512-O0KlUSq+lwvh+UiN3wZDT6wWkf7TNxTVv2/XXE5KqpRNbFJq3nRg2ftzBYFFO8QGpdWIrOB0uCTCtFjIxmVKQw==",
"dev": true,
"optional": true
"requires": {
"import-sort": "^6.0.0",
"import-sort-config": "^6.0.0",
"import-sort-parser-babylon": "^6.0.0",
"import-sort-parser-typescript": "^6.0.0"
}
},
"prettier-plugin-tailwindcss": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.1.4.tgz",
"integrity": "sha512-kt3YFWqxcG9+bilBI0hPF7RjQZNtgBRvjJZGw6B2MNAjPqlfcYIiZnNaIEnq4XimKLTzZYxz6jQnUXmSQ/5njg==",
"dev": true
},
"pretty-time": {
"version": "1.1.0",
@@ -7744,6 +7952,12 @@
}
}
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
"dev": true
},
"sshpk": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
@@ -8219,6 +8433,12 @@
"mime-types": "~2.1.24"
}
},
"typescript": {
"version": "3.9.10",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
"integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
"dev": true
},
"unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",

View File

@@ -15,6 +15,9 @@
"cross-env": "^5.1",
"laravel-mix": "^6.0.41",
"postcss": "^8.4.5",
"prettier": "^2.5.1",
"prettier-plugin-import-sort": "0.0.7",
"prettier-plugin-tailwindcss": "^0.1.4",
"resolve-url-loader": "^2.3.1",
"sass": "^1.49.0",
"sass-loader": "^8.0.2",

View File

@@ -1,6 +1,5 @@
<template>
<div>
<!--UI components-->
<Alert />
<ToasterWrapper />
@@ -21,12 +20,12 @@
</template>
<script>
import ToasterWrapper from "./components/Others/Notifications/ToasterWrapper";
import CookieDisclaimer from "./components/Others/CookieDisclaimer";
import Spinner from "./components/FilesView/Spinner";
import Vignette from "./components/Others/Vignette";
import Alert from "./components/FilesView/Alert";
import RestrictionWarningBar from "./RestrictionWarningBar"
import ToasterWrapper from './components/Others/Notifications/ToasterWrapper'
import CookieDisclaimer from './components/Others/CookieDisclaimer'
import Spinner from './components/FilesView/Spinner'
import Vignette from './components/Others/Vignette'
import Alert from './components/FilesView/Alert'
import RestrictionWarningBar from './RestrictionWarningBar'
import { mapGetters } from 'vuex'
import { events } from './bus'
@@ -38,79 +37,64 @@ export default {
ToasterWrapper,
Vignette,
Spinner,
Alert
Alert,
},
data() {
return {
isLoaded: false
isLoaded: false,
}
},
computed: {
...mapGetters([
'config',
'user',
]),
...mapGetters(['config', 'user']),
},
watch: {
'config.defaultThemeMode': function () {
this.handleDarkMode()
}
},
},
methods: {
spotlightListener(e) {
if (e.key === 'k' && e.metaKey) {
events.$emit('spotlight:show');
events.$emit('spotlight:show')
}
},
handleDarkMode() {
const app = document.getElementsByTagName("html")[0];
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
const app = document.getElementsByTagName('html')[0]
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)')
if (this.config.defaultThemeMode === 'dark') {
app.classList.add("dark")
app.classList.add('dark')
this.$store.commit('UPDATE_DARK_MODE_STATUS', true)
} else if (this.config.defaultThemeMode === 'light') {
app.classList.remove("dark")
app.classList.remove('dark')
this.$store.commit('UPDATE_DARK_MODE_STATUS', false)
} else if (this.config.defaultThemeMode === 'system' && prefersDarkScheme.matches) {
app.classList.add("dark")
app.classList.add('dark')
this.$store.commit('UPDATE_DARK_MODE_STATUS', true)
} else if (this.config.defaultThemeMode === 'system' && !prefersDarkScheme.matches) {
app.classList.remove("dark")
app.classList.remove('dark')
this.$store.commit('UPDATE_DARK_MODE_STATUS', false)
}
}
},
},
beforeMount() {
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', () => {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
this.handleDarkMode()
});
})
// Get installation state
let installation = this.$root.$data.config.installation
if (['setup-disclaimer', 'setup-database'].includes(installation))
this.isLoaded = true
if (['setup-disclaimer', 'setup-database'].includes(installation)) this.isLoaded = true
// Redirect to database verify code
if (installation === 'setup-database')
this.$router.push({name: 'StatusCheck'})
if (installation === 'setup-database') this.$router.push({ name: 'StatusCheck' })
// Redirect to starting installation process
if (installation === 'setup-disclaimer')
this.$router.push({name: 'InstallationDisclaimer'})
if (installation === 'setup-disclaimer') this.$router.push({ name: 'InstallationDisclaimer' })
if (installation === 'setup-done')
this.$store.dispatch('getLanguageTranslations', this.$root.$data.config.locale)
.then(() => {
this.$store.dispatch('getLanguageTranslations', this.$root.$data.config.locale).then(() => {
this.isLoaded = true
// Store config to vuex
@@ -119,8 +103,8 @@ export default {
rootDirectory: {
name: this.$t('locations.home'),
location: 'base',
id: undefined
}
id: undefined,
},
})
})
},
@@ -129,11 +113,11 @@ export default {
document.body.classList.add('windows')
}
window.addEventListener("keydown", this.spotlightListener);
window.addEventListener('keydown', this.spotlightListener)
},
destroyed() {
window.removeEventListener("keydown", this.spotlightListener);
}
window.removeEventListener('keydown', this.spotlightListener)
},
}
</script>
@@ -148,7 +132,7 @@ input:-webkit-autofill {
[v-cloak],
[v-cloak] > * {
display: none
display: none;
}
* {
@@ -166,7 +150,13 @@ input:-webkit-autofill {
}
.vue-feather {
path, circle, line, rect, polyline, ellipse, polygon {
path,
circle,
line,
rect,
polyline,
ellipse,
polygon {
color: inherit;
}
}
@@ -178,17 +168,17 @@ input:-webkit-autofill {
// Dark mode support
.dark {
* {
color: $dark_mode_text_primary;
}
body, html {
body,
html {
background: $dark_mode_background;
color: $dark_mode_text_primary;
img {
opacity: .95;
opacity: 0.95;
}
}
}

View File

@@ -1,6 +1,6 @@
<template>
<div v-if="$store.getters.isLimitedUser" class="bg-red-500 text-center py-1">
<router-link :to="{name: 'Billing'}" class="text-white font-bold text-xs">
<div v-if="$store.getters.isLimitedUser" class="bg-red-500 py-1 text-center">
<router-link :to="{ name: 'Billing' }" class="text-xs font-bold text-white">
{{ $t('Your functionality is restricted. Please review your billing settings.') }}
</router-link>
</div>

View File

@@ -1,4 +1,4 @@
window._ = require('lodash');
window._ = require('lodash')
/**
* We'll load the axios HTTP library which allows us to easily issue requests
@@ -6,9 +6,9 @@ window._ = require('lodash');
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = require('axios');
window.axios = require('axios')
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
/**
* Echo exposes an expressive API for subscribing to channels and listening

View File

@@ -1,14 +1,11 @@
<template>
<div :class="{'mb-7': !isLast}" class="sm:flex justify-between sm:space-x-8 sm:space-x-2 sm:space-y-0 space-y-4 w-full">
<div :class="{ 'mb-7': !isLast }" class="w-full justify-between space-y-4 sm:flex sm:space-x-8 sm:space-x-2 sm:space-y-0">
<!--Label for input-->
<div class="leading-5">
<label class="text-sm font-bold dark:text-gray-200 text-gray-700 mb-1.5 block">
{{ title }}:
</label>
<label class="mb-1.5 block text-sm font-bold text-gray-700 dark:text-gray-200"> {{ title }}: </label>
<!--Input Description-->
<span v-if="description" class="text-xs text-gray-500 leading-4 block" v-html="description"></span>
<span v-if="description" class="block text-xs leading-4 text-gray-500" v-html="description"></span>
<!--Input Description-->
<span v-if="error" class="error-message">
@@ -26,11 +23,6 @@
<script>
export default {
name: 'AppInputButton',
props: [
'description',
'isLast',
'title',
'error',
]
props: ['description', 'isLast', 'title', 'error'],
}
</script>

View File

@@ -1,14 +1,11 @@
<template>
<div :class="{'mb-7': !isLast}" class="flex items-center justify-between sm:space-x-8 space-x-2 w-full">
<div :class="{ 'mb-7': !isLast }" class="flex w-full items-center justify-between space-x-2 sm:space-x-8">
<!--Label for input-->
<div class="leading-5">
<label class="text-sm font-bold dark:text-gray-200 text-gray-700 mb-1.5 block">
{{ title }}:
</label>
<label class="mb-1.5 block text-sm font-bold text-gray-700 dark:text-gray-200"> {{ title }}: </label>
<!--Input Description-->
<span v-if="description" class="text-xs text-gray-500 leading-4 block" v-html="description"></span>
<span v-if="description" class="block text-xs leading-4 text-gray-500" v-html="description"></span>
<!--Input Description-->
<span v-if="error" class="error-message">
@@ -26,11 +23,6 @@
<script>
export default {
name: 'AppInputSwitch',
props: [
'description',
'isLast',
'title',
'error',
]
props: ['description', 'isLast', 'title', 'error'],
}
</script>

View File

@@ -1,32 +1,24 @@
<template>
<div :class="{ 'mb-7': !isLast }">
<!--Label for input-->
<label v-if="title" class="text-sm font-bold dark:text-gray-200 text-gray-700 mb-1.5 block">
{{ title }}:
</label>
<label v-if="title" class="mb-1.5 block text-sm font-bold text-gray-700 dark:text-gray-200"> {{ title }}: </label>
<!--Form element-->
<slot></slot>
<!--Input Description-->
<span v-if="error" class="text-red-800 pt-2 text-xs">
<span v-if="error" class="pt-2 text-xs text-red-800">
{{ error }}
</span>
<!--Input Description-->
<small v-if="description" class="text-xs text-gray-500 pt-2 leading-4 block" v-html="description"></small>
<small v-if="description" class="block pt-2 text-xs leading-4 text-gray-500" v-html="description"></small>
</div>
</template>
<script>
export default {
name: 'AppInputText',
props: [
'description',
'isLast',
'title',
'error',
]
props: ['description', 'isLast', 'title', 'error'],
}
</script>

View File

@@ -1,10 +1,17 @@
<template>
<div id="card-navigation" style="height: 62px" class="mb-7">
<div :class="{'fixed top-0 left-0 right-0 px-6 rounded-none backdrop-filter backdrop-blur-lg dark:bg-dark-foreground bg-white bg-opacity-50 z-10': fixedNav}">
<div class="whitespace-nowrap overflow-x-auto">
<div
:class="{
'fixed top-0 left-0 right-0 z-10 rounded-none bg-white bg-opacity-50 px-6 backdrop-blur-lg backdrop-filter dark:bg-dark-foreground': fixedNav,
}"
>
<div class="overflow-x-auto whitespace-nowrap">
<router-link
class="inline-block text-sm font-bold px-4 py-5 border-b-2 border-transparent border-bottom-theme"
:class="{'text-theme': routeName === page.route, 'dark:text-gray-100 text-gray-600': routeName !== page.route}"
class="border-bottom-theme inline-block border-b-2 border-transparent px-4 py-5 text-sm font-bold"
:class="{
'text-theme': routeName === page.route,
'text-gray-600 dark:text-gray-100': routeName !== page.route,
}"
v-for="(page, i) in pages"
:to="{ name: page.route }"
:key="i"
@@ -20,13 +27,11 @@
<script>
export default {
name: 'CardNavigation',
props: [
'pages'
],
props: ['pages'],
computed: {
routeName() {
return this.$route.name
}
},
},
data() {
return {
@@ -35,11 +40,11 @@ export default {
},
created() {
// Handle fixed mobile navigation
window.addEventListener("scroll", () => {
window.addEventListener('scroll', () => {
let card = document.getElementById('card-navigation')
this.fixedNav = card.getBoundingClientRect().top < 0;
});
}
this.fixedNav = card.getBoundingClientRect().top < 0
})
},
}
</script>

View File

@@ -10,15 +10,11 @@
<script>
export default {
name: 'DotLabel',
props: [
'color',
'title',
],
props: ['color', 'title'],
}
</script>
<style lang="scss" scoped>
.label {
display: flex;
align-items: center;
@@ -32,7 +28,7 @@
flex: none;
&.success {
background: #0ABB87;
background: #0abb87;
}
&.danger {

View File

@@ -1,59 +1,69 @@
<template>
<div>
<div class="flex items-center mb-4 rounded dark:bg-2x-dark-foreground bg-light-300 h-2.5">
<div v-for="(chart, i) in data" :key="i" :style="{width: (chart.progress > 1 ? chart.progress : 0) + '%'}" class="chart-wrapper">
<div class="mb-4 flex h-2.5 items-center rounded bg-light-300 dark:bg-2x-dark-foreground">
<div
v-for="(chart, i) in data"
:key="i"
:style="{
width: (chart.progress > 1 ? chart.progress : 0) + '%',
}"
class="chart-wrapper"
>
<!--<DotLabel class="label" :class="{'offset-top': chart.progress < 5}" :color="chart.color" :title="chart.value" />-->
<!--Only singe line-->
<span
v-if="data.length === 1"
:class="[{
'border-r-2 dark:border-gray-800 border-white rounded-tl-lg rounded-bl-lg': chart.progress < 100,
'border-none rounded-lg': chart.progress >= 100
}, chart.color]"
class="chart-progress w-full h-2.5 block"
:class="[
{
'rounded-tl-lg rounded-bl-lg border-r-2 border-white dark:border-gray-800': chart.progress < 100,
'rounded-lg border-none': chart.progress >= 100,
},
chart.color,
]"
class="chart-progress block h-2.5 w-full"
>
</span>
<!--Multiple line-->
<span
v-if="data.length > 1 && chart.progress > 0"
:class="[{
'rounded-tl-lg rounded-bl-lg border-r-2 dark:border-gray-800 border-white': i === 0,
'border-r-2 dark:border-gray-800 border-white': i < (data.length - 1),
'rounded-tr-lg rounded-br-lg': i === (data.length - 1),
}, chart.color]"
class="chart-progress w-full h-2.5 block"
:class="[
{
'rounded-tl-lg rounded-bl-lg border-r-2 border-white dark:border-gray-800': i === 0,
'border-r-2 border-white dark:border-gray-800': i < data.length - 1,
'rounded-tr-lg rounded-br-lg': i === data.length - 1,
},
chart.color,
]"
class="chart-progress block h-2.5 w-full"
></span>
</div>
</div>
<footer class="flex items-center w-full overflow-x-auto">
<footer class="flex w-full items-center overflow-x-auto">
<DotLabel v-for="(chart, i) in data" :key="i" :color="chart.color" :title="chart.title" class="mr-5" />
</footer>
</div>
</template>
<script>
import DotLabel from "./DotLabel";
import DotLabel from './DotLabel'
export default {
name: 'ProgressLine',
props: [
'data',
],
props: ['data'],
components: {
DotLabel,
}
},
}
</script>
<style lang="scss" scoped>
.chart-progress {
&.success {
background: #0ABB87;
box-shadow: 0 3px 10px rgba(#0ABB87, 0.5);
background: #0abb87;
box-shadow: 0 3px 10px rgba(#0abb87, 0.5);
}
&.danger {
@@ -84,8 +94,8 @@
.dark .chart-progress {
&.secondary {
background: #282A2F !important;
box-shadow: 0 3px 10px rgba(#282A2F, 0.5) !important;
background: #282a2f !important;
box-shadow: 0 3px 10px rgba(#282a2f, 0.5) !important;
}
}
</style>

View File

@@ -1,59 +1,68 @@
<template>
<DatatableWrapper @init="isLoading = false" api="/api/admin/dashboard/newbies" :paginator="false" :columns="columns" class="overflow-x-auto mt-6">
<DatatableWrapper @init="isLoading = false" api="/api/admin/dashboard/newbies" :paginator="false" :columns="columns" class="mt-6 overflow-x-auto">
<template slot-scope="{ row }">
<!--Not a subscription-->
<tr v-if="config.subscriptionType === 'none'" class="border-b dark:border-opacity-5 border-light border-dashed whitespace-nowrap">
<td class="py-3 md:pr-1 pr-3">
<router-link :to="{name: 'UserDetail', params: {id: row.data.id}}">
<tr v-if="config.subscriptionType === 'none'" class="whitespace-nowrap border-b border-dashed border-light dark:border-opacity-5">
<td class="py-3 pr-3 md:pr-1">
<router-link
:to="{
name: 'UserDetail',
params: { id: row.data.id },
}"
>
<div class="flex items-center">
<MemberAvatar
:is-border="false"
:size="44"
:member="row.data.relationships.settings"
/>
<MemberAvatar :is-border="false" :size="44" :member="row.data.relationships.settings" />
<div class="ml-3 pr-10">
<b class="text-sm font-bold block max-w-1 overflow-hidden text-ellipsis whitespace-nowrap" style="max-width: 155px;">
<b class="max-w-1 block overflow-hidden text-ellipsis whitespace-nowrap text-sm font-bold" style="max-width: 155px">
{{ row.data.relationships.settings.data.attributes.name }}
</b>
<span class="block text-xs dark:text-gray-500 text-gray-600">
<span class="block text-xs text-gray-600 dark:text-gray-500">
{{ row.data.attributes.email }}
</span>
</div>
</div>
</router-link>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<ColorLabel :color="$getUserRoleColor(row.data.attributes.role)">
{{ row.data.attributes.role }}
</ColorLabel>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<span v-if="row.data.attributes.storage.capacity !== 0" class="text-sm font-bold">
{{ row.data.attributes.storage.used_formatted }}
</span>
<span v-if="row.data.attributes.storage.capacity === 0" class="text-sm font-bold">
-
</span>
<span v-if="row.data.attributes.storage.capacity === 0" class="text-sm font-bold"> - </span>
</td>
<td class="md:px-1 px-3" v-if="config.storageLimit">
<td class="px-3 md:px-1" v-if="config.storageLimit">
<span v-if="row.data.attributes.storage.capacity !== 0" class="text-sm font-bold">
{{ row.data.attributes.storage.capacity_formatted }}
</span>
<span v-if="row.data.attributes.storage.capacity === 0" class="text-sm font-bold">
-
</span>
<span v-if="row.data.attributes.storage.capacity === 0" class="text-sm font-bold"> - </span>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<span class="text-sm font-bold">
{{ row.data.attributes.created_at }}
</span>
</td>
<td class="md:pl-1 pl-3 text-right">
<div class="flex space-x-2 w-full justify-end">
<router-link class="flex items-center justify-center w-8 h-8 rounded-md hover:bg-green-100 dark:bg-2x-dark-foreground bg-light-background transition-colors" :to="{name: 'UserDetail', params: {id: row.data.id}}">
<td class="pl-3 text-right md:pl-1">
<div class="flex w-full justify-end space-x-2">
<router-link
class="flex h-8 w-8 items-center justify-center rounded-md bg-light-background transition-colors hover:bg-green-100 dark:bg-2x-dark-foreground"
:to="{
name: 'UserDetail',
params: { id: row.data.id },
}"
>
<Edit2Icon size="15" class="opacity-75" />
</router-link>
<router-link class="flex items-center justify-center w-8 h-8 rounded-md hover:bg-red-100 dark:bg-2x-dark-foreground bg-light-background transition-colors" :to="{name: 'UserDelete', params: {id: row.data.id}}">
<router-link
class="flex h-8 w-8 items-center justify-center rounded-md bg-light-background transition-colors hover:bg-red-100 dark:bg-2x-dark-foreground"
:to="{
name: 'UserDelete',
params: { id: row.data.id },
}"
>
<Trash2Icon size="15" class="opacity-75" />
</router-link>
</div>
@@ -61,63 +70,72 @@
</tr>
<!--Fixed subscription-->
<tr v-if="config.subscriptionType === 'fixed'" class="border-b dark:border-opacity-5 border-light border-dashed whitespace-nowrap">
<td class="py-3 md:pr-1 pr-3">
<router-link :to="{name: 'UserDetail', params: {id: row.data.id}}">
<tr v-if="config.subscriptionType === 'fixed'" class="whitespace-nowrap border-b border-dashed border-light dark:border-opacity-5">
<td class="py-3 pr-3 md:pr-1">
<router-link
:to="{
name: 'UserDetail',
params: { id: row.data.id },
}"
>
<div class="flex items-center">
<MemberAvatar
:is-border="false"
:size="44"
:member="row.data.relationships.settings"
/>
<MemberAvatar :is-border="false" :size="44" :member="row.data.relationships.settings" />
<div class="ml-3 pr-10">
<b class="text-sm font-bold block max-w-1 overflow-hidden text-ellipsis whitespace-nowrap" style="max-width: 155px;">
<b class="max-w-1 block overflow-hidden text-ellipsis whitespace-nowrap text-sm font-bold" style="max-width: 155px">
{{ row.data.relationships.settings.data.attributes.name }}
</b>
<span class="block text-xs dark:text-gray-500 text-gray-600">
<span class="block text-xs text-gray-600 dark:text-gray-500">
{{ row.data.attributes.email }}
</span>
</div>
</div>
</router-link>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<ColorLabel :color="$getUserRoleColor(row.data.attributes.role)">
{{ row.data.attributes.role }}
</ColorLabel>
</td>
<td class="md:px-1 px-3" v-if="config.isSaaS">
<td class="px-3 md:px-1" v-if="config.isSaaS">
<span class="text-sm font-bold">
{{ row.data.relationships.subscription ? $t('global.premium') : $t('global.free') }}
</span>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<span v-if="row.data.attributes.storage.capacity !== 0" class="text-sm font-bold">
{{ row.data.attributes.storage.used_formatted }}
</span>
<span v-if="row.data.attributes.storage.capacity === 0" class="text-sm font-bold">
-
</span>
<span v-if="row.data.attributes.storage.capacity === 0" class="text-sm font-bold"> - </span>
</td>
<td class="md:px-1 px-3" v-if="config.storageLimit">
<td class="px-3 md:px-1" v-if="config.storageLimit">
<span v-if="row.data.attributes.storage.capacity !== 0" class="text-sm font-bold">
{{ row.data.attributes.storage.capacity_formatted }}
</span>
<span v-if="row.data.attributes.storage.capacity === 0" class="text-sm font-bold">
-
</span>
<span v-if="row.data.attributes.storage.capacity === 0" class="text-sm font-bold"> - </span>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<span class="text-sm font-bold">
{{ row.data.attributes.created_at }}
</span>
</td>
<td class="md:pl-1 pl-3 text-right">
<div class="flex space-x-2 w-full justify-end">
<router-link class="flex items-center justify-center w-8 h-8 rounded-md hover:bg-green-100 dark:bg-2x-dark-foreground bg-light-background transition-colors" :to="{name: 'UserDetail', params: {id: row.data.id}}">
<td class="pl-3 text-right md:pl-1">
<div class="flex w-full justify-end space-x-2">
<router-link
class="flex h-8 w-8 items-center justify-center rounded-md bg-light-background transition-colors hover:bg-green-100 dark:bg-2x-dark-foreground"
:to="{
name: 'UserDetail',
params: { id: row.data.id },
}"
>
<Edit2Icon size="15" class="opacity-75" />
</router-link>
<router-link class="flex items-center justify-center w-8 h-8 rounded-md hover:bg-red-100 dark:bg-2x-dark-foreground bg-light-background transition-colors" :to="{name: 'UserDelete', params: {id: row.data.id}}">
<router-link
class="flex h-8 w-8 items-center justify-center rounded-md bg-light-background transition-colors hover:bg-red-100 dark:bg-2x-dark-foreground"
:to="{
name: 'UserDelete',
params: { id: row.data.id },
}"
>
<Trash2Icon size="15" class="opacity-75" />
</router-link>
</div>
@@ -125,52 +143,65 @@
</tr>
<!--Metered subscription-->
<tr v-if="config.subscriptionType === 'metered'" class="border-b dark:border-opacity-5 border-light border-dashed whitespace-nowrap">
<td class="py-3 md:pr-1 pr-3">
<router-link :to="{name: 'UserDetail', params: {id: row.data.id}}">
<tr v-if="config.subscriptionType === 'metered'" class="whitespace-nowrap border-b border-dashed border-light dark:border-opacity-5">
<td class="py-3 pr-3 md:pr-1">
<router-link
:to="{
name: 'UserDetail',
params: { id: row.data.id },
}"
>
<div class="flex items-center">
<MemberAvatar
:is-border="false"
:size="44"
:member="row.data.relationships.settings"
/>
<MemberAvatar :is-border="false" :size="44" :member="row.data.relationships.settings" />
<div class="ml-3 pr-10">
<b class="text-sm font-bold block max-w-1 overflow-hidden text-ellipsis whitespace-nowrap" style="max-width: 155px;">
<b class="max-w-1 block overflow-hidden text-ellipsis whitespace-nowrap text-sm font-bold" style="max-width: 155px">
{{ row.data.relationships.settings.data.attributes.name }}
</b>
<span class="block text-xs dark:text-gray-500 text-gray-600">
<span class="block text-xs text-gray-600 dark:text-gray-500">
{{ row.data.attributes.email }}
</span>
</div>
</div>
</router-link>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<ColorLabel :color="$getUserRoleColor(row.data.attributes.role)">
{{ row.data.attributes.role }}
</ColorLabel>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<span class="text-sm font-bold">
{{ row.data.meta.usages.featureEstimates.storage.usage }}
</span>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<span class="text-sm font-bold">
{{ row.data.meta.usages.costEstimate }}
</span>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<span class="text-sm font-bold">
{{ row.data.attributes.created_at }}
</span>
</td>
<td class="md:pl-1 pl-3 text-right">
<div class="flex space-x-2 w-full justify-end">
<router-link class="flex items-center justify-center w-8 h-8 rounded-md hover:bg-green-100 dark:bg-2x-dark-foreground bg-light-background transition-colors" :to="{name: 'UserDetail', params: {id: row.data.id}}">
<td class="pl-3 text-right md:pl-1">
<div class="flex w-full justify-end space-x-2">
<router-link
class="flex h-8 w-8 items-center justify-center rounded-md bg-light-background transition-colors hover:bg-green-100 dark:bg-2x-dark-foreground"
:to="{
name: 'UserDetail',
params: { id: row.data.id },
}"
>
<Edit2Icon size="15" class="opacity-75" />
</router-link>
<router-link class="flex items-center justify-center w-8 h-8 rounded-md hover:bg-red-100 dark:bg-2x-dark-foreground bg-light-background transition-colors" :to="{name: 'UserDelete', params: {id: row.data.id}}">
<router-link
class="flex h-8 w-8 items-center justify-center rounded-md bg-light-background transition-colors hover:bg-red-100 dark:bg-2x-dark-foreground"
:to="{
name: 'UserDelete',
params: { id: row.data.id },
}"
>
<Trash2Icon size="15" class="opacity-75" />
</router-link>
</div>
@@ -181,19 +212,16 @@
</template>
<script>
import DatatableCellImage from "../Others/Tables/DatatableCellImage";
import DatatableWrapper from "../Others/Tables/DatatableWrapper";
import ColorLabel from "../Others/ColorLabel";
import {Trash2Icon, Edit2Icon} from "vue-feather-icons"
import MemberAvatar from "../FilesView/MemberAvatar";
import DatatableCellImage from '../Others/Tables/DatatableCellImage'
import DatatableWrapper from '../Others/Tables/DatatableWrapper'
import ColorLabel from '../Others/ColorLabel'
import { Trash2Icon, Edit2Icon } from 'vue-feather-icons'
import MemberAvatar from '../FilesView/MemberAvatar'
import { mapGetters } from 'vuex'
export default {
name: 'WidgetLatestRegistrations',
props: [
'icon',
'title'
],
props: ['icon', 'title'],
components: {
DatatableCellImage,
DatatableWrapper,
@@ -203,25 +231,23 @@
Edit2Icon,
},
computed: {
...mapGetters([
'config',
]),
...mapGetters(['config']),
columns() {
return {
metered: [
{
label: this.$t('admin_page_user.table.name'),
field: 'email',
sortable: true
sortable: true,
},
{
label: this.$t('admin_page_user.table.role'),
field: 'role',
sortable: true
sortable: true,
},
{
label: this.$t('admin_page_user.table.storage_used'),
sortable: false
sortable: false,
},
{
label: this.$t('Billing Est.'),
@@ -230,23 +256,23 @@
{
label: this.$t('admin_page_user.table.created_at'),
field: 'created_at',
sortable: true
sortable: true,
},
{
label: this.$t('admin_page_user.table.action'),
sortable: false
sortable: false,
},
],
fixed: [
{
label: this.$t('admin_page_user.table.name'),
field: 'email',
sortable: true
sortable: true,
},
{
label: this.$t('admin_page_user.table.role'),
field: 'role',
sortable: true
sortable: true,
},
{
label: this.$t('admin_page_user.table.plan'),
@@ -254,7 +280,7 @@
},
{
label: this.$t('admin_page_user.table.storage_used'),
sortable: false
sortable: false,
},
{
label: this.$t('Max Storage'),
@@ -264,27 +290,27 @@
{
label: this.$t('admin_page_user.table.created_at'),
field: 'created_at',
sortable: true
sortable: true,
},
{
label: this.$t('admin_page_user.table.action'),
sortable: false
sortable: false,
},
],
none: [
{
label: this.$t('admin_page_user.table.name'),
field: 'email',
sortable: true
sortable: true,
},
{
label: this.$t('admin_page_user.table.role'),
field: 'role',
sortable: true
sortable: true,
},
{
label: this.$t('admin_page_user.table.storage_used'),
sortable: false
sortable: false,
},
{
label: this.$t('Max Storage'),
@@ -294,15 +320,15 @@
{
label: this.$t('admin_page_user.table.created_at'),
field: 'created_at',
sortable: true
sortable: true,
},
{
label: this.$t('admin_page_user.table.action'),
sortable: false
sortable: false,
},
],
}[this.config.subscriptionType]
}
},
},
data() {
return {

View File

@@ -1,37 +1,29 @@
<template>
<DatatableWrapper
api="/api/admin/dashboard/transactions"
:columns="columns"
class="overflow-x-auto"
>
<DatatableWrapper api="/api/admin/dashboard/transactions" :columns="columns" class="overflow-x-auto">
<template slot-scope="{ row }">
<tr class="border-b dark:border-opacity-5 border-light border-dashed whitespace-nowrap">
<td class="py-5 md:pr-1 pr-3">
<tr class="whitespace-nowrap border-b border-dashed border-light dark:border-opacity-5">
<td class="py-5 pr-3 md:pr-1">
<span class="text-sm font-bold">
{{ row.data.attributes.note }}
</span>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<div v-if="row.data.relationships.user" class="flex items-center">
<MemberAvatar
:is-border="false"
:size="36"
:member="row.data.relationships.user"
/>
<MemberAvatar :is-border="false" :size="36" :member="row.data.relationships.user" />
<div class="ml-3 pr-10">
<b class="text-sm font-bold block max-w-1 overflow-hidden text-ellipsis whitespace-nowrap" style="max-width: 155px;">
<b class="max-w-1 block overflow-hidden text-ellipsis whitespace-nowrap text-sm font-bold" style="max-width: 155px">
{{ row.data.relationships.user.data.attributes.name }}
</b>
<span class="block text-xs dark:text-gray-500 text-gray-600">
<span class="block text-xs text-gray-600 dark:text-gray-500">
{{ row.data.relationships.user.data.attributes.email }}
</span>
</div>
</div>
<span v-if="! row.data.relationships.user" class="text-xs text-gray-500 font-bold">
<span v-if="!row.data.relationships.user" class="text-xs font-bold text-gray-500">
{{ $t('User was deleted') }}
</span>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<ColorLabel v-if="config.subscriptionType === 'fixed'" :color="$getTransactionStatusColor(row.data.attributes.status)">
{{ row.data.attributes.status }}
</ColorLabel>
@@ -39,19 +31,19 @@
{{ row.data.attributes.type }}
</ColorLabel>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<span class="text-sm font-bold" :class="$getTransactionTypeTextColor(row.data.attributes.type)">
{{ $getTransactionMark(row.data.attributes.type) + row.data.attributes.price }}
</span>
</td>
<td class="md:px-1 px-3">
<td class="px-3 md:px-1">
<span class="text-sm font-bold">
{{ row.data.attributes.created_at }}
</span>
</td>
<td class="md:pl-1 pl-3">
<div class="text-right md:w-full w-32">
<img class="w-32 md:inline-block" :src="$getPaymentLogo(row.data.attributes.driver)" :alt="row.data.attributes.driver">
<td class="pl-3 md:pl-1">
<div class="w-32 text-right md:w-full">
<img class="w-32 md:inline-block" :src="$getPaymentLogo(row.data.attributes.driver)" :alt="row.data.attributes.driver" />
</div>
</td>
</tr>
@@ -67,20 +59,17 @@
</template>
<script>
import DatatableCellImage from "../Others/Tables/DatatableCellImage";
import DatatableWrapper from "../Others/Tables/DatatableWrapper";
import ColorLabel from "../Others/ColorLabel";
import {Trash2Icon, Edit2Icon} from "vue-feather-icons"
import MemberAvatar from "../FilesView/MemberAvatar"
import InfoBox from "../Others/Forms/InfoBox"
import DatatableCellImage from '../Others/Tables/DatatableCellImage'
import DatatableWrapper from '../Others/Tables/DatatableWrapper'
import ColorLabel from '../Others/ColorLabel'
import { Trash2Icon, Edit2Icon } from 'vue-feather-icons'
import MemberAvatar from '../FilesView/MemberAvatar'
import InfoBox from '../Others/Forms/InfoBox'
import { mapGetters } from 'vuex'
export default {
name: 'WidgetLatestTransactions',
props: [
'icon',
'title'
],
props: ['icon', 'title'],
components: {
DatatableCellImage,
DatatableWrapper,
@@ -91,9 +80,7 @@
InfoBox,
},
computed: {
...mapGetters([
'config',
]),
...mapGetters(['config']),
},
data() {
return {
@@ -101,32 +88,32 @@
{
label: this.$t('Note'),
field: 'note',
sortable: true
sortable: true,
},
{
label: this.$t('User'),
field: 'user_id',
sortable: true
sortable: true,
},
{
label: this.$t('Status'),
field: 'status',
sortable: true
sortable: true,
},
{
label: this.$t('admin_page_invoices.table.total'),
field: 'amount',
sortable: true
sortable: true,
},
{
label: this.$t('Payed At'),
field: 'created_at',
sortable: true
sortable: true,
},
{
label: this.$t('Service'),
field: 'driver',
sortable: true
sortable: true,
},
],
}

View File

@@ -1,11 +1,11 @@
<template>
<div class="widget-card w-full">
<div class="widget-content dark:bg-dark-foreground bg-white">
<div class="widget-content bg-white dark:bg-dark-foreground">
<div class="flex items-center">
<users-icon v-if="icon === 'users'" size="16" class="vue-feather text-theme mr-3" />
<star-icon v-if="icon === 'star'" size="16" class="vue-feather text-theme mr-3" />
<hard-drive-icon v-if="icon === 'hard-drive'" size="16" class="vue-feather text-theme mr-3" />
<b class="font-bold text-base">
<b class="text-base font-bold">
{{ title }}
</b>
</div>
@@ -24,7 +24,7 @@
ChevronRightIcon,
HardDriveIcon,
StarIcon,
UsersIcon
}
UsersIcon,
},
}
</script>

View File

@@ -1,9 +1,9 @@
<template>
<button class="group flex items-center border-2 dark:border-gray-300 border-black rounded-lg inline-block mx-auto px-7 py-2.5 whitespace-nowrap">
<span class="font-extrabold text-lg pr-1">
<button class="group mx-auto inline-block flex items-center whitespace-nowrap rounded-lg border-2 border-black px-7 py-2.5 dark:border-gray-300">
<span class="pr-1 text-lg font-extrabold">
{{ text }}
</span>
<refresh-cw-icon v-if="loading" size="20" class="vue-feather text-theme -mr-1 sync-alt"/>
<refresh-cw-icon v-if="loading" size="20" class="vue-feather text-theme sync-alt -mr-1" />
<chevron-right-icon v-if="!loading && icon" size="20" class="vue-feather text-theme -mr-1" />
</button>
</template>
@@ -13,11 +13,7 @@
export default {
name: 'AuthContent',
props: [
'loading',
'icon',
'text'
],
props: ['loading', 'icon', 'text'],
components: {
ChevronRightIcon,
RefreshCwIcon,
@@ -29,12 +25,11 @@
},
created() {
this.isVisible = this.visible
}
},
}
</script>
<style scoped lang="scss">
.sync-alt {
animation: spin 1s linear infinite;
}
@@ -47,5 +42,4 @@
transform: rotate(360deg);
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div v-if="isVisible" class="text-center w-full max-w-xl">
<div v-if="isVisible" class="w-full max-w-xl text-center">
<slot></slot>
</div>
</template>
@@ -7,10 +7,7 @@
<script>
export default {
name: 'AuthContent',
props: [
'visible',
'name'
],
props: ['visible', 'name'],
data() {
return {
isVisible: false,
@@ -18,6 +15,6 @@
},
created() {
this.isVisible = this.visible
}
},
}
</script>

View File

@@ -1,5 +1,5 @@
<template>
<div class="flex justify-center items-center md:px-0 px-5">
<div class="flex items-center justify-center px-5 md:px-0">
<slot></slot>
</div>
</template>

View File

@@ -1,5 +1,5 @@
<template>
<div v-if="config.allowedFacebookLogin || config.allowedGoogleLogin || config.allowedGithubLogin" class="flex items-center justify-center mb-10">
<div v-if="config.allowedFacebookLogin || config.allowedGoogleLogin || config.allowedGithubLogin" class="mb-10 flex items-center justify-center">
<div v-if="config.allowedFacebookLogin" class="mx-5 cursor-pointer">
<facebook-icon @click="socialiteRedirect('facebook')" />
</div>
@@ -9,14 +9,14 @@
</div>
<div v-if="config.allowedGoogleLogin" class="mx-5 cursor-pointer">
<span @click="socialiteRedirect('google')" class="font-semibold text-3xl">G</span>
<span @click="socialiteRedirect('google')" class="text-3xl font-semibold">G</span>
</div>
</div>
</template>
<script>
import { FacebookIcon, GithubIcon } from 'vue-feather-icons'
import {mapGetters} from "vuex";
import { mapGetters } from 'vuex'
export default {
name: 'SocialiteAuthenticationButtons',
@@ -25,9 +25,7 @@ export default {
GithubIcon,
},
computed: {
...mapGetters([
'config'
])
...mapGetters(['config']),
},
methods: {
socialiteRedirect(provider) {
@@ -35,6 +33,6 @@ export default {
this.$store.dispatch('socialiteRedirect', provider)
},
}
},
}
</script>

View File

@@ -3,29 +3,23 @@
<!--Overlay component-->
<div
@click.capture="hidePopover"
class="absolute top-12 z-20 w-60 dark:bg-dark-foreground bg-white shadow-xl rounded-lg overflow-hidden"
class="absolute top-12 z-20 w-60 overflow-hidden rounded-lg bg-white shadow-xl dark:bg-dark-foreground"
:class="{ 'right-0': side === 'left', 'left-0': side === 'right' }"
>
<slot></slot>
</div>
<!--Clickable layer to close overlays-->
<div
@click="hidePopover"
class="fixed top-0 left-0 right-0 bottom-0 z-10 cursor-pointer"
></div>
<div @click="hidePopover" class="fixed top-0 left-0 right-0 bottom-0 z-10 cursor-pointer"></div>
</div>
</template>
<script>
import {events} from "../../bus";
import { events } from '../../bus'
export default {
name: 'PopoverItem',
props: [
'side',
'name',
],
props: ['side', 'name'],
data() {
return {
isVisible: false,
@@ -33,15 +27,15 @@
},
methods: {
hidePopover() {
setTimeout(() => this.isVisible = false, 10)
}
setTimeout(() => (this.isVisible = false), 10)
},
},
mounted() {
events.$on('popover:open', name => {
events.$on('popover:open', (name) => {
if (this.name === name) {
this.isVisible = !this.isVisible
}
})
}
},
}
</script>

View File

@@ -11,7 +11,6 @@
</script>
<style scoped lang="scss">
.popover-wrapper {
position: relative;
}

View File

@@ -11,7 +11,6 @@
</script>
<style scoped lang="scss">
.toolbar-button-wrapper {
margin-left: 28px;
display: flex;
@@ -19,10 +18,8 @@
}
@media only screen and (max-width: 1024px) {
.toolbar-button-wrapper {
margin-left: 25px;
}
}
</style>

View File

@@ -11,11 +11,11 @@
</script>
<style scoped lang="scss">
@import "resources/sass/vuefilemanager/_variables";
@import "resources/sass/vuefilemanager/_mixins";
@import 'resources/sass/vuefilemanager/_variables';
@import 'resources/sass/vuefilemanager/_mixins';
.toolbar-tools {
text-align: right;
display: flex;;
display: flex;
}
</style>

View File

@@ -1,22 +1,14 @@
<template>
<div
v-if="isFullPreview"
class="file-preview z-40"
ref="filePreview"
tabindex="-1"
@keydown.esc="closeFilePreview"
@keydown.right="next"
@keydown.left="prev"
>
<div v-if="isFullPreview" class="file-preview z-40" ref="filePreview" tabindex="-1" @keydown.esc="closeFilePreview" @keydown.right="next" @keydown.left="prev">
<FilePreviewToolbar />
<FilePreviewMedia />
</div>
</template>
<script>
import FilePreviewToolbar from "./FilePreviewToolbar";
import FilePreviewMedia from "./FilePreviewMedia";
import {events} from "../../bus";
import FilePreviewToolbar from './FilePreviewToolbar'
import FilePreviewMedia from './FilePreviewMedia'
import { events } from '../../bus'
export default {
name: 'FilePreview',
@@ -26,7 +18,7 @@
},
data() {
return {
isFullPreview: false
isFullPreview: false,
}
},
methods: {
@@ -39,7 +31,7 @@
},
prev() {
events.$emit('file-preview:prev')
}
},
},
updated() {
if (this.isFullPreview) {
@@ -47,9 +39,9 @@
}
},
mounted() {
events.$on('file-preview:show', () => this.isFullPreview = true)
events.$on('file-preview:show', () => (this.isFullPreview = true))
events.$on('file-preview:hide', () => this.closeFilePreview())
}
},
}
</script>

View File

@@ -1,6 +1,5 @@
<template>
<div v-if="currentFile" class="file-preview-wrapper">
<!--Arrow navigation-->
<div v-if="files.length > 1" class="navigation-arrows">
<div @click.prevent="prev" class="prev">
@@ -14,7 +13,6 @@
<!--File preview-->
<div class="file-wrapper-preview">
<!--Show PDF-->
<PdfFile v-if="isPDF" :file="currentFile" />
@@ -30,14 +28,14 @@
<script>
import { ChevronLeftIcon, ChevronRightIcon } from 'vue-feather-icons'
import ToolbarButton from "../FilesView/ToolbarButton";
import ImageFile from "./Media/ImageFile";
import PdfFile from "./Media/PdfFile";
import Audio from "./Media/Audio";
import Video from "./Media/Video";
import Spinner from "../FilesView/Spinner";
import ToolbarButton from '../FilesView/ToolbarButton'
import ImageFile from './Media/ImageFile'
import PdfFile from './Media/PdfFile'
import Audio from './Media/Audio'
import Video from './Media/Video'
import Spinner from '../FilesView/Spinner'
import { mapGetters } from 'vuex'
import {events} from "../../bus";
import { events } from '../../bus'
export default {
name: 'FilePreviewMedia',
@@ -52,15 +50,9 @@ export default {
Video,
},
computed: {
...mapGetters([
'fastPreview',
'clipboard',
'entries',
]),
...mapGetters(['fastPreview', 'clipboard', 'entries']),
currentFile() {
return this.fastPreview
? this.fastPreview
: this.files[Math.abs(this.currentIndex) % this.files.length]
return this.fastPreview ? this.fastPreview : this.files[Math.abs(this.currentIndex) % this.files.length]
},
isPDF() {
return this.currentFile.data.attributes.mimetype === 'pdf'
@@ -73,7 +65,7 @@ export default {
},
isImage() {
return this.currentFile.data.type === 'image'
}
},
},
data() {
return {
@@ -83,8 +75,7 @@ export default {
},
watch: {
files() {
if (this.files.length === 0)
events.$emit('file-preview-wrapper:hide')
if (this.files.length === 0) events.$emit('file-preview-wrapper:hide')
},
currentFile() {
if (this.clipboard[0]) {
@@ -111,17 +102,11 @@ export default {
getFilesForView() {
let requestedFile = this.clipboard[0]
this.entries.map(element => {
this.entries.map((element) => {
if (requestedFile.data.attributes.mimetype === 'pdf') {
if (element.data.attributes.mimetype === 'pdf')
this.files.push(element)
if (element.data.attributes.mimetype === 'pdf') this.files.push(element)
} else {
if (element.data.type === requestedFile.data.type)
this.files.push(element)
if (element.data.type === requestedFile.data.type) this.files.push(element)
}
})
@@ -148,14 +133,14 @@ export default {
if (this.currentIndex < 0) {
this.currentIndex = this.files.length - 1
}
}
},
},
created() {
events.$on('file-preview:next', () => this.next())
events.$on('file-preview:prev', () => this.prev())
this.getFilesForView()
}
},
}
</script>
@@ -164,8 +149,8 @@ export default {
@import '../../../sass/vuefilemanager/mixins';
.navigation-arrows {
.prev, .next {
.prev,
.next {
cursor: pointer;
position: absolute;
top: 45%;
@@ -234,17 +219,15 @@ export default {
}
@media only screen and (max-width: 960px) {
.file-preview-wrapper {
top: 53px;
}
}
.dark {
.navigation-arrows {
.prev, .next {
.prev,
.next {
color: $light-text;
filter: drop-shadow(0px 1px 0 rgba(17, 19, 20, 1));
}

View File

@@ -1,9 +1,8 @@
<template>
<div class="navigation-panel" v-if="currentFile">
<div class="name-wrapper">
<!--Close icon-->
<span @click="closeFullPreview" class="p-3 -m-3">
<span @click="closeFullPreview" class="-m-3 p-3">
<x-icon size="17" class="icon-close hover-text-theme" />
</span>
@@ -15,10 +14,9 @@
<!--Context menu handler-->
<PopoverWrapper>
<!--Icon-->
<span @click.stop="showItemContextMenu" class="p-3 -m-3">
<div class="py-0.5 px-1.5 align-middle inline-block rounded-md lg:bg-transparent dark:bg-dark-foreground bg-light-background transition-all duration-200">
<span @click.stop="showItemContextMenu" class="-m-3 p-3">
<div class="inline-block rounded-md bg-light-background py-0.5 px-1.5 align-middle transition-all duration-200 dark:bg-dark-foreground lg:bg-transparent">
<more-horizontal-icon size="14" />
</div>
</span>
@@ -40,7 +38,10 @@
<!--Item metadata-->
<div class="created-at-wrapper">
<p>{{ currentFile.data.attributes.filesize }}, {{ currentFile.data.attributes.created_at }}</p>
<p>
{{ currentFile.data.attributes.filesize }},
{{ currentFile.data.attributes.created_at }}
</p>
</div>
<!--Icon actions-->
@@ -51,7 +52,14 @@
</div>
<div class="navigation-tool-wrapper">
<ToolbarButton @click.native="downloadItem" class="mobile-hide" source="download" :action="$t('actions.download')" />
<ToolbarButton v-if="canShareItem" @click.native="$shareFileOrFolder(currentFile)" class="mobile-hide" :class="{ 'is-inactive': !canShareItem }" source="share" :action="$t('actions.share')" />
<ToolbarButton
v-if="canShareItem"
@click.native="$shareFileOrFolder(currentFile)"
class="mobile-hide"
:class="{ 'is-inactive': !canShareItem }"
source="share"
:action="$t('actions.share')"
/>
<ToolbarButton v-if="isImage" @click.native="printMethod()" source="print" :action="$t('actions.print')" />
</div>
</div>
@@ -59,15 +67,15 @@
</template>
<script>
import PopoverWrapper from "../Desktop/PopoverWrapper";
import PopoverItem from "../Desktop/PopoverItem";
import OptionGroup from "../FilesView/OptionGroup";
import Option from "../FilesView/Option";
import PopoverWrapper from '../Desktop/PopoverWrapper'
import PopoverItem from '../Desktop/PopoverItem'
import OptionGroup from '../FilesView/OptionGroup'
import Option from '../FilesView/Option'
import ToolbarButton from "../FilesView/ToolbarButton";
import ToolbarButton from '../FilesView/ToolbarButton'
import { XIcon, MoreHorizontalIcon } from 'vue-feather-icons'
import { mapGetters } from 'vuex'
import {events} from "../../bus";
import { events } from '../../bus'
export default {
name: 'FilePreviewToolbar',
@@ -81,18 +89,12 @@
XIcon,
},
computed: {
...mapGetters([
'fastPreview',
'clipboard',
'entries'
]),
...mapGetters(['fastPreview', 'clipboard', 'entries']),
currentFile() {
return this.fastPreview ? this.fastPreview : this.clipboard[0]
},
sharingTitle() {
return this.currentFile.data.relationships.shared
? this.$t('context_menu.share_edit')
: this.$t('context_menu.share')
return this.currentFile.data.relationships.shared ? this.$t('context_menu.share_edit') : this.$t('context_menu.share')
},
isImage() {
return this.currentFile.data.type === 'image'
@@ -103,17 +105,11 @@
files() {
let files = []
this.entries.map(element => {
this.entries.map((element) => {
if (this.currentFile.data.attributes.mimetype === 'pdf') {
if (element.data.attributes.mimetype === 'pdf')
files.push(element)
if (element.data.attributes.mimetype === 'pdf') files.push(element)
} else {
if (element.data.type === this.currentFile.data.type)
files.push(element)
if (element.data.type === this.currentFile.data.type) files.push(element)
}
})
@@ -158,15 +154,12 @@
win.print()
},
downloadItem() {
this.$downloadFile(
this.currentFile.data.attributes.file_url,
this.currentFile.data.attributes.name + '.' + this.currentFile.data.attributes.mimetype
)
this.$downloadFile(this.currentFile.data.attributes.file_url, this.currentFile.data.attributes.name + '.' + this.currentFile.data.attributes.mimetype)
},
closeFullPreview() {
events.$emit('file-preview:hide')
}
}
},
},
}
</script>
@@ -300,9 +293,7 @@
}
@media (max-width: 960px) {
.context-menu {
.name-wrapper {
width: 67%;
}

View File

@@ -1,17 +1,10 @@
<template>
<audio
:class="{'file-shadow': ! $isMobile() }"
class="file audio"
:src="file.data.attributes.file_url"
controls>
</audio>
<audio :class="{ 'file-shadow': !$isMobile() }" class="file audio" :src="file.data.attributes.file_url" controls></audio>
</template>
<script>
export default {
name: 'Audio',
props: [
'file'
],
props: ['file'],
}
</script>

View File

@@ -1,27 +1,18 @@
<template>
<img
id="printable-file"
class="file"
:class="{'file-shadow': ! $isMobile() }"
:src="imageSource"
/>
<img id="printable-file" class="file" :class="{ 'file-shadow': !$isMobile() }" :src="imageSource" />
</template>
<script>
export default {
name: 'ImageFile',
props: [
'file'
],
props: ['file'],
computed: {
imageSource() {
let windowWidth = window.innerWidth
if (windowWidth > 1280)
return this.file.data.attributes.thumbnail.xl
else
return this.file.data.attributes.thumbnail.lg
}
}
if (windowWidth > 1280) return this.file.data.attributes.thumbnail.xl
else return this.file.data.attributes.thumbnail.lg
},
},
}
</script>

View File

@@ -11,7 +11,7 @@
<script>
//todo: resolve pdf
import {events} from "../../../bus";
import { events } from '../../../bus'
//import pdf from 'pdfvuer'
export default {
@@ -19,13 +19,11 @@
components: {
//pdf,
},
props: [
'file'
],
props: ['file'],
watch: {
file() {
this.getPdf()
}
},
},
data() {
return {
@@ -39,7 +37,7 @@
this.pdfData = undefined
this.numPages = 0
let self = this;
let self = this
//self.pdfData = pdf.createLoadingTask(this.file.data.attributes.file_url);
@@ -51,9 +49,7 @@
}
if (window.innerWidth > 960) {
this.documentSize = localStorage.getItem('documentSize')
? parseInt(localStorage.getItem('documentSize'))
: 50;
this.documentSize = localStorage.getItem('documentSize') ? parseInt(localStorage.getItem('documentSize')) : 50
}
},
zoomIn() {
@@ -67,7 +63,7 @@
this.documentSize -= 10
localStorage.setItem('documentSize', this.documentSize)
}
}
},
},
created() {
this.getDocumentSize()
@@ -75,7 +71,7 @@
events.$on('document-zoom:in', () => this.zoomIn())
events.$on('document-zoom:out', () => this.zoomOut())
}
},
}
</script>

View File

@@ -16,9 +16,7 @@
<script>
export default {
name: 'Video',
props: [
'file'
],
props: ['file'],
}
</script>

View File

@@ -10,12 +10,7 @@
<p v-if="message" class="message">{{ message }}</p>
</div>
<div class="popup-actions">
<ButtonBase
@click.native="closePopup"
:button-style="buttonStyle"
class="action-confirm"
>{{ button }}
</ButtonBase>
<ButtonBase @click.native="closePopup" :button-style="buttonStyle" class="action-confirm">{{ button }} </ButtonBase>
</div>
</div>
</div>
@@ -23,13 +18,13 @@
</template>
<script>
import ButtonBase from "./ButtonBase";
import {events} from "../../bus";
import ButtonBase from './ButtonBase'
import { events } from '../../bus'
export default {
name: 'AlertPopup',
components: {
ButtonBase
ButtonBase,
},
data() {
return {
@@ -44,11 +39,11 @@
methods: {
closePopup() {
events.$emit('popup:close')
}
},
},
mounted() {
// Show alert
events.$on('alert:open', args => {
events.$on('alert:open', (args) => {
this.isVisibleWrapper = true
this.title = args.title
@@ -72,7 +67,7 @@
})
// Show alert
events.$on('success:open', args => {
events.$on('success:open', (args) => {
this.isVisibleWrapper = true
this.title = args.title
@@ -91,7 +86,7 @@
events.$on('popup:close', () => {
this.isVisibleWrapper = false
})
}
},
}
</script>
@@ -136,7 +131,6 @@
}
.popup-content {
.title {
@include font-size(22);
text-transform: uppercase;

View File

@@ -17,7 +17,7 @@
props: ['buttonStyle', 'loading'],
components: {
RefreshCwIcon,
}
},
}
</script>
@@ -48,20 +48,20 @@
}
&.theme-solid {
.content {
color: white;
}
}
&.danger {
background: rgba($danger, .1);
background: rgba($danger, 0.1);
.content {
color: $danger;
}
polyline, path {
polyline,
path {
stroke: $danger;
}
}
@@ -73,7 +73,8 @@
color: white;
}
polyline, path {
polyline,
path {
stroke: white;
}
}
@@ -85,7 +86,8 @@
color: $text;
}
polyline, path {
polyline,
path {
stroke: $text;
}
}
@@ -105,9 +107,7 @@
}
.dark {
.button-base {
&.secondary {
background: $dark_mode_foreground;
@@ -115,7 +115,8 @@
color: $dark_mode_text_primary;
}
polyline, path {
polyline,
path {
color: inherit;
}
}

View File

@@ -1,15 +1,7 @@
<template>
<label :class="buttonStyle" label="file" class="button file-input button-base">
<slot></slot>
<input
accept="*"
v-show="false"
@change="emmitFiles"
id="file"
type="file"
name="files[]"
multiple
/>
<input accept="*" v-show="false" @change="emmitFiles" id="file" type="file" name="files[]" multiple />
</label>
</template>
@@ -19,14 +11,14 @@
props: ['buttonStyle'],
data() {
return {
files: undefined
files: undefined,
}
},
methods: {
emmitFiles(e) {
this.$uploadFiles(e.target.files)
}
}
},
},
}
</script>

View File

@@ -1,8 +1,11 @@
<template>
<div>
<div
class="w-5 h-5 flex items-center justify-center rounded-md"
:class="{'bg-theme': isClicked, 'dark:bg-dark-foreground bg-light-background': !isClicked}"
class="flex h-5 w-5 items-center justify-center rounded-md"
:class="{
'bg-theme': isClicked,
'bg-light-background dark:bg-dark-foreground': !isClicked,
}"
@click="changeState"
>
<CheckIcon v-if="isClicked" class="vue-feather text-white" size="17" />
@@ -15,25 +18,23 @@ import {CheckIcon} from 'vue-feather-icons'
export default {
name: 'CheckBox',
props: [
'isClicked'
],
props: ['isClicked'],
components: {
CheckIcon
CheckIcon,
},
data() {
return {
isSwitched: undefined
isSwitched: undefined,
}
},
methods: {
changeState() {
this.isSwitched = !this.isSwitched
this.$emit('input', this.isSwitched)
}
},
},
mounted() {
this.isSwitched = this.isClicked
}
},
}
</script>

View File

@@ -3,11 +3,10 @@
v-show="isVisible"
:style="{ top: positionY + 'px', left: positionX + 'px' }"
@click="closeAndResetContextMenu"
class="absolute w-60 shadow-lg rounded-xl z-20 dark:bg-2x-dark-foreground bg-white overflow-hidden"
class="absolute z-20 w-60 overflow-hidden rounded-xl bg-white shadow-lg dark:bg-2x-dark-foreground"
ref="contextmenu"
>
<div id="menu-list" class="w-full">
<!--Show empty select contextmenu-->
<slot name="empty-select" v-if="!item" />
@@ -21,24 +20,19 @@
</template>
<script>
import {events} from "../../bus";
import { events } from '../../bus'
import { mapGetters } from 'vuex'
export default {
name: 'ContextMenu',
computed: {
...mapGetters([
'clipboard',
'user',
]),
...mapGetters(['clipboard', 'user']),
isMultiSelectContextMenu() {
// If is context Menu open on multi selected items open just options for the multi selected items
if (this.clipboard.length > 1 && this.clipboard.includes(this.item))
return false
if (this.clipboard.length > 1 && this.clipboard.includes(this.item)) return false
// If is context Menu open for the non selected item open options for the single item
if (this.clipboard.length < 2 || !this.clipboard.includes(this.item))
return true
if (this.clipboard.length < 2 || !this.clipboard.includes(this.item)) return true
},
},
data() {
@@ -46,7 +40,7 @@ export default {
item: undefined,
isVisible: false,
positionX: 0,
positionY: 0
positionY: 0,
}
},
methods: {
@@ -87,7 +81,7 @@ export default {
// Show context menu
this.isVisible = true
}
},
},
created() {
events.$on('context-menu:hide', () => this.closeAndResetContextMenu())
@@ -100,7 +94,7 @@ export default {
setTimeout(() => this.showContextMenu(event, item), 10)
})
events.$on('context-menu:current-folder', folder => {
events.$on('context-menu:current-folder', (folder) => {
this.item = folder
this.isVisible = !this.isVisible
@@ -111,6 +105,6 @@ export default {
this.positionX = container.offsetLeft
}
})
}
},
}
</script>

View File

@@ -1,33 +1,46 @@
<template>
<div id="desktop-toolbar" class="lg:block hidden">
<div id="desktop-toolbar" class="hidden lg:block">
<div class="toolbar-wrapper">
<div @click="goBack" class="location">
<div v-if="!isVisibleNavigationBars" @click="toggleNavigationBars" class="mr-2">
<menu-icon size="17" />
</div>
<chevron-left-icon :class="{'opacity-0 -translate-x-3': ! currentFolder, 'opacity-100 translate-x-0': currentFolder }" class="icon-back transform transition-all duration-200" size="17" />
<chevron-left-icon
:class="{
'-translate-x-3 opacity-0': !currentFolder,
'translate-x-0 opacity-100': currentFolder,
}"
class="icon-back transform transition-all duration-200"
size="17"
/>
<span :class="{ '-translate-x-4': !currentFolder }" class="location-title transform transition-all duration-200">
{{ $getCurrentLocationName() }}
</span>
<span :class="{'-translate-x-4 opacity-0': ! currentFolder, 'translate-x-0 opacity-100': currentFolder}" @click.stop="folderActions" class="transform location-more group transition-all duration-200" id="folder-actions">
<span
:class="{
'-translate-x-4 opacity-0': !currentFolder,
'translate-x-0 opacity-100': currentFolder,
}"
@click.stop="folderActions"
class="location-more group transform transition-all duration-200"
id="folder-actions"
>
<more-horizontal-icon size="14" class="icon-more group-hover-text-theme" />
</span>
</div>
<ToolbarWrapper>
<!--Search bar-->
<ToolbarGroup class="ml-0">
<SearchBar class="lg:block hidden" />
<SearchBar class="hidden lg:block" />
</ToolbarGroup>
<!--Create button for all pages except SharedWithMe-->
<ToolbarGroup v-if="$checkPermission(['master', 'editor']) && !$isThisRoute($route, ['SharedWithMe'])">
<span class="lg:hidden block">
<span class="block lg:hidden">
<ToolbarButton @click.stop.native="$openSpotlight()" source="search" :action="$t('Search files or folders')" />
</span>
@@ -41,7 +54,14 @@
</OptionGroup>
<OptionGroup>
<Option @click.stop.native="$createTeamFolder" :title="$t('Create Team Folder')" icon="users" />
<Option @click.stop.native="$createFolder" :class="{'is-inactive': canCreateFolderInView || isTeamFolderHomepage }" :title="$t('actions.create_folder')" icon="folder-plus" />
<Option
@click.stop.native="$createFolder"
:class="{
'is-inactive': canCreateFolderInView || isTeamFolderHomepage,
}"
:title="$t('actions.create_folder')"
icon="folder-plus"
/>
</OptionGroup>
</PopoverItem>
</PopoverWrapper>
@@ -49,7 +69,7 @@
<!--Create button for shared with me page-->
<ToolbarGroup v-if="$isThisRoute($route, ['SharedWithMe'])">
<span class="lg:hidden block">
<span class="block lg:hidden">
<ToolbarButton @click.stop.native="$openSpotlight()" source="search" :action="$t('Search files or folders')" />
</span>
@@ -58,10 +78,22 @@
<PopoverItem name="desktop-create" side="left">
<OptionGroup>
<OptionUpload :class="{'is-inactive': canUploadInView || isSharedWithMeHomepage }" :title="$t('actions.upload')" />
<OptionUpload
:class="{
'is-inactive': canUploadInView || isSharedWithMeHomepage,
}"
:title="$t('actions.upload')"
/>
</OptionGroup>
<OptionGroup>
<Option @click.stop.native="$createFolder" :class="{'is-inactive': canCreateFolderInView || isSharedWithMeHomepage }" :title="$t('actions.create_folder')" icon="folder-plus" />
<Option
@click.stop.native="$createFolder"
:class="{
'is-inactive': canCreateFolderInView || isSharedWithMeHomepage,
}"
:title="$t('actions.create_folder')"
icon="folder-plus"
/>
</OptionGroup>
</PopoverItem>
</PopoverWrapper>
@@ -69,10 +101,13 @@
<!--File Controls-->
<ToolbarGroup v-if="$checkPermission(['master', 'editor']) || ($isMobile() && $isThisRoute($route, ['SharedWithMe', 'TeamFolders']))">
<!--Team Heads-->
<PopoverWrapper v-if="$isThisRoute($route, ['TeamFolders', 'SharedWithMe'])">
<TeamMembersButton @click.stop.native="showTeamFolderMenu" size="32" class="dark:hover:bg-dark-foreground hover:bg-light-background rounded-lg cursor-pointer py-0.5 pl-2 pr-0.5" />
<TeamMembersButton
@click.stop.native="showTeamFolderMenu"
size="32"
class="cursor-pointer rounded-lg py-0.5 pl-2 pr-0.5 hover:bg-light-background dark:hover:bg-dark-foreground"
/>
<PopoverItem name="team-folder" side="left">
<TeamFolderPreview />
@@ -90,11 +125,39 @@
<!--Item actions-->
<span v-if="!$isMobile()" class="whitespace-nowrap">
<ToolbarButton v-if="canShowConvertToTeamFolder" @click.native="$convertAsTeamFolder(clipboard[0])" :class="{'is-inactive': ! canCreateTeamFolderInView }" source="user-plus" :action="$t('actions.convert_into_team_folder')" />
<ToolbarButton v-if="! $isThisRoute($route, ['SharedWithMe', 'Public'])" @click.native="$shareFileOrFolder(clipboard[0])" :class="{'is-inactive': canShareInView }" source="share" :action="$t('actions.share')" />
<ToolbarButton
v-if="canShowConvertToTeamFolder"
@click.native="$convertAsTeamFolder(clipboard[0])"
:class="{
'is-inactive': !canCreateTeamFolderInView,
}"
source="user-plus"
:action="$t('actions.convert_into_team_folder')"
/>
<ToolbarButton
v-if="!$isThisRoute($route, ['SharedWithMe', 'Public'])"
@click.native="$shareFileOrFolder(clipboard[0])"
:class="{ 'is-inactive': canShareInView }"
source="share"
:action="$t('actions.share')"
/>
<ToolbarButton @click.native="$moveFileOrFolder(clipboard[0])" :class="{'is-inactive': canMoveInView && ! canEdit }" source="move" :action="$t('actions.move')" />
<ToolbarButton @click.native="$deleteFileOrFolder(clipboard[0])" :class="{'is-inactive': canDeleteInView && ! canEdit }" source="trash" :action="$t('actions.delete')" />
<ToolbarButton
@click.native="$moveFileOrFolder(clipboard[0])"
:class="{
'is-inactive': canMoveInView && !canEdit,
}"
source="move"
:action="$t('actions.move')"
/>
<ToolbarButton
@click.native="$deleteFileOrFolder(clipboard[0])"
:class="{
'is-inactive': canDeleteInView && !canEdit,
}"
source="trash"
:action="$t('actions.delete')"
/>
</span>
</ToolbarGroup>
@@ -116,21 +179,21 @@
</template>
<script>
import FileSortingOptions from "./FileSortingOptions";
import UploadProgress from "./UploadProgress";
import PopoverWrapper from "../Desktop/PopoverWrapper";
import ToolbarWrapper from "../Desktop/ToolbarWrapper";
import ToolbarButton from "./ToolbarButton";
import OptionUpload from "./OptionUpload";
import ToolbarGroup from "../Desktop/ToolbarGroup";
import OptionGroup from "./OptionGroup";
import TeamMembersButton from "../Teams/Components/TeamMembersButton"
import PopoverItem from "../Desktop/PopoverItem";
import TeamFolderPreview from "../Teams/Components/TeamFolderPreview"
import FileSortingOptions from './FileSortingOptions'
import UploadProgress from './UploadProgress'
import PopoverWrapper from '../Desktop/PopoverWrapper'
import ToolbarWrapper from '../Desktop/ToolbarWrapper'
import ToolbarButton from './ToolbarButton'
import OptionUpload from './OptionUpload'
import ToolbarGroup from '../Desktop/ToolbarGroup'
import OptionGroup from './OptionGroup'
import TeamMembersButton from '../Teams/Components/TeamMembersButton'
import PopoverItem from '../Desktop/PopoverItem'
import TeamFolderPreview from '../Teams/Components/TeamFolderPreview'
import { MenuIcon, ChevronLeftIcon, MoreHorizontalIcon } from 'vue-feather-icons'
import SearchBar from "./SearchBar";
import Option from "./Option";
import {events} from "../../bus";
import SearchBar from './SearchBar'
import Option from './Option'
import { events } from '../../bus'
import { mapGetters } from 'vuex'
export default {
@@ -154,17 +217,10 @@
Option,
},
computed: {
...mapGetters([
'isVisibleNavigationBars',
'currentTeamFolder',
'currentFolder',
'sharedDetail',
'clipboard',
'user',
]),
...mapGetters(['isVisibleNavigationBars', 'currentTeamFolder', 'currentFolder', 'sharedDetail', 'clipboard', 'user']),
canEdit() {
if (this.currentTeamFolder && this.user && this.clipboard[0]) {
let member = this.currentTeamFolder.data.relationships.members.data.find(member => member.data.id === this.user.data.id)
let member = this.currentTeamFolder.data.relationships.members.data.find((member) => member.data.id === this.user.data.id)
return member.data.attributes.permission === 'can-edit'
}
@@ -172,9 +228,7 @@
return false
},
teamFolder() {
return this.currentTeamFolder
? this.currentTeamFolder
: this.clipboard[0]
return this.currentTeamFolder ? this.currentTeamFolder : this.clipboard[0]
},
isNotHomepage() {
if (this.$isThisRoute(this.$route, ['Public'])) {
@@ -184,12 +238,10 @@
return this.$route.params.id
},
isTeamFolderHomepage() {
return this.$isThisRoute(this.$route, ['TeamFolders'])
&& ! this.$route.params.id
return this.$isThisRoute(this.$route, ['TeamFolders']) && !this.$route.params.id
},
isSharedWithMeHomepage() {
return this.$isThisRoute(this.$route, ['SharedWithMe'])
&& ! this.$route.params.id
return this.$isThisRoute(this.$route, ['SharedWithMe']) && !this.$route.params.id
},
canCreateFolderInView() {
return !this.$isThisRoute(this.$route, ['Files', 'Public', 'TeamFolders', 'SharedWithMe'])
@@ -201,52 +253,22 @@
return !this.$isThisRoute(this.$route, ['Files', 'Public', 'TeamFolders', 'SharedWithMe'])
},
canDeleteInView() {
let routes = [
'TeamFolders',
'SharedWithMe',
'RecentUploads',
'MySharedItems',
'Trash',
'Public',
'Files',
]
return !this.$isThisRoute(this.$route, routes)
|| this.clipboard.length === 0
let routes = ['TeamFolders', 'SharedWithMe', 'RecentUploads', 'MySharedItems', 'Trash', 'Public', 'Files']
return !this.$isThisRoute(this.$route, routes) || this.clipboard.length === 0
},
canMoveInView() {
let routes = [
'SharedWithMe',
'RecentUploads',
'MySharedItems',
'Public',
'Files',
'TeamFolders',
]
return ! this.$isThisRoute(this.$route, routes)
|| this.clipboard.length === 0
let routes = ['SharedWithMe', 'RecentUploads', 'MySharedItems', 'Public', 'Files', 'TeamFolders']
return !this.$isThisRoute(this.$route, routes) || this.clipboard.length === 0
},
canShareInView() {
let routes = [
'TeamFolders',
'RecentUploads',
'MySharedItems',
'Public',
'Files',
]
return ! this.$isThisRoute(this.$route, routes)
|| this.clipboard.length > 1
|| this.clipboard.length === 0
let routes = ['TeamFolders', 'RecentUploads', 'MySharedItems', 'Public', 'Files']
return !this.$isThisRoute(this.$route, routes) || this.clipboard.length > 1 || this.clipboard.length === 0
},
canCreateTeamFolderInView() {
let routes = [
'MySharedItems',
'Files',
]
let routes = ['MySharedItems', 'Files']
return this.$isThisRoute(this.$route, routes)
&& this.clipboard.length === 1
&& this.clipboard[0].data.type === 'folder'
}
return this.$isThisRoute(this.$route, routes) && this.clipboard.length === 1 && this.clipboard[0].data.type === 'folder'
},
},
methods: {
toggleNavigationBars() {
@@ -256,8 +278,7 @@
if (this.isNotHomepage) this.$router.back()
},
showTeamFolderMenu() {
if (this.teamFolder)
events.$emit('popover:open', 'team-folder')
if (this.teamFolder) events.$emit('popover:open', 'team-folder')
},
showCreateMenu() {
events.$emit('popover:open', 'desktop-create')
@@ -273,8 +294,8 @@
</script>
<style scoped lang="scss">
@import "resources/sass/vuefilemanager/_variables";
@import "resources/sass/vuefilemanager/_mixins";
@import 'resources/sass/vuefilemanager/_variables';
@import 'resources/sass/vuefilemanager/_mixins';
.is-inactive {
opacity: 0.25;
@@ -344,7 +365,6 @@
@media only screen and (max-width: 1024px) {
.location {
.location-title {
max-width: 120px;
}
@@ -360,7 +380,6 @@
}
.dark {
.toolbar .directory-name {
color: $dark_mode_text_primary;
}

View File

@@ -1,27 +1,19 @@
<template>
<TitlePreview
icon="check-square"
:title="title"
:subtitle="subtitle"
id="drag-ui"
v-show="isVisible"
/>
<TitlePreview icon="check-square" :title="title" :subtitle="subtitle" id="drag-ui" v-show="isVisible" />
</template>
<script>
import TitlePreview from "./TitlePreview";
import TitlePreview from './TitlePreview'
import { mapGetters } from 'vuex'
import { events } from '../../bus'
export default {
name: 'DragUI',
components: {
TitlePreview
TitlePreview,
},
computed: {
...mapGetters([
'clipboard'
]),
...mapGetters(['clipboard']),
title() {
let filesLength = this.clipboard.length,
hasDraggedItem = this.clipboard.includes(this.draggedItem)
@@ -46,7 +38,6 @@ export default {
}
if ((filesLength < 2 || !hasDraggedItem) && this.draggedItem) {
// Subtitle for single folder
if (this.draggedItem.data.type === 'folder') {
return this.draggedItem.items == 0 ? this.$t('folder.empty') : this.$tc('folder.item_counts', this.draggedItem.items)
@@ -57,16 +48,16 @@ export default {
return '.' + this.draggedItem.data.attributes.mimetype
}
}
}
},
},
data() {
return {
isVisible: false,
draggedItem: undefined
draggedItem: undefined,
}
},
created() {
events.$on('dragstart', data => {
events.$on('dragstart', (data) => {
this.draggedItem = data
setTimeout(() => {
@@ -74,8 +65,8 @@ export default {
}, 100)
})
events.$on('drop', () => this.isVisible = false)
}
events.$on('drop', () => (this.isVisible = false))
},
}
</script>
@@ -100,5 +91,4 @@ export default {
background: $dark_mode_foreground;
}
}
</style>

View File

@@ -1,20 +1,19 @@
<template>
<div class="flex items-center justify-center h-full" v-if="isLoading || isEmpty">
<div class="flex h-full items-center justify-center" v-if="isLoading || isEmpty">
<!--Show message for user-->
<div v-if="!isLoading" class="text-content text-center">
<slot></slot>
</div>
<!--Show spinner when loading content-->
<div v-else class="sm:relative fixed top-0 bottom-0">
<div v-else class="fixed top-0 bottom-0 sm:relative">
<Spinner />
</div>
</div>
</template>
<script>
import Spinner from "./Spinner";
import Spinner from './Spinner'
import { mapGetters } from 'vuex'
export default {
@@ -23,14 +22,11 @@
Spinner,
},
computed: {
...mapGetters([
'isLoading',
'entries',
]),
...mapGetters(['isLoading', 'entries']),
isEmpty() {
return this.entries && this.entries.length === 0
}
}
},
},
}
</script>
@@ -53,7 +49,6 @@
}
.dark {
.title {
color: $dark_mode_text_primary;
}

View File

@@ -1,6 +1,5 @@
<template>
<div class="sticky dark:bg-dark-background bg-white top-14 pb-3 px-4 z-20 whitespace-nowrap overflow-x-auto lg:hidden block">
<div class="sticky top-14 z-20 block overflow-x-auto whitespace-nowrap bg-white px-4 pb-3 dark:bg-dark-background lg:hidden">
<!--Show Buttons-->
<div v-if="!isMultiSelectMode" class="mobile-actions">
<slot></slot>
@@ -25,9 +24,9 @@
</template>
<script>
import MobileActionButton from "./MobileActionButton";
import UploadProgress from "./UploadProgress";
import {mapGetters} from "vuex";
import MobileActionButton from './MobileActionButton'
import UploadProgress from './UploadProgress'
import { mapGetters } from 'vuex'
export default {
name: 'FileActionsMobile',
@@ -36,9 +35,7 @@
UploadProgress,
},
computed: {
...mapGetters([
'isMultiSelectMode'
])
...mapGetters(['isMultiSelectMode']),
},
methods: {
selectAll() {
@@ -50,7 +47,7 @@
disableMultiSelectMode() {
this.$store.commit('TOGGLE_MULTISELECT_MODE')
},
}
},
}
</script>

View File

@@ -3,9 +3,9 @@
:class="{
'user-dropping-file': isDragging,
'grid-view': itemViewType === 'grid' && !isVisibleSidebar,
'grid-view-sidebar': itemViewType === 'grid' && isVisibleSidebar
'grid-view-sidebar': itemViewType === 'grid' && isVisibleSidebar,
}"
class="lg:w-full lg:overflow-y-auto lg:h-full lg:px-0 px-4"
class="px-4 lg:h-full lg:w-full lg:overflow-y-auto lg:px-0"
@drop.stop.prevent="uploadDroppedItems($event)"
@keydown.delete="deleteItems"
@dragover="dragEnter"
@@ -28,8 +28,8 @@
</template>
<script>
import ItemHandler from "./ItemHandler";
import {events} from "../../bus";
import ItemHandler from './ItemHandler'
import { events } from '../../bus'
import { mapGetters } from 'vuex'
export default {
@@ -38,13 +38,7 @@
ItemHandler,
},
computed: {
...mapGetters([
'isVisibleSidebar',
'currentFolder',
'itemViewType',
'clipboard',
'entries'
]),
...mapGetters(['isVisibleSidebar', 'currentFolder', 'itemViewType', 'clipboard', 'entries']),
draggedItems() {
// Set opacity for dragged items
if (!this.clipboard.includes(this.draggingId)) {
@@ -54,7 +48,7 @@
if (this.clipboard.includes(this.draggingId)) {
return this.clipboard
}
}
},
},
data() {
return {
@@ -64,7 +58,7 @@
},
methods: {
deleteItems() {
if (this.clipboard.length > 0 && this.$checkPermission('master') || this.$checkPermission('editor')) {
if ((this.clipboard.length > 0 && this.$checkPermission('master')) || this.$checkPermission('editor')) {
this.$store.dispatch('deleteItem')
}
},
@@ -81,7 +75,7 @@
},
dragStart(data) {
let img = document.createElement('img')
img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
event.dataTransfer.setDragImage(img, 0, 0)
events.$emit('dragstart', data)
@@ -92,26 +86,29 @@
// TODO: founded issue on firefox
},
dragFinish(data, event) {
if (event.dataTransfer.items.length === 0) {
// Prevent to drop on file or image
if (data.data.type !== 'folder' || this.draggingId === data) return
// Prevent move selected folder to folder if in between selected folders
if (this.clipboard.find(item => item === data && this.clipboard.length > 1)) return
if (this.clipboard.find((item) => item === data && this.clipboard.length > 1)) return
// Move item if is not included in selected items
if (!this.clipboard.includes(this.draggingId)) {
this.$store.dispatch('moveItem', {to_item: data, noSelectedItem: this.draggingId})
this.$store.dispatch('moveItem', {
to_item: data,
noSelectedItem: this.draggingId,
})
}
// Move selected items to folder
if (this.clipboard.length > 0 && this.clipboard.includes(this.draggingId)) {
this.$store.dispatch('moveItem', {to_item: data, noSelectedItem: null})
this.$store.dispatch('moveItem', {
to_item: data,
noSelectedItem: null,
})
}
} else {
// Get id from current folder
const id = data.data.type !== 'folder' ? this.currentFolder.data.id : data.data.id
@@ -133,7 +130,7 @@
// Clear clipboard
this.$store.commit('CLIPBOARD_CLEAR')
}
},
},
created() {
events.$on('drop', () => {
@@ -143,16 +140,16 @@
this.draggingId = undefined
}, 10)
})
}
},
}
</script>
<style>
.grid-view {
@apply grid content-start xl:grid-cols-6 sm:grid-cols-4 grid-cols-3 xl:gap-4 lg:gap-2
@apply grid grid-cols-3 content-start sm:grid-cols-4 lg:gap-2 xl:grid-cols-6 xl:gap-4;
}
.grid-view-sidebar {
@apply grid content-start 2xl:grid-cols-5 xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2 grid-cols-3 xl:gap-4 lg:gap-2
@apply grid grid-cols-3 content-start md:grid-cols-2 lg:grid-cols-3 lg:gap-2 xl:grid-cols-4 xl:gap-4 2xl:grid-cols-5;
}
</style>

View File

@@ -9,14 +9,20 @@
</OptionGroup>
<OptionGroup>
<Option @click.native="goToTeamFolders" :title="$t('Team Folders')" icon="users" :is-active="$isThisRoute($route, 'TeamFolders')" :is-hover-disabled="true" />
<Option @click.native="goToSharedWithMe" :title="$t('Shared with Me')" icon="user-check" :is-active="$isThisRoute($route, 'SharedWithMe')" :is-hover-disabled="true" />
<Option
@click.native="goToSharedWithMe"
:title="$t('Shared with Me')"
icon="user-check"
:is-active="$isThisRoute($route, 'SharedWithMe')"
:is-hover-disabled="true"
/>
</OptionGroup>
</MenuMobileGroup>
</MenuMobile>
</template>
<script>
import MenuMobileGroup from "../Mobile/MenuMobileGroup";
import MenuMobileGroup from '../Mobile/MenuMobileGroup'
import OptionGroup from '../FilesView/OptionGroup'
import MenuMobile from '../Mobile/MenuMobile'
import Option from '../FilesView/Option'
@@ -49,6 +55,6 @@ export default {
goToSharedWithMe() {
this.$router.push({ name: 'SharedWithMe' })
},
}
},
}
</script>

View File

@@ -1,6 +1,6 @@
<template>
<div class="flex items-center justify-center">
<span class="text-theme dark-text-theme lg:text-xs text-tiny font-semibold absolute z-10 inline-block mx-auto mt-1 w-7 text-ellipsis overflow-hidden text-center">
<span class="text-theme dark-text-theme absolute z-10 mx-auto mt-1 inline-block w-7 overflow-hidden text-ellipsis text-center text-tiny font-semibold lg:text-xs">
{{ entry.data.attributes.mimetype }}
</span>
@@ -8,7 +8,8 @@
<path
stroke-width="0"
fill="#f4f5f6"
d="M22.1666667,13.546875 L22.1666667,0 L2.375,0 C1.05885417,0 0,1.06582031 0,2.390625 L0,48.609375 C0,49.9341797 1.05885417,51 2.375,51 L35.625,51 C36.9411458,51 38,49.9341797 38,48.609375 L38,15.9375 L24.5416667,15.9375 C23.2354167,15.9375 22.1666667,14.8617187 22.1666667,13.546875 Z M38,12.1423828 L38,12.75 L25.3333333,12.75 L25.3333333,0 L25.9369792,0 C26.5703125,0 27.1739583,0.249023438 27.6192708,0.697265625 L37.3072917,10.4589844 C37.7526042,10.9072266 38,11.5148437 38,12.1423828 Z"></path>
d="M22.1666667,13.546875 L22.1666667,0 L2.375,0 C1.05885417,0 0,1.06582031 0,2.390625 L0,48.609375 C0,49.9341797 1.05885417,51 2.375,51 L35.625,51 C36.9411458,51 38,49.9341797 38,48.609375 L38,15.9375 L24.5416667,15.9375 C23.2354167,15.9375 22.1666667,14.8617187 22.1666667,13.546875 Z M38,12.1423828 L38,12.75 L25.3333333,12.75 L25.3333333,0 L25.9369792,0 C26.5703125,0 27.1739583,0.249023438 27.6192708,0.697265625 L37.3072917,10.4589844 C37.7526042,10.9072266 38,11.5148437 38,12.1423828 Z"
></path>
</svg>
</div>
</template>
@@ -16,8 +17,6 @@
<script>
export default {
name: 'FileIconThumbnail',
props: [
'entry'
]
props: ['entry'],
}
</script>

View File

@@ -7,8 +7,8 @@
</template>
<script>
import FileSortingOptions from "./FileSortingOptions";
import MenuMobileGroup from "../Mobile/MenuMobileGroup";
import FileSortingOptions from './FileSortingOptions'
import MenuMobileGroup from '../Mobile/MenuMobileGroup'
import MenuMobile from '../Mobile/MenuMobile'
export default {

View File

@@ -12,8 +12,7 @@
</template>
<script>
import OptionGroup from "./OptionGroup";
import OptionGroup from './OptionGroup'
import Option from './Option'
import { ArrowUpIcon } from 'vue-feather-icons'
import { mapGetters } from 'vuex'
@@ -26,9 +25,7 @@ export default {
Option,
},
computed: {
...mapGetters([
'itemViewType'
]),
...mapGetters(['itemViewType']),
isGrid() {
return this.itemViewType === 'grid'
},
@@ -36,24 +33,22 @@ export default {
return this.itemViewType === 'list'
},
arrowForCreatedAtField() {
if (this.filter.field !== 'created_at')
return undefined
if (this.filter.field !== 'created_at') return undefined
return this.filter.sort === 'DESC' ? 'up' : 'down'
},
arrowForNameField() {
if (this.filter.field !== 'name')
return undefined
if (this.filter.field !== 'name') return undefined
return this.filter.sort === 'DESC' ? 'up' : 'down'
}
},
},
data() {
return {
filter: {
sort: 'DESC',
field: undefined
}
field: undefined,
},
}
},
methods: {
@@ -61,13 +56,17 @@ export default {
this.filter.field = field
// Set sorting direction
if (this.filter.sort === 'DESC')
this.filter.sort = 'ASC'
else if (this.filter.sort === 'ASC')
this.filter.sort = 'DESC'
if (this.filter.sort === 'DESC') this.filter.sort = 'ASC'
else if (this.filter.sort === 'ASC') this.filter.sort = 'DESC'
// Save to localStorage sorting options
localStorage.setItem('sorting', JSON.stringify({ sort: this.filter.sort, field: this.filter.field }))
localStorage.setItem(
'sorting',
JSON.stringify({
sort: this.filter.sort,
field: this.filter.field,
})
)
// Update sorting state in vuex
this.$store.commit('UPDATE_SORTING')
@@ -82,7 +81,7 @@ export default {
},
changePreview(previewType) {
this.$store.dispatch('togglePreviewType', previewType)
}
},
},
mounted() {
let sorting = JSON.parse(localStorage.getItem('sorting'))
@@ -90,6 +89,6 @@ export default {
// Set default sorting if in not setup in LocalStorage
this.filter.sort = sorting ? sorting.sort : 'DESC'
this.filter.field = sorting ? sorting.field : 'created_at'
}
},
}
</script>

View File

@@ -1,24 +1,17 @@
<template>
<div>
<VueFolderIcon
v-if="!item.data.attributes.isTeamFolder"
/>
<VueFolderTeamIcon
v-if="item.data.attributes.isTeamFolder"
style="width: 53px; height: 52px"
/>
<VueFolderIcon v-if="!item.data.attributes.isTeamFolder" />
<VueFolderTeamIcon v-if="item.data.attributes.isTeamFolder" style="width: 53px; height: 52px" />
</div>
</template>
<script>
import VueFolderTeamIcon from "./Icons/VueFolderTeamIcon"
import VueFolderIcon from "./Icons/VueFolderIcon"
import VueFolderTeamIcon from './Icons/VueFolderTeamIcon'
import VueFolderIcon from './Icons/VueFolderIcon'
export default {
name: 'FolderIcon',
props: [
'item',
],
props: ['item'],
components: {
VueFolderTeamIcon,
VueFolderIcon,

View File

@@ -1,5 +1,19 @@
<template>
<svg class="alphabet-icon" fill="none" stroke="currentColor" stroke-width="2" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" :width="`${size}px`" :height="`${size}px`" viewBox="-2 0 15 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg
class="alphabet-icon"
fill="none"
stroke="currentColor"
stroke-width="2"
fill-rule="evenodd"
stroke-linecap="round"
stroke-linejoin="round"
:width="`${size}px`"
:height="`${size}px`"
viewBox="-2 0 15 15"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<polyline id="Path" points="11.1999993 13.1999991 5.59999967 0.199999094 0 13.1999991 5.59999967 0.199999094"></polyline>
<line x1="2.25" y1="8" x2="8.75" y2="8" id="Line-2"></line>
</svg>
@@ -7,15 +21,15 @@
<script>
export default {
props: ['size']
props: ['size'],
}
</script>
<style lang="scss">
.alphabet-icon {
polyline, line, g {
polyline,
line,
g {
color: inherit;
}
}

View File

@@ -1,6 +1,23 @@
<template>
<svg class="preview-list-icon" fill="none" stroke="currentColor" stroke-width="1.5" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" width="15px" height="15px" viewBox="0 0 17 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M14.2729998,10.7729998 C15.6774712,10.0073227 16.384111,8.38688297 15.9895447,6.83668332 C15.5949785,5.28648367 14.1996249,4.20105605 12.5999998,4.19999993 L11.7179998,4.19999993 C11.1377566,1.9556703 9.23470173,0.300843012 6.93154234,0.0378706728 C4.62838295,-0.225101666 2.40127934,0.958148431 1.33005562,3.01391529 C0.258831904,5.06968215 0.564955244,7.57295196 2.09999996,9.30999984" id="Path"></path>
<svg
class="preview-list-icon"
fill="none"
stroke="currentColor"
stroke-width="1.5"
fill-rule="evenodd"
stroke-linecap="round"
stroke-linejoin="round"
width="15px"
height="15px"
viewBox="0 0 17 12"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<path
d="M14.2729998,10.7729998 C15.6774712,10.0073227 16.384111,8.38688297 15.9895447,6.83668332 C15.5949785,5.28648367 14.1996249,4.20105605 12.5999998,4.19999993 L11.7179998,4.19999993 C11.1377566,1.9556703 9.23470173,0.300843012 6.93154234,0.0378706728 C4.62838295,-0.225101666 2.40127934,0.958148431 1.33005562,3.01391529 C0.258831904,5.06968215 0.564955244,7.57295196 2.09999996,9.30999984"
id="Path"
></path>
<line x1="8.5" y1="7" x2="8.5" y2="12" id="Path"></line>
<line x1="6" y1="9.5" x2="11" y2="9.5" id="Path"></line>
</svg>
@@ -8,15 +25,14 @@
<script>
export default {
name: "CloudPlusIcon",
};
name: 'CloudPlusIcon',
}
</script>
<style lang="scss">
.preview-list-icon {
path, line {
path,
line {
color: inherit;
}
}

View File

@@ -1,6 +1,23 @@
<template>
<svg class="preview-list-icon" fill="none" stroke="currentColor" stroke-width="1.5" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" :width="`${size}px`" :height="`${size}px`" viewBox="0 -2 14 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M0,10.6420028 C0,8.60583431 0,5.5515816 0,1.47924466 C0,0.662280392 0.633305625,0 1.4145277,0 L4.95084696,0 L6.36537467,2.21886699 L12.7307493,2.21886699 C13.5119714,2.21886699 14.145277,2.88114738 14.145277,3.69811164 C14.145277,7.76603445 14.145277,7.76603445 14.145277,11.8339573 C14.145277,12.6509215 13.5119714,13.3132019 12.7307493,13.3132019 C11.9928651,13.3132019 12.1671651,13.3132019 11.798223,13.3132019" id="Path"></path>
<svg
class="preview-list-icon"
fill="none"
stroke="currentColor"
stroke-width="1.5"
fill-rule="evenodd"
stroke-linecap="round"
stroke-linejoin="round"
:width="`${size}px`"
:height="`${size}px`"
viewBox="0 -2 14 17"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<path
d="M0,10.6420028 C0,8.60583431 0,5.5515816 0,1.47924466 C0,0.662280392 0.633305625,0 1.4145277,0 L4.95084696,0 L6.36537467,2.21886699 L12.7307493,2.21886699 C13.5119714,2.21886699 14.145277,2.88114738 14.145277,3.69811164 C14.145277,7.76603445 14.145277,7.76603445 14.145277,11.8339573 C14.145277,12.6509215 13.5119714,13.3132019 12.7307493,13.3132019 C11.9928651,13.3132019 12.1671651,13.3132019 11.798223,13.3132019"
id="Path"
></path>
<polyline id="Path-Copy-8" points="9.49893123 9.53496452 6.74946561 6.60112928 4 9.53496452"></polyline>
<line x1="6.74946561" y1="6.60112928" x2="6.74946561" y2="13.2022586" id="Path-Copy-7"></line>
</svg>
@@ -8,14 +25,12 @@
<script>
export default {
props: ['size']
props: ['size'],
}
</script>
<style lang="scss">
.preview-list-icon {
path,
line,
polyline {

View File

@@ -1,5 +1,19 @@
<template>
<svg class="preview-list-icon" fill="none" stroke="currentColor" stroke-width="1.5" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" width="15px" height="15px" viewBox="0 0 20 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg
class="preview-list-icon"
fill="none"
stroke="currentColor"
stroke-width="1.5"
fill-rule="evenodd"
stroke-linecap="round"
stroke-linejoin="round"
width="15px"
height="15px"
viewBox="0 0 20 16"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<rect x="9.77777778" y="0" width="6.22222222" height="6.22222222"></rect>
<rect x="9.77777778" y="9.77777778" width="6.22222222" height="6.22222222"></rect>
<line x1="0" y1="2" x2="6" y2="2"></line>
@@ -10,15 +24,14 @@
<script>
export default {
name: "SortingIcon",
};
name: 'SortingIcon',
}
</script>
<style lang="scss">
.preview-list-icon {
rect, line {
rect,
line {
color: inherit;
}
}

View File

@@ -1,24 +1,24 @@
<template>
<svg width="53px" height="52px" viewBox="0 0 53 39" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M48.03125,6.5 L29.790833,6.5 C28.7431613,6.5 27.7373076,6.08896217 26.9894703,5.35523504 L22.6980297,1.14476496 C21.9501924,0.41103783 20.9443387,-6.36543387e-16 19.896667,0 L4.96875,0 L4.96875,0 C2.22455078,0 0,2.18257812 0,4.875 L0,34.125 C0,36.8174219 2.22455078,39 4.96875,39 L48.03125,39 C50.7754492,39 53,36.8174219 53,34.125 L53,11.375 C53,8.68257813 50.7754492,6.5 48.03125,6.5 Z"
<path
d="M48.03125,6.5 L29.790833,6.5 C28.7431613,6.5 27.7373076,6.08896217 26.9894703,5.35523504 L22.6980297,1.14476496 C21.9501924,0.41103783 20.9443387,-6.36543387e-16 19.896667,0 L4.96875,0 L4.96875,0 C2.22455078,0 0,2.18257812 0,4.875 L0,34.125 C0,36.8174219 2.22455078,39 4.96875,39 L48.03125,39 C50.7754492,39 53,36.8174219 53,34.125 L53,11.375 C53,8.68257813 50.7754492,6.5 48.03125,6.5 Z"
class="svg-color-theme"
stroke="none"
stroke-width="0">
</path>
<path d="M48.03125,6.5 L29.790833,6.5 C28.7431613,6.5 27.7373076,6.08896217 26.9894703,5.35523504 L22.6980297,1.14476496 C21.9501924,0.41103783 20.9443387,-6.36543387e-16 19.896667,0 L4.96875,0 L4.96875,0 C2.22455078,0 0,2.18257812 0,4.875 L0,34.125 C0,36.8174219 2.22455078,39 4.96875,39 L48.03125,39 C50.7754492,39 53,36.8174219 53,34.125 L53,11.375 C53,8.68257813 50.7754492,6.5 48.03125,6.5 Z"
stroke-width="0"
></path>
<path
d="M48.03125,6.5 L29.790833,6.5 C28.7431613,6.5 27.7373076,6.08896217 26.9894703,5.35523504 L22.6980297,1.14476496 C21.9501924,0.41103783 20.9443387,-6.36543387e-16 19.896667,0 L4.96875,0 L4.96875,0 C2.22455078,0 0,2.18257812 0,4.875 L0,34.125 C0,36.8174219 2.22455078,39 4.96875,39 L48.03125,39 C50.7754492,39 53,36.8174219 53,34.125 L53,11.375 C53,8.68257813 50.7754492,6.5 48.03125,6.5 Z"
fill="black"
fill-opacity="0.2"
stroke="none"
stroke-width="0">
</path>
<path d="M48.03125,12.75 C49.0609313,12.75 49.9941504,13.1577174 50.6692739,13.8201027 C51.3356976,14.4739525 51.75,15.3766531 51.75,16.375 L51.75,16.375 L51.75,34.125 C51.75,35.1233469 51.3356976,36.0260475 50.6692739,36.6798973 C49.9941504,37.3422826 49.0609313,37.75 48.03125,37.75 L48.03125,37.75 L4.96875,37.75 C3.93906868,37.75 3.00584961,37.3422826 2.33072613,36.6798973 C1.66430239,36.0260475 1.25,35.1233469 1.25,34.125 L1.25,34.125 L1.25,16.375 C1.25,15.3766531 1.66430239,14.4739525 2.33072613,13.8201027 C3.00584961,13.1577174 3.93906868,12.75 4.96875,12.75 L4.96875,12.75 Z"
stroke-width="0"
></path>
<path
d="M48.03125,12.75 C49.0609313,12.75 49.9941504,13.1577174 50.6692739,13.8201027 C51.3356976,14.4739525 51.75,15.3766531 51.75,16.375 L51.75,16.375 L51.75,34.125 C51.75,35.1233469 51.3356976,36.0260475 50.6692739,36.6798973 C49.9941504,37.3422826 49.0609313,37.75 48.03125,37.75 L48.03125,37.75 L4.96875,37.75 C3.93906868,37.75 3.00584961,37.3422826 2.33072613,36.6798973 C1.66430239,36.0260475 1.25,35.1233469 1.25,34.125 L1.25,34.125 L1.25,16.375 C1.25,15.3766531 1.66430239,14.4739525 2.33072613,13.8201027 C3.00584961,13.1577174 3.93906868,12.75 4.96875,12.75 L4.96875,12.75 Z"
stroke-width="2"
class="svg-color-theme"
fill="green">
</path>
fill="green"
></path>
</svg>
</template>

View File

@@ -1,32 +1,44 @@
<template>
<svg viewBox="0 0 53 39" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="V2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="team-folder">
<path d="M48.03125,6.5 L29.790833,6.5 C28.7431613,6.5 27.7373076,6.08896217 26.9894703,5.35523504 L22.6980297,1.14476496 C21.9501924,0.41103783 20.9443387,-6.36543387e-16 19.896667,0 L4.96875,0 L4.96875,0 C2.22455078,0 0,2.18257812 0,4.875 L0,34.125 C0,36.8174219 2.22455078,39 4.96875,39 L48.03125,39 C50.7754492,39 53,36.8174219 53,34.125 L53,11.375 C53,8.68257813 50.7754492,6.5 48.03125,6.5 Z"
<path
d="M48.03125,6.5 L29.790833,6.5 C28.7431613,6.5 27.7373076,6.08896217 26.9894703,5.35523504 L22.6980297,1.14476496 C21.9501924,0.41103783 20.9443387,-6.36543387e-16 19.896667,0 L4.96875,0 L4.96875,0 C2.22455078,0 0,2.18257812 0,4.875 L0,34.125 C0,36.8174219 2.22455078,39 4.96875,39 L48.03125,39 C50.7754492,39 53,36.8174219 53,34.125 L53,11.375 C53,8.68257813 50.7754492,6.5 48.03125,6.5 Z"
class="svg-color-theme"
stroke="none"
stroke-width="0">
</path>
<path d="M48.03125,6.5 L29.790833,6.5 C28.7431613,6.5 27.7373076,6.08896217 26.9894703,5.35523504 L22.6980297,1.14476496 C21.9501924,0.41103783 20.9443387,-6.36543387e-16 19.896667,0 L4.96875,0 L4.96875,0 C2.22455078,0 0,2.18257812 0,4.875 L0,34.125 C0,36.8174219 2.22455078,39 4.96875,39 L48.03125,39 C50.7754492,39 53,36.8174219 53,34.125 L53,11.375 C53,8.68257813 50.7754492,6.5 48.03125,6.5 Z"
stroke-width="0"
></path>
<path
d="M48.03125,6.5 L29.790833,6.5 C28.7431613,6.5 27.7373076,6.08896217 26.9894703,5.35523504 L22.6980297,1.14476496 C21.9501924,0.41103783 20.9443387,-6.36543387e-16 19.896667,0 L4.96875,0 L4.96875,0 C2.22455078,0 0,2.18257812 0,4.875 L0,34.125 C0,36.8174219 2.22455078,39 4.96875,39 L48.03125,39 C50.7754492,39 53,36.8174219 53,34.125 L53,11.375 C53,8.68257813 50.7754492,6.5 48.03125,6.5 Z"
fill="black"
fill-opacity="0.2"
stroke="none"
stroke-width="0">
</path>
<path d="M48.03125,12.75 C49.0609313,12.75 49.9941504,13.1577174 50.6692739,13.8201027 C51.3356976,14.4739525 51.75,15.3766531 51.75,16.375 L51.75,16.375 L51.75,34.125 C51.75,35.1233469 51.3356976,36.0260475 50.6692739,36.6798973 C49.9941504,37.3422826 49.0609313,37.75 48.03125,37.75 L48.03125,37.75 L4.96875,37.75 C3.93906868,37.75 3.00584961,37.3422826 2.33072613,36.6798973 C1.66430239,36.0260475 1.25,35.1233469 1.25,34.125 L1.25,34.125 L1.25,16.375 C1.25,15.3766531 1.66430239,14.4739525 2.33072613,13.8201027 C3.00584961,13.1577174 3.93906868,12.75 4.96875,12.75 L4.96875,12.75 Z"
stroke-width="0"
></path>
<path
d="M48.03125,12.75 C49.0609313,12.75 49.9941504,13.1577174 50.6692739,13.8201027 C51.3356976,14.4739525 51.75,15.3766531 51.75,16.375 L51.75,16.375 L51.75,34.125 C51.75,35.1233469 51.3356976,36.0260475 50.6692739,36.6798973 C49.9941504,37.3422826 49.0609313,37.75 48.03125,37.75 L48.03125,37.75 L4.96875,37.75 C3.93906868,37.75 3.00584961,37.3422826 2.33072613,36.6798973 C1.66430239,36.0260475 1.25,35.1233469 1.25,34.125 L1.25,34.125 L1.25,16.375 C1.25,15.3766531 1.66430239,14.4739525 2.33072613,13.8201027 C3.00584961,13.1577174 3.93906868,12.75 4.96875,12.75 L4.96875,12.75 Z"
stroke-width="2"
class="svg-color-theme"
fill="green">
</path>
<g id="Icon" transform="translate(8.000000, 20.000000)" class="svg-stroke-theme-darken" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.3" stroke="black" stroke-opacity="0.25">
<path d="M9.59999943,10.7999994 L9.59999943,9.59999943 C9.59999943,8.27451611 8.52548289,7.19999957 7.19999957,7.19999957 L2.39999986,7.19999957 C1.07451654,7.19999957 0,8.27451611 0,9.59999943 L0,10.7999994"></path>
fill="green"
></path>
<g
id="Icon"
transform="translate(8.000000, 20.000000)"
class="svg-stroke-theme-darken"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.3"
stroke="black"
stroke-opacity="0.25"
>
<path
d="M9.59999943,10.7999994 L9.59999943,9.59999943 C9.59999943,8.27451611 8.52548289,7.19999957 7.19999957,7.19999957 L2.39999986,7.19999957 C1.07451654,7.19999957 0,8.27451611 0,9.59999943 L0,10.7999994"
></path>
<circle cx="4.79999971" cy="2.39999986" r="2.39999986"></circle>
<path d="M13.1999992,10.7999994 L13.1999992,9.59999943 C13.1991834,8.50627014 12.4589985,7.55143166 11.3999993,7.27799957"></path>
<path d="M8.99999946,0.0779999954 C10.0619483,0.349901852 10.8047053,1.30679461 10.8047053,2.40299986 C10.8047053,3.4992051 10.0619483,4.45609786 8.99999946,4.72799972"></path>
<path
d="M8.99999946,0.0779999954 C10.0619483,0.349901852 10.8047053,1.30679461 10.8047053,2.40299986 C10.8047053,3.4992051 10.0619483,4.45609786 8.99999946,4.72799972"
></path>
</g>
</g>
</g>

View File

@@ -96,14 +96,13 @@ export default {
formatGps(location, ref) {
let data = []
location.forEach(location => {
location.forEach((location) => {
data.push(split(location, '/', 2)[0])
})
return `${data[0]}° ${data[1]}' ${data[2].substr(0, 4) / 100}" ${ref} `
}
},
},
}
</script>
@@ -122,7 +121,8 @@ export default {
padding: 9px 0;
border-bottom: 1px solid $light_mode_border;
b, span {
b,
span {
@include font-size(14);
color: $text;
}
@@ -130,12 +130,12 @@ export default {
}
.dark {
.meta-data-list {
li {
border-color: $dark_mode_border_color;
b, span {
b,
span {
color: $dark_mode_text_primary !important;
}
}

View File

@@ -1,10 +1,10 @@
<template>
<div class="2xl:w-104 w-96 px-2.5 overflow-y-auto overflow-x-hidden h-screen lg:block hidden">
<div class="hidden h-screen w-96 overflow-y-auto overflow-x-hidden px-2.5 lg:block 2xl:w-104">
<!--Is empty clipboard-->
<div v-if="isEmpty" class="flex items-center justify-center h-full">
<div v-if="isEmpty" class="flex h-full items-center justify-center">
<div class="text-center">
<eye-off-icon size="28" class="vue-feather text-gray-400 inline-block mb-3" />
<small class="text-sm block text-gray-400">
<eye-off-icon size="28" class="vue-feather mb-3 inline-block text-gray-400" />
<small class="block text-sm text-gray-400">
{{ $t('messages.nothing_to_preview') }}
</small>
</div>
@@ -23,33 +23,18 @@
<div v-if="isSingleFile && !isEmpty">
<FilePreviewDetail />
<TitlePreview
class="mb-6"
:icon="clipboard[0].data.type"
:title="clipboard[0].data.attributes.name"
:subtitle="clipboard[0].data.attributes.mimetype"
/>
<TitlePreview class="mb-6" :icon="clipboard[0].data.type" :title="clipboard[0].data.attributes.name" :subtitle="clipboard[0].data.attributes.mimetype" />
<!--Filesize-->
<ListInfoItem
v-if="singleFile.data.attributes.filesize"
:title="$t('file_detail.size')"
:content="singleFile.data.attributes.filesize"
/>
<ListInfoItem v-if="singleFile.data.attributes.filesize" :title="$t('file_detail.size')" :content="singleFile.data.attributes.filesize" />
<!--Created At-->
<ListInfoItem
:title="$t('file_detail.created_at')"
:content="singleFile.data.attributes.created_at"
/>
<ListInfoItem :title="$t('file_detail.created_at')" :content="singleFile.data.attributes.created_at" />
<!--Location-->
<ListInfoItem
v-if="$checkPermission(['master'])"
:title="$t('file_detail.where')"
>
<div @click="$moveFileOrFolder(singleFile)" class="flex items-center cursor-pointer">
<span class="inline-block font-bold text-sm">
<ListInfoItem v-if="$checkPermission(['master'])" :title="$t('file_detail.where')">
<div @click="$moveFileOrFolder(singleFile)" class="flex cursor-pointer items-center">
<span class="inline-block text-sm font-bold">
{{ singleFile.data.relationships.parent ? singleFile.data.relationships.parent.data.attributes.name : $t('locations.home') }}
</span>
<Edit2Icon size="10" class="ml-2" />
@@ -57,28 +42,22 @@
</ListInfoItem>
<!--Team-->
<ListInfoItem
v-if="singleFile.data.attributes.isTeamFolder"
:title="$t('Shared with the Team')"
>
<div class="flex items-center cursor-pointer" @click="$updateTeamFolder(singleFile)">
<ListInfoItem v-if="singleFile.data.attributes.isTeamFolder" :title="$t('Shared with the Team')">
<div class="flex cursor-pointer items-center" @click="$updateTeamFolder(singleFile)">
<TeamMembersPreview :folder="singleFile" :avatar-size="32" />
<Edit2Icon size="10" class="ml-2" />
</div>
</ListInfoItem>
<!--Shared-->
<ListInfoItem
v-if="$checkPermission('master') && singleFile.data.relationships.shared"
:title="$t('file_detail.shared')"
>
<div @click="$shareFileOrFolder(singleFile)" class="flex items-center cursor-pointer mb-2">
<span class="inline-block font-bold text-sm">
<ListInfoItem v-if="$checkPermission('master') && singleFile.data.relationships.shared" :title="$t('file_detail.shared')">
<div @click="$shareFileOrFolder(singleFile)" class="mb-2 flex cursor-pointer items-center">
<span class="inline-block text-sm font-bold">
{{ sharedInfo }}
</span>
<Edit2Icon size="10" class="ml-2" />
</div>
<div class="flex items-center w-full">
<div class="flex w-full items-center">
<lock-icon v-if="isLocked" @click="$shareFileOrFolder(singleFile)" size="17" class="hover-text-theme vue-feather cursor-pointer" />
<unlock-icon v-if="!isLocked" @click="$shareFileOrFolder(singleFile)" size="17" class="hover-text-theme vue-feather cursor-pointer" />
<CopyShareLink :item="singleFile" size="small" class="w-full pl-2.5" />
@@ -86,23 +65,17 @@
</ListInfoItem>
<!--Author-->
<ListInfoItem
v-if="canShowAuthor"
:title="$t('Author')"
>
<div class="flex items-center mt-1.5">
<ListInfoItem v-if="canShowAuthor" :title="$t('Author')">
<div class="mt-1.5 flex items-center">
<MemberAvatar :size="32" :member="singleFile.data.relationships.owner" />
<span class="ml-3 block font-bold font-sm">
<span class="font-sm ml-3 block font-bold">
{{ singleFile.data.relationships.owner.data.attributes.name }}
</span>
</div>
</ListInfoItem>
<!--Metadata-->
<ListInfoItem
v-if="canShowMetaData"
:title="$t('file_detail_meta.meta_data')"
>
<ListInfoItem v-if="canShowMetaData" :title="$t('file_detail_meta.meta_data')">
<ImageMetaData />
</ListInfoItem>
</div>
@@ -110,14 +83,14 @@
</template>
<script>
import FilePreviewDetail from "../Others/FilePreviewDetail";
import CopyShareLink from "../Others/Forms/CopyShareLink";
import FilePreviewDetail from '../Others/FilePreviewDetail'
import CopyShareLink from '../Others/Forms/CopyShareLink'
import { Edit2Icon, LockIcon, UnlockIcon, EyeOffIcon } from 'vue-feather-icons'
import ImageMetaData from "./ImageMetaData";
import TitlePreview from "./TitlePreview";
import TeamMembersPreview from "../Teams/Components/TeamMembersPreview"
import ListInfoItem from "../Others/ListInfoItem";
import MemberAvatar from "./MemberAvatar"
import ImageMetaData from './ImageMetaData'
import TitlePreview from './TitlePreview'
import TeamMembersPreview from '../Teams/Components/TeamMembersPreview'
import ListInfoItem from '../Others/ListInfoItem'
import MemberAvatar from './MemberAvatar'
import { mapGetters } from 'vuex'
export default {
@@ -136,11 +109,7 @@
LockIcon,
},
computed: {
...mapGetters([
'permissionOptions',
'clipboard',
'user',
]),
...mapGetters(['permissionOptions', 'clipboard', 'user']),
isEmpty() {
return this.clipboard.length === 0
},
@@ -157,16 +126,18 @@
return this.clipboard[0].data.relationships.shared.protected
},
sharedInfo() {
let title = this.permissionOptions.find(option => {
let title = this.permissionOptions.find((option) => {
return option.value === this.clipboard[0].data.relationships.shared.permission
})
return title ? this.$t(title.label) : this.$t('shared.can_download')
},
canShowAuthor() {
return this.$isThisRoute(this.$route, ['SharedWithMe', 'TeamFolders'])
&& this.clipboard[0].data.type !== 'folder'
&& this.user.data.id !== this.clipboard[0].data.relationships.owner.data.id
return (
this.$isThisRoute(this.$route, ['SharedWithMe', 'TeamFolders']) &&
this.clipboard[0].data.type !== 'folder' &&
this.user.data.id !== this.clipboard[0].data.relationships.owner.data.id
)
},
},
}

View File

@@ -1,91 +1,89 @@
<template>
<div
:class="{'dark:bg-dark-foreground bg-light-background': isClicked}"
class="flex flex-wrap items-center justify-center relative z-0 text-center lg:h-60 sm:h-56 h-48 px-1 pt-2 rounded-lg select-none border-2 border-transparent border-dashed dark:hover:bg-dark-foreground lg:hover:bg-light-background"
:class="{ 'bg-light-background dark:bg-dark-foreground': isClicked }"
class="relative z-0 flex h-48 select-none flex-wrap items-center justify-center rounded-lg border-2 border-dashed border-transparent px-1 pt-2 text-center dark:hover:bg-dark-foreground sm:h-56 lg:h-60 lg:hover:bg-light-background"
:draggable="canDrag"
spellcheck="false"
>
<!--MultiSelecting for the mobile version-->
<CheckBox v-if="isMultiSelectMode" :is-clicked="isClicked" class="mr-5" />
<div class="w-full">
<!--Item thumbnail-->
<div class="relative mx-auto">
<!--Emoji Icon-->
<Emoji
v-if="entry.data.attributes.emoji"
:emoji="entry.data.attributes.emoji"
class="text-5xl transform scale-150 inline-block mb-10"
/>
<Emoji v-if="entry.data.attributes.emoji" :emoji="entry.data.attributes.emoji" class="mb-10 inline-block scale-150 transform text-5xl" />
<!--Folder Icon-->
<FolderIcon v-if="isFolder && !entry.data.attributes.emoji" :item="entry" class="inline-block transform scale-150 lg:mt-2 lg:mb-8 mt-3 mb-5" />
<FolderIcon v-if="isFolder && !entry.data.attributes.emoji" :item="entry" class="mt-3 mb-5 inline-block scale-150 transform lg:mt-2 lg:mb-8" />
<!--File Icon-->
<div v-if="isFile || isVideo || isAudio || (isImage && !entry.data.attributes.thumbnail)" class="relative w-24 mx-auto">
<div v-if="isFile || isVideo || isAudio || (isImage && !entry.data.attributes.thumbnail)" class="relative mx-auto w-24">
<!--Member thumbnail for team folders-->
<MemberAvatar
v-if="user && canShowAuthor"
:size="38"
:is-border="true"
:member="entry.data.relationships.owner"
class="absolute lg:-bottom-7 right-2 -bottom-5 z-10 transform lg:scale-100 scale-75 z-10"
class="absolute right-2 -bottom-5 z-10 z-10 scale-75 transform lg:-bottom-7 lg:scale-100"
/>
<FileIconThumbnail :entry="entry" class="transform lg:scale-150 scale-125 lg:mb-12 lg:mt-6 mt-5 mb-10 z-0" />
<FileIconThumbnail :entry="entry" class="z-0 mt-5 mb-10 scale-125 transform lg:mb-12 lg:mt-6 lg:scale-150" />
</div>
<!--Image thumbnail-->
<div v-if="isImage && entry.data.attributes.thumbnail" class="relative inline-block lg:w-36 lg:h-28 w-28 h-24 mb-4">
<div v-if="isImage && entry.data.attributes.thumbnail" class="relative mb-4 inline-block h-24 w-28 lg:h-28 lg:w-36">
<!--Member thumbnail for team folders-->
<MemberAvatar
v-if="user && canShowAuthor"
:size="38"
:is-border="true"
:member="entry.data.relationships.owner"
class="absolute -right-3 -bottom-2.5 transform lg:scale-100 scale-75 z-10"
class="absolute -right-3 -bottom-2.5 z-10 scale-75 transform lg:scale-100"
/>
<img class="object-cover w-full h-full rounded-lg shadow-lg" :src="entry.data.attributes.thumbnail.sm" :alt="entry.data.attributes.name" loading="lazy" />
<img class="h-full w-full rounded-lg object-cover shadow-lg" :src="entry.data.attributes.thumbnail.sm" :alt="entry.data.attributes.name" loading="lazy" />
</div>
</div>
<!--Item Info-->
<div class="text-center">
<!--Item Title-->
<b class="inline-block leading-3 text-sm hover:underline w-full text-ellipsis overflow-hidden whitespace-nowrap md:px-6 tracking-tigh" ref="name" @input="renameItem" @keydown.delete.stop @click.stop :contenteditable="canEditName">
<b
class="tracking-tigh inline-block w-full overflow-hidden text-ellipsis whitespace-nowrap text-sm leading-3 hover:underline md:px-6"
ref="name"
@input="renameItem"
@keydown.delete.stop
@click.stop
:contenteditable="canEditName"
>
{{ itemName }}
</b>
<!--Item sub line-->
<div class="flex items-center justify-center">
<!--Shared Icon-->
<div v-if="$checkPermission('master') && entry.data.relationships.shared">
<link-icon size="12" class="mr-1.5 text-theme dark-text-theme vue-feather"/>
<link-icon size="12" class="text-theme dark-text-theme vue-feather mr-1.5" />
</div>
<!--File & Image sub line-->
<small v-if="!isFolder" class="block text-xs text-gray-500">
{{ entry.data.attributes.filesize }}<span class="lg:inline-block hidden text-xs text-gray-500">, {{ timeStamp }}</span>
{{ entry.data.attributes.filesize }}<span class="hidden text-xs text-gray-500 lg:inline-block">, {{ timeStamp }}</span>
</small>
<!--Folder sub line-->
<small v-if="isFolder" class="block text-xs text-gray-500">
{{ folderItems === 0 ? $t('folder.empty') : $tc('folder.item_counts', folderItems) }}<span class="lg:inline-block hidden text-xs text-gray-500">, {{ timeStamp }}</span>
{{ folderItems === 0 ? $t('folder.empty') : $tc('folder.item_counts', folderItems)
}}<span class="hidden text-xs text-gray-500 lg:inline-block">, {{ timeStamp }}</span>
</small>
</div>
</div>
<!-- Mobile item action button-->
<div v-if="mobileHandler && ! isMultiSelectMode && $isMobile()" class="flex items-center justify-center py-0.5 px-2 relative">
<div @mouseup.stop="$openInDetailPanel(entry)" class="p-2.5 sm:block hidden">
<eye-icon size="18" class="vue-feather opacity-30 inline-block" />
<div v-if="mobileHandler && !isMultiSelectMode && $isMobile()" class="relative flex items-center justify-center py-0.5 px-2">
<div @mouseup.stop="$openInDetailPanel(entry)" class="hidden p-2.5 sm:block">
<eye-icon size="18" class="vue-feather inline-block opacity-30" />
</div>
<div @mouseup.stop="showItemActions" class="p-2.5">
@@ -97,15 +95,15 @@
</template>
<script>
import FolderIcon from "./FolderIcon";
import FolderIcon from './FolderIcon'
import { LinkIcon, MoreHorizontalIcon, EyeIcon } from 'vue-feather-icons'
import FileIconThumbnail from "./FileIconThumbnail"
import MemberAvatar from "./MemberAvatar"
import Emoji from "../Others/Emoji"
import CheckBox from "./CheckBox"
import {debounce} from "lodash"
import {mapGetters} from "vuex"
import {events} from "../../bus"
import FileIconThumbnail from './FileIconThumbnail'
import MemberAvatar from './MemberAvatar'
import Emoji from '../Others/Emoji'
import CheckBox from './CheckBox'
import { debounce } from 'lodash'
import { mapGetters } from 'vuex'
import { events } from '../../bus'
export default {
name: 'ItemList',
@@ -119,10 +117,7 @@
EyeIcon,
Emoji,
},
props: [
'mobileHandler',
'entry',
],
props: ['mobileHandler', 'entry'],
data() {
return {
mobileMultiSelect: false,
@@ -130,13 +125,9 @@
}
},
computed: {
...mapGetters([
'isMultiSelectMode',
'clipboard',
'user',
]),
...mapGetters(['isMultiSelectMode', 'clipboard', 'user']),
isClicked() {
return this.clipboard.some(element => element.data.id === this.entry.data.id)
return this.clipboard.some((element) => element.data.id === this.entry.data.id)
},
isAudio() {
return this.entry.data.type === 'audio'
@@ -155,24 +146,24 @@
},
timeStamp() {
return this.entry.data.attributes.deleted_at
? this.$t('entry_thumbnail.deleted_at', {time: this.entry.data.attributes.deleted_at})
? this.$t('entry_thumbnail.deleted_at', {
time: this.entry.data.attributes.deleted_at,
})
: this.entry.data.attributes.created_at
},
canEditName() {
return !this.$isMobile()
&& !this.$isThisRoute(this.$route, ['Trash'])
&& !this.$checkPermission('visitor')
&& !(this.sharedDetail && this.sharedDetail.attributes.type === 'file')
return (
!this.$isMobile() &&
!this.$isThisRoute(this.$route, ['Trash']) &&
!this.$checkPermission('visitor') &&
!(this.sharedDetail && this.sharedDetail.attributes.type === 'file')
)
},
folderItems() {
return this.entry.data.attributes.deleted_at
? this.entry.data.attributes.trashed_items
: this.entry.data.attributes.items
return this.entry.data.attributes.deleted_at ? this.entry.data.attributes.trashed_items : this.entry.data.attributes.items
},
canShowAuthor() {
return this.$isThisRoute(this.$route, ['SharedWithMe', 'TeamFolders'])
&& !this.isFolder
&& this.user.data.id !== this.entry.data.relationships.owner.data.id
return this.$isThisRoute(this.$route, ['SharedWithMe', 'TeamFolders']) && !this.isFolder && this.user.data.id !== this.entry.data.relationships.owner.data.id
},
canDrag() {
return !this.isDeleted && this.$checkPermission(['master', 'editor'])
@@ -187,35 +178,32 @@
events.$emit('mobile-context-menu:show', this.entry)
},
renameItem: debounce(function (e) {
// Prevent submit empty string
if (e.target.innerText.trim() === '') return
this.$store.dispatch('renameItem', {
id: this.entry.data.id,
type: this.entry.data.type,
name: e.target.innerText
name: e.target.innerText,
})
}, 300)
}, 300),
},
created() {
// Set item name to own component variable
this.itemName = this.entry.data.attributes.name
// Change item name
events.$on('change:name', item => {
events.$on('change:name', (item) => {
if (this.entry.data.id === item.id) this.itemName = item.name
})
// Autofocus after newly created folder
events.$on('newFolder:focus', id => {
events.$on('newFolder:focus', (id) => {
if (!this.$isMobile() && this.entry.data.id === id) {
this.$refs.name.focus()
document.execCommand('selectAll')
}
})
}
},
}
</script>

View File

@@ -29,29 +29,20 @@
</template>
<script>
import {events} from "../../bus";
import { events } from '../../bus'
import ItemList from './ItemList'
import ItemGrid from './ItemGrid'
import { mapGetters } from 'vuex'
export default {
name: 'ItemHandler',
props: [
'disableHighlight',
'item',
],
props: ['disableHighlight', 'item'],
components: {
ItemList,
ItemGrid,
},
computed: {
...mapGetters([
'isMultiSelectMode',
'itemViewType',
'clipboard',
'entries',
'user',
]),
...mapGetters(['isMultiSelectMode', 'itemViewType', 'clipboard', 'entries', 'user']),
isFolder() {
return this.item.data.type === 'folder'
},
@@ -78,12 +69,11 @@ export default {
delay: 220,
clicks: 0,
timer: null
timer: null,
}
},
methods: {
clickFilter(e) {
// Handle click for mobile device
if (this.$isMobile()) {
this.clickedItem(e)
@@ -99,13 +89,12 @@ export default {
this.timer = setTimeout(() => {
this.clickedItem(e)
self.clicks = 0
}, this.delay);
}, this.delay)
} else {
clearTimeout(this.timer);
clearTimeout(this.timer)
this.goToItem(e)
this.clicks = 0;
this.clicks = 0
}
}
},
@@ -126,22 +115,19 @@ export default {
if (e.button === 2) return
if (!this.$isMobile()) {
// After click deselect new folder rename input
if (document.getSelection().toString().length) {
document.getSelection().removeAllRanges();
document.getSelection().removeAllRanges()
}
if ((e.ctrlKey || e.metaKey) && !e.shiftKey) {
// Click + Ctrl
if (this.clipboard.some(item => item.data.id === this.item.data.id)) {
if (this.clipboard.some((item) => item.data.id === this.item.data.id)) {
this.$store.commit('REMOVE_ITEM_FROM_CLIPBOARD', this.item)
} else {
this.$store.commit('ADD_ITEM_TO_CLIPBOARD', this.item)
}
} else if (e.shiftKey) {
// Click + Shift
let lastItem = this.entries.indexOf(this.clipboard[this.clipboard.length - 1])
let clickedItem = this.entries.indexOf(this.item)
@@ -163,7 +149,6 @@ export default {
}
}
} else {
// Click
this.$store.commit('CLIPBOARD_CLEAR')
this.$store.commit('ADD_ITEM_TO_CLIPBOARD', this.item)
@@ -171,13 +156,10 @@ export default {
}
if (!this.isMultiSelectMode && this.$isMobile()) {
if (this.isFolder) {
this.$goToFileView(this.item.data.id)
} else {
if (this.isImage || this.isVideo || this.isAudio || this.isPdf) {
this.$store.commit('CLIPBOARD_CLEAR')
this.$store.commit('ADD_ITEM_TO_CLIPBOARD', this.item)
@@ -187,7 +169,7 @@ export default {
}
if (this.isMultiSelectMode && this.$isMobile()) {
if (this.clipboard.some(item => item.data.id === this.item.data.id)) {
if (this.clipboard.some((item) => item.data.id === this.item.data.id)) {
this.$store.commit('REMOVE_ITEM_FROM_CLIPBOARD', this.item)
} else {
this.$store.commit('ADD_ITEM_TO_CLIPBOARD', this.item)
@@ -200,16 +182,13 @@ export default {
this.$store.commit('ADD_ITEM_TO_CLIPBOARD', this.item)
events.$emit('file-preview:show')
} else if (this.isFile || !this.isFolder && !this.isVideo && !this.isAudio && !this.isImage) {
} else if (this.isFile || (!this.isFolder && !this.isVideo && !this.isAudio && !this.isImage)) {
this.$downloadFile(this.item.data.attributes.file_url, this.item.data.attributes.name + '.' + this.item.data.attributes.mimetype)
} else if (this.isFolder) {
this.$goToFileView(this.item.data.id)
}
},
}
},
}
</script>
@@ -217,7 +196,6 @@ export default {
@import '../../../sass/vuefilemanager/variables';
@import '../../../sass/vuefilemanager/mixins';
.slide-from-left-move {
transition: transform 300s ease;
}
@@ -357,7 +335,7 @@ export default {
height: 52px;
/deep/ .folder-icon {
@include font-size(52)
@include font-size(52);
}
}
@@ -430,7 +408,6 @@ export default {
}
.dark {
.file-wrapper {
.icon-item {
.file-icon {
@@ -446,15 +423,13 @@ export default {
background: $dark_mode_background !important;
.file-icon {
path {
fill: $dark_mode_foreground !important;
stroke: #2F3C54;
stroke: #2f3c54;
}
}
.item-name {
.name {
color: $dark_mode_text_primary !important;
}

View File

@@ -1,27 +1,23 @@
<template>
<div :class="{'dark:bg-dark-foreground bg-light-background': isClicked && highlight, 'dark:hover:bg-dark-foreground hover:bg-light-background': highlight}" class="flex items-center px-2.5 py-2 rounded-xl select-none border-2 border-transparent border-dashed" :draggable="canDrag" spellcheck="false">
<div
:class="{
'bg-light-background dark:bg-dark-foreground': isClicked && highlight,
'hover:bg-light-background dark:hover:bg-dark-foreground': highlight,
}"
class="flex select-none items-center rounded-xl border-2 border-dashed border-transparent px-2.5 py-2"
:draggable="canDrag"
spellcheck="false"
>
<!--MultiSelecting for the mobile version-->
<CheckBox v-if="isMultiSelectMode" v-model="isClicked" :is-clicked="isClicked" class="mr-5" />
<!--Item thumbnail-->
<div class="w-16 relative">
<div class="relative w-16">
<!--Member thumbnail for team folders-->
<MemberAvatar
v-if="user && canShowAuthor"
:size="28"
:is-border="true"
:member="entry.data.relationships.owner"
class="absolute right-1.5 -bottom-2 z-10"
/>
<MemberAvatar v-if="user && canShowAuthor" :size="28" :is-border="true" :member="entry.data.relationships.owner" class="absolute right-1.5 -bottom-2 z-10" />
<!--Emoji Icon-->
<Emoji
v-if="entry.data.attributes.emoji"
:emoji="entry.data.attributes.emoji"
class="text-5xl ml-1 transform scale-110"
/>
<Emoji v-if="entry.data.attributes.emoji" :emoji="entry.data.attributes.emoji" class="ml-1 scale-110 transform text-5xl" />
<!--Folder Icon-->
<FolderIcon v-if="isFolder && !entry.data.attributes.emoji" :item="entry" />
@@ -30,29 +26,39 @@
<FileIconThumbnail v-if="isFile || isVideo || isAudio || (isImage && !entry.data.attributes.thumbnail)" :entry="entry" class="pr-2" />
<!--Image thumbnail-->
<img v-if="isImage && entry.data.attributes.thumbnail" class="w-12 h-12 rounded ml-0.5 object-cover" :src="entry.data.attributes.thumbnail.xs" :alt="entry.data.attributes.name" loading="lazy" />
<img
v-if="isImage && entry.data.attributes.thumbnail"
class="ml-0.5 h-12 w-12 rounded object-cover"
:src="entry.data.attributes.thumbnail.xs"
:alt="entry.data.attributes.name"
loading="lazy"
/>
</div>
<!--Item Info-->
<div class="pl-2">
<!--Item Title-->
<b class="block text-sm mb-0.5 text-ellipsis overflow-hidden hover:underline whitespace-nowrap" style="max-width: 240px" ref="name" @input="renameItem" @keydown.delete.stop @click.stop :contenteditable="canEditName">
<b
class="mb-0.5 block overflow-hidden text-ellipsis whitespace-nowrap text-sm hover:underline"
style="max-width: 240px"
ref="name"
@input="renameItem"
@keydown.delete.stop
@click.stop
:contenteditable="canEditName"
>
{{ itemName }}
</b>
<!--Item sub line-->
<div class="flex items-center">
<!--Shared Icon-->
<div v-if="$checkPermission('master') && entry.data.relationships.shared">
<link-icon size="12" class="mr-1.5 text-theme dark-text-theme vue-feather"/>
<link-icon size="12" class="text-theme dark-text-theme vue-feather mr-1.5" />
</div>
<!--File & Image sub line-->
<small v-if="! isFolder" class="block text-xs text-gray-500">
{{ entry.data.attributes.filesize }}, {{ timeStamp }}
</small>
<small v-if="!isFolder" class="block text-xs text-gray-500"> {{ entry.data.attributes.filesize }}, {{ timeStamp }} </small>
<!--Folder sub line-->
<small v-if="isFolder" class="block text-xs text-gray-500">
@@ -62,11 +68,11 @@
</div>
<!-- Mobile item action button-->
<div v-if="mobileHandler && ! isMultiSelectMode && $isMobile()" class="pr-1 flex-grow text-right relative">
<div @mouseup.stop="$openInDetailPanel(entry)" class="absolute right-10 p-2.5 -mr-4 transform -translate-y-2/4 lg:block hidden">
<eye-icon size="18" class="vue-feather opacity-30 inline-block" />
<div v-if="mobileHandler && !isMultiSelectMode && $isMobile()" class="relative flex-grow pr-1 text-right">
<div @mouseup.stop="$openInDetailPanel(entry)" class="absolute right-10 -mr-4 hidden -translate-y-2/4 transform p-2.5 lg:block">
<eye-icon size="18" class="vue-feather inline-block opacity-30" />
</div>
<div @mouseup.stop="showItemActions" class="absolute right-0 p-2.5 -mr-4 transform -translate-y-2/4">
<div @mouseup.stop="showItemActions" class="absolute right-0 -mr-4 -translate-y-2/4 transform p-2.5">
<MoreVerticalIcon size="18" class="vue-feather text-theme dark-text-theme inline-block" />
</div>
</div>
@@ -74,15 +80,15 @@
</template>
<script>
import Emoji from "../Others/Emoji";
import FolderIcon from "./FolderIcon";
import Emoji from '../Others/Emoji'
import FolderIcon from './FolderIcon'
import { LinkIcon, MoreVerticalIcon, EyeIcon } from 'vue-feather-icons'
import FileIconThumbnail from "./FileIconThumbnail";
import MemberAvatar from "./MemberAvatar";
import CheckBox from "./CheckBox";
import {debounce} from "lodash";
import {mapGetters} from "vuex";
import {events} from "../../bus";
import FileIconThumbnail from './FileIconThumbnail'
import MemberAvatar from './MemberAvatar'
import CheckBox from './CheckBox'
import { debounce } from 'lodash'
import { mapGetters } from 'vuex'
import { events } from '../../bus'
export default {
name: 'ItemList',
@@ -96,11 +102,7 @@
EyeIcon,
Emoji,
},
props: [
'mobileHandler',
'highlight',
'entry',
],
props: ['mobileHandler', 'highlight', 'entry'],
data() {
return {
mobileMultiSelect: false,
@@ -109,13 +111,9 @@
}
},
computed: {
...mapGetters([
'isMultiSelectMode',
'clipboard',
'user',
]),
...mapGetters(['isMultiSelectMode', 'clipboard', 'user']),
isClicked() {
return this.clipboard.some(element => element.data.id === this.entry.data.id)
return this.clipboard.some((element) => element.data.id === this.entry.data.id)
},
isVideo() {
return this.entry.data.type === 'video'
@@ -134,23 +132,24 @@
},
timeStamp() {
return this.entry.data.attributes.deleted_at
? this.$t('item_thumbnail.deleted_at', {time: this.entry.data.attributes.deleted_at})
? this.$t('item_thumbnail.deleted_at', {
time: this.entry.data.attributes.deleted_at,
})
: this.entry.data.attributes.created_at
},
canEditName() {
return !this.$isMobile()
&& !this.$isThisRoute(this.$route, ['Trash'])
&& !this.$checkPermission('visitor')
&& !(this.sharedDetail && this.sharedDetail.attributes.type === 'file')
return (
!this.$isMobile() &&
!this.$isThisRoute(this.$route, ['Trash']) &&
!this.$checkPermission('visitor') &&
!(this.sharedDetail && this.sharedDetail.attributes.type === 'file')
)
},
folderItems() {
return this.entry.data.attributes.deleted_at
? this.entry.data.attributes.trashed_items
: this.entry.data.attributes.items
return this.entry.data.attributes.deleted_at ? this.entry.data.attributes.trashed_items : this.entry.data.attributes.items
},
canShowAuthor() {
return !this.isFolder
&& this.user.data.id !== this.entry.data.relationships.owner.data.id
return !this.isFolder && this.user.data.id !== this.entry.data.relationships.owner.data.id
},
canDrag() {
return !this.isDeleted && this.$checkPermission(['master', 'editor'])
@@ -165,35 +164,32 @@
events.$emit('mobile-context-menu:show', this.entry)
},
renameItem: debounce(function (e) {
// Prevent submit empty string
if (e.target.innerText.trim() === '') return
this.$store.dispatch('renameItem', {
id: this.entry.data.id,
type: this.entry.data.type,
name: e.target.innerText
name: e.target.innerText,
})
}, 300)
}, 300),
},
created() {
// Set item name to own component variable
this.itemName = this.entry.data.attributes.name
// Change item name
events.$on('change:name', item => {
events.$on('change:name', (item) => {
if (this.entry.data.id === item.id) this.itemName = item.name
})
// Autofocus after newly created folder
events.$on('newFolder:focus', id => {
events.$on('newFolder:focus', (id) => {
if (!this.$isMobile() && this.entry.data.id === id) {
this.$refs.name.focus()
document.execCommand('selectAll')
}
})
}
},
}
</script>

View File

@@ -4,35 +4,42 @@
:style="{ width: size + 'px', height: size + 'px' }"
v-if="member.data.attributes.avatar"
:src="avatar"
:class="[borderRadius, {'border-3 border-white dark:border-dark-background': isBorder}]"
:class="[
borderRadius,
{
'border-3 border-white dark:border-dark-background': isBorder,
},
]"
class=""
/>
<div
v-else
class="flex items-center justify-center"
:class="[borderRadius, {'border-3 border-white dark:border-dark-background': isBorder}]"
:style="{width: size + 'px', height: size + 'px', background: member.data.attributes.color ? member.data.attributes.color : '#f4f5f6'}"
:class="[
borderRadius,
{
'border-3 border-white dark:border-dark-background': isBorder,
},
]"
:style="{
width: size + 'px',
height: size + 'px',
background: member.data.attributes.color ? member.data.attributes.color : '#f4f5f6',
}"
>
<span :class="fontSize" class="uppercase font-extrabold text-gray-900">
<span :class="fontSize" class="font-extrabold uppercase text-gray-900">
{{ letter }}
</span>
</div>
</div>
</template>
<script>
export default {
name: 'MemberAvatar',
props: [
'isBorder',
'member',
'size',
],
props: ['isBorder', 'member', 'size'],
computed: {
letter() {
let string = this.member.data.attributes.name
? this.member.data.attributes.name
: this.member.data.attributes.email
let string = this.member.data.attributes.name ? this.member.data.attributes.name : this.member.data.attributes.email
return string.substr(0, 1)
},
@@ -57,6 +64,6 @@
return this.member.data.attributes.avatar.xs
}
},
}
},
}
</script>

View File

@@ -1,5 +1,5 @@
<template>
<button class="inline-block dark:bg-2x-dark-foreground bg-light-background rounded-xl py-2 px-3.5 mr-2">
<button class="mr-2 inline-block rounded-xl bg-light-background py-2 px-3.5 dark:bg-2x-dark-foreground">
<div class="flex items-center">
<hard-drive-icon v-if="icon === 'hard-drive'" size="15" class="vue-feather dark-text-theme" />
<upload-cloud-icon v-if="icon === 'upload-cloud'" size="15" class="vue-feather dark-text-theme" />
@@ -26,7 +26,7 @@
<sorting-icon v-if="icon === 'preview-sorting'" class="vue-feather dark-text-theme preview-sorting" />
<cloud-plus-icon v-if="icon === 'cloud-plus'" class="vue-feather dark-text-theme preview-sorting" />
<span v-if="$slots.default" class="font-bold text-sm ml-2">
<span v-if="$slots.default" class="ml-2 text-sm font-bold">
<slot></slot>
</span>
</div>
@@ -34,15 +34,36 @@
</template>
<script>
import { UserCheckIcon, HardDriveIcon, UploadCloudIcon, LinkIcon, Trash2Icon, UsersIcon, SearchIcon, RefreshCwIcon, DownloadIcon, CopyIcon, FilterIcon, DollarSignIcon, CheckIcon, XSquareIcon, CheckSquareIcon, FolderPlusIcon, ListIcon, GridIcon, TrashIcon, UserPlusIcon, PlusIcon, CreditCardIcon } from 'vue-feather-icons'
import CloudPlusIcon from "./Icons/CloudPlusIcon";
import SortingIcon from "./Icons/SortingIcon";
import {
UserCheckIcon,
HardDriveIcon,
UploadCloudIcon,
LinkIcon,
Trash2Icon,
UsersIcon,
SearchIcon,
RefreshCwIcon,
DownloadIcon,
CopyIcon,
FilterIcon,
DollarSignIcon,
CheckIcon,
XSquareIcon,
CheckSquareIcon,
FolderPlusIcon,
ListIcon,
GridIcon,
TrashIcon,
UserPlusIcon,
PlusIcon,
CreditCardIcon,
} from 'vue-feather-icons'
import CloudPlusIcon from './Icons/CloudPlusIcon'
import SortingIcon from './Icons/SortingIcon'
export default {
name: 'MobileActionButton',
props: [
'icon'
],
props: ['icon'],
components: {
UserCheckIcon,
HardDriveIcon,
@@ -68,6 +89,6 @@
CopyIcon,
ListIcon,
GridIcon,
}
},
}
</script>

View File

@@ -4,14 +4,7 @@
<cloud-plus-icon class="icon dark-text-theme" size="15" />
<label label="file" class="label button file-input button-base">
<slot></slot>
<input
@change="emmitFiles"
v-show="false"
id="file"
type="file"
name="files[]"
multiple
/>
<input @change="emmitFiles" v-show="false" id="file" type="file" name="files[]" multiple />
</label>
</div>
</button>
@@ -19,7 +12,7 @@
<script>
import { UploadCloudIcon } from 'vue-feather-icons'
import CloudPlusIcon from "./Icons/CloudPlusIcon";
import CloudPlusIcon from './Icons/CloudPlusIcon'
export default {
name: 'MobileActionButtonUpload',
@@ -30,8 +23,8 @@
methods: {
emmitFiles(e) {
this.$uploadFiles(e.target.files)
}
}
},
},
}
</script>
@@ -70,7 +63,11 @@
.mobile-action-button {
background: $dark_mode_foreground;
path, line, polyline, rect, circle {
path,
line,
polyline,
rect,
circle {
color: inherit;
}

View File

@@ -17,9 +17,9 @@
</template>
<script>
import MenuMobileGroup from "../Mobile/MenuMobileGroup";
import ThumbnailItem from "../Others/ThumbnailItem";
import MenuMobile from "../Mobile/MenuMobile";
import MenuMobileGroup from '../Mobile/MenuMobileGroup'
import ThumbnailItem from '../Others/ThumbnailItem'
import MenuMobile from '../Mobile/MenuMobile'
import { mapGetters } from 'vuex'
export default {
@@ -30,9 +30,7 @@ export default {
MenuMobile,
},
computed: {
...mapGetters([
'clipboard',
]),
...mapGetters(['clipboard']),
},
}
</script>

View File

@@ -7,9 +7,9 @@
</template>
<script>
import MenuMobileGroup from "../Mobile/MenuMobileGroup";
import MenuMobile from "../Mobile/MenuMobile";
import {events} from "../../bus";
import MenuMobileGroup from '../Mobile/MenuMobileGroup'
import MenuMobile from '../Mobile/MenuMobile'
import { events } from '../../bus'
export default {
name: 'MobileContextMenu',
@@ -21,6 +21,6 @@ export default {
closeMenu() {
events.$emit('mobile-menu:hide')
},
}
},
}
</script>

View File

@@ -11,7 +11,7 @@
</template>
<script>
import ToolbarButton from "./ToolbarButton";
import ToolbarButton from './ToolbarButton'
import { mapGetters } from 'vuex'
export default {
@@ -20,22 +20,19 @@ export default {
ToolbarButton,
},
computed: {
...mapGetters([
'isMultiSelectMode',
'clipboard',
]),
...mapGetters(['isMultiSelectMode', 'clipboard']),
},
methods: {
closeSelecting() {
this.$store.commit('TOGGLE_MULTISELECT_MODE')
},
}
},
}
</script>
<style scoped lang="scss">
@import "resources/sass/vuefilemanager/_variables";
@import "resources/sass/vuefilemanager/_mixins";
@import 'resources/sass/vuefilemanager/_variables';
@import 'resources/sass/vuefilemanager/_mixins';
.multiselect-actions {
display: flex;
@@ -71,7 +68,6 @@ export default {
}
.options {
&.is-active {
opacity: 1 !important;
pointer-events: initial !important;
@@ -80,7 +76,6 @@ export default {
}
.dark {
.multiselect-actions {
background: $dark_mode_foreground;
}
@@ -111,5 +106,4 @@ export default {
.context-menu-leave-active {
position: absolute;
}
</style>

View File

@@ -9,9 +9,9 @@
</template>
<script>
import MenuMobileGroup from "../Mobile/MenuMobileGroup";
import MenuMobileGroup from '../Mobile/MenuMobileGroup'
import TeamFolderPreview from '../Teams/Components/TeamFolderPreview'
import MenuMobile from "../Mobile/MenuMobile";
import MenuMobile from '../Mobile/MenuMobile'
export default {
name: 'MobileTeamContextMenu',

View File

@@ -1,31 +1,43 @@
<template>
<div class="sticky top-0 dark:bg-dark-background bg-white flex text-center py-5 px-4 w-full justify-between items-center z-20 lg:hidden block">
<div class="sticky top-0 z-20 block flex w-full items-center justify-between bg-white py-5 px-4 text-center dark:bg-dark-background lg:hidden">
<!-- Go back-->
<div @click="goBack" class="go-back-button flex text-left items-center">
<chevron-left-icon size="17" :class="{'opacity-0 -translate-x-3': ! isLoadedFolder, 'opacity-100 translate-x-0': isLoadedFolder }" class="transform align-middle cursor-pointer mr-2 -ml-1 transition-all duration-200" />
<div @click="goBack" class="go-back-button flex items-center text-left">
<chevron-left-icon
size="17"
:class="{
'-translate-x-3 opacity-0': !isLoadedFolder,
'translate-x-0 opacity-100': isLoadedFolder,
}"
class="mr-2 -ml-1 transform cursor-pointer align-middle transition-all duration-200"
/>
<!--Folder Title-->
<div :class="{'-translate-x-4': ! isLoadedFolder}" class="transform lg:text-base text-sm align-middle font-bold overflow-hidden text-ellipsis inline-block whitespace-nowrap transition-all duration-200" style="max-width: 200px;">
<div
:class="{ '-translate-x-4': !isLoadedFolder }"
class="inline-block transform overflow-hidden text-ellipsis whitespace-nowrap align-middle text-sm font-bold transition-all duration-200 lg:text-base"
style="max-width: 200px"
>
{{ $getCurrentLocationName() }}
</div>
<span @click.stop="showItemActions" :class="{'-translate-x-4 opacity-0': ! currentFolder, 'translate-x-0 opacity-100': currentFolder}" class="transform py-0.5 px-1.5 ml-3 rounded-md dark:bg-dark-foreground bg-light-background transition-all duration-200">
<span
@click.stop="showItemActions"
:class="{
'-translate-x-4 opacity-0': !currentFolder,
'translate-x-0 opacity-100': currentFolder,
}"
class="ml-3 transform rounded-md bg-light-background py-0.5 px-1.5 transition-all duration-200 dark:bg-dark-foreground"
>
<more-horizontal-icon size="14" />
</span>
</div>
<div class="flex items-center relative">
<TeamMembersButton
v-if="$isThisRoute($route, ['TeamFolders', 'SharedWithMe'])"
size="28"
@click.stop.native="$showMobileMenu('team-menu')"
class="absolute right-9"
/>
<div class="relative flex items-center">
<TeamMembersButton v-if="$isThisRoute($route, ['TeamFolders', 'SharedWithMe'])" size="28" @click.stop.native="$showMobileMenu('team-menu')" class="absolute right-9" />
<!--More Actions-->
<div class="relative">
<div v-if="$checkPermission('master')" @click="showMobileNavigation" class="absolute right-0 p-4 -mr-2 transform -translate-y-2/4">
<div v-if="$checkPermission('master')" @click="showMobileNavigation" class="absolute right-0 -mr-2 -translate-y-2/4 transform p-4">
<menu-icon size="17" />
</div>
</div>
@@ -34,13 +46,13 @@
</template>
<script>
import TeamMembersPreview from "../Teams/Components/TeamMembersPreview";
import TeamMembersButton from "../Teams/Components/TeamMembersButton";
import ToolbarButton from "./ToolbarButton";
import SearchBar from "./SearchBar";
import TeamMembersPreview from '../Teams/Components/TeamMembersPreview'
import TeamMembersButton from '../Teams/Components/TeamMembersButton'
import ToolbarButton from './ToolbarButton'
import SearchBar from './SearchBar'
import { MenuIcon, ChevronLeftIcon, MoreHorizontalIcon } from 'vue-feather-icons'
import { mapGetters } from 'vuex'
import {events} from "../../bus";
import { events } from '../../bus'
export default {
name: 'MobileToolBar',
@@ -54,13 +66,7 @@
MenuIcon,
},
computed: {
...mapGetters([
'currentTeamFolder',
'isVisibleSidebar',
'currentFolder',
'itemViewType',
'clipboard',
]),
...mapGetters(['currentTeamFolder', 'isVisibleSidebar', 'currentFolder', 'itemViewType', 'clipboard']),
isLoadedFolder() {
return this.$route.params.id
},
@@ -85,6 +91,6 @@
events.$on('show:content', () => {
if (this.isSidebarMenu) this.isSidebarMenu = false
})
}
},
}
</script>

View File

@@ -1,5 +1,10 @@
<template>
<li class="2xl:py-4 xl:py-3 lg:py-2.5 py-4 px-5 flex items-center justify-between" :class="{'dark:hover:bg-4x-dark-foreground hover:bg-light-background cursor-pointer group': ! isHoverDisabled}">
<li
class="flex items-center justify-between py-4 px-5 lg:py-2.5 xl:py-3 2xl:py-4"
:class="{
'group cursor-pointer hover:bg-light-background dark:hover:bg-4x-dark-foreground': !isHoverDisabled,
}"
>
<div class="flex items-center">
<div class="mr-4">
<calendar-icon v-if="icon === 'calendar'" size="17" class="vue-feather group-hover-text-theme" :class="{ 'text-theme': isActive }" />
@@ -38,7 +43,7 @@
<box-icon v-if="icon === 'box'" size="17" class="vue-feather group-hover-text-theme" :class="{ 'text-theme': isActive }" />
<folder-plus-icon v-if="icon === 'folder-plus'" size="17" class="vue-feather group-hover-text-theme" :class="{ 'text-theme': isActive }" />
</div>
<b class="font-bold text-sm group-hover-text-theme" :class="{'text-theme': isActive}">
<b class="group-hover-text-theme text-sm font-bold" :class="{ 'text-theme': isActive }">
{{ title }}
</b>
</div>
@@ -51,7 +56,7 @@
</template>
<script>
import AlphabetIcon from "./Icons/AlphabetIcon";
import AlphabetIcon from './Icons/AlphabetIcon'
import {
UserMinusIcon,
UserCheckIcon,
@@ -92,13 +97,7 @@ import {
export default {
name: 'Option',
props:[
'isHoverDisabled',
'isActive',
'title',
'arrow',
'icon'
],
props: ['isHoverDisabled', 'isActive', 'title', 'arrow', 'icon'],
components: {
UserMinusIcon,
UserCheckIcon,
@@ -136,6 +135,6 @@ import {
ListIcon,
UserIcon,
EyeIcon,
}
},
}
</script>

View File

@@ -6,13 +6,12 @@
<script>
export default {
name: 'OptionGroup'
name: 'OptionGroup',
}
</script>
<style scoped lang="scss">
.option-group {
&:first-child {
padding-top: 0 !important;
}

View File

@@ -7,42 +7,20 @@
<div class="text-label group-hover-text-theme">
{{ title }}
<input
v-if="type === 'file'"
@change="emmitFiles"
v-show="false"
id="file"
type="file"
name="files[]"
multiple
/>
<input v-if="type === 'file'" @change="emmitFiles" v-show="false" id="file" type="file" name="files[]" multiple />
<input
v-if="type === 'folder'"
@change="emmitFiles"
v-show="false"
id="folder"
type="file"
name="folders[]"
webkitdirectory
mozdirectory
/>
<input v-if="type === 'folder'" @change="emmitFiles" v-show="false" id="folder" type="file" name="folders[]" webkitdirectory mozdirectory />
</div>
</label>
</template>
<script>
import FolderUploadIcon from "./Icons/FolderUploadIcon";
import {
UploadCloudIcon,
} from 'vue-feather-icons'
import FolderUploadIcon from './Icons/FolderUploadIcon'
import { UploadCloudIcon } from 'vue-feather-icons'
export default {
name: 'Option',
props:[
'title',
'type',
],
props: ['title', 'type'],
components: {
FolderUploadIcon,
UploadCloudIcon,
@@ -50,14 +28,14 @@ import {
methods: {
emmitFiles(e) {
this.$uploadFiles(e.target.files)
}
}
},
},
}
</script>
<style scoped lang="scss">
@import "resources/sass/vuefilemanager/_variables";
@import "resources/sass/vuefilemanager/_mixins";
@import 'resources/sass/vuefilemanager/_variables';
@import 'resources/sass/vuefilemanager/_mixins';
.menu-option {
white-space: nowrap;
@@ -94,7 +72,6 @@ import {
}
.dark {
.menu-option {
color: $dark_mode_text_primary;
@@ -103,5 +80,4 @@ import {
}
}
}
</style>

View File

@@ -15,19 +15,17 @@
</template>
<script>
import Spinner from "./Spinner";
import Spinner from './Spinner'
import { mapGetters } from 'vuex'
export default {
name: 'ProcessingPopup',
components: {
Spinner
Spinner,
},
computed: {
...mapGetters([
'processingPopup'
])
}
...mapGetters(['processingPopup']),
},
}
</script>
@@ -68,7 +66,6 @@ export default {
}
.popup-content {
.title {
@include font-size(22);
font-weight: 700;
@@ -90,7 +87,6 @@ export default {
}
.popup-content {
.title {
@include font-size(19);
}

View File

@@ -7,7 +7,7 @@
<script>
export default {
name: 'ProgressBar',
props: ['progress']
props: ['progress'],
}
</script>
@@ -31,14 +31,12 @@ export default {
}
.dark {
.progress-bar {
background: $dark_mode_foreground;
}
}
@media only screen and (min-width: 680px) {
.dark .progress-bar {
background: $dark_mode_foreground;
}

View File

@@ -1,13 +1,13 @@
<template>
<div @click="$openSpotlight()" class="relative dark:bg-dark-foreground bg-light-background rounded-lg cursor-pointer">
<div class="flex justify-between items-center px-5 py-2.5 xl:w-72 w-56 text-left">
<div @click="$openSpotlight()" class="relative cursor-pointer rounded-lg bg-light-background dark:bg-dark-foreground">
<div class="flex w-56 items-center justify-between px-5 py-2.5 text-left xl:w-72">
<div class="flex items-center">
<search-icon size="18" class="vue-feather dark:text-gray-600 text-gray-400" />
<span class="font-bold xl:text-sm text-xs dark:text-gray-600 text-gray-400 pl-2.5">
<search-icon size="18" class="vue-feather text-gray-400 dark:text-gray-600" />
<span class="pl-2.5 text-xs font-bold text-gray-400 dark:text-gray-600 xl:text-sm">
{{ $t('inputs.placeholder_search_files') }}
</span>
</div>
<span class="font-bold xl:text-sm text-xs dark:text-gray-600 text-gray-400 dark:border-opacity-5 border rounded px-1 py-0.5 tracking-normal">
<span class="rounded border px-1 py-0.5 text-xs font-bold tracking-normal text-gray-400 dark:border-opacity-5 dark:text-gray-600 xl:text-sm">
{{ metaKeyIcon }}+K
</span>
</div>

View File

@@ -6,7 +6,7 @@
<script>
export default {
name: 'Spinner'
name: 'Spinner',
}
</script>

View File

@@ -8,10 +8,10 @@
<file-icon v-if="icon === 'file'" class="text-theme vue-feather" size="19" />
</div>
<div>
<b class="font-bold text-base inline-block whitespace-nowrap 2xl:w-72 w-52 text-ellipsis overflow-hidden leading-3">
<b class="inline-block w-52 overflow-hidden text-ellipsis whitespace-nowrap text-base font-bold leading-3 2xl:w-72">
{{ title }}
</b>
<small class="font-bold text-xs text-gray-400 block">
<small class="block text-xs font-bold text-gray-400">
{{ subtitle }}
</small>
</div>
@@ -19,21 +19,11 @@
</template>
<script>
import {
CheckSquareIcon,
FolderIcon,
ImageIcon,
VideoIcon,
FileIcon,
} from "vue-feather-icons"
import { CheckSquareIcon, FolderIcon, ImageIcon, VideoIcon, FileIcon } from 'vue-feather-icons'
export default {
name: 'TitlePreview',
props: [
'subtitle',
'title',
'icon',
],
props: ['subtitle', 'title', 'icon'],
components: {
CheckSquareIcon,
FolderIcon,

View File

@@ -22,8 +22,8 @@
</template>
<script>
import SortingIcon from "./Icons/SortingIcon";
import CloudPlusIcon from "./Icons/CloudPlusIcon";
import SortingIcon from './Icons/SortingIcon'
import CloudPlusIcon from './Icons/CloudPlusIcon'
import {
SearchIcon,
UserPlusIcon,
@@ -41,14 +41,11 @@
InfoIcon,
LinkIcon,
XIcon,
} from "vue-feather-icons";
} from 'vue-feather-icons'
export default {
name: "ToolbarButton",
props: [
'source',
'action'
],
name: 'ToolbarButton',
props: ['source', 'action'],
components: {
SearchIcon,
CloudPlusIcon,
@@ -69,12 +66,12 @@
LinkIcon,
XIcon,
},
};
}
</script>
<style scoped lang="scss">
@import "resources/sass/vuefilemanager/_variables";
@import "resources/sass/vuefilemanager/_mixins";
@import 'resources/sass/vuefilemanager/_variables';
@import 'resources/sass/vuefilemanager/_mixins';
.preview-sorting {
transform: scale(1.3);
@@ -99,7 +96,11 @@
svg {
color: inherit;
path, line, polyline, rect, circle {
path,
line,
polyline,
rect,
circle {
color: inherit;
}
}

View File

@@ -2,7 +2,6 @@
<transition name="info-panel">
<div v-if="fileQueue.length > 0" class="upload-progress">
<div class="progress-title">
<!--Is processing-->
<span v-if="isProcessingFile" class="flex items-center justify-center">
<refresh-cw-icon size="12" class="sync-alt text-theme" />
@@ -11,7 +10,13 @@
<!--Multi file upload-->
<span v-if="!isProcessingFile && fileQueue.length > 0">
{{ $t('uploading.progress', {current:filesInQueueUploaded, total: filesInQueueTotal, progress: uploadingProgress}) }}
{{
$t('uploading.progress', {
current: filesInQueueUploaded,
total: filesInQueueTotal,
progress: uploadingProgress,
})
}}
</span>
</div>
<div class="progress-wrapper">
@@ -25,10 +30,10 @@
</template>
<script>
import ProgressBar from "./ProgressBar";
import ProgressBar from './ProgressBar'
import { RefreshCwIcon, XIcon } from 'vue-feather-icons'
import { mapGetters } from 'vuex'
import {events} from "../../bus";
import { events } from '../../bus'
export default {
name: 'UploadProgress',
@@ -38,19 +43,13 @@
XIcon,
},
computed: {
...mapGetters([
'filesInQueueUploaded',
'filesInQueueTotal',
'uploadingProgress',
'isProcessingFile',
'fileQueue',
])
...mapGetters(['filesInQueueUploaded', 'filesInQueueTotal', 'uploadingProgress', 'isProcessingFile', 'fileQueue']),
},
methods: {
cancelUpload() {
events.$emit('cancel-upload')
}
}
},
},
}
</script>
@@ -62,7 +61,8 @@
animation: spin 1s linear infinite;
margin-right: 5px;
polyline, path {
polyline,
path {
color: inherit;
}
}
@@ -100,7 +100,6 @@
padding: 0 7px 0 13px;
&:hover {
line {
color: inherit;
}

View File

@@ -10,7 +10,7 @@
<script>
export default {
name: 'IndexPageTile',
props: ['title', 'description', 'type']
props: ['title', 'description', 'type'],
}
</script>
@@ -61,7 +61,6 @@
@media only screen and (max-width: 960px) {
.page-title {
.title {
max-width: 100%;
font-size: 32px;

View File

@@ -7,7 +7,9 @@
<hard-drive-icon class="text-theme" size="26" />
</div>
<h1 class="title">{{ plan.data.attributes.name }}</h1>
<h2 class="description">{{ plan.data.attributes.description }}</h2>
<h2 class="description">
{{ plan.data.attributes.description }}
</h2>
</header>
<section class="plan-features">
<b class="storage-size">{{ plan.data.attributes.capacity_formatted }}</b>
@@ -25,7 +27,7 @@
</template>
<script>
import {HardDriveIcon} from "vue-feather-icons"
import { HardDriveIcon } from 'vue-feather-icons'
import axios from 'axios'
export default {
@@ -39,12 +41,11 @@
}
},
created() {
axios.get('/api/pricing')
.then(response => {
axios.get('/api/pricing').then((response) => {
this.plans = response.data
this.$emit('load', response.data)
})
}
},
}
</script>
@@ -69,10 +70,12 @@
}
.plan-header {
.icon {
path, line, polyline, rect, circle {
path,
line,
polyline,
rect,
circle {
color: inherit;
}
}
@@ -106,7 +109,6 @@
}
.plan-footer {
.sign-in-button {
width: 100%;
text-align: center;
@@ -136,21 +138,19 @@
}
@media only screen and (max-width: 960px) {
.plans-wrapper {
display: block;
margin: 0;
.plan {
padding: 30px 25px;
border-bottom: 1px solid #F7F7F7;
border-bottom: 1px solid #f7f7f7;
border-right: none;
}
}
}
.dark {
.plans-wrapper {
background: $dark_mode_foreground;
}
@@ -163,7 +163,6 @@
}
.plan-header {
.title {
color: $dark_mode_text_primary;
}
@@ -174,7 +173,6 @@
}
.plan-features {
.storage-size {
color: $dark_mode_text_primary;
}
@@ -185,7 +183,6 @@
}
.plan-footer {
.sign-in-button {
background: rgba($theme, 0.1);
@@ -195,7 +192,6 @@
}
.price {
.vat-disclaimer {
color: $dark_mode_text_primary;
}

View File

@@ -1,12 +1,6 @@
<template>
<div class="page-wrapper large get-started" v-if="index.section_get_started === '1'">
<PageTitle
class="page-title"
type="center"
:title="index.get_started_title"
:description="index.get_started_description"
></PageTitle>
<PageTitle class="page-title" type="center" :title="index.get_started_title" :description="index.get_started_description"></PageTitle>
<router-link tag="button" class="get-started-button bg-theme-800 hover-bg-theme shadow-theme" :to="{ name: 'SignUp' }">
<span class="content">{{ $t('page_index.get_started_button') }}</span>
@@ -41,7 +35,7 @@
</template>
<script>
import PageTitle from "./Components/PageTitle";
import PageTitle from './Components/PageTitle'
import {
ChevronRightIcon,
UploadCloudIcon,
@@ -93,7 +87,6 @@
@import '../../../sass/vuefilemanager/mixins';
.icons {
.icon {
position: absolute;
@@ -102,7 +95,8 @@
left: 37%;
transform: rotate(0deg);
circle, line {
circle,
line {
stroke: $yellow;
}
}
@@ -112,7 +106,8 @@
left: 23.5%;
transform: rotate(-20deg);
path, line {
path,
line {
stroke: $purple;
}
}
@@ -144,7 +139,9 @@
left: 17%;
transform: rotate(0deg);
polyline, line, path {
polyline,
line,
path {
stroke: $red;
}
}
@@ -170,7 +167,8 @@
left: 32%;
transform: rotate(13deg);
line, path {
line,
path {
stroke: $yellow;
}
}
@@ -180,7 +178,8 @@
right: 49%;
transform: rotate(-11deg);
line, path {
line,
path {
stroke: $theme;
}
}
@@ -190,7 +189,8 @@
right: 45%;
transform: rotate(0);
circle, path {
circle,
path {
stroke: $red;
}
}
@@ -212,7 +212,9 @@
right: 15.5%;
transform: rotate(21deg);
polyline, path, line {
polyline,
path,
line {
stroke: $red;
}
}
@@ -260,11 +262,12 @@
right: 44%;
transform: rotate(-12deg);
polyline, line, path {
polyline,
line,
path {
stroke: $red;
}
}
}
}

View File

@@ -1,7 +1,7 @@
<template>
<div class="page-wrapper large hero-screenshot">
<img class="hero-light" src="/assets/images/vuefilemanager-screenshot-light.png" :alt="config.app_name">
<img class="hero-dark" src="/assets/images/vuefilemanager-screenshot-dark.png" :alt="config.app_name">
<img class="hero-light" src="/assets/images/vuefilemanager-screenshot-light.png" :alt="config.app_name" />
<img class="hero-dark" src="/assets/images/vuefilemanager-screenshot-dark.png" :alt="config.app_name" />
<div class="icons">
<link-icon size="20" class="icon"></link-icon>
@@ -23,18 +23,7 @@
<script>
import { mapGetters } from 'vuex'
import {
FolderPlusIcon,
HardDriveIcon,
SettingsIcon,
Trash2Icon,
SearchIcon,
ImageIcon,
GridIcon,
LinkIcon,
StarIcon,
EyeIcon,
} from 'vue-feather-icons'
import { FolderPlusIcon, HardDriveIcon, SettingsIcon, Trash2Icon, SearchIcon, ImageIcon, GridIcon, LinkIcon, StarIcon, EyeIcon } from 'vue-feather-icons'
export default {
name: 'IndexHeroScreenshot',
@@ -62,7 +51,6 @@
@import '../../../sass/vuefilemanager/mixins';
.icons {
.icon {
z-index: 0;
position: absolute;
@@ -83,7 +71,8 @@
right: 28.5%;
transform: rotate(-12deg);
line, path {
line,
path {
stroke: $theme;
}
}
@@ -93,7 +82,8 @@
right: 41.5%;
transform: rotate(13deg);
path, line {
path,
line {
stroke: $yellow;
}
}
@@ -102,7 +92,8 @@
top: -16%;
right: 26%;
circle, path {
circle,
path {
stroke: $red;
}
}
@@ -126,7 +117,9 @@
right: 2.5%;
transform: rotate(22deg);
polyline, path, line {
polyline,
path,
line {
stroke: $red;
}
}
@@ -135,7 +128,8 @@
top: 14%;
right: 11%;
circle, line {
circle,
line {
stroke: $purple;
}
}
@@ -182,7 +176,7 @@
img {
border-radius: 8px;
width: 80%;
box-shadow: 0 7px 255px rgba(#19363C, 0.1);
box-shadow: 0 7px 255px rgba(#19363c, 0.1);
&.hero-dark {
display: none;
@@ -191,7 +185,6 @@
}
@media only screen and (max-width: 890px) {
.icons {
display: none;
}
@@ -207,11 +200,8 @@
}
.dark {
.hero-screenshot {
img {
&.hero-light {
display: none;
}

View File

@@ -1,14 +1,9 @@
<template>
<section class="main-features page-wrapper medium">
<PageTitle
v-if="index.section_features === '1'"
type="center"
:title="index.features_title"
:description="index.features_description"
></PageTitle>
<PageTitle v-if="index.section_features === '1'" type="center" :title="index.features_title" :description="index.features_description"></PageTitle>
<div v-if="index.section_feature_boxes === '1'" class="content">
<div class="hero">
<img src="/assets/images/hero-Illustration.svg" alt="Hero">
<img src="/assets/images/hero-Illustration.svg" alt="Hero" />
</div>
<div class="features">
<div class="feature">
@@ -51,7 +46,7 @@
<script>
import { UserIcon, CloudIcon, HardDriveIcon } from 'vue-feather-icons'
import PageTitle from "./Components/PageTitle";
import PageTitle from './Components/PageTitle'
import { mapGetters } from 'vuex'
export default {
@@ -107,7 +102,11 @@
&:nth-child(1) .icon {
background: rgba($yellow, 0.1);
path, line, polyline, rect, circle {
path,
line,
polyline,
rect,
circle {
stroke: $yellow;
}
}
@@ -115,7 +114,11 @@
&:nth-child(2) .icon {
background: rgba($theme, 0.1);
path, line, polyline, rect, circle {
path,
line,
polyline,
rect,
circle {
stroke: $theme;
}
}
@@ -123,7 +126,11 @@
&:nth-child(3) .icon {
background: rgba($purple, 0.1);
path, line, polyline, rect, circle {
path,
line,
polyline,
rect,
circle {
stroke: $purple;
}
}
@@ -136,7 +143,6 @@
}
@media only screen and (max-width: 1190px) {
.hero {
flex: 0 0 60%;
@@ -152,7 +158,6 @@
}
@media only screen and (max-width: 960px) {
.content {
display: block;
margin-top: 40px;
@@ -177,16 +182,12 @@
}
.dark {
.features {
.feature {
.description {
color: $dark_mode_text_secondary;
}
}
}
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<nav class="main-navigation">
<router-link :to="{ name: 'Homepage' }" tag="div" class="logo">
<img v-if="config.app_logo_horizontal" :src="$getImage(config.app_logo_horizontal)" :alt="config.app_name">
<img v-if="config.app_logo_horizontal" :src="$getImage(config.app_logo_horizontal)" :alt="config.app_name" />
<b v-if="!config.app_logo_horizontal" class="logo-text">{{ config.app_name }}</b>
</router-link>
<div class="navigation">
@@ -114,13 +114,11 @@
}
@media only screen and (max-width: 690px) {
.navigation {
display: none;
}
.logo {
img {
height: auto;
width: 190px;

View File

@@ -1,7 +1,7 @@
<template>
<footer class="page-wrapper medium">
<router-link :to="{ name: 'Homepage' }" tag="div" class="logo">
<img v-if="config.app_logo_horizontal" :src="$getImage(config.app_logo_horizontal)" :alt="config.app_name">
<img v-if="config.app_logo_horizontal" :src="$getImage(config.app_logo_horizontal)" :alt="config.app_name" />
<b v-if="!config.app_logo_horizontal" class="logo-text">{{ config.app_name }}</b>
</router-link>
<ul class="navigation-links">
@@ -105,7 +105,6 @@
}
.dark {
.copyright {
color: $dark_mode_text_secondary;
}

View File

@@ -1,12 +1,8 @@
<template>
<header class="main-header page-wrapper medium">
<PageTitle
:title="index.header_title"
:description="index.header_description"
/>
<PageTitle :title="index.header_title" :description="index.header_description" />
<div v-if="!config.isAuthenticated">
<!--User registration button-->
<router-link v-if="config.userRegistration" class="sign-up-button" :to="{ name: 'SignUp' }">
<AuthButton class="button" icon="chevron-right" :text="$t('page_index.sign_up_button')" />
@@ -24,7 +20,11 @@
</div>
<div class="feature">
<hard-drive-icon size="19" class="feature-icon"></hard-drive-icon>
<b class="feature-title">{{ $t('page_index.sign_feature_2', {defaultSpace: config.storageDefaultSpaceFormatted}) }}</b>
<b class="feature-title">{{
$t('page_index.sign_feature_2', {
defaultSpace: config.storageDefaultSpaceFormatted,
})
}}</b>
</div>
</div>
</div>
@@ -32,9 +32,9 @@
</template>
<script>
import HardDriveIcon from "vue-feather-icons/icons/HardDriveIcon";
import PageTitle from "./Components/PageTitle";
import AuthButton from "../Auth/AuthButton";
import HardDriveIcon from 'vue-feather-icons/icons/HardDriveIcon'
import PageTitle from './Components/PageTitle'
import AuthButton from '../Auth/AuthButton'
import { CreditCardIcon } from 'vue-feather-icons'
import { mapGetters } from 'vuex'
@@ -66,13 +66,21 @@
margin-right: 35px;
&:nth-child(1) {
path, line, polyline, rect, circle {
path,
line,
polyline,
rect,
circle {
stroke: $yellow;
}
}
&:nth-child(2) {
path, line, polyline, rect, circle {
path,
line,
polyline,
rect,
circle {
stroke: $purple;
}
}
@@ -107,7 +115,6 @@
}
@media only screen and (max-width: 690px) {
.main-header {
padding-top: 50px;
}

View File

@@ -21,8 +21,8 @@
</template>
<script>
import PricingTables from "./Components/PricingTables";
import AuthButton from "../Auth/AuthButton";
import PricingTables from './Components/PricingTables'
import AuthButton from '../Auth/AuthButton'
import { CloudIcon } from 'vue-feather-icons'
import { mapGetters } from 'vuex'
@@ -43,10 +43,9 @@
},
methods: {
pricingLoaded(pricing) {
if (pricing.length === 0)
this.isEmpty = true
}
}
if (pricing.length === 0) this.isEmpty = true
},
},
}
</script>
@@ -137,7 +136,6 @@
}
@media only screen and (max-width: 1190px) {
.cloud-bg {
display: none;
}
@@ -149,9 +147,7 @@
}
@media only screen and (max-width: 960px) {
.page-title {
.title {
font-size: 28px;
line-height: 1.25;

View File

@@ -1,23 +1,17 @@
<template>
<transition name="context-menu">
<div
v-if="isVisible"
@click="closeMenu"
class="fixed pb-4 bottom-0 left-0 right-0 z-50 overflow-hidden dark:bg-2x-dark-foreground bg-white rounded-tl-xl rounded-tr-xl"
>
<div v-if="isVisible" @click="closeMenu" class="fixed bottom-0 left-0 right-0 z-50 overflow-hidden rounded-tl-xl rounded-tr-xl bg-white pb-4 dark:bg-2x-dark-foreground">
<slot />
</div>
</transition>
</template>
<script>
import {events} from "../../bus";
import { events } from '../../bus'
export default {
name: 'MenuMobile',
props: [
'name'
],
props: ['name'],
data() {
return {
isVisible: false,
@@ -28,21 +22,19 @@
this.isVisible = false
events.$emit('mobile-menu:hide')
}
},
},
created() {
events.$on('mobile-menu:show', name => {
if (name === this.name)
this.isVisible = !this.isVisible
events.$on('mobile-menu:show', (name) => {
if (name === this.name) this.isVisible = !this.isVisible
})
events.$on('mobile-menu:hide', () => this.isVisible = false)
}
events.$on('mobile-menu:hide', () => (this.isVisible = false))
},
}
</script>
<style scoped lang="scss">
// Transition
.context-menu-enter-active,
.fade-enter-active {

View File

@@ -11,8 +11,8 @@
</script>
<style scoped lang="scss">
@import "resources/sass/vuefilemanager/_variables";
@import "resources/sass/vuefilemanager/_mixins";
@import 'resources/sass/vuefilemanager/_variables';
@import 'resources/sass/vuefilemanager/_mixins';
.menu-options {
margin-top: 10px;

View File

@@ -1,6 +1,5 @@
<template>
<header class="mobile-header">
<!-- Go back-->
<div @click="goBack" class="go-back">
<chevron-left-icon size="17" class="icon" />
@@ -19,25 +18,20 @@
<script>
import { events } from '../../bus'
import {
ChevronLeftIcon,
MenuIcon,
} from 'vue-feather-icons'
import { ChevronLeftIcon, MenuIcon } from 'vue-feather-icons'
export default {
name: 'MobileHeader',
props: [
'title'
],
props: ['title'],
components: {
ChevronLeftIcon,
MenuIcon,
},
methods: {
goBack() {
this.$router.back();
}
}
this.$router.back()
},
},
}
</script>
@@ -104,5 +98,4 @@
}
}
}
</style>

View File

@@ -17,7 +17,7 @@
components: {
Edit2Icon,
XIcon,
}
},
}
</script>
@@ -39,13 +39,14 @@
display: inline-block;
margin-right: 2px;
path, circle, line {
path,
circle,
line {
color: inherit;
}
}
}
.dark {
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<b class="color-label capitalize inline-block text-xs font-bold rounded-lg py-1 px-2" :class="color">
<b class="color-label inline-block rounded-lg py-1 px-2 text-xs font-bold capitalize" :class="color">
<slot></slot>
</b>
</template>
@@ -7,9 +7,7 @@
<script>
export default {
name: 'ColorLabel',
props: [
'color'
],
props: ['color'],
}
</script>
@@ -17,7 +15,6 @@
@import '../../../sass/vuefilemanager/variables';
.color-label {
&.purple {
color: $purple;
background: rgba($purple, 0.1);

View File

@@ -19,9 +19,7 @@ export default {
props: ['pickedColor'],
components: { CheckIcon },
computed: {
...mapGetters([
'config'
])
...mapGetters(['config']),
},
data() {
return {
@@ -48,20 +46,19 @@ export default {
'#FE7D6F',
'#4c4c4c',
'#06070B',
]
],
}
},
methods: {
setColor(value) {
this.selectedColor = value
this.$emit('input', value)
}
},
},
created() {
this.colors.push(this.config.app_color)
}
},
}
</script>
@@ -132,5 +129,4 @@ export default {
}
}
}
</style>

View File

@@ -1,33 +1,29 @@
<template>
<PopupWrapper name="confirm-password">
<PopupHeader :title="$t('Confirm Password')" icon="edit" />
<PopupContent>
<ValidationObserver @submit.prevent="confirmPassword" ref="passwordForm" v-slot="{ invalid }" tag="form">
<ValidationProvider tag="div" mode="passive" name="Password" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('popup_2fa.input_label')" :error="errors[0]" :is-last="true">
<input v-model="password" :class="{'border-red': errors[0]}" type="password" ref="input" class="focus-border-theme input-dark" :placeholder="$t('page_sign_in.placeholder_password')">
<input
v-model="password"
:class="{ 'border-red': errors[0] }"
type="password"
ref="input"
class="focus-border-theme input-dark"
:placeholder="$t('page_sign_in.placeholder_password')"
/>
</AppInputText>
</ValidationProvider>
</ValidationObserver>
</PopupContent>
<PopupActions>
<ButtonBase
class="w-full"
@click.native="$closePopup()"
button-style="secondary"
>
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="secondary">
{{ $t('global.cancel') }}
</ButtonBase>
<ButtonBase
class="w-full"
@click.native="confirmPassword"
button-style="theme"
:loading="isLoading"
:disabled="isLoading"
>
<ButtonBase class="w-full" @click.native="confirmPassword" button-style="theme" :loading="isLoading" :disabled="isLoading">
{{ $t('popup_2fa.confirm_button') }}
</ButtonBase>
</PopupActions>
@@ -36,19 +32,19 @@
<script>
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import PopupWrapper from "./Popup/PopupWrapper";
import PopupWrapper from './Popup/PopupWrapper'
import PopupActions from './Popup/PopupActions'
import PopupContent from './Popup/PopupContent'
import PopupHeader from './Popup/PopupHeader'
import ButtonBase from "../FilesView/ButtonBase";
import AppInputText from "../Admin/AppInputText"
import ButtonBase from '../FilesView/ButtonBase'
import AppInputText from '../Admin/AppInputText'
import { required } from 'vee-validate/dist/rules'
import { events } from '../../bus'
import { mapGetters } from 'vuex'
import axios from 'axios'
export default {
name: "ConfirmPassword",
name: 'ConfirmPassword',
components: {
ValidationProvider,
ValidationObserver,
@@ -61,9 +57,7 @@ export default {
required,
},
computed: {
...mapGetters([
'user'
]),
...mapGetters(['user']),
},
data() {
return {
@@ -78,16 +72,16 @@ export default {
axios
.post('/user/confirm-password', {
password: this.password
password: this.password,
})
.then(() => {
events.$emit('password:confirmed', this.args)
})
.catch(error => {
.catch((error) => {
if (error.response.status === 422) {
this.$refs.passwordForm.setErrors({
'Password': this.$t('validation_errors.incorrect_password')
});
Password: this.$t('validation_errors.incorrect_password'),
})
}
})
.finally(() => {
@@ -98,12 +92,11 @@ export default {
},
created() {
// Show popup
events.$on('popup:open', args => {
events.$on('popup:open', (args) => {
if (args.name !== 'confirm-password') return
this.args = args
})
}
},
}
</script>

View File

@@ -1,6 +1,9 @@
<template>
<div v-if="isVisibleDisclaimer" class="fixed bottom-0 sm:left-16 left-0 sm:right-auto right-0 sm:p-3 sm:w-56 w-full p-4 shadow-xl rounded-tl-xl rounded-tr-lg dark:bg-dark-foreground bg-white z-20">
<span @click="closeDisclaimer" class="absolute -right-1 -top-1 p-3 cursor-pointer">
<div
v-if="isVisibleDisclaimer"
class="fixed bottom-0 left-0 right-0 z-20 w-full rounded-tl-xl rounded-tr-lg bg-white p-4 shadow-xl dark:bg-dark-foreground sm:left-16 sm:right-auto sm:w-56 sm:p-3"
>
<span @click="closeDisclaimer" class="absolute -right-1 -top-1 cursor-pointer p-3">
<x-icon size="10" />
</span>
<i18n path="cookie_disclaimer.description" tag="p" class="text-xs">
@@ -21,13 +24,11 @@
XIcon,
},
computed: {
...mapGetters([
'config'
]),
...mapGetters(['config']),
},
data() {
return {
isVisibleDisclaimer: false
isVisibleDisclaimer: false,
}
},
methods: {
@@ -35,10 +36,10 @@
localStorage.setItem('isHiddenDisclaimer', 'true')
this.isVisibleDisclaimer = false
}
},
},
created() {
this.isVisibleDisclaimer = !localStorage.getItem('isHiddenDisclaimer');
}
this.isVisibleDisclaimer = !localStorage.getItem('isHiddenDisclaimer')
},
}
</script>

View File

@@ -1,19 +1,23 @@
<template>
<PopupWrapper name="create-folder">
<!--Title-->
<PopupHeader :title="$t('popup_create_folder.title')" icon="edit" />
<!--Content-->
<PopupContent>
<!--Form to set sharing-->
<ValidationObserver @submit.prevent="createFolder" ref="createForm" v-slot="{ invalid }" tag="form">
<!--Set folder name-->
<ValidationProvider tag="div" mode="passive" name="Title" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('popup_create_folder.label')" :error="errors[0]">
<input v-model="name" :class="{'border-red': errors[0]}" type="text" ref="input" class="focus-border-theme input-dark" :placeholder="$t('popup_create_folder.placeholder')">
<input
v-model="name"
:class="{ 'border-red': errors[0] }"
type="text"
ref="input"
class="focus-border-theme input-dark"
:placeholder="$t('popup_create_folder.placeholder')"
/>
</AppInputText>
</ValidationProvider>
@@ -28,36 +32,26 @@
<!--Actions-->
<PopupActions>
<ButtonBase
class="w-full"
@click.native="$closePopup()"
button-style="secondary"
>{{ $t('popup_move_item.cancel') }}
</ButtonBase>
<ButtonBase
class="w-full"
@click.native="createFolder"
button-style="theme"
>{{ $t('popup_create_folder.title') }}
</ButtonBase>
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="secondary">{{ $t('popup_move_item.cancel') }} </ButtonBase>
<ButtonBase class="w-full" @click.native="createFolder" button-style="theme">{{ $t('popup_create_folder.title') }} </ButtonBase>
</PopupActions>
</PopupWrapper>
</template>
<script>
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import PopupWrapper from "./Popup/PopupWrapper";
import PopupActions from "./Popup/PopupActions";
import PopupContent from "./Popup/PopupContent";
import PopupHeader from "./Popup/PopupHeader";
import ThumbnailItem from "./ThumbnailItem";
import ButtonBase from "../FilesView/ButtonBase";
import PopupWrapper from './Popup/PopupWrapper'
import PopupActions from './Popup/PopupActions'
import PopupContent from './Popup/PopupContent'
import PopupHeader from './Popup/PopupHeader'
import ThumbnailItem from './ThumbnailItem'
import ButtonBase from '../FilesView/ButtonBase'
import { required } from 'vee-validate/dist/rules'
import AppInputSwitch from "../Admin/AppInputSwitch"
import AppInputText from "../Admin/AppInputText"
import SwitchInput from "./Forms/SwitchInput"
import AppInputSwitch from '../Admin/AppInputSwitch'
import AppInputText from '../Admin/AppInputText'
import SwitchInput from './Forms/SwitchInput'
import { events } from '../../bus'
import EmojiPicker from "./EmojiPicker"
import EmojiPicker from './EmojiPicker'
export default {
name: 'CreateFolderPopup',
@@ -85,15 +79,14 @@
},
methods: {
async createFolder() {
// Validate fields
const isValid = await this.$refs.createForm.validate();
const isValid = await this.$refs.createForm.validate()
if (!isValid) return;
if (!isValid) return
await this.$store.dispatch('createFolder', {
name: this.name,
emoji: this.emoji
emoji: this.emoji,
})
this.$closePopup()
@@ -105,10 +98,8 @@
},
mounted() {
events.$on('popup:open', ({ name }) => {
if (name === 'create-folder' && ! this.$isMobile())
this.$nextTick(() => this.$refs.input.focus())
if (name === 'create-folder' && !this.$isMobile()) this.$nextTick(() => this.$refs.input.focus())
})
}
},
}
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,21 @@
<template>
<PopupWrapper name="create-personal-token">
<PopupHeader :title="$t('popup_personal_token.title')" icon="key" />
<PopupContent>
<ValidationObserver v-if="!token" @submit.prevent="createTokenForm" ref="createToken" v-slot="{ invalid }" tag="form">
<ValidationProvider tag="div" mode="passive" name="Token Name" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('popup_personal_token.label')" :error="errors[0]" :is-last="true">
<input v-model="name" :class="{'border-red': errors[0]}" type="text" ref="input" class="focus-border-theme input-dark" :placeholder="$t('popup_personal_token.plc')">
<input
v-model="name"
:class="{ 'border-red': errors[0] }"
type="text"
ref="input"
class="focus-border-theme input-dark"
:placeholder="$t('popup_personal_token.plc')"
/>
</AppInputText>
</ValidationProvider>
</ValidationObserver>
<AppInputText v-if="token" :title="$t('popup_personal_token.your_token')" :is-last="true">
@@ -21,34 +25,19 @@
<p v-html="$t('popup_personal_token.copy_token')"></p>
</InfoBox>
</AppInputText>
</PopupContent>
<PopupActions v-if="!token">
<ButtonBase
class="w-full"
@click.native="$closePopup()"
button-style="secondary"
>
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="secondary">
{{ $t('global.cancel') }}
</ButtonBase>
<ButtonBase
class="w-full"
@click.native="createTokenForm"
button-style="theme"
:loading="isLoading"
:disabled="isLoading"
>
<ButtonBase class="w-full" @click.native="createTokenForm" button-style="theme" :loading="isLoading" :disabled="isLoading">
{{ $t('personal_token.create_token') }}
</ButtonBase>
</PopupActions>
<PopupActions v-if="token">
<ButtonBase
class="w-full"
@click.native="closePopup"
button-style="theme"
>
<ButtonBase class="w-full" @click.native="closePopup" button-style="theme">
{{ $t('shared_form.button_done') }}
</ButtonBase>
</PopupActions>
@@ -56,21 +45,21 @@
</template>
<script>
import AppInputText from "../Admin/AppInputText";
import AppInputText from '../Admin/AppInputText'
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import PopupWrapper from "./Popup/PopupWrapper";
import PopupActions from "./Popup/PopupActions";
import PopupContent from "./Popup/PopupContent";
import PopupHeader from "./Popup/PopupHeader";
import CopyInput from "./Forms/CopyInput";
import ButtonBase from "../FilesView/ButtonBase";
import InfoBox from "./Forms/InfoBox";
import PopupWrapper from './Popup/PopupWrapper'
import PopupActions from './Popup/PopupActions'
import PopupContent from './Popup/PopupContent'
import PopupHeader from './Popup/PopupHeader'
import CopyInput from './Forms/CopyInput'
import ButtonBase from '../FilesView/ButtonBase'
import InfoBox from './Forms/InfoBox'
import { required } from 'vee-validate/dist/rules'
import { events } from '../../bus'
import axios from 'axios'
export default {
name: "CreatePersonalTokenPopup",
name: 'CreatePersonalTokenPopup',
components: {
ValidationProvider,
ValidationObserver,
@@ -88,12 +77,11 @@ export default {
return {
isLoading: false,
name: undefined,
token: undefined
token: undefined,
}
},
methods: {
async createTokenForm() {
const isValid = await this.$refs.createToken.validate()
if (!isValid) return
@@ -102,9 +90,9 @@ export default {
axios
.post('/api/user/tokens', {
name: this.name
name: this.name,
})
.then(response => {
.then((response) => {
this.token = response.data
events.$emit('reload-personal-access-tokens')
@@ -118,8 +106,8 @@ export default {
closePopup() {
this.$closePopup()
this.token = undefined
}
}
},
},
}
</script>
@@ -128,10 +116,8 @@ export default {
@import '../../../sass/vuefilemanager/forms';
.dark {
.info-box {
background: lighten($dark_mode_foreground, 3%);
}
}
</style>

View File

@@ -1,14 +1,7 @@
<template>
<div v-if="emoji">
<div
v-if="config.defaultEmoji === 'twemoji'"
v-html="transferEmoji"
style="font-size: inherit; transform: scale(0.95)"
></div>
<div
v-if="config.defaultEmoji === 'applemoji'"
style="font-size: inherit"
>
<div v-if="config.defaultEmoji === 'twemoji'" v-html="transferEmoji" style="font-size: inherit; transform: scale(0.95)"></div>
<div v-if="config.defaultEmoji === 'applemoji'" style="font-size: inherit">
{{ emoji.char }}
</div>
</div>
@@ -16,13 +9,11 @@
<script>
import twemoji from 'twemoji'
import {mapGetters} from "vuex";
import { mapGetters } from 'vuex'
export default {
name: 'Emoji',
props: [
'emoji',
],
props: ['emoji'],
data() {
return {
isApple: false,
@@ -30,18 +21,16 @@
}
},
computed: {
...mapGetters([
'config',
]),
...mapGetters(['config']),
transferEmoji() {
return twemoji.parse(this.emoji.char, {
folder: 'svg',
ext: '.svg',
attributes: () => ({
loading: 'lazy'
loading: 'lazy',
}),
})
})
}
},
},
}
</script>

View File

@@ -1,15 +1,14 @@
<template>
<div>
<!-- Search field -->
<div class="mb-3 relative flex items-center">
<div class="relative mb-3 flex items-center">
<!-- Selected emoji preview -->
<div v-if="defaultEmoji" class="select-none mr-3">
<div v-if="defaultEmoji" class="mr-3 select-none">
<Emoji :emoji="defaultEmoji" class="text-5xl" />
</div>
<!-- Search input -->
<input @click="openList" v-model="query" class="focus-border-theme input-dark" type="text" :placeholder="$t('Select or search emoji icon...')">
<input @click="openList" v-model="query" class="focus-border-theme input-dark" type="text" :placeholder="$t('Select or search emoji icon...')" />
</div>
<!-- Spinner -->
@@ -18,36 +17,43 @@
</div>
<!-- Emojis List -->
<div v-if="isOpen && isLoaded && emojis" @scroll="checkGroupInView" id="group-box" class="2xl:h-96 lg:h-60 h-96 overflow-y-auto select-none relative">
<div v-if="isOpen && isLoaded && emojis" @scroll="checkGroupInView" id="group-box" class="relative h-96 select-none overflow-y-auto lg:h-60 2xl:h-96">
<!-- Navigation of Emojis Groups -->
<ul v-if="! query" class="flex items-center justify-between space-x-1 sticky top-0 sm:dark:bg-4x-dark-foreground dark:bg-dark-background bg-white z-10" id="group-bar">
<li @click.stop="scrollToGroup(group.name)" v-for="(group,i) in emojis.groups" :key="i" class="w-14 h-14 flex items-center justify-center rounded-xl cursor-pointer dark:hover:bg-2x-dark-foreground hover:bg-light-background" :class="{'dark:bg-2x-dark-foreground bg-light-background': group.name === groupInView}">
<ul v-if="!query" class="sticky top-0 z-10 flex items-center justify-between space-x-1 bg-white dark:bg-dark-background sm:dark:bg-4x-dark-foreground" id="group-bar">
<li
@click.stop="scrollToGroup(group.name)"
v-for="(group, i) in emojis.groups"
:key="i"
class="flex h-14 w-14 cursor-pointer items-center justify-center rounded-xl hover:bg-light-background dark:hover:bg-2x-dark-foreground"
:class="{
'bg-light-background dark:bg-2x-dark-foreground': group.name === groupInView,
}"
>
<Emoji :emoji="group.emoji" class="text-3xl" />
</li>
</ul>
<!-- All Emojis -->
<div v-if="!query" v-for="(group, name) in allEmoji" :key="name" :id="`group-${name}`">
<label class="font-bold text-sm mt-4 mb-2 block">
<label class="mt-4 mb-2 block text-sm font-bold">
{{ name }}
</label>
<ul class="grid md:grid-cols-9 grid-cols-7 gap-4 space-between">
<li @click="setEmoji( emoji )" v-for="(emoji,i) in group" :key="i" class="flex items-center justify-center cursor-pointer">
<ul class="space-between grid grid-cols-7 gap-4 md:grid-cols-9">
<li @click="setEmoji(emoji)" v-for="(emoji, i) in group" :key="i" class="flex cursor-pointer items-center justify-center">
<Emoji :emoji="emoji" class="text-4xl" />
</li>
</ul>
</div>
<!-- Searched emojis -->
<ul v-if="query" class="grid md:grid-cols-9 grid-cols-7 gap-4 space-between">
<li @click="setEmoji( emoji )" v-for="(emoji,i) in filteredEmojis" :key="i" class="flex items-center justify-center cursor-pointer">
<ul v-if="query" class="space-between grid grid-cols-7 gap-4 md:grid-cols-9">
<li @click="setEmoji(emoji)" v-for="(emoji, i) in filteredEmojis" :key="i" class="flex cursor-pointer items-center justify-center">
<Emoji :emoji="emoji" class="text-4xl" />
</li>
</ul>
<!-- Not found -->
<span class="font-bold text-sm ml-2" v-if="filteredEmojis.length === 0 && query !== undefined">
<span class="ml-2 text-sm font-bold" v-if="filteredEmojis.length === 0 && query !== undefined">
{{ $t('There is nothing :(') }}
</span>
</div>
@@ -55,26 +61,22 @@
</template>
<script>
import Spinner from "../FilesView/Spinner";
import Emoji from "./Emoji";
import Spinner from '../FilesView/Spinner'
import Emoji from './Emoji'
import { debounce, groupBy } from 'lodash'
import { XIcon } from 'vue-feather-icons'
import { mapGetters } from 'vuex'
export default {
name: 'EmojiPicker',
props: [
'defaultEmoji',
],
props: ['defaultEmoji'],
components: {
Spinner,
Emoji,
XIcon,
},
computed: {
...mapGetters([
'emojis',
]),
...mapGetters(['emojis']),
allEmoji() {
return groupBy(this.emojis.list, 'group')
},
@@ -97,16 +99,16 @@ export default {
if (val === '' || val === undefined) return
// Filter emojis by query
this.filteredEmojis = this.emojis.list.filter(emoji => emoji.name.includes(val.toLowerCase()))
this.filteredEmojis = this.emojis.list.filter((emoji) => emoji.name.includes(val.toLowerCase()))
if (this.filteredEmojis.length === 0) {
console.log('empty');
console.log('empty')
}
}, 200),
},
methods: {
checkGroupInView: _.debounce(function () {
this.emojis.groups.forEach(group => {
this.emojis.groups.forEach((group) => {
let element = document.getElementById(`group-${group.name}`).getBoundingClientRect()
let groupBox = document.getElementById('group-box').getBoundingClientRect()
@@ -124,8 +126,8 @@ export default {
groupBox.scrollTo({
top: group.offsetTop - groupBar.clientHeight - 5,
left: 0,
behavior: 'smooth'
});
behavior: 'smooth',
})
this.groupInView = name
},
@@ -135,18 +137,19 @@ export default {
// Load emojis from server just if not loaded already
if (this.isOpen && !this.emojis) {
axios.get('/assets/emojis.json')
.then(response => {
axios
.get('/assets/emojis.json')
.then((response) => {
this.$store.commit('LOAD_EMOJIS_LIST', response.data)
})
.finally(() => this.isLoaded = true)
.finally(() => (this.isLoaded = true))
}
// Simulate loading for the list processing
if (this.emojis) {
setTimeout(() => {
this.isLoaded = true
}, 20);
}, 20)
}
this.groupInView = 'Smileys & Emotion'
@@ -161,6 +164,6 @@ export default {
mounted() {
// Open list of there isn't set any emoji
if (!this.defaultEmoji) this.openList()
}
},
}
</script>

View File

@@ -25,7 +25,7 @@
SettingsIcon,
FileTextIcon,
FileIcon,
}
},
}
</script>
@@ -50,7 +50,10 @@
}
.icon {
path, polyline, line, circle {
path,
polyline,
line,
circle {
stroke: $theme;
}
}

View File

@@ -1,34 +1,19 @@
<template>
<div v-if="canBePreview" class="w-full block mb-4">
<div v-if="canBePreview" class="mb-4 block w-full">
<!--Image-->
<img
v-if="singleFile.data.type === 'image' && singleFile.data.attributes.thumbnail"
:src="singleFile.data.attributes.thumbnail.md"
:alt="singleFile.data.attributes.name"
class="rounded-lg overflow-hidden w-full object-cover shadow-lg"
class="w-full overflow-hidden rounded-lg object-cover shadow-lg"
/>
<!--Audio-->
<audio
v-else-if="singleFile.data.type === 'audio'"
:src="singleFile.data.attributes.file_url"
controlsList="nodownload"
controls
class="w-full"
>
</audio>
<audio v-else-if="singleFile.data.type === 'audio'" :src="singleFile.data.attributes.file_url" controlsList="nodownload" controls class="w-full"></audio>
<!--Video-->
<video
class="w-full h-auto rounded-sm overflow-hidden"
v-else-if="singleFile.data.type === 'video'"
controlsList="nodownload"
disablePictureInPicture
playsinline
controls
>
<source :src="singleFile.data.attributes.file_url" type="video/mp4">
<video class="h-auto w-full overflow-hidden rounded-sm" v-else-if="singleFile.data.type === 'video'" controlsList="nodownload" disablePictureInPicture playsinline controls>
<source :src="singleFile.data.attributes.file_url" type="video/mp4" />
</video>
</div>
</template>
@@ -40,17 +25,13 @@
export default {
name: 'FilePreview',
computed: {
...mapGetters([
'clipboard',
]),
...mapGetters(['clipboard']),
singleFile() {
return this.clipboard[0]
},
canBePreview() {
return this.singleFile && !includes([
'folder', 'file'
], this.singleFile.data.type)
return this.singleFile && !includes(['folder', 'file'], this.singleFile.data.type)
},
},
}
}
</script>

View File

@@ -1,43 +1,28 @@
<template>
<div class="relative cursor-pointer">
<input
ref="file"
type="file"
@change="showImagePreview($event)"
class="absolute opacity-0 top-0 bottom-0 left-0 right-0 w-full z-10 cursor-pointer"
/>
<img
v-if="imagePreview"
ref="image"
:src="imagePreview"
class="md:w-16 w-14 md:h-16 h-14 object-cover rounded-xl relative z-0 shadow-lg cursor-pointer"
alt="avatar"
/>
<input ref="file" type="file" @change="showImagePreview($event)" class="absolute top-0 bottom-0 left-0 right-0 z-10 w-full cursor-pointer opacity-0" />
<img v-if="imagePreview" ref="image" :src="imagePreview" class="relative z-0 h-14 w-14 cursor-pointer rounded-xl object-cover shadow-lg md:h-16 md:w-16" alt="avatar" />
</div>
</template>
<script>
export default {
name: 'AvatarInput',
props: [
'avatar',
],
props: ['avatar'],
data() {
return {
imagePreview: undefined
imagePreview: undefined,
}
},
watch: {
imagePreview(val) {
this.$store.commit('UPDATE_AVATAR', val)
}
},
},
methods: {
showImagePreview(event) {
let imgPath = event.target.files[0].name,
extension = imgPath
.substring(imgPath.lastIndexOf('.') + 1)
.toLowerCase()
extension = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase()
if (['png', 'jpg', 'jpeg'].includes(extension)) {
let file = event.target.files[0],
@@ -52,11 +37,11 @@
} else {
alert(this.$t('validation_errors.wrong_image'))
}
}
},
},
created() {
// If there is default image then load
if (this.avatar) this.imagePreview = this.avatar
}
},
}
</script>

View File

@@ -1,28 +1,21 @@
<template>
<div @click="copyUrl" class="flex items-center relative">
<input ref="sel" :value="str" :id="id" type="text" class="pr-10 focus-border-theme input-dark" readonly>
<div @click="copyUrl" class="relative flex items-center">
<input ref="sel" :value="str" :id="id" type="text" class="focus-border-theme input-dark pr-10" readonly />
<!--Copy icon-->
<div class="absolute right-0 px-4">
<copy-icon v-if="! isCopiedLink" size="16" class="cursor-pointer hover-text-theme vue-feather"/>
<check-icon v-if="isCopiedLink" size="16" class="cursor-pointer text-theme vue-feather"/>
<copy-icon v-if="!isCopiedLink" size="16" class="hover-text-theme vue-feather cursor-pointer" />
<check-icon v-if="isCopiedLink" size="16" class="text-theme vue-feather cursor-pointer" />
</div>
</div>
</template>
<script>
import {
CopyIcon,
CheckIcon,
SendIcon
} from 'vue-feather-icons'
import { CopyIcon, CheckIcon, SendIcon } from 'vue-feather-icons'
export default {
name: 'CopyInput',
props: [
'size',
'str',
],
props: ['size', 'str'],
components: {
CheckIcon,
CopyIcon,
@@ -36,7 +29,6 @@ export default {
},
methods: {
copyUrl() {
// Get input value
let copyText = document.getElementById(this.id)
@@ -54,7 +46,7 @@ export default {
setTimeout(() => {
this.isCopiedLink = false
}, 1000)
}
}
},
},
}
</script>

Some files were not shown because too many files have changed in this diff Show More