@toolpad/core
Version:
Dashboard framework powered by Material UI.
386 lines (385 loc) • 14.8 kB
JavaScript
"use strict";
'use client';
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.List = List;
var React = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _material = require("@mui/material");
var _Box = _interopRequireDefault(require("@mui/material/Box"));
var _Button = _interopRequireDefault(require("@mui/material/Button"));
var _IconButton = _interopRequireDefault(require("@mui/material/IconButton"));
var _Stack = _interopRequireDefault(require("@mui/material/Stack"));
var _Tooltip = _interopRequireDefault(require("@mui/material/Tooltip"));
var _Typography = _interopRequireDefault(require("@mui/material/Typography"));
var _xDataGrid = require("@mui/x-data-grid");
var _Add = _interopRequireDefault(require("@mui/icons-material/Add"));
var _Refresh = _interopRequireDefault(require("@mui/icons-material/Refresh"));
var _Edit = _interopRequireDefault(require("@mui/icons-material/Edit"));
var _Delete = _interopRequireDefault(require("@mui/icons-material/Delete"));
var _invariant = _interopRequireDefault(require("invariant"));
var _useDialogs = require("../useDialogs");
var _useNotifications = require("../useNotifications");
var _NoSsr = require("../shared/NoSsr");
var _context = require("../shared/context");
var _LocalizationProvider = require("../AppProvider/LocalizationProvider");
var _cache = require("./cache");
var _useCachedDataSource = require("./useCachedDataSource");
var _localeText = require("./localeText");
var _jsxRuntime = require("react/jsx-runtime");
var _EditIcon, _DeleteIcon, _RefreshIcon, _AddIcon;
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
const ErrorOverlay = (0, _material.styled)('div')(({
theme
}) => ({
position: 'absolute',
backgroundColor: theme.palette.error.light,
borderRadius: '4px',
top: 0,
height: '100%',
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center',
p: 1,
zIndex: 10
}));
/**
*
* Demos:
*
* - [CRUD](https://mui.com/toolpad/core/react-crud/)
*
* API:
*
* - [List API](https://mui.com/toolpad/core/api/list)
*/
function List(props) {
const {
initialPageSize = 100,
onRowClick,
onCreateClick,
onEditClick,
onDelete,
dataSourceCache,
slots,
slotProps,
localeText: propsLocaleText
} = props;
const globalLocaleText = (0, _LocalizationProvider.useLocaleText)();
const localeText = {
..._localeText.CRUD_DEFAULT_LOCALE_TEXT,
...globalLocaleText,
...propsLocaleText
};
const crudContext = React.useContext(_context.CrudContext);
const dataSource = props.dataSource ?? crudContext.dataSource;
(0, _invariant.default)(dataSource, 'No data source found.');
const cache = React.useMemo(() => {
const manualCache = dataSourceCache ?? crudContext.dataSourceCache;
return typeof manualCache !== 'undefined' ? manualCache : new _cache.DataSourceCache();
}, [crudContext.dataSourceCache, dataSourceCache]);
const cachedDataSource = (0, _useCachedDataSource.useCachedDataSource)(dataSource, cache);
const {
fields,
validate,
...methods
} = cachedDataSource;
const {
getMany,
deleteOne
} = methods;
const routerContext = React.useContext(_context.RouterContext);
const appWindowContext = React.useContext(_context.WindowContext);
const appWindow = appWindowContext ?? (typeof window !== 'undefined' ? window : null);
const dialogs = (0, _useDialogs.useDialogs)();
const notifications = (0, _useNotifications.useNotifications)();
const [rowsState, setRowsState] = React.useState({
rows: [],
rowCount: 0
});
const [paginationModel, setPaginationModel] = React.useState({
page: routerContext?.searchParams.get('page') ? Number(routerContext?.searchParams.get('page')) : 0,
pageSize: routerContext?.searchParams.get('pageSize') ? Number(routerContext?.searchParams.get('pageSize')) : initialPageSize
});
const [filterModel, setFilterModel] = React.useState(routerContext?.searchParams.get('filter') ? JSON.parse(routerContext?.searchParams.get('filter') ?? '') : {
items: []
});
const [sortModel, setSortModel] = React.useState(routerContext?.searchParams.get('sort') ? JSON.parse(routerContext?.searchParams.get('sort') ?? '') : []);
const [isLoading, setIsLoading] = React.useState(true);
const [error, setError] = React.useState(null);
React.useEffect(() => {
if (appWindow) {
const url = new URL(appWindow.location.href);
url.searchParams.set('page', String(paginationModel.page));
url.searchParams.set('pageSize', String(paginationModel.pageSize));
if (!appWindow.frameElement) {
appWindow.history.pushState({}, '', url);
}
}
}, [appWindow, paginationModel.page, paginationModel.pageSize]);
React.useEffect(() => {
if (appWindow) {
const url = new URL(appWindow.location.href);
if (filterModel.items.length > 0 || filterModel.quickFilterValues && filterModel.quickFilterValues.length > 0) {
url.searchParams.set('filter', JSON.stringify(filterModel));
} else {
url.searchParams.delete('filter');
}
if (!appWindow.frameElement) {
appWindow.history.pushState({}, '', url);
}
}
}, [appWindow, filterModel]);
React.useEffect(() => {
if (appWindow) {
const url = new URL(appWindow.location.href);
if (sortModel.length > 0) {
url.searchParams.set('sort', JSON.stringify(sortModel));
} else {
url.searchParams.delete('sort');
}
if (!appWindow.frameElement) {
appWindow.history.pushState({}, '', url);
}
}
}, [appWindow, sortModel]);
const loadData = React.useCallback(async () => {
setError(null);
setIsLoading(true);
try {
const listData = await getMany({
paginationModel,
sortModel,
filterModel
});
setRowsState({
rows: listData.items,
rowCount: listData.itemCount
});
} catch (listDataError) {
setError(listDataError);
}
setIsLoading(false);
}, [filterModel, getMany, paginationModel, sortModel]);
React.useEffect(() => {
loadData();
}, [filterModel, getMany, loadData, paginationModel, sortModel]);
const handleRefresh = React.useCallback(() => {
if (!isLoading) {
cache?.clear();
loadData();
}
}, [cache, isLoading, loadData]);
const handleRowClick = React.useCallback(({
row
}) => {
if (onRowClick) {
onRowClick(row.id);
}
}, [onRowClick]);
const handleItemEdit = React.useCallback(itemId => () => {
if (onEditClick) {
onEditClick(itemId);
}
}, [onEditClick]);
const handleItemDelete = React.useCallback(itemId => async () => {
const confirmed = await dialogs.confirm(localeText.deleteConfirmMessage, {
title: localeText.deleteConfirmTitle,
severity: 'error',
okText: localeText.deleteConfirmLabel,
cancelText: localeText.deleteCancelLabel
});
if (confirmed) {
setIsLoading(true);
try {
await deleteOne?.(itemId);
if (onDelete) {
onDelete(itemId);
}
notifications.show(localeText.deleteSuccessMessage, {
severity: 'success',
autoHideDuration: 3000
});
loadData();
} catch (deleteError) {
notifications.show(`${localeText.deleteErrorMessage} ${deleteError.message}`, {
severity: 'error',
autoHideDuration: 3000
});
}
setIsLoading(false);
}
}, [deleteOne, dialogs, loadData, localeText.deleteCancelLabel, localeText.deleteConfirmLabel, localeText.deleteConfirmMessage, localeText.deleteConfirmTitle, localeText.deleteErrorMessage, localeText.deleteSuccessMessage, notifications, onDelete]);
const DataGridSlot = slots?.dataGrid ?? _xDataGrid.DataGrid;
const initialState = React.useMemo(() => ({
pagination: {
paginationModel: {
pageSize: initialPageSize
}
}
}), [initialPageSize]);
const columns = React.useMemo(() => {
return [...fields, {
field: 'actions',
type: 'actions',
flex: 1,
align: 'right',
getActions: ({
id
}) => [...(onEditClick ? [/*#__PURE__*/(0, _jsxRuntime.jsx)(_xDataGrid.GridActionsCellItem, {
icon: _EditIcon || (_EditIcon = /*#__PURE__*/(0, _jsxRuntime.jsx)(_Edit.default, {})),
label: localeText.editLabel,
onClick: handleItemEdit(id)
}, "edit-item")] : []), ...(deleteOne ? [/*#__PURE__*/(0, _jsxRuntime.jsx)(_xDataGrid.GridActionsCellItem, {
icon: _DeleteIcon || (_DeleteIcon = /*#__PURE__*/(0, _jsxRuntime.jsx)(_Delete.default, {})),
label: localeText.deleteLabel,
onClick: handleItemDelete(id)
}, "delete-item")] : [])]
}];
}, [deleteOne, fields, handleItemDelete, handleItemEdit, localeText.deleteLabel, localeText.editLabel, onEditClick]);
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Stack.default, {
sx: {
flex: 1,
width: '100%'
},
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_Stack.default, {
direction: "row",
alignItems: "center",
justifyContent: "space-between",
sx: {
mb: 1
},
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, {
title: localeText.reloadButtonLabel,
placement: "right",
enterDelay: 1000,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_IconButton.default, {
"aria-label": "refresh",
onClick: handleRefresh,
children: _RefreshIcon || (_RefreshIcon = /*#__PURE__*/(0, _jsxRuntime.jsx)(_Refresh.default, {}))
})
})
}), onCreateClick ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
variant: "contained",
onClick: onCreateClick,
startIcon: _AddIcon || (_AddIcon = /*#__PURE__*/(0, _jsxRuntime.jsx)(_Add.default, {})),
children: localeText.createNewButtonLabel
}) : null]
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Box.default, {
sx: {
flex: 1,
position: 'relative',
width: '100%'
},
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_NoSsr.NoSsr, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(DataGridSlot, {
rows: rowsState.rows,
rowCount: rowsState.rowCount,
columns: columns,
pagination: true,
sortingMode: "server",
filterMode: "server",
paginationMode: "server",
paginationModel: paginationModel,
onPaginationModelChange: setPaginationModel,
sortModel: sortModel,
onSortModelChange: setSortModel,
filterModel: filterModel,
onFilterModelChange: setFilterModel,
onRowClick: handleRowClick,
loading: isLoading,
initialState: initialState,
slots: {
toolbar: _xDataGrid.GridToolbar
}
// Prevent type conflicts if slotProps don't match DataGrid used for dataGrid slot
,
...slotProps?.dataGrid,
sx: {
[`& .${_xDataGrid.gridClasses.columnHeader}, & .${_xDataGrid.gridClasses.cell}`]: {
outline: 'transparent'
},
[`& .${_xDataGrid.gridClasses.columnHeader}:focus-within, & .${_xDataGrid.gridClasses.cell}:focus-within`]: {
outline: 'none'
},
...(onRowClick ? {
[`& .${_xDataGrid.gridClasses.row}:hover`]: {
cursor: 'pointer'
}
} : {}),
...slotProps?.dataGrid?.sx
}
})
}), error && /*#__PURE__*/(0, _jsxRuntime.jsx)(ErrorOverlay, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Typography.default, {
variant: "body1",
children: error.message
})
})]
})]
});
}
process.env.NODE_ENV !== "production" ? List.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
// │ These PropTypes are generated from the TypeScript type definitions. │
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
// └─────────────────────────────────────────────────────────────────────┘
/**
* Server-side [data source](https://mui.com/toolpad/core/react-crud/#data-sources).
*/
dataSource: _propTypes.default.object,
/**
* [Cache](https://mui.com/toolpad/core/react-crud/#data-caching) for the data source.
*/
dataSourceCache: _propTypes.default.shape({
cache: _propTypes.default.object.isRequired,
clear: _propTypes.default.func.isRequired,
get: _propTypes.default.func.isRequired,
set: _propTypes.default.func.isRequired,
ttl: _propTypes.default.number.isRequired
}),
/**
* Initial number of rows to show per page.
* @default 100
*/
initialPageSize: _propTypes.default.number,
/**
* Locale text for the component.
*/
localeText: _propTypes.default.object,
/**
* Callback fired when the "Create" button is clicked.
*/
onCreateClick: _propTypes.default.func,
/**
* Callback fired when the item is successfully deleted.
*/
onDelete: _propTypes.default.func,
/**
* Callback fired when the "Edit" button is clicked.
*/
onEditClick: _propTypes.default.func,
/**
* Callback fired when a row is clicked. Not called if the target clicked is an interactive element added by the built-in columns.
*/
onRowClick: _propTypes.default.func,
/**
* The props used for each slot inside.
* @default {}
*/
slotProps: _propTypes.default.shape({
dataGrid: _propTypes.default.object
}),
/**
* The components used for each slot inside.
* @default {}
*/
slots: _propTypes.default.shape({
dataGrid: _propTypes.default.func
})
} : void 0;