@monolith-forensics/monolith-ui 1.0.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/src/DateBox.js ADDED
@@ -0,0 +1,77 @@
1
+ import { useEffect } from "react";
2
+ import { useState, useRef } from "react";
3
+ import styled from "@emotion/styled";
4
+ import moment from "moment";
5
+ import SelectBox from "./SelectBox";
6
+ import Calendar from "./Calendar/Calendar";
7
+
8
+ const DateBox = styled(
9
+ ({
10
+ className,
11
+ labelName,
12
+ fieldName = Date.now(),
13
+ defaultValue = moment(),
14
+ onChange = () => {},
15
+ min,
16
+ max,
17
+ placeholder,
18
+ mask = "YYYY-MM-DD HH:mm:ss",
19
+ ...props
20
+ }) => {
21
+ const [stateValue, setStateValue] = useState(
22
+ defaultValue ? defaultValue : null
23
+ );
24
+ const selectBoxRef = useRef(null);
25
+
26
+ const hourFormat = mask.includes("HH") ? "24" : "12";
27
+
28
+ const handleChange = (data) => {
29
+ console.log(data);
30
+ // selectBoxRef.current.close();
31
+ selectBoxRef.current.setValue(moment(data).format(mask));
32
+
33
+ // Apply Date mask
34
+ const date = moment(data).format(mask);
35
+ setStateValue(date);
36
+ onChange({
37
+ fieldName: fieldName,
38
+ newValue: moment(data).toISOString(),
39
+ previousValue: moment(data).toISOString(),
40
+ });
41
+ };
42
+
43
+ useEffect(() => {
44
+ console.log("Acquire Date", stateValue);
45
+ }, [stateValue]);
46
+
47
+ return (
48
+ <SelectBox
49
+ selectBoxRef={selectBoxRef}
50
+ fieldName={fieldName}
51
+ labelName={labelName}
52
+ placeholder="Select Date"
53
+ // allowTextInput={true}
54
+ defaultValue={moment(stateValue).format(mask)}
55
+ style={{ marginBottom: 10 }}
56
+ dropDownStyle={{
57
+ maxHeight: 350,
58
+ display: "flex",
59
+ justifyContent: "center",
60
+ padding: 0,
61
+ width: "fit-content",
62
+ }}
63
+ >
64
+ <Calendar
65
+ key={1}
66
+ date={stateValue && moment(stateValue).toDate()}
67
+ hourFormat={hourFormat}
68
+ onDateChanged={handleChange}
69
+ />
70
+ </SelectBox>
71
+ );
72
+ }
73
+ )``;
74
+
75
+ DateBox.displayName = "DateBox";
76
+
77
+ export default DateBox;
package/src/Form.js ADDED
@@ -0,0 +1,116 @@
1
+ import { useState } from "react";
2
+ import { useEffect } from "react";
3
+ import { cloneElement } from "react";
4
+ import styled from "@emotion/styled";
5
+
6
+ const EditorTypes = [
7
+ "Input",
8
+ "TextArea",
9
+ "TextBox",
10
+ "TextAreaBox",
11
+ "NumberBox",
12
+ "SelectBox",
13
+ "DateBox",
14
+ "FormGroup",
15
+ "TagBox",
16
+ ];
17
+
18
+ const Form = styled(
19
+ ({
20
+ className,
21
+ children,
22
+ defaultFormData,
23
+ onChange = () => {},
24
+ style = {},
25
+ }) => {
26
+ const [stateFormData, setStateFormData] = useState(defaultFormData || {});
27
+
28
+ const handleUpdate = (data) => {
29
+ const { fieldName, newValue } = data;
30
+ onChange({
31
+ ...stateFormData,
32
+ [fieldName]: newValue,
33
+ });
34
+ setStateFormData({
35
+ ...stateFormData,
36
+ [fieldName]: newValue,
37
+ });
38
+ };
39
+
40
+ useEffect(() => {
41
+ console.log(stateFormData);
42
+ }, [stateFormData]);
43
+
44
+ return (
45
+ <div className={className + " form-container"} style={{ ...style }}>
46
+ {children.map((child) => {
47
+ if (EditorTypes.includes(child.type.displayName)) {
48
+ return cloneElement(child, {
49
+ onChange: handleUpdate,
50
+ className: "grid-item",
51
+ defaultValue: stateFormData[child.props.fieldName],
52
+ stateFormData: stateFormData,
53
+ });
54
+ }
55
+ return cloneElement(child, { className: "grid-item" });
56
+ })}
57
+ </div>
58
+ );
59
+ }
60
+ )`
61
+ display: grid;
62
+ grid-template-columns: repeat(2, calc(50% - 25px));
63
+ grid-gap: 10px;
64
+ grid-column-gap: 50px;
65
+ // grid-column-max-width: 300px;
66
+ margin: 10px 0;
67
+ .grid-item {
68
+ width: 100%;
69
+ }
70
+ `;
71
+
72
+ Form.displayName = "Form";
73
+
74
+ export const FormGroup = styled(
75
+ ({
76
+ className,
77
+ children,
78
+ colSpan = 1,
79
+ colCount = 2,
80
+ columnGap = 20,
81
+ onChange = () => {},
82
+ stateFormData,
83
+ style = {},
84
+ }) => {
85
+ return (
86
+ <div
87
+ className={className}
88
+ style={{
89
+ gridTemplateColumns: `repeat(${colCount}, 1fr)`,
90
+ gridColumn: `span ${colSpan}`,
91
+ columnGap: `${columnGap}px`,
92
+ ...style,
93
+ }}
94
+ >
95
+ {children.map((child) => {
96
+ if (EditorTypes.includes(child.type.displayName)) {
97
+ return cloneElement(child, {
98
+ onChange: onChange,
99
+ className: "grid-item",
100
+ defaultValue: stateFormData[child.props.fieldName],
101
+ });
102
+ }
103
+ return cloneElement(child, { className: "grid-item" });
104
+ })}
105
+ </div>
106
+ );
107
+ }
108
+ )`
109
+ display: grid;
110
+ grid-template-columns: repeat(2, 1fr);
111
+ column-gap: 20px;
112
+ `;
113
+
114
+ FormGroup.displayName = "FormGroup";
115
+
116
+ export default Form;
package/src/Input.js ADDED
@@ -0,0 +1,160 @@
1
+ import { useRef } from "react";
2
+ import { useEffect, forwardRef, useState } from "react";
3
+ import styled from "@emotion/styled";
4
+
5
+ const Input = styled(
6
+ ({
7
+ className,
8
+ fieldName = Date.now(),
9
+ style = {},
10
+ inputStyle = {},
11
+ labelStyle = {},
12
+ placeholder = "",
13
+ labelName = null,
14
+ readOnly = false,
15
+ value = "",
16
+ defaultValue = "",
17
+ type = "text",
18
+ inputRef = useRef(null),
19
+ min = null,
20
+ max = null,
21
+ disabled = false,
22
+ autoFocus = false,
23
+ onChange = () => {},
24
+ onFocus = () => {},
25
+ onBlur = () => {},
26
+ onKeyPress = () => {},
27
+ onKeyDown = () => {},
28
+ }) => {
29
+ const [inputValue, setInputValue] = useState(
30
+ !!defaultValue ? defaultValue : ""
31
+ );
32
+
33
+ const handleChange = (e) => {
34
+ onChange({
35
+ event: e,
36
+ fieldName: fieldName,
37
+ newValue: !!e.target.value ? e.target.value : null,
38
+ previousValue: inputValue,
39
+ });
40
+
41
+ setInputValue(e.target.value);
42
+ };
43
+
44
+ const handleBlur = (e) => {
45
+ onBlur({
46
+ event: e,
47
+ fieldName: fieldName,
48
+ newValue: !!e.target.value ? e.target.value : null,
49
+ previousValue: inputValue,
50
+ });
51
+ };
52
+
53
+ useEffect(() => {
54
+ setInputValue(defaultValue);
55
+ }, [defaultValue]);
56
+
57
+ return (
58
+ <div className={className} style={{ ...style }}>
59
+ {labelName && (
60
+ <InputLabel
61
+ labelName={labelName}
62
+ style={{ ...labelStyle }}
63
+ ></InputLabel>
64
+ )}
65
+ <input
66
+ ref={inputRef}
67
+ onChange={handleChange}
68
+ onFocus={onFocus}
69
+ onBlur={handleBlur}
70
+ onKeyPress={onKeyPress}
71
+ onKeyDown={onKeyDown}
72
+ type={type}
73
+ readOnly={readOnly}
74
+ placeholder={placeholder}
75
+ value={inputValue}
76
+ autoFocus={autoFocus}
77
+ min={min}
78
+ max={999}
79
+ // style={{ ...inputStyle }}
80
+ ></input>
81
+ </div>
82
+ );
83
+ }
84
+ )((props) => ({
85
+ "& input": {
86
+ fontSize: props?.inputStyle?.fontSize || 13,
87
+ border:
88
+ props?.inputStyle?.border ||
89
+ `1px solid ${props.theme.palette.input.border}`,
90
+ color: props.theme.palette.text.primary,
91
+ outline: "none",
92
+ padding: props?.inputStyle?.padding || "8px 8px",
93
+ width: props?.inputStyle?.width || "100%",
94
+ backgroundColor:
95
+ props?.inputStyle?.backgroundColor ||
96
+ props.theme.palette.input.background,
97
+ borderRadius: props?.inputStyle?.borderRadius || 5,
98
+ transition: "border 0.1s ease-in-out",
99
+ "::placeholder": {
100
+ color: props.theme.palette.input.placeholder,
101
+ },
102
+ "&:hover": {
103
+ border:
104
+ props?.inputStyle?.hover?.border ||
105
+ "1px solid " + props.theme.palette.input.border,
106
+ },
107
+ "&:focus": {
108
+ border: `1px solid ${props.theme.palette.primary.main}`,
109
+ },
110
+ },
111
+ }));
112
+
113
+ const test = `
114
+ // width: 100%;
115
+ input {
116
+ font-size: ${(props) => props?.inputStyle?.fontSize || "13px"};
117
+ border: ${(props) =>
118
+ props?.inputStyle?.border ||
119
+ "1px solid " + props.theme.palette.input.border};
120
+ color: ${(props) => props.theme.palette.text.primary};
121
+ outline: none;
122
+ padding: ${(props) => props?.inputStyle?.padding || "8px 8px"};
123
+ width: ${(props) => props?.inputStyle?.width || "100%"};
124
+ background-color: ${(props) =>
125
+ props?.inputStyle?.backgroundColor ||
126
+ props.theme.palette.input.background};
127
+ border-radius: 5px;
128
+ transition: border 0.1s ease-in-out;
129
+ ::placeholder {
130
+ color: ${(props) => props.theme.palette.input.placeholder};
131
+ }
132
+ &:hover {
133
+ border: ${(props) =>
134
+ props?.inputStyle?.hover?.border ||
135
+ "1px solid " + props.theme.palette.input.border};
136
+ }
137
+ &:focus {
138
+ border: 1px solid ${(props) => props.theme.palette.primary.main};
139
+ }
140
+ }
141
+ `;
142
+
143
+ Input.displayName = "Input";
144
+
145
+ export const InputLabel = styled(
146
+ ({ className, style = {}, labelName = "" }) => {
147
+ return (
148
+ <div className={className} style={{ ...style }}>
149
+ {labelName}
150
+ </div>
151
+ );
152
+ }
153
+ )`
154
+ color: ${(props) => props.theme.palette.text.secondary};
155
+ margin-bottom: 5px;
156
+ `;
157
+
158
+ InputLabel.displayName = "InputLabel";
159
+
160
+ export default Input;
package/src/Menu.js ADDED
@@ -0,0 +1,196 @@
1
+ import styled from "@emotion/styled";
2
+ import { CSSTransition } from "react-transition-group";
3
+ import { useMemo } from "react";
4
+ import { cloneElement } from "react";
5
+ import { useRef } from "react";
6
+ import { useEffect } from "react";
7
+ import { useState } from "react";
8
+
9
+ const Menu = styled(
10
+ ({
11
+ className,
12
+ children,
13
+ anchorEl,
14
+ open = false,
15
+ onClose = () => {},
16
+ onItemSelect = () => {},
17
+ dropDownStyle = {},
18
+ }) => {
19
+ const listBoxRef = useRef(null);
20
+ const [show, setShow] = useState(open);
21
+
22
+ function alignMenu() {
23
+ const { left, right, top, width, height } =
24
+ anchorEl.getBoundingClientRect();
25
+ const { innerWidth, innerHeight, screenX, screenY } = window;
26
+ const { offsetWidth, offsetHeight } = listBoxRef.current;
27
+
28
+ const x = left + offsetWidth > innerWidth ? right - offsetWidth : left;
29
+ const y =
30
+ top + height + offsetHeight > innerHeight
31
+ ? top - offsetHeight
32
+ : top + height;
33
+
34
+ listBoxRef.current.style.left = `${x}px`;
35
+ listBoxRef.current.style.top = `${y}px`;
36
+ }
37
+
38
+ //disable scroll events when menu is open
39
+ useEffect(() => {
40
+ if (open) {
41
+ document.addEventListener("scroll", handleScroll, true);
42
+ } else {
43
+ document.removeEventListener("scroll", handleScroll, true);
44
+ }
45
+ }, [open]);
46
+
47
+ const handleScroll = (e) => {
48
+ e.preventDefault();
49
+ e.stopPropagation();
50
+ };
51
+
52
+ // Close dropdown when clicked outside of the dropdown
53
+ useEffect(() => {
54
+ function handleClickOutside(event) {
55
+ if (listBoxRef.current && !listBoxRef.current.contains(event.target)) {
56
+ setShow(false);
57
+ onClose();
58
+ }
59
+ }
60
+ // Bind the event listener
61
+ document.addEventListener("mousedown", handleClickOutside);
62
+ return () => {
63
+ // Unbind the event listener on clean up
64
+ document.removeEventListener("mousedown", handleClickOutside);
65
+ };
66
+ }, []);
67
+
68
+ useEffect(() => {
69
+ setShow(open);
70
+ }, [open]);
71
+
72
+ if (!children || children.length === 0) return null;
73
+
74
+ return (
75
+ <>
76
+ <CSSTransition
77
+ in={show}
78
+ nodeRef={listBoxRef}
79
+ timeout={300}
80
+ classNames="select-box-options"
81
+ unmountOnExit
82
+ onEnter={() => alignMenu()}
83
+ // onExited={() => setShowButton(true)}
84
+ >
85
+ <div className={className}>
86
+ <div
87
+ ref={listBoxRef}
88
+ className={"select-box-options"}
89
+ style={{ ...dropDownStyle }}
90
+ onClick={(e) => e.stopPropagation()}
91
+ >
92
+ {children?.length
93
+ ? children.map((child) =>
94
+ cloneElement(child, {
95
+ key: child.props.key,
96
+ onItemSelect: onItemSelect,
97
+ })
98
+ )
99
+ : cloneElement(children, {
100
+ // key: children.props.key,
101
+ onItemSelect: onItemSelect,
102
+ })}
103
+ </div>
104
+ <div
105
+ className="select-box-backdrop"
106
+ onClick={(e) => e.stopPropagation()}
107
+ ></div>
108
+ </div>
109
+ </CSSTransition>
110
+ </>
111
+ );
112
+ }
113
+ )`
114
+ .select-box-backdrop {
115
+ position: fixed;
116
+ top: 0;
117
+ left: 0;
118
+ right: 0;
119
+ bottom: 0;
120
+ z-index: 4000;
121
+ width: 100vw;
122
+ height: 100vh;
123
+ cursor: default;
124
+ // background-color: rgba(0, 0, 0, 0.5);
125
+ }
126
+ .select-box-options {
127
+ position: fixed;
128
+ min-width: 150px;
129
+ min-height: 100px;
130
+ max-height: 325px;
131
+ overflow-y: auto;
132
+ background-color: ${(props) => props.theme.palette.background.default};
133
+ // background-color: ${(props) => props.theme.palette.input.background};
134
+ border: 1px solid ${(props) => props.theme.palette.divider};
135
+ border-radius: 5px;
136
+ z-index: 4001;
137
+ padding: 5px;
138
+ box-shadow: 2px 3px 10px 0px rgba(0, 0, 0, 0.2);
139
+ }
140
+ .select-box-options-enter {
141
+ opacity: 0;
142
+ transform: scale(0.9);
143
+ }
144
+ .select-box-options-enter-active {
145
+ opacity: 1;
146
+ transform: translateY(0);
147
+ transition: opacity 100ms, transform 100ms;
148
+ }
149
+ .select-box-options-exit {
150
+ opacity: 1;
151
+ }
152
+ .select-box-options-exit-active {
153
+ opacity: 0;
154
+ transform: scale(0.9);
155
+ transition: opacity 100ms, transform 100ms;
156
+ }
157
+ `;
158
+
159
+ Menu.displayName = "Menu";
160
+
161
+ export const MenuItem = styled(
162
+ ({
163
+ className,
164
+ children,
165
+ data = null,
166
+ onItemSelect = () => {},
167
+ style = {},
168
+ }) => {
169
+ return (
170
+ <div
171
+ className={`${className} select-box-item`}
172
+ style={{ ...style }}
173
+ onClick={(e) => {
174
+ e.stopPropagation();
175
+ onItemSelect(data);
176
+ }}
177
+ >
178
+ {children}
179
+ </div>
180
+ );
181
+ }
182
+ )`
183
+ border-radius: 5px;
184
+ cursor: pointer;
185
+ user-select: none;
186
+ padding: 5px;
187
+ font-weight: normal;
188
+ font-size: 12px;
189
+ &:hover {
190
+ background-color: ${(props) => props.theme.palette.action.hover};
191
+ }
192
+ `;
193
+
194
+ MenuItem.displayName = "MenuItem";
195
+
196
+ export default Menu;
@@ -0,0 +1,121 @@
1
+ import { useRef } from "react";
2
+ import styled from "@emotion/styled";
3
+ import Input from "./Input";
4
+ import ArrowDropDownOutlinedIcon from "@mui/icons-material/ArrowDropDownOutlined";
5
+ import ArrowDropUpOutlinedIcon from "@mui/icons-material/ArrowDropUpOutlined";
6
+ import { useState } from "react";
7
+ import { useEffect } from "react";
8
+
9
+ const NumberBox = styled(
10
+ ({
11
+ className,
12
+ fieldName = Date.now(),
13
+ defaultValue,
14
+ onChange = () => {},
15
+ min,
16
+ max,
17
+ placeholder,
18
+ inputStyle = {},
19
+ showArrows = true,
20
+ ...props
21
+ }) => {
22
+ const numberBoxRef = useRef(null);
23
+ const [stateValue, setStateValue] = useState(defaultValue || 0);
24
+
25
+ const handleIncrease = () => {
26
+ numberBoxRef.current.stepUp();
27
+ onChange({
28
+ fieldName: fieldName,
29
+ newValue: numberBoxRef.current.value,
30
+ previousValue: stateValue,
31
+ });
32
+ setStateValue(numberBoxRef.current.value);
33
+ };
34
+
35
+ const handleDecrease = () => {
36
+ numberBoxRef.current.stepDown();
37
+ onChange({
38
+ fieldName: fieldName,
39
+ newValue: numberBoxRef.current.value,
40
+ previousValue: stateValue,
41
+ });
42
+ setStateValue(numberBoxRef.current.value);
43
+ };
44
+
45
+ useEffect(() => {
46
+ const inputBox = numberBoxRef.current;
47
+
48
+ const validateNumber = (e) => {
49
+ e.preventDefault();
50
+ if (e.target.value < min) {
51
+ e.target.value = min;
52
+ } else if (e.target.value > max) {
53
+ e.target.value = max;
54
+ }
55
+ };
56
+
57
+ inputBox.addEventListener("keydown", validateNumber);
58
+ inputBox.addEventListener("change", validateNumber);
59
+
60
+ return () => {
61
+ inputBox.removeEventListener("keydown", validateNumber);
62
+ inputBox.removeEventListener("change", validateNumber);
63
+ };
64
+ }, []);
65
+
66
+ return (
67
+ <div className={className}>
68
+ <Input
69
+ inputRef={numberBoxRef}
70
+ labelName={props.labelName}
71
+ type={"number"}
72
+ placeholder={placeholder}
73
+ min={min}
74
+ max={max}
75
+ defaultValue={stateValue}
76
+ inputStyle={inputStyle}
77
+ ></Input>
78
+ {showArrows && (
79
+ <div className="select-box-arrow">
80
+ <div style={{ position: "absolute" }}>
81
+ <div style={{ height: 15 }} onClick={handleIncrease}>
82
+ <ArrowDropUpOutlinedIcon style={{ position: "relative" }} />
83
+ </div>
84
+ <div style={{ height: 15 }} onClick={handleDecrease}>
85
+ <ArrowDropDownOutlinedIcon style={{ position: "relative" }} />
86
+ </div>
87
+ </div>
88
+ </div>
89
+ )}
90
+ </div>
91
+ );
92
+ }
93
+ )`
94
+ position: relative;
95
+ // hide arrows
96
+ input::-webkit-outer-spin-button,
97
+ input::-webkit-inner-spin-button {
98
+ -webkit-appearance: none;
99
+ margin: 0;
100
+ }
101
+ input[type="number"] {
102
+ -moz-appearance: textfield;
103
+ }
104
+ .select-box-arrow {
105
+ position: absolute;
106
+ height: 30px;
107
+ right: 8px;
108
+ top: 23px;
109
+ bottom: 0;
110
+ display: flex;
111
+ align-items: center;
112
+ justify-content: center;
113
+ padding: 0 8px;
114
+ color: ${(props) => props.theme.palette.text.secondary};
115
+ cursor: pointer;
116
+ }
117
+ `;
118
+
119
+ NumberBox.displayName = "NumberBox";
120
+
121
+ export default NumberBox;