aboutsummaryrefslogtreecommitdiff
path: root/resources/vue/components/SortableToggleElement.vue
blob: 5024dab8fd83a1a6bc4a4ef09e27d32f3c8a289a (plain)
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>