@object-ui/plugin-kanban 3.3.2 → 3.4.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # @object-ui/plugin-kanban
2
2
 
3
+ ## 3.4.0
4
+
5
+ ### Patch Changes
6
+
7
+ - a2d7023: End-user feature batch — forms, designer history, import/export, and PWA offline sync.
8
+
9
+ **Forms (`@object-ui/fields`, `@object-ui/providers`)**
10
+ - `FileField`: native `<input capture="environment">` camera capture for mobile devices, plus a uploading-progress indicator driven by `UploadProvider`.
11
+ - `ImageField`: per-image inline crop/rotate via the lazy-loaded `ImageCropperDialog` (canvas-based, zero new deps).
12
+ - New `UploadProvider` in `@object-ui/providers` with pluggable adapters for S3 and Azure Blob (plus the default object-URL adapter for local previews). XHR-based with progress, abort, and retry.
13
+ - `LookupField`: `lookup.dependsOn: string | string[]` to chain dependent lookups (e.g. State depends on Country); the trigger is gated until parent values are present and the OData `$filter` is built automatically.
14
+
15
+ **Container-aware widget widths (`@object-ui/components`)**
16
+ - New `useResizeObserver(ref)` hook exposing `{ width, height }` of any element. SSR-safe; reads the initial size via `getBoundingClientRect`.
17
+ - `plugin-gantt` and `plugin-kanban` now react to their container size instead of `window.innerWidth`, so they behave correctly inside split panels and dashboards.
18
+
19
+ **Designer history (`@object-ui/plugin-designer`)**
20
+ - `useUndoRedo` (and therefore `useDesignerHistory`) gains `persistKey` + `storage` options to round-trip the undo/redo stack through `sessionStorage`, plus a `clearPersisted()` cleanup helper. Drafts now survive accidental tab refreshes.
21
+ - New `<HistoryPanel>` component renders the timeline visually with one-click jump-to-checkpoint via the new `jumpTo(index)` API.
22
+
23
+ **Import wizard (`@object-ui/plugin-grid`)**
24
+ - Saved column-mapping templates: name, save, re-apply, and delete via a new template bar in the mapping step. Persisted under `objectui:import-templates:${objectName}` (override via `templateStorageKey` / `templateStorage`).
25
+ - Inline validation correction: cells with errors in the preview step are now editable; corrections feed straight into the import without requiring a re-upload, with green-bar status indicators for fixed rows.
26
+
27
+ **PWA offline sync (`@object-ui/mobile`)**
28
+ - New `MemoryOfflineQueue` / `IndexedDbOfflineQueue` (`createOfflineQueue()` picks the best backend) backed by IndexedDB.
29
+ - `createOfflineDataSource(inner, { queue })` wraps any DataSource so mutations issued while offline (or that fail with a network-style error) are queued and replayed in order on reconnect. Includes `replay()`, `drop()`, `clear()`, `pending()`, an `onChange` notifier, and an opt-in `resolveConflict` hook for stale-write conflicts.
30
+ - New `useOfflineSync(source)` hook exposes `{ isOnline, pending, isReplaying, replay, drop, clear }` and auto-replays on the browser's `online` event.
31
+ - `getServiceWorkerSource(opts)` emits a customisable Service Worker that pre-caches the app shell, applies network-first to API requests, and broadcasts `REPLAY_QUEUE` to clients on Background Sync. `requestBackgroundSync(tag)` registers a one-shot sync from the page.
32
+
33
+ - b2be122: fix(mobile): round 2 — kanban readability, calendar default view, timeline dot clipping
34
+
35
+ **Kanban**
36
+ - Remove `font-mono` from card titles, descriptions, column headers, and empty-state labels — CRM cards no longer render in a monospace font.
37
+ - Constrain column body height (`max-h-full min-h-0` + `h-full` on the layout root) so `ScrollArea` activates and cards don't bleed past the viewport bottom.
38
+ - Opportunistically derive `description` (e.g. `$60K · Acme Corp · @owner`) and up to two `badges` (priority/severity/industry/rating) in `ObjectKanban` when the schema/source omits them, giving mobile cards more context at a glance.
39
+
40
+ **Calendar**
41
+ - `ObjectCalendar` previously hardcoded `view={schema.defaultView ?? 'month'}`, making the view-selector dropdown a no-op. Wire the `view` state through to the `<Calendar>` prop so user selection is respected.
42
+ - On mobile (viewport < 768 px) coerce `day` defaults to `month` via a synchronous lazy initialiser and a resize/orientation effect — avoids the useless 24-hour empty-hour grid for date-only events.
43
+
44
+ **Timeline**
45
+ - Add `ml-3` to the `<Timeline>` `<ol>` so the `absolute -left-3` marker dots are no longer clipped at the scroll-container edge.
46
+
47
+ - Updated dependencies [a2d7023]
48
+ - Updated dependencies [f1ca238]
49
+ - Updated dependencies [de881ef]
50
+ - @object-ui/components@3.4.0
51
+ - @object-ui/types@3.4.0
52
+ - @object-ui/core@3.4.0
53
+ - @object-ui/react@3.4.0
54
+
3
55
  ## 3.3.2
