@farmzone/fz-react-ui 1.1.1 → 1.1.3

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.cjs CHANGED
@@ -7924,7 +7924,8 @@ function PageFilter(props) {
7924
7924
  onChange: handleInputChange(optionKey),
7925
7925
  onKeyDown: (e) => {
7926
7926
  if (e.key === "Enter") handleSubmit();
7927
- }
7927
+ },
7928
+ leftIcon: option.leftIcon !== false ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { size: 16 }) : void 0
7928
7929
  }
7929
7930
  ) }, String(optionKey));
7930
7931
  case "select":
@@ -8087,6 +8088,122 @@ function easedFade(dir, opacity) {
8087
8088
  const c = (t) => `rgba(0,0,0,${(opacity * t).toFixed(3)})`;
8088
8089
  return `linear-gradient(${dir}, ${c(1)} 0%, ${c(0.72)} 20%, ${c(0.38)} 45%, ${c(0.1)} 70%, transparent 100%)`;
8089
8090
  }
8091
+ function clusterTabs(tabs, getTabGroup) {
8092
+ const clusters = [];
8093
+ const clusterByGroupKey = /* @__PURE__ */ new Map();
8094
+ for (const tab of tabs) {
8095
+ const group = getTabGroup(tab) ?? null;
8096
+ if (!group) {
8097
+ clusters.push({ group: null, tabs: [tab] });
8098
+ continue;
8099
+ }
8100
+ const existing = clusterByGroupKey.get(group.key);
8101
+ if (existing) {
8102
+ existing.tabs.push(tab);
8103
+ } else {
8104
+ const cluster = { group, tabs: [tab] };
8105
+ clusterByGroupKey.set(group.key, cluster);
8106
+ clusters.push(cluster);
8107
+ }
8108
+ }
8109
+ return clusters;
8110
+ }
8111
+ var TAB_PALETTE = [
8112
+ { base: "rgb(38, 117, 226)", bg: "rgba(38, 117, 226, 0.12)", border: "rgba(38, 117, 226, 0.4)" },
8113
+ // blue
8114
+ { base: "rgb(249, 115, 22)", bg: "rgba(249, 115, 22, 0.12)", border: "rgba(249, 115, 22, 0.4)" },
8115
+ // orange
8116
+ { base: "rgb(239, 68, 68)", bg: "rgba(239, 68, 68, 0.12)", border: "rgba(239, 68, 68, 0.4)" },
8117
+ // red
8118
+ { base: "rgb(34, 197, 94)", bg: "rgba(34, 197, 94, 0.12)", border: "rgba(34, 197, 94, 0.4)" },
8119
+ // green
8120
+ { base: "rgb(168, 85, 247)", bg: "rgba(168, 85, 247, 0.12)", border: "rgba(168, 85, 247, 0.4)" },
8121
+ // purple
8122
+ { base: "rgb(245, 158, 11)", bg: "rgba(245, 158, 11, 0.12)", border: "rgba(245, 158, 11, 0.4)" },
8123
+ // amber
8124
+ { base: "rgb(236, 72, 153)", bg: "rgba(236, 72, 153, 0.12)", border: "rgba(236, 72, 153, 0.4)" },
8125
+ // pink
8126
+ { base: "rgb(20, 184, 166)", bg: "rgba(20, 184, 166, 0.12)", border: "rgba(20, 184, 166, 0.4)" },
8127
+ // teal
8128
+ { base: "rgb(99, 102, 241)", bg: "rgba(99, 102, 241, 0.12)", border: "rgba(99, 102, 241, 0.4)" },
8129
+ // indigo
8130
+ { base: "rgb(6, 182, 212)", bg: "rgba(6, 182, 212, 0.12)", border: "rgba(6, 182, 212, 0.4)" }
8131
+ // cyan
8132
+ ];
8133
+ function assignGroupColors(groupKeys, existing) {
8134
+ const missing = groupKeys.filter((key) => !existing.has(key));
8135
+ if (missing.length === 0) return existing;
8136
+ const next = new Map(existing);
8137
+ for (const key of missing) {
8138
+ if (next.has(key)) continue;
8139
+ const used = new Set([...next.values()].map((c) => c.bg));
8140
+ const free = TAB_PALETTE.find((c) => !used.has(c.bg)) ?? TAB_PALETTE[next.size % TAB_PALETTE.length];
8141
+ next.set(key, free ?? TAB_PALETTE[0]);
8142
+ }
8143
+ return next;
8144
+ }
8145
+ function MultiTabPill(props) {
8146
+ const {
8147
+ tab,
8148
+ isActive,
8149
+ showClose,
8150
+ accentColor,
8151
+ style: s,
8152
+ onClick,
8153
+ onClose,
8154
+ registerRef,
8155
+ draggable = false,
8156
+ onPointerDown,
8157
+ dropIndicator = null,
8158
+ isDraggingThis = false
8159
+ } = props;
8160
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8161
+ "div",
8162
+ {
8163
+ ref: (el) => registerRef(tab.id, el),
8164
+ onPointerDown: draggable ? onPointerDown : void 0,
8165
+ onClick: () => {
8166
+ if (!isActive) onClick();
8167
+ },
8168
+ className: `
8169
+ group relative flex items-center gap-1.5 ${s.tabPx} ${s.tabPy} flex-shrink-0
8170
+ ${s.rounded} border border-b-0 select-none
8171
+ transition-colors duration-100
8172
+ ${isActive ? s.activeTab : s.inactiveTab}
8173
+ ${draggable ? isDraggingThis ? "cursor-grabbing" : "cursor-grab" : "cursor-pointer"}
8174
+ ${isDraggingThis ? "opacity-40" : ""}
8175
+ ${dropIndicator === "before" ? "[box-shadow:inset_2px_0_0_0_#3b82f6]" : ""}
8176
+ ${dropIndicator === "after" ? "[box-shadow:inset_-2px_0_0_0_#3b82f6]" : ""}
8177
+ `,
8178
+ children: [
8179
+ isActive && /* @__PURE__ */ jsxRuntime.jsx(
8180
+ "div",
8181
+ {
8182
+ className: `absolute top-0 left-0 right-0 h-0.5 ${s.rounded} ${s.indicator}`,
8183
+ style: accentColor ? { backgroundColor: accentColor } : void 0
8184
+ }
8185
+ ),
8186
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${s.tabTextSize} max-w-35 truncate leading-none ${isActive ? s.activeText : s.inactiveText}`, children: tab.label }),
8187
+ showClose && /* @__PURE__ */ jsxRuntime.jsx(
8188
+ "button",
8189
+ {
8190
+ onClick: (e) => {
8191
+ e.stopPropagation();
8192
+ onClose();
8193
+ },
8194
+ className: `
8195
+ flex-shrink-0 ${s.closeBtnWH} flex items-center justify-center
8196
+ transition-all duration-100 cursor-pointer
8197
+ ${s.closeBtn}
8198
+ ${isActive ? "opacity-100" : "opacity-40 group-hover:opacity-60"}
8199
+ `,
8200
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: s.closeBtnSize, strokeWidth: 2.5 })
8201
+ }
8202
+ )
8203
+ ]
8204
+ }
8205
+ );
8206
+ }
8090
8207
  function MultiTabBar(props) {
8091
8208
  const {
8092
8209
  tabs,
@@ -8096,25 +8213,77 @@ function MultiTabBar(props) {
8096
8213
  onTabAdd,
8097
8214
  onTabsReorder,
8098
8215
  variant = "light",
8099
- primaryColor
8216
+ primaryColor,
8217
+ getTabGroup
8100
8218
  } = props;
8101
8219
  const s = VARIANT_STYLES[variant];
8102
8220
  const scrollRef = React6.useRef(null);
8103
8221
  const tabRefs = React6.useRef(/* @__PURE__ */ new Map());
8222
+ const registerTabRef = (id, el) => {
8223
+ if (el) tabRefs.current.set(id, el);
8224
+ else tabRefs.current.delete(id);
8225
+ };
8104
8226
  const [showLeftFade, setShowLeftFade] = React6.useState(false);
8105
8227
  const [showRightFade, setShowRightFade] = React6.useState(false);
8106
8228
  const [draggingId, setDraggingId] = React6.useState(null);
8107
8229
  const [dropTargetId, setDropTargetId] = React6.useState(null);
8108
8230
  const [dropSide, setDropSide] = React6.useState("before");
8109
- const draggingIdRef = React6.useRef(null);
8231
+ const [draggingGroupKey, setDraggingGroupKey] = React6.useState(null);
8232
+ const [groupDropTargetKey, setGroupDropTargetKey] = React6.useState(null);
8233
+ const [groupDropSide, setGroupDropSide] = React6.useState("before");
8234
+ const [dragPos, setDragPos] = React6.useState(null);
8110
8235
  const dropTargetIdRef = React6.useRef(null);
8111
8236
  const dropSideRef = React6.useRef("before");
8237
+ const groupDropTargetKeyRef = React6.useRef(null);
8238
+ const groupDropSideRef = React6.useRef("before");
8112
8239
  const tabsRef = React6.useRef(tabs);
8113
- const dropHandledRef = React6.useRef(false);
8114
- draggingIdRef.current = draggingId;
8115
- dropTargetIdRef.current = dropTargetId;
8116
- dropSideRef.current = dropSide;
8117
- tabsRef.current = tabs;
8240
+ React6.useEffect(() => {
8241
+ dropTargetIdRef.current = dropTargetId;
8242
+ dropSideRef.current = dropSide;
8243
+ groupDropTargetKeyRef.current = groupDropTargetKey;
8244
+ groupDropSideRef.current = groupDropSide;
8245
+ tabsRef.current = tabs;
8246
+ });
8247
+ const groupRefs = React6.useRef(/* @__PURE__ */ new Map());
8248
+ const registerGroupRef = (key, el) => {
8249
+ if (el) groupRefs.current.set(key, el);
8250
+ else groupRefs.current.delete(key);
8251
+ };
8252
+ const clusters = React6.useMemo(
8253
+ () => getTabGroup ? clusterTabs(tabs, getTabGroup) : null,
8254
+ [tabs, getTabGroup]
8255
+ );
8256
+ const groupKeys = React6.useMemo(
8257
+ () => clusters ? Array.from(new Set(clusters.map((c) => c.group?.key).filter((k) => !!k))) : [],
8258
+ [clusters]
8259
+ );
8260
+ const [groupColorAssignments, setGroupColorAssignments] = React6.useState(() => /* @__PURE__ */ new Map());
8261
+ const groupColors = assignGroupColors(groupKeys, groupColorAssignments);
8262
+ if (groupColors !== groupColorAssignments) {
8263
+ setGroupColorAssignments(groupColors);
8264
+ }
8265
+ const [collapsedGroups, setCollapsedGroups] = React6.useState(/* @__PURE__ */ new Set());
8266
+ const [prevActiveTabId, setPrevActiveTabId] = React6.useState(activeTabId);
8267
+ if (activeTabId !== prevActiveTabId) {
8268
+ setPrevActiveTabId(activeTabId);
8269
+ if (clusters) {
8270
+ const activeCluster = clusters.find((c) => c.tabs.some((t) => t.id === activeTabId));
8271
+ const key = activeCluster?.group?.key;
8272
+ if (key && collapsedGroups.has(key)) {
8273
+ const next = new Set(collapsedGroups);
8274
+ next.delete(key);
8275
+ setCollapsedGroups(next);
8276
+ }
8277
+ }
8278
+ }
8279
+ const toggleGroupCollapsed = (key) => {
8280
+ setCollapsedGroups((prev) => {
8281
+ const next = new Set(prev);
8282
+ if (next.has(key)) next.delete(key);
8283
+ else next.add(key);
8284
+ return next;
8285
+ });
8286
+ };
8118
8287
  React6.useEffect(() => {
8119
8288
  if (!activeTabId || !scrollRef.current) return;
8120
8289
  const el = tabRefs.current.get(activeTabId);
@@ -8152,9 +8321,12 @@ function MultiTabBar(props) {
8152
8321
  el.addEventListener("wheel", onWheel, { passive: false });
8153
8322
  return () => el.removeEventListener("wheel", onWheel);
8154
8323
  }, []);
8155
- const resolveDropPosition = (clientX) => {
8324
+ const reorderEnabled = !!onTabsReorder;
8325
+ const pointerStateRef = React6.useRef(null);
8326
+ const suppressClickRef = React6.useRef(false);
8327
+ const resolveDropPosition = (clientX, draggingTabId) => {
8156
8328
  for (const [tabId, el] of tabRefs.current) {
8157
- if (tabId === draggingIdRef.current) continue;
8329
+ if (tabId === draggingTabId) continue;
8158
8330
  const rect = el.getBoundingClientRect();
8159
8331
  if (clientX >= rect.left && clientX < rect.right) {
8160
8332
  setDropSide(clientX < rect.left + rect.width / 2 ? "before" : "after");
@@ -8165,7 +8337,7 @@ function MultiTabBar(props) {
8165
8337
  let rightmostTabId = null;
8166
8338
  let maxRight = -Infinity;
8167
8339
  for (const [tabId, el] of tabRefs.current) {
8168
- if (tabId === draggingIdRef.current) continue;
8340
+ if (tabId === draggingTabId) continue;
8169
8341
  const rect = el.getBoundingClientRect();
8170
8342
  if (rect.right > maxRight) {
8171
8343
  maxRight = rect.right;
@@ -8189,180 +8361,322 @@ function MultiTabBar(props) {
8189
8361
  newTabs.splice(insertIdx, 0, moved);
8190
8362
  onTabsReorder(newTabs);
8191
8363
  };
8364
+ const resolveGroupDropPosition = (clientX, draggingKey) => {
8365
+ const candidates = [];
8366
+ for (const [tabId, el] of tabRefs.current) {
8367
+ const tab = tabsRef.current.find((t) => t.id === tabId);
8368
+ if (tab && getTabGroup?.(tab)?.key === draggingKey) continue;
8369
+ candidates.push([`tab:${tabId}`, el]);
8370
+ }
8371
+ for (const [key, el] of groupRefs.current) {
8372
+ if (key === draggingKey) continue;
8373
+ candidates.push([`group:${key}`, el]);
8374
+ }
8375
+ for (const [unitKey, el] of candidates) {
8376
+ const rect = el.getBoundingClientRect();
8377
+ if (clientX >= rect.left && clientX < rect.right) {
8378
+ setGroupDropSide(clientX < rect.left + rect.width / 2 ? "before" : "after");
8379
+ setGroupDropTargetKey(unitKey);
8380
+ return;
8381
+ }
8382
+ }
8383
+ let rightmostKey = null;
8384
+ let maxRight = -Infinity;
8385
+ for (const [unitKey, el] of candidates) {
8386
+ const rect = el.getBoundingClientRect();
8387
+ if (rect.right > maxRight) {
8388
+ maxRight = rect.right;
8389
+ rightmostKey = unitKey;
8390
+ }
8391
+ }
8392
+ if (rightmostKey && clientX >= maxRight) {
8393
+ setGroupDropSide("after");
8394
+ setGroupDropTargetKey(rightmostKey);
8395
+ }
8396
+ };
8397
+ const reorderGroupTabs = (draggedKey, targetUnitKey, side, sourceTabs) => {
8398
+ if (!onTabsReorder || !getTabGroup) return;
8399
+ const draggedTabs = sourceTabs.filter((t) => getTabGroup(t)?.key === draggedKey);
8400
+ const rest = sourceTabs.filter((t) => getTabGroup(t)?.key !== draggedKey);
8401
+ if (draggedTabs.length === 0) return;
8402
+ let insertIdx;
8403
+ if (targetUnitKey.startsWith("group:")) {
8404
+ const targetKey = targetUnitKey.slice("group:".length);
8405
+ if (targetKey === draggedKey) return;
8406
+ const targetIndices = rest.map((t, i) => getTabGroup(t)?.key === targetKey ? i : -1).filter((i) => i !== -1);
8407
+ if (targetIndices.length === 0) return;
8408
+ insertIdx = side === "before" ? targetIndices[0] : targetIndices[targetIndices.length - 1] + 1;
8409
+ } else {
8410
+ const targetTabId = targetUnitKey.slice("tab:".length);
8411
+ const idx = rest.findIndex((t) => t.id === targetTabId);
8412
+ if (idx === -1) return;
8413
+ insertIdx = side === "before" ? idx : idx + 1;
8414
+ }
8415
+ const newTabs = [...rest.slice(0, insertIdx), ...draggedTabs, ...rest.slice(insertIdx)];
8416
+ onTabsReorder(newTabs);
8417
+ };
8192
8418
  React6.useEffect(() => {
8193
- if (!onTabsReorder) return;
8194
- const onDocDragOver = (e) => {
8195
- if (!draggingIdRef.current) return;
8196
- e.preventDefault();
8197
- resolveDropPosition(e.clientX);
8419
+ if (!reorderEnabled) return;
8420
+ const DRAG_THRESHOLD = 4;
8421
+ const onPointerMove = (e) => {
8422
+ const state = pointerStateRef.current;
8423
+ if (!state) return;
8424
+ if (!state.dragging) {
8425
+ const dx = Math.abs(e.clientX - state.startX);
8426
+ const dy = Math.abs(e.clientY - state.startY);
8427
+ if (dx < DRAG_THRESHOLD && dy < DRAG_THRESHOLD) return;
8428
+ state.dragging = true;
8429
+ suppressClickRef.current = true;
8430
+ if (state.kind === "tab") setDraggingId(state.id);
8431
+ else setDraggingGroupKey(state.id);
8432
+ }
8433
+ setDragPos({ x: e.clientX, y: e.clientY });
8434
+ if (state.kind === "tab") resolveDropPosition(e.clientX, state.id);
8435
+ else resolveGroupDropPosition(e.clientX, state.id);
8198
8436
  };
8199
- const onDocDrop = (e) => {
8200
- if (dropHandledRef.current) {
8201
- dropHandledRef.current = false;
8202
- return;
8437
+ const onPointerUp = () => {
8438
+ const state = pointerStateRef.current;
8439
+ if (state?.dragging) {
8440
+ if (state.kind === "tab") {
8441
+ const target = dropTargetIdRef.current;
8442
+ if (target) reorderTabs(target, dropSideRef.current, tabsRef.current, state.id);
8443
+ } else {
8444
+ const target = groupDropTargetKeyRef.current;
8445
+ if (target) reorderGroupTabs(state.id, target, groupDropSideRef.current, tabsRef.current);
8446
+ }
8203
8447
  }
8204
- e.preventDefault();
8205
- const id = draggingIdRef.current;
8206
- const target = dropTargetIdRef.current;
8207
- if (id && target) reorderTabs(target, dropSideRef.current, tabsRef.current, id);
8448
+ pointerStateRef.current = null;
8208
8449
  setDraggingId(null);
8209
8450
  setDropTargetId(null);
8451
+ setDraggingGroupKey(null);
8452
+ setGroupDropTargetKey(null);
8453
+ setDragPos(null);
8454
+ requestAnimationFrame(() => {
8455
+ suppressClickRef.current = false;
8456
+ });
8210
8457
  };
8211
- document.addEventListener("dragover", onDocDragOver);
8212
- document.addEventListener("drop", onDocDrop);
8458
+ window.addEventListener("pointermove", onPointerMove);
8459
+ window.addEventListener("pointerup", onPointerUp);
8213
8460
  return () => {
8214
- document.removeEventListener("dragover", onDocDragOver);
8215
- document.removeEventListener("drop", onDocDrop);
8461
+ window.removeEventListener("pointermove", onPointerMove);
8462
+ window.removeEventListener("pointerup", onPointerUp);
8216
8463
  };
8217
- }, [onTabsReorder]);
8218
- const handleDragStart = (e, tabId) => {
8219
- setDraggingId(tabId);
8220
- e.dataTransfer.effectAllowed = "move";
8221
- };
8222
- const handleDrop = (e, targetTabId) => {
8223
- e.preventDefault();
8224
- e.stopPropagation();
8225
- dropHandledRef.current = true;
8226
- const id = draggingIdRef.current;
8227
- if (id && id !== targetTabId) reorderTabs(targetTabId, dropSideRef.current, tabsRef.current, id);
8228
- setDraggingId(null);
8229
- setDropTargetId(null);
8230
- };
8231
- const handleContainerDrop = (e) => {
8232
- e.preventDefault();
8233
- dropHandledRef.current = true;
8234
- const id = draggingIdRef.current;
8235
- if (id && onTabsReorder) {
8236
- const fromIdx = tabsRef.current.findIndex((t) => t.id === id);
8237
- if (fromIdx !== -1 && fromIdx < tabsRef.current.length - 1) {
8238
- const newTabs = [...tabsRef.current];
8239
- const moved = newTabs.splice(fromIdx, 1)[0];
8240
- newTabs.push(moved);
8241
- onTabsReorder(newTabs);
8242
- }
8243
- }
8244
- setDraggingId(null);
8245
- setDropTargetId(null);
8464
+ }, [reorderEnabled]);
8465
+ const handlePointerDown = (e, tabId) => {
8466
+ pointerStateRef.current = { kind: "tab", id: tabId, startX: e.clientX, startY: e.clientY, dragging: false };
8246
8467
  };
8247
- const handleDragEnd = () => {
8248
- dropHandledRef.current = false;
8249
- setDraggingId(null);
8250
- setDropTargetId(null);
8468
+ const handleGroupPointerDown = (e, groupKey) => {
8469
+ pointerStateRef.current = { kind: "group", id: groupKey, startX: e.clientX, startY: e.clientY, dragging: false };
8251
8470
  };
8252
8471
  const showPlus = !!onTabAdd;
8472
+ const canClose = tabs.length > 1;
8473
+ const draggingTab = draggingId ? tabs.find((tab) => tab.id === draggingId) : void 0;
8474
+ const draggingCluster = clusters?.find((c) => c.tabs.some((t) => t.id === draggingId));
8475
+ const draggingColor = draggingCluster?.group ? groupColors.get(draggingCluster.group.key)?.border : primaryColor;
8476
+ const draggingGroupCluster = draggingGroupKey ? clusters?.find((c) => c.group?.key === draggingGroupKey) : void 0;
8477
+ const draggingGroupColor = draggingGroupKey ? groupColors.get(draggingGroupKey) : void 0;
8478
+ const ghost = draggingGroupKey && draggingGroupCluster?.group ? {
8479
+ label: draggingGroupCluster.group.label,
8480
+ bg: (draggingGroupColor ?? TAB_PALETTE[0]).bg,
8481
+ border: (draggingGroupColor ?? TAB_PALETTE[0]).border,
8482
+ color: (draggingGroupColor ?? TAB_PALETTE[0]).base
8483
+ } : draggingTab ? { label: draggingTab.label, bg: "white", border: draggingColor ?? "#e5e7eb", color: "#111827" } : null;
8253
8484
  if (tabs.length === 0) return null;
8254
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex-shrink-0 w-full overflow-hidden ${s.container}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-stretch", children: [
8255
- showLeftFade && /* @__PURE__ */ jsxRuntime.jsx(
8256
- "div",
8257
- {
8258
- className: "absolute left-0 top-0 bottom-0 w-10 pointer-events-none z-10",
8259
- style: { background: easedFade("to right", s.fadeOpacity) }
8260
- }
8261
- ),
8262
- showRightFade && /* @__PURE__ */ jsxRuntime.jsx(
8263
- "div",
8264
- {
8265
- className: "absolute top-0 bottom-0 w-10 pointer-events-none z-10",
8266
- style: {
8267
- right: showPlus ? PLUS_BTN_WIDTH : 0,
8268
- background: easedFade("to left", s.fadeOpacity)
8485
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8486
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex-shrink-0 w-full overflow-hidden ${s.container}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-stretch", children: [
8487
+ showLeftFade && /* @__PURE__ */ jsxRuntime.jsx(
8488
+ "div",
8489
+ {
8490
+ className: "absolute left-0 top-0 bottom-0 w-10 pointer-events-none z-10",
8491
+ style: { background: easedFade("to right", s.fadeOpacity) }
8269
8492
  }
8270
- }
8271
- ),
8272
- /* @__PURE__ */ jsxRuntime.jsx(
8273
- "div",
8274
- {
8275
- ref: scrollRef,
8276
- className: `flex-1 min-w-0 flex items-end gap-0.5 ${s.scrollPadding} overflow-x-auto [&::-webkit-scrollbar]:hidden`,
8277
- style: { scrollbarWidth: "none" },
8278
- onScroll: updateFades,
8279
- onDragOver: (e) => {
8280
- if (draggingId) e.preventDefault();
8281
- },
8282
- onDrop: handleContainerDrop,
8283
- children: tabs.map((tab) => {
8284
- const isActive = tab.id === activeTabId;
8285
- const isDraggingThis = tab.id === draggingId;
8286
- const isDropTarget = dropTargetId === tab.id;
8287
- return /* @__PURE__ */ jsxRuntime.jsxs(
8288
- "div",
8289
- {
8290
- ref: (el) => {
8291
- if (el) tabRefs.current.set(tab.id, el);
8292
- else tabRefs.current.delete(tab.id);
8293
- },
8294
- draggable: !!onTabsReorder,
8295
- onClick: () => {
8296
- if (!isActive) onTabClick(tab.id);
8297
- },
8298
- onDragStart: (e) => handleDragStart(e, tab.id),
8299
- onDrop: (e) => handleDrop(e, tab.id),
8300
- onDragEnd: handleDragEnd,
8301
- className: `
8302
- group relative flex items-center gap-1.5 ${s.tabPx} ${s.tabPy} flex-shrink-0
8303
- ${s.rounded} border border-b-0 select-none
8304
- transition-colors duration-100
8305
- ${isActive ? s.activeTab : s.inactiveTab}
8306
- ${onTabsReorder ? "cursor-grab" : "cursor-pointer"}
8307
- ${isDraggingThis ? "opacity-40" : ""}
8308
- ${isDropTarget && dropSide === "before" ? "[box-shadow:inset_2px_0_0_0_#3b82f6]" : ""}
8309
- ${isDropTarget && dropSide === "after" ? "[box-shadow:inset_-2px_0_0_0_#3b82f6]" : ""}
8310
- `,
8311
- children: [
8312
- isActive && /* @__PURE__ */ jsxRuntime.jsx(
8313
- "div",
8314
- {
8315
- className: `absolute top-0 left-0 right-0 h-0.5 ${s.rounded} ${s.indicator}`,
8316
- style: primaryColor ? { backgroundColor: primaryColor } : void 0
8317
- }
8318
- ),
8319
- /* @__PURE__ */ jsxRuntime.jsx(
8320
- "span",
8321
- {
8322
- className: `${s.tabTextSize} max-w-35 truncate leading-none ${isActive ? s.activeText : s.inactiveText}`,
8323
- children: tab.label
8324
- }
8325
- ),
8326
- tabs.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(
8327
- "button",
8493
+ ),
8494
+ showRightFade && /* @__PURE__ */ jsxRuntime.jsx(
8495
+ "div",
8496
+ {
8497
+ className: "absolute top-0 bottom-0 w-10 pointer-events-none z-10",
8498
+ style: {
8499
+ right: showPlus ? PLUS_BTN_WIDTH : 0,
8500
+ background: easedFade("to left", s.fadeOpacity)
8501
+ }
8502
+ }
8503
+ ),
8504
+ /* @__PURE__ */ jsxRuntime.jsx(
8505
+ "div",
8506
+ {
8507
+ ref: scrollRef,
8508
+ className: `flex-1 min-w-0 flex items-end gap-0.5 ${s.scrollPadding} overflow-x-auto [&::-webkit-scrollbar]:hidden`,
8509
+ style: { scrollbarWidth: "none" },
8510
+ onScroll: updateFades,
8511
+ children: clusters ? clusters.map((cluster) => {
8512
+ if (!cluster.group) {
8513
+ return cluster.tabs.map((tab) => {
8514
+ const isDropTarget = dropTargetId === tab.id;
8515
+ const isGroupDropTarget2 = groupDropTargetKey === `tab:${tab.id}`;
8516
+ return /* @__PURE__ */ jsxRuntime.jsx(
8517
+ MultiTabPill,
8328
8518
  {
8329
- onClick: (e) => {
8330
- e.stopPropagation();
8331
- onTabClose(tab.id);
8519
+ tab,
8520
+ isActive: tab.id === activeTabId,
8521
+ showClose: canClose,
8522
+ accentColor: primaryColor,
8523
+ style: s,
8524
+ onClick: () => {
8525
+ if (suppressClickRef.current) return;
8526
+ onTabClick(tab.id);
8332
8527
  },
8333
- className: `
8334
- flex-shrink-0 ${s.closeBtnWH} flex items-center justify-center
8335
- transition-all duration-100 cursor-pointer
8336
- ${s.closeBtn}
8337
- ${isActive ? "opacity-100" : "opacity-40 group-hover:opacity-60"}
8338
- `,
8339
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: s.closeBtnSize, strokeWidth: 2.5 })
8340
- }
8341
- )
8342
- ]
8343
- },
8344
- tab.id
8345
- );
8346
- })
8347
- }
8348
- ),
8349
- showPlus && /* @__PURE__ */ jsxRuntime.jsx(
8350
- "div",
8351
- {
8352
- className: `flex-shrink-0 flex items-center justify-center ${s.plusBtnArea}`,
8353
- style: { width: PLUS_BTN_WIDTH },
8354
- children: /* @__PURE__ */ jsxRuntime.jsx(
8355
- "button",
8356
- {
8357
- onClick: onTabAdd,
8358
- title: "\uD604\uC7AC \uD398\uC774\uC9C0\uB97C \uC0C8 \uD0ED\uC73C\uB85C \uC5F4\uAE30",
8359
- className: `w-6 h-6 flex items-center justify-center rounded cursor-pointer transition-colors ${s.plusBtn}`,
8360
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 13, strokeWidth: 2 })
8361
- }
8362
- )
8363
- }
8364
- )
8365
- ] }) });
8528
+ onClose: () => onTabClose(tab.id),
8529
+ registerRef: registerTabRef,
8530
+ draggable: reorderEnabled,
8531
+ isDraggingThis: tab.id === draggingId,
8532
+ dropIndicator: isDropTarget ? dropSide : isGroupDropTarget2 ? groupDropSide : null,
8533
+ onPointerDown: (e) => handlePointerDown(e, tab.id)
8534
+ },
8535
+ tab.id
8536
+ );
8537
+ });
8538
+ }
8539
+ const { key, label } = cluster.group;
8540
+ const color = groupColors.get(key) ?? TAB_PALETTE[0];
8541
+ const collapsed = collapsedGroups.has(key);
8542
+ const groupHasActiveTab = cluster.tabs.some((t) => t.id === activeTabId);
8543
+ const isGroupDropTarget = groupDropTargetKey === `group:${key}`;
8544
+ const isDraggingThisGroup = draggingGroupKey === key;
8545
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8546
+ "div",
8547
+ {
8548
+ ref: (el) => registerGroupRef(key, el),
8549
+ className: "flex shrink-0 flex-col items-stretch",
8550
+ children: [
8551
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
8552
+ /* @__PURE__ */ jsxRuntime.jsxs(
8553
+ "button",
8554
+ {
8555
+ type: "button",
8556
+ onClick: () => {
8557
+ if (suppressClickRef.current) return;
8558
+ toggleGroupCollapsed(key);
8559
+ },
8560
+ onPointerDown: reorderEnabled ? (e) => handleGroupPointerDown(e, key) : void 0,
8561
+ "aria-expanded": !collapsed,
8562
+ title: collapsed ? "\uD558\uC704 \uD0ED \uD3BC\uCE58\uAE30" : "\uD558\uC704 \uD0ED \uC811\uAE30",
8563
+ className: `flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs font-bold whitespace-nowrap select-none ${reorderEnabled ? isDraggingThisGroup ? "cursor-grabbing" : "cursor-grab" : "cursor-pointer"} ${isDraggingThisGroup ? "opacity-40" : ""} ${isGroupDropTarget && groupDropSide === "before" ? "[box-shadow:inset_2px_0_0_0_#3b82f6]" : ""} ${isGroupDropTarget && groupDropSide === "after" ? "[box-shadow:inset_-2px_0_0_0_#3b82f6]" : ""}`,
8564
+ style: { backgroundColor: color.bg, borderColor: color.border, color: color.base },
8565
+ children: [
8566
+ collapsed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 12 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 12 }),
8567
+ label,
8568
+ canClose && /* @__PURE__ */ jsxRuntime.jsx(
8569
+ lucideReact.X,
8570
+ {
8571
+ size: 12,
8572
+ onClick: (e) => {
8573
+ e.stopPropagation();
8574
+ cluster.tabs.forEach((t) => onTabClose(t.id));
8575
+ }
8576
+ }
8577
+ )
8578
+ ]
8579
+ }
8580
+ ),
8581
+ !collapsed && cluster.tabs.map((tab) => {
8582
+ const isDropTarget = dropTargetId === tab.id;
8583
+ const isTabGroupDropTarget = groupDropTargetKey === `tab:${tab.id}`;
8584
+ return /* @__PURE__ */ jsxRuntime.jsx(
8585
+ MultiTabPill,
8586
+ {
8587
+ tab,
8588
+ isActive: tab.id === activeTabId,
8589
+ showClose: canClose,
8590
+ accentColor: color.base,
8591
+ style: s,
8592
+ onClick: () => {
8593
+ if (suppressClickRef.current) return;
8594
+ onTabClick(tab.id);
8595
+ },
8596
+ onClose: () => onTabClose(tab.id),
8597
+ registerRef: registerTabRef,
8598
+ draggable: reorderEnabled,
8599
+ isDraggingThis: tab.id === draggingId,
8600
+ dropIndicator: isDropTarget ? dropSide : isTabGroupDropTarget ? groupDropSide : null,
8601
+ onPointerDown: (e) => handlePointerDown(e, tab.id)
8602
+ },
8603
+ tab.id
8604
+ );
8605
+ })
8606
+ ] }),
8607
+ /* @__PURE__ */ jsxRuntime.jsx(
8608
+ "div",
8609
+ {
8610
+ className: "mt-1 h-0.75 rounded-full",
8611
+ style: { backgroundColor: color.base, opacity: groupHasActiveTab ? 1 : 0.35 }
8612
+ }
8613
+ )
8614
+ ]
8615
+ },
8616
+ key
8617
+ );
8618
+ }) : tabs.map((tab) => {
8619
+ const isDropTarget = dropTargetId === tab.id;
8620
+ return /* @__PURE__ */ jsxRuntime.jsx(
8621
+ MultiTabPill,
8622
+ {
8623
+ tab,
8624
+ isActive: tab.id === activeTabId,
8625
+ showClose: canClose,
8626
+ accentColor: primaryColor,
8627
+ style: s,
8628
+ onClick: () => {
8629
+ if (suppressClickRef.current) return;
8630
+ onTabClick(tab.id);
8631
+ },
8632
+ onClose: () => onTabClose(tab.id),
8633
+ registerRef: registerTabRef,
8634
+ draggable: reorderEnabled,
8635
+ isDraggingThis: tab.id === draggingId,
8636
+ dropIndicator: isDropTarget ? dropSide : null,
8637
+ onPointerDown: (e) => handlePointerDown(e, tab.id)
8638
+ },
8639
+ tab.id
8640
+ );
8641
+ })
8642
+ }
8643
+ ),
8644
+ showPlus && /* @__PURE__ */ jsxRuntime.jsx(
8645
+ "div",
8646
+ {
8647
+ className: `flex-shrink-0 flex items-center justify-center ${s.plusBtnArea}`,
8648
+ style: { width: PLUS_BTN_WIDTH },
8649
+ children: /* @__PURE__ */ jsxRuntime.jsx(
8650
+ "button",
8651
+ {
8652
+ onClick: onTabAdd,
8653
+ title: "\uD604\uC7AC \uD398\uC774\uC9C0\uB97C \uC0C8 \uD0ED\uC73C\uB85C \uC5F4\uAE30",
8654
+ className: `w-6 h-6 flex items-center justify-center rounded cursor-pointer transition-colors ${s.plusBtn}`,
8655
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 13, strokeWidth: 2 })
8656
+ }
8657
+ )
8658
+ }
8659
+ )
8660
+ ] }) }),
8661
+ dragPos && ghost ? reactDom.createPortal(
8662
+ /* @__PURE__ */ jsxRuntime.jsx(
8663
+ "div",
8664
+ {
8665
+ className: "pointer-events-none fixed z-50 flex items-center gap-1.5 rounded-md border px-2.5 py-1 text-xs font-bold whitespace-nowrap shadow-lg",
8666
+ style: {
8667
+ left: dragPos.x + 12,
8668
+ top: dragPos.y + 12,
8669
+ backgroundColor: ghost.bg,
8670
+ borderColor: ghost.border,
8671
+ color: ghost.color,
8672
+ transform: "rotate(-2deg)"
8673
+ },
8674
+ children: ghost.label
8675
+ }
8676
+ ),
8677
+ document.body
8678
+ ) : null
8679
+ ] });
8366
8680
  }
8367
8681
  var userScopedStorage = middleware.createJSONStorage(() => ({
8368
8682
  getItem: (name) => {