@carlonicora/nextjs-jsonapi 1.48.3 → 1.50.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.
Files changed (41) hide show
  1. package/dist/{BlockNoteEditor-57KPRQDB.js → BlockNoteEditor-LENHWRS2.js} +6 -6
  2. package/dist/{BlockNoteEditor-57KPRQDB.js.map → BlockNoteEditor-LENHWRS2.js.map} +1 -1
  3. package/dist/{BlockNoteEditor-GOWLYEKX.mjs → BlockNoteEditor-WLS36QIF.mjs} +2 -2
  4. package/dist/billing/index.js +299 -299
  5. package/dist/billing/index.mjs +1 -1
  6. package/dist/{chunk-4FSFLAVE.js → chunk-KWAUWJYX.js} +233 -132
  7. package/dist/chunk-KWAUWJYX.js.map +1 -0
  8. package/dist/{chunk-BSGK7GT3.mjs → chunk-VQ35TGD7.mjs} +106 -5
  9. package/dist/chunk-VQ35TGD7.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 +2 -0
  13. package/dist/components/index.d.ts +2 -0
  14. package/dist/components/index.js +2 -2
  15. package/dist/components/index.mjs +1 -1
  16. package/dist/contexts/index.js +2 -2
  17. package/dist/contexts/index.mjs +1 -1
  18. package/dist/scripts/generate-web-module/generator.d.ts.map +1 -1
  19. package/dist/scripts/generate-web-module/generator.js +42 -33
  20. package/dist/scripts/generate-web-module/generator.js.map +1 -1
  21. package/dist/scripts/generate-web-module/transformers/relationship-resolver.d.ts.map +1 -1
  22. package/dist/scripts/generate-web-module/transformers/relationship-resolver.js +8 -6
  23. package/dist/scripts/generate-web-module/transformers/relationship-resolver.js.map +1 -1
  24. package/dist/scripts/generate-web-module/types/json-schema.interface.d.ts +2 -0
  25. package/dist/scripts/generate-web-module/types/json-schema.interface.d.ts.map +1 -1
  26. package/dist/scripts/generate-web-module/types/template-data.interface.d.ts +2 -0
  27. package/dist/scripts/generate-web-module/types/template-data.interface.d.ts.map +1 -1
  28. package/dist/scripts/generate-web-module/validators/json-schema-validator.d.ts +1 -1
  29. package/dist/scripts/generate-web-module/validators/json-schema-validator.d.ts.map +1 -1
  30. package/dist/scripts/generate-web-module/validators/json-schema-validator.js +74 -25
  31. package/dist/scripts/generate-web-module/validators/json-schema-validator.js.map +1 -1
  32. package/package.json +1 -1
  33. package/scripts/generate-web-module/generator.ts +48 -37
  34. package/scripts/generate-web-module/transformers/relationship-resolver.ts +8 -6
  35. package/scripts/generate-web-module/types/json-schema.interface.ts +2 -0
  36. package/scripts/generate-web-module/types/template-data.interface.ts +2 -0
  37. package/scripts/generate-web-module/validators/json-schema-validator.ts +79 -27
  38. package/src/shadcnui/custom/multiple-selector.tsx +123 -5
  39. package/dist/chunk-4FSFLAVE.js.map +0 -1
  40. package/dist/chunk-BSGK7GT3.mjs.map +0 -1
  41. /package/dist/{BlockNoteEditor-GOWLYEKX.mjs.map → BlockNoteEditor-WLS36QIF.mjs.map} +0 -0
@@ -3,6 +3,7 @@
3
3
  import { Command as CommandPrimitive, useCommandState } from "cmdk";
4
4
  import { ChevronDownIcon, X } from "lucide-react";
5
5
  import * as React from "react";
6
+ import { createPortal } from "react-dom";
6
7
  import { forwardRef, useEffect } from "react";
7
8
 
8
9
  import { cn } from "../../utils/cn";
@@ -84,6 +85,8 @@ export interface MultipleSelectorProps {
84
85
  renderOption?: (option: Option) => React.ReactNode;
85
86
  /** Maximum number of badges to display before showing "+X more" */
86
87
  maxDisplayCount?: number;
88
+ /** Render dropdown in a portal to escape overflow containers (e.g., dialogs) */
89
+ usePortal?: boolean;
87
90
  }
88
91
 
89
92
  export interface MultipleSelectorRef {
@@ -200,14 +203,17 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
200
203
  hideClearAllButton = false,
201
204
  renderOption,
202
205
  maxDisplayCount,
206
+ usePortal = false,
203
207
  }: MultipleSelectorProps,
204
208
  ref: React.Ref<MultipleSelectorRef>,
205
209
  ) => {
206
210
  const inputRef = React.useRef<HTMLInputElement>(null);
211
+ const containerRef = React.useRef<HTMLDivElement>(null);
207
212
  const [open, setOpen] = React.useState(false);
208
213
  const [onScrollbar, setOnScrollbar] = React.useState(false);
209
214
  const [isLoading, setIsLoading] = React.useState(false);
210
215
  const dropdownRef = React.useRef<HTMLDivElement>(null);
216
+ const [dropdownPosition, setDropdownPosition] = React.useState({ top: 0, left: 0, width: 0 });
211
217
 
212
218
  const [selected, setSelected] = React.useState<Option[]>(value || []);
213
219
  const [options, setOptions] = React.useState<GroupOption>(transToGroupOption(arrayDefaultOptions, groupBy));
@@ -317,6 +323,42 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
317
323
  void exec();
318
324
  }, [debouncedSearchTerm, groupBy, open, triggerSearchOnFocus, onSearchSync]);
