@granto-umbrella/umbrella-components 2.1.0 → 2.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@granto-umbrella/umbrella-components",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "description": "Umbrella Components for React",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -31,7 +31,13 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@phosphor-icons/react": "^2.1.7",
34
+ "@radix-ui/react-dialog": "^1.1.7",
35
+ "@radix-ui/react-label": "^2.1.3",
36
+ "@radix-ui/react-popover": "^1.1.7",
37
+ "@radix-ui/react-radio-group": "^1.2.4",
38
+ "lucide-react": "^0.488.0",
34
39
  "react": "^18.3.1",
40
+ "react-day-picker": "^9.6.7",
35
41
  "react-dom": "^18.3.1",
36
42
  "react-hook-form": "^7.54.2",
37
43
  "react-select": "^5.10.0"
@@ -0,0 +1,15 @@
1
+ // Label.styled.tsx
2
+ import styled from "styled-components";
3
+ import * as LabelPrimitive from "@radix-ui/react-label";
4
+
5
+ export const StyledLabel = styled(LabelPrimitive.Root)`
6
+ font-size: 0.875rem; /* Equivalente a text-sm */
7
+ font-weight: 500; /* Equivalente a font-medium */
8
+ line-height: 1; /* Equivalente a leading-none */
9
+
10
+ /* Simula o comportamento de desabilitado */
11
+ &[data-disabled="true"] {
12
+ cursor: not-allowed;
13
+ opacity: 0.7;
14
+ }
15
+ `;
@@ -0,0 +1,16 @@
1
+ // Label.tsx
2
+ import * as React from "react";
3
+ import { StyledLabel } from "./Label.styles";
4
+
5
+ export type LabelProps = React.ComponentProps<typeof StyledLabel>;
6
+
7
+ export const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
8
+ ({ children, ...props }, ref) => {
9
+ return (
10
+ <StyledLabel ref={ref} {...props}>
11
+ {children}
12
+ </StyledLabel>
13
+ );
14
+ }
15
+ );
16
+ Label.displayName = "Label";
@@ -1,4 +1,4 @@
1
- import { semanticSizes } from "../../styles/tokens";
1
+ import { semanticSizes } from "../../../styles/tokens";
2
2
  import styled from "styled-components";
3
3
 
