Skip to content

New keyboard shortcuts + tooltips refactoring #519

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Jan 22, 2018
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 42 additions & 8 deletions src/devtools/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,39 @@
<span class="message" :key="message">{{ message }}</span>
</transition>
</span>
<a class="button components"
<a
class="button components"
:class="{ active: tab === 'components'}"
v-tooltip="$t('App.components.tooltip')"
@click="switchTab('components')"
v-tooltip="'Switch to Components'">
>
<i class="material-icons">device_hub</i>
<span class="pane-name">Components</span>
</a>
<a class="button vuex"
<a
class="button vuex"
:class="{ active: tab === 'vuex'}"
v-tooltip="$t('App.vuex.tooltip')"
@click="switchTab('vuex')"
v-tooltip="'Switch to Vuex'">
>
<i class="material-icons">restore</i>
<span class="pane-name">Vuex</span>
</a>
<a class="button events"
<a
class="button events"
:class="{ active: tab === 'events' }"
v-tooltip="$t('App.events.tooltip')"
@click="switchTab('events')"
v-tooltip="'Switch to Events'">
>
<i class="material-icons">grain</i>
<span class="pane-name">Events</span>
<span class="event-count" v-if="newEventCount > 0">{{ newEventCount }}</span>
</a>
<a class="button refresh"
<a
class="button refresh"
v-tooltip="$t('App.refresh.tooltip')"
@click="refresh"
v-tooltip="'Force Refresh'">
>
<i class="material-icons" ref="refresh">refresh</i>
<span class="pane-name">Refresh</span>
</a>
Expand All @@ -49,11 +57,18 @@ import ComponentsTab from './views/components/ComponentsTab.vue'
import EventsTab from './views/events/EventsTab.vue'
import VuexTab from './views/vuex/VuexTab.vue'
import { SPECIAL_TOKENS } from '../util'
import Keyboard, {
C,
E,
R,
V
} from './mixins/keyboard'

import { mapState } from 'vuex'

