@afeefa/vue-app 0.0.287 → 0.0.289
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.
- package/.afeefa/package/release/version.txt +1 -1
- package/package.json +1 -1
- package/src/components/ADraggableCheckboxGroup.vue +250 -0
- package/src/components/ATableHeader.vue +2 -2
- package/src/components/list/filters/ListFilterSelect.vue +5 -2
- package/src-admin/components/list/ListColumnSelector.vue +69 -11
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.289
|
package/package.json
CHANGED
@@ -0,0 +1,250 @@
|
|
1
|
+
<template>
|
2
|
+
<div>
|
3
|
+
<v-label v-if="$attrs.label">
|
4
|
+
{{ $attrs.label }}
|
5
|
+
</v-label>
|
6
|
+
|
7
|
+
<a-row vertical>
|
8
|
+
<div
|
9
|
+
v-for="(option, index) in options_"
|
10
|
+
:key="option.itemValue"
|
11
|
+
class="draggable-checkbox"
|
12
|
+
:class="{ 'dragging': draggedIndex === index, 'drag-over': dragOverIndex === index }"
|
13
|
+
@dragover="onDragOver(index, $event)"
|
14
|
+
@dragleave="onDragLeave"
|
15
|
+
@drop="onDrop(index, $event)"
|
16
|
+
>
|
17
|
+
<div class="checkbox-container">
|
18
|
+
<div
|
19
|
+
class="drag-handle"
|
20
|
+
:draggable="true"
|
21
|
+
@dragstart="onDragStart(index, $event)"
|
22
|
+
@dragend="onDragEnd"
|
23
|
+
>
|
24
|
+
<a-icon
|
25
|
+
size="1.5rem"
|
26
|
+
:icon="$icons.dragHandle"
|
27
|
+
/>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<a-checkbox
|
31
|
+
:label="option.itemText"
|
32
|
+
:value="isChecked(option.itemValue)"
|
33
|
+
hide-details
|
34
|
+
@input="checked(option.itemValue, $event)"
|
35
|
+
>
|
36
|
+
<template #label>
|
37
|
+
<div v-html="option.itemText" />
|
38
|
+
</template>
|
39
|
+
</a-checkbox>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
</a-row>
|
43
|
+
|
44
|
+
<div
|
45
|
+
v-if="errorMessages.length"
|
46
|
+
class="mt-1 error--text v-messages"
|
47
|
+
>
|
48
|
+
{{ errorMessages[0] }}
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
</template>
|
52
|
+
|
53
|
+
|
54
|
+
<script>
|
55
|
+
import { Component, Vue, Watch, Inject } from '@a-vue'
|
56
|
+
|
57
|
+
@Component({
|
58
|
+
props: ['value', 'options', 'validator']
|
59
|
+
})
|
60
|
+
export default class ACheckboxGroup extends Vue {
|
61
|
+
value_ = []
|
62
|
+
options_ = []
|
63
|
+
errorMessages = []
|
64
|
+
hasError = false
|
65
|
+
draggedIndex = null
|
66
|
+
dragOverIndex = null
|
67
|
+
|
68
|
+
@Inject({ from: 'form', default: null }) form
|
69
|
+
|
70
|
+
created () {
|
71
|
+
this.form && this.form.register(this)
|
72
|
+
this.value_ = this.value || []
|
73
|
+
this.init()
|
74
|
+
}
|
75
|
+
|
76
|
+
checked (key, value) {
|
77
|
+
if (value) {
|
78
|
+
if (!this.isChecked(key)) {
|
79
|
+
this.value_.push(key)
|
80
|
+
}
|
81
|
+
} else {
|
82
|
+
this.value_ = this.value_.filter(v => v !== key)
|
83
|
+
}
|
84
|
+
this.$emit('input', this.value_)
|
85
|
+
this.validate()
|
86
|
+
}
|
87
|
+
|
88
|
+
isChecked (key) {
|
89
|
+
return this.value_.includes(key)
|
90
|
+
}
|
91
|
+
|
92
|
+
@Watch('options')
|
93
|
+
optionsChanged () {
|
94
|
+
this.init()
|
95
|
+
}
|
96
|
+
|
97
|
+
@Watch('value')
|
98
|
+
valueChanged (value) {
|
99
|
+
this.value_ = value || []
|
100
|
+
}
|
101
|
+
|
102
|
+
async init () {
|
103
|
+
const options = typeof this.options === 'function' ? this.options() : this.options
|
104
|
+
|
105
|
+
if (options instanceof Promise) {
|
106
|
+
this.options_ = await options
|
107
|
+
} else {
|
108
|
+
this.options_ = options
|
109
|
+
}
|
110
|
+
|
111
|
+
this.$nextTick(() => {
|
112
|
+
this.validate()
|
113
|
+
})
|
114
|
+
}
|
115
|
+
|
116
|
+
validate () {
|
117
|
+
this.errorMessages = []
|
118
|
+
if (this.validator) {
|
119
|
+
const rules = this.validator.getRules(this.$attrs.label)
|
120
|
+
for (const rule of rules) {
|
121
|
+
const message = rule(this.value_)
|
122
|
+
if (typeof message === 'string') {
|
123
|
+
this.errorMessages.push(message)
|
124
|
+
break
|
125
|
+
}
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
this.hasError = !!this.errorMessages.length // VForm will watch the hasError prop
|
130
|
+
return !this.errorMessages.length
|
131
|
+
}
|
132
|
+
|
133
|
+
onDragStart (index, event) {
|
134
|
+
this.draggedIndex = index
|
135
|
+
event.dataTransfer.effectAllowed = 'move'
|
136
|
+
event.dataTransfer.setData('text/html', event.target)
|
137
|
+
|
138
|
+
// Set the entire checkbox container as the drag image
|
139
|
+
const checkboxContainer = event.target.closest('.draggable-checkbox')
|
140
|
+
if (checkboxContainer) {
|
141
|
+
event.dataTransfer.setDragImage(checkboxContainer, 0, 0)
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
onDragEnd () {
|
146
|
+
this.draggedIndex = null
|
147
|
+
this.dragOverIndex = null
|
148
|
+
|
149
|
+
this.$emit('orderChanged', this.options_)
|
150
|
+
}
|
151
|
+
|
152
|
+
onDragOver (index, event) {
|
153
|
+
event.preventDefault()
|
154
|
+
event.stopPropagation()
|
155
|
+
event.dataTransfer.dropEffect = 'move'
|
156
|
+
this.dragOverIndex = index
|
157
|
+
}
|
158
|
+
|
159
|
+
onDragLeave (event) {
|
160
|
+
// Only clear dragOverIndex if we're actually leaving the draggable-checkbox container
|
161
|
+
const rect = event.currentTarget.getBoundingClientRect()
|
162
|
+
const x = event.clientX
|
163
|
+
const y = event.clientY
|
164
|
+
|
165
|
+
if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
|
166
|
+
this.dragOverIndex = null
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
onDrop (index, event) {
|
171
|
+
event.preventDefault()
|
172
|
+
|
173
|
+
if (this.draggedIndex !== null && this.draggedIndex !== index) {
|
174
|
+
// Create a copy of the options array
|
175
|
+
const newOptions = [...this.options_]
|
176
|
+
|
177
|
+
// Remove the dragged item
|
178
|
+
const draggedItem = newOptions.splice(this.draggedIndex, 1)[0]
|
179
|
+
|
180
|
+
// Insert it at the new position
|
181
|
+
newOptions.splice(index, 0, draggedItem)
|
182
|
+
|
183
|
+
// Update the options
|
184
|
+
this.options_ = newOptions
|
185
|
+
|
186
|
+
// Emit an event to notify parent component about the reorder
|
187
|
+
this.$emit('reorder', newOptions)
|
188
|
+
}
|
189
|
+
|
190
|
+
this.draggedIndex = null
|
191
|
+
this.dragOverIndex = null
|
192
|
+
}
|
193
|
+
}
|
194
|
+
</script>
|
195
|
+
|
196
|
+
<style scoped>
|
197
|
+
.draggable-checkbox {
|
198
|
+
transition: all .2s ease;
|
199
|
+
border-radius: 4px;
|
200
|
+
padding: 2px;
|
201
|
+
}
|
202
|
+
|
203
|
+
.draggable-checkbox:hover {
|
204
|
+
background-color: rgba(0, 0, 0, .04);
|
205
|
+
}
|
206
|
+
|
207
|
+
.draggable-checkbox.dragging {
|
208
|
+
opacity: .5;
|
209
|
+
transform: scale(.95);
|
210
|
+
}
|
211
|
+
|
212
|
+
.draggable-checkbox.drag-over {
|
213
|
+
background-color: rgba(25, 118, 210, .1);
|
214
|
+
border: 2px dashed rgba(25, 118, 210, .3);
|
215
|
+
}
|
216
|
+
|
217
|
+
.checkbox-container {
|
218
|
+
display: flex;
|
219
|
+
align-items: center;
|
220
|
+
}
|
221
|
+
|
222
|
+
.drag-handle {
|
223
|
+
cursor: grab;
|
224
|
+
color: rgba(0, 0, 0, .54);
|
225
|
+
transition: all .2s ease;
|
226
|
+
display: flex;
|
227
|
+
align-items: center;
|
228
|
+
justify-content: center;
|
229
|
+
min-width: 20px;
|
230
|
+
height: 20px;
|
231
|
+
}
|
232
|
+
|
233
|
+
.drag-handle:hover {
|
234
|
+
background-color: rgba(0, 0, 0, .08);
|
235
|
+
color: rgba(0, 0, 0, .87);
|
236
|
+
}
|
237
|
+
|
238
|
+
.drag-handle:active {
|
239
|
+
cursor: grabbing;
|
240
|
+
}
|
241
|
+
|
242
|
+
.draggable-checkbox .v-input--checkbox {
|
243
|
+
pointer-events: auto;
|
244
|
+
flex: 1;
|
245
|
+
}
|
246
|
+
|
247
|
+
.draggable-checkbox .v-input__slot {
|
248
|
+
cursor: pointer;
|
249
|
+
}
|
250
|
+
</style>
|
@@ -45,7 +45,7 @@ export default class ATableHeader extends Vue {
|
|
45
45
|
|
46
46
|
> * {
|
47
47
|
padding: .4rem 0;
|
48
|
-
padding-right:
|
48
|
+
padding-right: 1.5rem;
|
49
49
|
|
50
50
|
&:last-child {
|
51
51
|
padding-right: 0;
|
@@ -56,7 +56,7 @@ export default class ATableHeader extends Vue {
|
|
56
56
|
> * {
|
57
57
|
border-bottom: 1px solid #EEEEEE;
|
58
58
|
padding-left: .5rem;
|
59
|
-
padding-right:
|
59
|
+
padding-right: 1.5rem;
|
60
60
|
padding-bottom: .6rem;
|
61
61
|
|
62
62
|
&:last-child {
|
@@ -10,6 +10,7 @@
|
|
10
10
|
:defaultValue="filter.defaultValue"
|
11
11
|
hide-details
|
12
12
|
v-bind="$attrs"
|
13
|
+
v-on="$listeners"
|
13
14
|
/>
|
14
15
|
</template>
|
15
16
|
|
@@ -58,6 +59,8 @@ export default class ListFilterSelect extends Mixins(ListFilterMixin) {
|
|
58
59
|
.fromRequest(this.filter.createOptionsRequest())
|
59
60
|
.load()
|
60
61
|
|
62
|
+
this.$emit('optionsLoaded', models)
|
63
|
+
|
61
64
|
return [
|
62
65
|
...this.getOptions(),
|
63
66
|
...models.map(model => {
|
@@ -78,10 +81,10 @@ export default class ListFilterSelect extends Mixins(ListFilterMixin) {
|
|
78
81
|
]
|
79
82
|
}
|
80
83
|
|
81
|
-
getOptions (
|
84
|
+
getOptions (append = false) {
|
82
85
|
return [
|
83
86
|
...this.filter.options
|
84
|
-
.filter(o => !!o.
|
87
|
+
.filter(o => !!o.append === append) // alle entfernen, denen append nicht uebereinstimmt
|
85
88
|
.map(o => {
|
86
89
|
return {
|
87
90
|
itemTitle: o.title,
|
@@ -3,14 +3,24 @@
|
|
3
3
|
<a-context-menu>
|
4
4
|
<template #activator>
|
5
5
|
<div class="contextButton">
|
6
|
-
{{ selectedColumns.length }}/{{ Object.keys(
|
6
|
+
{{ selectedColumns.length }}/{{ Object.keys(columns_).length }}
|
7
7
|
</div>
|
8
8
|
</template>
|
9
9
|
|
10
|
-
<a-checkbox-group
|
10
|
+
<a-draggable-checkbox-group
|
11
|
+
v-if="drag"
|
11
12
|
v-model="selectedColumns"
|
12
13
|
:options="options"
|
14
|
+
v-on="$listeners"
|
15
|
+
@input="columnSelected"
|
16
|
+
@orderChanged="columnOrderChanged"
|
17
|
+
/>
|
18
|
+
|
19
|
+
<a-checkbox-group
|
20
|
+
v-else
|
21
|
+
v-model="selectedColumns"
|
13
22
|
vertical
|
23
|
+
:options="options"
|
14
24
|
v-on="$listeners"
|
15
25
|
@input="columnSelected"
|
16
26
|
/>
|
@@ -22,29 +32,77 @@
|
|
22
32
|
import { Component, Vue } from '@a-vue'
|
23
33
|
|
24
34
|
@Component({
|
25
|
-
props: ['columns']
|
35
|
+
props: ['columns', 'storageKey', {drag: false}]
|
26
36
|
})
|
27
37
|
export default class ListColumnSelector extends Vue {
|
38
|
+
columns_ = {}
|
28
39
|
selectedColumns = []
|
29
40
|
|
30
41
|
created () {
|
31
|
-
this.
|
32
|
-
|
42
|
+
this.loadColumnConfiguration()
|
43
|
+
this.saveColumnConfiguration()
|
33
44
|
}
|
34
45
|
|
35
46
|
get options () {
|
36
|
-
return Object.keys(this.columns)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
47
|
+
return Object.keys(this.columns)
|
48
|
+
.map(k => {
|
49
|
+
return {
|
50
|
+
itemText: this.columns[k].title,
|
51
|
+
itemValue: k
|
52
|
+
}
|
53
|
+
})
|
42
54
|
}
|
43
55
|
|
44
56
|
columnSelected () {
|
45
57
|
for (const key in this.columns) {
|
46
58
|
this.columns[key].visible = this.selectedColumns.includes(key)
|
47
59
|
}
|
60
|
+
this.saveColumnConfiguration()
|
61
|
+
}
|
62
|
+
|
63
|
+
columnOrderChanged (options) {
|
64
|
+
const columns = {}
|
65
|
+
for (const option of options) {
|
66
|
+
columns[option.itemValue] = this.columns[option.itemValue]
|
67
|
+
}
|
68
|
+
|
69
|
+
this.columns_ = columns
|
70
|
+
this.$emit('update:columns', this.columns_)
|
71
|
+
this.saveColumnConfiguration()
|
72
|
+
}
|
73
|
+
|
74
|
+
loadColumnConfiguration () {
|
75
|
+
if (this.storageKey) {
|
76
|
+
try {
|
77
|
+
const storageItem = localStorage.getItem(`column-config-${this.storageKey}`)
|
78
|
+
if (storageItem) {
|
79
|
+
this.columns_ = JSON.parse(storageItem)
|
80
|
+
this.selectedColumns = Object.keys(this.columns_)
|
81
|
+
.filter(k => this.columns_[k].visible)
|
82
|
+
|
83
|
+
this.$emit('update:columns', this.columns_)
|
84
|
+
return
|
85
|
+
}
|
86
|
+
} catch (error) {
|
87
|
+
console.warn('Failed to load column configuration from localStorage:', error)
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
this.columns_ = this.columns
|
92
|
+
this.selectedColumns = Object.keys(this.columns_)
|
93
|
+
.filter(k => this.columns[k].visible)
|
94
|
+
}
|
95
|
+
|
96
|
+
saveColumnConfiguration () {
|
97
|
+
if (!this.storageKey) {
|
98
|
+
return
|
99
|
+
}
|
100
|
+
|
101
|
+
try {
|
102
|
+
localStorage.setItem(`column-config-${this.storageKey}`, JSON.stringify(this.columns_))
|
103
|
+
} catch (error) {
|
104
|
+
console.warn('Failed to save column configuration to localStorage:', error)
|
105
|
+
}
|
48
106
|
}
|
49
107
|
}
|
50
108
|
</script>
|