Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please add MaterialFieldCascader #320

Open
i19s-frank opened this issue Oct 20, 2020 · 5 comments
Open

Please add MaterialFieldCascader #320

i19s-frank opened this issue Oct 20, 2020 · 5 comments
Labels

Comments

@i19s-frank
Copy link

Is your feature request related to a problem? Please describe.
I have to migrate from antd to material but am strongly relying on the FieldCascader.

Describe the solution you'd like
Implement a MaterialFieldCascader.

Describe alternatives you've considered
I am currently investiganting whether I can implement it myself.

@i19s-frank
Copy link
Author

Hello Denis,

I have implemented a solution fit to our needs. Since I don't have the time to adapt it into a meaningful PR I wanted to at least share the idea the solution is based on.

Since Material doesn't provide a component like the antd field selector, I went with a List with sticky Headers.

The code is in TS but you get the idea:


interface FieldCascaderProps extends FieldProps {
  selectedField: string | Empty,
  parentField: string | Empty,
}

interface Section {
  path: string,
  label: string,
  fields: FieldItem[],
}

const MaterialFieldCascader = (props: FieldCascaderProps) => {
  const {
    items, placeholder, selectedKey, setField
  } = props;
  const useStyles = makeStyles((theme: Theme) =>
      createStyles({
        root: {
          width: '100%',
          maxWidth: 360,
          backgroundColor: theme.palette.background.paper,
          position: 'relative',
          overflow: 'auto',
          maxHeight: 300,
        },
        listSection: {
          backgroundColor: 'inherit',
        },
        ul: {
          backgroundColor: 'inherit',
          padding: 0,
        },
        nested: {
          paddingLeft: theme.spacing(4),
        },
        fieldSelectButton: {}
      }),
  );

  const classes = useStyles();
  const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null);

  const handleClick = (event: React.MouseEvent<any>) => {
    setAnchorEl(event.currentTarget);
  };
  const handleClose = () => {
    setAnchorEl(null);
  };

  const onFieldSeleced = (path: string) => {
    setField(path);
  }

  const open = Boolean(anchorEl);
  const id = open ? 'material-field-cascader-popover' : undefined;
  const sections: Section[] = [];

  let selectedTemp: string = placeholder!;

  for (let i in items) {
    let item = items[i];

    if (!item.items) {
      throw new Error("Cannot handle leaves in the root");
    }
    if (!item.path) {
      throw new Error("Path is missing!");
    }
    let section = {
      path: item.path, fields: [], label: item.label!
    };
    sections.push(section);
    handleChildren(item.items, sections, section);
  }

  function handleChildren(children: FieldItems, sections: Section[], parent: Section) {
    for (let i in children) {
      let child = children[i];
      // this is a leaf
      if (!child.items) {
        if (child.path === selectedKey) {
          selectedTemp = parent.label + " / " + child.label;
        }
        parent.fields.push(child)
      } else {
        let section = {
          path: child.path!, fields: [], label: parent.label + " / " + child.label
        };
        sections.push(section);
        handleChildren(child.items, sections, section);
      }
    }
  }

  const selected = selectedTemp;

  return <div>
    <Button aria-describedby={id} variant="text" className={classes.fieldSelectButton}
            onClick={handleClick}>
      {selected}
    </Button>
    <Popover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'center',
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 'center',
        }}
    >

      <List className={classes.root} subheader={<li/>}>
        {sections.map((section) => (
            <li id={`section-${section.path}`} key={`section-${section.path}`}
                className={classes.listSection}>
              <ul className={classes.ul}>
                <ListSubheader>{`${section.label}`}</ListSubheader>
                {section.fields.map((field) => (
                    <ListItem id={`item-${field.path}`} key={`item-${field.path}`}
                              className={classes.nested} onClick={(e) => {
                      onFieldSeleced(field.path!);
                      handleClose();
                    }}>
                      <ListItemText primary={`${field.label}`}/>
                    </ListItem>
                ))}
              </ul>
            </li>
        ))}
      </List>
    </Popover>
  </div>;
}
export default MaterialFieldCascader;

The implementation obviously isn't complete.

@Ognian
Copy link

Ognian commented Jul 12, 2021

Any news on this?

@ukrbublik
Copy link
Owner

I'll be working on features for MUI soon.
It would be great if someone open PR with code above

@ukrbublik ukrbublik added the MUI label Aug 19, 2021
@ukrbublik
Copy link
Owner

mui/material-ui#20591

@ukrbublik
Copy link
Owner

ukrbublik commented Aug 20, 2021

@ukrbublik ukrbublik added this to the UI new widgets milestone Jul 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants