3.0.0 • Published 3 years ago

@arterial/menu-surface v3.0.0

Weekly downloads
25
License
MIT
Repository
github
Last release
3 years ago

Arterial Menu Surface

Another React Material Menu Surface

Installation

npm install @arterial/menu-surface

Usage

Styles

Sass

@use "@material/menu-surface/index.scss" as menu-surface;
@use "@material/menu/index.scss" as menu;
@include menu-surface.core-styles;
@include menu.core-styles;

CSS

import '@material/menu-surface/dist/mdc.menu-surface.css';
import '@material/menu/dist/mdc.menu.css';

JSX

import {MenuSurface, MenuSurfaceAnchor, Corner} from '@arterial/menu-surface';

Regular Menu

function Regular() {
  const anchorRef = useRef();
  const arterialRef = useRef();
  const [open, setOpen] = useState(false);
  useEffect(() => {
    function handleBodyClick(e) {
      const {arterial} = e.target.dataset;
      if (!arterial) {
        setOpen(false);
      }
    }
    function handleWindowKeyDown(e) {
      const {arterial} = e.target.dataset;
      const isEscape = e.key === 'Escape' || e.keyCode === 27;
      const isTab = e.key === 'Tab' || e.keyCode === 9;
      if ((isEscape || isTab) && arterial === arterialRef.current) {
        setOpen(false);
      }
    }
    document.body.addEventListener('click', handleBodyClick);
    window.addEventListener('keydown', handleWindowKeyDown);
    return () => {
      document.body.removeEventListener('click', handleBodyClick);
      window.removeEventListener('keydown', handleWindowKeyDown);
    };
  }, []);
  return (
    <MenuSurfaceAnchor ref={anchorRef}>
      <Button label="Open Menu" onClick={() => setOpen(!open)} />
      <MenuSurface anchorRef={anchorRef} data-arterial="regular" open={open}>
        <List data-arterial="regular">
          <ListItem>
            <ListItemText>Menu Item 1</ListItemText>
          </ListItem>
          <ListItem>
            <ListItemText>Menu Item 2</ListItemText>
          </ListItem>
        </List>
      </MenuSurface>
    </MenuSurfaceAnchor>
  );
}

Other Variants

Fixed

function Fixed() {
  const anchorRef = useRef();
  const arterialRef = useRef();
  const [open, setOpen] = useState(false);
  useEffect(() => {
    function handleBodyClick(e) {
      const {arterial} = e.target.dataset;
      if (!arterial) {
        setOpen(false);
      }
    }
    function handleWindowKeyDown(e) {
      const {arterial} = e.target.dataset;
      const isEscape = e.key === 'Escape' || e.keyCode === 27;
      const isTab = e.key === 'Tab' || e.keyCode === 9;
      if ((isEscape || isTab) && arterial === arterialRef.current) {
        setOpen(false);
      }
    }
    document.body.addEventListener('click', handleBodyClick);
    window.addEventListener('keydown', handleWindowKeyDown);
    return () => {
      document.body.removeEventListener('click', handleBodyClick);
      window.removeEventListener('keydown', handleWindowKeyDown);
    };
  }, []);
  return (
    <MenuSurfaceAnchor ref={anchorRef}>
      <Button label="Open Menu" onClick={() => setOpen(!open)} />
      <MenuSurface
        anchorRef={anchorRef}
        data-arterial="fixed"
        fixed
        open={open}
      >
        <List data-arterial="fixed">
          <ListItem>
            <ListItemText>Menu Item 1</ListItemText>
          </ListItem>
          <ListItem>
            <ListItemText>Menu Item 2</ListItemText>
          </ListItem>
        </List>
      </MenuSurface>
    </MenuSurfaceAnchor>
  );
}

Quick Open

function QuickOpen() {
  const anchorRef = useRef();
  const arterialRef = useRef('quickOpen');
  const [open, setOpen] = useState(false);
  useEffect(() => {
    function handleBodyClick(e) {
      const {arterial} = e.target.dataset;
      if (!arterial) {
        setOpen(false);
      }
    }
    function handleWindowKeyDown(e) {
      const {arterial} = e.target.dataset;
      const isEscape = e.key === 'Escape' || e.keyCode === 27;
      const isTab = e.key === 'Tab' || e.keyCode === 9;
      if ((isEscape || isTab) && arterial === arterialRef.current) {
        setOpen(false);
      }
    }
    document.body.addEventListener('click', handleBodyClick);
    window.addEventListener('keydown', handleWindowKeyDown);
    return () => {
      document.body.removeEventListener('click', handleBodyClick);
      window.removeEventListener('keydown', handleWindowKeyDown);
    };
  }, []);
  return (
    <MenuSurfaceAnchor ref={anchorRef}>
      <Button
        data-arterial="quickOpen"
        label="Open Menu"
        onClick={() => setOpen(!open)}
        unelevated
      />
      <MenuSurface
        anchorRef={anchorRef}
        data-arterial="quickOpen"
        open={open}
        quickOpen
      >
        <List data-arterial="quickOpen">
          <ListItem>
            <ListItemText>Menu Item 1</ListItemText>
          </ListItem>
          <ListItem>
            <ListItemText>Menu Item 2</ListItemText>
          </ListItem>
        </List>
      </MenuSurface>
    </MenuSurfaceAnchor>
  );
}

