@oppulence/design-system 1.0.2

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 (80) hide show
  1. package/README.md +115 -0
  2. package/components.json +21 -0
  3. package/hooks/use-mobile.tsx +21 -0
  4. package/lib/utils.ts +6 -0
  5. package/package.json +104 -0
  6. package/postcss.config.mjs +8 -0
  7. package/src/components/atoms/aspect-ratio.tsx +21 -0
  8. package/src/components/atoms/avatar.tsx +91 -0
  9. package/src/components/atoms/badge.tsx +47 -0
  10. package/src/components/atoms/button.tsx +128 -0
  11. package/src/components/atoms/checkbox.tsx +24 -0
  12. package/src/components/atoms/container.tsx +42 -0
  13. package/src/components/atoms/heading.tsx +56 -0
  14. package/src/components/atoms/index.ts +21 -0
  15. package/src/components/atoms/input.tsx +18 -0
  16. package/src/components/atoms/kbd.tsx +23 -0
  17. package/src/components/atoms/label.tsx +15 -0
  18. package/src/components/atoms/logo.tsx +52 -0
  19. package/src/components/atoms/progress.tsx +79 -0
  20. package/src/components/atoms/separator.tsx +17 -0
  21. package/src/components/atoms/skeleton.tsx +13 -0
  22. package/src/components/atoms/slider.tsx +56 -0
  23. package/src/components/atoms/spinner.tsx +14 -0
  24. package/src/components/atoms/stack.tsx +126 -0
  25. package/src/components/atoms/switch.tsx +26 -0
  26. package/src/components/atoms/text.tsx +69 -0
  27. package/src/components/atoms/textarea.tsx +19 -0
  28. package/src/components/atoms/toggle.tsx +40 -0
  29. package/src/components/molecules/accordion.tsx +72 -0
  30. package/src/components/molecules/ai-chat.tsx +251 -0
  31. package/src/components/molecules/alert.tsx +131 -0
  32. package/src/components/molecules/breadcrumb.tsx +301 -0
  33. package/src/components/molecules/button-group.tsx +96 -0
  34. package/src/components/molecules/card.tsx +184 -0
  35. package/src/components/molecules/collapsible.tsx +21 -0
  36. package/src/components/molecules/command-search.tsx +148 -0
  37. package/src/components/molecules/empty.tsx +98 -0
  38. package/src/components/molecules/field.tsx +217 -0
  39. package/src/components/molecules/grid.tsx +141 -0
  40. package/src/components/molecules/hover-card.tsx +45 -0
  41. package/src/components/molecules/index.ts +29 -0
  42. package/src/components/molecules/input-group.tsx +151 -0
  43. package/src/components/molecules/input-otp.tsx +74 -0
  44. package/src/components/molecules/item.tsx +194 -0
  45. package/src/components/molecules/page-header.tsx +89 -0
  46. package/src/components/molecules/pagination.tsx +130 -0
  47. package/src/components/molecules/popover.tsx +96 -0
  48. package/src/components/molecules/radio-group.tsx +37 -0
  49. package/src/components/molecules/resizable.tsx +52 -0
  50. package/src/components/molecules/scroll-area.tsx +45 -0
  51. package/src/components/molecules/section.tsx +108 -0
  52. package/src/components/molecules/select.tsx +201 -0
  53. package/src/components/molecules/settings.tsx +197 -0
  54. package/src/components/molecules/table.tsx +111 -0
  55. package/src/components/molecules/tabs.tsx +74 -0
  56. package/src/components/molecules/theme-switcher.tsx +187 -0
  57. package/src/components/molecules/toggle-group.tsx +89 -0
  58. package/src/components/molecules/tooltip.tsx +66 -0
  59. package/src/components/organisms/alert-dialog.tsx +152 -0
  60. package/src/components/organisms/app-shell.tsx +939 -0
  61. package/src/components/organisms/calendar.tsx +212 -0
  62. package/src/components/organisms/carousel.tsx +230 -0
  63. package/src/components/organisms/chart.tsx +333 -0
  64. package/src/components/organisms/combobox.tsx +274 -0
  65. package/src/components/organisms/command.tsx +200 -0
  66. package/src/components/organisms/context-menu.tsx +229 -0
  67. package/src/components/organisms/dialog.tsx +134 -0
  68. package/src/components/organisms/drawer.tsx +123 -0
  69. package/src/components/organisms/dropdown-menu.tsx +256 -0
  70. package/src/components/organisms/index.ts +17 -0
  71. package/src/components/organisms/menubar.tsx +203 -0
  72. package/src/components/organisms/navigation-menu.tsx +143 -0
  73. package/src/components/organisms/page-layout.tsx +105 -0
  74. package/src/components/organisms/sheet.tsx +126 -0
  75. package/src/components/organisms/sidebar.tsx +723 -0
  76. package/src/components/organisms/sonner.tsx +41 -0
  77. package/src/components/ui/index.ts +3 -0
  78. package/src/index.ts +3 -0
  79. package/src/styles/globals.css +297 -0
  80. package/tailwind.config.ts +77 -0
