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