Using Grid with SortableJS - reactjs

I am using SortableJS for React and I am trying to implement horizontal Grid, so that the final result would look like this:
I managed to do that, however the sort function does not work at all. I think it has something to do with the tag attribute. When I try tag={Grid} I get following error:
Sortable: el must be an HTMLElement, not [object Object]
Any ideas how can I implement Grid to the tag attribute? Here is my code:
<Grid container>
<Sortable options={{ fallbackOnBody: true, group: "items", handle: reorderHandle }} tag={Grid} onChange={handleChange}>
{items.map((item, index) =>
<Paper key={index} data-id={index} className={classes.item} elevation={0}>
<ButtonHelper {...getHandleClass(editable)} key={index} icon={
<Badge badgeContent={index + 1} color="default">
{editable && <ReorderIcon />}
</Badge>}
/>
<Grid item xs={4} key={index}>
<div className={classes.gridItem}>
<GalleryInput className={classes.image} style={{ width: '300px' }} label="image" source={`${source}[${index}].image`} />
<br></br>
<TextInput label="desc" source={`${source}[${index}].desc`} />
{editable && <ButtonHelper icon={<RemoveIcon />} onClick={handleRemove(index)} className={classes.left} />}
</div>
</Grid>
</Paper>
)}
</Sortable>
</Grid>
Thank you for any help.

Since you are using a custom component, the tag prop accepts it as a forwarRef component only.
So you need to update in that way.
Sample Snippet
// This is just like a normal component, but now has a ref.
const CustomComponent = forwardRef<HTMLDivElement, any>((props, ref) => {
return <div ref={ref}>{props.children}</div>;
});
export const BasicFunction: FC = props => {
const [state, setState] = useState([
{ id: 1, name: "shrek" },
{ id: 2, name: "fiona" }
]);
return (
<ReactSortable tag={CustomComponent} list={state} setList={setState}>
{state.map(item => (
<div key={item.id}>{item.name}</div>
))}
</ReactSortable>
);
};

Related

How can I add item on localStroge through ReactJS

I don't understand how I can add an item on localStroge with the handle change method in react js.
The problem is I want to add a favorite list. When I click the favorite list all checkbox fill I need to control them.
I want to store the filled item in my local storage. handlechange function fill all favorite icon why?
Every click will be a single item fill. Is it possible to Material UI or Martial UI checkBox icon? How can I handle it?
Here is my UI view
function Main() {
// Here is my state define
const [checked, setChecked] = React.useState(
localStorage.getItem("love") === "true"
);
const handleChange = (e) => {
localStorage.setItem("love", `${e.target.checked}`);
setChecked(e.target.checked);
console.log(e.target.checked);
};
return (
<>
<div className="row mt-5">
{isLoading ? (
<>
{Array.from({ length }).map((_, i) => (
<MainLoading key={i} />
))}
</>
) : error ? (
<p>Error occured</p>
) : (
<>
{data?.map((product) => (
<div className="col-md-3 mb-5 text-center" key={product.id}>
<img
className="w-100"
style={{ height: "200px", objectFit: "contain" }}
src={product.image}
alt=""
/>
<div>{product.title.substring(0, 20)}</div>
<button
onClick={() => handelAddTocard(product)}
className="mt-3"
>
Add to card
</button>
<button>
<Link
to={`/details/${product.id}`}
className="mt-3 text-decoration-none text-black"
>
view details
</Link>
</button>
{/* How can i control evey single item */}
<Checkbox
checked={checked}
onChange={handleChange}
icon={<FavoriteBorder />}
checkedIcon={<Favorite />}
/>
</div>
))}
</>
)}
</div>
</>
);
}
export default Main;
The problem is that you are using a boolean and you have no way to identify a specific item.
If you want to favorite multiple items, I would use something like this:
const [checked, setChecked] = React.useState(
JSON.parse(localStorage.getItem("loveIds") || "[]")
);
const handleCheck = (id, productChecked) => {
const newItems = productChecked ? [...checked, id] : checked.filter(x => x !== id);
localStorage.setItem("loveIds", JSON.stringify(newItemS));
setChecked(newItems);
console.log(newItems);
};
// ...
<Checkbox
checked={checked}
onChange={(e) => handleCheck(product.id, e.target.checked)}
icon={<FavoriteBorder />}
checkedIcon={<Favorite />}
/>

How to make a reusable Material UI tabs component in React

