Skip to content

Commit 6628719

Browse files
crcatalaAkryum
authored andcommitted
feat: Add dropdown menu to DataField with Copy Value (#583)
* Add context menu to DataField Refs #442 * Adds more actions to dropdown * Currently adds one item - Copy Value (to clipboard) * feat: refactor to VueDropdown + auto close delay
1 parent 6757dd2 commit 6628719

File tree

3 files changed

+93
-22
lines changed

3 files changed

+93
-22
lines changed

src/devtools/components/DataField.vue

Lines changed: 81 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@
88
hide: 0
99
}"
1010
:open-group="'id' + _uid"
11+
:class="{
12+
'force-toolbar': contextMenuOpen || editing,
13+
}"
1114
class="self"
1215
popover-class="force-tooltip"
1316
trigger="hover"
1417
placement="left"
1518
offset="24"
1619
@click.native="onClick"
20+
@mouseenter.native="onContextMenuMouseEnter"
21+
@mouseleave.native="onContextMenuMouseLeave"
1722
>
1823
<span
1924
v-show="isExpandableType"
@@ -58,20 +63,20 @@
5863
<VueIcon
5964
v-tooltip="editErrorMessage"
6065
v-if="!editValid"
61-
class="icon-button warning"
66+
class="small-icon warning"
6267
icon="warning"
6368
/>
6469
<template v-else>
65-
<VueIcon
70+
<VueButton
6671
v-tooltip="$t('DataField.edit.cancel.tooltip')"
67-
class="icon-button medium"
68-
icon="cancel"
72+
class="icon-button flat"
73+
icon-left="cancel"
6974
@click="cancelEdit()"
7075
/>
71-
<VueIcon
76+
<VueButton
7277
v-tooltip="$t('DataField.edit.submit.tooltip')"
73-
class="icon-button"
74-
icon="save"
78+
class="icon-button flat"
79+
icon-left="save"
7580
@click="submitEdit()"
7681
/>
7782
</template>
@@ -86,38 +91,62 @@
8691
v-html="formattedValue"
8792
/>
8893
<span class="actions">
89-
<VueIcon
94+
<VueButton
9095
v-tooltip="'Edit value'"
9196
v-if="isValueEditable"
92-
class="edit-value icon-button"
93-
icon="edit"
97+
class="edit-value icon-button flat"
98+
icon-left="edit"
9499
@click="openEdit()"
95100
/>
96101
<template v-if="quickEdits">
97-
<VueIcon
102+
<VueButton
98103
v-tooltip="info.title || 'Quick edit'"
99104
v-for="(info, index) of quickEdits"
100105
:key="index"
101106
:class="info.class"
102-
:icon="info.icon"
107+
:icon-left="info.icon"
103108
class="quick-edit icon-button"
104109
@click="quickEdit(info, $event)"
105110
/>
106111
</template>
107-
<VueIcon
112+
<VueButton
108113
v-tooltip="'Add new value'"
109114
v-if="isSubfieldsEditable && !addingValue"
110-
class="add-value icon-button"
111-
icon="add_circle"
115+
class="add-value icon-button flat"
116+
icon-left="add_circle"
112117
@click="addNewValue()"
113118
/>
114-
<VueIcon
119+
<VueButton
115120
v-tooltip="'Remove value'"
116121
v-if="removable"
117-
class="remove-field icon-button"
118-
icon="delete"
122+
class="remove-field icon-button flat"
123+
icon-left="delete"
119124
@click="removeField()"
120125
/>
126+
127+
<!-- Context menu -->
128+
<VueDropdown
129+
:open.sync="contextMenuOpen"
130+
>
131+
<VueButton
132+
slot="trigger"
133+
icon-left="more_vert"
134+
class="icon-button flat"
135+
/>
136+
137+
<div
138+
class="context-menu-dropdown"
139+
@mouseenter="onContextMenuMouseEnter"
140+
@mouseleave="onContextMenuMouseLeave"
141+
>
142+
<VueDropdownButton
143+
icon-left="flip_to_front"
144+
@click="copyToClipboard"
145+
>
146+
{{ $t('DataField.contextMenu.copyValue') }}
147+
</VueDropdownButton>
148+
</div>
149+
</VueDropdown>
121150
</span>
122151
</template>
123152

