From c03609142b8cce685e9903bcd77c37d3efae889d Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Tue, 21 Apr 2026 14:05:19 +0200 Subject: [PATCH] dropdown menu formatter logic; dropdown menu from action col css fix; phrasen preload; WIP semesterstunden verification with the old page - hours dont add up yet; --- public/css/components/MyLv.css | 5 + public/js/components/Cis/Mylv/Table.js | 148 ++++++++++++++++++++----- 2 files changed, 126 insertions(+), 27 deletions(-) diff --git a/public/css/components/MyLv.css b/public/css/components/MyLv.css index 267e2f45e..1305cfc36 100644 --- a/public/css/components/MyLv.css +++ b/public/css/components/MyLv.css @@ -6,3 +6,8 @@ color: var(--fhc-myLv-disabled) !important; cursor: default; } + +/* adjustment to have bs5 dropdownmenus rendered properly over a tabulator table */ +.mylv-semester-table .tabulator-cell { + overflow: unset; +} \ No newline at end of file diff --git a/public/js/components/Cis/Mylv/Table.js b/public/js/components/Cis/Mylv/Table.js index 09c1b83ab..e16c96cc4 100644 --- a/public/js/components/Cis/Mylv/Table.js +++ b/public/js/components/Cis/Mylv/Table.js @@ -19,13 +19,16 @@ export default { mylvTableOptions: { height: Vue.ref(400), index: 'lehrveranstaltung_id', - layout: 'fitDataStretch', + layout: 'fitData', placeholder: this.$p.t('global/noDataAvailable'), columns: [ {title: Vue.computed(() => this.$p.t('lehre/studiengang')), field: 'sg_bezeichnung', widthGrow: 1}, {title: Vue.computed(() => this.$p.t('global/bezeichnung')), field: 'bezeichnung', widthGrow: 2}, {title: Vue.computed(() => this.$p.t('lehre/orgform')), field: 'orgform_kurzbz', widthGrow: 1}, {title: Vue.computed(() => this.$p.t('lehre/kurzbz')), field: 'studiengang_kuerzel', widthGrow: 1}, + {title: Vue.computed(() => this.$p.t('lehre/semesterstunden')), field: 'semesterstunden', + bottomCalc: this.semesterstundenCalc, bottomCalcFormatter: this.semesterstundenCalcFormatter, bottomCalcParams: this.semesterstundenParamLookup, + widthGrow: 1, visible: true}, {title: Vue.computed(() => this.$p.t('global/actions')), headerSort: false, field: 'menu', formatter: this.actionFormatter, widthGrow: 1, tooltip: this.spoofingFunc} ], @@ -47,6 +50,22 @@ export default { }, methods: { + semesterstundenCalcFormatter(cell) { + + return cell.getValue() + }, + semesterstundenParamLookup (values, data) { + const first = data[0] + debugger + return first ? first.anwesenheit + ' %' : '' + }, + semesterstundenCalc(values, data) { + let sum = 0 + values.forEach(val => { + sum += Number(val) + }) + return sum + }, spoofingFunc() { // intentionally send empty tooltip info so tabulator tooltip doesnt get rendered but hover event propagates // to individual button tooltips @@ -84,34 +103,109 @@ export default { str.length > limit + 3 ? `${str.slice(0, limit)}...` : str; data.menu.forEach((lvLink) => { - let button = document.createElement('button'); - button.className = 'btn btn-outline-secondary btn-action'; - if (!lvLink.c4_link) { - button.classList.add('unavailable'); - } - const icon = lvLink.c4_icon2 ?? 'fa-solid fa-pen-to-square' - button.id = lvLink.name - button.innerHTML = ''; - - button.title = lvLink.phrase ? this.$p.t(lvLink.phrase) : lvLink.name; - button.innerHTML += ''+abbreviate(button.title)+''; - button.addEventListener('click', (event) => { - // replicate a tag here - event.preventDefault(); - const url = this.c4_link(lvLink); - if (url) { - const target = lvLink.c4_target || '_blank'; + // render dropdown if we have a link and some some linklist + const hasDropdown = (lvLink.c4_moodle_links?.length || lvLink.c4_linkList?.length) && lvLink.c4_link; - if (target === '_blank') { - window.open(url, '_blank', 'noopener,noreferrer'); + if (hasDropdown) { + // button group + const group = document.createElement('div'); + group.className = 'btn-group'; + + // main action button + const button = document.createElement('a'); + button.className = 'fhc-body text-decoration-none text-truncate'; + if (!lvLink.c4_link) button.classList.add('unavailable'); + button.id = lvLink.name; + + const icon = lvLink.c4_icon2 ?? 'fa-solid fa-pen-to-square'; + const label = lvLink.phrase ? this.$p.t(lvLink.phrase) : lvLink.name; + button.title = label; + button.innerHTML = `${abbreviate(label)}`; + + button.addEventListener('click', (event) => { + event.preventDefault(); + const url = this.c4_link(lvLink); + if (url) { + const target = lvLink.c4_target || '_blank'; + if (target === '_blank') { + window.open(url, '_blank', 'noopener,noreferrer'); + } else { + window.location.href = url; + } } else { - window.location.href = url; + console.warn("Link is unavailable for:", lvLink.name); } - } else { - console.warn("Link is unavailable for:", lvLink.name); + }); + + // toggle button + const toggle = document.createElement('button'); + toggle.className = 'btn btn-sm dropdown-toggle dropdown-toggle-split border-0'; + toggle.type = 'button'; + toggle.dataset.bsToggle = 'dropdown'; // uses absolute position which gets clipped by tabulator + toggle.ariaExpanded = 'false'; + toggle.innerHTML = 'Toggle Dropdown'; + + // dropdown menu + const dropMenu = document.createElement('ul'); + dropMenu.className = 'dropdown-menu dropdown-menu p-0'; + dropMenu.style.zIndex = 9999 // over 9000 + dropMenu.style.position = 'fixed' + + // moodle links have priority to be dropdown items but both can be! + const items = lvLink.c4_moodle_links?.length + ? lvLink.c4_moodle_links.map(item => ({ text: item.lehrform, href: item.url })) + : lvLink.c4_linkList.map(([text, link]) => ({ text, href: link })); + + for(let i = 0; i < 10; i++) { + items.push({text: 'puffer', href: 'www.google.com'}) } - }); - container.append(button); + + items.forEach(({ text, href }) => { + const li = document.createElement('li'); + const a = document.createElement('a'); + a.className = 'dropdown-item border-bottom'; + a.href = href; + a.target = '#'; + a.textContent = text; + li.appendChild(a); + dropMenu.appendChild(li); + }); + + group.appendChild(button); + group.appendChild(toggle); + group.appendChild(dropMenu); + container.appendChild(group); + + } else { + // action button only + const button = document.createElement('a'); + button.className = 'fhc-body text-decoration-none text-truncate'; + if (!lvLink.c4_link) button.classList.add('unavailable'); + button.id = lvLink.name; + + const icon = lvLink.c4_icon2 ?? 'fa-solid fa-pen-to-square'; + const label = lvLink.phrase ? this.$p.t(lvLink.phrase) : lvLink.name; + button.title = label; + button.innerHTML = `${abbreviate(label)}`; + + button.addEventListener('click', (event) => { + event.preventDefault(); + const url = this.c4_link(lvLink); + if (url) { + const target = lvLink.c4_target || '_blank'; + if (target === '_blank') { + window.open(url, '_blank', 'noopener,noreferrer'); + } else { + window.location.href = url; + } + } else { + console.warn("Link is unavailable for:", lvLink.name); + } + }); + + container.appendChild(button); + } + }) } @@ -142,7 +236,7 @@ export default { } }, created() { - this.phrasenPromise = this.$p.loadCategory(['global']) + this.phrasenPromise = this.$p.loadCategory(['global', 'lehre', 'lvinfo']) this.phrasenPromise.then(()=> {this.phrasenResolved = true}) }, mounted() { @@ -161,7 +255,7 @@ export default { } }, template: ` -
+