@carlonicora/nextjs-jsonapi 3.3.4 → 3.3.6

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.
Files changed (29) hide show
  1. package/dist/{BlockNoteEditor-ZNEXXORX.mjs → BlockNoteEditor-BYMRWUVG.mjs} +2 -2
  2. package/dist/{BlockNoteEditor-J4TQJSHZ.js → BlockNoteEditor-USRMFZGP.js} +9 -9
  3. package/dist/{BlockNoteEditor-J4TQJSHZ.js.map → BlockNoteEditor-USRMFZGP.js.map} +1 -1
  4. package/dist/billing/index.js +333 -333
  5. package/dist/billing/index.mjs +1 -1
  6. package/dist/{chunk-KCZ6SPUQ.js → chunk-5WFYQE22.js} +234 -233
  7. package/dist/chunk-5WFYQE22.js.map +1 -0
  8. package/dist/{chunk-GCE4C7FG.mjs → chunk-CFZW6GU4.mjs} +14 -13
  9. package/dist/chunk-CFZW6GU4.mjs.map +1 -0
  10. package/dist/client/index.js +2 -2
  11. package/dist/client/index.mjs +1 -1
  12. package/dist/components/index.d.mts +39 -3
  13. package/dist/components/index.d.ts +39 -3
  14. package/dist/components/index.js +4 -2
  15. package/dist/components/index.js.map +1 -1
  16. package/dist/components/index.mjs +3 -1
  17. package/dist/contexts/index.js +2 -2
  18. package/dist/contexts/index.mjs +1 -1
  19. package/dist/features/help/index.js +31 -31
  20. package/dist/features/help/index.mjs +1 -1
  21. package/dist/features/tokenusage/index.js +75 -75
  22. package/dist/features/tokenusage/index.mjs +1 -1
  23. package/package.json +1 -1
  24. package/src/components/forms/DatePickerPopover.tsx +31 -3
  25. package/src/components/forms/DateRangeSelector.tsx +36 -12
  26. package/src/components/forms/__tests__/DateRangeSelector.spec.ts +74 -0
  27. package/dist/chunk-GCE4C7FG.mjs.map +0 -1
  28. package/dist/chunk-KCZ6SPUQ.js.map +0 -1
  29. /package/dist/{BlockNoteEditor-ZNEXXORX.mjs.map → BlockNoteEditor-BYMRWUVG.mjs.map} +0 -0
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { isValid, parse } from "date-fns";
4
4
  import { Calendar as CalendarIcon, CircleXIcon } from "lucide-react";
5
- import { ReactNode, useMemo, useState } from "react";
5
+ import { ReactElement, useMemo, useState } from "react";
6
6
  import { useI18nDateFnsLocale, useI18nLocale } from "../../i18n";
