2020-05-14 16:05:33 +02:00
|
|
|
|
<template>
|
2020-06-02 12:08:38 +02:00
|
|
|
|
<div class="ui-mediapicker">
|
|
|
|
|
|
<div v-if="previews.length > 0" class="ui-mediapicker-previews">
|
|
|
|
|
|
<div v-for="item in previews" class="ui-mediapicker-preview">
|
2021-01-09 19:09:31 +01:00
|
|
|
|
<div v-if="!item.error" class="ui-mediapicker-preview-image" :class="{'media-pattern': item.thumbnailSource }">
|
2020-06-02 12:08:38 +02:00
|
|
|
|
<img v-if="item.thumbnailSource" :src="item.thumbnailSource" :alt="item.name" />
|
2021-01-20 15:54:12 +01:00
|
|
|
|
<button v-if="!disabled" type="button" class="ui-mediapicker-preview-image-delete" @click="remove(item)" v-localize:title="'@ui.remove'"><ui-icon symbol="fth-x" :size="12" /></button>
|
|
|
|
|
|
<button v-if="!disabled" type="button" class="ui-mediapicker-preview-image-edit" @click="edit(item)" v-localize:title="'@ui.edit'"><ui-icon symbol="fth-edit-2" :size="12" /></button>
|
2020-06-02 12:08:38 +02:00
|
|
|
|
</div>
|
2020-08-31 10:49:35 +02:00
|
|
|
|
<div v-if="!item.error" class="ui-mediapicker-preview-text">
|
2020-06-02 12:08:38 +02:00
|
|
|
|
<b :title="item.name">{{getFilename(item.name)}}</b>
|
|
|
|
|
|
<span class="is-filesize">{{getFileinfo(item)}}</span>
|
|
|
|
|
|
</div>
|
2020-08-31 10:49:35 +02:00
|
|
|
|
<div v-if="item.error">
|
2020-09-06 13:30:39 +02:00
|
|
|
|
<ui-select-button icon="fth-alert-circle color-red" label="@errors.preview.notfound" description="@errors.preview.notfound_text" @click="remove({ id: item.id })" :tokens="{ id: item.id }" />
|
2020-08-31 10:49:35 +02:00
|
|
|
|
</div>
|
2020-06-02 12:08:38 +02:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div v-if="canAdd" class="ui-mediapicker-select" :class="{'is-disabled': disabled }">
|
|
|
|
|
|
<input ref="input" type="hidden" :value="value" />
|
|
|
|
|
|
<ui-select-button icon="fth-plus" label="@mediapicker.select_text" description="@mediapicker.select_description" @click="pick" :disabled="disabled" />
|
|
|
|
|
|
</div>
|
2020-05-14 16:05:33 +02:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
2020-11-20 15:17:41 +01:00
|
|
|
|
import MediaApi from 'zero/api/media.js'
|
2020-11-01 22:20:29 +01:00
|
|
|
|
import PickMediaOverlay from './overlay.vue';
|
2020-11-20 10:32:17 +01:00
|
|
|
|
import Overlay from 'zero/helpers/overlay.js';
|
2020-05-14 16:05:33 +02:00
|
|
|
|
import { each as _each, extend as _extend, debounce as _debounce, isArray as _isArray } from 'underscore';
|
2020-11-20 10:32:17 +01:00
|
|
|
|
import Strings from 'zero/helpers/strings.js';
|
2020-05-14 16:05:33 +02:00
|
|
|
|
|
|
|
|
|
|
const TYPES = {
|
|
|
|
|
|
ALL: 'all',
|
|
|
|
|
|
IMAGE: 'image',
|
|
|
|
|
|
VIDEO: 'video',
|
|
|
|
|
|
DOCUMENT: 'document',
|
|
|
|
|
|
OTHER: 'other'
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const DISPLAY = {
|
|
|
|
|
|
DEFAULT: 'default',
|
|
|
|
|
|
BIG: 'big',
|
|
|
|
|
|
GRID: 'grid'
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const MAX_FILENAME_LENGTH = 32;
|
|
|
|
|
|
|
|
|
|
|
|
const defaultConfig = {
|
|
|
|
|
|
// maximum media items
|
|
|
|
|
|
limit: 1,
|
|
|
|
|
|
// media type
|
|
|
|
|
|
type: TYPES.ALL,
|
|
|
|
|
|
// how the previews are displayed
|
|
|
|
|
|
display: DISPLAY.DEFAULT,
|
|
|
|
|
|
// whether the select button is disallowed
|
|
|
|
|
|
disallowSelect: false,
|
|
|
|
|
|
// whether file upload is disallowed
|
|
|
|
|
|
disallowUpload: false,
|
|
|
|
|
|
// allowed file extensions
|
|
|
|
|
|
fileExtensions: [],
|
|
|
|
|
|
// maximum file size in MiB
|
2021-09-23 11:29:10 +02:00
|
|
|
|
maxFileSize: 10,
|
|
|
|
|
|
// whether to operate on the core database
|
|
|
|
|
|
shared: false
|
2020-05-14 16:05:33 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: 'uiMediapicker',
|
|
|
|
|
|
|
|
|
|
|
|
props: {
|
|
|
|
|
|
value: {
|
2020-11-24 12:02:03 +01:00
|
|
|
|
type: [Array, String],
|
2020-05-14 16:05:33 +02:00
|
|
|
|
default: null
|
|
|
|
|
|
},
|
|
|
|
|
|
disabled: {
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
default: false
|
|
|
|
|
|
},
|
|
|
|
|
|
config: {
|
|
|
|
|
|
type: Object,
|
|
|
|
|
|
default: () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
return defaultConfig;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2020-06-02 12:08:38 +02:00
|
|
|
|
|
|
|
|
|
|
data: () => ({
|
2021-02-03 16:06:36 +01:00
|
|
|
|
configuration: null,
|
2020-06-02 12:08:38 +02:00
|
|
|
|
previews: []
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
watch: {
|
|
|
|
|
|
value()
|
|
|
|
|
|
{
|
|
|
|
|
|
this.updatePreviews();
|
|
|
|
|
|
}
|
2020-05-14 16:05:33 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
2020-06-02 12:08:38 +02:00
|
|
|
|
|
2020-05-15 14:23:47 +02:00
|
|
|
|
created()
|
|
|
|
|
|
{
|
2021-02-03 16:06:36 +01:00
|
|
|
|
this.configuration = JSON.parse(JSON.stringify(_extend(defaultConfig, this.config)));
|
2020-05-15 14:23:47 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
2020-06-02 12:08:38 +02:00
|
|
|
|
|
|
|
|
|
|
mounted()
|
|
|
|
|
|
{
|
|
|
|
|
|
this.updatePreviews();
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
computed: {
|
2020-06-02 13:36:33 +02:00
|
|
|
|
multiple()
|
|
|
|
|
|
{
|
|
|
|
|
|
return this.configuration.limit > 1;
|
|
|
|
|
|
},
|
2020-06-02 12:08:38 +02:00
|
|
|
|
canAdd()
|
|
|
|
|
|
{
|
|
|
|
|
|
return !this.disabled && this.configuration.limit - this.previews.length > 0;
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-05-14 16:05:33 +02:00
|
|
|
|
methods: {
|
|
|
|
|
|
|
2020-06-02 12:08:38 +02:00
|
|
|
|
updatePreviews()
|
2020-05-14 16:05:33 +02:00
|
|
|
|
{
|
2020-06-02 12:08:38 +02:00
|
|
|
|
//console.info('preview', JSON.parse(JSON.stringify(this.value)));
|
|
|
|
|
|
let ids = [];
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof this.value === 'string')
|
|
|
|
|
|
{
|
|
|
|
|
|
ids = [this.value];
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (_isArray(this.value))
|
|
|
|
|
|
{
|
|
|
|
|
|
ids = this.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.previews = [];
|
|
|
|
|
|
|
|
|
|
|
|
if (!ids || ids.length < 1)
|
|
|
|
|
|
{
|
2021-02-03 16:06:36 +01:00
|
|
|
|
this.$emit('previews', this.multiple ? [] : null);
|
2020-06-02 12:08:38 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MediaApi.getByIds(ids).then(res =>
|
|
|
|
|
|
{
|
2020-08-31 10:49:35 +02:00
|
|
|
|
_each(ids, id =>
|
2020-06-02 12:08:38 +02:00
|
|
|
|
{
|
2020-08-31 10:49:35 +02:00
|
|
|
|
let value = res[id];
|
|
|
|
|
|
|
2020-06-02 12:08:38 +02:00
|
|
|
|
if (!value)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.previews.push({
|
2020-09-06 13:30:39 +02:00
|
|
|
|
id: id,
|
|
|
|
|
|
error: true
|
2020-06-02 12:08:38 +02:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
this.previews.push(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2021-02-03 16:06:36 +01:00
|
|
|
|
|
|
|
|
|
|
this.$emit('previews', this.multiple ? this.previews : this.previews[0]);
|
2020-06-02 12:08:38 +02:00
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
remove(item)
|
|
|
|
|
|
{
|
|
|
|
|
|
let newValue = this.value;
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof this.value === 'string')
|
|
|
|
|
|
{
|
|
|
|
|
|
newValue = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (_isArray(this.value))
|
|
|
|
|
|
{
|
|
|
|
|
|
const index = this.value.indexOf(item.id);
|
|
|
|
|
|
newValue.splice(index, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.onChange(newValue);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getFilename(name)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (name.length < MAX_FILENAME_LENGTH)
|
|
|
|
|
|
{
|
|
|
|
|
|
return name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const parts = name.split('.');
|
|
|
|
|
|
const extension = parts.pop();
|
|
|
|
|
|
|
|
|
|
|
|
return parts.join('.').substring(0, MAX_FILENAME_LENGTH - 6) + '...' + extension;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getFileinfo(item)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (item.dimension)
|
|
|
|
|
|
{
|
|
|
|
|
|
return `${Strings.filesize(item.size)} – ${item.dimension.width} × ${item.dimension.height}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Strings.filesize(item.size);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onChange(value)
|
|
|
|
|
|
{
|
2020-05-14 16:05:33 +02:00
|
|
|
|
this.$emit('change', value);
|
|
|
|
|
|
this.$emit('input', value);
|
2020-06-02 12:08:38 +02:00
|
|
|
|
//this.updatePreviews();
|
2020-05-14 16:05:33 +02:00
|
|
|
|
// TODO this does not trigger the forms dirty flag
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2020-06-02 12:08:38 +02:00
|
|
|
|
|
2020-05-14 16:05:33 +02:00
|
|
|
|
pick()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (this.disabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let options = _extend({
|
|
|
|
|
|
title: '@iconpicker.title',
|
|
|
|
|
|
closeLabel: '@ui.close',
|
|
|
|
|
|
component: PickMediaOverlay,
|
2020-06-22 15:42:50 +02:00
|
|
|
|
model: this.configuration.limit > 1 ? this.value[0] : this.value,
|
2020-06-03 15:16:43 +02:00
|
|
|
|
folderId: null, //'mediaFolders.97-A',
|
2020-05-15 14:23:47 +02:00
|
|
|
|
width: 520
|
2020-05-14 16:05:33 +02:00
|
|
|
|
}, typeof this.config === 'object' ? this.config : {});
|
|
|
|
|
|
|
2020-09-06 15:47:14 +02:00
|
|
|
|
options.display = 'dialog';
|
|
|
|
|
|
|
2020-05-14 16:05:33 +02:00
|
|
|
|
return Overlay.open(options).then(value =>
|
|
|
|
|
|
{
|
2020-06-02 13:36:33 +02:00
|
|
|
|
let newValue = this.value;
|
|
|
|
|
|
|
|
|
|
|
|
if (this.multiple)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!newValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
newValue = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
newValue.push(value.id);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
newValue = value.id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.onChange(newValue);
|
2021-02-04 14:00:09 +01:00
|
|
|
|
|
|
|
|
|
|
return new Promise(resolve => resolve(newValue));
|
2020-05-14 16:05:33 +02:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-06-02 12:08:38 +02:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss">
|
|
|
|
|
|
.ui-mediapicker-previews
|
|
|
|
|
|
{
|
|
|
|
|
|
.display-grid &
|
|
|
|
|
|
{
|
|
|
|
|
|
display: flex;
|
2020-08-31 11:48:05 +02:00
|
|
|
|
gap: 10px;
|
2020-06-02 12:08:38 +02:00
|
|
|
|
|
|
|
|
|
|
.ui-mediapicker-preview + .ui-mediapicker-preview
|
|
|
|
|
|
{
|
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ui-mediapicker-previews + .ui-mediapicker-select,
|
|
|
|
|
|
.ui-mediapicker-preview + .ui-mediapicker-preview
|
|
|
|
|
|
{
|
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ui-mediapicker:not(.display-grid) .ui-mediapicker-previews + .ui-mediapicker-add
|
|
|
|
|
|
{
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
|
|
|
|
.ui-select-button + .ui-mediapicker-add-upload
|
|
|
|
|
|
{
|
|
|
|
|
|
margin-left: 0;
|
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ui-mediapicker-preview
|
|
|
|
|
|
{
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ui-mediapicker-preview-image
|
|
|
|
|
|
{
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
2021-01-09 19:09:31 +01:00
|
|
|
|
width: 80px;
|
|
|
|
|
|
height: 80px;
|
2020-06-02 12:08:38 +02:00
|
|
|
|
/*background: var(--color-bg);*/
|
2020-09-07 14:55:05 +02:00
|
|
|
|
border: 1px solid var(--color-line);
|
2021-01-20 15:54:12 +01:00
|
|
|
|
padding: 0;
|
2020-06-02 12:08:38 +02:00
|
|
|
|
border-radius: var(--radius);
|
2020-09-07 14:55:05 +02:00
|
|
|
|
color: var(--color-text);
|
2020-06-02 12:08:38 +02:00
|
|
|
|
position: relative;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
|
|
|
|
|
|
img
|
|
|
|
|
|
{
|
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
|
max-height: 100%;
|
|
|
|
|
|
margin: auto;
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
color: transprent;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
font-size: 0;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-06 13:30:39 +02:00
|
|
|
|
&.is-icon
|
|
|
|
|
|
{
|
2021-01-06 12:03:21 +01:00
|
|
|
|
width: 48px;
|
|
|
|
|
|
height: 48px;
|
2020-09-06 13:30:39 +02:00
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
border-radius: var(--radius);
|
2020-09-07 14:55:05 +02:00
|
|
|
|
background: var(--color-box);
|
|
|
|
|
|
color: var(--color-text);
|
2020-09-06 13:30:39 +02:00
|
|
|
|
text-align: center;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-02 12:08:38 +02:00
|
|
|
|
&:hover .ui-mediapicker-preview-image-delete,
|
|
|
|
|
|
&:hover .ui-mediapicker-preview-image-edit
|
|
|
|
|
|
{
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transition-delay: 0.1s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.display-big &, .display-grid &
|
|
|
|
|
|
{
|
|
|
|
|
|
width: 100px;
|
|
|
|
|
|
height: 100px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ui-mediapicker-preview-text
|
|
|
|
|
|
{
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
margin-left: 16px;
|
|
|
|
|
|
font-size: var(--font-size);
|
|
|
|
|
|
|
|
|
|
|
|
.is-filesize
|
|
|
|
|
|
{
|
2020-09-07 14:55:05 +02:00
|
|
|
|
color: var(--color-text-dim);
|
2020-06-02 12:08:38 +02:00
|
|
|
|
margin-top: 3px;
|
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.display-grid &
|
|
|
|
|
|
{
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ui-mediapicker-preview-image-delete,
|
|
|
|
|
|
.ui-mediapicker-preview-image-edit
|
|
|
|
|
|
{
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transition: opacity 0.15s ease;
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
right: 3px;
|
|
|
|
|
|
bottom: 3px;
|
|
|
|
|
|
width: 24px;
|
|
|
|
|
|
height: 24px;
|
|
|
|
|
|
line-height: 26px;
|
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
|
background: var(--color-negative);
|
2020-09-07 14:55:05 +02:00
|
|
|
|
color: var(--color-primary-text);
|
2020-06-02 12:08:38 +02:00
|
|
|
|
z-index: 2;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
|
|
|
|
|
|
.ui-media.display-default &
|
|
|
|
|
|
{
|
|
|
|
|
|
width: 20px;
|
|
|
|
|
|
height: 20px;
|
|
|
|
|
|
line-height: 22px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ui-mediapicker-preview-image-edit
|
|
|
|
|
|
{
|
|
|
|
|
|
right: 30px;
|
2020-09-07 14:55:05 +02:00
|
|
|
|
background: var(--color-box);
|
|
|
|
|
|
color: var(--color-text);
|
2020-06-02 12:08:38 +02:00
|
|
|
|
|
|
|
|
|
|
.ui-media.display-default &
|
|
|
|
|
|
{
|
|
|
|
|
|
right: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|