diff options
| author | Philipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de> | 2024-09-24 10:53:31 +0200 |
|---|---|---|
| committer | Philipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de> | 2024-09-24 10:53:31 +0200 |
| commit | 4459dd7917f4d1c34f40bb68f0e991e9c3d53e4c (patch) | |
| tree | 5c07151ae61276d334e88f6309c30d439a85c12e /resources/assets/javascripts/bootstrap | |
| parent | da0022e5c1abbf9825ae76debaabdff7e8623bb4 (diff) | |
| parent | 97a188592c679890a25c37ab78463add76a52ff7 (diff) | |
Merge branch 'main' into issue-3911issue-3911
Diffstat (limited to 'resources/assets/javascripts/bootstrap')
17 files changed, 125 insertions, 151 deletions
diff --git a/resources/assets/javascripts/bootstrap/application.js b/resources/assets/javascripts/bootstrap/application.js index a9f53df..4ad248f 100644 --- a/resources/assets/javascripts/bootstrap/application.js +++ b/resources/assets/javascripts/bootstrap/application.js @@ -353,6 +353,7 @@ jQuery(document).on('click', 'a[data-behaviour~="ajax-toggle"]', function (event (function ($) { $(document).on('click', 'form[name=course-details] fieldset legend', function () { $('#open_variable').attr('value', $(this).parent('fieldset').data('open')); + $(this).parent('fieldset').attr('aria-expanded', $(this).parent('fieldset').attr('aria-expanded') == 'true' ? 'false' : 'true'); }); }(jQuery)); diff --git a/resources/assets/javascripts/bootstrap/article.js b/resources/assets/javascripts/bootstrap/article.js index fbfc131..04316f5 100644 --- a/resources/assets/javascripts/bootstrap/article.js +++ b/resources/assets/javascripts/bootstrap/article.js @@ -13,6 +13,7 @@ // Open the contentbox article.toggleClass('open').removeClass('new'); + article.attr('aria-expanded', article.attr('aria-expanded') === 'true' ? 'false' : 'true'); }); // Open closed article contents when location hash matches diff --git a/resources/assets/javascripts/bootstrap/clipboard.js b/resources/assets/javascripts/bootstrap/clipboard.js index e525b35..a64a605 100644 --- a/resources/assets/javascripts/bootstrap/clipboard.js +++ b/resources/assets/javascripts/bootstrap/clipboard.js @@ -25,7 +25,9 @@ STUDIP.domReady(function () { jQuery(document).on('click', '.clipboard-remove-button', function (event) { event.preventDefault(); - STUDIP.Dialog.confirm($(this).data('confirm-message'), function() { + + const message = $(this).data('confirm-message'); + STUDIP.Dialog.confirm(message).done(() => { STUDIP.Clipboard.handleRemoveClick(event.target); }); }); @@ -62,10 +64,11 @@ STUDIP.domReady(function () { }); }); - jQuery(document).on('submit', '.clipboard-widget .new-clipboard-form', function (event) { - event.preventDefault(); - STUDIP.Clipboard.handleAddForm(event); - }); + jQuery(document).on( + 'submit', + '.clipboard-widget .new-clipboard-form', + STUDIP.Clipboard.handleAddForm + ); jQuery(document).on('click', '.clipboard-add-item-button', function (event) { event.preventDefault(); diff --git a/resources/assets/javascripts/bootstrap/consultations.js b/resources/assets/javascripts/bootstrap/consultations.js index ef79d9c..51ffa85 100644 --- a/resources/assets/javascripts/bootstrap/consultations.js +++ b/resources/assets/javascripts/bootstrap/consultations.js @@ -10,9 +10,9 @@ $(document).on('click', '.consultation-delete-check:not(.ignore)', event => { } let requests = ids.map(id => { - return STUDIP.jsonapi.GET(`consultation-slots/${id}/bookings`).then(result => result.data.length); + return STUDIP.jsonapi.withPromises().get(`consultation-slots/${id}/bookings`).then(response => response.data.length); }); - $.when(...requests).done((...results) => { + Promise.all(requests).then((...results) => { if (results.some(result => result > 0)) { $(event.target).addClass('ignore').click().removeClass('ignore'); } else { diff --git a/resources/assets/javascripts/bootstrap/contentbox.js b/resources/assets/javascripts/bootstrap/contentbox.js index 42c5df1..3f05331 100644 --- a/resources/assets/javascripts/bootstrap/contentbox.js +++ b/resources/assets/javascripts/bootstrap/contentbox.js @@ -13,5 +13,6 @@ $(document).on('click', 'section.contentbox article header h1 a', function(e) { // Open the contentbox article.toggleClass('open').removeClass('new'); + article.attr('aria-expanded', article.attr('aria-expanded') === 'true' ? 'false' : 'true'); } }); diff --git a/resources/assets/javascripts/bootstrap/copyable_links.js b/resources/assets/javascripts/bootstrap/copyable_links.js index 521eae4..b4f6fc6 100644 --- a/resources/assets/javascripts/bootstrap/copyable_links.js +++ b/resources/assets/javascripts/bootstrap/copyable_links.js @@ -16,24 +16,8 @@ $(document).on('click', 'a.copyable-link', function (event) { document.execCommand('Copy'); dummy.remove(); - // Show visual hint using a deferred (this way we don't need to - // duplicate the functionality in the done() handler) - (new Promise((resolve, reject) => { - let confirmation = $('<div class="copyable-link-confirmation copyable-link-success">'); - confirmation.text($gettext('Link wurde kopiert')); - confirmation.insertBefore('#content'); - - // Resolve deferred when animation has ended or after 2 seconds as a - // fail safe - let timeout = setTimeout(() => { - $(this).parent().off('animationend'); - resolve(confirmation); - }, 1500); - $(this).parent().one('animationend', () => { - clearTimeout(timeout); - resolve(confirmation); - }); - })).then((confirmation, parent) => { - confirmation.remove(); - }); + STUDIP.eventBus.emit( + 'push-system-notification', + { type: 'success', message: $gettext('Link wurde kopiert') } + ); }); diff --git a/resources/assets/javascripts/bootstrap/forms.js b/resources/assets/javascripts/bootstrap/forms.js index c34a0c1..7643612 100644 --- a/resources/assets/javascripts/bootstrap/forms.js +++ b/resources/assets/javascripts/bootstrap/forms.js @@ -1,4 +1,5 @@ import { $gettext, $gettextInterpolate } from '../lib/gettext'; +import Report from '../lib/report.js'; // Allow fieldsets to collapse $(document).on( @@ -291,9 +292,12 @@ STUDIP.ready(function () { url: v.STUDIPFORM_AUTOSAVEURL, data: params, type: 'post', - success() { - if (v.STUDIPFORM_REDIRECTURL) { - window.location.href = v.STUDIPFORM_REDIRECTURL + success(output) { + if (output === 'STUDIPFORM_STORE_SUCCESS' && v.STUDIPFORM_REDIRECTURL) { + //The form has been stored successfully: + window.location.href = v.STUDIPFORM_REDIRECTURL; + } else if (output !== 'STUDIPFORM_STORE_SUCCESS') { + Report.error($gettext('Es ist ein Fehler aufgetreten'), output); } } }); diff --git a/resources/assets/javascripts/bootstrap/global_search.js b/resources/assets/javascripts/bootstrap/global_search.js index 4d0738e..0e179b7 100644 --- a/resources/assets/javascripts/bootstrap/global_search.js +++ b/resources/assets/javascripts/bootstrap/global_search.js @@ -27,15 +27,47 @@ STUDIP.domReady(() => { // Enlarge search input on focus and show hints. $('#globalsearch-input').on('focus', function() { STUDIP.GlobalSearch.toggleSearchBar(true, false); - }); - - // Start search on Enter - $('#globalsearch-input').on('keypress', function(e) { - if (e.which === 13) { + }).on('keypress', (e) => { + // Start search on Enter + if (e.key === 'Enter') { STUDIP.GlobalSearch.doSearch(); return false; } }); + $('#globalsearch-searchbar').on('keydown', function(e) { + if (!['ArrowDown', 'ArrowUp'].includes(e.key)) { + return; + } + + e.preventDefault(); + + // Get all possible items + const items = $('#globalsearch-list [role=listitem]:visible'); + + // Find focussed element + const focussed = items.filter(':focus'); + + // Get index of focussed element in all items + let index = focussed.length > 0 ? items.index(focussed[0]) : null; + + // Move focussed element up or down in items + if (e.key === 'ArrowDown') { + index = (index ?? -1) + 1; + } else { + index = (index ?? items.length) - 1; + } + + // Clamp index to sane boundaries + if (index < 0) { + index = 0; + } else if (index > items.length - 1) { + index = items.length - 1; + } + + // Focus new element by index + items.get(index).focus(); + }); + // Close search on click on page. $('#navigation-level-1, #current-page-structure, #main-footer').on('click', function() { diff --git a/resources/assets/javascripts/bootstrap/resources.js b/resources/assets/javascripts/bootstrap/resources.js index 8c89b7f..7eb6a68 100644 --- a/resources/assets/javascripts/bootstrap/resources.js +++ b/resources/assets/javascripts/bootstrap/resources.js @@ -416,7 +416,7 @@ STUDIP.ready(function () { $("#BookingEndDateInput").prop('defaultValue', $(this).val()); $("#BookingEndDateInput").val($(this).val()).trigger('change'); } - updateRepeatEndSemesterByTimestamp(Math.floor(d / 1000)); + updateRepeatEndSemesterByTimestamp(d); } else if ($(this).attr('id') == 'BookingEndDateInput') { $("#end_date-weekdays span").addClass('invisible'); $("#end_date-weekdays #" + day_numer).removeClass('invisible'); @@ -545,38 +545,41 @@ STUDIP.ready(function () { } ); - function updateRepeatEndSemesterByTimestamp(timestamp, api_url = 'api.php/semesters') { - var semester = null; - jQuery.ajax( - STUDIP.URLHelper.getURL(api_url), - { - method: 'get', - dataType: 'json', - success: function (data) { - if (data) { - Object.values(data.collection).forEach(item => { - if (timestamp >= item.begin && timestamp < item.end) { - semester = item; - } - }); - if (semester) { - $("#semester_course_name").text(semester.title); - $(".semester-time-option").prop('disabled', false); - } else { - if (data.pagination && data.pagination.links.next != api_url) { - semester = updateRepeatEndSemesterByTimestamp(timestamp, data.pagination.links.next); - } else { - $("#semester_course_name").text('außerhalb definierter Zeiten'); - $(".semester-time-option").prop('checked', false); - $(".semester-time-option").prop('disabled', true); - $(".manual-time-option").prop('checked', true); - $(".manual-time-option").trigger('change'); - } - } - } - } + function updateRepeatEndSemesterByTimestamp(timestamp) { + (new Promise((resolve, reject) => { + const cache = STUDIP.Cache.getInstance('jsonapi'); + if (cache.has('semesters')) { + resolve(cache.get('semesters')); + } else { + STUDIP.jsonapi.GET('semesters', { data: { page: { limit: 100000 }}}) + .done(({data}) => { + cache.set('semesters', data); + resolve(data) + }) + .fail(() => { + reject(new Error('Could not load semesters')); + }); + } + })).then(semesters => { + const semester = semesters.find(({attributes}) => { + return new Date(attributes.start) <= timestamp + && timestamp <= new Date(attributes.end); + }); + + if (semester) { + $('#semester_course_name').text(semester.attributes.title); + $('.semester-time-option').prop('disabled', false); + } else { + $('#semester_course_name').text('außerhalb definierter Zeiten'); + $('.semester-time-option').prop({ + checked: false, + disabled: true + }); + $('.manual-time-option') + .prop('checked', true) + .trigger('change'); } - ); + }); } function updateViewURL(defaultView) { diff --git a/resources/assets/javascripts/bootstrap/responsive-navigation.js b/resources/assets/javascripts/bootstrap/responsive-navigation.js index aa81107..ad39d2b 100644 --- a/resources/assets/javascripts/bootstrap/responsive-navigation.js +++ b/resources/assets/javascripts/bootstrap/responsive-navigation.js @@ -1,6 +1,6 @@ import ResponsiveNavigation from '../../../vue/components/responsive/ResponsiveNavigation.vue'; -STUDIP.ready(() => { +STUDIP.domReady(() => { STUDIP.Vue.load().then(({ createApp }) => { createApp({ el: '#responsive-menu', diff --git a/resources/assets/javascripts/bootstrap/search.js b/resources/assets/javascripts/bootstrap/search.js index 6a0559b..7d4fb76 100644 --- a/resources/assets/javascripts/bootstrap/search.js +++ b/resources/assets/javascripts/bootstrap/search.js @@ -2,8 +2,6 @@ STUDIP.domReady(() => { var cache = STUDIP.Search.getCache(); // initially hide all filters except for the semester filter $('#reset-search').hide(); - STUDIP.Search.hideAllFilters(); - $('div#semester_filter').show(); STUDIP.Search.setActiveCategory('show_all_categories'); STUDIP.Search.showActiveFilters(STUDIP.Search.getFilter()); diff --git a/resources/assets/javascripts/bootstrap/studip_helper_attributes.js b/resources/assets/javascripts/bootstrap/studip_helper_attributes.js index c106de3..aea1823 100644 --- a/resources/assets/javascripts/bootstrap/studip_helper_attributes.js +++ b/resources/assets/javascripts/bootstrap/studip_helper_attributes.js @@ -276,6 +276,15 @@ $(document).on('click keydown', '[data-toggles]', function (event) { $(target).toggle(); } + const controls = $(event.currentTarget).attr('aria-controls'); + if (controls) { + // Find elements which control the expanded status of the same element. + const elements = $('[aria-controls="' + controls + '"]'); + const expanded = $(event.currentTarget).attr('aria-expanded') === 'true'; + // Set the aria-expanded status accordingly. + elements.attr('aria-expanded', !expanded); + } + event.preventDefault(); } }); diff --git a/resources/assets/javascripts/bootstrap/system-notifications.js b/resources/assets/javascripts/bootstrap/system-notifications.js new file mode 100644 index 0000000..7a85fcd --- /dev/null +++ b/resources/assets/javascripts/bootstrap/system-notifications.js @@ -0,0 +1,11 @@ +import SystemNotificationManager from '../../../vue/components/SystemNotificationManager.vue'; + +STUDIP.domReady(() => { + document.getElementById('system-notifications')?.classList.add('vueified'); + STUDIP.Vue.load().then(({ createApp }) => { + createApp({ + el: '#system-notifications', + components: { SystemNotificationManager } + }); + }); +}); diff --git a/resources/assets/javascripts/bootstrap/tooltip.js b/resources/assets/javascripts/bootstrap/tooltip.js deleted file mode 100644 index c84042b..0000000 --- a/resources/assets/javascripts/bootstrap/tooltip.js +++ /dev/null @@ -1,67 +0,0 @@ -// Attach global hover handler for tooltips. -// Applies to all elements having a "data-tooltip" attribute. -// Tooltip may be provided in the data-attribute itself or by -// defining a title attribute. The latter is prefered due to -// the obvious accessibility issues. - -var timeout = null; - -STUDIP.Tooltip.threshold = 6; - -$(document).on('mouseenter mouseleave focusin focusout', '[data-tooltip],.tooltip:has(.tooltip-content)', function(event) { - let data = $(this).data(); - - const visible = event.type === 'mouseenter' || event.type === 'focusin'; - const offset = $(this).offset(); - const x = offset.left + $(this).outerWidth(true) / 2; - const y = offset.top; - const delay = data.tooltipDelay ?? 300; - - let content; - let tooltip; - - if (!data.tooltipObject) { - // If tooltip has not yet been created (first hover), obtain it's - // contents and create the actual tooltip object. - if (!data.tooltip || !$.isPlainObject(data.tooltip)) { - content = $('<div/>').text(data.tooltip || $(this).attr('title')).html(); - } else if (data.tooltip.html !== undefined) { - content = data.tooltip.html; - } else if (data.tooltip.text !== undefined) { - content = data.tooltip.text; - } else { - throw "Invalid content for tooltip via data"; - } - if (!content) { - content = $(this).find('.tooltip-content').remove().html(); - } - $(this).attr('title', null); - $(this).attr('data-tooltip', content); - - tooltip = new STUDIP.Tooltip(x, y, content); - - data.tooltipObject = tooltip; - $(this).attr('aria-describedby', tooltip.id); - - $(this).on('remove', function() { - tooltip.remove(); - }); - } else if (visible) { - // If tooltip has already been created, update it's position. - // This is neccessary if the surrounding content is scrollable AND has - // been scrolled. Otherwise the tooltip would appear at it's previous - // and now wrong location. - data.tooltipObject.position(x, y); - } - - if (visible) { - $('.studip-tooltip').not(data.tooltipObject).hide(); - data.tooltipObject.show(); - } else { - timeout = setTimeout(() => data.tooltipObject.hide(), delay); - } -}).on('mouseenter focusin', '.studip-tooltip', () => { - clearTimeout(timeout); -}).on('mouseleave focusout', '.studip-tooltip', function() { - $(this).hide(); -}); diff --git a/resources/assets/javascripts/bootstrap/treeview.js b/resources/assets/javascripts/bootstrap/treeview.js index 998a70e..d132775 100644 --- a/resources/assets/javascripts/bootstrap/treeview.js +++ b/resources/assets/javascripts/bootstrap/treeview.js @@ -1,7 +1,8 @@ import StudipTree from '../../../vue/components/tree/StudipTree.vue' STUDIP.ready(() => { - document.querySelectorAll('[data-studip-tree]').forEach(element => { + document.querySelectorAll('[data-studip-tree]:not(.vueified)').forEach(element => { + element.classList.add('vueified'); STUDIP.Vue.load().then(({ createApp }) => { createApp({ el: element, diff --git a/resources/assets/javascripts/bootstrap/vue.js b/resources/assets/javascripts/bootstrap/vue.js index b8c938d..c6816a2 100644 --- a/resources/assets/javascripts/bootstrap/vue.js +++ b/resources/assets/javascripts/bootstrap/vue.js @@ -28,27 +28,17 @@ STUDIP.ready(() => { }); STUDIP.Vue.load().then(async ({createApp, store}) => { - let vm; if (config.store) { const storeConfig = await import(`../../../vue/store/${config.store}.js`); - console.log('store', storeConfig.default); store.registerModule(config.id, storeConfig.default, {root: true}); Object.keys(data).forEach(command => { store.commit(`${config.id}/${command}`, data[command]); }); - vm = createApp({components}); - } else { - vm = createApp({data, components}); } - // import myCoursesStore from '../stores/MyCoursesStore.js'; - // - // myCoursesStore.namespaced = true; - // - // store.registerModule('my-courses', myCoursesStore); - vm.$mount(this); + createApp({components, data}).$mount(this); }); $(this).attr('data-vue-app-created', ''); diff --git a/resources/assets/javascripts/bootstrap/wysiwyg.js b/resources/assets/javascripts/bootstrap/wysiwyg.js index fb158bb..9e18cf8 100644 --- a/resources/assets/javascripts/bootstrap/wysiwyg.js +++ b/resources/assets/javascripts/bootstrap/wysiwyg.js @@ -6,8 +6,11 @@ STUDIP.domReady(() => { $(document).on('focus blur', '.studip-dialog .ck-editor__editable_inline', function(event) { let height = this.clientHeight; let editor = this.ckeditorInstance; - editor.editing.view.change(writer => { - writer.setStyle('height', height + 'px', editor.editing.view.document.getRoot()); + // this is needed on Chrome, see https://gitlab.studip.de/studip/studip/-/issues/3510 + setTimeout(() => { + editor.editing.view.change(writer => { + writer.setStyle('height', height + 'px', editor.editing.view.document.getRoot()); + }); }); }); |
