How to customize React Antd table header with table data? - reactjs

I want to customer header table like it:

Merge the cells as below:
const columns = [
{
// title: "Title",
colSpan: 1,
// dataIndex: "tel",
render: (value, row, index) => {
const obj = {
children: value,
props: {}
};
if (index === 0) {
obj.props.rowSpan = 0;
}
if (index === 1) {
obj.props.rowSpan = 0; // merge here
}
return obj;
}
}
];
Refer: ant document of components-table-demo-colspan-rowspan

Related

Map Data Based off ID

If i'm using the map function, how would you limit it by ID, e.g I only want to map ID 1,2 out of 3 possible datapoints?
What's the industry standard solution?
export const userInputs = [
{
id: 1,
label: "First Name",
type: "text",
placeholder: "Remy",
},
{
id: 2,
label: "Surname",
type: "text",
placeholder: "Sharp",
},
{
id: 3,
label: "Email",
type: "email",
placeholder: "remysharp#gmail.com",
}
];
Mapping like this, for email, i'd only like name and surname to be mapped inputs.
{inputs.map((input) => (
<FormInput className="formInput" key={input.id}>
<UserInput type={input.type} placeholder={input.placeholder} />
</FormInput>
))}
Use for loop.
function SomeComponent() {
const useInputs = [
/*...*/
]
const limit = 2
const userInputDomList = []
for (let i = 0; i < userInputs.length; i++) {
if (limit >= i) break
userInputDomList.push(<div key={userInputs[i].id}> </div>)
}
return <section>{userInputDomList}</section>
}
you could use .filter or .map for same result but it will loop over all elements instead of stopping at limit index.
For id based selection
function SomeComponent() {
const useInputs = [
/*...*/
]
const userInputDomList = []
// using for loop
/* for (let i = 0; i < userInputs.length; i++) {
const id = userInputs[i].id
if (id === 3) {
userInputDomList.push(<div key={id}> </div>)
break
}
} */
// using for..of
for (const userInput of userInputs) {
if (userInput.id === 3) {
userInputDomList.push(<div key={userInput.id}> </div>)
break
}
}
return <section>{userInputDomList}</section>
}
if really want reander only one item to be rendered
function RenderMatchingId({ userInputs, userInputId }) {
for (const userInput of userInputs) {
if (userInput.id === userInputId) {
return <section>found {userInput.id}</section>
}
}
// fallback
return <section>404 not found</section>
}

React setting state using function not merging like objects

This function is called whenever MUI's data grid pro column width has been changed. I am trying to capture the "field" and "width" from the "params" and create an array of objects for all the columns that had changed width. My issue is that it just keeps adding the same object instead of merging them. For instance below I changed the width of the "wave" column two times and it just added the second change to the array.
I need help merging them properly
const handleColumnSizeChange = useCallback(
(params) => {
setColumnDefinitions((columnDefinitions) => {
return [
...columnDefinitions,
{ field: params.colDef.field, width: params.colDef.width },
];
});
},
[columnDefinitions]
);
console.log(columnDefinitions);
UPDATE:
I figured it out. I thought there was an easier way just using the spread in my previous function.
const handleColumnSizeChange = useCallback(
(params) => {
const { field, width } = params.colDef;
const check = columnDefinitions.some((e) => e.field === field);
if (check) {
const updatedDefs = columnDefinitions.map((column) => {
if (column.field === field) {
return { ...column, width: width };
}
return column;
});
setColumnDefinitions(updatedDefs);
} else {
// setColumnDefinitions((columnDefinitions) => {
// return [...columnDefinitions, { field: field, width: width }];
// });
setColumnDefinitions([
...columnDefinitions,
{ field: field, width: width },
]);
}
},
[columnDefinitions]
);
const handleColumnSizeChange = useCallback(
(params) => {
const { field, width } = params.colDef;
const check = columnDefinitions.some((e) => e.field === field);
if (check) {
const updatedDefs = columnDefinitions.map((column) => {
if (column.field === field) {
return { ...column, width: width };
}
return column;
});
setColumnDefinitions(updatedDefs);
} else {
setColumnDefinitions((columnDefinitions) => {
return [...columnDefinitions, { field: field, width: width }];
});
}
},
[columnDefinitions]
);

Generic filter for X number of properties

