better-mui-menu 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +117 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # better-mui-menu
2
+
3
+ `better-mui-menu` is a tiny component library that supplies a keyboard- and mouse-friendly Material UI menu with unlimited nesting depth. It wraps `@mui/material/Menu` + `Popper` + `Fade` and exposes a simple data-driven API so that a single `items` array can render both leaves and nested submenus.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install better-mui-menu
9
+ ```
10
+
11
+ Because the package delegates rendering to Material UI, make sure the runtime peers are installed as well:
12
+
13
+ ```bash
14
+ npm install @mui/material @emotion/react @emotion/styled @mui/icons-material
15
+ ```
16
+
17
+ The icons dependency is only required when you pass `startIcon`/`endIcon`, but it is part of the peer dependency list so that consumers can supply Material icons without additional typings setup.
18
+
19
+ ## Usage
20
+
21
+ ```tsx
22
+ import { useState } from 'react'
23
+ import Button from '@mui/material/Button'
24
+ import Cloud from '@mui/icons-material/Cloud'
25
+ import Save from '@mui/icons-material/Save'
26
+ import Menu, { type MenuItem } from 'better-mui-menu'
27
+
28
+ const menuItems: MenuItem[] = [
29
+ {
30
+ id: 'save',
31
+ label: 'Save',
32
+ startIcon: Save,
33
+ onClick: () => {
34
+ console.log('Save action')
35
+ },
36
+ },
37
+ {
38
+ type: 'divider',
39
+ },
40
+ {
41
+ label: 'Cloud actions',
42
+ startIcon: Cloud,
43
+ items: [
44
+ {
45
+ label: 'Upload',
46
+ onClick: () => console.log('Upload'),
47
+ },
48
+ {
49
+ label: 'Download',
50
+ onClick: () => console.log('Download'),
51
+ },
52
+ ],
53
+ },
54
+ ]
55
+
56
+ export function FileMenu() {
57
+ const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
58
+
59
+ return (
60
+ <>
61
+ <Button variant='contained' onClick={event => setAnchorEl(event.currentTarget)}>
62
+ Open file menu
63
+ </Button>
64
+ <Menu
65
+ anchorEl={anchorEl}
66
+ open={Boolean(anchorEl)}
67
+ onClose={() => setAnchorEl(null)}
68
+ items={menuItems}
69
+ />
70
+ </>
71
+ )
72
+ }
73
+ ```
74
+
75
+ ## API
76
+
77
+ ### `Menu` props
78
+
79
+ - `anchorEl: HTMLElement | null` – the anchor element that the root menu positions itself against.
80
+ - `open: boolean` – controlled open state of the root menu.
81
+ - `onClose: () => void` – invoked whenever the menu should close (overlay click, Escape, item selection).
82
+ - `items: MenuItem[]` – the array that drives both leaf menu items and nested branches.
83
+
84
+ ### `MenuItem`
85
+
86
+ `MenuItem` extends `@mui/material/MenuItemProps` (excluding `children`) so you can pass `disabled`, `dense`, `divider`, etc. The shape adds a few helpers:
87
+
88
+ - `type?: 'item' | 'divider'` – set `'divider'` to render a `Divider` instead of a `MenuItem`.
89
+ - `id?: string` – optional string used for ARIA attributes; a stable ID is generated automatically when omitted.
90
+ - `label: ReactNode` – the text or custom node shown inside the menu entry.
91
+ - `startIcon?: SvgIconComponent`, `endIcon?: SvgIconComponent` – render Material icons before/after the label using the shared `MenuItemContent` layout.
92
+ - `items?: MenuItem[]` – nested descriptors that render as a submenu via `NestedMenuItem`.
93
+ - `onClick?: MenuItemProps['onClick']` – clicking a leaf entry automatically propagates the handler and closes the entire menu stack.
94
+
95
+ ## Interactions & accessibility
96
+
97
+ - Nested menus appear in a `Popper` placed `right-start` from the parent trigger and use a shared fade transition (`transitionConfig`).
98
+ - Mouse hover keeps submenus open while the cursor travels between a trigger and its nested popper; moving away closes the submenu.
99
+ - Keyboard navigation supports `ArrowRight`/`Enter`/`Space` to open children, `ArrowLeft` to close, `ArrowUp`/`ArrowDown` to move between entries, and `Escape` to dismiss everything. The component wires the necessary `aria-haspopup`, `aria-controls`, `aria-expanded`, and `aria-labelledby` attributes so that screen readers describe the menu hierarchy.
100
+
101
+ ## Development
102
+
103
+ All development happens under `package/better-mui-menu`:
104
+
105
+ - `npm run dev` – runs `tsup --watch` to rebuild `src` into `dist`.
106
+ - `npm run build` – creates production bundles ready for publication.
107
+ - `npm run test` – executes the Jest suite defined in `src/Menu/Menu.test.tsx`.
108
+
109
+ The root workspace exposes `npm run dev:lib` and `npm run dev:demo` to simultaneously rebuild the library and power the demo app. When you change the menu source, keep `npm run dev` (or `npm run build`) running before refreshing the demo because the Vite app imports the library via its `file:` workspace link.
110
+
111
+ ## Demo
112
+
113
+ `app/demo` is a Vite + React 19 application that consumes the built package. From the repository root run:
114
+
115
+ ```bash
116
+ npm run dev:demo
117
+ ```
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "bugs": {
9
9
  "url": "https://github.com/eggei/better-mui-menu/issues"
10
10
  },
11
- "version": "1.0.0",
11
+ "version": "1.0.1",
12
12
  "type": "module",
13
13
  "sideEffects": false,
14
14
  "main": "./dist/index.cjs",