@@ -0,0 +1,212 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import {
5
+ DayPicker,
6
+ getDefaultClassNames,
7
+ type DayButton,
8
+ type DayPickerProps,
9
+ } from "react-day-picker";
10
+
11
+ import {
12
+ ChevronDownIcon,
13
+ ChevronLeftIcon,
14
+ ChevronRightIcon,
15
+ } from "lucide-react";
16
+ import { cn } from "../../../lib/utils";
17
+ import { Button, buttonVariants } from "../atoms/button";
18
+
19
+ type CalendarButtonVariant =
20
+ | "default"
21
+ | "outline"
22
+ | "secondary"
23
+ | "ghost"
24
+ | "destructive"
25
+ | "link";
26
+
27
+ interface CalendarBaseProps {
28
+ buttonVariant?: CalendarButtonVariant;
29
+ }
30
+
31
+ // Calendar accepts all DayPickerProps except className/classNames (we control those internally)
32
+ type CalendarProps = CalendarBaseProps & DayPickerProps;
33
+
34
+ function Calendar({
35
+ showOutsideDays = true,
36
+ captionLayout = "label",
37
+ buttonVariant = "ghost",
38
+ formatters,
39
+ components,
40
+ className: _className,
41
+ classNames: _classNames,
42
+ ...props
43
+ }: CalendarProps) {
44
+ const defaultClassNames = getDefaultClassNames();
45
+
46
+ return (
47
+ <DayPicker
48
+ showOutsideDays={showOutsideDays}
49
+ className={cn(
50
+ "p-3 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(8)] bg-background group/calendar [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
51
+ String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
52
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
53
+ )}
54
+ captionLayout={captionLayout}
55
+ formatters={{
56
+ formatMonthDropdown: (date) =>
57
+ date.toLocaleString("default", { month: "short" }),
58
+ ...formatters,
59
+ }}
60
+ classNames={{
61
+ root: cn("w-fit", defaultClassNames.root),
62
+ months: cn(
63
+ "flex gap-4 flex-col md:flex-row relative",
64
+ defaultClassNames.months,
65
+ ),
66
+ month: cn("flex flex-col w-full gap-4", defaultClassNames.month),
67
+ nav: cn(
68
+ "flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
69
+ defaultClassNames.nav,
70
+ ),
71
+ button_previous: cn(
72
+ buttonVariants({ variant: buttonVariant }),
73
+ "size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
74
+ defaultClassNames.button_previous,
75
+ ),
76
+ button_next: cn(
77
+ buttonVariants({ variant: buttonVariant }),
78
+ "size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
79
+ defaultClassNames.button_next,
80
+ ),
81
+ month_caption: cn(
82
+ "flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)",
83
+ defaultClassNames.month_caption,
84
+ ),
85
+ dropdowns: cn(
86
+ "w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5",
87
+ defaultClassNames.dropdowns,
88
+ ),
89
+ dropdown_root: cn(
90
+ "relative cn-calendar-dropdown-root rounded-(--cell-radius)",
91
+ defaultClassNames.dropdown_root,
92
+ ),
93
+ dropdown: cn(
94
+ "absolute bg-popover inset-0 opacity-0",
95
+ defaultClassNames.dropdown,
96
+ ),
97
+ caption_label: cn(
98
+ "select-none font-medium",
99
+ captionLayout === "label"
100
+ ? "text-sm"
101
+ : "cn-calendar-caption-label rounded-(--cell-radius) flex items-center gap-1 text-sm [&>svg]:text-muted-foreground [&>svg]:size-3.5",
102
+ defaultClassNames.caption_label,
103
+ ),
104
+ table: "w-full border-collapse",
105
+ weekdays: cn("flex", defaultClassNames.weekdays),
106
+ weekday: cn(
107
+ "text-muted-foreground rounded-(--cell-radius) flex-1 font-normal text-[0.8rem] select-none",
108
+ defaultClassNames.weekday,
109
+ ),
110
+ week: cn("flex w-full mt-2", defaultClassNames.week),
111
+ week_number_header: cn(
112
+ "select-none w-(--cell-size)",
113
+ defaultClassNames.week_number_header,
114
+ ),
115
+ week_number: cn(
116
+ "text-[0.8rem] select-none text-muted-foreground",
117
+ defaultClassNames.week_number,
118
+ ),
119
+ day: cn(
120
+ "relative w-full rounded-(--cell-radius) h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius) group/day aspect-square select-none",
121
+ props.showWeekNumber
122
+ ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)"
123
+ : "[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)",
124
+ defaultClassNames.day,
125
+ ),
126
+ range_start: cn(
127
+ "rounded-l-(--cell-radius) bg-muted relative after:bg-muted after:absolute after:inset-y-0 after:w-4 after:right-0 -z-0 isolate",
128
+ defaultClassNames.range_start,
129
+ ),
130
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
131
+ range_end: cn(
132
+ "rounded-r-(--cell-radius) bg-muted relative after:bg-muted after:absolute after:inset-y-0 after:w-4 after:left-0 -z-0 isolate",
133
+ defaultClassNames.range_end,
134
+ ),
135
+ today: cn(
136
+ "bg-muted text-foreground rounded-(--cell-radius) data-[selected=true]:rounded-none",
137
+ defaultClassNames.today,
138
+ ),
139
+ outside: cn(
140
+ "text-muted-foreground aria-selected:text-muted-foreground",
141
+ defaultClassNames.outside,
142
+ ),
143
+ disabled: cn(
144
+ "text-muted-foreground opacity-50",
145
+ defaultClassNames.disabled,
146
+ ),
147
+ hidden: cn("invisible", defaultClassNames.hidden),
148
+ }}
149
+ components={{
150
+ Root: ({ rootRef, ...componentProps }) => {
151
+ return <div data-slot="calendar" ref={rootRef} {...componentProps} />;
152
+ },
153
+ Chevron: ({ orientation, ...componentProps }) => {
154
+ if (orientation === "left") {
155
+ return <ChevronLeftIcon className="size-4" {...componentProps} />;
156
+ }
157
+
158
+ if (orientation === "right") {
159
+ return <ChevronRightIcon className="size-4" {...componentProps} />;
160
+ }
161
+
162
+ return <ChevronDownIcon className="size-4" {...componentProps} />;
163
+ },
164
+ DayButton: CalendarDayButton,
165
+ WeekNumber: ({ children, ...componentProps }) => {
166
+ return (
167
+ <td {...componentProps}>
168
+ <div className="flex size-(--cell-size) items-center justify-center text-center">
169
+ {children}
170
+ </div>
171
+ </td>
172
+ );
173
+ },
174
+ ...components,
175
+ }}
176
+ {...props}
177
+ />
178
+ );
179
+ }
180
+
181
+ function CalendarDayButton({
182
+ day,
183
+ modifiers,
184
+ ...props
185
+ }: Omit<React.ComponentProps<typeof DayButton>, "className">) {
186
+ const ref = React.useRef<HTMLButtonElement>(null);
187
+ React.useEffect(() => {
188
+ if (modifiers.focused) ref.current?.focus();
189
+ }, [modifiers.focused]);
190
+
191
+ return (
192
+ <Button
193
+ ref={ref}
194
+ variant="ghost"
195
+ size="calendar-day"
196
+ data-day={day.date.toLocaleDateString()}
197
+ data-selected-single={
198
+ modifiers.selected &&
199
+ !modifiers.range_start &&
200
+ !modifiers.range_end &&
201
+ !modifiers.range_middle
202
+ }
203
+ data-range-start={modifiers.range_start}
204
+ data-range-end={modifiers.range_end}
205
+ data-range-middle={modifiers.range_middle}
206
+ {...props}
207
+ />
208
+ );
209
+ }
210
+
211
+ export { Calendar, CalendarDayButton };
212
+ export type { CalendarProps };
@@ -0,0 +1,230 @@
1
+ import useEmblaCarousel, {
2
+ type UseEmblaCarouselType,
3
+ } from "embla-carousel-react";
4
+ import * as React from "react";
5
+
6
+ import { Button as ButtonPrimitive } from "@base-ui/react/button";
7
+ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
8
+ import { cn } from "../../../lib/utils";
9
+ import { buttonVariants } from "../atoms/button";
10
+
11
+ type CarouselApi = UseEmblaCarouselType[1];
12
+ type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
13
+ type CarouselOptions = UseCarouselParameters[0];
14
+ type CarouselPlugin = UseCarouselParameters[1];
15
+
16
+ type CarouselProps = {
17
+ opts?: CarouselOptions;
18
+ plugins?: CarouselPlugin;
19
+ orientation?: "horizontal" | "vertical";
20
+ setApi?: (api: CarouselApi) => void;
21
+ };
22
+
23
+ type CarouselContextProps = {
24
+ carouselRef: ReturnType<typeof useEmblaCarousel>[0];
25
+ api: ReturnType<typeof useEmblaCarousel>[1];
26
+ scrollPrev: () => void;
27
+ scrollNext: () => void;
28
+ canScrollPrev: boolean;
29
+ canScrollNext: boolean;
30
+ } & CarouselProps;
31
+
32
+ const CarouselContext = React.createContext<CarouselContextProps | null>(null);
33
+
34
+ function useCarousel() {
35
+ const context = React.useContext(CarouselContext);
36
+
37
+ if (!context) {
38
+ throw new Error("useCarousel must be used within a <Carousel />");
39
+ }
40
+
41
+ return context;
42
+ }
43
+
44
+ function Carousel({
45
+ orientation = "horizontal",
46
+ opts,
47
+ setApi,
48
+ plugins,
49
+ children,
50
+ ...props
51
+ }: Omit<React.ComponentProps<"div">, "className"> & CarouselProps) {
52
+ const [carouselRef, api] = useEmblaCarousel(
53
+ {
54
+ ...opts,
55
+ axis: orientation === "horizontal" ? "x" : "y",
56
+ },
57
+ plugins,
58
+ );
59
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false);
60
+ const [canScrollNext, setCanScrollNext] = React.useState(false);
61
+
62
+ const onSelect = React.useCallback((api: CarouselApi) => {
63
+ if (!api) return;
64
+ setCanScrollPrev(api.canScrollPrev());
65
+ setCanScrollNext(api.canScrollNext());
66
+ }, []);
67
+
68
+ const scrollPrev = React.useCallback(() => {
69
+ api?.scrollPrev();
70
+ }, [api]);
71
+
72
+ const scrollNext = React.useCallback(() => {
73
+ api?.scrollNext();
74
+ }, [api]);
75
+
76
+ const handleKeyDown = React.useCallback(
77
+ (event: React.KeyboardEvent<HTMLDivElement>) => {
78
+ if (event.key === "ArrowLeft") {
79
+ event.preventDefault();
80
+ scrollPrev();
81
+ } else if (event.key === "ArrowRight") {
82
+ event.preventDefault();
83
+ scrollNext();
84
+ }
85
+ },
86
+ [scrollPrev, scrollNext],
87
+ );
88
+
89
+ React.useEffect(() => {
90
+ if (!api || !setApi) return;
91
+ setApi(api);
92
+ }, [api, setApi]);
93
+
94
+ React.useEffect(() => {
95
+ if (!api) return;
96
+ onSelect(api);
97
+ api.on("reInit", onSelect);
98
+ api.on("select", onSelect);
99
+
100
+ return () => {
101
+ api?.off("select", onSelect);
102
+ };
103
+ }, [api, onSelect]);
104
+
105
+ return (
106
+ <CarouselContext.Provider
107
+ value={{
108
+ carouselRef,
109
+ api: api,
110
+ opts,
111
+ orientation:
112
+ orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
113
+ scrollPrev,
114
+ scrollNext,
115
+ canScrollPrev,
116
+ canScrollNext,
117
+ }}
118
+ >
119
+ <div
120
+ onKeyDownCapture={handleKeyDown}
121
+ className="relative"
122
+ role="region"
123
+ aria-roledescription="carousel"
124
+ data-slot="carousel"
125
+ {...props}
126
+ >
127
+ {children}
128
+ </div>
129
+ </CarouselContext.Provider>
130
+ );
131
+ }
132
+
133
+ function CarouselContent({
134
+ ...props
135
+ }: Omit<React.ComponentProps<"div">, "className">) {
136
+ const { carouselRef, orientation } = useCarousel();
137
+
138
+ return (
139
+ <div
140
+ ref={carouselRef}
141
+ className="overflow-hidden"
142
+ data-slot="carousel-content"
143
+ >
144
+ <div
145
+ className={cn(
146
+ "flex",
147
+ orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
148
+ )}
149
+ {...props}
150
+ />
151
+ </div>
152
+ );
153
+ }
154
+
155
+ function CarouselItem({
156
+ ...props
157
+ }: Omit<React.ComponentProps<"div">, "className">) {
158
+ const { orientation } = useCarousel();
159
+
160
+ return (
161
+ <div
162
+ role="group"
163
+ aria-roledescription="slide"
164
+ data-slot="carousel-item"
165
+ className={cn(
166
+ "min-w-0 shrink-0 grow-0 basis-full",
167
+ orientation === "horizontal" ? "pl-4" : "pt-4",
168
+ )}
169
+ {...props}
170
+ />
171
+ );
172
+ }
173
+
174
+ function CarouselPrevious({
175
+ ...props
176
+ }: Omit<ButtonPrimitive.Props, "className">) {
177
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel();
178
+
179
+ return (
180
+ <ButtonPrimitive
181
+ data-slot="carousel-previous"
182
+ className={cn(
183
+ buttonVariants({ variant: "outline", size: "icon-sm" }),
184
+ "rounded-full absolute touch-manipulation",
185
+ orientation === "horizontal"
186
+ ? "top-1/2 -left-12 -translate-y-1/2"
187
+ : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
188
+ )}
189
+ disabled={!canScrollPrev}
190
+ onClick={scrollPrev}
191
+ {...props}
192
+ >
193
+ <ChevronLeftIcon />
194
+ <span className="sr-only">Previous slide</span>
195
+ </ButtonPrimitive>
196
+ );
197
+ }
198
+
199
+ function CarouselNext({ ...props }: Omit<ButtonPrimitive.Props, "className">) {
200
+ const { orientation, scrollNext, canScrollNext } = useCarousel();
201
+
202
+ return (
203
+ <ButtonPrimitive
204
+ data-slot="carousel-next"
205
+ className={cn(
206
+ buttonVariants({ variant: "outline", size: "icon-sm" }),
207
+ "rounded-full absolute touch-manipulation",
208
+ orientation === "horizontal"
209
+ ? "top-1/2 -right-12 -translate-y-1/2"
210
+ : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
211
+ )}
212
+ disabled={!canScrollNext}
213
+ onClick={scrollNext}
214
+ {...props}
215
+ >
216
+ <ChevronRightIcon />
217
+ <span className="sr-only">Next slide</span>
218
+ </ButtonPrimitive>
219
+ );
220
+ }
221
+
222
+ export {
223
+ Carousel,
224
+ CarouselContent,
225
+ CarouselItem,
226
+ CarouselNext,
227
+ CarouselPrevious,
228
+ useCarousel,
229
+ type CarouselApi,
230
+ };