@monolith-forensics/monolith-ui 1.3.113 → 1.4.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.
@@ -1,9 +1,11 @@
1
1
  import React from "react";
2
2
  import { Size } from "../core";
3
3
  import { DropDownItem } from "..";
4
+ import { DropDownMenuProps } from "../DropDownMenu/types";
4
5
  export type InsertableItem = {
5
6
  label: string;
6
7
  value: string;
8
+ group?: string;
7
9
  };
8
10
  export interface TextAreaInputProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
9
11
  variant?: "contained" | "filled" | "outlined" | "text";
@@ -20,13 +22,28 @@ export interface TextAreaInputProps extends React.TextareaHTMLAttributes<HTMLTex
20
22
  rowHeight: number;
21
23
  }) => void;
22
24
  showActionMenu?: boolean;
25
+ /**
26
+ * Visual style of the action menu trigger.
27
+ * - `"ellipsis"` renders a minimal `...` icon button (default).
28
+ * - `"dropdown"` renders an outlined button with a label and arrow, similar to the Table Filter menu.
29
+ */
30
+ actionMenuVariant?: "ellipsis" | "dropdown";
23
31
  actionMenuOptions?: Array<{
24
32
  value: string;
25
33
  label: string;
26
34
  }>;
27
35
  onActionMenuSelect?: (item: DropDownItem) => void;
36
+ /** Label displayed on the actions dropdown button. Only used when `actionMenuVariant` is `"dropdown"`. Defaults to "Actions". */
37
+ actionMenuLabel?: string;
38
+ /** Icon rendered in the left section of the actions dropdown button. Only used when `actionMenuVariant` is `"dropdown"`. Defaults to MoreHorizontal. */
39
+ actionMenuIcon?: React.ReactNode;
40
+ /** Whether the actions dropdown is searchable. Only used when `actionMenuVariant` is `"dropdown"`. Defaults to false. */
41
+ actionMenuSearchable?: boolean;
42
+ /** Additional props forwarded to the DropDownMenu component. Only used when `actionMenuVariant` is `"dropdown"`. */
43
+ actionMenuProps?: Partial<DropDownMenuProps>;
28
44
  insertableItems?: InsertableItem[];
29
45
  onInsertItem?: (item: InsertableItem) => void;
46
+ groupInsertableItems?: boolean;
30
47
  }
31
48
  declare const TextAreaInput: React.ForwardRefExoticComponent<TextAreaInputProps & React.RefAttributes<HTMLTextAreaElement>>;
32
49
  export default TextAreaInput;
@@ -50,11 +50,11 @@ const TextAreaInput = forwardRef((props, ref) => {
50
50
  // UI
51
51
  label, error, required, colSpan = 1, size = "sm", description, maxRows = 6, minRows = 3, onHeightChange, cacheMeasurements,
52
52
  // Action menu
53
- showActionMenu = false, actionMenuOptions = DEFAULT_ACTIONS, onActionMenuSelect,
53
+ showActionMenu = false, actionMenuVariant = "ellipsis", actionMenuOptions = DEFAULT_ACTIONS, onActionMenuSelect, actionMenuLabel = "Actions", actionMenuIcon, actionMenuSearchable = false, actionMenuProps,
54
54
  // Insertable items
55
- insertableItems, onInsertItem } = props,
55
+ insertableItems, onInsertItem, groupInsertableItems } = props,
56
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"]);
57
+ rest = __rest(props, ["label", "error", "required", "colSpan", "size", "description", "maxRows", "minRows", "onHeightChange", "cacheMeasurements", "showActionMenu", "actionMenuVariant", "actionMenuOptions", "onActionMenuSelect", "actionMenuLabel", "actionMenuIcon", "actionMenuSearchable", "actionMenuProps", "insertableItems", "onInsertItem", "groupInsertableItems"]);
58
58
  // State for insert menu visibility
59
59
  const [showInsertMenu, setShowInsertMenu] = useState(false);
