diff --git a/.changeset/olive-falcons-type.md b/.changeset/olive-falcons-type.md new file mode 100644 index 0000000..bd18263 --- /dev/null +++ b/.changeset/olive-falcons-type.md @@ -0,0 +1,5 @@ +--- +'@lowdefy/community-plugin-aggrid': minor +--- + +Add support for rendering Button and Tag blocks within AgGrid cells. diff --git a/plugins/community-plugin-aggrid/package.json b/plugins/community-plugin-aggrid/package.json index 805bbf9..24d7c7b 100644 --- a/plugins/community-plugin-aggrid/package.json +++ b/plugins/community-plugin-aggrid/package.json @@ -53,6 +53,7 @@ "@ag-grid-community/react": "30.2.0", "@ag-grid-community/styles": "30.2.0", "@lowdefy/block-utils": "4.0.0-rc.11", + "@lowdefy/blocks-antd": "4.0.0-rc.11", "@lowdefy/helpers": "4.0.0-rc.11", "react": "18.2.0", "react-dom": "18.2.0" diff --git a/plugins/community-plugin-aggrid/src/AgGrid.js b/plugins/community-plugin-aggrid/src/AgGrid.js index 42bc621..1c7a85e 100644 --- a/plugins/community-plugin-aggrid/src/AgGrid.js +++ b/plugins/community-plugin-aggrid/src/AgGrid.js @@ -20,8 +20,9 @@ import { ClientSideRowModelModule } from '@ag-grid-community/client-side-row-mod import { CsvExportModule } from '@ag-grid-community/csv-export'; import processColDefs from './processColDefs.js'; +import mapOperators from './mapOperators.js'; -const AgGrid = ({ properties, methods, loading, events }) => { +const AgGrid = ({ components, events, loading, methods, properties }) => { const { quickFilterValue, columnDefs, @@ -30,11 +31,21 @@ const AgGrid = ({ properties, methods, loading, events }) => { ...someProperties } = properties; const [rowData, setRowData] = useState(newRowData ?? []); + const blockColumns = useRef({}); const gridRef = useRef(); const memoDefaultColDef = useMemo(() => defaultColDef); + const getRowId = useCallback( + (params) => + params.data[properties.rowId] ?? + params.data.id ?? + params.data._id ?? + JSON.stringify(params.data), + [] + ); + const onRowClick = useCallback((event) => { if (events.onRowClick) { methods.triggerEvent({ @@ -49,7 +60,8 @@ const AgGrid = ({ properties, methods, loading, events }) => { } }, []); const onCellClicked = useCallback((event) => { - if (events.onCellClick) { + //TODO: cellClick triggers once if button modifies row data + if (events.onCellClick && !blockColumns.current[event.column.colId]) { methods.triggerEvent({ name: 'onCellClick', event: { @@ -92,7 +104,7 @@ const AgGrid = ({ properties, methods, loading, events }) => { name: 'onFilterChanged', event: { rows: event.api.rowModel.rowsToDisplay.map((row) => row.data), - filter: this.gridApi.getFilterModel(), + filter: gridRef.current.api.getFilterModel(), }, }); } @@ -111,7 +123,6 @@ const AgGrid = ({ properties, methods, loading, events }) => { }, []); useEffect(() => { - methods.registerMethod('exportDataAsCsv', (args) => gridRef.current.api.exportDataAsCsv(args)); methods.registerMethod('exportDataAsCsv', (args) => gridRef.current.api.exportDataAsCsv(args)); methods.registerMethod('sizeColumnsToFit', () => gridRef.current.api.sizeColumnsToFit()); methods.registerMethod('setFilterModel', (model) => gridRef.current.api.setFilterModel(model)); @@ -136,8 +147,31 @@ const AgGrid = ({ properties, methods, loading, events }) => { } }, []); + const registerBlockEvent = useCallback(({ eventId, actions, rowEvent }) => { + if (!actions || events[eventId]) { + blockColumns.current[rowEvent.columnId] = !!events[eventId]; + return; + } + + methods.registerEvent({ + name: eventId, + actions: mapOperators(actions), + }); + blockColumns.current[rowEvent.columnId] = true; + }, []); + + const clearBlockEvents = useCallback(() => { + Object.keys(events).forEach((key) => { + if (key.endsWith('_actions')) { + delete events[key]; + } + }); + blockColumns.current = {}; + }, []); + useEffect(() => { if (JSON.stringify(rowData) !== JSON.stringify(newRowData)) { + clearBlockEvents(); setRowData(newRowData); } }, [newRowData]); @@ -157,8 +191,9 @@ const AgGrid = ({ properties, methods, loading, events }) => { onRowClicked={onRowClick} onCellClicked={onCellClicked} modules={[ClientSideRowModelModule, CsvExportModule]} - columnDefs={processColDefs(columnDefs, methods)} + columnDefs={processColDefs(columnDefs, methods, components, events, registerBlockEvent)} ref={gridRef} + getRowId={getRowId} /> ); }; diff --git a/plugins/community-plugin-aggrid/src/mapOperators.js b/plugins/community-plugin-aggrid/src/mapOperators.js new file mode 100644 index 0000000..6622721 --- /dev/null +++ b/plugins/community-plugin-aggrid/src/mapOperators.js @@ -0,0 +1,43 @@ +/* + Copyright 2021 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import { type } from '@lowdefy/helpers'; + +function recMapOperators(action) { + if (!type.isObject(action) && !type.isArray(action)) { + return action; + } + + if (type.isArray(action)) { + return action.map((actionObj) => recMapOperators(actionObj)); + } + + const newObject = {}; + const prefix = '_'; + + Object.entries(action).forEach(([key, value]) => { + const newKey = key.replace(new RegExp(`^${prefix}`), ''); + newObject[newKey] = recMapOperators(value); + }); + + return newObject; +} + +function mapOperators(actions) { + return recMapOperators(actions); +} + +export default mapOperators; diff --git a/plugins/community-plugin-aggrid/src/processColDefs.js b/plugins/community-plugin-aggrid/src/processColDefs.js index 5a36ac5..c43cbfc 100644 --- a/plugins/community-plugin-aggrid/src/processColDefs.js +++ b/plugins/community-plugin-aggrid/src/processColDefs.js @@ -16,12 +16,13 @@ import { renderHtml } from '@lowdefy/block-utils'; import { type } from '@lowdefy/helpers'; +import renderBlocks from './renderBlocks.js'; -function recProcessColDefs(columnDefs, methods) { +function recProcessColDefs(columnDefs, methods, components, events, registerEvent) { return columnDefs.map((col) => { const newColDef = {}; if (type.isArray(col.children)) { - newColDef.children = recProcessColDefs(col.children, methods); + newColDef.children = recProcessColDefs(col.children, methods, components); } if (type.isFunction(col.cellRenderer)) { newColDef.cellRenderer = (params) => { @@ -30,6 +31,23 @@ function recProcessColDefs(columnDefs, methods) { methods, }); }; + } else if (type.isArray(col.blocks) || type.isArray(col.cellRenderer)) { + //TODO: delete col.blocks + newColDef.cellRenderer = (params) => { + return renderBlocks({ + blocks: col.blocks, + methods, + components, + rowEvent: { + row: params.data, + rowIndex: params.rowIndex, + index: parseInt(params.node.id), + columnId: params.column.colId, + }, + events, + registerEvent, + }); + }; } return { ...col, @@ -38,8 +56,8 @@ function recProcessColDefs(columnDefs, methods) { }); } -function processColDefs(columnDefs = [], methods) { - return recProcessColDefs(columnDefs, methods); +function processColDefs(columnDefs = [], methods, components, events, registerEvent) { + return recProcessColDefs(columnDefs, methods, components, events, registerEvent); } export default processColDefs; diff --git a/plugins/community-plugin-aggrid/src/renderBlocks.js b/plugins/community-plugin-aggrid/src/renderBlocks.js new file mode 100644 index 0000000..46371ec --- /dev/null +++ b/plugins/community-plugin-aggrid/src/renderBlocks.js @@ -0,0 +1,66 @@ +/* + Copyright 2021 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import React from 'react'; + +import Tag from '@lowdefy/blocks-antd/blocks/Tag/Tag.js'; +import Button from '@lowdefy/blocks-antd/blocks/Button/Button.js'; + +const typeMap = { + Button: ({ block, methods, components, rowEvent, events, eventId }) => { + return ( +