diff --git a/apps/docs/package.json b/apps/docs/package.json index a5fc76b..b23ab90 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -23,6 +23,9 @@ "@solidjs/start": "^1.2.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "embla-carousel": "^8.6.0", + "embla-carousel-autoplay": "^8.6.0", + "embla-carousel-solid": "^8.6.0", "lucide-solid": "^0.562.0", "rimraf": "^6.0.1", "solid-js": "^1.9.9", @@ -38,7 +41,7 @@ "tailwindcss": "^4.1.14", "tsx": "^4.20.6", "tw-animate-css": "^1.4.0", - "vite": "^6.3.5", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "vite": "^6.3.5" } } diff --git a/apps/docs/src/config/docs.ts b/apps/docs/src/config/docs.ts index aad495b..c5e427e 100644 --- a/apps/docs/src/config/docs.ts +++ b/apps/docs/src/config/docs.ts @@ -75,6 +75,11 @@ export const docsConfig: Config = { title: "Card", href: "/docs/components/card" }, + { + title: "Carousel", + href: "/docs/components/carousel", + status: "new" + }, { title: "Checkbox", href: "/docs/components/checkbox" diff --git a/apps/docs/src/registry/__index__.tsx b/apps/docs/src/registry/__index__.tsx index 5c187dd..ba1a190 100644 --- a/apps/docs/src/registry/__index__.tsx +++ b/apps/docs/src/registry/__index__.tsx @@ -4,47 +4,53 @@ import { lazy } from "solid-js" export const Index: Record = { - "utils": { + utils: { name: "utils", description: "", type: "registry:lib", registryDependencies: undefined, component: lazy(() => import("~/registry/lib/utils.ts")), - files: [{ - path: "registry/lib/utils.ts", - type: "registry:lib", - target: "" - }], + files: [ + { + path: "registry/lib/utils.ts", + type: "registry:lib", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "accordion": { + accordion: { name: "accordion", description: "", type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/accordion.tsx")), - files: [{ - path: "registry/ui/accordion.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/accordion.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "alert": { + alert: { name: "alert", description: "", type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/alert.tsx")), - files: [{ - path: "registry/ui/alert.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/alert.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "alert-dialog": { name: "alert-dialog", @@ -52,13 +58,15 @@ export const Index: Record = { type: "registry:ui", registryDependencies: ["button"], component: lazy(() => import("~/registry/ui/alert-dialog.tsx")), - files: [{ - path: "registry/ui/alert-dialog.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/alert-dialog.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "aspect-ratio": { name: "aspect-ratio", @@ -66,111 +74,143 @@ export const Index: Record = { type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/aspect-ratio.tsx")), - files: [{ - path: "registry/ui/aspect-ratio.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/aspect-ratio.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "avatar": { + avatar: { name: "avatar", description: "", type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/avatar.tsx")), - files: [{ - path: "registry/ui/avatar.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/avatar.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "badge": { + badge: { name: "badge", description: "", type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/badge.tsx")), - files: [{ - path: "registry/ui/badge.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/badge.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "breadcrumb": { + breadcrumb: { name: "breadcrumb", description: "", type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/breadcrumb.tsx")), - files: [{ - path: "registry/ui/breadcrumb.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/breadcrumb.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "button": { + button: { name: "button", description: "", type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/button.tsx")), - files: [{ - path: "registry/ui/button.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/button.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "card": { + card: { name: "card", description: "", type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/card.tsx")), - files: [{ - path: "registry/ui/card.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/card.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-group": { name: "button-group", description: "", type: "registry:ui", - registryDependencies: ["button","separator"], + registryDependencies: ["button", "separator"], component: lazy(() => import("~/registry/ui/button-group.tsx")), - files: [{ - path: "registry/ui/button-group.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/button-group.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "checkbox": { + carousel: { + name: "carousel", + description: "", + type: "registry:ui", + registryDependencies: ["button"], + component: lazy(() => import("~/registry/ui/carousel.tsx")), + files: [ + { + path: "registry/ui/carousel.tsx", + type: "registry:ui", + target: "" + } + ], + categories: undefined, + meta: undefined + }, + checkbox: { name: "checkbox", description: "", type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/checkbox.tsx")), - files: [{ - path: "registry/ui/checkbox.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/checkbox.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "dropdown-menu": { name: "dropdown-menu", @@ -178,55 +218,63 @@ export const Index: Record = { type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/dropdown-menu.tsx")), - files: [{ - path: "registry/ui/dropdown-menu.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/dropdown-menu.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "field": { + field: { name: "field", description: "", type: "registry:ui", - registryDependencies: ["label","separator"], + registryDependencies: ["label", "separator"], component: lazy(() => import("~/registry/ui/field.tsx")), - files: [{ - path: "registry/ui/field.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/field.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "input": { + input: { name: "input", description: "", type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/input.tsx")), - files: [{ - path: "registry/ui/input.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/input.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "input-group": { name: "input-group", description: "", type: "registry:ui", - registryDependencies: ["button","input","textarea"], + registryDependencies: ["button", "input", "textarea"], component: lazy(() => import("~/registry/ui/input-group.tsx")), - files: [{ - path: "registry/ui/input-group.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/input-group.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "radio-group": { name: "radio-group", @@ -234,55 +282,63 @@ export const Index: Record = { type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/radio-group.tsx")), - files: [{ - path: "registry/ui/radio-group.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/radio-group.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "select": { + select: { name: "select", description: "", type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/select.tsx")), - files: [{ - path: "registry/ui/select.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/select.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "slider": { + slider: { name: "slider", description: "", type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/slider.tsx")), - files: [{ - path: "registry/ui/slider.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/slider.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, - "switch": { + switch: { name: "switch", description: "", type: "registry:ui", registryDependencies: undefined, component: lazy(() => import("~/registry/ui/switch.tsx")), - files: [{ - path: "registry/ui/switch.tsx", - type: "registry:ui", - target: "" - }], + files: [ + { + path: "registry/ui/switch.tsx", + type: "registry:ui", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "use-mobile": { name: "use-mobile", @@ -290,13 +346,15 @@ export const Index: Record = { type: "registry:hook", registryDependencies: undefined, component: lazy(() => import("~/registry/hooks/use-mobile.ts")), - files: [{ - path: "registry/hooks/use-mobile.ts", - type: "registry:hook", - target: "" - }], + files: [ + { + path: "registry/hooks/use-mobile.ts", + type: "registry:hook", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "accordion-demo": { name: "accordion-demo", @@ -304,13 +362,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["accordion"], component: lazy(() => import("~/registry/examples/accordion-demo.tsx")), - files: [{ - path: "registry/examples/accordion-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/accordion-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "alert-demo": { name: "alert-demo", @@ -318,27 +378,31 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["alert"], component: lazy(() => import("~/registry/examples/alert-demo.tsx")), - files: [{ - path: "registry/examples/alert-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/alert-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "alert-dialog-demo": { name: "alert-dialog-demo", description: "", type: "registry:example", - registryDependencies: ["alert-dialog","button"], + registryDependencies: ["alert-dialog", "button"], component: lazy(() => import("~/registry/examples/alert-dialog-demo.tsx")), - files: [{ - path: "registry/examples/alert-dialog-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/alert-dialog-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "aspect-ratio-demo": { name: "aspect-ratio-demo", @@ -346,13 +410,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["aspect-ratio"], component: lazy(() => import("~/registry/examples/aspect-ratio-demo.tsx")), - files: [{ - path: "registry/examples/aspect-ratio-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/aspect-ratio-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "avatar-demo": { name: "avatar-demo", @@ -360,13 +426,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["avatar"], component: lazy(() => import("~/registry/examples/avatar-demo.tsx")), - files: [{ - path: "registry/examples/avatar-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/avatar-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "badge-demo": { name: "badge-demo", @@ -374,13 +442,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["badge"], component: lazy(() => import("~/registry/examples/badge-demo.tsx")), - files: [{ - path: "registry/examples/badge-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/badge-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "breadcrumb-demo": { name: "breadcrumb-demo", @@ -388,13 +458,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["breadcrumb"], component: lazy(() => import("~/registry/examples/breadcrumb-demo.tsx")), - files: [{ - path: "registry/examples/breadcrumb-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/breadcrumb-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "breadcrumb-collapsed": { name: "breadcrumb-collapsed", @@ -402,27 +474,31 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["breadcrumb"], component: lazy(() => import("~/registry/examples/breadcrumb-collapsed.tsx")), - files: [{ - path: "registry/examples/breadcrumb-collapsed.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/breadcrumb-collapsed.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "breadcrumb-dropdown": { name: "breadcrumb-dropdown", description: "", type: "registry:example", - registryDependencies: ["breadcrumb","dropdown-menu","button"], + registryDependencies: ["breadcrumb", "dropdown-menu", "button"], component: lazy(() => import("~/registry/examples/breadcrumb-dropdown.tsx")), - files: [{ - path: "registry/examples/breadcrumb-dropdown.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/breadcrumb-dropdown.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "breadcrumb-link": { name: "breadcrumb-link", @@ -430,13 +506,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["breadcrumb"], component: lazy(() => import("~/registry/examples/breadcrumb-link.tsx")), - files: [{ - path: "registry/examples/breadcrumb-link.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/breadcrumb-link.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "breadcrumb-separator": { name: "breadcrumb-separator", @@ -444,13 +522,111 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["breadcrumb"], component: lazy(() => import("~/registry/examples/breadcrumb-separator.tsx")), - files: [{ - path: "registry/examples/breadcrumb-separator.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/breadcrumb-separator.tsx", + type: "registry:example", + target: "" + } + ], + categories: undefined, + meta: undefined + }, + "carousel-demo": { + name: "carousel-demo", + description: "", + type: "registry:example", + registryDependencies: ["carousel", "card"], + component: lazy(() => import("~/registry/examples/carousel-demo.tsx")), + files: [ + { + path: "registry/examples/carousel-demo.tsx", + type: "registry:example", + target: "" + } + ], + categories: undefined, + meta: undefined + }, + "carousel-size": { + name: "carousel-size", + description: "", + type: "registry:example", + registryDependencies: ["carousel", "card"], + component: lazy(() => import("~/registry/examples/carousel-size.tsx")), + files: [ + { + path: "registry/examples/carousel-size.tsx", + type: "registry:example", + target: "" + } + ], + categories: undefined, + meta: undefined + }, + "carousel-spacing": { + name: "carousel-spacing", + description: "", + type: "registry:example", + registryDependencies: ["carousel", "card"], + component: lazy(() => import("~/registry/examples/carousel-spacing.tsx")), + files: [ + { + path: "registry/examples/carousel-spacing.tsx", + type: "registry:example", + target: "" + } + ], + categories: undefined, + meta: undefined + }, + "carousel-orientation": { + name: "carousel-orientation", + description: "", + type: "registry:example", + registryDependencies: ["carousel", "card"], + component: lazy(() => import("~/registry/examples/carousel-orientation.tsx")), + files: [ + { + path: "registry/examples/carousel-orientation.tsx", + type: "registry:example", + target: "" + } + ], + categories: undefined, + meta: undefined + }, + "carousel-api": { + name: "carousel-api", + description: "", + type: "registry:example", + registryDependencies: ["carousel", "card"], + component: lazy(() => import("~/registry/examples/carousel-api.tsx")), + files: [ + { + path: "registry/examples/carousel-api.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined + }, + "carousel-plugin": { + name: "carousel-plugin", + description: "", + type: "registry:example", + registryDependencies: ["carousel", "card"], + component: lazy(() => import("~/registry/examples/carousel-plugin.tsx")), + files: [ + { + path: "registry/examples/carousel-plugin.tsx", + type: "registry:example", + target: "" + } + ], + categories: undefined, + meta: undefined }, "button-default": { name: "button-default", @@ -458,13 +634,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["button"], component: lazy(() => import("~/registry/examples/button-default.tsx")), - files: [{ - path: "registry/examples/button-default.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-default.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-demo": { name: "button-demo", @@ -472,13 +650,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["button"], component: lazy(() => import("~/registry/examples/button-demo.tsx")), - files: [{ - path: "registry/examples/button-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-destructive": { name: "button-destructive", @@ -486,13 +666,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["button"], component: lazy(() => import("~/registry/examples/button-destructive.tsx")), - files: [{ - path: "registry/examples/button-destructive.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-destructive.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-ghost": { name: "button-ghost", @@ -500,153 +682,175 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["button"], component: lazy(() => import("~/registry/examples/button-ghost.tsx")), - files: [{ - path: "registry/examples/button-ghost.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-ghost.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-group-demo": { name: "button-group-demo", description: "", type: "registry:example", - registryDependencies: ["button","button-group","dropdown-menu"], + registryDependencies: ["button", "button-group", "dropdown-menu"], component: lazy(() => import("~/registry/examples/button-group-demo.tsx")), - files: [{ - path: "registry/examples/button-group-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-group-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-group-dropdown-menu": { name: "button-group-dropdown-menu", description: "", type: "registry:example", - registryDependencies: ["button","button-group","dropdown-menu"], + registryDependencies: ["button", "button-group", "dropdown-menu"], component: lazy(() => import("~/registry/examples/button-group-dropdown-menu.tsx")), - files: [{ - path: "registry/examples/button-group-dropdown-menu.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-group-dropdown-menu.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-group-input-group": { name: "button-group-input-group", description: "", type: "registry:example", - registryDependencies: ["button","button-group","input-group"], + registryDependencies: ["button", "button-group", "input-group"], component: lazy(() => import("~/registry/examples/button-group-input-group.tsx")), - files: [{ - path: "registry/examples/button-group-input-group.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-group-input-group.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-group-input": { name: "button-group-input", description: "", type: "registry:example", - registryDependencies: ["button","button-group","input"], + registryDependencies: ["button", "button-group", "input"], component: lazy(() => import("~/registry/examples/button-group-input.tsx")), - files: [{ - path: "registry/examples/button-group-input.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-group-input.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-group-nested": { name: "button-group-nested", description: "", type: "registry:example", - registryDependencies: ["button","button-group"], + registryDependencies: ["button", "button-group"], component: lazy(() => import("~/registry/examples/button-group-nested.tsx")), - files: [{ - path: "registry/examples/button-group-nested.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-group-nested.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-group-orientation": { name: "button-group-orientation", description: "", type: "registry:example", - registryDependencies: ["button","button-group"], + registryDependencies: ["button", "button-group"], component: lazy(() => import("~/registry/examples/button-group-orientation.tsx")), - files: [{ - path: "registry/examples/button-group-orientation.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-group-orientation.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-group-select": { name: "button-group-select", description: "", type: "registry:example", - registryDependencies: ["button","button-group","select"], + registryDependencies: ["button", "button-group", "select"], component: lazy(() => import("~/registry/examples/button-group-select.tsx")), - files: [{ - path: "registry/examples/button-group-select.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-group-select.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-group-separator": { name: "button-group-separator", description: "", type: "registry:example", - registryDependencies: ["button","button-group"], + registryDependencies: ["button", "button-group"], component: lazy(() => import("~/registry/examples/button-group-separator.tsx")), - files: [{ - path: "registry/examples/button-group-separator.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-group-separator.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-group-size": { name: "button-group-size", description: "", type: "registry:example", - registryDependencies: ["button","button-group"], + registryDependencies: ["button", "button-group"], component: lazy(() => import("~/registry/examples/button-group-size.tsx")), - files: [{ - path: "registry/examples/button-group-size.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-group-size.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-group-split": { name: "button-group-split", description: "", type: "registry:example", - registryDependencies: ["button","button-group"], + registryDependencies: ["button", "button-group"], component: lazy(() => import("~/registry/examples/button-group-split.tsx")), - files: [{ - path: "registry/examples/button-group-split.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-group-split.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-icon": { name: "button-icon", @@ -654,13 +858,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["button"], component: lazy(() => import("~/registry/examples/button-icon.tsx")), - files: [{ - path: "registry/examples/button-icon.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-icon.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-link": { name: "button-link", @@ -668,27 +874,31 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["button"], component: lazy(() => import("~/registry/examples/button-link.tsx")), - files: [{ - path: "registry/examples/button-link.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-link.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-loading": { name: "button-loading", description: "", type: "registry:example", - registryDependencies: ["button","spinner"], + registryDependencies: ["button", "spinner"], component: lazy(() => import("~/registry/examples/button-loading.tsx")), - files: [{ - path: "registry/examples/button-loading.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-loading.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-outline": { name: "button-outline", @@ -696,13 +906,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["button"], component: lazy(() => import("~/registry/examples/button-outline.tsx")), - files: [{ - path: "registry/examples/button-outline.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-outline.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-rounded": { name: "button-rounded", @@ -710,13 +922,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["button"], component: lazy(() => import("~/registry/examples/button-rounded.tsx")), - files: [{ - path: "registry/examples/button-rounded.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-rounded.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-secondary": { name: "button-secondary", @@ -724,13 +938,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["button"], component: lazy(() => import("~/registry/examples/button-secondary.tsx")), - files: [{ - path: "registry/examples/button-secondary.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-secondary.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-sizes": { name: "button-sizes", @@ -738,13 +954,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["button"], component: lazy(() => import("~/registry/examples/button-sizes.tsx")), - files: [{ - path: "registry/examples/button-sizes.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-sizes.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "button-with-icon": { name: "button-with-icon", @@ -752,27 +970,31 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["button"], component: lazy(() => import("~/registry/examples/button-with-icon.tsx")), - files: [{ - path: "registry/examples/button-with-icon.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/button-with-icon.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "card-demo": { name: "card-demo", description: "", type: "registry:example", - registryDependencies: ["card","button","input","label"], + registryDependencies: ["card", "button", "input", "label"], component: lazy(() => import("~/registry/examples/card-demo.tsx")), - files: [{ - path: "registry/examples/card-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/card-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "dropdown-menu-checkboxes": { name: "dropdown-menu-checkboxes", @@ -780,13 +1002,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["dropdown-menu"], component: lazy(() => import("~/registry/examples/dropdown-menu-checkboxes.tsx")), - files: [{ - path: "registry/examples/dropdown-menu-checkboxes.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/dropdown-menu-checkboxes.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "checkbox-demo": { name: "checkbox-demo", @@ -794,13 +1018,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["checkbox"], component: lazy(() => import("~/registry/examples/checkbox-demo.tsx")), - files: [{ - path: "registry/examples/checkbox-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/checkbox-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "dropdown-menu-demo": { name: "dropdown-menu-demo", @@ -808,13 +1034,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["dropdown-menu"], component: lazy(() => import("~/registry/examples/dropdown-menu-demo.tsx")), - files: [{ - path: "registry/examples/dropdown-menu-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/dropdown-menu-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "dropdown-menu-dialog": { name: "dropdown-menu-dialog", @@ -822,13 +1050,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["dropdown-menu"], component: lazy(() => import("~/registry/examples/dropdown-menu-dialog.tsx")), - files: [{ - path: "registry/examples/dropdown-menu-dialog.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/dropdown-menu-dialog.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "dropdown-menu-radio-group": { name: "dropdown-menu-radio-group", @@ -836,41 +1066,47 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["dropdown-menu"], component: lazy(() => import("~/registry/examples/dropdown-menu-radio-group.tsx")), - files: [{ - path: "registry/examples/dropdown-menu-radio-group.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/dropdown-menu-radio-group.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "field-checkbox": { name: "field-checkbox", description: "", type: "registry:example", - registryDependencies: ["field","checkbox"], + registryDependencies: ["field", "checkbox"], component: lazy(() => import("~/registry/examples/field-checkbox.tsx")), - files: [{ - path: "registry/examples/field-checkbox.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/field-checkbox.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "field-choice-card": { name: "field-choice-card", description: "", type: "registry:example", - registryDependencies: ["field","radio-group"], + registryDependencies: ["field", "radio-group"], component: lazy(() => import("~/registry/examples/field-choice-card.tsx")), - files: [{ - path: "registry/examples/field-choice-card.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/field-choice-card.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "field-demo": { name: "field-demo", @@ -878,139 +1114,159 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["field"], component: lazy(() => import("~/registry/examples/field-demo.tsx")), - files: [{ - path: "registry/examples/field-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/field-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "field-fieldset": { name: "field-fieldset", description: "", type: "registry:example", - registryDependencies: ["field","input"], + registryDependencies: ["field", "input"], component: lazy(() => import("~/registry/examples/field-fieldset.tsx")), - files: [{ - path: "registry/examples/field-fieldset.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/field-fieldset.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "field-group-example": { name: "field-group-example", description: "", type: "registry:example", - registryDependencies: ["field","checkbox"], + registryDependencies: ["field", "checkbox"], component: lazy(() => import("~/registry/examples/field-group-example.tsx")), - files: [{ - path: "registry/examples/field-group-example.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/field-group-example.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "field-input": { name: "field-input", description: "", type: "registry:example", - registryDependencies: ["field","input"], + registryDependencies: ["field", "input"], component: lazy(() => import("~/registry/examples/field-input.tsx")), - files: [{ - path: "registry/examples/field-input.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/field-input.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "field-radio": { name: "field-radio", description: "", type: "registry:example", - registryDependencies: ["field","radio-group"], + registryDependencies: ["field", "radio-group"], component: lazy(() => import("~/registry/examples/field-radio.tsx")), - files: [{ - path: "registry/examples/field-radio.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/field-radio.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "field-responsive": { name: "field-responsive", description: "", type: "registry:example", - registryDependencies: ["field","button","input","textarea"], + registryDependencies: ["field", "button", "input", "textarea"], component: lazy(() => import("~/registry/examples/field-responsive.tsx")), - files: [{ - path: "registry/examples/field-responsive.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/field-responsive.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "field-select": { name: "field-select", description: "", type: "registry:example", - registryDependencies: ["field","select"], + registryDependencies: ["field", "select"], component: lazy(() => import("~/registry/examples/field-select.tsx")), - files: [{ - path: "registry/examples/field-select.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/field-select.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "field-slider": { name: "field-slider", description: "", type: "registry:example", - registryDependencies: ["field","slider"], + registryDependencies: ["field", "slider"], component: lazy(() => import("~/registry/examples/field-slider.tsx")), - files: [{ - path: "registry/examples/field-slider.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/field-slider.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "field-switch": { name: "field-switch", description: "", type: "registry:example", - registryDependencies: ["field","switch"], + registryDependencies: ["field", "switch"], component: lazy(() => import("~/registry/examples/field-switch.tsx")), - files: [{ - path: "registry/examples/field-switch.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/field-switch.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "field-textarea": { name: "field-textarea", description: "", type: "registry:example", - registryDependencies: ["field","textarea"], + registryDependencies: ["field", "textarea"], component: lazy(() => import("~/registry/examples/field-textarea.tsx")), - files: [{ - path: "registry/examples/field-textarea.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/field-textarea.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "input-group-demo": { name: "input-group-demo", @@ -1018,27 +1274,31 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["input-group"], component: lazy(() => import("~/registry/examples/input-group-demo.tsx")), - files: [{ - path: "registry/examples/input-group-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/input-group-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "radio-group-demo": { name: "radio-group-demo", description: "", type: "registry:example", - registryDependencies: ["radio-group","label"], + registryDependencies: ["radio-group", "label"], component: lazy(() => import("~/registry/examples/radio-group-demo.tsx")), - files: [{ - path: "registry/examples/radio-group-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/radio-group-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "select-demo": { name: "select-demo", @@ -1046,13 +1306,15 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["select"], component: lazy(() => import("~/registry/examples/select-demo.tsx")), - files: [{ - path: "registry/examples/select-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/select-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "slider-demo": { name: "slider-demo", @@ -1060,27 +1322,31 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["slider"], component: lazy(() => import("~/registry/examples/slider-demo.tsx")), - files: [{ - path: "registry/examples/slider-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/slider-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "switch-demo": { name: "switch-demo", description: "", type: "registry:example", - registryDependencies: ["switch","label"], + registryDependencies: ["switch", "label"], component: lazy(() => import("~/registry/examples/switch-demo.tsx")), - files: [{ - path: "registry/examples/switch-demo.tsx", - type: "registry:example", - target: "" - }], + files: [ + { + path: "registry/examples/switch-demo.tsx", + type: "registry:example", + target: "" + } + ], categories: undefined, - meta: undefined, + meta: undefined }, "tooltip-demo": { name: "tooltip-demo", @@ -1088,12 +1354,14 @@ export const Index: Record = { type: "registry:example", registryDependencies: ["tooltip"], component: lazy(() => import("~/registry/examples/tooltip-demo.tsx")), - files: [{ - path: "registry/examples/tooltip-demo.tsx", - type: "registry:example", - target: "" - }], - categories: undefined, - meta: undefined, - }, + files: [ + { + path: "registry/examples/tooltip-demo.tsx", + type: "registry:example", + target: "" + } + ], + categories: undefined, + meta: undefined + } } diff --git a/apps/docs/src/registry/examples/_registry.ts b/apps/docs/src/registry/examples/_registry.ts index 88afe4e..6433cc3 100644 --- a/apps/docs/src/registry/examples/_registry.ts +++ b/apps/docs/src/registry/examples/_registry.ts @@ -122,6 +122,73 @@ export const examples: Registry["items"] = [ } ] }, + { + name: "carousel-demo", + type: "registry:example", + registryDependencies: ["carousel", "card"], + files: [ + { + path: "examples/carousel-demo.tsx", + type: "registry:example" + } + ] + }, + { + name: "carousel-size", + type: "registry:example", + registryDependencies: ["carousel", "card"], + files: [ + { + path: "examples/carousel-size.tsx", + type: "registry:example" + } + ] + }, + { + name: "carousel-spacing", + type: "registry:example", + registryDependencies: ["carousel", "card"], + files: [ + { + path: "examples/carousel-spacing.tsx", + type: "registry:example" + } + ] + }, + { + name: "carousel-orientation", + type: "registry:example", + registryDependencies: ["carousel", "card"], + files: [ + { + path: "examples/carousel-orientation.tsx", + type: "registry:example" + } + ] + }, + { + name: "carousel-api", + type: "registry:example", + registryDependencies: ["carousel", "card"], + files: [ + { + path: "examples/carousel-api.tsx", + type: "registry:example" + } + ] + }, + { + name: "carousel-plugin", + type: "registry:example", + dependencies: ["embla-carousel-autoplay"], + registryDependencies: ["carousel", "card"], + files: [ + { + path: "examples/carousel-plugin.tsx", + type: "registry:example" + } + ] + }, { name: "button-default", type: "registry:example", diff --git a/apps/docs/src/registry/examples/carousel-api.tsx b/apps/docs/src/registry/examples/carousel-api.tsx new file mode 100644 index 0000000..d032557 --- /dev/null +++ b/apps/docs/src/registry/examples/carousel-api.tsx @@ -0,0 +1,55 @@ +import { createEffect, createSignal, For, on } from "solid-js" + +import { Card, CardContent } from "~/registry/ui/card" +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, + type CarouselApi +} from "~/registry/ui/carousel" + +export default function CarouselApiDemo() { + const [api, setApi] = createSignal() + const [current, setCurrent] = createSignal(0) + const [count, setCount] = createSignal(0) + + createEffect( + on(api, (emblaApi) => { + if (!emblaApi) return + + setCount(emblaApi.scrollSnapList().length) + setCurrent(emblaApi.selectedScrollSnap() + 1) + + emblaApi.on("select", () => { + setCurrent(emblaApi.selectedScrollSnap() + 1) + }) + }) + ) + + return ( +
+ + + + {(_, index) => ( + + + + {index() + 1} + + + + )} + + + + + +
+ Slide {current()} of {count()} +
+
+ ) +} diff --git a/apps/docs/src/registry/examples/carousel-demo.tsx b/apps/docs/src/registry/examples/carousel-demo.tsx new file mode 100644 index 0000000..d74623e --- /dev/null +++ b/apps/docs/src/registry/examples/carousel-demo.tsx @@ -0,0 +1,34 @@ +import { For } from "solid-js" + +import { Card, CardContent } from "~/registry/ui/card" +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious +} from "~/registry/ui/carousel" + +export default function CarouselDemo() { + return ( + + + + {(_, index) => ( + +
+ + + {index() + 1} + + +
+
+ )} +
+
+ + +
+ ) +} diff --git a/apps/docs/src/registry/examples/carousel-orientation.tsx b/apps/docs/src/registry/examples/carousel-orientation.tsx new file mode 100644 index 0000000..7c82402 --- /dev/null +++ b/apps/docs/src/registry/examples/carousel-orientation.tsx @@ -0,0 +1,40 @@ +import { For } from "solid-js" + +import { Card, CardContent } from "~/registry/ui/card" +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious +} from "~/registry/ui/carousel" + +export default function CarouselOrientation() { + return ( + + + + {(_, index) => ( + +
+ + + {index() + 1} + + +
+
+ )} +
+
+ + +
+ ) +} diff --git a/apps/docs/src/registry/examples/carousel-plugin.tsx b/apps/docs/src/registry/examples/carousel-plugin.tsx new file mode 100644 index 0000000..68feea0 --- /dev/null +++ b/apps/docs/src/registry/examples/carousel-plugin.tsx @@ -0,0 +1,42 @@ +import { For } from "solid-js" +import Autoplay from "embla-carousel-autoplay" + +import { Card, CardContent } from "~/registry/ui/card" +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious +} from "~/registry/ui/carousel" + +export default function CarouselPlugin() { + const plugin = Autoplay({ delay: 2000, stopOnInteraction: true }) + + return ( + plugin.play()} + > + + + {(_, index) => ( + +
+ + + {index() + 1} + + +
+
+ )} +
+
+ + +
+ ) +} diff --git a/apps/docs/src/registry/examples/carousel-size.tsx b/apps/docs/src/registry/examples/carousel-size.tsx new file mode 100644 index 0000000..f146839 --- /dev/null +++ b/apps/docs/src/registry/examples/carousel-size.tsx @@ -0,0 +1,39 @@ +import { For } from "solid-js" + +import { Card, CardContent } from "~/registry/ui/card" +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious +} from "~/registry/ui/carousel" + +export default function CarouselSize() { + return ( + + + + {(_, index) => ( + +
+ + + {index() + 1} + + +
+
+ )} +
+
+ + +
+ ) +} diff --git a/apps/docs/src/registry/examples/carousel-spacing.tsx b/apps/docs/src/registry/examples/carousel-spacing.tsx new file mode 100644 index 0000000..b64ed35 --- /dev/null +++ b/apps/docs/src/registry/examples/carousel-spacing.tsx @@ -0,0 +1,34 @@ +import { For } from "solid-js" + +import { Card, CardContent } from "~/registry/ui/card" +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious +} from "~/registry/ui/carousel" + +export default function CarouselSpacing() { + return ( + + + + {(_, index) => ( + +
+ + + {index() + 1} + + +
+
+ )} +
+
+ + +
+ ) +} diff --git a/apps/docs/src/registry/ui/_registry.ts b/apps/docs/src/registry/ui/_registry.ts index 80b4560..ae680ef 100644 --- a/apps/docs/src/registry/ui/_registry.ts +++ b/apps/docs/src/registry/ui/_registry.ts @@ -111,6 +111,18 @@ export const ui: Registry["items"] = [ } ] }, + { + name: "carousel", + type: "registry:ui", + dependencies: ["embla-carousel", "embla-carousel-solid", "lucide-solid"], + registryDependencies: ["button"], + files: [ + { + path: "ui/carousel.tsx", + type: "registry:ui" + } + ] + }, { name: "checkbox", type: "registry:ui", diff --git a/apps/docs/src/registry/ui/carousel.tsx b/apps/docs/src/registry/ui/carousel.tsx new file mode 100644 index 0000000..2ad4786 --- /dev/null +++ b/apps/docs/src/registry/ui/carousel.tsx @@ -0,0 +1,270 @@ +import type { Accessor, ComponentProps, JSX, ValidComponent } from "solid-js" +import { + createContext, + createEffect, + createSignal, + mergeProps, + onCleanup, + splitProps, + useContext +} from "solid-js" + +import type { PolymorphicProps } from "@kobalte/core/polymorphic" +import type { EmblaCarouselType, EmblaOptionsType, EmblaPluginType } from "embla-carousel" +import createEmblaCarousel from "embla-carousel-solid" +import { ChevronLeft, ChevronRight } from "lucide-solid" + +import { cn } from "~/lib/utils" +import { Button, type ButtonProps } from "~/registry/ui/button" + +type CarouselApi = EmblaCarouselType | undefined +type CarouselOptions = EmblaOptionsType +type CarouselPlugin = EmblaPluginType + +type CarouselProps = { + opts?: CarouselOptions + plugins?: CarouselPlugin[] + orientation?: "horizontal" | "vertical" + setApi?: (api: CarouselApi) => void +} + +type CarouselContextProps = { + carouselRef: ReturnType[0] + api: ReturnType[1] + scrollPrev: () => void + scrollNext: () => void + canScrollPrev: Accessor + canScrollNext: Accessor +} & CarouselProps + +const CarouselContext = createContext(null) + +function useCarousel() { + const context = useContext(CarouselContext) + + if (!context) { + throw new Error("useCarousel must be used within a ") + } + + return context +} + +type CarouselRootProps = ComponentProps & + CarouselProps & { + class?: string | undefined + children?: JSX.Element + } + +const Carousel = ( + rawProps: PolymorphicProps> +) => { + const props = mergeProps({ orientation: "horizontal" as const }, rawProps) + const [local, others] = splitProps(props as CarouselRootProps, [ + "class", + "children", + "opts", + "plugins", + "orientation", + "setApi" + ]) + + const [carouselRef, api] = createEmblaCarousel( + () => ({ + ...local.opts, + axis: local.orientation === "horizontal" ? "x" : "y" + }), + () => local.plugins ?? [] + ) + + const [canScrollPrev, setCanScrollPrev] = createSignal(false) + const [canScrollNext, setCanScrollNext] = createSignal(false) + + const onSelect = (emblaApi: EmblaCarouselType) => { + setCanScrollPrev(emblaApi.canScrollPrev()) + setCanScrollNext(emblaApi.canScrollNext()) + } + + const scrollPrev = () => { + api()?.scrollPrev() + } + + const scrollNext = () => { + api()?.scrollNext() + } + + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === "ArrowLeft") { + event.preventDefault() + scrollPrev() + } else if (event.key === "ArrowRight") { + event.preventDefault() + scrollNext() + } + } + + createEffect(() => { + const emblaApi = api() + if (!emblaApi || !local.setApi) return + local.setApi(emblaApi) + }) + + createEffect(() => { + const emblaApi = api() + if (!emblaApi) return + + onSelect(emblaApi) + emblaApi.on("reInit", onSelect) + emblaApi.on("select", onSelect) + + onCleanup(() => { + emblaApi.off("select", onSelect) + }) + }) + + return ( + +
+ {local.children} +
+
+ ) +} + +type CarouselContentProps = ComponentProps & { + class?: string | undefined +} + +const CarouselContent = ( + props: PolymorphicProps> +) => { + const [local, others] = splitProps(props as CarouselContentProps, ["class"]) + const { carouselRef, orientation } = useCarousel() + + return ( +
+
+
+ ) +} + +type CarouselItemProps = ComponentProps & { + class?: string | undefined +} + +const CarouselItem = ( + props: PolymorphicProps> +) => { + const [local, others] = splitProps(props as CarouselItemProps, ["class"]) + const { orientation } = useCarousel() + + return ( +
+ ) +} + +type CarouselPreviousProps = ButtonProps & { + class?: string | undefined +} + +const CarouselPrevious = ( + rawProps: PolymorphicProps> +) => { + const props = mergeProps({ variant: "outline" as const, size: "icon-sm" as const }, rawProps) + const [local, others] = splitProps(props as CarouselPreviousProps, ["class", "variant", "size"]) + const { orientation, scrollPrev, canScrollPrev } = useCarousel() + + return ( + + ) +} + +type CarouselNextProps = ButtonProps & { + class?: string | undefined +} + +const CarouselNext = ( + rawProps: PolymorphicProps> +) => { + const props = mergeProps({ variant: "outline" as const, size: "icon-sm" as const }, rawProps) + const [local, others] = splitProps(props as CarouselNextProps, ["class", "variant", "size"]) + const { orientation, scrollNext, canScrollNext } = useCarousel() + + return ( + + ) +} + +export { + type CarouselApi, + Carousel, + CarouselContent, + CarouselItem, + CarouselPrevious, + CarouselNext, + useCarousel +} diff --git a/apps/docs/src/routes/docs/components/carousel.mdx b/apps/docs/src/routes/docs/components/carousel.mdx new file mode 100644 index 0000000..c9c2bd7 --- /dev/null +++ b/apps/docs/src/routes/docs/components/carousel.mdx @@ -0,0 +1,319 @@ +--- +title: Carousel +description: A carousel with motion and swipe built using Embla. +links: + doc: https://www.embla-carousel.com/get-started/solid + api: https://www.embla-carousel.com/api +--- + +::::tab-group[preview] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/carousel-demo" frame=none showLineNumbers + +``` + +::: +:::: + +## About + +The carousel component is built using the [Embla Carousel](https://www.embla-carousel.com/) library. + +## Installation + +### CLI + +```package-exec +solidui-cli@latest add carousel +``` + +### Manual + +Install the following dependencies: + +```package-install +embla-carousel-solid lucide-solid +``` + +Copy and paste the following code into your project. + +```file="~/registry/ui/carousel.tsx" showLineNumbers + +``` + +## Usage + +```tsx +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious +} from "~/components/ui/carousel"; +``` + +```tsx + + + ... + ... + ... + + + + +``` + +## Examples + +### Sizes + +To set the size of the items, you can use the `basis` utility class on the ``. + +::::tab-group[sizes] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/carousel-size" frame=none showLineNumbers + +``` + +::: +:::: + +```tsx +// 33% of the carousel width. + + + ... + ... + ... + + +``` + +```tsx +// 50% on small screens and 33% on larger screens. + + + ... + ... + ... + + +``` + +### Spacing + +To set the spacing between the items, we use a `pl-[VALUE]` utility on the `` and a negative `-ml-[VALUE]` on the ``. + +::::tab-group[spacing] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/carousel-spacing" frame=none showLineNumbers + +``` + +::: +:::: + +```tsx + + + ... + ... + ... + + +``` + +### Orientation + +Use the `orientation` prop to set the orientation of the carousel. + +::::tab-group[orientation] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/carousel-orientation" frame=none showLineNumbers + +``` + +::: +:::: + +```tsx + + + ... + ... + ... + + +``` + +## Options + +You can pass options to the carousel using the `opts` prop. See the [Embla Carousel docs](https://www.embla-carousel.com/api/options/) for more information. + +```tsx + + + ... + ... + ... + + +``` + +## API + +Use a signal and the `setApi` prop to get an instance of the carousel API. + +::::tab-group[api] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/carousel-api" frame=none showLineNumbers + +``` + +::: +:::: + +```tsx +import { createEffect, createSignal, on } from "solid-js"; +import { type CarouselApi } from "~/components/ui/carousel"; + +export function Example() { + const [api, setApi] = createSignal(); + const [current, setCurrent] = createSignal(0); + const [count, setCount] = createSignal(0); + + createEffect( + on(api, (emblaApi) => { + if (!emblaApi) return; + + setCount(emblaApi.scrollSnapList().length); + setCurrent(emblaApi.selectedScrollSnap() + 1); + + emblaApi.on("select", () => { + setCurrent(emblaApi.selectedScrollSnap() + 1); + }); + }) + ); + + return ( + + + ... + ... + ... + + + ); +} +``` + +## Events + +You can listen to events using the api instance from `setApi`. + +```tsx +import { createEffect, createSignal, on } from "solid-js"; +import { type CarouselApi } from "~/components/ui/carousel"; + +export function Example() { + const [api, setApi] = createSignal(); + + createEffect( + on(api, (emblaApi) => { + if (!emblaApi) return; + + emblaApi.on("select", () => { + // Do something on select. + }); + }) + ); + + return ( + + + ... + ... + ... + + + ); +} +``` + +See the [Embla Carousel docs](https://www.embla-carousel.com/api/events/) for more information on using events. + +## Plugins + +You can use the `plugins` prop to add plugins to the carousel. + +```tsx +import Autoplay from "embla-carousel-autoplay"; + +export function Example() { + return ( + + // ... + + ); +} +``` + +::::tab-group[plugin] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/carousel-plugin" frame=none showLineNumbers + +``` + +::: +:::: + +See the [Embla Carousel docs](https://www.embla-carousel.com/api/plugins/) for more information on using plugins. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93557fe..90bb678 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,15 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + embla-carousel: + specifier: ^8.6.0 + version: 8.6.0 + embla-carousel-autoplay: + specifier: ^8.6.0 + version: 8.6.0(embla-carousel@8.6.0) + embla-carousel-solid: + specifier: ^8.6.0 + version: 8.6.0(solid-js@1.9.9) lucide-solid: specifier: ^0.562.0 version: 0.562.0(solid-js@1.9.9) @@ -4558,6 +4567,36 @@ packages: resolution: {integrity: sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==} dev: false + /embla-carousel-autoplay@8.6.0(embla-carousel@8.6.0): + resolution: {integrity: sha512-OBu5G3nwaSXkZCo1A6LTaFMZ8EpkYbwIaH+bPqdBnDGQ2fh4+NbzjXjs2SktoPNKCtflfVMc75njaDHOYXcrsA==} + peerDependencies: + embla-carousel: 8.6.0 + dependencies: + embla-carousel: 8.6.0 + dev: false + + /embla-carousel-reactive-utils@8.6.0(embla-carousel@8.6.0): + resolution: {integrity: sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==} + peerDependencies: + embla-carousel: 8.6.0 + dependencies: + embla-carousel: 8.6.0 + dev: false + + /embla-carousel-solid@8.6.0(solid-js@1.9.9): + resolution: {integrity: sha512-xQQjPZL+CQ4n2KemoeTu0BD9mk8tkVuSMOe/GEnzKM0lAoXY/akSYBNZ4dvJDE7TMeUCAuI6D9742TTLglGq/A==} + peerDependencies: + solid-js: ^1.0.0 + dependencies: + embla-carousel: 8.6.0 + embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0) + solid-js: 1.9.9 + dev: false + + /embla-carousel@8.6.0: + resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==} + dev: false + /emoji-regex-xs@1.0.0: resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} dev: false