react-core-ts 1.0.24 → 1.0.26

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/docs/store.ts CHANGED
@@ -1,76 +1,67 @@
1
1
  export const treeDropData = [
2
2
  {
3
- name: "Port A - This is the main port in the northern region with significant traffic and operations",
3
+ name: 'Port A - This is the main port in the northern region with significant traffic and operations',
4
4
  id: 22,
5
- children: [
6
- {
7
- name: "Port C - A key sub-port with heavy container traffic in the southern bay area",
8
- id: 1,
9
- children: [
10
- {
11
- name: "Port G - A small port used for local shipments and fishing operations",
12
- id: 2,
13
- children: [
14
- {
15
- name: "Port H - A minor local port focusing on fisheries and small-scale cargo",
16
- id: 3,
17
- children: [],
18
- },
19
- ],
20
- },
21
- {
22
- name: "Port I - A coastal port for regional trade and tourism operations",
23
- id: 4,
24
- children: [
25
- {
26
- name: "Port J - A harbor for tourist cruises and commercial boats",
27
- id: 5,
28
- children: [],
29
- },
30
- ],
31
- },
32
- ],
33
- },
34
- {
35
- name: "Port D - An industrial port dealing with large-scale cargo and ship repair services",
36
- id: 6,
37
- children: [
38
- {
39
- name: "Port K - A dock specializing in shipbuilding and marine repairs",
40
- id: 7,
41
- children: [
42
- {
43
- name: "Port L - A repair facility for both military and commercial vessels",
44
- id: 8,
45
- children: [],
46
- },
47
- ],
48
- },
49
- ],
50
- },
51
- ],
5
+ parentId: null,
52
6
  },
53
7
  {
54
- name: "Port B - This is the largest port in the central region, supporting international trade",
8
+ name: 'Port C - A key sub-port with heavy container traffic in the southern bay area',
9
+ id: 1,
10
+ parentId: 22,
11
+ },
12
+ {
13
+ name: 'Port G - A small port used for local shipments and fishing operations',
14
+ id: 2,
15
+ parentId: 1,
16
+ },
17
+ {
18
+ name: 'Port H - A minor local port focusing on fisheries and small-scale cargo',
19
+ id: 3,
20
+ parentId: 2,
21
+ },
22
+ {
23
+ name: 'Port I - A coastal port for regional trade and tourism operations',
24
+ id: 4,
25
+ parentId: 1,
26
+ },
27
+ {
28
+ name: 'Port J - A harbor for tourist cruises and commercial boats',
29
+ id: 5,
30
+ parentId: 4,
31
+ },
32
+ {
33
+ name: 'Port D - An industrial port dealing with large-scale cargo and ship repair services',
34
+ id: 6,
35
+ parentId: 22,
36
+ },
37
+ {
38
+ name: 'Port K - A dock specializing in shipbuilding and marine repairs',
39
+ id: 7,
40
+ parentId: 6,
41
+ },
42
+ {
43
+ name: 'Port L - A repair facility for both military and commercial vessels',
44
+ id: 8,
45
+ parentId: 7,
46
+ },
47
+ {
48
+ name: 'Port B - This is the largest port in the central region, supporting international trade',
55
49
  id: 9,
56
- children: [
57
- {
58
- name: "Port E - A primary export port dealing with raw materials and heavy goods",
59
- id: 10,
60
- children: [
61
- {
62
- name: "Port M - A small export hub for agricultural and mining products",
63
- id: 11,
64
- children: [
65
- {
66
- name: "Port N - A warehouse and storage center for exported goods",
67
- id: 12,
68
- children: [],
69
- },
70
- ],
71
- },
72
- ],
73
- },
74
- ],
50
+ parentId: null,
51
+ },
52
+ {
53
+ name: 'Port E - A primary export port dealing with raw materials and heavy goods',
54
+ id: 10,
55
+ parentId: 9,
56
+ },
57
+ {
58
+ name: 'Port M - A small export hub for agricultural and mining products',
59
+ id: 11,
60
+ parentId: 10,
61
+ },
62
+ {
63
+ name: 'Port N - A warehouse and storage center for exported goods',
64
+ id: 12,
65
+ parentId: 11,
75
66
  },
76
67
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-core-ts",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
4
4
  "description": "React Components",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,7 +8,7 @@
8
8
  "start": "webpack serve --open --mode development",
9
9
  "build": "tsc && cp -R src/styles dist/styles",
10
10
  "test": "jest",
11
- "prepublishOnly": "npm run build"
11
+ "prepublishOnly": "npm run build && rm -rf src"
12
12
  },
