minka-ds 0.1.1 → 0.1.2
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 +1 -1
- package/src/components/ui/data-table.tsx +31 -94
- package/src/components/ui/kbd.tsx +15 -0
- package/src/hooks/use-platform.ts +11 -0
- package/src/index.ts +4 -0
package/package.json
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
VisibilityState,
|
|
8
8
|
flexRender,
|
|
9
9
|
getCoreRowModel,
|
|
10
|
-
getPaginationRowModel,
|
|
11
10
|
getSortedRowModel,
|
|
12
11
|
useReactTable,
|
|
13
12
|
type Table as TanstackTable,
|
|
@@ -16,15 +15,6 @@ import { ChevronsUpDown, ChevronUp, ChevronDown, Columns3Cog } from "lucide-reac
|
|
|
16
15
|
|
|
17
16
|
import { cn } from "../../lib/utils"
|
|
18
17
|
import { Button } from "./button"
|
|
19
|
-
import {
|
|
20
|
-
Pagination,
|
|
21
|
-
PaginationContent,
|
|
22
|
-
PaginationEllipsis,
|
|
23
|
-
PaginationItem,
|
|
24
|
-
PaginationLink,
|
|
25
|
-
PaginationNext,
|
|
26
|
-
PaginationPrevious,
|
|
27
|
-
} from "./pagination"
|
|
28
18
|
import {
|
|
29
19
|
DropdownMenu,
|
|
30
20
|
DropdownMenuCheckboxItem,
|
|
@@ -44,12 +34,6 @@ import {
|
|
|
44
34
|
|
|
45
35
|
// ── Column header with sort control ──────────────────────────────────────────
|
|
46
36
|
|
|
47
|
-
interface DataTableColumnHeaderProps<TData, TValue>
|
|
48
|
-
extends React.HTMLAttributes<HTMLDivElement> {
|
|
49
|
-
column: TanstackTable<TData>["getColumn"] extends (id: string) => infer C ? NonNullable<C> : never
|
|
50
|
-
title: string
|
|
51
|
-
}
|
|
52
|
-
|
|
53
37
|
function DataTableColumnHeader<TData, TValue>({
|
|
54
38
|
column,
|
|
55
39
|
title,
|
|
@@ -117,108 +101,61 @@ function DataTableColumnToggle<TData>({
|
|
|
117
101
|
)
|
|
118
102
|
}
|
|
119
103
|
|
|
120
|
-
// ── Pagination ────────────────────────────────────────────────────────────────
|
|
121
|
-
|
|
122
|
-
function getPageNumbers(currentPage: number, totalPages: number): (number | "ellipsis")[] {
|
|
123
|
-
if (totalPages <= 7) return Array.from({ length: totalPages }, (_, i) => i + 1)
|
|
124
|
-
const pages: (number | "ellipsis")[] = [1]
|
|
125
|
-
if (currentPage > 3) pages.push("ellipsis")
|
|
126
|
-
const start = Math.max(2, currentPage - 1)
|
|
127
|
-
const end = Math.min(totalPages - 1, currentPage + 1)
|
|
128
|
-
for (let i = start; i <= end; i++) pages.push(i)
|
|
129
|
-
if (currentPage < totalPages - 2) pages.push("ellipsis")
|
|
130
|
-
pages.push(totalPages)
|
|
131
|
-
return pages
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function DataTablePagination<TData>({
|
|
135
|
-
table,
|
|
136
|
-
}: {
|
|
137
|
-
table: TanstackTable<TData>
|
|
138
|
-
}) {
|
|
139
|
-
const currentPage = table.getState().pagination.pageIndex + 1
|
|
140
|
-
const totalPages = table.getPageCount()
|
|
141
|
-
const pages = getPageNumbers(currentPage, totalPages)
|
|
142
|
-
|
|
143
|
-
return (
|
|
144
|
-
<div className="flex items-center justify-between">
|
|
145
|
-
<p className="text-body-sm text-[var(--color-text-muted)]">
|
|
146
|
-
Page {currentPage} of {totalPages}
|
|
147
|
-
</p>
|
|
148
|
-
<Pagination className="mx-0 w-auto justify-end">
|
|
149
|
-
<PaginationContent>
|
|
150
|
-
<PaginationItem>
|
|
151
|
-
<PaginationPrevious
|
|
152
|
-
onClick={() => table.previousPage()}
|
|
153
|
-
aria-disabled={!table.getCanPreviousPage()}
|
|
154
|
-
className={cn(!table.getCanPreviousPage() && "pointer-events-none opacity-50")}
|
|
155
|
-
/>
|
|
156
|
-
</PaginationItem>
|
|
157
|
-
{pages.map((page, i) =>
|
|
158
|
-
page === "ellipsis" ? (
|
|
159
|
-
<PaginationItem key={`ellipsis-${i}`}>
|
|
160
|
-
<PaginationEllipsis />
|
|
161
|
-
</PaginationItem>
|
|
162
|
-
) : (
|
|
163
|
-
<PaginationItem key={page}>
|
|
164
|
-
<PaginationLink
|
|
165
|
-
isActive={page === currentPage}
|
|
166
|
-
onClick={() => table.setPageIndex(page - 1)}
|
|
167
|
-
className="cursor-pointer"
|
|
168
|
-
>
|
|
169
|
-
{page}
|
|
170
|
-
</PaginationLink>
|
|
171
|
-
</PaginationItem>
|
|
172
|
-
)
|
|
173
|
-
)}
|
|
174
|
-
<PaginationItem>
|
|
175
|
-
<PaginationNext
|
|
176
|
-
onClick={() => table.nextPage()}
|
|
177
|
-
aria-disabled={!table.getCanNextPage()}
|
|
178
|
-
className={cn(!table.getCanNextPage() && "pointer-events-none opacity-50")}
|
|
179
|
-
/>
|
|
180
|
-
</PaginationItem>
|
|
181
|
-
</PaginationContent>
|
|
182
|
-
</Pagination>
|
|
183
|
-
</div>
|
|
184
|
-
)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
104
|
// ── DataTable ─────────────────────────────────────────────────────────────────
|
|
188
105
|
|
|
189
106
|
interface DataTableProps<TData, TValue> {
|
|
190
107
|
columns: ColumnDef<TData, TValue>[]
|
|
191
108
|
data: TData[]
|
|
192
|
-
|
|
109
|
+
batchSize?: number
|
|
193
110
|
onRowClick?: (row: TData) => void
|
|
111
|
+
className?: string
|
|
194
112
|
}
|
|
195
113
|
|
|
196
114
|
function DataTable<TData, TValue>({
|
|
197
115
|
columns,
|
|
198
116
|
data,
|
|
199
|
-
|
|
117
|
+
batchSize = 20,
|
|
200
118
|
onRowClick,
|
|
119
|
+
className,
|
|
201
120
|
}: DataTableProps<TData, TValue>) {
|
|
202
121
|
const [sorting, setSorting] = React.useState<SortingState>([])
|
|
203
122
|
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})
|
|
123
|
+
const [displayCount, setDisplayCount] = React.useState(batchSize)
|
|
124
|
+
const [hasMore, setHasMore] = React.useState(data.length > batchSize)
|
|
125
|
+
|
|
126
|
+
const displayedData = React.useMemo(
|
|
127
|
+
() => data.slice(0, displayCount),
|
|
128
|
+
[data, displayCount]
|
|
129
|
+
)
|
|
204
130
|
|
|
205
131
|
const table = useReactTable({
|
|
206
|
-
data,
|
|
132
|
+
data: displayedData,
|
|
207
133
|
columns,
|
|
208
134
|
getCoreRowModel: getCoreRowModel(),
|
|
209
|
-
getPaginationRowModel: getPaginationRowModel(),
|
|
210
135
|
getSortedRowModel: getSortedRowModel(),
|
|
211
136
|
onSortingChange: setSorting,
|
|
212
137
|
onColumnVisibilityChange: setColumnVisibility,
|
|
213
|
-
initialState: { pagination: { pageSize } },
|
|
214
138
|
state: { sorting, columnVisibility },
|
|
215
139
|
})
|
|
216
140
|
|
|
141
|
+
function handleScroll(e: React.UIEvent<HTMLDivElement>) {
|
|
142
|
+
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget
|
|
143
|
+
const remaining = scrollHeight - scrollTop - clientHeight
|
|
144
|
+
setHasMore(remaining > 2 || displayCount < data.length)
|
|
145
|
+
if (remaining < 120) {
|
|
146
|
+
setDisplayCount((c) => Math.min(c + batchSize, data.length))
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
217
150
|
return (
|
|
218
|
-
<div className="
|
|
219
|
-
<div
|
|
151
|
+
<div className={cn("relative flex flex-col min-h-0", className)}>
|
|
152
|
+
<div
|
|
153
|
+
onScroll={handleScroll}
|
|
154
|
+
className="flex-1 min-h-0 overflow-auto rounded-[var(--radius-card)] border border-[var(--color-border-default)] bg-[var(--color-bg-raised)] [&_[data-slot=table-container]]:overflow-visible"
|
|
155
|
+
>
|
|
156
|
+
|
|
220
157
|
<Table className="[&_th:first-child]:pl-4 [&_td:first-child]:pl-4">
|
|
221
|
-
<TableHeader className="bg-[var(--color-bg-base)]">
|
|
158
|
+
<TableHeader className="sticky top-0 z-10 bg-[var(--color-bg-base)]">
|
|
222
159
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
223
160
|
<TableRow key={headerGroup.id}>
|
|
224
161
|
{headerGroup.headers.map((header, index) => (
|
|
@@ -265,15 +202,15 @@ function DataTable<TData, TValue>({
|
|
|
265
202
|
</TableBody>
|
|
266
203
|
</Table>
|
|
267
204
|
</div>
|
|
268
|
-
|
|
205
|
+
{hasMore && (
|
|
206
|
+
<div className="pointer-events-none absolute bottom-0 left-0 right-0 h-16 rounded-b-[var(--radius-card)] bg-gradient-to-t from-[var(--color-bg-raised)] to-transparent" />
|
|
207
|
+
)}
|
|
269
208
|
</div>
|
|
270
209
|
)
|
|
271
|
-
|
|
272
210
|
}
|
|
273
211
|
|
|
274
212
|
export {
|
|
275
213
|
DataTable,
|
|
276
214
|
DataTableColumnHeader,
|
|
277
215
|
DataTableColumnToggle,
|
|
278
|
-
DataTablePagination,
|
|
279
216
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "../../lib/utils"
|
|
3
|
+
|
|
4
|
+
export function Kbd({ children, className }: { children: React.ReactNode; className?: string }) {
|
|
5
|
+
return (
|
|
6
|
+
<kbd
|
|
7
|
+
className={cn(
|
|
8
|
+
"inline-flex items-center gap-1 h-5 font-sans [border-radius:var(--radius-tooltip)] border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] px-1.5 text-caption text-[var(--color-text-muted)]",
|
|
9
|
+
className
|
|
10
|
+
)}
|
|
11
|
+
>
|
|
12
|
+
{children}
|
|
13
|
+
</kbd>
|
|
14
|
+
)
|
|
15
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// Utilities
|
|
2
2
|
export { cn } from "./lib/utils"
|
|
3
3
|
|
|
4
|
+
// Hooks
|
|
5
|
+
export { usePlatform } from "./hooks/use-platform"
|
|
6
|
+
|
|
4
7
|
// Components
|
|
5
8
|
export * from "./components/ui/badge"
|
|
6
9
|
export * from "./components/ui/cell"
|
|
@@ -15,6 +18,7 @@ export * from "./components/ui/dialog"
|
|
|
15
18
|
export * from "./components/ui/dropdown-menu"
|
|
16
19
|
export * from "./components/ui/input"
|
|
17
20
|
export * from "./components/ui/input-group"
|
|
21
|
+
export * from "./components/ui/kbd"
|
|
18
22
|
export * from "./components/ui/label"
|
|
19
23
|
export * from "./components/ui/pagination"
|
|
20
24
|
export * from "./components/ui/select"
|