React-beautiful-dnd: lazy loaded component keeps falling back to suspense when dragged - react-beautiful-dnd

I have a react component that dynamically loads a widget component using react lazy, with the component path passed as a prop.
It renders fine, but when I drag it, the component flickers between its Suspense fallback and the fully rendered component. How can I prevent the Suspense fallback from being shown while dragging?
The draggable list looks like this:
<DragDropContext
onDragEnd={result => {
console.log(result);
reorder({ result, previousData: dashboard.data });
}}
>
<Title title={`Dashboard`} />
<Droppable droppableId={"dashboard"}>
{(
provided
// snapshot
) => {
return (
<div
{...provided.droppableProps}
ref={provided.innerRef}
style={{
display: "flex",
flexDirection: "column"
}}
>
{Array.isArray(dashboardItems.data) && dashboardItems.data.length > 0 ? (
dashboardItems.data.map((item: any, index: number) => {
return (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => {
return (
<div ref={provided.innerRef} {...provided.draggableProps}>
<WidgetCard item={item} dragHandleProps={provided.dragHandleProps} />
{!snapshot.isDragging && <AddWidgetControl />}
</div>
);
}}
</Draggable>
);
})
) : (
<Button
onClick={() =>
addDashboardItem.mutate({
index: 0,
dashboardItem: widgets.overview
})
}
>
<Typography variant="body2">Add</Typography>
</Button>
)}
{provided.placeholder}
</div>
);
}}
</Droppable>
</DragDropContext>
and the WidgetCard component that conditionally renders the widgets using lazy looks like this:
const WidgetCard = ({
item,
dragHandleProps
}: {
item: DashboardItemEntity,
dragHandleProps?: any
}) => {
{...}
const renderLoader = () => <p>Loading</p>;
const WidgetComponent = lazy(() => import(`../${item.component}`));
{...}
return (
{...}
<CardContent sx={{ marginTop: 0, paddingTop: 0 }}>
{/* {description} */}
<Box sx={{ marginLeft: 2 }}>
<Suspense fallback={renderLoader()}>
<WidgetComponent item={item} />
</Suspense>
</Box>
</CardContent>
{...}
);
};

Related

MUI 5 Tab Label Custom Thickness

I need to change the thickness of label inside a tab using MUI 5.
Here are what I tried:
interface TabPanelProps {
children?: React.ReactNode;
index: number;
value: number;
}
function TabPanel(props: TabPanelProps) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
export default function UsersGroupsManagement() {
const [value, setValue] = React.useState(0);
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
setValue(newValue);
};
const StyledTab = styled(Tab)<TabProps>(({theme}) => ({
'& .MuiButtonBase-root-MuiTab-root': {
fontWeight: 'bold'
}
}));
const styledLabel = styled('label')({
color: 'darkslategray',
backgroundColor: 'aliceblue',
padding: 8,
borderRadius: 4,
});
return (
<Box sx={styles.userAccounts}>
<Box sx={styles.tabbox}>
<Tabs value={value} onChange={handleChange} aria-label="User Management Tabs" >
<Tab label="ADD NEW USER" {...a11yProps(0)} sx={{
'& .MuiButtonBase-root-MuiTab-root': {
color: 'black',
backgroundColor: 'red',
fontWeight: 'bold'
}
}}/>
<Tab label="MANAGE USERS" {...a11yProps(1)} sx={{
'& .MuiButtonBase-root-MuiTab-root': {
fontWeight: 'bold'
}
}}/>
</Tabs>
</Box>
<TabPanel value={value} index={0}>
<AddNewUser />
</TabPanel>
<TabPanel value={value} index={1}>
Item Two
</TabPanel>
</Box>
);
}
I tried to create a StyledTab but didnt work.
I tried to create a styled label didnt work at all.
I tried to give css from sx props using but didnt work.
Can you please explain me how can I manage to make thicker labeled tabs in Material UI v5?
u can follow something like this..
<Tabs
sx={{
"& .MuiTabs-indicator": { backgroundColor: "#E86826" },
"& .MuiTab-root.Mui-selected": { color: "#E86826", fontWeight:"700" },
}}
value={value}
onChange={handleChange}
aria-label="nav tabs example"
centered
>
Just Edit the tabs and u work is done...hope it helps

Only Show Text on Selection - MUI

