@evinvest/uikit 0.15.2 → 0.17.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/README.md CHANGED
@@ -170,8 +170,8 @@ measuring needs host-only `web-sys`). Known gaps:
170
170
  - **Rust overlays** (dialog, popover, dropdown, select, menus, tooltip, …) render
171
171
  inline with `position:fixed` + a backdrop and CSS-only placement — no portal,
172
172
  no viewport-measured floating, native focus order (no trap). TS overlays use a
173
- real `Portal`, single-flip `useFloating`, `useDismissableLayer`, and
174
- `useFocusScope`.
173
+ real `Portal`, single-flip `useFloating` (absolute in the document, so it
174
+ scrolls with the page natively), `useDismissableLayer`, and `useFocusScope`.
175
175
  - **chart:** the recharts plotting engine is not bundled. `ChartContainer` is a
176
176
  themed SVG host (emits `--color-*` from its config); `ChartTooltipContent` /
177
177
  `ChartLegendContent` are presentational and take explicit items. Draw series
@@ -182,17 +182,21 @@ measuring needs host-only `web-sys`). Known gaps:
182
182
  submit's default — inputs, validation and the order itself are the
183
183
  consumer's. The open-orders pane reuses `Table` and `Tabs`.
184
184
  - **calendar:** single month, single-date selection (no range/multi-month, no
185
- dropdown captions). `min`/`max` bound the grid at day granularity (days outside
186
- render `disabled` + `data-disabled="true"`); `disabled` freezes the
187
- whole grid and the nav the same way; the nav buttons take label overrides. TS-only `locale` renders the caption and weekday headers through
185
+ dropdown captions). The grid is always six fixed weeks of 36px cells, so its
186
+ size never changes with the month or the locale's weekday labels, and it
187
+ paints no background of its own (the host does). `min`/`max` bound the grid
188
+ at day granularity (days outside render `disabled` + `data-disabled="true"`);
189
+ `disabled` freezes the whole grid and the nav the same way; the nav buttons
190
+ take label overrides. TS-only `locale` renders the caption and weekday headers through
188
191
  `Intl` (Monday-first); Rust has no `Intl`, so its captions stay English. Rust
189
192
  does manual date math; TS uses the built-in `Date`.
190
193
  - **date-time-picker:** the kit's own bricks only — an outline trigger, a
191
194
  `Popover` with the `Calendar` and two numeric 24-hour hours/minutes fields (no
192
195
  native `datetime-local` / `time` input, so the browser's locale popup never
193
196
  appears). Its ARIA and button strings are `labels` overrides with English
194
- defaults — the nav buttons, the hours/minutes `aria-label`s, the clear button
195
- and the popover's own accessible name (`labels.dialog`). TS formats the trigger label through `Intl` when `locale` is set,
197
+ defaults — the nav buttons, the hours/minutes `aria-label`s, the clear button,
198
+ the close button (`labels.close`, which keeps the value) and the popover's own
199
+ accessible name (`labels.dialog`). TS formats the trigger label through `Intl` when `locale` is set,
196
200
  else `YYYY-MM-DD HH:MM`; Rust has no `Intl`: the ISO-like label or the
197
201
  `format` callback, English month/weekday captions. The hidden form value is
198
202
  unix seconds in TS and `YYYY-MM-DDTHH:MM` in Rust (no zone there). The Rust
@@ -215,9 +219,19 @@ measuring needs host-only `web-sys`). Known gaps:
215
219
  onto an arbitrary child (no `Slot`), so it publishes them as a
216
220
  `FormControlContext` that `Input`/`Textarea` consume; wrap a bare element and
217
221
  you wire them yourself.
218
- - **resizable / carousel / drawer:** pointer-drag physics are TS-only (keyboard
219
- in Rust for resizable; prev/next + keyboard for carousel; click-to-dismiss for
220
- drawer). Embla momentum / vaul drag-to-dismiss are not reproduced.
222
+ - **resizable / carousel:** pointer-drag physics are TS-only (keyboard in Rust
223
+ for resizable; prev/next + keyboard for carousel). Embla momentum is not
224
+ reproduced.
225
+ - **drawer:** the Vaul-style enter/exit motion is shared by both ports via
226
+ `motion.css` (inlined into `tokens.css`), keyed on `data-slot` +
227
+ `data-vaul-drawer-direction` + `data-state`; the panel stays mounted on close
228
+ until its exit `transitionend`. Drag-to-dismiss (Vaul's pointer physics, from
229
+ any of the four edges; opt a region out with `data-vaul-no-drag`) is TS-only;
230
+ Rust dismisses on scrim click / Escape / `DrawerClose`. The panel is
231
+ `touch-action: none` so a touch never turns into a page pan, which also means
232
+ the panel itself must not scroll: the kit renders children inside a
233
+ `data-slot="drawer-body"` scroller (`overflow-y-auto`), so cap the sheet with
234
+ `max-h-*` on `DrawerContent` and let the body scroll by itself.
221
235
  - **sidebar:** the mobile-sheet integration, cookie persistence, and keyboard
