diff --git a/.changeset/config.json b/.changeset/config.json index f0d15baf7..409a2b622 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,15 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": [ - "www", - "@dotui/ts-config", - "@dotui/api", - "@dotui/auth", - "@dotui/db", - "@dotui/registry", - "@dotui/colors", - "@dotui/types", - "@dotui/core" - ] + "ignore": ["www", "@dotui/ts-config", "@dotui/colors", "@dotui/types"] } diff --git a/.env.example b/.env.example index 00ae4959d..e69de29bb 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +0,0 @@ -POSTGRES_URL= - -GITHUB_CLIENT_ID= -GITHUB_CLIENT_SECRET= - -AUTH_SECRET= \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 025295d39..58c482410 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,26 +66,4 @@ jobs: fetch-depth: 0 - uses: ./.github/actions/setup - - run: pnpm test - - check-registry: - runs-on: ubuntu-latest - name: Check registry - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: ./.github/actions/setup - - - name: Build registry - run: pnpm build:registry - - - name: Check for uncommitted changes - run: | - if [[ -n $(git status --porcelain packages/core/src/__registry__/) ]]; then - echo "Error: Generated registry files are out of sync!" - echo "Please run 'pnpm build:registry' and commit the changes." - git diff packages/core/src/__registry__/ - exit 1 - fi - echo "Registry files are in sync." \ No newline at end of file + - run: pnpm test \ No newline at end of file diff --git a/STYLES_MIGRATION.md b/STYLES_MIGRATION.md new file mode 100644 index 000000000..c8d8acfb2 --- /dev/null +++ b/STYLES_MIGRATION.md @@ -0,0 +1,359 @@ +# Styles Migration + +## Architecture + +**Before:** Each style variant = separate file (`button/base.tsx`, `button/brutalist.tsx`). Meta pointed to different files. CLI installed the chosen file. + +**Now:** +- `base.tsx` = single component file with logic + `useStyles()` hook +- `styles.ts` = all style definitions in one place (base, brutalist, etc.) +- **Docs/preview**: `StyleProvider` context picks the active style → `useStyles()` returns the right one +- **CLI install**: transforms `base.tsx` → removes `useStyles()` → inlines the chosen style's `tv()` definition → outputs as `button.tsx` + +## Naming + +- **Preset** = entire design system config (colors, fonts, component style selections) +- **Style** = per-component visual option (`"base"`, `"brutalist"`, etc.) + +## Pattern + +### `meta.ts` + +Renamed `variants`/`defaultVariant` → `styles`/`defaultStyle`. Styles are component-level (not inside `files`). `styles.ts` is not listed in `files` (internal only). + +```ts +import type { RegistryItem } from "@/registry/types"; + +const buttonMeta = { + name: "button", + type: "registry:ui", + group: "buttons", + defaultStyle: "default", + styles: { + default: {}, + brutalist: {}, + }, + files: [ + { type: "registry:ui", path: "ui/button/base.tsx", target: "ui/button.tsx" }, + ], + registryDependencies: ["loader", "focus-styles"], +} satisfies RegistryItem; + +export default buttonMeta; + +export type ButtonStyle = keyof typeof buttonMeta.styles; +export const buttonStyleNames = Object.keys(buttonMeta.styles) as ButtonStyle[]; +export const defaultButtonStyle = buttonMeta.defaultStyle; +``` + +### `RegistryItem` type + +```ts +export type StyleMeta = { + description?: string; +}; + +export type RegistryItem = ShadcnRegistryItem & + ( + | { + styles: Record; + defaultStyle: string; + } + | { styles?: never; defaultStyle?: never } + ) & { + group?: ComponentGroup | null; + }; +``` + +### `styles.ts` + +Each component gets a `styles.ts` that extracts `tv()` definitions from `base.tsx`. `createStyles` takes the meta object so style map keys are type-checked against `meta.styles`. + +```ts +import { tv } from "tailwind-variants"; +import { createStyles } from "@/modules/core/styles"; +import componentMeta from "./meta"; + +const baseStyles = tv({ + base: "", +}); + +const defaultStyles = tv({ + extend: baseStyles, + base: "...", // all current styles go here +}); + +// Export type for VariantProps usage in base.tsx +export type ComponentStyles = typeof defaultStyles; + +export const { useStyles } = createStyles(componentMeta, { + default: defaultStyles, +}); +``` + +For slot-based components: +```ts +const baseStyles = tv({ + slots: { root: "", title: "" }, +}); + +const defaultStyles = tv({ + extend: baseStyles, + slots: { root: "...", title: "..." }, + variants: { ... }, +}); + +export type ComponentStyles = typeof defaultStyles; +``` + +### `base.tsx` + +- Remove `import { tv } from "tailwind-variants"` (keep `VariantProps` if used for prop types) +- Add `import { useStyles } from "./styles"` +- If `VariantProps` is used, add `import type { ComponentStyles } from "./styles"` and use `VariantProps` +- Replace inline `tv()` calls with `useStyles()` inside component functions +- For slot-based components, move `const { root, ... } = useStyles()()` inside each component function +- Add `// MARK: componentStyles` where the inlined `tv()` constant will go during CLI transform +- Add `// MARK: seperator` between each sub-component + +#### CLI transform + +When installed via CLI, the code is transformed: +- `import { useStyles } from "./styles"` → removed +- `import type { ComponentStyles } from "./styles"` → removed +- `ComponentStyles` → `typeof componentStyles` (e.g. `AlertStyles` → `typeof alertStyles`) +- `useStyles()` → `componentStyles` (the inlined tv constant) +- `useStyles()()` (slot-based) → `componentStyles()` +- `// MARK: componentStyles` → `const componentStyles = tv({ ... })` (the chosen style's tv definition inlined) +- `// MARK: seperator` → the visual separator comment (`/* ---...--- */`) + +### `examples.tsx` + +- Import `Examples` from `@/modules/create/preview/examples` +- Wrap all examples in `` instead of `<>` +- Use named export: `ComponentNameExamples` + +```tsx +import { Example } from "@/modules/create/preview/example"; +import { Examples } from "@/modules/create/preview/examples"; + +export default function ComponentNameExamples() { + return ( + + ... + + ); +} +``` + +### `createStyles` + +Takes the meta object directly. `T` is inferred from the styles map, and keys are type-checked against `meta.styles` — missing a key is a compile error. + +```ts +function createStyles< + M extends { name: string; defaultStyle: string; styles: Record }, + T extends Record, +>(meta: M, stylesMap: T) { + function useStyles(): T[keyof T] { + const selections = React.useContext(StyleContext); + const selected = selections[meta.name]; + if (selected && selected in stylesMap) { + return stylesMap[selected as keyof T]; + } + return stylesMap[meta.defaultStyle as keyof T]; + } + return { useStyles }; +} +``` + +## Special cases + +- `field/base.tsx`: exports `fieldStyles` (used by 11 consumers) — re-export from `styles.ts` +- `toggle-button/base.tsx`: exports `toggleButtonStyles` (type import by toggle-button-group) +- `button/base.tsx`: exports `buttonStyles` +- `link/base.tsx`: exports `linkVariants` + +## Migration checklist + +### `meta.ts` (rename `variants`→`styles`, `defaultVariant`→`defaultStyle`, update exports) + +- [x] accordion +- [x] alert +- [x] avatar +- [ ] badge +- [ ] breadcrumbs +- [ ] button +- [ ] calendar +- [ ] card +- [ ] checkbox +- [ ] checkbox-group +- [ ] color-area +- [ ] color-editor +- [ ] color-field +- [ ] color-picker +- [ ] color-slider +- [ ] color-swatch +- [ ] color-swatch-picker +- [ ] color-thumb +- [ ] combobox +- [ ] command +- [ ] date-field +- [ ] date-picker +- [ ] dialog +- [ ] disclosure +- [ ] drawer +- [ ] drop-zone +- [ ] empty +- [ ] field +- [ ] file-trigger +- [ ] form (no meta — skip?) +- [ ] group +- [ ] input +- [ ] kbd +- [ ] link +- [ ] list-box +- [ ] loader +- [ ] menu +- [ ] modal +- [ ] number-field +- [ ] overlay +- [ ] popover +- [ ] progress-bar +- [ ] radio-group +- [ ] react-hook-form +- [ ] search-field +- [ ] select +- [ ] separator +- [ ] sidebar +- [ ] skeleton +- [ ] slider +- [ ] switch +- [ ] table +- [ ] tabs +- [ ] tag-group +- [ ] text +- [ ] text-field +- [ ] time-field +- [ ] toast +- [ ] toggle-button +- [ ] toggle-button-group +- [ ] tooltip + +### `styles.ts` (create, extract `tv()` from `base.tsx`) + +- [x] accordion +- [x] alert +- [x] avatar +- [ ] badge +- [ ] breadcrumbs +- [ ] button +- [ ] calendar +- [ ] card +- [ ] checkbox +- [ ] color-area +- [ ] color-field +- [ ] color-slider +- [ ] color-swatch +- [ ] color-swatch-picker +- [ ] color-thumb +- [ ] command +- [ ] date-field +- [ ] date-picker +- [ ] dialog +- [ ] disclosure +- [ ] drawer +- [ ] drop-zone +- [ ] empty +- [ ] field +- [ ] group +- [ ] input +- [ ] kbd +- [ ] link +- [ ] list-box +- [ ] menu +- [ ] modal +- [ ] popover +- [ ] progress-bar +- [ ] radio-group +- [ ] search-field +- [ ] select +- [ ] separator +- [ ] sidebar +- [ ] slider +- [ ] switch +- [ ] table +- [ ] tabs +- [ ] tag-group +- [ ] text-field +- [ ] time-field +- [ ] toast +- [ ] toggle-button +- [ ] toggle-button-group +- [ ] tooltip + +### `examples.tsx` (wrap in ``) + +- [x] accordion +- [x] alert +- [x] avatar +- [ ] badge +- [ ] breadcrumbs +- [ ] button +- [ ] checkbox +- [ ] checkbox-group +- [ ] color-area +- [ ] color-editor +- [ ] color-field +- [ ] color-picker +- [ ] color-slider +- [ ] color-swatch +- [ ] color-swatch-picker +- [ ] combobox +- [ ] command +- [ ] date-field +- [ ] date-picker +- [ ] dialog +- [ ] disclosure +- [ ] drawer +- [ ] drop-zone +- [ ] empty +- [ ] file-trigger +- [ ] form +- [ ] group +- [ ] list-box +- [ ] menu +- [ ] modal +- [ ] number-field +- [ ] overlay +- [ ] popover +- [ ] progress-bar +- [ ] radio-group +- [ ] react-hook-form +- [ ] search-field +- [ ] select +- [ ] separator +- [ ] skeleton +- [ ] slider +- [ ] switch +- [ ] table +- [ ] tabs +- [ ] tag-group +- [ ] text-area +- [ ] text-field +- [ ] time-field +- [ ] toggle-button +- [ ] toggle-button-group +- [ ] tooltip + +### Components without `tv()` (skip `styles.ts`, only update `meta.ts` + `examples.tsx`) + +checkbox-group, color-editor, color-picker, combobox, file-trigger, loader, number-field, overlay, skeleton, text + +### Components without `examples.tsx` (skip examples update) + +calendar, card, color-thumb, field, header, heading, input, kbd, link, loader, pagination, sidebar, tanstack-form, text, toast, tree + +### Also needs migration + +- `www/src/registry/lib/focus-styles/meta.ts` — uses old `variants`/`defaultVariant` pattern diff --git a/biome.json b/biome.json index 0239dc75a..67a788073 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json", "root": true, "extends": ["@dotui/biome-config/base"] } diff --git a/config/biome-config/base.json b/config/biome-config/base.json index bd9475195..4adf7f056 100644 --- a/config/biome-config/base.json +++ b/config/biome-config/base.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json", "root": false, "vcs": { "enabled": true, @@ -41,6 +41,9 @@ }, "correctness": { "useExhaustiveDependencies": "warn" + }, + "a11y": { + "useValidAnchor": "off" } } }, diff --git a/config/biome-config/react-internal.json b/config/biome-config/react-internal.json index 03c10cae3..a7ae5e5d1 100644 --- a/config/biome-config/react-internal.json +++ b/config/biome-config/react-internal.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json", "root": false, "vcs": { "enabled": true, diff --git a/config/biome-config/start.json b/config/biome-config/start.json index d2d966ec6..724284922 100644 --- a/config/biome-config/start.json +++ b/config/biome-config/start.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json", "files": { "includes": ["**", "!**/routeTree.gen.ts", "!**/.nitro", "!**/.output", "!**/.source"] }, diff --git a/package.json b/package.json index 03ea1e336..76279130e 100644 --- a/package.json +++ b/package.json @@ -23,10 +23,10 @@ "build": "turbo run build", "dev": "turbo run dev --parallel", "dev:www": "pnpm --filter=www dev", + "dev:www:clean": "pnpm --filter=www dev:clean", "build:www": "pnpm --filter=www build", - "build:registry": "pnpm --filter=@dotui/registry build", + "build:registry": "pnpm --filter=www build:registry", "preview:www": "pnpm --filter=www preview", - "dev:registry": "chokidar 'packages/registry/src/ui/**/meta.ts' -c 'pnpm build:registry'", "build:references": "pnpm --filter=www build:references", "clean": "git clean -xdf node_modules", "clean:workspaces": "turbo run clean", @@ -42,7 +42,7 @@ "pub:release": "pnpm --filter=tailwindcss-autocontrast pub:release" }, "devDependencies": { - "@biomejs/biome": "^2.3.8", + "@biomejs/biome": "^2.4.10", "@changesets/changelog-github": "^0.5.2", "@changesets/cli": "^2.29.8", "@dotui/biome-config": "workspace:*", @@ -56,9 +56,5 @@ "vitest": "^4.0.16" }, "packageManager": "pnpm@10.24.0", - "pnpm": { - "patchedDependencies": { - "@tanstack/start-static-server-functions": "patches/@tanstack__start-static-server-functions.patch" - } - } + "pnpm": {} } diff --git a/packages/api/biome.json b/packages/api/biome.json deleted file mode 100644 index 54d045b56..000000000 --- a/packages/api/biome.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", - "root": false, - "extends": ["@dotui/biome-config/base"] -} diff --git a/packages/api/package.json b/packages/api/package.json deleted file mode 100644 index dd6c1d41a..000000000 --- a/packages/api/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@dotui/api", - "version": "0.1.0", - "private": true, - "type": "module", - "exports": { - ".": "./src/index.ts" - }, - "license": "MIT", - "scripts": { - "clean": "git clean -xdf .cache .turbo dist node_modules", - "typecheck": "tsc --noEmit --emitDeclarationOnly false" - }, - "dependencies": { - "@dotui/auth": "workspace:*", - "@dotui/db": "workspace:*", - "@trpc/server": "^11.2.0", - "superjson": "2.2.3", - "zod": "^4.2.1" - }, - "devDependencies": { - "@dotui/biome-config": "workspace:*", - "@dotui/ts-config": "workspace:*", - "typescript": "^5.8.3" - } -} diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts deleted file mode 100644 index 19de55ac0..000000000 --- a/packages/api/src/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server"; - -import { db } from "@dotui/db/client"; - -import { appRouter } from "./root"; -import { createCallerFactory, createTRPCContext } from "./trpc"; -import type { AppRouter } from "./root"; - -type RouterInputs = inferRouterInputs; -type RouterOutputs = inferRouterOutputs; - -/** Server-side caller for public (unauthenticated) procedures */ -const publicCaller = createCallerFactory(appRouter)({ - db, - session: null, - authApi: undefined as never, -}); - -export { appRouter, createTRPCContext, publicCaller }; -export type { AppRouter, RouterInputs, RouterOutputs }; diff --git a/packages/api/src/root.ts b/packages/api/src/root.ts deleted file mode 100644 index 6181533c8..000000000 --- a/packages/api/src/root.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { authRouter } from "./routers/auth"; -import { styleRouter } from "./routers/style"; -import { createTRPCRouter } from "./trpc"; - -export const appRouter = createTRPCRouter({ - auth: authRouter, - style: styleRouter, -}); - -export type AppRouter = typeof appRouter; diff --git a/packages/api/src/routers/auth.ts b/packages/api/src/routers/auth.ts deleted file mode 100644 index 02246ab5a..000000000 --- a/packages/api/src/routers/auth.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { TRPCRouterRecord } from "@trpc/server"; - -import { publicProcedure } from "../trpc"; - -export const authRouter = { - getSession: publicProcedure.query(({ ctx }) => { - return ctx.session; - }), -} satisfies TRPCRouterRecord; diff --git a/packages/api/src/routers/style.ts b/packages/api/src/routers/style.ts deleted file mode 100644 index 0f0618d2d..000000000 --- a/packages/api/src/routers/style.ts +++ /dev/null @@ -1,326 +0,0 @@ -import { TRPCError } from "@trpc/server"; -import { z } from "zod"; -import type { TRPCRouterRecord } from "@trpc/server"; - -import { and, eq } from "@dotui/db"; -import { createStyleSchema, style, updateStyleConfigSchema, user } from "@dotui/db/schemas"; - -import { protectedProcedure, publicProcedure } from "../trpc"; - -const uuidSchema = z.string().uuid("Invalid UUID format"); -const paginationSchema = z.object({ - limit: z.number().min(1).max(100).default(10), - offset: z.number().min(0).default(0), -}); - -export const styleRouter = { - getActive: protectedProcedure.query(({ ctx }) => { - return ctx.session.user.activeStyleId; - }), - setActive: protectedProcedure - .input( - z.object({ - styleId: uuidSchema, - }), - ) - .mutation(async ({ ctx, input }) => { - // Verify style exists - const styleRecord = await ctx.db.query.style.findFirst({ - where: eq(style.id, input.styleId), - }); - - if (!styleRecord) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Style not found", - }); - } - - // Verify user has access (must own it or it must be public/unlisted) - const isOwner = styleRecord.userId === ctx.session.user.id; - const isPublicOrUnlisted = ["public", "unlisted"].includes(styleRecord.visibility); - - if (!isOwner && !isPublicOrUnlisted) { - throw new TRPCError({ - code: "FORBIDDEN", - message: "You don't have access to this style", - }); - } - - await ctx.db - .update(user) - .set({ - activeStyleId: input.styleId, - }) - .where(eq(user.id, ctx.session.user.id)); - - return input.styleId; - }), - getPublicStyles: publicProcedure - .input( - paginationSchema.extend({ - featured: z.boolean().optional(), - sortBy: z.enum(["newest", "oldest"]).default("newest"), - }), - ) - .query(async ({ ctx, input }) => { - const { featured, sortBy, ...pagination } = input; - - // Build where condition - const whereCondition = - featured === true - ? and(eq(style.visibility, "public"), eq(style.isFeatured, true)) - : eq(style.visibility, "public"); - - const styles = await ctx.db.query.style.findMany({ - where: whereCondition, - orderBy: (s, { desc, asc }) => [sortBy === "oldest" ? asc(s.createdAt) : desc(s.createdAt)], - limit: pagination.limit, - offset: pagination.offset, - with: { - user: { - columns: { - username: true, - image: true, - }, - }, - }, - }); - - return styles; - }), - getMyStyles: protectedProcedure.input(paginationSchema).query(async ({ ctx, input }) => { - const styles = await ctx.db.query.style.findMany({ - where: eq(style.userId, ctx.session.user.id), - orderBy: (s, { desc }) => [desc(s.updatedAt)], - limit: input.limit, - offset: input.offset, - with: { - user: { - columns: { - username: true, - image: true, - }, - }, - }, - }); - - return styles; - }), - getById: publicProcedure.input(z.object({ id: uuidSchema })).query(async ({ ctx, input }) => { - const styleRecord = await ctx.db.query.style.findFirst({ - where: eq(style.id, input.id), - with: { - user: { - columns: { - username: true, - image: true, - }, - }, - }, - }); - - if (!styleRecord) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Style not found", - }); - } - - // Check access permissions - const isOwner = ctx.session?.user.id === styleRecord.userId; - const isPublicOrUnlisted = ["public", "unlisted"].includes(styleRecord.visibility); - - if (!isOwner && !isPublicOrUnlisted) { - throw new TRPCError({ - code: "FORBIDDEN", - message: "You don't have access to this private style", - }); - } - - return styleRecord; - }), - getBySlug: publicProcedure.input(z.object({ slug: z.string().min(1) })).query(async ({ ctx, input }) => { - const parts = input.slug.split("/"); - - // biome-ignore lint/suspicious/noImplicitAnyLet: styleRecord is conditionally assigned based on slug format - let styleRecord; - - if (parts.length === 2) { - // Format: "username/stylename" - const [username, styleName] = parts; - - if (!username || !styleName) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Invalid slug format", - }); - } - - // Find user - const targetUser = await ctx.db.query.user.findFirst({ - where: eq(user.username, username), - }); - - if (!targetUser) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "User not found", - }); - } - - // Find style - styleRecord = await ctx.db.query.style.findFirst({ - where: and(eq(style.userId, targetUser.id), eq(style.name, styleName)), - with: { - user: { - columns: { - username: true, - image: true, - }, - }, - }, - }); - } else if (parts.length === 1) { - // Format: "stylename" (public styles only) - styleRecord = await ctx.db.query.style.findFirst({ - where: and(eq(style.name, input.slug), eq(style.visibility, "public")), - with: { - user: { - columns: { - username: true, - image: true, - }, - }, - }, - }); - } else { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Invalid slug format. Use 'username/stylename' or 'stylename' for public styles", - }); - } - - if (!styleRecord) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Style not found", - }); - } - - // Check access permissions - const isOwner = ctx.session?.user.id === styleRecord.userId; - const isPublicOrUnlisted = ["public", "unlisted"].includes(styleRecord.visibility); - - if (!isOwner && !isPublicOrUnlisted) { - throw new TRPCError({ - code: "FORBIDDEN", - message: "You don't have access to this private style", - }); - } - - return styleRecord; - }), - create: protectedProcedure.input(createStyleSchema).mutation(async ({ ctx, input }) => { - // Ensure public style names are globally unique - if (input.visibility === "public") { - const existingPublic = await ctx.db.query.style.findFirst({ - where: and(eq(style.name, input.name), eq(style.visibility, "public")), - }); - - if (existingPublic) { - throw new TRPCError({ - code: "CONFLICT", - message: `Public style name '${input.name}' is already taken. Please choose another name.`, - }); - } - } - - // Ensure style names are unique per user regardless of visibility - const existing = await ctx.db.query.style.findFirst({ - where: and(eq(style.userId, ctx.session.user.id), eq(style.name, input.name)), - }); - - if (existing) { - throw new TRPCError({ - code: "CONFLICT", - message: ` Style ${input.name} already exists, please use a new name.`, - }); - } - - const [created] = await ctx.db - .insert(style) - .values({ - ...input, - userId: ctx.session.user.id, - }) - .returning(); - - return created; - }), - update: protectedProcedure.input(updateStyleConfigSchema).mutation(async ({ ctx, input }) => { - return await ctx.db.transaction(async (tx) => { - const existingStyle = await tx.query.style.findFirst({ - where: eq(style.id, input.id), - }); - - if (!existingStyle) { - throw new TRPCError({ - code: "NOT_FOUND", - message: `Style with ID '${input.id}' not found.`, - }); - } - - if (existingStyle.userId !== ctx.session.user.id) { - throw new TRPCError({ - code: "FORBIDDEN", - message: "You can only update your own styles.", - }); - } - - const [updatedStyle] = await tx - .update(style) - .set({ - config: input.config, - updatedAt: new Date(), - }) - .where(eq(style.id, input.id)) - .returning(); - - return updatedStyle; - }); - }), - delete: protectedProcedure.input(z.object({ id: uuidSchema })).mutation(async ({ ctx, input }) => { - return await ctx.db.transaction(async (tx) => { - const styleRecord = await tx.query.style.findFirst({ - where: eq(style.id, input.id), - }); - - if (!styleRecord) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Style not found", - }); - } - - if (styleRecord.userId !== ctx.session.user.id) { - throw new TRPCError({ - code: "FORBIDDEN", - message: "You can only delete your own styles", - }); - } - - // Prevent deleting active style - if (ctx.session.user.activeStyleId === input.id) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Cannot delete your active style. Please set a different active style first.", - }); - } - - await tx.delete(style).where(eq(style.id, input.id)); - - return { success: true }; - }); - }), -} satisfies TRPCRouterRecord; diff --git a/packages/api/src/trpc.ts b/packages/api/src/trpc.ts deleted file mode 100644 index 783d0bb4a..000000000 --- a/packages/api/src/trpc.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { initTRPC, TRPCError } from "@trpc/server"; -import superjson from "superjson"; -import { ZodError, z } from "zod"; - -import { db } from "@dotui/db/client"; -import type { Auth, Session } from "@dotui/auth"; - -/** CONTEXT **/ -export const createTRPCContext = async (opts: { - headers: Headers; - auth: Auth; -}): Promise<{ - authApi: Auth["api"]; - session: Session | null; - db: typeof db; -}> => { - const authApi = opts.auth.api; - const session = await authApi.getSession({ - headers: opts.headers, - }); - return { - authApi, - session, - db, - }; -}; - -/** INITIALIZATION **/ -const t = initTRPC.context().create({ - transformer: superjson, - errorFormatter: ({ shape, error }) => ({ - ...shape, - data: { - ...shape.data, - zodError: - error.cause instanceof ZodError ? z.flattenError(error.cause as ZodError>) : null, - }, - }), -}); - -/** ROUTER **/ -export const createTRPCRouter = t.router; - -/** CALLER **/ -export const createCallerFactory = t.createCallerFactory; - -/** PROCEDURES **/ -export const publicProcedure = t.procedure; - -export const protectedProcedure = t.procedure.use(({ ctx, next }) => { - if (!ctx.session?.user) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - return next({ - ctx: { - session: { ...ctx.session, user: ctx.session.user }, - }, - }); -}); - -export const adminProcedure = protectedProcedure.use(({ ctx, next }) => { - if (ctx.session.user.role !== "admin") { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - return next({ - ctx: { - session: ctx.session, - }, - }); -}); diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json deleted file mode 100644 index b2cd12aac..000000000 --- a/packages/api/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "@dotui/ts-config/internal-package.json", - "compilerOptions": { - "baseUrl": ".", - "jsx": "preserve", - "paths": { - "@dotui/api/*": ["./src/*"], - "@dotui/core/*": ["../core/src/*"] - } - }, - "include": ["src"], - "exclude": ["node_modules"] -} diff --git a/packages/auth/biome.json b/packages/auth/biome.json deleted file mode 100644 index 54d045b56..000000000 --- a/packages/auth/biome.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", - "root": false, - "extends": ["@dotui/biome-config/base"] -} diff --git a/packages/auth/env.ts b/packages/auth/env.ts deleted file mode 100644 index 59f6a7b30..000000000 --- a/packages/auth/env.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { createEnv } from "@t3-oss/env-nextjs"; -import { z } from "zod"; - -export function authEnv() { - return createEnv({ - server: { - GITHUB_CLIENT_ID: z.string().min(1), - GITHUB_CLIENT_SECRET: z.string().min(1), - AUTH_SECRET: process.env.NODE_ENV === "production" ? z.string().min(1) : z.string().min(1).optional(), - NODE_ENV: z.enum(["development", "production"]).optional(), - }, - experimental__runtimeEnv: {}, - skipValidation: !!process.env.CI || process.env.npm_lifecycle_event === "lint", - }); -} diff --git a/packages/auth/package.json b/packages/auth/package.json deleted file mode 100644 index 08a7a5bac..000000000 --- a/packages/auth/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "@dotui/auth", - "version": "0.1.0", - "private": true, - "type": "module", - "exports": { - ".": "./src/index.ts", - "./middleware": "./src/middleware.ts", - "./client": "./src/client.ts", - "./env": "./env.ts" - }, - "license": "MIT", - "scripts": { - "clean": "git clean -xdf .cache .turbo dist node_modules", - "generate": "dotenv -e ../../.env -- pnpx @better-auth/cli generate --config ./src/index.ts --output ../db/src/auth-schema.ts", - "typecheck": "tsc --noEmit" - }, - "dependencies": { - "@dotui/db": "workspace:*", - "@t3-oss/env-nextjs": "^0.13.6", - "better-auth": "^1.4.10", - "next": "^16.0.5", - "react": "^19.2.1", - "react-dom": "^19.2.1", - "zod": "^4.2.1" - }, - "devDependencies": { - "@dotui/biome-config": "workspace:*", - "@dotui/ts-config": "workspace:*", - "@types/react": "^19.2.2", - "typescript": "^5.8.3" - } -} diff --git a/packages/auth/src/index.ts b/packages/auth/src/index.ts deleted file mode 100644 index f8b9dd6e1..000000000 --- a/packages/auth/src/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { betterAuth } from "better-auth"; -import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { oAuthProxy } from "better-auth/plugins"; -import type { BetterAuthOptions, BetterAuthPlugin } from "better-auth"; - -import { db } from "@dotui/db/client"; - -export function initAuth(options: { - baseUrl: string; - productionUrl: string; - secret: string | undefined; - githubClientId: string; - githubClientSecret: string; - extraPlugins?: TExtraPlugins; -}) { - const config = { - database: drizzleAdapter(db, { - provider: "pg", - }), - baseURL: options.baseUrl, - secret: options.secret, - user: { - additionalFields: { - username: { - type: "string", - required: false, - }, - activeStyleId: { - type: "string", - required: false, - }, - role: { - type: "string", - required: false, - defaultValue: "user", - input: false, - }, - banned: { - type: "boolean", - required: false, - defaultValue: false, - input: false, - }, - banReason: { - type: "string", - required: false, - input: false, - }, - banExpires: { - type: "date", - required: false, - input: false, - }, - }, - }, - plugins: [ - oAuthProxy({ - currentURL: options.baseUrl, - productionURL: options.productionUrl, - }), - ...(options.extraPlugins ?? []), - ], - socialProviders: { - github: { - clientId: options.githubClientId, - clientSecret: options.githubClientSecret, - redirectURI: `${options.productionUrl}/api/auth/callback/github`, - mapProfileToUser: (profile) => { - return { - username: profile.login, - }; - }, - }, - }, - } satisfies BetterAuthOptions; - - return betterAuth(config); -} - -export type Auth = ReturnType; -export type Session = Auth["$Infer"]["Session"]; diff --git a/packages/auth/tsconfig.json b/packages/auth/tsconfig.json deleted file mode 100644 index 2f198af5a..000000000 --- a/packages/auth/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "@dotui/ts-config/base.json", - "compilerOptions": { - "baseUrl": ".", - "paths": { - "@dotui/core/*": ["../core/src/*"] - } - }, - "include": ["src", "*.ts", "eslint.config.js"], - "exclude": ["node_modules"] -} diff --git a/packages/colors/biome.json b/packages/colors/biome.json index d1d335be7..3a42c374a 100644 --- a/packages/colors/biome.json +++ b/packages/colors/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json", "root": false, "extends": ["@dotui/biome-config/base"], "linter": { diff --git a/packages/core/biome.json b/packages/core/biome.json deleted file mode 100644 index 54d045b56..000000000 --- a/packages/core/biome.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", - "root": false, - "extends": ["@dotui/biome-config/base"] -} diff --git a/packages/core/package.json b/packages/core/package.json deleted file mode 100644 index 097df43b1..000000000 --- a/packages/core/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "@dotui/core", - "private": true, - "version": "0.0.0", - "type": "module", - "license": "MIT", - "main": "./src/index.ts", - "types": "./src/index.ts", - "exports": { - ".": "./src/index.ts", - "./schemas": "./src/schemas/index.ts", - "./types": "./src/types.ts", - "./react": "./src/react/index.ts", - "./react/style-provider": "./src/react/style-provider.tsx", - "./react/dynamic-component": "./src/react/dynamic-component.tsx", - "./shadcn": "./src/shadcn/index.ts", - "./__registry__/variants": "./src/__registry__/variants.ts" - }, - "scripts": { - "clean": "git clean -xdf .cache .turbo dist node_modules", - "typecheck": "tsc --noEmit --emitDeclarationOnly false" - }, - "peerDependencies": { - "react": "^19.2.1", - "react-dom": "^19.2.1" - }, - "dependencies": { - "@dotui/colors": "workspace:*", - "@dotui/types": "workspace:*", - "shadcn": "^3.6.1", - "zod": "^4.2.1" - }, - "devDependencies": { - "@dotui/biome-config": "workspace:*", - "@dotui/ts-config": "workspace:*", - "@types/node": "^22.15.3", - "@types/react": "^19.2.2", - "typescript": "^5.8.3" - } -} diff --git a/packages/core/src/__registry__/base.ts b/packages/core/src/__registry__/base.ts deleted file mode 100644 index e168a2a67..000000000 --- a/packages/core/src/__registry__/base.ts +++ /dev/null @@ -1,25 +0,0 @@ -// AUTO-GENERATED - DO NOT EDIT -// Run "pnpm build" to regenerate - -export const base = [ - { - name: "base", - type: "registry:style", - dependencies: [ - "tailwind-variants", - "clsx", - "tailwind-merge", - "react-aria-components", - "tailwindcss-react-aria-components", - "tw-animate-css", - "tailwindcss-autocontrast", - ], - registryDependencies: ["utils", "focus-styles", "theme"], - extends: "none", - css: { - "@plugin tailwindcss-react-aria-components": {}, - "@plugin tailwindcss-autocontrast": {}, - }, - files: [], - }, -] as const; diff --git a/packages/core/src/__registry__/blocks.ts b/packages/core/src/__registry__/blocks.ts deleted file mode 100644 index 336c2ac3d..000000000 --- a/packages/core/src/__registry__/blocks.ts +++ /dev/null @@ -1,1129 +0,0 @@ -// AUTO-GENERATED - DO NOT EDIT -// Run "pnpm build" to regenerate - -export const blocksCategories = [ - { - name: "Featured", - slug: "featured", - }, -] as const; - -export const blocks = [ - { - name: "login", - type: "registry:block", - dependencies: ["@internationalized/date"], - registryDependencies: ["button", "text-field", "card", "link"], - description: "A simple login form.", - categories: ["featured", "authentication"], - meta: { - containerHeight: 600, - }, - files: [ - { - type: "registry:page", - path: "blocks/auth/login/page.tsx", - target: "app/login/page.tsx", - content: `import { LoginForm } from "@dotui/registry/blocks/auth/login/components/login-form"; - -export default function Page() { - return ( -
- -
- ); -} -`, - }, - { - type: "registry:component", - path: "blocks/auth/login/components/login-form.tsx", - target: "blocks/auth/login/components/login-form.tsx", - content: `"use client"; - -import { cn } from "@dotui/registry/lib/utils"; -import { Button } from "@dotui/registry/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@dotui/registry/ui/card"; -import { Label } from "@dotui/registry/ui/field"; -import { Input } from "@dotui/registry/ui/input"; -import { Link } from "@dotui/registry/ui/link"; -import { TextField } from "@dotui/registry/ui/text-field"; - -export function LoginForm(props: React.ComponentProps<"div">) { - return ( - - - Login to your account - Enter your email below to login to your account - - -
- - - -
-
-
- -
-
- Or -
-
- - - - - -

- Don't have an account?{" "} - - register - -

-
-
- ); -} -`, - }, - ], - }, - { - name: "cards", - type: "registry:block", - registryDependencies: ["all"], - description: "A set of cards.", - categories: ["featured", "showcase"], - meta: { - containerHeight: 600, - }, - files: [ - { - type: "registry:component", - path: "blocks/showcase/cards/components/cards.tsx", - target: "blocks/showcase/cards/components/cards.tsx", - content: `import { AccountMenu } from "@dotui/registry/blocks/showcase/cards/components/account-menu"; -import { Backlog } from "@dotui/registry/blocks/showcase/cards/components/backlog"; -import { Booking } from "@dotui/registry/blocks/showcase/cards/components/booking"; -import { ColorEditorCard } from "@dotui/registry/blocks/showcase/cards/components/color-editor"; -import { Filters } from "@dotui/registry/blocks/showcase/cards/components/filters"; -import { InviteMembers } from "@dotui/registry/blocks/showcase/cards/components/invite-members"; -import { LoginForm } from "@dotui/registry/blocks/showcase/cards/components/login-form"; -import { Notifications } from "@dotui/registry/blocks/showcase/cards/components/notifications"; -import { TeamName } from "@dotui/registry/blocks/showcase/cards/components/team-name"; -import { cn } from "@dotui/registry/lib/utils"; - -export function Cards(props: React.ComponentProps<"div">) { - return ( -
-
- - -
- - - - -
- - -
- - -
- -
-
- ); -} - -export default Cards; -`, - }, - { - type: "registry:component", - path: "blocks/showcase/cards/components/account-menu.tsx", - target: "blocks/showcase/cards/components/account-menu.tsx", - content: `"use client"; - -import { BookIcon, ContrastIcon, LanguagesIcon, LogOutIcon, SettingsIcon, User2Icon, Users2Icon } from "lucide-react"; - -import { cn } from "@dotui/registry/lib/utils"; -import { Avatar, AvatarFallback, AvatarImage } from "@dotui/registry/ui/avatar"; -import { Card, CardContent, CardHeader } from "@dotui/registry/ui/card"; -import { ListBox, ListBoxItem, ListBoxSection, ListBoxSectionHeader } from "@dotui/registry/ui/list-box"; -import { Separator } from "@dotui/registry/ui/separator"; - -export function AccountMenu({ className, ...props }: React.ComponentProps<"div">) { - return ( - - - - - M - -
-

mehdibha

-

- hello@mehdibha.com -

-
-
- - - - - Profile - - - - Settings - - - - Documentation - - - - Community - - - - Preferences - - - Theme - - - - Language - - - - - - Log out - - - -
- ); -} -`, - }, - { - type: "registry:component", - path: "blocks/showcase/cards/components/backlog.tsx", - target: "blocks/showcase/cards/components/backlog.tsx", - content: `"use client"; - -import { RiCheckboxCircleFill, RiProgress4Line, RiProgress6Line } from "@remixicon/react"; -import { AlertTriangle, Circle, CircleDashedIcon, MoreHorizontal, Settings2Icon, Zap } from "lucide-react"; - -import { Badge } from "@dotui/registry/ui/badge"; -import { Button } from "@dotui/registry/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@dotui/registry/ui/card"; -import { Input } from "@dotui/registry/ui/input"; -import { Menu, MenuContent, MenuItem } from "@dotui/registry/ui/menu"; -import { Overlay } from "@dotui/registry/ui/overlay"; -import { SearchField } from "@dotui/registry/ui/search-field"; -import { Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from "@dotui/registry/ui/table"; - -export const statuses = [ - { - value: "draft", - label: "Draft", - variant: "default", - icon: CircleDashedIcon, - }, - { - value: "in progress", - label: "In progress", - variant: "info", - icon: RiProgress4Line, - }, - { - value: "in review", - label: "In review", - variant: "warning", - icon: RiProgress6Line, - }, - { - value: "done", - label: "Done", - variant: "success", - icon: RiCheckboxCircleFill, - }, -] as const; - -export const priorities = [ - { - value: "P0", - label: "P0", - variant: "danger", - icon: AlertTriangle, - }, - { - value: "P1", - label: "P1", - variant: "warning", - icon: Zap, - }, - { - value: "P2", - label: "P2", - variant: "info", - icon: Circle, - }, - { - value: "P3", - label: "P3", - variant: "default", - icon: Circle, - }, -] as const; - -export const types = [ - { - value: "feature", - label: "Feature", - variant: "info", - }, - { - value: "bug", - label: "Bug", - variant: "danger", - }, - { - value: "tech debt", - label: "Tech Debt", - variant: "warning", - }, - { - value: "spike", - label: "Spike", - variant: "info", - }, - { - value: "chore", - label: "Chore", - variant: "neutral", - }, - { - value: "performance", - label: "Performance", - variant: "success", - }, -] as const; - -interface Column { - id: keyof Omit | "actions"; - name: string; - isRowHeader?: boolean; -} - -const columns: Column[] = [ - { name: "Title", id: "title", isRowHeader: true }, - { name: "Priority", id: "priority" }, - { name: "Status", id: "status" }, - { name: "Assignee", id: "assignee" }, - { name: "Story Points", id: "storyPoints" }, - { name: "", id: "actions" }, -]; - -interface Item { - id: number; - title: string; - priority: string; - status: string; - assignee: string; - storyPoints: string; - type: string; -} - -interface User { - username: string; - name: string; - avatar: string; -} - -export const users: User[] = [ - { - username: "shadcn", - name: "shadcn", - avatar: "https://github.com/shadcn.png", - }, - { - username: "tannerlinsley", - name: "Tanner Linsley", - avatar: "https://github.com/tannerlinsley.png", - }, - { - username: "t3dotgg", - name: "Theo Browne", - avatar: "https://github.com/t3dotgg.png", - }, - { - username: "rauchg", - name: "Guillermo Rauch", - avatar: "https://github.com/rauchg.png", - }, - { - username: "leerob", - name: "Lee Robinson", - avatar: "https://github.com/leerob.png", - }, - { - username: "steventey", - name: "Steven Tey", - avatar: "https://github.com/steventey.png", - }, -] as const; - -const data: Item[] = [ - { - id: 1, - title: "Refactor AuthProvider to support SSO + 2FA", - priority: "P0", - status: "in progress", - assignee: "shadcn", - storyPoints: "13", - type: "feature", - }, - { - id: 2, - title: "Fix race condition in payment webhooks", - priority: "P1", - status: "in review", - assignee: "tannerlinsley", - storyPoints: "5", - type: "bug", - }, - { - id: 3, - title: "Migrate legacy API from REST to GraphQL", - priority: "P2", - status: "draft", - assignee: "t3dotgg", - storyPoints: "21", - type: "tech debt", - }, - { - id: 4, - title: "Add Storybook stories for Button variants", - priority: "P3", - status: "done", - assignee: "rauchg", - storyPoints: "3", - type: "chore", - }, - { - id: 5, - title: "Spike: Evaluate Redis vs Kafka for event streaming", - priority: "P2", - status: "in progress", - assignee: "leerob", - storyPoints: "8", - type: "spike", - }, - { - id: 6, - title: "Implement lazy loading for ProductGrid component", - priority: "P1", - status: "draft", - assignee: "steventey", - storyPoints: "5", - type: "performance", - }, -]; - -export function Backlog(props: React.ComponentProps<"div">) { - return ( - - - Backlog - Here's a list of your tasks for this month. - - -
-
- - - -
-
- - -
-
- - - {(column) => {column.name}} - - - {(item) => ( - - - {() => { - const type = types.find((t) => t.value === item.type); - if (!type) return null; - return ( -
- {type?.label || item.type} - {item.title} -
- ); - }} -
- - {(() => { - const priority = priorities.find((p) => p.value === item.priority); - if (!priority) return null; - return {priority.label}; - })()} - - - {(() => { - const status = statuses.find((s) => s.value === item.status); - if (!status) return null; - const StatusIcon = status.icon || Circle; - return ( - - - {status?.label || item.status} - - ); - })()} - - - {(() => { - const user = users.find((u) => u.username === item.assignee); - if (!user) return null; - return ( -
- {user.name} - {user.name} -
- ); - })()} -
- - {item.storyPoints} - - - - - - - Edit - Duplicate - Archive - Delete - - - - -
- )} -
-
-
-
- ); -} -`, - }, - { - type: "registry:component", - path: "blocks/showcase/cards/components/booking.tsx", - target: "blocks/showcase/cards/components/booking.tsx", - content: `"use client"; - -import { parseDate } from "@internationalized/date"; - -import { cn } from "@dotui/registry/lib/utils"; -import { Button } from "@dotui/registry/ui/button"; -import { Calendar } from "@dotui/registry/ui/calendar"; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@dotui/registry/ui/card"; -import { Label } from "@dotui/registry/ui/field"; -import { Input } from "@dotui/registry/ui/input"; -import { TimeField } from "@dotui/registry/ui/time-field"; - -export function Booking({ className, ...props }: React.ComponentProps<"div">) { - return ( - - - Booking - Pick a time for your meeting. - - - -
- - - - - - - - -
-
- - - - -
- ); -} -`, - }, - { - type: "registry:component", - path: "blocks/showcase/cards/components/color-editor.tsx", - target: "blocks/showcase/cards/components/color-editor.tsx", - content: `"use client"; - -import { cn } from "@dotui/registry/lib/utils"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@dotui/registry/ui/card"; -import { ColorEditor } from "@dotui/registry/ui/color-editor"; - -export function ColorEditorCard({ className, ...props }: React.ComponentProps<"div">) { - return ( - - - Accent color - Edit the accent color of the app. - - - - - - ); -} -`, - }, - { - type: "registry:component", - path: "blocks/showcase/cards/components/filters.tsx", - target: "blocks/showcase/cards/components/filters.tsx", - content: `"use client"; - -// import { ZapIcon } from "lucide-react"; -import { cn } from "@dotui/registry/lib/utils"; -import { Button } from "@dotui/registry/ui/button"; -import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@dotui/registry/ui/card"; -import { Description, Label } from "@dotui/registry/ui/field"; -import { Slider, SliderControl, SliderOutput } from "@dotui/registry/ui/slider"; -import { Switch } from "@dotui/registry/ui/switch"; -import { Tag, TagGroup, TagList } from "@dotui/registry/ui/tag-group"; -import { ToggleButton } from "@dotui/registry/ui/toggle-button"; -import { ToggleButtonGroup } from "@dotui/registry/ui/toggle-button-group"; - -export function Filters({ className, ...props }: React.ComponentProps<"div">) { - return ( - - - Filters - - -
- - - Any type - Room - Entire home - -
- -
- - -
- - Trip price, includes all fees -
- - - - Wifi - TV - Kitchen - Pool - Washer - Dryer - Heating - Hair dryer - EV charger - Gym - BBQ grill - Breakfast - - - - - {/* */} - Instant booking - - -
- - - - -
- ); -} -`, - }, - { - type: "registry:component", - path: "blocks/showcase/cards/components/invite-members.tsx", - target: "blocks/showcase/cards/components/invite-members.tsx", - content: `"use client"; - -import { PlusCircleIcon } from "lucide-react"; - -import { ExternalLinkIcon } from "@dotui/registry/icons"; -import { Avatar, AvatarFallback, AvatarImage } from "@dotui/registry/ui/avatar"; -import { Button } from "@dotui/registry/ui/button"; -import { - Card, - CardAction, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@dotui/registry/ui/card"; -import { Label } from "@dotui/registry/ui/field"; -import { Input } from "@dotui/registry/ui/input"; -import { Select, SelectContent, SelectItem, SelectTrigger } from "@dotui/registry/ui/select"; -import { Separator } from "@dotui/registry/ui/separator"; -import { TextField } from "@dotui/registry/ui/text-field"; - -const teamMembers = [ - { - name: "shadcn", - email: "shadcn@vercel.com", - avatar: "https://github.com/shadcn.png", - role: "owner", - }, - { - name: "rauchg", - email: "rauchg@vercel.com", - avatar: "https://github.com/rauchg.png", - role: "member", - }, - { - name: "Lee Robinson", - email: "lee@cursor.com", - avatar: "https://github.com/leerob.png", - role: "member", - }, -]; - -export function InviteMembers(props: React.ComponentProps<"div">) { - return ( - - - Invite Members - Collaborate with members on this project. - - - - - - -
-
- - - - - -
- -
-

Team members

-
- {teamMembers.map((member) => ( -
-
- - - {member.name.charAt(0)} - -
-

{member.name}

-

{member.role}

-
-
- -
- ))} -
-
-
-
- - -

- Learn more about{" "} - - inviting members - - . -

- -
-
- ); -} -`, - }, - { - type: "registry:component", - path: "blocks/showcase/cards/components/login-form.tsx", - target: "blocks/showcase/cards/components/login-form.tsx", - content: `"use client"; - -import { cn } from "@dotui/registry/lib/utils"; -import { Button } from "@dotui/registry/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@dotui/registry/ui/card"; -import { Label } from "@dotui/registry/ui/field"; -import { Input } from "@dotui/registry/ui/input"; -import { Link } from "@dotui/registry/ui/link"; -import { TextField } from "@dotui/registry/ui/text-field"; - -export function LoginForm(props: React.ComponentProps<"div">) { - return ( - - - Login to your account - Enter your email below to login to your account - - -
- - - -
-
-
- -
-
- Or -
-
- - - - - -

- {/* TODO */} - Don't have an account?{" "} - - register - -

-
-
- ); -} -`, - }, - { - type: "registry:component", - path: "blocks/showcase/cards/components/notifications.tsx", - target: "blocks/showcase/cards/components/notifications.tsx", - content: `import React from "react"; - -import { cn } from "@dotui/registry/lib/utils"; -import { Avatar, AvatarFallback, AvatarImage } from "@dotui/registry/ui/avatar"; -import { Badge } from "@dotui/registry/ui/badge"; -import { Button } from "@dotui/registry/ui/button"; -import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@dotui/registry/ui/card"; -import { ListBox, ListBoxItem } from "@dotui/registry/ui/list-box"; -import { Separator } from "@dotui/registry/ui/separator"; -import { Tab, TabList, TabPanel, Tabs } from "@dotui/registry/ui/tabs"; - -export function Notifications({ className, ...props }: React.ComponentProps<"div">) { - return ( - - - - Notifications - 12 - - - - - - - - - All - Unread - Read - - {["all", "unread", "read"].map((tab) => ( - - - {notifications - .filter((notification) => { - if (tab === "all") return true; - if (tab === "unread") return !notification.read; - if (tab === "read") return notification.read; - return false; - }) - .map((notification, index) => ( - - - -
- - - - {notification.user.name - .split(" ") - .map((n) => n[0]) - .join("")} - - -
-

