diff --git a/src/devtools/components/DataField.vue b/src/devtools/components/DataField.vue index ad67e06ac..684b47640 100644 --- a/src/devtools/components/DataField.vue +++ b/src/devtools/components/DataField.vue @@ -8,12 +8,17 @@ hide: 0 }" :open-group="'id' + _uid" + :class="{ + 'force-toolbar': contextMenuOpen || editing, + }" class="self" popover-class="force-tooltip" trigger="hover" placement="left" offset="24" @click.native="onClick" + @mouseenter.native="onContextMenuMouseEnter" + @mouseleave.native="onContextMenuMouseLeave" > @@ -86,38 +91,62 @@ v-html="formattedValue" /> - - - + + + + + +
+ + {{ $t('DataField.contextMenu.copyValue') }} + +
+
@@ -186,7 +215,8 @@ import { sortByKey, openInEditor, escape, - specialTokenToString + specialTokenToString, + copyToClipboard } from 'src/util' import DataFieldEdit from '../mixins/data-field-edit' @@ -228,6 +258,7 @@ export default { data () { return { + contextMenuOpen: false, limit: Array.isArray(this.field.value) ? 10 : Infinity, expanded: this.depth === 0 && this.field.key !== '$route' && (subFieldCount(this.field.value) < 5) } @@ -399,6 +430,10 @@ export default { }, methods: { + copyToClipboard () { + copyToClipboard(this.field.value) + }, + onClick (event) { // Cancel if target is interactive if (event.target.tagName === 'INPUT' || event.target.className.includes('button')) { @@ -422,7 +457,18 @@ export default { } }, - hyphen: v => v.replace(/\s/g, '-') + hyphen: v => v.replace(/\s/g, '-'), + + onContextMenuMouseEnter () { + clearTimeout(this.$_contextMenuTimer) + }, + + onContextMenuMouseLeave () { + clearTimeout(this.$_contextMenuTimer) + this.$_contextMenuTimer = setTimeout(() => { + this.contextMenuOpen = false + }, 4000) + } } } @@ -460,16 +506,20 @@ export default { top -1px .icon-button user-select none - width 16px + width 20px height @width &:first-child margin-left 6px &:not(:last-child) margin-right 6px + .icon-button >>> .vue-ui-icon, + .small-icon + width 16px + height @width .warning >>> svg fill $orange &:hover, - &.editing + &.force-toolbar .actions visibility visible .colon @@ -499,6 +549,10 @@ export default { .vue-ui-dark-mode & color: #242424 + .edit-overlay + display inline-flex + align-items center + .key color #881391 .vue-ui-dark-mode & @@ -599,4 +653,9 @@ export default { .remove-field margin-left 10px + +.context-menu-dropdown + .vue-ui-button + display block + width 100% diff --git a/src/devtools/locales/en.js b/src/devtools/locales/en.js index 6663d463f..988ae1937 100644 --- a/src/devtools/locales/en.js +++ b/src/devtools/locales/en.js @@ -27,6 +27,9 @@ export default { tooltip: '[[{{keys.enter}}]] Submit change' } }, + contextMenu: { + copyValue: 'Copy Value' + }, quickEdit: { number: { tooltip: `Quick Edit

diff --git a/src/util.js b/src/util.js index bb4d9d5bc..3c3266c3a 100644 --- a/src/util.js +++ b/src/util.js @@ -546,3 +546,12 @@ export function escape (s) { function escapeChar (a) { return ESC[a] || a } + +export function copyToClipboard (state) { + const dummyTextArea = document.createElement('textarea') + dummyTextArea.textContent = stringify(state) + document.body.appendChild(dummyTextArea) + dummyTextArea.select() + document.execCommand('copy') + document.body.removeChild(dummyTextArea) +}