@rovula/ui 0.0.8 → 0.0.9

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 (45) hide show
  1. package/dist/cjs/bundle.css +72 -5
  2. package/dist/cjs/bundle.js +1 -1
  3. package/dist/cjs/bundle.js.map +1 -1
  4. package/dist/cjs/types/components/Dropdown/Dropdown.d.ts +26 -0
  5. package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +367 -0
  6. package/dist/cjs/types/components/Dropdown/Dropdown.styles.d.ts +11 -0
  7. package/dist/cjs/types/components/TextInput/TextInput.d.ts +3 -2
  8. package/dist/cjs/types/components/TextInput/TextInput.stories.d.ts +2 -16
  9. package/dist/cjs/types/index.d.ts +1 -1
  10. package/dist/components/Dropdown/Dropdown.js +58 -0
  11. package/dist/components/{Select/Select.stories.js → Dropdown/Dropdown.stories.js} +8 -11
  12. package/dist/components/Dropdown/Dropdown.styles.js +47 -0
  13. package/dist/components/TextInput/TextInput.js +3 -3
  14. package/dist/esm/bundle.css +72 -5
  15. package/dist/esm/bundle.js +1 -1
  16. package/dist/esm/bundle.js.map +1 -1
  17. package/dist/esm/types/components/Dropdown/Dropdown.d.ts +26 -0
  18. package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +367 -0
  19. package/dist/esm/types/components/Dropdown/Dropdown.styles.d.ts +11 -0
  20. package/dist/esm/types/components/TextInput/TextInput.d.ts +3 -2
  21. package/dist/esm/types/components/TextInput/TextInput.stories.d.ts +2 -16
  22. package/dist/esm/types/index.d.ts +1 -1
  23. package/dist/index.d.ts +10 -6
  24. package/dist/index.js +1 -1
  25. package/dist/src/theme/global.css +92 -6
  26. package/package.json +1 -1
  27. package/src/components/{Select/Select.stories.tsx → Dropdown/Dropdown.stories.tsx} +11 -14
  28. package/src/components/Dropdown/Dropdown.styles.ts +54 -0
  29. package/src/components/Dropdown/Dropdown.tsx +151 -0
  30. package/src/components/TextInput/TextInput.tsx +11 -2
  31. package/src/index.ts +1 -1
  32. package/dist/cjs/types/components/Select/Select copy.d.ts +0 -23
  33. package/dist/cjs/types/components/Select/Select.d.ts +0 -23
  34. package/dist/cjs/types/components/Select/Select.stories.d.ts +0 -348
  35. package/dist/cjs/types/components/Select/Select.styles.d.ts +0 -14
  36. package/dist/components/Select/Select copy.js +0 -42
  37. package/dist/components/Select/Select.js +0 -21
  38. package/dist/components/Select/Select.styles.js +0 -100
  39. package/dist/esm/types/components/Select/Select copy.d.ts +0 -23
  40. package/dist/esm/types/components/Select/Select.d.ts +0 -23
  41. package/dist/esm/types/components/Select/Select.stories.d.ts +0 -348
  42. package/dist/esm/types/components/Select/Select.styles.d.ts +0 -14
  43. package/src/components/Select/Select copy.tsx +0 -103
  44. package/src/components/Select/Select.styles.ts +0 -111
  45. package/src/components/Select/Select.tsx +0 -54
@@ -812,12 +812,8 @@ video {
812
812
  right: 0px;
813
813
  }
814
814
 
