Skip to content

Commit

Permalink
feat(QTime): add change event; sync view and focused header; fixes fo…
Browse files Browse the repository at this point in the history
…r kbd navigation; allow parsing partial date/time strings; do not reset min/sec when setting hour and sec when setting minute; update innerValue when using setNow before it's updated from external value quasarframework#12467, quasarframework#10727, quasarframework#8487, quasarframework#6306
  • Loading branch information
pdanpdan committed Apr 27, 2023
1 parent 8567e20 commit a86aa28
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 34 deletions.
78 changes: 78 additions & 0 deletions ui/dev/src/pages/form/time.vue
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,67 @@
</q-icon>
</q-input>
</div>

<div class="text-h6">
Close on change
</div>
<q-input filled v-model="time" mask="time" :rules="['time']">
<template v-slot:append>
<q-icon name="access_time" class="cursor-pointer" tabindex="0">
<q-popup-proxy cover transition-show="scale" transition-hide="scale" ref="timeProxy">
<q-time v-model="time" @change="onChangeTime">
<div class="row items-center justify-end">
<q-btn v-close-popup label="Close" color="primary" flat></q-btn>
</div>
</q-time>
</q-popup-proxy>
</q-icon>
</template>
</q-input>

<q-input filled v-model="timeWithSeconds" mask="fulltime" :rules="['fulltime']">
<template v-slot:append>
<q-icon name="access_time" class="cursor-pointer" tabindex="0">
<q-popup-proxy cover transition-show="scale" transition-hide="scale" ref="timeWithSecondsProxy">
<q-time
v-model="timeWithSeconds"
with-seconds
format24h
@change="onChangeTimeWithSeconds"
>
<div class="row items-center justify-end">
<q-btn v-close-popup label="Close" color="primary" flat></q-btn>
</div>
</q-time>
</q-popup-proxy>
</q-icon>
</template>
</q-input>

<q-input filled v-model="timePartial" :rules="['fulltime']">
<template v-slot:append>
<q-icon name="access_time" class="cursor-pointer" tabindex="0">
<q-popup-proxy cover transition-show="scale" transition-hide="scale" ref="timePartialProxy">
<q-time
v-model="timePartial"
with-seconds
format24h
@change="onChangeTimePartial"
>
<div class="row items-center justify-end">
<q-btn v-close-popup label="Close" color="primary" flat></q-btn>
</div>
</q-time>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<div class="row q-gutter-x-md">
<q-btn flat label="Set ::" @click="timePartial = '::'" />
<q-btn flat label="Set 14::" @click="timePartial = '14::'" />
<q-btn flat label="Set 23:40:" @click="timePartial = '23:40:'" />
<q-btn flat label="Set 09:10:30" @click="timePartial = '09:10:30'" />
</div>
</div>
</div>
</template>
Expand All @@ -180,6 +241,8 @@ export default {
nowBtn: false,
time: '10:56',
timeWithSeconds: '10:50:30',
timePartial: '12:10:',
nullTime: null,
input: null,
input2: '12:35 PM',
Expand Down Expand Up @@ -220,6 +283,21 @@ export default {
if (min !== null && (min <= 25 || min >= 58)) { return false }
if (sec !== null && sec % 10 !== 0) { return false }
return true
},
onChangeTime (...ev) {
console.log('time', ...ev)
this.$refs.timeProxy.hide()
},
onChangeTimeWithSeconds (...ev) {
console.log('timeWithSeconds', ...ev)
this.$refs.timeWithSecondsProxy.hide()
},
onChangeTimePartial (...ev) {
console.log('timePartial', ...ev)
this.$refs.timePartialProxy.hide()
}
}
}
Expand Down
108 changes: 74 additions & 34 deletions ui/src/components/time/QTime.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import TouchPan from '../../directives/TouchPan.js'