I only want to show the text of the tab on selection.
This is from the MUI documentation. How can I only show labels on selection? I assume I can use the state below and translate it somehow, how on the selection the color appears with the element.
Please see the code where I have tried to establish. I am new to this framework. If anyone can provide tips let me know!
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`vertical-tabpanel-${index}`}
aria-labelledby={`vertical-tab-${index}`}
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
TabPanel.propTypes = {
children: PropTypes.node,
index: PropTypes.number.isRequired,
value: PropTypes.number.isRequired,
};
function a11yProps(index) {
return {
id: `vertical-tab-${index}`,
'aria-controls': `vertical-tabpanel-${index}`,
};
}
//
const JobPostBuild = () => {
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Box
sx={{ flexGrow: 1, bgcolor: 'gray', display: 'flex', height: 500 }}
>
<Tabs
orientation="vertical"
variant="scrollable"
centered
value={value}
onChange={handleChange}
sx={{ borderRight: 1, borderColor: 'divider' }}
>
<Tab icon={<WorkIcon/>} label={"Role"} {...a11yProps(0)} />
<Tab icon={<AccountBoxIcon/>} label="People" {...a11yProps(1)} />
<Tab icon={<ViewCompactIcon/>} label="Build" {...a11yProps(2)} />
<Tab icon={<PostAddIcon/>} label="Post" {...a11yProps(3)} />
</Tabs>
<TabPanel value={value} index={0}>
Role
</TabPanel>
<TabPanel value={value} index={1}>
People
</TabPanel>
<TabPanel value={value} index={2}>
Build
</TabPanel>
<TabPanel value={value} index={3}>
Post
</TabPanel>
</Box>
);
}
export default JobPostBuild
Happy to discuss this over Discord please let me know!
You can check the value of selected tab to set a condition for displaying tab label:
<Tab label={value === 0 ? "Role" : ""} {...a11yProps(0)} />
Demo

the drawer covers the page

