@ceed/cds 1.7.3-next.1 → 1.7.4-next.1

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.
@@ -140,6 +140,13 @@ export type DataTableProps<T extends Record<PropertyKey, any>, GetId extends ((r
140
140
  * MUI에는 없는 인터페이스지만 Total Select 기능이 추가되었기 때문에 추가해야했다.
141
141
  */
142
142
  isTotalSelected?: boolean) => void;
143
+ /**
144
+ * row별로 선택 가능 여부를 결정하는 함수
145
+ */
146
+ isRowSelectable?: (params: {
147
+ row: T;
148
+ id: InferredIdType<T, GetId>;
149
+ }) => boolean;
143
150
  disableSelectionOnClick?: boolean;
144
151
  initialState?: Partial<{
145
152
  sorting: Partial<{
package/dist/index.cjs CHANGED
@@ -3508,7 +3508,8 @@ function useDataTableRenderer({
3508
3508
  onSelectionModelChange,
3509
3509
  editMode,
3510
3510
  getId: _getId,
3511
- isTotalSelected: _isTotalSelected
3511
+ isTotalSelected: _isTotalSelected,
3512
+ isRowSelectable
3512
3513
  }) {
3513
3514
  const [focusedRowId, setFocusedRowId] = (0, import_react25.useState)(null);
3514
3515
  const [sortModel, setSortModel] = useControlledState(
@@ -3584,14 +3585,27 @@ function useDataTableRenderer({
3584
3585
  () => !pagination || paginationMode === "server" ? rows : rows.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize),
3585
3586
  [rows, page, pageSize, paginationMode, pagination]
3586
3587
  );
3588
+ const selectableDataInPage = (0, import_react25.useMemo)(
3589
+ () => dataInPage.filter((row, i) => {
3590
+ if (!isRowSelectable) return true;
3591
+ return isRowSelectable({ row, id: getId(row, i) });
3592
+ }),
3593
+ [dataInPage, isRowSelectable, getId]
3594
+ );
3587
3595
  const isAllSelected = (0, import_react25.useMemo)(
3588
- () => dataInPage.length > 0 && dataInPage.every((row, i) => selectedModelSet.has(getId(row, i))),
3589
- [dataInPage, selectedModelSet, getId]
3596
+ () => selectableDataInPage.length > 0 && selectableDataInPage.every(
3597
+ (row, i) => selectedModelSet.has(getId(row, i))
3598
+ ),
3599
+ [selectableDataInPage, selectedModelSet, getId]
3590
3600
  );
3591
3601
  const rowCount = totalRowsProp || rows.length;
3602
+ const selectableRowCount = (0, import_react25.useMemo)(() => {
3603
+ if (!isRowSelectable) return rowCount;
3604
+ return rows.filter((row, i) => isRowSelectable({ row, id: getId(row, i) })).length;
3605
+ }, [rows, isRowSelectable, getId, rowCount]);
3592
3606
  const isTotalSelected = (0, import_react25.useMemo)(
3593
- () => _isTotalSelected ?? (rowCount > 0 && (selectionModel?.length || 0) === rowCount),
3594
- [_isTotalSelected, selectionModel, rowCount]
3607
+ () => _isTotalSelected ?? (selectableRowCount > 0 && (selectionModel?.length || 0) === selectableRowCount),
3608
+ [_isTotalSelected, selectionModel, selectableRowCount]
3595
3609
  );
3596
3610
  const columnWidths = useColumnWidths(columnsByField);
3597
3611
  const getWidth = (0, import_react25.useCallback)(
@@ -3687,9 +3701,10 @@ function useDataTableRenderer({
3687
3701
  }, [page, rowCount, pageSize, handlePageChange]);
3688
3702
  (0, import_react25.useEffect)(() => {
3689
3703
  onSelectionModelChange?.([]);
3690
- }, [page]);
3704
+ }, [page, onSelectionModelChange]);
3691
3705
  return {
3692
3706
  rowCount,
3707
+ selectableRowCount,
3693
3708
  page,
3694
3709
  pageSize,
3695
3710
  onPaginationModelChange: handlePageChange,
@@ -3705,13 +3720,22 @@ function useDataTableRenderer({
3705
3720
  (model) => selectedModelSet.has(model),
3706
3721
  [selectedModelSet]
3707
3722
  ),
3723
+ isRowSelectable: (0, import_react25.useCallback)(
3724
+ (rowId, row) => {
3725
+ if (!isRowSelectable) return true;
3726
+ return isRowSelectable({ row, id: rowId });
3727
+ },
3728
+ [isRowSelectable]
3729
+ ),
3708
3730
  focusedRowId,
3709
3731
  onRowFocus: (0, import_react25.useCallback)((rowId) => {
3710
3732
  setFocusedRowId(rowId);
3711
3733
  }, []),
3712
3734
  onAllCheckboxChange: (0, import_react25.useCallback)(() => {
3713
- onSelectionModelChange?.(isAllSelected ? [] : dataInPage.map(getId));
3714
- }, [isAllSelected, dataInPage, onSelectionModelChange, getId]),
3735
+ onSelectionModelChange?.(
3736
+ isAllSelected ? [] : selectableDataInPage.map(getId)
3737
+ );
3738
+ }, [isAllSelected, selectableDataInPage, onSelectionModelChange, getId]),
3715
3739
  onCheckboxChange: (0, import_react25.useCallback)(
3716
3740
  (event, selectedModel) => {
3717
3741
  if (selectedModelSet.has(selectedModel)) {
@@ -3728,11 +3752,15 @@ function useDataTableRenderer({
3728
3752
  ),
3729
3753
  columns,
3730
3754
  onTotalSelect: (0, import_react25.useCallback)(() => {
3755
+ const selectableRows = rows.filter((row, i) => {
3756
+ if (!isRowSelectable) return true;
3757
+ return isRowSelectable({ row, id: getId(row, i) });
3758
+ });
3731
3759
  onSelectionModelChange?.(
3732
- isTotalSelected ? [] : rows.map(getId),
3760
+ isTotalSelected ? [] : selectableRows.map(getId),
3733
3761
  !isTotalSelected
3734
3762
  );
3735
- }, [isTotalSelected, rows, onSelectionModelChange, getId])
3763
+ }, [isTotalSelected, rows, onSelectionModelChange, getId, isRowSelectable])
3736
3764
  };
3737
3765
  }
3738
3766
  function Component(props, apiRef) {
@@ -3742,6 +3770,7 @@ function Component(props, apiRef) {
3742
3770
  editMode,
3743
3771
  selectionModel,
3744
3772
  onSelectionModelChange,
3773
+ isRowSelectable,
3745
3774
  disableSelectionOnClick,
3746
3775
  onRowClick,
3747
3776
  pinnedColumns,
@@ -3789,10 +3818,12 @@ function Component(props, apiRef) {
3789
3818
  columns,
3790
3819
  isAllSelected,
3791
3820
  isSelectedRow,
3821
+ isRowSelectable: isRowSelectableCheck,
3792
3822
  onAllCheckboxChange,
3793
3823
  onCheckboxChange,
3794
3824
  getId,
3795
3825
  rowCount,
3826
+ selectableRowCount,
3796
3827
  page,
3797
3828
  pageSize,
3798
3829
  onPaginationModelChange,
@@ -3818,9 +3849,15 @@ function Component(props, apiRef) {
3818
3849
  const getRowClickHandler = (0, import_react25.useCallback)(
3819
3850
  (row, rowId) => (e) => {
3820
3851
  onRowClick?.({ row, rowId }, e);
3821
- checkboxSelection && !disableSelectionOnClick && onCheckboxChange(e, rowId);
3852
+ checkboxSelection && !disableSelectionOnClick && isRowSelectableCheck(rowId, row) && onCheckboxChange(e, rowId);
3822
3853
  },
3823
- [onRowClick, checkboxSelection, disableSelectionOnClick, onCheckboxChange]
3854
+ [
3855
+ onRowClick,
3856
+ checkboxSelection,
3857
+ disableSelectionOnClick,
3858
+ onCheckboxChange,
3859
+ isRowSelectableCheck
3860
+ ]
3824
3861
  );
3825
3862
  const getRowFocusHandler = (0, import_react25.useCallback)(
3826
3863
  (rowId) => () => {
@@ -3835,10 +3872,12 @@ function Component(props, apiRef) {
3835
3872
  []
3836
3873
  );
3837
3874
  const getCheckboxChangeHandler = (0, import_react25.useCallback)(
3838
- (rowId) => (e) => {
3839
- onCheckboxChange(e, rowId);
3875
+ (rowId, row) => (e) => {
3876
+ if (isRowSelectableCheck(rowId, row)) {
3877
+ onCheckboxChange(e, rowId);
3878
+ }
3840
3879
  },
3841
- [onCheckboxChange]
3880
+ [onCheckboxChange, isRowSelectableCheck]
3842
3881
  );
3843
3882
  (0, import_react25.useImperativeHandle)(apiRef, () => ({
3844
3883
  getRowIndexRelativeToVisibleRows(rowId) {
@@ -3876,7 +3915,7 @@ function Component(props, apiRef) {
3876
3915
  justifyContent: "space-between",
3877
3916
  alignItems: "center"
3878
3917
  },
3879
- !!checkboxSelection && /* @__PURE__ */ import_react25.default.createElement(Stack_default, { direction: "row", spacing: 1 }, !isAllSelected && /* @__PURE__ */ import_react25.default.createElement(Typography_default, { level: "body-xs" }, numberFormatter(selectionModel?.length || 0), " items selected"), isAllSelected && !isTotalSelected && /* @__PURE__ */ import_react25.default.createElement(Stack_default, { direction: "row", spacing: 1, alignItems: "center" }, /* @__PURE__ */ import_react25.default.createElement(Typography_default, { level: "body-xs" }, "All ", numberFormatter(selectionModel?.length || 0), " items on this page are selected."), /* @__PURE__ */ import_react25.default.createElement(Button_default, { size: "sm", variant: "plain", onClick: onTotalSelect }, "Select all ", numberFormatter(rowCount ?? rows.length), " items")), isTotalSelected && /* @__PURE__ */ import_react25.default.createElement(Stack_default, { direction: "row", spacing: 1, alignItems: "center" }, /* @__PURE__ */ import_react25.default.createElement(Typography_default, { level: "body-xs" }, "All ", numberFormatter(rowCount ?? rows.length), " items are selected."), /* @__PURE__ */ import_react25.default.createElement(
3918
+ !!checkboxSelection && /* @__PURE__ */ import_react25.default.createElement(Stack_default, { direction: "row", spacing: 1 }, !isAllSelected && /* @__PURE__ */ import_react25.default.createElement(Typography_default, { level: "body-xs" }, numberFormatter(selectionModel?.length || 0), " items selected"), isAllSelected && !isTotalSelected && /* @__PURE__ */ import_react25.default.createElement(Stack_default, { direction: "row", spacing: 1, alignItems: "center" }, /* @__PURE__ */ import_react25.default.createElement(Typography_default, { level: "body-xs" }, "All ", numberFormatter(selectionModel?.length || 0), " items on this page are selected."), /* @__PURE__ */ import_react25.default.createElement(Button_default, { size: "sm", variant: "plain", onClick: onTotalSelect }, "Select all ", numberFormatter(selectableRowCount), " items")), isTotalSelected && /* @__PURE__ */ import_react25.default.createElement(Stack_default, { direction: "row", spacing: 1, alignItems: "center" }, /* @__PURE__ */ import_react25.default.createElement(Typography_default, { level: "body-xs" }, "All ", numberFormatter(selectableRowCount), " items are selected."), /* @__PURE__ */ import_react25.default.createElement(
3880
3919
  Button_default,
3881
3920
  {
3882
3921
  size: "sm",
@@ -3921,6 +3960,7 @@ function Component(props, apiRef) {
3921
3960
  onChange: onAllCheckboxChange,
3922
3961
  checked: isAllSelected,
3923
3962
  indeterminate: (selectionModel || []).length > 0 && !isAllSelected,
3963
+ disabled: dataInPage.length > 0 && !selectableRowCount,
3924
3964
  ...checkboxProps
3925
3965
  }
3926
3966
  )
@@ -3998,8 +4038,9 @@ function Component(props, apiRef) {
3998
4038
  RenderCheckbox,
3999
4039
  {
4000
4040
  onClick: getCheckboxClickHandler(),
4001
- onChange: getCheckboxChangeHandler(rowId),
4041
+ onChange: getCheckboxChangeHandler(rowId, row),
4002
4042
  checked: isSelectedRow(rowId),
4043
+ disabled: !isRowSelectableCheck(rowId, row),
4003
4044
  ...checkboxProps
4004
4045
  }
4005
4046
  )
package/dist/index.js CHANGED
@@ -3476,7 +3476,8 @@ function useDataTableRenderer({
3476
3476
  onSelectionModelChange,
3477
3477
  editMode,
3478
3478
  getId: _getId,
3479
- isTotalSelected: _isTotalSelected
3479
+ isTotalSelected: _isTotalSelected,
3480
+ isRowSelectable
3480
3481
  }) {
3481
3482
  const [focusedRowId, setFocusedRowId] = useState6(null);
3482
3483
  const [sortModel, setSortModel] = useControlledState(
@@ -3552,14 +3553,27 @@ function useDataTableRenderer({
3552
3553
  () => !pagination || paginationMode === "server" ? rows : rows.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize),
3553
3554
  [rows, page, pageSize, paginationMode, pagination]
3554
3555
  );
3556
+ const selectableDataInPage = useMemo8(
3557
+ () => dataInPage.filter((row, i) => {
3558
+ if (!isRowSelectable) return true;
3559
+ return isRowSelectable({ row, id: getId(row, i) });
3560
+ }),
3561
+ [dataInPage, isRowSelectable, getId]
3562
+ );
3555
3563
  const isAllSelected = useMemo8(
3556
- () => dataInPage.length > 0 && dataInPage.every((row, i) => selectedModelSet.has(getId(row, i))),
3557
- [dataInPage, selectedModelSet, getId]
3564
+ () => selectableDataInPage.length > 0 && selectableDataInPage.every(
3565
+ (row, i) => selectedModelSet.has(getId(row, i))
3566
+ ),
3567
+ [selectableDataInPage, selectedModelSet, getId]
3558
3568
  );
3559
3569
  const rowCount = totalRowsProp || rows.length;
3570
+ const selectableRowCount = useMemo8(() => {
3571
+ if (!isRowSelectable) return rowCount;
3572
+ return rows.filter((row, i) => isRowSelectable({ row, id: getId(row, i) })).length;
3573
+ }, [rows, isRowSelectable, getId, rowCount]);
3560
3574
  const isTotalSelected = useMemo8(
3561
- () => _isTotalSelected ?? (rowCount > 0 && (selectionModel?.length || 0) === rowCount),
3562
- [_isTotalSelected, selectionModel, rowCount]
3575
+ () => _isTotalSelected ?? (selectableRowCount > 0 && (selectionModel?.length || 0) === selectableRowCount),
3576
+ [_isTotalSelected, selectionModel, selectableRowCount]
3563
3577
  );
3564
3578
  const columnWidths = useColumnWidths(columnsByField);
3565
3579
  const getWidth = useCallback9(
@@ -3655,9 +3669,10 @@ function useDataTableRenderer({
3655
3669
  }, [page, rowCount, pageSize, handlePageChange]);
3656
3670
  useEffect5(() => {
3657
3671
  onSelectionModelChange?.([]);
3658
- }, [page]);
3672
+ }, [page, onSelectionModelChange]);
3659
3673
  return {
3660
3674
  rowCount,
3675
+ selectableRowCount,
3661
3676
  page,
3662
3677
  pageSize,
3663
3678
  onPaginationModelChange: handlePageChange,
@@ -3673,13 +3688,22 @@ function useDataTableRenderer({
3673
3688
  (model) => selectedModelSet.has(model),
3674
3689
  [selectedModelSet]
3675
3690
  ),
3691
+ isRowSelectable: useCallback9(
3692
+ (rowId, row) => {
3693
+ if (!isRowSelectable) return true;
3694
+ return isRowSelectable({ row, id: rowId });
3695
+ },
3696
+ [isRowSelectable]
3697
+ ),
3676
3698
  focusedRowId,
3677
3699
  onRowFocus: useCallback9((rowId) => {
3678
3700
  setFocusedRowId(rowId);
3679
3701
  }, []),
3680
3702
  onAllCheckboxChange: useCallback9(() => {
3681
- onSelectionModelChange?.(isAllSelected ? [] : dataInPage.map(getId));
3682
- }, [isAllSelected, dataInPage, onSelectionModelChange, getId]),
3703
+ onSelectionModelChange?.(
3704
+ isAllSelected ? [] : selectableDataInPage.map(getId)
3705
+ );
3706
+ }, [isAllSelected, selectableDataInPage, onSelectionModelChange, getId]),
3683
3707
  onCheckboxChange: useCallback9(
3684
3708
  (event, selectedModel) => {
3685
3709
  if (selectedModelSet.has(selectedModel)) {
@@ -3696,11 +3720,15 @@ function useDataTableRenderer({
3696
3720
  ),
3697
3721
  columns,
3698
3722
  onTotalSelect: useCallback9(() => {
3723
+ const selectableRows = rows.filter((row, i) => {
3724
+ if (!isRowSelectable) return true;
3725
+ return isRowSelectable({ row, id: getId(row, i) });
3726
+ });
3699
3727
  onSelectionModelChange?.(
3700
- isTotalSelected ? [] : rows.map(getId),
3728
+ isTotalSelected ? [] : selectableRows.map(getId),
3701
3729
  !isTotalSelected
3702
3730
  );
3703
- }, [isTotalSelected, rows, onSelectionModelChange, getId])
3731
+ }, [isTotalSelected, rows, onSelectionModelChange, getId, isRowSelectable])
3704
3732
  };
3705
3733
  }
3706
3734
  function Component(props, apiRef) {
@@ -3710,6 +3738,7 @@ function Component(props, apiRef) {
3710
3738
  editMode,
3711
3739
  selectionModel,
3712
3740
  onSelectionModelChange,
3741
+ isRowSelectable,
3713
3742
  disableSelectionOnClick,
3714
3743
  onRowClick,
3715
3744
  pinnedColumns,
@@ -3757,10 +3786,12 @@ function Component(props, apiRef) {
3757
3786
  columns,
3758
3787
  isAllSelected,
3759
3788
  isSelectedRow,
3789
+ isRowSelectable: isRowSelectableCheck,
3760
3790
  onAllCheckboxChange,
3761
3791
  onCheckboxChange,
3762
3792
  getId,
3763
3793
  rowCount,
3794
+ selectableRowCount,
3764
3795
  page,
3765
3796
  pageSize,
3766
3797
  onPaginationModelChange,
@@ -3786,9 +3817,15 @@ function Component(props, apiRef) {
3786
3817
  const getRowClickHandler = useCallback9(
3787
3818
  (row, rowId) => (e) => {
3788
3819
  onRowClick?.({ row, rowId }, e);
3789
- checkboxSelection && !disableSelectionOnClick && onCheckboxChange(e, rowId);
3820
+ checkboxSelection && !disableSelectionOnClick && isRowSelectableCheck(rowId, row) && onCheckboxChange(e, rowId);
3790
3821
  },
3791
- [onRowClick, checkboxSelection, disableSelectionOnClick, onCheckboxChange]
3822
+ [
3823
+ onRowClick,
3824
+ checkboxSelection,
3825
+ disableSelectionOnClick,
3826
+ onCheckboxChange,
3827
+ isRowSelectableCheck
3828
+ ]
3792
3829
  );
3793
3830
  const getRowFocusHandler = useCallback9(
3794
3831
  (rowId) => () => {
@@ -3803,10 +3840,12 @@ function Component(props, apiRef) {
3803
3840
  []
3804
3841
  );
3805
3842
  const getCheckboxChangeHandler = useCallback9(
3806
- (rowId) => (e) => {
3807
- onCheckboxChange(e, rowId);
3843
+ (rowId, row) => (e) => {
3844
+ if (isRowSelectableCheck(rowId, row)) {
3845
+ onCheckboxChange(e, rowId);
3846
+ }
3808
3847
  },
3809
- [onCheckboxChange]
3848
+ [onCheckboxChange, isRowSelectableCheck]
3810
3849
  );
3811
3850
  useImperativeHandle2(apiRef, () => ({
3812
3851
  getRowIndexRelativeToVisibleRows(rowId) {
@@ -3844,7 +3883,7 @@ function Component(props, apiRef) {
3844
3883
  justifyContent: "space-between",
3845
3884
  alignItems: "center"
3846
3885
  },
3847
- !!checkboxSelection && /* @__PURE__ */ React23.createElement(Stack_default, { direction: "row", spacing: 1 }, !isAllSelected && /* @__PURE__ */ React23.createElement(Typography_default, { level: "body-xs" }, numberFormatter(selectionModel?.length || 0), " items selected"), isAllSelected && !isTotalSelected && /* @__PURE__ */ React23.createElement(Stack_default, { direction: "row", spacing: 1, alignItems: "center" }, /* @__PURE__ */ React23.createElement(Typography_default, { level: "body-xs" }, "All ", numberFormatter(selectionModel?.length || 0), " items on this page are selected."), /* @__PURE__ */ React23.createElement(Button_default, { size: "sm", variant: "plain", onClick: onTotalSelect }, "Select all ", numberFormatter(rowCount ?? rows.length), " items")), isTotalSelected && /* @__PURE__ */ React23.createElement(Stack_default, { direction: "row", spacing: 1, alignItems: "center" }, /* @__PURE__ */ React23.createElement(Typography_default, { level: "body-xs" }, "All ", numberFormatter(rowCount ?? rows.length), " items are selected."), /* @__PURE__ */ React23.createElement(
3886
+ !!checkboxSelection && /* @__PURE__ */ React23.createElement(Stack_default, { direction: "row", spacing: 1 }, !isAllSelected && /* @__PURE__ */ React23.createElement(Typography_default, { level: "body-xs" }, numberFormatter(selectionModel?.length || 0), " items selected"), isAllSelected && !isTotalSelected && /* @__PURE__ */ React23.createElement(Stack_default, { direction: "row", spacing: 1, alignItems: "center" }, /* @__PURE__ */ React23.createElement(Typography_default, { level: "body-xs" }, "All ", numberFormatter(selectionModel?.length || 0), " items on this page are selected."), /* @__PURE__ */ React23.createElement(Button_default, { size: "sm", variant: "plain", onClick: onTotalSelect }, "Select all ", numberFormatter(selectableRowCount), " items")), isTotalSelected && /* @__PURE__ */ React23.createElement(Stack_default, { direction: "row", spacing: 1, alignItems: "center" }, /* @__PURE__ */ React23.createElement(Typography_default, { level: "body-xs" }, "All ", numberFormatter(selectableRowCount), " items are selected."), /* @__PURE__ */ React23.createElement(
3848
3887
  Button_default,
3849
3888
  {
3850
3889
  size: "sm",
@@ -3889,6 +3928,7 @@ function Component(props, apiRef) {
3889
3928
  onChange: onAllCheckboxChange,
3890
3929
  checked: isAllSelected,
3891
3930
  indeterminate: (selectionModel || []).length > 0 && !isAllSelected,
3931
+ disabled: dataInPage.length > 0 && !selectableRowCount,
3892
3932
  ...checkboxProps
3893
3933
  }
3894
3934
  )
@@ -3966,8 +4006,9 @@ function Component(props, apiRef) {
3966
4006
  RenderCheckbox,
3967
4007
  {
3968
4008
  onClick: getCheckboxClickHandler(),
3969
- onChange: getCheckboxChangeHandler(rowId),
4009
+ onChange: getCheckboxChangeHandler(rowId, row),
3970
4010
  checked: isSelectedRow(rowId),
4011
+ disabled: !isRowSelectableCheck(rowId, row),
3971
4012
  ...checkboxProps
3972
4013
  }
3973
4014
  )