diff options
Diffstat (limited to 'resources')
| -rw-r--r-- | resources/assets/javascripts/bootstrap/forms.js | 14 | ||||
| -rw-r--r-- | resources/assets/javascripts/bootstrap/use-vue-components.js | 18 | ||||
| -rw-r--r-- | resources/assets/javascripts/entry-base.js | 1 | ||||
| -rw-r--r-- | resources/assets/stylesheets/studip.scss | 34 | ||||
| -rw-r--r-- | resources/vue/base-components.js | 2 | ||||
| -rw-r--r-- | resources/vue/components/StudipUserAvatar.vue | 38 | ||||
| -rw-r--r-- | resources/vue/components/avatar/UserAvatar.vue (renamed from resources/vue/components/UserAvatar.vue) | 6 | ||||
| -rw-r--r-- | resources/vue/components/avatar/UserAvatarDropdown.vue (renamed from resources/vue/components/forum/UserAvatarDropdown.vue) | 10 | ||||
| -rw-r--r-- | resources/vue/components/courseware/tasks/peer-review/PeerReviewListItem.vue | 40 | ||||
| -rw-r--r-- | resources/vue/components/forum/ForumMembers.vue | 8 | ||||
| -rw-r--r-- | resources/vue/components/forum/posts/Post.vue | 2 | ||||
| -rw-r--r-- | resources/vue/components/forum/posts/PostReactionShow.vue | 2 |
12 files changed, 94 insertions, 81 deletions
diff --git a/resources/assets/javascripts/bootstrap/forms.js b/resources/assets/javascripts/bootstrap/forms.js index dc57674..d22b15a 100644 --- a/resources/assets/javascripts/bootstrap/forms.js +++ b/resources/assets/javascripts/bootstrap/forms.js @@ -435,20 +435,6 @@ STUDIP.ready(function () { }); } - /* - * Form elements with the "simplevue" class are meant for forms that just need some vue components - * to do something fancy inside the form but which do not need the full functionality of the form builder. - */ - let simple_vue_items = document.querySelectorAll('form .simplevue:not(.vueified)'); - if (simple_vue_items.length > 0) { - STUDIP.Vue.load().then(({createApp}) => { - simple_vue_items.forEach(f => { - f.classList.add('vueified'); - createApp().mount(f); - }); - }); - } - // Well, this is really nasty: Select2 can't determine the select // element's width if it is hidden (by itself or by its parent). // This is due to the fact that elements are not rendered when hidden diff --git a/resources/assets/javascripts/bootstrap/use-vue-components.js b/resources/assets/javascripts/bootstrap/use-vue-components.js new file mode 100644 index 0000000..e262899 --- /dev/null +++ b/resources/assets/javascripts/bootstrap/use-vue-components.js @@ -0,0 +1,18 @@ +STUDIP.ready(function () { + const selectors = [ + '.use-vue-components', + 'form .simplevue' + ]; + const selector = selectors.map(selector => `${selector}:not(.vueified)`).join(','); + + const containers = document.querySelectorAll(selector); + + if (containers.length > 0) { + STUDIP.Vue.load().then(({ createApp }) => { + containers.forEach(container => { + container.classList.add('vueified'); + createApp().mount(container) + }); + }); + } +}); diff --git a/resources/assets/javascripts/entry-base.js b/resources/assets/javascripts/entry-base.js index 896189e..02fd471 100644 --- a/resources/assets/javascripts/entry-base.js +++ b/resources/assets/javascripts/entry-base.js @@ -78,6 +78,7 @@ import "./bootstrap/courseware.js" import "./bootstrap/external_pages.js" import "./bootstrap/vips.js" import "./bootstrap/admission.js" +import "./bootstrap/use-vue-components.js" import "./mvv_course_wizard.js" import "./mvv.js" diff --git a/resources/assets/stylesheets/studip.scss b/resources/assets/stylesheets/studip.scss index f6c8e9e..2bd65b9 100644 --- a/resources/assets/stylesheets/studip.scss +++ b/resources/assets/stylesheets/studip.scss @@ -588,7 +588,8 @@ div.info { padding-left: 1%; } } // course members -a.new-member { +a.new-member, +.new-member .user-avatar-dropdown__username { @include icon(after, star, attention, 8px); } @@ -654,6 +655,24 @@ input.allow-plaintext-toggle { } } +.users-table, +.scores-table { + &__avatar-container { + display: inline-flex; + align-items: center; + gap: 5px; + } +} + +.scores-table { + &__avatar-container { + .dropdown__content { + left: 0; + right: auto; + } + } +} + .links-preview { &__item { border: solid 1px $color--content-box-border; @@ -827,6 +846,10 @@ input.allow-plaintext-toggle { hr { margin: 10px 0; + border-top: 1px solid var(--color--divider); + border-bottom: none; + border-left: none; + border-right: none; } &__header { @@ -844,6 +867,7 @@ input.allow-plaintext-toggle { .user-info { flex: 1; + margin-right: 20px; .user-name { text-wrap: auto; @@ -891,13 +915,19 @@ input.allow-plaintext-toggle { } } +.user-avatar-container { + width: max-content; + display: inline-flex; + align-items: center; + gap: 5px; +} + .user-avatar-dropdown { &__preview { background: none; border: none; padding: 0; cursor: pointer; - display: flex; &:hover, &:focus, diff --git a/resources/vue/base-components.js b/resources/vue/base-components.js index 2524642..904afa1 100644 --- a/resources/vue/base-components.js +++ b/resources/vue/base-components.js @@ -34,6 +34,8 @@ const BaseComponents = { StudipTooltipIcon: defineAsyncComponent(() => import('./components/StudipTooltipIcon.vue')), StudipWysiwyg: defineAsyncComponent(() => import('./components/StudipWysiwyg.vue')), UserFilterInput: defineAsyncComponent(() => import('./components/form_inputs/UserFilterInput.vue')), + UserAvatar: defineAsyncComponent(() => import('./components/avatar/UserAvatar.vue')), + UserAvatarDropdown: defineAsyncComponent(() => import('./components/avatar/UserAvatarDropdown.vue')), }; export default BaseComponents; diff --git a/resources/vue/components/StudipUserAvatar.vue b/resources/vue/components/StudipUserAvatar.vue deleted file mode 100644 index 1020839..0000000 --- a/resources/vue/components/StudipUserAvatar.vue +++ /dev/null @@ -1,38 +0,0 @@ -<template> - <div class="studip-user-avatar" :class="{ 'studip-user-avatar-small': small }"> - <span> - <img :src="avatarUrl" role="presentation" /> - </span> - <span>{{ formattedName }}</span> - </div> -</template> - -<script> -export default { - props: { - avatarUrl: { - type: String, - required: true, - }, - formattedName: { - type: String, - required: true, - }, - small: { - type: Boolean, - default: false, - }, - }, -}; -</script> - -<style scoped> -.studip-user-avatar { - align-items: center; - display: flex; - gap: 0.25rem; -} -.studip-user-avatar-small img { - width: 1em; -} -</style> diff --git a/resources/vue/components/UserAvatar.vue b/resources/vue/components/avatar/UserAvatar.vue index 91153e9..def6719 100644 --- a/resources/vue/components/UserAvatar.vue +++ b/resources/vue/components/avatar/UserAvatar.vue @@ -1,6 +1,6 @@ <script setup> -import {$gettext} from "../../assets/javascripts/lib/gettext"; -import StudipIcon from "./StudipIcon.vue"; +import {$gettext} from "@/assets/javascripts/lib/gettext"; +import StudipIcon from "@/vue/components/StudipIcon.vue"; const props = defineProps({ user: { @@ -53,6 +53,7 @@ const openBlubberChat = () => { <ul class="user-avatar__actions"> <li> <button + type="button" v-if="user.id !== AUTH_ID" @click="openBlubberChat" class="action-item button-base" @@ -76,6 +77,7 @@ const openBlubberChat = () => { </li> <li> <button + type="button" v-if="user.id !== AUTH_ID" class="action-item button-base" :title="$gettext('Nachricht schreiben')" diff --git a/resources/vue/components/forum/UserAvatarDropdown.vue b/resources/vue/components/avatar/UserAvatarDropdown.vue index 82f693d..6706990 100644 --- a/resources/vue/components/forum/UserAvatarDropdown.vue +++ b/resources/vue/components/avatar/UserAvatarDropdown.vue @@ -1,7 +1,6 @@ <script setup> -import {$gettext} from "../../../assets/javascripts/lib/gettext"; -import Dropdown from "../Dropdown.vue"; -import UserAvatar from "../UserAvatar.vue"; +import Dropdown from "@/vue/components/Dropdown.vue"; +import UserAvatar from "@/vue/components/avatar/UserAvatar.vue"; defineProps({ user: { @@ -24,14 +23,15 @@ const isOpen = defineModel({ default: false }); <Dropdown class="user-avatar-dropdown" v-model="isOpen"> <template #trigger> <button - class="user-avatar-dropdown__preview" + class="user-avatar-dropdown__preview button-base" + type="button" @click="isOpen = !isOpen" v-bind="$attrs" :class="{ 'active': isOpen }" :title="label ?? user.name" - :aria-label="label ?? $gettext('vCard')" + :aria-label="label ?? user.name" :aria-pressed="isOpen" > <img class="user-profile" :src="user.avatar_url" :style="{ width: size, height: size }" :alt="user.name" /> diff --git a/resources/vue/components/courseware/tasks/peer-review/PeerReviewListItem.vue b/resources/vue/components/courseware/tasks/peer-review/PeerReviewListItem.vue index d24616e..ad7aec9 100644 --- a/resources/vue/components/courseware/tasks/peer-review/PeerReviewListItem.vue +++ b/resources/vue/components/courseware/tasks/peer-review/PeerReviewListItem.vue @@ -6,25 +6,37 @@ </a> </td> <td> - <a v-if="isUser(submitter)" :href="userProfile(submitter)"> - <UserAvatar - :avatar-url="submitter.meta.avatar.small" - :formatted-name="submitter.attributes['formatted-name']" - small + <div v-if="isUser(submitter)" class="user-avatar-container"> + <UserAvatarDropdown + :user="{ + id: submitter.id, + avatar_url: submitter.meta.avatar.small, + username: submitter.attributes['username'], + name: submitter.attributes['formatted-name'] + }" /> - </a> + <a :href="userProfile(submitter)"> + {{ submitter.attributes['formatted-name'] }} + </a> + </div> <a v-else :href="statusGroupUrl(submitter)"> {{ submitter.attributes.name }} </a> </td> <td> - <a v-if="isUser(reviewer)" :href="userProfile(reviewer)"> - <UserAvatar - :avatar-url="reviewer.meta.avatar.small" - :formatted-name="reviewer.attributes['formatted-name']" - small + <div v-if="isUser(reviewer)" class="user-avatar-container"> + <UserAvatarDropdown + :user="{ + id: reviewer.id, + avatar_url: reviewer.meta.avatar.small, + username: reviewer.attributes['username'], + name: reviewer.attributes['formatted-name'] + }" /> - </a> + <a :href="userProfile(reviewer)"> + {{ reviewer.attributes['formatted-name'] }} + </a> + </div> <a v-else :href="statusGroupUrl(reviewer)"> {{ reviewer.attributes.name }} </a> @@ -51,9 +63,9 @@ <script> import { mapGetters } from 'vuex'; import StudipDate from '@/vue/components/StudipDate.vue'; -import UserAvatar from '@/vue/components/StudipUserAvatar.vue'; import taskHelper from '../../../../mixins/courseware/task-helper.js'; import { getProcessStatus, ProcessStatus } from './definitions'; +import UserAvatarDropdown from "@/vue/components/avatar/UserAvatarDropdown.vue"; export default { mixins: [taskHelper], @@ -71,7 +83,7 @@ export default { required: true, }, }, - components: { StudipDate, UserAvatar }, + components: { StudipDate, UserAvatarDropdown }, computed: { ...mapGetters({ context: 'context', diff --git a/resources/vue/components/forum/ForumMembers.vue b/resources/vue/components/forum/ForumMembers.vue index 7e0e385..3258bff 100644 --- a/resources/vue/components/forum/ForumMembers.vue +++ b/resources/vue/components/forum/ForumMembers.vue @@ -1,10 +1,10 @@ <script setup> import {computed, ref} from "vue"; import {$gettext} from "../../../assets/javascripts/lib/gettext"; -import UserAvatarDropdown from "./UserAvatarDropdown.vue"; import Dropdown from "../Dropdown.vue"; import StudipIcon from "@/vue/components/StudipIcon.vue"; -import UserAvatar from "../UserAvatar.vue"; +import UserAvatar from "@/vue/components/avatar/UserAvatar.vue"; +import UserAvatarDropdown from "@/vue/components/avatar/UserAvatarDropdown.vue"; const props = defineProps({ members: { @@ -110,7 +110,7 @@ const isModerator = role => role === 'moderator'; @click="activeUserAvatar = user.id" :title="$gettext('Aufklappen')" :aria-label="$gettext('Aufklappen')" - class="show-avatar"> + class="show-avatar button-base"> <StudipIcon shape="arr_1down" :size="15" aria-hidden="true" /> </button> </div> @@ -119,7 +119,7 @@ const isModerator = role => role === 'moderator'; @click="activeUserAvatar = ''" :title="$gettext('Zuklappen')" :aria-label="$gettext('Zuklappen')" - class="hide-avatar"> + class="hide-avatar button-base"> <StudipIcon shape="arr_1up" :size="15" aria-hidden="true" /> </button> <UserAvatar v-if="activeUserAvatar === user.id" :user="user" /> diff --git a/resources/vue/components/forum/posts/Post.vue b/resources/vue/components/forum/posts/Post.vue index 48921aa..16332fb 100644 --- a/resources/vue/components/forum/posts/Post.vue +++ b/resources/vue/components/forum/posts/Post.vue @@ -3,6 +3,7 @@ import {computed, ref, useTemplateRef} from "vue"; import PostEditForm from "./PostEditForm.vue"; import PostCreateForm from "./PostCreateForm.vue"; import PostContent from "@/vue/components/forum/posts/PostContent.vue"; +import UserAvatarDropdown from "@/vue/components/avatar/UserAvatarDropdown.vue"; import PostReactions from "./PostReactions.vue"; import {useForumPost} from "../../../store/pinia/forum/ForumPost"; import {getDiscussionURL} from "@/vue/components/forum/helpers/urls"; @@ -10,7 +11,6 @@ import StudipDateTime from "@/vue/components/StudipDateTime.vue"; import StudipIcon from "@/vue/components/StudipIcon.vue"; import {$gettext} from "@/assets/javascripts/lib/gettext"; import LinksPreview from "@/vue/components/LinksPreview.vue"; -import UserAvatarDropdown from "../UserAvatarDropdown.vue"; import {userProfileURL} from "../helpers/urls"; import {useForumConfig} from "../../../store/pinia/forum/ForumConfig"; diff --git a/resources/vue/components/forum/posts/PostReactionShow.vue b/resources/vue/components/forum/posts/PostReactionShow.vue index 8ad13a2..e5790b6 100644 --- a/resources/vue/components/forum/posts/PostReactionShow.vue +++ b/resources/vue/components/forum/posts/PostReactionShow.vue @@ -1,7 +1,7 @@ <script setup> import {$gettext} from "../../../../assets/javascripts/lib/gettext"; import StudipDateTime from "../../StudipDateTime.vue"; -import UserAvatarDropdown from "../UserAvatarDropdown.vue"; +import UserAvatarDropdown from "@/vue/components/avatar/UserAvatarDropdown.vue"; import {REACTION_ICONS} from "./reactions"; import {userProfileURL} from "../helpers/urls"; import {computed, onMounted} from "vue"; |