I want to make a generic filter function. Currently I have a function that looks like this:
const filterRows = () => {
items.filter((item) => {
if(selectedDrinks.length > 0 && selectIds.length > 0) {
return selectedDrinks.includes(item.description) && selectedIds.includes(item.props.id)
}else if(selectedDrinks.length > 0) {
return selectedDrinks.includes(item.description)
}else if(selectedIds.length > 0) {
return selectedIds.includes(item.props.id)
}
}
}
The number of if checks I need to do will grow exponentially if I add one more thing to filter by.
I've made a pathetic try below. One issue I encountered is if I have a nested structure and want to access ["props/id"] as I don't know the syntax for it. Also tried ["props:id"] etc. And if I add multiple strings in the query it does not work either. And even if I could add multiple strings properly it would only work as an OR.
And for me it would be selectedDrinks && selectedId as both need to match for it to filter, not selectedDrinks || selectedIds
I want to include everything in both selectedDrinks and selectedIds as a query, and they should filter only if both are included in "assets" as description and props:id. I should also be able to add e.g "selectedNames" as a third "query parameter".
const selectedDrinks: string[] = [
"cola",
"fanta",
]
const selectedIds : string[] = [
"5",
"4",
]
interface s {
description: string;
name: string;
props: {
id: string
}
}
const items: s[] = [
{
description: "cola",
name: "computer",
props: {
id: "4"
}
},
{
description: "fanta",
name: "laptop",
props: {
id: "5"
}
},
{
description: "sprite",
name: "phone",
props: {
id: "6"
}
}
]
export function genericFilter<T>(
object: T,
filters: Array<keyof T>,
query: string[]
):boolean {
if(query.length === 0)
return true
return filters.some(filter => {
const value = object[filter]
console.log(value)
if(typeof value === "string") {
return value.toLowerCase().includes(query.map(q => q.toLowerCase()).join(""))
}
if(typeof value === "number") {
return value.toString().includes(query.map(q => q.toLowerCase()).join(""))
}
return false
})
}
const myFilterResult = items.filter((asset) => genericFilter(item, ["props", "name"], ["5"]))
console.log(myFilterResult)
If anyone is interested, here is how I solved it.
/**
*
* #returns A new list of filtered objects
* #param objects The objects that we want to filter
* #param properties The properties we want to apply on the object and compare with the query
* #param queries The queries we want to filter by
*/
export function genericFilter<T>(
objects: T[],
properties: Array<keyof T>,
queries: Array<string>[] | Array<number>[]
):T[] {
return objects.filter((object) => {
var count = 0;
properties.some((props) => {
const objectValue = object[props]
if(typeof objectValue === "string" || typeof objectValue === "number") {
queries.forEach((query) => {
query.forEach((queryValue) => {
if(queryValue === objectValue) {
count+=1;
}
})
})
}
})
return count === properties.length;
})
}
export default genericFilter;
How you call the function, can include X amount of filters and strings to search for.
const result = genericFilter(assets, ["description", "id", "name"], [selectedAssetTypes, selectedIds, selectedNames])

Ant design table row merge based on data instead of index value

{
title: 'Home phone',
colSpan: 2,
dataIndex: 'tel',
render: (value, row, index) => {
const obj = {
children: value,
props: {},
};
if (index === 2) {
obj.props.rowSpan = 2;
}
// These two are merged into above cell
if (index === 3) {
obj.props.rowSpan = 0;
}
if (index === 4) {
obj.props.colSpan = 0;
}
return obj;
},
},
In dis example based on index value rowsspan size is decided, can we do that based on previous column data (if there are two John Brown's then row span should be 2)?. So basically we need to decide row span size by sorting rows and comparing row value.
https://stackblitz.com/edit/react-bubauz?file=index.js
I can give you an idea with name column, mb it helps you.
If you want to merge Name column:
First of all you need to sort your table data.Then create Set(). And you need clear Set in useEffect().
const names = new Set();
{
title: 'Name',
rowSpan: 1,
dataIndex: 'name',
render: (value, row, index) => {
const obj = {
children: value,
props: {}
};
if (names.has(value)) {
obj.props.rowSpan = 0;
} else {
const occurCount = tableData.filter((data) => data.name === value).length;
obj.props.rowSpan = count;
names.add(value);
}
return obj;
}
}

grouping the data based on one property antd table

I am trying to group the data currently its working for three, if i remove any one gender then i wont be able to get it the same design,
Basically if the state_name is same then it should be grouped into same even there is only one gender or more than one as shown below
Codesandbox
Figured out the issue, basically having a variable outside, when the sameKey repeats not setting the count making rowSpan as 0 so it will be hidden.
Logic
let sameKey;
const columns = [
{
title: "District",
dataIndex: "state_name",
key: "state_name",
render: (value, row, index) => {
const obj = {
children: value,
props: {}
};
if (!(sameKey !== value)) {
obj.props.rowSpan = 0;
return obj;
}
const count = data.filter(item => item.state_name === value).length;
sameKey = value;
obj.props.rowSpan = count;
return obj;
}
},
See the codesandbox

Resources