Skip to content

Commit

Permalink
feat(QSplitter): add keyboard navigation quasarframework#12466
Browse files Browse the repository at this point in the history
  • Loading branch information
pdanpdan committed Feb 14, 2022
1 parent e149c38 commit 1620d0a
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 29 deletions.
29 changes: 22 additions & 7 deletions ui/dev/src/pages/components/splitter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
style="height: 700px; border: 1px solid black"
>
<template v-slot:before>
<div class="q-layout-padding">
<div class="q-layout-padding q-focusable relative-position" tabindex="0">
<div class="q-focus-helper" />

<div class="text-h1 q-mb-md">
Before
</div>
Expand All @@ -60,7 +62,9 @@
/>

<template v-slot:after>
<div class="q-layout-padding">
<div class="q-layout-padding q-focusable relative-position" tabindex="0">
<div class="q-focus-helper" />

<div class="text-h1 q-mb-md">
After
</div>
Expand All @@ -83,7 +87,9 @@
separator-class="bg-deep-orange"
>
<template v-slot:before>
<div class="q-layout-padding">
<div class="q-layout-padding q-focusable relative-position" tabindex="0">
<div class="q-focus-helper" />

<div class="text-h1 q-mb-md">
Before
</div>
Expand All @@ -109,17 +115,22 @@
separator-class="bg-deep-orange"
class="bg-white rounded-borders"
style="width: 50vw; height: 30vh"
@keydown.stop
>
<template v-slot:before>
<div class="q-layout-padding">
<div class="q-layout-padding q-focusable relative-position" tabindex="0">
<div class="q-focus-helper" />

<div v-for="n in 20" :key="n" class="q-my-md">
{{ n }}. Lorem ipsum dolor sit.
</div>
</div>
</template>

<template v-slot:after>
<div class="q-layout-padding">
<div class="q-layout-padding q-focusable relative-position" tabindex="0">
<div class="q-focus-helper" />

<div v-for="n in 20" :key="n" class="q-my-md">
{{ n }}. Lorem ipsum dolor sit.
</div>
Expand All @@ -137,7 +148,9 @@
separator-class="bg-deep-orange"
>
<template v-slot:before>
<div class="q-layout-padding">
<div class="q-layout-padding q-focusable relative-position" tabindex="0">
<div class="q-focus-helper" />

<div class="text-h1 q-mb-md">
After - Before
</div>
Expand All @@ -158,7 +171,9 @@
/>

<template v-slot:after>
<div class="q-layout-padding">
<div class="q-layout-padding q-focusable relative-position" tabindex="0">
<div class="q-focus-helper" />

<div class="text-h1 q-mb-md">
After - After
</div>
Expand Down
123 changes: 101 additions & 22 deletions ui/src/components/splitter/QSplitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ import DarkMixin from '../../mixins/dark.js'
import ListenersMixin from '../../mixins/listeners.js'

import { slot, mergeSlot } from '../../utils/slot.js'
import { stop } from '../../utils/event.js'
import { stop, stopAndPrevent } from '../../utils/event.js'
import cache from '../../utils/cache.js'

const keyDirections = {
37: 'left',
38: 'up',
39: 'right',
40: 'down'
}

export default Vue.extend({
name: 'QSplitter',

Expand Down Expand Up @@ -44,6 +51,8 @@ export default Vue.extend({
horizontal: Boolean,
disable: Boolean,

tabindex: [String, Number],

beforeClass: [Array, String, Object],
afterClass: [Array, String, Object],

Expand Down Expand Up @@ -113,31 +122,63 @@ export default Vue.extend({
}
}]
}
},

separatorAttrs () {
return this.disable === true
? { tabindex: -1, 'aria-disabled': 'true' }
: { tabindex: this.tabindex || 0 }
},

separatorEvents () {
return this.disable === true
? void 0
: { keydown: this.__panKeydown }
}
},

