Replace flyouts menu in protocol in results [SCI-9265]

This commit is contained in:
Anton 2023-09-15 12:39:56 +02:00
parent 1cb4f71db8
commit b0dc63ebb5
11 changed files with 456 additions and 363 deletions

View file

@ -140,6 +140,7 @@
.repositories-dropdown-menu { .repositories-dropdown-menu {
max-height: 250px; max-height: 250px;
overflow: auto; overflow: auto;
z-index: 251;
.repository { .repository {
@include font-button; @include font-button;

View file

@ -213,7 +213,7 @@
required: true required: true
} }
}, },
components: { Step, InlineEdit, ProtocolOptions, Tinymce, ReorderableItemsModal, ProtocolMetadata, PublishProtocol }, components: { Step, InlineEdit, ProtocolOptions, Tinymce, ReorderableItemsModal, ProtocolMetadata, PublishProtocol},
mixins: [UtilsMixin], mixins: [UtilsMixin],
computed: { computed: {
inRepository() { inRepository() {

View file

@ -14,86 +14,79 @@
<span class="sn-icon sn-icon-down"></span> <span class="sn-icon sn-icon-down"></span>
</button> </button>
<ul <ul
class="dropdown-menu dropdown-menu-right" class="dropdown-menu dropdown-menu-right rounded !p-2.5 sn-shadow-menu-sm"
aria-labelledby="dropdownProtocolOptions" aria-labelledby="dropdownProtocolOptions"
> >
<li v-if="protocol.attributes.urls.load_from_repo_url"> <li v-if="protocol.attributes.urls.load_from_repo_url">
<a <a class="!px-3 !py-2.5 hover:!bg-sn-super-light-blue !text-sn-blue"
ref="loadProtocol" ref="loadProtocol"
data-action="load-from-repository" data-action="load-from-repository"
@click="loadProtocol" @click="loadProtocol"
> >
<span class="sn-icon sn-icon-protocols-templates"></span>
<span>{{ i18n.t("my_modules.protocol.options_dropdown.load_from_repo") }}</span> <span>{{ i18n.t("my_modules.protocol.options_dropdown.load_from_repo") }}</span>
</a> </a>
</li> </li>
<li> <li>
<a <a class="!px-3 !py-2.5 hover:!bg-sn-super-light-blue !text-sn-blue"
data-toggle="modal" data-toggle="modal"
data-target="#newProtocolModal" data-target="#newProtocolModal"
v-bind:data-protocol-name="protocol.attributes.name" v-bind:data-protocol-name="protocol.attributes.name"
:class="{ disabled: !protocol.attributes.urls.save_to_repo_url }" :class="{ disabled: !protocol.attributes.urls.save_to_repo_url }"
> >
<span class="fas fa-save"></span>
<span>{{ <span>{{
i18n.t("my_modules.protocol.options_dropdown.save_to_repo") i18n.t("my_modules.protocol.options_dropdown.save_to_repo")
}}</span> }}</span>
</a> </a>
</li> </li>
<li> <li>
<a <a class="!px-3 !py-2.5 hover:!bg-sn-super-light-blue !text-sn-blue"
data-turbolinks="false" data-turbolinks="false"
:href="protocol.attributes.urls.export_url" :href="protocol.attributes.urls.export_url"
:class="{ disabled: !protocol.attributes.urls.export_url }" :class="{ disabled: !protocol.attributes.urls.export_url }"
> >
<span class="sn-icon sn-icon-import"></span>
<span>{{ <span>{{
i18n.t("my_modules.protocol.options_dropdown.export") i18n.t("my_modules.protocol.options_dropdown.export")
}}</span> }}</span>
</a> </a>
</li> </li>
<li v-if="protocol.attributes.urls.update_protocol_url"> <li v-if="protocol.attributes.urls.update_protocol_url">
<a <a class="!px-3 !py-2.5 hover:!bg-sn-super-light-blue !text-sn-blue"
ref="updateProtocol" ref="updateProtocol"
data-action="update-self" data-action="update-self"
@click="updateProtocol" @click="updateProtocol"
> >
<span class="fas fa-sync-alt"></span>
<span>{{ <span>{{
i18n.t("my_modules.protocol.options_dropdown.update_protocol") i18n.t("my_modules.protocol.options_dropdown.update_protocol")
}}</span> }}</span>
</a> </a>
</li> </li>
<li v-if="protocol.attributes.urls.unlink_url"> <li v-if="protocol.attributes.urls.unlink_url">
<a <a class="!px-3 !py-2.5 hover:!bg-sn-super-light-blue !text-sn-blue"
ref="unlinkProtocol" ref="unlinkProtocol"
data-action="unlink" data-action="unlink"
@click="unlinkProtocol" @click="unlinkProtocol"
> >
<span class="fas fa-unlink"></span>
<span>{{ <span>{{
i18n.t("my_modules.protocol.options_dropdown.unlink") i18n.t("my_modules.protocol.options_dropdown.unlink")
}}</span> }}</span>
</a> </a>
</li> </li>
<li v-if="protocol.attributes.urls.revert_protocol_url"> <li v-if="protocol.attributes.urls.revert_protocol_url">
<a <a class="!px-3 !py-2.5 hover:!bg-sn-super-light-blue !text-sn-blue"
ref="revertProtocol" ref="revertProtocol"
data-action="revert" data-action="revert"
@click="revertProtocol" @click="revertProtocol"
> >
<span class="sn-icon sn-icon-restore"></span>
<span>{{ <span>{{
i18n.t("my_modules.protocol.options_dropdown.revert_protocol") i18n.t("my_modules.protocol.options_dropdown.revert_protocol")
}}</span> }}</span>
</a> </a>
</li> </li>
<li v-if="canDeleteSteps"> <li v-if="canDeleteSteps">
<a <a class="!px-3 !py-2.5 hover:!bg-sn-super-light-blue !text-sn-blue"
data-turbolinks="false" data-turbolinks="false"
@click.prevent="openStepsDeletingModal()" @click.prevent="openStepsDeletingModal()"
> >
<span class="sn-icon sn-icon-delete"></span>
<span>{{ <span>{{
i18n.t("my_modules.protocol.options_dropdown.delete_steps") i18n.t("my_modules.protocol.options_dropdown.delete_steps")
}}</span> }}</span>

View file

@ -53,68 +53,29 @@
</div> </div>
<div class="elements-actions-container"> <div class="elements-actions-container">
<input type="file" class="hidden" ref="fileSelector" @change="loadFromComputer" multiple /> <input type="file" class="hidden" ref="fileSelector" @change="loadFromComputer" multiple />
<div ref="elementsDropdownButton" v-if="urls.update_url" class="dropdown">
<button class="btn btn-light dropdown-toggle insert-button" type="button" :id="'stepInserMenu_' + step.id" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
{{ i18n.t('protocols.steps.insert.button') }}
<span class="sn-icon sn-icon-down"></span>
</button>
<ul ref="elementsDropdown" class="dropdown-menu insert-element-dropdown dropdown-menu-right" :aria-labelledby="'stepInserMenu_' + step.id">
<li class="title">
{{ i18n.t('protocols.steps.insert.title') }}
</li>
<li class="action" @click="createElement('table')">
<i class="sn-icon sn-icon-tables"></i>
{{ i18n.t('protocols.steps.insert.table') }}
</li>
<li class="action dropdown-submenu-item">
<i class="sn-icon sn-icon-tables"></i>
{{ i18n.t('protocols.steps.insert.well_plate') }}
<span class="caret"></span>
<ul class="dropdown-submenu"> <MenuDropdown
<li v-for="option in wellPlateOptions" :key="option.dimensions.toString()" class="action" @click="createElement('table', option.dimensions, true)"> :listItems="this.insertMenu"
{{ i18n.t(option.label) }} :btnText="i18n.t('protocols.steps.insert.button')"
</li> :position="'right'"
</ul> :caret="true"
</li> @create:table="(...args) => this.createElement('table', ...args)"
<li class="action" @click="createElement('checklist')"> @create:checklist="createElement('checklist')"
<i class="sn-icon sn-icon-activities"></i> @create:text="createElement('text')"
{{ i18n.t('protocols.steps.insert.checklist') }} @create:file="openLoadFromComputer"
</li> @create:wopi_file="openWopiFileModal"
<li class="action" @click="createElement('text')"> @create:ove_file="openOVEditor"
<i class="sn-icon sn-icon-result-text"></i> @create:marvinjs_file="openMarvinJsModal($refs.marvinJsButton)"
{{ i18n.t('protocols.steps.insert.text') }} ></MenuDropdown>
</li> <span
<li class="action dropdown-submenu-item"> class="new-marvinjs-upload-button hidden"
<i class="sn-icon sn-icon-files"></i> :data-object-id="step.id"
{{ i18n.t('protocols.steps.insert.attachment') }} ref="marvinJsButton"
<span class="caret"></span> :data-marvin-url="step.attributes.marvinjs_context.marvin_js_asset_url"
<ul class="dropdown-submenu"> :data-object-type="step.attributes.type"
<li class="action" @click="openLoadFromComputer"> tabindex="0"
{{ i18n.t('protocols.steps.insert.add_file') }} ></span> <!-- Hidden element to support legacy code -->
</li>
<li class="action" v-if="step.attributes.wopi_enabled" @click="openWopiFileModal">
{{ i18n.t('assets.create_wopi_file.button_text') }}
</li>
<li v-if="step.attributes.open_vector_editor_context.new_sequence_asset_url" @click="openOVEditor" @keyup.enter="openOVEditor">
{{ i18n.t('open_vector_editor.new_sequence_file') }}
</li>
<li class="action" v-if="step.attributes.marvinjs_enabled" @click="openMarvinJsModal($refs.marvinJsButton)">
<span
class="new-marvinjs-upload-button text-sn-black text-decoration-none"
:data-object-id="step.id"
ref="marvinJsButton"
:data-marvin-url="step.attributes.marvinjs_context.marvin_js_asset_url"
:data-object-type="step.attributes.type"
tabindex="0"
>
{{ i18n.t('marvinjs.new_button') }}
</span>
</li>
</ul>
</li>
</ul>
</div>
<a href="#" <a href="#"
v-if="!inRepository" v-if="!inRepository"
ref="comments" ref="comments"
@ -131,27 +92,15 @@
{{ step.attributes.comments_count }} {{ step.attributes.comments_count }}
</span> </span>
</a> </a>
<div v-if="urls.update_url" class="step-actions-container"> <MenuDropdown
<div ref="actionsDropdownButton" class="dropdown"> :listItems="this.actionsMenu"
<button class="btn btn-light icon-btn dropdown-toggle insert-button" type="button" :id="'stepOptionsMenu_' + step.id" data-toggle="dropdown" data-display="static" aria-haspopup="true" aria-expanded="true"> :btnClasses="'btn btn-light icon-btn'"
<i class="sn-icon sn-icon-more-hori"></i> :position="'right'"
</button> :btnIcon="'sn-icon sn-icon-more-hori'"
<ul ref="actionsDropdown" class="dropdown-menu dropdown-menu-right insert-element-dropdown" :aria-labelledby="'stepOptionsMenu_' + step.id"> @reorder="openReorderModal"
<li class="title"> @duplicate="duplicateStep"
{{ i18n.t('protocols.steps.options_dropdown.title') }} @delete="showDeleteModal"
</li> ></MenuDropdown>
<li v-if="urls.reorder_elements_url" class="action" @click="openReorderModal" :class="{ 'disabled': elements.length < 2 }">
{{ i18n.t('protocols.steps.options_dropdown.rearrange') }}
</li>
<li v-if="urls.duplicate_step_url" class="action" @click="duplicateStep">
{{ i18n.t('protocols.steps.options_dropdown.duplicate') }}
</li>
<li v-if="urls.delete_url" class="action" @click="showDeleteModal">
{{ i18n.t('protocols.steps.options_dropdown.delete') }}
</li>
</ul>
</div>
</div>
</div> </div>
</div> </div>
<div class="collapse in" :id="'stepBody' + step.id"> <div class="collapse in" :id="'stepBody' + step.id">
@ -215,6 +164,7 @@
import Attachments from '../shared/content/attachments.vue' import Attachments from '../shared/content/attachments.vue'
import clipboardPasteModal from '../shared/content/attachments/clipboard_paste_modal.vue' import clipboardPasteModal from '../shared/content/attachments/clipboard_paste_modal.vue'
import ReorderableItemsModal from '../shared/reorderable_items_modal.vue' import ReorderableItemsModal from '../shared/reorderable_items_modal.vue'
import MenuDropdown from '../shared/menu_dropdown.vue'
import UtilsMixin from '../mixins/utils.js' import UtilsMixin from '../mixins/utils.js'
import AttachmentsMixin from '../shared/content/mixins/attachments.js' import AttachmentsMixin from '../shared/content/mixins/attachments.js'
@ -260,12 +210,12 @@
editingName: false, editingName: false,
inlineEditError: null, inlineEditError: null,
wellPlateOptions: [ wellPlateOptions: [
{ label: 'protocols.steps.insert.well_plate_options.32_x_48', dimensions: [32, 48] }, { text: I18n.t('protocols.steps.insert.well_plate_options.32_x_48'), emit: 'create:table', params: [32, 48] },
{ label: 'protocols.steps.insert.well_plate_options.16_x_24', dimensions: [16, 24] }, { text: I18n.t('protocols.steps.insert.well_plate_options.16_x_24'), emit: 'create:table', params: [16, 24] },
{ label: 'protocols.steps.insert.well_plate_options.8_x_12', dimensions: [8, 12] }, { text: I18n.t('protocols.steps.insert.well_plate_options.8_x_12'), emit: 'create:table', params: [8, 12] },
{ label: 'protocols.steps.insert.well_plate_options.6_x_8', dimensions: [6, 8] }, { text: I18n.t('protocols.steps.insert.well_plate_options.6_x_8'), emit: 'create:table', params: [6, 8] },
{ label: 'protocols.steps.insert.well_plate_options.6_x_4', dimensions: [6, 4] }, { text: I18n.t('protocols.steps.insert.well_plate_options.6_x_4'), emit: 'create:table', params: [6, 4] },
{ label: 'protocols.steps.insert.well_plate_options.2_x_3', dimensions: [2, 3] } { text: I18n.t('protocols.steps.insert.well_plate_options.2_x_3'), emit: 'create:table', params: [2, 3] }
] ]
} }
}, },
@ -279,7 +229,8 @@
clipboardPasteModal, clipboardPasteModal,
Attachments, Attachments,
StorageUsage, StorageUsage,
ReorderableItemsModal ReorderableItemsModal,
MenuDropdown
}, },
created() { created() {
this.loadAttachments(); this.loadAttachments();
@ -312,6 +263,81 @@
}, },
urls() { urls() {
return this.step.attributes.urls || {} return this.step.attributes.urls || {}
},
filesMenu() {
let menu = [];
if (this.urls.upload_attachment_url) {
menu = menu.concat([{
text: this.i18n.t('protocols.steps.insert.add_file'),
emit: 'create:file'
}]);
}
if (this.step.attributes.wopi_enabled) {
menu = menu.concat([{
text: this.i18n.t('assets.create_wopi_file.button_text'),
emit: 'create:wopi_file'
}]);
}
if (this.step.attributes.open_vector_editor_context.new_sequence_asset_url) {
menu = menu.concat([{
text: this.i18n.t('open_vector_editor.new_sequence_file'),
emit: 'create:ove_file'
}]);
}
if (this.step.attributes.marvinjs_enabled) {
menu = menu.concat([{
text: this.i18n.t('marvinjs.new_button'),
emit: 'create:marvinjs_file'
}]);
}
return menu;
},
insertMenu() {
let menu = [];
if (this.urls.update_url) {
menu = menu.concat([{
text: this.i18n.t('protocols.steps.insert.text'),
emit: 'create:text'
},{
text: this.i18n.t('protocols.steps.insert.attachment'),
submenu: this.filesMenu,
position: 'left'
},{
text: this.i18n.t('protocols.steps.insert.table'),
emit: 'create:table'
},{
text: this.i18n.t('protocols.steps.insert.well_plate'),
submenu: this.wellPlateOptions,
position: 'left'
},{
text: this.i18n.t('protocols.steps.insert.checklist'),
emit: 'create:checklist'
}]);
}
return menu;
},
actionsMenu() {
let menu = [];
if (this.urls.reorder_elements_url) {
menu = menu.concat([{
text: this.i18n.t('protocols.steps.options_dropdown.rearrange'),
emit: 'reorder'
}]);
}
if (this.urls.duplicate_step_url) {
menu = menu.concat([{
text: this.i18n.t('protocols.steps.options_dropdown.duplicate'),
emit: 'duplicate'
}]);
}
if (this.urls.delete_url) {
menu = menu.concat([{
text: this.i18n.t('protocols.steps.options_dropdown.delete'),
emit: 'delete'
}]);
}
return menu;
} }
}, },
methods: { methods: {
@ -465,7 +491,9 @@
} }
}); });
}, },
createElement(elementType, tableDimensions = [5,5], plateTemplate = false) { createElement(elementType, tableDimensions = null) {
let plateTemplate = tableDimensions != null;
tableDimensions ||= [5, 5];
$.post(this.urls[`create_${elementType}_url`], { tableDimensions: tableDimensions, plateTemplate: plateTemplate }, (result) => { $.post(this.urls[`create_${elementType}_url`], { tableDimensions: tableDimensions, plateTemplate: plateTemplate }, (result) => {
result.data.isNew = true; result.data.isNew = true;
this.elements.push(result.data) this.elements.push(result.data)

View file

@ -25,64 +25,19 @@
</div> </div>
<div class="result-head-right flex elements-actions-container"> <div class="result-head-right flex elements-actions-container">
<input type="file" class="hidden" ref="fileSelector" @change="loadFromComputer" multiple /> <input type="file" class="hidden" ref="fileSelector" @change="loadFromComputer" multiple />
<div ref="elementsDropdownButton" v-if="urls.update_url" class="dropdown"> <MenuDropdown
<button class="btn btn-light dropdown-toggle insert-button" type="button" :id="'resultInsertMenu_' + result.id" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> :listItems="this.insertMenu"
{{ i18n.t('my_modules.results.insert.button') }} :btnText="i18n.t('my_modules.results.insert.button')"
<span class="sn-icon sn-icon-down"></span> :position="'right'"
</button> :caret="true"
<ul ref="elementsDropdown" class="dropdown-menu insert-element-dropdown dropdown-menu-right" :aria-labelledby="'resultInsertMenu_' + result.id"> @create:table="(...args) => this.createElement('table', ...args)"
<li class="title"> @create:checklist="createElement('checklist')"
{{ i18n.t('my_modules.results.insert.title') }} @create:text="createElement('text')"
</li> @create:file="openLoadFromComputer"
<li class="action" @click="createElement('table')"> @create:wopi_file="openWopiFileModal"
<i class="sn-icon sn-icon-tables"></i> @create:ove_file="openOVEditor"
{{ i18n.t('my_modules.results.insert.table') }} @create:marvinjs_file="openMarvinJsModal($refs.marvinJsButton)"
</li> ></MenuDropdown>
<li class="action dropdown-submenu-item">
<i class="sn-icon sn-icon-tables"></i>
{{ i18n.t('my_modules.results.insert.well_plate') }}
<span class="caret"></span>
<ul class="dropdown-submenu">
<li v-for="option in wellPlateOptions" :key="option.dimensions.toString()" class="action" @click="createElement('table', option.dimensions, true)">
{{ i18n.t(option.label) }}
</li>
</ul>
</li>
<li class="action" @click="createElement('text')">
<i class="sn-icon sn-icon-result-text"></i>
{{ i18n.t('my_modules.results.insert.text') }}
</li>
<li class="action dropdown-submenu-item">
<i class="sn-icon sn-icon-files"></i>
{{ i18n.t('my_modules.results.insert.attachment') }}
<span class="caret"></span>
<ul class="dropdown-submenu">
<li class="action" @click="openLoadFromComputer">
{{ i18n.t('my_modules.results.insert.add_file') }}
</li>
<li class="action" v-if="result.attributes.wopi_enabled" @click="openWopiFileModal">
{{ i18n.t('assets.create_wopi_file.button_text') }}
</li>
<li v-if="result.attributes.open_vector_editor_context.new_sequence_asset_url" @click="openOVEditor" @keyup.enter="openOVEditor">
{{ i18n.t('open_vector_editor.new_sequence_file') }}
</li>
<li class="action" v-if="result.attributes.marvinjs_enabled" @click="openMarvinJsModal($refs.marvinJsButton)">
<span
class="new-marvinjs-upload-button text-sn-black text-decoration-none"
:data-object-id="result.id"
ref="marvinJsButton"
:data-marvin-url="result.attributes.marvinjs_context.marvin_js_asset_url"
:data-object-type="result.attributes.type"
tabindex="0"
>
{{ i18n.t('marvinjs.new_button') }}
</span>
</li>
</ul>
</li>
</ul>
</div>
<a href="#" <a href="#"
ref="comments" ref="comments"
class="open-comments-sidebar btn icon-btn btn-light" class="open-comments-sidebar btn icon-btn btn-light"
@ -91,28 +46,18 @@
:data-object-id="result.id"> :data-object-id="result.id">
<i class="sn-icon sn-icon-comments"></i> <i class="sn-icon sn-icon-comments"></i>
</a> </a>
<div v-if="!locked" ref="actionsDropdownButton" class="dropdown"> <MenuDropdown
<button class="btn btn-light icon-btn dropdown-toggle insert-button" type="button" :id="'resultOptionsMenu_' + result.id" data-toggle="dropdown" data-display="static" aria-haspopup="true" aria-expanded="true"> v-if="!locked"
<i class="sn-icon sn-icon-more-hori"></i> :listItems="this.actionsMenu"
</button> :btnClasses="'btn btn-light icon-btn'"
<ul ref="actionsDropdown" class="dropdown-menu dropdown-menu-right insert-element-dropdown" :aria-labelledby="'resultOptionsMenu_' + result.id"> :position="'right'"
<li class="action" @click="openReorderModal" v-if="urls.reorder_elements_url"> :btnIcon="'sn-icon sn-icon-more-hori'"
{{ i18n.t('my_modules.results.actions.rearrange') }} @reorder="openReorderModal"
</li> @duplicate="duplicateResult"
<li class="action" @click="duplicateResult" v-if="urls.duplicate_url && !result.attributes.archived"> @archive="archiveResult"
{{ i18n.t('my_modules.results.actions.duplicate') }} @restore="restoreResult"
</li> @delete="deleteResult"
<li class="action" @click="archiveResult" v-if="urls.archive_url"> ></MenuDropdown>
{{ i18n.t('my_modules.results.actions.archive') }}
</li>
<li class="action" @click="restoreResult" v-if="urls.restore_url">
{{ i18n.t('my_modules.results.actions.restore') }}
</li>
<li class="action" @click="deleteResult" v-if="urls.delete_url">
{{ i18n.t('my_modules.results.actions.delete') }}
</li>
</ul>
</div>
</div> </div>
</div> </div>
@ -165,6 +110,7 @@
import ResultText from '../shared/content/text.vue'; import ResultText from '../shared/content/text.vue';
import Attachments from '../shared/content/attachments.vue'; import Attachments from '../shared/content/attachments.vue';
import InlineEdit from '../shared/inline_edit.vue' import InlineEdit from '../shared/inline_edit.vue'
import MenuDropdown from '../shared/menu_dropdown.vue'
import AttachmentsMixin from '../shared/content/mixins/attachments.js' import AttachmentsMixin from '../shared/content/mixins/attachments.js'
import WopiFileModal from '../shared/content/attachments/mixins/wopi_file_modal.js' import WopiFileModal from '../shared/content/attachments/mixins/wopi_file_modal.js'
@ -185,12 +131,12 @@
attachmentsReady: false, attachmentsReady: false,
showFileModal: false, showFileModal: false,
wellPlateOptions: [ wellPlateOptions: [
{ label: 'my_modules.results.insert.well_plate_options.32_x_48', dimensions: [32, 48] }, { text: I18n.t('protocols.steps.insert.well_plate_options.32_x_48'), emit: 'create:table', params: [32, 48] },
{ label: 'my_modules.results.insert.well_plate_options.16_x_24', dimensions: [16, 24] }, { text: I18n.t('protocols.steps.insert.well_plate_options.16_x_24'), emit: 'create:table', params: [16, 24] },
{ label: 'my_modules.results.insert.well_plate_options.8_x_12', dimensions: [8, 12] }, { text: I18n.t('protocols.steps.insert.well_plate_options.8_x_12'), emit: 'create:table', params: [8, 12] },
{ label: 'my_modules.results.insert.well_plate_options.6_x_8', dimensions: [6, 8] }, { text: I18n.t('protocols.steps.insert.well_plate_options.6_x_8'), emit: 'create:table', params: [6, 8] },
{ label: 'my_modules.results.insert.well_plate_options.6_x_4', dimensions: [6, 4] }, { text: I18n.t('protocols.steps.insert.well_plate_options.6_x_4'), emit: 'create:table', params: [6, 4] },
{ label: 'my_modules.results.insert.well_plate_options.2_x_3', dimensions: [2, 3] } { text: I18n.t('protocols.steps.insert.well_plate_options.2_x_3'), emit: 'create:table', params: [2, 3] }
], ],
editingName: false editingName: false
} }
@ -201,7 +147,8 @@
ResultTable, ResultTable,
ResultText, ResultText,
Attachments, Attachments,
InlineEdit InlineEdit,
MenuDropdown
}, },
watch: { watch: {
resultToReload() { resultToReload() {
@ -223,6 +170,90 @@
}, },
locked() { locked() {
return !(this.urls.restore_url || this.urls.archive_url || this.urls.delete_url || this.urls.update_url) return !(this.urls.restore_url || this.urls.archive_url || this.urls.delete_url || this.urls.update_url)
},
filesMenu() {
let menu = [];
if (this.urls.upload_attachment_url) {
menu = menu.concat([{
text: this.i18n.t('my_modules.results.insert.add_file'),
emit: 'create:file'
}]);
}
if (this.result.attributes.wopi_enabled) {
menu = menu.concat([{
text: this.i18n.t('assets.create_wopi_file.button_text'),
emit: 'create:wopi_file'
}]);
}
if (this.result.attributes.open_vector_editor_context.new_sequence_asset_url) {
menu = menu.concat([{
text: this.i18n.t('open_vector_editor.new_sequence_file'),
emit: 'create:ove_file'
}]);
}
if (this.result.attributes.marvinjs_enabled) {
menu = menu.concat([{
text: this.i18n.t('marvinjs.new_button'),
emit: 'create:marvinjs_file'
}]);
}
return menu;
},
insertMenu() {
let menu = [];
if (this.urls.update_url) {
menu = menu.concat([{
text: this.i18n.t('my_modules.results.insert.text'),
emit: 'create:text'
},{
text: this.i18n.t('my_modules.results.insert.attachment'),
submenu: this.filesMenu,
position: 'left'
},{
text: this.i18n.t('my_modules.results.insert.table'),
emit: 'create:table'
},{
text: this.i18n.t('my_modules.results.insert.well_plate'),
submenu: this.wellPlateOptions,
position: 'left'
}]);
}
return menu;
},
actionsMenu() {
let menu = [];
if (this.urls.reorder_elements_url) {
menu = menu.concat([{
text: this.i18n.t('my_modules.results.actions.rearrange'),
emit: 'reorder'
}]);
}
if (this.urls.duplicate_url && !this.result.attributes.archived) {
menu = menu.concat([{
text: this.i18n.t('my_modules.results.actions.duplicate'),
emit: 'duplicate'
}]);
}
if (this.urls.archive_url) {
menu = menu.concat([{
text: this.i18n.t('my_modules.results.actions.archive'),
emit: 'archive'
}]);
}
if (this.urls.restore_url) {
menu = menu.concat([{
text: this.i18n.t('my_modules.results.actions.restore'),
emit: 'restore'
}]);
}
if (this.urls.delete_url) {
menu = menu.concat([{
text: this.i18n.t('my_modules.results.actions.delete'),
emit: 'delete'
}]);
}
return menu;
} }
}, },
created() { created() {

View file

@ -34,19 +34,13 @@
</button> </button>
<FilterDropdown :filters="filters" @applyFilters="setFilters" /> <FilterDropdown :filters="filters" @applyFilters="setFilters" />
<MenuDropdown
<div class="dropdown"> :listItems="this.sortMenu"
<button class="dropdown-toggle btn btn-light icon-btn mr-3" id="sortDropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> :btnClasses="'btn btn-light icon-btn'"
<i class="sn-icon sn-icon-sort"></i> :position="'right'"
</button> :btnIcon="'sn-icon sn-icon-sort'"
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="sortDropdown"> @sort="setSort"
<li v-for="sort in sorts" :key="sort"> ></MenuDropdown>
<a class="cursor-pointer" @click="setSort(sort)">
{{ i18n.t(`my_modules.results.sorts.${sort}`)}}
</a>
</li>
</ul>
</div>
</div> </div>
</div> </div>
</template> </template>
@ -62,6 +56,7 @@
]; ];
import FilterDropdown from '../shared/filters/filter_dropdown.vue'; import FilterDropdown from '../shared/filters/filter_dropdown.vue';
import MenuDropdown from '../shared/menu_dropdown.vue'
export default { export default {
name: 'ResultsToolbar', name: 'ResultsToolbar',
@ -77,7 +72,7 @@
filters: null filters: null
} }
}, },
components: { FilterDropdown }, components: { FilterDropdown, MenuDropdown },
created() { created() {
this.filters = [ this.filters = [
{ {
@ -100,6 +95,17 @@
this.sorts = SORTS; this.sorts = SORTS;
}, },
computed: {
sortMenu() {
return this.sorts.map(sort => {
return {
text: this.i18n.t(`my_modules.results.sorts.${sort}`),
emit: 'sort',
params: sort
}
})
}
},
methods: { methods: {
setSort(sort) { setSort(sort) {
this.$emit('setSort', sort); this.$emit('setSort', sort);

View file

@ -6,45 +6,20 @@
{{ i18n.t('protocols.steps.files', {count: attachments.length}) }} {{ i18n.t('protocols.steps.files', {count: attachments.length}) }}
</div> </div>
<div class="flex items-center gap-2" v-if="parent.attributes.attachments_manageble && attachmentsReady"> <div class="flex items-center gap-2" v-if="parent.attributes.attachments_manageble && attachmentsReady">
<div ref="actionsDropdownButton" class="dropdown sci-dropdown"> <MenuDropdown
<button class="btn btn-light dropdown-toggle" type="button" id="dropdownAttachmentsOptions" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> :listItems="this.viewModeMenu"
<span>{{ i18n.t("attachments.preview_menu") }}</span> :btnText="i18n.t('attachments.preview_menu')"
<span class="sn-icon sn-icon-down"></span> :position="'right'"
</button> :caret="true"
<ul ref="actionsDropdown" class="dropdown-menu dropdown-menu-right dropdown-attachment-options" @attachment:viewMode = "changeAttachmentsViewMode"
aria-labelledby="dropdownAttachmentsOptions" ></MenuDropdown>
:data-parent-id="parent.id" <MenuDropdown
> :listItems="this.sortMenu"
<template v-if="parent.attributes.urls.update_asset_view_mode_url"> :btnIcon="'sn-icon sn-icon-sort-down'"
<li v-for="(viewMode, index) in viewModeOptions" :key="`viewMode_${index}`"> :btnClasses="'btn btn-light icon-btn'"
<a :position="'right'"
class="attachments-view-mode action-link" @attachment:order = "changeAttachmentsOrder"
:class="viewMode == parent.attributes.assets_view_mode ? 'selected' : ''" ></MenuDropdown>
@click="changeAttachmentsViewMode(viewMode)"
v-html="i18n.t(`attachments.view_mode.${viewMode}_html`)"
></a>
</li>
</template>
</ul>
</div>
<div ref="sortDropdownButton" class="dropdown sci-dropdown">
<button class="btn btn-light icon-btn dropdown-toggle" type="button" id="dropdownSortAttachmentsOptions" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<i class="sn-icon sn-icon-sort-up"></i>
</button>
<ul ref="sortDropdown" class="dropdown-menu dropdown-menu-right dropdown-attachment-options"
aria-labelledby="dropdownSortAttachmentsOptions"
:data-parent-id="parent.id"
>
<li v-for="(orderOption, index) in orderOptions" :key="`orderOption_${index}`" :class="{'divider' : (orderOption == 'divider')}">
<a v-if="orderOption != 'divider'" class="action-link change-order"
@click="changeAttachmentsOrder(orderOption)"
:class="parent.attributes.assets_order == orderOption ? 'selected' : ''"
>
{{ i18n.t(`general.sort_new.${orderOption}`) }}
</a>
</li>
</ul>
</div>
</div> </div>
</div> </div>
<div class="attachments" :data-parent-id="parent.id"> <div class="attachments" :data-parent-id="parent.id">
@ -68,6 +43,7 @@
import thumbnailAttachment from './attachments/thumbnail.vue' import thumbnailAttachment from './attachments/thumbnail.vue'
import uploadingAttachment from './attachments/uploading.vue' import uploadingAttachment from './attachments/uploading.vue'
import emptyAttachment from './attachments/empty.vue' import emptyAttachment from './attachments/empty.vue'
import MenuDropdown from '../menu_dropdown.vue'
import WopiFileModal from './attachments/mixins/wopi_file_modal.js' import WopiFileModal from './attachments/mixins/wopi_file_modal.js'
@ -90,7 +66,7 @@
data() { data() {
return { return {
viewModeOptions: ['inline', 'thumbnail', 'list'], viewModeOptions: ['inline', 'thumbnail', 'list'],
orderOptions: ['new', 'old', 'divider', 'atoz', 'ztoa'] orderOptions: ['new', 'old', 'atoz', 'ztoa']
} }
}, },
mixins: [WopiFileModal, AttachmentMovedMixin], mixins: [WopiFileModal, AttachmentMovedMixin],
@ -99,7 +75,8 @@
inlineAttachment, inlineAttachment,
listAttachment, listAttachment,
uploadingAttachment, uploadingAttachment,
emptyAttachment emptyAttachment,
MenuDropdown
}, },
watch: { watch: {
attachmentsReady() { attachmentsReady() {
@ -128,6 +105,29 @@
return a.attributes.asset_order > b.attributes.asset_order ? 1 : -1; return a.attributes.asset_order > b.attributes.asset_order ? 1 : -1;
}) })
},
viewModeMenu() {
let menu = [];
this.viewModeOptions.forEach((viewMode) => {
menu.push({
text: this.i18n.t(`attachments.view_mode.${viewMode}_html`),
emit: 'attachment:viewMode',
params: viewMode
})
})
return menu;
},
sortMenu() {
let menu = [];
this.orderOptions.forEach((orderOption, i) => {
menu.push({
text: this.i18n.t(`general.sort_new.${orderOption}`),
emit: 'attachment:order',
params: orderOption,
dividerBefore: i === 2
})
})
return menu;
} }
}, },
mounted() { mounted() {

View file

@ -1,88 +1,39 @@
<template> <template>
<div class="dropdown asset-context-menu" ref="menu"> <div class="asset-context-menu" ref="menu">
<button class="btn btn-light btn-xs dropdown-toggle icon-btn" type="button" id="dropdownAssetContextMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <a class="marvinjs-edit-button hidden"
<i class="sn-icon sn-icon-more-hori"></i> v-if="attachment.attributes.asset_type == 'marvinjs' && attachment.attributes.urls.marvin_js_start_edit"
</button> ref="marvinjsEditButton"
:data-sketch-id="attachment.id"
<ul class="dropdown-menu dropdown-menu-right" :data-update-url="attachment.attributes.urls.marvin_js"
aria-labelledby="dropdownAssetContextMenu" :data-sketch-start-edit-url="attachment.attributes.urls.marvin_js_start_edit"
:data-asset-id="attachment.id" :data-sketch-name="attachment.attributes.metadata.name"
> :data-sketch-description="attachment.attributes.metadata.description"
<li v-if="attachment.attributes.wopi && attachment.attributes.urls.edit_asset" > ></a>
<a :href="attachment.attributes.urls.edit_asset" <a class="image-edit-button hidden"
id="wopi_file_edit_button" v-if="attachment.attributes.asset_type != 'marvinjs'
class="btn btn-light" && attachment.attributes.image_editable
:class="attachment.attributes.wopi_context.edit_supported ? '' : 'disabled'" && attachment.attributes.urls.start_edit_image"
:title="attachment.attributes.wopi_context.title" ref="imageEditButton"
target="_blank" :data-image-id="attachment.id"
> :data-image-name="attachment.attributes.file_name"
{{ attachment.attributes.wopi_context.button_text }} :data-image-url="attachment.attributes.urls.asset_file"
</a> :data-image-quality="attachment.attributes.image_context.quality"
</li> :data-image-mime-type="attachment.attributes.image_context.type"
<li v-if="attachment.attributes.asset_type == 'gene_sequence' && attachment.attributes.urls.open_vector_editor_edit"> :data-image-start-edit-url="attachment.attributes.urls.start_edit_image"
<a class="ove-edit-button" @click="openOVEditor(attachment.attributes.urls.open_vector_editor_edit)"> ></a>
{{ i18n.t('open_vector_editor.edit_sequence') }} <MenuDropdown
</a> class="ml-auto"
</li> :listItems="this.menu"
<li v-if="attachment.attributes.asset_type == 'marvinjs' && attachment.attributes.urls.marvin_js_start_edit"> :btnClasses="'btn btn-light icon-btn !bg-sn-white'"
<a class="marvinjs-edit-button" :position="'right'"
:data-sketch-id="attachment.id" :btnIcon="'sn-icon sn-icon-more-hori'"
:data-update-url="attachment.attributes.urls.marvin_js" @open_ove_editor="openOVEditor(attachment.attributes.urls.open_vector_editor_edit)"
:data-sketch-start-edit-url="attachment.attributes.urls.marvin_js_start_edit" @open_marvinjs_editor="openMarvinJsEditor"
:data-sketch-name="attachment.attributes.metadata.name" @open_scinote_editor="openScinoteEditor"
:data-sketch-description="attachment.attributes.metadata.description" @delete="deleteModal = true"
> @viewMode="changeViewMode"
{{ i18n.t('assets.file_preview.edit_in_marvinjs') }} @move="showMoveModal"
</a> ></MenuDropdown>
</li>
<li v-if="attachment.attributes.asset_type != 'marvinjs'
&& attachment.attributes.image_editable
&& attachment.attributes.urls.start_edit_image">
<a class="image-edit-button"
:data-image-id="attachment.id"
:data-image-name="attachment.attributes.file_name"
:data-image-url="attachment.attributes.urls.asset_file"
:data-image-quality="attachment.attributes.image_context.quality"
:data-image-mime-type="attachment.attributes.image_context.type"
:data-image-start-edit-url="attachment.attributes.urls.start_edit_image"
>
{{ i18n.t('assets.file_preview.edit_in_scinote') }}
</a>
</li>
<li>
<a :href="attachment.attributes.urls.download" data-turbolinks="false">
{{ i18n.t('Download') }}
</a>
</li>
<template v-if="attachment.attributes.urls.move_targets">
<li>
<a @click.prevent.stop="showMoveModal">
{{ i18n.t("assets.context_menu.move") }}
</a>
</li>
</template>
<template v-if="attachment.attributes.urls.delete">
<li>
<a @click.prevent.stop="deleteModal = true">
{{ i18n.t("assets.context_menu.delete") }}
</a>
</li>
</template>
<template v-if="attachment.attributes.urls.toggle_view_mode">
<li role="separator" class="divider"></li>
<li class="divider-label">
{{ i18n.t("assets.context_menu.set_view_size") }}
</li>
<li v-for="(viewMode, index) in viewModeOptions" :key="`viewMode_${index}`">
<a
class="change-preview-type"
:class="viewMode == attachment.attributes.view_mode ? 'selected' : ''"
@click.prevent.stop="changeViewMode(viewMode)"
v-html="i18n.t(`assets.context_menu.${viewMode}_html`)"
></a>
</li>
</template>
</ul>
<deleteAttachmentModal <deleteAttachmentModal
v-if="deleteModal" v-if="deleteModal"
:fileName="attachment.attributes.file_name" :fileName="attachment.attributes.file_name"
@ -100,10 +51,11 @@
import deleteAttachmentModal from './delete_modal.vue' import deleteAttachmentModal from './delete_modal.vue'
import moveAssetModal from '../modal/move.vue' import moveAssetModal from '../modal/move.vue'
import MoveMixin from './mixins/move.js' import MoveMixin from './mixins/move.js'
import MenuDropdown from '../../menu_dropdown.vue'
export default { export default {
name: 'contextMenu', name: 'contextMenu',
components: { deleteAttachmentModal, moveAssetModal }, components: { deleteAttachmentModal, moveAssetModal, MenuDropdown },
mixins: [MoveMixin], mixins: [MoveMixin],
props: { props: {
attachment: { attachment: {
@ -117,17 +69,65 @@
deleteModal: false deleteModal: false
} }
}, },
mounted() { computed: {
$(this.$refs.menu).on('show.bs.dropdown', function() { menu() {
let screenHeight = screen.height; let menu = [];
let dropdownPosition = this.getBoundingClientRect().y; if (this.attachment.attributes.wopi && this.attachment.attributes.urls.edit_asset) {
let dropdownMenu = $(this).find('.dropdown-menu'); menu.push({
if ((screenHeight / 2) < dropdownPosition) { text: this.attachment.attributes.wopi_context.button_text,
dropdownMenu.css({ top: 'unset', bottom: '100%' }); url: this.attachment.attributes.urls.edit_asset,
} else { url_target: '_blank'
dropdownMenu.css({ bottom: 'unset', top: '100%' }); })
} }
}) if (this.attachment.attributes.asset_type == 'gene_sequence' && this.attachment.attributes.urls.open_vector_editor_edit) {
menu.push({
text: this.i18n.t('open_vector_editor.edit_sequence'),
emit: 'open_ove_editor',
})
}
if (this.attachment.attributes.asset_type == 'marvinjs' && this.attachment.attributes.urls.marvin_js_start_edit) {
menu.push({
text: this.i18n.t('assets.file_preview.edit_in_marvinjs'),
emit: 'open_marvinjs_editor',
})
}
if (this.attachment.attributes.asset_type != 'marvinjs'
&& this.attachment.attributes.image_editable
&& this.attachment.attributes.urls.start_edit_image) {
menu.push({
text: this.i18n.t('assets.file_preview.edit_in_scinote'),
emit: 'open_scinote_editor',
})
}
menu.push({
text: this.i18n.t('Download'),
url: this.attachment.attributes.urls.download,
url_target: '_blank'
})
if (this.attachment.attributes.urls.move_targets) {
menu.push({
text: this.i18n.t('assets.context_menu.move'),
emit: 'move'
})
}
if (this.attachment.attributes.urls.delete) {
menu.push({
text: this.i18n.t('assets.context_menu.delete'),
emit: 'delete'
})
}
if (this.attachment.attributes.urls.toggle_view_mode) {
this.viewModeOptions.forEach((viewMode, i) => {
menu.push({
text: this.i18n.t(`assets.context_menu.${viewMode}_html`),
emit: 'viewMode',
params: viewMode,
dividerBefore: i === 0
})
})
}
return menu;
}
}, },
methods: { methods: {
changeViewMode(viewMode) { changeViewMode(viewMode) {
@ -145,7 +145,14 @@
}, },
openOVEditor(url) { openOVEditor(url) {
window.showIFrameModal(url); window.showIFrameModal(url);
} },
openMarvinJsEditor() {
console.log(this.$refs)
$(this.$refs.marvinjsEditButton).trigger('click');
},
openScinoteEditor() {
$(this.$refs.imageEditButton).trigger('click');
},
} }
} }
</script> </script>

View file

@ -7,8 +7,7 @@ export default {
}; };
}, },
methods: { methods: {
showMoveModal(event) { showMoveModal() {
event.stopPropagation();
this.movingAttachment = true; this.movingAttachment = true;
}, },
closeMoveModal() { closeMoveModal() {

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="w-full relative"> <div class="w-full relative flex">
<template v-if="editing"> <template v-if="editing">
<input type="text" <input type="text"
v-if="singleLine" v-if="singleLine"
@ -18,7 +18,7 @@
@focus="setCaretAtEnd"/> @focus="setCaretAtEnd"/>
<textarea v-else <textarea v-else
ref="input" ref="input"
class="overflow-hidden leading-5 inline-block outline-none px-0 py-1 border-0 border-solid border-b w-full border-t-transparent" class="overflow-hidden leading-5 inline-block outline-none px-0 py-1 border-0 border-solid border-y w-full border-t-transparent mb-[1px]"
:class="{ :class="{
'inline-edit-placeholder text-sn-grey caret-black': isBlank, 'inline-edit-placeholder text-sn-grey caret-black': isBlank,
'border-sn-delete-red': error, 'border-sn-delete-red': error,
@ -269,7 +269,7 @@
if (this.editing && !this.singleLine) { if (this.editing && !this.singleLine) {
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.input.style.height = '0px'; this.$refs.input.style.height = '0px';
this.$refs.input.style.height = this.$refs.input.scrollHeight - 3 + 'px'; this.$refs.input.style.height = this.$refs.input.scrollHeight + 'px';
}); });
} }
} }

