@megha-ui/react 1.2.188 → 1.2.189

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.
@@ -0,0 +1,57 @@
1
+ import React, { type JSX } from "react";
2
+ type DropdownOption = {
3
+ disabled?: boolean;
4
+ icon?: JSX.Element;
5
+ isDelete?: boolean;
6
+ label: string;
7
+ onDelete?: () => void;
8
+ style?: React.CSSProperties;
9
+ value: number | string;
10
+ };
11
+ type IconDropdownProps = {
12
+ asteriskColor?: string;
13
+ border?: string;
14
+ checkboxWrapper?: string;
15
+ className?: string;
16
+ ClearIcon?: any;
17
+ clearId?: string;
18
+ closeOnSelect?: boolean;
19
+ disabled?: boolean;
20
+ DropDownIcon?: any;
21
+ dropdownListBG?: string;
22
+ dropdownListWidth?: number | string;
23
+ isClear?: boolean;
24
+ isMultiple?: boolean;
25
+ label?: string;
26
+ labelFontSize?: number;
27
+ labelFontWeight?: number;
28
+ labelMarginBottom?: number;
29
+ marginBottom?: number | string;
30
+ marginLeft?: number | string;
31
+ marginRight?: number | string;
32
+ marginTop?: number | string;
33
+ maxDropdownHeight?: number | string;
34
+ menuFrom?: string;
35
+ compactDisplay?: boolean;
36
+ ultraCompactDisplay?: boolean;
37
+ onChange: (values: (number | string)[]) => void;
38
+ onMenuClose?: (e: MouseEvent) => void;
39
+ options: DropdownOption[];
40
+ placeholder?: string;
41
+ required?: boolean;
42
+ searchBorderColor?: string;
43
+ searchEnabled?: boolean;
44
+ selectedCharLength?: number;
45
+ selectedDisplay?: string;
46
+ selectedValues: (number | string)[];
47
+ style?: React.CSSProperties;
48
+ Tooltip?: any;
49
+ withTooltip?: boolean;
50
+ width?: number | string;
51
+ withValue?: boolean;
52
+ autoPosition?: boolean;
53
+ isSort?: boolean;
54
+ isLoading?: boolean;
55
+ };
56
+ declare const IconDropdown: React.FC<IconDropdownProps>;
57
+ export default IconDropdown;
@@ -0,0 +1,262 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { useDensity } from "../../context/DensityContext";
4
+ import { HiChevronDown } from "react-icons/hi";
5
+ import Block from "../block";
6
+ import Loader from "../loader";
7
+ import Text from "../text";
8
+ const IconDropdown = ({ className, ClearIcon, clearId, closeOnSelect = true, disabled = false, DropDownIcon, dropdownListBG, dropdownListWidth, isClear, maxDropdownHeight = "200px", menuFrom = "left", onChange, onMenuClose, options, searchBorderColor = "#2377ba", searchEnabled = true, selectedDisplay, selectedValues, style, Tooltip, withTooltip = false, withValue, labelFontSize, labelFontWeight, labelMarginBottom, asteriskColor, autoPosition = false, compactDisplay = false, ultraCompactDisplay = false, isSort = true, marginTop, marginRight, marginBottom, marginLeft, width, label, required, border, isLoading = false }) => {
9
+ var _a, _b;
10
+ const { density } = useDensity();
11
+ const [isOpen, setIsOpen] = useState(false);
12
+ const [searchTerm, setSearchTerm] = useState("");
13
+ const [filteredOptions, setFilteredOptions] = useState(options);
14
+ const wrapperRef = useRef(null);
15
+ const clearIconRef = useRef(null);
16
+ const searchInputRef = useRef(null);
17
+ const optionRefs = useRef([]);
18
+ const [highlightIndex, setHighlightIndex] = useState(0);
19
+ const [clickStyle, setClickStyle] = useState({
20
+ left: 0,
21
+ top: "100%",
22
+ });
23
+ const calculateAutoPosition = () => {
24
+ if (!wrapperRef.current)
25
+ return;
26
+ const rect = wrapperRef.current.getBoundingClientRect();
27
+ const bottomSpace = window.innerHeight - rect.bottom;
28
+ const topSpace = rect.top;
29
+ const heightNum = typeof maxDropdownHeight === "number"
30
+ ? maxDropdownHeight
31
+ : parseInt(`${maxDropdownHeight}`, 10) || 200;
32
+ if (bottomSpace < heightNum && topSpace > bottomSpace) {
33
+ setClickStyle({ left: 0, bottom: "100%" });
34
+ }
35
+ else {
36
+ setClickStyle({ left: 0, top: "100%" });
37
+ }
38
+ };
39
+ useEffect(() => {
40
+ var _a;
41
+ if (isOpen && searchEnabled) {
42
+ (_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
43
+ }
44
+ if (isOpen) {
45
+ setHighlightIndex(0);
46
+ }
47
+ }, [isOpen, searchEnabled]);
48
+ useEffect(() => {
49
+ if (isOpen) {
50
+ setHighlightIndex(0);
51
+ }
52
+ }, [filteredOptions, isOpen]);
53
+ useEffect(() => {
54
+ var _a;
55
+ if (isOpen && optionRefs.current[highlightIndex]) {
56
+ (_a = optionRefs.current[highlightIndex]) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ block: "nearest" });
57
+ }
58
+ }, [highlightIndex, isOpen]);
59
+ useEffect(() => {
60
+ function handleClickOutside(event) {
61
+ if (wrapperRef.current &&
62
+ !wrapperRef.current.contains(event.target)) {
63
+ const list = wrapperRef.current.querySelector("ul");
64
+ const isDisplayNone = list && window.getComputedStyle(list).display === "none";
65
+ if (!isDisplayNone) {
66
+ onMenuClose && onMenuClose(event);
67
+ setIsOpen(false);
68
+ }
69
+ }
70
+ }
71
+ document.addEventListener("mousedown", handleClickOutside);
72
+ return () => document.removeEventListener("mousedown", handleClickOutside);
73
+ }, [selectedValues, onMenuClose]);
74
+ useEffect(() => {
75
+ const handleKeyDown = (e) => {
76
+ var _a;
77
+ if (!isOpen)
78
+ return;
79
+ if (filteredOptions.length === 0)
80
+ return;
81
+ if (e.key === "ArrowDown") {
82
+ e.preventDefault();
83
+ setHighlightIndex((prev) => (prev + 1) % filteredOptions.length);
84
+ }
85
+ else if (e.key === "ArrowUp") {
86
+ e.preventDefault();
87
+ setHighlightIndex((prev) => prev === 0 ? filteredOptions.length - 1 : prev - 1);
88
+ }
89
+ else if (e.key === "Enter") {
90
+ e.preventDefault();
91
+ const option = filteredOptions[highlightIndex];
92
+ if (!option)
93
+ return;
94
+ handleSelect(option.value, (_a = option.disabled) !== null && _a !== void 0 ? _a : false);
95
+ }
96
+ };
97
+ window.addEventListener("keydown", handleKeyDown);
98
+ return () => window.removeEventListener("keydown", handleKeyDown);
99
+ }, [isOpen, filteredOptions, highlightIndex]);
100
+ useEffect(() => {
101
+ if (menuFrom !== "left") {
102
+ if (menuFrom === "top") {
103
+ setClickStyle({
104
+ bottom: "calc(100% + 5px)",
105
+ left: 0,
106
+ });
107
+ }
108
+ else if (menuFrom === "bottom") {
109
+ setClickStyle({
110
+ top: 0,
111
+ right: 0,
112
+ });
113
+ }
114
+ else {
115
+ setClickStyle({
116
+ right: 0,
117
+ top: "100%",
118
+ });
119
+ }
120
+ }
121
+ }, [menuFrom]);
122
+ useEffect(() => {
123
+ if (!autoPosition)
124
+ return;
125
+ if (isOpen) {
126
+ calculateAutoPosition();
127
+ window.addEventListener("resize", calculateAutoPosition);
128
+ return () => window.removeEventListener("resize", calculateAutoPosition);
129
+ }
130
+ }, [autoPosition, isOpen]);
131
+ useEffect(() => {
132
+ const _options = isSort
133
+ ? options
134
+ .sort((a, b) => (a.label > b.label ? 1 : -1))
135
+ .filter((option) => option.label.toLowerCase().includes(searchTerm.toLowerCase()))
136
+ : options.filter((option) => option.label.toLowerCase().includes(searchTerm.toLowerCase()));
137
+ setFilteredOptions(_options);
138
+ }, [searchTerm, options, isSort]);
139
+ const handleSelect = (value, isDisabled) => {
140
+ if (isDisabled)
141
+ return;
142
+ onChange([value]);
143
+ if (closeOnSelect)
144
+ setIsOpen(false);
145
+ };
146
+ const handleClear = () => {
147
+ if (!isClear)
148
+ return;
149
+ onChange([]);
150
+ };
151
+ const displayValue = selectedValues.length > 0
152
+ ? (_b = (_a = options.find((option) => option.value === selectedValues[0])) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : ""
153
+ : searchTerm;
154
+ const labelStyle = {
155
+ fontSize: labelFontSize,
156
+ fontWeight: labelFontWeight,
157
+ marginBottom: labelMarginBottom,
158
+ };
159
+ const asteriskStyle = {
160
+ color: asteriskColor,
161
+ };
162
+ const listItemStyle = {
163
+ paddingTop: 6,
164
+ paddingBottom: 6,
165
+ paddingLeft: "1rem",
166
+ paddingRight: "1rem",
167
+ borderBottom: "1px solid var(--divider-strong, #dddddd)",
168
+ cursor: "pointer",
169
+ };
170
+ const listItemDisabledStyle = {
171
+ cursor: "not-allowed",
172
+ color: "var(--muted, #a0a0a0)",
173
+ backgroundColor: "var(--disabled-bg)",
174
+ pointerEvents: "none",
175
+ };
176
+ const listItemHoverStyle = {
177
+ color: "var(--dropdown-hover-color)",
178
+ backgroundColor: "var(--dropdown-hover-bg)",
179
+ };
180
+ const selectedItemStyle = {
181
+ backgroundColor: "var(--dropdown-selected-bg)",
182
+ color: "var(--dropdown-selected-color)",
183
+ };
184
+ const handleClick = (e) => {
185
+ const clearEle = document.getElementById(clearId || "");
186
+ if (clearEle === e.target) {
187
+ e.preventDefault();
188
+ }
189
+ else if (clearEle === null || clearEle === void 0 ? void 0 : clearEle.contains(e.target)) {
190
+ e.preventDefault();
191
+ }
192
+ else if (disabled) {
193
+ e.preventDefault();
194
+ }
195
+ else {
196
+ if (!isOpen && autoPosition) {
197
+ calculateAutoPosition();
198
+ }
199
+ setIsOpen(!isOpen);
200
+ }
201
+ };
202
+ const wrapMiddle = (text, maxLength, wrapper = "...") => {
203
+ if (maxLength) {
204
+ if (text.length <= maxLength) {
205
+ return text;
206
+ }
207
+ const halfLength = Math.floor((maxLength - wrapper.length) / 2);
208
+ return text.slice(0, halfLength) + wrapper + text.slice(-halfLength);
209
+ }
210
+ return text;
211
+ };
212
+ return (_jsxs("div", { style: {
213
+ width: width !== null && width !== void 0 ? width : "100%",
214
+ marginBottom,
215
+ marginTop,
216
+ marginRight,
217
+ marginLeft,
218
+ }, children: [label && (_jsxs("p", { style: labelStyle, children: [label, required && _jsx("span", { style: asteriskStyle, children: " *" })] })), _jsxs("div", { ref: wrapperRef, className: `${className}`, style: Object.assign({ border, borderRadius: 4, padding: "0 0.5rem", alignItems: "center", display: "flex", minHeight: `var(--control-min-height, ${ultraCompactDisplay
219
+ ? "1.5rem"
220
+ : compactDisplay
221
+ ? "2rem"
222
+ : density === "ultraCompact"
223
+ ? "1.5rem"
224
+ : density === "compact"
225
+ ? "2rem"
226
+ : "2.5rem"})`, position: "relative" }, style), children: [_jsxs("div", { style: {
227
+ display: "flex",
228
+ alignItems: "center",
229
+ justifyContent: "space-between",
230
+ cursor: "pointer",
231
+ width: "100%",
232
+ }, ref: clearIconRef, onClick: handleClick, children: [withValue && (displayValue !== null && displayValue !== void 0 ? displayValue : selectedDisplay) && (_jsx("div", { style: { marginRight: "0.5rem" }, title: "Click to clear the value", children: selectedDisplay !== null && selectedDisplay !== void 0 ? selectedDisplay : displayValue })), _jsxs("div", { style: {
233
+ display: "flex",
234
+ alignItems: "center",
235
+ }, children: [isClear && (displayValue || selectedDisplay) && (_jsx("div", { style: { marginRight: "0.5rem" }, onClick: handleClear, id: clearId, title: "Click to clear the value", children: ClearIcon ? (ClearIcon) : (_jsx("span", { style: {
236
+ color: "var(--form-border-color, #dbdfe9)",
237
+ fontSize: "1rem",
238
+ }, children: "X" })) })), disabled ? (Tooltip && Tooltip) : DropDownIcon ? (DropDownIcon) : (_jsx(HiChevronDown, { fontSize: "1rem" })), withTooltip && Tooltip && Tooltip] })] }), _jsxs("ul", { style: Object.assign(Object.assign({ width: dropdownListWidth || "100%", position: "absolute", backgroundColor: dropdownListBG || "var(--dropdown-bg)", boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)", border: "1px solid var(--divider, #f5f5f5)", zIndex: 10, marginTop: 4, borderRadius: 4, overflow: "hidden" }, clickStyle), { display: !isOpen ? "none" : "" }), children: [searchEnabled && (_jsx("li", { style: { padding: "0.5rem" }, children: _jsx("input", { ref: searchInputRef, type: "text", placeholder: "Search...", value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), disabled: disabled, style: {
239
+ width: "100%",
240
+ padding: "0.5rem",
241
+ boxSizing: "border-box",
242
+ border: "1px solid",
243
+ borderColor: searchBorderColor,
244
+ borderRadius: 4,
245
+ } }) })), _jsx("div", { style: { maxHeight: maxDropdownHeight, overflowY: "auto" }, children: isLoading ? (_jsxs(Block, { as: "div", className: "flex items-center justify-between", children: [_jsx(Text, { as: "span", children: "Loading options" }), _jsx(Loader, { size: 12 })] })) : (filteredOptions.map((option, index) => (_jsxs("li", { ref: (el) => {
246
+ optionRefs.current[index] = el;
247
+ }, onClick: () => handleSelect(option.value, option.disabled || false), style: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, listItemStyle), { display: "flex", alignItems: "center" }), (index === highlightIndex ? listItemHoverStyle : {})), (option.disabled ? listItemDisabledStyle : {})), (index !== highlightIndex &&
248
+ selectedValues.includes(option.value)
249
+ ? selectedItemStyle
250
+ : {})), { borderBottom: index === filteredOptions.length - 1
251
+ ? "none"
252
+ : "1px solid var(--divider-strong, #dddddd)", wordWrap: "break-word", textWrap: "wrap", justifyContent: "space-between" }), onMouseEnter: (e) => {
253
+ if (!option.disabled) {
254
+ setHighlightIndex(index);
255
+ }
256
+ }, children: [_jsxs("div", { style: {
257
+ display: "flex",
258
+ alignItems: "start",
259
+ width: "100%",
260
+ }, children: [option.icon && option.isDelete && !option.isDelete && (_jsx("span", { style: { margin: "0 0.5rem" }, children: option.icon })), _jsx("span", { children: option.label })] }), option.icon && option.isDelete && option.icon] }, String(option.value))))) })] })] })] }));
261
+ };
262
+ export default IconDropdown;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@megha-ui/react",
3
- "version": "1.2.188",
3
+ "version": "1.2.189",
4
4
  "description": "A collection of reusable UI components for React applications, built with TypeScript.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",