mirror of
https://github.com/portainer/portainer.git
synced 2025-07-20 13:59:40 +02:00
refactor(app): move react components to react codebase [EE-3179] (#6971)
This commit is contained in:
parent
212400c283
commit
18252ab854
346 changed files with 642 additions and 644 deletions
478
app/react/components/datatables/useRowSelect.ts
Normal file
478
app/react/components/datatables/useRowSelect.ts
Normal file
|
@ -0,0 +1,478 @@
|
|||
/* eslint no-param-reassign: ["error", { "props": false }] */
|
||||
import { ChangeEvent, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
actions,
|
||||
makePropGetter,
|
||||
ensurePluginOrder,
|
||||
useGetLatest,
|
||||
useMountedLayoutEffect,
|
||||
Hooks,
|
||||
TableInstance,
|
||||
TableState,
|
||||
ActionType,
|
||||
ReducerTableState,
|
||||
IdType,
|
||||
Row,
|
||||
PropGetter,
|
||||
TableToggleRowsSelectedProps,
|
||||
TableToggleAllRowsSelectedProps,
|
||||
} from 'react-table';
|
||||
|
||||
type DefaultType = Record<string, unknown>;
|
||||
|
||||
interface UseRowSelectTableInstance<D extends DefaultType = DefaultType>
|
||||
extends TableInstance<D> {
|
||||
isAllRowSelected: boolean;
|
||||
selectSubRows: boolean;
|
||||
getSubRows(row: Row<D>): Row<D>[];
|
||||
isRowSelectable(row: Row<D>): boolean;
|
||||
}
|
||||
|
||||
const pluginName = 'useRowSelect';
|
||||
|
||||
// Actions
|
||||
actions.resetSelectedRows = 'resetSelectedRows';
|
||||
actions.toggleAllRowsSelected = 'toggleAllRowsSelected';
|
||||
actions.toggleRowSelected = 'toggleRowSelected';
|
||||
actions.toggleAllPageRowsSelected = 'toggleAllPageRowsSelected';
|
||||
|
||||
export function useRowSelect<D extends DefaultType>(hooks: Hooks<D>) {
|
||||
hooks.getToggleRowSelectedProps = [
|
||||
defaultGetToggleRowSelectedProps as PropGetter<
|
||||
D,
|
||||
TableToggleRowsSelectedProps
|
||||
>,
|
||||
];
|
||||
hooks.getToggleAllRowsSelectedProps = [
|
||||
defaultGetToggleAllRowsSelectedProps as PropGetter<
|
||||
D,
|
||||
TableToggleAllRowsSelectedProps
|
||||
>,
|
||||
];
|
||||
hooks.getToggleAllPageRowsSelectedProps = [
|
||||
defaultGetToggleAllPageRowsSelectedProps as PropGetter<
|
||||
D,
|
||||
TableToggleAllRowsSelectedProps
|
||||
>,
|
||||
];
|
||||
hooks.stateReducers.push(
|
||||
reducer as (
|
||||
newState: TableState<D>,
|
||||
action: ActionType,
|
||||
previousState?: TableState<D>,
|
||||
instance?: TableInstance<D>
|
||||
) => ReducerTableState<D> | undefined
|
||||
);
|
||||
hooks.useInstance.push(useInstance as (instance: TableInstance<D>) => void);
|
||||
hooks.prepareRow.push(prepareRow);
|
||||
}
|
||||
|
||||
useRowSelect.pluginName = pluginName;
|
||||
|
||||
function defaultGetToggleRowSelectedProps<D extends DefaultType>(
|
||||
props: D,
|
||||
{ instance, row }: { instance: UseRowSelectTableInstance<D>; row: Row<D> }
|
||||
) {
|
||||
const { manualRowSelectedKey = 'isSelected' } = instance;
|
||||
let checked = false;
|
||||
|
||||
if (row.original && row.original[manualRowSelectedKey]) {
|
||||
checked = true;
|
||||
} else {
|
||||
checked = row.isSelected;
|
||||
}
|
||||
|
||||
return [
|
||||
props,
|
||||
{
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => {
|
||||
row.toggleRowSelected(e.target.checked);
|
||||
},
|
||||
style: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
checked,
|
||||
title: 'Toggle Row Selected',
|
||||
indeterminate: row.isSomeSelected,
|
||||
disabled: !instance.isRowSelectable(row),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function defaultGetToggleAllRowsSelectedProps<D extends DefaultType>(
|
||||
props: D,
|
||||
{ instance }: { instance: UseRowSelectTableInstance<D> }
|
||||
) {
|
||||
return [
|
||||
props,
|
||||
{
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => {
|
||||
instance.toggleAllRowsSelected(e.target.checked);
|
||||
},
|
||||
style: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
checked: instance.isAllRowsSelected,
|
||||
title: 'Toggle All Rows Selected',
|
||||
indeterminate: Boolean(
|
||||
!instance.isAllRowsSelected &&
|
||||
Object.keys(instance.state.selectedRowIds).length
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function defaultGetToggleAllPageRowsSelectedProps<D extends DefaultType>(
|
||||
props: D,
|
||||
{ instance }: { instance: UseRowSelectTableInstance<D> }
|
||||
) {
|
||||
return [
|
||||
props,
|
||||
{
|
||||
onChange(e: ChangeEvent<HTMLInputElement>) {
|
||||
instance.toggleAllPageRowsSelected(e.target.checked);
|
||||
},
|
||||
style: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
checked: instance.isAllPageRowsSelected,
|
||||
title: 'Toggle All Current Page Rows Selected',
|
||||
indeterminate: Boolean(
|
||||
!instance.isAllPageRowsSelected &&
|
||||
instance.page.some(({ id }) => instance.state.selectedRowIds[id])
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function reducer<D extends Record<string, unknown>>(
|
||||
state: TableState<D>,
|
||||
action: ActionType,
|
||||
_previousState?: TableState<D>,
|
||||
instance?: UseRowSelectTableInstance<D>
|
||||
) {
|
||||
if (action.type === actions.init) {
|
||||
return {
|
||||
...state,
|
||||
selectedRowIds: <Record<IdType<D>, boolean>>{},
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === actions.resetSelectedRows) {
|
||||
return {
|
||||
...state,
|
||||
selectedRowIds: instance?.initialState.selectedRowIds || {},
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === actions.toggleAllRowsSelected) {
|
||||
const { value: setSelected } = action;
|
||||
|
||||
if (!instance) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const {
|
||||
isAllRowsSelected,
|
||||
rowsById,
|
||||
nonGroupedRowsById = rowsById,
|
||||
isRowSelectable = defaultIsRowSelectable,
|
||||
} = instance;
|
||||
|
||||
const selectAll =
|
||||
typeof setSelected !== 'undefined' ? setSelected : !isAllRowsSelected;
|
||||
|
||||
// Only remove/add the rows that are visible on the screen
|
||||
// Leave all the other rows that are selected alone.
|
||||
const selectedRowIds = { ...state.selectedRowIds };
|
||||
|
||||
Object.keys(nonGroupedRowsById).forEach((rowId: IdType<D>) => {
|
||||
if (selectAll) {
|
||||
const row = rowsById[rowId];
|
||||
if (isRowSelectable(row)) {
|
||||
selectedRowIds[rowId] = true;
|
||||
}
|
||||
} else {
|
||||
delete selectedRowIds[rowId];
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
selectedRowIds,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === actions.toggleRowSelected) {
|
||||
if (!instance) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const { id, value: setSelected } = action;
|
||||
const {
|
||||
rowsById,
|
||||
selectSubRows = true,
|
||||
getSubRows,
|
||||
isRowSelectable = defaultIsRowSelectable,
|
||||
} = instance;
|
||||
|
||||
const isSelected = state.selectedRowIds[id];
|
||||
const shouldExist =
|
||||
typeof setSelected !== 'undefined' ? setSelected : !isSelected;
|
||||
|
||||
if (isSelected === shouldExist) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const newSelectedRowIds = { ...state.selectedRowIds };
|
||||
|
||||
// eslint-disable-next-line no-inner-declarations
|
||||
function handleRowById(id: IdType<D>) {
|
||||
const row = rowsById[id];
|
||||
|
||||
if (!isRowSelectable(row)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!row.isGrouped) {
|
||||
if (shouldExist) {
|
||||
newSelectedRowIds[id] = true;
|
||||
} else {
|
||||
delete newSelectedRowIds[id];
|
||||
}
|
||||
}
|
||||
|
||||
if (selectSubRows && getSubRows(row)) {
|
||||
getSubRows(row).forEach((row) => handleRowById(row.id));
|
||||
}
|
||||
}
|
||||
|
||||
handleRowById(id);
|
||||
|
||||
return {
|
||||
...state,
|
||||
selectedRowIds: newSelectedRowIds,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === actions.toggleAllPageRowsSelected) {
|
||||
if (!instance) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const { value: setSelected } = action;
|
||||
const {
|
||||
page,
|
||||
rowsById,
|
||||
selectSubRows = true,
|
||||
isAllPageRowsSelected,
|
||||
getSubRows,
|
||||
} = instance;
|
||||
|
||||
const selectAll =
|
||||
typeof setSelected !== 'undefined' ? setSelected : !isAllPageRowsSelected;
|
||||
|
||||
const newSelectedRowIds = { ...state.selectedRowIds };
|
||||
|
||||
// eslint-disable-next-line no-inner-declarations
|
||||
function handleRowById(id: IdType<D>) {
|
||||
const row = rowsById[id];
|
||||
|
||||
if (!row.isGrouped) {
|
||||
if (selectAll) {
|
||||
newSelectedRowIds[id] = true;
|
||||
} else {
|
||||
delete newSelectedRowIds[id];
|
||||
}
|
||||
}
|
||||
|
||||
if (selectSubRows && getSubRows(row)) {
|
||||
getSubRows(row).forEach((row) => handleRowById(row.id));
|
||||
}
|
||||
}
|
||||
|
||||
page.forEach((row) => handleRowById(row.id));
|
||||
|
||||
return {
|
||||
...state,
|
||||
selectedRowIds: newSelectedRowIds,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
function useInstance<D extends Record<string, unknown>>(
|
||||
instance: UseRowSelectTableInstance<D>
|
||||
) {
|
||||
const {
|
||||
data,
|
||||
rows,
|
||||
getHooks,
|
||||
plugins,
|
||||
rowsById,
|
||||
nonGroupedRowsById = rowsById,
|
||||
autoResetSelectedRows = true,
|
||||
state: { selectedRowIds },
|
||||
selectSubRows = true,
|
||||
dispatch,
|
||||
page,
|
||||
getSubRows,
|
||||
isRowSelectable,
|
||||
} = instance;
|
||||
|
||||
ensurePluginOrder(
|
||||
plugins,
|
||||
['useFilters', 'useGroupBy', 'useSortBy', 'useExpanded', 'usePagination'],
|
||||
'useRowSelect'
|
||||
);
|
||||
|
||||
const selectedFlatRows = useMemo(() => {
|
||||
const selectedFlatRows = <Array<Row<D>>>[];
|
||||
|
||||
rows.forEach((row) => {
|
||||
const isSelected = selectSubRows
|
||||
? getRowIsSelected(row, selectedRowIds, getSubRows)
|
||||
: !!selectedRowIds[row.id];
|
||||
row.isSelected = !!isSelected;
|
||||
row.isSomeSelected = isSelected === null;
|
||||
|
||||
if (isSelected) {
|
||||
selectedFlatRows.push(row);
|
||||
}
|
||||
});
|
||||
|
||||
return selectedFlatRows;
|
||||
}, [rows, selectSubRows, selectedRowIds, getSubRows]);
|
||||
|
||||
let isAllRowsSelected = Boolean(
|
||||
Object.keys(nonGroupedRowsById).length && Object.keys(selectedRowIds).length
|
||||
);
|
||||
|
||||
let isAllPageRowsSelected = isAllRowsSelected;
|
||||
|
||||
if (isAllRowsSelected) {
|
||||
if (
|
||||
Object.keys(nonGroupedRowsById).some((id) => {
|
||||
const row = rowsById[id];
|
||||
|
||||
return !selectedRowIds[id] && isRowSelectable(row);
|
||||
})
|
||||
) {
|
||||
isAllRowsSelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAllRowsSelected) {
|
||||
if (
|
||||
page &&
|
||||
page.length &&
|
||||
page.some(({ id }) => {
|
||||
const row = rowsById[id];
|
||||
|
||||
return !selectedRowIds[id] && isRowSelectable(row);
|
||||
})
|
||||
) {
|
||||
isAllPageRowsSelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
const getAutoResetSelectedRows = useGetLatest(autoResetSelectedRows);
|
||||
|
||||
useMountedLayoutEffect(() => {
|
||||
if (getAutoResetSelectedRows()) {
|
||||
dispatch({ type: actions.resetSelectedRows });
|
||||
}
|
||||
}, [dispatch, data]);
|
||||
|
||||
const toggleAllRowsSelected = useCallback(
|
||||
(value) => dispatch({ type: actions.toggleAllRowsSelected, value }),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const toggleAllPageRowsSelected = useCallback(
|
||||
(value) => dispatch({ type: actions.toggleAllPageRowsSelected, value }),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const toggleRowSelected = useCallback(
|
||||
(id, value) => dispatch({ type: actions.toggleRowSelected, id, value }),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const getInstance = useGetLatest(instance);
|
||||
|
||||
const getToggleAllRowsSelectedProps = makePropGetter(
|
||||
getHooks().getToggleAllRowsSelectedProps,
|
||||
{ instance: getInstance() }
|
||||
);
|
||||
|
||||
const getToggleAllPageRowsSelectedProps = makePropGetter(
|
||||
getHooks().getToggleAllPageRowsSelectedProps,
|
||||
{ instance: getInstance() }
|
||||
);
|
||||
|
||||
Object.assign(instance, {
|
||||
selectedFlatRows,
|
||||
isAllRowsSelected,
|
||||
isAllPageRowsSelected,
|
||||
toggleRowSelected,
|
||||
toggleAllRowsSelected,
|
||||
getToggleAllRowsSelectedProps,
|
||||
getToggleAllPageRowsSelectedProps,
|
||||
toggleAllPageRowsSelected,
|
||||
});
|
||||
}
|
||||
|
||||
function prepareRow<D extends Record<string, unknown>>(
|
||||
row: Row<D>,
|
||||
{ instance }: { instance: TableInstance<D> }
|
||||
) {
|
||||
row.toggleRowSelected = (set) => instance.toggleRowSelected(row.id, set);
|
||||
|
||||
row.getToggleRowSelectedProps = makePropGetter(
|
||||
instance.getHooks().getToggleRowSelectedProps,
|
||||
{ instance, row }
|
||||
);
|
||||
}
|
||||
|
||||
function getRowIsSelected<D extends Record<string, unknown>>(
|
||||
row: Row<D>,
|
||||
selectedRowIds: Record<IdType<D>, boolean>,
|
||||
getSubRows: (row: Row<D>) => Array<Row<D>>
|
||||
) {
|
||||
if (selectedRowIds[row.id]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const subRows = getSubRows(row);
|
||||
|
||||
if (subRows && subRows.length) {
|
||||
let allChildrenSelected = true;
|
||||
let someSelected = false;
|
||||
|
||||
subRows.forEach((subRow) => {
|
||||
// Bail out early if we know both of these
|
||||
if (someSelected && !allChildrenSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (getRowIsSelected(subRow, selectedRowIds, getSubRows)) {
|
||||
someSelected = true;
|
||||
} else {
|
||||
allChildrenSelected = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (allChildrenSelected) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return someSelected ? null : false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function defaultIsRowSelectable<D extends DefaultType>(row: Row<D>) {
|
||||
return !!row.original.disabled;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue