@afeefa/vue-app 0.0.63 → 0.0.66

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.
Files changed (51) hide show
  1. package/.afeefa/package/release/version.txt +1 -1
  2. package/package.json +1 -1
  3. package/src/components/ABreadcrumbs.vue +75 -18
  4. package/src/components/ADatePicker.vue +1 -1
  5. package/src/components/AIcon.vue +3 -6
  6. package/src/components/AIconButton.vue +1 -2
  7. package/src/components/AModal.vue +21 -3
  8. package/src/components/ARichTextArea.vue +95 -85
  9. package/src/components/ARow.vue +0 -7
  10. package/src/components/ATableRow.vue +4 -0
  11. package/src/components/flying-context/FlyingContextEvent.js +5 -0
  12. package/src/components/form/EditForm.vue +13 -3
  13. package/src/components/form/EditModal.vue +52 -35
  14. package/src/components/form/fields/FormFieldRichTextArea.vue +5 -3
  15. package/src/components/list/ListViewMixin.js +25 -2
  16. package/src/components/mixins/ClickOutsideMixin.js +5 -1
  17. package/src/components/search-select/SearchSelectList.vue +0 -1
  18. package/src/components/vue/Component.js +9 -2
  19. package/src/events.js +2 -0
  20. package/src-admin/bootstrap.js +1 -0
  21. package/src-admin/components/App.vue +58 -59
  22. package/src-admin/components/FlyingContext.vue +77 -0
  23. package/src-admin/components/FlyingContextContainer.vue +85 -0
  24. package/src-admin/components/Sidebar.vue +66 -0
  25. package/src-admin/components/SidebarItem.vue +59 -0
  26. package/src-admin/components/StickyFooter.vue +42 -0
  27. package/src-admin/components/StickyFooterContainer.vue +66 -0
  28. package/src-admin/components/StickyHeader.vue +73 -0
  29. package/src-admin/components/app/AppBarButtons.vue +0 -7
  30. package/src-admin/components/app/AppBarTitle.vue +55 -11
  31. package/src-admin/components/app/AppBarTitleContainer.vue +2 -3
  32. package/src-admin/components/controls/SearchSelectFormField.vue +1 -0
  33. package/src-admin/components/detail/DetailProperty.vue +20 -16
  34. package/src-admin/components/form/EditFormButtons.vue +25 -6
  35. package/src-admin/components/form/RemoveButton.vue +17 -8
  36. package/src-admin/components/index.js +6 -7
  37. package/src-admin/components/list/ListView.vue +22 -9
  38. package/src-admin/components/pages/EditPage.vue +17 -8
  39. package/src-admin/components/routes/DataRouteMixin.js +24 -15
  40. package/src-admin/config/routing.js +0 -11
  41. package/src-admin/config/vuetify.js +22 -2
  42. package/src-admin/directives/index.js +26 -0
  43. package/src-admin/styles.scss +21 -4
  44. package/src-admin/components/pages/CreatePage.vue +0 -114
  45. package/src-admin/components/pages/DetailPage.vue +0 -143
  46. package/src-admin/components/pages/EditPageMixin.js +0 -96
  47. package/src-admin/components/pages/ListPage.vue +0 -55
  48. package/src-admin/components/routes/CreateRoute.vue +0 -15
  49. package/src-admin/components/routes/DetailRoute.vue +0 -85
  50. package/src-admin/components/routes/EditRoute.vue +0 -78
  51. package/src-admin/components/routes/ListRoute.vue +0 -110
@@ -1 +1 @@
1
- 0.0.63
1
+ 0.0.66
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afeefa/vue-app",
3
- "version": "0.0.63",
3
+ "version": "0.0.66",
4
4
  "description": "",
5
5
  "author": "Afeefa Kollektiv <kollektiv@afeefa.de>",
6
6
  "license": "MIT",
@@ -1,19 +1,38 @@
1
1
  <template>
2
- <div class="d-flex flex-wrap align-center">
3
- <div
4
- v-for="(breadcrumb, index) in breadcrumbs"
5
- :key="index"
6
- class="item mr-2 d-flex align-center"
7
- >
8
- <v-icon>$chevronRightIcon</v-icon>
9
-
10
- <router-link
11
- :to="breadcrumb.to"
12
- :exact="true"
2
+ <div class="a-breadcrumbs d-flex align-start gap-2 mr-4">
3
+ <div :class="['breadcrumbs d-flex align-center', {'flex-wrap': wrapBreadcrumbs_}]">
4
+ <div
5
+ v-for="(breadcrumb, index) in breadcrumbs"
6
+ :key="index"
7
+ class="item mr-2 d-flex align-center"
13
8
  >
14
- {{ getItemTitle(breadcrumb.title) }}
15
- </router-link>
9
+ <v-icon v-if="index > 0">
10
+ $chevronRightIcon
11
+ </v-icon>
12
+
13
+ <router-link
14
+ :to="breadcrumb.to"
15
+ :exact="true"
16
+ >
17
+ {{ breadcrumb.title }}
18
+ </router-link>
19
+ </div>
16
20
  </div>
