@icomponents/react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +40 -0
  2. package/package.json +165 -0
  3. package/src/assets/custom/icon-dir.tsx +105 -0
  4. package/src/assets/custom/icon-layout-compact.tsx +84 -0
  5. package/src/assets/custom/icon-layout-default.tsx +48 -0
  6. package/src/assets/custom/icon-layout-full.tsx +68 -0
  7. package/src/assets/custom/icon-sidebar-floating.tsx +62 -0
  8. package/src/assets/custom/icon-sidebar-inset.tsx +53 -0
  9. package/src/assets/custom/icon-sidebar-sidebar.tsx +48 -0
  10. package/src/assets/custom/icon-theme-dark.tsx +36 -0
  11. package/src/assets/custom/icon-theme-light.tsx +35 -0
  12. package/src/assets/custom/icon-theme-system.tsx +113 -0
  13. package/src/components/admin-page-header.tsx +29 -0
  14. package/src/components/color-theme-switch.tsx +93 -0
  15. package/src/components/coming-soon.tsx +16 -0
  16. package/src/components/command-menu.tsx +98 -0
  17. package/src/components/config-drawer.tsx +401 -0
  18. package/src/components/confirm-dialog.dom.test.tsx +60 -0
  19. package/src/components/confirm-dialog.tsx +75 -0
  20. package/src/components/data-table/bulk-actions.tsx +193 -0
  21. package/src/components/data-table/column-header.tsx +66 -0
  22. package/src/components/data-table/data-table.dom.test.tsx +54 -0
  23. package/src/components/data-table/data-table.tsx +189 -0
  24. package/src/components/data-table/faceted-filter.tsx +156 -0
  25. package/src/components/data-table/index.ts +5 -0
  26. package/src/components/data-table/pagination.tsx +127 -0
  27. package/src/components/data-table/tanstack-table.d.ts +12 -0
  28. package/src/components/data-table/toolbar.tsx +88 -0
  29. package/src/components/data-table/view-options.tsx +49 -0
  30. package/src/components/date-picker.tsx +39 -0
  31. package/src/components/detail-card.tsx +63 -0
  32. package/src/components/language-switch.tsx +38 -0
  33. package/src/components/layout/app-sidebar.tsx +59 -0
  34. package/src/components/layout/app-title.tsx +62 -0
  35. package/src/components/layout/header.tsx +61 -0
  36. package/src/components/layout/main.tsx +34 -0
  37. package/src/components/layout/nav-group.tsx +185 -0
  38. package/src/components/layout/nav-user.tsx +115 -0
  39. package/src/components/layout/screen-header.tsx +119 -0
  40. package/src/components/layout/team-switcher.tsx +84 -0
  41. package/src/components/layout/top-nav.tsx +68 -0
  42. package/src/components/layout/types.ts +44 -0
  43. package/src/components/learn-more.tsx +35 -0
  44. package/src/components/long-text.tsx +80 -0
  45. package/src/components/navigation-progress.tsx +18 -0
  46. package/src/components/password-input.tsx +36 -0
  47. package/src/components/profile-dropdown.tsx +106 -0
  48. package/src/components/search.tsx +38 -0
  49. package/src/components/select-dropdown.tsx +62 -0
  50. package/src/components/sign-out-dialog.tsx +31 -0
  51. package/src/components/skip-to-main.tsx +10 -0
  52. package/src/components/stat-tile.tsx +56 -0
  53. package/src/components/status-badge.test.ts +23 -0
  54. package/src/components/status-badge.tsx +49 -0
  55. package/src/components/theme-switch.tsx +61 -0
  56. package/src/components/ui/alert-dialog.tsx +134 -0
  57. package/src/components/ui/alert.tsx +59 -0
  58. package/src/components/ui/avatar.tsx +38 -0
  59. package/src/components/ui/badge.tsx +39 -0
  60. package/src/components/ui/base-ui-state-attrs.dom.test.tsx +80 -0
  61. package/src/components/ui/button.tsx +51 -0
  62. package/src/components/ui/calendar.tsx +174 -0
  63. package/src/components/ui/card.tsx +74 -0
  64. package/src/components/ui/checkbox.tsx +26 -0
  65. package/src/components/ui/collapsible.tsx +18 -0
  66. package/src/components/ui/command.tsx +160 -0
  67. package/src/components/ui/dialog.tsx +129 -0
  68. package/src/components/ui/dropdown-menu.tsx +254 -0
  69. package/src/components/ui/form.tsx +155 -0
  70. package/src/components/ui/input-otp.tsx +67 -0
  71. package/src/components/ui/input.tsx +20 -0
  72. package/src/components/ui/label.tsx +21 -0
  73. package/src/components/ui/popover.tsx +54 -0
  74. package/src/components/ui/radio-group.tsx +37 -0
  75. package/src/components/ui/scroll-area.tsx +57 -0
  76. package/src/components/ui/select.tsx +213 -0
  77. package/src/components/ui/separator.tsx +23 -0
  78. package/src/components/ui/sheet.tsx +128 -0
  79. package/src/components/ui/sidebar.tsx +702 -0
  80. package/src/components/ui/skeleton.tsx +13 -0
  81. package/src/components/ui/sonner.tsx +26 -0
  82. package/src/components/ui/switch.tsx +25 -0
  83. package/src/components/ui/table.tsx +89 -0
  84. package/src/components/ui/tabs.tsx +51 -0
  85. package/src/components/ui/textarea.tsx +17 -0
  86. package/src/components/ui/tooltip.tsx +65 -0
  87. package/src/config/fonts.ts +2 -0
  88. package/src/context/direction-provider.tsx +61 -0
  89. package/src/context/font-provider.tsx +70 -0
  90. package/src/context/language-provider.tsx +73 -0
  91. package/src/context/layout-provider.tsx +81 -0
  92. package/src/context/search-provider.tsx +48 -0
  93. package/src/context/theme-provider.tsx +108 -0
  94. package/src/hooks/sort-url.test.ts +16 -0
  95. package/src/hooks/use-dialog-state.tsx +15 -0
  96. package/src/hooks/use-mobile.tsx +16 -0
  97. package/src/hooks/use-table-url-state.ts +243 -0
  98. package/src/i18n/common.ts +164 -0
  99. package/src/i18n/create-i18n.test.ts +50 -0
  100. package/src/i18n/create-i18n.ts +67 -0
  101. package/src/i18n/types.ts +22 -0
  102. package/src/i18n/use-translation.ts +6 -0
  103. package/src/lib/clipboard.test.ts +46 -0
  104. package/src/lib/clipboard.ts +29 -0
  105. package/src/lib/cookies.ts +39 -0
  106. package/src/lib/format-cell.test.ts +57 -0
  107. package/src/lib/format-cell.ts +63 -0
  108. package/src/lib/handle-server-error.test.ts +85 -0
  109. package/src/lib/handle-server-error.ts +39 -0
  110. package/src/lib/random-id.ts +13 -0
  111. package/src/lib/show-submitted-data.tsx +14 -0
  112. package/src/lib/utils.test.ts +35 -0
  113. package/src/lib/utils.ts +78 -0
  114. package/src/secure-context.contract.test.ts +63 -0
  115. package/src/testids.contract.test.ts +113 -0
  116. package/src/testids.ts +8 -0
  117. package/src/theme-contract.test.ts +72 -0