7
7
  import {
8
8
  Calendar,
@@ -19,7 +19,19 @@ import {
19
19
  import { cn } from "../../utils";
20
20
 
21
21
  type DatePickerPopoverProps = {
22
- children: ReactNode;
22
+ /** MUST be a single element — it becomes the trigger element itself (see `render` below). */
23
+ children: ReactElement;
24
+ /**
25
+ * Whether the element passed as `children` is a real <button>.
26
+ *
27
+ * Base UI's trigger assumes it renders a native button. When `render` swaps
28
+ * in something else (a table cell's <div> wrapper), that assumption is wrong:
29
+ * native button semantics are lost and Base UI warns on every mount. Passing
30
+ * `false` makes it apply role="button" plus the keyboard handling itself, so
31
+ * a non-button trigger stays operable and accessible.
32
+ */
33
+ nativeButton?: boolean;
34
+
23
35
  value?: Date;
24
36
  onSelect: (date?: Date) => void;
25
37
  minDate?: Date;
@@ -29,6 +41,7 @@ type DatePickerPopoverProps = {
29
41
 
30
42
  export const DatePickerPopover = ({
31
43
  children,
44
+ nativeButton = true,
32
45
  value,
33
46
  onSelect,
34
47
  minDate,
@@ -126,7 +139,22 @@ export const DatePickerPopover = ({
126
139
 
127
140
  return (
128
141
  <Popover open={isOpen} onOpenChange={setIsOpen}>
129
- <PopoverTrigger>{children}</PopoverTrigger>
142
+ {/*
143
+ `render`, NOT `<PopoverTrigger>{children}</PopoverTrigger>`. Base UI's
144
+ Popover.Trigger otherwise renders its OWN <button> WRAPPING the child
145
+ and binds the open handler to that button. Two consequences, both real:
146
+ - Table callers pass a cell wrapper whose onClick calls
147
+ stopPropagation() (so clicking the cell does not trigger the row's
148
+ navigation). Being the deeper element, its handler runs FIRST, the
149
+ click never bubbles to Base UI's button, and the popover could never
150
+ open — for mouse users as much as for tests.
151
+ - Form callers pass a <Button>, which the wrapper turned into a
152
+ <button> inside a <button>: invalid HTML and a hydration error.
153
+ With `render` the child IS the trigger, so the handlers share one
154
+ element (stopPropagation stops ANCESTOR propagation, not other handlers
155
+ on the same node) and no nested button is produced.
156
+ */}
157
+ <PopoverTrigger nativeButton={nativeButton} render={children} />
130
158
  <PopoverContent className={cn("p-0", className)} align={align} onClick={(e) => e.stopPropagation()}>
131
159
  <div className="p-3">
132
160
  {/* Manual Input */}
@@ -9,6 +9,39 @@ import { useI18nDateFnsLocale } from "../../i18n";
9
9
  import { Button, Calendar, Popover, PopoverContent, PopoverTrigger } from "../../shadcnui";
10
10
  import { cn } from "../../utils";
11
11
 
12
+ /**
13
+ * Decides the range to hold after react-day-picker reports a selection.
14
+ *
15
+ * react-day-picker's `addToRange` NEVER moves `from` when the current range is
16
+ * already COMPLETE and the clicked day falls after it — it returns
17
+ * `{ from, to: clickedDay }` (see its `from && to` branch). So "did `from`
18
+ * change?" cannot distinguish "the user is starting a new range" from "the user
19
+ * moved the end": it is true only for a click BEFORE `from`.
20
+ *
21
+ * That is the asymmetry this component shipped with. Its default range is the
22
+ * whole CURRENT month, so clicking any day in the current month landed after
23
+ * `from` and silently became the END date — the start could never be moved
24
+ * forward — while clicking a day in a PAST month fell before `from`, changed it,
25
+ * and appeared to work.
26
+ *
27
+ * A click on a complete range therefore starts a NEW range at the clicked day,
28
+ * which is the conventional range-picker behaviour. `triggerDate` is the day
29
+ * react-day-picker reports as the one that was clicked (`onSelect`'s second
30
+ * argument) — the only reliable source for it, since the computed range hides it.
31
+ */
32
+ export function nextDateRange(params: {
33
+ current: DateRange | undefined;
34
+ computed: DateRange | undefined;
35
+ triggerDate: Date;
36
+ }): DateRange | undefined {
37
+ const { current, computed, triggerDate } = params;
38
+ // react-day-picker returns undefined to DESELECT (clicking the only selected
39
+ // day of a single-day range). Honour that before anything else.
40
+ if (!computed) return undefined;
41
+ if (current?.from && current?.to) return { from: triggerDate, to: undefined };
42
+ return computed;
43
+ }
44
+
12
45
  type DateRangeSelectorProps = {
13
46
  onDateChange: (date?: DateRange) => void;
14
47
  avoidSettingDates?: boolean;
@@ -49,18 +82,9 @@ export function DateRangeSelector({ onDateChange, avoidSettingDates, showPreviou
49
82
  }
50
83
  }, [date, prevRange, onDateChange]);
51
84
 
52
- // Custom handler to reset end date if a new start date is picked
53
- const handleSelect = (range: DateRange | undefined) => {
54
- if (!range) {
55
- setDate(undefined);
56
- return;
57
- }
58
- // If a new start date is picked, reset end date
59
- if (range.from && (!date?.from || range.from.getTime() !== date.from.getTime())) {
60
- setDate({ from: range.from, to: undefined });
61
- } else {
62
- setDate(range);
63
- }
85
+ // A click on an already-complete range starts a new one see nextDateRange().
86
+ const handleSelect = (range: DateRange | undefined, triggerDate: Date) => {
87
+ setDate(nextDateRange({ current: date, computed: range, triggerDate }));
64
88
  };
65
89
 
66
90
  // Show placeholder button during SSR to prevent hydration mismatch
@@ -0,0 +1,74 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { nextDateRange } from "../DateRangeSelector";
3
+
4
+ // The dates below mirror the component's own default range: the whole current
5
+ // month. "Today" is irrelevant to nextDateRange() — only the shape of the range
6
+ // matters — so fixed dates are used rather than a faked clock.
7
+ const AUG_1 = new Date(2026, 7, 1);
8
+ const AUG_10 = new Date(2026, 7, 10);
9
+ const AUG_20 = new Date(2026, 7, 20);
10
+ const AUG_31 = new Date(2026, 7, 31);
11
+ const JUL_10 = new Date(2026, 6, 10);
12
+
13
+ const CURRENT_MONTH = { from: AUG_1, to: AUG_31 };
14
+
15
+ describe("nextDateRange", () => {
16
+ // The reported bug: with the default (complete) current-month range, every
17
+ // click in the current month lands AFTER `from`, so react-day-picker returns
18
+ // { from: AUG_1, to: clicked } and `from` never moves. The selector must read
19
+ // that as "start a new range at the clicked day", not "move the end".
20
+ it("starts a new range when a complete range is clicked inside the current month", () => {
21
+ expect(
22
+ nextDateRange({
23
+ current: CURRENT_MONTH,
24
+ computed: { from: AUG_1, to: AUG_10 }, // what addToRange returns
25
+ triggerDate: AUG_10,
26
+ }),
27
+ ).toEqual({ from: AUG_10, to: undefined });
28
+ });
29
+
30
+ // The half that always worked, and which must keep working: a click BEFORE
31
+ // `from` does move `from`, so react-day-picker returns { from: clicked, to }.
32
+ // Same outcome either way — the clicked day becomes the new start.
33
+ it("starts a new range when a complete range is clicked in a past month", () => {
34
+ expect(
35
+ nextDateRange({
36
+ current: CURRENT_MONTH,
37
+ computed: { from: JUL_10, to: AUG_31 },
38
+ triggerDate: JUL_10,
39
+ }),
40
+ ).toEqual({ from: JUL_10, to: undefined });
41
+ });
42
+
43
+ it("completes an in-progress range with the second click", () => {
44
+ expect(
45
+ nextDateRange({
46
+ current: { from: AUG_10, to: undefined },
47
+ computed: { from: AUG_10, to: AUG_20 },
48
+ triggerDate: AUG_20,
49
+ }),
50
+ ).toEqual({ from: AUG_10, to: AUG_20 });
51
+ });
52
+
53
+ it("keeps react-day-picker's swap when the second click precedes the start", () => {
54
+ expect(
55
+ nextDateRange({
56
+ current: { from: AUG_20, to: undefined },
57
+ computed: { from: AUG_10, to: AUG_20 },
58
+ triggerDate: AUG_10,
59
+ }),
60
+ ).toEqual({ from: AUG_10, to: AUG_20 });
61
+ });
62
+
63
+ it("opens a range from the empty state", () => {
64
+ expect(
65
+ nextDateRange({ current: undefined, computed: { from: AUG_10, to: AUG_10 }, triggerDate: AUG_10 }),
66
+ ).toEqual({ from: AUG_10, to: AUG_10 });
67
+ });
68
+
69
+ // react-day-picker returns undefined to DESELECT; that must survive the
70
+ // complete-range branch rather than being turned into a new start.
71
+ it("clears when react-day-picker deselects", () => {
72
+ expect(nextDateRange({ current: { from: AUG_10, to: AUG_10 }, computed: undefined, triggerDate: AUG_10 })).toBeUndefined();
73
+ });
74
+ });