@snow-labs/brutal-ui 0.1.1 → 0.2.1
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/dist/components/brutal/cta-section.d.ts +7 -2
- package/dist/components/brutal/cta-section.js +136 -32
- package/dist/components/brutal/cta-section.js.map +1 -1
- package/dist/components/brutal/faq.d.ts +16 -0
- package/dist/components/brutal/faq.js +163 -0
- package/dist/components/brutal/faq.js.map +1 -0
- package/dist/components/brutal/feature-grid.d.ts +6 -2
- package/dist/components/brutal/feature-grid.js +73 -10
- package/dist/components/brutal/feature-grid.js.map +1 -1
- package/dist/components/brutal/footer.d.ts +14 -1
- package/dist/components/brutal/footer.js +181 -18
- package/dist/components/brutal/footer.js.map +1 -1
- package/dist/components/brutal/hero.d.ts +9 -2
- package/dist/components/brutal/hero.js +67 -22
- package/dist/components/brutal/hero.js.map +1 -1
- package/dist/components/brutal/index.d.ts +7 -2
- package/dist/components/brutal/index.js +1035 -128
- package/dist/components/brutal/index.js.map +1 -1
- package/dist/components/brutal/integration-grid.d.ts +1 -1
- package/dist/components/brutal/integration-grid.js +13 -5
- package/dist/components/brutal/integration-grid.js.map +1 -1
- package/dist/components/brutal/logo-cloud.d.ts +17 -0
- package/dist/components/brutal/logo-cloud.js +93 -0
- package/dist/components/brutal/logo-cloud.js.map +1 -0
- package/dist/components/brutal/nav.d.ts +3 -1
- package/dist/components/brutal/nav.js +166 -9
- package/dist/components/brutal/nav.js.map +1 -1
- package/dist/components/brutal/newsletter.d.ts +14 -0
- package/dist/components/brutal/newsletter.js +169 -0
- package/dist/components/brutal/newsletter.js.map +1 -0
- package/dist/components/brutal/pricing-table.d.ts +27 -0
- package/dist/components/brutal/pricing-table.js +251 -0
- package/dist/components/brutal/pricing-table.js.map +1 -0
- package/dist/components/brutal/section-divider.d.ts +14 -0
- package/dist/components/brutal/section-divider.js +70 -0
- package/dist/components/brutal/section-divider.js.map +1 -0
- package/dist/components/brutal/section.d.ts +7 -3
- package/dist/components/brutal/section.js +13 -5
- package/dist/components/brutal/section.js.map +1 -1
- package/dist/components/brutal/stats-bar.d.ts +16 -0
- package/dist/components/brutal/stats-bar.js +127 -0
- package/dist/components/brutal/stats-bar.js.map +1 -0
- package/dist/components/brutal/testimonials.d.ts +11 -3
- package/dist/components/brutal/testimonials.js +126 -33
- package/dist/components/brutal/testimonials.js.map +1 -1
- package/dist/components/brutal/wave-divider.d.ts +2 -12
- package/dist/components/brutal/wave-divider.js +54 -26
- package/dist/components/brutal/wave-divider.js.map +1 -1
- package/dist/components/dashboard/activity-feed.d.ts +18 -0
- package/dist/components/dashboard/activity-feed.js +105 -0
- package/dist/components/dashboard/activity-feed.js.map +1 -0
- package/dist/components/dashboard/app-shell.d.ts +19 -0
- package/dist/components/dashboard/app-shell.js +206 -0
- package/dist/components/dashboard/app-shell.js.map +1 -0
- package/dist/components/dashboard/empty-state.d.ts +14 -0
- package/dist/components/dashboard/empty-state.js +96 -0
- package/dist/components/dashboard/empty-state.js.map +1 -0
- package/dist/components/dashboard/file-upload.d.ts +12 -0
- package/dist/components/dashboard/file-upload.js +86 -0
- package/dist/components/dashboard/file-upload.js.map +1 -0
- package/dist/components/dashboard/index.d.ts +10 -0
- package/dist/components/dashboard/index.js +755 -0
- package/dist/components/dashboard/index.js.map +1 -0
- package/dist/components/dashboard/search-bar.d.ts +12 -0
- package/dist/components/dashboard/search-bar.js +49 -0
- package/dist/components/dashboard/search-bar.js.map +1 -0
- package/dist/components/dashboard/sidebar.d.ts +23 -0
- package/dist/components/dashboard/sidebar.js +113 -0
- package/dist/components/dashboard/sidebar.js.map +1 -0
- package/dist/components/dashboard/stat-card.d.ts +13 -0
- package/dist/components/dashboard/stat-card.js +55 -0
- package/dist/components/dashboard/stat-card.js.map +1 -0
- package/dist/components/dashboard/user-menu.d.ts +16 -0
- package/dist/components/dashboard/user-menu.js +179 -0
- package/dist/components/dashboard/user-menu.js.map +1 -0
- package/dist/components/dashboard/view-switcher.d.ts +12 -0
- package/dist/components/dashboard/view-switcher.js +130 -0
- package/dist/components/dashboard/view-switcher.js.map +1 -0
- package/dist/components/views/calendar-view.d.ts +17 -0
- package/dist/components/views/calendar-view.js +182 -0
- package/dist/components/views/calendar-view.js.map +1 -0
- package/dist/components/views/data-table.d.ts +15 -0
- package/dist/components/views/data-table.js +204 -0
- package/dist/components/views/data-table.js.map +1 -0
- package/dist/components/views/grid-view.d.ts +11 -0
- package/dist/components/views/grid-view.js +31 -0
- package/dist/components/views/grid-view.js.map +1 -0
- package/dist/components/views/index.d.ts +7 -0
- package/dist/components/views/index.js +542 -0
- package/dist/components/views/index.js.map +1 -0
- package/dist/components/views/kanban-board.d.ts +21 -0
- package/dist/components/views/kanban-board.js +153 -0
- package/dist/components/views/kanban-board.js.map +1 -0
- package/dist/components/views/list-view.d.ts +19 -0
- package/dist/components/views/list-view.js +96 -0
- package/dist/components/views/list-view.js.map +1 -0
- package/dist/index.d.ts +27 -3
- package/dist/index.js +1881 -142
- package/dist/index.js.map +1 -1
- package/dist/lib/animations.d.ts +68 -0
- package/dist/lib/animations.js +44 -0
- package/dist/lib/animations.js.map +1 -0
- package/dist/templates/dashboard.d.ts +40 -0
- package/dist/templates/dashboard.js +658 -0
- package/dist/templates/dashboard.js.map +1 -0
- package/dist/templates/index.d.ts +4 -0
- package/dist/templates/index.js +2001 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/saas-launch.d.ts +113 -0
- package/dist/templates/saas-launch.js +1394 -0
- package/dist/templates/saas-launch.js.map +1 -0
- package/dist/templates/studio.d.ts +72 -0
- package/dist/templates/studio.js +1099 -0
- package/dist/templates/studio.js.map +1 -0
- package/dist/theme.css +58 -15
- package/package.json +48 -2
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { useState, useRef, useEffect } from 'react';
|
|
5
|
+
import { useReactTable, getPaginationRowModel, getFilteredRowModel, getSortedRowModel, getCoreRowModel, flexRender } from '@tanstack/react-table';
|
|
6
|
+
import { Button as Button$1 } from '@base-ui/react/button';
|
|
7
|
+
import { cva } from 'class-variance-authority';
|
|
8
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
import { Input as Input$1 } from '@base-ui/react/input';
|
|
10
|
+
import { dropTargetForElements, draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
11
|
+
import { Avatar as Avatar$1 } from '@base-ui/react/avatar';
|
|
12
|
+
|
|
13
|
+
// src/lib/utils.ts
|
|
14
|
+
function cn(...inputs) {
|
|
15
|
+
return twMerge(clsx(inputs));
|
|
16
|
+
}
|
|
17
|
+
var buttonVariants = cva(
|
|
18
|
+
"group/button inline-flex shrink-0 items-center justify-center rounded-lg whitespace-nowrap font-bold transition-all duration-150 select-none outline-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
19
|
+
{
|
|
20
|
+
variants: {
|
|
21
|
+
variant: {
|
|
22
|
+
// Primary: Black bg, white text, offset shadow
|
|
23
|
+
default: "border-brutal border-foreground bg-primary text-primary-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm",
|
|
24
|
+
// CTA: Mint green bg — the Bannerbear signature
|
|
25
|
+
cta: "border-brutal border-foreground bg-cta text-cta-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm",
|
|
26
|
+
// Brand: Dynamic brand color bg
|
|
27
|
+
brand: "border-brutal border-foreground bg-brand text-brand-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm",
|
|
28
|
+
// Outline: White bg, black border, offset shadow
|
|
29
|
+
outline: "border-brutal border-foreground bg-background text-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm",
|
|
30
|
+
// Secondary: Light bg, border, smaller shadow
|
|
31
|
+
secondary: "border-brutal border-foreground bg-secondary text-secondary-foreground shadow-brutal-sm hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal active:translate-x-px active:translate-y-px active:shadow-none",
|
|
32
|
+
// Ghost: No border/shadow, subtle hover
|
|
33
|
+
ghost: "hover:bg-secondary hover:text-foreground",
|
|
34
|
+
// Link: Text only
|
|
35
|
+
link: "text-foreground underline-offset-4 hover:underline",
|
|
36
|
+
// Destructive
|
|
37
|
+
destructive: "border-brutal border-destructive bg-destructive text-destructive-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm",
|
|
38
|
+
// Nav: Thin border, no shadow (for nav Sign In buttons)
|
|
39
|
+
nav: "border border-foreground bg-background text-foreground hover:bg-foreground hover:text-background"
|
|
40
|
+
},
|
|
41
|
+
size: {
|
|
42
|
+
xs: "h-7 gap-1 px-2.5 text-xs",
|
|
43
|
+
sm: "h-8 gap-1.5 px-3 text-sm",
|
|
44
|
+
default: "h-10 gap-2 px-5 text-sm",
|
|
45
|
+
lg: "h-12 gap-2 px-7 text-base",
|
|
46
|
+
xl: "h-14 gap-2.5 px-9 text-lg",
|
|
47
|
+
icon: "size-10",
|
|
48
|
+
"icon-sm": "size-8",
|
|
49
|
+
"icon-lg": "size-12"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
defaultVariants: {
|
|
53
|
+
variant: "default",
|
|
54
|
+
size: "default"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
function Button({
|
|
59
|
+
className,
|
|
60
|
+
variant = "default",
|
|
61
|
+
size = "default",
|
|
62
|
+
...props
|
|
63
|
+
}) {
|
|
64
|
+
return /* @__PURE__ */ jsx(
|
|
65
|
+
Button$1,
|
|
66
|
+
{
|
|
67
|
+
"data-slot": "button",
|
|
68
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
69
|
+
...props
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
function Input({ className, type, ...props }) {
|
|
74
|
+
return /* @__PURE__ */ jsx(
|
|
75
|
+
Input$1,
|
|
76
|
+
{
|
|
77
|
+
type,
|
|
78
|
+
"data-slot": "input",
|
|
79
|
+
className: cn(
|
|
80
|
+
"h-10 w-full min-w-0 rounded-lg border-brutal border-foreground bg-background px-3 py-2 text-base font-medium shadow-brutal-sm transition-all outline-none placeholder:text-muted-foreground focus:shadow-brutal focus:-translate-x-0.5 focus:-translate-y-0.5 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive md:text-sm",
|
|
81
|
+
className
|
|
82
|
+
),
|
|
83
|
+
...props
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
function DataTable({
|
|
88
|
+
columns,
|
|
89
|
+
data,
|
|
90
|
+
searchColumn,
|
|
91
|
+
searchPlaceholder = "Search...",
|
|
92
|
+
pageSize = 10,
|
|
93
|
+
emptyMessage = "No data",
|
|
94
|
+
className
|
|
95
|
+
}) {
|
|
96
|
+
const [sorting, setSorting] = useState([]);
|
|
97
|
+
const [globalFilter, setGlobalFilter] = useState("");
|
|
98
|
+
const table = useReactTable({
|
|
99
|
+
data,
|
|
100
|
+
columns,
|
|
101
|
+
state: { sorting, globalFilter },
|
|
102
|
+
onSortingChange: setSorting,
|
|
103
|
+
onGlobalFilterChange: setGlobalFilter,
|
|
104
|
+
getCoreRowModel: getCoreRowModel(),
|
|
105
|
+
getSortedRowModel: getSortedRowModel(),
|
|
106
|
+
getFilteredRowModel: getFilteredRowModel(),
|
|
107
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
108
|
+
initialState: { pagination: { pageSize } }
|
|
109
|
+
});
|
|
110
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-4", className), children: [
|
|
111
|
+
searchColumn !== void 0 && /* @__PURE__ */ jsx(
|
|
112
|
+
Input,
|
|
113
|
+
{
|
|
114
|
+
placeholder: searchPlaceholder,
|
|
115
|
+
value: globalFilter,
|
|
116
|
+
onChange: (e) => setGlobalFilter(e.target.value),
|
|
117
|
+
className: "max-w-sm"
|
|
118
|
+
}
|
|
119
|
+
),
|
|
120
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-auto border-brutal border-foreground shadow-brutal", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
121
|
+
/* @__PURE__ */ jsx("thead", { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(
|
|
122
|
+
"tr",
|
|
123
|
+
{
|
|
124
|
+
className: "border-b-brutal border-foreground bg-secondary",
|
|
125
|
+
children: headerGroup.headers.map((header) => /* @__PURE__ */ jsxs(
|
|
126
|
+
"th",
|
|
127
|
+
{
|
|
128
|
+
className: cn(
|
|
129
|
+
"brutal-label px-4 py-3 text-left",
|
|
130
|
+
header.column.getCanSort() && "cursor-pointer select-none"
|
|
131
|
+
),
|
|
132
|
+
onClick: header.column.getToggleSortingHandler(),
|
|
133
|
+
children: [
|
|
134
|
+
flexRender(
|
|
135
|
+
header.column.columnDef.header,
|
|
136
|
+
header.getContext()
|
|
137
|
+
),
|
|
138
|
+
{
|
|
139
|
+
asc: " \u2191",
|
|
140
|
+
desc: " \u2193"
|
|
141
|
+
}[header.column.getIsSorted()] ?? ""
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
header.id
|
|
145
|
+
))
|
|
146
|
+
},
|
|
147
|
+
headerGroup.id
|
|
148
|
+
)) }),
|
|
149
|
+
/* @__PURE__ */ jsx("tbody", { children: table.getRowModel().rows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
|
|
150
|
+
"td",
|
|
151
|
+
{
|
|
152
|
+
colSpan: columns.length,
|
|
153
|
+
className: "px-4 py-8 text-center text-muted-foreground",
|
|
154
|
+
children: emptyMessage
|
|
155
|
+
}
|
|
156
|
+
) }) : table.getRowModel().rows.map((row, i) => /* @__PURE__ */ jsx(
|
|
157
|
+
"tr",
|
|
158
|
+
{
|
|
159
|
+
className: cn(
|
|
160
|
+
"border-b border-foreground/10 transition-colors hover:bg-brand-muted",
|
|
161
|
+
i % 2 === 1 && "bg-secondary/30"
|
|
162
|
+
),
|
|
163
|
+
children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: flexRender(
|
|
164
|
+
cell.column.columnDef.cell,
|
|
165
|
+
cell.getContext()
|
|
166
|
+
) }, cell.id))
|
|
167
|
+
},
|
|
168
|
+
row.id
|
|
169
|
+
)) })
|
|
170
|
+
] }) }),
|
|
171
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
172
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
173
|
+
"Page ",
|
|
174
|
+
table.getState().pagination.pageIndex + 1,
|
|
175
|
+
" of",
|
|
176
|
+
" ",
|
|
177
|
+
table.getPageCount()
|
|
178
|
+
] }),
|
|
179
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
180
|
+
/* @__PURE__ */ jsx(
|
|
181
|
+
Button,
|
|
182
|
+
{
|
|
183
|
+
variant: "outline",
|
|
184
|
+
size: "sm",
|
|
185
|
+
onClick: () => table.previousPage(),
|
|
186
|
+
disabled: !table.getCanPreviousPage(),
|
|
187
|
+
children: "Previous"
|
|
188
|
+
}
|
|
189
|
+
),
|
|
190
|
+
/* @__PURE__ */ jsx(
|
|
191
|
+
Button,
|
|
192
|
+
{
|
|
193
|
+
variant: "outline",
|
|
194
|
+
size: "sm",
|
|
195
|
+
onClick: () => table.nextPage(),
|
|
196
|
+
disabled: !table.getCanNextPage(),
|
|
197
|
+
children: "Next"
|
|
198
|
+
}
|
|
199
|
+
)
|
|
200
|
+
] })
|
|
201
|
+
] })
|
|
202
|
+
] });
|
|
203
|
+
}
|
|
204
|
+
function KanbanBoard({
|
|
205
|
+
columns,
|
|
206
|
+
renderCard,
|
|
207
|
+
onCardMove,
|
|
208
|
+
onCardAdd,
|
|
209
|
+
className
|
|
210
|
+
}) {
|
|
211
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex gap-4 overflow-x-auto pb-4", className), children: columns.map((column) => /* @__PURE__ */ jsx(
|
|
212
|
+
KanbanColumnComponent,
|
|
213
|
+
{
|
|
214
|
+
column,
|
|
215
|
+
renderCard,
|
|
216
|
+
onCardMove,
|
|
217
|
+
onCardAdd
|
|
218
|
+
},
|
|
219
|
+
column.id
|
|
220
|
+
)) });
|
|
221
|
+
}
|
|
222
|
+
function KanbanColumnComponent({
|
|
223
|
+
column,
|
|
224
|
+
renderCard,
|
|
225
|
+
onCardMove,
|
|
226
|
+
onCardAdd
|
|
227
|
+
}) {
|
|
228
|
+
const ref = useRef(null);
|
|
229
|
+
const [isDragOver, setIsDragOver] = useState(false);
|
|
230
|
+
const [addText, setAddText] = useState("");
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
if (!ref.current) return;
|
|
233
|
+
return dropTargetForElements({
|
|
234
|
+
element: ref.current,
|
|
235
|
+
getData: () => ({ columnId: column.id }),
|
|
236
|
+
onDragEnter: () => setIsDragOver(true),
|
|
237
|
+
onDragLeave: () => setIsDragOver(false),
|
|
238
|
+
onDrop: ({ source }) => {
|
|
239
|
+
setIsDragOver(false);
|
|
240
|
+
const data = source.data;
|
|
241
|
+
if (data.columnId !== column.id) {
|
|
242
|
+
onCardMove?.(
|
|
243
|
+
data.cardId,
|
|
244
|
+
data.columnId,
|
|
245
|
+
column.id,
|
|
246
|
+
column.cards.length
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}, [column.id, column.cards.length, onCardMove]);
|
|
252
|
+
return /* @__PURE__ */ jsxs(
|
|
253
|
+
"div",
|
|
254
|
+
{
|
|
255
|
+
ref,
|
|
256
|
+
className: cn(
|
|
257
|
+
"flex min-w-[280px] flex-col border-brutal border-foreground bg-secondary/30 p-3",
|
|
258
|
+
isDragOver && "ring-2 ring-brand"
|
|
259
|
+
),
|
|
260
|
+
children: [
|
|
261
|
+
/* @__PURE__ */ jsxs("div", { className: "brutal-label mb-3 flex items-center justify-between", children: [
|
|
262
|
+
/* @__PURE__ */ jsx("span", { children: column.title }),
|
|
263
|
+
/* @__PURE__ */ jsx("span", { className: "bg-foreground px-1.5 font-mono text-xs text-background", children: column.cards.length })
|
|
264
|
+
] }),
|
|
265
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col gap-2", children: column.cards.map((card) => /* @__PURE__ */ jsx(
|
|
266
|
+
KanbanCard,
|
|
267
|
+
{
|
|
268
|
+
card,
|
|
269
|
+
columnId: column.id,
|
|
270
|
+
renderCard
|
|
271
|
+
},
|
|
272
|
+
card.id
|
|
273
|
+
)) }),
|
|
274
|
+
onCardAdd && /* @__PURE__ */ jsx(
|
|
275
|
+
"form",
|
|
276
|
+
{
|
|
277
|
+
className: "mt-3",
|
|
278
|
+
onSubmit: (e) => {
|
|
279
|
+
e.preventDefault();
|
|
280
|
+
if (addText.trim()) {
|
|
281
|
+
onCardAdd(column.id, addText.trim());
|
|
282
|
+
setAddText("");
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
children: /* @__PURE__ */ jsx(
|
|
286
|
+
Input,
|
|
287
|
+
{
|
|
288
|
+
value: addText,
|
|
289
|
+
onChange: (e) => setAddText(e.target.value),
|
|
290
|
+
placeholder: "+ Add card...",
|
|
291
|
+
className: "text-sm"
|
|
292
|
+
}
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
)
|
|
296
|
+
]
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
function KanbanCard({
|
|
301
|
+
card,
|
|
302
|
+
columnId,
|
|
303
|
+
renderCard
|
|
304
|
+
}) {
|
|
305
|
+
const ref = useRef(null);
|
|
306
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
307
|
+
useEffect(() => {
|
|
308
|
+
if (!ref.current) return;
|
|
309
|
+
return draggable({
|
|
310
|
+
element: ref.current,
|
|
311
|
+
getInitialData: () => ({ cardId: card.id, columnId }),
|
|
312
|
+
onDragStart: () => setIsDragging(true),
|
|
313
|
+
onDrop: () => setIsDragging(false)
|
|
314
|
+
});
|
|
315
|
+
}, [card.id, columnId]);
|
|
316
|
+
return /* @__PURE__ */ jsx(
|
|
317
|
+
"div",
|
|
318
|
+
{
|
|
319
|
+
ref,
|
|
320
|
+
className: cn(
|
|
321
|
+
"cursor-grab border-brutal border-foreground bg-background p-3 shadow-brutal-sm transition-opacity active:cursor-grabbing",
|
|
322
|
+
isDragging && "opacity-50"
|
|
323
|
+
),
|
|
324
|
+
children: renderCard(card)
|
|
325
|
+
}
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
function GridView({
|
|
329
|
+
children,
|
|
330
|
+
columns = 3,
|
|
331
|
+
gap = 6,
|
|
332
|
+
className
|
|
333
|
+
}) {
|
|
334
|
+
return /* @__PURE__ */ jsx(
|
|
335
|
+
"div",
|
|
336
|
+
{
|
|
337
|
+
className: cn("grid", className),
|
|
338
|
+
style: {
|
|
339
|
+
gridTemplateColumns: `repeat(auto-fill, minmax(${Math.floor(100 / columns) - 2}%, 1fr))`,
|
|
340
|
+
gap: `${gap * 4}px`
|
|
341
|
+
},
|
|
342
|
+
children
|
|
343
|
+
}
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
var DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
347
|
+
function getDaysInMonth(year, month) {
|
|
348
|
+
return new Date(year, month + 1, 0).getDate();
|
|
349
|
+
}
|
|
350
|
+
function getFirstDayOfMonth(year, month) {
|
|
351
|
+
return new Date(year, month, 1).getDay();
|
|
352
|
+
}
|
|
353
|
+
function CalendarView({
|
|
354
|
+
events = [],
|
|
355
|
+
onEventClick,
|
|
356
|
+
onDateClick,
|
|
357
|
+
className
|
|
358
|
+
}) {
|
|
359
|
+
const [currentDate, setCurrentDate] = useState(/* @__PURE__ */ new Date());
|
|
360
|
+
const year = currentDate.getFullYear();
|
|
361
|
+
const month = currentDate.getMonth();
|
|
362
|
+
const daysInMonth = getDaysInMonth(year, month);
|
|
363
|
+
const firstDay = getFirstDayOfMonth(year, month);
|
|
364
|
+
const monthName = currentDate.toLocaleDateString("en-US", {
|
|
365
|
+
month: "long",
|
|
366
|
+
year: "numeric"
|
|
367
|
+
});
|
|
368
|
+
const cells = [];
|
|
369
|
+
for (let i = 0; i < firstDay; i++) cells.push(null);
|
|
370
|
+
for (let d = 1; d <= daysInMonth; d++) cells.push(d);
|
|
371
|
+
const getEventsForDay = (day) => events.filter((e) => {
|
|
372
|
+
const d = typeof e.date === "string" ? new Date(e.date) : e.date;
|
|
373
|
+
return d.getFullYear() === year && d.getMonth() === month && d.getDate() === day;
|
|
374
|
+
});
|
|
375
|
+
const today = /* @__PURE__ */ new Date();
|
|
376
|
+
const isToday = (day) => day === today.getDate() && month === today.getMonth() && year === today.getFullYear();
|
|
377
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-4", className), children: [
|
|
378
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
379
|
+
/* @__PURE__ */ jsx(
|
|
380
|
+
Button,
|
|
381
|
+
{
|
|
382
|
+
variant: "outline",
|
|
383
|
+
size: "sm",
|
|
384
|
+
onClick: () => setCurrentDate(new Date(year, month - 1)),
|
|
385
|
+
children: "\u2190"
|
|
386
|
+
}
|
|
387
|
+
),
|
|
388
|
+
/* @__PURE__ */ jsx("h3", { className: "brutal-h4", children: monthName }),
|
|
389
|
+
/* @__PURE__ */ jsx(
|
|
390
|
+
Button,
|
|
391
|
+
{
|
|
392
|
+
variant: "outline",
|
|
393
|
+
size: "sm",
|
|
394
|
+
onClick: () => setCurrentDate(new Date(year, month + 1)),
|
|
395
|
+
children: "\u2192"
|
|
396
|
+
}
|
|
397
|
+
)
|
|
398
|
+
] }),
|
|
399
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-7 border-brutal border-foreground", children: [
|
|
400
|
+
DAYS.map((day) => /* @__PURE__ */ jsx(
|
|
401
|
+
"div",
|
|
402
|
+
{
|
|
403
|
+
className: "brutal-label border-b-brutal border-foreground bg-secondary px-2 py-2 text-center text-xs",
|
|
404
|
+
children: day
|
|
405
|
+
},
|
|
406
|
+
day
|
|
407
|
+
)),
|
|
408
|
+
cells.map((day, i) => {
|
|
409
|
+
const dayEvents = day ? getEventsForDay(day) : [];
|
|
410
|
+
return /* @__PURE__ */ jsx(
|
|
411
|
+
"div",
|
|
412
|
+
{
|
|
413
|
+
className: cn(
|
|
414
|
+
"min-h-[80px] border-b border-r border-foreground/10 p-1",
|
|
415
|
+
day && onDateClick && "cursor-pointer hover:bg-secondary/50",
|
|
416
|
+
isToday(day ?? 0) && "bg-brand-muted"
|
|
417
|
+
),
|
|
418
|
+
onClick: () => day && onDateClick?.(new Date(year, month, day)),
|
|
419
|
+
children: day && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
420
|
+
/* @__PURE__ */ jsx(
|
|
421
|
+
"span",
|
|
422
|
+
{
|
|
423
|
+
className: cn(
|
|
424
|
+
"text-xs font-bold",
|
|
425
|
+
isToday(day) && "text-brand"
|
|
426
|
+
),
|
|
427
|
+
children: day
|
|
428
|
+
}
|
|
429
|
+
),
|
|
430
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex flex-col gap-0.5", children: [
|
|
431
|
+
dayEvents.slice(0, 3).map((event) => /* @__PURE__ */ jsx(
|
|
432
|
+
"div",
|
|
433
|
+
{
|
|
434
|
+
onClick: (e) => {
|
|
435
|
+
e.stopPropagation();
|
|
436
|
+
onEventClick?.(event);
|
|
437
|
+
},
|
|
438
|
+
className: "cursor-pointer truncate border border-foreground/20 bg-brand-muted px-1 text-[10px] font-medium hover:bg-brand",
|
|
439
|
+
style: event.color ? { borderLeftColor: event.color, borderLeftWidth: 2 } : void 0,
|
|
440
|
+
children: event.title
|
|
441
|
+
},
|
|
442
|
+
event.id
|
|
443
|
+
)),
|
|
444
|
+
dayEvents.length > 3 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
445
|
+
"+",
|
|
446
|
+
dayEvents.length - 3
|
|
447
|
+
] })
|
|
448
|
+
] })
|
|
449
|
+
] })
|
|
450
|
+
},
|
|
451
|
+
i
|
|
452
|
+
);
|
|
453
|
+
})
|
|
454
|
+
] })
|
|
455
|
+
] });
|
|
456
|
+
}
|
|
457
|
+
function Avatar({
|
|
458
|
+
className,
|
|
459
|
+
size = "default",
|
|
460
|
+
...props
|
|
461
|
+
}) {
|
|
462
|
+
return /* @__PURE__ */ jsx(
|
|
463
|
+
Avatar$1.Root,
|
|
464
|
+
{
|
|
465
|
+
"data-slot": "avatar",
|
|
466
|
+
"data-size": size,
|
|
467
|
+
className: cn(
|
|
468
|
+
"group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
|
|
469
|
+
className
|
|
470
|
+
),
|
|
471
|
+
...props
|
|
472
|
+
}
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
function AvatarImage({ className, ...props }) {
|
|
476
|
+
return /* @__PURE__ */ jsx(
|
|
477
|
+
Avatar$1.Image,
|
|
478
|
+
{
|
|
479
|
+
"data-slot": "avatar-image",
|
|
480
|
+
className: cn(
|
|
481
|
+
"aspect-square size-full rounded-full object-cover",
|
|
482
|
+
className
|
|
483
|
+
),
|
|
484
|
+
...props
|
|
485
|
+
}
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
function AvatarFallback({
|
|
489
|
+
className,
|
|
490
|
+
...props
|
|
491
|
+
}) {
|
|
492
|
+
return /* @__PURE__ */ jsx(
|
|
493
|
+
Avatar$1.Fallback,
|
|
494
|
+
{
|
|
495
|
+
"data-slot": "avatar-fallback",
|
|
496
|
+
className: cn(
|
|
497
|
+
"flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
|
|
498
|
+
className
|
|
499
|
+
),
|
|
500
|
+
...props
|
|
501
|
+
}
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
function ListView({
|
|
505
|
+
items,
|
|
506
|
+
onItemClick,
|
|
507
|
+
emptyMessage = "No items",
|
|
508
|
+
className
|
|
509
|
+
}) {
|
|
510
|
+
if (items.length === 0) {
|
|
511
|
+
return /* @__PURE__ */ jsx("p", { className: "py-8 text-center text-muted-foreground", children: emptyMessage });
|
|
512
|
+
}
|
|
513
|
+
return /* @__PURE__ */ jsx("div", { className: cn("border-brutal border-foreground shadow-brutal", className), children: items.map((item, i) => /* @__PURE__ */ jsxs(
|
|
514
|
+
"div",
|
|
515
|
+
{
|
|
516
|
+
onClick: () => onItemClick?.(item),
|
|
517
|
+
className: cn(
|
|
518
|
+
"flex items-center gap-4 px-4 py-3 transition-colors",
|
|
519
|
+
onItemClick && "cursor-pointer",
|
|
520
|
+
"hover:bg-secondary/50",
|
|
521
|
+
i < items.length - 1 && "border-b border-foreground/10"
|
|
522
|
+
),
|
|
523
|
+
children: [
|
|
524
|
+
item.avatar && /* @__PURE__ */ jsxs(Avatar, { className: "size-8", children: [
|
|
525
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: item.avatar, alt: item.title }),
|
|
526
|
+
/* @__PURE__ */ jsx(AvatarFallback, { children: item.title[0] })
|
|
527
|
+
] }),
|
|
528
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
529
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-bold", children: item.title }),
|
|
530
|
+
item.subtitle && /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: item.subtitle })
|
|
531
|
+
] }),
|
|
532
|
+
item.metadata && /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: item.metadata }),
|
|
533
|
+
item.actions && /* @__PURE__ */ jsx("div", { children: item.actions })
|
|
534
|
+
]
|
|
535
|
+
},
|
|
536
|
+
item.id
|
|
537
|
+
)) });
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export { CalendarView, DataTable, GridView, KanbanBoard, ListView };
|
|
541
|
+
//# sourceMappingURL=index.js.map
|
|
542
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils.ts","../../../src/components/ui/button.tsx","../../../src/components/ui/input.tsx","../../../src/components/views/data-table.tsx","../../../src/components/views/kanban-board.tsx","../../../src/components/views/grid-view.tsx","../../../src/components/views/calendar-view.tsx","../../../src/components/ui/avatar.tsx","../../../src/components/views/list-view.tsx"],"names":["ButtonPrimitive","jsx","InputPrimitive","useState","jsxs","AvatarPrimitive"],"mappings":"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","file":"index.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","\"use client\";\n\nimport { Button as ButtonPrimitive } from \"@base-ui/react/button\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"../../lib/utils\";\n\nconst buttonVariants = cva(\n \"group/button inline-flex shrink-0 items-center justify-center rounded-lg whitespace-nowrap font-bold transition-all duration-150 select-none outline-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n // Primary: Black bg, white text, offset shadow\n default:\n \"border-brutal border-foreground bg-primary text-primary-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm\",\n // CTA: Mint green bg — the Bannerbear signature\n cta: \"border-brutal border-foreground bg-cta text-cta-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm\",\n // Brand: Dynamic brand color bg\n brand:\n \"border-brutal border-foreground bg-brand text-brand-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm\",\n // Outline: White bg, black border, offset shadow\n outline:\n \"border-brutal border-foreground bg-background text-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm\",\n // Secondary: Light bg, border, smaller shadow\n secondary:\n \"border-brutal border-foreground bg-secondary text-secondary-foreground shadow-brutal-sm hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal active:translate-x-px active:translate-y-px active:shadow-none\",\n // Ghost: No border/shadow, subtle hover\n ghost: \"hover:bg-secondary hover:text-foreground\",\n // Link: Text only\n link: \"text-foreground underline-offset-4 hover:underline\",\n // Destructive\n destructive:\n \"border-brutal border-destructive bg-destructive text-destructive-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm\",\n // Nav: Thin border, no shadow (for nav Sign In buttons)\n nav: \"border border-foreground bg-background text-foreground hover:bg-foreground hover:text-background\",\n },\n size: {\n xs: \"h-7 gap-1 px-2.5 text-xs\",\n sm: \"h-8 gap-1.5 px-3 text-sm\",\n default: \"h-10 gap-2 px-5 text-sm\",\n lg: \"h-12 gap-2 px-7 text-base\",\n xl: \"h-14 gap-2.5 px-9 text-lg\",\n icon: \"size-10\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-12\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n ...props\n}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {\n return (\n <ButtonPrimitive\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n","import * as React from \"react\";\nimport { Input as InputPrimitive } from \"@base-ui/react/input\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n return (\n <InputPrimitive\n type={type}\n data-slot=\"input\"\n className={cn(\n \"h-10 w-full min-w-0 rounded-lg border-brutal border-foreground bg-background px-3 py-2 text-base font-medium shadow-brutal-sm transition-all outline-none placeholder:text-muted-foreground focus:shadow-brutal focus:-translate-x-0.5 focus:-translate-y-0.5 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive md:text-sm\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport { Input };\n","\"use client\";\n\nimport { cn } from \"../../lib/utils\";\nimport { useState } from \"react\";\nimport {\n useReactTable,\n getCoreRowModel,\n getSortedRowModel,\n getFilteredRowModel,\n getPaginationRowModel,\n flexRender,\n type ColumnDef,\n type SortingState,\n} from \"@tanstack/react-table\";\nimport { Button } from \"../ui/button\";\nimport { Input } from \"../ui/input\";\n\ninterface DataTableProps<TData> {\n columns: ColumnDef<TData, any>[];\n data: TData[];\n searchColumn?: string;\n searchPlaceholder?: string;\n pageSize?: number;\n emptyMessage?: string;\n className?: string;\n}\n\nexport function DataTable<TData>({\n columns,\n data,\n searchColumn,\n searchPlaceholder = \"Search...\",\n pageSize = 10,\n emptyMessage = \"No data\",\n className,\n}: DataTableProps<TData>) {\n const [sorting, setSorting] = useState<SortingState>([]);\n const [globalFilter, setGlobalFilter] = useState(\"\");\n\n const table = useReactTable({\n data,\n columns,\n state: { sorting, globalFilter },\n onSortingChange: setSorting,\n onGlobalFilterChange: setGlobalFilter,\n getCoreRowModel: getCoreRowModel(),\n getSortedRowModel: getSortedRowModel(),\n getFilteredRowModel: getFilteredRowModel(),\n getPaginationRowModel: getPaginationRowModel(),\n initialState: { pagination: { pageSize } },\n });\n\n return (\n <div className={cn(\"flex flex-col gap-4\", className)}>\n {searchColumn !== undefined && (\n <Input\n placeholder={searchPlaceholder}\n value={globalFilter}\n onChange={(e) => setGlobalFilter(e.target.value)}\n className=\"max-w-sm\"\n />\n )}\n\n <div className=\"overflow-auto border-brutal border-foreground shadow-brutal\">\n <table className=\"w-full text-sm\">\n <thead>\n {table.getHeaderGroups().map((headerGroup) => (\n <tr\n key={headerGroup.id}\n className=\"border-b-brutal border-foreground bg-secondary\"\n >\n {headerGroup.headers.map((header) => (\n <th\n key={header.id}\n className={cn(\n \"brutal-label px-4 py-3 text-left\",\n header.column.getCanSort() &&\n \"cursor-pointer select-none\"\n )}\n onClick={header.column.getToggleSortingHandler()}\n >\n {flexRender(\n header.column.columnDef.header,\n header.getContext()\n )}\n {{\n asc: \" ↑\",\n desc: \" ↓\",\n }[header.column.getIsSorted() as string] ?? \"\"}\n </th>\n ))}\n </tr>\n ))}\n </thead>\n <tbody>\n {table.getRowModel().rows.length === 0 ? (\n <tr>\n <td\n colSpan={columns.length}\n className=\"px-4 py-8 text-center text-muted-foreground\"\n >\n {emptyMessage}\n </td>\n </tr>\n ) : (\n table.getRowModel().rows.map((row, i) => (\n <tr\n key={row.id}\n className={cn(\n \"border-b border-foreground/10 transition-colors hover:bg-brand-muted\",\n i % 2 === 1 && \"bg-secondary/30\"\n )}\n >\n {row.getVisibleCells().map((cell) => (\n <td key={cell.id} className=\"px-4 py-3\">\n {flexRender(\n cell.column.columnDef.cell,\n cell.getContext()\n )}\n </td>\n ))}\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-muted-foreground\">\n Page {table.getState().pagination.pageIndex + 1} of{\" \"}\n {table.getPageCount()}\n </p>\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => table.previousPage()}\n disabled={!table.getCanPreviousPage()}\n >\n Previous\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => table.nextPage()}\n disabled={!table.getCanNextPage()}\n >\n Next\n </Button>\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { cn } from \"../../lib/utils\";\nimport { useEffect, useRef, useState, useCallback } from \"react\";\nimport {\n draggable,\n dropTargetForElements,\n} from \"@atlaskit/pragmatic-drag-and-drop/element/adapter\";\nimport { combine } from \"@atlaskit/pragmatic-drag-and-drop/combine\";\nimport { Input } from \"../ui/input\";\n\ninterface KanbanColumn<TCard> {\n id: string;\n title: string;\n cards: TCard[];\n}\n\ninterface KanbanBoardProps<TCard extends { id: string }> {\n columns: KanbanColumn<TCard>[];\n renderCard: (card: TCard) => React.ReactNode;\n onCardMove?: (\n cardId: string,\n fromColumn: string,\n toColumn: string,\n index: number\n ) => void;\n onCardAdd?: (columnId: string, title: string) => void;\n className?: string;\n}\n\nexport function KanbanBoard<TCard extends { id: string }>({\n columns,\n renderCard,\n onCardMove,\n onCardAdd,\n className,\n}: KanbanBoardProps<TCard>) {\n return (\n <div className={cn(\"flex gap-4 overflow-x-auto pb-4\", className)}>\n {columns.map((column) => (\n <KanbanColumnComponent\n key={column.id}\n column={column}\n renderCard={renderCard}\n onCardMove={onCardMove}\n onCardAdd={onCardAdd}\n />\n ))}\n </div>\n );\n}\n\nfunction KanbanColumnComponent<TCard extends { id: string }>({\n column,\n renderCard,\n onCardMove,\n onCardAdd,\n}: {\n column: KanbanColumn<TCard>;\n renderCard: (card: TCard) => React.ReactNode;\n onCardMove?: (\n cardId: string,\n fromColumn: string,\n toColumn: string,\n index: number\n ) => void;\n onCardAdd?: (columnId: string, title: string) => void;\n}) {\n const ref = useRef<HTMLDivElement>(null);\n const [isDragOver, setIsDragOver] = useState(false);\n const [addText, setAddText] = useState(\"\");\n\n useEffect(() => {\n if (!ref.current) return;\n return dropTargetForElements({\n element: ref.current,\n getData: () => ({ columnId: column.id }),\n onDragEnter: () => setIsDragOver(true),\n onDragLeave: () => setIsDragOver(false),\n onDrop: ({ source }) => {\n setIsDragOver(false);\n const data = source.data as { cardId: string; columnId: string };\n if (data.columnId !== column.id) {\n onCardMove?.(\n data.cardId as string,\n data.columnId as string,\n column.id,\n column.cards.length\n );\n }\n },\n });\n }, [column.id, column.cards.length, onCardMove]);\n\n return (\n <div\n ref={ref}\n className={cn(\n \"flex min-w-[280px] flex-col border-brutal border-foreground bg-secondary/30 p-3\",\n isDragOver && \"ring-2 ring-brand\"\n )}\n >\n <div className=\"brutal-label mb-3 flex items-center justify-between\">\n <span>{column.title}</span>\n <span className=\"bg-foreground px-1.5 font-mono text-xs text-background\">\n {column.cards.length}\n </span>\n </div>\n <div className=\"flex flex-1 flex-col gap-2\">\n {column.cards.map((card) => (\n <KanbanCard\n key={card.id}\n card={card}\n columnId={column.id}\n renderCard={renderCard}\n />\n ))}\n </div>\n {onCardAdd && (\n <form\n className=\"mt-3\"\n onSubmit={(e) => {\n e.preventDefault();\n if (addText.trim()) {\n onCardAdd(column.id, addText.trim());\n setAddText(\"\");\n }\n }}\n >\n <Input\n value={addText}\n onChange={(e) => setAddText(e.target.value)}\n placeholder=\"+ Add card...\"\n className=\"text-sm\"\n />\n </form>\n )}\n </div>\n );\n}\n\nfunction KanbanCard<TCard extends { id: string }>({\n card,\n columnId,\n renderCard,\n}: {\n card: TCard;\n columnId: string;\n renderCard: (card: TCard) => React.ReactNode;\n}) {\n const ref = useRef<HTMLDivElement>(null);\n const [isDragging, setIsDragging] = useState(false);\n\n useEffect(() => {\n if (!ref.current) return;\n return draggable({\n element: ref.current,\n getInitialData: () => ({ cardId: card.id, columnId }),\n onDragStart: () => setIsDragging(true),\n onDrop: () => setIsDragging(false),\n });\n }, [card.id, columnId]);\n\n return (\n <div\n ref={ref}\n className={cn(\n \"cursor-grab border-brutal border-foreground bg-background p-3 shadow-brutal-sm transition-opacity active:cursor-grabbing\",\n isDragging && \"opacity-50\"\n )}\n >\n {renderCard(card)}\n </div>\n );\n}\n","import { cn } from \"../../lib/utils\";\n\ninterface GridViewProps {\n children: React.ReactNode;\n columns?: number;\n gap?: number;\n className?: string;\n}\n\nexport function GridView({\n children,\n columns = 3,\n gap = 6,\n className,\n}: GridViewProps) {\n return (\n <div\n className={cn(\"grid\", className)}\n style={{\n gridTemplateColumns: `repeat(auto-fill, minmax(${Math.floor(100 / columns) - 2}%, 1fr))`,\n gap: `${gap * 4}px`,\n }}\n >\n {children}\n </div>\n );\n}\n","\"use client\";\n\nimport { cn } from \"../../lib/utils\";\nimport { useState } from \"react\";\nimport { Button } from \"../ui/button\";\n\ninterface CalendarEvent {\n id: string;\n title: string;\n date: Date | string;\n color?: string;\n}\n\ninterface CalendarViewProps {\n events?: CalendarEvent[];\n onEventClick?: (event: CalendarEvent) => void;\n onDateClick?: (date: Date) => void;\n className?: string;\n}\n\nconst DAYS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n\nfunction getDaysInMonth(year: number, month: number) {\n return new Date(year, month + 1, 0).getDate();\n}\n\nfunction getFirstDayOfMonth(year: number, month: number) {\n return new Date(year, month, 1).getDay();\n}\n\nexport function CalendarView({\n events = [],\n onEventClick,\n onDateClick,\n className,\n}: CalendarViewProps) {\n const [currentDate, setCurrentDate] = useState(new Date());\n const year = currentDate.getFullYear();\n const month = currentDate.getMonth();\n const daysInMonth = getDaysInMonth(year, month);\n const firstDay = getFirstDayOfMonth(year, month);\n const monthName = currentDate.toLocaleDateString(\"en-US\", {\n month: \"long\",\n year: \"numeric\",\n });\n\n const cells: (number | null)[] = [];\n for (let i = 0; i < firstDay; i++) cells.push(null);\n for (let d = 1; d <= daysInMonth; d++) cells.push(d);\n\n const getEventsForDay = (day: number) =>\n events.filter((e) => {\n const d = typeof e.date === \"string\" ? new Date(e.date) : e.date;\n return (\n d.getFullYear() === year &&\n d.getMonth() === month &&\n d.getDate() === day\n );\n });\n\n const today = new Date();\n const isToday = (day: number) =>\n day === today.getDate() &&\n month === today.getMonth() &&\n year === today.getFullYear();\n\n return (\n <div className={cn(\"flex flex-col gap-4\", className)}>\n <div className=\"flex items-center justify-between\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setCurrentDate(new Date(year, month - 1))}\n >\n ←\n </Button>\n <h3 className=\"brutal-h4\">{monthName}</h3>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setCurrentDate(new Date(year, month + 1))}\n >\n →\n </Button>\n </div>\n <div className=\"grid grid-cols-7 border-brutal border-foreground\">\n {DAYS.map((day) => (\n <div\n key={day}\n className=\"brutal-label border-b-brutal border-foreground bg-secondary px-2 py-2 text-center text-xs\"\n >\n {day}\n </div>\n ))}\n {cells.map((day, i) => {\n const dayEvents = day ? getEventsForDay(day) : [];\n return (\n <div\n key={i}\n className={cn(\n \"min-h-[80px] border-b border-r border-foreground/10 p-1\",\n day && onDateClick && \"cursor-pointer hover:bg-secondary/50\",\n isToday(day ?? 0) && \"bg-brand-muted\"\n )}\n onClick={() => day && onDateClick?.(new Date(year, month, day))}\n >\n {day && (\n <>\n <span\n className={cn(\n \"text-xs font-bold\",\n isToday(day) && \"text-brand\"\n )}\n >\n {day}\n </span>\n <div className=\"mt-1 flex flex-col gap-0.5\">\n {dayEvents.slice(0, 3).map((event) => (\n <div\n key={event.id}\n onClick={(e) => {\n e.stopPropagation();\n onEventClick?.(event);\n }}\n className=\"cursor-pointer truncate border border-foreground/20 bg-brand-muted px-1 text-[10px] font-medium hover:bg-brand\"\n style={\n event.color\n ? { borderLeftColor: event.color, borderLeftWidth: 2 }\n : undefined\n }\n >\n {event.title}\n </div>\n ))}\n {dayEvents.length > 3 && (\n <span className=\"text-[10px] text-muted-foreground\">\n +{dayEvents.length - 3}\n </span>\n )}\n </div>\n </>\n )}\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n","\"use client\"\n\nimport * as React from \"react\"\nimport { Avatar as AvatarPrimitive } from \"@base-ui/react/avatar\"\n\nimport { cn } from \"../../lib/utils\"\n\nfunction Avatar({\n className,\n size = \"default\",\n ...props\n}: AvatarPrimitive.Root.Props & {\n size?: \"default\" | \"sm\" | \"lg\"\n}) {\n return (\n <AvatarPrimitive.Root\n data-slot=\"avatar\"\n data-size={size}\n className={cn(\n \"group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {\n return (\n <AvatarPrimitive.Image\n data-slot=\"avatar-image\"\n className={cn(\n \"aspect-square size-full rounded-full object-cover\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction AvatarFallback({\n className,\n ...props\n}: AvatarPrimitive.Fallback.Props) {\n return (\n <AvatarPrimitive.Fallback\n data-slot=\"avatar-fallback\"\n className={cn(\n \"flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction AvatarBadge({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"avatar-badge\"\n className={cn(\n \"absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none\",\n \"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden\",\n \"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2\",\n \"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction AvatarGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"avatar-group\"\n className={cn(\n \"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction AvatarGroupCount({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"avatar-group-count\"\n className={cn(\n \"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport {\n Avatar,\n AvatarImage,\n AvatarFallback,\n AvatarGroup,\n AvatarGroupCount,\n AvatarBadge,\n}\n","import { cn } from \"../../lib/utils\";\nimport { Avatar, AvatarImage, AvatarFallback } from \"../ui/avatar\";\n\ninterface ListItem {\n id: string;\n title: string;\n subtitle?: string;\n avatar?: string;\n metadata?: React.ReactNode;\n actions?: React.ReactNode;\n}\n\ninterface ListViewProps {\n items: ListItem[];\n onItemClick?: (item: ListItem) => void;\n emptyMessage?: string;\n className?: string;\n}\n\nexport function ListView({\n items,\n onItemClick,\n emptyMessage = \"No items\",\n className,\n}: ListViewProps) {\n if (items.length === 0) {\n return (\n <p className=\"py-8 text-center text-muted-foreground\">{emptyMessage}</p>\n );\n }\n\n return (\n <div className={cn(\"border-brutal border-foreground shadow-brutal\", className)}>\n {items.map((item, i) => (\n <div\n key={item.id}\n onClick={() => onItemClick?.(item)}\n className={cn(\n \"flex items-center gap-4 px-4 py-3 transition-colors\",\n onItemClick && \"cursor-pointer\",\n \"hover:bg-secondary/50\",\n i < items.length - 1 && \"border-b border-foreground/10\"\n )}\n >\n {item.avatar && (\n <Avatar className=\"size-8\">\n <AvatarImage src={item.avatar} alt={item.title} />\n <AvatarFallback>{item.title[0]}</AvatarFallback>\n </Avatar>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-bold\">{item.title}</p>\n {item.subtitle && (\n <p className=\"truncate text-xs text-muted-foreground\">\n {item.subtitle}\n </p>\n )}\n </div>\n {item.metadata && (\n <div className=\"text-sm text-muted-foreground\">{item.metadata}</div>\n )}\n {item.actions && <div>{item.actions}</div>}\n </div>\n ))}\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface KanbanColumn<TCard> {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
cards: TCard[];
|
|
7
|
+
}
|
|
8
|
+
interface KanbanBoardProps<TCard extends {
|
|
9
|
+
id: string;
|
|
10
|
+
}> {
|
|
11
|
+
columns: KanbanColumn<TCard>[];
|
|
12
|
+
renderCard: (card: TCard) => React.ReactNode;
|
|
13
|
+
onCardMove?: (cardId: string, fromColumn: string, toColumn: string, index: number) => void;
|
|
14
|
+
onCardAdd?: (columnId: string, title: string) => void;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
declare function KanbanBoard<TCard extends {
|
|
18
|
+
id: string;
|
|
19
|
+
}>({ columns, renderCard, onCardMove, onCardAdd, className, }: KanbanBoardProps<TCard>): react_jsx_runtime.JSX.Element;
|
|
20
|
+
|
|
21
|
+
export { KanbanBoard };
|