Merge pull request #6540 from scinote-eln/master

Merge master
This commit is contained in:
Martin Artnik 2023-10-26 15:35:23 +02:00 committed by GitHub
commit 88b8498dc2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 152 additions and 38 deletions

View file

@ -1 +1 @@
1.29.1
1.29.1.1

View file

@ -1,7 +1,7 @@
@layer components {
.sci-label {
@apply text-sm font-medium text-sn-grey;
@apply text-sm font-medium text-sn-dark-grey;
}
.sci-input-container-v2 {

View file

@ -210,6 +210,14 @@
@publish="publishProtocol"
@cancel="closePublishModal"
/>
<clipboardPasteModal v-if="showClipboardPasteModal"
:image="pasteImages"
:objects="steps"
:objectType="'step'"
:selectedObjectId="firstObjectInViewport()"
@files="uploadFilesToStep"
@cancel="showClipboardPasteModal = false"
/>
</div>
</template>
@ -221,6 +229,8 @@
import Tinymce from '../shared/tinymce.vue'
import ReorderableItemsModal from '../shared/reorderable_items_modal.vue'
import PublishProtocol from './modals/publish_protocol.vue'
import clipboardPasteModal from '../shared/content/attachments/clipboard_paste_modal.vue'
import AssetPasteMixin from '../shared/content/attachments/mixins/paste.js'
import UtilsMixin from '../mixins/utils.js'
import stackableHeadersMixin from '../mixins/stackableHeadersMixin';
@ -234,8 +244,8 @@
required: true
}
},
components: { Step, InlineEdit, ProtocolOptions, Tinymce, ReorderableItemsModal, ProtocolMetadata, PublishProtocol},
mixins: [UtilsMixin, stackableHeadersMixin, moduleNameObserver],
components: { Step, InlineEdit, ProtocolOptions, Tinymce, ReorderableItemsModal, ProtocolMetadata, PublishProtocol, clipboardPasteModal},
mixins: [UtilsMixin, stackableHeadersMixin, moduleNameObserver, AssetPasteMixin],
computed: {
inRepository() {
return this.protocol.attributes.in_repository
@ -245,7 +255,7 @@
},
urls() {
return this.protocol.attributes.urls || {}
}
},
},
data() {
return {
@ -433,6 +443,16 @@
$('.my_module-name .view-mode').trigger('click');
$('.my_module-name .input-field').focus();
}, 300)
},
uploadFilesToStep(file, stepId) {
this.$children.find(child => child.step?.id == stepId).uploadFiles(file);
},
firstObjectInViewport() {
let step = $('.step-container:not(.locked)').toArray().find(element => {
const { top, bottom } = element.getBoundingClientRect()
return bottom > 0 && top < window.innerHeight
})
return step ? step.dataset.id : null
}
}
}

View file