21
+
22
+ <v-avatar
23
+ v-if="expandVisible"
24
+ class="expand"
25
+ color="#EEE"
26
+ size="1.3rem"
27
+ @click="wrapBreadcrumbs"
28
+ >
29
+ <a-icon>$caret{{ wrapBreadcrumbs_ ? 'Up' : 'Down' }}Icon</a-icon>
30
+ </v-avatar>
31
+
32
+ <div
33
+ v-else
34
+ class="expandDummy"
35
+ />
17
36
  </div>
18
37
  </template>
19
38
 
@@ -28,6 +47,8 @@ export default class ABreadcrumbs extends Vue {
28
47
  breadcrumbs = []
29
48
  titleCache = {}
30
49
  lastRoute = null
50
+ expandVisible = false
51
+ wrapBreadcrumbs_ = false
31
52
 
32
53
  created () {
33
54
  this.$events.on(SaveEvent.STOP_SAVING, this.afterSave)
@@ -101,20 +122,46 @@ export default class ABreadcrumbs extends Vue {
101
122
  }
102
123
 
103
124
  this.breadcrumbs = breadcrumbs
125
+ this.wrapBreadcrumbs_ = false
126
+
127
+ this.scrollBreadcrumbs()
128
+ }
129
+
130
+ scrollBreadcrumbs () {
131
+ this.$nextTick(() => {
132
+ const objDiv = this.$el.querySelector('.breadcrumbs')
133
+ if (objDiv.scrollWidth > objDiv.offsetWidth) {
134
+ objDiv.scrollLeft = objDiv.scrollWidth
135
+ this.expandVisible = true
136
+ } else {
137
+ objDiv.scrollLeft = 0
138
+ this.expandVisible = false
139
+ }
140
+ })
104
141
  }
105
142
 
106
- getItemTitle (title) {
107
- // title = title.concat(title).concat(title)
108
- if (title.length > 20) {
109
- title = title.slice(0, 10).trim() + '...' + title.slice(-10).trim()
143
+ wrapBreadcrumbs () {
144
+ this.wrapBreadcrumbs_ = !this.wrapBreadcrumbs_
145
+ if (this.wrapBreadcrumbs_) {
146
+ const objDiv = this.$el.querySelector('.breadcrumbs')
147
+ objDiv.scrollLeft = 0
148
+ } else {
149
+ this.scrollBreadcrumbs()
110
150
  }
111
- return title.toUpperCase()
112
151
  }
113
152
  }
114
153
  </script>
115
154
 
116
155
 
117
156
  <style lang="scss" scoped>
157
+ .a-breadcrumbs {
158
+ overflow: hidden;
159
+ }
160
+
161
+ .breadcrumbs {
162
+ overflow: hidden;
163
+ }
164
+
118
165
  .item {
119
166
  white-space: nowrap;
120
167
 
@@ -130,4 +177,14 @@ export default class ABreadcrumbs extends Vue {
130
177
  }
131
178
  }
132
179
  }
180
+
181
+ .expand {
182
+ cursor: pointer;
183
+ margin-top: 1px;
184
+ }
185
+
186
+ .expandDummy {
187
+ width: 1.5rem;
188
+ height: 1.5rem;
189
+ }
133
190
  </style>
@@ -13,7 +13,7 @@
13
13
  :label="label"
14
14
  :style="cwm_widthStyle"
15
15
  readonly
16
- v-bind="attrs"
16
+ v-bind="{...$attrs, ...attrs}"
17
17
  :rules="validationRules"
18
18
  v-on="on"
19
19
  @click.native="on.click"
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <v-icon
3
- :class="{isButton}"
3
+ :class="{button}"
4
4
  v-bind="$attrs"
5
5
  v-on="$listeners"
6
6
  >
@@ -13,17 +13,14 @@
13
13
  import { Component, Vue } from '@a-vue'
14
14
 
15
15
  @Component({
16
- props: ['button']
16
+ props: [{button: false}]
17
17
  })
18
18
  export default class AIcon extends Vue {
19
- get isButton () {
20
- return this.button !== undefined
21
- }
22
19
  }
23
20
  </script>
24
21
 
25
22
  <style lang="scss" scoped>
26
- .v-icon:not(.isButton)::after {
23
+ .v-icon:not(.button)::after {
27
24
  background: none;
28
25
  }
29
26
  </style>
@@ -1,12 +1,11 @@
1
1
  <template>
2
2
  <v-btn
3
- small
4
3
  v-bind="$attrs"
5
4
  v-on="$listeners"
6
5
  >
7
6
  <v-icon
8
7
  left
9
- class="mr-0"
8
+ class="mr-1"
10
9
  >
11
10
  {{ icon }}
12
11
  </v-icon>
@@ -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 {
@@ -58,13 +58,6 @@ export default class ARow extends Vue {
58
58
  <style scoped lang="scss">
59
59
  .a-row {
60
60
  display: flex;
61
- overflow: hidden;
62
- @media (max-width: 900px), (orientation : portrait) {
63
- flex-wrap: wrap;
64
- & > * {
65
- flex: 0 0 auto;
66
- }
67
- }
68
61
 
69
62
  &.full {
70
63
  width: 100%;
@@ -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