@afeefa/vue-app 0.0.132 → 0.0.133

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- 0.0.132
1
+ 0.0.133
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afeefa/vue-app",
3
- "version": "0.0.132",
3
+ "version": "0.0.133",
4
4
  "description": "",
5
5
  "author": "Afeefa Kollektiv <kollektiv@afeefa.de>",
6
6
  "license": "MIT",
@@ -1,38 +1,44 @@
1
1
  <template>
2
- <v-menu
3
- v-model="menu"
4
- :close-on-content-click="false"
5
- transition="scale-transition"
6
- offset-y
7
- min-width="auto"
8
- >
9
- <template #activator="{ on, attrs }">
10
- <v-text-field
11
- ref="input"
12
- :value="formattedDate"
13
- :label="label"
14
- :style="cwm_widthStyle"
15
- v-bind="{...$attrs, ...attrs, dense, outlined}"
16
- :rules="validationRules"
17
- :error-messages="errorMessages"
18
- :readonly="type === 'month'"
19
- v-on="on"
20
- @input="dateInputChanged"
21
- @click:clear="clearDate"
22
- @click.native="on.click"
23
- />
24
- </template>
25
-
26
- <v-date-picker
27
- v-if="menu"
28
- :value="date"
29
- no-title
30
- :type="type"
31
- v-bind="$attrs"
32
- :first-day-of-week="1"
33
- @input="dateChanged"
2
+ <div :class="['a-date-picker', {'type--month': type === 'month'}]">
3
+ <v-text-field
4
+ ref="input"
5
+ :value="formattedDate"
6
+ :label="label"
7
+ :placeholder="type === 'month' ? '' : 'DD.MM.JJJJ'"
8
+ :style="cwm_widthStyle"
9
+ v-bind="{...$attrs, dense, outlined}"
10
+ :error-messages="errorMessages"
11
+ append-icon="$calendarIcon"
12
+ :readonly="type === 'month'"
13
+ :clearable="clearable"
14
+ @input="dateInputChanged"
15
+ @click:clear="clearDate"
16
+ @click:append="open"
17
+ @mouseup="openIfMonth"
34
18
  />
35
- </v-menu>
19
+
20
+ <v-overlay
21
+ :value="isOpen"
22
+ :z-index="299"
23
+ :opacity="0"
24
+ />
25
+
26
+ <div :class="popUpCssClass">
27
+ <div
28
+ v-if="isOpen"
29
+ class="popUpContent elevation-6"
30
+ >
31
+ <v-date-picker
32
+ :value="date"
33
+ no-title
34
+ :type="type"
35
+ v-bind="$attrs"
36
+ :first-day-of-week="1"
37
+ @input="dateChanged"
38
+ />
39
+ </div>
40
+ </div>
41
+ </div>
36
42
  </template>
37
43
 
38
44
 
@@ -40,22 +46,22 @@
40
46
  import { Component, Mixins, Watch } from '@a-vue'
41
47
  import { formatDate } from '@a-vue/utils/format-date'
42
48
  import { ComponentWidthMixin } from './mixins/ComponentWidthMixin'
49
+ import { UsesPositionServiceMixin } from '../services/position/UsesPositionServiceMixin'
50
+ import { CancelOnEscMixin } from '@a-vue/services/escape/CancelOnEscMixin'
51
+ import { PositionConfig } from '../services/PositionService'
52
+ import { randomCssClass } from '../utils/random'
43
53
 
44
54
  @Component({
45
55
  props: ['value', 'validator', 'type', {dense: true, outlined: true}]
46
56
  })
