1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
<script setup lang="ts">
import {computed, useSlots} from "vue";
import {$gettextInterpolate} from "../../assets/javascripts/lib/gettext";
const props = defineProps({
tag: {
type: String,
default: 'th'
},
scope: {
type: String,
default: 'col',
validator(value: string | null): boolean {
return [null, 'row', 'col', 'rowgroup', 'colgroup'].includes(value);
}
},
column: {
type: String,
required: true
},
sortBy: {
type: String,
default : ''
},
sortDir: {
type: String,
default: 'asc'
},
active: {
type: Boolean,
default: true
},
label: {
type: String,
default: null
}
});
const emit = defineEmits(['update:sortBy', 'update:sortDir']);
const slots = useSlots();
const isActive = computed(() => props.sortBy === props.column);
const baseLabel = computed(() => {
if (props.label) {
return props.label;
}
const vnode = slots.default?.()[0];
return vnode?.children?.toString() ?? '';
});
const ariaSort = computed(() => {
if (!props.active) {
return undefined;
}
if (!isActive.value) {
return 'none';
}
return props.sortDir === 'asc' ? 'ascending' : 'descending';
});
const ariaLabel = computed(() => {
if (!props.active) {
return undefined;
}
if (!isActive.value || props.sortDir === 'desc') {
return $gettextInterpolate(
'Sortieren nach %{label}, aufsteigend sortieren.',
{label: baseLabel.value}
);
}
return $gettextInterpolate(
'Sortieren nach %{label}, absteigend sortieren.',
{label: baseLabel.value}
);
});
const cssClasses = computed(() => {
if (!props.active || !isActive.value) {
return [];
}
return props.sortDir === 'asc' ? ['sortasc'] : ['sortdesc'];
});
const toggleSort = () => {
let newDir = 'asc';
if (isActive.value) {
newDir = props.sortDir === 'asc' ? 'desc' : 'asc';
}
emit('update:sortBy', props.column);
emit('update:sortDir', newDir);
};
</script>
<template>
<component :is="tag"
:scope="scope"
:aria-sort="ariaSort"
:class="cssClasses"
>
<template v-if="!active">
<slot name="default"></slot>
</template>
<button v-else
type="button"
class="as-link"
@click="toggleSort"
:title="label"
:aria-label="ariaLabel"
>
<slot name="default"></slot>
</button>
</component>
</template>
|