my drawer is covering the page i want it to be flex to the left without covering any content. this is my first time using material-ui so if anyone can explain i would appreciate that
const drawerWidth = 240;
const SideBar = (props) => {
const useStyles = makeStyles((theme) => ({
root: { display: "flex" },
}));
const classes = useStyles();
return (
<div>
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
"& .MuiDrawer-paper": {
width: drawerWidth,
boxSizing: "border-box",
},
}}
variant="permanent"
anchor="left"
>
<Toolbar />
<List>
<Divider />
{props.jobs.map((job, i) => (
<ListItem
alignItems="flex-start"
key={i}
onClick={() => {
props.onClick(job);
}}
>
<ListItemText primary={job.title} secondary={job.career_level} />
</ListItem>
))}
</List>
</Drawer>
</div>````
What you're probably looking for is the variant='persistent' drawer
import * as React from 'react';
import { styled, useTheme } from '#mui/material/styles';
import Box from '#mui/material/Box';
import Drawer from '#mui/material/Drawer';
import CssBaseline from '#mui/material/CssBaseline';
import MuiAppBar from '#mui/material/AppBar';
import Toolbar from '#mui/material/Toolbar';
import List from '#mui/material/List';
import Typography from '#mui/material/Typography';
import Divider from '#mui/material/Divider';
import IconButton from '#mui/material/IconButton';
import MenuIcon from '#mui/icons-material/Menu';
import ChevronLeftIcon from '#mui/icons-material/ChevronLeft';
import ChevronRightIcon from '#mui/icons-material/ChevronRight';
import ListItem from '#mui/material/ListItem';
import ListItemIcon from '#mui/material/ListItemIcon';
import ListItemText from '#mui/material/ListItemText';
import InboxIcon from '#mui/icons-material/MoveToInbox';
import MailIcon from '#mui/icons-material/Mail';
const drawerWidth = 240;
const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })(
({ theme, open }) => ({
flexGrow: 1,
padding: theme.spacing(3),
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
marginLeft: `-${drawerWidth}px`,
...(open && {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
marginLeft: 0,
}),
}),
);
const AppBar = styled(MuiAppBar, {
shouldForwardProp: (prop) => prop !== 'open',
})(({ theme, open }) => ({
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
...(open && {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: `${drawerWidth}px`,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
}),
}));
const DrawerHeader = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar,
justifyContent: 'flex-end',
}));
export default function PersistentDrawerLeft() {
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return (
<Box sx={{ display: 'flex' }}>
<CssBaseline />
<AppBar position="fixed" open={open}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
sx={{ mr: 2, ...(open && { display: 'none' }) }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div">
Persistent drawer
</Typography>
</Toolbar>
</AppBar>
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
'& .MuiDrawer-paper': {
width: drawerWidth,
boxSizing: 'border-box',
},
}}
variant="persistent"
anchor="left"
open={open}
>
<DrawerHeader>
<IconButton onClick={handleDrawerClose}>
{theme.direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
</IconButton>
</DrawerHeader>
<Divider />
<List>
{['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{['All mail', 'Trash', 'Spam'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Drawer>
<Main open={open}>
<DrawerHeader />
<Typography paragraph>
Content
</Typography>
<Typography paragraph>
More content
</Typography>
</Main>
</Box>
);
}

React-MaterialUI: Horizontally aligning single tab to right and others to left in App Bar ->Tabs -> Tab

In my React application, I have a Navigation bar where in there are multiple Tabs, which are created with the use of Marerial UI's AppBar, Tabs and Tab component (in sequence), as below:
function associatedProps(index) {
return {
id: `nav-tab-${index}`,
'aria-controls': `nav-tabpanel-${index}`
};
}
function LinkTab(props) {
const history = useHistory();
const route = props.route;
console.log(props);
return (
<>
<Tab
component="a"
onClick={(event) => {
event.preventDefault();
history.push(route)
}}
{...props}
/>
</>
);
}
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
backgroundColor: theme.palette.background.paper,
height: theme.navBarHeight
},
tabIndicator: {
backgroundColor: PRIMARY_RED.default
},
tabBar: {
top: '80px'
}
}));
export default function NavTabs() {
const classes = useStyles();
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<div className={classes.root}>
<AppBar position="fixed" className={classes.tabBar}>
<Tabs
variant=""
classes={{indicator: classes.tabIndicator}}
value={value}
onChange={handleChange}
aria-label="nav tabs example"
>
<LinkTab {...PRIMARY_NAVIGATION.MY_LIST} {...associatedProps(0)} />
<LinkTab {...PRIMARY_NAVIGATION.MY_REQUESTS} {...associatedProps(1)} />
<LinkTab {...PRIMARY_NAVIGATION.REPORT} {...associatedProps(2)} />
</Tabs>
</AppBar>
</div>
);
}
Now herein this setup I wanted my REPORT tab to be aligned right of the App Bar. I do not see any CSS Rule or Prop which in Documentation, which can help me here.
Please suggest how can I achieve this in current setup.
You should set a class for Tabs like this:
const useStyles = makeStyles((theme) => ({
tabs: {
'&:last-child': {
position: 'absolute',
right: '0'
}
}
}));
export default function NavTabs() {
...
return (
<div className={classes.root}>
<AppBar position="fixed" className={classes.tabBar}>
<Tabs
variant=""
classes={classes.tabs}
value={value}
onChange={handleChange}
aria-label="nav tabs example"
>
<LinkTab {...PRIMARY_NAVIGATION.MY_LIST} {...associatedProps(0)} />
<LinkTab {...PRIMARY_NAVIGATION.MY_REQUESTS} {...associatedProps(1)} />
<LinkTab {...PRIMARY_NAVIGATION.REPORT} {...associatedProps(2)} />
</Tabs>
</AppBar>
</div>
);
Tabs do not provide a property to align a specific item to the start or end. But you can leverage css to achieve your result.
Add a className to the item to be right aligned and define a marginLeft property on it
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
backgroundColor: theme.palette.background.paper,
height: theme.navBarHeight
},
tabIndicator: {
backgroundColor: PRIMARY_RED.default
},
tabBar: {
top: '80px'
},
rightAlign: {
marginLeft: 'auto',
}
}));
export default function NavTabs() {
const classes = useStyles();
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<div className={classes.root}>
<AppBar position="fixed" className={classes.tabBar}>
<Tabs
variant=""
classes={{indicator: classes.tabIndicator}}
value={value}
onChange={handleChange}
aria-label="nav tabs example"
>
<LinkTab {...PRIMARY_NAVIGATION.MY_LIST} {...associatedProps(0)} />
<LinkTab {...PRIMARY_NAVIGATION.MY_REQUESTS} {...associatedProps(1)} />
<LinkTab {...PRIMARY_NAVIGATION.REPORT} {...associatedProps(2)} className={classes.rightAlign}/>
</Tabs>
</AppBar>
</div>
);
}
Sample working demo

Button sit at bottom of page centered

