@visns-studio/visns-components 5.1.5 → 5.1.7

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/package.json CHANGED
@@ -77,7 +77,7 @@
77
77
  "react-dom": "^17.0.0 || ^18.0.0"
78
78
  },
79
79
  "name": "@visns-studio/visns-components",
80
- "version": "5.1.5",
80
+ "version": "5.1.7",
81
81
  "description": "Various packages to assist in the development of our Custom Applications.",
82
82
  "main": "src/index.js",
83
83
  "files": [
@@ -144,6 +144,8 @@ function Field({
144
144
  break;
145
145
  }
146
146
 
147
+ console.info(settings);
148
+
147
149
  switch (settings.size) {
148
150
  case 'full':
149
151
  setFormItemClass(`${styles.formItem} ${styles.fwItem}`);
@@ -157,6 +159,10 @@ function Field({
157
159
  }
158
160
  }, [settings, counter]);
159
161
 
162
+ useEffect(() => {
163
+ console.info(formItemClass);
164
+ }, [formItemClass]);
165
+
160
166
  const fetchChildDropdownData = (s, v) => {
161
167
  let filter = {};
162
168
 
@@ -682,125 +682,6 @@ const GenericDynamic = ({
682
682
  return (
683
683
  <>
684
684
  <div className={styles.formSplit}>
685
- {multiple && shouldDisplayFormList(data, type) && (
686
- <>
687
- <div className={styles.gridtxt__header}>
688
- <span>Form List</span>
689
- </div>
690
- <table className={styles.contentTable}>
691
- <thead>
692
- <tr>
693
- <th>ID</th>
694
- <th>Form</th>
695
- <th>Created By</th>
696
- <th>Created At</th>
697
- <th>Updated By</th>
698
- <th>Updated At</th>
699
- <th></th>
700
- </tr>
701
- </thead>
702
- <tbody>
703
- {typeof data[type]?.map === 'function' &&
704
- data[type].map((item, index) => {
705
- const idAsNumber = parseFloat(item.id);
706
- if (
707
- !isNaN(idAsNumber) &&
708
- Number.isInteger(idAsNumber)
709
- ) {
710
- return (
711
- <tr
712
- key={`form-list-${index}`}
713
- onClick={(e) => {
714
- e.preventDefault();
715
- handleSelectForm(
716
- item,
717
- index
718
- );
719
- }}
720
- >
721
- <td>{item.id}</td>
722
- <td>{item.form_label}</td>
723
- <td>{item.created_by}</td>
724
- <td>
725
- {item.created_at
726
- ? moment(
727
- item.created_at
728
- ).format(
729
- 'DD-MM-YYYY hh:mm A'
730
- )
731
- : null}
732
- </td>
733
- <td>{item.updated_by}</td>
734
- <td>
735
- {item.updated_at
736
- ? moment(
737
- item.updated_at
738
- ).format(
739
- 'DD-MM-YYYY hh:mm A'
740
- )
741
- : null}
742
- </td>
743
- <td>
744
- <div
745
- className={
746
- styles.tdActions
747
- }
748
- >
749
- <span
750
- data-tooltip-id="system-tooltip"
751
- data-tooltip-content="Download Form"
752
- className="tooltip"
753
- onClick={() =>
754
- handleDownloadForm(
755
- item,
756
- label,
757
- originalData
758
- )
759
- }
760
- >
761
- <CloudDownload
762
- strokeWidth={
763
- 2
764
- }
765
- size={18}
766
- className={
767
- styles.tdAction
768
- }
769
- />
770
- </span>
771
- <span
772
- data-tooltip-id="system-tooltip"
773
- data-tooltip-content="Delete Form"
774
- className="tooltip"
775
- onClick={() =>
776
- handleDeleteForm(
777
- item.id,
778
- index
779
- )
780
- }
781
- >
782
- <Backspace
783
- strokeWidth={
784
- 2
785
- }
786
- size={18}
787
- className={
788
- styles.tdAction
789
- }
790
- />
791
- </span>
792
- </div>
793
- </td>
794
- </tr>
795
- );
796
- }
797
- return null;
798
- })}
799
- </tbody>
800
- </table>
801
- </>
802
- )}
803
-
804
685
  {activeForm && (
805
686
  <>
806
687
  <div className={styles.gridtxt__header}>
@@ -810,7 +691,7 @@ const GenericDynamic = ({
810
691
  </span>
811
692
  </div>
812
693
  <div className={styles.formcontainer}>
813
- <div className={styles.modalForm}>
694
+ <form className={styles.modalForm}>
814
695
  {typeof activeForm === 'object' &&
815
696
  !Array.isArray(activeForm)
816
697
  ? Object.entries(activeForm)
@@ -916,10 +797,133 @@ const GenericDynamic = ({
916
797
  />
917
798
  )
918
799
  )}
919
- </div>
800
+ </form>
920
801
  </div>
921
802
  </>
922
803
  )}
804
+
805
+ {multiple && shouldDisplayFormList(data, type) && (
806
+ <>
807
+ <div
808
+ className={styles.gridtxt__header}
809
+ style={{ paddingTop: '20px' }}
810
+ >
811
+ <span>Form List</span>
812
+ </div>
813
+ <table className={styles.contentTable}>
814
+ <thead>
815
+ <tr>
816
+ <th>ID</th>
817
+ <th>Form</th>
818
+ <th>Created By</th>
819
+ <th>Created At</th>
820
+ <th>Updated By</th>
821
+ <th>Updated At</th>
822
+ <th></th>
823
+ </tr>
824
+ </thead>
825
+ <tbody>
826
+ {typeof data[type]?.map === 'function' &&
827
+ data[type].map((item, index) => {
828
+ const idAsNumber = parseFloat(item.id);
829
+ if (
830
+ !isNaN(idAsNumber) &&
831
+ Number.isInteger(idAsNumber)
832
+ ) {
833
+ return (
834
+ <tr
835
+ key={`form-list-${index}`}
836
+ onClick={(e) => {
837
+ e.preventDefault();
838
+ handleSelectForm(
839
+ item,
840
+ index
841
+ );
842
+ }}
843
+ >
844
+ <td>{item.id}</td>
845
+ <td>{item.form_label}</td>
846
+ <td>{item.created_by}</td>
847
+ <td>
848
+ {item.created_at
849
+ ? moment(
850
+ item.created_at
851
+ ).format(
852
+ 'DD-MM-YYYY hh:mm A'
853
+ )
854
+ : null}
855
+ </td>
856
+ <td>{item.updated_by}</td>
857
+ <td>
858
+ {item.updated_at
859
+ ? moment(
860
+ item.updated_at
861
+ ).format(
862
+ 'DD-MM-YYYY hh:mm A'
863
+ )
864
+ : null}
865
+ </td>
866
+ <td>
867
+ <div
868
+ className={
869
+ styles.tdActions
870
+ }
871
+ >
872
+ <span
873
+ data-tooltip-id="system-tooltip"
874
+ data-tooltip-content="Download Form"
875
+ className="tooltip"
876
+ onClick={() =>
877
+ handleDownloadForm(
878
+ item,
879
+ label,
880
+ originalData
881
+ )
882
+ }
883
+ >
884
+ <CloudDownload
885
+ strokeWidth={
886
+ 2
887
+ }
888
+ size={18}
889
+ className={
890
+ styles.tdAction
891
+ }
892
+ />
893
+ </span>
894
+ <span
895
+ data-tooltip-id="system-tooltip"
896
+ data-tooltip-content="Delete Form"
897
+ className="tooltip"
898
+ onClick={() =>
899
+ handleDeleteForm(
900
+ item.id,
901
+ index
902
+ )
903
+ }
904
+ >
905
+ <Backspace
906
+ strokeWidth={
907
+ 2
908
+ }
909
+ size={18}
910
+ className={
911
+ styles.tdAction
912
+ }
913
+ />
914
+ </span>
915
+ </div>
916
+ </td>
917
+ </tr>
918
+ );
919
+ }
920
+ return null;
921
+ })}
922
+ </tbody>
923
+ </table>
924
+ <div style={{ paddingBottom: '56px' }}></div>
925
+ </>
926
+ )}
923
927
  </div>
924
928
  <div className={styles.polActions}>
925
929
  {buttons &&
@@ -1,16 +1,93 @@
1
- import React, { useEffect, useMemo, useState } from 'react';
1
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
2
+ import ReactDOM from 'react-dom';
2
3
  import debounce from 'lodash.debounce';
3
4
  import { toast } from 'react-toastify';
4
- import Popup from 'reactjs-popup';
5
5
  import { confirmAlert } from 'react-confirm-alert';
6
6
  import { TrashCan, ArrowUp, ArrowDown } from 'akar-icons';
7
+ import { CompactPicker } from 'react-color';
7
8
 
8
9
  import CustomFetch from '../Fetch';
9
- import Form from '../Form';
10
10
  import MultiSelect from '../MultiSelect';
11
11
 
12
12
  import 'react-confirm-alert/src/react-confirm-alert.css';
13
13
  import styles from './styles/GenericEditableTable.module.scss';
14
+ import { set } from 'lodash';
15
+
16
+ const ColourPicker = ({ value, columnId, keyCounter, onChange }) => {
17
+ const [colorPickerShow, setColorPickerShow] = useState(false);
18
+ const colorPickerRef = useRef(null);
19
+ const buttonRef = useRef(null); // Reference to the button for positioning
20
+ const popoverRef = useRef(null); // Reference to the popover
21
+
22
+ // Handle clicks outside of the color picker to close it
23
+ useEffect(() => {
24
+ const handleClickOutside = (event) => {
25
+ // Check if click is outside both the button and the popover
26
+ if (
27
+ popoverRef.current &&
28
+ !popoverRef.current.contains(event.target) &&
29
+ buttonRef.current &&
30
+ !buttonRef.current.contains(event.target)
31
+ ) {
32
+ setColorPickerShow(false); // Close the color picker
33
+ }
34
+ };
35
+
36
+ document.addEventListener('mousedown', handleClickOutside);
37
+ return () =>
38
+ document.removeEventListener('mousedown', handleClickOutside);
39
+ }, []);
40
+
41
+ const buttonRect = buttonRef.current
42
+ ? buttonRef.current.getBoundingClientRect()
43
+ : {}; // Get button position
44
+
45
+ return (
46
+ <div className={styles.cpicker}>
47
+ <div className={styles.cpicker__btn} ref={colorPickerRef}>
48
+ <button
49
+ ref={buttonRef} // Attach ref to the button
50
+ style={
51
+ value && value !== 'null' ? { background: value } : {}
52
+ }
53
+ onClick={(e) => {
54
+ e.preventDefault();
55
+ setColorPickerShow(!colorPickerShow); // Toggle visibility
56
+ }}
57
+ >
58
+ &#9744;
59
+ </button>
60
+
61
+ {colorPickerShow &&
62
+ ReactDOM.createPortal(
63
+ <div
64
+ ref={popoverRef} // Attach ref to the popover
65
+ className={styles.colour_popover}
66
+ style={{
67
+ position: 'absolute',
68
+ left: buttonRect.left, // Position it based on button's position
69
+ top: buttonRect.bottom + 5, // Position below the button with a small offset
70
+ zIndex: 9999,
71
+ }}
72
+ >
73
+ <div
74
+ className={styles.colour_cover}
75
+ onClick={() => setColorPickerShow(false)}
76
+ />
77
+ <CompactPicker
78
+ color={value || ''}
79
+ onChangeComplete={(color) => {
80
+ onChange(color.hex, columnId, keyCounter);
81
+ setColorPickerShow(false);
82
+ }}
83
+ />
84
+ </div>,
85
+ document.body // Render the popover inside the body
86
+ )}
87
+ </div>
88
+ </div>
89
+ );
90
+ };
14
91
 
15
92
  const GenericEditableTable = ({
16
93
  schedulingConfig,
@@ -93,6 +170,7 @@ const GenericEditableTable = ({
93
170
  };
94
171
 
95
172
  const handleFieldChange = (value, fieldId, keyCounter) => {
173
+ console.info('handleFieldChange', value, fieldId, keyCounter);
96
174
  setLocalData((prev) => {
97
175
  const updatedDetail = [...prev];
98
176
  updatedDetail[keyCounter] = {
@@ -178,6 +256,15 @@ const GenericEditableTable = ({
178
256
  if (!entry || !column) return null;
179
257
 
180
258
  switch (column.type) {
259
+ case 'colour':
260
+ return (
261
+ <ColourPicker
262
+ value={entry[column.id] || ''}
263
+ columnId={column.id}
264
+ keyCounter={keyCounter}
265
+ onChange={handleFieldChange}
266
+ />
267
+ );
181
268
  case 'currency':
182
269
  case 'number':
183
270
  return (
@@ -405,19 +492,24 @@ const GenericEditableTable = ({
405
492
  .reduce((sum, entry) => {
406
493
  // For each row, sum the values based on column.keys
407
494
  const subtotal =
408
- column.keys.reduce(
409
- (
410
- rowSum,
411
- key
412
- ) =>
413
- rowSum +
414
- (parseFloat(
415
- entry[
416
- key
417
- ]
418
- ) || 0),
419
- 0
420
- );
495
+ Array.isArray(
496
+ column.keys
497
+ ) // Ensure column.keys is an array
498
+ ? column.keys.reduce(
499
+ (
500
+ rowSum,
501
+ key
502
+ ) =>
503
+ rowSum +
504
+ (parseFloat(
505
+ entry[
506
+ key
507
+ ]
508
+ ) ||
509
+ 0),
510
+ 0
511
+ )
512
+ : 0; // Default to 0 if column.keys is not an array
421
513
  return sum + subtotal;
422
514
  }, 0)
423
515
  .toLocaleString('en-AU', {
@@ -197,3 +197,60 @@
197
197
  color: var(--primary-color); /* Set text color to primary color */
198
198
  border-top: 2px solid var(--primary-color); /* Add border top to separate subtotal row */
199
199
  }
200
+
201
+ .colour {
202
+ &__cover {
203
+ position: fixed;
204
+ top: 0;
205
+ right: 0;
206
+ bottom: 0;
207
+ left: 0;
208
+ }
209
+
210
+ &__popover {
211
+ position: absolute;
212
+ z-index: 9999;
213
+ left: 50%; /* Position to the center or adjust accordingly */
214
+ top: 50%; /* Adjust top based on your layout */
215
+ transform: translate(-50%, -50%); /* Center it */
216
+ }
217
+ }
218
+
219
+ .cpicker {
220
+ width: 100%;
221
+ display: flex;
222
+ flex-wrap: nowrap;
223
+ align-items: center;
224
+
225
+ &__btn {
226
+ flex: 1;
227
+
228
+ button {
229
+ width: 40px; /* Keep it compact */
230
+ height: 40px; /* Square button */
231
+ font-size: 1.5rem; /* Adjusted for a better icon size */
232
+ font-weight: 600;
233
+ color: #fff; /* White icon color */
234
+ background-color: #b0b0b0; /* Neutral grey base */
235
+ border: 2px solid #999; /* Light border for definition */
236
+ border-radius: 8px; /* Rounded corners */
237
+ cursor: pointer;
238
+ transition: all 0.3s ease; /* Smooth transition for hover effect */
239
+
240
+ &:hover {
241
+ background-color: #999; /* Darken the background on hover */
242
+ border-color: #666; /* Darken the border on hover */
243
+ }
244
+
245
+ &:focus {
246
+ outline: none; /* Remove default focus outline */
247
+ box-shadow: 0 0 0 2px rgba(72, 168, 58, 0.5); /* Custom focus outline */
248
+ }
249
+
250
+ &:active {
251
+ background-color: #888; /* Lighter grey on click */
252
+ transform: scale(0.98); /* Shrink on click */
253
+ }
254
+ }
255
+ }
256
+ }
@@ -148,6 +148,10 @@ select:not(:placeholder-shown) + .fi__span {
148
148
  color: rgba(var(--paragraph-rgb), 0.65) !important;
149
149
  }
150
150
 
151
+ .qtrItem {
152
+ flex-basis: 25%;
153
+ }
154
+
151
155
  .fwItem {
152
156
  flex-basis: 100%;
153
157
  }