From a7cf3091695338096190e48ead5a90a2233af439 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Thu, 20 Nov 2025 06:22:36 +0100 Subject: [PATCH 1/3] remove useless css Signed-off-by: dartcafe --- src/assets/scss/hacks.scss | 6 ++++++ src/components/Base/modules/Collapsible.vue | 1 - src/components/Base/modules/ConfigBox.vue | 3 --- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/assets/scss/hacks.scss b/src/assets/scss/hacks.scss index 44ea00c62e..5f451b8c96 100644 --- a/src/assets/scss/hacks.scss +++ b/src/assets/scss/hacks.scss @@ -4,6 +4,12 @@ */ // some hacks, do alter the display of third parity components +// FIXME: remove this hack after the issue is fixed +// make sure selects are 100% width in config box +.config-box .v-select { + width: 100%; +} + // FIXME: remove this hack after the issue is fixed // limit the width of the user bubble to prevent it from overflowing .user-bubble__name { diff --git a/src/components/Base/modules/Collapsible.vue b/src/components/Base/modules/Collapsible.vue index ca7afb1fad..90ddbd272c 100644 --- a/src/components/Base/modules/Collapsible.vue +++ b/src/components/Base/modules/Collapsible.vue @@ -222,7 +222,6 @@ const containerClass = computed(() => ({ .collapsible_container { position: relative; overflow: auto; - width: 100%; transition: height 0.3s ease; padding-inline-end: 8px; diff --git a/src/components/Base/modules/ConfigBox.vue b/src/components/Base/modules/ConfigBox.vue index 5090c80d80..c8dc35ccbe 100644 --- a/src/components/Base/modules/ConfigBox.vue +++ b/src/components/Base/modules/ConfigBox.vue @@ -48,9 +48,6 @@ const { name, info = '', indented = false } = defineProps() &__container { grid-area: container; - & > * { - width: 100%; - } } .indented { From 3e8b72adae7895c9d631fca8e4ed0e73f8a8b1e4 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Sun, 23 Nov 2025 11:57:43 +0100 Subject: [PATCH 2/3] Refactor ShareItem and UserItem Signed-off-by: dartcafe --- src/components/Shares/ShareItem.vue | 99 ++++++++++++++++++++----- src/components/User/UserAvatar.types.ts | 4 +- src/components/User/UserAvatar.vue | 98 ++++++++---------------- src/components/User/UserItem.vue | 6 +- 4 files changed, 119 insertions(+), 88 deletions(-) diff --git a/src/components/Shares/ShareItem.vue b/src/components/Shares/ShareItem.vue index 41c7168950..9191dd7f73 100644 --- a/src/components/Shares/ShareItem.vue +++ b/src/components/Shares/ShareItem.vue @@ -15,6 +15,11 @@ import ShareMenu from './ShareMenu.vue' import type { Share } from '../../stores/shares.types' import { AvatarTypeIcon } from '../User/UserAvatar.types' +import AdminIcon from 'vue-material-design-icons/ShieldCrownOutline.vue' +import ContactIcon from 'vue-material-design-icons/CardAccountDetailsOutline.vue' +import EmailIcon from 'vue-material-design-icons/EmailOutline.vue' +import ShareIcon from 'vue-material-design-icons/ShareVariantOutline.vue' +import PollGroupIcon from 'vue-material-design-icons/CodeBraces.vue' const emit = defineEmits(['showQrCode']) @@ -27,6 +32,8 @@ const publicShareDescription = computed(() => { return t('polls', 'Public link: {token}', { token: share.user.id }) }) +const typeIconSize = 16 + const userDescription = computed(() => { if (share.groupId && !share.pollId) { return t('polls', 'Poll group access') @@ -53,13 +60,6 @@ const userDescription = computed(() => { return '' }) -const computedTypeIcon = computed(() => { - if (share.groupId && !share.pollId) { - return 'pollGroupIcon' - } - return true -}) - const label = ref({ inputValue: '', inputProps: { @@ -71,6 +71,67 @@ const label = ref({ }, }) +const computedTypeIcon = computed(() => { + if (share.groupId && !share.pollId) { + return 'pollGroupIcon' + } + return share.type +}) + +const typeIconComponent = computed(() => { + switch (computedTypeIcon.value) { + case 'admin': + return AdminIcon + case 'contact': + return ContactIcon + case 'email': + return EmailIcon + case 'external': + return ShareIcon + case 'pollGroupIcon': + return PollGroupIcon + default: + return null + } +}) + +// const votedIndicator = computed(() => { +// if (share.voted) { +// return { +// class: 'vote-status voted', +// component: VotedIcon, +// name: t('polls', 'Has voted'), +// } +// } +// if (!share.voted) { +// return { +// class: 'vote-status unvoted', +// component: UnvotedIcon, +// name: t('polls', 'Has not voted'), +// } +// } +// return { class: 'vote-status empty', component: null, name: '' } +// }) + +const votedIndicator = computed(() => { + if (share.groupId || ['public', 'group'].includes(share.type)) { + return { class: 'vote-status empty', component: 'div', name: '' } + } + if (share.voted) { + return { + class: 'vote-status voted', + component: VotedIcon, + name: t('polls', 'Has voted'), + } + } + + return { + class: 'vote-status unvoted', + component: UnvotedIcon, + name: t('polls', 'Has not voted'), + } +}) + onMounted(() => { label.value.inputValue = share.label }) @@ -82,21 +143,21 @@ onMounted(() => { :class="{ deleted: share.deleted }" :user="share.user" show-email - :type-icon="computedTypeIcon" :description="userDescription" :label="share.label"> + + diff --git a/src/components/User/UserAvatar.types.ts b/src/components/User/UserAvatar.types.ts index 1daec53f45..f35b256ce9 100644 --- a/src/components/User/UserAvatar.types.ts +++ b/src/components/User/UserAvatar.types.ts @@ -3,4 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -export type AvatarTypeIcon = boolean | 'pollGroupIcon' +import { ShareType } from '@/stores/shares.types' + +export type AvatarTypeIcon = ShareType | 'pollGroupIcon' diff --git a/src/components/User/UserAvatar.vue b/src/components/User/UserAvatar.vue index 3dd2045711..f1aacc1533 100644 --- a/src/components/User/UserAvatar.vue +++ b/src/components/User/UserAvatar.vue @@ -9,33 +9,23 @@ import { computed } from 'vue' import { useRoute } from 'vue-router' import { createDefault } from '../../Types' import type { User, VirtualUserItemType } from '../../Types' -import type { AvatarTypeIcon } from './UserAvatar.types.ts' -import AdminIcon from 'vue-material-design-icons/ShieldCrownOutline.vue' import LinkIcon from 'vue-material-design-icons/Link.vue' -import ContactIcon from 'vue-material-design-icons/CardAccountDetailsOutline.vue' -import EmailIcon from 'vue-material-design-icons/EmailOutline.vue' -import ShareIcon from 'vue-material-design-icons/ShareVariantOutline.vue' import ContactGroupIcon from 'vue-material-design-icons/AccountGroupOutline.vue' import GroupIcon from 'vue-material-design-icons/AccountMultipleOutline.vue' import CircleIcon from 'vue-material-design-icons/GoogleCirclesExtended.vue' import DeletedUserIcon from 'vue-material-design-icons/AccountOffOutline.vue' import AnoymousIcon from 'vue-material-design-icons/Incognito.vue' -import PollGroupIcon from 'vue-material-design-icons/CodeBraces.vue' import OpenPollIcon from 'vue-material-design-icons/Earth.vue' const route = useRoute() interface Props { mdIconSize?: number virtualUserType?: VirtualUserItemType - typeIcon?: AvatarTypeIcon - typeIconSize?: number user: User } const { mdIconSize = 20, virtualUserType, - typeIcon = false, - typeIconSize = 16, user = createDefault(), } = defineProps() @@ -50,20 +40,35 @@ const avatarUserId = computed(() => { return user.id }) -const useIconSlot = computed( - () => - (virtualUserType - && ['internalAccess', 'addPublicLink'].includes(virtualUserType)) - || (user.type - && [ - 'public', - 'contactGroup', - 'group', - 'circle', - 'deleted', - 'anonymous', - ].includes(user.type)), -) +const iconTypeComponent = computed(() => { + if (virtualUserType) { + switch (virtualUserType) { + case 'addPublicLink': + return LinkIcon + case 'internalAccess': + return OpenPollIcon + default: + return null + } + } else { + switch (user.type) { + case 'public': + return LinkIcon + case 'anonymous': + return AnoymousIcon + case 'contactGroup': + return ContactGroupIcon + case 'group': + return GroupIcon + case 'circle': + return CircleIcon + case 'deleted': + return DeletedUserIcon + default: + return null + } + } +})