react-os-shell 0.9.0 → 0.10.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/dist/index.d.ts CHANGED
@@ -1025,15 +1025,19 @@ declare function EntityList<T>(props: EntityListProps<T>): react_jsx_runtime.JSX
1025
1025
  * library) and styled with the same Tailwind utilities + `grid-scroll` class the
1026
1026
  * shell already ships, so consumers get it for free.
1027
1027
  *
1028
- * Group items into columns with `columnOf`, render each card with `renderCard`,
1029
- * and handle moves with `onMove(id, toColumn)`. Drops only change a card's
1030
- * column; within-column order is presentational (optionally via `sortInColumn`).
1028
+ * Group items into columns with `columnOf`, render each card with `renderCard`.
1029
+ * Cards can be dragged **between** columns (to change which column they belong
1030
+ * to) and **within** a column (to reorder / prioritise). On drop, `onMove(id,
1031
+ * toColumn, toIndex)` fires — `toIndex` is the target position within `toColumn`
1032
+ * measured against that column's cards *excluding* the dragged one, so the
1033
+ * consumer can persist an order (e.g. midpoint between neighbours) that every
1034
+ * user then sees. Sort each column by that order via `sortInColumn`.
1031
1035
  *
1032
- * Drag affordance: as a card is dragged over a *different* column, the cards at
1033
- * and below the hovered position slide down (CSS transform transition) to open a
1034
- * gap the size of the dragged card, and the column highlights. The dragged card
1035
- * is dimmed. Insertion index tracks `dragenter` (once per card crossed) rather
1036
- * than `dragover` (every few ms) so the gap stays stable instead of oscillating.
1036
+ * Affordance: a blue **drop-line** shows exactly where the card will land
1037
+ * (between two cards, in either the source or a different column), and the
1038
+ * target column highlights. The dragged card dims. The insertion point tracks
1039
+ * `dragenter` (once per card crossed) so the line stays stable rather than
1040
+ * flickering.
1037
1041
  */
