@monolith-forensics/monolith-ui 1.3.95 → 1.3.97

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.
@@ -10,7 +10,9 @@ import { StyledInputContainer, StyledInnerItemContainer, EmptyComponent, GroupTi
10
10
  // Re-export for backward compatibility
11
11
  export { StyledInputContainer };
12
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
13
- DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
13
+ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false,
14
+ // Enhanced focus control props
15
+ triggerFocus = false, triggerOpen = false, onFocused, onOpened, }) => {
14
16
  var _a, _b, _c, _d, _e, _f;
15
17
  // Component setup and data processing
16
18
  const theme = useTheme();
@@ -351,6 +353,26 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
351
353
  };
352
354
  }, [topHeight, bottomHeight, isOpen]);
353
355
  // ============================================================================
356
+ // Enhanced Focus Control Effects
357
+ // ============================================================================
358
+ // Handle triggerFocus prop
359
+ useEffect(() => {
360
+ if (triggerFocus && inputRef.current) {
361
+ inputRef.current.focus();
362
+ onFocused === null || onFocused === void 0 ? void 0 : onFocused();
363
+ }
364
+ }, [triggerFocus, onFocused]);
365
+ // Handle triggerOpen prop
366
+ useEffect(() => {
367
+ if (triggerOpen) {
368
+ setIsOpen(true);
369
+ if (inputRef.current) {
370
+ inputRef.current.focus();
371
+ }
372
+ onOpened === null || onOpened === void 0 ? void 0 : onOpened();
373
+ }
374
+ }, [triggerOpen, onOpened]);
375
+ // ============================================================================
354
376
  // Render
355
377
  // ============================================================================
356
378
  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) => {
@@ -369,3 +391,4 @@ DropDownProps = {}, debounceTime = 175, sort = false, disabled = false, }) => {
369
391
  ? 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
392
  : filteredItems.map((item, index) => renderOptionItem(item, index)) }))] })) }) }))] }));
371
393
  };
394
+ SelectBox.displayName = "SelectBox";
@@ -42,4 +42,8 @@ export type SelectBoxProps = {
42
42
  onSearch?: (value: string) => void;
43
43
  searchFn?: (value: string) => void;
44
44
  onItemAdded?: (item: Option | string) => void;
45
+ triggerFocus?: boolean;
46
+ triggerOpen?: boolean;
47
+ onFocused?: () => void;
48
+ onOpened?: () => void;
45
49
  };
@@ -1,5 +1,11 @@
1
+ import React from "react";
1
2
  import { Size } from "../core";
2
- interface TextAreaInputProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
3
+ import { DropDownItem } from "..";
4
+ export type InsertableItem = {
5
+ label: string;
6
+ value: string;
7
+ };
8
+ export interface TextAreaInputProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
3
9
  variant?: "contained" | "filled" | "outlined" | "text";
4
10
  label?: string;
5
11
  error?: string;
@@ -9,10 +15,18 @@ interface TextAreaInputProps extends React.TextareaHTMLAttributes<HTMLTextAreaEl
9
15
  description?: string;
10
16
  maxRows?: number;
11
17
  minRows?: number;
18
+ cacheMeasurements?: boolean;
12
19
  onHeightChange?: (height: number, meta: {
13
20
  rowHeight: number;
14
21
  }) => void;
15
- cacheMeasurements?: boolean;
22
+ showActionMenu?: boolean;
23
+ actionMenuOptions?: Array<{
24
+ value: string;
25
+ label: string;
26
+ }>;
27
+ onActionMenuSelect?: (item: DropDownItem) => void;
28
+ insertableItems?: InsertableItem[];
29
+ onInsertItem?: (item: InsertableItem) => void;
16
30
  }
17
- declare const TextAreaInput: import("react").ForwardRefExoticComponent<TextAreaInputProps & import("react").RefAttributes<HTMLTextAreaElement>>;
31
+ declare const TextAreaInput: React.ForwardRefExoticComponent<TextAreaInputProps & React.RefAttributes<HTMLTextAreaElement>>;
18
32
  export default TextAreaInput;
