@trycompai/design-system 1.0.28 → 1.0.30

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trycompai/design-system",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "description": "Design system for Comp AI - shadcn-style components with Tailwind CSS",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -1,22 +1,235 @@
1
1
  import * as React from 'react';
2
2
 
3
+ import {
4
+ Pagination,
5
+ PaginationContent,
6
+ PaginationEllipsis,
7
+ PaginationItem,
8
+ PaginationLink,
9
+ PaginationNext,
10
+ PaginationPrevious,
11
+ } from './pagination';
12
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './select';
13
+ import { Text } from '../atoms/text';
14
+
15
+ type PaginationConfig = {
16
+ page: number;
17
+ pageCount: number;
18
+ onPageChange: (page: number) => void;
19
+ pageSize?: number;
20
+ pageSizeOptions?: number[];
21
+ onPageSizeChange?: (pageSize: number) => void;
22
+ siblingCount?: number;
23
+ showPrevNext?: boolean;
24
+ showEdges?: boolean;
25
+ };
26
+
27
+ type PaginationItemValue = number | 'ellipsis';
28
+
29
+ function getPaginationRange({
30
+ page,
31
+ pageCount,
32
+ siblingCount,
33
+ showEdges,
34
+ }: {
35
+ page: number;
36
+ pageCount: number;
37
+ siblingCount: number;
38
+ showEdges: boolean;
39
+ }): PaginationItemValue[] {
40
+ if (pageCount <= 1) {
41
+ return [];
42
+ }
43
+
44
+ const totalNumbers = siblingCount * 2 + (showEdges ? 5 : 3);
45
+ if (pageCount <= totalNumbers) {
46
+ return Array.from({ length: pageCount }, (_, index) => index + 1);
47
+ }
48
+
49
+ const leftSibling = Math.max(page - siblingCount, 1);
50
+ const rightSibling = Math.min(page + siblingCount, pageCount);
51
+ const showLeftEllipsis = showEdges ? leftSibling > 2 : leftSibling > 1;
52
+ const showRightEllipsis = showEdges ? rightSibling < pageCount - 1 : rightSibling < pageCount;
53
+
54
+ const range: PaginationItemValue[] = [];
55
+ if (showEdges) {
56
+ range.push(1);
57
+ }
58
+
59
+ if (showLeftEllipsis) {
60
+ range.push('ellipsis');
61
+ }
62
+
63
+ const start = showEdges ? Math.max(leftSibling, 2) : leftSibling;
64
+ const end = showEdges ? Math.min(rightSibling, pageCount - 1) : rightSibling;
65
+ for (let current = start; current <= end; current += 1) {
66
+ range.push(current);
67
+ }
68
+
69
+ if (showRightEllipsis) {
70
+ range.push('ellipsis');
71
+ }
72
+
73
+ if (showEdges) {
74
+ range.push(pageCount);
75
+ }
76
+
77
+ return range;
78
+ }
79
+
80
+ function renderPagination({
81
+ page,
82
+ pageCount,
83
+ onPageChange,
84
+ siblingCount = 1,
85
+ showPrevNext = true,
86
+ showEdges = true,
87
+ }: PaginationConfig) {
88
+ if (pageCount <= 1) {
89
+ return null;
90
+ }
91
+
92
+ const range = getPaginationRange({
93
+ page,
94
+ pageCount,
95
+ siblingCount,
96
+ showEdges,
97
+ });
98
+
99
+ return (
100
+ <Pagination>
101
+ <PaginationContent>
102
+ {showPrevNext && page > 1 && (
103
+ <PaginationItem>
104
+ <PaginationPrevious
105
+ href="#"
106
+ onClick={(event) => {
107
+ event.preventDefault();
108
+ onPageChange(page - 1);
109
+ }}
110
+ />
111
+ </PaginationItem>
112
+ )}
113
+ {range.map((item, index) =>
114
+ item === 'ellipsis' ? (
115
+ <PaginationItem key={`ellipsis-${index}`}>
116
+ <PaginationEllipsis />
117
+ </PaginationItem>
118
+ ) : (
119
+ <PaginationItem key={item}>
120
+ <PaginationLink
121
+ href="#"
122
+ isActive={item === page}
123
+ onClick={(event) => {
124
+ event.preventDefault();
125
+ onPageChange(item);
126
+ }}
127
+ >
128
+ {item}
129
+ </PaginationLink>
130
+ </PaginationItem>
131
+ ),
132
+ )}
133
+ {showPrevNext && page < pageCount && (
134
+ <PaginationItem>
135
+ <PaginationNext
136
+ href="#"
137
+ onClick={(event) => {
138
+ event.preventDefault();
139
+ onPageChange(page + 1);
140
+ }}
141
+ />
142
+ </PaginationItem>
143
+ )}
144
+ </PaginationContent>
145
+ </Pagination>
146
+ );
147
+ }
148
+
3
149
  function Table({
4
150
  variant = 'default',
151
+ pagination,
5
152
  ...props
6
153
  }: Omit<React.ComponentProps<'table'>, 'className'> & {
7
154
  variant?: 'default' | 'bordered';
155
+ pagination?: PaginationConfig;
8
156
  }) {
157
+ const [internalPage, setInternalPage] = React.useState(pagination?.page ?? 1);
158
+ React.useEffect(() => {
159
+ if (pagination && pagination.page !== internalPage) {
160
+ setInternalPage(pagination.page);
161
+ }
162
+ }, [pagination, internalPage]);
163
+
164
+ const paginationContent = pagination
165
+ ? renderPagination({
166
+ ...pagination,
167
+ page: internalPage,
168
+ onPageChange: (nextPage) => {
169
+ setInternalPage(nextPage);
170
+ pagination.onPageChange(nextPage);
171
+ },
172
+ })
173
+ : null;
174
+ const showPageSizeSelector = Boolean(pagination?.pageSize && pagination?.onPageSizeChange);
175
+ const rawPageSize = pagination?.pageSize ?? 20;
176
+ const pageSizeOptions = pagination?.pageSizeOptions ?? [20, 50, 100];
177
+ const normalizedPageSizeOptions = pageSizeOptions.includes(rawPageSize)
178
+ ? pageSizeOptions
179
+ : [rawPageSize, ...pageSizeOptions];
180
+
9
181
  return (
10
182
  <div
11
183
  data-slot="table-container"
12
184
  data-variant={variant}
13
- className="relative w-full overflow-x-auto data-[variant=bordered]:border data-[variant=bordered]:rounded-lg"
185
+ className="relative w-full data-[variant=bordered]:border data-[variant=bordered]:rounded-lg"
14
186
  >
15
- <table
16
- data-slot="table"
17
- className="w-full caption-bottom text-sm [&_[data-slot=text][data-default-size=true]]:text-sm"
18
- {...props}
19
- />
187
+ <div data-slot="table-scroll" className="w-full overflow-x-auto">
188
+ <table
189
+ data-slot="table"
190
+ className="w-full caption-bottom text-sm [&_[data-slot=text][data-default-size=true]]:text-sm"
191
+ {...props}
192
+ />
193
+ </div>
194
+ {paginationContent && (
195
+ <div
196
+ data-slot="table-pagination"
197
+ data-has-size={showPageSizeSelector ? 'true' : 'false'}
198
+ className="grid items-center border-t bg-muted/40 px-3 py-2 data-[variant=bordered]:rounded-b-lg grid-cols-[1fr_auto_1fr] gap-3"
199
+ >
200
+ {showPageSizeSelector && (
201
+ <div
202
+ data-slot="table-pagination-size"
203
+ className="flex items-center gap-2 whitespace-nowrap"
204
+ >
205
+ <Text as="span" size="xs" variant="muted">
206
+ Rows per page
207
+ </Text>
208
+ <div className="w-20">
209
+ <Select
210
+ value={String(rawPageSize)}
211
+ onValueChange={(value) => pagination?.onPageSizeChange?.(Number(value))}
212
+ >
213
+ <SelectTrigger size="sm">
214
+ <SelectValue />
215
+ </SelectTrigger>
216
+ <SelectContent align="start">
217
+ {normalizedPageSizeOptions.map((option) => (
218
+ <SelectItem key={option} value={String(option)}>
219
+ {option}
220
+ </SelectItem>
221
+ ))}
222
+ </SelectContent>
223
+ </Select>
224
+ </div>
225
+ </div>
226
+ )}
227
+ <div data-slot="table-pagination-controls" className="flex items-center justify-center">
228
+ {paginationContent}
229
+ </div>
230
+ <div data-slot="table-pagination-spacer" />
231
+ </div>
232
+ )}
20
233
  </div>
21
234
  );
22
235
  }
