@uniflowed/ui 0.2.0 → 0.4.0

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/input-otp.js CHANGED
@@ -112,7 +112,7 @@ function clean(raw: string, kind: InputOtpKind, length: number): string {
112
112
  * Renders no element of its own beyond the input — see the module header. The
113
113
  * caller's props go on the input, because the input is the field.
114
114
  */
115
- export component InputOtpRoot(
115
+ component InputOtpRoot(
116
116
  children: React.Node,
117
117
  defaultValue?: string = "",
118
118
  disabled?: boolean = false,
@@ -173,7 +173,7 @@ export component InputOtpRoot(
173
173
  * already been announced, and a reader told "group, 3, group, 4" is being read
174
174
  * a picture.
175
175
  */
176
- export component InputOtpGroup(children: React.Node, ...rest: Rest) {
176
+ component InputOtpGroup(children: React.Node, ...rest: Rest) {
177
177
  return (
178
178
  <div {...rest} aria-hidden="true">
179
179
  {children}
@@ -188,7 +188,7 @@ export component InputOtpGroup(children: React.Node, ...rest: Rest) {
188
188
  * whether there is one in it already, so a stylesheet can draw a cursor and a
189
189
  * border without this module having an opinion about either.
190
190
  */
191
- export component InputOtpSlot(children?: React.Node, index: number, ...rest: Rest) {
191
+ component InputOtpSlot(children?: React.Node, index: number, ...rest: Rest) {
192
192
  const otp = useInputOtp("InputOtp.Slot");
193
193
  const character = otp.value[index] ?? "";
194
194
  // The box the next character goes in, clamped so a full code lights its last
@@ -209,10 +209,26 @@ export component InputOtpSlot(children?: React.Node, index: number, ...rest: Res
209
209
  }
210
210
 
211
211
  /** The dash between two groups. Decoration, and it says so. */
212
- export component InputOtpSeparator(children?: React.Node, render?: RenderProp, ...rest: Rest) {
212
+ component InputOtpSeparator(children?: React.Node, render?: RenderProp, ...rest: Rest) {
213
213
  const props = withProps(rest, { "aria-hidden": "true", children });
214
214
  if (render != null) {
215
215
  return render(props);
216
216
  }
217
217
  return <div {...props} />;
218
218
  }
219
+
220
+ /**
221
+ * The parts, under the names the `InputOtp` namespace gives them.
222
+ *
223
+ * `index.js` re-exports this module whole — `export * as InputOtp from "./input-otp.js"` —
224
+ * so a caller writes `<InputOtp.Root>`, and the namespace is the prefix. Each
225
+ * part is still *declared* as `InputOtpRoot`, so React DevTools, a component
226
+ * stack and an error name the part a reader can find rather than one of forty
227
+ * `Root`s.
228
+ */
229
+ export {
230
+ InputOtpRoot as Root,
231
+ InputOtpGroup as Group,
232
+ InputOtpSlot as Slot,
233
+ InputOtpSeparator as Separator,
234
+ };
package/interactions.js CHANGED
@@ -1798,7 +1798,6 @@ hook useDescription(text: string | void): string | void {
1798
1798
  const entry: SharedDescription = shared;
1799
1799
  entry.users += 1;
1800
1800
  // The id is state because it must render only after the shared DOM node exists.
1801
- // uf-lint-disable-next-line react-compiler/set-state-in-effect
1802
1801
  setId(entry.node.id);
1803
1802
  return () => {
1804
1803
  entry.users -= 1;
package/internal/focus.js CHANGED
@@ -52,13 +52,22 @@ export const FOCUS_STOPS: string =
52
52
  * document.
53
53
  */
54
54
  export function focusable(root: HTMLElement): Array<HTMLElement> {
55
- return Array.from(root.querySelectorAll(FOCUS_STOPS)).filter(
56
- (element: $FlowFixMe) =>
55
+ const found: Array<HTMLElement> = [];
56
+ for (const element of root.querySelectorAll(FOCUS_STOPS)) {
57
+ if (
58
+ // Asked rather than annotated: `querySelectorAll` answers `Element`, and
59
+ // what focus lands on is an HTML or SVG element, both `HTMLElement` to
60
+ // Flow.
61
+ (element instanceof HTMLElement || element instanceof SVGElement) &&
57
62
  // All three hide a whole subtree, so all three are asked of the
58
63
  // ancestors; see the module header for what reading them off the element
59
64
  // alone let through.
60
65
  element.closest("[hidden]") == null &&
61
66
  element.closest("[inert]") == null &&
62
- element.closest('[aria-hidden="true"]') == null,
63
- );
67
+ element.closest('[aria-hidden="true"]') == null
68
+ ) {
69
+ found.push(element);
70
+ }
71
+ }
72
+ return found;
64
73
  }
@@ -69,9 +69,18 @@ const TYPEAHEAD_WINDOW = 500;
69
69
  * cannot see.
70
70
  */
71
71
  export function itemsOf(container: HTMLElement, item: string, owner: string): Array<HTMLElement> {
72
- return Array.from(container.querySelectorAll(item)).filter(
73
- (element: $FlowFixMe) => element.closest(owner) === container,
74
- );
72
+ const found: Array<HTMLElement> = [];
73
+ for (const element of container.querySelectorAll(item)) {
74
+ // An item is an HTML or SVG element, both `HTMLElement` to Flow; asked
75
+ // rather than annotated, because `querySelectorAll` answers `Element`.
76
+ if (
77
+ (element instanceof HTMLElement || element instanceof SVGElement) &&
78
+ element.closest(owner) === container
79
+ ) {
80
+ found.push(element);
81
+ }
82
+ }
83
+ return found;
75
84
  }
76
85
 
77
86
  /**
package/menu.js CHANGED
@@ -180,7 +180,7 @@ const MenuRadioContext: React.Context<MenuRadioState | null> = createContext(nul
180
180
  * whatever layout the caller wrote, and a wrapper would put a `<div>` between
181
181
  * them that the caller then has to style around.
182
182
  */
183
- export component MenuRoot(
183
+ component MenuRoot(
184
184
  children: React.Node,
185
185
  defaultOpen?: boolean = false,
186
186
  open?: boolean,
@@ -200,7 +200,7 @@ export component MenuRoot(
200
200
  * parent — and that difference is what `ArrowLeft`, `Escape` and "choosing an
201
201
  * item closes everything" are all defined in terms of.
202
202
  */
203
- export component MenuSub(
203
+ component MenuSub(
204
204
  children: React.Node,
205
205
  defaultOpen?: boolean = false,
206
206
  open?: boolean,
@@ -218,7 +218,7 @@ export component MenuSub(
218
218
  }
219
219
 
220
220
  /** The button that opens the menu. */
221
- export component MenuTrigger(children: React.Node, render?: RenderProp, ...rest: Rest) {
221
+ component MenuTrigger(children: React.Node, render?: RenderProp, ...rest: Rest) {
222
222
  const menu = useMenu("Menu.Trigger");
223
223
  useTriggerRegistration(menu);
224
224
  const props = withProps(withoutComposed(rest, ["onClick", "onKeyDown", "ref"]), {
@@ -269,7 +269,7 @@ export component MenuTrigger(children: React.Node, render?: RenderProp, ...rest:
269
269
  * and only the container can answer it. Items still get their own `Enter` and
270
270
  * `Space` from being buttons.
271
271
  */
272
- export component MenuBody(
272
+ component MenuBody(
273
273
  children: renders* (
274
274
  | MenuItem
275
275
  | MenuCheckboxItem
@@ -323,7 +323,6 @@ export component MenuBody(
323
323
  // `open` is menu-tree metadata; no ref value is read during render.
324
324
  // uf-lint-disable-next-line react-compiler/refs
325
325
  open: menu.open,
326
- // uf-lint-disable-next-line react-compiler/refs
327
326
  overlayRef: bodyRef,
328
327
  side: placement,
329
328
  sideOffset,
@@ -551,7 +550,7 @@ hook useMenuItem(
551
550
  * tree. That combination is what shadcn's copy step is usually reached for, and
552
551
  * what this package offers instead of it.
553
552
  */
554
- export component MenuItem(
553
+ component MenuItem(
555
554
  children: React.Node,
556
555
  disabled?: boolean = false,
557
556
  closeOnSelect?: boolean = true,
@@ -593,7 +592,7 @@ export component MenuItem(
593
592
  * summarises other checkboxes — `checkbox.js` has it, and a menu item is a
594
593
  * command rather than a summary of a table's rows.
595
594
  */
596
- export component MenuCheckboxItem(
595
+ component MenuCheckboxItem(
597
596
  children: React.Node,
598
597
  checked?: boolean,
599
598
  defaultChecked?: boolean = false,
@@ -640,7 +639,7 @@ export component MenuCheckboxItem(
640
639
  * a state the group starts in and never an event it reports, because no gesture
641
640
  * inside it unchooses an answer.
642
641
  */
643
- export component MenuRadioGroup(
642
+ component MenuRadioGroup(
644
643
  children: renders* (MenuRadioItem | MenuLabel | MenuSeparator),
645
644
  defaultValue?: string | null = null,
646
645
  value?: string | null,
@@ -688,7 +687,7 @@ export component MenuRadioGroup(
688
687
  * what a sort order or a zoom level in a native menu does; `closeOnSelect` is
689
688
  * the way to say otherwise for a choice that ends the visit.
690
689
  */
691
- export component MenuRadioItem(
690
+ component MenuRadioItem(
692
691
  children: React.Node,
693
692
  value: string,
694
693
  disabled?: boolean = false,
@@ -728,7 +727,7 @@ export component MenuRadioItem(
728
727
  * is why it reads the list context of the menu around it and the menu context
729
728
  * of the one below it.
730
729
  */
731
- export component MenuSubTrigger(children: React.Node, render?: RenderProp, ...rest: Rest) {
730
+ component MenuSubTrigger(children: React.Node, render?: RenderProp, ...rest: Rest) {
732
731
  const menu = useMenu("Menu.SubTrigger");
733
732
  const list = useContext(MenuListContext);
734
733
  const setActiveId = list?.setActiveId;
@@ -785,7 +784,7 @@ export component MenuSubTrigger(children: React.Node, render?: RenderProp, ...re
785
784
  * moving through the menu is told the group changed. It is not focusable and
786
785
  * the arrow keys pass straight over it.
787
786
  */
788
- export component MenuSeparator(render?: RenderProp, ...rest: Rest) {
787
+ component MenuSeparator(render?: RenderProp, ...rest: Rest) {
789
788
  const props = withProps(rest, { "aria-orientation": "horizontal", role: "separator" });
790
789
  if (render != null) {
791
790
  return render(props);
@@ -801,7 +800,7 @@ export component MenuSeparator(render?: RenderProp, ...rest: Rest) {
801
800
  * that is not in the document makes a screen reader announce *nothing*, which
802
801
  * is worse than an unnamed group.
803
802
  */
804
- export component MenuGroup(children: React.Node, render?: RenderProp, ...rest: Rest) {
803
+ component MenuGroup(children: React.Node, render?: RenderProp, ...rest: Rest) {
805
804
  const base = useId();
806
805
  const [labelled, setLabelled] = useState(false);
807
806
 
@@ -826,7 +825,7 @@ export component MenuGroup(children: React.Node, render?: RenderProp, ...rest: R
826
825
  * as ordinary content would have a reader hear the heading once as the group's
827
826
  * name and again as a stray line of text between the items.
828
827
  */
829
- export component MenuLabel(children: React.Node, render?: RenderProp, ...rest: Rest) {
828
+ component MenuLabel(children: React.Node, render?: RenderProp, ...rest: Rest) {
830
829
  const group = useContext(MenuGroupContext);
831
830
  const register = group?.registerLabel;
832
831
 
@@ -844,3 +843,27 @@ export component MenuLabel(children: React.Node, render?: RenderProp, ...rest: R
844
843
  }
845
844
  return <div {...props} />;
846
845
  }
846
+
847
+ /**
848
+ * The parts, under the names the `Menu` namespace gives them.
849
+ *
850
+ * `index.js` re-exports this module whole — `export * as Menu from "./menu.js"` —
851
+ * so a caller writes `<Menu.Root>`, and the namespace is the prefix. Each
852
+ * part is still *declared* as `MenuRoot`, so React DevTools, a component
853
+ * stack and an error name the part a reader can find rather than one of forty
854
+ * `Root`s.
855
+ */
856
+ export {
857
+ MenuRoot as Root,
858
+ MenuTrigger as Trigger,
859
+ MenuBody as Body,
860
+ MenuItem as Item,
861
+ MenuCheckboxItem as CheckboxItem,
862
+ MenuRadioGroup as RadioGroup,
863
+ MenuRadioItem as RadioItem,
864
+ MenuSeparator as Separator,
865
+ MenuGroup as Group,
866
+ MenuLabel as Label,
867
+ MenuSub as Sub,
868
+ MenuSubTrigger as SubTrigger,
869
+ };
package/menubar.js CHANGED
@@ -128,7 +128,7 @@ hook useMenubar(part: string): MenubarState {
128
128
  * a page with an application menubar and a formatting toolbar has two, and
129
129
  * "menu bar" twice tells a reader nothing about which is which.
130
130
  */
131
- export component MenubarRoot(children: renders* MenubarMenu, render?: RenderProp, ...rest: Rest) {
131
+ component MenubarRoot(children: renders* MenubarMenu, render?: RenderProp, ...rest: Rest) {
132
132
  const barRef = useRef<HTMLElement | null>(null);
133
133
  const [open, setOpenValue] = useState<string | null>(null);
134
134
  const [active, setActive] = useState<string | null>(null);
@@ -208,7 +208,7 @@ export component MenubarRoot(children: renders* MenubarMenu, render?: RenderProp
208
208
  * Renders no element of its own, for the reason `Menu.Root` gives: a trigger and
209
209
  * its body are siblings in whatever layout the caller wrote.
210
210
  */
211
- export component MenubarMenu(children: React.Node, value: string) {
211
+ component MenubarMenu(children: React.Node, value: string) {
212
212
  const bar = useMenubar("Menubar.Menu");
213
213
  const setOpen = bar.setOpen;
214
214
  const onOpenChange = useCallback(
@@ -237,7 +237,7 @@ export component MenubarMenu(children: React.Node, value: string) {
237
237
  * menubar — a reader is told "File, menu item, has popup, 1 of 3" — and it is
238
238
  * what makes the bar's arrow keys agree with what they were told is in it.
239
239
  */
240
- export component MenubarTrigger(children: React.Node, render?: RenderProp, ...rest: Rest) {
240
+ component MenubarTrigger(children: React.Node, render?: RenderProp, ...rest: Rest) {
241
241
  const bar = useMenubar("Menubar.Trigger");
242
242
  const menu = useMenu("Menubar.Trigger");
243
243
  const value = useContext(MenubarMenuContext);
@@ -278,8 +278,6 @@ export component MenubarTrigger(children: React.Node, render?: RenderProp, ...re
278
278
  menu.pendingFocusRef.current = end;
279
279
  bar.setOpen(value);
280
280
  }),
281
- // React calls callback refs during commit; menu focus restoration reads it later.
282
- // uf-lint-disable-next-line react-compiler/refs
283
281
  ref: composeRefs(rest.ref, (element: HTMLElement | null) => {
284
282
  // uf-lint-disable-next-line react-compiler/immutability
285
283
  menu.triggerRef.current = element;
@@ -293,3 +291,32 @@ export component MenubarTrigger(children: React.Node, render?: RenderProp, ...re
293
291
  }
294
292
  return <button {...props} type="button" />;
295
293
  }
294
+
295
+ /**
296
+ * The parts, under the names the `Menubar` namespace gives them.
297
+ *
298
+ * `index.js` re-exports this module whole — `export * as Menubar from "./menubar.js"` —
299
+ * so a caller writes `<Menubar.Root>`, and the namespace is the prefix. Each
300
+ * part is still *declared* as `MenubarRoot`, so React DevTools, a component
301
+ * stack and an error name the part a reader can find rather than one of forty
302
+ * `Root`s.
303
+ */
304
+ export { MenubarRoot as Root, MenubarMenu as Menu, MenubarTrigger as Trigger };
305
+
306
+ /**
307
+ * The parts `Menubar` shares with `Menu`, re-exported so the `Menubar`
308
+ * namespace is complete on its own: `<Menubar.Body>` is `Menu`'s part,
309
+ * not a copy of it.
310
+ */
311
+ export {
312
+ Body,
313
+ Item,
314
+ CheckboxItem,
315
+ RadioGroup,
316
+ RadioItem,
317
+ Separator,
318
+ Group,
319
+ Label,
320
+ Sub,
321
+ SubTrigger,
322
+ } from "./menu.js";
@@ -106,7 +106,7 @@ hook useNavigationMenuItem(part: string): NavigationMenuItemState {
106
106
  * trigger writing itself into a ref — is a registry that has to be kept in step
107
107
  * with a document that already knows the answer.
108
108
  */
109
- export component NavigationMenuRoot(
109
+ component NavigationMenuRoot(
110
110
  children: renders* NavigationMenuList,
111
111
  defaultValue?: string | null = null,
112
112
  value?: string | null,
@@ -145,13 +145,13 @@ export component NavigationMenuRoot(
145
145
  }
146
146
 
147
147
  /** The list of entries. A `<ul>`, because a reader is told how many there are. */
148
- export component NavigationMenuList(children: renders* NavigationMenuItem, ...rest: Rest) {
148
+ component NavigationMenuList(children: renders* NavigationMenuItem, ...rest: Rest) {
149
149
  useNavigationMenu("NavigationMenu.List");
150
150
  return <ul {...rest}>{children}</ul>;
151
151
  }
152
152
 
153
153
  /** One entry: a link on its own, or a button and the group it opens. */
154
- export component NavigationMenuItem(value: string, children: React.Node, ...rest: Rest) {
154
+ component NavigationMenuItem(value: string, children: React.Node, ...rest: Rest) {
155
155
  const menu = useNavigationMenu("NavigationMenu.Item");
156
156
  const base = useId();
157
157
  const [present, setPresent] = useState(false);
@@ -179,7 +179,7 @@ export component NavigationMenuItem(value: string, children: React.Node, ...rest
179
179
  }
180
180
 
181
181
  /** The button that opens an entry's group. Not a `menuitem`; see the header. */
182
- export component NavigationMenuTrigger(children: React.Node, ...rest: Rest) {
182
+ component NavigationMenuTrigger(children: React.Node, ...rest: Rest) {
183
183
  const item = useNavigationMenuItem("NavigationMenu.Trigger");
184
184
  const passed = withoutComposed(rest, ["onClick"]);
185
185
 
@@ -203,7 +203,7 @@ export component NavigationMenuTrigger(children: React.Node, ...rest: Rest) {
203
203
  * Named after its trigger, so a reader who lands in it by `Tab` is told which
204
204
  * entry they are inside rather than hearing an unnamed list of four links.
205
205
  */
206
- export component NavigationMenuBody(children: renders* NavigationMenuLink, ...rest: Rest) {
206
+ component NavigationMenuBody(children: renders* NavigationMenuLink, ...rest: Rest) {
207
207
  const item = useNavigationMenuItem("NavigationMenu.Body");
208
208
  // Registered only while the group is actually in the document, which for this
209
209
  // component means only while it is open — see the module header for why a
@@ -237,7 +237,7 @@ export component NavigationMenuBody(children: renders* NavigationMenuLink, ...re
237
237
  * behind them so that coming back — or a router that never unmounted the page —
238
238
  * does not leave it hanging open.
239
239
  */
240
- export component NavigationMenuLink(children: React.Node, ...rest: Rest) {
240
+ component NavigationMenuLink(children: React.Node, ...rest: Rest) {
241
241
  const item = useNavigationMenuItem("NavigationMenu.Link");
242
242
  const passed = withoutComposed(rest, ["onClick"]);
243
243
 
@@ -249,3 +249,21 @@ export component NavigationMenuLink(children: React.Node, ...rest: Rest) {
249
249
  </li>
250
250
  );
251
251
  }
252
+
253
+ /**
254
+ * The parts, under the names the `NavigationMenu` namespace gives them.
255
+ *
256
+ * `index.js` re-exports this module whole — `export * as NavigationMenu from "./navigation-menu.js"` —
257
+ * so a caller writes `<NavigationMenu.Root>`, and the namespace is the prefix. Each
258
+ * part is still *declared* as `NavigationMenuRoot`, so React DevTools, a component
259
+ * stack and an error name the part a reader can find rather than one of forty
260
+ * `Root`s.
261
+ */
262
+ export {
263
+ NavigationMenuRoot as Root,
264
+ NavigationMenuList as List,
265
+ NavigationMenuItem as Item,
266
+ NavigationMenuTrigger as Trigger,
267
+ NavigationMenuBody as Body,
268
+ NavigationMenuLink as Link,
269
+ };
package/number-field.js CHANGED
@@ -82,7 +82,7 @@ hook useNumber(): NumberState {
82
82
  return state;
83
83
  }
84
84
 
85
- export component NumberFieldRoot(
85
+ component NumberFieldRoot(
86
86
  children: React.Node,
87
87
  value?: number | null,
88
88
  defaultValue?: number | null = null,
@@ -189,7 +189,7 @@ export component NumberFieldRoot(
189
189
  }
190
190
 
191
191
  /** Field.Control may render this input to supply the label, description and error ids. */
192
- export component NumberFieldInput(render?: RenderProp, ...rest: Rest) {
192
+ component NumberFieldInput(render?: RenderProp, ...rest: Rest) {
193
193
  const state = useNumber();
194
194
  const props = withProps(rest, {
195
195
  type: "text",
@@ -225,11 +225,7 @@ export component NumberFieldInput(render?: RenderProp, ...rest: Rest) {
225
225
  return render != null ? render(props) : <input {...props} />;
226
226
  }
227
227
 
228
- export component NumberFieldIncrement(
229
- children?: React.Node = "+",
230
- render?: RenderProp,
231
- ...rest: Rest
232
- ) {
228
+ component NumberFieldIncrement(children?: React.Node = "+", render?: RenderProp, ...rest: Rest) {
233
229
  const state = useNumber();
234
230
  const props = withProps(rest, {
235
231
  children,
@@ -243,11 +239,7 @@ export component NumberFieldIncrement(
243
239
  });
244
240
  return render != null ? render(props) : <button {...props} />;
245
241
  }
246
- export component NumberFieldDecrement(
247
- children?: React.Node = "−",
248
- render?: RenderProp,
249
- ...rest: Rest
250
- ) {
242
+ component NumberFieldDecrement(children?: React.Node = "−", render?: RenderProp, ...rest: Rest) {
251
243
  const state = useNumber();
252
244
  const props = withProps(rest, {
253
245
  children,
@@ -261,3 +253,19 @@ export component NumberFieldDecrement(
261
253
  });
262
254
  return render != null ? render(props) : <button {...props} />;
263
255
  }
256
+
257
+ /**
258
+ * The parts, under the names the `NumberField` namespace gives them.
259
+ *
260
+ * `index.js` re-exports this module whole — `export * as NumberField from "./number-field.js"` —
261
+ * so a caller writes `<NumberField.Root>`, and the namespace is the prefix. Each
262
+ * part is still *declared* as `NumberFieldRoot`, so React DevTools, a component
263
+ * stack and an error name the part a reader can find rather than one of forty
264
+ * `Root`s.
265
+ */
266
+ export {
267
+ NumberFieldRoot as Root,
268
+ NumberFieldInput as Input,
269
+ NumberFieldIncrement as Increment,
270
+ NumberFieldDecrement as Decrement,
271
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniflowed/ui",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Headless, accessible React components whose composition Flow checks, part of the Unified Toolchain for Flow.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -19,10 +19,10 @@
19
19
  "!*.test.js"
20
20
  ],
21
21
  "dependencies": {
22
- "@uniflowed/core": "0.2.0",
23
- "@uniflowed/hooks": "0.2.0",
24
- "@uniflowed/react": "0.2.0",
25
- "@uniflowed/state": "0.2.0"
22
+ "@uniflowed/core": "0.4.0",
23
+ "@uniflowed/hooks": "0.4.0",
24
+ "@uniflowed/react": "0.4.0",
25
+ "@uniflowed/state": "0.4.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "react": ">=19"
package/pagination.js CHANGED
@@ -56,7 +56,7 @@ import { withProps, withoutComposed } from "./internal/merge-props.js";
56
56
  * links are on screen, and a component that showed five links out of
57
57
  * twenty-five would otherwise announce "page 4 of 5".
58
58
  */
59
- export component PaginationRoot(
59
+ component PaginationRoot(
60
60
  children: React.Node,
61
61
  label?: string = "Pagination",
62
62
  page?: number | null = null,
@@ -91,7 +91,7 @@ export component PaginationRoot(
91
91
  * A real list, so a reader is told how many there are before walking them and
92
92
  * can skip the whole thing in one keystroke.
93
93
  */
94
- export component PaginationContent(
94
+ component PaginationContent(
95
95
  children: renders* (PaginationItem | PaginationPrevious | PaginationNext),
96
96
  render?: RenderProp,
97
97
  ...rest: Rest
@@ -109,7 +109,7 @@ export component PaginationContent(
109
109
  * The `<li>` is structural and takes nothing; everything a caller passes goes
110
110
  * on the `<a>`, which is what they style and what a reader activates.
111
111
  */
112
- export component PaginationItem(
112
+ component PaginationItem(
113
113
  children: React.Node,
114
114
  current?: boolean = false,
115
115
  disabled?: boolean = false,
@@ -131,7 +131,7 @@ export component PaginationItem(
131
131
  * is not something a reader can act on. Pass `label` to translate it; the
132
132
  * glyph stays whatever the caller rendered.
133
133
  */
134
- export component PaginationPrevious(
134
+ component PaginationPrevious(
135
135
  children?: React.Node,
136
136
  label?: string = "Previous page",
137
137
  disabled?: boolean = false,
@@ -146,7 +146,7 @@ export component PaginationPrevious(
146
146
  }
147
147
 
148
148
  /** The link to the page after this one. See `Pagination.Previous`. */
149
- export component PaginationNext(
149
+ component PaginationNext(
150
150
  children?: React.Node,
151
151
  label?: string = "Next page",
152
152
  disabled?: boolean = false,
@@ -207,3 +207,20 @@ component PageLink(
207
207
  function defaultAnnouncement(page: number, pageCount: number): string {
208
208
  return `Page ${String(page)} of ${String(pageCount)}.`;
209
209
  }
210
+
211
+ /**
212
+ * The parts, under the names the `Pagination` namespace gives them.
213
+ *
214
+ * `index.js` re-exports this module whole — `export * as Pagination from "./pagination.js"` —
215
+ * so a caller writes `<Pagination.Root>`, and the namespace is the prefix. Each
216
+ * part is still *declared* as `PaginationRoot`, so React DevTools, a component
217
+ * stack and an error name the part a reader can find rather than one of forty
218
+ * `Root`s.
219
+ */
220
+ export {
221
+ PaginationRoot as Root,
222
+ PaginationContent as Content,
223
+ PaginationItem as Item,
224
+ PaginationPrevious as Previous,
225
+ PaginationNext as Next,
226
+ };
package/popover.js CHANGED
@@ -117,7 +117,7 @@ hook usePopover(part: string): PopoverState {
117
117
  * whatever layout the caller wrote, and a wrapper would put a `<div>` between
118
118
  * them for the caller to style around.
119
119
  */
120
- export component PopoverRoot(
120
+ component PopoverRoot(
121
121
  children: React.Node,
122
122
  defaultOpen?: boolean = false,
123
123
  open?: boolean,
@@ -152,7 +152,7 @@ export component PopoverRoot(
152
152
  * trigger is not "outside" for exactly this reason — closing there and letting
153
153
  * this click reopen made the press a no-op that flickered.
154
154
  */
155
- export component PopoverTrigger(children: React.Node, render?: RenderProp, ...rest: Rest) {
155
+ component PopoverTrigger(children: React.Node, render?: RenderProp, ...rest: Rest) {
156
156
  const popover = usePopover("Popover.Trigger");
157
157
  const passed = withoutComposed(rest, ["onClick", "ref"]);
158
158
  usePresence(popover.registerTrigger);
@@ -190,7 +190,7 @@ export component PopoverTrigger(children: React.Node, render?: RenderProp, ...re
190
190
  * separates this from a dialog, and the one a copy of `dialog.js` with the
191
191
  * `aria-modal` deleted would get wrong.
192
192
  */
193
- export component PopoverBody(
193
+ component PopoverBody(
194
194
  children: React.Node,
195
195
  align?: Align = "center",
196
196
  alignOffset?: number = 0,
@@ -234,7 +234,6 @@ export component PopoverBody(
234
234
  // `open` is popover metadata; no ref value is read during render.
235
235
  // uf-lint-disable-next-line react-compiler/refs
236
236
  open: popover.open,
237
- // uf-lint-disable-next-line react-compiler/refs
238
237
  overlayRef: bodyRef,
239
238
  side,
240
239
  sideOffset,
@@ -365,3 +364,14 @@ export component PopoverBody(
365
364
  }
366
365
  return <div {...props} />;
367
366
  }
367
+
368
+ /**
369
+ * The parts, under the names the `Popover` namespace gives them.
370
+ *
371
+ * `index.js` re-exports this module whole — `export * as Popover from "./popover.js"` —
372
+ * so a caller writes `<Popover.Root>`, and the namespace is the prefix. Each
373
+ * part is still *declared* as `PopoverRoot`, so React DevTools, a component
374
+ * stack and an error name the part a reader can find rather than one of forty
375
+ * `Root`s.
376
+ */
377
+ export { PopoverRoot as Root, PopoverTrigger as Trigger, PopoverBody as Body };
package/radio-group.js CHANGED
@@ -96,7 +96,7 @@ import { useControlled } from "./internal/controlled-state.js";
96
96
  * have to reach it. `ToggleGroup type="single"` renders through here and is the
97
97
  * reason that matters in practice rather than in principle.
98
98
  */
99
- export function radioSet(orientation: Orientation): RovingSet {
99
+ function radioSet(orientation: Orientation): RovingSet {
100
100
  return {
101
101
  item: '[role="radio"]',
102
102
  owner: '[role="radiogroup"]',
@@ -144,7 +144,7 @@ hook useRadioGroup(part: string): RadioGroupState {
144
144
  * `name` puts the answer where a form can submit it; see the hidden input
145
145
  * below.
146
146
  */
147
- export component RadioGroupRoot(
147
+ component RadioGroupRoot(
148
148
  children: React.Node,
149
149
  defaultValue?: string | null = null,
150
150
  value?: string | null,
@@ -232,7 +232,7 @@ export component RadioGroupRoot(
232
232
  * `disabled` leaves a gap they cannot ask about. The arrow keys step over it
233
233
  * either way, and so does the search for the item that holds the tab stop.
234
234
  */
235
- export component RadioGroupItem(
235
+ component RadioGroupItem(
236
236
  value: string,
237
237
  children?: React.Node,
238
238
  disabled?: boolean = false,
@@ -291,7 +291,7 @@ export component RadioGroupItem(
291
291
  * `[aria-checked="true"] > *`, and so the "only while chosen" part is not
292
292
  * something each caller reimplements.
293
293
  */
294
- export component RadioGroupIndicator(children?: React.Node, render?: RenderProp, ...rest: Rest) {
294
+ component RadioGroupIndicator(children?: React.Node, render?: RenderProp, ...rest: Rest) {
295
295
  const item = useContext(RadioItemContext);
296
296
  if (item == null) {
297
297
  throw new Error("RadioGroup.Indicator must be rendered inside a RadioGroup.Item");
@@ -302,3 +302,14 @@ export component RadioGroupIndicator(children?: React.Node, render?: RenderProp,
302
302
  const props = withProps(rest, { "aria-hidden": "true", children });
303
303
  return render == null ? <span {...props} /> : render(props);
304
304
  }
305
+
306
+ /**
307
+ * The parts, under the names the `RadioGroup` namespace gives them.
308
+ *
309
+ * `index.js` re-exports this module whole — `export * as RadioGroup from "./radio-group.js"` —
310
+ * so a caller writes `<RadioGroup.Root>`, and the namespace is the prefix. Each
311
+ * part is still *declared* as `RadioGroupRoot`, so React DevTools, a component
312
+ * stack and an error name the part a reader can find rather than one of forty
313
+ * `Root`s.
314
+ */
315
+ export { RadioGroupRoot as Root, RadioGroupItem as Item, RadioGroupIndicator as Indicator };