From c7332ab2a88ba64a1e302d5d03b1f494a3437797 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 08:30:23 +0000 Subject: [PATCH 01/34] refs #554 --- .../courseware/CoursewareActionWidget.vue | 63 ++++++++++++------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareActionWidget.vue b/resources/vue/components/courseware/CoursewareActionWidget.vue index 4772bb2cc31..b9c20267801 100644 --- a/resources/vue/components/courseware/CoursewareActionWidget.vue +++ b/resources/vue/components/courseware/CoursewareActionWidget.vue @@ -1,35 +1,54 @@ <template> <ul class="widget-list widget-links cw-action-widget" v-if="structuralElement"> - <li class="cw-action-widget-show-toc" @click="toggleTOC"> - {{ tocText }} + <li class="cw-action-widget-show-toc"> + <div @click="toggleTOC" @keydown.enter="toggleTOC" tabindex="0"> + {{ tocText }} + </div> </li> - <li class="cw-action-widget-show-consume-mode" @click="showConsumeMode"> - <translate>Vollbild einschalten</translate> + <li class="cw-action-widget-show-consume-mode"> + <div @click="showConsumeMode" @keydown.enter="showConsumeMode" tabindex="0"> + <translate>Vollbild einschalten</translate> + </div> </li> - <li v-if="canEdit" class="cw-action-widget-edit" @click="editElement"> - <translate>Seite bearbeiten</translate> + <li v-if="canEdit" class="cw-action-widget-edit"> + <div @click="editElement" @keydown.enter="editElement" tabindex="0"> + <translate>Seite bearbeiten</translate> + </div> </li> - <li v-if="canEdit" class="cw-action-widget-sort" @click="sortContainers"> - <translate>Abschnitte sortieren</translate> + <li v-if="canEdit" class="cw-action-widget-sort"> + <div @click="sortContainers" @keydown.enter="sortContainers" tabindex="0"> + <translate>Abschnitte sortieren</translate> + </div> </li> - <li v-if="canEdit" class="cw-action-widget-add" @click="addElement"> - <translate>Seite hinzufügen</translate> + <li v-if="canEdit" class="cw-action-widget-add"> + <div @click="addElement" @keydown.enter="addElement" tabindex="0"> + <translate>Seite hinzufügen</translate> + </div> </li> - <li class="cw-action-widget-info" @click="showElementInfo"><translate>Informationen anzeigen</translate></li> - <li class="cw-action-widget-star" @click="createBookmark"><translate>Lesezeichen setzen</translate></li> - <li v-if="canExport" @click="exportElement" class="cw-action-widget-export"> - <translate>Seite exportieren</translate> + <li class="cw-action-widget-info"> + <div @click="showElementInfo" @keydown.enter="showElementInfo" tabindex="0"> + <translate>Informationen anzeigen</translate> + </div> </li> - <li v-if="(canEdit || userIsTeacher) && canVisit" class="cw-action-widget-export-pdf"> - <a :href="pdfExportURL"> - <translate>Seite als pdf-Dokument exportieren</translate> - </a> + <li class="cw-action-widget-star"> + <div @click="createBookmark" @keydown.enter="createBookmark" tabindex="0"> + <translate>Lesezeichen setzen</translate> + </div> </li> - <li v-if="canEdit && oerEnabled && userIsTeacher" @click="oerElement" class="cw-action-widget-oer"> - <translate>Seite auf %{oerTitle} veröffentlichen</translate> + <li v-if="canEdit" class="cw-action-widget-export"> + <div @click="exportElement" @keydown.enter="exportElement" tabindex="0"> + <translate>Seite exportieren</translate> + </div> </li> - <li v-if="!isRoot && canEdit && !isTask" class="cw-action-widget-trash" @click="deleteElement"> - <translate>Seite löschen</translate> + <li v-if="canEdit && oerEnabled" class="cw-action-widget-oer"> + <div @click="oerElement" @keydown.enter="oerElement" tabindex="0"> + <translate>Seite auf %{oerTitle} veröffentlichen</translate> + </div> + </li> + <li v-if="!isRoot && canEdit" class="cw-action-widget-trash"> + <div @click="deleteElement" @keydown.enter="deleteElement" tabindex="0"> + <translate>Seite löschen</translate> + </div> </li> </ul> </template> -- GitLab From 5bf0b4404377a4df1701a5b31cbc66bed623467a Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 08:34:44 +0000 Subject: [PATCH 02/34] refs #554 --- .../courseware/CoursewareViewWidget.vue | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareViewWidget.vue b/resources/vue/components/courseware/CoursewareViewWidget.vue index fd3ee9ff7b5..918cf8b9306 100755 --- a/resources/vue/components/courseware/CoursewareViewWidget.vue +++ b/resources/vue/components/courseware/CoursewareViewWidget.vue @@ -1,24 +1,22 @@ <template> <ul class="widget-list widget-links sidebar-views cw-view-widget"> - <li - :class="{ active: readView }" - @click="setReadView" - > - <translate>Lesen</translate> + <li :class="{ active: readView }"> + <div @click="setReadView" @keydown.enter="setReadView" tabindex="0"> + <translate>Lesen</translate> + </div> </li> - <li - v-if="canEdit" - :class="{ active: editView }" - @click="setEditView" - > - <translate>Bearbeiten</translate> + <li v-if="canEdit" :class="{ active: editView }"> + <div @click="setEditView" @keydown.enter="setEditView" tabindex="0"> + <translate>Bearbeiten</translate> + </div> </li> <li v-if="context.type === 'courses' && canVisit" :class="{ active: discussView }" - @click="setDiscussView" > - <translate>Diskutieren</translate> + <div @click="setDiscussView" @keydown.enter="setDiscussView" tabindex="0"> + <translate>Diskutieren</translate> + </div> </li> </ul> </template> -- GitLab From 16b3a6c49ef8f339543cdd39a8c4360af139b52f Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 09:23:11 +0000 Subject: [PATCH 03/34] use a tag --- .../vue/components/courseware/CoursewareViewWidget.vue | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareViewWidget.vue b/resources/vue/components/courseware/CoursewareViewWidget.vue index 918cf8b9306..39dee0ec71b 100755 --- a/resources/vue/components/courseware/CoursewareViewWidget.vue +++ b/resources/vue/components/courseware/CoursewareViewWidget.vue @@ -1,14 +1,14 @@ <template> <ul class="widget-list widget-links sidebar-views cw-view-widget"> <li :class="{ active: readView }"> - <div @click="setReadView" @keydown.enter="setReadView" tabindex="0"> + <a href="#" @click="setReadView"> <translate>Lesen</translate> - </div> - </li> + </a> + </a> <li v-if="canEdit" :class="{ active: editView }"> - <div @click="setEditView" @keydown.enter="setEditView" tabindex="0"> + <a href="#" @click="setEditView"> <translate>Bearbeiten</translate> - </div> + </a> </li> <li v-if="context.type === 'courses' && canVisit" -- GitLab From f2389b354899db2b9ccd5a6e1f594b67c9add654 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 09:28:11 +0000 Subject: [PATCH 04/34] use a tag --- .../courseware/CoursewareActionWidget.vue | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareActionWidget.vue b/resources/vue/components/courseware/CoursewareActionWidget.vue index b9c20267801..a13b30999d0 100644 --- a/resources/vue/components/courseware/CoursewareActionWidget.vue +++ b/resources/vue/components/courseware/CoursewareActionWidget.vue @@ -1,19 +1,19 @@ <template> <ul class="widget-list widget-links cw-action-widget" v-if="structuralElement"> <li class="cw-action-widget-show-toc"> - <div @click="toggleTOC" @keydown.enter="toggleTOC" tabindex="0"> + <a href="#" @click="toggleTOC"> {{ tocText }} - </div> + </a> </li> <li class="cw-action-widget-show-consume-mode"> - <div @click="showConsumeMode" @keydown.enter="showConsumeMode" tabindex="0"> + <a href="#" @click="showConsumeMode"> <translate>Vollbild einschalten</translate> - </div> + </a> </li> <li v-if="canEdit" class="cw-action-widget-edit"> - <div @click="editElement" @keydown.enter="editElement" tabindex="0"> + <a href="#" @click="editElement"> <translate>Seite bearbeiten</translate> - </div> + </a> </li> <li v-if="canEdit" class="cw-action-widget-sort"> <div @click="sortContainers" @keydown.enter="sortContainers" tabindex="0"> @@ -21,34 +21,34 @@ </div> </li> <li v-if="canEdit" class="cw-action-widget-add"> - <div @click="addElement" @keydown.enter="addElement" tabindex="0"> + <a href="#" @click="addElement"> <translate>Seite hinzufügen</translate> - </div> + </a> </li> <li class="cw-action-widget-info"> - <div @click="showElementInfo" @keydown.enter="showElementInfo" tabindex="0"> + <a href="#" @click="showElementInfo"> <translate>Informationen anzeigen</translate> - </div> + </a> </li> <li class="cw-action-widget-star"> - <div @click="createBookmark" @keydown.enter="createBookmark" tabindex="0"> + <a href="#" @click="createBookmark"> <translate>Lesezeichen setzen</translate> - </div> + </a> </li> <li v-if="canEdit" class="cw-action-widget-export"> - <div @click="exportElement" @keydown.enter="exportElement" tabindex="0"> + <a href="#" @click="exportElement"> <translate>Seite exportieren</translate> - </div> + </a> </li> <li v-if="canEdit && oerEnabled" class="cw-action-widget-oer"> - <div @click="oerElement" @keydown.enter="oerElement" tabindex="0"> + <a href="#" @click="oerElement"> <translate>Seite auf %{oerTitle} veröffentlichen</translate> - </div> + </a> </li> <li v-if="!isRoot && canEdit" class="cw-action-widget-trash"> - <div @click="deleteElement" @keydown.enter="deleteElement" tabindex="0"> + <a href="#" @click="deleteElement"> <translate>Seite löschen</translate> - </div> + </a> </li> </ul> </template> -- GitLab From 48d24b48f74b03f195b4b4284a8647ca4de9107e Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 09:41:29 +0000 Subject: [PATCH 05/34] refs #554 --- .../components/courseware/CoursewareStructuralElement.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue index bb4a9411a8b..3eafe2eee9a 100755 --- a/resources/vue/components/courseware/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue @@ -9,13 +9,13 @@ <courseware-ribbon :canEdit="canEdit && canAddElements"> <template #buttons> <router-link v-if="prevElement" :to="'/structural_element/' + prevElement.id"> - <button class="cw-ribbon-button cw-ribbon-button-prev" :title="textRibbon.perv" /> + <div class="cw-ribbon-button cw-ribbon-button-prev" :title="textRibbon.perv" /> </router-link> - <button v-else class="cw-ribbon-button cw-ribbon-button-prev-disabled" /> + <div v-else class="cw-ribbon-button cw-ribbon-button-prev-disabled" :title="$gettext('keine vorherige Seite')"/> <router-link v-if="nextElement" :to="'/structural_element/' + nextElement.id"> - <button class="cw-ribbon-button cw-ribbon-button-next" :title="textRibbon.next" /> + <div class="cw-ribbon-button cw-ribbon-button-next" :title="textRibbon.next" /> </router-link> - <button v-else class="cw-ribbon-button cw-ribbon-button-next-disabled" /> + <div v-else class="cw-ribbon-button cw-ribbon-button-next-disabled" :title="$gettext('keine nächste Seite')"/> </template> <template #breadcrumbList> <li -- GitLab From 86c214e1f3b84ad5521789b1eef87750b2599831 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 09:46:51 +0000 Subject: [PATCH 06/34] refs #554 --- resources/assets/stylesheets/scss/courseware.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss index aaeecc57c3a..560465d1167 100755 --- a/resources/assets/stylesheets/scss/courseware.scss +++ b/resources/assets/stylesheets/scss/courseware.scss @@ -278,6 +278,7 @@ $consum_ribbon_width: calc(100% - 58px); max-width: calc(100% - 106px); .cw-ribbon-nav { + display: flex; min-width: 75px; } @@ -329,14 +330,13 @@ $consum_ribbon_width: calc(100% - 58px); .cw-ribbon-button { height: 24px; width: 24px; - margin: 0 0.5em; + margin: 0 7px; + padding: 1px 2px; border: none; background-color: transparent; background-repeat: no-repeat; background-position: center; background-size: 24px; - cursor: pointer; - outline: none; &.cw-ribbon-button-menu { @include background-icon(table-of-contents, clickable, 24); -- GitLab From fe66c3ccc8d9b9232ec89dd2b0031e320e509839 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 09:48:24 +0000 Subject: [PATCH 07/34] use a tag --- .../vue/components/courseware/CoursewareRibbon.vue | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareRibbon.vue b/resources/vue/components/courseware/CoursewareRibbon.vue index b8e3cb2c878..ebef9948286 100755 --- a/resources/vue/components/courseware/CoursewareRibbon.vue +++ b/resources/vue/components/courseware/CoursewareRibbon.vue @@ -14,13 +14,20 @@ </nav> </div> <div class="cw-ribbon-wrapper-right"> - <button class="cw-ribbon-button cw-ribbon-button-menu" @click="activeToolbar" :title="textRibbon.toolbar"></button> - <button + <a + href="#" + class="cw-ribbon-button cw-ribbon-button-menu" + :title="textRibbon.toolbar" + @click="activeToolbar" + > + </a> + <a + href="#" class="cw-ribbon-button" :class="[consumeMode ? 'cw-ribbon-button-zoom-out' : 'cw-ribbon-button-zoom']" :title="consumeMode ? textRibbon.fullscreen_off : textRibbon.fullscreen_on" @click="toggleConsumeMode" - ></button> + ></a> <slot name="menu" /> </div> <div v-if="consumeMode" class="cw-ribbon-consume-bottom"></div> -- GitLab From a44456fe6bf954cb14d362f8b6562e586fa3e1ac Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 10:19:13 +0000 Subject: [PATCH 08/34] refs #554 --- .../courseware/CoursewareRibbonToolbar.vue | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue index e5b79722582..81343163c8d 100755 --- a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue +++ b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue @@ -6,32 +6,29 @@ <div class="cw-ribbon-tool-content"> <div class="cw-ribbon-tool-content-nav"> <ul> - <li - tabindex="0" - ref="focusPoint" - :class="{ active: showContents }" - @click="displayTool('contents')" - > - <translate>Inhaltsverzeichnis</translate> + <li :class="{ active: showContents }"> + <a href="#" ref="focusPoint" @click="displayTool('contents')"> + <translate>Inhaltsverzeichnis</translate> + </a> </li> <li v-if="!consumeMode && showEditMode && canEdit" - tabindex="0" :class="{ active: showBlockAdder }" - @click="displayTool('blockadder')" > - <translate>Elemente hinzufügen</translate> + <a href="#" @click="displayTool('blockadder')"> + <translate>Elemente hinzufügen</translate> + </a> </li> <li v-if="!consumeMode && displaySettings" - tabindex="0" :class="{ active: showAdmin }" - @click="displayTool('admin')" > - <translate>Einstellungen</translate> + <a href="#" @click="displayTool('admin')"> + <translate>Einstellungen</translate> + </a> </li> </ul> - <button :title="textClose" class="cw-tools-hide-button" @click="$emit('deactivate')"></button> + <a href="#" :title="textClose" class="cw-tools-hide-button" @click="$emit('deactivate')"></a> </div> <div class="cw-ribbon-tool" ref="ribbonContent" @scroll="handleScroll"> <courseware-tools-contents v-if="showContents" /> @@ -192,7 +189,14 @@ export default { if (!newValue) { this.displayTool('contents'); } - } + }, + toolsActive(newValue) { + if (newValue) { + setTimeout(() => { + this.$refs.focusPoint.focus(); + }, 300); + } + }, }, }; </script> -- GitLab From b74aed4d961106741f8883213c0769c3b124b2a3 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 10:29:53 +0000 Subject: [PATCH 09/34] add focus trap --- .../courseware/CoursewareRibbonToolbar.vue | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue index 81343163c8d..afd30d4e5d6 100755 --- a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue +++ b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue @@ -1,47 +1,50 @@ <template> - <div - class="cw-ribbon-tools" - :class="{ unfold: toolsActive, 'cw-ribbon-tools-consume': consumeMode }" - > - <div class="cw-ribbon-tool-content"> - <div class="cw-ribbon-tool-content-nav"> - <ul> - <li :class="{ active: showContents }"> - <a href="#" ref="focusPoint" @click="displayTool('contents')"> - <translate>Inhaltsverzeichnis</translate> - </a> - </li> - <li - v-if="!consumeMode && showEditMode && canEdit" - :class="{ active: showBlockAdder }" - > - <a href="#" @click="displayTool('blockadder')"> - <translate>Elemente hinzufügen</translate> - </a> - </li> - <li - v-if="!consumeMode && displaySettings" - :class="{ active: showAdmin }" - > - <a href="#" @click="displayTool('admin')"> - <translate>Einstellungen</translate> - </a> - </li> - </ul> - <a href="#" :title="textClose" class="cw-tools-hide-button" @click="$emit('deactivate')"></a> - </div> - <div class="cw-ribbon-tool" ref="ribbonContent" @scroll="handleScroll"> - <courseware-tools-contents v-if="showContents" /> - <courseware-tools-blockadder v-if="showBlockAdder" @scrollTop="scrollTop('blockadder')"/> - <courseware-tools-admin v-if="showAdmin" /> + <focus-trap v-model="trap" :initial-focus="() => $refs.focusPoint"> + <div + class="cw-ribbon-tools" + :class="{ unfold: toolsActive, 'cw-ribbon-tools-consume': consumeMode }" + > + <div class="cw-ribbon-tool-content"> + <div class="cw-ribbon-tool-content-nav"> + <ul> + <li :class="{ active: showContents }"> + <a href="#" ref="focusPoint" @click="displayTool('contents')"> + <translate>Inhaltsverzeichnis</translate> + </a> + </li> + <li + v-if="!consumeMode && showEditMode && canEdit" + :class="{ active: showBlockAdder }" + > + <a href="#" @click="displayTool('blockadder')"> + <translate>Elemente hinzufügen</translate> + </a> + </li> + <li + v-if="!consumeMode && displaySettings" + :class="{ active: showAdmin }" + > + <a href="#" @click="displayTool('admin')"> + <translate>Einstellungen</translate> + </a> + </li> + </ul> + <a href="#" :title="textClose" class="cw-tools-hide-button" @click="$emit('deactivate')"></a> + </div> + <div class="cw-ribbon-tool" ref="ribbonContent" @scroll="handleScroll"> + <courseware-tools-contents v-if="showContents" /> + <courseware-tools-blockadder v-if="showBlockAdder" @scrollTop="scrollTop('blockadder')"/> + <courseware-tools-admin v-if="showAdmin" /> + </div> </div> </div> - </div> + </focus-trap> </template> <script> import CoursewareToolsAdmin from './CoursewareToolsAdmin.vue'; import CoursewareToolsBlockadder from './CoursewareToolsBlockadder.vue'; import CoursewareToolsContents from './CoursewareToolsContents.vue'; +import { FocusTrap } from 'focus-trap-vue'; import { mapGetters } from 'vuex'; export default { @@ -50,6 +53,7 @@ export default { CoursewareToolsAdmin, CoursewareToolsBlockadder, CoursewareToolsContents, + FocusTrap, }, props: { toolsActive: Boolean, @@ -65,7 +69,8 @@ export default { contents: 0, admin: 0, blockadder: 0 - } + }, + trap: false, }; }, computed: { -- GitLab From 96ca11ce45d4d58e6161673eac6e9149d3a164ae Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 10:53:24 +0000 Subject: [PATCH 10/34] add a tags --- .../courseware/CoursewareToolsBlockadder.vue | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareToolsBlockadder.vue b/resources/vue/components/courseware/CoursewareToolsBlockadder.vue index 7baa986c8ed..b00c3f57139 100755 --- a/resources/vue/components/courseware/CoursewareToolsBlockadder.vue +++ b/resources/vue/components/courseware/CoursewareToolsBlockadder.vue @@ -4,16 +4,18 @@ <li :class="{ 'active': showBlockadder }" class="cw-tools-element-adder-tab" - @click="displayBlockAdder" > - <translate>Blöcke</translate> + <a href="#" @click="displayBlockAdder"> + <translate>Blöcke</translate> + </a> </li> <li :class="{ 'active': showContaineradder }" class="cw-tools-element-adder-tab" - @click="displayContainerAdder" > - <translate>Abschnitte</translate> + <a href="#" @click="displayContainerAdder"> + <translate>Abschnitte</translate> + </a> </li> </ul> -- GitLab From 316c5390fd9f374ffe82537004c981866afa02e3 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 10:54:36 +0000 Subject: [PATCH 11/34] add a tag --- .../courseware/CoursewareBlockadderItem.vue | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareBlockadderItem.vue b/resources/vue/components/courseware/CoursewareBlockadderItem.vue index 78b3bc6db11..860847cd7be 100755 --- a/resources/vue/components/courseware/CoursewareBlockadderItem.vue +++ b/resources/vue/components/courseware/CoursewareBlockadderItem.vue @@ -1,12 +1,14 @@ <template> - <div class="cw-blockadder-item" :class="['cw-blockadder-item-' + type]" @click="addBlock"> - <header class="cw-blockadder-item-title"> - {{ title }} - </header> - <p class="cw-blockadder-item-description"> - {{ description }} - </p> - </div> + <a href="#" @click="addBlock"> + <div class="cw-blockadder-item" :class="['cw-blockadder-item-' + type]"> + <header class="cw-blockadder-item-title"> + {{ title }} + </header> + <p class="cw-blockadder-item-description"> + {{ description }} + </p> + </div> + </a> </template> <script> -- GitLab From 4b987bba403ee7f83ca06729f7b0db7ff821cbed Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 10:55:38 +0000 Subject: [PATCH 12/34] add a tag --- .../vue/components/courseware/CoursewareCollapsibleBox.vue | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareCollapsibleBox.vue b/resources/vue/components/courseware/CoursewareCollapsibleBox.vue index 3bd0f40f6a8..74796a3d516 100755 --- a/resources/vue/components/courseware/CoursewareCollapsibleBox.vue +++ b/resources/vue/components/courseware/CoursewareCollapsibleBox.vue @@ -1,7 +1,9 @@ <template> <div class="cw-collapsible" :class="{ 'cw-collapsible-open': isOpen }"> - <header :class="{ 'cw-collapsible-open': isOpen }" class="cw-collapsible-title" @click="isOpen = !isOpen"> - <studip-icon v-if="icon" :shape="icon" /> {{ title }} + <header :class="{ 'cw-collapsible-open': isOpen }" class="cw-collapsible-title"> + <a href="#" :aria-expanded="isOpen" @click="isOpen = !isOpen"> + <studip-icon v-if="icon" :shape="icon" /> {{ title }} + </a> </header> <div class="cw-collapsible-content" :class="{ 'cw-collapsible-content-open': isOpen }"> <slot></slot> -- GitLab From 25a4a668c14b0f4d334cbe37843ad4aa06edea3c Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 12:12:30 +0000 Subject: [PATCH 13/34] add key listener --- resources/vue/components/courseware/CoursewareRibbonToolbar.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue index afd30d4e5d6..6ae8f685cb6 100755 --- a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue +++ b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue @@ -3,6 +3,7 @@ <div class="cw-ribbon-tools" :class="{ unfold: toolsActive, 'cw-ribbon-tools-consume': consumeMode }" + @keydown.esc="$emit('deactivate')" > <div class="cw-ribbon-tool-content"> <div class="cw-ribbon-tool-content-nav"> -- GitLab From 7f4844691e7c4463d75c5bd695ff82ff8e3ff27a Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 12:14:49 +0000 Subject: [PATCH 14/34] remove button tag --- .../vue/components/courseware/CoursewareWellcomeScreen.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareWellcomeScreen.vue b/resources/vue/components/courseware/CoursewareWellcomeScreen.vue index 87f6672797c..52a629eaf46 100755 --- a/resources/vue/components/courseware/CoursewareWellcomeScreen.vue +++ b/resources/vue/components/courseware/CoursewareWellcomeScreen.vue @@ -5,8 +5,8 @@ <translate>Willkommen bei Courseware</translate> </header> <div class="cw-wellcome-screen-actions"> - <a href="https://hilfe.studip.de/help/5.0/de/Basis.Courseware" target="_blank"> - <button class="button"><translate>Mehr über Courseware erfahren</translate></button> + <a href="https://hilfe.studip.de/help/5.0/de/Basis.Courseware" target="_blank" class="button"> + <translate>Mehr über Courseware erfahren</translate> </a> <button class="button" :title="$gettext('Fügt einen Standard-Abschnitt mit einem Text-Block hinzu')" @click="addDefault"><translate>Ersten Inhalt erstellen</translate></button> <button class="button" @click="addContainer"><translate>Einen Abschnitt auswählen</translate></button> @@ -78,4 +78,4 @@ export default { } } -</script> \ No newline at end of file +</script> -- GitLab From 99b3ed4c61a8cee492b8d41c4d870ad5841cefa6 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 18 Jan 2022 13:35:02 +0000 Subject: [PATCH 15/34] fix default url to create href="#" --- resources/vue/components/StudipActionMenu.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/vue/components/StudipActionMenu.vue b/resources/vue/components/StudipActionMenu.vue index 95ec6a5f65a..6b8f229cd67 100644 --- a/resources/vue/components/StudipActionMenu.vue +++ b/resources/vue/components/StudipActionMenu.vue @@ -80,7 +80,7 @@ export default { } return { label: item.label, - url: item.url || false, + url: item.url || '#', emit: item.emit || false, emitArguments: item.emitArguments || [], icon: item.icon ? { -- GitLab From 7a93aa6867011a1b18619369ebef705d43218bfd Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Wed, 19 Jan 2022 14:59:37 +0100 Subject: [PATCH 16/34] set focus trap --- .../vue/components/courseware/CoursewareRibbonToolbar.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue index 6ae8f685cb6..2adceb021b9 100755 --- a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue +++ b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue @@ -71,7 +71,7 @@ export default { admin: 0, blockadder: 0 }, - trap: false, + trap: false }; }, computed: { @@ -199,7 +199,7 @@ export default { toolsActive(newValue) { if (newValue) { setTimeout(() => { - this.$refs.focusPoint.focus(); + this.trap = true; }, 300); } }, -- GitLab From abce9f845a31c24e12085a74a941946dc7df8aeb Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Wed, 19 Jan 2022 15:08:41 +0100 Subject: [PATCH 17/34] set consum mode switch focus --- resources/vue/components/courseware/CoursewareRibbon.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resources/vue/components/courseware/CoursewareRibbon.vue b/resources/vue/components/courseware/CoursewareRibbon.vue index ebef9948286..6485f19d01c 100755 --- a/resources/vue/components/courseware/CoursewareRibbon.vue +++ b/resources/vue/components/courseware/CoursewareRibbon.vue @@ -23,7 +23,8 @@ </a> <a href="#" - class="cw-ribbon-button" + ref="consumeModeSwitch" + class="cw-ribbon-button" :class="[consumeMode ? 'cw-ribbon-button-zoom-out' : 'cw-ribbon-button-zoom']" :title="consumeMode ? textRibbon.fullscreen_off : textRibbon.fullscreen_on" @click="toggleConsumeMode" @@ -119,6 +120,9 @@ export default { } }, 800); } + }, + consumeMode(newState) { + this.$refs.consumeModeSwitch.focus(); } } }; -- GitLab From 89a96fc07b6a38ba0c85457d3ddfcffe81cae610 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Wed, 19 Jan 2022 15:09:14 +0100 Subject: [PATCH 18/34] change a tag position --- .../components/courseware/CoursewareCollapsibleBox.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareCollapsibleBox.vue b/resources/vue/components/courseware/CoursewareCollapsibleBox.vue index 74796a3d516..fd1d19ce925 100755 --- a/resources/vue/components/courseware/CoursewareCollapsibleBox.vue +++ b/resources/vue/components/courseware/CoursewareCollapsibleBox.vue @@ -1,10 +1,10 @@ <template> <div class="cw-collapsible" :class="{ 'cw-collapsible-open': isOpen }"> - <header :class="{ 'cw-collapsible-open': isOpen }" class="cw-collapsible-title"> - <a href="#" :aria-expanded="isOpen" @click="isOpen = !isOpen"> + <a href="#" :aria-expanded="isOpen" @click="isOpen = !isOpen"> + <header :class="{ 'cw-collapsible-open': isOpen }" class="cw-collapsible-title"> <studip-icon v-if="icon" :shape="icon" /> {{ title }} - </a> - </header> + </header> + </a> <div class="cw-collapsible-content" :class="{ 'cw-collapsible-content-open': isOpen }"> <slot></slot> </div> -- GitLab From 5967552d809ccc488960a413d20d88c12b5fc4fe Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Wed, 19 Jan 2022 15:42:58 +0100 Subject: [PATCH 19/34] add consum mode focus trap --- .../assets/stylesheets/scss/courseware.scss | 2 - .../CoursewareStructuralElement.vue | 897 +++++++++--------- .../courseware/CoursewareViewWidget.vue | 4 +- 3 files changed, 457 insertions(+), 446 deletions(-) diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss index 560465d1167..1b52d239a20 100755 --- a/resources/assets/stylesheets/scss/courseware.scss +++ b/resources/assets/stylesheets/scss/courseware.scss @@ -443,8 +443,6 @@ $consum_ribbon_width: calc(100% - 58px); background-size: 24px; background-position: center right; background-color: #fff; - outline: none; - cursor: pointer; } ul { diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue index 3eafe2eee9a..4aae39e8508 100755 --- a/resources/vue/components/courseware/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue @@ -1,179 +1,425 @@ <template> - <div> - <div - :class="{ 'cw-structural-element-consumemode': consumeMode }" - class="cw-structural-element" - v-if="validContext" - > - <div class="cw-structural-element-content" v-if="structuralElement"> - <courseware-ribbon :canEdit="canEdit && canAddElements"> - <template #buttons> - <router-link v-if="prevElement" :to="'/structural_element/' + prevElement.id"> - <div class="cw-ribbon-button cw-ribbon-button-prev" :title="textRibbon.perv" /> - </router-link> - <div v-else class="cw-ribbon-button cw-ribbon-button-prev-disabled" :title="$gettext('keine vorherige Seite')"/> - <router-link v-if="nextElement" :to="'/structural_element/' + nextElement.id"> - <div class="cw-ribbon-button cw-ribbon-button-next" :title="textRibbon.next" /> - </router-link> - <div v-else class="cw-ribbon-button cw-ribbon-button-next-disabled" :title="$gettext('keine nächste Seite')"/> - </template> - <template #breadcrumbList> - <li - v-for="ancestor in ancestors" - :key="ancestor.id" - :title="ancestor.attributes.title" - class="cw-ribbon-breadcrumb-item" - > - <span> - <router-link :to="'/structural_element/' + ancestor.id"> - {{ ancestor.attributes.title || "–" }} - </router-link> - </span> - </li> - <li - class="cw-ribbon-breadcrumb-item cw-ribbon-breadcrumb-item-current" - :title="structuralElement.attributes.title" - > - <span>{{ structuralElement.attributes.title || "–" }}</span> - </li> - </template> - <template #breadcrumbFallback> - <li - class="cw-ribbon-breadcrumb-item cw-ribbon-breadcrumb-item-current" - :title="structuralElement.attributes.title" - > - <span>{{ structuralElement.attributes.title }}</span> - </li> - </template> - <template #menu> - <studip-action-menu - v-if="!consumeMode" - :items="menuItems" - class="cw-ribbon-action-menu" - @editCurrentElement="menuAction('editCurrentElement')" - @addElement="menuAction('addElement')" - @deleteCurrentElement="menuAction('deleteCurrentElement')" - @showInfo="menuAction('showInfo')" - @showExportOptions="menuAction('showExportOptions')" - @oerCurrentElement="menuAction('oerCurrentElement')" - @setBookmark="menuAction('setBookmark')" - @sortContainers="menuAction('sortContainers')" - @pdfExport="menuAction('pdfExport')" + <focus-trap v-model="consumModeTrap"> + <div> + <div + :class="{ 'cw-structural-element-consumemode': consumeMode }" + class="cw-structural-element" + v-if="validContext" + > + <div class="cw-structural-element-content" v-if="structuralElement"> + <courseware-ribbon :canEdit="canEdit && canAddElements"> + <template #buttons> + <router-link v-if="prevElement" :to="'/structural_element/' + prevElement.id"> + <div class="cw-ribbon-button cw-ribbon-button-prev" :title="textRibbon.perv" /> + </router-link> + <div v-else class="cw-ribbon-button cw-ribbon-button-prev-disabled" :title="$gettext('keine vorherige Seite')"/> + <router-link v-if="nextElement" :to="'/structural_element/' + nextElement.id"> + <div class="cw-ribbon-button cw-ribbon-button-next" :title="textRibbon.next" /> + </router-link> + <div v-else class="cw-ribbon-button cw-ribbon-button-next-disabled" :title="$gettext('keine nächste Seite')"/> + </template> + <template #breadcrumbList> + <li + v-for="ancestor in ancestors" + :key="ancestor.id" + :title="ancestor.attributes.title" + class="cw-ribbon-breadcrumb-item" + > + <span> + <router-link :to="'/structural_element/' + ancestor.id"> + {{ ancestor.attributes.title || "–" }} + </router-link> + </span> + </li> + <li + class="cw-ribbon-breadcrumb-item cw-ribbon-breadcrumb-item-current" + :title="structuralElement.attributes.title" + > + <span>{{ structuralElement.attributes.title || "–" }}</span> + </li> + </template> + <template #breadcrumbFallback> + <li + class="cw-ribbon-breadcrumb-item cw-ribbon-breadcrumb-item-current" + :title="structuralElement.attributes.title" + > + <span>{{ structuralElement.attributes.title }}</span> + </li> + </template> + <template #menu> + <studip-action-menu + v-if="!consumeMode" + :items="menuItems" + class="cw-ribbon-action-menu" + @editCurrentElement="menuAction('editCurrentElement')" + @addElement="menuAction('addElement')" + @deleteCurrentElement="menuAction('deleteCurrentElement')" + @showInfo="menuAction('showInfo')" + @showExportOptions="menuAction('showExportOptions')" + @oerCurrentElement="menuAction('oerCurrentElement')" + @setBookmark="menuAction('setBookmark')" + @sortContainers="menuAction('sortContainers')" + @pdfExport="menuAction('pdfExport')" + /> + </template> + </courseware-ribbon> + + <div + v-if="canVisit && !sortMode" + class="cw-container-wrapper" + :class="{ + 'cw-container-wrapper-consume': consumeMode, + 'cw-container-wrapper-discuss': discussView, + }" + > + <div v-if="structuralElementLoaded" class="cw-companion-box-wrapper"> + <courseware-empty-element-box + v-if="showEmptyElementBox" + :canEdit="canEdit" + :noContainers="noContainers" + /> + <courseware-wellcome-screen v-if="noContainers && isRoot && canEdit" /> + </div> + <courseware-structural-element-discussion + v-if="!noContainers && discussView" + :structuralElement="structuralElement" + :canEdit="canEdit" /> - </template> - </courseware-ribbon> - - <div - v-if="canVisit && !sortMode" - class="cw-container-wrapper" - :class="{ - 'cw-container-wrapper-consume': consumeMode, - 'cw-container-wrapper-discuss': discussView, - }" - > - <div v-if="structuralElementLoaded" class="cw-companion-box-wrapper"> - <courseware-empty-element-box - v-if="showEmptyElementBox" + <component + v-for="container in containers" + :key="container.id" + :is="containerComponent(container)" + :container="container" :canEdit="canEdit" - :noContainers="noContainers" + :canAddElements="canAddElements" + :isTeacher="userIsTeacher" + class="cw-container-item" /> - <courseware-wellcome-screen v-if="noContainers && isRoot && canEdit" /> </div> - <courseware-structural-element-discussion - v-if="!noContainers && discussView" - :structuralElement="structuralElement" - :canEdit="canEdit" - /> - <component - v-for="container in containers" - :key="container.id" - :is="containerComponent(container)" - :container="container" - :canEdit="canEdit" - :canAddElements="canAddElements" - :isTeacher="userIsTeacher" - class="cw-container-item" - /> - </div> - <div v-if="canVisit && canEdit && sortMode" class="cw-container-wrapper-sort-mode"> - <draggable - class="cw-structural-element-list-sort-mode" - tag="ul" - v-model="containerList" - v-bind="dragOptions" - handle=".cw-sortable-handle" - @start="isDragging = true" - @end="isDragging = false" - > - <transition-group type="transition" name="flip-containers"> - <li - v-for="container in containerList" - :key="container.id" - class="cw-container-item-sortable" - > - <span class="cw-sortable-handle"></span> - <span>{{ container.attributes.title }} ({{ container.attributes.width }})</span> - </li> - </transition-group> - </draggable> - <div class="cw-container-sort-buttons"> - <button class="button accept" @click="storeSort"> - <translate>Sortierung speichern</translate> - </button> - <button class="button cancel" @click="resetSort"> - <translate>Sortieren abbrechen</translate> - </button> + <div v-if="canVisit && canEdit && sortMode" class="cw-container-wrapper-sort-mode"> + <draggable + class="cw-structural-element-list-sort-mode" + tag="ul" + v-model="containerList" + v-bind="dragOptions" + handle=".cw-sortable-handle" + @start="isDragging = true" + @end="isDragging = false" + > + <transition-group type="transition" name="flip-containers"> + <li + v-for="container in containerList" + :key="container.id" + class="cw-container-item-sortable" + > + <span class="cw-sortable-handle"></span> + <span>{{ container.attributes.title }} ({{ container.attributes.width }})</span> + </li> + </transition-group> + </draggable> + <div class="cw-container-sort-buttons"> + <button class="button accept" @click="storeSort"> + <translate>Sortierung speichern</translate> + </button> + <button class="button cancel" @click="resetSort"> + <translate>Sortieren abbrechen</translate> + </button> + </div> </div> - </div> - <div - v-if="!canVisit" - class="cw-container-wrapper" - :class="{ 'cw-container-wrapper-consume': consumeMode }" - > - <div v-if="structuralElementLoaded" class="cw-companion-box-wrapper"> - <courseware-companion-box - mood="sad" - :msgCompanion="$gettext('Diese Seite steht Ihnen leider nicht zur Verfügung.')" - /> + <div + v-if="!canVisit" + class="cw-container-wrapper" + :class="{ 'cw-container-wrapper-consume': consumeMode }" + > + <div v-if="structuralElementLoaded" class="cw-companion-box-wrapper"> + <courseware-companion-box + mood="sad" + :msgCompanion="$gettext('Diese Seite steht Ihnen leider nicht zur Verfügung.')" + /> + </div> </div> </div> - </div> - <courseware-companion-overlay /> - - <studip-dialog - v-if="showEditDialog" - :title="textEdit.title" - :confirmText="textEdit.confirm" - :confirmClass="'accept'" - :closeText="textEdit.close" - :closeClass="'cancel'" - height="500" - width="500" - class="studip-dialog-with-tab" - @close="closeEditDialog" - @confirm="storeCurrentElement" - > - <template v-slot:dialogContent> - <courseware-tabs class="cw-tab-in-dialog"> - <courseware-tab :name="textEdit.basic" :selected="true"> - <form class="default" @submit.prevent=""> + <courseware-companion-overlay /> + + <studip-dialog + v-if="showEditDialog" + :title="textEdit.title" + :confirmText="textEdit.confirm" + :confirmClass="'accept'" + :closeText="textEdit.close" + :closeClass="'cancel'" + height="500" + width="500" + class="studip-dialog-with-tab" + @close="closeEditDialog" + @confirm="storeCurrentElement" + > + <template v-slot:dialogContent> + <courseware-tabs class="cw-tab-in-dialog"> + <courseware-tab :name="textEdit.basic" :selected="true"> + <form class="default" @submit.prevent=""> + <label> + <translate>Titel</translate> + <input type="text" v-model="currentElement.attributes.title" /> + </label> + <label> + <translate>Beschreibung</translate> + <textarea + v-model="currentElement.attributes.payload.description" + class="cw-structural-element-description" + /> + </label> + </form> + </courseware-tab> + <courseware-tab :name="textEdit.meta"> + <form class="default" @submit.prevent=""> + <label> + <translate>Farbe</translate> + <v-select + v-model="currentElement.attributes.payload.color" + :options="colors" + :reduce="(color) => color.class" + label="class" + class="cw-vs-select" + > + <template #open-indicator="selectAttributes"> + <span v-bind="selectAttributes" + ><studip-icon shape="arr_1down" size="10" + /></span> + </template> + <template #no-options="{ search, searching, loading }"> + <translate>Es steht keine Auswahl zur Verfügung</translate>. + </template> + <template #selected-option="{ name, hex }"> + <span class="vs__option-color" :style="{ 'background-color': hex }"></span + ><span>{{ name }}</span> + </template> + <template #option="{ name, hex }"> + <span class="vs__option-color" :style="{ 'background-color': hex }"></span + ><span>{{ name }}</span> + </template> + </v-select> + </label> + <label> + <translate>Zweck</translate> + <select v-model="currentElement.attributes.purpose"> + <option value="content"><translate>Inhalt</translate></option> + <option value="template"><translate>Vorlage</translate></option> + <option value="oer"><translate>OER-Material</translate></option> + <option value="portfolio"><translate>ePortfolio</translate></option> + <option value="draft"><translate>Entwurf</translate></option> + <option value="other"><translate>Sonstiges</translate></option> + </select> + </label> + <label> + <translate>Lizenztyp</translate> + <select v-model="currentElement.attributes.payload.license_type"> + <option v-for="license in licenses" :key="license.id" :value="license.id"> + {{ license.name }} + </option> + </select> + </label> + <label> + <translate>Geschätzter zeitlicher Aufwand</translate> + <input type="text" v-model="currentElement.attributes.payload.required_time" /> + </label> + <label> + <translate>Niveau</translate><br /> + <translate>von</translate> + <select v-model="currentElement.attributes.payload.difficulty_start"> + <option + v-for="difficulty_start in 12" + :key="difficulty_start" + :value="difficulty_start" + > + {{ difficulty_start }} + </option> + </select> + <translate>bis</translate> + <select v-model="currentElement.attributes.payload.difficulty_end"> + <option + v-for="difficulty_end in 12" + :key="difficulty_end" + :value="difficulty_end" + > + {{ difficulty_end }} + </option> + </select> + </label> + </form> + </courseware-tab> + <courseware-tab :name="textEdit.image"> + <form class="default" @submit.prevent=""> + <img + v-if="image" + :src="image" + class="cw-structural-element-image-preview" + :alt="$gettext('Vorschaubild')" + /> + <label v-if="image"> + <button class="button" @click="deleteImage" v-translate>Bild löschen</button> + </label> + <div v-if="uploadFileError" class="messagebox messagebox_error"> + {{ uploadFileError }} + </div> + <label v-if="!image"> + <translate>Bild hochladen</translate> + <input ref="upload_image" type="file" accept="image/*" @change="checkUploadFile" /> + </label> + </form> + </courseware-tab> + <courseware-tab :name="textEdit.approval"> + <courseware-structural-element-permissions + v-if="inCourse" + :element="currentElement" + @updateReadApproval="updateReadApproval" + @updateWriteApproval="updateWriteApproval" + /> + <!-- <h1> + <translate>Lehrende in Stud.IP</translate> + </h1> + <label> + <input + type="checkbox" + class="default" + value="copy_approval" + v-model="currentElement.attributes['copy-approval']" + /> + <translate>Seite zum kopieren für Lehrende freigeben</translate> + </label> --> + </courseware-tab> + <courseware-tab v-if="inCourse" :name="textEdit.visible"> + <form class="default" @submit.prevent=""> + <label> + <translate>Sichtbar ab</translate> + <input type="date" v-model="currentElement.attributes['release-date']" /> + </label> + <label> + <translate>Unsichtbar ab</translate> + <input type="date" v-model="currentElement.attributes['withdraw-date']" /> + </label> + </form> + </courseware-tab> + </courseware-tabs> + </template> + </studip-dialog> + + <studip-dialog + v-if="showAddDialog" + :title="$gettext('Seite hinzufügen')" + :confirmText="'Erstellen'" + :confirmClass="'accept'" + :closeText="$gettext('Schließen')" + :closeClass="'cancel'" + class="cw-structural-element-dialog" + @close="closeAddDialog" + @confirm="createElement" + > + <template v-slot:dialogContent> + <form class="default" @submit.prevent=""> + <label> + <translate>Position der neuen Seite</translate> + <select v-model="newChapterParent"> + <option v-if="!isRoot" value="sibling"> + <translate>Neben der aktuellen Seite</translate> + </option> + <option value="descendant"><translate>Unterhalb der aktuellen Seite</translate></option> + </select> + </label> + <label> + <translate>Name der neuen Seite</translate><br /> + <input v-model="newChapterName" type="text" /> + </label> + </form> + </template> + </studip-dialog> + + <studip-dialog + v-if="showInfoDialog" + :title="textInfo.title" + :closeText="textInfo.close" + :closeClass="'cancel'" + @close="showElementInfoDialog(false)" + > + <template v-slot:dialogContent> + <table class="cw-structural-element-info"> + <tr> + <td><translate>Titel</translate>:</td> + <td>{{ structuralElement.attributes.title }}</td> + </tr> + <tr> + <td><translate>Beschreibung</translate>:</td> + <td>{{ structuralElement.attributes.payload.description }}</td> + </tr> + <tr> + <td><translate>Seite wurde erstellt von</translate>:</td> + <td>{{ owner }}</td> + </tr> + <tr> + <td><translate>Seite wurde erstellt am</translate>:</td> + <td><iso-date :date="structuralElement.attributes.mkdate" /></td> + </tr> + <tr> + <td><translate>Zuletzt bearbeitet von</translate>:</td> + <td>{{ editor }}</td> + </tr> + <tr> + <td><translate>Zuletzt bearbeitet am</translate>:</td> + <td><iso-date :date="structuralElement.attributes.chdate" /></td> + </tr> + </table> + </template> + </studip-dialog> + + <studip-dialog + v-if="showExportDialog" + :title="textExport.title" + :confirmText="textExport.confirm" + :confirmClass="'accept'" + :closeText="textExport.close" + :closeClass="'cancel'" + height="350" + @close="showElementExportDialog(false)" + @confirm="exportCurrentElement" + > + <template v-slot:dialogContent> + <div v-show="!exportRunning"> + <translate> Hiermit exportieren Sie die Seite "%{ currentElement.attributes.title }" als ZIP-Datei.</translate> + <div class="cw-element-export"> <label> - <translate>Titel</translate> - <input type="text" v-model="currentElement.attributes.title" /> + <input type="checkbox" v-model="exportChildren" /> + <translate>Unterseiten exportieren</translate> </label> + </div> + </div> + + <studip-dialog + v-if="showOerDialog" + height="600" + width="600" + :title="textOer.title" + :confirmText="textOer.confirm" + :confirmClass="'accept'" + :closeText="textOer.close" + :closeClass="'cancel'" + @close="showElementOerDialog(false)" + @confirm="publishCurrentElement" + > + <template v-slot:dialogContent> + <form class="default" @submit.prevent=""> + <fieldset> + <legend><translate>Grunddaten</translate></legend> <label> - <translate>Beschreibung</translate> - <textarea - v-model="currentElement.attributes.payload.description" - class="cw-structural-element-description" + <p><translate>Vorschaubild</translate>:</p> + <img + v-if="currentElement.relationships.image.data" + :src="currentElement.relationships.image.meta['download-url']" + width="400" /> </label> - </form> - </courseware-tab> - <courseware-tab :name="textEdit.meta"> - <form class="default" @submit.prevent=""> <label> +<<<<<<< HEAD <translate>Farbe</translate> <studip-select v-model="currentElement.attributes.payload.color" @@ -198,297 +444,54 @@ ><span>{{ name }}</span> </template> </studip-select> +======= + <p><translate>Beschreibung</translate>:</p> + <p>{{ currentElement.attributes.payload.description }}</p> +>>>>>>> 6b1cf2bd3 (add consum mode focus trap) </label> <label> - <translate>Zweck</translate> - <select v-model="currentElement.attributes.purpose"> - <option value="content"><translate>Inhalt</translate></option> - <option value="template"><translate>Aufgabenvorlage</translate></option> - <option value="oer"><translate>OER-Material</translate></option> - <option value="portfolio"><translate>ePortfolio</translate></option> - <option value="draft"><translate>Entwurf</translate></option> - <option value="other"><translate>Sonstiges</translate></option> - </select> + <translate>Niveau</translate>: + <p> + {{ currentElement.attributes.payload.difficulty_start }} - + {{ currentElement.attributes.payload.difficulty_end }} + </p> </label> <label> - <translate>Lizenztyp</translate> - <select v-model="currentElement.attributes.payload.license_type"> - <option v-for="license in licenses" :key="license.id" :value="license.id"> - {{ license.name }} - </option> - </select> - </label> - <label> - <translate>Geschätzter zeitlicher Aufwand</translate> - <input type="text" v-model="currentElement.attributes.payload.required_time" /> - </label> - <label> - <translate>Niveau</translate><br /> - <translate>von</translate> - <select v-model="currentElement.attributes.payload.difficulty_start"> - <option - v-for="difficulty_start in 12" - :key="difficulty_start" - :value="difficulty_start" - > - {{ difficulty_start }} - </option> - </select> - <translate>bis</translate> - <select v-model="currentElement.attributes.payload.difficulty_end"> - <option - v-for="difficulty_end in 12" - :key="difficulty_end" - :value="difficulty_end" - > - {{ difficulty_end }} - </option> - </select> - </label> - </form> - </courseware-tab> - <courseware-tab :name="textEdit.image"> - <form class="default" @submit.prevent=""> - <img - v-if="image" - :src="image" - class="cw-structural-element-image-preview" - :alt="$gettext('Vorschaubild')" - /> - <label v-if="image"> - <button class="button" @click="deleteImage" v-translate>Bild löschen</button> - </label> - <div v-if="uploadFileError" class="messagebox messagebox_error"> - {{ uploadFileError }} - </div> - <label v-if="!image"> - <translate>Bild hochladen</translate> - <input ref="upload_image" type="file" accept="image/*" @change="checkUploadFile" /> + <translate>Lizenztyp</translate>: + <p>{{ currentLicenseName }}</p> </label> - </form> - </courseware-tab> - <courseware-tab :name="textEdit.approval"> - <courseware-structural-element-permissions - v-if="inCourse" - :element="currentElement" - @updateReadApproval="updateReadApproval" - @updateWriteApproval="updateWriteApproval" - /> - <!-- <h1> - <translate>Lehrende in Stud.IP</translate> - </h1> - <label> - <input - type="checkbox" - class="default" - value="copy_approval" - v-model="currentElement.attributes['copy-approval']" - /> - <translate>Seite zum kopieren für Lehrende freigeben</translate> - </label> --> - </courseware-tab> - <courseware-tab v-if="inCourse" :name="textEdit.visible"> - <form class="default" @submit.prevent=""> <label> - <translate>Sichtbar ab</translate> - <input type="date" v-model="currentElement.attributes['release-date']" /> + <translate>Sie können diese Daten unter "Seite bearbeiten" verändern.</translate> </label> + </fieldset> + <fieldset> + <legend><translate>Einstellungen</translate></legend> <label> - <translate>Unsichtbar ab</translate> - <input type="date" v-model="currentElement.attributes['withdraw-date']" /> + <translate>Unterseiten veröffentlichen</translate> + <input type="checkbox" v-model="oerChildren" /> </label> - </form> - </courseware-tab> - </courseware-tabs> - </template> - </studip-dialog> - - <studip-dialog - v-if="showAddDialog" - :title="$gettext('Seite hinzufügen')" - :confirmText="'Erstellen'" - :confirmClass="'accept'" - :closeText="$gettext('Schließen')" - :closeClass="'cancel'" - class="cw-structural-element-dialog" - @close="closeAddDialog" - @confirm="createElement" - > - <template v-slot:dialogContent> - <form class="default" @submit.prevent=""> - <label> - <translate>Position der neuen Seite</translate> - <select v-model="newChapterParent"> - <option v-if="!isRoot" value="sibling"> - <translate>Neben der aktuellen Seite</translate> - </option> - <option value="descendant"><translate>Unterhalb der aktuellen Seite</translate></option> - </select> - </label> - <label> - <translate>Name der neuen Seite</translate><br /> - <input v-model="newChapterName" type="text" required /> - <div class="invalid_message" :style="{ display: errorEmptyChapterName ? 'block' : 'none' }"> - <translate>Der Name der neuen Seite darf nicht leer sein.</translate> - </div> - </label> - </form> - </template> - </studip-dialog> - - <studip-dialog - v-if="showInfoDialog" - :title="textInfo.title" - :closeText="textInfo.close" - :closeClass="'cancel'" - @close="showElementInfoDialog(false)" - > - <template v-slot:dialogContent> - <table class="cw-structural-element-info"> - <tr> - <td><translate>Titel</translate>:</td> - <td>{{ structuralElement.attributes.title }}</td> - </tr> - <tr> - <td><translate>Beschreibung</translate>:</td> - <td>{{ structuralElement.attributes.payload.description }}</td> - </tr> - <tr> - <td><translate>Seite wurde erstellt von</translate>:</td> - <td>{{ owner }}</td> - </tr> - <tr> - <td><translate>Seite wurde erstellt am</translate>:</td> - <td><iso-date :date="structuralElement.attributes.mkdate" /></td> - </tr> - <tr> - <td><translate>Zuletzt bearbeitet von</translate>:</td> - <td>{{ editor }}</td> - </tr> - <tr> - <td><translate>Zuletzt bearbeitet am</translate>:</td> - <td><iso-date :date="structuralElement.attributes.chdate" /></td> - </tr> - </table> - </template> - </studip-dialog> - - <studip-dialog - v-if="showExportDialog" - :title="textExport.title" - :confirmText="textExport.confirm" - :confirmClass="'accept'" - :closeText="textExport.close" - :closeClass="'cancel'" - height="350" - @close="showElementExportDialog(false)" - @confirm="exportCurrentElement" - > - <template v-slot:dialogContent> - <div v-show="!exportRunning"> - <translate> Hiermit exportieren Sie die Seite "%{ currentElement.attributes.title }" als ZIP-Datei.</translate> - <div class="cw-element-export"> - <label> - <input type="checkbox" v-model="exportChildren" /> - <translate>Unterseiten exportieren</translate> - </label> - </div> - </div> - - <courseware-companion-box - v-show="exportRunning" - :msgCompanion="$gettext('Export läuft, bitte haben sie einen Moment Geduld...')" - mood="pointing" - /> - <div v-show="exportRunning" class="cw-import-zip"> - <header>{{ exportState }}:</header> - <div class="progress-bar-wrapper"> - <div - class="progress-bar" - role="progressbar" - :style="{ width: exportProgress + '%' }" - :aria-valuenow="exportProgress" - aria-valuemin="0" - aria-valuemax="100" - > - {{ exportProgress }}% - </div> - </div> - </div> - </template> - </studip-dialog> - - <studip-dialog - v-if="showOerDialog" - height="600" - width="600" - :title="textOer.title" - :confirmText="textOer.confirm" - :confirmClass="'accept'" - :closeText="textOer.close" - :closeClass="'cancel'" - @close="showElementOerDialog(false)" - @confirm="publishCurrentElement" - > - <template v-slot:dialogContent> - <form class="default" @submit.prevent=""> - <fieldset> - <legend><translate>Grunddaten</translate></legend> - <label> - <p><translate>Vorschaubild</translate>:</p> - <img - v-if="currentElement.relationships.image.data" - :src="currentElement.relationships.image.meta['download-url']" - width="400" - /> - </label> - <label> - <p><translate>Beschreibung</translate>:</p> - <p>{{ currentElement.attributes.payload.description }}</p> - </label> - <label> - <translate>Niveau</translate>: - <p> - {{ currentElement.attributes.payload.difficulty_start }} - - {{ currentElement.attributes.payload.difficulty_end }} - </p> - </label> - <label> - <translate>Lizenztyp</translate>: - <p>{{ currentLicenseName }}</p> - </label> - <label> - <translate>Sie können diese Daten unter "Seite bearbeiten" verändern.</translate> - </label> - </fieldset> - <fieldset> - <legend><translate>Einstellungen</translate></legend> - <label> - <translate>Unterseiten veröffentlichen</translate> - <input type="checkbox" v-model="oerChildren" /> - </label> - </fieldset> - </form> - </template> - </studip-dialog> - - <studip-dialog - v-if="showDeleteDialog" - :title="textDelete.title" - :question="textDelete.alert" - height="180" - @confirm="deleteCurrentElement" - @close="closeDeleteDialog" - ></studip-dialog> - </div> - <div v-else> - <courseware-companion-box - v-if="currentElement !== ''" - :msgCompanion="textCompanionWrongContext" - mood="sad" - /> + </fieldset> + </form> + </template> + </studip-dialog> + <studip-dialog + v-if="showDeleteDialog" + :title="textDelete.title" + :question="textDelete.alert" + height="180" + @confirm="deleteCurrentElement" + @close="closeDeleteDialog" + ></studip-dialog> + </div> + <div v-else> + <courseware-companion-box + v-if="currentElement !== ''" + :msgCompanion="textCompanionWrongContext" + mood="sad" + /> + </div> </div> - </div> + </focus-trap> </template> <script> @@ -507,6 +510,7 @@ import CoursewareRibbon from './CoursewareRibbon.vue'; import CoursewareTabs from './CoursewareTabs.vue'; import CoursewareTab from './CoursewareTab.vue'; import CoursewareExport from '@/vue/mixins/courseware/export.js'; +import { FocusTrap } from 'focus-trap-vue'; import IsoDate from './IsoDate.vue'; import StudipDialog from '../StudipDialog.vue'; import draggable from 'vuedraggable'; @@ -527,6 +531,7 @@ export default { CoursewareEmptyElementBox, CoursewareTabs, CoursewareTab, + FocusTrap, IsoDate, StudipDialog, draggable, @@ -582,6 +587,7 @@ export default { ghostClass: 'container-ghost', }, errorEmptyChapterName: false, + consumModeTrap: false, }; }, @@ -1319,6 +1325,13 @@ export default { containers() { this.containerList = this.containers; }, + consumeMode(newState) { + if (newState) { + this.consumModeTrap = true; + } else { + this.consumModeTrap = false; + } + }, }, // this line provides all the components to courseware plugins diff --git a/resources/vue/components/courseware/CoursewareViewWidget.vue b/resources/vue/components/courseware/CoursewareViewWidget.vue index 39dee0ec71b..9717d2d65d0 100755 --- a/resources/vue/components/courseware/CoursewareViewWidget.vue +++ b/resources/vue/components/courseware/CoursewareViewWidget.vue @@ -4,8 +4,8 @@ <a href="#" @click="setReadView"> <translate>Lesen</translate> </a> - </a> - <li v-if="canEdit" :class="{ active: editView }"> + </li> + <li :class="{ active: editView }"> <a href="#" @click="setEditView"> <translate>Bearbeiten</translate> </a> -- GitLab From 530c57d786ae4f6952edd0d6257b5640ef146280 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Wed, 19 Jan 2022 16:21:19 +0100 Subject: [PATCH 20/34] accessibility for cw manager --- .../assets/stylesheets/scss/courseware.scss | 34 +++++++++++++----- .../CoursewareManagerCopySelector.vue | 13 +++---- .../courseware/CoursewareManagerElement.vue | 13 +++++-- .../CoursewareManagerElementItem.vue | 35 ++++++++++++------- .../courseware/CoursewareManagerFiling.vue | 5 +-- 5 files changed, 70 insertions(+), 30 deletions(-) diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss index 1b52d239a20..2b88cdcf95f 100755 --- a/resources/assets/stylesheets/scss/courseware.scss +++ b/resources/assets/stylesheets/scss/courseware.scss @@ -2711,21 +2711,18 @@ m a n a g e r } .cw-manager-element-item { + display: flex; border: solid thin $content-color-40; padding: 1em; - margin-bottom: 4px; - text-align: center; + justify-content: space-between; vertical-align: middle; background-color: $white; color: $base-color; - cursor: pointer; img { vertical-align: middle; } - &:last-child { - margin-bottom: 0; - } + &:hover { color: $white; background-color: $base-color; @@ -2735,6 +2732,21 @@ m a n a g e r background-color: $white; color: $base-color; } + + &.cw-manager-element-item-inserter { + padding-left: 2em; + justify-content: start; + @include background-icon(arr_2left, clickable, 16); + background-position: 6px center; + background-repeat: no-repeat; + &:hover { + @include background-icon(arr_2left, info-alt, 16); + } + + img { + margin-right: 1em; + } + } } .cw-manager-filing { @@ -2767,8 +2779,14 @@ m a n a g e r .cw-manager-block-buttons, .cw-manager-container-buttons, .cw-manager-element-item-buttons { - display: inline; - float: right; + display: flex; + justify-content: end; + min-width: 38px; + + a { + flex: unset; + } + img { cursor: pointer; transition: opacity 0.4s ease-in-out; diff --git a/resources/vue/components/courseware/CoursewareManagerCopySelector.vue b/resources/vue/components/courseware/CoursewareManagerCopySelector.vue index 29d51dcab2c..8cc444f9e8b 100755 --- a/resources/vue/components/courseware/CoursewareManagerCopySelector.vue +++ b/resources/vue/components/courseware/CoursewareManagerCopySelector.vue @@ -13,18 +13,19 @@ <li v-for="semester in semesterMap" :key="semester.id"> <h3>{{semester.attributes.title}}</h3> <ul> - <li + <a v-for="course in coursesBySemester(semester)" :key="course.id" - class="cw-manager-copy-selector-course" + href="#" @click="loadRemoteCourseware(course.id)" > - <studip-icon :shape="getCourseIcon(course)" /> - {{course.attributes.title}} - </li> + <li class="cw-manager-copy-selector-course"> + <studip-icon :shape="getCourseIcon(course)" /> + {{course.attributes.title}} + </li> + </a> </ul> </li> - </ul> <courseware-companion-box v-if="!hasRemoteCid && semesterMap.length === 0" diff --git a/resources/vue/components/courseware/CoursewareManagerElement.vue b/resources/vue/components/courseware/CoursewareManagerElement.vue index f2c3254c0a7..a4750ec8238 100755 --- a/resources/vue/components/courseware/CoursewareManagerElement.vue +++ b/resources/vue/components/courseware/CoursewareManagerElement.vue @@ -8,15 +8,24 @@ <span v-for="element in breadcrumb" :key="element.id" + :title="element.attributes.title" + tabindex="0" class="cw-manager-element-breadcrumb-item" @click="selectChapter(element.id)" + @keydown.enter="selectChapter(element.id)" > {{ element.attributes.title }} </span> </div> <header> - <span v-if="elementInserterActive && moveSelfPossible && canEdit" @click="insertElement({element: currentElement, source: type})"> - <studip-icon shape="arr_2left" size="24" role="sort" /> + <span + v-if="elementInserterActive && moveSelfPossible && canEdit" + herf="#" + :title="$gettextInterpolate('%{ elementTitle } verschieben', {elementTitle: elementTitle})" + @click="insertElement({element: currentElement, source: type})" + @keydown.enter="insertElement({element: currentElement, source: type})" + > + <studip-icon shape="arr_2left" size="24" role="clickable" /> </span> {{ elementTitle }} </header> diff --git a/resources/vue/components/courseware/CoursewareManagerElementItem.vue b/resources/vue/components/courseware/CoursewareManagerElementItem.vue index 0162ca23f57..49ec34af44e 100755 --- a/resources/vue/components/courseware/CoursewareManagerElementItem.vue +++ b/resources/vue/components/courseware/CoursewareManagerElementItem.vue @@ -1,16 +1,27 @@ <template> - <div - class="cw-manager-element-item" - :class="{ 'cw-manager-element-item-sorting': sortChapters }" - @click="clickItem" - > - <span v-if="inserter" @click="clickItem"> - <studip-icon shape="arr_2left" size="16" role="sort" /> - </span> - {{ element.attributes.title }} - <div v-if="sortChapters" class="cw-manager-element-item-buttons"> - <studip-icon :class="{'cw-manager-icon-disabled' : !canMoveUp}" shape="arr_2up" size="16" role="sort" @click="moveUp" /> - <studip-icon :class="{'cw-manager-icon-disabled' : !canMoveDown}" shape="arr_2down" size="16" role="sort" @click="moveDown" /> + <div class="cw-manager-element-item-wrapper"> + <a + v-if="!sortChapters" + href="#" + class="cw-manager-element-item" + :class="[inserter ? 'cw-manager-element-item-inserter' : '']" + :title="inserter ? $gettextInterpolate('%{ elementTitle } verschieben', {elementTitle: element.attributes.title}) : element.attributes.title" + @click="clickItem"> + {{ element.attributes.title }} + </a> + <div + v-else + class="cw-manager-element-item cw-manager-element-item-sorting" + > + {{ element.attributes.title }} + <div v-if="sortChapters" class="cw-manager-element-item-buttons"> + <a v-if="canMoveUp" href="#" @click="moveUp" :title="$gettext('Element nach oben verschieben')"> + <studip-icon :class="{'cw-manager-icon-disabled' : !canMoveUp}" shape="arr_2up" size="16" role="clickable" /> + </a> + <a v-if="canMoveDown" href="#" @click="moveDown" :title="$gettext('Element nach unten verschieben')"> + <studip-icon :class="{'cw-manager-icon-disabled' : !canMoveDown}" shape="arr_2down" size="16" role="clickable" /> + </a> + </div> </div> </div> </template> diff --git a/resources/vue/components/courseware/CoursewareManagerFiling.vue b/resources/vue/components/courseware/CoursewareManagerFiling.vue index d6f1503f173..fa0a7133837 100755 --- a/resources/vue/components/courseware/CoursewareManagerFiling.vue +++ b/resources/vue/components/courseware/CoursewareManagerFiling.vue @@ -1,13 +1,14 @@ <template> - <div + <button class="cw-manager-filing" :class="{ 'cw-manager-filing-active': active, 'cw-manager-filing-disabled': disabled }" + :aria-pressed="active" @click="toggleFiling" > <span v-if="itemType === 'element'"><translate>Seite an dieser Stelle einfügen</translate> </span> <span v-if="itemType === 'container'"><translate>Abschnitt an dieser Stelle einfügen</translate> </span> <span v-if="itemType === 'block'"><translate>Block an dieser Stelle einfügen</translate> </span> - </div> + </button> </template> <script> -- GitLab From b1809051e0c857cde26b56ee6808164971225139 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Wed, 19 Jan 2022 16:49:52 +0100 Subject: [PATCH 21/34] accessibility for cw dashboard --- .../CoursewareDashboardProgress.vue | 28 +++++++++++++++---- .../CoursewareDashboardProgressItem.vue | 8 +++++- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareDashboardProgress.vue b/resources/vue/components/courseware/CoursewareDashboardProgress.vue index 0c32d825ea5..3172edf373c 100755 --- a/resources/vue/components/courseware/CoursewareDashboardProgress.vue +++ b/resources/vue/components/courseware/CoursewareDashboardProgress.vue @@ -1,13 +1,29 @@ <template> <div class="cw-dashboard-progress"> <div class="cw-dashboard-progress-breadcrumb"> - <span v-if="parent" @click="visitRoot"><studip-icon shape="home" /></span> - <span v-if="parent" @click="selectChapter(parent.id)"> / {{ parent.name }}</span> + <span + v-if="parent" + tabindex="0" + :title="$gettext('Hauptseite')" + @click="visitRoot" + @keydown.enter="visitRoot" + > + <studip-icon shape="home" /> + </span> + <span + v-if="parent" + tabindex="0" + :title="parent.name" + @click="selectChapter(parent.id)" + @keydown.enter="selectChapter(parent.id)" + > + / {{ parent.name }} + </span> </div> - <div class="cw-dashboard-progress-chapter" v-if="selected"> - <h1> - <a :href="chapterUrl">{{ selected.name }}</a> - </h1> + <div v-if="selected" class="cw-dashboard-progress-chapter"> + <a :href="chapterUrl" :title="$gettextInterpolate('%{ pageTitle } öffnen', {pageTitle: selected.name})"> + <h1>{{ selected.name }}</h1> + </a> <courseware-progress-circle :title="$gettext('diese Seite inkl. darunter liegende Seiten')" :value="parseInt(selected.progress.cumulative)" diff --git a/resources/vue/components/courseware/CoursewareDashboardProgressItem.vue b/resources/vue/components/courseware/CoursewareDashboardProgressItem.vue index adc26b7a872..3c96ed40379 100755 --- a/resources/vue/components/courseware/CoursewareDashboardProgressItem.vue +++ b/resources/vue/components/courseware/CoursewareDashboardProgressItem.vue @@ -1,5 +1,11 @@ <template> - <div class="cw-dashboard-progress-item" @click="$emit('selectChapter', chapterId)"> + <div + class="cw-dashboard-progress-item" + tabindex="0" + :title="name" + @click="$emit('selectChapter', chapterId)" + @keydown.enter="$emit('selectChapter', chapterId)" + > <div class="cw-dashboard-progress-item-value"> <courseware-progress-circle :value="parseInt(value)" /> </div> -- GitLab From bd31afd786f5aa4e59fb38bcf0b59403d8e79775 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 25 Jan 2022 16:28:28 +0100 Subject: [PATCH 22/34] fix review issues --- .../assets/stylesheets/scss/courseware.scss | 21 ++++++++++++---- .../courseware/CoursewareBlockAdderArea.vue | 5 ++-- .../CoursewareContainerAdderItem.vue | 18 +++++++------- .../CoursewareDashboardProgress.vue | 24 +++++++++---------- .../CoursewareDashboardProgressItem.vue | 7 +++--- .../CoursewareManagerCopySelector.vue | 17 +++++++------ .../courseware/CoursewareManagerElement.vue | 16 ++++++------- 7 files changed, 58 insertions(+), 50 deletions(-) diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss index 2b88cdcf95f..6de3ba6e129 100755 --- a/resources/assets/stylesheets/scss/courseware.scss +++ b/resources/assets/stylesheets/scss/courseware.scss @@ -1715,6 +1715,7 @@ b l o c k a d d e r } .cw-block-adder-area { + background-color: $white; border: solid thin $content-color-40; padding: 1em 0; color: $base-color; @@ -1723,6 +1724,10 @@ b l o c k a d d e r font-weight: 600; cursor: pointer; + &:hover { + border-color: $base-color; + } + &.cw-block-adder-active { border: solid thin $base-color; background-color: $base-color; @@ -2298,10 +2303,9 @@ d a s h b o a r d } .cw-dashboard-progress-item { + display: block; border-bottom: solid thin $content-color-40; - width: 100%; - cursor: pointer; - padding: 8px 0 8px 0; + padding: 10px 0; &:hover{ background-color: hsla(217,6%,45%,.2); @@ -2328,7 +2332,6 @@ d a s h b o a r d } } .cw-dashboard-progress-item-description { - width: calc(100% - 90px); color: $base-color; padding-left: 14px; text-overflow: ellipsis; @@ -2710,6 +2713,14 @@ m a n a g e r } + .cw-manager-element-item-wrapper { + margin-bottom: 4px; + + &:last-child { + margin-bottom: 0; + } + } + .cw-manager-element-item { display: flex; border: solid thin $content-color-40; @@ -4633,8 +4644,8 @@ cw tiles end list-style: none; } .cw-manager-copy-selector-course { + display: block; color: $base-color; - cursor: pointer; line-height: 2em; &:hover { diff --git a/resources/vue/components/courseware/CoursewareBlockAdderArea.vue b/resources/vue/components/courseware/CoursewareBlockAdderArea.vue index f2fdc863486..5b0ef2b3133 100755 --- a/resources/vue/components/courseware/CoursewareBlockAdderArea.vue +++ b/resources/vue/components/courseware/CoursewareBlockAdderArea.vue @@ -1,14 +1,15 @@ <template> - <div + <button class="cw-block-adder-area" :class="{ 'cw-block-adder-active': adderActive }" + :aria-pressed="adderActive" @click="selectBlockAdder" > <studip-icon v-show="!adderActive" shape="add" /> <studip-icon v-show="adderActive" shape="add" role="info_alt"/> <span v-show="!adderActive"><translate>Block zu diesem Abschnitt hinzufügen</translate></span> <span v-show="adderActive"><translate>Abschnitt aktiv - Blöcke werden hier eingefügt</translate></span> - </div> + </button> </template> <script> diff --git a/resources/vue/components/courseware/CoursewareContainerAdderItem.vue b/resources/vue/components/courseware/CoursewareContainerAdderItem.vue index dc3bdff84fc..bf64758f837 100755 --- a/resources/vue/components/courseware/CoursewareContainerAdderItem.vue +++ b/resources/vue/components/courseware/CoursewareContainerAdderItem.vue @@ -1,12 +1,14 @@ <template> - <div class="cw-blockadder-item" :class="['cw-blockadder-item-' + type]" @click="addContainer"> - <header class="cw-blockadder-item-title"> - {{ title }} - </header> - <p class="cw-blockadder-item-description"> - {{ description }} - </p> - </div> + <a href="#" @click="addContainer"> + <div class="cw-blockadder-item" :class="['cw-blockadder-item-' + type]"> + <header class="cw-blockadder-item-title"> + {{ title }} + </header> + <p class="cw-blockadder-item-description"> + {{ description }} + </p> + </div> + </a> </template> <script> import { mapActions } from 'vuex'; diff --git a/resources/vue/components/courseware/CoursewareDashboardProgress.vue b/resources/vue/components/courseware/CoursewareDashboardProgress.vue index 3172edf373c..36e4a510618 100755 --- a/resources/vue/components/courseware/CoursewareDashboardProgress.vue +++ b/resources/vue/components/courseware/CoursewareDashboardProgress.vue @@ -1,29 +1,27 @@ <template> <div class="cw-dashboard-progress"> - <div class="cw-dashboard-progress-breadcrumb"> - <span + <nav aria-label="Breadcrumb" class="cw-dashboard-progress-breadcrumb"> + <a v-if="parent" - tabindex="0" + href="#" :title="$gettext('Hauptseite')" @click="visitRoot" - @keydown.enter="visitRoot" > <studip-icon shape="home" /> - </span> - <span + </a> + <a v-if="parent" - tabindex="0" + href="#" :title="parent.name" @click="selectChapter(parent.id)" - @keydown.enter="selectChapter(parent.id)" > / {{ parent.name }} - </span> - </div> + </a> + </nav> <div v-if="selected" class="cw-dashboard-progress-chapter"> - <a :href="chapterUrl" :title="$gettextInterpolate('%{ pageTitle } öffnen', {pageTitle: selected.name})"> - <h1>{{ selected.name }}</h1> - </a> + <a :href="chapterUrl" :title="$gettextInterpolate('%{ pageTitle } öffnen', {pageTitle: selected.name})"> + <h1>{{ selected.name }}</h1> + </a> <courseware-progress-circle :title="$gettext('diese Seite inkl. darunter liegende Seiten')" :value="parseInt(selected.progress.cumulative)" diff --git a/resources/vue/components/courseware/CoursewareDashboardProgressItem.vue b/resources/vue/components/courseware/CoursewareDashboardProgressItem.vue index 3c96ed40379..e5ec0d23567 100755 --- a/resources/vue/components/courseware/CoursewareDashboardProgressItem.vue +++ b/resources/vue/components/courseware/CoursewareDashboardProgressItem.vue @@ -1,10 +1,9 @@ <template> - <div + <a + href="#" class="cw-dashboard-progress-item" - tabindex="0" :title="name" @click="$emit('selectChapter', chapterId)" - @keydown.enter="$emit('selectChapter', chapterId)" > <div class="cw-dashboard-progress-item-value"> <courseware-progress-circle :value="parseInt(value)" /> @@ -12,7 +11,7 @@ <div class="cw-dashboard-progress-item-description"> {{ name }} </div> - </div> + </a> </template> <script> diff --git a/resources/vue/components/courseware/CoursewareManagerCopySelector.vue b/resources/vue/components/courseware/CoursewareManagerCopySelector.vue index 8cc444f9e8b..01c2c36b7d7 100755 --- a/resources/vue/components/courseware/CoursewareManagerCopySelector.vue +++ b/resources/vue/components/courseware/CoursewareManagerCopySelector.vue @@ -13,17 +13,16 @@ <li v-for="semester in semesterMap" :key="semester.id"> <h3>{{semester.attributes.title}}</h3> <ul> - <a - v-for="course in coursesBySemester(semester)" - :key="course.id" - href="#" - @click="loadRemoteCourseware(course.id)" - > - <li class="cw-manager-copy-selector-course"> + <li v-for="course in coursesBySemester(semester)" :key="course.id"> + <a + href="#" + class="cw-manager-copy-selector-course" + @click="loadRemoteCourseware(course.id)" + > <studip-icon :shape="getCourseIcon(course)" /> {{course.attributes.title}} - </li> - </a> + </a> + </li> </ul> </li> </ul> diff --git a/resources/vue/components/courseware/CoursewareManagerElement.vue b/resources/vue/components/courseware/CoursewareManagerElement.vue index a4750ec8238..5a7f215f895 100755 --- a/resources/vue/components/courseware/CoursewareManagerElement.vue +++ b/resources/vue/components/courseware/CoursewareManagerElement.vue @@ -4,29 +4,27 @@ <courseware-companion-box v-if="insertingInProgress" :msgCompanion="text.inProgress" mood="pointing" /> <courseware-companion-box v-if="copyingFailed && !insertingInProgress" :msgCompanion="copyProcessFailedMessage" mood="sad" /> <div class="cw-manager-element-title"> - <div class="cw-manager-element-breadcrumb"> - <span + <nav aria-label="Breadcrumb" class="cw-manager-element-breadcrumb"> + <a v-for="element in breadcrumb" :key="element.id" :title="element.attributes.title" - tabindex="0" + href="#" class="cw-manager-element-breadcrumb-item" @click="selectChapter(element.id)" - @keydown.enter="selectChapter(element.id)" > {{ element.attributes.title }} - </span> - </div> + </a> + </nav> <header> - <span + <a v-if="elementInserterActive && moveSelfPossible && canEdit" herf="#" :title="$gettextInterpolate('%{ elementTitle } verschieben', {elementTitle: elementTitle})" @click="insertElement({element: currentElement, source: type})" - @keydown.enter="insertElement({element: currentElement, source: type})" > <studip-icon shape="arr_2left" size="24" role="clickable" /> - </span> + </a> {{ elementTitle }} </header> </div> -- GitLab From b9ff86086497cf86eb285d402284fafcc4bf0831 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Tue, 25 Jan 2022 16:37:22 +0100 Subject: [PATCH 23/34] add border color to cw-manager-filing on hover --- resources/assets/stylesheets/scss/courseware.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss index 6de3ba6e129..b4023598e10 100755 --- a/resources/assets/stylesheets/scss/courseware.scss +++ b/resources/assets/stylesheets/scss/courseware.scss @@ -2774,6 +2774,10 @@ m a n a g e r font-weight: 600; cursor: pointer; + &:hover { + border-color: $base-color; + } + &.cw-manager-filing-active { @include background-icon(arr_eol-down, info-alt, 24); background-color: $base-color; -- GitLab From 7018b68c4b559c02476ea2150a09470739fb9058 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Wed, 26 Jan 2022 16:49:24 +0100 Subject: [PATCH 24/34] accessibility for tabs --- .../assets/stylesheets/scss/courseware.scss | 52 ++++--- .../courseware/CoursewareCourseManager.vue | 14 +- .../courseware/CoursewareDialogCardsBlock.vue | 1 + .../courseware/CoursewareImageMapBlock.vue | 1 + .../courseware/CoursewareRibbonToolbar.vue | 137 +++++++++++++++--- .../CoursewareStructuralElement.vue | 10 +- .../components/courseware/CoursewareTab.vue | 10 +- .../components/courseware/CoursewareTabs.vue | 49 ++++++- .../courseware/CoursewareToolsBlockadder.vue | 69 +++++++-- 9 files changed, 263 insertions(+), 80 deletions(-) diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss index b4023598e10..f4d01902960 100755 --- a/resources/assets/stylesheets/scss/courseware.scss +++ b/resources/assets/stylesheets/scss/courseware.scss @@ -445,40 +445,44 @@ $consum_ribbon_width: calc(100% - 58px); background-color: #fff; } - ul { + .cw-ribbon-tool-content-tablist { list-style-type: none; width: 100%; display: flex; flex-wrap: wrap; margin: 0; padding: 0; - } - li { - padding: 0 8px; - margin-top: 8px; - text-align: center; - flex-grow: 0.5; - cursor: pointer; - - &:after { - display: block; - content: ''; - border-bottom: solid 3px $dark-gray-color-75; - transform: scaleX(0); - transition: transform 300ms ease-in-out; - margin: 17px 0 0 0; - } - &.active, - &:hover { - color: $black; + button { + background-color: $white; + border: none; + padding: 0 8px; + margin-top: 8px; + text-align: center; + flex-grow: 0.5; + cursor: pointer; + &:after { - transform: scaleX(1); + display: block; + content: ''; + border-bottom: solid 3px $dark-gray-color-75; + transform: scaleX(0); + transition: transform 300ms ease-in-out; + margin: 17px 0 0 0; + } + + &.active, + &:hover { + color: $black; + &:after { + transform: scaleX(1); + } } } } - &:hover li { + + &:hover button { &.active::after { transform: scaleX(0); } @@ -1494,8 +1498,9 @@ $icons: ( border: solid thin $content-color-40; border-bottom: none; - li { + button { background-color: $white; + border: none; padding: 1em 0 4px 0; margin: 0 7px 0 21px; color: $base-color; @@ -1649,6 +1654,7 @@ b l o c k a d d e r .cw-tools-element-adder-tab { background-color: $white; + border: none; padding: 1em 1.5em 0 1.5em; margin: 0 1em; color: $base-color; diff --git a/resources/vue/components/courseware/CoursewareCourseManager.vue b/resources/vue/components/courseware/CoursewareCourseManager.vue index aed709d1c02..a2dbe99a88d 100755 --- a/resources/vue/components/courseware/CoursewareCourseManager.vue +++ b/resources/vue/components/courseware/CoursewareCourseManager.vue @@ -2,7 +2,7 @@ <div class="cw-course-manager-wrapper"> <div class="cw-course-manager"> <courseware-tabs class="cw-course-manager-tabs"> - <courseware-tab :name="$gettext('Diese Courseware')" :selected="true"> + <courseware-tab :name="$gettext('Diese Courseware')" :selected="true" :index="0"> <courseware-manager-element type="current" :currentElement="currentElement" @@ -10,7 +10,7 @@ @reloadElement="reloadElements" /> </courseware-tab> - <courseware-tab :name="$gettext('Export')"> + <courseware-tab :name="$gettext('Export')" :index="1"> <button class="button" @click.prevent="doExportCourseware" @@ -31,7 +31,7 @@ </courseware-tabs> <courseware-tabs class="cw-course-manager-tabs"> - <courseware-tab :name="$gettext('FAQ')"> + <courseware-tab :name="$gettext('FAQ')" :index="0"> <courseware-collapsible-box :open="true" :title="$gettext('Wie finde ich die gewünschte Stelle?')"> <p><translate> Wählen Sie auf der linken Seite "Diese Courseware" aus. @@ -77,7 +77,7 @@ </translate></p> </courseware-collapsible-box> </courseware-tab> - <courseware-tab name="Verschieben" :selected="true"> + <courseware-tab name="Verschieben" :selected="true" :index="1"> <courseware-manager-element type="self" :currentElement="selfElement" @@ -88,11 +88,11 @@ /> </courseware-tab> - <courseware-tab :name="$gettext('Kopieren')"> + <courseware-tab :name="$gettext('Kopieren')" :index="2"> <courseware-manager-copy-selector @loadSelf="reloadElements" @reloadElement="reloadElements" /> </courseware-tab> - <courseware-tab :name="$gettext('Importieren')"> + <courseware-tab :name="$gettext('Importieren')" :index="3"> <courseware-companion-box v-show="!importRunning && importDone" :msgCompanion="$gettext('Import erfolgreich!')" mood="special"/> <courseware-companion-box v-show="importRunning" :msgCompanion="$gettext('Import läuft. Bitte verlassen Sie die Seite nicht bis der Import abgeschlossen wurde.')" mood="pointing"/> <button @@ -138,7 +138,7 @@ <input ref="importFile" type="file" accept=".zip" @change="setImport" style="visibility: hidden" /> </courseware-tab> - <courseware-tab v-if="context.type === 'courses'" :name="$gettext('Aufgabe verteilen')"> + <courseware-tab v-if="context.type === 'courses'" :name="$gettext('Aufgabe verteilen')" :index="4"> <courseware-manager-task-distributor /> </courseware-tab> </courseware-tabs> diff --git a/resources/vue/components/courseware/CoursewareDialogCardsBlock.vue b/resources/vue/components/courseware/CoursewareDialogCardsBlock.vue index cdee93a1f03..2777f33e297 100755 --- a/resources/vue/components/courseware/CoursewareDialogCardsBlock.vue +++ b/resources/vue/components/courseware/CoursewareDialogCardsBlock.vue @@ -63,6 +63,7 @@ <courseware-tab v-for="(card, index) in currentCards" :key="index" + :index="index" :name="$gettext('Karte') + ' ' + (index + 1).toString()" :selected="index === 0" canBeEmpty diff --git a/resources/vue/components/courseware/CoursewareImageMapBlock.vue b/resources/vue/components/courseware/CoursewareImageMapBlock.vue index 9596e8854a0..9ba60d662c1 100755 --- a/resources/vue/components/courseware/CoursewareImageMapBlock.vue +++ b/resources/vue/components/courseware/CoursewareImageMapBlock.vue @@ -54,6 +54,7 @@ <courseware-tab v-for="(shape, index) in currentShapes" :key="index" + :index="index" :name="shape.title" :icon="shape.title === '' ? 'link-extern' : ''" :selected="index === 0" diff --git a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue index 2adceb021b9..736f373ce12 100755 --- a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue +++ b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue @@ -1,5 +1,5 @@ <template> - <focus-trap v-model="trap" :initial-focus="() => $refs.focusPoint"> + <focus-trap v-model="trap" :initial-focus="() => $refs.contents"> <div class="cw-ribbon-tools" :class="{ unfold: toolsActive, 'cw-ribbon-tools-consume': consumeMode }" @@ -7,35 +7,62 @@ > <div class="cw-ribbon-tool-content"> <div class="cw-ribbon-tool-content-nav"> - <ul> - <li :class="{ active: showContents }"> - <a href="#" ref="focusPoint" @click="displayTool('contents')"> - <translate>Inhaltsverzeichnis</translate> - </a> - </li> - <li + <div role="tablist" class="cw-ribbon-tool-content-tablist"> + <button + :class="{ active: showContents }" + :aria-selected="showContents" + :aria-controls="$gettext('Inhaltsverzeichnis') + '-tab'" + :tabindex="showContents ? 0 : -1" + ref="contents" + @click="displayTool('contents')" + @keydown="handleKeyEvent('contents', $event)" + > + <translate>Inhaltsverzeichnis</translate> + </button> + <button v-if="!consumeMode && showEditMode && canEdit" :class="{ active: showBlockAdder }" + :aria-selected="showBlockAdder" + :aria-controls="$gettext('Elemente hinzufügen') + '-tab'" + :tabindex="showBlockAdder ? 0 : -1" + ref="blockadder" + @click="displayTool('blockadder')" + @keydown="handleKeyEvent('blockadder', $event)" > - <a href="#" @click="displayTool('blockadder')"> - <translate>Elemente hinzufügen</translate> - </a> - </li> - <li + <translate>Elemente hinzufügen</translate> + </button> + <button v-if="!consumeMode && displaySettings" :class="{ active: showAdmin }" + :aria-selected="showAdmin" + :aria-controls="$gettext('Einstellungen') + '-tab'" + :tabindex="showAdmin ? 0 : -1" + ref="admin" + @click="displayTool('admin')" + @keydown="handleKeyEvent('admin', $event)" > - <a href="#" @click="displayTool('admin')"> - <translate>Einstellungen</translate> - </a> - </li> - </ul> + <translate>Einstellungen</translate> + </button> + </div> <a href="#" :title="textClose" class="cw-tools-hide-button" @click="$emit('deactivate')"></a> </div> <div class="cw-ribbon-tool" ref="ribbonContent" @scroll="handleScroll"> - <courseware-tools-contents v-if="showContents" /> - <courseware-tools-blockadder v-if="showBlockAdder" @scrollTop="scrollTop('blockadder')"/> - <courseware-tools-admin v-if="showAdmin" /> + <courseware-tools-contents + v-if="showContents" + role="tabpanel" + :aria-labelledby="$gettext('Inhaltsverzeichnis')" + /> + <courseware-tools-blockadder + v-if="showBlockAdder" + role="tabpanel" + :aria-labelledby="$gettext('Elemente hinzufügen')" + @scrollTop="scrollTop('blockadder')" + /> + <courseware-tools-admin + v-if="showAdmin" + role="tabpanel" + :aria-labelledby="$gettext('Einstellungen')" + /> </div> </div> </div> @@ -105,14 +132,17 @@ export default { switch (tool) { case 'contents': this.showContents = true; + this.$refs.contents.focus(); this.disableContainerAdder(); break; case 'admin': this.showAdmin = true; + this.$refs.admin.focus(); this.disableContainerAdder(); break; case 'blockadder': this.showBlockAdder = true; + this.$refs.blockadder.focus(); break; } this.updateScrollPos(); @@ -170,6 +200,71 @@ export default { scrollTop: scrollPos }, 100); }); + }, + handleKeyEvent(tab, e) { + switch (e.keyCode) { + case 37: // left + case 38: // up + if (tab === 'contents') { + if (!this.consumeMode && this.displaySettings) { + this.displayTool('admin'); + break; + } + if (!this.consumeMode && this.showEditMode && this.canEdit) { + this.displayTool('blockadder'); + break; + } + } + if (tab === 'blockadder') { + this.displayTool('contents'); + break; + } + if (tab === 'admin') { + if (!this.consumeMode && this.showEditMode && this.canEdit) { + this.displayTool('blockadder'); + break; + } + this.displayTool('contents'); + break; + } + break; + case 39: // right + case 40: // down + if (tab === 'contents') { + if (!this.consumeMode && this.showEditMode && this.canEdit) { + this.displayTool('blockadder'); + break; + } + if (!this.consumeMode && this.displaySettings) { + this.displayTool('admin'); + break; + } + } + if (tab === 'blockadder') { + if (!this.consumeMode && this.displaySettings) { + this.displayTool('admin'); + break; + } + this.displayTool('contents'); + break; + } + if (tab === 'admin') { + this.displayTool('contents'); + } + break; + case 36: //pos1 + this.displayTool('contents'); + break; + case 35: //end + if (!this.consumeMode && this.displaySettings) { + this.displayTool('admin'); + break; + } + if (!this.consumeMode && this.showEditMode && this.canEdit) { + this.displayTool('blockadder'); + break; + } + } } }, mounted () { diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue index 4aae39e8508..626b123f5ea 100755 --- a/resources/vue/components/courseware/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue @@ -157,7 +157,7 @@ > <template v-slot:dialogContent> <courseware-tabs class="cw-tab-in-dialog"> - <courseware-tab :name="textEdit.basic" :selected="true"> + <courseware-tab :name="textEdit.basic" :selected="true" :index="0"> <form class="default" @submit.prevent=""> <label> <translate>Titel</translate> @@ -172,7 +172,7 @@ </label> </form> </courseware-tab> - <courseware-tab :name="textEdit.meta"> + <courseware-tab :name="textEdit.meta" :index="1"> <form class="default" @submit.prevent=""> <label> <translate>Farbe</translate> @@ -249,7 +249,7 @@ </label> </form> </courseware-tab> - <courseware-tab :name="textEdit.image"> + <courseware-tab :name="textEdit.image" :index="2"> <form class="default" @submit.prevent=""> <img v-if="image" @@ -269,7 +269,7 @@ </label> </form> </courseware-tab> - <courseware-tab :name="textEdit.approval"> + <courseware-tab :name="textEdit.approval" :index="3"> <courseware-structural-element-permissions v-if="inCourse" :element="currentElement" @@ -289,7 +289,7 @@ <translate>Seite zum kopieren für Lehrende freigeben</translate> </label> --> </courseware-tab> - <courseware-tab v-if="inCourse" :name="textEdit.visible"> + <courseware-tab v-if="inCourse" :name="textEdit.visible" :index="4"> <form class="default" @submit.prevent=""> <label> <translate>Sichtbar ab</translate> diff --git a/resources/vue/components/courseware/CoursewareTab.vue b/resources/vue/components/courseware/CoursewareTab.vue index c11a552e253..f9c91010c4e 100755 --- a/resources/vue/components/courseware/CoursewareTab.vue +++ b/resources/vue/components/courseware/CoursewareTab.vue @@ -1,5 +1,11 @@ <template> - <div class="cw-tab" :class="{ 'cw-tab-active': isActive }"> + <div + tabindex="0" + role="tabpanel" + class="cw-tab" + :class="{ 'cw-tab-active': isActive }" + :aria-labelledby="name" + > <slot></slot> </div> </template> @@ -10,7 +16,7 @@ export default { props: { name: {type: String, required: true }, selected: { type: Boolean, default: false }, - index: {type: Number, default: 0 }, + index: {type: Number, required: true }, icon: {type: String, default: ''}, }, data() { diff --git a/resources/vue/components/courseware/CoursewareTabs.vue b/resources/vue/components/courseware/CoursewareTabs.vue index 4df1a5d724c..26923bb1f30 100755 --- a/resources/vue/components/courseware/CoursewareTabs.vue +++ b/resources/vue/components/courseware/CoursewareTabs.vue @@ -1,7 +1,7 @@ <template> <div class="cw-tabs"> - <ul class="cw-tabs-nav"> - <li + <div role="tablist" class="cw-tabs-nav"> + <button v-for="(tab, index) in tabs" :key="index" :class="[ @@ -9,15 +9,18 @@ tab.icon !== '' && tab.name !== '' ? 'cw-tabs-nav-icon-text-' + tab.icon : '', tab.icon !== '' && tab.name === '' ? 'cw-tabs-nav-icon-solo-' + tab.icon : '', ]" + :aria-selected="tab.isActive" + :aria-controls="tab.name + '-tab'" + :id="tab.href" :href="tab.href" - tabindex="0" + :tabindex="tab.isActive ? 0 : -1" @click="selectTab(tab)" - @keydown.enter="selectTab(tab)" - @keydown.space="selectTab(tab)" + @keydown="handleKeyEvent(tab, $event)" + :ref="tab.href" > {{ tab.name }} - </li> - </ul> + </button> + </div> <div class="cw-tabs-content"> <slot></slot> </div> @@ -28,18 +31,48 @@ export default { name: 'courseware-tabs', data() { - return { tabs: [] }; + return { + tabs: [], + }; }, created() { this.tabs = this.$children; }, methods: { selectTab(selectedTab) { + let view = this; this.tabs.forEach((tab) => { tab.isActive = tab.index + '-' + tab.name === selectedTab.index + '-' + selectedTab.name; + view.$refs[selectedTab.href][0].focus(); }); this.$emit('selectTab', selectedTab.name); }, + handleKeyEvent(tab, e) { + switch (e.keyCode) { + case 37: // left + case 38: // up + if (tab.index > 0) { + this.selectTab(this.tabs[tab.index - 1]); + } else { + this.selectTab(this.tabs[this.tabs.length - 1]); + } + break; + case 39: // right + case 40: // down + if (tab.index < this.tabs.length - 1) { + this.selectTab(this.tabs[tab.index + 1]); + } else { + this.selectTab(this.tabs[0]); + } + break; + case 36: //pos1 + this.selectTab(this.tabs[0]); + break; + case 35: //end + this.selectTab(this.tabs[this.tabs.length - 1]); + break; + } + } }, }; </script> diff --git a/resources/vue/components/courseware/CoursewareToolsBlockadder.vue b/resources/vue/components/courseware/CoursewareToolsBlockadder.vue index b00c3f57139..719e68aad78 100755 --- a/resources/vue/components/courseware/CoursewareToolsBlockadder.vue +++ b/resources/vue/components/courseware/CoursewareToolsBlockadder.vue @@ -1,25 +1,38 @@ <template> <div class="cw-tools-element-adder"> - <ul class="cw-tools-element-adder-tabs"> - <li + <div role="tablist" class="cw-tools-element-adder-tabs"> + <button :class="{ 'active': showBlockadder }" class="cw-tools-element-adder-tab" + :aria-selected="showBlockadder" + :aria-controls="$gettext('Blöcke') + '-tab'" + :tabindex="showBlockadder ? 0 : -1" + ref="blockadder" + @click="displayBlockAdder" + @keydown="handleKeyEvent('block', $event)" > - <a href="#" @click="displayBlockAdder"> - <translate>Blöcke</translate> - </a> - </li> - <li + <translate>Blöcke</translate> + </button> + <button :class="{ 'active': showContaineradder }" class="cw-tools-element-adder-tab" + :aria-selected="showContaineradder" + :aria-controls="$gettext('Abschnitte') + '-tab'" + :tabindex="showContaineradder ? 0 : -1" + ref="containeradder" + @click="displayContainerAdder" + @keydown="handleKeyEvent('container', $event)" > - <a href="#" @click="displayContainerAdder"> - <translate>Abschnitte</translate> - </a> - </li> - </ul> + <translate>Abschnitte</translate> + </button> + </div> - <div v-show="showBlockadder" class="cw-tools cw-tools-blockadder"> + <div + v-show="showBlockadder" + class="cw-tools cw-tools-blockadder" + role="tabpanel" + :aria-labelledby="$gettext('Blöcke')" + > <courseware-collapsible-box :title="textBlockHelper"> <courseware-block-helper :blockTypes="blockTypes" /> </courseware-collapsible-box> @@ -100,7 +113,12 @@ </courseware-collapsible-box> </div> - <div v-show="showContaineradder" class="cw-tools cw-tools-containeradder"> + <div + v-show="showContaineradder" + class="cw-tools cw-tools-containeradder" + role="tabpanel" + :aria-labelledby="$gettext('Abschnitte')" + > <courseware-collapsible-box v-for="(style, index) in containerStyles" :key="index" @@ -189,11 +207,13 @@ export default { displayContainerAdder() { this.showContaineradder = true; this.showBlockadder = false; + this.$refs.containeradder.focus(); }, displayBlockAdder() { this.showContaineradder = false; this.showBlockadder = true; this.disableContainerAdder(); + this.$refs.blockadder.focus(); }, toggleFavItem(block) { if (this.isBlockFav(block)) { @@ -218,6 +238,27 @@ export default { endEditFavs() { this.showEditFavs = false; this.$emit('scrollTop'); + }, + handleKeyEvent(tab, e) { + switch (e.keyCode) { + case 37: // left + case 40: // down + case 39: // right + case 38: // up + if (tab === 'block') { + this.displayContainerAdder(); + } + if (tab === 'container') { + this.displayBlockAdder(); + } + break; + case 36: //pos1 + this.displayBlockAdder(); + break; + case 35: //end + this.displayContainerAdder(); + break; + } } }, mounted() { -- GitLab From f362470a7fd6855340a184f163d37c83ff041b56 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Fri, 28 Jan 2022 15:18:49 +0100 Subject: [PATCH 25/34] add ids to tabs and fix aria-controls and aria-labelledby --- .../courseware/CoursewareRibbonToolbar.vue | 18 ++++++++++++------ .../courseware/CoursewareStructuralElement.vue | 6 +----- .../components/courseware/CoursewareTab.vue | 3 ++- .../courseware/CoursewareToolsBlockadder.vue | 12 ++++++++---- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue index 736f373ce12..94c777aac5d 100755 --- a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue +++ b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue @@ -9,9 +9,10 @@ <div class="cw-ribbon-tool-content-nav"> <div role="tablist" class="cw-ribbon-tool-content-tablist"> <button + id="cw-ribbon-tool-content-tablist-contents" :class="{ active: showContents }" :aria-selected="showContents" - :aria-controls="$gettext('Inhaltsverzeichnis') + '-tab'" + aria-controls="cw-ribbon-tool-contents" :tabindex="showContents ? 0 : -1" ref="contents" @click="displayTool('contents')" @@ -21,9 +22,10 @@ </button> <button v-if="!consumeMode && showEditMode && canEdit" + id="cw-ribbon-tool-content-tablist-blockadder" :class="{ active: showBlockAdder }" :aria-selected="showBlockAdder" - :aria-controls="$gettext('Elemente hinzufügen') + '-tab'" + aria-controls="cw-ribbon-tool-blockadder" :tabindex="showBlockAdder ? 0 : -1" ref="blockadder" @click="displayTool('blockadder')" @@ -33,9 +35,10 @@ </button> <button v-if="!consumeMode && displaySettings" + id="cw-ribbon-tool-content-tablist-admin" :class="{ active: showAdmin }" :aria-selected="showAdmin" - :aria-controls="$gettext('Einstellungen') + '-tab'" + aria-controls="cw-ribbon-tool-admin" :tabindex="showAdmin ? 0 : -1" ref="admin" @click="displayTool('admin')" @@ -49,19 +52,22 @@ <div class="cw-ribbon-tool" ref="ribbonContent" @scroll="handleScroll"> <courseware-tools-contents v-if="showContents" + id="cw-ribbon-tool-contents" role="tabpanel" - :aria-labelledby="$gettext('Inhaltsverzeichnis')" + aria-labelledby="cw-ribbon-tool-content-tablist-contents" /> <courseware-tools-blockadder v-if="showBlockAdder" + id="cw-ribbon-tool-blockadder" role="tabpanel" - :aria-labelledby="$gettext('Elemente hinzufügen')" + aria-labelledby="cw-ribbon-tool-content-tablist-blockadder" @scrollTop="scrollTop('blockadder')" /> <courseware-tools-admin v-if="showAdmin" + id="cw-ribbon-tool-admin" role="tabpanel" - :aria-labelledby="$gettext('Einstellungen')" + aria-labelledby="cw-ribbon-tool-content-tablist-admin" /> </div> </div> diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue index 626b123f5ea..c24ff9aff93 100755 --- a/resources/vue/components/courseware/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue @@ -1326,11 +1326,7 @@ export default { this.containerList = this.containers; }, consumeMode(newState) { - if (newState) { - this.consumModeTrap = true; - } else { - this.consumModeTrap = false; - } + this.consumModeTrap = newState; }, }, diff --git a/resources/vue/components/courseware/CoursewareTab.vue b/resources/vue/components/courseware/CoursewareTab.vue index f9c91010c4e..e307e6d1c37 100755 --- a/resources/vue/components/courseware/CoursewareTab.vue +++ b/resources/vue/components/courseware/CoursewareTab.vue @@ -3,8 +3,9 @@ tabindex="0" role="tabpanel" class="cw-tab" + :id="name + '-tab'" :class="{ 'cw-tab-active': isActive }" - :aria-labelledby="name" + :aria-labelledby="href" > <slot></slot> </div> diff --git a/resources/vue/components/courseware/CoursewareToolsBlockadder.vue b/resources/vue/components/courseware/CoursewareToolsBlockadder.vue index 719e68aad78..a60cb0129c9 100755 --- a/resources/vue/components/courseware/CoursewareToolsBlockadder.vue +++ b/resources/vue/components/courseware/CoursewareToolsBlockadder.vue @@ -2,10 +2,11 @@ <div class="cw-tools-element-adder"> <div role="tablist" class="cw-tools-element-adder-tabs"> <button + id="cw-tools-element-adder-tablist-blockadder" :class="{ 'active': showBlockadder }" class="cw-tools-element-adder-tab" :aria-selected="showBlockadder" - :aria-controls="$gettext('Blöcke') + '-tab'" + aria-controls="cw-tools-blockadder" :tabindex="showBlockadder ? 0 : -1" ref="blockadder" @click="displayBlockAdder" @@ -14,10 +15,11 @@ <translate>Blöcke</translate> </button> <button + id="cw-tools-element-adder-tablist-containeradder" :class="{ 'active': showContaineradder }" class="cw-tools-element-adder-tab" :aria-selected="showContaineradder" - :aria-controls="$gettext('Abschnitte') + '-tab'" + aria-controls="cw-tools-containeradder" :tabindex="showContaineradder ? 0 : -1" ref="containeradder" @click="displayContainerAdder" @@ -29,9 +31,10 @@ <div v-show="showBlockadder" + id="cw-tools-blockadder" class="cw-tools cw-tools-blockadder" role="tabpanel" - :aria-labelledby="$gettext('Blöcke')" + aria-labelledby="cw-tools-element-adder-tablist-blockadder" > <courseware-collapsible-box :title="textBlockHelper"> <courseware-block-helper :blockTypes="blockTypes" /> @@ -115,9 +118,10 @@ <div v-show="showContaineradder" + id="cw-tools-containeradder" class="cw-tools cw-tools-containeradder" role="tabpanel" - :aria-labelledby="$gettext('Abschnitte')" + aria-labelledby="cw-tools-element-adder-tablist-containeradder" > <courseware-collapsible-box v-for="(style, index) in containerStyles" -- GitLab From 2e35a0c1a21a86ad562f15cd4979cb90248b5e6f Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Fri, 28 Jan 2022 15:21:01 +0100 Subject: [PATCH 26/34] remove tabindex of cw-tab tabpanel --- resources/vue/components/courseware/CoursewareTab.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/vue/components/courseware/CoursewareTab.vue b/resources/vue/components/courseware/CoursewareTab.vue index e307e6d1c37..95d80aad5d2 100755 --- a/resources/vue/components/courseware/CoursewareTab.vue +++ b/resources/vue/components/courseware/CoursewareTab.vue @@ -1,6 +1,5 @@ <template> <div - tabindex="0" role="tabpanel" class="cw-tab" :id="name + '-tab'" -- GitLab From a96690a72db64ff9690d93dd070e77a48a5c6e22 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Thu, 3 Feb 2022 09:43:46 +0100 Subject: [PATCH 27/34] fix misspell --- .../vue/components/courseware/CoursewareManagerElement.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/vue/components/courseware/CoursewareManagerElement.vue b/resources/vue/components/courseware/CoursewareManagerElement.vue index 5a7f215f895..8167a287c19 100755 --- a/resources/vue/components/courseware/CoursewareManagerElement.vue +++ b/resources/vue/components/courseware/CoursewareManagerElement.vue @@ -19,7 +19,7 @@ <header> <a v-if="elementInserterActive && moveSelfPossible && canEdit" - herf="#" + href="#" :title="$gettextInterpolate('%{ elementTitle } verschieben', {elementTitle: elementTitle})" @click="insertElement({element: currentElement, source: type})" > -- GitLab From 3169b83077798c0f7abaa8463573a87f00f3cd83 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Thu, 3 Feb 2022 09:56:12 +0100 Subject: [PATCH 28/34] fix tabpanel id --- resources/vue/components/courseware/CoursewareTab.vue | 7 +++++-- resources/vue/components/courseware/CoursewareTabs.vue | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareTab.vue b/resources/vue/components/courseware/CoursewareTab.vue index 95d80aad5d2..42fd7644dad 100755 --- a/resources/vue/components/courseware/CoursewareTab.vue +++ b/resources/vue/components/courseware/CoursewareTab.vue @@ -2,7 +2,7 @@ <div role="tabpanel" class="cw-tab" - :id="name + '-tab'" + :id="id" :class="{ 'cw-tab-active': isActive }" :aria-labelledby="href" > @@ -26,7 +26,10 @@ export default { }, computed: { href() { - return '#' +this.index + '-' +this.name.toLowerCase().replace(/ /g, '-'); + return '#' +this.index + '-' + this.name.toLowerCase().replace(/ /g, '-'); + }, + id() { + return this.index + '-' + this.name.toLowerCase().replace(/ /g, '-') + '-tabpanel'; }, }, mounted() { diff --git a/resources/vue/components/courseware/CoursewareTabs.vue b/resources/vue/components/courseware/CoursewareTabs.vue index 26923bb1f30..fcfd0f66711 100755 --- a/resources/vue/components/courseware/CoursewareTabs.vue +++ b/resources/vue/components/courseware/CoursewareTabs.vue @@ -10,7 +10,7 @@ tab.icon !== '' && tab.name === '' ? 'cw-tabs-nav-icon-solo-' + tab.icon : '', ]" :aria-selected="tab.isActive" - :aria-controls="tab.name + '-tab'" + :aria-controls="tab.id" :id="tab.href" :href="tab.href" :tabindex="tab.isActive ? 0 : -1" -- GitLab From 297c792994f008c1e87d4b748abc31ef9317de4a Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Thu, 3 Feb 2022 11:07:55 +0100 Subject: [PATCH 29/34] set aria-controls if tabpanel is part of the dom --- .../vue/components/courseware/CoursewareRibbonToolbar.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue index 94c777aac5d..d45b18e6ded 100755 --- a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue +++ b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue @@ -12,7 +12,7 @@ id="cw-ribbon-tool-content-tablist-contents" :class="{ active: showContents }" :aria-selected="showContents" - aria-controls="cw-ribbon-tool-contents" + :aria-controls="showContents ? 'cw-ribbon-tool-contents' : ''" :tabindex="showContents ? 0 : -1" ref="contents" @click="displayTool('contents')" @@ -25,7 +25,7 @@ id="cw-ribbon-tool-content-tablist-blockadder" :class="{ active: showBlockAdder }" :aria-selected="showBlockAdder" - aria-controls="cw-ribbon-tool-blockadder" + :aria-controls="showBlockAdder ? 'cw-ribbon-tool-blockadder' : ''" :tabindex="showBlockAdder ? 0 : -1" ref="blockadder" @click="displayTool('blockadder')" @@ -38,7 +38,7 @@ id="cw-ribbon-tool-content-tablist-admin" :class="{ active: showAdmin }" :aria-selected="showAdmin" - aria-controls="cw-ribbon-tool-admin" + :aria-controls="showAdmin ? 'cw-ribbon-tool-admin': ''" :tabindex="showAdmin ? 0 : -1" ref="admin" @click="displayTool('admin')" -- GitLab From 20eeb4266bbdb7bc1a641bd27bec93a5189d5b7a Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Fri, 4 Feb 2022 15:44:24 +0100 Subject: [PATCH 30/34] there is only one type of courseware tabs --- .../assets/stylesheets/scss/courseware.scss | 141 ++++------ .../courseware/CoursewareActionWidget.vue | 4 +- .../courseware/CoursewareBlockAdderArea.vue | 1 + .../courseware/CoursewareDialogCardsBlock.vue | 2 +- .../courseware/CoursewareEmptyElementBox.vue | 1 + .../courseware/CoursewareRibbon.vue | 1 + .../courseware/CoursewareRibbonToolbar.vue | 261 +++++------------- .../components/courseware/CoursewareTab.vue | 10 +- .../components/courseware/CoursewareTabs.vue | 85 ++++-- .../courseware/CoursewareToolsBlockadder.vue | 242 ++++++---------- .../courseware/CoursewareViewWidget.vue | 25 +- .../vue/store/courseware/courseware.module.js | 12 + 12 files changed, 319 insertions(+), 466 deletions(-) diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss index f4d01902960..1ff8d9351f9 100755 --- a/resources/assets/stylesheets/scss/courseware.scss +++ b/resources/assets/stylesheets/scss/courseware.scss @@ -423,20 +423,21 @@ $consum_ribbon_width: calc(100% - 58px); top: 0; background-color: $white; margin: 0; - padding: 10px 0 0 0; + padding: 0; color: $base-color; display: flex; - border-bottom: solid thin $content-color-40; z-index: 43; .cw-tools-hide-button { + position: absolute; border: none; height: 36px; width: 24px; min-width: 24px; margin-right: 1em; padding: 0 4px; - float: right; + right: 0; + top: 12px; @include background-icon(decline, clickable, 24); background-repeat: no-repeat; @@ -445,49 +446,41 @@ $consum_ribbon_width: calc(100% - 58px); background-color: #fff; } - .cw-ribbon-tool-content-tablist { - list-style-type: none; + >.cw-ribbon-tool-content-tablist { width: 100%; - display: flex; - flex-wrap: wrap; - margin: 0; - padding: 0; - - button { - background-color: $white; + >.cw-tabs-nav { border: none; - padding: 0 8px; - margin-top: 8px; - text-align: center; - flex-grow: 0.5; - cursor: pointer; - - &:after { - display: block; - content: ''; - border-bottom: solid 3px $dark-gray-color-75; - transform: scaleX(0); - transition: transform 300ms ease-in-out; - margin: 17px 0 0 0; - } - - &.active, - &:hover { - color: $black; - &:after { - transform: scaleX(1); + width: calc(100% - 48px); + + >button { + padding: 20px 8px 4px 8px; + margin-top: 0; + max-width: unset; + flex-grow: 0.5; + &::after { + margin-top: 16px; } } - } - } - - &:hover button { - &.active::after { - transform: scaleX(0); } - &:hover::after { - transform: scaleX(1); + >.cw-tabs-content { + border: none; + border-top: solid thin $content-color-40; + padding: 0; + + >.cw-tab { + max-height: 700px; + padding: 14px 8px 8px 8px; + overflow-y: auto; + overflow-x: hidden; + scrollbar-width: thin; + scrollbar-color: $base-color $white; + + &.cw-ribbon-tool-blockadder-tab { + overflow: hidden; + padding: 0; + } + } } } } @@ -1606,7 +1599,7 @@ $icons: ( .cw-tabs-content { border: none; min-width: 500px; - min-height: 400px; + min-height: 400px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: $base-color #f5f5f5; @@ -1645,55 +1638,35 @@ b l o c k a d d e r * * * * * * * * * */ .cw-tools-element-adder-tabs { - display: flex; - list-style: none; - padding: 0; - margin: 0; - border: solid thin $content-color-40; - border-bottom: none; - - .cw-tools-element-adder-tab { - background-color: $white; + .cw-tabs-nav { + margin-top: 4px; border: none; - padding: 1em 1.5em 0 1.5em; - margin: 0 1em; - color: $base-color; - cursor: pointer; - text-align: center; - flex-grow: 1; + border-bottom: solid thin $content-color-40; - &:after { - display: block; - margin-top: 4px; - content: ''; - border-bottom: solid 3px $dark-gray-color-75; - transform: scaleX(0); - transition: transform 300ms ease-in-out; + button { + max-width: unset; + padding: 1em 1.5em 4px 1.5em; + margin: 0px 2em; } - - &.active, - &:hover { - color: $black; - &:after { - transform: scaleX(1); - } - } - } - - &:hover .cw-tools-element-adder-tab{ - &.active::after { - transform: scaleX(0); - } - &:hover::after { - transform: scaleX(1); - } + .cw-tabs-content { + border: none; + max-height: 640px; + overflow-x: hidden; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: $base-color #f5f5f5; } -} - -.cw-tools-element-adder { - padding-bottom: 4em; + .cw-collapsible { + .cw-collapsible-content { + visibility: unset; + display: none; + &.cw-collapsible-content-open { + display: block; + } + } + } } .cw-blockadder-item { diff --git a/resources/vue/components/courseware/CoursewareActionWidget.vue b/resources/vue/components/courseware/CoursewareActionWidget.vue index a13b30999d0..ab643047e93 100644 --- a/resources/vue/components/courseware/CoursewareActionWidget.vue +++ b/resources/vue/components/courseware/CoursewareActionWidget.vue @@ -167,7 +167,8 @@ export default { lockObject: 'lockObject', setConsumeMode: 'coursewareConsumeMode', setViewMode: 'coursewareViewMode', - setShowToolbar: 'coursewareShowToolbar' + setShowToolbar: 'coursewareShowToolbar', + setSelectedToolbarItem: 'coursewareSelectedToolbarItem' }), async editElement() { if (this.blockedByAnotherUser) { @@ -216,6 +217,7 @@ export default { }, showConsumeMode() { this.setViewMode('read'); + this.setSelectedToolbarItem('contents'); this.setConsumeMode(true); }, }, diff --git a/resources/vue/components/courseware/CoursewareBlockAdderArea.vue b/resources/vue/components/courseware/CoursewareBlockAdderArea.vue index 5b0ef2b3133..7bc25136e70 100755 --- a/resources/vue/components/courseware/CoursewareBlockAdderArea.vue +++ b/resources/vue/components/courseware/CoursewareBlockAdderArea.vue @@ -42,6 +42,7 @@ export default { } else { this.adderActive = true; this.$store.dispatch('coursewareBlockAdder', { container: this.container, section: this.section }); + this.$store.dispatch('coursewareSelectedToolbarItem', 'blockadder'); this.$store.dispatch('coursewareShowToolbar', true); } }, diff --git a/resources/vue/components/courseware/CoursewareDialogCardsBlock.vue b/resources/vue/components/courseware/CoursewareDialogCardsBlock.vue index 2777f33e297..2256795d4ca 100755 --- a/resources/vue/components/courseware/CoursewareDialogCardsBlock.vue +++ b/resources/vue/components/courseware/CoursewareDialogCardsBlock.vue @@ -58,7 +58,7 @@ <button class="button add" @click="addCard"><translate>Karte hinzufügen</translate></button> <courseware-tabs v-if="currentCards.length > 0" - @selectTab="activateCard(parseInt($event.replace($gettext('Karte') + ' ', '')) - 1)" + @selectTab="activateCard(parseInt($event.name.replace($gettext('Karte') + ' ', '')) - 1)" > <courseware-tab v-for="(card, index) in currentCards" diff --git a/resources/vue/components/courseware/CoursewareEmptyElementBox.vue b/resources/vue/components/courseware/CoursewareEmptyElementBox.vue index 8680a555e2b..b64413d26c8 100755 --- a/resources/vue/components/courseware/CoursewareEmptyElementBox.vue +++ b/resources/vue/components/courseware/CoursewareEmptyElementBox.vue @@ -38,6 +38,7 @@ export default { this.$store.dispatch('coursewareViewMode', 'edit'); this.$store.dispatch('coursewareConsumeMode', false); this.$store.dispatch('coursewareContainerAdder', true); + this.$store.dispatch('coursewareSelectedToolbarItem', 'blockadder'); this.$store.dispatch('coursewareShowToolbar', true); }, switchToEditView() { diff --git a/resources/vue/components/courseware/CoursewareRibbon.vue b/resources/vue/components/courseware/CoursewareRibbon.vue index 6485f19d01c..da6580705f2 100755 --- a/resources/vue/components/courseware/CoursewareRibbon.vue +++ b/resources/vue/components/courseware/CoursewareRibbon.vue @@ -84,6 +84,7 @@ export default { toggleConsumeMode() { if (!this.consumeMode) { this.$store.dispatch('coursewareConsumeMode', true); + this.$store.dispatch('coursewareSelectedToolbarItem', 'contents'); this.$store.dispatch('coursewareViewMode', 'read'); } else { this.$store.dispatch('coursewareConsumeMode', false); diff --git a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue index d45b18e6ded..d06450acf0b 100755 --- a/resources/vue/components/courseware/CoursewareRibbonToolbar.vue +++ b/resources/vue/components/courseware/CoursewareRibbonToolbar.vue @@ -1,5 +1,5 @@ <template> - <focus-trap v-model="trap" :initial-focus="() => $refs.contents"> + <focus-trap v-model="trap" :initial-focus="() => initialFocusElement"> <div class="cw-ribbon-tools" :class="{ unfold: toolsActive, 'cw-ribbon-tools-consume': consumeMode }" @@ -7,83 +7,72 @@ > <div class="cw-ribbon-tool-content"> <div class="cw-ribbon-tool-content-nav"> - <div role="tablist" class="cw-ribbon-tool-content-tablist"> - <button - id="cw-ribbon-tool-content-tablist-contents" - :class="{ active: showContents }" - :aria-selected="showContents" - :aria-controls="showContents ? 'cw-ribbon-tool-contents' : ''" - :tabindex="showContents ? 0 : -1" + <courseware-tabs + class="cw-ribbon-tool-content-tablist" + ref="tabs" + @selectTab="selectTool($event.alias)" + > + <courseware-tab + :name="$gettext('Inhaltsverzeichnis')" + :selected="showContents" + alias="contents" ref="contents" - @click="displayTool('contents')" - @keydown="handleKeyEvent('contents', $event)" + :index="0" > - <translate>Inhaltsverzeichnis</translate> - </button> - <button + <courseware-tools-contents + id="cw-ribbon-tool-contents" + /> + </courseware-tab> + <courseware-tab v-if="!consumeMode && showEditMode && canEdit" - id="cw-ribbon-tool-content-tablist-blockadder" - :class="{ active: showBlockAdder }" - :aria-selected="showBlockAdder" - :aria-controls="showBlockAdder ? 'cw-ribbon-tool-blockadder' : ''" - :tabindex="showBlockAdder ? 0 : -1" - ref="blockadder" - @click="displayTool('blockadder')" - @keydown="handleKeyEvent('blockadder', $event)" + :name="$gettext('Elemente hinzufügen')" + :selected="showBlockAdder" + alias="blockadder" + class="cw-ribbon-tool-blockadder-tab" + :index="1" > - <translate>Elemente hinzufügen</translate> - </button> - <button + <courseware-tools-blockadder + id="cw-ribbon-tool-blockadder" + /> + </courseware-tab> + <courseware-tab v-if="!consumeMode && displaySettings" - id="cw-ribbon-tool-content-tablist-admin" - :class="{ active: showAdmin }" - :aria-selected="showAdmin" - :aria-controls="showAdmin ? 'cw-ribbon-tool-admin': ''" - :tabindex="showAdmin ? 0 : -1" - ref="admin" - @click="displayTool('admin')" - @keydown="handleKeyEvent('admin', $event)" + :name="$gettext('Einstellungen')" + :selected="showAdmin" + alias="admin" + :index="2" > - <translate>Einstellungen</translate> - </button> - </div> - <a href="#" :title="textClose" class="cw-tools-hide-button" @click="$emit('deactivate')"></a> - </div> - <div class="cw-ribbon-tool" ref="ribbonContent" @scroll="handleScroll"> - <courseware-tools-contents - v-if="showContents" - id="cw-ribbon-tool-contents" - role="tabpanel" - aria-labelledby="cw-ribbon-tool-content-tablist-contents" - /> - <courseware-tools-blockadder - v-if="showBlockAdder" - id="cw-ribbon-tool-blockadder" - role="tabpanel" - aria-labelledby="cw-ribbon-tool-content-tablist-blockadder" - @scrollTop="scrollTop('blockadder')" - /> - <courseware-tools-admin - v-if="showAdmin" - id="cw-ribbon-tool-admin" - role="tabpanel" - aria-labelledby="cw-ribbon-tool-content-tablist-admin" - /> + <courseware-tools-admin + id="cw-ribbon-tool-admin" + /> + </courseware-tab> + </courseware-tabs> + <a + href="#" + :title="$gettext('schließen')" + class="cw-tools-hide-button" + ref="closeTools" + @click="$emit('deactivate')"> + </a> </div> </div> </div> </focus-trap> </template> <script> +import CoursewareTabs from './CoursewareTabs.vue'; +import CoursewareTab from './CoursewareTab.vue'; import CoursewareToolsAdmin from './CoursewareToolsAdmin.vue'; import CoursewareToolsBlockadder from './CoursewareToolsBlockadder.vue'; import CoursewareToolsContents from './CoursewareToolsContents.vue'; import { FocusTrap } from 'focus-trap-vue'; -import { mapGetters } from 'vuex'; +import { mapActions, mapGetters } from 'vuex'; export default { name: 'courseware-ribbon-toolbar', components: { + CoursewareTabs, + CoursewareTab, CoursewareToolsAdmin, CoursewareToolsBlockadder, CoursewareToolsContents, @@ -98,13 +87,8 @@ export default { showContents: true, showAdmin: false, showBlockAdder: false, - textClose: this.$gettext('schließen'), - scrollPos: { - contents: 0, - admin: 0, - blockadder: 0 - }, - trap: false + trap: false, + initialFocusElement: null }; }, computed: { @@ -117,6 +101,7 @@ export default { context: 'context', userById: 'users/byId', userId: 'userId', + selectedToolbarItem: 'selectedToolbarItem', }), showEditMode() { return this.viewMode === 'edit'; @@ -130,176 +115,74 @@ export default { }, }, methods: { - displayTool(tool) { + ...mapActions({ + setToolbarItem: 'coursewareSelectedToolbarItem' + }), + selectTool(alias) { this.showContents = false; this.showAdmin = false; this.showBlockAdder = false; - switch (tool) { + switch (alias) { case 'contents': this.showContents = true; - this.$refs.contents.focus(); this.disableContainerAdder(); + this.scrollToCurrent(); break; case 'admin': this.showAdmin = true; - this.$refs.admin.focus(); this.disableContainerAdder(); break; case 'blockadder': this.showBlockAdder = true; - this.$refs.blockadder.focus(); break; } - this.updateScrollPos(); + + if (this.selectedToolbarItem !== alias) { + this.setToolbarItem(alias); + } }, disableContainerAdder() { if (this.containerAdder !== false) { this.$store.dispatch('coursewareContainerAdder', false); } }, - scrollTop(tool) { - switch (tool) { - case 'contents': - this.$set(this.scrollPos, 'contents', 0); - break; - case 'admin': - this.$set(this.scrollPos, 'admin', 0); - break; - case 'blockadder': - this.$set(this.scrollPos, 'blockadder', 0); - break; - } - this.updateScrollPos(); - }, - handleScroll: function() { - if (this.timeout) { - clearTimeout(this.timeout); - } - - this.timeout = setTimeout(() => { - var currentScrollPos = this.$refs.ribbonContent.scrollTop; - if (this.showContents) { - this.$set(this.scrollPos, 'contents', currentScrollPos); - } - if (this.showAdmin) { - this.$set(this.scrollPos, 'admin', currentScrollPos); - } - if (this.showBlockAdder) { - this.$set(this.scrollPos, 'blockadder', currentScrollPos); - } - }, 100); + scrollToCurrent() { + setTimeout(() => { + let contents = this.$refs.contents.$el; + let current = contents.querySelector('.cw-tree-item-link-current'); + contents.scroll({ top: current.offsetTop, behavior: 'smooth' }); + }, 360); }, - updateScrollPos() { - var scrollPos = 0; - if (this.showContents) { - scrollPos = this.scrollPos.contents; - } - if (this.showAdmin) { - scrollPos = this.scrollPos.admin; - } - if (this.showBlockAdder) { - scrollPos = this.scrollPos.blockadder; - } - this.$nextTick(function() { - $(this.$refs.ribbonContent).stop().animate({ - scrollTop: scrollPos - }, 100); - }); - }, - handleKeyEvent(tab, e) { - switch (e.keyCode) { - case 37: // left - case 38: // up - if (tab === 'contents') { - if (!this.consumeMode && this.displaySettings) { - this.displayTool('admin'); - break; - } - if (!this.consumeMode && this.showEditMode && this.canEdit) { - this.displayTool('blockadder'); - break; - } - } - if (tab === 'blockadder') { - this.displayTool('contents'); - break; - } - if (tab === 'admin') { - if (!this.consumeMode && this.showEditMode && this.canEdit) { - this.displayTool('blockadder'); - break; - } - this.displayTool('contents'); - break; - } - break; - case 39: // right - case 40: // down - if (tab === 'contents') { - if (!this.consumeMode && this.showEditMode && this.canEdit) { - this.displayTool('blockadder'); - break; - } - if (!this.consumeMode && this.displaySettings) { - this.displayTool('admin'); - break; - } - } - if (tab === 'blockadder') { - if (!this.consumeMode && this.displaySettings) { - this.displayTool('admin'); - break; - } - this.displayTool('contents'); - break; - } - if (tab === 'admin') { - this.displayTool('contents'); - } - break; - case 36: //pos1 - this.displayTool('contents'); - break; - case 35: //end - if (!this.consumeMode && this.displaySettings) { - this.displayTool('admin'); - break; - } - if (!this.consumeMode && this.showEditMode && this.canEdit) { - this.displayTool('blockadder'); - break; - } - } - } }, mounted () { - this.updateScrollPos(); + this.selectTool(this.selectedToolbarItem); }, watch: { adderStorage(newValue) { if (Object.keys(newValue).length !== 0) { - this.displayTool('blockadder'); + this.selectTool('blockadder'); } }, consumeMode(newValue) { if (newValue) { - this.displayTool('contents'); + this.selectTool('contents'); } }, containerAdder(newValue) { if (newValue === true) { - this.displayTool('blockadder'); + this.selectTool('blockadder'); } }, showEditMode(newValue) { if (!newValue) { - this.displayTool('contents'); + this.selectTool('contents'); } }, toolsActive(newValue) { if (newValue) { setTimeout(() => { + this.initialFocusElement = this.$refs.tabs.getTabButtonByAlias(this.selectedToolbarItem); this.trap = true; }, 300); } diff --git a/resources/vue/components/courseware/CoursewareTab.vue b/resources/vue/components/courseware/CoursewareTab.vue index 42fd7644dad..5c5217283f8 100755 --- a/resources/vue/components/courseware/CoursewareTab.vue +++ b/resources/vue/components/courseware/CoursewareTab.vue @@ -4,7 +4,7 @@ class="cw-tab" :id="id" :class="{ 'cw-tab-active': isActive }" - :aria-labelledby="href" + :aria-labelledby="selectorId" > <slot></slot> </div> @@ -15,6 +15,7 @@ export default { name: 'courseware-tab', props: { name: {type: String, required: true }, + alias: {type: String, default: ''}, selected: { type: Boolean, default: false }, index: {type: Number, required: true }, icon: {type: String, default: ''}, @@ -25,7 +26,7 @@ export default { }; }, computed: { - href() { + selectorId() { return '#' +this.index + '-' + this.name.toLowerCase().replace(/ /g, '-'); }, id() { @@ -35,5 +36,10 @@ export default { mounted() { this.isActive = this.selected; }, + watch: { + selected(newValue) { + this.isActive = newValue; + } + } }; </script> diff --git a/resources/vue/components/courseware/CoursewareTabs.vue b/resources/vue/components/courseware/CoursewareTabs.vue index fcfd0f66711..db2d7cca646 100755 --- a/resources/vue/components/courseware/CoursewareTabs.vue +++ b/resources/vue/components/courseware/CoursewareTabs.vue @@ -2,7 +2,7 @@ <div class="cw-tabs"> <div role="tablist" class="cw-tabs-nav"> <button - v-for="(tab, index) in tabs" + v-for="(tab, index) in sortedTabs" :key="index" :class="[ tab.isActive ? 'is-active' : '', @@ -11,12 +11,11 @@ ]" :aria-selected="tab.isActive" :aria-controls="tab.id" - :id="tab.href" - :href="tab.href" + :id="tab.selectorId" :tabindex="tab.isActive ? 0 : -1" - @click="selectTab(tab)" - @keydown="handleKeyEvent(tab, $event)" - :ref="tab.href" + @click="selectTab(tab.selectorId)" + @keydown="handleKeyEvent($event)" + :ref="tab.selectorId" > {{ tab.name }} </button> @@ -35,44 +34,86 @@ export default { tabs: [], }; }, + computed: { + sortedTabs() { + return this.tabs.sort((a, b) => { + return a.index > b.index ? 1 : a.index < b.index ? -1 : 0; + }); + } + }, created() { this.tabs = this.$children; }, methods: { - selectTab(selectedTab) { + selectTab(selectorId) { let view = this; this.tabs.forEach((tab) => { - tab.isActive = tab.index + '-' + tab.name === selectedTab.index + '-' + selectedTab.name; - view.$refs[selectedTab.href][0].focus(); + tab.isActive = false; + if (tab.selectorId === selectorId) { + tab.isActive = true; + view.$refs[selectorId][0].focus(); + view.$emit('selectTab', tab); + } }); - this.$emit('selectTab', selectedTab.name); }, - handleKeyEvent(tab, e) { + handleKeyEvent(e) { + let tablist = e.target.parentElement; switch (e.keyCode) { case 37: // left case 38: // up - if (tab.index > 0) { - this.selectTab(this.tabs[tab.index - 1]); + if (tablist.firstChild.id !== e.target.id) { + this.selectTab(e.target.previousElementSibling.id); } else { - this.selectTab(this.tabs[this.tabs.length - 1]); + this.selectTab(tablist.lastChild.id); } break; case 39: // right case 40: // down - if (tab.index < this.tabs.length - 1) { - this.selectTab(this.tabs[tab.index + 1]); - } else { - this.selectTab(this.tabs[0]); - } + if (tablist.lastChild.id !== e.target.id) { + this.selectTab(e.target.nextElementSibling.id); + } else { + this.selectTab(tablist.firstChild.id); + } break; case 36: //pos1 - this.selectTab(this.tabs[0]); + this.selectTab(tablist.firstChild.id); break; case 35: //end - this.selectTab(this.tabs[this.tabs.length - 1]); + this.selectTab(tablist.lastChild.id); break; } + }, + getButtonById(id) { + return this.$refs[id][0]; + }, + getFirstTabButton() { + let selectorId = this.sortedTabs[0].selectorId; + return this.$refs[selectorId][0]; + }, + getTabButtonByName(name) { + let selectorId = null; + this.tabs.forEach((tab) => { + if (tab.name === name) { + selectorId = tab.selectorId; + } + }); + if (selectorId) { + return this.$refs[selectorId][0]; + } + return null; + }, + getTabButtonByAlias(alias) { + let selectorId = null; + this.tabs.forEach((tab) => { + if (tab.alias === alias) { + selectorId = tab.selectorId; + } + }); + if (selectorId) { + return this.$refs[selectorId][0]; + } + return null; } - }, + } }; </script> diff --git a/resources/vue/components/courseware/CoursewareToolsBlockadder.vue b/resources/vue/components/courseware/CoursewareToolsBlockadder.vue index a60cb0129c9..929c5a4c731 100755 --- a/resources/vue/components/courseware/CoursewareToolsBlockadder.vue +++ b/resources/vue/components/courseware/CoursewareToolsBlockadder.vue @@ -1,63 +1,54 @@ <template> <div class="cw-tools-element-adder"> - <div role="tablist" class="cw-tools-element-adder-tabs"> - <button - id="cw-tools-element-adder-tablist-blockadder" - :class="{ 'active': showBlockadder }" - class="cw-tools-element-adder-tab" - :aria-selected="showBlockadder" - aria-controls="cw-tools-blockadder" - :tabindex="showBlockadder ? 0 : -1" - ref="blockadder" - @click="displayBlockAdder" - @keydown="handleKeyEvent('block', $event)" - > - <translate>Blöcke</translate> - </button> - <button - id="cw-tools-element-adder-tablist-containeradder" - :class="{ 'active': showContaineradder }" - class="cw-tools-element-adder-tab" - :aria-selected="showContaineradder" - aria-controls="cw-tools-containeradder" - :tabindex="showContaineradder ? 0 : -1" - ref="containeradder" - @click="displayContainerAdder" - @keydown="handleKeyEvent('container', $event)" - > - <translate>Abschnitte</translate> - </button> - </div> - - <div - v-show="showBlockadder" - id="cw-tools-blockadder" - class="cw-tools cw-tools-blockadder" - role="tabpanel" - aria-labelledby="cw-tools-element-adder-tablist-blockadder" - > - <courseware-collapsible-box :title="textBlockHelper"> - <courseware-block-helper :blockTypes="blockTypes" /> - </courseware-collapsible-box> - - <courseware-collapsible-box :title="textAdderFavs" :open="favoriteBlockTypes.length > 0"> - <div class="cw-element-adder-wrapper" v-if="!showEditFavs"> - <courseware-companion-box - v-if="favoriteBlockTypes.length === 0" - mood="sad" - :msgCompanion="textFavsEmpty" - /> - <courseware-blockadder-item - v-for="(block, index) in favoriteBlockTypes" - :key="index" - :title="block.title" - :icon="block.icon" - :type="block.type" - :description="block.description" - /> - </div> - - <div class="cw-element-adder-favs-wrapper" v-if="showEditFavs"> + <courseware-tabs class="cw-tools-element-adder-tabs"> + <courseware-tab :name="$gettext('Blöcke')" :selected="showBlockadder" :index="0"> + <courseware-collapsible-box :title="textBlockHelper"> + <courseware-block-helper :blockTypes="blockTypes" /> + </courseware-collapsible-box> + <courseware-collapsible-box :title="textAdderFavs" :open="favoriteBlockTypes.length > 0"> + <div class="cw-element-adder-wrapper" v-if="!showEditFavs"> + <courseware-companion-box + v-if="favoriteBlockTypes.length === 0" + mood="sad" + :msgCompanion="textFavsEmpty" + /> + <courseware-blockadder-item + v-for="(block, index) in favoriteBlockTypes" + :key="index" + :title="block.title" + :icon="block.icon" + :type="block.type" + :description="block.description" + /> + </div> + <div class="cw-element-adder-favs-wrapper" v-if="showEditFavs"> + <div class="cw-element-adder-all-blocks" :class="{ 'fav-edit-active': showEditFavs }"> + <courseware-blockadder-item + v-for="(block, index) in blockTypes" + :key="index" + :title="block.title" + :type="block.type" + :description="block.description" + /> + </div> + <div class="cw-element-adder-favs"> + <div + v-for="(block, index) in blockTypes" + :key="'fav-item-' + index" + class="cw-block-fav-item" + :class="[isBlockFav(block) ? 'cw-block-fav-item-active' : '']" + @click="toggleFavItem(block)" + ></div> + </div> + </div> + <button v-show="!showEditFavs" class="button" @click="showEditFavs = true"> + <translate>Favoriten bearbeiten</translate> + </button> + <button v-show="showEditFavs" class="button" @click="endEditFavs"> + <translate>Favoriten bearbeiten schließen</translate> + </button> + </courseware-collapsible-box> + <courseware-collapsible-box :title="textAdderAll"> <div class="cw-element-adder-all-blocks" :class="{ 'fav-edit-active': showEditFavs }"> <courseware-blockadder-item v-for="(block, index) in blockTypes" @@ -67,84 +58,50 @@ :description="block.description" /> </div> - <div class="cw-element-adder-favs"> - <div - v-for="(block, index) in blockTypes" - :key="'fav-item-' + index" - class="cw-block-fav-item" - :class="[isBlockFav(block) ? 'cw-block-fav-item-active' : '']" - @click="toggleFavItem(block)" - ></div> + </courseware-collapsible-box> + <courseware-collapsible-box + v-for="(category, index) in blockCategories" + :key="index" + :title="category.title" + :open="category.type === 'basis' && favoriteBlockTypes.length === 0" + > + <div v-for="(block, index) in blockTypes" :key="index"> + <courseware-blockadder-item + v-if="block.categories.includes(category.type)" + :title="block.title" + :icon="block.icon" + :type="block.type" + :description="block.description" + /> </div> - </div> - - <button v-show="!showEditFavs" class="button" @click="showEditFavs = true"> - <translate>Favoriten bearbeiten</translate> - </button> - <button v-show="showEditFavs" class="button" @click="endEditFavs"> - <translate>Favoriten bearbeiten schließen</translate> - </button> - </courseware-collapsible-box> - - <courseware-collapsible-box :title="textAdderAll"> - <div class="cw-element-adder-all-blocks" :class="{ 'fav-edit-active': showEditFavs }"> - <courseware-blockadder-item - v-for="(block, index) in blockTypes" - :key="index" - :title="block.title" - :type="block.type" - :description="block.description" - /> - </div> - </courseware-collapsible-box> - - <courseware-collapsible-box - v-for="(category, index) in blockCategories" - :key="index" - :title="category.title" - :open="category.type === 'basis' && favoriteBlockTypes.length === 0" - > - <div v-for="(block, index) in blockTypes" :key="index"> - <courseware-blockadder-item - v-if="block.categories.includes(category.type)" - :title="block.title" - :icon="block.icon" - :type="block.type" - :description="block.description" - /> - </div> - </courseware-collapsible-box> - </div> - - <div - v-show="showContaineradder" - id="cw-tools-containeradder" - class="cw-tools cw-tools-containeradder" - role="tabpanel" - aria-labelledby="cw-tools-element-adder-tablist-containeradder" - > - <courseware-collapsible-box - v-for="(style, index) in containerStyles" - :key="index" - :title="style.title" - :open="index === 0" - > - <courseware-container-adder-item - v-for="(container, index) in containerTypes" + </courseware-collapsible-box> + </courseware-tab> + <courseware-tab :name="$gettext('Abschnitte')" :selected="showContaineradder" :index="1"> + <courseware-collapsible-box + v-for="(style, index) in containerStyles" :key="index" - :title="container.title" - :type="container.type" - :colspan="style.colspan" - :description="container.description" - :firstSection="$gettext('erstes Element')" - :secondSection="$gettext('zweites Element')" - ></courseware-container-adder-item> - </courseware-collapsible-box> - </div> + :title="style.title" + :open="index === 0" + > + <courseware-container-adder-item + v-for="(container, index) in containerTypes" + :key="index" + :title="container.title" + :type="container.type" + :colspan="style.colspan" + :description="container.description" + :firstSection="$gettext('erstes Element')" + :secondSection="$gettext('zweites Element')" + ></courseware-container-adder-item> + </courseware-collapsible-box> + </courseware-tab> + </courseware-tabs> </div> </template> <script> +import CoursewareTabs from './CoursewareTabs.vue'; +import CoursewareTab from './CoursewareTab.vue'; import CoursewareCollapsibleBox from './CoursewareCollapsibleBox.vue'; import CoursewareBlockadderItem from './CoursewareBlockadderItem.vue'; import CoursewareContainerAdderItem from './CoursewareContainerAdderItem.vue'; @@ -155,6 +112,8 @@ import CoursewareCompanionBox from './CoursewareCompanionBox.vue'; export default { name: 'cw-tools-blockadder', components: { + CoursewareTabs, + CoursewareTab, CoursewareCollapsibleBox, CoursewareBlockadderItem, CoursewareContainerAdderItem, @@ -211,13 +170,11 @@ export default { displayContainerAdder() { this.showContaineradder = true; this.showBlockadder = false; - this.$refs.containeradder.focus(); }, displayBlockAdder() { this.showContaineradder = false; this.showBlockadder = true; this.disableContainerAdder(); - this.$refs.blockadder.focus(); }, toggleFavItem(block) { if (this.isBlockFav(block)) { @@ -243,27 +200,6 @@ export default { this.showEditFavs = false; this.$emit('scrollTop'); }, - handleKeyEvent(tab, e) { - switch (e.keyCode) { - case 37: // left - case 40: // down - case 39: // right - case 38: // up - if (tab === 'block') { - this.displayContainerAdder(); - } - if (tab === 'container') { - this.displayBlockAdder(); - } - break; - case 36: //pos1 - this.displayBlockAdder(); - break; - case 35: //end - this.displayContainerAdder(); - break; - } - } }, mounted() { if (this.containerAdder === true) { diff --git a/resources/vue/components/courseware/CoursewareViewWidget.vue b/resources/vue/components/courseware/CoursewareViewWidget.vue index 9717d2d65d0..61fd77b554c 100755 --- a/resources/vue/components/courseware/CoursewareViewWidget.vue +++ b/resources/vue/components/courseware/CoursewareViewWidget.vue @@ -14,9 +14,9 @@ v-if="context.type === 'courses' && canVisit" :class="{ active: discussView }" > - <div @click="setDiscussView" @keydown.enter="setDiscussView" tabindex="0"> + <a href="#" @click="setDiscussView"> <translate>Diskutieren</translate> - </div> + </a> </li> </ul> </template> @@ -40,24 +40,21 @@ export default { }, discussView() { return this.viewMode === 'discuss'; - }, - canEdit() { - if (!this.structuralElement) { - return false; - } - return this.structuralElement.attributes['can-edit']; - }, + } }, methods: { - ...mapActions( - ['coursewareBlockAdder'] - ), + ...mapActions({ + coursewareViewMode: 'coursewareViewMode', + coursewareBlockAdder: 'coursewareBlockAdder', + setToolbarItem: 'coursewareSelectedToolbarItem', + }), setReadView() { - this.$store.dispatch('coursewareViewMode', 'read'); + this.coursewareViewMode('read'); + this.setToolbarItem('contents'); this.coursewareBlockAdder({}); }, setEditView() { - this.$store.dispatch('coursewareViewMode', 'edit'); + this.coursewareViewMode('edit'); }, setDiscussView() { this.$store.dispatch('coursewareViewMode', 'discuss'); diff --git a/resources/vue/store/courseware/courseware.module.js b/resources/vue/store/courseware/courseware.module.js index dd3c61bbbb1..dd806598117 100755 --- a/resources/vue/store/courseware/courseware.module.js +++ b/resources/vue/store/courseware/courseware.module.js @@ -20,6 +20,7 @@ const getDefaultState = () => { pluginManager: null, showCompanionOverlay: false, showToolbar: false, + selectedToolbarItem: 'contents', urlHelper: null, userId: null, viewMode: 'read', @@ -101,6 +102,9 @@ const getters = { showToolbar(state) { return state.showToolbar; }, + selectedToolbarItem(state) { + return state.selectedToolbarItem; + }, blockAdder(state) { return state.blockAdder; }, @@ -719,6 +723,10 @@ export const actions = { context.commit('coursewareShowToolbarSet', toolbar); }, + coursewareSelectedToolbarItem(context, item) { + context.commit('coursewareSelectedToolbarItemSet', item); + }, + coursewareBlockAdder(context, adder) { context.commit('coursewareBlockAdderSet', adder); }, @@ -1160,6 +1168,10 @@ export const mutations = { state.showToolbar = data; }, + coursewareSelectedToolbarItemSet(state, data) { + state.selectedToolbarItem = data; + }, + coursewareBlockAdderSet(state, data) { state.blockAdder = data; }, -- GitLab From 4fb662e7e721d6351fb892ae2dfa6f2864d22c32 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Thu, 17 Feb 2022 11:07:14 +0100 Subject: [PATCH 31/34] add missing lines of code --- .../courseware/CoursewareActionWidget.vue | 12 ++++++---- .../CoursewareStructuralElement.vue | 23 +++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareActionWidget.vue b/resources/vue/components/courseware/CoursewareActionWidget.vue index ab643047e93..2c98a3ba22f 100644 --- a/resources/vue/components/courseware/CoursewareActionWidget.vue +++ b/resources/vue/components/courseware/CoursewareActionWidget.vue @@ -16,9 +16,9 @@ </a> </li> <li v-if="canEdit" class="cw-action-widget-sort"> - <div @click="sortContainers" @keydown.enter="sortContainers" tabindex="0"> + <a href="#" @click="sortContainers"> <translate>Abschnitte sortieren</translate> - </div> + </a> </li> <li v-if="canEdit" class="cw-action-widget-add"> <a href="#" @click="addElement"> @@ -40,6 +40,11 @@ <translate>Seite exportieren</translate> </a> </li> + <li v-if="(canEdit || userIsTeacher) && canVisit" class="cw-action-widget-export-pdf"> + <a :href="pdfExportURL"> + <translate>Seite als pdf-Dokument exportieren</translate> + </a> + </li> <li v-if="canEdit && oerEnabled" class="cw-action-widget-oer"> <a href="#" @click="oerElement"> <translate>Seite auf %{oerTitle} veröffentlichen</translate> @@ -74,9 +79,6 @@ export default { consumeMode: 'consumeMode', showToolbar: 'showToolbar', userIsTeacher: 'userIsTeacher', - consumeMode: 'consumeMode', - showToolbar: 'showToolbar', - userIsTeacher: 'userIsTeacher', }), isRoot() { if (!this.structuralElement) { diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue index c24ff9aff93..d118a2fd395 100755 --- a/resources/vue/components/courseware/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue @@ -394,6 +394,29 @@ </div> </div> + <courseware-companion-box + v-show="exportRunning" + :msgCompanion="$gettext('Export läuft, bitte haben sie einen Moment Geduld...')" + mood="pointing" + /> + <div v-show="exportRunning" class="cw-import-zip"> + <header>{{ exportState }}:</header> + <div class="progress-bar-wrapper"> + <div + class="progress-bar" + role="progressbar" + :style="{ width: exportProgress + '%' }" + :aria-valuenow="exportProgress" + aria-valuemin="0" + aria-valuemax="100" + > + {{ exportProgress }}% + </div> + </div> + </div> + </template> + </studip-dialog> + <studip-dialog v-if="showOerDialog" height="600" -- GitLab From a7f1c2ac1a2fcd88d399c8e38fc6aa73ce8c1ecb Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Fri, 18 Feb 2022 08:42:02 +0100 Subject: [PATCH 32/34] fixes according to review --- resources/vue/components/StudipDialog.vue | 2 +- .../courseware/CoursewareAdminTemplates.vue | 12 +++---- .../CoursewareContentOverviewElements.vue | 6 ++-- .../CoursewareDashboardStudents.vue | 12 +++---- .../courseware/CoursewareDefaultContainer.vue | 4 +-- .../courseware/CoursewareListContainer.vue | 2 +- .../CoursewareStructuralElement.vue | 32 ++++++------------- .../courseware/CoursewareTabsContainer.vue | 2 +- 8 files changed, 30 insertions(+), 42 deletions(-) diff --git a/resources/vue/components/StudipDialog.vue b/resources/vue/components/StudipDialog.vue index b248bb64aab..1dabe6ecf6c 100755 --- a/resources/vue/components/StudipDialog.vue +++ b/resources/vue/components/StudipDialog.vue @@ -30,7 +30,7 @@ :class="{ 'studip-dialog-warning': question, 'studip-dialog-alert': alert }" class="studip-dialog-body" role="dialog" - :aria-modal="'true'" + aria-modal="true" :aria-labelledby="dialogTitleId" :aria-describedby="dialogDescId" ref="dialog" diff --git a/resources/vue/components/courseware/CoursewareAdminTemplates.vue b/resources/vue/components/courseware/CoursewareAdminTemplates.vue index c237ebb6491..b3f330c708f 100755 --- a/resources/vue/components/courseware/CoursewareAdminTemplates.vue +++ b/resources/vue/components/courseware/CoursewareAdminTemplates.vue @@ -28,10 +28,10 @@ <studip-dialog v-if="showAddTemplateDialog" :title="$gettext('Vorlage hinzufügen')" - :confirmText="'Erstellen'" - :confirmClass="'accept'" + :confirmText="$gettext('Erstellen')" + confirmClass="accept" :closeText="$gettext('Schließen')" - :closeClass="'cancel'" + closeClass="cancel" class="cw-admin-template-dialog" height="360" @close="closeAddDialog" @@ -73,10 +73,10 @@ <studip-dialog v-if="showEditTemplateDialog" :title="$gettext('Vorlage bearbeiten')" - :confirmText="'Speichern'" - :confirmClass="'accept'" + :confirmText="$gettext('Speichern')" + confirmClass="accept" :closeText="$gettext('Schließen')" - :closeClass="'cancel'" + closeClass="cancel" class="cw-admin-template-dialog" @close="closeEditDialog" @confirm="updateCurrentTemplate" diff --git a/resources/vue/components/courseware/CoursewareContentOverviewElements.vue b/resources/vue/components/courseware/CoursewareContentOverviewElements.vue index 75bc3c76975..fb98985ab2d 100755 --- a/resources/vue/components/courseware/CoursewareContentOverviewElements.vue +++ b/resources/vue/components/courseware/CoursewareContentOverviewElements.vue @@ -52,10 +52,10 @@ :title="$gettext('Neues Lernmaterial anlegen')" height="600" width="500" - :confirmText="'Erstellen'" - :confirmClass="'accept'" + :confirmText="$gettext('Erstellen')" + confirmClass="accept" :closeText="$gettext('Schließen')" - :closeClass="'cancel'" + closeClass="cancel" class="cw-structural-element-dialog" @close="closeAddDialog" @confirm="createElement" diff --git a/resources/vue/components/courseware/CoursewareDashboardStudents.vue b/resources/vue/components/courseware/CoursewareDashboardStudents.vue index 4e49327716b..6498b432f27 100755 --- a/resources/vue/components/courseware/CoursewareDashboardStudents.vue +++ b/resources/vue/components/courseware/CoursewareDashboardStudents.vue @@ -124,9 +124,9 @@ v-if="showRenewalDialog" :title="text.renewalDialog.title" :confirmText="text.renewalDialog.confirm" - :confirmClass="'accept'" + confirmClass="accept" :closeText="text.renewalDialog.close" - :closeClass="'cancel'" + closeClass="cancel" height="350" @close=" showRenewalDialog = false; @@ -158,9 +158,9 @@ v-if="showEditFeedbackDialog" :title="text.editFeedbackDialog.title" :confirmText="text.editFeedbackDialog.confirm" - :confirmClass="'accept'" + confirmClass="accept" :closeText="text.editFeedbackDialog.close" - :closeClass="'cancel'" + closeClass="cancel" height="420" @close=" showEditFeedbackDialog = false; @@ -188,9 +188,9 @@ v-if="showAddFeedbackDialog" :title="text.addFeedbackDialog.title" :confirmText="text.addFeedbackDialog.confirm" - :confirmClass="'accept'" + confirmClass="accept" :closeText="text.addFeedbackDialog.close" - :closeClass="'cancel'" + closeClass="cancel" @close=" showAddFeedbackDialog = false; currentDialogFeedback = {}; diff --git a/resources/vue/components/courseware/CoursewareDefaultContainer.vue b/resources/vue/components/courseware/CoursewareDefaultContainer.vue index edc46207e01..b1c370c5cca 100755 --- a/resources/vue/components/courseware/CoursewareDefaultContainer.vue +++ b/resources/vue/components/courseware/CoursewareDefaultContainer.vue @@ -22,9 +22,9 @@ v-if="showEditDialog" :title="textEditTitle" :confirmText="textEditConfirm" - :confirmClass="'accept'" + confirmClass="accept" :closeText="textEditClose" - :closeClass="'cancel'" + closeClass="cancel" @close="closeEdit" @confirm="storeContainer" height="400" diff --git a/resources/vue/components/courseware/CoursewareListContainer.vue b/resources/vue/components/courseware/CoursewareListContainer.vue index 8e8506aedc1..69fc69f4584 100755 --- a/resources/vue/components/courseware/CoursewareListContainer.vue +++ b/resources/vue/components/courseware/CoursewareListContainer.vue @@ -1,7 +1,7 @@ <template> <courseware-default-container :container="container" - :containerClass="'cw-container-list'" + containerClass="cw-container-list" :canEdit="canEdit" :isTeacher="isTeacher" @storeContainer="storeContainer" diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue index d118a2fd395..3cb13cb3586 100755 --- a/resources/vue/components/courseware/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue @@ -146,9 +146,9 @@ v-if="showEditDialog" :title="textEdit.title" :confirmText="textEdit.confirm" - :confirmClass="'accept'" + confirmClass="accept" :closeText="textEdit.close" - :closeClass="'cancel'" + closeClass="cancel" height="500" width="500" class="studip-dialog-with-tab" @@ -276,18 +276,6 @@ @updateReadApproval="updateReadApproval" @updateWriteApproval="updateWriteApproval" /> - <!-- <h1> - <translate>Lehrende in Stud.IP</translate> - </h1> - <label> - <input - type="checkbox" - class="default" - value="copy_approval" - v-model="currentElement.attributes['copy-approval']" - /> - <translate>Seite zum kopieren für Lehrende freigeben</translate> - </label> --> </courseware-tab> <courseware-tab v-if="inCourse" :name="textEdit.visible" :index="4"> <form class="default" @submit.prevent=""> @@ -308,10 +296,10 @@ <studip-dialog v-if="showAddDialog" :title="$gettext('Seite hinzufügen')" - :confirmText="'Erstellen'" - :confirmClass="'accept'" + :confirmText="$gettext('Erstellen')" + confirmClass="accept" :closeText="$gettext('Schließen')" - :closeClass="'cancel'" + closeClass="cancel" class="cw-structural-element-dialog" @close="closeAddDialog" @confirm="createElement" @@ -339,7 +327,7 @@ v-if="showInfoDialog" :title="textInfo.title" :closeText="textInfo.close" - :closeClass="'cancel'" + closeClass="cancel" @close="showElementInfoDialog(false)" > <template v-slot:dialogContent> @@ -376,9 +364,9 @@ v-if="showExportDialog" :title="textExport.title" :confirmText="textExport.confirm" - :confirmClass="'accept'" + confirmClass="accept" :closeText="textExport.close" - :closeClass="'cancel'" + closeClass="cancel" height="350" @close="showElementExportDialog(false)" @confirm="exportCurrentElement" @@ -423,9 +411,9 @@ width="600" :title="textOer.title" :confirmText="textOer.confirm" - :confirmClass="'accept'" + confirmClass="accept" :closeText="textOer.close" - :closeClass="'cancel'" + closeClass="cancel" @close="showElementOerDialog(false)" @confirm="publishCurrentElement" > diff --git a/resources/vue/components/courseware/CoursewareTabsContainer.vue b/resources/vue/components/courseware/CoursewareTabsContainer.vue index 66c66926515..328eee1c482 100755 --- a/resources/vue/components/courseware/CoursewareTabsContainer.vue +++ b/resources/vue/components/courseware/CoursewareTabsContainer.vue @@ -1,7 +1,7 @@ <template> <courseware-default-container :container="container" - :containerClass="'cw-container-tabs'" + containerClass="cw-container-tabs" :canEdit="canEdit" :isTeacher="isTeacher" @storeContainer="storeContainer" -- GitLab From f326e78ff8198caacaa5d1cde70e82800f12d523 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Fri, 18 Feb 2022 08:56:49 +0100 Subject: [PATCH 33/34] fix focus layout --- resources/assets/stylesheets/scss/courseware.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss index 1ff8d9351f9..55da7833b1c 100755 --- a/resources/assets/stylesheets/scss/courseware.scss +++ b/resources/assets/stylesheets/scss/courseware.scss @@ -453,8 +453,8 @@ $consum_ribbon_width: calc(100% - 58px); width: calc(100% - 48px); >button { - padding: 20px 8px 4px 8px; - margin-top: 0; + padding: 18px 8px 4px 8px; + margin-top: 2px; max-width: unset; flex-grow: 0.5; &::after { -- GitLab From e1d4c9061532511f9f7609f8c18876d4a6b0d516 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Fri, 18 Feb 2022 16:12:12 +0100 Subject: [PATCH 34/34] resolve merge conflict --- .../CoursewareStructuralElement.vue | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue index 3cb13cb3586..60f3aac77d0 100755 --- a/resources/vue/components/courseware/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue @@ -430,35 +430,8 @@ /> </label> <label> -<<<<<<< HEAD - <translate>Farbe</translate> - <studip-select - v-model="currentElement.attributes.payload.color" - :options="colors" - :reduce="(color) => color.class" - label="class" - > - <template #open-indicator="selectAttributes"> - <span v-bind="selectAttributes" - ><studip-icon shape="arr_1down" size="10" - /></span> - </template> - <template #no-options="{ search, searching, loading }"> - <translate>Es steht keine Auswahl zur Verfügung.</translate> - </template> - <template #selected-option="{ name, hex }"> - <span class="vs__option-color" :style="{ 'background-color': hex }"></span - ><span>{{ name }}</span> - </template> - <template #option="{ name, hex }"> - <span class="vs__option-color" :style="{ 'background-color': hex }"></span - ><span>{{ name }}</span> - </template> - </studip-select> -======= <p><translate>Beschreibung</translate>:</p> <p>{{ currentElement.attributes.payload.description }}</p> ->>>>>>> 6b1cf2bd3 (add consum mode focus trap) </label> <label> <translate>Niveau</translate>: -- GitLab