@mui/material#Accordion JavaScript Examples

The following examples show how to use @mui/material#Accordion. 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: Settings1.js    From react-saas-template with MIT License 4 votes vote down vote up
function Settings1(props) {
  const { classes, pushMessageToSnackbar } = props;
  const [isSaveLoading, setIsSaveLoading] = useState(false);
  const [isDefaultLoading, setIsDefaultLoading] = useState(false);
  const [option1, setOption1] = useState("None");
  const [option2, setOption2] = useState("None");
  const [option3, setOption3] = useState("None");
  const [option4, setOption4] = useState("None");
  const [option5, setOption5] = useState("2 Days");
  const [option6, setOption6] = useState(7500);

  const handleChange = useCallback(
    (event) => {
      const { name, value } = event.target;
      if (name === "option6") {
        if (value > 7500 || value < 1000) {
          return;
        }
      }
      switch (name) {
        case "option1": {
          setOption1(value);
          break;
        }
        case "option2": {
          setOption2(value);
          break;
        }
        case "option3": {
          setOption3(value);
          break;
        }
        case "option4": {
          setOption4(value);
          break;
        }
        case "option5": {
          setOption5(value);
          break;
        }
        case "option6": {
          setOption6(value);
          break;
        }
        default:
          throw new Error("No branch selected in switch statement.");
      }
    },
    [setOption1, setOption2, setOption3, setOption4, setOption5, setOption6]
  );

  const resetState = useCallback(() => {
    setIsSaveLoading(false);
    setIsDefaultLoading(false);
    setOption1("None");
    setOption2("None");
    setOption3("None");
    setOption4("None");
    setOption5("2 Days");
    setOption6(7500);
  }, [
    setIsSaveLoading,
    setIsDefaultLoading,
    setOption1,
    setOption2,
    setOption3,
    setOption4,
    setOption5,
    setOption6,
  ]);

  const onSetDefault = useCallback(() => {
    setIsDefaultLoading(true);
    setTimeout(() => {
      pushMessageToSnackbar({
        text: "Your settings have been reset to default",
      });
      resetState();
    }, 1500);
  }, [pushMessageToSnackbar, resetState]);

  const onSubmit = useCallback(() => {
    setIsSaveLoading(true);
    setTimeout(() => {
      pushMessageToSnackbar({
        text: "Your settings have been saved",
      });
      setIsSaveLoading(false);
    }, 1500);
  }, [setIsSaveLoading, pushMessageToSnackbar]);

  const inputs = [
    {
      state: option1,
      label: "Option 1",
      stateName: "option1",
    },
    {
      state: option2,
      label: "Option 2",
      stateName: "option2",
    },
    {
      state: option3,
      label: "Option 3",
      stateName: "option3",
    },
    {
      state: option4,
      label: "Option 4",
      stateName: "option4",
    },
  ];

  return (
    <Accordion>
      <AccordionSummary expandIcon={<ExpandMoreIcon />}>
        <Typography>Settings 1</Typography>
      </AccordionSummary>
      <AccordionDetails className={classes.dBlock}>
        <List disablePadding>
          <Bordered disableVerticalPadding disableBorderRadius>
            {inputs.map((element, index) => (
              <ListItem
                className="listItemLeftPadding"
                disableGutters
                divider
                key={index}
              >
                <ListItemText>
                  <Typography variant="body2">{element.label}</Typography>
                </ListItemText>
                <FormControl variant="outlined">
                  <ListItemSecondaryAction
                    className={classes.ListItemSecondaryAction}
                  >
                    <Select
                      value={element.state}
                      onChange={handleChange}
                      input={
                        <OutlinedInput
                          name={element.stateName}
                          labelWidth={0}
                          className={classes.numberInput}
                          classes={{ input: classes.numberInputInput }}
                        />
                      }
                      MenuProps={{ disableScrollLock: true }}
                    >
                      {inputOptions.map((innerElement) => (
                        <MenuItem value={innerElement} key={innerElement}>
                          {innerElement}
                        </MenuItem>
                      ))}
                    </Select>
                  </ListItemSecondaryAction>
                </FormControl>
              </ListItem>
            ))}
            <ListItem className="listItemLeftPadding" disableGutters divider>
              <ListItemText>
                <Typography variant="body2">Option 5</Typography>
              </ListItemText>
              <FormControl variant="outlined">
                <ListItemSecondaryAction
                  className={classes.ListItemSecondaryAction}
                >
                  <Select
                    value={option5}
                    onChange={handleChange}
                    input={
                      <OutlinedInput
                        name="option5"
                        labelWidth={0}
                        className={classes.numberInput}
                        classes={{ input: classes.numberInputInput }}
                      />
                    }
                    MenuProps={{ disableScrollLock: true }}
                  >
                    {[
                      "Always",
                      "6 Hours",
                      "12 Hours",
                      "1 Day",
                      "2 Days",
                      "3 Days",
                      "1 Week",
                    ].map((element) => (
                      <MenuItem value={element} key={element}>
                        {element}
                      </MenuItem>
                    ))}
                  </Select>
                </ListItemSecondaryAction>
              </FormControl>
            </ListItem>
            <ListItem className="listItemLeftPadding" disableGutters>
              <ListItemText>
                <Typography variant="body2">Option 6</Typography>
              </ListItemText>
              <FormControl variant="outlined">
                <ListItemSecondaryAction
                  className={classes.ListItemSecondaryAction}
                >
                  <OutlinedInput
                    labelWidth={0}
                    name="option6"
                    value={option6}
                    type="number"
                    onChange={handleChange}
                    className={classes.numberInput}
                    classes={{ input: classes.numberInputInput }}
                    inputProps={{ step: 20 }}
                  />
                </ListItemSecondaryAction>
              </FormControl>
            </ListItem>
          </Bordered>
        </List>
      </AccordionDetails>
      <AccordionDetails className={classes.accordionDetails}>
        <Box mr={1}>
          <Button
            onClick={onSetDefault}
            disabled={isSaveLoading || isDefaultLoading}
          >
            Default {isDefaultLoading && <ButtonCircularProgress />}
          </Button>
        </Box>
        <Button
          variant="contained"
          color="secondary"
          disabled={isSaveLoading || isDefaultLoading}
          onClick={onSubmit}
        >
          Save {isSaveLoading && <ButtonCircularProgress />}
        </Button>
      </AccordionDetails>
    </Accordion>
  );
}
Example #2
Source File: Settings2.js    From react-saas-template with MIT License 4 votes vote down vote up
function Settings2(props) {
  const { pushMessageToSnackbar, classes } = props;
  const [isDefaultLoading, setIsDefaultLoading] = useState(false);
  const [isSaveLoading, setIsSaveLoading] = useState(false);
  const [option1, setOption1] = useState(false);
  const [option2, setOption2] = useState(false);
  const [option3, setOption3] = useState(false);
  const [option4, setOption4] = useState(false);
  const [option5, setOption5] = useState(false);
  const [option6, setOption6] = useState("Both");
  const [option7, setOption7] = useState("2 weeks");

  const resetState = useCallback(() => {
    setIsDefaultLoading(false);
    setIsSaveLoading(false);
    setOption1(false);
    setOption2(false);
    setOption3(false);
    setOption4(false);
    setOption5(false);
    setOption6("Both");
    setOption7("2 weeks");
  }, [
    setOption1,
    setOption2,
    setOption3,
    setOption4,
    setOption5,
    setOption6,
    setOption7,
  ]);

  const onSubmit = useCallback(() => {
    setIsSaveLoading(true);
    setTimeout(() => {
      pushMessageToSnackbar({
        text: "Your settings have been saved",
      });
      setIsSaveLoading(false);
    }, 1500);
  }, [pushMessageToSnackbar, setIsSaveLoading]);

  const onSetDefault = useCallback(() => {
    setIsDefaultLoading(true);
    setTimeout(() => {
      pushMessageToSnackbar({
        text: "Your settings have been reset to default",
      });
      resetState();
    }, 1500);
  }, [pushMessageToSnackbar, resetState, setIsDefaultLoading]);

  const handleInputChange = useCallback(
    (event) => {
      const { name, value } = event.target;
      switch (name) {
        case "option6": {
          setOption6(value);
          break;
        }
        case "option7": {
          setOption7(value);
          break;
        }
        default:
          throw new Error("No branch selected in switch statement");
      }
    },
    [setOption6, setOption7]
  );

  const handleCheckboxChange = (name) => (event) => {
    switch (name) {
      case "option1":
        setOption1(event.target.checked);
        break;
      case "option2":
        setOption2(event.target.checked);
        break;
      case "option3":
        setOption3(event.target.checked);
        break;
      case "option4":
        setOption4(event.target.checked);
        break;
      case "option5":
        setOption5(event.target.checked);
        break;
      default:
        throw new Error("No branch selected in switch statement.");
    }
  };

  const inputs = [
    {
      title: "Option 1",
      secondaryAction: (
        <Checkbox
          value="option1"
          color="primary"
          checked={option1}
          onChange={handleCheckboxChange("option1")}
        />
      ),
    },
    {
      title: "Option 2",
      secondaryAction: (
        <Checkbox
          value="option2"
          color="primary"
          checked={option2}
          onChange={handleCheckboxChange("option2")}
        />
      ),
    },
    {
      title: "Option 3",
      secondaryAction: (
        <Checkbox
          value="option3"
          color="primary"
          checked={option3}
          onChange={handleCheckboxChange("option3")}
        />
      ),
      helpText: "You can add some further explanation here.",
    },
    {
      title: "Option 4",
      secondaryAction: (
        <Checkbox
          value="option4"
          color="primary"
          checked={option4}
          onChange={handleCheckboxChange("option4")}
        />
      ),
    },
    {
      title: "Option 5",
      secondaryAction: (
        <Checkbox
          value="option5"
          color="primary"
          checked={option5}
          onChange={handleCheckboxChange("option5")}
        />
      ),
    },
    {
      title: "Option 6",
      secondaryAction: (
        <Select
          value={option6}
          input={
            <OutlinedInput
              onChange={handleInputChange}
              labelWidth={0}
              className={classes.numberInput}
              classes={{ input: classes.numberInputInput }}
              name="option6"
            />
          }
        >
          <MenuItem value="Both">Both</MenuItem>
          <MenuItem value="Male+">Male+</MenuItem>
          <MenuItem value="Female+">Female+</MenuItem>
          <MenuItem value="Only male">Only male</MenuItem>
          <MenuItem value="Only female">Only female</MenuItem>
        </Select>
      ),
      helpText: "You can add some further explanation here.",
    },
    {
      title: "Option 7",
      secondaryAction: (
        <Select
          value={option7}
          input={
            <OutlinedInput
              onChange={handleInputChange}
              labelWidth={0}
              className={classes.numberInput}
              classes={{ input: classes.numberInputInput }}
              name="option7"
            />
          }
        >
          <MenuItem value="None">None</MenuItem>
          <MenuItem value="6 hours">6 hours</MenuItem>
          <MenuItem value="12 hours">12 hours</MenuItem>
          <MenuItem value="1 day">1 day</MenuItem>
          <MenuItem value="3 days">3 days</MenuItem>
          <MenuItem value="1 week">1 week</MenuItem>
          <MenuItem value="2 weeks">2 weeks</MenuItem>
          <MenuItem value="1 month">1 month</MenuItem>
          <MenuItem value="3 months">3 months</MenuItem>
          <MenuItem value="6 months">6 months</MenuItem>
        </Select>
      ),
      helpText: "You can add some further explanation here.",
    },
  ];

  return (
    <Accordion>
      <AccordionSummary expandIcon={<ExpandMoreIcon />}>
        <Typography>Settings 2</Typography>
      </AccordionSummary>
      <AccordionDetails className={classes.dBlock}>
        <List disablePadding>
          <Bordered disableVerticalPadding disableBorderRadius>
            {inputs.map((element, index) => (
              <ListItem
                key={index}
                divider={index !== inputs.length - 1}
                className="listItemLeftPadding"
              >
                <ListItemText>
                  <Typography variant="body2">
                    {element.title}
                    {element.helpText && <HelpIcon title={element.helpText} />}
                  </Typography>
                </ListItemText>
                <ListItemSecondaryAction>
                  <FormControl variant="outlined">
                    {element.secondaryAction}
                  </FormControl>
                </ListItemSecondaryAction>
              </ListItem>
            ))}
          </Bordered>
        </List>
      </AccordionDetails>
      <AccordionDetails className={classes.AccordionDetails}>
        <Box mr={1}>
          <Button
            onClick={onSetDefault}
            disabled={isSaveLoading || isDefaultLoading}
          >
            Default {isDefaultLoading && <ButtonCircularProgress />}
          </Button>
        </Box>
        <Button
          variant="contained"
          color="secondary"
          onClick={onSubmit}
          disabled={isSaveLoading || isDefaultLoading}
        >
          Save {isSaveLoading && <ButtonCircularProgress />}
        </Button>
      </AccordionDetails>
    </Accordion>
  );
}
Example #3
Source File: UserDataArea.js    From react-saas-template with MIT License 4 votes vote down vote up
function CustomTable(props) {
  const { pushMessageToSnackbar, classes, targets, setTargets } = props;
  const [order, setOrder] = useState("asc");
  const [orderBy, setOrderBy] = useState(null);
  const [page, setPage] = useState(0);
  const [isDeleteTargetDialogOpen, setIsDeleteTargetDialogOpen] = useState(
    false
  );
  const [deleteTargetDialogRow, setDeleteTargetDialogRow] = useState(null);
  const [isDeleteTargetLoading, setIsDeleteTargetLoading] = useState(false);

  const handleRequestSort = useCallback(
    (__, property) => {
      const _orderBy = property;
      let _order = "desc";
      if (orderBy === property && order === "desc") {
        _order = "asc";
      }
      setOrder(_order);
      setOrderBy(_orderBy);
    },
    [setOrder, setOrderBy, order, orderBy]
  );

  const deleteTarget = useCallback(() => {
    setIsDeleteTargetLoading(true);
    setTimeout(() => {
      setIsDeleteTargetDialogOpen(false);
      setIsDeleteTargetLoading(false);
      const _targets = [...targets];
      const index = _targets.findIndex(
        (element) => element.id === deleteTargetDialogRow.id
      );
      _targets.splice(index, 1);
      setTargets(_targets);
      pushMessageToSnackbar({
        text: "Your friend has been removed",
      });
    }, 1500);
  }, [
    setIsDeleteTargetDialogOpen,
    setIsDeleteTargetLoading,
    pushMessageToSnackbar,
    setTargets,
    deleteTargetDialogRow,
    targets,
  ]);

  const handleChangePage = useCallback(
    (_, page) => {
      setPage(page);
    },
    [setPage]
  );

  const handleDeleteTargetDialogClose = useCallback(() => {
    setIsDeleteTargetDialogOpen(false);
  }, [setIsDeleteTargetDialogOpen]);

  const handleDeleteTargetDialogOpen = useCallback(
    (row) => {
      setIsDeleteTargetDialogOpen(true);
      setDeleteTargetDialogRow(row);
    },
    [setIsDeleteTargetDialogOpen, setDeleteTargetDialogRow]
  );

  const toggleTarget = useCallback(
    (row) => {
      const _targets = [...targets];
      const index = _targets.findIndex((element) => element.id === row.id);
      row.isActivated = !row.isActivated;
      _targets[index] = row;
      if (row.isActivated) {
        pushMessageToSnackbar({
          text: "The row is now activated",
        });
      } else {
        pushMessageToSnackbar({
          text: "The row is now deactivated",
        });
      }
      setTargets(_targets);
    },
    [pushMessageToSnackbar, targets, setTargets]
  );

  return (
    <Accordion>
      <AccordionSummary expandIcon={<ExpandMoreIcon />}>
        <Typography>Some user data</Typography>
      </AccordionSummary>
      <ConfirmationDialog
        open={isDeleteTargetDialogOpen}
        title="Confirmation"
        content={
          deleteTargetDialogRow ? (
            <span>
              {"Do you really want to remove the friend "}
              <b>{deleteTargetDialogRow.name}</b>
              {" from your list?"}
            </span>
          ) : null
        }
        onClose={handleDeleteTargetDialogClose}
        onConfirm={deleteTarget}
        loading={isDeleteTargetLoading}
      />
      <Box width="100%">
        <div className={classes.tableWrapper}>
          {targets.length > 0 ? (
            <Table aria-labelledby="tableTitle">
              <EnhancedTableHead
                order={order}
                orderBy={orderBy}
                onRequestSort={handleRequestSort}
                rowCount={targets.length}
                rows={rows}
              />
              <TableBody>
                {stableSort(targets, getSorting(order, orderBy))
                  .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
                  .map((row, index) => (
                    <TableRow hover tabIndex={-1} key={index}>
                      <TableCell
                        component="th"
                        scope="row"
                        className={classes.firstData}
                      >
                        <Avatar
                          className={classes.avatar}
                          src={row.profilePicUrl}
                        />
                      </TableCell>
                      <TableCell component="th" scope="row">
                        {row.name}
                      </TableCell>
                      <TableCell component="th" scope="row">
                        {row.number1}
                      </TableCell>
                      <TableCell component="th" scope="row">
                        {row.number2}
                      </TableCell>
                      <TableCell component="th" scope="row">
                        {row.number3}
                      </TableCell>
                      <TableCell component="th" scope="row">
                        {row.number4}
                      </TableCell>
                      <TableCell component="th" scope="row">
                        <Box display="flex" justifyContent="flex-end">
                          {row.isActivated ? (
                            <IconButton
                              className={classes.iconButton}
                              onClick={() => {
                                toggleTarget(row);
                              }}
                              aria-label="Pause"
                              size="large">
                              <PauseCircleOutlineIcon
                                className={classes.blackIcon}
                              />
                            </IconButton>
                          ) : (
                            <IconButton
                              className={classes.iconButton}
                              color="primary"
                              onClick={() => {
                                toggleTarget(row);
                              }}
                              aria-label="Resume"
                              size="large">
                              <PlayCirlceOutlineIcon />
                            </IconButton>
                          )}
                          <IconButton
                            className={classes.iconButton}
                            onClick={() => {
                              handleDeleteTargetDialogOpen(row);
                            }}
                            aria-label="Delete"
                            size="large">
                            <DeleteIcon className={classes.blackIcon} />
                          </IconButton>
                        </Box>
                      </TableCell>
                    </TableRow>
                  ))}
              </TableBody>
            </Table>
          ) : (
            <Box m={2}>
              <HighlightedInformation>
                No friends added yet.
              </HighlightedInformation>
            </Box>
          )}
        </div>
        <div className={classes.alignRight}>
          <TablePagination
            component="div"
            count={targets.length}
            rowsPerPage={rowsPerPage}
            page={page}
            backIconButtonProps={{
              "aria-label": "Previous Page",
            }}
            nextIconButtonProps={{
              "aria-label": "Next Page",
            }}
            onPageChange={handleChangePage}
            classes={{
              select: classes.dNone,
              selectIcon: classes.dNone,
              actions: targets.length > 0 ? classes.dBlock : classes.dNone,
              caption: targets.length > 0 ? classes.dBlock : classes.dNone,
            }}
            labelRowsPerPage=""
          />
        </div>
      </Box>
    </Accordion>
  );
}
Example #4
Source File: AddClass.js    From admin-web with GNU Affero General Public License v3.0 4 votes vote down vote up
render() {
    const { classes, t, open, onClose, _classes } = this.props;
    const { classname, parentClasses, members, filters, loading, autocompleteInput } = this.state;

    return (
      <Dialog
        onClose={onClose}
        open={open}
        maxWidth="md"
        fullWidth
        TransitionProps={{
          onEnter: this.handleEnter,
        }}>
        <DialogTitle>{t('addHeadline', { item: 'Group' })}</DialogTitle>
        <DialogContent style={{ minWidth: 400 }}>
          <FormControl className={classes.form}>
            <TextField 
              className={classes.input} 
              label={t("Groupname")} 
              fullWidth 
              value={classname || ''}
              onChange={this.handleInput('classname')}
              autoFocus
              required
            />
            <MagnitudeAutocomplete
              value={parentClasses || []}
              filterAttribute={'classname'}
              inputValue={autocompleteInput}
              onChange={this.handleAuto('parentClasses')}
              className={classes.input} 
              options={_classes}
              onInputChange={this.handleInput('autocompleteInput')}
              label={t("Parent groups")}
              multiple
            />
            <TextField 
              className={classes.input} 
              label={t("Members (separate by comma)")} 
              fullWidth 
              value={members || ''}
              onChange={this.handleMemberInput}
            />
          </FormControl>
          <div>
            <Typography variant="body1">{t('Filters (All must be true)')}</Typography>
            {filters.map((ANDFilter, ANDidx) =>
              <Accordion
                className={classes.panel}
                elevation={2 /* 1 has global overwrite */}
                key={ANDidx}
                defaultExpanded
              >
                <AccordionSummary>
                  <Grid container justifyContent="space-between">
                    <Typography body="body1">{t('Filter (One must be true)')}</Typography>
                    <IconButton onClick={this.handleRemoveAND(ANDidx)} size="large">
                      <Delete fontSize="small" color="error"/>
                    </IconButton>
                  </Grid>
                </AccordionSummary>
                <AccordionDetails>
                  <Grid container>
                    {ANDFilter.map((ORFilter, ORidx) =>  
                      <Grid item xs={12} key={ORidx} className={classes.grid}>
                        <Autocomplete
                          value={ORFilter.prop || ''}
                          inputValue={ORFilter.prop || ''}
                          onChange={this.handleAutocomplete(ANDidx, ORidx, 'prop')}
                          onInputChange={this.handleFilterInput(ANDidx, ORidx, 'prop')}
                          freeSolo
                          className={classes.flexTextfield} 
                          options={this.columns}
                          renderInput={(params) => (
                            <TextField
                              {...params}
                              label={t("Name of property to match")}
                            />
                          )}
                        />
                        <TextField
                          className={classes.flexTextfield} 
                          label={t("Comparison operator")}
                          value={ORFilter.op || ''}
                          onChange={this.handleFilterInput(ANDidx, ORidx, 'op')}
                          select
                        >
                          {this.operators.map(op =>
                            <MenuItem value={op.value} key={op.label}>{op.label}</MenuItem>
                          )}
                        </TextField>
                        <TextField
                          className={classes.flexTextfield} 
                          label={t("Compare value (binary operators)")}
                          value={ORFilter.val || ''}
                          onChange={this.handleFilterInput(ANDidx, ORidx, 'val')}
                        />
                        {filters[ANDidx].length > 1 &&
                        <IconButton onClick={this.handleRemoveOR(ANDidx, ORidx)} size="large">
                          <Delete fontSize="small" color="error"/>
                        </IconButton>}
                      </Grid>
                    )}
                    <Grid container justifyContent="center" className={classes.marginTop}>
                      <Button variant="outlined" onClick={this.handleAddOR(ANDidx)}>{t('Add or-statement')}</Button>
                    </Grid>
                  </Grid>
                </AccordionDetails>
              </Accordion>
            )}
            <Grid container justifyContent="center" className={classes.marginTop}>
              <Button variant="outlined" onClick={this.handleAddAND}>{t('Add and-statement')}</Button>
            </Grid>
          </div>
        </DialogContent>
        <DialogActions>
          <Button
            onClick={onClose}
            color="secondary"
          >
            {t('Cancel')}
          </Button>
          <Button
            onClick={this.handleAdd}
            variant="contained"
            color="primary"
            disabled={loading || !classname}
          >
            {loading ? <CircularProgress size={24}/> : t('Add')}
          </Button>
        </DialogActions>
      </Dialog>
    );
  }