319
325
 
326
+ useEffect(() => {
327
+ if (!usePortal || !open || !containerRef.current) return;
328
+
329
+ const updatePosition = () => {
330
+ if (containerRef.current) {
331
+ const rect = containerRef.current.getBoundingClientRect();
332
+ setDropdownPosition({
333
+ top: rect.bottom + window.scrollY + 4,
334
+ left: rect.left + window.scrollX,
335
+ width: rect.width,
336
+ });
337
+ }
338
+ };
339
+
340
+ updatePosition();
341
+
342
+ const handleScroll = () => {
343
+ if (containerRef.current) {
344
+ const rect = containerRef.current.getBoundingClientRect();
345
+ if (rect.bottom < 0 || rect.top > window.innerHeight) {
346
+ setOpen(false);
347
+ } else {
348
+ updatePosition();
349
+ }
350
+ }
351
+ };
352
+
353
+ window.addEventListener("scroll", handleScroll, true);
354
+ window.addEventListener("resize", updatePosition);
355
+
356
+ return () => {
357
+ window.removeEventListener("scroll", handleScroll, true);
358
+ window.removeEventListener("resize", updatePosition);
359
+ };
360
+ }, [usePortal, open]);
361
+
320
362
  useEffect(() => {
321
363
  const doSearch = async () => {
322
364
  setIsLoading(true);
@@ -429,6 +471,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
429
471
  filter={commandFilter()}
430
472
  >
431
473
  <div
474
+ ref={containerRef}
432
475
  className={cn(
433
476
  "flex min-h-10 items-start justify-between rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 md:text-sm",
434
477
  {
@@ -534,10 +577,85 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
534
577
  <ChevronDownIcon className={cn("size-5 text-muted-foreground/50", selected.length >= 1 && "hidden")} />
535
578
  </div>
536
579
  </div>
537
- <div className="relative">
538
- {open && (
580
+ {!usePortal && (
581
+ <div className="relative">
582
+ {open && (
583
+ <CommandList
584
+ className="absolute top-1 z-10 w-full rounded-md border bg-background text-foreground shadow-md outline-none animate-in"
585
+ onMouseLeave={() => {
586
+ setOnScrollbar(false);
587
+ }}
588
+ onMouseEnter={() => {
589
+ setOnScrollbar(true);
590
+ }}
591
+ onMouseUp={() => {
592
+ inputRef?.current?.focus();
593
+ }}
594
+ >
595
+ {isLoading ? (
596
+ <>{loadingIndicator}</>
597
+ ) : (
598
+ <>
599
+ {EmptyItem()}
600
+ {CreatableItem()}
601
+ {!selectFirstItem && <CommandItem value="-" className="hidden" />}
602
+ {Object.entries(selectables).map(([key, dropdowns]) => (
603
+ <CommandGroup
604
+ key={key}
605
+ heading={key}
606
+ className="h-full overflow-auto [&_[cmdk-group-heading]]:border-b [&_[cmdk-group-heading]]:border-border/50 [&_[cmdk-group-heading]]:mb-1"
607
+ >
608
+ <>
609
+ {dropdowns.map((option) => {
610
+ return (
611
+ <CommandItem
612
+ key={option.value}
613
+ value={option.label}
614
+ disabled={option.disable}
615
+ onMouseDown={(e) => {
616
+ e.preventDefault();
617
+ e.stopPropagation();
618
+ }}
619
+ onSelect={() => {
620
+ if (selected.length >= maxSelected) {
621
+ onMaxSelected?.(selected.length);
622
+ return;
623
+ }
624
+ setInputValue("");
625
+ const newOptions = [...selected, option];
626
+ setSelected(newOptions);
627
+ onChange?.(newOptions);
628
+ }}
629
+ className={cn(
630
+ "cursor-pointer bg-transparent hover:bg-accent data-selected:bg-transparent data-selected:hover:bg-accent",
631
+ option.disable && "cursor-default text-muted-foreground",
632
+ )}
633
+ >
634
+ {renderOption ? renderOption(option) : option.label}
635
+ </CommandItem>
636
+ );
637
+ })}
638
+ </>
639
+ </CommandGroup>
640
+ ))}
641
+ </>
642
+ )}
643
+ </CommandList>
644
+ )}
645
+ </div>
646
+ )}
647
+ {usePortal &&
648
+ open &&
649
+ typeof window !== "undefined" &&
650
+ createPortal(
539
651
  <CommandList
540
- className="absolute top-1 z-10 w-full rounded-md border bg-background text-foreground shadow-md outline-none animate-in"
652
+ ref={dropdownRef}
653
+ className="fixed z-50 max-h-80 rounded-md border bg-background text-foreground shadow-md outline-none animate-in"
654
+ style={{
655
+ top: dropdownPosition.top,
656
+ left: dropdownPosition.left,
657
+ width: dropdownPosition.width,
658
+ }}
541
659
  onMouseLeave={() => {
542
660
  setOnScrollbar(false);
543
661
  }}
@@ -596,9 +714,9 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
596
714
  ))}
597
715
  </>
598
716
  )}
599
- </CommandList>
717
+ </CommandList>,
718
+ document.body,
600
719
  )}
601
- </div>
602
720
  </Command>
603
721
  );
604
722
  },