@monolith-forensics/monolith-ui 1.4.3 → 1.5.0
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/DropDownMenu/DropDownMenu.js +2 -2
- package/dist/DropDownMenu/components/MenuItemList.d.ts +2 -0
- package/dist/DropDownMenu/components/MenuItemList.js +100 -4
- package/dist/DropDownMenu/types.d.ts +3 -0
- package/dist/Table/TableMenu/TableMenu.js +2 -5
- package/dist/Table/TableProvider.js +44 -5
- package/dist/Table/Utils/index.d.ts +1 -0
- package/dist/Table/Utils/index.js +1 -0
- package/dist/Table/Utils/tableExport.d.ts +9 -0
- package/dist/Table/Utils/tableExport.js +102 -0
- package/dist/Table/types.d.ts +1 -0
- package/dist/TextAreaInput/TextAreaInput.d.ts +13 -12
- package/dist/TextAreaInput/TextAreaInput.js +38 -106
- package/dist/TextAreaInput/index.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/package.json +2 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { Menu, MenuItemList, StyledInnerItemContainer } from "./components";
|
|
4
|
-
export const DropDownMenu = ({ data, children, defaultValue, variant, arrow, size, searchable, loading, onScroll, onScrollToTop, onScrollToBottom, onSearch, manualSearch, multiselect, renderOption, onItemSelect, onChange, buttonProps, TooltipContent, dropDownProps, query, disabled, }) => {
|
|
4
|
+
export const DropDownMenu = ({ data, children, defaultValue, variant, arrow, size, searchable, grouped, onAddNew, loading, onScroll, onScrollToTop, onScrollToBottom, onSearch, manualSearch, multiselect, renderOption, onItemSelect, onChange, buttonProps, TooltipContent, dropDownProps, query, disabled, }) => {
|
|
5
5
|
var _a;
|
|
6
6
|
const isObjectArray = (_a = Object.keys((data === null || data === void 0 ? void 0 : data[0]) || {})) === null || _a === void 0 ? void 0 : _a.includes("label");
|
|
7
7
|
const [selected, setSelected] = useState(defaultValue || []);
|
|
@@ -25,5 +25,5 @@ export const DropDownMenu = ({ data, children, defaultValue, variant, arrow, siz
|
|
|
25
25
|
const handleScrollToBottom = (e) => {
|
|
26
26
|
onScrollToBottom === null || onScrollToBottom === void 0 ? void 0 : onScrollToBottom(e);
|
|
27
27
|
};
|
|
28
|
-
return (_jsx(Menu, { label: children, disabled: disabled, arrow: arrow, buttonSize: size, variant: variant, multiselect: multiselect, buttonProps: buttonProps, onMenuClose: handleMenuClose, dropDownProps: dropDownProps, children: _jsxs(StyledInnerItemContainer, { children: [loading && _jsx("div", { children: "Loading..." }), !loading && (_jsx(MenuItemList, { menuItems: data, searchable: searchable, onSearch: onSearch, manualSearch: manualSearch, selected: selected, TooltipContent: TooltipContent, multiselect: multiselect, size: size, handleAddItem: handleAddItem, handleRemoveItem: handleRemoveItem, onItemSelect: onItemSelect, renderOption: renderOption, onScroll: onScroll, onScrollToTop: onScrollToTop, onScrollToBottom: handleScrollToBottom, query: query }))] }) }));
|
|
28
|
+
return (_jsx(Menu, { label: children, disabled: disabled, arrow: arrow, buttonSize: size, variant: variant, multiselect: multiselect, buttonProps: buttonProps, onMenuClose: handleMenuClose, dropDownProps: dropDownProps, children: _jsxs(StyledInnerItemContainer, { children: [loading && _jsx("div", { children: "Loading..." }), !loading && (_jsx(MenuItemList, { menuItems: data, searchable: searchable, grouped: grouped, onAddNew: onAddNew, onSearch: onSearch, manualSearch: manualSearch, selected: selected, TooltipContent: TooltipContent, multiselect: multiselect, size: size, handleAddItem: handleAddItem, handleRemoveItem: handleRemoveItem, onItemSelect: onItemSelect, renderOption: renderOption, onScroll: onScroll, onScrollToTop: onScrollToTop, onScrollToBottom: handleScrollToBottom, query: query }))] }) }));
|
|
29
29
|
};
|
|
@@ -10,6 +10,8 @@ export declare const MenuItemList: React.FC<{
|
|
|
10
10
|
selected?: DropDownItem[];
|
|
11
11
|
TooltipContent?: ComponentType<any>;
|
|
12
12
|
multiselect?: boolean;
|
|
13
|
+
grouped?: boolean;
|
|
14
|
+
onAddNew?: (value: string) => void;
|
|
13
15
|
size?: Size;
|
|
14
16
|
handleAddItem: (item: DropDownItem) => void;
|
|
15
17
|
handleRemoveItem: (item: DropDownItem) => void;
|
|
@@ -13,6 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
13
13
|
import { useLayoutEffect, useRef, useState } from "react";
|
|
14
14
|
import styled from "styled-components";
|
|
15
15
|
import { FixedSizeList } from "react-window";
|
|
16
|
+
import { Plus } from "lucide-react";
|
|
16
17
|
import CheckBox from "../../CheckBox";
|
|
17
18
|
import { DropDownMenu } from "../DropDownMenu";
|
|
18
19
|
import { MenuItem } from "./MenuItem";
|
|
@@ -30,11 +31,71 @@ const filterMenuItems = (menuItems, searchValue) => {
|
|
|
30
31
|
return (_c = item.toLowerCase) === null || _c === void 0 ? void 0 : _c.call(item).includes(searchValue.toLowerCase());
|
|
31
32
|
});
|
|
32
33
|
};
|
|
34
|
+
const buildGroupedDisplayList = (items) => {
|
|
35
|
+
const groups = {};
|
|
36
|
+
for (const item of items) {
|
|
37
|
+
const key = item.group || "Other";
|
|
38
|
+
if (!groups[key])
|
|
39
|
+
groups[key] = [];
|
|
40
|
+
groups[key].push(item);
|
|
41
|
+
}
|
|
42
|
+
const sortedKeys = Object.keys(groups).sort((a, b) => a.localeCompare(b));
|
|
43
|
+
const result = [];
|
|
44
|
+
for (const key of sortedKeys) {
|
|
45
|
+
result.push({
|
|
46
|
+
_isGroupHeader: true,
|
|
47
|
+
label: key,
|
|
48
|
+
value: `__group__${key}`,
|
|
49
|
+
});
|
|
50
|
+
result.push(...groups[key]);
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
33
54
|
const ListViewPort = styled.div.attrs({ className: "ListViewPort" }) `
|
|
34
55
|
display: flex;
|
|
35
56
|
flex-direction: column;
|
|
36
57
|
`;
|
|
37
|
-
|
|
58
|
+
const GroupHeader = styled.div `
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
gap: 8px;
|
|
62
|
+
color: ${(props) => props.theme.palette.text.secondary};
|
|
63
|
+
font-weight: 500;
|
|
64
|
+
user-select: none;
|
|
65
|
+
padding: ${({ $size }) => $size === "xs"
|
|
66
|
+
? "2px 8px"
|
|
67
|
+
: $size === "sm"
|
|
68
|
+
? "4px 10px"
|
|
69
|
+
: $size === "md"
|
|
70
|
+
? "4px 12px"
|
|
71
|
+
: $size === "lg"
|
|
72
|
+
? "5px 14px"
|
|
73
|
+
: $size === "xl"
|
|
74
|
+
? "6px 16px"
|
|
75
|
+
: "2px 8px"};
|
|
76
|
+
font-size: ${({ $size }) => $size === "xs"
|
|
77
|
+
? "10px"
|
|
78
|
+
: $size === "sm"
|
|
79
|
+
? "11px"
|
|
80
|
+
: $size === "md"
|
|
81
|
+
? "13px"
|
|
82
|
+
: $size === "lg"
|
|
83
|
+
? "15px"
|
|
84
|
+
: $size === "xl"
|
|
85
|
+
? "17px"
|
|
86
|
+
: "10px"};
|
|
87
|
+
|
|
88
|
+
.group-line {
|
|
89
|
+
border-top: 1px solid ${(props) => props.theme.palette.divider};
|
|
90
|
+
flex: 1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.group-label {
|
|
94
|
+
white-space: nowrap;
|
|
95
|
+
min-width: fit-content;
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
export const MenuItemList = ({ menuItems, searchable, onSearch, manualSearch, selected, TooltipContent, multiselect, grouped, onAddNew, size, handleAddItem, handleRemoveItem, onItemSelect, renderOption, onScroll, onScrollToTop, onScrollToBottom, query, }) => {
|
|
38
99
|
var _a;
|
|
39
100
|
const [searchValue, setSearchValue] = useState("");
|
|
40
101
|
const _b = query !== null && query !== void 0 ? query : {}, { queryKey, queryFn, getNextPageParam, initialPageParam } = _b, rest = __rest(_b, ["queryKey", "queryFn", "getNextPageParam", "initialPageParam"]);
|
|
@@ -69,6 +130,29 @@ export const MenuItemList = ({ menuItems, searchable, onSearch, manualSearch, se
|
|
|
69
130
|
const filteredItems = searchable
|
|
70
131
|
? filterMenuItems(visibleItems, searchValue)
|
|
71
132
|
: visibleItems;
|
|
133
|
+
const hasExactMatch = searchValue.trim() !== "" &&
|
|
134
|
+
filteredItems.some((item) => {
|
|
135
|
+
var _a, _b;
|
|
136
|
+
return ((_a = item === null || item === void 0 ? void 0 : item.label) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === searchValue.trim().toLowerCase() ||
|
|
137
|
+
((_b = item === null || item === void 0 ? void 0 : item.value) === null || _b === void 0 ? void 0 : _b.toString().toLowerCase()) ===
|
|
138
|
+
searchValue.trim().toLowerCase();
|
|
139
|
+
});
|
|
140
|
+
const displayItems = (() => {
|
|
141
|
+
let items = grouped
|
|
142
|
+
? buildGroupedDisplayList(filteredItems)
|
|
143
|
+
: filteredItems;
|
|
144
|
+
if (onAddNew && searchValue.trim() && !hasExactMatch) {
|
|
145
|
+
items = [
|
|
146
|
+
...items,
|
|
147
|
+
{
|
|
148
|
+
_isAddNew: true,
|
|
149
|
+
label: `Add "${searchValue.trim()}"`,
|
|
150
|
+
value: searchValue.trim(),
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
}
|
|
154
|
+
return items;
|
|
155
|
+
})();
|
|
72
156
|
const isObjectArray = (_a = Object.keys((visibleItems === null || visibleItems === void 0 ? void 0 : visibleItems[0]) || {})) === null || _a === void 0 ? void 0 : _a.includes("label");
|
|
73
157
|
const isLoading = isLoadingInfiniteQuery;
|
|
74
158
|
const handleOnScroll = (event) => {
|
|
@@ -114,7 +198,7 @@ export const MenuItemList = ({ menuItems, searchable, onSearch, manualSearch, se
|
|
|
114
198
|
}, [targetElm.current, isLoading]);
|
|
115
199
|
const overscanCount = 10;
|
|
116
200
|
const itemHeight = 25;
|
|
117
|
-
const itemCount = (
|
|
201
|
+
const itemCount = (displayItems === null || displayItems === void 0 ? void 0 : displayItems.length) || 0;
|
|
118
202
|
const height = viewPortDimensions.height
|
|
119
203
|
? viewPortDimensions.height
|
|
120
204
|
: itemCount * itemHeight < 200
|
|
@@ -134,8 +218,20 @@ export const MenuItemList = ({ menuItems, searchable, onSearch, manualSearch, se
|
|
|
134
218
|
alignItems: "center",
|
|
135
219
|
height: "100%",
|
|
136
220
|
padding: "10px 0",
|
|
137
|
-
}, children: [_jsx("div", { style: { fontSize: "12px" }, children: "Loading..." }), _jsx(Loader, {})] })), !isLoading && (_jsx(ListViewPort, { ref: targetElm, children: _jsx(FixedSizeList, { itemData:
|
|
138
|
-
const item =
|
|
221
|
+
}, children: [_jsx("div", { style: { fontSize: "12px" }, children: "Loading..." }), _jsx(Loader, {})] })), !isLoading && (_jsx(ListViewPort, { ref: targetElm, children: _jsx(FixedSizeList, { itemData: displayItems, overscanCount: overscanCount, height: height, width: width, itemCount: itemCount, itemSize: itemHeight, outerRef: listElm, children: ({ data, index, style }) => {
|
|
222
|
+
const item = data === null || data === void 0 ? void 0 : data[index];
|
|
223
|
+
if (!item)
|
|
224
|
+
return null;
|
|
225
|
+
if (item._isGroupHeader) {
|
|
226
|
+
return (_jsxs(GroupHeader, { "$size": size, style: style, children: [_jsx("span", { className: "group-line" }), _jsx("span", { className: "group-label", children: item.label }), _jsx("span", { className: "group-line" })] }, item.value));
|
|
227
|
+
}
|
|
228
|
+
if (item._isAddNew) {
|
|
229
|
+
return (_jsx(MenuItem, { className: "MenuItem", size: size, leftSection: _jsx(Plus, { size: 14 }), onClick: (e) => {
|
|
230
|
+
e.preventDefault();
|
|
231
|
+
e.stopPropagation();
|
|
232
|
+
onAddNew === null || onAddNew === void 0 ? void 0 : onAddNew(item.value);
|
|
233
|
+
}, style: style, children: item.label }, "__add_new__"));
|
|
234
|
+
}
|
|
139
235
|
const isSelected = !!(selected === null || selected === void 0 ? void 0 : selected.find((s) => {
|
|
140
236
|
return isObjectArray ? (s === null || s === void 0 ? void 0 : s.value) === (item === null || item === void 0 ? void 0 : item.value) : s === item;
|
|
141
237
|
}));
|
|
@@ -15,6 +15,7 @@ export type DropDownItem = {
|
|
|
15
15
|
rightSection?: React.ReactNode;
|
|
16
16
|
disabled?: boolean;
|
|
17
17
|
visible?: boolean;
|
|
18
|
+
group?: string;
|
|
18
19
|
};
|
|
19
20
|
export type SearchInputProps = React.ComponentPropsWithoutRef<typeof Input>;
|
|
20
21
|
export type StyledContentProps = {
|
|
@@ -42,6 +43,8 @@ export type DropDownMenuProps = {
|
|
|
42
43
|
onSearch?: (value: string) => void;
|
|
43
44
|
searchable?: boolean;
|
|
44
45
|
manualSearch?: boolean;
|
|
46
|
+
grouped?: boolean;
|
|
47
|
+
onAddNew?: (value: string) => void;
|
|
45
48
|
loading?: boolean;
|
|
46
49
|
arrow?: boolean;
|
|
47
50
|
dropDownProps?: ComponentPropsWithoutRef<typeof StyledContent>;
|
|
@@ -30,7 +30,7 @@ const FlexedRow = styled.div `
|
|
|
30
30
|
`;
|
|
31
31
|
const TableMenu = () => {
|
|
32
32
|
var _a, _b, _c, _d, _e;
|
|
33
|
-
const { data, columnState, searchState, toggleColumnVisibility, tableMenuOptions, runSearch, enableSelection, compactState, toggleCompact, totalRecords, getCalculatedSelectionTotal, filterState, handleFilterChange, clearSearch, } = useTable();
|
|
33
|
+
const { data, columnState, searchState, toggleColumnVisibility, tableMenuOptions, runSearch, enableSelection, compactState, toggleCompact, totalRecords, getCalculatedSelectionTotal, handleTableExport, filterState, handleFilterChange, clearSearch, } = useTable();
|
|
34
34
|
const inputRef = useRef(null);
|
|
35
35
|
if ((tableMenuOptions === null || tableMenuOptions === void 0 ? void 0 : tableMenuOptions.enabled) !== true)
|
|
36
36
|
return null;
|
|
@@ -111,10 +111,7 @@ const TableMenu = () => {
|
|
|
111
111
|
title: "Export List to XLSX",
|
|
112
112
|
size: "xxs",
|
|
113
113
|
style: { padding: "0px 4px" },
|
|
114
|
-
}, onItemSelect: (item) => {
|
|
115
|
-
var _a, _b;
|
|
116
|
-
return (_b = (_a = tableMenuOptions === null || tableMenuOptions === void 0 ? void 0 : tableMenuOptions.exportOptions) === null || _a === void 0 ? void 0 : _a.onExport) === null || _b === void 0 ? void 0 : _b.call(_a, item.value);
|
|
117
|
-
}, dropDownProps: {
|
|
114
|
+
}, onItemSelect: (item) => void handleTableExport(item.value), dropDownProps: {
|
|
118
115
|
style: { width: 175, maxWidth: 400 },
|
|
119
116
|
}, children: _jsx(DownloadIcon, { size: 14 }) })), (compactOptions === null || compactOptions === void 0 ? void 0 : compactOptions.enabled) === true && (_jsx(Button, { variant: "outlined", title: "Toggle Compact", size: "xxs", style: { padding: "0px 4px" }, onClick: () => toggleCompact(), children: compactState ? _jsx(Rows4Icon, { size: 14 }) : _jsx(Rows3Icon, { size: 14 }) })), (columnSelectorOptions === null || columnSelectorOptions === void 0 ? void 0 : columnSelectorOptions.enabled) === true && (_jsx(DropDownMenu, { variant: "outlined", size: "xs", data: columnState.map((col) => ({
|
|
120
117
|
label: (col === null || col === void 0 ? void 0 : col.caption) || col.dataField,
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
11
|
var t = {};
|
|
3
12
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -18,6 +27,7 @@ import shortUUID from "short-uuid";
|
|
|
18
27
|
import { SelectionStatus } from "./enums";
|
|
19
28
|
import moment from "moment";
|
|
20
29
|
import { useControlled } from "../Utilities";
|
|
30
|
+
import { exportTableToExcel } from "./Utils";
|
|
21
31
|
const calculateSelectionTotal = (selectionState, totalRecords, dataLength = 0) => {
|
|
22
32
|
if (!selectionState) {
|
|
23
33
|
return 0;
|
|
@@ -37,7 +47,7 @@ const calculateSelectionTotal = (selectionState, totalRecords, dataLength = 0) =
|
|
|
37
47
|
};
|
|
38
48
|
export const TableContext = createContext(null);
|
|
39
49
|
const TableProvider = (_a) => {
|
|
40
|
-
var { children, columns, data, keyField, tableInstanceRef, stateStorage, tableMenuOptions, manualSorting, manualFiltering, manualSearch, height, maxHeight, minHeight, focusedRowId, enableColumnResize, enableSorting, compact, totalRecords, onColumnStateChange, onColumnReorder, onColumnHeaderClick, onSort, onRowUpdated, tableElement, headerRowElm, tableDimensions, targetElm, listElm, defaultSelectionState, selectionState, onSelectionChange, defaultFilterState, filterState, onFilterChange } = _a, props = __rest(_a, ["children", "columns", "data", "keyField", "tableInstanceRef", "stateStorage", "tableMenuOptions", "manualSorting", "manualFiltering", "manualSearch", "height", "maxHeight", "minHeight", "focusedRowId", "enableColumnResize", "enableSorting", "compact", "totalRecords", "onColumnStateChange", "onColumnReorder", "onColumnHeaderClick", "onSort", "onRowUpdated", "tableElement", "headerRowElm", "tableDimensions", "targetElm", "listElm", "defaultSelectionState", "selectionState", "onSelectionChange", "defaultFilterState", "filterState", "onFilterChange"]);
|
|
50
|
+
var { children, columns, data, keyField, tableInstanceRef, stateStorage, tableMenuOptions, manualSorting, manualFiltering, manualSearch, manualExport, height, maxHeight, minHeight, focusedRowId, enableColumnResize, enableSorting, compact, totalRecords, onColumnStateChange, onColumnReorder, onColumnHeaderClick, onSort, onRowUpdated, tableElement, headerRowElm, tableDimensions, targetElm, listElm, defaultSelectionState, selectionState, onSelectionChange, defaultFilterState, filterState, onFilterChange } = _a, props = __rest(_a, ["children", "columns", "data", "keyField", "tableInstanceRef", "stateStorage", "tableMenuOptions", "manualSorting", "manualFiltering", "manualSearch", "manualExport", "height", "maxHeight", "minHeight", "focusedRowId", "enableColumnResize", "enableSorting", "compact", "totalRecords", "onColumnStateChange", "onColumnReorder", "onColumnHeaderClick", "onSort", "onRowUpdated", "tableElement", "headerRowElm", "tableDimensions", "targetElm", "listElm", "defaultSelectionState", "selectionState", "onSelectionChange", "defaultFilterState", "filterState", "onFilterChange"]);
|
|
41
51
|
const _columns = useMemo(() => columns
|
|
42
52
|
.map((child, index) => {
|
|
43
53
|
if (child.dataField === "__key") {
|
|
@@ -192,6 +202,18 @@ const TableProvider = (_a) => {
|
|
|
192
202
|
const clearSearch = () => {
|
|
193
203
|
updateSearchState("");
|
|
194
204
|
};
|
|
205
|
+
const handleTableExport = (exportType) => __awaiter(void 0, void 0, void 0, function* () {
|
|
206
|
+
var _a, _b;
|
|
207
|
+
if (manualExport === true) {
|
|
208
|
+
(_b = (_a = tableMenuOptions === null || tableMenuOptions === void 0 ? void 0 : tableMenuOptions.exportOptions) === null || _a === void 0 ? void 0 : _a.onExport) === null || _b === void 0 ? void 0 : _b.call(_a, exportType);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
yield exportTableToExcel({
|
|
212
|
+
columns: columnState,
|
|
213
|
+
data: _data,
|
|
214
|
+
exportMode: exportType,
|
|
215
|
+
});
|
|
216
|
+
});
|
|
195
217
|
const updateSortState = (sortState) => {
|
|
196
218
|
var _a;
|
|
197
219
|
setSortState(sortState);
|
|
@@ -209,20 +231,36 @@ const TableProvider = (_a) => {
|
|
|
209
231
|
// sort data
|
|
210
232
|
return data.sort((a, b) => {
|
|
211
233
|
if (sortState) {
|
|
234
|
+
const aValue = a[sortState.dataField];
|
|
235
|
+
const bValue = b[sortState.dataField];
|
|
236
|
+
const aIsEmpty = aValue === null || aValue === undefined;
|
|
237
|
+
const bIsEmpty = bValue === null || bValue === undefined;
|
|
238
|
+
// Treat empty values as the smallest values.
|
|
239
|
+
if (aIsEmpty && bIsEmpty) {
|
|
240
|
+
return 0;
|
|
241
|
+
}
|
|
242
|
+
if (aIsEmpty || bIsEmpty) {
|
|
243
|
+
if (sortState.dir === "asc") {
|
|
244
|
+
return aIsEmpty ? -1 : 1;
|
|
245
|
+
}
|
|
246
|
+
if (sortState.dir === "desc") {
|
|
247
|
+
return aIsEmpty ? 1 : -1;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
212
250
|
if (sortState.dir === "asc") {
|
|
213
|
-
if (
|
|
251
|
+
if (aValue < bValue) {
|
|
214
252
|
return -1;
|
|
215
253
|
}
|
|
216
|
-
if (
|
|
254
|
+
if (aValue > bValue) {
|
|
217
255
|
return 1;
|
|
218
256
|
}
|
|
219
257
|
return 0;
|
|
220
258
|
}
|
|
221
259
|
else if (sortState.dir === "desc") {
|
|
222
|
-
if (
|
|
260
|
+
if (aValue > bValue) {
|
|
223
261
|
return -1;
|
|
224
262
|
}
|
|
225
|
-
if (
|
|
263
|
+
if (aValue < bValue) {
|
|
226
264
|
return 1;
|
|
227
265
|
}
|
|
228
266
|
return 0;
|
|
@@ -583,6 +621,7 @@ const TableProvider = (_a) => {
|
|
|
583
621
|
toggleColumnVisibility,
|
|
584
622
|
runSearch,
|
|
585
623
|
clearSearch,
|
|
624
|
+
handleTableExport,
|
|
586
625
|
handleFilterChange, filterState: _filterState, compactState,
|
|
587
626
|
toggleCompact,
|
|
588
627
|
getCalculatedSelectionTotal,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TableExportOptions } from "../enums";
|
|
2
|
+
import { ColumnState } from "../types";
|
|
3
|
+
type TableExportParams = {
|
|
4
|
+
columns: ColumnState[];
|
|
5
|
+
data: any[];
|
|
6
|
+
exportMode: TableExportOptions;
|
|
7
|
+
};
|
|
8
|
+
export declare const exportTableToExcel: ({ columns, data, exportMode, }: TableExportParams) => Promise<void>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { isValidElement } from "react";
|
|
11
|
+
import { TableExportOptions } from "../enums";
|
|
12
|
+
const getColumnHeader = (column) => {
|
|
13
|
+
return column.caption || column.dataField;
|
|
14
|
+
};
|
|
15
|
+
const toExportablePrimitive = (value) => {
|
|
16
|
+
if (value === undefined || value === null)
|
|
17
|
+
return null;
|
|
18
|
+
if (typeof value === "string" ||
|
|
19
|
+
typeof value === "number" ||
|
|
20
|
+
typeof value === "boolean" ||
|
|
21
|
+
value instanceof Date) {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
if (value instanceof RegExp) {
|
|
25
|
+
return value.toString();
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(value)) {
|
|
28
|
+
return value.map((item) => toExportablePrimitive(item)).join(", ");
|
|
29
|
+
}
|
|
30
|
+
if (typeof value === "object") {
|
|
31
|
+
try {
|
|
32
|
+
return JSON.stringify(value);
|
|
33
|
+
}
|
|
34
|
+
catch (_a) {
|
|
35
|
+
return String(value);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return String(value);
|
|
39
|
+
};
|
|
40
|
+
const extractTextFromNode = (node) => {
|
|
41
|
+
if (node === null || node === undefined || typeof node === "boolean") {
|
|
42
|
+
return "";
|
|
43
|
+
}
|
|
44
|
+
if (typeof node === "string" || typeof node === "number") {
|
|
45
|
+
return String(node);
|
|
46
|
+
}
|
|
47
|
+
if (Array.isArray(node)) {
|
|
48
|
+
return node.map((child) => extractTextFromNode(child)).join("");
|
|
49
|
+
}
|
|
50
|
+
if (isValidElement(node)) {
|
|
51
|
+
return extractTextFromNode(node.props.children);
|
|
52
|
+
}
|
|
53
|
+
return "";
|
|
54
|
+
};
|
|
55
|
+
const getExportCellValue = (row, column) => {
|
|
56
|
+
const rawValue = row[column.dataField];
|
|
57
|
+
if (!column.render) {
|
|
58
|
+
return toExportablePrimitive(rawValue);
|
|
59
|
+
}
|
|
60
|
+
const renderedText = extractTextFromNode(column.render({ rowData: row }));
|
|
61
|
+
if (renderedText.trim().length > 0) {
|
|
62
|
+
return renderedText;
|
|
63
|
+
}
|
|
64
|
+
return toExportablePrimitive(rawValue);
|
|
65
|
+
};
|
|
66
|
+
const getColumnsForExport = (columns, exportMode) => {
|
|
67
|
+
return columns
|
|
68
|
+
.filter((column) => {
|
|
69
|
+
if (exportMode === TableExportOptions.ExportVisible) {
|
|
70
|
+
return column.visible !== false;
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
})
|
|
74
|
+
.sort((a, b) => a.orderValue - b.orderValue);
|
|
75
|
+
};
|
|
76
|
+
const getFileName = () => {
|
|
77
|
+
const timestamp = new Date().toISOString().replaceAll(":", "-");
|
|
78
|
+
return `table-export-${timestamp}.xlsx`;
|
|
79
|
+
};
|
|
80
|
+
const downloadBuffer = (buffer) => {
|
|
81
|
+
const blob = new Blob([buffer], {
|
|
82
|
+
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
83
|
+
});
|
|
84
|
+
const url = window.URL.createObjectURL(blob);
|
|
85
|
+
const anchor = document.createElement("a");
|
|
86
|
+
anchor.href = url;
|
|
87
|
+
anchor.download = getFileName();
|
|
88
|
+
anchor.click();
|
|
89
|
+
window.URL.revokeObjectURL(url);
|
|
90
|
+
};
|
|
91
|
+
export const exportTableToExcel = (_a) => __awaiter(void 0, [_a], void 0, function* ({ columns, data, exportMode, }) {
|
|
92
|
+
const { default: ExcelJS } = yield import("exceljs");
|
|
93
|
+
const exportColumns = getColumnsForExport(columns, exportMode);
|
|
94
|
+
const workbook = new ExcelJS.Workbook();
|
|
95
|
+
const worksheet = workbook.addWorksheet("Table Export");
|
|
96
|
+
worksheet.addRow(exportColumns.map((column) => getColumnHeader(column)));
|
|
97
|
+
data.forEach((row) => {
|
|
98
|
+
worksheet.addRow(exportColumns.map((column) => getExportCellValue(row, column)));
|
|
99
|
+
});
|
|
100
|
+
const buffer = yield workbook.xlsx.writeBuffer();
|
|
101
|
+
downloadBuffer(buffer);
|
|
102
|
+
});
|
package/dist/Table/types.d.ts
CHANGED
|
@@ -134,6 +134,7 @@ export type TableContextType = {
|
|
|
134
134
|
toggleColumnVisibility: (dataField: string) => void;
|
|
135
135
|
runSearch: (query: string) => void;
|
|
136
136
|
clearSearch: () => void;
|
|
137
|
+
handleTableExport: (e: TableExportOptions) => Promise<void> | void;
|
|
137
138
|
handleFilterChange: (query: Query) => void;
|
|
138
139
|
selectAllRows: () => void;
|
|
139
140
|
clearSelections: () => void;
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import React, { ReactNode } from "react";
|
|
2
2
|
import { Size } from "../core";
|
|
3
|
-
import {
|
|
4
|
-
export type
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import { DropDownMenuProps } from "../DropDownMenu/types";
|
|
4
|
+
export type ActionMenuOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* Visual style of the action menu trigger.
|
|
7
|
+
* - `"ellipsis"` renders a minimal `...` icon button.
|
|
8
|
+
* - `"dropdown"` renders an outlined button with a label and arrow (default).
|
|
9
|
+
*/
|
|
10
|
+
variant?: "ellipsis" | "dropdown";
|
|
11
|
+
/** Label for the trigger button. Only used when `variant` is `"dropdown"`. Defaults to `"Insert"`. */
|
|
12
|
+
label?: ReactNode;
|
|
13
|
+
/** Props forwarded to the underlying DropDownMenu component. `data` carries the menu items. */
|
|
14
|
+
menuOptions: DropDownMenuProps;
|
|
7
15
|
};
|
|
8
16
|
export interface TextAreaInputProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
9
17
|
variant?: "contained" | "filled" | "outlined" | "text";
|
|
@@ -19,14 +27,7 @@ export interface TextAreaInputProps extends React.TextareaHTMLAttributes<HTMLTex
|
|
|
19
27
|
onHeightChange?: (height: number, meta: {
|
|
20
28
|
rowHeight: number;
|
|
21
29
|
}) => void;
|
|
22
|
-
|
|
23
|
-
actionMenuOptions?: Array<{
|
|
24
|
-
value: string;
|
|
25
|
-
label: string;
|
|
26
|
-
}>;
|
|
27
|
-
onActionMenuSelect?: (item: DropDownItem) => void;
|
|
28
|
-
insertableItems?: InsertableItem[];
|
|
29
|
-
onInsertItem?: (item: InsertableItem) => void;
|
|
30
|
+
actionMenuOptions?: ActionMenuOptions;
|
|
30
31
|
}
|
|
31
32
|
declare const TextAreaInput: React.ForwardRefExoticComponent<TextAreaInputProps & React.RefAttributes<HTMLTextAreaElement>>;
|
|
32
33
|
export default TextAreaInput;
|
|
@@ -11,34 +11,9 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import styled from "styled-components";
|
|
14
|
-
import { forwardRef,
|
|
15
|
-
import { TextArea, FieldLabel, DropDownMenu
|
|
14
|
+
import { forwardRef, useRef } from "react";
|
|
15
|
+
import { TextArea, FieldLabel, DropDownMenu } from "..";
|
|
16
16
|
import { MoreHorizontal } from "lucide-react";
|
|
17
|
-
const DEFAULT_ACTIONS = [
|
|
18
|
-
{ value: "clear", label: "Clear Text" },
|
|
19
|
-
{ value: "insert", label: "Insert Item" },
|
|
20
|
-
];
|
|
21
|
-
const TextAreaWrapper = styled.div `
|
|
22
|
-
position: relative;
|
|
23
|
-
`;
|
|
24
|
-
const InsertMenuOverlay = styled.div `
|
|
25
|
-
position: absolute;
|
|
26
|
-
top: 0;
|
|
27
|
-
left: 0;
|
|
28
|
-
right: 0;
|
|
29
|
-
z-index: 10;
|
|
30
|
-
opacity: ${({ $visible }) => ($visible ? 1 : 0)};
|
|
31
|
-
pointer-events: ${({ $visible }) => ($visible ? "auto" : "none")};
|
|
32
|
-
transform: ${({ $visible }) => $visible ? "translateY(0)" : "translateY(-4px)"};
|
|
33
|
-
transition:
|
|
34
|
-
opacity 0.2s ease,
|
|
35
|
-
transform 0.2s ease;
|
|
36
|
-
`;
|
|
37
|
-
const StyledInsertSelectBox = styled(SelectBox) `
|
|
38
|
-
width: 100%;
|
|
39
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
40
|
-
border: 1px solid ${({ theme }) => theme.palette.primary.main};
|
|
41
|
-
`;
|
|
42
17
|
const StyledMoreHorizontal = styled(MoreHorizontal) `
|
|
43
18
|
color: ${({ theme }) => theme.palette.text.secondary};
|
|
44
19
|
&:hover {
|
|
@@ -46,21 +21,10 @@ const StyledMoreHorizontal = styled(MoreHorizontal) `
|
|
|
46
21
|
}
|
|
47
22
|
`;
|
|
48
23
|
const TextAreaInput = forwardRef((props, ref) => {
|
|
49
|
-
const {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// Action menu
|
|
53
|
-
showActionMenu = false, actionMenuOptions = DEFAULT_ACTIONS, onActionMenuSelect,
|
|
54
|
-
// Insertable items
|
|
55
|
-
insertableItems, onInsertItem } = props,
|
|
56
|
-
// Rest of props for TextArea
|
|
57
|
-
rest = __rest(props, ["label", "error", "required", "colSpan", "size", "description", "maxRows", "minRows", "onHeightChange", "cacheMeasurements", "showActionMenu", "actionMenuOptions", "onActionMenuSelect", "insertableItems", "onInsertItem"]);
|
|
58
|
-
// State for insert menu visibility
|
|
59
|
-
const [showInsertMenu, setShowInsertMenu] = useState(false);
|
|
60
|
-
const [triggerSelectBoxOpen, setTriggerSelectBoxOpen] = useState(false);
|
|
24
|
+
const { label, error, required, colSpan = 1, size = "sm", description, maxRows = 6, minRows = 3, onHeightChange, cacheMeasurements, actionMenuOptions } = props, rest = __rest(props, ["label", "error", "required", "colSpan", "size", "description", "maxRows", "minRows", "onHeightChange", "cacheMeasurements", "actionMenuOptions"]);
|
|
25
|
+
const { variant: menuVariant = "dropdown", label: menuLabel = "Insert", menuOptions = { data: [] }, } = actionMenuOptions !== null && actionMenuOptions !== void 0 ? actionMenuOptions : {};
|
|
26
|
+
const { data: menuData = [], onItemSelect: consumerOnItemSelect, onAddNew: consumerOnAddNew } = menuOptions, restMenuOptions = __rest(menuOptions, ["data", "onItemSelect", "onAddNew"]);
|
|
61
27
|
const textareaRef = useRef(null);
|
|
62
|
-
const insertMenuRef = useRef(null);
|
|
63
|
-
// Merge refs
|
|
64
28
|
const mergedRef = (node) => {
|
|
65
29
|
textareaRef.current = node;
|
|
66
30
|
if (typeof ref === "function") {
|
|
@@ -70,79 +34,40 @@ const TextAreaInput = forwardRef((props, ref) => {
|
|
|
70
34
|
ref.current = node;
|
|
71
35
|
}
|
|
72
36
|
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
37
|
+
const insertAtCursor = (text) => {
|
|
38
|
+
var _a;
|
|
39
|
+
const textarea = textareaRef.current;
|
|
40
|
+
if (!textarea)
|
|
76
41
|
return;
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
setShowInsertMenu(true);
|
|
91
|
-
// Trigger SelectBox to open using the new enhanced props
|
|
92
|
-
setTriggerSelectBoxOpen(true);
|
|
93
|
-
}
|
|
94
|
-
else if (item.value === "clear") {
|
|
95
|
-
// Built-in clear functionality
|
|
96
|
-
const textarea = textareaRef.current;
|
|
97
|
-
if (textarea) {
|
|
98
|
-
textarea.value = "";
|
|
99
|
-
textarea.focus();
|
|
100
|
-
// Trigger change event so controlled components update
|
|
101
|
-
const event = new Event("input", { bubbles: true });
|
|
102
|
-
textarea.dispatchEvent(event);
|
|
103
|
-
}
|
|
104
|
-
onActionMenuSelect === null || onActionMenuSelect === void 0 ? void 0 : onActionMenuSelect(item);
|
|
42
|
+
const start = textarea.selectionStart;
|
|
43
|
+
const end = textarea.selectionEnd;
|
|
44
|
+
const currentValue = textarea.value;
|
|
45
|
+
const newValue = currentValue.slice(0, start) + text + currentValue.slice(end);
|
|
46
|
+
const nativeSet = (_a = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value")) === null || _a === void 0 ? void 0 : _a.set;
|
|
47
|
+
nativeSet === null || nativeSet === void 0 ? void 0 : nativeSet.call(textarea, newValue);
|
|
48
|
+
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
49
|
+
textarea.focus();
|
|
50
|
+
textarea.setSelectionRange(start + text.length, start + text.length);
|
|
51
|
+
};
|
|
52
|
+
const handleItemSelect = (item) => {
|
|
53
|
+
if (consumerOnItemSelect) {
|
|
54
|
+
consumerOnItemSelect(item);
|
|
105
55
|
}
|
|
106
56
|
else {
|
|
107
|
-
|
|
57
|
+
insertAtCursor(String(item.value));
|
|
108
58
|
}
|
|
109
59
|
};
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
};
|
|
114
|
-
const handleInsertSelect = (value, option) => {
|
|
115
|
-
console.log("Selected value:", value, "Selected option:", option); // Debug log
|
|
116
|
-
// SelectBox passes (value, option) - we want the full option object
|
|
117
|
-
const item = option;
|
|
118
|
-
if (!item || !item.value) {
|
|
119
|
-
console.warn("Invalid item selected:", item);
|
|
120
|
-
setShowInsertMenu(false);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
if (onInsertItem) {
|
|
124
|
-
onInsertItem(item);
|
|
60
|
+
const handleAddNew = (value) => {
|
|
61
|
+
if (consumerOnAddNew) {
|
|
62
|
+
consumerOnAddNew(value);
|
|
125
63
|
}
|
|
126
64
|
else {
|
|
127
|
-
|
|
128
|
-
const textarea = textareaRef.current;
|
|
129
|
-
if (textarea) {
|
|
130
|
-
const start = textarea.selectionStart;
|
|
131
|
-
const end = textarea.selectionEnd;
|
|
132
|
-
const currentValue = textarea.value;
|
|
133
|
-
const newValue = currentValue.slice(0, start) + item.value + currentValue.slice(end);
|
|
134
|
-
textarea.value = newValue;
|
|
135
|
-
textarea.focus();
|
|
136
|
-
textarea.setSelectionRange(start + item.value.length, start + item.value.length);
|
|
137
|
-
// Trigger change event
|
|
138
|
-
const event = new Event("input", { bubbles: true });
|
|
139
|
-
textarea.dispatchEvent(event);
|
|
140
|
-
}
|
|
65
|
+
insertAtCursor(value);
|
|
141
66
|
}
|
|
142
|
-
setShowInsertMenu(false);
|
|
143
67
|
};
|
|
144
|
-
|
|
145
|
-
|
|
68
|
+
const hasMenuItems = menuData.length > 0;
|
|
69
|
+
return (_jsxs("div", { style: { gridColumn: `span ${colSpan}`, height: "fit-content" }, children: [label && (_jsx(FieldLabel, { asterisk: required, error: error, description: description, size: size, actionComponent: hasMenuItems ? (menuVariant === "ellipsis" ? (_jsx(DropDownMenu, Object.assign({ data: menuData, variant: "text", size: "xs", arrow: false, searchable: true, onAddNew: handleAddNew, onItemSelect: handleItemSelect, buttonProps: {
|
|
70
|
+
"aria-label": "Insert item",
|
|
146
71
|
style: {
|
|
147
72
|
minWidth: "auto",
|
|
148
73
|
border: "none",
|
|
@@ -152,7 +77,14 @@ const TextAreaInput = forwardRef((props, ref) => {
|
|
|
152
77
|
height: 16,
|
|
153
78
|
width: 16,
|
|
154
79
|
},
|
|
155
|
-
},
|
|
80
|
+
}, dropDownProps: {
|
|
81
|
+
style: { width: 200, maxWidth: 400 },
|
|
82
|
+
} }, restMenuOptions, { children: _jsx(StyledMoreHorizontal, { size: 16 }) }))) : (_jsx(DropDownMenu, Object.assign({ data: menuData, variant: "outlined", size: "xs", arrow: true, searchable: true, onAddNew: handleAddNew, onItemSelect: handleItemSelect, buttonProps: {
|
|
83
|
+
title: typeof menuLabel === "string" ? menuLabel : undefined,
|
|
84
|
+
size: "xxs",
|
|
85
|
+
}, dropDownProps: {
|
|
86
|
+
style: { width: 200, maxWidth: 400 },
|
|
87
|
+
} }, restMenuOptions, { children: menuLabel })))) : null, children: label })), _jsx(TextArea, Object.assign({ ref: mergedRef, size: size, maxRows: maxRows, minRows: minRows, onHeightChange: onHeightChange, cacheMeasurements: cacheMeasurements }, rest))] }));
|
|
156
88
|
});
|
|
157
89
|
TextAreaInput.displayName = "TextAreaInput";
|
|
158
90
|
export default TextAreaInput;
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export { default as IconButton } from "./IconButton";
|
|
|
16
16
|
export { default as DateInput } from "./DateInput";
|
|
17
17
|
export { default as TextArea } from "./TextArea";
|
|
18
18
|
export { default as TextAreaInput } from "./TextAreaInput";
|
|
19
|
+
export type { ActionMenuOptions, TextAreaInputProps } from "./TextAreaInput";
|
|
19
20
|
export { default as TagBox } from "./TagBox";
|
|
20
21
|
export type { TagBoxProps, TagboxOption } from "./TagBox";
|
|
21
22
|
export { default as FieldLabel } from "./FieldLabel";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monolith-forensics/monolith-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"author": "Matt Danner (Monolith Forensics LLC)",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"@uiw/codemirror-theme-vscode": "^4.23.6",
|
|
60
60
|
"@uiw/react-codemirror": "^4.23.6",
|
|
61
61
|
"deepmerge": "^4.3.1",
|
|
62
|
+
"exceljs": "^4.4.0",
|
|
62
63
|
"lucide-react": "^0.469.0",
|
|
63
64
|
"moment": "^2.29.1",
|
|
64
65
|
"overlayscrollbars": "^2.6.0",
|