@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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
14
|
-
import { forwardRef } from "react";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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;
|