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')"
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 >
301325import debounce from ' debounce'
302326
327+ import Check from ' vue-material-design-icons/Check.vue'
328+ import ContentCopy from ' vue-material-design-icons/ContentCopy.vue'
303329import Delete from ' vue-material-design-icons/Delete.vue'
330+ import HelpCircleOutline from ' vue-material-design-icons/HelpCircleOutline.vue'
304331import MagnifyMinusOutline from ' vue-material-design-icons/MagnifyMinusOutline.vue'
305332import MagnifyPlusOutline from ' vue-material-design-icons/MagnifyPlusOutline.vue'
306333import Undo from ' vue-material-design-icons/UndoVariant.vue'
@@ -315,25 +342,33 @@ import { generateOcsUrl } from '@nextcloud/router'
315342
316343import NcButton from ' @nextcloud/vue/components/NcButton'
317344import NcCheckboxRadioSwitch from ' @nextcloud/vue/components/NcCheckboxRadioSwitch'
345+ import NcDialog from ' @nextcloud/vue/components/NcDialog'
346+ import NcFormBoxButton from ' @nextcloud/vue/components/NcFormBoxButton'
318347import NcLoadingIcon from ' @nextcloud/vue/components/NcLoadingIcon'
319348import NcNoteCard from ' @nextcloud/vue/components/NcNoteCard'
320349import NcSettingsSection from ' @nextcloud/vue/components/NcSettingsSection'
321- import NcTextArea from ' @nextcloud/vue/components/NcTextArea'
322350import NcTextField from ' @nextcloud/vue/components/NcTextField'
323351import { useIsDarkTheme } from ' @nextcloud/vue/composables/useIsDarkTheme'
324352
353+ import CodeEditor from ' ../../components/CodeEditor.vue'
354+
325355export 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