@rovula/ui 0.0.26 → 0.0.27

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 (67) hide show
  1. package/dist/cjs/bundle.css +185 -37
  2. package/dist/cjs/bundle.js +3 -3
  3. package/dist/cjs/bundle.js.map +1 -1
  4. package/dist/cjs/types/components/AlertDialog/{Alert.stories.d.ts → AlertDialog.stories.d.ts} +4 -0
  5. package/dist/cjs/types/components/Calendar/Calendar.stories.d.ts +7 -7
  6. package/dist/cjs/types/components/RadioGroup/RadioGroup.stories.d.ts +1 -1
  7. package/dist/cjs/types/components/Text/Text.d.ts +5 -6
  8. package/dist/cjs/types/components/Text/Text.stories.d.ts +10 -2
  9. package/dist/cjs/types/components/Toast/Toast.d.ts +17 -0
  10. package/dist/cjs/types/components/Toast/Toast.stories.d.ts +358 -0
  11. package/dist/cjs/types/components/Toast/Toast.styles.d.ts +12 -0
  12. package/dist/cjs/types/components/Toast/Toaster.d.ts +1 -0
  13. package/dist/cjs/types/components/Toast/useToast.d.ts +55 -0
  14. package/dist/cjs/types/components/Tooltip/Tooltip.d.ts +8 -0
  15. package/dist/cjs/types/components/Tooltip/Tooltip.stories.d.ts +59 -0
  16. package/dist/cjs/types/components/Tooltip/TooltipSimple.d.ts +13 -0
  17. package/dist/cjs/types/index.d.ts +5 -0
  18. package/dist/components/AlertDialog/{Alert.stories.js → AlertDialog.stories.js} +13 -1
  19. package/dist/components/Checkbox/Checkbox.js +5 -4
  20. package/dist/components/Popover/Popover.js +1 -1
  21. package/dist/components/RadioGroup/RadioGroup.js +1 -4
  22. package/dist/components/Switch/Switch.js +1 -1
  23. package/dist/components/Text/Text.js +3 -5
  24. package/dist/components/Toast/Toast.js +52 -0
  25. package/dist/components/Toast/Toast.stories.js +82 -0
  26. package/dist/components/Toast/Toast.styles.js +60 -0
  27. package/dist/components/Toast/Toaster.js +24 -0
  28. package/dist/components/Toast/useToast.js +121 -0
  29. package/dist/components/Tooltip/Tooltip.js +26 -0
  30. package/dist/components/Tooltip/Tooltip.stories.js +61 -0
  31. package/dist/components/Tooltip/TooltipSimple.js +18 -0
  32. package/dist/esm/bundle.css +185 -37
  33. package/dist/esm/bundle.js +3 -3
  34. package/dist/esm/bundle.js.map +1 -1
  35. package/dist/esm/types/components/AlertDialog/{Alert.stories.d.ts → AlertDialog.stories.d.ts} +4 -0
  36. package/dist/esm/types/components/Calendar/Calendar.stories.d.ts +7 -7
  37. package/dist/esm/types/components/RadioGroup/RadioGroup.stories.d.ts +1 -1
  38. package/dist/esm/types/components/Text/Text.d.ts +5 -6
  39. package/dist/esm/types/components/Text/Text.stories.d.ts +10 -2
  40. package/dist/esm/types/components/Toast/Toast.d.ts +17 -0
  41. package/dist/esm/types/components/Toast/Toast.stories.d.ts +358 -0
  42. package/dist/esm/types/components/Toast/Toast.styles.d.ts +12 -0
  43. package/dist/esm/types/components/Toast/Toaster.d.ts +1 -0
  44. package/dist/esm/types/components/Toast/useToast.d.ts +55 -0
  45. package/dist/esm/types/components/Tooltip/Tooltip.d.ts +8 -0
  46. package/dist/esm/types/components/Tooltip/Tooltip.stories.d.ts +59 -0
  47. package/dist/esm/types/components/Tooltip/TooltipSimple.d.ts +13 -0
  48. package/dist/esm/types/index.d.ts +5 -0
  49. package/dist/index.d.ts +94 -6
  50. package/dist/index.js +5 -0
  51. package/dist/src/theme/global.css +229 -45
  52. package/package.json +4 -1
  53. package/src/components/AlertDialog/{Alert.stories.tsx → AlertDialog.stories.tsx} +41 -1
  54. package/src/components/Checkbox/Checkbox.tsx +9 -4
  55. package/src/components/Popover/Popover.tsx +1 -1
  56. package/src/components/RadioGroup/RadioGroup.tsx +6 -7
  57. package/src/components/Switch/Switch.tsx +8 -5
  58. package/src/components/Text/Text.tsx +33 -40
  59. package/src/components/Toast/Toast.stories.tsx +144 -0
  60. package/src/components/Toast/Toast.styles.tsx +73 -0
  61. package/src/components/Toast/Toast.tsx +116 -0
  62. package/src/components/Toast/Toaster.tsx +50 -0
  63. package/src/components/Toast/useToast.ts +222 -0
  64. package/src/components/Tooltip/Tooltip.stories.tsx +152 -0
  65. package/src/components/Tooltip/Tooltip.tsx +38 -0
  66. package/src/components/Tooltip/TooltipSimple.tsx +46 -0
  67. package/src/index.ts +5 -0
