@jonmilner/ui 0.4.4 → 0.5.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.
@@ -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.text-field, components.toast, components.visually-hidden;
2
- @layer components.menu{._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}}}
2
+ @layer components.menu{._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-color:var(--color-border)}._393eil4:before{content:"";border-block-start:var(--space-1) solid;border-block-start-color:inherit;display:block}@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}}}
@@ -2,65 +2,47 @@
2
2
  import { join as e } from "../styles/join.mjs";
3
3
  import { splitStyleProps as t } from "../styles/split-style-props.mjs";
4
4
  import { Box as n } from "../Box/index.mjs";
5
- import { PopupScope as r, triggerElement as i, usePopupScope as a } from "../popup/client.mjs";
6
- import { positioner as o } from "./Menu.css.mjs";
7
- import { jsx as s } from "react/jsx-runtime";
8
- import { createContext as c, use as l, useCallback as u, useLayoutEffect as d, useRef as f } from "react";
9
- import { Menu as p } from "@base-ui/react/menu";
5
+ import { PopupScope as r, triggerElement as i, useClosingRule as a, usePopupScope as o, useRecordOpenChange as s } from "../popup/client.mjs";
6
+ import { positioner as c } from "./Menu.css.mjs";
7
+ import { jsx as l } from "react/jsx-runtime";
8
+ import { Menu as u } from "@base-ui/react/menu";
10
9
  //#region src/Menu/client.tsx