- {notification.user.name}{" "} - {notification.content ? notification.content : {notification.text}} -

-
-

{notification.timestamp}

- {notification.action && ( -
- -
- )} -
-
-
-
-
- ))} -
-
- ))} -
-
-
- ); -} - -const notifications = [ - { - user: { - name: "Guillermo Rauch", - avatar: "https://avatars.githubusercontent.com/rauchg", - }, - text: "starred your repository dotUI.", - content: ( - <> - starred mehdibha/dotUI. - - ), - read: false, - timestamp: "2 hours ago", - }, - { - user: { - name: "Lee Robinson", - avatar: "https://avatars.githubusercontent.com/leerob", - }, - text: "invited you to the Vercel GitHub organization.", - content: ( - <> - invited you to join Cursor on GitHub. - - ), - read: false, - action: { label: "View invite" }, - timestamp: "7 hours ago", - }, - { - user: { - name: "Tim Neutkens", - avatar: "https://avatars.githubusercontent.com/timneutkens", - }, - text: "published a new release v14.2.0-canary on vercel/next.js.", - content: ( - <> - published v14.2.0-canary on - vercel/next.js. - - ), - read: false, - action: { label: "See release" }, - timestamp: "Yesterday", - }, - { - user: { - name: "Steven Tey", - avatar: "https://avatars.githubusercontent.com/steven-tey", - }, - text: "opened a pull request: Improve docs.", - content: ( - <> - opened PR: Improve docs in - mehdibha/dotUI. - - ), - read: true, - action: { label: "Review PR" }, - timestamp: "Yesterday", - }, - { - user: { - name: "Shu Ding", - avatar: "https://avatars.githubusercontent.com/shuding", - }, - text: "starred your repository dotUI.", - content: ( - <> - starred mehdibha/dotUI. - - ), - read: true, - timestamp: "2 days ago", - }, - { - user: { - name: "Delba de Oliveira", - avatar: "https://avatars.githubusercontent.com/delbaoliveira", - }, - text: "commented on issue: Add theme presets.", - content: ( - <> - commented on #128: - Add theme presets. - - ), - read: false, - action: { label: "Reply" }, - timestamp: "3 days ago", - }, -]; -`, - }, - { - type: "registry:component", - path: "blocks/showcase/cards/components/team-name.tsx", - target: "blocks/showcase/cards/components/team-name.tsx", - content: `"use client"; - -import { cn } from "@dotui/registry/lib/utils"; -import { Button } from "@dotui/registry/ui/button"; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@dotui/registry/ui/card"; -import { Input } from "@dotui/registry/ui/input"; -import { TextField } from "@dotui/registry/ui/text-field"; - -export function TeamName({ className, ...props }: React.ComponentProps<"div">) { - return ( - - - Team Name - - This is your team's visible name within the platform. For example, the name of your company or department. - - - - - - - - -

Please use 32 characters at maximum.

- -
-
- ); -} -`, - }, - ], - }, -] as const; diff --git a/packages/core/src/__registry__/hooks.ts b/packages/core/src/__registry__/hooks.ts deleted file mode 100644 index a38f5d889..000000000 --- a/packages/core/src/__registry__/hooks.ts +++ /dev/null @@ -1,36 +0,0 @@ -// AUTO-GENERATED - DO NOT EDIT -// Run "pnpm build" to regenerate - -export const hooks = [ - { - name: "use-mobile", - type: "registry:hook", - files: [ - { - type: "registry:hook", - path: "hooks/use-mobile.ts", - target: "hooks/use-mobile.ts", - content: `import * as React from "react"; - -const MOBILE_BREAKPOINT = 768; - -export function useIsMobile() { - const [isMobile, setIsMobile] = React.useState(undefined); - - React.useEffect(() => { - const mql = window.matchMedia(\`(max-width: \${MOBILE_BREAKPOINT - 1}px)\`); - const onChange = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - }; - mql.addEventListener("change", onChange); - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - return () => mql.removeEventListener("change", onChange); - }, []); - - return !!isMobile; -} -`, - }, - ], - }, -] as const; diff --git a/packages/core/src/__registry__/icons.ts b/packages/core/src/__registry__/icons.ts deleted file mode 100644 index d0407bd6e..000000000 --- a/packages/core/src/__registry__/icons.ts +++ /dev/null @@ -1,976 +0,0 @@ -// AUTO-GENERATED - DO NOT EDIT -// Run "pnpm build" to regenerate - -export const iconLibraries = [ - { - name: "lucide", - label: "Lucide icons", - package: "lucide-react", - import: "lucide-react", - }, - { - name: "remix", - label: "Remix icons", - package: "@remixicon/react", - import: "@remixicon/react", - }, - { - name: "tabler", - label: "Tabler icons", - package: "@tabler/icons-react", - import: "@tabler/icons-react", - }, - { - name: "hugeicons", - label: "Huge icons", - package: "@hugeicons/react", - import: "@hugeicons/react", - }, -] as const; - -export type IconLibraryName = (typeof iconLibraries)[number]["name"]; - -export const icons = { - PlusIcon: { - lucide: "PlusIcon", - remix: "RiAddLine", - tabler: "IconPlus", - hugeicons: "PlusSignIcon", - }, - PaperclipIcon: { - lucide: "PaperclipIcon", - remix: "RiAttachmentLine", - tabler: "IconPaperclip", - hugeicons: "AttachmentIcon", - }, - SparklesIcon: { - lucide: "SparklesIcon", - remix: "RiSparklingLine", - tabler: "IconSparkles", - hugeicons: "SparklesIcon", - }, - ShoppingBagIcon: { - lucide: "ShoppingBagIcon", - remix: "RiShoppingBagLine", - tabler: "IconShoppingBag", - hugeicons: "ShoppingBag01Icon", - }, - WandIcon: { - lucide: "WandIcon", - remix: "RiMagicLine", - tabler: "IconWand", - hugeicons: "MagicWand01Icon", - }, - MousePointerIcon: { - lucide: "MousePointerIcon", - remix: "RiCursorLine", - tabler: "IconPointer", - hugeicons: "Cursor01Icon", - }, - MoreHorizontalIcon: { - lucide: "MoreHorizontalIcon", - remix: "RiMoreLine", - tabler: "IconDots", - hugeicons: "MoreHorizontalCircle01Icon", - }, - ShareIcon: { - lucide: "ShareIcon", - remix: "RiShareLine", - tabler: "IconShare", - hugeicons: "Share03Icon", - }, - BookOpenIcon: { - lucide: "BookOpenIcon", - remix: "RiBookOpenLine", - tabler: "IconBook", - hugeicons: "BookOpen01Icon", - }, - GlobeIcon: { - lucide: "GlobeIcon", - remix: "RiGlobalLine", - tabler: "IconWorld", - hugeicons: "GlobalIcon", - }, - PenToolIcon: { - lucide: "PenToolIcon", - remix: "RiPenNibLine", - tabler: "IconPencil", - hugeicons: "QuillWrite01Icon", - }, - AudioLinesIcon: { - lucide: "AudioLinesIcon", - remix: "RiSoundModuleLine", - tabler: "IconMicrophone", - hugeicons: "Mic01Icon", - }, - ArrowUpIcon: { - lucide: "ArrowUpIcon", - remix: "RiArrowUpLine", - tabler: "IconArrowUp", - hugeicons: "ArrowUp01Icon", - }, - ChevronDownIcon: { - lucide: "ChevronDownIcon", - remix: "RiArrowDownSLine", - tabler: "IconChevronDown", - hugeicons: "ArrowDown01Icon", - }, - SettingsIcon: { - lucide: "SettingsIcon", - remix: "RiSettings3Line", - tabler: "IconSettings", - hugeicons: "Settings01Icon", - }, - FolderIcon: { - lucide: "FolderIcon", - remix: "RiFolderLine", - tabler: "IconFolder", - hugeicons: "Folder01Icon", - }, - CircleCheckIcon: { - lucide: "CircleCheckIcon", - remix: "RiCheckboxCircleLine", - tabler: "IconCircleCheck", - hugeicons: "CheckmarkCircle02Icon", - }, - LightbulbIcon: { - lucide: "LightbulbIcon", - remix: "RiLightbulbLine", - tabler: "IconBulb", - hugeicons: "BulbIcon", - }, - ContainerIcon: { - lucide: "ContainerIcon", - remix: "RiBox3Line", - tabler: "IconBox", - hugeicons: "CubeIcon", - }, - ZapIcon: { - lucide: "ZapIcon", - remix: "RiFlashlightLine", - tabler: "IconBolt", - hugeicons: "ZapIcon", - }, - ServerIcon: { - lucide: "ServerIcon", - remix: "RiServerLine", - tabler: "IconServer", - hugeicons: "DatabaseIcon", - }, - InfoIcon: { - lucide: "InfoIcon", - remix: "RiInformationLine", - tabler: "IconInfoCircle", - hugeicons: "InformationCircleIcon", - }, - TerminalIcon: { - lucide: "TerminalIcon", - remix: "RiTerminalBoxLine", - tabler: "IconTerminal", - hugeicons: "SourceCodeIcon", - }, - CopyIcon: { - lucide: "CopyIcon", - remix: "RiFileCopyLine", - tabler: "IconCopy", - hugeicons: "Copy01Icon", - }, - MonitorIcon: { - lucide: "MonitorIcon", - remix: "RiComputerLine", - tabler: "IconDeviceDesktop", - hugeicons: "ComputerIcon", - }, - DownloadIcon: { - lucide: "DownloadIcon", - remix: "RiDownloadLine", - tabler: "IconDownload", - hugeicons: "Download01Icon", - }, - SearchIcon: { - lucide: "SearchIcon", - remix: "RiSearchLine", - tabler: "IconSearch", - hugeicons: "Search01Icon", - }, - UploadIcon: { - lucide: "UploadIcon", - remix: "RiUpload2Line", - tabler: "IconUpload", - hugeicons: "Upload01Icon", - }, - CloudCogIcon: { - lucide: "CloudCogIcon", - remix: "RiCloudLine", - tabler: "IconCloudCog", - hugeicons: "AiCloud01Icon", - }, - GitBranchIcon: { - lucide: "GitBranchIcon", - remix: "RiGitBranchLine", - tabler: "IconGitBranch", - hugeicons: "GitBranchIcon", - }, - BotIcon: { - lucide: "BotIcon", - remix: "RiRobotLine", - tabler: "IconRobot", - hugeicons: "RoboticIcon", - }, - SendIcon: { - lucide: "SendIcon", - remix: "RiSendPlaneLine", - tabler: "IconSend", - hugeicons: "SentIcon", - }, - MenuIcon: { - lucide: "MenuIcon", - remix: "RiMenuLine", - tabler: "IconMenu", - hugeicons: "Menu09Icon", - }, - XIcon: { - lucide: "XIcon", - remix: "RiCloseLine", - tabler: "IconX", - hugeicons: "Cancel01Icon", - }, - HomeIcon: { - lucide: "HomeIcon", - remix: "RiHomeLine", - tabler: "IconHome", - hugeicons: "Home01Icon", - }, - CircleIcon: { - lucide: "CircleIcon", - remix: "RiCircleLine", - tabler: "IconCircle", - hugeicons: "CircleIcon", - }, - LayoutGridIcon: { - lucide: "LayoutGridIcon", - remix: "RiLayoutGridLine", - tabler: "IconLayoutGrid", - hugeicons: "GridIcon", - }, - MailIcon: { - lucide: "MailIcon", - remix: "RiMailLine", - tabler: "IconMail", - hugeicons: "Mail01Icon", - }, - LinkIcon: { - lucide: "LinkIcon", - remix: "RiLinkM", - tabler: "IconLink", - hugeicons: "Link01Icon", - }, - SmileIcon: { - lucide: "SmileIcon", - remix: "RiEmotionLine", - tabler: "IconMoodSmile", - hugeicons: "SmileIcon", - }, - CircleAlertIcon: { - lucide: "CircleAlertIcon", - remix: "RiErrorWarningLine", - tabler: "IconExclamationCircle", - hugeicons: "Alert01Icon", - }, - UserIcon: { - lucide: "UserIcon", - remix: "RiUserLine", - tabler: "IconUser", - hugeicons: "UserIcon", - }, - StarIcon: { - lucide: "StarIcon", - remix: "RiStarLine", - tabler: "IconStar", - hugeicons: "StarIcon", - }, - CodeIcon: { - lucide: "CodeIcon", - remix: "RiCodeLine", - tabler: "IconCode", - hugeicons: "CodeIcon", - }, - HeartIcon: { - lucide: "HeartIcon", - remix: "RiHeartLine", - tabler: "IconHeart", - hugeicons: "FavouriteIcon", - }, - LogOutIcon: { - lucide: "LogOutIcon", - remix: "RiLogoutBoxLine", - tabler: "IconLogout", - hugeicons: "Logout01Icon", - }, - MinusIcon: { - lucide: "MinusIcon", - remix: "RiSubtractLine", - tabler: "IconMinus", - hugeicons: "MinusSignIcon", - }, - ArrowLeftIcon: { - lucide: "ArrowLeftIcon", - remix: "RiArrowLeftLine", - tabler: "IconArrowLeft", - hugeicons: "ArrowLeft01Icon", - }, - MailCheckIcon: { - lucide: "MailCheckIcon", - remix: "RiMailCheckLine", - tabler: "IconMailCheck", - hugeicons: "MailValidation01Icon", - }, - ArchiveIcon: { - lucide: "ArchiveIcon", - remix: "RiArchiveLine", - tabler: "IconArchive", - hugeicons: "Archive02Icon", - }, - ClockIcon: { - lucide: "ClockIcon", - remix: "RiTimeLine", - tabler: "IconClock", - hugeicons: "Clock01Icon", - }, - CalendarPlusIcon: { - lucide: "CalendarPlusIcon", - remix: "RiCalendarEventLine", - tabler: "IconCalendarPlus", - hugeicons: "CalendarAdd01Icon", - }, - ListFilterIcon: { - lucide: "ListFilterIcon", - remix: "RiFilterLine", - tabler: "IconFilterPlus", - hugeicons: "FilterIcon", - }, - TagIcon: { - lucide: "TagIcon", - remix: "RiPriceTag3Line", - tabler: "IconTag", - hugeicons: "Tag01Icon", - }, - Trash2Icon: { - lucide: "Trash2Icon", - remix: "RiDeleteBin6Line", - tabler: "IconTrash", - hugeicons: "Delete02Icon", - }, - ArrowRightIcon: { - lucide: "ArrowRightIcon", - remix: "RiArrowRightLine", - tabler: "IconArrowRight", - hugeicons: "ArrowRight02Icon", - }, - VolumeX: { - lucide: "VolumeX", - remix: "RiVolumeMuteLine", - tabler: "IconVolume", - hugeicons: "VolumeOffIcon", - }, - CheckIcon: { - lucide: "CheckIcon", - remix: "RiCheckLine", - tabler: "IconCheck", - hugeicons: "Tick02Icon", - }, - UserRoundXIcon: { - lucide: "UserRoundXIcon", - remix: "RiUserUnfollowLine", - tabler: "IconUserX", - hugeicons: "UserRemove01Icon", - }, - AlertTriangleIcon: { - lucide: "AlertTriangleIcon", - remix: "RiAlertLine", - tabler: "IconAlertTriangle", - hugeicons: "Alert02Icon", - }, - TrashIcon: { - lucide: "TrashIcon", - remix: "RiDeleteBinLine", - tabler: "IconTrash", - hugeicons: "Delete01Icon", - }, - BluetoothIcon: { - lucide: "BluetoothIcon", - remix: "RiBluetoothLine", - tabler: "IconBluetooth", - hugeicons: "BluetoothIcon", - }, - MoreVerticalIcon: { - lucide: "MoreVerticalIcon", - remix: "RiMore2Line", - tabler: "IconDotsVertical", - hugeicons: "MoreVerticalCircle01Icon", - }, - FileIcon: { - lucide: "FileIcon", - remix: "RiFileLine", - tabler: "IconFile", - hugeicons: "File01Icon", - }, - FolderOpenIcon: { - lucide: "FolderOpenIcon", - remix: "RiFolderOpenLine", - tabler: "IconFolderOpen", - hugeicons: "FolderOpenIcon", - }, - FileCodeIcon: { - lucide: "FileCodeIcon", - remix: "RiFileCodeLine", - tabler: "IconFileCode", - hugeicons: "CodeIcon", - }, - FolderSearchIcon: { - lucide: "FolderSearchIcon", - remix: "RiFileSearchLine", - tabler: "IconFolderSearch", - hugeicons: "Search01Icon", - }, - SaveIcon: { - lucide: "SaveIcon", - remix: "RiSaveLine", - tabler: "IconDeviceFloppy", - hugeicons: "FloppyDiskIcon", - }, - EyeIcon: { - lucide: "EyeIcon", - remix: "RiEyeLine", - tabler: "IconEye", - hugeicons: "EyeIcon", - }, - LayoutIcon: { - lucide: "LayoutIcon", - remix: "RiLayoutLine", - tabler: "IconLayout", - hugeicons: "Layout01Icon", - }, - PaletteIcon: { - lucide: "PaletteIcon", - remix: "RiPaletteLine", - tabler: "IconPalette", - hugeicons: "PaintBoardIcon", - }, - SunIcon: { - lucide: "SunIcon", - remix: "RiSunLine", - tabler: "IconSun", - hugeicons: "Sun01Icon", - }, - MoonIcon: { - lucide: "MoonIcon", - remix: "RiMoonLine", - tabler: "IconMoon", - hugeicons: "MoonIcon", - }, - HelpCircleIcon: { - lucide: "HelpCircleIcon", - remix: "RiQuestionLine", - tabler: "IconHelpCircle", - hugeicons: "HelpCircleIcon", - }, - FileTextIcon: { - lucide: "FileTextIcon", - remix: "RiFileTextLine", - tabler: "IconFileText", - hugeicons: "File01Icon", - }, - CalendarIcon: { - lucide: "CalendarIcon", - remix: "RiCalendarLine", - tabler: "IconCalendar", - hugeicons: "Calendar01Icon", - }, - Search: { - lucide: "Search", - remix: "RiSearchLine", - tabler: "IconSearch", - hugeicons: "Search01Icon", - }, - CheckCircle2Icon: { - lucide: "CheckCircle2Icon", - remix: "RiCheckboxCircleLine", - tabler: "IconCircleCheckFilled", - hugeicons: "CheckmarkCircle02Icon", - }, - CircleDollarSignIcon: { - lucide: "CircleDollarSignIcon", - remix: "RiMoneyDollarCircleLine", - tabler: "IconCoin", - hugeicons: "DollarCircleIcon", - }, - ArrowUpRightIcon: { - lucide: "ArrowUpRightIcon", - remix: "RiArrowRightUpLine", - tabler: "IconArrowUpRight", - hugeicons: "ArrowUpRight01Icon", - }, - BadgeCheck: { - lucide: "BadgeCheck", - remix: "RiVerifiedBadgeLine", - tabler: "IconRosetteDiscountCheck", - hugeicons: "CheckmarkBadge02Icon", - }, - ArrowLeftCircleIcon: { - lucide: "ArrowLeftCircleIcon", - remix: "RiArrowLeftCircleLine", - tabler: "IconCircleArrowLeft", - hugeicons: "CircleArrowLeft02Icon", - }, - FlipHorizontalIcon: { - lucide: "FlipHorizontalIcon", - remix: "RiFlipHorizontalLine", - tabler: "IconFlipHorizontal", - hugeicons: "FlipHorizontalIcon", - }, - FlipVerticalIcon: { - lucide: "FlipVerticalIcon", - remix: "RiFlipVerticalLine", - tabler: "IconFlipVertical", - hugeicons: "FlipVerticalIcon", - }, - RotateCwIcon: { - lucide: "RotateCwIcon", - remix: "RiRestartLine", - tabler: "IconRotateClockwise2", - hugeicons: "Rotate01Icon", - }, - Clock2Icon: { - lucide: "Clock2Icon", - remix: "RiTimeLine", - tabler: "IconClockHour2", - hugeicons: "Clock03Icon", - }, - CaptionsIcon: { - lucide: "CaptionsIcon", - remix: "RiClosedCaptioningLine", - tabler: "IconTextCaption", - hugeicons: "ClosedCaptionIcon", - }, - TrendingUpIcon: { - lucide: "TrendingUpIcon", - remix: "RiArrowRightUpLine", - tabler: "IconTrendingUp", - hugeicons: "Analytics01Icon", - }, - ChevronRightIcon: { - lucide: "ChevronRightIcon", - remix: "RiArrowRightSLine", - tabler: "IconChevronRight", - hugeicons: "ArrowRight01Icon", - }, - MinimizeIcon: { - lucide: "MinimizeIcon", - remix: "RiContractLeftRightLine", - tabler: "IconMinimize", - hugeicons: "MinusSignIcon", - }, - MaximizeIcon: { - lucide: "MaximizeIcon", - remix: "RiExpandLeftRightLine", - tabler: "IconMaximize", - hugeicons: "PlusSignIcon", - }, - CreditCardIcon: { - lucide: "CreditCardIcon", - remix: "RiBankCardLine", - tabler: "IconCreditCard", - hugeicons: "CreditCardIcon", - }, - CalculatorIcon: { - lucide: "CalculatorIcon", - remix: "RiCalculatorLine", - tabler: "IconCalculator", - hugeicons: "CalculatorIcon", - }, - InboxIcon: { - lucide: "InboxIcon", - remix: "RiInboxLine", - tabler: "IconArchive", - hugeicons: "Archive02Icon", - }, - FolderPlusIcon: { - lucide: "FolderPlusIcon", - remix: "RiFolderAddLine", - tabler: "IconFolderPlus", - hugeicons: "FolderAddIcon", - }, - ScissorsIcon: { - lucide: "ScissorsIcon", - remix: "RiScissorsLine", - tabler: "IconCut", - hugeicons: "ScissorIcon", - }, - ClipboardPasteIcon: { - lucide: "ClipboardPasteIcon", - remix: "RiClipboardLine", - tabler: "IconClipboard", - hugeicons: "ClipboardIcon", - }, - ListIcon: { - lucide: "ListIcon", - remix: "RiListUnordered", - tabler: "IconList", - hugeicons: "Menu05Icon", - }, - ZoomInIcon: { - lucide: "ZoomInIcon", - remix: "RiZoomInLine", - tabler: "IconZoomIn", - hugeicons: "ZoomInAreaIcon", - }, - ZoomOutIcon: { - lucide: "ZoomOutIcon", - remix: "RiZoomOutLine", - tabler: "IconZoomOut", - hugeicons: "ZoomOutAreaIcon", - }, - BellIcon: { - lucide: "BellIcon", - remix: "RiNotification3Line", - tabler: "IconBell", - hugeicons: "Notification01Icon", - }, - ImageIcon: { - lucide: "ImageIcon", - remix: "RiImageLine", - tabler: "IconPhoto", - hugeicons: "Image01Icon", - }, - KeyboardIcon: { - lucide: "KeyboardIcon", - remix: "RiKeyboardLine", - tabler: "IconKeyboard", - hugeicons: "KeyboardIcon", - }, - LanguagesIcon: { - lucide: "LanguagesIcon", - remix: "RiTranslate", - tabler: "IconLanguage", - hugeicons: "LanguageCircleIcon", - }, - ShieldIcon: { - lucide: "ShieldIcon", - remix: "RiShieldLine", - tabler: "IconShield", - hugeicons: "SecurityIcon", - }, - PencilIcon: { - lucide: "PencilIcon", - remix: "RiPencilLine", - tabler: "IconPencil", - hugeicons: "Edit01Icon", - }, - ActivityIcon: { - lucide: "ActivityIcon", - remix: "RiPulseLine", - tabler: "IconActivity", - hugeicons: "Cardiogram01Icon", - }, - PanelLeftIcon: { - lucide: "PanelLeftIcon", - remix: "RiLayoutLeftLine", - tabler: "IconLayoutSidebar", - hugeicons: "LayoutLeftIcon", - }, - ArrowDownIcon: { - lucide: "ArrowDownIcon", - remix: "RiArrowDownLine", - tabler: "IconArrowDown", - hugeicons: "ArrowDown01Icon", - }, - MessageSquareIcon: { - lucide: "MessageSquareIcon", - remix: "RiChat1Line", - tabler: "IconMessage", - hugeicons: "Message01Icon", - }, - WalletIcon: { - lucide: "WalletIcon", - remix: "RiWalletLine", - tabler: "IconWallet", - hugeicons: "Wallet01Icon", - }, - Building2Icon: { - lucide: "Building2Icon", - remix: "RiBankLine", - tabler: "IconBuildingBank", - hugeicons: "BankIcon", - }, - BadgeCheckIcon: { - lucide: "BadgeCheckIcon", - remix: "RiVerifiedBadgeLine", - tabler: "IconRosetteDiscountCheck", - hugeicons: "CheckmarkBadge01Icon", - }, - ChevronsUpDownIcon: { - lucide: "ChevronsUpDownIcon", - remix: "RiExpandUpDownLine", - tabler: "IconSelector", - hugeicons: "UnfoldMoreIcon", - }, - CircleDashedIcon: { - lucide: "CircleDashedIcon", - remix: "RiLoader3Line", - tabler: "IconCircleDashed", - hugeicons: "Loading01Icon", - }, - EyeOffIcon: { - lucide: "EyeOffIcon", - remix: "RiEyeOffLine", - tabler: "IconEyeClosed", - hugeicons: "ViewOffIcon", - }, - MicIcon: { - lucide: "MicIcon", - remix: "RiMicLine", - tabler: "IconMicrophone", - hugeicons: "VoiceIcon", - }, - RadioIcon: { - lucide: "RadioIcon", - remix: "RiRadioButtonLine", - tabler: "IconPlayerRecordFilled", - hugeicons: "RecordIcon", - }, - ExternalLinkIcon: { - lucide: "ExternalLinkIcon", - remix: "RiExternalLinkLine", - tabler: "IconExternalLink", - hugeicons: "LinkSquare02Icon", - }, - RefreshCwIcon: { - lucide: "RefreshCwIcon", - remix: "RiRefreshLine", - tabler: "IconRefresh", - hugeicons: "RefreshIcon", - }, - BoldIcon: { - lucide: "BoldIcon", - remix: "RiBold", - tabler: "IconBold", - hugeicons: "TextBoldIcon", - }, - ItalicIcon: { - lucide: "ItalicIcon", - remix: "RiItalic", - tabler: "IconItalic", - hugeicons: "TextItalicIcon", - }, - UnderlineIcon: { - lucide: "UnderlineIcon", - remix: "RiUnderline", - tabler: "IconUnderline", - hugeicons: "TextUnderlineIcon", - }, - TableIcon: { - lucide: "TableIcon", - remix: "RiTable2", - tabler: "IconTable", - hugeicons: "Table01Icon", - }, - ChartLineIcon: { - lucide: "ChartLineIcon", - remix: "RiLineChartLine", - tabler: "IconChartLine", - hugeicons: "ChartLineData01Icon", - }, - ChartBarIcon: { - lucide: "ChartBarIcon", - remix: "RiBarChartLine", - tabler: "IconChartBar", - hugeicons: "ChartColumnIcon", - }, - ChartPieIcon: { - lucide: "ChartPieIcon", - remix: "RiPieChartLine", - tabler: "IconChartPie", - hugeicons: "PieChartIcon", - }, - TerminalSquareIcon: { - lucide: "TerminalSquareIcon", - remix: "RiTerminalLine", - tabler: "IconTerminal2", - hugeicons: "SourceCodeSquareIcon", - }, - BookOpen: { - lucide: "BookOpen", - remix: "RiBookOpenLine", - tabler: "IconBook", - hugeicons: "BookOpen02Icon", - }, - Settings2Icon: { - lucide: "Settings2Icon", - remix: "RiSettings4Line", - tabler: "IconSettings", - hugeicons: "Settings05Icon", - }, - FrameIcon: { - lucide: "FrameIcon", - remix: "RiCropLine", - tabler: "IconFrame", - hugeicons: "CropIcon", - }, - PieChartIcon: { - lucide: "PieChartIcon", - remix: "RiPieChartLine", - tabler: "IconChartPie", - hugeicons: "PieChartIcon", - }, - MapIcon: { - lucide: "MapIcon", - remix: "RiMapLine", - tabler: "IconMap", - hugeicons: "MapsIcon", - }, - ShoppingCartIcon: { - lucide: "ShoppingCartIcon", - remix: "RiShoppingCartLine", - tabler: "IconShoppingCart", - hugeicons: "ShoppingCart01Icon", - }, - LifeBuoy: { - lucide: "LifeBuoy", - remix: "RiLifebuoyLine", - tabler: "IconLifebuoy", - hugeicons: "ChartRingIcon", - }, - Send: { - lucide: "Send", - remix: "RiSendPlaneLine", - tabler: "IconSend", - hugeicons: "SentIcon", - }, - AppWindowIcon: { - lucide: "AppWindowIcon", - remix: "RiWindowLine", - tabler: "IconAppWindow", - hugeicons: "CursorInWindowIcon", - }, - BookmarkIcon: { - lucide: "BookmarkIcon", - remix: "RiBookmarkLine", - tabler: "IconBookmark", - hugeicons: "Bookmark01Icon", - }, - ChevronUpIcon: { - lucide: "ChevronUpIcon", - remix: "RiArrowUpSLine", - tabler: "IconChevronUp", - hugeicons: "ArrowUp01Icon", - }, - ChevronLeftIcon: { - lucide: "ChevronLeftIcon", - remix: "RiArrowLeftSLine", - tabler: "IconChevronLeft", - hugeicons: "ArrowLeft01Icon", - }, - TriangleAlertIcon: { - lucide: "TriangleAlertIcon", - remix: "RiAlertLine", - tabler: "IconAlertTriangle", - hugeicons: "Alert02Icon", - }, - OctagonXIcon: { - lucide: "OctagonXIcon", - remix: "RiCloseCircleLine", - tabler: "IconAlertOctagon", - hugeicons: "MultiplicationSignCircleIcon", - }, - Loader2Icon: { - lucide: "Loader2Icon", - remix: "RiLoader4Line", - tabler: "IconLoader", - hugeicons: "Loading03Icon", - }, - VolumeOffIcon: { - lucide: "VolumeOffIcon", - remix: "RiVolumeMuteLine", - tabler: "IconVolume", - hugeicons: "VolumeOffIcon", - }, - AlertCircleIcon: { - lucide: "AlertCircleIcon", - remix: "RiAlertLine", - tabler: "IconAlertCircle", - hugeicons: "Alert01Icon", - }, - User2Icon: { - lucide: "User2Icon", - remix: "RiUserLine", - tabler: "IconUser", - hugeicons: "UserIcon", - }, - ArrowRightCircleIcon: { - lucide: "ArrowRightCircleIcon", - remix: "RiArrowRightCircleLine", - tabler: "IconCircleArrowRight", - hugeicons: "CircleArrowRight02Icon", - }, - LogInIcon: { - lucide: "LogInIcon", - remix: "RiLoginBoxLine", - tabler: "IconLogin", - hugeicons: "Login01Icon", - }, - PenSquareIcon: { - lucide: "PenSquareIcon", - remix: "RiEditLine", - tabler: "IconEdit", - hugeicons: "Edit02Icon", - }, - CameraIcon: { - lucide: "CameraIcon", - remix: "RiCameraLine", - tabler: "IconCamera", - hugeicons: "Camera01Icon", - }, - PlusSquareIcon: { - lucide: "PlusSquareIcon", - remix: "RiAddBoxLine", - tabler: "IconSquarePlus", - hugeicons: "PlusSignSquareIcon", - }, - SquarePenIcon: { - lucide: "SquarePenIcon", - remix: "RiEdit2Line", - tabler: "IconEdit", - hugeicons: "Edit02Icon", - }, - Volume1Icon: { - lucide: "Volume1Icon", - remix: "RiVolumeDownLine", - tabler: "IconVolume2", - hugeicons: "VolumeLowIcon", - }, - Volume2Icon: { - lucide: "Volume2Icon", - remix: "RiVolumeUpLine", - tabler: "IconVolume", - hugeicons: "VolumeHighIcon", - }, - XCircleIcon: { - lucide: "XCircleIcon", - remix: "RiCloseCircleLine", - tabler: "IconCircleX", - hugeicons: "Cancel01Icon", - }, - TimerIcon: { - lucide: "TimerIcon", - remix: "RiTimerLine", - tabler: "IconAlarm", - hugeicons: "Time01Icon", - }, - PinIcon: { - lucide: "PinIcon", - remix: "RiPushpinLine", - tabler: "IconPinned", - hugeicons: "PinIcon", - }, -} as const; diff --git a/packages/core/src/__registry__/index.ts b/packages/core/src/__registry__/index.ts deleted file mode 100644 index d5c901eb0..000000000 --- a/packages/core/src/__registry__/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -// AUTO-GENERATED - DO NOT EDIT -// Run "pnpm build" to regenerate - -import { base } from "./base"; -import { hooks } from "./hooks"; -import { lib } from "./lib"; -import { ui } from "./ui"; - -/** - * Combined registry of all items (UI, base, lib, hooks) - */ -export const registry = [...base, ...ui, ...lib, ...hooks]; - -export { base } from "./base"; -export { hooks } from "./hooks"; -export { iconLibraries, icons } from "./icons"; -export { lib } from "./lib"; -export { ui } from "./ui"; diff --git a/packages/core/src/__registry__/lib.ts b/packages/core/src/__registry__/lib.ts deleted file mode 100644 index ef864e2b3..000000000 --- a/packages/core/src/__registry__/lib.ts +++ /dev/null @@ -1,54 +0,0 @@ -// AUTO-GENERATED - DO NOT EDIT -// Run "pnpm build" to regenerate - -export const lib = [ - { - name: "utils", - type: "registry:lib", - files: [ - { - type: "registry:lib", - path: "lib/utils/index.ts", - target: "lib/utils.ts", - content: `import { clsx } from "clsx"; -import { twMerge } from "tailwind-merge"; -import type { ClassValue } from "clsx"; - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} -`, - }, - ], - }, - { - name: "focus-styles", - type: "registry:lib", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:lib", - path: "lib/focus-styles/basic.ts", - target: "lib/focus-styles.ts", - content: `import { tv } from "tailwind-variants"; - -export const focusRing = tv({ - base: "outline-hidden ring-0 ring-border-focus focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-bg", -}); - -export const focusInput = tv({ - base: "ring-0 focus-within:ring-2 focus-within:ring-border-focus", -}); - -export const focusRingGroup = tv({ - base: "outline-hidden ring-0 ring-border-focus group-focus-visible:ring-2 group-focus-visible:ring-offset-2 group-focus-visible:ring-offset-bg", -}); -`, - }, - ], - }, - }, - }, -] as const; diff --git a/packages/core/src/__registry__/ui.ts b/packages/core/src/__registry__/ui.ts deleted file mode 100644 index 8cc243afc..000000000 --- a/packages/core/src/__registry__/ui.ts +++ /dev/null @@ -1,6265 +0,0 @@ -// AUTO-GENERATED - DO NOT EDIT -// Run "pnpm build" to regenerate - -export const ui = [ - { - name: "accordion", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/accordion/basic.tsx", - target: "ui/accordion.tsx", - content: `"use client"; - -import { DisclosureGroup as AriaDisclosureGroup, composeRenderProps } from "react-aria-components"; -import { tv } from "tailwind-variants"; - -const accordionStyles = tv({ - base: "**:data-disclosure:not-last:border-b", -}); - -interface AccordionProps extends React.ComponentProps {} -function Accordion({ className, ...props }: AccordionProps) { - return ( - accordionStyles({ className: c }))} - {...props} - /> - ); -} - -export { Accordion }; - -export type { AccordionProps }; -`, - }, - ], - }, - }, - }, - { - name: "alert", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/alert/basic.tsx", - target: "ui/alert.tsx", - content: `import { tv } from "tailwind-variants"; -import type * as React from "react"; -import type { VariantProps } from "tailwind-variants"; - -const alertVariants = tv({ - slots: { - root: "relative grid w-full items-start gap-y-0.5 rounded-lg border bg-card px-4 py-3 text-sm has-[>svg]:has-data-alert-action:grid-cols-[calc(var(--spacing)*4)_1fr_auto] has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-data-alert-action:grid-cols-[1fr_auto] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", - title: "line-clamp-1 min-h-4 font-medium tracking-tight [svg~&]:col-start-2", - description: "grid justify-items-start gap-1 text-fg-muted [&_p]:leading-relaxed [svg~&]:col-start-2", - action: - "flex gap-1 sm:row-start-1 sm:row-end-3 sm:self-center sm:[[data-alert-title]~&]:col-start-2 sm:[svg~&]:col-start-2 sm:[svg~[data-alert-description]~&]:col-start-3 sm:[svg~[data-alert-title]~&]:col-start-3", - }, - variants: { - variant: { - neutral: { - root: "text-fg", - }, - danger: { - root: "text-fg-danger *:data-alert-description:text-fg-danger/90", - }, - warning: { - root: "text-fg-warning *:data-alert-description:text-fg-warning/90", - }, - info: { - root: "text-fg-info *:data-alert-description:text-fg-info/90", - }, - success: { - root: "text-fg-success *:data-alert-description:text-fg-success/90", - }, - }, - }, - defaultVariants: { - variant: "neutral", - }, -}); - -const { root, title, description, action } = alertVariants(); - -/* -----------------------------------------------------------------------------------------------*/ - -interface AlertProps extends React.ComponentProps<"div">, VariantProps {} - -function Alert({ className, variant, ...props }: AlertProps) { - return
; -} - -/* -----------------------------------------------------------------------------------------------*/ - -interface AlertTitleProps extends React.ComponentProps<"div"> {} - -function AlertTitle({ className, ...props }: AlertTitleProps) { - return
; -} - -/* -----------------------------------------------------------------------------------------------*/ - -interface AlertDescriptionProps extends React.ComponentProps<"div"> {} - -function AlertDescription({ className, ...props }: AlertDescriptionProps) { - return
; -} - -/* -----------------------------------------------------------------------------------------------*/ - -interface AlertActionProps extends React.ComponentProps<"div"> {} - -function AlertAction({ className, ...props }: AlertActionProps) { - return
; -} - -/* -----------------------------------------------------------------------------------------------*/ - -export { Alert, AlertTitle, AlertDescription, AlertAction }; - -export type { AlertProps, AlertTitleProps, AlertDescriptionProps, AlertActionProps }; -`, - }, - ], - }, - }, - }, - { - name: "avatar", - type: "registry:ui", - defaultVariant: "base", - variants: { - base: { - files: [ - { - type: "registry:ui", - path: "ui/avatar/base.tsx", - target: "ui/avatar.tsx", - content: `"use client"; - -import * as React from "react"; -import { tv } from "tailwind-variants"; -import type { VariantProps } from "tailwind-variants"; - -import { useImageLoadingStatus } from "@dotui/registry/hooks/use-image-loading-status"; -import { createContext } from "@dotui/registry/lib/context"; -import type { ImageLoadingStatus } from "@dotui/registry/hooks/use-image-loading-status"; - -const avatarStyles = tv({ - slots: { - root: [ - "group/avatar relative inline-flex shrink-0 rounded-full bg-muted align-middle", - "*:data-badge:absolute *:data-badge:not-with-[right]:not-with-[left]:right-0 *:data-badge:not-with-[bottom]:not-with-[top]:bottom-0", - ], - image: "aspect-square size-full rounded-[inherit] object-cover", - fallback: - "flex size-full select-none items-center justify-center rounded-[inherit] bg-muted text-sm group-data-[size=sm]/avatar:text-xs", - badge: [ - "absolute right-0 with-[left]:right-auto bottom-0 with-[top]:bottom-auto z-10 inline-flex select-none items-center justify-center rounded-full bg-primary text-fg-on-primary bg-blend-color ring-2 ring-bg", - "not-with-[size]:group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden", - "not-with-[size]:group-data-[size=md]/avatar:size-2.5 group-data-[size=md]/avatar:[&>svg]:size-2", - "not-with-[size]:group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", - ], - group: "group/avatar-group flex -space-x-2 *:data-avatar:ring-2 *:data-avatar:ring-bg", - groupCount: - "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-fg-muted text-sm ring-2 ring-bg group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3", - }, - variants: { - size: { - sm: { group: "*:data-avatar:size-6", root: "size-6" }, - md: { group: "*:data-avatar:size-8", root: "size-8" }, - lg: { group: "*:data-avatar:size-10", root: "size-10" }, - }, - }, - defaultVariants: { - size: "md", - }, -}); - -const { group, root, image, fallback, badge, groupCount } = avatarStyles(); - -const [AvatarContext, useAvatarContext] = createContext<{ - status: ImageLoadingStatus; - setStatus: (status: ImageLoadingStatus) => void; -}>({ - name: "Avatar", - strict: true, -}); - -/* ------------------------------------------------------------------------------------------------- - * Avatar - * -----------------------------------------------------------------------------------------------*/ - -interface AvatarProps extends React.ComponentProps<"span">, VariantProps {} - -function Avatar({ className, size = "md", ...props }: AvatarProps) { - const [status, setStatus] = React.useState("idle"); - - return ( - - - - ); -} - -/* ------------------------------------------------------------------------------------------------- - * Avatar Image - * -----------------------------------------------------------------------------------------------*/ - -interface AvatarImageProps extends Omit, "src"> { - src?: string; -} - -function AvatarImage({ src, alt, className, referrerPolicy, crossOrigin, ...props }: AvatarImageProps) { - const status = useImageLoadingStatus(src, { referrerPolicy, crossOrigin }); - const { setStatus } = useAvatarContext("AvatarImage"); - - React.useLayoutEffect(() => { - setStatus(status); - }, [status, setStatus]); - - if (status === "loaded") - return {alt}; - - return null; -} - -/* ------------------------------------------------------------------------------------------------- - * Avatar Fallback - * -----------------------------------------------------------------------------------------------*/ - -interface AvatarFallbackProps extends React.ComponentProps<"span"> {} - -const AvatarFallback = ({ className, ...props }: AvatarFallbackProps) => { - const { status } = useAvatarContext("AvatarFallback"); - if (status !== "loaded") return ; - return null; -}; - -/* ------------------------------------------------------------------------------------------------- - * Avatar Badge - * -----------------------------------------------------------------------------------------------*/ - -interface AvatarBadgeProps extends React.ComponentProps<"span"> {} - -const AvatarBadge = ({ className, ...props }: AvatarBadgeProps) => { - return ; -}; - -/* ------------------------------------------------------------------------------------------------- - * Avatar Group - * -----------------------------------------------------------------------------------------------*/ - -interface AvatarGroupProps extends React.ComponentProps<"div">, VariantProps {} - -const AvatarGroup = ({ className, size, ...props }: AvatarGroupProps) => { - return
; -}; - -/* ------------------------------------------------------------------------------------------------- - * Avatar Group Count - * -----------------------------------------------------------------------------------------------*/ - -interface AvatarGroupCountProps extends React.ComponentProps<"span"> {} - -const AvatarGroupCount = ({ className, ...props }: AvatarGroupCountProps) => { - return ; -}; - -/* -----------------------------------------------------------------------------------------------*/ - -export { AvatarGroup, Avatar, AvatarImage, AvatarFallback, AvatarBadge, AvatarGroupCount }; - -export type { - AvatarGroupProps, - AvatarProps, - AvatarImageProps, - AvatarFallbackProps, - AvatarBadgeProps, - AvatarGroupCountProps, -}; -`, - }, - ], - }, - }, - }, - { - name: "badge", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/badge/basic.tsx", - target: "ui/badge.tsx", - content: `import { tv } from "tailwind-variants"; -import type * as React from "react"; -import type { VariantProps } from "tailwind-variants"; - -const badgeStyles = tv({ - base: "inline-flex w-fit shrink-0 items-center justify-center gap-1 whitespace-nowrap rounded-md px-2 py-0.5 font-medium text-xs [&>svg]:pointer-events-none", - variants: { - variant: { - default: "bg-neutral text-fg-on-neutral", - primary: "bg-primary text-fg-on-primary", - danger: "bg-danger text-fg-on-danger", - success: "bg-success text-fg-on-success", - warning: "bg-warning text-fg-on-warning", - info: "bg-info text-fg-on-info", - }, - size: { - sm: "px-1.5 py-0.25 [&>svg]:size-3", - md: "px-2 py-0.5 [&>svg]:size-3", - lg: "px-2.5 py-0.75 [&>svg]:size-3", - }, - }, - defaultVariants: { - variant: "default", - size: "md", - }, -}); - -interface BadgeProps extends React.ComponentProps<"span">, VariantProps {} -const Badge = ({ className, variant, size, ...props }: BadgeProps) => { - return ; -}; - -export type { BadgeProps }; -export { Badge }; -`, - }, - ], - }, - }, - }, - { - name: "breadcrumbs", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/breadcrumbs/basic.tsx", - target: "ui/breadcrumbs.tsx", - content: `"use client"; - -import { ChevronRightIcon } from "lucide-react"; -import { - Breadcrumb as AriaBreadcrumb, - Breadcrumbs as AriaBreadcrumbs, - Link as AriaLink, - composeRenderProps, -} from "react-aria-components"; -import { tv } from "tailwind-variants"; -import type { BreadcrumbsProps as AriaBreadcrumbsProps } from "react-aria-components"; - -const breadcrumbsStyles = tv({ - slots: { - root: "wrap-break-word flex flex-wrap items-center gap-1.5 text-fg-muted text-sm [&_svg]:size-4", - item: "inline-flex items-center gap-1", - link: [ - "focus-reset focus-visible:focus-ring", - "inline-flex items-center gap-1 rounded px-0.5 current:text-fg leading-none transition-colors disabled:cursor-default disabled:not-current:text-fg-disabled hover:[a]:text-fg", - ], - }, -}); - -const { root, item, link } = breadcrumbsStyles(); - -interface BreadcrumbsProps extends AriaBreadcrumbsProps { - ref?: React.RefObject; -} -const Breadcrumbs = ({ className, ...props }: BreadcrumbsProps) => { - return ; -}; - -type BreadcrumbProps = BreadcrumbItemProps & Omit; -const Breadcrumb = ({ ref, children, ...props }: BreadcrumbProps) => { - return ( - - {composeRenderProps(children, (children, { isCurrent }) => ( - <> - {children} - {!isCurrent && } - - ))} - - ); -}; - -interface BreadcrumbItemProps extends React.ComponentProps {} -const BreadcrumbItem = ({ className, ...props }: BreadcrumbItemProps) => ( - item({ className }))} {...props} /> -); - -interface BreadcrumbLinkProps extends React.ComponentProps {} -const BreadcrumbLink = ({ className, ...props }: BreadcrumbLinkProps) => ( - link({ className }))} {...props} /> -); - -export { Breadcrumbs, Breadcrumb, BreadcrumbItem, BreadcrumbLink }; - -export type { BreadcrumbsProps, BreadcrumbProps, BreadcrumbItemProps, BreadcrumbLinkProps }; -`, - }, - ], - registryDependencies: ["focus-styles"], - }, - }, - }, - { - name: "button", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/button/basic.tsx", - target: "ui/button.tsx", - content: `"use client"; - -import { - Button as AriaButton, - ButtonContext as AriaButtonContext, - Link as AriaLink, - composeRenderProps, -} from "react-aria-components"; -import { tv } from "tailwind-variants"; -import type * as React from "react"; -import type { VariantProps } from "tailwind-variants"; - -import { useButtonAspect } from "@dotui/registry/hooks/use-button-aspect"; -import { createVariantsContext } from "@dotui/registry/lib/context"; -import { Loader } from "@dotui/registry/ui/loader"; - -const buttonStyles = tv({ - base: [ - "relative box-border inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm leading-normal transition-[background-color,border-color,color,box-shadow] data-icon-only:px-0", - "*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2", - // svg - "[&_svg]:pointer-events-none [&_svg]:not-with-[size]:size-4 [&_svg]:shrink-0", - // focus state - "focus-reset focus-visible:focus-ring", - // disabled state - "disabled:cursor-default disabled:border-border-disabled disabled:bg-disabled disabled:text-fg-disabled", - // pending state - "pending:cursor-default pending:border-border-disabled pending:bg-disabled pending:text-transparent pending:**:not-data-[slot=spinner]:not-in-data-[slot=spinner]:opacity-0 pending:**:data-[slot=spinner]:text-fg-muted", - ], - variants: { - variant: { - default: - "border pressed:border-border-active bg-neutral pressed:bg-neutral-active text-fg-on-neutral hover:border-border-hover hover:bg-neutral-hover", - primary: - "pending:border-0 bg-primary pressed:bg-primary-active text-fg-on-primary [--color-disabled:var(--neutral-500)] [--color-fg-disabled:var(--neutral-300)] hover:bg-primary-hover disabled:border-0", - quiet: "bg-transparent pressed:bg-inverse/20 text-fg hover:bg-inverse/10", - link: "text-fg underline-offset-4 hover:underline", - warning: "bg-warning pressed:bg-warning-active text-fg-on-warning hover:bg-warning-hover", - danger: "bg-danger pressed:bg-danger-active text-fg-on-danger hover:bg-danger-hover", - }, - size: { - sm: "h-8 px-3 has-[>svg]:px-2.5 data-icon-only:not-with-[size]:not-with-[w]:w-8", - md: "h-9 px-4 has-[>svg]:px-3 data-icon-only:not-with-[size]:not-with-[w]:w-9", - lg: "h-10 px-5 has-[>svg]:px-4 data-icon-only:not-with-[size]:not-with-[w]:w-10", - }, - }, - defaultVariants: { - variant: "default", - size: "md", - }, -}); - -type ButtonVariants = VariantProps; - -const [ButtonProvider, useContextProps] = createVariantsContext< - ButtonVariants, - React.ComponentProps ->(AriaButtonContext); - -/* -----------------------------------------------------------------------------------------------*/ - -interface ButtonProps extends React.ComponentProps, ButtonVariants { - aspect?: "default" | "square" | "auto"; -} - -const Button = (localProps: ButtonProps) => { - const { variant, size, aspect = "auto", className, slot, style, children, ...props } = useContextProps(localProps); - - const isIconOnly = useButtonAspect(children, aspect); - - return ( - buttonStyles({ variant, size, className: cn }))} - slot={slot} - style={style} - {...props} - > - {composeRenderProps(children, (children, { isPending }) => ( - <> - {isPending && ( - - )} - {typeof children === "string" ? {children} : children} - - ))} - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface LinkButtonProps extends React.ComponentProps, VariantProps { - aspect?: "default" | "square" | "auto"; -} - -const LinkButton = (localProps: LinkButtonProps) => { - const { variant, size, aspect = "auto", className, slot, style, children, ...props } = useContextProps(localProps); - - const isIconOnly = useButtonAspect(children, aspect); - - return ( - buttonStyles({ variant, size, className: cn }))} - slot={slot} - style={style} - {...props} - > - {composeRenderProps(children, (children) => ( - <>{typeof children === "string" ? {children} : children} - ))} - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -export type { ButtonProps, LinkButtonProps }; - -export { Button, LinkButton, ButtonProvider, buttonStyles }; -`, - }, - ], - registryDependencies: ["loader", "focus-styles"], - }, - ripple: { - files: [ - { - type: "registry:ui", - path: "ui/button/ripple.tsx", - target: "ui/button.tsx", - content: `"use client"; - -import { - Button as AriaButton, - ButtonContext as AriaButtonContext, - Link as AriaLink, - composeRenderProps, -} from "react-aria-components"; -import { tv } from "tailwind-variants"; -import type * as React from "react"; -import type { VariantProps } from "tailwind-variants"; - -import { useButtonAspect } from "@dotui/registry/hooks/use-button-aspect"; -import { createVariantsContext } from "@dotui/registry/lib/context"; -import { Loader } from "@dotui/registry/ui/loader"; - -const buttonStyles = tv({ - base: [ - "ripple relative box-border inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm leading-normal transition-[background-color,border-color,color,box-shadow] data-icon-only:px-0", - "*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2", - // focus state - "focus-reset focus-visible:focus-ring", - // disabled state - "disabled:cursor-default disabled:border-border-disabled disabled:bg-disabled disabled:text-fg-disabled", - // pending state - "pending:cursor-default pending:border-border-disabled pending:bg-disabled pending:text-transparent pending:**:not-data-[slot=spinner]:not-in-data-[slot=spinner]:opacity-0 pending:**:data-[slot=spinner]:text-fg-muted", - ], - variants: { - variant: { - default: - "border pressed:border-border-active bg-neutral pressed:bg-neutral-active text-fg-on-neutral hover:border-border-hover hover:bg-neutral-hover", - primary: - "pending:border-0 bg-primary pressed:bg-primary-active text-fg-on-primary [--color-disabled:var(--neutral-500)] [--color-fg-disabled:var(--neutral-300)] hover:bg-primary-hover disabled:border-0", - quiet: "bg-transparent pressed:bg-inverse/20 text-fg hover:bg-inverse/10", - link: "text-fg underline-offset-4 hover:underline", - warning: "bg-warning pressed:bg-warning-active text-fg-on-warning hover:bg-warning-hover", - danger: "bg-danger pressed:bg-danger-active text-fg-on-danger hover:bg-danger-hover", - }, - size: { - sm: "h-8 px-3 data-icon-only:not-with-[size]:not-with-[w]:w-8 [&_svg]:size-4", - md: "h-9 px-4 data-icon-only:not-with-[size]:not-with-[w]:w-9 [&_svg]:size-4", - lg: "h-10 px-5 data-icon-only:not-with-[size]:not-with-[w]:w-10 [&_svg]:size-5", - }, - }, - defaultVariants: { - variant: "default", - size: "md", - }, -}); - -type ButtonVariants = VariantProps; - -const [ButtonProvider, useContextProps] = createVariantsContext< - ButtonVariants, - React.ComponentProps ->(AriaButtonContext); - -/* -----------------------------------------------------------------------------------------------*/ - -interface ButtonProps extends React.ComponentProps, ButtonVariants { - aspect?: "default" | "square" | "auto"; -} - -const Button = (localProps: ButtonProps) => { - const { variant, size, aspect = "auto", className, slot, style, children, ...props } = useContextProps(localProps); - - const isIconOnly = useButtonAspect(children, aspect); - - return ( - buttonStyles({ variant, size, className: cn }))} - slot={slot} - style={style} - {...props} - > - {composeRenderProps(children, (children, { isPending }) => ( - <> - {isPending && ( - - )} - {typeof children === "string" ? {children} : children} - - ))} - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface LinkButtonProps extends React.ComponentProps, VariantProps { - aspect?: "default" | "square" | "auto"; -} - -const LinkButton = (localProps: LinkButtonProps) => { - const { variant, size, aspect = "auto", className, slot, style, children, ...props } = useContextProps(localProps); - - const isIconOnly = useButtonAspect(children, aspect); - - return ( - buttonStyles({ variant, size, className: cn }))} - slot={slot} - style={style} - {...props} - > - {composeRenderProps(children, (children) => ( - <>{typeof children === "string" ? {children} : children} - ))} - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -export type { ButtonProps, LinkButtonProps }; - -export { Button, LinkButton, ButtonProvider, buttonStyles }; -`, - }, - ], - }, - }, - }, - { - name: "calendar", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/calendar/basic.tsx", - target: "ui/calendar.tsx", - content: `"use client"; - -import React from "react"; -import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; -import { - Calendar as AriaCalendar, - CalendarCell as AriaCalendarCell, - CalendarContext as AriaCalendarContext, - CalendarGrid as AriaCalendarGrid, - CalendarGridBody as AriaCalendarGridBody, - CalendarGridHeader as AriaCalendarGridHeader, - CalendarHeaderCell as AriaCalendarHeaderCell, - Heading as AriaHeading, - RangeCalendar as AriaRangeCalendar, - RangeCalendarContext as AriaRangeCalendarContext, - RangeCalendarStateContext as AriaRangeCalendarStateContext, - composeRenderProps, - useSlottedContext, -} from "react-aria-components"; -import { tv } from "tailwind-variants"; -import type { - CalendarProps as AriaCalendarProps, - RangeCalendarProps as AriaRangeCalendarProps, - DateValue, -} from "react-aria-components"; -import type { VariantProps } from "tailwind-variants"; - -import { Button } from "@dotui/registry/ui/button"; - -const calendarStyles = tv({ - slots: { - root: "flex flex-col gap-4", - header: "flex items-center justify-between gap-2", - grid: "w-full border-collapse", - gridHeader: "", - gridHeaderCell: "font-normal text-fg-muted text-xs", - gridBody: "", - }, - variants: { - standalone: { - true: { - root: "rounded-md border bg-bg p-3", - }, - }, - }, -}); - -const calendarCellStyles = tv({ - slots: { - cellRoot: - "flex outside-month:hidden items-center justify-center outline-none selection-end:rounded-r-md selection-start:rounded-l-md", - cell: [ - "focus-reset focus-visible:focus-ring", - "my-1 flex size-8 cursor-pointer unavailable:cursor-default items-center justify-center rounded-md pressed:bg-inverse/20 text-sm unavailable:text-fg-disabled unavailable:not-data-disabled:line-through transition-colors read-only:cursor-default hover:bg-inverse/10 hover:unavailable:bg-transparent hover:read-only:bg-transparent disabled:cursor-default disabled:bg-transparent disabled:text-fg-disabled", - ], - }, - variants: { - variant: { - primary: {}, - accent: {}, - }, - range: { - true: { - cellRoot: "selected: selected:bg-inverse/10 selected:invalid:bg-danger-muted selected:invalid:text-fg-danger", - cell: "selection-end:invalid:bg-danger selection-start:invalid:bg-danger selection-end:invalid:text-fg-on-danger selection-start:invalid:text-fg-on-danger", - }, - false: { - cell: "selected:invalid:bg-danger selected:invalid:text-fg-on-danger", - }, - }, - }, - compoundVariants: [ - { - variant: "primary", - range: false, - className: { - cell: "selected:bg-primary selected:text-fg-on-primary", - }, - }, - { - variant: "accent", - range: false, - className: { - cell: "selected:bg-accent selected:text-fg-on-accent", - }, - }, - { - variant: "primary", - range: true, - className: { - cell: "selection-end:bg-primary selection-start:bg-primary selection-end:text-fg-on-primary selection-start:text-fg-on-primary", - }, - }, - { - variant: "accent", - range: true, - className: { - cell: "selection-end:bg-accent selection-start:bg-accent selection-end:text-fg-on-accent selection-start:text-fg-on-accent", - }, - }, - ], - defaultVariants: { - variant: "accent", - }, -}); - -const { root, header, grid, gridHeader, gridHeaderCell, gridBody } = calendarStyles(); - -const { cellRoot, cell } = calendarCellStyles(); - -/* -----------------------------------------------------------------------------------------------*/ - -type CalendarProps = - | ({ - mode?: "single"; - } & AriaCalendarProps) - | ({ - mode: "range"; - } & AriaRangeCalendarProps); - -const Calendar = ({ mode, className, ...props }: CalendarProps) => { - const rangeCalendarContext = useSlottedContext(AriaRangeCalendarContext); - const calendarContext = useSlottedContext(AriaCalendarContext); - - if (mode === "range" || rangeCalendarContext) { - const standalone = Object.keys(rangeCalendarContext ?? {}).length === 0; - return ( - ["className"], (className) => - root({ standalone, className }), - )} - {...(props as AriaRangeCalendarProps)} - > - {composeRenderProps( - props.children as AriaRangeCalendarProps["children"], - (children) => - children ?? ( - <> - - - - ), - )} - - ); - } - - const standalone = !!calendarContext; - return ( - ["className"], (className) => - root({ standalone, className }), - )} - {...(props as AriaCalendarProps)} - > - {composeRenderProps( - props.children as AriaCalendarProps["children"], - (children) => - children ?? ( - <> - - - - ), - )} - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface CalendarHeaderProps extends React.ComponentProps<"header"> {} - -const CalendarHeader = ({ className, ...props }: CalendarHeaderProps) => { - return ( -
- {props.children ?? ( - <> - - - - - )} -
- ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface CalendarGridProps extends React.ComponentProps {} - -const CalendarGrid = ({ className, ...props }: CalendarGridProps) => { - return ( - - {props.children ?? ( - <> - {(day) => {day}} - {(date) => } - - )} - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface CalendarGridHeaderProps extends React.ComponentProps {} -const CalendarGridHeader = ({ className, ...props }: CalendarGridHeaderProps) => { - return ; -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface CalendarHeaderCellProps extends React.ComponentProps {} -const CalendarHeaderCell = ({ className, ...props }: CalendarHeaderCellProps) => { - return ; -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface CalendarGridBodyProps extends React.ComponentProps {} -const CalendarGridBody = ({ className, ...props }: CalendarGridBodyProps) => { - return ; -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface CalendarCellProps - extends React.ComponentProps, - Omit, "range"> {} -const CalendarCell = ({ variant = "accent", children, className, ...props }: CalendarCellProps) => { - const rangeCalendarState = React.use(AriaRangeCalendarStateContext); - const range = !!rangeCalendarState; - - return ( - - cellRoot({ - range, - variant, - className, - }), - )} - > - {composeRenderProps( - children, - ( - _, - { - isSelected, - isFocused, - isHovered, - isPressed, - isUnavailable, - isDisabled, - isFocusVisible, - isInvalid, - isOutsideMonth, - isOutsideVisibleRange, - isSelectionEnd, - isSelectionStart, - formattedDate, - }, - ) => ( - - {formattedDate} - - ), - )} - - ); -}; - -export { - Calendar, - CalendarHeader, - CalendarGrid, - CalendarGridHeader, - CalendarHeaderCell, - CalendarGridBody, - CalendarCell, - calendarStyles, -}; - -export type { - CalendarProps, - CalendarHeaderProps, - CalendarGridProps, - CalendarGridHeaderProps, - CalendarHeaderCellProps, - CalendarGridBodyProps, - CalendarCellProps, -}; -`, - }, - ], - registryDependencies: ["button", "text", "focus-styles"], - }, - }, - }, - { - name: "card", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/card/basic.tsx", - target: "ui/card.tsx", - content: `import { tv } from "tailwind-variants"; -import type * as React from "react"; - -const cardStyles = tv({ - slots: { - root: "flex flex-col gap-6 rounded-xl border bg-card py-6 text-fg shadow-sm", - header: - "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", - title: "font-semibold leading-none", - description: "text-fg-muted text-sm", - action: "col-start-2 row-span-2 row-start-1 self-start justify-self-end", - content: "flex-1 px-6", - footer: "flex items-center px-6 [.border-t]:pt-6", - }, -}); - -const { root, header, title, description, action, content, footer } = cardStyles(); - -/* -----------------------------------------------------------------------------------------------*/ - -interface CardProps extends React.ComponentProps<"div"> {} - -function Card({ className, ...props }: CardProps) { - return
; -} - -/* -----------------------------------------------------------------------------------------------*/ - -interface CardHeaderProps extends React.ComponentProps<"div"> {} - -function CardHeader({ className, ...props }: CardHeaderProps) { - return
; -} - -/* -----------------------------------------------------------------------------------------------*/ - -interface CardTitleProps extends React.ComponentProps<"div"> {} - -function CardTitle({ className, ...props }: CardTitleProps) { - return
; -} - -/* -----------------------------------------------------------------------------------------------*/ - -interface CardDescriptionProps extends React.ComponentProps<"div"> {} - -function CardDescription({ className, ...props }: CardDescriptionProps) { - return
; -} - -/* -----------------------------------------------------------------------------------------------*/ - -interface CardActionProps extends React.ComponentProps<"div"> {} - -function CardAction({ className, ...props }: CardActionProps) { - return
; -} - -/* -----------------------------------------------------------------------------------------------*/ - -interface CardContentProps extends React.ComponentProps<"div"> {} - -function CardContent({ className, ...props }: CardContentProps) { - return
; -} - -/* -----------------------------------------------------------------------------------------------*/ - -interface CardFooterProps extends React.ComponentProps<"div"> {} - -function CardFooter({ className, ...props }: CardFooterProps) { - return
; -} - -/* -----------------------------------------------------------------------------------------------*/ - -export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent }; - -export type { - CardProps, - CardHeaderProps, - CardTitleProps, - CardDescriptionProps, - CardActionProps, - CardContentProps, - CardFooterProps, -}; -`, - }, - ], - registryDependencies: ["button", "text", "focus-styles"], - }, - }, - }, - { - name: "checkbox-group", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/checkbox-group/basic.tsx", - target: "ui/checkbox-group.tsx", - content: `"use client"; - -import { CheckboxGroup as AriaCheckboxGroup, composeRenderProps } from "react-aria-components"; -import type { CheckboxGroupProps } from "react-aria-components"; - -import { fieldStyles } from "@dotui/registry/ui/field"; - -const { field } = fieldStyles(); - -const CheckboxGroup = ({ className, ...props }: CheckboxGroupProps) => { - return ( - field({ className }))} {...props} /> - ); -}; - -export type { CheckboxGroupProps }; -export { CheckboxGroup }; -`, - }, - ], - registryDependencies: ["field", "checkbox"], - }, - }, - }, - { - name: "checkbox", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/checkbox/basic.tsx", - target: "ui/checkbox.tsx", - content: `"use client"; - -import { CheckIcon, MinusIcon } from "lucide-react"; -import { Checkbox as AriaCheckbox, composeRenderProps } from "react-aria-components"; -import { tv } from "tailwind-variants"; -import type * as React from "react"; -import type { CheckboxRenderProps } from "react-aria-components"; - -import { createContext } from "@dotui/registry/lib/context"; -import { cn } from "@dotui/registry/lib/utils"; - -const checkboxStyles = tv({ - slots: { - root: [ - "focus-reset focus-visible:focus-ring", - "flex items-center gap-2 text-sm leading-none has-data-[slot=description]:items-start", - "disabled:cursor-not-allowed disabled:text-fg-disabled", - ], - indicator: [ - "flex size-4 shrink-0 items-center justify-center rounded-sm border border-border-control bg-transparent text-transparent", - "transition-[background-color,border-color,box-shadow,color] duration-75", - // selected state - "selected:border-transparent selected:bg-primary selected:text-fg-on-primary", - // read-only state - "read-only:cursor-default", - // disabled state - "disabled:cursor-not-allowed disabled:border-border-disabled selected:disabled:bg-disabled selected:disabled:text-fg-disabled indeterminate:disabled:bg-disabled", - // invalid state - "invalid:border-border-danger invalid:selected:bg-danger-muted invalid:selected:text-fg-onMutedDanger", - // indeterminate state - "indeterminate:border-transparent indeterminate:bg-primary indeterminate:text-fg-on-primary", - ], - }, -}); - -const { root, indicator } = checkboxStyles(); - -const [InternalCheckboxProvider, useInternalCheckbox] = createContext({ - strict: true, -}); - -/* -----------------------------------------------------------------------------------------------*/ - -interface CheckboxProps extends React.ComponentProps {} - -const Checkbox = ({ className, ...props }: CheckboxProps) => { - return ( - - props.children - ? root({ className }) - : indicator({ - className: cn(className, "focus-reset focus-visible:focus-ring"), - }), - )} - {...props} - > - {composeRenderProps(props.children, (children, renderProps) => { - return children ? ( - {children} - ) : renderProps.isIndeterminate ? ( - - ) : ( - - ); - })} - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface CheckboxIndicatorProps extends React.ComponentProps<"div"> {} - -const CheckboxIndicator = ({ className, ...props }: CheckboxIndicatorProps) => { - const ctx = useInternalCheckbox("CheckboxIndicator"); - return ( -
- {ctx.isIndeterminate ? : } -
- ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -export { Checkbox, CheckboxIndicator }; - -export type { CheckboxProps, CheckboxIndicatorProps }; -`, - }, - ], - registryDependencies: ["focus-styles"], - }, - }, - }, - { - name: "color-area", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/color-area/basic.tsx", - target: "ui/color-area.tsx", - content: `"use client"; - -import { ColorArea as AriaColorArea, composeRenderProps } from "react-aria-components"; -import { tv } from "tailwind-variants"; - -import { ColorThumb } from "@dotui/registry/ui/color-thumb"; - -const colorAreaStyles = tv({ - base: "block size-48 min-w-20 rounded-md disabled:[background:var(--color-disabled)]!", -}); - -/* -----------------------------------------------------------------------------------------------*/ - -type ColorAreaProps = React.ComponentProps; - -const ColorArea = ({ className, ...props }: ColorAreaProps) => { - return ( - colorAreaStyles({ className }))} {...props}> - {props.children || } - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -export { ColorArea }; - -export type { ColorAreaProps }; -`, - }, - ], - registryDependencies: ["color-thumb"], - }, - }, - }, - { - name: "color-editor", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/color-editor/basic.tsx", - target: "ui/color-editor.tsx", - content: `import React from "react"; -import { getColorChannels } from "react-aria-components"; - -import { cn } from "@dotui/registry/lib/utils"; -import { ColorArea } from "@dotui/registry/ui/color-area"; -import { ColorField } from "@dotui/registry/ui/color-field"; -import { ColorSlider } from "@dotui/registry/ui/color-slider"; -import { Input } from "@dotui/registry/ui/input"; -import { Select, SelectContent, SelectItem, SelectTrigger } from "@dotui/registry/ui/select"; - -type ColorFormat = "hex" | "rgb" | "hsl" | "hsb"; - -interface ColorEditorProps extends React.ComponentProps<"div"> { - colorFormat?: ColorFormat; - showAlphaChannel?: boolean; - showFormatSelector?: boolean; -} - -const ColorEditor = ({ - colorFormat: ColorFormatProp = "hex", - showAlphaChannel = false, - showFormatSelector = true, - className, - ...props -}: ColorEditorProps) => { - const [colorFormat, setColorFormat] = React.useState(ColorFormatProp); - - return ( -
-
- - - {showAlphaChannel && ( - - )} -
-
- {showFormatSelector && ( - - )} -
- {colorFormat === "hex" ? ( - - - - ) : ( - getColorChannels(colorFormat).map((channel) => ( - - - - )) - )} -
-
-
- ); -}; - -export { ColorEditor }; -export type { ColorEditorProps }; -`, - }, - ], - registryDependencies: ["color-area", "color-slider", "select"], - }, - }, - }, - { - name: "color-field", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/color-field/basic.tsx", - target: "ui/color-field.tsx", - content: `"use client"; - -import { ColorField as AriaColorField, composeRenderProps } from "react-aria-components"; -import { tv } from "tailwind-variants"; -import type * as React from "react"; - -import { fieldStyles } from "@dotui/registry/ui/field/basic"; - -const colorFieldStyles = tv({ - base: [fieldStyles().field({ orientation: "vertical" }), ""], -}); - -interface ColorFieldProps extends React.ComponentProps {} - -const ColorField = ({ className, ...props }: ColorFieldProps) => { - return ( - colorFieldStyles({ className }))} - {...props} - /> - ); -}; - -export { ColorField }; -export type { ColorFieldProps }; -`, - }, - ], - registryDependencies: ["field", "input"], - }, - }, - }, - { - name: "color-picker", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/color-picker/basic.tsx", - target: "ui/color-picker.tsx", - content: `"use client"; - -import { useContext } from "react"; -import { - ColorPicker as AriaColorPicker, - ColorPickerStateContext as AriaColorPickerStateContext, - composeRenderProps, -} from "react-aria-components"; -import type { ColorPickerProps as AriaColorPickerProps, ColorPickerState } from "react-aria-components"; - -import { Button } from "@dotui/registry/ui/button"; -import { ColorSwatch } from "@dotui/registry/ui/color-swatch"; -import { Dialog, DialogContent } from "@dotui/registry/ui/dialog"; -import { Overlay } from "@dotui/registry/ui/overlay"; -import type { ButtonProps } from "@dotui/registry/ui/button"; -import type { DialogContentProps, DialogProps } from "@dotui/registry/ui/dialog"; - -interface ColorPickerProps extends AriaColorPickerProps, Omit {} - -const ColorPicker = ({ defaultOpen, isOpen, onOpenChange, ...props }: ColorPickerProps) => { - return ( - - {composeRenderProps(props.children, (children) => ( - - {children} - - ))} - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface ColorPickerTriggerProps extends Omit { - children?: React.ReactNode | ((props: ColorPickerState) => React.ReactNode); -} - -const ColorPickerTrigger = ({ children, ...props }: ColorPickerTriggerProps) => { - const state = useContext(AriaColorPickerStateContext)!; - return ( - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface ColorPickerContentProps extends DialogContentProps {} -const ColorPickerContent = ({ children, ...props }: ColorPickerContentProps) => { - return ( - - {children} - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -export { ColorPicker, ColorPickerTrigger, ColorPickerContent }; -export type { ColorPickerProps, ColorPickerTriggerProps, ColorPickerContentProps }; -`, - }, - ], - registryDependencies: [ - "button", - "color-area", - "color-field", - "color-slider", - "color-swatch", - "dialog", - "select", - ], - }, - }, - }, - { - name: "color-slider", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/color-slider/basic.tsx", - target: "ui/color-slider.tsx", - content: `"use client"; - -import { useSlotId } from "@react-aria/utils"; -import { - ColorSlider as AriaColorSlider, - SliderOutput as AriaSliderOutput, - SliderTrack as AriaSliderTrack, - composeRenderProps, - Provider, - TextContext, -} from "react-aria-components"; -import { tv } from "tailwind-variants"; - -import { ColorThumb } from "@dotui/registry/ui/color-thumb"; - -const colorSliderStyles = tv({ - slots: { - root: "flex flex-col gap-2", - output: "text-fg-muted text-sm", - track: - "relative rounded-md before:absolute before:inset-0 before:z-[-1] before:rounded-[inherit] before:bg-[repeating-conic-gradient(#e6e6e6_0%_25%,#fff_0%_50%)] before:bg-center before:bg-size-[16px_16px] before:content-[''] orientation-horizontal:**:data-[slot=color-thumb]:top-1/2 orientation-vertical:**:data-[slot=color-thumb]:left-1/2 disabled:[background:var(--color-disabled)]!", - }, - variants: { - orientation: { - horizontal: { - root: "w-48", - track: "h-6 w-full", - }, - vertical: { - root: "h-48 items-center", - track: "w-6 flex-1", - }, - }, - }, - defaultVariants: { - orientation: "horizontal", - }, -}); - -const { root, track, output } = colorSliderStyles(); - -/* -----------------------------------------------------------------------------------------------*/ - -interface ColorSliderProps extends React.ComponentProps {} - -const ColorSlider = ({ className, ...props }: ColorSliderProps) => { - const descriptionId = useSlotId(); - return ( - - root({ orientation, className: cn }))} - aria-describedby={descriptionId} - {...props} - > - {props.children ?? } - - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface ColorSliderControlProps extends React.ComponentProps {} - -const ColorSliderControl = ({ className, ...props }: ColorSliderControlProps) => { - return ( - track({ orientation, className: cn }))} - {...props} - > - {props.children ?? } - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface ColorSliderOutputProps extends React.ComponentProps {} - -const ColorSliderOutput = ({ className, ...props }: ColorSliderOutputProps) => { - return ( - output({ className }))} {...props} /> - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -export { ColorSlider, ColorSliderControl, ColorSliderOutput }; - -export type { ColorSliderProps, ColorSliderControlProps, ColorSliderOutputProps }; -`, - }, - ], - registryDependencies: ["field", "color-thumb"], - }, - }, - }, - { - name: "color-swatch-picker", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/color-swatch-picker/basic.tsx", - target: "ui/color-swatch-picker.tsx", - content: `"use client"; - -import { - ColorSwatchPicker as AriaColorSwatchPicker, - ColorSwatchPickerItem as AriaColorSwatchPickerItem, - composeRenderProps, -} from "react-aria-components"; -import { tv } from "tailwind-variants"; -import type React from "react"; - -import { ColorSwatch } from "@dotui/registry/ui/color-swatch"; - -const colorSwatchPickerStyles = tv({ - slots: { - root: "flex flex-wrap gap-1", - item: [ - "relative size-8 rounded-md transition-shadow focus:z-10 *:data-[slot=color-swatch]:size-full *:data-[slot=color-swatch]:rounded-[inherit]", - // focus state - "focus-reset focus-visible:focus-ring", - // disabled state - "disabled:cursor-not-allowed disabled:*:data-[slot=color-swatch]:[background:color-mix(in_oklab,var(--color-disabled)_90%,var(--color))]!", - // selected state - "before:absolute before:inset-0 before:scale-90 selected:before:scale-100 before:rounded-[inherit] before:bg-bg before:opacity-0 selected:before:opacity-100 before:outline-2 before:outline-inverse before:transition-[opacity,scale] before:duration-100 before:content-['']", - ], - }, -}); - -const { root, item } = colorSwatchPickerStyles(); - -/* -----------------------------------------------------------------------------------------------*/ - -interface ColorSwatchPickerProps extends React.ComponentProps {} - -const ColorSwatchPicker = ({ className, ...props }: ColorSwatchPickerProps) => { - return ( - root({ className }))} {...props} /> - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface ColorSwatchPickerItemProps extends React.ComponentProps {} -const ColorSwatchPickerItem = ({ className, style, ...props }: ColorSwatchPickerItemProps) => { - return ( - item({ className }))} - style={composeRenderProps( - style, - (style, { color }) => - ({ - "--color": color.toString(), - ...style, - }) as React.CSSProperties, - )} - {...props} - > - - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -const CompoundColorSwatchPicker = Object.assign(ColorSwatchPicker, { - Item: ColorSwatchPickerItem, -}); - -export type { ColorSwatchPickerProps, ColorSwatchPickerItemProps }; -export { CompoundColorSwatchPicker as ColorSwatchPicker, ColorSwatchPickerItem }; -`, - }, - ], - registryDependencies: ["focus-styles", "color-swatch"], - }, - }, - }, - { - name: "color-swatch", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/color-swatch/basic.tsx", - target: "ui/color-swatch.tsx", - content: `"use client"; - -import { ColorSwatch as AriaColorSwatch, composeRenderProps } from "react-aria-components"; -import { tv } from "tailwind-variants"; - -const colorSwatchStyles = tv({ - base: "relative size-5 rounded-sm border", -}); - -interface ColorSwatchProps extends React.ComponentProps {} -const ColorSwatch = ({ className, style, ...props }: ColorSwatchProps) => { - return ( - colorSwatchStyles({ className }))} - style={composeRenderProps(style, (style, { color }) => ({ - ...style, - background: \`linear-gradient(\${color}, \${color}), - repeating-conic-gradient(#CCC 0% 25%, white 0% 50%) 50% / 16px 16px\`, - }))} - {...props} - /> - ); -}; - -export type { ColorSwatchProps }; -export { ColorSwatch }; -`, - }, - ], - }, - }, - }, - { - name: "color-thumb", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/color-thumb/basic.tsx", - target: "ui/color-thumb.tsx", - content: `"use client"; - -import { ColorThumb as AriaColorThumb } from "react-aria-components"; -import { tv } from "tailwind-variants"; -import type { ColorThumbProps as AriaColorThumbProps } from "react-aria-components"; - -const colorThumbStyles = tv({ - base: [ - "focus-reset focus-visible:focus-ring", - "z-30 size-6 rounded-full border-2 border-white ring-1 ring-black/40 disabled:border-border-disabled disabled:bg-disabled!", - "group-orientation-horizontal/color-slider:top-1/2 group-orientation-vertical/color-slider:left-1/2", - ], -}); - -interface ColorThumbProps extends Omit { - className?: string; -} -const ColorThumb = ({ className, ...props }: ColorThumbProps) => { - return ; -}; - -export type { ColorThumbProps }; -export { ColorThumb }; -`, - }, - ], - registryDependencies: ["focus-styles"], - }, - }, - }, - { - name: "combobox", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/combobox/basic.tsx", - target: "ui/combobox.tsx", - content: `"use client"; - -import React from "react"; -import { useResizeObserver } from "@react-aria/utils"; -import { ChevronDownIcon } from "lucide-react"; -import { mergeProps } from "react-aria"; -import { - ComboBox as AriaCombobox, - GroupContext as AriaGroupContext, - PopoverContext as AriaPopoverContext, - composeRenderProps, - Provider, -} from "react-aria-components"; -import type { ComboBoxProps as AriaComboboxProps } from "react-aria-components"; - -import { cn } from "@dotui/registry/lib/utils"; -import { Button } from "@dotui/registry/ui/button"; -import { fieldStyles } from "@dotui/registry/ui/field"; -import { Input, InputAddon, InputGroup } from "@dotui/registry/ui/input"; -import { - ListBox, - ListBoxItem, - ListBoxSection, - ListBoxSectionHeader, - ListBoxVirtualizer, -} from "@dotui/registry/ui/list-box"; -import { Popover } from "@dotui/registry/ui/popover"; -import type { InputGroupProps } from "@dotui/registry/ui/input"; -import type { ListBoxProps } from "@dotui/registry/ui/list-box"; -import type { PopoverProps } from "@dotui/registry/ui/popover"; - -/* -----------------------------------------------------------------------------------------------*/ - -interface ComboboxProps extends Omit, "className"> { - className?: string; -} -const Combobox = ({ menuTrigger = "focus", className, ...props }: ComboboxProps) => { - return ( - - {composeRenderProps(props.children, (children) => ( - {children} - ))} - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -/** - * This abstraction allows the Combobox to work with InputGroup and - * sync the trigger width with the popover dropdown. - */ - -const ComboboxInner = ({ children }: { children: React.ReactNode }) => { - const [menuWidth, setMenuWidth] = React.useState(undefined); - - const groupProps = React.use(AriaGroupContext); - const popoverProps = React.use(AriaPopoverContext); - const triggerRef = React.useRef(null); - - const onResize = React.useCallback(() => { - if (triggerRef.current) { - const triggerWidth = triggerRef.current.getBoundingClientRect().width; - setMenuWidth(\`\${triggerWidth}px\`); - } - }, []); - - useResizeObserver({ - ref: triggerRef, - onResize: onResize, - }); - - return ( - - {children} - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface ComboboxInputProps extends InputGroupProps { - placeholder?: string; -} - -const ComboboxInput = ({ placeholder, ...props }: ComboboxInputProps) => { - return ( - - - - - - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface ComboboxContentProps - extends ListBoxProps, - Pick { - virtulized?: boolean; -} - -const ComboboxContent = ({ - virtulized, - placement, - defaultOpen, - isOpen, - onOpenChange, - ...props -}: ComboboxContentProps) => { - if (virtulized) { - return ( - - - - - - ); - } - - return ( - - - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -export { - Combobox, - ComboboxInput, - ComboboxContent, - ListBoxItem as ComboboxItem, - ListBoxSection as ComboboxSection, - ListBoxSectionHeader as ComboboxSectionHeader, -}; - -export type { ComboboxProps, ComboboxInputProps, ComboboxContentProps }; -`, - }, - ], - registryDependencies: ["field", "button", "input", "list-box", "overlay"], - }, - }, - }, - { - name: "command", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/command/basic.tsx", - target: "ui/command.tsx", - content: `"use client"; - -import { SearchIcon } from "lucide-react"; -import { Autocomplete as AriaAutocomplete, useFilter } from "react-aria-components"; -import { tv } from "tailwind-variants"; - -import { Input, InputAddon, InputGroup } from "@dotui/registry/ui/input"; -import { - ListBox, - ListBoxItem, - ListBoxSection, - ListBoxSectionHeader, - ListBoxVirtualizer, -} from "@dotui/registry/ui/list-box"; -import { SearchField } from "@dotui/registry/ui/search-field"; -import type { ListBoxProps } from "@dotui/registry/ui/list-box"; -import type { PopoverProps } from "@dotui/registry/ui/popover"; -import type { SearchFieldProps } from "@dotui/registry/ui/search-field"; - -const commandStyles = tv({ - slots: { - base: [ - "in-drawer:rounded-[inherit] in-modal:rounded-[inherit] in-popover:rounded-[inherit] rounded-lg not-in-popover:not-in-modal:not-in-drawer:border not-in-popover:not-in-modal:not-in-drawer:bg-card", - "**:data-[slot=list-box]:w-full **:data-[slot=list-box]:border-0 **:data-[slot=list-box]:bg-transparent", - "**:data-[slot=search-field]:w-full **:data-[slot=search-field]:outline-none [&_[data-slot=search-field]_[data-slot=input-group]]:rounded-b-none [&_[data-slot=search-field]_[data-slot=input-group]]:border-0 [&_[data-slot=search-field]_[data-slot=input-group]]:border-b [&_[data-slot=search-field]_[data-slot=input-group]]:bg-transparent", - "in-modal:w-full", - ], - }, -}); - -const { base } = commandStyles(); - -/* -----------------------------------------------------------------------------------------------*/ - -interface CommandProps extends React.ComponentProps<"div"> {} - -function Command({ className, ...props }: CommandProps) { - const { contains } = useFilter({ - sensitivity: "base", - ignorePunctuation: true, - }); - - return ( - -
-
- ); -} - -/* -----------------------------------------------------------------------------------------------*/ - -interface CommandInputProps extends SearchFieldProps { - placeholder?: string; -} - -const CommandInput = ({ placeholder, ...props }: CommandInputProps) => { - return ( - - {/* TODO: Remove this */} - - - - - - - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface CommandContentProps extends ListBoxProps { - placement?: PopoverProps["placement"]; - virtulized?: boolean; -} - -const CommandContent = ({ virtulized, placement, ...props }: CommandContentProps) => { - if (virtulized) { - return ( - - - - ); - } - - return ; -}; - -/* -----------------------------------------------------------------------------------------------*/ - -export { - Command, - CommandInput, - CommandContent, - ListBoxItem as CommandItem, - ListBoxSection as CommandSection, - ListBoxSectionHeader as CommandSectionHeader, -}; - -export type { CommandProps, CommandContentProps, CommandInputProps }; -`, - }, - ], - }, - }, - }, - { - name: "date-field", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/date-field/basic.tsx", - target: "ui/date-field.tsx", - content: `"use client"; - -import { DateField as AriaDateField, composeRenderProps } from "react-aria-components"; -import { tv } from "tailwind-variants"; -import type { DateFieldProps as AriaDateFieldProps, DateValue } from "react-aria-components"; - -import { fieldStyles } from "@dotui/registry/ui/field"; - -const dateFieldStyles = tv({ - base: [fieldStyles().field({ orientation: "vertical" })], -}); - -/* -----------------------------------------------------------------------------------------------*/ - -interface DateFieldProps extends AriaDateFieldProps {} - -const DateField = ({ className, ...props }: DateFieldProps) => { - return ( - dateFieldStyles({ className }))} - {...props} - /> - ); -}; - -export type { DateFieldProps }; -export { DateField }; -`, - }, - ], - registryDependencies: ["field", "input"], - }, - }, - }, - { - name: "date-picker", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/date-picker/basic.tsx", - target: "ui/date-picker.tsx", - content: `"use client"; - -import { useContext } from "react"; -import { CalendarIcon } from "lucide-react"; -import { - DateRangePicker as AriaDataRangePicker, - DatePicker as AriaDatePicker, - RangeCalendarContext as AriaRangeCalendarContext, - composeRenderProps, -} from "react-aria-components"; -import { tv } from "tailwind-variants"; -import type { - DateRangePickerProps as AriaDataRangePickerProps, - DatePickerProps as AriaDatePickerProps, - DateValue, -} from "react-aria-components"; - -import { Button } from "@dotui/registry/ui/button"; -import { DialogContent, type DialogContentProps } from "@dotui/registry/ui/dialog"; -import { DateInput, InputAddon, InputGroup } from "@dotui/registry/ui/input"; -import { Overlay, type OverlayProps } from "@dotui/registry/ui/overlay"; -import type { InputGroupProps } from "@dotui/registry/ui/input"; - -const datePickerStyles = tv({ - base: "flex flex-col items-start gap-2", -}); - -type DatePickerProps = - | ({ - mode?: "single"; - } & AriaDatePickerProps) - | ({ - mode: "range"; - } & AriaDataRangePickerProps); - -const DatePicker = ({ mode = "single", className, ...props }: DatePickerProps) => { - if (mode === "range") { - return ( - ["className"], (className) => - datePickerStyles({ className }), - )} - {...(props as AriaDataRangePickerProps)} - /> - ); - } - - return ( - ["className"], (className) => - datePickerStyles({ className }), - )} - {...(props as AriaDatePickerProps)} - /> - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface DatePickerInputProps extends InputGroupProps {} - -const DatePickerInput = (props: DatePickerInputProps) => { - const rangeCalendarContext = useContext(AriaRangeCalendarContext); - const mode = rangeCalendarContext ? "range" : "single"; - - return ( - - {mode === "single" ? ( - - ) : ( - <> - - - - - )} - - - - - ); -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface DatePickerContentProps - extends DialogContentProps, - Pick {} - -const DatePickerContent = ({ - children, - type = "popover", - mobileType, - popoverProps, - ...props -}: DatePickerContentProps) => { - return ( - - {children} - - ); -}; - -export type { DatePickerProps, DatePickerContentProps, DatePickerInputProps }; -export { DatePicker, DatePickerContent, DatePickerInput }; -`, - }, - ], - registryDependencies: ["button", "calendar", "field", "input", "dialog"], - }, - }, - }, - { - name: "dialog", - type: "registry:ui", - defaultVariant: "basic", - variants: { - basic: { - files: [ - { - type: "registry:ui", - path: "ui/dialog/basic.tsx", - target: "ui/dialog.tsx", - content: `"use client"; - -import { - Dialog as AriaDialog, - DialogTrigger as AriaDialogTrigger, - Heading as AriaHeading, - Text as AriaText, -} from "react-aria-components"; -import { tv } from "tailwind-variants"; -import type * as React from "react"; - -const dialogStyles = tv({ - slots: { - content: "relative flex flex-col gap-4 in-data-popover:p-4 p-6 outline-none", - header: "flex flex-col gap-2 text-left", - heading: "font-semibold in-popover:font-medium in-popover:text-base text-lg leading-none", - description: "text-fg-muted text-sm", - body: "flex flex-1 flex-col gap-2", - inset: "-mx-6 in-popover:-mx-4 border bg-muted in-popover:px-4 px-6 py-4", - footer: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", - }, -}); - -const { content, header, heading, description, body, footer, inset } = dialogStyles(); - -/* -----------------------------------------------------------------------------------------------*/ - -interface DialogProps extends React.ComponentProps {} - -const Dialog = (props: DialogProps) => { - return ; -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface DialogContentProps extends React.ComponentProps {} - -const DialogContent = ({ className, ...props }: DialogContentProps) => { - return ; -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface DialogHeaderProps extends React.ComponentProps<"header"> {} - -const DialogHeader = ({ className, ...props }: DialogHeaderProps) => { - return
; -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface DialogHeadingProps extends React.ComponentProps {} - -const DialogHeading = ({ className, ...props }: DialogHeadingProps) => { - return ; -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface DialogDescriptionProps extends Omit, "slot"> {} - -const DialogDescription = ({ className, ...props }: DialogDescriptionProps) => { - return ; -}; - -/* -----------------------------------------------------------------------------------------------*/ - -interface DialogBodyProps extends React.ComponentProps<"div"> {} - -const DialogBody = ({ className, ...props }: DialogBodyProps) => { - return
; -}; - -/* -----------------------------------------------------------------------------------------------*/ - -type DialogFooterProps = React.ComponentProps<"footer">; - -const DialogFooter = ({ className, ...props }: DialogFooterProps) => { - return