@greatapps/common 1.1.769 → 1.1.771

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 (60) hide show
  1. package/dist/components/account/AccountModals.mjs +6 -3
  2. package/dist/components/account/AccountModals.mjs.map +1 -1
  3. package/dist/components/account/sections/ChangeEmailModal.mjs +1 -2
  4. package/dist/components/account/sections/ChangeEmailModal.mjs.map +1 -1
  5. package/dist/components/account/sections/ChangePhoneModal.mjs +1 -2
  6. package/dist/components/account/sections/ChangePhoneModal.mjs.map +1 -1
  7. package/dist/components/account/sections/MyProfileSection.mjs +5 -2
  8. package/dist/components/account/sections/MyProfileSection.mjs.map +1 -1
  9. package/dist/components/layouts/UsersSelectorPopover.mjs.map +1 -1
  10. package/dist/components/modals/AccountFrozenModal.mjs +1 -1
  11. package/dist/components/modals/AccountFrozenModal.mjs.map +1 -1
  12. package/dist/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.mjs +3 -2
  13. package/dist/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.mjs.map +1 -1
  14. package/dist/components/modals/promo/AiCreditsOfferModal/components/LoseOfferDialog.mjs +2 -2
  15. package/dist/components/modals/promo/AiCreditsOfferModal/components/LoseOfferDialog.mjs.map +1 -1
  16. package/dist/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.mjs +1 -1
  17. package/dist/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.mjs.map +1 -1
  18. package/dist/components/ui/form/PhoneInput.mjs +1 -1
  19. package/dist/components/ui/form/PhoneInput.mjs.map +1 -1
  20. package/dist/components/ui/form/Select.mjs +4 -1
  21. package/dist/components/ui/form/Select.mjs.map +1 -1
  22. package/dist/components/ui/overlay/Dialog.mjs +1 -12
  23. package/dist/components/ui/overlay/Dialog.mjs.map +1 -1
  24. package/dist/components/ui/overlay/Popover.mjs +4 -1
  25. package/dist/components/ui/overlay/Popover.mjs.map +1 -1
  26. package/dist/components/ui/overlay/Sheet.mjs +9 -3
  27. package/dist/components/ui/overlay/Sheet.mjs.map +1 -1
  28. package/dist/components/ui/overlay/Tooltip.mjs +4 -1
  29. package/dist/components/ui/overlay/Tooltip.mjs.map +1 -1
  30. package/dist/components/ui/overlay/layers.mjs +35 -0
  31. package/dist/components/ui/overlay/layers.mjs.map +1 -0
  32. package/dist/components/widgets/ImageUpload/ImageCropModal.mjs +1 -2
  33. package/dist/components/widgets/ImageUpload/ImageCropModal.mjs.map +1 -1
  34. package/dist/components/widgets/ImageUpload/ImageTooSmallModal.mjs +2 -2
  35. package/dist/components/widgets/ImageUpload/ImageTooSmallModal.mjs.map +1 -1
  36. package/dist/components/widgets/ImageUpload/ImageUpload.mjs +4 -2
  37. package/dist/components/widgets/ImageUpload/ImageUpload.mjs.map +1 -1
  38. package/dist/index.mjs +3 -0
  39. package/dist/index.mjs.map +1 -1
  40. package/package.json +1 -1
  41. package/src/components/account/AccountModals.tsx +9 -3
  42. package/src/components/account/sections/ChangeEmailModal.tsx +1 -2
  43. package/src/components/account/sections/ChangePhoneModal.tsx +1 -2
  44. package/src/components/account/sections/MyProfileSection.tsx +6 -2
  45. package/src/components/layouts/UsersSelectorPopover.tsx +5 -0
  46. package/src/components/modals/AccountFrozenModal.tsx +1 -1
  47. package/src/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.tsx +12 -8
  48. package/src/components/modals/promo/AiCreditsOfferModal/components/LoseOfferDialog.tsx +2 -2
  49. package/src/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.tsx +1 -3
  50. package/src/components/ui/form/PhoneInput.tsx +1 -1
  51. package/src/components/ui/form/Select.tsx +5 -1
  52. package/src/components/ui/overlay/Dialog.tsx +4 -16
  53. package/src/components/ui/overlay/Popover.tsx +5 -1
  54. package/src/components/ui/overlay/Sheet.tsx +17 -3
  55. package/src/components/ui/overlay/Tooltip.tsx +6 -1
  56. package/src/components/ui/overlay/layers.tsx +95 -0
  57. package/src/components/widgets/ImageUpload/ImageCropModal.tsx +2 -3
  58. package/src/components/widgets/ImageUpload/ImageTooSmallModal.tsx +2 -2
  59. package/src/components/widgets/ImageUpload/ImageUpload.tsx +15 -11
  60. package/src/index.ts +1 -0
