@farmzone/fz-react-ui 1.0.7 → 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 +190 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +190 -85
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/tw.css +6 -0
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -557,15 +557,23 @@ interface ModalHeaderProps {
|
|
|
557
557
|
ref?: Ref<HTMLDivElement>;
|
|
558
558
|
children: ReactNode;
|
|
559
559
|
className?: string;
|
|
560
|
+
style?: CSSProperties;
|
|
560
561
|
}
|
|
561
562
|
declare function ModalHeader(props: ModalHeaderProps): react.JSX.Element;
|
|
562
563
|
|
|
563
564
|
type ModalType = "detail" | "create" | "edit" | "search" | "info" | "delete" | "warning";
|
|
565
|
+
type ModalIconHeaderVariant = "default" | "system-light" | "system-dark";
|
|
566
|
+
type ModalIconHeaderSize = "sm" | "md";
|
|
564
567
|
interface ModalIconHeaderProps {
|
|
565
568
|
type: ModalType;
|
|
566
569
|
title: string;
|
|
567
570
|
description?: string;
|
|
568
571
|
onClose: () => void;
|
|
572
|
+
action?: ReactNode;
|
|
573
|
+
variant?: ModalIconHeaderVariant;
|
|
574
|
+
size?: ModalIconHeaderSize;
|
|
575
|
+
/** default / system-light: 그라디언트·아이콘 색을 primaryColor 기준으로 자동 적용 */
|
|
576
|
+
primaryColor?: string;
|
|
569
577
|
className?: string;
|
|
570
578
|
}
|
|
571
579
|
declare function ModalIconHeader(props: ModalIconHeaderProps): react.JSX.Element;
|
|
@@ -650,6 +658,8 @@ interface CheckboxInfo {
|
|
|
650
658
|
selectedKeys: Array<string | number>;
|
|
651
659
|
onSelectionChange: (keys: Array<string | number>) => void;
|
|
652
660
|
fixed?: boolean;
|
|
661
|
+
disabledKeys?: Array<string | number>;
|
|
662
|
+
disabledTooltip?: React.ReactNode | ((key: string | number) => React.ReactNode);
|
|
653
663
|
}
|
|
654
664
|
type DragEndResult = {
|
|
655
665
|
type: "row" | "column";
|
|
@@ -674,6 +684,8 @@ interface TableProps<T> {
|
|
|
674
684
|
onColumnDragEnd?: (result: DragEndResult) => void;
|
|
675
685
|
tooltipConfig?: "always" | "overflow" | false;
|
|
676
686
|
isDynamicHeight?: boolean;
|
|
687
|
+
showHorizontalDivider?: boolean;
|
|
688
|
+
showVerticalDivider?: boolean;
|
|
677
689
|
}
|
|
678
690
|
declare function Table<T>(props: TableProps<T>): react.JSX.Element;
|
|
679
691
|
|
|
@@ -957,6 +969,7 @@ type SelectOption<P> = BaseOption<P> & {
|
|
|
957
969
|
options: Array<SelectableOption>;
|
|
958
970
|
placeholder?: string;
|
|
959
971
|
containerClassName?: string;
|
|
972
|
+
canReset?: boolean;
|
|
960
973
|
};
|
|
961
974
|
type RadioOption<P> = BaseOption<P> & {
|
|
962
975
|
type: "radio";
|
|
@@ -1045,6 +1058,7 @@ interface MultiTabBarProps {
|
|
|
1045
1058
|
onTabClick: (tabId: string) => void;
|
|
1046
1059
|
onTabClose: (tabId: string) => void;
|
|
1047
1060
|
onTabAdd?: () => void;
|
|
1061
|
+
onTabsReorder?: (tabs: Array<MultiTab>) => void;
|
|
1048
1062
|
variant?: MultiTabVariant;
|
|
1049
1063
|
primaryColor?: string;
|
|
1050
1064
|
}
|
|
@@ -1144,6 +1158,7 @@ interface SidebarProps {
|
|
|
1144
1158
|
footer?: ReactNode;
|
|
1145
1159
|
className?: string;
|
|
1146
1160
|
showCollapseButton?: boolean;
|
|
1161
|
+
collapseButtonPlacement?: "left" | "right";
|
|
1147
1162
|
onClose?: () => void;
|
|
1148
1163
|
variant?: SidebarVariant;
|
|
1149
1164
|
primaryColor?: string;
|
|
@@ -1241,6 +1256,10 @@ interface DetailModalFrameProps<T, S extends z.ZodTypeAny = z.ZodTypeAny> {
|
|
|
1241
1256
|
onDirtyChange?: (isDirty: boolean) => void;
|
|
1242
1257
|
/** 제공 시 ModalIconHeader로 렌더링 (read 모드: 이 type, edit 모드: "edit" 자동 적용) */
|
|
1243
1258
|
headerType?: ModalType;
|
|
1259
|
+
/** ModalIconHeader 스타일 변형 */
|
|
1260
|
+
headerIconVariant?: ModalIconHeaderVariant;
|
|
1261
|
+
/** ModalIconHeader 사이즈 */
|
|
1262
|
+
headerIconSize?: ModalIconHeaderSize;
|
|
1244
1263
|
}
|
|
1245
1264
|
declare function DetailModalFrame<T, S extends z.ZodTypeAny = z.ZodTypeAny>(props: DetailModalFrameProps<T, S>): react.JSX.Element;
|
|
1246
1265
|
|
package/dist/index.d.ts
CHANGED
|
@@ -557,15 +557,23 @@ interface ModalHeaderProps {
|
|
|
557
557
|
ref?: Ref<HTMLDivElement>;
|
|
558
558
|
children: ReactNode;
|
|
559
559
|
className?: string;
|
|
560
|
+
style?: CSSProperties;
|
|
560
561
|
}
|
|
561
562
|
declare function ModalHeader(props: ModalHeaderProps): react.JSX.Element;
|
|
562
563
|
|
|
563
564
|
type ModalType = "detail" | "create" | "edit" | "search" | "info" | "delete" | "warning";
|
|
565
|
+
type ModalIconHeaderVariant = "default" | "system-light" | "system-dark";
|
|
566
|
+
type ModalIconHeaderSize = "sm" | "md";
|
|
564
567
|
interface ModalIconHeaderProps {
|
|
565
568
|
type: ModalType;
|
|
566
569
|
title: string;
|
|
567
570
|
description?: string;
|
|
568
571
|
onClose: () => void;
|
|
572
|
+
action?: ReactNode;
|
|
573
|
+
variant?: ModalIconHeaderVariant;
|
|
574
|
+
size?: ModalIconHeaderSize;
|
|
575
|
+
/** default / system-light: 그라디언트·아이콘 색을 primaryColor 기준으로 자동 적용 */
|
|
576
|
+
primaryColor?: string;
|
|
569
577
|
className?: string;
|
|
570
578
|
}
|
|
571
579
|
declare function ModalIconHeader(props: ModalIconHeaderProps): react.JSX.Element;
|
|
@@ -650,6 +658,8 @@ interface CheckboxInfo {
|
|
|
650
658
|
selectedKeys: Array<string | number>;
|
|
651
659
|
onSelectionChange: (keys: Array<string | number>) => void;
|
|
652
660
|
fixed?: boolean;
|
|
661
|
+
disabledKeys?: Array<string | number>;
|
|
662
|
+
disabledTooltip?: React.ReactNode | ((key: string | number) => React.ReactNode);
|
|
653
663
|
}
|
|
654
664
|
type DragEndResult = {
|
|
655
665
|
type: "row" | "column";
|
|
@@ -674,6 +684,8 @@ interface TableProps<T> {
|
|
|
674
684
|
onColumnDragEnd?: (result: DragEndResult) => void;
|
|
675
685
|
tooltipConfig?: "always" | "overflow" | false;
|
|
676
686
|
isDynamicHeight?: boolean;
|
|
687
|
+
showHorizontalDivider?: boolean;
|
|
688
|
+
showVerticalDivider?: boolean;
|
|
677
689
|
}
|
|
678
690
|
declare function Table<T>(props: TableProps<T>): react.JSX.Element;
|
|
679
691
|
|
|
@@ -957,6 +969,7 @@ type SelectOption<P> = BaseOption<P> & {
|
|
|
957
969
|
options: Array<SelectableOption>;
|
|
958
970
|
placeholder?: string;
|
|
959
971
|
containerClassName?: string;
|
|
972
|
+
canReset?: boolean;
|
|
960
973
|
};
|
|
961
974
|
type RadioOption<P> = BaseOption<P> & {
|
|
962
975
|
type: "radio";
|
|
@@ -1045,6 +1058,7 @@ interface MultiTabBarProps {
|
|
|
1045
1058
|
onTabClick: (tabId: string) => void;
|
|
1046
1059
|
onTabClose: (tabId: string) => void;
|
|
1047
1060
|
onTabAdd?: () => void;
|
|
1061
|
+
onTabsReorder?: (tabs: Array<MultiTab>) => void;
|
|
1048
1062
|
variant?: MultiTabVariant;
|
|
1049
1063
|
primaryColor?: string;
|
|
1050
1064
|
}
|
|
@@ -1144,6 +1158,7 @@ interface SidebarProps {
|
|
|
1144
1158
|
footer?: ReactNode;
|
|
1145
1159
|
className?: string;
|
|
1146
1160
|
showCollapseButton?: boolean;
|
|
1161
|
+
collapseButtonPlacement?: "left" | "right";
|
|
1147
1162
|
onClose?: () => void;
|
|
1148
1163
|
variant?: SidebarVariant;
|
|
1149
1164
|
primaryColor?: string;
|
|
@@ -1241,6 +1256,10 @@ interface DetailModalFrameProps<T, S extends z.ZodTypeAny = z.ZodTypeAny> {
|
|
|
1241
1256
|
onDirtyChange?: (isDirty: boolean) => void;
|
|
1242
1257
|
/** 제공 시 ModalIconHeader로 렌더링 (read 모드: 이 type, edit 모드: "edit" 자동 적용) */
|
|
1243
1258
|
headerType?: ModalType;
|
|
1259
|
+
/** ModalIconHeader 스타일 변형 */
|
|
1260
|
+
headerIconVariant?: ModalIconHeaderVariant;
|
|
1261
|
+
/** ModalIconHeader 사이즈 */
|
|
1262
|
+
headerIconSize?: ModalIconHeaderSize;
|
|
1244
1263
|
}
|
|
1245
1264
|
declare function DetailModalFrame<T, S extends z.ZodTypeAny = z.ZodTypeAny>(props: DetailModalFrameProps<T, S>): react.JSX.Element;
|
|
1246
1265
|
|
package/dist/index.js
CHANGED
|
@@ -465,12 +465,10 @@ function Input2(props) {
|
|
|
465
465
|
const actualType = type === "password" && showPassword ? "text" : type;
|
|
466
466
|
const handlePasswordToggle = () => setShowPassword((prev) => !prev);
|
|
467
467
|
const finalRightIcon = rightIcon || (shouldShowPasswordToggle ? /* @__PURE__ */ jsx(
|
|
468
|
-
"
|
|
468
|
+
"div",
|
|
469
469
|
{
|
|
470
|
-
type: "button",
|
|
471
470
|
onClick: handlePasswordToggle,
|
|
472
471
|
className: "text-muted-foreground hover:text-foreground cursor-pointer transition-colors",
|
|
473
|
-
disabled,
|
|
474
472
|
children: showPassword ? /* @__PURE__ */ jsx(EyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Eye, { className: "h-4 w-4" })
|
|
475
473
|
}
|
|
476
474
|
) : null);
|
|
@@ -3446,36 +3444,100 @@ var MODAL_ICONS = {
|
|
|
3446
3444
|
delete: Trash2,
|
|
3447
3445
|
warning: TriangleAlert
|
|
3448
3446
|
};
|
|
3447
|
+
var VARIANT_CONFIG = {
|
|
3448
|
+
default: {
|
|
3449
|
+
container: "bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-100",
|
|
3450
|
+
iconBg: "bg-blue-100 rounded-full",
|
|
3451
|
+
iconColor: "text-main",
|
|
3452
|
+
titleColor: "text-gray-900",
|
|
3453
|
+
descColor: "text-gray-500",
|
|
3454
|
+
closeBtn: "text-gray-400 group-hover:text-gray-600"
|
|
3455
|
+
},
|
|
3456
|
+
"system-light": {
|
|
3457
|
+
container: "bg-gray-50 border-gray-200",
|
|
3458
|
+
iconBg: "bg-white border border-gray-200 shadow-sm rounded-lg",
|
|
3459
|
+
iconColor: "text-slate-600",
|
|
3460
|
+
titleColor: "text-gray-900",
|
|
3461
|
+
descColor: "text-gray-500",
|
|
3462
|
+
closeBtn: "text-gray-400 group-hover:text-gray-600"
|
|
3463
|
+
},
|
|
3464
|
+
"system-dark": {
|
|
3465
|
+
container: "bg-slate-800 border-slate-700",
|
|
3466
|
+
iconBg: "bg-slate-700 border border-slate-600 shadow-sm rounded-lg",
|
|
3467
|
+
iconColor: "text-slate-300",
|
|
3468
|
+
titleColor: "text-slate-100",
|
|
3469
|
+
descColor: "text-slate-400",
|
|
3470
|
+
closeBtn: "text-slate-500 group-hover:text-slate-300"
|
|
3471
|
+
}
|
|
3472
|
+
};
|
|
3473
|
+
var SIZE_CONFIG = {
|
|
3474
|
+
md: {
|
|
3475
|
+
padding: "px-6 py-5",
|
|
3476
|
+
iconBox: "w-10 h-10",
|
|
3477
|
+
iconSize: 20,
|
|
3478
|
+
gap: "gap-3",
|
|
3479
|
+
titleSize: "text-xl font-bold",
|
|
3480
|
+
descSize: "text-sm mt-0.5",
|
|
3481
|
+
closeBtnBox: "w-10 h-10",
|
|
3482
|
+
closeIconSize: 20
|
|
3483
|
+
},
|
|
3484
|
+
sm: {
|
|
3485
|
+
padding: "px-4 py-4",
|
|
3486
|
+
iconBox: "w-8 h-8",
|
|
3487
|
+
iconSize: 16,
|
|
3488
|
+
gap: "gap-2.5",
|
|
3489
|
+
titleSize: "text-lg font-bold",
|
|
3490
|
+
descSize: "text-sm mt-0.5",
|
|
3491
|
+
closeBtnBox: "w-8 h-8",
|
|
3492
|
+
closeIconSize: 16
|
|
3493
|
+
}
|
|
3494
|
+
};
|
|
3449
3495
|
function ModalIconHeader(props) {
|
|
3450
|
-
const {
|
|
3496
|
+
const {
|
|
3497
|
+
type,
|
|
3498
|
+
title,
|
|
3499
|
+
description,
|
|
3500
|
+
onClose,
|
|
3501
|
+
action,
|
|
3502
|
+
variant = "default",
|
|
3503
|
+
size: size2 = "md",
|
|
3504
|
+
primaryColor,
|
|
3505
|
+
className
|
|
3506
|
+
} = props;
|
|
3451
3507
|
const Icon2 = MODAL_ICONS[type];
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
type: "button",
|
|
3471
|
-
onClick: onClose,
|
|
3472
|
-
className: "flex items-center justify-center w-10 h-10 rounded-full transition-colors group cursor-pointer hover:bg-black/5 shrink-0",
|
|
3473
|
-
children: /* @__PURE__ */ jsx(X, { size: 20, className: "text-gray-400 group-hover:text-gray-600 transition-colors" })
|
|
3474
|
-
}
|
|
3475
|
-
)
|
|
3508
|
+
const v = VARIANT_CONFIG[variant];
|
|
3509
|
+
const s = SIZE_CONFIG[size2];
|
|
3510
|
+
const hasColor = !!primaryColor;
|
|
3511
|
+
const isDark = variant === "system-dark";
|
|
3512
|
+
const containerStyle = hasColor && variant === "default" ? {
|
|
3513
|
+
background: `linear-gradient(to right, color-mix(in srgb, ${primaryColor} 6%, white), color-mix(in srgb, ${primaryColor} 12%, white))`,
|
|
3514
|
+
borderColor: `color-mix(in srgb, ${primaryColor} 25%, transparent)`
|
|
3515
|
+
} : void 0;
|
|
3516
|
+
const iconBgStyle = hasColor ? {
|
|
3517
|
+
backgroundColor: isDark ? `color-mix(in srgb, ${primaryColor} 20%, #334155)` : `color-mix(in srgb, ${primaryColor} 15%, white)`
|
|
3518
|
+
} : void 0;
|
|
3519
|
+
const iconColorStyle = hasColor && !isDark ? { color: primaryColor } : void 0;
|
|
3520
|
+
return /* @__PURE__ */ jsx(ModalHeader, { className: cn("p-0 text-left space-y-0", v.container, className), style: containerStyle, children: /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between", s.padding), children: [
|
|
3521
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex items-center", s.gap), children: [
|
|
3522
|
+
/* @__PURE__ */ jsx("div", { className: cn("flex items-center justify-center shrink-0", s.iconBox, v.iconBg), style: iconBgStyle, children: /* @__PURE__ */ jsx(Icon2, { size: s.iconSize, className: hasColor && !isDark ? void 0 : v.iconColor, style: iconColorStyle }) }),
|
|
3523
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
3524
|
+
/* @__PURE__ */ jsx("h2", { className: cn(s.titleSize, v.titleColor), children: title }),
|
|
3525
|
+
description && /* @__PURE__ */ jsx("p", { className: cn(s.descSize, v.descColor), children: description })
|
|
3476
3526
|
] })
|
|
3477
|
-
}
|
|
3478
|
-
|
|
3527
|
+
] }),
|
|
3528
|
+
action ?? /* @__PURE__ */ jsx(
|
|
3529
|
+
"button",
|
|
3530
|
+
{
|
|
3531
|
+
type: "button",
|
|
3532
|
+
onClick: onClose,
|
|
3533
|
+
className: cn(
|
|
3534
|
+
"flex items-center justify-center rounded-full transition-colors group cursor-pointer hover:bg-black/5 shrink-0",
|
|
3535
|
+
s.closeBtnBox
|
|
3536
|
+
),
|
|
3537
|
+
children: /* @__PURE__ */ jsx(X, { size: s.closeIconSize, className: cn("transition-colors", v.closeBtn) })
|
|
3538
|
+
}
|
|
3539
|
+
)
|
|
3540
|
+
] }) });
|
|
3479
3541
|
}
|
|
3480
3542
|
function Modal(props) {
|
|
3481
3543
|
const {
|
|
@@ -3929,8 +3991,6 @@ function ArrowIcon(props) {
|
|
|
3929
3991
|
}
|
|
3930
3992
|
);
|
|
3931
3993
|
}
|
|
3932
|
-
var DEFAULT_SORT_KEY = "";
|
|
3933
|
-
var DEFAULT_SORT_ORDER = "asc";
|
|
3934
3994
|
var DEFAULT_SORT_OPTIONS = {
|
|
3935
3995
|
visibleType: "always",
|
|
3936
3996
|
sortKey: "",
|
|
@@ -3979,27 +4039,18 @@ function TableHeader(props) {
|
|
|
3979
4039
|
if (onSortChange) {
|
|
3980
4040
|
const isActive = sortOption?.sortKey === columnKey;
|
|
3981
4041
|
const currentOrder = sortOption?.sortOrder;
|
|
3982
|
-
if (isActive) {
|
|
3983
|
-
|
|
3984
|
-
onSortChange(columnKey, "asc");
|
|
3985
|
-
} else if (currentOrder === "asc") {
|
|
3986
|
-
onSortChange(DEFAULT_SORT_KEY, DEFAULT_SORT_ORDER);
|
|
3987
|
-
} else {
|
|
3988
|
-
onSortChange(columnKey, "desc");
|
|
3989
|
-
}
|
|
4042
|
+
if (isActive && currentOrder === "desc") {
|
|
4043
|
+
onSortChange(columnKey, "asc");
|
|
3990
4044
|
} else {
|
|
3991
4045
|
onSortChange(columnKey, "desc");
|
|
3992
4046
|
}
|
|
3993
4047
|
return;
|
|
3994
4048
|
}
|
|
3995
4049
|
setInternalSortState((prev) => {
|
|
3996
|
-
if (prev.columnKey
|
|
3997
|
-
return { columnKey, order: "desc" };
|
|
3998
|
-
}
|
|
3999
|
-
if (prev.order === "desc") {
|
|
4050
|
+
if (prev.columnKey === columnKey && prev.order === "desc") {
|
|
4000
4051
|
return { columnKey, order: "asc" };
|
|
4001
4052
|
}
|
|
4002
|
-
return { columnKey
|
|
4053
|
+
return { columnKey, order: "desc" };
|
|
4003
4054
|
});
|
|
4004
4055
|
};
|
|
4005
4056
|
const renderSortIcon = (columnKey) => {
|
|
@@ -4635,9 +4686,11 @@ function Table(props) {
|
|
|
4635
4686
|
paginationInfo,
|
|
4636
4687
|
renderFooter,
|
|
4637
4688
|
tooltipConfig = "overflow",
|
|
4638
|
-
isDynamicHeight = true
|
|
4689
|
+
isDynamicHeight = true,
|
|
4690
|
+
showHorizontalDivider = true,
|
|
4691
|
+
showVerticalDivider = true
|
|
4639
4692
|
} = props;
|
|
4640
|
-
const { fixed = false, onSelectionChange, selectedKeys = [] } = checkboxInfo ?? {};
|
|
4693
|
+
const { fixed = false, onSelectionChange, selectedKeys = [], disabledKeys = [], disabledTooltip } = checkboxInfo ?? {};
|
|
4641
4694
|
const scrollRef = useRef(null);
|
|
4642
4695
|
const footerRef = useRef(null);
|
|
4643
4696
|
const [tableHeight, setTableHeight] = useState(500);
|
|
@@ -4679,11 +4732,14 @@ function Table(props) {
|
|
|
4679
4732
|
const footerH = footerRef.current?.getBoundingClientRect().height ?? 0;
|
|
4680
4733
|
setTableHeight(window.innerHeight - top - footerH - 24);
|
|
4681
4734
|
}, [showSkeleton, showEmpty]);
|
|
4682
|
-
const isAllSelected = data.length > 0 && selectedKeys.length === data.length;
|
|
4683
4735
|
const selectedKeysRef = useRef(selectedKeys);
|
|
4684
4736
|
selectedKeysRef.current = selectedKeys;
|
|
4685
4737
|
const onSelectionChangeRef = useRef(onSelectionChange);
|
|
4686
4738
|
onSelectionChangeRef.current = onSelectionChange;
|
|
4739
|
+
const disabledKeysRef = useRef(disabledKeys);
|
|
4740
|
+
disabledKeysRef.current = disabledKeys;
|
|
4741
|
+
const disabledTooltipRef = useRef(disabledTooltip);
|
|
4742
|
+
disabledTooltipRef.current = disabledTooltip;
|
|
4687
4743
|
const dataRef = useRef(data);
|
|
4688
4744
|
dataRef.current = data;
|
|
4689
4745
|
const onRowClickRef = useRef(onRowClick);
|
|
@@ -4698,6 +4754,8 @@ function Table(props) {
|
|
|
4698
4754
|
},
|
|
4699
4755
|
[rowKey]
|
|
4700
4756
|
);
|
|
4757
|
+
const selectableKeys = data.map((record, index) => getRowKey(record, index)).filter((key) => !disabledKeys.includes(key));
|
|
4758
|
+
const isAllSelected = selectableKeys.length > 0 && selectableKeys.every((key) => selectedKeys.includes(key));
|
|
4701
4759
|
const allColumns = useMemo(() => {
|
|
4702
4760
|
const checkboxCol = {
|
|
4703
4761
|
key: "__checkbox__",
|
|
@@ -4712,19 +4770,24 @@ function Table(props) {
|
|
|
4712
4770
|
checked: isAllSelected,
|
|
4713
4771
|
onChange: () => {
|
|
4714
4772
|
if (!onSelectionChangeRef.current) return;
|
|
4715
|
-
const allKeys = dataRef.current.map((record, index) => getRowKey(record, index));
|
|
4773
|
+
const allKeys = dataRef.current.map((record, index) => getRowKey(record, index)).filter((key) => !disabledKeysRef.current.includes(key));
|
|
4716
4774
|
onSelectionChangeRef.current(isAllSelected ? [] : allKeys);
|
|
4717
4775
|
}
|
|
4718
4776
|
}
|
|
4719
4777
|
),
|
|
4720
4778
|
render: (_value, record, rowIdx) => {
|
|
4721
4779
|
const key = getRowKey(record, rowIdx);
|
|
4780
|
+
const isDisabled = disabledKeysRef.current.includes(key);
|
|
4781
|
+
const tooltip = disabledTooltipRef.current;
|
|
4782
|
+
const tooltipContent = isDisabled && tooltip ? typeof tooltip === "function" ? tooltip(key) : tooltip : void 0;
|
|
4722
4783
|
return /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
4723
4784
|
Checkbox,
|
|
4724
4785
|
{
|
|
4725
4786
|
checked: selectedKeysRef.current.includes(key),
|
|
4787
|
+
disabled: isDisabled,
|
|
4788
|
+
tooltipConfig: tooltipContent ? { content: tooltipContent, position: "bottom" } : void 0,
|
|
4726
4789
|
onChange: () => {
|
|
4727
|
-
if (!onSelectionChangeRef.current) return;
|
|
4790
|
+
if (!onSelectionChangeRef.current || isDisabled) return;
|
|
4728
4791
|
const cur = selectedKeysRef.current;
|
|
4729
4792
|
const next = cur.includes(key) ? cur.filter((k) => k !== key) : [...cur, key];
|
|
4730
4793
|
onSelectionChangeRef.current(next);
|
|
@@ -4985,7 +5048,7 @@ function Table(props) {
|
|
|
4985
5048
|
id: `${col.key}-body`,
|
|
4986
5049
|
"data-row-cell": "true",
|
|
4987
5050
|
"data-col-key": col.key,
|
|
4988
|
-
className: `${isCheckbox ? "p-0" : "p-3"} whitespace-nowrap text-sm text-gray-900 border-
|
|
5051
|
+
className: `${isCheckbox ? "p-0" : "p-3"} whitespace-nowrap text-sm text-gray-900 border-gray-300/70 last:border-r-0 bg-inherit group-hover:bg-gray-100 transition-all ${showVerticalDivider ? "border-r" : ""} ${showHorizontalDivider ? "border-b" : ""}`,
|
|
4989
5052
|
style: {
|
|
4990
5053
|
width,
|
|
4991
5054
|
minWidth: minWidth || width,
|
|
@@ -7694,7 +7757,7 @@ function PageFilter(props) {
|
|
|
7694
7757
|
onChange: handleSelectChange(optionKey),
|
|
7695
7758
|
options: option.options,
|
|
7696
7759
|
placeholder: "\uC120\uD0DD",
|
|
7697
|
-
canReset: true,
|
|
7760
|
+
canReset: option.canReset ?? true,
|
|
7698
7761
|
containerClassName: option.containerClassName ?? "w-30"
|
|
7699
7762
|
}
|
|
7700
7763
|
) }, String(optionKey));
|
|
@@ -7845,16 +7908,24 @@ function easedFade(dir, opacity) {
|
|
|
7845
7908
|
return `linear-gradient(${dir}, ${c(1)} 0%, ${c(0.72)} 20%, ${c(0.38)} 45%, ${c(0.1)} 70%, transparent 100%)`;
|
|
7846
7909
|
}
|
|
7847
7910
|
function MultiTabBar(props) {
|
|
7848
|
-
const {
|
|
7911
|
+
const {
|
|
7912
|
+
tabs,
|
|
7913
|
+
activeTabId,
|
|
7914
|
+
onTabClick,
|
|
7915
|
+
onTabClose,
|
|
7916
|
+
onTabAdd,
|
|
7917
|
+
onTabsReorder,
|
|
7918
|
+
variant = "light",
|
|
7919
|
+
primaryColor
|
|
7920
|
+
} = props;
|
|
7849
7921
|
const s = VARIANT_STYLES[variant];
|
|
7850
7922
|
const scrollRef = useRef(null);
|
|
7851
7923
|
const tabRefs = useRef(/* @__PURE__ */ new Map());
|
|
7852
7924
|
const [showLeftFade, setShowLeftFade] = useState(false);
|
|
7853
7925
|
const [showRightFade, setShowRightFade] = useState(false);
|
|
7854
|
-
const
|
|
7855
|
-
const
|
|
7856
|
-
const
|
|
7857
|
-
const dragScrollLeft = useRef(0);
|
|
7926
|
+
const [draggingId, setDraggingId] = useState(null);
|
|
7927
|
+
const [dropTargetId, setDropTargetId] = useState(null);
|
|
7928
|
+
const [dropSide, setDropSide] = useState("before");
|
|
7858
7929
|
useEffect(() => {
|
|
7859
7930
|
if (!activeTabId || !scrollRef.current) return;
|
|
7860
7931
|
const el = tabRefs.current.get(activeTabId);
|
|
@@ -7882,23 +7953,6 @@ function MultiTabBar(props) {
|
|
|
7882
7953
|
if (scrollRef.current) obs.observe(scrollRef.current);
|
|
7883
7954
|
return () => obs.disconnect();
|
|
7884
7955
|
}, [tabs]);
|
|
7885
|
-
const handleMouseDown = (e) => {
|
|
7886
|
-
if (!scrollRef.current) return;
|
|
7887
|
-
isDragging.current = true;
|
|
7888
|
-
hasDragged.current = false;
|
|
7889
|
-
dragStartX.current = e.pageX - scrollRef.current.getBoundingClientRect().left;
|
|
7890
|
-
dragScrollLeft.current = scrollRef.current.scrollLeft;
|
|
7891
|
-
};
|
|
7892
|
-
const handleMouseMove = (e) => {
|
|
7893
|
-
if (!isDragging.current || !scrollRef.current) return;
|
|
7894
|
-
const x = e.pageX - scrollRef.current.getBoundingClientRect().left;
|
|
7895
|
-
const walk = x - dragStartX.current;
|
|
7896
|
-
if (Math.abs(walk) > 4) hasDragged.current = true;
|
|
7897
|
-
scrollRef.current.scrollLeft = dragScrollLeft.current - walk;
|
|
7898
|
-
};
|
|
7899
|
-
const handleStopDrag = () => {
|
|
7900
|
-
isDragging.current = false;
|
|
7901
|
-
};
|
|
7902
7956
|
useEffect(() => {
|
|
7903
7957
|
const el = scrollRef.current;
|
|
7904
7958
|
if (!el) return;
|
|
@@ -7909,6 +7963,46 @@ function MultiTabBar(props) {
|
|
|
7909
7963
|
el.addEventListener("wheel", onWheel, { passive: false });
|
|
7910
7964
|
return () => el.removeEventListener("wheel", onWheel);
|
|
7911
7965
|
}, []);
|
|
7966
|
+
const handleDragStart = (e, tabId) => {
|
|
7967
|
+
setDraggingId(tabId);
|
|
7968
|
+
e.dataTransfer.effectAllowed = "move";
|
|
7969
|
+
};
|
|
7970
|
+
const handleDragOver = (e, tabId) => {
|
|
7971
|
+
e.preventDefault();
|
|
7972
|
+
if (tabId === draggingId) return;
|
|
7973
|
+
e.dataTransfer.dropEffect = "move";
|
|
7974
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
7975
|
+
setDropSide(e.clientX < rect.left + rect.width / 2 ? "before" : "after");
|
|
7976
|
+
setDropTargetId(tabId);
|
|
7977
|
+
};
|
|
7978
|
+
const handleDrop = (e, targetTabId) => {
|
|
7979
|
+
e.preventDefault();
|
|
7980
|
+
if (!draggingId || draggingId === targetTabId) {
|
|
7981
|
+
setDraggingId(null);
|
|
7982
|
+
setDropTargetId(null);
|
|
7983
|
+
return;
|
|
7984
|
+
}
|
|
7985
|
+
if (onTabsReorder) {
|
|
7986
|
+
const fromIdx = tabs.findIndex((t) => t.id === draggingId);
|
|
7987
|
+
if (fromIdx !== -1) {
|
|
7988
|
+
const newTabs = [...tabs];
|
|
7989
|
+
const moved = newTabs.splice(fromIdx, 1)[0];
|
|
7990
|
+
let insertIdx = newTabs.findIndex((t) => t.id === targetTabId);
|
|
7991
|
+
if (insertIdx !== -1) {
|
|
7992
|
+
if (dropSide === "after") insertIdx += 1;
|
|
7993
|
+
newTabs.splice(insertIdx, 0, moved);
|
|
7994
|
+
onTabsReorder(newTabs);
|
|
7995
|
+
}
|
|
7996
|
+
}
|
|
7997
|
+
}
|
|
7998
|
+
setDraggingId(null);
|
|
7999
|
+
setDropTargetId(null);
|
|
8000
|
+
};
|
|
8001
|
+
const handleDragEnd = () => {
|
|
8002
|
+
setDraggingId(null);
|
|
8003
|
+
setDropTargetId(null);
|
|
8004
|
+
};
|
|
8005
|
+
const showPlus = !!onTabAdd;
|
|
7912
8006
|
if (tabs.length === 0) return null;
|
|
7913
8007
|
return /* @__PURE__ */ jsx("div", { className: `flex-shrink-0 w-full overflow-hidden ${s.container}`, children: /* @__PURE__ */ jsxs("div", { className: "relative flex items-stretch", children: [
|
|
7914
8008
|
showLeftFade && /* @__PURE__ */ jsx(
|
|
@@ -7923,7 +8017,7 @@ function MultiTabBar(props) {
|
|
|
7923
8017
|
{
|
|
7924
8018
|
className: "absolute top-0 bottom-0 w-10 pointer-events-none z-10",
|
|
7925
8019
|
style: {
|
|
7926
|
-
right: PLUS_BTN_WIDTH,
|
|
8020
|
+
right: showPlus ? PLUS_BTN_WIDTH : 0,
|
|
7927
8021
|
background: easedFade("to left", s.fadeOpacity)
|
|
7928
8022
|
}
|
|
7929
8023
|
}
|
|
@@ -7935,12 +8029,10 @@ function MultiTabBar(props) {
|
|
|
7935
8029
|
className: `flex-1 min-w-0 flex items-end gap-0.5 ${s.scrollPadding} overflow-x-auto [&::-webkit-scrollbar]:hidden`,
|
|
7936
8030
|
style: { scrollbarWidth: "none" },
|
|
7937
8031
|
onScroll: updateFades,
|
|
7938
|
-
onMouseDown: handleMouseDown,
|
|
7939
|
-
onMouseMove: handleMouseMove,
|
|
7940
|
-
onMouseUp: handleStopDrag,
|
|
7941
|
-
onMouseLeave: handleStopDrag,
|
|
7942
8032
|
children: tabs.map((tab) => {
|
|
7943
8033
|
const isActive = tab.id === activeTabId;
|
|
8034
|
+
const isDraggingThis = tab.id === draggingId;
|
|
8035
|
+
const isDropTarget = dropTargetId === tab.id;
|
|
7944
8036
|
return /* @__PURE__ */ jsxs(
|
|
7945
8037
|
"div",
|
|
7946
8038
|
{
|
|
@@ -7948,15 +8040,23 @@ function MultiTabBar(props) {
|
|
|
7948
8040
|
if (el) tabRefs.current.set(tab.id, el);
|
|
7949
8041
|
else tabRefs.current.delete(tab.id);
|
|
7950
8042
|
},
|
|
8043
|
+
draggable: !!onTabsReorder,
|
|
7951
8044
|
onClick: () => {
|
|
7952
|
-
if (hasDragged.current) return;
|
|
7953
8045
|
if (!isActive) onTabClick(tab.id);
|
|
7954
8046
|
},
|
|
8047
|
+
onDragStart: (e) => handleDragStart(e, tab.id),
|
|
8048
|
+
onDragOver: (e) => handleDragOver(e, tab.id),
|
|
8049
|
+
onDrop: (e) => handleDrop(e, tab.id),
|
|
8050
|
+
onDragEnd: handleDragEnd,
|
|
7955
8051
|
className: `
|
|
7956
8052
|
group relative flex items-center gap-1.5 ${s.tabPx} ${s.tabPy} flex-shrink-0
|
|
7957
|
-
${s.rounded} border border-b-0
|
|
8053
|
+
${s.rounded} border border-b-0 select-none
|
|
7958
8054
|
transition-colors duration-100
|
|
7959
8055
|
${isActive ? s.activeTab : s.inactiveTab}
|
|
8056
|
+
${onTabsReorder ? "cursor-grab" : "cursor-pointer"}
|
|
8057
|
+
${isDraggingThis ? "opacity-40" : ""}
|
|
8058
|
+
${isDropTarget && dropSide === "before" ? "[box-shadow:inset_2px_0_0_0_#3b82f6]" : ""}
|
|
8059
|
+
${isDropTarget && dropSide === "after" ? "[box-shadow:inset_-2px_0_0_0_#3b82f6]" : ""}
|
|
7960
8060
|
`,
|
|
7961
8061
|
children: [
|
|
7962
8062
|
isActive && /* @__PURE__ */ jsx(
|
|
@@ -7996,7 +8096,7 @@ function MultiTabBar(props) {
|
|
|
7996
8096
|
})
|
|
7997
8097
|
}
|
|
7998
8098
|
),
|
|
7999
|
-
|
|
8099
|
+
showPlus && /* @__PURE__ */ jsx(
|
|
8000
8100
|
"div",
|
|
8001
8101
|
{
|
|
8002
8102
|
className: `flex-shrink-0 flex items-center justify-center ${s.plusBtnArea}`,
|
|
@@ -8993,6 +9093,7 @@ function Sidebar(props) {
|
|
|
8993
9093
|
footer,
|
|
8994
9094
|
className,
|
|
8995
9095
|
showCollapseButton = false,
|
|
9096
|
+
collapseButtonPlacement = "right",
|
|
8996
9097
|
variant = "light",
|
|
8997
9098
|
primaryColor
|
|
8998
9099
|
} = props;
|
|
@@ -9079,7 +9180,7 @@ function Sidebar(props) {
|
|
|
9079
9180
|
"button",
|
|
9080
9181
|
{
|
|
9081
9182
|
onClick: () => setIsCollapsed((prev) => !prev),
|
|
9082
|
-
className: `absolute top-4
|
|
9183
|
+
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"}`,
|
|
9083
9184
|
children: isCollapsed ? /* @__PURE__ */ jsx(PanelRightClose, { size: 18, color: primaryColor ?? s.collapseIcon }) : /* @__PURE__ */ jsx(PanelLeftClose, { size: 18, color: primaryColor ?? s.collapseIcon })
|
|
9084
9185
|
}
|
|
9085
9186
|
),
|
|
@@ -9499,7 +9600,9 @@ function DetailModalFrame(props) {
|
|
|
9499
9600
|
renderExtraContent,
|
|
9500
9601
|
renderFooterExtra,
|
|
9501
9602
|
onDirtyChange,
|
|
9502
|
-
headerType
|
|
9603
|
+
headerType,
|
|
9604
|
+
headerIconVariant,
|
|
9605
|
+
headerIconSize
|
|
9503
9606
|
} = props;
|
|
9504
9607
|
const controller = useDetailController({ mode, onModeChange });
|
|
9505
9608
|
useEffect(() => {
|
|
@@ -9543,7 +9646,9 @@ function DetailModalFrame(props) {
|
|
|
9543
9646
|
{
|
|
9544
9647
|
type: isEditMode ? "edit" : headerType,
|
|
9545
9648
|
title,
|
|
9546
|
-
onClose: handleClose
|
|
9649
|
+
onClose: handleClose,
|
|
9650
|
+
variant: headerIconVariant,
|
|
9651
|
+
size: headerIconSize
|
|
9547
9652
|
}
|
|
9548
9653
|
) : /* @__PURE__ */ jsx(ModalHeader, { className: "flex flex-row items-center justify-between", children: /* @__PURE__ */ jsx("span", { className: "text-base font-semibold text-gray-900", children: title }) }),
|
|
9549
9654
|
/* @__PURE__ */ jsxs(ModalBody, { className: "flex-1 min-h-0 overflow-hidden p-0 flex flex-col", children: [
|