@trycompai/design-system 1.0.29 → 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.29",
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
  }