mirror of
https://github.com/VueFileManager/vuefilemanager.git
synced 2026-04-21 17:12:15 +00:00
added prettier
This commit is contained in:
@@ -9,43 +9,44 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Edit2Icon, XIcon } from 'vue-feather-icons'
|
||||
import { Edit2Icon, XIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'ActionButton',
|
||||
props: ['icon'],
|
||||
components: {
|
||||
Edit2Icon,
|
||||
XIcon,
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: 'ActionButton',
|
||||
props: ['icon'],
|
||||
components: {
|
||||
Edit2Icon,
|
||||
XIcon,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.action-button {
|
||||
cursor: pointer;
|
||||
.action-button {
|
||||
cursor: pointer;
|
||||
|
||||
.label {
|
||||
@include font-size(12);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.icon {
|
||||
@include font-size(10);
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
margin-right: 2px;
|
||||
|
||||
path, circle, line {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
.label {
|
||||
@include font-size(12);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.dark {
|
||||
.icon {
|
||||
@include font-size(10);
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
margin-right: 2px;
|
||||
|
||||
path,
|
||||
circle,
|
||||
line {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,41 +1,38 @@
|
||||
<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>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ColorLabel',
|
||||
props: [
|
||||
'color'
|
||||
],
|
||||
}
|
||||
export default {
|
||||
name: 'ColorLabel',
|
||||
props: ['color'],
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
|
||||
.color-label {
|
||||
|
||||
&.purple {
|
||||
color: $purple;
|
||||
background: rgba($purple, 0.1);
|
||||
}
|
||||
|
||||
&.yellow {
|
||||
color: $yellow;
|
||||
background: rgba($yellow, 0.1);
|
||||
}
|
||||
|
||||
&.green {
|
||||
color: $theme;
|
||||
background: rgba($theme, 0.1);
|
||||
}
|
||||
|
||||
&.red {
|
||||
color: $danger;
|
||||
background: rgba($danger, 0.1);
|
||||
}
|
||||
.color-label {
|
||||
&.purple {
|
||||
color: $purple;
|
||||
background: rgba($purple, 0.1);
|
||||
}
|
||||
|
||||
&.yellow {
|
||||
color: $yellow;
|
||||
background: rgba($yellow, 0.1);
|
||||
}
|
||||
|
||||
&.green {
|
||||
color: $theme;
|
||||
background: rgba($theme, 0.1);
|
||||
}
|
||||
|
||||
&.red {
|
||||
color: $danger;
|
||||
background: rgba($danger, 0.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="color-pick-wrapper">
|
||||
<div class="color-pick-wrapper">
|
||||
<label class="main-label">{{ $t('popup_rename.color_pick_label') }}:</label>
|
||||
<ul class="color-wrapper">
|
||||
<li v-for="(color, i) in colors" :key="i" @click="setColor( color )" class="single-color">
|
||||
<check-icon v-if="color === selectedColor" class="color-icon" size="22"/>
|
||||
<span :style="{background:color}" class="color-box"></span>
|
||||
<li v-for="(color, i) in colors" :key="i" @click="setColor(color)" class="single-color">
|
||||
<check-icon v-if="color === selectedColor" class="color-icon" size="22" />
|
||||
<span :style="{ background: color }" class="color-box"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -16,14 +16,12 @@ import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'ColorPicker',
|
||||
props: [ 'pickedColor' ],
|
||||
props: ['pickedColor'],
|
||||
components: { CheckIcon },
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'config'
|
||||
])
|
||||
...mapGetters(['config']),
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
selectedColor: this.pickedColor,
|
||||
colors: [
|
||||
@@ -48,20 +46,19 @@ export default {
|
||||
'#FE7D6F',
|
||||
'#4c4c4c',
|
||||
'#06070B',
|
||||
]
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setColor (value) {
|
||||
|
||||
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>
|
||||
|
||||
@@ -1,109 +1,102 @@
|
||||
<template>
|
||||
<PopupWrapper name="confirm-password">
|
||||
|
||||
<PopupHeader :title="$t('Confirm Password')" icon="edit" />
|
||||
|
||||
<PopupContent>
|
||||
<ValidationObserver @submit.prevent="confirmPassword" ref="passwordForm" v-slot="{ invalid }" tag="form">
|
||||
<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')">
|
||||
</AppInputText>
|
||||
<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')"
|
||||
/>
|
||||
</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"
|
||||
>
|
||||
{{ $t('popup_2fa.confirm_button') }}
|
||||
<ButtonBase class="w-full" @click.native="confirmPassword" button-style="theme" :loading="isLoading" :disabled="isLoading">
|
||||
{{ $t('popup_2fa.confirm_button') }}
|
||||
</ButtonBase>
|
||||
</PopupActions>
|
||||
</PopupWrapper>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
|
||||
import PopupWrapper from "./Popup/PopupWrapper";
|
||||
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 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 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",
|
||||
components: {
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
AppInputText,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
required,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user'
|
||||
]),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
password: '',
|
||||
args: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirmPassword() {
|
||||
this.isLoading = true
|
||||
name: 'ConfirmPassword',
|
||||
components: {
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
AppInputText,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
required,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
password: '',
|
||||
args: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirmPassword() {
|
||||
this.isLoading = true
|
||||
|
||||
axios
|
||||
.post('/user/confirm-password', {
|
||||
password: this.password
|
||||
})
|
||||
.then(() => {
|
||||
events.$emit('password:confirmed', this.args)
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.response.status === 422) {
|
||||
this.$refs.passwordForm.setErrors({
|
||||
'Password': this.$t('validation_errors.incorrect_password')
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false
|
||||
this.password = undefined
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// Show popup
|
||||
events.$on('popup:open', args => {
|
||||
axios
|
||||
.post('/user/confirm-password', {
|
||||
password: this.password,
|
||||
})
|
||||
.then(() => {
|
||||
events.$emit('password:confirmed', this.args)
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.response.status === 422) {
|
||||
this.$refs.passwordForm.setErrors({
|
||||
Password: this.$t('validation_errors.incorrect_password'),
|
||||
})
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false
|
||||
this.password = undefined
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// Show popup
|
||||
events.$on('popup:open', (args) => {
|
||||
if (args.name !== 'confirm-password') return
|
||||
|
||||
if (args.name !== 'confirm-password') return
|
||||
|
||||
this.args = args
|
||||
})
|
||||
}
|
||||
this.args = args
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,44 +1,45 @@
|
||||
<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">
|
||||
<router-link :to="{name: 'DynamicPage', params: {slug: 'cookie-policy'}}" class="text-theme text-xs">
|
||||
{{ $t('cookie_disclaimer.button') }}
|
||||
</router-link>
|
||||
<router-link :to="{ name: 'DynamicPage', params: { slug: 'cookie-policy' } }" class="text-theme text-xs">
|
||||
{{ $t('cookie_disclaimer.button') }}
|
||||
</router-link>
|
||||
</i18n>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {XIcon} from 'vue-feather-icons'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { XIcon } from 'vue-feather-icons'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'CookieDisclaimer',
|
||||
components: {
|
||||
XIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'config'
|
||||
]),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isVisibleDisclaimer: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeDisclaimer() {
|
||||
localStorage.setItem('isHiddenDisclaimer', 'true')
|
||||
|
||||
this.isVisibleDisclaimer = false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.isVisibleDisclaimer = !localStorage.getItem('isHiddenDisclaimer');
|
||||
export default {
|
||||
name: 'CookieDisclaimer',
|
||||
components: {
|
||||
XIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['config']),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isVisibleDisclaimer: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeDisclaimer() {
|
||||
localStorage.setItem('isHiddenDisclaimer', 'true')
|
||||
|
||||
this.isVisibleDisclaimer = false
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.isVisibleDisclaimer = !localStorage.getItem('isHiddenDisclaimer')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,114 +1,105 @@
|
||||
<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')">
|
||||
</AppInputText>
|
||||
<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')"
|
||||
/>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
|
||||
<AppInputSwitch :title="$t('Emoji as an Icon')" :description="$t('Replace folder icon with an Emoji')" :is-last="! isEmoji">
|
||||
<SwitchInput v-model="isEmoji" :state="isEmoji" />
|
||||
</AppInputSwitch>
|
||||
<AppInputSwitch :title="$t('Emoji as an Icon')" :description="$t('Replace folder icon with an Emoji')" :is-last="!isEmoji">
|
||||
<SwitchInput v-model="isEmoji" :state="isEmoji" />
|
||||
</AppInputSwitch>
|
||||
|
||||
<!--Set emoji-->
|
||||
<EmojiPicker v-if="isEmoji" v-model="emoji" :default-emoji="emoji"/>
|
||||
<!--Set emoji-->
|
||||
<EmojiPicker v-if="isEmoji" v-model="emoji" :default-emoji="emoji" />
|
||||
</ValidationObserver>
|
||||
</PopupContent>
|
||||
|
||||
<!--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 {required} from 'vee-validate/dist/rules'
|
||||
import AppInputSwitch from "../Admin/AppInputSwitch"
|
||||
import AppInputText from "../Admin/AppInputText"
|
||||
import SwitchInput from "./Forms/SwitchInput"
|
||||
import {events} from '../../bus'
|
||||
import EmojiPicker from "./EmojiPicker"
|
||||
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 { required } from 'vee-validate/dist/rules'
|
||||
import AppInputSwitch from '../Admin/AppInputSwitch'
|
||||
import AppInputText from '../Admin/AppInputText'
|
||||
import SwitchInput from './Forms/SwitchInput'
|
||||
import { events } from '../../bus'
|
||||
import EmojiPicker from './EmojiPicker'
|
||||
|
||||
export default {
|
||||
name: 'CreateFolderPopup',
|
||||
components: {
|
||||
AppInputSwitch,
|
||||
SwitchInput,
|
||||
EmojiPicker,
|
||||
AppInputText,
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
ThumbnailItem,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
required,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
isEmoji: false,
|
||||
emoji: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async createFolder() {
|
||||
|
||||
// Validate fields
|
||||
const isValid = await this.$refs.createForm.validate();
|
||||
|
||||
if (!isValid) return;
|
||||
|
||||
await this.$store.dispatch('createFolder', {
|
||||
name: this.name,
|
||||
emoji: this.emoji
|
||||
})
|
||||
|
||||
this.$closePopup()
|
||||
|
||||
this.name = undefined
|
||||
this.isEmoji = false
|
||||
this.emoji = undefined
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
events.$on('popup:open', ({name}) => {
|
||||
|
||||
if (name === 'create-folder' && ! this.$isMobile())
|
||||
this.$nextTick(() => this.$refs.input.focus())
|
||||
})
|
||||
export default {
|
||||
name: 'CreateFolderPopup',
|
||||
components: {
|
||||
AppInputSwitch,
|
||||
SwitchInput,
|
||||
EmojiPicker,
|
||||
AppInputText,
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
ThumbnailItem,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
required,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
isEmoji: false,
|
||||
emoji: undefined,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async createFolder() {
|
||||
// Validate fields
|
||||
const isValid = await this.$refs.createForm.validate()
|
||||
|
||||
if (!isValid) return
|
||||
|
||||
await this.$store.dispatch('createFolder', {
|
||||
name: this.name,
|
||||
emoji: this.emoji,
|
||||
})
|
||||
|
||||
this.$closePopup()
|
||||
|
||||
this.name = undefined
|
||||
this.isEmoji = false
|
||||
this.emoji = undefined
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
events.$on('popup:open', ({ name }) => {
|
||||
if (name === 'create-folder' && !this.$isMobile()) this.$nextTick(() => this.$refs.input.focus())
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,54 +1,43 @@
|
||||
<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">
|
||||
|
||||
<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')">
|
||||
</AppInputText>
|
||||
<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')"
|
||||
/>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
|
||||
</ValidationObserver>
|
||||
|
||||
<AppInputText v-if="token" :title="$t('popup_personal_token.your_token')" :is-last="true">
|
||||
<CopyInput size="small" :str="token['plainTextToken']" />
|
||||
|
||||
<InfoBox style="margin-bottom: 0; margin-top: 20px">
|
||||
<p v-html="$t('popup_personal_token.copy_token')"></p>
|
||||
</InfoBox>
|
||||
</AppInputText>
|
||||
<AppInputText v-if="token" :title="$t('popup_personal_token.your_token')" :is-last="true">
|
||||
<CopyInput size="small" :str="token['plainTextToken']" />
|
||||
|
||||
<InfoBox style="margin-bottom: 0; margin-top: 20px">
|
||||
<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"
|
||||
>
|
||||
<PopupActions v-if="!token">
|
||||
<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"
|
||||
>
|
||||
{{ $t('personal_token.create_token') }}
|
||||
<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,82 +45,79 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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 {required} from 'vee-validate/dist/rules'
|
||||
import {events} from '../../bus'
|
||||
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 { required } from 'vee-validate/dist/rules'
|
||||
import { events } from '../../bus'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: "CreatePersonalTokenPopup",
|
||||
components: {
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
AppInputText,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
CopyInput,
|
||||
required,
|
||||
InfoBox,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
name: undefined,
|
||||
token: undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async createTokenForm() {
|
||||
name: 'CreatePersonalTokenPopup',
|
||||
components: {
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
AppInputText,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
CopyInput,
|
||||
required,
|
||||
InfoBox,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
name: undefined,
|
||||
token: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async createTokenForm() {
|
||||
const isValid = await this.$refs.createToken.validate()
|
||||
|
||||
const isValid = await this.$refs.createToken.validate()
|
||||
if (!isValid) return
|
||||
|
||||
if (!isValid) return
|
||||
this.isLoading = true
|
||||
|
||||
this.isLoading = true
|
||||
axios
|
||||
.post('/api/user/tokens', {
|
||||
name: this.name,
|
||||
})
|
||||
.then((response) => {
|
||||
this.token = response.data
|
||||
|
||||
axios
|
||||
.post('/api/user/tokens', {
|
||||
name: this.name
|
||||
})
|
||||
.then(response => {
|
||||
this.token = response.data
|
||||
|
||||
events.$emit('reload-personal-access-tokens')
|
||||
})
|
||||
.catch(() => this.$isSomethingWrong())
|
||||
.finally(() => {
|
||||
this.isLoading = false
|
||||
this.name = undefined
|
||||
})
|
||||
},
|
||||
closePopup() {
|
||||
this.$closePopup()
|
||||
this.token = undefined
|
||||
}
|
||||
}
|
||||
events.$emit('reload-personal-access-tokens')
|
||||
})
|
||||
.catch(() => this.$isSomethingWrong())
|
||||
.finally(() => {
|
||||
this.isLoading = false
|
||||
this.name = undefined
|
||||
})
|
||||
},
|
||||
closePopup() {
|
||||
this.$closePopup()
|
||||
this.token = undefined
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/inapp-forms';
|
||||
@import '../../../sass/vuefilemanager/forms';
|
||||
|
||||
.dark {
|
||||
|
||||
.info-box {
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
}
|
||||
}
|
||||
@import '../../../sass/vuefilemanager/inapp-forms';
|
||||
@import '../../../sass/vuefilemanager/forms';
|
||||
|
||||
.dark {
|
||||
.info-box {
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,55 +1,44 @@
|
||||
<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"
|
||||
>
|
||||
{{ emoji.char }}
|
||||
</div>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import twemoji from 'twemoji'
|
||||
import {mapGetters} from "vuex";
|
||||
import twemoji from 'twemoji'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'Emoji',
|
||||
props: [
|
||||
'emoji',
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
isApple: false,
|
||||
sizeClass: undefined,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'config',
|
||||
]),
|
||||
transferEmoji() {
|
||||
return twemoji.parse(this.emoji.char, {
|
||||
folder: 'svg',
|
||||
ext: '.svg',
|
||||
attributes: () => ({
|
||||
loading: 'lazy'
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
export default {
|
||||
name: 'Emoji',
|
||||
props: ['emoji'],
|
||||
data() {
|
||||
return {
|
||||
isApple: false,
|
||||
sizeClass: undefined,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['config']),
|
||||
transferEmoji() {
|
||||
return twemoji.parse(this.emoji.char, {
|
||||
folder: 'svg',
|
||||
ext: '.svg',
|
||||
attributes: () => ({
|
||||
loading: 'lazy',
|
||||
}),
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.emoji {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
font-size: inherit;
|
||||
}
|
||||
</style>
|
||||
.emoji {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
font-size: inherit;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,112 +1,114 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Search field -->
|
||||
<div class="mb-3 relative flex items-center">
|
||||
<!-- Search field -->
|
||||
<div class="relative mb-3 flex items-center">
|
||||
<!-- Selected emoji preview -->
|
||||
<div v-if="defaultEmoji" class="mr-3 select-none">
|
||||
<Emoji :emoji="defaultEmoji" class="text-5xl" />
|
||||
</div>
|
||||
|
||||
<!-- Selected emoji preview -->
|
||||
<div v-if="defaultEmoji" class="select-none mr-3">
|
||||
<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...')" />
|
||||
</div>
|
||||
|
||||
<!-- Search input -->
|
||||
<input @click="openList" v-model="query" class="focus-border-theme input-dark" type="text" :placeholder="$t('Select or search emoji icon...')">
|
||||
</div>
|
||||
|
||||
<!-- Spinner -->
|
||||
<div v-if="isOpen && !isLoaded" class="relative h-20 select-none">
|
||||
<Spinner />
|
||||
</div>
|
||||
<!-- Spinner -->
|
||||
<div v-if="isOpen && !isLoaded" class="relative h-20 select-none">
|
||||
<Spinner />
|
||||
</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="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>
|
||||
|
||||
<!-- 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}">
|
||||
<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="mt-4 mb-2 block text-sm font-bold">
|
||||
{{ name }}
|
||||
</label>
|
||||
<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>
|
||||
|
||||
<!-- 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">
|
||||
{{ 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">
|
||||
<Emoji :emoji="emoji" class="text-4xl" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Searched emojis -->
|
||||
<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>
|
||||
|
||||
<!-- 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">
|
||||
<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">
|
||||
{{ $t('There is nothing :(') }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- Not found -->
|
||||
<span class="ml-2 text-sm font-bold" v-if="filteredEmojis.length === 0 && query !== undefined">
|
||||
{{ $t('There is nothing :(') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Spinner from "../FilesView/Spinner";
|
||||
import Emoji from "./Emoji";
|
||||
import {debounce, groupBy} from 'lodash'
|
||||
import {XIcon} from 'vue-feather-icons'
|
||||
import {mapGetters} from 'vuex'
|
||||
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',
|
||||
]),
|
||||
allEmoji() {
|
||||
return groupBy(this.emojis.list, 'group')
|
||||
},
|
||||
...mapGetters(['emojis']),
|
||||
allEmoji() {
|
||||
return groupBy(this.emojis.list, 'group')
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
query: undefined,
|
||||
query: undefined,
|
||||
filteredEmojis: [],
|
||||
isOpen: false,
|
||||
isLoaded: false,
|
||||
groupInView: 'Smileys & Emotion',
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
query: debounce(function (val) {
|
||||
// Clear search results
|
||||
this.filteredEmojis = []
|
||||
watch: {
|
||||
query: debounce(function (val) {
|
||||
// Clear search results
|
||||
this.filteredEmojis = []
|
||||
|
||||
// Reset query
|
||||
if (val === '' || val === undefined) return
|
||||
// Reset query
|
||||
if (val === '' || val === undefined) return
|
||||
|
||||
// Filter emojis by query
|
||||
this.filteredEmojis = this.emojis.list.filter(emoji => emoji.name.includes(val.toLowerCase()))
|
||||
// Filter emojis by query
|
||||
this.filteredEmojis = this.emojis.list.filter((emoji) => emoji.name.includes(val.toLowerCase()))
|
||||
|
||||
if (this.filteredEmojis.length === 0) {
|
||||
console.log('empty');
|
||||
}
|
||||
}, 200),
|
||||
},
|
||||
if (this.filteredEmojis.length === 0) {
|
||||
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()
|
||||
|
||||
@@ -117,50 +119,51 @@ export default {
|
||||
})
|
||||
}, 300),
|
||||
scrollToGroup(name) {
|
||||
let groupBar = document.getElementById('group-bar')
|
||||
let groupBox = document.getElementById('group-box')
|
||||
let groupBar = document.getElementById('group-bar')
|
||||
let groupBox = document.getElementById('group-box')
|
||||
let group = document.getElementById(`group-${name}`)
|
||||
|
||||
groupBox.scrollTo({
|
||||
top: group.offsetTop - groupBar.clientHeight - 5,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
groupBox.scrollTo({
|
||||
top: group.offsetTop - groupBar.clientHeight - 5,
|
||||
left: 0,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
|
||||
this.groupInView = name
|
||||
},
|
||||
openList() {
|
||||
// Open list if it's not opened
|
||||
if (! this.isOpen) this.isOpen = true
|
||||
// Open list if it's not opened
|
||||
if (!this.isOpen) this.isOpen = true
|
||||
|
||||
// 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'
|
||||
},
|
||||
setEmoji(value) {
|
||||
this.query = undefined
|
||||
this.query = undefined
|
||||
this.isOpen = false
|
||||
|
||||
this.$emit('input', value)
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// Open list of there isn't set any emoji
|
||||
if (! this.defaultEmoji) this.openList()
|
||||
}
|
||||
// Open list of there isn't set any emoji
|
||||
if (!this.defaultEmoji) this.openList()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -16,59 +16,62 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { FileIcon, FileTextIcon, SettingsIcon } from 'vue-feather-icons'
|
||||
import { FileIcon, FileTextIcon, SettingsIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'EmptyPageContent',
|
||||
props: ['icon','title','description'],
|
||||
components: {
|
||||
SettingsIcon,
|
||||
FileTextIcon,
|
||||
FileIcon,
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: 'EmptyPageContent',
|
||||
props: ['icon', 'title', 'description'],
|
||||
components: {
|
||||
SettingsIcon,
|
||||
FileTextIcon,
|
||||
FileIcon,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.empty-page-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
.empty-page-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
.content {
|
||||
.content {
|
||||
margin: 0 auto;
|
||||
max-width: 360px;
|
||||
|
||||
/deep/ .button-base {
|
||||
margin: 0 auto;
|
||||
max-width: 360px;
|
||||
|
||||
/deep/ .button-base {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
path, polyline, line, circle {
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include font-size(23);
|
||||
font-weight: 700;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include font-size(16);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
path,
|
||||
polyline,
|
||||
line,
|
||||
circle {
|
||||
stroke: $theme;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include font-size(23);
|
||||
font-weight: 700;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include font-size(16);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,56 +1,37 @@
|
||||
<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="w-full overflow-hidden rounded-lg object-cover shadow-lg"
|
||||
/>
|
||||
|
||||
<!--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"
|
||||
/>
|
||||
<!--Audio-->
|
||||
<audio v-else-if="singleFile.data.type === 'audio'" :src="singleFile.data.attributes.file_url" controlsList="nodownload" controls class="w-full"></audio>
|
||||
|
||||
<!--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-->
|
||||
<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>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from 'vuex'
|
||||
import {includes} from 'lodash'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { includes } from 'lodash'
|
||||
|
||||
export default {
|
||||
name: 'FilePreview',
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'clipboard',
|
||||
]),
|
||||
singleFile() {
|
||||
return this.clipboard[0]
|
||||
},
|
||||
canBePreview() {
|
||||
return this.singleFile && !includes([
|
||||
'folder', 'file'
|
||||
], this.singleFile.data.type)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
export default {
|
||||
name: 'FilePreview',
|
||||
computed: {
|
||||
...mapGetters(['clipboard']),
|
||||
singleFile() {
|
||||
return this.clipboard[0]
|
||||
},
|
||||
canBePreview() {
|
||||
return this.singleFile && !includes(['folder', 'file'], this.singleFile.data.type)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,62 +1,47 @@
|
||||
<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',
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
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()
|
||||
export default {
|
||||
name: 'AvatarInput',
|
||||
props: ['avatar'],
|
||||
data() {
|
||||
return {
|
||||
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()
|
||||
|
||||
if (['png', 'jpg', 'jpeg'].includes(extension)) {
|
||||
let file = event.target.files[0],
|
||||
reader = new FileReader()
|
||||
if (['png', 'jpg', 'jpeg'].includes(extension)) {
|
||||
let file = event.target.files[0],
|
||||
reader = new FileReader()
|
||||
|
||||
reader.onload = () => (this.imagePreview = reader.result)
|
||||
reader.onload = () => (this.imagePreview = reader.result)
|
||||
|
||||
reader.readAsDataURL(file)
|
||||
reader.readAsDataURL(file)
|
||||
|
||||
// Update user avatar
|
||||
this.$updateImage('/user/settings', 'avatar', event.target.files[0])
|
||||
} else {
|
||||
alert(this.$t('validation_errors.wrong_image'))
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// If there is default image then load
|
||||
if (this.avatar) this.imagePreview = this.avatar
|
||||
}
|
||||
}
|
||||
// Update user avatar
|
||||
this.$updateImage('/user/settings', 'avatar', event.target.files[0])
|
||||
} else {
|
||||
alert(this.$t('validation_errors.wrong_image'))
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// If there is default image then load
|
||||
if (this.avatar) this.imagePreview = this.avatar
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -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-->
|
||||
<!--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,
|
||||
@@ -31,12 +24,11 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
isCopiedLink: false,
|
||||
id: 'link-input-' + Math.floor(Math.random() * 10000000),
|
||||
id: 'link-input-' + Math.floor(Math.random() * 10000000),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
copyUrl() {
|
||||
|
||||
// Get input value
|
||||
let copyText = document.getElementById(this.id)
|
||||
|
||||
@@ -54,7 +46,7 @@ export default {
|
||||
setTimeout(() => {
|
||||
this.isCopiedLink = false
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,47 +1,63 @@
|
||||
<template>
|
||||
<div class="flex items-center relative">
|
||||
<input ref="sel" :value="item.data.relationships.shared.data.attributes.link" :id="id" type="text" class="pr-16 py-2 pl-3 text-sm focus-border-theme w-full dark:bg-2x-dark-foreground bg-light-background rounded-lg appearance-none border-transparent font-bold border" readonly>
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
ref="sel"
|
||||
:value="item.data.relationships.shared.data.attributes.link"
|
||||
:id="id"
|
||||
type="text"
|
||||
class="focus-border-theme w-full appearance-none rounded-lg border border-transparent bg-light-background py-2 pr-16 pl-3 text-sm font-bold dark:bg-2x-dark-foreground"
|
||||
readonly
|
||||
/>
|
||||
|
||||
<!--Copy icon-->
|
||||
<div class="flex items-center">
|
||||
<!--Copy icon-->
|
||||
<div class="flex items-center">
|
||||
<div @click="copyUrl" class="absolute right-9 p-1">
|
||||
<copy-icon v-if="! isCopiedLink" size="14" class="cursor-pointer hover-text-theme vue-feather"/>
|
||||
<check-icon v-if="isCopiedLink" size="14" class="cursor-pointer hover-text-theme vue-feather"/>
|
||||
<copy-icon v-if="!isCopiedLink" size="14" class="hover-text-theme vue-feather cursor-pointer" />
|
||||
<check-icon v-if="isCopiedLink" size="14" class="hover-text-theme vue-feather cursor-pointer" />
|
||||
</div>
|
||||
<div @click.stop.prevent="moreOptions" class="absolute right-2.5 p-1">
|
||||
<more-horizontal-icon size="14" class="cursor-pointer hover-text-theme vue-feather" />
|
||||
<more-horizontal-icon size="14" class="hover-text-theme vue-feather cursor-pointer" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--Hidden options-->
|
||||
<ul v-if="isOpenedMoreOptions" class="shadow-xl rounded-lg absolute top-12 left-0 right-0 z-10 overflow-y-auto overflow-x-hidden select-none">
|
||||
<li @click="getQrCode" class="flex items-center py-2.5 px-5 block cursor-pointer dark:bg-2x-dark-foreground dark:hover:bg-4x-dark-foreground hover:bg-light-background bg-white">
|
||||
<div class="w-8">
|
||||
<camera-icon size="14" />
|
||||
</div>
|
||||
<span class="text-sm font-bold">
|
||||
{{ $t('Get QR Code') }}
|
||||
</span>
|
||||
</li>
|
||||
<li @click="sendViaEmail" class="flex items-center py-2.5 px-5 block cursor-pointer dark:bg-2x-dark-foreground dark:hover:bg-4x-dark-foreground hover:bg-light-background bg-white">
|
||||
<div class="w-8">
|
||||
<send-icon size="14" />
|
||||
</div>
|
||||
<span class="text-sm font-bold">
|
||||
{{ $t('sharelink.share_via_email') }}
|
||||
</span>
|
||||
</li>
|
||||
<li @click="copyIframe" class="flex items-center py-2.5 px-5 block cursor-pointer dark:bg-2x-dark-foreground dark:hover:bg-4x-dark-foreground hover:bg-light-background bg-white">
|
||||
<div class="w-8">
|
||||
<code-icon size="14" />
|
||||
</div>
|
||||
<span class="text-sm font-bold">
|
||||
{{ $t('sharelink.copy_embed') }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<!--Hidden options-->
|
||||
<ul v-if="isOpenedMoreOptions" class="absolute top-12 left-0 right-0 z-10 select-none overflow-y-auto overflow-x-hidden rounded-lg shadow-xl">
|
||||
<li
|
||||
@click="getQrCode"
|
||||
class="block flex cursor-pointer items-center bg-white py-2.5 px-5 hover:bg-light-background dark:bg-2x-dark-foreground dark:hover:bg-4x-dark-foreground"
|
||||
>
|
||||
<div class="w-8">
|
||||
<camera-icon size="14" />
|
||||
</div>
|
||||
<span class="text-sm font-bold">
|
||||
{{ $t('Get QR Code') }}
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
@click="sendViaEmail"
|
||||
class="block flex cursor-pointer items-center bg-white py-2.5 px-5 hover:bg-light-background dark:bg-2x-dark-foreground dark:hover:bg-4x-dark-foreground"
|
||||
>
|
||||
<div class="w-8">
|
||||
<send-icon size="14" />
|
||||
</div>
|
||||
<span class="text-sm font-bold">
|
||||
{{ $t('sharelink.share_via_email') }}
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
@click="copyIframe"
|
||||
class="block flex cursor-pointer items-center bg-white py-2.5 px-5 hover:bg-light-background dark:bg-2x-dark-foreground dark:hover:bg-4x-dark-foreground"
|
||||
>
|
||||
<div class="w-8">
|
||||
<code-icon size="14" />
|
||||
</div>
|
||||
<span class="text-sm font-bold">
|
||||
{{ $t('sharelink.copy_embed') }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<textarea v-model="iframeCode" ref="iframe" class="absolute right-full opacity-0 pointer-events-none"></textarea>
|
||||
<textarea v-model="iframeCode" ref="iframe" class="pointer-events-none absolute right-full opacity-0"></textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -51,67 +67,64 @@ import { events } from '../../../bus'
|
||||
|
||||
export default {
|
||||
name: 'CopyShareLink',
|
||||
props: [
|
||||
'item',
|
||||
],
|
||||
props: ['item'],
|
||||
components: {
|
||||
MoreHorizontalIcon,
|
||||
CameraIcon,
|
||||
MoreHorizontalIcon,
|
||||
CameraIcon,
|
||||
CheckIcon,
|
||||
CopyIcon,
|
||||
CodeIcon,
|
||||
SendIcon
|
||||
CopyIcon,
|
||||
CodeIcon,
|
||||
SendIcon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: 'link-input-' + Math.floor(Math.random() * 10000000),
|
||||
iframeCode: '',
|
||||
id: 'link-input-' + Math.floor(Math.random() * 10000000),
|
||||
iframeCode: '',
|
||||
isCopiedLink: false,
|
||||
isOpenedMoreOptions: false,
|
||||
isOpenedMoreOptions: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
moreOptions() {
|
||||
this.isOpenedMoreOptions = ! this.isOpenedMoreOptions
|
||||
this.isOpenedMoreOptions = !this.isOpenedMoreOptions
|
||||
},
|
||||
getQrCode() {
|
||||
events.$emit('popup:open', {
|
||||
name: 'share-edit',
|
||||
item: this.item,
|
||||
section: 'qr-code',
|
||||
})
|
||||
getQrCode() {
|
||||
events.$emit('popup:open', {
|
||||
name: 'share-edit',
|
||||
item: this.item,
|
||||
section: 'qr-code',
|
||||
})
|
||||
|
||||
this.isOpenedMoreOptions = false
|
||||
},
|
||||
sendViaEmail() {
|
||||
this.isOpenedMoreOptions = false
|
||||
},
|
||||
sendViaEmail() {
|
||||
events.$emit('popup:open', {
|
||||
name: 'share-edit',
|
||||
item: this.item,
|
||||
section: 'email-sharing',
|
||||
})
|
||||
|
||||
this.isOpenedMoreOptions = false
|
||||
this.isOpenedMoreOptions = false
|
||||
},
|
||||
copyIframe() {
|
||||
// generate iframe
|
||||
this.iframeCode = `<iframe src="${this.item.data.relationships.shared.link}" width="790" height="400" allowfullscreen frameborder="0"></iframe>`
|
||||
copyIframe() {
|
||||
// generate iframe
|
||||
this.iframeCode = `<iframe src="${this.item.data.relationships.shared.link}" width="790" height="400" allowfullscreen frameborder="0"></iframe>`
|
||||
|
||||
let copyText = this.$refs.iframe
|
||||
let copyText = this.$refs.iframe
|
||||
|
||||
copyText.select()
|
||||
copyText.setSelectionRange(0, 99999)
|
||||
copyText.select()
|
||||
copyText.setSelectionRange(0, 99999)
|
||||
|
||||
document.execCommand('copy')
|
||||
document.execCommand('copy')
|
||||
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your web insert code was copied'),
|
||||
})
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your web insert code was copied'),
|
||||
})
|
||||
|
||||
this.isOpenedMoreOptions = false
|
||||
this.isOpenedMoreOptions = false
|
||||
},
|
||||
copyUrl() {
|
||||
|
||||
// Get input value
|
||||
var copyText = document.getElementById(this.id)
|
||||
|
||||
@@ -129,7 +142,7 @@ export default {
|
||||
setTimeout(() => {
|
||||
this.isCopiedLink = false
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,60 +1,58 @@
|
||||
<template>
|
||||
<div class="flex items-center mb-8">
|
||||
<edit-2-icon v-if="!icon" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<frown-icon v-if="icon === 'frown'" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<file-text-icon v-if="icon === 'file-text'" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<dollar-sign-icon v-if="icon === 'dollar'" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<credit-card-icon v-if="icon === 'credit-card'" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<bar-chart-icon v-if="icon === 'bar-chart'" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<settings-icon v-if="icon === 'settings'" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<hard-drive-icon v-if="icon === 'hard-drive'" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<smartphone-icon v-if="icon === 'smartphone'" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<shield-icon v-if="icon === 'shield'" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<bell-icon v-if="icon === 'bell'" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<key-icon v-if="icon === 'key'" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<users-icon v-if="icon === 'users'" size="22" class="mr-3 vue-feather text-theme dark-text-theme" />
|
||||
<b class="font-bold dark:text-gray-200 sm:text-lg text-md">
|
||||
<div class="mb-8 flex items-center">
|
||||
<edit-2-icon v-if="!icon" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<frown-icon v-if="icon === 'frown'" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<file-text-icon v-if="icon === 'file-text'" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<dollar-sign-icon v-if="icon === 'dollar'" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<credit-card-icon v-if="icon === 'credit-card'" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<bar-chart-icon v-if="icon === 'bar-chart'" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<settings-icon v-if="icon === 'settings'" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<hard-drive-icon v-if="icon === 'hard-drive'" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<smartphone-icon v-if="icon === 'smartphone'" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<shield-icon v-if="icon === 'shield'" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<bell-icon v-if="icon === 'bell'" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<key-icon v-if="icon === 'key'" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<users-icon v-if="icon === 'users'" size="22" class="vue-feather text-theme dark-text-theme mr-3" />
|
||||
<b class="text-md font-bold dark:text-gray-200 sm:text-lg">
|
||||
<slot></slot>
|
||||
</b>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
UsersIcon,
|
||||
ShieldIcon,
|
||||
CreditCardIcon,
|
||||
DollarSignIcon,
|
||||
SmartphoneIcon,
|
||||
HardDriveIcon,
|
||||
BarChartIcon,
|
||||
SettingsIcon,
|
||||
FileTextIcon,
|
||||
FrownIcon,
|
||||
Edit2Icon,
|
||||
BellIcon,
|
||||
KeyIcon,
|
||||
} from 'vue-feather-icons'
|
||||
import {
|
||||
UsersIcon,
|
||||
ShieldIcon,
|
||||
CreditCardIcon,
|
||||
DollarSignIcon,
|
||||
SmartphoneIcon,
|
||||
HardDriveIcon,
|
||||
BarChartIcon,
|
||||
SettingsIcon,
|
||||
FileTextIcon,
|
||||
FrownIcon,
|
||||
Edit2Icon,
|
||||
BellIcon,
|
||||
KeyIcon,
|
||||
} from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'FormLabel',
|
||||
props: [
|
||||
'icon'
|
||||
],
|
||||
components: {
|
||||
UsersIcon,
|
||||
CreditCardIcon,
|
||||
DollarSignIcon,
|
||||
SmartphoneIcon,
|
||||
HardDriveIcon,
|
||||
BarChartIcon,
|
||||
SettingsIcon,
|
||||
FileTextIcon,
|
||||
ShieldIcon,
|
||||
FrownIcon,
|
||||
Edit2Icon,
|
||||
BellIcon,
|
||||
KeyIcon,
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: 'FormLabel',
|
||||
props: ['icon'],
|
||||
components: {
|
||||
UsersIcon,
|
||||
CreditCardIcon,
|
||||
DollarSignIcon,
|
||||
SmartphoneIcon,
|
||||
HardDriveIcon,
|
||||
BarChartIcon,
|
||||
SettingsIcon,
|
||||
FileTextIcon,
|
||||
ShieldIcon,
|
||||
FrownIcon,
|
||||
Edit2Icon,
|
||||
BellIcon,
|
||||
KeyIcon,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,21 +4,11 @@
|
||||
<x-icon size="14" class="close-icon text-theme" />
|
||||
</div>
|
||||
|
||||
<input
|
||||
ref="file"
|
||||
type="file"
|
||||
@change="showImagePreview($event)"
|
||||
class="dummy"
|
||||
/>
|
||||
<img
|
||||
ref="image"
|
||||
:src="imagePreview"
|
||||
class="image-preview"
|
||||
v-if="imagePreview"
|
||||
/>
|
||||
<input ref="file" type="file" @change="showImagePreview($event)" class="dummy" />
|
||||
<img ref="image" :src="imagePreview" class="image-preview" v-if="imagePreview" />
|
||||
|
||||
<div class="dropzone-message" v-show="! isData">
|
||||
<image-icon size="28" class="icon-upload text-theme mx-auto mb-1"/>
|
||||
<div class="dropzone-message" v-show="!isData">
|
||||
<image-icon size="28" class="icon-upload text-theme mx-auto mb-1" />
|
||||
<span class="dropzone-title">
|
||||
{{ $t('input_image.title') }}
|
||||
</span>
|
||||
@@ -30,179 +20,179 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { XIcon, ImageIcon } from 'vue-feather-icons'
|
||||
import { XIcon, ImageIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'ImageInput',
|
||||
props: [
|
||||
'image', 'error'
|
||||
],
|
||||
components: {
|
||||
ImageIcon,
|
||||
XIcon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imagePreview: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isData() {
|
||||
return typeof this.imagePreview === 'undefined' || this.imagePreview === '' ? false : true
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
resetImage() {
|
||||
this.imagePreview = undefined
|
||||
this.$emit('input', undefined)
|
||||
},
|
||||
showImagePreview(event) {
|
||||
const imgPath = event.target.files[0].name,
|
||||
extn = imgPath
|
||||
.substring(imgPath.lastIndexOf('.') + 1)
|
||||
.toLowerCase()
|
||||
|
||||
if (['png', 'jpg', 'jpeg', 'svg'].includes(extn)) {
|
||||
const file = event.target.files[0],
|
||||
reader = new FileReader()
|
||||
|
||||
reader.onload = () => (this.imagePreview = reader.result)
|
||||
|
||||
reader.readAsDataURL(file)
|
||||
|
||||
// Update user avatar
|
||||
this.$emit('input', event.target.files[0])
|
||||
} else {
|
||||
alert( this.$t('validation_errors.wrong_image') )
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// If has default image then load
|
||||
if (this.image) this.imagePreview = this.image
|
||||
export default {
|
||||
name: 'ImageInput',
|
||||
props: ['image', 'error'],
|
||||
components: {
|
||||
ImageIcon,
|
||||
XIcon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imagePreview: undefined,
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isData() {
|
||||
return typeof this.imagePreview === 'undefined' || this.imagePreview === '' ? false : true
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
resetImage() {
|
||||
this.imagePreview = undefined
|
||||
this.$emit('input', undefined)
|
||||
},
|
||||
showImagePreview(event) {
|
||||
const imgPath = event.target.files[0].name,
|
||||
extn = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase()
|
||||
|
||||
if (['png', 'jpg', 'jpeg', 'svg'].includes(extn)) {
|
||||
const file = event.target.files[0],
|
||||
reader = new FileReader()
|
||||
|
||||
reader.onload = () => (this.imagePreview = reader.result)
|
||||
|
||||
reader.readAsDataURL(file)
|
||||
|
||||
// Update user avatar
|
||||
this.$emit('input', event.target.files[0])
|
||||
} else {
|
||||
alert(this.$t('validation_errors.wrong_image'))
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// If has default image then load
|
||||
if (this.image) this.imagePreview = this.image
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.dropzone {
|
||||
border: 1px dashed #a1abc2;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 175px;
|
||||
.dropzone {
|
||||
border: 1px dashed #a1abc2;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 175px;
|
||||
|
||||
&.is-error {
|
||||
border: 2px dashed rgba(253, 57, 122, 0.3);
|
||||
&.is-error {
|
||||
border: 2px dashed rgba(253, 57, 122, 0.3);
|
||||
|
||||
.dropzone-title {
|
||||
color: $danger;
|
||||
.dropzone-title {
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
.icon-upload {
|
||||
rect,
|
||||
circle,
|
||||
polyline {
|
||||
stroke: $danger;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-upload {
|
||||
rect, circle, polyline {
|
||||
stroke: $danger
|
||||
input[type='file'] {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.image-preview {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
left: 0;
|
||||
padding: 25px;
|
||||
display: block;
|
||||
|
||||
&.fit-image {
|
||||
object-fit: cover;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.dropzone-message {
|
||||
padding: 50px 0;
|
||||
width: 100%;
|
||||
|
||||
.icon-upload {
|
||||
rect,
|
||||
circle,
|
||||
polyline {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.dropzone-title {
|
||||
@include font-size(16);
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropzone-description {
|
||||
color: $text_muted;
|
||||
@include font-size(12);
|
||||
}
|
||||
}
|
||||
|
||||
.reset-image {
|
||||
z-index: 2;
|
||||
background: white;
|
||||
border-radius: 50px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
@include transform(translateY(-50%) translateX(50%));
|
||||
padding: 0px 4px;
|
||||
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12);
|
||||
|
||||
.close-icon {
|
||||
vertical-align: middle;
|
||||
|
||||
line {
|
||||
path {
|
||||
fill: $text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input[type='file'] {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.image-preview {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
left: 0;
|
||||
padding: 25px;
|
||||
display: block;
|
||||
|
||||
&.fit-image {
|
||||
object-fit: cover;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
}
|
||||
.dark {
|
||||
.dropzone {
|
||||
border-color: rgba(white, 0.2);
|
||||
|
||||
.dropzone-message {
|
||||
padding: 50px 0;
|
||||
width: 100%;
|
||||
|
||||
.icon-upload {
|
||||
rect, circle, polyline {
|
||||
color: inherit
|
||||
path,
|
||||
polyline,
|
||||
line {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.dropzone-title {
|
||||
@include font-size(16);
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropzone-description {
|
||||
color: $text_muted;
|
||||
@include font-size(12);
|
||||
}
|
||||
}
|
||||
|
||||
.reset-image {
|
||||
z-index: 2;
|
||||
background: white;
|
||||
border-radius: 50px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
@include transform(translateY(-50%) translateX(50%));
|
||||
padding: 0px 4px;
|
||||
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12);
|
||||
|
||||
.close-icon {
|
||||
vertical-align: middle;
|
||||
|
||||
line {
|
||||
path {
|
||||
fill: $text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.dropzone {
|
||||
border-color: rgba(white, 0.2);
|
||||
|
||||
.dropzone-message {
|
||||
|
||||
.icon-upload {
|
||||
path, polyline, line {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.dropzone-description {
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,27 +5,91 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'InfoBox',
|
||||
props: ['type']
|
||||
}
|
||||
export default {
|
||||
name: 'InfoBox',
|
||||
props: ['type'],
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../sass/vuefilemanager/variables";
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.info-box {
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 32px;
|
||||
background: $light_background;
|
||||
text-align: left;
|
||||
|
||||
&.error {
|
||||
background: rgba($danger, 0.1);
|
||||
|
||||
p,
|
||||
a {
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
font-weight: 600;
|
||||
|
||||
/deep/ a {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/deep/ b {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
b {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 700;
|
||||
@include font-size(15);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-top: 15px;
|
||||
display: block;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
.info-box {
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 32px;
|
||||
background: $light_background;
|
||||
text-align: left;
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.info-box {
|
||||
background: $dark_mode_foreground;
|
||||
|
||||
&.error {
|
||||
background: rgba($danger, 0.1);
|
||||
|
||||
p, a {
|
||||
p,
|
||||
a {
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
@@ -35,80 +99,14 @@
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
font-weight: 600;
|
||||
|
||||
/deep/ a {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/deep/ b {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
b {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 700;
|
||||
@include font-size(15);
|
||||
line-height: 1.6;
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-top: 15px;
|
||||
display: block;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
|
||||
.info-box {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.dark {
|
||||
|
||||
.info-box {
|
||||
background: $dark_mode_foreground;
|
||||
|
||||
&.error {
|
||||
background: rgba($danger, 0.1);
|
||||
|
||||
p, a {
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
ul {
|
||||
|
||||
li {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<!--<label class="input-label">{{ label }}:</label>-->
|
||||
<div class="input-wrapper focus-within-border-theme" :class="{'is-error' : isError}" @click="$refs.input.focus()">
|
||||
<!--<label class="input-label">{{ label }}:</label>-->
|
||||
<div class="input-wrapper focus-within-border-theme" :class="{ 'is-error': isError }" @click="$refs.input.focus()">
|
||||
<div class="email-list">
|
||||
<div class="email-tag bg-theme-100" :class="{'mb-offset': getCharactersLength > 45}" v-for="(email, i) in emails" :key="i">
|
||||
<div class="email-tag bg-theme-100" :class="{ 'mb-offset': getCharactersLength > 45 }" v-for="(email, i) in emails" :key="i">
|
||||
<span class="text-theme">{{ email }}</span>
|
||||
<x-icon @click="removeEmail(email)" class="icon" size="14"/>
|
||||
<x-icon @click="removeEmail(email)" class="icon" size="14" />
|
||||
</div>
|
||||
<input @keydown.delete=removeLastEmail($event) @keyup="handleEmail()" v-model="email" :size="inputSize" class="email-input" :placeholder="placeHolder" autocomplete="new-password" ref="input"/>
|
||||
<input
|
||||
@keydown.delete="removeLastEmail($event)"
|
||||
@keyup="handleEmail()"
|
||||
v-model="email"
|
||||
:size="inputSize"
|
||||
class="email-input"
|
||||
:placeholder="placeHolder"
|
||||
autocomplete="new-password"
|
||||
ref="input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span class="error-message" v-if="isError">{{ isError }}</span>
|
||||
@@ -24,74 +33,68 @@ export default {
|
||||
props: ['isError', 'label'],
|
||||
computed: {
|
||||
getCharactersLength() {
|
||||
return this.emails.join( '' ).length
|
||||
return this.emails.join('').length
|
||||
},
|
||||
placeHolder() {
|
||||
return !this.emails.length ? this.$t( 'shared_form.email_placeholder' ) : ''
|
||||
return !this.emails.length ? this.$t('shared_form.email_placeholder') : ''
|
||||
},
|
||||
inputSize() {
|
||||
return this.email && this.email.length > 14 ? this.email.length : 14
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
emails: [],
|
||||
email: undefined
|
||||
email: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
removeEmail( email ) {
|
||||
this.emails = this.emails.filter( item => item !== email )
|
||||
removeEmail(email) {
|
||||
this.emails = this.emails.filter((item) => item !== email)
|
||||
|
||||
// After romove email send new emails list to parent
|
||||
events.$emit( 'emailsInputValues', this.emails )
|
||||
events.$emit('emailsInputValues', this.emails)
|
||||
},
|
||||
removeLastEmail( event ) {
|
||||
|
||||
removeLastEmail(event) {
|
||||
// If is input empty and presse backspace remove last email from array
|
||||
if ( event.code === 'Backspace' && this.email === '' )
|
||||
this.emails.pop()
|
||||
if (event.code === 'Backspace' && this.email === '') this.emails.pop()
|
||||
},
|
||||
handleEmail() {
|
||||
|
||||
if ( this.email.length > 0 ) {
|
||||
if (this.email.length > 0) {
|
||||
// Get index of @ and last dot
|
||||
let lastDot = this.email.lastIndexOf( '.' )
|
||||
let at = this.email.indexOf( '@' )
|
||||
let lastDot = this.email.lastIndexOf('.')
|
||||
let at = this.email.indexOf('@')
|
||||
|
||||
// Check if is after @ some dot, if email have @ anf if dont have more like one
|
||||
if ( lastDot < at || at === -1 || this.email.match(/@/g).length > 1 ) return
|
||||
if (lastDot < at || at === -1 || this.email.match(/@/g).length > 1) return
|
||||
|
||||
// First email dont need to be separated by comma or space to be sended
|
||||
if( this.emails.length === 0 )
|
||||
events.$emit('emailsInputValues', [this.email])
|
||||
|
||||
if (this.emails.length === 0) events.$emit('emailsInputValues', [this.email])
|
||||
|
||||
// After come or backspace push the single email to array or emails
|
||||
if ( this.email.includes(',') || this.email.includes(' ') ) {
|
||||
|
||||
let email = this.email.replace( /[","," "]/, '' )
|
||||
if (this.email.includes(',') || this.email.includes(' ')) {
|
||||
let email = this.email.replace(/[","," "]/, '')
|
||||
|
||||
this.email = ''
|
||||
|
||||
// Push single email to aray of emails
|
||||
this.emails.push( email )
|
||||
this.emails.push(email)
|
||||
|
||||
events.$emit( 'emailsInputValues', this.emails )
|
||||
events.$emit('emailsInputValues', this.emails)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.input.focus()
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../../../sass/vuefilemanager/inapp-forms";
|
||||
@import '../../../../sass/vuefilemanager/inapp-forms';
|
||||
@import '../../../../sass/vuefilemanager/forms';
|
||||
|
||||
.input-label {
|
||||
@@ -158,25 +161,24 @@ export default {
|
||||
|
||||
.email-input {
|
||||
width: auto;
|
||||
border: none ;
|
||||
border: none;
|
||||
font-weight: 700;
|
||||
@include font-size(16);
|
||||
padding-left: 11px;
|
||||
|
||||
&::placeholder {
|
||||
color: rgba($text-muted, .5)
|
||||
color: rgba($text-muted, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.input-wrapper {
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
|
||||
.email-list {
|
||||
|
||||
.email-input {
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
color: $dark_mode_text_primary;
|
||||
|
||||
&::placeholder {
|
||||
|
||||
@@ -6,19 +6,12 @@
|
||||
<div @click="clearInput" v-if="query" class="icon">
|
||||
<x-icon class="pointer" size="19"></x-icon>
|
||||
</div>
|
||||
<input
|
||||
v-model="query"
|
||||
@input="$emit('input', query)"
|
||||
class="query focus-border-theme"
|
||||
type="text"
|
||||
name="searchInput"
|
||||
:placeholder="$t('search_translations')"
|
||||
/>
|
||||
<input v-model="query" @input="$emit('input', query)" class="query focus-border-theme" type="text" name="searchInput" :placeholder="$t('search_translations')" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {SearchIcon, XIcon} from 'vue-feather-icons'
|
||||
import { SearchIcon, XIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'SearchInput',
|
||||
@@ -110,5 +103,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,92 +1,79 @@
|
||||
<template>
|
||||
<div class="select-box">
|
||||
<div class="box-item active-bg-theme-100 active-border-theme"
|
||||
:class="{'active': item.value === input}"
|
||||
@click="getSelectedValue(item)"
|
||||
v-for="(item, i) in data" :key="i"
|
||||
>
|
||||
<div class="box-item active-bg-theme-100 active-border-theme" :class="{ active: item.value === input }" @click="getSelectedValue(item)" v-for="(item, i) in data" :key="i">
|
||||
<span class="box-value active-text-theme">{{ item.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SelectBoxInput',
|
||||
props: [
|
||||
'data',
|
||||
'value',
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
input: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getSelectedValue(item) {
|
||||
if (! this.input || this.input !== item.value)
|
||||
this.input = item.value
|
||||
else
|
||||
this.input = undefined
|
||||
|
||||
this.$emit('input', this.input)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.value)
|
||||
this.input = this.value
|
||||
export default {
|
||||
name: 'SelectBoxInput',
|
||||
props: ['data', 'value'],
|
||||
data() {
|
||||
return {
|
||||
input: undefined,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getSelectedValue(item) {
|
||||
if (!this.input || this.input !== item.value) this.input = item.value
|
||||
else this.input = undefined
|
||||
|
||||
this.$emit('input', this.input)
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.value) this.input = this.value
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
@import "../../../../sass/vuefilemanager/inapp-forms";
|
||||
@import "../../../../sass/vuefilemanager/forms";
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../../sass/vuefilemanager/inapp-forms';
|
||||
@import '../../../../sass/vuefilemanager/forms';
|
||||
|
||||
.select-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
.select-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.box-item {
|
||||
margin-bottom: 10px;
|
||||
padding: 12px 4px;
|
||||
text-align: center;
|
||||
background: $light_background;
|
||||
border-radius: 8px;
|
||||
font-weight: 700;
|
||||
border: 2px solid $light_background;
|
||||
cursor: pointer;
|
||||
flex-direction: column;
|
||||
flex-basis: 55px;
|
||||
|
||||
.box-value {
|
||||
@include font-size(15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
.select-box {
|
||||
.box-item {
|
||||
margin-bottom: 10px;
|
||||
padding: 12px 4px;
|
||||
text-align: center;
|
||||
background: $light_background;
|
||||
border-radius: 8px;
|
||||
font-weight: 700;
|
||||
border: 2px solid $light_background;
|
||||
cursor: pointer;
|
||||
flex-direction: column;
|
||||
flex-basis: 55px;
|
||||
|
||||
.box-value {
|
||||
@include font-size(15);
|
||||
}
|
||||
flex-basis: calc(34% - 10px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
.select-box {
|
||||
|
||||
.box-item {
|
||||
flex-basis: calc(34% - 10px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.dark {
|
||||
.select-box {
|
||||
|
||||
.box-item {
|
||||
border-color: $dark_mode_border_color;
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
}
|
||||
.dark {
|
||||
.select-box {
|
||||
.box-item {
|
||||
border-color: $dark_mode_border_color;
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
<template>
|
||||
<div class="select">
|
||||
|
||||
<!--Area-->
|
||||
<div class="input-area bg-light-background rounded-lg" :class="{'is-active': isOpen, 'is-error': isError}" @click="openMenu">
|
||||
|
||||
<div class="input-area rounded-lg bg-light-background" :class="{ 'is-active': isOpen, 'is-error': isError }" @click="openMenu">
|
||||
<!--If is selected-->
|
||||
<div class="selected w-full flex items-center" v-if="selected">
|
||||
<div class="selected flex w-full items-center" v-if="selected">
|
||||
<div class="option-icon" v-if="selected.icon">
|
||||
<user-icon v-if="selected.icon === 'user'" size="14" class="vue-feather text-theme" />
|
||||
<edit2-icon v-if="selected.icon === 'user-edit'" size="14" class="vue-feather text-theme" />
|
||||
</div>
|
||||
<span class="whitespace-nowrap w-full inline-block overflow-hidden text-ellipsis option-value pl-2">
|
||||
{{ selected.label }}
|
||||
</span>
|
||||
<span class="option-value inline-block w-full overflow-hidden text-ellipsis whitespace-nowrap pl-2">
|
||||
{{ selected.label }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!--If is empty-->
|
||||
<div class="not-selected" v-if="! selected">
|
||||
<div class="not-selected" v-if="!selected">
|
||||
<span class="option-value placehoder">{{ placeholder }}</span>
|
||||
</div>
|
||||
|
||||
@@ -27,7 +25,7 @@
|
||||
<transition name="slide-in">
|
||||
<div class="input-options rounded-lg" v-if="isOpen">
|
||||
<div v-if="options.length > 5" class="select-search">
|
||||
<input v-model="query" ref="search" type="text" :placeholder="$t('select_search_placeholder')" class="search-input focus-border-theme rounded-lg">
|
||||
<input v-model="query" ref="search" type="text" :placeholder="$t('select_search_placeholder')" class="search-input focus-border-theme rounded-lg" />
|
||||
</div>
|
||||
<ul class="option-list">
|
||||
<li class="option-item" @click="selectOption(option)" v-for="(option, i) in optionList" :key="i">
|
||||
@@ -44,126 +42,216 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ChevronDownIcon, Edit2Icon, UserIcon } from 'vue-feather-icons'
|
||||
import {debounce, omitBy} from "lodash"
|
||||
import { ChevronDownIcon, Edit2Icon, UserIcon } from 'vue-feather-icons'
|
||||
import { debounce, omitBy } from 'lodash'
|
||||
|
||||
export default {
|
||||
name:'SelectInput',
|
||||
props: [
|
||||
'placeholder',
|
||||
'options',
|
||||
'isError',
|
||||
'default',
|
||||
],
|
||||
components: {
|
||||
Edit2Icon,
|
||||
UserIcon,
|
||||
ChevronDownIcon
|
||||
export default {
|
||||
name: 'SelectInput',
|
||||
props: ['placeholder', 'options', 'isError', 'default'],
|
||||
components: {
|
||||
Edit2Icon,
|
||||
UserIcon,
|
||||
ChevronDownIcon,
|
||||
},
|
||||
watch: {
|
||||
query: debounce(function (val) {
|
||||
this.searchedResults = omitBy(this.options, (string) => {
|
||||
return !string.label.toLowerCase().includes(val.toLowerCase())
|
||||
})
|
||||
}, 200),
|
||||
},
|
||||
computed: {
|
||||
isSearching() {
|
||||
return this.searchedResults && this.query !== ''
|
||||
},
|
||||
watch: {
|
||||
query: debounce(function (val) {
|
||||
this.searchedResults = omitBy(this.options, string => {
|
||||
return !string.label.toLowerCase().includes(val.toLowerCase())
|
||||
})
|
||||
}, 200),
|
||||
optionList() {
|
||||
return this.isSearching ? this.searchedResults : this.options
|
||||
},
|
||||
computed: {
|
||||
isSearching() {
|
||||
return this.searchedResults && this.query !== ''
|
||||
},
|
||||
optionList() {
|
||||
return this.isSearching
|
||||
? this.searchedResults
|
||||
: this.options
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchedResults: undefined,
|
||||
selected: undefined,
|
||||
isOpen: false,
|
||||
query: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectOption(option) {
|
||||
|
||||
// Emit selected
|
||||
this.$emit('input', option.value)
|
||||
this.$emit('change', option.value)
|
||||
|
||||
// Get selected
|
||||
this.selected = option
|
||||
|
||||
// Close menu
|
||||
this.isOpen = false
|
||||
},
|
||||
openMenu() {
|
||||
this.isOpen = ! this.isOpen
|
||||
|
||||
if (this.isOpen) {
|
||||
this.$nextTick(() => this.$refs.search.focus());
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.default)
|
||||
this.selected = this.options.find(option => option.value === this.default)
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchedResults: undefined,
|
||||
selected: undefined,
|
||||
isOpen: false,
|
||||
query: '',
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectOption(option) {
|
||||
// Emit selected
|
||||
this.$emit('input', option.value)
|
||||
this.$emit('change', option.value)
|
||||
|
||||
// Get selected
|
||||
this.selected = option
|
||||
|
||||
// Close menu
|
||||
this.isOpen = false
|
||||
},
|
||||
openMenu() {
|
||||
this.isOpen = !this.isOpen
|
||||
|
||||
if (this.isOpen) {
|
||||
this.$nextTick(() => this.$refs.search.focus())
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.default) this.selected = this.options.find((option) => option.value === this.default)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
|
||||
/* TODO: refactor to the tailwind */
|
||||
/* TODO: refactor to the tailwind */
|
||||
|
||||
.select {
|
||||
position: relative;
|
||||
user-select: none;
|
||||
.select {
|
||||
position: relative;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.select-search {
|
||||
background: white;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding: 13px;
|
||||
|
||||
.search-input {
|
||||
border: 1px solid transparent;
|
||||
background: $light_background;
|
||||
@include transition(150ms);
|
||||
@include font-size(14);
|
||||
padding: 13px 20px;
|
||||
appearance: none;
|
||||
font-weight: 700;
|
||||
outline: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.input-options {
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.12);
|
||||
background: white;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
top: 65px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
max-height: 295px;
|
||||
overflow-y: auto;
|
||||
|
||||
.option-item {
|
||||
padding: 13px 20px;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: $theme;
|
||||
background: $light_background;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input-area {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
justify-content: space-between;
|
||||
@include transition(150ms);
|
||||
align-items: center;
|
||||
padding: 13px 20px;
|
||||
display: flex;
|
||||
outline: 0;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
|
||||
.chevron {
|
||||
@include transition(150ms);
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
//box-shadow: 0 0 7px rgba($theme, 0.3);
|
||||
|
||||
.chevron {
|
||||
@include transform(rotate(180deg));
|
||||
}
|
||||
}
|
||||
|
||||
&.is-error {
|
||||
border-color: $danger;
|
||||
box-shadow: 0 0 7px rgba($danger, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.option-icon {
|
||||
width: 20px;
|
||||
display: inline-block;
|
||||
@include font-size(10);
|
||||
}
|
||||
|
||||
.option-value {
|
||||
@include font-size(14);
|
||||
font-weight: 700;
|
||||
vertical-align: middle;
|
||||
|
||||
&.placehoder {
|
||||
color: rgba($text, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.slide-in-enter-active {
|
||||
transition: all 150ms ease;
|
||||
}
|
||||
|
||||
.slide-in-enter /* .list-leave-active below version 2.1.8 */ {
|
||||
opacity: 0;
|
||||
transform: translateY(-50px);
|
||||
}
|
||||
|
||||
.dark {
|
||||
.select-search {
|
||||
background: white;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding: 13px;
|
||||
background: $dark_mode_foreground;
|
||||
|
||||
.search-input {
|
||||
border: 1px solid transparent;
|
||||
background: $light_background;
|
||||
@include transition(150ms);
|
||||
@include font-size(14);
|
||||
padding: 13px 20px;
|
||||
appearance: none;
|
||||
font-weight: 700;
|
||||
outline: 0;
|
||||
width: 100%;
|
||||
background: $dark_mode_background;
|
||||
}
|
||||
}
|
||||
|
||||
.input-area {
|
||||
background: $dark_mode_foreground;
|
||||
border-color: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
.popup-wrapper {
|
||||
.input-area {
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
}
|
||||
}
|
||||
|
||||
.input-options {
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.12);
|
||||
background: white;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
top: 65px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
max-height: 295px;
|
||||
overflow-y: auto;
|
||||
background: $dark_mode_foreground;
|
||||
|
||||
.option-item {
|
||||
padding: 13px 20px;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
border-bottom: none;
|
||||
|
||||
&:hover {
|
||||
color: $theme;
|
||||
background: $light_background;
|
||||
background: lighten($dark_mode_foreground, 5%);
|
||||
|
||||
.option-icon {
|
||||
path,
|
||||
circle {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@@ -172,113 +260,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.input-area {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
justify-content: space-between;
|
||||
@include transition(150ms);
|
||||
align-items: center;
|
||||
padding: 13px 20px;
|
||||
display: flex;
|
||||
outline: 0;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
|
||||
.chevron {
|
||||
@include transition(150ms);
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
//box-shadow: 0 0 7px rgba($theme, 0.3);
|
||||
|
||||
.chevron {
|
||||
@include transform(rotate(180deg));
|
||||
}
|
||||
}
|
||||
|
||||
&.is-error {
|
||||
border-color: $danger;
|
||||
box-shadow: 0 0 7px rgba($danger, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.option-icon {
|
||||
width: 20px;
|
||||
display: inline-block;
|
||||
@include font-size(10);
|
||||
}
|
||||
|
||||
.option-value {
|
||||
@include font-size(14);
|
||||
font-weight: 700;
|
||||
vertical-align: middle;
|
||||
|
||||
&.placehoder {
|
||||
color: rgba($text, 0.5);
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.slide-in-enter-active {
|
||||
transition: all 150ms ease;
|
||||
}
|
||||
|
||||
.slide-in-enter /* .list-leave-active below version 2.1.8 */
|
||||
{
|
||||
opacity: 0;
|
||||
transform: translateY(-50px);
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
||||
.select-search {
|
||||
background: $dark_mode_foreground;
|
||||
|
||||
.search-input {
|
||||
background: $dark_mode_background;
|
||||
}
|
||||
}
|
||||
|
||||
.input-area {
|
||||
background: $dark_mode_foreground;
|
||||
border-color: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
.popup-wrapper {
|
||||
.input-area {
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
}
|
||||
}
|
||||
|
||||
.input-options {
|
||||
background: $dark_mode_foreground;
|
||||
|
||||
.option-item {
|
||||
border-bottom: none;
|
||||
|
||||
&:hover {
|
||||
background: lighten($dark_mode_foreground, 5%);
|
||||
|
||||
.option-icon {
|
||||
|
||||
path, circle {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.option-value {
|
||||
|
||||
&.placehoder {
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,147 +7,136 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SetupBox',
|
||||
props: ['title', 'description', 'theme'],
|
||||
}
|
||||
export default {
|
||||
name: 'SetupBox',
|
||||
props: ['title', 'description', 'theme'],
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.setup-box {
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 30px;
|
||||
.setup-box {
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 30px;
|
||||
|
||||
.title {
|
||||
@include font-size(21);
|
||||
margin-bottom: 5px;
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include font-size(15);
|
||||
line-height: 1.5;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&.base {
|
||||
background: $light_background;
|
||||
}
|
||||
|
||||
&.danger {
|
||||
background: $light_background;
|
||||
|
||||
.title {
|
||||
@include font-size(21);
|
||||
margin-bottom: 5px;
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
color: $danger;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ input {
|
||||
&[type='text'],
|
||||
&[type='number'],
|
||||
.input-area {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .input-area {
|
||||
background: white;
|
||||
}
|
||||
|
||||
/deep/ .form {
|
||||
margin-top: 20px;
|
||||
|
||||
&.block-form {
|
||||
max-width: 450px;
|
||||
|
||||
.single-line-form {
|
||||
display: flex;
|
||||
|
||||
.submit-button {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
.setup-box {
|
||||
/deep/ .form {
|
||||
&.block-form {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
min-width: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
.setup-box {
|
||||
padding: 15px;
|
||||
|
||||
.title {
|
||||
@include font-size(17);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include font-size(15);
|
||||
line-height: 1.5;
|
||||
margin-bottom: 20px;
|
||||
@include font-size(14);
|
||||
}
|
||||
|
||||
/deep/ .form.block-form {
|
||||
.single-line-form {
|
||||
display: block;
|
||||
|
||||
.submit-button {
|
||||
margin-left: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.setup-box {
|
||||
&.base {
|
||||
background: $light_background;
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
&.danger {
|
||||
background: $light_background;
|
||||
|
||||
.title {
|
||||
color: $danger;
|
||||
}
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
/deep/ input {
|
||||
|
||||
&[type='text'],
|
||||
&[type='number'],
|
||||
.input-area {
|
||||
background: white;
|
||||
background: $dark_mode_background;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .input-area {
|
||||
background: white;
|
||||
}
|
||||
|
||||
/deep/ .form {
|
||||
margin-top: 20px;
|
||||
|
||||
&.block-form {
|
||||
max-width: 450px;
|
||||
|
||||
.single-line-form {
|
||||
display: flex;
|
||||
|
||||
.submit-button {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
|
||||
.setup-box {
|
||||
|
||||
/deep/ .form {
|
||||
|
||||
&.block-form {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
min-width: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
|
||||
.setup-box {
|
||||
padding: 15px;
|
||||
|
||||
.title {
|
||||
@include font-size(17);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include font-size(14);
|
||||
}
|
||||
|
||||
/deep/ .form.block-form {
|
||||
|
||||
.single-line-form {
|
||||
display: block;
|
||||
|
||||
.submit-button {
|
||||
margin-left: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.dark {
|
||||
|
||||
.setup-box {
|
||||
|
||||
&.base {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
&.danger {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
/deep/ input {
|
||||
|
||||
&[type='text'],
|
||||
&[type='number'],
|
||||
.input-area {
|
||||
background: $dark_mode_background;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .input-area {
|
||||
background: $dark_mode_background;
|
||||
}
|
||||
background: $dark_mode_background;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
<template>
|
||||
<div class="input-wrapper">
|
||||
<div class="switch-content">
|
||||
<label class="input-label" v-if="label">
|
||||
{{ label }}:
|
||||
</label>
|
||||
<label class="input-label" v-if="label"> {{ label }}: </label>
|
||||
<small class="input-info" v-if="info">
|
||||
{{ info }}
|
||||
</small>
|
||||
{{ info }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="switch-content text-right">
|
||||
<div
|
||||
class="switch"
|
||||
:class="{ active: state }"
|
||||
@click="changeState"
|
||||
>
|
||||
<div class="switch" :class="{ active: state }" @click="changeState">
|
||||
<div class="switch-button"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -22,93 +16,86 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SwitchInput',
|
||||
props: [
|
||||
'label',
|
||||
'name',
|
||||
'state',
|
||||
'info',
|
||||
'input',
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
isSwitched: undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeState() {
|
||||
this.isSwitched = !this.isSwitched
|
||||
this.$emit('input', this.isSwitched)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.isSwitched = this.state
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: 'SwitchInput',
|
||||
props: ['label', 'name', 'state', 'info', 'input'],
|
||||
data() {
|
||||
return {
|
||||
isSwitched: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeState() {
|
||||
this.isSwitched = !this.isSwitched
|
||||
this.$emit('input', this.isSwitched)
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isSwitched = this.state
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
.input-label {
|
||||
color: $text;
|
||||
}
|
||||
.input-label {
|
||||
color: $text;
|
||||
}
|
||||
|
||||
.switch-content {
|
||||
width: 100%;
|
||||
.switch-content {
|
||||
width: 100%;
|
||||
|
||||
&:last-child {
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:last-child {
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.switch {
|
||||
width: 50px;
|
||||
height: 28px;
|
||||
border-radius: 50px;
|
||||
display: block;
|
||||
background: #f1f1f5;
|
||||
position: relative;
|
||||
@include transition;
|
||||
.switch {
|
||||
width: 50px;
|
||||
height: 28px;
|
||||
border-radius: 50px;
|
||||
display: block;
|
||||
background: #f1f1f5;
|
||||
position: relative;
|
||||
@include transition;
|
||||
|
||||
.switch-button {
|
||||
@include transition;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 50px;
|
||||
display: block;
|
||||
background: white;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
box-shadow: 0 2px 4px rgba(37, 38, 94, 0.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
.switch-button {
|
||||
@include transition;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 50px;
|
||||
display: block;
|
||||
background: white;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
box-shadow: 0 2px 4px rgba(37, 38, 94, 0.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.active {
|
||||
&.active {
|
||||
.switch-button {
|
||||
left: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.switch-button {
|
||||
left: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dark {
|
||||
.switch {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
.dark {
|
||||
.switch {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
.popup-wrapper {
|
||||
.switch {
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
}
|
||||
}
|
||||
}
|
||||
.popup-wrapper {
|
||||
.switch {
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,23 +3,23 @@
|
||||
<div id="loader" v-show="isLoading">
|
||||
<Spinner></Spinner>
|
||||
</div>
|
||||
<slot v-show="! isLoading"></slot>
|
||||
<slot v-show="!isLoading"></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Spinner from "../../FilesView/Spinner";
|
||||
import Spinner from '../../FilesView/Spinner'
|
||||
|
||||
export default {
|
||||
name: 'PageTab',
|
||||
props: ['isLoading'],
|
||||
components: {
|
||||
Spinner,
|
||||
},
|
||||
}
|
||||
export default {
|
||||
name: 'PageTab',
|
||||
props: ['isLoading'],
|
||||
components: {
|
||||
Spinner,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
</style>
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
</style>
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PageTabGroup',
|
||||
}
|
||||
export default {
|
||||
name: 'PageTabGroup',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.page-tab-group {
|
||||
margin-bottom: 65px;
|
||||
}
|
||||
.page-tab-group {
|
||||
margin-bottom: 65px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ListInfo',
|
||||
}
|
||||
export default {
|
||||
name: 'ListInfo',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
</style>
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
<template>
|
||||
<div class="mb-4">
|
||||
<small class="text-theme font-bold text-xs block">
|
||||
{{ title }}
|
||||
</small>
|
||||
<b v-if="content" class="inline-block font-bold text-sm">
|
||||
{{ content }}
|
||||
</b>
|
||||
<small class="text-theme block text-xs font-bold">
|
||||
{{ title }}
|
||||
</small>
|
||||
<b v-if="content" class="inline-block text-sm font-bold">
|
||||
{{ content }}
|
||||
</b>
|
||||
|
||||
<slot v-if="$slots.default" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ListInfoItem',
|
||||
props: [
|
||||
'content',
|
||||
'title',
|
||||
]
|
||||
}
|
||||
export default {
|
||||
name: 'ListInfoItem',
|
||||
props: ['content', 'title'],
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,37 +1,35 @@
|
||||
<template>
|
||||
<MenuMobile name="user-navigation">
|
||||
|
||||
<!--User avatar-->
|
||||
<UserHeadline v-if="!clickedSubmenu" class="p-5 pb-3" />
|
||||
|
||||
<!--User estimate-->
|
||||
<div v-if="config.subscriptionType === 'metered' && user && !clickedSubmenu" class="block px-5 pt-2">
|
||||
<div class="dark:bg-4x-dark-foreground bg-light-background px-3 py-1.5 rounded-lg">
|
||||
<span class="text-sm font-semibold">
|
||||
{{ $t('Your current estimated usage:') }}
|
||||
</span>
|
||||
<span class="text-sm font-bold text-theme">
|
||||
{{ user.data.meta.usages.costEstimate }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="config.subscriptionType === 'metered' && user && !clickedSubmenu" class="block px-5 pt-2">
|
||||
<div class="rounded-lg bg-light-background px-3 py-1.5 dark:bg-4x-dark-foreground">
|
||||
<span class="text-sm font-semibold">
|
||||
{{ $t('Your current estimated usage:') }}
|
||||
</span>
|
||||
<span class="text-theme text-sm font-bold">
|
||||
{{ user.data.meta.usages.costEstimate }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--Go back button-->
|
||||
<div v-if="clickedSubmenu" @click.stop="showSubmenu(undefined)" class="flex items-center p-5 pb-4">
|
||||
<chevron-left-icon size="19" class="vue-feather text-theme mr-2 -ml-1" />
|
||||
<span class="text-theme font-bold text-sm">
|
||||
{{ backTitle }}
|
||||
</span>
|
||||
<span class="text-theme text-sm font-bold">
|
||||
{{ backTitle }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!--Menu links-->
|
||||
<MenuMobileGroup>
|
||||
|
||||
<!--Main navigation-->
|
||||
<OptionGroup v-if="!clickedSubmenu">
|
||||
<Option @click.native="goToFiles" :title="$t('menu.files')" icon="hard-drive" :is-hover-disabled="true"/>
|
||||
<Option @click.native.stop="showSubmenu('settings')" :title="$t('menu.settings')" icon="user" arrow="right" :is-hover-disabled="true"/>
|
||||
<Option v-if="isAdmin" @click.native.stop="showSubmenu('admin')" :title="$t('menu.admin')" icon="settings" arrow="right" :is-hover-disabled="true"/>
|
||||
<Option @click.native="goToFiles" :title="$t('menu.files')" icon="hard-drive" :is-hover-disabled="true" />
|
||||
<Option @click.native.stop="showSubmenu('settings')" :title="$t('menu.settings')" icon="user" arrow="right" :is-hover-disabled="true" />
|
||||
<Option v-if="isAdmin" @click.native.stop="showSubmenu('admin')" :title="$t('menu.admin')" icon="settings" arrow="right" :is-hover-disabled="true" />
|
||||
</OptionGroup>
|
||||
<OptionGroup v-if="!clickedSubmenu">
|
||||
<Option @click.native="logOut" :title="$t('menu.logout')" icon="power" :is-hover-disabled="true" />
|
||||
@@ -42,7 +40,7 @@
|
||||
<Option @click.native="goToRoute('Profile')" :title="$t('menu.profile')" icon="user" :is-hover-disabled="true" />
|
||||
<Option @click.native="goToRoute('Password')" :title="$t('menu.password')" icon="lock" :is-hover-disabled="true" />
|
||||
<Option @click.native="goToRoute('Storage')" :title="$t('menu.storage')" icon="hard-drive" :is-hover-disabled="true" />
|
||||
<Option @click.native="goToRoute('Billing')" v-if="config.isSaaS" :title="$t('Billing')" icon="cloud" :is-hover-disabled="true" />
|
||||
<Option @click.native="goToRoute('Billing')" v-if="config.isSaaS" :title="$t('Billing')" icon="cloud" :is-hover-disabled="true" />
|
||||
</OptionGroup>
|
||||
|
||||
<!--Submenu: Admin settings-->
|
||||
@@ -61,7 +59,13 @@
|
||||
<!--Submenu: Billing settings-->
|
||||
<OptionGroup v-if="clickedSubmenu === 'admin' && config.isSaaS">
|
||||
<Option @click.native="goToRoute('AppPayments')" :title="$t('Payments')" icon="credit-card" :is-hover-disabled="true" />
|
||||
<Option @click.native="goToRoute('Subscriptions')" v-if="config.subscriptionType === 'fixed'" :title="$t('Subscriptions')" icon="credit-card" :is-hover-disabled="true" />
|
||||
<Option
|
||||
@click.native="goToRoute('Subscriptions')"
|
||||
v-if="config.subscriptionType === 'fixed'"
|
||||
:title="$t('Subscriptions')"
|
||||
icon="credit-card"
|
||||
:is-hover-disabled="true"
|
||||
/>
|
||||
<Option @click.native="goToRoute('Plans')" :title="$t('admin_menu.plans')" icon="database" :is-hover-disabled="true" />
|
||||
<Option @click.native="goToRoute('Invoices')" :title="$t('Transactions')" icon="file-text" :is-hover-disabled="true" />
|
||||
</OptionGroup>
|
||||
@@ -70,63 +74,59 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MenuMobileGroup from "../Mobile/MenuMobileGroup";
|
||||
import OptionGroup from "../FilesView/OptionGroup";
|
||||
import UserHeadline from "../Sidebar/UserHeadline";
|
||||
import MenuMobile from "../Mobile/MenuMobile";
|
||||
import Option from "../FilesView/Option";
|
||||
import {ChevronLeftIcon} from 'vue-feather-icons'
|
||||
import {mapGetters} from 'vuex'
|
||||
import MenuMobileGroup from '../Mobile/MenuMobileGroup'
|
||||
import OptionGroup from '../FilesView/OptionGroup'
|
||||
import UserHeadline from '../Sidebar/UserHeadline'
|
||||
import MenuMobile from '../Mobile/MenuMobile'
|
||||
import Option from '../FilesView/Option'
|
||||
import { ChevronLeftIcon } from 'vue-feather-icons'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'MobileNavigation',
|
||||
components: {
|
||||
ChevronLeftIcon,
|
||||
MenuMobileGroup,
|
||||
UserHeadline,
|
||||
OptionGroup,
|
||||
MenuMobile,
|
||||
Option,
|
||||
export default {
|
||||
name: 'MobileNavigation',
|
||||
components: {
|
||||
ChevronLeftIcon,
|
||||
MenuMobileGroup,
|
||||
UserHeadline,
|
||||
OptionGroup,
|
||||
MenuMobile,
|
||||
Option,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['config', 'user']),
|
||||
isAdmin() {
|
||||
return this.user && this.user.data.attributes.role === 'admin'
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'config',
|
||||
'user',
|
||||
]),
|
||||
isAdmin() {
|
||||
return this.user && this.user.data.attributes.role === 'admin'
|
||||
},
|
||||
backTitle() {
|
||||
let location = {
|
||||
'settings': this.$t('menu.settings'),
|
||||
'admin': this.$t('menu.admin')
|
||||
}
|
||||
|
||||
return 'Go back from ' + location[this.clickedSubmenu]
|
||||
backTitle() {
|
||||
let location = {
|
||||
settings: this.$t('menu.settings'),
|
||||
admin: this.$t('menu.admin'),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
clickedSubmenu: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goToRoute(route) {
|
||||
this.$router.push({name: route})
|
||||
this.clickedSubmenu = undefined
|
||||
},
|
||||
showSubmenu(name) {
|
||||
this.clickedSubmenu = name
|
||||
},
|
||||
goToFiles() {
|
||||
if (this.$route.name !== 'Files')
|
||||
this.$router.push({name: 'Files'})
|
||||
|
||||
this.$store.dispatch('getFolder')
|
||||
},
|
||||
logOut() {
|
||||
this.$store.dispatch('logOut')
|
||||
},
|
||||
return 'Go back from ' + location[this.clickedSubmenu]
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
clickedSubmenu: undefined,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goToRoute(route) {
|
||||
this.$router.push({ name: route })
|
||||
this.clickedSubmenu = undefined
|
||||
},
|
||||
showSubmenu(name) {
|
||||
this.clickedSubmenu = name
|
||||
},
|
||||
goToFiles() {
|
||||
if (this.$route.name !== 'Files') this.$router.push({ name: 'Files' })
|
||||
|
||||
this.$store.dispatch('getFolder')
|
||||
},
|
||||
logOut() {
|
||||
this.$store.dispatch('logOut')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,167 +3,151 @@
|
||||
<!--Title-->
|
||||
<PopupHeader :title="$t('popup_move_item.title')" icon="move" />
|
||||
|
||||
<!--Content-->
|
||||
<PopupContent v-if="pickedItem" class="sm:overflow-y-auto md:pb-0 sm:max-h-96 h-full pb-6">
|
||||
|
||||
<!--Content-->
|
||||
<PopupContent v-if="pickedItem" class="h-full pb-6 sm:max-h-96 sm:overflow-y-auto md:pb-0">
|
||||
<!--Show Spinner when loading folders-->
|
||||
<Spinner v-if="isLoadingTree" />
|
||||
|
||||
<!--Folder tree-->
|
||||
<div v-if="! isLoadingTree && navigation">
|
||||
<!--Folder tree-->
|
||||
<div v-if="!isLoadingTree && navigation">
|
||||
<ThumbnailItem v-if="clipboard.length < 2 || isSelectedItem" class="mb-5" :item="pickedItem" />
|
||||
|
||||
<TitlePreview
|
||||
class="mb-4"
|
||||
icon="check-square"
|
||||
:title="$t('file_detail.selected_multiple')"
|
||||
:subtitle="this.clipboard.length + ' ' + $tc('file_detail.items', this.clipboard.length)"
|
||||
v-if="clipboard.length > 1 && !isSelectedItem"
|
||||
/>
|
||||
|
||||
class="mb-4"
|
||||
icon="check-square"
|
||||
:title="$t('file_detail.selected_multiple')"
|
||||
:subtitle="this.clipboard.length + ' ' + $tc('file_detail.items', this.clipboard.length)"
|
||||
v-if="clipboard.length > 1 && !isSelectedItem"
|
||||
/>
|
||||
|
||||
<TreeMenu class="-mx-4" :disabled-by-id="pickedItem" :depth="1" :nodes="items" v-for="items in navigation" :key="items.id" />
|
||||
</div>
|
||||
</PopupContent>
|
||||
|
||||
<!--Actions-->
|
||||
<!--Actions-->
|
||||
<PopupActions>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
@click.native="$closePopup()"
|
||||
button-style="secondary"
|
||||
>{{ $t('popup_move_item.cancel') }}
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
@click.native="moveItem"
|
||||
:button-style="selectedFolder ? 'theme' : 'secondary'"
|
||||
>{{ $t('popup_move_item.submit') }}
|
||||
</ButtonBase>
|
||||
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="secondary">{{ $t('popup_move_item.cancel') }} </ButtonBase>
|
||||
<ButtonBase class="w-full" @click.native="moveItem" :button-style="selectedFolder ? 'theme' : 'secondary'">{{ $t('popup_move_item.submit') }} </ButtonBase>
|
||||
</PopupActions>
|
||||
</PopupWrapper>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PopupWrapper from "./Popup/PopupWrapper";
|
||||
import PopupActions from "./Popup/PopupActions";
|
||||
import TitlePreview from "../FilesView/TitlePreview";
|
||||
import PopupContent from "./Popup/PopupContent";
|
||||
import PopupHeader from "./Popup/PopupHeader";
|
||||
import ThumbnailItem from "./ThumbnailItem";
|
||||
import ButtonBase from "../FilesView/ButtonBase";
|
||||
import Spinner from "../FilesView/Spinner";
|
||||
import TreeMenu from "./TreeMenu";
|
||||
import {isArray, isNull} from 'lodash'
|
||||
import {mapGetters} from 'vuex'
|
||||
import {events} from '../../bus'
|
||||
import PopupWrapper from './Popup/PopupWrapper'
|
||||
import PopupActions from './Popup/PopupActions'
|
||||
import TitlePreview from '../FilesView/TitlePreview'
|
||||
import PopupContent from './Popup/PopupContent'
|
||||
import PopupHeader from './Popup/PopupHeader'
|
||||
import ThumbnailItem from './ThumbnailItem'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import Spinner from '../FilesView/Spinner'
|
||||
import TreeMenu from './TreeMenu'
|
||||
import { isArray, isNull } from 'lodash'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { events } from '../../bus'
|
||||
|
||||
export default {
|
||||
name: 'MoveItemPopup',
|
||||
components: {
|
||||
ThumbnailItem,
|
||||
TitlePreview,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
TreeMenu,
|
||||
Spinner,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'navigation',
|
||||
'clipboard',
|
||||
]),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedFolder: undefined,
|
||||
pickedItem: undefined,
|
||||
isLoadingTree: true,
|
||||
isSelectedItem: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
moveItem() {
|
||||
// Prevent empty submit
|
||||
if (!this.selectedFolder) return
|
||||
export default {
|
||||
name: 'MoveItemPopup',
|
||||
components: {
|
||||
ThumbnailItem,
|
||||
TitlePreview,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
TreeMenu,
|
||||
Spinner,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['navigation', 'clipboard']),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedFolder: undefined,
|
||||
pickedItem: undefined,
|
||||
isLoadingTree: true,
|
||||
isSelectedItem: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
moveItem() {
|
||||
// Prevent empty submit
|
||||
if (!this.selectedFolder) return
|
||||
|
||||
// Prevent to move items to the same parent
|
||||
if (isArray(this.selectedFolder) && this.clipboard.find(item => item.parent_id === this.selectedFolder.id)) return
|
||||
// Prevent to move items to the same parent
|
||||
if (isArray(this.selectedFolder) && this.clipboard.find((item) => item.parent_id === this.selectedFolder.id)) return
|
||||
|
||||
// Move item
|
||||
if (!this.isSelectedItem) {
|
||||
this.$store.dispatch('moveItem', {to_item: this.selectedFolder, isSelectedItem: null})
|
||||
}
|
||||
// Move item
|
||||
if (!this.isSelectedItem) {
|
||||
this.$store.dispatch('moveItem', {
|
||||
to_item: this.selectedFolder,
|
||||
isSelectedItem: null,
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isSelectedItem) {
|
||||
this.$store.dispatch('moveItem', {to_item: this.selectedFolder, isSelectedItem: this.pickedItem})
|
||||
}
|
||||
if (this.isSelectedItem) {
|
||||
this.$store.dispatch('moveItem', {
|
||||
to_item: this.selectedFolder,
|
||||
isSelectedItem: this.pickedItem,
|
||||
})
|
||||
}
|
||||
|
||||
// Close popup
|
||||
events.$emit('popup:close')
|
||||
// Close popup
|
||||
events.$emit('popup:close')
|
||||
|
||||
// If is mobile, close the selecting mod after done the move action
|
||||
if (this.$isMobile())
|
||||
this.$store.commit('DISABLE_MULTISELECT_MODE')
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
events.$on('pick-folder', folder => {
|
||||
// If is mobile, close the selecting mod after done the move action
|
||||
if (this.$isMobile()) this.$store.commit('DISABLE_MULTISELECT_MODE')
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
events.$on('pick-folder', (folder) => {
|
||||
if (folder.id === this.pickedItem.data.id) {
|
||||
this.selectedFolder = undefined
|
||||
} else if (!folder.id && folder.location === 'base') {
|
||||
this.selectedFolder = 'base'
|
||||
} else {
|
||||
this.selectedFolder = folder
|
||||
}
|
||||
})
|
||||
|
||||
if (folder.id === this.pickedItem.data.id) {
|
||||
this.selectedFolder = undefined
|
||||
// Show Move item popup
|
||||
events.$on('popup:open', (args) => {
|
||||
if (args.name !== 'move') return
|
||||
|
||||
} else if (!folder.id && folder.location === 'base') {
|
||||
this.selectedFolder = 'base'
|
||||
// Show tree spinner
|
||||
this.isLoadingTree = true
|
||||
|
||||
} else {
|
||||
this.selectedFolder = folder
|
||||
}
|
||||
})
|
||||
// Get folder tree and hide spinner
|
||||
if (this.$isThisRoute(this.$route, ['SharedWithMe'])) {
|
||||
this.$store.dispatch('getTeamFolderTree').then(() => {
|
||||
this.isLoadingTree = false
|
||||
})
|
||||
} else {
|
||||
this.$store.dispatch('getFolderTree').then(() => {
|
||||
this.isLoadingTree = false
|
||||
})
|
||||
}
|
||||
|
||||
// Show Move item popup
|
||||
events.$on('popup:open', args => {
|
||||
// Store picked item
|
||||
if (!this.clipboard.includes(args.item[0])) {
|
||||
this.pickedItem = args.item[0]
|
||||
this.isSelectedItem = true
|
||||
}
|
||||
|
||||
if (args.name !== 'move') return
|
||||
if (this.clipboard.includes(args.item[0])) {
|
||||
this.pickedItem = this.clipboard[0]
|
||||
this.isSelectedItem = false
|
||||
}
|
||||
})
|
||||
|
||||
// Show tree spinner
|
||||
this.isLoadingTree = true
|
||||
|
||||
// Get folder tree and hide spinner
|
||||
if (this.$isThisRoute(this.$route, ['SharedWithMe'])) {
|
||||
|
||||
this.$store.dispatch('getTeamFolderTree').then(() => {
|
||||
this.isLoadingTree = false
|
||||
})
|
||||
} else {
|
||||
|
||||
this.$store.dispatch('getFolderTree').then(() => {
|
||||
this.isLoadingTree = false
|
||||
})
|
||||
}
|
||||
|
||||
// Store picked item
|
||||
if (!this.clipboard.includes(args.item[0])) {
|
||||
this.pickedItem = args.item[0]
|
||||
this.isSelectedItem = true
|
||||
}
|
||||
|
||||
if (this.clipboard.includes(args.item[0])) {
|
||||
this.pickedItem = this.clipboard[0]
|
||||
this.isSelectedItem = false
|
||||
}
|
||||
})
|
||||
|
||||
// Close popup
|
||||
events.$on('popup:close', () => {
|
||||
|
||||
// Clear selected folder
|
||||
setTimeout(() => {
|
||||
this.selectedFolder = undefined
|
||||
}, 150)
|
||||
})
|
||||
}
|
||||
}
|
||||
// Close popup
|
||||
events.$on('popup:close', () => {
|
||||
// Clear selected folder
|
||||
setTimeout(() => {
|
||||
this.selectedFolder = undefined
|
||||
}, 150)
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,92 +1,101 @@
|
||||
<template>
|
||||
<transition appear name="fade">
|
||||
<div
|
||||
v-if="isActive"
|
||||
class="relative mt-4 p-4 md:w-96 w-full shadow-lg rounded-xl overflow-hidden backdrop-filter backdrop-blur-lg bg-opacity-80"
|
||||
:class="{'dark:bg-2x-dark-foreground bg-red-50': isDanger, 'dark:bg-2x-dark-foreground bg-green-50': isSuccess}"
|
||||
>
|
||||
v-if="isActive"
|
||||
class="relative mt-4 w-full overflow-hidden rounded-xl bg-opacity-80 p-4 shadow-lg backdrop-blur-lg backdrop-filter md:w-96"
|
||||
:class="{
|
||||
'bg-red-50 dark:bg-2x-dark-foreground': isDanger,
|
||||
'bg-green-50 dark:bg-2x-dark-foreground': isSuccess,
|
||||
}"
|
||||
>
|
||||
<!--Content-->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<div>
|
||||
<check-icon v-if="isSuccess" size="22" class="vue-feather text-green-600" />
|
||||
<x-icon v-if="isDanger" size="22" class="vue-feather text-red-600" />
|
||||
</div>
|
||||
|
||||
<!--Content-->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<div>
|
||||
<check-icon v-if="isSuccess" size="22" class="vue-feather text-green-600" />
|
||||
<x-icon v-if="isDanger" size="22" class="vue-feather text-red-600" />
|
||||
</div>
|
||||
<p
|
||||
class="px-4 font-bold"
|
||||
:class="{
|
||||
'text-green-600': isSuccess,
|
||||
'text-red-600': isDanger,
|
||||
}"
|
||||
>
|
||||
{{ item.message }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p
|
||||
class="px-4 font-bold"
|
||||
:class="{'text-green-600': isSuccess, 'text-red-600': isDanger}"
|
||||
>
|
||||
{{ item.message }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div @click="isActive = false" class="p-2 cursor-pointer">
|
||||
<x-icon size="16" class="vue-feather dark:text-white text-black opacity-50" />
|
||||
</div>
|
||||
<div @click="isActive = false" class="cursor-pointer p-2">
|
||||
<x-icon size="16" class="vue-feather text-black opacity-50 dark:text-white" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--Progress bar-->
|
||||
<!--Progress bar-->
|
||||
<div class="absolute bottom-0 left-0 right-0">
|
||||
<span class="w-0 h-1 block bar-animation" :class="{'bg-green-400': isSuccess, 'bg-red-400': isDanger}"></span>
|
||||
<span
|
||||
class="bar-animation block h-1 w-0"
|
||||
:class="{
|
||||
'bg-green-400': isSuccess,
|
||||
'bg-red-400': isDanger,
|
||||
}"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {XIcon, CheckIcon} from 'vue-feather-icons'
|
||||
import { XIcon, CheckIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CheckIcon,
|
||||
XIcon,
|
||||
export default {
|
||||
components: {
|
||||
CheckIcon,
|
||||
XIcon,
|
||||
},
|
||||
props: ['item'],
|
||||
computed: {
|
||||
isDanger() {
|
||||
return this.item.type === 'danger'
|
||||
},
|
||||
props: [
|
||||
'item'
|
||||
],
|
||||
computed: {
|
||||
isDanger() {
|
||||
return this.item.type === 'danger'
|
||||
},
|
||||
isSuccess() {
|
||||
return this.item.type === 'success'
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isActive: 1
|
||||
}
|
||||
isSuccess() {
|
||||
return this.item.type === 'success'
|
||||
},
|
||||
created() {
|
||||
setTimeout(() => (this.isActive = 0), 6000)
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isActive: 1,
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
setTimeout(() => (this.isActive = 0), 6000)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bar-animation {
|
||||
animation: progressbar 6s linear;
|
||||
}
|
||||
.bar-animation {
|
||||
animation: progressbar 6s linear;
|
||||
}
|
||||
|
||||
@keyframes progressbar {
|
||||
0% {
|
||||
width: 0;
|
||||
}
|
||||
100% {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@keyframes progressbar {
|
||||
0% {
|
||||
width: 0;
|
||||
}
|
||||
100% {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: 0.3s ease;
|
||||
}
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(100%)
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<template>
|
||||
<div v-if="notifications.length > 0" class="fixed lg:bottom-8 bottom-4 lg:right-8 right-4 md:left-auto left-4 z-50">
|
||||
<ToasterItem :item="item" v-for="(item, i) in notifications" :key="i"/>
|
||||
<div v-if="notifications.length > 0" class="fixed bottom-4 right-4 left-4 z-50 md:left-auto lg:bottom-8 lg:right-8">
|
||||
<ToasterItem :item="item" v-for="(item, i) in notifications" :key="i" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ToasterItem from "./ToasterItem";
|
||||
import {events} from '../../../bus'
|
||||
import ToasterItem from './ToasterItem'
|
||||
import { events } from '../../../bus'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ToasterItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
notifications: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
events.$on('toaster', notification => this.notifications.push(notification))
|
||||
export default {
|
||||
components: {
|
||||
ToasterItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
notifications: [],
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
events.$on('toaster', (notification) => this.notifications.push(notification))
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -10,79 +10,74 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ChevronLeftIcon } from 'vue-feather-icons'
|
||||
import { ChevronLeftIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'PageHeader',
|
||||
props: [
|
||||
'title', 'canBack'
|
||||
],
|
||||
components: {
|
||||
ChevronLeftIcon
|
||||
},
|
||||
}
|
||||
export default {
|
||||
name: 'PageHeader',
|
||||
props: ['title', 'canBack'],
|
||||
components: {
|
||||
ChevronLeftIcon,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: white;
|
||||
z-index: 9;
|
||||
width: 100%;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.title {
|
||||
@include font-size(18);
|
||||
font-weight: 700;
|
||||
color: $text;
|
||||
}
|
||||
|
||||
.go-back {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
margin-top: -4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: white;
|
||||
z-index: 9;
|
||||
width: 100%;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.title {
|
||||
@include font-size(18);
|
||||
font-weight: 700;
|
||||
color: $text;
|
||||
}
|
||||
|
||||
.go-back {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
margin-top: -4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
@media only screen and (max-width: 690px) {
|
||||
.page-header {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
.dark {
|
||||
.page-header {
|
||||
background: $dark_mode_background;
|
||||
|
||||
.title {
|
||||
@include font-size(18);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
.page-header {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
||||
.page-header {
|
||||
background: $dark_mode_background;
|
||||
|
||||
.title {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.icon path {
|
||||
fill: $theme;
|
||||
}
|
||||
.title {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.icon path {
|
||||
fill: $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-between py-4 border-b dark:border-opacity-5 border-light border-dashed">
|
||||
<div>
|
||||
<img :src="$getPaymentLogo(driver)" :alt="driver" class="h-6">
|
||||
<small class="text-xs text-gray-500 pt-2 leading-4 block">
|
||||
{{ description }}
|
||||
</small>
|
||||
</div>
|
||||
<div v-if="$slots.default" class="bg-theme-200 inline-block px-3 py-1 rounded-lg relative">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between border-b border-dashed border-light py-4 dark:border-opacity-5">
|
||||
<div>
|
||||
<img :src="$getPaymentLogo(driver)" :alt="driver" class="h-6" />
|
||||
<small class="block pt-2 text-xs leading-4 text-gray-500">
|
||||
{{ description }}
|
||||
</small>
|
||||
</div>
|
||||
<div v-if="$slots.default" class="bg-theme-200 relative inline-block rounded-lg px-3 py-1">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'PaymentMethod',
|
||||
props: [
|
||||
'description',
|
||||
'driver',
|
||||
]
|
||||
}
|
||||
export default {
|
||||
name: 'PaymentMethod',
|
||||
props: ['description', 'driver'],
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -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>
|
||||
@@ -28,191 +30,184 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ButtonBase from "../FilesView/ButtonBase";
|
||||
import {HardDriveIcon} from "vue-feather-icons"
|
||||
import { mapGetters } from 'vuex'
|
||||
import axios from 'axios'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import { HardDriveIcon } from 'vue-feather-icons'
|
||||
import { mapGetters } from 'vuex'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'PlanPricingTables',
|
||||
components: {
|
||||
HardDriveIcon,
|
||||
ButtonBase,
|
||||
},
|
||||
props: [
|
||||
'customRoute'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
plans: undefined,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
},
|
||||
methods: {
|
||||
selectPlan(plan) {
|
||||
this.$emit('selected-plan', plan)
|
||||
|
||||
let route = this.customRoute ? this.customRoute : 'UpgradeBilling'
|
||||
|
||||
this.$router.push({name: route})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
axios.get('/api/pricing')
|
||||
.then(response => {
|
||||
this.plans = response.data.filter(plan => {
|
||||
return plan.data.attributes.capacity > this.user.data.attributes.max_storage_amount
|
||||
})
|
||||
this.$emit('load', false)
|
||||
})
|
||||
export default {
|
||||
name: 'PlanPricingTables',
|
||||
components: {
|
||||
HardDriveIcon,
|
||||
ButtonBase,
|
||||
},
|
||||
props: ['customRoute'],
|
||||
data() {
|
||||
return {
|
||||
plans: undefined,
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
},
|
||||
methods: {
|
||||
selectPlan(plan) {
|
||||
this.$emit('selected-plan', plan)
|
||||
|
||||
let route = this.customRoute ? this.customRoute : 'UpgradeBilling'
|
||||
|
||||
this.$router.push({ name: route })
|
||||
},
|
||||
},
|
||||
created() {
|
||||
axios.get('/api/pricing').then((response) => {
|
||||
this.plans = response.data.filter((plan) => {
|
||||
return plan.data.attributes.capacity > this.user.data.attributes.max_storage_amount
|
||||
})
|
||||
this.$emit('load', false)
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.plan {
|
||||
text-align: center;
|
||||
flex: 0 0 33%;
|
||||
padding: 0 25px;
|
||||
margin-bottom: 45px;
|
||||
.plan {
|
||||
text-align: center;
|
||||
flex: 0 0 33%;
|
||||
padding: 0 25px;
|
||||
margin-bottom: 45px;
|
||||
|
||||
.plan-wrapper {
|
||||
box-shadow: 0 7px 20px 5px hsla(220, 36%, 16%, 0.03);
|
||||
padding: 25px;
|
||||
border-radius: 8px;
|
||||
@include transition;
|
||||
.plan-wrapper {
|
||||
box-shadow: 0 7px 20px 5px hsla(220, 36%, 16%, 0.03);
|
||||
padding: 25px;
|
||||
border-radius: 8px;
|
||||
@include transition;
|
||||
|
||||
&:hover {
|
||||
@include transform(translateY(-20px) scale(1.05));
|
||||
box-shadow: 0 15px 25px 5px hsla(220, 36%, 16%, 0.08);
|
||||
&:hover {
|
||||
@include transform(translateY(-20px) scale(1.05));
|
||||
box-shadow: 0 15px 25px 5px hsla(220, 36%, 16%, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.plan-header {
|
||||
.icon {
|
||||
path,
|
||||
line,
|
||||
polyline,
|
||||
rect,
|
||||
circle {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.plan-header {
|
||||
.title {
|
||||
@include font-size(22);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.icon {
|
||||
path, line, polyline, rect, circle {
|
||||
color: inherit;
|
||||
}
|
||||
.description {
|
||||
@include font-size(14);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.plan-features {
|
||||
margin: 65px 0;
|
||||
|
||||
.storage-size {
|
||||
@include font-size(48);
|
||||
font-weight: 900;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.storage-description {
|
||||
display: block;
|
||||
@include font-size(15);
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
.plan-footer {
|
||||
.sign-in-button {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.price {
|
||||
@include font-size(18);
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.vat-disclaimer {
|
||||
@include font-size(11);
|
||||
color: $text;
|
||||
display: block;
|
||||
font-weight: 300;
|
||||
opacity: 0.45;
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.plans-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -25px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
.plans-wrapper {
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.plan {
|
||||
.plan-wrapper {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
.plan-header {
|
||||
.title {
|
||||
@include font-size(22);
|
||||
font-weight: 800;
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include font-size(14);
|
||||
font-weight: 600;
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.plan-features {
|
||||
margin: 65px 0;
|
||||
|
||||
.storage-size {
|
||||
@include font-size(48);
|
||||
font-weight: 900;
|
||||
line-height: 1.1;
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.storage-description {
|
||||
display: block;
|
||||
@include font-size(15);
|
||||
font-weight: 800;
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
|
||||
.plan-footer {
|
||||
|
||||
.sign-in-button {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
background: rgba($theme, 0.1);
|
||||
|
||||
/deep/ .content {
|
||||
color: $theme;
|
||||
}
|
||||
}
|
||||
|
||||
.price {
|
||||
@include font-size(18);
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.vat-disclaimer {
|
||||
@include font-size(11);
|
||||
color: $text;
|
||||
display: block;
|
||||
font-weight: 300;
|
||||
opacity: 0.45;
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.plans-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -25px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
|
||||
.plans-wrapper {
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.plan {
|
||||
|
||||
.plan-wrapper {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
|
||||
.plan-header {
|
||||
|
||||
.title {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.plan-features {
|
||||
|
||||
.storage-size {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.storage-description {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
|
||||
.plan-footer {
|
||||
|
||||
.sign-in-button {
|
||||
background: rgba($theme, 0.1);
|
||||
|
||||
/deep/ .content {
|
||||
color: $theme;
|
||||
}
|
||||
}
|
||||
|
||||
.price {
|
||||
|
||||
.vat-disclaimer {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,83 +1,73 @@
|
||||
<template>
|
||||
<PopupWrapper>
|
||||
<div class="text-center h-full flex items-center justify-center px-8 transform md:translate-y-0 -translate-y-7">
|
||||
<div>
|
||||
<img src="https://twemoji.maxcdn.com/v/13.1.0/svg/1f914.svg" alt="" class="w-20 mx-auto md:mt-6 mb-4">
|
||||
<div class="flex h-full -translate-y-7 transform items-center justify-center px-8 text-center md:translate-y-0">
|
||||
<div>
|
||||
<img src="https://twemoji.maxcdn.com/v/13.1.0/svg/1f914.svg" alt="" class="mx-auto mb-4 w-20 md:mt-6" />
|
||||
|
||||
<h1 v-if="title" class="text-2xl font-bold mb-2">
|
||||
{{ title }}
|
||||
</h1>
|
||||
<p v-if="message" class="text-sm mb-4">
|
||||
{{ message }}
|
||||
</p>
|
||||
</div>
|
||||
<h1 v-if="title" class="mb-2 text-2xl font-bold">
|
||||
{{ title }}
|
||||
</h1>
|
||||
<p v-if="message" class="mb-4 text-sm">
|
||||
{{ message }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PopupActions>
|
||||
<ButtonBase
|
||||
@click.native="closePopup"
|
||||
button-style="secondary"
|
||||
class="w-full"
|
||||
>{{ $t('global.cancel') }}
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
@click.native="confirm"
|
||||
:button-style="buttonColor"
|
||||
class="w-full"
|
||||
>{{ $t('global.confirm_action') }}
|
||||
</ButtonBase>
|
||||
<ButtonBase @click.native="closePopup" button-style="secondary" class="w-full">{{ $t('global.cancel') }} </ButtonBase>
|
||||
<ButtonBase @click.native="confirm" :button-style="buttonColor" class="w-full">{{ $t('global.confirm_action') }} </ButtonBase>
|
||||
</PopupActions>
|
||||
</PopupWrapper>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PopupWrapper from "./PopupWrapper";
|
||||
import PopupActions from "./PopupActions";
|
||||
import ButtonBase from "../../FilesView/ButtonBase";
|
||||
import {events} from '../../../bus'
|
||||
import PopupWrapper from './PopupWrapper'
|
||||
import PopupActions from './PopupActions'
|
||||
import ButtonBase from '../../FilesView/ButtonBase'
|
||||
import { events } from '../../../bus'
|
||||
|
||||
export default {
|
||||
name: 'ConfirmPopup',
|
||||
components: {
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
ButtonBase,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
confirmationData: [],
|
||||
message: undefined,
|
||||
title: undefined,
|
||||
buttonColor: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closePopup() {
|
||||
events.$emit('popup:close')
|
||||
},
|
||||
confirm() {
|
||||
// Close popup
|
||||
events.$emit('popup:close')
|
||||
|
||||
// Confirmation popup
|
||||
events.$emit('action:confirmed', this.confirmationData)
|
||||
|
||||
// Clear confirmation data
|
||||
this.confirmationData = []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// Show confirm
|
||||
events.$on('confirm:open', args => {
|
||||
this.title = args.title
|
||||
this.message = args.message
|
||||
this.confirmationData = args.action
|
||||
this.buttonColor = 'danger'
|
||||
|
||||
if (args.buttonColor) {
|
||||
this.buttonColor = args.buttonColor
|
||||
}
|
||||
})
|
||||
export default {
|
||||
name: 'ConfirmPopup',
|
||||
components: {
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
ButtonBase,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
confirmationData: [],
|
||||
message: undefined,
|
||||
title: undefined,
|
||||
buttonColor: undefined,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closePopup() {
|
||||
events.$emit('popup:close')
|
||||
},
|
||||
confirm() {
|
||||
// Close popup
|
||||
events.$emit('popup:close')
|
||||
|
||||
// Confirmation popup
|
||||
events.$emit('action:confirmed', this.confirmationData)
|
||||
|
||||
// Clear confirmation data
|
||||
this.confirmationData = []
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// Show confirm
|
||||
events.$on('confirm:open', (args) => {
|
||||
this.title = args.title
|
||||
this.message = args.message
|
||||
this.confirmationData = args.action
|
||||
this.buttonColor = 'danger'
|
||||
|
||||
if (args.buttonColor) {
|
||||
this.buttonColor = args.buttonColor
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="md:relative absolute bottom-0 left-0 right-0 flex items-center space-x-4 px-6 py-4 pb-6">
|
||||
<div class="absolute bottom-0 left-0 right-0 flex items-center space-x-4 px-6 py-4 pb-6 md:relative">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PopupActions',
|
||||
}
|
||||
export default {
|
||||
name: 'PopupActions',
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
<template>
|
||||
<div
|
||||
:class="type"
|
||||
class="md:relative md:top-0 md:bottom-0 absolute top-16 bottom-24 left-0 right-0 h-auto overflow-y-auto overflow-x-auto px-6"
|
||||
>
|
||||
<div :class="type" class="absolute top-16 bottom-24 left-0 right-0 h-auto overflow-x-auto overflow-y-auto px-6 md:relative md:top-0 md:bottom-0">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PopupContent',
|
||||
props: [
|
||||
'type'
|
||||
]
|
||||
}
|
||||
</script>
|
||||
export default {
|
||||
name: 'PopupContent',
|
||||
props: ['type'],
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,50 +1,48 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-between px-6 pt-6 pb-6">
|
||||
<div class="flex items-center">
|
||||
<div class="mr-3">
|
||||
<corner-down-right-icon v-if="icon === 'move'" size="18" class="vue-feather text-theme" />
|
||||
<share-icon v-if="icon === 'share'" size="18" class="vue-feather text-theme" />
|
||||
<edit2-icon v-if="icon === 'edit'" size="18" class="vue-feather text-theme" />
|
||||
<key-icon v-if="icon === 'key'" size="18" class="vue-feather text-theme" />
|
||||
<users-icon v-if="icon === 'users'" size="18" class="vue-feather text-theme" />
|
||||
<user-plus-icon v-if="icon === 'user-plus'" size="18" class="vue-feather text-theme" />
|
||||
<credit-card-icon v-if="icon === 'credit-card'" size="18" class="vue-feather text-theme" />
|
||||
</div>
|
||||
<div class="mr-3">
|
||||
<corner-down-right-icon v-if="icon === 'move'" size="18" class="vue-feather text-theme" />
|
||||
<share-icon v-if="icon === 'share'" size="18" class="vue-feather text-theme" />
|
||||
<edit2-icon v-if="icon === 'edit'" size="18" class="vue-feather text-theme" />
|
||||
<key-icon v-if="icon === 'key'" size="18" class="vue-feather text-theme" />
|
||||
<users-icon v-if="icon === 'users'" size="18" class="vue-feather text-theme" />
|
||||
<user-plus-icon v-if="icon === 'user-plus'" size="18" class="vue-feather text-theme" />
|
||||
<credit-card-icon v-if="icon === 'credit-card'" size="18" class="vue-feather text-theme" />
|
||||
</div>
|
||||
|
||||
<b class="font-bold text-base">
|
||||
{{ title }}
|
||||
</b>
|
||||
<b class="text-base font-bold">
|
||||
{{ title }}
|
||||
</b>
|
||||
</div>
|
||||
<div @click="closePopup" class="cursor-pointer p-3 -m-3">
|
||||
<div @click="closePopup" class="-m-3 cursor-pointer p-3">
|
||||
<x-icon size="14" class="hover-text-theme vue-feather" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {CreditCardIcon, KeyIcon, UserPlusIcon, CornerDownRightIcon, LinkIcon, XIcon, Edit2Icon, ShareIcon, UsersIcon} from 'vue-feather-icons'
|
||||
import {events} from '../../../bus'
|
||||
import { CreditCardIcon, KeyIcon, UserPlusIcon, CornerDownRightIcon, LinkIcon, XIcon, Edit2Icon, ShareIcon, UsersIcon } from 'vue-feather-icons'
|
||||
import { events } from '../../../bus'
|
||||
|
||||
export default {
|
||||
name: 'PopupHeader',
|
||||
props: [
|
||||
'title', 'icon'
|
||||
],
|
||||
components: {
|
||||
CornerDownRightIcon,
|
||||
CreditCardIcon,
|
||||
UserPlusIcon,
|
||||
UsersIcon,
|
||||
ShareIcon,
|
||||
Edit2Icon,
|
||||
LinkIcon,
|
||||
KeyIcon,
|
||||
XIcon,
|
||||
export default {
|
||||
name: 'PopupHeader',
|
||||
props: ['title', 'icon'],
|
||||
components: {
|
||||
CornerDownRightIcon,
|
||||
CreditCardIcon,
|
||||
UserPlusIcon,
|
||||
UsersIcon,
|
||||
ShareIcon,
|
||||
Edit2Icon,
|
||||
LinkIcon,
|
||||
KeyIcon,
|
||||
XIcon,
|
||||
},
|
||||
methods: {
|
||||
closePopup() {
|
||||
events.$emit('popup:close')
|
||||
},
|
||||
methods: {
|
||||
closePopup() {
|
||||
events.$emit('popup:close')
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<template>
|
||||
<transition name="popup">
|
||||
<div
|
||||
class="popup lg:absolute fixed top-0 left-0 right-0 bottom-0 overflow-y-auto grid h-full p-10"
|
||||
@click.self="closePopup"
|
||||
v-if="isVisibleWrapper"
|
||||
>
|
||||
<div class="popup fixed top-0 left-0 right-0 bottom-0 grid h-full overflow-y-auto p-10 lg:absolute" @click.self="closePopup" v-if="isVisibleWrapper">
|
||||
<div class="popup-wrapper">
|
||||
<slot></slot>
|
||||
</div>
|
||||
@@ -13,140 +9,132 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {events} from '../../../bus'
|
||||
import { events } from '../../../bus'
|
||||
|
||||
export default {
|
||||
name: 'PopupWrapper',
|
||||
props: [
|
||||
'name'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
isVisibleWrapper: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closePopup() {
|
||||
events.$emit('popup:close')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
// Open called popup
|
||||
events.$on('popup:open', ({name}) => {
|
||||
|
||||
if (this.name === name)
|
||||
this.isVisibleWrapper = true
|
||||
|
||||
if( (this.name !== name))
|
||||
this.isVisibleWrapper = false
|
||||
})
|
||||
|
||||
// Open called popup
|
||||
events.$on('confirm:open', ({name}) => {
|
||||
|
||||
if (this.name === name)
|
||||
this.isVisibleWrapper = true
|
||||
})
|
||||
|
||||
// Close popup
|
||||
events.$on('popup:close', () => this.isVisibleWrapper = false)
|
||||
export default {
|
||||
name: 'PopupWrapper',
|
||||
props: ['name'],
|
||||
data() {
|
||||
return {
|
||||
isVisibleWrapper: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closePopup() {
|
||||
events.$emit('popup:close')
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// Open called popup
|
||||
events.$on('popup:open', ({ name }) => {
|
||||
if (this.name === name) this.isVisibleWrapper = true
|
||||
|
||||
if (this.name !== name) this.isVisibleWrapper = false
|
||||
})
|
||||
|
||||
// Open called popup
|
||||
events.$on('confirm:open', ({ name }) => {
|
||||
if (this.name === name) this.isVisibleWrapper = true
|
||||
})
|
||||
|
||||
// Close popup
|
||||
events.$on('popup:close', () => (this.isVisibleWrapper = false))
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.popup {
|
||||
z-index: 41;
|
||||
}
|
||||
.popup {
|
||||
z-index: 41;
|
||||
}
|
||||
|
||||
.popup-wrapper {
|
||||
box-shadow: $light_mode_popup_shadow;
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
margin: auto;
|
||||
width: 480px;
|
||||
z-index: 12;
|
||||
}
|
||||
|
||||
// Desktop, tablet
|
||||
.medium,
|
||||
.large {
|
||||
// Animations
|
||||
.popup-enter-active {
|
||||
animation: popup-in 0.35s 0.15s ease both;
|
||||
}
|
||||
|
||||
.popup-leave-active {
|
||||
animation: popup-in 0.15s ease reverse;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes popup-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.7);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes popup-slide-in {
|
||||
0% {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
.popup {
|
||||
overflow: hidden;
|
||||
}
|
||||
.popup-wrapper {
|
||||
box-shadow: $light_mode_popup_shadow;
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
margin: auto;
|
||||
width: 480px;
|
||||
z-index: 12;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
transform: translateY(0) scale(1);
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
// Desktop, tablet
|
||||
.medium, .large {
|
||||
// Animations
|
||||
.popup-enter-active {
|
||||
animation: popup-in 0.35s 0.15s ease both;
|
||||
}
|
||||
|
||||
.popup-leave-active {
|
||||
animation: popup-in 0.15s ease reverse;
|
||||
}
|
||||
// Animations
|
||||
.popup-enter-active {
|
||||
animation: popup-slide-in 0.35s 0.15s ease both;
|
||||
}
|
||||
|
||||
@keyframes popup-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.7);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
.popup-leave-active {
|
||||
animation: popup-slide-in 0.15s ease reverse;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes popup-slide-in {
|
||||
0% {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
@media only screen and (max-width: 320px) {
|
||||
.popup-wrapper {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
.popup {
|
||||
overflow: hidden;
|
||||
}
|
||||
.popup-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
transform: translateY(0) scale(1);
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
border-radius: 0px;
|
||||
}
|
||||
// Animations
|
||||
.popup-enter-active {
|
||||
animation: popup-slide-in 0.35s 0.15s ease both;
|
||||
}
|
||||
.popup-leave-active {
|
||||
animation: popup-slide-in 0.15s ease reverse;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 320px){
|
||||
.popup-wrapper {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.dark {
|
||||
.popup-wrapper {
|
||||
background: $dark_mode_foreground;
|
||||
box-shadow: $dark_mode_popup_shadow;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
.dark {
|
||||
.popup-wrapper {
|
||||
background: $dark_mode_foreground;
|
||||
box-shadow: $dark_mode_popup_shadow;
|
||||
background: $dark_mode_background;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
|
||||
.dark {
|
||||
.popup-wrapper {
|
||||
background: $dark_mode_background;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,36 +1,41 @@
|
||||
<template>
|
||||
<PopupWrapper name="rename-item">
|
||||
<!--Title-->
|
||||
<PopupHeader :title="$t('popup_rename.title', {item: itemTypeTitle})" icon="edit" />
|
||||
<PopupHeader :title="$t('popup_rename.title', { item: itemTypeTitle })" icon="edit" />
|
||||
|
||||
<!--Content-->
|
||||
<PopupContent>
|
||||
|
||||
<!--Item Thumbnail-->
|
||||
<ThumbnailItem class="mb-5" :item="pickedItem" :setFolderIcon="{emoji: emoji, color: null}" />
|
||||
<ThumbnailItem class="mb-5" :item="pickedItem" :setFolderIcon="{ emoji: emoji, color: null }" />
|
||||
|
||||
<!--Form to set sharing-->
|
||||
<ValidationObserver @submit.prevent="changeName" ref="renameForm" v-slot="{ invalid }" tag="form">
|
||||
|
||||
<!--Update item name-->
|
||||
<ValidationProvider tag="div" mode="passive" name="Name" rules="required" v-slot="{ errors }">
|
||||
<AppInputText :title="$t('popup_rename.label')" :error="errors[0]" :is-last="pickedItem.data.type !== 'folder'">
|
||||
<div class="flex items-center relative">
|
||||
<input v-model="pickedItem.data.attributes.name" :class="{'border-red': errors[0]}" ref="input" type="text" class="focus-border-theme input-dark" :placeholder="$t('popup_rename.placeholder')">
|
||||
<div @click="pickedItem.data.attributes.name = ''" class="absolute right-4">
|
||||
<x-icon class="close-icon hover-text-theme" size="14" />
|
||||
</div>
|
||||
</div>
|
||||
</AppInputText>
|
||||
<AppInputText :title="$t('popup_rename.label')" :error="errors[0]" :is-last="pickedItem.data.type !== 'folder'">
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
v-model="pickedItem.data.attributes.name"
|
||||
:class="{ 'border-red': errors[0] }"
|
||||
ref="input"
|
||||
type="text"
|
||||
class="focus-border-theme input-dark"
|
||||
:placeholder="$t('popup_rename.placeholder')"
|
||||
/>
|
||||
<div @click="pickedItem.data.attributes.name = ''" class="absolute right-4">
|
||||
<x-icon class="close-icon hover-text-theme" size="14" />
|
||||
</div>
|
||||
</div>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
|
||||
<!--Emoji-->
|
||||
<AppInputSwitch v-if="pickedItem.data.type === 'folder'" :title="$t('Emoji as an Icon')" :description="$t('Replace folder icon with an Emoji')" :is-last="! isEmoji">
|
||||
<SwitchInput v-model="isEmoji" :state="isEmoji" />
|
||||
</AppInputSwitch>
|
||||
<!--Emoji-->
|
||||
<AppInputSwitch v-if="pickedItem.data.type === 'folder'" :title="$t('Emoji as an Icon')" :description="$t('Replace folder icon with an Emoji')" :is-last="!isEmoji">
|
||||
<SwitchInput v-model="isEmoji" :state="isEmoji" />
|
||||
</AppInputSwitch>
|
||||
|
||||
<!--Set emoji-->
|
||||
<EmojiPicker v-if="pickedItem.data.type === 'folder' && isEmoji" v-model="emoji" :default-emoji="emoji"/>
|
||||
<!--Set emoji-->
|
||||
<EmojiPicker v-if="pickedItem.data.type === 'folder' && isEmoji" v-model="emoji" :default-emoji="emoji" />
|
||||
</ValidationObserver>
|
||||
</PopupContent>
|
||||
|
||||
@@ -47,30 +52,30 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
|
||||
import PopupWrapper from "./Popup/PopupWrapper";
|
||||
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 AppInputSwitch from "../Admin/AppInputSwitch"
|
||||
import AppInputText from "../Admin/AppInputText"
|
||||
import {required} from 'vee-validate/dist/rules'
|
||||
import SwitchInput from "./Forms/SwitchInput"
|
||||
import EmojiPicker from "./EmojiPicker"
|
||||
import {XIcon} from 'vue-feather-icons'
|
||||
import {events} from '../../bus'
|
||||
import ThumbnailItem from './ThumbnailItem'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import AppInputSwitch from '../Admin/AppInputSwitch'
|
||||
import AppInputText from '../Admin/AppInputText'
|
||||
import { required } from 'vee-validate/dist/rules'
|
||||
import SwitchInput from './Forms/SwitchInput'
|
||||
import EmojiPicker from './EmojiPicker'
|
||||
import { XIcon } from 'vue-feather-icons'
|
||||
import { events } from '../../bus'
|
||||
|
||||
export default {
|
||||
name: 'RenameItemPopup',
|
||||
components: {
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
EmojiPicker,
|
||||
AppInputSwitch,
|
||||
SwitchInput,
|
||||
AppInputText,
|
||||
EmojiPicker,
|
||||
AppInputSwitch,
|
||||
SwitchInput,
|
||||
AppInputText,
|
||||
ThumbnailItem,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
@@ -78,51 +83,47 @@ export default {
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
required,
|
||||
XIcon
|
||||
XIcon,
|
||||
},
|
||||
computed: {
|
||||
itemTypeTitle() {
|
||||
return this.pickedItem && this.pickedItem.data.type === 'folder'
|
||||
? this.$t('types.folder')
|
||||
: this.$t('types.file')
|
||||
return this.pickedItem && this.pickedItem.data.type === 'folder' ? this.$t('types.folder') : this.$t('types.file')
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isEmoji(val) {
|
||||
if (!val) {
|
||||
events.$emit('setFolderIcon', { emoji: undefined })
|
||||
this.emoji = undefined
|
||||
} else {
|
||||
events.$emit('setFolderIcon', { emoji: this.emoji })
|
||||
}
|
||||
},
|
||||
emoji(val) {
|
||||
events.$emit('setFolderIcon', {
|
||||
emoji: val,
|
||||
})
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isEmoji(val) {
|
||||
if (! val) {
|
||||
events.$emit('setFolderIcon', {emoji: undefined})
|
||||
this.emoji = undefined
|
||||
} else {
|
||||
events.$emit('setFolderIcon', {emoji: this.emoji})
|
||||
}
|
||||
},
|
||||
emoji(val) {
|
||||
events.$emit('setFolderIcon', {
|
||||
emoji: val
|
||||
})
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pickedItem: undefined,
|
||||
isEmoji: false,
|
||||
emoji: undefined,
|
||||
isEmoji: false,
|
||||
emoji: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeName() {
|
||||
if (this.pickedItem.data.attributes.name && this.pickedItem.data.attributes.name !== '') {
|
||||
|
||||
let item = {
|
||||
id: this.pickedItem.data.id,
|
||||
type: this.pickedItem.data.type,
|
||||
name: this.pickedItem.data.attributes.name,
|
||||
}
|
||||
|
||||
item['emoji'] = this.emoji || null
|
||||
item['emoji'] = this.emoji || null
|
||||
|
||||
if (! this.isEmoji)
|
||||
item['emoji'] = null
|
||||
if (!this.isEmoji) item['emoji'] = null
|
||||
|
||||
// Rename item request
|
||||
this.$store.dispatch('renameItem', item)
|
||||
@@ -132,36 +133,34 @@ export default {
|
||||
|
||||
this.$closePopup()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
||||
// Show popup
|
||||
events.$on('popup:open', args => {
|
||||
|
||||
events.$on('popup:open', (args) => {
|
||||
if (args.name !== 'rename-item') return
|
||||
|
||||
this.isEmoji = false
|
||||
this.isEmoji = false
|
||||
|
||||
if (!this.$isMobile()) {
|
||||
if (!this.$isMobile()) {
|
||||
this.$nextTick(() => this.$refs.input.focus())
|
||||
}
|
||||
|
||||
// Set default emoji if exist
|
||||
if (args.item.data.attributes.emoji) {
|
||||
this.isEmoji = true
|
||||
this.emoji = args.item.data.attributes.emoji
|
||||
}
|
||||
// Set default emoji if exist
|
||||
if (args.item.data.attributes.emoji) {
|
||||
this.isEmoji = true
|
||||
this.emoji = args.item.data.attributes.emoji
|
||||
}
|
||||
|
||||
// Store picked item
|
||||
this.pickedItem = args.item
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../../sass/vuefilemanager/inapp-forms";
|
||||
@import '../../../sass/vuefilemanager/inapp-forms';
|
||||
@import '../../../sass/vuefilemanager/forms';
|
||||
|
||||
.input {
|
||||
@@ -200,7 +199,6 @@ export default {
|
||||
.dark {
|
||||
.close-icon-wrapper {
|
||||
&:hover {
|
||||
|
||||
.close-icon {
|
||||
line {
|
||||
color: inherit !important;
|
||||
|
||||
@@ -5,30 +5,29 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SectionTitle',
|
||||
}
|
||||
export default {
|
||||
name: 'SectionTitle',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.text-label {
|
||||
@include font-size(12);
|
||||
color: #afafaf;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1024px) {
|
||||
}
|
||||
|
||||
.dark {
|
||||
.text-label {
|
||||
@include font-size(12);
|
||||
color: #AFAFAF;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1024px) {
|
||||
|
||||
}
|
||||
|
||||
.dark {
|
||||
.text-label {
|
||||
color: $theme;
|
||||
}
|
||||
color: $theme;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,61 +3,50 @@
|
||||
<PopupHeader :title="$t('Select Payment Method')" icon="credit-card" />
|
||||
|
||||
<PopupContent style="padding: 0 20px">
|
||||
<!--PayPal implementation-->
|
||||
<div
|
||||
v-if="config.isPayPal"
|
||||
:class="{
|
||||
'mb-2 rounded-xl bg-light-background px-4 dark:bg-2x-dark-foreground': paypal.isMethodsLoaded,
|
||||
}"
|
||||
>
|
||||
<PaymentMethod @click.native="pickedPaymentMethod('paypal')" driver="paypal" :description="config.paypal_payment_description">
|
||||
<div v-if="paypal.isMethodLoading" class="translate-y-3 scale-50 transform">
|
||||
<Spinner />
|
||||
</div>
|
||||
<span v-if="!paypal.isMethodsLoaded" :class="{ 'opacity-0': paypal.isMethodLoading }" class="text-theme cursor-pointer text-sm font-bold">
|
||||
{{ $t('Select') }}
|
||||
</span>
|
||||
</PaymentMethod>
|
||||
|
||||
<!--PayPal implementation-->
|
||||
<div
|
||||
v-if="config.isPayPal"
|
||||
:class="{'dark:bg-2x-dark-foreground bg-light-background rounded-xl px-4 mb-2': paypal.isMethodsLoaded}"
|
||||
>
|
||||
<PaymentMethod
|
||||
@click.native="pickedPaymentMethod('paypal')"
|
||||
driver="paypal"
|
||||
:description="config.paypal_payment_description"
|
||||
>
|
||||
<div v-if="paypal.isMethodLoading" class="transform scale-50 translate-y-3">
|
||||
<Spinner />
|
||||
</div>
|
||||
<span v-if="! paypal.isMethodsLoaded" :class="{'opacity-0': paypal.isMethodLoading}" class="text-sm text-theme font-bold cursor-pointer">
|
||||
{{ $t('Select') }}
|
||||
</span>
|
||||
</PaymentMethod>
|
||||
<!--PayPal Buttons-->
|
||||
<div id="paypal-button-container"></div>
|
||||
</div>
|
||||
|
||||
<!--PayPal Buttons-->
|
||||
<div id="paypal-button-container"></div>
|
||||
</div>
|
||||
|
||||
<!--Paystack implementation-->
|
||||
<PaymentMethod
|
||||
v-if="config.isPaystack"
|
||||
driver="paystack"
|
||||
:description="config.paystack_payment_description"
|
||||
>
|
||||
<paystack
|
||||
@click.native="pickedPaymentMethod('paystack')"
|
||||
v-if="user && config"
|
||||
:channels="['card', 'bank', 'ussd', 'qr', 'mobile_money', 'bank_transfer']"
|
||||
class="font-bold"
|
||||
currency="ZAR"
|
||||
:amount="singleChargeAmount * 100"
|
||||
:email="user.data.attributes.email"
|
||||
:paystackkey="config.paystack_public_key"
|
||||
:reference="$generatePaystackReference()"
|
||||
:callback="paystackPaymentSuccessful"
|
||||
:close="paystackClosed"
|
||||
>
|
||||
<span class="text-sm text-theme font-bold cursor-pointer">
|
||||
{{ $t('Select') }}
|
||||
</span>
|
||||
</paystack>
|
||||
</PaymentMethod>
|
||||
<!--Paystack implementation-->
|
||||
<PaymentMethod v-if="config.isPaystack" driver="paystack" :description="config.paystack_payment_description">
|
||||
<paystack
|
||||
@click.native="pickedPaymentMethod('paystack')"
|
||||
v-if="user && config"
|
||||
:channels="['card', 'bank', 'ussd', 'qr', 'mobile_money', 'bank_transfer']"
|
||||
class="font-bold"
|
||||
currency="ZAR"
|
||||
:amount="singleChargeAmount * 100"
|
||||
:email="user.data.attributes.email"
|
||||
:paystackkey="config.paystack_public_key"
|
||||
:reference="$generatePaystackReference()"
|
||||
:callback="paystackPaymentSuccessful"
|
||||
:close="paystackClosed"
|
||||
>
|
||||
<span class="text-theme cursor-pointer text-sm font-bold">
|
||||
{{ $t('Select') }}
|
||||
</span>
|
||||
</paystack>
|
||||
</PaymentMethod>
|
||||
</PopupContent>
|
||||
|
||||
<PopupActions>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
@click.native="$closePopup()"
|
||||
button-style="secondary"
|
||||
>
|
||||
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="secondary">
|
||||
{{ $t('Cancel Payment') }}
|
||||
</ButtonBase>
|
||||
</PopupActions>
|
||||
@@ -65,117 +54,117 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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 { loadScript } from "@paypal/paypal-js"
|
||||
import PaymentMethod from "./PaymentMethod"
|
||||
import Spinner from "../FilesView/Spinner"
|
||||
import {events} from '../../bus'
|
||||
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 { loadScript } from '@paypal/paypal-js'
|
||||
import PaymentMethod from './PaymentMethod'
|
||||
import Spinner from '../FilesView/Spinner'
|
||||
import { events } from '../../bus'
|
||||
import paystack from 'vue-paystack'
|
||||
import {mapGetters} from "vuex"
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: "SelectSingleChargeMethodPopup",
|
||||
components: {
|
||||
PaymentMethod,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
paystack,
|
||||
Spinner,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
paypal: {
|
||||
isMethodsLoaded: false,
|
||||
isMethodLoading: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'singleChargeAmount',
|
||||
'config',
|
||||
'user',
|
||||
]),
|
||||
},
|
||||
methods: {
|
||||
pickedPaymentMethod(driver) {
|
||||
if (driver === 'paystack') {
|
||||
this.$closePopup()
|
||||
}
|
||||
if (driver === 'paypal' && !this.paypal.isMethodsLoaded) {
|
||||
this.PayPalInitialization()
|
||||
}
|
||||
},
|
||||
async PayPalInitialization() {
|
||||
this.paypal.isMethodLoading = true
|
||||
name: 'SelectSingleChargeMethodPopup',
|
||||
components: {
|
||||
PaymentMethod,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
paystack,
|
||||
Spinner,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
paypal: {
|
||||
isMethodsLoaded: false,
|
||||
isMethodLoading: false,
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['singleChargeAmount', 'config', 'user']),
|
||||
},
|
||||
methods: {
|
||||
pickedPaymentMethod(driver) {
|
||||
if (driver === 'paystack') {
|
||||
this.$closePopup()
|
||||
}
|
||||
if (driver === 'paypal' && !this.paypal.isMethodsLoaded) {
|
||||
this.PayPalInitialization()
|
||||
}
|
||||
},
|
||||
async PayPalInitialization() {
|
||||
this.paypal.isMethodLoading = true
|
||||
|
||||
let paypal;
|
||||
let paypal
|
||||
|
||||
try {
|
||||
paypal = await loadScript({
|
||||
'client-id': this.config.paypal_client_id,
|
||||
'vault': true,
|
||||
});
|
||||
} catch (error) {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('Failed to load the PayPal service'),
|
||||
})
|
||||
}
|
||||
try {
|
||||
paypal = await loadScript({
|
||||
'client-id': this.config.paypal_client_id,
|
||||
vault: true,
|
||||
})
|
||||
} catch (error) {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('Failed to load the PayPal service'),
|
||||
})
|
||||
}
|
||||
|
||||
const userId = this.user.data.id
|
||||
const amount = this.singleChargeAmount
|
||||
const userId = this.user.data.id
|
||||
const amount = this.singleChargeAmount
|
||||
|
||||
this.paypal.isMethodsLoaded = true
|
||||
this.paypal.isMethodLoading = false
|
||||
this.paypal.isMethodsLoaded = true
|
||||
this.paypal.isMethodLoading = false
|
||||
|
||||
// Initialize paypal buttons for single charge
|
||||
await paypal.Buttons({
|
||||
createOrder: function(data, actions) {
|
||||
return actions.order.create({
|
||||
purchase_units: [{
|
||||
amount: {
|
||||
value: amount,
|
||||
},
|
||||
custom_id: userId
|
||||
}]
|
||||
});
|
||||
}
|
||||
}).render('#paypal-button-container');
|
||||
},
|
||||
paystackPaymentSuccessful() {
|
||||
this.$closePopup()
|
||||
// Initialize paypal buttons for single charge
|
||||
await paypal
|
||||
.Buttons({
|
||||
createOrder: function (data, actions) {
|
||||
return actions.order.create({
|
||||
purchase_units: [
|
||||
{
|
||||
amount: {
|
||||
value: amount,
|
||||
},
|
||||
custom_id: userId,
|
||||
},
|
||||
],
|
||||
})
|
||||
},
|
||||
})
|
||||
.render('#paypal-button-container')
|
||||
},
|
||||
paystackPaymentSuccessful() {
|
||||
this.$closePopup()
|
||||
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your payment was successfully received.'),
|
||||
})
|
||||
},
|
||||
paystackClosed() {
|
||||
// ...
|
||||
}
|
||||
},
|
||||
created() {
|
||||
events.$on('popup:close', () => this.paypal.isMethodsLoaded = false)
|
||||
}
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your payment was successfully received.'),
|
||||
})
|
||||
},
|
||||
paystackClosed() {
|
||||
// ...
|
||||
},
|
||||
},
|
||||
created() {
|
||||
events.$on('popup:close', () => (this.paypal.isMethodsLoaded = false))
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.mobile-actions {
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
margin: 0 -20px;
|
||||
padding: 10px 0 10px 20px;
|
||||
}
|
||||
.mobile-actions {
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
margin: 0 -20px;
|
||||
padding: 10px 0 10px 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,72 +1,81 @@
|
||||
<template>
|
||||
<PopupWrapper name="share-create">
|
||||
<!--Title-->
|
||||
<PopupHeader :title="$t('popup_share_create.title', {item: itemTypeTitle})" icon="share" />
|
||||
<PopupHeader :title="$t('popup_share_create.title', { item: itemTypeTitle })" icon="share" />
|
||||
|
||||
<!--Content-->
|
||||
<PopupContent>
|
||||
|
||||
<!--Item Thumbnail-->
|
||||
<ThumbnailItem class="mb-5" :item="pickedItem" />
|
||||
|
||||
<!--Form to set sharing-->
|
||||
<ValidationObserver v-if="! isGeneratedShared" @submit.prevent ref="shareForm" v-slot="{ invalid }" tag="form">
|
||||
|
||||
<ValidationObserver v-if="!isGeneratedShared" @submit.prevent ref="shareForm" v-slot="{ invalid }" tag="form">
|
||||
<!--Permission Select-->
|
||||
<ValidationProvider v-if="isFolder" tag="div" mode="passive" name="Permission" rules="required" v-slot="{ errors }">
|
||||
<AppInputText :title="$t('shared_form.label_permission')" :error="errors[0]">
|
||||
<SelectInput v-model="shareOptions.permission" :options="$translateSelectOptions(permissionOptions)" :placeholder="$t('shared_form.placeholder_permission')" :isError="errors[0]" />
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
<ValidationProvider v-if="isFolder" tag="div" mode="passive" name="Permission" rules="required" v-slot="{ errors }">
|
||||
<AppInputText :title="$t('shared_form.label_permission')" :error="errors[0]">
|
||||
<SelectInput
|
||||
v-model="shareOptions.permission"
|
||||
:options="$translateSelectOptions(permissionOptions)"
|
||||
:placeholder="$t('shared_form.placeholder_permission')"
|
||||
:isError="errors[0]"
|
||||
/>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
|
||||
<!--Password Switch-->
|
||||
<div>
|
||||
<AppInputSwitch :title="$t('shared_form.label_password_protection')" :description="$t('popup.share.password_description')">
|
||||
<SwitchInput v-model="shareOptions.isPassword" class="switch" :state="shareOptions.isPassword" />
|
||||
</AppInputSwitch>
|
||||
<div>
|
||||
<AppInputSwitch :title="$t('shared_form.label_password_protection')" :description="$t('popup.share.password_description')">
|
||||
<SwitchInput v-model="shareOptions.isPassword" class="switch" :state="shareOptions.isPassword" />
|
||||
</AppInputSwitch>
|
||||
|
||||
<!--Set password-->
|
||||
<ValidationProvider v-if="shareOptions.isPassword" tag="div" mode="passive" name="Password" rules="required" v-slot="{ errors }">
|
||||
<AppInputText :error="errors[0]" class="-mt-2">
|
||||
<input v-model="shareOptions.password" :class="{'border-red': errors[0]}" type="text" class="focus-border-theme input-dark" :placeholder="$t('page_sign_in.placeholder_password')">
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
<!--Set password-->
|
||||
<ValidationProvider v-if="shareOptions.isPassword" tag="div" mode="passive" name="Password" rules="required" v-slot="{ errors }">
|
||||
<AppInputText :error="errors[0]" class="-mt-2">
|
||||
<input
|
||||
v-model="shareOptions.password"
|
||||
:class="{ 'border-red': errors[0] }"
|
||||
type="text"
|
||||
class="focus-border-theme input-dark"
|
||||
:placeholder="$t('page_sign_in.placeholder_password')"
|
||||
/>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
|
||||
<!--Expiration switch-->
|
||||
<div>
|
||||
<AppInputSwitch :title="$t('expiration')" :description="$t('popup.share.expiration_description')">
|
||||
<SwitchInput v-model="isExpiration" class="switch" :state="isExpiration" />
|
||||
</AppInputSwitch>
|
||||
<div>
|
||||
<AppInputSwitch :title="$t('expiration')" :description="$t('popup.share.expiration_description')">
|
||||
<SwitchInput v-model="isExpiration" class="switch" :state="isExpiration" />
|
||||
</AppInputSwitch>
|
||||
|
||||
<!--Set expiration-->
|
||||
<AppInputText v-if="isExpiration" class="-mt-2">
|
||||
<AppInputText v-if="isExpiration" class="-mt-2">
|
||||
<SelectBoxInput v-model="shareOptions.expiration" :data="$translateSelectOptions(expirationList)" class="box" />
|
||||
</AppInputText>
|
||||
</div>
|
||||
</AppInputText>
|
||||
</div>
|
||||
|
||||
<!--Send on emails switch-->
|
||||
<div>
|
||||
<AppInputSwitch :title="$t('popup.share.email_send')" :description="$t('popup.share.email_description')" :is-last="! isEmailSharing">
|
||||
<SwitchInput v-model="isEmailSharing" class="switch" :state="isEmailSharing" />
|
||||
</AppInputSwitch>
|
||||
<div>
|
||||
<AppInputSwitch :title="$t('popup.share.email_send')" :description="$t('popup.share.email_description')" :is-last="!isEmailSharing">
|
||||
<SwitchInput v-model="isEmailSharing" class="switch" :state="isEmailSharing" />
|
||||
</AppInputSwitch>
|
||||
|
||||
<!--Set expiration-->
|
||||
<ValidationProvider v-if="isEmailSharing" tag="div" mode="passive" name="Email" rules="required" v-slot="{ errors }" class="-mt-2">
|
||||
<MultiEmailInput rules="required" v-model="shareOptions.emails" :label="$t('shared_form.recipients_label')" :isError="errors[0]" />
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
<MultiEmailInput rules="required" v-model="shareOptions.emails" :label="$t('shared_form.recipients_label')" :isError="errors[0]" />
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
</ValidationObserver>
|
||||
|
||||
<!--Copy generated link-->
|
||||
<AppInputText v-if="isGeneratedShared" :title="$t('shared_form.label_share_vie_email')" :is-last="true">
|
||||
<CopyShareLink :item="pickedItem" />
|
||||
</AppInputText>
|
||||
<AppInputText v-if="isGeneratedShared" :title="$t('shared_form.label_share_vie_email')" :is-last="true">
|
||||
<CopyShareLink :item="pickedItem" />
|
||||
</AppInputText>
|
||||
</PopupContent>
|
||||
|
||||
<!--Actions-->
|
||||
<PopupActions>
|
||||
<ButtonBase v-if="! isGeneratedShared" class="w-full" @click.native="$closePopup()" button-style="secondary">
|
||||
<ButtonBase v-if="!isGeneratedShared" class="w-full" @click.native="$closePopup()" button-style="secondary">
|
||||
{{ $t('popup_move_item.cancel') }}
|
||||
</ButtonBase>
|
||||
<ButtonBase class="w-full" @click.native="submitShareOptions" button-style="theme" :loading="isLoading" :disabled="isLoading">
|
||||
@@ -77,26 +86,26 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AppInputText from "../Admin/AppInputText";
|
||||
import AppInputSwitch from "../Admin/AppInputSwitch";
|
||||
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
|
||||
import SelectBoxInput from "./Forms/SelectBoxInput";
|
||||
import PopupWrapper from "./Popup/PopupWrapper";
|
||||
import AppInputText from '../Admin/AppInputText'
|
||||
import AppInputSwitch from '../Admin/AppInputSwitch'
|
||||
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
|
||||
import SelectBoxInput from './Forms/SelectBoxInput'
|
||||
import PopupWrapper from './Popup/PopupWrapper'
|
||||
import PopupActions from './Popup/PopupActions'
|
||||
import PopupContent from './Popup/PopupContent'
|
||||
import PopupHeader from './Popup/PopupHeader'
|
||||
import MultiEmailInput from "./Forms/MultiEmailInput";
|
||||
import SwitchInput from "./Forms/SwitchInput";
|
||||
import SelectInput from "./Forms/SelectInput";
|
||||
import ThumbnailItem from "./ThumbnailItem";
|
||||
import ActionButton from "./ActionButton";
|
||||
import CopyShareLink from "./Forms/CopyShareLink";
|
||||
import ButtonBase from "../FilesView/ButtonBase";
|
||||
import InfoBox from "./Forms/InfoBox";
|
||||
import {LinkIcon, MailIcon} from 'vue-feather-icons'
|
||||
import {required} from 'vee-validate/dist/rules'
|
||||
import {mapGetters} from 'vuex'
|
||||
import {events} from '../../bus'
|
||||
import MultiEmailInput from './Forms/MultiEmailInput'
|
||||
import SwitchInput from './Forms/SwitchInput'
|
||||
import SelectInput from './Forms/SelectInput'
|
||||
import ThumbnailItem from './ThumbnailItem'
|
||||
import ActionButton from './ActionButton'
|
||||
import CopyShareLink from './Forms/CopyShareLink'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import InfoBox from './Forms/InfoBox'
|
||||
import { LinkIcon, MailIcon } from 'vue-feather-icons'
|
||||
import { required } from 'vee-validate/dist/rules'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { events } from '../../bus'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
@@ -104,8 +113,8 @@ export default {
|
||||
components: {
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
AppInputText,
|
||||
AppInputSwitch,
|
||||
AppInputText,
|
||||
AppInputSwitch,
|
||||
SelectBoxInput,
|
||||
ThumbnailItem,
|
||||
ActionButton,
|
||||
@@ -121,13 +130,10 @@ export default {
|
||||
MailIcon,
|
||||
required,
|
||||
LinkIcon,
|
||||
InfoBox
|
||||
InfoBox,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'permissionOptions',
|
||||
'expirationList'
|
||||
]),
|
||||
...mapGetters(['permissionOptions', 'expirationList']),
|
||||
itemTypeTitle() {
|
||||
return this.pickedItem && this.pickedItem.data.type === 'folder' ? this.$t('types.folder') : this.$t('types.file')
|
||||
},
|
||||
@@ -136,17 +142,17 @@ export default {
|
||||
},
|
||||
submitButtonText() {
|
||||
return this.isGeneratedShared ? this.$t('shared_form.button_done') : this.$t('shared_form.button_generate')
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isExpiration(val) {
|
||||
if (!val) this.shareOptions.expiration = undefined
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isExpiration(val) {
|
||||
if (! val) this.shareOptions.expiration = undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isExpiration: false,
|
||||
isEmailSharing: false,
|
||||
isExpiration: false,
|
||||
isEmailSharing: false,
|
||||
shareOptions: {
|
||||
isPassword: false,
|
||||
expiration: undefined,
|
||||
@@ -154,17 +160,16 @@ export default {
|
||||
permission: undefined,
|
||||
type: undefined,
|
||||
id: undefined,
|
||||
emails: undefined
|
||||
emails: undefined,
|
||||
},
|
||||
pickedItem: undefined,
|
||||
isGeneratedShared: false,
|
||||
isLoading: false,
|
||||
sharedViaEmail: false
|
||||
sharedViaEmail: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async submitShareOptions() {
|
||||
|
||||
// If shared was generated, then close popup
|
||||
if (this.isGeneratedShared) {
|
||||
events.$emit('popup:close')
|
||||
@@ -182,14 +187,13 @@ export default {
|
||||
// Send request to get share link
|
||||
axios
|
||||
.post(`/api/share`, this.shareOptions)
|
||||
.then(response => {
|
||||
|
||||
.then((response) => {
|
||||
// End loading
|
||||
this.isGeneratedShared = true
|
||||
|
||||
this.$store.commit('UPDATE_SHARED_ITEM', response.data)
|
||||
|
||||
this.pickedItem.data.relationships.shared = response.data
|
||||
this.pickedItem.data.relationships.shared = response.data
|
||||
})
|
||||
.catch(() => {
|
||||
events.$emit('alert:open', {
|
||||
@@ -203,15 +207,13 @@ export default {
|
||||
.finally(() => {
|
||||
this.isLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
||||
events.$on('emailsInputValues', emails => this.shareOptions.emails = emails)
|
||||
events.$on('emailsInputValues', (emails) => (this.shareOptions.emails = emails))
|
||||
|
||||
// Show popup
|
||||
events.$on('popup:open', args => {
|
||||
|
||||
events.$on('popup:open', (args) => {
|
||||
if (args.name !== 'share-create') return
|
||||
|
||||
// Store picked item
|
||||
@@ -223,23 +225,22 @@ export default {
|
||||
|
||||
// Close popup
|
||||
events.$on('popup:close', () => {
|
||||
|
||||
// Restore data
|
||||
setTimeout(() => {
|
||||
this.isGeneratedShared = false
|
||||
this.isExpiration = false
|
||||
this.isEmailSharing = false
|
||||
this.shareOptions = {
|
||||
isPassword: false,
|
||||
expiration: undefined,
|
||||
password: undefined,
|
||||
permission: undefined,
|
||||
type: undefined,
|
||||
id: undefined,
|
||||
emails: undefined
|
||||
}
|
||||
this.isGeneratedShared = false
|
||||
this.isExpiration = false
|
||||
this.isEmailSharing = false
|
||||
this.shareOptions = {
|
||||
isPassword: false,
|
||||
expiration: undefined,
|
||||
password: undefined,
|
||||
permission: undefined,
|
||||
type: undefined,
|
||||
id: undefined,
|
||||
emails: undefined,
|
||||
}
|
||||
}, 150)
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,356 +3,339 @@
|
||||
<!--Title-->
|
||||
<PopupHeader :title="popupTitle" icon="share" />
|
||||
|
||||
<!--Qr Code-->
|
||||
<div v-if="pickedItem && activeSection === 'qr-code'">
|
||||
<PopupContent class="flex justify-center items-center">
|
||||
<div v-if="! qrCode" class="relative my-8">
|
||||
<Spinner />
|
||||
</div>
|
||||
<div v-if="qrCode" v-html="qrCode" class="my-5 overflow-hidden rounded-xl"></div>
|
||||
</PopupContent>
|
||||
<!--Qr Code-->
|
||||
<div v-if="pickedItem && activeSection === 'qr-code'">
|
||||
<PopupContent class="flex items-center justify-center">
|
||||
<div v-if="!qrCode" class="relative my-8">
|
||||
<Spinner />
|
||||
</div>
|
||||
<div v-if="qrCode" v-html="qrCode" class="my-5 overflow-hidden rounded-xl"></div>
|
||||
</PopupContent>
|
||||
|
||||
<PopupActions>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
@click.native="showSection(undefined)"
|
||||
button-style="secondary"
|
||||
>
|
||||
{{ $t('Show Details') }}
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
@click.native="$closePopup()"
|
||||
button-style="theme"
|
||||
>
|
||||
{{ $t('shared_form.button_done') }}
|
||||
</ButtonBase>
|
||||
</PopupActions>
|
||||
</div>
|
||||
<PopupActions>
|
||||
<ButtonBase class="w-full" @click.native="showSection(undefined)" button-style="secondary">
|
||||
{{ $t('Show Details') }}
|
||||
</ButtonBase>
|
||||
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="theme">
|
||||
{{ $t('shared_form.button_done') }}
|
||||
</ButtonBase>
|
||||
</PopupActions>
|
||||
</div>
|
||||
|
||||
<!--Share via email-->
|
||||
<div v-if="pickedItem && activeSection === 'email-sharing'">
|
||||
<PopupContent>
|
||||
<!--Item Thumbnail-->
|
||||
<ThumbnailItem class="mb-4" :item="pickedItem" />
|
||||
<!--Share via email-->
|
||||
<div v-if="pickedItem && activeSection === 'email-sharing'">
|
||||
<PopupContent>
|
||||
<!--Item Thumbnail-->
|
||||
<ThumbnailItem class="mb-4" :item="pickedItem" />
|
||||
|
||||
<ValidationObserver @submit.prevent v-slot="{ invalid }" ref="shareEmail" tag="form">
|
||||
<ValidationProvider tag="div" mode="passive" name="Email" rules="required" v-slot="{ errors }">
|
||||
<AppInputText title="Share with" :error="errors[0]" :is-last="true">
|
||||
<MultiEmailInput rules="required" v-model="emails" :label="$t('shared_form.label_send_to_recipients')" />
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
</ValidationObserver>
|
||||
</PopupContent>
|
||||
<ValidationObserver @submit.prevent v-slot="{ invalid }" ref="shareEmail" tag="form">
|
||||
<ValidationProvider tag="div" mode="passive" name="Email" rules="required" v-slot="{ errors }">
|
||||
<AppInputText title="Share with" :error="errors[0]" :is-last="true">
|
||||
<MultiEmailInput rules="required" v-model="emails" :label="$t('shared_form.label_send_to_recipients')" />
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
</ValidationObserver>
|
||||
</PopupContent>
|
||||
|
||||
<PopupActions>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
@click.native="showSection(undefined)"
|
||||
button-style="secondary"
|
||||
>
|
||||
{{ $t('Show Details') }}
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
@click.native="sendViaEmail"
|
||||
button-style="theme"
|
||||
:loading="isLoading"
|
||||
:disabled="isLoading"
|
||||
>
|
||||
{{ $t('Send') }}
|
||||
</ButtonBase>
|
||||
</PopupActions>
|
||||
</div>
|
||||
<PopupActions>
|
||||
<ButtonBase class="w-full" @click.native="showSection(undefined)" button-style="secondary">
|
||||
{{ $t('Show Details') }}
|
||||
</ButtonBase>
|
||||
<ButtonBase class="w-full" @click.native="sendViaEmail" button-style="theme" :loading="isLoading" :disabled="isLoading">
|
||||
{{ $t('Send') }}
|
||||
</ButtonBase>
|
||||
</PopupActions>
|
||||
</div>
|
||||
|
||||
<!--Update sharing-->
|
||||
<div v-if="pickedItem && ! activeSection">
|
||||
<PopupContent>
|
||||
<!--Item Thumbnail-->
|
||||
<ThumbnailItem class="mb-5" :item="pickedItem" />
|
||||
<!--Update sharing-->
|
||||
<div v-if="pickedItem && !activeSection">
|
||||
<PopupContent>
|
||||
<!--Item Thumbnail-->
|
||||
<ThumbnailItem class="mb-5" :item="pickedItem" />
|
||||
|
||||
<!--Get share link-->
|
||||
<AppInputText :title="$t('shared_form.label_share_vie_email')">
|
||||
<CopyShareLink :item="pickedItem" />
|
||||
</AppInputText>
|
||||
<!--Get share link-->
|
||||
<AppInputText :title="$t('shared_form.label_share_vie_email')">
|
||||
<CopyShareLink :item="pickedItem" />
|
||||
</AppInputText>
|
||||
|
||||
<ValidationObserver @submit.prevent ref="shareForm" v-slot="{ invalid }" tag="form">
|
||||
<ValidationObserver @submit.prevent ref="shareForm" v-slot="{ invalid }" tag="form">
|
||||
<!--Permission Select-->
|
||||
<ValidationProvider v-if="isFolder" tag="div" mode="passive" name="Permission" rules="required" v-slot="{ errors }">
|
||||
<AppInputText :title="$t('shared_form.label_permission')" :error="errors[0]">
|
||||
<SelectInput
|
||||
v-model="shareOptions.permission"
|
||||
:options="$translateSelectOptions(permissionOptions)"
|
||||
:default="shareOptions.permission"
|
||||
:placeholder="$t('shared_form.placeholder_permission')"
|
||||
:isError="errors[0]"
|
||||
/>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
|
||||
<!--Permission Select-->
|
||||
<ValidationProvider v-if="isFolder" tag="div" mode="passive" name="Permission" rules="required" v-slot="{ errors }">
|
||||
<AppInputText :title="$t('shared_form.label_permission')" :error="errors[0]">
|
||||
<SelectInput v-model="shareOptions.permission" :options="$translateSelectOptions(permissionOptions)" :default="shareOptions.permission" :placeholder="$t('shared_form.placeholder_permission')" :isError="errors[0]" />
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
<!--Password Switch-->
|
||||
<div>
|
||||
<AppInputSwitch :title="$t('shared_form.label_password_protection')" :description="$t('popup.share.password_description')">
|
||||
<SwitchInput v-model="shareOptions.isProtected" class="switch" :state="shareOptions.isProtected" />
|
||||
</AppInputSwitch>
|
||||
|
||||
<!--Password Switch-->
|
||||
<div>
|
||||
<AppInputSwitch :title="$t('shared_form.label_password_protection')" :description="$t('popup.share.password_description')">
|
||||
<SwitchInput v-model="shareOptions.isProtected" class="switch" :state="shareOptions.isProtected" />
|
||||
</AppInputSwitch>
|
||||
<ActionButton
|
||||
v-if="pickedItem.data.relationships.shared.data.attributes.protected && canChangePassword && shareOptions.isProtected"
|
||||
@click.native="changePassword"
|
||||
class="mb-6 -mt-4"
|
||||
>
|
||||
{{ $t('popup_share_edit.change_pass') }}
|
||||
</ActionButton>
|
||||
|
||||
<ActionButton v-if="(pickedItem.data.relationships.shared.data.attributes.protected && canChangePassword) && shareOptions.isProtected" @click.native="changePassword" class="mb-6 -mt-4">
|
||||
{{ $t('popup_share_edit.change_pass') }}
|
||||
</ActionButton>
|
||||
<!--Set password-->
|
||||
<ValidationProvider v-if="shareOptions.isProtected && !canChangePassword" tag="div" mode="passive" name="Password" rules="required" v-slot="{ errors }">
|
||||
<AppInputText :error="errors[0]" class="-mt-2">
|
||||
<input
|
||||
v-model="shareOptions.password"
|
||||
:class="{ 'border-red': errors[0] }"
|
||||
type="text"
|
||||
class="focus-border-theme input-dark"
|
||||
:placeholder="$t('page_sign_in.placeholder_password')"
|
||||
/>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
|
||||
<!--Set password-->
|
||||
<ValidationProvider v-if="shareOptions.isProtected && ! canChangePassword" tag="div" mode="passive" name="Password" rules="required" v-slot="{ errors }">
|
||||
<AppInputText :error="errors[0]" class="-mt-2">
|
||||
<input v-model="shareOptions.password" :class="{'border-red': errors[0]}" type="text" class="focus-border-theme input-dark" :placeholder="$t('page_sign_in.placeholder_password')">
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
<!--Expiration switch-->
|
||||
<div>
|
||||
<AppInputSwitch :title="$t('expiration')" :description="$t('popup.share.expiration_description')" :is-last="!shareOptions.expiration">
|
||||
<SwitchInput v-model="shareOptions.expiration" class="switch" :state="shareOptions.expiration ? 1 : 0" />
|
||||
</AppInputSwitch>
|
||||
|
||||
<!--Expiration switch-->
|
||||
<div>
|
||||
<AppInputSwitch :title="$t('expiration')" :description="$t('popup.share.expiration_description')" :is-last="!shareOptions.expiration">
|
||||
<SwitchInput v-model="shareOptions.expiration" class="switch" :state="shareOptions.expiration ? 1 : 0" />
|
||||
</AppInputSwitch>
|
||||
<!--Set expiration-->
|
||||
<AppInputText v-if="shareOptions.expiration" class="-mt-2" :is-last="true">
|
||||
<SelectBoxInput v-model="shareOptions.expiration" :data="$translateSelectOptions(expirationList)" :value="shareOptions.expiration" class="box" />
|
||||
</AppInputText>
|
||||
</div>
|
||||
</ValidationObserver>
|
||||
</PopupContent>
|
||||
|
||||
<!--Set expiration-->
|
||||
<AppInputText v-if="shareOptions.expiration" class="-mt-2" :is-last="true">
|
||||
<SelectBoxInput v-model="shareOptions.expiration" :data="$translateSelectOptions(expirationList)" :value="shareOptions.expiration" class="box" />
|
||||
</AppInputText>
|
||||
</div>
|
||||
</ValidationObserver>
|
||||
</PopupContent>
|
||||
|
||||
<PopupActions>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
@click.native="destroySharing"
|
||||
:button-style="destroyButtonStyle"
|
||||
:loading="isDeleting"
|
||||
:disabled="isDeleting"
|
||||
>
|
||||
{{ destroyButtonText }}
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
@click.native="updateShareOptions"
|
||||
button-style="theme"
|
||||
:loading="isLoading"
|
||||
:disabled="isLoading"
|
||||
>
|
||||
{{ $t('Store Changes') }}
|
||||
</ButtonBase>
|
||||
</PopupActions>
|
||||
</div>
|
||||
<PopupActions>
|
||||
<ButtonBase class="w-full" @click.native="destroySharing" :button-style="destroyButtonStyle" :loading="isDeleting" :disabled="isDeleting">
|
||||
{{ destroyButtonText }}
|
||||
</ButtonBase>
|
||||
<ButtonBase class="w-full" @click.native="updateShareOptions" button-style="theme" :loading="isLoading" :disabled="isLoading">
|
||||
{{ $t('Store Changes') }}
|
||||
</ButtonBase>
|
||||
</PopupActions>
|
||||
</div>
|
||||
</PopupWrapper>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
|
||||
import MultiEmailInput from "./Forms/MultiEmailInput";
|
||||
import SelectBoxInput from './Forms/SelectBoxInput'
|
||||
import CopyShareLink from './Forms/CopyShareLink'
|
||||
import PopupWrapper from "./Popup/PopupWrapper";
|
||||
import PopupActions from './Popup/PopupActions'
|
||||
import PopupContent from './Popup/PopupContent'
|
||||
import PopupHeader from './Popup/PopupHeader'
|
||||
import SwitchInput from './Forms/SwitchInput'
|
||||
import SelectInput from './Forms/SelectInput'
|
||||
import ThumbnailItem from "./ThumbnailItem";
|
||||
import ActionButton from "./ActionButton";
|
||||
import ButtonBase from "../FilesView/ButtonBase";
|
||||
import AppInputSwitch from "../Admin/AppInputSwitch"
|
||||
import AppInputText from "../Admin/AppInputText"
|
||||
import {required} from 'vee-validate/dist/rules'
|
||||
import Spinner from "../FilesView/Spinner"
|
||||
import {events} from '../../bus'
|
||||
import {mapGetters} from 'vuex'
|
||||
import axios from 'axios'
|
||||
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
|
||||
import MultiEmailInput from './Forms/MultiEmailInput'
|
||||
import SelectBoxInput from './Forms/SelectBoxInput'
|
||||
import CopyShareLink from './Forms/CopyShareLink'
|
||||
import PopupWrapper from './Popup/PopupWrapper'
|
||||
import PopupActions from './Popup/PopupActions'
|
||||
import PopupContent from './Popup/PopupContent'
|
||||
import PopupHeader from './Popup/PopupHeader'
|
||||
import SwitchInput from './Forms/SwitchInput'
|
||||
import SelectInput from './Forms/SelectInput'
|
||||
import ThumbnailItem from './ThumbnailItem'
|
||||
import ActionButton from './ActionButton'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import AppInputSwitch from '../Admin/AppInputSwitch'
|
||||
import AppInputText from '../Admin/AppInputText'
|
||||
import { required } from 'vee-validate/dist/rules'
|
||||
import Spinner from '../FilesView/Spinner'
|
||||
import { events } from '../../bus'
|
||||
import { mapGetters } from 'vuex'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'ShareEditPopup',
|
||||
components: {
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
MultiEmailInput,
|
||||
AppInputSwitch,
|
||||
SelectBoxInput,
|
||||
ThumbnailItem,
|
||||
CopyShareLink,
|
||||
ActionButton,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
AppInputText,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
SelectInput,
|
||||
SwitchInput,
|
||||
ButtonBase,
|
||||
required,
|
||||
Spinner,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'permissionOptions',
|
||||
'expirationList',
|
||||
'user',
|
||||
]),
|
||||
popupTitle() {
|
||||
return {
|
||||
'qr-code': this.$t('Get your QR code'),
|
||||
'email-sharing': this.$t('Share on multiple emails'),
|
||||
}[this.activeSection] || this.$t('popup_share_edit.title')
|
||||
},
|
||||
isFolder() {
|
||||
return this.pickedItem && this.pickedItem.data.type === 'folder'
|
||||
},
|
||||
destroyButtonText() {
|
||||
return this.isConfirmedDestroy ? this.$t('popup_share_edit.confirm') : this.$t('popup_share_edit.stop')
|
||||
},
|
||||
destroyButtonStyle() {
|
||||
return this.isConfirmedDestroy ? 'danger-solid' : 'secondary'
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'shareOptions.expiration': function (val) {
|
||||
if (!val) {
|
||||
this.shareOptions.expiration = undefined
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeSection: undefined,
|
||||
shareOptions: undefined,
|
||||
pickedItem: undefined,
|
||||
emails: undefined,
|
||||
qrCode: undefined,
|
||||
isConfirmedDestroy: false,
|
||||
canChangePassword: false,
|
||||
isMoreOptions: false,
|
||||
isDeleting: false,
|
||||
isLoading: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getQrCode() {
|
||||
axios.get(`/api/share/${this.shareOptions.token}/qr`)
|
||||
.then(response => {
|
||||
this.qrCode = response.data
|
||||
})
|
||||
.catch(() => this.$isSomethingWrong())
|
||||
},
|
||||
showSection(section = undefined) {
|
||||
this.activeSection = section
|
||||
},
|
||||
changePassword() {
|
||||
this.canChangePassword = false
|
||||
},
|
||||
async sendViaEmail() {
|
||||
// Validate email field
|
||||
const isValid = await this.$refs.shareEmail.validate();
|
||||
export default {
|
||||
name: 'ShareEditPopup',
|
||||
components: {
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
MultiEmailInput,
|
||||
AppInputSwitch,
|
||||
SelectBoxInput,
|
||||
ThumbnailItem,
|
||||
CopyShareLink,
|
||||
ActionButton,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
AppInputText,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
SelectInput,
|
||||
SwitchInput,
|
||||
ButtonBase,
|
||||
required,
|
||||
Spinner,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permissionOptions', 'expirationList', 'user']),
|
||||
popupTitle() {
|
||||
return (
|
||||
{
|
||||
'qr-code': this.$t('Get your QR code'),
|
||||
'email-sharing': this.$t('Share on multiple emails'),
|
||||
}[this.activeSection] || this.$t('popup_share_edit.title')
|
||||
)
|
||||
},
|
||||
isFolder() {
|
||||
return this.pickedItem && this.pickedItem.data.type === 'folder'
|
||||
},
|
||||
destroyButtonText() {
|
||||
return this.isConfirmedDestroy ? this.$t('popup_share_edit.confirm') : this.$t('popup_share_edit.stop')
|
||||
},
|
||||
destroyButtonStyle() {
|
||||
return this.isConfirmedDestroy ? 'danger-solid' : 'secondary'
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'shareOptions.expiration': function (val) {
|
||||
if (!val) {
|
||||
this.shareOptions.expiration = undefined
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeSection: undefined,
|
||||
shareOptions: undefined,
|
||||
pickedItem: undefined,
|
||||
emails: undefined,
|
||||
qrCode: undefined,
|
||||
isConfirmedDestroy: false,
|
||||
canChangePassword: false,
|
||||
isMoreOptions: false,
|
||||
isDeleting: false,
|
||||
isLoading: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getQrCode() {
|
||||
axios
|
||||
.get(`/api/share/${this.shareOptions.token}/qr`)
|
||||
.then((response) => {
|
||||
this.qrCode = response.data
|
||||
})
|
||||
.catch(() => this.$isSomethingWrong())
|
||||
},
|
||||
showSection(section = undefined) {
|
||||
this.activeSection = section
|
||||
},
|
||||
changePassword() {
|
||||
this.canChangePassword = false
|
||||
},
|
||||
async sendViaEmail() {
|
||||
// Validate email field
|
||||
const isValid = await this.$refs.shareEmail.validate()
|
||||
|
||||
if (!isValid) return;
|
||||
if (!isValid) return
|
||||
|
||||
this.isLoading = true
|
||||
this.isLoading = true
|
||||
|
||||
axios.post(`/api/share/${this.shareOptions.token}/email`, {
|
||||
emails: this.emails
|
||||
})
|
||||
.catch(() => {
|
||||
this.$isSomethingWrong()
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false
|
||||
this.$closePopup()
|
||||
})
|
||||
},
|
||||
async destroySharing() {
|
||||
// Set confirm button
|
||||
if (!this.isConfirmedDestroy) {
|
||||
this.isConfirmedDestroy = true
|
||||
axios
|
||||
.post(`/api/share/${this.shareOptions.token}/email`, {
|
||||
emails: this.emails,
|
||||
})
|
||||
.catch(() => {
|
||||
this.$isSomethingWrong()
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false
|
||||
this.$closePopup()
|
||||
})
|
||||
},
|
||||
async destroySharing() {
|
||||
// Set confirm button
|
||||
if (!this.isConfirmedDestroy) {
|
||||
this.isConfirmedDestroy = true
|
||||
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Start deleting spinner button
|
||||
this.isDeleting = true
|
||||
// Start deleting spinner button
|
||||
this.isDeleting = true
|
||||
|
||||
// Send delete request
|
||||
await this.$store.dispatch('shareCancel', this.pickedItem)
|
||||
.catch(() => {
|
||||
// End deleting spinner button
|
||||
this.isDeleting = false
|
||||
}).finally(() => this.$closePopup())
|
||||
},
|
||||
async updateShareOptions() {
|
||||
// Validate fields
|
||||
const isValid = await this.$refs.shareForm.validate();
|
||||
// Send delete request
|
||||
await this.$store
|
||||
.dispatch('shareCancel', this.pickedItem)
|
||||
.catch(() => {
|
||||
// End deleting spinner button
|
||||
this.isDeleting = false
|
||||
})
|
||||
.finally(() => this.$closePopup())
|
||||
},
|
||||
async updateShareOptions() {
|
||||
// Validate fields
|
||||
const isValid = await this.$refs.shareForm.validate()
|
||||
|
||||
if (!isValid) return;
|
||||
if (!isValid) return
|
||||
|
||||
this.isLoading = true
|
||||
this.isLoading = true
|
||||
|
||||
// Send request to get share link
|
||||
axios
|
||||
.post('/api/share/' + this.shareOptions.token, {
|
||||
permission: this.shareOptions.permission,
|
||||
protected: this.shareOptions.isProtected,
|
||||
expiration: this.shareOptions.expiration,
|
||||
password: this.shareOptions.password ? this.shareOptions.password : undefined,
|
||||
_method: 'patch'
|
||||
})
|
||||
.then(response => {
|
||||
// Update shared data
|
||||
this.$store.commit('UPDATE_SHARED_ITEM', response.data)
|
||||
// Send request to get share link
|
||||
axios
|
||||
.post('/api/share/' + this.shareOptions.token, {
|
||||
permission: this.shareOptions.permission,
|
||||
protected: this.shareOptions.isProtected,
|
||||
expiration: this.shareOptions.expiration,
|
||||
password: this.shareOptions.password ? this.shareOptions.password : undefined,
|
||||
_method: 'patch',
|
||||
})
|
||||
.then((response) => {
|
||||
// Update shared data
|
||||
this.$store.commit('UPDATE_SHARED_ITEM', response.data)
|
||||
|
||||
events.$emit('popup:close')
|
||||
})
|
||||
.catch(() => {
|
||||
this.$isSomethingWrong()
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
events.$on('emailsInputValues', emails => this.emails = emails)
|
||||
events.$emit('popup:close')
|
||||
})
|
||||
.catch(() => {
|
||||
this.$isSomethingWrong()
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
events.$on('emailsInputValues', (emails) => (this.emails = emails))
|
||||
|
||||
// Show popup
|
||||
events.$on('popup:open', args => {
|
||||
// Show popup
|
||||
events.$on('popup:open', (args) => {
|
||||
if (args.name !== 'share-edit') return
|
||||
|
||||
if (args.name !== 'share-edit') return
|
||||
// Store picked item
|
||||
this.pickedItem = args.item
|
||||
|
||||
// Store picked item
|
||||
this.pickedItem = args.item
|
||||
// Store shared options
|
||||
this.shareOptions = {
|
||||
id: args.item.data.relationships.shared.data.id,
|
||||
token: args.item.data.relationships.shared.data.attributes.token,
|
||||
expiration: args.item.data.relationships.shared.data.attributes.expire_in,
|
||||
isProtected: args.item.data.relationships.shared.data.attributes.protected,
|
||||
permission: args.item.data.relationships.shared.data.attributes.permission,
|
||||
password: undefined,
|
||||
}
|
||||
|
||||
// Store shared options
|
||||
this.shareOptions = {
|
||||
id: args.item.data.relationships.shared.data.id,
|
||||
token: args.item.data.relationships.shared.data.attributes.token,
|
||||
expiration: args.item.data.relationships.shared.data.attributes.expire_in,
|
||||
isProtected: args.item.data.relationships.shared.data.attributes.protected,
|
||||
permission: args.item.data.relationships.shared.data.attributes.permission,
|
||||
password: undefined,
|
||||
}
|
||||
|
||||
if (args.section)
|
||||
this.activeSection = args.section
|
||||
if (args.section) this.activeSection = args.section
|
||||
|
||||
if (args.section === 'qr-code')
|
||||
this.getQrCode()
|
||||
if (args.section === 'qr-code') this.getQrCode()
|
||||
|
||||
this.canChangePassword = args.item.data.relationships.shared.data.attributes.protected
|
||||
})
|
||||
this.canChangePassword = args.item.data.relationships.shared.data.attributes.protected
|
||||
})
|
||||
|
||||
events.$on('popup:close', () => {
|
||||
|
||||
// Reset data
|
||||
setTimeout(() => {
|
||||
this.isDeleting = false
|
||||
this.isConfirmedDestroy = false
|
||||
this.canChangePassword = false
|
||||
this.shareOptions = undefined
|
||||
this.pickedItem = undefined
|
||||
this.activeSection = undefined
|
||||
this.qrCode = undefined
|
||||
}, 150)
|
||||
})
|
||||
}
|
||||
}
|
||||
events.$on('popup:close', () => {
|
||||
// Reset data
|
||||
setTimeout(() => {
|
||||
this.isDeleting = false
|
||||
this.isConfirmedDestroy = false
|
||||
this.canChangePassword = false
|
||||
this.shareOptions = undefined
|
||||
this.pickedItem = undefined
|
||||
this.activeSection = undefined
|
||||
this.qrCode = undefined
|
||||
}, 150)
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -5,18 +5,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "TabOption",
|
||||
name: 'TabOption',
|
||||
props: ['title', 'icon', 'selected'],
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
isActive: false
|
||||
isActive: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.isActive = this.selected
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,125 +2,117 @@
|
||||
<div>
|
||||
<div class="tab-wrapper">
|
||||
<div class="tab" :class="{ active: tab.isActive }" @click="selectTab(tab)" v-for="(tab, i) in tabs" :key="i">
|
||||
|
||||
<!--Icon-->
|
||||
<mail-icon v-if="tab.icon === 'email'" class="tab-icon text-theme dark-text-theme" size="17"/>
|
||||
<link-icon v-if="tab.icon === 'link'" class="tab-icon text-theme dark-text-theme" size="17"/>
|
||||
<smile-icon v-if="tab.icon === 'emoji'" class="tab-icon text-theme dark-text-theme" size="17"/>
|
||||
<folder-icon v-if="tab.icon === 'folder'" class="tab-icon text-theme dark-text-theme" size="17"/>
|
||||
<mail-icon v-if="tab.icon === 'email'" class="tab-icon text-theme dark-text-theme" size="17" />
|
||||
<link-icon v-if="tab.icon === 'link'" class="tab-icon text-theme dark-text-theme" size="17" />
|
||||
<smile-icon v-if="tab.icon === 'emoji'" class="tab-icon text-theme dark-text-theme" size="17" />
|
||||
<folder-icon v-if="tab.icon === 'folder'" class="tab-icon text-theme dark-text-theme" size="17" />
|
||||
|
||||
<!--Title-->
|
||||
<b class="tab-title">
|
||||
{{tab.title}}
|
||||
{{ tab.title }}
|
||||
</b>
|
||||
</div>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
import { LinkIcon, MailIcon, SmileIcon, FolderIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'TabWrapper',
|
||||
components: {
|
||||
LinkIcon,
|
||||
MailIcon,
|
||||
SmileIcon,
|
||||
FolderIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: "TabWrapper",
|
||||
components: {
|
||||
LinkIcon,
|
||||
MailIcon,
|
||||
SmileIcon,
|
||||
FolderIcon
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
tabs: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectTab(selectedTab) {
|
||||
this.tabs.forEach(tab => {
|
||||
tab.isActive = tab.title == selectedTab.title
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.tabs = this.$children
|
||||
FolderIcon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabs: [],
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectTab(selectedTab) {
|
||||
this.tabs.forEach((tab) => {
|
||||
tab.isActive = tab.title == selectedTab.title
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.tabs = this.$children
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../../../sass/vuefilemanager/inapp-forms';
|
||||
@import '../../../sass/vuefilemanager/forms';
|
||||
@import '../../../sass/vuefilemanager/inapp-forms';
|
||||
@import '../../../sass/vuefilemanager/forms';
|
||||
|
||||
.tab-wrapper {
|
||||
.tab-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
background: white;
|
||||
color: $text;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e9eb;
|
||||
|
||||
.tab-title {
|
||||
@include font-size(14);
|
||||
}
|
||||
|
||||
.tab {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
background: white;
|
||||
color: $text;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #E8E9EB;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
|
||||
.tab-title {
|
||||
@include font-size(14);
|
||||
}
|
||||
&.active {
|
||||
background: $light_background;
|
||||
|
||||
.tab {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
|
||||
&.active {
|
||||
background: $light_background;
|
||||
|
||||
.tab-title {
|
||||
color: $text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab-icon {
|
||||
margin-right: 10px;
|
||||
|
||||
path,
|
||||
circle,
|
||||
line,
|
||||
polyline {
|
||||
color: inherit !important;
|
||||
.tab-title {
|
||||
color: $text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab-icon {
|
||||
margin-right: 10px;
|
||||
|
||||
.dark {
|
||||
path,
|
||||
circle,
|
||||
line,
|
||||
polyline {
|
||||
color: inherit !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.tab-wrapper {
|
||||
background: $dark_mode_foreground;
|
||||
border-color: transparent;
|
||||
|
||||
.tab.active {
|
||||
background: lighten($dark_mode_foreground, 7%);
|
||||
|
||||
.tab-title {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.popup-wrapper {
|
||||
.tab-wrapper {
|
||||
background: $dark_mode_foreground;
|
||||
border-color: transparent;
|
||||
|
||||
.tab.active {
|
||||
background: lighten($dark_mode_foreground, 7%);
|
||||
|
||||
.tab-title {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.popup-wrapper {
|
||||
.tab-wrapper {
|
||||
background: lighten($dark_mode_foreground, 2%);
|
||||
}
|
||||
background: lighten($dark_mode_foreground, 2%);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,58 +1,54 @@
|
||||
<template>
|
||||
<template>
|
||||
<tr class="table-row">
|
||||
<td
|
||||
class="table-cell"
|
||||
v-for="(collumn, index) in normalizedColumns"
|
||||
:key="index"
|
||||
>
|
||||
<td class="table-cell" v-for="(collumn, index) in normalizedColumns" :key="index">
|
||||
<span>{{ collumn }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['data'],
|
||||
computed: {
|
||||
normalizedColumns() {
|
||||
// Remove ID from object
|
||||
if (this.data['id']) delete this.data['id']
|
||||
export default {
|
||||
props: ['data'],
|
||||
computed: {
|
||||
normalizedColumns() {
|
||||
// Remove ID from object
|
||||
if (this.data['id']) delete this.data['id']
|
||||
|
||||
// Return object
|
||||
return Object.values(this.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return object
|
||||
return Object.values(this.data)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.table-row {
|
||||
border-radius: 8px;
|
||||
.table-row {
|
||||
border-radius: 8px;
|
||||
|
||||
&:hover {
|
||||
background: $light_background;
|
||||
&:hover {
|
||||
background: $light_background;
|
||||
}
|
||||
|
||||
.table-cell {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.table-cell {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
&:last-child {
|
||||
padding-right: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
span {
|
||||
@include font-size(16);
|
||||
font-weight: bold;
|
||||
}
|
||||
span {
|
||||
@include font-size(16);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,85 +1,70 @@
|
||||
<template>
|
||||
<div class="flex items-center shrink-0 grow-0">
|
||||
<MemberAvatar
|
||||
class="mr-3 shrink-0"
|
||||
:is-border="false"
|
||||
:size="52"
|
||||
:member="member"
|
||||
/>
|
||||
<div class="info">
|
||||
<b class="name" v-if="title">{{ title }}</b>
|
||||
<span class="description" v-if="description">{{ description }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<template>
|
||||
<div class="flex shrink-0 grow-0 items-center">
|
||||
<MemberAvatar class="mr-3 shrink-0" :is-border="false" :size="52" :member="member" />
|
||||
<div class="info">
|
||||
<b class="name" v-if="title">{{ title }}</b>
|
||||
<span class="description" v-if="description">{{ description }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MemberAvatar from "../../FilesView/MemberAvatar";
|
||||
import MemberAvatar from '../../FilesView/MemberAvatar'
|
||||
|
||||
export default {
|
||||
name:'DatatableCellImage',
|
||||
props: [
|
||||
'member',
|
||||
'title',
|
||||
'description',
|
||||
'image-size'
|
||||
],
|
||||
components: {
|
||||
MemberAvatar,
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: 'DatatableCellImage',
|
||||
props: ['member', 'title', 'description', 'image-size'],
|
||||
components: {
|
||||
MemberAvatar,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../../sass/vuefilemanager/variables';
|
||||
@import '../../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.info {
|
||||
.info {
|
||||
.name,
|
||||
.description {
|
||||
max-width: 150px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.name, .description {
|
||||
max-width: 150px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
}
|
||||
.name {
|
||||
@include font-size(15);
|
||||
line-height: 1;
|
||||
color: $text;
|
||||
}
|
||||
|
||||
.name {
|
||||
@include font-size(15);
|
||||
line-height: 1;
|
||||
color: $text;
|
||||
}
|
||||
.description {
|
||||
color: $text-muted;
|
||||
@include font-size(12);
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
color: $text-muted;
|
||||
@include font-size(12);
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
||||
.cell-image-thumbnail {
|
||||
|
||||
.image {
|
||||
|
||||
img {
|
||||
|
||||
&.blurred {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
|
||||
.name {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: $dark_mode_text_secondary;
|
||||
.dark {
|
||||
.cell-image-thumbnail {
|
||||
.image {
|
||||
img {
|
||||
&.blurred {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
.name {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,240 +2,271 @@
|
||||
<div class="w-full">
|
||||
<table v-if="hasData" class="w-full">
|
||||
<thead>
|
||||
<tr class="whitespace-nowrap">
|
||||
<th
|
||||
class="text-left"
|
||||
v-for="(column, index) in columns"
|
||||
@click="sort(column.field, column.sortable)"
|
||||
:key="index"
|
||||
:class="{ 'sortable cursor-pointer': column.sortable, 'text-right': (Object.values(columns).length - 1) === index }"
|
||||
v-if="! column.hidden"
|
||||
>
|
||||
<span class="dark:text-gray-500 text-gray-400 text-xs">
|
||||
{{ column.label }}
|
||||
</span>
|
||||
<tr class="whitespace-nowrap">
|
||||
<th
|
||||
class="text-left"
|
||||
v-for="(column, index) in columns"
|
||||
@click="sort(column.field, column.sortable)"
|
||||
:key="index"
|
||||
:class="{
|
||||
'sortable cursor-pointer': column.sortable,
|
||||
'text-right': Object.values(columns).length - 1 === index,
|
||||
}"
|
||||
v-if="!column.hidden"
|
||||
>
|
||||
<span class="text-xs text-gray-400 dark:text-gray-500">
|
||||
{{ column.label }}
|
||||
</span>
|
||||
|
||||
<chevron-up-icon
|
||||
v-if="column.sortable"
|
||||
:class="{ 'arrow-down': filter.sort === 'ASC' }"
|
||||
class="inline-block vue-feather dark:text-gray-500 text-gray-300"
|
||||
size="12"
|
||||
/>
|
||||
</th>
|
||||
</tr>
|
||||
<chevron-up-icon
|
||||
v-if="column.sortable"
|
||||
:class="{ 'arrow-down': filter.sort === 'ASC' }"
|
||||
class="vue-feather inline-block text-gray-300 dark:text-gray-500"
|
||||
size="12"
|
||||
/>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="table-body">
|
||||
<slot v-for="row in data.data" :row="row">
|
||||
<DatatableCell :data="row" :key="row.id" />
|
||||
</slot>
|
||||
<slot v-for="row in data.data" :row="row">
|
||||
<DatatableCell :data="row" :key="row.id" />
|
||||
</slot>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!--Empty data slot-->
|
||||
<slot v-if="! isLoading && ! hasData" name="empty-page"></slot>
|
||||
|
||||
<!--Paginator-->
|
||||
<div v-if="paginator && hasData" class="mt-6 flex justify-between items-center">
|
||||
<!--Empty data slot-->
|
||||
<slot v-if="!isLoading && !hasData" name="empty-page"></slot>
|
||||
|
||||
<!--Paginator-->
|
||||
<div v-if="paginator && hasData" class="mt-6 flex items-center justify-between">
|
||||
<!--Show if there is only 6 pages-->
|
||||
<ul v-if="data.meta.total > 15 && data.meta.last_page <= 6" class="pagination flex items-center">
|
||||
|
||||
<!--Go previous icon-->
|
||||
<li class="p-1 inline-block previous">
|
||||
<a @click="goToPage(pageIndex - 1)" class="page-link" :class="{ 'opacity-20 cursor-default': pageIndex === 1 }">
|
||||
<li class="previous inline-block p-1">
|
||||
<a
|
||||
@click="goToPage(pageIndex - 1)"
|
||||
class="page-link"
|
||||
:class="{
|
||||
'cursor-default opacity-20': pageIndex === 1,
|
||||
}"
|
||||
>
|
||||
<chevron-left-icon size="14" class="inline-block" />
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li v-for="(page, index) in data.meta.last_page" :key="index" class="p-1 inline-block" @click="goToPage(page)">
|
||||
<a class="page-link" :class="{'dark:text-gray-300 dark:bg-4x-dark-foreground bg-light-background': pageIndex === page }">
|
||||
<li v-for="(page, index) in data.meta.last_page" :key="index" class="inline-block p-1" @click="goToPage(page)">
|
||||
<a
|
||||
class="page-link"
|
||||
:class="{
|
||||
'bg-light-background dark:bg-4x-dark-foreground dark:text-gray-300': pageIndex === page,
|
||||
}"
|
||||
>
|
||||
{{ page }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!--Go next icon-->
|
||||
<li class="p-1 inline-block next">
|
||||
<a @click="goToPage(pageIndex + 1)" class="page-link" :class="{ 'opacity-20 cursor-default': pageIndex === data.meta.last_page }">
|
||||
<!--Go next icon-->
|
||||
<li class="next inline-block p-1">
|
||||
<a
|
||||
@click="goToPage(pageIndex + 1)"
|
||||
class="page-link"
|
||||
:class="{
|
||||
'cursor-default opacity-20': pageIndex === data.meta.last_page,
|
||||
}"
|
||||
>
|
||||
<chevron-right-icon size="14" class="inline-block" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!--Show if there is more than 6 pages-->
|
||||
<!--Show if there is more than 6 pages-->
|
||||
<ul v-if="data.meta.total > 15 && data.meta.last_page > 6" class="pagination flex items-center">
|
||||
|
||||
<!--Go previous icon-->
|
||||
<li class="p-1 inline-block previous">
|
||||
<a @click="goToPage(pageIndex - 1)" class="page-link" :class="{'opacity-20 cursor-default': pageIndex === 1 }">
|
||||
<li class="previous inline-block p-1">
|
||||
<a
|
||||
@click="goToPage(pageIndex - 1)"
|
||||
class="page-link"
|
||||
:class="{
|
||||
'cursor-default opacity-20': pageIndex === 1,
|
||||
}"
|
||||
>
|
||||
<chevron-left-icon size="14" class="inline-block" />
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!--Show first Page-->
|
||||
<li class="p-1 inline-block" v-if="pageIndex >= 5" @click="goToPage(1)">
|
||||
<a class="page-link">
|
||||
1
|
||||
</a>
|
||||
<!--Show first Page-->
|
||||
<li class="inline-block p-1" v-if="pageIndex >= 5" @click="goToPage(1)">
|
||||
<a class="page-link"> 1 </a>
|
||||
</li>
|
||||
|
||||
<li v-if="pageIndex < 5" v-for="(page, index) in 5" :key="index" class="p-1 inline-block" @click="goToPage(page)">
|
||||
<a class="page-link" :class="{'dark:text-gray-300 dark:bg-4x-dark-foreground bg-light-background': pageIndex === page }">
|
||||
<li v-if="pageIndex < 5" v-for="(page, index) in 5" :key="index" class="inline-block p-1" @click="goToPage(page)">
|
||||
<a
|
||||
class="page-link"
|
||||
:class="{
|
||||
'bg-light-background dark:bg-4x-dark-foreground dark:text-gray-300': pageIndex === page,
|
||||
}"
|
||||
>
|
||||
{{ page }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="p-1 inline-block" v-if="pageIndex >= 5">
|
||||
<li class="inline-block p-1" v-if="pageIndex >= 5">
|
||||
<a class="page-link">...</a>
|
||||
</li>
|
||||
|
||||
<!--Floated Pages-->
|
||||
<li v-if="pageIndex >= 5 && pageIndex < (data.meta.last_page - 3)" v-for="(page, index) in floatPages" :key="index" class="p-1 inline-block" @click="goToPage(page)">
|
||||
<a class="page-link" :class="{'dark:text-gray-300 dark:bg-4x-dark-foreground bg-light-background': pageIndex === page }">
|
||||
<!--Floated Pages-->
|
||||
<li v-if="pageIndex >= 5 && pageIndex < data.meta.last_page - 3" v-for="(page, index) in floatPages" :key="index" class="inline-block p-1" @click="goToPage(page)">
|
||||
<a
|
||||
class="page-link"
|
||||
:class="{
|
||||
'bg-light-background dark:bg-4x-dark-foreground dark:text-gray-300': pageIndex === page,
|
||||
}"
|
||||
>
|
||||
{{ page }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="p-1 inline-block" v-if="pageIndex < (data.meta.last_page - 3)">
|
||||
<li class="inline-block p-1" v-if="pageIndex < data.meta.last_page - 3">
|
||||
<a class="page-link">...</a>
|
||||
</li>
|
||||
|
||||
<li v-if="pageIndex > (data.meta.last_page - 4)" v-for="(page, index) in 5" :key="index" class="p-1 inline-block" @click="goToPage(data.meta.last_page - (4 - index))">
|
||||
<a class="page-link" :class="{ 'dark:text-gray-300 dark:bg-4x-dark-foreground bg-light-background': pageIndex === (data.meta.last_page - (4 - index)) }">
|
||||
<li
|
||||
v-if="pageIndex > data.meta.last_page - 4"
|
||||
v-for="(page, index) in 5"
|
||||
:key="index"
|
||||
class="inline-block p-1"
|
||||
@click="goToPage(data.meta.last_page - (4 - index))"
|
||||
>
|
||||
<a
|
||||
class="page-link"
|
||||
:class="{
|
||||
'bg-light-background dark:bg-4x-dark-foreground dark:text-gray-300': pageIndex === data.meta.last_page - (4 - index),
|
||||
}"
|
||||
>
|
||||
{{ data.meta.last_page - (4 - index) }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!--Show last page-->
|
||||
<li class="p-1 inline-block" v-if="pageIndex < (data.meta.last_page - 3)" @click="goToPage(data.meta.last_page)">
|
||||
<!--Show last page-->
|
||||
<li class="inline-block p-1" v-if="pageIndex < data.meta.last_page - 3" @click="goToPage(data.meta.last_page)">
|
||||
<a class="page-link">
|
||||
{{ data.meta.last_page }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!--Go next icon-->
|
||||
<li class="p-1 inline-block next">
|
||||
<a @click="goToPage(pageIndex + 1)" class="page-link" :class="{ 'opacity-20 cursor-default': pageIndex === data.meta.last_page }">
|
||||
<!--Go next icon-->
|
||||
<li class="next inline-block p-1">
|
||||
<a
|
||||
@click="goToPage(pageIndex + 1)"
|
||||
class="page-link"
|
||||
:class="{
|
||||
'cursor-default opacity-20': pageIndex === data.meta.last_page,
|
||||
}"
|
||||
>
|
||||
<chevron-right-icon size="14" class="inline-block" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<span class="dark:text-gray-500 text-xs text-gray-600">
|
||||
Showing {{ data.meta.from }} - {{ data.meta.to }} from {{ data.meta.total }} records
|
||||
</span>
|
||||
<span class="text-xs text-gray-600 dark:text-gray-500"> Showing {{ data.meta.from }} - {{ data.meta.to }} from {{ data.meta.total }} records </span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {ChevronUpIcon, ChevronLeftIcon, ChevronRightIcon} from 'vue-feather-icons'
|
||||
import DatatableCell from "./DatatableCell";
|
||||
import axios from "axios";
|
||||
import { ChevronUpIcon, ChevronLeftIcon, ChevronRightIcon } from 'vue-feather-icons'
|
||||
import DatatableCell from './DatatableCell'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'DatatableWrapper',
|
||||
props: [
|
||||
'paginator',
|
||||
'tableData',
|
||||
'columns',
|
||||
'scope',
|
||||
'api',
|
||||
],
|
||||
components: {
|
||||
ChevronRightIcon,
|
||||
ChevronLeftIcon,
|
||||
DatatableCell,
|
||||
ChevronUpIcon,
|
||||
},
|
||||
computed: {
|
||||
hasData() {
|
||||
return this.data && this.data.data && this.data.data.length > 0
|
||||
},
|
||||
floatPages() {
|
||||
return [(this.pageIndex - 1), this.pageIndex, (this.pageIndex + 1)];
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: undefined,
|
||||
isLoading: true,
|
||||
pageIndex: 1,
|
||||
filter: {
|
||||
sort: 'DESC',
|
||||
field: undefined,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goToPage(index) {
|
||||
if (index > this.data.meta.last_page || index === 0) return
|
||||
name: 'DatatableWrapper',
|
||||
props: ['paginator', 'tableData', 'columns', 'scope', 'api'],
|
||||
components: {
|
||||
ChevronRightIcon,
|
||||
ChevronLeftIcon,
|
||||
DatatableCell,
|
||||
ChevronUpIcon,
|
||||
},
|
||||
computed: {
|
||||
hasData() {
|
||||
return this.data && this.data.data && this.data.data.length > 0
|
||||
},
|
||||
floatPages() {
|
||||
return [this.pageIndex - 1, this.pageIndex, this.pageIndex + 1]
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: undefined,
|
||||
isLoading: true,
|
||||
pageIndex: 1,
|
||||
filter: {
|
||||
sort: 'DESC',
|
||||
field: undefined,
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goToPage(index) {
|
||||
if (index > this.data.meta.last_page || index === 0) return
|
||||
|
||||
this.pageIndex = index
|
||||
this.pageIndex = index
|
||||
|
||||
this.getPage(index)
|
||||
},
|
||||
sort(field, sortable) {
|
||||
this.getPage(index)
|
||||
},
|
||||
sort(field, sortable) {
|
||||
// Prevent sortable if is disabled
|
||||
if (!sortable) return
|
||||
|
||||
// Prevent sortable if is disabled
|
||||
if (!sortable) return
|
||||
// Set filter
|
||||
this.filter.field = field
|
||||
|
||||
// Set filter
|
||||
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'
|
||||
}
|
||||
|
||||
// Set sorting direction
|
||||
if (this.filter.sort === 'DESC') {
|
||||
this.filter.sort = 'ASC'
|
||||
} else if (this.filter.sort === 'ASC') {
|
||||
this.filter.sort = 'DESC'
|
||||
}
|
||||
this.getPage(this.pageIndex)
|
||||
},
|
||||
getPage(page) {
|
||||
// Get api URI
|
||||
this.URI = this.api
|
||||
|
||||
this.getPage(this.pageIndex)
|
||||
},
|
||||
getPage(page) {
|
||||
// Set page index
|
||||
if (this.paginator) this.URI = this.URI + '?page=' + page
|
||||
|
||||
// Get api URI
|
||||
this.URI = this.api;
|
||||
// Add filder URI if is defined sorting
|
||||
if (this.filter.field) this.URI = this.URI + (this.paginator ? '&' : '?') + 'sort=' + this.filter.field + '&direction=' + this.filter.sort
|
||||
|
||||
// Set page index
|
||||
if (this.paginator)
|
||||
this.URI = this.URI + '?page=' + page
|
||||
this.isLoading = true
|
||||
|
||||
// Add filder URI if is defined sorting
|
||||
if (this.filter.field)
|
||||
// Get data
|
||||
axios
|
||||
.get(this.URI)
|
||||
.then((response) => {
|
||||
this.data = response.data
|
||||
this.$emit('data', response.data)
|
||||
})
|
||||
.catch(() => this.$isSomethingWrong())
|
||||
.finally(() => {
|
||||
this.$emit('init', true)
|
||||
this.isLoading = false
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.api) this.getPage(this.pageIndex)
|
||||
|
||||
this.URI = this.URI + (this.paginator ? '&' : '?') + 'sort=' + this.filter.field + '&direction=' + this.filter.sort
|
||||
|
||||
this.isLoading = true
|
||||
|
||||
// Get data
|
||||
axios.get(this.URI)
|
||||
.then(response => {
|
||||
this.data = response.data
|
||||
this.$emit('data', response.data)
|
||||
|
||||
})
|
||||
.catch(() => this.$isSomethingWrong())
|
||||
.finally(() => {
|
||||
this.$emit('init', true)
|
||||
this.isLoading = false
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.api)
|
||||
this.getPage(this.pageIndex)
|
||||
|
||||
if (this.tableData)
|
||||
this.data = this.tableData,
|
||||
this.isLoading = false
|
||||
}
|
||||
if (this.tableData) (this.data = this.tableData), (this.isLoading = false)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-link {
|
||||
@apply w-8 h-8 flex justify-center items-center block rounded-lg font-bold text-sm cursor-pointer hover:bg-light-background transition duration-200;
|
||||
}
|
||||
</style>
|
||||
.page-link {
|
||||
@apply block flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg text-sm font-bold transition duration-200 hover:bg-light-background;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,33 +5,33 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TextLabel',
|
||||
}
|
||||
export default {
|
||||
name: 'TextLabel',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.text-label {
|
||||
padding-left: 25px;
|
||||
@include font-size(12);
|
||||
color: #afafaf;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1024px) {
|
||||
.text-label {
|
||||
padding-left: 25px;
|
||||
@include font-size(12);
|
||||
color: #AFAFAF;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1024px) {
|
||||
.text-label {
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.text-label {
|
||||
opacity: 0.35;
|
||||
}
|
||||
.dark {
|
||||
.text-label {
|
||||
opacity: 0.35;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TextLabel',
|
||||
}
|
||||
export default {
|
||||
name: 'TextLabel',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.theme-label {
|
||||
@include font-size(14);
|
||||
color: $theme;
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.theme-label {
|
||||
@include font-size(14);
|
||||
color: $theme;
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,135 +1,118 @@
|
||||
<template>
|
||||
<div class="flex items-center rounded-xl select-none" spellcheck="false">
|
||||
<div class="flex select-none items-center rounded-xl" spellcheck="false">
|
||||
<!--Item thumbnail-->
|
||||
<div class="relative w-16">
|
||||
<!--Member thumbnail for team folders-->
|
||||
<MemberAvatar v-if="user && canShowAuthor" :size="28" :is-border="true" :member="item.data.relationships.owner" class="absolute right-1.5 -bottom-2 z-10" />
|
||||
|
||||
<!--Item thumbnail-->
|
||||
<div class="w-16 relative">
|
||||
<!--Emoji Icon-->
|
||||
<Emoji v-if="item.data.attributes.emoji" :emoji="item.data.attributes.emoji" class="ml-1 scale-110 transform text-5xl" />
|
||||
|
||||
<!--Member thumbnail for team folders-->
|
||||
<MemberAvatar
|
||||
v-if="user && canShowAuthor"
|
||||
:size="28"
|
||||
:is-border="true"
|
||||
:member="item.data.relationships.owner"
|
||||
class="absolute right-1.5 -bottom-2 z-10"
|
||||
/>
|
||||
<!--Folder Icon-->
|
||||
<FolderIcon v-if="isFolder && !item.data.attributes.emoji" :item="item" />
|
||||
|
||||
<!--Emoji Icon-->
|
||||
<Emoji
|
||||
v-if="item.data.attributes.emoji"
|
||||
:emoji="item.data.attributes.emoji"
|
||||
class="text-5xl ml-1 transform scale-110"
|
||||
/>
|
||||
<!--File Icon-->
|
||||
<FileIconThumbnail v-if="isFile || isVideo || isAudio || (isImage && !item.data.attributes.thumbnail)" :item="item" class="pr-2" />
|
||||
|
||||
<!--Folder Icon-->
|
||||
<FolderIcon v-if="isFolder && !item.data.attributes.emoji" :item="item" />
|
||||
<!--Image thumbnail-->
|
||||
<img
|
||||
v-if="isImage && item.data.attributes.thumbnail"
|
||||
class="ml-0.5 h-12 w-12 rounded object-cover"
|
||||
:src="item.data.attributes.thumbnail.xs"
|
||||
:alt="item.data.attributes.name"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!--File Icon-->
|
||||
<FileIconThumbnail v-if="isFile || isVideo || isAudio || (isImage && !item.data.attributes.thumbnail)" :item="item" class="pr-2" />
|
||||
<!--Item Info-->
|
||||
<div class="pl-2">
|
||||
<!--Item Title-->
|
||||
<b class="mb-0.5 block overflow-hidden text-ellipsis whitespace-nowrap text-sm hover:underline" style="max-width: 240px">
|
||||
{{ item.data.attributes.name }}
|
||||
</b>
|
||||
|
||||
<!--Image thumbnail-->
|
||||
<img v-if="isImage && item.data.attributes.thumbnail" class="w-12 h-12 rounded ml-0.5 object-cover" :src="item.data.attributes.thumbnail.xs" :alt="item.data.attributes.name" loading="lazy" />
|
||||
</div>
|
||||
<!--Item sub line-->
|
||||
<div class="flex items-center">
|
||||
<!--Shared Icon-->
|
||||
<div v-if="$checkPermission('master') && item.data.relationships.shared">
|
||||
<link-icon size="12" class="text-theme dark-text-theme vue-feather mr-1.5" />
|
||||
</div>
|
||||
|
||||
<!--Item Info-->
|
||||
<div class="pl-2">
|
||||
<!--File & Image sub line-->
|
||||
<small v-if="!isFolder" class="block text-xs text-gray-500"> {{ item.data.attributes.filesize }}, {{ timeStamp }} </small>
|
||||
|
||||
<!--Item Title-->
|
||||
<b class="block text-sm mb-0.5 text-ellipsis overflow-hidden hover:underline whitespace-nowrap" style="max-width: 240px">
|
||||
{{ item.data.attributes.name }}
|
||||
</b>
|
||||
|
||||
<!--Item sub line-->
|
||||
<div class="flex items-center">
|
||||
|
||||
<!--Shared Icon-->
|
||||
<div v-if="$checkPermission('master') && item.data.relationships.shared">
|
||||
<link-icon size="12" class="mr-1.5 text-theme dark-text-theme vue-feather"/>
|
||||
</div>
|
||||
|
||||
<!--File & Image sub line-->
|
||||
<small v-if="! isFolder" class="block text-xs text-gray-500">
|
||||
{{ item.data.attributes.filesize }}, {{ timeStamp }}
|
||||
</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) }}, {{ timeStamp }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--Folder sub line-->
|
||||
<small v-if="isFolder" class="block text-xs text-gray-500">
|
||||
{{ folderItems === 0 ? $t('folder.empty') : $tc('folder.item_counts', folderItems) }}, {{ timeStamp }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {LinkIcon, EyeIcon} from 'vue-feather-icons'
|
||||
import FileIconThumbnail from "../FilesView/FileIconThumbnail";
|
||||
import MemberAvatar from "../FilesView/MemberAvatar";
|
||||
import Emoji from "./Emoji";
|
||||
import {mapGetters} from 'vuex'
|
||||
import FolderIcon from "../FilesView/FolderIcon";
|
||||
import { LinkIcon, EyeIcon } from 'vue-feather-icons'
|
||||
import FileIconThumbnail from '../FilesView/FileIconThumbnail'
|
||||
import MemberAvatar from '../FilesView/MemberAvatar'
|
||||
import Emoji from './Emoji'
|
||||
import { mapGetters } from 'vuex'
|
||||
import FolderIcon from '../FilesView/FolderIcon'
|
||||
|
||||
export default {
|
||||
name: 'ThumbnailItem',
|
||||
props: [
|
||||
'setFolderIcon',
|
||||
'item',
|
||||
'info',
|
||||
],
|
||||
components: {
|
||||
FileIconThumbnail,
|
||||
MemberAvatar,
|
||||
FolderIcon,
|
||||
Emoji,
|
||||
LinkIcon,
|
||||
EyeIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'isMultiSelectMode',
|
||||
'clipboard',
|
||||
'user',
|
||||
]),
|
||||
isClicked() {
|
||||
return this.clipboard.some(element => element.data.id === this.item.data.id)
|
||||
},
|
||||
isVideo() {
|
||||
return this.item.data.type === 'video'
|
||||
},
|
||||
isAudio() {
|
||||
return this.item.data.type === 'audio'
|
||||
},
|
||||
isFile() {
|
||||
return this.item.data.type === 'file'
|
||||
},
|
||||
isImage() {
|
||||
return this.item.data.type === 'image'
|
||||
},
|
||||
isFolder() {
|
||||
return this.item.data.type === 'folder'
|
||||
},
|
||||
timeStamp() {
|
||||
return this.item.data.attributes.deleted_at
|
||||
? this.$t('item_thumbnail.deleted_at', {time: this.item.data.attributes.deleted_at})
|
||||
: this.item.data.attributes.created_at
|
||||
},
|
||||
canEditName() {
|
||||
return !this.$isMobile()
|
||||
&& !this.$isThisRoute(this.$route, ['Trash'])
|
||||
&& !this.$checkPermission('visitor')
|
||||
&& !(this.sharedDetail && this.sharedDetail.attributes.type === 'file')
|
||||
},
|
||||
folderItems() {
|
||||
return this.item.data.attributes.deleted_at
|
||||
? this.item.data.attributes.trashed_items
|
||||
: this.item.data.attributes.items
|
||||
},
|
||||
canShowAuthor() {
|
||||
return !this.isFolder
|
||||
&& this.user.data.id !== this.item.data.relationships.owner.data.id
|
||||
},
|
||||
canDrag() {
|
||||
return !this.isDeleted && this.$checkPermission(['master', 'editor'])
|
||||
},
|
||||
export default {
|
||||
name: 'ThumbnailItem',
|
||||
props: ['setFolderIcon', 'item', 'info'],
|
||||
components: {
|
||||
FileIconThumbnail,
|
||||
MemberAvatar,
|
||||
FolderIcon,
|
||||
Emoji,
|
||||
LinkIcon,
|
||||
EyeIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isMultiSelectMode', 'clipboard', 'user']),
|
||||
isClicked() {
|
||||
return this.clipboard.some((element) => element.data.id === this.item.data.id)
|
||||
},
|
||||
}
|
||||
isVideo() {
|
||||
return this.item.data.type === 'video'
|
||||
},
|
||||
isAudio() {
|
||||
return this.item.data.type === 'audio'
|
||||
},
|
||||
isFile() {
|
||||
return this.item.data.type === 'file'
|
||||
},
|
||||
isImage() {
|
||||
return this.item.data.type === 'image'
|
||||
},
|
||||
isFolder() {
|
||||
return this.item.data.type === 'folder'
|
||||
},
|
||||
timeStamp() {
|
||||
return this.item.data.attributes.deleted_at
|
||||
? this.$t('item_thumbnail.deleted_at', {
|
||||
time: this.item.data.attributes.deleted_at,
|
||||
})
|
||||
: this.item.data.attributes.created_at
|
||||
},
|
||||
canEditName() {
|
||||
return (
|
||||
!this.$isMobile() &&
|
||||
!this.$isThisRoute(this.$route, ['Trash']) &&
|
||||
!this.$checkPermission('visitor') &&
|
||||
!(this.sharedDetail && this.sharedDetail.attributes.type === 'file')
|
||||
)
|
||||
},
|
||||
folderItems() {
|
||||
return this.item.data.attributes.deleted_at ? this.item.data.attributes.trashed_items : this.item.data.attributes.items
|
||||
},
|
||||
canShowAuthor() {
|
||||
return !this.isFolder && this.user.data.id !== this.item.data.relationships.owner.data.id
|
||||
},
|
||||
canDrag() {
|
||||
return !this.isDeleted && this.$checkPermission(['master', 'editor'])
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,134 +1,125 @@
|
||||
<template>
|
||||
<div :class="{'opacity-50 pointer-events-none': disabledById && disabledById.data.id === nodes.id || !disableId, 'mb-2.5': isRootDepth}">
|
||||
|
||||
<div
|
||||
:class="{
|
||||
'pointer-events-none opacity-50': (disabledById && disabledById.data.id === nodes.id) || !disableId,
|
||||
'mb-2.5': isRootDepth,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
:class="{'is-disabled-item': false}"
|
||||
:style="indent"
|
||||
class="relative flex items-center select-none py-2 px-1.5 cursor-pointer relative whitespace-nowrap transition-all duration-150"
|
||||
>
|
||||
<!--Arrow icon-->
|
||||
<span @click.stop="showTree" class="p-2 -m-2">
|
||||
<chevron-right-icon
|
||||
:class="{'transform rotate-90': isVisible, 'text-theme dark-text-theme': isSelectedItem, 'opacity-100': nodes.folders.length !== 0}"
|
||||
class="vue-feather transition-all duration-300 mr-2 opacity-0"
|
||||
size="17"
|
||||
/>
|
||||
</span>
|
||||
:class="{ 'is-disabled-item': false }"
|
||||
:style="indent"
|
||||
class="relative relative flex cursor-pointer select-none items-center whitespace-nowrap py-2 px-1.5 transition-all duration-150"
|
||||
>
|
||||
<!--Arrow icon-->
|
||||
<span @click.stop="showTree" class="-m-2 p-2">
|
||||
<chevron-right-icon
|
||||
:class="{
|
||||
'rotate-90 transform': isVisible,
|
||||
'text-theme dark-text-theme': isSelectedItem,
|
||||
'opacity-100': nodes.folders.length !== 0,
|
||||
}"
|
||||
class="vue-feather mr-2 opacity-0 transition-all duration-300"
|
||||
size="17"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<!--Item icon-->
|
||||
<hard-drive-icon v-if="nodes.location === 'files'" size="17" class="icon vue-feather" :class="{'text-theme dark-text-theme': isSelectedItem}" />
|
||||
<users-icon v-if="nodes.location === 'team-folders'" size="17" class="icon vue-feather" :class="{'text-theme dark-text-theme': isSelectedItem}" />
|
||||
<user-plus-icon v-if="nodes.location === 'shared-with-me'" size="17" class="icon vue-feather" :class="{'text-theme dark-text-theme': isSelectedItem}" />
|
||||
<folder-icon v-if="! nodes.location" size="17" class="icon vue-feather" :class="{'text-theme dark-text-theme': isSelectedItem}" />
|
||||
<!--Item icon-->
|
||||
<hard-drive-icon v-if="nodes.location === 'files'" size="17" class="icon vue-feather" :class="{ 'text-theme dark-text-theme': isSelectedItem }" />
|
||||
<users-icon v-if="nodes.location === 'team-folders'" size="17" class="icon vue-feather" :class="{ 'text-theme dark-text-theme': isSelectedItem }" />
|
||||
<user-plus-icon v-if="nodes.location === 'shared-with-me'" size="17" class="icon vue-feather" :class="{ 'text-theme dark-text-theme': isSelectedItem }" />
|
||||
<folder-icon v-if="!nodes.location" size="17" class="icon vue-feather" :class="{ 'text-theme dark-text-theme': isSelectedItem }" />
|
||||
|
||||
<!--Item label-->
|
||||
<b
|
||||
@click="getFolder"
|
||||
class="text-sm font-bold whitespace-nowrap overflow-x-hidden text-ellipsis inline-block ml-3 transition-all duration-150"
|
||||
:class="{'text-theme': isSelectedItem}"
|
||||
>
|
||||
{{ nodes.name }}
|
||||
</b>
|
||||
<!--Item label-->
|
||||
<b
|
||||
@click="getFolder"
|
||||
class="ml-3 inline-block overflow-x-hidden text-ellipsis whitespace-nowrap text-sm font-bold transition-all duration-150"
|
||||
:class="{ 'text-theme': isSelectedItem }"
|
||||
>
|
||||
{{ nodes.name }}
|
||||
</b>
|
||||
</div>
|
||||
|
||||
<!--Children-->
|
||||
<tree-node
|
||||
:disabled-by-id="disabledById"
|
||||
:depth="depth + 1"
|
||||
v-if="isVisible"
|
||||
:nodes="item"
|
||||
v-for="item in nodes.folders"
|
||||
:key="item.id"
|
||||
/>
|
||||
<!--Children-->
|
||||
<tree-node :disabled-by-id="disabledById" :depth="depth + 1" v-if="isVisible" :nodes="item" v-for="item in nodes.folders" :key="item.id" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
//import TreeMenu from './TreeMenu'
|
||||
import {FolderIcon, ChevronRightIcon, HardDriveIcon, UsersIcon, UserPlusIcon} from 'vue-feather-icons'
|
||||
import {events} from '../../bus'
|
||||
import {mapGetters} from 'vuex'
|
||||
//import TreeMenu from './TreeMenu'
|
||||
import { FolderIcon, ChevronRightIcon, HardDriveIcon, UsersIcon, UserPlusIcon } from 'vue-feather-icons'
|
||||
import { events } from '../../bus'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'TreeMenu',
|
||||
props: [
|
||||
'disabledById',
|
||||
'nodes',
|
||||
'depth',
|
||||
],
|
||||
components: {
|
||||
ChevronRightIcon,
|
||||
HardDriveIcon,
|
||||
UserPlusIcon,
|
||||
FolderIcon,
|
||||
UsersIcon,
|
||||
'tree-node': () => import('./TreeMenuNavigator'),
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'clipboard'
|
||||
]),
|
||||
indent() {
|
||||
return {paddingLeft: this.depth * 20 + 'px'}
|
||||
},
|
||||
disableId() {
|
||||
let canBeShow = true
|
||||
export default {
|
||||
name: 'TreeMenu',
|
||||
props: ['disabledById', 'nodes', 'depth'],
|
||||
components: {
|
||||
ChevronRightIcon,
|
||||
HardDriveIcon,
|
||||
UserPlusIcon,
|
||||
FolderIcon,
|
||||
UsersIcon,
|
||||
'tree-node': () => import('./TreeMenuNavigator'),
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['clipboard']),
|
||||
indent() {
|
||||
return { paddingLeft: this.depth * 20 + 'px' }
|
||||
},
|
||||
disableId() {
|
||||
let canBeShow = true
|
||||
|
||||
if (this.clipboard.includes(this.disabledById)) {
|
||||
this.clipboard.map(item => {
|
||||
if (item.data.id === this.nodes.id) {
|
||||
canBeShow = false
|
||||
}
|
||||
})
|
||||
}
|
||||
return canBeShow
|
||||
},
|
||||
isRootDepth() {
|
||||
return this.depth === 1
|
||||
},
|
||||
isSelectedItem() {
|
||||
return this.isSelected && this.nodes.isMovable || this.isSelected && !this.isRootDepth
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isVisible: false,
|
||||
isSelected: false,
|
||||
isInactive: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getFolder() {
|
||||
if ((this.isRootDepth && this.nodes.isMovable) || !this.isRootDepth) {
|
||||
events.$emit('show-folder-item', this.nodes)
|
||||
events.$emit('pick-folder', this.nodes)
|
||||
}
|
||||
},
|
||||
showTree() {
|
||||
this.isVisible = !this.isVisible
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.clipboard.includes(this.disabledById)) {
|
||||
this.clipboard.map((item) => {
|
||||
if (item.data.id === this.nodes.id) {
|
||||
canBeShow = false
|
||||
}
|
||||
})
|
||||
}
|
||||
return canBeShow
|
||||
},
|
||||
isRootDepth() {
|
||||
return this.depth === 1
|
||||
},
|
||||
isSelectedItem() {
|
||||
return (this.isSelected && this.nodes.isMovable) || (this.isSelected && !this.isRootDepth)
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isVisible: false,
|
||||
isSelected: false,
|
||||
isInactive: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getFolder() {
|
||||
if ((this.isRootDepth && this.nodes.isMovable) || !this.isRootDepth) {
|
||||
events.$emit('show-folder-item', this.nodes)
|
||||
events.$emit('pick-folder', this.nodes)
|
||||
}
|
||||
},
|
||||
showTree() {
|
||||
this.isVisible = !this.isVisible
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// Show first location
|
||||
if (this.depth === 1 && this.nodes.isOpen) this.isVisible = true
|
||||
|
||||
// Show first location
|
||||
if (this.depth === 1 && this.nodes.isOpen)
|
||||
this.isVisible = true
|
||||
// Select clicked folder
|
||||
events.$on('pick-folder', (node) => {
|
||||
this.isSelected = false
|
||||
|
||||
// Select clicked folder
|
||||
events.$on('pick-folder', node => {
|
||||
this.isSelected = false
|
||||
if (this.nodes.id === node.id) this.isSelected = true
|
||||
})
|
||||
|
||||
if (this.nodes.id === node.id)
|
||||
this.isSelected = true
|
||||
})
|
||||
// Select clicked folder
|
||||
events.$on('show-folder-item', (node) => {
|
||||
this.isSelected = false
|
||||
|
||||
// Select clicked folder
|
||||
events.$on('show-folder-item', node => {
|
||||
this.isSelected = false
|
||||
|
||||
if (this.nodes.id === node.id)
|
||||
this.isSelected = true
|
||||
})
|
||||
}
|
||||
}
|
||||
if (this.nodes.id === node.id) this.isSelected = true
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,144 +1,146 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
@click="goToFolder"
|
||||
class="flex items-center py-2.5 rounded-lg border-2 border-transparent border-dashed cursor-pointer"
|
||||
:class="{'border-theme': area, 'pointer-events-none opacity-50': disabledFolder || disabled && draggedItem.length > 0 }"
|
||||
:style="indent"
|
||||
@dragover.prevent="dragEnter"
|
||||
@dragleave="dragLeave"
|
||||
@drop="dragFinish()"
|
||||
>
|
||||
<div @click.stop.prevent="showTree" class="p-2 -ml-2 -my-2 cursor-pointer">
|
||||
<chevron-right-icon
|
||||
size="17"
|
||||
class="vue-feather"
|
||||
:class="{'transform rotate-90': isVisible, 'opacity-0': nodes.folders.length === 0}"
|
||||
/>
|
||||
</div>
|
||||
<folder-icon size="17" class="mr-2.5 vue-feather" :class="{'text-theme': isSelected}" />
|
||||
<b
|
||||
class="font-bold text-xs max-w-1 overflow-hidden text-ellipsis whitespace-nowrap"
|
||||
:class="{'text-theme': isSelected}"
|
||||
>
|
||||
{{ nodes.name }}
|
||||
</b>
|
||||
</div>
|
||||
<tree-node :disabled="disableChildren" :depth="depth + 1" v-if="isVisible" :nodes="item" v-for="item in nodes.folders" :key="item.id" />
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
@click="goToFolder"
|
||||
class="flex cursor-pointer items-center rounded-lg border-2 border-dashed border-transparent py-2.5"
|
||||
:class="{
|
||||
'border-theme': area,
|
||||
'pointer-events-none opacity-50': disabledFolder || (disabled && draggedItem.length > 0),
|
||||
}"
|
||||
:style="indent"
|
||||
@dragover.prevent="dragEnter"
|
||||
@dragleave="dragLeave"
|
||||
@drop="dragFinish()"
|
||||
>
|
||||
<div @click.stop.prevent="showTree" class="-my-2 -ml-2 cursor-pointer p-2">
|
||||
<chevron-right-icon
|
||||
size="17"
|
||||
class="vue-feather"
|
||||
:class="{
|
||||
'rotate-90 transform': isVisible,
|
||||
'opacity-0': nodes.folders.length === 0,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
<folder-icon size="17" class="vue-feather mr-2.5" :class="{ 'text-theme': isSelected }" />
|
||||
<b class="max-w-1 overflow-hidden text-ellipsis whitespace-nowrap text-xs font-bold" :class="{ 'text-theme': isSelected }">
|
||||
{{ nodes.name }}
|
||||
</b>
|
||||
</div>
|
||||
<tree-node :disabled="disableChildren" :depth="depth + 1" v-if="isVisible" :nodes="item" v-for="item in nodes.folders" :key="item.id" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {FolderIcon, ChevronRightIcon} from 'vue-feather-icons'
|
||||
import {mapGetters} from 'vuex'
|
||||
import {events} from '../../bus'
|
||||
import { FolderIcon, ChevronRightIcon } from 'vue-feather-icons'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { events } from '../../bus'
|
||||
|
||||
export default {
|
||||
name: 'TreeMenuNavigator',
|
||||
props: [
|
||||
'disabled',
|
||||
'nodes',
|
||||
'depth',
|
||||
],
|
||||
components: {
|
||||
'tree-node': () => import('./TreeMenuNavigator'),
|
||||
ChevronRightIcon,
|
||||
FolderIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'clipboard',
|
||||
]),
|
||||
isSelected() {
|
||||
return this.$route.params.id === this.nodes.id
|
||||
},
|
||||
disabledFolder() {
|
||||
let disableFolder = false
|
||||
export default {
|
||||
name: 'TreeMenuNavigator',
|
||||
props: ['disabled', 'nodes', 'depth'],
|
||||
components: {
|
||||
'tree-node': () => import('./TreeMenuNavigator'),
|
||||
ChevronRightIcon,
|
||||
FolderIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['clipboard']),
|
||||
isSelected() {
|
||||
return this.$route.params.id === this.nodes.id
|
||||
},
|
||||
disabledFolder() {
|
||||
let disableFolder = false
|
||||
|
||||
if (this.draggedItem.length > 0) {
|
||||
if (this.draggedItem.length > 0) {
|
||||
this.draggedItem.forEach((item) => {
|
||||
//Disable the parent of the folder
|
||||
if (item.type === 'folder' && this.nodes.id === item.parent_id) {
|
||||
disableFolder = true
|
||||
}
|
||||
//Disable the self folder with all children
|
||||
if (this.nodes.id === item.id && item.type === 'folder') {
|
||||
disableFolder = true
|
||||
this.disableChildren = true
|
||||
}
|
||||
if (this.disabled) {
|
||||
this.disableChildren = true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
disableFolder = false
|
||||
this.disableChildren = false
|
||||
}
|
||||
return disableFolder
|
||||
},
|
||||
indent() {
|
||||
let offset = window.innerWidth <= 1024 ? 14 : 18
|
||||
|
||||
this.draggedItem.forEach(item => {
|
||||
//Disable the parent of the folder
|
||||
if (item.type === "folder" && this.nodes.id === item.parent_id) {
|
||||
disableFolder = true
|
||||
}
|
||||
//Disable the self folder with all children
|
||||
if (this.nodes.id === item.id && item.type === 'folder') {
|
||||
disableFolder = true
|
||||
this.disableChildren = true
|
||||
}
|
||||
if (this.disabled) {
|
||||
this.disableChildren = true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
disableFolder = false
|
||||
this.disableChildren = false
|
||||
}
|
||||
return disableFolder
|
||||
},
|
||||
indent() {
|
||||
return {
|
||||
paddingLeft: this.depth === 0 ? 0 : offset * this.depth + 'px',
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
disableChildren: false,
|
||||
isVisible: false,
|
||||
draggedItem: [],
|
||||
area: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goToFolder() {
|
||||
this.$goToFileView(this.nodes.id)
|
||||
},
|
||||
dragFinish() {
|
||||
// Move no selected item
|
||||
if (!this.clipboard.includes(this.draggedItem[0])) {
|
||||
this.$store.dispatch('moveItem', {
|
||||
to_item: this.nodes,
|
||||
noSelectedItem: this.draggedItem[0],
|
||||
})
|
||||
}
|
||||
|
||||
let offset = window.innerWidth <= 1024 ? 14 : 18;
|
||||
// Move all selected items
|
||||
if (this.clipboard.includes(this.draggedItem[0])) {
|
||||
this.$store.dispatch('moveItem', {
|
||||
to_item: this.nodes,
|
||||
noSelectedItem: null,
|
||||
})
|
||||
}
|
||||
|
||||
return {paddingLeft: this.depth === 0 ? 0 : offset * this.depth + 'px'}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
disableChildren: false,
|
||||
isVisible: false,
|
||||
draggedItem: [],
|
||||
area: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goToFolder() {
|
||||
this.$goToFileView(this.nodes.id)
|
||||
},
|
||||
dragFinish() {
|
||||
// Move no selected item
|
||||
if (!this.clipboard.includes(this.draggedItem[0])) {
|
||||
this.$store.dispatch('moveItem', {to_item: this.nodes, noSelectedItem: this.draggedItem[0]})
|
||||
}
|
||||
this.draggedItem = []
|
||||
this.area = false
|
||||
|
||||
// Move all selected items
|
||||
if (this.clipboard.includes(this.draggedItem[0])) {
|
||||
this.$store.dispatch('moveItem', {to_item: this.nodes, noSelectedItem: null})
|
||||
}
|
||||
events.$emit('drop')
|
||||
},
|
||||
dragEnter() {
|
||||
this.area = true
|
||||
},
|
||||
dragLeave() {
|
||||
this.area = false
|
||||
},
|
||||
showTree() {
|
||||
this.isVisible = !this.isVisible
|
||||
},
|
||||
},
|
||||
created() {
|
||||
events.$on('drop', () => {
|
||||
this.draggedItem = []
|
||||
})
|
||||
|
||||
this.draggedItem = []
|
||||
this.area = false
|
||||
|
||||
events.$emit('drop')
|
||||
},
|
||||
dragEnter() {
|
||||
this.area = true
|
||||
},
|
||||
dragLeave() {
|
||||
this.area = false
|
||||
},
|
||||
showTree() {
|
||||
this.isVisible = !this.isVisible
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
events.$on('drop', () => {
|
||||
this.draggedItem = []
|
||||
})
|
||||
|
||||
//Get dragged item
|
||||
events.$on('dragstart', (data) => {
|
||||
//If is dragged item not selected
|
||||
if (!this.clipboard.includes(data)) {
|
||||
this.draggedItem = [data]
|
||||
}
|
||||
//If are the dragged items selected
|
||||
if (this.clipboard.includes(data)) {
|
||||
this.draggedItem = this.clipboard
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
//Get dragged item
|
||||
events.$on('dragstart', (data) => {
|
||||
//If is dragged item not selected
|
||||
if (!this.clipboard.includes(data)) {
|
||||
this.draggedItem = [data]
|
||||
}
|
||||
//If are the dragged items selected
|
||||
if (this.clipboard.includes(data)) {
|
||||
this.draggedItem = this.clipboard
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
<template>
|
||||
<PopupWrapper name="two-factor-qr-setup">
|
||||
|
||||
<PopupHeader :title="$t('popup_2fa.title')" icon="edit" />
|
||||
|
||||
<PopupContent>
|
||||
<div v-if="qrCode" class="flex justify-center">
|
||||
<div v-html="qrCode" class="my-5"></div>
|
||||
</div>
|
||||
<PopupContent>
|
||||
<div v-if="qrCode" class="flex justify-center">
|
||||
<div v-html="qrCode" class="my-5"></div>
|
||||
</div>
|
||||
|
||||
<InfoBox style="margin-bottom: 0">
|
||||
<p v-html="$t('popup_2fa.help')"></p>
|
||||
</InfoBox>
|
||||
<InfoBox style="margin-bottom: 0">
|
||||
<p v-html="$t('popup_2fa.help')"></p>
|
||||
</InfoBox>
|
||||
</PopupContent>
|
||||
|
||||
<PopupActions>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
@click.native="closeQrCodePopup"
|
||||
:button-style="closeQrButtonStyle"
|
||||
>
|
||||
<ButtonBase class="w-full" @click.native="closeQrCodePopup" :button-style="closeQrButtonStyle">
|
||||
{{ closeQrButtonText }}
|
||||
</ButtonBase>
|
||||
</PopupActions>
|
||||
@@ -26,103 +21,95 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AppInputText from "../Admin/AppInputText";
|
||||
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
|
||||
import PopupWrapper from "./Popup/PopupWrapper";
|
||||
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 ButtonBase from "../FilesView/ButtonBase";
|
||||
import InfoBox from "./Forms/InfoBox";
|
||||
import {required} from 'vee-validate/dist/rules'
|
||||
import {mapGetters} from 'vuex'
|
||||
import {events} from '../../bus'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import InfoBox from './Forms/InfoBox'
|
||||
import { required } from 'vee-validate/dist/rules'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { events } from '../../bus'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: "TwoFactorQrSetupPopup",
|
||||
components: {
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
AppInputText,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
required,
|
||||
InfoBox,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user'
|
||||
]),
|
||||
closeQrButtonText() {
|
||||
return this.isConfirmedClose
|
||||
? this.$t('popup_2fa.disappear_qr')
|
||||
: this.$t('shared_form.button_done')
|
||||
},
|
||||
closeQrButtonStyle() {
|
||||
return this.isConfirmedClose
|
||||
? 'danger'
|
||||
: 'theme'
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
qrCode: '',
|
||||
isConfirmedClose: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
enable() {
|
||||
axios
|
||||
.post('/user/two-factor-authentication')
|
||||
.then(() => {
|
||||
name: 'TwoFactorQrSetupPopup',
|
||||
components: {
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
AppInputText,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
required,
|
||||
InfoBox,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
closeQrButtonText() {
|
||||
return this.isConfirmedClose ? this.$t('popup_2fa.disappear_qr') : this.$t('shared_form.button_done')
|
||||
},
|
||||
closeQrButtonStyle() {
|
||||
return this.isConfirmedClose ? 'danger' : 'theme'
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
qrCode: '',
|
||||
isConfirmedClose: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
enable() {
|
||||
axios
|
||||
.post('/user/two-factor-authentication')
|
||||
.then(() => {
|
||||
this.$store.commit('CHANGE_TWO_FACTOR_AUTHENTICATION_STATE', true)
|
||||
|
||||
this.$store.commit('CHANGE_TWO_FACTOR_AUTHENTICATION_STATE', true)
|
||||
this.getQrCode()
|
||||
})
|
||||
.catch(() => {
|
||||
this.$isSomethingWrong()
|
||||
})
|
||||
},
|
||||
getQrCode() {
|
||||
axios
|
||||
.get('/user/two-factor-qr-code')
|
||||
.then((response) => {
|
||||
this.qrCode = response.data.svg
|
||||
})
|
||||
.catch(() => {
|
||||
this.$isSomethingWrong()
|
||||
})
|
||||
},
|
||||
closeQrCodePopup() {
|
||||
if (!this.isConfirmedClose) {
|
||||
this.isConfirmedClose = true
|
||||
} else {
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('popup_2fa.toaster_enabled'),
|
||||
})
|
||||
|
||||
this.getQrCode()
|
||||
})
|
||||
.catch(() => {
|
||||
this.$isSomethingWrong()
|
||||
})
|
||||
},
|
||||
getQrCode() {
|
||||
axios
|
||||
.get('/user/two-factor-qr-code')
|
||||
.then(response => {
|
||||
this.qrCode = response.data.svg
|
||||
})
|
||||
.catch(() => {
|
||||
this.$isSomethingWrong()
|
||||
})
|
||||
},
|
||||
closeQrCodePopup() {
|
||||
if (!this.isConfirmedClose) {
|
||||
this.isConfirmedClose = true
|
||||
} else {
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('popup_2fa.toaster_enabled'),
|
||||
})
|
||||
this.qrCode = undefined
|
||||
this.isConfirmedClose = false
|
||||
|
||||
this.qrCode = undefined
|
||||
this.isConfirmedClose = false
|
||||
this.$closePopup()
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// Show popup
|
||||
events.$on('popup:open', (args) => {
|
||||
if (args.name !== 'two-factor-qr-setup') return
|
||||
|
||||
this.$closePopup()
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Show popup
|
||||
events.$on('popup:open', args => {
|
||||
|
||||
if (args.name !== 'two-factor-qr-setup') return
|
||||
|
||||
this.enable()
|
||||
})
|
||||
}
|
||||
this.enable()
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,42 +3,37 @@
|
||||
<PopupHeader :title="$t('popup_2fa.popup_codes_title')" icon="key" />
|
||||
|
||||
<PopupContent style="padding: 0 20px">
|
||||
<div class="mobile-actions">
|
||||
<MobileActionButton @click.native="copyCodes" icon="copy">
|
||||
{{ $t('context_menu.copy') }}
|
||||
</MobileActionButton>
|
||||
|
||||
<div class="mobile-actions">
|
||||
<MobileActionButton @click.native="copyCodes" icon="copy">
|
||||
{{ $t('context_menu.copy') }}
|
||||
</MobileActionButton>
|
||||
<MobileActionButton @click.native="downloadCodes" icon="download">
|
||||
{{ $t('context_menu.download') }}
|
||||
</MobileActionButton>
|
||||
|
||||
<MobileActionButton @click.native="downloadCodes" icon="download">
|
||||
{{ $t('context_menu.download') }}
|
||||
</MobileActionButton>
|
||||
<MobileActionButton @click.native="regenerateCodes" icon="refresh">
|
||||
{{ $t('context_menu.codes_regenerate') }}
|
||||
</MobileActionButton>
|
||||
</div>
|
||||
|
||||
<MobileActionButton @click.native="regenerateCodes" icon="refresh">
|
||||
{{ $t('context_menu.codes_regenerate') }}
|
||||
</MobileActionButton>
|
||||
</div>
|
||||
<ul v-if="!isLoading" class="codes-list">
|
||||
<li v-for="(code, i) in codes" :key="i">{{ code }}</li>
|
||||
</ul>
|
||||
|
||||
<ul v-if="! isLoading" class="codes-list">
|
||||
<li v-for="(code, i) in codes" :key="i">{{ code }}</li>
|
||||
</ul>
|
||||
<div v-if="isLoading" class="spinner-wrapper">
|
||||
<Spinner />
|
||||
</div>
|
||||
|
||||
<div v-if="isLoading" class="spinner-wrapper">
|
||||
<Spinner />
|
||||
</div>
|
||||
<textarea v-model="inputCodes" ref="codes" class="codes-output"></textarea>
|
||||
|
||||
<textarea v-model="inputCodes" ref="codes" class="codes-output"></textarea>
|
||||
|
||||
<InfoBox style="margin-bottom: 0">
|
||||
<p v-html="$t('popup_2fa.popup_codes_disclaimer')"></p>
|
||||
</InfoBox>
|
||||
<InfoBox style="margin-bottom: 0">
|
||||
<p v-html="$t('popup_2fa.popup_codes_disclaimer')"></p>
|
||||
</InfoBox>
|
||||
</PopupContent>
|
||||
|
||||
<PopupActions>
|
||||
<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>
|
||||
@@ -46,159 +41,160 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MobileActionButton from "../FilesView/MobileActionButton";
|
||||
import PopupWrapper from "./Popup/PopupWrapper";
|
||||
import MobileActionButton from '../FilesView/MobileActionButton'
|
||||
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 InfoBox from "./Forms/InfoBox";
|
||||
import Spinner from "../FilesView/Spinner";
|
||||
import {mapGetters} from "vuex"
|
||||
import {events} from '../../bus'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import InfoBox from './Forms/InfoBox'
|
||||
import Spinner from '../FilesView/Spinner'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { events } from '../../bus'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: "TwoFactorRecoveryCodesPopup",
|
||||
components: {
|
||||
MobileActionButton,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
Spinner,
|
||||
InfoBox,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
codes: undefined,
|
||||
isLoading: true,
|
||||
inputCodes: undefined,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
},
|
||||
methods: {
|
||||
copyCodes() {
|
||||
let copyText = this.$refs.codes
|
||||
name: 'TwoFactorRecoveryCodesPopup',
|
||||
components: {
|
||||
MobileActionButton,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
Spinner,
|
||||
InfoBox,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
codes: undefined,
|
||||
isLoading: true,
|
||||
inputCodes: undefined,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
},
|
||||
methods: {
|
||||
copyCodes() {
|
||||
let copyText = this.$refs.codes
|
||||
|
||||
copyText.select()
|
||||
copyText.setSelectionRange(0, 99999)
|
||||
copyText.select()
|
||||
copyText.setSelectionRange(0, 99999)
|
||||
|
||||
document.execCommand('copy')
|
||||
document.execCommand('copy')
|
||||
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('popup_2fa.toaster_codes_copied'),
|
||||
})
|
||||
},
|
||||
downloadCodes() {
|
||||
// Create txt content
|
||||
let recoveryCodes = "data:x-application/xml;charset=utf-8," + escape(this.codes.join("\n"));
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('popup_2fa.toaster_codes_copied'),
|
||||
})
|
||||
},
|
||||
downloadCodes() {
|
||||
// Create txt content
|
||||
let recoveryCodes = 'data:x-application/xml;charset=utf-8,' + escape(this.codes.join('\n'))
|
||||
|
||||
// Create download link
|
||||
let downloadLink = document.createElement("a")
|
||||
// Create download link
|
||||
let downloadLink = document.createElement('a')
|
||||
|
||||
downloadLink.href = recoveryCodes
|
||||
downloadLink.download = "recovery-codes.txt"
|
||||
downloadLink.href = recoveryCodes
|
||||
downloadLink.download = 'recovery-codes.txt'
|
||||
|
||||
// Download .txt
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
document.body.removeChild(downloadLink);
|
||||
},
|
||||
regenerateCodes() {
|
||||
this.isLoading = true
|
||||
// Download .txt
|
||||
document.body.appendChild(downloadLink)
|
||||
downloadLink.click()
|
||||
document.body.removeChild(downloadLink)
|
||||
},
|
||||
regenerateCodes() {
|
||||
this.isLoading = true
|
||||
|
||||
axios.post('/user/two-factor-recovery-codes')
|
||||
.then(() => {
|
||||
this.getRecoveryCodes()
|
||||
axios
|
||||
.post('/user/two-factor-recovery-codes')
|
||||
.then(() => {
|
||||
this.getRecoveryCodes()
|
||||
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('popup_2fa.toaster_codes_regenerated'),
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
this.$isSomethingWrong()
|
||||
})
|
||||
.finally(() => this.isLoading = false)
|
||||
},
|
||||
getRecoveryCodes() {
|
||||
axios.get('/user/two-factor-recovery-codes')
|
||||
.then(response => {
|
||||
this.codes = response.data
|
||||
this.inputCodes = response.data.join("\n")
|
||||
})
|
||||
.catch(() => {
|
||||
this.$isSomethingWrong()
|
||||
})
|
||||
.finally(() => this.isLoading = false)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
events.$on('popup:open', ({name}) => {
|
||||
if ('two-factor-recovery-codes' === name)
|
||||
this.getRecoveryCodes()
|
||||
})
|
||||
}
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('popup_2fa.toaster_codes_regenerated'),
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
this.$isSomethingWrong()
|
||||
})
|
||||
.finally(() => (this.isLoading = false))
|
||||
},
|
||||
getRecoveryCodes() {
|
||||
axios
|
||||
.get('/user/two-factor-recovery-codes')
|
||||
.then((response) => {
|
||||
this.codes = response.data
|
||||
this.inputCodes = response.data.join('\n')
|
||||
})
|
||||
.catch(() => {
|
||||
this.$isSomethingWrong()
|
||||
})
|
||||
.finally(() => (this.isLoading = false))
|
||||
},
|
||||
},
|
||||
created() {
|
||||
events.$on('popup:open', ({ name }) => {
|
||||
if ('two-factor-recovery-codes' === name) this.getRecoveryCodes()
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.mobile-actions {
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
margin: 0 -20px;
|
||||
padding: 10px 0 10px 20px;
|
||||
}
|
||||
.mobile-actions {
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
margin: 0 -20px;
|
||||
padding: 10px 0 10px 20px;
|
||||
}
|
||||
|
||||
.codes-list {
|
||||
margin: 5px 0 15px;
|
||||
padding-left: 30px;
|
||||
.codes-list {
|
||||
margin: 5px 0 15px;
|
||||
padding-left: 30px;
|
||||
|
||||
li {
|
||||
@include font-size(14);
|
||||
font-weight: bold;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid $light_mode_border;
|
||||
list-style: circle;
|
||||
li {
|
||||
@include font-size(14);
|
||||
font-weight: bold;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid $light_mode_border;
|
||||
list-style: circle;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.codes-output {
|
||||
position: absolute;
|
||||
right: -9999px;
|
||||
}
|
||||
.codes-output {
|
||||
position: absolute;
|
||||
right: -9999px;
|
||||
}
|
||||
|
||||
.spinner-wrapper {
|
||||
height: 339px;
|
||||
position: relative;
|
||||
.spinner-wrapper {
|
||||
height: 339px;
|
||||
position: relative;
|
||||
|
||||
.spinner {
|
||||
top: 46% !important;
|
||||
}
|
||||
}
|
||||
.spinner {
|
||||
top: 46% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.codes-list {
|
||||
.dark {
|
||||
.codes-list {
|
||||
li {
|
||||
border-color: $dark_mode_border_color;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
border-color: $dark_mode_border_color;
|
||||
}
|
||||
}
|
||||
|
||||
.info-box, .mobile-action-button {
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
}
|
||||
}
|
||||
.info-box,
|
||||
.mobile-action-button {
|
||||
background: lighten($dark_mode_foreground, 3%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,81 +5,78 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {events} from '../../bus'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { events } from '../../bus'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'Vignette',
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'processingPopup'
|
||||
]),
|
||||
isVisible() {
|
||||
return this.processingPopup || this.isVisibleVignette
|
||||
},
|
||||
export default {
|
||||
name: 'Vignette',
|
||||
computed: {
|
||||
...mapGetters(['processingPopup']),
|
||||
isVisible() {
|
||||
return this.processingPopup || this.isVisibleVignette
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isVisibleVignette: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closePopup() {
|
||||
events.$emit('popup:close')
|
||||
events.$emit('spotlight:hide')
|
||||
events.$emit('mobile-menu:hide')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Show vignette
|
||||
events.$on('popup:open', () => this.isVisibleVignette = true)
|
||||
events.$on('spotlight:show', () => this.isVisibleVignette = true)
|
||||
events.$on('mobile-menu:show', () => this.isVisibleVignette = true)
|
||||
events.$on('alert:open', () => this.isVisibleVignette = true)
|
||||
events.$on('success:open', () => this.isVisibleVignette = true)
|
||||
events.$on('confirm:open', () => this.isVisibleVignette = true)
|
||||
|
||||
// Hide vignette
|
||||
events.$on('mobile-menu:hide', () => this.isVisibleVignette = false)
|
||||
events.$on('popup:close', () => this.isVisibleVignette = false)
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isVisibleVignette: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closePopup() {
|
||||
events.$emit('popup:close')
|
||||
events.$emit('spotlight:hide')
|
||||
events.$emit('mobile-menu:hide')
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// Show vignette
|
||||
events.$on('popup:open', () => (this.isVisibleVignette = true))
|
||||
events.$on('spotlight:show', () => (this.isVisibleVignette = true))
|
||||
events.$on('mobile-menu:show', () => (this.isVisibleVignette = true))
|
||||
events.$on('alert:open', () => (this.isVisibleVignette = true))
|
||||
events.$on('success:open', () => (this.isVisibleVignette = true))
|
||||
events.$on('confirm:open', () => (this.isVisibleVignette = true))
|
||||
|
||||
// Hide vignette
|
||||
events.$on('mobile-menu:hide', () => (this.isVisibleVignette = false))
|
||||
events.$on('popup:close', () => (this.isVisibleVignette = false))
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.vignette {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 40;
|
||||
background: $light_mode_vignette;
|
||||
.vignette {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 40;
|
||||
background: $light_mode_vignette;
|
||||
}
|
||||
|
||||
// Dark mode
|
||||
.dark .vignette {
|
||||
background: $dark_mode_vignette;
|
||||
}
|
||||
|
||||
.vignette-enter-active {
|
||||
animation: vignette-in 0.35s ease;
|
||||
}
|
||||
|
||||
.vignette-leave-active {
|
||||
animation: vignette-in 0.15s ease reverse;
|
||||
}
|
||||
|
||||
@keyframes vignette-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
// Dark mode
|
||||
.dark .vignette {
|
||||
background: $dark_mode_vignette;
|
||||
}
|
||||
|
||||
|
||||
.vignette-enter-active {
|
||||
animation: vignette-in 0.35s ease;
|
||||
}
|
||||
|
||||
.vignette-leave-active {
|
||||
animation: vignette-in 0.15s ease reverse;
|
||||
}
|
||||
|
||||
@keyframes vignette-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user