groovinads-ui 1.2.19 → 1.2.21

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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "groovinads-ui",
3
3
  "description": "Groovinads UI is a React component library designed exclusively for Groovinads applications. It provides ready-to-use UI elements styled according to Groovinads design guidelines to facilitate rapid development.",
4
- "version": "1.2.19",
4
+ "version": "1.2.21",
5
5
  "keywords": [
6
6
  "css",
7
7
  "sass",
@@ -72,5 +72,8 @@
72
72
  "sb": "storybook dev -p 6006",
73
73
  "build-storybook": "storybook build",
74
74
  "build-lib": "rollup -c"
75
+ },
76
+ "dependencies": {
77
+ "react-datepicker": "^7.3.0"
75
78
  }
76
79
  }
@@ -16,10 +16,9 @@ const DropdownComponent = ({
16
16
  align = 'start',
17
17
  show,
18
18
  }) => {
19
-
20
19
  const [Toggle, Menu] = children;
21
20
 
22
- const modifiedToggle = {...Toggle, align}; // Al toggle le agrega el align
21
+ const modifiedToggle = { ...Toggle, align }; // Al toggle le agrega el align
23
22
 
24
23
  const modifiedMenu = React.cloneElement(Menu, {
25
24
  as: 'ul',
@@ -27,12 +26,16 @@ const DropdownComponent = ({
27
26
  className: fullWidth ? 'w-100' : '',
28
27
  popperConfig: overflow
29
28
  ? {
30
- strategy: 'fixed',
31
- onFirstUpdate: () => window.dispatchEvent(new CustomEvent('scroll'))
32
- }
29
+ strategy: 'fixed',
30
+ onFirstUpdate: () => window.dispatchEvent(new CustomEvent('scroll')),
31
+ }
33
32
  : undefined,
34
- children: Menu.props.children.map((child) => React.cloneElement(child, {as: 'li'}))}
35
- );
33
+ children: Menu.props.children.length
34
+ ? Menu.props.children.map((child) =>
35
+ React.cloneElement(child, { as: 'li' }),
36
+ )
37
+ : React.cloneElement(Menu.props.children, { as: 'li' }),
38
+ });
36
39
 