13
13
  "repository": {
14
14
  "type": "git",
@@ -1,6 +1,7 @@
1
1
  /** @type {import('tailwindcss').Config} */
2
2
  module.exports = {
3
3
  content: [],
4
+ prefix: 'qbs-',
4
5
  purge: ['./src/**/*.{js,jsx,ts,tsx}', './docs/**/*.{html,js}'],
5
6
  theme: {
6
7
  extend: {
@@ -1,489 +0,0 @@
1
- import Tooltip from './utilities/tooltip';
2
- import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
3
-
4
- import { AutoSuggestionInputProps } from './commontypes';
5
- import { useSuggestions } from './utilities/autosuggestions';
6
- import { debounce } from './utilities/debounce';
7
- import { deepEqual } from './utilities/deepEqual';
8
- import { filterSuggestions } from './utilities/filterSuggestions';
9
- import { Close, DropArrow, Search, Spinner } from './utilities/icons';
10
-
11
- type ValueProps = {
12
- [key: string]: string;
13
- };
14
- const AutoComplete = forwardRef<HTMLInputElement, AutoSuggestionInputProps>(
15
- (
16
- {
17
- label,
18
- onChange,
19
- getData = async () => [],
20
- data = [],
21
- errors,
22
- required = false,
23
- name,
24
- fullWidth = false,
25
- placeholder,
26
- id,
27
- type = 'custom_select',
28
- selectedItems: propsSeelctedItems = [],
29
- readOnly = false,
30
- disabled = false,
31
- value,
32
- isMultiple = false,
33
- desc = 'name',
34
- descId = 'id',
35
- singleSelect,
36
- className,
37
- async = false,
38
- paginationEnabled,
39
- initialLoad,
40
- actionLabel,
41
- handleAction,
42
- nextBlock,
43
- notDataMessage,
44
- onFocus,
45
- },
46
- ref
47
- ) => {
48
- const dropdownRef = useRef<HTMLDivElement>(null);
49
- // State Hooks Section
50
- const [isInitialRender, setIsInitialRender] = useState(true);
51
-
52
- const [inputValue, setInputValue] = useState<string>(value);
53
- const [searchValue, setSearchValue] = useState<string>('');
54
- const [nextPage, setNextPage] = useState<number | undefined>(1);
55
- const [dropOpen, setDropOpen] = useState<boolean>(false);
56
- const [selectedItems, setSelectedItems] = useState<ValueProps[]>([]);
57
- // API call for suggestions through a custom hook
58
- const [dropdownPosition, setDropdownPosition] = useState('bottom');
59
- const inputRef = useRef(null);
60
- const dropRef = useRef(null);
61
- useImperativeHandle(ref, () => inputRef.current);
62
-
63
- const adjustDropdownPosition = () => {
64
- if (inputRef.current && dropRef.current) {
65
- const inputBoxRect = inputRef.current.getBoundingClientRect();
66
- const viewportHeight = window.innerHeight;
67
-
68
- const spaceAbove = inputBoxRect.top;
69
- const spaceBelow = viewportHeight - inputBoxRect.bottom;
70
- if (spaceAbove > spaceBelow) {
71
- setDropdownPosition('top');
72
- } else {
73
- setDropdownPosition('bottom');
74
- }
75
- }
76
- };
77
-
78
- useEffect(() => {
79
- window.addEventListener('resize', adjustDropdownPosition);
80
- adjustDropdownPosition();
81
-
82
- return () => {
83
- window.removeEventListener('resize', adjustDropdownPosition);
84
- };
85
- }, [dropOpen]);
86
-
87
- const { suggestions, isLoading, handlePickSuggestions } = useSuggestions(
88
- getData,
89
- data,
90
- dropOpen,
91
- async,
92
- paginationEnabled,
93
- initialLoad,
94
- inputValue,
95
- isMultiple,
96
- setNextPage,
97
- selectedItems
98
- // nextBlock
99
- );
100
-
101
- // Handling the selection of a suggestion
102
- const handleSuggestionClick = useCallback((suggestion: ValueProps) => {
103
- if (isMultiple) {
104
- setSelectedItems((prev) => [...prev, suggestion]);
105
- } else {
106
- setInputValue(suggestion[desc]);
107
- }
108
- setSearchValue('');
109
- setInputValue(suggestion[desc]);
110
- onChange(suggestion);
111
- setDropOpen(false);
112
- }, []);
113
-
114
- // Adding debounce to avoid making API calls on every keystroke
115
- const handleChangeWithDebounce = debounce((value) => {
116
- if ((type === 'auto_complete' || type === 'auto_suggestion') && async) {
117
- handlePickSuggestions(value, 1);
118
- }
119
- }, 1000);
120
-
121
- const handleMultiSelect = (e: any, suggestion: ValueProps) => {
122
- const { checked } = e.target;
123
- if (isMultiple) {
124
- if (checked) {
125
- setSelectedItems((prev) => [...prev, suggestion]);
126
- } else {
127
- setSelectedItems((prev) => {
128
- return prev.filter(
129
- (item, i) => item[descId] !== suggestion[descId]
130
- );
131
- });
132
- }
133
- } else {
134
- if (checked) {
135
- setInputValue(suggestion[desc]);
136
- onChange(suggestion);
137
- } else {
138
- setInputValue('');
139
- }
140
- }
141
- };
142
- useEffect(() => {
143
- if (!deepEqual(selectedItems, propsSeelctedItems))
144
- setSelectedItems(propsSeelctedItems);
145
- }, [propsSeelctedItems]);
146
- // Effect to set the input value whenever `value` prop changes
147
- useEffect(() => {
148
- setInputValue(value ?? '');
149
- }, [value]);
150
-
151
- const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
152
- const { value } = e.target;
153
- setDropOpen(true);
154
- setSearchValue(value);
155
- handleChangeWithDebounce(value);
156
- if (!value) {
157
- setInputValue('');
158
- onChange({ [descId]: '', [desc]: '' });
159
- }
160
- };
161
- const handleSuggestionChange = (e: React.ChangeEvent<HTMLInputElement>) => {
162
- const { value } = e.target;
163
- setDropOpen(true);
164
- setSearchValue(value);
165
- handleChangeWithDebounce(value);
166
- };
167
-
168
- const handleBlur = () => {
169
- setTimeout(() => {
170
- setDropOpen(false);
171
- }, 200);
172
- };
173
-
174
- const handleClear = () => {
175
- if (searchValue) {
176
- setSearchValue('');
177
- setDropOpen(false);
178
- } else {
179
- setInputValue('');
180
- onChange({ [descId]: '', [desc]: '' });
181
- setDropOpen(false);
182
- }
183
- };
184
-
185
- const generateClassName = useCallback(() => {
186
- return `qbs-textfield-default ${className} ${
187
- errors && errors?.message ? 'textfield-error' : 'textfield'
188
- }`;
189
- }, [errors, name]);
190
- const handleRemoveSelectedItem = (index: number) => {
191
- setSelectedItems((prev) => {
192
- return prev.filter((_, i) => i !== index);
193
- });
194
- };
195
-
196
- useEffect(() => {
197
- if (isInitialRender) {
198
- setIsInitialRender(false);
199
- } else {
200
- onChange(selectedItems);
201
- }
202
- }, [selectedItems]);
203
-
204
- useEffect(() => {
205
- const handleClickOutside = (event: React.MouseEvent) => {
206
- if (
207
- dropdownRef.current &&
208
- event.target instanceof Node &&
209
- !dropdownRef.current.contains(event.target)
210
- ) {
211
- setDropOpen(false);
212
- setSearchValue('');
213
- }
214
- };
215
- document.addEventListener('mousedown', handleClickOutside as any);
216
- return () => {
217
- document.removeEventListener('mousedown', handleClickOutside as any);
218
- };
219
- }, []);
220
-
221
- // Filtering suggestions based on type and search value
222
- const selected: any = isMultiple ? selectedItems : inputValue;
223
- const filteredData: ValueProps[] = filterSuggestions(
224
- suggestions,
225
- searchValue,
226
- type,
227
- desc,
228
- selected,
229
- async
230
- );
231
- const isSelected = (
232
- item: ValueProps,
233
- selectedItems: ValueProps[] | string
234
- ): boolean => {
235
- if (Array.isArray(selectedItems)) {
236
- return selectedItems.some(
237
- (selectedItem) => selectedItem[desc] === item[desc]
238
- );
239
- } else {
240
- return item[desc] === selectedItems;
241
- }
242
- };
243
-
244
- const handleLoadMore = () => {
245
- if (paginationEnabled) {
246
- handlePickSuggestions(searchValue, nextPage + 1, true);
247
- setNextPage(nextPage + 1);
248
- }
249
- };
250
-
251
- const handleOnClick = () => {
252
- !disabled && !readOnly ? setDropOpen(true) : '';
253
- };
254
- const tooltipContent =
255
- selectedItems?.length > 1
256
- ? selectedItems
257
- ?.slice(1)
258
- .map((item) => item[desc])
259
- .join(', ')
260
- : '';
261
-
262
- return (
263
- <div className={fullWidth ? 'fullWidth' : 'autoWidth'} ref={dropdownRef}>
264
- {label && (
265
- <div
266
- style={{
267
- marginBottom: 5,
268
- display: 'flex',
269
- justifyContent: 'space-between',
270
- }}
271
- >
272
- <label className={`labels label-text`}>
273
- {label}
274
- {required && <span className="text-error"> *</span>}
275
- </label>
276
- <span onClick={() => handleAction?.()} className={`action_label`}>
277
- {actionLabel}
278
- </span>
279
- </div>
280
- )}
281
- {/* Displaying selected items for multi-select */}
282
-
283
- <div style={{ position: 'relative' }}>
284
- <div className="selected-items-container ">
285
- {selectedItems?.length > 0 && (
286
- <>
287
- <div key={selectedItems[0].id} className="selected-item">
288
- <Tooltip title={selectedItems[0]?.[desc]}>
289
- {selectedItems[0]?.[desc]?.length > 8
290
- ? `${selectedItems[0]?.[desc].substring(0, 8)}...`
291
- : selectedItems[0]?.[desc]}
292
- </Tooltip>
293
- <button
294
- onClick={() => handleRemoveSelectedItem(0)}
295
- className="remove-item-btn"
296
- aria-label={`Remove ${selectedItems[0]?.[desc]}`}
297
- >
298
- X
299
- </button>
300
- </div>
301
- <Tooltip title={tooltipContent}>
302
- {selectedItems?.length > 1 && (
303
- <div className="selected-item-more">
304
- +{selectedItems?.length - 1} more
305
- </div>
306
- )}
307
- </Tooltip>
308
- </>
309
- )}
310
- </div>
311
- <input
312
- id={id}
313
- ref={inputRef}
314
- type="text"
315
- value={
316
- type === 'auto_suggestion'
317
- ? inputValue
318
- : searchValue || inputValue
319
- }
320
- onChange={handleChange}
321
- // onBlur={handleBlur}
322
- onFocus={onFocus}
323
- onClick={() => handleOnClick()}
324
- className={generateClassName()}
325
- placeholder={selectedItems?.length > 0 ? '' : placeholder ?? ''}
326
- readOnly={
327
- readOnly || type === 'custom_select' || type == 'auto_suggestion'
328
- }
329
- disabled={disabled}
330
- data-testid="custom-autocomplete"
331
- />
332
-
333
- {/* Icons for Clearing Input or Toggling Dropdown */}
334
- <div className="qbs-autocomplete-close-icon">
335
- {(inputValue || searchValue) && !disabled && !readOnly && (
336
- <button
337
- onClick={handleClear}
338
- className="icon-button"
339
- aria-label="clear"
340
- >
341
- <Close />
342
- </button>
343
- )}
344
-
345
- <button
346
- disabled={disabled || readOnly}
347
- type="button"
348
- onClick={() => setDropOpen(!dropOpen)}
349
- className="icon-button"
350
- aria-label="toggle"
351
- >
352
- <DropArrow />
353
- </button>
354
- </div>
355
-
356
- {/* Displaying Loading Spinner */}
357
-
358
- {/* Suggestions Dropdown */}
359
- {dropOpen && (
360
- <ul
361
- ref={dropRef}
362
- className={`qbs-autocomplete-suggestions ${dropdownPosition}`}
363
- >
364
- {type == 'auto_suggestion' && (
365
- <div
366
- style={{ position: 'relative' }}
367
- className="react-core-ts-search-container"
368
- >
369
- <span className="dropdown-search-icon">
370
- <Search />
371
- </span>
372
- <input
373
- className="dropdown-search-input"
374
- onChange={handleSuggestionChange}
375
- value={searchValue}
376
- placeholder="Search"
377
- />
378
- </div>
379
- )}
380
-
381
- <div className="qbs-autocomplete-suggestions-sub">
382
- {/* Displaying Suggestions or Not Found Message */}
383
- {/* {isLoading && (
384
- <div style={{ display: 'flex', justifyContent: 'center' }}>
385
- <span>
386
- <Spinner />
387
- </span>
388
- </div>
389
-
390
- {/* )} */}
391
-
392
- {filteredData?.length > 0 ? (
393
- filteredData.map((suggestion: ValueProps, idx: number) => (
394
- <div
395
- key={idx.toString()}
396
- className={`qbs-autocomplete-listitem-container ${
397
- (isMultiple || singleSelect) &&
398
- 'qbs-autocomplete-checkbox-container'
399
- } ${
400
- isSelected(suggestion, selected) ? 'is-selected' : ''
401
- }`}
402
- >
403
- {(isMultiple || singleSelect) && (
404
- <div className="qbs-autocomplete-checkbox">
405
- <input
406
- onChange={(e) => handleMultiSelect(e, suggestion)}
407
- type="checkbox"
408
- checked={isSelected(suggestion, selected)}
409
- id={`qbs-checkbox-${idx.toString()}`}
410
- />
411
- <label htmlFor={`qbs-checkbox-${idx.toString()}`}>
412
- <svg
413
- width="8"
414
- height="6"
415
- viewBox="0 0 8 6"
416
- fill="none"
417
- xmlns="http://www.w3.org/2000/svg"
418
- >
419
- <path
420
- d="M0 3.21739L2.89883 6L8 1.06994L6.89494 0L2.89883 3.86768L1.09728 2.14745L0 3.21739Z"
421
- fill="white"
422
- />
423
- </svg>
424
- </label>
425
- </div>
426
- )}
427
- <li
428
- key={idx}
429
- className={`qbs-autocomplete-suggestions-item ${
430
- isSelected(suggestion, selected) ? 'is-selected' : ''
431
- }`}
432
- onClick={() => handleSuggestionClick(suggestion)}
433
- data-testid={suggestion[desc]}
434
- >
435
- {suggestion[desc]}
436
- </li>
437
- </div>
438
- ))
439
- ) : (
440
- <>
441
- {isLoading ? (
442
- <div
443
- style={{ display: 'flex', justifyContent: 'center' }}
444
- >
445
- <span>
446
- <Spinner />
447
- </span>
448
- </div>
449
- ) : (
450
- <li
451
- className="qbs-autocomplete-notfound"
452
- onClick={handleBlur}
453
- >
454
- {notDataMessage ?? 'No Results Found'}
455
- </li>
456
- )}
457
- </>
458
- )}
459
- {paginationEnabled &&
460
- nextBlock !== 0 &&
461
- nextBlock !== undefined &&
462
- filteredData?.length > 0 && (
463
- <div
464
- className="loadMoreSection"
465
- onClick={() => handleLoadMore()}
466
- >
467
- <p style={{ margin: 2 }}>Load More</p>
468
- </div>
469
- )}
470
- </div>
471
- </ul>
472
- )}
473
- </div>
474
-
475
- {/* Displaying Validation Error */}
476
- {errors && (
477
- <div
478
- className="text-error text-error-label mt-[1px]"
479
- data-testid="autocomplete-error"
480
- >
481
- {errors.message}
482
- </div>
483
- )}
484
- </div>
485
- );
486
- }
487
- );
488
-
489
- export default React.memo(AutoComplete);