mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-12-26 01:35:34 +08:00
Merge pull request #4819 from scinote-eln/features/label-template-logo
Features/label template logo
This commit is contained in:
commit
d4b0f0da9d
10 changed files with 155 additions and 25 deletions
1
Gemfile
1
Gemfile
|
@ -97,6 +97,7 @@ gem 'devise-async',
|
|||
git: 'https://github.com/mhfs/devise-async.git',
|
||||
branch: 'devise-4.x'
|
||||
gem 'image_processing', '~> 1.12'
|
||||
gem 'img2zpl', git: 'https://github.com/scinote-eln/img2zpl'
|
||||
gem 'rufus-scheduler', '~> 3.5'
|
||||
|
||||
gem 'discard', '~> 1.0'
|
||||
|
|
|
@ -39,6 +39,13 @@ GIT
|
|||
devise-async (0.10.2)
|
||||
devise (>= 4.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/scinote-eln/img2zpl
|
||||
revision: 23d61cfc3e90ac4caa62dd08546fa0d7590a5140
|
||||
specs:
|
||||
img2zpl (1.0.1)
|
||||
mini_magick (~> 4.9)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
|
|
|
@ -139,7 +139,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
.inser-field-dropdown {
|
||||
.insert-field-dropdown {
|
||||
.dimensions-container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
img {
|
||||
margin-top: 27px;
|
||||
}
|
||||
}
|
||||
|
||||
.open-dropdown-button:not(.collapsed) {
|
||||
.fas {
|
||||
@include rotate(-180deg);
|
||||
|
@ -171,7 +180,12 @@
|
|||
display: flex;
|
||||
padding: 10px 10px 10px 24px;
|
||||
|
||||
.fas {
|
||||
.fas:not(.fa-plus-square) {
|
||||
margin-left: -1.25em;
|
||||
margin-right: .25em;
|
||||
}
|
||||
|
||||
.fa-plus-square {
|
||||
@include font-main;
|
||||
display: none;
|
||||
margin-left: auto;
|
||||
|
@ -180,7 +194,7 @@
|
|||
&:hover {
|
||||
background-color: $color-concrete;
|
||||
|
||||
.fas {
|
||||
.fa-plus-square {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<template>
|
||||
<div ref="modal" @keydown.esc="cancel" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-sm" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">
|
||||
{{ i18n.t('label_templates.show.insert_dropdown.logo_modal.title') }}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{ i18n.t('label_templates.show.insert_dropdown.logo_modal.description') }}</p>
|
||||
<div class="dimensions-container">
|
||||
<div class="sci-input-container">
|
||||
<label>{{ i18n.t('label_templates.show.insert_dropdown.logo_modal.width', {unit: unit}) }}</label>
|
||||
<input type="number" min="0" v-model="width" class="sci-input-field" @change="updateHeight">
|
||||
</div>
|
||||
<img src="/images/icon_small/link.svg"/>
|
||||
<div class="sci-input-container">
|
||||
<label>{{ i18n.t('label_templates.show.insert_dropdown.logo_modal.height', {unit: unit}) }}</label>
|
||||
<input type="number" min="0" v-model="height" class="sci-input-field" @change="updateWidth">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @click="cancel">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" @click="confirm">{{ i18n.t('label_templates.show.insert_dropdown.logo_modal.insert') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'logoInsertModal',
|
||||
props: {
|
||||
unit: { type: String, required: true },
|
||||
dimension: { type: Array, required: true }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
ratio: 1
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.$emit('cancel');
|
||||
});
|
||||
this.width = this.dimension[0]
|
||||
this.height = this.dimension[1]
|
||||
this.ratio = this.dimension[0] / this.dimension[1]
|
||||
},
|
||||
methods: {
|
||||
updateHeight() {
|
||||
this.height = Math.round(this.width * 10 / this.ratio) / 10
|
||||
},
|
||||
updateWidth() {
|
||||
this.width = Math.round(this.height * this.ratio * 10) / 10
|
||||
},
|
||||
confirm() {
|
||||
this.$emit('insert:tag', {tag: `{{LOGO, ${this.unit}, ${this.width}, ${this.height}}}`});
|
||||
$(this.$refs.modal).modal('hide');
|
||||
},
|
||||
cancel() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -59,7 +59,7 @@
|
|||
<InsertFieldDropdown
|
||||
v-if="editingContent"
|
||||
:labelTemplate="labelTemplate"
|
||||
@insertField="insertField"
|
||||
@insertTag="insertTag"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="editingContent">
|
||||
|
@ -293,12 +293,12 @@
|
|||
saveCursorPosition() {
|
||||
this.cursorPos = $(this.$refs.contentInput).prop('selectionStart');
|
||||
},
|
||||
insertField(field) {
|
||||
insertTag(tag) {
|
||||
this.enableContentEdit();
|
||||
let textBefore = this.newContent.substring(0, this.cursorPos);
|
||||
let textAfter = this.newContent.substring(this.cursorPos, this.newContent.length);
|
||||
this.newContent = textBefore + field + textAfter;
|
||||
this.cursorPos = this.cursorPos + field.length;
|
||||
this.newContent = textBefore + tag + textAfter;
|
||||
this.cursorPos = this.cursorPos + tag.length;
|
||||
|
||||
this.$nextTick(() => {
|
||||
$(this.$refs.contentInput).prop('selectionStart', this.cursorPos);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div ref="dropdown" class="inser-field-dropdown dropdown">
|
||||
<div ref="dropdown" class="insert-field-dropdown dropdown">
|
||||
<a class="open-dropdown-button collapsed" role="button" data-toggle="dropdown" id="fieldsContainer" aria-expanded="false">
|
||||
{{ i18n.t('label_templates.show.insert_dropdown.button') }}
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
|
@ -22,7 +22,7 @@
|
|||
:data-template="tooltipTemplate"
|
||||
class="field-element"
|
||||
:title="i18n.t('label_templates.show.insert_dropdown.field_code', {code: field.tag})"
|
||||
@click="$emit('insertField', field.tag)"
|
||||
@click="insertTag(field)"
|
||||
>
|
||||
{{ field.key }}
|
||||
<i class="fas fa-plus-square"></i>
|
||||
|
@ -36,8 +36,9 @@
|
|||
:data-template="tooltipTemplate"
|
||||
class="field-element"
|
||||
:title="i18n.t('label_templates.show.insert_dropdown.field_code', {code: field.tag})"
|
||||
@click="$emit('insertField', field.tag)"
|
||||
@click="insertTag(field)"
|
||||
>
|
||||
<i v-if="field.icon" :class="field.icon"></i>
|
||||
{{ field.key }}
|
||||
<i class="fas fa-plus-square"></i>
|
||||
</div>
|
||||
|
@ -51,7 +52,7 @@
|
|||
:data-template="tooltipTemplate"
|
||||
class="field-element"
|
||||
:title="i18n.t('label_templates.show.insert_dropdown.field_code', {code: field.tag})"
|
||||
@click="$emit('insertField', field.tag)"
|
||||
@click="insertTag(field)"
|
||||
>
|
||||
{{ field.key }}
|
||||
<i class="fas fa-plus-square"></i>
|
||||
|
@ -62,10 +63,17 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<LogoInsertModal v-if="openLogoModal"
|
||||
:unit="labelTemplate.attributes.unit"
|
||||
:dimension="logoDimension"
|
||||
@insert:tag="insertTag"
|
||||
@cancel="openLogoModal = false"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LogoInsertModal from './components/logo_insert_modal.vue'
|
||||
|
||||
export default {
|
||||
name: 'InsertFieldDropdown',
|
||||
props: {
|
||||
|
@ -81,9 +89,12 @@
|
|||
common: [],
|
||||
repositories: []
|
||||
},
|
||||
openLogoModal: false,
|
||||
logoDimension: null,
|
||||
searchValue: ''
|
||||
}
|
||||
},
|
||||
components: {LogoInsertModal},
|
||||
computed: {
|
||||
tooltipTemplate() {
|
||||
return `<div class="tooltip" role="tooltip">
|
||||
|
@ -135,6 +146,14 @@
|
|||
});
|
||||
},
|
||||
methods: {
|
||||
insertTag(field) {
|
||||
if (field.id == 'logo') {
|
||||
this.logoDimension = field.dimension
|
||||
this.openLogoModal = true
|
||||
return
|
||||
}
|
||||
this.$emit('insertTag', field.tag)
|
||||
},
|
||||
filterArray(array, key) {
|
||||
return array.filter(field => field[key].toLowerCase().indexOf(this.searchValue.toLowerCase()) !== -1)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module LabelTemplates
|
||||
class ColumnNotFoundError < StandardError; end
|
||||
class LogoNotFoundError < StandardError; end
|
||||
class LogoParamsError < StandardError; end
|
||||
|
||||
class RepositoryRowService
|
||||
class UnsupportedKeyError < StandardError; end
|
||||
|
||||
class ColumnNotFoundError < StandardError; end
|
||||
|
||||
class LogoNotFoundError < StandardError; end
|
||||
|
||||
MAX_PRINTABLE_ITEM_NAME_LENGTH = 64
|
||||
|
||||
|
@ -21,7 +19,9 @@ module LabelTemplates
|
|||
keys = @label_template.content.scan(/(?<=\{\{).*?(?=\}\})/).uniq
|
||||
label = keys.reduce(@label_template.content.dup) do |rendered_content, key|
|
||||
rendered_content.gsub!(/\{\{#{key}\}\}/, fetch_value(key))
|
||||
rescue ColumnNotFoundError, LogoNotFoundError => e
|
||||
rescue LabelTemplates::ColumnNotFoundError,
|
||||
LabelTemplates::LogoNotFoundError,
|
||||
LabelTemplates::LogoParamsError => e
|
||||
errors.push(e)
|
||||
rendered_content
|
||||
end
|
||||
|
@ -49,7 +49,7 @@ module LabelTemplates
|
|||
when /^c_(.*)/
|
||||
name = Regexp.last_match(1)
|
||||
unless @repository_columns.include?(name)
|
||||
raise ColumnNotFoundError, I18n.t('label_templates.repository_row.errors.column_not_found')
|
||||
raise LabelTemplates::ColumnNotFoundError, I18n.t('label_templates.repository_row.errors.column_not_found')
|
||||
end
|
||||
|
||||
fetch_custom_column_value(name)
|
||||
|
@ -61,15 +61,15 @@ module LabelTemplates
|
|||
@repository_row.created_by.full_name
|
||||
when 'ADDED_ON'
|
||||
I18n.l(@repository_row.created_at, format: :full)
|
||||
when 'LOGO'
|
||||
logo
|
||||
when /^LOGO/
|
||||
logo(key)
|
||||
else
|
||||
raise ColumnNotFoundError, I18n.t('label_templates.repository_row.errors.column_not_found')
|
||||
raise LabelTemplates::ColumnNotFoundError, I18n.t('label_templates.repository_row.errors.column_not_found')
|
||||
end
|
||||
end
|
||||
|
||||
def logo
|
||||
raise LogoNotFoundError, I18n.t('label_templates.repository_row.errors.logo_not_supported')
|
||||
def logo(_key)
|
||||
raise LabelTemplates::LogoNotFoundError, I18n.t('label_templates.repository_row.errors.logo_not_supported')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,13 +16,17 @@ module LabelTemplates
|
|||
def tags
|
||||
{
|
||||
default: DEFAULT_COLUMNS,
|
||||
common: repository_tags.pluck(:tags).reduce(:&),
|
||||
common: common_columns + repository_tags.pluck(:tags).reduce(:&),
|
||||
repositories: repository_tags
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def common_columns
|
||||
@common_columns ||= []
|
||||
end
|
||||
|
||||
def repository_tags
|
||||
@repository_tags ||=
|
||||
Repository.includes(:repository_columns).active.where(team: @team).map do |repository|
|
||||
|
|
|
@ -897,6 +897,12 @@ en:
|
|||
nothing_found: 'Nothing found…'
|
||||
field_code: 'Field code: %{code}'
|
||||
search_placeholder: 'Type to search…'
|
||||
logo_modal:
|
||||
title: 'Insert logo into the label'
|
||||
description: 'Please specify width or height, another dimension will adjust proportionally'
|
||||
width: 'Width (%{unit})'
|
||||
height: 'Height (%{unit})'
|
||||
insert: 'Insert'
|
||||
buttons:
|
||||
refresh: 'Refresh preview'
|
||||
save: 'Save template code'
|
||||
|
|
7
public/images/icon_small/link.svg
Normal file
7
public/images/icon_small/link.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 11H6V12H5V11Z" fill="#A0A0A8"/>
|
||||
<path d="M10 11H14V12H10V11Z" fill="#A0A0A8"/>
|
||||
<path d="M13 10H14.5C15.3284 10 16 10.6716 16 11.5C16 12.3284 15.3284 13 14.5 13H13V14H14.5C15.8807 14 17 12.8807 17 11.5C17 10.1193 15.8807 9 14.5 9H13V10Z" fill="#A0A0A8"/>
|
||||
<path d="M11 10V9H9.5C8.11929 9 7 10.1193 7 11.5C7 12.8807 8.11929 14 9.5 14H11V13H9.5C8.67157 13 8 12.3284 8 11.5C8 10.6716 8.67157 10 9.5 10H11Z" fill="#A0A0A8"/>
|
||||
<path d="M18 11H19V12H18V11Z" fill="#A0A0A8"/>
|
||||
</svg>
|
After Width: | Height: | Size: 581 B |
Loading…
Reference in a new issue