diff --git a/docs/src/components/Announcement.astro b/docs/src/components/Announcement.astro
index 6290176..8541a64 100644
--- a/docs/src/components/Announcement.astro
+++ b/docs/src/components/Announcement.astro
@@ -2,8 +2,8 @@
---
-
- New textarea component
+
+ New pagination component
+
+## Installation
+
+
+
+
+ CLI
+ Manual
+
+
+
+```bash
+npx shadcn-ng@latest add pagination
+```
+
+
+
+
+
+
+
+Copy and paste the following code into your project.
+
+
+
+Update the import paths to match your project setup.
+
+
+
+
+
+
+
+## Usage
+
+```tsx
+import {
+ UbPaginationContentDirective,
+ UbPaginationDirective,
+ UbPaginationEllipsisComponent,
+ UbPaginationItemDirective,
+ UbPaginationLinkDirective,
+ UbPaginationNextDirective,
+ UbPaginationPreviousDirective,
+} from "@/components/ui/pagination"
+```
+
+```html
+
+```
diff --git a/docs/src/registry/default/example/pagination-demo.ts b/docs/src/registry/default/example/pagination-demo.ts
new file mode 100644
index 0000000..aed29ea
--- /dev/null
+++ b/docs/src/registry/default/example/pagination-demo.ts
@@ -0,0 +1,50 @@
+import {
+ UbPaginationContentDirective,
+ UbPaginationDirective,
+ UbPaginationEllipsisComponent,
+ UbPaginationItemDirective,
+ UbPaginationLinkDirective,
+ UbPaginationNextDirective,
+ UbPaginationPreviousDirective,
+} from '@/registry/default/ui/pagination'
+
+import { Component } from '@angular/core'
+
+@Component({
+ standalone: true,
+ selector: '[pagination-demo-default]',
+ imports: [
+ UbPaginationContentDirective,
+ UbPaginationDirective,
+ UbPaginationEllipsisComponent,
+ UbPaginationItemDirective,
+ UbPaginationLinkDirective,
+ UbPaginationNextDirective,
+ UbPaginationPreviousDirective,
+ ],
+ template: `
+
+ `,
+})
+export default class PaginationDemoDefault { }
diff --git a/docs/src/registry/default/ui/pagination.ts b/docs/src/registry/default/ui/pagination.ts
new file mode 100644
index 0000000..77aab88
--- /dev/null
+++ b/docs/src/registry/default/ui/pagination.ts
@@ -0,0 +1,136 @@
+import { cn } from '@/lib/utils'
+import { buttonVariants, type UbButtonSize } from '@/registry/default/ui/button'
+import { booleanAttribute, Component, computed, Directive, effect, inject, input } from '@angular/core'
+import { NgIcon, provideIcons } from '@ng-icons/core'
+import { lucideChevronLeft, lucideChevronRight, lucideEllipsis } from '@ng-icons/lucide'
+
+@Directive({
+ standalone: true,
+ selector: 'nav[ubPagination]',
+ host: {
+ 'role': 'navigation',
+ 'aria-label': 'pagination',
+ '[class]': 'computedClass()',
+ },
+})
+export class UbPaginationDirective {
+ class = input()
+ computedClass = computed(() => cn('mx-auto flex w-full justify-center', this.class()))
+}
+
+@Directive({
+ standalone: true,
+ selector: 'ul[ubPaginationContent]',
+ host: {
+ '[class]': 'computedClass()',
+ },
+})
+export class UbPaginationContentDirective {
+ class = input()
+ computedClass = computed(() => cn('flex flex-row items-center gap-1', this.class()))
+}
+
+@Directive({
+ standalone: true,
+ selector: 'li[ubPaginationItem]',
+ host: {
+ '[class]': 'computedClass()',
+ },
+})
+export class UbPaginationItemDirective {
+ class = input()
+ computedClass = computed(() => cn('', this.class()))
+}
+
+@Directive({
+ standalone: true,
+ selector: '[ubPaginationLink]',
+ host: {
+ '[aria-current]': 'isActive() ? "page" : undefined',
+ '[class]': 'computedClass()',
+ },
+})
+export class UbPaginationLinkDirective {
+ class = input()
+ isActive = input(false, { transform: booleanAttribute })
+ size = input('icon')
+ computedClass = computed(() => cn(
+ buttonVariants({
+ variant: this.isActive() ? 'outline' : 'ghost',
+ size: this.size(),
+ }),
+ this.class(),
+ ))
+}
+
+@Component({
+ standalone: true,
+ selector: '[ubPaginationPrevious]',
+ imports: [NgIcon],
+ hostDirectives: [UbPaginationLinkDirective],
+ host: {
+ '[class]': 'computedClass()',
+ 'aria-label': 'Go to previous page',
+ },
+ providers: [provideIcons({ lucideChevronLeft })],
+ template: `
+
+ Previous
+ `,
+})
+export class UbPaginationPreviousDirective {
+ class = input()
+ computedClass = computed(() => cn('gap-1 pl-2.5', this.class()))
+ protected size = input('default')
+ private ubPaginationLinkDirective = inject(UbPaginationLinkDirective, { host: true })
+
+ setConfig = effect(() => {
+ this.ubPaginationLinkDirective.size = this.size
+ })
+}
+
+@Component({
+ standalone: true,
+ selector: '[ubPaginationNext]',
+ imports: [NgIcon],
+ providers: [provideIcons({ lucideChevronRight })],
+ hostDirectives: [UbPaginationLinkDirective],
+ host: {
+ '[class]': 'computedClass()',
+ 'aria-label': 'Go to next page',
+ },
+ template: `
+ Next
+
+ `,
+})
+export class UbPaginationNextDirective {
+ class = input()
+ computedClass = computed(() => cn('gap-1 pr-2.5', this.class()))
+ protected size = input('default')
+ private ubPaginationLinkDirective = inject(UbPaginationLinkDirective, { host: true })
+
+ setConfig = effect(() => {
+ this.ubPaginationLinkDirective.size = this.size
+ })
+}
+
+@Component({
+ standalone: true,
+ imports: [NgIcon],
+ providers: [provideIcons({ lucideEllipsis })],
+ selector: 'ub-pagination-ellipsis',
+ template: `
+
+
+ More pages
+
+ `,
+})
+export class UbPaginationEllipsisComponent {
+ class = input()
+ computedClass = computed(() => cn('flex h-9 w-9 items-center justify-center', this.class()))
+}
diff --git a/docs/src/registry/new-york/example/pagination-demo.ts b/docs/src/registry/new-york/example/pagination-demo.ts
new file mode 100644
index 0000000..a284784
--- /dev/null
+++ b/docs/src/registry/new-york/example/pagination-demo.ts
@@ -0,0 +1,50 @@
+import {
+ UbPaginationContentDirective,
+ UbPaginationDirective,
+ UbPaginationEllipsisComponent,
+ UbPaginationItemDirective,
+ UbPaginationLinkDirective,
+ UbPaginationNextDirective,
+ UbPaginationPreviousDirective,
+} from '@/registry/new-york/ui/pagination'
+
+import { Component } from '@angular/core'
+
+@Component({
+ standalone: true,
+ selector: '[pagination-demo-new-york]',
+ imports: [
+ UbPaginationContentDirective,
+ UbPaginationDirective,
+ UbPaginationEllipsisComponent,
+ UbPaginationItemDirective,
+ UbPaginationLinkDirective,
+ UbPaginationNextDirective,
+ UbPaginationPreviousDirective,
+ ],
+ template: `
+
+ `,
+})
+export default class PaginationDemoNewYork { }
diff --git a/docs/src/registry/new-york/ui/button.ts b/docs/src/registry/new-york/ui/button.ts
index 4c51fc8..4c6de81 100644
--- a/docs/src/registry/new-york/ui/button.ts
+++ b/docs/src/registry/new-york/ui/button.ts
@@ -36,8 +36,8 @@ export const buttonVariants = cva(
type ButtonProps = VariantProps
-export type ButtonSize = NonNullable
-export type ButtonVariant = NonNullable
+export type UbButtonSize = NonNullable
+export type UbButtonVariant = NonNullable
@Directive({
selector: '[ubButton]',
@@ -49,9 +49,9 @@ export type ButtonVariant = NonNullable
export class UbButtonDirective {
readonly class = input()
- readonly variant = input('default')
+ readonly variant = input('default')
- readonly size = input('default')
+ readonly size = input('default')
protected computedClass = computed(() =>
cn(
diff --git a/docs/src/registry/new-york/ui/pagination.ts b/docs/src/registry/new-york/ui/pagination.ts
new file mode 100644
index 0000000..82ffa02
--- /dev/null
+++ b/docs/src/registry/new-york/ui/pagination.ts
@@ -0,0 +1,136 @@
+import { cn } from '@/lib/utils'
+import { buttonVariants, type UbButtonSize } from '@/registry/new-york/ui/button'
+import { booleanAttribute, Component, computed, Directive, effect, inject, input } from '@angular/core'
+import { NgIcon, provideIcons } from '@ng-icons/core'
+import { lucideChevronLeft, lucideChevronRight, lucideEllipsis } from '@ng-icons/lucide'
+
+@Directive({
+ standalone: true,
+ selector: 'nav[ubPagination]',
+ host: {
+ 'role': 'navigation',
+ 'aria-label': 'pagination',
+ '[class]': 'computedClass()',
+ },
+})
+export class UbPaginationDirective {
+ class = input()
+ computedClass = computed(() => cn('mx-auto flex w-full justify-center', this.class()))
+}
+
+@Directive({
+ standalone: true,
+ selector: 'ul[ubPaginationContent]',
+ host: {
+ '[class]': 'computedClass()',
+ },
+})
+export class UbPaginationContentDirective {
+ class = input()
+ computedClass = computed(() => cn('flex flex-row items-center gap-1', this.class()))
+}
+
+@Directive({
+ standalone: true,
+ selector: 'li[ubPaginationItem]',
+ host: {
+ '[class]': 'computedClass()',
+ },
+})
+export class UbPaginationItemDirective {
+ class = input()
+ computedClass = computed(() => cn('', this.class()))
+}
+
+@Directive({
+ standalone: true,
+ selector: '[ubPaginationLink]',
+ host: {
+ '[aria-current]': 'isActive() ? "page" : undefined',
+ '[class]': 'computedClass()',
+ },
+})
+export class UbPaginationLinkDirective {
+ class = input()
+ isActive = input(false, { transform: booleanAttribute })
+ size = input('icon')
+ computedClass = computed(() => cn(
+ buttonVariants({
+ variant: this.isActive() ? 'outline' : 'ghost',
+ size: this.size(),
+ }),
+ this.class(),
+ ))
+}
+
+@Component({
+ standalone: true,
+ selector: '[ubPaginationPrevious]',
+ imports: [NgIcon],
+ hostDirectives: [UbPaginationLinkDirective],
+ host: {
+ '[class]': 'computedClass()',
+ 'aria-label': 'Go to previous page',
+ },
+ providers: [provideIcons({ lucideChevronLeft })],
+ template: `
+
+ Previous
+ `,
+})
+export class UbPaginationPreviousDirective {
+ class = input()
+ computedClass = computed(() => cn('gap-1 pl-2.5', this.class()))
+ protected size = input('default')
+ private ubPaginationLinkDirective = inject(UbPaginationLinkDirective, { host: true })
+
+ setConfig = effect(() => {
+ this.ubPaginationLinkDirective.size = this.size
+ })
+}
+
+@Component({
+ standalone: true,
+ selector: '[ubPaginationNext]',
+ imports: [NgIcon],
+ providers: [provideIcons({ lucideChevronRight })],
+ hostDirectives: [UbPaginationLinkDirective],
+ host: {
+ '[class]': 'computedClass()',
+ 'aria-label': 'Go to next page',
+ },
+ template: `
+ Next
+
+ `,
+})
+export class UbPaginationNextDirective {
+ class = input()
+ computedClass = computed(() => cn('gap-1 pr-2.5', this.class()))
+ protected size = input('default')
+ private ubPaginationLinkDirective = inject(UbPaginationLinkDirective, { host: true })
+
+ setConfig = effect(() => {
+ this.ubPaginationLinkDirective.size = this.size
+ })
+}
+
+@Component({
+ standalone: true,
+ imports: [NgIcon],
+ providers: [provideIcons({ lucideEllipsis })],
+ selector: 'ub-pagination-ellipsis',
+ template: `
+
+
+ More pages
+
+ `,
+})
+export class UbPaginationEllipsisComponent {
+ class = input()
+ computedClass = computed(() => cn('flex h-9 w-9 items-center justify-center', this.class()))
+}
diff --git a/docs/src/registry/registry-examples.ts b/docs/src/registry/registry-examples.ts
index 2b4a22b..8ee54f2 100644
--- a/docs/src/registry/registry-examples.ts
+++ b/docs/src/registry/registry-examples.ts
@@ -151,6 +151,12 @@ export const examples: Registry = [
registryDependencies: ['input'],
files: ['example/input-file.ts'],
},
+ {
+ name: 'pagination-demo',
+ type: 'registry:example',
+ registryDependencies: ['pagination'],
+ files: ['example/pagination-demo.ts'],
+ },
{
name: 'progress-demo',
type: 'registry:example',
diff --git a/docs/src/registry/registry-ui.ts b/docs/src/registry/registry-ui.ts
index 498f87b..7c243a7 100644
--- a/docs/src/registry/registry-ui.ts
+++ b/docs/src/registry/registry-ui.ts
@@ -89,6 +89,11 @@ export const ui: Registry = [
dependencies: ['@radix-ng/primitives'],
files: [{ path: 'ui/label.ts', type: 'registry:ui' }],
},
+ {
+ name: 'pagination',
+ type: 'registry:ui',
+ files: [{ path: 'ui/pagination.ts', type: 'registry:ui' }],
+ },
{
name: 'progress',
type: 'registry:ui',