aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan-Hendrik Willms <tleilax+studip@gmail.com>2026-03-22 16:13:59 +0100
committerJan-Hendrik Willms <tleilax+studip@gmail.com>2026-03-22 16:13:59 +0100
commit1620fc15d1d21989fb38d9f3596cdf03f8535ff5 (patch)
treefe16b79cba4b1105ccbc9c4ab16b4b606dd10950
parentdda96cbbdf9237d90297ed1559f0a4d27713b2ec (diff)
implement logic that may collapse list, fixes #6380biest-6380
-rw-r--r--app/controllers/course/overview.php4
-rw-r--r--lib/classes/CourseDateList.php4
-rw-r--r--resources/assets/javascripts/bootstrap/lists.js57
-rw-r--r--resources/assets/javascripts/entry-base.js1
-rw-r--r--templates/dates/course_date_list.php13
5 files changed, 76 insertions, 3 deletions
diff --git a/app/controllers/course/overview.php b/app/controllers/course/overview.php
index 4325de8..e87417e 100644
--- a/app/controllers/course/overview.php
+++ b/app/controllers/course/overview.php
@@ -67,7 +67,9 @@ class Course_OverviewController extends AuthenticatedController
$this->next_date = $this->course->getNextDate();
$this->first_date = $this->course->getFirstDate();
$show_link = $GLOBALS["perm"]->have_studip_perm('autor', $this->course_id) && $this->course->isToolActive('schedule');
- $this->times_rooms = $this->course->getAllDatesInSemester()->toHtml();
+ $this->times_rooms = $this->course->getAllDatesInSemester()->toHtml(
+ as_collapsible_list: true
+ );
//Load lecturers:
$lecturers = $this->course->getMembersWithStatus('dozent');
diff --git a/lib/classes/CourseDateList.php b/lib/classes/CourseDateList.php
index cb1c504..02261e0 100644
--- a/lib/classes/CourseDateList.php
+++ b/lib/classes/CourseDateList.php
@@ -180,7 +180,8 @@ class CourseDateList implements Stringable
public function toHtml(
bool $group_by_rooms = false,
bool $with_room_names = false,
- bool $with_cancelled_dates = false
+ bool $with_cancelled_dates = false,
+ bool $as_collapsible_list = false
) : string {
if ($this->isEmpty()) {
return _('Die Zeiten der Veranstaltung stehen nicht fest.');
@@ -230,6 +231,7 @@ class CourseDateList implements Stringable
} else {
$template = $GLOBALS['template_factory']->open('dates/course_date_list');
$template->with_room_names = $with_room_names;
+ $template->as_collapsible_list = $as_collapsible_list;
$template->collection = $this;
}
$template->with_cancelled_dates = $with_cancelled_dates;
diff --git a/resources/assets/javascripts/bootstrap/lists.js b/resources/assets/javascripts/bootstrap/lists.js
new file mode 100644
index 0000000..ac7b85c
--- /dev/null
+++ b/resources/assets/javascripts/bootstrap/lists.js
@@ -0,0 +1,57 @@
+import {$gettext} from "@/assets/javascripts/lib/gettext";
+
+(() => {
+ let uniqueId = 0;
+
+ STUDIP.ready(() => {
+ document.querySelectorAll('.collapsible-list').forEach(list => {
+ if (!list.matches('ol,ul')) {
+ console.error('Not applicable for anything else than ol/ul lists');
+ }
+
+ const data = list.dataset;
+
+ if (data.collapsibleListInitialized !== undefined) {
+ return;
+ }
+
+ data.collapsibleListInitialized = true;
+
+ const label = data.label ?? $gettext('Einträge');
+ const max = Number.parseInt(data.collapseAfter ?? 5, 10);
+ const items = Array.from(list.children);
+ const hiddenItems = items.slice(max);
+ const hiddenCount = hiddenItems.length;
+
+ if (hiddenCount === 0) {
+ return;
+ }
+
+ const id = list.getAttribute('id') ?? `collapsable-list-${uniqueId++}`;
+ list.setAttribute('id', id);
+
+ const button = Object.assign(document.createElement('button'), {
+ className: 'as-link',
+ textContent: '',
+ type: 'button',
+ });
+ button.setAttribute('aria-controls', id);
+
+ const update = (collapsed) => {
+ button.textContent = collapsed
+ ? $gettext('%{count} weitere %{label} anzeigen', {count: hiddenCount, label})
+ : $gettext('weniger %{label} anzeigen', {label});
+ button.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
+ hiddenItems.forEach(li => li.toggleAttribute('hidden', collapsed));
+ };
+
+ update(true);
+
+ button.addEventListener('click', () => {
+ update(button.getAttribute('aria-expanded') === 'true');
+ });
+
+ list.after(button);
+ });
+ });
+})();
diff --git a/resources/assets/javascripts/entry-base.js b/resources/assets/javascripts/entry-base.js
index ad114f1..b4f807c 100644
--- a/resources/assets/javascripts/entry-base.js
+++ b/resources/assets/javascripts/entry-base.js
@@ -15,6 +15,7 @@ import "./jquery-bundle.js"
import "./init.js"
import "./bootstrap/responsive.js"
import "./bootstrap/vue.js"
+import "./bootstrap/lists.js"
import "./studip-ui.js"
import "./bootstrap/fullscreen.js"
diff --git a/templates/dates/course_date_list.php b/templates/dates/course_date_list.php
index bcc9eb7..5f2fd56 100644
--- a/templates/dates/course_date_list.php
+++ b/templates/dates/course_date_list.php
@@ -3,14 +3,25 @@
* @var CourseDateList $collection
* @var bool $with_room_names
* @var bool $with_cancelled_dates
+ * @var bool $as_collapsible_list
*
* @var SeminarCycleDate $regular_date
* @var CourseDate $single_date
* @var CourseExDate $cancelled_date
*/
?>
+<?php
+$html_attributes = HTMLAttributes::from([
+ 'class' => 'list-unstyled',
+]);
+
+if ($as_collapsible_list) {
+ $html_attributes['class'] = 'collapsible-list';
+ $html_attributes['data-label'] = _('Termine');
+}
+?>
<? if (!$collection->isEmpty()) : ?>
- <ul class="list-unstyled">
+ <ul <?= $html_attributes ?>>
<? foreach ($collection->getRegularDates() as $regular_date) : ?>
<li>
<?= $regular_date->toString($with_room_names ? 'long-start' : 'long-start-without-room', true) ?>