Right Click

function RightClick() {
  const anchorRef = useRef();
  const arterialRef = useRef('rightClick');
  const [open, setOpen] = useState(false);
  const [position, setPosition] = useState({x: 0, y: 0});
  useEffect(() => {
    function handleBodyClick(e) {
      const {arterial} = e.target.dataset;
      if (!arterial) {
        setOpen(false);
      }
    }
    function handleWindowKeyDown(e) {
      const {arterial} = e.target.dataset;
      const isEscape = e.key === 'Escape' || e.keyCode === 27;
      const isTab = e.key === 'Tab' || e.keyCode === 9;
      if ((isEscape || isTab) && arterial === arterialRef.current) {
        setOpen(false);
      }
    }
    function handleRightClick(e) {
      e.preventDefault();
      setPosition({x: e.clientX, y: e.clientY});
      setOpen(true);
    }
    document.body.addEventListener('click', handleBodyClick);
    window.addEventListener('keydown', handleWindowKeyDown);
    document
      .getElementById('right-click-demo')
      .addEventListener('contextmenu', handleRightClick);
    return () => {
      document.body.removeEventListener('click', handleBodyClick);
      window.removeEventListener('keydown', handleWindowKeyDown);
      document
        .getElementById('right-click-demo')
        .removeEventListener('contextmenu', handleRightClick);
    };
  }, []);
  return (
    <MenuSurface
      anchorRef={anchorRef}
      data-arterial="rightClick"
      open={open}
      position={position}
    >
      <List data-arterial="rightClick">
        <ListItem>
          <ListItemText>Menu Item 1</ListItemText>
        </ListItem>
        <ListItem>
          <ListItemText>Menu Item 2</ListItemText>
        </ListItem>
      </List>
    </MenuSurface>
  );
}

Bottom Left

function BottomLeft() {
  const anchorRef = useRef();
  const arterialRef = useRef('bottomLeft');
  const [open, setOpen] = useState(false);
  useEffect(() => {
    function handleBodyClick(e) {
      const {arterial} = e.target.dataset;
      if (!arterial) {
        setOpen(false);
      }
    }
    function handleWindowKeyDown(e) {
      const {arterial} = e.target.dataset;
      const isEscape = e.key === 'Escape' || e.keyCode === 27;
      const isTab = e.key === 'Tab' || e.keyCode === 9;
      if ((isEscape || isTab) && arterial === arterialRef.current) {
        setOpen(false);
      }
    }
    document.body.addEventListener('click', handleBodyClick);
    window.addEventListener('keydown', handleWindowKeyDown);
    return () => {
      document.body.removeEventListener('click', handleBodyClick);
      window.removeEventListener('keydown', handleWindowKeyDown);
    };
  }, []);
  return (
    <MenuSurfaceAnchor ref={anchorRef}>
      <Button
        data-arterial="bottomLeft"
        label="Open Menu"
        onClick={() => setOpen(!open)}
        unelevated
      />
      <MenuSurface
        anchorCorner={Corner.BOTTOM_LEFT}
        anchorRef={anchorRef}
        data-arterial="bottomLeft"
        open={open}
      >
        <List data-arterial="bottomLeft">
          <ListItem>
            <ListItemText>Menu Item 1</ListItemText>
          </ListItem>
          <ListItem>
            <ListItemText>Menu Item 2</ListItemText>
          </ListItem>
        </List>
      </MenuSurface>
    </MenuSurfaceAnchor>
  );
}

Top Right