@ -4,7 +4,8 @@
@drop.prevent="dropFile"
@dragenter.prevent="dragEnter($event)"
@dragover.prevent
:class="{ 'draging-file': dragingFile, 'editing-name': editingName }"
:data-id="step.id"
:class="{ 'draging-file': dragingFile, 'editing-name': editingName, 'locked': !urls.update_url }"
>
<div class="drop-message" @dragleave.prevent="!showFileModal ? dragingFile = false : null">
{{ i18n.t('protocols.steps.drop_message', { position: step.attributes.position + 1 }) }}
@ -134,12 +135,6 @@
</div>
</div>
<deleteStepModal v-if="confirmingDelete" @confirm="deleteStep" @cancel="closeDeleteModal"/>
<clipboardPasteModal v-if="showClipboardPasteModal"
:parent="step"
:image="pasteImages"
@files="uploadFiles"
@cancel="showClipboardPasteModal = false"
/>
<ReorderableItemsModal v-if="reordering"
:title="i18n.t('protocols.steps.modals.reorder_elements.title', { step_position: step.attributes.position + 1 })"
:items="reorderableElements"
@ -162,7 +157,6 @@
import Checklist from '../shared/content/checklist.vue'
import deleteStepModal from './modals/delete_step.vue'
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'
@ -202,7 +196,6 @@
attachmentsReady: false,
confirmingDelete: false,
showFileModal: false,
showClipboardPasteModal: false,
showCommentsSidebar: false,
dragingFile: false,
reordering: false,
@ -226,7 +219,6 @@
StepText,
Checklist,
deleteStepModal,
clipboardPasteModal,
Attachments,
StorageUsage,
ReorderableItemsModal,
@ -542,10 +534,6 @@
rect.right <= (window.innerWidth || $(window).width())
);
},
copyPasteImageModal(pasteImages) {
this.pasteImages = pasteImages;
this.showClipboardPasteModal = true;
},
insertElement(element) {
let position = element.attributes.position;
this.elements = this.elements.map( s => {

View file

@ -3,7 +3,8 @@
@drop.prevent="dropFile"
@dragenter.prevent="dragEnter($event)"
@dragover.prevent
:class="{ 'bg-sn-super-light-blue': dragingFile, 'bg-white': !dragingFile }"
:data-id="result.id"
:class="{ 'bg-sn-super-light-blue': dragingFile, 'bg-white': !dragingFile, 'locked': locked }"
>
<div class="text-xl items-center flex flex-col text-sn-blue h-full justify-center left-0 absolute top-0 w-full"
v-if="dragingFile"

View file

@ -32,6 +32,14 @@
@result:drag_enter="dragEnter"
/>
</div>
<clipboardPasteModal v-if="showClipboardPasteModal"
:image="pasteImages"
:objects="results"
:objectType="'result'"
:selectedObjectId="firstObjectInViewport()"
@files="uploadFilesToResult"
@cancel="showClipboardPasteModal = false"
/>
</div>
</template>
@ -43,10 +51,13 @@
import stackableHeadersMixin from '../mixins/stackableHeadersMixin';
import moduleNameObserver from '../mixins/moduleNameObserver';
import clipboardPasteModal from '../shared/content/attachments/clipboard_paste_modal.vue'
import AssetPasteMixin from '../shared/content/attachments/mixins/paste.js'
export default {
name: 'Results',
components: { ResultsToolbar, Result },
mixins: [stackableHeadersMixin, moduleNameObserver],
components: { ResultsToolbar, Result, clipboardPasteModal },
mixins: [stackableHeadersMixin, moduleNameObserver, AssetPasteMixin],
props: {
url: { type: String, required: true },
canCreate: { type: String, required: true },
@ -136,6 +147,16 @@
},
dragEnter(id) {
this.activeDragResult = id;
},
uploadFilesToResult(file, resultId) {
this.$children.find(child => child.result?.id == resultId).uploadFiles(file);
},
firstObjectInViewport() {
let result = $('.result-wrapper:not(.locked)').toArray().find(element => {
const { top, bottom } = element.getBoundingClientRect()
return bottom > 0 && top < window.innerHeight
})
return result ? result.dataset.id : null
}
}
}

View file

@ -12,29 +12,68 @@
<h4 class="modal-title">{{i18n.t('assets.from_clipboard.modal_title')}}</h4>
</div>
<div class="modal-body">
<p><strong>{{i18n.t('assets.from_clipboard.image_preview')}}</strong></p>
<canvas style="border:1px solid grey;max-width:400px;max-height:300px" id="clipboardPreview" />
<p><strong>{{i18n.t('assets.from_clipboard.file_name')}}</strong></p>
<div class="input-group">
<input id="clipboardImageName" type="text" class="form-control"
<label class="sci-label">{{i18n.t('assets.from_clipboard.image_preview')}}</label>
<div class="flex justify-center w-full">
<canvas class="max-h-80 max-w-lg rounded border border-solid border-sn-light-grey" ref="preview" />
</div>
<div class="w-full py-6">
<label class="sci-label">{{i18n.t(`assets.from_clipboard.select_${objectType}`)}}</label>
<SelectSearch
:value="target"
@change="setTarget"
:options="targets"
:isLoading="false"
:placeholder="
i18n.t(`protocols.steps.modals.move_element.${objectType}.search_placeholder`)
"
:searchPlaceholder="
i18n.t(`protocols.steps.modals.move_element.${objectType}.search_placeholder`)
"
/>
</div>
<label class="sci-label">{{i18n.t('assets.from_clipboard.file_name')}}</label>
<div class="sci-input-container-v2">
<input id="clipboardImageName" type="text" class="sci-input-field !pr-16" v-model="fileName"
:placeholder="i18n.t('assets.from_clipboard.file_name_placeholder')" aria-describedby="image-name">
<span class="input-group-addon" id="image-name"></span>
<span class="absolute right-2.5 text-sn-grey ">
.{{ extension }}
</span>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="cancel">{{i18n.t('general.cancel')}}</button>
<button type="button" class="btn btn-success" @click="uploadImage">{{i18n.t('assets.from_clipboard.add_image')}}</button>
<button type="button" class="btn btn-success" :disabled="!valid" @click="uploadImage">{{i18n.t('assets.from_clipboard.add_image')}}</button>
</div>
</div>
</div>
</div>
</template>
<script>
import SelectSearch from "../../select_search.vue";
export default {
name: 'clipboardPasteModal',
props: {
parent: Object,
image: DataTransferItem
objects: Array,
image: DataTransferItem,
objectType: String,
selectedObjectId: String
},
data () {
return {
target: null,
targets: [],
fileName: '',
extension: '',
}
},
components: {
SelectSearch
},
computed: {
valid() {
return this.target && this.fileName.length > 0;
}
},
mounted() {
$(this.$refs.modal).modal('show');
@ -42,15 +81,26 @@
$(this.$refs.modal).on('hidden.bs.modal', () => {
this.$emit('cancel');
});
if (this.selectedObjectId) this.target = this.selectedObjectId;
this.targets = this.objects.map((object) => {
return [
object.id,
object.attributes.name
]
});
},
methods: {
setTarget(target) {
this.target = target;
},
cancel() {
$(this.$refs.modal).modal('hide');
},
appendImage(item) {
let imageBlob = item.getAsFile();
if (imageBlob) {
var canvas = document.getElementById('clipboardPreview');
var canvas = this.$refs.preview;
var ctx = canvas.getContext('2d');
var img = new Image();
img.onload = function() {
@ -63,12 +113,12 @@
let extension = imageBlob.name.slice(
(Math.max(0, imageBlob.name.lastIndexOf('.')) || Infinity) + 1
);
$('#image-name').html('.' + extension); // add extension near file name
this.extension = extension;
this.imageBlob = imageBlob;
}
},
uploadImage() {
let newName = $('#clipboardImageName').val();
let newName = this.fileName;
let imageBlog = this.imageBlob;
// check if the name is set
if (newName && newName.length > 0) {
@ -82,7 +132,7 @@
this.imageBlob = new File([blob], name, { type: imageBlog.type });
}
$(this.$refs.modal).modal('hide');
this.$emit('files', this.imageBlob);
this.$emit('files', this.imageBlob, this.target);
}
}
}

View file

@ -0,0 +1,32 @@
export default {
data() {
return {
showClipboardPasteModal: false,
pasteImages: null,
};
},
mounted() {
document.addEventListener('paste', this.handlePaste);
},
unmounted() {
document.removeEventListener('paste', this.handlePaste);
},
methods: {
handlePaste(e) {
if ( e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' ) return;
this.pasteImages = this.getImagesFromClipboard(e);
if (this.pasteImages && this.firstObjectInViewport()) this.showClipboardPasteModal = true;
},
getImagesFromClipboard(e) {
let image = null;
if (e.clipboardData) {
for (let i = 0; i < e.clipboardData.items.length; i++) {
if (e.clipboardData.items[i].type.indexOf('image') !== -1) {
image = e.clipboardData.items[i];
}
}
}
return image;
},
},
};

View file

@ -3454,9 +3454,11 @@ en:
drop_label: 'Drop to add to Step'
file_label: 'File'
from_clipboard:
modal_title: 'Add image from clipboard'
image_preview: 'Image preview'
add_image: 'Add'
modal_title: 'Insert image from clipboard'
image_preview: 'Pasted image preview'
select_step: 'Select to which step youd like to attach image'
select_result: 'Select to which result youd like to attach image'
add_image: 'Insert'
file_name: 'File name'
file_name_placeholder: 'Image'
placeholder: