import React, { useState, useRef, useEffect } from 'react'
import { Tabs, Tab, styled, Typography, Box } from '@mui/material'
import { Home, Favorite, Settings } from '@mui/icons-material'
import { motion, AnimatePresence } from 'framer-motion'
const StyledTabs = styled(Tabs)({
position: 'fixed',
top: 20,
right: 20,
backgroundColor: 'rgba(156, 39, 176, 0.1)',
borderRadius: 20,
minHeight: 'unset',
padding: 4,
'& .MuiTabs-indicator': {
display: 'none',
},
})
const StyledTab = styled(Tab)(({ theme }) => ({
minWidth: 'unset',
minHeight: 'unset',
padding: theme.spacing(1),
borderRadius: '50%',
zIndex: 1,
}))
const IconWrapper = styled(motion.div)({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: 24,
height: 24,
})
const SelectionBackground = styled(motion.div)({
width: 40,
height: 40,
borderRadius: '50%',
backgroundColor: '#9c27b0',
position: 'absolute',
zIndex: 0,
})
const TabView = () => {
const [value, setValue] = useState(0)
const tabRefs = useRef<(HTMLButtonElement | null)[]>([])
const [selectionPosition, setSelectionPosition] = useState({ left: 0, top: 0 })
const tabContent = [
{ icon: <Home />, label: 'Home', content: 'Welcome to the Home tab!' },
{ icon: <Favorite />, label: 'Favorites', content: 'Here are your favorite items.' },
{ icon: <Settings />, label: 'Settings', content: 'Adjust your settings here.' },
]
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
if (newValue >= 0 && newValue < tabContent.length) {
setValue(newValue)
updateSelectionPosition(newValue)
}
}
const updateSelectionPosition = (index: number) => {
const currentTab = tabRefs.current[index]
if (currentTab) {
const { offsetLeft, offsetTop } = currentTab
setSelectionPosition({ left: offsetLeft, top: offsetTop })
}
}
useEffect(() => {
updateSelectionPosition(0)
}, [])
return (
<>
<StyledTabs value={value} onChange={handleChange} aria-label="icon tabs example">
<SelectionBackground
initial={false}
animate={{
left: selectionPosition.left,
top: selectionPosition.top,
}}
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
/>
{tabContent.map((tab, index) => (
<StyledTab
key={index}
icon={
<IconWrapper>
<AnimatePresence mode="wait" initial={false}>
<motion.div
key={index}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
>
{React.cloneElement(tab.icon, {
style: { color: value === index ? '#ffffff' : 'rgba(156, 39, 176, 0.7)' },
})}
</motion.div>
</AnimatePresence>
</IconWrapper>
}
aria-label={tab.label}
ref={(el) => (tabRefs.current[index] = el)}
/>
))}
</StyledTabs>
<Box p={3} mt={10}>
{tabContent[value] && (
<>
<Typography variant="h4" gutterBottom>
{tabContent[value].label}
</Typography>
<Typography variant="body1">
{tabContent[value].content}
</Typography>
</>
)}
</Box>
</>
)
}
For now, the webview only needs to render the following "Tabs" internally:
v0 has kindly given me some (probably broken) code for this, as follows: