@greatapps/common 1.1.788 → 1.1.789

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 (43) hide show
  1. package/dist/components/account/AccountModals.mjs +3 -6
  2. package/dist/components/account/AccountModals.mjs.map +1 -1
  3. package/dist/components/account/DisableTwoFactorAuthModal.mjs +2 -3
  4. package/dist/components/account/DisableTwoFactorAuthModal.mjs.map +1 -1
  5. package/dist/components/account/TwoFactorAuthModal.mjs +4 -5
  6. package/dist/components/account/TwoFactorAuthModal.mjs.map +1 -1
  7. package/dist/components/account/sections/MyProfileSection.mjs +2 -5
  8. package/dist/components/account/sections/MyProfileSection.mjs.map +1 -1
  9. package/dist/components/layouts/UsersSelectorPopover.mjs +1 -1
  10. package/dist/components/layouts/UsersSelectorPopover.mjs.map +1 -1
  11. package/dist/components/modals/ModalManager.mjs +2 -5
  12. package/dist/components/modals/ModalManager.mjs.map +1 -1
  13. package/dist/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.mjs +2 -3
  14. package/dist/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.mjs.map +1 -1
  15. package/dist/components/ui/overlay/Dialog.mjs +6 -7
  16. package/dist/components/ui/overlay/Dialog.mjs.map +1 -1
  17. package/dist/components/ui/overlay/Popover.mjs +3 -3
  18. package/dist/components/ui/overlay/Popover.mjs.map +1 -1
  19. package/dist/components/ui/overlay/Sheet.mjs +6 -6
  20. package/dist/components/ui/overlay/Sheet.mjs.map +1 -1
  21. package/dist/components/ui/overlay/layers.mjs +44 -18
  22. package/dist/components/ui/overlay/layers.mjs.map +1 -1
  23. package/dist/components/widgets/ImageUpload/ImageTooSmallModal.mjs +2 -3
  24. package/dist/components/widgets/ImageUpload/ImageTooSmallModal.mjs.map +1 -1
  25. package/dist/components/widgets/ImageUpload/ImageUpload.mjs +2 -4
  26. package/dist/components/widgets/ImageUpload/ImageUpload.mjs.map +1 -1
  27. package/dist/index.mjs +3 -1
  28. package/dist/index.mjs.map +1 -1
  29. package/package.json +8 -7
  30. package/src/components/account/AccountModals.tsx +3 -9
  31. package/src/components/account/DisableTwoFactorAuthModal.tsx +55 -58
  32. package/src/components/account/TwoFactorAuthModal.tsx +147 -152
  33. package/src/components/account/sections/MyProfileSection.tsx +2 -6
  34. package/src/components/layouts/UsersSelectorPopover.tsx +1 -6
  35. package/src/components/modals/ModalManager.tsx +3 -9
  36. package/src/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.tsx +8 -12
  37. package/src/components/ui/overlay/Dialog.tsx +33 -39
  38. package/src/components/ui/overlay/Popover.tsx +16 -15
  39. package/src/components/ui/overlay/Sheet.tsx +50 -56
  40. package/src/components/ui/overlay/layers.tsx +45 -73
  41. package/src/components/widgets/ImageUpload/ImageTooSmallModal.tsx +20 -23
  42. package/src/components/widgets/ImageUpload/ImageUpload.tsx +11 -15
  43. package/src/index.ts +1 -1
@@ -5,7 +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
+ import { OverlayLayerContext, useDialogLayer, useOverlayLayer } from "./layers";
9
9
 
