@lotics/ui 39.0.0 → 40.0.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/AGENTS.md +5 -1
- package/MIGRATION.md +29 -0
- package/docs/catalog.md +11 -4
- package/package.json +1 -1
- package/src/popover.tsx +18 -3
package/AGENTS.md
CHANGED
|
@@ -30,7 +30,11 @@ CURRENT major only — upgrading an app across majors is `MIGRATION.md`.
|
|
|
30
30
|
- **Responsive layout measures the CONTAINER, not the screen.** `useContainerSize()` reports the
|
|
31
31
|
nearest `SizeBoundary`; wrap one around any region whose width stops tracking its parent's, and
|
|
32
32
|
`Dialog`/`Drawer`/popover bodies already are boundaries so their contents get the panel
|
|
33
|
-
rather than the region they were opened from.
|
|
33
|
+
rather than the region they were opened from. The overlay's OWN presentation is not one of
|
|
34
|
+
these questions and is not yours to answer: `PopoverContent` reads the screen itself and takes
|
|
35
|
+
no prop, because it fills the viewport whatever box opened it — handing it a container width is
|
|
36
|
+
how a narrow column on a wide display opened a full-bleed phone sheet.
|
|
37
|
+
`useScreenSize()` answers a question about the DEVICE — is there a keyboard
|
|
34
38
|
worth a shortcut hint, are these touch targets — and reaching for it to decide SPACE is how a
|
|
35
39
|
panel a few hundred pixels wide lays its contents out for the whole display. (A component that
|
|
36
40
|
needs a number rather than a bucket — how many columns fit — measures its own box; `Table`
|
package/MIGRATION.md
CHANGED
|
@@ -4,6 +4,35 @@ Breaking changes, newest first — normally per major, plus the rare minor that
|
|
|
4
4
|
anyway (recorded under its exact version). The current contract lives in `AGENTS.md` + `docs/`;
|
|
5
5
|
this file exists only to move an app from one release to the next.
|
|
6
6
|
|
|
7
|
+
## 40.0.0 — `PopoverContent` decides its own presentation; `small` is gone
|
|
8
|
+
|
|
9
|
+
`PopoverContent` took a `small` prop meaning "render as a bottom sheet". It now asks
|
|
10
|
+
`useScreenSize()` itself. **Delete the prop** — passing it is a type error.
|
|
11
|
+
|
|
12
|
+
```diff
|
|
13
|
+
- <PopoverContent small={small}>
|
|
14
|
+
+ <PopoverContent>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Why the prop had to go rather than be defaulted.** A popover is an overlay: it fills the
|
|
18
|
+
viewport regardless of which box it was opened from, so the only input is how wide that
|
|
19
|
+
viewport is — and there is exactly one right answer per viewport. Every caller had to
|
|
20
|
+
supply it by hand, and the two ways to get it wrong both shipped: one passed
|
|
21
|
+
`useContainerSize()` — the width of the *column* holding the trigger — so a narrow chat
|
|
22
|
+
column on a 1728px display opened a full-bleed modal sheet; another passed nothing and so
|
|
23
|
+
never became a sheet on a real phone. Leaving the prop as an override would have kept both
|
|
24
|
+
mistakes writable.
|
|
25
|
+
|
|
26
|
+
**What changes for a NARROW-VIEWPORT host.** The rule is the viewport, not the device, so a
|
|
27
|
+
surface that is a few hundred pixels wide on a desktop — a browser side panel, an embedded
|
|
28
|
+
frame — now gets the bottom sheet where a popover that passed nothing was previously always
|
|
29
|
+
anchored. If that is wrong for your surface, argue it as a change to the threshold or to what
|
|
30
|
+
the rule reads, for every consumer at once; do not reach for a per-instance override.
|
|
31
|
+
|
|
32
|
+
If you were computing `small` only to feed this prop, drop the hook call too. `useScreenSize`
|
|
33
|
+
remains exported for its real uses — deciding whether a surface is a `Dialog` or a `Popover`
|
|
34
|
+
at all, sizing touch targets, showing a keyboard shortcut.
|
|
35
|
+
|
|
7
36
|
## 39.0.0 — `PageHeader.leading` is `trailing`, and it renders AFTER the title
|
|
8
37
|
|
|
9
38
|
38.0.0 shipped `leading`, a slot before the title. It is now **`trailing`**, in the same
|
package/docs/catalog.md
CHANGED
|
@@ -1247,8 +1247,13 @@ source (`src/<module>.tsx`/`.ts`) is the API reference.
|
|
|
1247
1247
|
control both dismisses this popover AND activates that control in one click; clicking outside,
|
|
1248
1248
|
scrolling an ancestor, or Escape dismisses. Interactions inside a layer the popover opened
|
|
1249
1249
|
from within (a nested popover, an `Alert` confirm, a full-screen Modal like the file preview)
|
|
1250
|
-
never dismiss it — clicks, scrolls, and Escape belong to that layer until it closes.
|
|
1251
|
-
`small
|
|
1250
|
+
never dismiss it — clicks, scrolls, and Escape belong to that layer until it closes. In a
|
|
1251
|
+
NARROW VIEWPORT (`small`, under 768) it presents instead as a modal bottom sheet (scrim, close
|
|
1252
|
+
button, slide-up) — decided by `PopoverContent` itself off the screen size, with nothing to pass
|
|
1253
|
+
and no way to answer it differently: an overlay fills the viewport whatever box it was opened
|
|
1254
|
+
from, so the width of the column holding the trigger is never the question. The viewport, not
|
|
1255
|
+
the device — a few-hundred-pixel side panel on a desktop gets the sheet too.
|
|
1256
|
+
`PopoverContent` already insets its
|
|
1252
1257
|
body 12px — put content directly in it, NEVER add your own padding `View` (that double-pads);
|
|
1253
1258
|
title/actions go in `PopoverHeader` / `PopoverFooter`. **A fixed width is the `width` PROP,
|
|
1254
1259
|
never `style`**: `style` reaches only the BODY, and the panel is `max-content`, so a width set
|
|
@@ -1786,8 +1791,10 @@ source (`src/<module>.tsx`/`.ts`) is the API reference.
|
|
|
1786
1791
|
width crosses a threshold — a height-only change (the mobile soft keyboard) and a resize
|
|
1787
1792
|
inside one bucket both return the identical value. Correct only for
|
|
1788
1793
|
questions about the device rather than the space: is there a keyboard to show a shortcut
|
|
1789
|
-
for, are these touch-sized targets, and
|
|
1790
|
-
|
|
1794
|
+
for, are these touch-sized targets, and whether a surface is a `Dialog` on a phone and a
|
|
1795
|
+
`Popover` otherwise. A popover's own anchored-vs-sheet presentation is NOT one of them —
|
|
1796
|
+
`PopoverContent` reads this itself and takes no prop. Everything spatial wants
|
|
1797
|
+
`useContainerSize`.
|
|
1791
1798
|
- **`use_async_fn`** — `useAsyncFn`: wrap an async function into a manual-trigger mutation —
|
|
1792
1799
|
`[run, {loading, data, error}]`, unmount-safe, the error lands in state AND rethrows; the
|
|
1793
1800
|
pending-state engine for a submit/download/upload action.
|
package/package.json
CHANGED
package/src/popover.tsx
CHANGED
|
@@ -13,6 +13,7 @@ import { MIN_CONTROL_WIDTH } from "./control_surface";
|
|
|
13
13
|
import { IconButton } from "./icon_button";
|
|
14
14
|
import { Portal } from "./portal";
|
|
15
15
|
import { SizeBoundary } from "./size_boundary";
|
|
16
|
+
import { useScreenSize } from "./use_screen_size";
|
|
16
17
|
import { Divider } from "./divider";
|
|
17
18
|
import { useOverlayScope } from "./overlay_scope";
|
|
18
19
|
import {
|
|
@@ -209,8 +210,6 @@ export interface PopoverContentProps {
|
|
|
209
210
|
* both.
|
|
210
211
|
*/
|
|
211
212
|
width?: number;
|
|
212
|
-
/** When true, renders as bottom sheet on small screens (close button, slide-up animation) */
|
|
213
|
-
small?: boolean;
|
|
214
213
|
/** Accessible name for the bottom-sheet close button. Defaults to the locale's `overlay.close` ("Close" / "Đóng"). */
|
|
215
214
|
closeLabel?: string;
|
|
216
215
|
/** When true (default), the popover moves focus into its content on open and
|
|
@@ -230,10 +229,26 @@ export function PopoverContent(props: PopoverContentProps) {
|
|
|
230
229
|
contentContainerStyle,
|
|
231
230
|
disableBodyScroll,
|
|
232
231
|
width,
|
|
233
|
-
small = false,
|
|
234
232
|
closeLabel = locale.overlay.close,
|
|
235
233
|
manageFocus = true,
|
|
236
234
|
} = props;
|
|
235
|
+
// The VIEWPORT, never the container, and never a caller's opinion of either.
|
|
236
|
+
// A popover is an overlay: it fills the viewport regardless of the box it was
|
|
237
|
+
// opened from, so the only input to "anchored or bottom sheet" is how wide
|
|
238
|
+
// that viewport is — under `small` (<768) the sheet wins.
|
|
239
|
+
//
|
|
240
|
+
// This was a `small` prop and every caller answered it by hand. One answered
|
|
241
|
+
// with `useContainerSize()`, the width of whatever column held the trigger, so
|
|
242
|
+
// a narrow chat column on a desktop opened a full-bleed sheet; another passed
|
|
243
|
+
// nothing and never became a sheet on a real phone. Reading it here is the
|
|
244
|
+
// only way neither can be written again.
|
|
245
|
+
//
|
|
246
|
+
// Note this is the viewport and NOT "is this a phone": a browser side panel is
|
|
247
|
+
// a few hundred pixels of a desktop and gets the sheet too. That is the rule
|
|
248
|
+
// behaving as defined rather than a bug, but it is the reason a surface that
|
|
249
|
+
// wants a different presentation must change the RULE here, for everyone, and
|
|
250
|
+
// never re-introduce a per-caller override.
|
|
251
|
+
const { small } = useScreenSize();
|
|
237
252
|
const { open, onOpenChange, triggerRef, side, align, offset, inheritTriggerWidth } =
|
|
238
253
|
usePopoverContext();
|
|
239
254
|
|