@afeefa/vue-app 0.0.298 → 0.0.300
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/ADatePicker.vue +5 -0
- package/src/components/APagination.vue +5 -1
- package/src/components/ASelect.vue +13 -0
- package/src/components/index.js +2 -0
- package/src/components/list/ListFilterBar.vue +177 -0
- package/src/components/list/ListFilterEvent.js +5 -0
- package/src/components/list/ListFilterMixin.js +12 -1
- package/src/components/list/ListViewMixin.js +5 -0
- package/src/components/list/filters/ListFilterDate.vue +4 -0
- package/src/components/list/filters/ListFilterPage.vue +5 -4
- package/src/components/list/filters/ListFilterSearch2.vue +14 -3
- package/src/components/list/filters/ListFilterSearchSelect.vue +5 -0
- package/src/components/list/filters/ListFilterSelect.vue +5 -0
- package/src/events.js +1 -0
- package/src-admin/components/HSeparator.vue +26 -13
- package/src-admin/styles.scss +4 -0
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.300
|
package/package.json
CHANGED
@@ -81,6 +81,11 @@ export default class ADatePicker extends Mixins(ComponentWidthMixin, UsesPositio
|
|
81
81
|
this.setFocus()
|
82
82
|
}
|
83
83
|
|
84
|
+
@Watch('formattedDate')
|
85
|
+
formattedDateChanged () {
|
86
|
+
this.$emit('displayedDateChanged', this.formattedDate)
|
87
|
+
}
|
88
|
+
|
84
89
|
get clearable () {
|
85
90
|
if (this.validator && this.validator.getParam('filled')) {
|
86
91
|
return false
|
@@ -1,5 +1,8 @@
|
|
1
1
|
<template>
|
2
|
-
<a-row
|
2
|
+
<a-row
|
3
|
+
gap="3"
|
4
|
+
class="aPagination"
|
5
|
+
>
|
3
6
|
<v-pagination
|
4
7
|
ref="pagination"
|
5
8
|
class="pagination-nav"
|
@@ -35,6 +38,7 @@ export default class APagination extends Vue {
|
|
35
38
|
<style scoped lang="scss">
|
36
39
|
:deep(.v-pagination) {
|
37
40
|
gap: .3rem;
|
41
|
+
|
38
42
|
.v-pagination__item,
|
39
43
|
.v-pagination__more,
|
40
44
|
.v-pagination__navigation {
|
@@ -8,6 +8,7 @@
|
|
8
8
|
:multiple="multiple"
|
9
9
|
v-bind="{...$attrs, dense, outlined}"
|
10
10
|
v-on="$listeners"
|
11
|
+
@change="selectedItemTitleChanged"
|
11
12
|
/>
|
12
13
|
</template>
|
13
14
|
|
@@ -47,6 +48,14 @@ export default class ASelect extends Mixins(ComponentWidthMixin) {
|
|
47
48
|
this.init()
|
48
49
|
}
|
49
50
|
|
51
|
+
@Watch('$attrs.value')
|
52
|
+
selectedItemTitleChanged () {
|
53
|
+
this.$nextTick(() => {
|
54
|
+
const title = this.$refs.select?.selectedItems[0]?.itemTitle
|
55
|
+
this.$emit('selectedItemTitleChanged', title)
|
56
|
+
})
|
57
|
+
}
|
58
|
+
|
50
59
|
@Watch('items')
|
51
60
|
itemsChanged () {
|
52
61
|
this.init()
|
@@ -86,6 +95,8 @@ export default class ASelect extends Mixins(ComponentWidthMixin) {
|
|
86
95
|
this.items_ = items
|
87
96
|
}
|
88
97
|
|
98
|
+
this.selectedItemTitleChanged()
|
99
|
+
|
89
100
|
if (this.validator) {
|
90
101
|
this.$nextTick(() => {
|
91
102
|
const valid = this.select.validate(true)
|
@@ -95,6 +106,8 @@ export default class ASelect extends Mixins(ComponentWidthMixin) {
|
|
95
106
|
this.select.selectedItems = [{
|
96
107
|
itemTitle: 'Ungültiger Wert'
|
97
108
|
}]
|
109
|
+
|
110
|
+
this.selectedItemTitleChanged()
|
98
111
|
}
|
99
112
|
})
|
100
113
|
}
|
package/src/components/index.js
CHANGED
@@ -21,6 +21,7 @@ import ListFilterSearch from './list/filters/ListFilterSearch'
|
|
21
21
|
import ListFilterSearch2 from './list/filters/ListFilterSearch2'
|
22
22
|
import ListFilterSearchSelect from './list/filters/ListFilterSearchSelect'
|
23
23
|
import ListFilterSelect from './list/filters/ListFilterSelect'
|
24
|
+
import ListFilterBar from './list/ListFilterBar'
|
24
25
|
|
25
26
|
Vue.component('EditForm', EditForm)
|
26
27
|
Vue.component('NestedEditForm', NestedEditForm)
|
@@ -43,3 +44,4 @@ Vue.component('ListFilterSearch2', ListFilterSearch2)
|
|
43
44
|
Vue.component('ListFilterSelect', ListFilterSelect)
|
44
45
|
Vue.component('ListFilterSearchSelect', ListFilterSearchSelect)
|
45
46
|
Vue.component('ListFilterDate', ListFilterDate)
|
47
|
+
Vue.component('ListFilterBar', ListFilterBar)
|
@@ -0,0 +1,177 @@
|
|
1
|
+
<template>
|
2
|
+
<div class="listFilterBar">
|
3
|
+
<a-row>
|
4
|
+
<list-filter-search2
|
5
|
+
label="Suche"
|
6
|
+
maxWidth="220"
|
7
|
+
:translateOption="title => {
|
8
|
+
return title
|
9
|
+
.replace('Sprint', $t('SPRINT'))
|
10
|
+
.replace('Kunde', $t('KUNDE'))
|
11
|
+
}"
|
12
|
+
/>
|
13
|
+
|
14
|
+
<div
|
15
|
+
class="filterToggle"
|
16
|
+
@click="collapsed = !collapsed"
|
17
|
+
>
|
18
|
+
<a-row>
|
19
|
+
<v-icon
|
20
|
+
class="mr-2"
|
21
|
+
color="#CCCCCC"
|
22
|
+
>
|
23
|
+
$filterIcon
|
24
|
+
</v-icon>
|
25
|
+
|
26
|
+
<div>Filter {{ countSelectedFilters }}/{{ countFilters }}</div>
|
27
|
+
|
28
|
+
<v-icon
|
29
|
+
class="contextButton"
|
30
|
+
size="2rem"
|
31
|
+
>
|
32
|
+
{{ collapsed ? '$caretRightIcon' : '$caretDownIcon' }}
|
33
|
+
</v-icon>
|
34
|
+
</a-row>
|
35
|
+
</div>
|
36
|
+
</a-row>
|
37
|
+
|
38
|
+
<collapse-transition>
|
39
|
+
<div
|
40
|
+
v-show="!collapsed"
|
41
|
+
class="content"
|
42
|
+
>
|
43
|
+
<a-grid
|
44
|
+
ref="filterGrid"
|
45
|
+
gap="3"
|
46
|
+
cols="3"
|
47
|
+
even
|
48
|
+
class="mt-4 pa-4"
|
49
|
+
style="background: #F4F4F4;"
|
50
|
+
>
|
51
|
+
<slot />
|
52
|
+
</a-grid>
|
53
|
+
</div>
|
54
|
+
</collapse-transition>
|
55
|
+
|
56
|
+
<div
|
57
|
+
v-if="collapsed"
|
58
|
+
:class="['chip-container', selectedFilters.length ? 'mt-4' : '']"
|
59
|
+
>
|
60
|
+
<v-chip
|
61
|
+
v-for="filter in selectedFilters"
|
62
|
+
:key="filter.name"
|
63
|
+
color="#EEEEEE"
|
64
|
+
text-color="#666666"
|
65
|
+
close
|
66
|
+
@click:close="resetFilter(filter.name)"
|
67
|
+
>
|
68
|
+
<div>{{ filter.label }}: <b>{{ filter.value }}</b></div>
|
69
|
+
</v-chip>
|
70
|
+
</div>
|
71
|
+
|
72
|
+
<list-filter-page />
|
73
|
+
</div>
|
74
|
+
</template>
|
75
|
+
|
76
|
+
|
77
|
+
<script>
|
78
|
+
import { Component, Vue } from '@a-vue'
|
79
|
+
import { ListFilterEvent } from '@a-vue/events'
|
80
|
+
|
81
|
+
@Component
|
82
|
+
export default class ListFilterBar extends Vue {
|
83
|
+
collapsed = true
|
84
|
+
selectedFilters = []
|
85
|
+
|
86
|
+
created () {
|
87
|
+
this.$events.on(ListFilterEvent.CHANGE, this.listFilterChanged)
|
88
|
+
}
|
89
|
+
|
90
|
+
destroyed () {
|
91
|
+
this.$events.off(ListFilterEvent.CHANGE, this.listFilterChanged)
|
92
|
+
}
|
93
|
+
|
94
|
+
get listView () {
|
95
|
+
let parent = this.$parent
|
96
|
+
while (parent) {
|
97
|
+
if (parent.LIST_VIEW) {
|
98
|
+
return parent
|
99
|
+
}
|
100
|
+
parent = parent.$parent
|
101
|
+
}
|
102
|
+
return null
|
103
|
+
}
|
104
|
+
|
105
|
+
get filters () {
|
106
|
+
return this.listView.filters
|
107
|
+
}
|
108
|
+
|
109
|
+
get countFilters () {
|
110
|
+
return this.$slots.default.filter(vnode => !!vnode.tag).length
|
111
|
+
}
|
112
|
+
|
113
|
+
get countSelectedFilters () {
|
114
|
+
const coreFilters = ['q', 'qfield', 'page', 'page_size']
|
115
|
+
let minus = 0
|
116
|
+
for (const name of coreFilters) {
|
117
|
+
if (!this.filters[name].hasDefaultValueSet()) {
|
118
|
+
minus++
|
119
|
+
}
|
120
|
+
}
|
121
|
+
return Object.values(this.filters).filter(f => !f.hasDefaultValueSet()).length - minus
|
122
|
+
}
|
123
|
+
|
124
|
+
listFilterChanged ({payload: {name, label, value}}) {
|
125
|
+
this.setSelectedFilter(name, label, value)
|
126
|
+
}
|
127
|
+
|
128
|
+
setSelectedFilter (name, label, value) {
|
129
|
+
const hasValue = !this.filters[name].hasDefaultValueSet()
|
130
|
+
|
131
|
+
if (!hasValue) {
|
132
|
+
this.selectedFilters = this.selectedFilters.filter(f => f.name !== name)
|
133
|
+
} else {
|
134
|
+
if (this.selectedFilters.some(f => f.name === name)) {
|
135
|
+
this.selectedFilters = this.selectedFilters.map(f => {
|
136
|
+
if (f.name === name) {
|
137
|
+
return { name, label, value }
|
138
|
+
}
|
139
|
+
return f
|
140
|
+
})
|
141
|
+
} else {
|
142
|
+
this.selectedFilters.push({ name, label, value })
|
143
|
+
}
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
resetFilter (name) {
|
148
|
+
this.listView.resetFilter(name)
|
149
|
+
this.setSelectedFilter(name, null, null)
|
150
|
+
}
|
151
|
+
}
|
152
|
+
</script>
|
153
|
+
|
154
|
+
|
155
|
+
<style lang="scss" scoped>
|
156
|
+
.filterToggle {
|
157
|
+
cursor: pointer;
|
158
|
+
background: #F4F4F4;
|
159
|
+
padding: 0 4px;
|
160
|
+
margin-left: .5rem;
|
161
|
+
}
|
162
|
+
|
163
|
+
.chip-container {
|
164
|
+
display: flex;
|
165
|
+
flex-wrap: wrap;
|
166
|
+
gap: 4px;
|
167
|
+
}
|
168
|
+
|
169
|
+
::v-deep(.aPagination) {
|
170
|
+
margin-top: 14px;
|
171
|
+
}
|
172
|
+
|
173
|
+
::v-deep(.pageSizeSelect) {
|
174
|
+
margin-top: 15px;
|
175
|
+
}
|
176
|
+
|
177
|
+
</style>
|
@@ -1,12 +1,23 @@
|
|
1
|
-
import { Component, Vue } from '@a-vue'
|
1
|
+
import { Component, Vue, Watch } from '@a-vue'
|
2
|
+
import { ListFilterEvent } from '@a-vue/events'
|
2
3
|
|
3
4
|
@Component({
|
4
5
|
props: ['name', 'label', 'optionRequestParams']
|
5
6
|
})
|
6
7
|
export class ListFilterMixin extends Vue {
|
8
|
+
displayValue = null
|
7
9
|
name_ = null
|
8
10
|
maxWidth_ = null
|
9
11
|
|
12
|
+
@Watch('displayValue')
|
13
|
+
displayValueChanged () {
|
14
|
+
this.$events.dispatch(new ListFilterEvent(ListFilterEvent.CHANGE, {
|
15
|
+
name: this.name,
|
16
|
+
label: this.label,
|
17
|
+
value: this.displayValue
|
18
|
+
}))
|
19
|
+
}
|
20
|
+
|
10
21
|
get _name () {
|
11
22
|
return this.name || this.name_
|
12
23
|
}
|
@@ -125,6 +125,11 @@ export class ListViewMixin extends Vue {
|
|
125
125
|
this.load()
|
126
126
|
}
|
127
127
|
|
128
|
+
resetFilter (filterName) {
|
129
|
+
this.filters[filterName].reset()
|
130
|
+
this.listViewModel.filterValueChanged(filterName)
|
131
|
+
}
|
132
|
+
|
128
133
|
resetFilters () {
|
129
134
|
this.listViewModel.resetFilters()
|
130
135
|
}
|
@@ -8,6 +8,7 @@
|
|
8
8
|
hide-details
|
9
9
|
v-bind="$attrs"
|
10
10
|
v-on="$listeners"
|
11
|
+
@displayedDateChanged="displayedDateChanged"
|
11
12
|
/>
|
12
13
|
</template>
|
13
14
|
|
@@ -18,5 +19,8 @@ import { ListFilterMixin } from '../ListFilterMixin'
|
|
18
19
|
|
19
20
|
@Component
|
20
21
|
export default class ListFilterDate extends Mixins(ListFilterMixin) {
|
22
|
+
displayedDateChanged (formattedDate) {
|
23
|
+
this.displayValue = formattedDate
|
24
|
+
}
|
21
25
|
}
|
22
26
|
</script>
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<template>
|
2
2
|
<a-row gap="8">
|
3
3
|
<a-pagination
|
4
|
-
v-if="count && numPages > 1"
|
4
|
+
v-if="$has.page && count && numPages > 1"
|
5
5
|
v-model="filter.value"
|
6
6
|
:length="numPages"
|
7
|
-
:
|
7
|
+
:totalVisible="_totalVisible"
|
8
8
|
>
|
9
9
|
<template v-if="$has.page_number">
|
10
10
|
<span class="pageNumber">{{ filter.value }} / {{ numPages }}</span>
|
@@ -14,6 +14,7 @@
|
|
14
14
|
<a-select
|
15
15
|
v-if="$has.page_size"
|
16
16
|
v-model="pageSizeFilter.value"
|
17
|
+
class="pageSizeSelect"
|
17
18
|
:label="label || 'Anzahl'"
|
18
19
|
:items="pageSizeFilter.options"
|
19
20
|
:defaultValue="pageSizeFilter.defaultValue"
|
@@ -32,7 +33,7 @@ import { ListFilterMixin } from '../ListFilterMixin'
|
|
32
33
|
props: ['totalVisible']
|
33
34
|
})
|
34
35
|
export default class ListFilterPage extends Mixins(ListFilterMixin) {
|
35
|
-
$hasOptions = ['page_size', {page_number: false}]
|
36
|
+
$hasOptions = ['page', 'page_size', {page_number: false}]
|
36
37
|
|
37
38
|
name_ = 'page'
|
38
39
|
|
@@ -41,7 +42,7 @@ export default class ListFilterPage extends Mixins(ListFilterMixin) {
|
|
41
42
|
}
|
42
43
|
|
43
44
|
get _totalVisible () {
|
44
|
-
return this.totalVisible === undefined ?
|
45
|
+
return this.totalVisible === undefined ? 11 : this.totalVisible // allow 0 for totalVisible
|
45
46
|
}
|
46
47
|
|
47
48
|
get numPages () {
|
@@ -27,7 +27,13 @@
|
|
27
27
|
import { Component, Mixins } from '@a-vue'
|
28
28
|
import { ListFilterMixin } from '../ListFilterMixin'
|
29
29
|
|
30
|
-
@Component
|
30
|
+
@Component({
|
31
|
+
props: [{
|
32
|
+
translateOption: {
|
33
|
+
default: () => title => title
|
34
|
+
}
|
35
|
+
}]
|
36
|
+
})
|
31
37
|
export default class ListFilterSearch2 extends Mixins(ListFilterMixin) {
|
32
38
|
name_ = 'q'
|
33
39
|
maxWidth_ = 200
|
@@ -50,7 +56,7 @@ export default class ListFilterSearch2 extends Mixins(ListFilterMixin) {
|
|
50
56
|
get qFieldOptions () {
|
51
57
|
return this.filters.qfield.options.map(o => ({
|
52
58
|
...o,
|
53
|
-
itemText: o.title,
|
59
|
+
itemText: this.translateOption(o.title),
|
54
60
|
itemValue: o.value
|
55
61
|
}))
|
56
62
|
}
|
@@ -61,7 +67,8 @@ export default class ListFilterSearch2 extends Mixins(ListFilterMixin) {
|
|
61
67
|
if (this.hasQFieldOptions() && !this.filters.qfield.hasDefaultValueSet()) {
|
62
68
|
const selected = this.qFieldOptions.filter(o => o.value === this.filters.qfield.value)[0]
|
63
69
|
if (selected) {
|
64
|
-
|
70
|
+
const qFieldTitle = this.translateOption(selected.title)
|
71
|
+
return `${label} (${qFieldTitle})`
|
65
72
|
}
|
66
73
|
}
|
67
74
|
|
@@ -73,6 +80,10 @@ export default class ListFilterSearch2 extends Mixins(ListFilterMixin) {
|
|
73
80
|
|
74
81
|
<style lang="scss" scoped>
|
75
82
|
::v-deep() {
|
83
|
+
.v-text-field .v-label {
|
84
|
+
max-width: unset;
|
85
|
+
}
|
86
|
+
|
76
87
|
.v-radio {
|
77
88
|
margin-right: 14px !important;
|
78
89
|
margin-top: -4px;
|
@@ -141,6 +141,11 @@ export default class ListFilterSearchSelect extends Mixins(ListFilterMixin) {
|
|
141
141
|
}
|
142
142
|
}
|
143
143
|
|
144
|
+
@Watch('inputModel')
|
145
|
+
inputModelChanged () {
|
146
|
+
this.displayValue = this.inputModel
|
147
|
+
}
|
148
|
+
|
144
149
|
calculateSelectorSize () {
|
145
150
|
const input = this.$refs.input.$el
|
146
151
|
const inputWidth = input.offsetWidth
|
@@ -11,6 +11,7 @@
|
|
11
11
|
hide-details
|
12
12
|
v-bind="$attrs"
|
13
13
|
v-on="$listeners"
|
14
|
+
@selectedItemTitleChanged="selectedItemTitleChanged"
|
14
15
|
/>
|
15
16
|
</template>
|
16
17
|
|
@@ -34,6 +35,10 @@ export default class ListFilterSelect extends Mixins(ListFilterMixin) {
|
|
34
35
|
this.reloadOptions()
|
35
36
|
}
|
36
37
|
|
38
|
+
selectedItemTitleChanged (title) {
|
39
|
+
this.displayValue = title
|
40
|
+
}
|
41
|
+
|
37
42
|
get clearable () {
|
38
43
|
if (this.multiple) {
|
39
44
|
return !!this.filter.value?.length
|
package/src/events.js
CHANGED
@@ -4,3 +4,4 @@ export { SaveEvent } from './components/save-indicator/SaveEvent'
|
|
4
4
|
export { AlertEvent } from './components/alert/AlertEvent'
|
5
5
|
export { DialogEvent } from './components/dialog/DialogEvent'
|
6
6
|
export { FlyingContextEvent } from './components/flying-context/FlyingContextEvent'
|
7
|
+
export { ListFilterEvent } from './components/list/ListFilterEvent'
|
@@ -6,13 +6,26 @@
|
|
6
6
|
:class="['label', {collapsible}]"
|
7
7
|
@click="collapseClick"
|
8
8
|
>
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
<a-row>
|
10
|
+
<v-icon
|
11
|
+
v-if="icon"
|
12
|
+
class="mr-2"
|
13
|
+
color="#CCCCCC"
|
14
|
+
>
|
15
|
+
{{ icon }}
|
14
16
|
</v-icon>
|
15
|
-
|
17
|
+
|
18
|
+
<div>{{ label }}</div>
|
19
|
+
|
20
|
+
<template v-if="collapsible">
|
21
|
+
<v-icon
|
22
|
+
class="contextButton"
|
23
|
+
size="2rem"
|
24
|
+
>
|
25
|
+
{{ collapsed_ ? '$caretRightIcon' : '$caretDownIcon' }}
|
26
|
+
</v-icon>
|
27
|
+
</template>
|
28
|
+
</a-row>
|
16
29
|
</div>
|
17
30
|
</div>
|
18
31
|
</template>
|
@@ -22,7 +35,7 @@
|
|
22
35
|
import { Component, Vue, Watch } from '@a-vue'
|
23
36
|
|
24
37
|
@Component({
|
25
|
-
props: ['label', {first: false, collapsible: false, collapsed: false}]
|
38
|
+
props: ['label', 'icon', {first: false, collapsible: false, collapsed: false}]
|
26
39
|
})
|
27
40
|
export default class HSeparator extends Vue {
|
28
41
|
collapsed_ = false
|
@@ -59,7 +72,7 @@ export default class HSeparator extends Vue {
|
|
59
72
|
|
60
73
|
hr {
|
61
74
|
border: none;
|
62
|
-
border-top: 2px solid #
|
75
|
+
border-top: 2px solid #DDDDDD;
|
63
76
|
}
|
64
77
|
|
65
78
|
.label {
|
@@ -67,12 +80,12 @@ hr {
|
|
67
80
|
background: white;
|
68
81
|
padding: 0 1rem;
|
69
82
|
text-transform: uppercase;
|
70
|
-
letter-spacing:
|
71
|
-
color: #
|
72
|
-
font-size: .
|
83
|
+
letter-spacing: 3px;
|
84
|
+
color: #BBBBBB;
|
85
|
+
font-size: .9rem;
|
73
86
|
white-space: nowrap;
|
74
87
|
|
75
|
-
left:
|
88
|
+
left: 50%;
|
76
89
|
transform: translateX(-50%);
|
77
90
|
|
78
91
|
&.collapsible {
|
@@ -81,7 +94,7 @@ hr {
|
|
81
94
|
&:hover {
|
82
95
|
color: #333333;
|
83
96
|
|
84
|
-
.v-icon {
|
97
|
+
.v-icon.contextButton {
|
85
98
|
color: #333333 !important;
|
86
99
|
}
|
87
100
|
}
|
package/src-admin/styles.scss
CHANGED