aboutsummaryrefslogtreecommitdiffstats
path: root/core/src/views
diff options
context:
space:
mode:
authorFerdinand Thiessen <opensource@fthiessen.de>2024-08-28 13:10:25 +0200
committerFerdinand Thiessen <opensource@fthiessen.de>2024-09-03 16:07:49 +0200
commit04b25ba59d48ea3ab1d34d9f05ede33fc7ad7fbc (patch)
treed16c43eaeea478254009d7d2a87b75af0bc553ce /core/src/views
parent5118f6684bd2f4f8d693ada710e845b6af5c5395 (diff)
downloadnextcloud-server-04b25ba59d48ea3ab1d34d9f05ede33fc7ad7fbc.tar.gz
nextcloud-server-04b25ba59d48ea3ab1d34d9f05ede33fc7ad7fbc.zip
feat: Implement Vue UI for public page menu
This adds a Vue implementation of the public page menu, that is the menu that can be added using `PublicTemplateResponse::setHeaderActions`. Co-authored-by: Ferdinand Thiessen <opensource@fthiessen.de> Co-authored-by: Louis <louis@chmn.me> Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Diffstat (limited to 'core/src/views')
-rw-r--r--core/src/views/PublicPageMenu.vue131
1 files changed, 131 insertions, 0 deletions
diff --git a/core/src/views/PublicPageMenu.vue b/core/src/views/PublicPageMenu.vue
new file mode 100644
index 00000000000..a9ff78a7c5f
--- /dev/null
+++ b/core/src/views/PublicPageMenu.vue
@@ -0,0 +1,131 @@
+<!--
+ - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+ -->
+<template>
+ <div class="public-page-menu__wrapper">
+ <NcButton v-if="primaryAction"
+ id="public-page-menu--primary"
+ class="public-page-menu__primary"
+ :href="primaryAction.href"
+ type="primary"
+ @click="openDialogIfNeeded">
+ <template v-if="primaryAction.icon" #icon>
+ <div :class="['icon', primaryAction.icon, 'public-page-menu__primary-icon']" />
+ </template>
+ {{ primaryAction.label }}
+ </NcButton>
+
+ <NcHeaderMenu v-if="secondaryActions.length > 0"
+ id="public-page-menu"
+ :aria-label="t('core', 'More actions')"
+ :open.sync="showMenu">
+ <template #trigger>
+ <IconMore :size="20" />
+ </template>
+ <ul :aria-label="t('core', 'More actions')"
+ class="public-page-menu"
+ role="menu">
+ <component :is="getComponent(entry)"
+ v-for="entry, index in secondaryActions"
+ :key="index"
+ v-bind="entry"
+ @click="showMenu = false" />
+ </ul>
+ </NcHeaderMenu>
+ </div>
+</template>
+
+<script setup lang="ts">
+import { spawnDialog } from '@nextcloud/dialogs'
+import { loadState } from '@nextcloud/initial-state'
+import { t } from '@nextcloud/l10n'
+import { useIsSmallMobile } from '@nextcloud/vue/dist/Composables/useIsMobile.js'
+import { computed, ref, type Ref } from 'vue'
+import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
+import NcHeaderMenu from '@nextcloud/vue/dist/Components/NcHeaderMenu.js'
+import IconMore from 'vue-material-design-icons/DotsHorizontal.vue'
+import PublicPageMenuEntry from '../components/PublicPageMenu/PublicPageMenuEntry.vue'
+import PublicPageMenuCustomEntry from '../components/PublicPageMenu/PublicPageMenuCustomEntry.vue'
+import PublicPageMenuExternalEntry from '../components/PublicPageMenu/PublicPageMenuExternalEntry.vue'
+import PublicPageMenuExternalDialog from '../components/PublicPageMenu/PublicPageMenuExternalDialog.vue'
+import PublicPageMenuLinkEntry from '../components/PublicPageMenu/PublicPageMenuLinkEntry.vue'
+
+interface IPublicPageMenu {
+ id: string
+ label: string
+ href: string
+ icon?: string
+ html?: string
+ details?: string
+}
+
+const menuEntries = loadState<Array<IPublicPageMenu>>('core', 'public-page-menu')
+
+/** used to conditionally close the menu when clicking entry */
+const showMenu = ref(false)
+
+const isMobile = useIsSmallMobile() as Readonly<Ref<boolean>>
+/** The primary menu action - only showed when not on mobile */
+const primaryAction = computed(() => isMobile.value ? undefined : menuEntries[0])
+/** All other secondary actions (including primary action on mobile) */
+const secondaryActions = computed(() => isMobile.value ? menuEntries : menuEntries.slice(1))
+
+/**
+ * Get the render component for an entry
+ * @param entry The entry to get the component for
+ */
+function getComponent(entry: IPublicPageMenu) {
+ if ('html' in entry) {
+ return PublicPageMenuCustomEntry
+ }
+ switch (entry.id) {
+ case 'save':
+ return PublicPageMenuExternalEntry
+ case 'directLink':
+ return PublicPageMenuLinkEntry
+ default:
+ return PublicPageMenuEntry
+ }
+}
+
+/**
+ * Open the "federated share" dialog if needed
+ */
+function openDialogIfNeeded() {
+ if (primaryAction.value?.id !== 'save') {
+ return
+ }
+ spawnDialog(PublicPageMenuExternalDialog, { label: primaryAction.value.label })
+}
+</script>
+
+<style scoped lang="scss">
+.public-page-menu {
+ box-sizing: border-box;
+
+ > :deep(*) {
+ box-sizing: border-box;
+ }
+
+ &__wrapper {
+ display: flex;
+ flex-direction: row;
+ gap: var(--default-grid-baseline);
+ }
+
+ &__primary {
+ height: var(--default-clickable-area);
+ margin-block: calc((var(--header-height) - var(--default-clickable-area)) / 2);
+
+ // Ensure the correct focus-visible color is used (as this is rendered directly on the background(-image))
+ &:focus-visible {
+ border-color: var(--color-background-plain-text) !important;
+ }
+ }
+
+ &__primary-icon {
+ filter: var(--primary-invert-if-bright);
+ }
+}
+</style>