fix(ui): stack page headers and toolbars on mobile
Non-wrapping justify-between headers now stack below sm; fixed-width searches go full-width. Calendar header wraps and seeds isMobile at setup to avoid the month-grid flash.
This commit is contained in:
parent
ccf888258d
commit
49282d2acb
8 changed files with 28 additions and 25 deletions
|
|
@ -83,7 +83,7 @@ const changeRole = (member: Member, role: string) => {
|
|||
|
||||
<template>
|
||||
<div class="flex flex-col space-y-6">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<HeadingSmall
|
||||
:title="$t('settings.workspace.members_heading')"
|
||||
:description="$t('settings.workspace.members_description')"
|
||||
|
|
@ -115,7 +115,7 @@ const changeRole = (member: Member, role: string) => {
|
|||
<TableCell>
|
||||
<DropdownMenu v-if="canManageTeam && member.id !== currentUserId">
|
||||
<DropdownMenuTrigger as-child>
|
||||
<Button variant="outline" size="icon" class="size-8">
|
||||
<Button variant="outline" size="icon" class="size-9">
|
||||
<IconDots class="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ const platformLabel = (platform: string): string => platform.charAt(0).toUpperCa
|
|||
<template>
|
||||
<AutomationDetailLayout :automation="automation" current="metrics">
|
||||
<div class="space-y-6 p-4">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<h2 class="text-sm font-semibold text-foreground/70">{{ $t('automations.metrics.overview') }}</h2>
|
||||
<DateRangePicker v-model="dateRange" dusk="metrics-range" />
|
||||
</div>
|
||||
|
|
@ -85,9 +85,9 @@ const platformLabel = (platform: string): string => platform.charAt(0).toUpperCa
|
|||
</div>
|
||||
|
||||
<div class="rounded-xl border-2 border-foreground/10 bg-card p-4">
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<div class="mb-3 flex flex-wrap items-center justify-between gap-2">
|
||||
<h3 class="text-sm font-semibold">{{ $t('automations.metrics.runs_over_time') }}</h3>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<div v-for="item in legend" :key="item.key" class="flex items-center gap-1.5">
|
||||
<span class="size-2.5 rounded-full" :style="{ backgroundColor: item.color }"></span>
|
||||
<span class="text-xs text-foreground/60">{{ $t(item.label) }}</span>
|
||||
|
|
|
|||
|
|
@ -85,13 +85,13 @@ const hasActiveSearch = computed(() => Boolean(searchQuery.value?.trim()));
|
|||
<div class="flex h-full flex-1 flex-col gap-6 px-6 py-8">
|
||||
<PageHeader :title="$t('labels.title')" :description="$t('labels.description')" />
|
||||
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="relative">
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div class="relative w-full sm:w-auto">
|
||||
<IconSearch class="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
|
||||
<Input
|
||||
v-model="searchQuery"
|
||||
:placeholder="trans('labels.search')"
|
||||
class="w-64 pl-9"
|
||||
class="w-full pl-9 sm:w-64"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -52,8 +52,9 @@ interface Props {
|
|||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
// Mobile detection
|
||||
const isMobile = ref(false);
|
||||
// Mobile detection — seed from the viewport at setup (available on client render
|
||||
// and SPA navigations) so a narrow screen never paints the month grid for a frame.
|
||||
const isMobile = ref(typeof window !== 'undefined' ? window.innerWidth < 1024 : false);
|
||||
const { canCreatePost } = useWorkspaceRole();
|
||||
|
||||
const createPostUrl = (isoDate: string | null = null) =>
|
||||
|
|
@ -253,7 +254,7 @@ const formatTime = (scheduledAt: string): string => {
|
|||
|
||||
<AppLayout :fullWidth="true">
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="grid shrink-0 grid-cols-[auto_1fr_auto] items-center gap-3 border-b-2 border-foreground bg-card px-4 py-3 md:px-6">
|
||||
<header class="flex shrink-0 flex-wrap items-center justify-between gap-3 border-b-2 border-foreground bg-card px-4 py-3 md:grid md:grid-cols-[auto_1fr_auto] md:px-6">
|
||||
<div class="flex items-center gap-2 pl-12 md:pl-0">
|
||||
<Button variant="outline" size="icon" @click="navigate(-1)">
|
||||
<IconChevronLeft class="size-4" />
|
||||
|
|
@ -264,14 +265,16 @@ const formatTime = (scheduledAt: string): string => {
|
|||
<Button variant="outline" size="icon" @click="navigate(1)">
|
||||
<IconChevronRight class="size-4" />
|
||||
</Button>
|
||||
<DatePicker v-if="isMobile" v-model="selectedDate" @update:model-value="(v: any) => goToDate(v)" />
|
||||
</div>
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="hidden items-center justify-center md:flex">
|
||||
<span class="truncate text-sm font-bold capitalize text-foreground">
|
||||
{{ headerTitle }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div v-if="isMobile" class="w-44">
|
||||
<DatePicker v-model="selectedDate" :show-time="false" @update:model-value="(v: any) => goToDate(v)" />
|
||||
</div>
|
||||
<Tabs v-if="!isMobile" :default-value="view" @update:model-value="switchView">
|
||||
<TabsList>
|
||||
<TabsTrigger value="day">{{ $t('calendar.day') }}</TabsTrigger>
|
||||
|
|
|
|||
|
|
@ -183,14 +183,14 @@ useWorkspaceEcho(
|
|||
<PageHeader :title="pageTitle" />
|
||||
|
||||
<!-- Toolbar -->
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="relative">
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div class="flex w-full items-center gap-3 sm:w-auto">
|
||||
<div class="relative w-full sm:w-auto">
|
||||
<IconSearch class="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-foreground/60" />
|
||||
<Input
|
||||
v-model="searchQuery"
|
||||
:placeholder="trans('posts.search')"
|
||||
class="w-64 pl-9"
|
||||
class="w-full pl-9 sm:w-64"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ const tabs = computed(() => [
|
|||
|
||||
<SettingsTabsNav :tabs="tabs" active="api-keys" />
|
||||
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<HeadingSmall
|
||||
:title="$t('settings.api_keys.heading')"
|
||||
:description="$t('settings.api_keys.description')"
|
||||
|
|
@ -128,7 +128,7 @@ const tabs = computed(() => [
|
|||
<TableCell>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<Button variant="outline" size="icon" class="size-8">
|
||||
<Button variant="outline" size="icon" class="size-9">
|
||||
<IconDots class="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
|
|
|||
|
|
@ -91,13 +91,13 @@ const hasActiveSearch = computed(() => Boolean(searchQuery.value?.trim()));
|
|||
<div class="flex h-full flex-1 flex-col gap-6 px-6 py-8">
|
||||
<PageHeader :title="$t('signatures.title')" />
|
||||
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="relative">
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div class="relative w-full sm:w-auto">
|
||||
<IconSearch class="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
|
||||
<Input
|
||||
v-model="searchQuery"
|
||||
:placeholder="trans('signatures.search')"
|
||||
class="w-64 pl-9"
|
||||
class="w-full pl-9 sm:w-64"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -130,7 +130,7 @@ const hasActiveSearch = computed(() => Boolean(searchQuery.value?.trim()));
|
|||
@click="openEditDialog(signature)"
|
||||
>
|
||||
<TableCell>{{ signature.name }}</TableCell>
|
||||
<TableCell class="max-w-md">
|
||||
<TableCell class="max-w-[160px] sm:max-w-md">
|
||||
<p class="truncate">{{ signature.content }}</p>
|
||||
</TableCell>
|
||||
<TableCell>{{ formatDate(signature.created_at) }}</TableCell>
|
||||
|
|
|
|||
|
|
@ -41,14 +41,14 @@ defineProps<Props>();
|
|||
|
||||
<AppLayout :title="workspace.name">
|
||||
<div class="flex flex-col gap-6 p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold tracking-tight">{{ workspace.name }}</h1>
|
||||
<p class="text-muted-foreground">
|
||||
Workspace dashboard
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<Link :href="settings.url()">
|
||||
<Button variant="ghost" size="icon">
|
||||
<IconSettings class="h-4 w-4" />
|
||||
|
|
|
|||
Loading…
Reference in a new issue