@astral/ui 4.113.3 → 4.114.0
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/components/ActionGroup/ActionGroup.js +2 -2
- package/components/ActionGroup/SecondaryActions/SecondaryActions.d.ts +1 -1
- package/components/ActionGroup/SecondaryActions/SecondaryActions.js +2 -2
- package/components/ActionGroup/types.d.ts +9 -0
- package/components/DataGrid/DataGrid.js +13 -5
- package/components/DataGrid/DataGridContext/DataGridProvider/DataGridProvider.d.ts +3 -2
- package/components/DataGrid/DataGridContext/DataGridProvider/DataGridProvider.js +3 -2
- package/components/DataGrid/DataGridSelectionContext/DataGridSelectionContext.d.ts +25 -0
- package/components/DataGrid/DataGridSelectionContext/DataGridSelectionContext.js +13 -0
- package/components/DataGrid/DataGridSelectionContext/DataGridSelectionProvider/DataGridSelectionProvider.d.ts +7 -0
- package/components/DataGrid/DataGridSelectionContext/DataGridSelectionProvider/DataGridSelectionProvider.js +8 -0
- package/components/DataGrid/DataGridSelectionContext/DataGridSelectionProvider/index.d.ts +1 -0
- package/components/DataGrid/DataGridSelectionContext/DataGridSelectionProvider/index.js +1 -0
- package/components/DataGrid/DataGridSelectionContext/index.d.ts +2 -0
- package/components/DataGrid/DataGridSelectionContext/index.js +2 -0
- package/components/DataGrid/constants.d.ts +2 -0
- package/components/DataGrid/constants.js +2 -0
- package/components/DataGrid/styles.d.ts +4 -0
- package/components/DataGrid/styles.js +26 -0
- package/components/DataGrid/types.d.ts +7 -0
- package/components/DataGrid/useLogic/useLogic.d.ts +3 -1
- package/components/DataGrid/useLogic/useLogic.js +10 -2
- package/components/DataGrid/utils/checkIsMassActionsOpen/checkIsMassActionsOpen.d.ts +10 -0
- package/components/DataGrid/utils/checkIsMassActionsOpen/checkIsMassActionsOpen.js +4 -0
- package/components/DataGrid/utils/checkIsMassActionsOpen/index.d.ts +1 -0
- package/components/DataGrid/utils/checkIsMassActionsOpen/index.js +1 -0
- package/components/DataGrid/utils/index.d.ts +1 -0
- package/components/DataGrid/utils/index.js +1 -0
- package/components/DataGridInfinite/DataGridInfinite.js +17 -13
- package/components/DataGridInfinite/Footer/Footer.d.ts +6 -0
- package/components/DataGridInfinite/Footer/Footer.js +25 -0
- package/components/DataGridInfinite/Footer/index.d.ts +2 -0
- package/components/DataGridInfinite/Footer/index.js +1 -0
- package/components/DataGridInfinite/Footer/types.d.ts +26 -0
- package/components/DataGridInfinite/Footer/types.js +1 -0
- package/components/DataGridInfinite/constants.d.ts +1 -0
- package/components/DataGridInfinite/constants.js +1 -0
- package/components/DataGridInfinite/styles.d.ts +4 -0
- package/components/DataGridInfinite/styles.js +24 -0
- package/components/DataGridInfinite/useLogic/useLogic.d.ts +6 -3
- package/components/DataGridInfinite/useLogic/useLogic.js +32 -8
- package/components/DataGridMassActions/DataGridMassActions.d.ts +15 -0
- package/components/DataGridMassActions/DataGridMassActions.js +32 -0
- package/components/DataGridMassActions/constants.d.ts +4 -0
- package/components/DataGridMassActions/constants.js +5 -0
- package/components/DataGridMassActions/index.d.ts +2 -0
- package/components/DataGridMassActions/index.js +1 -0
- package/components/DataGridMassActions/public.d.ts +2 -0
- package/components/DataGridMassActions/public.js +1 -0
- package/components/DataGridMassActions/styles.d.ts +8 -0
- package/components/DataGridMassActions/styles.js +49 -0
- package/components/DataGridMassActions/tests/testDataGridMassActions.d.ts +23 -0
- package/components/DataGridMassActions/tests/testDataGridMassActions.js +77 -0
- package/components/DataGridMassActions/types.d.ts +2 -0
- package/components/DataGridMassActions/types.js +1 -0
- package/components/DataGridMassActions/useLogic/index.d.ts +1 -0
- package/components/DataGridMassActions/useLogic/index.js +1 -0
- package/components/DataGridMassActions/useLogic/useLogic.d.ts +6 -0
- package/components/DataGridMassActions/useLogic/useLogic.js +21 -0
- package/components/IconDropdownButton/IconDropdownButton.d.ts +5 -0
- package/components/IconDropdownButton/IconDropdownButton.js +2 -2
- package/components/MassActions/MassActions.d.ts +5 -0
- package/components/MassActions/MassActions.js +17 -0
- package/components/MassActions/constants.d.ts +6 -0
- package/components/MassActions/constants.js +12 -0
- package/components/MassActions/index.d.ts +2 -0
- package/components/MassActions/index.js +1 -0
- package/components/MassActions/styles.d.ts +34 -0
- package/components/MassActions/styles.js +43 -0
- package/components/MassActions/types.d.ts +20 -0
- package/components/MassActions/types.js +1 -0
- package/components/MassActions/useLogic/index.d.ts +1 -0
- package/components/MassActions/useLogic/index.js +1 -0
- package/components/MassActions/useLogic/useLogic.d.ts +7 -0
- package/components/MassActions/useLogic/useLogic.js +9 -0
- package/components/PageHeader/PageActions/SecondaryActions/styles.d.ts +1 -1
- package/components/Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/hooks/index.d.ts +0 -1
- package/components/Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/hooks/index.js +0 -1
- package/components/Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/useLogic.js +2 -1
- package/node/components/ActionGroup/ActionGroup.js +2 -2
- package/node/components/ActionGroup/SecondaryActions/SecondaryActions.d.ts +1 -1
- package/node/components/ActionGroup/SecondaryActions/SecondaryActions.js +2 -2
- package/node/components/ActionGroup/types.d.ts +9 -0
- package/node/components/DataGrid/DataGrid.js +12 -4
- package/node/components/DataGrid/DataGridContext/DataGridProvider/DataGridProvider.d.ts +3 -2
- package/node/components/DataGrid/DataGridContext/DataGridProvider/DataGridProvider.js +3 -2
- package/node/components/DataGrid/DataGridSelectionContext/DataGridSelectionContext.d.ts +25 -0
- package/node/components/DataGrid/DataGridSelectionContext/DataGridSelectionContext.js +16 -0
- package/node/components/DataGrid/DataGridSelectionContext/DataGridSelectionProvider/DataGridSelectionProvider.d.ts +7 -0
- package/node/components/DataGrid/DataGridSelectionContext/DataGridSelectionProvider/DataGridSelectionProvider.js +12 -0
- package/node/components/DataGrid/DataGridSelectionContext/DataGridSelectionProvider/index.d.ts +1 -0
- package/node/components/DataGrid/DataGridSelectionContext/DataGridSelectionProvider/index.js +17 -0
- package/node/components/DataGrid/DataGridSelectionContext/index.d.ts +2 -0
- package/node/components/DataGrid/DataGridSelectionContext/index.js +18 -0
- package/node/components/DataGrid/constants.d.ts +2 -0
- package/node/components/DataGrid/constants.js +2 -0
- package/node/components/DataGrid/styles.d.ts +4 -0
- package/node/components/DataGrid/styles.js +27 -1
- package/node/components/DataGrid/types.d.ts +7 -0
- package/node/components/DataGrid/useLogic/useLogic.d.ts +3 -1
- package/node/components/DataGrid/useLogic/useLogic.js +9 -1
- package/node/components/DataGrid/utils/checkIsMassActionsOpen/checkIsMassActionsOpen.d.ts +10 -0
- package/node/components/DataGrid/utils/checkIsMassActionsOpen/checkIsMassActionsOpen.js +8 -0
- package/node/components/DataGrid/utils/checkIsMassActionsOpen/index.d.ts +1 -0
- package/node/components/DataGrid/utils/checkIsMassActionsOpen/index.js +5 -0
- package/node/components/DataGrid/utils/index.d.ts +1 -0
- package/node/components/DataGrid/utils/index.js +3 -1
- package/node/components/DataGridInfinite/DataGridInfinite.js +19 -15
- package/node/components/DataGridInfinite/Footer/Footer.d.ts +6 -0
- package/node/components/DataGridInfinite/Footer/Footer.js +29 -0
- package/node/components/DataGridInfinite/Footer/index.d.ts +2 -0
- package/node/components/DataGridInfinite/Footer/index.js +17 -0
- package/node/components/DataGridInfinite/Footer/types.d.ts +26 -0
- package/node/components/DataGridInfinite/Footer/types.js +2 -0
- package/node/components/DataGridInfinite/constants.d.ts +1 -0
- package/node/components/DataGridInfinite/constants.js +1 -0
- package/node/components/DataGridInfinite/styles.d.ts +4 -0
- package/node/components/DataGridInfinite/styles.js +27 -3
- package/node/components/DataGridInfinite/useLogic/useLogic.d.ts +6 -3
- package/node/components/DataGridInfinite/useLogic/useLogic.js +30 -6
- package/node/components/DataGridMassActions/DataGridMassActions.d.ts +15 -0
- package/node/components/DataGridMassActions/DataGridMassActions.js +36 -0
- package/node/components/DataGridMassActions/constants.d.ts +4 -0
- package/node/components/DataGridMassActions/constants.js +8 -0
- package/node/components/DataGridMassActions/index.d.ts +2 -0
- package/node/components/DataGridMassActions/index.js +5 -0
- package/node/components/DataGridMassActions/public.d.ts +2 -0
- package/node/components/DataGridMassActions/public.js +5 -0
- package/node/components/DataGridMassActions/styles.d.ts +8 -0
- package/node/components/DataGridMassActions/styles.js +52 -0
- package/node/components/DataGridMassActions/tests/testDataGridMassActions.d.ts +23 -0
- package/node/components/DataGridMassActions/tests/testDataGridMassActions.js +81 -0
- package/node/components/DataGridMassActions/types.d.ts +2 -0
- package/node/components/DataGridMassActions/types.js +2 -0
- package/node/components/DataGridMassActions/useLogic/index.d.ts +1 -0
- package/node/components/DataGridMassActions/useLogic/index.js +17 -0
- package/node/components/DataGridMassActions/useLogic/useLogic.d.ts +6 -0
- package/node/components/DataGridMassActions/useLogic/useLogic.js +25 -0
- package/node/components/IconDropdownButton/IconDropdownButton.d.ts +5 -0
- package/node/components/IconDropdownButton/IconDropdownButton.js +2 -2
- package/node/components/MassActions/MassActions.d.ts +5 -0
- package/node/components/MassActions/MassActions.js +21 -0
- package/node/components/MassActions/constants.d.ts +6 -0
- package/node/components/MassActions/constants.js +15 -0
- package/node/components/MassActions/index.d.ts +2 -0
- package/node/components/MassActions/index.js +5 -0
- package/node/components/MassActions/styles.d.ts +34 -0
- package/node/components/MassActions/styles.js +46 -0
- package/node/components/MassActions/types.d.ts +20 -0
- package/node/components/MassActions/types.js +2 -0
- package/node/components/MassActions/useLogic/index.d.ts +1 -0
- package/node/components/MassActions/useLogic/index.js +17 -0
- package/node/components/MassActions/useLogic/useLogic.d.ts +7 -0
- package/node/components/MassActions/useLogic/useLogic.js +13 -0
- package/node/components/PageHeader/PageActions/SecondaryActions/styles.d.ts +1 -1
- package/node/components/Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/hooks/index.d.ts +0 -1
- package/node/components/Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/hooks/index.js +0 -1
- package/node/components/Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/useLogic.js +2 -1
- package/package.json +1 -1
- /package/components/{Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/hooks/useDelayedUnmount → useDelayedUnmount}/index.d.ts +0 -0
- /package/components/{Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/hooks/useDelayedUnmount → useDelayedUnmount}/index.js +0 -0
- /package/components/{Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/hooks/useDelayedUnmount → useDelayedUnmount}/useDelayedUnmount.d.ts +0 -0
- /package/components/{Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/hooks/useDelayedUnmount → useDelayedUnmount}/useDelayedUnmount.js +0 -0
- /package/node/components/{Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/hooks/useDelayedUnmount → useDelayedUnmount}/index.d.ts +0 -0
- /package/node/components/{Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/hooks/useDelayedUnmount → useDelayedUnmount}/index.js +0 -0
- /package/node/components/{Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/hooks/useDelayedUnmount → useDelayedUnmount}/useDelayedUnmount.d.ts +0 -0
- /package/node/components/{Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/hooks/useDelayedUnmount → useDelayedUnmount}/useDelayedUnmount.js +0 -0
|
@@ -2,9 +2,13 @@ import { type ChangeEvent } from 'react';
|
|
|
2
2
|
import type { ListRange } from 'react-virtuoso';
|
|
3
3
|
import { type DataGridRow } from '../../DataGrid';
|
|
4
4
|
import { type CellValue } from '../../DataGrid/types';
|
|
5
|
+
import { type FooterContext } from '../Footer';
|
|
5
6
|
import { type DataGridInfiniteProps, type DataGridInfiniteVirtuosoHandle } from '../types';
|
|
6
7
|
type UseLogicParams<TData extends Record<string, CellValue> = DataGridRow, TSortField extends keyof TData = keyof TData> = DataGridInfiniteProps<TData, TSortField>;
|
|
7
|
-
export declare const useLogic: <TData extends Record<string, unknown> = DataGridRow, TSortField extends keyof TData = keyof TData>({ keyId, columns, rows, variant, tree, subrows, selectedRows, maxHeight, isLoading, isError, isDisabled, isEndReached, onEndReached, onStartReached, isStartReached, onSelectRow, isHideSelectAll, initialVisibleItemIndex, isHideHeaderIfNoData, gridViewMode: gridViewModeProp, mobileDisplayVariant, virtuosoRef, }: UseLogicParams<TData, TSortField>) => {
|
|
8
|
+
export declare const useLogic: <TData extends Record<string, unknown> = DataGridRow, TSortField extends keyof TData = keyof TData>({ keyId, columns, rows, variant, tree, subrows, selectedRows, maxHeight, isLoading, isError, isDisabled, isEndReached, endOfScrollMsg, onRetry, onEndReached, onStartReached, isStartReached, onSelectRow, isHideSelectAll, initialVisibleItemIndex, isHideHeaderIfNoData, gridViewMode: gridViewModeProp, massActions, mobileDisplayVariant, virtuosoRef, }: UseLogicParams<TData, TSortField>) => {
|
|
9
|
+
footerContext: FooterContext;
|
|
10
|
+
hasMassActions: boolean;
|
|
11
|
+
isMassActionsOpen: boolean;
|
|
8
12
|
containerCssVars: import("react").CSSProperties;
|
|
9
13
|
mobileSkeletonProps: {
|
|
10
14
|
cellsCount: number;
|
|
@@ -19,7 +23,6 @@ export declare const useLogic: <TData extends Record<string, unknown> = DataGrid
|
|
|
19
23
|
expandedLevel?: number | undefined;
|
|
20
24
|
initialVisibleChildrenCount?: number | undefined;
|
|
21
25
|
} | undefined;
|
|
22
|
-
isEndReached: boolean | undefined;
|
|
23
26
|
isHideHead: boolean;
|
|
24
27
|
hasGroupedColumns: boolean;
|
|
25
28
|
gridViewMode: "standard" | "full";
|
|
@@ -32,7 +35,7 @@ export declare const useLogic: <TData extends Record<string, unknown> = DataGrid
|
|
|
32
35
|
ref: (node: DataGridInfiniteVirtuosoHandle | null) => void;
|
|
33
36
|
endReached: () => void;
|
|
34
37
|
rangeChanged: (range: ListRange) => void;
|
|
35
|
-
totalListHeightChanged: () => void;
|
|
38
|
+
totalListHeightChanged: (listHeight: number) => void;
|
|
36
39
|
startReached: () => void;
|
|
37
40
|
firstItemIndex: number;
|
|
38
41
|
};
|
|
@@ -2,12 +2,12 @@ import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
|
2
2
|
import { EXPANDED_LEVEL_BY_DEFAULT, INITIAL_OPENED_NESTED_CHILDREN_COUNT_ON_MOBILE_BY_DEFAULT, } from '../../DataGrid/constants';
|
|
3
3
|
import { Variant } from '../../DataGrid/enums';
|
|
4
4
|
import { useDataGridMobileLogic } from '../../DataGrid/useLogic/hooks';
|
|
5
|
-
import { getDataGridCssVars, getFlattenColumns, getGridTemplateColumns, isGroupColumns, } from '../../DataGrid/utils';
|
|
5
|
+
import { checkIsMassActionsOpen, getDataGridCssVars, getFlattenColumns, getGridTemplateColumns, isGroupColumns, } from '../../DataGrid/utils';
|
|
6
6
|
import { useViewportType } from '../../useViewportType';
|
|
7
7
|
import { prop } from '../../utils/prop';
|
|
8
8
|
import { uniqueBy } from '../../utils/uniqueBy';
|
|
9
|
-
import { dataGridInfiniteClassnames, VIRTUOSO_CONTAINER_ID,
|
|
10
|
-
export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selectedRows = [], maxHeight, isLoading, isError, isDisabled, isEndReached, onEndReached, onStartReached, isStartReached, onSelectRow, isHideSelectAll, initialVisibleItemIndex, isHideHeaderIfNoData = false, gridViewMode: gridViewModeProp = 'standard', mobileDisplayVariant = variant === 'subrows' ? 'cards' : 'list', virtuosoRef, }) => {
|
|
9
|
+
import { dataGridInfiniteClassnames, VIRTUOSO_CONTAINER_ID, } from '../constants';
|
|
10
|
+
export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selectedRows = [], maxHeight, isLoading, isError, isDisabled, isEndReached, endOfScrollMsg, onRetry, onEndReached, onStartReached, isStartReached, onSelectRow, isHideSelectAll, initialVisibleItemIndex, isHideHeaderIfNoData = false, gridViewMode: gridViewModeProp = 'standard', massActions, mobileDisplayVariant = variant === 'subrows' ? 'cards' : 'list', virtuosoRef, }) => {
|
|
11
11
|
const virtuoso = useRef(null);
|
|
12
12
|
const handleVirtuosoRef = useCallback((node) => {
|
|
13
13
|
virtuoso.current = node;
|
|
@@ -112,11 +112,11 @@ export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selecte
|
|
|
112
112
|
const handleScrollToTop = () => {
|
|
113
113
|
virtuoso.current?.scrollIntoView({ index: 0, behavior: 'smooth' });
|
|
114
114
|
};
|
|
115
|
-
const recalculateVerticalScroll = () => {
|
|
115
|
+
const recalculateVerticalScroll = (listHeight) => {
|
|
116
116
|
const container = document.getElementById(VIRTUOSO_CONTAINER_ID);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
setHasVerticalScroll(
|
|
117
|
+
if (container) {
|
|
118
|
+
const massActionsSpacerHeight = container.querySelector(`.${dataGridInfiniteClassnames.massActionsSpacer}`)?.clientHeight ?? 0;
|
|
119
|
+
setHasVerticalScroll(listHeight - massActionsSpacerHeight > container.clientHeight);
|
|
120
120
|
}
|
|
121
121
|
};
|
|
122
122
|
const isHideHead = isHideHeaderIfNoData && rows.length === 0;
|
|
@@ -130,14 +130,38 @@ export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selecte
|
|
|
130
130
|
const isNotRenderHeader = (!isLoading && !isError && !isStartReached) ||
|
|
131
131
|
!onStartReached ||
|
|
132
132
|
isStartReached;
|
|
133
|
+
const hasMassActions = Boolean(massActions);
|
|
134
|
+
const isMassActionsOpen = checkIsMassActionsOpen({
|
|
135
|
+
hasMassActions,
|
|
136
|
+
selectedRowsCount: selectedRows.length,
|
|
137
|
+
isMobile,
|
|
138
|
+
});
|
|
139
|
+
const footerContext = useMemo(() => ({
|
|
140
|
+
isLoading,
|
|
141
|
+
isError,
|
|
142
|
+
isEndReached: isEndReached && hasVerticalScroll,
|
|
143
|
+
endOfScrollMsg,
|
|
144
|
+
onRetry,
|
|
145
|
+
hasMassActions,
|
|
146
|
+
}), [
|
|
147
|
+
isLoading,
|
|
148
|
+
isError,
|
|
149
|
+
isEndReached,
|
|
150
|
+
hasVerticalScroll,
|
|
151
|
+
endOfScrollMsg,
|
|
152
|
+
onRetry,
|
|
153
|
+
hasMassActions,
|
|
154
|
+
]);
|
|
133
155
|
return {
|
|
156
|
+
footerContext,
|
|
157
|
+
hasMassActions,
|
|
158
|
+
isMassActionsOpen,
|
|
134
159
|
containerCssVars,
|
|
135
160
|
mobileSkeletonProps,
|
|
136
161
|
isNoData,
|
|
137
162
|
isMobile,
|
|
138
163
|
isDataGridDisabled,
|
|
139
164
|
treeRenderConfig,
|
|
140
|
-
isEndReached: isEndReached && hasVerticalScroll,
|
|
141
165
|
isHideHead,
|
|
142
166
|
hasGroupedColumns,
|
|
143
167
|
gridViewMode,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type DataGridMassActionsProps } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Панель массовых действий над выбранными строками `DataGrid` и `DataGridInfinite`.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* <DataGrid
|
|
7
|
+
* massActions={
|
|
8
|
+
* <DataGridMassActions
|
|
9
|
+
* entityDeclension={['документ', 'документа', 'документов']}
|
|
10
|
+
* main={[{ text: 'Отправить', onClick: handleSend }]}
|
|
11
|
+
* />
|
|
12
|
+
* }
|
|
13
|
+
* />
|
|
14
|
+
*/
|
|
15
|
+
export declare const DataGridMassActions: ({ className, ...props }: DataGridMassActionsProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cva } from '../utils/cva';
|
|
3
|
+
import { dataGridMassActionsClassnames } from './constants';
|
|
4
|
+
import { StyledMassActions } from './styles';
|
|
5
|
+
import { useLogic } from './useLogic';
|
|
6
|
+
const rootCva = cva(dataGridMassActionsClassnames.root, {
|
|
7
|
+
variants: {
|
|
8
|
+
isOpen: {
|
|
9
|
+
true: dataGridMassActionsClassnames.open,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* Панель массовых действий над выбранными строками `DataGrid` и `DataGridInfinite`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* <DataGrid
|
|
18
|
+
* massActions={
|
|
19
|
+
* <DataGridMassActions
|
|
20
|
+
* entityDeclension={['документ', 'документа', 'документов']}
|
|
21
|
+
* main={[{ text: 'Отправить', onClick: handleSend }]}
|
|
22
|
+
* />
|
|
23
|
+
* }
|
|
24
|
+
* />
|
|
25
|
+
*/
|
|
26
|
+
export const DataGridMassActions = ({ className, ...props }) => {
|
|
27
|
+
const { isOpen, isMounted, selectedCount, handleClose } = useLogic();
|
|
28
|
+
if (!isOpen && !isMounted) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return (_jsx(StyledMassActions, { ...props, className: rootCva({ isOpen, className }), selectedCount: selectedCount, onClose: handleClose }));
|
|
32
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DataGridMassActions } from './DataGridMassActions';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DataGridMassActions } from './DataGridMassActions';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const StyledMassActions: import("../styled").StyledComponent<Pick<import("..").ActionGroupProps, "main" | "secondary"> & {
|
|
2
|
+
selectedCount: number;
|
|
3
|
+
entityDeclension: [string, string, string];
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
className?: string | undefined;
|
|
6
|
+
} & {
|
|
7
|
+
theme?: import("@emotion/react").Theme | undefined;
|
|
8
|
+
}, {}, {}>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { MassActions } from '../MassActions';
|
|
2
|
+
import { keyframes, styled } from '../styled';
|
|
3
|
+
import { dataGridMassActionsClassnames } from './constants';
|
|
4
|
+
const slideIn = keyframes `
|
|
5
|
+
from {
|
|
6
|
+
transform: translateY(100%);
|
|
7
|
+
|
|
8
|
+
opacity: 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
to {
|
|
12
|
+
transform: translateY(0);
|
|
13
|
+
|
|
14
|
+
opacity: 1;
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
const slideOut = keyframes `
|
|
18
|
+
from {
|
|
19
|
+
transform: translateY(0);
|
|
20
|
+
|
|
21
|
+
opacity: 1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
to {
|
|
25
|
+
transform: translateY(100%);
|
|
26
|
+
|
|
27
|
+
opacity: 0;
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
export const StyledMassActions = styled(MassActions) `
|
|
31
|
+
position: absolute;
|
|
32
|
+
z-index: ${({ theme }) => theme.zIndex.appBar};
|
|
33
|
+
right: 0;
|
|
34
|
+
bottom: ${({ theme }) => theme.spacing(3)};
|
|
35
|
+
left: 0;
|
|
36
|
+
|
|
37
|
+
max-width: calc(100% - ${({ theme }) => theme.spacing(8)});
|
|
38
|
+
margin-inline: auto;
|
|
39
|
+
|
|
40
|
+
animation: ${slideOut}
|
|
41
|
+
${({ theme }) => theme.transitions.duration.leavingScreen}ms
|
|
42
|
+
${({ theme }) => theme.transitions.easing.easeIn} forwards;
|
|
43
|
+
|
|
44
|
+
&.${dataGridMassActionsClassnames.open} {
|
|
45
|
+
animation: ${slideIn}
|
|
46
|
+
${({ theme }) => theme.transitions.duration.standard}ms
|
|
47
|
+
${({ theme }) => theme.transitions.easing.easeOut} forwards;
|
|
48
|
+
}
|
|
49
|
+
`;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode } from 'react';
|
|
2
|
+
import { type DataGridColumns } from '../../DataGrid/types';
|
|
3
|
+
type DataItem = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
type GridRenderProps = {
|
|
8
|
+
rows: DataItem[];
|
|
9
|
+
columns: DataGridColumns<DataItem>[];
|
|
10
|
+
selectedRows: DataItem[];
|
|
11
|
+
onSelectRow: (rows: DataItem[]) => void;
|
|
12
|
+
massActions: ReactNode;
|
|
13
|
+
onRetry: () => void;
|
|
14
|
+
};
|
|
15
|
+
type TestDataGridMassActionsParams = {
|
|
16
|
+
render: (ui: ReactElement) => void;
|
|
17
|
+
renderGrid: (props: GridRenderProps) => ReactNode;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Общие тесты связки таблицы и DataGridMassActions для DataGrid и DataGridInfinite
|
|
21
|
+
*/
|
|
22
|
+
export declare const testDataGridMassActions: ({ render, renderGrid, }: TestDataGridMassActionsParams) => void;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { screen, setWindowWidth, userEvents, viewportSize, waitForElementToBeRemoved, } from '@ui/dev/tests';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { beforeEach, expect, it } from 'vitest';
|
|
5
|
+
import { DataGridMassActions } from '../DataGridMassActions';
|
|
6
|
+
const TEST_ROWS = [
|
|
7
|
+
{ id: '1', name: 'Договор поставки' },
|
|
8
|
+
{ id: '2', name: 'Счёт-фактура' },
|
|
9
|
+
];
|
|
10
|
+
const TEST_COLUMNS = [
|
|
11
|
+
{
|
|
12
|
+
field: 'name',
|
|
13
|
+
label: 'Наименование',
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* Общие тесты связки таблицы и DataGridMassActions для DataGrid и DataGridInfinite
|
|
18
|
+
*/
|
|
19
|
+
export const testDataGridMassActions = ({ render, renderGrid, }) => {
|
|
20
|
+
const TestComponent = ({ initialSelectedRows, }) => {
|
|
21
|
+
const [selectedRows, setSelectedRows] = useState(initialSelectedRows);
|
|
22
|
+
return renderGrid({
|
|
23
|
+
rows: TEST_ROWS,
|
|
24
|
+
columns: TEST_COLUMNS,
|
|
25
|
+
selectedRows,
|
|
26
|
+
onSelectRow: setSelectedRows,
|
|
27
|
+
massActions: (_jsx(DataGridMassActions, { entityDeclension: ['документ', 'документа', 'документов'], main: [{ text: 'Отправить' }] })),
|
|
28
|
+
onRetry: () => { },
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
const setup = ({ initialSelectedRows = [], } = {}) => {
|
|
32
|
+
render(_jsx(TestComponent, { initialSelectedRows: initialSelectedRows }));
|
|
33
|
+
};
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
setWindowWidth(viewportSize.desktop.default.width);
|
|
36
|
+
});
|
|
37
|
+
it('Панель не отображается при пустом выборе', () => {
|
|
38
|
+
setup();
|
|
39
|
+
const closeButton = screen.queryByRole('button', {
|
|
40
|
+
name: 'Снять выделение',
|
|
41
|
+
});
|
|
42
|
+
expect(closeButton).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
it('Панель отображается при выборе строки', async () => {
|
|
45
|
+
setup();
|
|
46
|
+
// 0 - "Выбрать все", 1 - первая строка
|
|
47
|
+
await userEvents.click(screen.getAllByRole('checkbox')[1]);
|
|
48
|
+
expect(screen.getByText('Выбрано: 1 документ')).toBeVisible();
|
|
49
|
+
});
|
|
50
|
+
it('Панель остаётся открытой после нажатия на действие, если выбор не очищен', async () => {
|
|
51
|
+
setup({ initialSelectedRows: TEST_ROWS });
|
|
52
|
+
await userEvents.click(screen.getByRole('button', { name: 'Отправить' }));
|
|
53
|
+
expect(screen.getByText('Выбрано: 2 документа')).toBeVisible();
|
|
54
|
+
});
|
|
55
|
+
it('Выбор строк снимается при закрытии панели', async () => {
|
|
56
|
+
setup({ initialSelectedRows: TEST_ROWS });
|
|
57
|
+
await userEvents.click(screen.getByRole('button', { name: 'Снять выделение' }));
|
|
58
|
+
const checkedCheckboxes = screen.queryAllByRole('checkbox', {
|
|
59
|
+
checked: true,
|
|
60
|
+
});
|
|
61
|
+
expect(checkedCheckboxes).toHaveLength(0);
|
|
62
|
+
});
|
|
63
|
+
it('Панель скрывается при закрытии', async () => {
|
|
64
|
+
setup({ initialSelectedRows: TEST_ROWS });
|
|
65
|
+
await userEvents.click(screen.getByRole('button', { name: 'Снять выделение' }));
|
|
66
|
+
// Панель размонтируется после анимации скрытия
|
|
67
|
+
await expect(waitForElementToBeRemoved(() => screen.queryByRole('button', { name: 'Снять выделение' }))).resolves.toBeUndefined();
|
|
68
|
+
});
|
|
69
|
+
it('Панель не отображается на мобильных разрешениях', () => {
|
|
70
|
+
setWindowWidth(viewportSize.mobile.default.width);
|
|
71
|
+
setup({ initialSelectedRows: TEST_ROWS });
|
|
72
|
+
const closeButton = screen.queryByRole('button', {
|
|
73
|
+
name: 'Снять выделение',
|
|
74
|
+
});
|
|
75
|
+
expect(closeButton).toBeNull();
|
|
76
|
+
});
|
|
77
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useLogic';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useLogic';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useContext, useState } from 'react';
|
|
2
|
+
import { DataGridSelectionContext } from '../../DataGrid/DataGridSelectionContext';
|
|
3
|
+
import { useTheme } from '../../theme/hooks';
|
|
4
|
+
import { useDelayedUnmount } from '../../useDelayedUnmount';
|
|
5
|
+
export const useLogic = () => {
|
|
6
|
+
const { selectedRows, onSelectRow, isMassActionsOpen: isOpen, } = useContext(DataGridSelectionContext);
|
|
7
|
+
const { transitions: { duration: { leavingScreen }, }, } = useTheme();
|
|
8
|
+
const { isMounted } = useDelayedUnmount({ isOpen, duration: leavingScreen });
|
|
9
|
+
// Во время анимации скрытия выбор уже пустой — показываем последнее количество выбранных строк
|
|
10
|
+
const [lastSelectedCount, setLastSelectedCount] = useState(selectedRows.length);
|
|
11
|
+
if (selectedRows.length > 0 && selectedRows.length !== lastSelectedCount) {
|
|
12
|
+
setLastSelectedCount(selectedRows.length);
|
|
13
|
+
}
|
|
14
|
+
const handleClose = () => onSelectRow?.([]);
|
|
15
|
+
return {
|
|
16
|
+
isOpen,
|
|
17
|
+
isMounted,
|
|
18
|
+
selectedCount: lastSelectedCount,
|
|
19
|
+
handleClose,
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import { type IconButtonProps } from '../IconButton';
|
|
3
|
+
import { type MenuProps } from '../Menu';
|
|
3
4
|
export type IconDropdownButtonProps = IconButtonProps & {
|
|
4
5
|
/**
|
|
5
6
|
* Иконка кнопки
|
|
6
7
|
*/
|
|
7
8
|
icon: ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* Пропсы выпадающего меню
|
|
11
|
+
*/
|
|
12
|
+
popoverProps?: Omit<MenuProps, 'open' | 'anchorEl' | 'title' | 'children'>;
|
|
8
13
|
};
|
|
9
14
|
export declare const IconDropdownButton: import("react").ForwardRefExoticComponent<Omit<IconDropdownButtonProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -4,7 +4,7 @@ import { forwardRef } from 'react';
|
|
|
4
4
|
import { IconButton } from '../IconButton';
|
|
5
5
|
import { Menu } from '../Menu';
|
|
6
6
|
import { useMenu } from '../useMenu';
|
|
7
|
-
export const IconDropdownButton = forwardRef(({ children, icon, title, ...props }, ref) => {
|
|
7
|
+
export const IconDropdownButton = forwardRef(({ children, icon, title, popoverProps, ...props }, ref) => {
|
|
8
8
|
const { open, anchorRef, handleOpenMenu, handleCloseMenu } = useMenu(ref);
|
|
9
|
-
return (_jsxs(_Fragment, { children: [_jsx(ClickAwayListener, { onClickAway: handleCloseMenu, mouseEvent: "onMouseUp", children: _jsx(IconButton, { ...props, ref: anchorRef, onClick: handleOpenMenu, selected: open, title: title, children: icon }) }), _jsx(Menu, { title: title, open: open, anchorEl: anchorRef.current, children: children })] }));
|
|
9
|
+
return (_jsxs(_Fragment, { children: [_jsx(ClickAwayListener, { onClickAway: handleCloseMenu, mouseEvent: "onMouseUp", children: _jsx(IconButton, { ...props, ref: anchorRef, onClick: handleOpenMenu, selected: open, title: title, children: icon }) }), _jsx(Menu, { ...popoverProps, title: title, open: open, anchorEl: anchorRef.current, children: children })] }));
|
|
10
10
|
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type MassActionsProps } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Базовый компонент панели массовых действий
|
|
4
|
+
*/
|
|
5
|
+
export declare const MassActions: ({ main, secondary, selectedCount, entityDeclension, onClose, className, }: MassActionsProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CrossOutlineMd } from '../../icons/CrossOutlineMd';
|
|
3
|
+
import { IconButton } from '../IconButton';
|
|
4
|
+
import { Tag } from '../Tag';
|
|
5
|
+
import { SECONDARY_POPOVER_PROPS } from './constants';
|
|
6
|
+
import { ReservedLabel, Selection, SelectionLabel, StyledActionGroup, Wrapper, } from './styles';
|
|
7
|
+
import { useLogic } from './useLogic';
|
|
8
|
+
/**
|
|
9
|
+
* Базовый компонент панели массовых действий
|
|
10
|
+
*/
|
|
11
|
+
export const MassActions = ({ main, secondary, selectedCount, entityDeclension, onClose, className, }) => {
|
|
12
|
+
const { selectionLabel, reservedLabel } = useLogic({
|
|
13
|
+
entityDeclension,
|
|
14
|
+
selectedCount,
|
|
15
|
+
});
|
|
16
|
+
return (_jsxs(Wrapper, { className: className, children: [_jsx(StyledActionGroup, { main: main, secondary: secondary, secondaryPopoverProps: SECONDARY_POPOVER_PROPS, size: "large" }), _jsxs(Selection, { children: [_jsxs(SelectionLabel, { children: [_jsx(ReservedLabel, { variant: "text", size: "large", label: reservedLabel, "aria-hidden": true }), _jsx(Tag, { variant: "text", size: "large", label: selectionLabel, "aria-live": "polite" })] }), _jsx(IconButton, { variant: "text", "aria-label": "\u0421\u043D\u044F\u0442\u044C \u0432\u044B\u0434\u0435\u043B\u0435\u043D\u0438\u0435", onClick: onClose, children: _jsx(CrossOutlineMd, {}) })] })] }));
|
|
17
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type IconDropdownButtonProps } from '../IconDropdownButton';
|
|
2
|
+
export declare const SECONDARY_POPOVER_PROPS: IconDropdownButtonProps['popoverProps'];
|
|
3
|
+
/**
|
|
4
|
+
* Количество выбранных строк, под которое заранее резервируется ширина счётчика
|
|
5
|
+
*/
|
|
6
|
+
export declare const RESERVED_SELECTED_COUNT = 999;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Смещение меню вверх от кнопки. Компенсирует margin-top 8px у меню из темы и оставляет отступ 8px до кнопки
|
|
3
|
+
*/
|
|
4
|
+
const ANCHOR_VERTICAL_POSITION = -16;
|
|
5
|
+
export const SECONDARY_POPOVER_PROPS = {
|
|
6
|
+
anchorOrigin: { vertical: ANCHOR_VERTICAL_POSITION, horizontal: 'left' },
|
|
7
|
+
transformOrigin: { vertical: 'bottom', horizontal: 'left' },
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Количество выбранных строк, под которое заранее резервируется ширина счётчика
|
|
11
|
+
*/
|
|
12
|
+
export const RESERVED_SELECTED_COUNT = 999;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { MassActions } from './MassActions';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
export declare const Wrapper: import("../styled").StyledComponent<{
|
|
3
|
+
theme?: import("@emotion/react").Theme | undefined;
|
|
4
|
+
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
5
|
+
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
6
|
+
export declare const Selection: import("../styled").StyledComponent<{
|
|
7
|
+
theme?: import("@emotion/react").Theme | undefined;
|
|
8
|
+
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
9
|
+
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
10
|
+
export declare const StyledActionGroup: import("../styled").StyledComponent<import("../ActionGroup").ActionGroupProps<import("react").ElementType, import("react").ElementType> & {
|
|
11
|
+
theme?: import("@emotion/react").Theme | undefined;
|
|
12
|
+
}, {}, {}>;
|
|
13
|
+
/**
|
|
14
|
+
* Невидимая заглушка и счётчик лежат в одной ячейке: ширину задаёт заглушка, поэтому панель не прыгает при смене количества
|
|
15
|
+
*/
|
|
16
|
+
export declare const SelectionLabel: import("../styled").StyledComponent<{
|
|
17
|
+
theme?: import("@emotion/react").Theme | undefined;
|
|
18
|
+
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
19
|
+
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
20
|
+
export declare const ReservedLabel: import("../styled").StyledComponent<{
|
|
21
|
+
disabled?: boolean | undefined;
|
|
22
|
+
label?: import("react").ReactNode;
|
|
23
|
+
icon?: import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
24
|
+
className?: string | undefined;
|
|
25
|
+
onDelete?: ((event: import("react").SyntheticEvent<Element, Event>) => void) | undefined;
|
|
26
|
+
color?: import("../Tag").TagColor | undefined;
|
|
27
|
+
variant?: import("../Tag").TagVariant | undefined;
|
|
28
|
+
size?: import("../Tag").TagSize | undefined;
|
|
29
|
+
startAddon?: import("../Tag").TagAddon | undefined;
|
|
30
|
+
endAddon?: import("../Tag").TagAddon | undefined;
|
|
31
|
+
style?: import("react").CSSProperties | undefined;
|
|
32
|
+
} & import("react").HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement> & {
|
|
33
|
+
theme?: import("@emotion/react").Theme | undefined;
|
|
34
|
+
}, {}, {}>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ActionGroup } from '../ActionGroup';
|
|
2
|
+
import { styled } from '../styled';
|
|
3
|
+
import { Tag } from '../Tag';
|
|
4
|
+
export const Wrapper = styled.div `
|
|
5
|
+
display: flex;
|
|
6
|
+
gap: ${({ theme }) => theme.spacing(3)};
|
|
7
|
+
align-items: center;
|
|
8
|
+
|
|
9
|
+
width: fit-content;
|
|
10
|
+
padding: ${({ theme }) => theme.spacing(4)};
|
|
11
|
+
|
|
12
|
+
background-color: ${({ theme }) => theme.palette.background.default};
|
|
13
|
+
|
|
14
|
+
/* 16px по макету, токена под это значение в теме нет */
|
|
15
|
+
border-radius: calc(${({ theme }) => theme.shape.medium} * 2);
|
|
16
|
+
box-shadow: ${({ theme }) => theme.elevation[200]};
|
|
17
|
+
`;
|
|
18
|
+
export const Selection = styled.div `
|
|
19
|
+
display: flex;
|
|
20
|
+
gap: ${({ theme }) => theme.spacing(2)};
|
|
21
|
+
align-items: center;
|
|
22
|
+
|
|
23
|
+
padding-left: ${({ theme }) => theme.spacing(2)};
|
|
24
|
+
`;
|
|
25
|
+
export const StyledActionGroup = styled(ActionGroup) `
|
|
26
|
+
gap: ${({ theme }) => theme.spacing(3)};
|
|
27
|
+
`;
|
|
28
|
+
/**
|
|
29
|
+
* Невидимая заглушка и счётчик лежат в одной ячейке: ширину задаёт заглушка, поэтому панель не прыгает при смене количества
|
|
30
|
+
*/
|
|
31
|
+
export const SelectionLabel = styled.div `
|
|
32
|
+
display: grid;
|
|
33
|
+
justify-items: start;
|
|
34
|
+
|
|
35
|
+
font-variant-numeric: tabular-nums;
|
|
36
|
+
|
|
37
|
+
& > * {
|
|
38
|
+
grid-area: 1 / 1;
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
export const ReservedLabel = styled(Tag) `
|
|
42
|
+
visibility: hidden;
|
|
43
|
+
`;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ActionGroupProps } from '../ActionGroup';
|
|
2
|
+
export type MassActionsProps = Pick<ActionGroupProps, 'main' | 'secondary'> & {
|
|
3
|
+
/**
|
|
4
|
+
* Количество выбранных элементов
|
|
5
|
+
*/
|
|
6
|
+
selectedCount: number;
|
|
7
|
+
/**
|
|
8
|
+
* Три формы названия выбранной сущности
|
|
9
|
+
* @example ['документ', 'документа', 'документов']
|
|
10
|
+
*/
|
|
11
|
+
entityDeclension: [string, string, string];
|
|
12
|
+
/**
|
|
13
|
+
* Обработчик закрытия панели
|
|
14
|
+
*/
|
|
15
|
+
onClose: () => void;
|
|
16
|
+
/**
|
|
17
|
+
* Название класса, применяется к корневому компоненту
|
|
18
|
+
*/
|
|
19
|
+
className?: string;
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useLogic';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useLogic';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type MassActionsProps } from '../types';
|
|
2
|
+
type UseLogicParams = Pick<MassActionsProps, 'entityDeclension' | 'selectedCount'>;
|
|
3
|
+
export declare const useLogic: ({ entityDeclension, selectedCount, }: UseLogicParams) => {
|
|
4
|
+
selectionLabel: string;
|
|
5
|
+
reservedLabel: string;
|
|
6
|
+
};
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { declensionOfWords } from '@astral/utils/declension/declensionOfWords';
|
|
2
|
+
import { RESERVED_SELECTED_COUNT } from '../constants';
|
|
3
|
+
export const useLogic = ({ entityDeclension, selectedCount, }) => {
|
|
4
|
+
const selectionLabel = `Выбрано: ${selectedCount} ${declensionOfWords(entityDeclension)(selectedCount)}`;
|
|
5
|
+
// Самая длинная форма слова — чтобы ширина панели не менялась при смене склонения
|
|
6
|
+
const longestEntity = entityDeclension.reduce((longest, entity) => entity.length > longest.length ? entity : longest);
|
|
7
|
+
const reservedLabel = `Выбрано: ${RESERVED_SELECTED_COUNT} ${longestEntity}`;
|
|
8
|
+
return { selectionLabel, reservedLabel };
|
|
9
|
+
};
|
|
@@ -9,4 +9,4 @@ export declare const StyledMobileButton: import("../../../styled").StyledCompone
|
|
|
9
9
|
} & Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, ""> & import("react").RefAttributes<HTMLButtonElement> & {
|
|
10
10
|
theme?: import("@emotion/react").Theme | undefined;
|
|
11
11
|
}, {}, {}>;
|
|
12
|
-
export declare const StyledSecondaryActions: <TSecondaryActionComponent extends import("react").ElementType = import("react").ElementType>({ actions, isDisabled, className, size, }: import("../../../ActionGroup").SecondaryActionProps<TSecondaryActionComponent>) => import("react/jsx-runtime").JSX.Element | null;
|
|
12
|
+
export declare const StyledSecondaryActions: <TSecondaryActionComponent extends import("react").ElementType = import("react").ElementType>({ actions, isDisabled, className, size, popoverProps, }: import("../../../ActionGroup").SecondaryActionProps<TSecondaryActionComponent>) => import("react/jsx-runtime").JSX.Element | null;
|
package/components/Tree/TreeItem/MobileSlidePanel/MobileSlidePanelContent/useLogic/useLogic.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useContext } from 'react';
|
|
2
2
|
import { useTheme } from '../../../../../theme/hooks';
|
|
3
|
+
import { useDelayedUnmount } from '../../../../../useDelayedUnmount';
|
|
3
4
|
import { MobileSlidePanelContext } from '../../MobileSlidePanelContext';
|
|
4
|
-
import {
|
|
5
|
+
import { usePanelHeader, useRegisterClose } from './hooks';
|
|
5
6
|
/**
|
|
6
7
|
* Собирает жизненный цикл панели: присутствие в DOM, закрытие и шапку стека.
|
|
7
8
|
*/
|