minka-ds 0.1.0 → 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
CHANGED
|
@@ -25,8 +25,6 @@ const badgeVariants = cva(
|
|
|
25
25
|
"bg-[var(--color-bg-error)] text-[var(--color-feedback-error)] border-transparent",
|
|
26
26
|
info:
|
|
27
27
|
"bg-[var(--color-bg-info)] text-[var(--color-feedback-info)] border-transparent",
|
|
28
|
-
pending:
|
|
29
|
-
"bg-[var(--color-bg-warning)] text-[var(--color-text-default)] border-transparent",
|
|
30
28
|
outline:
|
|
31
29
|
"border-[var(--color-border-default)] text-[var(--color-text-default)] [a&]:hover:bg-[var(--color-action-ghost-hover)]",
|
|
32
30
|
ghost:
|
|
@@ -24,15 +24,15 @@ function TextStack({ primary, secondary, className }: TextStackProps) {
|
|
|
24
24
|
)
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
// ──
|
|
28
|
-
// Single-line
|
|
27
|
+
// ── DataCell ──────────────────────────────────────────────────────────────────
|
|
28
|
+
// Single-line plain data value. Use for IDs, timestamps, codes.
|
|
29
29
|
|
|
30
|
-
interface
|
|
30
|
+
interface DataCellProps {
|
|
31
31
|
children: React.ReactNode
|
|
32
32
|
className?: string
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function
|
|
35
|
+
function DataCell({ children, className }: DataCellProps) {
|
|
36
36
|
return (
|
|
37
37
|
<span
|
|
38
38
|
className={cn(
|
|
@@ -94,10 +94,10 @@ function ActionCell({ children, className }: ActionCellProps) {
|
|
|
94
94
|
)
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
export { TextStack,
|
|
97
|
+
export { TextStack, DataCell, AmountCell, BadgeCell, ActionCell }
|
|
98
98
|
export type {
|
|
99
99
|
TextStackProps,
|
|
100
|
-
|
|
100
|
+
DataCellProps,
|
|
101
101
|
AmountCellProps,
|
|
102
102
|
BadgeCellProps,
|
|
103
103
|
ActionCellProps,
|
|
@@ -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,106 +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
|
|
110
|
+
onRowClick?: (row: TData) => void
|
|
111
|
+
className?: string
|
|
193
112
|
}
|
|
194
113
|
|
|
195
114
|
function DataTable<TData, TValue>({
|
|
196
115
|
columns,
|
|
197
116
|
data,
|
|
198
|
-
|
|
117
|
+
batchSize = 20,
|
|
118
|
+
onRowClick,
|
|
119
|
+
className,
|
|
199
120
|
}: DataTableProps<TData, TValue>) {
|
|
200
121
|
const [sorting, setSorting] = React.useState<SortingState>([])
|
|
201
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
|
+
)
|
|
202
130
|
|
|
203
131
|
const table = useReactTable({
|
|
204
|
-
data,
|
|
132
|
+
data: displayedData,
|
|
205
133
|
columns,
|
|
206
134
|
getCoreRowModel: getCoreRowModel(),
|
|
207
|
-
getPaginationRowModel: getPaginationRowModel(),
|
|
208
135
|
getSortedRowModel: getSortedRowModel(),
|
|
209
136
|
onSortingChange: setSorting,
|
|
210
137
|
onColumnVisibilityChange: setColumnVisibility,
|
|
211
|
-
initialState: { pagination: { pageSize } },
|
|
212
138
|
state: { sorting, columnVisibility },
|
|
213
139
|
})
|
|
214
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
|
+
|
|
215
150
|
return (
|
|
216
|
-
<div className="
|
|
217
|
-
<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
|
+
|
|
218
157
|
<Table className="[&_th:first-child]:pl-4 [&_td:first-child]:pl-4">
|
|
219
|
-
<TableHeader className="bg-[var(--color-bg-base)]">
|
|
158
|
+
<TableHeader className="sticky top-0 z-10 bg-[var(--color-bg-base)]">
|
|
220
159
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
221
160
|
<TableRow key={headerGroup.id}>
|
|
222
161
|
{headerGroup.headers.map((header, index) => (
|
|
@@ -240,6 +179,8 @@ function DataTable<TData, TValue>({
|
|
|
240
179
|
<TableRow
|
|
241
180
|
key={row.id}
|
|
242
181
|
data-state={row.getIsSelected() ? "selected" : undefined}
|
|
182
|
+
onClick={onRowClick ? () => onRowClick(row.original) : undefined}
|
|
183
|
+
className={onRowClick ? "cursor-pointer" : undefined}
|
|
243
184
|
>
|
|
244
185
|
{row.getVisibleCells().map((cell) => (
|
|
245
186
|
<TableCell key={cell.id}>
|
|
@@ -261,15 +202,15 @@ function DataTable<TData, TValue>({
|
|
|
261
202
|
</TableBody>
|
|
262
203
|
</Table>
|
|
263
204
|
</div>
|
|
264
|
-
|
|
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
|
+
)}
|
|
265
208
|
</div>
|
|
266
209
|
)
|
|
267
|
-
|
|
268
210
|
}
|
|
269
211
|
|
|
270
212
|
export {
|
|
271
213
|
DataTable,
|
|
272
214
|
DataTableColumnHeader,
|
|
273
215
|
DataTableColumnToggle,
|
|
274
|
-
DataTablePagination,
|
|
275
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"
|