@rovula/ui 0.0.10 → 0.0.12

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 (59) hide show
  1. package/dist/cjs/bundle.css +66 -2
  2. package/dist/cjs/bundle.js +23 -1
  3. package/dist/cjs/bundle.js.map +1 -1
  4. package/dist/cjs/types/components/Button/Button.d.ts +14 -3
  5. package/dist/cjs/types/components/Button/Buttons.stories.d.ts +8 -6
  6. package/dist/cjs/types/components/DataTable/DataTable.d.ts +9 -0
  7. package/dist/cjs/types/components/DataTable/DataTable.stories.d.ts +22 -0
  8. package/dist/cjs/types/components/Dropdown/Dropdown.d.ts +29 -3
  9. package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +31 -30
  10. package/dist/cjs/types/components/Label/Label.stories.d.ts +1 -1
  11. package/dist/cjs/types/components/RadioGroup/RadioGroup.stories.d.ts +1 -1
  12. package/dist/cjs/types/components/Table/Table.d.ts +3 -1
  13. package/dist/cjs/types/components/Table/Table.stories.d.ts +4 -1
  14. package/dist/cjs/types/components/Text/Text.d.ts +3 -3
  15. package/dist/cjs/types/components/Text/Text.stories.d.ts +3 -9
  16. package/dist/cjs/types/components/TextInput/TextInput.d.ts +20 -2
  17. package/dist/cjs/types/components/TextInput/TextInput.stories.d.ts +28 -1
  18. package/dist/cjs/types/index.d.ts +4 -0
  19. package/dist/components/Button/Button.js +4 -3
  20. package/dist/components/DataTable/DataTable.js +71 -0
  21. package/dist/components/DataTable/DataTable.stories.js +57 -0
  22. package/dist/components/Dropdown/Dropdown.js +15 -5
  23. package/dist/components/Dropdown/Dropdown.stories.js +48 -0
  24. package/dist/components/Table/Table.js +6 -6
  25. package/dist/components/Text/Text.js +3 -2
  26. package/dist/components/TextInput/TextInput.js +5 -7
  27. package/dist/components/TextInput/TextInput.stories.js +22 -0
  28. package/dist/esm/bundle.css +66 -2
  29. package/dist/esm/bundle.js +23 -1
  30. package/dist/esm/bundle.js.map +1 -1
  31. package/dist/esm/types/components/Button/Button.d.ts +14 -3
  32. package/dist/esm/types/components/Button/Buttons.stories.d.ts +8 -6
  33. package/dist/esm/types/components/DataTable/DataTable.d.ts +9 -0
  34. package/dist/esm/types/components/DataTable/DataTable.stories.d.ts +22 -0
  35. package/dist/esm/types/components/Dropdown/Dropdown.d.ts +29 -3
  36. package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +31 -30
  37. package/dist/esm/types/components/Label/Label.stories.d.ts +1 -1
  38. package/dist/esm/types/components/RadioGroup/RadioGroup.stories.d.ts +1 -1
  39. package/dist/esm/types/components/Table/Table.d.ts +3 -1
  40. package/dist/esm/types/components/Table/Table.stories.d.ts +4 -1
  41. package/dist/esm/types/components/Text/Text.d.ts +3 -3
  42. package/dist/esm/types/components/Text/Text.stories.d.ts +3 -9
  43. package/dist/esm/types/components/TextInput/TextInput.d.ts +20 -2
  44. package/dist/esm/types/components/TextInput/TextInput.stories.d.ts +28 -1
  45. package/dist/esm/types/index.d.ts +4 -0
  46. package/dist/index.d.ts +74 -8
  47. package/dist/index.js +1 -0
  48. package/dist/src/theme/global.css +85 -2
  49. package/package.json +3 -1
  50. package/src/components/Button/Button.tsx +47 -39
  51. package/src/components/DataTable/DataTable.stories.tsx +72 -0
  52. package/src/components/DataTable/DataTable.tsx +171 -0
  53. package/src/components/Dropdown/Dropdown.stories.tsx +87 -3
  54. package/src/components/Dropdown/Dropdown.tsx +147 -109
  55. package/src/components/Table/Table.tsx +17 -8
  56. package/src/components/Text/Text.tsx +21 -19
  57. package/src/components/TextInput/TextInput.stories.tsx +46 -1
  58. package/src/components/TextInput/TextInput.tsx +7 -7
  59. package/src/index.ts +6 -0