1038
1042
  interface KanbanColumn {
1039
1043
  /** Stable column key — what `columnOf` returns and `onMove` receives. */
@@ -1049,12 +1053,17 @@ interface KanbanProps<T> {
1049
1053
  columns: KanbanColumn[];
1050
1054
  columnOf: (item: T) => string;
1051
1055
  getId: (item: T) => string;
1052
- /** Called when a card is dropped on a column (its own `value`). */
1053
- onMove: (id: string, toColumn: string) => void;
1056
+ /**
1057
+ * Fired on drop. `toColumn` is the destination column's `value`; `toIndex` is
1058
+ * the target position within that column measured against its cards
1059
+ * **excluding** the dragged card (0 = top). A same-column drop that wouldn't
1060
+ * change the order is not reported.
1061
+ */
1062
+ onMove: (id: string, toColumn: string, toIndex: number) => void;
1054
1063
  /** Inner card content — the card chrome (border, padding, hover) is provided. */
1055
1064
  renderCard: (item: T) => ReactNode;
1056
1065
  onCardClick?: (item: T) => void;
1057
- /** Optional comparator for ordering within a column. */
1066
+ /** Comparator for ordering within a column — sort by the persisted order field. */
1058
1067
  sortInColumn?: (a: T, b: T) => number;
1059
1068
  isLoading?: boolean;
1060
1069
  loadingText?: string;
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import { useAuth, useShellAuth } from './chunk-ADJ3CERD.js';
19
19
  export { ShellAuthProvider, setShellAuthBridge, useShellAuth } from './chunk-ADJ3CERD.js';
20
20
  import { glassStyle, startMenuCategories, navSections, isSection, GLASS_INPUT_BG, navIcons, sectionIcons } from './chunk-ZF6AYO4G.js';
21
21
  export { GLASS_DIVIDER, GLASS_INPUT_BG, glassStyle, setShellNavIcons } from './chunk-ZF6AYO4G.js';
22
- import { createContext, lazy, useState, useRef, useEffect, useCallback, useMemo, useLayoutEffect, useContext, Suspense, isValidElement, cloneElement, useSyncExternalStore } from 'react';
22
+ import { createContext, lazy, useState, useRef, useEffect, useCallback, useMemo, useLayoutEffect, useContext, Suspense, isValidElement, cloneElement, Fragment as Fragment$1, useSyncExternalStore } from 'react';
23
23
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
24
24
  import { Dialog, DialogBackdrop, DialogPanel, DialogTitle } from '@headlessui/react';
25
25
  import { createPortal } from 'react-dom';
@@ -1400,7 +1400,7 @@ function WidgetManager({ open, onClose }) {
1400
1400
  }
1401
1401
 
1402
1402
  // src/version.ts
1403
- var VERSION = "0.9.0" ;
1403
+ var VERSION = "0.10.0" ;
1404
1404
  var APP_VERSION = VERSION;
1405
1405
 
1406
1406
  // src/changelog.ts
@@ -6273,8 +6273,6 @@ function Kanban({
6273
6273
  columnEmptyText = "Drop here"
6274
6274
  }) {
6275
6275
  const [dragId, setDragId] = useState(null);
6276
- const [fromCol, setFromCol] = useState(null);
6277
- const [gap, setGap] = useState(0);
6278
6276
  const [over, setOver] = useState(null);
6279
6277
  const grouped = useMemo(() => {
6280
6278
  const map = {};
@@ -6285,27 +6283,36 @@ function Kanban({
6285
6283
  }, [items, columns, columnOf, sortInColumn]);
6286
6284
  const reset = () => {
6287
6285
  setDragId(null);
6288
- setFromCol(null);
6289
6286
  setOver(null);
6290
6287
  };
6291
- const isActive = (col) => over !== null && over.col === col && fromCol !== col;
6288
+ const commitMove = (col) => {
6289
+ if (dragId && over && over.col === col) {
6290
+ const colItems = grouped[col] ?? [];
6291
+ const dp = colItems.findIndex((it) => getId(it) === dragId);
6292
+ const sameCol = dp !== -1;
6293
+ const noop = sameCol && (over.index === dp || over.index === dp + 1);
6294
+ if (!noop) {
6295
+ const toIndex = sameCol && over.index > dp ? over.index - 1 : over.index;
6296
+ onMove(dragId, col, toIndex);
6297
+ }
6298
+ }
6299
+ reset();
6300
+ };
6292
6301
  if (isLoading) return /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500 p-4", children: loadingText });
6293
6302
  if (items.length === 0) {
6294
6303
  return /* @__PURE__ */ jsx(Fragment, { children: emptyState ?? /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500 p-4", children: "No items." }) });
6295
6304
  }
6296
6305
  return /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-x-auto grid-scroll", children: /* @__PURE__ */ jsx("div", { className: "flex gap-3 h-full min-w-max pb-2", children: columns.map((col) => {
6297
6306
  const colItems = grouped[col.value] ?? [];
6298
- const active = isActive(col.value);
6307
+ const isOver = over !== null && over.col === col.value;
6308
+ const dp = dragId !== null ? colItems.findIndex((it) => getId(it) === dragId) : -1;
6309
+ const lineAt = isOver && !(dp !== -1 && (over.index === dp || over.index === dp + 1)) ? over.index : -1;
6299
6310
  return /* @__PURE__ */ jsxs(
6300
6311
  "div",
6301
6312
  {
6302
- className: `flex flex-col w-72 shrink-0 rounded-xl bg-gray-50 border transition-colors ${active ? "border-blue-400 ring-2 ring-blue-300/60" : "border-gray-200"}`,
6313
+ className: `flex flex-col w-72 shrink-0 rounded-xl bg-gray-50 border transition-colors ${isOver ? "border-blue-400 ring-2 ring-blue-300/60" : "border-gray-200"}`,
6303
6314
  onDragOver: (e) => e.preventDefault(),
6304
- onDragEnter: () => setOver((prev) => prev && prev.col === col.value ? prev : { col: col.value, index: colItems.length }),
6305
- onDrop: () => {
6306
- if (dragId) onMove(dragId, col.value);
6307
- reset();
6308
- },
6315
+ onDrop: () => commitMove(col.value),
6309
6316
  children: [
6310
6317
  /* @__PURE__ */ jsxs(
6311
6318
  "div",
@@ -6324,48 +6331,46 @@ function Kanban({
6324
6331
  "div",
6325
6332
  {
6326
6333
  className: "flex-1 overflow-y-auto p-2 space-y-2 min-h-[120px]",
6327
- style: { paddingBottom: active ? gap + 8 : 8, transition: "padding-bottom 160ms ease" },
6334
+ onDragEnter: () => setOver(
6335
+ (prev) => prev && prev.col === col.value && prev.index === colItems.length ? prev : { col: col.value, index: colItems.length }
6336
+ ),
6328
6337
  children: [
6329
6338
  colItems.map((item, index) => {
6330
6339
  const id = getId(item);
6331
- const shift = active && over !== null && index >= over.index ? gap : 0;
6332
- const style = dragId === null ? {} : {
6333
- transform: `translateY(${shift}px)`,
6334
- transition: "transform 160ms cubic-bezier(0.2, 0, 0, 1), opacity 120ms ease",
6335
- opacity: id === dragId ? 0.4 : 1
6336
- };
6337
- return /* @__PURE__ */ jsx(
6338
- "div",
6339
- {
6340
- draggable: true,
6341
- onDragStart: (e) => {
6342
- setDragId(id);
6343
- setFromCol(col.value);
6344
- setGap(Math.round(e.currentTarget.getBoundingClientRect().height) + 8);
6345
- try {
6346
- e.dataTransfer.effectAllowed = "move";
6347
- } catch {
6348
- }
6349
- },
6350
- onDragEnter: (e) => {
6351
- e.stopPropagation();
6352
- setOver(
6353
- (prev) => prev && prev.col === col.value && prev.index === index ? prev : { col: col.value, index }
6354
- );
6355
- },
6356
- onDragEnd: reset,
6357
- onClick: onCardClick ? () => onCardClick(item) : void 0,
6358
- style,
6359
- className: `rounded-lg bg-white border border-gray-200 p-3 shadow-sm hover:border-blue-400 hover:shadow transition ${onCardClick ? "cursor-pointer" : ""}`,
6360
- children: renderCard(item)
6361
- },
6362
- id
6363
- );
6340
+ const isDragged = id === dragId;
6341
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
6342
+ lineAt === index && /* @__PURE__ */ jsx("div", { className: "h-0.5 rounded-full bg-blue-500", "aria-hidden": true }),
6343
+ /* @__PURE__ */ jsx(
6344
+ "div",
6345
+ {
6346
+ draggable: true,
6347
+ onDragStart: (e) => {
6348
+ setDragId(id);
6349
+ try {
6350
+ e.dataTransfer.effectAllowed = "move";
6351
+ } catch {
6352
+ }
6353
+ },
6354
+ onDragEnter: (e) => {
6355
+ e.stopPropagation();
6356
+ setOver(
6357
+ (prev) => prev && prev.col === col.value && prev.index === index ? prev : { col: col.value, index }
6358
+ );
6359
+ },
6360
+ onDragEnd: reset,
6361
+ onClick: onCardClick ? () => onCardClick(item) : void 0,
6362
+ style: dragId === null ? void 0 : { opacity: isDragged ? 0.4 : 1, transition: "opacity 120ms ease" },
6363
+ className: `rounded-lg bg-white border border-gray-200 p-3 shadow-sm hover:border-blue-400 hover:shadow transition ${onCardClick ? "cursor-pointer" : ""}`,
6364
+ children: renderCard(item)
6365
+ }
6366
+ )
6367
+ ] }, id);
6364
6368
  }),
6369
+ colItems.length > 0 && lineAt === colItems.length && /* @__PURE__ */ jsx("div", { className: "h-0.5 rounded-full bg-blue-500", "aria-hidden": true }),
6365
6370
  colItems.length === 0 && /* @__PURE__ */ jsx(
6366
6371
  "div",
6367
6372
  {
6368
- className: `text-[11px] text-center rounded-lg transition-all duration-150 ${active ? "border-2 border-dashed border-blue-300 bg-blue-50/50 text-blue-400 py-8" : "text-gray-400 py-6"}`,
6373
+ className: `text-[11px] text-center rounded-lg transition-all duration-150 ${isOver ? "border-2 border-dashed border-blue-300 bg-blue-50/50 text-blue-400 py-8" : "text-gray-400 py-6"}`,
6369
6374
  children: columnEmptyText
6370
6375
  }
6371
6376
  )