@afeefa/vue-app 0.0.62 → 0.0.65

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. package/.afeefa/package/release/version.txt +1 -1
  2. package/README.md +3 -1
  3. package/package.json +1 -1
  4. package/src/components/ABreadcrumbs.vue +3 -2
  5. package/src/components/AModal.vue +21 -3
  6. package/src/components/ARichTextArea.vue +95 -85
  7. package/src/components/ATableRow.vue +4 -0
  8. package/src/components/flying-context/FlyingContextEvent.js +5 -0
  9. package/src/components/form/EditForm.vue +13 -3
  10. package/src/components/form/EditModal.vue +52 -35
  11. package/src/components/form/NestedEditForm.vue +4 -0
  12. package/src/components/form/fields/FormFieldRichTextArea.vue +5 -3
  13. package/src/components/list/ListViewMixin.js +14 -2
  14. package/src/components/search-select/SearchSelectList.vue +0 -1
  15. package/src/components/vue/Component.js +9 -2
  16. package/src/events.js +2 -0
  17. package/src-admin/bootstrap.js +1 -0
  18. package/src-admin/components/App.vue +55 -6
  19. package/src-admin/components/FlyingContext.vue +77 -0
  20. package/src-admin/components/FlyingContextContainer.vue +85 -0
  21. package/src-admin/components/StickyFooter.vue +42 -0
  22. package/src-admin/components/StickyFooterContainer.vue +66 -0
  23. package/src-admin/components/form/EditFormButtons.vue +8 -3
  24. package/src-admin/components/index.js +4 -7
  25. package/src-admin/components/list/ListView.vue +22 -9
  26. package/src-admin/components/pages/EditPage.vue +23 -9
  27. package/src-admin/components/routes/DataRouteMixin.js +24 -15
  28. package/src-admin/config/routing.js +0 -11
  29. package/src-admin/config/vuetify.js +7 -1
  30. package/src-admin/directives/index.js +26 -0
  31. package/src-admin/styles.scss +1 -4
  32. package/src-admin/components/pages/CreatePage.vue +0 -114
  33. package/src-admin/components/pages/DetailPage.vue +0 -143
  34. package/src-admin/components/pages/EditPageMixin.js +0 -96
  35. package/src-admin/components/pages/ListPage.vue +0 -55
  36. package/src-admin/components/routes/CreateRoute.vue +0 -15
  37. package/src-admin/components/routes/DetailRoute.vue +0 -85
  38. package/src-admin/components/routes/EditRoute.vue +0 -78
  39. package/src-admin/components/routes/ListRoute.vue +0 -110
@@ -1 +1 @@
1
- 0.0.62
1
+ 0.0.65
package/README.md CHANGED
@@ -1 +1,3 @@
1
- # @afeefa/vue-app
1
+ # @afeefa/vue-app
2
+
3
+ Force push :-);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afeefa/vue-app",
3
- "version": "0.0.62",
3
+ "version": "0.0.65",
4
4
  "description": "",
5
5
  "author": "Afeefa Kollektiv <kollektiv@afeefa.de>",
6
6
  "license": "MIT",
@@ -106,9 +106,10 @@ export default class ABreadcrumbs extends Vue {
106
106
  getItemTitle (title) {
107
107
  // title = title.concat(title).concat(title)
108
108
  if (title.length > 20) {
109
- title = title.slice(0, 10).trim() + '...' + title.slice(-10).trim()
109
+ // title = title.slice(0, 10).trim() + '...' + title.slice(-10).trim()
110
110
  }
111
- return title.toUpperCase()
111
+ return title
112
+ // return title.toUpperCase()
112
113
  }
113
114
  }
114
115
  </script>
@@ -7,6 +7,8 @@
7
7
  v-bind="$attrs"
8
8
  :contentClass="modalId"
9
9
  transition="v-fade-transition"
10
+ :persistent="true"
11
+ :no-click-animation="true"
10
12
  @click:outside="cancel"
11
13
  @keydown.esc="cancel"
12
14
  >
@@ -41,7 +43,7 @@ import { getZIndex } from 'vuetify/lib/util/helpers'
41
43
  import { ComponentWidthMixin } from './mixins/ComponentWidthMixin'
42
44
 
