diff options
Diffstat (limited to 'resources/vue/components')
| -rw-r--r-- | resources/vue/components/SortableToggleElement.vue | 120 | ||||
| -rw-r--r-- | resources/vue/components/my-courses/TableView.vue | 26 |
2 files changed, 136 insertions, 10 deletions
diff --git a/resources/vue/components/SortableToggleElement.vue b/resources/vue/components/SortableToggleElement.vue new file mode 100644 index 0000000..5024dab --- /dev/null +++ b/resources/vue/components/SortableToggleElement.vue @@ -0,0 +1,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> diff --git a/resources/vue/components/my-courses/TableView.vue b/resources/vue/components/my-courses/TableView.vue index f8ffa85..ab4951f 100644 --- a/resources/vue/components/my-courses/TableView.vue +++ b/resources/vue/components/my-courses/TableView.vue @@ -18,16 +18,20 @@ </span> </th> <th></th> - <th v-if="displaySemNumber" :class="getOrderClasses('number')"> - <a href="#" @click.prevent="changeOrder('number')"> - {{ $gettext('Nr.') }} - </a> - </th> - <th :class="getOrderClasses('name')"> - <a href="#" @click.prevent="changeOrder('name')"> - {{ $gettext('Name') }} - </a> - </th> + <sortable-toggle-element v-if="displaySemNumber" + column="number" + v-model:sort-by="orderBy" + v-model:sort-dir="orderDir" + :label="$gettext('Veranstaltungsnummer')" + > + {{ $gettext('Nr.') }} + </sortable-toggle-element> + <sortable-toggle-element column="name" + v-model:sort-by="orderBy" + v-model:sort-dir="orderDir" + > + {{ $gettext('Name') }} + </sortable-toggle-element> <th v-if="!responsiveDisplay" >{{ $gettext('Inhalt') }}</th> <th v-if="!responsiveDisplay"></th> </tr> @@ -88,6 +92,7 @@ <script> import MyCoursesMixin from '../../mixins/MyCoursesMixin.js'; +import SortableToggleElement from "../SortableToggleElement.vue"; const defaultIconSize = parseInt( getComputedStyle(document.body).getPropertyValue('--icon-size-default'), @@ -96,6 +101,7 @@ const defaultIconSize = parseInt( export default { name: 'TableView', + components: {SortableToggleElement}, mixins: [MyCoursesMixin], props: { iconSize: { |