@@ -1,4 +1,12 @@
1
- import React, { useCallback, useEffect, useMemo, useState } from "react";
1
+ import React, {
2
+ Fragment,
3
+ ReactNode,
4
+ forwardRef,
5
+ useCallback,
6
+ useEffect,
7
+ useMemo,
8
+ useState,
9
+ } from "react";
2
10
 
3
11
  import TextInput, { InputProps } from "../TextInput/TextInput";
4
12
  import {
@@ -9,12 +17,20 @@ import {
9
17
 
10
18
  import { ChevronDownIcon } from "@heroicons/react/16/solid";
11
19
 
12
- type Options = {
20
+ type RenderLabelCallbackArg = {
13
21
  value: string;
14
22
  label: string;
23
+ handleOnClick: () => void;
24
+ className: string;
15
25
  };
16
26
 
17
- type DropdownProps = {
27
+ export type Options = {
28
+ value: string;
29
+ label: string;
30
+ renderLabel?: (config: RenderLabelCallbackArg) => ReactNode;
31
+ };
32
+
33
+ export type DropdownProps = {
18
34
  id?: string;
19
35
  label?: string;
20
36
  size?: "sm" | "md" | "lg";
@@ -34,118 +50,140 @@ type DropdownProps = {
34
50
  onSelect?: (value: Options) => void;
35
51
  } & Omit<InputProps, "value">;
36
52
 
37
- const Dropdown = ({
38
- id,
39
- options,
40
- value,
41
- label,
42
- size = "md",
43
- rounded = "normal",
44
- variant = "outline",
45
- helperText,
46
- errorMessage,
47
- fullwidth = true,
48
- disabled = false,
49
- error = false,
50
- filterMode = false,
51
- required = true,
52
- onChangeText,
53
- onSelect,
54
- ...props
55
- }: DropdownProps) => {
56
- const _id = id || `${label}-select`;
57
-
58
- const [isFocused, setIsFocused] = useState(false);
59
- const [selectedOption, setSelectedOption] = useState<
60
- Options | null | undefined
61
- >(null);
62
- const [textValue, setTextValue] = useState("");
53
+ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
54
+ (
55
+ {
56
+ id,
57
+ options,
58
+ value,
59
+ label,
60
+ size = "md",
61
+ rounded = "normal",
62
+ variant = "outline",
63
+ helperText,
64
+ errorMessage,
65
+ fullwidth = true,
66
+ disabled = false,
67
+ error = false,
68
+ filterMode = false,
69
+ required = true,
70
+ onChangeText,
71
+ onSelect,
72
+ ...props
73
+ },
74
+ ref
75
+ ) => {
76
+ const _id = id || `${label}-select`;
63
77
 
64
- useEffect(() => {
65
- if (value && !selectedOption) {
66
- setSelectedOption(value);
67
- }
68
- }, [value, selectedOption]);
78
+ const [isFocused, setIsFocused] = useState(false);
79
+ const [selectedOption, setSelectedOption] = useState<
80
+ Options | null | undefined
81
+ >(null);
82
+ const [textValue, setTextValue] = useState("");
69
83
 
70
- const handleOnChangeText = useCallback(
71
- (event: React.ChangeEvent<HTMLInputElement>) => {
72
- onChangeText?.(event);
73
- setTextValue(event.target.value);
74
- },
75
- [onChangeText]
76
- );
84
+ useEffect(() => {
85
+ if (value && !selectedOption) {
86
+ setSelectedOption(value);
87
+ }
88
+ }, [value, selectedOption]);
77
89
 
78
- const handleOptionClick = useCallback(
79
- (option: Options) => {
80
- setSelectedOption(option);
81
- setTextValue(option.label);
82
- onSelect?.(option);
83
- },
84
- [onSelect]
85
- );
90
+ const handleOnChangeText = useCallback(
91
+ (event: React.ChangeEvent<HTMLInputElement>) => {
92
+ onChangeText?.(event);
93
+ setTextValue(event.target.value);
94
+ },
95
+ [onChangeText]
96
+ );
86
97
 
87
- const optionsFiltered = useMemo(() => {
88
- return options.filter(
89
- (option) =>
90
- !filterMode ||
91
- option.label?.toLowerCase().includes(textValue?.toLowerCase())
98
+ const handleOptionClick = useCallback(
99
+ (option: Options) => {
100
+ setSelectedOption(option);
101
+ setTextValue(option.label);
102
+ onSelect?.(option);
103
+ },
104
+ [onSelect]
92
105
  );
93
- }, [options, filterMode, textValue]);
94
106
 
95
- const renderOptions = () => (
96
- <ul className="absolute mt-1 w-full bg-white border border-gray-300 rounded-md shadow-md z-10 max-h-60 overflow-y-auto">
97
- {optionsFiltered.map((option) => (
98
- <li
99
- key={option.value}
100
- onMouseDown={() => handleOptionClick(option)}
101
- className={`px-4 py-2 hover:bg-gray-100 cursor-pointer ${
102
- selectedOption?.value === option.value ? " bg-gray-200" : ""
103
- }`}
104
- >
105
- {option.label}
106
- </li>
107
- ))}
108
- {optionsFiltered.length === 0 && (
109
- <li className="px-4 py-14 text-center text-input-text">Not found</li>
110
- )}
111
- </ul>
112
- );
107
+ const optionsFiltered = useMemo(() => {
108
+ return options.filter(
109
+ (option) =>
110
+ !filterMode ||
111
+ option.label?.toLowerCase().includes(textValue?.toLowerCase())
112
+ );
113
+ }, [options, filterMode, textValue]);
113
114
 
114
- return (
115
- <div className={`relative ${fullwidth ? "w-full" : ""}`}>
116
- <TextInput
117
- {...props}
118
- readOnly={!filterMode}
119
- value={textValue}
120
- onChange={handleOnChangeText}
121
- label={label}
122
- placeholder=" "
123
- type="text"
124
- rounded={rounded}
125
- variant={variant}
126
- helperText={helperText}
127
- errorMessage={errorMessage}
128
- fullwidth={fullwidth}
129
- error={error}
130
- required={required}
131
- id={_id}
132
- disabled={disabled}
133
- hasClearIcon={false}
134
- size={size}
135
- className={customInputVariant({ size })}
136
- onFocus={() => setIsFocused(true)}
137
- onBlur={() => setIsFocused(false)}
138
- endIcon={
139
- <div className={iconWrapperVariant({ size })}>
140
- <ChevronDownIcon
141
- className={dropdownIconVariant({ size, isFocus: isFocused })}
142
- />
143
- </div>
144
- }
145
- />
146
- {isFocused && renderOptions()}
147
- </div>
148
- );
149
- };
115
+ const renderOptions = () => (
116
+ <ul className="absolute mt-1 w-full bg-white border border-gray-300 rounded-md shadow-md z-10 max-h-60 overflow-y-auto">
117
+ {optionsFiltered.map((option) => {
118
+ if (option.renderLabel) {
119
+ return (
120
+ <Fragment key={option.value}>
121
+ {option.renderLabel({
122
+ value: option.value,
123
+ label: option.label,
124
+ handleOnClick: () => handleOptionClick(option),
125
+ className: `px-4 py-2 hover:bg-gray-100 cursor-pointer ${
126
+ selectedOption?.value === option.value ? " bg-gray-200" : ""
127
+ }`,
128
+ })}
129
+ </Fragment>
130
+ );
131
+ }
132
+ return (
133
+ <li
134
+ key={option.value}
135
+ onMouseDown={() => handleOptionClick(option)}
136
+ className={`px-4 py-2 hover:bg-gray-100 cursor-pointer ${
137
+ selectedOption?.value === option.value ? " bg-gray-200" : ""
138
+ }`}
139
+ >
140
+ {option.label}
141
+ </li>
142
+ );
143
+ })}
144
+ {optionsFiltered.length === 0 && (
145
+ <li className="px-4 py-14 text-center text-input-text">Not found</li>
146
+ )}
147
+ </ul>
148
+ );
149
+
150
+ return (
151
+ <div className={`relative ${fullwidth ? "w-full" : ""}`}>
152
+ <TextInput
153
+ {...props}
154
+ ref={ref}
155
+ readOnly={!filterMode}
156
+ value={textValue}
157
+ onChange={handleOnChangeText}
158
+ label={label}
159
+ placeholder=" "
160
+ type="text"
161
+ rounded={rounded}
162
+ variant={variant}
163
+ helperText={helperText}
164
+ errorMessage={errorMessage}
165
+ fullwidth={fullwidth}
166
+ error={error}
167
+ required={required}
168
+ id={_id}
169
+ disabled={disabled}
170
+ hasClearIcon={false}
171
+ size={size}
172
+ className={customInputVariant({ size })}
173
+ onFocus={() => setIsFocused(true)}
174
+ onBlur={() => setIsFocused(false)}
175
+ endIcon={
176
+ <div className={iconWrapperVariant({ size })}>
177
+ <ChevronDownIcon
178
+ className={dropdownIconVariant({ size, isFocus: isFocused })}
179
+ />
180
+ </div>
181
+ }
182
+ />
183
+ {isFocused && renderOptions()}
184
+ </div>
185
+ );
186
+ }
187
+ );
150
188
 
151
189
  export default Dropdown;
@@ -4,12 +4,14 @@ import { cn } from "@/utils/cn";
4
4
 
5
5
  const Table = React.forwardRef<
6
6
  HTMLTableElement,
7
- React.HTMLAttributes<HTMLTableElement>
8
- >(({ className, ...props }, ref) => (
9
- <div className="relative w-full overflow-auto">
7
+ {
8
+ rootRef?: React.LegacyRef<HTMLDivElement>;
9
+ } & React.HTMLAttributes<HTMLTableElement>
10
+ >(({ className, rootRef, ...props }, ref) => (
11
+ <div className="relative h-full w-full overflow-auto" ref={rootRef}>
10
12
  <table
11
13
  ref={ref}
12
- className={cn("w-full caption-bottom text-sm", className)}
14
+ className={cn("w-full caption-bottom text-sm border-collapse", className)}
13
15
  {...props}
14
16
  />
15
17
  </div>
@@ -20,7 +22,11 @@ const TableHeader = React.forwardRef<
20
22
  HTMLTableSectionElement,
21
23
  React.HTMLAttributes<HTMLTableSectionElement>
22
24
  >(({ className, ...props }, ref) => (
23
- <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
25
+ <thead
26
+ ref={ref}
27
+ className={cn("[&_tr]:border-b bg-secondary-80", className)}
28
+ {...props}
29
+ />
24
30
  ));
25
31
  TableHeader.displayName = "TableHeader";
26
32
 
@@ -58,7 +64,7 @@ const TableRow = React.forwardRef<
58
64
  <tr
59
65
  ref={ref}
60
66
  className={cn(
61
- "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
67
+ "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-grey-20",
62
68
  className
63
69
  )}
64
70
  {...props}
@@ -73,7 +79,7 @@ const TableHead = React.forwardRef<
73
79
  <th
74
80
  ref={ref}
75
81
  className={cn(
76
- "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
82
+ " h-12 py-3 px-6 text-left align-middle typography-body2 text-textcolor-grey-dark [&:has([role=checkbox])]:pr-4 [&:has([role=checkbox])]:w-4",
77
83
  className
78
84
  )}
79
85
  {...props}
@@ -87,7 +93,10 @@ const TableCell = React.forwardRef<
87
93
  >(({ className, ...props }, ref) => (
88
94
  <td
89
95
  ref={ref}
90
- className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
96
+ className={cn(
97
+ " py-3 px-6 text-left align-middle typography-body3 text-textcolor-grey-dark [&:has([role=checkbox])]:pr-4 [&:has([role=checkbox])]:w-4",
98
+ className
99
+ )}
91
100
  {...props}
92
101
  />
93
102
  ));
@@ -1,6 +1,6 @@
1
- import React, { FC } from "react";
1
+ import React, { FC, forwardRef } from "react";
2
2
 
3
- type TextProps = {
3
+ export type TextProps = {
4
4
  variant?:
5
5
  | "h1"
6
6
  | "h2"
@@ -38,22 +38,24 @@ type TextProps = {
38
38
  id?: string;
39
39
  };
40
40
 
41
- const Text: FC<TextProps> = ({
42
- variant = "body1",
43
- tag: Tag = "p",
44
- children,
45
- className,
46
- color,
47
- style,
48
- }) => {
49
- return (
50
- <Tag
51
- className={`typography-${variant} text-${color} ${className}`}
52
- style={style}
53
- >
54
- {children}
55
- </Tag>
56
- );
57
- };
41
+ const Text = forwardRef<TextProps["tag"], TextProps>(
42
+ ({
43
+ variant = "body1",
44
+ tag: Tag = "p",
45
+ children,
46
+ className,
47
+ color,
48
+ style,
49
+ }) => {
50
+ return (
51
+ <Tag
52
+ className={`typography-${variant} text-${color} ${className}`}
53
+ style={style}
54
+ >
55
+ {children}
56
+ </Tag>
57
+ );
58
+ }
59
+ );
58
60
 
59
61
  export default Text;
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useRef } from "react";
2
2
  import type { Meta, StoryObj } from "@storybook/react";
3
3
  import TextInput from "./TextInput";
4
4
 
@@ -42,3 +42,48 @@ export const Default = {
42
42
  );
43
43
  },
44
44
  } satisfies StoryObj;
45
+
46
+ const InputWithRef = (props: any) => {
47
+ const inputRef = useRef<HTMLInputElement | null>(null);
48
+
49
+ return (
50
+ <TextInput
51
+ id="1"
52
+ size="lg"
53
+ {...props}
54
+ ref={inputRef}
55
+ labelClassName="peer-focus:bg-red-500"
56
+ onKeyDown={(e) => {
57
+ if (e.code === "Enter") {
58
+ inputRef.current?.blur?.();
59
+ }
60
+ }}
61
+ />
62
+ );
63
+ };
64
+
65
+ export const CustomLabel = {
66
+ args: {
67
+ label: "Lorem Ipsum",
68
+ // value: "Lorem Ipsum",
69
+ fullwidth: true,
70
+ },
71
+ render: (args) => {
72
+ console.log("args ", args);
73
+ const props: typeof args = {
74
+ ...args,
75
+ };
76
+ return (
77
+ <div className="flex flex-row gap-4 w-full">
78
+ <TextInput
79
+ id="1"
80
+ size="lg"
81
+ {...args}
82
+ labelClassName="peer-focus:bg-red-500"
83
+ />
84
+ <InputWithRef id="2" size="md" {...args} />
85
+ <TextInput id="3" size="sm" {...args} />
86
+ </div>
87
+ );
88
+ },
89
+ } satisfies StoryObj;
@@ -12,8 +12,8 @@ import {
12
12
  inputVariant,
13
13
  labelVariant,
14
14
  } from "./TextInput.styles";
15
- import { twMerge } from "tailwind-merge";
16
15
  import { XCircleIcon, ExclamationCircleIcon } from "@heroicons/react/16/solid";
16
+ import { cn } from "@/utils/cn";
17
17
 
18
18
  export type InputProps = {
19
19
  id?: string;
@@ -31,9 +31,10 @@ export type InputProps = {
31
31
  hasClearIcon?: boolean;
32
32
  endIcon?: ReactNode;
33
33
  className?: string;
34
+ labelClassName?: string;
34
35
  } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">;
35
36
 
36
- const TextInput: FC<InputProps> = forwardRef(
37
+ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
37
38
  (
38
39
  {
39
40
  id,
@@ -50,6 +51,7 @@ const TextInput: FC<InputProps> = forwardRef(
50
51
  required = true,
51
52
  hasClearIcon = true,
52
53
  endIcon,
54
+ labelClassName,
53
55
  ...props
54
56
  },
55
57
  ref
@@ -74,9 +76,7 @@ const TextInput: FC<InputProps> = forwardRef(
74
76
  const iconWrapperClassname = iconWrapperVariant({ size });
75
77
  const iconClassname = iconVariant({ size });
76
78
 
77
- useImperativeHandle(ref, () => ({
78
- clearInput: handleClearInput,
79
- }));
79
+ useImperativeHandle(ref, () => inputRef?.current as HTMLInputElement);
80
80
 
81
81
  const handleClearInput = () => {
82
82
  if (inputRef.current) {
@@ -94,7 +94,7 @@ const TextInput: FC<InputProps> = forwardRef(
94
94
  type={type}
95
95
  id={_id}
96
96
  disabled={disabled}
97
- className={twMerge(inputClassname, props.className)}
97
+ className={cn(inputClassname, props.className)}
98
98
  />
99
99
  {hasClearIcon && (
100
100
  <div className={iconWrapperClassname}>
@@ -106,7 +106,7 @@ const TextInput: FC<InputProps> = forwardRef(
106
106
  </div>
107
107
  )}
108
108
  {endIcon}
109
- <label htmlFor={_id} className={labelClassname}>
109
+ <label htmlFor={_id} className={cn(labelClassname, labelClassName)}>
110
110
  {label} {required && <span className="text-error">*</span>}
111
111
  </label>
112
112
  </div>
package/src/index.ts CHANGED
@@ -10,9 +10,15 @@ export { Checkbox } from "./components/Checkbox/Checkbox";
10
10
  export { Label } from "./components/Label/Label";
11
11
  export { Input } from "./components/Input/Input";
12
12
  export * from "./components/Table/Table";
13
+ export * from "./components/DataTable/DataTable";
13
14
  export * from "./components/Dialog/Dialog";
14
15
  export * from "./components/AlertDialog/AlertDialog";
15
16
 
17
+ // Export component types
18
+ export type { ButtonProps } from "./components/Button/Button";
19
+ export type { InputProps } from "./components/TextInput/TextInput";
20
+ export type { DropdownProps, Options } from "./components/Dropdown/Dropdown";
21
+
16
22
  // UTILS
17
23
  export {
18
24
  resloveTimestamp,