diff --git a/.gitignore b/.gitignore index 81ca99ed4..a0c91f387 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ dist .DS_Store *.pem +www-old/ + # debug npm-debug.log* yarn-debug.log* @@ -37,4 +39,4 @@ yarn-error.log* .vercel # vscode plugin -*.vsix \ No newline at end of file +*.vsix diff --git a/apps/www/.gitignore b/apps/www/.gitignore index 55a12ae71..c09047d52 100644 --- a/apps/www/.gitignore +++ b/apps/www/.gitignore @@ -1,28 +1,44 @@ -# deps -/node_modules +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. -# generated content -.contentlayer -.content-collections -.source +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions -# test & build +# testing /coverage + +# next.js /.next/ /out/ + +# production /build -*.tsbuildinfo # misc .DS_Store *.pem -/.pnp -.pnp.js + +# debug npm-debug.log* yarn-debug.log* yarn-error.log* +.pnpm-debug.log* -# others -.env*.local +# env files (can opt-in for committing if needed) +.env* + +# vercel .vercel -next-env.d.ts \ No newline at end of file + +# typescript +*.tsbuildinfo +next-env.d.ts + +# fumadocs source +.source diff --git a/apps/www/README.md b/apps/www/README.md index e1a157820..e215bc4cc 100644 --- a/apps/www/README.md +++ b/apps/www/README.md @@ -1,26 +1,36 @@ -# fuma-base +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). -This is a Next.js application generated with -[Create Fumadocs](https://github.com/fuma-nama/fumadocs). +## Getting Started -Run development server: +First, run the development server: ```bash npm run dev # or +yarn dev +# or pnpm dev # or -yarn dev +bun dev ``` -Open http://localhost:3000 with your browser to see the result. +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. ## Learn More -To learn more about Next.js and Fumadocs, take a look at the following -resources: +To learn more about Next.js, take a look at the following resources: -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js - features and API. +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. -- [Fumadocs](https://fumadocs.vercel.app) - learn about Fumadocs + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/apps/www/next.config.mjs b/apps/www/next.config.mjs index 20a35b725..fce83cf28 100644 --- a/apps/www/next.config.mjs +++ b/apps/www/next.config.mjs @@ -1,7 +1,5 @@ import { createMDX } from 'fumadocs-mdx/next'; -const withMDX = createMDX(); - /** @type {import('next').NextConfig} */ const config = { reactStrictMode: true, @@ -12,11 +10,6 @@ const config = { // !! WARN !! ignoreBuildErrors: true }, - eslint: { - // Warning: This allows production builds to successfully complete even if - // your project has ESLint errors. - ignoreDuringBuilds: true - }, experimental: { optimizePackageImports: ['shiki'] }, @@ -31,7 +24,19 @@ const config = { destination: '/llms.mdx/:path*' } ]; + }, + async redirects() { + // TODO: remove this once we have a proper home page + return [ + { + source: '/', + destination: '/docs', + permanent: true + } + ]; } }; +const withMDX = createMDX(); + export default withMDX(config); diff --git a/apps/www/package.json b/apps/www/package.json index 316a76861..1cf03b2aa 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -1,29 +1,31 @@ { "name": "www", - "version": "1.0.0", + "version": "0.1.0", "private": true, "scripts": { "prebuild": "cd ../.. && npm run build:apsara", - "build": "next build", "dev": "next dev", + "build": "next build", "start": "next start", "postinstall": "fumadocs-mdx" }, "dependencies": { + "@radix-ui/react-icons": "^1.3.2", "@raystack/apsara": "workspace:*", + "@types/mdast": "^4.0.4", "class-variance-authority": "^0.7.1", "dayjs": "^1.11.11", - "fumadocs-core": "^14.7.7", + "fumadocs-core": "16.0.7", "fumadocs-docgen": "^1.3.8", - "fumadocs-mdx": "^11.5.6", + "fumadocs-mdx": "13.0.5", "fumadocs-typescript": "^4.0.6", - "fumadocs-ui": "^14.7.7", - "lucide-react": "^0.477.0", - "next": "14.2.5", + "fumadocs-ui": "16.0.7", + "lucide-react": "^0.548.0", + "next": "16.0.7", "next-themes": "^0.4.4", "prettier": "^2.8.8", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.2.1", + "react-dom": "^19.2.1", "react-live": "^4.1.8", "remark": "^15.0.1", "remark-gfm": "^4.0.1", @@ -32,15 +34,11 @@ "zod": "^3.24.2" }, "devDependencies": { - "@types/mdast": "^4.0.4", "@types/mdx": "^2.0.13", - "@types/node": "22.13.4", - "@types/prettier": "^3.0.0", - "@types/react": "^18.0.28", - "@types/react-dom": "^18.0.11", - "eslint": "^8", - "eslint-config-next": "14", - "typescript": "^5.7.3" + "@types/node": "^24.9.2", + "@types/react": "^19.2.2", + "@types/react-dom": "^19.2.2", + "typescript": "^5.9.3" }, "engines": { "node": ">=22" diff --git a/apps/www/public/assets/logo.png b/apps/www/public/assets/logo.png index 61cad6a69..c4be1ab12 100644 Binary files a/apps/www/public/assets/logo.png and b/apps/www/public/assets/logo.png differ diff --git a/apps/www/public/assets/logo.svg b/apps/www/public/assets/logo.svg deleted file mode 100644 index a678b0b18..000000000 --- a/apps/www/public/assets/logo.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/source.config.ts b/apps/www/source.config.ts index d5852b227..1e0978753 100644 --- a/apps/www/source.config.ts +++ b/apps/www/source.config.ts @@ -1,11 +1,19 @@ import * as path from 'node:path'; import { fileURLToPath } from 'url'; -import { TagSchema } from '@/lib/types'; -import { remarkInstall } from 'fumadocs-docgen'; +import { SourceSchema, TagSchema } from '@/lib/types'; +import { + rehypeToc, + remarkGfm, + remarkHeading, + remarkImage, + remarkNpm, + remarkStructure +} from 'fumadocs-core/mdx-plugins'; import { defineConfig, defineDocs, - frontmatterSchema + frontmatterSchema, + metaSchema } from 'fumadocs-mdx/config'; import { GeneratorOptions, @@ -28,13 +36,32 @@ export const docs = defineDocs({ dir: 'src/content/docs', docs: { schema: frontmatterSchema.extend({ + source: SourceSchema, tag: TagSchema - }) + }), + postprocess: { + includeProcessedMarkdown: true + } + }, + meta: { + schema: metaSchema } }); export default defineConfig({ mdxOptions: { - remarkPlugins: [remarkInstall, [remarkAutoTypeTable, { generator }]] + remarkPlugins: () => { + return [ + remarkGfm, + [remarkHeading, { generateToc: false }], + [remarkImage, { useImport: undefined }], + // remarkCodeTab, + remarkNpm, + remarkStructure, + // remarkInstall, + [remarkAutoTypeTable, { generator }] + ]; + }, + rehypePlugins: () => [rehypeToc] } }); diff --git a/apps/www/src/app/(home)/page.tsx b/apps/www/src/app/(home)/page.tsx index 4544dddb8..c4cc58d48 100644 --- a/apps/www/src/app/(home)/page.tsx +++ b/apps/www/src/app/(home)/page.tsx @@ -1,17 +1,15 @@ -import Logo from "@/components/logo"; -import { Card } from "fumadocs-ui/components/card"; -import { Notebook, Paintbrush } from "lucide-react"; -import styles from "./page.module.css"; +import Logo from '@/components/logo'; +import styles from './page.module.css'; export const metadata = { - title: "Apsara", + title: 'Apsara' }; export default function HomePage() { return (
- +