4
56
 
5
57
  ### Patch Changes
@@ -592,10 +592,10 @@ function Y({ card: e, conditionalFormatting: t }) {
592
592
  /* @__PURE__ */ (0, K.jsxs)(x, {
593
593
  className: "p-4",
594
594
  children: [/* @__PURE__ */ (0, K.jsx)(S, {
595
- className: "text-sm font-medium font-mono tracking-tight text-foreground group-hover:text-primary transition-colors",
595
+ className: "text-sm font-medium tracking-tight text-foreground group-hover:text-primary transition-colors",
596
596
  children: e.title
597
597
  }), e.description && /* @__PURE__ */ (0, K.jsx)(b, {
598
- className: "text-xs text-muted-foreground font-mono",
598
+ className: "text-xs text-muted-foreground",
599
599
  children: e.description
600
600
  })]
601
601
  }),
@@ -693,13 +693,13 @@ function Q({ column: e, cards: t, onToggle: n, enableVirtual: i, quickAdd: a, on
693
693
  onClick: () => n(!e.collapsed),
694
694
  children: e.collapsed ? /* @__PURE__ */ (0, K.jsx)(T, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, K.jsx)(r, { className: "h-4 w-4" })
695
695
  }), !e.collapsed && /* @__PURE__ */ (0, K.jsxs)(K.Fragment, { children: [/* @__PURE__ */ (0, K.jsx)("h3", {
696
- className: "font-mono text-sm font-semibold tracking-wider text-primary/90 uppercase truncate",
696
+ className: " text-sm font-semibold tracking-wider text-primary/90 uppercase truncate",
697
697
  children: e.title
698
698
  }), /* @__PURE__ */ (0, K.jsxs)("div", {
699
699
  className: "flex items-center gap-2",
700
700
  children: [
701
701
  /* @__PURE__ */ (0, K.jsxs)("span", {
702
- className: q("font-mono text-xs", m ? "text-destructive" : v ? "text-yellow-500" : "text-muted-foreground"),
702
+ className: q(" text-xs", m ? "text-destructive" : v ? "text-yellow-500" : "text-muted-foreground"),
703
703
  children: [l.length, e.limit && ` / ${e.limit}`]
704
704
  }),
705
705
  m && /* @__PURE__ */ (0, K.jsx)(g, {
@@ -713,7 +713,7 @@ function Q({ column: e, cards: t, onToggle: n, enableVirtual: i, quickAdd: a, on
713
713
  }), e.collapsed && /* @__PURE__ */ (0, K.jsxs)("div", {
714
714
  className: "flex flex-col items-center gap-1",
715
715
  children: [/* @__PURE__ */ (0, K.jsx)("span", {
716
- className: "font-mono text-xs font-bold text-primary/90 [writing-mode:vertical-rl] rotate-180",
716
+ className: " text-xs font-bold text-primary/90 [writing-mode:vertical-rl] rotate-180",
717
717
  children: e.title
718
718
  }), /* @__PURE__ */ (0, K.jsx)(g, {
719
719
  variant: "secondary",
@@ -2,10 +2,10 @@ import { r as e, t } from "./plus-CHsXVJSY.js";
2
2
  import { a as n, c as r, d as i, f as a, i as o, l as s, n as c, o as l, r as u, s as d, t as f, u as p } from "./sortable.esm-LJG1TjKd.js";
3
3
  import * as m from "react";
4
4
  import { useDnd as h, useHasDndProvider as g } from "@object-ui/react";
5
- import { Badge as _, Button as v, Card as y, CardContent as b, CardDescription as x, CardHeader as S, CardTitle as C, Input as w, ScrollArea as T } from "@object-ui/components";
5
+ import { Badge as _, Button as v, Card as y, CardContent as b, CardDescription as x, CardHeader as S, CardTitle as C, Input as w, ScrollArea as T, useResizeObserver as E } from "@object-ui/components";
6
6
  //#region src/KanbanImpl.tsx
7
- var E = e(), D = (...e) => e.filter(Boolean).join(" "), O = "Uncategorized";
8
- function k(e, t) {
7
+ var D = e(), O = (...e) => e.filter(Boolean).join(" "), k = "Uncategorized";
8
+ function A(e, t) {
9
9
  if (!t || t.length === 0) return {};
10
10
  for (let n of t) {
11
11
  let t = e[n.field];
@@ -32,13 +32,13 @@ function k(e, t) {
32
32
  }
33
33
  return {};
34
34
  }
35
- function A({ card: e, onCardClick: t, conditionalFormatting: n }) {
35
+ function j({ card: e, onCardClick: t, conditionalFormatting: n }) {
36
36
  let { attributes: r, listeners: i, setNodeRef: o, transform: s, transition: c, isDragging: l } = u({ id: e.id }), d = {
37
37
  transform: a.Transform.toString(s),
38
38
  transition: c,
39
39
  opacity: l ? .5 : void 0
40
- }, f = k(e, n);
41
- return /* @__PURE__ */ (0, E.jsx)("div", {
40
+ }, f = A(e, n);
41
+ return /* @__PURE__ */ (0, D.jsx)("div", {
42
42
  ref: o,
43
43
  style: d,
44
44
  ...r,
@@ -46,34 +46,34 @@ function A({ card: e, onCardClick: t, conditionalFormatting: n }) {
46
46
  role: "listitem",
47
47
  "aria-label": e.title,
48
48
  onClick: () => t?.(e),
49
- children: /* @__PURE__ */ (0, E.jsxs)(y, {
49
+ children: /* @__PURE__ */ (0, D.jsxs)(y, {
50
50
  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",
51
51
  style: f,
52
52
  children: [
53
- e.coverImage && /* @__PURE__ */ (0, E.jsx)("div", {
53
+ e.coverImage && /* @__PURE__ */ (0, D.jsx)("div", {
54
54
  className: "w-full h-32 overflow-hidden rounded-t-lg",
55
- children: /* @__PURE__ */ (0, E.jsx)("img", {
55
+ children: /* @__PURE__ */ (0, D.jsx)("img", {
56
56
  src: e.coverImage,
57
57
  alt: "",
58
58
  className: "w-full h-full object-cover",
59
59
  loading: "lazy"
60
60
  })
61
61
  }),
62
- /* @__PURE__ */ (0, E.jsxs)(S, {
62
+ /* @__PURE__ */ (0, D.jsxs)(S, {
63
63
  className: "p-2 sm:p-4",
64
- children: [/* @__PURE__ */ (0, E.jsx)(C, {
65
- className: "text-xs sm:text-sm font-medium font-mono tracking-tight text-foreground group-hover:text-primary transition-colors",
64
+ children: [/* @__PURE__ */ (0, D.jsx)(C, {
65
+ className: "text-xs sm:text-sm font-medium tracking-tight text-foreground group-hover:text-primary transition-colors",
66
66
  children: e.title
67
- }), e.description && /* @__PURE__ */ (0, E.jsx)(x, {
68
- className: "text-xs text-muted-foreground font-mono line-clamp-2 sm:line-clamp-none",
67
+ }), e.description && /* @__PURE__ */ (0, D.jsx)(x, {
68
+ className: "text-xs text-muted-foreground line-clamp-2 sm:line-clamp-none",
69
69
  children: e.description
70
70
  })]
71
71
  }),
72
- e.badges && e.badges.length > 0 && /* @__PURE__ */ (0, E.jsx)(b, {
72
+ e.badges && e.badges.length > 0 && /* @__PURE__ */ (0, D.jsx)(b, {
73
73
  className: "p-2 sm:p-4 pt-0",
74
- children: /* @__PURE__ */ (0, E.jsx)("div", {
74
+ children: /* @__PURE__ */ (0, D.jsx)("div", {
75
75
  className: "flex flex-wrap gap-1",
76
- children: e.badges.map((e, t) => /* @__PURE__ */ (0, E.jsx)(_, {
76
+ children: e.badges.map((e, t) => /* @__PURE__ */ (0, D.jsx)(_, {
77
77
  variant: e.variant || "default",
78
78
  className: "text-xs",
79
79
  children: e.label
@@ -84,14 +84,14 @@ function A({ card: e, onCardClick: t, conditionalFormatting: n }) {
84
84
  })
85
85
  });
86
86
  }
87
- function j({ columnId: e, onAdd: n }) {
87
+ function M({ columnId: e, onAdd: n }) {
88
88
  let [r, i] = m.useState(!1), [a, o] = m.useState(""), s = m.useRef(null), c = () => {
89
89
  let t = a.trim();
90
90
  t && (n(e, t), o("")), i(!1);
91
91
  };
92
- return r ? /* @__PURE__ */ (0, E.jsx)("div", {
92
+ return r ? /* @__PURE__ */ (0, D.jsx)("div", {
93
93
  className: "mt-2 space-y-2",
94
- children: /* @__PURE__ */ (0, E.jsx)(w, {
94
+ children: /* @__PURE__ */ (0, D.jsx)(w, {
95
95
  ref: s,
96
96
  value: a,
97
97
  onChange: (e) => o(e.target.value),
@@ -103,80 +103,81 @@ function j({ columnId: e, onAdd: n }) {
103
103
  className: "text-sm",
104
104
  autoFocus: !0
105
105
  })
106
- }) : /* @__PURE__ */ (0, E.jsxs)(v, {
106
+ }) : /* @__PURE__ */ (0, D.jsxs)(v, {
107
107
  variant: "ghost",
108
108
  size: "sm",
109
109
  className: "w-full mt-2 text-muted-foreground hover:text-foreground",
110
110
  onClick: () => {
111
111
  i(!0), setTimeout(() => s.current?.focus(), 0);
112
112
  },
113
- children: [/* @__PURE__ */ (0, E.jsx)(t, { className: "h-4 w-4 mr-1" }), "Add Card"]
113
+ children: [/* @__PURE__ */ (0, D.jsx)(t, { className: "h-4 w-4 mr-1" }), "Add Card"]
114
114
  });
115
115
  }
116
- function M({ column: e, cards: t, onCardClick: n, quickAdd: r, onQuickAdd: i, conditionalFormatting: a }) {
117
- let s = t || [], { setNodeRef: c } = u({
116
+ function N({ column: e, cards: t, onCardClick: n, quickAdd: r, onQuickAdd: i, conditionalFormatting: a, columnStyle: s }) {
117
+ let c = t || [], { setNodeRef: l } = u({
118
118
  id: e.id,
119
119
  data: { type: "column" }
120
- }), l = e.limit && s.length >= e.limit;
121
- return /* @__PURE__ */ (0, E.jsxs)("div", {
122
- ref: c,
120
+ }), d = e.limit && c.length >= e.limit, p = s && s.width != null ? "flex-shrink-0" : "w-[85vw] sm:w-80 flex-shrink-0";
121
+ return /* @__PURE__ */ (0, D.jsxs)("div", {
122
+ ref: l,
123
123
  role: "group",
124
124
  "aria-label": e.title,
125
- className: D("flex flex-col w-[85vw] sm:w-80 flex-shrink-0 rounded-lg border border-border bg-card/20 backdrop-blur-sm shadow-xl snap-start", e.className),
126
- children: [/* @__PURE__ */ (0, E.jsx)("div", {
125
+ style: s,
126
+ className: O("flex flex-col rounded-lg border border-border bg-card/20 backdrop-blur-sm shadow-xl snap-start max-h-full min-h-0", p, e.className),
127
+ children: [/* @__PURE__ */ (0, D.jsx)("div", {
127
128
  className: "p-3 sm:p-4 border-b border-border/50 bg-muted/30 rounded-t-lg",
128
- children: /* @__PURE__ */ (0, E.jsxs)("div", {
129
+ children: /* @__PURE__ */ (0, D.jsxs)("div", {
129
130
  className: "flex items-center justify-between",
130
- children: [/* @__PURE__ */ (0, E.jsx)("h3", {
131
+ children: [/* @__PURE__ */ (0, D.jsx)("h3", {
131
132
  id: `kanban-col-${e.id}`,
132
- className: "font-mono text-xs sm:text-sm font-semibold tracking-wider text-primary/90 uppercase truncate",
133
+ className: " text-xs sm:text-sm font-semibold tracking-wider text-primary/90 uppercase truncate",
133
134
  children: e.title
134
- }), /* @__PURE__ */ (0, E.jsxs)("div", {
135
+ }), /* @__PURE__ */ (0, D.jsxs)("div", {
135
136
  className: "flex items-center gap-2",
136
- children: [/* @__PURE__ */ (0, E.jsxs)(_, {
137
+ children: [/* @__PURE__ */ (0, D.jsxs)(_, {
137
138
  variant: "secondary",
138
- className: "text-xs font-mono tabular-nums",
139
- children: [s.length, e.limit && ` / ${e.limit}`]
140
- }), l && /* @__PURE__ */ (0, E.jsx)(_, {
139
+ className: "text-xs tabular-nums",
140
+ children: [c.length, e.limit && ` / ${e.limit}`]
141
+ }), d && /* @__PURE__ */ (0, D.jsx)(_, {
141
142
  variant: "destructive",
142
143
  className: "text-xs",
143
144
  children: "Full"
144
145
  })]
145
146
  })]
146
147
  })
147
- }), /* @__PURE__ */ (0, E.jsxs)(T, {
148
+ }), /* @__PURE__ */ (0, D.jsxs)(T, {
148
149
  className: "flex-1 p-4",
149
- children: [/* @__PURE__ */ (0, E.jsx)(f, {
150
- items: s.map((e) => e.id),
150
+ children: [/* @__PURE__ */ (0, D.jsx)(f, {
151
+ items: c.map((e) => e.id),
151
152
  strategy: o,
152
- children: /* @__PURE__ */ (0, E.jsxs)("div", {
153
+ children: /* @__PURE__ */ (0, D.jsxs)("div", {
153
154
  className: "space-y-2",
154
155
  role: "list",
155
156
  "aria-label": `${e.title} cards`,
156
- children: [s.length === 0 && /* @__PURE__ */ (0, E.jsx)("div", {
157
+ children: [c.length === 0 && /* @__PURE__ */ (0, D.jsx)("div", {
157
158
  className: "flex flex-col items-center justify-center py-8 text-muted-foreground/50",
158
- children: /* @__PURE__ */ (0, E.jsx)("span", {
159
- className: "text-xs font-mono",
159
+ children: /* @__PURE__ */ (0, D.jsx)("span", {
160
+ className: "text-xs",
160
161
  children: "No cards"
161
162
  })
162
- }), s.map((e) => /* @__PURE__ */ (0, E.jsx)(A, {
163
+ }), c.map((e) => /* @__PURE__ */ (0, D.jsx)(j, {
163
164
  card: e,
164
165
  onCardClick: n,
165
166
  conditionalFormatting: a
166
167
  }, e.id))]
167
168
  })
168
- }), r && i && /* @__PURE__ */ (0, E.jsx)(j, {
169
+ }), r && i && /* @__PURE__ */ (0, D.jsx)(M, {
169
170
  columnId: e.id,
170
171
  onAdd: i
171
172
  })]
172
173
  })]
173
174
  });
174
175
  }
175
- function N({ children: e }) {
176
- return /* @__PURE__ */ (0, E.jsx)(E.Fragment, { children: e(h()) });
176
+ function P({ children: e }) {
177
+ return /* @__PURE__ */ (0, D.jsx)(D.Fragment, { children: e(h()) });
177
178
  }
178
- function P({ columns: e, onCardMove: t, onCardClick: n, className: r, quickAdd: i, onQuickAdd: a, coverImageField: o, conditionalFormatting: s, swimlaneField: c }) {
179
- return g() ? /* @__PURE__ */ (0, E.jsx)(N, { children: (l) => /* @__PURE__ */ (0, E.jsx)(F, {
179
+ function F({ columns: e, onCardMove: t, onCardClick: n, className: r, quickAdd: i, onQuickAdd: a, coverImageField: o, conditionalFormatting: s, swimlaneField: c }) {
180
+ return g() ? /* @__PURE__ */ (0, D.jsx)(P, { children: (l) => /* @__PURE__ */ (0, D.jsx)(I, {
180
181
  columns: e,
181
182
  onCardMove: t,
182
183
  onCardClick: n,
@@ -187,7 +188,7 @@ function P({ columns: e, onCardMove: t, onCardClick: n, className: r, quickAdd:
187
188
  coverImageField: o,
188
189
  conditionalFormatting: s,
189
190
  swimlaneField: c
190
- }) }) : /* @__PURE__ */ (0, E.jsx)(F, {
191
+ }) }) : /* @__PURE__ */ (0, D.jsx)(I, {
191
192
  columns: e,
192
193
  onCardMove: t,
193
194
  onCardClick: n,
@@ -200,46 +201,46 @@ function P({ columns: e, onCardMove: t, onCardClick: n, className: r, quickAdd:
200
201
  swimlaneField: c
201
202
  });
202
203
  }
203
- function F({ columns: e, onCardMove: t, onCardClick: a, className: u, dnd: h, quickAdd: g, onQuickAdd: _, coverImageField: v, conditionalFormatting: y, swimlaneField: b }) {
204
- let [x, S] = m.useState(null), C = b ? `objectui:kanban-collapsed:${b}` : null, [w, T] = m.useState(() => {
205
- if (!C) return /* @__PURE__ */ new Set();
204
+ function I({ columns: e, onCardMove: t, onCardClick: a, className: u, dnd: h, quickAdd: g, onQuickAdd: _, coverImageField: v, conditionalFormatting: y, swimlaneField: b }) {
205
+ let [x, S] = m.useState(null), C = m.useRef(null), { width: w } = E(C), T = m.useMemo(() => w ? w < 480 ? { width: Math.max(w - 32, 220) } : w < 720 ? { width: 280 } : { width: 320 } : {}, [w]), A = b ? `objectui:kanban-collapsed:${b}` : null, [M, P] = m.useState(() => {
206
+ if (!A) return /* @__PURE__ */ new Set();
206
207
  try {
207
- let e = localStorage.getItem(C);
208
+ let e = localStorage.getItem(A);
208
209
  if (e) {
209
210
  let t = JSON.parse(e);
210
211
  if (Array.isArray(t)) return new Set(t.filter((e) => typeof e == "string"));
211
212
  }
212
213
  } catch {}
213
214
  return /* @__PURE__ */ new Set();
214
- }), k = m.useMemo(() => (e || []).map((e) => ({
215
+ }), F = m.useMemo(() => (e || []).map((e) => ({
215
216
  ...e,
216
217
  cards: e.cards || []
217
- })), [e]), [j, N] = m.useState(k);
218
+ })), [e]), [I, L] = m.useState(F);
218
219
  m.useEffect(() => {
219
- N(k);
220
- }, [k]);
221
- let P = m.useMemo(() => {
220
+ L(F);
221
+ }, [F]);
222
+ let R = m.useMemo(() => {
222
223
  if (!b) return null;
223
- let e = j.flatMap((e) => e.cards), t = /* @__PURE__ */ new Set();
224
+ let e = I.flatMap((e) => e.cards), t = /* @__PURE__ */ new Set();
224
225
  return e.forEach((e) => {
225
226
  let n = e[b];
226
- t.add(n == null ? O : String(n));
227
+ t.add(n == null ? k : String(n));
227
228
  }), Array.from(t).sort();
228
- }, [j, b]), F = m.useCallback((e) => {
229
- T((t) => {
229
+ }, [I, b]), z = m.useCallback((e) => {
230
+ P((t) => {
230
231
  let n = new Set(t);
231
- if (n.has(e) ? n.delete(e) : n.add(e), C) try {
232
- localStorage.setItem(C, JSON.stringify([...n]));
232
+ if (n.has(e) ? n.delete(e) : n.add(e), A) try {
233
+ localStorage.setItem(A, JSON.stringify([...n]));
233
234
  } catch {}
234
235
  return n;
235
236
  });
236
- }, [C]), I = i(p(d, { activationConstraint: { distance: 5 } }), p(r, { activationConstraint: {
237
+ }, [A]), B = i(p(d, { activationConstraint: { distance: 5 } }), p(r, { activationConstraint: {
237
238
  delay: 200,
238
239
  tolerance: 5
239
- } })), L = (e) => {
240
- let { active: t } = e, n = z(t.id);
240
+ } })), V = (e) => {
241
+ let { active: t } = e, n = U(t.id);
241
242
  if (S(n), h && n) {
242
- let e = B(n.id);
243
+ let e = W(n.id);
243
244
  e && h.startDrag({
244
245
  id: n.id,
245
246
  type: "kanban-card",
@@ -247,7 +248,7 @@ function F({ columns: e, onCardMove: t, onCardClick: a, className: u, dnd: h, qu
247
248
  sourceId: e.id
248
249
  });
249
250
  }
250
- }, R = (e) => {
251
+ }, H = (e) => {
251
252
  let { active: n, over: r } = e;
252
253
  if (S(null), !r) {
253
254
  h && h.endDrag();
@@ -258,20 +259,20 @@ function F({ columns: e, onCardMove: t, onCardClick: a, className: u, dnd: h, qu
258
259
  h && h.endDrag();
259
260
  return;
260
261
  }
261
- let o = B(i), s = B(a) || V(a);
262
+ let o = W(i), s = W(a) || G(a);
262
263
  if (!o || !s) {
263
264
  h && h.endDrag();
264
265
  return;
265
266
  }
266
267
  if (o.id === s.id) {
267
268
  let e = [...o.cards], t = c(e, e.findIndex((e) => e.id === i), e.findIndex((e) => e.id === a));
268
- N((e) => e.map((e) => e.id === o.id ? {
269
+ L((e) => e.map((e) => e.id === o.id ? {
269
270
  ...e,
270
271
  cards: t
271
272
  } : e));
272
273
  } else {
273
274
  let e = [...o.cards], n = [...s.cards], r = e.findIndex((e) => e.id === i), c = a === s.id ? n.length : n.findIndex((e) => e.id === a), [l] = e.splice(r, 1);
274
- n.splice(c, 0, l), N((t) => t.map((t) => t.id === o.id ? {
275
+ n.splice(c, 0, l), L((t) => t.map((t) => t.id === o.id ? {
275
276
  ...t,
276
277
  cards: e
277
278
  } : t.id === s.id ? {
@@ -280,36 +281,37 @@ function F({ columns: e, onCardMove: t, onCardClick: a, className: u, dnd: h, qu
280
281
  } : t)), t && t(i, o.id, s.id, c);
281
282
  }
282
283
  h && h.endDrag(s.id);
283
- }, z = m.useCallback((e) => {
284
- for (let t of j) {
284
+ }, U = m.useCallback((e) => {
285
+ for (let t of I) {
285
286
  let n = t.cards.find((t) => t.id === e);
286
287
  if (n) return n;
287
288
  }
288
289
  return null;
289
- }, [j]), B = m.useCallback((e) => j.find((t) => t.cards.some((t) => t.id === e)) || null, [j]), V = m.useCallback((e) => j.find((t) => t.id === e) || null, [j]);
290
- return /* @__PURE__ */ (0, E.jsxs)(n, {
291
- sensors: I,
290
+ }, [I]), W = m.useCallback((e) => I.find((t) => t.cards.some((t) => t.id === e)) || null, [I]), G = m.useCallback((e) => I.find((t) => t.id === e) || null, [I]);
291
+ return /* @__PURE__ */ (0, D.jsxs)(n, {
292
+ sensors: B,
292
293
  collisionDetection: s,
293
- onDragStart: L,
294
- onDragEnd: R,
295
- children: [
296
- /* @__PURE__ */ (0, E.jsxs)("div", {
294
+ onDragStart: V,
295
+ onDragEnd: H,
296
+ children: [/* @__PURE__ */ (0, D.jsxs)("div", {
297
+ ref: C,
298
+ className: "flex flex-col min-w-0 min-h-0 h-full",
299
+ children: [/* @__PURE__ */ (0, D.jsxs)("div", {
297
300
  className: "flex sm:hidden items-center justify-between px-3 pb-2 text-xs text-muted-foreground",
298
- children: [/* @__PURE__ */ (0, E.jsxs)("span", { children: [j.length, " columns"] }), /* @__PURE__ */ (0, E.jsx)("span", { children: "← Swipe to navigate →" })]
299
- }),
300
- P ? /* @__PURE__ */ (0, E.jsxs)("div", {
301
- className: D("flex flex-col gap-1 p-2 sm:p-4 min-w-0 overflow-hidden", u),
301
+ children: [/* @__PURE__ */ (0, D.jsxs)("span", { children: [I.length, " columns"] }), /* @__PURE__ */ (0, D.jsx)("span", { children: "← Swipe to navigate →" })]
302
+ }), R ? /* @__PURE__ */ (0, D.jsxs)("div", {
303
+ className: O("flex flex-col gap-1 p-2 sm:p-4 min-w-0 overflow-hidden", u),
302
304
  role: "region",
303
305
  "aria-label": "Kanban board with swimlanes",
304
- children: [/* @__PURE__ */ (0, E.jsx)("div", {
306
+ children: [/* @__PURE__ */ (0, D.jsx)("div", {
305
307
  className: "flex gap-3 sm:gap-4 pl-36 sm:pl-44 overflow-x-auto",
306
- children: j.map((e) => /* @__PURE__ */ (0, E.jsxs)("div", {
308
+ children: I.map((e) => /* @__PURE__ */ (0, D.jsxs)("div", {
307
309
  className: "w-[85vw] sm:w-80 flex-shrink-0 text-center",
308
- children: [/* @__PURE__ */ (0, E.jsx)("span", {
309
- className: "font-mono text-xs sm:text-sm font-semibold tracking-wider text-primary/90 uppercase",
310
+ children: [/* @__PURE__ */ (0, D.jsx)("span", {
311
+ className: " text-xs sm:text-sm font-semibold tracking-wider text-primary/90 uppercase",
310
312
  children: e.title
311
- }), /* @__PURE__ */ (0, E.jsxs)("span", {
312
- className: "ml-2 font-mono text-xs text-muted-foreground",
313
+ }), /* @__PURE__ */ (0, D.jsxs)("span", {
314
+ className: "ml-2 text-xs text-muted-foreground",
313
315
  children: [
314
316
  "(",
315
317
  e.cards.length,
@@ -317,25 +319,25 @@ function F({ columns: e, onCardMove: t, onCardClick: a, className: u, dnd: h, qu
317
319
  ]
318
320
  })]
319
321
  }, e.id))
320
- }), P.map((e) => {
321
- let t = w.has(e), n = j.reduce((t, n) => t + n.cards.filter((t) => (t[b] == null ? O : String(t[b])) === e).length, 0);
322
- return /* @__PURE__ */ (0, E.jsxs)("div", {
322
+ }), R.map((e) => {
323
+ let t = M.has(e), n = I.reduce((t, n) => t + n.cards.filter((t) => (t[b] == null ? k : String(t[b])) === e).length, 0);
324
+ return /* @__PURE__ */ (0, D.jsxs)("div", {
323
325
  className: "border rounded-lg bg-muted/10",
324
- children: [/* @__PURE__ */ (0, E.jsxs)("button", {
326
+ children: [/* @__PURE__ */ (0, D.jsxs)("button", {
325
327
  className: "w-full flex items-center gap-2 px-3 py-2 text-left hover:bg-muted/30 transition-colors",
326
- onClick: () => F(e),
328
+ onClick: () => z(e),
327
329
  "aria-expanded": !t,
328
330
  children: [
329
- /* @__PURE__ */ (0, E.jsx)("span", {
330
- className: D("transition-transform text-xs", t ? "" : "rotate-90"),
331
+ /* @__PURE__ */ (0, D.jsx)("span", {
332
+ className: O("transition-transform text-xs", t ? "" : "rotate-90"),
331
333
  children: "▶"
332
334
  }),
333
- /* @__PURE__ */ (0, E.jsx)("span", {
334
- className: "font-mono text-xs font-semibold text-muted-foreground uppercase tracking-wider",
335
+ /* @__PURE__ */ (0, D.jsx)("span", {
336
+ className: " text-xs font-semibold text-muted-foreground uppercase tracking-wider",
335
337
  children: e
336
338
  }),
337
- /* @__PURE__ */ (0, E.jsxs)("span", {
338
- className: "font-mono text-xs text-muted-foreground",
339
+ /* @__PURE__ */ (0, D.jsxs)("span", {
340
+ className: " text-xs text-muted-foreground",
339
341
  children: [
340
342
  "(",
341
343
  n,
@@ -343,20 +345,20 @@ function F({ columns: e, onCardMove: t, onCardClick: a, className: u, dnd: h, qu
343
345
  ]
344
346
  })
345
347
  ]
346
- }), !t && /* @__PURE__ */ (0, E.jsx)("div", {
348
+ }), !t && /* @__PURE__ */ (0, D.jsx)("div", {
347
349
  className: "flex gap-3 sm:gap-4 overflow-x-auto px-2 pb-3 pl-36 sm:pl-44",
348
- children: j.map((t) => {
349
- let n = t.cards.filter((t) => (t[b] == null ? O : String(t[b])) === e);
350
- return /* @__PURE__ */ (0, E.jsx)("div", {
350
+ children: I.map((t) => {
351
+ let n = t.cards.filter((t) => (t[b] == null ? k : String(t[b])) === e);
352
+ return /* @__PURE__ */ (0, D.jsx)("div", {
351
353
  className: "w-[85vw] sm:w-80 flex-shrink-0 min-h-[60px] rounded-md bg-card/20 p-2",
352
- children: /* @__PURE__ */ (0, E.jsx)(f, {
354
+ children: /* @__PURE__ */ (0, D.jsx)(f, {
353
355
  items: n.map((e) => e.id),
354
356
  strategy: o,
355
- children: /* @__PURE__ */ (0, E.jsx)("div", {
357
+ children: /* @__PURE__ */ (0, D.jsx)("div", {
356
358
  className: "space-y-2",
357
359
  role: "list",
358
360
  "aria-label": `${t.title} - ${e} cards`,
359
- children: n.map((e) => /* @__PURE__ */ (0, E.jsx)(A, {
361
+ children: n.map((e) => /* @__PURE__ */ (0, D.jsx)(j, {
360
362
  card: e,
361
363
  onCardClick: a,
362
364
  conditionalFormatting: y
@@ -368,29 +370,29 @@ function F({ columns: e, onCardMove: t, onCardClick: a, className: u, dnd: h, qu
368
370
  })]
369
371
  }, e);
370
372
  })]
371
- }) : /* @__PURE__ */ (0, E.jsx)("div", {
372
- className: D("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", u),
373
+ }) : /* @__PURE__ */ (0, D.jsx)("div", {
374
+ className: O("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", u),
373
375
  role: "region",
374
376
  "aria-label": "Kanban board",
375
- children: j.map((e) => /* @__PURE__ */ (0, E.jsx)(M, {
377
+ children: I.map((e) => /* @__PURE__ */ (0, D.jsx)(N, {
376
378
  column: e,
377
379
  cards: e.cards,
378
380
  onCardClick: a,
379
381
  quickAdd: g,
380
382
  onQuickAdd: _,
381
- conditionalFormatting: y
383
+ conditionalFormatting: y,
384
+ columnStyle: T
382
385
  }, e.id))
383
- }),
384
- /* @__PURE__ */ (0, E.jsx)(l, { children: /* @__PURE__ */ (0, E.jsx)("div", {
385
- "aria-live": "assertive",
386
- "aria-label": x ? `Dragging ${x.title}` : void 0,
387
- children: x ? /* @__PURE__ */ (0, E.jsx)(A, {
388
- card: x,
389
- conditionalFormatting: y
390
- }) : null
391
- }) })
392
- ]
386
+ })]
387
+ }), /* @__PURE__ */ (0, D.jsx)(l, { children: /* @__PURE__ */ (0, D.jsx)("div", {
388
+ "aria-live": "assertive",
389
+ "aria-label": x ? `Dragging ${x.title}` : void 0,
390
+ children: x ? /* @__PURE__ */ (0, D.jsx)(j, {
391
+ card: x,
392
+ conditionalFormatting: y
393
+ }) : null
394
+ }) })]
393
395
  });
394
396
  }
395
397
  //#endregion
396
- export { P as default };
398
+ export { F as default };