import { slot } from '../../utils/private/slot.js'
import { formatDate, __splitDate, __safeCreateDate } from '../../utils/date.js'
import { position } from '../../utils/event.js'
import { position, prevent } from '../../utils/event.js'
import { pad } from '../../utils/format.js'
import cache from '../../utils/private/cache.js'
import DateTimeMixin from '../../mixins/datetime.js'

function preventKbdArrows (ev) {
[ 37, 38, 39, 40 ].includes(ev.keyCode) === true && prevent(ev)
}

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

Expand Down Expand Up @@ -87,10 +91,14 @@ export default Vue.extend({
this.innerModel = model

if (model.hour === null) {
this.view = 'Hour'
this.__goToView('Hour')
}
else {
this.isAM = model.hour < 12

if (model.minute === null && this.view === 'Second') {
this.__goToView('Minute')
}
}
}
},
Expand Down Expand Up @@ -287,15 +295,23 @@ export default Vue.extend({

methods: {
setNow () {
const date = {
const nowStr = this.__updateValue({
...this.__getCurrentDate(),
...this.__getCurrentTime()
}
})

this.__updateValue(date)
Object.assign(this.innerModel, date) // reset any pending changes to innerModel
const model = __splitDate(
nowStr,
this.computedMask,
this.computedLocale,
this.calendar,
this.defaultDateModel
)

this.innerModel = model
this.isAM = model.hour < 12

this.view = 'Hour'
this.__goToView('Hour')
},

__getValidValues (start, count, testFn) {
Expand Down Expand Up @@ -407,12 +423,24 @@ export default Vue.extend({
}
},

__goToView (view) {
if (this.view !== view) {
this.view = view
}
if (this.$refs[view] !== void 0 && this.$refs[view] !== document.activeElement) {
this.$refs[view].focus()
}
},

__goToNextView () {
if (this.view === 'Hour') {
this.view = 'Minute'
this.__goToView('Minute')
}
else if (this.withSeconds && this.view === 'Minute') {
this.view = 'Second'
this.__goToView('Second')
}
else {
this.__updateValue(void 0, true)
}
},

Expand Down Expand Up @@ -517,10 +545,10 @@ export default Vue.extend({

__onKeyupHour (e) {
if (e.keyCode === 13) { // ENTER
this.view = 'Hour'
this.view === 'Hour' && this.__goToNextView()
}
else if ([ 37, 39 ].includes(e.keyCode)) {
const payload = e.keyCode === 37 ? -1 : 1
else if ([ 37, 38, 39, 40 ].includes(e.keyCode)) {
const payload = e.keyCode === 37 || e.keyCode === 40 ? -1 : 1

if (this.validHours !== void 0) {
const values = this.computedFormat24h === true
Expand Down Expand Up @@ -555,10 +583,10 @@ export default Vue.extend({

__onKeyupMinute (e) {
if (e.keyCode === 13) { // ENTER
this.view = 'Minute'
this.view === 'Minute' && this.__goToNextView()
}
else if ([ 37, 39 ].includes(e.keyCode)) {
const payload = e.keyCode === 37 ? -1 : 1
else if ([ 37, 38, 39, 40 ].includes(e.keyCode)) {
const payload = e.keyCode === 37 || e.keyCode === 40 ? -1 : 1

if (this.validMinutes !== void 0) {
const values = this.validMinutes.values
Expand Down Expand Up @@ -587,10 +615,10 @@ export default Vue.extend({

__onKeyupSecond (e) {
if (e.keyCode === 13) { // ENTER
this.view = 'Second'
this.view === 'Second' && this.__goToNextView()
}
else if ([ 37, 39 ].includes(e.keyCode)) {
const payload = e.keyCode === 37 ? -1 : 1
else if ([ 37, 38, 39, 40 ].includes(e.keyCode)) {
const payload = e.keyCode === 37 || e.keyCode === 40 ? -1 : 1

if (this.validSeconds !== void 0) {
const values = this.validSeconds.values
Expand Down Expand Up @@ -622,9 +650,10 @@ export default Vue.extend({
h('div', {
staticClass: 'q-time__link',
class: this.view === 'Hour' ? 'q-time__link--active' : 'cursor-pointer',
attrs: { tabindex: this.computedTabindex },
attrs: { tabindex: this.computedTabindex, 'data-autofocus': this.view === 'Hour' },
on: cache(this, 'vH', {
click: () => { this.view = 'Hour' },
focus: () => { this.view = 'Hour' },
keydown: preventKbdArrows,
keyup: this.__onKeyupHour
})
}, [ this.stringModel.hour ]),
Expand All @@ -635,11 +664,13 @@ export default Vue.extend({
'div',
this.minLink === true
? {
ref: 'Minute',
staticClass: 'q-time__link',
class: this.view === 'Minute' ? 'q-time__link--active' : 'cursor-pointer',
attrs: { tabindex: this.computedTabindex },
attrs: { tabindex: this.computedTabindex, 'data-autofocus': this.view === 'Minute' },
on: cache(this, 'vM', {
click: () => { this.view = 'Minute' },
focus: () => { this.view = 'Minute' },
keydown: preventKbdArrows,
keyup: this.__onKeyupMinute
})
}
Expand All @@ -656,11 +687,13 @@ export default Vue.extend({
'div',
this.secLink === true
? {
ref: 'Second',
staticClass: 'q-time__link',
class: this.view === 'Second' ? 'q-time__link--active' : 'cursor-pointer',
attrs: { tabindex: this.computedTabindex },
attrs: { tabindex: this.computedTabindex, 'data-autofocus': this.view === 'Second' },
on: cache(this, 'vS', {
click: () => { this.view = 'Second' },
focus: () => { this.view = 'Second' },
keydown: preventKbdArrows,
keyup: this.__onKeyupSecond
})
}
Expand Down Expand Up @@ -780,21 +813,22 @@ export default Vue.extend({
__setHour (hour) {
if (this.innerModel.hour !== hour) {
this.innerModel.hour = hour
this.innerModel.minute = null
this.innerModel.second = null
this.__verifyAndUpdate()
}
},

__setMinute (minute) {
if (this.innerModel.minute !== minute) {
this.innerModel.minute = minute
this.innerModel.second = null
this.withSeconds !== true && this.__updateValue({ minute })
this.__verifyAndUpdate()
}
},

__setSecond (second) {
this.innerModel.second !== second && this.__updateValue({ second })
if (this.innerModel.second !== second) {
this.innerModel.second = second
this.__verifyAndUpdate()
}
},

__setAm () {
Expand Down Expand Up @@ -822,20 +856,20 @@ export default Vue.extend({
__verifyAndUpdate () {
if (this.hourInSelection !== void 0 && this.hourInSelection(this.innerModel.hour) !== true) {
this.innerModel = __splitDate()
this.view = 'Hour'
this.__goToView('Hour')
return
}

if (this.minuteInSelection !== void 0 && this.minuteInSelection(this.innerModel.minute) !== true) {
this.innerModel.minute = null
this.innerModel.second = null
this.view = 'Minute'
this.__goToView('Minute')
return
}

if (this.withSeconds === true && this.secondInSelection !== void 0 && this.secondInSelection(this.innerModel.second) !== true) {
this.innerModel.second = null
this.view = 'Second'
this.__goToView('Second')
return
}

Expand All @@ -846,7 +880,7 @@ export default Vue.extend({
this.__updateValue()
},

__updateValue (obj) {
__updateValue (obj, final) {
const date = Object.assign({ ...this.innerModel }, obj)

const val = this.calendar === 'persian'
Expand All @@ -870,7 +904,13 @@ export default Vue.extend({
)

date.changed = val !== this.value
this.$emit('input', val, date)

if (final !== true || date.changed === true) {
this.$emit('input', val, date)
}
final === true && this.$emit('change', val, date)

return val
}
},

Expand Down
Loading

0 comments on commit a86aa28

Please sign in to comment.