Merge branch '547-videos-do-not-display' into 'dev'
Fix gallery view/preview See merge request keanuapp/keanuapp-weblite!251
This commit is contained in:
commit
33d9b3ecd9
8 changed files with 259 additions and 29 deletions
|
|
@ -21,7 +21,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.h-100 {
|
.h-100 {
|
||||||
width: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.white-space-pre {
|
.white-space-pre {
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
|
|
@ -37,4 +37,4 @@
|
||||||
}
|
}
|
||||||
.box-shadow-none {
|
.box-shadow-none {
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@
|
||||||
" v-on:touchend="touchEnd" v-on:touchcancel="touchCancel" v-on:touchmove="touchMove">
|
" v-on:touchend="touchEnd" v-on:touchcancel="touchCancel" v-on:touchmove="touchMove">
|
||||||
<component :is="componentForEvent(event)" :room="room" :originalEvent="event" :nextEvent="filteredEvents[index + 1]"
|
<component :is="componentForEvent(event)" :room="room" :originalEvent="event" :nextEvent="filteredEvents[index + 1]"
|
||||||
:timelineSet="timelineSet" v-on:send-quick-reaction.stop="sendQuickReaction"
|
:timelineSet="timelineSet" v-on:send-quick-reaction.stop="sendQuickReaction"
|
||||||
|
:componentFn="componentForEvent"
|
||||||
v-on:context-menu="showContextMenuForEvent($event)" v-on:own-avatar-clicked="viewProfile"
|
v-on:context-menu="showContextMenuForEvent($event)" v-on:own-avatar-clicked="viewProfile"
|
||||||
v-on:other-avatar-clicked="showAvatarMenuForEvent($event)" v-on:download="download(event)"
|
v-on:other-avatar-clicked="showAvatarMenuForEvent($event)" v-on:download="download(event)"
|
||||||
v-on:poll-closed="pollWasClosed(event)"
|
v-on:poll-closed="pollWasClosed(event)"
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@
|
||||||
<v-card flat>
|
<v-card flat>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
|
|
||||||
<v-container class="mt-0 pa-0 pt-3 action-row-container-no-dividers">
|
<v-container class="mt-0 pa-0 pt-3 pb-3 action-row-container-no-dividers">
|
||||||
<ActionRow v-for="item in menuItems" :key="item.name" :icon="item.icon" :iconSize="16" :text="item.text" @click="$emit('close');item.handler()" />
|
<ActionRow v-for="item in menuItems" :key="item.name" :icon="item.icon" :iconSize="16" :text="item.text" @click="$emit('close');item.handler()" />
|
||||||
|
|
||||||
<v-row class="profile-row clickable" @click="viewProfile" no-gutters align-content="center">
|
<v-row v-if="showProfile" class="profile-row clickable" @click="viewProfile" no-gutters align-content="center">
|
||||||
<v-col cols="auto" class="me-2">
|
<v-col cols="auto" class="me-2">
|
||||||
<v-avatar class="avatar-32" size="32" color="#e0e0e0" @click.stop="viewProfile">
|
<v-avatar class="avatar-32" size="32" color="#e0e0e0" @click.stop="viewProfile">
|
||||||
<img v-if="userAvatar" :src="userAvatar" />
|
<img v-if="userAvatar" :src="userAvatar" />
|
||||||
|
|
@ -40,6 +40,12 @@ export default {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showProfile: {
|
||||||
|
type: Boolean,
|
||||||
|
default: function () {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
menuItems: {
|
menuItems: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: function() {
|
default: function() {
|
||||||
|
|
@ -116,7 +122,7 @@ export default {
|
||||||
|
|
||||||
.profile-row {
|
.profile-row {
|
||||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
padding: 20px 20px !important;
|
padding: 20px 20px 8px 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-row:after {
|
.action-row:after {
|
||||||
|
|
|
||||||
143
src/components/file_mode/GalleryItemsView.vue
Normal file
143
src/components/file_mode/GalleryItemsView.vue
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
<template>
|
||||||
|
<div class="fill-screen file-drop-root">
|
||||||
|
|
||||||
|
<div class="chat-header">
|
||||||
|
<v-container fluid class="d-flex justify-space-between align-center">
|
||||||
|
<v-icon @click.stop="$emit('close')" color="white" class="clickable">arrow_back</v-icon>
|
||||||
|
<div class="room-name no-upper">{{ displayDate }}</div>
|
||||||
|
<v-icon @click.stop="showMoreMenu = true" color="white" class="clickable">more_vert</v-icon>
|
||||||
|
</v-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="file-drop-current-item">
|
||||||
|
<ThumbnailView :item="items[currentItemIndex]" />
|
||||||
|
<div class="download-button clickable" @click.stop="downloadOne">
|
||||||
|
<v-icon color="black">arrow_downward</v-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="file-drop-thumbnail-container">
|
||||||
|
<div :class="{ 'file-drop-thumbnail': true, 'clickable': true, 'current': id == currentItemIndex }"
|
||||||
|
@click="currentItemIndex = id" v-for="(currentImageInput, id) in items" :key="id">
|
||||||
|
<v-img v-if="currentImageInput && currentImageInput.src" :src="currentImageInput.src" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MORE MENU POPUP -->
|
||||||
|
<MoreMenuPopup :show="showMoreMenu" :menuItems="moreMenuItems" :showProfile="false" @close="showMoreMenu = false" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MoreMenuPopup from "../MoreMenuPopup";
|
||||||
|
import messageMixin from "../messages/messageMixin";
|
||||||
|
import util from "../../plugins/utils";
|
||||||
|
import ThumbnailView from './ThumbnailView.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin],
|
||||||
|
components: { MoreMenuPopup, ThumbnailView },
|
||||||
|
props: {
|
||||||
|
items: {
|
||||||
|
type: Array,
|
||||||
|
default: function () {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initialItem: {
|
||||||
|
type: Object,
|
||||||
|
default: function() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentItemIndex: 0,
|
||||||
|
showMoreMenu: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
document.body.classList.add("dark");
|
||||||
|
if (this.initialItem) {
|
||||||
|
this.currentItemIndex = this.items.findIndex((v) => v === this.initialItem);
|
||||||
|
if (this.currentItemIndex < 0) {
|
||||||
|
this.currentItemIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
document.body.classList.remove("dark");
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
displayDate() {
|
||||||
|
return util.formatRecordStartTime(this.originalEvent.getTs())
|
||||||
|
},
|
||||||
|
moreMenuItems() {
|
||||||
|
let items = [];
|
||||||
|
items.push({
|
||||||
|
icon: '$vuetify.icons.ic_download', text: this.$t("message.download_all"), handler: () => {
|
||||||
|
this.downloadAll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return items;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
items(newValue, oldValue) {
|
||||||
|
// Added or removed?
|
||||||
|
if (newValue && oldValue && newValue.length > oldValue.length) {
|
||||||
|
this.currentItemIndex = oldValue.length;
|
||||||
|
} else if (newValue) {
|
||||||
|
this.currentItemIndex = newValue.length - 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
downloadOne() {
|
||||||
|
if (this.currentItemIndex >= 0 && this.currentItemIndex < this.items.length) {
|
||||||
|
util.download(this.$matrix.matrixClient, this.items[this.currentItemIndex].event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
downloadAll() {
|
||||||
|
this.items.forEach(item => util.download(this.$matrix.matrixClient, item.event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
|
||||||
|
.chat-header {
|
||||||
|
position: relative !important;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drop-current-item {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 21px;
|
||||||
|
bottom: 21px;
|
||||||
|
width: 34px;
|
||||||
|
height: 34px;
|
||||||
|
background: rgba(255,255,255,0.8);
|
||||||
|
border-radius: 17px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fill-screen {
|
||||||
|
position: fixed !important;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: black;
|
||||||
|
z-index: 20;
|
||||||
|
justify-content: space-between !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
61
src/components/file_mode/ThumbnailView.vue
Normal file
61
src/components/file_mode/ThumbnailView.vue
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
<template>
|
||||||
|
<v-responsive v-if="item.event.getContent().msgtype == 'm.video'" :class="{'thumbnail-item': true, 'preview': previewOnly}"
|
||||||
|
@click.stop="$emit('itemclick', {item: item})">
|
||||||
|
<video :src="item.src" :controls="!previewOnly" class="w-100 h-100">
|
||||||
|
{{ $t('fallbacks.video_file') }}
|
||||||
|
</video>
|
||||||
|
</v-responsive>
|
||||||
|
<v-img v-else-if="item.event.getContent().msgtype == 'm.image'" :aspect-ratio="previewOnly ? (16 / 9) : undefined" :class="{'thumbnail-item': true, 'preview': previewOnly}" :src="item.src" :contain="!previewOnly" :cover="previewOnly"
|
||||||
|
@click.stop="$emit('itemclick', {item: item})" />
|
||||||
|
<div v-else :class="{'thumbnail-item': true, 'preview': previewOnly, 'file-item': true}" @click.stop="$emit('itemclick', {item: item})">
|
||||||
|
<v-icon>description</v-icon>
|
||||||
|
{{ $sanitize(item.event.getContent().body) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
/**
|
||||||
|
* Item is an object of { event: MXEvent, src: URL }
|
||||||
|
*/
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
default: function () {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
previewOnly: {
|
||||||
|
type: Boolean,
|
||||||
|
default: function() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
|
||||||
|
.thumbnail-item {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 20px;
|
||||||
|
.v-icon {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<message-incoming v-bind="{ ...$props, ...$attrs }" v-on="$listeners">
|
<message-incoming v-bind="{ ...$props, ...$attrs }" v-on="$listeners" v-if="items.length > 1">
|
||||||
<div class="bubble">
|
<div class="bubble">
|
||||||
<div class="original-message" v-if="inReplyToText">
|
<div class="original-message" v-if="inReplyToText">
|
||||||
<div class="original-message-sender">
|
<div class="original-message-sender">
|
||||||
|
|
@ -14,14 +14,10 @@
|
||||||
<v-container fluid class="imageCollection">
|
<v-container fluid class="imageCollection">
|
||||||
<v-row wrap>
|
<v-row wrap>
|
||||||
<v-col v-for="({ size, item }) in layoutedItems()" :key="item.event.getId()" :cols="size">
|
<v-col v-for="({ size, item }) in layoutedItems()" :key="item.event.getId()" :cols="size">
|
||||||
<v-img :aspect-ratio="16 / 9" :src="item.src" cover @click.stop="dialogSrc = item.src; dialog = true" />
|
<ThumbnailView :item="item" :previewOnly="true" v-on:itemclick="onItemClick($event)" />
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
<div style="text-align: end">
|
|
||||||
<v-btn class="download-all-button" @click.stop="downloadAll">{{ $t("message.download_all") }} <v-icon
|
|
||||||
color="white">arrow_downward</v-icon></v-btn>
|
|
||||||
</div>
|
|
||||||
<i v-if="event.isRedacted()" class="deleted-text">
|
<i v-if="event.isRedacted()" class="deleted-text">
|
||||||
<v-icon :color="this.senderIsAdminOrModerator(this.event) ? 'white' : ''" size="small">block</v-icon>
|
<v-icon :color="this.senderIsAdminOrModerator(this.event) ? 'white' : ''" size="small">block</v-icon>
|
||||||
{{ $t('message.incoming_message_deleted_text') }}
|
{{ $t('message.incoming_message_deleted_text') }}
|
||||||
|
|
@ -32,26 +28,29 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<v-dialog v-model="dialog" :width="$vuetify.breakpoint.smAndUp ? '940px' : '90%'">
|
<GalleryItemsView :originalEvent="originalEvent" :items="items" :initialItem="showItem" v-if="!!showItem" v-on:close="showItem = null" />
|
||||||
<v-img style="background:white" :src="dialogSrc" />
|
|
||||||
</v-dialog>
|
|
||||||
</message-incoming>
|
</message-incoming>
|
||||||
|
<component v-else-if="items.length == 1" :is="componentFn(items[0].event)"
|
||||||
|
:originalEvent="items[0].event"
|
||||||
|
v-bind="{...$props, ...$attrs}" v-on="$listeners"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MessageIncoming from "./MessageIncoming.vue";
|
import MessageIncoming from "./MessageIncoming.vue";
|
||||||
import messageMixin from "./messageMixin";
|
import messageMixin from "./messageMixin";
|
||||||
import util from "../../plugins/utils";
|
import util from "../../plugins/utils";
|
||||||
|
import GalleryItemsView from '../file_mode/GalleryItemsView.vue';
|
||||||
|
import ThumbnailView from '../file_mode/ThumbnailView.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
extends: MessageIncoming,
|
extends: MessageIncoming,
|
||||||
components: { MessageIncoming },
|
components: { MessageIncoming, GalleryItemsView, ThumbnailView },
|
||||||
mixins: [messageMixin],
|
mixins: [messageMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
items: [],
|
items: [],
|
||||||
dialog: false,
|
showItem: null,
|
||||||
dialogSrc: null,
|
|
||||||
thread: null,
|
thread: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -80,6 +79,9 @@ export default {
|
||||||
onAddRelation() {
|
onAddRelation() {
|
||||||
this.processThread();
|
this.processThread();
|
||||||
},
|
},
|
||||||
|
onItemClick(event) {
|
||||||
|
this.showItem = event.item;
|
||||||
|
},
|
||||||
processThread() {
|
processThread() {
|
||||||
this.items = this.timelineSet.relations.getAllChildEventsForEvent(this.event.getId()).map(e => {
|
this.items = this.timelineSet.relations.getAllChildEventsForEvent(this.event.getId()).map(e => {
|
||||||
let ret = {
|
let ret = {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<message-outgoing v-bind="{ ...$props, ...$attrs }" v-on="$listeners">
|
<message-outgoing v-bind="{ ...$props, ...$attrs }" v-on="$listeners" v-if="items.length > 1">
|
||||||
<div class="bubble">
|
<div class="bubble">
|
||||||
<div class="original-message" v-if="inReplyToText">
|
<div class="original-message" v-if="inReplyToText">
|
||||||
<div class="original-message-sender">
|
<div class="original-message-sender">
|
||||||
|
|
@ -12,14 +12,10 @@
|
||||||
<v-container fluid class="imageCollection">
|
<v-container fluid class="imageCollection">
|
||||||
<v-row wrap>
|
<v-row wrap>
|
||||||
<v-col v-for="({ size, item }) in layoutedItems()" :key="item.event.getId()" :cols="size">
|
<v-col v-for="({ size, item }) in layoutedItems()" :key="item.event.getId()" :cols="size">
|
||||||
<v-img :aspect-ratio="16 / 9" :src="item.src" cover @click.stop="dialogSrc = item.src; dialog = true" />
|
<ThumbnailView :item="item" :previewOnly="true" v-on:itemclick="onItemClick($event)" />
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
<div style="text-align: end">
|
|
||||||
<v-btn class="download-all-button" @click.stop="downloadAll">{{ $t("message.download_all") }} <v-icon
|
|
||||||
color="white">arrow_downward</v-icon></v-btn>
|
|
||||||
</div>
|
|
||||||
<i v-if="event.isRedacted()" class="deleted-text">
|
<i v-if="event.isRedacted()" class="deleted-text">
|
||||||
<v-icon size="small">block</v-icon>
|
<v-icon size="small">block</v-icon>
|
||||||
{{ $t('message.outgoing_message_deleted_text') }}
|
{{ $t('message.outgoing_message_deleted_text') }}
|
||||||
|
|
@ -30,23 +26,29 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<GalleryItemsView :originalEvent="originalEvent" :items="items" :initialItem="showItem" v-if="!!showItem" v-on:close="showItem = null" />
|
||||||
</message-outgoing>
|
</message-outgoing>
|
||||||
|
<component v-else-if="items.length == 1" :is="componentFn(items[0].event)"
|
||||||
|
:originalEvent="items[0].event"
|
||||||
|
v-bind="{...$props, ...$attrs}" v-on="$listeners"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MessageOutgoing from "./MessageOutgoing.vue";
|
import MessageOutgoing from "./MessageOutgoing.vue";
|
||||||
import messageMixin from "./messageMixin";
|
import messageMixin from "./messageMixin";
|
||||||
import util from "../../plugins/utils";
|
import util from "../../plugins/utils";
|
||||||
|
import GalleryItemsView from '../file_mode/GalleryItemsView.vue';
|
||||||
|
import ThumbnailView from '../file_mode/ThumbnailView.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
extends: MessageOutgoing,
|
extends: MessageOutgoing,
|
||||||
components: { MessageOutgoing },
|
components: { MessageOutgoing, GalleryItemsView, ThumbnailView },
|
||||||
mixins: [messageMixin],
|
mixins: [messageMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
items: [],
|
items: [],
|
||||||
dialog: false,
|
showItem: null,
|
||||||
dialogSrc: null,
|
|
||||||
thread: null,
|
thread: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -75,6 +77,9 @@ export default {
|
||||||
onAddRelation() {
|
onAddRelation() {
|
||||||
this.processThread();
|
this.processThread();
|
||||||
},
|
},
|
||||||
|
onItemClick(event) {
|
||||||
|
this.showItem = event.item;
|
||||||
|
},
|
||||||
processThread() {
|
processThread() {
|
||||||
this.items = this.timelineSet.relations.getAllChildEventsForEvent(this.event.getId()).map(e => {
|
this.items = this.timelineSet.relations.getAllChildEventsForEvent(this.event.getId()).map(e => {
|
||||||
let ret = {
|
let ret = {
|
||||||
|
|
@ -123,9 +128,6 @@ export default {
|
||||||
}
|
}
|
||||||
return rows
|
return rows
|
||||||
},
|
},
|
||||||
downloadAll() {
|
|
||||||
this.items.forEach(item => util.download(this.$matrix.matrixClient, item.event));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -151,5 +153,14 @@ export default {
|
||||||
.col {
|
.col {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -36,6 +36,12 @@ export default {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
componentFn: {
|
||||||
|
type: Function,
|
||||||
|
default: function () {
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue