Skip to content

Commit eddb775

Browse files
phre1EkaterinaVuspinn
authored
Feature/date input field (#132)
* Add date input field * Add mask presets + fix iOS issues * Faux iOS date placeholder styles * Export MaskPresets in npm module Co-authored-by: EkaterinaVu <ekaterina.vujasinovic@gmail.com> Co-authored-by: dennis <dennis@ditdot.hr>
1 parent bd570f7 commit eddb775

File tree

11 files changed

+138
-17
lines changed

11 files changed

+138
-17
lines changed

src/assets/css/common.css

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ header.vff-header svg.f-logo {
355355
.vff input[type=email],
356356
.vff input[type=url],
357357
.vff input[type=password],
358+
.vff input[type=date],
358359
.vff textarea {
359360
-webkit-appearance: none;
360361
-moz-appearance: none;
@@ -384,6 +385,7 @@ header.vff-header svg.f-logo {
384385
.vff .f-full-width input[type=email],
385386
.vff .f-full-width input[type=url],
386387
.vff .f-full-width input[type=password],
388+
.vff .f-full-width input[type=date],
387389
.vff .f-full-width textarea,
388390
.vff .f-full-width span.faux-form{
389391
width: 100%;
@@ -437,6 +439,27 @@ header.vff-header svg.f-logo {
437439
height: auto;
438440
}
439441

442+
/* ios datepicker */
443+
.vff.vff-is-ios .field-datetype:not(.f-has-value) .f-answer > span {
444+
position: relative;
445+
top: 0;
446+
left: 0;
447+
}
448+
449+
.vff.vff-is-ios .field-datetype:not(.f-has-value) .f-answer > span::before {
450+
position: absolute;
451+
content: attr(data-placeholder);
452+
display: block;
453+
pointer-events: none;
454+
padding: 0.12em .2em;
455+
}
456+
457+
.vff.vff-is-ios input[type=date] {
458+
height: 32px;
459+
display: block;
460+
}
461+
462+
440463
/*
441464
links
442465
*/
@@ -825,6 +848,7 @@ header.vff-header svg.f-logo {
825848
.vff input[type=email],
826849
.vff input[type=url],
827850
.vff input[type=password],
851+
.vff input[type=date],
828852
.vff textarea{
829853
font-size: .78em;
830854
}
@@ -870,6 +894,7 @@ header.vff-header svg.f-logo {
870894
.vff input[type=email],
871895
.vff input[type=url],
872896
.vff input[type=password],
897+
.vff input[type=date],
873898
.vff textarea {
874899
line-height: 1.4;
875900
padding: .16em .2em;

src/assets/css/themes/theme-green.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ header.vff-header svg.f-logo {
6969
.vff input[type=email],
7070
.vff input[type=url],
7171
.vff input[type=password],
72+
.vff input[type=date],
7273
.vff textarea,
7374
.vff span.faux-form {
7475
border-bottom-color: var(--vff-secondary-text-color);
@@ -104,6 +105,14 @@ header.vff-header svg.f-logo {
104105
font-weight: 300;
105106
}
106107

108+
109+
/*faux input type date placeholder for iOS*/
110+
.vff.vff-is-ios .field-datetype:not(.f-has-value) .f-answer > span::before{
111+
color: var(--vff-secondary-text-color);
112+
font-weight: 300;
113+
font-size: .72em;
114+
}
115+
107116
/*field-multiplechoicetype*/
108117
.vff ul.f-radios li {
109118
border: 1px solid var(--vff-secondary-text-color);

src/assets/css/themes/theme-minimal.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ header.vff-header svg.f-logo{
5757
.vff input[type=email],
5858
.vff input[type=url],
5959
.vff input[type=password],
60+
.vff input[type=date],
6061
.vff textarea,
6162
.vff span.faux-form {
6263
border-bottom-color: var(--vff-secondary-text-color);
@@ -98,6 +99,13 @@ header.vff-header svg.f-logo{
9899
font-weight: 300;
99100
}
100101

102+
/*faux input type date placeholder for iOS*/
103+
.vff.vff-is-ios .field-datetype:not(.f-has-value) .f-answer > span::before{
104+
color: var(--vff-secondary-text-color);
105+
font-weight: 300;
106+
font-size: .72em;
107+
}
108+
101109
/*field-multiplechoicetype*/
102110
.vff ul.f-radios li {
103111
border: 1px solid var(--vff-secondary-text-color);

src/assets/css/themes/theme-purple.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ header.vff-header svg.f-logo{
7373
.vff input[type=email],
7474
.vff input[type=url],
7575
.vff input[type=password],
76+
.vff input[type=date],
7677
.vff textarea,
7778
.vff span.faux-form {
7879
border-bottom-color: var(--vff-secondary-text-color);
@@ -114,6 +115,13 @@ header.vff-header svg.f-logo{
114115
font-weight: 300;
115116
}
116117

118+
/*faux input type date placeholder for iOS*/
119+
.vff.vff-is-ios .field-datetype:not(.f-has-value) .f-answer > span::before{
120+
color: var(--vff-secondary-text-color);
121+
font-weight: 300;
122+
font-size: .72em;
123+
}
124+
117125
/*field-multiplechoicetype*/
118126
.vff ul.f-radios li {
119127
border: 1px solid var(--vff-secondary-form-bg-color);

src/components/FlowForm.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Form template and logic
22

33
<template>
4-
<div class="vff" :class="{'vff-not-standalone': !standalone}">
4+
<div class="vff" :class="{'vff-not-standalone': !standalone, 'vff-is-mobile': isMobile, 'vff-is-ios': isIos}">
55
<div class="f-container">
66
<div class="f-form-wrap">
77
<flow-form-question
@@ -126,6 +126,7 @@
126126
127127
import FlowFormQuestion from './Question.vue'
128128
import LanguageModel from '../models/LanguageModel'
129+
import { IsMobile } from '../mixins/IsMobile'
129130
130131
export default {
131132
name: 'FlowForm',
@@ -147,6 +148,9 @@
147148
default: true
148149
}
149150
},
151+
mixins: [
152+
IsMobile,
153+
],
150154
data() {
151155
return {
152156
completed: false,

src/components/Question.vue

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
import FlowFormSectionBreakType from './QuestionTypes/SectionBreakType.vue'
107107
import FlowFormTextType from './QuestionTypes/TextType.vue'
108108
import FlowFormUrlType from './QuestionTypes/UrlType.vue'
109+
import FlowFormDateType from './QuestionTypes/DateType.vue'
109110
import { IsMobile } from '../mixins/IsMobile'
110111
111112
@@ -121,7 +122,8 @@
121122
FlowFormPhoneType,
122123
FlowFormSectionBreakType,
123124
FlowFormTextType,
124-
FlowFormUrlType
125+
FlowFormUrlType,
126+
FlowFormDateType
125127
},
126128
props: {
127129
question: QuestionModel,
@@ -239,17 +241,22 @@
239241
}
240242
},
241243
computed: {
242-
mainClasses() {
243-
const classes = {
244-
'q-is-active': this.active,
245-
'q-is-inactive': !this.active,
246-
'f-fade-in-down': this.reverse,
247-
'f-fade-in-up': !this.reverse
248-
}
244+
mainClasses: {
245+
cache: false,
246+
get() {
247+
const classes = {
248+
'q-is-active': this.active,
249+
'q-is-inactive': !this.active,
250+
'f-fade-in-down': this.reverse,
251+
'f-fade-in-up': !this.reverse,
252+
'f-focused': this.$refs.questionComponent && this.$refs.questionComponent.focused,
253+
'f-has-value': this.$refs.questionComponent && this.$refs.questionComponent.hasValue
254+
}
249255
250-
classes['field-' + this.question.type.toLowerCase().substring(8)] = true
256+
classes['field-' + this.question.type.toLowerCase().substring(8)] = true
251257
252-
return classes
258+
return classes
259+
}
253260
},
254261
255262
showHelperText() {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<script>
2+
/*
3+
Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence
4+
https://github.com/ditdot-dev/vue-flow-form
5+
https://www.ditdot.hr/en
6+
*/
7+
8+
import TextType from './TextType.vue'
9+
import { QuestionType } from '../../models/QuestionModel'
10+
import LanguageModel from '../../models/LanguageModel'
11+
12+
export default {
13+
extends: TextType,
14+
name: QuestionType.Date,
15+
data() {
16+
return {
17+
inputType: 'date',
18+
canReceiveFocus: true
19+
}
20+
},
21+
methods: {
22+
validate() {
23+
if (this.question.min && this.dataValue < this.question.min) {
24+
return false
25+
}
26+
27+
if (this.question.max && this.dataValue > this.question.max) {
28+
return false
29+
}
30+
31+
return !this.question.required || this.hasValue
32+
}
33+
}
34+
}
35+
</script>

src/components/QuestionTypes/TextType.vue

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<span>
2+
<span v-bind:data-placeholder="inputType === 'date' ? placeholder : null">
33
<the-mask
44
v-if="question.mask"
55
ref="input"
@@ -15,6 +15,10 @@
1515
v-on:keyup.native.enter.prevent="onEnter"
1616
v-on:keyup.native.tab.prevent="onEnter"
1717
v-bind:placeholder="placeholder"
18+
v-bind:min="question.min"
19+
v-bind:max="question.max"
20+
v-on:change="onChange"
21+
v-bind:tokens="tokens"
1822
/>
1923
<input
2024
v-else
@@ -28,6 +32,9 @@
2832
v-on:keyup.tab.prevent="onEnter"
2933
v-on:focus="setFocus"
3034
v-on:blur="unsetFocus"
35+
v-bind:min="question.min"
36+
v-bind:max="question.max"
37+
v-on:change="onChange"
3138
v-bind:placeholder="placeholder"
3239
/>
3340
</span>
@@ -51,12 +58,14 @@
5158
components: {
5259
TheMask
5360
},
61+
5462
data() {
5563
return {
5664
inputType: 'text',
5765
canReceiveFocus: true
5866
}
5967
},
68+
6069
methods: {
6170
validate() {
6271
if (this.question.mask && this.dataValue.length !== this.question.mask.length) {

src/main.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Import vue component
22
import FlowForm from './components/FlowForm.vue'
3-
import QuestionModel, { QuestionType, ChoiceOption, LinkOption } from './models/QuestionModel'
3+
import QuestionModel, { QuestionType, ChoiceOption, LinkOption, MaskPresets } from './models/QuestionModel'
44
import LanguageModel from './models/LanguageModel'
55

66
// IE11 Object.assign polyfill
@@ -37,5 +37,6 @@ export {
3737
QuestionType,
3838
ChoiceOption,
3939
LinkOption,
40-
LanguageModel
40+
LanguageModel,
41+
MaskPresets
4142
}

src/mixins/IsMobile.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
*/
66

77
// Simple mobile device/tablet detection
8-
const isMobile = !!(typeof navigator !== 'undefined' && navigator.userAgent.match(/android|iphone|ipad|ipod/i))
8+
const isIos = navigator.userAgent.match(/iphone|ipad|ipod/i) || (navigator.userAgent.indexOf("Mac") !== -1 && "ontouchend" in document)
9+
const isMobile = !!(typeof navigator !== 'undefined' && (isIos || navigator.userAgent.match(/android/i)))
910

1011
// Mixin that adds an `isMobile` data variable
1112
export const IsMobile = {
1213
data() {
1314
return {
15+
isIos,
1416
isMobile
1517
}
1618
}

0 commit comments

Comments
 (0)