@farmzone/fz-react-ui 1.0.8 → 1.0.9

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
@@ -3478,36 +3478,100 @@ var MODAL_ICONS = {
3478
3478
  delete: lucideReact.Trash2,
3479
3479
  warning: lucideReact.TriangleAlert
3480
3480
  };
3481
+ var VARIANT_CONFIG = {
3482
+ default: {
3483
+ container: "bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-100",
3484
+ iconBg: "bg-blue-100 rounded-full",
3485
+ iconColor: "text-main",
3486
+ titleColor: "text-gray-900",
3487
+ descColor: "text-gray-500",
3488
+ closeBtn: "text-gray-400 group-hover:text-gray-600"
3489
+ },
3490
+ "system-light": {
3491
+ container: "bg-gray-50 border-gray-200",
3492
+ iconBg: "bg-white border border-gray-200 shadow-sm rounded-lg",
3493
+ iconColor: "text-slate-600",
3494
+ titleColor: "text-gray-900",
3495
+ descColor: "text-gray-500",
3496
+ closeBtn: "text-gray-400 group-hover:text-gray-600"
3497
+ },
3498
+ "system-dark": {
3499
+ container: "bg-slate-800 border-slate-700",
3500
+ iconBg: "bg-slate-700 border border-slate-600 shadow-sm rounded-lg",
3501
+ iconColor: "text-slate-300",
3502
+ titleColor: "text-slate-100",
3503
+ descColor: "text-slate-400",
3504
+ closeBtn: "text-slate-500 group-hover:text-slate-300"
3505
+ }
3506
+ };
3507
+ var SIZE_CONFIG = {
3508
+ md: {
3509
+ padding: "px-6 py-5",
3510
+ iconBox: "w-10 h-10",
3511
+ iconSize: 20,
3512
+ gap: "gap-3",
3513
+ titleSize: "text-xl font-bold",
3514
+ descSize: "text-sm mt-0.5",
3515
+ closeBtnBox: "w-10 h-10",
3516
+ closeIconSize: 20
3517
+ },
3518
+ sm: {
3519
+ padding: "px-4 py-4",
3520
+ iconBox: "w-8 h-8",
3521
+ iconSize: 16,
3522
+ gap: "gap-2.5",
3523
+ titleSize: "text-lg font-bold",
3524
+ descSize: "text-sm mt-0.5",
3525
+ closeBtnBox: "w-8 h-8",
3526
+ closeIconSize: 16
3527
+ }
3528
+ };
3481
3529
  function ModalIconHeader(props) {
3482
- const { type, title, description, onClose, className } = props;
3530
+ const {
3531
+ type,
3532
+ title,
3533
+ description,
3534
+ onClose,
3535
+ action,
3536
+ variant = "default",
3537
+ size: size2 = "md",
3538
+ primaryColor,
3539
+ className
3540
+ } = props;
3483
3541
  const Icon2 = MODAL_ICONS[type];
3484
- return /* @__PURE__ */ jsxRuntime.jsx(
3485
- ModalHeader,
3486
- {
3487
- className: cn(
3488
- "bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-100 p-0 text-left space-y-0",
3489
- className
3490
- ),
3491
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-5", children: [
3492
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
3493
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-10 h-10 bg-blue-100 rounded-full shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(Icon2, { size: 20, className: "text-main" }) }),
3494
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3495
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-bold text-gray-900", children: title }),
3496
- description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-600 mt-0.5", children: description })
3497
- ] })
3498
- ] }),
3499
- /* @__PURE__ */ jsxRuntime.jsx(
3500
- "button",
3501
- {
3502
- type: "button",
3503
- onClick: onClose,
3504
- className: "flex items-center justify-center w-10 h-10 rounded-full transition-colors group cursor-pointer hover:bg-black/5 shrink-0",
3505
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 20, className: "text-gray-400 group-hover:text-gray-600 transition-colors" })
3506
- }
3507
- )
3542
+ const v = VARIANT_CONFIG[variant];
3543
+ const s = SIZE_CONFIG[size2];
3544
+ const hasColor = !!primaryColor;
3545
+ const isDark = variant === "system-dark";
3546
+ const containerStyle = hasColor && variant === "default" ? {
3547
+ background: `linear-gradient(to right, color-mix(in srgb, ${primaryColor} 6%, white), color-mix(in srgb, ${primaryColor} 12%, white))`,
3548
+ borderColor: `color-mix(in srgb, ${primaryColor} 25%, transparent)`
3549
+ } : void 0;
3550
+ const iconBgStyle = hasColor ? {
3551
+ backgroundColor: isDark ? `color-mix(in srgb, ${primaryColor} 20%, #334155)` : `color-mix(in srgb, ${primaryColor} 15%, white)`
3552
+ } : void 0;
3553
+ const iconColorStyle = hasColor && !isDark ? { color: primaryColor } : void 0;
3554
+ return /* @__PURE__ */ jsxRuntime.jsx(ModalHeader, { className: cn("p-0 text-left space-y-0", v.container, className), style: containerStyle, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex items-center justify-between", s.padding), children: [
3555
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex items-center", s.gap), children: [
3556
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex items-center justify-center shrink-0", s.iconBox, v.iconBg), style: iconBgStyle, children: /* @__PURE__ */ jsxRuntime.jsx(Icon2, { size: s.iconSize, className: hasColor && !isDark ? void 0 : v.iconColor, style: iconColorStyle }) }),
3557
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3558
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: cn(s.titleSize, v.titleColor), children: title }),
3559
+ description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn(s.descSize, v.descColor), children: description })
3508
3560
  ] })
