pixel-react 1.0.8 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. package/lib/components/AllProjectsDropdown/AllProjectsDropdown.d.ts +3 -0
  2. package/lib/components/AllProjectsDropdown/AllProjectsDropdown.stories.d.ts +6 -0
  3. package/lib/components/AllProjectsDropdown/index.d.ts +1 -0
  4. package/lib/components/AppHeader/AppHeader.d.ts +4 -0
  5. package/lib/components/AppHeader/AppHeader.stories.d.ts +7 -0
  6. package/lib/components/AppHeader/index.d.ts +1 -0
  7. package/lib/components/AppHeader/types.d.ts +26 -0
  8. package/lib/components/Input/types.d.ts +1 -1
  9. package/lib/components/InputWithDropdown/types.d.ts +1 -1
  10. package/lib/components/Modal/types.d.ts +2 -0
  11. package/lib/components/MultiSelect/MultiSelect.d.ts +1 -1
  12. package/lib/components/MultiSelect/MultiSelect.stories.d.ts +1 -0
  13. package/lib/components/MultiSelect/MultiSelectTypes.d.ts +3 -0
  14. package/lib/index.d.ts +67 -4
  15. package/lib/index.esm.js +440 -89
  16. package/lib/index.esm.js.map +1 -1
  17. package/lib/index.js +442 -88
  18. package/lib/index.js.map +1 -1
  19. package/lib/tsconfig.tsbuildinfo +1 -1
  20. package/package.json +1 -1
  21. package/src/assets/Themes/BaseTheme.scss +5 -0
  22. package/src/assets/Themes/DarkTheme.scss +3 -0
  23. package/src/assets/icons/all_projects.svg +3 -0
  24. package/src/assets/icons/android_icon.svg +6 -0
  25. package/src/assets/icons/download_icon.svg +4 -0
  26. package/src/assets/icons/fireflink_icon.svg +4 -0
  27. package/src/assets/icons/fireflink_logo.svg +13 -0
  28. package/src/assets/icons/mobile_icon.svg +3 -0
  29. package/src/assets/icons/ms_dynamic.svg +4 -0
  30. package/src/assets/icons/sales_force.svg +7 -0
  31. package/src/assets/icons/switch_license_icon.svg +123 -0
  32. package/src/assets/icons/vertical_separator.svg +3 -0
  33. package/src/assets/icons/web&mobile_icon.svg +3 -0
  34. package/src/assets/icons/web_icon.svg +3 -0
  35. package/src/assets/styles/_colors.scss +2 -1
  36. package/src/components/AllProjectsDropdown/AllProjectsDropdown.scss +70 -0
  37. package/src/components/AllProjectsDropdown/AllProjectsDropdown.stories.tsx +21 -0
  38. package/src/components/AllProjectsDropdown/AllProjectsDropdown.tsx +148 -0
  39. package/src/components/AllProjectsDropdown/index.ts +1 -0
  40. package/src/components/AppHeader/AppHeader.scss +67 -0
  41. package/src/components/AppHeader/AppHeader.stories.tsx +156 -0
  42. package/src/components/AppHeader/AppHeader.tsx +124 -0
  43. package/src/components/AppHeader/index.ts +1 -0
  44. package/src/components/AppHeader/types.ts +27 -0
  45. package/src/components/Icon/iconList.ts +26 -0
  46. package/src/components/IconButton/IconButton.scss +2 -1
  47. package/src/components/Input/types.ts +1 -1
  48. package/src/components/InputWithDropdown/types.ts +1 -3
  49. package/src/components/Modal/Modal.stories.tsx +6 -2
  50. package/src/components/Modal/Modal.tsx +6 -2
  51. package/src/components/Modal/modal.scss +6 -4
  52. package/src/components/Modal/types.ts +2 -0
  53. package/src/components/MultiSelect/MultiSelect.scss +8 -1
  54. package/src/components/MultiSelect/MultiSelect.stories.tsx +26 -0
  55. package/src/components/MultiSelect/MultiSelect.tsx +68 -12
  56. package/src/components/MultiSelect/MultiSelectTypes.ts +3 -0
  57. package/src/components/Select/Select.scss +1 -1
  58. package/src/index.ts +7 -0
@@ -4,7 +4,6 @@ import './modal.scss';
4
4
  import { ModalProps } from './types';
5
5
  import { ThemeContext } from '../ThemeProvider/ThemeProvider';
6
6
 
