@mui/material/styles#useTheme TypeScript Examples
The following examples show how to use
@mui/material/styles#useTheme.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: DrawerLayout.tsx From react-component-library with BSD 3-Clause "New" or "Revised" License | 6 votes |
DrawerLayoutRender: React.ForwardRefRenderFunction<unknown, DrawerLayoutProps> = (
props: DrawerLayoutProps,
ref: any
) => {
const { children, drawer, classes, ...otherDivProps } = props;
const theme = useTheme();
const [padding, setPadding] = useState<number | string>(0);
const [drawerOpen, setDrawerOpen] = useState(false);
const defaultClasses = useStyles();
const style: CSSProperties = { paddingLeft: 0, paddingRight: 0 };
style.paddingLeft = theme.direction === 'ltr' ? padding : 0;
style.paddingRight = theme.direction === 'rtl' ? padding : 0;
return (
<DrawerLayoutContext.Provider
value={{
setPadding,
setDrawerOpen,
}}
>
<div
ref={ref}
className={clsx(defaultClasses.root, classes.root, {
[defaultClasses.expanded]: !drawerOpen,
[classes.expanded]: !drawerOpen,
})}
{...otherDivProps}
>
<div className={clsx(defaultClasses.drawer, classes.drawer)}>{drawer}</div>
<div className={clsx(defaultClasses.content, classes.content)} style={style}>
{children}
</div>
</div>
</DrawerLayoutContext.Provider>
);
}
Example #2
Source File: FormGroup.tsx From ui-schema with MIT License | 6 votes |
FormGroupBase: React.ComponentType<WidgetProps<MuiWidgetBinding> & WithValue> = (props) => {
const {storeKeys, widgets} = props
const {WidgetRenderer} = widgets
const {spacing} = useTheme()
let {schema} = props
// deleting the `widget` to directly use `PluginStack` for nesting
// with `widget` it would lead to an endless loop
// using e.g. default `object` renderer then
// @ts-ignore
schema = schema.delete('widget')
return <FormControl
component="fieldset"
style={{
display: 'block',
marginBottom: spacing(1),
}}
>
<FormLabel component="legend">
<TransTitle schema={schema} storeKeys={storeKeys}/>
</FormLabel>
<MuiFormGroup
style={{
marginTop: spacing(1),
marginBottom: spacing(1),
}}
>
<WidgetRenderer {...props} schema={schema}/>
</MuiFormGroup>
{/*<FormHelperText>Be careful</FormHelperText>*/}
</FormControl>
}
Example #3
Source File: index.tsx From next-typescript-materialui-jest-starter with MIT License | 6 votes |
Home = () => {
const theme = useTheme();
const hello = "Hello";
return (
<>
<div style={{ color: theme.palette.primary.main }}>{hello}</div>
<Button name="Click Me" color="primary" />
<Example />
</>
);
}
Example #4
Source File: DesktopSideBar.tsx From Tachidesk-WebUI with Mozilla Public License 2.0 | 6 votes |
export default function DesktopSideBar({ navBarItems }: IProps) {
const location = useLocation();
const theme = useTheme();
const iconFor = (path: string, IconComponent: any, SelectedIconComponent: any) => {
if (location.pathname === path) return <SelectedIconComponent sx={{ color: 'primary.main' }} fontSize="large" />;
return <IconComponent sx={{ color: (theme.palette.mode === 'dark') ? 'grey.A400' : 'grey.600' }} fontSize="large" />;
};
return (
<SideNavBarContainer>
{
// eslint-disable-next-line react/destructuring-assignment
navBarItems.map(({
path, title, IconComponent, SelectedIconComponent,
}: NavbarItem) => (
<Link to={path} style={{ color: 'inherit', textDecoration: 'none' }} key={path}>
<ListItem disableRipple button key={title}>
<ListItemIcon sx={{ minWidth: '0' }}>
<Tooltip placement="right" title={title}>
{iconFor(path, IconComponent, SelectedIconComponent)}
</Tooltip>
</ListItemIcon>
</ListItem>
</Link>
))
}
</SideNavBarContainer>
);
}
Example #5
Source File: QueuedAttachmentGeneric.tsx From airmessage-web with Apache License 2.0 | 6 votes |
export default function QueuedAttachmentGeneric(props: {queueData: QueuedAttachmentProps}) {
const theme = useTheme();
return (
<QueuedAttachment queueData={props.queueData}>
<div className={styles.content} style={{backgroundColor: theme.palette.background.default}}>
<InsertDriveFileRounded />
</div>
</QueuedAttachment>
);
}
Example #6
Source File: EmptyView.tsx From Tachidesk-WebUI with Mozilla Public License 2.0 | 6 votes |
export default function EmptyView({ message, messageExtra }: IProps) {
const theme = useTheme();
const isMobileWidth = useMediaQuery(theme.breakpoints.down('sm'));
return (
<Box sx={{
position: 'absolute',
left: `calc(50% + ${isMobileWidth ? '0px' : theme.spacing(8 / 2)})`,
top: '50%',
transform: 'translate(-50%, -50%)',
textAlign: 'center',
}}
>
<Typography variant="h3" gutterBottom>
{getRandomErrorFace()}
</Typography>
<Typography variant="h5">
{message}
</Typography>
{messageExtra}
</Box>
);
}
Example #7
Source File: Profiler.tsx From NekoMaid with MIT License | 6 votes |
Pie: React.FC<{ title: string, data: any[], formatter?: any }> = React.memo(({ title, data, formatter }) => <Grid item sm={6} xs={12}>
<Card>
<CardHeader title={title} />
<Divider />
{data.length
? <ReactECharts style={{ height: 300 }} theme={useTheme().palette.mode === 'dark' ? 'dark' : undefined} option={{
backgroundColor: 'rgba(0, 0, 0, 0)',
itemStyle: {
borderRadius: 5,
borderColor: 'rgba(0, 0, 0, 0)',
borderWidth: 4
},
tooltip: {
trigger: 'item',
formatter
},
series: [{
type: 'pie',
radius: '50%',
data,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}]
}} />
: <CardContent><Empty /></CardContent>}
</Card>
</Grid>)
Example #8
Source File: MessageModifierTapbackRow.tsx From airmessage-web with Apache License 2.0 | 6 votes |
function MessageModifierTapback(props: {type: TapbackType, count: number}) {
const theme = useTheme();
const iconColor = theme.palette.text.secondary;
let icon: JSX.Element;
switch(props.type) {
case TapbackType.Love:
icon = <TapbackLoveIcon className={styles.icon} style={{color: iconColor}} />;
break;
case TapbackType.Like:
icon = <TapbackLikeIcon className={styles.icon} style={{color: iconColor}} />;
break;
case TapbackType.Dislike:
icon = <TapbackDislikeIcon className={styles.icon} style={{color: iconColor}} />;
break;
case TapbackType.Laugh:
icon = <TapbackLaughIcon className={styles.icon} style={{color: iconColor}} />;
break;
case TapbackType.Emphasis:
icon = <TapbackEmphasisIcon className={styles.icon} style={{color: iconColor}} />;
break;
case TapbackType.Question:
icon = <TapbackQuestionIcon className={styles.icon} style={{color: iconColor}} />;
break;
}
return (
<div className={styles.tapback} style={{backgroundColor: theme.palette.messageIncoming.main, borderColor: theme.palette.background.default}}>
{icon}
{props.count > 1 && <span className={styles.label} style={{color: iconColor}}>{props.count}</span>}
</div>
);
}
Example #9
Source File: Empty.tsx From NekoMaid with MIT License | 6 votes |
EmptyIcon: React.FC<React.SVGProps<SVGSVGElement>> = props => {
return <svg
width='121'
height='100'
viewBox='0 0 184 152'
xmlns='http://www.w3.org/2000/svg'
{...props}
style={{
...(props.style || {}),
filter: useTheme().palette.mode === 'dark'
? 'brightness(0.7) drop-shadow(rgba(255, 255, 255, 0.2) 1px 3px 6px)'
: 'drop-shadow(rgba(0, 0, 0, 0.1) 2px 4px 4px)'
}}
>
<g fill='none' fillRule='evenodd'>
<g transform='translate(24 31.67)'>
<path fill='#aeb8c2' d='M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z' />
<path fill='#f5f5f7' d='M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z' />
<path fill='#dce0e6' d='M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z' />
</g>
<path fill='#dce0e6' d='M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z' />
<g fill='#fff' transform='translate(149.65 15.383)'>
<ellipse cx='20.654' cy='3.167' rx='2.849' ry='2.815' />
<path d='M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z' />
</g>
</g>
</svg>
}
Example #10
Source File: VerticalTabs.tsx From console with GNU Affero General Public License v3.0 | 5 votes |
VerticalTabs = ({
children,
classes,
selectedTab = "0",
routes,
isRouteTabs,
}: VerticalTabsProps) => {
const [value, setValue] = React.useState(selectedTab);
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down("md"));
const handleChange = (event: React.SyntheticEvent, newValue: string) => {
setValue(newValue);
};
const headerList: TabProps[] = [];
const contentList: React.ReactNode[] = [];
if (!children) return null;
children.forEach((child) => {
headerList.push(child.tabConfig);
contentList.push(child.content);
});
return (
<TabContext value={`${value}`}>
<Box className={classes.tabsContainer}>
<Box className={classes.tabsHeaderContainer}>
<TabList
onChange={handleChange}
orientation={isSmallScreen ? "horizontal" : "vertical"}
variant={isSmallScreen ? "scrollable" : "standard"}
scrollButtons="auto"
className={classes.tabList}
>
{headerList.map((item, index) => {
if (item) {
return (
<Tab
className={classes.tabHeader}
key={`v-tab-${index}`}
value={`${index}`}
style={tabStripStyle}
{...item}
disableRipple
disableTouchRipple
focusRipple={true}
/>
);
}
return null;
})}
</TabList>
</Box>
<Box className={classes.tabContentContainer}>
{!isRouteTabs
? contentList.map((item, index) => {
return (
<TabPanel
classes={{ ...classes.tabPanel }}
key={`v-tab-p-${index}`}
value={`${index}`}
>
{item ? item : null}
</TabPanel>
);
})
: null}
{isRouteTabs ? (
<div className={classes.tabPanel}>{routes}</div>
) : null}
</Box>
</Box>
</TabContext>
);
}
Example #11
Source File: index.tsx From yearn-watch-legacy with GNU Affero General Public License v3.0 | 5 votes |
headCells: HeadCell<StrategyTVLListItem>[] = [
{
numeric: false,
disablePadding: false,
align: 'center',
label: 'Strategy Name',
format: (item: GenericListItem) => {
const theme = useTheme();
return (
<Link
style={{
color:
theme.palette.mode === 'light' ? 'blue' : '#ce93d8',
}}
to={`/network/${item.network}/vault/${item.vault}/strategy/${item.strategy}`}
target="_blank"
>
{`${item.name} (${extractAddress(
item.strategy as string
)})`}
</Link>
);
},
},
{
numeric: true,
disablePadding: false,
align: 'center',
label: 'Activation',
format: (item: GenericListItem) => {
return <div>{new Date(item.activation).toUTCString()}</div>;
},
},
{
id: 'estimatedTotalAssetsUsdcNumber',
numeric: true,
disablePadding: false,
align: 'center',
label: 'TVL (MM)',
},
{
id: 'totalTvlPercentage',
numeric: true,
disablePadding: false,
align: 'center',
label: 'TVL %',
},
{
id: 'debtOutstandingUsdcNumber',
numeric: true,
disablePadding: false,
align: 'center',
label: 'Debt Outstanding (MM)',
getStyle: (item: StrategyTVLListItem) => {
const hasHighDebtToAssetsRatio =
item.debtOutstandingUsdcNumber >
HIGH_DEBT_TO_ASSETS_RATIO * item.estimatedTotalAssetsUsdcNumber;
return hasHighDebtToAssetsRatio
? {
backgroundColor: '#f5f514',
}
: {};
},
},
{
id: 'tvlImpact',
numeric: true,
disablePadding: false,
align: 'center',
label: 'TVL Impact (5-1 Extreme-Low)',
},
]
Example #12
Source File: Dashboard.tsx From NekoMaid with MIT License | 5 votes |
WorldMap: React.FC<{ players: Player[] }> = React.memo(({ players }) => {
const theme = useTheme()
const his = useHistory()
const globalData = useGlobalData()
const [, update] = useState(0)
if (!mapAdded) {
const node = document.createElement('script')
node.type = 'text/javascript'
node.src = 'http://api.map.baidu.com/getscript?v=3.0&ak=' + (globalData.bMapKey || '8G2uX6PFlYK3XCdcWYxH5sPVEA9K88QT')
node.onload = () => {
mapLoaded = true
update(id => id + 1)
}
document.body.appendChild(node)
mapAdded = true
}
return mapLoaded
? <ReactECharts
style={{ height: 750, maxHeight: '100vh' }}
onEvents={{
click ({ data: { name } }: { data: { name: string } }) {
if (players.some(it => it.name === name)) his.push('/NekoMaid/playerList/' + name)
}
}}
option={{
backgroundColor: 'rgba(0, 0, 0, 0)',
tooltip: { trigger: 'item' },
bmap: {
center: [104.114129, 37.550339],
zoom: 3,
roam: true,
mapStyle: theme.palette.mode === 'dark' ? { styleJson } : undefined
},
series: [{
type: 'effectScatter',
coordinateSystem: 'bmap',
data: players.filter(it => it.loc).map(it => ({ name: it.name, value: [it.loc![0], it.loc![1]], ip: it.ip })),
label: {
formatter: '{b}',
position: 'right',
show: true
},
tooltip: {
trigger: 'item',
formatter: ({ data }: any) => 'IP: ' + data.ip
},
encode: { value: 2 },
showEffectOn: 'emphasis',
rippleEffect: { brushType: 'stroke' },
symbolSize: 10,
itemStyle: {
color: theme.palette.primary.main,
shadowBlur: 4
},
hoverAnimation: true
}]
}}
/>
: <></>
})
Example #13
Source File: App.tsx From sapio-studio with Mozilla Public License 2.0 | 5 votes |
function ContractViewer(props: {
model: DiagramModel;
engine: DiagramEngine;
current_contract: ContractModel;
}) {
const dispatch = useDispatch();
const entity_id: EntityType = useSelector(selectEntityToView);
const show = useSelector(selectShouldViewEntity);
const theme = useTheme();
const timing_simulator_enabled = useSelector(selectSimIsShowing);
const details = entity_id[0] !== 'NULL' && show;
const { model, engine, current_contract } = props;
React.useEffect(() => {
if (entity_id[0] === 'TXN')
jump_to_entity(
model,
engine,
TXIDAndWTXIDMap.get_by_txid_s(
current_contract.txid_map,
entity_id[1]
) ?? null
);
else if (entity_id[0] === 'UTXO')
jump_to_entity(
model,
engine,
TXIDAndWTXIDMap.get_by_txid_s(
current_contract.txid_map,
entity_id[1].hash
)?.utxo_models[entity_id[1].nIn] ?? null
);
}, [entity_id]);
return (
<>
<div className="main-container">
<DemoCanvasWidget
engine={engine}
model={model}
background={theme.palette.background.paper}
color={theme.palette.divider}
>
<CanvasWidget engine={engine as any} key={'main'} />
</DemoCanvasWidget>
</div>
<Collapse in={details}>
<CurrentlyViewedEntity current_contract={current_contract} />
</Collapse>
<div className="area-overlays">
<Collapse in={timing_simulator_enabled}>
<SimulationController
contract={current_contract}
engine={engine}
hide={() => dispatch(toggle_showing())}
/>
</Collapse>
</div>
</>
);
}
Example #14
Source File: EditorJSWidget.tsx From ui-schema with MIT License | 5 votes |
EditorJSWidget = (
{
schema, storeKeys,
showValidity, valid, errors,
required, tools, hideTitle,
}: WidgetProps & RichContentProps
): React.ReactElement => {
const uid = useUID()
const [focused, setFocused] = React.useState(false)
const [ready, setReady] = React.useState(false)
const [empty, setEmpty] = React.useState(true)
const dense = schema.getIn(['view', 'dense']) as boolean
const theme = useTheme()
const styles = useEditorStyles(theme, {dense})
return <FormControl sx={styles.wrapper}>
{!hideTitle && !schema.getIn(['view', 'hideTitle']) ?
<InputLabel
focused={focused} shrink={focused || !empty}
margin={dense ? 'dense' : undefined}
error={!valid}
>
<TransTitle schema={schema} storeKeys={storeKeys}/>
</InputLabel> : null}
<Box
sx={styles.editor}
className={clsx(
inputClasses.underline,
focused ? inputClasses.focused : null
)}
>
<EditorJS
uid={uid}
ready={ready}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
onReady={() => setReady(true)}
onEmptyChange={(e) => setEmpty(e)}
storeKeys={storeKeys}
tools={tools}
required={Boolean(schema.get('deleteOnEmpty') || required)}
/>
</Box>
<ValidityHelperText
/* only pass down errors which are not for a specific sub-schema */
errors={errors}
showValidity={showValidity}
schema={schema}
/>
</FormControl>
}
Example #15
Source File: CategorySelect.tsx From mojito_pdm with Creative Commons Attribution Share Alike 4.0 International | 5 votes |
CategorySelect: React.FC = () => {
const theme = useTheme()
const [category, setCategory] = useRecoilState(CarState.categorySearch)
const handleChange = (event: SelectChangeEvent) => {
setCategory(event.target.value)
};
return (
<>
<div>
<FormControl variant="outlined" sx={{margin: theme.spacing(1), minWidth: 240}} color="error">
<InputLabel sx={{color: "white"}}>Category</InputLabel>
<Select sx={{color: "white"}}
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
value={category}
onChange={handleChange}
label="Category"
>
<MenuItem value="">
<em>All</em>
</MenuItem>
<MenuItem value={"Sports"}>Sports</MenuItem>
<MenuItem value={"Compacts"}>Compacts</MenuItem>
<MenuItem value={"Muscle"}>Muscle</MenuItem>
<MenuItem value={"Sedan"}>Sedan</MenuItem>
<MenuItem value={"Coupe"}>Coupé</MenuItem>
<MenuItem value={"Super"}>Super</MenuItem>
<MenuItem value={"SUV"}>SUV</MenuItem>
<MenuItem value={"Vans"}>Vans</MenuItem>
<MenuItem value={"Offroad"}>Offroad</MenuItem>
<MenuItem value={"Sports Classics"}>Sports Classics</MenuItem>
<MenuItem value={"Motorcycles"}>Motorcycles</MenuItem>
</Select>
</FormControl>
</div>
</>
)
}
Example #16
Source File: SoftDivider.tsx From airmessage-web with Apache License 2.0 | 5 votes |
export default function SoftDivider(props: {vertical?: boolean}) {
const color = useTheme().palette.divider;
return (
<div className={props.vertical ? styles.vertical : styles.horizontal} style={{backgroundColor: color}} />
);
}
Example #17
Source File: VehCard.tsx From mojito_pdm with Creative Commons Attribution Share Alike 4.0 International | 5 votes |
VehCard: React.FC<Car> = ({
name,
brand,
description,
brandLogo,
image,
price,
category,
spawncode,
trunkspace,
performance
}) => {
const theme = useTheme();
const [open, setOpen] = useState(false)
const testDrive = async () => {
await fetchNui("test_drive", {vehicle: spawncode})
}
// Functions
const handleOpen = () => setOpen(true)
const handleClose = () => setOpen(false)
return (
<Card sx={{
boxShadow: theme.shadows[3],
margin: theme.spacing(2)
}} variant="outlined">
<CardHeader
avatar={<img height={40} style={{maxWidth: 100, maxHeight: 40}} src={brandLogo} alt={brand}/>}
title={name}
subheader={category}
/>
<CardMedia style={{height: "150px"}} image={image}/>
<CardActions>
<Tooltip TransitionComponent={Zoom} sx={{maxWidth: 120}} arrow title="Test drive this vehicle">
<Button
size="small"
variant={theme.palette.mode === "dark" ? "contained" : "outlined"}
color="primary"
startIcon={<DirectionsCarIcon/>}
onClick={testDrive}
disableElevation
>
TEST DRIVE
</Button>
</Tooltip>
<Tooltip TransitionComponent={Zoom} arrow sx={{maxWidth: 120}}
title="View more information about this vehicle">
<Button
size="small"
variant={theme.palette.mode === "dark" ? "contained" : "outlined"}
color="primary"
startIcon={<AssignmentIcon/>}
onClick={handleOpen}
disableElevation
>
MORE INFO
</Button>
</Tooltip>
<Modal
open={open}
onClose={handleClose}
>
{<ModalBody
name={name}
brand={brand}
description={description}
price={price}
trunkspace={trunkspace}
setOpen={setOpen}
performance={performance}
spawncode={spawncode}
/>}
</Modal>
</CardActions>
</Card>
)
}
Example #18
Source File: DefaultNavBar.tsx From Tachidesk-WebUI with Mozilla Public License 2.0 | 5 votes |
export default function DefaultNavBar() {
const { title, action, override } = useContext(NavBarContext);
const { darkTheme } = useContext(DarkTheme);
const theme = useTheme();
const history = useHistory();
const isMobileWidth = useMediaQuery(theme.breakpoints.down('sm'));
const isMainRoute = navbarItems.some(({ path }) => path === history.location.pathname);
// Allow default navbar to be overrided
if (override.status) return override.value;
let navbar = <></>;
if (isMobileWidth) {
if (isMainRoute) {
navbar = <MobileBottomBar navBarItems={navbarItems.filter((it) => it.show !== 'desktop')} />;
}
} else {
navbar = <DesktopSideBar navBarItems={navbarItems.filter((it) => it.show !== 'mobile')} />;
}
return (
<Box sx={{ flexGrow: 1 }}>
<AppBar position="fixed" color={darkTheme ? 'default' : 'primary'}>
<Toolbar>
{
!navbarItems.some(({ path }) => path === history.location.pathname)
&& (
<IconButton
edge="start"
sx={{ marginRight: theme.spacing(2) }}
color="inherit"
aria-label="menu"
disableRipple
// when page is opened in new tab backbutton will
// take you to the library
onClick={() => (history.length === 1 ? history.push('/library') : history.goBack())}
size="large"
>
<ArrowBack />
</IconButton>
)
}
<Typography variant={isMobileWidth ? 'h6' : 'h5'} sx={{ flexGrow: 1 }}>
{title}
</Typography>
{action}
</Toolbar>
</AppBar>
{navbar}
</Box>
);
}
Example #19
Source File: MessageInput.tsx From airmessage-web with Apache License 2.0 | 5 votes |
export default function MessageInput(props: Props) {
const theme = useTheme();
const colorBG = theme.palette.messageIncoming.main;
const {
onMessageChange: propsOnMessageChange,
onMessageSubmit: propsOnMessageSubmit,
message: propsMessage,
attachments: propsAttachments,
onAttachmentAdd: propsOnAttachmentAdd
} = props;
const handleChange = useCallback((event: ChangeEvent<HTMLTextAreaElement>) => {
propsOnMessageChange(event.target.value);
}, [propsOnMessageChange]);
const submitInput = useCallback(() => {
propsOnMessageSubmit(propsMessage, propsAttachments);
}, [propsOnMessageSubmit, propsMessage, propsAttachments]);
const handleKeyPress = useCallback((event: React.KeyboardEvent<HTMLElement>) => {
if(!event.shiftKey && event.key === "Enter") {
event.preventDefault();
submitInput();
}
}, [submitInput]);
const handlePaste = useCallback((event: React.ClipboardEvent<HTMLElement>) => {
propsOnAttachmentAdd(Array.from(event.clipboardData.files));
}, [propsOnAttachmentAdd]);
return (
<div className={styles.root} style={{backgroundColor: colorBG}}>
<Flipper flipKey={props.attachments.map(attachment => attachment.id).join(" ")}>
{props.attachments.length > 0 &&
<div className={styles.attachmentqueue}>
{props.attachments.map((file) => {
const queueData: QueuedAttachmentProps = {
file: file.file,
onRemove: () => props.onAttachmentRemove(file)
};
let component: React.ReactNode;
if(file.file.type.startsWith("image/")) component = <QueuedAttachmentImage queueData={queueData} />;
else component = <QueuedAttachmentGeneric queueData={queueData} />;
return (<Flipped flipId={"attachmentqueue-" + file.id} key={file.id} onAppear={onAttachmentAppear} onExit={onAttachmentExit}>
{component}
</Flipped>);
})}
</div>
}
<div className={styles.control}>
<InputBase className={styles.textfield} maxRows="5" multiline fullWidth autoFocus placeholder={props.placeholder} value={props.message} onChange={handleChange} onKeyPress={handleKeyPress} onPaste={handlePaste} />
<IconButton size="small" color="primary" disabled={props.message.trim() === "" && props.attachments.length === 0} onClick={submitInput}><PushIcon /></IconButton>
</div>
</Flipper>
</div>
);
}
Example #20
Source File: MobileBottomBar.tsx From Tachidesk-WebUI with Mozilla Public License 2.0 | 5 votes |
export default function MobileBottomBar({ navBarItems }: IProps) {
const location = useLocation();
const theme = useTheme();
const iconFor = (path: string, IconComponent: any, SelectedIconComponent: any) => {
if (location.pathname === path) return <SelectedIconComponent sx={{ color: 'primary.main' }} fontSize="medium" />;
return <IconComponent sx={{ color: (theme.palette.mode === 'dark') ? 'grey.A400' : 'grey.600' }} fontSize="medium" />;
};
return (
<BottomNavContainer>
{
navBarItems.map((
{
path, title, IconComponent, SelectedIconComponent,
}: NavbarItem,
) => (
<Link to={path} key={path}>
<ListItem disableRipple button sx={{ justifyContent: 'center', padding: '8px' }} key={title}>
<Box
display="flex"
flexDirection="column"
alignItems="center"
>
{iconFor(path, IconComponent, SelectedIconComponent)}
<Box sx={{
fontSize: '0.65rem',
// eslint-disable-next-line no-nested-ternary
color: location.pathname === path
? 'primary.main'
: ((theme.palette.mode === 'dark')
? 'grey.A400'
: 'grey.600'),
}}
>
{title}
</Box>
</Box>
</ListItem>
</Link>
))
}
</BottomNavContainer>
);
}
Example #21
Source File: Files.tsx From NekoMaid with MIT License | 4 votes |
Editor: React.FC<{ plugin: Plugin, editorRef: React.Ref<UnControlled>, loading: { '!#LOADING'?: boolean },
dirs: Record<string, boolean>, refresh: () => void }> = React.memo(({ plugin, editorRef, loading, dirs, refresh }) => {
const doc = (editorRef as any).current?.editor?.doc
const theme = useTheme()
const his = useHistory()
const lnText = useRef('')
const path = useLocation().pathname.replace(/^\/NekoMaid\/files\/?/, '')
const [text, setText] = useState<string | null>(null)
const [error, setError] = useState('')
const [saving, setSaving] = useState(false)
const [notUndo, setNotUndo] = useState(true)
const [notRedo, setNotRedo] = useState(true)
const [notSave, setNotSave] = useState(true)
const [isNewFile, setIsNewFile] = useState(false)
useEffect(() => {
setText(null)
setError('')
setIsNewFile(false)
lnText.current = ''
if (!path || dirs[path] || path.endsWith('/')) return
loading['!#LOADING'] = true
plugin.emit('files:content', (data: number | string | null) => {
loading['!#LOADING'] = false
switch (data) {
case null: return setError(lang.files.unsupportedFormat)
case 0: return setError(lang.files.notExists)
case 1:
setIsNewFile(true)
setText('')
setNotSave(true)
break
case 2: return his.replace('./')
case 3: return setError(lang.files.tooBig)
default:
if (typeof data !== 'string') return
setText(data)
lnText.current = data.replace(/\r/g, '')
setNotSave(true)
}
}, path)
}, [path])
return <Card sx={{
position: 'relative',
'& .CodeMirror-dialog, .CodeMirror-scrollbar-filler': { backgroundColor: theme.palette.background.paper + '!important' }
}}>
<CardHeader
title={<span style={{ fontWeight: 'normal' }}>
{lang.files.editor}{path && ': ' + path}{path && isNewFile && <span className='bold'> ({lang.files.newFile})</span>}</span>}
sx={{ position: 'relative' }}
action={!error && path && text != null
? <Box sx={cardActionStyles}>
<Tooltip title={lang.files.undo}>
<span><IconButton size='small' disabled={notUndo} onClick={() => doc?.undo()}><Undo /></IconButton></span>
</Tooltip>
<Tooltip title={lang.files.undo}>
<span><IconButton size='small' disabled={notRedo} onClick={() => doc?.redo()}><Redo /></IconButton></span>
</Tooltip>
<Tooltip title={lang.files.save}>{saving
? <CircularProgress size={24} sx={{ margin: '5px' }} />
: <span><IconButton
size='small'
disabled={notSave}
onClick={() => {
if (!doc) return
setSaving(true)
const data = doc.getValue(text?.includes('\r\n') ? '\r\n' : '\n')
plugin.emit('files:update', (res: boolean) => {
setSaving(false)
if (!res) failed()
lnText.current = data.replace(/\r/g, '')
setText(data)
setNotSave(true)
if (isNewFile) {
setIsNewFile(false)
success()
refresh()
}
}, path, data)
}}
><Save /></IconButton></span>}</Tooltip>
</Box>
: undefined}
/>
<Divider />
{(error || !path) && <CardContent><Empty title={error || lang.files.notSelected} /></CardContent>}
<div style={{ position: text == null ? 'absolute' : undefined }}>
<UnControlled
ref={editorRef}
value={text == null ? EMPTY : text}
options={{
phrases: lang.codeMirrorPhrases,
mode: text == null ? '' : getMode(path),
theme: theme.palette.mode === 'dark' ? 'material' : 'one-light',
lineNumbers: true
}}
onChange={(_: any, { removed }: { removed: string[] }, newText: string) => {
setNotSave(lnText.current === newText)
if (removed?.[0] === EMPTY) doc?.clearHistory()
const histroy = doc?.historySize()
if (!histroy) return
setNotUndo(!histroy.undo)
setNotRedo(!histroy.redo)
}}
/>
</div>
</Card>
})
Example #22
Source File: DownloadQueue.tsx From Tachidesk-WebUI with Mozilla Public License 2.0 | 4 votes |
export default function DownloadQueue() {
const [, setWsClient] = useState<WebSocket>();
const [queueState, setQueueState] = useState<IQueue>(initialQueue);
const { queue, status } = queueState;
const history = useHistory();
const theme = useTheme();
const { setTitle, setAction } = useContext(NavbarContext);
const toggleQueueStatus = () => {
if (status === 'Stopped') {
client.get('/api/v1/downloads/start');
} else {
client.get('/api/v1/downloads/stop');
}
};
useEffect(() => {
setTitle('Download Queue');
setAction(() => {
if (status === 'Stopped') {
return (
<IconButton onClick={toggleQueueStatus} size="large">
<PlayArrowIcon />
</IconButton>
);
}
return (
<IconButton onClick={toggleQueueStatus} size="large">
<PauseIcon />
</IconButton>
);
});
}, [status]);
useEffect(() => {
const wsc = new WebSocket(`${baseWebsocketUrl}/api/v1/downloads`);
wsc.onmessage = (e) => {
setQueueState(JSON.parse(e.data));
};
setWsClient(wsc);
}, []);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const onDragEnd = (result: DropResult) => {
};
if (queue.length === 0) {
return <EmptyView message="No downloads" />;
}
return (
<>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided) => (
<List ref={provided.innerRef}>
{queue.map((item, index) => (
<Draggable
key={`${item.mangaId}-${item.chapterIndex}`}
draggableId={`${item.mangaId}-${item.chapterIndex}`}
index={index}
>
{(provided, snapshot) => (
<ListItem
ContainerProps={{ ref: provided.innerRef } as any}
sx={{
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'flex-start',
padding: 2,
margin: '10px',
'&:hover': {
backgroundColor: 'action.hover',
transition: 'background-color 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
},
'&:active': {
backgroundColor: 'action.selected',
transition: 'background-color 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
},
}}
onClick={() => history.push(`/manga/${item.chapter.mangaId}`)}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(
snapshot.isDragging,
provided.draggableProps.style,
theme.palette,
)}
ref={provided.innerRef}
>
<ListItemIcon sx={{ margin: 'auto 0' }}>
<DragHandleIcon />
</ListItemIcon>
<Box sx={{ display: 'flex' }}>
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Typography variant="h5" component="h2">
{item.manga.title}
</Typography>
<Typography variant="caption" display="block" gutterBottom>
{`${item.chapter.name} `
+ `(${(item.progress * 100).toFixed(2)}%)`
+ ` => state: ${item.state}`}
</Typography>
</Box>
</Box>
<IconButton
sx={{ marginLeft: 'auto' }}
onClick={(e) => {
// deleteCategory(index);
// prevent parent tags from getting the event
e.stopPropagation();
}}
size="large"
>
<DeleteIcon />
</IconButton>
</ListItem>
)}
</Draggable>
))}
{provided.placeholder}
</List>
)}
</Droppable>
</DragDropContext>
</>
);
}
Example #23
Source File: Profiler.tsx From NekoMaid with MIT License | 4 votes |
Timings: React.FC = React.memo(() => {
const plugin = usePlugin()
const theme = useTheme()
const { isTimingsV1 } = useGlobalData()
const [status, setStatus] = useState(false)
const [data, setData] = useState<TimingsData | null>(null)
useEffect(() => {
const off = plugin.emit('profiler:timingsStatus', setStatus).on('profiler:timings', setData)
return () => { off() }
}, [])
const [tree, entitiesTick, tilesTick] = useMemo(() => {
if (!data) return []
const entitiesTickMap: Record<string, { value: number, name: string, count: number }> = {}
const tilesTickMap: Record<string, { value: number, name: string, count: number }> = {}
const map: Record<number, [number, number, number, [number, number, number][] | undefined] | undefined> = { }
data.data.forEach(it => (map[it[0]] = it))
const createNode = (id: number, percent: number) => {
const cur = map[id]
if (!cur) return
map[id] = undefined
const [, count, time] = cur
const handler = data.handlers[id] || [0, lang.unknown]
const handlerName = data.groups[handler[0]] || lang.unknown
const name = handler[1]
const children = cur[cur.length - 1]
if (isTimingsV1) {
if (name.startsWith('tickEntity - ')) {
const came = name.slice(13).replace(/^Entity(Mob)?/, '')
const entity = decamelize(came)
const node = entitiesTickMap[entity]
if (node) {
node.count += count
node.value += time
} else entitiesTickMap[entity] = { count, value: time, name: minecraft['entity.minecraft.' + entity] || came }
} else if (name.startsWith('tickTileEntity - ')) {
const came = name.slice(17).replace(/^TileEntity(Mob)?/, '')
const entity = decamelize(came)
const node = tilesTickMap[entity]
if (node) {
node.count += count
node.value += time
} else tilesTickMap[entity] = { count, value: time, name: minecraft['block.minecraft.' + entity] || came }
}
} else {
if (name.startsWith('tickEntity - ') && name.endsWith('ick')) {
const res = ENTITY_TYPE.exec(name)
if (res) {
const node = entitiesTickMap[res[1]]
if (node) {
node.count += count
node.value += time
} else entitiesTickMap[res[1]] = { count, value: time, name: minecraft['entity.minecraft.' + res[1]] || res[1] }
}
} else if (name.startsWith('tickTileEntity - ')) {
const arr = name.split('.')
const came = arr[arr.length - 1].replace(/^TileEntity(Mob)?/, '')
const tile = decamelize(came)
const node = tilesTickMap[tile]
if (node) {
node.count += count
node.value += time
} else tilesTickMap[tile] = { count, value: time, name: minecraft['block.minecraft.' + tile] || came }
}
}
return <TreeItem
key={id}
nodeId={id.toString()}
label={<Box sx={{
'& .info, .count': { color: 'transparent' },
'&:hover .count': { color: 'inherit' },
'&:hover .info': {
color: theme.palette.primary.contrastText,
textShadow: theme.palette.mode === 'light'
? '#000 1px 0 0, #000 0 1px 0, #000 -1px 0 0, #000 0 -1px 0'
: '#fff 1px 0 0, #fff 0 1px 0, #fff -1px 0 0, #fff 0 -1px 0'
}
}}>
<Box sx={{
position: 'relative',
zIndex: 2,
display: 'flex',
alignItems: 'center'
}}>
{handlerName !== 'Minecraft' && <><Typography color='primary' component='span'>
{isTimingsV1 ? 'Bukkit' : lang.plugin + ':' + handlerName}</Typography>::</>}
{name}
<Typography variant='caption' className='count'>({lang.profiler.timingsCount}: {count})</Typography>
</Box>
<Box className='info' sx={{
position: 'absolute',
height: 10,
right: 0,
top: '50%',
marginTop: '-5px',
minWidth: 40,
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}}>
<Typography variant='caption' sx={{ position: 'absolute' }}>({Math.round(100 * percent)}%)</Typography>
<div style={{ width: 100 * percent + 'px' }} className='bar' />
</Box>
</Box>}
>{Array.isArray(children) && children.sort((a, b) => b[2] - a[2]).map(it => createNode(it[0], percent * (it[2] / time)))}</TreeItem>
}
// eslint-disable-next-line react/jsx-key
return [<TreeView defaultCollapseIcon={<ExpandMore />} defaultExpandIcon={<ChevronRight />} defaultExpanded={['1']}>
{createNode(1, 1)}
</TreeView>, Object.values(entitiesTickMap), Object.values(tilesTickMap)]
}, [data])
return <Container maxWidth={false} sx={{ py: 3 }}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Card>
<CardHeader title='Timings' sx={{ position: 'relative' }} action={<FormControlLabel
control={<Switch checked={status} onChange={e => plugin.emit('profiler:timingsStatus', setStatus, e.target.checked)} />}
label={minecraft['addServer.resourcePack.enabled']}
sx={cardActionStyles}
/>} />
<Divider />
{status
? <Box sx={{
position: 'relative',
minHeight: data ? undefined : 300,
'& .bar': { backgroundColor: theme.palette.primary.main, height: 10, marginLeft: 'auto', borderRadius: 2 }
}}>
<CircularLoading loading={!data} />
{tree}
</Box>
: <CardContent><Empty title={lang.profiler.timingsNotStarted} /></CardContent>}
</Card>
</Grid>
{data && <Pie title={lang.profiler.entitiesTick} data={entitiesTick!} formatter={countFormatter} />}
{data && <Pie title={lang.profiler.tilesTick} data={tilesTick!} formatter={countFormatter} />}
</Grid>
</Container>
})
Example #24
Source File: BidOverWriteComponent.tsx From professor-prebid with Apache License 2.0 | 4 votes |
BidOverWriteComponent = ({ prebid, debugConfigState, setDebugConfigState }: BidOverWriteComponentProps): JSX.Element => {
const theme = useTheme();
const [detectedBidderNames, setDetectedBidderNames] = useState<string[]>([]);
const [detectedAdUnitCodes, setDetectedAdUnitCodes] = useState<string[]>([]);
const [bidsOverwriteEnabled, setBidOverwriteEnabled] = useState<boolean>(false);
const [cpm, setCpm] = React.useState(20.0);
const [selectedBidders, setSelectedBidders] = React.useState<string[]>(debugConfigState?.bidders || []);
const [selectedAdUnitCodes, setSelectedAdUnitCodes] = React.useState<string[]>(debugConfigState?.bids?.map((bid) => bid.adUnitCode) || []);
const handleAdunitSelectionChange = (event: SelectChangeEvent<string[]>) => {
const selectedAdUnitCodesArray = typeof event.target.value === 'string' ? event.target.value.split(',') : event.target.value;
updateDebugConfig(selectedAdUnitCodesArray, selectedBidders, cpm);
};
const handleBidderSelectionChange = (event: SelectChangeEvent<string[]>) => {
const selectedBiddersArray = typeof event.target.value === 'string' ? event.target.value.split(',') : event.target.value;
updateDebugConfig(selectedAdUnitCodes, selectedBiddersArray, cpm);
};
const handleBidOverWriteEnabledChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setBidOverwriteEnabled(event.target.checked);
if (!event.target.checked) {
setDebugConfigState({ ...debugConfigState, bids: undefined });
}
};
const handleCpmChange = (event: React.ChangeEvent<HTMLInputElement>) => {
updateDebugConfig(selectedAdUnitCodes, selectedBidders, Number(event.target.value));
};
const updateDebugConfig = (selectedAdUnitCodes: string[], selectedBidders: string[], cpm: number): void => {
const bids: IPrebidDebugConfigBid[] = [];
if (selectedAdUnitCodes.length === 0) {
selectedBidders.forEach((bidder: string) => {
bids.push({ bidder, cpm });
});
} else {
selectedAdUnitCodes.forEach((adUnitCode: string) => {
selectedBidders.forEach((bidder: string) => {
bids.push({ adUnitCode, bidder, cpm });
});
});
}
if (bids.length > 0) {
setDebugConfigState({ ...debugConfigState, bids });
} else {
setDebugConfigState({ ...debugConfigState, bids: undefined });
}
};
useEffect(() => {
if (!bidsOverwriteEnabled) {
setBidOverwriteEnabled(!!Array.isArray(debugConfigState?.bids));
}
setCpm(debugConfigState?.bids?.length > 0 ? Number(debugConfigState?.bids[0].cpm) : 20.0);
setSelectedBidders(debugConfigState?.bids?.map((item) => item.bidder).filter((v, i, a) => a.indexOf(v) === i) || []);
setSelectedAdUnitCodes(
debugConfigState?.bids
?.map((item) => item.adUnitCode)
// .filter((i) => i)
.filter((v, i, a) => v && a.indexOf(v) === i) || []
);
}, [bidsOverwriteEnabled, debugConfigState?.bids]);
useEffect(() => {
const events = prebid.events?.filter((event) => ['auctionInit', 'auctionEnd'].includes(event.eventType)) || [];
const bidderNamesSet = events.reduce((previousValue, currentValue) => {
const adUnitsArray = (currentValue as IPrebidAuctionEndEventData).args.adUnits || [];
adUnitsArray.forEach((adUnit) => adUnit.bids.forEach((bid) => previousValue.add(bid.bidder)));
return previousValue;
}, new Set<string>());
setDetectedBidderNames(Array.from(bidderNamesSet));
const adUnitCodesSet = events.reduce((previousValue, currentValue) => {
const adUnitsCodesArray = (currentValue as IPrebidAuctionEndEventData).args.adUnitCodes || [];
adUnitsCodesArray.forEach((adUnitCode) => previousValue.add(adUnitCode));
return previousValue;
}, new Set<string>());
setDetectedAdUnitCodes(Array.from(adUnitCodesSet));
}, [prebid.events]);
logger.log(`[PopUp][BidOverWriteComponent]: render `, detectedBidderNames, bidsOverwriteEnabled, cpm);
return (
<React.Fragment>
<Grid item md={1} xs={1}>
<Box sx={{ alignContent: 'center', [theme.breakpoints.down('sm')]: { transform: 'rotate(90deg)' } }}>
<FormControl>
<FormControlLabel label="" control={<Switch checked={bidsOverwriteEnabled} onChange={handleBidOverWriteEnabledChange} />} />
</FormControl>
</Box>
</Grid>
<Grid item md={2} xs={2}>
<FormControl sx={{ height: 1 }}>
<Box component="form" noValidate autoComplete="off" sx={{ height: 1 }}>
<TextField
sx={{ height: 1, '& div': { height: 1 } }}
type="number"
label="cpm"
value={cpm}
onChange={handleCpmChange}
variant="outlined"
disabled={!bidsOverwriteEnabled}
/>
</Box>
</FormControl>
</Grid>
<Grid item md={4.5} xs={4.5}>
<FormControl sx={{ height: 1, width: 1, '& .MuiOutlinedInput-root': { height: 1, alignItems: 'baseline' } }}>
<InputLabel>Select Bidder(s)</InputLabel>
<Select
multiple
name="bidders"
value={selectedBidders}
onChange={handleBidderSelectionChange}
input={<OutlinedInput label="Detected Bidders" />}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value, index) => (
<Chip key={index} label={value} />
))}
</Box>
)}
MenuProps={MenuProps}
disabled={!bidsOverwriteEnabled}
>
{detectedBidderNames.map((name, index) => (
<MenuItem key={index} value={name} style={getStyles(name, selectedBidders, theme)}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Grid item md={4.5} xs={4.5}>
<FormControl sx={{ height: 1, width: 1, '& .MuiOutlinedInput-root': { height: 1, alignItems: 'baseline' } }}>
<InputLabel>Select AdUnitCode(s)</InputLabel>
<Select
multiple
name="adUnitCodes"
value={selectedAdUnitCodes}
onChange={handleAdunitSelectionChange}
input={<OutlinedInput label="Detected AdUnit(s)" />}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value, index) => (
<Chip key={index} label={value} />
))}
</Box>
)}
MenuProps={MenuProps}
disabled={!bidsOverwriteEnabled || selectedBidders.length === 0}
>
{detectedAdUnitCodes.map((name, index) => (
<MenuItem key={index} value={name} style={getStyles(name, selectedAdUnitCodes, theme)}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
</React.Fragment>
);
}
Example #25
Source File: TablePaginationActions.tsx From ui-schema with MIT License | 4 votes |
TablePaginationActions: React.ComponentType<TablePaginationActionsProps> = (props) => {
const theme = useTheme()
const {
count, page, rowsPerPage, onPageChange,
backIconButtonProps: backIconButtonPropsTmp, nextIconButtonProps: nextIconButtonPropsTmp,
} = props
const btnSize = backIconButtonPropsTmp?.size
// @ts-ignore
const {noFirstPageButton, ...backIconButtonProps} = backIconButtonPropsTmp || {}
// @ts-ignore
const {noLastPageButton, ...nextIconButtonProps} = nextIconButtonPropsTmp || {}
const handleFirstPageButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onPageChange(event, 0)
}
const handleBackButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onPageChange(event, page - 1)
}
const handleNextButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onPageChange(event, page + 1)
}
const handleLastPageButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1))
}
const iconStyle = {
padding: btnSize === 'small' ? 2 : undefined,
}
return (
<div style={{flexShrink: 0, marginLeft: theme.spacing(2.5)}}>
{noFirstPageButton ? null :
<IconButton
onClick={handleFirstPageButtonClick}
disabled={page === 0}
size={btnSize}
>
<AccessTooltipIcon title={<Trans text={'pagination.first-page'}/>}>
{theme.direction === 'rtl' ? <LastPageIcon style={iconStyle}/> : <FirstPageIcon style={iconStyle}/>}
</AccessTooltipIcon>
</IconButton>}
<IconButton
onClick={handleBackButtonClick} disabled={page === 0}
size={btnSize}
{...backIconButtonProps}
>
<AccessTooltipIcon title={<Trans text={'pagination.prev-page'}/>}>
{theme.direction === 'rtl' ? <KeyboardArrowRight style={iconStyle}/> : <KeyboardArrowLeft style={iconStyle}/>}
</AccessTooltipIcon>
</IconButton>
<IconButton
onClick={handleNextButtonClick}
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
style={{
padding: btnSize === 'small' ? 2 : undefined,
}}
size={btnSize}
{...nextIconButtonProps}
>
<AccessTooltipIcon title={<Trans text={'pagination.next-page'}/>}>
{theme.direction === 'rtl' ? <KeyboardArrowLeft style={iconStyle}/> : <KeyboardArrowRight style={iconStyle}/>}
</AccessTooltipIcon>
</IconButton>
{noLastPageButton ? null :
<IconButton
onClick={handleLastPageButtonClick}
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
size={btnSize}
style={{
padding: btnSize === 'small' ? 2 : undefined,
}}
>
<AccessTooltipIcon title={<Trans text={'pagination.last-page'}/>}>
{theme.direction === 'rtl' ? <FirstPageIcon style={iconStyle}/> : <LastPageIcon style={iconStyle}/>}
</AccessTooltipIcon>
</IconButton>}
</div>
)
}
Example #26
Source File: ChapterCard.tsx From Tachidesk-WebUI with Mozilla Public License 2.0 | 4 votes |
export default function ChapterCard(props: IProps) {
const theme = useTheme();
const {
chapter, triggerChaptersUpdate, downloadStatusString, showChapterNumber,
} = props;
const dateStr = chapter.uploadDate && new Date(chapter.uploadDate).toLocaleDateString();
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
// prevent parent tags from getting the event
event.stopPropagation();
event.preventDefault();
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const sendChange = (key: string, value: any) => {
handleClose();
const formData = new FormData();
formData.append(key, value);
if (key === 'read') { formData.append('lastPageRead', '1'); }
client.patch(`/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}`, formData)
.then(() => triggerChaptersUpdate());
};
const downloadChapter = () => {
client.get(`/api/v1/download/${chapter.mangaId}/chapter/${chapter.index}`);
handleClose();
};
const deleteChapter = () => {
client.delete(`/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}`)
.then(() => triggerChaptersUpdate());
handleClose();
};
const readChapterColor = theme.palette.mode === 'dark' ? '#acacac' : '#b0b0b0';
return (
<>
<li>
<Card
sx={{
margin: '10px',
':hover': {
backgroundColor: 'action.hover',
transition: 'background-color 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
cursor: 'pointer',
},
':active': {
backgroundColor: 'action.selected',
transition: 'background-color 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
},
}}
>
<Link
to={`/manga/${chapter.mangaId}/chapter/${chapter.index}`}
style={{
textDecoration: 'none',
color: chapter.read ? readChapterColor : theme.palette.text.primary,
}}
>
<CardContent
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: 2,
}}
>
<Box sx={{ display: 'flex' }}>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<Typography variant="h5" component="h2">
<span style={{ color: theme.palette.primary.dark }}>
{chapter.bookmarked && <BookmarkIcon />}
</span>
{ showChapterNumber ? `Chapter ${chapter.chapterNumber}` : chapter.name}
</Typography>
<Typography variant="caption" display="block" gutterBottom>
{chapter.scanlator}
{chapter.scanlator && ' '}
{dateStr}
{downloadStatusString}
</Typography>
</div>
</Box>
<IconButton aria-label="more" onClick={handleClick} size="large">
<MoreVertIcon />
</IconButton>
</CardContent>
</Link>
<Menu
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
{downloadStatusString.endsWith('Downloaded')
&& <MenuItem onClick={deleteChapter}>Delete</MenuItem>}
{downloadStatusString.length === 0
&& <MenuItem onClick={downloadChapter}>Download</MenuItem> }
<MenuItem onClick={() => sendChange('bookmarked', !chapter.bookmarked)}>
{chapter.bookmarked && 'Remove bookmark'}
{!chapter.bookmarked && 'Bookmark'}
</MenuItem>
<MenuItem onClick={() => sendChange('read', !chapter.read)}>
{`Mark as ${chapter.read ? 'unread' : 'read'}`}
</MenuItem>
<MenuItem onClick={() => sendChange('markPrevRead', true)}>
Mark previous as Read
</MenuItem>
</Menu>
</Card>
</li>
</>
);
}
Example #27
Source File: AircraftInfoOverlay.tsx From react-flight-tracker with MIT License | 4 votes |
AircraftInfoOverlay: React.FC<Props> = (props) => {
// External hooks
const theme = useTheme();
// States
const [lastPositionPastSeconds, setLastPositionPastSeconds] = useState(0);
// Refs
const updateIntverlIDRef = useRef(0);
const lastPositionPastSecondsRef = useRef(lastPositionPastSeconds);
lastPositionPastSecondsRef.current = lastPositionPastSeconds;
// Effects
useEffect(() => {
// Mount
// Unmount
return () => {
clearInterval(updateIntverlIDRef.current);
}
}, []);
useEffect(() => {
if (!props.selectedAircraft || !props.selectedAircraft.stateVector) {
clearInterval(updateIntverlIDRef.current);
return;
}
clearInterval(updateIntverlIDRef.current);
const lastPositionSeconds = props.selectedAircraft.stateVector.time_position ? props.selectedAircraft.stateVector.time_position : Math.floor(Date.now() / 1000);
setLastPositionPastSeconds(Math.floor(Date.now() / 1000) - lastPositionSeconds);
updateIntverlIDRef.current = window.setInterval(handleUpdate, 1000);
}, [props.selectedAircraft?.stateVector]);
const handleUpdate = () => {
setLastPositionPastSeconds(lastPositionPastSecondsRef.current + 1);
};
const renderHeader = () => {
if (!props.selectedAircraft)
return undefined;
if (!props.selectedAircraft.stateVector)
return undefined;
const stateVector = props.selectedAircraft.stateVector;
// Get altitude
var altitude = stateVector.geo_altitude;
if ((altitude === null) || (altitude < 0))
altitude = stateVector.baro_altitude;
if ((altitude === null) || (altitude < 0))
altitude = 0;
// Get vertical rate
const verticalRate = stateVector.vertical_rate ? stateVector.vertical_rate : 0;
// Get true track
const trueTrack = stateVector.true_track ? stateVector.true_track : 0.0;
const FlightIcon = getIcon(stateVector.on_ground, verticalRate, altitude);
return (
<React.Fragment>
<Box
sx={{
width: '100%',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
alignContent: 'center'
}}>
<Box
sx={{
backgroundColor: theme.palette.primary.main,
borderRadius: '50%',
width: 48,
height: 48,
display: 'flex',
alignItems: 'center',
alignContent: 'center',
justifyItems: 'center',
justifyContent: 'center',
textAlign: 'center'
}}>
<FlightIcon
css={(theme) => ({
fill: theme.palette.primary.contrastText,
width: 32,
height: 32,
transform: `rotate(${getRotation(trueTrack, verticalRate, altitude!)}deg)`
})} />
</Box>
<Box
sx={{
flex: 'auto',
marginLeft: 1,
marginRight: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
alignContent: 'flex-start'
}}>
<Typography
variant='h6'>
{stateVector.callsign ? stateVector.callsign : '?'}
</Typography>
<Typography
variant='body1'>
{stateVector.origin_country}
</Typography>
</Box>
<IconButton
aria-label="close"
onClick={() => {
if (!props.selectedAircraft)
return;
if (!props.selectedAircraft.stateVector)
return undefined;
if (props.onRelease)
props.onRelease(props.selectedAircraft.stateVector.icao24)
}}>
<CloseIcon color='error' />
</IconButton>
</Box>
<Box
sx={{
width: '100%',
height: 2,
marginTop: 1,
marginBottom: 2,
backgroundColor: (theme) => theme.palette.primary.main
}} />
</React.Fragment>
);
};
const renderFlightData = () => {
if (!props.selectedAircraft)
return undefined;
if (!props.selectedAircraft.stateVector)
return undefined;
const stateVector = props.selectedAircraft.stateVector;
var options: Intl.DateTimeFormatOptions = {
year: 'numeric', month: 'numeric', day: 'numeric',
hour: 'numeric', minute: 'numeric', second: 'numeric'
};
var lastPositionTime = '?';
if (stateVector.time_position !== null) {
var date = new Date(stateVector.time_position * 1000);
lastPositionTime = new Intl.DateTimeFormat('de-CH', options).format(date)
}
var lastContactTime = '?';
if (stateVector.last_contact !== null) {
var lastContactDate = new Date(stateVector.last_contact * 1000);
lastContactTime = new Intl.DateTimeFormat('de-CH', options).format(lastContactDate)
}
// Get altitude
const barometricAltitude = stateVector.baro_altitude ? stateVector.baro_altitude : 0;
const geometricAltitude = stateVector.geo_altitude ? stateVector.geo_altitude : 0;
var altitude = stateVector.geo_altitude;
if ((altitude === null) || (altitude < 0))
altitude = stateVector.baro_altitude;
if ((altitude === null) || (altitude < 0))
altitude = 0;
// Get velocity
const velocity = stateVector.velocity ? stateVector.velocity : -1;
// Get vertical rate
const verticalRate = stateVector.vertical_rate ? stateVector.vertical_rate : 0.0;
// Get true track
const trueTrack = stateVector.true_track ? stateVector.true_track : 0.0;
var textContainerStyle: SxProps<Theme> = {
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
alignContent: 'flex-start'
};
var spaceStyle: SxProps<Theme> = {
height: 8,
minHeight: 8
};
return (
<React.Fragment>
<Box
sx={textContainerStyle}>
<Typography
variant='body2'>
{'Last contact'}
</Typography>
<Typography
variant='body1'>
{`${lastContactTime} [${lastPositionPastSeconds.toString()}s]`}
</Typography>
</Box>
<Box sx={spaceStyle} />
<Box
sx={textContainerStyle}>
<Typography
variant='body2'>
{'Last position update'}
</Typography>
<Typography
variant='body1'>
{`${lastPositionTime} [${lastPositionPastSeconds.toString()}s]`}
</Typography>
</Box>
<Box sx={spaceStyle} />
<Box
sx={textContainerStyle}>
<Typography
variant='body2'>
{'Barometric altitude'}
</Typography>
<Typography
variant='body1'>
{`${getFormattedValue(barometricAltitude, 1)} m [${getFormattedValue(barometricAltitude * 3.28084, 1)} ft.]`}
</Typography>
</Box>
<Box sx={spaceStyle} />
<Box
sx={textContainerStyle}>
<Typography
variant='body2'>
{'Geometric altitude'}
</Typography>
<Typography
variant='body1'>
{`${getFormattedValue(geometricAltitude, 1)} m [${getFormattedValue(geometricAltitude * 3.28084, 1)} ft.]`}
</Typography>
</Box>
<Box sx={spaceStyle} />
<Box
sx={textContainerStyle}>
<Typography
variant='body2'>
{'Velocity'}
</Typography>
<Typography
variant='body1'>
{`${getFormattedValue(velocity * 3.6, 1)} km/h [${getFormattedValue(velocity, 1)} m/s]`}
</Typography>
</Box>
<Box sx={spaceStyle} />
<Box
sx={textContainerStyle}>
<Typography
variant='body2'>
{'Longitude / Latitude'}
</Typography>
<Typography
variant='body1'>
{`${getFormattedValue(stateVector.longitude ? stateVector.longitude : -1, 3)} ° / ${getFormattedValue(stateVector.latitude ? stateVector.latitude : -1, 3)} °`}
</Typography>
</Box>
<Box sx={spaceStyle} />
<Box
sx={textContainerStyle}>
<Typography
variant='body2'>
{'Rotation'}
</Typography>
<Typography
variant='body1'>
{`${getFormattedValue(trueTrack, 1)} °`}
</Typography>
</Box>
<Box sx={spaceStyle} />
<Box
sx={textContainerStyle}>
<Typography
variant='body2'>
{'Vertical rate'}
</Typography>
<Typography
variant='body1'>
{`${getFormattedValue(verticalRate, 1)} m/s`}
</Typography>
</Box>
<Box sx={spaceStyle} />
<Box
sx={textContainerStyle}>
<Typography
variant='body2'>
{'Status'}
</Typography>
<Typography
variant='body1'>
{getStatusText(stateVector.on_ground, verticalRate, altitude)}
</Typography>
</Box>
<Box sx={spaceStyle} />
<Box
sx={textContainerStyle}>
<Typography
variant='body2'>
{'ICAO24'}
</Typography>
<Typography
variant='body1'>
{stateVector.icao24}
</Typography>
</Box>
<Box sx={spaceStyle} />
<Box
sx={textContainerStyle}>
<Typography
variant='body2'>
{'Transpondercode [Squawk]'}
</Typography>
<Typography
variant='body1'>
{stateVector.squawk ? stateVector.squawk : -1}
</Typography>
</Box>
</React.Fragment>
);
};
if (!props.selectedAircraft)
return (
<Box
sx={{
position: 'relative',
minWidth: 268,
minHeight: 84,
height: '100%',
backgroundColor: theme.palette.background.paper,
borderRadius: 2,
boxShadow: 5,
opacity: 0.9,
display: 'flex',
flexDirection: 'column',
alignContent: 'center',
alignItems: 'center',
justifyContent: 'center',
justifyItems: 'center'
}}>
<Indicator1
color={theme.palette.primary.main} />
</Box>
);
return (
<Box
sx={{
position: 'relative',
minWidth: 268,
height: 'auto',
width: 'auto',
backgroundColor: theme.palette.background.paper,
borderRadius: 2,
boxShadow: 5,
opacity: 0.9,
padding: theme.spacing(1)
}}>
{renderHeader()}
{renderFlightData()}
</Box>
);
}
Example #28
Source File: ModalBody.tsx From mojito_pdm with Creative Commons Attribution Share Alike 4.0 International | 4 votes |
ModalBody: React.FC<Modal> = ({name, brand, description, price, trunkspace, setOpen, performance, spawncode}) => {
const [modalStyle] = useState(getModalStyle)
const theme = useTheme()
const [pDialogueOpen, setpDialogueOpen] = useState(false) // Purchase Dialogue
const [fDialogueOpen, setfDialogueOpen] = useState(false) // Finance Dialogue
const {visible} = useVisibility()
useEffect(() => {
if (visible) return
setOpen(false)
}, [visible, setOpen])
const handleClose = () => {
setOpen(false)
}
const handlepDialogueClose = () => {
setpDialogueOpen(false)
}
const handlefDialogueClose = () => {
setfDialogueOpen(false)
}
const buyEnabled = useRecoilValue(GlobalState.canBuy)
return (
<>
<div style={{
...modalStyle,
position: "absolute",
width: 600,
backgroundColor: theme.palette.background.paper,
boxShadow: theme.shadows[7],
padding: theme.spacing(2, 4, 3),
color: theme.palette.mode === "dark" ? "white" : "black"
}}>
<h2>{`${brand} ${name}`}</h2>
<h4>
Price: {price} <br/>
Trunk Space: {trunkspace}
</h4>
{performance &&
<Typography>
Power <LinearProgress value={performance.power} variant="determinate"/>
Acceleration <LinearProgress value={performance.acceleration} variant="determinate"/>
Handling <LinearProgress value={performance.handling} variant="determinate"/>
Top Speed <LinearProgress value={performance.topspeed} variant="determinate"/>
</Typography>
}
<p>
{description}
</p>
<Stack direction="row" spacing={2}>
<Button size="small" variant="outlined" color="error" onClick={handleClose}>Close</Button>
{ buyEnabled && <Button size="small" variant="outlined" color="primary" onClick={() => setpDialogueOpen(true)}> Buy </Button> }
{ buyEnabled && <Button size="small" variant="outlined" color="primary" onClick={() => setfDialogueOpen(true)}> Finance </Button> }
</Stack>
<Dialog
open={pDialogueOpen}
TransitionComponent={Transition}
keepMounted
onClose={handlepDialogueClose}
>
<PurchaseDialogueBody
spawncode={spawncode}
price={price}
setDialogueOpen={setpDialogueOpen}
setModalOpen={setOpen}
/>
</Dialog>
<Dialog
open={fDialogueOpen}
TransitionComponent={Transition}
keepMounted
onClose={handlefDialogueClose}
fullWidth
maxWidth={"xs"}
>
<FinanceDialogueBody
spawncode={spawncode}
price={price}
setDialogueOpen={setfDialogueOpen}
setModalOpen={setOpen}
/>
</Dialog>
</div>
</>
)
}
Example #29
Source File: LicensePlans.tsx From console with GNU Affero General Public License v3.0 | 4 votes |
LicensePlans = ({
licenseInfo,
setLicenseModal,
operatorMode,
}: IRegisterStatus) => {
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm"));
let currentPlan = !licenseInfo
? "community"
: licenseInfo?.plan?.toLowerCase();
const isCommunityPlan = currentPlan === LICENSE_PLANS.COMMUNITY;
const isStandardPlan = currentPlan === LICENSE_PLANS.STANDARD;
const isEnterprisePlan = currentPlan === LICENSE_PLANS.ENTERPRISE;
const isPaidPlan = PAID_PLANS.includes(currentPlan);
/*In smaller screen use tabbed view to show features*/
const [xsPlanView, setXsPlanView] = useState("");
let isXsViewCommunity = xsPlanView === LICENSE_PLANS.COMMUNITY;
let isXsViewStandard = xsPlanView === LICENSE_PLANS.STANDARD;
let isXsViewEnterprise = xsPlanView === LICENSE_PLANS.ENTERPRISE;
const getCommunityPlanHeader = () => {
return (
<PlanHeader
isActive={isCommunityPlan}
isXsViewActive={isXsViewCommunity}
title={"community"}
onClick={isSmallScreen ? onPlanClick : null}
>
<Box className="title-block">
<Box className="title-main">
<div className="iconContainer">
<ConsoleAgpl />
</div>
</Box>
</Box>
</PlanHeader>
);
};
const getStandardPlanHeader = () => {
return (
<PlanHeader
isActive={isStandardPlan}
isXsViewActive={isXsViewStandard}
title={"Standard"}
onClick={isSmallScreen ? onPlanClick : null}
>
<Box className="title-block">
<Box className="title-main">
<div className="iconContainer">
<ConsoleStandard />
</div>
</Box>
</Box>
</PlanHeader>
);
};
const getEnterpriseHeader = () => {
return (
<PlanHeader
isActive={isEnterprisePlan}
isXsViewActive={isXsViewEnterprise}
title={"Enterprise"}
onClick={isSmallScreen ? onPlanClick : null}
>
<Box className="title-block">
<Box className="title-main">
<div className="iconContainer">
<ConsoleEnterprise />
</div>
</Box>
</Box>
</PlanHeader>
);
};
const getButton = (
link: string,
btnText: string,
variant: any,
plan: string
) => {
let linkToNav =
currentPlan !== "community" ? "https://subnet.min.io" : link;
return (
<Button
variant={variant}
color="primary"
target="_blank"
rel="noopener noreferrer"
sx={{
marginTop: "12px",
width: "80%",
"&.MuiButton-contained": {
padding: 0,
paddingLeft: "8px",
paddingRight: "8px",
},
}}
href={linkToNav}
disabled={
currentPlan !== LICENSE_PLANS.COMMUNITY && currentPlan !== plan
}
onClick={(e) => {
e.preventDefault();
window.open(
`${linkToNav}?ref=${operatorMode ? "op" : "con"}`,
"_blank"
);
}}
>
{btnText}
</Button>
);
};
const onPlanClick = (plan: string) => {
setXsPlanView(plan);
};
useEffect(() => {
if (isSmallScreen) {
setXsPlanView(currentPlan || "community");
} else {
setXsPlanView("");
}
}, [isSmallScreen, currentPlan]);
const linkTracker = `?ref=${operatorMode ? "op" : "con"}`;
const featureList = FEATURE_ITEMS;
return (
<Fragment>
<Box
sx={{
border: "1px solid #eaeaea",
borderTop: "0px",
marginBottom: "45px",
"&::-webkit-scrollbar": {
width: "5px",
height: "5px",
},
"&::-webkit-scrollbar-track": {
background: "#F0F0F0",
borderRadius: 0,
boxShadow: "inset 0px 0px 0px 0px #F0F0F0",
},
"&::-webkit-scrollbar-thumb": {
background: "#777474",
borderRadius: 0,
},
"&::-webkit-scrollbar-thumb:hover": {
background: "#5A6375",
},
}}
>
<Box
className={"title-blue-bar"}
sx={{
height: "8px",
borderBottom: "8px solid rgb(6 48 83)",
}}
/>
<Box
className={isPaidPlan ? "paid-plans-only" : ""}
sx={{
display: "grid",
margin: "0 1.5rem 0 1.5rem",
gridTemplateColumns: {
sm: "1fr 1fr 1fr 1fr",
xs: "1fr 1fr 1fr",
},
"&.paid-plans-only": {
display: "grid",
gridTemplateColumns: "1fr 1fr 1fr",
},
"& .features-col": {
flex: 1,
minWidth: "260px",
"@media (max-width: 600px)": {
display: "none",
},
},
"& .xs-only": {
display: "none",
},
"& .button-box": {
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: "5px 0px 25px 0px",
borderLeft: "1px solid #eaeaea",
},
"& .plan-header": {
height: "99px",
borderBottom: "1px solid #eaeaea",
},
"& .feature-title": {
height: "25px",
paddingLeft: "26px",
fontSize: "14px",
background: "#E5E5E5",
"@media (max-width: 600px)": {
"& .feature-title-info .xs-only": {
display: "block",
},
},
},
"& .feature-name": {
minHeight: "60px",
padding: "5px",
borderBottom: "1px solid #eaeaea",
display: "flex",
alignItems: "center",
paddingLeft: "26px",
fontSize: "14px",
fontWeight: 600,
},
"& .feature-item": {
display: "flex",
flexFlow: "column",
alignItems: "center",
justifyContent: "center",
minHeight: "60px",
padding: "0 15px 0 15px",
borderBottom: "1px solid #eaeaea",
borderLeft: " 1px solid #eaeaea",
fontSize: "14px",
"& .link-text": {
color: "#2781B0",
},
"&.icon-yes": {
width: "15px",
height: "15px",
},
},
"& .feature-item-info": {
flex: 1,
display: "flex",
flexFlow: "column",
alignItems: "center",
justifyContent: "space-around",
textAlign: "center",
"@media (max-width: 600px)": {
display: "flex",
flexFlow: "row",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
"& .xs-only": {
display: "block",
flex: 1,
},
"& .plan-feature": {
flex: 1,
textAlign: "center",
paddingRight: "10px",
},
},
},
"& .plan-col": {
minWidth: "260px",
flex: 1,
},
"& .active-plan-col": {
background: "#FDFDFD 0% 0% no-repeat padding-box",
boxShadow: " 0px 3px 20px #00000038",
"& .plan-header": {
backgroundColor: "#2781B0",
},
"& .feature-title": {
background: "#F7F7F7",
},
},
}}
>
<Box className="features-col">
{featureList.map((fi) => {
const featureTitleRow = fi.featureTitleRow;
const isHeader = fi.isHeader;
if (isHeader) {
if (isPaidPlan) {
return (
<Box
key={fi.desc}
className="plan-header"
sx={{
fontSize: "14px",
paddingLeft: "26px",
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
"& .link-text": {
color: "#2781B0",
},
"& .min-icon": {
marginRight: "10px",
color: "#2781B0",
fill: "#2781B0",
},
}}
>
<LicenseDocIcon />
<a
href={`https://subnet.min.io/terms-and-conditions/${currentPlan}`}
rel="noreferrer noopener"
className={"link-text"}
>
View License agreement <br />
for the registered plan.
</a>
</Box>
);
}
return (
<Box
key={fi.desc}
className={`plan-header`}
sx={{
fontSize: "14px",
fontWeight: 600,
paddingLeft: "26px",
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
}}
>
{fi.label}
</Box>
);
}
if (featureTitleRow) {
return (
<Box
key={fi.desc}
className="feature-title"
sx={{
fontSize: "14px",
fontWeight: 600,
textTransform: "uppercase",
}}
>
<div>{getRenderValue(fi.desc)} </div>
</Box>
);
}
return (
<Box key={fi.desc} className="feature-name" style={fi.style}>
<div>{getRenderValue(fi.desc)} </div>
</Box>
);
})}
</Box>
{!isPaidPlan ? (
<Box
className={`plan-col ${
isCommunityPlan ? "active-plan-col" : "non-active-plan-col"
}`}
>
{COMMUNITY_PLAN_FEATURES.map((fi, idx) => {
const featureLabel = featureList[idx].desc;
const { featureTitleRow, isHeader, isOssLicenseLink } = fi;
if (isHeader) {
return getCommunityPlanHeader();
}
if (featureTitleRow) {
return (
<FeatureTitleRowCmp
key={fi.id}
featureLabel={featureLabel}
/>
);
}
if (isOssLicenseLink) {
return (
<Box
key={fi.id}
className="feature-item"
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<a
href={"https://www.gnu.org/licenses/agpl-3.0.en.html"}
rel="noreferrer noopener"
className={"link-text"}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setLicenseModal && setLicenseModal(true);
}}
>
GNU AGPL v3
</a>
</Box>
);
}
return (
<PricingFeatureItem
key={fi.id}
featureLabel={featureLabel}
label={fi.label}
detail={fi.detail}
xsLabel={fi.xsLabel}
style={fi.style}
/>
);
})}
<Box className="button-box">
{getButton(
`https://slack.min.io${linkTracker}`,
"Join Slack",
"outlined",
LICENSE_PLANS.COMMUNITY
)}
</Box>
</Box>
) : null}
<Box
className={`plan-col ${
isStandardPlan ? "active-plan-col" : "non-active-plan-col"
}`}
>
{STANDARD_PLAN_FEATURES.map((fi, idx) => {
const featureLabel = featureList[idx].desc;
const featureTitleRow = fi.featureTitleRow;
const isHeader = fi.isHeader;
if (isHeader) {
return getStandardPlanHeader();
}
if (featureTitleRow) {
return (
<FeatureTitleRowCmp key={fi.id} featureLabel={featureLabel} />
);
}
return (
<PricingFeatureItem
key={fi.id}
featureLabel={featureLabel}
label={fi.label}
detail={fi.detail}
xsLabel={fi.xsLabel}
style={fi.style}
/>
);
})}
<Box className="button-box">
{getButton(
`https://min.io/signup${linkTracker}`,
!PAID_PLANS.includes(currentPlan)
? "Subscribe"
: "Login to SUBNET",
"contained",
LICENSE_PLANS.STANDARD
)}
</Box>
</Box>
<Box
className={`plan-col ${
isEnterprisePlan ? "active-plan-col" : "non-active-plan-col"
}`}
>
{ENTERPRISE_PLAN_FEATURES.map((fi, idx) => {
const featureLabel = featureList[idx].desc;
const { featureTitleRow, isHeader, yesIcon } = fi;
if (isHeader) {
return getEnterpriseHeader();
}
if (featureTitleRow) {
return (
<FeatureTitleRowCmp key={fi.id} featureLabel={featureLabel} />
);
}
if (yesIcon) {
return (
<Box className="feature-item">
<Box className="feature-item-info">
<div className="xs-only"></div>
<Box className="plan-feature">
<CheckCircleIcon />
</Box>
</Box>
</Box>
);
}
return (
<PricingFeatureItem
key={fi.id}
featureLabel={featureLabel}
label={fi.label}
detail={fi.detail}
style={fi.style}
/>
);
})}
<Box className="button-box">
{getButton(
`https://min.io/signup${linkTracker}`,
!PAID_PLANS.includes(currentPlan)
? "Subscribe"
: "Login to SUBNET",
"contained",
LICENSE_PLANS.ENTERPRISE
)}
</Box>
</Box>
</Box>
</Box>
</Fragment>
);
}