@@ -0,0 +1,134 @@
1
+ import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
2
+ import { buttonVariants } from "@icomponents/react/components/ui/button";
3
+ import { cn } from "@icomponents/react/lib/utils";
4
+ import * as React from "react";
5
+
6
+ function AlertDialog({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
7
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
8
+ }
9
+
10
+ function AlertDialogTrigger({
11
+ ...props
12
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
13
+ return <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />;
14
+ }
15
+
16
+ function AlertDialogPortal({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
17
+ return <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />;
18
+ }
19
+
20
+ function AlertDialogOverlay({
21
+ className,
22
+ ...props
23
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Backdrop>) {
24
+ return (
25
+ <AlertDialogPrimitive.Backdrop
26
+ data-slot="alert-dialog-overlay"
27
+ className={cn(
28
+ "fixed inset-0 z-50 bg-black/50 data-closed:animate-out data-closed:fade-out-0 data-open:animate-in data-open:fade-in-0",
29
+ className
30
+ )}
31
+ {...props}
32
+ />
33
+ );
34
+ }
35
+
36
+ function AlertDialogContent({
37
+ className,
38
+ ...props
39
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Popup>) {
40
+ return (
41
+ <AlertDialogPortal>
42
+ <AlertDialogOverlay />
43
+ <AlertDialogPrimitive.Popup
44
+ data-slot="alert-dialog-content"
45
+ className={cn(
46
+ "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 sm:max-w-lg",
47
+ className
48
+ )}
49
+ {...props}
50
+ />
51
+ </AlertDialogPortal>
52
+ );
53
+ }
54
+
55
+ function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">) {
56
+ return (
57
+ <div
58
+ data-slot="alert-dialog-header"
59
+ className={cn("flex flex-col gap-2 text-center sm:text-start", className)}
60
+ {...props}
61
+ />
62
+ );
63
+ }
64
+
65
+ function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">) {
66
+ return (
67
+ <div
68
+ data-slot="alert-dialog-footer"
69
+ className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
70
+ {...props}
71
+ />
72
+ );
73
+ }
74
+
75
+ function AlertDialogTitle({
76
+ className,
77
+ ...props
78
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
79
+ return (
80
+ <AlertDialogPrimitive.Title
81
+ data-slot="alert-dialog-title"
82
+ className={cn("text-lg font-semibold", className)}
83
+ {...props}
84
+ />
85
+ );
86
+ }
87
+
88
+ function AlertDialogDescription({
89
+ className,
90
+ ...props
91
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
92
+ return (
93
+ <AlertDialogPrimitive.Description
94
+ data-slot="alert-dialog-description"
95
+ className={cn("text-sm text-muted-foreground", className)}
96
+ {...props}
97
+ />
98
+ );
99
+ }
100
+
101
+ // Base UI AlertDialog has no Action part — reuse Close (click fires, then the dialog closes),
102
+ // styled as the primary button. Same close-on-click semantics as Radix Action.
103
+ function AlertDialogAction({
104
+ className,
105
+ ...props
106
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Close>) {
107
+ return <AlertDialogPrimitive.Close className={cn(buttonVariants(), className)} {...props} />;
108
+ }
109
+
110
+ function AlertDialogCancel({
111
+ className,
112
+ ...props
113
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Close>) {
114
+ return (
115
+ <AlertDialogPrimitive.Close
116
+ className={cn(buttonVariants({ variant: "outline" }), className)}
117
+ {...props}
118
+ />
119
+ );
120
+ }
121
+
122
+ export {
123
+ AlertDialog,
124
+ AlertDialogPortal,
125
+ AlertDialogOverlay,
126
+ AlertDialogTrigger,
127
+ AlertDialogContent,
128
+ AlertDialogHeader,
129
+ AlertDialogFooter,
130
+ AlertDialogTitle,
131
+ AlertDialogDescription,
132
+ AlertDialogAction,
133
+ AlertDialogCancel,
134
+ };
@@ -0,0 +1,59 @@
1
+ import { cn } from "@icomponents/react/lib/utils";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import * as React from "react";
4
+
5
+ const alertVariants = cva(
6
+ "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "bg-card text-card-foreground",
11
+ destructive:
12
+ "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
13
+ },
14
+ },
15
+ defaultVariants: {
16
+ variant: "default",
17
+ },
18
+ }
19
+ );
20
+
21
+ function Alert({
22
+ className,
23
+ variant,
24
+ ...props
25
+ }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
26
+ return (
27
+ <div
28
+ data-slot="alert"
29
+ role="alert"
30
+ className={cn(alertVariants({ variant }), className)}
31
+ {...props}
32
+ />
33
+ );
34
+ }
35
+
36
+ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
37
+ return (
38
+ <div
39
+ data-slot="alert-title"
40
+ className={cn("col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight", className)}
41
+ {...props}
42
+ />
43
+ );
44
+ }
45
+
46
+ function AlertDescription({ className, ...props }: React.ComponentProps<"div">) {
47
+ return (
48
+ <div
49
+ data-slot="alert-description"
50
+ className={cn(
51
+ "col-start-2 grid justify-items-start gap-1 text-sm text-muted-foreground [&_p]:leading-relaxed",
52
+ className
53
+ )}
54
+ {...props}
55
+ />
56
+ );
57
+ }
58
+
59
+ export { Alert, AlertTitle, AlertDescription };
@@ -0,0 +1,38 @@
1
+ import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar";
2
+ import { cn } from "@icomponents/react/lib/utils";
3
+ import * as React from "react";
4
+
5
+ function Avatar({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
6
+ return (
7
+ <AvatarPrimitive.Root
8
+ data-slot="avatar"
9
+ className={cn("relative flex size-8 shrink-0 overflow-hidden rounded-full", className)}
10
+ {...props}
11
+ />
12
+ );
13
+ }
14
+
15
+ function AvatarImage({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
16
+ return (
17
+ <AvatarPrimitive.Image
18
+ data-slot="avatar-image"
19
+ className={cn("aspect-square size-full", className)}
20
+ {...props}
21
+ />
22
+ );
23
+ }
24
+
25
+ function AvatarFallback({
26
+ className,
27
+ ...props
28
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
29
+ return (
30
+ <AvatarPrimitive.Fallback
31
+ data-slot="avatar-fallback"
32
+ className={cn("flex size-full items-center justify-center rounded-full bg-muted", className)}
33
+ {...props}
34
+ />
35
+ );
36
+ }
37
+
38
+ export { Avatar, AvatarImage, AvatarFallback };
@@ -0,0 +1,39 @@
1
+ import { mergeProps } from "@base-ui/react/merge-props";
2
+ import { useRender } from "@base-ui/react/use-render";
3
+ import { cn } from "@icomponents/react/lib/utils";
4
+ import { cva, type VariantProps } from "class-variance-authority";
5
+
6
+ const badgeVariants = cva(
7
+ "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
12
+ secondary:
13
+ "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
14
+ destructive:
15
+ "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
16
+ outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ variant: "default",
21
+ },
22
+ }
23
+ );
24
+
25
+ // Slot/asChild polymorphism → Base UI `useRender` (per migrate-radix-to-base). Default element is a
26
+ // <span>; pass `render` to compose (e.g. an <a> for a link badge).
27
+ function Badge({
28
+ className,
29
+ variant,
30
+ render,
31
+ ...props
32
+ }: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
33
+ return useRender({
34
+ render: render ?? <span data-slot="badge" />,
35
+ props: mergeProps<"span">({ className: cn(badgeVariants({ variant }), className) }, props),
36
+ });
37
+ }
38
+
39
+ export { Badge, badgeVariants };
@@ -0,0 +1,80 @@
1
+ import { afterEach, describe, expect, it } from "bun:test";
2
+
3
+ import { cleanup, render, screen } from "@testing-library/react";
4
+ import { userEvent } from "@testing-library/user-event";
5
+
6
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./collapsible";
7
+ import {
8
+ DropdownMenu,
9
+ DropdownMenuContent,
10
+ DropdownMenuItem,
11
+ DropdownMenuTrigger,
12
+ } from "./dropdown-menu";
13
+ import { Switch } from "./switch";
14
+
15
+ /**
16
+ * Контракт data-атрибутов Base UI — то, на что цепляются Tailwind-варианты во ВСЁМ пакете.
17
+ *
18
+ * Зачем этот тест существует. Открытое/закрытое состояние в Base UI выражается атрибутом-флагом
19
+ * (`data-popup-open`, `data-panel-open`, `data-checked`), а НЕ радиксовым `data-state="open"`.
20
+ * Разница не видна ни компилятору, ни обычному DOM-тесту: класс `data-[state=open]:bg-accent`
21
+ * прекрасно компилируется, просто никогда ни к чему не применяется. Такая опечатка не падает —
22
+ * она тихо гасит подсветку открытого меню, поворот шеврона, активный пункт сайдбара.
23
+ *
24
+ * Именно это и случилось при переносе с Radix: пять селекторов пережили порт мёртвыми.
25
+ * Тест фиксирует имена атрибутов, чтобы следующий апгрейд Base UI (или следующий порт) ломался
26
+ * ГРОМКО — здесь, а не через месяц в глазах пользователя.
27
+ */
28
+ afterEach(cleanup);
29
+
30
+ describe("Base UI: контракт атрибутов состояния", () => {
31
+ it("триггер меню помечается data-popup-open (а не data-state=open)", async () => {
32
+ render(
33
+ <DropdownMenu>
34
+ <DropdownMenuTrigger data-testid="trigger">меню</DropdownMenuTrigger>
35
+ <DropdownMenuContent>
36
+ <DropdownMenuItem>пункт</DropdownMenuItem>
37
+ </DropdownMenuContent>
38
+ </DropdownMenu>
39
+ );
40
+
41
+ const trigger = screen.getByTestId("trigger");
42
+ expect(trigger.hasAttribute("data-popup-open")).toBe(false);
43
+
44
+ await userEvent.click(trigger);
45
+
46
+ expect(trigger.hasAttribute("data-popup-open")).toBe(true);
47
+ // Радиксовая форма НЕ появляется — селекторы data-[state=open] были бы мертвы.
48
+ expect(trigger.getAttribute("data-state")).toBeNull();
49
+ });
50
+
51
+ it("триггер и панель Collapsible помечаются data-panel-open / data-open", async () => {
52
+ render(
53
+ <Collapsible>
54
+ <CollapsibleTrigger data-testid="trigger">раскрыть</CollapsibleTrigger>
55
+ <CollapsibleContent data-testid="panel">содержимое</CollapsibleContent>
56
+ </Collapsible>
57
+ );
58
+
59
+ const trigger = screen.getByTestId("trigger");
60
+ expect(trigger.hasAttribute("data-panel-open")).toBe(false);
61
+
62
+ await userEvent.click(trigger);
63
+
64
+ expect(trigger.hasAttribute("data-panel-open")).toBe(true);
65
+ expect(trigger.getAttribute("data-state")).toBeNull();
66
+ expect(screen.getByTestId("panel").hasAttribute("data-open")).toBe(true);
67
+ });
68
+
69
+ it("switch помечается data-checked / data-unchecked", async () => {
70
+ render(<Switch data-testid="switch" />);
71
+
72
+ const control = screen.getByTestId("switch");
73
+ expect(control.hasAttribute("data-unchecked")).toBe(true);
74
+
75
+ await userEvent.click(control);
76
+
77
+ expect(control.hasAttribute("data-checked")).toBe(true);
78
+ expect(control.getAttribute("data-state")).toBeNull();
79
+ });
80
+ });
@@ -0,0 +1,51 @@
1
+ import { Button as ButtonPrimitive } from "@base-ui/react/button";
2
+ import { cn } from "@icomponents/react/lib/utils";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import * as React from "react";
5
+
6
+ const buttonVariants = cva(
7
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
12
+ destructive:
13
+ "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
14
+ outline:
15
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
16
+ secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
17
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
18
+ link: "text-primary underline-offset-4 hover:underline",
19
+ },
20
+ size: {
21
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
22
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
23
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
24
+ icon: "size-9",
25
+ },
26
+ },
27
+ defaultVariants: {
28
+ variant: "default",
29
+ size: "default",
30
+ },
31
+ }
32
+ );
33
+
34
+ // Migrated to Base UI's real Button primitive (per migrate-radix-to-base). Polymorphism is the
35
+ // `render` prop (Base UI) instead of Radix `asChild` — no consumer used `<Button asChild>`.
36
+ function Button({
37
+ className,
38
+ variant,
39
+ size,
40
+ ...props
41
+ }: React.ComponentProps<typeof ButtonPrimitive> & VariantProps<typeof buttonVariants>) {
42
+ return (
43
+ <ButtonPrimitive
44
+ data-slot="button"
45
+ className={cn(buttonVariants({ variant, size, className }))}
46
+ {...props}
47
+ />
48
+ );
49
+ }
50
+
51
+ export { Button, buttonVariants };
@@ -0,0 +1,174 @@
1
+ import { Button, buttonVariants } from "@icomponents/react/components/ui/button";
2
+ import { cn } from "@icomponents/react/lib/utils";
3
+ import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
4
+ import * as React from "react";
5
+ import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker";
6
+
7
+ function Calendar({
8
+ className,
9
+ classNames,
10
+ showOutsideDays = true,
11
+ captionLayout = "label",
12
+ buttonVariant = "ghost",
13
+ formatters,
14
+ components,
15
+ ...props
16
+ }: React.ComponentProps<typeof DayPicker> & {
17
+ buttonVariant?: React.ComponentProps<typeof Button>["variant"];
18
+ }) {
19
+ const defaultClassNames = getDefaultClassNames();
20
+
21
+ return (
22
+ <DayPicker
23
+ showOutsideDays={showOutsideDays}
24
+ className={cn(
25
+ "group/calendar bg-background p-3 [--cell-size:--spacing(8)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent",
26
+ String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
27
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
28
+ className
29
+ )}
30
+ captionLayout={captionLayout}
31
+ formatters={{
32
+ formatMonthDropdown: (date) => date.toLocaleString("default", { month: "short" }),
33
+ ...formatters,
34
+ }}
35
+ classNames={{
36
+ root: cn("w-fit", defaultClassNames.root),
37
+ months: cn("relative flex flex-col gap-4 md:flex-row", defaultClassNames.months),
38
+ month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
39
+ nav: cn(
40
+ "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
41
+ defaultClassNames.nav
42
+ ),
43
+ button_previous: cn(
44
+ buttonVariants({ variant: buttonVariant }),
45
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
46
+ defaultClassNames.button_previous
47
+ ),
48
+ button_next: cn(
49
+ buttonVariants({ variant: buttonVariant }),
50
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
51
+ defaultClassNames.button_next
52
+ ),
53
+ month_caption: cn(
54
+ "flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
55
+ defaultClassNames.month_caption
56
+ ),
57
+ dropdowns: cn(
58
+ "flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
59
+ defaultClassNames.dropdowns
60
+ ),
61
+ dropdown_root: cn(
62
+ "relative rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50",
63
+ defaultClassNames.dropdown_root
64
+ ),
65
+ dropdown: cn("absolute inset-0 bg-popover opacity-0", defaultClassNames.dropdown),
66
+ caption_label: cn(
67
+ "font-medium select-none",
68
+ captionLayout === "label"
69
+ ? "text-sm"
70
+ : "flex h-8 items-center gap-1 rounded-md ps-2 pe-1 text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
71
+ defaultClassNames.caption_label
72
+ ),
73
+ table: "w-full border-collapse",
74
+ weekdays: cn("flex", defaultClassNames.weekdays),
75
+ weekday: cn(
76
+ "flex-1 rounded-md text-[0.8rem] font-normal text-muted-foreground select-none",
77
+ defaultClassNames.weekday
78
+ ),
79
+ week: cn("mt-2 flex w-full", defaultClassNames.week),
80
+ week_number_header: cn("w-(--cell-size) select-none", defaultClassNames.week_number_header),
81
+ week_number: cn(
82
+ "text-[0.8rem] text-muted-foreground select-none",
83
+ defaultClassNames.week_number
84
+ ),
85
+ day: cn(
86
+ "group/day relative aspect-square h-full w-full p-0 text-center select-none [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md",
87
+ defaultClassNames.day
88
+ ),
89
+ range_start: cn("rounded-l-md bg-accent", defaultClassNames.range_start),
90
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
91
+ range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
92
+ today: cn(
93
+ "rounded-md bg-accent text-accent-foreground data-[selected=true]:rounded-none",
94
+ defaultClassNames.today
95
+ ),
96
+ outside: cn(
97
+ "text-muted-foreground aria-selected:text-muted-foreground",
98
+ defaultClassNames.outside
99
+ ),
100
+ disabled: cn("text-muted-foreground opacity-50", defaultClassNames.disabled),
101
+ hidden: cn("invisible", defaultClassNames.hidden),
102
+ ...classNames,
103
+ }}
104
+ components={{
105
+ Root: ({ className, rootRef, ...props }) => {
106
+ return <div data-slot="calendar" ref={rootRef} className={cn(className)} {...props} />;
107
+ },
108
+ Chevron: ({ className, orientation, ...props }) => {
109
+ if (orientation === "left") {
110
+ return <ChevronLeftIcon className={cn("size-4", className)} {...props} />;
111
+ }
112
+
113
+ if (orientation === "right") {
114
+ return <ChevronRightIcon className={cn("size-4", className)} {...props} />;
115
+ }
116
+
117
+ return <ChevronDownIcon className={cn("size-4", className)} {...props} />;
118
+ },
119
+ DayButton: CalendarDayButton,
120
+ WeekNumber: ({ children, ...props }) => {
121
+ return (
122
+ <td {...props}>
123
+ <div className="flex size-(--cell-size) items-center justify-center text-center">
124
+ {children}
125
+ </div>
126
+ </td>
127
+ );
128
+ },
129
+ ...components,
130
+ }}
131
+ {...props}
132
+ />
133
+ );
134
+ }
135
+
136
+ function CalendarDayButton({
137
+ className,
138
+ day,
139
+ modifiers,
140
+ ...props
141
+ }: React.ComponentProps<typeof DayButton>) {
142
+ const defaultClassNames = getDefaultClassNames();
143
+
144
+ const ref = React.useRef<HTMLButtonElement>(null);
145
+ React.useEffect(() => {
146
+ if (modifiers.focused) ref.current?.focus();
147
+ }, [modifiers.focused]);
148
+
149
+ return (
150
+ <Button
151
+ ref={ref}
152
+ variant="ghost"
153
+ size="icon"
154
+ data-day={day.date.toLocaleDateString()}
155
+ data-selected-single={
156
+ modifiers.selected &&
157
+ !modifiers.range_start &&
158
+ !modifiers.range_end &&
159
+ !modifiers.range_middle
160
+ }
161
+ data-range-start={modifiers.range_start}
162
+ data-range-end={modifiers.range_end}
163
+ data-range-middle={modifiers.range_middle}
164
+ className={cn(
165
+ "flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-accent-foreground [&>span]:text-xs [&>span]:opacity-70",
166
+ defaultClassNames.day,
167
+ className
168
+ )}
169
+ {...props}
170
+ />
171
+ );
172
+ }
173
+
174
+ export { Calendar, CalendarDayButton };
@@ -0,0 +1,74 @@
1
+ import { cn } from "@icomponents/react/lib/utils";
2
+ import * as React from "react";
3
+
4
+ function Card({ className, ...props }: React.ComponentProps<"div">) {
5
+ return (
6
+ <div
7
+ data-slot="card"
8
+ className={cn(
9
+ "flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm",
10
+ className
11
+ )}
12
+ {...props}
13
+ />
14
+ );
15
+ }
16
+
17
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
18
+ return (
19
+ <div
20
+ data-slot="card-header"
21
+ className={cn(
22
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ );
28
+ }
29
+
30
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
31
+ return (
32
+ <div
33
+ data-slot="card-title"
34
+ className={cn("leading-none font-semibold", className)}
35
+ {...props}
36
+ />
37
+ );
38
+ }
39
+
40
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
41
+ return (
42
+ <div
43
+ data-slot="card-description"
44
+ className={cn("text-sm text-muted-foreground", className)}
45
+ {...props}
46
+ />
47
+ );
48
+ }
49
+
50
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
51
+ return (
52
+ <div
53
+ data-slot="card-action"
54
+ className={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
55
+ {...props}
56
+ />
57
+ );
58
+ }
59
+
60
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
61
+ return <div data-slot="card-content" className={cn("px-6", className)} {...props} />;
62
+ }
63
+
64
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
65
+ return (
66
+ <div
67
+ data-slot="card-footer"
68
+ className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
69
+ {...props}
70
+ />
71
+ );
72
+ }
73
+
74
+ export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent };
@@ -0,0 +1,26 @@
1
+ import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
2
+ import { cn } from "@icomponents/react/lib/utils";
3
+ import { CheckIcon } from "lucide-react";
4
+ import * as React from "react";
5
+
6
+ function Checkbox({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
7
+ return (
8
+ <CheckboxPrimitive.Root
9
+ data-slot="checkbox"
10
+ className={cn(
11
+ "peer size-4 shrink-0 rounded-lg border border-input shadow-xs transition-shadow outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:data-checked:bg-primary",
12
+ className
13
+ )}
14
+ {...props}
15
+ >
16
+ <CheckboxPrimitive.Indicator
17
+ data-slot="checkbox-indicator"
18
+ className="flex items-center justify-center text-current transition-none"
19
+ >
20
+ <CheckIcon className="size-3.5" />
21
+ </CheckboxPrimitive.Indicator>
22
+ </CheckboxPrimitive.Root>
23
+ );
24
+ }
25
+
26
+ export { Checkbox };