815
- .top-2 {
816
- top: 0.5rem;
817
- }
818
-
819
- .top-4 {
820
- top: 1rem;
815
+ .z-10 {
816
+ z-index: 10;
821
817
  }
822
818
 
823
819
  .z-50 {
@@ -897,6 +893,20 @@ video {
897
893
  height: 1.25rem;
898
894
  }
899
895
 
896
+ .size-6 {
897
+ width: 1.5rem;
898
+ height: 1.5rem;
899
+ }
900
+
901
+ .size-\[14px\] {
902
+ width: 14px;
903
+ height: 14px;
904
+ }
905
+
906
+ .max-h-60 {
907
+ max-height: 15rem;
908
+ }
909
+
900
910
  .w-\[200px\] {
901
911
  width: 200px;
902
912
  }
@@ -917,6 +927,11 @@ video {
917
927
  max-width: 48rem;
918
928
  }
919
929
 
930
+ .rotate-180 {
931
+ --tw-rotate: 180deg;
932
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
933
+ }
934
+
920
935
  .cursor-pointer {
921
936
  cursor: pointer;
922
937
  }
@@ -982,6 +997,10 @@ video {
982
997
  overflow-x: auto;
983
998
  }
984
999
 
1000
+ .overflow-y-auto {
1001
+ overflow-y: auto;
1002
+ }
1003
+
985
1004
  .whitespace-nowrap {
986
1005
  white-space: nowrap;
987
1006
  }
@@ -1010,6 +1029,10 @@ video {
1010
1029
  border-radius: 0.5rem;
1011
1030
  }
1012
1031
 
1032
+ .rounded-md {
1033
+ border-radius: 0.375rem;
1034
+ }
1035
+
1013
1036
  .rounded-none {
1014
1037
  border-radius: 0px;
1015
1038
  }
@@ -1039,6 +1062,11 @@ video {
1039
1062
  border-color: rgb(var(--error-100) / var(--tw-border-opacity));
1040
1063
  }
1041
1064
 
1065
+ .border-gray-300 {
1066
+ --tw-border-opacity: 1;
1067
+ border-color: rgb(209 213 219 / var(--tw-border-opacity));
1068
+ }
1069
+
1042
1070
  .border-info {
1043
1071
  --tw-border-opacity: 1;
1044
1072
  border-color: rgb(var(--info-default) / var(--tw-border-opacity));
@@ -1083,6 +1111,11 @@ video {
1083
1111
  background-color: rgb(var(--error-100) / var(--tw-bg-opacity));
1084
1112
  }
1085
1113
 
1114
+ .bg-gray-200 {
1115
+ --tw-bg-opacity: 1;
1116
+ background-color: rgb(229 231 235 / var(--tw-bg-opacity));
1117
+ }
1118
+
1086
1119
  .bg-info {
1087
1120
  --tw-bg-opacity: 1;
1088
1121
  background-color: rgb(var(--info-default) / var(--tw-bg-opacity));
@@ -1127,6 +1160,11 @@ video {
1127
1160
  background-color: rgb(var(--warning-default) / var(--tw-bg-opacity));
1128
1161
  }
1129
1162
 
1163
+ .bg-white {
1164
+ --tw-bg-opacity: 1;
1165
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
1166
+ }
1167
+
1130
1168
  .fill-error {
1131
1169
  fill: rgb(var(--error-100) / 1);
1132
1170
  }
@@ -1135,6 +1173,18 @@ video {
1135
1173
  fill: rgb(var(--input-active-stroke-color) / 1);
1136
1174
  }
1137
1175
 
1176
+ .fill-input-text {
1177
+ fill: rgb(var(--input-default-text-color) / 1);
1178
+ }
1179
+
1180
+ .fill-input-text-active {
1181
+ fill: rgb(var(--input-active-text-color) / 1);
1182
+ }
1183
+
1184
+ .fill-input-text-disabled {
1185
+ fill: rgb(var(--input-disabled-text-color) / 1);
1186
+ }
1187
+
1138
1188
  .p-1 {
1139
1189
  padding: 0.25rem;
1140
1190
  }
@@ -1176,6 +1226,11 @@ video {
1176
1226
  padding-bottom: 0.25rem;
1177
1227
  }
1178
1228
 
1229
+ .py-14 {
1230
+ padding-top: 3.5rem;
1231
+ padding-bottom: 3.5rem;
1232
+ }
1233
+
1179
1234
  .py-2 {
1180
1235
  padding-top: 0.5rem;
1181
1236
  padding-bottom: 0.5rem;
@@ -1186,6 +1241,22 @@ video {
1186
1241
  padding-bottom: 1rem;
1187
1242
  }
1188
1243
 
1244
+ .pe-\[30px\] {
1245
+ padding-inline-end: 30px;
1246
+ }
1247
+
1248
+ .pe-\[40px\] {
1249
+ padding-inline-end: 40px;
1250
+ }
1251
+
1252
+ .pe-\[48px\] {
1253
+ padding-inline-end: 48px;
1254
+ }
1255
+
1256
+ .text-center {
1257
+ text-align: center;
1258
+ }
1259
+
1189
1260
  .align-middle {
1190
1261
  vertical-align: middle;
1191
1262
  }
@@ -1453,6 +1524,12 @@ video {
1453
1524
  text-underline-offset: 4px;
1454
1525
  }
1455
1526
 
1527
+ .shadow-md {
1528
+ --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
1529
+ --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
1530
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1531
+ }
1532
+
1456
1533
  .outline-none {
1457
1534
  outline: 2px solid transparent;
1458
1535
  outline-offset: 2px;
@@ -1487,6 +1564,10 @@ video {
1487
1564
  --tw-ring-color: rgb(var(--input-disabled-stroke-color) / var(--tw-ring-opacity));
1488
1565
  }
1489
1566
 
1567
+ .filter {
1568
+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1569
+ }
1570
+
1490
1571
  .transition-all {
1491
1572
  transition-property: all;
1492
1573
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
@@ -1671,6 +1752,11 @@ video {
1671
1752
  background-color: rgb(var(--error-120) / var(--tw-bg-opacity));
1672
1753
  }
1673
1754
 
1755
+ .hover\:bg-gray-100:hover {
1756
+ --tw-bg-opacity: 1;
1757
+ background-color: rgb(243 244 246 / var(--tw-bg-opacity));
1758
+ }
1759
+
1674
1760
  .hover\:bg-info-100:hover {
1675
1761
  --tw-bg-opacity: 1;
1676
1762
  background-color: rgb(var(--info-100)) / var(--tw-bg-opacity));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rovula/ui",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "main": "dist/cjs/bundle.js",
5
5
  "module": "dist/esm/bundle.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,11 +1,11 @@
1
1
  import React from "react";
2
2
  import type { Meta, StoryObj } from "@storybook/react";
3
- import Select from "./Select";
3
+ import Dropdown from "./Dropdown";
4
4
 
5
5
  // More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction
6
6
  const meta = {
7
- title: "Components/Select",
8
- component: Select,
7
+ title: "Components/Dropdown",
8
+ component: Dropdown,
9
9
  tags: ["autodocs"],
10
10
  parameters: {
11
11
  // More on how to position stories at: https://storybook.js.org/docs/7.0/react/configure/story-layout
@@ -18,21 +18,18 @@ const meta = {
18
18
  </div>
19
19
  ),
20
20
  ],
21
- } satisfies Meta<typeof Select>;
21
+ } satisfies Meta<typeof Dropdown>;
22
22
 
23
23
  export default meta;
24
24
 
25
- const options = [
26
- { value: "option1", label: "Option 1" },
27
- { value: "option2", label: "Option 2" },
28
- { value: "option3", label: "Option 3" },
29
- { value: "option4", label: "Option 4" },
30
- ];
25
+ const options = new Array(100).fill("").map((__, index) => ({
26
+ value: `option${index + 1}`,
27
+ label: `Option ${index + 1}`,
28
+ }));
31
29
 
32
30
  export const Default = {
33
31
  args: {
34
32
  label: "Choose an option:",
35
- // value: "",
36
33
  fullwidth: true,
37
34
  options,
38
35
  },
@@ -43,9 +40,9 @@ export const Default = {
43
40
  };
44
41
  return (
45
42
  <div className="flex flex-row gap-4 w-full">
46
- <Select id="1" size="lg" options={options} {...args} />
47
- <Select id="2" size="md" options={options} {...args} />
48
- <Select id="3" size="sm" options={options} {...args} />
43
+ <Dropdown id="1" size="lg" options={options} {...args} />
44
+ <Dropdown id="2" size="md" options={options} {...args} />
45
+ <Dropdown id="3" size="sm" options={options} {...args} />
49
46
  </div>
50
47
  );
51
48
  },
@@ -0,0 +1,54 @@
1
+ import React from "react";
2
+ import { cva } from "class-variance-authority";
3
+
4
+ export const iconWrapperVariant = cva(
5
+ ["absolute inset-y-0 right-0 flex items-center justify-center"],
6
+ {
7
+ variants: {
8
+ size: {
9
+ sm: "mr-2",
10
+ md: "mr-3",
11
+ lg: "mr-4",
12
+ },
13
+ },
14
+ defaultVariants: {
15
+ size: "md",
16
+ },
17
+ }
18
+ );
19
+
20
+ export const dropdownIconVariant = cva(["transition-all"], {
21
+ variants: {
22
+ size: {
23
+ sm: "size-[14px]",
24
+ md: "size-5",
25
+ lg: "size-6",
26
+ },
27
+ disabled: {
28
+ true: "fill-input-text-disabled",
29
+ false: "fill-input-text",
30
+ },
31
+ isFocus: {
32
+ true: "fill-input-text-active rotate-180",
33
+ false: "",
34
+ },
35
+ },
36
+ defaultVariants: {
37
+ size: "md",
38
+ disabled: false,
39
+ isFocus: false,
40
+ },
41
+ });
42
+
43
+ export const customInputVariant = cva([], {
44
+ variants: {
45
+ size: {
46
+ sm: "pe-[30px]",
47
+ md: "pe-[40px]",
48
+ lg: "pe-[48px]",
49
+ },
50
+ },
51
+ defaultVariants: {
52
+ size: "md",
53
+ },
54
+ });
@@ -0,0 +1,151 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from "react";
2
+
3
+ import TextInput, { InputProps } from "../TextInput/TextInput";
4
+ import {
5
+ customInputVariant,
6
+ dropdownIconVariant,
7
+ iconWrapperVariant,
8
+ } from "./Dropdown.styles";
9
+
10
+ import { ChevronDownIcon } from "@heroicons/react/16/solid";
11
+
12
+ type Options = {
13
+ value: string;
14
+ label: string;
15
+ };
16
+
17
+ type DropdownProps = {
18
+ id?: string;
19
+ label?: string;
20
+ size?: "sm" | "md" | "lg";
21
+ rounded?: "none" | "normal" | "full";
22
+ variant?: "flat" | "outline" | "underline";
23
+ helperText?: string;
24
+ errorMessage?: string;
25
+ filterMode?: boolean;
26
+ fullwidth?: boolean;
27
+ disabled?: boolean;
28
+ error?: boolean;
29
+ required?: boolean;
30
+ className?: string;
31
+ options: Options[];
32
+ value?: Options;
33
+ onChangeText?: InputProps["onChange"];
34
+ onSelect?: (value: Options) => void;
35
+ } & Omit<InputProps, "value">;
36
+
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("");
63
+
64
+ useEffect(() => {
65
+ if (value && !selectedOption) {
66
+ setSelectedOption(value);
67
+ }
68
+ }, [value, selectedOption]);
69
+
70
+ const handleOnChangeText = useCallback(
71
+ (event: React.ChangeEvent<HTMLInputElement>) => {
72
+ onChangeText?.(event);
73
+ setTextValue(event.target.value);
74
+ },
75
+ [onChangeText]
76
+ );
77
+
78
+ const handleOptionClick = useCallback(
79
+ (option: Options) => {
80
+ setSelectedOption(option);
81
+ setTextValue(option.label);
82
+ onSelect?.(option);
83
+ },
84
+ [onSelect]
85
+ );
86
+
87
+ const optionsFiltered = useMemo(() => {
88
+ return options.filter(
89
+ (option) =>
90
+ !filterMode ||
91
+ option.label?.toLowerCase().includes(textValue?.toLowerCase())
92
+ );
93
+ }, [options, filterMode, textValue]);
94
+
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
+ );
113
+
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
+ };
150
+
151
+ export default Dropdown;
@@ -1,4 +1,10 @@
1
- import React, { FC, forwardRef, useImperativeHandle, useRef } from "react";
1
+ import React, {
2
+ FC,
3
+ ReactNode,
4
+ forwardRef,
5
+ useImperativeHandle,
6
+ useRef,
7
+ } from "react";
2
8
  import {
3
9
  helperTextVariant,
4
10
  iconVariant,
@@ -9,7 +15,7 @@ import {
9
15
  import { twMerge } from "tailwind-merge";
10
16
  import { XCircleIcon, ExclamationCircleIcon } from "@heroicons/react/16/solid";
11
17
 
12
- type InputProps = {
18
+ export type InputProps = {
13
19
  id?: string;
14
20
  label?: string;
15
21
  size?: "sm" | "md" | "lg";
@@ -23,6 +29,7 @@ type InputProps = {
23
29
  error?: boolean;
24
30
  required?: boolean;
25
31
  hasClearIcon?: boolean;
32
+ endIcon?: ReactNode;
26
33
  className?: string;
27
34
  } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">;
28
35
 
@@ -42,6 +49,7 @@ const TextInput: FC<InputProps> = forwardRef(
42
49
  error = false,
43
50
  required = true,
44
51
  hasClearIcon = true,
52
+ endIcon,
45
53
  ...props
46
54
  },
47
55
  ref
@@ -97,6 +105,7 @@ const TextInput: FC<InputProps> = forwardRef(
97
105
  />
98
106
  </div>
99
107
  )}
108
+ {endIcon}
100
109
  <label htmlFor={_id} className={labelClassname}>
101
110
  {label} {required && <span className="text-error">*</span>}
102
111
  </label>
package/src/index.ts CHANGED
@@ -6,7 +6,7 @@ export { default as Table } from "./components/Table/Table";
6
6
  export { default as TextInput } from "./components/TextInput/TextInput";
7
7
  export { default as Text } from "./components/Text/Text";
8
8
  export { default as Tabs } from "./components/Tabs/Tabs";
9
- export { default as Select } from "./components/Select/Select";
9
+ export { default as Dropdown } from "./components/Dropdown/Dropdown";
10
10
 
11
11
  // UTILS
12
12
  export {
@@ -1,23 +0,0 @@
1
- import React from "react";
2
- type Options = {
3
- value: string;
4
- label: string;
5
- };
6
- type SelectProps = {
7
- id?: string;
8
- label?: string;
9
- size?: "sm" | "md" | "lg";
10
- rounded?: "none" | "normal" | "full";
11
- variant?: "flat" | "outline" | "underline";
12
- type?: React.HTMLInputTypeAttribute;
13
- helperText?: string;
14
- errorMessage?: string;
15
- fullwidth?: boolean;
16
- disabled?: boolean;
17
- error?: boolean;
18
- required?: boolean;
19
- className?: string;
20
- options: Options[];
21
- } & Omit<React.InputHTMLAttributes<HTMLSelectElement>, "size">;
22
- declare const Select: ({ id, options, label, size, rounded, variant, type, helperText, errorMessage, fullwidth, disabled, error, required, ...props }: SelectProps) => import("react/jsx-runtime").JSX.Element;
23
- export default Select;
@@ -1,23 +0,0 @@
1
- import React from "react";
2
- type Options = {
3
- value: string;
4
- label: string;
5
- };
6
- type SelectProps = {
7
- id?: string;
8
- label?: string;
9
- size?: "sm" | "md" | "lg";
10
- rounded?: "none" | "normal" | "full";
11
- variant?: "flat" | "outline" | "underline";
12
- type?: React.HTMLInputTypeAttribute;
13
- helperText?: string;
14
- errorMessage?: string;
15
- fullwidth?: boolean;
16
- disabled?: boolean;
17
- error?: boolean;
18
- required?: boolean;
19
- className?: string;
20
- options: Options[];
21
- } & Omit<React.InputHTMLAttributes<HTMLSelectElement>, "size">;
22
- declare const Select: ({ id, options, label, size, rounded, variant, type, helperText, errorMessage, fullwidth, disabled, error, required, ...props }: SelectProps) => import("react/jsx-runtime").JSX.Element;
23
- export default Select;