mirror of
https://github.com/nextcloud/server.git
synced 2026-06-11 09:42:09 -04:00
141 lines
3.9 KiB
Vue
141 lines
3.9 KiB
Vue
<!--
|
|
- 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"
|
|
variant="primary"
|
|
@click="openDialogIfNeeded">
|
|
<template v-if="primaryAction.icon" #icon>
|
|
<div class="icon public-page-menu__primary-icon" :class="[primaryAction.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 { loadState } from '@nextcloud/initial-state'
|
|
import { t } from '@nextcloud/l10n'
|
|
import { useIsSmallMobile } from '@nextcloud/vue/composables/useIsMobile'
|
|
import { spawnDialog } from '@nextcloud/vue/functions/dialog'
|
|
import {
|
|
type Ref,
|
|
|
|
computed,
|
|
ref,
|
|
} from 'vue'
|
|
import NcButton from '@nextcloud/vue/components/NcButton'
|
|
import NcHeaderMenu from '@nextcloud/vue/components/NcHeaderMenu'
|
|
import IconMore from 'vue-material-design-icons/DotsHorizontal.vue'
|
|
import PublicPageMenuCustomEntry from '../components/PublicPageMenu/PublicPageMenuCustomEntry.vue'
|
|
import PublicPageMenuEntry from '../components/PublicPageMenu/PublicPageMenuEntry.vue'
|
|
import PublicPageMenuExternalDialog from '../components/PublicPageMenu/PublicPageMenuExternalDialog.vue'
|
|
import PublicPageMenuExternalEntry from '../components/PublicPageMenu/PublicPageMenuExternalEntry.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>
|