export default {
name: 'app',
mixins: [Keyboard],
components: {
components: ComponentsTab,
vuex: VuexTab,
Expand Down Expand Up @@ -98,6 +113,25 @@ export default {
const activeBar = this.$el.querySelector('.active-bar')
activeBar.style.left = activeButton.offsetLeft + 'px'
activeBar.style.width = activeButton.offsetWidth + 'px'
},
onKeyDown ({ keyCode, altKey, ctrlKey, metaKey, shiftKey }) {
if (altKey) {
if (keyCode === C) {
this.switchTab('components')
return false
} else if (keyCode === V) {
this.switchTab('vuex')
return false
} else if (keyCode === E) {
this.switchTab('events')
return false
}
} else if (shiftKey && (ctrlKey || metaKey)) {
if (keyCode === R) {
this.refresh()
return false
}
}
}
},
mounted () {
Expand Down
4 changes: 2 additions & 2 deletions src/devtools/components/DataField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@
<template v-else>
<i
class="icon-button material-icons"
v-tooltip="cancelEditTooltip"
v-tooltip="$t('DataField.edit.cancel.tooltip')"
@click="cancelEdit()"
>close</i>
<i
class="icon-button material-icons"
v-tooltip="submitEditTooltip"
v-tooltip="$t('DataField.edit.submit.tooltip')"
@click="submitEdit()"
>done</i>
</template>
Expand Down
6 changes: 3 additions & 3 deletions src/devtools/components/ScrollPane.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ export default {
},
mounted () {
if (this.scrollEvent) {
bridge.on(this.scrollEvent, this.scroll)
bridge.on(this.scrollEvent, this.scrollToBottom)
}
},
destroyed () {
if (this.scrollEvent) {
bridge.removeListener(this.scrollEvent, this.scroll)
bridge.removeListener(this.scrollEvent, this.scrollToBottom)
}
},
methods: {
scroll () {
scrollToBottom () {
this.$nextTick(() => {
const container = this.$refs.scrollContainer
if (container.children.length) {
Expand Down
5 changes: 1 addition & 4 deletions src/devtools/components/StateInspector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
>
<div
class="data-type selectable-item"
v-tooltip="dataTypeTooltip"
v-tooltip="$t('StateInspector.dataType.tooltip')"
@click="toggle(dataType, $event)"
>
<span
Expand Down Expand Up @@ -76,9 +76,6 @@ export default {
(keyOrder[b] || (b.charCodeAt(0) + 999))
)
})
},
dataTypeTooltip () {
return `<span class="keyboard">${this.$keys.ctrl}</span> + <i class="material-icons">mouse</i>: Collapse All<br><span class="keyboard">${this.$keys.shift}</span> + <i class="material-icons">mouse</i>: Expand All`
}
},
methods: {
Expand Down
17 changes: 17 additions & 0 deletions src/devtools/env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Vue from 'vue'

// Env
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now since you moved this code to a separate file this comment seems redundant


export const isChrome = typeof chrome !== 'undefined' && !!chrome.devtools
export const isMac = navigator.platform === 'MacIntel'
export const keys = {
ctrl: isMac ? '&#8984;' : 'Ctrl',
shift: 'Shift',
alt: isMac ? '&#8997;' : 'Alt'
}

Object.defineProperties(Vue.prototype, {
'$isChrome': { get: () => isChrome },
'$isMac': { get: () => isMac },
'$keys': { get: () => keys }
})
7 changes: 5 additions & 2 deletions src/devtools/global.styl
Original file line number Diff line number Diff line change
Expand Up @@ -299,14 +299,17 @@ $arrow-color = #444

.keyboard
display inline-block
min-width 22px
text-align center
background rgba($grey, .3)
padding 2px 4px 0
border-radius 3px
margin-bottom 6px
box-shadow 0 3px 0 rgba($grey, .2)
margin-right 4px
.dark &
background rgba($grey, .7)
box-shadow 0 3px 0 rgba($grey, .4)
background rgba($grey, .9)
box-shadow 0 3px 0 rgba($grey, .6)

.mono
font-family Menlo, Consolas, monospace
Expand Down
17 changes: 1 addition & 16 deletions src/devtools/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,7 @@ import App from './App.vue'
import store from './store'
import './plugins'
import { parse } from '../util'

// Env

const isChrome = typeof chrome !== 'undefined' && !!chrome.devtools
const isMac = navigator.platform === 'MacIntel'
const keys = {
ctrl: isMac ? '&#8984;' : 'Ctrl',
shift: 'Shift',
alt: isMac ? '&#8997;' : 'Alt'
}

Object.defineProperties(Vue.prototype, {
'$isChrome': { get: () => isChrome },
'$isMac': { get: () => isMac },
'$keys': { get: () => keys }
})
import { isChrome } from './env'

// UI

Expand Down
78 changes: 78 additions & 0 deletions src/devtools/locales/en.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
export default {
App: {
components: {
tooltip: '[{{keys.alt}}] + [C] Switch to Components'
},
events: {
tooltip: '[{{keys.alt}}] + [E] Switch to Events'
},
refresh: {
tooltip: '[{{keys.ctrl}}] + [{{keys.shift}}] + [R] Force Refresh'
},
vuex: {
tooltip: '[{{keys.alt}}] + [V] Switch to Vuex'
}
},
StateInspector: {
dataType: {
tooltip: '[{{keys.ctrl}}] + |mouse|: Collapse All<br>[{{keys.shift}}] + |mouse|: Expand All'
}
},
DataField: {
edit: {
cancel: {
tooltip: '[Esc] Cancel'
},
submit: {
tooltip: '[Enter] Submit change'
}
},
quickEdit: {
number: {
tooltip: `Quick Edit<br><br>
[{{keys.ctrl}}] + |mouse|: {{operator}}5<br>
[{{keys.shift}}] + |mouse|: {{operator}}10<br>
[{{keys.alt}}] + |mouse|: {{operator}}100`
}
}
},
ComponentTree: {
select: {
tooltip: '[S] Select component in the page'
},
filter: {
tooltip: '[F] Filter components by name'
}
},
EventsHistory: {
filter: {
tooltip: '[F] To filter on components, type <span class="input-example"><i class="material-icons">search</i> &lt;MyComponent&gt;</span> or just <span class="input-example"><i class="material-icons">search</i> &lt;mycomp</span>.'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTML with classes in translations make it much harder to maintain code dependent on it, you never know which change might affect it. I'd be careful about this

},
clear: {
tooltip: '[{{keys.ctrl}}] + [C] Clear Log'
},
startRecording: {
tooltip: '[R] Start recording'
},
stopRecording: {
tooltip: '[R] Stop recording'
}
},
VuexHistory: {
filter: {
tooltip: '[F] Filter mutations'
},
commitAll: {
tooltip: '[{{keys.ctrl}}] + [C] Commit all'
},
revertAll: {
tooltip: '[{{keys.ctrl}}] + [R] Revert all'
},
startRecording: {
tooltip: '[R] Start recording'
},
stopRecording: {
tooltip: '[R] Stop recording'
}
}
}
15 changes: 3 additions & 12 deletions src/devtools/mixins/data-field-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,6 @@ export default {
}
}
return null
},

cancelEditTooltip () {
return '<span class="keyboard">Esc</span> Cancel'
},

submitEditTooltip () {
return '<span class="keyboard">Enter</span> Submit change'
}
},

Expand Down Expand Up @@ -236,10 +228,9 @@ export default {
},

quickEditNumberTooltip (operator) {
return `Quick Edit<br><br>
<span class="keyboard">${this.$keys.ctrl}</span> + <i class="material-icons">mouse</i>: ${operator}5<br>
<span class="keyboard">${this.$keys.shift}</span> + <i class="material-icons">mouse</i>: ${operator}10<br>
<span class="keyboard">${this.$keys.alt}</span> + <i class="material-icons">mouse</i>: ${operator}100`
return this.$t('DataField.quickEdit.number.tooltip', {
operator
})
}
}
}
12 changes: 12 additions & 0 deletions src/devtools/mixins/entry-list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { scrollIntoView } from 'src/util'