222
236
  shortcut are omitted.
223
237
  - **brand chrome (header / footer / status pages):** TS routes links through an
package/dist/index.d.ts CHANGED
@@ -80,11 +80,28 @@ interface FloatingResult {
80
80
  align: Align;
81
81
  }
82
82
  /**
83
- * Positions a floating element against an anchor with `position: fixed` and
84
- * `getBoundingClientRect` — the dep-light stand-in for `@radix-ui/react-popper`
85
- * / floating-ui. Supports a preferred `side`/`align`, an `offset`, and a single
86
- * collision flip onto the opposite side when the preferred side overflows the
87
- * viewport. Recomputes on scroll and resize while `open`.
83
+ * Positions a floating element against an anchor with `position: absolute` in
84
+ * the coordinate space of its containing block (the `Portal` puts it in
85
+ * `document.body`, so normally document coordinates) — the dep-light stand-in
86
+ * for `@radix-ui/react-popper` / floating-ui. Supports a preferred
87
+ * `side`/`align`, an `offset`, and a single collision flip onto the opposite
88
+ * side when the preferred side overflows the viewport.
89
+ *
90
+ * Two update modes:
91
+ * - `place()` — full pass (flip + clamp into the viewport) on open, on window
92
+ * `resize`, and whenever the anchor or the floating element changes size.
93
+ * - `sync()` — re-derives the coordinates from the anchor with the side and
94
+ * the clamp shift already chosen; runs on every `scroll` (captured on
95
+ * `window`, so nested scroll containers count too).
96
+ *
97
+ * Because the element is absolutely positioned in the document, a document
98
+ * scroll moves it natively with the page — `sync()` then computes the same
99
+ * numbers and is a no-op — so the overlay never lags behind the compositor.
100
+ * Only a nested scroll container changes the coordinates, and there a frame
101
+ * of lag is acceptable. The side is never revisited on scroll: no jumping.
102
+ * A page that scrolls `body` instead of the document (`html { overflow:
103
+ * hidden } body { overflow: auto }`) is such a nested scroller: correct, but
104
+ * with the frame of lag the `fixed` strategy had.
88
105
  *
89
106
  * Rust's overlays use CSS-only placement (`data-side`) and do not measure; see
90
107
  * the README "Limitations".
@@ -109,11 +126,22 @@ declare function useFloating(opts: {
109
126
  declare function useFocusScope(enabled: boolean): React.RefObject<HTMLDivElement | null>;
110
127
 
111
128
  /**
112
- * Tracks whether an overlay should be mounted. Mount/unmount steps in lockstep
113
- * with `present`: enter animations (`data-[state=open]:animate-in`) still play
114
- * on mount, but the kit intentionally does **not** defer unmount for an exit
115
- * animation — deferring made portaled overlays flicker on close. Pass `ref`
116
- * through to the animated node (kept for API stability / future use).
129
+ * Keeps an overlay mounted through its exit animation. Enter animations
130
+ * (`data-[state=open]:animate-in`) play on mount; once `present` turns false
131
+ * the consumer flips the node to `data-state="closed"` in the same render and
132
+ * the node stays until its exit animation ends (or the fallback timer fires).
133
+ * Re-opening mid-exit just flips the state back; the node is never remounted.
134
+ *
135
+ * The exit was dropped once (5728b40) because closing flickered: tw-animate's
136
+ * `animate-out` runs with `animation-fill-mode: none`, so after `animationend`
137
+ * the node snaps back to full opacity/scale for the frame React needs to
138
+ * process an async unmount. Hence `flushSync` inside the native listener —
139
+ * the unmount commits before the browser paints that frame (Radix Presence
140
+ * does the same). With no animation on the node (jsdom, overlays without an
141
+ * exit class) unmount is immediate, as before. The Rust mirror still does not
142
+ * defer unmount.
143
+ *
144
+ * Pass `ref` to the animated node — the one carrying `data-state`.
117
145
  */