@@ -53,7 +53,7 @@ function SheetContent({
53
53
  <SheetPrimitive.Close
54
54
  data-slot="sheet-close"
55
55
  render={<Button variant="ghost" size="icon-sm" />}
56
- className="absolute top-4 right-4"
56
+ className="absolute top-4 right-6"
57
57
  >
58
58
  <Close className="size-4" />
59
59
  <span className="sr-only">Close</span>
@@ -65,18 +65,18 @@ function SheetContent({
65
65
  }
66
66
 
67
67
  function SheetHeader({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
68
- return <div data-slot="sheet-header" className="gap-0.5 px-4 pt-4 flex flex-col" {...props} />;
68
+ return <div data-slot="sheet-header" className="gap-0.5 px-6 pt-4 flex flex-col" {...props} />;
69
69
  }
70
70
 
71
71
  function SheetBody({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
72
- return <div data-slot="sheet-body" className="flex-1 min-h-0 overflow-y-auto px-4 py-4" {...props} />;
72
+ return <div data-slot="sheet-body" className="flex-1 min-h-0 overflow-y-auto px-6 py-4" {...props} />;
73
73
  }
74
74
 
75
75
  function SheetFooter({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
76
76
  return (
77
77
  <div
78
78
  data-slot="sheet-footer"
79
- className="gap-2 px-4 py-4 mt-auto flex flex-col border-t bg-background sticky bottom-0"
79
+ className="gap-2 px-6 py-4 mt-auto flex flex-col border-t bg-background sticky bottom-0"
80
80
  {...props}
81
81
  />
82
82
  );