10
10
  function Sheet({ onOpenChange, modal = false, ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
11
11
  const handleOpenChange = React.useCallback((open: boolean) => {
@@ -14,16 +14,7 @@ function Sheet({ onOpenChange, modal = false, ...props }: React.ComponentProps<t
14
14
  onOpenChange?.(open);
15
15
  }, [onOpenChange]);
16
16
 
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
+ return <SheetPrimitive.Root data-slot="sheet" modal={modal} onOpenChange={handleOpenChange} {...props} />;
27
18
  }
28
19
 
29
20
  function SheetTrigger({
@@ -71,58 +62,61 @@ function SheetContent({
71
62
  showCloseButton = true,
72
63
  noAnimation = false,
73
64
  ...props
74
- }: React.ComponentProps<typeof SheetPrimitive.Content> & {
65
+ }: React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content> & {
75
66
  side?: "top" | "right" | "bottom" | "left";
76
67
  showCloseButton?: boolean;
77
68
  noAnimation?: boolean;
78
69
  }) {
79
- const layer = useDialogLayer();
70
+ const { layer, presenceRef } = useOverlayLayer();
80
71
  return (
81
- <SheetPortal>
82
- <SheetOverlay className={noAnimation ? "!animate-none" : undefined} />
83
- <SheetPrimitive.Content
84
- data-slot="sheet-content"
85
- onInteractOutside={(e) => e.preventDefault()}
86
- onPointerDownOutside={(e) => e.preventDefault()}
87
- className={cn(
88
- "bg-background fixed flex flex-col gap-4 shadow-lg",
89
- layer.content,
90
- !noAnimation &&
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",
92
- !noAnimation &&
72
+ <OverlayLayerContext.Provider value={layer}>
73
+ <SheetPortal>
74
+ <SheetOverlay className={noAnimation ? "!animate-none" : undefined} />
75
+ <SheetPrimitive.Content
76
+ ref={presenceRef}
77
+ data-slot="sheet-content"
78
+ onInteractOutside={(e) => e.preventDefault()}
79
+ onPointerDownOutside={(e) => e.preventDefault()}
80
+ className={cn(
81
+ "bg-background fixed flex flex-col gap-4 shadow-lg",
82
+ layer.content,
83
+ !noAnimation &&
84
+ "data-[state=open]:animate-in data-[state=closed]:animate-out transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
85
+ !noAnimation &&
86
+ side === "right" &&
87
+ "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right",
88
+ !noAnimation &&
89
+ side === "left" &&
90
+ "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left",
91
+ !noAnimation &&
92
+ side === "top" &&
93
+ "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
94
+ !noAnimation &&
95
+ side === "bottom" &&
96
+ "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
93
97
  side === "right" &&
94
- "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right",
95
- !noAnimation &&
98
+ "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
96
99
  side === "left" &&
97
- "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left",
98
- !noAnimation &&
99
- side === "top" &&
100
- "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
101
- !noAnimation &&
102
- side === "bottom" &&
103
- "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
104
- side === "right" &&
105
- "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
106
- side === "left" &&
107
- "inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
108
- side === "top" && "inset-x-0 top-0 h-auto border-b",
109
- side === "bottom" && "inset-x-0 bottom-0 h-auto border-t",
110
- className,
111
- )}
112
- {...props}
113
- >
114
- {children}
115
- {showCloseButton && (
116
- <SheetPrimitive.Close
117
- data-slot="sheet-close"
118
- className="absolute top-3 right-3 hover:cursor-pointer flex size-8 items-center justify-center rounded-lg p-2 text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-950 focus:outline-none"
119
- >
120
- <IconX className="size-[18px]" />
121
- <span className="sr-only">Close</span>
122
- </SheetPrimitive.Close>
123
- )}
124
- </SheetPrimitive.Content>
125
- </SheetPortal>
100
+ "inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
101
+ side === "top" && "inset-x-0 top-0 h-auto border-b",
102
+ side === "bottom" && "inset-x-0 bottom-0 h-auto border-t",
103
+ className,
104
+ )}
105
+ {...props}
106
+ >
107
+ {children}
108
+ {showCloseButton && (
109
+ <SheetPrimitive.Close
110
+ data-slot="sheet-close"
111
+ className="absolute top-3 right-3 hover:cursor-pointer flex size-8 items-center justify-center rounded-lg p-2 text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-950 focus:outline-none"
112
+ >
113
+ <IconX className="size-[18px]" />
114
+ <span className="sr-only">Close</span>
115
+ </SheetPrimitive.Close>
116
+ )}
117
+ </SheetPrimitive.Content>
118
+ </SheetPortal>
119
+ </OverlayLayerContext.Provider>
126
120
  );
127
121
  }
128
122
 
@@ -2,94 +2,66 @@
2
2
 
3
3
  import * as React from "react";
4
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 = [
5
+ export const OVERLAY_LAYERS = [
30
6
  { overlay: "z-[1000]", content: "z-[1001]", popover: "z-[1002]", tooltip: "z-[1003]" },
31
7
  { overlay: "z-[1010]", content: "z-[1011]", popover: "z-[1012]", tooltip: "z-[1013]" },
32
8
  { overlay: "z-[1020]", content: "z-[1021]", popover: "z-[1022]", tooltip: "z-[1023]" },
33
9
  { overlay: "z-[1030]", content: "z-[1031]", popover: "z-[1032]", tooltip: "z-[1033]" },
34
10
  { overlay: "z-[1040]", content: "z-[1041]", popover: "z-[1042]", tooltip: "z-[1043]" },
35
11
  { overlay: "z-[1050]", content: "z-[1051]", popover: "z-[1052]", tooltip: "z-[1053]" },
12
+ { overlay: "z-[1060]", content: "z-[1061]", popover: "z-[1062]", tooltip: "z-[1063]" },
13
+ { overlay: "z-[1070]", content: "z-[1071]", popover: "z-[1072]", tooltip: "z-[1073]" },
36
14
  ] as const;
37
15
 
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;
16
+ export const ROOT_LAYER = { overlay: "z-[1000]", content: "z-[1001]", popover: "z-[1002]", tooltip: "z-50" } as const;
45
17
 
46
- export type DialogLayerClasses = (typeof DIALOG_LAYERS)[number] | typeof ROOT_LAYER;
18
+ export type DialogLayerClasses = (typeof OVERLAY_LAYERS)[number] | typeof ROOT_LAYER;
47
19
 
48
- export const DialogDepthContext = React.createContext(0);
20
+ const openOverlays = new Set<object>();
21
+ const overlayListeners = new Set<() => void>();
22
+ let saturationWarned = false;
49
23
 
50
- const saturationWarned = new Set<number>();
24
+ function subscribeOverlays(onStoreChange: () => void) {
25
+ overlayListeners.add(onStoreChange);
26
+ return () => {
27
+ overlayListeners.delete(onStoreChange);
28
+ };
29
+ }
51
30
 
52
- export function useDialogLayer(): DialogLayerClasses {
53
- const depth = React.useContext(DialogDepthContext);
54
- if (depth <= 0) return ROOT_LAYER;
31
+ export const OverlayLayerContext = React.createContext<DialogLayerClasses>(ROOT_LAYER);
32
+
33
+ export function useOverlayLayer() {
34
+ const token = React.useRef({});
35
+ const rank = React.useSyncExternalStore(
36
+ subscribeOverlays,
37
+ () => Math.max(Array.from(openOverlays).indexOf(token.current), 0),
38
+ () => 0,
39
+ );
55
40
 
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
- }
41
+ const presenceRef = React.useCallback(() => {
42
+ openOverlays.add(token.current);
43
+ overlayListeners.forEach((notify) => notify());
44
+ if (process.env.NODE_ENV !== "production" && openOverlays.size > OVERLAY_LAYERS.length && !saturationWarned) {
45
+ saturationWarned = true;
46
+ console.warn(
47
+ `[great-lib-common] ${openOverlays.size} overlays visiveis, mas a escala de z so tem ${OVERLAY_LAYERS.length} faixas. ` +
48
+ `Deste nivel em diante todos dividem a ultima faixa e o overlay de cima cai sob o conteudo de baixo. ` +
49
+ `Se o empilhamento for legitimo, adicione uma faixa em ui/overlay/layers.tsx.`,
50
+ );
51
+ }
52
+ return () => {
53
+ openOverlays.delete(token.current);
54
+ overlayListeners.forEach((notify) => notify());
55
+ };
56
+ }, []);
64
57
 
65
- return DIALOG_LAYERS[Math.min(depth - 1, DIALOG_LAYERS.length - 1)];
58
+ return { layer: OVERLAY_LAYERS[Math.min(rank, OVERLAY_LAYERS.length - 1)], presenceRef };
59
+ }
60
+
61
+ export function useDialogLayer(): DialogLayerClasses {
62
+ return React.useContext(OverlayLayerContext);
66
63
  }
67
64
 
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
65
  export function DialogLayer({ children }: { children: React.ReactNode }) {
93
- const depth = React.useContext(DialogDepthContext);
94
- return <DialogDepthContext.Provider value={depth + 1}>{children}</DialogDepthContext.Provider>;
66
+ return children;
95
67
  }
@@ -8,7 +8,6 @@ import {
8
8
  DialogHeader,
9
9
  DialogTitle,
10
10
  } from '../../ui/overlay/Dialog'
11
- import { DialogLayer } from '../../ui/overlay/layers'
12
11
  import { Button } from '../../ui/buttons/Button'
13
12
 
14
13
  interface ImageTooSmallModalProps {
@@ -34,29 +33,27 @@ export function ImageTooSmallModal({
34
33
  const dimensionText = `${minWidth}x${minHeight}px`
35
34
 
36
35
  return (
37
- <DialogLayer>
38
- <Dialog open={open} onOpenChange={onOpenChange}>
39
- <DialogContent className="w-full md:w-[480px]! lg:w-[480px]! max-w-[480px]! max-[480px]:w-[calc(100%-32px)] p-0 gap-0">
40
- <DialogHeader className="p-5">
41
- <DialogTitle className="text-center text-[16px]!">
42
- {translate('common.imageUpload.tooSmallTitleWithDimension', { dimensionText })}
43
- </DialogTitle>
44
- </DialogHeader>
36
+ <Dialog open={open} onOpenChange={onOpenChange}>
37
+ <DialogContent className="w-full md:w-[480px]! lg:w-[480px]! max-w-[480px]! max-[480px]:w-[calc(100%-32px)] p-0 gap-0">
38
+ <DialogHeader className="p-5">
39
+ <DialogTitle className="text-center text-[16px]!">
40
+ {translate('common.imageUpload.tooSmallTitleWithDimension', { dimensionText })}
41
+ </DialogTitle>
42
+ </DialogHeader>
45
43
 
46
- <div className="px-5 pb-5">
47
- <p className="paragraph-small-regular text-zinc-600 text-center">
48
- {translate('common.imageUpload.tooSmallBodyFileWithDimension', { fileName, dimensionText })}
49
- </p>
50
- </div>
44
+ <div className="px-5 pb-5">
45
+ <p className="paragraph-small-regular text-zinc-600 text-center">
46
+ {translate('common.imageUpload.tooSmallBodyFileWithDimension', { fileName, dimensionText })}
47
+ </p>
48
+ </div>
51
49
 
52
- <DialogFooter className="flex-row! items-center justify-start! gap-3 p-5 border-t border-zinc-200">
53
- <Button onClick={onSelectAnother}>{translate('common.imageUpload.sendAnother')}</Button>
54
- <Button variant="secondary" onClick={onCancel}>
55
- {translate('common.actions.cancel')}
56
- </Button>
57
- </DialogFooter>
58
- </DialogContent>
59
- </Dialog>
60
- </DialogLayer>
50
+ <DialogFooter className="flex-row! items-center justify-start! gap-3 p-5 border-t border-zinc-200">
51
+ <Button onClick={onSelectAnother}>{translate('common.imageUpload.sendAnother')}</Button>
52
+ <Button variant="secondary" onClick={onCancel}>
53
+ {translate('common.actions.cancel')}
54
+ </Button>
55
+ </DialogFooter>
56
+ </DialogContent>
57
+ </Dialog>
61
58
  )
62
59
  }
@@ -4,7 +4,6 @@ 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'
8
7
  import { Button } from '../../ui/buttons/Button'
9
8
  import type { ImageConfig } from '../../../modules/images/types/image.type'
10
9
 
@@ -115,20 +114,17 @@ export function ImageUpload({
115
114
  )}
116
115
 
117
116
  {originalImage && (
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>
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
+ />
132
128
  )}
133
129
  </div>
134
130
  )
package/src/index.ts CHANGED
@@ -472,7 +472,7 @@ export {
472
472
  DialogTitle,
473
473
  DialogTrigger,
474
474
  } from "./components/ui/overlay/Dialog";
475
- export { DialogLayer, useDialogLayer } from "./components/ui/overlay/layers";
475
+ export { DialogLayer, OverlayLayerContext, useDialogLayer, useOverlayLayer } from "./components/ui/overlay/layers";
476
476
  export {
477
477
  Popover,
478
478
  PopoverTrigger,