118
146
  declare function usePresence(present: boolean): {
119
147
  isPresent: boolean;
@@ -1242,10 +1270,10 @@ declare function SheetTitle({ className, ...props }: React.ComponentProps<"h2">)
1242
1270
  declare function SheetDescription({ className, ...props }: React.ComponentProps<"p">): React.JSX.Element;
1243
1271
 
1244
1272
  declare const drawerDirectionClasses: {
1245
- readonly bottom: "data-[state=open]:slide-in-from-bottom data-[state=closed]:slide-out-to-bottom inset-x-0 bottom-0 mt-24 max-h-[80vh] flex-col rounded-t-lg border-b-0";
1246
- readonly top: "data-[state=open]:slide-in-from-top data-[state=closed]:slide-out-to-top inset-x-0 top-0 mb-24 max-h-[80vh] flex-col rounded-b-lg border-t-0";
1247
- readonly left: "data-[state=open]:slide-in-from-left data-[state=closed]:slide-out-to-left inset-y-0 left-0 w-3/4 flex-row border-r sm:max-w-sm";
1248
- readonly right: "data-[state=open]:slide-in-from-right data-[state=closed]:slide-out-to-right inset-y-0 right-0 w-3/4 flex-row border-l sm:max-w-sm";
1273
+ readonly bottom: "inset-x-0 bottom-0 mt-24 max-h-[80vh] flex-col rounded-t-lg border-b-0";
1274
+ readonly top: "inset-x-0 top-0 mb-24 max-h-[80vh] flex-col rounded-b-lg border-t-0";
1275
+ readonly left: "inset-y-0 left-0 w-3/4 flex-row border-r sm:max-w-sm";
1276
+ readonly right: "inset-y-0 right-0 w-3/4 flex-row border-l sm:max-w-sm";
1249
1277
  };
1250
1278
  type DrawerDirection = keyof typeof drawerDirectionClasses;
1251
1279
 
@@ -1265,7 +1293,20 @@ interface DrawerCloseProps extends React.ComponentProps<"button"> {
1265
1293
  asChild?: boolean;
1266
1294
  }
1267
1295
  declare function DrawerClose({ asChild, onClick, ...props }: DrawerCloseProps): React.JSX.Element;
1296
+ /**
1297
+ * Standalone scrim, redundant with the one {@link DrawerContent} renders (as in
1298
+ * shadcn). Mounted only while open; the animated exit lives on the content's
1299
+ * own scrim.
1300
+ */
1268
1301
  declare function DrawerOverlay({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element | null;
1302
+ /**
1303
+ * The panel plus its scrim. Presence outlives `open`: once it turns false the
1304
+ * panel flips to `data-state="closed"` and is unmounted when its exit
1305
+ * `transform` transition ends (or the fallback timer fires). Re-opening
1306
+ * mid-exit just flips the state back and the same transition carries the
1307
+ * panel home. Drag-to-dismiss is TS-only pointer physics; opt a region out
1308
+ * with `data-vaul-no-drag`.
1309
+ */
1269
1310
  declare function DrawerContent({ className, children, ...props }: React.ComponentProps<"div">): React.JSX.Element | null;
1270
1311
  declare function DrawerHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
1271
1312
  declare function DrawerFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
@@ -1431,6 +1472,8 @@ interface DateTimePickerLabels {
1431
1472
  clear?: string;
1432
1473
  /** `aria-label` of the popover dialog; "Choose date and time". */
1433
1474
  dialog?: string;
1475
+ /** `aria-label` of the close button; "Close". */
1476
+ close?: string;
1434
1477
  }
1435
1478
  interface DateTimePickerProps extends React.AriaAttributes, Pick<React.ComponentProps<"button">, "onFocus" | "onBlur"> {
1436
1479
  /** Controlled value; `null` = empty. Local-zone `Date` (seconds/ms are zeroed on every change). */
@@ -1476,10 +1519,11 @@ interface DateTimePickerProps extends React.AriaAttributes, Pick<React.Component
1476
1519
  * `datetime-local`, so the browser's locale popup never appears.
1477
1520
  *
1478
1521
  * Every edit reports through `onChange`; the popover stays open after a day
1479
- * click (the operator still sets the time) and closes on clear. Opening moves
1480
- * focus into the hours field; closing hands it back to the trigger unless the
1481
- * operator already focused something else. Any other `aria-*` prop, `onFocus`
1482
- * and `onBlur` land on the trigger.
1522
+ * click (the operator still sets the time) and closes on clear or on the close
1523
+ * button (which keeps the value). Opening moves focus into the hours field;
1524
+ * closing hands it back to the trigger unless the operator already focused
1525
+ * something else. Any other `aria-*` prop, `onFocus` and `onBlur` land on the
1526
+ * trigger.
1483
1527
  */
1484
1528
  declare function DateTimePicker({ value: valueProp, defaultValue, onChange, min, max, locale, format, placeholder, labels, disabled, name, today, open, defaultOpen, onOpenChange, align, className, id, ...rest }: DateTimePickerProps): React.JSX.Element;
1485
1529