@visns-studio/visns-components 5.2.6 → 5.2.8
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
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
79
79
|
},
|
|
80
80
|
"name": "@visns-studio/visns-components",
|
|
81
|
-
"version": "5.2.
|
|
81
|
+
"version": "5.2.8",
|
|
82
82
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
83
83
|
"main": "src/index.js",
|
|
84
84
|
"files": [
|
|
@@ -215,12 +215,15 @@ function Form({
|
|
|
215
215
|
const handleSelectOption = () => {
|
|
216
216
|
updateFormData({ [id]: inputValue });
|
|
217
217
|
|
|
218
|
-
// Update any matching form data properties
|
|
219
|
-
Object.keys(inputValue)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
218
|
+
// Update any matching form data properties
|
|
219
|
+
Object.keys(inputValue)
|
|
220
|
+
.filter(
|
|
221
|
+
(key) =>
|
|
222
|
+
formData.hasOwnProperty(key) &&
|
|
223
|
+
key !== 'id' &&
|
|
224
|
+
key !== 'label'
|
|
225
|
+
)
|
|
226
|
+
.forEach((key) => updateFormData({ [key]: inputValue[key] }));
|
|
224
227
|
|
|
225
228
|
formSettings.fields.forEach((field) => {
|
|
226
229
|
if (field.id === id && field.trigger) {
|
|
@@ -8,7 +8,9 @@ import Popup from 'reactjs-popup';
|
|
|
8
8
|
import dayjs from 'dayjs';
|
|
9
9
|
import parse from 'html-react-parser';
|
|
10
10
|
import { createSwapy } from 'swapy';
|
|
11
|
-
import { CircleX, Minus, Plus, Star } from 'akar-icons';
|
|
11
|
+
import { CircleX, Minus, Plus, Star, TrashCan } from 'akar-icons';
|
|
12
|
+
import { confirmAlert } from 'react-confirm-alert';
|
|
13
|
+
import 'react-confirm-alert/src/react-confirm-alert.css';
|
|
12
14
|
|
|
13
15
|
import CustomFetch from '../../crm/Fetch';
|
|
14
16
|
import MultiSelect from '../../crm/MultiSelect';
|
|
@@ -30,6 +32,18 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
30
32
|
const [modalShow, setModalShow] = useState(false);
|
|
31
33
|
const [modalContent, setModalContent] = useState(null);
|
|
32
34
|
const [editMode, setEditMode] = useState(false);
|
|
35
|
+
const [showAddWidgetModal, setShowAddWidgetModal] = useState(false);
|
|
36
|
+
const [selectedAddWidgetId, setSelectedAddWidgetId] = useState('');
|
|
37
|
+
// Flag used only for triggering reinitialization after widget addition.
|
|
38
|
+
const [widgetAdded, setWidgetAdded] = useState(false);
|
|
39
|
+
|
|
40
|
+
// Compute available widgets from original settings that are not yet added.
|
|
41
|
+
const availableWidgets = setting.widgets.filter(
|
|
42
|
+
(widget) =>
|
|
43
|
+
!dashboardSetting.widgets.find(
|
|
44
|
+
(dashWidget) => dashWidget.id === widget.id
|
|
45
|
+
)
|
|
46
|
+
);
|
|
33
47
|
|
|
34
48
|
const getDefaultDate = (defaultValue) => {
|
|
35
49
|
switch (defaultValue) {
|
|
@@ -42,7 +56,7 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
42
56
|
}
|
|
43
57
|
};
|
|
44
58
|
|
|
45
|
-
// Initialize filters based on each widget’s filters
|
|
59
|
+
// Initialize filters based on each widget’s filters.
|
|
46
60
|
useEffect(() => {
|
|
47
61
|
const initialFilters = {};
|
|
48
62
|
dashboardSetting.widgets.forEach((widget) => {
|
|
@@ -70,7 +84,6 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
70
84
|
try {
|
|
71
85
|
const promises = dashboardSetting.widgets.map(async (widget) => {
|
|
72
86
|
const widgetFilters = appliedFilters[widget.id] || {};
|
|
73
|
-
|
|
74
87
|
if (widget.filters) {
|
|
75
88
|
for (const filter of widget.filters) {
|
|
76
89
|
if (filter.url) {
|
|
@@ -97,7 +110,6 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
97
110
|
}
|
|
98
111
|
}
|
|
99
112
|
}
|
|
100
|
-
|
|
101
113
|
if (widget.api?.url && widget.api?.method) {
|
|
102
114
|
return CustomFetch(
|
|
103
115
|
widget.api.url,
|
|
@@ -107,7 +119,6 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
107
119
|
}
|
|
108
120
|
return null;
|
|
109
121
|
});
|
|
110
|
-
|
|
111
122
|
const results = await Promise.all(promises);
|
|
112
123
|
const fetchedData = dashboardSetting.widgets.reduce(
|
|
113
124
|
(acc, widget, index) => {
|
|
@@ -150,7 +161,6 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
150
161
|
const handleFilterSelectChange = (value, action, id) => {
|
|
151
162
|
const widgetId = id.split('.')[0];
|
|
152
163
|
const filterId = id.split('.')[1];
|
|
153
|
-
|
|
154
164
|
if (action.action === 'select-option') {
|
|
155
165
|
setFilters((prevFilters) => ({
|
|
156
166
|
...prevFilters,
|
|
@@ -207,7 +217,6 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
207
217
|
} else if (direction === 'decrease' && currentIndex > 0) {
|
|
208
218
|
newSize = order[currentIndex - 1];
|
|
209
219
|
}
|
|
210
|
-
|
|
211
220
|
return { ...widget, size: newSize };
|
|
212
221
|
}
|
|
213
222
|
return widget;
|
|
@@ -218,7 +227,7 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
218
227
|
});
|
|
219
228
|
};
|
|
220
229
|
|
|
221
|
-
// Toggle the highlight state of a widget
|
|
230
|
+
// Toggle the highlight state of a widget.
|
|
222
231
|
const toggleHighlight = (widgetId) => {
|
|
223
232
|
setDashboardSetting((prevSetting) => {
|
|
224
233
|
const updatedWidgets = prevSetting.widgets.map((widget) => {
|
|
@@ -233,7 +242,48 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
233
242
|
});
|
|
234
243
|
};
|
|
235
244
|
|
|
236
|
-
//
|
|
245
|
+
// Delete a widget from the dashboard after confirming.
|
|
246
|
+
const handleDeleteWidget = (widgetId) => {
|
|
247
|
+
setDashboardSetting((prevSetting) => {
|
|
248
|
+
const newWidgets = prevSetting.widgets.filter(
|
|
249
|
+
(widget) => widget.id !== widgetId
|
|
250
|
+
);
|
|
251
|
+
const newSetting = { ...prevSetting, widgets: newWidgets };
|
|
252
|
+
updateDashboardSetting(newSetting);
|
|
253
|
+
return newSetting;
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const confirmDelete = (widgetId) => {
|
|
258
|
+
confirmAlert({
|
|
259
|
+
title: 'Confirm to delete',
|
|
260
|
+
message: 'Are you sure you want to delete this widget?',
|
|
261
|
+
buttons: [
|
|
262
|
+
{ label: 'Yes', onClick: () => handleDeleteWidget(widgetId) },
|
|
263
|
+
{ label: 'No' },
|
|
264
|
+
],
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// Add a widget to the dashboard (append to end). After adding, set widgetAdded flag.
|
|
269
|
+
const handleAddWidget = () => {
|
|
270
|
+
const widgetToAdd = setting.widgets.find(
|
|
271
|
+
(widget) => widget.id === selectedAddWidgetId
|
|
272
|
+
);
|
|
273
|
+
if (widgetToAdd) {
|
|
274
|
+
setDashboardSetting((prevSetting) => {
|
|
275
|
+
const newWidgets = [...prevSetting.widgets, widgetToAdd];
|
|
276
|
+
const newSetting = { ...prevSetting, widgets: newWidgets };
|
|
277
|
+
updateDashboardSetting(newSetting);
|
|
278
|
+
return newSetting;
|
|
279
|
+
});
|
|
280
|
+
setSelectedAddWidgetId('');
|
|
281
|
+
setShowAddWidgetModal(false);
|
|
282
|
+
setWidgetAdded(true); // Trigger reinitialization after addition.
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// Map widget size to a CSS class.
|
|
237
287
|
const getGridClass = (size) => {
|
|
238
288
|
switch (size) {
|
|
239
289
|
case 'full':
|
|
@@ -465,7 +515,6 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
465
515
|
}, {});
|
|
466
516
|
widget.props.legendLabel = (e) =>
|
|
467
517
|
legendMapping[e.id] || e.id;
|
|
468
|
-
|
|
469
518
|
const CustomTooltip = ({ id, value, color }) => {
|
|
470
519
|
const label = legendMapping[id] || id;
|
|
471
520
|
return (
|
|
@@ -482,12 +531,10 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
482
531
|
</div>
|
|
483
532
|
);
|
|
484
533
|
};
|
|
485
|
-
|
|
486
534
|
widget.props.tooltip = CustomTooltip;
|
|
487
535
|
}
|
|
488
536
|
const barData =
|
|
489
537
|
widgetData.bar?.data || widgetData.data || widgetData;
|
|
490
|
-
|
|
491
538
|
if (barData?.length > 0) {
|
|
492
539
|
return (
|
|
493
540
|
<div style={{ height: widget.height || '600px' }}>
|
|
@@ -784,17 +831,11 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
784
831
|
}
|
|
785
832
|
};
|
|
786
833
|
|
|
787
|
-
//
|
|
834
|
+
// Swap handler.
|
|
788
835
|
const handleWidgetSwap = (event) => {
|
|
789
|
-
const parseSlot = (slot) =>
|
|
790
|
-
// Slot format: "widget-slot-INDEX"
|
|
791
|
-
const parts = slot.split('-');
|
|
792
|
-
return parseInt(parts[2], 10);
|
|
793
|
-
};
|
|
794
|
-
|
|
836
|
+
const parseSlot = (slot) => parseInt(slot.split('-')[2], 10);
|
|
795
837
|
const fromIndex = parseSlot(event.fromSlot);
|
|
796
838
|
const toIndex = parseSlot(event.toSlot);
|
|
797
|
-
|
|
798
839
|
setDashboardSetting((prevSetting) => {
|
|
799
840
|
const newWidgets = [...prevSetting.widgets];
|
|
800
841
|
[newWidgets[fromIndex], newWidgets[toIndex]] = [
|
|
@@ -807,120 +848,138 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
807
848
|
});
|
|
808
849
|
};
|
|
809
850
|
|
|
851
|
+
// Effect to reinitialize Swapy when editMode changes.
|
|
810
852
|
useEffect(() => {
|
|
811
853
|
if (editMode && container.current) {
|
|
854
|
+
swapy.current?.destroy();
|
|
812
855
|
swapy.current = createSwapy(container.current, {
|
|
813
856
|
manualSwap: true,
|
|
814
857
|
});
|
|
815
858
|
swapy.current.onSwap((event) => {
|
|
816
|
-
console.log('swap event', event);
|
|
817
859
|
handleWidgetSwap(event);
|
|
818
860
|
});
|
|
819
|
-
}
|
|
820
|
-
return () => {
|
|
861
|
+
} else {
|
|
821
862
|
swapy.current?.destroy();
|
|
822
863
|
swapy.current = null;
|
|
823
|
-
}
|
|
864
|
+
}
|
|
865
|
+
// This effect depends solely on editMode.
|
|
824
866
|
}, [editMode]);
|
|
825
867
|
|
|
868
|
+
// Separate effect to reinitialize Swapy after a widget is added.
|
|
869
|
+
useEffect(() => {
|
|
870
|
+
if (widgetAdded && editMode && container.current) {
|
|
871
|
+
swapy.current?.destroy();
|
|
872
|
+
swapy.current = createSwapy(container.current, {
|
|
873
|
+
manualSwap: true,
|
|
874
|
+
});
|
|
875
|
+
swapy.current.onSwap((event) => {
|
|
876
|
+
handleWidgetSwap(event);
|
|
877
|
+
});
|
|
878
|
+
setWidgetAdded(false);
|
|
879
|
+
}
|
|
880
|
+
// This effect only triggers when widgetAdded changes.
|
|
881
|
+
}, [widgetAdded, editMode]);
|
|
882
|
+
|
|
826
883
|
const renderWidgets = () => {
|
|
827
|
-
return dashboardSetting.widgets.map((widget, index) =>
|
|
828
|
-
|
|
884
|
+
return dashboardSetting.widgets.map((widget, index) => (
|
|
885
|
+
<div
|
|
886
|
+
key={widget.id}
|
|
887
|
+
className={`${getGridClass(widget.size)} ${
|
|
888
|
+
widget.type !== 'list'
|
|
889
|
+
? widget.highlight
|
|
890
|
+
? styles['dashwidget--highlight']
|
|
891
|
+
: styles.dashwidget
|
|
892
|
+
: ''
|
|
893
|
+
} ${
|
|
894
|
+
editMode
|
|
895
|
+
? widget.highlight
|
|
896
|
+
? styles.editModeWidgetHighlight
|
|
897
|
+
: styles.editModeWidgetNormal
|
|
898
|
+
: ''
|
|
899
|
+
}`}
|
|
900
|
+
style={editMode ? { cursor: 'grab' } : {}}
|
|
901
|
+
{...(editMode && { 'data-swapy-slot': `widget-slot-${index}` })}
|
|
902
|
+
>
|
|
829
903
|
<div
|
|
830
|
-
key={widget.id}
|
|
831
|
-
className={`${getGridClass(widget.size)} ${
|
|
832
|
-
widget.type !== 'list'
|
|
833
|
-
? widget.highlight
|
|
834
|
-
? styles['dashwidget--highlight']
|
|
835
|
-
: styles.dashwidget
|
|
836
|
-
: ''
|
|
837
|
-
} ${
|
|
838
|
-
editMode
|
|
839
|
-
? widget.highlight
|
|
840
|
-
? styles.editModeWidgetHighlight
|
|
841
|
-
: styles.editModeWidgetNormal
|
|
842
|
-
: ''
|
|
843
|
-
}`}
|
|
844
|
-
style={editMode ? { cursor: 'grab' } : {}}
|
|
845
904
|
{...(editMode && {
|
|
846
|
-
'data-swapy-
|
|
905
|
+
'data-swapy-item': `widget-${widget.id}`,
|
|
847
906
|
})}
|
|
907
|
+
className={`${styles.widgetItem}`}
|
|
848
908
|
>
|
|
849
|
-
<div
|
|
850
|
-
{
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
<div className={styles.
|
|
856
|
-
<
|
|
909
|
+
<div className={styles.widgetTitle}>
|
|
910
|
+
<h2>{widget.title}</h2>
|
|
911
|
+
</div>
|
|
912
|
+
{renderFilters(widget)}
|
|
913
|
+
<div>{renderWidget(widget)}</div>
|
|
914
|
+
{widget.modal && (
|
|
915
|
+
<div className={styles.widgetAction}>
|
|
916
|
+
<button
|
|
917
|
+
className={`${styles.btn} ${styles.dmore}`}
|
|
918
|
+
onClick={() => openModal(widget.id)}
|
|
919
|
+
>
|
|
920
|
+
View all{' '}
|
|
921
|
+
<span>({data[widget.id]?.length || 0})</span>
|
|
922
|
+
</button>
|
|
857
923
|
</div>
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
{
|
|
861
|
-
<div className={styles.
|
|
924
|
+
)}
|
|
925
|
+
{editMode && (
|
|
926
|
+
<div className={styles.widgetTools}>
|
|
927
|
+
<div className={styles.resizeTools}>
|
|
862
928
|
<button
|
|
863
|
-
|
|
864
|
-
onClick={() => openModal(widget.id)}
|
|
929
|
+
onClick={() => toggleHighlight(widget.id)}
|
|
865
930
|
>
|
|
866
|
-
|
|
867
|
-
<span>
|
|
868
|
-
({data[widget.id]?.length || 0})
|
|
869
|
-
</span>
|
|
931
|
+
<Star strokeWidth={2} size={12} />
|
|
870
932
|
</button>
|
|
871
|
-
|
|
872
|
-
)}
|
|
873
|
-
{editMode && (
|
|
874
|
-
<div className={styles.widgetTools}>
|
|
875
|
-
<div className={styles.resizeTools}>
|
|
933
|
+
{(widget.size || '1/3') !== '1/3' && (
|
|
876
934
|
<button
|
|
877
935
|
onClick={() =>
|
|
878
|
-
|
|
936
|
+
changeSize(widget.id, 'decrease')
|
|
879
937
|
}
|
|
880
938
|
>
|
|
881
|
-
<
|
|
939
|
+
<Minus strokeWidth={2} size={12} />
|
|
882
940
|
</button>
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
)}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
changeSize(
|
|
899
|
-
widget.id,
|
|
900
|
-
'increase'
|
|
901
|
-
)
|
|
902
|
-
}
|
|
903
|
-
>
|
|
904
|
-
<Plus strokeWidth={2} size={12} />
|
|
905
|
-
</button>
|
|
906
|
-
)}
|
|
907
|
-
</div>
|
|
941
|
+
)}
|
|
942
|
+
{(widget.size || '1/3') !== 'full' && (
|
|
943
|
+
<button
|
|
944
|
+
onClick={() =>
|
|
945
|
+
changeSize(widget.id, 'increase')
|
|
946
|
+
}
|
|
947
|
+
>
|
|
948
|
+
<Plus strokeWidth={2} size={12} />
|
|
949
|
+
</button>
|
|
950
|
+
)}
|
|
951
|
+
<button
|
|
952
|
+
onClick={() => confirmDelete(widget.id)}
|
|
953
|
+
>
|
|
954
|
+
<TrashCan strokeWidth={2} size={12} />
|
|
955
|
+
</button>
|
|
908
956
|
</div>
|
|
909
|
-
|
|
910
|
-
|
|
957
|
+
</div>
|
|
958
|
+
)}
|
|
911
959
|
</div>
|
|
912
|
-
|
|
913
|
-
|
|
960
|
+
</div>
|
|
961
|
+
));
|
|
914
962
|
};
|
|
915
963
|
|
|
916
964
|
return (
|
|
917
965
|
<>
|
|
918
966
|
<div className={styles.editButtonContainer}>
|
|
967
|
+
{editMode && (
|
|
968
|
+
<>
|
|
969
|
+
<button
|
|
970
|
+
className={styles.btn}
|
|
971
|
+
style={{ marginRight: '10px' }}
|
|
972
|
+
onClick={() => setShowAddWidgetModal(true)}
|
|
973
|
+
>
|
|
974
|
+
Add Widget
|
|
975
|
+
</button>
|
|
976
|
+
</>
|
|
977
|
+
)}
|
|
919
978
|
<button
|
|
920
979
|
className={styles.btn}
|
|
921
980
|
onClick={() => setEditMode((prev) => !prev)}
|
|
922
981
|
>
|
|
923
|
-
{editMode ? 'Done' : 'Edit'}
|
|
982
|
+
{editMode ? 'Done' : 'Edit Dashboard'}
|
|
924
983
|
</button>
|
|
925
984
|
</div>
|
|
926
985
|
<div className={styles.grid} ref={container}>
|
|
@@ -954,6 +1013,63 @@ function GenericDashboard({ setting, userProfile }) {
|
|
|
954
1013
|
</div>
|
|
955
1014
|
</div>
|
|
956
1015
|
</Popup>
|
|
1016
|
+
<Popup
|
|
1017
|
+
open={showAddWidgetModal}
|
|
1018
|
+
onClose={() => setShowAddWidgetModal(false)}
|
|
1019
|
+
modal
|
|
1020
|
+
>
|
|
1021
|
+
<div className="modalwrap">
|
|
1022
|
+
<div className={`${styles.addWidgetPopup} modal`}>
|
|
1023
|
+
<div className="modal__header">
|
|
1024
|
+
<h1>Add a Widget</h1>
|
|
1025
|
+
<button
|
|
1026
|
+
className="modal__close"
|
|
1027
|
+
onClick={() => setShowAddWidgetModal(false)}
|
|
1028
|
+
>
|
|
1029
|
+
<CircleX strokeWidth={1} size={24} />
|
|
1030
|
+
</button>
|
|
1031
|
+
</div>
|
|
1032
|
+
<div className="modal__content">
|
|
1033
|
+
{availableWidgets.length > 0 ? (
|
|
1034
|
+
<div>
|
|
1035
|
+
<select
|
|
1036
|
+
value={selectedAddWidgetId}
|
|
1037
|
+
onChange={(e) =>
|
|
1038
|
+
setSelectedAddWidgetId(
|
|
1039
|
+
e.target.value
|
|
1040
|
+
)
|
|
1041
|
+
}
|
|
1042
|
+
className={styles.addWidgetSelect}
|
|
1043
|
+
>
|
|
1044
|
+
<option value="">
|
|
1045
|
+
-- Select a widget --
|
|
1046
|
+
</option>
|
|
1047
|
+
{availableWidgets.map((widget) => (
|
|
1048
|
+
<option
|
|
1049
|
+
key={widget.id}
|
|
1050
|
+
value={widget.id}
|
|
1051
|
+
>
|
|
1052
|
+
{widget.title}
|
|
1053
|
+
</option>
|
|
1054
|
+
))}
|
|
1055
|
+
</select>
|
|
1056
|
+
<button
|
|
1057
|
+
className={styles.addWidgetButton}
|
|
1058
|
+
onClick={handleAddWidget}
|
|
1059
|
+
disabled={!selectedAddWidgetId}
|
|
1060
|
+
>
|
|
1061
|
+
Add
|
|
1062
|
+
</button>
|
|
1063
|
+
</div>
|
|
1064
|
+
) : (
|
|
1065
|
+
<div className={styles.noWidgetsMessage}>
|
|
1066
|
+
No more widgets available
|
|
1067
|
+
</div>
|
|
1068
|
+
)}
|
|
1069
|
+
</div>
|
|
1070
|
+
</div>
|
|
1071
|
+
</div>
|
|
1072
|
+
</Popup>
|
|
957
1073
|
</div>
|
|
958
1074
|
</>
|
|
959
1075
|
);
|
|
@@ -536,3 +536,38 @@
|
|
|
536
536
|
.widgetItem {
|
|
537
537
|
position: relative;
|
|
538
538
|
}
|
|
539
|
+
|
|
540
|
+
/* Styles for the Add Widget Popup */
|
|
541
|
+
.addWidgetSelect {
|
|
542
|
+
width: 100%;
|
|
543
|
+
padding: 10px;
|
|
544
|
+
margin-bottom: 1rem;
|
|
545
|
+
border: 1px solid #dadce0;
|
|
546
|
+
border-radius: 5px;
|
|
547
|
+
font-size: 1rem;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.addWidgetButton {
|
|
551
|
+
width: 100%;
|
|
552
|
+
padding: 10px;
|
|
553
|
+
background: var(--primary-color);
|
|
554
|
+
color: var(--tertiary-color);
|
|
555
|
+
border: none;
|
|
556
|
+
border-radius: 5px;
|
|
557
|
+
font-size: 1rem;
|
|
558
|
+
font-weight: 600;
|
|
559
|
+
text-transform: uppercase;
|
|
560
|
+
cursor: pointer;
|
|
561
|
+
transition: background-color 0.25s ease;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.addWidgetButton:hover {
|
|
565
|
+
background: var(--secondary-color);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.noWidgetsMessage {
|
|
569
|
+
text-align: center;
|
|
570
|
+
font-size: 1rem;
|
|
571
|
+
color: #888;
|
|
572
|
+
padding: 20px;
|
|
573
|
+
}
|