@@ -26,6 +26,7 @@ import { readOfferStageCookie, writeOfferStageCookie } from '../../../../modules
26
26
  import { useAiCreditsOfferModal } from '../../../../store/useAiCreditsOfferModal';
27
27
  import { useModalManager } from '../../../../store/useModalManager';
28
28
  import { LoseOfferDialog } from './components/LoseOfferDialog';
29
+ import { DialogLayer } from '../../../ui/overlay/layers';
29
30
  import { OfferIllustration } from './components/OfferIllustration';
30
31
  import { OfferPaymentField } from './components/OfferPaymentField';
31
32
 
@@ -388,14 +389,17 @@ export default function AiCreditsOfferModal() {
388
389
  </DialogContent>
389
390
  </Dialog>
390
391
 
391
- <LoseOfferDialog
392
- open={confirmingExit}
393
- discountLabel={discountLabel}
394
- creditsLabel={creditsLabel}
395
- fullPriceLabel={fullPrice != null ? formatCurrencyNumber(fullPrice) : ''}
396
- onKeep={() => setConfirmingExit(false)}
397
- onLose={dismiss}
398
- />
392
+ {/* Irmao do `Dialog` acima, entao nasceria na mesma faixa de z. */}
393
+ <DialogLayer>
394
+ <LoseOfferDialog
395
+ open={confirmingExit}
396
+ discountLabel={discountLabel}
397
+ creditsLabel={creditsLabel}
398
+ fullPriceLabel={fullPrice != null ? formatCurrencyNumber(fullPrice) : ''}
399
+ onKeep={() => setConfirmingExit(false)}
400
+ onLose={dismiss}
401
+ />
402
+ </DialogLayer>
399
403
  </>
400
404
  );
401
405
  }
