@object-ui/plugin-kanban 4.0.1 → 4.0.4

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.
@@ -0,0 +1,401 @@
1
+ import * as e from "react";
2
+ import { useDnd as t, useHasDndProvider as n } from "@object-ui/react";
3
+ import { Badge as r, Button as i, Card as a, CardContent as o, CardDescription as s, CardHeader as c, CardTitle as l, Input as u, ScrollArea as d, useResizeObserver as f } from "@object-ui/components";
4
+ import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
5
+ import { Plus as g } from "lucide-react";
6
+ import { DndContext as _, DragOverlay as v, PointerSensor as y, TouchSensor as b, closestCorners as x, useSensor as S, useSensors as C } from "@dnd-kit/core";
7
+ import { SortableContext as w, arrayMove as T, useSortable as E, verticalListSortingStrategy as D } from "@dnd-kit/sortable";
8
+ import { CSS as O } from "@dnd-kit/utilities";
9
+ //#region src/KanbanImpl.tsx
10
+ var k = (...e) => e.filter(Boolean).join(" "), A = "Uncategorized";
11
+ function j(e, t) {
12
+ if (!t || t.length === 0) return {};
13
+ for (let n of t) {
14
+ let t = e[n.field];
15
+ if (t == null) continue;
16
+ let r = !1, i = String(t);
17
+ switch (n.operator) {
18
+ case "equals":
19
+ r = i === String(n.value);
20
+ break;
21
+ case "not_equals":
22
+ r = i !== String(n.value);
23
+ break;
24
+ case "contains":
25
+ r = i.toLowerCase().includes(String(n.value).toLowerCase());
26
+ break;
27
+ case "in":
28
+ r = Array.isArray(n.value) && n.value.includes(i);
29
+ break;
30
+ }
31
+ if (r) return {
32
+ ...n.backgroundColor ? { backgroundColor: n.backgroundColor } : {},
33
+ ...n.borderColor ? { borderColor: n.borderColor } : {}
34
+ };
35
+ }
36
+ return {};
37
+ }
38
+ function M({ card: e, onCardClick: t, conditionalFormatting: n }) {
39
+ let { attributes: i, listeners: u, setNodeRef: d, transform: f, transition: p, isDragging: g } = E({ id: e.id }), _ = {
40
+ transform: O.Transform.toString(f),
41
+ transition: p,
42
+ opacity: g ? .5 : void 0
43
+ }, v = j(e, n);
44
+ return /* @__PURE__ */ m("div", {
45
+ ref: d,
46
+ style: _,
47
+ ...i,
48
+ ...u,
49
+ role: "listitem",
50
+ "aria-label": e.title,
51
+ onClick: () => t?.(e),
52
+ children: /* @__PURE__ */ h(a, {
53
+ className: "mb-2 cursor-grab active:cursor-grabbing border-border border-l-4 border-l-primary/40 bg-card/60 hover:border-primary/40 hover:shadow-lg hover:shadow-primary/10 transition-all duration-300 group touch-manipulation",
54
+ style: v,
55
+ children: [
56
+ e.coverImage && /* @__PURE__ */ m("div", {
57
+ className: "w-full h-32 overflow-hidden rounded-t-lg",
58
+ children: /* @__PURE__ */ m("img", {
59
+ src: e.coverImage,
60
+ alt: "",
61
+ className: "w-full h-full object-cover",
62
+ loading: "lazy"
63
+ })
64
+ }),
65
+ /* @__PURE__ */ h(c, {
66
+ className: "p-2 sm:p-4",
67
+ children: [/* @__PURE__ */ m(l, {
68
+ className: "text-xs sm:text-sm font-medium tracking-tight text-foreground group-hover:text-primary transition-colors",
69
+ children: e.title
70
+ }), e.description && /* @__PURE__ */ m(s, {
71
+ className: "text-xs text-muted-foreground line-clamp-2 sm:line-clamp-none",
72
+ children: e.description
73
+ })]
74
+ }),
75
+ e.badges && e.badges.length > 0 && /* @__PURE__ */ m(o, {
76
+ className: "p-2 sm:p-4 pt-0",
77
+ children: /* @__PURE__ */ m("div", {
78
+ className: "flex flex-wrap gap-1",
79
+ children: e.badges.map((e, t) => /* @__PURE__ */ m(r, {
80
+ variant: e.variant || "default",
81
+ className: "text-xs",
82
+ children: e.label
83
+ }, t))
84
+ })
85
+ })
86
+ ]
87
+ })
88
+ });
89
+ }
90
+ function N({ columnId: t, onAdd: n }) {
91
+ let [r, a] = e.useState(!1), [o, s] = e.useState(""), c = e.useRef(null), l = () => {
92
+ let e = o.trim();
93
+ e && (n(t, e), s("")), a(!1);
94
+ };
95
+ return r ? /* @__PURE__ */ m("div", {
96
+ className: "mt-2 space-y-2",
97
+ children: /* @__PURE__ */ m(u, {
98
+ ref: c,
99
+ value: o,
100
+ onChange: (e) => s(e.target.value),
101
+ onKeyDown: (e) => {
102
+ e.key === "Enter" ? (e.preventDefault(), l()) : e.key === "Escape" && (s(""), a(!1));
103
+ },
104
+ onBlur: l,
105
+ placeholder: "Enter card title...",
106
+ className: "text-sm",
107
+ autoFocus: !0
108
+ })
109
+ }) : /* @__PURE__ */ h(i, {
110
+ variant: "ghost",
111
+ size: "sm",
112
+ className: "w-full mt-2 text-muted-foreground hover:text-foreground",
113
+ onClick: () => {
114
+ a(!0), setTimeout(() => c.current?.focus(), 0);
115
+ },
116
+ children: [/* @__PURE__ */ m(g, { className: "h-4 w-4 mr-1" }), "Add Card"]
117
+ });
118
+ }
119
+ function P({ column: e, cards: t, onCardClick: n, quickAdd: i, onQuickAdd: a, conditionalFormatting: o, columnStyle: s }) {
120
+ let c = t || [], { setNodeRef: l } = E({
121
+ id: e.id,
122
+ data: { type: "column" }
123
+ }), u = e.limit && c.length >= e.limit, f = s && s.width != null ? "flex-shrink-0" : "w-[85vw] sm:w-80 flex-shrink-0";
124
+ return /* @__PURE__ */ h("div", {
125
+ ref: l,
126
+ role: "group",
127
+ "aria-label": e.title,
128
+ style: s,
129
+ className: k("flex flex-col rounded-lg border border-border bg-card/20 backdrop-blur-sm shadow-xl snap-start max-h-full min-h-0", f, e.className),
130
+ children: [/* @__PURE__ */ m("div", {
131
+ className: "p-3 sm:p-4 border-b border-border/50 bg-muted/30 rounded-t-lg",
132
+ children: /* @__PURE__ */ h("div", {
133
+ className: "flex items-center justify-between",
134
+ children: [/* @__PURE__ */ m("h3", {
135
+ id: `kanban-col-${e.id}`,
136
+ className: " text-xs sm:text-sm font-semibold tracking-wider text-primary/90 uppercase truncate",
137
+ children: e.title
138
+ }), /* @__PURE__ */ h("div", {
139
+ className: "flex items-center gap-2",
140
+ children: [/* @__PURE__ */ h(r, {
141
+ variant: "secondary",
142
+ className: "text-xs tabular-nums",
143
+ children: [c.length, e.limit && ` / ${e.limit}`]
144
+ }), u && /* @__PURE__ */ m(r, {
145
+ variant: "destructive",
146
+ className: "text-xs",
147
+ children: "Full"
148
+ })]
149
+ })]
150
+ })
151
+ }), /* @__PURE__ */ h(d, {
152
+ className: "flex-1 p-4",
153
+ children: [/* @__PURE__ */ m(w, {
154
+ items: c.map((e) => e.id),
155
+ strategy: D,
156
+ children: /* @__PURE__ */ h("div", {
157
+ className: "space-y-2",
158
+ role: "list",
159
+ "aria-label": `${e.title} cards`,
160
+ children: [c.length === 0 && /* @__PURE__ */ m("div", {
161
+ className: "flex flex-col items-center justify-center py-8 text-muted-foreground/50",
162
+ children: /* @__PURE__ */ m("span", {
163
+ className: "text-xs",
164
+ children: "No cards"
165
+ })
166
+ }), c.map((e) => /* @__PURE__ */ m(M, {
167
+ card: e,
168
+ onCardClick: n,
169
+ conditionalFormatting: o
170
+ }, e.id))]
171
+ })
172
+ }), i && a && /* @__PURE__ */ m(N, {
173
+ columnId: e.id,
174
+ onAdd: a
175
+ })]
176
+ })]
177
+ });
178
+ }
179
+ function F({ children: e }) {
180
+ return /* @__PURE__ */ m(p, { children: e(t()) });
181
+ }
182
+ function I({ columns: e, onCardMove: t, onCardClick: r, className: i, quickAdd: a, onQuickAdd: o, coverImageField: s, conditionalFormatting: c, swimlaneField: l }) {
183
+ return n() ? /* @__PURE__ */ m(F, { children: (n) => /* @__PURE__ */ m(L, {
184
+ columns: e,
185
+ onCardMove: t,
186
+ onCardClick: r,
187
+ className: i,
188
+ dnd: n,
189
+ quickAdd: a,
190
+ onQuickAdd: o,
191
+ coverImageField: s,
192
+ conditionalFormatting: c,
193
+ swimlaneField: l
194
+ }) }) : /* @__PURE__ */ m(L, {
195
+ columns: e,
196
+ onCardMove: t,
197
+ onCardClick: r,
198
+ className: i,
199
+ dnd: null,
200
+ quickAdd: a,
201
+ onQuickAdd: o,
202
+ coverImageField: s,
203
+ conditionalFormatting: c,
204
+ swimlaneField: l
205
+ });
206
+ }
207
+ function L({ columns: t, onCardMove: n, onCardClick: r, className: i, dnd: a, quickAdd: o, onQuickAdd: s, coverImageField: c, conditionalFormatting: l, swimlaneField: u }) {
208
+ let [d, p] = e.useState(null), g = e.useRef(null), { width: E } = f(g), O = e.useMemo(() => E ? E < 480 ? { width: Math.max(E - 32, 220) } : E < 720 ? { width: 280 } : { width: 320 } : {}, [E]), j = u ? `objectui:kanban-collapsed:${u}` : null, [N, F] = e.useState(() => {
209
+ if (!j) return /* @__PURE__ */ new Set();
210
+ try {
211
+ let e = localStorage.getItem(j);
212
+ if (e) {
213
+ let t = JSON.parse(e);
214
+ if (Array.isArray(t)) return new Set(t.filter((e) => typeof e == "string"));
215
+ }
216
+ } catch {}
217
+ return /* @__PURE__ */ new Set();
218
+ }), I = e.useMemo(() => (t || []).map((e) => ({
219
+ ...e,
220
+ cards: e.cards || []
221
+ })), [t]), [L, R] = e.useState(I);
222
+ e.useEffect(() => {
223
+ R(I);
224
+ }, [I]);
225
+ let z = e.useMemo(() => {
226
+ if (!u) return null;
227
+ let e = L.flatMap((e) => e.cards), t = /* @__PURE__ */ new Set();
228
+ return e.forEach((e) => {
229
+ let n = e[u];
230
+ t.add(n == null ? A : String(n));
231
+ }), Array.from(t).sort();
232
+ }, [L, u]), B = e.useCallback((e) => {
233
+ F((t) => {
234
+ let n = new Set(t);
235
+ if (n.has(e) ? n.delete(e) : n.add(e), j) try {
236
+ localStorage.setItem(j, JSON.stringify([...n]));
237
+ } catch {}
238
+ return n;
239
+ });
240
+ }, [j]), V = C(S(y, { activationConstraint: { distance: 5 } }), S(b, { activationConstraint: {
241
+ delay: 200,
242
+ tolerance: 5
243
+ } })), H = (e) => {
244
+ let { active: t } = e, n = W(t.id);
245
+ if (p(n), a && n) {
246
+ let e = G(n.id);
247
+ e && a.startDrag({
248
+ id: n.id,
249
+ type: "kanban-card",
250
+ data: n,
251
+ sourceId: e.id
252
+ });
253
+ }
254
+ }, U = (e) => {
255
+ let { active: t, over: r } = e;
256
+ if (p(null), !r) {
257
+ a && a.endDrag();
258
+ return;
259
+ }
260
+ let i = t.id, o = r.id;
261
+ if (i === o) {
262
+ a && a.endDrag();
263
+ return;
264
+ }
265
+ let s = G(i), c = G(o) || K(o);
266
+ if (!s || !c) {
267
+ a && a.endDrag();
268
+ return;
269
+ }
270
+ if (s.id === c.id) {
271
+ let e = [...s.cards], t = T(e, e.findIndex((e) => e.id === i), e.findIndex((e) => e.id === o));
272
+ R((e) => e.map((e) => e.id === s.id ? {
273
+ ...e,
274
+ cards: t
275
+ } : e));
276
+ } else {
277
+ let e = [...s.cards], t = [...c.cards], r = e.findIndex((e) => e.id === i), a = o === c.id ? t.length : t.findIndex((e) => e.id === o), [l] = e.splice(r, 1);
278
+ t.splice(a, 0, l), R((n) => n.map((n) => n.id === s.id ? {
279
+ ...n,
280
+ cards: e
281
+ } : n.id === c.id ? {
282
+ ...n,
283
+ cards: t
284
+ } : n)), n && n(i, s.id, c.id, a);
285
+ }
286
+ a && a.endDrag(c.id);
287
+ }, W = e.useCallback((e) => {
288
+ for (let t of L) {
289
+ let n = t.cards.find((t) => t.id === e);
290
+ if (n) return n;
291
+ }
292
+ return null;
293
+ }, [L]), G = e.useCallback((e) => L.find((t) => t.cards.some((t) => t.id === e)) || null, [L]), K = e.useCallback((e) => L.find((t) => t.id === e) || null, [L]);
294
+ return /* @__PURE__ */ h(_, {
295
+ sensors: V,
296
+ collisionDetection: x,
297
+ onDragStart: H,
298
+ onDragEnd: U,
299
+ children: [/* @__PURE__ */ h("div", {
300
+ ref: g,
301
+ className: "flex flex-col min-w-0 min-h-0 h-full",
302
+ children: [/* @__PURE__ */ h("div", {
303
+ className: "flex sm:hidden items-center justify-between px-3 pb-2 text-xs text-muted-foreground",
304
+ children: [/* @__PURE__ */ h("span", { children: [L.length, " columns"] }), /* @__PURE__ */ m("span", { children: "← Swipe to navigate →" })]
305
+ }), z ? /* @__PURE__ */ h("div", {
306
+ className: k("flex flex-col gap-1 p-2 sm:p-4 min-w-0 overflow-hidden", i),
307
+ role: "region",
308
+ "aria-label": "Kanban board with swimlanes",
309
+ children: [/* @__PURE__ */ m("div", {
310
+ className: "flex gap-3 sm:gap-4 pl-36 sm:pl-44 overflow-x-auto",
311
+ children: L.map((e) => /* @__PURE__ */ h("div", {
312
+ className: "w-[85vw] sm:w-80 flex-shrink-0 text-center",
313
+ children: [/* @__PURE__ */ m("span", {
314
+ className: " text-xs sm:text-sm font-semibold tracking-wider text-primary/90 uppercase",
315
+ children: e.title
316
+ }), /* @__PURE__ */ h("span", {
317
+ className: "ml-2 text-xs text-muted-foreground",
318
+ children: [
319
+ "(",
320
+ e.cards.length,
321
+ ")"
322
+ ]
323
+ })]
324
+ }, e.id))
325
+ }), z.map((e) => {
326
+ let t = N.has(e), n = L.reduce((t, n) => t + n.cards.filter((t) => (t[u] == null ? A : String(t[u])) === e).length, 0);
327
+ return /* @__PURE__ */ h("div", {
328
+ className: "border rounded-lg bg-muted/10",
329
+ children: [/* @__PURE__ */ h("button", {
330
+ className: "w-full flex items-center gap-2 px-3 py-2 text-left hover:bg-muted/30 transition-colors",
331
+ onClick: () => B(e),
332
+ "aria-expanded": !t,
333
+ children: [
334
+ /* @__PURE__ */ m("span", {
335
+ className: k("transition-transform text-xs", t ? "" : "rotate-90"),
336
+ children: "▶"
337
+ }),
338
+ /* @__PURE__ */ m("span", {
339
+ className: " text-xs font-semibold text-muted-foreground uppercase tracking-wider",
340
+ children: e
341
+ }),
342
+ /* @__PURE__ */ h("span", {
343
+ className: " text-xs text-muted-foreground",
344
+ children: [
345
+ "(",
346
+ n,
347
+ ")"
348
+ ]
349
+ })
350
+ ]
351
+ }), !t && /* @__PURE__ */ m("div", {
352
+ className: "flex gap-3 sm:gap-4 overflow-x-auto px-2 pb-3 pl-36 sm:pl-44",
353
+ children: L.map((t) => {
354
+ let n = t.cards.filter((t) => (t[u] == null ? A : String(t[u])) === e);
355
+ return /* @__PURE__ */ m("div", {
356
+ className: "w-[85vw] sm:w-80 flex-shrink-0 min-h-[60px] rounded-md bg-card/20 p-2",
357
+ children: /* @__PURE__ */ m(w, {
358
+ items: n.map((e) => e.id),
359
+ strategy: D,
360
+ children: /* @__PURE__ */ m("div", {
361
+ className: "space-y-2",
362
+ role: "list",
363
+ "aria-label": `${t.title} - ${e} cards`,
364
+ children: n.map((e) => /* @__PURE__ */ m(M, {
365
+ card: e,
366
+ onCardClick: r,
367
+ conditionalFormatting: l
368
+ }, e.id))
369
+ })
370
+ })
371
+ }, t.id);
372
+ })
373
+ })]
374
+ }, e);
375
+ })]
376
+ }) : /* @__PURE__ */ m("div", {
377
+ className: k("flex gap-3 sm:gap-4 overflow-x-auto snap-x snap-mandatory p-2 sm:p-4 bg-muted/10 rounded-lg [-webkit-overflow-scrolling:touch] min-w-0 min-h-0 h-full", i),
378
+ role: "region",
379
+ "aria-label": "Kanban board",
380
+ children: L.map((e) => /* @__PURE__ */ m(P, {
381
+ column: e,
382
+ cards: e.cards,
383
+ onCardClick: r,
384
+ quickAdd: o,
385
+ onQuickAdd: s,
386
+ conditionalFormatting: l,
387
+ columnStyle: O
388
+ }, e.id))
389
+ })]
390
+ }), /* @__PURE__ */ m(v, { children: /* @__PURE__ */ m("div", {
391
+ "aria-live": "assertive",
392
+ "aria-label": d ? `Dragging ${d.title}` : void 0,
393
+ children: d ? /* @__PURE__ */ m(M, {
394
+ card: d,
395
+ conditionalFormatting: l
396
+ }) : null
397
+ }) })]
398
+ });
399
+ }
400
+ //#endregion
401
+ export { I as default };
package/dist/index.js CHANGED
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/plugin-kanban",
3
- "version": "4.0.1",
3
+ "version": "4.0.4",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Kanban board plugin for Object UI, powered by dnd-kit",
@@ -29,10 +29,10 @@
29
29
  "@dnd-kit/utilities": "^3.2.2",
30
30
  "@tanstack/react-virtual": "^3.13.24",
31
31
  "lucide-react": "^1.14.0",
32
- "@object-ui/components": "4.0.1",
33
- "@object-ui/core": "4.0.1",
34
- "@object-ui/react": "4.0.1",
35
- "@object-ui/types": "4.0.1"
32
+ "@object-ui/components": "4.0.4",
33
+ "@object-ui/core": "4.0.4",
34
+ "@object-ui/react": "4.0.4",
35
+ "@object-ui/types": "4.0.4"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "react": "^18.0.0 || ^19.0.0",
@@ -45,7 +45,7 @@
45
45
  "typescript": "^6.0.3",
46
46
  "vite": "^8.0.10",
47
47
  "vite-plugin-dts": "^5.0.0",
48
- "@object-ui/data-objectstack": "4.0.1"
48
+ "@object-ui/data-objectstack": "4.0.4"
49
49
  },
50
50
  "keywords": [
51
51
  "objectui",