@mui/x-data-grid-premium
Version:
The Premium plan edition of the MUI X Data Grid Components.
368 lines (366 loc) • 15.6 kB
JavaScript
"use strict";
'use client';
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.useGridAiAssistant = exports.aiAssistantStateInitializer = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var React = _interopRequireWildcard(require("react"));
var _isDeepEqual = require("@mui/x-internals/isDeepEqual");
var _xDataGridPro = require("@mui/x-data-grid-pro");
var _internals = require("@mui/x-data-grid-pro/internals");
var _gridAiAssistantSelectors = require("./gridAiAssistantSelectors");
var _gridChartsIntegrationSelectors = require("../chartsIntegration/gridChartsIntegrationSelectors");
var _jsxRuntime = require("react/jsx-runtime");
const DEFAULT_SAMPLE_COUNT = 5;
const MAX_CHART_DATA_POINTS = 1000;
const aiAssistantStateInitializer = (state, props) => {
if (!props.aiAssistant) {
return (0, _extends2.default)({}, state, {
aiAssistant: {
activeConversationIndex: 0,
conversations: []
}
});
}
return (0, _extends2.default)({}, state, {
aiAssistant: {
activeConversationIndex: 0,
conversations: props.aiAssistantConversations ?? props.initialState?.aiAssistant?.conversations ?? []
}
});
};
exports.aiAssistantStateInitializer = aiAssistantStateInitializer;
const useGridAiAssistant = (apiRef, props) => {
const {
onPrompt,
allowAiAssistantDataSampling,
slots,
rowSelection,
disableColumnFilter,
disableRowGrouping,
disableAggregation,
disableColumnSorting,
disablePivoting,
chartsIntegration,
experimentalFeatures,
getPivotDerivedColumns
} = props;
const previousUnwrappedGroupingModel = React.useRef([]);
const activeChartId = (0, _gridChartsIntegrationSelectors.gridChartsIntegrationActiveChartIdSelector)(apiRef);
const columnsLookup = (0, _xDataGridPro.gridColumnLookupSelector)(apiRef);
const columns = Object.values(columnsLookup);
const rows = Object.values((0, _xDataGridPro.gridRowsLookupSelector)(apiRef));
const isAiAssistantAvailable = !!props.aiAssistant;
apiRef.current.registerControlState({
stateId: 'aiAssistantConversations',
propModel: props.aiAssistantConversations,
propOnChange: props.onAiAssistantConversationsChange,
stateSelector: _gridAiAssistantSelectors.gridAiAssistantConversationsSelector,
changeEvent: 'aiAssistantConversationsChange'
});
apiRef.current.registerControlState({
stateId: 'aiAssistantActiveConversationIndex',
propModel: props.aiAssistantActiveConversationIndex,
propOnChange: props.onAiAssistantActiveConversationIndexChange,
stateSelector: _gridAiAssistantSelectors.gridAiAssistantActiveConversationIndexSelector,
changeEvent: 'aiAssistantActiveConversationIndexChange'
});
const preferencePanelPreProcessing = React.useCallback((initialValue, value) => {
if (isAiAssistantAvailable && slots.aiAssistantPanel && value === _xDataGridPro.GridPreferencePanelsValue.aiAssistant) {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(slots.aiAssistantPanel, {});
}
return initialValue;
}, [isAiAssistantAvailable, slots]);
const collectSampleData = React.useCallback(() => {
const columnExamples = {};
columns.forEach(column => {
columnExamples[column.field] = Array.from({
length: Math.min(DEFAULT_SAMPLE_COUNT, rows.length)
}).map(() => {
const row = rows[Math.floor(Math.random() * rows.length)];
return apiRef.current.getRowValue(row, column);
});
});
return columnExamples;
}, [apiRef, columns, rows]);
const getPromptContext = React.useCallback((allowDataSampling = false) => {
if (!isAiAssistantAvailable) {
return '';
}
const examples = allowDataSampling ? collectSampleData() : {};
const columnsContext = columns.reduce((acc, column) => {
const columnContextWithoutExamples = {
field: column.field,
description: column.description ?? null,
examples: [],
type: column.type ?? 'string',
allowedOperators: column.filterOperators?.map(operator => operator.value) ?? []
};
acc.push((0, _extends2.default)({}, columnContextWithoutExamples, {
examples: examples[column.field] ?? column.examples ?? []
}));
if (disablePivoting) {
return acc;
}
(getPivotDerivedColumns?.(column, apiRef.current.getLocaleText) || []).forEach(col => acc.push((0, _extends2.default)({}, columnContextWithoutExamples, col, {
derivedFrom: column.field
})));
return acc;
}, []);
return JSON.stringify(columnsContext);
}, [apiRef, columns, collectSampleData, getPivotDerivedColumns, isAiAssistantAvailable, disablePivoting]);
const updateChart = React.useCallback(result => {
if (!result.chart) {
return;
}
apiRef.current.updateChartDimensionsData(activeChartId, result.chart.dimensions.map(item => ({
field: item
})));
apiRef.current.updateChartValuesData(activeChartId, result.chart.values.map(item => ({
field: item
})));
}, [apiRef, activeChartId]);
const applyPromptResult = React.useCallback(result => {
if (!isAiAssistantAvailable) {
return;
}
const interestColumns = [];
if (!disableColumnFilter) {
apiRef.current.setFilterModel({
items: result.filters.map((filter, index) => {
const item = {
id: index,
field: filter.column,
operator: filter.operator,
value: filter.value
};
const column = columnsLookup[filter.column];
if (column.type === 'singleSelect') {
const options = (0, _internals.getValueOptions)(column) ?? [];
const found = options.find(option => typeof option === 'object' && option.label === filter.value);
if (found) {
item.value = found.value;
}
}
return item;
}),
logicOperator: result.filterOperator ?? _xDataGridPro.GridLogicOperator.And,
quickFilterValues: []
});
interestColumns.push(...result.filters.map(f => f.column));
} else {
result.filters = [];
}
let appliedPivoting = false;
if (!disablePivoting && 'columns' in result.pivoting) {
apiRef.current.setPivotActive(true);
apiRef.current.setPivotModel({
columns: result.pivoting.columns.map(c => ({
field: c.column,
sort: c.direction
})),
rows: result.pivoting.rows.map(r => ({
field: r
})),
values: result.pivoting.values.map(valueObj => {
const [field] = Object.keys(valueObj);
return {
field,
aggFunc: valueObj[field]
};
})
});
appliedPivoting = true;
} else if ('columns' in result.pivoting) {
// if pivoting is disabled and there are pivoting results, try to move them into grouping and aggregation
apiRef.current.setPivotActive(false);
result.pivoting.columns.forEach(c => {
result.grouping.push({
column: c.column
});
});
result.pivoting.rows.forEach(r => {
result.grouping.push({
column: r
});
});
result.pivoting.values.forEach(valueObj => {
const [field] = Object.keys(valueObj);
result.aggregation[field] = valueObj[field];
});
// remove the pivoting results data
result.pivoting = {};
} else {
apiRef.current.setPivotActive(false);
}
if (!disableRowGrouping && !appliedPivoting) {
apiRef.current.setRowGroupingModel(result.grouping.map(g => g.column));
} else {
result.grouping = [];
}
if (!disableAggregation && !appliedPivoting) {
apiRef.current.setAggregationModel(result.aggregation);
interestColumns.push(...Object.keys(result.aggregation));
} else {
result.aggregation = {};
}
if (!disableColumnSorting) {
apiRef.current.setSortModel(result.sorting.map(s => ({
field: s.column,
sort: s.direction
})));
} else {
result.sorting = [];
}
if (experimentalFeatures?.charts && chartsIntegration && activeChartId && result.chart) {
if (appliedPivoting) {
const unsubscribe = apiRef.current.subscribeEvent('rowsSet', () => {
const unwrappedGroupingModel = Object.keys((0, _xDataGridPro.gridColumnGroupsUnwrappedModelSelector)(apiRef));
// wait until unwrapped grouping model changes
if (!result.chart || unwrappedGroupingModel.length === 0 || (0, _isDeepEqual.isDeepEqual)(previousUnwrappedGroupingModel.current, unwrappedGroupingModel)) {
return;
}
previousUnwrappedGroupingModel.current = unwrappedGroupingModel;
const visibleRowsCount = (0, _xDataGridPro.gridVisibleRowsSelector)(apiRef).rows.length;
const maxColumns = Math.floor(MAX_CHART_DATA_POINTS / visibleRowsCount);
// we assume that the pivoting was adjusted to what needs to be shown in the chart
// so we can just pick up all the columns that were created by pivoting
// to avoid rendering issues, set the limit to MAX_CHART_DATA_POINTS data points (rows * columns)
result.chart.values = unwrappedGroupingModel.slice(0, maxColumns);
updateChart(result);
unsubscribe();
});
} else {
updateChart(result);
}
}
const visibleRowsData = (0, _internals.getVisibleRows)(apiRef);
const rowSelectionModel = {
type: 'include',
ids: new Set()
};
const selection = rowSelection ? result.select : -1;
if (selection !== -1) {
for (let i = 0; i < result.select; i += 1) {
const row = visibleRowsData.rows[i];
const id = apiRef.current.getRowId(row);
rowSelectionModel.ids.add(id);
}
}
apiRef.current.setRowSelectionModel(rowSelectionModel);
const targetIndex = Number(columnsLookup[_xDataGridPro.GRID_CHECKBOX_SELECTION_FIELD] !== undefined) + Number(result.grouping.length);
interestColumns.reverse().forEach(c => apiRef.current.setColumnIndex(c, targetIndex));
}, [apiRef, updateChart, rowSelection, disableColumnFilter, disableRowGrouping, disableAggregation, disableColumnSorting, disablePivoting, columnsLookup, isAiAssistantAvailable, activeChartId, chartsIntegration, experimentalFeatures?.charts]);
const setActiveConversationId = React.useCallback(id => {
if (!isAiAssistantAvailable) {
return;
}
const conversations = (0, _gridAiAssistantSelectors.gridAiAssistantConversationsSelector)(apiRef);
const activeConversationIndex = (0, _gridAiAssistantSelectors.gridAiAssistantActiveConversationIndexSelector)(apiRef);
if (!conversations[activeConversationIndex]) {
return;
}
conversations[activeConversationIndex].id = id;
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
aiAssistant: (0, _extends2.default)({}, state.aiAssistant, {
conversations
})
}));
}, [apiRef, isAiAssistantAvailable]);
const setConversationPrompts = React.useCallback((index, callback) => {
if (!isAiAssistantAvailable) {
return;
}
const currentConversations = (0, _gridAiAssistantSelectors.gridAiAssistantConversationsSelector)(apiRef);
const targetConversation = currentConversations[index];
const newPrompts = typeof callback === 'function' ? callback(targetConversation === undefined ? [] : targetConversation.prompts) : callback;
const newConversations = currentConversations.toSpliced(targetConversation === undefined ? currentConversations.length : index, 1, (0, _extends2.default)({}, targetConversation, {
title: newPrompts[newPrompts.length - 1].value,
// TODO: make the title configurable
prompts: newPrompts
}));
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
aiAssistant: (0, _extends2.default)({}, state.aiAssistant, {
conversations: newConversations
})
}));
}, [apiRef, isAiAssistantAvailable]);
const processPrompt = React.useCallback(async value => {
if (!onPrompt) {
return undefined;
}
const activeConversationIndex = (0, _gridAiAssistantSelectors.gridAiAssistantActiveConversationIndexSelector)(apiRef);
const activeConversation = (0, _gridAiAssistantSelectors.gridAiAssistantActiveConversationSelector)(apiRef);
const date = Date.now();
apiRef.current.setLoading(true);
setConversationPrompts(activeConversationIndex, prevPrompts => [...prevPrompts, {
value,
createdAt: new Date(date),
variant: 'processing',
helperText: apiRef.current.getLocaleText('promptProcessing')
}]);
try {
const response = await onPrompt(value, getPromptContext(allowAiAssistantDataSampling), activeConversation?.id);
applyPromptResult(response);
setActiveConversationId(response.conversationId);
setConversationPrompts(activeConversationIndex, prevPrompts => prevPrompts.map(item => item.createdAt.getTime() === date ? (0, _extends2.default)({}, item, {
response,
variant: 'success',
helperText: ''
}) : item));
return response;
} catch (error) {
setConversationPrompts(activeConversationIndex, prevPrompts => prevPrompts.map(item => item.createdAt.getTime() === date ? (0, _extends2.default)({}, item, {
variant: 'error',
helperText: error.message
}) : item));
return error;
} finally {
apiRef.current.setLoading(false);
}
}, [apiRef, allowAiAssistantDataSampling, onPrompt, getPromptContext, applyPromptResult, setConversationPrompts, setActiveConversationId]);
const setActiveConversationIndex = React.useCallback(index => {
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
aiAssistant: (0, _extends2.default)({}, state.aiAssistant, {
activeConversationIndex: index
})
}));
const conversation = (0, _gridAiAssistantSelectors.gridAiAssistantActiveConversationSelector)(apiRef);
if (!conversation) {
throw new Error('Conversation not found');
}
return conversation;
}, [apiRef]);
const setConversations = React.useCallback(callback => {
if (!isAiAssistantAvailable) {
return;
}
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
aiAssistant: (0, _extends2.default)({}, state.aiAssistant, {
conversations: typeof callback === 'function' ? callback(state.aiAssistant?.conversations) : callback
})
}));
}, [apiRef, isAiAssistantAvailable]);
React.useEffect(() => {
if (props.aiAssistantConversations) {
setConversations(props.aiAssistantConversations);
}
}, [apiRef, props.aiAssistantConversations, setConversations]);
React.useEffect(() => {
if (props.aiAssistantActiveConversationIndex) {
setActiveConversationIndex(props.aiAssistantActiveConversationIndex);
}
}, [apiRef, props.aiAssistantActiveConversationIndex, setActiveConversationIndex]);
(0, _internals.useGridRegisterPipeProcessor)(apiRef, 'preferencePanel', preferencePanelPreProcessing);
(0, _xDataGridPro.useGridApiMethod)(apiRef, {
aiAssistant: {
processPrompt,
setConversations,
setActiveConversationIndex
}
}, 'public');
};
exports.useGridAiAssistant = useGridAiAssistant;