Make headers sticky again (#17850)

Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
This commit is contained in:
Nitwel
2023-04-07 12:29:41 +02:00
committed by GitHub
parent 99e6c7f8fd
commit 88a401ca20
15 changed files with 125 additions and 64 deletions

View File

@@ -25,7 +25,13 @@
</nav>
<main ref="mainEl" class="main">
<header-bar :title="title" primary-action-icon="close" @primary="$emit('cancel')">
<header-bar
:title="title"
primary-action-icon="close"
:small="smallHeader"
:shadow="headerShadow"
@primary="$emit('cancel')"
>
<template #title><slot name="title" /></template>
<template #headline>
<slot name="subtitle">
@@ -74,6 +80,8 @@ interface Props {
icon?: string;
sidebarLabel?: string;
cancelable?: boolean;
headerShadow?: boolean;
smallHeader?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
@@ -83,6 +91,8 @@ const props = withDefaults(defineProps<Props>(), {
icon: 'box',
sidebarLabel: i18n.global.t('sidebar'),
cancelable: true,
headerShadow: true,
smallHeader: false,
});
const emit = defineEmits(['cancel', 'update:modelValue']);

View File

@@ -17,6 +17,7 @@ export default defineLayout<LayoutOptions, LayoutQuery>({
name: '$t:layouts.cards.cards',
icon: 'grid_4',
component: CardsLayout,
headerShadow: false,
slots: {
options: CardsOptions,
sidebar: () => undefined,

View File

@@ -29,6 +29,7 @@ export default defineLayout<LayoutOptions, LayoutQuery>({
sidebar: () => undefined,
actions: TabularActions,
},
headerShadow: false,
setup(props, { emit }) {
const router = useRouter();

View File

@@ -11,7 +11,11 @@
show-select="none"
collection="directus_activity"
>
<private-view :title="t('activity_feed')">
<private-view
:title="t('activity_feed')"
:small-header="currentLayout?.smallHeader"
:header-shadow="currentLayout?.headerShadow"
>
<template #title-outer:prepend>
<v-button class="header-icon" rounded disabled icon secondary>
<v-icon name="access_time" />
@@ -30,7 +34,7 @@
<activity-navigation v-model:filter="roleFilter" />
</template>
<component :is="`layout-${layout}`" v-bind="layoutState" class="layout">
<component :is="`layout-${layout}`" v-bind="layoutState">
<template #no-results>
<v-info :title="t('no_results')" icon="search" center>
{{ t('no_results_copy') }}
@@ -60,15 +64,16 @@
</template>
<script lang="ts">
import { useI18n } from 'vue-i18n';
import { defineComponent, computed, ref } from 'vue';
import ActivityNavigation from '../components/navigation.vue';
import { useExtension } from '@/composables/use-extension';
import { usePreset } from '@/composables/use-preset';
import { useLayout } from '@directus/composables';
import LayoutSidebarDetail from '@/views/private/components/layout-sidebar-detail.vue';
import SearchInput from '@/views/private/components/search-input.vue';
import { useLayout } from '@directus/composables';
import { Filter } from '@directus/types';
import { mergeFilters } from '@directus/utils';
import { computed, defineComponent, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import ActivityNavigation from '../components/navigation.vue';
export default defineComponent({
name: 'ActivityCollection',
@@ -87,6 +92,8 @@ export default defineComponent({
const { layoutWrapper } = useLayout(layout);
const currentLayout = useExtension('layout', layout);
const roleFilter = ref<Filter | null>(null);
return {
@@ -100,6 +107,7 @@ export default defineComponent({
filter,
roleFilter,
mergeFilters,
currentLayout,
};
function useBreadcrumb() {

View File

@@ -19,6 +19,7 @@
v-else
:title="bookmark ? bookmarkTitle : currentCollection.name"
:small-header="currentLayout?.smallHeader"
:header-shadow="currentLayout?.headerShadow"
>
<template #title-outer:prepend>
<v-button class="header-icon" :class="{ archive }" rounded icon secondary disabled>
@@ -202,7 +203,7 @@
</template>
</v-info>
<component :is="`layout-${layout || 'tabular'}`" v-else class="layout" v-bind="layoutState">
<component :is="`layout-${layout || 'tabular'}`" v-else v-bind="layoutState">
<template #no-results>
<v-info :title="t('no_results')" icon="search" center>
{{ t('no_results_copy') }}
@@ -686,10 +687,6 @@ export default defineComponent({
--v-button-color-disabled: var(--foreground-normal);
}
.layout {
--layout-offset-top: 64px;
}
.bookmark-controls {
.add,
.save,

View File

@@ -13,7 +13,12 @@
collection="directus_files"
:reset-preset="resetPreset"
>
<private-view :title="title" :class="{ dragging }">
<private-view
:title="title"
:class="{ dragging }"
:small-header="currentLayout?.smallHeader"
:header-shadow="currentLayout?.headerShadow"
>
<template v-if="breadcrumb" #headline>
<v-breadcrumb :items="breadcrumb" />
</template>
@@ -114,7 +119,7 @@
<files-navigation :current-folder="folder" />
</template>
<component :is="`layout-${layout}`" class="layout" v-bind="layoutState">
<component :is="`layout-${layout}`" v-bind="layoutState">
<template #no-results>
<v-info v-if="!filter && !search" :title="t('file_count', 0)" icon="folder" center>
{{ t('no_files_copy') }}
@@ -185,29 +190,30 @@
</template>
<script lang="ts">
import { useI18n } from 'vue-i18n';
import { defineComponent, computed, ref, PropType, onMounted, onUnmounted, nextTick } from 'vue';
import FilesNavigation from '../components/navigation.vue';
import api from '@/api';
import { usePreset } from '@/composables/use-preset';
import LayoutSidebarDetail from '@/views/private/components/layout-sidebar-detail.vue';
import AddFolder from '../components/add-folder.vue';
import SearchInput from '@/views/private/components/search-input.vue';
import FolderPicker from '@/views/private/components/folder-picker.vue';
import emitter, { Events } from '@/events';
import { useRouter, onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router';
import { useNotificationsStore } from '@/stores/notifications';
import { useUserStore } from '@/stores/user';
import { usePermissionsStore } from '@/stores/permissions';
import { subDays } from 'date-fns';
import { useFolders, Folder } from '@/composables/use-folders';
import { useEventListener } from '@/composables/use-event-listener';
import { useLayout } from '@directus/composables';
import { uploadFiles } from '@/utils/upload-files';
import { useExtension } from '@/composables/use-extension';
import { Folder, useFolders } from '@/composables/use-folders';
import { usePreset } from '@/composables/use-preset';
import emitter, { Events } from '@/events';
import { useNotificationsStore } from '@/stores/notifications';
import { usePermissionsStore } from '@/stores/permissions';
import { useUserStore } from '@/stores/user';
import { unexpectedError } from '@/utils/unexpected-error';
import { uploadFiles } from '@/utils/upload-files';
import DrawerBatch from '@/views/private/components/drawer-batch.vue';
import FolderPicker from '@/views/private/components/folder-picker.vue';
import LayoutSidebarDetail from '@/views/private/components/layout-sidebar-detail.vue';
import SearchInput from '@/views/private/components/search-input.vue';
import { useLayout } from '@directus/composables';
import { Filter } from '@directus/types';
import { mergeFilters } from '@directus/utils';
import { subDays } from 'date-fns';
import { PropType, computed, defineComponent, nextTick, onMounted, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { onBeforeRouteLeave, onBeforeRouteUpdate, useRouter } from 'vue-router';
import AddFolder from '../components/add-folder.vue';
import FilesNavigation from '../components/navigation.vue';
type Item = {
[field: string]: any;
@@ -249,6 +255,8 @@ export default defineComponent({
const { layout, layoutOptions, layoutQuery, filter, search, resetPreset } = usePreset(ref('directus_files'));
const currentLayout = useExtension('layout', layout);
const { confirmDelete, deleting, batchDelete, error: deleteError, batchEditActive } = useBatch();
const { breadcrumb, title } = useBreadcrumb();
@@ -357,6 +365,7 @@ export default defineComponent({
batchEditActive,
filter,
mergeFilters,
currentLayout,
};
function useBatch() {
@@ -668,11 +677,6 @@ export default defineComponent({
.header-icon {
--v-button-color-disabled: var(--foreground-normal);
}
.layout {
--layout-offset-top: 64px;
}
.drop-border {
position: fixed;
z-index: 500;

View File

@@ -13,7 +13,11 @@
:collection="collection"
:clear-filters="clearFilters"
>
<private-view :title="t('settings_presets')" :small-header="currentLayout?.smallHeader">
<private-view
:title="t('settings_presets')"
:small-header="currentLayout?.smallHeader"
:header-shadow="currentLayout?.headerShadow"
>
<template #headline>
<v-breadcrumb :items="[{ name: t('settings'), to: '/settings' }]" />
</template>
@@ -87,7 +91,7 @@
<settings-navigation />
</template>
<component :is="`layout-${layout || 'tabular'}`" class="layout" v-bind="layoutState">
<component :is="`layout-${layout || 'tabular'}`" v-bind="layoutState">
<template #no-results>
<v-info :title="t('no_presets')" icon="bookmark" center>
{{ t('no_presets_copy') }}
@@ -317,8 +321,4 @@ export default defineComponent({
--v-button-background-color-hover: var(--danger) !important;
--v-button-color-hover: var(--white) !important;
}
.layout {
--layout-offset-top: 64px;
}
</style>

View File

@@ -9,7 +9,11 @@
:collection="values.collection"
readonly
>
<private-view :title="t('editing_preset')">
<private-view
:title="t('editing_preset')"
:small-header="currentLayout?.smallHeader"
:header-shadow="currentLayout?.headerShadow"
>
<template #headline>
<v-breadcrumb :items="[{ name: t('settings_presets'), to: '/settings/presets' }]" />
</template>
@@ -145,6 +149,7 @@ import { useShortcut } from '@/composables/use-shortcut';
import { useEditsGuard } from '@/composables/use-edits-guard';
import { isEqual } from 'lodash';
import { useExtensions } from '@/extensions';
import { useExtension } from '@/composables/use-extension';
type FormattedPreset = {
id: number;
@@ -195,6 +200,8 @@ const layoutFilter = computed<any>({
const layout = computed(() => values.value.layout);
const currentLayout = useExtension('layout', layout);
const { layoutWrapper } = useLayout(layout);
useShortcut('meta+s', () => {

View File

@@ -10,7 +10,11 @@
:search="search"
collection="directus_webhooks"
>
<private-view :title="t('webhooks')">
<private-view
:title="t('webhooks')"
:small-header="currentLayout?.smallHeader"
:header-shadow="currentLayout?.headerShadow"
>
<template #headline><v-breadcrumb :items="[{ name: t('settings'), to: '/settings' }]" /></template>
<template #title-outer:prepend>
@@ -56,7 +60,7 @@
</v-button>
</template>
<component :is="`layout-${layout}`" class="layout" v-bind="layoutState">
<component :is="`layout-${layout}`" v-bind="layoutState">
<template #no-results>
<v-info :title="t('no_results')" icon="search" center>
{{ t('no_results_copy') }}
@@ -100,6 +104,7 @@ import { usePreset } from '@/composables/use-preset';
import { useLayout } from '@directus/composables';
import api from '@/api';
import SearchInput from '@/views/private/components/search-input.vue';
import { useExtension } from '@/composables/use-extension';
type Item = {
[field: string]: any;
@@ -120,6 +125,8 @@ export default defineComponent({
const { layoutWrapper } = useLayout(layout);
const currentLayout = useExtension('layout', layout);
return {
t,
addNewLink,
@@ -136,6 +143,7 @@ export default defineComponent({
layout,
search,
clearFilters,
currentLayout,
};
async function refresh() {

View File

@@ -13,7 +13,11 @@
collection="directus_users"
:reset-preset="resetPreset"
>
<private-view :title="title">
<private-view
:title="title"
:small-header="currentLayout?.smallHeader"
:header-shadow="currentLayout?.headerShadow"
>
<template v-if="breadcrumb" #headline>
<v-breadcrumb :items="breadcrumb" />
</template>
@@ -100,7 +104,7 @@
<users-invite v-if="canInviteUsers" v-model="userInviteModalActive" @update:model-value="refresh" />
<component :is="`layout-${layout}`" class="layout" v-bind="layoutState">
<component :is="`layout-${layout}`" v-bind="layoutState">
<template #no-results>
<v-info v-if="!filter && !search" :title="t('user_count', 0)" icon="people_alt" center>
{{ t('no_users_copy') }}
@@ -181,6 +185,7 @@ import { Role } from '@directus/types';
import { mergeFilters } from '@directus/utils';
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router';
import useNavigation from '../composables/use-navigation';
import { useExtension } from '@/composables/use-extension';
type Item = {
[field: string]: any;
@@ -208,6 +213,8 @@ const selection = ref<Item[]>([]);
const { layout, layoutOptions, layoutQuery, filter, search, resetPreset } = usePreset(ref('directus_users'));
const { addNewLink } = useLinks();
const currentLayout = useExtension('layout', layout);
const { confirmDelete, deleting, batchDelete, batchEditActive } = useBatch();
const { breadcrumb, title } = useBreadcrumb();
@@ -369,8 +376,4 @@ function usePermissions() {
.header-icon {
--v-button-color-disabled: var(--foreground-normal);
}
.layout {
--layout-offset-top: 64px;
}
</style>

View File

@@ -11,7 +11,13 @@
select-mode
:show-select="multiple ? 'multiple' : 'one'"
>
<v-drawer v-model="internalActive" :title="t('select_item')" @cancel="cancel">
<v-drawer
v-model="internalActive"
:title="t('select_item')"
:small-header="currentLayout?.smallHeader"
:header-shadow="currentLayout?.headerShadow"
@cancel="cancel"
>
<template #subtitle>
<v-breadcrumb :items="[{ name: collectionInfo.name, disabled: true }]" />
</template>
@@ -32,15 +38,17 @@
</v-button>
</template>
<component :is="`layout-${localLayout}`" class="layout" v-bind="layoutState">
<template #no-results>
<v-info :title="t('item_count', 0)" :icon="collectionInfo.icon" center />
</template>
<div class="layout">
<component :is="`layout-${localLayout}`" v-bind="layoutState">
<template #no-results>
<v-info :title="t('item_count', 0)" :icon="collectionInfo.icon" center />
</template>
<template #no-items>
<v-info :title="t('item_count', 0)" :icon="collectionInfo.icon" center />
</template>
</component>
<template #no-items>
<v-info :title="t('item_count', 0)" :icon="collectionInfo.icon" center />
</template>
</component>
</div>
</v-drawer>
</component>
</template>
@@ -52,6 +60,7 @@ import { Filter } from '@directus/types';
import { usePreset } from '@/composables/use-preset';
import { useCollection, useLayout } from '@directus/composables';
import SearchInput from '@/views/private/components/search-input.vue';
import { useExtension } from '@/composables/use-extension';
export default defineComponent({
components: { SearchInput },
@@ -96,6 +105,8 @@ export default defineComponent({
const localOptions = ref(layoutOptions.value);
const localQuery = ref(layoutQuery.value);
const currentLayout = useExtension('layout', localLayout);
const layoutSelection = computed<any>({
get() {
return internalSelection.value;
@@ -135,6 +146,7 @@ export default defineComponent({
collectionInfo,
search,
presetFilter,
currentLayout,
};
function useActiveState() {
@@ -210,6 +222,7 @@ export default defineComponent({
<style lang="scss" scoped>
.layout {
--layout-offset-top: 0px;
display: contents;
--layout-offset-top: calc(var(--header-bar-height) - 1px);
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<header ref="headerEl" class="header-bar" :class="{ collapsed, small }">
<header ref="headerEl" class="header-bar" :class="{ collapsed, small, shadow }">
<v-button secondary class="nav-toggle" icon rounded @click="$emit('primary')">
<v-icon :name="primaryActionIcon" />
</v-button>
@@ -59,6 +59,10 @@ export default defineComponent({
type: Boolean,
default: false,
},
shadow: {
type: Boolean,
default: true,
},
},
emits: ['primary', 'toggle:sidebar'],
setup() {
@@ -191,7 +195,7 @@ export default defineComponent({
pointer-events: none;
}
&.collapsed {
&.collapsed.shadow {
box-shadow: 0 4px 7px -4px rgb(0 0 0 / 0.2);
.title-container {

View File

@@ -30,6 +30,7 @@
<div id="main-content" ref="contentEl" class="content">
<header-bar
:small="smallHeader"
:shadow="headerShadow"
show-sidebar-toggle
:title="title"
@toggle:sidebar="sidebarOpen = !sidebarOpen"
@@ -98,9 +99,10 @@ import SidebarDetailGroup from './components/sidebar-detail-group.vue';
interface Props {
title?: string | null;
smallHeader?: boolean;
headerShadow?: boolean;
}
const props = withDefaults(defineProps<Props>(), { title: null, smallHeader: false });
const props = withDefaults(defineProps<Props>(), { title: null, smallHeader: false, headerShadow: true });
const { t } = useI18n();
@@ -240,6 +242,7 @@ function openSidebar(event: PointerEvent) {
.private-view {
--content-padding: 12px;
--content-padding-bottom: 60px;
--layout-offset-top: calc(var(--header-bar-height) - 1px);
display: flex;
width: 100%;

View File

@@ -4,3 +4,4 @@
- br41nslug
- licitdev
- bryantgillespie
- Nitwel

View File

@@ -13,6 +13,7 @@ export interface LayoutConfig<Options = any, Query = any> {
actions: Component;
};
smallHeader?: boolean;
headerShadow?: boolean;
setup: (props: LayoutProps<Options, Query>, ctx: LayoutContext) => Record<string, unknown>;
}