next-helios-fe 1.6.13 → 1.6.14

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": "next-helios-fe",
3
- "version": "1.6.13",
3
+ "version": "1.6.14",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,13 +1,10 @@
1
1
  "use client";
2
2
  import React, { useState, useEffect, useRef } from "react";
3
+ import { Dropdown } from "../../../components";
3
4
  import { Icon } from "@iconify/react";
4
- import { createPortal } from "react-dom";
5
5
 
6
6
  export interface MultipleSelectProps
7
- extends Omit<
8
- React.ButtonHTMLAttributes<HTMLButtonElement>,
9
- "onChange" | "value"
10
- > {
7
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange" | "value"> {
11
8
  options?: {
12
9
  width?: "full" | "fit";
13
10
  height?: "short" | "medium" | "high";
@@ -20,6 +17,7 @@ export interface MultipleSelectProps
20
17
  }[];
21
18
  placeholder?: string;
22
19
  required?: boolean;
20
+ disabled?: boolean;
23
21
  value?: string[];
24
22
  onClick?: () => void;
25
23
  onChange?: (e: {
@@ -35,6 +33,7 @@ export const MultipleSelect: React.FC<MultipleSelectProps> = ({
35
33
  menus,
36
34
  placeholder,
37
35
  required,
36
+ disabled,
38
37
  value,
39
38
  onClick,
40
39
  onChange,
@@ -42,13 +41,10 @@ export const MultipleSelect: React.FC<MultipleSelectProps> = ({
42
41
  }) => {
43
42
  const [tempValue, setTempValue] = useState<string[]>([]);
44
43
  const [openDropdown, setOpenDropdown] = useState(false);
45
- const [position, setPosition] = useState<{
46
- top: number;
47
- left: number;
48
- } | null>(null);
49
44
  const [dropdownWidth, setDropdownWidth] = useState<number>(0);
50
- const triggerRef = useRef<HTMLDivElement>(null);
51
- const dropdownRef = useRef<HTMLDivElement>(null);
45
+ const [dropdownHeight, setDropdownHeight] = useState<number>(0);
46
+ const inputRef = useRef<HTMLDivElement>(null);
47
+ const dropdownRef = useRef<HTMLButtonElement>(null);
52
48
  const width = options?.width === "fit" ? "w-fit" : "w-full";
53
49
  const height =
54
50
  options?.height === "short"
@@ -58,51 +54,13 @@ export const MultipleSelect: React.FC<MultipleSelectProps> = ({
58
54
  : "py-1";
59
55
 
60
56
  useEffect(() => {
61
- const handleClickOutside = (e: MouseEvent) => {
62
- if (
63
- dropdownRef.current &&
64
- !dropdownRef.current.contains(e.target as Node) &&
65
- !triggerRef.current?.contains(e.target as Node)
66
- ) {
57
+ document.addEventListener("mousedown", (e) => {
58
+ if (e.target instanceof HTMLElement) {
67
59
  setOpenDropdown(false);
68
60
  }
69
- };
70
-
71
- document.addEventListener("mousedown", handleClickOutside);
72
- return () => {
73
- document.removeEventListener("mousedown", handleClickOutside);
74
- };
61
+ });
75
62
  }, []);
76
63
 
77
- useEffect(() => {
78
- if (triggerRef.current) {
79
- const rect = triggerRef.current.getBoundingClientRect();
80
- const dropdownHeight = dropdownRef.current?.offsetHeight || 0;
81
- const windowHeight = window.innerHeight;
82
-
83
- setPosition({
84
- top: rect.bottom + window.scrollY + 10,
85
- left: rect.left + window.scrollX,
86
- });
87
-
88
- setDropdownWidth(rect.width);
89
-
90
- if (rect.bottom + dropdownHeight > windowHeight) {
91
- setPosition((prev) =>
92
- prev
93
- ? { ...prev, top: rect.top + window.scrollY - dropdownHeight - 10 }
94
- : null
95
- );
96
- }
97
- }
98
-
99
- if (openDropdown) {
100
- document.getElementById("body")!.style.overflow = "hidden";
101
- } else {
102
- document.getElementById("body")!.style.overflow = "auto";
103
- }
104
- }, [openDropdown]);
105
-
106
64
  useEffect(() => {
107
65
  if (value) {
108
66
  setTempValue(value);
@@ -118,94 +76,154 @@ export const MultipleSelect: React.FC<MultipleSelectProps> = ({
118
76
  }, [tempValue]);
119
77
 
120
78
  return (
121
- <label className={`grid gap-2 ${width}`}>
122
- {label && (
123
- <span
124
- className={`text-sm select-none ${
125
- required && "after:content-['*'] after:ml-1 after:text-danger"
126
- }`}
127
- >
128
- {label}
129
- </span>
130
- )}
131
- <div className="relative" ref={triggerRef}>
132
- <button
133
- type="button"
134
- className={`group/button flex justify-between items-center gap-2 w-full min-h-10 px-4 border-default border rounded-md bg-secondary-bg cursor-pointer caret-transparent focus:outline-none focus:ring-1 focus:ring-primary focus:shadow focus:shadow-primary focus:border-primary-dark disabled:bg-secondary-light disabled:text-slate-400 disabled:cursor-default ${height}`}
135
- disabled={rest.disabled}
136
- onClick={() => {
137
- onClick && onClick();
138
- setOpenDropdown(true);
139
- }}
140
- {...rest}
141
- >
142
- {!tempValue || tempValue?.length === 0 ? (
143
- <span className="text-slate-300 duration-300 translate-x-0 group-focus/button:translate-x-1">
144
- {placeholder}
145
- </span>
146
- ) : (
147
- <div className="flex flex-wrap gap-2 h-min">
148
- {tempValue?.map((item) => {
149
- return (
150
- <div
151
- key={item}
152
- className="flex items-center gap-2 px-2 py-0.5 rounded-md bg-primary text-white cursor-default"
153
- >
154
- <span>{menus.find((i) => i.value === item)?.label}</span>
79
+ <div className="flex flex-row-reverse items-end">
80
+ <label className={`grid gap-2 ${width}`}>
81
+ {label && (
82
+ <span
83
+ className={`text-sm select-none ${
84
+ required && "after:content-['*'] after:ml-1 after:text-danger"
85
+ }`}
86
+ >
87
+ {label}
88
+ </span>
89
+ )}
90
+ <div className="relative flex items-center">
91
+ <div
92
+ ref={inputRef}
93
+ className={`group/button flex justify-between items-center gap-2 w-full min-h-10 px-4 border rounded-md caret-transparent focus:outline-none focus:ring-1 focus:ring-primary focus:shadow focus:shadow-primary focus:border-primary-dark ${height} ${
94
+ disabled
95
+ ? "bg-secondary-light cursor-default pointer-events-none"
96
+ : "bg-secondary-bg cursor-pointer"
97
+ } ${
98
+ openDropdown
99
+ ? "outline-none ring-1 ring-primary shadow shadow-primary border-primary-dark"
100
+ : "border-default"
101
+ }`}
102
+ onClick={(e) => {
103
+ e.preventDefault();
104
+ onClick && onClick();
105
+ setOpenDropdown(true);
106
+ dropdownRef.current?.click();
107
+ setDropdownWidth(
108
+ inputRef?.current?.getBoundingClientRect()?.width || 0
109
+ );
110
+ setDropdownHeight(
111
+ inputRef?.current?.getBoundingClientRect()?.height || 0
112
+ );
113
+ }}
114
+ {...rest}
115
+ >
116
+ {!tempValue || tempValue?.length === 0 ? (
117
+ <span
118
+ className={`text-slate-300 select-none duration-300 ${
119
+ openDropdown ? "translate-x-1" : "translate-x-0"
120
+ }`}
121
+ >
122
+ {placeholder}
123
+ </span>
124
+ ) : (
125
+ <div className="flex flex-wrap gap-2 h-min pointer-events-none invisible">
126
+ {tempValue?.map((item) => {
127
+ return (
155
128
  <div
156
- className="cursor-pointer"
157
- onClick={() => {
158
- setTempValue(tempValue.filter((i) => i !== item));
159
- }}
129
+ key={item}
130
+ className="flex items-center gap-2 px-2 py-0.5 rounded-md bg-primary text-white cursor-default pointer-events-auto"
160
131
  >
161
- <Icon icon="pajamas:close" />
132
+ <span>{menus.find((i) => i.value === item)?.label}</span>
133
+ <div
134
+ className="cursor-pointer"
135
+ onClick={() => {
136
+ setTempValue(tempValue.filter((i) => i !== item));
137
+ }}
138
+ >
139
+ <Icon icon="pajamas:close" />
140
+ </div>
162
141
  </div>
163
- </div>
164
- );
165
- })}
142
+ );
143
+ })}
144
+ </div>
145
+ )}
146
+ <div className="ms-auto text-xl text-slate-400 pointer-events-none">
147
+ <Icon
148
+ icon={`gravity-ui:chevron-${openDropdown ? "up" : "down"}`}
149
+ />
166
150
  </div>
167
- )}
168
- <div className="text-xl text-slate-400 pointer-events-none">
169
- <Icon icon={`gravity-ui:chevron-${openDropdown ? "up" : "down"}`} />
170
151
  </div>
171
- </button>
172
- {openDropdown &&
173
- position &&
174
- createPortal(
175
- <div
152
+ <div
153
+ className="absolute flex flex-wrap gap-2 h-min px-4 pointer-events-none"
154
+ style={{ width: dropdownWidth - 52 }}
155
+ >
156
+ {tempValue?.map((item) => {
157
+ return (
158
+ <div
159
+ key={item}
160
+ className={`flex items-center gap-2 px-2 py-0.5 rounded-md text-white select-none cursor-default pointer-events-auto ${
161
+ disabled ? "bg-secondary" : "bg-primary"
162
+ }`}
163
+ >
164
+ <span>{menus.find((i) => i.value === item)?.label}</span>
165
+ <div
166
+ className="cursor-pointer active:opacity-70 active:duration-300 active:ease-out disabled:active:opacity-100"
167
+ onClick={() => {
168
+ if (!disabled) {
169
+ setTempValue(tempValue.filter((i) => i !== item));
170
+ }
171
+ }}
172
+ >
173
+ <Icon icon="pajamas:close" />
174
+ </div>
175
+ </div>
176
+ );
177
+ })}
178
+ </div>
179
+ </div>
180
+ </label>
181
+ <div className="w-0 overflow-hidden">
182
+ <Dropdown
183
+ placement="bottom-start"
184
+ dismissOnClick={false}
185
+ trigger={
186
+ <button
187
+ type="button"
176
188
  ref={dropdownRef}
177
- className="absolute max-h-40 p-1 z-50 pointer-events-auto bg-secondary-bg shadow border rounded-md overflow-auto"
189
+ className="w-0 my-0.5"
178
190
  style={{
179
- top: position.top,
180
- left: position.left,
181
- width: dropdownWidth,
191
+ height: dropdownHeight,
182
192
  }}
183
193
  >
184
- {menus.length === 0 ? (
185
- <div className="flex justify-center">
186
- <span className="px-4 py-1">No data found</span>
187
- </div>
188
- ) : (
189
- menus.map((item, index) => (
190
- <button
191
- key={index}
192
- type="button"
193
- className="min-w-40 w-full my-0.5 px-4 py-2 rounded-md text-sm text-left text-default hover:bg-secondary-light disabled:bg-primary-transparent"
194
- disabled={
195
- tempValue?.find((i) => i === item.value) ? true : false
196
- }
197
- onMouseDown={() => {
198
- setTempValue([...tempValue, item.value]);
199
- }}
200
- >
201
- {item.label}
202
- </button>
203
- ))
204
- )}
205
- </div>,
206
- document.body
207
- )}
194
+ 1
195
+ </button>
196
+ }
197
+ >
198
+ <div
199
+ style={{
200
+ width: dropdownWidth - 11,
201
+ }}
202
+ >
203
+ {menus.length === 0 ? (
204
+ <div className="flex justify-center">
205
+ <span className="px-4 py-1">No data found</span>
206
+ </div>
207
+ ) : (
208
+ menus.map((item, index) => (
209
+ <button
210
+ key={index}
211
+ type="button"
212
+ className="min-w-40 w-full my-0.5 px-4 py-2 rounded-md text-sm text-left text-default hover:bg-secondary-light disabled:bg-primary-transparent"
213
+ disabled={
214
+ tempValue?.find((i) => i === item.value) ? true : false
215
+ }
216
+ onClick={() => {
217
+ setTempValue([...tempValue, item.value]);
218
+ }}
219
+ >
220
+ {item.label}
221
+ </button>
222
+ ))
223
+ )}
224
+ </div>
225
+ </Dropdown>
208
226
  </div>
209
- </label>
227
+ </div>
210
228
  );
211
229
  };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import React, { useState, useEffect, useRef } from "react";
3
+ import { Dropdown } from "../../../components";
3
4
  import { Icon } from "@iconify/react";
4
- import { createPortal } from "react-dom";
5
5
 
6
6
  export interface SelectProps
7
7
  extends React.SelectHTMLAttributes<HTMLSelectElement> {
@@ -27,13 +27,9 @@ export const Select: React.FC<SelectProps> = ({
27
27
  }) => {
28
28
  const [tempValue, setTempValue] = useState("");
29
29
  const [openDropdown, setOpenDropdown] = useState(false);
30
- const [position, setPosition] = useState<{
31
- top: number;
32
- left: number;
33
- } | null>(null);
34
30
  const [dropdownWidth, setDropdownWidth] = useState<number>(0);
35
- const triggerRef = useRef<HTMLDivElement>(null);
36
- const dropdownRef = useRef<HTMLDivElement>(null);
31
+ const inputRef = useRef<HTMLInputElement>(null);
32
+ const dropdownRef = useRef<HTMLButtonElement>(null);
37
33
  const width = options?.width === "fit" ? "w-fit" : "w-full";
38
34
  const height =
39
35
  options?.height === "short"
@@ -43,51 +39,13 @@ export const Select: React.FC<SelectProps> = ({
43
39
  : "py-1.5";
44
40
 
45
41
  useEffect(() => {
46
- const handleClickOutside = (e: MouseEvent) => {
47
- if (
48
- dropdownRef.current &&
49
- !dropdownRef.current.contains(e.target as Node) &&
50
- !triggerRef.current?.contains(e.target as Node)
51
- ) {
42
+ document.addEventListener("mousedown", (e) => {
43
+ if (e.target instanceof HTMLElement) {
52
44
  setOpenDropdown(false);
53
45
  }
54
- };
55
-
56
- document.addEventListener("mousedown", handleClickOutside);
57
- return () => {
58
- document.removeEventListener("mousedown", handleClickOutside);
59
- };
46
+ });
60
47
  }, []);
61
48
 
62
- useEffect(() => {
63
- if (triggerRef.current) {
64
- const rect = triggerRef.current.getBoundingClientRect();
65
- const dropdownHeight = dropdownRef.current?.offsetHeight || 0;
66
- const windowHeight = window.innerHeight;
67
-
68
- setPosition({
69
- top: rect.bottom + window.scrollY + 10,
70
- left: rect.left + window.scrollX,
71
- });
72
-
73
- setDropdownWidth(rect.width);
74
-
75
- if (rect.bottom + dropdownHeight > windowHeight) {
76
- setPosition((prev) =>
77
- prev
78
- ? { ...prev, top: rect.top + window.scrollY - dropdownHeight - 10 }
79
- : null
80
- );
81
- }
82
- }
83
-
84
- if (openDropdown) {
85
- document.getElementById("body")!.style.overflow = "hidden";
86
- } else {
87
- document.getElementById("body")!.style.overflow = "auto";
88
- }
89
- }, [openDropdown]);
90
-
91
49
  useEffect(() => {
92
50
  if (rest.value || rest.value === "") {
93
51
  setTempValue(rest.value as string);
@@ -108,24 +66,27 @@ export const Select: React.FC<SelectProps> = ({
108
66
  }, [tempValue]);
109
67
 
110
68
  return (
111
- <label className={`grid gap-2 ${width}`}>
112
- {label && (
113
- <span
114
- className={`text-sm select-none ${
115
- rest.required && "after:content-['*'] after:ml-1 after:text-danger"
116
- }`}
117
- >
118
- {label}
119
- </span>
120
- )}
121
- <div className="relative" ref={triggerRef}>
122
- <div
123
- className="relative flex items-center cursor-pointer"
124
- onClick={() => setOpenDropdown(!openDropdown)}
125
- >
69
+ <div className="flex flex-row-reverse items-end">
70
+ <label className={`grid gap-2 ${width}`}>
71
+ {label && (
72
+ <span
73
+ className={`text-sm select-none ${
74
+ rest.required &&
75
+ "after:content-['*'] after:ml-1 after:text-danger"
76
+ }`}
77
+ >
78
+ {label}
79
+ </span>
80
+ )}
81
+ <div className="relative flex items-center cursor-pointer">
126
82
  <input
83
+ ref={inputRef}
127
84
  type="text"
128
- className={`w-full px-4 border-default border rounded-md bg-secondary-bg cursor-pointer caret-transparent placeholder:duration-300 placeholder:translate-x-0 focus:placeholder:translate-x-1 placeholder:text-slate-300 focus:outline-none focus:ring-1 focus:ring-primary focus:shadow focus:shadow-primary focus:border-primary-dark disabled:bg-secondary-light disabled:text-slate-400 disabled:cursor-default ${height}`}
85
+ className={`w-full px-4 border rounded-md bg-secondary-bg cursor-pointer caret-transparent placeholder:duration-300 placeholder:text-slate-300 focus:placeholder:translate-x-1 focus:outline-none focus:ring-1 focus:ring-primary focus:shadow focus:shadow-primary focus:border-primary-dark disabled:bg-secondary-light disabled:text-slate-400 disabled:cursor-default ${height} ${
86
+ openDropdown
87
+ ? "placeholder:translate-x-1 outline-none ring-1 ring-primary shadow shadow-primary border-primary-dark"
88
+ : "border-default placeholder:translate-x-0"
89
+ }`}
129
90
  placeholder={placeholder}
130
91
  required={rest.required}
131
92
  disabled={rest.disabled}
@@ -134,55 +95,67 @@ export const Select: React.FC<SelectProps> = ({
134
95
  ? menus.find((item) => item.value === tempValue)?.label
135
96
  : ""
136
97
  }
137
- onClick={() => setOpenDropdown(true)}
98
+ onClick={(e) => {
99
+ e.preventDefault();
100
+ setOpenDropdown(true);
101
+ dropdownRef.current?.click();
102
+ setDropdownWidth(
103
+ inputRef?.current?.getBoundingClientRect()?.width || 0
104
+ );
105
+ }}
138
106
  />
139
107
  <div className="absolute right-4 text-xl text-slate-400 pointer-events-none">
140
108
  <Icon icon={`gravity-ui:chevron-${openDropdown ? "up" : "down"}`} />
141
109
  </div>
142
110
  </div>
143
- {openDropdown &&
144
- position &&
145
- createPortal(
146
- <div
111
+ <select className="hidden" {...rest}>
112
+ {menus.map((item, index) => (
113
+ <option key={index} value={item.value}>
114
+ {item.label}
115
+ </option>
116
+ ))}
117
+ </select>
118
+ </label>
119
+ <div className="w-0 overflow-hidden">
120
+ <Dropdown
121
+ placement="bottom-start"
122
+ trigger={
123
+ <button
124
+ type="button"
147
125
  ref={dropdownRef}
148
- className="absolute max-h-40 p-1 z-50 pointer-events-auto bg-secondary-bg shadow border rounded-md overflow-auto"
149
- style={{
150
- top: position.top,
151
- left: position.left,
152
- width: dropdownWidth,
153
- }}
126
+ className={`w-0 my-0.5 ${height}`}
154
127
  >
155
- {menus.length === 0 ? (
156
- <div className="flex justify-center">
157
- <span className="px-4 py-1">No data found</span>
158
- </div>
159
- ) : (
160
- menus.map((item, index) => (
161
- <button
162
- key={index}
163
- type="button"
164
- className="min-w-40 w-full my-0.5 px-4 py-2 rounded-md text-sm text-left text-default hover:bg-secondary-light disabled:bg-primary-transparent"
165
- disabled={tempValue === item.value}
166
- onMouseDown={() => {
167
- setTempValue(item.value);
168
- setOpenDropdown(false);
169
- }}
170
- >
171
- {item.label}
172
- </button>
173
- ))
174
- )}
175
- </div>,
176
- document.body
177
- )}
128
+ 1
129
+ </button>
130
+ }
131
+ >
132
+ <div
133
+ style={{
134
+ width: dropdownWidth - 11,
135
+ }}
136
+ >
137
+ {menus.length === 0 ? (
138
+ <div className="flex justify-center">
139
+ <span className="px-4 py-1">No data found</span>
140
+ </div>
141
+ ) : (
142
+ menus.map((item, index) => (
143
+ <button
144
+ key={index}
145
+ type="button"
146
+ className="min-w-40 w-full my-0.5 px-4 py-2 rounded-md text-sm text-left hover:bg-secondary-light disabled:bg-primary-transparent disabled:text-primary"
147
+ disabled={tempValue === item.value}
148
+ onClick={() => {
149
+ setTempValue(item.value);
150
+ }}
151
+ >
152
+ {item.label}
153
+ </button>
154
+ ))
155
+ )}
156
+ </div>
157
+ </Dropdown>
178
158
  </div>
179
- <select className="hidden" {...rest}>
180
- {menus.map((item, index) => (
181
- <option key={index} value={item.value}>
182
- {item.label}
183
- </option>
184
- ))}
185
- </select>
186
- </label>
159
+ </div>
187
160
  );
188
161
  };