pixel-react 1.1.2 → 1.1.4
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/.yarn/install-state.gz +0 -0
- package/lib/components/AddButton/AddButton.d.ts +5 -0
- package/lib/components/AddButton/AddButton.stories.d.ts +6 -0
- package/lib/components/AddButton/index.d.ts +1 -0
- package/lib/components/AddButton/types.d.ts +4 -0
- package/lib/components/AppHeader/types.d.ts +14 -10
- package/lib/components/Paper/Paper.d.ts +4 -0
- package/lib/components/Paper/Paper.stories.d.ts +11 -0
- package/lib/components/Paper/index.d.ts +1 -0
- package/lib/components/Paper/types.d.ts +15 -0
- package/lib/components/VariableInput/VariableInput.d.ts +4 -0
- package/lib/components/VariableInput/VariableInput.stories.d.ts +6 -0
- package/lib/components/VariableInput/index.d.ts +1 -0
- package/lib/components/VariableInput/types.d.ts +53 -0
- package/lib/index.d.ts +129 -11
- package/lib/index.esm.js +6934 -977
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +6943 -976
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/ffID/ffID.stories.d.ts +1 -1
- package/lib/utils/ffID/ffid.d.ts +1 -2
- package/lib/utils/findAndInsert/findAndInsert.d.ts +7 -0
- package/lib/utils/findAndInsert/findAndInsert.stories.d.ts +7 -0
- package/lib/utils/getEncryptedData/getEncryptedData.d.ts +1 -2
- package/package.json +1 -1
- package/src/assets/Themes/BaseTheme.scss +2 -0
- package/src/assets/Themes/DarkTheme.scss +2 -0
- package/src/components/AllProjectsDropdown/AllProjectsDropdown.tsx +1 -1
- package/src/components/AppHeader/AppHeader.stories.tsx +20 -10
- package/src/components/AppHeader/AppHeader.tsx +24 -17
- package/src/components/AppHeader/types.ts +17 -13
- package/src/components/NLPInput/components/NlpDropDown/NlpDropdown.tsx +1 -1
- package/src/components/Paper/Paper.scss +13 -0
- package/src/components/Paper/Paper.stories.tsx +77 -0
- package/src/components/Paper/Paper.tsx +14 -0
- package/src/components/Paper/index.ts +1 -0
- package/src/components/Paper/types.ts +19 -0
- package/src/components/Select/components/Dropdown/Dropdown.tsx +1 -1
- package/src/components/VariableInput/VariableInput.scss +128 -0
- package/src/components/VariableInput/VariableInput.stories.tsx +32 -0
- package/src/components/VariableInput/VariableInput.tsx +352 -0
- package/src/components/VariableInput/index.ts +1 -0
- package/src/components/VariableInput/types.ts +56 -0
- package/src/index.ts +27 -2
- package/src/utils/ffID/ffID.stories.tsx +1 -1
- package/src/utils/ffID/ffid.ts +1 -3
- package/src/utils/getEncryptedData/getEncryptedData.stories.tsx +3 -3
- package/src/utils/getEncryptedData/getEncryptedData.ts +1 -3
- /package/src/utils/{find → findAndInsert}/findAndInsert.stories.tsx +0 -0
- /package/src/utils/{find → findAndInsert}/findAndInsert.ts +0 -0
@@ -0,0 +1,128 @@
|
|
1
|
+
.ff_variable_input_container {
|
2
|
+
.ff_label_container {
|
3
|
+
display: flex;
|
4
|
+
align-items: center;
|
5
|
+
margin-bottom: 4px;
|
6
|
+
|
7
|
+
&.ff_danger_label {
|
8
|
+
.ff_input_label {
|
9
|
+
color: var(--error);
|
10
|
+
}
|
11
|
+
|
12
|
+
.ff_required_asterisk {
|
13
|
+
color: var(--error);
|
14
|
+
}
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
.ff_required_asterisk {
|
19
|
+
color: var(--input-error-text-color);
|
20
|
+
font-size: 1.1em;
|
21
|
+
margin-right: 4px;
|
22
|
+
}
|
23
|
+
|
24
|
+
.ff_input_label {
|
25
|
+
font-size: 14px;
|
26
|
+
font-weight: 500;
|
27
|
+
color: var(--text-color);
|
28
|
+
|
29
|
+
&.ff_no_hover {
|
30
|
+
color: var(--toggle-disable-icon-color);
|
31
|
+
}
|
32
|
+
|
33
|
+
&.ff_disabled_label {
|
34
|
+
color: var(--toggle-disable-icon-color);
|
35
|
+
cursor: not-allowed;
|
36
|
+
}
|
37
|
+
|
38
|
+
&.ff_danger_label {
|
39
|
+
color: var(--error);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
.ff_content_editable {
|
44
|
+
min-width: 8rem;
|
45
|
+
border: 1px solid var(--input-default-border-color);
|
46
|
+
padding: 8px;
|
47
|
+
min-height: 30px;
|
48
|
+
border-radius: 4px;
|
49
|
+
font-family: Arial, sans-serif;
|
50
|
+
font-size: 14px;
|
51
|
+
line-height: 20px;
|
52
|
+
transition: border-color 0.2s ease;
|
53
|
+
position: relative;
|
54
|
+
text-align: left;
|
55
|
+
display: flex;
|
56
|
+
align-items: center;
|
57
|
+
overflow: auto;
|
58
|
+
|
59
|
+
.ff_var_red {
|
60
|
+
color: var(--status-rejected-text-color);
|
61
|
+
}
|
62
|
+
|
63
|
+
.ff_var_def_color {
|
64
|
+
color: var(--text-color);
|
65
|
+
}
|
66
|
+
|
67
|
+
.ff_var_green {
|
68
|
+
color: var(--status-success-text-color);
|
69
|
+
}
|
70
|
+
|
71
|
+
&::after {
|
72
|
+
content: '*';
|
73
|
+
color: var(--input-error-text-color);
|
74
|
+
font-size: 1.2em;
|
75
|
+
position: absolute;
|
76
|
+
top: 2px;
|
77
|
+
right: 2px;
|
78
|
+
display: none;
|
79
|
+
}
|
80
|
+
|
81
|
+
&.ff_required::after {
|
82
|
+
display: inline;
|
83
|
+
}
|
84
|
+
|
85
|
+
&.ff_required_empty:focus-within,
|
86
|
+
&.ff_required_empty:focus {
|
87
|
+
border-color: var(--input-error-text-color);
|
88
|
+
}
|
89
|
+
|
90
|
+
&:empty::before {
|
91
|
+
content: attr(data-placeholder);
|
92
|
+
color: var(--ff-search-filed-placeholder-text);
|
93
|
+
position: absolute;
|
94
|
+
user-select: none;
|
95
|
+
pointer-events: none;
|
96
|
+
font-size: medium;
|
97
|
+
}
|
98
|
+
|
99
|
+
&.ff_disabled {
|
100
|
+
pointer-events: none;
|
101
|
+
opacity: 0.5;
|
102
|
+
background-color: var(--input-default-border-color);
|
103
|
+
}
|
104
|
+
|
105
|
+
&.ff_invalid_input {
|
106
|
+
border: 1px solid var(--input-error-text-color);
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
.ff_suggestions {
|
111
|
+
border: 1px solid var(--input-default-border-color);
|
112
|
+
margin-top: 4px;
|
113
|
+
padding: 4px;
|
114
|
+
border-radius: 4px;
|
115
|
+
list-style-type: none;
|
116
|
+
background-color: var(--ff-select-background-color);
|
117
|
+
max-height: 150px;
|
118
|
+
overflow-y: auto;
|
119
|
+
}
|
120
|
+
|
121
|
+
.ff_suggestion_item {
|
122
|
+
padding: 4px;
|
123
|
+
cursor: pointer;
|
124
|
+
&:hover {
|
125
|
+
background-color: var(--ff-select-background-color);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
2
|
+
import VariableInput from './VariableInput';
|
3
|
+
|
4
|
+
const meta: Meta<typeof VariableInput> = {
|
5
|
+
title: 'Components/VariableInput',
|
6
|
+
component: VariableInput,
|
7
|
+
parameters: {
|
8
|
+
layout: 'centered',
|
9
|
+
},
|
10
|
+
tags: ['autodocs'],
|
11
|
+
};
|
12
|
+
|
13
|
+
type Story = StoryObj<typeof VariableInput>;
|
14
|
+
|
15
|
+
const defaultArgs = {
|
16
|
+
name: 'input',
|
17
|
+
label: '',
|
18
|
+
disabled: false,
|
19
|
+
placeholder: 'Enter URL',
|
20
|
+
value: '',
|
21
|
+
type: 'url',
|
22
|
+
list:['name','urlName','passwork','emailId','default','errorVar']
|
23
|
+
};
|
24
|
+
|
25
|
+
export const Default: Story = {
|
26
|
+
args: {
|
27
|
+
...defaultArgs,
|
28
|
+
label: 'Enter URL',
|
29
|
+
type: 'url',
|
30
|
+
},
|
31
|
+
};
|
32
|
+
export default meta;
|
@@ -0,0 +1,352 @@
|
|
1
|
+
import classNames from 'classnames';
|
2
|
+
import React, { useState, useRef, useEffect } from 'react';
|
3
|
+
import './VariableInput.scss';
|
4
|
+
import { VariableInputProps } from './types';
|
5
|
+
import Typography from '../Typography';
|
6
|
+
|
7
|
+
interface ParsedPart {
|
8
|
+
text: string;
|
9
|
+
textType: string;
|
10
|
+
}
|
11
|
+
|
12
|
+
const VariableInput = ({
|
13
|
+
type = 'url',
|
14
|
+
name = '',
|
15
|
+
label,
|
16
|
+
disabled = false,
|
17
|
+
required = false,
|
18
|
+
placeholder = '',
|
19
|
+
value = '',
|
20
|
+
error,
|
21
|
+
className = '',
|
22
|
+
onChange,
|
23
|
+
onKeyDown,
|
24
|
+
onBlur,
|
25
|
+
onFocus,
|
26
|
+
list=[],
|
27
|
+
...props
|
28
|
+
}: VariableInputProps) => {
|
29
|
+
const [inputValue, setInputValue] = useState<string>('');
|
30
|
+
const [cursorPosition, setCursorPosition] = useState<number>(0);
|
31
|
+
const [isAddingText, setIsAddingText] = useState<boolean>(true);
|
32
|
+
const [suggestions, setSuggestions] = useState<string[]>([]);
|
33
|
+
const [showSuggestions, setShowSuggestions] = useState<boolean>(false);
|
34
|
+
const contentEditableRef = useRef<HTMLDivElement>(null);
|
35
|
+
const undoStack = useRef<string[]>([]);
|
36
|
+
const redoStack = useRef<string[]>([]);
|
37
|
+
|
38
|
+
useEffect(() => {
|
39
|
+
if (value) {
|
40
|
+
setInputValue(value);
|
41
|
+
}
|
42
|
+
}, [value]);
|
43
|
+
|
44
|
+
const parseUrl = (url: string): ParsedPart[] => {
|
45
|
+
const regex = /\$\{(\w+)\}/g; //checking the text variable or not
|
46
|
+
const parts: ParsedPart[] = [];
|
47
|
+
let lastIndex = 0;
|
48
|
+
let match;
|
49
|
+
|
50
|
+
while ((match = regex.exec(url)) !== null) {
|
51
|
+
const [fullMatch, variable] = match;
|
52
|
+
const isInList = list?.includes(variable ?? '');
|
53
|
+
|
54
|
+
if (match.index > lastIndex) {
|
55
|
+
parts.push({ text: url.slice(lastIndex, match.index), textType: 'normal' });
|
56
|
+
}
|
57
|
+
|
58
|
+
parts.push({
|
59
|
+
text: fullMatch,
|
60
|
+
textType: isInList ? 'variable' : 'nonVariable',
|
61
|
+
});
|
62
|
+
|
63
|
+
lastIndex = match.index + fullMatch.length;
|
64
|
+
}
|
65
|
+
|
66
|
+
if (lastIndex < url.length) {
|
67
|
+
parts.push({ text: url.slice(lastIndex), textType: 'normal' });
|
68
|
+
}
|
69
|
+
|
70
|
+
return parts;
|
71
|
+
};
|
72
|
+
|
73
|
+
const updateContentEditable = () => {
|
74
|
+
const parsedParts = parseUrl(inputValue);
|
75
|
+
const contentEditableElement = contentEditableRef.current;
|
76
|
+
|
77
|
+
if (contentEditableElement) {
|
78
|
+
contentEditableElement.innerHTML = parsedParts
|
79
|
+
.map((part) => {
|
80
|
+
const varClassName = classNames({
|
81
|
+
['ff_var_red']: part.textType === 'nonVariable',
|
82
|
+
['ff_var_green']: part.textType === 'variable',
|
83
|
+
['ff_var_def_textType']:
|
84
|
+
part.textType !== 'nonVariable' && part.textType !== 'variable',
|
85
|
+
});
|
86
|
+
|
87
|
+
return `<Typography class="${varClassName}">${part.text}</Typography>`;
|
88
|
+
})
|
89
|
+
.join('');
|
90
|
+
restoreCursorPosition(contentEditableElement);
|
91
|
+
}
|
92
|
+
};
|
93
|
+
|
94
|
+
const restoreCursorPosition = (element: HTMLDivElement) => {
|
95
|
+
const selection = window.getSelection();
|
96
|
+
const range = document.createRange();
|
97
|
+
const textNodes = getTextNodes(element);
|
98
|
+
|
99
|
+
if (textNodes.length > 0) {
|
100
|
+
const lastNode = textNodes[textNodes.length - 1];
|
101
|
+
const positionAdjustment = isAddingText ? 1 : -1;
|
102
|
+
|
103
|
+
const newOffset = Math.max(
|
104
|
+
0,
|
105
|
+
Math.min(
|
106
|
+
cursorPosition + positionAdjustment,
|
107
|
+
(lastNode as Text).length || 0
|
108
|
+
)
|
109
|
+
);
|
110
|
+
if (lastNode) {
|
111
|
+
range.setStart(lastNode, newOffset);
|
112
|
+
}
|
113
|
+
range.collapse(true);
|
114
|
+
} else {
|
115
|
+
range.selectNodeContents(element);
|
116
|
+
range.collapse(false);
|
117
|
+
}
|
118
|
+
|
119
|
+
selection?.removeAllRanges();
|
120
|
+
selection?.addRange(range);
|
121
|
+
element.focus();
|
122
|
+
};
|
123
|
+
|
124
|
+
const getTextNodes = (element: HTMLDivElement): Node[] => {
|
125
|
+
const nodes: Node[] = [];
|
126
|
+
const walker = document.createTreeWalker(
|
127
|
+
element,
|
128
|
+
NodeFilter.SHOW_TEXT,
|
129
|
+
null
|
130
|
+
);
|
131
|
+
let node;
|
132
|
+
while ((node = walker.nextNode())) {
|
133
|
+
nodes.push(node);
|
134
|
+
}
|
135
|
+
return nodes;
|
136
|
+
};
|
137
|
+
|
138
|
+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
139
|
+
const text = e.currentTarget.innerText.split('\n').join('');
|
140
|
+
setInputValue(text);
|
141
|
+
undoStack.current.push(text);
|
142
|
+
redoStack.current = [];
|
143
|
+
|
144
|
+
if (required && !text.trim()) {
|
145
|
+
e.currentTarget.classList.add('ff_required_empty');
|
146
|
+
} else {
|
147
|
+
e.currentTarget.classList.remove('ff_required_empty');
|
148
|
+
}
|
149
|
+
|
150
|
+
if (type === 'url') {
|
151
|
+
const urlPattern = new RegExp(
|
152
|
+
'^(https?://)?([\\w.-]+)\\.([a-zA-Z]{2,})(/([\\w.-]+|\\$\\{[\\w.-]+\\}))*/?$'
|
153
|
+
);
|
154
|
+
|
155
|
+
if (!urlPattern.test(text)) {
|
156
|
+
e.currentTarget.classList.add('ff_invalid_input');
|
157
|
+
} else {
|
158
|
+
e.currentTarget.classList.remove('ff_invalid_input');
|
159
|
+
e.currentTarget.classList.remove('ff_required_empty');
|
160
|
+
}
|
161
|
+
} else {
|
162
|
+
e.currentTarget.classList.add('ff_invalid_input');
|
163
|
+
}
|
164
|
+
|
165
|
+
if (onChange) {
|
166
|
+
onChange(text);
|
167
|
+
}
|
168
|
+
|
169
|
+
const match = /\$\{(\w*)$/.exec(text);
|
170
|
+
if (match) {
|
171
|
+
const query = match[1];
|
172
|
+
const filteredSuggestions = list?.filter((item: string) =>
|
173
|
+
item.startsWith(query ?? '')
|
174
|
+
);
|
175
|
+
setSuggestions(filteredSuggestions);
|
176
|
+
setShowSuggestions(true);
|
177
|
+
} else {
|
178
|
+
setShowSuggestions(false);
|
179
|
+
}
|
180
|
+
};
|
181
|
+
|
182
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
183
|
+
const cursorPos = getCursorPosition();
|
184
|
+
setCursorPosition(cursorPos);
|
185
|
+
setIsAddingText(true);
|
186
|
+
|
187
|
+
const currentText = contentEditableRef.current
|
188
|
+
? contentEditableRef.current.textContent || ''
|
189
|
+
: '';
|
190
|
+
|
191
|
+
if (e.key === 'Backspace' || e.key === 'Delete') {
|
192
|
+
setIsAddingText(false);
|
193
|
+
if (!currentText.trim()) {
|
194
|
+
e.preventDefault();
|
195
|
+
if (contentEditableRef.current) {
|
196
|
+
contentEditableRef.current.textContent = '';
|
197
|
+
contentEditableRef.current.blur();
|
198
|
+
}
|
199
|
+
return;
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
if (e.key === 'Enter') {
|
204
|
+
e.preventDefault();
|
205
|
+
insertTextAtCursor('\n');
|
206
|
+
} else if (e.ctrlKey && e.key === 'z') {
|
207
|
+
e.preventDefault();
|
208
|
+
undo();
|
209
|
+
} else if (e.ctrlKey && e.key === 'y') {
|
210
|
+
e.preventDefault();
|
211
|
+
redo();
|
212
|
+
}
|
213
|
+
|
214
|
+
if (onKeyDown) {
|
215
|
+
onKeyDown(e as React.KeyboardEvent<HTMLInputElement>);
|
216
|
+
}
|
217
|
+
};
|
218
|
+
|
219
|
+
const getCursorPosition = (): number => {
|
220
|
+
const selection = window.getSelection();
|
221
|
+
if (selection && selection.rangeCount > 0) {
|
222
|
+
const range = selection.getRangeAt(0);
|
223
|
+
return range.startOffset;
|
224
|
+
}
|
225
|
+
return 0;
|
226
|
+
};
|
227
|
+
|
228
|
+
const undo = () => {
|
229
|
+
if (undoStack.current.length > 1) {
|
230
|
+
redoStack.current.push(undoStack.current.pop()!);
|
231
|
+
setInputValue(undoStack.current[undoStack.current.length - 1] ?? '');
|
232
|
+
setIsAddingText(false);
|
233
|
+
}
|
234
|
+
};
|
235
|
+
|
236
|
+
const redo = () => {
|
237
|
+
if (redoStack.current.length > 0) {
|
238
|
+
const textToRedo = redoStack.current.pop()!;
|
239
|
+
setInputValue(textToRedo);
|
240
|
+
undoStack.current.push(textToRedo);
|
241
|
+
setIsAddingText(true);
|
242
|
+
}
|
243
|
+
};
|
244
|
+
|
245
|
+
const insertTextAtCursor = (text: string) => {
|
246
|
+
const selection = window.getSelection();
|
247
|
+
const range = selection?.getRangeAt(0);
|
248
|
+
if (range) {
|
249
|
+
range.deleteContents();
|
250
|
+
const newTextNode = document.createTextNode(text);
|
251
|
+
range.insertNode(newTextNode);
|
252
|
+
|
253
|
+
const lineBreak = document.createElement('br');
|
254
|
+
range.insertNode(lineBreak);
|
255
|
+
|
256
|
+
range.setStartAfter(lineBreak);
|
257
|
+
range.collapse(true);
|
258
|
+
|
259
|
+
selection?.removeAllRanges();
|
260
|
+
selection?.addRange(range);
|
261
|
+
contentEditableRef.current?.focus();
|
262
|
+
setInputValue(contentEditableRef.current?.innerText || '');
|
263
|
+
}
|
264
|
+
};
|
265
|
+
|
266
|
+
const handleSuggestionClick = (suggestion: string) => {
|
267
|
+
const newText = inputValue.replace(/\$\{\w*$/, `\$\{${suggestion}`);
|
268
|
+
setInputValue(newText);
|
269
|
+
setShowSuggestions(false);
|
270
|
+
};
|
271
|
+
|
272
|
+
useEffect(() => {
|
273
|
+
updateContentEditable();
|
274
|
+
}, [inputValue]);
|
275
|
+
|
276
|
+
const inputClasses = classNames('ff_content_editable', {
|
277
|
+
['ff_placeholder']: !inputValue,
|
278
|
+
['ff_disabled']: disabled,
|
279
|
+
['ff_required']: required,
|
280
|
+
['ff_invalid_input']: error,
|
281
|
+
});
|
282
|
+
|
283
|
+
return (
|
284
|
+
<div className={'ff_variable_input_container'}>
|
285
|
+
{label && (
|
286
|
+
<label
|
287
|
+
htmlFor={name}
|
288
|
+
className={classNames('ff_label_container', {
|
289
|
+
['ff_labelDanger']: error,
|
290
|
+
})}
|
291
|
+
>
|
292
|
+
{required && (
|
293
|
+
<Typography className={'ff_required_asterisk'}>*</Typography>
|
294
|
+
)}
|
295
|
+
<Typography
|
296
|
+
className={classNames('ff_input_label', {
|
297
|
+
['ff_no_hover']: value,
|
298
|
+
['ff_disabled_label']: disabled,
|
299
|
+
['ff_danger_label']: error,
|
300
|
+
})}
|
301
|
+
>
|
302
|
+
{label}
|
303
|
+
</Typography>
|
304
|
+
</label>
|
305
|
+
)}
|
306
|
+
<div
|
307
|
+
id={name}
|
308
|
+
contentEditable={!disabled}
|
309
|
+
ref={contentEditableRef}
|
310
|
+
onInput={handleInputChange}
|
311
|
+
onKeyDown={handleKeyDown}
|
312
|
+
onFocus={(e) => {
|
313
|
+
e.currentTarget.style.outline = 'none';
|
314
|
+
if (onFocus) onFocus(e as React.FocusEvent<HTMLInputElement>);
|
315
|
+
}}
|
316
|
+
onBlur={(e) => {
|
317
|
+
e.currentTarget.style.outline = 'none';
|
318
|
+
if (onBlur) onBlur(e as React.FocusEvent<HTMLInputElement>);
|
319
|
+
|
320
|
+
if (required && !inputValue.trim()) {
|
321
|
+
e.currentTarget.classList.add('ff_required_empty');
|
322
|
+
} else {
|
323
|
+
e.currentTarget.classList.remove('ff_required_empty');
|
324
|
+
}
|
325
|
+
}}
|
326
|
+
className={inputClasses}
|
327
|
+
suppressContentEditableWarning
|
328
|
+
aria-required={required ? 'true' : 'false'}
|
329
|
+
data-name={name}
|
330
|
+
data-type={type}
|
331
|
+
data-placeholder={placeholder}
|
332
|
+
spellCheck={false}
|
333
|
+
{...props}
|
334
|
+
/>
|
335
|
+
{showSuggestions && (
|
336
|
+
<ul className={'ff_suggestions'}>
|
337
|
+
{suggestions.map((suggestion) => (
|
338
|
+
<li
|
339
|
+
key={suggestion}
|
340
|
+
onClick={() => handleSuggestionClick(suggestion)}
|
341
|
+
className={'ff_suggestion_item'}
|
342
|
+
>
|
343
|
+
{suggestion}
|
344
|
+
</li>
|
345
|
+
))}
|
346
|
+
</ul>
|
347
|
+
)}
|
348
|
+
</div>
|
349
|
+
);
|
350
|
+
};
|
351
|
+
|
352
|
+
export default VariableInput;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {default} from './VariableInput'
|
@@ -0,0 +1,56 @@
|
|
1
|
+
export interface VariableInputProps {
|
2
|
+
/**
|
3
|
+
* Name | name of the input field
|
4
|
+
*/
|
5
|
+
name: string;
|
6
|
+
/**
|
7
|
+
* Label | field label to be displayed
|
8
|
+
*/
|
9
|
+
label: string;
|
10
|
+
/**
|
11
|
+
* value | input field value
|
12
|
+
*/
|
13
|
+
value: string | null;
|
14
|
+
/**
|
15
|
+
* type to set color/style of the input field
|
16
|
+
*/
|
17
|
+
type: 'text' | 'password' | 'number' | 'email' | 'url' | 'time';
|
18
|
+
/**
|
19
|
+
* error | If true, error message will be displayed
|
20
|
+
*/
|
21
|
+
error?: boolean;
|
22
|
+
/**
|
23
|
+
* to disable the input field
|
24
|
+
*/
|
25
|
+
disabled?: boolean;
|
26
|
+
/**
|
27
|
+
* if true, input field will be mandatory
|
28
|
+
*/
|
29
|
+
required?: boolean;
|
30
|
+
/**
|
31
|
+
* placeholder for the input field
|
32
|
+
*/
|
33
|
+
placeholder?: string;
|
34
|
+
/**
|
35
|
+
* classnames to style the input field
|
36
|
+
*/
|
37
|
+
className?: string;
|
38
|
+
/**
|
39
|
+
* onChange, onKeyDown, onBlur, onFocus actions
|
40
|
+
*/
|
41
|
+
onChange?: (value: string) => void | undefined;
|
42
|
+
|
43
|
+
onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
|
44
|
+
|
45
|
+
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
|
46
|
+
|
47
|
+
onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
|
48
|
+
/**
|
49
|
+
* id to select the input field uniquely
|
50
|
+
*/
|
51
|
+
id?: string;
|
52
|
+
/**
|
53
|
+
* list of variables
|
54
|
+
*/
|
55
|
+
list?: string[];
|
56
|
+
}
|
package/src/index.ts
CHANGED
@@ -38,10 +38,12 @@ import StateDropdown from './components/StateDropdown';
|
|
38
38
|
import IconButton from './components/IconButton';
|
39
39
|
import Modal from './components/Modal';
|
40
40
|
import DragAndDrop from './components/DragAndDrop/DragAndDrop';
|
41
|
+
import VariableInput from './components/VariableInput';
|
41
42
|
import AllProjectsDropdown from './components/AllProjectsDropdown';
|
42
43
|
import PieChart from './components/Charts/PieChart';
|
43
44
|
import AppHeader from './components/AppHeader';
|
44
|
-
import
|
45
|
+
import Paper from './components/Paper';
|
46
|
+
import DashboardDonutChart from './components/Charts/DashboardDonutChart';
|
45
47
|
import Recaptcha from './components/FF_Captcha/Recaptcha';
|
46
48
|
import NLPInput from './components/NLPInput';
|
47
49
|
|
@@ -52,6 +54,20 @@ import {
|
|
52
54
|
getExtensionWithPeriod,
|
53
55
|
} from './utils/getExtension/getExtension';
|
54
56
|
|
57
|
+
import { findAndInsert } from './utils/findAndInsert/findAndInsert';
|
58
|
+
import { ffid } from './utils/ffID/ffid';
|
59
|
+
|
60
|
+
import { debounce } from './utils/debounce/debounce';
|
61
|
+
import { compareArrays } from './utils/compareArrays/compareArrays';
|
62
|
+
|
63
|
+
import { compareObjects } from './utils/compareObjects/compareObjects';
|
64
|
+
|
65
|
+
import { getEncryptedData } from './utils/getEncryptedData/getEncryptedData';
|
66
|
+
|
67
|
+
import { throttle } from './utils/throttle/throttle';
|
68
|
+
|
69
|
+
import { truncateText } from './utils/truncateText/truncateText';
|
70
|
+
|
55
71
|
export {
|
56
72
|
Button,
|
57
73
|
Tooltip,
|
@@ -96,14 +112,23 @@ export {
|
|
96
112
|
Modal,
|
97
113
|
PieChart,
|
98
114
|
DashboardDonutChart,
|
99
|
-
|
100
115
|
DragAndDrop,
|
101
116
|
AllProjectsDropdown,
|
102
117
|
AppHeader,
|
118
|
+
VariableInput,
|
119
|
+
Paper,
|
103
120
|
Recaptcha,
|
104
121
|
NLPInput,
|
105
122
|
// utils exports
|
106
123
|
checkEmpty,
|
107
124
|
getExtension,
|
108
125
|
getExtensionWithPeriod,
|
126
|
+
findAndInsert,
|
127
|
+
ffid,
|
128
|
+
compareArrays,
|
129
|
+
compareObjects,
|
130
|
+
debounce,
|
131
|
+
throttle,
|
132
|
+
getEncryptedData,
|
133
|
+
truncateText,
|
109
134
|
};
|
package/src/utils/ffID/ffid.ts
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
const ffid = (): string =>
|
1
|
+
export const ffid = (): string =>
|
2
2
|
'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) =>
|
3
3
|
(
|
4
4
|
(char === 'x' ? Math.random() * 16 : ((Math.random() * 16) & 0x3) | 0x8) |
|
5
5
|
0
|
6
6
|
).toString(16)
|
7
7
|
);
|
8
|
-
|
9
|
-
export default ffid;
|
@@ -1,10 +1,10 @@
|
|
1
1
|
// getEncryptData.stories.tsx
|
2
2
|
import { useState } from 'react';
|
3
|
-
import
|
3
|
+
import { getEncryptedData } from './getEncryptedData';
|
4
4
|
|
5
5
|
export default {
|
6
6
|
title: 'Utils/getEncryptedData',
|
7
|
-
component:
|
7
|
+
component: getEncryptedData,
|
8
8
|
};
|
9
9
|
|
10
10
|
export const InteractivePlayground = () => {
|
@@ -13,7 +13,7 @@ export const InteractivePlayground = () => {
|
|
13
13
|
const [encryptedData, setEncryptedData] = useState<string | null>(null);
|
14
14
|
|
15
15
|
const handleEncrypt = () => {
|
16
|
-
const result =
|
16
|
+
const result = getEncryptedData(data, publicKey);
|
17
17
|
setEncryptedData(result.toString());
|
18
18
|
};
|
19
19
|
|
@@ -1,10 +1,8 @@
|
|
1
1
|
import JSEncrypt from 'jsencrypt';
|
2
2
|
|
3
|
-
const getEncryptedData = (data: string, publicKey: string) => {
|
3
|
+
export const getEncryptedData = (data: string, publicKey: string) => {
|
4
4
|
const encrypt = new JSEncrypt();
|
5
5
|
encrypt.setPublicKey(publicKey);
|
6
6
|
const enData = encrypt.encrypt(data);
|
7
7
|
return enData;
|
8
8
|
};
|
9
|
-
|
10
|
-
export default getEncryptedData;
|
File without changes
|
File without changes
|