4
4
  export const ButtonGroupContainer = styled.div<{ $orientation: string }>`
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { ButtonGroupProps } from "./ButtonGroup.types";
3
- import Button from "../atoms/Button";
3
+ import Button from "../../atoms/Button";
4
4
  import { ButtonGroupContainer } from "./ButtonGroup.styles";
5
5
 
6
6
  const ButtonGroup: React.FC<ButtonGroupProps> = ({
@@ -0,0 +1,143 @@
1
+ import styled from "styled-components";
2
+ import { DayPicker as RDPDayPicker } from "react-day-picker";
3
+
4
+ export const StyledDayPicker = styled(RDPDayPicker)`
5
+ padding: 0.75rem; /* p-3 */
6
+
7
+ /* Estilo para o container dos meses */
8
+ .rdp-months {
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: 1rem; /* equivalente a space-y-4 */
12
+ }
13
+
14
+ @media (min-width: 640px) {
15
+ .rdp-months {
16
+ flex-direction: row;
17
+ gap: 0 1rem; /* sm:flex-row com space-x-4 e sm:space-y-0 */
18
+ }
19
+ }
20
+
21
+ /* Cada mês */
22
+ .rdp-month {
23
+ margin-bottom: 1rem;
24
+ }
25
+
26
+ /* Legenda (caption) do mês */
27
+ .rdp-caption {
28
+ display: flex;
29
+ justify-content: center;
30
+ padding-top: 0.25rem; /* pt-1 */
31
+ position: relative;
32
+ align-items: center;
33
+ }
34
+
35
+ .rdp-caption_label {
36
+ font-size: 0.875rem; /* text-sm */
37
+ font-weight: 500; /* font-medium */
38
+ }
39
+
40
+ /* Navegação */
41
+ .rdp-nav {
42
+ display: flex;
43
+ align-items: center;
44
+ gap: 0.25rem; /* space-x-1 */
45
+ position: relative;
46
+ }
47
+
48
+ .rdp-nav_button {
49
+ height: 1.75rem; /* h-7 */
50
+ width: 1.75rem; /* w-7 */
51
+ background: transparent;
52
+ padding: 0;
53
+ opacity: 0.5;
54
+ /* Pode-se definir aqui estilos de border se desejar imitar o buttonVariants({ variant: "outline" }) */
55
+ border: 1px solid #d1d5db;
56
+ cursor: pointer;
57
+ transition: opacity 0.2s ease;
58
+
59
+ &:hover {
60
+ opacity: 1;
61
+ }
62
+ }
63
+
64
+ .rdp-nav_button_previous {
65
+ position: absolute;
66
+ left: 0.25rem; /* left-1 */
67
+ }
68
+
69
+ .rdp-nav_button_next {
70
+ position: absolute;
71
+ right: 0.25rem; /* right-1 */
72
+ }
73
+
74
+ /* Tabela de dias */
75
+ .rdp-table {
76
+ width: 100%; /* w-full */
77
+ border-collapse: collapse;
78
+ margin-top: 0.25rem; /* espaço vertical */
79
+ }
80
+
81
+ .rdp-head_row {
82
+ display: flex;
83
+ }
84
+
85
+ .rdp-head_cell {
86
+ font-size: 0.8rem; /* text-[0.8rem] */
87
+ color: #6b7280; /* text-muted-foreground */
88
+ border-radius: 0.375rem; /* rounded-md */
89
+ width: 2.25rem; /* w-9 */
90
+ font-weight: 400; /* font-normal */
91
+ }
92
+
93
+ .rdp-row {
94
+ display: flex;
95
+ width: 100%;
96
+ margin-top: 0.5rem; /* mt-2 */
97
+ }
98
+
99
+ .rdp-cell {
100
+ height: 2.25rem; /* h-9 */
101
+ width: 2.25rem; /* w-9 */
102
+ text-align: center;
103
+ font-size: 0.875rem; /* text-sm */
104
+ padding: 0;
105
+ position: relative;
106
+
107
+ &:focus-within {
108
+ z-index: 20;
109
+ }
110
+ }
111
+
112
+ /* Botão do dia */
113
+ .rdp-day {
114
+ height: 2.25rem; /* h-9 */
115
+ width: 2.25rem; /* w-9 */
116
+ padding: 0;
117
+ font-size: 0.875rem;
118
+ font-weight: 400;
119
+ background: transparent;
120
+ border: none;
121
+ cursor: pointer;
122
+ /* Estilos adicionais podem ser aplicados conforme necessário */
123
+ }
124
+
125
+ .rdp-day_selected {
126
+ background: #3b82f6; /* bg-primary (substitua conforme seu tema) */
127
+ color: #ffffff; /* text-primary-foreground */
128
+ }
129
+
130
+ .rdp-day_today {
131
+ background: #f3f4f6; /* bg-accent */
132
+ color: #111827; /* text-accent-foreground */
133
+ }
134
+
135
+ .rdp-day_outside {
136
+ color: #6b7280; /* text-muted-foreground */
137
+ }
138
+
139
+ .rdp-day_disabled {
140
+ color: #6b7280;
141
+ opacity: 0.5;
142
+ }
143
+ `;
@@ -0,0 +1,31 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import * as React from "react";
3
+ import { ChevronLeft, ChevronRight } from "lucide-react";
4
+ import { DayPicker as RDPDayPicker } from "react-day-picker";
5
+ import { StyledDayPicker } from "./Calendar.styles";
6
+
7
+ export type CalendarProps = React.ComponentProps<typeof RDPDayPicker>;
8
+
9
+ function Calendar({ className, ...props }: CalendarProps) {
10
+ return (
11
+ <StyledDayPicker
12
+ className={className}
13
+ showOutsideDays={true}
14
+ components={
15
+ {
16
+ IconPrevious: ({ className, ...props }: { className?: string }) => (
17
+ <ChevronLeft className={className} {...props} />
18
+ ),
19
+ IconNext: ({ className, ...props }: { className?: string }) => (
20
+ <ChevronRight className={className} {...props} />
21
+ ),
22
+ } as unknown as any
23
+ }
24
+ {...props}
25
+ />
26
+ );
27
+ }
28
+
29
+ Calendar.displayName = "Calendar";
30
+
31
+ export { Calendar };
@@ -0,0 +1,65 @@
1
+ import styled, { keyframes } from "styled-components";
2
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
3
+
4
+ const animateIn = keyframes`
5
+ from { opacity: 0; transform: scale(0.95); }
6
+ to { opacity: 1; transform: scale(1); }
7
+ `;
8
+
9
+ const animateOut = keyframes`
10
+ from { opacity: 1; transform: scale(1); }
11
+ to { opacity: 0; transform: scale(0.95); }
12
+ `;
13
+
14
+ const slideInFromTop2 = keyframes`
15
+ from { transform: translateY(-10px); opacity: 0; }
16
+ to { transform: translateY(0); opacity: 1; }
17
+ `;
18
+
19
+ const slideInFromRight2 = keyframes`
20
+ from { transform: translateX(10px); opacity: 0; }
21
+ to { transform: translateX(0); opacity: 1; }
22
+ `;
23
+
24
+ const slideInFromLeft2 = keyframes`
25
+ from { transform: translateX(-10px); opacity: 0; }
26
+ to { transform: translateX(0); opacity: 1; }
27
+ `;
28
+
29
+ const slideInFromBottom2 = keyframes`
30
+ from { transform: translateY(10px); opacity: 0; }
31
+ to { transform: translateY(0); opacity: 1; }
32
+ `;
33
+
34
+ export const StyledPopoverContent = styled(PopoverPrimitive.Content)`
35
+ z-index: 50;
36
+ width: 18rem;
37
+ border-radius: 0.375rem;
38
+ border: 1px solid #e5e7eb;
39
+ background-color: var(--popover-bg, #fff);
40
+ padding: 1rem;
41
+ color: var(--popover-foreground, #000);
42
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
43
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
44
+ outline: none;
45
+
46
+ &[data-state="open"] {
47
+ animation: ${animateIn} 0.3s ease-out forwards;
48
+ }
49
+ &[data-state="closed"] {
50
+ animation: ${animateOut} 0.3s ease-in forwards;
51
+ }
52
+
53
+ &[data-side="bottom"] {
54
+ animation: ${slideInFromTop2} 0.3s ease-out forwards;
55
+ }
56
+ &[data-side="left"] {
57
+ animation: ${slideInFromRight2} 0.3s ease-out forwards;
58
+ }
59
+ &[data-side="right"] {
60
+ animation: ${slideInFromLeft2} 0.3s ease-out forwards;
61
+ }
62
+ &[data-side="top"] {
63
+ animation: ${slideInFromBottom2} 0.3s ease-out forwards;
64
+ }
65
+ `;
@@ -0,0 +1,23 @@
1
+ import * as React from "react";
2
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
3
+ import { StyledPopoverContent } from "./Popover.styles";
4
+
5
+ const Popover = PopoverPrimitive.Root;
6
+ const PopoverTrigger = PopoverPrimitive.Trigger;
7
+
8
+ const PopoverContent = React.forwardRef<
9
+ React.ElementRef<typeof PopoverPrimitive.Content>,
10
+ React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
11
+ >(({ align = "center", sideOffset = 4, ...props }, ref) => (
12
+ <PopoverPrimitive.Portal>
13
+ <StyledPopoverContent
14
+ ref={ref}
15
+ align={align}
16
+ sideOffset={sideOffset}
17
+ {...props}
18
+ />
19
+ </PopoverPrimitive.Portal>
20
+ ));
21
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
22
+
23
+ export { Popover, PopoverTrigger, PopoverContent };
@@ -0,0 +1,51 @@
1
+ import { semanticColors, semanticShadows } from "../../../styles/tokens";
2
+ import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
3
+ import styled from "styled-components";
4
+ import { Circle } from "lucide-react";
5
+
6
+ export const StyledRadioGroup = styled(RadioGroupPrimitive.Root)`
7
+ display: grid;
8
+ gap: 0.5rem;
9
+ `;
10
+
11
+ export const StyledRadioGroupItem = styled(RadioGroupPrimitive.Item)`
12
+ display: flex;
13
+ align-items: center;
14
+ justify-content: center;
15
+
16
+ width: 1rem;
17
+ height: 1rem;
18
+ aspect-ratio: 1 / 1;
19
+ border-radius: 9999px;
20
+ border: 1px solid ${semanticColors.branding.border.enabled};
21
+ color: ${semanticColors.branding.surface.enabled};
22
+ outline: none;
23
+
24
+ &:focus-visible {
25
+ outline: none;
26
+ box-shadow: 0 0 0 2px ${semanticShadows.shadow.none};
27
+ outline-offset: 2px;
28
+ }
29
+
30
+ &:disabled {
31
+ cursor: not-allowed;
32
+ opacity: 0.5;
33
+ }
34
+ `;
35
+
36
+ export const StyledIndicator = styled(RadioGroupPrimitive.Indicator)`
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: center;
40
+ width: 1rem;
41
+ height: 1rem;
42
+ border-radius: 9999px;
43
+ color: ${semanticColors.branding.surface.enabled};
44
+ `;
45
+
46
+ export const StyledCircle = styled(Circle)`
47
+ width: 0.625rem;
48
+ height: 0.625rem;
49
+ fill: currentColor;
50
+ color: currentColor;
51
+ `;
@@ -0,0 +1,32 @@
1
+ import * as React from "react";
2
+ import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
3
+ import {
4
+ StyledRadioGroup,
5
+ StyledRadioGroupItem,
6
+ StyledIndicator,
7
+ StyledCircle,
8
+ } from "./RadioBoxGroup.styles";
9
+
10
+ const RadioGroup = React.forwardRef<
11
+ React.ElementRef<typeof RadioGroupPrimitive.Root>,
12
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
13
+ >(({ ...props }, ref) => {
14
+ return <StyledRadioGroup {...props} ref={ref} />;
15
+ });
16
+ RadioGroup.displayName = "RadioGroup";
17
+
18
+ const RadioGroupItem = React.forwardRef<
19
+ React.ElementRef<typeof RadioGroupPrimitive.Item>,
20
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
21
+ >(({ ...props }, ref) => {
22
+ return (
23
+ <StyledRadioGroupItem {...props} ref={ref}>
24
+ <StyledIndicator>
25
+ <StyledCircle />
26
+ </StyledIndicator>
27
+ </StyledRadioGroupItem>
28
+ );
29
+ });
30
+ RadioGroupItem.displayName = "RadioGroupItem";
31
+
32
+ export { RadioGroup, RadioGroupItem };
@@ -0,0 +1,106 @@
1
+ // Dialog.styled.tsx
2
+ import styled from "styled-components";
3
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
4
+
5
+ /* ========================= */
6
+ /* === Overlay Component === */
7
+ /* ========================= */
8
+ export const StyledDialogOverlay = styled(DialogPrimitive.Overlay)`
9
+ position: fixed;
10
+ inset: 0;
11
+ z-index: 50;
12
+ background-color: rgba(0, 0, 0, 0.8);
13
+ /* Você pode definir animações adicionais aqui, caso deseje */
14
+ `;
15
+
16
+ /* ========================== */
17
+ /* === Content Component === */
18
+ /* ========================== */
19
+ export const StyledDialogContent = styled(DialogPrimitive.Content)`
20
+ position: fixed;
21
+ left: 50%;
22
+ top: 50%;
23
+ z-index: 50;
24
+ display: grid;
25
+ width: 100%;
26
+ max-width: 32rem; /* Equivalente a max-w-lg */
27
+ transform: translate(-50%, -50%);
28
+ gap: 1rem; /* gap-4 */
29
+ border: 1px solid #e5e7eb;
30
+ background-color: #fff; /* bg-background */
31
+ padding: 1.5rem; /* p-6 */
32
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
33
+ 0 4px 6px -2px rgba(0, 0, 0, 0.05);
34
+ border-radius: 0.5rem; /* sm:rounded-lg */
35
+ transition: all 0.2s ease;
36
+ `;
37
+
38
+ /* ============================ */
39
+ /* === Close Button Component === */
40
+ /* ============================ */
41
+ export const StyledDialogClose = styled(DialogPrimitive.Close)`
42
+ position: absolute;
43
+ top: 1rem; /* top-4 */
44
+ right: 1rem; /* right-4 */
45
+ background: none;
46
+ border: none;
47
+ border-radius: 0.125rem; /* rounded-sm */
48
+ opacity: 0.7;
49
+ cursor: pointer;
50
+ transition: opacity 0.2s ease;
51
+
52
+ &:hover {
53
+ opacity: 1;
54
+ }
55
+
56
+ &:focus {
57
+ outline: none;
58
+ box-shadow: 0 0 0 2px #3b82f6; /* focus:ring-2 focus:ring-ring */
59
+ }
60
+ `;
61
+
62
+ /* ========================== */
63
+ /* === Header Component === */
64
+ /* ========================== */
65
+ export const StyledDialogHeader = styled.div`
66
+ display: flex;
67
+ flex-direction: column;
68
+ gap: 0.375rem; /* Aproximadamente space-y-1.5 */
69
+ text-align: center;
70
+
71
+ @media (min-width: 640px) {
72
+ text-align: left;
73
+ }
74
+ `;
75
+
76
+ /* ========================== */
77
+ /* === Footer Component === */
78
+ /* ========================== */
79
+ export const StyledDialogFooter = styled.div`
80
+ display: flex;
81
+ flex-direction: column-reverse;
82
+ justify-content: flex-end;
83
+ gap: 0.5rem; /* Aproximadamente sm:space-x-2 */
84
+
85
+ @media (min-width: 640px) {
86
+ flex-direction: row;
87
+ }
88
+ `;
89
+
90
+ /* =========================== */
91
+ /* === Title Component === */
92
+ /* =========================== */
93
+ export const StyledDialogTitle = styled(DialogPrimitive.Title)`
94
+ font-size: 1.125rem; /* text-lg */
95
+ font-weight: 600; /* font-semibold */
96
+ line-height: 1; /* leading-none */
97
+ letter-spacing: -0.025em; /* tracking-tight */
98
+ `;
99
+
100
+ /* =============================== */
101
+ /* === Description Component === */
102
+ /* =============================== */
103
+ export const StyledDialogDescription = styled(DialogPrimitive.Description)`
104
+ font-size: 0.875rem; /* text-sm */
105
+ color: #6b7280; /* text-muted-foreground */
106
+ `;
@@ -0,0 +1,69 @@
1
+ // Dialog.tsx
2
+ import * as React from "react";
3
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
4
+ import { X } from "lucide-react";
5
+ import {
6
+ StyledDialogOverlay,
7
+ StyledDialogContent,
8
+ StyledDialogClose,
9
+ StyledDialogHeader,
10
+ StyledDialogFooter,
11
+ StyledDialogTitle,
12
+ StyledDialogDescription,
13
+ } from "./Dialog.styles";
14
+
15
+ const Dialog = DialogPrimitive.Root;
16
+ const DialogTrigger = DialogPrimitive.Trigger;
17
+ const DialogPortal = DialogPrimitive.Portal;
18
+
19
+ const DialogContent = React.forwardRef<
20
+ React.ElementRef<typeof DialogPrimitive.Content>,
21
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
22
+ >(({ children, ...props }, ref) => (
23
+ <DialogPortal>
24
+ <StyledDialogOverlay />
25
+ <StyledDialogContent ref={ref} {...props}>
26
+ {children}
27
+ <StyledDialogClose>
28
+ <X style={{ height: "1rem", width: "1rem" }} />
29
+ <span className="sr-only">Close</span>
30
+ </StyledDialogClose>
31
+ </StyledDialogContent>
32
+ </DialogPortal>
33
+ ));
34
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
35
+
36
+ const DialogHeader: React.FC<React.HTMLAttributes<HTMLDivElement>> = (
37
+ props
38
+ ) => <StyledDialogHeader {...props} />;
39
+ DialogHeader.displayName = "DialogHeader";
40
+
41
+ const DialogFooter: React.FC<React.HTMLAttributes<HTMLDivElement>> = (
42
+ props
43
+ ) => <StyledDialogFooter {...props} />;
44
+ DialogFooter.displayName = "DialogFooter";
45
+
46
+ const DialogTitle = React.forwardRef<
47
+ React.ElementRef<typeof DialogPrimitive.Title>,
48
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
49
+ >((props, ref) => <StyledDialogTitle ref={ref} {...props} />);
50
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
51
+
52
+ const DialogDescription = React.forwardRef<
53
+ React.ElementRef<typeof DialogPrimitive.Description>,
54
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
55
+ >((props, ref) => <StyledDialogDescription ref={ref} {...props} />);
56
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
57
+
58
+ export {
59
+ Dialog,
60
+ DialogTrigger,
61
+ DialogPortal,
62
+ DialogContent,
63
+ StyledDialogOverlay as DialogOverlay,
64
+ StyledDialogClose as DialogClose,
65
+ DialogHeader,
66
+ DialogFooter,
67
+ DialogTitle,
68
+ DialogDescription,
69
+ };
@@ -0,0 +1,35 @@
1
+ // Form.styled.tsx
2
+ import styled from "styled-components";
3
+
4
+ /* Container para cada item do formulário */
5
+ export const StyledFormItem = styled.div`
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: 0.5rem; /* Aproximadamente o "space-y-2" do Tailwind */
9
+ `;
10
+
11
+ /* Estilo para o label do formulário; a propriedade "error" permite alterar a cor se necessário */
12
+ export const StyledFormLabel = styled.label<{ error?: boolean }>`
13
+ font-size: 0.875rem; /* text-sm */
14
+ font-weight: 500; /* font-medium */
15
+ line-height: 1; /* leading-none */
16
+ color: ${(props) => (props.error ? "#dc2626" : "inherit")};
17
+ `;
18
+
19
+ /* Estilo para o container de controle de formulário (por exemplo, input) */
20
+ export const StyledFormControl = styled.div`
21
+ /* Adicione aqui quaisquer estilos específicos para o container do input, se necessário */
22
+ `;
23
+
24
+ /* Estilo para a descrição do formulário */
25
+ export const StyledFormDescription = styled.p`
26
+ font-size: 0.875rem; /* text-sm */
27
+ color: #6b7280; /* text-muted-foreground */
28
+ `;
29
+
30
+ /* Estilo para a mensagem (geralmente de erro) */
31
+ export const StyledFormMessage = styled.p`
32
+ font-size: 0.875rem; /* text-sm */
33
+ font-weight: 500; /* font-medium */
34
+ color: #dc2626; /* text-destructive */
35
+ `;
@@ -0,0 +1,158 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import {
4
+ Controller,
5
+ ControllerProps,
6
+ FieldPath,
7
+ FieldValues,
8
+ FormProvider,
9
+ useFormContext,
10
+ } from "react-hook-form";
11
+ import {
12
+ StyledFormItem,
13
+ StyledFormLabel,
14
+ StyledFormDescription,
15
+ StyledFormMessage,
16
+ } from "./Form.styles";
17
+
18
+ /* Reexporta o FormProvider como Form para facilitar o uso */
19
+ const Form = FormProvider;
20
+
21
+ type FormFieldContextValue<
22
+ TFieldValues extends FieldValues = FieldValues,
23
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
24
+ > = {
25
+ name: TName;
26
+ };
27
+
28
+ const FormFieldContext = React.createContext<FormFieldContextValue>(
29
+ {} as FormFieldContextValue
30
+ );
31
+
32
+ const FormField = <
33
+ TFieldValues extends FieldValues = FieldValues,
34
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
35
+ >({
36
+ ...props
37
+ }: ControllerProps<TFieldValues, TName>) => {
38
+ return (
39
+ <FormFieldContext.Provider value={{ name: props.name }}>
40
+ <Controller {...props} />
41
+ </FormFieldContext.Provider>
42
+ );
43
+ };
44
+
45
+ type FormItemContextValue = { id: string };
46
+
47
+ const FormItemContext = React.createContext<FormItemContextValue>(
48
+ {} as FormItemContextValue
49
+ );
50
+
51
+ const FormItem = React.forwardRef<
52
+ HTMLDivElement,
53
+ React.HTMLAttributes<HTMLDivElement>
54
+ >(({ ...props }, ref) => {
55
+ const id = React.useId();
56
+ return (
57
+ <FormItemContext.Provider value={{ id }}>
58
+ <StyledFormItem ref={ref} {...props} />
59
+ </FormItemContext.Provider>
60
+ );
61
+ });
62
+ FormItem.displayName = "FormItem";
63
+
64
+ const useFormField = () => {
65
+ const fieldContext = React.useContext(FormFieldContext);
66
+ const itemContext = React.useContext(FormItemContext);
67
+ const { getFieldState, formState } = useFormContext();
68
+
69
+ const fieldState = getFieldState(fieldContext.name, formState);
70
+
71
+ if (!fieldContext) {
72
+ throw new Error("useFormField should be used within <FormField>");
73
+ }
74
+
75
+ const { id } = itemContext;
76
+ return {
77
+ id,
78
+ name: fieldContext.name,
79
+ formItemId: `${id}-form-item`,
80
+ formDescriptionId: `${id}-form-item-description`,
81
+ formMessageId: `${id}-form-item-message`,
82
+ ...fieldState,
83
+ };
84
+ };
85
+
86
+ const FormLabel = React.forwardRef<
87
+ HTMLLabelElement,
88
+ React.ComponentPropsWithoutRef<"label">
89
+ >(({ children, ...props }, ref) => {
90
+ const { error, formItemId } = useFormField();
91
+ return (
92
+ <StyledFormLabel ref={ref} htmlFor={formItemId} error={!!error} {...props}>
93
+ {children}
94
+ </StyledFormLabel>
95
+ );
96
+ });
97
+ FormLabel.displayName = "FormLabel";
98
+
99
+ const FormControl = React.forwardRef<
100
+ React.ElementRef<typeof Slot>,
101
+ React.ComponentPropsWithoutRef<typeof Slot>
102
+ >(({ ...props }, ref) => {
103
+ const { error, formItemId, formDescriptionId, formMessageId } =
104
+ useFormField();
105
+ return (
106
+ <Slot
107
+ ref={ref}
108
+ id={formItemId}
109
+ aria-describedby={
110
+ !error
111
+ ? `${formDescriptionId}`
112
+ : `${formDescriptionId} ${formMessageId}`
113
+ }
114
+ aria-invalid={!!error}
115
+ {...props}
116
+ />
117
+ );
118
+ });
119
+ FormControl.displayName = "FormControl";
120
+
121
+ const FormDescription = React.forwardRef<
122
+ HTMLParagraphElement,
123
+ React.HTMLAttributes<HTMLParagraphElement>
124
+ >(({ children, ...props }, ref) => {
125
+ const { formDescriptionId } = useFormField();
126
+ return (
127
+ <StyledFormDescription ref={ref} id={formDescriptionId} {...props}>
128
+ {children}
129
+ </StyledFormDescription>
130
+ );
131
+ });
132
+ FormDescription.displayName = "FormDescription";
133
+
134
+ const FormMessage = React.forwardRef<
135
+ HTMLParagraphElement,
136
+ React.HTMLAttributes<HTMLParagraphElement>
137
+ >(({ children, ...props }, ref) => {
138
+ const { error, formMessageId } = useFormField();
139
+ const body = error ? String(error?.message) : children;
140
+ if (!body) return null;
141
+ return (
142
+ <StyledFormMessage ref={ref} id={formMessageId} {...props}>
143
+ {body}
144
+ </StyledFormMessage>
145
+ );
146
+ });
147
+ FormMessage.displayName = "FormMessage";
148
+
149
+ export {
150
+ useFormField,
151
+ Form,
152
+ FormItem,
153
+ FormLabel,
154
+ FormControl,
155
+ FormDescription,
156
+ FormMessage,
157
+ FormField,
158
+ };
package/src/index.ts CHANGED
@@ -1,44 +1,106 @@
1
1
  /* eslint-disable react-refresh/only-export-components */
2
2
  // Import all components
3
+ import {
4
+ Dialog,
5
+ DialogContent,
6
+ DialogDescription,
7
+ DialogPortal,
8
+ DialogTitle,
9
+ DialogTrigger,
10
+ } from "@radix-ui/react-dialog";
11
+ import { Form } from "react-hook-form";
12
+ import Badge from "./components/atoms/Badge/Badge";
13
+ import Breadcrumb from "./components/atoms/Breadcrumb/Breadcrumb";
3
14
  import Button from "./components/atoms/Button";
15
+ import { Checkbox } from "./components/atoms/Checkbox/Checkbox";
4
16
  import Icon from "./components/atoms/Icon";
5
17
  import Input from "./components/atoms/Input";
6
- import Select from "./components/atoms/Select";
7
- import AlertDialog from "./components/organisms/AlertDialog/AlertDialog";
8
- import Badge from "./components/atoms/Badge/Badge";
9
- import Breadcrumb from "./components/atoms/Breadcrumb/Breadcrumb";
18
+ import { Label } from "./components/atoms/Label/Label";
10
19
  import RadioButton from "./components/atoms/RadioButton/RadioButton";
11
- import { Checkbox } from "./components/atoms/Checkbox/Checkbox";
20
+ import Select from "./components/atoms/Select";
12
21
  import Switch from "./components/atoms/Switch/Switch";
13
22
  import Text from "./components/atoms/Text";
14
23
  import Textarea from "./components/atoms/Textarea/Textarea";
15
- import ButtonGroup from "./components/molecules/ButtonGroup";
24
+ import ButtonGroup from "./components/molecules/ButtonGroup/ButtonGroup";
25
+ import { Calendar } from "./components/molecules/Calendar/Calendar";
26
+ import {
27
+ Popover,
28
+ PopoverContent,
29
+ PopoverTrigger,
30
+ } from "./components/molecules/Popover/Popover";
31
+ import {
32
+ RadioGroup,
33
+ RadioGroupItem,
34
+ } from "./components/molecules/RadioBoxGroup/RadioBoxGroup";
35
+ import AlertDialog from "./components/organisms/AlertDialog/AlertDialog";
36
+ import {
37
+ DialogFooter,
38
+ DialogHeader,
39
+ } from "./components/organisms/Dialog/Dialog";
40
+ import {
41
+ StyledDialogClose,
42
+ StyledDialogOverlay,
43
+ } from "./components/organisms/Dialog/Dialog.styles";
44
+ import {
45
+ FormControl,
46
+ FormDescription,
47
+ FormField,
48
+ FormItem,
49
+ FormLabel,
50
+ FormMessage,
51
+ useFormField,
52
+ } from "./components/organisms/Form/Form";
16
53
 
17
54
  // Export all components
18
55
  export {
19
- Button,
20
- Icon,
21
- Input,
22
- Select,
23
56
  AlertDialog,
24
57
  Badge,
25
58
  Breadcrumb,
26
- RadioButton,
59
+ Button,
60
+ ButtonGroup,
61
+ Calendar,
27
62
  Checkbox,
63
+ Dialog,
64
+ DialogContent,
65
+ DialogDescription,
66
+ DialogFooter,
67
+ DialogHeader,
68
+ DialogPortal,
69
+ DialogTitle,
70
+ DialogTrigger,
71
+ Form,
72
+ FormControl,
73
+ FormDescription,
74
+ FormField,
75
+ FormItem,
76
+ FormLabel,
77
+ FormMessage,
78
+ Icon,
79
+ Input,
80
+ Label,
81
+ Popover,
82
+ PopoverContent,
83
+ PopoverTrigger,
84
+ RadioButton,
85
+ RadioGroup,
86
+ RadioGroupItem,
87
+ Select,
88
+ StyledDialogClose,
89
+ StyledDialogOverlay,
28
90
  Switch,
29
91
  Text,
30
92
  Textarea,
31
- ButtonGroup,
93
+ useFormField,
32
94
  };
33
95
 
34
96
  // Export all tokens from a new file
35
- export { primitiveColors, semanticColors } from "./styles/tokens/colors";
36
97
  export { primitiveBorders, semanticBorders } from "./styles/tokens/borders";
98
+ export { primitiveColors, semanticColors } from "./styles/tokens/colors";
99
+ export { primitiveRadius, semanticRadius } from "./styles/tokens/radius";
37
100
  export { primitiveShadows, semanticShadows } from "./styles/tokens/shadows";
38
101
  export { primitiveSizes, semanticSizes } from "./styles/tokens/sizes";
39
- export { primitiveRadius, semanticRadius } from "./styles/tokens/radius";
40
102
  export { typographyTokens } from "./styles/tokens/typography";
41
103
 
42
104
  // Export all types
43
- export type { Shadows } from "./types/shadows.types";
44
105
  export type { Radius } from "./types/radius.types";
106
+ export type { Shadows } from "./types/shadows.types";