mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-14 00:19:16 +01:00
Segment preview (#1194)
* segment preview * fix: loading and font size inconsistencies * update segment accordion styles
This commit is contained in:
parent
672a3f0b92
commit
b7de1fba52
@ -26,9 +26,6 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
'&:before': {
|
'&:before': {
|
||||||
opacity: '0 !important',
|
opacity: '0 !important',
|
||||||
},
|
},
|
||||||
'&:first-of-type, &:last-of-type': {
|
|
||||||
borderRadius: theme.shape.borderRadiusMedium,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
accordionEdit: {
|
accordionEdit: {
|
||||||
backgroundColor: '#F6F6FA',
|
backgroundColor: '#F6F6FA',
|
||||||
|
@ -48,7 +48,7 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
fontWeight: theme.fontWeight.bold,
|
fontWeight: theme.fontWeight.bold,
|
||||||
fontSize: theme.fontSizes.subHeader,
|
fontSize: theme.fontSizes.bodySize,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
import { makeStyles } from 'tss-react/mui';
|
||||||
|
|
||||||
|
export const useStyles = makeStyles()(theme => ({
|
||||||
|
container: {
|
||||||
|
width: '100%',
|
||||||
|
padding: theme.spacing(2, 3),
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
fontSize: theme.fontSizes.smallBody,
|
||||||
|
border: `1px solid ${theme.palette.dividerAlternative}`,
|
||||||
|
position: 'relative',
|
||||||
|
borderRadius: '5px',
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
textDecoration: 'none',
|
||||||
|
marginLeft: theme.spacing(1),
|
||||||
|
'&:hover': {
|
||||||
|
textDecoration: 'underline',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
accordion: {
|
||||||
|
border: `1px solid ${theme.palette.dividerAlternative}`,
|
||||||
|
borderRadius: theme.shape.borderRadiusMedium,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
boxShadow: 'none',
|
||||||
|
margin: 0,
|
||||||
|
},
|
||||||
|
accordionRoot: {
|
||||||
|
transition: 'all 0.1s ease',
|
||||||
|
},
|
||||||
|
accordionExpanded: {
|
||||||
|
backgroundColor: theme.palette.neutral.light,
|
||||||
|
},
|
||||||
|
previewButton: {
|
||||||
|
paddingTop: 0,
|
||||||
|
paddingBottom: 0,
|
||||||
|
marginLeft: 'auto',
|
||||||
|
fontSize: theme.fontSizes.smallBody,
|
||||||
|
},
|
||||||
|
summary: {
|
||||||
|
fontSize: theme.fontSizes.smallBody,
|
||||||
|
margin: theme.spacing(0.5, 0),
|
||||||
|
},
|
||||||
|
}));
|
81
frontend/src/component/common/SegmentItem/SegmentItem.tsx
Normal file
81
frontend/src/component/common/SegmentItem/SegmentItem.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { useState, VFC } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { DonutLarge } from '@mui/icons-material';
|
||||||
|
import { ISegment } from 'interfaces/segment';
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionDetails,
|
||||||
|
AccordionSummary,
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/material';
|
||||||
|
import { ConstraintAccordionList } from '../ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList';
|
||||||
|
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
|
||||||
|
import { useStyles } from './SegmentItem.styles';
|
||||||
|
|
||||||
|
interface ISegmentItemProps {
|
||||||
|
segment: ISegment;
|
||||||
|
isExpanded?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SegmentItem: VFC<ISegmentItemProps> = ({
|
||||||
|
segment,
|
||||||
|
isExpanded,
|
||||||
|
}) => {
|
||||||
|
const { classes } = useStyles();
|
||||||
|
const [isOpen, setIsOpen] = useState(isExpanded || false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Accordion
|
||||||
|
expanded={isOpen}
|
||||||
|
className={classes.accordion}
|
||||||
|
classes={{
|
||||||
|
root: classes.accordionRoot,
|
||||||
|
expanded: classes.accordionExpanded,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AccordionSummary
|
||||||
|
id={`segment-accordion-${segment.id}`}
|
||||||
|
className={classes.summary}
|
||||||
|
>
|
||||||
|
<DonutLarge color="secondary" sx={{ mr: 1 }} />
|
||||||
|
Segment:
|
||||||
|
<Link
|
||||||
|
to={`/segments/edit/${segment.id}`}
|
||||||
|
className={classes.link}
|
||||||
|
>
|
||||||
|
{segment.name}
|
||||||
|
</Link>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={!isExpanded}
|
||||||
|
show={
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => setIsOpen(value => !value)}
|
||||||
|
className={classes.previewButton}
|
||||||
|
>
|
||||||
|
{isOpen ? 'Close preview' : 'Preview'}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails sx={{ pt: 0 }}>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={segment!.constraints?.length > 0}
|
||||||
|
show={
|
||||||
|
<ConstraintAccordionList
|
||||||
|
constraints={segment!.constraints}
|
||||||
|
showLabel={false}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
elseShow={
|
||||||
|
<Typography>
|
||||||
|
This segment has no constraints.
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
);
|
||||||
|
};
|
@ -26,6 +26,7 @@ const StyledCenteredContent = styled(StyledContent)(({ theme }) => ({
|
|||||||
backgroundColor: theme.palette.activityIndicators.primary,
|
backgroundColor: theme.palette.activityIndicators.primary,
|
||||||
border: `1px solid ${theme.palette.primary.border}`,
|
border: `1px solid ${theme.palette.primary.border}`,
|
||||||
borderRadius: theme.shape.borderRadiusLarge,
|
borderRadius: theme.shape.borderRadiusLarge,
|
||||||
|
padding: theme.spacing(0.75, 1.5),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const StrategySeparator = ({ text }: IStrategySeparatorProps) => {
|
export const StrategySeparator = ({ text }: IStrategySeparatorProps) => {
|
||||||
|
@ -16,7 +16,7 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: 'auto 1fr',
|
gridTemplateColumns: 'auto 1fr',
|
||||||
gridGap: '.5rem',
|
gridGap: '.5rem',
|
||||||
fontSize: theme.fontSizes.subHeader,
|
fontSize: theme.fontSizes.bodySize,
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
color: theme.palette.primary.main,
|
color: theme.palette.primary.main,
|
||||||
|
@ -3,5 +3,7 @@ import { makeStyles } from 'tss-react/mui';
|
|||||||
export const useStyles = makeStyles()(theme => ({
|
export const useStyles = makeStyles()(theme => ({
|
||||||
divider: {
|
divider: {
|
||||||
border: `1px dashed ${theme.palette.divider}`,
|
border: `1px dashed ${theme.palette.divider}`,
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -12,10 +12,7 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
gap: '0.5rem',
|
gap: '0.5rem',
|
||||||
},
|
},
|
||||||
and: {
|
and: {
|
||||||
color: theme.palette.grey[600],
|
|
||||||
fontSize: theme.fontSizes.smallerBody,
|
fontSize: theme.fontSizes.smallerBody,
|
||||||
border: '1px solid',
|
|
||||||
borderColor: theme.palette.grey[300],
|
|
||||||
padding: theme.spacing(0.75, 1),
|
padding: theme.spacing(0.75, 1),
|
||||||
display: 'block',
|
display: 'block',
|
||||||
marginTop: 'auto',
|
marginTop: 'auto',
|
||||||
@ -23,6 +20,8 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
borderRadius: theme.shape.borderRadius,
|
borderRadius: theme.shape.borderRadius,
|
||||||
lineHeight: 1,
|
lineHeight: 1,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
backgroundColor: theme.palette.secondaryContainer,
|
||||||
},
|
},
|
||||||
selectedSegmentsLabel: {
|
selectedSegmentsLabel: {
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
|
@ -3,7 +3,7 @@ import { ISegment } from 'interfaces/segment';
|
|||||||
import { useStyles } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegmentList.styles';
|
import { useStyles } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegmentList.styles';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { FeatureStrategySegmentChip } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegmentChip';
|
import { FeatureStrategySegmentChip } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegmentChip';
|
||||||
import { ConstraintAccordionList } from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList';
|
import { SegmentItem } from 'component/common/SegmentItem/SegmentItem';
|
||||||
|
|
||||||
interface IFeatureStrategySegmentListProps {
|
interface IFeatureStrategySegmentListProps {
|
||||||
segments: ISegment[];
|
segments: ISegment[];
|
||||||
@ -48,16 +48,10 @@ export const FeatureStrategySegmentList = ({
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<ConditionallyRender
|
|
||||||
condition={Boolean(preview && preview.constraints.length === 0)}
|
|
||||||
show={() => <p>This segment has no constraints.</p>}
|
|
||||||
/>
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={Boolean(preview)}
|
condition={Boolean(preview)}
|
||||||
show={() => (
|
show={() => (
|
||||||
<ConstraintAccordionList
|
<SegmentItem segment={preview as ISegment} isExpanded />
|
||||||
constraints={preview!.constraints}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -19,7 +19,7 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
fontSize: theme.fontSizes.subHeader,
|
fontSize: theme.fontSizes.bodySize,
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
margin: 0,
|
margin: 0,
|
||||||
marginBottom: '0.5rem',
|
marginBottom: '0.5rem',
|
||||||
|
@ -31,7 +31,7 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
percentage: {
|
percentage: {
|
||||||
color: theme.palette.primary.main,
|
color: theme.palette.primary.main,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
fontSize: theme.fontSizes.subHeader,
|
fontSize: theme.fontSizes.bodySize,
|
||||||
},
|
},
|
||||||
percentageCircle: {
|
percentageCircle: {
|
||||||
margin: '0 1rem',
|
margin: '0 1rem',
|
||||||
|
@ -19,7 +19,7 @@ const SeparatorContainer = styled('div')(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const SeparatorContent = styled('span')(({ theme }) => ({
|
const SeparatorContent = styled('span')(({ theme }) => ({
|
||||||
fontSize: theme.fontSizes.subHeader,
|
fontSize: theme.fontSizes.bodySize,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
padding: '0 1rem',
|
padding: '0 1rem',
|
||||||
background: theme.palette.secondaryContainer,
|
background: theme.palette.secondaryContainer,
|
||||||
|
@ -28,7 +28,7 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
fontSize: theme.fontSizes.subHeader,
|
fontSize: theme.fontSizes.bodySize,
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
margin: 0,
|
margin: 0,
|
||||||
},
|
},
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
import { makeStyles } from 'tss-react/mui';
|
|
||||||
|
|
||||||
export const useStyles = makeStyles()(theme => ({
|
|
||||||
container: {
|
|
||||||
width: '100%',
|
|
||||||
padding: theme.spacing(2, 3),
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
border: `1px solid ${theme.palette.dividerAlternative}`,
|
|
||||||
position: 'relative',
|
|
||||||
borderRadius: '5px',
|
|
||||||
},
|
|
||||||
link: {
|
|
||||||
textDecoration: 'none',
|
|
||||||
marginLeft: theme.spacing(1),
|
|
||||||
'&:hover': {
|
|
||||||
textDecoration: 'underline',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
@ -1,10 +1,8 @@
|
|||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { DonutLarge } from '@mui/icons-material';
|
|
||||||
import { useStyles } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment.styles';
|
|
||||||
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
|
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
|
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
|
||||||
|
import { SegmentItem } from '../../../../common/SegmentItem/SegmentItem';
|
||||||
|
|
||||||
interface IFeatureOverviewSegmentProps {
|
interface IFeatureOverviewSegmentProps {
|
||||||
strategyId: string;
|
strategyId: string;
|
||||||
@ -13,7 +11,6 @@ interface IFeatureOverviewSegmentProps {
|
|||||||
export const FeatureOverviewSegment = ({
|
export const FeatureOverviewSegment = ({
|
||||||
strategyId,
|
strategyId,
|
||||||
}: IFeatureOverviewSegmentProps) => {
|
}: IFeatureOverviewSegmentProps) => {
|
||||||
const { classes: styles } = useStyles();
|
|
||||||
const { segments } = useSegments(strategyId);
|
const { segments } = useSegments(strategyId);
|
||||||
|
|
||||||
if (!segments || segments.length === 0) {
|
if (!segments || segments.length === 0) {
|
||||||
@ -28,21 +25,9 @@ export const FeatureOverviewSegment = ({
|
|||||||
condition={index > 0}
|
condition={index > 0}
|
||||||
show={<StrategySeparator text="AND" />}
|
show={<StrategySeparator text="AND" />}
|
||||||
/>
|
/>
|
||||||
<div className={styles.container}>
|
<SegmentItem segment={segment} />
|
||||||
<DonutLarge color="secondary" sx={{ mr: 1 }} /> Segment:{' '}
|
|
||||||
<Link
|
|
||||||
to={segmentPath(segment.id)}
|
|
||||||
className={styles.link}
|
|
||||||
>
|
|
||||||
{segment.name}
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const segmentPath = (segmentId: number): string => {
|
|
||||||
return `/segments/edit/${segmentId}`;
|
|
||||||
};
|
|
||||||
|
@ -76,21 +76,6 @@ export const FeatureView = () => {
|
|||||||
|
|
||||||
const activeTab = tabData.find(tab => tab.path === pathname) ?? tabData[0];
|
const activeTab = tabData.find(tab => tab.path === pathname) ?? tabData[0];
|
||||||
|
|
||||||
const renderTabs = () => {
|
|
||||||
return tabData.map((tab, index) => {
|
|
||||||
return (
|
|
||||||
<Tab
|
|
||||||
data-loading
|
|
||||||
key={tab.title}
|
|
||||||
label={tab.title}
|
|
||||||
value={tab.path}
|
|
||||||
onClick={() => navigate(tab.path)}
|
|
||||||
className={styles.tabButton}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (status === 404) {
|
if (status === 404) {
|
||||||
return <FeatureNotFound />;
|
return <FeatureNotFound />;
|
||||||
}
|
}
|
||||||
@ -168,7 +153,15 @@ export const FeatureView = () => {
|
|||||||
indicatorColor="primary"
|
indicatorColor="primary"
|
||||||
textColor="primary"
|
textColor="primary"
|
||||||
>
|
>
|
||||||
{renderTabs()}
|
{tabData.map(tab => (
|
||||||
|
<Tab
|
||||||
|
key={tab.title}
|
||||||
|
label={tab.title}
|
||||||
|
value={tab.path}
|
||||||
|
onClick={() => navigate(tab.path)}
|
||||||
|
className={styles.tabButton}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,21 +80,6 @@ const Project = () => {
|
|||||||
/* eslint-disable-next-line */
|
/* eslint-disable-next-line */
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const renderTabs = () => {
|
|
||||||
return tabs.map(tab => {
|
|
||||||
return (
|
|
||||||
<Tab
|
|
||||||
data-loading
|
|
||||||
key={tab.title}
|
|
||||||
label={tab.title}
|
|
||||||
value={tab.path}
|
|
||||||
onClick={() => navigate(tab.path)}
|
|
||||||
className={styles.tabButton}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ref}>
|
<div ref={ref}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
@ -135,7 +120,15 @@ const Project = () => {
|
|||||||
indicatorColor="primary"
|
indicatorColor="primary"
|
||||||
textColor="primary"
|
textColor="primary"
|
||||||
>
|
>
|
||||||
{renderTabs()}
|
{tabs.map(tab => (
|
||||||
|
<Tab
|
||||||
|
key={tab.title}
|
||||||
|
label={tab.title}
|
||||||
|
value={tab.path}
|
||||||
|
onClick={() => navigate(tab.path)}
|
||||||
|
className={styles.tabButton}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,7 +36,6 @@ export default createTheme({
|
|||||||
},
|
},
|
||||||
fontSizes: {
|
fontSizes: {
|
||||||
mainHeader: '1.25rem',
|
mainHeader: '1.25rem',
|
||||||
subHeader: '1.1rem',
|
|
||||||
bodySize: '1rem',
|
bodySize: '1rem',
|
||||||
smallBody: `${14 / 16}rem`,
|
smallBody: `${14 / 16}rem`,
|
||||||
smallerBody: `${12 / 16}rem`,
|
smallerBody: `${12 / 16}rem`,
|
||||||
@ -260,12 +259,24 @@ export default createTheme({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MuiAccordion: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
'&:first-of-type, &:last-of-type': {
|
||||||
|
borderRadius: theme.shape.borderRadiusMedium,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
MuiAccordionSummary: {
|
MuiAccordionSummary: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
'& > .MuiAccordionSummary-content.Mui-expanded': {
|
'& > .MuiAccordionSummary-content.Mui-expanded': {
|
||||||
margin: '12px 0',
|
margin: '12px 0',
|
||||||
},
|
},
|
||||||
|
'&.Mui-expanded': {
|
||||||
|
minHeight: '0',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,6 @@ declare module '@mui/material/styles' {
|
|||||||
*/
|
*/
|
||||||
fontSizes: {
|
fontSizes: {
|
||||||
mainHeader: string;
|
mainHeader: string;
|
||||||
subHeader: string;
|
|
||||||
bodySize: string;
|
bodySize: string;
|
||||||
smallBody: string;
|
smallBody: string;
|
||||||
smallerBody: string;
|
smallerBody: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user