@@ -10,13 +10,129 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
- import { TextArea, FieldLabel } from "..";
14
- import { forwardRef } from "react";
15
- const TextAreaInput = forwardRef((_a, ref) => {
16
- var { label, error, required, colSpan = 1, size = "sm", description, maxRows = 6, minRows = 3, onHeightChange, cacheMeasurements } = _a, rest = __rest(_a, ["label", "error", "required", "colSpan", "size", "description", "maxRows", "minRows", "onHeightChange", "cacheMeasurements"]);
17
- return (_jsxs("div", { style: {
18
- gridColumn: `span ${colSpan}`,
19
- height: "fit-content",
20
- }, children: [label && (_jsx(FieldLabel, { error: error, asterisk: required, size: size, description: description, children: label })), _jsx(TextArea, Object.assign({ ref: ref, size: size, maxRows: maxRows, minRows: minRows, onHeightChange: onHeightChange, cacheMeasurements: cacheMeasurements }, rest))] }));
13
+ import styled from "styled-components";
14
+ import { forwardRef, useState, useRef, useEffect } from "react";
15
+ import { TextArea, FieldLabel, DropDownMenu, SelectBox, } from "..";
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: opacity 0.2s ease, transform 0.2s ease;
34
+ `;
35
+ const StyledInsertSelectBox = styled(SelectBox) `
36
+ width: 100%;
37
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
38
+ border: 1px solid ${({ theme }) => theme.palette.primary.main};
39
+ `;
40
+ const TextAreaInput = forwardRef((props, ref) => {
41
+ const {
42
+ // UI
43
+ label, error, required, colSpan = 1, size = "sm", description, maxRows = 6, minRows = 3, onHeightChange, cacheMeasurements,
44
+ // Action menu
45
+ showActionMenu = false, actionMenuOptions = DEFAULT_ACTIONS, onActionMenuSelect,
46
+ // Insertable items
47
+ insertableItems, onInsertItem } = props,
48
+ // Rest of props for TextArea
49
+ rest = __rest(props, ["label", "error", "required", "colSpan", "size", "description", "maxRows", "minRows", "onHeightChange", "cacheMeasurements", "showActionMenu", "actionMenuOptions", "onActionMenuSelect", "insertableItems", "onInsertItem"]);
50
+ // State for insert menu visibility
51
+ const [showInsertMenu, setShowInsertMenu] = useState(false);
52
+ const [triggerSelectBoxOpen, setTriggerSelectBoxOpen] = useState(false);
53
+ const textareaRef = useRef(null);
54
+ const insertMenuRef = useRef(null);
55
+ // Merge refs
56
+ const mergedRef = (node) => {
57
+ textareaRef.current = node;
58
+ if (typeof ref === "function") {
59
+ ref(node);
60
+ }
61
+ else if (ref) {
62
+ ref.current = node;
63
+ }
64
+ };
65
+ // Handle click outside to close insert menu
66
+ useEffect(() => {
67
+ if (!showInsertMenu)
68
+ return;
69
+ const handleClickOutside = (event) => {
70
+ if (insertMenuRef.current &&
71
+ !insertMenuRef.current.contains(event.target)) {
72
+ setShowInsertMenu(false);
73
+ }
74
+ };
75
+ document.addEventListener("mousedown", handleClickOutside);
76
+ return () => {
77
+ document.removeEventListener("mousedown", handleClickOutside);
78
+ };
79
+ }, [showInsertMenu]);
80
+ const handleActionSelect = (item) => {
81
+ if (item.value === "insert" && (insertableItems === null || insertableItems === void 0 ? void 0 : insertableItems.length)) {
82
+ setShowInsertMenu(true);
83
+ // Trigger SelectBox to open using the new enhanced props
84
+ setTriggerSelectBoxOpen(true);
85
+ }
86
+ else {
87
+ onActionMenuSelect === null || onActionMenuSelect === void 0 ? void 0 : onActionMenuSelect(item);
88
+ }
89
+ };
90
+ const handleSelectBoxOpened = () => {
91
+ // Reset the trigger after SelectBox has opened
92
+ setTriggerSelectBoxOpen(false);
93
+ };
94
+ const handleInsertSelect = (value, option) => {
95
+ console.log("Selected value:", value, "Selected option:", option); // Debug log
96
+ // SelectBox passes (value, option) - we want the full option object
97
+ const item = option;
98
+ if (!item || !item.value) {
99
+ console.warn("Invalid item selected:", item);
100
+ setShowInsertMenu(false);
101
+ return;
102
+ }
103
+ if (onInsertItem) {
104
+ onInsertItem(item);
105
+ }
106
+ else {
107
+ // Default behavior: insert at current cursor position
108
+ const textarea = textareaRef.current;
109
+ if (textarea) {
110
+ const start = textarea.selectionStart;
111
+ const end = textarea.selectionEnd;
112
+ const currentValue = textarea.value;
113
+ const newValue = currentValue.slice(0, start) + item.value + currentValue.slice(end);
114
+ textarea.value = newValue;
115
+ textarea.focus();
116
+ textarea.setSelectionRange(start + item.value.length, start + item.value.length);
117
+ // Trigger change event
118
+ const event = new Event("input", { bubbles: true });
119
+ textarea.dispatchEvent(event);
120
+ }
121
+ }
122
+ setShowInsertMenu(false);
123
+ };
124
+ 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: {
125
+ "aria-label": "Open actions",
126
+ style: {
127
+ minWidth: "auto",
128
+ border: "none",
129
+ background: "transparent",
130
+ padding: 0,
131
+ margin: 0,
132
+ height: 16,
133
+ width: 16,
134
+ },
135
+ }, children: _jsx(MoreHorizontal, { 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)), (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 }) }))] })] }));
21
136
  });
137
+ TextAreaInput.displayName = "TextAreaInput";
22
138
  export default TextAreaInput;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monolith-forensics/monolith-ui",
3
- "version": "1.3.95",
3
+ "version": "1.3.97",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "author": "Matt Danner (Monolith Forensics LLC)",