Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions packages/chronicle/src/components/api/ApiSkeleton.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
.layout {
padding-left: var(--rs-space-9);
padding-right: var(--rs-space-9);
width: 100%;
}

.left {
min-width: 0;
flex: 0 1 545px;
}

.right {
min-width: 376px;
max-width: 500px;
width: 100%;
}

.methodBar {
padding: var(--rs-space-3) 0;
}

.fieldRow {
padding: var(--rs-space-3) 0;
}

.codeBlock {
background: var(--rs-color-background-neutral-secondary);
border-radius: var(--rs-radius-4);
padding: var(--rs-space-5);
min-height: 180px;
}

.responseBlock {
background: var(--rs-color-background-neutral-secondary);
border-radius: var(--rs-radius-4);
padding: var(--rs-space-5);
min-height: 120px;
}

.sidebarGroup {
margin-top: var(--rs-space-8);
width: 100%;
padding: 0 var(--rs-space-3);
}

.sidebarGroup:first-child {
margin-top: 0;
}

.sidebarItem {
padding: var(--rs-space-3) 0;
}

@media (max-width: 1100px) {
.layout {
flex-direction: column;
gap: var(--rs-space-9);
}

.left,
.right {
min-width: 0;
max-width: 100%;
width: 100%;
}
}

@media (max-width: 768px) {
.layout {
gap: var(--rs-space-5);
padding-left: var(--rs-space-5);
padding-right: var(--rs-space-5);
}
}
105 changes: 105 additions & 0 deletions packages/chronicle/src/components/api/ApiSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { Skeleton, Flex, Sidebar } from '@raystack/apsara';
import { cx } from 'class-variance-authority';
import styles from './ApiSkeleton.module.css';
import layoutStyles from '@/themes/default/Layout.module.css';
import apiLayoutStyles from '@/pages/ApiLayout.module.css';

export function ApiPageSkeleton() {
return (
<Flex align="start" justify="between" className={styles.layout}>
<Flex direction="column" gap={9} className={styles.left}>
<Flex direction="column" gap={7}>
<Flex direction="column" gap={4}>
<Skeleton width="40%" height="var(--rs-line-height-t3)" />
<Skeleton width="60%" height="var(--rs-line-height-regular)" />
</Flex>
<Flex align="center" gap={3} className={styles.methodBar}>
<Skeleton width="48px" height="24px" />
<Skeleton width="200px" height="var(--rs-line-height-regular)" />
</Flex>
</Flex>

{[0, 1, 2].map(section => (
<Flex direction="column" gap={4} key={section}>
<Skeleton width="120px" height="var(--rs-line-height-small)" />
{[0, 1, 2, 3].map(row => (
<Flex align="center" gap={4} className={styles.fieldRow} key={row}>
<Skeleton width="80px" height="var(--rs-line-height-small)" />
<Skeleton width="60px" height="var(--rs-line-height-small)" />
</Flex>
))}
</Flex>
))}
</Flex>

<Flex direction="column" gap={8} className={styles.right}>
<Flex direction="column" gap={3} className={styles.codeBlock}>
<Skeleton width="50%" height="var(--rs-line-height-small)" />
{[0, 1, 2, 3, 4].map(i => (
<Skeleton key={i} width={`${70 + (i % 3) * 10}%`} height="var(--rs-line-height-small)" />
))}
</Flex>
<Flex direction="column" gap={3} className={styles.responseBlock}>
<Skeleton width="40%" height="var(--rs-line-height-small)" />
{[0, 1, 2].map(i => (
<Skeleton key={i} width={`${60 + (i % 2) * 20}%`} height="var(--rs-line-height-small)" />
))}
</Flex>
</Flex>
</Flex>
);
}

function SidebarSkeleton() {
return (
<>
{[0, 1, 2].map(group => (
<Flex direction="column" gap={3} className={styles.sidebarGroup} key={group}>
<Skeleton width="80px" height="var(--rs-line-height-small)" />
{[0, 1, 2, 3].map(item => (
<Flex align="center" gap={3} className={styles.sidebarItem} key={item}>
<Skeleton width="100%" height="var(--rs-line-height-small)" />
</Flex>
))}
</Flex>
))}
</>
);
}

export function ApiFullSkeleton() {
return (
<Flex direction="column" className={cx(layoutStyles.layout, apiLayoutStyles.layout)}>
<Flex className={layoutStyles.body}>
<Sidebar
defaultOpen
collapsible={false}
className={cx(layoutStyles.sidebar, apiLayoutStyles.sidebar)}
>
<Sidebar.Header className={layoutStyles.sidebarHeader}>
<Skeleton width="100px" height="28px" />
</Sidebar.Header>
<Sidebar.Main className={layoutStyles.sidebarMain}>
<SidebarSkeleton />
</Sidebar.Main>
</Sidebar>
<Flex direction="column" className={layoutStyles.mainArea}>
<div className={layoutStyles.cardWrapper}>
<div className={layoutStyles.card}>
<nav className={layoutStyles.subNav}>
<Flex align="center" gap={3}>
<Skeleton width="24px" height="24px" />
<Skeleton width="24px" height="24px" />
<Skeleton width="150px" height="var(--rs-line-height-small)" />
</Flex>
</nav>
<main className={cx(layoutStyles.content, apiLayoutStyles.content)}>
<ApiPageSkeleton />
</main>
</div>
</div>
</Flex>
</Flex>
</Flex>
);
}
1 change: 1 addition & 0 deletions packages/chronicle/src/components/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { ApiResponsePanel } from './api-response-panel'
export { PlaygroundDialog } from './playground-dialog'
export { MethodBadge } from './method-badge'
export { JsonEditor } from './json-editor'
export { ApiPageSkeleton, ApiFullSkeleton } from './ApiSkeleton'
6 changes: 5 additions & 1 deletion packages/chronicle/src/pages/ApiLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { cx } from 'class-variance-authority';
import type { ReactNode } from 'react';
import { ApiFullSkeleton } from '@/components/api/ApiSkeleton';
import { buildApiPageTree } from '@/lib/api-routes';
import { usePageContext } from '@/lib/page-context';
import { getTheme } from '@/themes/registry';
Expand All @@ -10,7 +11,10 @@ interface ApiLayoutProps {
}

export function ApiLayout({ children }: ApiLayoutProps) {
const { config, apiSpecs } = usePageContext();
const { config, apiSpecs, isLoading } = usePageContext();

if (isLoading) return <ApiFullSkeleton />;

const { Layout, className } = getTheme(config.theme?.name);
const tree = buildApiPageTree(apiSpecs);

Expand Down
5 changes: 4 additions & 1 deletion packages/chronicle/src/pages/ApiPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { OpenAPIV3 } from 'openapi-types';
import { Navigate } from 'react-router';
import { ApiOverview } from '@/components/api';
import { ApiPageSkeleton } from '@/components/api/ApiSkeleton';
import { findApiOperation, getFirstApiUrl } from '@/lib/api-routes';
import { Head } from '@/lib/head';
import { usePageContext } from '@/lib/page-context';
Expand All @@ -10,7 +11,9 @@ interface ApiPageProps {
}

export function ApiPage({ slug }: ApiPageProps) {
const { config, apiSpecs } = usePageContext();
const { config, apiSpecs, isLoading } = usePageContext();

if (isLoading) return <ApiPageSkeleton />;

if (slug.length === 0) {
const firstUrl = getFirstApiUrl(apiSpecs);
Expand Down
Loading