3509
- }
3510
- );
3561
+ ] }),
3562
+ action ?? /* @__PURE__ */ jsxRuntime.jsx(
3563
+ "button",
3564
+ {
3565
+ type: "button",
3566
+ onClick: onClose,
3567
+ className: cn(
3568
+ "flex items-center justify-center rounded-full transition-colors group cursor-pointer hover:bg-black/5 shrink-0",
3569
+ s.closeBtnBox
3570
+ ),
3571
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: s.closeIconSize, className: cn("transition-colors", v.closeBtn) })
3572
+ }
3573
+ )
3574
+ ] }) });
3511
3575
  }
3512
3576
  function Modal(props) {
3513
3577
  const {
@@ -7727,7 +7791,7 @@ function PageFilter(props) {
7727
7791
  onChange: handleSelectChange(optionKey),
7728
7792
  options: option.options,
7729
7793
  placeholder: "\uC120\uD0DD",
7730
- canReset: true,
7794
+ canReset: option.canReset ?? true,
7731
7795
  containerClassName: option.containerClassName ?? "w-30"
7732
7796
  }
7733
7797
  ) }, String(optionKey));
@@ -7878,16 +7942,24 @@ function easedFade(dir, opacity) {
7878
7942
  return `linear-gradient(${dir}, ${c(1)} 0%, ${c(0.72)} 20%, ${c(0.38)} 45%, ${c(0.1)} 70%, transparent 100%)`;
7879
7943
  }
