Skip to content

Commit fc77a48

Browse files
authored
Merge pull request #6065 from LibreSign/backport/6063/stable32
[stable32] Feat/improve signature template editor
2 parents 526dfbc + 0b204d6 commit fc77a48

2 files changed

Lines changed: 115 additions & 24 deletions

File tree

src/components/CodeEditor.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export default {
6161
tabSize: 4,
6262
indentWithTabs: true,
6363
placeholder: this.placeholder,
64+
viewportMargin: Infinity,
6465
}
6566
},
6667
},
@@ -90,7 +91,7 @@ export default {
9091
9192
.CodeMirror {
9293
height: auto;
93-
min-height: 200px;
94+
min-height: 80px;
9495
font-family: 'Courier New', Courier, monospace;
9596
font-size: 14px;
9697
line-height: 1.5;

src/views/Settings/SignatureStamp.vue

Lines changed: 113 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,27 +49,21 @@
4949
</fieldset>
5050
<div v-if="renderMode !== 'GRAPHIC_ONLY'">
5151
<div class="settings-section__row">
52-
<ul class="available-variables">
53-
<li v-for="(availableDescription, availableName) in availableVariables"
54-
:key="availableName"
55-
:class="{rtl: isRTLDirection}">
56-
<strong :class="{rtl: isRTLDirection}">{{ availableName }}:</strong>
57-
<span>{{ availableDescription }}</span>
58-
</li>
59-
</ul>
52+
<NcButton type="tertiary"
53+
:aria-label="t('libresign', 'Show available variables')"
54+
@click="showVariablesDialog = true">
55+
<template #icon>
56+
<HelpCircleOutline :size="20" />
57+
</template>
58+
{{ t('libresign', 'Available variables') }}
59+
</NcButton>
6060
</div>
6161
<div class="settings-section__row">
62-
<NcTextArea ref="textareaEditor"
63-
:value.sync="inputValue"
62+
<CodeEditor
63+
v-model="inputValue"
6464
:label="t('libresign', 'Signature text template')"
6565
:placeholder="t('libresign', 'Signature text template')"
66-
:spellcheck="false"
67-
:success="dislaySuccessTemplate"
68-
resize="vertical"
69-
@keydown.enter="saveTemplate"
70-
@blur="saveTemplate"
71-
@mousemove="resizeHeight"
72-
@keypress="resizeHeight" />
66+
@input="debouncedSaveTemplate" />
7367
<NcButton v-if="displayResetTemplate"
7468
type="tertiary"
7569
:aria-label="t('libresign', 'Reset to default')"
@@ -295,12 +289,45 @@
295289
<p>{{ t('libresign', 'If no background image or signature template is provided, no visible signature will be added to the document.') }}</p>
296290
</NcNoteCard>
297291
</div>
292+
293+
<NcDialog :name="t('libresign', 'Available template variables')"
294+
:open.sync="showVariablesDialog"
295+
size="normal">
296+
<div class="variables-dialog">
297+
<p class="variables-dialog__description">
298+
{{ t('libresign', 'Click on a variable to copy it to clipboard') }}
299+
</p>
300+
<div class="variables-list">
301+
<NcFormBoxButton v-for="(availableDescription, availableName) in availableVariables"
302+
:key="availableName"
303+
inverted-accent
304+
@click="copyToClipboard(getVariableText(availableName))">
305+
<template #default>
306+
<span class="hidden-visually">
307+
{{ t('libresign', 'Copy to clipboard') }}
308+
</span>
309+
{{ getVariableText(availableName) }}
310+
</template>
311+
<template #icon>
312+
<Check v-if="isCopied(availableName)" :size="20" />
313+
<ContentCopy v-else :size="20" />
314+
</template>
315+
<template #description>
316+
<p class="variable-description">{{ availableDescription }}</p>
317+
</template>
318+
</NcFormBoxButton>
319+
</div>
320+
</div>
321+
</NcDialog>
298322
</NcSettingsSection>
299323
</template>
300324
<script>
301325
import debounce from 'debounce'
302326
327+
import Check from 'vue-material-design-icons/Check.vue'
328+
import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
303329
import Delete from 'vue-material-design-icons/Delete.vue'
330+
import HelpCircleOutline from 'vue-material-design-icons/HelpCircleOutline.vue'
304331
import MagnifyMinusOutline from 'vue-material-design-icons/MagnifyMinusOutline.vue'
305332
import MagnifyPlusOutline from 'vue-material-design-icons/MagnifyPlusOutline.vue'
306333
import Undo from 'vue-material-design-icons/UndoVariant.vue'
@@ -315,25 +342,33 @@ import { generateOcsUrl } from '@nextcloud/router'
315342
316343
import NcButton from '@nextcloud/vue/components/NcButton'
317344
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
345+
import NcDialog from '@nextcloud/vue/components/NcDialog'
346+
import NcFormBoxButton from '@nextcloud/vue/components/NcFormBoxButton'
318347
import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
319348
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
320349
import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection'
321-
import NcTextArea from '@nextcloud/vue/components/NcTextArea'
322350
import NcTextField from '@nextcloud/vue/components/NcTextField'
323351
import { useIsDarkTheme } from '@nextcloud/vue/composables/useIsDarkTheme'
324352
353+
import CodeEditor from '../../components/CodeEditor.vue'
354+
325355
export default {
326356
name: 'SignatureStamp',
327357
components: {
358+
Check,
359+
CodeEditor,
360+
ContentCopy,
328361
Delete,
362+
HelpCircleOutline,
329363
MagnifyMinusOutline,
330364
MagnifyPlusOutline,
331365
NcButton,
332366
NcCheckboxRadioSwitch,
367+
NcDialog,
368+
NcFormBoxButton,
333369
NcLoadingIcon,
334370
NcNoteCard,
335371
NcSettingsSection,
336-
NcTextArea,
337372
NcTextField,
338373
Undo,
339374
Upload,
@@ -377,6 +412,8 @@ export default {
377412
isRTLDirection: isRTL(),
378413
availableVariables: loadState('libresign', 'signature_available_variables'),
379414
isOverflowing: false,
415+
showVariablesDialog: false,
416+
copiedVariable: null,
380417
}
381418
},
382419
computed: {
@@ -463,13 +500,40 @@ export default {
463500
},
464501
},
465502
mounted() {
466-
this.resizeHeight()
467503
subscribe('collect-metadata:changed', this.refreshAfterChangeCollectMetadata)
468504
},
505+
created() {
506+
this.debouncedSaveTemplate = debounce(this.saveTemplate, 500)
507+
},
469508
beforeUnmount() {
470509
unsubscribe('collect-metadata:changed')
471510
},
472511
methods: {
512+
getVariableText(name) {
513+
return name
514+
},
515+
isCopied(name) {
516+
return this.copiedVariable === this.getVariableText(name)
517+
},
518+
copyToClipboard(text) {
519+
if (this.copiedVariable === text) {
520+
return
521+
}
522+
523+
const value = text
524+
try {
525+
navigator.clipboard.writeText(value)
526+
} catch {
527+
// Fallback for a case when clipboard API is not available or permission denied
528+
// eslint-disable-next-line no-alert
529+
prompt('', value)
530+
}
531+
532+
this.copiedVariable = text
533+
setTimeout(() => {
534+
this.copiedVariable = null
535+
}, 2000)
536+
},
473537
reset() {
474538
this.dislaySuccessTemplate = false
475539
this.errorMessageBackground = ''
@@ -639,6 +703,13 @@ export default {
639703
display: flex;
640704
gap: 0 4px;
641705
align-items: flex-start;
706+
:deep(.code-editor) {
707+
flex: 1;
708+
.CodeMirror {
709+
height: auto;
710+
min-height: 80px;
711+
}
712+
}
642713
:deep(.textarea) {
643714
flex: 1;
644715
textarea {
@@ -709,12 +780,31 @@ export default {
709780
input[type="file"] {
710781
display: none;
711782
}
712-
.available-variables {
713-
margin-bottom: 1em;
714-
}
715783
.rtl {
716784
direction: rtl;
717785
text-align: right;
718786
}
719787
}
788+
789+
.variables-dialog {
790+
padding: 16px;
791+
792+
&__description {
793+
margin-bottom: 16px;
794+
color: var(--color-text-lighter);
795+
font-size: 14px;
796+
}
797+
}
798+
799+
.variables-list {
800+
display: flex;
801+
flex-direction: column;
802+
gap: 8px;
803+
max-height: 60vh;
804+
overflow-y: auto;
805+
}
806+
807+
.variable-description {
808+
margin: 0;
809+
}
720810
</style>

0 commit comments

Comments
 (0)