@material-ui/core#Fab JavaScript Examples
The following examples show how to use
@material-ui/core#Fab.
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: CoinField.js From Alternative-Uniswap-Interface with GNU General Public License v3.0 | 6 votes |
export function RemoveLiquidityField2(props) {
// This component is used to selecting a coin and entering a value, the props are explained below:
// onClick - (string) => void - Called when the button is clicked
// symbol - string - The text displayed on the button
// value - string - The value of the text field
// onChange - (e) => void - Called when the text field changes
// activeField - boolean - Whether text can be entered into this field or not
const classes = useStyles();
const { onClick, symbol } = props;
return (
<div className={classes.container_blank}>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
className={classes.grid}
>
{/* Button */}
<Grid item xs={3}>
<Fab
size="small"
variant="extended"
onClick={onClick}
className={classes.fab}
>
{symbol}
<ExpandMoreIcon />
</Fab>
</Grid>
</Grid>
</div>
);
}
Example #2
Source File: StartButton.js From budgie-stream with MIT License | 6 votes |
export default function StartButton() {
const classes = useStyles();
const { playback } = useContext(ClientContext);
const [state, setState] = playback;
const devices = state.devices.filter((device) => device.selected === true);
const disabled = !!!devices.length;
const handleButtonClick = () => {
togglePlay(devices, !state.playing);
setState((prevState) => ({
...prevState,
playing: !prevState.playing,
}));
};
return (
<div className={classes.root}>
<Fab
disabled={disabled}
style={{ color: "#5e81ac" }}
onClick={handleButtonClick}
>
{state.playing ? <Stop /> : <PlayArrow />}
</Fab>
</div>
);
}
Example #3
Source File: OPEdit.js From Interceptor with MIT License | 6 votes |
render() {
//console.log("Rendering OPEdit");
const { classes } = this.props;
const op_data = _.cloneDeep(this.state.op_data);
return (
<Fragment>
<div style={{ padding: 20 }}>
<Grid
container
spacing={2}
direction="row"
justify="flex-start"
alignItems="center"
>
{this.renderParams(op_data)}
<Grid item></Grid>
</Grid>
</div>
<Fab variant="extended" color="secondary" className={classes.loadFab} onClick={() => this.props.procData({ opEdit: { loadRequest: true } })}>
<ArrowDownwardIcon className={classes.extendedIcon} />
Load
</Fab>
<Fab variant="extended" color="secondary" className={classes.saveFab} onClick={() => this.props.procData({ opEdit: this.state.op_data })}>
<ArrowUpwardIcon className={classes.extendedIcon} />
Save
</Fab>
</Fragment>
);
}
Example #4
Source File: TopPanel.js From to-view-list with MIT License | 6 votes |
TopPanel = () => {
const [, dispatch] = useEntryContext();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('xs'));
const classes = useTopPanelStyles();
return (
<Paper className={classes.root}>
<Search />
<Filter />
{!isMobile ? (
<Button
className={classes.desktopButton}
component={RouterLink}
to="/add_update"
size="large"
variant="contained"
color="primary"
startIcon={<PostAddIcon />}
onClick={() => dispatch(resetEditValues())}
>
Add Entry
</Button>
) : (
<HideOnScroll>
<Fab
className={classes.fab}
color="primary"
component={RouterLink}
to="/add_update"
onClick={() => dispatch(resetEditValues())}
>
<PostAddIcon />
</Fab>
</HideOnScroll>
)}
</Paper>
);
}
Example #5
Source File: ScrollToTop.jsx From archeage-tools with The Unlicense | 6 votes |
render() {
const { scrollY, height } = this.state;
return (
<Zoom in={scrollY >= height * 0.5} unmountOnExit>
<Fab
color="primary"
className="fab"
onClick={this.handleClick}
>
<ExpandLessIcon />
</Fab>
</Zoom>
);
}
Example #6
Source File: CoinField.js From Alternative-Uniswap-Interface with GNU General Public License v3.0 | 5 votes |
export function RemoveLiquidityField1(props) {
// This component is used to selecting a coin and entering a value, the props are explained below:
// onClick - (string) => void - Called when the button is clicked
// symbol - string - The text displayed on the button
// value - string - The value of the text field
// onChange - (e) => void - Called when the text field changes
// activeField - boolean - Whether text can be entered into this field or not
const classes = useStyles();
const { onClick, symbol, value, onChange, activeField } = props;
return (
<div className={classes.container_blank}>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
className={classes.grid}
>
{/* Button */}
<Grid item xs={3}>
<Fab
size="small"
variant="extended"
onClick={onClick}
className={classes.fab}
>
{symbol}
<ExpandMoreIcon />
</Fab>
</Grid>
{/* Text Field */}
<Grid item xs={9}>
<InputBase
value={value}
onChange={onChange}
placeholder="0.0"
disabled={!activeField}
classes={{
root: classes.container_input,
input: classes.inputBase,
}}
/>
</Grid>
{/* </div> */}
</Grid>
</div>
);
}
Example #7
Source File: MapGeojsonMarkers.jsx From Zulu with MIT License | 5 votes |
render() {
var center = [this.state.lat, this.state.lng];
const basemapsDict = {
dark: " https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}.png",
osm: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
hot: "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png",
}
return (
<Map zoom={this.state.zoom} center={center}>
<div className="leaflet-bottom leaflet-right buttons-container">
<Container>
<Fab
color="primary"
aria-label="edit"
tooltip="Add a new story!"
onClick={this.showModal}>
<FaPlus />
</Fab>
</Container>
</div>
{
<Modal position={[this.state.lat, this.state.lng]} show={this.state.showModal} onHide={this.closeModal.bind(this)}>
<form onSubmit={this.handleSubmit}>
<h3> Add your Story. </h3>
<label>
<br />
Title:
<br />
<input name="storyTitle" type="text" defaultValue={this.state.storyTitle} onChange={this.handleChange} />
<br />
</label>
<label>
<br />
Body:<br />
<textarea name="storyBody" defaultValue={this.state.storyBody} onChange={this.handleChange} />
<br />
</label>
<label>
<br />
Add a photo: (optional) <br />
<input type="file" style={{ marginRight: "-95px" }} ref={this.fileInput} />
<br />
</label>
<br />
<br />
<input type="submit" value="Submit" />
</form>
</Modal>}
<TileLayer
attribution='&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url={basemapsDict[this.state.basemap]}
/>
<Basemap basemap={this.state.basemap} onChange={this.onBMChange} />
<GeojsonLayer lat={center[0]} lng={center[1]} maxDist={5000} cluster={true} />
<GeoWikipediaLayer lat={center[0]} lng={center[1]} maxDist={5000} cluster={true} />
<Marker position={center} icon={MyLocationIcon}>
<Popup>
<div>Your Location - latitude: {Number(this.state.lat).toFixed(4)} - longitude: {Number(this.state.lng).toFixed(4)}</div>
</Popup>
</Marker>
</Map>
);
}
Example #8
Source File: Waifu.jsx From animeworldz with MIT License | 5 votes |
function Waifu() {
const listInnerRef = useRef(null);
const [picsList, setPicsList] = useState([]);
const [refreshKey, setRefreshKey] = useState(0);
const [keyword, setKeyword] = useState("waifu");
const classes = useStyles();
const getWaifuPics = () => {
axios
.post(`https://api.waifu.pics/many/sfw/${keyword}`, {})
.then((res) => setPicsList(res.data))
.catch((err) => console.log(err));
};
useEffect(() => {
getWaifuPics();
}, [refreshKey, keyword]);
let temp = [];
if (picsList.length !== 0) {
picsList.files.map((img) => {
temp.push({
src: img,
width: 4,
height: 4,
});
});
}
const handleChange = (event) => {
setKeyword(event.target.value);
};
const onScroll = () => {
if (listInnerRef.current) {
const { scrollTop, scrollHeight, clientHeight } = listInnerRef.current;
if (scrollTop + clientHeight === scrollHeight) {
console.log("reached bottom");
}
}
};
return (
<div className={classes.root} ref={listInnerRef} onScroll={onScroll}>
<Gallery photos={temp ? temp : ""} />
<Select
value={keyword}
onChange={handleChange}
className={classes.select}
color="secondary"
variant="outlined"
autoFocus={true}
>
<MenuItem value={"waifu"}>Waifu</MenuItem>
<MenuItem value={"neko"}>Neko</MenuItem>
<MenuItem value={"megumin"}>Megumin</MenuItem>
<MenuItem value={"cuddle"}>Cuddle</MenuItem>
<MenuItem value={"pat"}>Pat</MenuItem>
<MenuItem value={"slap"}>Slap</MenuItem>
<MenuItem value={"dance"}>Dance</MenuItem>
</Select>
<Fab
color="secondary"
aria-label="refresh"
className={classes.fab}
onClick={() => setRefreshKey((prev) => prev + 1)}
>
<RefreshRounded />
</Fab>
</div>
);
}
Example #9
Source File: CoinField.js From Alternative-Uniswap-Interface with GNU General Public License v3.0 | 5 votes |
export default function CoinField(props) {
// This component is used to selecting a token and entering a value, the props are explained below:
// onClick - (string) => void - Called when the button is clicked
// symbol - string - The text displayed on the button
// value - string - The value of the text field
// onChange - (e) => void - Called when the text field changes
// activeField - boolean - Whether text can be entered into this field or not
const classes = useStyles();
const { onClick, symbol, value, onChange, activeField } = props;
return (
<div className={classes.container}>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
className={classes.grid}
>
{/* Button */}
<Grid item xs={3}>
<Fab
size="small"
variant="extended"
onClick={onClick}
className={classes.fab}
>
{symbol}
<ExpandMoreIcon />
</Fab>
</Grid>
{/* Text Field */}
<Grid item xs={9}>
<InputBase
value={value}
onChange={onChange}
placeholder="0.0"
disabled={!activeField}
classes={{ root: classes.input, input: classes.inputBase }}
/>
</Grid>
</Grid>
</div>
);
}
Example #10
Source File: VidDropzone.js From youtube-clone with MIT License | 5 votes |
function StyledDropzone() {
const dispatch = useDispatch();
const classes = useStyles();
const onDrop = ([videoFile]) => {
if (videoFile) {
dispatch(uploadVideo(videoFile));
}
};
const {
getRootProps,
getInputProps,
open,
isDragAccept,
isDragReject,
} = useDropzone({
noClick: true,
noKeyboard: true,
accept: "video/mp4",
maxSize: 25 * 1024 * 1024,
onDrop,
});
return (
<div className={classes.root}>
<div
{...getRootProps({
className: clsx(classes.content, {
[classes.acceptStyle]: isDragAccept,
[classes.rejectStyle]: isDragReject,
}),
})}
>
<input {...getInputProps()} />
<Fab className={clsx(classes.button, classes.uploadBtn)} onClick={open}>
<PublishIcon className={classes.uploadIcon} />
</Fab>
<Typography variant="body1">
Drag and drop a <strong>.mp4 </strong> file to upload
</Typography>
<Typography variant="body2" gutterBottom>
Your videos will be private until you publish them.
</Typography>
<Typography variant="caption" gutterBottom>
* Video Uploads are limited to 25 MB.
</Typography>
<Button
className={classes.button}
variant="contained"
color="primary"
onClick={open}
>
Select File
</Button>
</div>
</div>
);
}
Example #11
Source File: index.jsx From playground with MIT License | 5 votes |
export default function TemporaryDrawer(props) {
const [state, setState] = React.useState({
right: false,
});
const toggleDrawer = (anchor, open) => (event) => {
if (
event.type === "keydown" &&
(event.key === "Tab" || event.key === "Shift")
) {
return;
}
setState({ ...state, [anchor]: open });
};
return (
<div>
<React.Fragment>
<Fab
style={{
position: "fixed",
right: "0.5rem",
bottom: "1rem",
}}
variant="extended"
size="small"
color="secondary"
onClick={toggleDrawer("right", true)}
>
Prop Explorer
</Fab>
<Drawer
anchor={"right"}
open={state["right"]}
onClose={toggleDrawer("right", false)}
>
<div
style={{
width: "70vw",
maxWidth: 700,
paddingLeft: "1rem",
}}
>
{props.children}
</div>
</Drawer>
</React.Fragment>
</div>
);
}
Example #12
Source File: LoanApprovalPage.js From SESTA-FMS with GNU Affero General Public License v3.0 | 4 votes |
render() {
const { classes } = this.props;
let data = this.state.data;
let statusValue = this.state.values.selectedStatus;
return (
<Layout breadcrumbs={APPROVE_LOAN_BREADCRUMBS}>
{!this.state.isLoader ? (
<Grid>
<div className="App">
<h5 className={style.loan}>LOANS</h5>
<h2 className={style.title}>Loan Approval</h2>
<Card className={classes.mainContent}>
<Grid
container
spacing={3}
style={{ padding: "20px 0px", alignItems: "center" }}
>
<Grid spacing={1} xs={1}>
<PersonIcon className={classes.Icon} />
</Grid>
<Grid spacing={1} xs={11}>
<Grid container spacing={3}>
<Grid spacing={2} xs={3}>
<b>
<div className={classes.member}>
LOANEE
<br />
<span className={classes.fieldValues}>
{data.loanee}
</span>
</div>
</b>
</Grid>
<Grid spacing={2} xs={3}>
<b>
<div className={classes.member}>
SHG GROUP <br />
<span className={classes.fieldValues}>
{data.shg}
</span>
</div>
</b>
</Grid>
<Grid spacing={2} xs={3}>
<b>
<div className={classes.member}>
VILLAGE <br />
<span className={classes.fieldValues}>
{data.village}
</span>
</div>
</b>
</Grid>
<Grid spacing={2} xs={3}>
<b>
<div className={classes.member}>
VILLAGE ORGANIZATION <br />
<span className={classes.fieldValues}>
{data.vo}
</span>
</div>
</b>
</Grid>
</Grid>
</Grid>
</Grid>
<Divider />
<Grid
container
spacing={3}
style={{ padding: "20px 0px", alignItems: "center" }}
>
<Grid spacing={1} xs={1}>
<MoneyIcon className={classes.Icon} />
</Grid>
<Grid spacing={1} xs={11}>
<Grid container spacing={3}>
<Grid spacing={2} xs={3}>
<b>
<div className={classes.member}>
PURPOSE
<br />
<span className={classes.fieldValues}>
{data.purpose}
</span>
</div>
</b>
</Grid>
<Grid spacing={2} xs={3}>
<b>
<div className={classes.member}>
APPLICATION DATE <br />
<span className={classes.fieldValues}>
{data.applicationDate}
</span>
</div>
</b>
</Grid>
<Grid spacing={2} xs={3}>
<b>
<div className={classes.member}>
LOAN AMOUNT <br />
<span className={classes.fieldValues}>
{data.loanAmount}
</span>
</div>
</b>
</Grid>
<Grid spacing={2} xs={3}>
<b>
<div className={classes.member}>
LOAN DURATION <br />
<span className={classes.fieldValues}>
{data.duration}
</span>
</div>
</b>
</Grid>
</Grid>
</Grid>
</Grid>
<Divider />
<Grid container spacing={3} style={{ padding: "20px 0px" }}>
<Grid item md={12}>
<label htmlFor="upload-file">
<input
style={{ display: "none" }}
required
type="file"
name="upload-file"
id="upload-file"
onChange={this.onChangeHandler}
/>
<Fab
color="primary"
size="medium"
component="span"
aria-label="add"
variant="extended"
>
<FileCopyIcon /> Upload loan application
</Fab>
</label>{" "}
<IconButton
aria-label="cancel"
color="secondary"
style={{ paddingLeft: "2px" }}
>
<CancelIcon onClick={this.cancelFile} />
</IconButton>
{this.state.fileName !== "" ? (
<label style={{ color: "green", fontSize: "11px" }}>
Selected File: {this.state.fileName}
</label>
) : (
<label style={{ color: "red", fontSize: "11px" }}>
No File Selected!
</label>
)}
</Grid>
<Grid item md={5} xs={12}>
<Autocomplete
id="selectStatus"
name="loanStatus"
options={this.state.loanStatusList}
variant="outlined"
getOptionLabel={(option) => option.name}
placeholder="Select Status"
onChange={this.handleStatusChange}
value={
statusValue
? this.state.loanStatusList[
this.state.loanStatusList.findIndex(function (
item,
i
) {
return item.id === statusValue;
})
] || null
: null
}
renderInput={(params) => (
<Input
{...params}
fullWidth
label="Select Status"
name="loanStatus"
variant="outlined"
/>
)}
/>
</Grid>
<Grid item md={7} xs={12}>
<Input
fullWidth
label="Comment*"
name="comment"
error={this.hasError("comment")}
helperText={
this.hasError("comment")
? this.state.errors.comment[0]
: null
}
value={this.state.values.comment || ""}
onChange={this.handleChange}
variant="outlined"
/>
</Grid>
</Grid>
<Divider />
<br />
<Grid>
<Button onClick={this.onSave.bind(this)}>Save</Button>
<Button
color="secondary"
clicked={this.cancelForm}
component={Link}
to="/loans"
>
Cancel
</Button>
</Grid>
</Card>
</div>
</Grid>
) : (
<Spinner />
)}
</Layout>
);
}
Example #13
Source File: ActivityPage.js From SESTA-FMS with GNU Affero General Public License v3.0 | 4 votes |
render() {
let activitytypeFilter = this.state.getActivitytype;
let addActivitytype = this.state.values.addActivitytype;
return (
<Layout
breadcrumbs={
this.state.editPage[0]
? EDIT_ACTIVITY_BREADCRUMBS
: ADD_ACTIVITY_BREADCRUMBS
}
>
{!this.state.isLoader ? (
<Card style={{ maxWidth: "45rem" }}>
<CardHeader
title={this.state.editPage[0] ? "Edit Activity" : "Add Activity"}
subheader={
this.state.editPage[0]
? "You can edit activity data here!"
: "You can add new activity data here!"
}
/>
<Divider />
<CardContent>
<Grid container spacing={3}>
<Grid item md={12} xs={12}>
{this.state.formSubmitted === false ? (
<Snackbar severity="error" Showbutton={false}>
{this.state.errorCode}
</Snackbar>
) : null}
</Grid>
<Grid item md={6} xs={12}>
<Autotext
id="combo-box-demo"
options={activitytypeFilter}
variant="outlined"
label="Select Activity Type*"
getOptionLabel={(option) => option.name}
onChange={(event, value) => {
this.handleAutocompleteChange(event, value);
}}
defaultValue={[]}
value={
addActivitytype
? activitytypeFilter[
activitytypeFilter.findIndex(function (item, i) {
return item.id === addActivitytype;
})
] || null
: null
}
error={this.hasError("addActivitytype")}
helperText={
this.hasError("addActivitytype")
? this.state.errors.addActivitytype[0]
: null
}
renderInput={(params) => (
<Input
fullWidth
label="Select Activity Type*"
name="addActivitytype"
variant="outlined"
/>
)}
/>
</Grid>
<Grid item md={6} xs={12}>
<Input
fullWidth
label="Description*"
name="addTitle"
error={this.hasError("addTitle")}
helperText={
this.hasError("addTitle")
? this.state.errors.addTitle[0]
: null
}
value={this.state.values.addTitle || ""}
onChange={this.handleChange}
variant="outlined"
/>
</Grid>
<Grid item md={3} xs={12}>
<Datepicker
label="Date*"
name="addStartDate"
error={this.hasError("addStartDate")}
helperText={
this.hasError("addStartDate")
? this.state.errors.addStartDate[0]
: null
}
value={this.state.values.addStartDate || null}
format={"dd MMM yyyy"}
onChange={(value) =>
this.setState({
values: { ...this.state.values, addStartDate: value },
})
}
/>
</Grid>
<Grid item md={9} xs={12}>
<TextField
id="outlined-multiline-static"
fullWidth
label="Status / Comments"
rows={10}
name="addDescription"
value={this.state.values.addDescription || ""}
onChange={this.handleChange}
variant="outlined"
/>
</Grid>
<Grid item md={12}>
<label htmlFor="upload-file">
<input
style={{ display: "none" }}
required
type="file"
name="upload-file"
id="upload-file"
onChange={this.onChangeHandler}
/>
<Fab
color="primary"
size="medium"
component="span"
aria-label="add"
variant="extended"
>
<FileCopyIcon /> Upload Activity Document
</Fab>
</label>{" "}
<IconButton
aria-label="cancel"
color="secondary"
style={{ paddingLeft: "2px" }}
>
<CancelIcon onClick={this.cancelFile} />
</IconButton>
{this.state.fileName !== "" ? (
<label style={{ color: "green", fontSize: "11px" }}>
Selected File: {this.state.fileName}
</label>
) : (
<label style={{ color: "red", fontSize: "11px" }}>
No File Selected!
</label>
)}
</Grid>
</Grid>
</CardContent>
<Divider />
<CardActions style={{ padding: "15px" }}>
<Button type="submit" onClick={this.onSave.bind(this)}>
Save
</Button>
<Button
color="secondary"
clicked={this.cancelForm}
component={Link}
to="/Activities"
>
cancel
</Button>
</CardActions>
</Card>
) : (
<Spinner />
)}
</Layout>
);
}
Example #14
Source File: PageWrapper.jsx From zubhub with GNU Affero General Public License v3.0 | 4 votes |
/**
* @function PageWrapper View
* @author Raymond Ndibe <[email protected]>
*
* @todo - describe function's signature
*/
function PageWrapper(props) {
const backToTopEl = React.useRef(null);
const classes = useStyles();
const common_classes = useCommonStyles();
const trigger = useScrollTrigger();
const [searchType, setSearchType] = useState(
getQueryParams(window.location.href).get('type') || SearchType.PROJECTS,
);
const formRef = useRef();
const token = useSelector(state => state.auth.token);
const [state, setState] = React.useState({
username: null,
anchor_el: null,
loading: false,
open_search_form: false,
});
const [options, setOptions] = useState([]);
const [query, setQuery] = useState('');
const [queryInput, setQueryInput] = useState('');
const throttledFetchOptions = useMemo(
() =>
throttle(async (query, searchType) => {
if (query.length === 0) {
setOptions([]);
return;
}
const api = new API();
let completions = [];
if (searchType === SearchType.TAGS) {
completions = await api.autocompleteTags({ query, token });
completions = completions.map(({ name }) => ({
title: name,
}));
} else if (searchType === SearchType.PROJECTS) {
completions = await api.autocompleteProjects({ query, token });
completions = completions.map(({ id, title, creator, images }) => ({
title,
shortInfo: creator.username,
image: images.length > 0 ? images[0].image_url : null,
link: `/projects/${id}`,
}));
} else {
completions = await api.autocompleteCreators({ query, token });
completions = completions.map(({ username, avatar }) => ({
title: username,
image: avatar,
link: `/creators/${username}`,
}));
}
setOptions(completions);
}, 2),
[],
);
useEffect(() => {
throttledFetchOptions(
query ||
(props.location.search &&
getQueryParams(window.location.href).get('q')),
searchType,
);
}, [query, searchType]);
useEffect(() => {
throttledFetchOptions.cancel();
}, []);
useEffect(() => {
handleSetState({ loading: true });
fetchHero(props)
.then(() => {
if (props.auth.token) {
return props.getAuthUser(props);
}
})
.finally(() => {
handleSetState({ loading: false });
});
}, [props.auth.token]);
React.useEffect(() => {
handleSetState(handleProfileMenuClose());
}, [trigger]);
const handleSetState = obj => {
if (obj) {
Promise.resolve(obj).then(obj => {
setState(state => ({ ...state, ...obj }));
});
}
};
const onSearchOptionClick = async (_, option) => {
if (!option || !option.title) return;
await new Promise(resolve => setTimeout(resolve, 100));
if (option.link) {
window.history.pushState({}, '', option.link);
window.location.reload();
return;
}
const queryParams = new URLSearchParams({
type: searchType,
q: option.title,
});
window.history.pushState({}, '', `/search?${queryParams}`);
window.location.reload();
};
const handleSubmit = e => {
e.preventDefault();
const queryParams = new URLSearchParams({
type: searchType,
q: query,
});
window.history.pushState({}, '', `/search?${queryParams}`);
window.location.reload();
};
const { anchor_el, loading, open_search_form } = state;
const { t } = props;
const { zubhub, hero } = props.projects;
const profileMenuOpen = Boolean(anchor_el);
return (
<>
<ToastContainer />
<CssBaseline />
<AppBar className={classes.navBarStyle}>
<Container className={classes.mainContainerStyle}>
<Toolbar className={classes.toolBarStyle}>
<Box className={classes.logoStyle}>
<Link to="/">
<img
src={zubhub?.header_logo_url ? zubhub.header_logo_url : logo}
alt="logo"
/>
</Link>
<Box
className={clsx(
classes.languageSelectBoxStyle,
common_classes.displayInlineFlex,
common_classes.alignCenter,
common_classes.addOnSmallScreen,
)}
>
<TranslateIcon />
<Select
className={classes.languageSelectStyle}
value=""
onChange={e => handleChangeLanguage({ e, props })}
>
{Object.keys(languageMap).map((ln, index) => (
<MenuItem key={index} value={ln}>
{languageMap[ln]}
</MenuItem>
))}
</Select>
</Box>
<Box
className={clsx(
classes.languageSelectBoxStyle,
common_classes.displayInlineFlex,
common_classes.alignCenter,
common_classes.removeOnSmallScreen,
)}
>
<TranslateIcon />
<Select
className={classes.languageSelectStyle}
value={props.i18n.language}
onChange={e => handleChangeLanguage({ e, props })}
>
{Object.keys(languageMap).map((ln, index) => (
<MenuItem key={index} value={ln}>
{languageMap[ln]}
</MenuItem>
))}
</Select>
</Box>
<form
action="/search"
className={clsx(classes.searchFormStyle, classes.removeOn894)}
role="search"
onSubmit={handleSubmit}
ref={formRef}
>
<FormControl variant="outlined">
<InputLabel
htmlFor="q"
className={classes.searchFormLabelStyle}
>
{t('pageWrapper.inputs.search.label')}
</InputLabel>
<FormGroup row>
<FormControl variant="outlined">
<InputSelect
searchType={searchType}
onSearchTypeChange={setSearchType}
name="type"
>
<MenuItem value={SearchType.PROJECTS}>
Projects
</MenuItem>
<MenuItem value={SearchType.CREATORS}>
Creators
</MenuItem>
<MenuItem value={SearchType.TAGS}>Tags</MenuItem>
</InputSelect>
</FormControl>
<Autocomplete
options={options}
defaultValue={{
title:
props.location.search &&
getQueryParams(window.location.href).get('q'),
}}
renderOption={(option, { inputValue }) => (
<Option
option={option}
inputValue={inputValue}
onOptionClick={onSearchOptionClick}
/>
)}
onChange={onSearchOptionClick}
>
{params => (
<TextField
name="q"
id="q"
type="search"
variant="outlined"
{...params}
InputProps={{
...params.InputProps,
className: clsx(
classes.searchFormInputStyle,
'search-form-input',
),
endAdornment: (
<InputAdornment position="end">
<IconButton
type="submit"
className={classes.searchFormSubmitStyle}
aria-label={t(
'pageWrapper.inputs.search.label',
)}
>
<SearchIcon />
</IconButton>
</InputAdornment>
),
pattern: '(.|s)*S(.|s)*',
defaultValue: {
title:
props.location.search &&
getQueryParams(window.location.href).get('q'),
},
}}
onChange={e => setQuery(e.target.value)}
placeholder={`${t(
'pageWrapper.inputs.search.label',
)}...`}
/>
)}
</Autocomplete>
</FormGroup>
</FormControl>
</form>
</Box>
<div className={classes.navActionStyle}>
{!props.auth.token ? (
<>
<IconButton
className={clsx(
classes.toggleSearchFormStyle,
classes.addOn894,
)}
id="toggle-search"
aria-label="toggle search form"
onClick={() =>
handleSetState(handleToggleSearchForm(state))
}
>
<SearchIcon />
</IconButton>
<Link
className={clsx(
classes.textDecorationNone,
common_classes.removeOnSmallScreen,
)}
to="/login"
>
<CustomButton
variant="outlined"
size="large"
secondaryButtonStyle
customButtonStyle
>
{t('pageWrapper.navbar.login')}
</CustomButton>
</Link>
<Link
className={clsx(
classes.textDecorationNone,
common_classes.removeOnSmallScreen,
)}
to="/signup"
>
<CustomButton
variant="contained"
size="large"
primaryButtonStyle
customButtonStyle
className={common_classes.marginLeft1em}
>
{t('pageWrapper.navbar.signup')}
</CustomButton>
</Link>
<MenuRoundedIcon
className={common_classes.addOnSmallScreen}
aria-label={t('pageWrapper.navbar.menu')}
aria-controls="menu"
aria-haspopup="true"
onClick={e => handleSetState(handleProfileMenuOpen(e))}
/>
<Menu
className={common_classes.addOnSmallScreen}
disableScrollLock={true}
id="menu"
anchorEl={anchor_el}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={profileMenuOpen}
onClose={e => handleSetState(handleProfileMenuClose(e))}
>
<MenuItem>
<Link className={classes.textDecorationNone} to="/login">
<Typography
variant="subtitle2"
color="textPrimary"
component="span"
>
{t('pageWrapper.navbar.login')}
</Typography>
</Link>
</MenuItem>
<MenuItem>
<Link className={classes.textDecorationNone} to="/signup">
<Typography
variant="subtitle2"
color="textPrimary"
component="span"
>
{t('pageWrapper.navbar.signup')}
</Typography>
</Link>
</MenuItem>
</Menu>
</>
) : (
<>
<Link
className={clsx(
classes.textDecorationNone,
common_classes.marginRight1em,
common_classes.removeOnSmallScreen,
)}
to="/projects/create"
>
<CustomButton
variant="contained"
primaryButtonStyle
customButtonStyle
size="small"
>
{t('pageWrapper.navbar.createProject')}
</CustomButton>
</Link>
<IconButton
className={clsx(
classes.toggleSearchFormStyle,
classes.addOn894,
)}
id="toggle-search"
aria-label="toggle search form"
onClick={() =>
handleSetState(handleToggleSearchForm(state))
}
>
<SearchIcon />
</IconButton>
<NotificationButton
className={clsx(
common_classes.marginRight1em,
common_classes.removeOnSmallScreen,
)}
/>
<Avatar
className={clsx(
classes.avatarStyle,
common_classes.removeOnSmallScreen,
)}
aria-label={`${props.auth.username}' Avatar`}
aria-controls="profile_menu"
aria-haspopup="true"
onClick={e => handleSetState(handleProfileMenuOpen(e))}
src={props.auth.avatar}
alt={props.auth.username}
/>
<Menu
className={classes.profileMenuStyle}
disableScrollLock={true}
id="profile_menu"
anchorEl={anchor_el}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={profileMenuOpen}
onClose={e => handleSetState(handleProfileMenuClose(e))}
>
<MenuItem>
<Tooltip title={props.auth.username} placement="top">
<Typography
variant="subtitle2"
color="textPrimary"
component="span"
className={classes.profileStyle}
>
{props.auth.username }
</Typography>
</Tooltip>
</MenuItem>
<MenuItem>
<a className={classes.textDecorationNone} href="/profile">
<Typography
variant="subtitle2"
color="textPrimary"
component="span"
>
{t('pageWrapper.navbar.profile')}
</Typography>
</a>
</MenuItem>
<MenuItem className={common_classes.addOnSmallScreen}>
<Link
className={classes.textDecorationNone}
to="/projects/create"
>
<Typography
variant="subtitle2"
color="textPrimary"
component="span"
>
{t('pageWrapper.navbar.createProject')}
</Typography>
</Link>
</MenuItem>
<MenuItem>
<Link
className={classes.textDecorationNone}
to={`/creators/${props.auth.username}/projects`}
>
<Typography
variant="subtitle2"
color="textPrimary"
component="span"
>
{t('pageWrapper.navbar.projects')}
</Typography>
</Link>
</MenuItem>
<MenuItem>
<Link
className={classes.textDecorationNone}
to={`/creators/${props.auth.username}/followers`}
>
<Typography
variant="subtitle2"
color="textPrimary"
component="span"
>
{t('pageWrapper.navbar.followers')}
</Typography>
</Link>
</MenuItem>
<MenuItem>
<Link
className={classes.textDecorationNone}
to={`/creators/${props.auth.username}/following`}
>
<Typography
variant="subtitle2"
color="textPrimary"
component="span"
>
{t('pageWrapper.navbar.following')}
</Typography>
</Link>
</MenuItem>
<MenuItem>
<Link
className={classes.textDecorationNone}
to="/projects/saved"
>
<Typography
variant="subtitle2"
color="textPrimary"
component="span"
>
{t('pageWrapper.navbar.savedProjects')}
</Typography>
</Link>
</MenuItem>
<MenuItem className={classes.logOutStyle}>
<Typography
className={common_classes.colorRed}
variant="subtitle2"
component="span"
onClick={e => logout(e, props)}
>
{t('pageWrapper.navbar.logout')}
</Typography>
</MenuItem>
</Menu>
</>
)}
</div>
</Toolbar>
{open_search_form ? (
<ClickAwayListener
onClickAway={e => handleSetState(closeSearchFormOrIgnore(e))}
>
<form
action="/search"
className={clsx(classes.smallSearchFormStyle, classes.addOn894)}
role="search"
ref={formRef}
>
<FormControl variant="outlined" style={{ minWidth: 'unset' }}>
<InputSelect
searchType={searchType}
onSearchTypeChange={setSearchType}
name="type"
>
<MenuItem value={SearchType.PROJECTS}>Projects</MenuItem>
<MenuItem value={SearchType.CREATORS}>Creators</MenuItem>
<MenuItem value={SearchType.TAGS}>Tags</MenuItem>
</InputSelect>
</FormControl>
<FormControl
variant="outlined"
style={{ flex: '1 1 auto', maxWidth: '350px' }}
>
<InputLabel
htmlFor="q"
className={classes.searchFormLabelStyle}
>
{t('pageWrapper.inputs.search.label')}
</InputLabel>
<Autocomplete
options={options}
defaultValue={
props.location.search &&
getQueryParams(window.location.href).get('q')
}
renderOption={(option, { inputValue }) => (
<Option
option={option}
inputValue={inputValue}
onOptionClick={onSearchOptionClick}
/>
)}
onChange={onSearchOptionClick}
>
{params => (
<TextField
name="q"
id="q"
type="search"
variant="outlined"
{...params}
InputProps={{
...params.InputProps,
className: clsx(
classes.smallSearchFormInputStyle,
'search-form-input',
),
endAdornment: (
<InputAdornment position="end">
<IconButton
type="submit"
className={classes.searchFormSubmitStyle}
aria-label={t(
'pageWrapper.inputs.search.label',
)}
>
<SearchIcon />
</IconButton>
</InputAdornment>
),
pattern: '(.|s)*S(.|s)*',
}}
placeholder={`${t(
'pageWrapper.inputs.search.label',
)}...`}
onChange={e => setQuery(e.target.value)}
/>
)}
</Autocomplete>
</FormControl>
</form>
</ClickAwayListener>
) : null}
</Container>
</AppBar>
<Toolbar ref={backToTopEl} />
{loading ? <LoadingPage /> : props.children}
<footer className={clsx('footer-distributed', classes.footerStyle)}>
<Box>
<a href="https://unstructured.studio">
<img
src={
zubhub?.footer_logo_url
? zubhub.footer_logo_url
: unstructuredLogo
}
className={classes.footerLogoStyle}
alt="unstructured-studio-logo"
/>
</a>
<div>
<Box
className={clsx(
classes.languageSelectBoxStyle,
common_classes.displayInlineFlex,
common_classes.alignCenter,
common_classes.addOnSmallScreen,
)}
>
<TranslateIcon />
<Select
className={classes.languageSelectStyle}
value=""
onChange={e => handleChangeLanguage({ e, props })}
>
{Object.keys(languageMap).map((ln, index) => (
<MenuItem key={index} value={ln}>
{languageMap[ln]}
</MenuItem>
))}
</Select>
</Box>
<Box
className={clsx(
classes.languageSelectBoxStyle,
common_classes.displayInlineFlex,
common_classes.alignCenter,
common_classes.removeOnSmallScreen,
)}
>
<TranslateIcon />
<Select
className={classes.languageSelectStyle}
value={props.i18n.language}
onChange={e => handleChangeLanguage({ e, props })}
>
{Object.keys(languageMap).map((ln, index) => (
<MenuItem key={index} value={ln}>
{languageMap[ln]}
</MenuItem>
))}
</Select>
</Box>
</div>
</Box>
<section className={classes.footerSectionStyle}>
<Box className={classes.footerBoxStyle}>
<Typography
variant="subtitle2"
color="textPrimary"
className={classes.footerTitleStyle}
>
{t('pageWrapper.footer.privacy')}
</Typography>
<Link
to={`/privacy_policy`}
className={common_classes.textDecorationNone}
>
<Typography
variant="subtitle2"
color="textPrimary"
className={classes.footerLinkStyle}
>
{t('pageWrapper.footer.guidelines')}
</Typography>
</Link>
<Link
to={`/terms_of_use`}
className={common_classes.textDecorationNone}
>
<Typography
variant="subtitle2"
color="textPrimary"
className={classes.footerLinkStyle}
>
{t('pageWrapper.footer.termsOfUse')}
</Typography>
</Link>
</Box>
<Box className={classes.footerBoxStyle}>
<Typography
variant="subtitle2"
color="textPrimary"
className={classes.footerTitleStyle}
>
{t('pageWrapper.footer.about')}
</Typography>
<Link to="/about" className={common_classes.textDecorationNone}>
<Typography
variant="subtitle2"
color="textPrimary"
className={classes.footerLinkStyle}
>
{t('pageWrapper.footer.zubhub')}
</Typography>
</Link>
</Box>
<Box className={classes.footerBoxStyle}>
<Typography
variant="subtitle2"
color="textPrimary"
className={classes.footerTitleStyle}
>
{t('pageWrapper.footer.help')}
</Typography>
<a
target="__blank"
rel="noreferrer"
href={
hero.tinkering_resource_url
? hero.tinkering_resource_url
: 'https://kriti.unstructured.studio/'
}
className={common_classes.textDecorationNone}
>
<Typography
variant="subtitle2"
color="textPrimary"
className={classes.footerLinkStyle}
>
{t('pageWrapper.footer.resources')}
</Typography>
</a>
<Link
to={`/faqs`}
className={clsx(
common_classes.textDecorationNone,
common_classes.displayNone,
)}
>
<Typography
variant="subtitle2"
color="textPrimary"
className={classes.footerLinkStyle}
>
{t('pageWrapper.footer.faqs')}
</Typography>
</Link>
<a
href="mailto:[email protected]"
className={common_classes.textDecorationNone}
>
<Typography
variant="subtitle2"
color="textPrimary"
className={classes.footerLinkStyle}
>
{t('pageWrapper.footer.contactUs')}
</Typography>
</a>
</Box>
</section>
<Zoom in={useScrollTrigger}>
<div
onClick={e => handleScrollTopClick(e, backToTopEl)}
role="presentation"
className={classes.scrollTopButtonStyle}
>
<Fab color="secondary" size="small" aria-label="scroll back to top">
<KeyboardArrowUpIcon />
</Fab>
</div>
</Zoom>
</footer>
</>
);
}
Example #15
Source File: Project.jsx From zubhub with GNU Affero General Public License v3.0 | 4 votes |
/**
* @function Project Component
* @author Raymond Ndibe <[email protected]>
*
* @todo - describe function's signature
*/
function Project(props) {
const classes = useStyles();
const common_classes = useCommonStyles();
const { project, t } = props;
return (
<Link to={`/projects/${project.id}`} className={classes.textDecorationNone}>
<Card className={classes.root}>
<CardMedia className={classes.mediaBoxStyle} title={project.title}>
<Tooltip
title={getPublishTypeLabel(project.publish.type)}
placement="right-start"
arrow
>
<Box className={classes.publishStyle}>
{project.publish.type === publish_type.Draft
? t('project.publish.draft')
: ''}
{project.publish.type === publish_type.Preview
? t('project.publish.preview')
: ''}
{project.publish.type ===
publish_type['Authenticated Creators'] ? (
<LockIcon />
) : (
''
)}
{project.publish.type === publish_type.Public ? (
<PublicIcon />
) : (
''
)}
</Box>
</Tooltip>
{project.video ? (
<>
<img
className={classes.mediaImageStyle}
src={buildVideoThumbnailURL(project.video)}
alt={project.title}
/>
<img className={classes.playIconStyle} src={playIcon} alt="" />
</>
) : project.images.length > 0 ? (
<img
className={classes.mediaImageStyle}
src={project.images[0].image_url}
alt={project.title}
/>
) : null}
</CardMedia>
<CardActionArea className={classes.actionAreaStyle}>
<CardContent
className={clsx(classes.contentStyle, classes.positionRelative)}
>
<Fab
className={classes.fabButtonStyle}
size="small"
aria-label="save button"
onClick={(e, id = project.id) => toggleSave(e, id, props)}
>
{project.saved_by.includes(props.auth.id) ? (
<BookmarkIcon aria-label="unsave" />
) : (
<BookmarkBorderIcon aria-label="save" />
)}
</Fab>
<Fab
className={clsx(classes.fabButtonStyle, classes.likeButtonStyle)}
size="small"
aria-label="like button"
variant="extended"
onClick={(e, id = project.id) => toggleLike(e, id, props)}
>
{project.likes.includes(props.auth.id) ? (
<ClapIcon arial-label="unlike" />
) : (
<ClapBorderIcon arial-label="like" />
)}
{nFormatter(project.likes.length)}
</Fab>
<Typography
className={classes.titleStyle}
variant="h5"
component="h2"
>
{project.title}
</Typography>
<Box className={classes.descriptionStyle}>
<Typography
variant="subtitle2"
color="textSecondary"
component="p"
>
{formatProjectDescription(project.description)}
</Typography>
</Box>
<Link
to={`/creators/${project.creator.username}`}
className={classes.textDecorationNone}
>
<Box className={classes.creatorBoxStyle}>
<Avatar
className={classes.creatorAvatarStyle}
src={project.creator.avatar}
alt={project.creator.username}
/>
<Typography
color="textSecondary"
variant="caption"
component="p"
>
{project.creator.username}
</Typography>
<Link
className={common_classes.textDecorationNone}
to={`/search/?q=${project.creator.tags[0]}&tab=creators`}
>
<Typography
className={clsx(common_classes.baseTagStyle, {
[common_classes.extendedTagStyle]: !isBaseTag(
project.creator.tags[0],
),
})}
component="h2"
>
{project.creator.tags[0]}
</Typography>
</Link>
</Box>
</Link>
<Box className={classes.captionStyle}>
<Box className={classes.captionStyle}>
<Typography
className={clsx(
classes.captionIconStyle,
classes.VisibilityIconStyle,
)}
color="textSecondary"
variant="caption"
component="span"
>
<VisibilityIcon /> {project.views_count}
</Typography>
<Typography
className={classes.captionIconStyle}
color="textSecondary"
variant="caption"
component="span"
>
<CommentIcon /> {project.comments_count}
</Typography>
</Box>
<Typography
color="textSecondary"
variant="caption"
component="span"
>
{`${dFormatter(project.created_on).value} ${t(
`date.${dFormatter(project.created_on).key}`,
)} ${t('date.ago')}`}
</Typography>
</Box>
</CardContent>
</CardActionArea>
</Card>
</Link>
);
}
Example #16
Source File: ChatMenu.js From dipact with GNU General Public License v3.0 | 4 votes |
render() {
return (
<div
style={{
position: "relative",
height: "100%",
}}
>
<Slide
direction="up"
in={!!this.state.activeChannel}
mountOnEnter
unmountOnExit
>
<div
style={{
top: 0,
left: 0,
bottom: 0,
right: 0,
background: "#ffffff",
position: "absolute",
zIndex: 1200,
}}
>
<ChatChannel
onNewGameState={this.props.onNewGameState}
gameState={this.props.gameState}
game={this.props.game}
phases={this.props.phases}
isActive={this.props.isActive}
createMessageLink={this.state.createMessageLink}
channel={this.state.activeChannel}
close={this.closeChannel}
loaded={(_) => {
this.loadChannels(true);
}}
parent={this}
/>
</div>
</Slide>
{this.state.channels && this.state.channels.length > 0 ? (
""
) : (
<Typography>No chat channels currently.</Typography>
)}
<ButtonGroup
orientation="vertical"
style={{
width: "100%",
height: "100%",
transform: "translateZ(0)",
WebkitTransform: "translateZ(0)",
overflowY: !!this.state.activeChannel
? "hidden"
: "scroll",
}}
>
{this.state.channels.map((channel) => {
return (
<Button
style={{
width: "100%",
justifyContent: "left",
paddingTop: "12px",
paddingBottom: "12px",
border: "none",
borderBottom: "1px solid rgb(40,26,26,0.1)",
borderRadius: "0px",
}}
onClick={(_) => {
this.openChannel(channel);
}}
key={channel.Properties.Members.join(",")}
>
{this.member &&
channel.Properties.NMessagesSince &&
channel.Properties.NMessagesSince.NMessages >
0 ? (
<Badge
badgeContent={
channel.Properties.NMessagesSince
.NMessages
}
overlap="circle"
color="primary"
>
{this.variant.Properties.Nations
.length ===
channel.Properties.Members.length ? (
<NationAvatarGroup
game={this.props.game}
newGameState={
this.props.newGameState
}
gameState={this.props.gameState}
variant={this.variant}
nations={
channel.Properties.Members
}
/>
) : (
<NationAvatarGroup
game={this.props.game}
newGameState={
this.props.newGameState
}
gameState={this.props.gameState}
variant={this.variant}
nations={channel.Properties.Members.filter(
(n) => {
return (
!this.member ||
n !==
this.member
.Nation
);
}
)}
/>
)}
</Badge>
) : this.variant.Properties.Nations.length ===
channel.Properties.Members.length ? (
<NationAvatarGroup
game={this.props.game}
newGameState={this.props.newGameState}
gameState={this.props.gameState}
variant={this.variant}
nations={channel.Properties.Members}
/>
) : (
<NationAvatarGroup
game={this.props.game}
newGameState={this.props.newGameState}
gameState={this.props.gameState}
variant={this.variant}
nations={channel.Properties.Members.filter(
(n) => {
return (
!this.member ||
n !== this.member.Nation
);
}
)}
/>
)}
{channel.Properties.NMessages &&
channel.Properties.LatestMessage ? (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
marginLeft: "8px",
minWidth: "0",
}}
>
<Typography
variant="body1"
style={{
textTransform: "none",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
width: "100%",
textAlign: "left",
}}
>
{channel.Properties.Members
.length ===
this.variant.Properties.Nations
.length
? "Everyone"
: channel.Properties.Members.filter(
(n) => {
return (
!this.member ||
n !==
this.member
.Nation
);
}
).map((n, i) => {
if (i === 0) {
return n;
} else {
return ", " + n;
}
})}{" "}
({channel.Properties.NMessages})
</Typography>
<Typography
variant="body2"
style={{
textTransform: "none",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
width: "100%",
textAlign: "left",
}}
>
{this.member ? (
this.member.Nation ===
channel.Properties.LatestMessage
.Sender ? (
<span
style={{
fontStyle: "italic",
}}
>
You:{" "}
</span>
) : channel.Properties.Members
.length > 2 ? (
channel.Properties
.LatestMessage.Sender +
": "
) : (
""
)
) : (
channel.Properties.LatestMessage
.Sender + ": "
)}
{
channel.Properties.LatestMessage
.Body
}
</Typography>
</div>
) : (
""
)}
</Button>
);
})}
</ButtonGroup>
{this.state.createMessageLink ? (
<React.Fragment>
<Fab
style={{
margin: "0px",
top: "auto",
right: "20px",
bottom: "20px",
left: "auto",
position: "fixed",
display: !!this.state.activeChannel
? "none"
: "flex",
}}
color="secondary"
aria-label="edit"
onClick={(_) => {
this.createChannelDialog.setState({
open: true,
});
}}
>
<CreateMessageIcon />
</Fab>
<CreateChannelDialog
game={this.props.game}
createChannel={(channel) => {
const newChannels = this.state.channels;
const oldChannel = newChannels.find((ch) => {
return helpers.deepEqual(
channel.Properties.Members,
ch.Properties.Members
);
});
if (!oldChannel) {
newChannels.push(channel);
}
const channelToUse = oldChannel || channel;
this.setState(
{
channels: newChannels,
},
(_) => {
gtag("event", "create_chat_channel");
this.openChannel(channelToUse);
}
);
}}
parentCB={(c) => {
this.createChannelDialog = c;
}}
/>
</React.Fragment>
) : (
""
)}
</div>
);
}
Example #17
Source File: Maps.js From KEDS-Rideshare-KUHacks with MIT License | 4 votes |
Maps = compose(
withProps({
googleMapURL:
"https://maps.googleapis.com/maps/api/js?key=AIzaSyAUxSCFAa8dpHXlqjdMlRRvuQm1rbUUP7A&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `580px` }} />,
mapElement: <div style={{ height: `100%` }} />
}),
withScriptjs,
withGoogleMap,
lifecycle({
componentDidMount() {
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route(
{
origin: new google.maps.LatLng(19.1217707406339, 72.83944134193857),
destination: new google.maps.LatLng(19.17060002212831, 72.79517092313263),
travelMode: google.maps.TravelMode.DRIVING
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result,
distance: result.routes[ 0 ].legs[ 0 ].distance.text,
time: result.routes[ 0 ].legs[ 0 ].duration.text,
});
} else {
console.error(`error fetching directions ${result}`);
}
}
);
},
componentWillMount() {
const refs = {}
this.setState({
bounds: null,
source: { lat: 19.1217707406339, lng: 72.83944134193857 },
destination: { lat: 19.17060002212831, lng: 72.79517092313263 },
center: {
lat: 41.9, lng: -87.624
},
markers: [],
onMapMounted: ref => {
refs.map = ref;
},
onBoundsChanged: () => {
this.setState({
bounds: refs.map.getBounds(),
center: refs.map.getCenter(),
})
},
onSourceBoxMounted: ref => {
refs.sourceBox = ref;
},
onDestinationBoxMounted: ref => {
refs.destinationBox = ref;
},
onSourcePlacesChanged: () => {
const places = refs.sourceBox.getPlaces();
const bounds = new google.maps.LatLngBounds();
places.forEach(place => {
if (place.geometry.viewport) {
bounds.union(place.geometry.viewport)
} else {
bounds.extend(place.geometry.location)
}
});
const nextMarkers = places.map(place => ({
position: place.geometry.location,
}));
const nextCenter = _.get(nextMarkers, '0.position', this.state.center);
let s = { lat: nextCenter.lat(), lng: nextCenter.lng() };
this.setState({
center: nextCenter,
markers: nextMarkers,
source: s
});
refs.map.fitBounds(bounds);
},
onDestinationPlacesChanged: () => {
console.log(refs);
const places = refs.destinationBox.getPlaces();
const bounds = new google.maps.LatLngBounds();
places.forEach(place => {
if (place.geometry.viewport) {
bounds.union(place.geometry.viewport)
} else {
bounds.extend(place.geometry.location)
}
});
const nextMarkers = places.map(place => ({
position: place.geometry.location,
}));
const nextCenter = _.get(nextMarkers, '0.position', this.state.center);
console.log(nextCenter);
let d = { lat: nextCenter.lat(), lng: nextCenter.lng() };
this.state.getRoute(this.state.source, d);
this.setState({
center: nextCenter,
markers: nextMarkers,
destination: d,
});
refs.map.fitBounds(bounds);
},
getRoute: (source, destination) => {
console.log('Source', source.lat, source.lng);
console.log('Destination', destination.lat, destination.lng);
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route(
{
origin: new google.maps.LatLng(source),
destination: new google.maps.LatLng(destination),
travelMode: google.maps.TravelMode.DRIVING
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
console.log(result);
localStorage.setItem('sourceLat', source.lat);
localStorage.setItem('sourceLng', source.lng);
localStorage.setItem('destinationLat', destination.lat);
localStorage.setItem('destinationLng', destination.lng)
localStorage.setItem('distance', result.routes[ 0 ].legs[ 0 ].distance.text);
localStorage.setItem('time', result.routes[ 0 ].legs[ 0 ].duration.text);
this.setState({
directions: result,
distance: result.routes[ 0 ].legs[ 0 ].distance.text,
time: result.routes[ 0 ].legs[ 0 ].duration.text,
});
} else {
console.error(`error fetching directions ${result}`);
}
}
);
}
});
},
})
)(props => (
<GoogleMap defaultZoom={8} ref={props.onMapMounted} defaultCenter={{ lat: 72.77587292177071, lng: 18.89286755846978 }}>
<SearchBox
ref={props.onSourceBoxMounted}
bounds={props.bounds}
controlPosition={google.maps.ControlPosition.TOP_LEFT}
onPlacesChanged={props.onSourcePlacesChanged}
>
<input
type="text"
placeholder="Enter Source"
style={{
boxSizing: `border-box`,
border: `1px solid transparent`,
width: `240px`,
height: `32px`,
marginTop: `27px`,
padding: `0 12px`,
borderRadius: `3px`,
boxShadow: `0 2px 6px rgba(0, 0, 0, 0.3)`,
fontSize: `14px`,
outline: `none`,
textOverflow: `ellipses`,
}}
/>
</SearchBox>
<SearchBox
ref={props.onDestinationBoxMounted}
bounds={props.bounds}
controlPosition={google.maps.ControlPosition.TOP_LEFT}
onPlacesChanged={props.onDestinationPlacesChanged}
>
<input
type="text"
placeholder="Enter Destination"
style={{
boxSizing: `border-box`,
border: `1px solid transparent`,
width: `240px`,
height: `32px`,
marginTop: `27px`,
marginLeft: '5px',
padding: `0 12px`,
borderRadius: `3px`,
boxShadow: `0 2px 6px rgba(0, 0, 0, 0.3)`,
fontSize: `14px`,
outline: `none`,
textOverflow: `ellipses`,
}}
/>
</SearchBox>
<Fab variant="extended"
color="secondary"
href="/admin/steps"
style={{
position: "absolute",
right: "55px",
bottom: "25px"
}}
>
<LocalTaxi />
Ride Now
</Fab>
{props.directions && <DirectionsRenderer directions={props.directions} />}
</GoogleMap>
))
Example #18
Source File: HeaderView.js From react-code-splitting-2021-04-26 with MIT License | 4 votes |
Header = ({ classes, isSidebarOpened, toggleSidebar, ...props }) => (
<AppBar position="fixed" className={classes.appBar}>
<Toolbar className={classes.toolbar}>
<IconButton
color="inherit"
onClick={toggleSidebar}
className={classNames(
classes.headerMenuButton,
classes.headerMenuButtonCollapse
)}
>
{isSidebarOpened ? (
<ArrowBackIcon
classes={{
root: classNames(classes.headerIcon, classes.headerIconCollapse)
}}
/>
) : (
<MenuIcon
classes={{
root: classNames(classes.headerIcon, classes.headerIconCollapse)
}}
/>
)}
</IconButton>
<Typography variant="h6" weight="medium" className={classes.logotype}>React Material Admin</Typography>
<div className={classes.grow} />
<div
className={classNames(classes.search, {
[classes.searchFocused]: props.isSearchOpen
})}
>
<div
className={classNames(classes.searchIcon, {
[classes.searchIconOpened]: props.isSearchOpen
})}
onClick={props.toggleSearch}
>
<SearchIcon classes={{ root: classes.headerIcon }} />
</div>
<InputBase
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput
}}
/>
</div>
<IconButton
color="inherit"
aria-haspopup="true"
aria-controls="mail-menu"
onClick={props.openNotificationsMenu}
className={classes.headerMenuButton}
>
<Badge
badgeContent={
props.isNotificationsUnread ? notifications.length : null
}
colortheme="warning"
>
<NotificationsIcon classes={{ root: classes.headerIcon }} />
</Badge>
</IconButton>
<IconButton
color="inherit"
aria-haspopup="true"
aria-controls="mail-menu"
onClick={props.openMailMenu}
className={classes.headerMenuButton}
>
<Badge
badgeContent={props.isMailsUnread ? messages.length : null}
color="secondary"
>
<MailIcon classes={{ root: classes.headerIcon }} />
</Badge>
</IconButton>
<IconButton
aria-haspopup="true"
color="inherit"
className={classes.headerMenuButton}
aria-controls="profile-menu"
onClick={props.openProfileMenu}
>
<AccountIcon classes={{ root: classes.headerIcon }} />
</IconButton>
<Menu
id="mail-menu"
open={Boolean(props.mailMenu)}
anchorEl={props.mailMenu}
onClose={props.closeMailMenu}
MenuListProps={{ className: classes.headerMenuList }}
className={classes.headerMenu}
classes={{ paper: classes.profileMenu }}
disableAutoFocusItem
>
<div className={classes.profileMenuUser}>
<Typography variant="h4" weight="medium">
New Messages
</Typography>
<Typography
className={classes.profileMenuLink}
component="a"
color="secondary"
>
{messages.length} New Messages
</Typography>
</div>
{messages.map(message => (
<MenuItem key={message.id} className={classes.messageNotification}>
<div className={classes.messageNotificationSide}>
<UserAvatar color={message.variant} name={message.name} />
<Typography size="sm" color="textSecondary">
{message.time}
</Typography>
</div>
<div
className={classNames(
classes.messageNotificationSide,
classes.messageNotificationBodySide
)}
>
<Typography weight="medium" gutterBottom>
{message.name}
</Typography>
<Typography color="textSecondary">{message.message}</Typography>
</div>
</MenuItem>
))}
<Fab
variant="extended"
color="primary"
aria-label="Add"
className={classes.sendMessageButton}
>
Send New Message
<SendIcon className={classes.sendButtonIcon} />
</Fab>
</Menu>
<Menu
id="notifications-menu"
open={Boolean(props.notificationsMenu)}
anchorEl={props.notificationsMenu}
onClose={props.closeNotificationsMenu}
className={classes.headerMenu}
disableAutoFocusItem
>
{notifications.map(notification => (
<MenuItem
key={notification.id}
onClick={props.closeNotificationsMenu}
className={classes.headerMenuItem}
>
<Notification {...notification} typographyVariant="inherit" />
</MenuItem>
))}
</Menu>
<Menu
id="profile-menu"
open={Boolean(props.profileMenu)}
anchorEl={props.profileMenu}
onClose={props.closeProfileMenu}
className={classes.headerMenu}
classes={{ paper: classes.profileMenu }}
disableAutoFocusItem
>
<div className={classes.profileMenuUser}>
<Typography variant="h4" weight="medium">
John Smith
</Typography>
<Typography
className={classes.profileMenuLink}
component="a"
color="primary"
href="https://flatlogic.com"
>
Flalogic.com
</Typography>
</div>
<MenuItem
className={classNames(
classes.profileMenuItem,
classes.headerMenuItem
)}
>
<AccountIcon className={classes.profileMenuIcon} /> Profile
</MenuItem>
<MenuItem
className={classNames(
classes.profileMenuItem,
classes.headerMenuItem
)}
>
<AccountIcon className={classes.profileMenuIcon} /> Tasks
</MenuItem>
<MenuItem
className={classNames(
classes.profileMenuItem,
classes.headerMenuItem
)}
>
<AccountIcon className={classes.profileMenuIcon} /> Messages
</MenuItem>
<div className={classes.profileMenuUser}>
<Typography
className={classes.profileMenuLink}
color="primary"
onClick={props.signOut}
>
Sign Out
</Typography>
</div>
</Menu>
</Toolbar>
</AppBar>
)
Example #19
Source File: Header.js From react-code-splitting-2021-04-26 with MIT License | 4 votes |
export default function Header(props) {
var classes = useStyles();
// global
var layoutState = useLayoutState();
var layoutDispatch = useLayoutDispatch();
var userDispatch = useUserDispatch();
// local
var [mailMenu, setMailMenu] = useState(null);
var [isMailsUnread, setIsMailsUnread] = useState(true);
var [notificationsMenu, setNotificationsMenu] = useState(null);
var [isNotificationsUnread, setIsNotificationsUnread] = useState(true);
var [profileMenu, setProfileMenu] = useState(null);
var [isSearchOpen, setSearchOpen] = useState(false);
return (
<AppBar position="fixed" className={classes.appBar}>
<Toolbar className={classes.toolbar}>
<IconButton
color="inherit"
onClick={() => toggleSidebar(layoutDispatch)}
className={classNames(
classes.headerMenuButtonSandwich,
classes.headerMenuButtonCollapse,
)}
>
{layoutState.isSidebarOpened ? (
<ArrowBackIcon
classes={{
root: classNames(
classes.headerIcon,
classes.headerIconCollapse,
),
}}
/>
) : (
<MenuIcon
classes={{
root: classNames(
classes.headerIcon,
classes.headerIconCollapse,
),
}}
/>
)}
</IconButton>
<Typography variant="h6" weight="medium" className={classes.logotype}>
React Material Admin
</Typography>
<div className={classes.grow} />
<Button component={Link} href="https://flatlogic.com/templates/react-material-admin-full" variant={"outlined"} color={"secondary"} className={classes.purchaseBtn}>Unlock full version</Button>
<div
className={classNames(classes.search, {
[classes.searchFocused]: isSearchOpen,
})}
>
<div
className={classNames(classes.searchIcon, {
[classes.searchIconOpened]: isSearchOpen,
})}
onClick={() => setSearchOpen(!isSearchOpen)}
>
<SearchIcon classes={{ root: classes.headerIcon }} />
</div>
<InputBase
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
/>
</div>
<IconButton
color="inherit"
aria-haspopup="true"
aria-controls="mail-menu"
onClick={e => {
setNotificationsMenu(e.currentTarget);
setIsNotificationsUnread(false);
}}
className={classes.headerMenuButton}
>
<Badge
badgeContent={isNotificationsUnread ? notifications.length : null}
color="warning"
>
<NotificationsIcon classes={{ root: classes.headerIcon }} />
</Badge>
</IconButton>
<IconButton
color="inherit"
aria-haspopup="true"
aria-controls="mail-menu"
onClick={e => {
setMailMenu(e.currentTarget);
setIsMailsUnread(false);
}}
className={classes.headerMenuButton}
>
<Badge
badgeContent={isMailsUnread ? messages.length : null}
color="secondary"
>
<MailIcon classes={{ root: classes.headerIcon }} />
</Badge>
</IconButton>
<IconButton
aria-haspopup="true"
color="inherit"
className={classes.headerMenuButton}
aria-controls="profile-menu"
onClick={e => setProfileMenu(e.currentTarget)}
>
<AccountIcon classes={{ root: classes.headerIcon }} />
</IconButton>
<Menu
id="mail-menu"
open={Boolean(mailMenu)}
anchorEl={mailMenu}
onClose={() => setMailMenu(null)}
MenuListProps={{ className: classes.headerMenuList }}
className={classes.headerMenu}
classes={{ paper: classes.profileMenu }}
disableAutoFocusItem
>
<div className={classes.profileMenuUser}>
<Typography variant="h4" weight="medium">
New Messages
</Typography>
<Typography
className={classes.profileMenuLink}
component="a"
color="secondary"
>
{messages.length} New Messages
</Typography>
</div>
{messages.map(message => (
<MenuItem key={message.id} className={classes.messageNotification}>
<div className={classes.messageNotificationSide}>
<UserAvatar color={message.variant} name={message.name} />
<Typography size="sm" color="text" colorBrightness="secondary">
{message.time}
</Typography>
</div>
<div
className={classNames(
classes.messageNotificationSide,
classes.messageNotificationBodySide,
)}
>
<Typography weight="medium" gutterBottom>
{message.name}
</Typography>
<Typography color="text" colorBrightness="secondary">
{message.message}
</Typography>
</div>
</MenuItem>
))}
<Fab
variant="extended"
color="primary"
aria-label="Add"
className={classes.sendMessageButton}
>
Send New Message
<SendIcon className={classes.sendButtonIcon} />
</Fab>
</Menu>
<Menu
id="notifications-menu"
open={Boolean(notificationsMenu)}
anchorEl={notificationsMenu}
onClose={() => setNotificationsMenu(null)}
className={classes.headerMenu}
disableAutoFocusItem
>
{notifications.map(notification => (
<MenuItem
key={notification.id}
onClick={() => setNotificationsMenu(null)}
className={classes.headerMenuItem}
>
<Notification {...notification} typographyVariant="inherit" />
</MenuItem>
))}
</Menu>
<Menu
id="profile-menu"
open={Boolean(profileMenu)}
anchorEl={profileMenu}
onClose={() => setProfileMenu(null)}
className={classes.headerMenu}
classes={{ paper: classes.profileMenu }}
disableAutoFocusItem
>
<div className={classes.profileMenuUser}>
<Typography variant="h4" weight="medium">
John Smith
</Typography>
<Typography
className={classes.profileMenuLink}
component="a"
color="primary"
href="https://flatlogic.com"
>
Flalogic.com
</Typography>
</div>
<MenuItem
className={classNames(
classes.profileMenuItem,
classes.headerMenuItem,
)}
>
<AccountIcon className={classes.profileMenuIcon} /> Profile
</MenuItem>
<MenuItem
className={classNames(
classes.profileMenuItem,
classes.headerMenuItem,
)}
>
<AccountIcon className={classes.profileMenuIcon} /> Tasks
</MenuItem>
<MenuItem
className={classNames(
classes.profileMenuItem,
classes.headerMenuItem,
)}
>
<AccountIcon className={classes.profileMenuIcon} /> Messages
</MenuItem>
<div className={classes.profileMenuUser}>
<Typography
className={classes.profileMenuLink}
color="primary"
onClick={() => signOut(userDispatch, props.history)}
>
Sign Out
</Typography>
</div>
</Menu>
</Toolbar>
</AppBar>
);
}
Example #20
Source File: Admin.js From FireShort with MIT License | 4 votes |
render() {
const { classes } = this.props;
const { inputBackdrop, newPsw } = this.state;
return (
<React.Fragment>
{inputBackdrop ? (
<div
style={{
position: 'fixed',
width: '100vw',
height: '100vh',
background: 'rgb(0,0,0,.5)',
display: 'grid',
placeItems: 'center',
zIndex: 10,
}}
>
<div style={{ padding: "20px", backgroundColor: "white" }}>
<h3>Protect Link With Password</h3>
<div style={{ display: "block", padding: "20px" }}>
<input
placeholder='Enter Password...'
value={newPsw}
style={{ padding: "15px", fontSize: "15px", borderRadius: "2px", width: "100%" }}
onChange={(e) => this.setState({ newPsw: e.target.value })}
/>
<div style={{ marginTop: "25px" }}>
<button onClick={(e) => this.onPswSave(e)} style={{ padding: "12px", color: "white", backgroundColor: "black", fontSize: "15px", border: "none", marginRight: "15px", borderRadius: "5px", cursor: "pointer" }}>Save</button>
<button onClick={(e) => this.setState({ inputBackdrop: false })} style={{ padding: "12px", color: "white", backgroundColor: "red", fontSize: "15px", border: "none", marginRight: "15px", borderRadius: "5px", cursor: "pointer" }}>Cancel</button>
</div>
</div>
</div>
</div>
) : null}
<CssBaseline />
<Header />
{this.state.loading && <LinearProgress color='secondary' />}
<main>
<MainToolBar
state={this.state}
updateViewMode={this.updateViewMode}
refresh={this.updateUrls}
/>
{this.state.shortUrls.length > 0 ? (
<>
{this.state.viewMode === 'module' ? (
<CardUrls
shortUrls={this.props.links}
handleEditShortUrl={this.handleEditShortUrl}
handleDeleteShortUrl={this.handleDeleteShortUrl}
openHits={this.getHits}
// updateHits={this.updateUrls}
toggleSecurity={this.toggleSecurity}
/>
) : (
<ListUrls
shortUrls={this.state.shortUrls}
handleEditShortUrl={this.handleEditShortUrl}
handleDeleteShortUrl={this.handleDeleteShortUrl}
toggleSecurity={this.toggleSecurity}
openHits={this.getHits}
/>
)}
</>
) : (
<>
{this.state.loading == false &&
<Container maxWidth='md'>
<img src={'/Images/pixeltrue-search.svg'} style={{margin: "30px auto", display: "block", width: "100%", maxHeight: "400px" }} />
<Card className={classes.toolBarRoot} style={{marginBottom: "30px"}}>
<Typography align="center" style={{padding: "30px 60px"}} variant="h6">
Oops! Looks like you don't have any links. Press the "+" icon below to start adding links.
</Typography>
</Card>
</Container>
}
</>
)}
<Fab
aria-label='Add'
className={classes.fab}
color='primary'
onClick={this.handleClickOpen}
>
<AddIcon />
</Fab>
<Backdrop className={classes.backdrop} open={this.state.backdrop}>
<CircularProgress color='inherit' />
</Backdrop>
<UrlsDialog
state={this.state}
handleClose={this.handleClose}
handleLurlChange={this.handleLurlChange}
handleCurlChange={this.handleCurlChange}
handleSubmit={this.handleSubmit}
handleTrackChange={this.handleTrackChange}
handleProtectChange={this.handleProtectChange}
handlePswChange={this.handlePswChange}
/>
<HitsDialog
state={this.state}
hitActivity={this.state.hits}
handleClose={this.handleClose}
/>
<Snackbar
open={this.state.successToast}
autoHideDuration={6000}
onClose={this.handleToastClose}
>
<Alert onClose={this.handleToastClose} severity='success'>
Successfully added!
</Alert>
</Snackbar>
</main>
</React.Fragment>
);
}
Example #21
Source File: Dashboard.js From jobtriage with MIT License | 4 votes |
Dashboard = () => {
const showToast = useToast();
const showLoader = useLoader();
const classes = useStyles();
const history = useHistory();
const [applicationsData, setApplicationsData] = React.useState();
const [boardData, setBoardData] = useState(data);
const [openJobAdd, setOpenJobAdd] = React.useState(false);
const handleJobAddOpen = () => {
setOpenJobAdd(true);
};
const handleJobAddClose = () => {
setOpenJobAdd(false);
};
const getJobApplications = () => {
showLoader(true);
APIService.getJobApplications()
.then(resp => {
const appData = resp.data;
setApplicationsData(appData);
const parsedData = parseApplicationData(appData);
setBoardData(parsedData);
}).finally(() => showLoader(false));
};
useEffect(() => {
getJobApplications();
}, []);
const handleDrag = (id, source, target) => {
if (source !== target) {
APIService.updateApplicationStatus(id, target)
.catch(() => showToast('Error while deleting updating Job application', ToastConstants.ERROR));
}
};
const cardDelete = id => {
APIService.deleteApplication(id).catch(() => showToast('Error while deleting Job application', ToastConstants.ERROR));
};
const isEmptyBoard = () => {
return applicationsData.length === 0;
};
return (
<div>
{ applicationsData
? (
<div>
<Typography color="primary" variant="h5">
Dashboard
</Typography>
{ isEmptyBoard() && <EmptyBoard /> }
{!isEmptyBoard() && (
<Board
data={boardData}
style={{ backgroundColor: '#fff', height: 'auto' }}
handleDragEnd={handleDrag}
onCardDelete={cardDelete}
onCardClick={cardId => history.push(`/application/${cardId}`)}
laneStyle={{ backgroundColor: '#d9c8f5' }}
cardStyle={{ backgroundColor: '#ffe' }}
/>
)}
<Fab
color="primary"
aria-label="Add job"
className={classes.fab}
onClick={handleJobAddOpen}
>
<Add />
</Fab>
<AddJob
open={openJobAdd}
onClose={handleJobAddClose}
onChange={getJobApplications}
/>
</div>
)
: <HorizontalLoader />}
</div>
);
}
Example #22
Source File: Employee.js From inventory-management-web with MIT License | 4 votes |
export default function Employee() {
// list of employees got from API
const [employeeList, setEmployeeList] = useState([]);
// contains the index of the row, if delete is used
const [deletedRow, setDeletedRow] = useState([]);
// true when waiting for an response from API
const [isLoading, setIsLoading] = useState(false);
// dialog box
const [open, setOpen] = useState(false);
// row to be selected on clicking the delete icon
const [selectedRow, setSelectedRow] = useState({});
const history = useHistory();
const { setSnack } = useContext(SnackContext);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const apiFetch = async () => {
try {
setIsLoading(true);
const response = await getEndPoint('/auth/users/', null, history);
const { data } = response;
// map genders got from API
const genderMapper = { M: 'Male', F: 'Female', Other: 'Other' };
const list = data.map(val => ({
firstName: val.first_name,
lastName: val.last_name,
name: `${val.first_name} ${val.last_name}`,
age: val.age,
gender: genderMapper[val.gender],
email: val.email,
isStaff: val.is_staff,
}));
setEmployeeList(list);
setIsLoading(false);
} catch (e) {
console.log(e);
}
};
// call API on component load
useEffect(() => {
apiFetch();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const classes = useStyles();
// handle click on the FAB
const handleFabClick = () => {
history.push('/addemployee');
};
// handle user edit
const handleEdit = row => {
console.log(row);
// open the create user form and pass the data as props
history.push('/updateemployee', {
firstName: row.firstName,
lastName: row.lastName,
age: row.age,
isStaff: row.isStaff,
email: row.email,
});
};
// handle user delete
const handleDelete = async row => {
setIsLoading(true);
const { email, name } = row;
setDeletedRow(prevState => [...prevState, employeeList.indexOf(row)]);
try {
const formData = new FormData();
formData.append('email', email);
await postEndPoint('/auth/user_delete/', formData, null, history);
setIsLoading(false);
// add success snackbar on successful request
setSnack({
open: true,
message: `Succesfully deleted ${name}`,
action: '',
actionParams: '',
type: 'success',
});
} catch (e) {
console.log(e);
}
};
return (
<>
{isLoading ? <Spinner /> : null}
<Typography variant='h3' className={classes.heading}>
Employees
</Typography>
<Paper className={classes.paper}>
<TableContainer>
<Table className={classes.table} aria-label='simple table'>
<TableHead>
<TableRow>
<TableCell />
<TableCell>Name</TableCell>
<TableCell>Gender</TableCell>
<TableCell>Email</TableCell>
<TableCell align='right'>Age</TableCell>
</TableRow>
</TableHead>
<TableBody>
{employeeList.map((row, index) => (
<TableRow
key={row.email}
hover
className={deletedRow.includes(index) ? 'delete' : ''}
>
<TableCell className={classes.firstColumn}>
<Hidden xsDown>
<IconButton
onClick={() => {
handleEdit(row);
}}
>
<EditIcon />
</IconButton>
<IconButton
onClick={() => {
setSelectedRow(row);
handleClickOpen();
}}
>
<DeleteIcon />
</IconButton>
</Hidden>
<Hidden smUp>
<MobileEditMenu
handleDelete={() => {
setSelectedRow(row);
handleClickOpen();
}}
handleEdit={handleEdit}
row={row}
/>
</Hidden>
</TableCell>
<TableCell>{row.name}</TableCell>
<TableCell>{row.gender}</TableCell>
<TableCell>{row.email}</TableCell>
<TableCell align='right'>{row.age}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Paper>
<DialogBox
open={open}
handleClose={handleClose}
selectedRow={selectedRow}
handleDelete={handleDelete}
number='1'
/>
<Fab
color='primary'
aria-label='add'
className={classes.fab}
onClick={handleFabClick}
>
<AddIcon />
</Fab>
</>
);
}
Example #23
Source File: SetScaleModal.js From floor-plan-lab with MIT License | 4 votes |
function SetScaleModal() {
const classes = useStyles();
const dispatch = useDispatch();
const [scaleModalOpen, setScaleModalOpen] = React.useState(false);
const [feetInput, setFeetInput] = React.useState(1);
const [inchesInput, setInchesInput] = React.useState(0);
const scale = useSelector(state => state.sheet.scale);
const handleClose = () => {
setScaleModalOpen(false);
};
const toggleModal = () => {
setScaleModalOpen(!scaleModalOpen);
setFeetInput(scale.ft);
setInchesInput(scale.in);
}
const handleFeetInput = (value) => {
setFeetInput(value);
}
const handleInchesInput = (value) => {
setInchesInput(value);
}
const onSave = () => {
const feet = parseInt(feetInput);
const inches = parseInt(inchesInput);
if (feet > 0 || inches > 0) {
// Valid number entered
dispatch(setScale({ ft: feet, in: inches }));
setScaleModalOpen(false);
} else {
// Error
}
}
return (
<>
<Tooltip title={<span className={classes.toolTip}>Change Scale</span>} placement='top' arrow>
<Fab onClick={toggleModal} variant="extended" size='small' className={classes.fab} style={{ paddingLeft: 16, paddingRight: 16, }}>
<span className="far fa-square" style={{ paddingRight: 8 }}></span>
= {scale.ft}'{scale.in}"
</Fab>
</Tooltip>
<Modal
open={scaleModalOpen}
onClose={handleClose}
aria-labelledby="change-scale"
>
<Paper className={classes.paper}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant='h6'>
Grid Scale
</Typography>
</Grid>
<Grid item xs={12}>
<Grid container>
<Grid item>
<img src={boxSideLengthImg} className={classes.img} />
</Grid>
<Grid item>
<Grid container className={classes.inputContainer}>
<Grid item>
<NumericInput strict min={0} max={50} size={3} value={feetInput} onChange={(e) => handleFeetInput(e)} />
</Grid>
<Grid item xs>
<Typography style={{ paddingLeft: 4 }}>Feet</Typography>
</Grid>
</Grid>
</Grid>
<Grid item>
<Grid container className={classes.inputContainer}>
<Grid item>
<NumericInput strict min={0} max={11} size={3} value={inchesInput} onChange={(e) => handleInchesInput(e)} />
</Grid>
<Grid item xs>
<Typography style={{ paddingLeft: 4 }}>Inches</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Typography variant='caption' style={{ color: 'red' }}>
Note: Changing the scale will resize objects but not walls.
</Typography>
</Grid>
<Grid item xs={6}>
<Button fullWidth variant='contained' color='primary' onClick={onSave}>
Save
</Button>
</Grid>
<Grid item xs={6}>
<Button fullWidth variant='contained' onClick={handleClose}>
Cancel
</Button>
</Grid>
</Grid>
</Paper>
</Modal>
</>
);
}
Example #24
Source File: resources.js From covid19Nepal-react with MIT License | 4 votes |
function Resources(props) {
const [data, setData] = useState([]);
const [partData, setPartData] = useState([]);
const [ogData, setOgData] = useState([]);
const [fetched, setFetched] = useState(false);
const [city, setCity] = useState('all');
const [category, setCategory] = useState('all');
const [nepalstate, setNepalState] = useState('all');
const [resourcedict, setResourceDict] = useState({});
const [showTable, setShowTable] = useState(false);
const [isDesktop, setIsDesktop] = useState(false);
const [hasScrolled, setHasScrolled] = useState(false);
const [anchorEl, setAnchorEl] = React.useState(null);
const [searchValue, setSearchValue] = useState('');
useEffect(() => {
if (fetched === false) {
getResources();
}
}, [fetched, data, resourcedict]);
const checkForResizeEvent = useCallback((event) => {
if (window.innerWidth > 639) setIsDesktop(true);
else setIsDesktop(false);
}, []);
useEffect(() => {
if (window.innerWidth > 639) setIsDesktop(true);
else setIsDesktop(false);
window.addEventListener('resize', checkForResizeEvent);
return () => {
window.removeEventListener('resize', checkForResizeEvent);
};
}, [isDesktop, checkForResizeEvent]);
const checkScrollEvent = useCallback((event) => {
window.pageYOffset > 100 ? setHasScrolled(true) : setHasScrolled(false);
}, []);
useEffect(() => {
window.pageYOffset > 100 ? setHasScrolled(true) : setHasScrolled(false);
window.addEventListener('scroll', checkScrollEvent);
return () => {
window.removeEventListener('scroll', checkScrollEvent);
};
}, [hasScrolled, checkScrollEvent]);
const getResources = async () => {
try {
const [response] = await Promise.all([
axios.get('https://api.nepalcovid19.org/resources/resources.json'),
]);
// setData(response.data.resources);
const hashmap = {};
response.data.resources.forEach((x) => {
if (typeof hashmap[x['state']] === 'undefined')
hashmap[x['state']] = {};
if (typeof hashmap[x['state']][x['city']] === 'undefined')
hashmap[x['state']][x['city']] = {};
if (
typeof hashmap[x['state']][x['city']][x['category']] === 'undefined'
)
hashmap[x['state']][x['city']][x['category']] = [];
if (Array.isArray(hashmap[x['state']][x['city']][x['category']]))
hashmap[x['state']][x['city']][x['category']].push(x);
});
setResourceDict(hashmap);
setFetched(true);
} catch (err) {}
};
const handleDisclaimerClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleDisclaimerClose = () => {
setAnchorEl(null);
};
const isDisclaimerOpen = Boolean(anchorEl);
const id = isDisclaimerOpen ? 'simple-popover' : undefined;
function animateScroll() {
document.body.scrollTo({top: 0, behavior: 'smooth'}); // For Safari
document.documentElement.scrollTo({top: 0, behavior: 'smooth'}); // For Chrome, Firefox, IE and Opera
}
const memocols = React.useMemo(
() => [
{
Header: 'City',
accessor: 'city',
},
{
Header: 'Category',
accessor: 'category',
},
{
Header: 'Organisation',
accessor: 'nameoftheorganisation',
},
{
Header: 'Description',
accessor: 'descriptionandorserviceprovided',
},
{
Header: 'Phone',
accessor: 'phonenumber',
},
{
Header: 'Source',
accessor: 'contact',
isVisible: false,
},
],
[]
);
// const memodata = React.useMemo(() => data, [data])
const getCityOptions = function () {
if (nepalstate) {
if (nepalstate === 'all') return [];
else {
return Object.keys(resourcedict[nepalstate])
.sort()
.map((x, i) => (
<option
key={i}
value={x}
style={{
fontFamily: 'archia',
fontSize: '11px !important',
fontWeight: 600,
textTransform: 'uppercase',
}}
>
{x}
</option>
));
}
} else return [];
// return getCityList().map((x) => <option value={x}>{x}</option>)
};
const getnepalstateOptions = function () {
// let defaultOption = ['Please select']
return Object.keys(resourcedict)
.sort()
.map((x, i) => (
<option
key={i}
value={x}
style={{
fontFamily: 'archia',
fontSize: '11px !important',
fontWeight: 600,
textTransform: 'uppercase',
}}
>
{x}
</option>
));
};
const getCategoryOptions = function () {
if (nepalstate && city) {
if (nepalstate === 'all') {
const array = [];
Object.values(resourcedict).forEach((state) => {
Object.values(state).forEach((citydata) => {
Object.keys(citydata).forEach((x) => {
if (array.indexOf(x) === -1) array.push(x);
});
});
});
return array.sort().map((x, i) => (
<option
key={i}
value={x}
style={{
fontFamily: 'archia',
fontSize: '11px !important',
fontWeight: 600,
textTransform: 'uppercase',
}}
>
{x}
</option>
));
} else {
if (city === 'all') {
const array = [];
Object.values(resourcedict[nepalstate]).forEach((citydata) => {
Object.keys(citydata).forEach((x) => {
if (array.indexOf(x) === -1) array.push(x);
});
});
return array.sort().map((x, i) => (
<option
key={i}
value={x}
style={{
fontFamily: 'archia',
fontSize: '11px !important',
fontWeight: 600,
textTransform: 'uppercase',
}}
>
{x}
</option>
));
} else {
return Object.keys(resourcedict[nepalstate][city])
.sort()
.map((x, i) => (
<option
key={i}
value={x}
style={{
fontFamily: 'archia',
fontSize: '11px !important',
fontWeight: 600,
textTransform: 'uppercase',
}}
>
{x}
</option>
));
}
}
} else return [];
};
const filterTable = function () {
let a = [];
if (category === 'all') {
if (city === 'all') {
if (nepalstate === 'all') {
Object.values(resourcedict).forEach((state) => {
Object.values(state).forEach((citydata) => {
Object.values(citydata).forEach((category) => {
category.forEach((x) => a.push(x));
});
});
});
} else {
Object.values(resourcedict[nepalstate]).forEach((citydata) => {
Object.values(citydata).forEach((category) => {
category.forEach((x) => a.push(x));
});
});
}
} else {
Object.values(resourcedict[nepalstate][city]).forEach((x) => {
x.forEach((y) => a.push(y));
});
}
} else {
if (nepalstate === 'all' && city === 'all') {
Object.values(resourcedict).forEach((state) => {
Object.values(state).forEach((citydata) => {
Object.values(citydata).forEach((categorydata) => {
categorydata.forEach((x) => {
if (x.category === category) a.push(x);
});
});
});
});
} else if (nepalstate !== 'all' && city === 'all') {
Object.values(resourcedict[nepalstate]).forEach((citydata) => {
if (category in citydata) {
citydata[category].forEach((x) => {
a.push(x);
});
}
});
} else {
a = resourcedict[nepalstate][city][category];
}
}
try {
if ('PAN Nepal' in resourcedict) {
resourcedict['PAN Nepal']['Multiple']['CoVID-19 Testing Lab'].forEach(
(element) => {
a.push(element);
}
);
}
} catch (err) {}
setData(a);
setOgData(a);
setPartData(a.slice(0, 30));
setShowTable(true);
try {
document.getElementById('input-field-searchbar').value = '';
} catch {}
setSearchValue('');
};
const changeNepalState = function (changedstateevent) {
setNepalState(changedstateevent.target.value);
// setCity(
// Object.keys(resourcedict[changedstateevent.target.value]).sort()[0]
// );
if (changedstateevent.target.value === '') {
setCity('');
document.getElementById('cityselect1').selectedIndex = 0;
setCategory('');
document.getElementById('categoryselect').selectedIndex = 0;
} else {
setCity('all');
document.getElementById('cityselect1').selectedIndex = 1;
setCategory('all');
document.getElementById('categoryselect').selectedIndex = 1;
}
};
const changeCity = function (changedcityevent) {
setCity(changedcityevent.target.value);
setCategory('all');
document.getElementById('categoryselect').selectedIndex = 1;
};
const changeCategory = function (changedcategoryevent) {
setCategory(changedcategoryevent.target.value);
};
const appendData = function () {
const tempArr = partData.concat(
data.slice(partData.length, partData.length + 30)
);
setPartData(tempArr);
};
const openSharingLink = function (message) {
const shareUri = `https://www.addtoany.com/share#url=${encodeURI(
'https://www.nepalcovid19.org/essentials'
)}&title=${encodeURI(message)}`;
const h = 500;
const w = 500;
const left = window.screen.width / 2 - w / 2;
const top = window.screen.height / 2 - h / 2;
return window.open(
shareUri,
document.title,
'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=' +
w +
', height=' +
h +
', top=' +
top +
', left=' +
left
);
};
const openSharingTray = function () {
const message =
'Discover nearest coronavirus support and essential service providers such as testing lab centres, accommodation shelters and vegetable vendors at ';
if (navigator.share !== undefined) {
navigator
.share({
title: document.title,
text: message,
url: 'https://www.nepalcovid19.org/essentials',
})
.then()
.catch((error) => {});
} else {
openSharingLink(message);
}
};
useDebounce(
() => {
const newData = getSuggestions(searchValue, ogData);
setData(newData);
setPartData(newData.slice(0, 30));
},
300,
[searchValue, ogData]
);
return (
<div className="Resources" id="top-elem">
<Helmet>
<title>Essentials - nepalcovid19.org</title>
<meta name="title" content="Essentials - nepalcovid19.org" />
</Helmet>
<div className="filtersection">
<div className="filtertitle">
<h3>Service Before Self</h3>
</div>
{!isDesktop && (
<FiltersMobile
handleDisclaimerClick={handleDisclaimerClick}
popoverid={id}
isDisclaimerOpen={isDisclaimerOpen}
anchorEl={anchorEl}
handleDisclaimerClose={handleDisclaimerClose}
nepalstate={nepalstate}
changeNepalState={changeNepalState}
stateoptions={getnepalstateOptions()}
city={city}
changeCity={changeCity}
cityoptions={getCityOptions()}
category={category}
changeCategory={changeCategory}
servicesoptions={getCategoryOptions()}
filterTable={filterTable}
openSharingTray={openSharingTray}
/>
)}
{isDesktop && (
<FiltersDesktop
handleDisclaimerClick={handleDisclaimerClick}
popoverid={id}
isDisclaimerOpen={isDisclaimerOpen}
anchorEl={anchorEl}
handleDisclaimerClose={handleDisclaimerClose}
nepalstate={nepalstate}
changeNepalState={changeNepalState}
stateoptions={getnepalstateOptions()}
city={city}
changeCity={changeCity}
cityoptions={getCityOptions()}
category={category}
changeCategory={changeCategory}
servicesoptions={getCategoryOptions()}
filterTable={filterTable}
openSharingTray={openSharingTray}
/>
)}
</div>
{showTable && (
<React.Fragment>
<div className="searchbar">
<TextField
id="input-field-searchbar"
label="Search keyword"
fullWidth={true}
InputLabelProps={{
shrink: true,
}}
style={{
width: '100%',
}}
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<Icon.Search size="0.9em" />
</InputAdornment>
),
}}
onChange={(event) => {
setInterval(setSearchValue(event.target.value));
}}
/>
</div>
<ResourceTable
columns={memocols}
data={partData}
totalCount={data.length}
isDesktop={isDesktop}
onScrollUpdate={appendData}
searchValue={searchValue}
/>
<div>
<Fade in={hasScrolled}>
<Fab
color="inherit"
aria-label="gototop"
id="gototopbtn"
onClick={animateScroll}
size="small"
style={{
position: 'fixed',
bottom: '1rem',
right: '1rem',
zIndex: '1000',
}}
>
<Icon.Navigation2 strokeWidth="2.5" color="#4c75f2" />
</Fab>
</Fade>
</div>
</React.Fragment>
)}
</div>
);
}
Example #25
Source File: PostFormModal.js From reddish with MIT License | 4 votes |
AddPostModal = ({
actionType,
handleMenuClose,
postToEditType,
postToEditTitle,
postToEditSub,
postToEditId,
textSubmission,
linkSubmission,
fromSubreddit,
}) => {
const [open, setOpen] = useState(false);
const [postType, setPostType] = useState('Text');
const user = useSelector((state) => state.user);
const classes = useDialogStyles();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('xs'));
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const handleTextPost = () => {
setPostType('Text');
handleClickOpen();
};
const handleImagePost = () => {
setPostType('Image');
handleClickOpen();
};
const handleLinkPost = () => {
setPostType('Link');
handleClickOpen();
};
const handleMenuClick = () => {
handleClickOpen();
handleMenuClose();
};
if (!user) {
return null;
}
return (
<div>
{actionType === 'edit' ? (
<MenuItem onClick={handleMenuClick}>
<ListItemIcon>
<EditIcon style={{ marginRight: 7 }} />
Edit Post
</ListItemIcon>
</MenuItem>
) : isMobile ? (
<HideOnScroll>
<Fab
className={classes.fab}
color="primary"
onClick={handleClickOpen}
>
<PostAddIcon />
</Fab>
</HideOnScroll>
) : (
<Paper variant="outlined" className={classes.createPostWrapper}>
{user.avatar && user.avatar.exists ? (
<Avatar
alt={user.username}
src={getCircularAvatar(user.avatar.imageLink)}
/>
) : (
<Avatar className={classes.defaultAvatar}>
{user.username[0]}
</Avatar>
)}
<Button
color="primary"
variant="outlined"
onClick={handleTextPost}
fullWidth
className={classes.createBtn}
startIcon={<PostAddIcon />}
size="large"
>
Create Post
</Button>
<div className={classes.iconGroup}>
<IconButton onClick={handleImagePost}>
<ImageIcon color="primary" />
</IconButton>
<IconButton onClick={handleLinkPost}>
<LinkIcon color="primary" />
</IconButton>
</div>
</Paper>
)}
<Dialog
open={open}
onClose={handleClose}
classes={{ paper: classes.dialogWrapper }}
maxWidth="md"
fullWidth={true}
>
<DialogTitle onClose={handleClose}>
{actionType === 'edit' ? 'Update your post' : 'Add a new post'}
</DialogTitle>
<DialogContent>
<PostForm
actionType={actionType}
postType={postType}
postToEditType={postToEditType}
postToEditTitle={postToEditTitle}
postToEditSub={postToEditSub}
postToEditId={postToEditId}
textSubmission={textSubmission}
linkSubmission={linkSubmission}
fromSubreddit={fromSubreddit}
/>
</DialogContent>
</Dialog>
</div>
);
}
Example #26
Source File: GuideViewer.jsx From archeage-tools with The Unlicense | 4 votes |
render() {
const { mobile, match: { params: { guide: guideSlug } } } = this.props;
const { toc } = this.state;
const guideData = Guides[unslug(guideSlug)];
if (!guideData) {
return <NotFound />;
}
setTitle(`${guideData.name} Guide`);
return (
<div className="guide-container">
<div className="section">
<div className="guide-viewer">
{guideData.sections.map((section, sId) => (
<React.Fragment key={`${slug(guideData.name)}-s${sId}`}>
<Paper id={`${slug(section.title)}`}>
{section.tabContent &&
<TabContent title={section.title} tabs={section.tabContent} />}
{!section.tabContent && section.paragraphs &&
<>
<AppBar position="static">
<Toolbar variant="dense">
<Typography variant="h5" className="title-text">{section.title}</Typography>
</Toolbar>
</AppBar>
<div className="body-container">
{section.paragraphs.map((line, pId) => {
const key = `${slug(guideData.name)}-s${sId}-p${pId}`;
if (typeof line === 'string') {
return <Typography key={key}>{line}</Typography>;
} else {
return <KeyComponent key={key}>{line}</KeyComponent>;
}
})}
</div>
</>}
</Paper>
{(sId + 1) % 3 === 0 &&
<AdContainer type="horizontal" />}
</React.Fragment>
),
)}
</div>
{!mobile &&
<Sticky holderProps={{ className: 'guide-toc' }}>
<Paper>
<AppBar position="static">
<Toolbar variant="dense">
<Typography variant="subtitle1" className="title-text">{guideData.name}</Typography>
</Toolbar>
</AppBar>
<div className="body-container">
<Typography variant="subtitle2">Author: {guideData.meta.author}</Typography>
<Typography variant="subtitle2">Last Updated: {guideData.meta.lastUpdated}</Typography>
<Typography variant="subtitle1">Table of Contents</Typography>
{guideData.sections.map((section, sId) => (
<Link
to={`#${slug(section.title)}`}
onClick={() => this.goSection(slug(section.title))}
color="primary"
key={`toc-${sId}`}
>
<Typography>{sId + 1}. {section.title}</Typography>
</Link>
))}
</div>
</Paper>
<AdContainer type="square" />
</Sticky>}
</div>
<ScrollToTop />
{mobile &&
<Fab
color="primary"
className="fab-left"
onClick={this.handleToCClick}
>
<MenuIcon />
</Fab>}
<Drawer anchor="left" open={mobile && toc} onClose={this.closeToC}>
<List style={{ width: 250 }}>
<ListItem><Typography variant="h6">{guideData.name}</Typography></ListItem>
<ListItem>
<Typography variant="subtitle2">
Author: {guideData.meta.author}<br />
Last Updated: {guideData.meta.lastUpdated}
</Typography>
</ListItem>
<hr />
{guideData.sections.map((section, sId) => (
<RouterLink
to={`#${slug(section.title)}`}
onClick={() => this.goSection(slug(section.title), 'auto')}
color="primary"
className="no-link"
key={`toc-drawer-${sId}`}
>
<ListItem button>
<ListItemText primary={`${sId + 1}. ${section.title}`} />
</ListItem>
</RouterLink>
))}
</List>
</Drawer>
</div>
);
}
Example #27
Source File: ImageScroller.js From treetracker-admin-client with GNU Affero General Public License v3.0 | 4 votes |
export default function ImageScroller(props) {
const {
images,
selectedImage,
loading = false,
blankMessage = '',
imageRotation,
onSelectChange,
} = props;
const classes = useStyle();
const [maxImages, setMaxImages] = useState(MAX_IMAGES_INCREMENT);
const imageScrollerRef = useRef(null);
let [rotation, setRotation] = useState(imageRotation);
function loadMoreImages() {
setMaxImages(maxImages + MAX_IMAGES_INCREMENT);
}
function scrollImagesLeft() {
scrollImages(-NUM_IMAGE_CARDS);
}
function scrollImagesRight() {
scrollImages(NUM_IMAGE_CARDS);
}
function scrollImages(numImages) {
const startPos =
Math.round(imageScrollerRef.current.scrollLeft / IMAGE_CARD_SIZE) *
IMAGE_CARD_SIZE;
imageScrollerRef.current.scrollTo({
top: 0,
left: startPos + numImages * IMAGE_CARD_SIZE,
behavior: 'smooth',
});
}
function handleRotationChange() {
let newRotation = rotation + 90;
if (newRotation > 270) {
newRotation = 0;
}
setRotation(newRotation);
onSelectChange('imageRotation', newRotation);
}
function handleImageChange(img) {
onSelectChange('imageUrl', img);
if (images.length > 1) {
setRotation(0);
}
}
return (
<div className={classes.container}>
<Grid
item
className={`image-list ${classes.imageList}`}
ref={imageScrollerRef}
>
{loading ? (
<div className={classes.placeholder}>
<CircularProgress id="loading" />
</div>
) : images.length ? (
images.slice(0, maxImages).map((img, idx) => (
<Card
key={`${idx}_${img}`}
className={`image-card ${classes.imageCard} ${
img === selectedImage && classes.selectedImageCard
}`}
>
<OptimizedImage
src={img}
width={IMAGE_CARD_SIZE}
height={IMAGE_CARD_SIZE}
className={classes.image}
fixed
rotation={img === selectedImage ? rotation : 0}
onClick={() => handleImageChange(img)}
/>
{img === selectedImage ? (
<Fab
id="click-rotate"
className={classes.clickRotate}
onClick={handleRotationChange}
>
<Rotate90DegreesCcwIcon
style={{ transform: `rotateY(180deg)` }}
/>
</Fab>
) : (
''
)}
</Card>
))
) : (
<div className={classes.placeholder}>{blankMessage}</div>
)}
{maxImages < images.length && (
<Button
id="load-more"
className={classes.loadMore}
onClick={loadMoreImages}
>
Load more
</Button>
)}
</Grid>
{images.length > NUM_IMAGE_CARDS && (
<>
<Fab
id="scroll-left"
className={`${classes.scrollButton} ${classes.scrollLeft}`}
onClick={scrollImagesLeft}
>
<ChevronLeft />
</Fab>
<Fab
id="scroll-right"
className={`${classes.scrollButton} ${classes.scrollRight}`}
onClick={scrollImagesRight}
>
<ChevronRight />
</Fab>
</>
)}
</div>
);
}