View file

@ -3,22 +3,48 @@
<button ref="openBtn" :class="btnClasses" @click="showMenu = !showMenu"> <button ref="openBtn" :class="btnClasses" @click="showMenu = !showMenu">
<i v-if="btnIcon" :class="btnIcon"></i> <i v-if="btnIcon" :class="btnIcon"></i>
{{ btnText }} {{ btnText }}
<i v-if="caret && showMenu" class="fas fa-caret-up"></i> <i v-if="caret && showMenu" class="sn-icon sn-icon-up"></i>
<i v-else-if="caret" class="fas fa-caret-down"></i> <i v-else-if="caret" class="sn-icon sn-icon-down"></i>
</button> </button>
<div ref="flyout" <div ref="flyout"
class="absolute z-50 bg-sn-white rounded p-2.5 sn-shadow-menu-sm" class="absolute z-[150] bg-sn-white rounded p-2.5 sn-shadow-menu-sm min-w-full"
:class="{'right-0': position === 'right', 'left-0': position === 'left'}" :class="{'right-0': position === 'right', 'left-0': position === 'left'}"
v-if="showMenu" v-if="showMenu"
v-click-outside="{handler: 'closeMenu', exclude: ['openBtn', 'flyout']}"> v-click-outside="{handler: 'closeMenu', exclude: ['openBtn', 'flyout']}">
<a v-for="(item, i) in listItems" <span v-for="(item, i) in listItems" :key="i">
:key="i" <a :href="item.url" v-if="!item.submenu"
:href="item.url" :class="{'border-0 border-t border-solid border-sn-light-grey': item.dividerBefore}"
class="block whitespace-nowrap px-3 py-2.5 hover:no-underline cursor-pointer hover:bg-sn-super-light-blue" :traget="item.url_target || '_self'"
@click="handleClick($event, item)" class="block whitespace-nowrap px-3 py-2.5 hover:no-underline cursor-pointer hover:bg-sn-super-light-blue"
> @click="handleClick($event, item)"
{{ item.text }} >
</a> {{ item.text }}
</a>
<span v-else
@click="showSubmenu = i"
:class="{'!bg-sn-super-light-grey': showSubmenu == i}"
class="flex group items-center relative text-sn-blue whitespace-nowrap px-3 py-2.5 hover:no-underline cursor-pointer hover:bg-sn-super-light-blue"
>
{{ item.text }}
<i class="sn-icon sn-icon-right ml-auto"></i>
<div v-if="showSubmenu == i"
class="absolute top-0 bg-sn-white rounded p-2.5 sn-shadow-menu-sm"
:class="{
'left-0 ml-[calc(100%_+_0.625rem)]': item.position === 'right',
'right-0 mr-[calc(100%_+_0.625rem)]': item.position === 'left'
}"
>
<a v-for="(sub_item, si) in item.submenu" :key="si"
:href="sub_item.url"
:traget="sub_item.url_target || '_self'"
class="block whitespace-nowrap px-3 py-2.5 hover:no-underline cursor-pointer hover:bg-sn-super-light-blue"
@click="handleClick($event, sub_item)"
>
{{ sub_item.text }}
</a>
</div>
</span>
</span>
</div> </div>
</div> </div>
</template> </template>
@ -28,7 +54,7 @@
export default { export default {
name: 'DropdownMenu', name: 'DropdownMenu',
props: { props: {
listItems: { type: Array, required: true }, listItems: { type: Array, default: () => [] },
position: { type: String, default: 'left' }, position: { type: String, default: 'left' },
btnClasses: { type: String, default: 'btn btn-light' }, btnClasses: { type: String, default: 'btn btn-light' },
btnText: { type: String, required: false }, btnText: { type: String, required: false },
@ -38,11 +64,13 @@ export default {
data() { data() {
return { return {
showMenu: false, showMenu: false,
showSubmenu: null,
} }
}, },
methods: { methods: {
closeMenu() { closeMenu() {
this.showMenu = false; this.showMenu = false;
this.showSubmenu = null;
}, },
handleClick(event, item) { handleClick(event, item) {
if (!item.url) { if (!item.url) {
@ -50,10 +78,10 @@ export default {
} }
if (item.emit) { if (item.emit) {
this.$emit(item.emit) this.$emit(item.emit, item.params)
} }
this.showMenu = false; this.closeMenu();
} }
} }
} }