47
- export default class ADatePicker extends Mixins(ComponentWidthMixin) {
57
+ export default class ADatePicker extends Mixins(ComponentWidthMixin, UsesPositionServiceMixin, CancelOnEscMixin) {
48
58
  value_ = null
49
- menu = false
50
59
  errorMessages = []
60
+ popUpId = randomCssClass(10)
61
+ isOpen = false
51
62
 
52
63
  created () {
53
- if (this.value) {
54
- this.value_ = this.value
55
- } else {
56
- this.value_Changed() // force validation if date is null
57
- }
58
- this.$emit('input', this.value_)
64
+ this.value_ = this.value
59
65
  }
60
66
 
61
67
  mounted () {
@@ -65,27 +71,112 @@ export default class ADatePicker extends Mixins(ComponentWidthMixin) {
65
71
  @Watch('value')
66
72
  valueChanged () {
67
73
  this.value_ = this.value
74
+ this.validate()
68
75
  }
69
76
 
70
- @Watch('value_')
71
- value_Changed () { // validate on any change
72
- this.validateDateValue()
77
+ get clearable () {
78
+ if (this.validator && this.validator.param('null') === false) {
79
+ return false
80
+ }
81
+ return true
73
82
  }
74
83
 
75
- validateDateValue () {
76
- this.errorMessages = []
77
- if (this.validator) {
78
- const rules = this.validator.getRules(this.label)
79
- for (const rule of rules) {
80
- const message = rule(this.value_)
81
- if (typeof message === 'string') {
82
- this.errorMessages.push(message)
83
- break
84
- }
85
- }
84
+ openIfMonth () {
85
+ if (this.type === 'month') {
86
+ this.open()
86
87
  }
87
88
  }
88
89
 
90
+ open () {
91
+ window.addEventListener('mousedown', this.onClickOutside)
92
+
93
+ const container = this.getContainer()
94
+
95
+ container.appendChild(this.popUp)
96
+ this.positionize()
97
+
98
+ this.isOpen = true
99
+
100
+ this.coe_watchCancel()
101
+
102
+ this.$emit('open')
103
+ }
104
+
105
+ close () {
106
+ if (!this.isOpen) {
107
+ return
108
+ }
109
+
110
+ this.urp_unregisterPositionWatchers()
111
+
112
+ window.removeEventListener('mousedown', this.onClickOutside)
113
+
114
+ this.$el.appendChild(this.popUp)
115
+
116
+ this.isOpen = false
117
+
118
+ this.coe_unwatchCancel()
119
+
120
+ this.$emit('close')
121
+ }
122
+
123
+ positionize () {
124
+ const position = new PositionConfig()
125
+ .setAnchor(this.$refs.input, '.v-input__slot')
126
+ .setTarget(
127
+ document.documentElement,
128
+ '.' + this.popUpCssClass + ' .popUpContent'
129
+ )
130
+ .targetTop()
131
+ .anchorBottom()
132
+ .diffY('.5rem')
133
+ .alternativeY(p => {
134
+ p.targetBottom().anchorTop().diffY('-.5rem')
135
+ })
136
+
137
+ this.urp_registerPositionWatcher(position)
138
+ }
139
+
140
+ get popUpCssClass () {
141
+ return 'popUp-' + this.popUpId
142
+ }
143
+
144
+ getContainer () {
145
+ return document.querySelector('.v-application')
146
+ }
147
+
148
+ get popUp () {
149
+ const container = this.getContainer()
150
+ return container.querySelector('.' + this.popUpCssClass)
151
+ }
152
+
153
+ coe_cancelOnEsc () {
154
+ this.close()
155
+ return false // stop esc propagation
156
+ }
157
+
158
+ onClickOutside (e) {
159
+ const popUp = this.popUp
160
+
161
+ // no popup present
162
+ if (!popUp) {
163
+ return
164
+ }
165
+
166
+ // popup clicked
167
+ if (popUp.contains(e.target)) {
168
+ return
169
+ }
170
+
171
+ // activator clicked
172
+ const activator = this.$refs.input.$el
173
+ if (activator && activator.contains(e.target)) {
174
+ return
175
+ }
176
+
177
+ this.close()
178
+ }
179
+
89
180
  get date () {
90
181
  return this.value_
91
182
  ? this.value_.toISOString().substr(0, this.type === 'month' ? 7 : 10)
@@ -98,25 +189,28 @@ export default class ADatePicker extends Mixins(ComponentWidthMixin) {
98
189
 
99
190
  clearDate () {
100
191
  this.value_ = null
192
+ this.validate()
101
193
  this.$emit('input', this.value_)
102
194
  }
103
195
 
104
- isDate (value) {
196
+ textInputIsDate (value) {
105
197
  return value && value.match(/^(3[01]|[12][0-9]|0?[1-9]).(1[012]|0?[1-9]).((?:19|20)\d{2})$/)
106
198
  }
107
199
 
108
200
  dateInputChanged (value) {
109
201
  if (!value) {
110
202
  this.dateChanged(null)
111
- } else if (this.isDate(value)) {
203
+ } else if (this.validateTextInput(value)) {
112
204
  const [day, month, year] = value.split('.')
113
205
  this.dateChanged(new Date(year + '-' + month + '-' + day))
114
206
  }
115
207
  }
116
208
 
117
209
  dateChanged (date) {
118
- this.menu = false
119
210
  this.value_ = date ? new Date(date) : null
211
+ this.validate()
212
+
213
+ this.close()
120
214
  this.$emit('input', this.value_)
121
215
  }
122
216
 
@@ -133,31 +227,68 @@ export default class ADatePicker extends Mixins(ComponentWidthMixin) {
133
227
  }
134
228
 
135
229
  validate () {
136
- this.validateDateValue()
230
+ this.errorMessages = []
231
+ if (this.validator) {
232
+ const rules = this.validator.getRules(this.label)
233
+ for (const rule of rules) {
234
+ const message = rule(this.value_)
235
+ if (typeof message === 'string') {
236
+ this.errorMessages.push(message)
237
+ break
238
+ }
239
+ }
240
+ }
137
241
  }
138
242
 
139
- get validationRules () {
140
- if (this.type !== 'month') {
141
- const dateFormat = v => {
142
- if (v && !this.isDate(v)) {
143
- return 'Das Datum sollte das Format TT.MM.JJJJ haben.'
144
- }
145
- return true
243
+ validateTextInput (value) {
244
+ this.errorMessages = []
245
+ const rules = this.textInputValidationRules
246
+ for (const rule of rules) {
247
+ const message = rule(value)
248
+ if (typeof message === 'string') {
249
+ this.errorMessages.push(message)
250
+ break
146
251
  }
147
- return [dateFormat]
148
252
  }
253
+ return !this.errorMessages.length
254
+ }
255
+
256
+ get textInputValidationRules () {
257
+ if (this.type === 'month') {
258
+ return []
259
+ }
260
+
261
+ return [v => {
262
+ if (v && !this.textInputIsDate(v)) {
263
+ return 'Das Datum sollte das Format TT.MM.JJJJ haben.'
264
+ }
265
+ return true
266
+ }]
267
+ }
268
+
269
+ get dateValidationRules () {
270
+ if (this.validator) {
271
+ return this.validator.getRules(this.label)
272
+ }
273
+ return []
149
274
  }
150
275
  }
151
276
  </script>
152
277
 
153
278
 
154
279
  <style lang="scss" scoped>
155
- :deep(.v-select__slot) {
156
- cursor: pointer;
280
+ .popUpContent {
281
+ min-height: 2.2rem;
282
+ position: absolute;
283
+ z-index: 400;
284
+ display: block;
285
+ background-color: white;
286
+ padding: .5rem;
287
+ transition: left .2s;
288
+ }
157
289
 
158
- input {
159
- cursor: pointer;
160
- }
290
+ .a-date-picker.type--month :deep(input) {
291
+ cursor: pointer;
161
292
  }
162
293
 
163
294
  .v-text-field :deep(.v-input__icon--clear) { // always show clear icon, https://github.com/vuetifyjs/vuetify/pull/15876
@@ -41,7 +41,6 @@
41
41
  <edit-form-buttons
42
42
  :changed="changed"
43
43
  :valid="valid"
44
- small
45
44
  angular
46
45
  :has="{reset: !!modelToEdit.id}"
47
46
  @save="$emit('save', modelToEdit, ignoreChangesOnClose, close)"