The design system
for the next big thing @@ -21,22 +19,6 @@ export default function HomePage() { built using Radix UI.

-
- } - href="/docs" - className={styles.card} - /> - } - href="/playground" - className={styles.card} - /> -
); } diff --git a/apps/www/src/app/api/search/route.ts b/apps/www/src/app/api/search/route.ts index 570732244..b7ff59245 100644 --- a/apps/www/src/app/api/search/route.ts +++ b/apps/www/src/app/api/search/route.ts @@ -1,4 +1,6 @@ -import { docs } from "@/lib/source"; -import { createFromSource } from "fumadocs-core/search/server"; +import { docs } from '@/lib/source'; +import { createFromSource } from 'fumadocs-core/search/server'; -export const { GET } = createFromSource(docs); +export const { GET } = createFromSource(docs, { + language: 'english' +}); diff --git a/apps/www/src/app/docs/[[...slug]]/page.module.css b/apps/www/src/app/docs/[[...slug]]/page.module.css index eabd8b9c3..8d4514d70 100644 --- a/apps/www/src/app/docs/[[...slug]]/page.module.css +++ b/apps/www/src/app/docs/[[...slug]]/page.module.css @@ -1,16 +1,14 @@ -.title { - display: flex; - gap: 12px; - align-items: center; -} -.actions { - display: flex; - align-items: center; +.content { + max-width: 806px; + padding: var(--rs-space-15) var(--rs-space-7); + width: 100%; } -.container { - display: flex; - gap: 12px; - align-items: center; - justify-content: space-between; +:global(.prose) { + gap: var(--rs-space-3); + color: var(--rs-color-foreground-base-secondary); + font-size: var(--rs-font-size-regular); + font-weight: var(--rs-font-weight-regular); + line-height: var(--rs-line-height-regular); + letter-spacing: var(--rs-letter-spacing-regular); } diff --git a/apps/www/src/app/docs/[[...slug]]/page.tsx b/apps/www/src/app/docs/[[...slug]]/page.tsx index df2256354..8e4430085 100644 --- a/apps/www/src/app/docs/[[...slug]]/page.tsx +++ b/apps/www/src/app/docs/[[...slug]]/page.tsx @@ -1,61 +1,80 @@ -import { LLMCopyButton, ViewOptions } from '@/components/ai/page-actions'; -import Demo from '@/components/demo'; -import Tag from '@/components/tag'; +import DocsNavbar from '@/components/docs/navbar'; +import { mdxComponents } from '@/components/mdx'; +import TableOfContents from '@/components/toc/toc'; import { docs } from '@/lib/source'; -import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; -import { TypeTable } from 'fumadocs-ui/components/type-table'; -import defaultMdxComponents from 'fumadocs-ui/mdx'; -import { - DocsBody, - DocsDescription, - DocsPage, - DocsTitle -} from 'fumadocs-ui/page'; +import { Flex, Headline, Text } from '@raystack/apsara'; +import { createRelativeLink } from 'fumadocs-ui/mdx'; +import type { Metadata } from 'next'; import { notFound } from 'next/navigation'; import styles from './page.module.css'; -export default async function Page(props: { - params: Promise<{ slug?: string[] }>; -}) { +export default async function Page(props: PageProps<'/docs/[[...slug]]'>) { const params = await props.params; const page = docs.getPage(params.slug); if (!page) notFound(); const MDX = page.data.body; + console.log('page', page); + return ( - - -
-
- {page.data.title} - -
-
- - -
-
-
- {page.data.description} - - + + + + + {page.data.title} + + {page.data.description} + + + + + + + + + + ); } @@ -63,9 +82,9 @@ export async function generateStaticParams() { return docs.generateParams(); } -export async function generateMetadata(props: { - params: Promise<{ slug?: string[] }>; -}) { +export async function generateMetadata( + props: PageProps<'/docs/[[...slug]]'> +): Promise { const params = await props.params; const page = docs.getPage(params.slug); if (!page) notFound(); diff --git a/apps/www/src/app/docs/layout.module.css b/apps/www/src/app/docs/layout.module.css new file mode 100644 index 000000000..946033346 --- /dev/null +++ b/apps/www/src/app/docs/layout.module.css @@ -0,0 +1,13 @@ +.container { + display: flex; + flex-direction: row; + position: relative; +} +.sidebar { + height: 100vh; + position: sticky; + top: 0; +} +.content { + flex: 1; +} diff --git a/apps/www/src/app/docs/layout.tsx b/apps/www/src/app/docs/layout.tsx index 9f4e4da23..86e9b46a1 100644 --- a/apps/www/src/app/docs/layout.tsx +++ b/apps/www/src/app/docs/layout.tsx @@ -1,24 +1,14 @@ -import type { ReactNode } from "react"; -import { DocsLayout } from "fumadocs-ui/layouts/docs"; -import { docs } from "@/lib/source"; -import ThemeSwitcher from "@/components/theme-switcher"; -import { SidebarItem } from "@/components/docs/sidebar-item"; +import DocsSidebar from '@/components/docs/sidebar'; +import { docs } from '@/lib/source'; +import { Flex } from '@raystack/apsara'; +import type { ReactNode } from 'react'; +import styles from './layout.module.css'; export default function Layout({ children }: { children: ReactNode }) { return ( - }} - disableThemeSwitch={true} - sidebar={{ - collapsible: false, - footer: , - hideSearch: true, - components: { - Item: SidebarItem, - }, - }}> - {children} - + + +
{children}
+
); } diff --git a/apps/www/src/app/icons/layout.tsx b/apps/www/src/app/icons/layout.tsx deleted file mode 100644 index 3b7029687..000000000 --- a/apps/www/src/app/icons/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { type ReactNode } from "react"; - -export const metadata = { - title: "Apsara Icons", -}; - -export default function Layout({ children }: { children: ReactNode }) { - return children; -} diff --git a/apps/www/src/app/icons/page.module.css b/apps/www/src/app/icons/page.module.css deleted file mode 100644 index e704a3da7..000000000 --- a/apps/www/src/app/icons/page.module.css +++ /dev/null @@ -1,82 +0,0 @@ -.container { - position: relative; - display: flex; - min-height: 100vh; - padding: 28px; - gap: 40px; -} - -.aside { - position: sticky; - top: 28px; -} - -.main { - display: flex; - flex-direction: column; - width: 100%; - height: 100%; - justify-content: center; - align-items: center; - padding-top: 20px; - flex: 1; -} -.info { - display: flex; - flex-direction: column; - align-items: flex-start; - align-self: stretch; - gap: 12px; -} -.info > * { - margin: 0; -} -.content { - display: flex; - flex-direction: column; - gap: 40px; - max-width: 720px; - width: 100%; -} -.controls { - display: flex; - justify-content: space-between; - align-items: center; -} -.spacer { - width: 100%; - height: 100%; -} -.icons { - display: flex; - flex-wrap: wrap; - gap: 24px; - row-gap: 32px; -} -.icon { - display: flex; - gap: 8px; - flex-direction: column; - width: 100px; - cursor: pointer; -} -.icon:hover .iconImage, -.icon.active .iconImage { - background-color: hsl(var(--fd-secondary) / 1); -} -.iconImage { - aspect-ratio: 1 / 1; - border: 1px solid hsl(var(--fd-border) / 1); - background-color: hsl(var(--fd-popover) / 1); - border-radius: 12px; - justify-content: center; - align-items: center; - display: flex; -} -.iconText { - font-size: 12px; - color: hsl(var(--fd-foreground) / 1); - text-align: center; - overflow: auto; - text-overflow: ellipsis; -} diff --git a/apps/www/src/app/icons/page.tsx b/apps/www/src/app/icons/page.tsx deleted file mode 100644 index e0e3b544b..000000000 --- a/apps/www/src/app/icons/page.tsx +++ /dev/null @@ -1,80 +0,0 @@ -'use client'; -import IconDetails, { IconDetailsProps } from '@/components/icon-details'; -import IconEmpty from '@/components/icon-details/icon-empty'; -import { Search, Tabs } from '@raystack/apsara'; -import * as Icons from '@raystack/apsara/icons'; -import { cx } from 'class-variance-authority'; -import { useState } from 'react'; -import style from './page.module.css'; - -type TabType = 'normal' | 'filled'; - -const FILTERS: Record Boolean> = { - normal: ([name]) => !name.includes('Filled'), - filled: ([name]) => name.includes('Filled') -}; - -const Page = () => { - const [tab, setTab] = useState('normal'); - const [search, setSearch] = useState(''); - const [icon, setIcon] = useState(null); - - return ( -
-
-
-
-

Apsara Icons

-

- A search input component with built-in search icon and optional - clear button. -

-
-
- setTab(value as TabType)}> - - Normal - Filled - - -
- setSearch(e.target.value)} - onClear={() => setSearch('')} - /> -
-
- {Object.entries(Icons) - .filter(FILTERS[tab]) - .filter(([name]) => - name.toLowerCase().includes(search.toLowerCase()) - ) - .map(([name, Icon]) => ( -
setIcon({ name, icon: Icon })} - > -
- -
-

{name}

-
- ))} -
-
-
- -
- ); -}; - -export default Page; diff --git a/apps/www/src/app/layout.module.css b/apps/www/src/app/layout.module.css new file mode 100644 index 000000000..be77bad4c --- /dev/null +++ b/apps/www/src/app/layout.module.css @@ -0,0 +1,6 @@ +.body { + display: flex; + flex-direction: column; + min-height: 100vh; + background: var(--rs-color-background-base-primary); +} diff --git a/apps/www/src/app/layout.tsx b/apps/www/src/app/layout.tsx index 38f4f075d..067ff791d 100644 --- a/apps/www/src/app/layout.tsx +++ b/apps/www/src/app/layout.tsx @@ -1,34 +1,29 @@ -import { RootProvider } from "fumadocs-ui/provider"; -import { Inter } from "next/font/google"; -import type { ReactNode } from "react"; -import Navbar from "@/components/navbar"; -import "fumadocs-ui/style.css"; -import "@raystack/apsara/style.css"; -import "@/styles.css"; -import { ThemeProvider } from "@/components/theme"; +import { ThemeProvider } from '@/components/theme'; +import { NextProvider } from 'fumadocs-core/framework/next'; +import { Inter } from 'next/font/google'; +import type { ReactNode } from 'react'; +import '@/styles.css'; +import '@raystack/apsara/normalize.css'; +import '@raystack/apsara/style.css'; +import { ThemeProvider as NextThemeProvider } from 'next-themes'; +import styles from './layout.module.css'; const inter = Inter({ - subsets: ["latin"], + subsets: ['latin'] }); export default function Layout({ children }: { children: ReactNode }) { return ( - + - + - - - - - {children} - - + + + + {children} + + ); diff --git a/apps/www/src/app/playground/layout.module.css b/apps/www/src/app/playground/layout.module.css deleted file mode 100644 index b018de651..000000000 --- a/apps/www/src/app/playground/layout.module.css +++ /dev/null @@ -1,19 +0,0 @@ -.container { - position: relative; - display: flex; - min-height: 100vh; -} -.aside { - position: sticky; - top: var(--fd-nav-height); - padding: 28px; - height: fit-content; -} -.main { - flex: 1; - padding: 28px; - display: flex; - gap: 40px; - flex-direction: column; - align-items: flex-start; -} diff --git a/apps/www/src/app/playground/layout.tsx b/apps/www/src/app/playground/layout.tsx deleted file mode 100644 index 46fce2256..000000000 --- a/apps/www/src/app/playground/layout.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { type ReactNode } from "react"; -import styles from "./layout.module.css"; -import ThemeCustomizer from "@/components/theme-customiser"; - -export default function Layout({ children }: { children: ReactNode }) { - return ( -
-
{children}
- -
- ); -} diff --git a/apps/www/src/app/playground/page.module.css b/apps/www/src/app/playground/page.module.css deleted file mode 100644 index 3c8e69c99..000000000 --- a/apps/www/src/app/playground/page.module.css +++ /dev/null @@ -1,7 +0,0 @@ -.container { - display: flex; - width: 100%; - gap: 100px; - flex-direction: column; - padding-bottom: 100px; -} diff --git a/apps/www/src/app/playground/page.tsx b/apps/www/src/app/playground/page.tsx deleted file mode 100644 index 0f1e5c6b3..000000000 --- a/apps/www/src/app/playground/page.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import * as Playground from '@/components/playground'; -import styles from './page.module.css'; - -export const metadata = { - title: 'Apsara Playground' -}; - -export default function Page() { - return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ); -} diff --git a/apps/www/src/assets/github.tsx b/apps/www/src/assets/github.tsx new file mode 100644 index 000000000..bd5dd9696 --- /dev/null +++ b/apps/www/src/assets/github.tsx @@ -0,0 +1,22 @@ +import { FC, SVGProps } from 'react'; + +export const GitHubIcon: FC> = ({ + width = 24, + height = 24, + ...props +}) => { + return ( + + + + ); +}; + +export default GitHubIcon; diff --git a/apps/www/src/components/demo/demo-controls.tsx b/apps/www/src/components/demo/demo-controls.tsx index 4aadc1ea0..19287edc2 100644 --- a/apps/www/src/components/demo/demo-controls.tsx +++ b/apps/www/src/components/demo/demo-controls.tsx @@ -1,11 +1,26 @@ -import { Info, Home, Laugh, X, Ban } from "lucide-react"; -import styles from "./styles.module.css"; +import { camelCaseToWords } from '@/lib/utils'; +import { + InfoCircledIcon, + Pencil2Icon, + PlusIcon, + TransformIcon, + UploadIcon +} from '@radix-ui/react-icons'; +import { + Flex, + IconButton, + InputField, + Select, + Switch, + Text +} from '@raystack/apsara'; +import { cx } from 'class-variance-authority'; +import styles from './styles.module.css'; import { ComponentPropsType, ControlsType, - PropChangeHandlerType, -} from "./types"; -import { cx } from "class-variance-authority"; + PropChangeHandlerType +} from './types'; type PropControlsProps = { controls: ControlsType; @@ -14,84 +29,141 @@ type PropControlsProps = { }; const ICONS_MAP = { - none: { icon: , value: "" }, - info: { icon: , value: "" }, - close: { icon: , value: "" }, - home: { icon: , value: "" }, - laugh: { icon: , value: "" }, + plus: { icon: , value: '' }, + transform: { icon: , value: '' }, + pencil: { icon: , value: '' }, + info: { icon: , value: '' }, + upload: { icon: , value: '' } }; + export default function DemoControls({ controls, componentProps, - onPropChange, + onPropChange }: PropControlsProps) { return ( -
+
{Object.entries(controls).map(([prop, control]) => { - const propValue = componentProps?.[prop] ?? ""; + const propLabel = camelCaseToWords(prop); + const propValue = componentProps?.[prop] ?? ''; + const isCheckbox = control.type === 'checkbox'; + const isIcon = control.type === 'icon'; + + // For checkbox and icon types, render in a special container + if (isCheckbox || isIcon) { + return ( +
+ + + {propLabel} + + { + if (isCheckbox) onPropChange(prop, checked); + else + onPropChange( + prop, + checked ? String(ICONS_MAP.plus.value) : '' + ); + }} + /> + + {isIcon && ( + + {Object.values(ICONS_MAP).map((icon, index) => ( + { + onPropChange(prop, String(icon.value)); + e.preventDefault(); + e.stopPropagation(); + }} + aria-label={`Select ${icon.value || 'none'} icon`} + > + {icon.icon} + + ))} + + )} +
+ ); + } + + // For select type + if (control.type === 'select') { + const selectValue = + propValue !== undefined && propValue !== null + ? String(propValue) + : undefined; + return ( +
+ + {propLabel} + + +
+ ); + } + + // For text and number types return ( - +
+ { + if (control.type === 'number') { + onPropChange(prop, Number(e.target.value)); + } else { + onPropChange(prop, e.target.value); + } + }} + type={control.type === 'number' ? 'number' : 'text'} + min={control.min} + max={control.max} + className={cx(styles.noShadow, styles.inputLabel)} + /> +
); })} - +
); } diff --git a/apps/www/src/components/demo/demo-playground.tsx b/apps/www/src/components/demo/demo-playground.tsx index 7a5c65189..e0cffe294 100644 --- a/apps/www/src/components/demo/demo-playground.tsx +++ b/apps/www/src/components/demo/demo-playground.tsx @@ -1,28 +1,28 @@ -"use client"; +'use client'; -import { useState } from "react"; +import { IconButton } from '@raystack/apsara'; +import { RefreshCw } from 'lucide-react'; import { - useSearchParams, - useRouter, ReadonlyURLSearchParams, -} from "next/navigation"; -import { LiveProvider } from "react-live"; -import styles from "./styles.module.css"; -import DemoControls from "./demo-controls"; -import Preview from "../preview"; -import Editor from "../editor"; + useRouter, + useSearchParams +} from 'next/navigation'; +import { useState } from 'react'; +import { LiveProvider } from 'react-live'; +import Editor from '../editor'; +import Preview from '../preview'; +import DemoControls from './demo-controls'; +import styles from './styles.module.css'; import { ComponentPropsType, ControlsType, DemoPlaygroundProps, - PropChangeHandlerType, -} from "./types"; -import { RefreshCw } from "lucide-react"; -import { buttonVariants } from "fumadocs-ui/components/api"; + PropChangeHandlerType +} from './types'; const getInitialProps = ( controls: ControlsType, - searchParams?: ReadonlyURLSearchParams, + searchParams?: ReadonlyURLSearchParams ) => { const initialProps: ComponentPropsType = {}; @@ -31,7 +31,7 @@ const getInitialProps = ( const value = (searchParams && searchParams.get(key)) ?? initialValue ?? defaultValue; - initialProps[key] = type === "checkbox" ? value === "true" : value; + initialProps[key] = type === 'checkbox' ? value === 'true' : value; }); return initialProps; }; @@ -39,19 +39,19 @@ const getInitialProps = ( export default function DemoPlayground({ scope, controls, - getCode, + getCode }: DemoPlaygroundProps) { const searchParams = useSearchParams(); const router = useRouter(); const [componentProps, setComponentProps] = useState(() => - getInitialProps(controls, searchParams), + getInitialProps(controls, searchParams) ); const updatedProps = Object.fromEntries( Object.entries(componentProps).filter( - ([key, value]) => value !== controls[key]?.defaultValue, - ), + ([key, value]) => value !== controls[key]?.defaultValue + ) ); const code = getCode(updatedProps, componentProps).trim(); @@ -78,18 +78,18 @@ export default function DemoPlayground({ return ( -
+
- + + +
-
+
{tabs && (
{tabs.map((tab, index) => ( @@ -30,9 +30,10 @@ export default function DemoPreview({ key={tab.name} className={cx( styles.tab, - index === activeTab && styles.activeTab, + index === activeTab && styles.activeTab )} - onClick={() => setActiveTab(index)}> + onClick={() => setActiveTab(index)} + > {tab.name} ))} @@ -41,16 +42,28 @@ export default function DemoPreview({
+ {Array.isArray(codePreview) ? ( - tab.label)} - className={styles.codeTabGroup}> - {codePreview.map(tab => ( - - - - ))} - +
+
+ {codePreview.map((tab, index) => ( + + ))} +
+ +
) : ( )} @@ -58,3 +71,19 @@ export default function DemoPreview({ ); } +{ + /* // {Array.isArray(codePreview) ? ( */ +} +// tab.label)} +// className={styles.codeTabGroup} +// > +// {codePreview.map(tab => ( +// +// +// +// ))} +// +// ) : ( +// +// )} diff --git a/apps/www/src/components/demo/demo.tsx b/apps/www/src/components/demo/demo.tsx index caf6cd3c2..eb28a087e 100644 --- a/apps/www/src/components/demo/demo.tsx +++ b/apps/www/src/components/demo/demo.tsx @@ -1,5 +1,12 @@ 'use client'; +import { + InfoCircledIcon, + Pencil2Icon, + PlusIcon, + TransformIcon, + UploadIcon +} from '@radix-ui/react-icons'; import * as Apsara from '@raystack/apsara'; import { Home, Info, Laugh, X } from 'lucide-react'; import NextLink from 'next/link'; @@ -23,7 +30,12 @@ export default function Demo(props: DemoProps) { X, Home, Laugh, - NextLink + NextLink, + PlusIcon, + TransformIcon, + Pencil2Icon, + InfoCircledIcon, + UploadIcon } } = props; diff --git a/apps/www/src/components/demo/index.ts b/apps/www/src/components/demo/index.ts index df8619d52..7a5d10400 100644 --- a/apps/www/src/components/demo/index.ts +++ b/apps/www/src/components/demo/index.ts @@ -1 +1 @@ -export { default } from "./demo"; +export { default } from './demo'; diff --git a/apps/www/src/components/demo/styles.module.css b/apps/www/src/components/demo/styles.module.css index 68ca6e276..2cecd5181 100644 --- a/apps/www/src/components/demo/styles.module.css +++ b/apps/www/src/components/demo/styles.module.css @@ -1,103 +1,132 @@ .container { display: flex; flex-direction: column; - border-radius: 8px; + border-radius: var(--rs-radius-2, 4px); overflow: hidden; - border: 1px solid hsl(var(--fd-border) / 1); + border: 1px solid var(--rs-color-border-base-primary); + width: 100%; } .previewContainer { - min-height: 160px; + min-height: 368px; display: flex; width: 100%; + /* border-bottom: 0.5px solid var(--rs-color-border-base-primary); */ + max-height: 340px; } + .preview { - flex: 3; + flex: 1; position: relative; - background-color: hsl(var(--color-fd-background) / 1); + display: flex; + align-items: center; + justify-content: center; + min-height: 368px; + min-width: 0; + border-bottom: 0.5px solid var(--rs-color-border-base-primary); } + .previewReset { position: absolute; bottom: 8px; right: 8px; + padding: var(--rs-space-1, 2px); + width: var(--rs-space-5, 16px); + height: var(--rs-space-5, 16px); + border-radius: var(--rs-radius-1, 2px); } .form { - flex: 1; + width: 240px; + display: flex; + flex-direction: column; + gap: var(--rs-space-6, 20px); + padding: var(--rs-space-5, 16px); + border-bottom: 0.5px solid var(--rs-color-border-base-primary); + border-left: 0.5px solid var(--rs-color-border-base-primary); + background-color: var(--rs-color-background-base-primary); + overflow-x: hidden; + overflow-y: auto; + flex-shrink: 0; +} +.noShadow { + box-shadow: none; +} + +.controlField { display: flex; flex-direction: column; - justify-content: start; - column-gap: 40px; - row-gap: 16px; - font-size: 14px; - padding: 20px; - border-left: 1px solid hsl(var(--fd-border) / 1); - max-height: 400px; - overflow: auto; - min-width: 240px; - background: hsl(var(--fd-secondary) / 0.5); -} - -.label { + gap: var(--rs-space-2, 4px); + width: 100%; +} + +.controlSection { display: flex; flex-direction: column; - text-transform: capitalize; + gap: var(--rs-space-5, 16px); + padding: var(--rs-space-3, 8px); + background-color: var(--rs-color-background-base-secondary); + border: 0.5px solid var(--rs-color-border-base-primary); + border-radius: var(--rs-radius-2, 4px); + width: 100%; +} + +.controlHeader { width: 100%; } -.label:has(input[type="checkbox"]) { - flex-direction: row-reverse; - gap: 8px; - /* margin-left: 20px; */ - width: max-content; - height: max-content; + +.selectLabel { + margin-bottom: var(--rs-space-2, 4px); +} + +.inputLabel { + font-weight: var(--rs-font-weight-medium); +} + +.selectTrigger { + width: 100%; + border: 1px solid var(--rs-color-border-base-tertiary); + padding: var(--rs-space-2) var(--rs-space-3); +} + +.iconContainer { + width: 100%; + justify-content: space-between; } -.input, -.select, .iconButton { - padding: 4px 6px; - border: 1px solid hsl(var(--fd-border) / 1); - background-color: hsl(var(--fd-background) / 1); - border-radius: 6px; + padding: var(--rs-space-1, 2px); + border-radius: var(--rs-radius-1, 2px); + width: 20px; + height: 20px; } -.input:focus, -.select:focus { - border-color: #007bff; - outline: none; +.iconButton.active { + border-color: var(--rs-color-background-accent-emphasis); } + .tabs { display: flex; - border-bottom: 1px solid hsl(var(--fd-border) / 1); - background-color: hsl(var(--fd-muted) / 1); + border-bottom: 0.5px solid var(--rs-color-border-base-primary); + background-color: var(--rs-color-background-base-secondary); } .tab { background: none; border: none; - padding: 10px 16px; + padding: var(--rs-space-3) var(--rs-space-4); cursor: pointer; - font-size: 14px; - font-weight: 500; - color: hsl(var(--fd-muted-foreground) / 1); + color: var(--rs-color-foreground-base-secondary); transition: color 0.2s ease-in-out, border-bottom 0.2s ease-in-out; - border-bottom: 1px solid transparent; + border-bottom: 0.5px solid transparent; + font-size: var(--rs-font-size-small); + letter-spacing: var(--rs-letter-spacing-small); + line-height: var(--rs-line-height-small); } .activeTab { - color: hsl(var(--fd-foreground) / 1); - border-bottom: 1px solid var(--rs-color-background-accent-emphasis); -} -.iconContainer { - display: flex; - gap: 12px; - align-items: center; -} -.iconButton { - padding: 6px; -} -.iconButton.active { - border-color: var(--rs-color-background-accent-emphasis); + color: var(--rs-color-foreground-base-primary); + border-bottom: 0.5px solid var(--rs-color-background-accent-emphasis); } .codeTabGroup { @@ -119,8 +148,9 @@ flex-direction: column; } .form { + width: 100%; max-height: 280px; - border-top: 1px solid hsl(var(--fd-border) / 1); + border-top: 0.5px solid var(--rs-color-border-base-primary); border-left: none; } } diff --git a/apps/www/src/components/demo/types.ts b/apps/www/src/components/demo/types.ts index 12b74c76c..4f0f0bafa 100644 --- a/apps/www/src/components/demo/types.ts +++ b/apps/www/src/components/demo/types.ts @@ -5,7 +5,7 @@ type TabProps = { code: string; }; export type DemoPreviewProps = { - type: "code"; + type: 'code'; code?: string; tabs?: { name: string; code: string }[]; scope?: ScopeType; @@ -13,7 +13,7 @@ export type DemoPreviewProps = { }; export type DemoPlaygroundProps = { - type: "playground"; + type: 'playground'; controls: ControlsType; getCode: GetCodeType; scope?: ScopeType; @@ -21,11 +21,11 @@ export type DemoPlaygroundProps = { export type DemoProps = { scope?: ScopeType; - data: Omit | Omit; + data: Omit | Omit; }; export type ControlType = { - type: "select" | "text" | "checkbox" | "number" | "icon"; + type: 'select' | 'text' | 'checkbox' | 'number' | 'icon'; options?: string[]; defaultValue?: string | boolean; initialValue?: string | boolean; @@ -37,12 +37,12 @@ export type ControlsType = Record; export type PropChangeHandlerType = ( prop: string, - value: string | boolean | number, + value: string | boolean | number ) => void; export type ComponentPropsType = Record; export type GetCodeType = ( updatedProps: Record, - props: Record, + props: Record ) => string; diff --git a/apps/www/src/components/docs/docs-body.module.css b/apps/www/src/components/docs/docs-body.module.css index 8f4c8f984..79038ff54 100644 --- a/apps/www/src/components/docs/docs-body.module.css +++ b/apps/www/src/components/docs/docs-body.module.css @@ -190,7 +190,7 @@ color: var(--tw-prose-quotes); border-inline-start-width: 0.25rem; border-inline-start-color: var(--tw-prose-quote-borders); - quotes: "\201C""\201D""\2018""\2019"; + quotes: "\201C" "\201D" "\2018" "\2019"; } .content blockquote p:first-of-type::before { @@ -290,8 +290,8 @@ font-weight: 500; font-family: inherit; color: var(--tw-prose-kbd); - box-shadow: 0 0 0 1px var(--tw-prose-kbd-shadows), - 0 3px 0 var(--tw-prose-kbd-shadows); + box-shadow: 0 0 0 1px var(--tw-prose-kbd-shadows), 0 3px 0 + var(--tw-prose-kbd-shadows); } /* .content code { diff --git a/apps/www/src/components/docs/docs-body.tsx b/apps/www/src/components/docs/docs-body.tsx index 048c9ebbc..2899cac73 100644 --- a/apps/www/src/components/docs/docs-body.tsx +++ b/apps/www/src/components/docs/docs-body.tsx @@ -1,6 +1,6 @@ -import { forwardRef, HTMLAttributes } from "react"; -import { cx } from "class-variance-authority"; -import styles from "./docs-body.module.css"; +import { cx } from 'class-variance-authority'; +import { HTMLAttributes, forwardRef } from 'react'; +import styles from './docs-body.module.css'; export const DocsBody = forwardRef< HTMLDivElement, diff --git a/apps/www/src/components/docs/index.ts b/apps/www/src/components/docs/index.ts index 2fa4a343d..681658934 100644 --- a/apps/www/src/components/docs/index.ts +++ b/apps/www/src/components/docs/index.ts @@ -1 +1 @@ -export { default as DocsBody } from "./docs-body"; +export { default as DocsBody } from './docs-body'; diff --git a/apps/www/src/components/docs/navbar.module.css b/apps/www/src/components/docs/navbar.module.css new file mode 100644 index 000000000..db61acfe7 --- /dev/null +++ b/apps/www/src/components/docs/navbar.module.css @@ -0,0 +1,41 @@ +.navbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--rs-space-4) var(--rs-space-7); + backdrop-filter: blur(1px); + position: relative; + width: 100%; + box-sizing: border-box; + position: sticky; + top: 0; + backdrop-filter: blur(1px); + border-bottom: 0.5px solid var(--rs-color-border-base-primary, #e8e8e8); + position: relative; + width: 100%; + box-sizing: border-box; + position: sticky; + top: 0; + z-index: var(--rs-z-index-portal); + height: 50px; +} + +.left { + display: flex; + align-items: center; + gap: var(--rs-space-3); + flex-shrink: 0; +} + +.actions { + display: flex; + align-items: center; + gap: var(--rs-space-3); + flex-shrink: 0; +} +.actions button { + transition: none; +} +.breadcrumb-separator { + height: var(--rs-space-4); +} diff --git a/apps/www/src/components/docs/navbar.tsx b/apps/www/src/components/docs/navbar.tsx new file mode 100644 index 000000000..c551a8a55 --- /dev/null +++ b/apps/www/src/components/docs/navbar.tsx @@ -0,0 +1,108 @@ +'use client'; +import { SourceType } from '@/lib/types'; +import { Breadcrumb, Button } from '@raystack/apsara'; +import { useBreadcrumb } from 'fumadocs-core/breadcrumb'; +import { Root } from 'fumadocs-core/page-tree'; +import { usePathname } from 'next/navigation'; +import { Fragment, useState } from 'react'; +import styles from './navbar.module.css'; + +const cache = new Map(); + +interface DocsNavbarProps { + url: string; + title: string; + pageTree: Root; + source: SourceType; +} + +export default function DocsNavbar({ + url, + title, + pageTree, + source +}: DocsNavbarProps) { + const pathname = usePathname(); + const items = useBreadcrumb(pathname, pageTree); + const breadcrumbItems = items.length > 0 ? items : [{ name: 'Overview' }]; + + const [isLoading, setLoading] = useState(false); + const [checked, setChecked] = useState(false); + const markdownUrl = `${url}.mdx`; + + const handleCopyMarkdown = async () => { + const cached = cache.get(markdownUrl); + if (cached) { + await navigator.clipboard.writeText(cached); + setChecked(true); + setTimeout(() => setChecked(false), 2000); + return; + } + + setLoading(true); + + try { + await navigator.clipboard.write([ + new ClipboardItem({ + 'text/plain': fetch(markdownUrl).then(async res => { + const content = await res.text(); + cache.set(markdownUrl, content); + return content; + }) + }) + ]); + + setChecked(true); + setTimeout(() => setChecked(false), 2000); + } finally { + setLoading(false); + } + }; + + const handleViewSource = () => { + // Construct GitHub URL or source viewer URL + // Adjust based on your repository structure + if (source) window.open(source, '_blank'); + }; + + return ( +
+
+ {breadcrumbItems.length > 0 && ( + + {breadcrumbItems.map((item, index) => ( + + {item.name} + + + ))} + {title} + + )} +
+
+ + {source && ( + + )} +
+
+ ); +} diff --git a/apps/www/src/components/docs/search.module.css b/apps/www/src/components/docs/search.module.css new file mode 100644 index 000000000..e8bc18145 --- /dev/null +++ b/apps/www/src/components/docs/search.module.css @@ -0,0 +1,112 @@ +.searchContainer { + border-radius: var(--rs-radius-5); + overflow: hidden; +} +.searchCommand { + border: none; + border-radius: 0; + box-shadow: none; +} +.inputContainer { + position: relative; +} +.inputContainer svg { + color: var(--rs-color-foreground-base-primary); + opacity: 1; +} +.inputContainer div[cmdk-input-wrapper] { + flex: 1; + color: var(--rs-color-foreground-base-primary); +} +.inputContainer div[cmdk-input-wrapper]:focus-within { + border-color: var(--rs-color-border-accent-emphasis); +} + +.input { + padding: var(--rs-space-5) 0; + font-size: var(--rs-font-size-small); + font-weight: var(--rs-font-weight-regular); + line-height: var(--rs-line-height-small); + letter-spacing: var(--rs-letter-spacing-small); +} +.input::placeholder { + color: var(--rs-color-foreground-base-tertiary); +} +.searchClearButton { + position: absolute; + right: var(--rs-space-4); +} + +.searchList { + padding-top: var(--rs-space-2); + padding-bottom: var(--rs-space-2); + background: var(--rs-color-background-base-primary, #fff); + max-height: 400px; +} +.searchList:focus { + outline: none; +} +.searchGroup { + padding: var(--rs-space-5) var(--rs-space-3) var(--rs-space-2) + var(--rs-space-3); +} +.searchGroup [cmdk-group-heading] { + color: var(--rs-color-foreground-base-tertiary); + font-size: var(--rs-font-size-mini); + font-weight: var(--rs-font-weight-medium); + line-height: var(--rs-line-height-mini); + letter-spacing: var(--rs-letter-spacing-mini); + text-transform: capitalize; +} + +.searchItem { + padding: var(--rs-space-3); + border-radius: var(--rs-radius-2); + display: flex; + flex-direction: column; + gap: var(--rs-space-1); + margin-top: var(--rs-space-2); +} +.searchItem:first-child { + margin-top: 0; +} +.searchSubItemsContainer { + margin-left: var(--rs-space-3); + padding-left: var(--rs-space-3); + border-left: 0.5px solid var(--rs-color-border-base-secondary); +} + +.searchItem[data-selected="true"], +.searchItem:hover { + background: rgba(15, 15, 15, 0.04); +} + +.searchItemIcon { + width: 32px; + height: 32px; + border-radius: 12px; + background: var(--rs-color-background-base-secondary, #f3f3f3); + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 13px; + font-weight: 600; + color: var(--rs-color-text-base-secondary, #5f5f5f); +} + +.searchItemText { + display: flex; + flex-direction: column; + gap: 2px; + flex: 1; +} + +.searchItemTitle { + font-weight: 500; + color: var(--rs-color-text-base-primary, #1f1f1f); +} + +.searchItemMeta { + font-size: var(--rs-font-size-body-small, 12px); + color: var(--rs-color-text-base-secondary, #6f6f6f); +} diff --git a/apps/www/src/components/docs/search.tsx b/apps/www/src/components/docs/search.tsx new file mode 100644 index 000000000..3c35448fc --- /dev/null +++ b/apps/www/src/components/docs/search.tsx @@ -0,0 +1,224 @@ +'use client'; +import { getFileFromUrl, getFolderFromUrl } from '@/lib/utils'; +import { + Cross1Icon, + ExclamationTriangleIcon, + MagnifyingGlassIcon +} from '@radix-ui/react-icons'; +import { + Command, + Dialog, + EmptyState, + Flex, + IconButton, + Text +} from '@raystack/apsara'; +import { cx } from 'class-variance-authority'; +import { + type Item as PageItem, + Root, + flattenTree +} from 'fumadocs-core/page-tree'; +import { useDocsSearch } from 'fumadocs-core/search/client'; +import { useRouter } from 'next/navigation'; +import { Fragment, useCallback, useEffect, useMemo, useState } from 'react'; +import styles from './search.module.css'; + +type Item = Omit & { + items?: Item[]; + id: string; +}; +type SearchItems = { + heading: string; + items: Item[]; +}; + +export default function DocsSearch({ pageTree }: { pageTree: Root }) { + const router = useRouter(); + const [open, setOpen] = useState(false); + + const { search, setSearch, query } = useDocsSearch({ + type: 'fetch' + }); + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') { + event.preventDefault(); + setOpen(prev => !prev); + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, []); + + const defaultItems = useMemo(() => { + if (!pageTree?.children?.length) return []; + const flattened = flattenTree(pageTree.children); + if (!flattened.length) return []; + + const items = flattened + .slice(0, 10) + .reduce>((acc, item) => { + const folder = getFolderFromUrl(item.url); + if (!acc[folder]) { + acc[folder] = []; + } + acc[folder].push({ ...item, id: item.url }); + return acc; + }, {}); + return Object.entries(items).map(([heading, items]) => ({ + heading, + items + })); + }, [pageTree]); + + const trimmedQuery = search.trim(); + const isSearching = trimmedQuery.length > 0; + const results = + isSearching && query.data && query.data !== 'empty' ? query.data : []; + + const searchResults = useMemo(() => { + if (results.length === 0) return []; + + const grouped = results.reduce>>( + (acc, result) => { + const folder = getFolderFromUrl(result.url); + const file = getFileFromUrl(result.url); + if (!acc[folder]) acc[folder] = {}; + if (!acc[folder][file]) + acc[folder][file] = { + name: file, + url: result.url, + items: [], + id: result.id + }; + const isTopLevel = result.type === 'page'; + if (isTopLevel) { + acc[folder][file] = { + ...acc[folder][file], + name: result.content, + url: result.url, + id: result.id + }; + } else { + acc[folder][file].items?.push({ + name: result.content, + url: result.url, + id: result.id + }); + } + return acc; + }, + {} + ); + console.log('grouped', grouped); + + return Object.entries(grouped).map(([heading, items]) => ({ + heading, + items: Object.values(items) + })); + }, [results]); + + const items = !isSearching ? defaultItems : searchResults; + + const handleSelect = useCallback( + (url: string) => { + setOpen(false); + setSearch(''); + router.push(url); + }, + [router, setSearch] + ); + + return ( + + + + + + + + 1}> + + + {search.length > 0 && ( + setSearch('')} + className={styles.searchClearButton} + > + + + )} + + + + } + /> + + {items.map((section, index) => ( +
+ + {section.items.map(item => ( + + handleSelect(item.url)} + className={styles.searchItem} + > + + {item.name} + + + {item.items?.length && item.items.length > 0 && ( +
+ {item.items?.map(subItem => ( + handleSelect(subItem.url)} + className={cx( + styles.searchItem, + styles.searchSubItem + )} + > + + {subItem.name} + + + ))} +
+ )} +
+ ))} +
+ {index < defaultItems.length - 1 && } +
+ ))} +
+
+
+
+ ); +} diff --git a/apps/www/src/components/docs/sidebar-item.module.css b/apps/www/src/components/docs/sidebar-item.module.css deleted file mode 100644 index afddb5a9b..000000000 --- a/apps/www/src/components/docs/sidebar-item.module.css +++ /dev/null @@ -1,35 +0,0 @@ -.item { - display: flex; - flex-direction: row; - align-items: center; - gap: 0.5rem; - border-radius: 0.375rem; - padding: 0.5rem 0.75rem; - color: hsl(var(--fd-muted-foreground) / 1); - transition-property: color, background-color, border-color; - transition-duration: 100ms; - overflow-wrap: anywhere; -} - -.item svg { - width: 1rem; - height: 1rem; -} - -.item.active { - background-color: hsl(var(--fd-primary) / 0.1); - font-weight: 500; - color: hsl(var(--fd-primary) / 1); -} - -.item:not(.active):hover { - background-color: hsl(var(--fd-accent) / 0.5); - color: hsl(var(--fd-accent-foreground) / 0.8); - transition: none; -} - -@media (min-width: 768px) { - .item { - padding: 0.375rem 0.5rem; - } -} diff --git a/apps/www/src/components/docs/sidebar-item.tsx b/apps/www/src/components/docs/sidebar-item.tsx deleted file mode 100644 index db949ac0d..000000000 --- a/apps/www/src/components/docs/sidebar-item.tsx +++ /dev/null @@ -1,30 +0,0 @@ -"use client"; - -import { docs } from "@/lib/source"; -import { isActiveUrl } from "@/lib/utils"; -import { PageTree } from "fumadocs-core/server"; -import Link from "next/link"; -import { usePathname } from "next/navigation"; -import { useMemo } from "react"; -import Tag from "../tag"; -import styles from "./sidebar-item.module.css"; - -export function SidebarItem({ item }: { item: PageTree.Item }) { - const tag = useMemo( - () => docs.getNodePage(item)?.data?.tag ?? "", - [item.url], - ); - - const pathname = usePathname(); - const active = - item.url !== undefined && isActiveUrl(item.url, pathname, false); - - return ( - - {item.name} - - ); -} diff --git a/apps/www/src/components/docs/sidebar.module.css b/apps/www/src/components/docs/sidebar.module.css new file mode 100644 index 000000000..2fe1582a2 --- /dev/null +++ b/apps/www/src/components/docs/sidebar.module.css @@ -0,0 +1,17 @@ +.sidebar { + padding: 0; + border-right-width: 0.5px !important; +} +.header { + height: 50px; + padding: 0 var(--rs-space-5); + border-bottom: 0.5px solid var(--rs-color-border-base-primary); + border-radius: 0; +} +.main { + padding: var(--rs-space-5); + gap: var(--rs-space-5); +} +.main a { + transition: none; +} diff --git a/apps/www/src/components/docs/sidebar.tsx b/apps/www/src/components/docs/sidebar.tsx new file mode 100644 index 000000000..4e1a9c4d2 --- /dev/null +++ b/apps/www/src/components/docs/sidebar.tsx @@ -0,0 +1,106 @@ +'use client'; + +import { isActiveUrl } from '@/lib/utils'; +import { Flex, Sidebar } from '@raystack/apsara'; +import { cx } from 'class-variance-authority'; +import { Node, Root } from 'fumadocs-core/page-tree'; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import { type ReactNode } from 'react'; +import Logo from '../logo'; +import { ThemeToggle } from '../theme-switcher'; +import DocsSearch from './search'; +import styles from './sidebar.module.css'; + +type Props = { + pageTree: Root; + className?: string; +}; + +function renderNode(node: Node, pathname: string): ReactNode { + // Handle folders with both children and index + if ( + node.type === 'folder' && + node.index && + node.children && + node.children.length > 0 + ) { + return ( + + {/* Render index item first */} + {renderNode(node.index, pathname)} + {/* Recursively render all children */} + {node.children.map(child => renderNode(child, pathname))} + + ); + } + + // Handle folders with index but no children + if ( + node.type === 'folder' && + node.index && + (!node.children || node.children.length === 0) + ) { + return renderNode(node.index, pathname); + } + + // Handle folders normally + if (node.type === 'folder') { + return ( + + {node.children.map(child => renderNode(child, pathname))} + + ); + } + + // Handle page items + if (node.type === 'page') { + return ( + } + active={isActiveUrl(node.url, pathname, false)} + > + {node.name as string} + + ); + } + + // Handle separators (if needed) + if (node.type === 'separator') { + return null; + } + + return null; +} + +export default function DocsSidebar({ pageTree, className }: Props) { + const pathname = usePathname(); + return ( + + + + + + + + + + + + + + {pageTree.children.map(item => renderNode(item, pathname))} + + + ); +} diff --git a/apps/www/src/components/editor/editor.module.css b/apps/www/src/components/editor/editor.module.css index cb3463944..f7b1b5262 100644 --- a/apps/www/src/components/editor/editor.module.css +++ b/apps/www/src/components/editor/editor.module.css @@ -12,3 +12,7 @@ .editor [data-radix-scroll-area-viewport] { overscroll-behavior: auto; } +.code { + padding-left: var(--rs-space-3); + padding-bottom: var(--rs-space-6); +} diff --git a/apps/www/src/components/editor/editor.tsx b/apps/www/src/components/editor/editor.tsx index 75e9ea113..099660ee0 100644 --- a/apps/www/src/components/editor/editor.tsx +++ b/apps/www/src/components/editor/editor.tsx @@ -1,18 +1,26 @@ -import { DynamicCodeBlock } from "fumadocs-ui/components/dynamic-codeblock"; -import styles from "./editor.module.css"; -import { useMemo } from "react"; -import { getFormattedCode } from "@/lib/prettier"; +import { getFormattedCode } from '@/lib/prettier'; +import { CodeBlock } from '@raystack/apsara'; +import { useMemo } from 'react'; +import styles from './editor.module.css'; type props = { code?: string; }; -export default function Editor({ code = "" }: props) { +export default function Editor({ code = '' }: props) { const formattedCode = useMemo(() => getFormattedCode(code), [code]); return (
- + + + + {formattedCode} + + + + +
); } diff --git a/apps/www/src/components/editor/index.ts b/apps/www/src/components/editor/index.ts index 6f8ae5825..b6b0fe66d 100644 --- a/apps/www/src/components/editor/index.ts +++ b/apps/www/src/components/editor/index.ts @@ -1 +1 @@ -export { default } from "./editor"; +export { default } from './editor'; diff --git a/apps/www/src/components/icon-details/icon-empty.tsx b/apps/www/src/components/icon-details/icon-empty.tsx index 4bb7728b6..f32c01227 100644 --- a/apps/www/src/components/icon-details/icon-empty.tsx +++ b/apps/www/src/components/icon-details/icon-empty.tsx @@ -1,10 +1,10 @@ -import { cx } from "class-variance-authority"; -import { MousePointerClick } from "lucide-react"; -import styles from "./icon-details.module.css"; +import { cx } from 'class-variance-authority'; +import { MousePointerClick } from 'lucide-react'; +import styles from './icon-details.module.css'; export default function IconEmpty() { return ( -
+

Select an icon

diff --git a/apps/www/src/components/icon-details/index.ts b/apps/www/src/components/icon-details/index.ts index 96d823fb9..e7cb545fc 100644 --- a/apps/www/src/components/icon-details/index.ts +++ b/apps/www/src/components/icon-details/index.ts @@ -1,3 +1,3 @@ -export { default } from "./icon-details"; -export * from "./icon-details"; -export * from "./icon-empty"; +export { default } from './icon-details'; +export * from './icon-details'; +export * from './icon-empty'; diff --git a/apps/www/src/components/kbd/index.ts b/apps/www/src/components/kbd/index.ts new file mode 100644 index 000000000..fbbeae156 --- /dev/null +++ b/apps/www/src/components/kbd/index.ts @@ -0,0 +1 @@ +export { Kbd } from './kbd'; diff --git a/apps/www/src/components/kbd/kbd.module.css b/apps/www/src/components/kbd/kbd.module.css new file mode 100644 index 000000000..767cde464 --- /dev/null +++ b/apps/www/src/components/kbd/kbd.module.css @@ -0,0 +1,11 @@ +.container { + display: flex; + align-items: center; + gap: var(--rs-space-2); +} + +.kbd { + padding: var(--rs-space-1); + border-radius: var(--rs-radius-1); + background: var(--rs-color-background-neutral-primary); +} diff --git a/apps/www/src/components/kbd/kbd.tsx b/apps/www/src/components/kbd/kbd.tsx new file mode 100644 index 000000000..92086c568 --- /dev/null +++ b/apps/www/src/components/kbd/kbd.tsx @@ -0,0 +1,51 @@ +import { Text } from '@raystack/apsara'; +import { cx } from 'class-variance-authority'; +import { HTMLAttributes } from 'react'; +import styles from './kbd.module.css'; + +interface KBDProps extends HTMLAttributes { + children: string | number; + mode?: 'single' | 'combination'; +} + +export const Kbd = ({ + children, + mode = 'single', + className, + ...props +}: KBDProps) => { + const content = String(children); + + if (mode === 'combination') { + const keys = content.split(' '); + return ( + + {keys.map((key, index) => ( + + {key} + + ))} + + ); + } + + return ( + + {content} + + ); +}; diff --git a/apps/www/src/components/linear-dropdown-demo.tsx b/apps/www/src/components/linear-dropdown-demo.tsx index 26094dd7b..f8fbeffbc 100644 --- a/apps/www/src/components/linear-dropdown-demo.tsx +++ b/apps/www/src/components/linear-dropdown-demo.tsx @@ -1,19 +1,19 @@ -import { Avatar, Button, DropdownMenu, Flex, Text } from "@raystack/apsara"; -import { Calendar, ChevronRight, Download } from "lucide-react"; -import { Fragment, ReactNode, useState } from "react"; +import { Avatar, Button, DropdownMenu, Flex, Text } from '@raystack/apsara'; +import { Calendar, ChevronRight, Download } from 'lucide-react'; +import { Fragment, ReactNode, useState } from 'react'; type DropdownMenuItem = | { - type: "item"; + type: 'item'; label: string | string[]; disabled?: boolean; trailingIcon?: ReactNode; leadingIcon?: ReactNode; } - | { type: "separator" } - | { type: "group"; label: string; items: DropdownMenuItem[] } + | { type: 'separator' } + | { type: 'group'; label: string; items: DropdownMenuItem[] } | { - type: "submenu"; + type: 'submenu'; label: string; items: DropdownMenuItem[]; trailingIcon?: ReactNode; @@ -22,182 +22,182 @@ type DropdownMenuItem = const dropdownMenuData: DropdownMenuItem[] = [ { - type: "group", - label: "Actions", + type: 'group', + label: 'Actions', items: [ { - type: "submenu", - label: "Assign member...", + type: 'submenu', + label: 'Assign member...', items: [ { - type: "item", - label: "Rohan", + type: 'item', + label: 'Rohan', leadingIcon: ( - - ), + + ) }, { - type: "item", - label: "Rohil", + type: 'item', + label: 'Rohil', leadingIcon: ( - - ), + + ) }, { - type: "item", - label: "Gaurav", + type: 'item', + label: 'Gaurav', leadingIcon: ( - - ), + + ) }, { - type: "item", - label: "Abhishek", + type: 'item', + label: 'Abhishek', leadingIcon: ( - - ), + + ) }, { - type: "item", - label: "Aman", + type: 'item', + label: 'Aman', leadingIcon: ( - - ), + + ) }, { - type: "item", - label: "Risabh", + type: 'item', + label: 'Risabh', leadingIcon: ( - - ), + + ) }, { - type: "item", - label: "Ajinkya", + type: 'item', + label: 'Ajinkya', leadingIcon: ( - - ), - }, - ], + + ) + } + ] }, - { type: "item", label: "Subscribe..." }, - { type: "item", label: "Rename..." }, - ], + { type: 'item', label: 'Subscribe...' }, + { type: 'item', label: 'Rename...' } + ] }, - { type: "separator" }, + { type: 'separator' }, { - type: "group", - label: "More", + type: 'group', + label: 'More', items: [ { - type: "submenu", - label: "Export", + type: 'submenu', + label: 'Export', items: [ { - type: "item", - label: "All (.zip)", - leadingIcon: , + type: 'item', + label: 'All (.zip)', + leadingIcon: }, { - type: "submenu", - label: "CSV", + type: 'submenu', + label: 'CSV', leadingIcon: , items: [ { - type: "item", - label: "All", - leadingIcon: , + type: 'item', + label: 'All', + leadingIcon: }, { - type: "item", - label: "3 Months", - leadingIcon: , + type: 'item', + label: '3 Months', + leadingIcon: }, { - type: "item", - label: "6 Months", - leadingIcon: , - }, - ], + type: 'item', + label: '6 Months', + leadingIcon: + } + ] }, { - type: "submenu", - label: "PDF", + type: 'submenu', + label: 'PDF', leadingIcon: , items: [ { - type: "item", - label: "All", - leadingIcon: , + type: 'item', + label: 'All', + leadingIcon: }, { - type: "item", - label: "3 Months", - leadingIcon: , + type: 'item', + label: '3 Months', + leadingIcon: }, { - type: "item", - label: "6 Months", - leadingIcon: , - }, - ], - }, - ], + type: 'item', + label: '6 Months', + leadingIcon: + } + ] + } + ] }, - { type: "item", label: "Copy", disabled: true }, + { type: 'item', label: 'Copy', disabled: true }, { - type: "item", - label: "Delete...", + type: 'item', + label: 'Delete...', trailingIcon: ( - + ⌘⇧D - ), - }, - ], - }, + ) + } + ] + } ]; function filterDropdownMenuItems( items: DropdownMenuItem[], query: string, path: string[] = [], - isInsideSubmenu = false, + isInsideSubmenu = false ): DropdownMenuItem[] { if (!query?.length) return items; const normalizedQuery = query.trim().toLowerCase(); const results: DropdownMenuItem[] = []; for (const item of items) { - if (item.type === "separator") continue; + if (item.type === 'separator') continue; - if (item.type === "item") { + if (item.type === 'item') { const fullPath = isInsideSubmenu ? [...path, item.label] : [item.label]; - const flatLabel = fullPath.join(" ").toLowerCase(); + const flatLabel = fullPath.join(' ').toLowerCase(); if (flatLabel.includes(normalizedQuery)) { results.push({ ...item, - label: fullPath, + label: fullPath } as DropdownMenuItem); } } - if (item.type === "submenu") { + if (item.type === 'submenu') { const nested = filterDropdownMenuItems( item.items, query, [...path, item.label], - true, + true ); results.push(...nested); } - if (item.type === "group") { + if (item.type === 'group') { const nested = filterDropdownMenuItems( item.items, query, path, - isInsideSubmenu, + isInsideSubmenu ); results.push(...nested); } @@ -207,7 +207,7 @@ function filterDropdownMenuItems( } export default function LinearDropdownDemo() { - const [searchQuery, setSearchQuery] = useState(""); + const [searchQuery, setSearchQuery] = useState(''); const renderDropdownMenu = (items: DropdownMenuItem[], query: string) => { const filteredItems = filterDropdownMenuItems(items, query); @@ -222,21 +222,22 @@ export default function LinearDropdownDemo() { return filteredItems.map((item, index) => { switch (item.type) { - case "group": + case 'group': return ( {item.label} {item.items && renderDropdownMenu(item.items, query)} ); - case "separator": + case 'separator': return ; - case "submenu": + case 'submenu': return ( + leadingIcon={item.leadingIcon} + > {item.label} @@ -244,13 +245,14 @@ export default function LinearDropdownDemo() { ); - case "item": + case 'item': return ( + leadingIcon={item.leadingIcon} + > {Array.isArray(item.label) ? item.label.map((part, i) => ( @@ -271,12 +273,13 @@ export default function LinearDropdownDemo() { setSearchQuery(value)}> + autocompleteMode='manual' + onSearch={(value: string) => setSearchQuery(value)} + > - + {renderDropdownMenu(dropdownMenuData, searchQuery)} diff --git a/apps/www/src/components/logo/index.ts b/apps/www/src/components/logo/index.ts index 880ba3517..f0d8afd94 100644 --- a/apps/www/src/components/logo/index.ts +++ b/apps/www/src/components/logo/index.ts @@ -1 +1 @@ -export { default } from "./logo"; +export { default } from './logo'; diff --git a/apps/www/src/components/logo/logo.tsx b/apps/www/src/components/logo/logo.tsx index e9d51157f..2330438c9 100644 --- a/apps/www/src/components/logo/logo.tsx +++ b/apps/www/src/components/logo/logo.tsx @@ -1,20 +1,20 @@ -import Image from "next/image"; -import { cx } from "class-variance-authority"; -import styles from "./logo.module.css"; +import { cx } from 'class-variance-authority'; +import Image from 'next/image'; +import styles from './logo.module.css'; type Props = { - variant?: "small" | "large"; + variant?: 'small' | 'large'; onlyWordmark?: boolean; }; export default function Logo({ - variant = "small", - onlyWordmark = false, + variant = 'small', + onlyWordmark = false }: Props) { - const size = variant === "small" ? 24 : 48; + const size = variant === 'small' ? 24 : 48; return (
- + {!onlyWordmark &&

Apsara

}
); diff --git a/apps/www/src/components/mdx/code.tsx b/apps/www/src/components/mdx/code.tsx new file mode 100644 index 000000000..d8ff96e7d --- /dev/null +++ b/apps/www/src/components/mdx/code.tsx @@ -0,0 +1,37 @@ +'use client'; + +import { CodeBlock } from '@raystack/apsara'; +import { cx } from 'class-variance-authority'; +import { HTMLAttributes } from 'react'; +import styles from './mdx-components.module.css'; +import { usePreContext } from './pre-context'; + +export const Code = ({ children, className }: HTMLAttributes) => { + const { hasPreParent } = usePreContext(); + + const code = String(children).trim(); + if (!hasPreParent) + return {code}; + + const language = className?.split('-')?.[1] || 'ts'; + const codeLines = code.split('\n').length; + + return ( + + + + {code} + + + + + ); +}; diff --git a/apps/www/src/components/mdx/index.ts b/apps/www/src/components/mdx/index.ts new file mode 100644 index 000000000..a1b6f8211 --- /dev/null +++ b/apps/www/src/components/mdx/index.ts @@ -0,0 +1 @@ +export * from './mdx-components'; diff --git a/apps/www/src/components/mdx/mdx-components.module.css b/apps/www/src/components/mdx/mdx-components.module.css new file mode 100644 index 000000000..21d7272a1 --- /dev/null +++ b/apps/www/src/components/mdx/mdx-components.module.css @@ -0,0 +1,60 @@ +.code-block-content { + border-radius: var(--rs-radius-2); + border: 1px solid var(--rs-color-border-base-primary); + background: var(--rs-color-background-base-secondary); +} +.code-block-content-code { + padding-left: var(--rs-space-3); +} +.code-block-content-copy-btn-fix { + top: var(--rs-space-4); +} +.code-inline { + border-radius: var(--rs-radius-2); + border: 1px solid var(--rs-color-border-base-primary); + padding: var(--rs-space-1) var(--rs-space-2); + background: var(--rs-color-background-base-secondary); + font-family: var(--rs-font-mono); + font-size: var(--rs-font-size-mono-small); + letter-spacing: var(--rs-letter-spacing-mono-small); + line-height: var(--rs-line-height-mono-small); + color: var(--rs-color-foreground-base-primary); +} + +.prose-h1, +.prose-h2 { + margin-top: var(--rs-space-11); +} +.prose-h3 { + margin-top: var(--rs-space-9); +} +.prose-h4 { + margin-top: var(--rs-space-7); +} +.prose-h5 { + margin-top: var(--rs-space-5); +} +.prose-h6 { + margin-top: var(--rs-space-4); +} + +.prose-code, +.prose-type-table, +:global(.prose) [data-demo] { + margin-top: var(--rs-space-4); +} + +:global(.prose) ul, +:global(.prose) ol { + padding-left: var(--rs-space-5); + margin: 0; +} + +:global(.prose) ul li, +:global(.prose) ol li { + margin-bottom: var(--rs-space-3); +} +:global(.prose) ul li:first-child, +:global(.prose) ol li:first-child { + margin-top: var(--rs-space-3); +} diff --git a/apps/www/src/components/mdx/mdx-components.tsx b/apps/www/src/components/mdx/mdx-components.tsx new file mode 100644 index 000000000..fb1766f40 --- /dev/null +++ b/apps/www/src/components/mdx/mdx-components.tsx @@ -0,0 +1,130 @@ +import { Headline, Tabs, Text } from '@raystack/apsara'; +import { cx } from 'class-variance-authority'; +import { Image as FrameworkImage } from 'fumadocs-core/framework'; +import Link from 'fumadocs-core/link'; +import type { + AnchorHTMLAttributes, + ComponentPropsWithoutRef, + FC, + HTMLAttributes, + ImgHTMLAttributes, + TableHTMLAttributes +} from 'react'; +import Demo from '../demo'; +import { TypeTable } from '../typetable'; +import { Code } from './code'; +import styles from './mdx-components.module.css'; +import { PreContextProvider } from './pre-context'; + +function Image( + props: ImgHTMLAttributes & { + sizes?: string; + } +) { + return ( + + ); +} + +function Table(props: TableHTMLAttributes) { + return ( +
+ + + ); +} + +const mdxComponents = { + CodeBlockTabsTrigger: ( + props: ComponentPropsWithoutRef + ) => , + CodeBlockTabs: (props: HTMLAttributes) => ( + + {props.children} + + ), + CodeBlockTabsList: (props: HTMLAttributes) => ( + + ), + CodeBlockTab: (props: ComponentPropsWithoutRef) => ( + + ), + code: Code, + pre: (props: HTMLAttributes) => ( + {props.children} + ), + a: Link as FC>, + img: Image, + h1: (props: HTMLAttributes) => ( + + ), + h2: (props: HTMLAttributes) => ( + + ), + h3: (props: HTMLAttributes) => ( + + ), + h4: (props: HTMLAttributes) => ( + + ), + h5: (props: HTMLAttributes) => ( + + ), + h6: (props: HTMLAttributes) => ( + + ), + table: Table, + TypeTable: (props: ComponentPropsWithoutRef) => ( + + ), + Demo +}; + +// export const createRelativeLink: typeof import('./mdx.server').createRelativeLink = +// () => { +// throw new Error( +// '`createRelativeLink` is only supported in Node.js environment', +// ); +// }; + +export { mdxComponents }; diff --git a/apps/www/src/components/mdx/pre-context.tsx b/apps/www/src/components/mdx/pre-context.tsx new file mode 100644 index 000000000..a9a707ed9 --- /dev/null +++ b/apps/www/src/components/mdx/pre-context.tsx @@ -0,0 +1,24 @@ +'use client'; + +import { ReactNode, createContext, useContext } from 'react'; + +const PreContext = createContext<{ + hasPreParent: boolean; +}>({ + hasPreParent: false +}); + +export const PreContextProvider = ({ + children, + hasPreParent +}: { children: ReactNode; hasPreParent: boolean }) => { + return ( + + {children} + + ); +}; + +export const usePreContext = () => { + return useContext(PreContext); +}; diff --git a/apps/www/src/components/navbar/index.ts b/apps/www/src/components/navbar/index.ts index 8fb380621..b4a0bf9f0 100644 --- a/apps/www/src/components/navbar/index.ts +++ b/apps/www/src/components/navbar/index.ts @@ -1 +1 @@ -export { default } from "./navbar"; +export { default } from './navbar'; diff --git a/apps/www/src/components/navbar/navbar.module.css b/apps/www/src/components/navbar/navbar.module.css index b4d2d7ba8..ef0c3bd28 100644 --- a/apps/www/src/components/navbar/navbar.module.css +++ b/apps/www/src/components/navbar/navbar.module.css @@ -4,7 +4,7 @@ align-items: center; position: sticky; top: 0; - z-index: 100; + z-index: var(--rs-z-index-portal); height: var(--fd-nav-height); padding: 8px 16px; background: hsl(var(--fd-popover) / 1); diff --git a/apps/www/src/components/navbar/navbar.tsx b/apps/www/src/components/navbar/navbar.tsx index 01afc3c90..0d64f2494 100644 --- a/apps/www/src/components/navbar/navbar.tsx +++ b/apps/www/src/components/navbar/navbar.tsx @@ -1,26 +1,26 @@ -"use client"; +'use client'; -import Link from "next/link"; -import { usePathname } from "next/navigation"; -import Logo from "../logo"; -import styles from "./navbar.module.css"; -import { LargeSearchToggle } from "fumadocs-ui/components/layout/search-toggle"; -import { cx } from "class-variance-authority"; -import { ThemeToggle } from "../theme-switcher"; +import { cx } from 'class-variance-authority'; +import { LargeSearchToggle } from 'fumadocs-ui/components/layout/search-toggle'; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import Logo from '../logo'; +import { ThemeToggle } from '../theme-switcher'; +import styles from './navbar.module.css'; const LINKS = [ { - name: "Documentation", - url: "/docs", + name: 'Documentation', + url: '/docs' }, { - name: "Playground", - url: "/playground", + name: 'Playground', + url: '/playground' }, { - name: "Icons", - url: "/icons", - }, + name: 'Icons', + url: '/icons' + } ]; export default function Navbar() { const pathname = usePathname(); @@ -28,16 +28,17 @@ export default function Navbar() { return (
- - + + {LINKS.map(link => (

+ pathname.startsWith(link.url) && styles.active + )} + > {link.name}

@@ -46,15 +47,16 @@ export default function Navbar() {
- + - + height={24} + > +
diff --git a/apps/www/src/components/playground/announcement-bar-examples.tsx b/apps/www/src/components/playground/announcement-bar-examples.tsx index 76cace9d0..a2a0436d1 100644 --- a/apps/www/src/components/playground/announcement-bar-examples.tsx +++ b/apps/www/src/components/playground/announcement-bar-examples.tsx @@ -1,25 +1,25 @@ -"use client"; -import { AnnouncementBar, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +'use client'; +import { AnnouncementBar, Flex } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function AnnouncementBarExamples() { return ( - - + + diff --git a/apps/www/src/components/playground/avatar-examples.tsx b/apps/www/src/components/playground/avatar-examples.tsx index 5f361d123..365215834 100644 --- a/apps/www/src/components/playground/avatar-examples.tsx +++ b/apps/www/src/components/playground/avatar-examples.tsx @@ -1,58 +1,58 @@ -"use client"; -import { Avatar, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +'use client'; +import { Avatar, Flex } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function AvatarExamples() { return ( - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - + + + + + diff --git a/apps/www/src/components/playground/badge-examples.tsx b/apps/www/src/components/playground/badge-examples.tsx index aa968395a..8aafaf88c 100644 --- a/apps/www/src/components/playground/badge-examples.tsx +++ b/apps/www/src/components/playground/badge-examples.tsx @@ -1,29 +1,29 @@ -"use client"; +'use client'; -import { Badge, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; -import { Home, Laugh } from "lucide-react"; +import { Badge, Flex } from '@raystack/apsara'; +import { Home, Laugh } from 'lucide-react'; +import PlaygroundLayout from './playground-layout'; export function BadgeExamples() { return ( - - - Accent - Warning - Danger - Success - Neutral - Gradient + + + Accent + Warning + Danger + Success + Neutral + Gradient - - Micro - Small - Regular + + Micro + Small + Regular - - }>Badge - }>Badge - Badge + + }>Badge + }>Badge + Badge ); diff --git a/apps/www/src/components/playground/button-examples.tsx b/apps/www/src/components/playground/button-examples.tsx index e300e69e5..cabadf6a8 100644 --- a/apps/www/src/components/playground/button-examples.tsx +++ b/apps/www/src/components/playground/button-examples.tsx @@ -1,88 +1,88 @@ -"use client"; +'use client'; -import { Button, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Button, Flex } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function ButtonExamples() { return ( - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/src/components/playground/calendar-examples.tsx b/apps/www/src/components/playground/calendar-examples.tsx index d851dd586..bdf67650c 100644 --- a/apps/www/src/components/playground/calendar-examples.tsx +++ b/apps/www/src/components/playground/calendar-examples.tsx @@ -1,15 +1,15 @@ -"use client"; +'use client'; -import { Calendar, DatePicker, Flex, RangePicker } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Calendar, DatePicker, Flex, RangePicker } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function CalendarExamples() { return ( - - + + - - + + ); diff --git a/apps/www/src/components/playground/callout-examples.tsx b/apps/www/src/components/playground/callout-examples.tsx index 6023c50f1..3432534c7 100644 --- a/apps/www/src/components/playground/callout-examples.tsx +++ b/apps/www/src/components/playground/callout-examples.tsx @@ -1,26 +1,26 @@ -"use client"; +'use client'; -import { Button, Callout, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Button, Callout, Flex } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function CalloutExamples() { return ( - - - Default Callout - Success Callout - Alert Callout - Gradient Callout - Accent Callout - Attention Callout - Normal Callout - + + + Default Callout + Success Callout + Alert Callout + Gradient Callout + Accent Callout + Attention Callout + Normal Callout + With Outline Callout - alert("Dismissed!")}> + alert('Dismissed!')}> Dismissible Callout - Action}> + Action}> A short message to attract user's attention diff --git a/apps/www/src/components/playground/checkbox-examples.tsx b/apps/www/src/components/playground/checkbox-examples.tsx index f5750b6f5..a2a3e1f17 100644 --- a/apps/www/src/components/playground/checkbox-examples.tsx +++ b/apps/www/src/components/playground/checkbox-examples.tsx @@ -1,15 +1,15 @@ -"use client"; +'use client'; -import { Checkbox, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Checkbox, Flex } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function CheckboxExamples() { return ( - - + + - + diff --git a/apps/www/src/components/playground/chip-examples.tsx b/apps/www/src/components/playground/chip-examples.tsx index ba27b4921..6c29e5ef5 100644 --- a/apps/www/src/components/playground/chip-examples.tsx +++ b/apps/www/src/components/playground/chip-examples.tsx @@ -1,57 +1,60 @@ -"use client"; +'use client'; -import { Chip, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Chip, Flex } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function ChipExamples() { return ( - - - Small - Large + + + Small + Large - - + + Outline - + Filled - + Outline - + Filled - - Add Item - Next - + + Add Item + Next + Download - + alert("dismissed")} - ariaLabel="Dismissible chip"> + onDismiss={() => alert('dismissed')} + ariaLabel='Dismissible chip' + > Dismissable Chip alert("dismissed")} - ariaLabel="Dismissible chip"> + onDismiss={() => alert('dismissed')} + ariaLabel='Dismissible chip' + > Dismissable Chip alert("dismissed")} - ariaLabel="Dismissible chip"> + onDismiss={() => alert('dismissed')} + ariaLabel='Dismissible chip' + > Dismissable Chip diff --git a/apps/www/src/components/playground/command-examples.tsx b/apps/www/src/components/playground/command-examples.tsx index 4f5abe717..88ab19be6 100644 --- a/apps/www/src/components/playground/command-examples.tsx +++ b/apps/www/src/components/playground/command-examples.tsx @@ -1,23 +1,23 @@ -"use client"; +'use client'; -import { Command, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Command, Flex } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function CommandExamples() { return ( - + - + No results found. - + Calendar Search Emoji Calculator - + Profile Billing Settings diff --git a/apps/www/src/components/playground/container-examples.tsx b/apps/www/src/components/playground/container-examples.tsx index 3df3f9b64..cf2ebe7ca 100644 --- a/apps/www/src/components/playground/container-examples.tsx +++ b/apps/www/src/components/playground/container-examples.tsx @@ -1,34 +1,34 @@ -"use client"; +'use client'; -import { Container, Flex, Text } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Container, Flex, Text } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function ContainerExamples() { return ( - - - + + + Lorem Ipsum is simply dummy text of the printing and typesetting - industry. Lorem Ipsum has been the industry standard dummy text - ever since the 1500s, when an unknown printer took a galley of type - and scrambled it to make a type specimen book. + industry. Lorem Ipsum has been the industry standard dummy text ever + since the 1500s, when an unknown printer took a galley of type and + scrambled it to make a type specimen book. - + Lorem Ipsum is simply dummy text of the printing and typesetting - industry. Lorem Ipsum has been the industry standard dummy text - ever since the 1500s, when an unknown printer took a galley of type - and scrambled it to make a type specimen book. + industry. Lorem Ipsum has been the industry standard dummy text ever + since the 1500s, when an unknown printer took a galley of type and + scrambled it to make a type specimen book. - + Lorem Ipsum is simply dummy text of the printing and typesetting - industry. Lorem Ipsum has been the industry standard dummy text - ever since the 1500s, when an unknown printer took a galley of type - and scrambled it to make a type specimen book. + industry. Lorem Ipsum has been the industry standard dummy text ever + since the 1500s, when an unknown printer took a galley of type and + scrambled it to make a type specimen book. diff --git a/apps/www/src/components/playground/data-table-examples.tsx b/apps/www/src/components/playground/data-table-examples.tsx index b6abbb550..f06db31b9 100644 --- a/apps/www/src/components/playground/data-table-examples.tsx +++ b/apps/www/src/components/playground/data-table-examples.tsx @@ -1,11 +1,11 @@ -"use client"; +'use client'; -import PlaygroundLayout from "./playground-layout"; -import DataTableDemo from "../datatable-demo"; +import DataTableDemo from '../datatable-demo'; +import PlaygroundLayout from './playground-layout'; export function DataTableExamples() { return ( - + ); diff --git a/apps/www/src/components/playground/dialog-examples.tsx b/apps/www/src/components/playground/dialog-examples.tsx index 1602a0d8d..83e1ddc12 100644 --- a/apps/www/src/components/playground/dialog-examples.tsx +++ b/apps/www/src/components/playground/dialog-examples.tsx @@ -1,23 +1,24 @@ -"use client"; +'use client'; -import { Button, Dialog, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Button, Dialog, Flex } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function DialogExamples() { return ( - - + + + overlayClassName='custom-overlay' + overlayStyle={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }} + > Custom Styled Dialog - + This dialog has custom width and overlay styling. @@ -25,7 +26,7 @@ export function DialogExamples() { - + No Close Button diff --git a/apps/www/src/components/playground/dropdown-menu-examples.tsx b/apps/www/src/components/playground/dropdown-menu-examples.tsx index 4b647bcfd..b32e25e0f 100644 --- a/apps/www/src/components/playground/dropdown-menu-examples.tsx +++ b/apps/www/src/components/playground/dropdown-menu-examples.tsx @@ -1,15 +1,15 @@ -"use client"; +'use client'; -import { Button, DropdownMenu, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Button, DropdownMenu, Flex } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function DropdownMenuExamples() { return ( - - + + - + Profile @@ -20,7 +20,7 @@ export function DropdownMenuExamples() { - + 📝}>Edit @@ -33,7 +33,7 @@ export function DropdownMenuExamples() { - + Actions diff --git a/apps/www/src/components/playground/empty-state-examples.tsx b/apps/www/src/components/playground/empty-state-examples.tsx index 0927b836f..fcf6b3d26 100644 --- a/apps/www/src/components/playground/empty-state-examples.tsx +++ b/apps/www/src/components/playground/empty-state-examples.tsx @@ -1,18 +1,18 @@ -"use client"; +'use client'; -import { Button, EmptyState } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; -import { X } from "lucide-react"; +import { Button, EmptyState } from '@raystack/apsara'; +import { X } from 'lucide-react'; +import PlaygroundLayout from './playground-layout'; export function EmptyStateExamples() { return ( - + } primaryAction={} - secondaryAction={} + secondaryAction={} /> ); diff --git a/apps/www/src/components/playground/flex-examples.tsx b/apps/www/src/components/playground/flex-examples.tsx index 4fb9e9e74..98054703d 100644 --- a/apps/www/src/components/playground/flex-examples.tsx +++ b/apps/www/src/components/playground/flex-examples.tsx @@ -1,33 +1,33 @@ -"use client"; +'use client'; -import { Flex, Button } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Button, Flex } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function FlexExamples() { return ( - - - + + + - + - + - + - + diff --git a/apps/www/src/components/playground/headline-examples.tsx b/apps/www/src/components/playground/headline-examples.tsx index 858abb991..802efb456 100644 --- a/apps/www/src/components/playground/headline-examples.tsx +++ b/apps/www/src/components/playground/headline-examples.tsx @@ -1,36 +1,36 @@ -"use client"; +'use client'; -import { Headline, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Flex, Headline } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function HeadlineExamples() { return ( - - - - + + + + Large Headline - Medium Headline + Medium Headline - + Small Headline - - + + Left Aligned - + Center Aligned - + Right Aligned - - + + This is a truncated headline that is very long diff --git a/apps/www/src/components/playground/icon-button-examples.tsx b/apps/www/src/components/playground/icon-button-examples.tsx index a593b0546..515910321 100644 --- a/apps/www/src/components/playground/icon-button-examples.tsx +++ b/apps/www/src/components/playground/icon-button-examples.tsx @@ -1,13 +1,13 @@ -"use client"; +'use client'; -import { IconButton, Flex } from "@raystack/apsara"; -import { Info } from "lucide-react"; -import PlaygroundLayout from "./playground-layout"; +import { Flex, IconButton } from '@raystack/apsara'; +import { Info } from 'lucide-react'; +import PlaygroundLayout from './playground-layout'; export function IconButtonExamples() { return ( - - + + diff --git a/apps/www/src/components/playground/image-examples.tsx b/apps/www/src/components/playground/image-examples.tsx index b4bfb6af9..fe2ba7919 100644 --- a/apps/www/src/components/playground/image-examples.tsx +++ b/apps/www/src/components/playground/image-examples.tsx @@ -1,39 +1,39 @@ -"use client"; +'use client'; -import { Image, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Flex, Image } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function ImageExamples() { return ( - - + + diff --git a/apps/www/src/components/playground/indicator-examples.tsx b/apps/www/src/components/playground/indicator-examples.tsx index dbbbcc685..b9739ec98 100644 --- a/apps/www/src/components/playground/indicator-examples.tsx +++ b/apps/www/src/components/playground/indicator-examples.tsx @@ -1,35 +1,35 @@ -"use client"; +'use client'; -import { Indicator, Flex, Button } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Button, Flex, Indicator } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function IndicatorExamples() { return ( - - - - - + + + + + - - + + - - + + - - + + - - + + - - - + + + - - + + diff --git a/apps/www/src/components/playground/input-field-examples.tsx b/apps/www/src/components/playground/input-field-examples.tsx index 846fc0baf..0dd240f7d 100644 --- a/apps/www/src/components/playground/input-field-examples.tsx +++ b/apps/www/src/components/playground/input-field-examples.tsx @@ -1,40 +1,40 @@ -"use client"; +'use client'; -import { InputField, Flex } from "@raystack/apsara"; -import { Home, Info } from "lucide-react"; -import PlaygroundLayout from "./playground-layout"; +import { Flex, InputField } from '@raystack/apsara'; +import { Home, Info } from 'lucide-react'; +import PlaygroundLayout from './playground-layout'; export function InputFieldExamples() { return ( - - - - + + + + - + } trailingIcon={} /> - + diff --git a/apps/www/src/components/playground/label-examples.tsx b/apps/www/src/components/playground/label-examples.tsx index 9470ae9da..be1f98307 100644 --- a/apps/www/src/components/playground/label-examples.tsx +++ b/apps/www/src/components/playground/label-examples.tsx @@ -1,19 +1,19 @@ -"use client"; +'use client'; -import { Label, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Flex, Label } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function LabelExamples() { return ( - - - - - -
diff --git a/apps/www/src/components/playground/text-area-examples.tsx b/apps/www/src/components/playground/text-area-examples.tsx index 52aa2a5e4..f8ec4f422 100644 --- a/apps/www/src/components/playground/text-area-examples.tsx +++ b/apps/www/src/components/playground/text-area-examples.tsx @@ -1,23 +1,23 @@ -"use client"; +'use client'; -import { TextArea, Flex } from "@raystack/apsara"; -import PlaygroundLayout from "./playground-layout"; +import { Flex, TextArea } from '@raystack/apsara'; +import PlaygroundLayout from './playground-layout'; export function TextAreaExamples() { return ( - - -