export default {
watch: {
inspectedIndex (value) {
this.$nextTick(() => {
const el = value === -1 ? this.$refs.baseEntry : this.$refs.entries[value]
el && scrollIntoView(document.querySelector('.left .scroll'), el, false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend we use .js- prefixed classes for everything that is being handled by JS. It would allow us to more safely deal with the code and potential refactors.

})
}
}
}
23 changes: 19 additions & 4 deletions src/devtools/mixins/keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,34 @@ export const LEFT = 37
export const UP = 38
export const RIGHT = 39
export const DOWN = 40
export const C = 67
export const E = 69
export const F = 70
export const R = 82
export const S = 83
export const V = 86

const activeInstances = []

document.addEventListener('keyup', e => {
if (e.target.tagName === 'INPUT') {
document.addEventListener('keydown', e => {
if (
e.target.tagName === 'INPUT' ||
e.target.tagName === 'TEXTAREA'
) {
return
}
let result = true
activeInstances.forEach(vm => {
if (vm.onKeyUp) {
vm.onKeyUp(e)
if (vm.onKeyDown) {
const r = vm.onKeyDown(e)
if (r === false) {
result = false
}
}
})
if (!result) {
e.preventDefault()
}
})

export default {
Expand Down
18 changes: 18 additions & 0 deletions src/devtools/plugins.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Vue from 'vue'
import VTooltip from 'v-tooltip'
import VI18n from './plugins/i18n'
import { keys } from './env'

Vue.use(VTooltip, {
defaultDelay: {
Expand All @@ -8,3 +10,19 @@ Vue.use(VTooltip, {
},
defaultOffset: 2
})

const currentLocale = 'en'
const locales = require.context('./locales')
const keyboardReg = /\[(\w+)\]/g
const iconReg = /\|(\w+)\|/g
Vue.use(VI18n, {
strings: locales(`./${currentLocale}`).default,
defaultValues: {
keys
},
replacer: text => {
text = text.replace(keyboardReg, '<span class="keyboard">$1</span>')
Copy link
Member

@michalsnik michalsnik Jan 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this solution. Now whenever [] or || appears somewhere in translation, it's going to wrap this part with one of those elements. As for the icon - if it's a generic app-wide solution I'm ok with it, but probably with better interpolation signs. e.g <<iconName>>, or at least ||iconName|| - though the first one more readable I guess.
But keyboard is rather case specific thing, and I wouldn't put it here. I'm thinking about alternative ways, and perhaps it would be better to make a dedicated file just for tooltips content, and import keys explicitly there.
That would leave us with translations in tact, without extra concerns. WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to have a lot of keyboard shortcuts in the strings--more than icons 😛

text = text.replace(iconReg, '<i class="material-icons">$1</i>')
return text
}
})
Loading