7880
7944
  function MultiTabBar(props) {
7881
- const { tabs, activeTabId, onTabClick, onTabClose, onTabAdd, variant = "light", primaryColor } = props;
7945
+ const {
7946
+ tabs,
7947
+ activeTabId,
7948
+ onTabClick,
7949
+ onTabClose,
7950
+ onTabAdd,
7951
+ onTabsReorder,
7952
+ variant = "light",
7953
+ primaryColor
7954
+ } = props;
7882
7955
  const s = VARIANT_STYLES[variant];
7883
7956
  const scrollRef = React6.useRef(null);
7884
7957
  const tabRefs = React6.useRef(/* @__PURE__ */ new Map());
7885
7958
  const [showLeftFade, setShowLeftFade] = React6.useState(false);
7886
7959
  const [showRightFade, setShowRightFade] = React6.useState(false);
7887
- const isDragging = React6.useRef(false);
7888
- const hasDragged = React6.useRef(false);
7889
- const dragStartX = React6.useRef(0);
7890
- const dragScrollLeft = React6.useRef(0);
7960
+ const [draggingId, setDraggingId] = React6.useState(null);
7961
+ const [dropTargetId, setDropTargetId] = React6.useState(null);
7962
+ const [dropSide, setDropSide] = React6.useState("before");
7891
7963
  React6.useEffect(() => {
7892
7964
  if (!activeTabId || !scrollRef.current) return;
7893
7965
  const el = tabRefs.current.get(activeTabId);
@@ -7915,23 +7987,6 @@ function MultiTabBar(props) {
7915
7987
  if (scrollRef.current) obs.observe(scrollRef.current);
7916
7988
  return () => obs.disconnect();
7917
7989
  }, [tabs]);
7918
- const handleMouseDown = (e) => {
7919
- if (!scrollRef.current) return;
7920
- isDragging.current = true;
7921
- hasDragged.current = false;
7922
- dragStartX.current = e.pageX - scrollRef.current.getBoundingClientRect().left;
7923
- dragScrollLeft.current = scrollRef.current.scrollLeft;
7924
- };
7925
- const handleMouseMove = (e) => {
7926
- if (!isDragging.current || !scrollRef.current) return;
7927
- const x = e.pageX - scrollRef.current.getBoundingClientRect().left;
7928
- const walk = x - dragStartX.current;
7929
- if (Math.abs(walk) > 4) hasDragged.current = true;
7930
- scrollRef.current.scrollLeft = dragScrollLeft.current - walk;
7931
- };
7932
- const handleStopDrag = () => {
7933
- isDragging.current = false;
7934
- };
7935
7990
  React6.useEffect(() => {
7936
7991
  const el = scrollRef.current;
7937
7992
  if (!el) return;
@@ -7942,6 +7997,46 @@ function MultiTabBar(props) {
7942
7997
  el.addEventListener("wheel", onWheel, { passive: false });
7943
7998
  return () => el.removeEventListener("wheel", onWheel);
7944
7999
  }, []);
8000
+ const handleDragStart = (e, tabId) => {
8001
+ setDraggingId(tabId);
8002
+ e.dataTransfer.effectAllowed = "move";
8003
+ };
8004
+ const handleDragOver = (e, tabId) => {
8005
+ e.preventDefault();
8006
+ if (tabId === draggingId) return;
8007
+ e.dataTransfer.dropEffect = "move";
8008
+ const rect = e.currentTarget.getBoundingClientRect();
8009
+ setDropSide(e.clientX < rect.left + rect.width / 2 ? "before" : "after");
8010
+ setDropTargetId(tabId);
8011
+ };
8012
+ const handleDrop = (e, targetTabId) => {
8013
+ e.preventDefault();
8014
+ if (!draggingId || draggingId === targetTabId) {
8015
+ setDraggingId(null);
8016
+ setDropTargetId(null);
8017
+ return;
8018
+ }
8019
+ if (onTabsReorder) {
8020
+ const fromIdx = tabs.findIndex((t) => t.id === draggingId);
8021
+ if (fromIdx !== -1) {
8022
+ const newTabs = [...tabs];
8023
+ const moved = newTabs.splice(fromIdx, 1)[0];
8024
+ let insertIdx = newTabs.findIndex((t) => t.id === targetTabId);
8025
+ if (insertIdx !== -1) {
8026
+ if (dropSide === "after") insertIdx += 1;
8027
+ newTabs.splice(insertIdx, 0, moved);
8028
+ onTabsReorder(newTabs);
8029
+ }
8030
+ }
8031
+ }
8032
+ setDraggingId(null);
8033
+ setDropTargetId(null);
8034
+ };
8035
+ const handleDragEnd = () => {
8036
+ setDraggingId(null);
8037
+ setDropTargetId(null);
8038
+ };
8039
+ const showPlus = !!onTabAdd;
7945
8040
  if (tabs.length === 0) return null;
7946
8041
  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: [
7947
8042
  showLeftFade && /* @__PURE__ */ jsxRuntime.jsx(
@@ -7956,7 +8051,7 @@ function MultiTabBar(props) {
7956
8051
  {
7957
8052
  className: "absolute top-0 bottom-0 w-10 pointer-events-none z-10",
7958
8053
  style: {
7959
- right: PLUS_BTN_WIDTH,
8054
+ right: showPlus ? PLUS_BTN_WIDTH : 0,
7960
8055
  background: easedFade("to left", s.fadeOpacity)
7961
8056
  }
7962
8057
  }
@@ -7968,12 +8063,10 @@ function MultiTabBar(props) {
7968
8063
  className: `flex-1 min-w-0 flex items-end gap-0.5 ${s.scrollPadding} overflow-x-auto [&::-webkit-scrollbar]:hidden`,
7969
8064
  style: { scrollbarWidth: "none" },
7970
8065
  onScroll: updateFades,
7971
- onMouseDown: handleMouseDown,
7972
- onMouseMove: handleMouseMove,
7973
- onMouseUp: handleStopDrag,
7974
- onMouseLeave: handleStopDrag,
7975
8066
  children: tabs.map((tab) => {
7976
8067
  const isActive = tab.id === activeTabId;
8068
+ const isDraggingThis = tab.id === draggingId;
8069
+ const isDropTarget = dropTargetId === tab.id;
7977
8070
  return /* @__PURE__ */ jsxRuntime.jsxs(
7978
8071
  "div",
7979
8072
  {
@@ -7981,15 +8074,23 @@ function MultiTabBar(props) {
7981
8074
  if (el) tabRefs.current.set(tab.id, el);
7982
8075
  else tabRefs.current.delete(tab.id);
7983
8076
  },
8077
+ draggable: !!onTabsReorder,
7984
8078
  onClick: () => {
7985
- if (hasDragged.current) return;
7986
8079
  if (!isActive) onTabClick(tab.id);
7987
8080
  },
8081
+ onDragStart: (e) => handleDragStart(e, tab.id),
8082
+ onDragOver: (e) => handleDragOver(e, tab.id),
8083
+ onDrop: (e) => handleDrop(e, tab.id),
8084
+ onDragEnd: handleDragEnd,
7988
8085
  className: `
7989
8086
  group relative flex items-center gap-1.5 ${s.tabPx} ${s.tabPy} flex-shrink-0
7990
- ${s.rounded} border border-b-0 cursor-pointer select-none
8087
+ ${s.rounded} border border-b-0 select-none
7991
8088
  transition-colors duration-100
7992
8089
  ${isActive ? s.activeTab : s.inactiveTab}
8090
+ ${onTabsReorder ? "cursor-grab" : "cursor-pointer"}
8091
+ ${isDraggingThis ? "opacity-40" : ""}
8092
+ ${isDropTarget && dropSide === "before" ? "[box-shadow:inset_2px_0_0_0_#3b82f6]" : ""}
8093
+ ${isDropTarget && dropSide === "after" ? "[box-shadow:inset_-2px_0_0_0_#3b82f6]" : ""}
7993
8094
  `,
7994
8095
  children: [
7995
8096
  isActive && /* @__PURE__ */ jsxRuntime.jsx(
@@ -8029,7 +8130,7 @@ function MultiTabBar(props) {
8029
8130
  })
8030
8131
  }
8031
8132
  ),
8032
- onTabAdd && /* @__PURE__ */ jsxRuntime.jsx(
8133
+ showPlus && /* @__PURE__ */ jsxRuntime.jsx(
8033
8134
  "div",
8034
8135
  {
8035
8136
  className: `flex-shrink-0 flex items-center justify-center ${s.plusBtnArea}`,
@@ -9026,6 +9127,7 @@ function Sidebar(props) {
9026
9127
  footer,
9027
9128
  className,
9028
9129
  showCollapseButton = false,
9130
+ collapseButtonPlacement = "right",
9029
9131
  variant = "light",
9030
9132
  primaryColor
9031
9133
  } = props;
@@ -9112,7 +9214,7 @@ function Sidebar(props) {
9112
9214
  "button",
9113
9215
  {
9114
9216
  onClick: () => setIsCollapsed((prev) => !prev),
9115
- className: `absolute top-4 right-2 z-20 w-6.5 h-6.5 rounded-full flex items-center justify-center transition-colors cursor-pointer ${s.collapseBtn}`,
9217
+ className: `absolute top-4 z-20 w-6.5 h-6.5 rounded-full flex items-center justify-center transition-colors cursor-pointer ${s.collapseBtn} ${collapseButtonPlacement === "left" ? "left-2" : "right-2"}`,
9116
9218
  children: isCollapsed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelRightClose, { size: 18, color: primaryColor ?? s.collapseIcon }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelLeftClose, { size: 18, color: primaryColor ?? s.collapseIcon })
9117
9219
  }
9118
9220
  ),
@@ -9532,7 +9634,9 @@ function DetailModalFrame(props) {
9532
9634
  renderExtraContent,
9533
9635
  renderFooterExtra,
9534
9636
  onDirtyChange,
9535
- headerType
9637
+ headerType,
9638
+ headerIconVariant,
9639
+ headerIconSize
9536
9640
  } = props;
9537
9641
  const controller = useDetailController({ mode, onModeChange });
9538
9642
  React6.useEffect(() => {
@@ -9576,7 +9680,9 @@ function DetailModalFrame(props) {
9576
9680
  {
9577
9681
  type: isEditMode ? "edit" : headerType,
9578
9682
  title,
9579
- onClose: handleClose
9683
+ onClose: handleClose,
9684
+ variant: headerIconVariant,
9685
+ size: headerIconSize
9580
9686
  }
9581
9687
  ) : /* @__PURE__ */ jsxRuntime.jsx(ModalHeader, { className: "flex flex-row items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold text-gray-900", children: title }) }),
9582
9688
  /* @__PURE__ */ jsxRuntime.jsxs(ModalBody, { className: "flex-1 min-h-0 overflow-hidden p-0 flex flex-col", children: [