diff --git a/package-lock.json b/package-lock.json index 8c411fc1d9db559d65dd9a8069ca39f497beb358..d224e5da62466eee806f0b42e4b55158f44b194f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@fullcalendar/resource-timegrid": "^4.3.0", "@fullcalendar/resource-timeline": "^4.3.0", "@fullcalendar/timegrid": "^4.3.0", + "@popperjs/core": "^2.11.2", "autoprefixer": "^10.2.5", "axios": "^0.21.0", "babel-loader": "^8.2.1", @@ -1307,6 +1308,16 @@ "node": ">=10" } }, + "node_modules/@popperjs/core": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz", + "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@trysound/sax": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.1.1.tgz", @@ -15365,6 +15376,12 @@ } } }, + "@popperjs/core": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz", + "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==", + "dev": true + }, "@trysound/sax": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.1.1.tgz", diff --git a/package.json b/package.json index ea983208adcc8be5c17d0a5a54a2ac7b5f589f07..6f883cd72b5d590905f9be825f9bd8f8da8f491a 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@fullcalendar/resource-timegrid": "^4.3.0", "@fullcalendar/resource-timeline": "^4.3.0", "@fullcalendar/timegrid": "^4.3.0", + "@popperjs/core": "^2.11.2", "autoprefixer": "^10.2.5", "axios": "^0.21.0", "babel-loader": "^8.2.1", diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss index 54f44004568a4ad0353ac56b479ebb4d231d1e75..aaeecc57c3ad8919ce21b1fe6ae21b580ebf3330 100755 --- a/resources/assets/stylesheets/scss/courseware.scss +++ b/resources/assets/stylesheets/scss/courseware.scss @@ -169,14 +169,14 @@ c o n t e n t s .loading-indicator span.load-3 { animation: loading-animation-3 1s linear 20; } - + @keyframes loading-animation-1 { 0% { transform: scale(1); } 16% { transform: scale(1.3); } 33% { transform: scale(1); } 100% { transform: scale(1); } } - + @keyframes loading-animation-2 { 0% { transform: scale(1); } 33% { transform: scale(1); } @@ -184,7 +184,7 @@ c o n t e n t s 65% { transform: scale(1); } 100% { transform: scale(1); } } - + @keyframes loading-animation-3 { 0% { transform: scale(1); } 66% { transform: scale(1); } @@ -2288,7 +2288,7 @@ d a s h b o a r d .responsive-display { .cw-dashboard { .cw-dashboard-box { - + &.cw-dashboard-box-full { width: 100% } @@ -4608,31 +4608,6 @@ cw tiles cw tiles end */ -/* -vSelect -*/ -.cw-vs-select { - max-width: 48em; - - .vs__dropdown-toggle { - border: solid thin $content-color-40; - border-radius: 0; - } - .vs__option-with-icon{ - padding-left: 8px; - } - .vs__option-color { - border: solid thin $content-color-40; - padding-left: 20px; - height: 20px; - margin-right: 4px; - } -} - -/* -vSelect end -*/ - /* cw manager copy */ .cw-manager-copy-selector { diff --git a/resources/assets/stylesheets/scss/select.scss b/resources/assets/stylesheets/scss/select.scss new file mode 100755 index 0000000000000000000000000000000000000000..f7a44410fa1151e5a1fd26a1f1bfded48f8bc377 --- /dev/null +++ b/resources/assets/stylesheets/scss/select.scss @@ -0,0 +1,38 @@ +.studip-v-select, .studip-v-select-detachted-ul { + max-width: 48em; + + .vs__option-with-icon{ + padding-left: 8px; + } + + .vs__option-color { + border: solid thin $content-color-40; + padding-left: 20px; + height: 20px; + margin-right: 4px; + } + + .vs__dropdown-toggle { + border: solid thin $content-color-40; + border-radius: 0; + } + + .vs__dropdown-menu, &.vs__dropdown-menu { + border-radius: 0; + } + + &.studip-v-select-drop-up { + border-bottom: solid thin $content-color-40; + border-top: none; + } + + &.studip-v-select-ul-drop-up { + border-bottom: none; + border-top: solid thin $content-color-40; + box-shadow: 0px -3px 6px 0 rgba(0, 0, 0, 0.15); + } + + &.studip-v-select-ul-dialog { + z-index: 3002; + } +} diff --git a/resources/assets/stylesheets/studip.scss b/resources/assets/stylesheets/studip.scss index 2f46b79b9a1732088f055db08ed3ba234689463d..1c92ea605a748f76fde83e566064f2ee1e7a0935 100644 --- a/resources/assets/stylesheets/studip.scss +++ b/resources/assets/stylesheets/studip.scss @@ -28,6 +28,7 @@ @import "scss/tooltip"; @import "scss/table_of_contents"; @import "scss/wiki"; +@import "scss/select"; @import "scss/grid"; diff --git a/resources/vue/base-components.js b/resources/vue/base-components.js index 54d52fe01835a8c0dabd6b828f96b2c11971493c..b57094e559b94bb2ee1532aa700e1205d7f88f97 100644 --- a/resources/vue/base-components.js +++ b/resources/vue/base-components.js @@ -10,6 +10,7 @@ import StudipMessageBox from './components/StudipMessageBox.vue'; import StudipProxyCheckbox from './components/StudipProxyCheckbox.vue'; import StudipProxiedCheckbox from './components/StudipProxiedCheckbox.vue'; import StudipTooltipIcon from './components/StudipTooltipIcon.vue'; +import StudipSelect from './components/StudipSelect.vue'; const BaseComponents = { Quicksearch, @@ -24,6 +25,7 @@ const BaseComponents = { StudipProxyCheckbox, StudipProxiedCheckbox, StudipTooltipIcon, + StudipSelect, }; export default BaseComponents; diff --git a/resources/vue/components/StudipSelect.vue b/resources/vue/components/StudipSelect.vue new file mode 100755 index 0000000000000000000000000000000000000000..6347b2964ffd161b1fb5e248456b2b60e852d96e --- /dev/null +++ b/resources/vue/components/StudipSelect.vue @@ -0,0 +1,102 @@ +<template> + <v-select ref="select" + @change="updateValue" + v-bind="{...$props, ...$attrs}" + v-on="$listeners" + :calculate-position="withPopper" + class="studip-v-select" + append-to-body + > + <template v-for="(index, name) in $scopedSlots" v-slot:[name]="data"> + <slot :name="name" v-bind="data"></slot> + </template> + </v-select> +</template> + +<script> +import vSelect from 'vue-select'; +import { createPopper } from '@popperjs/core' +import 'vue-select/dist/vue-select.css' +export default { + name: 'studip-select', + inheritAttrs: false, + components: { + vSelect, + }, + props: { + maxHeight: { + type: String, + default: '12em' + }, + }, + methods: { + updateValue(val) { + this.$emit('input', val) + }, + withPopper(dropdownList, component, { width }) { + if (component.$el?.offsetParent.classList.contains('studip-dialog-content')) { + dropdownList.classList.add('studip-v-select-ul-dialog'); + } + dropdownList.style.width = width + dropdownList.style.maxHeight = this.maxHeight; + dropdownList.classList.add('studip-v-select-detachted-ul'); + let dropdownListHeight = parseFloat(this.getStyleValue(dropdownList, 'height')) + + parseFloat(this.getStyleValue(dropdownList, 'paddingTop')) + + parseFloat(this.getStyleValue(dropdownList, 'paddingBottom')); + const popper = createPopper(component.$refs.toggle, dropdownList, { + placement: this.calculatePlacement(dropdownListHeight), + modifiers: [ + { + name: 'offset', + options: { + offset: [0, -1], + }, + }, + { + name: 'toggleClass', + enabled: true, + phase: 'write', + fn({ state }) { + component.$refs.dropdownMenu.classList.toggle( + 'studip-v-select-ul-drop-up', + state.placement === 'top' + ) + component.$el.classList.toggle( + 'studip-v-select-drop-up', + state.placement === 'top' + ) + }, + }, + ], + }) + return () => popper.destroy() + }, + calculatePlacement(dropdownListHeight) { + let scrollTop = window.pageYOffset || document.documentElement.scrollTop; + let selectBottom = Math.ceil( + this.$refs.select.$el.getBoundingClientRect().bottom + scrollTop + ); + let totalExpandedList = selectBottom + dropdownListHeight; + let totalDocHeight = Math.max( + document.body.scrollHeight, + document.body.offsetHeight, + document.documentElement.clientHeight, + document.documentElement.scrollHeight, + document.documentElement.offsetHeight + ); + let footerHeight = document.getElementById('layout_footer').offsetHeight; + let functionalAreaHeight = totalDocHeight - footerHeight; + return totalExpandedList >= functionalAreaHeight ? 'top' : 'bottom'; + }, + getStyleValue(element, styleProp) { + let result = ''; + if (window.getComputedStyle) { + result = getComputedStyle(element)[styleProp]; + } else if (element.currentStyle) { + result = element.currentStyle[styleProp]; + } + return result; + } + } +}; +</script> diff --git a/resources/vue/components/courseware/CoursewareAccordionContainer.vue b/resources/vue/components/courseware/CoursewareAccordionContainer.vue index 28f69aaba670639acc80a6d1f1b276f10eca516d..fee4720b7f2179edbc8f18efe40c5324b4c85b87 100755 --- a/resources/vue/components/courseware/CoursewareAccordionContainer.vue +++ b/resources/vue/components/courseware/CoursewareAccordionContainer.vue @@ -45,7 +45,7 @@ <component :is="component(block)" :block="block" :canEdit="canEdit" :isTeacher="isTeacher" /> </li> </transition-group> - + </draggable> </courseware-collapsible-box> <div v-if="sortMode && canEdit"> @@ -62,7 +62,7 @@ </label> <label> <translate>Icon</translate> - <v-select :options="icons" v-model="section.icon" class="cw-vs-select"> + <studip-select :options="icons" v-model="section.icon"> <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> @@ -75,7 +75,7 @@ <template #option="option"> <studip-icon :shape="option.label"/> <span class="vs__option-with-icon">{{option.label}}</span> </template> - </v-select> + </studip-select> </label> <label class="cw-container-section-delete" diff --git a/resources/vue/components/courseware/CoursewareChartBlock.vue b/resources/vue/components/courseware/CoursewareChartBlock.vue index b67d1edc50508aa0d70eb1ce48124a7d3db2464e..8dd58ec2433dd07e3eb00ce62c09d4b69a076bb0 100755 --- a/resources/vue/components/courseware/CoursewareChartBlock.vue +++ b/resources/vue/components/courseware/CoursewareChartBlock.vue @@ -49,13 +49,12 @@ </label> <label> <translate>Farbe</translate> - <v-select + <studip-select :options="colors" :reduce="colors => colors.value" label="rgb" :clearable="false" v-model="item.color" - class="cw-vs-select" @option:selected="buildChart" > <template #open-indicator="selectAttributes"> @@ -70,7 +69,7 @@ <template #option="{name, rgb}"> <span class="vs__option-color" :style="{'background-color': 'rgb(' + rgb + ')'}"></span><span>{{name}}</span> </template> - </v-select> + </studip-select> </label> </fieldset> </form> diff --git a/resources/vue/components/courseware/CoursewareContentOverviewElements.vue b/resources/vue/components/courseware/CoursewareContentOverviewElements.vue index 17e661c4badec78f4f4ff6e837522c6691bafbd7..75bc3c7697532b8fad2efe1455782ad4541e6ebf 100755 --- a/resources/vue/components/courseware/CoursewareContentOverviewElements.vue +++ b/resources/vue/components/courseware/CoursewareContentOverviewElements.vue @@ -183,7 +183,6 @@ :options="colors" :reduce="(color) => color.class" label="class" - class="cw-vs-select" > <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes" diff --git a/resources/vue/components/courseware/CoursewareHeadlineBlock.vue b/resources/vue/components/courseware/CoursewareHeadlineBlock.vue index ab203ed62ccd132ab2164a4ad4b36d19dbb2846f..1adfd945e0cfd2e6212cffa5f917d43a2ecb50bf 100755 --- a/resources/vue/components/courseware/CoursewareHeadlineBlock.vue +++ b/resources/vue/components/courseware/CoursewareHeadlineBlock.vue @@ -57,13 +57,12 @@ </label> <label> <translate>Textfarbe</translate> - <v-select + <studip-select :options="colors" label="hex" :reduce="color => color.hex" :clearable="false" v-model="currentTextColor" - class="cw-vs-select" > <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> @@ -77,11 +76,11 @@ <template #option="{name, hex}"> <span class="vs__option-color" :style="{'background-color': hex}"></span><span>{{name}}</span> </template> - </v-select> + </studip-select> </label> <label> <translate>Icon</translate> - <v-select :clearable="false" :options="icons" v-model="currentIcon" class="cw-vs-select"> + <studip-select :clearable="false" :options="icons" v-model="currentIcon"> <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> @@ -94,17 +93,16 @@ <template #option="option"> <studip-icon :shape="option.label"/> <span class="vs__option-with-icon">{{option.label}}</span> </template> - </v-select> + </studip-select> </label> <label> <translate>Icon-Farbe</translate> - <v-select + <studip-select :options="iconColors" label="name" :reduce="iconColor => iconColor.class" :clearable="false" v-model="currentIconColor" - class="cw-vs-select" > <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> @@ -118,7 +116,7 @@ <template #option="{name, hex}"> <span class="vs__option-color" :style="{'background-color': hex}"></span><span>{{name}}</span> </template> - </v-select> + </studip-select> </label> <label> <translate>Hintergrundtyp</translate> @@ -129,13 +127,12 @@ </label> <label v-if="currentBackgroundType === 'color'"> <translate>Hintergrundfarbe</translate> - <v-select + <studip-select :options="colors" label="hex" :reduce="color => color.hex" v-model="currentBackgroundColor" :clearable="false" - class="cw-vs-select" > <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> @@ -149,7 +146,7 @@ <template #option="{name, hex}"> <span class="vs__option-color" :style="{'background-color': hex}"></span><span>{{name}}</span> </template> - </v-select> + </studip-select> </label> <label v-if="currentBackgroundType === 'image'"> <translate>Hintergrundbild</translate> diff --git a/resources/vue/components/courseware/CoursewareImageMapBlock.vue b/resources/vue/components/courseware/CoursewareImageMapBlock.vue index d79443fb244988c6aade9c75d14ac54cea44172a..9596e8854a022a93fe5df5737a5e27752750e690 100755 --- a/resources/vue/components/courseware/CoursewareImageMapBlock.vue +++ b/resources/vue/components/courseware/CoursewareImageMapBlock.vue @@ -60,13 +60,12 @@ > <label> <translate>Farbe</translate> - <v-select + <studip-select :options="colors" label="name" :reduce="color => color.class" :clearable="false" v-model="shape.data.color" - class="cw-vs-select" > <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> @@ -80,7 +79,7 @@ <template #option="{name, rgba}"> <span class="vs__option-color" :style="{'background-color': rgba}"></span><span>{{name}}</span> </template> - </v-select> + </studip-select> </label> <label v-if="shape.type === 'arc'" class="cw-block-image-map-dimensions"> X: <input type="number" v-model="shape.data.centerX" @change="drawScreen" /> Y: @@ -376,7 +375,7 @@ export default { shapeWidth = shapeWidth || 0; let newText = []; - + if (shapeWidth <= 0) { return [text]; } diff --git a/resources/vue/components/courseware/CoursewareKeyPointBlock.vue b/resources/vue/components/courseware/CoursewareKeyPointBlock.vue index d8474fb99a370d2805af93a4a69d30e472a8c475..39124e548b9d1e4b6de11ac93d738d9c550157f5 100755 --- a/resources/vue/components/courseware/CoursewareKeyPointBlock.vue +++ b/resources/vue/components/courseware/CoursewareKeyPointBlock.vue @@ -29,13 +29,12 @@ <label for="cw-keypoint-color"> <translate>Farbe</translate> - <v-select + <studip-select :options="colors" label="icon" :clearable="false" :reduce="option => option.icon" v-model="currentColor" - class="cw-vs-select" > <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> @@ -49,12 +48,12 @@ <template #option="{name, hex}"> <span class="vs__option-color" :style="{'background-color': hex}"></span><span>{{name}}</span> </template> - </v-select> + </studip-select> </label> <label for="cw-keypoint-icons"> <translate>Icon</translate> - <v-select :options="icons" :clearable="false" v-model="currentIcon" class="cw-vs-select"> + <studip-select :options="icons" :clearable="false" v-model="currentIcon"> <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> @@ -67,7 +66,7 @@ <template #option="option"> <studip-icon :shape="option.label"/> <span class="vs__option-with-icon">{{option.label}}</span> </template> - </v-select> + </studip-select> </label> </form> </template> diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue index 090f37a614986b795f82a576a4807d71f71057a2..f30c92455b9ec10b1adbcb6682d04608febaa870 100755 --- a/resources/vue/components/courseware/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue @@ -175,12 +175,11 @@ <form class="default" @submit.prevent=""> <label> <translate>Farbe</translate> - <v-select + <studip-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" @@ -198,7 +197,7 @@ <span class="vs__option-color" :style="{ 'background-color': hex }"></span ><span>{{ name }}</span> </template> - </v-select> + </studip-select> </label> <label> <translate>Zweck</translate> diff --git a/resources/vue/components/courseware/CoursewareTabsContainer.vue b/resources/vue/components/courseware/CoursewareTabsContainer.vue index c9bb646e7a59422c7343131665c950cf51c6c3a6..66c669265153ca707a2bfbfbf66419c8972fb910 100755 --- a/resources/vue/components/courseware/CoursewareTabsContainer.vue +++ b/resources/vue/components/courseware/CoursewareTabsContainer.vue @@ -73,7 +73,7 @@ </label> <label> <translate>Icon</translate> - <v-select :options="icons" v-model="section.icon" class="cw-vs-select"> + <studip-select :options="icons" v-model="section.icon"> <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> @@ -86,7 +86,7 @@ <template #option="option"> <studip-icon :shape="option.label"/> <span class="vs__option-with-icon">{{option.label}}</span> </template> - </v-select> + </studip-select> </label> <label class="cw-container-section-delete" diff --git a/resources/vue/courseware-index-app.js b/resources/vue/courseware-index-app.js index 4135e2c08ecb0bb12ed376f21503977d0c02b068..adafcd448a1011e6931bdd5f02e3dbe057f78bc5 100755 --- a/resources/vue/courseware-index-app.js +++ b/resources/vue/courseware-index-app.js @@ -8,10 +8,6 @@ import VueRouter from 'vue-router'; import Vuex from 'vuex'; import axios from 'axios'; import { mapResourceModules } from '@elan-ev/reststate-vuex'; -import vSelect from 'vue-select'; -import 'vue-select/dist/vue-select.css' - -Vue.component('v-select', vSelect); const mountApp = (STUDIP, createApp, element) => { const getHttpClient = () =>