@jonmilner/ui 0.4.0 → 0.4.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.
- package/dist/Menu/Menu.css +1 -1
- package/dist/Menu/Menu.css.mjs +2 -2
- package/dist/Menu/index.d.mts +6 -0
- package/dist/Menu/index.mjs +82 -54
- package/dist/docs/Menu.md +44 -8
- package/dist/docs/llms.txt +1 -1
- package/dist/docs/manifest.json +44 -1
- package/dist/index.d.mts +2 -2
- package/dist/lint/index.mjs +1 -0
- package/package.json +1 -1
package/dist/Menu/Menu.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
@layer tokens, reset, atoms, components.box, components.checkbox, components.clickable, components.icon, components.icon-button, components.inline, components.stack, components.text, components.badge, components.button, components.chip, components.field, components.heading, components.dialog, components.alert-dialog, components.menu, components.multiselect, components.search-field, components.tabs, components.tag, components.toast, components.visually-hidden;
|
|
2
|
-
@layer components.menu{:where(._393eil0[data-closed])>[data-base-ui-focus-guard]{visibility:hidden}._393eil1{border:var(--space-1) solid var(--color-border);transform-origin:var(--transform-origin);transition:opacity var(--duration-fast) var(--easing-standard), scale var(--duration-fast) var(--easing-enter)}._393eil1:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}._393eil1[data-starting-style],._393eil1[data-ending-style]{opacity:0;scale:calc(1 - var(--scale-med))}._393eil2{cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none}._393eil2:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}._393eil2[data-highlighted]{background-color:light-dark(var(--color-bg-muted),color-mix(in srgb, var(--color-fg) 10%, var(--color-bg-raised)))}._393eil2[aria-disabled=true]{color:var(--color-fg-muted);cursor:not-allowed}._393eil3{border-block-start:var(--space-1) solid var(--color-border)}@media (forced-colors:active){._393eil2[data-highlighted]{forced-color-adjust:none;color:highlighttext;background-color:highlight}._393eil2[aria-disabled=true]{color:graytext}}}
|
|
2
|
+
@layer components.menu{:where(._393eil0[data-closed])>[data-base-ui-focus-guard]{visibility:hidden}._393eil1{border:var(--space-1) solid var(--color-border);transform-origin:var(--transform-origin);transition:opacity var(--duration-fast) var(--easing-standard), scale var(--duration-fast) var(--easing-enter)}._393eil1:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}._393eil1[data-starting-style],._393eil1[data-ending-style]{opacity:0;scale:calc(1 - var(--scale-med))}._393eil2{cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none}._393eil2:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}._393eil2[data-highlighted]{background-color:light-dark(var(--color-bg-muted),color-mix(in srgb, var(--color-fg) 10%, var(--color-bg-raised)))}._393eil2[aria-disabled=true]{color:var(--color-fg-muted);cursor:not-allowed}._393eil3{text-decoration:none}._393eil4{border-block-start:var(--space-1) solid var(--color-border)}@media (forced-colors:active){._393eil2[data-highlighted]:not([aria-disabled=true]){forced-color-adjust:none;color:highlighttext;background-color:highlight}._393eil2[aria-disabled=true]{color:graytext}}}
|
package/dist/Menu/Menu.css.mjs
CHANGED
|
@@ -3,6 +3,6 @@ import "./Menu.css";
|
|
|
3
3
|
/* empty css */
|
|
4
4
|
/* empty css */
|
|
5
5
|
//#region src/Menu/Menu.css.ts
|
|
6
|
-
var e = "_393eil0", t = "_393eil1", n = "_393eil2", r = "_393eil3";
|
|
6
|
+
var e = "_393eil0", t = "_393eil1", n = "_393eil2", r = "_393eil3", i = "_393eil4";
|
|
7
7
|
//#endregion
|
|
8
|
-
export { n as item, t as popup, e as positioner,
|
|
8
|
+
export { n as item, r as linkItem, t as popup, e as positioner, i as separator };
|
package/dist/Menu/index.d.mts
CHANGED
|
@@ -14,6 +14,10 @@ export type MenuPopupProps = PartProps<Menu$1.Popup.Props> & Pick<Menu$1.Positio
|
|
|
14
14
|
export type MenuItemProps = PartProps<Menu$1.Item.Props> & {
|
|
15
15
|
variant?: "neutral" | "danger";
|
|
16
16
|
};
|
|
17
|
+
export type MenuLinkItemProps = PartProps<Menu$1.LinkItem.Props> & {
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
href: string;
|
|
20
|
+
};
|
|
17
21
|
export type MenuSeparatorProps = PartProps<Menu$1.Separator.Props>;
|
|
18
22
|
export type MenuGroupProps = PartProps<Menu$1.Group.Props> & {
|
|
19
23
|
label: ReactNode;
|
|
@@ -21,12 +25,14 @@ export type MenuGroupProps = PartProps<Menu$1.Group.Props> & {
|
|
|
21
25
|
declare function MenuRoot({ children, ...props }: MenuProps): import("react").JSX.Element;
|
|
22
26
|
declare function MenuPopup({ side, align, backgroundColor, borderRadius, boxShadow, padding, className, style, ...props }: MenuPopupProps): import("react").JSX.Element;
|
|
23
27
|
declare function MenuItem({ variant, display, alignItems, gap, minBlockSize, paddingInline, borderRadius, color, className, style, children, ...props }: MenuItemProps): import("react").JSX.Element;
|
|
28
|
+
declare function MenuLinkItem({ href, disabled, closeOnClick, onClick, display, alignItems, gap, minBlockSize, paddingInline, borderRadius, color, className, style, children, ...props }: MenuLinkItemProps): import("react").JSX.Element;
|
|
24
29
|
declare function MenuSeparator({ marginBlock, marginInline, className, style, ...props }: MenuSeparatorProps): import("react").JSX.Element;
|
|
25
30
|
declare function MenuGroup({ label, className, style, children, ...props }: MenuGroupProps): import("react").JSX.Element;
|
|
26
31
|
export declare const Menu: typeof MenuRoot & {
|
|
27
32
|
Trigger: typeof MenuTrigger;
|
|
28
33
|
Popup: typeof MenuPopup;
|
|
29
34
|
Item: typeof MenuItem;
|
|
35
|
+
LinkItem: typeof MenuLinkItem;
|
|
30
36
|
Separator: typeof MenuSeparator;
|
|
31
37
|
Group: typeof MenuGroup;
|
|
32
38
|
};
|
package/dist/Menu/index.mjs
CHANGED
|
@@ -3,85 +3,112 @@ import { splitStyleProps as t } from "../styles/split-style-props.mjs";
|
|
|
3
3
|
import { Box as n } from "../Box/index.mjs";
|
|
4
4
|
import { Text as r } from "../Text/index.mjs";
|
|
5
5
|
import { PopupScope as i } from "../popup/client.mjs";
|
|
6
|
-
import { item as a,
|
|
7
|
-
import { MenuPortal as
|
|
8
|
-
import { jsx as
|
|
9
|
-
import { Children as
|
|
10
|
-
import { Menu as
|
|
6
|
+
import { item as a, linkItem as o, popup as s, separator as c } from "./Menu.css.mjs";
|
|
7
|
+
import { MenuPortal as l, MenuPositioner as u, MenuTrigger as d } from "./client.mjs";
|
|
8
|
+
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
9
|
+
import { Children as m } from "react";
|
|
10
|
+
import { Menu as h } from "@base-ui/react/menu";
|
|
11
11
|
//#region src/Menu/index.tsx
|
|
12
|
-
function
|
|
13
|
-
return /* @__PURE__ */
|
|
12
|
+
function g({ children: e, ...t }) {
|
|
13
|
+
return /* @__PURE__ */ f(i, { children: /* @__PURE__ */ f(h.Root, {
|
|
14
14
|
...t,
|
|
15
15
|
children: e
|
|
16
16
|
}) });
|
|
17
17
|
}
|
|
18
|
-
function
|
|
18
|
+
function _({ side: r = "bottom", align: i = "start", backgroundColor: a = "bgRaised", borderRadius: o = "med", boxShadow: c = "med", padding: d = "4", className: p, style: m, ...g }) {
|
|
19
19
|
let [_, v] = t(g);
|
|
20
|
-
return /* @__PURE__ */
|
|
20
|
+
return /* @__PURE__ */ f(l, { children: /* @__PURE__ */ f(u, {
|
|
21
21
|
align: i,
|
|
22
22
|
side: r,
|
|
23
|
-
children: /* @__PURE__ */
|
|
23
|
+
children: /* @__PURE__ */ f(h.Popup, {
|
|
24
24
|
...v,
|
|
25
|
-
render: /* @__PURE__ */
|
|
25
|
+
render: /* @__PURE__ */ f(n, {
|
|
26
26
|
backgroundColor: a,
|
|
27
|
-
borderRadius:
|
|
28
|
-
boxShadow:
|
|
29
|
-
padding:
|
|
27
|
+
borderRadius: o,
|
|
28
|
+
boxShadow: c,
|
|
29
|
+
padding: d,
|
|
30
30
|
..._,
|
|
31
|
-
className: e(
|
|
32
|
-
style:
|
|
31
|
+
className: e(s, p),
|
|
32
|
+
style: m
|
|
33
33
|
})
|
|
34
34
|
})
|
|
35
35
|
}) });
|
|
36
36
|
}
|
|
37
|
-
function
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
37
|
+
function v(e) {
|
|
38
|
+
return m.map(e, (e) => typeof e == "string" || typeof e == "number" ? /* @__PURE__ */ f(r, {
|
|
39
|
+
as: "span",
|
|
40
|
+
font: "label",
|
|
41
|
+
size: "sm",
|
|
42
|
+
children: e
|
|
43
|
+
}) : e);
|
|
44
|
+
}
|
|
45
|
+
function y({ variant: r = "neutral", display: i = "flex", alignItems: o = "center", gap: s = "8", minBlockSize: c = "sm", paddingInline: l = "12", borderRadius: u = "sm", color: d = r === "danger" ? "danger" : "fg", className: p, style: m, children: g, ..._ }) {
|
|
46
|
+
let [y, b] = t(_);
|
|
47
|
+
return /* @__PURE__ */ f(h.Item, {
|
|
48
|
+
...b,
|
|
49
|
+
render: /* @__PURE__ */ f(n, {
|
|
50
|
+
display: i,
|
|
51
|
+
alignItems: o,
|
|
52
|
+
gap: s,
|
|
53
|
+
minBlockSize: c,
|
|
54
|
+
paddingInline: l,
|
|
55
|
+
borderRadius: u,
|
|
56
|
+
color: d,
|
|
57
|
+
...y,
|
|
58
|
+
className: e(a, p),
|
|
59
|
+
style: m
|
|
52
60
|
}),
|
|
53
|
-
children:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
children: v(g)
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function b({ href: r, disabled: i = !1, closeOnClick: s = !0, onClick: c, display: l = "flex", alignItems: u = "center", gap: d = "8", minBlockSize: p = "sm", paddingInline: m = "12", borderRadius: g = "sm", color: _ = "fg", className: y, style: b, children: x, ...S }) {
|
|
65
|
+
let [C, w] = t(S);
|
|
66
|
+
return /* @__PURE__ */ f(h.LinkItem, {
|
|
67
|
+
...w,
|
|
68
|
+
"aria-disabled": i || void 0,
|
|
69
|
+
closeOnClick: !i && s,
|
|
70
|
+
href: i ? void 0 : r,
|
|
71
|
+
onClick: i ? void 0 : c,
|
|
72
|
+
render: /* @__PURE__ */ f(n, {
|
|
73
|
+
as: "a",
|
|
74
|
+
display: l,
|
|
75
|
+
alignItems: u,
|
|
76
|
+
gap: d,
|
|
77
|
+
minBlockSize: p,
|
|
78
|
+
paddingInline: m,
|
|
79
|
+
borderRadius: g,
|
|
80
|
+
color: _,
|
|
81
|
+
...C,
|
|
82
|
+
className: e(a, o, y),
|
|
83
|
+
style: b
|
|
84
|
+
}),
|
|
85
|
+
children: v(x)
|
|
59
86
|
});
|
|
60
87
|
}
|
|
61
|
-
function
|
|
62
|
-
let [l, u] = t(
|
|
63
|
-
return /* @__PURE__ */
|
|
88
|
+
function x({ marginBlock: r = "4", marginInline: i = "8", className: a, style: o, ...s }) {
|
|
89
|
+
let [l, u] = t(s);
|
|
90
|
+
return /* @__PURE__ */ f(h.Separator, {
|
|
64
91
|
...u,
|
|
65
|
-
render: /* @__PURE__ */
|
|
92
|
+
render: /* @__PURE__ */ f(n, {
|
|
66
93
|
marginBlock: r,
|
|
67
94
|
marginInline: i,
|
|
68
95
|
...l,
|
|
69
|
-
className: e(
|
|
96
|
+
className: e(c, a),
|
|
70
97
|
style: o
|
|
71
98
|
})
|
|
72
99
|
});
|
|
73
100
|
}
|
|
74
|
-
function
|
|
101
|
+
function S({ label: e, className: i, style: a, children: o, ...s }) {
|
|
75
102
|
let [c, l] = t(s);
|
|
76
|
-
return /* @__PURE__ */
|
|
103
|
+
return /* @__PURE__ */ p(h.Group, {
|
|
77
104
|
...l,
|
|
78
|
-
render: /* @__PURE__ */
|
|
105
|
+
render: /* @__PURE__ */ f(n, {
|
|
79
106
|
...c,
|
|
80
107
|
className: i,
|
|
81
108
|
style: a
|
|
82
109
|
}),
|
|
83
|
-
children: [/* @__PURE__ */
|
|
84
|
-
render: /* @__PURE__ */
|
|
110
|
+
children: [/* @__PURE__ */ f(h.GroupLabel, {
|
|
111
|
+
render: /* @__PURE__ */ f(r, {
|
|
85
112
|
as: "div",
|
|
86
113
|
color: "fgMuted",
|
|
87
114
|
font: "label",
|
|
@@ -93,12 +120,13 @@ function y({ label: e, className: i, style: a, children: o, ...s }) {
|
|
|
93
120
|
}), o]
|
|
94
121
|
});
|
|
95
122
|
}
|
|
96
|
-
var
|
|
97
|
-
Trigger:
|
|
98
|
-
Popup:
|
|
99
|
-
Item:
|
|
100
|
-
|
|
101
|
-
|
|
123
|
+
var C = Object.assign(g, {
|
|
124
|
+
Trigger: d,
|
|
125
|
+
Popup: _,
|
|
126
|
+
Item: y,
|
|
127
|
+
LinkItem: b,
|
|
128
|
+
Separator: x,
|
|
129
|
+
Group: S
|
|
102
130
|
});
|
|
103
131
|
//#endregion
|
|
104
|
-
export {
|
|
132
|
+
export { C as Menu };
|
package/dist/docs/Menu.md
CHANGED
|
@@ -2,31 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
A list of actions that opens from a button: a row's "Edit, Duplicate, Delete", a toolbar's overflow. Built on Base UI's Menu, which owns the keyboard and focus: arrow keys, Home and End move through the items, typing jumps to a matching item, Esc and an outside click close it, and focus returns to the trigger. The parts add the look: a raised surface with a border and shadow, a highlighted item, a zoom-and-fade that is a plain fade under reduced motion.
|
|
5
|
+
A list of actions that opens from a button: a row's "Edit, Duplicate, Delete", a toolbar's overflow. It may mix in links ("Open invoice", "Download PDF") as `Menu.LinkItem`, real anchors that keep the browser's link behavior. Built on Base UI's Menu, which owns the keyboard and focus: arrow keys, Home and End move through the items, typing jumps to a matching item, Esc and an outside click close it, and focus returns to the trigger. The parts add the look: a raised surface with a border and shadow, a highlighted item, a zoom-and-fade that is a plain fade under reduced motion.
|
|
6
6
|
|
|
7
7
|
## When to use
|
|
8
8
|
|
|
9
9
|
- Several actions on one object that would crowd the layout as buttons.
|
|
10
10
|
- Actions that need no input of their own; each item runs one thing.
|
|
11
|
+
- Actions mixed with a few links on the same object: the links are `Menu.LinkItem`.
|
|
11
12
|
|
|
12
13
|
## When not to use
|
|
13
14
|
|
|
14
15
|
- Picking a value for a form field: use a select (v0.2 forms) or radio buttons; for several values from a long list, use `MultiSelect`.
|
|
15
|
-
- Navigation between pages:
|
|
16
|
+
- Navigation between pages: a menu that is only links is still navigation, so show the links on the page or in a nav. Link items are for a menu that mixes actions and links.
|
|
16
17
|
- One or two actions: show them as buttons.
|
|
17
18
|
- Content a user reads, or anything interactive inside the list (fields, toggles): use a popover or a dialog.
|
|
18
19
|
|
|
19
20
|
## Rules
|
|
20
21
|
|
|
21
|
-
- Compose the parts: `Menu` > `Menu.Trigger` + `Menu.Popup` > `Menu.Item`, `Menu.Separator`, `Menu.Group`. `Menu` holds the open state and renders no element, so it takes no `className` or `style`; every other part does, plus style props.
|
|
22
|
+
- Compose the parts: `Menu` > `Menu.Trigger` + `Menu.Popup` > `Menu.Item`, `Menu.LinkItem`, `Menu.Separator`, `Menu.Group`. `Menu` holds the open state and renders no element, so it takes no `className` or `style`; every other part does, plus style props.
|
|
22
23
|
- `Menu.Trigger` is a bare `Clickable`: give it its look with style props (or your own class), and an accessible name. An icon-only trigger needs `aria-label`.
|
|
23
24
|
- For a trigger that reads as a button ("Actions", "New"), pass Button's `variant`, `size`, `iconStart` or `iconEnd` (a `ChevronDownIcon` for "Actions ▾"): it renders a `Button`, with its fill, border and height, and keeps the trigger's behavior and `aria-disabled`. While the menu is open it shows Button's open look, a held tint and an inset ring (ADR 0063); a bare trigger has none, so style it yourself off `aria-expanded`. Style props still override the defaults.
|
|
24
25
|
- `Menu.Item` runs its `onClick` on click, Enter and Space, then the menu closes. Pass `closeOnClick={false}` to keep it open.
|
|
26
|
+
- `Menu.LinkItem` is a real `<a role="menuitem">` for an entry that goes somewhere. `href` is required; `target`, `rel` and `download` pass through. Hover shows the URL, Cmd/Ctrl-click and middle-click open a new tab, and a click, Enter or Space follows it and closes the menu. Never navigate from a `Menu.Item`'s `onClick` (`location.assign`, a router's `navigate`): it loses all of that. Router-aware (client-side) links are not offered. It takes no `variant`.
|
|
25
27
|
- `variant="danger"` marks a destructive item. It still runs on one click, so open a confirm dialog from it (Cancel focused first) before anything irreversible happens.
|
|
26
|
-
- `disabled` on an item renders `aria-disabled`: it stays announced and reachable with the arrow keys, and does nothing. On the trigger it keeps the button focusable and stops it opening.
|
|
28
|
+
- `disabled` on an item renders `aria-disabled`: it stays announced and reachable with the arrow keys, and does nothing. A disabled `Menu.LinkItem` also drops its `href`, so no click or context menu reaches the URL. On the trigger it keeps the button focusable and stops it opening.
|
|
27
29
|
- `Menu.Group` takes a `label` that is shown and names the group. Separate groups with `Menu.Separator`.
|
|
28
30
|
- The popup portals into the trigger's nearest `ThemeProvider`, so it keeps the brand and mode. That provider must not clip or stack its content: wrap the app root, and keep a nested provider off elements with `overflow: hidden`, a `transform` or their own `z-index`, or the popup is clipped or drawn under siblings. It sits on `zIndex` `popover`. A menu opened only through `open`, with no `Menu.Trigger`, has no provider to find and portals to `<body>`.
|
|
29
|
-
- Item text is wrapped in `Text` for you; an `Icon` beside it is spaced by the item's `gap`.
|
|
31
|
+
- Item and link item text is wrapped in `Text` for you; an `Icon` beside it is spaced by the item's `gap`.
|
|
30
32
|
- `side` (`bottom` by default) and `align` (`start`) place the popup against the trigger; it flips when it would overflow the viewport.
|
|
31
33
|
- Never add key handlers or `tabIndex` to the parts; Base UI handles the keyboard.
|
|
32
34
|
- The module has no hooks, so a server component can render the parts; `onClick` and other functions must come from a client component.
|
|
@@ -69,6 +71,18 @@ A list of actions that opens from a button: a row's "Edit, Duplicate, Delete", a
|
|
|
69
71
|
</Menu>
|
|
70
72
|
```
|
|
71
73
|
|
|
74
|
+
```tsx
|
|
75
|
+
// Actions and links on one invoice: the links are real anchors.
|
|
76
|
+
<Menu.Popup>
|
|
77
|
+
<Menu.Item onClick={edit}>Edit</Menu.Item>
|
|
78
|
+
<Menu.Separator />
|
|
79
|
+
<Menu.LinkItem href={`/invoices/${id}`}>Open invoice</Menu.LinkItem>
|
|
80
|
+
<Menu.LinkItem href={`/invoices/${id}.pdf`} target="_blank" rel="noopener">
|
|
81
|
+
Download PDF
|
|
82
|
+
</Menu.LinkItem>
|
|
83
|
+
</Menu.Popup>
|
|
84
|
+
```
|
|
85
|
+
|
|
72
86
|
## Don't
|
|
73
87
|
|
|
74
88
|
```tsx
|
|
@@ -84,16 +98,24 @@ A list of actions that opens from a button: a row's "Edit, Duplicate, Delete", a
|
|
|
84
98
|
```
|
|
85
99
|
|
|
86
100
|
```tsx
|
|
87
|
-
//
|
|
101
|
+
// Navigates from an action: no URL on hover, no new tab. Use <Menu.LinkItem href="/settings">.
|
|
88
102
|
<Menu.Item onClick={() => navigate("/settings")}>Settings</Menu.Item>
|
|
89
103
|
```
|
|
90
104
|
|
|
105
|
+
```tsx
|
|
106
|
+
// Only links: this is navigation. Show the links on the page or in a nav.
|
|
107
|
+
<Menu.Popup>
|
|
108
|
+
<Menu.LinkItem href="/settings">Settings</Menu.LinkItem>
|
|
109
|
+
<Menu.LinkItem href="/billing">Billing</Menu.LinkItem>
|
|
110
|
+
</Menu.Popup>
|
|
111
|
+
```
|
|
112
|
+
|
|
91
113
|
## Accessibility
|
|
92
114
|
|
|
93
|
-
- The trigger has `aria-haspopup="menu"` and `aria-expanded`; the popup is `role="menu"` labelled by the trigger, items are `role="menuitem"`, groups are `role="group"` named by their label.
|
|
115
|
+
- The trigger has `aria-haspopup="menu"` and `aria-expanded`; the popup is `role="menu"` labelled by the trigger, items and link items are `role="menuitem"` (a link item is an `<a>` with an `href`, so the browser treats it as a link), groups are `role="group"` named by their label.
|
|
94
116
|
- Opening from the keyboard focuses the first item; from the pointer, the popup. Focus stays in the menu until it closes, then returns to the trigger.
|
|
95
117
|
- A closed menu stays on the page while it fades out, but is `aria-hidden` from the moment it closes, so a menu opened during the fade (the next row's) is the only one a screen reader or a role query finds. Nothing in it is in the tab order. Focus stays in it until the fade ends, then returns to the trigger.
|
|
96
|
-
- The highlighted item gets a fill (`bgMuted` in light mode; in dark mode `bgRaised` with 10% `fg` mixed in, since `bgMuted` sits too close to the popup there), and the shared focus ring when reached from the keyboard. Under forced colors it is `Highlight` with `HighlightText`, disabled items are `GrayText
|
|
118
|
+
- The highlighted item gets a fill (`bgMuted` in light mode; in dark mode `bgRaised` with 10% `fg` mixed in, since `bgMuted` sits too close to the popup there), and the shared focus ring when reached from the keyboard. Under forced colors it is `Highlight` with `HighlightText`, disabled items are `GrayText` on the page's fill even when highlighted (`GrayText` pairs with `Canvas` only), and the popup keeps its border.
|
|
97
119
|
- The danger item's `danger` color passes 4.5:1 on the popup and on the highlight fill in every preset (`Menu.test.tsx` checks it, with `fg` and `fgMuted`); the word ("Delete") carries the meaning, not the color.
|
|
98
120
|
|
|
99
121
|
## Props
|
|
@@ -139,6 +161,20 @@ Also accepts the style props (see `manifest.json` `styleProps`).
|
|
|
139
161
|
|
|
140
162
|
Also accepts the style props (see `manifest.json` `styleProps`).
|
|
141
163
|
|
|
164
|
+
### Menu.LinkItem
|
|
165
|
+
|
|
166
|
+
| Prop | Type | Required | Description |
|
|
167
|
+
| --- | --- | --- | --- |
|
|
168
|
+
| `className` | `string` | no | |
|
|
169
|
+
| `closeOnClick` | `boolean` | no | Whether to close the menu when the item is clicked. |
|
|
170
|
+
| `disabled` | `boolean` | no | |
|
|
171
|
+
| `href` | `string` | yes | |
|
|
172
|
+
| `id` | `string` | no | |
|
|
173
|
+
| `label` | `string` | no | Overrides the text used for keyboard text navigation and filtering. Falls back to the rendered text when not provided. |
|
|
174
|
+
| `style` | `CSSProperties` | no | |
|
|
175
|
+
|
|
176
|
+
Also accepts the style props (see `manifest.json` `styleProps`).
|
|
177
|
+
|
|
142
178
|
### Menu.Popup
|
|
143
179
|
|
|
144
180
|
| Prop | Type | Required | Description |
|
package/dist/docs/llms.txt
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- [Icon](Icon.md): The base for every icon: an `<svg>` on a 24-unit grid that draws its children with round 2-unit strokes in `currentColor`. Generated icons from `@jonmilner/ui/icons` (`ArrowRightIcon`, …) wrap it and take the same props, less `children` and `viewBox`. `size` takes a `fontSize` step, so an icon matches the `Text` beside it; `color` takes a color role.
|
|
18
18
|
- [IconButton](IconButton.md): A compact button whose only visible content is an icon, for an action the icon makes plain: close, edit, refresh. A required `label` names it for assistive technology (`aria-label`); the icon is decorative. It is a `<button type="button">` on `Clickable`, so it has the shared focus ring, the 24px minimum target and `aria-disabled` that blocks activation while the button stays focusable. Two variants from Button's fills (`primary`, `secondary`) and three square sizes at Button's heights (`sm`, `med`, `lg`: 32, 40 and 48 px).
|
|
19
19
|
- [Inline](Inline.md): A horizontal row of children that wraps onto new lines, with even space between them on both axes. `Inline` is a `Box` with `display="flex"`, `flexDirection="row"`, `flexWrap="wrap"` and `alignItems="center"` as default style props, spaced by a required `gap`. Children render as they are: no wrapper per child.
|
|
20
|
-
- [Menu](Menu.md): A list of actions that opens from a button: a row's "Edit, Duplicate, Delete", a toolbar's overflow. Built on Base UI's Menu, which owns the keyboard and focus: arrow keys, Home and End move through the items, typing jumps to a matching item, Esc and an outside click close it, and focus returns to the trigger. The parts add the look: a raised surface with a border and shadow, a highlighted item, a zoom-and-fade that is a plain fade under reduced motion.
|
|
20
|
+
- [Menu](Menu.md): A list of actions that opens from a button: a row's "Edit, Duplicate, Delete", a toolbar's overflow. It may mix in links ("Open invoice", "Download PDF") as `Menu.LinkItem`, real anchors that keep the browser's link behavior. Built on Base UI's Menu, which owns the keyboard and focus: arrow keys, Home and End move through the items, typing jumps to a matching item, Esc and an outside click close it, and focus returns to the trigger. The parts add the look: a raised surface with a border and shadow, a highlighted item, a zoom-and-fade that is a plain fade under reduced motion.
|
|
21
21
|
- [MultiSelect](MultiSelect.md): A form field for picking any number of values from a list too long to show at once. A trigger styled as Button's secondary look opens a listbox of options; each selected option shows a check mark, and the trigger summarises the selection as the joined labels, or a count ("3 selected") once the labels would not fit. Built on Base UI's `Select` in multiple mode (ADR 0003), which owns the keyboard and focus: arrow keys, Home and End move through the options, typing jumps to a match, Esc and an outside click close it, and focus returns to the trigger. It composes `Field` for its label, description and error (ADR 0026).
|
|
22
22
|
- [SearchField](SearchField.md): A form field for a search query: an `<input type="search">` (role `searchbox`) with a search icon at its inline start and, while it holds a value, a clear button at its inline end. Escape empties a field that has a value, and the clear button empties it and puts focus back in the input, so a keyboard user never loses their place (ADR 0087). Built on Base UI's `Input` (ADR 0003); it composes `Field` for its label, description and error (ADR 0026). Three sizes at Button's heights (`sm`, `med`, `lg`: 32, 40 and 48 px).
|
|
23
23
|
- [Stack](Stack.md): A vertical column of children with even space between them. `Stack` is a `Box` with `display="flex"` and `flexDirection="column"` as default style props, spaced by a required `gap`. Children render as they are: no wrapper per child.
|
package/dist/docs/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./manifest.schema.json",
|
|
3
3
|
"package": "@jonmilner/ui",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.1",
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
7
7
|
"name": "AlertDialog",
|
|
@@ -1205,6 +1205,49 @@
|
|
|
1205
1205
|
}
|
|
1206
1206
|
]
|
|
1207
1207
|
},
|
|
1208
|
+
{
|
|
1209
|
+
"name": "Menu.LinkItem",
|
|
1210
|
+
"styleProps": true,
|
|
1211
|
+
"props": [
|
|
1212
|
+
{
|
|
1213
|
+
"name": "className",
|
|
1214
|
+
"type": "string",
|
|
1215
|
+
"required": false
|
|
1216
|
+
},
|
|
1217
|
+
{
|
|
1218
|
+
"name": "closeOnClick",
|
|
1219
|
+
"type": "boolean",
|
|
1220
|
+
"required": false,
|
|
1221
|
+
"description": "Whether to close the menu when the item is clicked."
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
"name": "disabled",
|
|
1225
|
+
"type": "boolean",
|
|
1226
|
+
"required": false
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
"name": "href",
|
|
1230
|
+
"type": "string",
|
|
1231
|
+
"required": true
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
"name": "id",
|
|
1235
|
+
"type": "string",
|
|
1236
|
+
"required": false
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
"name": "label",
|
|
1240
|
+
"type": "string",
|
|
1241
|
+
"required": false,
|
|
1242
|
+
"description": "Overrides the text used for keyboard text navigation and filtering.\nFalls back to the rendered text when not provided."
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
"name": "style",
|
|
1246
|
+
"type": "CSSProperties",
|
|
1247
|
+
"required": false
|
|
1248
|
+
}
|
|
1249
|
+
]
|
|
1250
|
+
},
|
|
1208
1251
|
{
|
|
1209
1252
|
"name": "Menu.Popup",
|
|
1210
1253
|
"styleProps": true,
|
package/dist/index.d.mts
CHANGED
|
@@ -14,7 +14,7 @@ import { Heading, HeadingLevel, HeadingProps, HeadingSize } from "./Heading/inde
|
|
|
14
14
|
import { IconButton, IconButtonProps } from "./IconButton/index.mjs";
|
|
15
15
|
import { Inline, InlineProps } from "./Inline/index.mjs";
|
|
16
16
|
import { MenuTriggerProps } from "./Menu/client.mjs";
|
|
17
|
-
import { Menu, MenuGroupProps, MenuItemProps, MenuPopupProps, MenuProps, MenuSeparatorProps } from "./Menu/index.mjs";
|
|
17
|
+
import { Menu, MenuGroupProps, MenuItemProps, MenuLinkItemProps, MenuPopupProps, MenuProps, MenuSeparatorProps } from "./Menu/index.mjs";
|
|
18
18
|
import { MultiSelect, MultiSelectOption, MultiSelectProps } from "./MultiSelect/index.mjs";
|
|
19
19
|
import { SearchFieldInputProps } from "./SearchField/client.mjs";
|
|
20
20
|
import { SearchField, SearchFieldProps } from "./SearchField/index.mjs";
|
|
@@ -26,4 +26,4 @@ import { Toast } from "./Toast/index.mjs";
|
|
|
26
26
|
import { VisuallyHidden, VisuallyHiddenProps } from "./VisuallyHidden/index.mjs";
|
|
27
27
|
import { Theme, defineTheme } from "./theme/define-theme.mjs";
|
|
28
28
|
import { ThemeProvider, ThemeProviderProps } from "./theme/ThemeProvider.mjs";
|
|
29
|
-
export { AlertDialog, type AlertDialogProps, type AlertDialogTriggerProps, Badge, type BadgeProps, Box, type BoxProps, Button, type ButtonProps, Checkbox, type CheckboxProps, Chip, ChipGroup, type ChipGroupProps, type ChipProps, Clickable, type ClickableProps, Dialog, type DialogActionsProps, type DialogBodyProps, type DialogCloseProps, type DialogPopupProps, type DialogProps, type DialogTriggerProps, Field, type FieldDescriptionProps, type FieldErrorProps, type FieldLabelProps, type FieldProps, Heading, type HeadingLevel, type HeadingProps, type HeadingSize, Icon, IconButton, type IconButtonProps, type IconProps, Inline, type InlineProps, Menu, type MenuGroupProps, type MenuItemProps, type MenuPopupProps, type MenuProps, type MenuSeparatorProps, type MenuTriggerProps, MultiSelect, type MultiSelectOption, type MultiSelectProps, SearchField, type SearchFieldInputProps, type SearchFieldProps, Stack, type StackProps, type StyleProps, Tabs, type TabsListProps, type TabsPanelProps, type TabsProps, type TabsTabProps, Tag, type TagProps, Text, type TextProps, type Theme, ThemeProvider, type ThemeProviderProps, Toast, type ToastOptions, type ToastProviderProps, type ToastVariant, VisuallyHidden, type VisuallyHiddenProps, defineTheme, useToast };
|
|
29
|
+
export { AlertDialog, type AlertDialogProps, type AlertDialogTriggerProps, Badge, type BadgeProps, Box, type BoxProps, Button, type ButtonProps, Checkbox, type CheckboxProps, Chip, ChipGroup, type ChipGroupProps, type ChipProps, Clickable, type ClickableProps, Dialog, type DialogActionsProps, type DialogBodyProps, type DialogCloseProps, type DialogPopupProps, type DialogProps, type DialogTriggerProps, Field, type FieldDescriptionProps, type FieldErrorProps, type FieldLabelProps, type FieldProps, Heading, type HeadingLevel, type HeadingProps, type HeadingSize, Icon, IconButton, type IconButtonProps, type IconProps, Inline, type InlineProps, Menu, type MenuGroupProps, type MenuItemProps, type MenuLinkItemProps, type MenuPopupProps, type MenuProps, type MenuSeparatorProps, type MenuTriggerProps, MultiSelect, type MultiSelectOption, type MultiSelectProps, SearchField, type SearchFieldInputProps, type SearchFieldProps, Stack, type StackProps, type StyleProps, Tabs, type TabsListProps, type TabsPanelProps, type TabsProps, type TabsTabProps, Tag, type TagProps, Text, type TextProps, type Theme, ThemeProvider, type ThemeProviderProps, Toast, type ToastOptions, type ToastProviderProps, type ToastVariant, VisuallyHidden, type VisuallyHiddenProps, defineTheme, useToast };
|
package/dist/lint/index.mjs
CHANGED