@@ -186,7 +215,8 @@ import {
186215
sortByKey,
187216
openInEditor,
188217
escape,
189-
specialTokenToString
218+
specialTokenToString,
219+
copyToClipboard
190220
} from 'src/util'
191221
192222
import DataFieldEdit from '../mixins/data-field-edit'
@@ -228,6 +258,7 @@ export default {
228258
229259
data () {
230260
return {
261+
contextMenuOpen: false,
231262
limit: Array.isArray(this.field.value) ? 10 : Infinity,
232263
expanded: this.depth === 0 && this.field.key !== '$route' && (subFieldCount(this.field.value) < 5)
233264
}
@@ -399,6 +430,10 @@ export default {
399430
},
400431
401432
methods: {
433+
copyToClipboard () {
434+
copyToClipboard(this.field.value)
435+
},
436+
402437
onClick (event) {
403438
// Cancel if target is interactive
404439
if (event.target.tagName === 'INPUT' || event.target.className.includes('button')) {
@@ -422,7 +457,18 @@ export default {
422457
}
423458
},
424459
425-
hyphen: v => v.replace(/\s/g, '-')
460+
hyphen: v => v.replace(/\s/g, '-'),
461+
462+
onContextMenuMouseEnter () {
463+
clearTimeout(this.$_contextMenuTimer)
464+
},
465+
466+
onContextMenuMouseLeave () {
467+
clearTimeout(this.$_contextMenuTimer)
468+
this.$_contextMenuTimer = setTimeout(() => {
469+
this.contextMenuOpen = false
470+
}, 4000)
471+
}
426472
}
427473
}
428474
</script>
@@ -460,16 +506,20 @@ export default {
460506
top -1px
461507
.icon-button
462508
user-select none
463-
width 16px
509+
width 20px
464510
height @width
465511
&:first-child
466512
margin-left 6px
467513
&:not(:last-child)
468514
margin-right 6px
515+
.icon-button >>> .vue-ui-icon,
516+
.small-icon
517+
width 16px
518+
height @width
469519
.warning >>> svg
470520
fill $orange
471521
&:hover,
472-
&.editing
522+
&.force-toolbar
473523
.actions
474524
visibility visible
475525
.colon
@@ -499,6 +549,10 @@ export default {
499549
.vue-ui-dark-mode &
500550
color: #242424
501551
552+
.edit-overlay
553+
display inline-flex
554+
align-items center
555+
502556
.key
503557
color #881391
504558
.vue-ui-dark-mode &
@@ -599,4 +653,9 @@ export default {
599653
600654
.remove-field
601655
margin-left 10px
656+
657+
.context-menu-dropdown
658+
.vue-ui-button
659+
display block
660+
width 100%
602661
</style>

src/devtools/locales/en.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ export default {
2727
tooltip: '[[{{keys.enter}}]] Submit change'
2828
}
2929
},
30+
contextMenu: {
31+
copyValue: 'Copy Value'
32+
},
3033
quickEdit: {
3134
number: {
3235
tooltip: `Quick Edit<br><br>

src/util.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,3 +546,12 @@ export function escape (s) {
546546
function escapeChar (a) {
547547
return ESC[a] || a
548548
}
549+
550+
export function copyToClipboard (state) {
551+
const dummyTextArea = document.createElement('textarea')
552+
dummyTextArea.textContent = stringify(state)
553+
document.body.appendChild(dummyTextArea)
554+
dummyTextArea.select()
555+
document.execCommand('copy')
556+
document.body.removeChild(dummyTextArea)
557+
}

0 commit comments

Comments
 (0)