43
45
  @Component({
44
- props: ['show', 'title', 'triggerClass', 'anchorPosition']
46
+ props: ['show', 'title', 'beforeClose', 'triggerClass', 'anchorPosition']
45
47
  })
46
48
  export default class ADialog extends Mixins(UsesPositionServiceMixin, ComponentWidthMixin) {
47
49
  modalId = randomCssClass(10)
@@ -83,7 +85,15 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin, ComponentW
83
85
  * and emit a visibility event
84
86
  */
85
87
  @Watch('show')
86
- showChanged () {
88
+ async showChanged () {
89
+ // check if a modal is allowed to be closed
90
+ if (this.modal && !this.show && this.beforeClose) {
91
+ const result = await this.beforeClose()
92
+ if (!result) {
93
+ return
94
+ }
95
+ }
96
+
87
97
  this.modal = this.show
88
98
  }
89
99
 
@@ -121,7 +131,15 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin, ComponentW
121
131
  this.urp_registerPositionWatcher(this.position)
122
132
  }
123
133
 
124
- cancel () {
134
+ async cancel () {
135
+ // check if a modal is allowed to be closed
136
+ if (this.modal && this.beforeClose) {
137
+ const result = await this.beforeClose()
138
+ if (!result) {
139
+ return
140
+ }
141
+ }
142
+
125
143
  this.modal = false
126
144
  }
127
145
 
@@ -4,85 +4,84 @@
4
4
  v-if="editor"
5
5
  class="menu-bar"
6
6
  >
7
- <v-btn
8
- small
9
- :class="['menu-button', {'is-active': focus && editor.isActive('bold')}]"
10
- @click="editor.chain().focus().toggleBold().run()"
11
- >
12
- <v-icon>{{ boldIcon }}</v-icon>
13
- </v-btn>
14
-
15
- <v-btn
16
- small
17
- :class="['menu-button', {'is-active': focus && editor.isActive('italic')}]"
18
- @click="editor.chain().focus().toggleItalic().run()"
19
- >
20
- <v-icon>{{ italicIcon }}</v-icon>
21
- </v-btn>
22
-
23
- <v-btn
24
- small
25
- :class="['menu-button', 'strike', {'is-active': focus && editor.isActive('strike')}]"
26
- @click="editor.chain().focus().toggleStrike().run()"
27
- >
28
- <v-icon>{{ strikeIcon }}</v-icon>
29
- </v-btn>
30
-
31
- <v-btn
32
- small
33
- :class="['menu-button', {'is-active': focus && editor.isActive('heading', {level: 1})}]"
34
- @click="editor.chain().focus().toggleHeading({level: 1}).run()"
35
- >
36
- <v-icon>{{ h1Icon }}</v-icon>
37
- </v-btn>
38
-
39
- <v-btn
40
- small
41
- :class="['menu-button', {'is-active': focus && editor.isActive('heading', {level: 2})}]"
42
- @click="editor.chain().focus().toggleHeading({level: 2}).run()"
43
- >
44
- <v-icon>{{ h2Icon }}</v-icon>
45
- </v-btn>
46
-
47
- <v-btn
48
- small
49
- :class="['menu-button', {'is-active': focus && editor.isActive('bulletList')}]"
50
- @click="editor.chain().focus().toggleBulletList().run()"
51
- >
52
- <v-icon>{{ ulIcon }}</v-icon>
53
- </v-btn>
54
-
55
- <v-btn
56
- small
57
- :class="['menu-button', {'is-active': focus && editor.isActive('orderedList')}]"
58
- @click="editor.chain().focus().toggleOrderedList().run()"
59
- >
60
- <v-icon>{{ olIcon }}</v-icon>
61
- </v-btn>
62
-
63
- <v-btn
64
- small
65
- :class="['menu-button', {'is-active': focus && editor.isActive('blockquote')}]"
66
- @click="editor.chain().focus().toggleBlockquote().run()"
67
- >
68
- <v-icon>{{ commentIcon }}</v-icon>
69
- </v-btn>
70
-
71
- <v-btn
72
- small
73
- class="menu-button"
74
- @click="editor.chain().focus().undo().run()"
75
- >
76
- <v-icon>{{ undoIcon }}</v-icon>
77
- </v-btn>
78
-
79
- <v-btn
80
- small
81
- class="menu-button"
82
- @click="editor.chain().focus().redo().run()"
83
- >
84
- <v-icon>{{ redoIcon }}</v-icon>
85
- </v-btn>
7
+ <div>
8
+ <v-btn
9
+ small
10
+ :class="['menu-button', {'is-active': focus && editor.isActive('bold')}]"
11
+ @click="editor.chain().focus().toggleBold().run()"
12
+ >
13
+ <v-icon>{{ boldIcon }}</v-icon>
14
+ </v-btn>
15
+
16
+ <v-btn
17
+ small
18
+ :class="['menu-button', {'is-active': focus && editor.isActive('italic')}]"
19
+ @click="editor.chain().focus().toggleItalic().run()"
20
+ >
21
+ <v-icon>{{ italicIcon }}</v-icon>
22
+ </v-btn>
23
+
24
+ <v-btn
25
+ small
26
+ :class="['menu-button', 'strike', {'is-active': focus && editor.isActive('strike')}]"
27
+ @click="editor.chain().focus().toggleStrike().run()"
28
+ >
29
+ <v-icon>{{ strikeIcon }}</v-icon>
30
+ </v-btn>
31
+
32
+ <v-btn
33
+ small
34
+ :class="['menu-button', {'is-active': focus && editor.isActive('heading', {level: 1})}]"
35
+ @click="editor.chain().focus().toggleHeading({level: 1}).run()"
36
+ >
37
+ <v-icon>{{ h1Icon }}</v-icon>
38
+ </v-btn>
39
+
40
+ <v-btn
41
+ small
42
+ :class="['menu-button', {'is-active': focus && editor.isActive('heading', {level: 2})}]"
43
+ @click="editor.chain().focus().toggleHeading({level: 2}).run()"
44
+ >
45
+ <v-icon>{{ h2Icon }}</v-icon>
46
+ </v-btn>
47
+
48
+ <v-btn
49
+ small
50
+ :class="['menu-button', {'is-active': focus && editor.isActive('bulletList')}]"
51
+ @click="editor.chain().focus().toggleBulletList().run()"
52
+ >
53
+ <v-icon>{{ ulIcon }}</v-icon>
54
+ </v-btn>
55
+
56
+ <v-btn
57
+ small
58
+ :class="['menu-button', {'is-active': focus && editor.isActive('orderedList')}]"
59
+ @click="editor.chain().focus().toggleOrderedList().run()"
60
+ >
61
+ <v-icon>{{ olIcon }}</v-icon>
62
+ </v-btn>
63
+
64
+ <v-btn
65
+ small
66
+ :class="['menu-button', {'is-active': focus && editor.isActive('blockquote')}]"
67
+ @click="editor.chain().focus().toggleBlockquote().run()"
68
+ >
69
+ <v-icon>{{ commentIcon }}</v-icon>
70
+ </v-btn>
71
+
72
+ <v-btn
73
+ small
74
+ class="menu-button"
75
+ :disabled="initialValue === editor.getHTML()"
76
+ @click="editor.chain().focus().undo().run()"
77
+ >
78
+ <v-icon>{{ undoIcon }}</v-icon>
79
+ </v-btn>
80
+ </div>
81
+
82
+ <div>
83
+ <slot name="buttons" />
84
+ </div>
86
85
  </div>
87
86
 
88
87
  <editor-content
@@ -119,6 +118,7 @@ import {
119
118
  export default class ARichTextArea extends Vue {
120
119
  editor = null
121
120
  internalValue = null
121
+ initialValue = null
122
122
  focus = false
123
123
 
124
124
  boldIcon = mdiFormatBold
@@ -133,6 +133,7 @@ export default class ARichTextArea extends Vue {
133
133
  redoIcon = mdiRotateRight
134
134
 
135
135
  created () {
136
+ this.initialValue = this.value
136
137
  this.internalValue = this.value
137
138
  }
138
139
 
@@ -158,14 +159,20 @@ export default class ARichTextArea extends Vue {
158
159
  this.$emit('blur')
159
160
  }
160
161
  })
161
-
162
- this.editor.commands.setContent(this.internalValue, false)
163
162
  }
164
163
 
165
164
  beforeDestroy () {
166
165
  this.editor.destroy()
167
166
  }
168
167
 
168
+ /**
169
+ * reset the text area to disable the undo button
170
+ * e.g. after saving the form while keeping it open
171
+ */
172
+ reset () {
173
+ this.initialValue = this.value
174
+ }
175
+
169
176
  @Watch('value')
170
177
  valueChanged () {
171
178
  this.internalValue = this.value
@@ -192,10 +199,6 @@ export default class ARichTextArea extends Vue {
192
199
 
193
200
 
194
201
  <style lang="scss" scoped>
195
- .v-input:not(.v-input--is-focused) ::v-deep .v-counter {
196
- display: none;
197
- }
198
-
199
202
  .a-rich-text-editor {
200
203
  ::v-deep .ProseMirror {
201
204
  &-focused {
@@ -205,9 +208,12 @@ export default class ARichTextArea extends Vue {
205
208
  }
206
209
 
207
210
  .menu-bar {
211
+ display: flex;
212
+ justify-content: space-between;
208
213
  margin: -.2rem 0 .5rem -.2rem;
209
214
  }
210
215
 
216
+ .menu-bar .v-btn,
211
217
  .menu-button {
212
218
  padding: 0 !important;
213
219
  width: 30px !important;
@@ -239,6 +245,10 @@ export default class ARichTextArea extends Vue {
239
245
  &.is-active {
240
246
  background: #ECECEC !important;
241
247
  }
248
+
249
+ &[disabled] {
250
+ background: none !important;
251
+ }
242
252
  }
243
253
 
244
254
  ::v-deep .ProseMirror {
@@ -62,6 +62,10 @@ export default class ATableRow extends Vue {
62
62
  }
63
63
  }
64
64
 
65
+ &.selectable {
66
+ cursor: pointer;
67
+ }
68
+
65
69
  &:hover, &.selected {
66
70
  background: #F4F4F4;
67
71
  }
@@ -0,0 +1,5 @@
1
+ import { BaseEvent } from '@a-vue/plugins/event-bus/BaseEvent'
2
+
3
+ export class FlyingContextEvent extends BaseEvent {
4
+ static HIDE_ALL = 'FlyingContextEvent:hide-all'
5
+ }
@@ -4,9 +4,10 @@
4
4
  autocomplete="off"
5
5
  >
6
6
  <slot
7
+ name="form"
7
8
  :changed="changed"
8
9
  :valid="valid"
9
- :model="modelToEdit"
10
+ :modelToEdit="modelToEdit"
10
11
  />
11
12
  </v-form>
12
13
  </template>
@@ -27,16 +28,22 @@ export default class EditForm extends Vue {
27
28
  modelToEdit = null
28
29
  valid = false
29
30
  lastJson = null
31
+ forcedUnchange = false
30
32
 
31
33
  created () {
32
34
  this.reset()
33
35
  }
34
36
 
37
+ forceUnchanged () {
38
+ this.forcedUnchange = true
39
+ this.$emit('update:changed', false)
40
+ }
41
+
35
42
  reset () {
36
43
  if (this.createModelToEdit) {
37
44
  this.modelToEdit = this.createModelToEdit(this.model)
38
- } else {
39
- this.modelToEdit = this.model
45
+ } else if (this.model) {
46
+ this.modelToEdit = this.model.cloneForEdit()
40
47
  }
41
48
  this.lastJson = this.json
42
49
  }
@@ -65,6 +72,9 @@ export default class EditForm extends Vue {
65
72
  }
66
73
 
67
74
  get changed () {
75
+ if (this.forcedUnchange) {
76
+ return false
77
+ }
68
78
  // console.log(this.json)
69
79
  // console.log(this.lastJson)
70
80
  return this.json !== this.lastJson
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <a-modal
3
3
  :title="title"
4
+ :beforeClose="beforeClose"
4
5
  :show.sync="show_"
5
6
  v-bind="$attrs"
6
7
  >
@@ -10,16 +11,18 @@
10
11
 
11
12
  <edit-form
12
13
  v-if="show_"
14
+ ref="form"
13
15
  :model="model"
16
+ :createModelToEdit="createModelToEdit"
14
17
  >
15
- <template #fields>
18
+ <template #form="{modelToEdit, changed, valid}">
16
19
  <slot
17
- name="fields"
18
- :model="model"
20
+ name="form"
21
+ :modelToEdit="modelToEdit"
22
+ :changed="changed"
23
+ :valid="valid"
19
24
  />
20
- </template>
21
25
 
22
- <template #default="{changed, valid}">
23
26
  <a-row
24
27
  class="mt-8 mb-1 pb-1 gap-4"
25
28
  right
@@ -31,25 +34,14 @@
31
34
  Schließen
32
35
  </v-btn>
33
36
 
34
- <a-row gap="2">
35
- <v-btn
36
- small
37
- :disabled="!changed || !valid"
38
- color="green white--text"
39
- @click="save"
40
- >
41
- Speichern
42
- </v-btn>
43
-
44
- <v-icon
45
- v-if="changed"
46
- small
47
- text
48
- @click="reset"
49
- >
50
- {{ undoIcon }}
51
- </v-icon>
52
- </a-row>
37
+ <edit-form-buttons
38
+ :changed="changed"
39
+ :valid="valid"
40
+ small
41
+ :has="{reset: !!modelToEdit.id}"
42
+ @save="$emit('save', modelToEdit, ignoreChangesOnClose)"
43
+ @reset="$refs.form.reset()"
44
+ />
53
45
  </a-row>
54
46
  </template>
55
47
  </edit-form>
@@ -59,21 +51,26 @@
59
51
 
60
52
  <script>
61
53
  import { Component, Vue, Watch } from '@a-vue'
62
- import { mdiRotateLeft} from '@mdi/js'
54
+ import { DialogEvent } from '@a-vue/events'
63
55
 
64
56
  @Component({
65
- props: ['model', 'title', 'show']
57
+ props: ['model', 'createModelToEdit', 'title', 'show']
66
58
  })
67
59
  export default class EditModal extends Vue {
68
60
  show_ = false
61
+ ignoreChangesOnClose_ = false
69
62
 
70
- undoIcon = mdiRotateLeft
63
+ created () {
64
+ if (this.show) { // open on create with v-show
65
+ this.open()
66
+ }
67
+ }
71
68
 
72
69
  /**
73
70
  * visiblility changes from outside
74
71
  * this will trigger the show_ watcher,
75
72
  * forward the change to the modal and
76
- * later emit a open/close event
73
+ * later emit an open/close event
77
74
  */
78
75
  @Watch('show')
79
76
  showChanged () {
@@ -90,7 +87,6 @@ export default class EditModal extends Vue {
90
87
  @Watch('show_')
91
88
  show_Changed () {
92
89
  if (this.show_) {
93
- this.reset()
94
90
  this.$emit('open')
95
91
  } else {
96
92
  this.$emit('close')
@@ -101,16 +97,37 @@ export default class EditModal extends Vue {
101
97
  this.show_ = true
102
98
  }
103
99
 
104
- close () {
105
- this.show_ = false
100
+ async beforeClose () {
101
+ // run only if show_ is true to prevent double checks with a-modal
102
+ if (this.show_ && this.$refs.form.changed && !this.ignoreChangesOnClose_) {
103
+ const result = await this.$events.dispatch(new DialogEvent(DialogEvent.SHOW, {
104
+ title: 'Änderungen verwerfen?',
105
+ message: 'Im Formular sind nicht gespeicherte Änderungen. Sollen diese verworfen werden?',
106
+ yesButton: 'Verwerfen'
107
+ }))
108
+ if (result !== DialogEvent.RESULT_YES) {
109
+ return false
110
+ }
111
+ }
112
+ return true
106
113
  }
107
114
 
108
- reset () {
109
- this.$emit('reset')
115
+ async close () {
116
+ const result = await this.beforeClose()
117
+ if (!result) {
118
+ return
119
+ }
120
+
121
+ this.show_ = false
110
122
  }
111
123
 
112
- save () {
113
- this.$emit('save')
124
+ /**
125
+ * hook to allow to leave a just created (saved) model
126
+ */
127
+ ignoreChangesOnClose () {
128
+ // this.$refs.form.forceUnchanged()
129
+ console.info('TODO switch form to forceUnchanged')
130
+ this.ignoreChangesOnClose_ = true
114
131
  }
115
132
  }
116
133
  </script>
@@ -21,5 +21,9 @@ export default class NestedEditForm extends Vue {
21
21
  console.warn('Nested edit form does not have a model.')
22
22
  }
23
23
  }
24
+
25
+ get modelToEdit () {
26
+ return this.model
27
+ }
24
28
  }
25
29
  </script>
@@ -1,7 +1,9 @@
1
1
  <template>
2
- <a-rich-text-area
3
- v-model="model[name]"
4
- />
2
+ <a-rich-text-area v-model="model[name]">
3
+ <template #buttons>
4
+ <slot name="buttons" />
5
+ </template>
6
+ </a-rich-text-area>
5
7
  </template>
6
8
 
7
9
  <script>
@@ -64,9 +64,16 @@ export class ListViewMixin extends Vue {
64
64
  .on('change', this.filtersChanged) // listen to change
65
65
 
66
66
  this._filtersInitialized()
67
+ this.$emit('update:filters', this.filters)
68
+ this.$emit('update:listViewModel', this.listViewModel)
67
69
 
68
70
  if (this.models) {
69
71
  this.$emit('update:count', this.meta_.count_search)
72
+
73
+ this.$emit('onLoad', {
74
+ models: this.models_,
75
+ meta: this.meta_
76
+ })
70
77
  } else {
71
78
  this.load()
72
79
  }
@@ -132,7 +139,7 @@ export class ListViewMixin extends Vue {
132
139
 
133
140
  if (!models) { // error happened
134
141
  this.isLoading = false
135
- this.$emit('update:isLoading', this.isLoading)
142
+ this.$emit('update:isLoading', false)
136
143
  return
137
144
  }
138
145
 
@@ -144,8 +151,13 @@ export class ListViewMixin extends Vue {
144
151
  }
145
152
 
146
153
  this.isLoading = false
147
- this.$emit('update:isLoading', this.isLoading)
154
+ this.$emit('update:isLoading', false)
148
155
 
149
156
  this.$emit('update:count', this.meta_.count_search)
157
+
158
+ this.$emit('onLoad', {
159
+ models,
160
+ meta
161
+ })
150
162
  }
151
163
  }
@@ -67,7 +67,6 @@ export default class SearchSelectList extends Mixins(ListViewMixin) {
67
67
  if (this.q) {
68
68
  this.filters.q.value = this.q
69
69
  }
70
- this.$emit('update:filters', this.filters)
71
70
  }
72
71
  }
73
72
  </script>
@@ -32,9 +32,16 @@ function propsWithDefaults (props) {
32
32
  // property: { some object }, should be a normal vue props object
33
33
  } else if (value && typeof value === 'object' && value.constructor === Object) {
34
34
  normalizedProps[subProp] = value
35
- // property: true, null, ...
35
+ // property: true, false, null, ...
36
36
  } else {
37
- normalizedProps[subProp] = { default: value }
37
+ if (typeof value === 'boolean') {
38
+ normalizedProps[subProp] = {
39
+ type: Boolean,
40
+ default: value
41
+ }
42
+ } else {
43
+ normalizedProps[subProp] = { default: value }
44
+ }
38
45
  }
39
46
  })
40
47
  } else {
package/src/events.js CHANGED
@@ -1,4 +1,6 @@
1
+ export { BaseEvent } from './plugins/event-bus/BaseEvent'
1
2
  export { LoadingEvent } from './components/loading-indicator/LoadingEvent'
2
3
  export { SaveEvent } from './components/save-indicator/SaveEvent'
3
4
  export { AlertEvent } from './components/alert/AlertEvent'
4
5
  export { DialogEvent } from './components/dialog/DialogEvent'
6
+ export { FlyingContextEvent } from './components/flying-context/FlyingContextEvent'
@@ -1,5 +1,6 @@
1
1
  import './config/event-bus'
2
2
  import './config/components'
3
+ import './directives'
3
4
 
4
5
  import { translationPlugin } from '@a-admin/plugins/translation/TranslationPlugin'
5
6
  import { apiResourcesPlugin } from '@a-vue/plugins/api-resources/ApiResourcesPlugin'