diff options
Diffstat (limited to 'core/src')
-rw-r--r-- | core/src/components/AccountMenu/AccountMenuEntry.vue | 38 | ||||
-rw-r--r-- | core/src/components/PublicPageMenu/PublicPageMenuEntry.vue | 8 | ||||
-rw-r--r-- | core/src/public-page-user-menu.ts | 15 | ||||
-rw-r--r-- | core/src/views/AccountMenu.vue | 2 | ||||
-rw-r--r-- | core/src/views/PublicPageUserMenu.vue | 138 |
5 files changed, 185 insertions, 16 deletions
diff --git a/core/src/components/AccountMenu/AccountMenuEntry.vue b/core/src/components/AccountMenu/AccountMenuEntry.vue index 47db84a7d33..d983226d273 100644 --- a/core/src/components/AccountMenu/AccountMenuEntry.vue +++ b/core/src/components/AccountMenu/AccountMenuEntry.vue @@ -11,28 +11,30 @@ compact :href="href" :name="name" - target="_self"> + target="_self" + @click="onClick"> <template #icon> - <img class="account-menu-entry__icon" + <NcLoadingIcon v-if="loading" :size="20" class="account-menu-entry__loading" /> + <slot v-else-if="$scopedSlots.icon" name="icon" /> + <img v-else + class="account-menu-entry__icon" :class="{ 'account-menu-entry__icon--active': active }" :src="iconSource" alt=""> </template> - <template v-if="loading" #indicator> - <NcLoadingIcon /> - </template> </NcListItem> </template> -<script> +<script lang="ts"> import { loadState } from '@nextcloud/initial-state' +import { defineComponent } from 'vue' import NcListItem from '@nextcloud/vue/components/NcListItem' import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon' const versionHash = loadState('core', 'versionHash', '') -export default { +export default defineComponent({ name: 'AccountMenuEntry', components: { @@ -55,11 +57,11 @@ export default { }, active: { type: Boolean, - required: true, + default: false, }, icon: { type: String, - required: true, + default: '', }, }, @@ -76,11 +78,17 @@ export default { }, methods: { - handleClick() { - this.loading = true + onClick(e: MouseEvent) { + this.$emit('click', e) + + // Allow to not show the loading indicator + // in case the click event was already handled + if (!e.defaultPrevented) { + this.loading = true + } }, }, -} +}) </script> <style lang="scss" scoped> @@ -96,6 +104,12 @@ export default { } } + &__loading { + height: 20px; + width: 20px; + margin: calc((var(--default-clickable-area) - 20px) / 2); // 20px icon size + } + :deep(.list-item-content__main) { width: fit-content; } diff --git a/core/src/components/PublicPageMenu/PublicPageMenuEntry.vue b/core/src/components/PublicPageMenu/PublicPageMenuEntry.vue index 4a8640f38a8..413806c7089 100644 --- a/core/src/components/PublicPageMenu/PublicPageMenuEntry.vue +++ b/core/src/components/PublicPageMenu/PublicPageMenuEntry.vue @@ -11,22 +11,24 @@ role="presentation" @click="$emit('click')"> <template #icon> - <div role="presentation" :class="['icon', icon, 'public-page-menu-entry__icon']" /> + <slot v-if="$scopedSlots.icon" name="icon" /> + <div v-else role="presentation" :class="['icon', icon, 'public-page-menu-entry__icon']" /> </template> </NcListItem> </template> <script setup lang="ts"> -import NcListItem from '@nextcloud/vue/components/NcListItem' import { onMounted } from 'vue' +import NcListItem from '@nextcloud/vue/components/NcListItem' + const props = defineProps<{ /** Only emit click event but do not open href */ clickOnly?: boolean // menu entry props id: string label: string - icon: string + icon?: string href: string details?: string }>() diff --git a/core/src/public-page-user-menu.ts b/core/src/public-page-user-menu.ts new file mode 100644 index 00000000000..25024271fb5 --- /dev/null +++ b/core/src/public-page-user-menu.ts @@ -0,0 +1,15 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { getCSPNonce } from '@nextcloud/auth' +import Vue from 'vue' + +import PublicPageUserMenu from './views/PublicPageUserMenu.vue' + +__webpack_nonce__ = getCSPNonce() + +const View = Vue.extend(PublicPageUserMenu) +const instance = new View() +instance.$mount('#public-page-user-menu') diff --git a/core/src/views/AccountMenu.vue b/core/src/views/AccountMenu.vue index d1b4694ebc1..cac02129bac 100644 --- a/core/src/views/AccountMenu.vue +++ b/core/src/views/AccountMenu.vue @@ -211,7 +211,7 @@ export default defineComponent({ } } - // Ensure we do not wast space, as the header menu sets a default width of 350px + // Ensure we do not waste space, as the header menu sets a default width of 350px :deep(.header-menu__content) { width: fit-content !important; } diff --git a/core/src/views/PublicPageUserMenu.vue b/core/src/views/PublicPageUserMenu.vue new file mode 100644 index 00000000000..ff6f4090b2a --- /dev/null +++ b/core/src/views/PublicPageUserMenu.vue @@ -0,0 +1,138 @@ +<!-- + - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later + --> +<template> + <NcHeaderMenu id="public-page-user-menu" + class="public-page-user-menu" + is-nav + :aria-label="t('core', 'User menu')" + :description="avatarDescription"> + <template #trigger> + <NcAvatar class="public-page-user-menu__avatar" + disable-menu + disable-tooltip + is-guest + :user="displayName || '?'" /> + </template> + + <!-- Privacy notice --> + <NcNoteCard class="public-page-user-menu__list-note" + :text="privacyNotice" + type="info" /> + + <ul class="public-page-user-menu__list"> + <!-- Nickname dialog --> + <AccountMenuEntry id="set-nickname" + :name="!displayName ? t('core', 'Set public name') : t('core', 'Change public name')" + href="#" + @click.prevent.stop="setNickname"> + <template #icon> + <IconAccount /> + </template> + </AccountMenuEntry> + </ul> + </NcHeaderMenu> +</template> + +<script lang="ts"> +import type { NextcloudUser } from '@nextcloud/auth' + +import '@nextcloud/dialogs/style.css' +import { defineComponent } from 'vue' +import { getGuestUser } from '@nextcloud/auth' +import { showGuestUserPrompt } from '@nextcloud/dialogs' +import { subscribe } from '@nextcloud/event-bus' +import { t } from '@nextcloud/l10n' + +import NcAvatar from '@nextcloud/vue/components/NcAvatar' +import NcHeaderMenu from '@nextcloud/vue/components/NcHeaderMenu' +import NcNoteCard from '@nextcloud/vue/components/NcNoteCard' +import IconAccount from 'vue-material-design-icons/Account.vue' + +import AccountMenuEntry from '../components/AccountMenu/AccountMenuEntry.vue' + +export default defineComponent({ + name: 'PublicPageUserMenu', + components: { + AccountMenuEntry, + IconAccount, + NcAvatar, + NcHeaderMenu, + NcNoteCard, + }, + + setup() { + return { + t, + } + }, + + data() { + return { + displayName: getGuestUser().displayName, + } + }, + + computed: { + avatarDescription(): string { + return t('core', 'User menu') + }, + + privacyNotice(): string { + return this.displayName + ? t('core', 'You will be identified as {user} by the account owner.', { user: this.displayName }) + : t('core', 'You are currently not identified.') + }, + }, + + mounted() { + subscribe('user:info:changed', (user: NextcloudUser) => { + this.displayName = user.displayName || '' + }) + }, + + methods: { + setNickname() { + showGuestUserPrompt({ + nickname: this.displayName, + cancellable: true, + }) + }, + }, +}) +</script> + +<style scoped lang="scss"> +.public-page-user-menu { + &, * { + box-sizing: border-box; + } + + // Ensure we do not waste space, as the header menu sets a default width of 350px + :deep(.header-menu__content) { + width: fit-content !important; + } + + &__list-note { + padding-block: 5px !important; + padding-inline: 5px !important; + max-width: 300px; + margin: 5px !important; + margin-bottom: 0 !important; + } + + &__list { + display: inline-flex; + flex-direction: column; + padding-block: var(--default-grid-baseline) 0; + width: 100%; + + > :deep(li) { + box-sizing: border-box; + // basically "fit-content" + flex: 0 1; + } + } +} +</style> |