@granto-umbrella/umbrella-components 2.2.8 → 2.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@granto-umbrella/umbrella-components",
3
- "version": "2.2.8",
3
+ "version": "2.3.0",
4
4
  "description": "Umbrella Components for React",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,63 +1,31 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1
2
  import * as React from "react";
2
3
  import { ChevronLeft, ChevronRight } from "lucide-react";
3
- import { DayPicker } from "react-day-picker";
4
+ import { DayPicker as RDPDayPicker } from "react-day-picker";
5
+ import { StyledDayPicker } from "./Calendar.styles";
4
6
 
5
- import { cn } from "@/lib/utils";
7
+ export type CalendarProps = React.ComponentProps<typeof RDPDayPicker>;
6
8
 
7
- export type CalendarProps = React.ComponentProps<typeof DayPicker>;
8
-
9
- function Calendar({
10
- className,
11
- classNames,
12
- showOutsideDays = true,
13
- ...props
14
- }: CalendarProps) {
9
+ function Calendar({ className, ...props }: CalendarProps) {
15
10
  return (
16
- <DayPicker
17
- showOutsideDays={showOutsideDays}
18
- className={cn("p-3", className)}
19
- classNames={{
20
- months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
21
- month: "space-y-4",
22
- caption: "flex justify-center pt-1 relative items-center",
23
- caption_label: "text-sm font-medium",
24
- nav: "space-x-1 flex items-center",
25
- nav_button: cn(
26
- "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
27
- ),
28
- nav_button_previous: "absolute left-1",
29
- nav_button_next: "absolute right-1",
30
- table: "w-full border-collapse space-y-1",
31
- head_row: "flex",
32
- head_cell:
33
- "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
34
- row: "flex w-full mt-2",
35
- cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
36
- day: cn("h-9 w-9 p-0 font-normal aria-selected:opacity-100"),
37
- day_range_end: "day-range-end",
38
- day_selected:
39
- "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
40
- day_today: "bg-accent text-accent-foreground",
41
- day_outside:
42
- "day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
43
- day_disabled: "text-muted-foreground opacity-50",
44
- day_range_middle:
45
- "aria-selected:bg-accent aria-selected:text-accent-foreground",
46
- day_hidden: "invisible",
47
- ...classNames,
48
- }}
49
- components={{
50
- IconLeft: ({ className, ...props }) => (
51
- <ChevronLeft className={cn("h-4 w-4", className)} {...props} />
52
- ),
53
- IconRight: ({ className, ...props }) => (
54
- <ChevronRight className={cn("h-4 w-4", className)} {...props} />
55
- ),
56
- }}
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
+ }
57
24
  {...props}
58
25
  />
59
26
  );
60
27
  }
28
+
61
29
  Calendar.displayName = "Calendar";
62
30
 
63
31
  export { Calendar };
@@ -1,25 +1,19 @@
1
1
  import * as React from "react";
2
2
  import * as PopoverPrimitive from "@radix-ui/react-popover";
3
-
4
- import { cn } from "../../../lib/utils";
3
+ import { StyledPopoverContent } from "./Popover.styles";
5
4
 
6
5
  const Popover = PopoverPrimitive.Root;
7
-
8
6
  const PopoverTrigger = PopoverPrimitive.Trigger;
9
7
 
10
8
  const PopoverContent = React.forwardRef<
11
9
  React.ElementRef<typeof PopoverPrimitive.Content>,
12
10
  React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
