Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AG Grid Buttons #55

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
5 changes: 5 additions & 0 deletions .changeset/olive-falcons-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lowdefy/community-plugin-aggrid': minor
---

Add support for rendering Button and Tag blocks within AgGrid cells.
1 change: 1 addition & 0 deletions plugins/community-plugin-aggrid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
45 changes: 40 additions & 5 deletions plugins/community-plugin-aggrid/src/AgGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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({
Expand All @@ -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: {
Expand Down Expand Up @@ -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(),
},
});
}
Expand All @@ -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));
Expand All @@ -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]);
Expand All @@ -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}
/>
);
};
Expand Down
43 changes: 43 additions & 0 deletions plugins/community-plugin-aggrid/src/mapOperators.js
Original file line number Diff line number Diff line change
@@ -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;
26 changes: 22 additions & 4 deletions plugins/community-plugin-aggrid/src/processColDefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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,
Expand All @@ -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;
66 changes: 66 additions & 0 deletions plugins/community-plugin-aggrid/src/renderBlocks.js
Original file line number Diff line number Diff line change
@@ -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 (
<Button
blockId={eventId}
components={components}
methods={methods}
events={events}
properties={block.properties}
rename={{
events: {
onClick: eventId,
},
}}
onClick={() => methods.triggerEvent({ name: eventId, event: rowEvent })}
/>
);
},
Tag: ({ block, methods, components, rowEvent, events, eventId }) => {
// TODO: Define onClose
return (
<Tag
blockId={eventId}
components={components}
methods={methods}
properties={block.properties}
onClick={() => methods.triggerEvent({ name: eventId, event: rowEvent })}
/>
);
},
};

function renderBlocks({ blocks, methods, components, rowEvent, events, registerEvent }) {
return (
<div style={{ display: 'flex', gap: '4px' }}>
{blocks.map((block, index) => {
const eventId = `${block.id}_${rowEvent.rowIndex}_${index}_actions`;
registerEvent({ eventId, actions: block.events?.onClick, rowEvent });
return typeMap[block.type]({ block, methods, components, rowEvent, events, eventId });
})}
</div>
);
}

export default renderBlocks;