fix(posts): merge the editor status into the mobile switcher bar
On mobile the header was a whole bar showing only the Draft/Saving/Saved status. Move that status into the switcher bar (right side) and hide the header on mobile (the scheduled banner still shows), reclaiming a row. The switcher clears the floating hamburger when it's the top bar.
This commit is contained in:
parent
b7351667e2
commit
da5066e43e
2 changed files with 78 additions and 27 deletions
|
|
@ -1,6 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
import { IconCircleCheck, IconLoader2 } from '@tabler/icons-vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { PostStatus } from '@/types/post';
|
||||
|
||||
type MobileView = 'compose' | 'channels' | 'preview' | 'comments';
|
||||
|
||||
const props = defineProps<{
|
||||
isSaving: boolean;
|
||||
showSaved: boolean;
|
||||
status: string;
|
||||
}>();
|
||||
|
||||
const activeView = defineModel<MobileView>('activeView', { required: true });
|
||||
|
||||
const items: { key: MobileView; label: string }[] = [
|
||||
|
|
@ -9,25 +20,55 @@ const items: { key: MobileView; label: string }[] = [
|
|||
{ key: 'preview', label: 'posts.edit.tabs.preview' },
|
||||
{ key: 'comments', label: 'posts.edit.tabs.comments' },
|
||||
];
|
||||
|
||||
const PUBLISHED_STATUSES: readonly string[] = [PostStatus.Published, PostStatus.PartiallyPublished];
|
||||
const isPublished = computed(() => PUBLISHED_STATUSES.includes(props.status));
|
||||
// When not scheduled the header is hidden on mobile, so this nav is the top bar:
|
||||
// it shows the status chip and must clear the floating hamburger.
|
||||
const headerHidden = computed(() => props.status !== PostStatus.Scheduled);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
data-testid="editor-mobile-nav"
|
||||
class="flex shrink-0 gap-1 overflow-x-auto border-b-2 border-foreground bg-card px-2 py-2 lg:hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
||||
class="flex shrink-0 items-center gap-2 border-b-2 border-foreground bg-card px-2 py-2 lg:hidden"
|
||||
>
|
||||
<button
|
||||
v-for="item in items"
|
||||
:key="item.key"
|
||||
type="button"
|
||||
:data-testid="`editor-nav-${item.key}`"
|
||||
class="shrink-0 rounded-lg border-2 px-3 py-1.5 text-sm font-semibold transition-colors"
|
||||
:class="activeView === item.key
|
||||
? 'border-foreground bg-violet-100 text-foreground'
|
||||
: 'border-transparent text-foreground/60 hover:text-foreground'"
|
||||
@click="activeView = item.key"
|
||||
<div
|
||||
class="flex flex-1 gap-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
||||
:class="{ 'pl-14': headerHidden }"
|
||||
>
|
||||
{{ $t(item.label) }}
|
||||
</button>
|
||||
<button
|
||||
v-for="item in items"
|
||||
:key="item.key"
|
||||
type="button"
|
||||
:data-testid="`editor-nav-${item.key}`"
|
||||
class="shrink-0 rounded-lg border-2 px-3 py-1.5 text-sm font-semibold transition-colors"
|
||||
:class="activeView === item.key
|
||||
? 'border-foreground bg-violet-100 text-foreground'
|
||||
: 'border-transparent text-foreground/60 hover:text-foreground'"
|
||||
@click="activeView = item.key"
|
||||
>
|
||||
{{ $t(item.label) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="headerHidden" class="shrink-0 pr-1">
|
||||
<span v-if="isSaving" class="flex items-center gap-1 text-xs font-semibold text-foreground/70">
|
||||
<IconLoader2 class="size-3.5 animate-spin" />
|
||||
{{ $t('posts.edit.saving') }}
|
||||
</span>
|
||||
<span v-else-if="showSaved" class="flex items-center gap-1 text-xs font-semibold text-emerald-700">
|
||||
<IconCircleCheck class="size-3.5" stroke-width="2.5" />
|
||||
{{ $t('posts.edit.saved') }}
|
||||
</span>
|
||||
<span v-else-if="isPublished" class="flex items-center gap-1 text-xs font-semibold text-emerald-700">
|
||||
<IconCircleCheck class="size-3.5" stroke-width="2.5" />
|
||||
{{ $t('posts.edit.status.published') }}
|
||||
</span>
|
||||
<span v-else class="flex items-center gap-1.5 text-xs font-semibold text-foreground/60">
|
||||
<span class="size-2 rounded-full bg-foreground/40" />
|
||||
{{ $t('posts.edit.draft') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -376,24 +376,34 @@ usePostEcho(post.value.id, '.post.comment.created', (e: any) => {
|
|||
|
||||
<AppLayout :full-width="true">
|
||||
<div class="flex flex-col flex-1 min-h-0">
|
||||
<PostEditorHeader
|
||||
:post="post"
|
||||
:can-edit="canCreatePost"
|
||||
<!-- On mobile the status moves into the switcher and the actions into the
|
||||
bottom bar, so the header is desktop-only — except the scheduled banner,
|
||||
which stays as the locked-state explanation. -->
|
||||
<div :class="isScheduled ? 'shrink-0' : 'hidden shrink-0 lg:block'">
|
||||
<PostEditorHeader
|
||||
:post="post"
|
||||
:can-edit="canCreatePost"
|
||||
:is-saving="isSaving"
|
||||
:show-saved="showSaved"
|
||||
:is-submitting="isSubmitting"
|
||||
:is-post-action-disabled="isPostActionDisabled"
|
||||
:post-action-tooltip="postActionTooltip"
|
||||
:pick-time-label="pickTimeLabel"
|
||||
v-model:has-picked-time="hasPickedTime"
|
||||
v-model:scheduled-date-time="scheduledDateTime"
|
||||
@delete="deletePost"
|
||||
@unschedule="unschedulePost"
|
||||
@submit="submit"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PostEditorMobileNav
|
||||
v-model:active-view="mobileView"
|
||||
:is-saving="isSaving"
|
||||
:show-saved="showSaved"
|
||||
:is-submitting="isSubmitting"
|
||||
:is-post-action-disabled="isPostActionDisabled"
|
||||
:post-action-tooltip="postActionTooltip"
|
||||
:pick-time-label="pickTimeLabel"
|
||||
v-model:has-picked-time="hasPickedTime"
|
||||
v-model:scheduled-date-time="scheduledDateTime"
|
||||
@delete="deletePost"
|
||||
@unschedule="unschedulePost"
|
||||
@submit="submit"
|
||||
:status="post.status"
|
||||
/>
|
||||
|
||||
<PostEditorMobileNav v-model:active-view="mobileView" />
|
||||
|
||||
<div class="relative flex-1 overflow-hidden">
|
||||
<div
|
||||
v-if="isPublishing"
|
||||
|
|
|
|||
Loading…
Reference in a new issue