methods: {
__panStart () {
const size = this.$el.getBoundingClientRect()[this.prop]

this.__dir = this.horizontal === true ? 'up' : 'left'
this.__maxValue = this.unit === '%' ? 100 : size
this.__value = Math.min(this.__maxValue, this.computedLimits[1], Math.max(this.computedLimits[0], this.value))
this.__multiplier = (this.reverse !== true ? 1 : -1) *
(this.horizontal === true ? 1 : (this.$q.lang.rtl === true ? -1 : 1)) *
(this.unit === '%' ? (size === 0 ? 0 : 100 / size) : 1)

this.$el.classList.add('q-splitter--active')
},

__panProgress (val) {
this.__normalized = Math.min(this.__maxValue, this.computedLimits[1], Math.max(this.computedLimits[0], val))

this.$refs[this.side].style[this.prop] = this.__getCSSValue(this.__normalized)

if (this.emitImmediately === true && this.value !== this.__normalized) {
this.$emit('input', this.__normalized)
}
},

__panEnd () {
this.__panCleanup !== void 0 && this.__panCleanup()

if (this.__normalized !== this.value) {
this.$emit('input', this.__normalized)
}

this.$el.classList.remove('q-splitter--active')
},

__pan (evt) {
if (evt.isFinal === true) {
if (this.__normalized !== this.value) {
this.$emit('input', this.__normalized)
}

this.$el.classList.remove('q-splitter--active')
this.__panEnd()
return
}

if (evt.isFirst === true) {
const size = this.$el.getBoundingClientRect()[this.prop]

this.__dir = this.horizontal === true ? 'up' : 'left'
this.__maxValue = this.unit === '%' ? 100 : size
this.__value = Math.min(this.__maxValue, this.computedLimits[1], Math.max(this.computedLimits[0], this.value))
this.__multiplier = (this.reverse !== true ? 1 : -1) *
(this.horizontal === true ? 1 : (this.$q.lang.rtl === true ? -1 : 1)) *
(this.unit === '%' ? (size === 0 ? 0 : 100 / size) : 1)

this.$el.classList.add('q-splitter--active')
this.__panStart()
return
}

Expand All @@ -146,13 +187,47 @@ export default Vue.extend({
(evt.direction === this.__dir ? -1 : 1) *
evt.distance[this.horizontal === true ? 'y' : 'x']

this.__normalized = Math.min(this.__maxValue, this.computedLimits[1], Math.max(this.computedLimits[0], val))
this.__panProgress(val)
},

this.$refs[this.side].style[this.prop] = this.__getCSSValue(this.__normalized)
__panKeydown (evt) {
this.qListeners.keydown !== void 0 && this.$emit('keydown', evt)

if (this.emitImmediately === true && this.value !== this.__normalized) {
this.$emit('input', this.__normalized)
if (
this.disable === true ||
evt.defaultPrevented === true ||
(this.horizontal !== true && [ 37, 39 ].indexOf(evt.keyCode) === -1) ||
(this.horizontal === true && [ 38, 40 ].indexOf(evt.keyCode) === -1)
) {
return
}

stopAndPrevent(evt)

if (this.__panCleanup === void 0) {
document.addEventListener('keyup', this.__panEnd)
document.addEventListener('focusout', this.__panEnd)

this.__panCleanup = () => {
this.__panCleanup = void 0
document.removeEventListener('keyup', this.__panEnd)
document.removeEventListener('focusout', this.__panEnd)

this.__panEnd()
}

this.__panStart()
this.__normalized = this.__value
}

const direction = keyDirections[evt.keyCode]

const val = this.__normalized +
this.__multiplier *
(direction === this.__dir ? -1 : 1) *
(evt.shiftKey === true ? 1 : 10)

this.__panProgress(val)
},

__normalize (val, limits) {
Expand All @@ -169,8 +244,11 @@ export default Vue.extend({
}
},

beforeDestroy () {
this.__panCleanup !== void 0 && this.__panCleanup()
},

render (h) {
const attrs = this.disable === true ? { 'aria-disabled': 'true' } : void 0
const child = [
h('div', {
ref: 'before',
Expand All @@ -184,7 +262,8 @@ export default Vue.extend({
staticClass: 'q-splitter__separator',
style: this.separatorStyle,
class: this.separatorClass,
attrs
attrs: this.separatorAttrs,
on: this.separatorEvents
}, [
h('div', {
staticClass: 'absolute-full q-splitter__separator-area',
Expand Down
4 changes: 4 additions & 0 deletions ui/src/components/splitter/QSplitter.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
"category": "content|model"
},

"tabindex": {
"extends": "tabindex"
},

"disable": {
"extends": "disable"
},
Expand Down

0 comments on commit 1620d0a

Please sign in to comment.