Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions examples/kendo-react-stackblitz-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@progress/kendo-data-query": "^1.7.0",
"@progress/kendo-data-query": "^1.7.1",
"@progress/kendo-drawing": "^1.21.2",
"@progress/kendo-react-charts": "^10.0.0",
"@progress/kendo-react-dateinputs": "^10.0.0",
"@progress/kendo-react-dialogs": "^10.0.0",
"@progress/kendo-react-dropdowns": "^10.0.0",
"@progress/kendo-react-grid": "^10.0.0",
"@progress/kendo-react-inputs": "^10.0.0",
"@progress/kendo-react-intl": "^10.0.0",
"@progress/kendo-react-layout": "^10.0.0",
"@progress/kendo-react-tooltip": "^10.0.0",
"@progress/kendo-theme-default": "^10.0.1",
"@progress/kendo-react-charts": "^11.0.0",
"@progress/kendo-react-common": "^11.0.0",
"@progress/kendo-react-dateinputs": "^11.0.0",
"@progress/kendo-react-dialogs": "^11.0.0",
"@progress/kendo-react-dropdowns": "^11.0.0",
"@progress/kendo-react-grid": "^11.0.0",
"@progress/kendo-react-inputs": "^11.0.0",
"@progress/kendo-react-intl": "^11.0.0",
"@progress/kendo-react-layout": "^11.0.0",
"@progress/kendo-react-tooltip": "^11.0.0",
"@progress/kendo-theme-default": "^11.0.2",
"bootstrap": "5.2.1",
"hammerjs": "^2.0.8",
"react": "^18.2.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { sampleProducts } from './sample-products';

const data = [...sampleProducts];

const generateId = (data) => data.reduce((acc, current) => Math.max(acc, current.ProductID), 0) + 1;

export const insertItem = (item) => {
item.ProductID = generateId(data);
data.unshift(item);
return data;
};

export const getItems = () => {
return data;
};

export const updateItem = (item) => {
const index = data.findIndex((record) => record.ProductID === item.ProductID);
data[index] = item;
return data;
};

export const deleteItem = (item) => {
const index = data.findIndex((record) => record.ProductID === item.ProductID);
data.splice(index, 1);
return data;
};
200 changes: 97 additions & 103 deletions examples/kendo-react-stackblitz-app/src/components/GridPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,149 +3,143 @@ import { sampleProducts } from '../common/sample-products';
import { MyCommandCell } from './MyCommandCell.jsx';
import { Grid, GridColumn as Column, GridToolbar } from '@progress/kendo-react-grid';
import { Button } from '@progress/kendo-react-buttons';
import { process } from '@progress/kendo-data-query';
import { insertItem, getItems, updateItem, deleteItem } from '../common/products-services';
import ThemeChooser from './ThemeChooser';

const GridPage = (props) => {
const editField = "inEdit";
const [data, setData] = useState(sampleProducts);
const [dataState, setDataState] = useState({skip: 0, take: 10 })

const generateId = data => data.reduce((acc, current) => Math.max(acc, current.ProductID), 0) + 1;
const GridContext = React.createContext({});

const removeItem = (data, item) => {
let index = data.findIndex(p => (p === item || item.ProductID) && (p.ProductID === item.ProductID));
if (index >= 0) {
data.splice(index, 1);
}
}
const CommandCell = (props) => {
const { enterEdit, remove, add, discard, update, cancel } = React.useContext(GridContext);
return (
<MyCommandCell
{...props}
edit={enterEdit}
remove={remove}
add={add}
discard={discard}
update={update}
cancel={cancel}
/>
)
};

const DATA_ITEM_KEY = 'ProductID';

const enterEdit = (dataItem) => {
setData(data.map(item =>
item.ProductID === dataItem.ProductID ?
{ ...item, inEdit: true } : item
));
}
const GridPage = (props) => {
const [data, setData] = useState(sampleProducts);
const [edit, setEdit] = useState({});

const remove = (dataItem) => {

const newData = [...data];
removeItem(newData, dataItem);
removeItem(sampleProducts, dataItem);
const newData = deleteItem(dataItem);
setData([...newData]);
}
};

const add = (dataItem) => {
dataItem.inEdit = undefined;
dataItem.ProductID = generateId(sampleProducts);

sampleProducts.unshift(dataItem);
setData([...data])
}

const discard = (dataItem) => {
const newData = [data];
removeItem(newData, dataItem);

const newData = insertItem(dataItem);
setData(newData);
}
setEdit({});
};

