@afeefa/vue-app 0.0.74 → 0.0.77

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.74
1
+ 0.0.77
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afeefa/vue-app",
3
- "version": "0.0.74",
3
+ "version": "0.0.77",
4
4
  "description": "",
5
5
  "author": "Afeefa Kollektiv <kollektiv@afeefa.de>",
6
6
  "license": "MIT",
@@ -18,7 +18,7 @@ export class SaveAction extends ApiAction {
18
18
 
19
19
  processError (result) {
20
20
  eventBus.dispatch(new AlertEvent(AlertEvent.ERROR, {
21
- headline: 'Die Daten konntent nicht gespeichert werden.',
21
+ headline: 'Die Daten konnten nicht gespeichert werden.',
22
22
  message: result.message,
23
23
  detail: result.detail
24
24
  }))
@@ -17,9 +17,10 @@
17
17
  <h3>{{ _headline }}</h3>
18
18
 
19
19
  <div v-html="message" />
20
+
20
21
  <div
21
22
  v-if="detail"
22
- class="pt-3"
23
+ class="pt-1 details"
23
24
  v-html="detail"
24
25
  />
25
26
  </div>
@@ -126,7 +127,12 @@ export default class AAlert extends Vue {
126
127
 
127
128
  h3 {
128
129
  margin-bottom: .5rem;
129
- font-size: 1.4rem;
130
+ font-size: 1.2rem;
131
+ line-height: 1.2;
132
+ }
133
+
134
+ .details {
135
+ font-size: .9rem;
130
136
  }
131
137
 
132
138
  .closeButton {
@@ -5,8 +5,9 @@
5
5
  :contentClass="_dialogId"
6
6
  transition="v-fade-transition"
7
7
  v-bind="$attrs"
8
+ persistent
9
+ no-click-animation
8
10
  @click:outside="cancel"
9
- @keydown.esc="cancel"
10
11
  >
11
12
  <template #activator="{ on }">
12
13
  <div v-on="on">
@@ -18,7 +19,7 @@
18
19
  v-if="dialog"
19
20
  class="pb-1"
20
21
  >
21
- <v-card-title>
22
+ <v-card-title class="pa-3">
22
23
  {{ title }}
23
24
  </v-card-title>
24
25
 
@@ -26,7 +27,7 @@
26
27
  <span v-html="message" />
27
28
  </v-card-text>
28
29
 
29
- <v-card-text>
30
+ <v-card-text v-if="$slots.default">
30
31
  <slot :props="props" />
31
32
  </v-card-text>
32
33
 
@@ -59,11 +60,12 @@ import { DialogEvent } from './dialog/DialogEvent'
59
60
  import { PositionConfig } from '../services/PositionService'
60
61
  import { UsesPositionServiceMixin } from '../services/position/UsesPositionServiceMixin'
61
62
  import { randomCssClass } from '../utils/random'
63
+ import { CancelOnEscMixin } from '@a-vue/services/escape/CancelOnEscMixin'
62
64
 
63
65
  @Component({
64
66
  props: ['id', 'anchor', 'active', 'payload']
65
67
  })
66
- export default class ADialog extends Mixins(UsesPositionServiceMixin) {
68
+ export default class ADialog extends Mixins(UsesPositionServiceMixin, CancelOnEscMixin) {
67
69
  dialogId = randomCssClass(10)
68
70
 
69
71
  title = null
@@ -88,6 +90,11 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin) {
88
90
  this.$events.off(DialogEvent.SHOW, this.show)
89
91
  }
90
92
 
93
+ coe_cancelOnEsc () {
94
+ this.cancel()
95
+ return false // stop esc propagation
96
+ }
97
+
91
98
  @Watch('dialog')
92
99
  dialogChanged () {
93
100
  // called without event from activator
@@ -105,6 +112,13 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin) {
105
112
 
106
113
  this.calledViaEvent = false
107
114
  }
115
+
116
+ // register for esc
117
+ if (this.dialog) {
118
+ this.coe_watchCancel()
119
+ } else {
120
+ this.coe_unwatchCancel()
121
+ }
108
122
  }
109
123
 
110
124
  get _dialogId () {
@@ -195,11 +209,11 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin) {
195
209
  <style lang="scss" scoped>
196
210
  .v-card__title {
197
211
  background: #EEEEEE;
198
- padding: .3rem 1rem !important;
212
+ padding: .6rem 1rem .3rem !important;
199
213
  }
200
214
 
201
215
  .v-card__text {
202
- padding: .5rem 1rem !important;
216
+ padding: .8rem 1rem !important;
203
217
  }
204
218
 
205
219
  ::v-deep .v-dialog {
@@ -7,10 +7,9 @@
7
7
  v-bind="$attrs"
8
8
  :contentClass="modalId"
9
9
  transition="v-fade-transition"
10
- :persistent="true"
11
- :no-click-animation="true"
10
+ persistent
11
+ no-click-animation
12
12
  @click:outside="cancel"
13
- @keydown.esc="cancel"
14
13
  >
15
14
  <template #activator="{ on }">
16
15
  <div
@@ -41,11 +40,12 @@ import { UsesPositionServiceMixin } from '@a-vue/services/position/UsesPositionS
41
40
  import { randomCssClass } from '../utils/random'
42
41
  import { getZIndex } from 'vuetify/lib/util/helpers'
43
42
  import { ComponentWidthMixin } from './mixins/ComponentWidthMixin'
43
+ import { CancelOnEscMixin } from '@a-vue/services/escape/CancelOnEscMixin'
44
44
 
45
45
  @Component({
46
46
  props: ['show', 'title', 'beforeClose', 'triggerClass', 'anchorPosition']
47
47
  })
48
- export default class ADialog extends Mixins(UsesPositionServiceMixin, ComponentWidthMixin) {
48
+ export default class ADialog extends Mixins(UsesPositionServiceMixin, ComponentWidthMixin, CancelOnEscMixin) {
49
49
  modalId = randomCssClass(10)
50
50
 
51
51
  modal = false
@@ -79,6 +79,11 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin, ComponentW
79
79
  }
80
80
  }
81
81
 
82
+ coe_cancelOnEsc () {
83
+ this.cancel()
84
+ return false // stop esc propagation
85
+ }
86
+
82
87
  /**
83
88
  * visiblility changes from outside
84
89
  * this will trigger the modal watcher
@@ -107,6 +112,14 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin, ComponentW
107
112
  } else {
108
113
  this.removeTransientAnchor()
109
114
  }
115
+
116
+ // register for esc
117
+ if (this.modal) {
118
+ this.coe_watchCancel()
119
+ } else {
120
+ this.coe_unwatchCancel()
121
+ }
122
+
110
123
  this.$emit('update:show', this.modal)
111
124
  }
112
125
 
@@ -168,11 +181,11 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin, ComponentW
168
181
  <style lang="scss" scoped>
169
182
  .v-card__title {
170
183
  background: #EEEEEE;
171
- padding: .3rem 1rem !important;
184
+ padding: .6rem 1rem .3rem !important;
172
185
  }
173
186
 
174
187
  .v-card__text {
175
- padding: .5rem 1rem !important;
188
+ padding: .8rem 1rem !important;
176
189
  color: inherit !important;
177
190
  }
178
191
 
@@ -9,9 +9,12 @@
9
9
  <template v-for="option in options_">
10
10
  <v-radio
11
11
  :key="option.itemText"
12
- :label="option.itemText"
13
12
  :value="option.itemValue"
14
- />
13
+ >
14
+ <template #label>
15
+ <div v-html="option.itemText" />
16
+ </template>
17
+ </v-radio>
15
18
  </template>
16
19
  </v-radio-group>
17
20
  </template>
@@ -62,6 +65,7 @@ export default class ARadioGroup extends Vue {
62
65
 
63
66
  <style lang="scss" scoped>
64
67
  .v-input {
68
+ margin: 0;
65
69
 
66
70
  &:not(.hasLabel) {
67
71
  ::v-deep legend {
@@ -2,7 +2,6 @@
2
2
  <v-text-field
3
3
  ref="input"
4
4
  :type="type"
5
- :appendIcon="appendIcon"
6
5
  :autocomplete="autocomplete"
7
6
  :rules="validationRules"
8
7
  :counter="counter"
@@ -23,6 +22,8 @@ import { ComponentWidthMixin } from './mixins/ComponentWidthMixin'
23
22
  props: ['focus', 'debounce', 'validator', {password: false, number: false}]
24
23
  })
25
24
  export default class ATextField extends Mixins(ComponentWidthMixin) {
25
+ $hasOptions = ['counter']
26
+
26
27
  showPassword = false
27
28
 
28
29
  created () {
@@ -84,6 +85,10 @@ export default class ATextField extends Mixins(ComponentWidthMixin) {
84
85
  }
85
86
 
86
87
  get counter () {
88
+ if (!this.$has.counter) {
89
+ return false
90
+ }
91
+
87
92
  if (!this.validator) {
88
93
  return false
89
94
  }
@@ -2,7 +2,7 @@ import { Component, Vue } from '@a-vue'
2
2
  import { ListAction } from '@a-vue/api-resources/ApiActions'
3
3
 
4
4
  @Component({
5
- props: ['name', 'label']
5
+ props: ['name', 'label', 'additionalRules']
6
6
  })
7
7
  export class FormFieldMixin extends Vue {
8
8
  get model () {
@@ -87,6 +87,12 @@ export class FormFieldMixin extends Vue {
87
87
  }
88
88
 
89
89
  get validator () {
90
+ const validator = this.field.getValidator()
91
+ if (this.additionalRules) {
92
+ this.additionalRules.forEach(validate => {
93
+ validator.addRule(validate)
94
+ })
95
+ }
90
96
  return this.field.getValidator()
91
97
  }
92
98
 
@@ -55,6 +55,7 @@ export default class FormFieldText extends Mixins(FormFieldMixin) {
55
55
  }
56
56
 
57
57
  this.model[this.name] = value
58
+ this.$emit('input', value)
58
59
  }
59
60
 
60
61
  setInternalValue (value, reset = false) {
@@ -11,6 +11,10 @@ export function debounce (callback, delay = 300, condition = () => true) {
11
11
  clearTimeout(timeout)
12
12
  }
13
13
 
14
+ /**
15
+ * fire immediately if condition falsy
16
+ * e.g. textfield content gets canceled using (x)
17
+ */
14
18
  if (!condition(...args)) {
15
19
  callback(...args)
16
20
  return
@@ -97,7 +97,7 @@
97
97
 
98
98
  <v-container
99
99
  fluid
100
- class="pa-8 pt-2"
100
+ class="pa-8 pt-0"
101
101
  >
102
102
  <sticky-header />
103
103
 
@@ -230,6 +230,7 @@ export default class App extends Vue {
230
230
  left: 0;
231
231
  top: 0;
232
232
  padding: .2rem 1.1rem;
233
+ background-color: white;
233
234
  }
234
235
 
235
236
  .a-breadcrumbs {
@@ -7,14 +7,15 @@
7
7
  </template>
8
8
 
9
9
  <script>
10
- import { Component, Watch, Vue } from '@a-vue'
10
+ import { Component, Watch, Mixins } from '@a-vue'
11
11
  import { FlyingContextEvent } from '@a-vue/events'
12
12
  import { randomCssClass } from '@a-vue/utils/random'
13
+ import { CancelOnEscMixin } from '@a-vue/services/escape/CancelOnEscMixin'
13
14
 
14
15
  @Component({
15
16
  props: [{show: false}]
16
17
  })
17
- export default class FlyingContext extends Vue {
18
+ export default class FlyingContext extends Mixins(CancelOnEscMixin) {
18
19
  isVisible = false
19
20
  contextId = randomCssClass(10)
20
21
 
@@ -44,8 +45,10 @@ export default class FlyingContext extends Vue {
44
45
  if (this.isVisible) {
45
46
  const container = this.getSidebarContainer()
46
47
  container.appendChild(el)
48
+ this.coe_watchCancel() // show context -> watch esc
47
49
  } else {
48
50
  this.$el.appendChild(el)
51
+ this.coe_unwatchCancel() // hide context -> do not watch esc any more
49
52
  }
50
53
  }
51
54
 
@@ -62,11 +65,17 @@ export default class FlyingContext extends Vue {
62
65
  onHide () {
63
66
  if (this.isVisible) {
64
67
  this.$el.appendChild(this.getContent())
68
+ this.coe_unwatchCancel() // hide context -> do not watch esc any more
65
69
  this.isVisible = false
66
70
  this.$emit('hide')
67
71
  }
68
72
  }
69
73
 
74
+ coe_cancelOnEsc () {
75
+ this.onHide()
76
+ return false // stop esc propagation
77
+ }
78
+
70
79
  getContent () {
71
80
  return document.querySelector('.' + this.contextId)
72
81
  }
@@ -27,6 +27,7 @@
27
27
  <script>
28
28
  import { Component, Vue } from '@a-vue'
29
29
  import { FlyingContextEvent } from '@a-vue/events'
30
+ import { getZIndex } from 'vuetify/lib/util/helpers'
30
31
 
31
32
  @Component({
32
33
  props: []
@@ -66,6 +67,12 @@ export default class FlyingContextContainer extends Vue {
66
67
  parent = parent.parentElement
67
68
  }
68
69
 
70
+ // popup clicked
71
+ const targetIndex = getZIndex(e.target)
72
+ if (targetIndex > 200) {
73
+ return
74
+ }
75
+
69
76
  // check if flying context ist clicked
70
77
  if (!this.$el.contains(e.target)) {
71
78
  this.hide()
@@ -57,8 +57,9 @@ export default class StickyHeader extends Vue {
57
57
  #stickyHeader {
58
58
  position: sticky;
59
59
  top: -1px;
60
- margin: -1rem -2rem 2rem;
60
+ margin: 0 -2rem 2rem;
61
61
  padding: 1rem 2rem;
62
+ background-color: white;
62
63
 
63
64
  &:not(.visible) {
64
65
  display: none !important;
@@ -8,5 +8,6 @@
8
8
  <style lang="scss" scoped>
9
9
  .detailMeta {
10
10
  color: gray;
11
+ font-size: .9rem;
11
12
  }
12
13
  </style>
@@ -3,18 +3,32 @@
3
3
  gap="1"
4
4
  v-bind="$attrs"
5
5
  >
6
- <v-btn
7
- fab
8
- small
9
- :disabled="($has.reset && !changed) || !valid"
10
- color="green white--text"
11
- title="Speichern"
12
- @click="$emit('save')"
13
- >
14
- <v-icon>
15
- $checkIcon
16
- </v-icon>
17
- </v-btn>
6
+ <v-tooltip bottom>
7
+ <template #activator="{ on }">
8
+ <div v-on="disabled ? on : null">
9
+ <v-btn
10
+ fab
11
+ small
12
+ :disabled="disabled"
13
+ color="green white--text"
14
+ title="Speichern"
15
+ @click="$emit('save')"
16
+ >
17
+ <v-icon>
18
+ $checkIcon
19
+ </v-icon>
20
+ </v-btn>
21
+ </div>
22
+ </template>
23
+ <span v-if="disabled">
24
+ <template v-if="!valid">
25
+ Daten berichtigen,<br>um zu speichern
26
+ </template>
27
+ <template v-else>
28
+ Daten ändern,<br>um zu speichern
29
+ </template>
30
+ </span>
31
+ </v-tooltip>
18
32
 
19
33
  <v-hover
20
34
  v-if="$has.reset && changed"
@@ -49,12 +63,9 @@ export default class EditFormButtons extends Vue {
49
63
  $hasOptions = ['reset']
50
64
 
51
65
  undoIcon = mdiRotateLeft
52
- }
53
- </script>
54
-
55
66
 
56
- <style lang="scss" scoped>
57
- .v-icon--disabled {
58
- opacity: .3;
67
+ get disabled () {
68
+ return (this.$has.reset && !this.changed) || !this.valid
69
+ }
59
70
  }
60
- </style>
71
+ </script>
@@ -2,14 +2,14 @@
2
2
  <div>
3
3
  <v-hover v-slot="{ hover }">
4
4
  <v-btn
5
- :class="'removeButton-' + dialogId"
5
+ :class="'a-btn-standard removeButton-' + dialogId"
6
6
  fab
7
7
  x-small
8
8
  :color="(hover ? 'red' : 'grey lighten-3')"
9
- title="Löschen"
9
+ :title="title"
10
10
  @click="remove"
11
11
  >
12
- <v-icon :color="hover ? 'white' : '#999999'">
12
+ <v-icon class="white--hover">
13
13
  $trashCanIcon
14
14
  </v-icon>
15
15
  </v-btn>
@@ -17,7 +17,7 @@
17
17
 
18
18
  <a-dialog
19
19
  :id="dialogId"
20
- :anchor="[document, '.removeButton-' + dialogId]"
20
+ :anchor="'.removeButton-' + dialogId"
21
21
  :active="protect ? removeKey === removeConfirmed : true"
22
22
  >
23
23
  <template v-if="protect">
@@ -41,11 +41,10 @@ import { DialogEvent } from '@a-vue/events'
41
41
  import { randomCssClass } from '@a-vue/utils/random'
42
42
 
43
43
  @Component({
44
- props: ['itemTitle', 'protect']
44
+ props: ['title', 'itemTitle', 'protect']
45
45
  })
46
46
  export default class EditPage extends Vue {
47
47
  dialogId = randomCssClass(10)
48
- document = document
49
48
  removeKey = null
50
49
  removeConfirmed = null
51
50
 
@@ -33,7 +33,7 @@
33
33
  v-for="model in models_"
34
34
  :key="model.id"
35
35
  v-flying-context-trigger="hasFlyingContext"
36
- :class="{selectable: hasFlyingContext}"
36
+ :class="getRowClasses(model)"
37
37
  v-bind="getRowAttributes(model)"
38
38
  v-on="getRowListeners(model)"
39
39
  @click="emitFlyingContext(model)"
@@ -82,7 +82,7 @@ import { ListViewMixin } from '@a-vue/components/list/ListViewMixin'
82
82
  import { LoadingEvent } from '@a-vue/events'
83
83
 
84
84
  @Component({
85
- props: ['rowAttributes', 'rowListeners']
85
+ props: ['rowAttributes', 'rowClasses', 'rowListeners']
86
86
  })
87
87
  export default class ListView extends Mixins(ListViewMixin) {
88
88
  $hasOptions = ['icon']
@@ -103,7 +103,37 @@ export default class ListView extends Mixins(ListViewMixin) {
103
103
  if (typeof this.rowAttributes === 'function') {
104
104
  return this.rowAttributes(model)
105
105
  }
106
- return this.rowAttributes
106
+ return this.rowAttributes || {}
107
+ }
108
+
109
+ getRowClasses (model) {
110
+ let classes
111
+
112
+ if (typeof this.rowClasses === 'function') {
113
+ classes = this.rowClasses(model)
114
+ } else {
115
+ classes = this.rowClasses || {}
116
+ }
117
+
118
+ if (Array.isArray(classes)) { // ['a', {b: true/false}, 'c']
119
+ classes = classes.reduce((ac, a) => {
120
+ if (typeof a === 'object') { // {className: true/false}
121
+ return {...ac, ...a}
122
+ }
123
+ return {...ac, [a]: true} // 'className'
124
+ }, {})
125
+ } else if (typeof classes === 'string') { // 'a b c'
126
+ classes = {
127
+ [classes]: true
128
+ }
129
+ } // else { a: true, b: true, c: true }
130
+
131
+ classes = {
132
+ selectable: this.hasFlyingContext,
133
+ ...classes
134
+ }
135
+
136
+ return classes
107
137
  }
108
138
 
109
139
  getRowListeners (model) {
@@ -116,7 +146,7 @@ export default class ListView extends Mixins(ListViewMixin) {
116
146
 
117
147
  emitFlyingContext (model) {
118
148
  if (window.getSelection().toString()) { // do not open if text selected
119
- console.log(window.getSelection().toString())
149
+ // console.log(window.getSelection().toString())
120
150
  return
121
151
  }
122
152
  this.$emit('flyingContext', model)
@@ -18,7 +18,8 @@ import {
18
18
  mdiPencil,
19
19
  mdiPlus,
20
20
  mdiPrinter,
21
- mdiThumbUpOutline
21
+ mdiThumbUpOutline,
22
+ mdiCurrencyEur
22
23
  } from '@mdi/js'
23
24
  import Vue from 'vue'
24
25
  import Vuetify from 'vuetify/lib'
@@ -48,7 +49,8 @@ export default new Vuetify({
48
49
  arrowLeftIcon: mdiArrowLeft,
49
50
  caretDownIcon: mdiMenuDown,
50
51
  caretUpIcon: mdiMenuUp,
51
- printerIcon: mdiPrinter
52
+ printerIcon: mdiPrinter,
53
+ euroSymbol: mdiCurrencyEur
52
54
  }
53
55
  },
54
56
  breakpoint: {
@@ -28,11 +28,39 @@
28
28
  }
29
29
  }
30
30
 
31
+ .theme--light.v-btn.a-btn-standard {
32
+ background-color: #E9E9E9;
33
+
34
+ &:not(:hover) {
35
+ background-color: #EEEEEE !important;
36
+ border-color: #EEEEEE !important;
37
+ }
38
+
39
+ span,
40
+ .v-icon {
41
+ color: grey !important;
42
+ }
43
+
44
+ &:hover {
45
+ span,
46
+ .v-icon {
47
+ &.white--hover {
48
+ color: white !important;
49
+ }
50
+ }
51
+ }
52
+ }
53
+
31
54
  .theme--light.v-btn.v-btn--has-bg {
32
55
  background-color: #E9E9E9;
33
56
  }
34
57
 
58
+ .theme--light.v-btn:focus::before {
59
+ opacity: .08;
60
+ }
61
+
35
62
  .theme--light.v-btn.v-btn--disabled,
63
+ .theme--light.v-btn.v-btn--disabled span,
36
64
  .theme--light.v-btn.v-btn--disabled .v-icon,
37
65
  .theme--light.v-btn.v-btn--disabled .v-btn__loading {
38
66
  color: white !important;