Revert "Unify workspace dropdowns; Update layouts (#458)"
This reverts commit 4bc8448a1d.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import type { Cms } from '@kit/cms';
|
||||
import { ContentRenderer } from '@kit/cms';
|
||||
|
||||
import { ChangelogHeader } from './changelog-header';
|
||||
import { ChangelogNavigation } from './changelog-navigation';
|
||||
|
||||
interface ChangelogDetailProps {
|
||||
entry: Cms.ContentItem;
|
||||
content: unknown;
|
||||
previousEntry: Cms.ContentItem | null;
|
||||
nextEntry: Cms.ContentItem | null;
|
||||
}
|
||||
|
||||
export function ChangelogDetail({
|
||||
entry,
|
||||
content,
|
||||
previousEntry,
|
||||
nextEntry,
|
||||
}: ChangelogDetailProps) {
|
||||
return (
|
||||
<div>
|
||||
<ChangelogHeader entry={entry} />
|
||||
|
||||
<div className="mx-auto flex max-w-3xl flex-col space-y-6 py-8">
|
||||
<article className="markdoc">
|
||||
<ContentRenderer content={content} />
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<ChangelogNavigation
|
||||
previousEntry={previousEntry}
|
||||
nextEntry={nextEntry}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
import { type Cms } from '@kit/cms';
|
||||
import { If } from '@kit/ui/if';
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
import { DateBadge } from './date-badge';
|
||||
|
||||
interface ChangelogEntryProps {
|
||||
entry: Cms.ContentItem;
|
||||
highlight?: boolean;
|
||||
}
|
||||
|
||||
export function ChangelogEntry({
|
||||
entry,
|
||||
highlight = false,
|
||||
}: ChangelogEntryProps) {
|
||||
const { title, slug, publishedAt, description } = entry;
|
||||
const entryUrl = `/changelog/${slug}`;
|
||||
|
||||
return (
|
||||
<div className="flex gap-6 md:gap-8">
|
||||
<div className="md:border-border relative flex flex-1 flex-col space-y-0 gap-y-2.5 border-l border-dashed border-transparent pb-4 md:pl-8 lg:pl-12">
|
||||
{highlight ? (
|
||||
<span className="absolute top-5.5 left-0 hidden h-2.5 w-2.5 -translate-x-1/2 md:flex">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-red-400 opacity-75"></span>
|
||||
<span className="relative inline-flex h-2.5 w-2.5 rounded-full bg-red-400"></span>
|
||||
</span>
|
||||
) : (
|
||||
<div
|
||||
className={cn(
|
||||
'bg-muted absolute top-5.5 left-0 hidden h-2.5 w-2.5 -translate-x-1/2 rounded-full md:block',
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="hover:bg-muted/50 active:bg-muted rounded-md transition-colors">
|
||||
<Link href={entryUrl} className="block space-y-2 p-4">
|
||||
<div>
|
||||
<DateBadge date={publishedAt} />
|
||||
</div>
|
||||
|
||||
<h3 className="text-xl leading-tight font-semibold tracking-tight group-hover/link:underline">
|
||||
{title}
|
||||
</h3>
|
||||
|
||||
<If condition={description}>
|
||||
{(desc) => (
|
||||
<p className="text-muted-foreground text-sm leading-relaxed">
|
||||
{desc}
|
||||
</p>
|
||||
)}
|
||||
</If>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
|
||||
import { Cms } from '@kit/cms';
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
import { CoverImage } from '../../blog/_components/cover-image';
|
||||
import { DateFormatter } from '../../blog/_components/date-formatter';
|
||||
|
||||
export function ChangelogHeader({ entry }: { entry: Cms.ContentItem }) {
|
||||
const { title, publishedAt, description, image } = entry;
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col">
|
||||
<div className="border-border/50 border-b py-4">
|
||||
<div className="mx-auto flex max-w-3xl items-center justify-between">
|
||||
<Link
|
||||
href="/changelog"
|
||||
className="text-muted-foreground hover:text-primary flex items-center gap-1.5 text-sm font-medium transition-colors"
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
<Trans i18nKey="marketing:changelog" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cn('border-border/50 border-b py-8')}>
|
||||
<div className="mx-auto flex max-w-3xl flex-col gap-y-2.5">
|
||||
<div>
|
||||
<span className="text-muted-foreground text-xs">
|
||||
<DateFormatter dateString={publishedAt} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1 className="font-heading text-2xl font-medium tracking-tighter xl:text-4xl dark:text-white">
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
{description && (
|
||||
<h2
|
||||
className="text-muted-foreground text-base"
|
||||
dangerouslySetInnerHTML={{ __html: description }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<If condition={image}>
|
||||
{(imageUrl) => (
|
||||
<div className="relative mx-auto mt-8 flex h-[378px] w-full max-w-3xl justify-center">
|
||||
<CoverImage
|
||||
preloadImage
|
||||
className="rounded-md"
|
||||
title={title}
|
||||
src={imageUrl}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
|
||||
import type { Cms } from '@kit/cms';
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
import { DateFormatter } from '../../blog/_components/date-formatter';
|
||||
|
||||
interface ChangelogNavigationProps {
|
||||
previousEntry: Cms.ContentItem | null;
|
||||
nextEntry: Cms.ContentItem | null;
|
||||
}
|
||||
|
||||
interface NavLinkProps {
|
||||
entry: Cms.ContentItem;
|
||||
direction: 'previous' | 'next';
|
||||
}
|
||||
|
||||
function NavLink({ entry, direction }: NavLinkProps) {
|
||||
const isPrevious = direction === 'previous';
|
||||
|
||||
const Icon = isPrevious ? ChevronLeft : ChevronRight;
|
||||
const i18nKey = isPrevious
|
||||
? 'marketing:changelogNavigationPrevious'
|
||||
: 'marketing:changelogNavigationNext';
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={`/changelog/${entry.slug}`}
|
||||
className={cn(
|
||||
'border-border/50 hover:bg-muted/50 group flex flex-col gap-2 rounded-lg border p-4 transition-all',
|
||||
!isPrevious && 'text-right md:items-end',
|
||||
)}
|
||||
>
|
||||
<div className="text-muted-foreground flex items-center gap-2 text-xs">
|
||||
{isPrevious && <Icon className="h-3 w-3" />}
|
||||
|
||||
<span className="font-medium tracking-wider uppercase">
|
||||
<Trans i18nKey={i18nKey} />
|
||||
</span>
|
||||
{!isPrevious && <Icon className="h-3 w-3" />}
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<h3 className="group-hover:text-primary text-sm leading-tight font-semibold transition-colors">
|
||||
{entry.title}
|
||||
</h3>
|
||||
|
||||
<div className="text-muted-foreground text-xs">
|
||||
<DateFormatter dateString={entry.publishedAt} />
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export function ChangelogNavigation({
|
||||
previousEntry,
|
||||
nextEntry,
|
||||
}: ChangelogNavigationProps) {
|
||||
return (
|
||||
<div className="border-border/50 border-t py-8">
|
||||
<div className="mx-auto max-w-3xl">
|
||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||
<If condition={previousEntry} fallback={<div />}>
|
||||
{(prev) => <NavLink entry={prev} direction="previous" />}
|
||||
</If>
|
||||
|
||||
<If condition={nextEntry} fallback={<div />}>
|
||||
{(next) => <NavLink entry={next} direction="next" />}
|
||||
</If>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
import { ArrowLeft, ArrowRight } from 'lucide-react';
|
||||
|
||||
import { Button } from '@kit/ui/button';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
interface ChangelogPaginationProps {
|
||||
currentPage: number;
|
||||
canGoToNextPage: boolean;
|
||||
canGoToPreviousPage: boolean;
|
||||
}
|
||||
|
||||
export function ChangelogPagination({
|
||||
currentPage,
|
||||
canGoToNextPage,
|
||||
canGoToPreviousPage,
|
||||
}: ChangelogPaginationProps) {
|
||||
const nextPage = currentPage + 1;
|
||||
const previousPage = currentPage - 1;
|
||||
|
||||
return (
|
||||
<div className="flex justify-end gap-2">
|
||||
{canGoToPreviousPage && (
|
||||
<Button asChild variant="outline" size="sm">
|
||||
<Link href={`/changelog?page=${previousPage}`}>
|
||||
<ArrowLeft className="mr-2 h-3 w-3" />
|
||||
<span>
|
||||
<Trans i18nKey="marketing:changelogPaginationPrevious" />
|
||||
</span>
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{canGoToNextPage && (
|
||||
<Button asChild variant="outline" size="sm">
|
||||
<Link href={`/changelog?page=${nextPage}`}>
|
||||
<span>
|
||||
<Trans i18nKey="marketing:changelogPaginationNext" />
|
||||
</span>
|
||||
<ArrowRight className="ml-2 h-3 w-3" />
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { format } from 'date-fns';
|
||||
import { CalendarIcon } from 'lucide-react';
|
||||
|
||||
interface DateBadgeProps {
|
||||
date: string;
|
||||
}
|
||||
|
||||
export function DateBadge({ date }: DateBadgeProps) {
|
||||
const formattedDate = format(new Date(date), 'MMMM d, yyyy');
|
||||
|
||||
return (
|
||||
<div className="text-muted-foreground flex flex-shrink-0 items-center gap-2 text-sm">
|
||||
<CalendarIcon className="size-3" />
|
||||
<span>{formattedDate}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user