@evinvest/uikit 0.16.0 → 0.17.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.
- package/README.md +15 -9
- package/dist/index.d.ts +45 -14
- package/dist/index.js +205 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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,23 @@ 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).
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
|
191
|
+
`Intl` (Monday-first): `weekday: "short"`, falling back to `"narrow"` for a
|
|
192
|
+
locale whose short form contains whitespace (vi's "Thứ 2" → "T2", he's
|
|
193
|
+
"יום ב׳" → "ב׳") as long as narrow still yields seven distinct labels. Rust has no `Intl`, so its captions
|
|
194
|
+
stay English. Rust does manual date math; TS uses the built-in `Date`.
|
|
190
195
|
- **date-time-picker:** the kit's own bricks only — an outline trigger, a
|
|
191
196
|
`Popover` with the `Calendar` and two numeric 24-hour hours/minutes fields (no
|
|
192
197
|
native `datetime-local` / `time` input, so the browser's locale popup never
|
|
193
198
|
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
|
-
|
|
199
|
+
defaults — the nav buttons, the hours/minutes `aria-label`s, the clear button,
|
|
200
|
+
the close button (`labels.close`, which keeps the value) and the popover's own
|
|
201
|
+
accessible name (`labels.dialog`). TS formats the trigger label through `Intl` when `locale` is set,
|
|
196
202
|
else `YYYY-MM-DD HH:MM`; Rust has no `Intl`: the ISO-like label or the
|
|
197
203
|
`format` callback, English month/weekday captions. The hidden form value is
|
|
198
204
|
unix seconds in TS and `YYYY-MM-DDTHH:MM` in Rust (no zone there). The Rust
|
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:
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
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
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
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;
|
|
@@ -1444,6 +1472,8 @@ interface DateTimePickerLabels {
|
|
|
1444
1472
|
clear?: string;
|
|
1445
1473
|
/** `aria-label` of the popover dialog; "Choose date and time". */
|
|
1446
1474
|
dialog?: string;
|
|
1475
|
+
/** `aria-label` of the close button; "Close". */
|
|
1476
|
+
close?: string;
|
|
1447
1477
|
}
|
|
1448
1478
|
interface DateTimePickerProps extends React.AriaAttributes, Pick<React.ComponentProps<"button">, "onFocus" | "onBlur"> {
|
|
1449
1479
|
/** Controlled value; `null` = empty. Local-zone `Date` (seconds/ms are zeroed on every change). */
|
|
@@ -1489,10 +1519,11 @@ interface DateTimePickerProps extends React.AriaAttributes, Pick<React.Component
|
|
|
1489
1519
|
* `datetime-local`, so the browser's locale popup never appears.
|
|
1490
1520
|
*
|
|
1491
1521
|
* Every edit reports through `onChange`; the popover stays open after a day
|
|
1492
|
-
* click (the operator still sets the time) and closes on clear
|
|
1493
|
-
*
|
|
1494
|
-
*
|
|
1495
|
-
* and `onBlur` land on the
|
|
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.
|
|
1496
1527
|
*/
|
|
1497
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;
|
|
1498
1529
|
|
package/dist/index.js
CHANGED
|
@@ -109,37 +109,46 @@ function useDismissableLayer(opts) {
|
|
|
109
109
|
|
|
110
110
|
// src/primitives/use-floating.ts
|
|
111
111
|
import * as React5 from "react";
|
|
112
|
+
var ABSOLUTE = Object.freeze({ position: "absolute", top: 0, left: 0 });
|
|
113
|
+
var POSITIONED = /* @__PURE__ */ new Set(["relative", "absolute", "fixed", "sticky"]);
|
|
114
|
+
function toContainingBlock(floating, x, y) {
|
|
115
|
+
const parent = floating.offsetParent;
|
|
116
|
+
const doc = floating.ownerDocument;
|
|
117
|
+
const root = parent === doc.body || parent === doc.documentElement || parent === null;
|
|
118
|
+
const positioned = parent instanceof HTMLElement && (!root || POSITIONED.has(doc.defaultView?.getComputedStyle(parent).position ?? ""));
|
|
119
|
+
if (!positioned) {
|
|
120
|
+
const win = doc.defaultView;
|
|
121
|
+
return { x: x + (win?.scrollX ?? 0), y: y + (win?.scrollY ?? 0), root: true };
|
|
122
|
+
}
|
|
123
|
+
const p = parent.getBoundingClientRect();
|
|
124
|
+
return {
|
|
125
|
+
x: x - p.left - parent.clientLeft + parent.scrollLeft,
|
|
126
|
+
y: y - p.top - parent.clientTop + parent.scrollTop,
|
|
127
|
+
root: false
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function layoutSize(el) {
|
|
131
|
+
return { width: el.offsetWidth, height: el.offsetHeight };
|
|
132
|
+
}
|
|
133
|
+
function viewport() {
|
|
134
|
+
const el = document.documentElement;
|
|
135
|
+
return {
|
|
136
|
+
vw: el.clientWidth || window.innerWidth,
|
|
137
|
+
vh: el.clientHeight || window.innerHeight
|
|
138
|
+
};
|
|
139
|
+
}
|
|
112
140
|
function useFloating(opts) {
|
|
113
141
|
const { anchorRef, open, side = "bottom", align = "center", offset = 4 } = opts;
|
|
114
142
|
const floatingRef = React5.useRef(null);
|
|
115
|
-
const [
|
|
116
|
-
|
|
117
|
-
top: 0,
|
|
118
|
-
left: 0
|
|
119
|
-
});
|
|
120
|
-
const [resolved, setResolved] = React5.useState({
|
|
143
|
+
const [placedSide, setPlacedSide] = React5.useState(side);
|
|
144
|
+
const placement = React5.useRef({
|
|
121
145
|
side,
|
|
122
|
-
|
|
146
|
+
shiftX: 0,
|
|
147
|
+
shiftY: 0
|
|
123
148
|
});
|
|
124
149
|
React5.useLayoutEffect(() => {
|
|
125
150
|
if (!open) return;
|
|
126
|
-
function
|
|
127
|
-
const anchor = anchorRef.current;
|
|
128
|
-
const floating = floatingRef.current;
|
|
129
|
-
if (!anchor || !floating) return;
|
|
130
|
-
const a = anchor.getBoundingClientRect();
|
|
131
|
-
const f = floating.getBoundingClientRect();
|
|
132
|
-
const vw = window.innerWidth;
|
|
133
|
-
const vh = window.innerHeight;
|
|
134
|
-
let placed = side;
|
|
135
|
-
if (side === "bottom" && a.bottom + offset + f.height > vh && a.top - offset - f.height > 0)
|
|
136
|
-
placed = "top";
|
|
137
|
-
else if (side === "top" && a.top - offset - f.height < 0 && a.bottom + offset + f.height < vh)
|
|
138
|
-
placed = "bottom";
|
|
139
|
-
else if (side === "right" && a.right + offset + f.width > vw && a.left - offset - f.width > 0)
|
|
140
|
-
placed = "left";
|
|
141
|
-
else if (side === "left" && a.left - offset - f.width < 0 && a.right + offset + f.width < vw)
|
|
142
|
-
placed = "right";
|
|
151
|
+
function ideal(a, f, placed) {
|
|
143
152
|
let top = 0;
|
|
144
153
|
let left = 0;
|
|
145
154
|
const vertical = placed === "top" || placed === "bottom";
|
|
@@ -156,36 +165,88 @@ function useFloating(opts) {
|
|
|
156
165
|
else if (align === "end") top = a.bottom - f.height;
|
|
157
166
|
else top = a.top + a.height / 2 - f.height / 2;
|
|
158
167
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
168
|
+
return { top, left };
|
|
169
|
+
}
|
|
170
|
+
function write(floating, f, top, left, contain) {
|
|
171
|
+
const p = toContainingBlock(floating, left, top);
|
|
172
|
+
let { x, y } = p;
|
|
173
|
+
if (contain && p.root) {
|
|
174
|
+
const icbWidth = floating.ownerDocument.documentElement.clientWidth || window.innerWidth;
|
|
175
|
+
x = Math.max(0, Math.min(x, icbWidth - f.width));
|
|
176
|
+
y = Math.max(0, y);
|
|
177
|
+
}
|
|
178
|
+
floating.style.translate = `${Math.round(x)}px ${Math.round(y)}px`;
|
|
179
|
+
}
|
|
180
|
+
function place() {
|
|
181
|
+
const anchor = anchorRef.current;
|
|
182
|
+
const floating = floatingRef.current;
|
|
183
|
+
if (!anchor || !floating) return;
|
|
184
|
+
const a = anchor.getBoundingClientRect();
|
|
185
|
+
const f = layoutSize(floating);
|
|
186
|
+
const { vw, vh } = viewport();
|
|
187
|
+
let placed = side;
|
|
188
|
+
if (side === "bottom" && a.bottom + offset + f.height > vh && a.top - offset - f.height > 0)
|
|
189
|
+
placed = "top";
|
|
190
|
+
else if (side === "top" && a.top - offset - f.height < 0 && a.bottom + offset + f.height < vh)
|
|
191
|
+
placed = "bottom";
|
|
192
|
+
else if (side === "right" && a.right + offset + f.width > vw && a.left - offset - f.width > 0)
|
|
193
|
+
placed = "left";
|
|
194
|
+
else if (side === "left" && a.left - offset - f.width < 0 && a.right + offset + f.width < vw)
|
|
195
|
+
placed = "right";
|
|
196
|
+
const { top, left } = ideal(a, f, placed);
|
|
197
|
+
const clampedLeft = Math.max(offset, Math.min(left, vw - f.width - offset));
|
|
198
|
+
const clampedTop = Math.max(offset, Math.min(top, vh - f.height - offset));
|
|
199
|
+
placement.current = {
|
|
200
|
+
side: placed,
|
|
201
|
+
shiftX: clampedLeft - left,
|
|
202
|
+
shiftY: clampedTop - top
|
|
203
|
+
};
|
|
204
|
+
write(floating, f, clampedTop, clampedLeft, false);
|
|
205
|
+
setPlacedSide((prev) => prev === placed ? prev : placed);
|
|
206
|
+
}
|
|
207
|
+
function sync() {
|
|
208
|
+
const anchor = anchorRef.current;
|
|
209
|
+
const floating = floatingRef.current;
|
|
210
|
+
if (!anchor || !floating) return;
|
|
211
|
+
const a = anchor.getBoundingClientRect();
|
|
212
|
+
const f = layoutSize(floating);
|
|
213
|
+
const { side: placed, shiftX, shiftY } = placement.current;
|
|
214
|
+
const { top, left } = ideal(a, f, placed);
|
|
215
|
+
write(floating, f, top + shiftY, left + shiftX, true);
|
|
216
|
+
}
|
|
217
|
+
place();
|
|
218
|
+
window.addEventListener("scroll", sync, true);
|
|
219
|
+
window.addEventListener("resize", place);
|
|
220
|
+
let observer;
|
|
221
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
222
|
+
observer = new ResizeObserver(place);
|
|
223
|
+
if (anchorRef.current) observer.observe(anchorRef.current);
|
|
224
|
+
if (floatingRef.current) observer.observe(floatingRef.current);
|
|
225
|
+
}
|
|
175
226
|
return () => {
|
|
176
|
-
|
|
177
|
-
window.removeEventListener("
|
|
178
|
-
|
|
227
|
+
window.removeEventListener("scroll", sync, true);
|
|
228
|
+
window.removeEventListener("resize", place);
|
|
229
|
+
observer?.disconnect();
|
|
179
230
|
};
|
|
180
231
|
}, [open, side, align, offset, anchorRef]);
|
|
181
|
-
return { floatingRef, style, side:
|
|
232
|
+
return { floatingRef, style: ABSOLUTE, side: placedSide, align };
|
|
182
233
|
}
|
|
183
234
|
|
|
184
235
|
// src/primitives/focus-scope.tsx
|
|
185
236
|
import * as React6 from "react";
|
|
186
|
-
var FOCUSABLE =
|
|
237
|
+
var FOCUSABLE = [
|
|
238
|
+
"a[href]",
|
|
239
|
+
"button:not([disabled])",
|
|
240
|
+
"textarea:not([disabled])",
|
|
241
|
+
"input:not([disabled])",
|
|
242
|
+
"select:not([disabled])",
|
|
243
|
+
"[tabindex]"
|
|
244
|
+
].map((sel) => `${sel}:not([tabindex="-1"])`).join(",");
|
|
245
|
+
function focusCandidates(root) {
|
|
246
|
+
return Array.from(root.querySelectorAll(FOCUSABLE));
|
|
247
|
+
}
|
|
187
248
|
function focusable(root) {
|
|
188
|
-
return
|
|
249
|
+
return focusCandidates(root).filter(
|
|
189
250
|
(el) => el.offsetWidth > 0 || el.offsetHeight > 0 || el === document.activeElement
|
|
190
251
|
);
|
|
191
252
|
}
|
|
@@ -227,9 +288,44 @@ function useFocusScope(enabled) {
|
|
|
227
288
|
|
|
228
289
|
// src/primitives/presence.ts
|
|
229
290
|
import * as React7 from "react";
|
|
291
|
+
import { flushSync } from "react-dom";
|
|
292
|
+
var EXIT_FALLBACK_MS = 1e3;
|
|
230
293
|
function usePresence(present) {
|
|
231
294
|
const ref = React7.useRef(null);
|
|
232
|
-
|
|
295
|
+
const [mounted, setMounted] = React7.useState(present);
|
|
296
|
+
React7.useLayoutEffect(() => {
|
|
297
|
+
if (present) {
|
|
298
|
+
setMounted(true);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const node = ref.current;
|
|
302
|
+
if (!node) {
|
|
303
|
+
setMounted(false);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const name = getComputedStyle(node).animationName;
|
|
307
|
+
if (name === "" || name === "none") {
|
|
308
|
+
setMounted(false);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const prevPointerEvents = node.style.pointerEvents;
|
|
312
|
+
node.style.pointerEvents = "none";
|
|
313
|
+
const onEnd = (e) => {
|
|
314
|
+
if (e.target !== node) return;
|
|
315
|
+
if (e.animationName && !name.includes(e.animationName)) return;
|
|
316
|
+
flushSync(() => setMounted(false));
|
|
317
|
+
};
|
|
318
|
+
node.addEventListener("animationend", onEnd);
|
|
319
|
+
node.addEventListener("animationcancel", onEnd);
|
|
320
|
+
const timer = setTimeout(() => setMounted(false), EXIT_FALLBACK_MS);
|
|
321
|
+
return () => {
|
|
322
|
+
clearTimeout(timer);
|
|
323
|
+
node.removeEventListener("animationend", onEnd);
|
|
324
|
+
node.removeEventListener("animationcancel", onEnd);
|
|
325
|
+
node.style.pointerEvents = prevPointerEvents;
|
|
326
|
+
};
|
|
327
|
+
}, [present]);
|
|
328
|
+
return { isPresent: present || mounted, ref };
|
|
233
329
|
}
|
|
234
330
|
|
|
235
331
|
// src/primitives/use-roving-focus.ts
|
|
@@ -5197,14 +5293,14 @@ function useItem() {
|
|
|
5197
5293
|
function NavigationMenu({
|
|
5198
5294
|
className,
|
|
5199
5295
|
children,
|
|
5200
|
-
viewport = true,
|
|
5296
|
+
viewport: viewport2 = true,
|
|
5201
5297
|
...props
|
|
5202
5298
|
}) {
|
|
5203
5299
|
return /* @__PURE__ */ jsxs18(
|
|
5204
5300
|
"nav",
|
|
5205
5301
|
{
|
|
5206
5302
|
"data-slot": "navigation-menu",
|
|
5207
|
-
"data-viewport":
|
|
5303
|
+
"data-viewport": viewport2,
|
|
5208
5304
|
role: "navigation",
|
|
5209
5305
|
className: cn(
|
|
5210
5306
|
NAVIGATION_MENU,
|
|
@@ -5213,7 +5309,7 @@ function NavigationMenu({
|
|
|
5213
5309
|
...props,
|
|
5214
5310
|
children: [
|
|
5215
5311
|
children,
|
|
5216
|
-
|
|
5312
|
+
viewport2 && /* @__PURE__ */ jsx48(NavigationMenuViewport, {})
|
|
5217
5313
|
]
|
|
5218
5314
|
}
|
|
5219
5315
|
);
|
|
@@ -5911,7 +6007,7 @@ import { jsx as jsx52, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
|
5911
6007
|
var CLOSE_THRESHOLD = 0.25;
|
|
5912
6008
|
var VELOCITY_THRESHOLD = 0.4;
|
|
5913
6009
|
var DRAG_AFTER_OPEN_MS = 500;
|
|
5914
|
-
var
|
|
6010
|
+
var EXIT_FALLBACK_MS2 = 600;
|
|
5915
6011
|
var DrawerContext = React62.createContext(null);
|
|
5916
6012
|
function useDrawer() {
|
|
5917
6013
|
const ctx = React62.useContext(DrawerContext);
|
|
@@ -6118,7 +6214,7 @@ function DrawerContent({ className, children, ...props }) {
|
|
|
6118
6214
|
}, [open]);
|
|
6119
6215
|
React62.useEffect(() => {
|
|
6120
6216
|
if (!closing) return;
|
|
6121
|
-
const id = setTimeout(() => setPresent(false),
|
|
6217
|
+
const id = setTimeout(() => setPresent(false), EXIT_FALLBACK_MS2);
|
|
6122
6218
|
return () => clearTimeout(id);
|
|
6123
6219
|
}, [closing]);
|
|
6124
6220
|
const drag = useDrawerDrag({
|
|
@@ -6878,17 +6974,17 @@ function ChartLegendContent({
|
|
|
6878
6974
|
import * as React66 from "react";
|
|
6879
6975
|
|
|
6880
6976
|
// src/generated/calendar.ts
|
|
6881
|
-
var CALENDAR_ROOT = "
|
|
6977
|
+
var CALENDAR_ROOT = "p-3 w-fit";
|
|
6882
6978
|
var CALENDAR_NAV_BUTTON = "size-8 p-0 select-none";
|
|
6883
6979
|
var CALENDAR_NAV = "flex items-center justify-between gap-1 w-full px-1";
|
|
6884
6980
|
var CALENDAR_CAPTION = "text-sm font-medium select-none";
|
|
6885
6981
|
var CALENDAR_GRID = "w-full border-collapse mt-4";
|
|
6886
6982
|
var CALENDAR_WEEKDAY_ROW = "flex";
|
|
6887
|
-
var CALENDAR_WEEKDAY = "text-ink-soft rounded-md
|
|
6983
|
+
var CALENDAR_WEEKDAY = "text-ink-soft rounded-md w-9 shrink-0 font-normal text-[0.8rem] select-none whitespace-nowrap";
|
|
6888
6984
|
var CALENDAR_WEEK = "flex w-full mt-2";
|
|
6889
|
-
var CALENDAR_DAY_EMPTY = "relative
|
|
6890
|
-
var CALENDAR_DAY_CELL = "relative
|
|
6891
|
-
var CALENDAR_DAY = "size-auto w-full aspect-square font-normal leading-none";
|
|
6985
|
+
var CALENDAR_DAY_EMPTY = "relative size-9 shrink-0 p-0 select-none";
|
|
6986
|
+
var CALENDAR_DAY_CELL = "relative size-9 shrink-0 p-0 text-center select-none";
|
|
6987
|
+
var CALENDAR_DAY = "size-auto w-full aspect-square font-normal leading-none tabular-nums";
|
|
6892
6988
|
var CALENDAR_DAY_SELECTED = "bg-primary text-on-primary";
|
|
6893
6989
|
var CALENDAR_DAY_TODAY = "bg-hover text-ink rounded-md";
|
|
6894
6990
|
|
|
@@ -6947,13 +7043,19 @@ function outOfRange(day, min, max) {
|
|
|
6947
7043
|
return !!min && t < startOfDay(min).getTime() || !!max && t > startOfDay(max).getTime();
|
|
6948
7044
|
}
|
|
6949
7045
|
var MONDAY = new Date(2024, 0, 1);
|
|
6950
|
-
function
|
|
6951
|
-
const fmt = new Intl.DateTimeFormat(locale, { weekday
|
|
7046
|
+
function formatWeekdays(locale, weekday) {
|
|
7047
|
+
const fmt = new Intl.DateTimeFormat(locale, { weekday });
|
|
6952
7048
|
return Array.from(
|
|
6953
7049
|
{ length: 7 },
|
|
6954
7050
|
(_, i) => fmt.format(new Date(MONDAY.getFullYear(), MONDAY.getMonth(), MONDAY.getDate() + i))
|
|
6955
7051
|
);
|
|
6956
7052
|
}
|
|
7053
|
+
function localisedWeekdays(locale) {
|
|
7054
|
+
const short = formatWeekdays(locale, "short");
|
|
7055
|
+
if (!short.some((label) => /\s/.test(label))) return short;
|
|
7056
|
+
const narrow = formatWeekdays(locale, "narrow");
|
|
7057
|
+
return new Set(narrow).size === narrow.length ? narrow : short;
|
|
7058
|
+
}
|
|
6957
7059
|
function Calendar({
|
|
6958
7060
|
selected,
|
|
6959
7061
|
onSelect,
|
|
@@ -6987,7 +7089,7 @@ function Calendar({
|
|
|
6987
7089
|
const cells = [];
|
|
6988
7090
|
for (let i = 0; i < lead; i += 1) cells.push(null);
|
|
6989
7091
|
for (let day = 1; day <= total; day += 1) cells.push(day);
|
|
6990
|
-
while (cells.length
|
|
7092
|
+
while (cells.length < 42) cells.push(null);
|
|
6991
7093
|
const weeks = [];
|
|
6992
7094
|
for (let i = 0; i < cells.length; i += 7) weeks.push(cells.slice(i, i + 7));
|
|
6993
7095
|
const navClass = buttonVariants({
|
|
@@ -7129,6 +7231,25 @@ function CalendarIcon() {
|
|
|
7129
7231
|
}
|
|
7130
7232
|
);
|
|
7131
7233
|
}
|
|
7234
|
+
function XIcon3() {
|
|
7235
|
+
return /* @__PURE__ */ jsx57(
|
|
7236
|
+
"svg",
|
|
7237
|
+
{
|
|
7238
|
+
className: "size-4",
|
|
7239
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
7240
|
+
width: "24",
|
|
7241
|
+
height: "24",
|
|
7242
|
+
viewBox: "0 0 24 24",
|
|
7243
|
+
fill: "none",
|
|
7244
|
+
stroke: "currentColor",
|
|
7245
|
+
strokeWidth: "2",
|
|
7246
|
+
strokeLinecap: "round",
|
|
7247
|
+
strokeLinejoin: "round",
|
|
7248
|
+
"aria-hidden": true,
|
|
7249
|
+
children: /* @__PURE__ */ jsx57("path", { d: "M18 6 6 18M6 6l12 12" })
|
|
7250
|
+
}
|
|
7251
|
+
);
|
|
7252
|
+
}
|
|
7132
7253
|
var pad = (n) => String(n).padStart(2, "0");
|
|
7133
7254
|
function atMinute(d) {
|
|
7134
7255
|
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes());
|
|
@@ -7184,6 +7305,7 @@ function DateTimePicker({
|
|
|
7184
7305
|
});
|
|
7185
7306
|
const rootRef = React67.useRef(null);
|
|
7186
7307
|
const hoursRef = React67.useRef(null);
|
|
7308
|
+
const focusTrigger = () => rootRef.current?.querySelector("[data-slot=date-time-picker-trigger]")?.focus();
|
|
7187
7309
|
React67.useEffect(() => {
|
|
7188
7310
|
if (!isOpen) return;
|
|
7189
7311
|
hoursRef.current?.focus();
|
|
@@ -7191,9 +7313,7 @@ function DateTimePicker({
|
|
|
7191
7313
|
const active = document.activeElement;
|
|
7192
7314
|
const content = hoursRef.current?.closest("[data-slot=date-time-picker-content]");
|
|
7193
7315
|
const lost = !active || active === document.body || !!content?.contains(active);
|
|
7194
|
-
if (lost)
|
|
7195
|
-
rootRef.current?.querySelector("[data-slot=date-time-picker-trigger]")?.focus();
|
|
7196
|
-
}
|
|
7316
|
+
if (lost) focusTrigger();
|
|
7197
7317
|
};
|
|
7198
7318
|
}, [isOpen]);
|
|
7199
7319
|
const emit = (next) => setValue(clampMoment(atMinute(next), min, max));
|
|
@@ -7225,6 +7345,15 @@ function DateTimePicker({
|
|
|
7225
7345
|
setValue(null);
|
|
7226
7346
|
setOpen(false);
|
|
7227
7347
|
};
|
|
7348
|
+
const onContentKeyDown = (e) => {
|
|
7349
|
+
if (e.key !== "Tab") return;
|
|
7350
|
+
const items = focusCandidates(e.currentTarget);
|
|
7351
|
+
const edge = e.shiftKey ? items[0] : items[items.length - 1];
|
|
7352
|
+
if (!edge || e.target !== edge) return;
|
|
7353
|
+
e.preventDefault();
|
|
7354
|
+
setOpen(false);
|
|
7355
|
+
focusTrigger();
|
|
7356
|
+
};
|
|
7228
7357
|
const onContentBlur = (e) => {
|
|
7229
7358
|
const next = e.relatedTarget;
|
|
7230
7359
|
if (!(next instanceof Node)) return;
|
|
@@ -7278,6 +7407,7 @@ function DateTimePicker({
|
|
|
7278
7407
|
"aria-label": labels.dialog ?? "Choose date and time",
|
|
7279
7408
|
align,
|
|
7280
7409
|
onBlur: onContentBlur,
|
|
7410
|
+
onKeyDown: onContentKeyDown,
|
|
7281
7411
|
className: DATE_TIME_PICKER_CONTENT,
|
|
7282
7412
|
children: [
|
|
7283
7413
|
/* @__PURE__ */ jsx57(
|
|
@@ -7336,6 +7466,18 @@ function DateTimePicker({
|
|
|
7336
7466
|
onClick: onClear,
|
|
7337
7467
|
children: labels.clear ?? "Clear"
|
|
7338
7468
|
}
|
|
7469
|
+
),
|
|
7470
|
+
/* @__PURE__ */ jsx57(
|
|
7471
|
+
"button",
|
|
7472
|
+
{
|
|
7473
|
+
type: "button",
|
|
7474
|
+
className: buttonVariants({ variant: "ghost", size: "sm", icon: true }),
|
|
7475
|
+
"data-slot": "date-time-picker-close",
|
|
7476
|
+
"aria-label": labels.close ?? "Close",
|
|
7477
|
+
disabled,
|
|
7478
|
+
onClick: () => setOpen(false),
|
|
7479
|
+
children: /* @__PURE__ */ jsx57(XIcon3, {})
|
|
7480
|
+
}
|
|
7339
7481
|
)
|
|
7340
7482
|
] })
|
|
7341
7483
|
]
|