UNPKG

@mui/x-data-grid-premium

Version:

The Premium plan edition of the MUI X Data Grid Components.

368 lines (366 loc) 15.6 kB
"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;