Example #5
Source File: ClassDetails.js    From admin-web with GNU Affero General Public License v3.0 4 votes vote down vote up
render() {
    const { classes, t, domain, _classes } = this.props;
    const writable = this.context.includes(DOMAIN_ADMIN_WRITE);
    const { _class, autocompleteInput, snackbar, stack } = this.state;
    const { classname, parentClasses, members, filters, children } = _class;
    return (
      <ViewWrapper
        topbarTitle={t('Groups')}
        snackbar={snackbar}
        onSnackbarClose={() => this.setState({ snackbar: '' })}
      >
        <Paper className={classes.paper} elevation={1}>
          <Grid container>
            <Typography
              color="primary"
              variant="h5"
            >
              {t('editHeadline', { item: 'Group' })}
            </Typography>
          </Grid>
          <FormControl className={classes.form}>
            <Breadcrumbs className={classes.breadcrumbs}>
              {stack.map((_class, idx) =>
                <Link
                  className={classes.breadcrumb}
                  key={_class.ID}
                  color="inherit"
                  onClick={this.handleBreadcrumb(_class.ID, idx)}
                >
                  {_class.classname}
                </Link>
              )}
            </Breadcrumbs>
            <TextField 
              className={classes.input} 
              label={t("Groupname")} 
              fullWidth 
              value={classname || ''}
              onChange={this.handleInput('classname')}
              autoFocus
              required
            />
            <MagnitudeAutocomplete
              value={parentClasses || []}
              filterAttribute={'classname'}
              inputValue={autocompleteInput}
              onChange={this.handleAutocomplete('parentClasses')}
              className={classes.input} 
              options={_classes || []}
              onInputChange={this.handleAutocompleteInput}
              label={t("Parent groups")}
              placeholder={t("Search groups") + "..."}
              multiple
              autoSelect
            />
            <TextField 
              className={classes.input} 
              label={t("Members (separate by comma)")} 
              fullWidth 
              value={members || ''}
              onChange={this.handleMemberInput}
            />
          </FormControl>
          <div>
            <Typography variant="body1">{t('Filters (All must be true)')}</Typography>
            {filters && filters.map((ANDFilter, ANDidx) =>
              <Accordion
                className={classes.panel}
                elevation={2 /* 1 has global overwrite */}
                key={ANDidx}
                defaultExpanded
              >
                <AccordionSummary>
                  <Grid container justifyContent="space-between">
                    <Typography body="body1">{t('Filter (One must be true)')}</Typography>
                    <IconButton onClick={this.handleRemoveAND(ANDidx)} size="large">
                      <Delete fontSize="small" color="error"/>
                    </IconButton>
                  </Grid>
                </AccordionSummary>
                <AccordionDetails>
                  <Grid container>
                    {ANDFilter.map((ORFilter, ORidx) =>  
                      <Grid item xs={12} key={ORidx} className={classes.grid}>
                        <Autocomplete
                          value={ORFilter.prop || ''}
                          inputValue={ORFilter.prop || ''}
                          onChange={this.handleAutocomplete(ANDidx, ORidx, 'prop')}
                          onInputChange={this.handleFilterInput(ANDidx, ORidx, 'prop')}
                          freeSolo
                          className={classes.flexTextfield} 
                          options={this.columns}
                          renderInput={(params) => (
                            <TextField
                              {...params}
                              label={t("Name of property to match")}
                            />
                          )}
                        />
                        <TextField
                          className={classes.flexTextfield} 
                          label={t("Comparison operator")}
                          value={ORFilter.op || ''}
                          onChange={this.handleFilterInput(ANDidx, ORidx, 'op')}
                          select
                        >
                          {this.operators.map(op =>
                            <MenuItem value={op.value} key={op.label}>{op.label}</MenuItem>
                          )}
                        </TextField>
                        <TextField
                          className={classes.flexTextfield} 
                          label={t("Compare value (binary operators)")}
                          value={ORFilter.val || ''}
                          onChange={this.handleFilterInput(ANDidx, ORidx, 'val')}
                        />
                        {filters[ANDidx].length > 1 &&
                        <IconButton onClick={this.handleRemoveOR(ANDidx, ORidx)} size="large">
                          <Delete fontSize="small" color="error"/>
                        </IconButton>}
                      </Grid>
                    )}
                    <Grid container justifyContent="center">
                      <Button variant="outlined" onClick={this.handleAddOR(ANDidx)}>{t('Add or-statement')}</Button>
                    </Grid>
                  </Grid>
                </AccordionDetails>
              </Accordion>
            )}
            <Grid container justifyContent="center">
              <Button variant="outlined" onClick={this.handleAddAND}>{t('Add and-statement')}</Button>
            </Grid>
          </div>
          <Typography variant="h6">{t('Children')}</Typography>
          <List>
            {children && children.map(child =>
              <ListItem
                key={child.ID}
                button
                onClick={this.handleChildClicked(child)}
              >
                <ListItemText primary={child.classname} />
              </ListItem>
            )}
          </List>
          <Button
            variant="text"
            color="secondary"
            onClick={this.handleNavigation(domain.ID + '/classes')}
            style={{ marginRight: 8 }}
          >
            {t('Back')}
          </Button>
          <Button
            variant="contained"
            color="primary"
            onClick={this.handleEdit}
            disabled={!writable}
          >
            {t('Save')}
          </Button>
        </Paper>
      </ViewWrapper>
    );
  }