const update = (dataItem) => {
const newData = [...data]
const updatedItem = { ...dataItem, inEdit: undefined };

updateItem(newData, updatedItem);
updateItem(sampleProducts, updatedItem);
dataItem.inEdit = false;
const newData = updateItem(dataItem);
setData(newData);
setEdit((edit) => ({ ...edit, [String(dataItem[DATA_ITEM_KEY])]: false }));
};

const discard = (dataItem) => {
const newData = [...data];
newData.splice(0, 1);
setData(newData);
}
setEdit(() => ({ ...edit, [String(dataItem[DATA_ITEM_KEY])]: false }));
};

const cancel = (dataItem) => {
const originalItem = sampleProducts.find(p => p.ProductID === dataItem.ProductID);
const newData = data.map(item => item.ProductID === originalItem.ProductID ? originalItem : item);
const originalItem = getItems().find((p) => p[DATA_ITEM_KEY] === dataItem[DATA_ITEM_KEY]);
const newData = data.map((item) =>
item[DATA_ITEM_KEY] === originalItem?.[DATA_ITEM_KEY] ? originalItem : item
);

setData(newData);
}
setEdit(() => ({ ...edit, [String(dataItem[DATA_ITEM_KEY])]: false }));
};

const updateItem = (data, item) => {
let index = data.findIndex(p => p === item || (item.ProductID && p.ProductID === item.ProductID));
if (index >= 0) {
data[index] = { ...item };
}
}
const enterEdit = (dataItem) => {
setEdit((edit) => ({ ...edit, [String(dataItem[DATA_ITEM_KEY])]: true }));
};

const itemChange = (event) => {
const newData = data.map(item =>
item.ProductID === event.dataItem.ProductID ?
{ ...item, [event.field]: event.value } : item
const field = event.field || '';
const newData = data.map((item) =>
item[DATA_ITEM_KEY] === event.dataItem[DATA_ITEM_KEY] ? { ...item, [field]: event.value } : item
);

setData(newData);
}
};

const addNew = () => {
const newDataItem = { inEdit: true, Discontinued: false };
const newDataItem = {
[DATA_ITEM_KEY]: null,
Discontinued: false
};

setData([newDataItem, ...data]);
}
setEdit((edit) => ({ ...edit, [String(newDataItem[DATA_ITEM_KEY])]: true }));
};

const cancelCurrentChanges = () => {
setData([...sampleProducts]);
}