@@ -52,8 +52,8 @@ export function LoseOfferDialog({
52
52
  <DialogContent
53
53
  showCloseButton={false}
54
54
  onOpenAutoFocus={(event) => event.preventDefault()}
55
- overlayClassName="z-[1002] bg-black/40"
56
- className="z-[1003] flex flex-col gap-0 p-0 max-[440px]:w-[calc(100%-32px)] md:w-[420px]! lg:w-[420px]! max-w-[420px]! data-[state=open]:animate-[gp-offer-confirm-in_260ms_cubic-bezier(0.16,1,0.3,1)]"
55
+ overlayClassName="bg-black/40"
56
+ className="flex flex-col gap-0 p-0 max-[440px]:w-[calc(100%-32px)] md:w-[420px]! lg:w-[420px]! max-w-[420px]! data-[state=open]:animate-[gp-offer-confirm-in_260ms_cubic-bezier(0.16,1,0.3,1)]"
57
57
  >
58
58
  <DialogHeader className="gap-3 p-5 text-left">
59
59
  <div className="flex w-fit items-center justify-center rounded-lg bg-red-50 p-2.5">
@@ -56,8 +56,6 @@ export function OfferPaymentField({ cardId, onCardAdded }: OfferPaymentFieldProp
56
56
 
57
57
  <div className="flex shrink-0 items-center gap-2">
58
58
  <CardBrandIcon brand={card.brand} />
59
- {/* `z-[1002]`: o tooltip nasce num portal e precisa passar por cima do
60
- `DialogContent` (z-1001), senão fica escondido atrás do modal. */}
61
59
  <Tooltip>
62
60
  <TooltipTrigger asChild>
63
61
  <button
@@ -69,7 +67,7 @@ export function OfferPaymentField({ cardId, onCardAdded }: OfferPaymentFieldProp
69
67
  <IconPencil size={18} />
70
68
  </button>
71
69
  </TooltipTrigger>
72
- <TooltipContent side="top" sideOffset={6} className="z-[1002]">
70
+ <TooltipContent side="top" sideOffset={6}>
73
71
  {translate('common.promo.aiCreditsOffer.editPayment')}
74
72
  </TooltipContent>
75
73
  </Tooltip>
@@ -144,7 +144,7 @@ export default function PhoneInput({
144
144
  <IconChevronDown size={14} className={cn("text-zinc-500 transition-transform duration-200", open && "rotate-180")} />
145
145
  </button>
146
146
  </PopoverTrigger>
147
- <PopoverContent className="w-72 p-0 z-[1010]" align="start" side="top">
147
+ <PopoverContent className="w-72 p-0" align="start" side="top">
148
148
  <div className="flex items-center gap-2 p-2 border-b border-zinc-100">
149
149
  <IconSearch size={14} className="text-zinc-400 shrink-0" />
150
150
  <input
@@ -4,6 +4,7 @@ import * as React from "react";
4
4
  import * as SelectPrimitive from "@radix-ui/react-select";
5
5
  import { IconCheck, IconChevronDown, IconChevronUp } from '@tabler/icons-react';
6
6
  import { cn } from "../../../infra/utils/clsx";
7
+ import { useDialogLayer } from "../overlay/layers";
7
8
 
8
9
  function Select({
9
10
  ...props
@@ -68,12 +69,15 @@ function SelectContent({
68
69
  align = "center",
69
70
  ...props
70
71
  }: React.ComponentProps<typeof SelectPrimitive.Content>) {
72
+ // Portaliza para o body, entao disputa z com o conteudo do modal que o abriu.
73
+ const layer = useDialogLayer();
71
74
  return (
72
75
  <SelectPrimitive.Portal>
73
76
  <SelectPrimitive.Content
74
77
  data-slot="select-content"
75
78
  className={cn(
76
- "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-[1002] max-h-(--radix-select-content-available-height) min-w-32 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
79
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative max-h-(--radix-select-content-available-height) min-w-32 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
80
+ layer.popover,
77
81
  position === "popper" &&
78
82
  "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
79
83
  className,
@@ -4,23 +4,11 @@ import * as React from "react";
4
4
  import * as DialogPrimitive from "@radix-ui/react-dialog";
5
5
  import { IconX } from '@tabler/icons-react';
6
6
  import { cn } from "../../../infra/utils/clsx";
7
+ import { DialogDepthContext, useDialogLayer } from "./layers";
7
8
 
8
- // Cada `Dialog` aninhado sobe uma camada de z-index, para que o overlay do modal
9
- // de cima cubra o conteudo do modal de baixo. Classes literais para o Tailwind gerar.
10
- const DialogDepthContext = React.createContext(0);
11
-
12
- const DIALOG_LAYERS = [
13
- { overlay: "z-[1000]", content: "z-[1001]" },
14
- { overlay: "z-[1010]", content: "z-[1011]" },
15
- { overlay: "z-[1020]", content: "z-[1021]" },
16
- { overlay: "z-[1030]", content: "z-[1031]" },
17
- ] as const;
18
-
19
- function useDialogLayer() {
20
- const depth = React.useContext(DialogDepthContext);
21
- const index = Math.min(Math.max(depth - 1, 0), DIALOG_LAYERS.length - 1);
22
- return DIALOG_LAYERS[index];
23
- }
9
+ // Cada `Dialog` aninhado sobe uma faixa de z-index, para que o overlay do modal
10
+ // de cima cubra o conteudo do modal de baixo. A escala (e os dropdowns e
11
+ // tooltips que precisam acompanhar) vive em `./layers`.
24
12
 
25
13
  function Dialog({
26
14
  ...props
@@ -5,6 +5,7 @@
5
5
  import * as React from "react"
6
6
  import * as PopoverPrimitive from "@radix-ui/react-popover"
7
7
  import { cn } from "../../../infra/utils/clsx"
8
+ import { useDialogLayer } from "./layers"
8
9
 
9
10
  function Popover({
10
11
  ...props
@@ -24,6 +25,8 @@ function PopoverContent({
24
25
  sideOffset = 4,
25
26
  ...props
26
27
  }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
28
+ // Portaliza para o body, entao disputa z com o conteudo do modal que o abriu.
29
+ const layer = useDialogLayer()
27
30
  return (
28
31
  <PopoverPrimitive.Portal>
29
32
  <PopoverPrimitive.Content
@@ -31,7 +34,8 @@ function PopoverContent({
31
34
  align={align}
32
35
  sideOffset={sideOffset}
33
36
  className={cn(
34
- "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-[1002] w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
37
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
38
+ layer.popover,
35
39
  className
36
40
  )}
37
41
  {...props}
@@ -5,6 +5,7 @@ import * as SheetPrimitive from "@radix-ui/react-dialog";
5
5
  import { IconX } from '@tabler/icons-react';
6
6
  import { cn } from "../../../infra/utils/clsx";
7
7
  import { hideCrisp, showCrisp } from "../../embeds/CrispEmbed";
8
+ import { DialogDepthContext, useDialogLayer } from "./layers";
8
9
 
9
10
  function Sheet({ onOpenChange, modal = false, ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
10
11
  const handleOpenChange = React.useCallback((open: boolean) => {
@@ -13,7 +14,16 @@ function Sheet({ onOpenChange, modal = false, ...props }: React.ComponentProps<t
13
14
  onOpenChange?.(open);
14
15
  }, [onOpenChange]);
15
16
 
16
- return <SheetPrimitive.Root data-slot="sheet" modal={modal} onOpenChange={handleOpenChange} {...props} />;
17
+ // Mesma faixa de z do `Dialog`: um `Dialog` aberto de dentro de um `Sheet` era
18
+ // profundidade 1, entao o overlay dele (1000) nascia sob o conteudo do sheet
19
+ // (1001). Somando aqui tambem, ele passa para a faixa de cima.
20
+ const depth = React.useContext(DialogDepthContext);
21
+
22
+ return (
23
+ <DialogDepthContext.Provider value={depth + 1}>
24
+ <SheetPrimitive.Root data-slot="sheet" modal={modal} onOpenChange={handleOpenChange} {...props} />
25
+ </DialogDepthContext.Provider>
26
+ );
17
27
  }
18
28
 
19
29
  function SheetTrigger({
@@ -38,12 +48,14 @@ function SheetOverlay({
38
48
  className,
39
49
  ...props
40
50
  }: React.ComponentProps<"div">) {
51
+ const layer = useDialogLayer();
41
52
  return (
42
53
  <SheetPrimitive.Close tabIndex={-1} asChild>
43
54
  <div
44
55
  data-slot="sheet-overlay"
45
56
  className={cn(
46
- "animate-in fade-in-0 fixed inset-0 z-[1000] bg-black/50 cursor-default",
57
+ "animate-in fade-in-0 fixed inset-0 bg-black/50 cursor-default",
58
+ layer.overlay,
47
59
  className,
48
60
  )}
49
61
  {...props}
@@ -64,6 +76,7 @@ function SheetContent({
64
76
  showCloseButton?: boolean;
65
77
  noAnimation?: boolean;
66
78
  }) {
79
+ const layer = useDialogLayer();
67
80
  return (
68
81
  <SheetPortal>
69
82
  <SheetOverlay className={noAnimation ? "!animate-none" : undefined} />
@@ -72,7 +85,8 @@ function SheetContent({
72
85
  onInteractOutside={(e) => e.preventDefault()}
73
86
  onPointerDownOutside={(e) => e.preventDefault()}
74
87
  className={cn(
75
- "bg-background fixed z-[1001] flex flex-col gap-4 shadow-lg",
88
+ "bg-background fixed flex flex-col gap-4 shadow-lg",
89
+ layer.content,
76
90
  !noAnimation &&
77
91
  "data-[state=open]:animate-in data-[state=closed]:animate-out transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
78
92
  !noAnimation &&
@@ -5,6 +5,7 @@
5
5
  import * as React from "react"
6
6
  import * as TooltipPrimitive from "@radix-ui/react-tooltip"
7
7
  import { cn } from "../../../infra/utils/clsx"
8
+ import { useDialogLayer } from "./layers"
8
9
 
9
10
  function TooltipProvider({
10
11
  delayDuration = 0,
@@ -41,13 +42,17 @@ function TooltipContent({
41
42
  children,
42
43
  ...props
43
44
  }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
45
+ // Portaliza para o body. Fora de modal segue no `z-50` do resto da pagina;
46
+ // dentro de um, sobe para a faixa dele em vez de ser remendado no call site.
47
+ const layer = useDialogLayer()
44
48
  return (
45
49
  <TooltipPrimitive.Portal>
46
50
  <TooltipPrimitive.Content
47
51
  data-slot="tooltip-content"
48
52
  sideOffset={sideOffset}
49
53
  className={cn(
50
- "bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance pointer-events-none",
54
+ "bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance pointer-events-none",
55
+ layer.tooltip,
51
56
  className
52
57
  )}
53
58
  {...props}
@@ -0,0 +1,95 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ /**
6
+ * Escala unica de z-index para tudo que flutua sobre a pagina.
7
+ *
8
+ * Cada nivel de modal aninhado ganha uma faixa propria de 10 unidades, e dentro
9
+ * da faixa a ordem e sempre a mesma:
10
+ *
11
+ * base + 0 overlay do modal
12
+ * base + 1 conteudo do modal
13
+ * base + 2 dropdown portalizado aberto de dentro dele (Select, Popover)
14
+ * base + 3 tooltip portalizado aberto de dentro dele
15
+ *
16
+ * O ponto dos dois ultimos: `Select`, `Popover` e `Tooltip` renderizam num
17
+ * portal para o `document.body`, entao no DOM eles sao irmaos do conteudo do
18
+ * modal e a disputa de z e global. Com z fixo (`z-[1002]`) eles funcionavam so
19
+ * no primeiro nivel: dentro de um modal aninhado o conteudo sobe para 1011 e o
20
+ * dropdown fica preso em 1002, abrindo atras do modal. Derivar da mesma faixa
21
+ * resolve em qualquer profundidade.
22
+ *
23
+ * O portal preserva o contexto React (so o alvo no DOM muda), por isso o
24
+ * `useDialogLayer` de dentro do dropdown enxerga a profundidade do modal que o
25
+ * abriu.
26
+ *
27
+ * Classes literais de proposito: o Tailwind so gera o que ve escrito.
28
+ */
29
+ const DIALOG_LAYERS = [
30
+ { overlay: "z-[1000]", content: "z-[1001]", popover: "z-[1002]", tooltip: "z-[1003]" },
31
+ { overlay: "z-[1010]", content: "z-[1011]", popover: "z-[1012]", tooltip: "z-[1013]" },
32
+ { overlay: "z-[1020]", content: "z-[1021]", popover: "z-[1022]", tooltip: "z-[1023]" },
33
+ { overlay: "z-[1030]", content: "z-[1031]", popover: "z-[1032]", tooltip: "z-[1033]" },
34
+ { overlay: "z-[1040]", content: "z-[1041]", popover: "z-[1042]", tooltip: "z-[1043]" },
35
+ { overlay: "z-[1050]", content: "z-[1051]", popover: "z-[1052]", tooltip: "z-[1053]" },
36
+ ] as const;
37
+
38
+ /**
39
+ * Fora de qualquer modal. `overlay` e `content` nunca sao lidos aqui (so existem
40
+ * dentro de um `Dialog`/`Sheet`, que ja somam 1), mas dropdown e tooltip sim, e
41
+ * mantem exatamente os valores que tinham antes desta escala existir: o tooltip
42
+ * segue no `z-50` do resto da pagina em vez de saltar para a faixa dos modais.
43
+ */
44
+ const ROOT_LAYER = { overlay: "z-[1000]", content: "z-[1001]", popover: "z-[1002]", tooltip: "z-50" } as const;
45
+
46
+ export type DialogLayerClasses = (typeof DIALOG_LAYERS)[number] | typeof ROOT_LAYER;
47
+
48
+ export const DialogDepthContext = React.createContext(0);
49
+
50
+ const saturationWarned = new Set<number>();
51
+
52
+ export function useDialogLayer(): DialogLayerClasses {
53
+ const depth = React.useContext(DialogDepthContext);
54
+ if (depth <= 0) return ROOT_LAYER;
55
+
56
+ if (depth > DIALOG_LAYERS.length && process.env.NODE_ENV !== "production" && !saturationWarned.has(depth)) {
57
+ saturationWarned.add(depth);
58
+ console.warn(
59
+ `[great-lib-common] ${depth} modais empilhados, mas a escala de z so tem ${DIALOG_LAYERS.length} faixas. ` +
60
+ `Deste nivel em diante todos dividem a ultima faixa e o overlay de cima cai sob o conteudo de baixo. ` +
61
+ `Se o empilhamento for legitimo, adicione uma faixa em ui/overlay/layers.tsx.`,
62
+ );
63
+ }
64
+
65
+ return DIALOG_LAYERS[Math.min(depth - 1, DIALOG_LAYERS.length - 1)];
66
+ }
67
+
68
+ /**
69
+ * Sobe uma faixa para a subarvore, sem precisar de um modal no meio.
70
+ *
71
+ * A profundidade sai da arvore React, nao da ordem de abertura, entao dois
72
+ * modais declarados como irmaos nascem os dois na mesma faixa e o de cima abre
73
+ * atras do de baixo:
74
+ *
75
+ * <>
76
+ * <Dialog>...</Dialog>
77
+ * <ConfirmDialog /> // mesma faixa do de cima
78
+ * </>
79
+ *
80
+ * Envolvendo o segundo, ele passa a valer um nivel a mais que o vizinho, e o
81
+ * ajuste e relativo: continua correto se o conjunto inteiro for parar dentro de
82
+ * outro modal.
83
+ *
84
+ * <>
85
+ * <Dialog>...</Dialog>
86
+ * <DialogLayer><ConfirmDialog /></DialogLayer>
87
+ * </>
88
+ *
89
+ * E a alternativa ao `className="z-[1003]"` na mao, que trava o modal num numero
90
+ * absoluto e quebra assim que ele e reaproveitado noutra profundidade.
91
+ */
92
+ export function DialogLayer({ children }: { children: React.ReactNode }) {
93
+ const depth = React.useContext(DialogDepthContext);
94
+ return <DialogDepthContext.Provider value={depth + 1}>{children}</DialogDepthContext.Provider>;
95
+ }
@@ -304,10 +304,9 @@ export function ImageCropModal({
304
304
  <DialogContent
305
305
  className={
306
306
  isTooSmall
307
- ? 'w-full md:w-[480px]! lg:w-[480px]! max-w-[480px]! max-[480px]:w-[calc(100%-32px)] p-0 gap-0 z-[1003]!'
308
- : 'w-full md:w-[650px]! lg:w-[650px]! max-w-[650px]! max-[650px]:w-[calc(100%-32px)] p-0 gap-0 z-[1003]!'
307
+ ? 'w-full md:w-[480px]! lg:w-[480px]! max-w-[480px]! max-[480px]:w-[calc(100%-32px)] p-0 gap-0'
308
+ : 'w-full md:w-[650px]! lg:w-[650px]! max-w-[650px]! max-[650px]:w-[calc(100%-32px)] p-0 gap-0'
309
309
  }
310
- overlayClassName="z-[1002]!"
311
310
  >
312
311
  {isTooSmall ? (
313
312
  <>
@@ -35,8 +35,8 @@ export function ImageTooSmallModal({
35
35
  return (
36
36
  <Dialog open={open} onOpenChange={onOpenChange}>
37
37
  <DialogContent
38
- className="w-full md:w-[480px]! lg:w-[480px]! max-w-[480px]! max-[480px]:w-[calc(100%-32px)] p-0 gap-0 z-[1003]!"
39
- overlayClassName="z-[1002]!"
38
+ className="w-full md:w-[480px]! lg:w-[480px]! max-w-[480px]! max-[480px]:w-[calc(100%-32px)] p-0 gap-0 z-[1003]"
39
+ overlayClassName="z-[1002]"
40
40
  >
41
41
  <DialogHeader className="p-5">
42
42
  <DialogTitle className="text-center text-[16px]!">
@@ -4,6 +4,7 @@ import { useRef } from 'react'
4
4
  import { useImageUpload } from '../../../modules/images/hooks/use-image-upload.hook'
5
5
  import { ACCEPTED_IMAGE_FORMATS_STRING } from '../../../modules/images/constants/image.constants'
6
6
  import { ImageCropModal } from './ImageCropModal'
7
+ import { DialogLayer } from '../../ui/overlay/layers'
7
8
  import { Button } from '../../ui/buttons/Button'
8
9
  import type { ImageConfig } from '../../../modules/images/types/image.type'
9
10
 
@@ -114,17 +115,20 @@ export function ImageUpload({
114
115
  )}
115
116
 
116
117
  {originalImage && (
117
- <ImageCropModal
118
- open={showCropModal}
119
- onOpenChange={setShowCropModal}
120
- imageSrc={originalImage}
121
- aspectRatio={config.aspectRatio}
122
- onConfirm={handleCropConfirm}
123
- minWidth={config.width}
124
- minHeight={config.height}
125
- fileName={fileName}
126
- onSelectAnother={() => inputRef.current?.click()}
127
- />
118
+ // Abre por cima do que hospeda o widget, que normalmente e um modal.
119
+ <DialogLayer>
120
+ <ImageCropModal
121
+ open={showCropModal}
122
+ onOpenChange={setShowCropModal}
123
+ imageSrc={originalImage}
124
+ aspectRatio={config.aspectRatio}
125
+ onConfirm={handleCropConfirm}
126
+ minWidth={config.width}
127
+ minHeight={config.height}
128
+ fileName={fileName}
129
+ onSelectAnother={() => inputRef.current?.click()}
130
+ />
131
+ </DialogLayer>
128
132
  )}
129
133
  </div>
130
134
  )
package/src/index.ts CHANGED
@@ -465,6 +465,7 @@ export {
465
465
  DialogTitle,
466
466
  DialogTrigger,
467
467
  } from "./components/ui/overlay/Dialog";
468
+ export { DialogLayer, useDialogLayer } from "./components/ui/overlay/layers";
468
469
  export {
469
470
  Popover,
470
471
  PopoverTrigger,