37
40
  return (
38
41
  <Dropdown
@@ -86,7 +86,7 @@ const DropdownFilter = ({
86
86
  />
87
87
  </div>
88
88
  <div className='filter-values'>
89
- { valuesSelected.length == values.length ? 'All selected' : valuesSelected.length ? valuesSelected.join('; ') : 'None'}
89
+ { valuesSelected.length === values.length ? 'All selected' : valuesSelected.length ? valuesSelected.join('; ') : 'None'}
90
90
  </div>
91
91
  </Dropdown.Toggle>
92
92
  {!locked && (
@@ -1,17 +1,17 @@
1
- import React, { useState } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
 
4
4
  // BOOTSTRAP
5
5
  import Dropdown from 'react-bootstrap/Dropdown';
6
6
 
7
7
  // COMPONENTS
8
- import { Icon, PillComponent } from '../../components/Labels';
9
- import { Checkbox, Input } from '../../components/Inputs';
8
+ import { Icon, PillComponent } from '../Labels';
9
+ import { Checkbox, Input } from '../Inputs';
10
10
 
11
11
  // HOOKS
12
12
  import { useTextFormatter } from '../../hooks/index';
13
13
 
14
- const DropdownInput = ({
14
+ const DropdownMultiSelect = ({
15
15
  autoClose = false,
16
16
  className = '',
17
17
  drop,
@@ -26,15 +26,17 @@ const DropdownInput = ({
26
26
  setValuesSelected,
27
27
  values = [],
28
28
  show,
29
- idInPill = false,
30
- showStatus= '',
29
+ idInPill = false,
30
+ showStatus = '',
31
+ focus = false,
32
+ buttonVariant = 'input',
31
33
  }) => {
32
34
  const [query, setQuery] = useState('');
33
35
 
34
36
  const { highlightText } = useTextFormatter();
35
37
 
36
38
  const handleCheckbox = (status, value) => {
37
- let i = values.findIndex(
39
+ const i = values.findIndex(
38
40
  (item) =>
39
41
  (object ? item[idKey] : item) === (object ? value[idKey] : value),
40
42
  );
@@ -58,11 +60,13 @@ const DropdownInput = ({
58
60
  };
59
61
 
60
62
  const valuesFromSearch = () =>
61
- values.filter((item) =>
62
- (object ? `${item[idKey]} ${item[nameKey]}` : item)
63
- .toLowerCase()
64
- .includes(query.toLowerCase()) && (showStatus ? parseInt(item.status) == parseInt(showStatus) : true),
65
- );
63
+ values.filter(
64
+ (item) =>
65
+ (object ? `${item[idKey]} ${item[nameKey]}` : item)
66
+ .toLowerCase()
67
+ .includes(query.toLowerCase()) &&
68
+ (showStatus ? parseInt(item.status) === parseInt(showStatus) : true),
69
+ );
66
70
 
67
71
  return (
68
72
  <Dropdown
@@ -72,8 +76,8 @@ const DropdownInput = ({
72
76
  onToggle={onToggle}
73
77
  drop={drop}
74
78
  >
75
- <Dropdown.Toggle as={'div'} className='btn btn-input w-100'>
76
- {valuesSelected.length > 0 && (
79
+ <Dropdown.Toggle as={'div'} className={`btn btn-${buttonVariant} w-100`}>
80
+ {( valuesSelected.length > 0 && buttonVariant === 'input' ) && (
77
81
  <div className='wrapper'>
78
82
  {valuesSelected.map((value, index) => {
79
83
  return (
@@ -86,9 +90,8 @@ const DropdownInput = ({
86
90
  }}
87
91
  key={'Dropdown.PillComponent' + index}
88
92
  >
89
- {idInPill ? `#${value[idKey]} - ` : ''}
93
+ {idInPill ? `#${value[idKey]} - ` : ''}
90
94
  <span>{object ? value[nameKey] : value}</span>
91
-
92
95
  </PillComponent>
93
96
  );
94
97
  })}
@@ -123,7 +126,7 @@ const DropdownInput = ({
123
126
  icon={'magnifying-glass'}
124
127
  size={'xs'}
125
128
  className={'w-100'}
126
- customRef={show}
129
+ focus={focus}
127
130
  />
128
131
  </Dropdown.Item>
129
132
 
@@ -165,7 +168,7 @@ const DropdownInput = ({
165
168
  );
166
169
  };
167
170
 
168
- DropdownInput.propTypes = {
171
+ DropdownMultiSelect.propTypes = {
169
172
  inputLabel: PropTypes.string,
170
173
  searchLabel: PropTypes.string,
171
174
  autoClose: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
@@ -180,8 +183,16 @@ DropdownInput.propTypes = {
180
183
  object: PropTypes.bool,
181
184
  nameKey: PropTypes.string,
182
185
  idKey: PropTypes.string,
183
- idInPill: PropTypes.bool,
184
- showStatus: PropTypes.string,
186
+ idInPill: PropTypes.bool,
187
+ showStatus: PropTypes.string,
188
+ focus: PropTypes.bool,
189
+ buttonVariant: PropTypes.oneOf([
190
+ 'input',
191
+ 'primary',
192
+ 'secondary',
193
+ 'terciary',
194
+ 'outline',
195
+ ]),
185
196
  };
186
197
 
187
- export default DropdownInput;
198
+ export default DropdownMultiSelect;
@@ -0,0 +1,306 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import DatePicker from 'react-datepicker'; // https://reactdatepicker.com
3
+ import PropTypes from 'prop-types';
4
+
5
+ // COMPONENTS
6
+ import { Icon } from '../../Labels';
7
+ import DropdownComponent from '../DropdownComponent'
8
+ import PeriodAndDetailDropdowns from './PeriodAndDetailDropdowns';
9
+
10
+ const DropdownDatePicker = ({
11
+ datePicker1StartDate,
12
+ setDatePicker1StartDate,
13
+ datePicker1EndDate,
14
+ setDatePicker1EndDate,
15
+ datePicker2StartDate,
16
+ setDatePicker2StartDate,
17
+ datePicker2EndDate,
18
+ setDatePicker2EndDate,
19
+ tableQueryObj,
20
+ setTableQueryObj,
21
+ fetchData,
22
+ variant = 'input',
23
+ locked = false,
24
+ }) => {
25
+ // eslint-disable-next-line no-unused-vars
26
+ const [compareDates, setCompareDates] = useState(false);
27
+ const [activeTab, setActiveTab] = useState(1);
28
+
29
+ /* We have a 'local' state for datepickers inside the dropdown, so the user can cancel the dropdown and go back to previous values. */
30
+ /* When user confirms selected values, the 'local' state replaces the values previously stored in the DateFilters component. */
31
+ /* ========== STATE FOR DATEPCIKER 1 ========== */
32
+ const [DDStartDate1, setDDStartDate1] = useState(datePicker1StartDate);
33
+ const [DDEndDate1, setDDEndDate1] = useState(datePicker1EndDate);
34
+ const [DDStartDate2, setDDStartDate2] = useState(datePicker2StartDate);
35
+ const [DDEndDate2, setDDEndDate2] = useState(datePicker2EndDate);
36
+
37
+ const onChangeDDDate1 = (dates) => {
38
+ const [start, end] = dates;
39
+ setDDStartDate1(start);
40
+ setDDEndDate1(end);
41
+ };
42
+
43
+ /* State for dropdowns only visible when selecting 'compare dates' option */
44
+ const [DDPeriod1, setDDPeriod1] = useState('Custom range');
45
+ const [DDDetailLevel1, setDDDetailLevel1] = useState('Daily');
46
+
47
+ /* ========== STATE FOR DATEPICKER 2 ========== - DatePicker 2 is only visible when selecting 'compare dates' option */
48
+ const [DDStartDate2, setDDStartDate2] = useState(datePicker2StartDate);
49
+ const [DDEndDate2, setDDEndDate2] = useState(datePicker2EndDate);
50
+
51
+ const onChangeDDDate2 = (dates) => {
52
+ const [start, end] = dates;
53
+ setDDStartDate2(start);
54
+ setDDEndDate2(end);
55
+ };
56
+
57
+ /* State for dropdowns only visible when selecting 'compare dates' option */
58
+ const [DDPeriod2, setDDPeriod2] = useState('Custom range');
59
+ const [DDDetailLevel2, setDDDetailLevel2] = useState('Daily');
60
+
61
+ const dropdown = useRef(null);
62
+
63
+ const closeDropwdown = () => {
64
+ if (dropdown?.current?.className)
65
+ dropdown.current.className = 'dropdown-menu p-3 shadow-2';
66
+ };
67
+
68
+ const formatDate = (date) => {
69
+ if (!date) return undefined;
70
+ return date.toLocaleString('en-US', {
71
+ month: 'short',
72
+ day: '2-digit',
73
+ year: 'numeric',
74
+ });
75
+ };
76
+
77
+ const formatDateForQueryObj = (date) => {
78
+ if (!date) return undefined;
79
+
80
+ const year = date?.getFullYear();
81
+ const month = (1 + date?.getMonth())?.toString()?.padStart(2, '0');
82
+ const day = date?.getDate()?.toString()?.padStart(2, '0');
83
+
84
+ return `${year}-${month}-${day} 00:00:00`;
85
+ };
86
+
87
+ const updateDateFilters = () => {
88
+ const newQueryObj = { ...tableQueryObj };
89
+
90
+ // Update state stored in the DateFilters component
91
+ setDatePicker1StartDate(DDStartDate1);
92
+ if (DDEndDate1 === null) {
93
+ setDatePicker1EndDate(DDStartDate1);
94
+ newQueryObj.report_request.date_to = formatDateForQueryObj(DDStartDate1);
95
+ } else {
96
+ setDatePicker1EndDate(DDEndDate1);
97
+ newQueryObj.report_request.date_to = formatDateForQueryObj(DDEndDate1);
98
+ }
99
+ setDatePicker2StartDate(DDStartDate2);
100
+ setDatePicker2EndDate(DDEndDate2);
101
+
102
+ // Update tableDateFilterQuery object
103
+ if (DDStartDate1)
104
+ newQueryObj.report_request.date_from =
105
+ formatDateForQueryObj(DDStartDate1);
106
+
107
+ newQueryObj.report_request.id_period = 0;
108
+
109
+ setTableQueryObj(newQueryObj);
110
+
111
+ fetchData(newQueryObj);
112
+
113
+ closeDropwdown();
114
+ };
115
+
116
+ const isMobile = window?.innerWidth < 500;
117
+
118
+ // ========== HACK ========== - Remove unnecesary div generated by the library
119
+ const removeBugDiv = () =>
120
+ document?.querySelector('.react-datepicker__aria-live')?.remove();
121
+
122
+ useEffect(() => removeBugDiv(), []);
123
+
124
+ return (
125
+ <div className='dropdown dropdown-pills'>
126
+ {/* INPUT */}
127
+ <label className='dropdown-label' htmlFor='reportDatePicker'>
128
+ Period
129
+ </label>
130
+
131
+ {variant === 'input' ? (
132
+ <button
133
+ className='btn btn-input dropdown-toggle'
134
+ id='reportDatePicker'
135
+ type='button'
136
+ data-bs-toggle='dropdown'
137
+ aria-expanded='false'
138
+ >
139
+ {/* Start date */}
140
+ <span className='fw-bold highlighted'>
141
+ {formatDate(datePicker1StartDate)}
142
+ </span>
143
+ <span>to</span>
144
+ {/* End date */}
145
+ <span className='fw-bold highlighted'>
146
+ {formatDate(datePicker1EndDate)}
147
+ </span>
148
+ <i className='fa-solid fa-calendar-day'></i>
149
+ </button>
150
+ ) : (
151
+ <DropdownComponent>
152
+ <div className='filter-heading'>
153
+ <div className='filter-title'>
154
+ <span>Date</span>
155
+ {locked && (
156
+ <Icon style={'solid'} iconName={'lock-keyhole'} scale={0.7} />
157
+ )}
158
+ </div>
159
+ <Icon
160
+ style={'solid'}
161
+ iconName={'angle-down'}
162
+ scale={1}
163
+ className='caret'
164
+ />
165
+ </div>
166
+ <div className='filter-values'>
167
+ <span className='fw-bold highlighted'>
168
+ {formatDate(datePicker1StartDate)}
169
+ </span>
170
+ <span>to</span>
171
+ {/* End date */}
172
+ <span className='fw-bold highlighted'>
173
+ {formatDate(datePicker1EndDate)}
174
+ </span>
175
+ </div>
176
+ </DropdownComponent>
177
+ )}
178
+
179
+ {/* DROPDOWN */}
180
+ <div
181
+ className='dropdown-menu p-3 shadow-2'
182
+ onClick={(e) => e.stopPropagation()} // Prevent Dropdown from closing when clicking inside of it
183
+ ref={dropdown}
184
+ >
185
+ {compareDates && (
186
+ <>
187
+ {/* DATE TABS */}
188
+ <ul className='nav nav-tabs' id='dateComparisonTabs' role='tablist'>
189
+ <li
190
+ className='nav-item'
191
+ role='presentation'
192
+ onClick={() => setActiveTab(1)}
193
+ >
194
+ <button
195
+ className='nav-link active'
196
+ id='date1ComparisonTab'
197
+ data-bs-toggle='tab'
198
+ data-bs-target='#date1ComparisonTabPane'
199
+ type='button'
200
+ role='tab'
201
+ aria-controls='date1ComparisonTabPane'
202
+ aria-selected='true'
203
+ >
204
+ Date 1
205
+ </button>
206
+ </li>
207
+
208
+ <li
209
+ className='nav-item'
210
+ role='presentation'
211
+ onClick={() => setActiveTab(2)}
212
+ >
213
+ <button
214
+ className='nav-link'
215
+ id='date2ComparisonTab'
216
+ data-bs-toggle='tab'
217
+ data-bs-target='#date2ComparisonTabPane'
218
+ type='button'
219
+ role='tab'
220
+ aria-controls='date2ComparisonTabPane'
221
+ aria-selected='false'
222
+ >
223
+ Date 2
224
+ </button>
225
+ </li>
226
+ </ul>
227
+
228
+ {/* PERIOD AND DETAIL LEVEL DROPDOWNS */}
229
+ <PeriodAndDetailDropdowns
230
+ DDPeriod1={DDPeriod1}
231
+ setDDPeriod1={setDDPeriod1}
232
+ DDDetailLevel1={DDDetailLevel1}
233
+ setDDDetailLevel1={setDDDetailLevel1}
234
+ DDPeriod2={DDPeriod2}
235
+ setDDPeriod2={setDDPeriod2}
236
+ DDDetailLevel2={DDDetailLevel2}
237
+ setDDDetailLevel2={setDDDetailLevel2}
238
+ />
239
+ </>
240
+ )}
241
+
242
+ {/* DATE PICKERS */}
243
+ {activeTab === 1 ? (
244
+ <DatePicker
245
+ className='form-control form-search'
246
+ selected={DDStartDate1}
247
+ onChange={onChangeDDDate1}
248
+ startDate={DDStartDate1}
249
+ endDate={DDEndDate1}
250
+ monthsShown={isMobile ? 1 : 2}
251
+ selectsRange
252
+ inline
253
+ />
254
+ ) : (
255
+ <DatePicker
256
+ className='form-control form-search'
257
+ selected={DDStartDate2}
258
+ onChange={onChangeDDDate2}
259
+ startDate={DDStartDate2}
260
+ endDate={DDEndDate2}
261
+ monthsShown={isMobile ? 1 : 2}
262
+ selectsRange
263
+ inline
264
+ />
265
+ )}
266
+
267
+ <div className='d-flex flex-nowrap flex-column flex-md-row'>
268
+ <div className='d-flex justify-content-end gap-3 flex-column-reverse flex-md-row w-100'>
269
+ <button
270
+ className='btn btn-small btn-terciary'
271
+ onClick={() => closeDropwdown()}
272
+ >
273
+ Cancel
274
+ </button>
275
+
276
+ <button
277
+ className='btn btn-small'
278
+ // disabled={!DDStartDate1 || !DDEndDate1} // Start and end dates must be selected
279
+ onClick={() => updateDateFilters()}
280
+ >
281
+ Set date
282
+ </button>
283
+ </div>
284
+ </div>
285
+ </div>
286
+ </div>
287
+ );
288
+ };
289
+
290
+ DropdownDatePicker.propTypes = {
291
+ datePicker1StartDate: PropTypes.instanceOf(Date),
292
+ setDatePicker1StartDate: PropTypes.func,
293
+ datePicker1EndDate: PropTypes.instanceOf(Date),
294
+ setDatePicker1EndDate: PropTypes.func,
295
+ datePicker2StartDate: PropTypes.instanceOf(Date),
296
+ setDatePicker2StartDate: PropTypes.func,
297
+ datePicker2EndDate: PropTypes.instanceOf(Date),
298
+ setDatePicker2EndDate: PropTypes.func,
299
+ tableQueryObj: PropTypes.object,
300
+ setTableQueryObj: PropTypes.func,
301
+ fetchData: PropTypes.func,
302
+ variant: PropTypes.oneOf(['input', 'filter']),
303
+ locked: PropTypes.bool,
304
+ };
305
+
306
+ export default DropdownDatePicker;