@monolith-forensics/monolith-ui 1.3.71 → 1.3.92
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/dist/FieldLabel/FieldLabel.d.ts +3 -2
- package/dist/FieldLabel/FieldLabel.js +22 -6
- package/dist/FieldLabel/index.d.ts +1 -0
- package/dist/ItemTotal/ItemTotal.d.ts +3 -5
- package/dist/ItemTotal/ItemTotal.js +1 -2
- package/dist/ItemTotal/index.d.ts +1 -2
- package/dist/ItemTotal/index.js +1 -1
- package/dist/SearchableTextArea/SearchableTextArea.d.ts +25 -0
- package/dist/SearchableTextArea/SearchableTextArea.js +153 -0
- package/dist/SearchableTextArea/index.d.ts +2 -0
- package/dist/SearchableTextArea/index.js +1 -0
- package/dist/SelectBox/SelectBox.d.ts +2 -3
- package/dist/SelectBox/SelectBox.js +105 -279
- package/dist/SelectBox/select-box.styled-components.d.ts +19 -0
- package/dist/SelectBox/select-box.styled-components.js +172 -0
- package/dist/SelectBox/select-box.utils.d.ts +19 -0
- package/dist/SelectBox/select-box.utils.js +46 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ interface InfoComponentProps {
|
|
|
7
7
|
label?: string | ReactNode;
|
|
8
8
|
}
|
|
9
9
|
export declare const InfoComponent: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<InfoComponentProps, never>> & string & Omit<({ className, children, description, label }: InfoComponentProps) => string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined, keyof import("react").Component<any, {}, any>>;
|
|
10
|
-
interface FieldLabelProps {
|
|
10
|
+
export interface FieldLabelProps {
|
|
11
11
|
className?: string;
|
|
12
12
|
children?: ReactNode;
|
|
13
13
|
error?: string;
|
|
@@ -15,6 +15,7 @@ interface FieldLabelProps {
|
|
|
15
15
|
size?: Size;
|
|
16
16
|
asterisk?: boolean;
|
|
17
17
|
style?: React.CSSProperties;
|
|
18
|
+
actionComponent?: ReactNode;
|
|
18
19
|
}
|
|
19
|
-
declare const FieldLabel: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<FieldLabelProps, never>> & string & Omit<({ className, children, error, description, size, style, }: FieldLabelProps) => import("react/jsx-runtime").JSX.Element, keyof import("react").Component<any, {}, any>>;
|
|
20
|
+
declare const FieldLabel: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<FieldLabelProps, never>> & string & Omit<({ className, children, error, description, size, style, actionComponent, }: FieldLabelProps) => import("react/jsx-runtime").JSX.Element, keyof import("react").Component<any, {}, any>>;
|
|
20
21
|
export default FieldLabel;
|
|
@@ -24,22 +24,38 @@ export const InfoComponent = styled(({ className, children, description, label }
|
|
|
24
24
|
margin-bottom: 5px;
|
|
25
25
|
}
|
|
26
26
|
`;
|
|
27
|
-
const FieldLabel = styled(({ className, children, error, description, size = "sm", style, }) => {
|
|
28
|
-
return (_jsxs("div", { className: className + " FieldLabel", style: style, children: [_jsx(InfoComponent, { description: description, label: children, children: _jsx("div", { className: "label", children: children }) }), error && (_jsx("div", { className: "error", title: error, children: error || "Invalid Value" }))] }));
|
|
27
|
+
const FieldLabel = styled(({ className, children, error, description, size = "sm", style, actionComponent, }) => {
|
|
28
|
+
return (_jsxs("div", { className: className + " FieldLabel", style: style, children: [_jsxs("div", { className: "label-content", children: [_jsx(InfoComponent, { description: description, label: children, children: _jsx("div", { className: "label", children: children }) }), error && (_jsx("div", { className: "error", title: error, children: error || "Invalid Value" }))] }), actionComponent && (_jsx("div", { className: "action-section", children: actionComponent }))] }));
|
|
29
29
|
}) `
|
|
30
30
|
user-select: none;
|
|
31
|
-
font-family:
|
|
32
|
-
"
|
|
31
|
+
font-family:
|
|
32
|
+
apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
|
|
33
|
+
Arial, sans-serif;
|
|
33
34
|
user-select: none;
|
|
34
35
|
display: flex;
|
|
35
36
|
flex-direction: row;
|
|
36
|
-
align-items:
|
|
37
|
-
|
|
37
|
+
align-items: flex-start;
|
|
38
|
+
justify-content: space-between;
|
|
39
|
+
width: 100%;
|
|
38
40
|
|
|
39
41
|
min-height: 15px;
|
|
40
42
|
font-weight: 400;
|
|
41
43
|
margin-bottom: 2.5px;
|
|
42
44
|
|
|
45
|
+
.label-content {
|
|
46
|
+
display: flex;
|
|
47
|
+
flex-direction: row;
|
|
48
|
+
align-items: baseline;
|
|
49
|
+
gap: 5px;
|
|
50
|
+
flex: 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.action-section {
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
margin-left: 8px;
|
|
57
|
+
}
|
|
58
|
+
|
|
43
59
|
font-size: ${({ size = "sm" }) => size === "xs"
|
|
44
60
|
? "10px"
|
|
45
61
|
: size === "sm"
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
|
|
2
|
+
export type ItemTotalProps = {
|
|
3
3
|
className?: string;
|
|
4
4
|
total: number;
|
|
5
5
|
Icon: React.ComponentType<any>;
|
|
6
6
|
style?: React.CSSProperties;
|
|
7
7
|
title?: string;
|
|
8
|
-
}
|
|
9
|
-
declare const ItemTotal: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<ItemTotalProps, never>> & string & Omit<({ className, total, Icon, style, title, }: ItemTotalProps) => import("react/jsx-runtime").JSX.Element, keyof React.Component<any, {}, any>>;
|
|
10
|
-
export default ItemTotal;
|
|
11
|
-
export type { ItemTotalProps };
|
|
8
|
+
};
|
|
9
|
+
export declare const ItemTotal: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<ItemTotalProps, never>> & string & Omit<({ className, total, Icon, style, title, }: ItemTotalProps) => import("react/jsx-runtime").JSX.Element, keyof React.Component<any, {}, any>>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import styled from "styled-components";
|
|
3
|
-
const ItemTotal = styled(({ className, total, Icon, style = {}, title = "Total Items", }) => {
|
|
3
|
+
export const ItemTotal = styled(({ className, total, Icon, style = {}, title = "Total Items", }) => {
|
|
4
4
|
return (_jsxs("div", { className: className, title: title, style: style, children: [_jsx(Icon, { size: 14, style: {
|
|
5
5
|
fontSize: 14,
|
|
6
6
|
marginRight: 5,
|
|
@@ -19,4 +19,3 @@ const ItemTotal = styled(({ className, total, Icon, style = {}, title = "Total I
|
|
|
19
19
|
color: ${({ theme }) => theme.palette.text.primary};
|
|
20
20
|
}
|
|
21
21
|
`;
|
|
22
|
-
export default ItemTotal;
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export type { ItemTotalProps } from "./ItemTotal";
|
|
1
|
+
export * from "./ItemTotal";
|
package/dist/ItemTotal/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from "./ItemTotal";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface SearchableTextAreaProps {
|
|
2
|
+
label?: string;
|
|
3
|
+
data?: Array<{
|
|
4
|
+
label: string;
|
|
5
|
+
value: string;
|
|
6
|
+
}>;
|
|
7
|
+
searchable?: boolean;
|
|
8
|
+
allowCustomValue?: boolean;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
value?: string;
|
|
13
|
+
onChange?: (value: string) => void;
|
|
14
|
+
selectProps?: Record<string, any>;
|
|
15
|
+
textAreaPlaceholder?: string;
|
|
16
|
+
textAreaMinRows?: number;
|
|
17
|
+
textAreaMaxRows?: number;
|
|
18
|
+
textAreaProps?: Record<string, any>;
|
|
19
|
+
initialValue?: string;
|
|
20
|
+
}
|
|
21
|
+
declare function SearchableTextArea({ label, data, searchable, allowCustomValue, required, error, description, selectProps, value, onChange, textAreaPlaceholder, textAreaMinRows, textAreaMaxRows, textAreaProps, initialValue, }: SearchableTextAreaProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
declare namespace SearchableTextArea {
|
|
23
|
+
var displayName: string;
|
|
24
|
+
}
|
|
25
|
+
export default SearchableTextArea;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef } from "react";
|
|
3
|
+
import styled from "styled-components";
|
|
4
|
+
import { MoreHorizontal } from "lucide-react";
|
|
5
|
+
import { SelectBox } from "../SelectBox";
|
|
6
|
+
import FieldLabel from "../FieldLabel";
|
|
7
|
+
import DropDownMenu from "../DropDownMenu";
|
|
8
|
+
// Wrapper components for layout
|
|
9
|
+
const CombinedInputWrapper = styled.div `
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
width: 100%;
|
|
13
|
+
position: relative;
|
|
14
|
+
`;
|
|
15
|
+
const TextAreaRow = styled.div `
|
|
16
|
+
position: relative;
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: flex-start;
|
|
19
|
+
width: 100%;
|
|
20
|
+
`;
|
|
21
|
+
const SelectBoxWrapper = styled.div `
|
|
22
|
+
position: relative;
|
|
23
|
+
width: 100%;
|
|
24
|
+
`;
|
|
25
|
+
const StyledSelectBox = styled(SelectBox) `
|
|
26
|
+
/* Completely hide the SelectBox but keep it functional for dropdown */
|
|
27
|
+
position: absolute;
|
|
28
|
+
top: 0;
|
|
29
|
+
left: 0;
|
|
30
|
+
width: 100%;
|
|
31
|
+
opacity: 0;
|
|
32
|
+
pointer-events: auto; /* Allow interactions for dropdown */
|
|
33
|
+
z-index: -1;
|
|
34
|
+
`;
|
|
35
|
+
const StyledFieldLabel = styled(FieldLabel) `
|
|
36
|
+
.action-section {
|
|
37
|
+
margin-left: 2px !important; /* Reduce spacing from default 8px */
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.label-content {
|
|
41
|
+
gap: 2px !important; /* Reduce gap from default 5px */
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
// Styled TextArea that is now the primary input
|
|
45
|
+
const StyledTextArea = styled.textarea `
|
|
46
|
+
border: 1px solid
|
|
47
|
+
${({ theme, $hasError }) => $hasError ? theme.palette.error.main : theme.palette.input.border};
|
|
48
|
+
border-radius: 4px;
|
|
49
|
+
padding: 10px;
|
|
50
|
+
font-family: ${({ theme }) => theme.typography.fontFamily};
|
|
51
|
+
font-size: 13px;
|
|
52
|
+
background-color: ${({ theme }) => theme.palette.input.background};
|
|
53
|
+
color: ${({ theme }) => theme.palette.text.primary};
|
|
54
|
+
resize: none;
|
|
55
|
+
outline: none;
|
|
56
|
+
transition: border-color 0.2s ease;
|
|
57
|
+
width: 100%;
|
|
58
|
+
min-height: ${({ $minRows = 3 }) => $minRows * 1.5 + 1.5}em;
|
|
59
|
+
${({ $maxRows }) => $maxRows && `max-height: ${$maxRows * 1.5 + 1.5}em;`}
|
|
60
|
+
pointer-events: auto;
|
|
61
|
+
user-select: text;
|
|
62
|
+
z-index: 2;
|
|
63
|
+
position: relative;
|
|
64
|
+
|
|
65
|
+
&:focus {
|
|
66
|
+
border-color: ${({ theme }) => theme.palette.primary.main};
|
|
67
|
+
z-index: 3;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&::placeholder {
|
|
71
|
+
color: ${({ theme }) => theme.palette.text.secondary};
|
|
72
|
+
opacity: 0.5;
|
|
73
|
+
}
|
|
74
|
+
`;
|
|
75
|
+
function SearchableTextArea({ label, data, searchable, allowCustomValue, required, error, description, selectProps = {}, value, onChange, textAreaPlaceholder, textAreaMinRows = 3, textAreaMaxRows, textAreaProps = {}, initialValue = "", }) {
|
|
76
|
+
// Use controlled value if provided, otherwise use internal state
|
|
77
|
+
const [internalValue, setInternalValue] = useState(initialValue);
|
|
78
|
+
const [selectValue, setSelectValue] = useState(null);
|
|
79
|
+
const [selectKey, setSelectKey] = useState(0); // Force rerender key
|
|
80
|
+
// Ref to control the SelectBox dropdown
|
|
81
|
+
const selectBoxRef = useRef(null);
|
|
82
|
+
const textAreaValue = value !== undefined ? value : internalValue;
|
|
83
|
+
// Handle SelectBox selection - overrides textarea content then clears selectbox
|
|
84
|
+
const handleSelectChange = (newValue) => {
|
|
85
|
+
if (newValue) {
|
|
86
|
+
// Override textarea content with selected value
|
|
87
|
+
setInternalValue(newValue);
|
|
88
|
+
// Also call onChange if provided (for form integration)
|
|
89
|
+
if (onChange) {
|
|
90
|
+
onChange(newValue);
|
|
91
|
+
}
|
|
92
|
+
setTimeout(() => {
|
|
93
|
+
setSelectValue(null);
|
|
94
|
+
setSelectKey((prev) => prev + 1);
|
|
95
|
+
}, 100);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
setSelectValue(newValue);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
// Handle textarea changes
|
|
102
|
+
const handleTextAreaChange = (event) => {
|
|
103
|
+
const newValue = event.target.value;
|
|
104
|
+
// Always update internal state for display purposes
|
|
105
|
+
setInternalValue(newValue);
|
|
106
|
+
// Also call onChange if provided (for form integration)
|
|
107
|
+
if (onChange) {
|
|
108
|
+
onChange(newValue);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
// Handle keyboard shortcuts for dropdown
|
|
112
|
+
const handleTextAreaKeyDown = (event) => {
|
|
113
|
+
// Ctrl/Cmd + Space to open dropdown
|
|
114
|
+
if ((event.ctrlKey || event.metaKey) && event.code === "Space") {
|
|
115
|
+
event.preventDefault();
|
|
116
|
+
handleSearchClick();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const handleSearchClick = () => {
|
|
120
|
+
if (selectBoxRef.current) {
|
|
121
|
+
const selectInput = selectBoxRef.current.querySelector("input");
|
|
122
|
+
if (selectInput) {
|
|
123
|
+
selectInput.focus();
|
|
124
|
+
selectInput.click();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
// Used to create red border when zod validation detects errors
|
|
129
|
+
const hasError = Boolean(error);
|
|
130
|
+
return (_jsxs(CombinedInputWrapper, { children: [label && (_jsx(StyledFieldLabel, { asterisk: required, error: error, description: description, actionComponent: _jsx(DropDownMenu, { data: [
|
|
131
|
+
{
|
|
132
|
+
value: "search",
|
|
133
|
+
label: "Select Recent Entry",
|
|
134
|
+
visible: true,
|
|
135
|
+
},
|
|
136
|
+
], variant: "text", size: "xs", arrow: false, onItemSelect: (item) => {
|
|
137
|
+
if (item.value === "search") {
|
|
138
|
+
handleSearchClick();
|
|
139
|
+
}
|
|
140
|
+
}, buttonProps: {
|
|
141
|
+
style: {
|
|
142
|
+
minWidth: "auto",
|
|
143
|
+
border: "none",
|
|
144
|
+
background: "transparent",
|
|
145
|
+
padding: "0",
|
|
146
|
+
margin: "0",
|
|
147
|
+
height: "16px",
|
|
148
|
+
width: "16px",
|
|
149
|
+
},
|
|
150
|
+
}, children: _jsx(MoreHorizontal, { size: 16 }) }), children: label })), _jsx(TextAreaRow, { children: _jsx(StyledTextArea, Object.assign({ placeholder: textAreaPlaceholder || "Enter details about the move", "$minRows": textAreaMinRows, "$maxRows": textAreaMaxRows, "$hasError": hasError, value: textAreaValue, onChange: handleTextAreaChange, onKeyDown: handleTextAreaKeyDown }, textAreaProps)) }), _jsx(SelectBoxWrapper, { ref: selectBoxRef, children: _jsx(StyledSelectBox, Object.assign({ data: data, searchable: searchable, allowCustomValue: allowCustomValue, arrow: false, value: selectValue || undefined, onChange: handleSelectChange, hasError: hasError }, selectProps), selectKey) })] }));
|
|
151
|
+
}
|
|
152
|
+
SearchableTextArea.displayName = "SearchableTextArea";
|
|
153
|
+
export default SearchableTextArea;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./SearchableTextArea";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { SelectBoxProps } from "..";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}>> & string;
|
|
2
|
+
import { StyledInputContainer } from "./select-box.styled-components";
|
|
3
|
+
export { StyledInputContainer };
|
|
5
4
|
export declare const SelectBox: React.FC<SelectBoxProps>;
|
|
@@ -1,253 +1,69 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
-
t[p] = s[p];
|
|
5
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
-
t[p[i]] = s[p[i]];
|
|
9
|
-
}
|
|
10
|
-
return t;
|
|
11
|
-
};
|
|
12
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
|
-
import styled, { useTheme } from "styled-components";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTheme } from "styled-components";
|
|
14
3
|
import { useFloating, flip, offset, FloatingPortal, autoUpdate, } from "@floating-ui/react";
|
|
15
|
-
import { useCallback, useEffect, useRef, useState
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
16
5
|
import { Input, FieldLabel, Tooltip, Loader } from "..";
|
|
17
6
|
import { useDebouncedCallback } from "use-debounce";
|
|
18
7
|
import { StyledContent, StyledFloatContainer, ArrowButton, ClearButton, } from "../core";
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
flex-direction: row;
|
|
25
|
-
border: none;
|
|
26
|
-
outline: none;
|
|
27
|
-
height: fit-content;
|
|
28
|
-
|
|
29
|
-
width: ${({ width }) => {
|
|
30
|
-
if (typeof width === "undefined")
|
|
31
|
-
return "100%";
|
|
32
|
-
if (width === null)
|
|
33
|
-
return "100%";
|
|
34
|
-
if (typeof width === "string")
|
|
35
|
-
return width;
|
|
36
|
-
if (typeof width === "number")
|
|
37
|
-
return `${width}px`;
|
|
38
|
-
}};
|
|
39
|
-
|
|
40
|
-
&[data-disabled="true"] {
|
|
41
|
-
opacity: 0.5;
|
|
42
|
-
pointer-events: none;
|
|
43
|
-
|
|
44
|
-
> * {
|
|
45
|
-
pointer-events: none;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
`;
|
|
49
|
-
const StyledInnerItemContainer = styled.div `
|
|
50
|
-
overflow-y: auto;
|
|
51
|
-
|
|
52
|
-
&[data-scroll-active="true"] {
|
|
53
|
-
padding-right: 5px;
|
|
54
|
-
}
|
|
55
|
-
`;
|
|
56
|
-
const EmptyComponent = styled.div `
|
|
57
|
-
display: flex;
|
|
58
|
-
align-items: center;
|
|
59
|
-
justify-content: center;
|
|
60
|
-
padding: 5px;
|
|
61
|
-
font-size: 12px;
|
|
62
|
-
font-weight: 500;
|
|
63
|
-
color: ${(props) => props.theme.palette.text.secondary};
|
|
64
|
-
`;
|
|
65
|
-
const GroupTitle = styled((_a) => {
|
|
66
|
-
var { className, children } = _a, props = __rest(_a, ["className", "children"]);
|
|
67
|
-
return (_jsxs("div", Object.assign({ className: className }, props, { children: [_jsx("div", { className: "group-line" }), _jsx("div", { className: "group-label", children: children }), _jsx("div", { className: "group-line" })] })));
|
|
68
|
-
}) `
|
|
69
|
-
display: flex;
|
|
70
|
-
flex-direction: row;
|
|
71
|
-
align-items: center;
|
|
72
|
-
justify-content: space-between;
|
|
73
|
-
gap: 10px;
|
|
74
|
-
|
|
75
|
-
color: ${(props) => props.theme.palette.text.secondary};
|
|
76
|
-
|
|
77
|
-
padding: ${({ size }) => size === "xs"
|
|
78
|
-
? "2px 8px"
|
|
79
|
-
: size === "sm"
|
|
80
|
-
? "4px 10px"
|
|
81
|
-
: size === "md"
|
|
82
|
-
? "4px 12px"
|
|
83
|
-
: size === "lg"
|
|
84
|
-
? "5px 14px"
|
|
85
|
-
: size === "xl"
|
|
86
|
-
? "6px 16px"
|
|
87
|
-
: "2px 8px"};
|
|
88
|
-
|
|
89
|
-
.group-label {
|
|
90
|
-
white-space: nowrap;
|
|
91
|
-
overflow: hidden;
|
|
92
|
-
text-overflow: ellipsis;
|
|
93
|
-
width: fit-content;
|
|
94
|
-
min-width: fit-content;
|
|
95
|
-
|
|
96
|
-
font-weight: 500;
|
|
97
|
-
|
|
98
|
-
font-size: ${({ size }) => size === "xs"
|
|
99
|
-
? "11px"
|
|
100
|
-
: size === "sm"
|
|
101
|
-
? "13px"
|
|
102
|
-
: size === "md"
|
|
103
|
-
? "15px"
|
|
104
|
-
: size === "lg"
|
|
105
|
-
? "17px"
|
|
106
|
-
: size === "xl"
|
|
107
|
-
? "19px"
|
|
108
|
-
: "11px"};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
.group-line {
|
|
112
|
-
border-top: 1px solid ${(props) => props.theme.palette.divider};
|
|
113
|
-
width: 100%;
|
|
114
|
-
}
|
|
115
|
-
`;
|
|
116
|
-
const ActionMenu = styled.div ``;
|
|
117
|
-
const StyledItem = styled.div `
|
|
118
|
-
color: ${(props) => props.theme.palette.text.primary};
|
|
119
|
-
border-radius: 3px;
|
|
120
|
-
display: flex;
|
|
121
|
-
align-items: center;
|
|
122
|
-
min-height: 25px;
|
|
123
|
-
padding: 7px 10px;
|
|
124
|
-
position: relative;
|
|
125
|
-
user-select: none;
|
|
126
|
-
outline: none;
|
|
127
|
-
|
|
128
|
-
cursor: pointer;
|
|
129
|
-
|
|
130
|
-
font-family: ${({ theme }) => theme.typography.fontFamily};
|
|
131
|
-
|
|
132
|
-
font-size: ${({ size }) => size === "xs"
|
|
133
|
-
? "11px"
|
|
134
|
-
: size === "sm"
|
|
135
|
-
? "13px"
|
|
136
|
-
: size === "md"
|
|
137
|
-
? "15px"
|
|
138
|
-
: size === "lg"
|
|
139
|
-
? "17px"
|
|
140
|
-
: size === "xl"
|
|
141
|
-
? "19px"
|
|
142
|
-
: "11px"};
|
|
143
|
-
|
|
144
|
-
padding: ${({ size }) => size === "xs"
|
|
145
|
-
? "2px 8px"
|
|
146
|
-
: size === "sm"
|
|
147
|
-
? "4px 10px"
|
|
148
|
-
: size === "md"
|
|
149
|
-
? "4px 12px"
|
|
150
|
-
: size === "lg"
|
|
151
|
-
? "5px 14px"
|
|
152
|
-
: size === "xl"
|
|
153
|
-
? "6px 16px"
|
|
154
|
-
: "2px 8px"};
|
|
155
|
-
|
|
156
|
-
&:hover {
|
|
157
|
-
background-color: ${(props) => props.theme.palette.action.hover};
|
|
158
|
-
color: ${(props) => props.theme.palette.text.primary};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Custom prop, checks if item is selected and returns true
|
|
162
|
-
&[data-selected="true"] {
|
|
163
|
-
background-color: ${(props) => props.theme.palette.divider};
|
|
164
|
-
color: ${(props) => props.theme.palette.text.primary};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
&[data-disabled] {
|
|
168
|
-
color: ${(props) => props.theme.palette.text.secondary};
|
|
169
|
-
opacity: 0.5;
|
|
170
|
-
pointer-events: none;
|
|
171
|
-
}
|
|
172
|
-
`;
|
|
173
|
-
/**
|
|
174
|
-
*
|
|
175
|
-
* Attempts to resolve the value to an Option object
|
|
176
|
-
* if the value is a string, it will attempt to find the corresponding Option object
|
|
177
|
-
* if the value is an Option object, it will attempt to find the corresponding Option object
|
|
178
|
-
* if the value is not found, it will return the value as is so that custom values can be displayed without a lookup
|
|
179
|
-
*/
|
|
180
|
-
const resolveValue = (value, data) => {
|
|
181
|
-
let foundOption;
|
|
182
|
-
if (value === undefined)
|
|
183
|
-
return undefined;
|
|
184
|
-
if (value === null)
|
|
185
|
-
return null;
|
|
186
|
-
if (typeof value === "string") {
|
|
187
|
-
foundOption = data.find((item) => item.value === value);
|
|
188
|
-
}
|
|
189
|
-
else if (typeof value === "number") {
|
|
190
|
-
foundOption = data.find((item) => item.value === value);
|
|
191
|
-
}
|
|
192
|
-
else if (typeof value === "object") {
|
|
193
|
-
foundOption = data.find((item) => item.value == value.value);
|
|
194
|
-
}
|
|
195
|
-
if (foundOption)
|
|
196
|
-
return foundOption;
|
|
197
|
-
if (typeof value === "string") {
|
|
198
|
-
return { value, label: value };
|
|
199
|
-
}
|
|
200
|
-
if (typeof value === "number") {
|
|
201
|
-
return { value, label: value.toString() };
|
|
202
|
-
}
|
|
203
|
-
return value;
|
|
204
|
-
};
|
|
205
|
-
const StyledContainer = styled.div `
|
|
206
|
-
position: relative;
|
|
207
|
-
cursor: pointer;
|
|
208
|
-
width: 100%;
|
|
209
|
-
`;
|
|
210
|
-
export const SelectBox = ({ className, data = [], placeholder = "Select...", arrow = true, onChange, onSearch, searchFn, onScroll, loading, defaultValue, value, onItemAdded, size = "sm", variant = "outlined", width = "100%", allowCustomValue = false, searchable = false, clearable = false, label, description, required = false, error, openOnFocus = true, renderOption, actionComponent, focused, grouped, OptionTooltip, // Custom tooltip component for search menu items
|
|
8
|
+
import { DEFAULT_DROPDOWN_HEIGHT, DROPDOWN_OFFSET, getEmptyMessage, KEYBOARD_KEYS, resolveValue, } from "./select-box.utils";
|
|
9
|
+
import { StyledInputContainer, StyledInnerItemContainer, EmptyComponent, GroupTitle, ActionMenu, StyledItem, StyledContainer, } from "./select-box.styled-components";
|
|
10
|
+
// Re-export for backward compatibility
|
|
11
|
+
export { StyledInputContainer };
|
|
12
|
+
export const SelectBox = ({ className, data = [], placeholder = "Select...", arrow = true, onChange, onSearch, searchFn, onScroll, loading, defaultValue, value, size = "sm", variant = "outlined", width = "100%", allowCustomValue = false, searchable = false, clearable = false, label, description, required = false, error, openOnFocus = true, renderOption, actionComponent, focused, grouped, OptionTooltip, // Custom tooltip component for search menu items
|
|
211
13
|
DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
|
|
212
|
-
var _a, _b, _c, _d, _e, _f
|
|
14
|
+
var _a, _b, _c, _d, _e, _f;
|
|
15
|
+
// Component setup and data processing
|
|
213
16
|
const theme = useTheme();
|
|
214
|
-
|
|
215
|
-
|
|
17
|
+
// Type guard to check if an item is an Option object
|
|
18
|
+
const isOption = (item) => {
|
|
19
|
+
return (typeof item === "object" &&
|
|
20
|
+
item !== null &&
|
|
21
|
+
typeof item.label === "string" &&
|
|
22
|
+
item.value !== undefined);
|
|
23
|
+
};
|
|
216
24
|
const isControlled = useRef(value !== undefined);
|
|
217
|
-
const resolvedOptions = data.map((item) =>
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
25
|
+
const resolvedOptions = data.map((item) => {
|
|
26
|
+
if (isOption(item)) {
|
|
27
|
+
return {
|
|
28
|
+
value: item.value,
|
|
29
|
+
label: item.label,
|
|
30
|
+
group: item.group,
|
|
31
|
+
disabled: item.disabled,
|
|
32
|
+
data: item.data,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Treat as string
|
|
37
|
+
const stringValue = item;
|
|
38
|
+
return {
|
|
39
|
+
value: stringValue,
|
|
40
|
+
label: stringValue,
|
|
41
|
+
group: undefined,
|
|
42
|
+
disabled: undefined,
|
|
43
|
+
data: undefined,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
});
|
|
229
47
|
const resolvedDefaultValue = resolveValue(defaultValue, resolvedOptions);
|
|
230
|
-
/**
|
|
231
|
-
* Get the correct option if the value is a string
|
|
232
|
-
* otherwise, use the value as is
|
|
233
|
-
* this allows setting the value with a string or Option object
|
|
234
|
-
*/
|
|
235
48
|
const resolvedValue = resolveValue(value, resolvedOptions);
|
|
49
|
+
// Value and input state
|
|
236
50
|
const [valueState, setValueState] = useState(resolvedDefaultValue);
|
|
237
|
-
/**
|
|
238
|
-
* Get the correct value based on whether the component is controlled or not
|
|
239
|
-
* if controlled, use the controlled value, otherwise use the resolved value
|
|
240
|
-
*/
|
|
241
51
|
const _value = isControlled.current ? resolvedValue : valueState;
|
|
242
52
|
const [inputValue, setInputValue] = useState(typeof _value === "object" ? (_value === null || _value === void 0 ? void 0 : _value.label) || "" : _value || "");
|
|
53
|
+
// Dropdown state
|
|
243
54
|
const [isOpen, setIsOpen] = useState(false);
|
|
244
|
-
const [searchValue, setSearchValue] = useState("");
|
|
245
|
-
const [customItems, setCustomItems] = useState([]);
|
|
246
55
|
const [placement, setPlacement] = useState("bottom-start");
|
|
247
56
|
const [dropDownHeight, setDropDownHeight] = useState(null);
|
|
57
|
+
// Search and custom items state
|
|
58
|
+
const [searchValue, setSearchValue] = useState("");
|
|
59
|
+
const [customItems, setCustomItems] = useState([]);
|
|
60
|
+
// Refs
|
|
248
61
|
const inputRef = useRef(null);
|
|
249
62
|
const containerRef = useRef(null);
|
|
250
63
|
const scrollContainerRef = useRef(null);
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// Data Processing and Filtering
|
|
66
|
+
// ============================================================================
|
|
251
67
|
// Remove duplicates from search dropdown data
|
|
252
68
|
const uniqueItems = Array.from(new Map([...resolvedOptions, ...customItems].map((item) => [item.value, item])).values());
|
|
253
69
|
// search filter for dropdown items
|
|
@@ -293,6 +109,9 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
|
|
|
293
109
|
}))
|
|
294
110
|
.sort((a, b) => a.label.localeCompare(b.label))
|
|
295
111
|
: [];
|
|
112
|
+
// ============================================================================
|
|
113
|
+
// Floating UI Setup
|
|
114
|
+
// ============================================================================
|
|
296
115
|
const { refs, floatingStyles, update } = useFloating({
|
|
297
116
|
open: isOpen,
|
|
298
117
|
onOpenChange: setIsOpen,
|
|
@@ -302,6 +121,9 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
|
|
|
302
121
|
middleware: [flip(), offset(5)],
|
|
303
122
|
whileElementsMounted: autoUpdate,
|
|
304
123
|
});
|
|
124
|
+
// ============================================================================
|
|
125
|
+
// Event Handlers
|
|
126
|
+
// ============================================================================
|
|
305
127
|
const toggleOpen = () => {
|
|
306
128
|
setIsOpen((prev) => {
|
|
307
129
|
if (!prev) {
|
|
@@ -369,15 +191,10 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
|
|
|
369
191
|
setInputValue((option === null || option === void 0 ? void 0 : option.label) || "");
|
|
370
192
|
onChange === null || onChange === void 0 ? void 0 : onChange(option === null || option === void 0 ? void 0 : option.value, option);
|
|
371
193
|
}, [onChange]);
|
|
372
|
-
const handleAddItem = useCallback((newItem) => {
|
|
373
|
-
setCustomItems((prev) => [...prev, newItem]);
|
|
374
|
-
onItemAdded === null || onItemAdded === void 0 ? void 0 : onItemAdded(newItem);
|
|
375
|
-
}, [onItemAdded, isObjectArray, handleChangeSelection]);
|
|
376
194
|
const handleKeyDown = (e) => {
|
|
377
195
|
var _a;
|
|
378
|
-
const currentInputValue = inputValue;
|
|
379
196
|
// Escape key
|
|
380
|
-
if (e.key ===
|
|
197
|
+
if (e.key === KEYBOARD_KEYS.ESCAPE) {
|
|
381
198
|
const reference = (_a = refs === null || refs === void 0 ? void 0 : refs.reference) === null || _a === void 0 ? void 0 : _a.current;
|
|
382
199
|
reference === null || reference === void 0 ? void 0 : reference.blur();
|
|
383
200
|
setSearchValue("");
|
|
@@ -386,12 +203,12 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
|
|
|
386
203
|
return;
|
|
387
204
|
}
|
|
388
205
|
// Enter key
|
|
389
|
-
if (e.key ===
|
|
206
|
+
if (e.key === KEYBOARD_KEYS.ENTER) {
|
|
390
207
|
toggleOpen();
|
|
391
208
|
return;
|
|
392
209
|
}
|
|
393
210
|
// Arrow down
|
|
394
|
-
if (e.key ===
|
|
211
|
+
if (e.key === KEYBOARD_KEYS.ARROW_DOWN) {
|
|
395
212
|
e.preventDefault();
|
|
396
213
|
let newItem = filteredItems[0]; // Loop back to the first item
|
|
397
214
|
// Find the index of the currently selected item
|
|
@@ -410,7 +227,7 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
|
|
|
410
227
|
return;
|
|
411
228
|
}
|
|
412
229
|
// Arrow up
|
|
413
|
-
if (e.key ===
|
|
230
|
+
if (e.key === KEYBOARD_KEYS.ARROW_UP) {
|
|
414
231
|
e.preventDefault();
|
|
415
232
|
let newItem = filteredItems[filteredItems.length - 1]; // Loop back to the last item
|
|
416
233
|
// Find the index of the currently selected item
|
|
@@ -432,7 +249,7 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
|
|
|
432
249
|
return;
|
|
433
250
|
}
|
|
434
251
|
// Tab key
|
|
435
|
-
if (e.key ===
|
|
252
|
+
if (e.key === KEYBOARD_KEYS.TAB) {
|
|
436
253
|
if (!_value)
|
|
437
254
|
setInputValue("");
|
|
438
255
|
setSearchValue("");
|
|
@@ -449,19 +266,50 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
|
|
|
449
266
|
if (searchable) {
|
|
450
267
|
setSearchValue(value);
|
|
451
268
|
update();
|
|
452
|
-
onSearch === null || onSearch === void 0 ? void 0 : onSearch(
|
|
269
|
+
onSearch === null || onSearch === void 0 ? void 0 : onSearch(value);
|
|
453
270
|
}
|
|
454
271
|
};
|
|
455
|
-
|
|
456
|
-
|
|
272
|
+
// ============================================================================
|
|
273
|
+
// Render Helper Functions
|
|
274
|
+
// ============================================================================
|
|
275
|
+
const renderOptionItem = (item, index) => (_jsx(Tooltip, { content: OptionTooltip ? _jsx(OptionTooltip, { data: item.data }) : null, side: "left", children: _jsx(StyledItem, { className: "mfFloatingItem", onClick: (e) => handleItemClick(e, item), "data-selected": (_value === null || _value === void 0 ? void 0 : _value.value) === item.value, "data-disabled": item.disabled, size: size, children: (renderOption === null || renderOption === void 0 ? void 0 : renderOption(item)) || _jsx(_Fragment, { children: item === null || item === void 0 ? void 0 : item.label }) }, index) }, index));
|
|
276
|
+
const renderActionButton = () => {
|
|
277
|
+
if (clearable && (_value || !!inputValue)) {
|
|
278
|
+
return (_jsx(ClearButton, { className: "input-btn", onClick: handleClear, onMouseDown: (e) => {
|
|
279
|
+
e.preventDefault();
|
|
280
|
+
e.stopPropagation();
|
|
281
|
+
} }));
|
|
282
|
+
}
|
|
283
|
+
if (arrow) {
|
|
284
|
+
return (_jsx(ArrowButton, { onClick: (e) => {
|
|
285
|
+
e.preventDefault();
|
|
286
|
+
}, onMouseDown: (e) => {
|
|
287
|
+
e.preventDefault();
|
|
288
|
+
e.stopPropagation();
|
|
289
|
+
toggleOpen();
|
|
290
|
+
} }));
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
};
|
|
294
|
+
// ============================================================================
|
|
295
|
+
// Computed Values
|
|
296
|
+
// ============================================================================
|
|
297
|
+
const referenceEl = (_a = refs === null || refs === void 0 ? void 0 : refs.reference) === null || _a === void 0 ? void 0 : _a.current;
|
|
298
|
+
const contentWidth = ((_d = (_c = (_b = referenceEl === null || referenceEl === void 0 ? void 0 : referenceEl.getClientRects) === null || _b === void 0 ? void 0 : _b.call(referenceEl)) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.width) || "100%";
|
|
457
299
|
const scrollEl = scrollContainerRef === null || scrollContainerRef === void 0 ? void 0 : scrollContainerRef.current;
|
|
458
300
|
const scrollActive = scrollEl
|
|
459
301
|
? (scrollEl === null || scrollEl === void 0 ? void 0 : scrollEl.scrollHeight) > (scrollEl === null || scrollEl === void 0 ? void 0 : scrollEl.clientHeight)
|
|
460
302
|
: false;
|
|
461
303
|
// get height between bottom of the floating container and the bottom of the viewport
|
|
462
|
-
const bottomHeight = window.innerHeight -
|
|
304
|
+
const bottomHeight = window.innerHeight -
|
|
305
|
+
((_e = referenceEl === null || referenceEl === void 0 ? void 0 : referenceEl.getBoundingClientRect()) === null || _e === void 0 ? void 0 : _e.bottom) -
|
|
306
|
+
DROPDOWN_OFFSET;
|
|
463
307
|
// get height between top of the floating container and the top of the viewport
|
|
464
|
-
const topHeight = (((
|
|
308
|
+
const topHeight = (((_f = referenceEl === null || referenceEl === void 0 ? void 0 : referenceEl.getBoundingClientRect()) === null || _f === void 0 ? void 0 : _f.top) -
|
|
309
|
+
DROPDOWN_OFFSET);
|
|
310
|
+
// ============================================================================
|
|
311
|
+
// Effects
|
|
312
|
+
// ============================================================================
|
|
465
313
|
// Close on outside click
|
|
466
314
|
useEffect(() => {
|
|
467
315
|
const close = (e) => {
|
|
@@ -480,15 +328,6 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
|
|
|
480
328
|
document.addEventListener("click", close);
|
|
481
329
|
return () => document.removeEventListener("click", close);
|
|
482
330
|
}, [refs.floating, refs.reference, handleBlur]);
|
|
483
|
-
// Sync inputValue with controlled value changes
|
|
484
|
-
useEffect(() => {
|
|
485
|
-
if (isControlled.current && resolvedValue !== undefined) {
|
|
486
|
-
const newLabel = typeof resolvedValue === "object"
|
|
487
|
-
? (resolvedValue === null || resolvedValue === void 0 ? void 0 : resolvedValue.label) || ""
|
|
488
|
-
: resolvedValue || "";
|
|
489
|
-
setInputValue(newLabel);
|
|
490
|
-
}
|
|
491
|
-
}, [resolvedValue, isControlled]);
|
|
492
331
|
// handle scroll item into view
|
|
493
332
|
useEffect(() => {
|
|
494
333
|
var _a, _b;
|
|
@@ -498,7 +337,7 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
|
|
|
498
337
|
}
|
|
499
338
|
}, [_value]);
|
|
500
339
|
useEffect(() => {
|
|
501
|
-
if (bottomHeight <
|
|
340
|
+
if (bottomHeight < DEFAULT_DROPDOWN_HEIGHT) {
|
|
502
341
|
setPlacement("top-start");
|
|
503
342
|
setDropDownHeight(topHeight);
|
|
504
343
|
}
|
|
@@ -511,35 +350,22 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
|
|
|
511
350
|
setDropDownHeight(bottomHeight);
|
|
512
351
|
};
|
|
513
352
|
}, [topHeight, bottomHeight, isOpen]);
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
e.preventDefault();
|
|
519
|
-
}, onMouseDown: (e) => {
|
|
520
|
-
e.preventDefault();
|
|
521
|
-
e.stopPropagation();
|
|
522
|
-
toggleOpen();
|
|
523
|
-
} })) : null] }), isOpen && (_jsx(FloatingPortal, { preserveTabOrder: true, children: _jsx(StyledFloatContainer, { ref: (ref) => {
|
|
353
|
+
// ============================================================================
|
|
354
|
+
// Render
|
|
355
|
+
// ============================================================================
|
|
356
|
+
return (_jsxs(StyledContainer, { className: className, children: [label && (_jsx(FieldLabel, { error: error, asterisk: required, size: size, description: description, children: label })), _jsxs(StyledInputContainer, { ref: refs.setReference, onMouseDown: () => setIsOpen(true), width: width, onKeyDown: handleKeyDown, "data-open": isOpen, "data-disabled": disabled, children: [_jsx(Input, { ref: inputRef, value: inputValue, onChange: handleInputChange, onFocus: handleFocus, autoFocus: focused, placeholder: placeholder, size: size, readOnly: !searchable && !allowCustomValue, "data-button-right": arrow || clearable, style: isOpen ? { borderColor: theme.palette.primary.main } : {} }), renderActionButton()] }), isOpen && (_jsx(FloatingPortal, { preserveTabOrder: true, children: _jsx(StyledFloatContainer, { ref: (ref) => {
|
|
524
357
|
containerRef.current = ref;
|
|
525
358
|
refs.setFloating(ref);
|
|
526
359
|
}, style: floatingStyles, className: "mfFloating", children: _jsxs(StyledContent, Object.assign({ className: "mfFloatingContent", style: {
|
|
527
360
|
width: contentWidth,
|
|
528
361
|
maxWidth: contentWidth,
|
|
529
362
|
maxHeight: DropDownProps.autoHeight
|
|
530
|
-
? (dropDownHeight ||
|
|
363
|
+
? (dropDownHeight || DEFAULT_DROPDOWN_HEIGHT) -
|
|
364
|
+
DROPDOWN_OFFSET
|
|
531
365
|
: "",
|
|
532
366
|
}, variant: variant, "data-empty": filteredItems.length === 0 }, DropDownProps, { children: [loading && _jsx(Loader, {}), !loading && actionComponent && (_jsx(ActionMenu, { onClick: () => {
|
|
533
367
|
setIsOpen(false);
|
|
534
|
-
}, children: actionComponent })), !loading && filteredItems.length === 0 && (_jsx(EmptyComponent, { children: allowCustomValue &&
|
|
535
|
-
?
|
|
536
|
-
:
|
|
537
|
-
? `No items found`
|
|
538
|
-
: "No items" })), !loading && (_jsx(StyledInnerItemContainer, { ref: scrollContainerRef, "data-scroll-active": scrollActive, onScroll: onScroll, children: grouped
|
|
539
|
-
? groups.map((group, index) => (_jsxs("div", { children: [_jsx(GroupTitle, { size: size, children: group.label }), group.items.map((item, index) => {
|
|
540
|
-
return (_jsx(Tooltip, { content: OptionTooltip ? (_jsx(OptionTooltip, { data: item.data })) : null, side: "left", children: _jsx(StyledItem, { className: "mfFloatingItem", onClick: (e) => handleItemClick(e, item), "data-selected": (_value === null || _value === void 0 ? void 0 : _value.value) === item.value, "data-disabled": item.disabled, size: size, children: (renderOption === null || renderOption === void 0 ? void 0 : renderOption(item)) || _jsx(_Fragment, { children: item === null || item === void 0 ? void 0 : item.label }) }, index) }, index));
|
|
541
|
-
})] }, group.label)))
|
|
542
|
-
: filteredItems.map((item, index) => {
|
|
543
|
-
return (_jsx(Tooltip, { content: OptionTooltip ? (_jsx(OptionTooltip, { data: item.data })) : null, side: "left", children: _jsx(StyledItem, { className: "mfFloatingItem", onClick: (e) => handleItemClick(e, item), "data-selected": (_value === null || _value === void 0 ? void 0 : _value.value) === item.value, "data-disabled": item.disabled, size: size, children: (renderOption === null || renderOption === void 0 ? void 0 : renderOption(item)) || _jsx(_Fragment, { children: item === null || item === void 0 ? void 0 : item.label }) }, index) }, index));
|
|
544
|
-
}) }))] })) }) }))] }));
|
|
368
|
+
}, children: actionComponent })), !loading && filteredItems.length === 0 && (_jsx(EmptyComponent, { children: getEmptyMessage(allowCustomValue, searchValue) })), !loading && (_jsx(StyledInnerItemContainer, { ref: scrollContainerRef, "data-scroll-active": scrollActive, onScroll: onScroll, children: grouped
|
|
369
|
+
? groups.map((group, index) => (_jsxs("div", { children: [_jsx(GroupTitle, { size: size, children: group.label }), group.items.map((item, index) => renderOptionItem(item, index))] }, group.label)))
|
|
370
|
+
: filteredItems.map((item, index) => renderOptionItem(item, index)) }))] })) }) }))] }));
|
|
545
371
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { Size } from "../core";
|
|
3
|
+
export declare const StyledInputContainer: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
4
|
+
width?: string | number | null;
|
|
5
|
+
}>> & string;
|
|
6
|
+
export declare const StyledInnerItemContainer: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
7
|
+
export declare const EmptyComponent: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
8
|
+
interface GroupTitleProps {
|
|
9
|
+
className?: string;
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
size?: Size;
|
|
12
|
+
}
|
|
13
|
+
export declare const GroupTitle: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<GroupTitleProps, never>> & string & Omit<({ className, children, ...props }: GroupTitleProps) => import("react/jsx-runtime").JSX.Element, keyof import("react").Component<any, {}, any>>;
|
|
14
|
+
export declare const ActionMenu: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
15
|
+
export declare const StyledItem: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
16
|
+
size: Size;
|
|
17
|
+
}>> & string;
|
|
18
|
+
export declare const StyledContainer: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
import styled from "styled-components";
|
|
14
|
+
export const StyledInputContainer = styled.div `
|
|
15
|
+
font-family: ${({ theme }) => theme.typography.fontFamily};
|
|
16
|
+
|
|
17
|
+
position: relative;
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: row;
|
|
20
|
+
border: none;
|
|
21
|
+
outline: none;
|
|
22
|
+
height: fit-content;
|
|
23
|
+
|
|
24
|
+
width: ${({ width }) => {
|
|
25
|
+
if (typeof width === "undefined")
|
|
26
|
+
return "100%";
|
|
27
|
+
if (width === null)
|
|
28
|
+
return "100%";
|
|
29
|
+
if (typeof width === "string")
|
|
30
|
+
return width;
|
|
31
|
+
if (typeof width === "number")
|
|
32
|
+
return `${width}px`;
|
|
33
|
+
}};
|
|
34
|
+
|
|
35
|
+
&[data-disabled="true"] {
|
|
36
|
+
opacity: 0.5;
|
|
37
|
+
pointer-events: none;
|
|
38
|
+
|
|
39
|
+
> * {
|
|
40
|
+
pointer-events: none;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
export const StyledInnerItemContainer = styled.div `
|
|
45
|
+
overflow-y: auto;
|
|
46
|
+
|
|
47
|
+
&[data-scroll-active="true"] {
|
|
48
|
+
padding-right: 5px;
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
export const EmptyComponent = styled.div `
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
justify-content: center;
|
|
55
|
+
padding: 5px;
|
|
56
|
+
font-size: 12px;
|
|
57
|
+
font-weight: 500;
|
|
58
|
+
color: ${(props) => props.theme.palette.text.secondary};
|
|
59
|
+
`;
|
|
60
|
+
export const GroupTitle = styled((_a) => {
|
|
61
|
+
var { className, children } = _a, props = __rest(_a, ["className", "children"]);
|
|
62
|
+
return (_jsxs("div", Object.assign({ className: className }, props, { children: [_jsx("div", { className: "group-line" }), _jsx("div", { className: "group-label", children: children }), _jsx("div", { className: "group-line" })] })));
|
|
63
|
+
}) `
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-direction: row;
|
|
66
|
+
align-items: center;
|
|
67
|
+
justify-content: space-between;
|
|
68
|
+
gap: 10px;
|
|
69
|
+
|
|
70
|
+
color: ${(props) => props.theme.palette.text.secondary};
|
|
71
|
+
|
|
72
|
+
padding: ${({ size }) => size === "xs"
|
|
73
|
+
? "2px 8px"
|
|
74
|
+
: size === "sm"
|
|
75
|
+
? "4px 10px"
|
|
76
|
+
: size === "md"
|
|
77
|
+
? "4px 12px"
|
|
78
|
+
: size === "lg"
|
|
79
|
+
? "5px 14px"
|
|
80
|
+
: size === "xl"
|
|
81
|
+
? "6px 16px"
|
|
82
|
+
: "2px 8px"};
|
|
83
|
+
|
|
84
|
+
.group-label {
|
|
85
|
+
white-space: nowrap;
|
|
86
|
+
overflow: hidden;
|
|
87
|
+
text-overflow: ellipsis;
|
|
88
|
+
width: fit-content;
|
|
89
|
+
min-width: fit-content;
|
|
90
|
+
|
|
91
|
+
font-weight: 500;
|
|
92
|
+
|
|
93
|
+
font-size: ${({ size }) => size === "xs"
|
|
94
|
+
? "11px"
|
|
95
|
+
: size === "sm"
|
|
96
|
+
? "13px"
|
|
97
|
+
: size === "md"
|
|
98
|
+
? "15px"
|
|
99
|
+
: size === "lg"
|
|
100
|
+
? "17px"
|
|
101
|
+
: size === "xl"
|
|
102
|
+
? "19px"
|
|
103
|
+
: "11px"};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.group-line {
|
|
107
|
+
border-top: 1px solid ${(props) => props.theme.palette.divider};
|
|
108
|
+
width: 100%;
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
export const ActionMenu = styled.div ``;
|
|
112
|
+
export const StyledItem = styled.div `
|
|
113
|
+
color: ${(props) => props.theme.palette.text.primary};
|
|
114
|
+
border-radius: 3px;
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
min-height: 25px;
|
|
118
|
+
padding: 7px 10px;
|
|
119
|
+
position: relative;
|
|
120
|
+
user-select: none;
|
|
121
|
+
outline: none;
|
|
122
|
+
|
|
123
|
+
cursor: pointer;
|
|
124
|
+
|
|
125
|
+
font-family: ${({ theme }) => theme.typography.fontFamily};
|
|
126
|
+
|
|
127
|
+
font-size: ${({ size }) => size === "xs"
|
|
128
|
+
? "11px"
|
|
129
|
+
: size === "sm"
|
|
130
|
+
? "13px"
|
|
131
|
+
: size === "md"
|
|
132
|
+
? "15px"
|
|
133
|
+
: size === "lg"
|
|
134
|
+
? "17px"
|
|
135
|
+
: size === "xl"
|
|
136
|
+
? "19px"
|
|
137
|
+
: "11px"};
|
|
138
|
+
|
|
139
|
+
padding: ${({ size }) => size === "xs"
|
|
140
|
+
? "2px 8px"
|
|
141
|
+
: size === "sm"
|
|
142
|
+
? "4px 10px"
|
|
143
|
+
: size === "md"
|
|
144
|
+
? "4px 12px"
|
|
145
|
+
: size === "lg"
|
|
146
|
+
? "5px 14px"
|
|
147
|
+
: size === "xl"
|
|
148
|
+
? "6px 16px"
|
|
149
|
+
: "2px 8px"};
|
|
150
|
+
|
|
151
|
+
&:hover {
|
|
152
|
+
background-color: ${(props) => props.theme.palette.action.hover};
|
|
153
|
+
color: ${(props) => props.theme.palette.text.primary};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Custom prop, checks if item is selected and returns true
|
|
157
|
+
&[data-selected="true"] {
|
|
158
|
+
background-color: ${(props) => props.theme.palette.divider};
|
|
159
|
+
color: ${(props) => props.theme.palette.text.primary};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
&[data-disabled] {
|
|
163
|
+
color: ${(props) => props.theme.palette.text.secondary};
|
|
164
|
+
opacity: 0.5;
|
|
165
|
+
pointer-events: none;
|
|
166
|
+
}
|
|
167
|
+
`;
|
|
168
|
+
export const StyledContainer = styled.div `
|
|
169
|
+
position: relative;
|
|
170
|
+
cursor: pointer;
|
|
171
|
+
width: 100%;
|
|
172
|
+
`;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Option } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* Attempts to resolve the value to an Option object
|
|
5
|
+
* if the value is a string, it will attempt to find the corresponding Option object
|
|
6
|
+
* if the value is an Option object, it will attempt to find the corresponding Option object
|
|
7
|
+
* if the value is not found, it will return the value as is so that custom values can be displayed without a lookup
|
|
8
|
+
*/
|
|
9
|
+
export declare const KEYBOARD_KEYS: {
|
|
10
|
+
readonly ESCAPE: "Escape";
|
|
11
|
+
readonly ENTER: "Enter";
|
|
12
|
+
readonly ARROW_DOWN: "ArrowDown";
|
|
13
|
+
readonly ARROW_UP: "ArrowUp";
|
|
14
|
+
readonly TAB: "Tab";
|
|
15
|
+
};
|
|
16
|
+
export declare const DEFAULT_DROPDOWN_HEIGHT = 250;
|
|
17
|
+
export declare const DROPDOWN_OFFSET = 10;
|
|
18
|
+
export declare const getEmptyMessage: (allowCustomValue: boolean, searchValue: string) => string;
|
|
19
|
+
export declare const resolveValue: (value: number | string | Option | undefined, data: Option[]) => Option | null | undefined;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Attempts to resolve the value to an Option object
|
|
4
|
+
* if the value is a string, it will attempt to find the corresponding Option object
|
|
5
|
+
* if the value is an Option object, it will attempt to find the corresponding Option object
|
|
6
|
+
* if the value is not found, it will return the value as is so that custom values can be displayed without a lookup
|
|
7
|
+
*/
|
|
8
|
+
// Constants
|
|
9
|
+
export const KEYBOARD_KEYS = {
|
|
10
|
+
ESCAPE: "Escape",
|
|
11
|
+
ENTER: "Enter",
|
|
12
|
+
ARROW_DOWN: "ArrowDown",
|
|
13
|
+
ARROW_UP: "ArrowUp",
|
|
14
|
+
TAB: "Tab",
|
|
15
|
+
};
|
|
16
|
+
export const DEFAULT_DROPDOWN_HEIGHT = 250;
|
|
17
|
+
export const DROPDOWN_OFFSET = 10;
|
|
18
|
+
// Helper functions
|
|
19
|
+
export const getEmptyMessage = (allowCustomValue, searchValue) => {
|
|
20
|
+
if (allowCustomValue && searchValue) {
|
|
21
|
+
return `Add "${searchValue}" as a new item`;
|
|
22
|
+
}
|
|
23
|
+
return searchValue ? "No items found" : "No items";
|
|
24
|
+
};
|
|
25
|
+
export const resolveValue = (value, data) => {
|
|
26
|
+
let foundOption;
|
|
27
|
+
if (value === undefined)
|
|
28
|
+
return undefined;
|
|
29
|
+
if (value === null)
|
|
30
|
+
return null;
|
|
31
|
+
if (typeof value === "object") {
|
|
32
|
+
foundOption = data.find((item) => item.value == value.value);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
foundOption = data.find((item) => item.value === value);
|
|
36
|
+
}
|
|
37
|
+
if (foundOption)
|
|
38
|
+
return foundOption;
|
|
39
|
+
if (typeof value === "string") {
|
|
40
|
+
return { value, label: value };
|
|
41
|
+
}
|
|
42
|
+
if (typeof value === "number") {
|
|
43
|
+
return { value, label: value.toString() };
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type { DropDownItem } from "./DropDownMenu";
|
|
|
14
14
|
export { default as DateInput } from "./DateInput";
|
|
15
15
|
export { default as TextArea } from "./TextArea";
|
|
16
16
|
export { default as TextAreaInput } from "./TextAreaInput";
|
|
17
|
+
export { default as SearchableTextArea } from "./SearchableTextArea";
|
|
18
|
+
export type { SearchableTextAreaProps } from "./SearchableTextArea";
|
|
17
19
|
export { default as TagBox } from "./TagBox";
|
|
18
20
|
export type { TagBoxProps, TagboxOption } from "./TagBox";
|
|
19
21
|
export { default as FieldLabel } from "./FieldLabel";
|
|
@@ -28,8 +30,7 @@ export { default as Tooltip } from "./Tooltip";
|
|
|
28
30
|
export { default as Pill } from "./Pill";
|
|
29
31
|
export { default as Calendar } from "./Calendar";
|
|
30
32
|
export { default as Typography } from "./Typography";
|
|
31
|
-
export
|
|
32
|
-
export type { ItemTotalProps } from "./ItemTotal";
|
|
33
|
+
export * from "./ItemTotal";
|
|
33
34
|
export * from "./RichTextEditor";
|
|
34
35
|
export { default as Loader } from "./Loader";
|
|
35
36
|
export type { LoaderProps } from "./Loader";
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export { default as DropDownMenu } from "./DropDownMenu";
|
|
|
8
8
|
export { default as DateInput } from "./DateInput";
|
|
9
9
|
export { default as TextArea } from "./TextArea";
|
|
10
10
|
export { default as TextAreaInput } from "./TextAreaInput";
|
|
11
|
+
export { default as SearchableTextArea } from "./SearchableTextArea";
|
|
11
12
|
export { default as TagBox } from "./TagBox";
|
|
12
13
|
export { default as FieldLabel } from "./FieldLabel";
|
|
13
14
|
export { default as Modal } from "./Modal";
|
|
@@ -21,7 +22,7 @@ export { default as Tooltip } from "./Tooltip";
|
|
|
21
22
|
export { default as Pill } from "./Pill";
|
|
22
23
|
export { default as Calendar } from "./Calendar";
|
|
23
24
|
export { default as Typography } from "./Typography";
|
|
24
|
-
export
|
|
25
|
+
export * from "./ItemTotal";
|
|
25
26
|
export * from "./RichTextEditor";
|
|
26
27
|
export { default as Loader } from "./Loader";
|
|
27
28
|
export * from "./QueryFilter";
|