I am working on a React project, and I divided some of the pages into tabs using the MUI Tab component, so I can have multiple components in one page and render each component accordingly, so I have created the Tabs component. However, I can only see one first index tab.
Reusable tab MUI component:
export default function useTabs(props) {
const { children, value, index, label, ...other } = props;
const [selectedValue, setSelectedValue] = React.useState(0);
const handleChange = (event, newValue) => {
setSelectedValue(newValue);
};
return (
<Box sx={{ width: "100%" }}>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<Tabs
value={selectedValue}
onChange={handleChange}
className={classes.tab}
textColor="primary"
indicatorColor="primary"
>
<Tab label={label} {...a11yProps(0)} className={classes.tabText} />
{/* <Tab className={classes.tabText} label={label} {...a11yProps(1)} /> */}
</Tabs>
</Box>
<TabPanel value={selectedValue} index={index}>
{children} // Rendering tab children here, but getting only the first component
</TabPanel>
</Box>
);
}
Here is how I am using it:
// Import the reusable component
import Tab from "../common/Tabs";
export default function JobsRecruitments() {
return (
<>
<Tab label="tab name" index={0}>
<MyComponent />
</Tab>
</>
)
}
I don't think it's a good idea to use children if you want reusable tabs. That's because your tabs have to be consistent: if you have 3 child nodes, you should also have 3 tab labels. If you're using children, you can easily lose track of that.
However, if you want to make a reusable tab component, I would suggest passing an array of objects containing the tab label and Component as a property to your custom Tab component. Here's an example of what I mean:
export default function BasicTabs({ tabs }) {
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Box sx={{ width: "100%" }}>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<Tabs
value={value}
onChange={handleChange}
aria-label="basic tabs example"
>
{tabs.map(({ label }, i) => (
<Tab label={label} key={i} />
))}
</Tabs>
</Box>
{tabs.map(({ Component }, i) => (
<TabPanel value={value} index={i} key={i}>
{Component}
</TabPanel>
))}
</Box>
);
}
And then you can use this component like this:
import Tabs from "./MyTabs";
const tabs = [
{
label: "Tab 1",
Component: <div>Hello, I am tab 1</div>
},
{
label: "Tab 2",
Component: <div>Hello, I am tab 2</div>
},
{
label: "Tab 3",
Component: (
<div>
<h1>Tab with heading</h1>
<p>Hello I am a tab with a heading</p>
</div>
)
}
];
export default function App() {
return (
<div>
<Tabs tabs={tabs} />
</div>
);
}
Here's the Codesandbox with the full example

How to get rid of shadows in the Material-UI theme

