@uniflowed/ui 0.0.0-alpha.40 → 0.0.0-alpha.41

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/toast.js CHANGED
@@ -356,26 +356,26 @@ export component ToastRegion(
356
356
  ...rest: Rest
357
357
  ) {
358
358
  const queued = useSyncExternalStore(subscribeToQueue, readQueue, readQueue);
359
- const region = useElementRef<HTMLElement>();
359
+ const regionRef = useElementRef<HTMLElement>();
360
360
  // Where `F6` came from, so pressing it again gives focus back.
361
- const cameFrom = useRef<HTMLElement | null>(null);
361
+ const cameFromRef = useRef<HTMLElement | null>(null);
362
362
  const passed = withoutComposed(rest, ["ref"]);
363
363
 
364
364
  useKeyCombo(
365
365
  "f6",
366
366
  () => {
367
- const element = region.current;
367
+ const element = regionRef.current;
368
368
  if (element == null) {
369
369
  return;
370
370
  }
371
371
  const active: $FlowFixMe = element.ownerDocument.activeElement;
372
372
  if (active != null && element.contains(active)) {
373
- const back = cameFrom.current;
374
- cameFrom.current = null;
373
+ const back = cameFromRef.current;
374
+ cameFromRef.current = null;
375
375
  back?.focus?.();
376
376
  return;
377
377
  }
378
- cameFrom.current = active;
378
+ cameFromRef.current = active;
379
379
  element.focus();
380
380
  },
381
381
  // The one case that matters is a notification arriving while the reader is
@@ -400,7 +400,7 @@ export component ToastRegion(
400
400
  {...passed}
401
401
  aria-label={label}
402
402
  ref={composeRefs(rest.ref, (element) => {
403
- region.current = element;
403
+ regionRef.current = element;
404
404
  })}
405
405
  role="region"
406
406
  // So `F6` can put focus on the region itself, including while it is
@@ -430,13 +430,13 @@ export component ToastRegion(
430
430
  export component ToastRoot(children: React.Node, ...rest: Rest) {
431
431
  const notification = useNotification("Toast.Root");
432
432
  const base = useId();
433
- const element = useElementRef<HTMLElement>();
433
+ const elementRef = useElementRef<HTMLElement>();
434
434
  const [titled, setTitled] = useState(false);
435
435
  const [described, setDescribed] = useState(false);
436
436
  const passed = withoutComposed(rest, ["ref"]);
437
437
 
438
- const hovered = useHover(element);
439
- const focusInside = useFocusWithin(element);
438
+ const hovered = useHover(elementRef);
439
+ const focusInside = useFocusWithin(elementRef);
440
440
  const documentVisible = useDocumentVisible();
441
441
  const paused = hovered || focusInside || !documentVisible;
442
442
 
@@ -465,6 +465,8 @@ export component ToastRoot(children: React.Node, ...rest: Rest) {
465
465
  // A notification whose duration was changed under it — "Uploading…" with no
466
466
  // duration becoming "Uploaded" with one — starts its countdown from there.
467
467
  useEffect(() => {
468
+ // The timeout duration is state because render hands it to useTimeout.
469
+ // uf-lint-disable-next-line react-compiler/set-state-in-effect
468
470
  setLeft(duration);
469
471
  startedAt.current = Date.now();
470
472
  }, [duration]);
@@ -486,7 +488,7 @@ export component ToastRoot(children: React.Node, ...rest: Rest) {
486
488
  aria-describedby={described ? parts.descriptionId : undefined}
487
489
  aria-labelledby={titled ? parts.titleId : undefined}
488
490
  ref={composeRefs(rest.ref, (node) => {
489
- element.current = node;
491
+ elementRef.current = node;
490
492
  })}
491
493
  role="group"
492
494
  >
package/toggle-group.js CHANGED
@@ -201,6 +201,8 @@ export component ToggleGroupRoot(
201
201
  // dozen commands on the way to the one the reader wanted.
202
202
  moveOnKey(event, group, toggleSet(orientation));
203
203
  }),
204
+ // React calls callback refs during commit; keyboard handlers read it later.
205
+ // uf-lint-disable-next-line react-compiler/refs
204
206
  ref: composeRefs(rest.ref, (element: HTMLElement | null) => {
205
207
  rootRef.current = element;
206
208
  }),
package/tooltip.js CHANGED
@@ -126,7 +126,7 @@ type TooltipState = {|
126
126
  * cannot be closed. It is cleared when the reader leaves the trigger, so
127
127
  * coming back opens it again.
128
128
  */
129
- readonly dismissed: { current: boolean },
129
+ readonly dismissedRef: { current: boolean },
130
130
  |};
131
131
 
132
132
  const TooltipContext: React.Context<TooltipState | null> = createContext(null);
@@ -180,7 +180,7 @@ export component TooltipRoot(
180
180
  const scope = useContext(TooltipScopeContext);
181
181
  const [isOpen, setOpen] = useControlled(open, defaultOpen, onOpenChange);
182
182
  const triggerRef = useRef<HTMLElement | null>(null);
183
- const dismissed = useRef(false);
183
+ const dismissedRef = useRef(false);
184
184
  const intent = useHoverIntent(setOpen);
185
185
  const own = openDelay ?? scope?.delayDuration ?? DEFAULT_OPEN_DELAY;
186
186
  const group = scope?.group;
@@ -217,7 +217,7 @@ export component TooltipRoot(
217
217
  () => ({
218
218
  base,
219
219
  closeDelay,
220
- dismissed,
220
+ dismissedRef,
221
221
  intent,
222
222
  open: isOpen,
223
223
  openDelay: () => group?.delayFor(own) ?? own,
@@ -245,19 +245,19 @@ export component TooltipRoot(
245
245
  */
246
246
  export component TooltipTrigger(children?: React.Node, render?: RenderProp, ...rest: Rest) {
247
247
  const tooltip = useTooltip("Tooltip.Trigger");
248
- const { closeDelay, dismissed, intent, openDelay, setOpen, triggerRef } = tooltip;
248
+ const { closeDelay, dismissedRef, intent, openDelay, setOpen, triggerRef } = tooltip;
249
249
  useFocusableTrigger(triggerRef, "Tooltip.Trigger");
250
250
 
251
251
  // Whether the pointer put focus here. A click focuses the button, and
252
252
  // reopening the tooltip the click just dismissed would put it back over the
253
253
  // thing the reader pressed — so a focus that arrived with a press opens
254
254
  // nothing, and the next one, which is the keyboard's, does.
255
- const pressed = useRef(false);
255
+ const pressedRef = useRef(false);
256
256
 
257
257
  useEventListener(triggerRef, "pointerenter", (event: $FlowFixMe) => {
258
258
  // A tap is not a hover. See the module header: opening here is what eats
259
259
  // the tap the control was there to receive.
260
- if (event.pointerType === "touch" || dismissed.current) {
260
+ if (event.pointerType === "touch" || dismissedRef.current) {
261
261
  return;
262
262
  }
263
263
  intent.openAfter(openDelay());
@@ -265,33 +265,35 @@ export component TooltipTrigger(children?: React.Node, render?: RenderProp, ...r
265
265
  useEventListener(triggerRef, "pointerleave", () => {
266
266
  // Leaving is what makes a dismissal stop applying: coming back is a fresh
267
267
  // gesture and deserves a fresh answer.
268
- dismissed.current = false;
268
+ dismissedRef.current = false;
269
269
  intent.closeAfter(closeDelay);
270
270
  });
271
271
  useEventListener(triggerRef, "pointerdown", () => {
272
- pressed.current = true;
272
+ pressedRef.current = true;
273
273
  intent.cancel();
274
274
  setOpen(false);
275
275
  });
276
276
  useEventListener(triggerRef, "focusin", () => {
277
- if (pressed.current) {
278
- pressed.current = false;
277
+ if (pressedRef.current) {
278
+ pressedRef.current = false;
279
279
  return;
280
280
  }
281
- if (dismissed.current) {
281
+ if (dismissedRef.current) {
282
282
  return;
283
283
  }
284
284
  // No delay: the reader has already said what they want by arriving here.
285
285
  intent.openAfter(0);
286
286
  });
287
287
  useEventListener(triggerRef, "focusout", () => {
288
- pressed.current = false;
289
- dismissed.current = false;
288
+ pressedRef.current = false;
289
+ dismissedRef.current = false;
290
290
  intent.closeAfter(0);
291
291
  });
292
292
 
293
293
  // Annotated because this one is not written inside a `ref={...}`, and there
294
294
  // is nothing else here for Flow to infer the element's type from.
295
+ // React calls callback refs during commit; pointer and focus handlers read it later.
296
+ // uf-lint-disable-next-line react-compiler/refs
295
297
  const attach = composeRefs(rest.ref, (element: HTMLElement | null) => {
296
298
  triggerRef.current = element;
297
299
  });
@@ -330,14 +332,14 @@ export component TooltipBody(
330
332
  ...rest: Rest
331
333
  ) {
332
334
  const tooltip = useTooltip("Tooltip.Body");
333
- const { closeDelay, intent, open, triggerRef } = tooltip;
335
+ const { closeDelay, dismissedRef, intent, open, triggerRef } = tooltip;
334
336
  const bodyRef = useRef<HTMLElement | null>(null);
335
337
  const close = useStableCallback(() => {
336
338
  // `Escape` dismisses it *and* keeps it dismissed while the reader is still
337
339
  // on the trigger. Without the flag the pointer that is still resting there
338
340
  // — or, for a hover card, the focus it hands back — reopens it at once,
339
341
  // and the key does nothing a reader can see.
340
- tooltip.dismissed.current = true;
342
+ dismissedRef.current = true;
341
343
  intent.cancel();
342
344
  tooltip.setOpen(false);
343
345
  });
@@ -385,6 +387,8 @@ export component TooltipBody(
385
387
  "data-side": anchored.side,
386
388
  "data-state": "open",
387
389
  id: `${tooltip.base}-body`,
390
+ // React calls callback refs during commit; placement effects read it later.
391
+ // uf-lint-disable-next-line react-compiler/refs
388
392
  ref: composeRefs(rest.ref, (element) => {
389
393
  bodyRef.current = element;
390
394
  }),