const CommandCell = (props) => (
<MyCommandCell
{...props}
edit={enterEdit}
remove={remove}
add={add}
discard={discard}
update={update}
cancel={cancel}
editField={editField}
/>
);
const hasEditedItem = data.some(p => p.inEdit);
return (
<div className="container-fluid">
<ThemeChooser changeTheme={props.changeTheme} theme={props.theme}/>
<div className='row my-4'>
<div className='col-12 col-lg-9 border-right'>
<Grid
data={process(data, dataState)}
onItemChange={itemChange}
editField={editField}
// pageable // uncomment to enable paging
// sortable // uncomment to enable sorting
// filterable // uncomment to enable filtering
onDataStateChange={(e) =>
setDataState(e.dataState)
} // uncomment to enable data operations
{...dataState} // uncomment to enable data operations
>
<GridToolbar>
<Button
title="Add new"
themeColor={'primary'}
onClick={addNew}
>
Add new
</Button>
{hasEditedItem && (
<GridContext.Provider value={{ enterEdit, remove, add, discard, update, cancel }}>
<Grid
data={data}
autoProcessData={true}
dataItemKey={DATA_ITEM_KEY}

pageable={true}
defaultSkip={0}
defaultTake={10}

edit={edit}
editable={{ mode: 'inline' }}
navigatable={{ mode: 'inline' }}

sortable={true}
filterable={true}

onItemChange={itemChange}
>
<GridToolbar>
<Button
title="Cancel current changes"
onClick={cancelCurrentChanges}
title="Add new"
themeColor={'primary'}
onClick={addNew}
>
Cancel current changes
Add new
</Button>
)}
</GridToolbar>
<Column field="ProductID" title="Id" width="50px" editable={false} />
<Column field="ProductName" title="Product Name" />
<Column field="FirstOrderedOn" title="First Ordered" editor="date" format="{0:d}" />
<Column field="UnitsInStock" title="Units" width="150px" editor="numeric" />
<Column field="Discontinued" title="Discontinued" editor="boolean" />
<Column cell={CommandCell} width="240px" />
</Grid>
{Object.keys(edit).length > 0 && (
<Button
title="Cancel current changes"
onClick={cancelCurrentChanges}
>
Cancel current changes
</Button>
)}
</GridToolbar>
<Column field="ProductID" title="Id" width="50px" editable={false} filterable={false} />
<Column field="ProductName" title="Product Name" />
<Column field="UnitPrice" title="Unit Price" />
<Column field="UnitsInStock" title="Units" width="150px" editor="numeric" />
<Column field="Discontinued" title="Discontinued" editor="boolean" />
<Column cells={{ data: (props) => <CommandCell {...props} />}} width="240px" filterable={false} />
</Grid>
</GridContext.Provider>
</div>
<div className='col-12 col-lg-3 mt-3 mt-lg-0'>
<h3>KendoReact Grid</h3>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const Home = (props) => {
<div className='row'>
<div className='col-12'>
<h1 className='welcome mb-0'>Welcome to KendoReact</h1>
<h2 className='sub-header mt-0'>This is a sample application built with KendoReact - a set of over 60 UI components built from the ground-up specifically for React.</h2>
<h2 className='sub-header mt-0'>This is a sample application built with KendoReact - a React component library of 120+ enterprise-grade UI components and an AI Coding Assistant. Build polished, high-performing and accessible applications.</h2>
</div>
</div>
<div className='row'>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,38 @@
import * as React from "react";
import { Button } from '@progress/kendo-react-buttons';
import { classNames } from '@progress/kendo-react-common';

export const MyCommandCell = (props) => {
const { dataItem } = props;
const inEdit = dataItem[props.editField];
const isNewItem = dataItem.ProductID === undefined;
return inEdit ? (
<td className="k-command-cell">
<Button
className="k-grid-save-command"
themeColor={"primary"}
onClick={() =>
isNewItem ? props.add(dataItem) : props.update(dataItem)
}
>
{isNewItem ? "Add" : "Update"}
</Button>
const inEdit = props.isInEdit;
const isNewItem = dataItem.ProductID === null;

const onDeleteData = (dataItem) => {
props.remove(dataItem);
};
return (
<td {...props.tdProps} className={classNames('k-command-cell', ...props.className)}>
<Button
className="k-grid-cancel-command"
className={inEdit ? 'k-grid-save-command' : 'k-grid-edit-command'}
themeColor={'primary'}
onClick={() =>
isNewItem ? props.discard(dataItem) : props.cancel(dataItem)
inEdit ? (isNewItem ? props.add(dataItem) : props.update(dataItem)) : props.edit(dataItem)
}
>
{isNewItem ? "Discard" : "Cancel"}
</Button>
</td>
) : (
<td className="k-command-cell">
<Button
className="k-grid-edit-command"
themeColor={"primary"}
onClick={() => props.edit(dataItem)}
>
Edit
{inEdit ? (isNewItem ? 'Add' : 'Update') : 'Edit'}
</Button>
<Button
className="k-grid-remove-command"
className={inEdit ? 'k-grid-cancel-command' : 'k-grid-remove-command'}
themeColor={'base'}
onClick={() =>
// eslint-disable-next-line no-restricted-globals
confirm("Confirm deleting: " + dataItem.ProductName) &&
props.remove(dataItem)
inEdit
? isNewItem
? props.discard(dataItem)
: props.cancel(dataItem)
: onDeleteData(props.dataItem)
}
>
Remove
{inEdit ? (isNewItem ? 'Discard' : 'Cancel') : 'Remove'}
</Button>
</td>
);
Expand Down