diff --git a/.changeset/eleven-walls-try.md b/.changeset/eleven-walls-try.md
new file mode 100644
index 0000000..07ace13
--- /dev/null
+++ b/.changeset/eleven-walls-try.md
@@ -0,0 +1,5 @@
+---
+"@utima/ui": patch
+---
+
+Fixed button and patch invalid border styles on non-outline variatns
diff --git a/.changeset/smooth-flies-thank.md b/.changeset/smooth-flies-thank.md
new file mode 100644
index 0000000..c9a9db2
--- /dev/null
+++ b/.changeset/smooth-flies-thank.md
@@ -0,0 +1,5 @@
+---
+"@utima/ui": minor
+---
+
+Added new `Pagination` component
diff --git a/packages/ui/src/components/badge/Badge.stories.tsx b/packages/ui/src/components/badge/Badge.stories.tsx
index 5cf6104..2c68342 100644
--- a/packages/ui/src/components/badge/Badge.stories.tsx
+++ b/packages/ui/src/components/badge/Badge.stories.tsx
@@ -35,3 +35,16 @@ export const TextWrap: Story = {
'Lorem ipsum dolor sit amet consectetur adipisicing elit. Sint, voluptate?',
},
};
+
+export const Multiline: Story = {
+ args: {
+ children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
+ },
+ decorators: [
+ Story => (
+
+
+
+ ),
+ ],
+};
diff --git a/packages/ui/src/components/badge/Badge.styles.ts b/packages/ui/src/components/badge/Badge.styles.ts
index 7a05223..23f56cc 100644
--- a/packages/ui/src/components/badge/Badge.styles.ts
+++ b/packages/ui/src/components/badge/Badge.styles.ts
@@ -5,7 +5,7 @@ import { twOverrides } from '@/overrides';
export const badgeDef = twOverrides(
{
badge:
- 'rounded-radius border font-semibold inline-flex items-center transition-all h-atuo',
+ 'rounded-radius font-semibold inline-flex items-center transition-all h-atuo',
variants: {
variant: {
primary: 'bg-primary border-primary text-primary-fg',
@@ -19,22 +19,22 @@ export const badgeDef = twOverrides(
link: 'text-foreground border-transparent underline underline-offset-4',
},
size: {
- xs: 'py-0.5 px-1 gap-1 text-[10px]',
- sm: 'py-[2px] px-1.5 gap-1 text-xs',
- md: 'py-1 px-2 gap-1 text-xs',
- lg: 'py-1.5 px-3 gap-1.5 text-sm',
- xl: 'py-2 px-3 gap-2 text-base',
+ xs: 'min-h-5 px-1 gap-1 text-[10px]',
+ sm: 'min-h-6 py-[2px] px-1.5 gap-1 text-xs',
+ md: 'min-h-7 py-[2px] px-2 gap-1 text-xs',
+ lg: 'min-h-9 py-1 px-3 gap-1.5 text-sm',
+ xl: 'min-h-10 py-1 px-3 gap-2 text-base',
},
outline: {
- primary: `bg-background text-primary`,
- secondary: `bg-background text-secondary`,
- muted: `bg-background text-muted`,
- success: `bg-background text-success`,
- danger: `bg-background text-danger`,
- warning: `bg-background text-warning`,
- info: `bg-background text-info`,
- ghost: `bg-background`,
- link: `bg-background`,
+ primary: `bg-background border text-primary`,
+ secondary: `bg-background border text-secondary`,
+ muted: `bg-background border text-muted`,
+ success: `bg-background border text-success`,
+ danger: `bg-background border text-danger`,
+ warning: `bg-background border text-warning`,
+ info: `bg-background border text-info`,
+ ghost: `bg-background border`,
+ link: `bg-background border`,
},
disabled: {
DEFAULT: 'opacity-65 cursor-not-allowed',
diff --git a/packages/ui/src/components/button/Button.stories.tsx b/packages/ui/src/components/button/Button.stories.tsx
index ba27dc1..c1df61b 100644
--- a/packages/ui/src/components/button/Button.stories.tsx
+++ b/packages/ui/src/components/button/Button.stories.tsx
@@ -33,3 +33,16 @@ export const Icon: Story = {
children: null,
},
};
+
+export const Multiline: Story = {
+ args: {
+ children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
+ },
+ decorators: [
+ Story => (
+
+
+
+ ),
+ ],
+};
diff --git a/packages/ui/src/components/button/Button.styles.ts b/packages/ui/src/components/button/Button.styles.ts
index 7591a8a..589a4dc 100644
--- a/packages/ui/src/components/button/Button.styles.ts
+++ b/packages/ui/src/components/button/Button.styles.ts
@@ -9,7 +9,7 @@ import { globalDef } from '../global.styles';
*/
export const buttonDef = twOverrides(
{
- button: `${globalDef.ring} border outline-none box-border inline-flex font-semibold items-center justify-center rounded-radius transition-all disabled:opacity-65 disabled:cursor-not-allowed`,
+ button: `${globalDef.ring} outline-none box-border inline-flex font-semibold items-center justify-center rounded-radius transition-all disabled:opacity-65 disabled:cursor-not-allowed`,
variants: {
variant: {
primary:
@@ -30,27 +30,27 @@ export const buttonDef = twOverrides(
link: 'text-foreground hover:text-foreground/70 active:text-foreground/90 underline-offset-4 hover:underline ring-foreground',
},
size: {
- xs: 'py-1 px-2 text-xs gap-x-1',
- sm: 'py-1.5 px-3 text-sm gap-x-1',
- md: 'py-2 px-4 text-sm gap-x-1',
- lg: 'py-2.5 px-5 text-base gap-x-1.5',
- xl: 'py-2.5 px-6 text-lg gap-x-2',
+ xs: 'min-h-7 py-1 px-2 text-xs gap-x-1',
+ sm: 'min-h-9 py-1 px-3 text-sm gap-x-1',
+ md: 'min-h-10 py-1.5 px-4 text-sm gap-x-1',
+ lg: 'min-h-11 py-1.5 px-5 text-base gap-x-1.5',
+ xl: 'min-h-12 py-2 px-6 text-lg gap-x-2',
'icon-xs': 'size-7',
- 'icon-sm': 'size-8',
- 'icon-md': 'size-9',
- 'icon-lg': 'size-10',
+ 'icon-sm': 'size-9',
+ 'icon-md': 'size-10',
+ 'icon-lg': 'size-11',
'icon-xl': 'size-12',
},
outline: {
- primary: `bg-background text-primary hover:bg-primary/10 active:bg-primary/15`,
- secondary: `bg-background text-secondary hover:bg-secondary/10 active:bg-secondary/15`,
- muted: `bg-background text-default hover:bg-default/10 active:bg-default/15`,
- success: `bg-background text-success hover:bg-success/10 active:bg-success/15`,
- danger: `bg-background text-danger hover:bg-danger/10 active:bg-danger/15`,
- warning: `bg-background text-warning hover:bg-warning/10 active:bg-warning/15`,
- info: `bg-background text-info hover:bg-info/10 active:bg-info/15`,
- ghost: `bg-background border-primary/25`,
- link: `bg-background border-primary`,
+ primary: `bg-background border text-primary hover:bg-primary/10 active:bg-primary/15`,
+ secondary: `bg-background border text-secondary hover:bg-secondary/10 active:bg-secondary/15`,
+ muted: `bg-background border text-default hover:bg-default/10 active:bg-default/15`,
+ success: `bg-background border text-success hover:bg-success/10 active:bg-success/15`,
+ danger: `bg-background border text-danger hover:bg-danger/10 active:bg-danger/15`,
+ warning: `bg-background border text-warning hover:bg-warning/10 active:bg-warning/15`,
+ info: `bg-background border text-info hover:bg-info/10 active:bg-info/15`,
+ ghost: `bg-background border border-primary/25`,
+ link: `bg-background border border-primary`,
},
},
},
diff --git a/packages/ui/src/components/pagination/Pagination.stories.tsx b/packages/ui/src/components/pagination/Pagination.stories.tsx
new file mode 100644
index 0000000..7668581
--- /dev/null
+++ b/packages/ui/src/components/pagination/Pagination.stories.tsx
@@ -0,0 +1,44 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import * as Pagination from './index';
+import { PaginationEllipsis } from './PaginationEllipsis';
+import { PaginationLink } from './PaginationLink';
+
+const meta: Meta = {
+ component: Pagination.Root,
+ tags: ['autodocs'],
+ title: 'Components/Pagination',
+ args: {
+ children: (
+
+
+ Previous
+
+
+ 1
+
+
+ 3
+
+
+
+
+
+ 10
+
+
+ 12
+
+
+ Next
+
+
+ ),
+ },
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Basic: Story = {};
diff --git a/packages/ui/src/components/pagination/Pagination.styles.ts b/packages/ui/src/components/pagination/Pagination.styles.ts
new file mode 100644
index 0000000..8e157c7
--- /dev/null
+++ b/packages/ui/src/components/pagination/Pagination.styles.ts
@@ -0,0 +1,10 @@
+export const paginationDef = {
+ root: 'mx-auto flex w-full justify-center',
+ content: 'flex flex-row items-center gap-1',
+ icon: 'size-4',
+ ellipsis: {
+ content: 'flex h-9 w-9 items-center text-primary justify-center',
+ },
+ item: '',
+ link: 'cursor-pointer',
+};
diff --git a/packages/ui/src/components/pagination/PaginationContent.tsx b/packages/ui/src/components/pagination/PaginationContent.tsx
new file mode 100644
index 0000000..caa1c6d
--- /dev/null
+++ b/packages/ui/src/components/pagination/PaginationContent.tsx
@@ -0,0 +1,12 @@
+import { memo, type ComponentPropsWithoutRef } from 'react';
+
+import { cn } from '@/utils';
+
+import { paginationDef } from './Pagination.styles';
+
+export const PaginationContent = memo(function PaginationContent({
+ className,
+ ...restProps
+}: ComponentPropsWithoutRef<'nav'>) {
+ return ;
+});
diff --git a/packages/ui/src/components/pagination/PaginationEllipsis.tsx b/packages/ui/src/components/pagination/PaginationEllipsis.tsx
new file mode 100644
index 0000000..416798e
--- /dev/null
+++ b/packages/ui/src/components/pagination/PaginationEllipsis.tsx
@@ -0,0 +1,21 @@
+import { MoreHorizontal } from 'lucide-react';
+import { memo, type ComponentPropsWithoutRef } from 'react';
+
+import { cn } from '@/utils';
+
+import { paginationDef } from './Pagination.styles';
+
+export const PaginationEllipsis = memo(function PaginationEllipsis({
+ className,
+ ...restProps
+}: ComponentPropsWithoutRef<'span'>) {
+ return (
+
+
+
+ );
+});
diff --git a/packages/ui/src/components/pagination/PaginationItem.tsx b/packages/ui/src/components/pagination/PaginationItem.tsx
new file mode 100644
index 0000000..6dbdb66
--- /dev/null
+++ b/packages/ui/src/components/pagination/PaginationItem.tsx
@@ -0,0 +1,12 @@
+import { memo, type ComponentPropsWithoutRef } from 'react';
+
+import { cn } from '@/utils';
+
+import { paginationDef } from './Pagination.styles';
+
+export const PaginationItem = memo(function PaginationItem({
+ className,
+ ...props
+}: ComponentPropsWithoutRef<'li'>) {
+ return ;
+});
diff --git a/packages/ui/src/components/pagination/PaginationLink.tsx b/packages/ui/src/components/pagination/PaginationLink.tsx
new file mode 100644
index 0000000..3cbd638
--- /dev/null
+++ b/packages/ui/src/components/pagination/PaginationLink.tsx
@@ -0,0 +1,51 @@
+import { Slot } from '@radix-ui/react-slot';
+import type { VariantProps } from 'class-variance-authority';
+import { forwardRef, type ComponentPropsWithoutRef } from 'react';
+
+import { cn } from '@/utils';
+
+import { paginationDef } from './Pagination.styles';
+import { usePaginationContext } from './usePaginationContext';
+import { buttonStyles } from '../button/Button.styles';
+
+export interface PaginationLinkProps
+ extends ComponentPropsWithoutRef<'a'>,
+ Pick, 'size'> {
+ asChild?: boolean;
+ active?: boolean;
+}
+
+export const PaginationLink = forwardRef<
+ HTMLAnchorElement,
+ PaginationLinkProps
+>(function PaginationLink(
+ {
+ className,
+ children,
+ size = 'md',
+ active = false,
+ asChild = false,
+ ...restProps
+ },
+ ref,
+) {
+ const { size: contextSize } = usePaginationContext();
+ const Comp = asChild ? Slot : 'a';
+
+ return (
+
+ {children}
+
+ );
+});
diff --git a/packages/ui/src/components/pagination/PaginationNext.tsx b/packages/ui/src/components/pagination/PaginationNext.tsx
new file mode 100644
index 0000000..087bc4d
--- /dev/null
+++ b/packages/ui/src/components/pagination/PaginationNext.tsx
@@ -0,0 +1,17 @@
+import { ChevronRight } from 'lucide-react';
+import { memo } from 'react';
+
+import { paginationDef } from './Pagination.styles';
+import { PaginationLink, type PaginationLinkProps } from './PaginationLink';
+
+export const PaginationNext = memo(function PaginationNext({
+ children,
+ ...restProps
+}: PaginationLinkProps) {
+ return (
+
+ {children}
+
+
+ );
+});
diff --git a/packages/ui/src/components/pagination/PaginationPrev.tsx b/packages/ui/src/components/pagination/PaginationPrev.tsx
new file mode 100644
index 0000000..8c6a2e5
--- /dev/null
+++ b/packages/ui/src/components/pagination/PaginationPrev.tsx
@@ -0,0 +1,17 @@
+import { ChevronLeft } from 'lucide-react';
+import { memo } from 'react';
+
+import { paginationDef } from './Pagination.styles';
+import { PaginationLink, type PaginationLinkProps } from './PaginationLink';
+
+export const PaginationPrev = memo(function PaginationPrev({
+ children,
+ ...restProps
+}: PaginationLinkProps) {
+ return (
+
+
+ {children}
+
+ );
+});
diff --git a/packages/ui/src/components/pagination/PaginationRoot.tsx b/packages/ui/src/components/pagination/PaginationRoot.tsx
new file mode 100644
index 0000000..696df57
--- /dev/null
+++ b/packages/ui/src/components/pagination/PaginationRoot.tsx
@@ -0,0 +1,31 @@
+import type { VariantProps } from 'class-variance-authority';
+import { memo, useMemo, type ComponentPropsWithoutRef } from 'react';
+
+import { cn } from '@/utils';
+
+import { paginationDef } from './Pagination.styles';
+import { PaginationContext } from './usePaginationContext';
+import type { buttonStyles } from '../button/Button.styles';
+
+export interface PaginationRootProps extends ComponentPropsWithoutRef<'nav'> {
+ size?: VariantProps['size'];
+}
+
+export const PaginationRoot = memo(function PaginationRoot({
+ className,
+ size,
+ ...props
+}: PaginationRootProps) {
+ const contextValue = useMemo(() => ({ size }), [size]);
+
+ return (
+
+
+
+ );
+});
diff --git a/packages/ui/src/components/pagination/index.ts b/packages/ui/src/components/pagination/index.ts
new file mode 100644
index 0000000..0990eb5
--- /dev/null
+++ b/packages/ui/src/components/pagination/index.ts
@@ -0,0 +1,7 @@
+export { PaginationRoot as Root } from './PaginationRoot';
+export { PaginationEllipsis as Ellipsis } from './PaginationEllipsis';
+export { PaginationLink as Link } from './PaginationLink';
+export { PaginationNext as Next } from './PaginationNext';
+export { PaginationPrev as Prev } from './PaginationPrev';
+export { PaginationContent as Content } from './PaginationContent';
+export { PaginationItem as Item } from './PaginationItem';
diff --git a/packages/ui/src/components/pagination/usePaginationContext.tsx b/packages/ui/src/components/pagination/usePaginationContext.tsx
new file mode 100644
index 0000000..50ae7e9
--- /dev/null
+++ b/packages/ui/src/components/pagination/usePaginationContext.tsx
@@ -0,0 +1,24 @@
+import type { VariantProps } from 'class-variance-authority';
+import { createContext, useContext } from 'react';
+
+import type { buttonStyles } from '../button/Button.styles';
+
+export interface PaginationContextType {
+ size: VariantProps['size'];
+}
+
+export const PaginationContext = createContext(
+ undefined!,
+);
+
+export function usePaginationContext() {
+ const context = useContext(PaginationContext);
+
+ if (!context) {
+ throw new Error(
+ 'No PaginationContext.Provider found when calling usePaginationContext.',
+ );
+ }
+
+ return context;
+}