13
- >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
11
+ >(({ align = "center", sideOffset = 4, ...props }, ref) => (
14
12
  <PopoverPrimitive.Portal>
15
- <PopoverPrimitive.Content
13
+ <StyledPopoverContent
16
14
  ref={ref}
17
15
  align={align}
18
16
  sideOffset={sideOffset}
19
- className={cn(
20
- "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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",
21
- className
22
- )}
23
17
  {...props}
24
18
  />
25
19
  </PopoverPrimitive.Portal>
@@ -0,0 +1,54 @@
1
+ import {
2
+ semanticColors,
3
+ semanticRadius,
4
+ semanticSizes,
5
+ typographyTokens,
6
+ } from "../../../styles/tokens";
7
+ import styled from "styled-components";
8
+
9
+ export const ToggleContainer = styled.div`
10
+ position: relative;
11
+ display: inline-flex;
12
+ background: transparent;
13
+ border-radius: ${semanticRadius.global.radius.md2};
14
+ padding: ${semanticSizes.global.padding.xs};
15
+ white-space: nowrap;
16
+ `;
17
+
18
+ export const Slider = styled.div<{
19
+ activeIndex: number;
20
+ count: number;
21
+ }>`
22
+ position: absolute;
23
+ top: ${semanticSizes.global.padding.xs};
24
+ left: ${({ activeIndex, count }) => (activeIndex * 100) / count}%;
25
+ width: ${({ count }) => 100 / count}%;
26
+ height: calc(100% - ${semanticSizes.global.padding.xs} * 2);
27
+ background: ${semanticColors.base.background};
28
+ border-radius: ${semanticRadius.global.radius.md2};
29
+ transition: left 200ms ease-in-out;
30
+ z-index: 0;
31
+ `;
32
+
33
+ export const TabButton = styled.button<{ active: boolean }>`
34
+ position: relative;
35
+ z-index: 1;
36
+ display: flex;
37
+ width: 230px;
38
+ justify-content: center;
39
+ align-items: center;
40
+ padding: ${semanticSizes.global.padding.sm} ${semanticSizes.global.padding.lg};
41
+ font-size: ${typographyTokens.fontSizes.bodyS};
42
+ color: ${({ active }) =>
43
+ active ? semanticColors.base.text : semanticColors.neutral[400]};
44
+ background: none;
45
+ border: none;
46
+ cursor: pointer;
47
+ transition: color 200ms;
48
+
49
+ white-space: nowrap;
50
+
51
+ &:focus {
52
+ outline: none;
53
+ }
54
+ `;
@@ -0,0 +1,35 @@
1
+ import React, { useState } from "react";
2
+ import { ToggleContainer, Slider, TabButton } from "./TabToggle.styles";
3
+
4
+ export type TabItem = {
5
+ title: string;
6
+ quantity: number;
7
+ };
8
+
9
+ export type TabToggleProps = {
10
+ items: TabItem[];
11
+ };
12
+
13
+ export const TabToggle: React.FC<TabToggleProps> = ({ items }) => {
14
+ const [activeIndex, setActiveIndex] = useState(0);
15
+ const count = items.length;
16
+
17
+ return (
18
+ <ToggleContainer>
19
+ {/* Slider agora sabe quantas abas existem */}
20
+ <Slider activeIndex={activeIndex} count={count} />
21
+
22
+ {items.map(({ title, quantity }, i) => (
23
+ <TabButton
24
+ key={title}
25
+ active={i === activeIndex}
26
+ onClick={() => setActiveIndex(i)}
27
+ >
28
+ {title} ({quantity})
29
+ </TabButton>
30
+ ))}
31
+ </ToggleContainer>
32
+ );
33
+ };
34
+
35
+ export default TabToggle;
@@ -0,0 +1,57 @@
1
+ import * as React from "react";
2
+ import { format } from "date-fns";
3
+ import { Calendar, CalendarIcon } from "lucide-react";
4
+
5
+ import { cn } from "../../../lib/utils";
6
+ import { FormControl } from "../Form/Form";
7
+ import {
8
+ Popover,
9
+ PopoverContent,
10
+ PopoverTrigger,
11
+ } from "../../molecules/Popover/Popover";
12
+ import Button from "../../atoms/Button/Button";
13
+
14
+ export type DatePickerFieldProps = {
15
+ formField: {
16
+ value: Date | null;
17
+ onChange: (date: Date | null) => void;
18
+ };
19
+ };
20
+
21
+ const DatePickerField: React.FC<DatePickerFieldProps> = ({ formField }) => {
22
+ return (
23
+ <Popover>
24
+ <PopoverTrigger asChild>
25
+ <FormControl>
26
+ <Button
27
+ variant="outline"
28
+ className={cn(
29
+ "w-[240px] pl-3 text-left font-normal",
30
+ !formField.value && "text-muted-foreground"
31
+ )}
32
+ >
33
+ {formField.value ? (
34
+ format(formField.value, "PPP")
35
+ ) : (
36
+ <span>CALENDÁRIO</span>
37
+ )}
38
+ <CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
39
+ </Button>
40
+ </FormControl>
41
+ </PopoverTrigger>
42
+ <PopoverContent className="w-auto p-0" align="start">
43
+ <Calendar
44
+ mode="single"
45
+ selected={formField.value}
46
+ onSelect={formField.onChange}
47
+ disabled={(date) =>
48
+ date > new Date() || date < new Date("1900-01-01")
49
+ }
50
+ initialFocus
51
+ />
52
+ </PopoverContent>
53
+ </Popover>
54
+ );
55
+ };
56
+
57
+ export default DatePickerField;
@@ -1,5 +1,6 @@
1
+ /* eslint-disable react-refresh/only-export-components */
2
+ // Form.tsx
1
3
  import * as React from "react";
2
- import * as LabelPrimitive from "@radix-ui/react-label";
3
4
  import { Slot } from "@radix-ui/react-slot";
4
5
  import {
5
6
  Controller,
@@ -9,10 +10,14 @@ import {
9
10
  FormProvider,
10
11
  useFormContext,
11
12
  } from "react-hook-form";
13
+ import {
14
+ StyledFormItem,
15
+ StyledFormLabel,
16
+ StyledFormDescription,
17
+ StyledFormMessage,
18
+ } from "./Form.styles";
12
19
 
13
- import { cn } from "../../../lib/utils";
14
- import { Label } from "../../atoms/Label/Label";
15
-
20
+ /* Reexporta o FormProvider como Form para facilitar o uso */
16
21
  const Form = FormProvider;
17
22
 
18
23
  type FormFieldContextValue<
@@ -39,6 +44,25 @@ const FormField = <
39
44
  );
40
45
  };
41
46
 
47
+ type FormItemContextValue = { id: string };
48
+
49
+ const FormItemContext = React.createContext<FormItemContextValue>(
50
+ {} as FormItemContextValue
51
+ );
52
+
53
+ const FormItem = React.forwardRef<
54
+ HTMLDivElement,
55
+ React.HTMLAttributes<HTMLDivElement>
56
+ >(({ ...props }, ref) => {
57
+ const id = React.useId();
58
+ return (
59
+ <FormItemContext.Provider value={{ id }}>
60
+ <StyledFormItem ref={ref} {...props} />
61
+ </FormItemContext.Provider>
62
+ );
63
+ });
64
+ FormItem.displayName = "FormItem";
65
+
42
66
  const useFormField = () => {
43
67
  const fieldContext = React.useContext(FormFieldContext);
44
68
  const itemContext = React.useContext(FormItemContext);
@@ -51,7 +75,6 @@ const useFormField = () => {
51
75
  }
52
76
 
53
77
  const { id } = itemContext;
54
-
55
78
  return {
56
79
  id,
57
80
  name: fieldContext.name,
@@ -62,41 +85,15 @@ const useFormField = () => {
62
85
  };
63
86
  };
64
87
 
65
- type FormItemContextValue = {
66
- id: string;
67
- };
68
-
69
- const FormItemContext = React.createContext<FormItemContextValue>(
70
- {} as FormItemContextValue
71
- );
72
-
73
- const FormItem = React.forwardRef<
74
- HTMLDivElement,
75
- React.HTMLAttributes<HTMLDivElement>
76
- >(({ className, ...props }, ref) => {
77
- const id = React.useId();
78
-
79
- return (
80
- <FormItemContext.Provider value={{ id }}>
81
- <div ref={ref} className={cn("space-y-2", className)} {...props} />
82
- </FormItemContext.Provider>
83
- );
84
- });
85
- FormItem.displayName = "FormItem";
86
-
87
88
  const FormLabel = React.forwardRef<
88
- React.ElementRef<typeof LabelPrimitive.Root>,
89
- React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
90
- >(({ className, ...props }, ref) => {
89
+ HTMLLabelElement,
90
+ React.ComponentPropsWithoutRef<"label">
91
+ >(({ children, ...props }, ref) => {
91
92
  const { error, formItemId } = useFormField();
92
-
93
93
  return (
94
- <Label
95
- ref={ref}
96
- className={cn(error && "text-destructive", className)}
97
- htmlFor={formItemId}
98
- {...props}
99
- />
94
+ <StyledFormLabel ref={ref} htmlFor={formItemId} error={!!error} {...props}>
95
+ {children}
96
+ </StyledFormLabel>
100
97
  );
101
98
  });
102
99
  FormLabel.displayName = "FormLabel";
@@ -107,7 +104,6 @@ const FormControl = React.forwardRef<
107
104
  >(({ ...props }, ref) => {
108
105
  const { error, formItemId, formDescriptionId, formMessageId } =
109
106
  useFormField();
110
-
111
107
  return (
112
108
  <Slot
113
109
  ref={ref}
@@ -127,16 +123,12 @@ FormControl.displayName = "FormControl";
127
123
  const FormDescription = React.forwardRef<
128
124
  HTMLParagraphElement,
129
125
  React.HTMLAttributes<HTMLParagraphElement>
130
- >(({ className, ...props }, ref) => {
126
+ >(({ children, ...props }, ref) => {
131
127
  const { formDescriptionId } = useFormField();
132
-
133
128
  return (
134
- <p
135
- ref={ref}
136
- id={formDescriptionId}
137
- className={cn("text-sm text-muted-foreground", className)}
138
- {...props}
139
- />
129
+ <StyledFormDescription ref={ref} id={formDescriptionId} {...props}>
130
+ {children}
131
+ </StyledFormDescription>
140
132
  );
141
133
  });
142
134
  FormDescription.displayName = "FormDescription";
@@ -144,23 +136,14 @@ FormDescription.displayName = "FormDescription";
144
136
  const FormMessage = React.forwardRef<
145
137
  HTMLParagraphElement,
146
138
  React.HTMLAttributes<HTMLParagraphElement>
147
- >(({ className, children, ...props }, ref) => {
139
+ >(({ children, ...props }, ref) => {
148
140
  const { error, formMessageId } = useFormField();
149
141
  const body = error ? String(error?.message) : children;
150
-
151
- if (!body) {
152
- return null;
153
- }
154
-
142
+ if (!body) return null;
155
143
  return (
156
- <p
157
- ref={ref}
158
- id={formMessageId}
159
- className={cn("text-sm font-medium text-destructive", className)}
160
- {...props}
161
- >
144
+ <StyledFormMessage ref={ref} id={formMessageId} {...props}>
162
145
  {body}
163
- </p>
146
+ </StyledFormMessage>
164
147
  );
165
148
  });
166
149
  FormMessage.displayName = "FormMessage";
@@ -3,6 +3,7 @@ export const primitiveRadius = {
3
3
  half: "0.0625rem", // 1px
4
4
  x1: "0.125rem", // 2px
5
5
  x2: "0.25rem", // 4px
6
+ x3: "0.5rem", // 8px
6
7
  x4: "1rem", // 16px
7
8
  x1000: "62.4375rem", // 9999px (Full radius, para bordas arredondadas completas)
8
9
  },
@@ -13,6 +14,7 @@ export const semanticRadius = {
13
14
  radius: {
14
15
  sm: primitiveRadius.radius.x1, // 2px
15
16
  md: primitiveRadius.radius.x2, // 4px
17
+ md2: primitiveRadius.radius.x3, // 8px
16
18
  lg: primitiveRadius.radius.x4, // 16px
17
19
  full: primitiveRadius.radius.x1000, // 9999px para bordas completamente arredondadas
18
20
  },