groovinads-ui 1.2.55 → 1.2.58

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.55",
4
+ "version": "1.2.58",
5
5
  "keywords": [
6
6
  "css",
7
7
  "sass",
@@ -14,31 +14,10 @@ const Button = ({
14
14
  className = '',
15
15
  style = 'default',
16
16
  processing = false,
17
+ disabled = false,
17
18
  }) => {
18
- let classes = `btn ${className}`;
19
-
20
- // Variant
21
- if (variant !== 'default') {
22
- classes += ` btn-${variant}`;
23
- }
24
-
25
- // Size
26
- if (size !== 'md') {
27
- classes += ` btn-${size}`;
28
- }
29
-
30
- // Style
31
- if (style !== 'default') {
32
- classes += ` btn-${style}`;
33
- }
34
-
35
- // Processing
36
- if (processing) {
37
- classes += ` btn-processing`;
38
- }
39
-
40
- // Icon
41
- const content = processing ? (
19
+ // Icon
20
+ const content = processing ? (
42
21
  <>
43
22
  <Spinner scale={1} />
44
23
  {children && <span>{children}…</span>}
@@ -68,7 +47,16 @@ const content = processing ? (
68
47
  );
69
48
 
70
49
  return (
71
- <button className={classes} onClick={onClick}>
50
+ <button
51
+ className={
52
+ `btn ${className}
53
+ btn-${variant}
54
+ btn-${style}
55
+ ${processing ? 'btn-processing' : ''}
56
+ ${disabled ? 'disabled' : ''}`
57
+ }
58
+ onClick={onClick}
59
+ >
72
60
  {content}
73
61
  </button>
74
62
  );
@@ -1,150 +1,148 @@
1
1
  import PropTypes from 'prop-types';
2
- import React, { useEffect, useRef, useState } from 'react'
2
+ import React, { useEffect, useRef, useState } from 'react';
3
3
  import { format } from 'date-fns';
4
4
 
5
5
  // COMPONENTS
6
- import { Dropdown } from 'react-bootstrap'
6
+ import { Dropdown } from 'react-bootstrap';
7
7
  import { Icon } from '../Labels';
8
8
  import { Button } from '../Button';
9
9
  import DatePicker from 'react-datepicker';
10
10
 
11
-
12
11
  export default function DropdownSimpleDatePicker({
13
- className = '',
14
- show,
15
- setShow,
16
- onToggle,
17
- inputLabel = 'period',
18
- overflow = false,
19
- date,
20
- setDate,
21
- handleClear,
12
+ className = '',
13
+ show,
14
+ setShow,
15
+ onToggle,
16
+ inputLabel = 'period',
17
+ overflow = false,
18
+ date,
19
+ setDate,
20
+ handleClear,
21
+ minDate,
22
+ maxDate,
22
23
  }) {
23
-
24
- const [internalShow, setInternalShow] = useState(!!show);
25
- const dropdownRef = useRef(null);
26
-
27
- const internalToggle = () => {
28
- const newShowState = !internalShow;
29
- setInternalShow(newShowState);
30
-
31
- if (newShowState) window.dispatchEvent(new CustomEvent('dropdownToggle', { detail: inputLabel }));
32
-
33
- try {
34
- onToggle();
35
- } catch (error) { }
36
- };
37
-
38
- const closeDropdown = () => {
39
- setInternalShow(false);
40
- try {
41
- setShow(false);
42
- } catch (error) { }
43
- };
44
-
45
- const updateDateFilters = (newDate) => {
46
- setDate(newDate?.toString());
47
- setTimeout(() => {
48
- closeDropdown();
49
- }, 200);
50
- };
51
-
52
-
53
- const clearDate = () => {
54
- closeDropdown();
55
-
56
- if (handleClear) {
57
- handleClear()
58
- return
59
- }
60
- setDate(null);
61
- }
62
-
63
- useEffect(() => {
64
- const handleClickOutside = (event) => {
65
- if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
66
- closeDropdown();
67
- }
68
- };
69
-
70
- document.addEventListener('click', handleClickOutside);
71
- return () => {
72
- document.removeEventListener('click', handleClickOutside);
73
- };
74
- }, []);
75
-
76
- useEffect(() => {
77
- setInternalShow(!!show);
78
- }, [show]);
79
-
80
- return (
81
- <Dropdown
82
- show={internalShow}
83
- className={className}
84
- ref={dropdownRef}
85
- >
86
- <Dropdown.Toggle
87
- variant='toggle-filter'
88
- onClick={internalToggle}
89
- className={`btn-input w-100`}
90
- >
91
- <span className='dropdown-label'>{inputLabel}</span>
92
- <span>
93
- {date ? format(new Date(date), 'EEE MMM dd yyyy') : ''}
94
- </span>
95
-
96
- <Icon iconName='angle-down' className={'caret'} />
97
-
98
- </Dropdown.Toggle>
99
-
100
- <Dropdown.Menu
101
- popperConfig={
102
- overflow
103
- ? {
104
- strategy: 'fixed',
105
- onFirstUpdate: () =>
106
- window.dispatchEvent(new CustomEvent('scroll')),
107
- }
108
- : undefined
109
- }
110
- >
111
- <Dropdown.Item as={'div'}>
112
-
113
- <DatePicker
114
- className='form-control form-search'
115
- selected={date || ''}
116
- onChange={updateDateFilters}
117
- startDate={date || ''}
118
- monthsShown={1}
119
- inline
120
- />
121
-
122
- <div className='px-2 w-100'>
123
- <Button
124
- onClick={clearDate}
125
- icon='calendar-circle-minus'
126
- size='xs'
127
- variant='terciary'
128
- className='w-100'
129
- >
130
- Clear
131
- </Button>
132
- </div>
133
- </Dropdown.Item>
134
- </Dropdown.Menu>
135
- </Dropdown>
136
- )
24
+ const [internalShow, setInternalShow] = useState(!!show);
25
+ const dropdownRef = useRef(null);
26
+
27
+ const internalToggle = () => {
28
+ const newShowState = !internalShow;
29
+ setInternalShow(newShowState);
30
+
31
+ if (newShowState)
32
+ window.dispatchEvent(
33
+ new CustomEvent('dropdownToggle', { detail: inputLabel }),
34
+ );
35
+
36
+ try {
37
+ onToggle();
38
+ } catch (error) {}
39
+ };
40
+
41
+ const closeDropdown = () => {
42
+ setInternalShow(false);
43
+ try {
44
+ setShow(false);
45
+ } catch (error) {}
46
+ };
47
+
48
+ const updateDateFilters = (newDate) => {
49
+ setDate(newDate?.toString());
50
+ setTimeout(() => {
51
+ closeDropdown();
52
+ }, 200);
53
+ };
54
+
55
+ const clearDate = () => {
56
+ closeDropdown();
57
+
58
+ if (handleClear) {
59
+ handleClear();
60
+ return;
61
+ }
62
+ setDate(null);
63
+ };
64
+
65
+ useEffect(() => {
66
+ const handleClickOutside = (event) => {
67
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
68
+ closeDropdown();
69
+ }
70
+ };
71
+
72
+ document.addEventListener('click', handleClickOutside);
73
+ return () => {
74
+ document.removeEventListener('click', handleClickOutside);
75
+ };
76
+ }, []);
77
+
78
+ useEffect(() => {
79
+ setInternalShow(!!show);
80
+ }, [show]);
81
+
82
+ return (
83
+ <Dropdown show={internalShow} className={className} ref={dropdownRef}>
84
+ <Dropdown.Toggle
85
+ variant='toggle-filter'
86
+ onClick={internalToggle}
87
+ className={`btn-input w-100`}
88
+ >
89
+ <span className='dropdown-label'>{inputLabel}</span>
90
+ <span>{date ? format(new Date(date), 'EEE MMM dd yyyy') : ''}</span>
91
+
92
+ <Icon iconName='angle-down' className={'caret'} />
93
+ </Dropdown.Toggle>
94
+
95
+ <Dropdown.Menu
96
+ popperConfig={
97
+ overflow
98
+ ? {
99
+ strategy: 'fixed',
100
+ onFirstUpdate: () =>
101
+ window.dispatchEvent(new CustomEvent('scroll')),
102
+ }
103
+ : undefined
104
+ }
105
+ >
106
+ <Dropdown.Item as={'div'}>
107
+ <DatePicker
108
+ className='form-control form-search'
109
+ selected={date || ''}
110
+ onChange={updateDateFilters}
111
+ startDate={date || ''}
112
+ monthsShown={1}
113
+ inline
114
+ minDate={minDate}
115
+ maxDate={maxDate}
116
+ />
117
+
118
+ <div className='px-2 w-100'>
119
+ <Button
120
+ onClick={clearDate}
121
+ icon='calendar-circle-minus'
122
+ size='xs'
123
+ variant='terciary'
124
+ className='w-100'
125
+ >
126
+ Clear
127
+ </Button>
128
+ </div>
129
+ </Dropdown.Item>
130
+ </Dropdown.Menu>
131
+ </Dropdown>
132
+ );
137
133
  }
138
134
 
139
135
  DropdownSimpleDatePicker.propTypes = {
140
- className: PropTypes.string,
141
- show: PropTypes.bool,
142
- setShow: PropTypes.func,
143
- onToggle: PropTypes.func,
144
- inputLabel: PropTypes.string,
145
- overflow: PropTypes.bool,
146
- date: PropTypes.string,
147
- setDate: PropTypes.func,
148
- clearDate: PropTypes.func,
149
- handleClear: PropTypes.func,
150
- };
136
+ className: PropTypes.string,
137
+ show: PropTypes.bool,
138
+ setShow: PropTypes.func,
139
+ onToggle: PropTypes.func,
140
+ inputLabel: PropTypes.string,
141
+ overflow: PropTypes.bool,
142
+ date: PropTypes.string,
143
+ setDate: PropTypes.func,
144
+ clearDate: PropTypes.func,
145
+ handleClear: PropTypes.func,
146
+ minDate: PropTypes.object,
147
+ maxDate: PropTypes.object,
148
+ };
@@ -1,4 +1,4 @@
1
- import React, { Children, useEffect, useRef, useState } from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import DatePicker from 'react-datepicker'; // https://reactdatepicker.com
3
3
  import PropTypes from 'prop-types';
4
4
  import Dropdown from 'react-bootstrap/Dropdown';
@@ -6,9 +6,6 @@ import Dropdown from 'react-bootstrap/Dropdown';
6
6
  // COMPONENTS
7
7
  import { Icon } from '../../Labels';
8
8
  import { Button } from '../../Button';
9
- import { Switch } from '../../Inputs';
10
- import { Tabnav } from '../../Navigation';
11
- // import PeriodAndDetailDropdowns from './PeriodAndDetailDropdowns';
12
9
 
13
10
  // HOOKS
14
11
  import { useTextFormatter } from '../../../hooks';
@@ -31,12 +28,13 @@ const DropdownDatePicker = ({
31
28
  setDateFrom2,
32
29
  dateTo2,
33
30
  setDateTo2,
31
+ minDate,
32
+ maxDate,
34
33
  }) => {
35
- //const [activeTab, setActiveTab] = useState(1);
34
+ // const [activeTab, setActiveTab] = useState(1);
36
35
 
37
36
  const [compareDates, setCompareDates] = useState(true);
38
37
 
39
-
40
38
  /* We have a 'local' state for datepickers inside the dropdown, so the user can cancel the dropdown and go back to previous values. */
41
39
  /* When user confirms selected values, the 'local' state replaces the values previously stored in the DateFilters component. */
42
40
  /* ========== STATE FOR DATEPCIKER 1 ========== */
@@ -252,6 +250,8 @@ const DropdownDatePicker = ({
252
250
  monthsShown={isMobile ? 1 : 2}
253
251
  selectsRange
254
252
  inline
253
+ minDate={minDate}
254
+ maxDate={maxDate}
255
255
  />
256
256
 
257
257
  <div className='datepicker-footer'>
@@ -0,0 +1,149 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ // COMPONENTS
5
+ import { PillComponent } from '../Labels';
6
+
7
+ const InputChip = ({
8
+ className ='',
9
+ placeholder,
10
+ keywordsList = [],
11
+ setKeywordsList,
12
+ counter = false,
13
+ nonRecomendedKeywords = [],
14
+ maxKeywordLength, // max keyword length (each keyword).
15
+ recomendedKeywords, // Num recomendedKeywords.
16
+ maxKeywords, // maxKeywords (total array).
17
+ requiredText, // requiered--> maxKeywordsList.
18
+
19
+ }) => {
20
+ // STATE
21
+ const [inputValue, setInputValue] = useState('');
22
+ const [error, setError] = useState('');
23
+
24
+ // VARIABLES
25
+ const strokeDasharray = 56.55
26
+
27
+ // FUNCTIONS
28
+ // ADD
29
+ const handleKeyDown = (e) => {
30
+ if (
31
+ e.key === 'Enter' &&
32
+ inputValue.trim() !== ''
33
+ && keywordsList.length < maxKeywords
34
+ ) {
35
+ // MAX KEYWORD LENGTH
36
+ if (inputValue.length > maxKeywordLength) {
37
+ setError(`Each keyword must be ${maxKeywordLength} characters or less.`);
38
+ setTimeout(() => setError(''), 2000);
39
+ setInputValue('');
40
+ return;
41
+ }
42
+
43
+ const newChipsList = [...keywordsList, inputValue.trim()];
44
+ setKeywordsList(newChipsList);
45
+ setInputValue('');
46
+ }
47
+ };
48
+
49
+ // DELETE
50
+ const handleDelete = (chipToDelete) => {
51
+ const newChipsList = keywordsList.filter((chip) => chip !== chipToDelete);
52
+ setKeywordsList(newChipsList);
53
+ };
54
+
55
+ return (
56
+ <>
57
+ <div className={`position-relative ${className}`}>
58
+ <div className={`input-chip ${keywordsList.length === maxKeywords ? 'not-validated' : ''}`} data-error={requiredText}>
59
+
60
+ <div className='wrapper'>
61
+ {/* PILLS */}
62
+ {keywordsList.map((chip, index) => (
63
+ <PillComponent
64
+ key={index}
65
+ closeButton={true}
66
+ color={
67
+ nonRecomendedKeywords.includes(chip) ? 'danger' : 'green'
68
+ }
69
+ onClick={() => handleDelete(chip)}
70
+ >
71
+ {chip}
72
+ </PillComponent>
73
+ ))}
74
+ </div>
75
+
76
+ {/* INPUT */}
77
+ <input
78
+ type='text'
79
+ value={inputValue}
80
+ onKeyDown={handleKeyDown}
81
+ onChange={(e) => {
82
+ setInputValue(e.target.value);
83
+ }}
84
+ placeholder={placeholder}
85
+ />
86
+
87
+ {/* KEYWORDS COUNTER */}
88
+ {counter && keywordsList.length >= recomendedKeywords && (
89
+ <div className={`progress-counter-wrapper ${
90
+ keywordsList.length >= maxKeywords
91
+ ? 'progress-value-error'
92
+ : keywordsList.length >= recomendedKeywords
93
+ ? 'progress-value-warning'
94
+ : ''
95
+ }`}
96
+ >
97
+ <svg
98
+ width='20'
99
+ height='20'
100
+ viewBox='0 0 20 20'
101
+ xmlns='http://www.w3.org/2000/svg'
102
+ className='progress-counter'
103
+ >
104
+ <circle
105
+ className='progress-bg'
106
+ cx='10'
107
+ cy='10'
108
+ r='9'
109
+ fill='none'
110
+ />
111
+
112
+ <circle
113
+ className='progress-value'
114
+ cx='10'
115
+ cy='10'
116
+ r='9'
117
+ strokeDasharray={strokeDasharray}
118
+ strokeDashoffset={strokeDasharray * (1 - keywordsList.length / maxKeywords)}
119
+ strokeLinecap='round'
120
+ fill='none'
121
+ />
122
+ </svg>
123
+ <span className='progress-counter-text'>
124
+ {keywordsList.length}
125
+ </span>
126
+ </div>
127
+ )}
128
+ </div>
129
+
130
+ {/* ERROR MESSAGGE */}
131
+ {error && <small className='form-text text-muted'>{error}</small>}
132
+ </div>
133
+ </>
134
+ );
135
+ };
136
+
137
+ InputChip.propTypes = {
138
+ className: PropTypes.string,
139
+ placeholder: PropTypes.string.isRequired,
140
+ keywordsList: PropTypes.array.isRequired,
141
+ setKeywordsList: PropTypes.func.isRequired,
142
+ maxKeywordLength: PropTypes.number.isRequired,
143
+ nonRecomendedKeywords: PropTypes.array,
144
+ maxKeywords: PropTypes.number.isRequired,
145
+ requiredText: PropTypes.string.isRequired,
146
+ counter: PropTypes.bool,
147
+ };
148
+
149
+ export default InputChip;
@@ -3,5 +3,6 @@ import Input from './Input';
3
3
  import Radio from './Radio';
4
4
  import Switch from './Switch';
5
5
  import Textarea from './Textarea';
6
+ import InputChip from './InputChip';
6
7
 
7
- export { Checkbox, Input, Radio, Switch, Textarea }
8
+ export { Checkbox, Input, Radio, Switch, Textarea, InputChip };
@@ -2,10 +2,22 @@ import React from 'react';
2
2
  import Button from '../components/Button/Button';
3
3
 
4
4
  export default {
5
- title: 'Buttons/Button',
6
- component: Button,
5
+ title: 'Buttons/Button',
6
+ component: Button,
7
7
  };
8
8
 
9
- const Template = (args) => <Button {...args}>Button</Button>;
9
+ const Template = (args) => {
10
+ return (
11
+ <>
12
+ <Button
13
+ variant='primary'
14
+ size='lg'
15
+ onClick={() => console.log('Button clicked')}
16
+ >
17
+ Disabled Button
18
+ </Button>
19
+ </>
20
+ );
21
+ };
10
22
 
11
- export const Default = Template.bind({});
23
+ export const Default = Template.bind({});
@@ -23,7 +23,7 @@ const Template = (args) => {
23
23
 
24
24
  useEffect(() => {
25
25
  console.log('dates To:', dateTo);
26
- }, [ dateTo ]);
26
+ }, [dateTo]);
27
27
 
28
28
  return (
29
29
  <>
@@ -34,7 +34,7 @@ const Template = (args) => {
34
34
  // show={show}
35
35
  // setShow={setShow}
36
36
  // onToggle={handleToggle}
37
- //variant='filter'
37
+ // variant='filter'
38
38
  dateFrom={dateFrom}
39
39
  setDateFrom={setDateFrom}
40
40
  dateTo={dateTo}
@@ -45,6 +45,23 @@ const Template = (args) => {
45
45
  // setDateTo2={setDateTo2}
46
46
  />
47
47
  </div>
48
+
49
+ <div>
50
+ <p>min max</p>
51
+ <DropdownDatePicker
52
+ {...args}
53
+ // show={show}
54
+ // setShow={setShow}
55
+ // onToggle={handleToggle}
56
+ // variant='filter'
57
+ dateFrom={dateFrom}
58
+ setDateFrom={setDateFrom}
59
+ dateTo={dateTo}
60
+ setDateTo={setDateTo}
61
+ minDate={new Date('2025-01-01')}
62
+ maxDate={new Date('2025-01-31')}
63
+ />
64
+ </div>
48
65
  </>
49
66
  );
50
67
  };
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useState } from 'react';
2
2
  import DropdownSimpleDatePicker from '../components/Dropdowns/DropdownSimpleDatePicker';
3
3
 
4
4
  export default {
@@ -13,39 +13,42 @@ const Template = (args) => {
13
13
  const [endDate, setEndDate] = useState('');
14
14
 
15
15
  return (
16
- // <>
17
- // <button onClick={() => setShow(!show)}>Toggle</button>
18
- // <div className='col-2'>
19
- // <DropdownSimpleDatePicker
20
- // {...args}
21
- // dateFrom={dateFrom}
22
- // setDateFrom={setDateFrom}
23
- // date={dateTo}
24
- // setDate={setDateTo}
25
- // />
26
- // </div>
27
- // </>
28
-
29
16
  <>
30
17
  <div className='d-flex justify-content-between mt-3'>
31
18
  {/* DROPDOWN TO TEST THE handleGlobalToggle */}
32
-
33
19
  <div className='col-3'>
34
20
  <DropdownSimpleDatePicker
35
21
  {...args}
36
- inputLabel="Start Date"
22
+ inputLabel='Start Date'
37
23
  date={startDate}
38
24
  setDate={setStartDate}
39
25
  />
40
26
  </div>
41
-
27
+
28
+ <div className='col-3'>
29
+ <DropdownSimpleDatePicker
30
+ {...args}
31
+ inputLabel='End Date'
32
+ date={endDate}
33
+ setDate={setEndDate}
34
+ handleClear={() => {
35
+ console.log('Aqui');
36
+ }}
37
+ />
38
+ </div>
39
+
42
40
  <div className='col-3'>
41
+ <p>minmax</p>
43
42
  <DropdownSimpleDatePicker
44
43
  {...args}
45
- inputLabel="End Date"
44
+ inputLabel='End Date'
46
45
  date={endDate}
47
46
  setDate={setEndDate}
48
- handleClear={() => {console.log('Aqui');}}
47
+ handleClear={() => {
48
+ console.log('Aqui');
49
+ }}
50
+ minDate={new Date('2025-01-01')}
51
+ maxDate={new Date('2025-01-31')}
49
52
  />
50
53
  </div>
51
54
  </div>