|
| 1 | +import { ListItemProps, styled } from '@mui/material'; |
| 2 | +import { Resizable } from 're-resizable'; |
| 3 | +import React from 'react'; |
| 4 | +import Draggable from 'react-draggable'; |
| 5 | +import { Box, BoxProps, IconButton, ListItem, Tooltip } from '../../base'; |
| 6 | +import { CloseIcon, CollapseAllIcon, ExpandAllIcon } from '../../icons'; |
| 7 | +import { PanelDragHandleIcon } from '../../icons/PanelDragHandle'; |
| 8 | +import { useTheme } from '../../theme'; |
| 9 | +import { ErrorBoundary } from '../ErrorBoundary'; |
| 10 | + |
| 11 | +export const ListHeader = styled(ListItem)(({ theme }) => ({ |
| 12 | + padding: theme.spacing(0.5, 0.5), |
| 13 | + marginBlock: theme.spacing(1), |
| 14 | + '& .MuiListItemText-primary': { |
| 15 | + fontSize: '1rem', |
| 16 | + textTransform: 'capitalize', |
| 17 | + fontWeight: 700 |
| 18 | + }, |
| 19 | + cursor: 'pointer', |
| 20 | + '&:hover': { |
| 21 | + backgroundColor: theme.palette.action.hover |
| 22 | + }, |
| 23 | + '& .MuiSvgIcon-root': { |
| 24 | + opacity: 0, |
| 25 | + transition: 'opacity 0.2s' |
| 26 | + }, |
| 27 | + '&:hover .MuiSvgIcon-root': { |
| 28 | + opacity: 1 |
| 29 | + } |
| 30 | +})); |
| 31 | + |
| 32 | +interface CustomListItemProps extends ListItemProps { |
| 33 | + isVisible: boolean; |
| 34 | +} |
| 35 | + |
| 36 | +// Use the new interface in the styled component |
| 37 | +export const StyledListItem = styled(ListItem, { |
| 38 | + shouldForwardProp: (props) => props !== 'isVisible' |
| 39 | +})<CustomListItemProps>(({ theme, isVisible }) => ({ |
| 40 | + padding: theme.spacing(0.05, 0.5), |
| 41 | + fontStyle: isVisible ? 'normal' : 'italic', |
| 42 | + overflow: 'hidden', |
| 43 | + textOverflow: 'ellipsis', |
| 44 | + whiteSpace: 'nowrap', |
| 45 | + '& .MuiSvgIcon-root': { |
| 46 | + height: 20, |
| 47 | + width: 20 |
| 48 | + }, |
| 49 | + '& .MuiListItemIcon-root': { |
| 50 | + minWidth: 0, |
| 51 | + opacity: isVisible ? 0.8 : 0.3 |
| 52 | + }, |
| 53 | + '& .MuiTypography-root': { |
| 54 | + fontSize: '0.9rem', |
| 55 | + opacity: isVisible ? 1 : 0.5 |
| 56 | + } |
| 57 | +})); |
| 58 | + |
| 59 | +type PanelProps = { |
| 60 | + isOpen: boolean; |
| 61 | + children: React.ReactNode; |
| 62 | + areAllExpanded?: boolean; |
| 63 | + toggleExpandAll?: () => void; |
| 64 | + handleClose: () => void; |
| 65 | + sx?: BoxProps['sx']; |
| 66 | + id?: string; |
| 67 | + intitialPosition?: { |
| 68 | + left?: string | number; |
| 69 | + right?: string | number; |
| 70 | + top?: string | number; |
| 71 | + bottom?: string | number; |
| 72 | + }; |
| 73 | +}; |
| 74 | + |
| 75 | +export const DrawerHeader = styled('div')(({ theme }) => ({ |
| 76 | + display: 'flex', |
| 77 | + alignItems: 'center', |
| 78 | + padding: theme.spacing(4, 2), |
| 79 | + alignContent: 'stretch', |
| 80 | + justifyContent: 'space-between', |
| 81 | + cursor: 'move', |
| 82 | + background: |
| 83 | + theme.palette.mode === 'light' |
| 84 | + ? 'linear-gradient(90deg, #3B687B 0%, #507D90 100%)' |
| 85 | + : 'linear-gradient(90deg, #28353A 0%, #3D4F57 100%)', |
| 86 | + height: '3rem', |
| 87 | + flexShrink: 0 |
| 88 | +})); |
| 89 | + |
| 90 | +const PanelBody = styled(Box)(({ theme }) => ({ |
| 91 | + padding: theme.spacing(2), |
| 92 | + backgroundColor: theme.palette.background.surfaces, |
| 93 | + overflow: 'auto', |
| 94 | + flex: 1, |
| 95 | + minHeight: 0 |
| 96 | +})); |
| 97 | + |
| 98 | +// New container for Resizable content |
| 99 | +const ResizableContent = styled('div')({ |
| 100 | + height: '100%', |
| 101 | + display: 'flex', |
| 102 | + flexDirection: 'column', |
| 103 | + minHeight: '3rem' |
| 104 | +}); |
| 105 | + |
| 106 | +// // watches for the size of the element |
| 107 | +// const useDimensions = (ref: React.RefObject<HTMLDivElement>) => { |
| 108 | +// const [dimensions, setDimensions] = React.useState({ width: 0, height: 0 }); |
| 109 | +// React.useEffect(() => { |
| 110 | +// const { current } = ref; |
| 111 | +// if (current) { |
| 112 | +// const resizeObserver = new ResizeObserver((entries) => { |
| 113 | +// entries.forEach((entry) => { |
| 114 | +// setDimensions({ |
| 115 | +// width: entry.contentRect.width, |
| 116 | +// height: entry.contentRect.height |
| 117 | +// }); |
| 118 | +// }); |
| 119 | +// }); |
| 120 | +// resizeObserver.observe(current); |
| 121 | +// return () => { |
| 122 | +// resizeObserver.unobserve(current); |
| 123 | +// }; |
| 124 | +// } |
| 125 | +// }, [ref]); |
| 126 | +// return dimensions; |
| 127 | +// }; |
| 128 | + |
| 129 | +const Panel_: React.FC<PanelProps> = ({ |
| 130 | + isOpen, |
| 131 | + id = 'panel', |
| 132 | + children, |
| 133 | + areAllExpanded, |
| 134 | + toggleExpandAll, |
| 135 | + handleClose, |
| 136 | + intitialPosition, |
| 137 | + sx |
| 138 | +}) => { |
| 139 | + const theme = useTheme(); |
| 140 | + // const mode = theme?.palette?.type; |
| 141 | + if (!isOpen) return null; |
| 142 | + return ( |
| 143 | + // <SistentThemeProviderWithoutBaseLine initialMode={mode}> |
| 144 | + <Draggable handle=".drag-handle"> |
| 145 | + <Box |
| 146 | + sx={{ |
| 147 | + borderRadius: '8px', |
| 148 | + overflow: 'hidden', |
| 149 | + flexShrink: 0, |
| 150 | + zIndex: 99999, |
| 151 | + position: 'absolute', |
| 152 | + backgroundColor: theme.palette.background.blur?.light, |
| 153 | + boxShadow: '0 4px 16px #05003812', |
| 154 | + maxHeight: '80%', |
| 155 | + display: 'flex', |
| 156 | + boxSizing: 'border-box', |
| 157 | + ...(intitialPosition || { |
| 158 | + top: '6rem', |
| 159 | + right: '2rem' |
| 160 | + }), |
| 161 | + ...(sx || {}) |
| 162 | + }} |
| 163 | + > |
| 164 | + <Resizable |
| 165 | + defaultSize={{ width: '18rem', height: 'auto' }} |
| 166 | + onResize={() => { |
| 167 | + window.dispatchEvent(new Event('panel-resize')); |
| 168 | + }} |
| 169 | + enable={{ |
| 170 | + top: true, |
| 171 | + right: true, |
| 172 | + bottom: true, |
| 173 | + left: true, |
| 174 | + topRight: true, |
| 175 | + bottomRight: true, |
| 176 | + bottomLeft: true, |
| 177 | + topLeft: true |
| 178 | + }} |
| 179 | + > |
| 180 | + <ResizableContent> |
| 181 | + <ErrorBoundary> |
| 182 | + <div className="drag-handle"> |
| 183 | + <DrawerHeader> |
| 184 | + <Box display="flex" justifyContent="flex-end" padding="8px"> |
| 185 | + {toggleExpandAll && ( |
| 186 | + <Tooltip title={areAllExpanded ? 'Collapse All' : 'Expand All'}> |
| 187 | + <IconButton onClick={toggleExpandAll}> |
| 188 | + {areAllExpanded ? <CollapseAllIcon /> : <ExpandAllIcon />} |
| 189 | + </IconButton> |
| 190 | + </Tooltip> |
| 191 | + )} |
| 192 | + </Box> |
| 193 | + <PanelDragHandleIcon |
| 194 | + fill={theme.palette.icon.default} |
| 195 | + style={{ marginTop: '-3rem', position: 'absolute', left: '50%' }} |
| 196 | + /> |
| 197 | + <div |
| 198 | + style={{ |
| 199 | + display: 'flex', |
| 200 | + justifyContent: 'end', |
| 201 | + alignItems: 'center', |
| 202 | + flex: 1 |
| 203 | + }} |
| 204 | + > |
| 205 | + <div |
| 206 | + id={`${id}-panel-header-actions-container`} |
| 207 | + style={{ |
| 208 | + display: 'flex', |
| 209 | + gap: '1rem', |
| 210 | + justifyContent: 'flex-end', |
| 211 | + alignItems: 'center' |
| 212 | + }} |
| 213 | + ></div> |
| 214 | + <IconButton onClick={handleClose}> |
| 215 | + <CloseIcon fill={theme.palette.icon.default} /> |
| 216 | + </IconButton> |
| 217 | + </div> |
| 218 | + </DrawerHeader> |
| 219 | + </div> |
| 220 | + |
| 221 | + <PanelBody className="panel-body">{children}</PanelBody> |
| 222 | + </ErrorBoundary> |
| 223 | + </ResizableContent> |
| 224 | + </Resizable> |
| 225 | + </Box> |
| 226 | + </Draggable> |
| 227 | + // </SistentThemeProviderWithoutBaseLine> |
| 228 | + ); |
| 229 | +}; |
| 230 | + |
| 231 | +export const Panel: React.FC<PanelProps> = ({ ...props }) => { |
| 232 | + return ( |
| 233 | + // <SistentThemeProviderWithoutBaseLine initialMode={mode}> |
| 234 | + <Panel_ {...props} /> |
| 235 | + // </SistentThemeProviderWithoutBaseLine> |
| 236 | + ); |
| 237 | +}; |
0 commit comments