@@ -0,0 +1,222 @@
1
+ "use client";
2
+
3
+ // Inspired by react-hot-toast library
4
+ import * as React from "react";
5
+ import { ToastActionElement, ToastProps } from "./Toast";
6
+
7
+ const TOAST_LIMIT = 1;
8
+ const TOAST_REMOVE_DELAY = 6000;
9
+
10
+ type ToasterToast = ToastProps & {
11
+ id: string;
12
+ title?: React.ReactNode;
13
+ description?: React.ReactNode;
14
+ action?: ToastActionElement;
15
+ contentMode?: "vertical" | "horizontal";
16
+ };
17
+
18
+ type ToasterPosition =
19
+ | "top-center"
20
+ | "top-left"
21
+ | "top-right"
22
+ | "bottom-center"
23
+ | "bottom-left"
24
+ | "bottom-right"
25
+ | undefined;
26
+
27
+ const actionTypes = {
28
+ ADD_TOAST: "ADD_TOAST",
29
+ UPDATE_TOAST: "UPDATE_TOAST",
30
+ DISMISS_TOAST: "DISMISS_TOAST",
31
+ REMOVE_TOAST: "REMOVE_TOAST",
32
+ ADD_POSITION: "ADD_POSITION",
33
+ } as const;
34
+
35
+ let count = 0;
36
+
37
+ function genId() {
38
+ count = (count + 1) % Number.MAX_SAFE_INTEGER;
39
+ return count.toString();
40
+ }
41
+
42
+ type ActionType = typeof actionTypes;
43
+
44
+ type Action =
45
+ | {
46
+ type: ActionType["ADD_TOAST"];
47
+ toast: ToasterToast;
48
+ }
49
+ | {
50
+ type: ActionType["UPDATE_TOAST"];
51
+ toast: Partial<ToasterToast>;
52
+ }
53
+ | {
54
+ type: ActionType["DISMISS_TOAST"];
55
+ toastId?: ToasterToast["id"];
56
+ }
57
+ | {
58
+ type: ActionType["REMOVE_TOAST"];
59
+ toastId?: ToasterToast["id"];
60
+ }
61
+ | {
62
+ type: ActionType["ADD_POSITION"];
63
+ position?: ToasterPosition;
64
+ };
65
+
66
+ interface State {
67
+ toasts: ToasterToast[];
68
+ position?: ToasterPosition;
69
+ }
70
+
71
+ const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
72
+
73
+ const addToRemoveQueue = (toastId: string) => {
74
+ if (toastTimeouts.has(toastId)) {
75
+ return;
76
+ }
77
+
78
+ const timeout = setTimeout(() => {
79
+ toastTimeouts.delete(toastId);
80
+ dispatch({
81
+ type: "REMOVE_TOAST",
82
+ toastId: toastId,
83
+ });
84
+ }, TOAST_REMOVE_DELAY);
85
+
86
+ toastTimeouts.set(toastId, timeout);
87
+ };
88
+
89
+ export const reducer = (state: State, action: Action): State => {
90
+ switch (action.type) {
91
+ case "ADD_TOAST":
92
+ return {
93
+ ...state,
94
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
95
+ };
96
+
97
+ case "UPDATE_TOAST":
98
+ return {
99
+ ...state,
100
+ toasts: state.toasts.map((t) =>
101
+ t.id === action.toast.id ? { ...t, ...action.toast } : t
102
+ ),
103
+ };
104
+
105
+ case "DISMISS_TOAST": {
106
+ const { toastId } = action;
107
+
108
+ // ! Side effects ! - This could be extracted into a dismissToast() action,
109
+ // but I'll keep it here for simplicity
110
+ if (toastId) {
111
+ addToRemoveQueue(toastId);
112
+ } else {
113
+ state.toasts.forEach((toast) => {
114
+ addToRemoveQueue(toast.id);
115
+ });
116
+ }
117
+
118
+ return {
119
+ ...state,
120
+ toasts: state.toasts.map((t) =>
121
+ t.id === toastId || toastId === undefined
122
+ ? {
123
+ ...t,
124
+ open: false,
125
+ }
126
+ : t
127
+ ),
128
+ };
129
+ }
130
+ case "REMOVE_TOAST":
131
+ if (action.toastId === undefined) {
132
+ return {
133
+ ...state,
134
+ toasts: [],
135
+ };
136
+ }
137
+ return {
138
+ ...state,
139
+ toasts: state.toasts.filter((t) => t.id !== action.toastId),
140
+ };
141
+ case "ADD_POSITION":
142
+ return {
143
+ ...state,
144
+ position: action.position,
145
+ };
146
+ }
147
+ };
148
+
149
+ const listeners: Array<(state: State) => void> = [];
150
+
151
+ let memoryState: State = { toasts: [], position: undefined };
152
+
153
+ function dispatch(action: Action) {
154
+ memoryState = reducer(memoryState, action);
155
+ listeners.forEach((listener) => {
156
+ listener(memoryState);
157
+ });
158
+ }
159
+
160
+ type Toast = Omit<ToasterToast, "id">;
161
+
162
+ function toast({ ...props }: Toast) {
163
+ const id = genId();
164
+
165
+ const update = (props: ToasterToast) =>
166
+ dispatch({
167
+ type: "UPDATE_TOAST",
168
+ toast: { ...props, id },
169
+ });
170
+ const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
171
+
172
+ dispatch({
173
+ type: "ADD_TOAST",
174
+ toast: {
175
+ ...props,
176
+ id,
177
+ open: true,
178
+ onOpenChange: (open) => {
179
+ if (!open) dismiss();
180
+ },
181
+ },
182
+ });
183
+
184
+ return {
185
+ id: id,
186
+ dismiss,
187
+ update,
188
+ };
189
+ }
190
+
191
+ type ToastOptions = {
192
+ position?: State["position"];
193
+ };
194
+
195
+ function useToast(options?: ToastOptions) {
196
+ const [state, setState] = React.useState<State>(memoryState);
197
+
198
+ React.useEffect(() => {
199
+ listeners.push(setState);
200
+ return () => {
201
+ const index = listeners.indexOf(setState);
202
+ if (index > -1) {
203
+ listeners.splice(index, 1);
204
+ }
205
+ };
206
+ }, [state]);
207
+
208
+ React.useEffect(() => {
209
+ dispatch({
210
+ type: "ADD_POSITION",
211
+ position: options?.position,
212
+ });
213
+ }, [options?.position]);
214
+
215
+ return {
216
+ ...state,
217
+ toast,
218
+ dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
219
+ };
220
+ }
221
+
222
+ export { useToast, toast };
@@ -0,0 +1,152 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import { useArgs } from "@storybook/preview-api";
4
+ import {
5
+ Tooltip,
6
+ TooltipArrow,
7
+ TooltipContent,
8
+ TooltipProvider,
9
+ TooltipTrigger,
10
+ } from "./Tooltip";
11
+ import Button from "../Button/Button";
12
+ import { TooltipSimple } from "./TooltipSimple";
13
+
14
+ const meta = {
15
+ title: "Components/Tooltip",
16
+ // component: TooltipProvider,
17
+ tags: ["react-docgen-typescript"],
18
+ parameters: {
19
+ layout: "fullscreen",
20
+ },
21
+ decorators: [
22
+ (Story) => (
23
+ <div className="p-5 flex w-full">
24
+ <Story />
25
+ </div>
26
+ ),
27
+ ],
28
+ } satisfies Meta<typeof Tooltip>;
29
+
30
+ export default meta;
31
+
32
+ export const Default = {
33
+ args: {
34
+ delayDuration: 700,
35
+ skipDelayDuration: 300,
36
+ disableHoverableContent: false,
37
+ },
38
+ render: (args: any) => {
39
+ console.log("args ", args);
40
+ const props: typeof args = {
41
+ ...args,
42
+ };
43
+ return (
44
+ <div className="flex flex-row gap-4 w-full">
45
+ <div className="flex flex-1 justify-center items-center space-x-2">
46
+ <TooltipProvider
47
+ delayDuration={props.delayDuration}
48
+ skipDelayDuration={props.skipDelayDuration}
49
+ disableHoverableContent={props.disableHoverableContent}
50
+ >
51
+ <Tooltip>
52
+ <TooltipTrigger>Hover</TooltipTrigger>
53
+ <TooltipContent>
54
+ <p>Add to library</p>
55
+ <TooltipArrow className="m-0" />
56
+ </TooltipContent>
57
+ </Tooltip>
58
+ </TooltipProvider>
59
+ </div>
60
+ </div>
61
+ );
62
+ },
63
+ } satisfies StoryObj;
64
+
65
+ export const Position = {
66
+ argTypes: {
67
+ side: { control: "select", options: ["top", "right", "bottom", "left"] },
68
+ align: { control: "select", options: ["start", "center", "end"] },
69
+ },
70
+ args: {
71
+ side: "top",
72
+ align: "center",
73
+ sideOffset: 4,
74
+ },
75
+ render: function Render(args) {
76
+ return (
77
+ <div className="flex flex-row gap-4 h-full w-full">
78
+ <div className="flex flex-1 h-60 justify-center items-center space-x-2">
79
+ <TooltipProvider>
80
+ <Tooltip open>
81
+ <TooltipTrigger>Hover</TooltipTrigger>
82
+ <TooltipContent
83
+ side={args.side}
84
+ align={args.align}
85
+ sideOffset={args.sideOffset}
86
+ >
87
+ <p>Add to library</p>
88
+ <TooltipArrow className="m-0" />
89
+ </TooltipContent>
90
+ </Tooltip>
91
+ </TooltipProvider>
92
+ </div>
93
+ </div>
94
+ );
95
+ },
96
+ } satisfies StoryObj<{
97
+ side: "top" | "right" | "bottom" | "left";
98
+ align: "start" | "center" | "end";
99
+ sideOffset: number;
100
+ }>;
101
+
102
+ export const Controlled = {
103
+ args: {
104
+ open: false,
105
+ },
106
+ render: function Render(args) {
107
+ const [{ open }, updateArgs] = useArgs();
108
+
109
+ return (
110
+ <div className="flex flex-row gap-4 w-full">
111
+ <div className="flex flex-1 justify-center items-center space-x-2">
112
+ <TooltipProvider>
113
+ <Tooltip defaultOpen open={open}>
114
+ <TooltipTrigger asChild>
115
+ <Button
116
+ variant="outline"
117
+ onMouseOver={() => updateArgs({ open: true })}
118
+ onMouseLeave={() => updateArgs({ open: false })}
119
+ >
120
+ Hover
121
+ </Button>
122
+ </TooltipTrigger>
123
+ <TooltipContent>
124
+ <p>Add to library</p>
125
+ <TooltipArrow className="m-0" />
126
+ </TooltipContent>
127
+ </Tooltip>
128
+ </TooltipProvider>
129
+ </div>
130
+ </div>
131
+ );
132
+ },
133
+ } satisfies StoryObj;
134
+
135
+ export const Simple = {
136
+ args: {
137
+ open: false,
138
+ },
139
+ render: function Render(args) {
140
+ const [{ open }, updateArgs] = useArgs();
141
+
142
+ return (
143
+ <div className="flex flex-row gap-4 w-full">
144
+ <div className="flex flex-1 justify-center items-center space-x-2">
145
+ <TooltipSimple content="Tooltip content">
146
+ <button>Tooltip trigger</button>
147
+ </TooltipSimple>
148
+ </div>
149
+ </div>
150
+ );
151
+ },
152
+ } satisfies StoryObj;
@@ -0,0 +1,38 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
5
+
6
+ import { cn } from "@/utils/cn";
7
+
8
+ const TooltipProvider = TooltipPrimitive.Provider;
9
+
10
+ const Tooltip = TooltipPrimitive.Root;
11
+
12
+ const TooltipTrigger = TooltipPrimitive.Trigger;
13
+
14
+ const TooltipArrow = TooltipPrimitive.Arrow;
15
+
16
+ const TooltipContent = React.forwardRef<
17
+ React.ElementRef<typeof TooltipPrimitive.Content>,
18
+ React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
19
+ >(({ className, sideOffset = 4, ...props }, ref) => (
20
+ <TooltipPrimitive.Content
21
+ ref={ref}
22
+ sideOffset={sideOffset}
23
+ className={cn(
24
+ "z-50 overflow-hidden rounded-md bg-common-black px-3 py-1.5 typography-buttonMS text-common-white shadow-md 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",
25
+ className
26
+ )}
27
+ {...props}
28
+ />
29
+ ));
30
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
31
+
32
+ export {
33
+ Tooltip,
34
+ TooltipTrigger,
35
+ TooltipContent,
36
+ TooltipProvider,
37
+ TooltipArrow,
38
+ };
@@ -0,0 +1,46 @@
1
+ "use client";
2
+
3
+ import React, { ReactNode } from "react";
4
+ import {
5
+ Tooltip,
6
+ TooltipTrigger,
7
+ TooltipContent,
8
+ TooltipArrow,
9
+ TooltipProvider,
10
+ } from "./Tooltip";
11
+
12
+ interface TooltipProps {
13
+ children: ReactNode;
14
+ content: ReactNode;
15
+ open?: boolean;
16
+ defaultOpen?: boolean;
17
+ onOpenChange?: (open: boolean) => void;
18
+ side?: "top" | "bottom" | "left" | "right";
19
+ align?: "start" | "center" | "end";
20
+ className?: string;
21
+ }
22
+
23
+ export function TooltipSimple({
24
+ children,
25
+ content,
26
+ open,
27
+ defaultOpen,
28
+ onOpenChange,
29
+ ...props
30
+ }: TooltipProps) {
31
+ return (
32
+ <TooltipProvider>
33
+ <Tooltip
34
+ open={open}
35
+ defaultOpen={defaultOpen}
36
+ onOpenChange={onOpenChange}
37
+ >
38
+ <TooltipTrigger asChild>{children}</TooltipTrigger>
39
+ <TooltipContent side="top" align="center" {...props}>
40
+ {content}
41
+ <TooltipArrow width={11} height={5} />
42
+ </TooltipContent>
43
+ </Tooltip>
44
+ </TooltipProvider>
45
+ );
46
+ }
package/src/index.ts CHANGED
@@ -33,6 +33,11 @@ export * from "./components/Search/Search";
33
33
  export * from "./components/Slider/Slider";
34
34
  export * from "./components/Switch/Switch";
35
35
  export * from "./components/DropdownMenu/DropdownMenu";
36
+ export * from "./components/Tooltip/Tooltip";
37
+ export * from "./components/Tooltip/TooltipSimple";
38
+ export * from "./components/Toast/Toast";
39
+ export * from "./components/Toast/Toaster";
40
+ export * from "./components/Toast/useToast";
36
41
 
37
42
  // Export component types
38
43
  export type { ButtonProps } from "./components/Button/Button";