7
-
8
7
  const Modal: React.FC<ModalProps> = ({
9
8
  isOpen,
10
9
  onClose,
@@ -18,6 +17,8 @@ const Modal: React.FC<ModalProps> = ({
18
17
  shouldCloseOnEsc = true,
19
18
  ariaHideApp = true,
20
19
  shouldCloseOnOverlayClick = true,
20
+ customWidth = '660px', // default width
21
+ customHeight = 'auto', // default height
21
22
  children,
22
23
  }) => {
23
24
  useEffect(() => {
@@ -52,6 +53,7 @@ const Modal: React.FC<ModalProps> = ({
52
53
  >
53
54
  <div
54
55
  className={`ff-modal-content ${currentTheme} ${contentClassName || ''}`}
56
+ style={{ width: customWidth, height: customHeight }}
55
57
  onClick={(e) => e.stopPropagation()}
56
58
  aria-label={contentLabel}
57
59
  >
@@ -61,7 +63,9 @@ const Modal: React.FC<ModalProps> = ({
61
63
  {children}
62
64
  </div>
63
65
  {isFooterDisplayed && (
64
- <div className="ff-modal-footer">{footerContent}</div>
66
+ <div className="ff-modal-footer" style={{ width: customWidth }}>
67
+ {footerContent}
68
+ </div>
65
69
  )}
66
70
  </div>,
67
71
  document.body
@@ -1,4 +1,5 @@
1
1
  @use '../../assets/styles/mixins' as *;
2
+
2
3
  .ff-modal-overlay {
3
4
  position: fixed;
4
5
  top: 0;
@@ -13,18 +14,19 @@
13
14
  .ff-modal-content {
14
15
  background: var(--ff-mini-modal-border);
15
16
  position: relative;
16
- max-width: 549px;
17
- width: 100%;
17
+ max-width: 100%;
18
18
  border-radius: 12px 12px 0 0;
19
19
  padding: 16px;
20
+
20
21
  .ff-modal-header {
21
22
  height: 32px;
22
- width: 448px;
23
+ width: 100%;
23
24
  }
24
25
  }
26
+
25
27
  .ff-modal-footer {
26
28
  background-color: var(--expandable-menu-option-bg);
27
- max-width: 549px;
29
+ max-width: 100%;
28
30
  width: 100%;
29
31
  height: 32px;
30
32
  border-radius: 0 0 12px 12px;
@@ -34,4 +34,6 @@ export interface ModalProps {
34
34
  /***Content to be displayed inside the modal */
35
35
  children: ReactNode;
36
36
  isFooterDisplayed: boolean;
37
+ customWidth: string;
38
+ customHeight?: string;
37
39
  }
@@ -108,6 +108,13 @@
108
108
  }
109
109
  }
110
110
  }
111
+ .ff-multiselect-more-chip{
112
+ width: 1rem;
113
+ @extend .fontXs;
114
+ font-weight: 600;
115
+ line-height: 16px;
116
+ color: var(--brand-color);
117
+ }
111
118
  }
112
119
  }
113
120
  &__toggle {
@@ -182,7 +189,7 @@
182
189
  .error-text {
183
190
  @extend .font-size-8;
184
191
  position: absolute;
185
- top: 36px;
192
+ margin-top: 0.25rem;
186
193
  left: 12px;
187
194
  color: var(--error-light);
188
195
  letter-spacing: 0.5px;
@@ -52,6 +52,32 @@ export const Default3: Story = {
52
52
  ...defaultArgs,
53
53
  },
54
54
  };
55
+ export const EmailGroup: Story = {
56
+ render: () => {
57
+ const [options] = useState([
58
+ { label: 'Sample1@gmail.com', value: 'sample1@gmail.com' },
59
+ { label: 'Sample2@gmail.com', value: 'sample2@gmail.com' },
60
+ ]);
61
+ const [selectedOptions, setSelectedOptions] = useState<
62
+ { label?: string; value?: string }[]
63
+ >([{ label: 'Sample1@gmail.com', value: 'sample1@gmail.com'}]);
64
+ const onChange = (options: { label?: string; value?: string }[]) => {
65
+ setSelectedOptions(options);
66
+ };
67
+ return (
68
+ <MultiSelect
69
+ label={'Enter Email'}
70
+ type='email'
71
+ required
72
+ options={options}
73
+ selectedOptions={selectedOptions}
74
+ onChange={onChange}
75
+ acceptNewOption={true}
76
+ displayCount={true}
77
+ />
78
+ );
79
+ },
80
+ };
55
81
 
56
82
  export const Controlled: Story = {
57
83
  render: () => {
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef, useState } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import { createPortal } from 'react-dom';
3
3
  import classNames from 'classnames';
4
4
  import './MultiSelect.scss';
@@ -30,8 +30,10 @@ const ChipElement = ({
30
30
  };
31
31
  const MultiSelect = ({
32
32
  options,
33
+ type = "text",
33
34
  selectedOptions = [],
34
35
  onChange = () => {},
36
+ acceptNewOption = false,
35
37
  zIndex = 100,
36
38
  label = '',
37
39
  onSearch = () => {},
@@ -40,6 +42,7 @@ const MultiSelect = ({
40
42
  errorMessage = 'Fill this field',
41
43
  withSelectButton = false,
42
44
  onSelect = () => {},
45
+ displayCount = false
43
46
  }: MultiSelectProps) => {
44
47
  const [isOpen, setIsOpen] = useState<boolean>(false);
45
48
  const [allOptions, setAllOptions] = useState(options);
@@ -47,6 +50,7 @@ const MultiSelect = ({
47
50
  const [searchedKeyword, setSearchedKeyword] = useState('');
48
51
  const [isSelectFocusedOnce, setIsSelectFocusedOnce] =
49
52
  useState<boolean>(false);
53
+ const [inputError, setInputError] = useState<string>('')
50
54
 
51
55
  const [dropdownPosition, setDropdownPosition] = useState<{
52
56
  top: number;
@@ -67,6 +71,9 @@ const MultiSelect = ({
67
71
  const selectWrapper = useRef<HTMLInputElement>(null);
68
72
  let isFieldSkipped = isSelectFocusedOnce && selectedOptions.length === 0;
69
73
 
74
+ const maxVisibleChips = 2;
75
+ const hiddenCount = selectedOptions.length - maxVisibleChips;
76
+
70
77
  const handleClick = () => {
71
78
  if (!isOpen) {
72
79
  setIsOpen(true);
@@ -102,6 +109,32 @@ const MultiSelect = ({
102
109
  e.stopPropagation();
103
110
  handleOptionChange(option, false);
104
111
  };
112
+ const handleKeyEnter = (e: React.KeyboardEvent<HTMLDivElement>) => {
113
+ if (acceptNewOption && e.key === "Enter") {
114
+ setInputError('');
115
+ if (type === "email") {
116
+ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
117
+ if (!emailPattern.test(searchedKeyword)) {
118
+ setIsOpen(false);
119
+ setInputError("Please enter a valid email address.");
120
+ return;
121
+ }
122
+ }
123
+
124
+ const newOption = {
125
+ label: searchedKeyword,
126
+ value: searchedKeyword.toLowerCase(),
127
+ isChecked: true,
128
+ };
129
+ const filteredOptions = [...allOptions].filter(option => option.isChecked === true);
130
+
131
+ setAllOptions([...allOptions, newOption]);
132
+ setSearchedKeyword('');
133
+ onChange?.([...filteredOptions, { label: searchedKeyword, value: searchedKeyword.toLocaleLowerCase() }]);
134
+ setIsOpen(false);
135
+ }
136
+ };
137
+
105
138
  const calculatePosition = () => {
106
139
  if (dropdownWrapper.current && selectWrapper.current) {
107
140
  const rect = dropdownWrapper.current?.getBoundingClientRect();
@@ -159,6 +192,7 @@ const MultiSelect = ({
159
192
  !dropdownRef.current.contains(event?.target as Node) &&
160
193
  !selectWrapper.current.contains(event?.target as Node)
161
194
  ) {
195
+ setInputError('')
162
196
  setIsOpen(false);
163
197
  if (!isSelectFocusedOnce) {
164
198
  setIsSelectFocusedOnce(true);
@@ -179,7 +213,7 @@ const MultiSelect = ({
179
213
  className={classNames('ff-multiselect-wrapper', {
180
214
  'ff-multiselect-wrapper--with-options': selectedOptions?.length,
181
215
  'ff-multiselect-wrapper--opened-dropdown': isOpen,
182
- 'ff-multiselect-wrapper--error': isFieldSkipped && required,
216
+ 'ff-multiselect-wrapper--error': (isFieldSkipped && required) || inputError,
183
217
  'ff-multiselect-wrapper--disabled': disabled,
184
218
  })}
185
219
  >
@@ -197,15 +231,28 @@ const MultiSelect = ({
197
231
  {label}
198
232
  </span>
199
233
  <div className="ff-multiselect-chip-container">
200
- {selectedOptions.map((option) => (
201
- <ChipElement
202
- key={option?.label}
203
- label={option?.label || ''}
204
- onChipCloseClick={(e) => handleChipCloseClick(option, e)}
205
- />
206
- ))}
234
+ {displayCount ?
235
+ <>
236
+ {selectedOptions.slice(0, maxVisibleChips).map((option) => (
237
+ <ChipElement
238
+ key={option?.label}
239
+ label={option?.label || ''}
240
+ onChipCloseClick={(e) => handleChipCloseClick(option, e)}
241
+ />
242
+ ))}
243
+ </> :
244
+ selectedOptions.map((option) => (
245
+ <ChipElement
246
+ key={option?.label}
247
+ label={option?.label || ''}
248
+ onChipCloseClick={(e) => handleChipCloseClick(option, e)}
249
+ />
250
+ ))
251
+ }
207
252
  <div className="ff-multiselect-input-container">
208
253
  <input
254
+ value={searchedKeyword}
255
+ type={type}
209
256
  autoComplete="off"
210
257
  placeholder="search..."
211
258
  ref={inputRef}
@@ -216,6 +263,7 @@ const MultiSelect = ({
216
263
  setSearchedKeyword(e.target.value);
217
264
  onSearch?.(e.target.value);
218
265
  }}
266
+ onKeyDown={handleKeyEnter}
219
267
  id="input-ele"
220
268
  className="ff-select-input"
221
269
  style={{
@@ -224,6 +272,14 @@ const MultiSelect = ({
224
272
  }}
225
273
  />
226
274
  </div>
275
+ {hiddenCount > 0 && (
276
+ <div
277
+ className="ff-multiselect-more-chip"
278
+ onClick={toggleDropdown}
279
+ >
280
+ +{hiddenCount}
281
+ </div>
282
+ )}
227
283
  </div>
228
284
  </div>
229
285
  <div onClick={toggleDropdown} className="ff-multiselect__toggle">
@@ -238,8 +294,8 @@ const MultiSelect = ({
238
294
  </div>
239
295
  </div>
240
296
  <div ref={dropdownWrapper}>
241
- {isFieldSkipped && required && errorMessage && (
242
- <div className="error-text">{errorMessage}</div>
297
+ {(inputError || (isFieldSkipped && required && errorMessage)) && (
298
+ <div className="error-text">{inputError || errorMessage }</div>
243
299
  )}
244
300
  {isOpen &&
245
301
  createPortal(
@@ -261,4 +317,4 @@ const MultiSelect = ({
261
317
  );
262
318
  };
263
319
 
264
- export default MultiSelect;
320
+ export default MultiSelect;
@@ -6,16 +6,19 @@ interface Option {
6
6
  }
7
7
  interface MultiSelectProps {
8
8
  options: Option[];
9
+ type? : 'email' | 'text';
9
10
  label: string;
10
11
  selectedOptions?: Option[];
11
12
  disabled?: boolean;
12
13
  onSearch?: (searchedKeyword: string) => void;
13
14
  onChange?: (selectedOptions: Option[]) => void;
15
+ acceptNewOption?: boolean;
14
16
  zIndex?: number;
15
17
  required?: boolean;
16
18
  errorMessage?: string;
17
19
  withSelectButton?: boolean;
18
20
  onSelect?: () => void;
21
+ displayCount?:boolean;
19
22
  }
20
23
 
21
24
  export { Option, MultiSelectProps };
@@ -112,7 +112,7 @@
112
112
  @include absolute-position(6px, auto, auto, auto);
113
113
  width: calc(100% - 38px);
114
114
  min-height: calc(100% - 8px);
115
- padding: 0 8px;
115
+ padding: 0 28px 0 8px;
116
116
  border-radius: 4px;
117
117
  border: 1px solid transparent;
118
118
  z-index: 100;
package/src/index.ts CHANGED
@@ -36,7 +36,10 @@ import Search from './components/Search/Search';
36
36
  import DatePicker from './components/DatePicker';
37
37
  import StateDropdown from './components/StateDropdown';
38
38
  import IconButton from './components/IconButton';
39
+ import Modal from './components/Modal';
39
40
  import DragAndDrop from './components/DragAndDrop/DragAndDrop';
41
+ import AllProjectsDropdown from './components/AllProjectsDropdown';
42
+ import AppHeader from './components/AppHeader';
40
43
 
41
44
  // Utils imports
42
45
  import { checkEmpty } from './utils/checkEmpty/checkEmpty';
@@ -86,7 +89,11 @@ export {
86
89
  StateDropdown,
87
90
  StatusButton,
88
91
  IconButton,
92
+ Modal,
93
+
89
94
  DragAndDrop,
95
+ AllProjectsDropdown,
96
+ AppHeader,
90
97
 
91
98
  // utils exports
92
99
  checkEmpty,