Compare commits

...
41 Commits
Author SHA1 Message Date
hanzo-dev 891818443a feat(@hanzo/ui): add billing module to dist build
Add billing/index tsup entry so @hanzo/ui/billing resolves from npm.
Update package.json export to point to dist instead of source.
Bumps to v5.3.41.
2026-03-11 17:55:00 -07:00
hanzo-dev 532d8e692c feat(@hanzo/ui): add navigation/hanzo-shell build entry
Add separate tsup entry point for navigation/hanzo-shell so the
wildcard package export ./navigation/* resolves to a real dist file.
Bumps version to 5.3.40.
2026-03-11 17:40:01 -07:00
hanzo-dev e45833b5f2 feat: add hard refresh and settings to HanzoHeader shell 2026-03-11 11:38:55 -07:00
hanzo-dev dffa6cef34 feat: add @hanzo/ui/auth package with IAMLoginButton and AuthGuard
New auth components for unified IAM integration:
- IAMLoginButton: "Sign in with Hanzo" button that initiates OAuth flow
- AuthGuard: wrapper that redirects to IAM when unauthenticated
- Re-exports useHanzoAuth, UserOrgDropdown, HanzoUser/HanzoOrg types
Adds ./auth and ./auth/* subpath exports to package.json.
2026-03-10 16:59:46 -07:00
hanzo-dev 04407e9cb5 feat(shell): org-aware app switcher, white-label domains, hanzo.space
- chat.hanzo.ai → hanzo.chat (canonical domain)
- Add hanzo.space (Storage) to DEFAULT_HANZO_APPS
- Add ORG_DOMAINS map: hanzo, lux, zoo, pars with per-org domains
- Add getAppsForOrg() for white-label app switcher URLs
- HanzoHeader now resolves org-specific domains automatically
- UserOrgDropdown Account/Billing links adapt to current org
- Export OrgDomains type and getAppsForOrg from hanzo-shell
2026-03-10 16:06:51 -07:00
hanzo-dev 9f0e37d8c3 fix(billing): export BillingSection type from @hanzo/ui/billing 2026-03-10 15:32:03 -07:00
hanzo-dev bf18ef7a35 chore: gitignore dist-test output 2026-03-03 10:43:12 -08:00
hanzo-dev edda5a41b8 feat: add UserAvatar with three-tier fallback (photo → Gravatar → generative SVG)
BeamAvatar: deterministic face-like SVG from any string, zero deps, boring-avatars beam algorithm
UserAvatar: three-tier fallback — uploaded photo, Gravatar SHA-256, generative beam SVG
UserOrgDropdown now uses UserAvatar instead of simple letter initials
2026-03-03 20:37:01 -08:00
Zach Kelling 320885b073 chore(ui): bump to v5.3.38
Published with HanzoCommandPalette component.
2026-03-03 17:35:30 -08:00
Zach Kelling d97b670580 feat(ui): add HanzoCommandPalette — shared Cmd+K command palette
Zero-dependency React component for cross-app command navigation.
Supports extensible app-specific commands, keyboard nav, search filtering.
2026-03-03 17:33:30 -08:00
Zach Kelling b8ecd6c408 fix(ci): resolve TypeScript build errors blocking deployment
- resizable.tsx: update react-resizable-panels v4 imports (PanelGroup→Group, PanelResizeHandle→Separator)
- blob.ts: wrap Buffer in Uint8Array for BlobPart compatibility
- tsconfig.build.json: exclude typo-plugin JS from type-checked build
2026-03-03 14:33:02 -08:00
Zach Kelling 4086d54305 test: add static CI test to validate all docs URLs have matching MDX files
Runs 228 tests checking that every href in docs.ts config resolves to
an actual MDX file. Catches 404s before deployment without needing a
running server. Reports orphaned MDX files not in nav config.
2026-03-03 14:25:06 -08:00
Zach Kelling 6831117b97 feat: full docs modernization — zero 404s, 585 pages, dep updates
- Enable all 15 finance component docs (from .disabled → .mdx)
- Create stock-market.mdx, 8 chart docs, 8 block docs, 2 framework docs
- Add direction.tsx component (RTL/LTR support from upstream shadcn)
- Add xs/icon-xs button sizes, data-variant/data-size attributes
- Fix build-registry.mts to handle default exports in namespace re-exports
- Add default props to order-entry, positions-list, orders-history, trading-panel
- Fix 18 navigation stubs → re-export from root implementations
- Add 44 orphaned MDX files to nav (installation, dark-mode, desktop, guides, etc)
- Migrate 6 framer-motion imports → motion/react
- Bump React 19.2.0→19.2.4, Next.js 16.0.1→16.1.6, motion→12.34.0
- Align lucide-react, sonner v2, tailwind-merge v3, postcss-selector-parser v7
- Remove framer-motion dep (superseded by motion)
- Fix @types/react-dom, TypeScript versions across workspaces
2026-03-03 07:34:51 -08:00
hanzo-dev 6ea1042d44 feat: @hanzo/shop payment popup + Square billing components
- @hanzo/shop: new embeddable 3-tab topup widget (card/crypto/bank)
  - HanzoTopup dialog, TopupButton trigger
  - CardInput (Square Web Payments SDK), CryptoInput (MPC chains), BankInput (ACH)
  - hooks: useSquareCard, useTopup
- @hanzo/commerce client: add getWalletAddress, addBankAccount, addCryptoWallet, topup
- @hanzo/ui billing: comprehensive billing dashboard components
  - OverviewDashboard, PaymentManager (Square), CreditsPanel, UsagePanel
  - InvoicesPayments, TransactionsPanel, SquareCardForm
  - CostExplorer, SpendAlerts, AccountMembers, AccountSwitcher
  - BillingSettings, SupportTiersPanel, PromotionsPanel
  - GuidedSetup, AnimatedCard, StatusBar
2026-03-02 11:09:00 -08:00
hanzo-dev d9f090bb53 feat(commerce): update commerce client SDK 2026-03-01 19:40:49 -08:00
hanzo-dev e1184390dc feat(@hanzo/commerce): extend client with checkout, coupons, referrals, affiliates
Add comprehensive commerce client methods:
- validateCoupon / applyCoupon (pre-checkout validation)
- createCheckoutSession (hosted checkout, returns checkoutUrl)
- tokenizeCard (S2S card tokenization without external SDK)
- referrals: createReferralProgram, getReferralLink, trackReferral, getReferralStats
- affiliates: registerAffiliate, getAffiliateStats, getAffiliateCommissions
- Backwards-compatible: commerceUrl still works, baseUrl preferred
- Default baseUrl: https://api.hanzo.ai
- Request timeout support (timeoutMs, default 15s)
2026-03-01 16:20:08 -08:00
hanzo-dev a42e158a07 fix(ci): add NEXT_PUBLIC_APP_URL env and coverage json-summary reporter 2026-03-01 16:01:01 -08:00
hanzo-dev 6347fe0aeb feat: remove @hanzo/auth, all auth goes through @hanzo/iam
- Move pkg/auth and pkg/auth-firebase to deprecated/ (excluded from workspace)
- Remove @hanzo/auth from commerce and checkout peerDependencies
- Remove useAuth() from payment-step-form; contact form defaults to empty strings
- Update pnpm lockfile

Production auth is now handled entirely by IAM (hanzo.id). The legacy
auth package is preserved in deprecated/ for reference.
2026-03-01 15:39:33 -08:00
hanzo-dev f08abfe55a chore: update pnpm lockfile 2026-03-01 15:27:19 -08:00
hanzo-dev fbe58782d9 chore(@hanzo/ui): bump to 5.3.37 — publish model components 2026-03-01 15:25:31 -08:00
hanzo-dev 16ffaea878 feat(@hanzo/ui): add shared model components — ModelCard, ModelTable, ModelLibrary, ZenEnso
Adds @hanzo/ui/models export with data-agnostic model UI components
that work across hanzo.ai, zen-docs, and any other Hanzo site:

- ZenModelLike/ModelFamilyLike interfaces (structural compatibility
  with @hanzo/zen-models, no cross-package dependency needed)
- ModelCard: rich clickable card with status badges, spec, action buttons
- ModelTable: static table view with pricing and context columns
- ModelLibrary: full catalog with family sections and filter toggle
- ZenEnso: animated SVG enso circle logo component

Fix ./models ESM export path (dist/models/index.mjs not dist/src/models/).
2026-03-01 15:19:28 -08:00
hanzo-dev a4f1a1c16f feat: add HanzoShell shared navigation components
New pkg/ui/src/navigation/hanzo-shell/ module:
- HanzoHeader: sticky 14px header with logo/breadcrumb/app-switcher/user-dropdown
- HanzoMark: official H-mark SVG with hover origami animation + brand context menu
  (right-click → Brand Guidelines, Press Kit, Download Logo, Copy SVG, hanzo.ai)
- AppSwitcher: 3x3-grid icon → dropdown listing all Hanzo apps
- UserOrgDropdown: avatar/initials, org switcher (from IAM groups), sign-out
- useHanzoAuth: zero-dep hook reads hanzo-auth-token from localStorage,
  fetches iam.hanzo.ai/api/userinfo, maps IAM groups to org list
- DEFAULT_HANZO_APPS: Account, Billing, Console, Chat, Platform

Design tokens: bg-[#09090b]/90, border-white/[0.07], text-[11-13px], monochrome
Exported from @hanzo/ui navigation index for use across all Hanzo properties.
2026-03-01 12:38:58 -08:00
hanzo-dev 8ed0e27d73 [cleanup] remove AI slop files 2026-02-28 12:25:38 -08:00
hanzo-dev 9958dde439 feat: add @hanzo/og programmatic OG image package
Shared package for generating OpenGraph/social images across all Hanzo
sites. Supports 6 layout variants (page, model, code, stat, split,
minimal) with inline styles for next/og ImageResponse compatibility.
Includes HANZO_AI_THEME and HANZO_INDUSTRIES_THEME brand presets.
2026-02-28 10:45:00 -08:00
hanzo-dev e64e9cdbdc perf: reduce @hanzo/ui bundle 74% by removing duplicate entries and enabling minification
- Remove duplicate tsup entries: primitives-export and primitives/index
  both compiled same primitives/index-standard.ts → save 2×436K=870K
- Enable minify:true; safe because 'use client' banner is added post-build
  via onSuccess hook, not as source directive
- Update ./primitives export in package.json to point to ./dist/index.mjs
- Result: dist 10M → 2.6M, index.mjs 436K → 314K (minified)

Published as @hanzo/ui@5.3.36
2026-02-28 10:27:43 -08:00
hanzo-dev 095773b2da chore: bump versions — @hanzo/ui@5.3.35, @hanzo/auth-firebase@1.0.0, @hanzo/commerce@7.5.1 2026-02-27 21:30:24 -08:00
hanzo-dev 926985e5ad chore: update generated registry and color files from build 2026-02-27 21:28:17 -08:00
hanzo-dev 24ace5354a fix: resolve build errors — add avatar/org to HanzoUserInfoStore, fix Zod v3/v4 type mismatch, generate d.ts files in @hanzo/ui build 2026-02-27 21:20:16 -08:00
hanzo-dev 412ee93a25 chore: untrack .claude/settings.local.json, add to gitignore 2026-02-27 20:42:26 -08:00
hanzo-dev 751111a0d7 chore: update pnpm-lock.yaml 2026-02-27 19:35:51 -08:00
hanzo-dev 8dab1688f7 Remove Firebase from @hanzo/auth and @hanzo/commerce
Firebase has been completely removed from core packages:
- @hanzo/auth v2.8.0: stub implementations replace Firebase auth/wallet
- @hanzo/commerce v7.5.0: no-op order persistence replaces Firestore
- Firebase code preserved in @hanzo/auth-firebase (optional package)
- Zero Firebase imports in auth and commerce packages
2026-02-27 19:35:51 -08:00
Zoo Queenandhanzo-dev 7887485e95 feat(commerce): add billingRefund method for correction deposits
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-02-20 19:07:18 -08:00
Zoo Queenandhanzo-dev 3e1283365a refactor: clean Commerce API client — one class, no aliases
Commerce is THE client for the Commerce API. No aliases, no backwards
compat wrappers. import { Commerce } from '@hanzo/commerce'

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-02-17 21:06:22 -08:00
Zoo Queenandhanzo-dev 8919c32d0f feat: rename BillingClient to CommerceClient, add @hanzo/commerce/client
CommerceClient is the canonical Commerce API client. @hanzo/commerce/billing
re-exports as BillingClient for backwards compat.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-02-17 21:05:35 -08:00
Zoo Queenandhanzo-dev be95271bc4 fix: update repository URLs to hanzoai/ui for npm provenance
npm provenance requires repository.url to match the publishing repo.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-02-17 21:02:20 -08:00
Zoo Queenandhanzo-dev f2752141ff feat: add BillingClient to @hanzo/commerce, fix npm-publish token
- billing.ts: canonical billing client for Commerce API
- Exports at @hanzo/commerce/billing
- Fix workspace:* peerDeps (resolve to version ranges for npm compat)
- Fix npm-publish workflow to use NPM_TOKEN secret

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-02-17 20:59:46 -08:00
Zoo Queenandhanzo-dev b6311022f3 fix: add avatar and organization fields to Firebase auth store
All HanzoUserInfo implementations must include the new fields.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-02-17 20:55:38 -08:00
Zoo Queenandhanzo-dev 07f1182d52 feat: add IAM auth service as primary auth provider
- IamAuthService: OIDC/PKCE-based auth, auto-registers when IAM config present
- Enhanced types: avatar, organization fields on HanzoUserInfo
- AuthServiceConf: IAM fields (iamServerUrl, iamClientId, etc.)
- Firebase becomes optional integration, IAM is the default
- Fix npm-publish workflow: add id-token permission for provenance

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-02-17 20:52:49 -08:00
Zoo Queenandhanzo-dev bacd545b67 fix: resolve pnpm version conflict in publish workflow
Remove explicit version to let packageManager field in package.json
control the pnpm version.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-02-17 20:47:34 -08:00
Zoo Queenandhanzo-dev d95693b61a chore: bump @hanzo/auth to 2.7.0
Adds shared OrgProjectSwitcher component for unified org/project
selection across all Hanzo apps.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-02-17 20:45:28 -08:00
Zoo Queenandhanzo-dev dce79e1309 feat: add shared OrgProjectSwitcher component to @hanzo/auth
Props-driven org/project/environment switcher used across all
Hanzo services. Supports single-org display, multi-org dropdown,
project selector, and environment badge.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-02-17 20:38:07 -08:00
386 changed files with 16912 additions and 5943 deletions
-17
View File
@@ -1,17 +0,0 @@
{
"permissions": {
"allow": [
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(git push:*)",
"Bash(pnpm registry:build:*)",
"Bash(pnpm build:*)",
"Bash(pnpm add:*)",
"Bash(pnpm dev)",
"Bash(jq:*)",
"Bash(curl:*)"
],
"deny": [],
"ask": []
}
}
+5 -1
View File
@@ -1,5 +1,9 @@
name: NPM Publish
permissions:
contents: write
id-token: write
on:
workflow_dispatch:
inputs:
@@ -78,7 +82,7 @@ jobs:
- name: Bump version and publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true
run: |
# Set up npm and pnpm auth
-2
View File
@@ -57,8 +57,6 @@ jobs:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
+1
View File
@@ -48,6 +48,7 @@ jobs:
run: pnpm build
env:
NODE_OPTIONS: --max-old-space-size=8192
NEXT_PUBLIC_APP_URL: https://ui.hanzo.ai
- name: Run component health check
run: pnpm health-check
+2
View File
@@ -78,3 +78,5 @@ playwright-report/
.fleet
.notes
.claude/settings.local.json
pkg/ui/dist-test/
+430 -8
View File
File diff suppressed because one or more lines are too long
+12 -5
View File
@@ -1,8 +1,15 @@
// @ts-nocheck
import { dynamic } from '@hanzo/docs-mdx/runtime/dynamic';
import * as Config from '../source.config';
import { dynamic } from "@hanzo/docs-mdx/runtime/dynamic"
const create = await dynamic<typeof Config, import("@hanzo/docs-mdx/runtime/types").InternalTypeConfig & {
DocData: {
import * as Config from "../source.config"
const create = await dynamic<
typeof Config,
import("@hanzo/docs-mdx/runtime/types").InternalTypeConfig & {
DocData: {}
}
}>(Config, {"configPath":"source.config.ts","environment":"next","outDir":".docs"}, {"doc":{"passthroughs":["extractedReferences"]}});
>(
Config,
{ configPath: "source.config.ts", environment: "next", outDir: ".docs" },
{ doc: { passthroughs: ["extractedReferences"] } }
)
+388 -192
View File
File diff suppressed because one or more lines are too long
+11
View File
@@ -203,6 +203,17 @@ export const Index: Record<string, any> = {
subcategory: "undefined",
chunks: []
},
"direction": {
name: "direction",
type: "components:ui",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/ui/direction")),
source: "",
files: ["registry/default/ui/direction.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"dialog": {
name: "dialog",
type: "components:ui",
+143
View File
@@ -0,0 +1,143 @@
/**
* docs-no-404.test.ts
*
* Static validation: every URL in docs.ts config has a matching MDX file.
* No server needed — runs in CI before deployment.
*
* Run: pnpm test (via vitest)
*/
import { describe, it, expect } from 'vitest'
import fs from 'node:fs'
import path from 'node:path'
const CONTENT_DIR = path.resolve(__dirname, '../content/docs')
const CONFIG_PATH = path.resolve(__dirname, '../config/docs.ts')
/** Extract all href values from docs.ts config */
function extractUrlsFromConfig(): string[] {
const content = fs.readFileSync(CONFIG_PATH, 'utf-8')
const hrefRegex = /href:\s*["'`]([^"'`]+)["'`]/g
const urls: string[] = []
let match
while ((match = hrefRegex.exec(content)) !== null) {
urls.push(match[1])
}
return urls.filter(u => u.startsWith('/docs'))
}
/** Convert a /docs/... URL to its expected MDX file path */
function urlToMdxPath(url: string): string[] {
// /docs -> index.mdx
// /docs/charts/bar -> charts/bar.mdx OR charts/bar/index.mdx
const slug = url.replace(/^\/docs\/?/, '') || ''
if (!slug) {
return [path.join(CONTENT_DIR, 'index.mdx')]
}
return [
path.join(CONTENT_DIR, `${slug}.mdx`),
path.join(CONTENT_DIR, slug, 'index.mdx'),
]
}
/** Recursively find all MDX files in content/docs */
function findAllMdxFiles(dir: string, prefix = ''): string[] {
const results: string[] = []
if (!fs.existsSync(dir)) return results
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const fullPath = path.join(dir, entry.name)
if (entry.isDirectory()) {
results.push(...findAllMdxFiles(fullPath, `${prefix}${entry.name}/`))
} else if (entry.name.endsWith('.mdx')) {
const slug = entry.name === 'index.mdx'
? prefix.replace(/\/$/, '')
: `${prefix}${entry.name.replace('.mdx', '')}`
if (slug) {
results.push(`/docs/${slug}`)
} else {
results.push('/docs')
}
}
}
return results
}
describe('Documentation pages — zero 404s', () => {
const configUrls = extractUrlsFromConfig()
const mdxPages = findAllMdxFiles(CONTENT_DIR)
it('should find docs config with URLs', () => {
expect(configUrls.length).toBeGreaterThan(100)
})
it('should find MDX content files', () => {
expect(mdxPages.length).toBeGreaterThan(100)
})
describe('every config URL has a matching MDX file', () => {
for (const url of configUrls) {
it(`${url}`, () => {
const possiblePaths = urlToMdxPath(url)
const exists = possiblePaths.some(p => fs.existsSync(p))
if (!exists) {
throw new Error(
`Missing MDX file for ${url}. Expected one of:\n` +
possiblePaths.map(p => ` - ${p}`).join('\n')
)
}
})
}
})
describe('every MDX file is reachable from config', () => {
const configSet = new Set(configUrls)
const unreachable: string[] = []
for (const page of mdxPages) {
if (!configSet.has(page)) {
unreachable.push(page)
}
}
it('should have no orphaned MDX files (or document known exceptions)', () => {
// Allow a small number of known orphans (category index pages, etc.)
const KNOWN_ORPHANS = new Set([
'/docs/ai/voice-settings',
'/docs/components/animation-components',
])
const unexpected = unreachable.filter(u => !KNOWN_ORPHANS.has(u))
if (unexpected.length > 0) {
console.warn(`Orphaned MDX files (not in nav config):\n${unexpected.map(u => ` ${u}`).join('\n')}`)
}
// Warn but don't fail for orphans — they just won't be in nav
expect(true).toBe(true)
})
})
it('summary: config URLs vs MDX files', () => {
const configSet = new Set(configUrls)
const mdxSet = new Set(mdxPages)
const missingMdx = configUrls.filter(u => {
const paths = urlToMdxPath(u)
return !paths.some(p => fs.existsSync(p))
})
const orphanedMdx = mdxPages.filter(p => !configSet.has(p))
console.log(`\n📊 Docs coverage report:`)
console.log(` Config URLs: ${configUrls.length}`)
console.log(` MDX files: ${mdxPages.length}`)
console.log(` Missing MDX: ${missingMdx.length}`)
console.log(` Orphaned: ${orphanedMdx.length}`)
if (missingMdx.length > 0) {
console.error(`\n❌ URLs with no MDX file:\n${missingMdx.map(u => ` ${u}`).join('\n')}`)
}
if (orphanedMdx.length > 0) {
console.warn(`\n⚠️ MDX files not in nav:\n${orphanedMdx.map(u => ` ${u}`).join('\n')}`)
}
// This is the hard check — every config URL must have content
expect(missingMdx).toEqual([])
})
})
+29 -29
View File
@@ -1,8 +1,9 @@
"use client"
import * as React from "react"
import { AlertCircle, RefreshCw } from "lucide-react"
import { Index } from "@/__registry__"
import { AlertCircle, RefreshCw } from "lucide-react"
import { Badge } from "@/registry/default/ui/badge"
import { Button } from "@/registry/default/ui/button"
import { Card } from "@/registry/default/ui/card"
@@ -64,24 +65,21 @@ function BlockPreview({ blockName }: { blockName: string }) {
setState({ loaded: true, error: true, showContent: true })
}, [])
const handleRetry = React.useCallback(
(e: React.MouseEvent) => {
e.preventDefault()
e.stopPropagation()
setState({ loaded: false, error: false, showContent: false })
if (iframeRef.current) {
// Force reload by resetting src
const src = iframeRef.current.src
iframeRef.current.src = ""
setTimeout(() => {
if (iframeRef.current) {
iframeRef.current.src = src
}
}, 0)
}
},
[]
)
const handleRetry = React.useCallback((e: React.MouseEvent) => {
e.preventDefault()
e.stopPropagation()
setState({ loaded: false, error: false, showContent: false })
if (iframeRef.current) {
// Force reload by resetting src
const src = iframeRef.current.src
iframeRef.current.src = ""
setTimeout(() => {
if (iframeRef.current) {
iframeRef.current.src = src
}
}, 0)
}
}, [])
React.useEffect(() => {
return () => {
@@ -92,7 +90,10 @@ function BlockPreview({ blockName }: { blockName: string }) {
}, [])
return (
<div ref={containerRef} className="relative aspect-[4/3] min-h-[400px] bg-muted/50">
<div
ref={containerRef}
className="relative aspect-[4/3] min-h-[400px] bg-muted/50"
>
{/* Loading skeleton */}
{!state.showContent && (
<div className="absolute inset-0 flex items-center justify-center">
@@ -273,15 +274,14 @@ export default function BuilderPage() {
<h3 className="text-base font-semibold leading-tight">
{blockName}
</h3>
{block?.category &&
block?.category !== "undefined" && (
<Badge
variant="outline"
className="shrink-0 text-xs font-medium"
>
{block.category}
</Badge>
)}
{block?.category && block?.category !== "undefined" && (
<Badge
variant="outline"
className="shrink-0 text-xs font-medium"
>
{block.category}
</Badge>
)}
</div>
{block?.subcategory &&
block?.subcategory !== "undefined" && (
+1 -1
View File
@@ -1,12 +1,12 @@
import Link from "next/link"
import { notFound } from "next/navigation"
import { mdxComponents } from "@/mdx-components"
import { findNeighbour } from "@hanzo/docs-core/page-tree"
import {
IconArrowLeft,
IconArrowRight,
IconArrowUpRight,
} from "@tabler/icons-react"
import { findNeighbour } from "@hanzo/docs-core/page-tree"
import { source } from "@/lib/source"
import { absoluteUrl } from "@/lib/utils"
+5 -5
View File
@@ -2,11 +2,6 @@ import "@/styles/globals.css"
import { Metadata, Viewport } from "next"
const META_THEME_COLORS = {
light: "white",
dark: "black",
}
import { siteConfig } from "@/config/site"
import { fontMono, fontSans } from "@/lib/fonts"
import { cn } from "@/lib/utils"
@@ -23,6 +18,11 @@ import {
Toaster as NewYorkToaster,
} from "@/registry/default/ui/toaster"
const META_THEME_COLORS = {
light: "white",
dark: "black",
}
export const metadata: Metadata = {
title: {
default: siteConfig.name,
+5 -1
View File
@@ -222,7 +222,11 @@ function BlockViewerToolbar({ styleName }: { styleName: Style["name"] }) {
}}
title="Copy install command"
>
{isCopied ? <Check className="!h-3.5 !w-3.5" /> : <Clipboard className="!h-3.5 !w-3.5" />}
{isCopied ? (
<Check className="!h-3.5 !w-3.5" />
) : (
<Clipboard className="!h-3.5 !w-3.5" />
)}
<span className="sr-only">Copy install command</span>
</Button>
<Separator orientation="vertical" className="mx-1 !h-4" />
+200
View File
@@ -97,6 +97,82 @@ export const docsConfig: DocsConfig = {
href: "/docs/changelog",
items: [],
},
{
title: "MCP Server",
href: "/docs/mcp",
items: [],
label: "New",
},
{
title: "About",
href: "/docs/about",
items: [],
},
],
},
{
title: "Installation",
items: [
{
title: "Next.js",
href: "/docs/installation/next",
items: [],
},
{
title: "Vite",
href: "/docs/installation/vite",
items: [],
},
{
title: "Remix",
href: "/docs/installation/remix",
items: [],
},
{
title: "Astro",
href: "/docs/installation/astro",
items: [],
},
{
title: "Gatsby",
href: "/docs/installation/gatsby",
items: [],
},
{
title: "Laravel",
href: "/docs/installation/laravel",
items: [],
},
{
title: "Manual",
href: "/docs/installation/manual",
items: [],
},
],
},
{
title: "Dark Mode",
items: [
{
title: "Next.js",
href: "/docs/dark-mode/next",
items: [],
},
{
title: "Vite",
href: "/docs/dark-mode/vite",
items: [],
},
{
title: "Remix",
href: "/docs/dark-mode/remix",
items: [],
},
{
title: "Astro",
href: "/docs/dark-mode/astro",
items: [],
},
],
},
{
@@ -143,6 +219,12 @@ export const docsConfig: DocsConfig = {
href: "/docs/components/button",
items: [],
},
{
title: "Button Group",
href: "/docs/components/button-group",
items: [],
label: "New",
},
{
title: "Calendar",
href: "/docs/components/calendar",
@@ -188,6 +270,30 @@ export const docsConfig: DocsConfig = {
href: "/docs/components/data-table",
items: [],
},
{
title: "Desktop",
href: "/docs/components/desktop",
items: [],
label: "New",
},
{
title: "Desktop Hooks",
href: "/docs/components/desktop-hooks",
items: [],
label: "New",
},
{
title: "Desktop Spotlight",
href: "/docs/components/desktop-spotlight",
items: [],
label: "New",
},
{
title: "Desktop Window",
href: "/docs/components/desktop-window",
items: [],
label: "New",
},
{
title: "Date Picker",
href: "/docs/components/date-picker",
@@ -198,6 +304,12 @@ export const docsConfig: DocsConfig = {
href: "/docs/components/dialog",
items: [],
},
{
title: "Direction",
href: "/docs/components/direction",
items: [],
label: "New",
},
{
title: "Drawer",
href: "/docs/components/drawer",
@@ -208,6 +320,18 @@ export const docsConfig: DocsConfig = {
href: "/docs/components/dropdown-menu",
items: [],
},
{
title: "Empty State",
href: "/docs/components/empty",
items: [],
label: "New",
},
{
title: "Field",
href: "/docs/components/field",
items: [],
label: "New",
},
{
title: "Form",
href: "/docs/components/form",
@@ -223,12 +347,24 @@ export const docsConfig: DocsConfig = {
href: "/docs/components/input",
items: [],
},
{
title: "Input Group",
href: "/docs/components/input-group",
items: [],
label: "New",
},
{
title: "Input OTP",
href: "/docs/components/input-otp",
items: [],
label: "New",
},
{
title: "Item",
href: "/docs/components/item",
items: [],
label: "New",
},
{
title: "Label",
href: "/docs/components/label",
@@ -613,6 +749,24 @@ export const docsConfig: DocsConfig = {
items: [],
label: "New",
},
{
title: "Particles Background",
href: "/docs/components/particles-background",
items: [],
label: "New",
},
{
title: "Grid Pattern",
href: "/docs/components/grid-pattern",
items: [],
label: "New",
},
{
title: "Timeline",
href: "/docs/components/timeline",
items: [],
label: "New",
},
{
title: "Pin List",
href: "/docs/components/pin-list",
@@ -1028,6 +1182,18 @@ export const docsConfig: DocsConfig = {
items: [],
label: "New",
},
{
title: "AI Code",
href: "/docs/ai/code",
items: [],
label: "New",
},
{
title: "AI Voice",
href: "/docs/ai/voice",
items: [],
label: "New",
},
],
},
{
@@ -1174,6 +1340,40 @@ export const docsConfig: DocsConfig = {
},
],
},
{
title: "Guides",
items: [
{
title: "Page Builder",
href: "/docs/guides/page-builder",
items: [],
label: "New",
},
{
title: "Visual Workflows",
href: "/docs/guides/visual-workflows",
items: [],
label: "New",
},
],
},
{
title: "Hanzo React",
items: [
{
title: "Getting Started",
href: "/docs/hanzo-react/getting-started",
items: [],
label: "New",
},
{
title: "Hooks",
href: "/docs/hanzo-react/hooks",
items: [],
label: "New",
},
],
},
{
title: "Packages",
items: [
+27
View File
@@ -0,0 +1,27 @@
---
title: Contact Forms
description: Contact form sections with validation, multiple fields, and submission handling.
---
## Overview
Contact form blocks provide complete form layouts for collecting user inquiries. They include input validation, loading states, and success confirmation.
## Features
- **Form Validation**: Built-in HTML5 and custom validation
- **Multiple Fields**: Name, email, subject, and message inputs
- **Loading States**: Visual feedback during form submission
- **Success Message**: Confirmation after successful submission
- **Responsive Design**: Adapts to all screen sizes
- **Accessible**: Semantic form elements with proper labels
## Usage
```tsx
import ContactBlock from "@/registry/default/block/contact"
export default function Page() {
return <ContactBlock />
}
```
+26
View File
@@ -0,0 +1,26 @@
---
title: CTA Sections
description: Call-to-action sections for driving user conversions and signups.
---
## Overview
CTA (Call-to-Action) blocks are designed to drive user engagement and conversions. They feature prominent buttons, compelling copy, and focused layouts that guide users toward a specific action.
## Features
- **Conversion-Focused**: Designed to maximize click-through rates
- **Responsive Design**: Adapts to all screen sizes
- **Multiple Layouts**: Centered, split, and banner variations
- **Button Variants**: Primary, secondary, and ghost CTA styles
- **Background Options**: Solid colors, gradients, and image backgrounds
## Usage
```tsx
import CTABlock from "@/registry/default/block/cta"
export default function Page() {
return <CTABlock />
}
```
+27
View File
@@ -0,0 +1,27 @@
---
title: FAQ Sections
description: Frequently asked questions sections with expandable accordions and search.
---
## Overview
FAQ blocks present commonly asked questions in organized, expandable layouts. They use accordion patterns for progressive disclosure and optional search filtering.
## Features
- **Accordion Layout**: Expandable question/answer pairs
- **Search Filtering**: Optional search to find specific questions
- **Categories**: Group questions by topic
- **Responsive**: Full-width layout that works on all devices
- **Accessible**: Keyboard navigation and screen reader support
- **Animated**: Smooth expand/collapse transitions
## Usage
```tsx
import FAQBlock from "@/registry/default/block/faq"
export default function Page() {
return <FAQBlock />
}
```
+26
View File
@@ -0,0 +1,26 @@
---
title: Feature Sections
description: Feature grid and list sections for highlighting product capabilities.
---
## Overview
Feature blocks display product capabilities in organized grid or list layouts. They combine icons, headings, and descriptions to communicate value propositions clearly.
## Features
- **Grid Layouts**: 2, 3, or 4 column responsive grids
- **Icon Support**: Lucide icons or custom SVGs
- **Responsive**: Stacks to single column on mobile
- **Customizable**: Flexible content and styling options
- **Accessible**: Semantic HTML with proper heading hierarchy
## Usage
```tsx
import FeaturesBlock from "@/registry/default/block/features"
export default function Page() {
return <FeaturesBlock />
}
```
+36
View File
@@ -0,0 +1,36 @@
---
title: Hero Sections
description: Full-width hero sections for landing pages with headlines, CTAs, and imagery.
---
## Overview
Hero blocks are viewport-sized sections designed for the top of landing pages. They feature bold typography, call-to-action buttons, and visual elements to capture user attention.
## Variations
### App UI Showcase
<ComponentPreview name="showcase-app-ui-01" />
A hero section showcasing your app's UI with a centered layout and product screenshot.
### E-commerce Showcase
<ComponentPreview name="showcase-ecommerce-01" />
A hero section designed for e-commerce with product imagery and shopping CTAs.
### Marketing Showcase
<ComponentPreview name="showcase-marketing-01" />
A bold marketing hero section with large typography and conversion-focused design.
## Features
- **Responsive Design**: Adapts to all screen sizes
- **CTA Buttons**: Primary and secondary call-to-action buttons
- **Visual Elements**: Support for images, gradients, and animations
- **Typography**: Large, bold headlines with supporting text
- **Customizable**: Easy to modify colors, text, and layout
+27
View File
@@ -0,0 +1,27 @@
---
title: Stats Sections
description: Statistics and metrics sections with counters, charts, and key figures.
---
## Overview
Stats blocks display key metrics and achievements in visually impactful layouts. They feature animated counters, comparison figures, and trend indicators.
## Features
- **Animated Counters**: Number animations on scroll into view
- **Multiple Layouts**: Grid, inline, and featured stat variations
- **Trend Indicators**: Up/down arrows with percentage changes
- **Responsive**: Adapts from multi-column to single column on mobile
- **Icons**: Support for metric-specific icons
- **Customizable**: Easy to update values and labels
## Usage
```tsx
import StatsBlock from "@/registry/default/block/stats"
export default function Page() {
return <StatsBlock />
}
```
+27
View File
@@ -0,0 +1,27 @@
---
title: Team Sections
description: Team member grid and list sections with photos, roles, and social links.
---
## Overview
Team blocks showcase team members with profile photos, names, roles, and social links. They support multiple layout variations for different page contexts.
## Features
- **Grid Layout**: Responsive grid with team member cards
- **Profile Photos**: Avatar images with fallback initials
- **Role Display**: Job titles and department labels
- **Social Links**: GitHub, Twitter, LinkedIn integration
- **Responsive**: Adapts columns based on screen size
- **Hover Effects**: Interactive card hover states
## Usage
```tsx
import TeamBlock from "@/registry/default/block/team"
export default function Page() {
return <TeamBlock />
}
```
+27
View File
@@ -0,0 +1,27 @@
---
title: Testimonials
description: Testimonial sections with user quotes, ratings, and social proof.
---
## Overview
Testimonial blocks display customer reviews and social proof in visually appealing layouts. They support avatars, star ratings, company logos, and quote formatting.
## Features
- **Multiple Layouts**: Grid, carousel, and featured testimonial styles
- **Avatar Support**: User photos with name and title
- **Star Ratings**: Visual rating display
- **Company Logos**: Brand association for B2B testimonials
- **Responsive**: Adapts from multi-column to single column on mobile
- **Animated**: Optional entrance animations and carousel transitions
## Usage
```tsx
import TestimonialsBlock from "@/registry/default/block/testimonials"
export default function Page() {
return <TestimonialsBlock />
}
```
+64
View File
@@ -0,0 +1,64 @@
---
title: Area Chart
description: Area charts for visualizing trends and cumulative values over time.
---
<ComponentPreview name="chart-demo" description="Area chart example" />
## Overview
Area charts are used to show trends over time or across categories. They combine line charts with filled areas beneath the line to emphasize volume.
## Usage
```tsx
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@hanzo/ui"
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
const data = [
{ month: "Jan", desktop: 186, mobile: 80 },
{ month: "Feb", desktop: 305, mobile: 200 },
{ month: "Mar", desktop: 237, mobile: 120 },
]
const config = {
desktop: { label: "Desktop", color: "hsl(var(--chart-1))" },
mobile: { label: "Mobile", color: "hsl(var(--chart-2))" },
}
export function AreaChartDemo() {
return (
<ChartContainer config={config}>
<AreaChart data={data}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" />
<ChartTooltip content={<ChartTooltipContent />} />
<Area
dataKey="desktop"
fill="var(--color-desktop)"
stroke="var(--color-desktop)"
/>
<Area
dataKey="mobile"
fill="var(--color-mobile)"
stroke="var(--color-mobile)"
/>
</AreaChart>
</ChartContainer>
)
}
```
## Variants
### Stacked Area
Stack multiple areas to show cumulative totals.
### Gradient Fill
Apply gradient fills for visual depth.
### Step Area
Use `type="step"` for discrete step transitions.
+58
View File
@@ -0,0 +1,58 @@
---
title: Bar Chart
description: Bar charts for comparing categorical data and distributions.
---
<ComponentPreview name="chart-demo" description="Bar chart example" />
## Overview
Bar charts display data with rectangular bars proportional to their values. They are ideal for comparing quantities across categories.
## Usage
```tsx
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@hanzo/ui"
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
const data = [
{ month: "Jan", desktop: 186 },
{ month: "Feb", desktop: 305 },
{ month: "Mar", desktop: 237 },
]
const config = {
desktop: { label: "Desktop", color: "hsl(var(--chart-1))" },
}
export function BarChartDemo() {
return (
<ChartContainer config={config}>
<BarChart data={data}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" />
<ChartTooltip content={<ChartTooltipContent />} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
</BarChart>
</ChartContainer>
)
}
```
## Variants
### Horizontal Bar
Use `layout="vertical"` for horizontal orientation.
### Stacked Bar
Stack multiple bars to show composition within categories.
### Mixed Bar
Combine different bar styles and data series.
### With Labels
Add data labels directly on bars using custom label components.
+108
View File
@@ -0,0 +1,108 @@
---
title: Charts
description: Beautiful, composable chart components built on Recharts.
---
## Overview
@hanzo/ui provides a set of chart components built on top of [Recharts](https://recharts.org/). The chart components are designed to be composable and work with Tailwind CSS theming.
## Installation
<Tabs defaultValue="cli">
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx @hanzo/ui@latest add chart
```
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Install recharts:</Step>
```bash
pnpm add recharts
```
<Step>Copy the chart components into your project.</Step>
<ComponentSource name="chart" />
</Steps>
</TabsContent>
</Tabs>
## Components
The chart package includes the following components:
- **ChartContainer** - Responsive container that handles theming and sizing
- **ChartTooltip** - Styled tooltip component
- **ChartTooltipContent** - Content renderer for tooltips
- **ChartLegend** - Chart legend component
- **ChartLegendContent** - Content renderer for legends
## Chart Types
- [Area Chart](/docs/charts/area) - For showing trends over time
- [Bar Chart](/docs/charts/bar) - For comparing categorical data
- [Line Chart](/docs/charts/line) - For showing continuous data
- [Pie Chart](/docs/charts/pie) - For showing proportional data
- [Radar Chart](/docs/charts/radar) - For multivariate data comparison
- [Radial Chart](/docs/charts/radial) - For circular data visualization
- [Tooltip](/docs/charts/tooltip) - Shared tooltip component
## Usage
```tsx
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@hanzo/ui"
import { Bar, BarChart, XAxis } from "recharts"
const data = [
{ month: "Jan", value: 186 },
{ month: "Feb", value: 305 },
{ month: "Mar", value: 237 },
]
const config = {
value: { label: "Value", color: "hsl(var(--chart-1))" },
}
export function MyChart() {
return (
<ChartContainer config={config}>
<BarChart data={data}>
<XAxis dataKey="month" />
<ChartTooltip content={<ChartTooltipContent />} />
<Bar dataKey="value" fill="var(--color-value)" />
</BarChart>
</ChartContainer>
)
}
```
## Theming
Charts use CSS variables for theming. Define chart colors in your CSS:
```css
:root {
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
}
```
+60
View File
@@ -0,0 +1,60 @@
---
title: Line Chart
description: Line charts for showing continuous data trends and comparisons.
---
<ComponentPreview name="chart-demo" description="Line chart example" />
## Overview
Line charts connect data points with straight or curved lines, ideal for showing trends and changes over continuous intervals.
## Usage
```tsx
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@hanzo/ui"
import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
const data = [
{ month: "Jan", desktop: 186, mobile: 80 },
{ month: "Feb", desktop: 305, mobile: 200 },
{ month: "Mar", desktop: 237, mobile: 120 },
]
const config = {
desktop: { label: "Desktop", color: "hsl(var(--chart-1))" },
mobile: { label: "Mobile", color: "hsl(var(--chart-2))" },
}
export function LineChartDemo() {
return (
<ChartContainer config={config}>
<LineChart data={data}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" />
<ChartTooltip content={<ChartTooltipContent />} />
<Line dataKey="desktop" stroke="var(--color-desktop)" strokeWidth={2} />
<Line dataKey="mobile" stroke="var(--color-mobile)" strokeWidth={2} />
</LineChart>
</ChartContainer>
)
}
```
## Variants
### With Dots
Show data point markers on the line.
### Custom Dots
Use custom shapes for data point markers.
### Linear vs Curved
Toggle between `type="linear"` and `type="monotone"` interpolation.
### Interactive
Add hover interactions and clickable data points.
+52
View File
@@ -0,0 +1,52 @@
---
title: Pie Chart
description: Pie and donut charts for showing proportional data and distributions.
---
## Overview
Pie charts display data as proportional slices of a circle, ideal for showing parts of a whole.
## Usage
```tsx
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@hanzo/ui"
import { Pie, PieChart } from "recharts"
const data = [
{ browser: "Chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "Safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "Firefox", visitors: 187, fill: "var(--color-firefox)" },
]
const config = {
chrome: { label: "Chrome", color: "hsl(var(--chart-1))" },
safari: { label: "Safari", color: "hsl(var(--chart-2))" },
firefox: { label: "Firefox", color: "hsl(var(--chart-3))" },
}
export function PieChartDemo() {
return (
<ChartContainer config={config}>
<PieChart>
<ChartTooltip content={<ChartTooltipContent />} />
<Pie data={data} dataKey="visitors" nameKey="browser" />
</PieChart>
</ChartContainer>
)
}
```
## Variants
### Donut Chart
Use `innerRadius` and `outerRadius` to create donut charts.
### With Labels
Add labels inside or outside slices.
### Interactive
Highlight slices on hover with `activeIndex`.
+53
View File
@@ -0,0 +1,53 @@
---
title: Radar Chart
description: Radar charts for multivariate data comparison and analysis.
---
## Overview
Radar charts display multivariate data on axes starting from the same point, useful for comparing multiple quantitative variables.
## Usage
```tsx
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@hanzo/ui"
import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
const data = [
{ subject: "Math", A: 120, B: 110 },
{ subject: "Chinese", A: 98, B: 130 },
{ subject: "English", A: 86, B: 130 },
{ subject: "Geography", A: 99, B: 100 },
{ subject: "Physics", A: 85, B: 90 },
{ subject: "History", A: 65, B: 85 },
]
const config = {
A: { label: "Student A", color: "hsl(var(--chart-1))" },
B: { label: "Student B", color: "hsl(var(--chart-2))" },
}
export function RadarChartDemo() {
return (
<ChartContainer config={config}>
<RadarChart data={data}>
<PolarGrid />
<PolarAngleAxis dataKey="subject" />
<ChartTooltip content={<ChartTooltipContent />} />
<Radar dataKey="A" fill="var(--color-A)" fillOpacity={0.6} />
<Radar dataKey="B" fill="var(--color-B)" fillOpacity={0.6} />
</RadarChart>
</ChartContainer>
)
}
```
## Variants
### Filled Radar
Use `fillOpacity` to create filled radar areas.
### Multiple Series
Compare multiple datasets on the same radar.
+52
View File
@@ -0,0 +1,52 @@
---
title: Radial Chart
description: Radial bar charts for circular data visualization and progress indicators.
---
## Overview
Radial charts display data in a circular layout, useful for progress indicators, gauges, and circular comparisons.
## Usage
```tsx
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@hanzo/ui"
import { RadialBar, RadialBarChart } from "recharts"
const data = [
{ name: "Chrome", visitors: 275, fill: "var(--color-chrome)" },
{ name: "Safari", visitors: 200, fill: "var(--color-safari)" },
{ name: "Firefox", visitors: 187, fill: "var(--color-firefox)" },
]
const config = {
chrome: { label: "Chrome", color: "hsl(var(--chart-1))" },
safari: { label: "Safari", color: "hsl(var(--chart-2))" },
firefox: { label: "Firefox", color: "hsl(var(--chart-3))" },
}
export function RadialChartDemo() {
return (
<ChartContainer config={config}>
<RadialBarChart data={data} innerRadius={30} outerRadius={110}>
<ChartTooltip content={<ChartTooltipContent />} />
<RadialBar dataKey="visitors" />
</RadialBarChart>
</ChartContainer>
)
}
```
## Variants
### Stacked Radial
Stack multiple radial bars concentrically.
### With Labels
Add labels to radial segments for clarity.
### Progress Gauge
Use a single radial bar as a progress or gauge indicator.
+61
View File
@@ -0,0 +1,61 @@
---
title: Chart Tooltip
description: Customizable tooltip component for all chart types.
---
## Overview
The chart tooltip component provides a consistent, themed tooltip across all chart types. It integrates with the chart config system for automatic label and color resolution.
## Usage
```tsx
import { ChartTooltip, ChartTooltipContent } from "@hanzo/ui"
// Inside any chart component:
;<ChartTooltip content={<ChartTooltipContent />} />
```
## Props
### ChartTooltipContent
| Prop | Type | Default | Description |
| ------------- | --------------------------- | ------- | ------------------------- |
| hideLabel | boolean | false | Hide the label in tooltip |
| hideIndicator | boolean | false | Hide the color indicator |
| indicator | "line" \| "dot" \| "dashed" | "dot" | Indicator style |
| nameKey | string | - | Key for item names |
| labelKey | string | - | Key for labels |
## Customization
### Custom Formatter
```tsx
<ChartTooltip
content={
<ChartTooltipContent
formatter={(value, name) => (
<span>
{name}: ${value.toLocaleString()}
</span>
)}
/>
}
/>
```
### Custom Label
```tsx
<ChartTooltip
content={<ChartTooltipContent labelFormatter={(label) => `Date: ${label}`} />}
/>
```
## Indicator Styles
- **dot** - Small colored circle (default)
- **line** - Colored line indicator
- **dashed** - Dashed line indicator
+90 -79
View File
@@ -63,18 +63,28 @@ function Desktop() {
return (
<div>
<button onClick={() => windows.openWindow('Settings')}>Open Settings</button>
<button onClick={() => windows.toggleWindow('Terminal')}>Toggle Terminal</button>
<button onClick={() => windows.openWindow("Settings")}>
Open Settings
</button>
<button onClick={() => windows.toggleWindow("Terminal")}>
Toggle Terminal
</button>
<button onClick={() => windows.closeAllWindows()}>Close All</button>
{windows.isOpen('Settings') && (
<Window title="Settings" onClose={() => windows.closeWindow('Settings')}>
{windows.isOpen("Settings") && (
<Window
title="Settings"
onClose={() => windows.closeWindow("Settings")}
>
...
</Window>
)}
{windows.isOpen('Terminal') && (
<Window title="Terminal" onClose={() => windows.closeWindow('Terminal')}>
{windows.isOpen("Terminal") && (
<Window
title="Terminal"
onClose={() => windows.closeWindow("Terminal")}
>
...
</Window>
)}
@@ -85,17 +95,17 @@ function Desktop() {
#### API
| Method | Type | Description |
|--------|------|-------------|
| `isOpen` | `(id: string) => boolean` | Check if a window is open |
| `openWindow` | `(id: string) => void` | Open a window |
| `closeWindow` | `(id: string) => void` | Close a window |
| `toggleWindow` | `(id: string) => void` | Toggle a window's open state |
| `closeAllWindows` | `() => void` | Close all windows |
| `focusWindow` | `(id: string) => void` | Focus/activate a window |
| `activeWindow` | `string \| null` | Currently active window |
| `openWindows` | `string[]` | List of all open window IDs |
| `windows` | `Record<string, boolean>` | Window state map |
| Method | Type | Description |
| ----------------- | ------------------------- | ---------------------------- |
| `isOpen` | `(id: string) => boolean` | Check if a window is open |
| `openWindow` | `(id: string) => void` | Open a window |
| `closeWindow` | `(id: string) => void` | Close a window |
| `toggleWindow` | `(id: string) => void` | Toggle a window's open state |
| `closeAllWindows` | `() => void` | Close all windows |
| `focusWindow` | `(id: string) => void` | Focus/activate a window |
| `activeWindow` | `string \| null` | Currently active window |
| `openWindows` | `string[]` | List of all open window IDs |
| `windows` | `Record<string, boolean>` | Window state map |
---
@@ -150,23 +160,23 @@ function SettingsPanel() {
#### API
| Property | Type | Description |
|----------|------|-------------|
| `theme` | `'light' \| 'dark' \| 'system'` | Current theme |
| `setTheme` | `(theme) => void` | Set theme |
| `colorScheme` | `string` | Color scheme name |
| `setColorScheme` | `(scheme) => void` | Set color scheme |
| `showDock` | `boolean` | Whether dock is visible |
| `setShowDock` | `(show) => void` | Toggle dock visibility |
| `dockPosition` | `'bottom' \| 'left' \| 'right'` | Dock position |
| `setDockPosition` | `(position) => void` | Set dock position |
| `dockMagnification` | `boolean` | Dock magnification enabled |
| `setDockMagnification` | `(enabled) => void` | Toggle magnification |
| `fontSize` | `number` | Base font size |
| `setFontSize` | `(size) => void` | Set font size |
| `windowTransparency` | `number` | Window transparency (0-1) |
| `setWindowTransparency` | `(opacity) => void` | Set transparency |
| `resetToDefaults` | `() => void` | Reset all settings |
| Property | Type | Description |
| ----------------------- | ------------------------------- | -------------------------- |
| `theme` | `'light' \| 'dark' \| 'system'` | Current theme |
| `setTheme` | `(theme) => void` | Set theme |
| `colorScheme` | `string` | Color scheme name |
| `setColorScheme` | `(scheme) => void` | Set color scheme |
| `showDock` | `boolean` | Whether dock is visible |
| `setShowDock` | `(show) => void` | Toggle dock visibility |
| `dockPosition` | `'bottom' \| 'left' \| 'right'` | Dock position |
| `setDockPosition` | `(position) => void` | Set dock position |
| `dockMagnification` | `boolean` | Dock magnification enabled |
| `setDockMagnification` | `(enabled) => void` | Toggle magnification |
| `fontSize` | `number` | Base font size |
| `setFontSize` | `(size) => void` | Set font size |
| `windowTransparency` | `number` | Window transparency (0-1) |
| `setWindowTransparency` | `(opacity) => void` | Set transparency |
| `resetToDefaults` | `() => void` | Reset all settings |
---
@@ -182,20 +192,18 @@ function Desktop() {
return (
<div>
<button onClick={() => overlays.open('spotlight')}>
<button onClick={() => overlays.open("spotlight")}>
Open Spotlight (⌘+Space)
</button>
<button onClick={() => overlays.open('about')}>
About This Mac
</button>
<button onClick={() => overlays.open("about")}>About This Mac</button>
{overlays.isOpen('spotlight') && (
<Spotlight onClose={() => overlays.close('spotlight')} />
{overlays.isOpen("spotlight") && (
<Spotlight onClose={() => overlays.close("spotlight")} />
)}
{overlays.isOpen('about') && (
<AboutDialog onClose={() => overlays.close('about')} />
{overlays.isOpen("about") && (
<AboutDialog onClose={() => overlays.close("about")} />
)}
</div>
)
@@ -204,14 +212,14 @@ function Desktop() {
#### API
| Property | Type | Description |
|----------|------|-------------|
| `overlays` | `OverlayState` | Current overlay states |
| `isOpen` | `(id: string) => boolean` | Check if an overlay is open |
| `open` | `(id: string) => void` | Open an overlay by id |
| `close` | `(id: string) => void` | Close an overlay by id |
| `toggle` | `(id: string) => void` | Toggle an overlay |
| `closeAll` | `() => void` | Close all overlays |
| Property | Type | Description |
| ---------- | ------------------------- | --------------------------- |
| `overlays` | `OverlayState` | Current overlay states |
| `isOpen` | `(id: string) => boolean` | Check if an overlay is open |
| `open` | `(id: string) => void` | Open an overlay by id |
| `close` | `(id: string) => void` | Close an overlay by id |
| `toggle` | `(id: string) => void` | Toggle an overlay |
| `closeAll` | `() => void` | Close all overlays |
Built-in overlay IDs: `spotlight`, `contextMenu`, `modal`, `drawer`
@@ -259,29 +267,29 @@ function Desktop() {
#### Shortcut Definition
| Property | Type | Description |
|----------|------|-------------|
| `key` | `string` | Key to listen for |
| `meta` | `boolean` | Require ⌘ (Mac) / Ctrl (Windows) |
| `alt` | `boolean` | Require Alt/Option |
| `shift` | `boolean` | Require Shift |
| `ctrl` | `boolean` | Require Control |
| `action` | `() => void` | Callback when triggered |
| Property | Type | Description |
| -------- | ------------ | -------------------------------- |
| `key` | `string` | Key to listen for |
| `meta` | `boolean` | Require ⌘ (Mac) / Ctrl (Windows) |
| `alt` | `boolean` | Require Alt/Option |
| `shift` | `boolean` | Require Shift |
| `ctrl` | `boolean` | Require Control |
| `action` | `() => void` | Callback when triggered |
## Complete Example
```tsx
import {
Window,
Spotlight,
useWindowManager,
useDesktopSettings,
useKeyboardShortcuts,
useOverlayManager,
useKeyboardShortcuts
useWindowManager,
Window,
} from "@hanzo/ui/desktop"
import { Dock, DockItem } from "@hanzo/ui/dock"
const apps = ['Finder', 'Terminal', 'Safari', 'Mail', 'Calendar', 'Settings']
const apps = ["Finder", "Terminal", "Safari", "Mail", "Calendar", "Settings"]
export function Desktop() {
const windows = useWindowManager()
@@ -290,9 +298,9 @@ export function Desktop() {
// Register keyboard shortcuts
useKeyboardShortcuts([
{ key: ' ', meta: true, action: overlays.openSpotlight },
{ key: 'q', meta: true, action: () => windows.close(windows.activeWindow) },
{ key: ',', meta: true, action: () => windows.open('Settings') },
{ key: " ", meta: true, action: overlays.openSpotlight },
{ key: "q", meta: true, action: () => windows.close(windows.activeWindow) },
{ key: ",", meta: true, action: () => windows.open("Settings") },
])
return (
@@ -300,33 +308,36 @@ export function Desktop() {
className="h-screen w-screen relative"
style={{
opacity: settings.windowTransparency,
fontSize: settings.fontSize
fontSize: settings.fontSize,
}}
>
{/* Desktop Background */}
<div className="absolute inset-0 bg-gradient-to-br from-blue-500 to-purple-600" />
{/* Windows */}
{apps.map(app => windows.isOpen(app) && (
<Window
key={app}
title={app}
onClose={() => windows.close(app)}
windowType={settings.theme === 'dark' ? 'dark' : 'default'}
>
<div className="p-4">{app} content</div>
</Window>
))}
{apps.map(
(app) =>
windows.isOpen(app) && (
<Window
key={app}
title={app}
onClose={() => windows.close(app)}
windowType={settings.theme === "dark" ? "dark" : "default"}
>
<div className="p-4">{app} content</div>
</Window>
)
)}
{/* Spotlight */}
{overlays.spotlight && (
<Spotlight
isOpen={true}
onClose={overlays.closeSpotlight}
items={apps.map(app => ({
items={apps.map((app) => ({
id: app.toLowerCase(),
label: app,
category: 'Applications'
category: "Applications",
}))}
onSelect={(item) => {
windows.open(item.label)
@@ -341,7 +352,7 @@ export function Desktop() {
position={settings.dockPosition}
magnification={settings.dockMagnification ? 60 : 0}
>
{apps.map(app => (
{apps.map((app) => (
<DockItem
key={app}
tooltip={app}
+138 -55
View File
@@ -57,9 +57,19 @@ npm install framer-motion lucide-react
import { Spotlight, SpotlightItem } from "@hanzo/ui/desktop"
const items: SpotlightItem[] = [
{ id: 'settings', title: 'Settings', category: 'Applications', icon: <Settings /> },
{ id: 'documents', title: 'Documents', category: 'Folders', icon: <Folder /> },
{ id: 'music', title: 'Music', category: 'Applications', icon: <Music /> },
{
id: "settings",
title: "Settings",
category: "Applications",
icon: <Settings />,
},
{
id: "documents",
title: "Documents",
category: "Folders",
icon: <Folder />,
},
{ id: "music", title: "Music", category: "Applications", icon: <Music /> },
]
export function SpotlightDemo() {
@@ -74,7 +84,7 @@ export function SpotlightDemo() {
onClose={() => setIsOpen(false)}
items={items}
onSelect={(item) => {
console.log('Selected:', item)
console.log("Selected:", item)
setIsOpen(false)
}}
/>
@@ -87,25 +97,25 @@ export function SpotlightDemo() {
### Spotlight
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `isOpen` | `boolean` | - | Controls visibility of the spotlight dialog |
| `onClose` | `() => void` | - | Callback when spotlight is closed |
| `items` | `SpotlightItem[]` | - | Array of searchable items |
| `onSelect` | `(item: SpotlightItem) => void` | - | Callback when an item is selected |
| `placeholder` | `string` | `'Search...'` | Search input placeholder text |
| Prop | Type | Default | Description |
| ------------- | ------------------------------- | ------------- | ------------------------------------------- |
| `isOpen` | `boolean` | - | Controls visibility of the spotlight dialog |
| `onClose` | `() => void` | - | Callback when spotlight is closed |
| `items` | `SpotlightItem[]` | - | Array of searchable items |
| `onSelect` | `(item: SpotlightItem) => void` | - | Callback when an item is selected |
| `placeholder` | `string` | `'Search...'` | Search input placeholder text |
### SpotlightItem
| Property | Type | Description |
|----------|------|-------------|
| `id` | `string` | Unique identifier for the item |
| `title` | `string` | Display title |
| `subtitle` | `string` | Optional subtitle/description text |
| `icon` | `ReactNode` | Optional icon to display |
| `category` | `string` | Category for grouping |
| `keywords` | `string[]` | Optional search keywords |
| `action` | `() => void` | Optional action callback |
| Property | Type | Description |
| ---------- | ------------ | ---------------------------------- |
| `id` | `string` | Unique identifier for the item |
| `title` | `string` | Display title |
| `subtitle` | `string` | Optional subtitle/description text |
| `icon` | `ReactNode` | Optional icon to display |
| `category` | `string` | Category for grouping |
| `keywords` | `string[]` | Optional search keywords |
| `action` | `() => void` | Optional action callback |
## Features
@@ -120,26 +130,70 @@ export function SpotlightDemo() {
### Application Launcher
```tsx
import { Spotlight, SpotlightItem, useKeyboardShortcuts } from "@hanzo/ui/desktop"
import {
Terminal,
Chrome,
Mail,
Spotlight,
SpotlightItem,
useKeyboardShortcuts,
} from "@hanzo/ui/desktop"
import {
Calendar,
Chrome,
Folder,
Mail,
Music,
Settings,
Folder
Terminal,
} from "lucide-react"
const apps: SpotlightItem[] = [
{ id: 'terminal', label: 'Terminal', category: 'Applications', icon: <Terminal className="h-5 w-5" /> },
{ id: 'safari', label: 'Safari', category: 'Applications', icon: <Chrome className="h-5 w-5" /> },
{ id: 'mail', label: 'Mail', category: 'Applications', icon: <Mail className="h-5 w-5" /> },
{ id: 'calendar', label: 'Calendar', category: 'Applications', icon: <Calendar className="h-5 w-5" /> },
{ id: 'music', label: 'Music', category: 'Applications', icon: <Music className="h-5 w-5" /> },
{ id: 'settings', label: 'System Preferences', category: 'Applications', icon: <Settings className="h-5 w-5" /> },
{ id: 'documents', label: 'Documents', category: 'Folders', icon: <Folder className="h-5 w-5" /> },
{ id: 'downloads', label: 'Downloads', category: 'Folders', icon: <Folder className="h-5 w-5" /> },
{
id: "terminal",
label: "Terminal",
category: "Applications",
icon: <Terminal className="h-5 w-5" />,
},
{
id: "safari",
label: "Safari",
category: "Applications",
icon: <Chrome className="h-5 w-5" />,
},
{
id: "mail",
label: "Mail",
category: "Applications",
icon: <Mail className="h-5 w-5" />,
},
{
id: "calendar",
label: "Calendar",
category: "Applications",
icon: <Calendar className="h-5 w-5" />,
},
{
id: "music",
label: "Music",
category: "Applications",
icon: <Music className="h-5 w-5" />,
},
{
id: "settings",
label: "System Preferences",
category: "Applications",
icon: <Settings className="h-5 w-5" />,
},
{
id: "documents",
label: "Documents",
category: "Folders",
icon: <Folder className="h-5 w-5" />,
},
{
id: "downloads",
label: "Downloads",
category: "Folders",
icon: <Folder className="h-5 w-5" />,
},
]
export function AppLauncher() {
@@ -147,7 +201,7 @@ export function AppLauncher() {
// ⌘+Space to open (like macOS)
useKeyboardShortcuts([
{ key: ' ', meta: true, action: () => setIsOpen(true) }
{ key: " ", meta: true, action: () => setIsOpen(true) },
])
return (
@@ -157,7 +211,7 @@ export function AppLauncher() {
items={apps}
placeholder="Search applications..."
onSelect={(item) => {
console.log('Launch:', item.label)
console.log("Launch:", item.label)
setIsOpen(false)
}}
/>
@@ -169,22 +223,51 @@ export function AppLauncher() {
```tsx
import { Spotlight, SpotlightItem } from "@hanzo/ui/desktop"
import {
Save,
Copy,
Scissors,
Clipboard,
Undo,
Redo
} from "lucide-react"
import { Clipboard, Copy, Redo, Save, Scissors, Undo } from "lucide-react"
const commands: SpotlightItem[] = [
{ id: 'save', label: 'Save', category: 'File', icon: <Save />, shortcut: '⌘S' },
{ id: 'copy', label: 'Copy', category: 'Edit', icon: <Copy />, shortcut: '⌘C' },
{ id: 'cut', label: 'Cut', category: 'Edit', icon: <Scissors />, shortcut: '⌘X' },
{ id: 'paste', label: 'Paste', category: 'Edit', icon: <Clipboard />, shortcut: '⌘V' },
{ id: 'undo', label: 'Undo', category: 'Edit', icon: <Undo />, shortcut: '⌘Z' },
{ id: 'redo', label: 'Redo', category: 'Edit', icon: <Redo />, shortcut: '⌘⇧Z' },
{
id: "save",
label: "Save",
category: "File",
icon: <Save />,
shortcut: "⌘S",
},
{
id: "copy",
label: "Copy",
category: "Edit",
icon: <Copy />,
shortcut: "⌘C",
},
{
id: "cut",
label: "Cut",
category: "Edit",
icon: <Scissors />,
shortcut: "⌘X",
},
{
id: "paste",
label: "Paste",
category: "Edit",
icon: <Clipboard />,
shortcut: "⌘V",
},
{
id: "undo",
label: "Undo",
category: "Edit",
icon: <Undo />,
shortcut: "⌘Z",
},
{
id: "redo",
label: "Redo",
category: "Edit",
icon: <Redo />,
shortcut: "⌘⇧Z",
},
]
export function CommandPalette() {
@@ -197,7 +280,7 @@ export function CommandPalette() {
items={commands}
placeholder="Type a command..."
onSelect={(item) => {
console.log('Execute:', item.id)
console.log("Execute:", item.id)
setIsOpen(false)
}}
/>
@@ -207,9 +290,9 @@ export function CommandPalette() {
## Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| `↑` / `↓` | Navigate between items |
| `Enter` | Select highlighted item |
| `Escape` | Close spotlight |
| Shortcut | Action |
| --------------- | -------------------------------- |
| `↑` / `↓` | Navigate between items |
| `Enter` | Select highlighted item |
| `Escape` | Close spotlight |
| `⌘K` / `⌘Space` | Open spotlight (when configured) |
+19 -19
View File
@@ -75,17 +75,17 @@ export function WindowDemo() {
## Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `title` | `string` | - | Window title displayed in the title bar |
| `onClose` | `() => void` | - | Callback when the close button is clicked |
| `initialPosition` | `{ x: number, y: number }` | `{ x: 100, y: 100 }` | Initial window position |
| `initialSize` | `{ width: number, height: number }` | `{ width: 800, height: 600 }` | Initial window dimensions |
| `minWidth` | `number` | `200` | Minimum window width |
| `minHeight` | `number` | `150` | Minimum window height |
| `windowType` | `'default' \| 'dark' \| 'light' \| 'transparent'` | `'default'` | Window style variant |
| `className` | `string` | - | Additional CSS classes for the window |
| `children` | `ReactNode` | - | Window content |
| Prop | Type | Default | Description |
| ----------------- | ------------------------------------------------- | ----------------------------- | ----------------------------------------- |
| `title` | `string` | - | Window title displayed in the title bar |
| `onClose` | `() => void` | - | Callback when the close button is clicked |
| `initialPosition` | `{ x: number, y: number }` | `{ x: 100, y: 100 }` | Initial window position |
| `initialSize` | `{ width: number, height: number }` | `{ width: 800, height: 600 }` | Initial window dimensions |
| `minWidth` | `number` | `200` | Minimum window width |
| `minHeight` | `number` | `150` | Minimum window height |
| `windowType` | `'default' \| 'dark' \| 'light' \| 'transparent'` | `'default'` | Window style variant |
| `className` | `string` | - | Additional CSS classes for the window |
| `children` | `ReactNode` | - | Window content |
## Variants
@@ -128,7 +128,7 @@ export function WindowDemo() {
```tsx
import { Window } from "@hanzo/ui/desktop"
import { Settings, User, Bell } from "lucide-react"
import { Bell, Settings, User } from "lucide-react"
export function SettingsWindow() {
const [isOpen, setIsOpen] = useState(true)
@@ -164,35 +164,35 @@ export function SettingsWindow() {
### Multiple Windows
```tsx
import { Window, useWindowManager } from "@hanzo/ui/desktop"
import { useWindowManager, Window } from "@hanzo/ui/desktop"
export function MultiWindowDemo() {
const windows = useWindowManager()
return (
<div className="relative h-screen">
{windows.isOpen('Settings') && (
{windows.isOpen("Settings") && (
<Window
title="Settings"
onClose={() => windows.close('Settings')}
onClose={() => windows.close("Settings")}
initialPosition={{ x: 100, y: 100 }}
>
<div className="p-4">Settings content</div>
</Window>
)}
{windows.isOpen('Editor') && (
{windows.isOpen("Editor") && (
<Window
title="Editor"
onClose={() => windows.close('Editor')}
onClose={() => windows.close("Editor")}
initialPosition={{ x: 200, y: 150 }}
>
<div className="p-4">Editor content</div>
</Window>
)}
<button onClick={() => windows.open('Settings')}>Open Settings</button>
<button onClick={() => windows.open('Editor')}>Open Editor</button>
<button onClick={() => windows.open("Settings")}>Open Settings</button>
<button onClick={() => windows.open("Editor")}>Open Editor</button>
</div>
)
}
+20 -22
View File
@@ -8,19 +8,19 @@ The Desktop namespace provides components and hooks for building macOS-style des
## Components
| Component | Description |
|-----------|-------------|
| [Window](/docs/components/desktop-window) | Draggable, resizable window with minimize, maximize, and close controls |
| [Spotlight](/docs/components/desktop-spotlight) | macOS Spotlight-style search and command palette |
| Component | Description |
| ----------------------------------------------- | ----------------------------------------------------------------------- |
| [Window](/docs/components/desktop-window) | Draggable, resizable window with minimize, maximize, and close controls |
| [Spotlight](/docs/components/desktop-spotlight) | macOS Spotlight-style search and command palette |
## Hooks
| Hook | Description |
|------|-------------|
| `useWindowManager` | Manage multiple windows with open/close/toggle state |
| `useDesktopSettings` | Persist desktop settings like theme, dock position |
| `useOverlayManager` | Manage overlay states (spotlight, about dialog, etc.) |
| `useKeyboardShortcuts` | Register keyboard shortcuts (⌘+Space, ⌘+Q, etc.) |
| Hook | Description |
| ---------------------- | ----------------------------------------------------- |
| `useWindowManager` | Manage multiple windows with open/close/toggle state |
| `useDesktopSettings` | Persist desktop settings like theme, dock position |
| `useOverlayManager` | Manage overlay states (spotlight, about dialog, etc.) |
| `useKeyboardShortcuts` | Register keyboard shortcuts (⌘+Space, ⌘+Q, etc.) |
See [Desktop Hooks](/docs/components/desktop-hooks) for detailed hook documentation.
@@ -51,12 +51,12 @@ Then import from the desktop namespace:
```tsx
import {
Window,
Spotlight,
useWindowManager,
useDesktopSettings,
useKeyboardShortcuts,
useOverlayManager,
useKeyboardShortcuts
useWindowManager,
Window,
} from "@hanzo/ui/desktop"
```
@@ -68,10 +68,10 @@ import {
```tsx
import {
Window,
Spotlight,
useKeyboardShortcuts,
useWindowManager,
useKeyboardShortcuts
Window,
} from "@hanzo/ui/desktop"
import { Dock, DockItem } from "@hanzo/ui/dock"
@@ -80,16 +80,16 @@ export function MyDesktop() {
const [showSpotlight, setShowSpotlight] = useState(false)
useKeyboardShortcuts([
{ key: ' ', meta: true, action: () => setShowSpotlight(true) },
{ key: " ", meta: true, action: () => setShowSpotlight(true) },
])
return (
<div className="h-screen relative">
{/* Windows */}
{windows.isOpen('Settings') && (
{windows.isOpen("Settings") && (
<Window
title="Settings"
onClose={() => windows.closeWindow('Settings')}
onClose={() => windows.closeWindow("Settings")}
>
<div className="p-4">Settings content</div>
</Window>
@@ -99,9 +99,7 @@ export function MyDesktop() {
<Spotlight
isOpen={showSpotlight}
onClose={() => setShowSpotlight(false)}
items={[
{ id: 'settings', title: 'Settings', category: 'Apps' },
]}
items={[{ id: "settings", title: "Settings", category: "Apps" }]}
onSelect={(item) => {
windows.openWindow(item.title)
setShowSpotlight(false)
@@ -112,7 +110,7 @@ export function MyDesktop() {
<Dock position="bottom">
<DockItem
tooltip="Settings"
onClick={() => windows.toggleWindow('Settings')}
onClick={() => windows.toggleWindow("Settings")}
/>
</Dock>
</div>
+45
View File
@@ -0,0 +1,45 @@
---
title: Direction
description: Provider and hook for RTL/LTR text direction support.
---
## Overview
The Direction component provides RTL (right-to-left) and LTR (left-to-right) text direction support through a React context provider and hook. Useful for internationalization and bidirectional text layouts.
## Usage
```tsx
import { DirectionProvider, useDirection } from "@hanzo/ui"
export function App() {
return (
<DirectionProvider direction="rtl">
<MyComponent />
</DirectionProvider>
)
}
function MyComponent() {
const direction = useDirection()
return <div>Current direction: {direction}</div>
}
```
## Props
### DirectionProvider
| Prop | Type | Default | Description |
| --------- | -------------- | ------- | --------------- |
| dir | "ltr" \| "rtl" | - | Text direction |
| direction | "ltr" \| "rtl" | - | Alias for `dir` |
| children | ReactNode | - | Child elements |
### useDirection
Returns the current direction from the nearest `DirectionProvider`.
```tsx
const direction = useDirection() // "ltr" | "rtl"
```
@@ -0,0 +1,56 @@
---
title: Stock Market
description: Display stock market overview with major indices and equities.
component: true
---
<ComponentPreview name="market-overview" description="Stock market overview" />
## Installation
<Tabs defaultValue="cli">
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx @hanzo/ui@latest add market-overview
```
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Install the following dependencies:</Step>
```bash
npm install @hanzo/ui
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="market-overview" />
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</Tabs>
## Usage
```tsx
import { MarketOverview } from "@hanzo/ui/finance"
export default function Demo() {
return <MarketOverview tabs={["indices"]} colorTheme="dark" />
}
```
@@ -0,0 +1,119 @@
---
title: React Native
description: Using @hanzo/ui components in React Native and Expo applications.
---
# React Native Guide (Beta)
@hanzo/ui provides React Native components built on top of React Native's core primitives and NativeWind for styling. This integration is currently in beta.
## Installation
<Steps>
### Install the package
```bash
npm install @hanzo/ui
# or
pnpm add @hanzo/ui
```
### Install NativeWind
```bash
npm install nativewind
npm install -D tailwindcss
```
### Configure Tailwind
```js title="tailwind.config.js"
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./App.{js,jsx,ts,tsx}",
"./src/**/*.{js,jsx,ts,tsx}",
"./node_modules/@hanzo/ui/**/*.{js,ts,jsx,tsx}",
],
presets: [require("nativewind/preset")],
theme: {
extend: {},
},
plugins: [],
}
```
</Steps>
## Usage
```tsx
import { useState } from "react"
import { Button, Card, Input } from "@hanzo/ui/native"
export default function App() {
const [count, setCount] = useState(0)
return (
<Card>
<Button onPress={() => setCount((c) => c + 1)}>
Clicked {count} times
</Button>
<Input value={String(count)} />
</Card>
)
}
```
## Expo Integration
```tsx
// app/(tabs)/index.tsx
import { Button, Card } from "@hanzo/ui/native"
export default function HomeScreen() {
return (
<Card>
<Button>Click me</Button>
</Card>
)
}
```
## Component Coverage
**Available:**
- Button, Card, Input, Label, Badge
- Checkbox, Switch, Separator
- Tabs, Avatar
**In Progress:**
- Dialog and Sheet (modal-based)
- Select and Popover
- Navigation components
**Not Available:**
- 3D components
- Browser-specific components (Safari mockup, etc.)
- Chart components
## Platform Differences
Some components behave differently on native platforms:
- **Dialog** uses React Native's `Modal` component
- **Sheet** uses bottom sheet gestures via `react-native-gesture-handler`
- **Animations** use `react-native-reanimated` instead of Framer Motion
## Status
This integration is in **beta**. Component coverage is limited compared to the web version. Please report issues on [GitHub](https://github.com/hanzoai/ui/issues).
## Next Steps
- [View all components](/docs/components)
- [React Guide](/docs/frameworks/react) (stable)
+100
View File
@@ -0,0 +1,100 @@
---
title: Svelte
description: Using @hanzo/ui components in Svelte and SvelteKit applications.
---
# Svelte Guide (Beta)
@hanzo/ui provides Svelte 5 components with full TypeScript support. This integration is currently in beta.
## Installation
<Steps>
### Install the package
```bash
npm install @hanzo/ui
# or
pnpm add @hanzo/ui
```
### Install Svelte and dependencies
```bash
npm install svelte
npm install -D tailwindcss postcss autoprefixer
```
### Configure Tailwind
```js title="tailwind.config.js"
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{html,js,svelte,ts}",
"./node_modules/@hanzo/ui/**/*.{js,ts,svelte}",
],
theme: {
extend: {},
},
plugins: [],
}
```
</Steps>
## Usage
```svelte
<script lang="ts">
import { Button, Card, Input } from '@hanzo/ui/svelte'
let count = $state(0)
</script>
<Card>
<Button onclick={() => count++}>
Clicked {count} times
</Button>
<Input bind:value={count} type="number" />
</Card>
```
## SvelteKit Integration
```svelte
<!-- src/routes/+page.svelte -->
<script lang="ts">
import { Button, Card } from '@hanzo/ui/svelte'
</script>
<Card>
<h1>Welcome to SvelteKit</h1>
<Button>Click me</Button>
</Card>
```
## Component Coverage
**Available:**
- Button, Card, Input, Label, Badge
- Checkbox, Dialog, Select, Separator
- Tabs, Toggle, Tooltip
**In Progress:**
- Form components
- Data Table
- Advanced animations
## Status
This integration is in **beta**. Some components may have limited functionality compared to the React version. Please report issues on [GitHub](https://github.com/hanzoai/ui/issues).
## Next Steps
- [View all components](/docs/components)
- [React Guide](/docs/frameworks/react) (stable)
- [Vue Guide](/docs/frameworks/vue) (stable)
+8 -8
View File
@@ -28,6 +28,10 @@
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@faker-js/faker": "^10.0.0",
"@hanzo/docs-core": "16.2.6",
"@hanzo/docs-docgen": "3.0.4",
"@hanzo/docs-mdx": "14.3.0",
"@hanzo/docs-ui": "16.2.6",
"@hanzo/logo": "^1.0.3",
"@hanzo/ui": "workspace:^",
"@hookform/resolvers": "^5.2.2",
@@ -41,6 +45,7 @@
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-context-menu": "^2.2.16",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-direction": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-icons": "^1.3.2",
@@ -82,11 +87,6 @@
"date-fns": "^4.1.0",
"embla-carousel-autoplay": "8.6.0",
"embla-carousel-react": "8.6.0",
"framer-motion": "^12.23.22",
"@hanzo/docs-core": "16.2.6",
"@hanzo/docs-docgen": "3.0.4",
"@hanzo/docs-mdx": "14.3.0",
"@hanzo/docs-ui": "16.2.6",
"fumadocs-core": "^16.4.8",
"fumadocs-mdx": "^14.2.6",
"geist": "^1.5.1",
@@ -94,9 +94,9 @@
"jotai": "^2.15.0",
"js-yaml": "^4.1.0",
"lodash": "^4.17.21",
"lucide-react": "0.544.0",
"lucide-react": "^0.544.0",
"monaco-editor": "^0.53.0",
"motion": "^12.23.22",
"motion": "^12.34.0",
"next-themes": "0.4.6",
"qrcode.react": "^4.2.0",
"react": "19.2.0",
@@ -144,7 +144,7 @@
"eslint": "^9.39.1",
"eslint-config-next": "16.0.1",
"happy-dom": "^20.0.10",
"next": "16.0.1",
"next": "^16.1.6",
"npm-run-all": "^4.1.5",
"playwright": "^1.55.1",
"png-to-ico": "^3.0.1",
+14 -14
View File
@@ -19,7 +19,12 @@
"destructive-foreground": "gray-50",
"border": "gray-200",
"input": "gray-200",
"ring": "gray-950"
"ring": "gray-950",
"chart-1": "12 76% 61%",
"chart-2": "173 58% 39%",
"chart-3": "197 37% 24%",
"chart-4": "43 74% 66%",
"chart-5": "27 87% 67%"
},
"dark": {
"background": "gray-950",
@@ -40,7 +45,12 @@
"destructive-foreground": "gray-50",
"border": "gray-800",
"input": "gray-800",
"ring": "gray-300"
"ring": "gray-300",
"chart-1": "220 70% 50%",
"chart-2": "160 60% 45%",
"chart-3": "30 80% 55%",
"chart-4": "280 65% 60%",
"chart-5": "340 75% 55%"
}
},
"cssVars": {
@@ -63,12 +73,7 @@
"destructive-foreground": "210 20% 98%",
"border": "220 13% 91%",
"input": "220 13% 91%",
"ring": "224 71.4% 4.1%",
"chart-1": "12 76% 61%",
"chart-2": "173 58% 39%",
"chart-3": "197 37% 24%",
"chart-4": "43 74% 66%",
"chart-5": "27 87% 67%"
"ring": "224 71.4% 4.1%"
},
"dark": {
"background": "224 71.4% 4.1%",
@@ -89,12 +94,7 @@
"destructive-foreground": "210 20% 98%",
"border": "215 27.9% 16.9%",
"input": "215 27.9% 16.9%",
"ring": "216 12.2% 83.9%",
"chart-1": "220 70% 50%",
"chart-2": "160 60% 45%",
"chart-3": "30 80% 55%",
"chart-4": "280 65% 60%",
"chart-5": "340 75% 55%"
"ring": "216 12.2% 83.9%"
}
},
"inlineColorsTemplate": "@tailwind base;\n @tailwind components;\n @tailwind utilities;\n ",
File diff suppressed because it is too large Load Diff
+14 -14
View File
@@ -19,7 +19,12 @@
"destructive-foreground": "neutral-50",
"border": "neutral-200",
"input": "neutral-200",
"ring": "neutral-950"
"ring": "neutral-950",
"chart-1": "12 76% 61%",
"chart-2": "173 58% 39%",
"chart-3": "197 37% 24%",
"chart-4": "43 74% 66%",
"chart-5": "27 87% 67%"
},
"dark": {
"background": "neutral-950",
@@ -40,7 +45,12 @@
"destructive-foreground": "neutral-50",
"border": "neutral-800",
"input": "neutral-800",
"ring": "neutral-300"
"ring": "neutral-300",
"chart-1": "220 70% 50%",
"chart-2": "160 60% 45%",
"chart-3": "30 80% 55%",
"chart-4": "280 65% 60%",
"chart-5": "340 75% 55%"
}
},
"cssVars": {
@@ -63,12 +73,7 @@
"destructive-foreground": "0 0% 98%",
"border": "0 0% 89.8%",
"input": "0 0% 89.8%",
"ring": "0 0% 3.9%",
"chart-1": "12 76% 61%",
"chart-2": "173 58% 39%",
"chart-3": "197 37% 24%",
"chart-4": "43 74% 66%",
"chart-5": "27 87% 67%"
"ring": "0 0% 3.9%"
},
"dark": {
"background": "0 0% 3.9%",
@@ -89,12 +94,7 @@
"destructive-foreground": "0 0% 98%",
"border": "0 0% 14.9%",
"input": "0 0% 14.9%",
"ring": "0 0% 83.1%",
"chart-1": "220 70% 50%",
"chart-2": "160 60% 45%",
"chart-3": "30 80% 55%",
"chart-4": "280 65% 60%",
"chart-5": "340 75% 55%"
"ring": "0 0% 83.1%"
}
},
"inlineColorsTemplate": "@tailwind base;\n @tailwind components;\n @tailwind utilities;\n ",
+13 -8
View File
@@ -19,7 +19,12 @@
"destructive-foreground": "slate-50",
"border": "slate-200",
"input": "slate-200",
"ring": "slate-950"
"ring": "slate-950",
"chart-1": "12 76% 61%",
"chart-2": "173 58% 39%",
"chart-3": "197 37% 24%",
"chart-4": "43 74% 66%",
"chart-5": "27 87% 67%"
},
"dark": {
"background": "slate-950",
@@ -40,7 +45,12 @@
"destructive-foreground": "slate-50",
"border": "slate-800",
"input": "slate-800",
"ring": "slate-300"
"ring": "slate-300",
"chart-1": "220 70% 50%",
"chart-2": "160 60% 45%",
"chart-3": "30 80% 55%",
"chart-4": "280 65% 60%",
"chart-5": "340 75% 55%"
}
},
"cssVars": {
@@ -63,12 +73,7 @@
"destructive-foreground": "210 40% 98%",
"border": "214.3 31.8% 91.4%",
"input": "214.3 31.8% 91.4%",
"ring": "222.2 84% 4.9%",
"chart-1": "12 76% 61%",
"chart-2": "173 58% 39%",
"chart-3": "197 37% 24%",
"chart-4": "43 74% 66%",
"chart-5": "27 87% 67%"
"ring": "222.2 84% 4.9%"
},
"dark": {
"background": "222.2 84% 4.9%",
+14 -14
View File
@@ -19,7 +19,12 @@
"destructive-foreground": "stone-50",
"border": "stone-200",
"input": "stone-200",
"ring": "stone-950"
"ring": "stone-950",
"chart-1": "12 76% 61%",
"chart-2": "173 58% 39%",
"chart-3": "197 37% 24%",
"chart-4": "43 74% 66%",
"chart-5": "27 87% 67%"
},
"dark": {
"background": "stone-950",
@@ -40,7 +45,12 @@
"destructive-foreground": "stone-50",
"border": "stone-800",
"input": "stone-800",
"ring": "stone-300"
"ring": "stone-300",
"chart-1": "220 70% 50%",
"chart-2": "160 60% 45%",
"chart-3": "30 80% 55%",
"chart-4": "280 65% 60%",
"chart-5": "340 75% 55%"
}
},
"cssVars": {
@@ -63,12 +73,7 @@
"destructive-foreground": "60 9.1% 97.8%",
"border": "20 5.9% 90%",
"input": "20 5.9% 90%",
"ring": "20 14.3% 4.1%",
"chart-1": "12 76% 61%",
"chart-2": "173 58% 39%",
"chart-3": "197 37% 24%",
"chart-4": "43 74% 66%",
"chart-5": "27 87% 67%"
"ring": "20 14.3% 4.1%"
},
"dark": {
"background": "20 14.3% 4.1%",
@@ -89,12 +94,7 @@
"destructive-foreground": "60 9.1% 97.8%",
"border": "12 6.5% 15.1%",
"input": "12 6.5% 15.1%",
"ring": "24 5.7% 82.9%",
"chart-1": "220 70% 50%",
"chart-2": "160 60% 45%",
"chart-3": "30 80% 55%",
"chart-4": "280 65% 60%",
"chart-5": "340 75% 55%"
"ring": "24 5.7% 82.9%"
}
},
"inlineColorsTemplate": "@tailwind base;\n @tailwind components;\n @tailwind utilities;\n ",
+14 -14
View File
@@ -19,7 +19,12 @@
"destructive-foreground": "zinc-50",
"border": "zinc-200",
"input": "zinc-200",
"ring": "zinc-950"
"ring": "zinc-950",
"chart-1": "12 76% 61%",
"chart-2": "173 58% 39%",
"chart-3": "197 37% 24%",
"chart-4": "43 74% 66%",
"chart-5": "27 87% 67%"
},
"dark": {
"background": "zinc-950",
@@ -40,7 +45,12 @@
"destructive-foreground": "zinc-50",
"border": "zinc-800",
"input": "zinc-800",
"ring": "zinc-300"
"ring": "zinc-300",
"chart-1": "220 70% 50%",
"chart-2": "160 60% 45%",
"chart-3": "30 80% 55%",
"chart-4": "280 65% 60%",
"chart-5": "340 75% 55%"
}
},
"cssVars": {
@@ -63,12 +73,7 @@
"destructive-foreground": "0 0% 98%",
"border": "240 5.9% 90%",
"input": "240 5.9% 90%",
"ring": "240 10% 3.9%",
"chart-1": "12 76% 61%",
"chart-2": "173 58% 39%",
"chart-3": "197 37% 24%",
"chart-4": "43 74% 66%",
"chart-5": "27 87% 67%"
"ring": "240 10% 3.9%"
},
"dark": {
"background": "240 10% 3.9%",
@@ -89,12 +94,7 @@
"destructive-foreground": "0 0% 98%",
"border": "240 3.7% 15.9%",
"input": "240 3.7% 15.9%",
"ring": "240 4.9% 83.9%",
"chart-1": "220 70% 50%",
"chart-2": "160 60% 45%",
"chart-3": "30 80% 55%",
"chart-4": "280 65% 60%",
"chart-5": "340 75% 55%"
"ring": "240 4.9% 83.9%"
}
},
"inlineColorsTemplate": "@tailwind base;\n @tailwind components;\n @tailwind utilities;\n ",
+10
View File
@@ -184,6 +184,16 @@
],
"type": "components:ui"
},
{
"name": "direction",
"dependencies": [
"@radix-ui/react-direction"
],
"files": [
"ui/direction.tsx"
],
"type": "components:ui"
},
{
"name": "dialog",
"dependencies": [
@@ -7,7 +7,7 @@
"files": [
{
"name": "advanced-chart.tsx",
"content": "export * from \"../ui/advanced-chart\"\n"
"content": "export { default } from \"../ui/advanced-chart\"\nexport * from \"../ui/advanced-chart\"\n"
}
],
"type": "components:finance",
@@ -4,7 +4,7 @@
"files": [
{
"name": "advanced-navigation-bar.tsx",
"content": "export default function Component() {\n return (\n <div className=\"flex items-center justify-center p-8\">\n <p className=\"text-muted-foreground\">Component coming soon</p>\n </div>\n )\n}\n"
"content": "export * from \"../advanced-navigation-bar\"\n"
}
],
"type": "components:ui"
@@ -4,7 +4,7 @@
"files": [
{
"name": "ai-model-selector-navigation-bar.tsx",
"content": "export default function Component() {\n return (\n <div className=\"flex items-center justify-center p-8\">\n <p className=\"text-muted-foreground\">Component coming soon</p>\n </div>\n )\n}\n"
"content": "export * from \"../ai-model-selector-navigation-bar\"\n"
}
],
"type": "components:ui"
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
"files": [
{
"name": "app-switcher-navigation-bar.tsx",
"content": "export default function Component() {\n return (\n <div className=\"flex items-center justify-center p-8\">\n <p className=\"text-muted-foreground\">Component coming soon</p>\n </div>\n )\n}\n"
"content": "export * from \"../app-switcher-navigation-bar\"\n"
}
],
"type": "components:ui"
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
"files": [
{
"name": "breadcrumb-and-filters-navigation-bar.tsx",
"content": "export default function Component() {\n return (\n <div className=\"flex items-center justify-center p-8\">\n <p className=\"text-muted-foreground\">Component coming soon</p>\n </div>\n )\n}\n"
"content": "export * from \"../breadcrumb-and-filters-navigation-bar\"\n"
}
],
"type": "components:ui"
@@ -4,7 +4,7 @@
"files": [
{
"name": "breadcrumb-navigation-bar.tsx",
"content": "export default function Component() {\n return (\n <div className=\"flex items-center justify-center p-8\">\n <p className=\"text-muted-foreground\">Component coming soon</p>\n </div>\n )\n}\n"
"content": "export * from \"../breadcrumb-navigation-bar\"\n"
}
],
"type": "components:ui"
@@ -6,7 +6,7 @@
"files": [
{
"name": "button-icon.tsx",
"content": "import { ChevronRight } from \"lucide-react\"\n\nimport { Button } from \"@/registry/default/ui/button\"\n\nexport default function ButtonIcon() {\n return (\n <Button variant=\"outline\" size=\"icon\">\n <ChevronRight className=\"h-4 w-4\" />\n </Button>\n )\n}\n"
"content": "import { ChevronRight } from \"lucide-react\"\n\nimport { Button } from \"@/registry/default/ui/button\"\n\nexport default function ButtonIcon() {\n return (\n <Button variant=\"outline\" size=\"icon\">\n <ChevronRight />\n </Button>\n )\n}\n"
}
],
"type": "components:example"
@@ -6,7 +6,7 @@
"files": [
{
"name": "button-loading.tsx",
"content": "import { Loader2 } from \"lucide-react\"\n\nimport { Button } from \"@/registry/default/ui/button\"\n\nexport default function ButtonLoading() {\n return (\n <Button disabled>\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n Please wait\n </Button>\n )\n}\n"
"content": "import { Loader2 } from \"lucide-react\"\n\nimport { Button } from \"@/registry/default/ui/button\"\n\nexport default function ButtonLoading() {\n return (\n <Button disabled>\n <Loader2 className=\"animate-spin\" />\n Please wait\n </Button>\n )\n}\n"
}
],
"type": "components:example"
@@ -6,7 +6,7 @@
"files": [
{
"name": "button-with-icon.tsx",
"content": "import { Mail } from \"lucide-react\"\n\nimport { Button } from \"@/registry/default/ui/button\"\n\nexport default function ButtonWithIcon() {\n return (\n <Button>\n <Mail className=\"mr-2 h-4 w-4\" /> Login with Email\n </Button>\n )\n}\n"
"content": "import { Mail } from \"lucide-react\"\n\nimport { Button } from \"@/registry/default/ui/button\"\n\nexport default function ButtonWithIcon() {\n return (\n <Button>\n <Mail /> Login with Email\n </Button>\n )\n}\n"
}
],
"type": "components:example"
@@ -6,7 +6,7 @@
"files": [
{
"name": "button.tsx",
"content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nexport type ButtonProps = React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean\n }\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n )\n }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n"
"content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n xs: \"h-7 rounded-md gap-1 px-2 text-xs has-[>svg]:px-1.5\",\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-xs\": \"size-7\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nexport type ButtonProps = React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean\n }\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n )\n }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n"
}
],
"type": "components:ui"
@@ -8,7 +8,7 @@
"files": [
{
"name": "calendar-form.tsx",
"content": "\"use client\"\n\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { format } from \"date-fns\"\nimport { CalendarIcon } from \"lucide-react\"\nimport { useForm } from \"react-hook-form\"\nimport { z } from \"zod\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport { Calendar } from \"@/registry/default/ui/calendar\"\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/registry/default/ui/form\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/registry/default/ui/popover\"\nimport { toast } from \"@/registry/default/ui/use-toast\"\n\nconst FormSchema = z.object({\n dob: z.date({\n message: \"A date of birth is required.\",\n }),\n})\n\nexport default function CalendarForm() {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n })\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast({\n title: \"You submitted the following values:\",\n description: (\n <pre className=\"mt-2 w-[340px] rounded-md bg-slate-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n })\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-8\">\n <FormField\n control={form.control}\n name=\"dob\"\n render={({ field }) => (\n <FormItem className=\"flex flex-col\">\n <FormLabel>Date of birth</FormLabel>\n <Popover>\n <PopoverTrigger asChild>\n <FormControl>\n <Button\n variant={\"outline\"}\n className={cn(\n \"w-[240px] pl-3 text-left font-normal\",\n !field.value && \"text-muted-foreground\"\n )}\n >\n {field.value ? (\n format(field.value, \"PPP\")\n ) : (\n <span>Pick a date</span>\n )}\n <CalendarIcon className=\"ml-auto h-4 w-4 opacity-50\" />\n </Button>\n </FormControl>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\" align=\"start\">\n <Calendar\n mode=\"single\"\n selected={field.value}\n onSelect={field.onChange}\n disabled={(date) =>\n date > new Date() || date < new Date(\"1900-01-01\")\n }\n initialFocus\n />\n </PopoverContent>\n </Popover>\n <FormDescription>\n Your date of birth is used to calculate your age.\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n )\n}\n"
"content": "\"use client\"\n\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { format } from \"date-fns\"\nimport { CalendarIcon } from \"lucide-react\"\nimport { useForm } from \"react-hook-form\"\nimport { z } from \"zod\"\n\nimport { cn } from \"@/lib/utils\"\nimport { toast } from \"@/registry/default/hooks/use-toast\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport { Calendar } from \"@/registry/default/ui/calendar\"\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/registry/default/ui/form\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/registry/default/ui/popover\"\n\nconst FormSchema = z.object({\n dob: z.date({\n message: \"A date of birth is required.\",\n }),\n})\n\nexport default function CalendarForm() {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n })\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast({\n title: \"You submitted the following values:\",\n description: (\n <pre className=\"mt-2 w-[340px] rounded-md bg-slate-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n })\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-8\">\n <FormField\n control={form.control}\n name=\"dob\"\n render={({ field }) => (\n <FormItem className=\"flex flex-col\">\n <FormLabel>Date of birth</FormLabel>\n <Popover>\n <PopoverTrigger asChild>\n <FormControl>\n <Button\n variant={\"outline\"}\n className={cn(\n \"w-[240px] pl-3 text-left font-normal\",\n !field.value && \"text-muted-foreground\"\n )}\n >\n {field.value ? (\n format(field.value, \"PPP\")\n ) : (\n <span>Pick a date</span>\n )}\n <CalendarIcon className=\"ml-auto h-4 w-4 opacity-50\" />\n </Button>\n </FormControl>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\" align=\"start\">\n <Calendar\n mode=\"single\"\n selected={field.value}\n onSelect={field.onChange}\n disabled={(date) =>\n date > new Date() || date < new Date(\"1900-01-01\")\n }\n initialFocus\n />\n </PopoverContent>\n </Popover>\n <FormDescription>\n Your date of birth is used to calculate your age.\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n )\n}\n"
}
],
"type": "components:example"
@@ -8,7 +8,7 @@
"files": [
{
"name": "card-demo.tsx",
"content": "import { BellRing, Check } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/registry/default/ui/card\"\nimport { Switch } from \"@/registry/default/ui/switch\"\n\nconst notifications = [\n {\n title: \"Your call has been confirmed.\",\n description: \"1 hour ago\",\n },\n {\n title: \"You have a new message!\",\n description: \"1 hour ago\",\n },\n {\n title: \"Your subscription is expiring soon!\",\n description: \"2 hours ago\",\n },\n]\n\ntype CardProps = React.ComponentProps<typeof Card>\n\nexport default function CardDemo({ className, ...props }: CardProps) {\n return (\n <Card className={cn(\"w-[380px]\", className)} {...props}>\n <CardHeader>\n <CardTitle>Notifications</CardTitle>\n <CardDescription>You have 3 unread messages.</CardDescription>\n </CardHeader>\n <CardContent className=\"grid gap-4\">\n <div className=\" flex items-center space-x-4 rounded-md border p-4\">\n <BellRing />\n <div className=\"flex-1 space-y-1\">\n <p className=\"text-sm font-medium leading-none\">\n Push Notifications\n </p>\n <p className=\"text-sm text-muted-foreground\">\n Send notifications to device.\n </p>\n </div>\n <Switch />\n </div>\n <div>\n {notifications.map((notification, index) => (\n <div\n key={index}\n className=\"mb-4 grid grid-cols-[25px_1fr] items-start pb-4 last:mb-0 last:pb-0\"\n >\n <span className=\"flex h-2 w-2 translate-y-1 rounded-full bg-sky-500\" />\n <div className=\"space-y-1\">\n <p className=\"text-sm font-medium leading-none\">\n {notification.title}\n </p>\n <p className=\"text-sm text-muted-foreground\">\n {notification.description}\n </p>\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n <CardFooter>\n <Button className=\"w-full\">\n <Check className=\"mr-2 h-4 w-4\" /> Mark all as read\n </Button>\n </CardFooter>\n </Card>\n )\n}\n"
"content": "import { BellRing, Check } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/registry/default/ui/card\"\nimport { Switch } from \"@/registry/default/ui/switch\"\n\nconst notifications = [\n {\n title: \"Your call has been confirmed.\",\n description: \"1 hour ago\",\n },\n {\n title: \"You have a new message!\",\n description: \"1 hour ago\",\n },\n {\n title: \"Your subscription is expiring soon!\",\n description: \"2 hours ago\",\n },\n]\n\ntype CardProps = React.ComponentProps<typeof Card>\n\nexport default function CardDemo({ className, ...props }: CardProps) {\n return (\n <Card className={cn(\"w-[380px]\", className)} {...props}>\n <CardHeader>\n <CardTitle>Notifications</CardTitle>\n <CardDescription>You have 3 unread messages.</CardDescription>\n </CardHeader>\n <CardContent className=\"grid gap-4\">\n <div className=\" flex items-center space-x-4 rounded-md border p-4\">\n <BellRing />\n <div className=\"flex-1 space-y-1\">\n <p className=\"text-sm font-medium leading-none\">\n Push Notifications\n </p>\n <p className=\"text-sm text-muted-foreground\">\n Send notifications to device.\n </p>\n </div>\n <Switch />\n </div>\n <div>\n {notifications.map((notification, index) => (\n <div\n key={index}\n className=\"mb-4 grid grid-cols-[25px_1fr] items-start pb-4 last:mb-0 last:pb-0\"\n >\n <span className=\"flex h-2 w-2 translate-y-1 rounded-full bg-sky-500\" />\n <div className=\"space-y-1\">\n <p className=\"text-sm font-medium leading-none\">\n {notification.title}\n </p>\n <p className=\"text-sm text-muted-foreground\">\n {notification.description}\n </p>\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n <CardFooter>\n <Button className=\"w-full\">\n <Check /> Mark all as read\n </Button>\n </CardFooter>\n </Card>\n )\n}\n"
}
],
"type": "components:example"
@@ -4,7 +4,7 @@
"files": [
{
"name": "centered-logo-navigation-bar.tsx",
"content": "export default function Component() {\n return (\n <div className=\"flex items-center justify-center p-8\">\n <p className=\"text-muted-foreground\">Component coming soon</p>\n </div>\n )\n}\n"
"content": "export * from \"../centered-logo-navigation-bar\"\n"
}
],
"type": "components:ui"
@@ -7,7 +7,7 @@
"files": [
{
"name": "checkbox-form-multiple.tsx",
"content": "\"use client\"\n\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { useForm } from \"react-hook-form\"\nimport { z } from \"zod\"\n\nimport { Button } from \"@/registry/default/ui/button\"\nimport { Checkbox } from \"@/registry/default/ui/checkbox\"\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/registry/default/ui/form\"\nimport { toast } from \"@/registry/default/ui/use-toast\"\n\nconst items = [\n {\n id: \"recents\",\n label: \"Recents\",\n },\n {\n id: \"home\",\n label: \"Home\",\n },\n {\n id: \"applications\",\n label: \"Applications\",\n },\n {\n id: \"desktop\",\n label: \"Desktop\",\n },\n {\n id: \"downloads\",\n label: \"Downloads\",\n },\n {\n id: \"documents\",\n label: \"Documents\",\n },\n] as const\n\nconst FormSchema = z.object({\n items: z.array(z.string()).refine((value) => value.some((item) => item), {\n message: \"You have to select at least one item.\",\n }),\n})\n\nexport default function CheckboxReactHookFormMultiple() {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n items: [\"recents\", \"home\"],\n },\n })\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast({\n title: \"You submitted the following values:\",\n description: (\n <pre className=\"mt-2 w-[340px] rounded-md bg-slate-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n })\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-8\">\n <FormField\n control={form.control}\n name=\"items\"\n render={() => (\n <FormItem>\n <div className=\"mb-4\">\n <FormLabel className=\"text-base\">Sidebar</FormLabel>\n <FormDescription>\n Select the items you want to display in the sidebar.\n </FormDescription>\n </div>\n {items.map((item) => (\n <FormField\n key={item.id}\n control={form.control}\n name=\"items\"\n render={({ field }) => {\n return (\n <FormItem\n key={item.id}\n className=\"flex flex-row items-start space-x-3 space-y-0\"\n >\n <FormControl>\n <Checkbox\n checked={field.value?.includes(item.id)}\n onCheckedChange={(checked) => {\n return checked\n ? field.onChange([...field.value, item.id])\n : field.onChange(\n field.value?.filter(\n (value) => value !== item.id\n )\n )\n }}\n />\n </FormControl>\n <FormLabel className=\"font-normal\">\n {item.label}\n </FormLabel>\n </FormItem>\n )\n }}\n />\n ))}\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n )\n}\n"
"content": "\"use client\"\n\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { useForm } from \"react-hook-form\"\nimport { z } from \"zod\"\n\nimport { toast } from \"@/registry/default/hooks/use-toast\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport { Checkbox } from \"@/registry/default/ui/checkbox\"\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/registry/default/ui/form\"\n\nconst items = [\n {\n id: \"recents\",\n label: \"Recents\",\n },\n {\n id: \"home\",\n label: \"Home\",\n },\n {\n id: \"applications\",\n label: \"Applications\",\n },\n {\n id: \"desktop\",\n label: \"Desktop\",\n },\n {\n id: \"downloads\",\n label: \"Downloads\",\n },\n {\n id: \"documents\",\n label: \"Documents\",\n },\n] as const\n\nconst FormSchema = z.object({\n items: z.array(z.string()).refine((value) => value.some((item) => item), {\n message: \"You have to select at least one item.\",\n }),\n})\n\nexport default function CheckboxReactHookFormMultiple() {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n items: [\"recents\", \"home\"],\n },\n })\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast({\n title: \"You submitted the following values:\",\n description: (\n <pre className=\"mt-2 w-[340px] rounded-md bg-slate-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n })\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-8\">\n <FormField\n control={form.control}\n name=\"items\"\n render={() => (\n <FormItem>\n <div className=\"mb-4\">\n <FormLabel className=\"text-base\">Sidebar</FormLabel>\n <FormDescription>\n Select the items you want to display in the sidebar.\n </FormDescription>\n </div>\n {items.map((item) => (\n <FormField\n key={item.id}\n control={form.control}\n name=\"items\"\n render={({ field }) => {\n return (\n <FormItem\n key={item.id}\n className=\"flex flex-row items-start space-x-3 space-y-0\"\n >\n <FormControl>\n <Checkbox\n checked={field.value?.includes(item.id)}\n onCheckedChange={(checked) => {\n return checked\n ? field.onChange([...field.value, item.id])\n : field.onChange(\n field.value?.filter(\n (value) => value !== item.id\n )\n )\n }}\n />\n </FormControl>\n <FormLabel className=\"font-normal\">\n {item.label}\n </FormLabel>\n </FormItem>\n )\n }}\n />\n ))}\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n )\n}\n"
}
],
"type": "components:example"
@@ -7,7 +7,7 @@
"files": [
{
"name": "checkbox-form-single.tsx",
"content": "\"use client\"\n\nimport Link from \"next/link\"\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { useForm } from \"react-hook-form\"\nimport { z } from \"zod\"\n\nimport { Button } from \"@/registry/default/ui/button\"\nimport { Checkbox } from \"@/registry/default/ui/checkbox\"\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n} from \"@/registry/default/ui/form\"\nimport { toast } from \"@/registry/default/ui/use-toast\"\n\nconst FormSchema = z.object({\n mobile: z.boolean().default(false).optional(),\n})\n\nexport default function CheckboxReactHookFormSingle() {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n mobile: true,\n },\n })\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast({\n title: \"You submitted the following values:\",\n description: (\n <pre className=\"mt-2 w-[340px] rounded-md bg-slate-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n })\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-6\">\n <FormField\n control={form.control}\n name=\"mobile\"\n render={({ field }) => (\n <FormItem className=\"flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4\">\n <FormControl>\n <Checkbox\n checked={field.value}\n onCheckedChange={field.onChange}\n />\n </FormControl>\n <div className=\"space-y-1 leading-none\">\n <FormLabel>\n Use different settings for my mobile devices\n </FormLabel>\n <FormDescription>\n You can manage your mobile notifications in the{\" \"}\n <Link href=\"/examples/forms\">mobile settings</Link> page.\n </FormDescription>\n </div>\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n )\n}\n"
"content": "\"use client\"\n\nimport Link from \"next/link\"\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { useForm } from \"react-hook-form\"\nimport { z } from \"zod\"\n\nimport { toast } from \"@/registry/default/hooks/use-toast\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport { Checkbox } from \"@/registry/default/ui/checkbox\"\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n} from \"@/registry/default/ui/form\"\n\nconst FormSchema = z.object({\n mobile: z.boolean().default(false).optional(),\n})\n\nexport default function CheckboxReactHookFormSingle() {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n mobile: true,\n },\n })\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast({\n title: \"You submitted the following values:\",\n description: (\n <pre className=\"mt-2 w-[340px] rounded-md bg-slate-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n })\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-6\">\n <FormField\n control={form.control}\n name=\"mobile\"\n render={({ field }) => (\n <FormItem className=\"flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4\">\n <FormControl>\n <Checkbox\n checked={field.value}\n onCheckedChange={field.onChange}\n />\n </FormControl>\n <div className=\"space-y-1 leading-none\">\n <FormLabel>\n Use different settings for my mobile devices\n </FormLabel>\n <FormDescription>\n You can manage your mobile notifications in the{\" \"}\n <Link href=\"/examples/forms\">mobile settings</Link> page.\n </FormDescription>\n </div>\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n )\n}\n"
}
],
"type": "components:example"
@@ -6,7 +6,7 @@
"files": [
{
"name": "checkbox.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as CheckboxPrimitive from \"@radix-ui/react-checkbox\"\nimport { Check } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Checkbox = React.forwardRef<\n React.ElementRef<typeof CheckboxPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>\n>(({ className, ...props }, ref) => (\n <CheckboxPrimitive.Root\n ref={ref}\n className={cn(\n \"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground\",\n className\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n className={cn(\"flex items-center justify-center text-current\")}\n >\n <Check className=\"h-4 w-4\" />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n))\nCheckbox.displayName = CheckboxPrimitive.Root.displayName\n\nexport { Checkbox }\n"
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as CheckboxPrimitive from \"@radix-ui/react-checkbox\"\nimport { Check } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Checkbox = React.forwardRef<\n React.ElementRef<typeof CheckboxPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>\n>(({ className, ...props }, ref) => (\n <CheckboxPrimitive.Root\n ref={ref}\n className={cn(\n \"grid place-content-center peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground\",\n className\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n className={cn(\"grid place-content-center text-current\")}\n >\n <Check className=\"h-4 w-4\" />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n))\nCheckbox.displayName = CheckboxPrimitive.Root.displayName\n\nexport { Checkbox }\n"
}
],
"type": "components:ui"
@@ -4,7 +4,7 @@
"files": [
{
"name": "collaboration-navigation-bar.tsx",
"content": "export default function Component() {\n return (\n <div className=\"flex items-center justify-center p-8\">\n <p className=\"text-muted-foreground\">Component coming soon</p>\n </div>\n )\n}\n"
"content": "export * from \"../collaboration-navigation-bar\"\n"
}
],
"type": "components:ui"
@@ -6,7 +6,7 @@
"files": [
{
"name": "collapsible-demo.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronsUpDown, Plus, X } from \"lucide-react\"\n\nimport { Button } from \"@/registry/default/ui/button\"\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"@/registry/default/ui/collapsible\"\n\nexport default function CollapsibleDemo() {\n const [isOpen, setIsOpen] = React.useState(false)\n\n return (\n <Collapsible\n open={isOpen}\n onOpenChange={setIsOpen}\n className=\"w-[350px] space-y-2\"\n >\n <div className=\"flex items-center justify-between space-x-4 px-4\">\n <h4 className=\"text-sm font-semibold\">\n @peduarte starred 3 repositories\n </h4>\n <CollapsibleTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\" className=\"w-9 p-0\">\n <ChevronsUpDown className=\"h-4 w-4\" />\n <span className=\"sr-only\">Toggle</span>\n </Button>\n </CollapsibleTrigger>\n </div>\n <div className=\"rounded-md border px-4 py-3 font-mono text-sm\">\n @radix-ui/primitives\n </div>\n <CollapsibleContent className=\"space-y-2\">\n <div className=\"rounded-md border px-4 py-3 font-mono text-sm\">\n @radix-ui/colors\n </div>\n <div className=\"rounded-md border px-4 py-3 font-mono text-sm\">\n @stitches/react\n </div>\n </CollapsibleContent>\n </Collapsible>\n )\n}\n"
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronsUpDown } from \"lucide-react\"\n\nimport { Button } from \"@/registry/default/ui/button\"\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"@/registry/default/ui/collapsible\"\n\nexport default function CollapsibleDemo() {\n const [isOpen, setIsOpen] = React.useState(false)\n\n return (\n <Collapsible\n open={isOpen}\n onOpenChange={setIsOpen}\n className=\"w-[350px] space-y-2\"\n >\n <div className=\"flex items-center justify-between space-x-4 px-4\">\n <h4 className=\"text-sm font-semibold\">\n @peduarte starred 3 repositories\n </h4>\n <CollapsibleTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\" className=\"w-9 p-0\">\n <ChevronsUpDown className=\"h-4 w-4\" />\n <span className=\"sr-only\">Toggle</span>\n </Button>\n </CollapsibleTrigger>\n </div>\n <div className=\"rounded-md border px-4 py-3 font-mono text-sm\">\n @radix-ui/primitives\n </div>\n <CollapsibleContent className=\"space-y-2\">\n <div className=\"rounded-md border px-4 py-3 font-mono text-sm\">\n @radix-ui/colors\n </div>\n <div className=\"rounded-md border px-4 py-3 font-mono text-sm\">\n @stitches/react\n </div>\n </CollapsibleContent>\n </Collapsible>\n )\n}\n"
}
],
"type": "components:example"
@@ -6,7 +6,7 @@
"files": [
{
"name": "combobox-demo.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Check, ChevronsUpDown } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n} from \"@/registry/default/ui/command\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/registry/default/ui/popover\"\n\nconst frameworks = [\n {\n value: \"next.js\",\n label: \"Next.js\",\n },\n {\n value: \"sveltekit\",\n label: \"SvelteKit\",\n },\n {\n value: \"nuxt.js\",\n label: \"Nuxt.js\",\n },\n {\n value: \"remix\",\n label: \"Remix\",\n },\n {\n value: \"astro\",\n label: \"Astro\",\n },\n]\n\nexport default function ComboboxDemo() {\n const [open, setOpen] = React.useState(false)\n const [value, setValue] = React.useState(\"\")\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n className=\"w-[200px] justify-between\"\n >\n {value\n ? frameworks.find((framework) => framework.value === value)?.label\n : \"Select framework...\"}\n <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-[200px] p-0\">\n <Command>\n <CommandInput placeholder=\"Search framework...\" />\n <CommandEmpty>No framework found.</CommandEmpty>\n <CommandGroup>\n {frameworks.map((framework) => (\n <CommandItem\n key={framework.value}\n value={framework.value}\n onSelect={(currentValue) => {\n setValue(currentValue === value ? \"\" : currentValue)\n setOpen(false)\n }}\n >\n <Check\n className={cn(\n \"mr-2 h-4 w-4\",\n value === framework.value ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n {framework.label}\n </CommandItem>\n ))}\n </CommandGroup>\n </Command>\n </PopoverContent>\n </Popover>\n )\n}\n"
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Check, ChevronsUpDown } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"@/registry/default/ui/command\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/registry/default/ui/popover\"\n\nconst frameworks = [\n {\n value: \"next.js\",\n label: \"Next.js\",\n },\n {\n value: \"sveltekit\",\n label: \"SvelteKit\",\n },\n {\n value: \"nuxt.js\",\n label: \"Nuxt.js\",\n },\n {\n value: \"remix\",\n label: \"Remix\",\n },\n {\n value: \"astro\",\n label: \"Astro\",\n },\n]\n\nexport default function ComboboxDemo() {\n const [open, setOpen] = React.useState(false)\n const [value, setValue] = React.useState(\"\")\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n className=\"w-[200px] justify-between\"\n >\n {value\n ? frameworks.find((framework) => framework.value === value)?.label\n : \"Select framework...\"}\n <ChevronsUpDown className=\"opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-[200px] p-0\">\n <Command>\n <CommandInput placeholder=\"Search framework...\" />\n <CommandEmpty>No framework found.</CommandEmpty>\n <CommandGroup>\n {frameworks.map((framework) => (\n <CommandItem\n key={framework.value}\n value={framework.value}\n onSelect={(currentValue) => {\n setValue(currentValue === value ? \"\" : currentValue)\n setOpen(false)\n }}\n >\n <Check\n className={cn(\n \"mr-2 h-4 w-4\",\n value === framework.value ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n {framework.label}\n </CommandItem>\n ))}\n </CommandGroup>\n </Command>\n </PopoverContent>\n </Popover>\n )\n}\n"
}
],
"type": "components:example"
@@ -8,7 +8,7 @@
"files": [
{
"name": "combobox-dropdown-menu.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Calendar, MoreHorizontal, Tags, Trash, User } from \"lucide-react\"\n\nimport { Button } from \"@/registry/default/ui/button\"\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"@/registry/default/ui/command\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from \"@/registry/default/ui/dropdown-menu\"\n\nconst labels = [\n \"feature\",\n \"bug\",\n \"enhancement\",\n \"documentation\",\n \"design\",\n \"question\",\n \"maintenance\",\n]\n\nexport default function ComboboxDropdownMenu() {\n const [label, setLabel] = React.useState(\"feature\")\n const [open, setOpen] = React.useState(false)\n\n return (\n <div className=\"flex w-full flex-col items-start justify-between rounded-md border px-4 py-3 sm:flex-row sm:items-center\">\n <p className=\"text-sm font-medium leading-none\">\n <span className=\"mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground\">\n {label}\n </span>\n <span className=\"text-muted-foreground\">Create a new project</span>\n </p>\n <DropdownMenu open={open} onOpenChange={setOpen}>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\">\n <MoreHorizontal />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-[200px]\">\n <DropdownMenuLabel>Actions</DropdownMenuLabel>\n <DropdownMenuGroup>\n <DropdownMenuItem>\n <User className=\"mr-2 h-4 w-4\" />\n Assign to...\n </DropdownMenuItem>\n <DropdownMenuItem>\n <Calendar className=\"mr-2 h-4 w-4\" />\n Set due date...\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuSub>\n <DropdownMenuSubTrigger>\n <Tags className=\"mr-2 h-4 w-4\" />\n Apply label\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"p-0\">\n <Command>\n <CommandInput\n placeholder=\"Filter label...\"\n autoFocus={true}\n />\n <CommandList>\n <CommandEmpty>No label found.</CommandEmpty>\n <CommandGroup>\n {labels.map((label) => (\n <CommandItem\n key={label}\n value={label}\n onSelect={(value) => {\n setLabel(value)\n setOpen(false)\n }}\n >\n {label}\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n <DropdownMenuSeparator />\n <DropdownMenuItem className=\"text-red-600\">\n <Trash className=\"mr-2 h-4 w-4\" />\n Delete\n <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>\n </DropdownMenuItem>\n </DropdownMenuGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )\n}\n"
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Calendar, MoreHorizontal, Tags, Trash, User } from \"lucide-react\"\n\nimport { Button } from \"@/registry/default/ui/button\"\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"@/registry/default/ui/command\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from \"@/registry/default/ui/dropdown-menu\"\n\nconst labels = [\n \"feature\",\n \"bug\",\n \"enhancement\",\n \"documentation\",\n \"design\",\n \"question\",\n \"maintenance\",\n]\n\nexport default function ComboboxDropdownMenu() {\n const [label, setLabel] = React.useState(\"feature\")\n const [open, setOpen] = React.useState(false)\n\n return (\n <div className=\"flex w-full flex-col items-start justify-between rounded-md border px-4 py-3 sm:flex-row sm:items-center\">\n <p className=\"text-sm font-medium leading-none\">\n <span className=\"mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground\">\n {label}\n </span>\n <span className=\"text-muted-foreground\">Create a new project</span>\n </p>\n <DropdownMenu open={open} onOpenChange={setOpen}>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\">\n <MoreHorizontal />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-[200px]\">\n <DropdownMenuLabel>Actions</DropdownMenuLabel>\n <DropdownMenuGroup>\n <DropdownMenuItem>\n <User />\n Assign to...\n </DropdownMenuItem>\n <DropdownMenuItem>\n <Calendar />\n Set due date...\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuSub>\n <DropdownMenuSubTrigger>\n <Tags />\n Apply label\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"p-0\">\n <Command>\n <CommandInput\n placeholder=\"Filter label...\"\n autoFocus={true}\n />\n <CommandList>\n <CommandEmpty>No label found.</CommandEmpty>\n <CommandGroup>\n {labels.map((label) => (\n <CommandItem\n key={label}\n value={label}\n onSelect={(value) => {\n setLabel(value)\n setOpen(false)\n }}\n >\n {label}\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n <DropdownMenuSeparator />\n <DropdownMenuItem className=\"text-red-600\">\n <Trash />\n Delete\n <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>\n </DropdownMenuItem>\n </DropdownMenuGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )\n}\n"
}
],
"type": "components:example"
@@ -7,7 +7,7 @@
"files": [
{
"name": "combobox-form.tsx",
"content": "\"use client\"\n\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { Check, ChevronsUpDown } from \"lucide-react\"\nimport { useForm } from \"react-hook-form\"\nimport { z } from \"zod\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n} from \"@/registry/default/ui/command\"\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/registry/default/ui/form\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/registry/default/ui/popover\"\nimport { toast } from \"@/registry/default/ui/use-toast\"\n\nconst languages = [\n { label: \"English\", value: \"en\" },\n { label: \"French\", value: \"fr\" },\n { label: \"German\", value: \"de\" },\n { label: \"Spanish\", value: \"es\" },\n { label: \"Portuguese\", value: \"pt\" },\n { label: \"Russian\", value: \"ru\" },\n { label: \"Japanese\", value: \"ja\" },\n { label: \"Korean\", value: \"ko\" },\n { label: \"Chinese\", value: \"zh\" },\n] as const\n\nconst FormSchema = z.object({\n language: z.string({\n message: \"Please select a language.\",\n }),\n})\n\nexport default function ComboboxForm() {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n })\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast({\n title: \"You submitted the following values:\",\n description: (\n <pre className=\"mt-2 w-[340px] rounded-md bg-slate-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n })\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-6\">\n <FormField\n control={form.control}\n name=\"language\"\n render={({ field }) => (\n <FormItem className=\"flex flex-col\">\n <FormLabel>Language</FormLabel>\n <Popover>\n <PopoverTrigger asChild>\n <FormControl>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n className={cn(\n \"w-[200px] justify-between\",\n !field.value && \"text-muted-foreground\"\n )}\n >\n {field.value\n ? languages.find(\n (language) => language.value === field.value\n )?.label\n : \"Select language\"}\n <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </FormControl>\n </PopoverTrigger>\n <PopoverContent className=\"w-[200px] p-0\">\n <Command>\n <CommandInput placeholder=\"Search language...\" />\n <CommandEmpty>No language found.</CommandEmpty>\n <CommandGroup>\n {languages.map((language) => (\n <CommandItem\n value={language.label}\n key={language.value}\n onSelect={() => {\n form.setValue(\"language\", language.value)\n }}\n >\n <Check\n className={cn(\n \"mr-2 h-4 w-4\",\n language.value === field.value\n ? \"opacity-100\"\n : \"opacity-0\"\n )}\n />\n {language.label}\n </CommandItem>\n ))}\n </CommandGroup>\n </Command>\n </PopoverContent>\n </Popover>\n <FormDescription>\n This is the language that will be used in the dashboard.\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n )\n}\n"
"content": "\"use client\"\n\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { Check, ChevronsUpDown } from \"lucide-react\"\nimport { useForm } from \"react-hook-form\"\nimport { z } from \"zod\"\n\nimport { cn } from \"@/lib/utils\"\nimport { toast } from \"@/registry/default/hooks/use-toast\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"@/registry/default/ui/command\"\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/registry/default/ui/form\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/registry/default/ui/popover\"\n\nconst languages = [\n { label: \"English\", value: \"en\" },\n { label: \"French\", value: \"fr\" },\n { label: \"German\", value: \"de\" },\n { label: \"Spanish\", value: \"es\" },\n { label: \"Portuguese\", value: \"pt\" },\n { label: \"Russian\", value: \"ru\" },\n { label: \"Japanese\", value: \"ja\" },\n { label: \"Korean\", value: \"ko\" },\n { label: \"Chinese\", value: \"zh\" },\n] as const\n\nconst FormSchema = z.object({\n language: z.string({\n message: \"Please select a language.\",\n }),\n})\n\nexport default function ComboboxForm() {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n })\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast({\n title: \"You submitted the following values:\",\n description: (\n <pre className=\"mt-2 w-[340px] rounded-md bg-slate-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n })\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-6\">\n <FormField\n control={form.control}\n name=\"language\"\n render={({ field }) => (\n <FormItem className=\"flex flex-col\">\n <FormLabel>Language</FormLabel>\n <Popover>\n <PopoverTrigger asChild>\n <FormControl>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n className={cn(\n \"w-[200px] justify-between\",\n !field.value && \"text-muted-foreground\"\n )}\n >\n {field.value\n ? languages.find(\n (language) => language.value === field.value\n )?.label\n : \"Select language\"}\n <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </FormControl>\n </PopoverTrigger>\n <PopoverContent className=\"w-[200px] p-0\">\n <Command>\n <CommandInput placeholder=\"Search language...\" />\n <CommandEmpty>No language found.</CommandEmpty>\n <CommandGroup>\n {languages.map((language) => (\n <CommandItem\n value={language.label}\n key={language.value}\n onSelect={() => {\n form.setValue(\"language\", language.value)\n }}\n >\n <Check\n className={cn(\n \"mr-2 h-4 w-4\",\n language.value === field.value\n ? \"opacity-100\"\n : \"opacity-0\"\n )}\n />\n {language.label}\n </CommandItem>\n ))}\n </CommandGroup>\n </Command>\n </PopoverContent>\n </Popover>\n <FormDescription>\n This is the language that will be used in the dashboard.\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n )\n}\n"
}
],
"type": "components:example"
@@ -6,7 +6,7 @@
"files": [
{
"name": "command-demo.tsx",
"content": "import {\n Calculator,\n Calendar,\n CreditCard,\n Settings,\n Smile,\n User,\n} from \"lucide-react\"\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n CommandShortcut,\n} from \"@/registry/default/ui/command\"\n\nexport default function CommandDemo() {\n return (\n <Command className=\"rounded-lg border shadow-md\">\n <CommandInput placeholder=\"Type a command or search...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup heading=\"Suggestions\">\n <CommandItem>\n <Calendar className=\"mr-2 h-4 w-4\" />\n <span>Calendar</span>\n </CommandItem>\n <CommandItem>\n <Smile className=\"mr-2 h-4 w-4\" />\n <span>Search Emoji</span>\n </CommandItem>\n <CommandItem>\n <Calculator className=\"mr-2 h-4 w-4\" />\n <span>Calculator</span>\n </CommandItem>\n </CommandGroup>\n <CommandSeparator />\n <CommandGroup heading=\"Settings\">\n <CommandItem>\n <User className=\"mr-2 h-4 w-4\" />\n <span>Profile</span>\n <CommandShortcut>⌘P</CommandShortcut>\n </CommandItem>\n <CommandItem>\n <CreditCard className=\"mr-2 h-4 w-4\" />\n <span>Billing</span>\n <CommandShortcut>⌘B</CommandShortcut>\n </CommandItem>\n <CommandItem>\n <Settings className=\"mr-2 h-4 w-4\" />\n <span>Settings</span>\n <CommandShortcut>⌘S</CommandShortcut>\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n )\n}\n"
"content": "import {\n Calculator,\n Calendar,\n CreditCard,\n Settings,\n Smile,\n User,\n} from \"lucide-react\"\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n CommandShortcut,\n} from \"@/registry/default/ui/command\"\n\nexport default function CommandDemo() {\n return (\n <Command className=\"rounded-lg border shadow-md md:min-w-[450px]\">\n <CommandInput placeholder=\"Type a command or search...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup heading=\"Suggestions\">\n <CommandItem>\n <Calendar />\n <span>Calendar</span>\n </CommandItem>\n <CommandItem>\n <Smile />\n <span>Search Emoji</span>\n </CommandItem>\n <CommandItem disabled>\n <Calculator />\n <span>Calculator</span>\n </CommandItem>\n </CommandGroup>\n <CommandSeparator />\n <CommandGroup heading=\"Settings\">\n <CommandItem>\n <User />\n <span>Profile</span>\n <CommandShortcut>⌘P</CommandShortcut>\n </CommandItem>\n <CommandItem>\n <CreditCard />\n <span>Billing</span>\n <CommandShortcut>⌘B</CommandShortcut>\n </CommandItem>\n <CommandItem>\n <Settings />\n <span>Settings</span>\n <CommandShortcut>⌘S</CommandShortcut>\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n )\n}\n"
}
],
"type": "components:example"
@@ -7,7 +7,7 @@
"files": [
{
"name": "command-dialog.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n Calculator,\n Calendar,\n CreditCard,\n Settings,\n Smile,\n User,\n} from \"lucide-react\"\n\nimport {\n CommandDialog,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n CommandShortcut,\n} from \"@/registry/default/ui/command\"\n\nexport default function CommandDialogDemo() {\n const [open, setOpen] = React.useState(false)\n\n React.useEffect(() => {\n const down = (e: KeyboardEvent) => {\n if (e.key === \"j\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n setOpen((open) => !open)\n }\n }\n\n document.addEventListener(\"keydown\", down)\n return () => document.removeEventListener(\"keydown\", down)\n }, [])\n\n return (\n <>\n <p className=\"text-sm text-muted-foreground\">\n Press{\" \"}\n <kbd className=\"pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100\">\n <span className=\"text-xs\">⌘</span>J\n </kbd>\n </p>\n <CommandDialog open={open} onOpenChange={setOpen}>\n <CommandInput placeholder=\"Type a command or search...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup heading=\"Suggestions\">\n <CommandItem>\n <Calendar className=\"mr-2 h-4 w-4\" />\n <span>Calendar</span>\n </CommandItem>\n <CommandItem>\n <Smile className=\"mr-2 h-4 w-4\" />\n <span>Search Emoji</span>\n </CommandItem>\n <CommandItem>\n <Calculator className=\"mr-2 h-4 w-4\" />\n <span>Calculator</span>\n </CommandItem>\n </CommandGroup>\n <CommandSeparator />\n <CommandGroup heading=\"Settings\">\n <CommandItem>\n <User className=\"mr-2 h-4 w-4\" />\n <span>Profile</span>\n <CommandShortcut>⌘P</CommandShortcut>\n </CommandItem>\n <CommandItem>\n <CreditCard className=\"mr-2 h-4 w-4\" />\n <span>Billing</span>\n <CommandShortcut>⌘B</CommandShortcut>\n </CommandItem>\n <CommandItem>\n <Settings className=\"mr-2 h-4 w-4\" />\n <span>Settings</span>\n <CommandShortcut>⌘S</CommandShortcut>\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </CommandDialog>\n </>\n )\n}\n"
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n Calculator,\n Calendar,\n CreditCard,\n Settings,\n Smile,\n User,\n} from \"lucide-react\"\n\nimport {\n CommandDialog,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n CommandShortcut,\n} from \"@/registry/default/ui/command\"\n\nexport default function CommandDialogDemo() {\n const [open, setOpen] = React.useState(false)\n\n React.useEffect(() => {\n const down = (e: KeyboardEvent) => {\n if (e.key === \"j\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n setOpen((open) => !open)\n }\n }\n\n document.addEventListener(\"keydown\", down)\n return () => document.removeEventListener(\"keydown\", down)\n }, [])\n\n return (\n <>\n <p className=\"text-sm text-muted-foreground\">\n Press{\" \"}\n <kbd className=\"pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100\">\n <span className=\"text-xs\">⌘</span>J\n </kbd>\n </p>\n <CommandDialog open={open} onOpenChange={setOpen}>\n <CommandInput placeholder=\"Type a command or search...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup heading=\"Suggestions\">\n <CommandItem>\n <Calendar />\n <span>Calendar</span>\n </CommandItem>\n <CommandItem>\n <Smile />\n <span>Search Emoji</span>\n </CommandItem>\n <CommandItem>\n <Calculator />\n <span>Calculator</span>\n </CommandItem>\n </CommandGroup>\n <CommandSeparator />\n <CommandGroup heading=\"Settings\">\n <CommandItem>\n <User />\n <span>Profile</span>\n <CommandShortcut>⌘P</CommandShortcut>\n </CommandItem>\n <CommandItem>\n <CreditCard />\n <span>Billing</span>\n <CommandShortcut>⌘B</CommandShortcut>\n </CommandItem>\n <CommandItem>\n <Settings />\n <span>Settings</span>\n <CommandShortcut>⌘S</CommandShortcut>\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </CommandDialog>\n </>\n )\n}\n"
}
],
"type": "components:example"
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
"files": [
{
"name": "communication-navigation-bar.tsx",
"content": "export default function Component() {\n return (\n <div className=\"flex items-center justify-center p-8\">\n <p className=\"text-muted-foreground\">Component coming soon</p>\n </div>\n )\n}\n"
"content": "export * from \"../communication-navigation-bar\"\n"
}
],
"type": "components:ui"
@@ -7,7 +7,7 @@
"files": [
{
"name": "company-profile.tsx",
"content": "export * from \"../ui/company-profile\"\n"
"content": "export { default } from \"../ui/company-profile\"\nexport * from \"../ui/company-profile\"\n"
}
],
"type": "components:finance",
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
"files": [
{
"name": "context-switcher-navigation-bar.tsx",
"content": "export default function Component() {\n return (\n <div className=\"flex items-center justify-center p-8\">\n <p className=\"text-muted-foreground\">Component coming soon</p>\n </div>\n )\n}\n"
"content": "export * from \"../context-switcher-navigation-bar\"\n"
}
],
"type": "components:ui"
@@ -7,7 +7,7 @@
"files": [
{
"name": "crypto-screener.tsx",
"content": "export * from \"../ui/crypto-screener\"\n"
"content": "export { default } from \"../ui/crypto-screener\"\nexport * from \"../ui/crypto-screener\"\n"
}
],
"type": "components:finance",
@@ -4,7 +4,7 @@
"files": [
{
"name": "dashboard-navigation-bar.tsx",
"content": "export default function Component() {\n return (\n <div className=\"flex items-center justify-center p-8\">\n <p className=\"text-muted-foreground\">Component coming soon</p>\n </div>\n )\n}\n"
"content": "export * from \"../dashboard-navigation-bar\"\n"
}
],
"type": "components:ui"
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@
"files": [
{
"name": "date-picker-demo.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { format } from \"date-fns\"\nimport { Calendar as CalendarIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport { Calendar } from \"@/registry/default/ui/calendar\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/registry/default/ui/popover\"\n\nexport default function DatePickerDemo() {\n const [date, setDate] = React.useState<Date>()\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant={\"outline\"}\n className={cn(\n \"w-[280px] justify-start text-left font-normal\",\n !date && \"text-muted-foreground\"\n )}\n >\n <CalendarIcon className=\"mr-2 h-4 w-4\" />\n {date ? format(date, \"PPP\") : <span>Pick a date</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\">\n <Calendar\n mode=\"single\"\n selected={date}\n onSelect={setDate}\n initialFocus\n />\n </PopoverContent>\n </Popover>\n )\n}\n"
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { format } from \"date-fns\"\nimport { CalendarIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport { Calendar } from \"@/registry/default/ui/calendar\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/registry/default/ui/popover\"\n\nexport default function DatePickerDemo() {\n const [date, setDate] = React.useState<Date>()\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant={\"outline\"}\n className={cn(\n \"w-[280px] justify-start text-left font-normal\",\n !date && \"text-muted-foreground\"\n )}\n >\n <CalendarIcon />\n {date ? format(date, \"PPP\") : <span>Pick a date</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\">\n <Calendar\n mode=\"single\"\n selected={date}\n onSelect={setDate}\n initialFocus\n />\n </PopoverContent>\n </Popover>\n )\n}\n"
}
],
"type": "components:example"

Some files were not shown because too many files have changed in this diff Show More