From 5b9662db5d78e2cc13ce913c340caae874f0c267 Mon Sep 17 00:00:00 2001 From: Jost Date: Tue, 19 May 2020 11:33:02 +0200 Subject: [PATCH 1/6] Add prop for manual input delay --- src/vue-timepicker.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vue-timepicker.vue b/src/vue-timepicker.vue index 53d7589..9a0ee54 100644 --- a/src/vue-timepicker.vue +++ b/src/vue-timepicker.vue @@ -64,6 +64,7 @@ export default { autoScroll: { type: Boolean, default: false }, manualInput: { type: Boolean, default: false }, + manualInputDelay: { type: Number, default: 1000 }, hideDropdown: { type: Boolean, default: false }, debugMode: { type: Boolean, default: false } @@ -1604,6 +1605,7 @@ export default { const currentChunk = this.getCurrentTokenChunk() if (!currentChunk || (currentChunk.type !== 'apm' && isApm) || (currentChunk.type === 'apm' && !isApm)) { return } this.kbInputLog = `${this.kbInputLog.substr(-1)}${newChar}` + this.setKbInput(this.kbInputLog) this.debounceSetKbInput() }, @@ -1611,9 +1613,8 @@ export default { window.clearTimeout(this.kbInputTimer) this.kbInputTimer = window.setTimeout(() => { window.clearTimeout(this.kbInputTimer) - this.setKbInput(this.kbInputLog) this.kbInputLog = '' - }, 500) + }, this.manualInputDelay) }, setKbInput (value) { From ac5ccdc212887531988f5da56838a688919d969b Mon Sep 17 00:00:00 2001 From: Phoenix Wong Date: Sat, 23 May 2020 17:19:01 +0800 Subject: [PATCH 2/6] Enhancements for the instant manual input feedback (#113) - Rename the new property to `manualInputTimeout` - Add failsafe checking - Clear manual input logs right after pressing arrows and tab keys to prevent adhesive values - Add Playground demo accordingly --- demo/src/components/Playground.vue | 23 ++++++++++++++++++++ src/vue-timepicker.vue | 34 +++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/demo/src/components/Playground.vue b/demo/src/components/Playground.vue index 58696bd..a27964a 100644 --- a/demo/src/components/Playground.vue +++ b/demo/src/components/Playground.vue @@ -78,6 +78,9 @@ export default { customBlurDelay: false, blurDelay: 300, + customManualInputTimeout: false, + manualInputTimeout: 1000, + playgroundData: {}, playgroundFullValue: {}, playgroundErroredData: undefined, @@ -185,6 +188,10 @@ export default { start += (`\n :blur-delay="${this.blurDelay}"`) } + if (this.customManualInputTimeout) { + start += (`\n :manual-input-timeout="${this.manualInputTimeout}"`) + } + if (this.lazyMode) { start += ('\n lazy') } @@ -592,6 +599,10 @@ export default { toggleBlurDelay () { this.blurDelay = 300 + }, + + toggleManualInputTimeout () { + this.manualInputTimeout = 1000 } }, @@ -818,6 +829,18 @@ section#playground input(v-model.number="blurDelay" type="range" min="50" max="1500" step="50") span(v-text="blurDelay") + #manualInputTimeout.config-block(v-if="manualInput") + h3.subtitle + a.anchor # + | Customized Manual Input Timeout + config-row(is-group) + label.options + input(v-model="customManualInputTimeout" type="checkbox" @input="toggleManualInputTimeout") + |  Set Manual Input Timeout + label.range-wrapper(v-if="customManualInputTimeout") + input(v-model.number="manualInputTimeout" type="range" min="50" max="5000" step="50") + span(v-text="manualInputTimeout") + #skipErrorStyle.config-block h3.subtitle a.anchor # diff --git a/src/vue-timepicker.vue b/src/vue-timepicker.vue index 9a0ee54..1c35478 100644 --- a/src/vue-timepicker.vue +++ b/src/vue-timepicker.vue @@ -19,7 +19,8 @@ const DEFAULT_OPTIONS = { hideDisabledItems: false, advancedKeyboard: false, hideDropdown: false, - blurDelay: 300 + blurDelay: 300, + manualInputTimeout: 1000 } export default { @@ -64,7 +65,7 @@ export default { autoScroll: { type: Boolean, default: false }, manualInput: { type: Boolean, default: false }, - manualInputDelay: { type: Number, default: 1000 }, + manualInputTimeout: { type: [ Number, String ] }, hideDropdown: { type: Boolean, default: false }, debugMode: { type: Boolean, default: false } @@ -202,6 +203,10 @@ export default { options.blurDelay = +this.blurDelay } + if (this.manualInputTimeout && +this.manualInputTimeout > 0) { + options.manualInputTimeout = +this.manualInputTimeout + } + return options }, @@ -1492,13 +1497,16 @@ export default { // Arrow keys } else if (evt.keyCode >= 37 && evt.keyCode <= 40) { evt.preventDefault() + this.clearKbInputLog() this.arrowHandler(evt) // Delete|Backspace } else if (evt.keyCode === 8 || evt.keyCode === 46) { evt.preventDefault() + this.clearKbInputLog() this.clearTime() // Tab } else if (evt.keyCode === 9) { + this.clearKbInputLog() this.tabHandler(evt) // Prevent any Non-ESC and non-pasting inputs } else if (evt.keyCode !== 27 && !(evt.metaKey || evt.ctrlKey)) { @@ -1562,7 +1570,8 @@ export default { this.readStringValues(pastingText) } else { this.kbInputLog = pastingText.substr(-2, 2) - this.debounceSetKbInput() + this.setKbInput() + this.debounceClearKbLog() } }, @@ -1605,21 +1614,26 @@ export default { const currentChunk = this.getCurrentTokenChunk() if (!currentChunk || (currentChunk.type !== 'apm' && isApm) || (currentChunk.type === 'apm' && !isApm)) { return } this.kbInputLog = `${this.kbInputLog.substr(-1)}${newChar}` - this.setKbInput(this.kbInputLog) - this.debounceSetKbInput() + this.setKbInput() + this.debounceClearKbLog() + }, + + clearKbInputLog () { + window.clearTimeout(this.kbInputTimer) + this.kbInputLog = '' }, - debounceSetKbInput () { + debounceClearKbLog () { window.clearTimeout(this.kbInputTimer) this.kbInputTimer = window.setTimeout(() => { - window.clearTimeout(this.kbInputTimer) - this.kbInputLog = '' - }, this.manualInputDelay) + this.clearKbInputLog() + }, this.opts.manualInputTimeout) }, setKbInput (value) { + value = value || this.kbInputLog const currentChunk = this.getCurrentTokenChunk() - if (!currentChunk) { return } + if (!currentChunk || !value || !value.length) { return } const chunkType = currentChunk.type const chunkToken = currentChunk.token From baf3432b2dc05e0721fbccc80f56fb2fc67b56fd Mon Sep 17 00:00:00 2001 From: Phoenix Wong Date: Sat, 23 May 2020 23:33:09 +0800 Subject: [PATCH 3/6] Upgrade `vue-cli` to v4, along with other dependencies --- CONTRIBUTING.md | 5 +- babel.config.js | 2 +- demo/babel.config.js | 2 +- demo/package.json | 20 +- demo/src/components/Samples.vue | 392 +++++++++++++++++--------------- package.json | 70 +++--- 6 files changed, 259 insertions(+), 232 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b8fdc96..1c5a953 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,8 +15,11 @@ yarn dev For detailed explanation on how things work, checkout the [Vue Cli Guide](https://cli.vuejs.org/guide/). -> NOTE: Start from `^0.2.0`, we develop Demo pages with [**Yarn**]( +**NOTE:** + +- Start from `^0.2.0`, we develop Demo pages with [**Yarn**]( https://yarnpkg.com/), [**Pug**](https://pugjs.org/), and [**Stylus**](http://stylus-lang.com/) +- Upgraded to **@vue/cli v4** from `^1.1.1` --- diff --git a/babel.config.js b/babel.config.js index ba17966..e955840 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,5 +1,5 @@ module.exports = { presets: [ - '@vue/app' + '@vue/cli-plugin-babel/preset' ] } diff --git a/demo/babel.config.js b/demo/babel.config.js index ba17966..e955840 100644 --- a/demo/babel.config.js +++ b/demo/babel.config.js @@ -1,5 +1,5 @@ module.exports = { presets: [ - '@vue/app' + '@vue/cli-plugin-babel/preset' ] } diff --git a/demo/package.json b/demo/package.json index 2b39e30..4401fc1 100644 --- a/demo/package.json +++ b/demo/package.json @@ -8,22 +8,22 @@ "lint": "vue-cli-service lint" }, "dependencies": { - "core-js": "^2.6.9", - "highlight.js": "^9.15.10", - "vue": "^2.6.10", + "core-js": "^3.6.5", + "highlight.js": "^9.18.1", + "vue": "^2.6.11", "vue-highlight.js": "^3.1.0" }, "devDependencies": { - "@vue/cli-plugin-babel": "^3.11.0", - "@vue/cli-plugin-eslint": "^3.11.0", - "@vue/cli-service": "^3.11.0", - "babel-eslint": "^10.0.3", - "eslint": "^5.16.0", - "eslint-plugin-vue": "^5.0.0", + "@vue/cli-plugin-babel": "^4.3.1", + "@vue/cli-plugin-eslint": "^4.3.1", + "@vue/cli-service": "^4.3.1", + "babel-eslint": "^10.1.0", + "eslint": "^6.8.0", + "eslint-plugin-vue": "^6.2.2", "stylus": "^0.54.7", "stylus-loader": "^3.0.2", "vue-cli-plugin-pug": "^1.0.7", - "vue-template-compiler": "^2.6.10" + "vue-template-compiler": "^2.6.11" }, "eslintConfig": { "root": false, diff --git a/demo/src/components/Samples.vue b/demo/src/components/Samples.vue index 8abc2d9..e3f3037 100644 --- a/demo/src/components/Samples.vue +++ b/demo/src/components/Samples.vue @@ -138,11 +138,12 @@ section#mostlyUsedSamples | string, you can set timepicker in form of 12 hours template(v-slot:codes) highlight-code(lang="html" data-title="HTML") - | <!-- 12-hour sample 1 --> - | <vue-timepicker format="hh:mm A"></vue-timepicker> - |   - | <!-- 12-hour sample 2 --> - | <vue-timepicker format="h:m a"></vue-timepicker> + pre + | <!-- 12-hour sampleaa 1 --> + | <vue-timepicker format="hh:mm A"></vue-timepicker> + |   + | <!-- 12-hour sample 2 --> + | <vue-timepicker format="h:m a"></vue-timepicker> template(v-slot:preview) b 12-hour sample 1 p @@ -175,14 +176,15 @@ section#mostlyUsedSamples | for example template(v-slot:codes) highlight-code(lang="html" data-title="HTML") - | <!-- 10-minute interval --> - | <vue-timepicker :minute-interval="10"></vue-timepicker> - |   - | <!-- 15-second interval --> - | <vue-timepicker format="HH:mm:ss" :second-interval="15"></vue-timepicker> - |   - | <!-- 5-minute interval plus 10-second interval --> - | <vue-timepicker format="hh:mm:ss" :minute-interval="5" :second-interval="10"></vue-timepicker> + pre + | <!-- 10-minute interval --> + | <vue-timepicker :minute-interval="10"></vue-timepicker> + |   + | <!-- 15-second interval --> + | <vue-timepicker format="HH:mm:ss" :second-interval="15"></vue-timepicker> + |   + | <!-- 5-minute interval plus 10-second interval --> + | <vue-timepicker format="hh:mm:ss" :minute-interval="5" :second-interval="10"></vue-timepicker> template(v-slot:preview) b 10-minute interval p @@ -203,18 +205,19 @@ section#mostlyUsedSamples | Timepicker takes v-model value in object format by default. template(v-slot:codes) highlight-code(lang="javascript" data-title="JS") - | // Define format and initial data - | data () { - | return { - | yourFormat: 'hh:mm:ss a', - | yourData: { - | hh: '03', - | mm: '05', - | ss: '00', - | a: 'am' - | } - | } - | } + pre + | // Define format and initial data + | data () { + | return { + | yourFormat: 'hh:mm:ss a', + | yourData: { + | hh: '03', + | mm: '05', + | ss: '00', + | a: 'am' + | } + | } + | } highlight-code(lang="html" data-title="HTML") | <vue-timepicker :format="yourFormat" v-model="yourData"></vue-timepicker> template(v-slot:preview) @@ -231,26 +234,28 @@ section#mostlyUsedSamples | From v1.0.0+, timepicker also supports v-model value in string format. template(v-slot:codes) highlight-code(lang="javascript" data-title="JS") - | // Set initial data in string format - | data () { - | return { - | simpleStringValue: '02:30', - | - | // paired with format 'h:mm:ss A' - | yourStringValue: '3:mm:05 A', - | - | unsetStringValue: '' - | } - | } + pre + | // Set initial data in string format + | data () { + | return { + | simpleStringValue: '02:30', + | + | // paired with format 'h:mm:ss A' + | yourStringValue: '3:mm:05 A', + | + | unsetStringValue: '' + | } + | } highlight-code(lang="html" data-title="HTML") - | <!-- default 24-hour sample --> - | <vue-timepicker v-model="simpleStringValue"></vue-timepicker> - | - | <!-- 12-hour format with partial value set --> - | <vue-timepicker v-model="yourStringValue" format="h:mm:ss A"></vue-timepicker> - | - | <!-- unset/unknown initial value --> - | <vue-timepicker v-model="unsetStringValue"></vue-timepicker> + pre + | <!-- default 24-hour sample --> + | <vue-timepicker v-model="simpleStringValue"></vue-timepicker> + | + | <!-- 12-hour format with partial value set --> + | <vue-timepicker v-model="yourStringValue" format="h:mm:ss A"></vue-timepicker> + | + | <!-- unset/unknown initial value --> + | <vue-timepicker v-model="unsetStringValue"></vue-timepicker> template(v-slot:preview) b default 24-hour sample p @@ -282,23 +287,25 @@ section#mostlyUsedSamples | usage template(v-slot:codes) highlight-code(lang="javascript" data-title="JS") - | data () { - | return { - | yourDaysArray: [ - | {start_time: {HH: '08', mm: '00'}, end_time: {HH: '09', mm: '00'}}, - | {start_time: {HH: '15', mm: '00'}, end_time: {HH: '', mm: ''}}, - | {start_time: {HH: '', mm: ''}, end_time: {HH: '13', mm: '30'}}, - | {start_time: {HH: '', mm: ''}, end_time: {HH: '', mm: ''}} - | ] - | } - | } + pre + | data () { + | return { + | yourDaysArray: [ + | {start_time: {HH: '08', mm: '00'}, end_time: {HH: '09', mm: '00'}}, + | {start_time: {HH: '15', mm: '00'}, end_time: {HH: '', mm: ''}}, + | {start_time: {HH: '', mm: ''}, end_time: {HH: '13', mm: '30'}}, + | {start_time: {HH: '', mm: ''}, end_time: {HH: '', mm: ''}} + | ] + | } + | } highlight-code(lang="html" data-title="HTML") - | <p v-for="(day, index) in yourDaysArray"> - | <label>Day {{ index + 1 }}: </label> - | <vue-timepicker v-model="day.start_time" placeholder="Start Time"></vue-timepicker> - | <span> to </span> - | <vue-timepicker v-model="day.end_time" placeholder="End Time"></vue-timepicker> - | </p> + pre + | <p v-for="(day, index) in yourDaysArray"> + | <label>Day {{ index + 1 }}: </label> + | <vue-timepicker v-model="day.start_time" placeholder="Start Time"></vue-timepicker> + | <span> to </span> + | <vue-timepicker v-model="day.end_time" placeholder="End Time"></vue-timepicker> + | </p> template(v-slot:preview) p(v-for="(day, index) in yourDaysArray") label Day {{ index + 1 }}:  @@ -314,13 +321,14 @@ section#mostlyUsedSamples p(slot="description") Define the hour values you want and disable the rest template(v-slot:codes) highlight-code(lang="html" data-title="HTML") - | <!-- Hour Range Sample 1 --> - | <vue-timepicker :hour-range="[5, [8, 12], [14, 17], 19]"></vue-timepicker> - | <!-- >> Equals to :hour-range="[5, 8, 9, 10, 11, 12, 14, 15, 16, 17, 19]" --> - |   - | <!-- Hour Range Sample 2 (12-hour format) --> - | <vue-timepicker :hour-range="['7a', '9a', '11a', '1p', ['3p', '5p'], '7p']" format="hh:mm a"></vue-timepicker> - | <!-- >> Equals to :hour-range="['7a', '9a', '11a', '1p', '3p', '4p', '5p', '7p']" --> + pre + | <!-- Hour Range Sample 1 --> + | <vue-timepicker :hour-range="[5, [8, 12], [14, 17], 19]"></vue-timepicker> + | <!-- >> Equals to :hour-range="[5, 8, 9, 10, 11, 12, 14, 15, 16, 17, 19]" --> + |   + | <!-- Hour Range Sample 2 (12-hour format) --> + | <vue-timepicker :hour-range="['7a', '9a', '11a', '1p', ['3p', '5p'], '7p']" format="hh:mm a"></vue-timepicker> + | <!-- >> Equals to :hour-range="['7a', '9a', '11a', '1p', '3p', '4p', '5p', '7p']" --> template(v-slot:preview) b Hour Range Sample 1 p @@ -336,20 +344,21 @@ section#mostlyUsedSamples p Similar to Hour Range, you can set available minute/second values base on your needs. template(v-slot:codes) highlight-code(lang="html" data-title="HTML") - | <!-- Minute range only --> - | <vue-timepicker :minute-range="[0, 6, [10, 30], 42, 50]"></vue-timepicker> - | - | <!-- Minute range + 5-minute interval --> - | <vue-timepicker :minute-range="[0, 6, [10, 30], 42, 50]" :minute-interval="5"></vue-timepicker> - | - | <!-- Second range only --> - | <vue-timepicker format="H:m:s" :second-range="[0, 6, [10, 30], 42, 50]"></vue-timepicker> - | - | <!-- Second range + 10-second interval --> - | <vue-timepicker format="H:m:s" :second-range="[0, 6, [10, 30], 42, 50]" :second-interval="10"></vue-timepicker> - | - | <!-- Minute and Second ranges + 10-minute interval + 5-second interval --> - | <vue-timepicker format="HH:mm:ss" :minute-range="[0, 6, [10, 30], 42, 50]" :second-range="[0, 6, [10, 30], 42, 50]" :minute-interval="10" :second-interval="5"></vue-timepicker> + pre + | <!-- Minute range only --> + | <vue-timepicker :minute-range="[0, 6, [10, 30], 42, 50]"></vue-timepicker> + | + | <!-- Minute range + 5-minute interval --> + | <vue-timepicker :minute-range="[0, 6, [10, 30], 42, 50]" :minute-interval="5"></vue-timepicker> + | + | <!-- Second range only --> + | <vue-timepicker format="H:m:s" :second-range="[0, 6, [10, 30], 42, 50]"></vue-timepicker> + | + | <!-- Second range + 10-second interval --> + | <vue-timepicker format="H:m:s" :second-range="[0, 6, [10, 30], 42, 50]" :second-interval="10"></vue-timepicker> + | + | <!-- Minute and Second ranges + 10-minute interval + 5-second interval --> + | <vue-timepicker format="HH:mm:ss" :minute-range="[0, 6, [10, 30], 42, 50]" :second-range="[0, 6, [10, 30], 42, 50]" :minute-interval="10" :second-interval="5"></vue-timepicker> template(v-slot:preview) b Minute range only p @@ -387,17 +396,18 @@ section#mostlyUsedSamples | : Hide disabled second values only. template(v-slot:codes) highlight-code(lang="html" data-title="HTML") - | <!-- hide-disabled-items --> - | <vue-timepicker hide-disabled-items format="HH:mm:ss" :hour-range="[[9, 17]]" :minute-range="[0, 10, 15, 30, 50]" :second-range="[5, 15, 25, 45]"></vue-timepicker> - | - | <!-- hide-disabled-hours --> - | <vue-timepicker hide-disabled-hours format="HH:mm:ss" :hour-range="[[9, 17]]" :minute-range="[0, 10, 15, 30, 50]" :second-range="[5, 15, 25, 45]"></vue-timepicker> - | - | <!-- hide-disabled-minutes --> - | <vue-timepicker hide-disabled-minutes format="HH:mm:ss" :hour-range="[[9, 17]]" :minute-range="[0, 10, 15, 30, 50]" :second-range="[5, 15, 25, 45]"></vue-timepicker> - | - | <!-- hide-disabled-seconds --> - | <vue-timepicker hide-disabled-seconds format="HH:mm:ss" :hour-range="[[9, 17]]" :minute-range="[0, 10, 15, 30, 50]" :second-range="[5, 15, 25, 45]"></vue-timepicker> + pre + | <!-- hide-disabled-items --> + | <vue-timepicker hide-disabled-items format="HH:mm:ss" :hour-range="[[9, 17]]" :minute-range="[0, 10, 15, 30, 50]" :second-range="[5, 15, 25, 45]"></vue-timepicker> + | + | <!-- hide-disabled-hours --> + | <vue-timepicker hide-disabled-hours format="HH:mm:ss" :hour-range="[[9, 17]]" :minute-range="[0, 10, 15, 30, 50]" :second-range="[5, 15, 25, 45]"></vue-timepicker> + | + | <!-- hide-disabled-minutes --> + | <vue-timepicker hide-disabled-minutes format="HH:mm:ss" :hour-range="[[9, 17]]" :minute-range="[0, 10, 15, 30, 50]" :second-range="[5, 15, 25, 45]"></vue-timepicker> + | + | <!-- hide-disabled-seconds --> + | <vue-timepicker hide-disabled-seconds format="HH:mm:ss" :hour-range="[[9, 17]]" :minute-range="[0, 10, 15, 30, 50]" :second-range="[5, 15, 25, 45]"></vue-timepicker> template(v-slot:preview) b hide-disabled-items p @@ -419,11 +429,12 @@ section#mostlyUsedSamples | Automatically close the dropdown when user finish selecting all of the required fields. template(v-slot:codes) highlight-code(lang="html" data-title="HTML") - | <!-- Auto-close on complete --> - | <vue-timepicker format="hh:mm A" close-on-complete></vue-timepicker> - | - | <!-- Default - close by clicking anywhere outside of the dropdown --> - | <vue-timepicker format="hh:mm A"></vue-timepicker> + pre + | <!-- Auto-close on complete --> + | <vue-timepicker format="hh:mm A" close-on-complete></vue-timepicker> + | + | <!-- Default - close by clicking anywhere outside of the dropdown --> + | <vue-timepicker format="hh:mm A"></vue-timepicker> template(v-slot:preview) b Auto-close on complete p @@ -469,25 +480,27 @@ section#mostlyUsedSamples p Play around with the two pickers below to see their data changes in live. template(v-slot:codes) highlight-code(lang="html" data-title="HTML") - | <!-- No argument --> - | <vue-timepicker v-model="demoData1" @change="changeHandler"></vue-timepicker> - |   - | <!-- Custom argument --> - | <vue-timepicker v-model="demoData2" @change="otherChangeHandler($event, 'foo', 42)"></vue-timepicker> + pre + | <!-- No argument --> + | <vue-timepicker v-model="demoData1" @change="changeHandler"></vue-timepicker> + |   + | <!-- Custom argument --> + | <vue-timepicker v-model="demoData2" @change="otherChangeHandler($event, 'foo', 42)"></vue-timepicker> highlight-code(lang="javascript" data-title="JS") - | methods: { - | // No argument - | changeHandler (eventData) { - | // eventData -> {data: {HH:..., mm:...}, displayTime: 'HH:mm'} - | }, - | - | // Customized arguments - | otherChangeHandler (eventData, arg1, arg2) { - | // eventData -> {data: {HH:..., mm:...}, displayTime: 'HH:mm'} - | // arg1 -> 'foo' - | // arg2 -> 42 - | } - | } + pre + | methods: { + | // No argument + | changeHandler (eventData) { + | // eventData -> {data: {HH:..., mm:...}, displayTime: 'HH:mm'} + | }, + | + | // Customized arguments + | otherChangeHandler (eventData, arg1, arg2) { + | // eventData -> {data: {HH:..., mm:...}, displayTime: 'HH:mm'} + | // arg1 -> 'foo' + | // arg2 -> 42 + | } + | } template(v-slot:preview) b No argument p @@ -550,22 +563,23 @@ section#mostlyUsedSamples template(v-slot:codes) highlight-code(lang="html" data-title="HTML") - | <label for="otherInput">Text Input<label/> - | <input id="otherInput" type="text" placeholder="Text" /> - | - | <!-- Default, with minimal keyboard support --> - | <label for="simplePicker">Default Vue Timepicker<label/> - | <vue-timepicker id="simplePicker"></vue-timepicker> - | - | <label for="moreInput">Number Input<label/> - | <input id="moreInput" type="number" placeholder="Number" /> - | - | <!-- Advanced Keyboard Support Enabled --> - | <label for="pickerKB">Vue Timepicker with Advanced Keyboard support<label/> - | <vue-timepicker advanced-keyboard format="h:mm:ss A" :hour-range="[['7a', '5p']]" id="pickerKB"></vue-timepicker> - | - | <label for="oneMoreInput">One More Text Input<label/> - | <input id="oneMoreInput" type="text" placeholder="More Text" /> + pre + | <label for="otherInput">Text Input<label/> + | <input id="otherInput" type="text" placeholder="Text" /> + | + | <!-- Default, with minimal keyboard support --> + | <label for="simplePicker">Default Vue Timepicker<label/> + | <vue-timepicker id="simplePicker"></vue-timepicker> + | + | <label for="moreInput">Number Input<label/> + | <input id="moreInput" type="number" placeholder="Number" /> + | + | <!-- Advanced Keyboard Support Enabled --> + | <label for="pickerKB">Vue Timepicker with Advanced Keyboard support<label/> + | <vue-timepicker advanced-keyboard format="h:mm:ss A" :hour-range="[['7a', '5p']]" id="pickerKB"></vue-timepicker> + | + | <label for="oneMoreInput">One More Text Input<label/> + | <input id="oneMoreInput" type="text" placeholder="More Text" /> template(v-slot:preview) b label(for="otherInput") Text Input @@ -595,20 +609,22 @@ section#mostlyUsedSamples | Allow users to input values manually. Please note that the additional hide-dropdown option works with manual-input mode only. template(v-slot:codes) highlight-code(lang="html" data-title="HTML") - | <!-- 24-hour format with empty init value --> - | <vue-timepicker manual-input></vue-timepicker> - | - | <!-- 12-hour format with a predefined value --> - | <vue-timepicker format="h:mm a" v-model="manualStringValue" manual-input></vue-timepicker> - | - | <!-- Manual input + hide dropdown --> - | <vue-timepicker manual-input hide-dropdown></vue-timepicker> + pre + | <!-- 24-hour format with empty init value --> + | <vue-timepicker manual-input></vue-timepicker> + | + | <!-- 12-hour format with a predefined value --> + | <vue-timepicker format="h:mm a" v-model="manualStringValue" manual-input></vue-timepicker> + | + | <!-- Manual input + hide dropdown --> + | <vue-timepicker manual-input hide-dropdown></vue-timepicker> highlight-code(lang="javascript" data-title="JS") - | data () { - | return { - | manualStringValue: '8:15 pm' - | } - | } + pre + | data () { + | return { + | manualStringValue: '8:15 pm' + | } + | } template(v-slot:preview) b 24-hour format with empty init value p @@ -631,16 +647,18 @@ section#mostlyUsedSamples | Help identifying current status of the dropdown picker template(v-slot:codes) highlight-code(lang="javascript" data-title="JS") - | // Define a variable for logging the status - | data () { - | return { - | dropdownStatus: 'closed' - | } - | } + pre + | // Define a variable for logging the status + | data () { + | return { + | dropdownStatus: 'closed' + | } + | } highlight-code(lang="html" data-title="HTML") - | <p>Dropdown Status: I'm
{{
dropdownStatus
}}
!</p> - |   - | <vue-timepicker @open="dropdownStatus = 'opened'" @close="dropdownStatus = 'closed'"></vue-timepicker> + pre + | <p>Dropdown Status: I'm {{dropdownStatus}}!</p> + |   + | <vue-timepicker @open="dropdownStatus = 'opened'" @close="dropdownStatus = 'closed'"></vue-timepicker> template(v-slot:preview) b Dropdown Status: I'm {{dropdownStatus}}! p @@ -657,16 +675,18 @@ section#mostlyUsedSamples | Help to identify the focus/blur state of the Timepicker when the dropdown is force hidden by hide-dropdown. template(v-slot:codes) highlight-code(lang="javascript" data-title="JS") - | // Define a variable for logging the status - | data () { - | return { - | focusState: 'blurred' - | } - | } + pre + | // Define a variable for logging the status + | data () { + | return { + | focusState: 'blurred' + | } + | } highlight-code(lang="html" data-title="HTML") - | <p>Focus Status: I'm
{{
focusState
}}
!</p> - |   - | <vue-timepicker manual-input hide-dropdown @focus="focusState = 'focused'" @blur="focusState = 'blurred'"></vue-timepicker> + pre + | <p>Focus Status: I'm {{focusState}}!</p> + |   + | <vue-timepicker manual-input hide-dropdown @focus="focusState = 'focused'" @blur="focusState = 'blurred'"></vue-timepicker> template(v-slot:preview) b Focus State: I'm {{focusState}}! p @@ -679,11 +699,12 @@ section#mostlyUsedSamples | Define customized labels for hour, minute, second, and APM pickers. template(v-slot:codes) highlight-code(lang="html" data-title="HTML") - | <!-- 24-hour format with customized hour and minute labels --> - | <vue-timepicker hour-label="heure" minute-label="minute"></vue-timepicker> - | - | <!-- 12-hour format with customized am/pm text --> - | <vue-timepicker hour-label="時" minute-label="分" second-label="秒" apm-label="午" am-text="上午" pm-text="下午" format="h:mm:ss a"></vue-timepicker> + pre + | <!-- 24-hour format with customized hour and minute labels --> + | <vue-timepicker hour-label="heure" minute-label="minute"></vue-timepicker> + | + | <!-- 12-hour format with customized am/pm text --> + | <vue-timepicker hour-label="時" minute-label="分" second-label="秒" apm-label="午" am-text="上午" pm-text="下午" format="h:mm:ss a"></vue-timepicker> template(v-slot:preview) b 24-hour format with customized hour and minute label p @@ -701,11 +722,12 @@ section#mostlyUsedSamples | and the dropdown picker's width template(v-slot:codes) highlight-code(lang="html" data-title="HTML") - | <!-- In `px` --> - | <vue-timepicker input-width="100px"></vue-timepicker> - | - | <!-- In `em` --> - | <vue-timepicker input-width="12em" format="HH:mm:ss"></vue-timepicker> + pre + | <!-- In `px` --> + | <vue-timepicker input-width="100px"></vue-timepicker> + | + | <!-- In `em` --> + | <vue-timepicker input-width="12em" format="HH:mm:ss"></vue-timepicker> template(v-slot:preview) b In `px` p @@ -720,19 +742,21 @@ section#mostlyUsedSamples p(slot="description") Auto-scroll to selected value on dropdown open. template(v-slot:codes) highlight-code(lang="html" data-title="HTML") - | <!-- Default format --> - | <vue-timepicker auto-scroll v-model="autoScrollData1"></vue-timepicker> - |   - | <!-- 12-hour format --> - | <vue-timepicker auto-scroll format="h:mm:ss a" v-model="autoScrollData2"></vue-timepicker> + pre + | <!-- Default format --> + | <vue-timepicker auto-scroll v-model="autoScrollData1"></vue-timepicker> + |   + | <!-- 12-hour format --> + | <vue-timepicker auto-scroll format="h:mm:ss a" v-model="autoScrollData2"></vue-timepicker> highlight-code(lang="javascript" data-title="JS") - | // Initial values - | data () { - | return { - | autoScrollData1: '08:40', - | autoScrollData2: '5:30:20 pm' - | } - | } + pre + | // Initial values + | data () { + | return { + | autoScrollData1: '08:40', + | autoScrollData2: '5:30:20 pm' + | } + | } template(v-slot:preview) b Default format p diff --git a/package.json b/package.json index be95bf0..d31e3b6 100644 --- a/package.json +++ b/package.json @@ -2,52 +2,39 @@ "name": "vue2-timepicker", "version": "1.1.0", "description": "A dropdown time picker (hour|minute|second) for Vue 2.x, with flexible time format support", + "author": "Phoenix Wong ", "scripts": { - "dev": "cd demo && yarn serve", - "dev:init": "cd demo && yarn install", "build": "vue-cli-service build --target lib --name VueTimepicker ./src/index.js", - "build:demo": "cd demo && yarn build" + "build:demo": "cd demo && yarn build", + "dev": "cd demo && yarn serve", + "dev:init": "cd demo && yarn install" }, "main": "dist/VueTimepicker.umd.js", "module": "dist/VueTimepicker.common.js", - "unpkg": "dist/VueTimepicker.umd.min.js", - "css": "dist/VueTimepicker.css", "browser": { "./sfc": "src/vue-timepicker.vue" }, - "repository": { - "type": "git", - "url": "git+https://github.com/phoenixwong/vue2-timepicker.git" - }, - "keywords": [ - "vue", - "vue2", - "time", - "picker", - "dropdown", - "input", - "UI" - ], - "devDependencies": { - "@vue/cli-plugin-babel": "^3.11.0", - "@vue/cli-plugin-eslint": "^3.11.0", - "@vue/cli-service": "^3.11.0", - "babel-eslint": "^10.0.3", - "core-js": "^2.6.9", - "eslint": "^5.16.0", - "eslint-plugin-vue": "^5.0.0", - "vue": "^2.6.10", - "vue-template-compiler": "^2.6.10" - }, - "peerDependencies": { - "vue": "^2.6.5" - }, + "unpkg": "dist/VueTimepicker.umd.min.js", "files": [ "dist/*", "src/*", "*.json", "*.md" ], + "devDependencies": { + "@vue/cli-plugin-babel": "~4.3.1", + "@vue/cli-plugin-eslint": "~4.3.1", + "@vue/cli-service": "~4.3.1", + "babel-eslint": "^10.1.0", + "core-js": "^3.6.5", + "eslint": "^6.8.0", + "eslint-plugin-vue": "^6.2.2", + "vue": "^2.6.11", + "vue-template-compiler": "^2.6.11" + }, + "peerDependencies": { + "vue": "^2.6.5" + }, "eslintConfig": { "root": true, "env": { @@ -71,10 +58,23 @@ "> 1%", "last 2 versions" ], - "author": "Phoenix Wong ", - "license": "MIT", "bugs": { "url": "https://github.com/phoenixwong/vue2-timepicker/issues" }, - "homepage": "https://github.com/phoenixwong/vue2-timepicker#readme" + "css": "dist/VueTimepicker.css", + "homepage": "https://github.com/phoenixwong/vue2-timepicker#readme", + "keywords": [ + "vue", + "vue2", + "time", + "picker", + "dropdown", + "input", + "UI" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/phoenixwong/vue2-timepicker.git" + } } From 5eb0064a1d5c107dea41d1ae4affe5e22bb98be6 Mon Sep 17 00:00:00 2001 From: Phoenix Wong Date: Sun, 24 May 2020 20:59:09 +0800 Subject: [PATCH 4/6] Codes cleaning up - Rewrite some statements for better performance - Typo and inconsistent variable names fixes --- src/vue-timepicker.vue | 248 ++++++++++++++++------------------------- 1 file changed, 96 insertions(+), 152 deletions(-) diff --git a/src/vue-timepicker.vue b/src/vue-timepicker.vue index 1c35478..0859538 100644 --- a/src/vue-timepicker.vue +++ b/src/vue-timepicker.vue @@ -3,7 +3,8 @@ const CONFIG = { HOUR_TOKENS: ['HH', 'H', 'hh', 'h', 'kk', 'k'], MINUTE_TOKENS: ['mm', 'm'], SECOND_TOKENS: ['ss', 's'], - APM_TOKENS: ['A', 'a'] + APM_TOKENS: ['A', 'a'], + BASIC_TYPES: ['hour', 'minute', 'second', 'apm'] } const DEFAULT_OPTIONS = { @@ -561,16 +562,16 @@ export default { if (!this.restrictedHourRange && !this.minuteRangeList && !this.secondRangeList && this.opts.minuteInterval === 1 && this.opts.secondInterval === 1) { return [] } const result = [] - if (!this.isEmptyValue(this.hourType, this.hour) && (!this.isValidValue(this.hourType, this.hour) || this.isDisabledHour(this.hour))) { + if (!this.isEmptyValue(this.hourType, this.hour) && (!this.isValidValue(this.hourType, this.hour) || this.isDisabled('hour', this.hour))) { result.push('hour') } - if (!this.isEmptyValue(this.minuteType, this.minute) && (!this.isValidValue(this.minuteType, this.minute) || this.isDisabledMinute(this.minute) || this.notInMinuteInterval(this.minute))) { + if (!this.isEmptyValue(this.minuteType, this.minute) && (!this.isValidValue(this.minuteType, this.minute) || this.isDisabled('minute', this.minute) || this.notInMinuteInterval(this.minute))) { result.push('minute') } - if (this.secondType && !this.isEmptyValue(this.secondType, this.second) && (!this.isValidValue(this.secondType, this.second) || this.isDisabledSecond(this.second) || this.notInSecondInterval(this.second))) { + if (this.secondType && !this.isEmptyValue(this.secondType, this.second) && (!this.isValidValue(this.secondType, this.second) || this.isDisabled('second', this.second) || this.notInSecondInterval(this.second))) { result.push('second') } - if (this.apmType && !this.isEmptyValue(this.apmType, this.apm) && (!this.isValidValue(this.apmType, this.apm) || this.isDisabledApm(this.apm))) { + if (this.apmType && !this.isEmptyValue(this.apmType, this.apm) && (!this.isValidValue(this.apmType, this.apm) || this.isDisabled('apm', this.apm))) { result.push('apm') } if (result.length) { @@ -623,31 +624,21 @@ export default { i = +i switch (token) { case 'H': + case 'h': + case 'k': case 'm': case 's': + if (['h', 'k'].includes(token) && i === 0) { + return token === 'k' ? '24' : '12' + } return String(i) case 'HH': case 'mm': case 'ss': - return i < 10 ? `0${i}` : String(i) - case 'h': - if (i === 0) { - return '12' - } - return String(i) - case 'k': - if (i === 0) { - return '24' - } - return String(i) case 'hh': - if (i === 0) { - return '12' - } - return i < 10 ? `0${i}` : String(i) case 'kk': - if (i === 0) { - return '24' + if (['hh', 'kk'].includes(token) && i === 0) { + return token === 'kk' ? '24' : '12' } return i < 10 ? `0${i}` : String(i) default: @@ -707,36 +698,21 @@ export default { }, renderList (listType, interval) { - if (!listType || (listType !== 'minute' && listType !== 'second')) { return } + if (!['minute','second'].includes(listType)) { return } - if (listType === 'minute') { - interval = interval || this.opts.minuteInterval || DEFAULT_OPTIONS.minuteInterval - } else { - interval = interval || this.opts.secondInterval || DEFAULT_OPTIONS.secondInterval - } + const isMinute = listType === 'minute' + interval = interval || (isMinute ? (this.opts.minuteInterval || DEFAULT_OPTIONS.minuteInterval) : (this.opts.secondInterval || DEFAULT_OPTIONS.secondInterval)) const result = [] for (let i = 0; i < 60; i += interval) { - if (listType === 'minute') { - result.push(this.formatValue(this.minuteType, i)) - } else { - result.push(this.formatValue(this.secondType, i)) - } + result.push(this.formatValue(isMinute ? this.minuteType : this.secondType, i)) } - if (listType === 'minute') { - this.minutes = result - } else { - this.seconds = result - } + isMinute ? this.minutes = result : this.seconds = result }, renderApmList () { - let apms = [] - if (this.apmType) { - apms = this.apmType === 'A' ? ['AM', 'PM'] : ['am', 'pm'] - } - this.apms = apms + this.apms = this.apmType === 'A' ? ['AM', 'PM'] : ['am', 'pm'] }, readValues () { @@ -763,7 +739,7 @@ export default { return } - ['hour', 'minute', 'second', 'apm'].forEach(section => { + CONFIG.BASIC_TYPES.forEach(section => { const sectionType = this[`${section}Type`] if (values.indexOf(sectionType) > -1) { const sanitizedValue = this.sanitizedValue(sectionType, timeValue[sectionType]) @@ -897,27 +873,12 @@ export default { this.timeValue = timeValue }, - setValueFromString (parsedValue, tokenType) { - if (!tokenType || !parsedValue) { return '' } - let stdValue = '' - switch (tokenType) { - case `${this.hourType}`: - stdValue = (parsedValue !== this.hourType) ? parsedValue : '' - this.hour = stdValue - break - case `${this.minuteType}`: - stdValue = (parsedValue !== this.minuteType) ? parsedValue : '' - this.minute = stdValue - break - case `${this.secondType}`: - stdValue = (parsedValue !== this.secondType) ? parsedValue : '' - this.second = stdValue - break - case `${this.apmType}`: - stdValue = (parsedValue !== this.apmType) ? parsedValue : '' - this.apm = stdValue - break - } + setValueFromString (parsedValue, token) { + if (!token || !parsedValue) { return '' } + const tokenType = this.getTokenType(token) + if (!tokenType || !tokenType.length) { return '' } + const stdValue = (parsedValue !== this[`${tokenType}Type`]) ? parsedValue : '' + this[tokenType] = stdValue return stdValue }, @@ -928,7 +889,7 @@ export default { const baseHourType = this.hourType const hourValue = this.isNumber(baseHour) ? +baseHour : '' - const apmValue = (this.baseOn12Hours && this.apm) ? String(this.apm).toLowerCase() : false + const apmValue = (this.baseOn12Hours && this.apm) ? this.lowerCasedApm(this.apm) : false CONFIG.HOUR_TOKENS.forEach((token) => { if (token === baseHourType) { @@ -1079,6 +1040,27 @@ export default { return value >= 12 && value < 24 }, + isDisabled (type, value) { + if (!this.isBasicType(type)) { return true } + switch (type) { + case 'hour': + return this.isDisabledHour(value) + case 'minute': + case 'second': + if (!this[`${type}RangeList`]) { + return false + } + return !this[`${type}RangeList`].includes(value) + case 'apm': + if (!this.restrictedHourRange) { + return false + } + return !this.has[this.lowerCasedApm(value)] + default: + return true + } + }, + isDisabledHour (value) { if (!this.restrictedHourRange) { return false } if (this.baseOn12Hours) { @@ -1099,21 +1081,6 @@ export default { return !this.restrictedHourRange.includes(+value) }, - isDisabledMinute (value) { - if (!this.minuteRangeList) { return false } - return !this.minuteRangeList.includes(value) - }, - - isDisabledSecond (value) { - if (!this.secondRangeList) { return false } - return !this.secondRangeList.includes(value) - }, - - isDisabledApm (value) { - if (!this.restrictedHourRange) { return false } - return !this.has[(value || '').toLowerCase()] - }, - notInMinuteInterval (value) { if (this.opts.minuteInterval === 1) { return false } return +value % this.opts.minuteInterval !== 0 @@ -1146,10 +1113,10 @@ export default { }, apmDisplayText (apmValue) { - if (this.amText && (apmValue || '').toLowerCase() === 'am') { + if (this.amText && this.lowerCasedApm(apmValue) === 'am') { return this.amText } - if (this.pmText && (apmValue || '').toLowerCase() === 'pm') { + if (this.pmText && this.lowerCasedApm(apmValue) === 'pm') { return this.pmText } return apmValue @@ -1193,18 +1160,8 @@ export default { }, select (type, value) { - if (type === 'hour') { - if (this.isDisabledHour(value)) { return } - this.hour = value - } else if (type === 'minute') { - if (this.isDisabledMinute(value)) { return } - this.minute = value - } else if (type === 'second') { - if (this.isDisabledSecond(value)) { return } - this.second = value - } else if (type === 'apm') { - if (this.isDisabledApm(value)) { return } - this.apm = value + if (this.isBasicType(type) && !this.isDisabled(type, value)) { + this[type] = value } }, @@ -1344,25 +1301,17 @@ export default { return siblingsInCol[selfIndex + 1] }, - prevItem (columnClass, dataKey, isManualInput) { + prevItem (columnClass, dataKey, isManualInput = false) { const targetItem = this.getClosestSibling(columnClass, dataKey, true) if (targetItem) { - if (isManualInput) { - return targetItem - } else { - targetItem.focus() - } + return isManualInput ? targetItem : targetItem.focus() } }, - nextItem (columnClass, dataKey, isManualInput) { + nextItem (columnClass, dataKey, isManualInput = false) { const targetItem = this.getClosestSibling(columnClass, dataKey, false) if (targetItem) { - if (isManualInput) { - return targetItem - } else { - targetItem.focus() - } + return isManualInput ? targetItem : targetItem.focus() } }, @@ -1473,7 +1422,7 @@ export default { this.selectionTimer = window.setTimeout(() => { window.clearTimeout(this.selectionTimer) if (this.$refs && this.$refs.input) { - const nearestSlot = this.getNearesChunkByPos(this.$refs.input.selectionStart || 0) + const nearestSlot = this.getNearestChunkByPos(this.$refs.input.selectionStart || 0) this.debounceSetInputSelection(nearestSlot) } }, 50) @@ -1534,11 +1483,7 @@ export default { this.setSanitizedValueToSection('apm', inputIsCustomApmText) } - if (this.has.customApmText) { - this.$refs.input.value = this.customDisplayTime - } else { - this.$refs.input.value = this.displayTime - } + this.$refs.input.value = this.has.customApmText ? this.customDisplayTime : this.displayTime this.$nextTick(() => { if (this.bakCurrentPos) { @@ -1639,9 +1584,9 @@ export default { let validValue if (chunkType === 'apm') { - if ((value || '').toLowerCase().includes('a')) { + if (this.lowerCasedApm(value).includes('a')) { validValue = 'am' - } else if ((value || '').toLowerCase().includes('p')) { + } else if (this.lowerCasedApm(value).includes('p')) { validValue = 'pm' } if (validValue) { @@ -1665,9 +1610,9 @@ export default { } if (this.debugMode) { if (validValue) { - this.debugLog(`Successfully set value "${validValue}" from latest input "${value}" for column "${chunkType}"`) + this.debugLog(`Successfully set value "${validValue}" from latest input "${value}" for the "${chunkType}" slot`) } else { - this.debugLog(`Value "${value}" is invalid in the "${chunkType}" column`) + this.debugLog(`Value "${value}" is invalid in the "${chunkType}" slot`) } } }, @@ -1681,7 +1626,7 @@ export default { } }, - getNearesChunkByPos (startPos) { + getNearestChunkByPos (startPos) { if (!this.tokenChunksPos || !this.tokenChunksPos.length) { return } let nearest let nearestDelta = -1 @@ -1745,7 +1690,7 @@ export default { if (!this.baseOn12Hours) { return item === currentValue } else { - const valueKey = `${currentValue}${(this.apm || '').toLowerCase() === 'pm' ? 'p' : 'a'}` + const valueKey = `${currentValue}${this.lowerCasedApm(this.apm) === 'pm' ? 'p' : 'a'}` return item === valueKey } }) @@ -1806,7 +1751,7 @@ export default { }, getCurrentTokenChunk () { - return this.getNearesChunkByPos((this.$refs.input && this.$refs.input.selectionStart) || 0) + return this.getNearestChunkByPos((this.$refs.input && this.$refs.input.selectionStart) || 0) }, getChunkPosByToken (token) { @@ -1868,6 +1813,14 @@ export default { return !isNaN(parseFloat(value)) && isFinite(value) }, + isBasicType (type) { + return CONFIG.BASIC_TYPES.includes(type) + }, + + lowerCasedApm (apmValue) { + return (apmValue || '').toLowerCase() + }, + getTokenRegex (token) { switch (token) { case 'HH': @@ -1918,18 +1871,9 @@ export default { }, getTokenType (token) { - switch (token) { - case `${this.hourType}`: - return 'hour' - case `${this.minuteType}`: - return 'minute' - case `${this.secondType}`: - return 'second' - case `${this.apmType}`: - return 'apm' - default: - return '' - } + const typesInUse = CONFIG.BASIC_TYPES.filter(tokenType => this[`${tokenType}Type`]) + const activeTokens = typesInUse.map(tokenType => this[`${tokenType}Type`]) + return typesInUse[activeTokens.indexOf(token)] || '' }, debugLog (logText) { @@ -2015,10 +1959,10 @@ export default {