function TopRight() {
  const anchorRef = useRef();
  const arterialRef = useRef('topRight');
  const [open, setOpen] = useState(false);
  useEffect(() => {
    function handleBodyClick(e) {
      const {arterial} = e.target.dataset;
      if (!arterial) {
        setOpen(false);
      }
    }
    function handleWindowKeyDown(e) {
      const {arterial} = e.target.dataset;
      const isEscape = e.key === 'Escape' || e.keyCode === 27;
      const isTab = e.key === 'Tab' || e.keyCode === 9;
      if ((isEscape || isTab) && arterial === arterialRef.current) {
        setOpen(false);
      }
    }
    document.body.addEventListener('click', handleBodyClick);
    window.addEventListener('keydown', handleWindowKeyDown);
    return () => {
      document.body.removeEventListener('click', handleBodyClick);
      window.removeEventListener('keydown', handleWindowKeyDown);
    };
  }, []);
  return (
    <MenuSurfaceAnchor ref={anchorRef}>
      <Button
        data-arterial="topRight"
        label="Open Menu"
        onClick={() => setOpen(!open)}
        unelevated
      />
      <MenuSurface
        anchorCorner={Corner.TOP_RIGHT}
        anchorRef={anchorRef}
        data-arterial="topRight"
        open={open}
      >
        <List data-arterial="topRight">
          <ListItem>
            <ListItemText>Menu Item 1</ListItemText>
          </ListItem>
          <ListItem>
            <ListItemText>Menu Item 2</ListItemText>
          </ListItem>
        </List>
      </MenuSurface>
    </MenuSurfaceAnchor>
  );
}

Bottom Right

function BottomRight() {
  const anchorRef = useRef();
  const arterialRef = useRef('bottomRight');
  const [open, setOpen] = useState(false);
  useEffect(() => {
    function handleBodyClick(e) {
      const {arterial} = e.target.dataset;
      console.log(arterial);
      if (!arterial) {
        setOpen(false);
      }
    }
    function handleWindowKeyDown(e) {
      const {arterial} = e.target.dataset;
      const isEscape = e.key === 'Escape' || e.keyCode === 27;
      const isTab = e.key === 'Tab' || e.keyCode === 9;
      if ((isEscape || isTab) && arterial === arterialRef.current) {
        setOpen(false);
      }
    }
    document.body.addEventListener('click', handleBodyClick);
    window.addEventListener('keydown', handleWindowKeyDown);
    return () => {
      document.body.removeEventListener('click', handleBodyClick);
      window.removeEventListener('keydown', handleWindowKeyDown);
    };
  }, []);
  return (
    <MenuSurfaceAnchor ref={anchorRef} style={{width: 'max-content'}}>
      <Button
        data-arterial="bottomRight"
        label="Open Menu"
        onClick={() => setOpen(!open)}
        unelevated
      />
      <MenuSurface
        anchorCorner={Corner.BOTTOM_RIGHT}
        anchorRef={anchorRef}
        data-arterial="bottomRight"
        open={open}
      >
        <List data-arterial="bottomRight">
          <ListItem>
            <ListItemText>Menu Item 1</ListItemText>
          </ListItem>
          <ListItem>
            <ListItemText>Menu Item 2</ListItemText>
          </ListItem>
        </List>
      </MenuSurface>
    </MenuSurfaceAnchor>
  );
}

Props

MenuSurface

NameTypeDescription
anchorCornerCornerCorner of the menu surface to which menu surface is attached to anchor.
anchorMargin{ top, right, bottom, left }Sets the margin between the menu surface and the anchor.
anchorRefRefA reference to the anchor element.
childrennodeElements to be displayed within root element.
classNamestringClasses to be applied to the root element.
dirltr | rtlIndicates the directionality of the element's text. Defaults to auto.
fixedbooleanEnables the fixed variant.
openbooleanIndicates the menu surface is open.
originCornerCornerCorner of the menu surface to attach to the anchor.
position{ x, y }Sets the anchors absolute position.
quickOpenbooleanDisables the open/close animation of the menu surface.
styleobjectStyles to be applied to the root element.
tagstring | objectHTML tag to be applied to the root element. Defaults to div.

MenuSurfaceAnchor

NameTypeDescription
childrennodeElements to be displayed within root element.
classNamestringClasses to be applied to the root element.
tagstring | objectHTML tag to be applied to the root element. Defaults to div.
3.0.0

3 years ago

2.0.3

4 years ago

2.0.2

4 years ago

2.0.1

4 years ago

2.0.0

4 years ago

1.2.0

4 years ago

1.1.0

4 years ago

1.0.6

4 years ago

1.0.5

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago

1.0.0-alpha.6

4 years ago

1.0.0-alpha.5

4 years ago

1.0.0-alpha.4

4 years ago

1.0.0-alpha.3

4 years ago

1.0.0-alpha.0

4 years ago