I need to get rid of shadows in MuiPaper component.
I found some solution:
for example
but they didn't work.
I can override only root component (MuiPaper) but the shadow is set by the class MuiPaper-elevation1-24.
code that render component
const List = props => (
<List {...props} title="lists" filters={<Filter />} sort={{ field: 'timestamps.createdAt', order: 'DESC' }}>
<Datagrid rowClick="show" expand={<Edit />} >
<TextField source="attributes.campaignUuid" label="Campaign Uuid" />
<TextField source="attributes.affiliateId" label="Affiliate Id" />
<DateField source="attributes.createdAt" label="Created At" showTime locales="ru-RU" sortBy="timestamps.createdAt" />
<DateField source="attributes.updatedAt" label="Updated At" showTime locales="ru-RU" sortBy="timestamps.updatedAt" />
<DeleteButton />
</Datagrid>
</List>
);
And HTML that I receive:
<div class="list-page Component-root-904">
<div class="MuiPaper-root-21 MuiPaper-elevation1-24 MuiPaper-rounded-22 MuiCard-root-740 Component-card-905"></div>
</div>
Try this:
import { withStyles, createStyles } from '#material-ui/core/styles';
const listStyles = theme => createStyles({
card: {
boxShadow: 'none',
},
});
const List = withStyles(listStyles)(({ classes, ...props }) => (
<List classes={classes} title="lists" filters={<Filter />} sort={{ field: 'timestamps.createdAt', order: 'DESC' }} {...props}>
...
</List>
);

Material-UI custom JSS Styles removed on Component Re-render

I have a temporary Drawer with Tabs inside. When the Drawer is open and the color theme is toggled, the custom styles for the component in the tab are removed, breaking the styling. If I close the Drawer or toggle between tabs, it fixes the issue. This is only happening in two of the tabs, not all of them. See the pictures below.
For simplicity, I'll show the code for the Check Results tab. The Cog icon is being stripped of its styling and defaulting to the left.
...
const styles: StyleRulesCallback = () => ({
buttonDiv: {
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'flex-end',
},
});
...
type Props = StateProps & DispatchProps & WithStyles<typeof styles>;
class CheckResultsPanel extends React.Component<Props> {
...
render() {
this.selectedKeys = [];
const tree = this.renderTree();
const { classes } = this.props;
return (
<div id="checkResultsPanel">
<div className={classes.buttonDiv}>
<IconButton onClick={this.designSettingsClick} aria-label="Design Settings">
<SettingsIcon />
</IconButton>
</div>
<Divider />
{tree}
</div>
);
}
The components in question are being imported to the Drawer like so:
<div className="right-sidebar-container">
<div id="loadCaseObjectTabs" className={classes.root}>
<AppBar position="static">
<Tabs value={this.state.topValue} onChange={this.topHandleChange} fullWidth>
<Tab label="Objects" />
<Tab label="Load Cases" />
</Tabs>
</AppBar>
{topValue === 0 ? <div className={classes.tabs}><NavigationPanel /></div> : undefined}
{topValue === 1 ? <div className={classes.tabs}><LoadCasesPanel /></div> : undefined}
</div>
<div id="checkResultsPropertiesTabs" className={classes.root}>
<AppBar position="static">
<Tabs value={bottomTabIndices[this.props.bottom]} onChange={this.bottomHandleChange} fullWidth>
<Tab label="Check Results" />
<Tab label="Properties" />
</Tabs>
</AppBar>
{this.props.bottom === 'checkResults' ? <div className={classes.tabs}><CheckResultsPanel /></div> : undefined}
{this.props.bottom === 'properties' ? <div className={classes.tabs}><PropertiesPanel /></div> : undefined}
</div>
</div>
We discovered the issue. Apologies for the vague example. The issue was caused by optimization we implemented. The component was only set to update if certain props were changed and the classes were not being passed through this logic. When the themes were toggled, it changed all the styling for the application but since the component did not update the styles were not being applied.
shouldComponentUpdate(nextProps: Props) {
...
if (this.props.classes !== nextProps.classes) return true;
...
}

React Material UI Delete Selected Cells

I am using a table example from material ui.
This is the link to the online project https://codesandbox.io/s/209kpmpvx0
The example allows you to cell multiple rows and a delete icon gets displayed when you click on the row.
I would like to be able to print out all the selected rows after the delete icon has been pressed.
This is the class that allows you to select a row.
class EnhancedTable extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: [],
}
handleClick = (event, id) => {
const { selected } = this.state;
const selectedIndex = selected.indexOf(id);
let newSelected = [];
this.setState({ selected: newSelected });
};
I put the following code selectedId={selected.id} into the EnhancedTableHead component.
return (
<Paper className={classes.root}>
<EnhancedTableToolbar numSelected={selected.length} />
<div className={classes.tableWrapper}>
<Table className={classes.table} aria-labelledby="tableTitle">
<EnhancedTableHead
numSelected={selected.length}
selectedId={selected.id}
order={order}
orderBy={orderBy}
onSelectAllClick={this.handleSelectAllClick}
onRequestSort={this.handleRequestSort}
rowCount={data.length}
/>
Then I added the selectedId to the EnhancedTableToolbar. I also added an event handler into the delete icon.
let EnhancedTableToolbar = props => {
const { numSelected, classes } = props;
return (
<Toolbar
className={classNames(classes.root, {
[classes.highlight]: numSelected > 0
})}
>
<div className={classes.title}>
{numSelected > 0 ? (
<Typography color="inherit" variant="subheading">
{numSelected} selected
</Typography>
) : (
<Typography variant="title" id="tableTitle">
Nutrition
</Typography>
)}
</div>
<div className={classes.spacer} />
<div className={classes.actions}>
{numSelected > 0 ? (
<Tooltip title="Delete">
<IconButton aria-label="Delete">
<DeleteIcon onClick={printdeletearray} />
</IconButton>
</Tooltip>
) : (
<Tooltip title="Filter list">
<IconButton aria-label="Filter list">
<FilterListIcon />
</IconButton>
</Tooltip>
)}
</div>
</Toolbar>
);
};
I get an error saying selectedId is undefined.
const printdeletearray = () => {
console.log(this.state.selected);
};
You made two mistakes I guess:
firstly you should pass selected as selectedId props in <EnhancedTableToolbar />:
<EnhancedTableToolbar
numSelected={selected.length}
selectedId={selected}
/>
the second mistake is that you should pass selectedId to printdeletearray() like this:
<DeleteIcon onClick={() => printdeletearray(selectedId)} />
and here is your Demo

Resources