Merge pull request #4819 from scinote-eln/features/label-template-logo

Features/label template logo
This commit is contained in:
Alex Kriuchykhin 2023-01-13 11:58:01 +01:00 committed by GitHub
commit d4b0f0da9d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 155 additions and 25 deletions

View file

@ -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'

View file

@ -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:

View file

@ -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;
}
}

View file

@ -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">&times;</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>

View file

@ -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);

View file

@ -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)
}

View file

@ -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

View file

@ -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|

View file

@ -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'

View 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