I would like a scroll to the top button to sit at the bottom, much like a footer. Current Behavior: I have an array I'm filtering through and displaying different lengths of data. When there is only one item in a certain category the button will move all the way to the top of the page under the item. Wanted Behavior: I would like the button to stay at the bottom and not move, but not sticky. my button styling is as follows:
import React, { useState, useContext } from "react"
// components
import SelectStatus from "../components/SelectStatus"
import RepoCard from "../components/RepoCard"
// Context
import { GithubContext } from "../context/GithubContext"
// Material UI Stuff
import TextField from "#material-ui/core/TextField"
import CardContent from "#material-ui/core/CardContent"
import Button from "#material-ui/core/Button"
import Card from "#material-ui/core/Card"
import Grid from "#material-ui/core/Grid"
import { makeStyles } from "#material-ui/core/styles"
import Container from "#material-ui/core/Container"
// context
const useStyles = makeStyles((theme) => ({
cardGrid: {
paddingTop: theme.spacing(8),
paddingBottom: theme.spacing(8),
},
card: {
display: "flex",
marginBottom: 10,
minHeight: 90,
},
form: {
display: "flex",
alignItems: "center",
width: "100%",
},
content: {
display: "flex",
alignItems: "center",
width: "100%",
justifyContent: "space-between",
},
jobField: {
margin: 0,
padding: 0,
},
grid: {
padding: 0,
},
dashboardContainer: {
marginTop: 70,
padding: 10,
},
loading: {
textAlign: "center",
},
}))
const INITIAL_STATE = {
language: "All",
search: "",
}
const Profile = () => {
const [formData, setFormData] = useState(INITIAL_STATE)
const [updated, setUpdated] = useState(false)
const [created, setCreated] = useState(false)
const { data } = useContext(GithubContext)
const handleUpdated = () => {
setUpdated(!updated)
data &&
data.sort((a, b) => {
if (updated) return a.updated_at > b.updated_at ? -1 : 1
return a.updated_at > b.updated_at ? 1 : -1
})
}
const handleCreated = () => {
setCreated(!created)
data &&
data.sort((a, b) => {
if (created) return a.created_at > b.created_at ? -1 : 1
return a.created_at > b.created_at ? 1 : -1
})
}
const handleInputChange = (field) => (e) => {
setFormData({ ...formData, [field]: e.target.value })
}
const classes = useStyles()
return (
<>
<div style={{ marginTop: 85, marginBottom: 85 }}>
<Container className={classes.dashboardContainer}>
<Card className={classes.card} style={{ width: "100%" }}>
<CardContent className={classes.content}>
<div className={classes.form}>
<Grid
container
spacing={2}
alignItems='center'
justify='space-between'
>
<Grid item sm={4} xs={12} className={classes.grid}>
<SelectStatus
language={formData.language}
handleInputChange={handleInputChange}
/>
</Grid>
<Grid item sm={4} xs={12} className={classes.grid}>
<TextField
className={classes.jobField}
margin='normal'
fullWidth
id='search'
name='search'
label='Search by Title'
placeholder='Search by Title'
onChange={handleInputChange("search")}
value={formData.search}
/>
</Grid>
<Grid item sm={2} xs={12} className={classes.grid}>
<Button
fullWidth
variant='contained'
color='primary'
onClick={handleUpdated}
>
Updated {updated ? "(oldest)" : "(newest)"}
</Button>
</Grid>
<Grid item sm={2} xs={12} className={classes.grid}>
<Button
fullWidth
variant='contained'
color='primary'
onClick={handleCreated}
>
Created {created ? "(oldest)" : "(newest)"}
</Button>
</Grid>
</Grid>
</div>
</CardContent>
</Card>
</Container>
{!data ? (
<h1 className={classes.loading}>Initializing Repos...</h1>
) : (
<Container style={{ padding: 10 }}>
{!data ? (
<div style={{ placeItems: "center" }}>Loading...</div>
) : (
<Grid container alignItems='center' spacing={4}>
{data &&
data
.filter((data) => {
if (formData.language === "All") return true
return data.language === formData.language
})
.filter((data) => {
if (formData.search === "") return true
return (data.name + data.language)
.toLowerCase()
.includes(formData.search.toLowerCase())
})
.map((user) => <RepoCard key={user.id} user={user} />)}
</Grid>
)}
</Container>
)}
<Button
variant='contained'
color='primary'
disableElevation
style={{
borderRadius: 0,
display: "block",
marginLeft: "auto",
marginRight: "auto",
position: "relative",
marginTop: "80px"
}}
>
Back to Top
</Button>
</div>
</>
)
}
export default Profile
You can use flex and space-around feature. like below:
<div class="container">
<div>
your items
</div>
<Button
variant='contained'
color='primary'
disableElevation
style={{
borderRadius: 0,
elevation: "disabled",
display: "block",
marginLeft: "auto",
marginRight: "auto",
marginTop: "80px",
}}
>
Back to Top
</Button>
</div>
Style:
.container{
display: flex;
flex-direction:column;
justify-content: space-between;
}
Using this you split your content to two part.
First part which will be your array items will be shown at the top.
The second part which is your button will be shown at the bottom.
This is because your making a space between first part and second part by using space-around of justify-content.
without sticky or fixed the only thing i can think of is using position: absolute; and bottom: 0; or placing them in a container with some combination of the same, then positioning it as needed. same as Joeri in the comments, would need a bit more context to help :) good luck!

Resources