11
- function m({ disabled: e = !1, openOnHover: t, delay: n, closeDelay: r, handle: o, payload: c, ...l }) {
12
- let { ref: u } = a();
13
- return /* @__PURE__ */ s(p.Trigger, {
10
+ function d({ disabled: e = !1, openOnHover: t, delay: n, closeDelay: r, handle: a, payload: s, ...c }) {
11
+ let { ref: d } = o();
12
+ return /* @__PURE__ */ l(u.Trigger, {
14
13
  disabled: e,
15
14
  openOnHover: t,
16
15
  delay: n,
17
16
  closeDelay: r,
18
- handle: o,
19
- payload: c,
20
- ref: u,
21
- render: i(l)
17
+ handle: a,
18
+ payload: s,
19
+ ref: d,
20
+ render: i(c)
22
21
  });
23
22
  }
24
- function h({ children: e }) {
25
- let { container: t } = a();
26
- return /* @__PURE__ */ s(p.Portal, {
23
+ function f({ children: e }) {
24
+ let { container: t } = o();
25
+ return /* @__PURE__ */ l(u.Portal, {
27
26
  container: t,
28
27
  children: e
29
28
  });
30
29
  }
31
- var g = c({ current: {
32
- keyboard: !1,
33
- opener: null
34
- } });
35
- function _({ onOpenChange: e, ...t }) {
36
- let n = f({
37
- keyboard: !1,
38
- opener: null
30
+ function p(e) {
31
+ return /* @__PURE__ */ l(r, { children: /* @__PURE__ */ l(m, { ...e }) });
32
+ }
33
+ function m({ onOpenChange: e, ...t }) {
34
+ return /* @__PURE__ */ l(u.Root, {
35
+ ...t,
36
+ onOpenChange: s(e)
39
37
  });
40
- return /* @__PURE__ */ s(r, { children: /* @__PURE__ */ s(g, {
41
- value: n,
42
- children: /* @__PURE__ */ s(p.Root, {
43
- ...t,
44
- onOpenChange: (t, r) => {
45
- n.current = t ? {
46
- keyboard: !1,
47
- opener: r.trigger ?? null
48
- } : {
49
- ...n.current,
50
- keyboard: r.event instanceof KeyboardEvent,
51
- reason: r.reason
52
- }, e?.(t, r);
53
- }
54
- })
55
- }) });
56
38
  }
57
- function v({ finalFocus: e, ...n }) {
58
- return /* @__PURE__ */ s(p.Positioner, {
39
+ function h({ finalFocus: e, ...n }) {
40
+ return /* @__PURE__ */ l(u.Positioner, {
59
41
  ...n,
60
42
  collisionPadding: 8,
61
43
  render: (n, { open: r }) => {
62
44
  let [, i] = t(n);
63
- return /* @__PURE__ */ s(b, {
45
+ return /* @__PURE__ */ l(g, {
64
46
  ...i,
65
47
  finalFocus: e,
66
48
  open: r
@@ -69,44 +51,16 @@ function v({ finalFocus: e, ...n }) {
69
51
  sideOffset: 4
70
52
  });
71
53
  }
72
- var y = /* @__PURE__ */ new Set([
73
- "trigger-hover",
74
- "focus-out",
75
- "outside-press"
76
- ]);
77
- function b({ open: t, finalFocus: r, ref: i, className: c, ...p }) {
78
- let { trigger: m } = a(), h = l(g), _ = f(null), v = u((e) => {
79
- _.current = e;
80
- let t = typeof i == "function" ? i(e) : void 0;
81
- return i && typeof i != "function" && (i.current = e), () => {
82
- _.current = null, typeof t == "function" ? t() : typeof i == "function" ? i(null) : i && (i.current = null);
83
- };
84
- }, [i]);
85
- return d(() => {
86
- let e = _.current;
87
- if (!e) return;
88
- if (t) {
89
- e.inert = !1;
90
- return;
91
- }
92
- let { reason: n, keyboard: i, opener: a } = h.current, o = a?.isConnected ? a : m.current;
93
- if (e.contains(e.ownerDocument.activeElement) && !r && !y.has(n ?? "") && o instanceof HTMLElement) {
94
- let e = { preventScroll: !0 };
95
- i && (e.focusVisible = !0), o.focus(e);
96
- }
97
- e.inert = !0;
98
- }, [
99
- t,
100
- r,
101
- h,
102
- m
103
- ]), /* @__PURE__ */ s(n, {
104
- ...p,
105
- "aria-hidden": !t || void 0,
106
- className: e(o, c),
107
- ref: v,
54
+ function g({ open: t, finalFocus: r, ref: i, className: o, ...s }) {
55
+ return /* @__PURE__ */ l(n, {
56
+ ...s,
57
+ ...a(i, {
58
+ finalFocus: r,
59
+ open: t
60
+ }),
61
+ className: e(c, o),
108
62
  zIndex: "popover"
109
63
  });
110
64
  }
111
65
  //#endregion
112
- export { h as MenuPortal, v as MenuPositioner, _ as MenuRootScope, m as MenuTrigger };
66
+ export { f as MenuPortal, h as MenuPositioner, p as MenuRootScope, d as MenuTrigger };
@@ -26,7 +26,7 @@ declare function MenuRoot(props: MenuProps): import("react").JSX.Element;
26
26
  declare function MenuPopup({ side, align, backgroundColor, borderRadius, boxShadow, padding, className, style, ...props }: MenuPopupProps): import("react").JSX.Element;
27
27
  declare function MenuItem({ variant, display, alignItems, gap, minBlockSize, paddingInline, borderRadius, color, className, style, children, ...props }: MenuItemProps): import("react").JSX.Element;
28
28
  declare function MenuLinkItem({ href, disabled, closeOnClick, onClick, display, alignItems, gap, minBlockSize, paddingInline, borderRadius, color, className, style, children, ...props }: MenuLinkItemProps): import("react").JSX.Element;
29
- declare function MenuSeparator({ marginBlock, marginInline, className, style, ...props }: MenuSeparatorProps): import("react").JSX.Element;
29
+ declare function MenuSeparator({ paddingBlock, paddingInline, className, style, ...props }: MenuSeparatorProps): import("react").JSX.Element;
30
30
  declare function MenuGroup({ label, className, style, children, ...props }: MenuGroupProps): import("react").JSX.Element;
31
31
  export declare const Menu: typeof MenuRoot & {
32
32
  Trigger: typeof MenuTrigger;
@@ -82,13 +82,13 @@ function b({ href: r, disabled: o = !1, closeOnClick: s = !0, onClick: c, displa
82
82
  children: v(x)
83
83
  });
84
84
  }
85
- function x({ marginBlock: r = "4", marginInline: i = "8", className: a, style: o, ...c }) {
85
+ function x({ paddingBlock: r = "4", paddingInline: i = "8", className: a, style: o, ...c }) {
86
86
  let [l, u] = t(c);
87
87
  return /* @__PURE__ */ f(h.Separator, {
88
88
  ...u,
89
89
  render: /* @__PURE__ */ f(n, {
90
- marginBlock: r,
91
- marginInline: i,
90
+ paddingBlock: r,
91
+ paddingInline: i,
92
92
  ...l,
93
93
  className: e(s, a),
94
94
  style: o
@@ -1,4 +1,5 @@
1
1
  import { ReactNode } from "react";
2
+ import "@base-ui/react/select";
2
3
  //#region src/MultiSelect/client.d.ts
3
4
  export type Option = {
4
5
  disabled?: boolean;
@@ -1,56 +1,57 @@
1
1
  "use client";
2
- import { Box as e } from "../Box/index.mjs";
3
- import { Text as t } from "../Text/index.mjs";
4
- import { PopupScope as n, triggerElement as r, usePopupScope as i } from "../popup/client.mjs";
5
- import { Field as a } from "../Field/index.mjs";
6
- import { ChevronDownIcon as o } from "../icons/ChevronDownIcon.mjs";
7
- import { labels as s, popup as c, summary as l, trigger as u, value as d } from "./MultiSelect.css.mjs";
8
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
9
- import { createContext as m, use as h, useId as g, useLayoutEffect as _, useMemo as v, useRef as y, useState as b } from "react";
10
- import { Select as x } from "@base-ui/react/select";
2
+ import { splitStyleProps as e } from "../styles/split-style-props.mjs";
3
+ import { Box as t } from "../Box/index.mjs";
4
+ import { Text as n } from "../Text/index.mjs";
5
+ import { PopupScope as r, triggerElement as i, useClosingRule as a, usePopupScope as o, useRecordOpenChange as s } from "../popup/client.mjs";
6
+ import { Field as c } from "../Field/index.mjs";
7
+ import { ChevronDownIcon as l } from "../icons/ChevronDownIcon.mjs";
8
+ import { labels as u, popup as d, summary as f, trigger as p, value as m } from "./MultiSelect.css.mjs";
9
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
10
+ import { createContext as _, use as v, useId as y, useLayoutEffect as b, useMemo as x, useRef as S, useState as C } from "react";
11
+ import { Select as w } from "@base-ui/react/select";
11
12
  //#region src/MultiSelect/client.tsx
12
- function S({ options: e, placeholder: t, countLabel: n, disabled: a }) {
13
- let { ref: s } = i(), c = v(() => new Map(e.map((e) => [e.value, e.label])), [e]);
14
- return /* @__PURE__ */ f(x.Trigger, {
15
- disabled: a,
16
- ref: s,
17
- render: r({
13
+ function T({ options: e, placeholder: t, countLabel: n, disabled: r }) {
14
+ let { ref: a } = o(), s = x(() => new Map(e.map((e) => [e.value, e.label])), [e]);
15
+ return /* @__PURE__ */ h(w.Trigger, {
16
+ disabled: r,
17
+ ref: a,
18
+ render: i({
18
19
  variant: "secondary",
19
- iconEnd: o,
20
- className: u,
21
- children: /* @__PURE__ */ f(x.Value, {
22
- className: d,
20
+ iconEnd: l,
21
+ className: p,
22
+ children: /* @__PURE__ */ h(w.Value, {
23
+ className: m,
23
24
  placeholder: t,
24
- children: (e) => e.length > 0 ? /* @__PURE__ */ f(C, {
25
+ children: (e) => e.length > 0 ? /* @__PURE__ */ h(E, {
25
26
  count: n.replaceAll("{count}", String(e.length)),
26
- labels: e.map((e) => c.get(e) ?? e)
27
+ labels: e.map((e) => s.get(e) ?? e)
27
28
  }) : t
28
29
  })
29
30
  })
30
31
  });
31
32
  }
32
- function C({ labels: n, count: r }) {
33
- let i = y(null), [a, o] = b(!0), c = n.join(", ");
34
- _(() => {
33
+ function E({ labels: e, count: r }) {
34
+ let i = S(null), [a, o] = C(!0), s = e.join(", ");
35
+ b(() => {
35
36
  let e = i.current;
36
37
  if (!e) return;
37
38
  let t = () => o(e.scrollWidth <= e.clientWidth);
38
39
  t(), document.fonts?.ready.then(t);
39
40
  let n = new ResizeObserver(t);
40
41
  return n.observe(e), () => n.disconnect();
41
- }, [c]);
42
- let u = !a && n.length > 1;
43
- return /* @__PURE__ */ p(e, {
42
+ }, [s]);
43
+ let c = !a && e.length > 1;
44
+ return /* @__PURE__ */ g(t, {
44
45
  as: "span",
45
- className: l,
46
- children: [/* @__PURE__ */ f(t, {
47
- "aria-hidden": u || void 0,
46
+ className: f,
47
+ children: [/* @__PURE__ */ h(n, {
48
+ "aria-hidden": c || void 0,
48
49
  as: "span",
49
- className: s,
50
+ className: u,
50
51
  ref: i,
51
52
  truncate: "box",
52
- children: c
53
- }), u && /* @__PURE__ */ f(t, {
53
+ children: s
54
+ }), c && /* @__PURE__ */ h(n, {
54
55
  as: "span",
55
56
  className: "_1seauxl4",
56
57
  truncate: "box",
@@ -58,39 +59,73 @@ function C({ labels: n, count: r }) {
58
59
  })]
59
60
  });
60
61
  }
61
- var w = m(void 0);
62
- function T({ children: e }) {
63
- return /* @__PURE__ */ f(n, { children: /* @__PURE__ */ f(w, {
64
- value: g(),
62
+ var D = _(void 0);
63
+ function O({ children: e }) {
64
+ return /* @__PURE__ */ h(r, { children: /* @__PURE__ */ h(D, {
65
+ value: y(),
65
66
  children: e
66
67
  }) });
67
68
  }
68
- function E({ children: e }) {
69
- return /* @__PURE__ */ f(a.Label, {
70
- id: h(w),
69
+ function k(e) {
70
+ return /* @__PURE__ */ h(w.Root, {
71
+ ...e,
72
+ multiple: !0,
73
+ onOpenChange: s()
74
+ });
75
+ }
76
+ function A({ children: e }) {
77
+ return /* @__PURE__ */ h(c.Label, {
78
+ id: v(D),
71
79
  nativeLabel: !1,
72
80
  children: e
73
81
  });
74
82
  }
75
- function D({ children: t }) {
76
- return /* @__PURE__ */ f(x.Popup, {
77
- "aria-labelledby": h(w),
78
- render: /* @__PURE__ */ f(e, {
83
+ function j({ children: e }) {
84
+ return /* @__PURE__ */ h(w.Popup, {
85
+ "aria-labelledby": v(D),
86
+ render: /* @__PURE__ */ h(t, {
79
87
  backgroundColor: "bgRaised",
80
88
  borderRadius: "med",
81
89
  boxShadow: "med",
82
- className: c,
90
+ className: d,
83
91
  padding: "4"
84
92
  }),
85
- children: t
93
+ children: e
86
94
  });
87
95
  }
88
- function O({ children: e }) {
89
- let { container: t } = i();
90
- return /* @__PURE__ */ f(x.Portal, {
96
+ function M({ children: e }) {
97
+ let { container: t } = o();
98
+ return /* @__PURE__ */ h(w.Portal, {
91
99
  container: t,
92
100
  children: e
93
101
  });
94
102
  }
103
+ function N({ children: t }) {
104
+ return /* @__PURE__ */ h(w.Positioner, {
105
+ align: "start",
106
+ alignItemWithTrigger: !1,
107
+ collisionPadding: 8,
108
+ render: (t, { open: n }) => {
109
+ let [, r] = e(t);
110
+ return /* @__PURE__ */ h(P, {
111
+ ...r,
112
+ open: n
113
+ });
114
+ },
115
+ side: "bottom",
116
+ sideOffset: 4,
117
+ children: t
118
+ });
119
+ }
120
+ function P({ open: e, ref: n, ...r }) {
121
+ return /* @__PURE__ */ h(t, {
122
+ ...r,
123
+ ...a(n, {
124
+ finalFocus: !1,
125
+ open: e
126
+ }),
127
+ zIndex: "popover"
128
+ });
129
+ }
95
130
  //#endregion
96
- export { E as MultiSelectLabel, D as MultiSelectPopup, O as MultiSelectPortal, T as MultiSelectScope, S as MultiSelectTrigger };
131
+ export { A as MultiSelectLabel, j as MultiSelectPopup, M as MultiSelectPortal, N as MultiSelectPositioner, k as MultiSelectRoot, O as MultiSelectScope, T as MultiSelectTrigger };
@@ -3,73 +3,64 @@ import { Text as t } from "../Text/index.mjs";
3
3
  import { CheckIcon as n } from "../icons/CheckIcon.mjs";
4
4
  import { Field as r } from "../Field/index.mjs";
5
5
  import { check as i, item as a } from "./MultiSelect.css.mjs";
6
- import { MultiSelectLabel as o, MultiSelectPopup as s, MultiSelectPortal as c, MultiSelectScope as l, MultiSelectTrigger as u } from "./client.mjs";
7
- import { jsx as d, jsxs as f } from "react/jsx-runtime";
8
- import { Select as p } from "@base-ui/react/select";
6
+ import { MultiSelectLabel as o, MultiSelectPopup as s, MultiSelectPortal as c, MultiSelectPositioner as l, MultiSelectRoot as u, MultiSelectScope as d, MultiSelectTrigger as f } from "./client.mjs";
7
+ import { jsx as p, jsxs as m } from "react/jsx-runtime";
8
+ import { Select as h } from "@base-ui/react/select";
9
9
  //#region src/MultiSelect/index.tsx
10
- function m({ options: m, value: h, defaultValue: g, onValueChange: _, label: v, description: y, error: b, placeholder: x = "Select", countLabel: S = "{count} selected", disabled: C = !1, required: w = !1, name: T, className: E, style: D, ...O }) {
11
- return /* @__PURE__ */ f(r, {
12
- ...O,
13
- className: E,
14
- disabled: C,
15
- invalid: b != null && b !== !1 && b !== "",
16
- style: D,
10
+ function g({ options: g, value: _, defaultValue: v, onValueChange: y, label: b, description: x, error: S, placeholder: C = "Select", countLabel: w = "{count} selected", disabled: T = !1, required: E = !1, name: D, className: O, style: k, ...A }) {
11
+ return /* @__PURE__ */ m(r, {
12
+ ...A,
13
+ className: O,
14
+ disabled: T,
15
+ invalid: S != null && S !== !1 && S !== "",
16
+ style: k,
17
17
  children: [
18
- /* @__PURE__ */ d(l, { children: /* @__PURE__ */ f(p.Root, {
19
- defaultValue: g,
20
- disabled: C,
21
- multiple: !0,
22
- name: T,
23
- onValueChange: _,
24
- required: w,
25
- value: h,
18
+ /* @__PURE__ */ p(d, { children: /* @__PURE__ */ m(u, {
19
+ defaultValue: v,
20
+ disabled: T,
21
+ name: D,
22
+ onValueChange: y,
23
+ required: E,
24
+ value: _,
26
25
  children: [
27
- /* @__PURE__ */ d(o, { children: v }),
28
- /* @__PURE__ */ d(u, {
29
- countLabel: S,
30
- disabled: C,
31
- options: m,
32
- placeholder: x
26
+ /* @__PURE__ */ p(o, { children: b }),
27
+ /* @__PURE__ */ p(f, {
28
+ countLabel: w,
29
+ disabled: T,
30
+ options: g,
31
+ placeholder: C
33
32
  }),
34
- /* @__PURE__ */ d(c, { children: /* @__PURE__ */ d(p.Positioner, {
35
- align: "start",
36
- alignItemWithTrigger: !1,
37
- collisionPadding: 8,
38
- render: /* @__PURE__ */ d(e, { zIndex: "popover" }),
39
- side: "bottom",
40
- sideOffset: 4,
41
- children: /* @__PURE__ */ d(s, { children: m.map((r) => /* @__PURE__ */ f(p.Item, {
42
- disabled: r.disabled,
43
- label: r.label,
44
- render: /* @__PURE__ */ d(e, {
45
- alignItems: "center",
46
- borderRadius: "sm",
47
- className: a,
48
- display: "flex",
49
- gap: "8",
50
- minBlockSize: "sm",
51
- paddingInline: "12"
52
- }),
53
- value: r.value,
54
- children: [/* @__PURE__ */ d(n, {
55
- className: i,
33
+ /* @__PURE__ */ p(c, { children: /* @__PURE__ */ p(l, { children: /* @__PURE__ */ p(s, { children: g.map((r) => /* @__PURE__ */ m(h.Item, {
34
+ disabled: r.disabled,
35
+ label: r.label,
36
+ render: /* @__PURE__ */ p(e, {
37
+ alignItems: "center",
38
+ borderRadius: "sm",
39
+ className: a,
40
+ display: "flex",
41
+ gap: "8",
42
+ minBlockSize: "sm",
43
+ paddingInline: "12"
44
+ }),
45
+ value: r.value,
46
+ children: [/* @__PURE__ */ p(n, {
47
+ className: i,
48
+ size: "sm"
49
+ }), /* @__PURE__ */ p(h.ItemText, {
50
+ render: /* @__PURE__ */ p(t, {
51
+ as: "span",
52
+ font: "label",
56
53
  size: "sm"
57
- }), /* @__PURE__ */ d(p.ItemText, {
58
- render: /* @__PURE__ */ d(t, {
59
- as: "span",
60
- font: "label",
61
- size: "sm"
62
- }),
63
- children: r.label
64
- })]
65
- }, r.value)) })
66
- }) })
54
+ }),
55
+ children: r.label
56
+ })]
57
+ }, r.value)) }) }) })
67
58
  ]
68
59
  }) }),
69
- y != null && /* @__PURE__ */ d(r.Description, { children: y }),
70
- /* @__PURE__ */ d(r.Error, { children: b })
60
+ x != null && /* @__PURE__ */ p(r.Description, { children: x }),
61
+ /* @__PURE__ */ p(r.Error, { children: S })
71
62
  ]
72
63
  });
73
64
  }
74
65
  //#endregion
75
- export { m as MultiSelect };
66
+ export { g as MultiSelect };
package/dist/docs/Menu.md CHANGED
@@ -27,6 +27,7 @@ A list of actions that opens from a button: a row's "Edit, Duplicate, Delete", a
27
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.
28
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.
29
29
  - `Menu.Group` takes a `label` that is shown and names the group. Separate groups with `Menu.Separator`.
30
+ - `Menu.Separator` draws a line inside its own padding (`paddingBlock="4"`, `paddingInline="8"`) and sets no margin. Change its spacing with `paddingBlock` and `paddingInline` (the `padding` shorthand loses to these defaults, decision 0065); a `margin*` prop adds space outside the padding. `style={{ borderBlockStartColor }}` recolours the line.
30
31
  - 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>`.
31
32
  - Item and link item text is wrapped in `Text` for you; an `Icon` beside it is spaced by the item's `gap`.
32
33
  - `side` (`bottom` by default) and `align` (`start`) place the popup against the trigger; it flips when it would overflow the viewport.
@@ -89,7 +89,8 @@ A form field for picking any number of values from a list too long to show at on
89
89
  ## Accessibility
90
90
 
91
91
  - The trigger is a button with `aria-haspopup="listbox"` and `aria-expanded`, labelled by the `Field` label and described by the description and error. The popup is `role="listbox"` with `aria-multiselectable="true"`, options are `role="option"` with `aria-selected`.
92
- - Opening from the keyboard focuses an option; focus stays in the list until it closes, then returns to the trigger. Choosing an option toggles it and leaves the list open, so several can be picked in one visit.
92
+ - Opening from the keyboard focuses an option; focus stays in the list until it closes. Choosing an option toggles it and leaves the list open, so several can be picked in one visit. Closed by Esc or the trigger, focus returns at once to the trigger, so a Tab pressed while the list fades goes on to the next control. A press outside or a Tab out leaves focus to Base UI.
93
+ - A closed list stays on the page while it fades out, but is `inert` and `aria-hidden` from the moment it closes, so a list opened during the fade (the next filter's) is the only one a screen reader or a role query finds, and nothing in the fading one takes focus or a click (ADR 0113).
93
94
  - Selection is a check mark on the option as well as its highlight, so it is not color alone. The check's space is always in the row, so the text never shifts.
94
95
  - The trigger summary is text: the joined labels, or the count. When the count replaces the labels, the labels are hidden from assistive technology so the count is read once.
95
96
  - The shared focus ring shows on the trigger and, from the keyboard, on the highlighted option. Under forced colors the highlighted option is `Highlight` with `HighlightText`, and the popup keeps its border.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "./manifest.schema.json",
3
3
  "package": "@jonmilner/ui",
4
- "version": "0.4.4",
4
+ "version": "0.5.1",
5
5
  "components": [
6
6
  {
7
7
  "name": "AlertDialog",
@@ -0,0 +1,29 @@
1
+ # 0.5.0
2
+
3
+ `Menu.Separator` drops its default `marginBlock="4"` and `marginInline="8"` for the same spacing as padding inside it, with its line drawn within, because a component never puts margin on its root (decision 0095); a bare separator looks the same, but a `margin*` prop you pass now adds outside the padding instead of replacing the old margin.
4
+
5
+ ## Find
6
+
7
+ In the app's source (`*.tsx`), every `Menu.Separator` that passes any prop: regex `<Menu\.Separator\s+[^/>\s]` searched multiline (`rg -U`), since a long tag's props sit on their own lines. A bare `<Menu.Separator />` needs nothing. Check each match for `margin*` and `padding*` props and for a `className` or `style` that sets margin, padding or the line's border. A match with props spread from elsewhere (`<Menu.Separator {...props} />`) needs a read of where they come from.
8
+
9
+ ## Replace
10
+
11
+ Move the spacing to the matching `paddingBlock` or `paddingInline` prop, so it replaces the default instead of adding to it. A margin the default padding already covers can go. The `padding` shorthand loses to those two defaults (decision 0065), so use the longhands. A `padding*` prop you already passed used to sit inside the default margin; it now replaces that spacing, so add the old 4px block or 8px inline to it to keep the gap.
12
+
13
+ ```tsx
14
+ // before: 8px above and below, 8px inset
15
+ <Menu.Separator marginBlock="8" />
16
+ // after
17
+ <Menu.Separator paddingBlock="8" />
18
+
19
+ // before: a full-width line
20
+ <Menu.Separator marginInline="0" />
21
+ // after
22
+ <Menu.Separator paddingInline="0" />
23
+ ```
24
+
25
+ A `className` that set `margin` on the separator moves to `padding` the same way. The line is now the separator's `::before`, not a border on its root, so a `className` that restyled the line's width or style targets `::before` instead (`.myDivider::before { border-block-start-width: 2px }`). Its colour still comes from the root: `className` or `style` setting `borderBlockStartColor` on the separator recolours the line; one that sets a border style on the root draws a second line, so drop it.
26
+
27
+ ## Verify
28
+
29
+ Run `rg -U '<Menu\.Separator\s+[^/>\s]' --glob '*.tsx'` again: no match passes a `margin*` prop, a `padding` shorthand, or a `className` or `style` that sets a border style on the root, except margin you meant to add outside the divider. Then open a menu with a separator: the line keeps the spacing it had before the upgrade.
@@ -3,23 +3,33 @@ import { Clickable as e } from "../Clickable/index.mjs";
3
3
  import { Button as t } from "../Button/index.mjs";
4
4
  import { useThemeContainer as n } from "../theme/use-theme-container.mjs";
5
5
  import { jsx as r } from "react/jsx-runtime";
6
- import { createContext as i, use as a } from "react";
6
+ import { createContext as i, use as a, useCallback as o, useLayoutEffect as s, useRef as c } from "react";
7
7
  //#region src/popup/client.tsx
8
- var o = i({
8
+ var l = i({
9
9
  container: { current: null },
10
10
  ref: () => {},
11
11
  trigger: { current: null }
12
- });
13
- function s({ children: e }) {
14
- return /* @__PURE__ */ r(o, {
12
+ }), u = i({ current: {
13
+ keyboard: !1,
14
+ opener: null
15
+ } });
16
+ function d({ children: e }) {
17
+ let t = c({
18
+ keyboard: !1,
19
+ opener: null
20
+ });
21
+ return /* @__PURE__ */ r(l, {
15
22
  value: n(),
16
- children: e
23
+ children: /* @__PURE__ */ r(u, {
24
+ value: t,
25
+ children: e
26
+ })
17
27
  });
18
28
  }
19
- function c() {
20
- return a(o);
29
+ function f() {
30
+ return a(l);
21
31
  }
22
- function l({ variant: n, size: i, iconStart: a, iconEnd: o, ...s }) {
32
+ function p({ variant: n, size: i, iconStart: a, iconEnd: o, ...s }) {
23
33
  return n || i || a || o ? /* @__PURE__ */ r(t, {
24
34
  ...s,
25
35
  variant: n,
@@ -28,5 +38,63 @@ function l({ variant: n, size: i, iconStart: a, iconEnd: o, ...s }) {
28
38
  iconEnd: o
29
39
  }) : /* @__PURE__ */ r(e, { ...s });
30
40
  }
41
+ function m(e) {
42
+ let t = a(u);
43
+ return (n, r) => {
44
+ t.current = n ? {
45
+ keyboard: !1,
46
+ opener: r.trigger ?? null
47
+ } : {
48
+ ...t.current,
49
+ keyboard: r.event instanceof KeyboardEvent,
50
+ reason: r.reason
51
+ }, e?.(n, r);
52
+ };
53
+ }
54
+ function h(e, t) {
55
+ if (typeof e == "function") {
56
+ let n = e(t);
57
+ return typeof n == "function" ? n : () => e(null);
58
+ }
59
+ return e && (e.current = t), () => {
60
+ e && (e.current = null);
61
+ };
62
+ }
63
+ var g = /* @__PURE__ */ new Set([
64
+ "trigger-hover",
65
+ "focus-out",
66
+ "outside-press"
67
+ ]);
68
+ function _(e, { finalFocus: t, open: n }) {
69
+ let { trigger: r } = f(), i = a(u), l = c(null), d = o((t) => {
70
+ l.current = t;
71
+ let n = h(e, t);
72
+ return () => {
73
+ l.current = null, n();
74
+ };
75
+ }, [e]);
76
+ return s(() => {
77
+ let e = l.current;
78
+ if (!e) return;
79
+ if (n) {
80
+ e.inert = !1;
81
+ return;
82
+ }
83
+ let { reason: a, keyboard: o, opener: s } = i.current, c = s?.isConnected ? s : r.current;
84
+ if (e.contains(e.ownerDocument.activeElement) && !t && !g.has(a ?? "") && c instanceof HTMLElement) {
85
+ let e = { preventScroll: !0 };
86
+ o && (e.focusVisible = !0), c.focus(e);
87
+ }
88
+ e.inert = !0;
89
+ }, [
90
+ n,
91
+ t,
92
+ i,
93
+ r
94
+ ]), {
95
+ "aria-hidden": !n || void 0,
96
+ ref: d
97
+ };
98
+ }
31
99
  //#endregion
32
- export { s as PopupScope, l as triggerElement, c as usePopupScope };
100
+ export { d as PopupScope, p as triggerElement, _ as useClosingRule, f as usePopupScope, m as useRecordOpenChange };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jonmilner/ui",
3
- "version": "0.4.4",
3
+ "version": "0.5.1",
4
4
  "files": [
5
5
  "dist",
6
6
  "THIRD_PARTY_NOTICES.md"