* Reorganize repo * Overhaul website design and rewrite in NextJS and Typescript * Delete website-publish.yml
169 lines
4.5 KiB
TypeScript
169 lines
4.5 KiB
TypeScript
import AddIcon from '@mui/icons-material/Add';
|
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
|
import Button from '@mui/material/Button';
|
|
import IconButton from '@mui/material/IconButton';
|
|
import Menu from '@mui/material/Menu';
|
|
import MenuItem from '@mui/material/MenuItem';
|
|
import { styled } from '@mui/material/styles';
|
|
import React, { useCallback } from 'react';
|
|
|
|
import { transientOptions } from '../../lib';
|
|
import { colors, colorsRaw, transitions } from './styles';
|
|
|
|
import type { MouseEvent, ReactNode } from 'react';
|
|
import type { ObjectField, TranslatedProps } from '../../interface';
|
|
|
|
const TopBarContainer = styled('div')`
|
|
position: relative;
|
|
align-items: center;
|
|
background-color: ${colors.textFieldBorder};
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 2px 8px;
|
|
`;
|
|
|
|
interface ExpandButtonContainerProps {
|
|
$hasError: boolean;
|
|
}
|
|
|
|
const ExpandButtonContainer = styled(
|
|
'div',
|
|
transientOptions,
|
|
)<ExpandButtonContainerProps>(
|
|
({ $hasError }) => `
|
|
display: flex;
|
|
align-items: center;
|
|
color: rgba(0, 0, 0, 0.6);
|
|
font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif;
|
|
font-weight: 400;
|
|
font-size: 1rem;
|
|
line-height: 1.4375em;
|
|
letter-spacing: 0.00938em;
|
|
${$hasError ? `color: ${colorsRaw.red}` : ''}
|
|
`,
|
|
);
|
|
|
|
export interface ObjectWidgetTopBarProps {
|
|
allowAdd?: boolean;
|
|
types?: ObjectField[];
|
|
onAdd?: (event: MouseEvent) => void;
|
|
onAddType?: (name: string) => void;
|
|
onCollapseToggle: (event: MouseEvent) => void;
|
|
collapsed: boolean;
|
|
heading: ReactNode;
|
|
label?: string;
|
|
hasError?: boolean;
|
|
}
|
|
|
|
const ObjectWidgetTopBar = ({
|
|
allowAdd,
|
|
types,
|
|
onAdd,
|
|
onAddType,
|
|
onCollapseToggle,
|
|
collapsed,
|
|
heading,
|
|
label,
|
|
hasError = false,
|
|
t,
|
|
}: TranslatedProps<ObjectWidgetTopBarProps>) => {
|
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
|
const open = Boolean(anchorEl);
|
|
const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
|
|
setAnchorEl(event.currentTarget);
|
|
}, []);
|
|
const handleClose = useCallback(() => {
|
|
setAnchorEl(null);
|
|
}, []);
|
|
const handleAddType = useCallback(
|
|
(type: ObjectField) => () => {
|
|
handleClose();
|
|
onAddType?.(type.name);
|
|
},
|
|
[handleClose, onAddType],
|
|
);
|
|
|
|
const renderTypesDropdown = useCallback(
|
|
(types: ObjectField[]) => {
|
|
return (
|
|
<div>
|
|
<Button
|
|
id="types-button"
|
|
aria-controls={open ? 'types-menu' : undefined}
|
|
aria-haspopup="true"
|
|
aria-expanded={open ? 'true' : undefined}
|
|
onClick={handleClick}
|
|
variant="outlined"
|
|
size="small"
|
|
endIcon={<AddIcon fontSize="small" />}
|
|
>
|
|
{t('editor.editorWidgets.list.addType', { item: label })}
|
|
</Button>
|
|
<Menu
|
|
id="types-menu"
|
|
anchorEl={anchorEl}
|
|
open={open}
|
|
onClose={handleClose}
|
|
MenuListProps={{
|
|
'aria-labelledby': 'types-button',
|
|
}}
|
|
>
|
|
{types.map((type, idx) =>
|
|
type ? (
|
|
<MenuItem key={idx} onClick={handleAddType(type)}>
|
|
{type.label ?? type.name}
|
|
</MenuItem>
|
|
) : null,
|
|
)}
|
|
</Menu>
|
|
</div>
|
|
);
|
|
},
|
|
[open, handleClick, t, label, anchorEl, handleClose, handleAddType],
|
|
);
|
|
|
|
const renderAddButton = useCallback(() => {
|
|
return (
|
|
<Button
|
|
onClick={onAdd}
|
|
endIcon={<AddIcon fontSize="small" />}
|
|
size="small"
|
|
variant="outlined"
|
|
>
|
|
{t('editor.editorWidgets.list.add', { item: label })}
|
|
</Button>
|
|
);
|
|
}, [t, label, onAdd]);
|
|
|
|
const renderAddUI = useCallback(() => {
|
|
if (!allowAdd) {
|
|
return null;
|
|
}
|
|
if (types && types.length > 0) {
|
|
return renderTypesDropdown(types);
|
|
} else {
|
|
return renderAddButton();
|
|
}
|
|
}, [allowAdd, types, renderTypesDropdown, renderAddButton]);
|
|
|
|
return (
|
|
<TopBarContainer>
|
|
<ExpandButtonContainer $hasError={hasError}>
|
|
<IconButton onClick={onCollapseToggle} data-testid="expand-button">
|
|
<ExpandMoreIcon
|
|
sx={{
|
|
transform: `rotateZ(${collapsed ? '-90deg' : '0deg'})`,
|
|
transition: `transform ${transitions.main};`,
|
|
color: hasError ? colorsRaw.red : undefined,
|
|
}}
|
|
/>
|
|
</IconButton>
|
|
{heading}
|
|
</ExpandButtonContainer>
|
|
{renderAddUI()}
|
|
</TopBarContainer>
|
|
);
|
|
};
|
|
|
|
export default ObjectWidgetTopBar;
|