@visns-studio/visns-components 4.6.11 → 4.6.13

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.
@@ -756,20 +756,25 @@ const DataGrid = forwardRef(
756
756
  };
757
757
 
758
758
  const sortTrigger = () => {
759
- const url = form.url.replace('/ajax', '');
759
+ let baseUrl = form.url.replace('/ajax', '');
760
+ let sortUrl = paramValue
761
+ ? `${baseUrl}/${paramValue}/sort`
762
+ : `${baseUrl}/sort`;
763
+
764
+ if (form.sort?.url) {
765
+ sortUrl = form.sort.url;
766
+ }
760
767
 
761
768
  const newState = {
762
- form: form,
769
+ form,
763
770
  page: {
764
771
  data: pageData,
765
772
  setting: pageSetting,
766
773
  },
767
- paramValue: paramValue,
774
+ paramValue,
768
775
  };
769
776
 
770
- navigate(`${url}/${paramValue}/sort`, {
771
- state: newState,
772
- });
777
+ navigate(sortUrl, { state: newState });
773
778
  };
774
779
 
775
780
  const renderCreateButton = () => {
@@ -545,7 +545,6 @@ function Form({
545
545
  e.preventDefault();
546
546
 
547
547
  const { type } = e.target.dataset;
548
-
549
548
  let fields = formSettings.fields;
550
549
  let _inputClass = inputClass;
551
550
  let validation = '';
@@ -556,86 +555,39 @@ function Form({
556
555
  return;
557
556
  }
558
557
 
559
- if (item.required === true) {
560
- if (
561
- formData[item.id] === '' ||
562
- formData[item.id] === undefined
563
- ) {
558
+ if (item.required) {
559
+ if (!formData[item.id]) {
564
560
  if (
565
561
  item.hasOwnProperty('required_check') &&
566
- item.required_check !== ''
562
+ item.required_check &&
563
+ !formData[item.required_check]
567
564
  ) {
568
- if (formData[item.required_check] === '') {
569
- if (
570
- !validation.includes(
571
- item.label +
572
- ' is a required field.<br/>'
573
- )
574
- ) {
575
- validation +=
576
- item.label +
577
- ' is a required field.<br/>';
578
- }
579
- _inputClass[item.id] =
580
- styles.inputError;
581
- }
565
+ validation += `${item.label} is a required field.<br/>`;
566
+ _inputClass[item.id] = styles.inputError;
582
567
  } else {
583
- if (
584
- !validation.includes(
585
- item.label +
586
- ' is a required field.<br/>'
587
- )
588
- ) {
589
- validation +=
590
- item.label +
591
- ' is a required field.<br/>';
592
- }
568
+ validation += `${item.label} is a required field.<br/>`;
593
569
  _inputClass[item.id] = styles.inputError;
594
570
  }
595
571
  } else {
596
- if (
597
- item.hasOwnProperty('required_check') &&
598
- item.required_check !== ''
599
- ) {
600
- _inputClass[item.required_check] = '';
601
- }
602
572
  _inputClass[item.id] = '';
603
573
  }
574
+ } else if (
575
+ item.hasOwnProperty('required_rely') &&
576
+ item.required_rely &&
577
+ formData[item.required_rely] &&
578
+ !formData[item.id]
579
+ ) {
580
+ validation += `${item.label} is a required field.<br/>`;
581
+ _inputClass[item.id] = styles.inputError;
604
582
  } else {
605
- if (
606
- item.hasOwnProperty('required_rely') &&
607
- item.required_rely !== ''
608
- ) {
609
- if (formData[item.required_rely] === '') {
610
- _inputClass[item.id] = '';
611
- } else {
612
- if (formData[item.id] === '') {
613
- if (
614
- !validation.includes(
615
- item.label +
616
- ' is a required field.<br/>'
617
- )
618
- ) {
619
- validation +=
620
- item.label +
621
- ' is a required field.<br/>';
622
- }
623
- _inputClass[item.id] =
624
- styles.inputError;
625
- } else {
626
- _inputClass[item.id] = '';
627
- }
628
- }
629
- } else {
630
- _inputClass[item.id] = '';
631
- }
583
+ _inputClass[item.id] = '';
632
584
  }
633
585
  });
634
586
 
635
587
  setInputClass(_inputClass);
636
588
  }
637
589
 
638
- if (validation !== '') {
590
+ if (validation) {
639
591
  toast.error(<div>{parse(validation)}</div>);
640
592
  } else {
641
593
  let url = formSettings.url;
@@ -652,32 +604,26 @@ function Form({
652
604
  } else {
653
605
  switch (formType) {
654
606
  case 'create':
655
- if (
656
- formSettings.hasOwnProperty('json') &&
657
- formSettings.json === true
658
- ) {
607
+ if (formSettings.json) {
659
608
  url += '/jsonStore';
660
609
  }
661
610
  break;
662
611
  case 'update':
663
612
  case 'delete':
664
- if (
665
- formSettings.hasOwnProperty('json') &&
666
- formSettings.json === true
667
- ) {
668
- if (formType === 'update') {
669
- url += `/jsonUpdate`;
670
- } else {
671
- url += `/jsonDelete/${
672
- formData[formSettings.primaryKey]
673
- }`;
674
- }
613
+ if (formSettings.json) {
614
+ url +=
615
+ formType === 'update'
616
+ ? '/jsonUpdate'
617
+ : `/jsonDelete/${
618
+ formData[
619
+ formSettings.primaryKey
620
+ ]
621
+ }`;
675
622
  } else {
676
623
  url += `/${
677
624
  formData[formSettings.primaryKey]
678
625
  }`;
679
626
  }
680
-
681
627
  method =
682
628
  formType === 'update' ? 'PUT' : 'DELETE';
683
629
  break;
@@ -686,46 +632,37 @@ function Form({
686
632
 
687
633
  if (!_.isEmpty(formData)) {
688
634
  Object.keys(formData).forEach((item) => {
689
- if (
690
- formData[item] !== undefined &&
691
- (item === 'file' ||
692
- (formData[item] instanceof File &&
693
- formData[item] !== null))
694
- ) {
635
+ if (formData[item] instanceof File) {
695
636
  fileUpload = true;
696
637
  fileKey = item;
697
638
  }
698
639
  });
699
640
  }
700
641
 
701
- if (fileUpload === true && formData[fileKey] !== null) {
642
+ if (fileUpload && formData[fileKey]) {
702
643
  if (Array.isArray(formData[fileKey])) {
703
- const files = formData[fileKey];
704
-
705
- const uploadPromises = files.map(async (file) => {
706
- const fileRes = await Vapor.store(file, {
707
- progress: (progress) => {
708
- setUploadProgress(progress * 100);
709
- },
710
- });
711
-
712
- return {
713
- uuid: fileRes.uuid,
714
- key: fileRes.key,
715
- bucket: fileRes.bucket,
716
- filename: file.name,
717
- filesize: file.size,
718
- extension: fileRes.extension,
719
- file_relationship: fileKey,
720
- };
721
- });
722
-
723
- const uploadedFiles = await Promise.all(
644
+ const uploadPromises = formData[fileKey].map(
645
+ async (file) => {
646
+ const fileRes = await Vapor.store(file, {
647
+ progress: (progress) => {
648
+ setUploadProgress(progress * 100);
649
+ },
650
+ });
651
+ return {
652
+ uuid: fileRes.uuid,
653
+ key: fileRes.key,
654
+ bucket: fileRes.bucket,
655
+ filename: file.name,
656
+ filesize: file.size,
657
+ extension: fileRes.extension,
658
+ file_relationship: fileKey,
659
+ };
660
+ }
661
+ );
662
+ formData['uploadedFiles'] = await Promise.all(
724
663
  uploadPromises
725
664
  );
726
-
727
- formData['uploadedFiles'] = uploadedFiles;
728
- } else if (formData[fileKey] instanceof File) {
665
+ } else {
729
666
  const fileRes = await Vapor.store(
730
667
  formData[fileKey],
731
668
  {
@@ -734,7 +671,6 @@ function Form({
734
671
  },
735
672
  }
736
673
  );
737
-
738
674
  formData['uuid'] = fileRes.uuid;
739
675
  formData['key'] = fileRes.key;
740
676
  formData['bucket'] = fileRes.bucket;
@@ -745,60 +681,40 @@ function Form({
745
681
  }
746
682
  }
747
683
 
748
- const fieldMap = formSettings.fields.reduce(
749
- (acc, field) => {
750
- acc[field.id] = field;
751
- return acc;
752
- },
753
- {}
754
- );
755
-
756
- let payload = { ...formData };
757
- Object.keys(formData).forEach((key) => {
758
- const value = formData[key];
759
- const field = fieldMap[key];
760
-
761
- if (field && field.type === 'multi-dropdown-ajax') {
762
- if (
763
- typeof value === 'object' &&
764
- value !== null &&
765
- 'value' in value &&
766
- 'label' in value
767
- ) {
768
- payload[key] = value.value;
769
- }
684
+ // Transform keys with dots into nested objects
685
+ const payload = Object.keys(formData).reduce((acc, key) => {
686
+ if (key.includes('.')) {
687
+ const [root, sub] = key.split('.');
688
+ if (!acc[root]) acc[root] = {};
689
+ acc[root][sub] = formData[key];
690
+ } else {
691
+ acc[key] = formData[key];
770
692
  }
771
- });
693
+ return acc;
694
+ }, {});
695
+
696
+ console.info(payload);
772
697
 
773
698
  const res = await CustomFetch(url, method, payload);
774
699
 
775
700
  if (res.data.error === '') {
776
- if (fetchTable) {
777
- fetchTable();
778
- }
779
-
701
+ if (fetchTable) fetchTable();
780
702
  if (closeModal) {
781
703
  closeModal();
782
-
783
704
  if (type === 'saveAnother') {
784
705
  fetchData();
785
706
  modalOpen('create', 0);
786
707
  }
787
708
  }
788
-
789
709
  toast.success(
790
- formSettings?.save?.toast?.success
791
- ? formSettings.save.toast.success
792
- : 'Entry successfully saved into the system.'
710
+ formSettings?.save?.toast?.success ||
711
+ 'Entry successfully saved into the system.'
793
712
  );
794
713
 
795
- if (
796
- formSettings.hasOwnProperty('redirect') &&
797
- formSettings.redirect &&
798
- res.data.data &&
799
- res.data.data.id
800
- ) {
801
- navigate(formSettings.redirect + res.data.data.id);
714
+ if (formSettings.redirect && res.data.data?.id) {
715
+ navigate(
716
+ `${formSettings.redirect}${res.data.data.id}`
717
+ );
802
718
  }
803
719
 
804
720
  if (res.data.redirect) {
@@ -806,30 +722,26 @@ function Form({
806
722
  }
807
723
 
808
724
  if (
809
- formSettings?.save?.return?.type &&
725
+ formSettings?.save?.return?.type === 'table' &&
810
726
  formSettings?.save?.return?.param
811
727
  ) {
812
- if (formSettings.save.return.type === 'table') {
813
- if (
814
- result &&
815
- result[formSettings.save.return.param]
816
- .length > 0
817
- ) {
818
- const td = parseTableData(
819
- result[formSettings.save.return.param]
820
- );
821
-
822
- setTableData((prevState) => ({
823
- ...prevState,
824
- ...td,
825
- }));
826
- } else {
827
- setTableData((prevState) => ({
828
- ...prevState,
829
- columns: [],
830
- dataSource: [],
831
- }));
832
- }
728
+ if (
729
+ res.data[formSettings.save.return.param]
730
+ ?.length > 0
731
+ ) {
732
+ const td = parseTableData(
733
+ res.data[formSettings.save.return.param]
734
+ );
735
+ setTableData((prevState) => ({
736
+ ...prevState,
737
+ ...td,
738
+ }));
739
+ } else {
740
+ setTableData((prevState) => ({
741
+ ...prevState,
742
+ columns: [],
743
+ dataSource: [],
744
+ }));
833
745
  }
834
746
  }
835
747
  }
@@ -1,12 +1,22 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import { Link, useLocation } from 'react-router-dom';
2
+ import { useLocation, useParams } from 'react-router-dom';
3
3
  import { arrayMoveImmutable } from 'array-move';
4
4
  import { toast } from 'react-toastify';
5
- import { CircleChevronRight } from 'akar-icons';
6
5
  import CustomFetch from '../../crm/Fetch';
7
- import SortableList from '../../cms/sorting/List';
6
+ import SortableList from '../sorting/List';
7
+ import Breadcrumb from '../Breadcrumb';
8
8
  import styles from './styles/GenericSort.module.css';
9
9
 
10
+ // Utility function to convert camelCase to space-separated words with uppercase first letters
11
+ function camelCaseToUcFirst(str) {
12
+ return str
13
+ .replace(/([A-Z])/g, ' $1') // Add a space before each uppercase letter
14
+ .replace(/^./, function (str) {
15
+ return str.toUpperCase();
16
+ }) // Capitalize the first letter
17
+ .trim(); // Remove leading and trailing whitespace
18
+ }
19
+
10
20
  const ChildSort = ({ item, url }) => {
11
21
  const [data, setData] = useState(item.child);
12
22
 
@@ -17,13 +27,14 @@ const ChildSort = ({ item, url }) => {
17
27
  useEffect(() => {
18
28
  if (data.length > 0) {
19
29
  CustomFetch(
20
- url + '/sort_update',
30
+ `${url}/sort_update`,
21
31
  'POST',
22
32
  {
23
33
  list: data,
24
34
  },
25
35
  function (result) {
26
36
  if (result.error === '') {
37
+ toast.success('Sort updated successfully');
27
38
  } else {
28
39
  toast.error(String(result.error));
29
40
  }
@@ -47,15 +58,18 @@ const ChildSort = ({ item, url }) => {
47
58
  );
48
59
  };
49
60
 
50
- const GenericSort = ({ category = false }) => {
61
+ const GenericSort = ({ category = false, layout = 'full' }) => {
51
62
  const location = useLocation();
63
+ const routeParams = useParams();
52
64
  const {
53
65
  form,
54
- page: { data, setting },
66
+ page = {}, // Provide a default empty object for page
55
67
  paramValue,
56
- } = location.state;
68
+ } = location.state || {}; // Safely access location.state
69
+
57
70
  const [sortData, setSortData] = useState([]);
58
- const url = form.url;
71
+ const url = form?.url;
72
+ const data = page?.data || {}; // Safely access data from page, default to an empty object
59
73
 
60
74
  const onSortEnd = ({ oldIndex, newIndex }) => {
61
75
  setSortData((prevItem) =>
@@ -68,7 +82,7 @@ const GenericSort = ({ category = false }) => {
68
82
  let fetchUrl = url;
69
83
  let payload = {};
70
84
 
71
- if (form.json) {
85
+ if (form?.json) {
72
86
  fetchUrl = fetchUrl + '/jsonSortList';
73
87
  payload.id = paramValue;
74
88
  } else {
@@ -76,7 +90,6 @@ const GenericSort = ({ category = false }) => {
76
90
  }
77
91
 
78
92
  const res = await CustomFetch(fetchUrl, 'POST', payload);
79
-
80
93
  setSortData(res.data);
81
94
  } catch (err) {
82
95
  console.log(err);
@@ -90,7 +103,7 @@ const GenericSort = ({ category = false }) => {
90
103
  list: sortData,
91
104
  };
92
105
 
93
- if (form.json) {
106
+ if (form?.json) {
94
107
  fetchUrl = fetchUrl + '/jsonSortUpdate';
95
108
  payload.id = paramValue;
96
109
  } else {
@@ -123,37 +136,23 @@ const GenericSort = ({ category = false }) => {
123
136
  <>
124
137
  <div className={styles.grid}>
125
138
  <div className={styles.grid__row}>
126
- <div className={`${styles.grid__full} ${styles.crmtitle}`}>
127
- <h1>
128
- <Link
129
- to={
130
- setting && setting.previousUrl
131
- ? setting.previousUrl
132
- : null
133
- }
134
- >
135
- {setting && setting.title
136
- ? setting.title
137
- : null}
138
- </Link>{' '}
139
- {data && data[setting.titleKey] ? (
140
- <Link
141
- to={
142
- setting && setting.previousUrl
143
- ? `${setting.previousUrl}/${paramValue}`
144
- : null
145
- }
146
- >
147
- <CircleChevronRight
148
- strokeWidth={2}
149
- size={18}
150
- />{' '}
151
- {data[setting.titleKey]}{' '}
152
- </Link>
153
- ) : null}
154
- <CircleChevronRight strokeWidth={2} size={18} />{' '}
155
- {form?.sort?.title}
156
- </h1>
139
+ <div
140
+ className={`${styles.grid__full} ${
141
+ layout === 'full'
142
+ ? styles.crmtitle
143
+ : styles.crmtitleSimple
144
+ }`}
145
+ >
146
+ <Breadcrumb
147
+ data={{
148
+ label: camelCaseToUcFirst(routeParams.dataId),
149
+ }} // Use utility function here
150
+ page={{
151
+ parentTitle: 'Settings',
152
+ parentUrl: '/settings',
153
+ titleKey: 'label',
154
+ }}
155
+ />
157
156
  </div>
158
157
  </div>
159
158
  </div>
@@ -165,7 +164,7 @@ const GenericSort = ({ category = false }) => {
165
164
  items={sortData}
166
165
  onSortEnd={onSortEnd}
167
166
  axis="xy"
168
- helperClass="dragitem"
167
+ helperClass={styles.dragitem}
169
168
  transitionDuration={250}
170
169
  useDragHandle
171
170
  />
@@ -34,7 +34,7 @@
34
34
  min-height: auto;
35
35
  padding: 5px 20px;
36
36
  margin: 0;
37
- background: var(--primary-color);
37
+ background: var(--tertiary-color);
38
38
  border-radius: var(--br);
39
39
  border: 1px solid rgba(var(--item-color-rgb), 1.15);
40
40
  position: relative;
@@ -42,7 +42,6 @@
42
42
  align-items: center;
43
43
 
44
44
  h1 {
45
- color: var(--background-color);
46
45
  font-weight: 700;
47
46
  font-size: 1.15em;
48
47
  margin: 0;
@@ -53,11 +52,11 @@
53
52
 
54
53
  a {
55
54
  text-decoration: none;
56
- color: var(--secondary-color);
55
+ color: rgba(var(--secondary-color-rgb), 1.1);
57
56
  }
58
57
 
59
58
  svg {
60
- color: rgba(var(--tertiary-color-rgb), 0.5);
59
+ color: rgba(var(--primary-color-rgb), 0.25);
61
60
  position: relative;
62
61
  top: 0;
63
62
  margin: 0 0.5rem;
@@ -65,3 +64,60 @@
65
64
  }
66
65
  }
67
66
  }
67
+
68
+ .dragitem {
69
+ list-style: none;
70
+ display: flex;
71
+ justify-content: center;
72
+ align-items: center;
73
+ flex-wrap: wrap;
74
+ border-radius: 5px;
75
+ border: 1px solid rgba(var(--primary-color-rgb), 0.2);
76
+ box-shadow: 0px 0px 20px rgba(var(--primary-color-rgb), 0.25);
77
+ padding: 1.25rem;
78
+ box-sizing: border-box;
79
+ cursor: grab;
80
+ list-style: none;
81
+ color: var(--paragraph-color);
82
+
83
+ svg {
84
+ width: 100%;
85
+ max-width: 18px;
86
+ position: absolute;
87
+ left: 5px;
88
+ top: 5px;
89
+ }
90
+
91
+ .pencil-edit {
92
+ width: 100%;
93
+ max-width: 18px;
94
+ position: absolute;
95
+ left: 5px;
96
+ top: 30px;
97
+ }
98
+
99
+ .trash-delete {
100
+ width: 100%;
101
+ max-width: 18px;
102
+ position: absolute;
103
+ left: 5px;
104
+ top: 60px;
105
+ }
106
+
107
+ .sortimg {
108
+ width: 50px;
109
+ position: relative;
110
+ height: 50px;
111
+ margin-left: auto;
112
+ border-radius: 5px;
113
+ overflow: hidden;
114
+
115
+ img {
116
+ display: block;
117
+ object-fit: cover;
118
+ height: 100%;
119
+ width: 100%;
120
+ overflow: hidden;
121
+ }
122
+ }
123
+ }
@@ -0,0 +1,117 @@
1
+ import React from 'react';
2
+ import { SortableElement, sortableHandle } from 'react-sortable-hoc';
3
+ import { ChevronVertical, Pencil, TrashCan } from 'akar-icons';
4
+
5
+ import styles from './styles/Item.module.css';
6
+
7
+ const DragHandle = sortableHandle(() => (
8
+ <ChevronVertical className="drag-handle" strokeWidth={1} size={26} />
9
+ ));
10
+
11
+ // Utility function to truncate text
12
+ const truncateText = (text, maxLength) => {
13
+ if (text && text.length > maxLength) {
14
+ return `${text.substring(0, maxLength)}...`;
15
+ }
16
+ return text;
17
+ };
18
+
19
+ const SortableItem = ({
20
+ containerIndex,
21
+ handleClick,
22
+ handleEdit,
23
+ handleDelete,
24
+ showImage,
25
+ value,
26
+ }) => {
27
+ const renderImage = (d) => {
28
+ let htmlContainer = '';
29
+
30
+ if (showImage) {
31
+ const imageUrl = d.image_url || d.file_url;
32
+ htmlContainer = imageUrl ? (
33
+ <div className={styles.sortimg}>
34
+ <img src={imageUrl} alt="" />
35
+ </div>
36
+ ) : (
37
+ <div className={styles.sortimg}>
38
+ {/* Placeholder image */}
39
+ <img
40
+ src="data:image/png;base64,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"
41
+ alt="Placeholder"
42
+ />
43
+ </div>
44
+ );
45
+ }
46
+
47
+ return htmlContainer;
48
+ };
49
+
50
+ const renderEditButton = () => {
51
+ if (!handleEdit) return null;
52
+
53
+ if (showImage) {
54
+ if (
55
+ value.hasOwnProperty('file_title') &&
56
+ value.hasOwnProperty('file_description')
57
+ ) {
58
+ return <EditButton />;
59
+ }
60
+ return null;
61
+ }
62
+
63
+ return <EditButton />;
64
+ };
65
+
66
+ const EditButton = () => (
67
+ <Pencil
68
+ className={styles['pencil-edit']}
69
+ strokeWidth={1}
70
+ size={26}
71
+ onClick={(e) => {
72
+ e.preventDefault();
73
+ e.stopPropagation();
74
+ handleEdit(value);
75
+ }}
76
+ />
77
+ );
78
+
79
+ return (
80
+ <li
81
+ onClick={(e) => {
82
+ e.preventDefault();
83
+ if (handleClick) {
84
+ handleClick(containerIndex);
85
+ }
86
+ }}
87
+ >
88
+ <DragHandle />
89
+ {truncateText(
90
+ value.label || value.file_title || value.file_name,
91
+ 30
92
+ )}
93
+
94
+ {renderImage(value)}
95
+
96
+ {renderEditButton()}
97
+
98
+ {handleDelete && (
99
+ <TrashCan
100
+ className={styles['trash-delete']}
101
+ strokeWidth={1}
102
+ size={26}
103
+ onClick={(e) => {
104
+ e.preventDefault();
105
+ e.stopPropagation();
106
+ handleDelete(
107
+ value.id || containerIndex,
108
+ value.label || value.file_name
109
+ );
110
+ }}
111
+ />
112
+ )}
113
+ </li>
114
+ );
115
+ };
116
+
117
+ export default SortableElement(SortableItem);
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import VisnsSortableItem from './Item';
3
+ import { SortableContainer } from 'react-sortable-hoc';
4
+
5
+ import styles from './styles/List.module.css';
6
+
7
+ const SortableList = ({
8
+ handleClick,
9
+ handleDelete,
10
+ handleEdit,
11
+ items,
12
+ showImage,
13
+ }) => {
14
+ return (
15
+ <ul className={styles.sortlist}>
16
+ {items.map((value, index) => (
17
+ <VisnsSortableItem
18
+ key={`item-${index}`}
19
+ containerIndex={index}
20
+ index={index}
21
+ handleClick={handleClick}
22
+ handleDelete={handleDelete}
23
+ handleEdit={handleEdit}
24
+ showImage={showImage}
25
+ value={value}
26
+ />
27
+ ))}
28
+ </ul>
29
+ );
30
+ };
31
+
32
+ export default SortableContainer(SortableList);
@@ -0,0 +1,68 @@
1
+ .sortlist {
2
+ width: 100%;
3
+ display: grid;
4
+ grid-gap: 0.5rem;
5
+ grid-template-columns: repeat(4, 1fr);
6
+ box-sizing: border-box;
7
+ padding: 0;
8
+ margin: 0;
9
+ list-style: none;
10
+
11
+ li {
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
+ flex-wrap: wrap;
16
+ border-radius: 5px;
17
+ border: 1px solid rgba(var(--primary-color-rgb), 0.2);
18
+ box-shadow: 0px 0px 20px rgba(var(--primary-color-rgb), 0.1);
19
+ padding: 1.25rem;
20
+ box-sizing: border-box;
21
+ cursor: pointer;
22
+ list-style: none;
23
+ position: relative;
24
+ color: var(--paragraph-color);
25
+ user-select: none;
26
+
27
+ svg {
28
+ width: 100%;
29
+ max-width: 18px;
30
+ position: absolute;
31
+ left: 5px;
32
+ top: 5px;
33
+ }
34
+
35
+ .pencil-edit {
36
+ width: 100%;
37
+ max-width: 18px;
38
+ position: absolute;
39
+ left: 5px;
40
+ top: 30px;
41
+ }
42
+
43
+ .trash-delete {
44
+ width: 100%;
45
+ max-width: 18px;
46
+ position: absolute;
47
+ left: 5px;
48
+ top: 60px;
49
+ }
50
+ }
51
+
52
+ .sortimg {
53
+ width: 50px;
54
+ position: relative;
55
+ height: 50px;
56
+ margin-left: auto;
57
+ border-radius: 5px;
58
+ overflow: hidden;
59
+
60
+ img {
61
+ display: block;
62
+ object-fit: cover;
63
+ height: 100%;
64
+ width: 100%;
65
+ overflow: hidden;
66
+ }
67
+ }
68
+ }
@@ -0,0 +1,68 @@
1
+ .sortlist {
2
+ width: 100%;
3
+ display: grid;
4
+ grid-gap: 0.5rem;
5
+ grid-template-columns: repeat(4, 1fr);
6
+ box-sizing: border-box;
7
+ padding: 0;
8
+ margin: 0;
9
+ list-style: none;
10
+
11
+ li {
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
+ flex-wrap: wrap;
16
+ border-radius: 5px;
17
+ border: 1px solid rgba(var(--primary-color-rgb), 0.2);
18
+ box-shadow: 0px 0px 20px rgba(var(--primary-color-rgb), 0.1);
19
+ padding: 1.25rem;
20
+ box-sizing: border-box;
21
+ cursor: pointer;
22
+ list-style: none;
23
+ position: relative;
24
+ color: var(--paragraph-color);
25
+ user-select: none;
26
+
27
+ svg {
28
+ width: 100%;
29
+ max-width: 18px;
30
+ position: absolute;
31
+ left: 5px;
32
+ top: 5px;
33
+ }
34
+
35
+ .pencil-edit {
36
+ width: 100%;
37
+ max-width: 18px;
38
+ position: absolute;
39
+ left: 5px;
40
+ top: 30px;
41
+ }
42
+
43
+ .trash-delete {
44
+ width: 100%;
45
+ max-width: 18px;
46
+ position: absolute;
47
+ left: 5px;
48
+ top: 60px;
49
+ }
50
+ }
51
+
52
+ .sortimg {
53
+ width: 50px;
54
+ position: relative;
55
+ height: 50px;
56
+ margin-left: auto;
57
+ border-radius: 5px;
58
+ overflow: hidden;
59
+
60
+ img {
61
+ display: block;
62
+ object-fit: cover;
63
+ height: 100%;
64
+ width: 100%;
65
+ overflow: hidden;
66
+ }
67
+ }
68
+ }
@@ -160,7 +160,7 @@ select:not(:placeholder-shown) + .fi__span {
160
160
  ):not(.inovua-react-toolkit-combo-box__input):not(
161
161
  .inovua-react-toolkit-date-input__input
162
162
  ):not(.inovua-react-toolkit-numeric-input__input):hover {
163
- border: 1px solid rgba(var(--highlight-color-rgb), 0.85);
163
+ border: 1px solid rgba(var(--paragraph-color-rgb), 0.85);
164
164
  transition: border 0.15s linear;
165
165
  }
166
166
 
@@ -172,7 +172,7 @@ select:not(:placeholder-shown) + .fi__span {
172
172
  ):not(.inovua-react-toolkit-combo-box__input):not(
173
173
  .inovua-react-toolkit-date-input__input
174
174
  ):not(.inovua-react-toolkit-numeric-input__input):focus {
175
- border: 1px solid rgba(var(--highlight-color-rgb), 0.85);
175
+ border: 1px solid rgba(var(--paragraph-color-rgb), 0.85);
176
176
  }
177
177
 
178
178
  input[type='file'] {
package/package.json CHANGED
@@ -59,7 +59,7 @@
59
59
  "react-dom": "^17.0.1"
60
60
  },
61
61
  "name": "@visns-studio/visns-components",
62
- "version": "4.6.11",
62
+ "version": "4.6.13",
63
63
  "description": "Various packages to assist in the development of our Custom Applications.",
64
64
  "main": "index.js",
65
65
  "scripts": {