60
60
  const [triggerSelectBoxOpen, setTriggerSelectBoxOpen] = useState(false);
@@ -75,10 +75,16 @@ const TextAreaInput = forwardRef((props, ref) => {
75
75
  if (!showInsertMenu)
76
76
  return;
77
77
  const handleClickOutside = (event) => {
78
- if (insertMenuRef.current &&
79
- !insertMenuRef.current.contains(event.target)) {
80
- setShowInsertMenu(false);
81
- }
78
+ var _a, _b, _c;
79
+ const target = event.target;
80
+ // Ignore clicks inside the insert menu container
81
+ if ((_a = insertMenuRef.current) === null || _a === void 0 ? void 0 : _a.contains(target))
82
+ return;
83
+ // Ignore clicks inside the SelectBox floating portal dropdown
84
+ const floatingEl = (_c = (_b = target).closest) === null || _c === void 0 ? void 0 : _c.call(_b, ".mfFloating");
85
+ if (floatingEl)
86
+ return;
87
+ setShowInsertMenu(false);
82
88
  };
83
89
  document.addEventListener("mousedown", handleClickOutside);
84
90
  return () => {
@@ -86,6 +92,7 @@ const TextAreaInput = forwardRef((props, ref) => {
86
92
  };
87
93
  }, [showInsertMenu]);
88
94
  const handleActionSelect = (item) => {
95
+ var _a;
89
96
  if (item.value === "insert" && (insertableItems === null || insertableItems === void 0 ? void 0 : insertableItems.length)) {
90
97
  setShowInsertMenu(true);
91
98
  // Trigger SelectBox to open using the new enhanced props
@@ -95,11 +102,10 @@ const TextAreaInput = forwardRef((props, ref) => {
95
102
  // Built-in clear functionality
96
103
  const textarea = textareaRef.current;
97
104
  if (textarea) {
98
- textarea.value = "";
105
+ const nativeSet = (_a = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value")) === null || _a === void 0 ? void 0 : _a.set;
106
+ nativeSet === null || nativeSet === void 0 ? void 0 : nativeSet.call(textarea, "");
107
+ textarea.dispatchEvent(new Event("input", { bubbles: true }));
99
108
  textarea.focus();
100
- // Trigger change event so controlled components update
101
- const event = new Event("input", { bubbles: true });
102
- textarea.dispatchEvent(event);
103
109
  }
104
110
  onActionMenuSelect === null || onActionMenuSelect === void 0 ? void 0 : onActionMenuSelect(item);
105
111
  }
@@ -112,6 +118,7 @@ const TextAreaInput = forwardRef((props, ref) => {
112
118
  setTriggerSelectBoxOpen(false);
113
119
  };
114
120
  const handleInsertSelect = (value, option) => {
121
+ var _a;
115
122
  console.log("Selected value:", value, "Selected option:", option); // Debug log
116
123
  // SelectBox passes (value, option) - we want the full option object
117
124
  const item = option;
@@ -131,28 +138,40 @@ const TextAreaInput = forwardRef((props, ref) => {
131
138
  const end = textarea.selectionEnd;
132
139
  const currentValue = textarea.value;
133
140
  const newValue = currentValue.slice(0, start) + item.value + currentValue.slice(end);
134
- textarea.value = newValue;
141
+ // Use the native setter so React's synthetic event system
142
+ // recognises the change on controlled inputs
143
+ const nativeSet = (_a = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value")) === null || _a === void 0 ? void 0 : _a.set;
144
+ nativeSet === null || nativeSet === void 0 ? void 0 : nativeSet.call(textarea, newValue);
145
+ // Dispatch as a bubbling event so React's onChange fires
146
+ textarea.dispatchEvent(new Event("input", { bubbles: true }));
135
147
  textarea.focus();
136
148
  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
149
  }
141
150
  }
142
151
  setShowInsertMenu(false);
143
152
  };
144
- return (_jsxs("div", { style: { gridColumn: `span ${colSpan}`, height: "fit-content" }, children: [label && (_jsx(FieldLabel, { asterisk: required, error: error, description: description, size: size, actionComponent: showActionMenu ? (_jsx(DropDownMenu, { data: actionMenuOptions, variant: "text", size: "xs", arrow: false, onItemSelect: handleActionSelect, buttonProps: {
145
- "aria-label": "Open actions",
146
- style: {
147
- minWidth: "auto",
148
- border: "none",
149
- background: "transparent",
150
- padding: 0,
151
- margin: 0,
152
- height: 16,
153
- width: 16,
154
- },
155
- }, children: _jsx(StyledMoreHorizontal, { size: 16 }) })) : null, children: label })), _jsxs(TextAreaWrapper, { children: [_jsx(TextArea, Object.assign({ ref: mergedRef, size: size, maxRows: maxRows, minRows: minRows, onHeightChange: onHeightChange, cacheMeasurements: cacheMeasurements }, rest)), showInsertMenu && (insertableItems === null || insertableItems === void 0 ? void 0 : insertableItems.length) && (_jsx(InsertMenuOverlay, { ref: insertMenuRef, "$visible": showInsertMenu, children: _jsx(StyledInsertSelectBox, { data: insertableItems, placeholder: "Select item to insert...", searchable: true, clearable: false, arrow: false, focused: showInsertMenu, openOnFocus: true, triggerOpen: triggerSelectBoxOpen, onOpened: handleSelectBoxOpened, onChange: handleInsertSelect, size: size }) }))] })] }));
153
+ return (_jsxs("div", { style: { gridColumn: `span ${colSpan}`, height: "fit-content" }, children: [label && (_jsx(FieldLabel, { asterisk: required, error: error, description: description, size: size, actionComponent: showActionMenu
154
+ ? actionMenuVariant === "dropdown"
155
+ ? (_jsx(DropDownMenu, Object.assign({ data: actionMenuOptions, variant: "outlined", size: "xs", arrow: true, searchable: actionMenuSearchable, onItemSelect: handleActionSelect, buttonProps: {
156
+ title: actionMenuLabel,
157
+ size: "xxs",
158
+ leftSection: actionMenuIcon,
159
+ }, dropDownProps: {
160
+ style: { width: 175, maxWidth: 400 },
161
+ } }, actionMenuProps, { children: actionMenuLabel })))
162
+ : (_jsx(DropDownMenu, { data: actionMenuOptions, variant: "text", size: "xs", arrow: false, onItemSelect: handleActionSelect, buttonProps: {
163
+ "aria-label": "Open actions",
164
+ style: {
165
+ minWidth: "auto",
166
+ border: "none",
167
+ background: "transparent",
168
+ padding: 0,
169
+ margin: 0,
170
+ height: 16,
171
+ width: 16,
172
+ },
173
+ }, children: _jsx(StyledMoreHorizontal, { size: 16 }) }))
174
+ : null, children: label })), _jsxs(TextAreaWrapper, { children: [_jsx(TextArea, Object.assign({ ref: mergedRef, size: size, maxRows: maxRows, minRows: minRows, onHeightChange: onHeightChange, cacheMeasurements: cacheMeasurements }, rest)), showInsertMenu && (insertableItems === null || insertableItems === void 0 ? void 0 : insertableItems.length) && (_jsx(InsertMenuOverlay, { ref: insertMenuRef, "$visible": showInsertMenu, children: _jsx(StyledInsertSelectBox, { data: insertableItems, grouped: groupInsertableItems, searchable: true, clearable: false, arrow: false, focused: showInsertMenu, openOnFocus: true, triggerOpen: triggerSelectBoxOpen, onOpened: handleSelectBoxOpened, onChange: handleInsertSelect, size: size }) }))] })] }));
156
175
  });
157
176
  TextAreaInput.displayName = "TextAreaInput";
158
177
  export default TextAreaInput;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monolith-forensics/monolith-ui",
3
- "version": "1.3.113",
3
+ "version": "1.4.0",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "author": "Matt Danner (Monolith Forensics LLC)",