@monolith-forensics/monolith-ui 1.3.62 → 1.3.91

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.
@@ -1,11 +1,9 @@
1
1
  import React from "react";
2
- interface ItemTotalProps {
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 { default } from "./ItemTotal";
2
- export type { ItemTotalProps } from "./ItemTotal";
1
+ export * from "./ItemTotal";
@@ -1 +1 @@
1
- export { default } from "./ItemTotal";
1
+ export * from "./ItemTotal";
@@ -1,5 +1,4 @@
1
1
  import { SelectBoxProps } from "..";
2
- 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>, {
3
- width?: string | number | null;
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
- 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, 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, } from "react";
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
- export const StyledInputContainer = styled.div `
20
- font-family: ${({ theme }) => theme.typography.fontFamily};
21
-
22
- position: relative;
23
- display: flex;
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, _g;
14
+ var _a, _b, _c, _d, _e, _f;
15
+ // Component setup and data processing
213
16
  const theme = useTheme();
214
- const isObjectArray = (_a = Object.keys((data === null || data === void 0 ? void 0 : data[0]) || {})) === null || _a === void 0 ? void 0 : _a.includes("label");
215
- // Determine controlled status only once at mount
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
- value: isObjectArray ? item.value : item,
219
- label: isObjectArray ? item.label : item,
220
- group: isObjectArray ? item.group : undefined,
221
- disabled: isObjectArray ? item.disabled : undefined,
222
- data: isObjectArray ? item.data : undefined,
223
- }));
224
- /**
225
- * Get the correct option if the default value is a string
226
- * otherwise, use the default value as is
227
- * this allows setting the value with a string or Option object
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 === "Escape") {
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 === "Enter") {
206
+ if (e.key === KEYBOARD_KEYS.ENTER) {
390
207
  toggleOpen();
391
208
  return;
392
209
  }
393
210
  // Arrow down
394
- if (e.key === "ArrowDown") {
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 === "ArrowUp") {
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 === "Tab") {
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(searchValue);
269
+ onSearch === null || onSearch === void 0 ? void 0 : onSearch(value);
453
270
  }
454
271
  };
455
- const referenceEl = (_b = refs === null || refs === void 0 ? void 0 : refs.reference) === null || _b === void 0 ? void 0 : _b.current;
456
- const contentWidth = ((_e = (_d = (_c = referenceEl === null || referenceEl === void 0 ? void 0 : referenceEl.getClientRects) === null || _c === void 0 ? void 0 : _c.call(referenceEl)) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.width) || "100%";
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 - ((_f = referenceEl === null || referenceEl === void 0 ? void 0 : referenceEl.getBoundingClientRect()) === null || _f === void 0 ? void 0 : _f.bottom) - 10;
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 = (((_g = referenceEl === null || referenceEl === void 0 ? void 0 : referenceEl.getBoundingClientRect()) === null || _g === void 0 ? void 0 : _g.top) - 10);
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) => {
@@ -489,7 +337,7 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
489
337
  }
490
338
  }, [_value]);
491
339
  useEffect(() => {
492
- if (bottomHeight < 250) {
340
+ if (bottomHeight < DEFAULT_DROPDOWN_HEIGHT) {
493
341
  setPlacement("top-start");
494
342
  setDropDownHeight(topHeight);
495
343
  }
@@ -502,35 +350,22 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
502
350
  setDropDownHeight(bottomHeight);
503
351
  };
504
352
  }, [topHeight, bottomHeight, isOpen]);
505
- 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 } : {} }), clearable && (_value || !!inputValue) ? (_jsx(ClearButton, { className: "input-btn", onClick: handleClear, onMouseDown: (e) => {
506
- e.preventDefault();
507
- e.stopPropagation();
508
- } })) : arrow ? (_jsx(ArrowButton, { onClick: (e) => {
509
- e.preventDefault();
510
- }, onMouseDown: (e) => {
511
- e.preventDefault();
512
- e.stopPropagation();
513
- toggleOpen();
514
- } })) : 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) => {
515
357
  containerRef.current = ref;
516
358
  refs.setFloating(ref);
517
359
  }, style: floatingStyles, className: "mfFloating", children: _jsxs(StyledContent, Object.assign({ className: "mfFloatingContent", style: {
518
360
  width: contentWidth,
519
361
  maxWidth: contentWidth,
520
362
  maxHeight: DropDownProps.autoHeight
521
- ? (dropDownHeight || 250) - 10
363
+ ? (dropDownHeight || DEFAULT_DROPDOWN_HEIGHT) -
364
+ DROPDOWN_OFFSET
522
365
  : "",
523
366
  }, variant: variant, "data-empty": filteredItems.length === 0 }, DropDownProps, { children: [loading && _jsx(Loader, {}), !loading && actionComponent && (_jsx(ActionMenu, { onClick: () => {
524
367
  setIsOpen(false);
525
- }, children: actionComponent })), !loading && filteredItems.length === 0 && (_jsx(EmptyComponent, { children: allowCustomValue && searchValue
526
- ? `Add "${searchValue}" as a new item`
527
- : !!searchValue
528
- ? `No items found`
529
- : "No items" })), !loading && (_jsx(StyledInnerItemContainer, { ref: scrollContainerRef, "data-scroll-active": scrollActive, onScroll: onScroll, children: grouped
530
- ? groups.map((group, index) => (_jsxs("div", { children: [_jsx(GroupTitle, { size: size, children: group.label }), group.items.map((item, index) => {
531
- 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));
532
- })] }, group.label)))
533
- : filteredItems.map((item, index) => {
534
- 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));
535
- }) }))] })) }) }))] }));
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)) }))] })) }) }))] }));
536
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
+ };
@@ -33,7 +33,7 @@ const TableMenu = () => {
33
33
  const inputRef = useRef(null);
34
34
  if ((tableMenuOptions === null || tableMenuOptions === void 0 ? void 0 : tableMenuOptions.enabled) !== true)
35
35
  return null;
36
- const { addButtonOptions, filterOptions, tableCountOptions, exportOptions, compactOptions, columnSelectorOptions, searchOptions, children, } = tableMenuOptions;
36
+ const { addButtonOptions, filterOptions, tableCountOptions, exportOptions, compactOptions, columnSelectorOptions, actionOptions, searchOptions, children, } = tableMenuOptions;
37
37
  const queryFilter = useQueryFilter({
38
38
  defaultFilter: filterState,
39
39
  filterDefinitions: (filterOptions === null || filterOptions === void 0 ? void 0 : filterOptions.filterDefinitions) || [],
@@ -84,7 +84,7 @@ const TableMenu = () => {
84
84
  }, onItemSelect: handleSelectFilter, dropDownProps: {
85
85
  style: { width: 175, maxWidth: 400 },
86
86
  }, searchable: true, children: "Filter" })), (tableCountOptions === null || tableCountOptions === void 0 ? void 0 : tableCountOptions.recordsTotalEnabled) === true && (_jsx(InfoBadge, { total: totalRecords || data.length })), enableSelection === true &&
87
- (tableCountOptions === null || tableCountOptions === void 0 ? void 0 : tableCountOptions.selectionTotalEnabled) === true && (_jsx(InfoBadge, { total: getCalculatedSelectionTotal(), hint: "Selected Items", icon: CheckSquareIcon })), children] }), _jsxs(FlexedRow, { children: [(exportOptions === null || exportOptions === void 0 ? void 0 : exportOptions.enabled) === true && (_jsx(DropDownMenu, { variant: "outlined", size: "xs", data: [
87
+ (tableCountOptions === null || tableCountOptions === void 0 ? void 0 : tableCountOptions.selectionTotalEnabled) === true && (_jsx(InfoBadge, { total: getCalculatedSelectionTotal(), hint: "Selected Items", icon: CheckSquareIcon })), children] }), _jsxs(FlexedRow, { children: [(actionOptions === null || actionOptions === void 0 ? void 0 : actionOptions.enabled) === true && actionOptions.menu, (exportOptions === null || exportOptions === void 0 ? void 0 : exportOptions.enabled) === true && (_jsx(DropDownMenu, { variant: "outlined", size: "xs", data: [
88
88
  {
89
89
  label: "Export Visible Columns",
90
90
  value: TableExportOptions.ExportVisible,
@@ -227,6 +227,10 @@ export type TableMenuOptions = {
227
227
  recordsTotalEnabled?: boolean;
228
228
  selectionTotalEnabled?: boolean;
229
229
  };
230
+ actionOptions?: {
231
+ enabled?: boolean;
232
+ menu?: React.ReactNode;
233
+ };
230
234
  searchOptions?: {
231
235
  enabled?: boolean;
232
236
  placeholder?: string;
package/dist/index.d.ts CHANGED
@@ -28,8 +28,7 @@ export { default as Tooltip } from "./Tooltip";
28
28
  export { default as Pill } from "./Pill";
29
29
  export { default as Calendar } from "./Calendar";
30
30
  export { default as Typography } from "./Typography";
31
- export { default as ItemTotal } from "./ItemTotal";
32
- export type { ItemTotalProps } from "./ItemTotal";
31
+ export * from "./ItemTotal";
33
32
  export * from "./RichTextEditor";
34
33
  export { default as Loader } from "./Loader";
35
34
  export type { LoaderProps } from "./Loader";
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ export { default as Tooltip } from "./Tooltip";
21
21
  export { default as Pill } from "./Pill";
22
22
  export { default as Calendar } from "./Calendar";
23
23
  export { default as Typography } from "./Typography";
24
- export { default as ItemTotal } from "./ItemTotal";
24
+ export * from "./ItemTotal";
25
25
  export * from "./RichTextEditor";
26
26
  export { default as Loader } from "./Loader";
27
27
  export * from "./QueryFilter";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monolith-forensics/monolith-ui",
3
- "version": "1.3.62",
3
+ "version": "1.3.91",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "author": "Matt Danner (Monolith Forensics LLC)",