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 {
max-height: 250px;
overflow: auto;
z-index: 251;
.repository {
@include font-button;

View file

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

View file

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

View file

@ -53,68 +53,29 @@
</div>
<div class="elements-actions-container">
<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">
<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('checklist')">
<i class="sn-icon sn-icon-activities"></i>
{{ i18n.t('protocols.steps.insert.checklist') }}
</li>
<li class="action" @click="createElement('text')">
<i class="sn-icon sn-icon-result-text"></i>
{{ i18n.t('protocols.steps.insert.text') }}
</li>
<li class="action dropdown-submenu-item">
<i class="sn-icon sn-icon-files"></i>
{{ i18n.t('protocols.steps.insert.attachment') }}
<span class="caret"></span>
<ul class="dropdown-submenu">
<li class="action" @click="openLoadFromComputer">
{{ i18n.t('protocols.steps.insert.add_file') }}
</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>
<MenuDropdown
:listItems="this.insertMenu"
:btnText="i18n.t('protocols.steps.insert.button')"
:position="'right'"
:caret="true"
@create:table="(...args) => this.createElement('table', ...args)"
@create:checklist="createElement('checklist')"
@create:text="createElement('text')"
@create:file="openLoadFromComputer"
@create:wopi_file="openWopiFileModal"
@create:ove_file="openOVEditor"
@create:marvinjs_file="openMarvinJsModal($refs.marvinJsButton)"
></MenuDropdown>
<span
class="new-marvinjs-upload-button hidden"
: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"
></span> <!-- Hidden element to support legacy code -->
<a href="#"
v-if="!inRepository"
ref="comments"
@ -131,27 +92,15 @@
{{ step.attributes.comments_count }}
</span>
</a>
<div v-if="urls.update_url" class="step-actions-container">
<div ref="actionsDropdownButton" class="dropdown">
<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">
<i class="sn-icon sn-icon-more-hori"></i>
</button>
<ul ref="actionsDropdown" class="dropdown-menu dropdown-menu-right insert-element-dropdown" :aria-labelledby="'stepOptionsMenu_' + step.id">
<li class="title">
{{ i18n.t('protocols.steps.options_dropdown.title') }}
</li>
<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>
<MenuDropdown
:listItems="this.actionsMenu"
:btnClasses="'btn btn-light icon-btn'"
:position="'right'"
:btnIcon="'sn-icon sn-icon-more-hori'"
@reorder="openReorderModal"
@duplicate="duplicateStep"
@delete="showDeleteModal"
></MenuDropdown>
</div>
</div>
<div class="collapse in" :id="'stepBody' + step.id">
@ -215,6 +164,7 @@
import Attachments from '../shared/content/attachments.vue'
import clipboardPasteModal from '../shared/content/attachments/clipboard_paste_modal.vue'
import ReorderableItemsModal from '../shared/reorderable_items_modal.vue'
import MenuDropdown from '../shared/menu_dropdown.vue'
import UtilsMixin from '../mixins/utils.js'
import AttachmentsMixin from '../shared/content/mixins/attachments.js'
@ -260,12 +210,12 @@
editingName: false,
inlineEditError: null,
wellPlateOptions: [
{ label: 'protocols.steps.insert.well_plate_options.32_x_48', dimensions: [32, 48] },
{ label: 'protocols.steps.insert.well_plate_options.16_x_24', dimensions: [16, 24] },
{ label: 'protocols.steps.insert.well_plate_options.8_x_12', dimensions: [8, 12] },
{ label: 'protocols.steps.insert.well_plate_options.6_x_8', dimensions: [6, 8] },
{ label: 'protocols.steps.insert.well_plate_options.6_x_4', dimensions: [6, 4] },
{ label: 'protocols.steps.insert.well_plate_options.2_x_3', dimensions: [2, 3] }
{ text: I18n.t('protocols.steps.insert.well_plate_options.32_x_48'), emit: 'create:table', params: [32, 48] },
{ text: I18n.t('protocols.steps.insert.well_plate_options.16_x_24'), emit: 'create:table', params: [16, 24] },
{ text: I18n.t('protocols.steps.insert.well_plate_options.8_x_12'), emit: 'create:table', params: [8, 12] },
{ text: I18n.t('protocols.steps.insert.well_plate_options.6_x_8'), emit: 'create:table', params: [6, 8] },
{ text: I18n.t('protocols.steps.insert.well_plate_options.6_x_4'), emit: 'create:table', params: [6, 4] },
{ text: I18n.t('protocols.steps.insert.well_plate_options.2_x_3'), emit: 'create:table', params: [2, 3] }
]
}
},
@ -279,7 +229,8 @@
clipboardPasteModal,
Attachments,
StorageUsage,
ReorderableItemsModal
ReorderableItemsModal,
MenuDropdown
},
created() {
this.loadAttachments();
@ -312,6 +263,81 @@
},
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: {
@ -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) => {
result.data.isNew = true;
this.elements.push(result.data)

View file

@ -25,64 +25,19 @@
</div>
<div class="result-head-right flex elements-actions-container">
<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="'resultInsertMenu_' + result.id" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
{{ i18n.t('my_modules.results.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="'resultInsertMenu_' + result.id">
<li class="title">
{{ i18n.t('my_modules.results.insert.title') }}
</li>
<li class="action" @click="createElement('table')">
<i class="sn-icon sn-icon-tables"></i>
{{ i18n.t('my_modules.results.insert.table') }}
</li>
<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>
<MenuDropdown
:listItems="this.insertMenu"
:btnText="i18n.t('my_modules.results.insert.button')"
:position="'right'"
:caret="true"
@create:table="(...args) => this.createElement('table', ...args)"
@create:checklist="createElement('checklist')"
@create:text="createElement('text')"
@create:file="openLoadFromComputer"
@create:wopi_file="openWopiFileModal"
@create:ove_file="openOVEditor"
@create:marvinjs_file="openMarvinJsModal($refs.marvinJsButton)"
></MenuDropdown>
<a href="#"
ref="comments"
class="open-comments-sidebar btn icon-btn btn-light"
@ -91,28 +46,18 @@
:data-object-id="result.id">
<i class="sn-icon sn-icon-comments"></i>
</a>
<div v-if="!locked" ref="actionsDropdownButton" class="dropdown">
<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">
<i class="sn-icon sn-icon-more-hori"></i>
</button>
<ul ref="actionsDropdown" class="dropdown-menu dropdown-menu-right insert-element-dropdown" :aria-labelledby="'resultOptionsMenu_' + result.id">
<li class="action" @click="openReorderModal" v-if="urls.reorder_elements_url">
{{ i18n.t('my_modules.results.actions.rearrange') }}
</li>
<li class="action" @click="duplicateResult" v-if="urls.duplicate_url && !result.attributes.archived">
{{ i18n.t('my_modules.results.actions.duplicate') }}
</li>
<li class="action" @click="archiveResult" v-if="urls.archive_url">
{{ 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>
<MenuDropdown
v-if="!locked"
:listItems="this.actionsMenu"
:btnClasses="'btn btn-light icon-btn'"
:position="'right'"
:btnIcon="'sn-icon sn-icon-more-hori'"
@reorder="openReorderModal"
@duplicate="duplicateResult"
@archive="archiveResult"
@restore="restoreResult"
@delete="deleteResult"
></MenuDropdown>
</div>
</div>
@ -165,6 +110,7 @@
import ResultText from '../shared/content/text.vue';
import Attachments from '../shared/content/attachments.vue';
import InlineEdit from '../shared/inline_edit.vue'
import MenuDropdown from '../shared/menu_dropdown.vue'
import AttachmentsMixin from '../shared/content/mixins/attachments.js'
import WopiFileModal from '../shared/content/attachments/mixins/wopi_file_modal.js'
@ -185,12 +131,12 @@
attachmentsReady: false,
showFileModal: false,
wellPlateOptions: [
{ label: 'my_modules.results.insert.well_plate_options.32_x_48', dimensions: [32, 48] },
{ label: 'my_modules.results.insert.well_plate_options.16_x_24', dimensions: [16, 24] },
{ label: 'my_modules.results.insert.well_plate_options.8_x_12', dimensions: [8, 12] },
{ label: 'my_modules.results.insert.well_plate_options.6_x_8', dimensions: [6, 8] },
{ label: 'my_modules.results.insert.well_plate_options.6_x_4', dimensions: [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.32_x_48'), emit: 'create:table', params: [32, 48] },
{ text: I18n.t('protocols.steps.insert.well_plate_options.16_x_24'), emit: 'create:table', params: [16, 24] },
{ text: I18n.t('protocols.steps.insert.well_plate_options.8_x_12'), emit: 'create:table', params: [8, 12] },
{ text: I18n.t('protocols.steps.insert.well_plate_options.6_x_8'), emit: 'create:table', params: [6, 8] },
{ text: I18n.t('protocols.steps.insert.well_plate_options.6_x_4'), emit: 'create:table', params: [6, 4] },
{ text: I18n.t('protocols.steps.insert.well_plate_options.2_x_3'), emit: 'create:table', params: [2, 3] }
],
editingName: false
}
@ -201,7 +147,8 @@
ResultTable,
ResultText,
Attachments,
InlineEdit
InlineEdit,
MenuDropdown
},
watch: {
resultToReload() {
@ -223,6 +170,90 @@
},
locked() {
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() {

View file

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

View file

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

View file

@ -1,88 +1,39 @@
<template>
<div class="dropdown 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">
<i class="sn-icon sn-icon-more-hori"></i>
</button>
<ul class="dropdown-menu dropdown-menu-right"
aria-labelledby="dropdownAssetContextMenu"
:data-asset-id="attachment.id"
>
<li v-if="attachment.attributes.wopi && attachment.attributes.urls.edit_asset" >
<a :href="attachment.attributes.urls.edit_asset"
id="wopi_file_edit_button"
class="btn btn-light"
:class="attachment.attributes.wopi_context.edit_supported ? '' : 'disabled'"
:title="attachment.attributes.wopi_context.title"
target="_blank"
>
{{ attachment.attributes.wopi_context.button_text }}
</a>
</li>
<li v-if="attachment.attributes.asset_type == 'gene_sequence' && attachment.attributes.urls.open_vector_editor_edit">
<a class="ove-edit-button" @click="openOVEditor(attachment.attributes.urls.open_vector_editor_edit)">
{{ i18n.t('open_vector_editor.edit_sequence') }}
</a>
</li>
<li v-if="attachment.attributes.asset_type == 'marvinjs' && attachment.attributes.urls.marvin_js_start_edit">
<a class="marvinjs-edit-button"
:data-sketch-id="attachment.id"
:data-update-url="attachment.attributes.urls.marvin_js"
:data-sketch-start-edit-url="attachment.attributes.urls.marvin_js_start_edit"
:data-sketch-name="attachment.attributes.metadata.name"
:data-sketch-description="attachment.attributes.metadata.description"
>
{{ i18n.t('assets.file_preview.edit_in_marvinjs') }}
</a>
</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>
<div class="asset-context-menu" ref="menu">
<a class="marvinjs-edit-button hidden"
v-if="attachment.attributes.asset_type == 'marvinjs' && attachment.attributes.urls.marvin_js_start_edit"
ref="marvinjsEditButton"
:data-sketch-id="attachment.id"
:data-update-url="attachment.attributes.urls.marvin_js"
:data-sketch-start-edit-url="attachment.attributes.urls.marvin_js_start_edit"
:data-sketch-name="attachment.attributes.metadata.name"
:data-sketch-description="attachment.attributes.metadata.description"
></a>
<a class="image-edit-button hidden"
v-if="attachment.attributes.asset_type != 'marvinjs'
&& attachment.attributes.image_editable
&& attachment.attributes.urls.start_edit_image"
ref="imageEditButton"
: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"
></a>
<MenuDropdown
class="ml-auto"
:listItems="this.menu"
:btnClasses="'btn btn-light icon-btn !bg-sn-white'"
:position="'right'"
:btnIcon="'sn-icon sn-icon-more-hori'"
@open_ove_editor="openOVEditor(attachment.attributes.urls.open_vector_editor_edit)"
@open_marvinjs_editor="openMarvinJsEditor"
@open_scinote_editor="openScinoteEditor"
@delete="deleteModal = true"
@viewMode="changeViewMode"
@move="showMoveModal"
></MenuDropdown>
<deleteAttachmentModal
v-if="deleteModal"
:fileName="attachment.attributes.file_name"
@ -100,10 +51,11 @@
import deleteAttachmentModal from './delete_modal.vue'
import moveAssetModal from '../modal/move.vue'
import MoveMixin from './mixins/move.js'
import MenuDropdown from '../../menu_dropdown.vue'
export default {
name: 'contextMenu',
components: { deleteAttachmentModal, moveAssetModal },
components: { deleteAttachmentModal, moveAssetModal, MenuDropdown },
mixins: [MoveMixin],
props: {
attachment: {
@ -117,17 +69,65 @@
deleteModal: false
}
},
mounted() {
$(this.$refs.menu).on('show.bs.dropdown', function() {
let screenHeight = screen.height;
let dropdownPosition = this.getBoundingClientRect().y;
let dropdownMenu = $(this).find('.dropdown-menu');
if ((screenHeight / 2) < dropdownPosition) {
dropdownMenu.css({ top: 'unset', bottom: '100%' });
} else {
dropdownMenu.css({ bottom: 'unset', top: '100%' });
computed: {
menu() {
let menu = [];
if (this.attachment.attributes.wopi && this.attachment.attributes.urls.edit_asset) {
menu.push({
text: this.attachment.attributes.wopi_context.button_text,
url: this.attachment.attributes.urls.edit_asset,
url_target: '_blank'
})
}
})
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: {
changeViewMode(viewMode) {
@ -145,7 +145,14 @@
},
openOVEditor(url) {
window.showIFrameModal(url);
}
},
openMarvinJsEditor() {
console.log(this.$refs)
$(this.$refs.marvinjsEditButton).trigger('click');
},
openScinoteEditor() {
$(this.$refs.imageEditButton).trigger('click');
},
}
}
</script>

View file

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

View file

@ -1,5 +1,5 @@
<template>
<div class="w-full relative">
<div class="w-full relative flex">
<template v-if="editing">
<input type="text"
v-if="singleLine"
@ -18,7 +18,7 @@
@focus="setCaretAtEnd"/>
<textarea v-else
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="{
'inline-edit-placeholder text-sn-grey caret-black': isBlank,
'border-sn-delete-red': error,
@ -269,7 +269,7 @@
if (this.editing && !this.singleLine) {
this.$nextTick(() => {
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">
<i v-if="btnIcon" :class="btnIcon"></i>
{{ btnText }}
<i v-if="caret && showMenu" class="fas fa-caret-up"></i>
<i v-else-if="caret" class="fas fa-caret-down"></i>
<i v-if="caret && showMenu" class="sn-icon sn-icon-up"></i>
<i v-else-if="caret" class="sn-icon sn-icon-down"></i>
</button>
<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'}"
v-if="showMenu"
v-click-outside="{handler: 'closeMenu', exclude: ['openBtn', 'flyout']}">
<a v-for="(item, i) in listItems"
:key="i"
:href="item.url"
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>
<span v-for="(item, i) in listItems" :key="i">
<a :href="item.url" v-if="!item.submenu"
:class="{'border-0 border-t border-solid border-sn-light-grey': item.dividerBefore}"
:traget="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, item)"
>
{{ 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>
</template>
@ -28,7 +54,7 @@
export default {
name: 'DropdownMenu',
props: {
listItems: { type: Array, required: true },
listItems: { type: Array, default: () => [] },
position: { type: String, default: 'left' },
btnClasses: { type: String, default: 'btn btn-light' },
btnText: { type: String, required: false },
@ -38,11 +64,13 @@ export default {
data() {
return {
showMenu: false,
showSubmenu: null,
}
},
methods: {
closeMenu() {
this.showMenu = false;
this.showSubmenu = null;
},
handleClick(event, item) {
if (!item.url) {
@ -50,10 +78,10 @@ export default {
}
if (item.emit) {
this.$emit(item.emit)
this.$emit(item.emit, item.params)
}
this.showMenu = false;
this.closeMenu();
}
}
}