aboutsummaryrefslogtreecommitdiff
path: root/resources/vue/components
diff options
context:
space:
mode:
Diffstat (limited to 'resources/vue/components')
-rw-r--r--resources/vue/components/SortableToggleElement.vue120
-rw-r--r--resources/vue/components/my-courses/TableView.vue26
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: {