design-react-kit 5.6.0 → 5.7.0

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.
Files changed (112) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/Accordion/AccordionBody.cjs +1 -1
  3. package/dist/Accordion/AccordionBody.cjs.map +1 -1
  4. package/dist/Accordion/AccordionBody.js +31 -21
  5. package/dist/Accordion/AccordionBody.js.map +1 -1
  6. package/dist/Accordion/AccordionHeader.cjs +1 -1
  7. package/dist/Accordion/AccordionHeader.cjs.map +1 -1
  8. package/dist/Accordion/AccordionHeader.js +1 -1
  9. package/dist/Accordion/AccordionHeader.js.map +1 -1
  10. package/dist/Autocomplete/Autocomplete.cjs +1 -3
  11. package/dist/Autocomplete/Autocomplete.cjs.map +1 -1
  12. package/dist/Autocomplete/Autocomplete.js +27 -4
  13. package/dist/Autocomplete/Autocomplete.js.map +1 -1
  14. package/dist/Carousel/Carousel.cjs +2 -0
  15. package/dist/Carousel/Carousel.cjs.map +1 -0
  16. package/dist/Carousel/Carousel.js +193 -0
  17. package/dist/Carousel/Carousel.js.map +1 -0
  18. package/dist/Carousel/CarouselSlide.cjs +2 -0
  19. package/dist/Carousel/CarouselSlide.cjs.map +1 -0
  20. package/dist/Carousel/CarouselSlide.js +8 -0
  21. package/dist/Carousel/CarouselSlide.js.map +1 -0
  22. package/dist/Input/Input.cjs +1 -1
  23. package/dist/Input/Input.cjs.map +1 -1
  24. package/dist/Input/Input.js +3 -9
  25. package/dist/Input/Input.js.map +1 -1
  26. package/dist/Input/TextArea.cjs +1 -1
  27. package/dist/Input/TextArea.cjs.map +1 -1
  28. package/dist/Input/utils.cjs +1 -1
  29. package/dist/Input/utils.cjs.map +1 -1
  30. package/dist/Input/utils.js +2 -4
  31. package/dist/Input/utils.js.map +1 -1
  32. package/dist/Rating/Rating.cjs +1 -1
  33. package/dist/Rating/Rating.cjs.map +1 -1
  34. package/dist/Transfer/BacktransferButton.cjs +2 -0
  35. package/dist/Transfer/BacktransferButton.cjs.map +1 -0
  36. package/dist/Transfer/BacktransferButton.js +20 -0
  37. package/dist/Transfer/BacktransferButton.js.map +1 -0
  38. package/dist/Transfer/Item.cjs +2 -0
  39. package/dist/Transfer/Item.cjs.map +1 -0
  40. package/dist/Transfer/Item.js +15 -0
  41. package/dist/Transfer/Item.js.map +1 -0
  42. package/dist/Transfer/ResetButton.cjs +2 -0
  43. package/dist/Transfer/ResetButton.cjs.map +1 -0
  44. package/dist/Transfer/ResetButton.js +16 -0
  45. package/dist/Transfer/ResetButton.js.map +1 -0
  46. package/dist/Transfer/SelectAllCheckbox.cjs +2 -0
  47. package/dist/Transfer/SelectAllCheckbox.cjs.map +1 -0
  48. package/dist/Transfer/SelectAllCheckbox.js +11 -0
  49. package/dist/Transfer/SelectAllCheckbox.js.map +1 -0
  50. package/dist/Transfer/Source.cjs +2 -0
  51. package/dist/Transfer/Source.cjs.map +1 -0
  52. package/dist/Transfer/Source.js +61 -0
  53. package/dist/Transfer/Source.js.map +1 -0
  54. package/dist/Transfer/Target.cjs +2 -0
  55. package/dist/Transfer/Target.cjs.map +1 -0
  56. package/dist/Transfer/Target.js +60 -0
  57. package/dist/Transfer/Target.js.map +1 -0
  58. package/dist/Transfer/Transfer.cjs +2 -0
  59. package/dist/Transfer/Transfer.cjs.map +1 -0
  60. package/dist/Transfer/Transfer.js +21 -0
  61. package/dist/Transfer/Transfer.js.map +1 -0
  62. package/dist/Transfer/TransferButton.cjs +2 -0
  63. package/dist/Transfer/TransferButton.cjs.map +1 -0
  64. package/dist/Transfer/TransferButton.js +20 -0
  65. package/dist/Transfer/TransferButton.js.map +1 -0
  66. package/dist/Transfer/useTransferContext.cjs +2 -0
  67. package/dist/Transfer/useTransferContext.cjs.map +1 -0
  68. package/dist/Transfer/useTransferContext.js +63 -0
  69. package/dist/Transfer/useTransferContext.js.map +1 -0
  70. package/dist/Video/Video.cjs +2 -0
  71. package/dist/Video/Video.cjs.map +1 -0
  72. package/dist/Video/Video.js +99 -0
  73. package/dist/Video/Video.js.map +1 -0
  74. package/dist/index.cjs +1 -3
  75. package/dist/index.cjs.map +1 -1
  76. package/dist/index.js +4 -0
  77. package/dist/index.js.map +1 -1
  78. package/dist/types/Autocomplete/Autocomplete.d.ts +10 -5
  79. package/dist/types/Carousel/Carousel.d.ts +7 -0
  80. package/dist/types/Carousel/CarouselSlide.d.ts +2 -0
  81. package/dist/types/Input/Input.d.ts +2 -4
  82. package/dist/types/Input/utils.d.ts +1 -1
  83. package/dist/types/Transfer/BacktransferButton.d.ts +3 -0
  84. package/dist/types/Transfer/Item.d.ts +12 -0
  85. package/dist/types/Transfer/ResetButton.d.ts +3 -0
  86. package/dist/types/Transfer/SelectAllCheckbox.d.ts +10 -0
  87. package/dist/types/Transfer/Source.d.ts +9 -0
  88. package/dist/types/Transfer/Target.d.ts +9 -0
  89. package/dist/types/Transfer/Transfer.d.ts +40 -0
  90. package/dist/types/Transfer/TransferButton.d.ts +3 -0
  91. package/dist/types/Transfer/useTransferContext.d.ts +49 -0
  92. package/dist/types/Video/Video.d.ts +58 -0
  93. package/dist/types/index.d.ts +6 -0
  94. package/package.json +6 -3
  95. package/src/Accordion/AccordionBody.tsx +32 -22
  96. package/src/Accordion/AccordionHeader.tsx +0 -1
  97. package/src/Autocomplete/Autocomplete.tsx +69 -18
  98. package/src/Carousel/Carousel.tsx +210 -0
  99. package/src/Carousel/CarouselSlide.tsx +14 -0
  100. package/src/Input/Input.tsx +10 -19
  101. package/src/Input/utils.tsx +3 -5
  102. package/src/Transfer/BacktransferButton.tsx +33 -0
  103. package/src/Transfer/Item.tsx +33 -0
  104. package/src/Transfer/ResetButton.tsx +28 -0
  105. package/src/Transfer/SelectAllCheckbox.tsx +30 -0
  106. package/src/Transfer/Source.tsx +89 -0
  107. package/src/Transfer/Target.tsx +87 -0
  108. package/src/Transfer/Transfer.tsx +31 -0
  109. package/src/Transfer/TransferButton.tsx +33 -0
  110. package/src/Transfer/useTransferContext.tsx +110 -0
  111. package/src/Video/Video.tsx +205 -0
  112. package/src/index.ts +6 -0
@@ -0,0 +1,210 @@
1
+ import React, { FC } from 'react';
2
+ import classNames from 'classnames';
3
+
4
+ import {Splide, SplideProps} from '@splidejs/react-splide'
5
+
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ const CONFIG_DEFAULT: any = {
8
+ slideFocus: false,
9
+ rewind: true,
10
+ perMove: 1,
11
+ i18n: {
12
+ prev: 'Slide precedente',
13
+ next: 'Slide successiva',
14
+ first: 'Vai alla prima slide',
15
+ last: 'Vai all’ultima slide',
16
+ slideX: 'Vai alla slide %s',
17
+ pageX: 'Vai a pagina %s',
18
+ play: 'Attiva autoplay',
19
+ pause: 'Pausa autoplay',
20
+ carousel: 'Carosello',
21
+ select: 'Seleziona una slide da mostrare',
22
+ slide: 'slide',
23
+ slideLabel: '%s di %s',
24
+ },
25
+ }
26
+
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ const CONFIGS: any= {
29
+ 'landscape-three-cols': {
30
+ type: 'slide',
31
+ perPage: 3,
32
+ gap: 24,
33
+ padding: { left: 0, right: 0 },
34
+ arrows: false,
35
+ breakpoints: {
36
+ 768: {
37
+ perPage: 1,
38
+ gap: 24,
39
+ padding: { left: 0, right: 0 },
40
+ arrows: false,
41
+ },
42
+ 992: {
43
+ perPage: 2,
44
+ gap: 24,
45
+ padding: { left: 40, right: 40 },
46
+ arrows: false,
47
+ },
48
+ },
49
+ },
50
+ 'landscape-three-cols-arrows': {
51
+ type: 'slide',
52
+ perPage: 3,
53
+ gap: 24,
54
+ padding: { left: 0, right: 0 },
55
+ arrows: true,
56
+ breakpoints: {
57
+ 768: {
58
+ perPage: 1,
59
+ gap: 24,
60
+ padding: { left: 40, right: 40 },
61
+ arrows: true,
62
+ },
63
+ 992: {
64
+ perPage: 2,
65
+ gap: 24,
66
+ padding: { left: 40, right: 40 },
67
+ arrows: true,
68
+ },
69
+ },
70
+ },
71
+ 'big-image': {
72
+ type: 'loop',
73
+ perPage: 1,
74
+ gap: 48,
75
+ padding: { left: 320, right: 320 },
76
+ arrows: false,
77
+ breakpoints: {
78
+ 768: {
79
+ perPage: 1,
80
+ gap: 0,
81
+ padding: { left: 0, right: 0 },
82
+ arrows: false,
83
+ },
84
+ 992: {
85
+ perPage: 1,
86
+ gap: 24,
87
+ padding: { left: 160, right: 160 },
88
+ arrows: false,
89
+ },
90
+ },
91
+ },
92
+ 'standard-image': {
93
+ type: 'loop',
94
+ perPage: 3,
95
+ gap: 24,
96
+ padding: { left: 48, right: 48 },
97
+ arrows: false,
98
+ breakpoints: {
99
+ 768: {
100
+ perPage: 1,
101
+ gap: 24,
102
+ padding: { left: 40, right: 40 },
103
+ arrows: false,
104
+ },
105
+ 992: {
106
+ perPage: 2,
107
+ gap: 24,
108
+ padding: { left: 48, right: 48 },
109
+ arrows: false,
110
+ },
111
+ },
112
+ },
113
+ 'landscape': {
114
+ type: 'slide',
115
+ perPage: 1,
116
+ gap: 24,
117
+ padding: { left: 0, right: 0 },
118
+ arrows: false,
119
+ breakpoints: {
120
+ 768: {
121
+ perPage: 1,
122
+ gap: 24,
123
+ padding: { left: 0, right: 0 },
124
+ arrows: false,
125
+ },
126
+ 992: {
127
+ perPage: 1,
128
+ gap: 24,
129
+ padding: { left: 24, right: 24 },
130
+ arrows: false,
131
+ },
132
+ },
133
+ },
134
+ 'calendar-wrapper': {
135
+ type: 'slide',
136
+ perPage: 4,
137
+ gap: 0,
138
+ padding: { left: 0, right: 0 },
139
+ arrows: false,
140
+ breakpoints: {
141
+ 560: {
142
+ perPage: 1,
143
+ gap: 0,
144
+ padding: { left: 24, right: 24 },
145
+ arrows: false,
146
+ },
147
+ 768: {
148
+ perPage: 2,
149
+ gap: 0,
150
+ padding: { left: 0, right: 0 },
151
+ arrows: false,
152
+ },
153
+ 992: {
154
+ perPage: 3,
155
+ gap: 0,
156
+ padding: { left: 0, right: 0 },
157
+ arrows: false,
158
+ },
159
+ },
160
+ },
161
+ }
162
+
163
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
164
+ const EXTRA_CLASSES: any= {
165
+ 'landscape-three-cols': [
166
+ 'it-carousel-landscape-abstract-three-cols'
167
+ ],
168
+ 'landscape-three-cols-arrows': [
169
+ 'it-carousel-landscape-abstract-three-cols-arrow-visible'
170
+ ],
171
+ 'big-image': [
172
+ 'it-carousel-landscape-abstract-three-cols',
173
+ 'it-full-carousel',
174
+ 'it-big-img'
175
+ ],
176
+ 'standard-image': [
177
+ 'it-carousel-landscape-abstract-three-cols',
178
+ 'it-full-carousel',
179
+ 'it-standard-image'
180
+ ],
181
+ 'landscape': [
182
+ 'it-carousel-landscape-abstract'
183
+ ],
184
+ 'calendar-wrapper': [
185
+ 'it-calendar-wrapper'
186
+ ]
187
+ }
188
+
189
+ export interface CarouselProps extends SplideProps {
190
+ type: string;
191
+ children?: React.ReactNode;
192
+ }
193
+
194
+ // Splide wrapper
195
+ export const Carousel: FC<CarouselProps> = ({
196
+ className = '',
197
+ type,
198
+ children,
199
+ ...attributes
200
+ }) => {
201
+ let conf = Object.assign({}, CONFIG_DEFAULT)
202
+ if (['big-image', 'standard-image'].includes(type)){
203
+ conf = Object.assign({}, conf, CONFIGS['landscape-three-cols'])
204
+ }
205
+ conf = Object.assign({}, conf, CONFIGS[type])
206
+ return <Splide
207
+ {...attributes}
208
+ className={classNames('it-carousel-wrapper', className, ...EXTRA_CLASSES[type])}
209
+ options={conf}>{children}</Splide>;
210
+ };
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import {SplideSlide} from '@splidejs/react-splide'
3
+
4
+ // Splide wrapper
5
+ export const CarouselSlide: React.FC<JSX.IntrinsicElements['li']> = ({
6
+ children,
7
+ }) => {
8
+
9
+ return <SplideSlide>
10
+ <div className='it-single-slide-wrapper'>
11
+ {children}
12
+ </div>
13
+ </SplideSlide>;
14
+ };
@@ -62,7 +62,7 @@ export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
62
62
  decrementLabel?: string | ReactNode;
63
63
  /** Testo di esempio da utilizzare per il campo. */
64
64
  placeholder?: string;
65
- /** Testo di validazione per l'elemento del moduleo form. */
65
+ /** Testo di validazione per l'elemento del modulo form. */
66
66
  validationText?: string;
67
67
  /** Testo di aiuto per l'elemento del moduleo form. Richiede che il componente `Input` abbia la prop `id` impostata. */
68
68
  infoText?: string;
@@ -77,8 +77,6 @@ export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
77
77
  innerRef?: Ref<HTMLInputElement>;
78
78
  /** Utilizzare per mostrare testo statico non modificabile. */
79
79
  plaintext?: boolean;
80
- /** Utilizzare per mostrare un elemento addon a fianco (prima o dopo) il campo input all'interno del componente */
81
- addon?: boolean;
82
80
  /** Utilizzare per mostrare un elemento un simbolo attivando la proprietà addon nel campo input all'interno del componente */
83
81
  addonText?: string;
84
82
  /** Oggetto contenente la nuova mappatura per le classi CSS. */
@@ -118,7 +116,6 @@ export const Input = ({
118
116
  cssModule,
119
117
  type = 'text',
120
118
  tag,
121
- addon,
122
119
  addonText,
123
120
  static: staticInput,
124
121
  plaintext,
@@ -173,13 +170,11 @@ export const Input = ({
173
170
  let { bsSize, valid, ...rest } = attributes;
174
171
 
175
172
  const Tag = getTag({ tag, plaintext, staticInput, type });
176
- addon = addonText != null ? true : addon;
177
173
  const formControlClass = getFormControlClass(
178
174
  {
179
175
  plaintext,
180
176
  staticInput,
181
177
  type,
182
- addon,
183
178
  normalized
184
179
  },
185
180
  cssModule
@@ -304,10 +299,8 @@ export const Input = ({
304
299
  };
305
300
 
306
301
  if (['currency', 'percentage', 'adaptive', 'number'].includes(type)) {
307
- if (containerProps.extraLabelClass) {
302
+ if (containerProps.extraLabelClass && ['currency', 'percentage'].includes(type)) {
308
303
  containerProps.extraLabelClass = containerProps.extraLabelClass + ' input-symbol-label';
309
- } else {
310
- containerProps.extraLabelClass = 'input-symbol-label';
311
304
  }
312
305
  return (
313
306
  <InputContainer {...containerProps}>
@@ -363,16 +356,14 @@ export const Input = ({
363
356
 
364
357
  if (indeterminateCheckboxInput) {
365
358
  return (
366
- <InputContainer {...containerProps}>
367
- <Tag
368
- {...rest}
369
- {...extraAttributes}
370
- {...sharedAttributes}
371
- className={inputClasses}
372
- data-testid={testId}
373
- indeterminate={'true'}
374
- />
375
- </InputContainer>
359
+ <Tag
360
+ {...rest}
361
+ {...extraAttributes}
362
+ {...sharedAttributes}
363
+ className={inputClasses}
364
+ data-testid={testId}
365
+ indeterminate={'true'}
366
+ />
376
367
  );
377
368
  }
378
369
 
@@ -8,9 +8,9 @@ type ValidationProps = Pick<InputProps, 'valid'>;
8
8
  type TypeProps = Pick<InputProps, 'plaintext' | 'type'> & {
9
9
  staticInput?: boolean;
10
10
  };
11
- type FormControlProps = Pick<InputProps, 'addon' | 'normalized'> & TypeProps;
11
+ type FormControlProps = Pick<InputProps, 'normalized'> & TypeProps;
12
12
 
13
- function getFormControlClassInternal({ plaintext, staticInput, type = 'text', addon, normalized }: FormControlProps) {
13
+ function getFormControlClassInternal({ plaintext, staticInput, type = 'text', normalized }: FormControlProps) {
14
14
  const formControlClass = 'form-control';
15
15
  if (plaintext || staticInput || normalized) {
16
16
  return `${formControlClass}-plaintext`;
@@ -19,9 +19,7 @@ function getFormControlClassInternal({ plaintext, staticInput, type = 'text', ad
19
19
  return `${formControlClass}-file`;
20
20
  }
21
21
  if (['radio', 'checkbox'].indexOf(type) > -1) {
22
- if (addon) {
23
- return null;
24
- }
22
+ return null;
25
23
  }
26
24
  return formControlClass;
27
25
  }
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { Icon } from '../Icon/Icon';
3
+ import { useTransferContext } from './useTransferContext';
4
+
5
+ const BacktransferButton = () => {
6
+ const { targetCandidates, sourceItems, targetItems, setSourceItems, setTargetItems, setTargetCandidates } =
7
+ useTransferContext();
8
+ const handleTransfer: React.MouseEventHandler<HTMLAnchorElement> = (e) => {
9
+ e.preventDefault();
10
+ const newTargetItems = targetItems.filter((item) => !targetCandidates.includes(item.id));
11
+ const newSourceItems = sourceItems.concat(targetItems.filter((item) => targetCandidates.includes(item.id)));
12
+ setSourceItems({ items: newSourceItems });
13
+ setTargetItems({ items: newTargetItems });
14
+ setTargetCandidates([]);
15
+ };
16
+ return (
17
+ <>
18
+ <a
19
+ className={`backtransfer ${targetCandidates.length > 0 ? 'active' : ''}`}
20
+ href='#'
21
+ role='button'
22
+ aria-label='Sposta indietro'
23
+ onClick={handleTransfer}
24
+ data-testid='backtransfer-button'
25
+ >
26
+ <Icon icon='it-arrow-left' />
27
+ </a>
28
+ <span className='visually-hidden'>Etichetta per freccia sinistra</span>
29
+ </>
30
+ );
31
+ };
32
+
33
+ export { BacktransferButton };
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { FormGroup, Input, Label } from '..';
3
+
4
+ const Item = ({ id, children }: { id: string; children: React.ReactNode }) => {
5
+ return (
6
+ <>
7
+ {id} - {children}
8
+ </>
9
+ );
10
+ };
11
+
12
+ const InternalItem = ({
13
+ id,
14
+ checked,
15
+ children,
16
+ onChange
17
+ }: {
18
+ id: string;
19
+ checked?: boolean;
20
+ children: React.ReactNode;
21
+ onChange?: React.ChangeEventHandler<HTMLInputElement>;
22
+ }) => {
23
+ return (
24
+ <FormGroup check>
25
+ <Input checked={checked} onChange={onChange} id={id} type='checkbox' />
26
+ <Label for={id} check>
27
+ {children}
28
+ </Label>
29
+ </FormGroup>
30
+ );
31
+ };
32
+
33
+ export { InternalItem, Item };
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import { Icon } from '../Icon/Icon';
3
+ import { useTransferContext } from './useTransferContext';
4
+
5
+ const ResetButton = () => {
6
+ const { reset, isChanged } = useTransferContext();
7
+ const handleReset: React.MouseEventHandler<HTMLAnchorElement> = (e) => {
8
+ e.preventDefault();
9
+ reset();
10
+ };
11
+ return (
12
+ <>
13
+ <a
14
+ className={`reset ${isChanged ? 'active' : ''}`}
15
+ href='#'
16
+ role='button'
17
+ aria-label='Reset'
18
+ onClick={handleReset}
19
+ data-testid='reset-button'
20
+ >
21
+ <Icon icon='it-restore' />
22
+ </a>
23
+ <span className='visually-hidden'>Etichetta per icona di reset</span>
24
+ </>
25
+ );
26
+ };
27
+
28
+ export { ResetButton };
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { FormGroup, Input, Label } from '..';
3
+
4
+ const SelectAllCheckbox = ({
5
+ className,
6
+ description,
7
+ checked,
8
+ id,
9
+ onChange,
10
+ children
11
+ }: {
12
+ className?: string;
13
+ id: string;
14
+ checked?: boolean;
15
+ onChange: React.ChangeEventHandler<HTMLInputElement>;
16
+ children: React.ReactNode;
17
+ description?: string;
18
+ }) => {
19
+ return (
20
+ <FormGroup check>
21
+ <Input className={className} checked={checked} onChange={onChange} id={id} type='checkbox' />
22
+ <Label for={id} check>
23
+ {children}
24
+ {description && <span className='descr'>{description}</span>}
25
+ </Label>
26
+ </FormGroup>
27
+ );
28
+ };
29
+
30
+ export { SelectAllCheckbox };
@@ -0,0 +1,89 @@
1
+ import React, { useEffect } from 'react';
2
+ import { InternalItem, Item } from './Item';
3
+ import { SelectAllCheckbox } from './SelectAllCheckbox';
4
+ import { useTransferContext } from './useTransferContext';
5
+
6
+ const SourceItem = ({ id, children }: { id: string; children: React.ReactNode }) => {
7
+ const { sourceCandidates, setSourceCandidates } = useTransferContext();
8
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
9
+ if (e.target.checked) {
10
+ setSourceCandidates([...new Set([...sourceCandidates, e.target.id])]);
11
+ } else {
12
+ setSourceCandidates(sourceCandidates.filter((item) => item !== e.target.id));
13
+ }
14
+ };
15
+ const isChecked = sourceCandidates.includes(id);
16
+ return (
17
+ <InternalItem checked={isChecked} id={id} onChange={handleChange}>
18
+ {children}
19
+ </InternalItem>
20
+ );
21
+ };
22
+
23
+ const Source: React.FC<{ children?: React.ReactNode }> & {
24
+ Header: React.FC<{ children?: React.ReactNode }>;
25
+ } = ({ children }) => {
26
+ const { sourceItems, setSourceItems } = useTransferContext();
27
+ const otherComponents: React.ReactNode[] = [];
28
+ const childrenSource = React.Children.map(children, (child) => {
29
+ if (React.isValidElement(child) && child.type === Item) {
30
+ const { id, children } = child.props;
31
+ return { id, content: children };
32
+ }
33
+ otherComponents.push(child);
34
+ return null;
35
+ })?.filter((item) => item !== null);
36
+
37
+ useEffect(() => {
38
+ if (!childrenSource) return;
39
+ setSourceItems({ items: childrenSource, setOriginals: true });
40
+ }, []);
41
+
42
+ return (
43
+ <div className='it-transfer-wrapper source' data-testid='transfer-source'>
44
+ {otherComponents}
45
+ {sourceItems.map(({ id, content }) => (
46
+ <SourceItem key={id} id={id}>
47
+ {content}
48
+ </SourceItem>
49
+ ))}
50
+
51
+ <br />
52
+ </div>
53
+ );
54
+ };
55
+
56
+ Source.Header = ({ children }: { children?: React.ReactNode }) => {
57
+ const { sourceItems, sourceCandidates, setSourceCandidates } = useTransferContext();
58
+
59
+ const getStatus = () => {
60
+ if (sourceCandidates.length === 0) {
61
+ return { checked: false };
62
+ }
63
+ if (sourceItems.every((item) => sourceCandidates.includes(item.id))) {
64
+ return { checked: true };
65
+ }
66
+ return { checked: false, className: 'semi-checked' };
67
+ };
68
+
69
+ return (
70
+ <div className='transfer-header' data-testid='transfer-source-header'>
71
+ <SelectAllCheckbox
72
+ {...getStatus()}
73
+ onChange={(e) => {
74
+ if (e.target.checked) {
75
+ setSourceCandidates([...new Set(sourceItems.map((item) => item.id))]);
76
+ } else {
77
+ setSourceCandidates([]);
78
+ }
79
+ }}
80
+ id='transfer-source-header'
81
+ description={children ? 'Source' : undefined}
82
+ >
83
+ {children ? children : 'Source'}
84
+ </SelectAllCheckbox>
85
+ </div>
86
+ );
87
+ };
88
+
89
+ export { Source };
@@ -0,0 +1,87 @@
1
+ import React, { useEffect } from 'react';
2
+ import { InternalItem, Item } from './Item';
3
+ import { SelectAllCheckbox } from './SelectAllCheckbox';
4
+ import { useTransferContext } from './useTransferContext';
5
+
6
+ const TargetItem = ({ id, children }: { id: string; children: React.ReactNode }) => {
7
+ const { targetCandidates, setTargetCandidates } = useTransferContext();
8
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
9
+ if (e.target.checked) {
10
+ setTargetCandidates([...new Set([...targetCandidates, e.target.id])]);
11
+ } else {
12
+ setTargetCandidates(targetCandidates.filter((item) => item !== e.target.id));
13
+ }
14
+ };
15
+ const isChecked = targetCandidates.includes(id);
16
+ return (
17
+ <InternalItem checked={isChecked} id={id} onChange={handleChange}>
18
+ {children}
19
+ </InternalItem>
20
+ );
21
+ };
22
+
23
+ const Target: React.FC<{ children?: React.ReactNode }> & {
24
+ Header: React.FC<{ children?: React.ReactNode }>;
25
+ } = ({ children }) => {
26
+ const { targetItems, setTargetItems } = useTransferContext();
27
+ const otherComponents: React.ReactNode[] = [];
28
+ const childrenTarget = React.Children.map(children, (child) => {
29
+ if (React.isValidElement(child) && child.type === Item) {
30
+ const { id, children } = child.props;
31
+ return { id, content: children };
32
+ }
33
+ otherComponents.push(child);
34
+ return null;
35
+ })?.filter((item) => item !== null);
36
+
37
+ useEffect(() => {
38
+ if (!childrenTarget) return;
39
+ setTargetItems({ items: childrenTarget, setOriginals: true });
40
+ }, []);
41
+
42
+ return (
43
+ <div className='it-transfer-wrapper target' data-testid='transfer-target'>
44
+ {otherComponents}
45
+ {targetItems.map(({ id, content }) => (
46
+ <TargetItem key={id} id={id}>
47
+ {content}
48
+ </TargetItem>
49
+ ))}
50
+ </div>
51
+ );
52
+ };
53
+
54
+ Target.Header = ({ children }: { children?: React.ReactNode }) => {
55
+ const { targetItems, targetCandidates, setTargetCandidates } = useTransferContext();
56
+
57
+ const getStatus = () => {
58
+ if (targetCandidates.length === 0) {
59
+ return { checked: false };
60
+ }
61
+ if (targetItems.every((item) => targetCandidates.includes(item.id))) {
62
+ return { checked: true };
63
+ }
64
+ return { checked: false, className: 'semi-checked' };
65
+ };
66
+
67
+ return (
68
+ <div className='transfer-header' data-testid='transfer-target-header'>
69
+ <SelectAllCheckbox
70
+ {...getStatus()}
71
+ onChange={(e) => {
72
+ if (e.target.checked) {
73
+ setTargetCandidates([...new Set(targetItems.map((item) => item.id))]);
74
+ } else {
75
+ setTargetCandidates([]);
76
+ }
77
+ }}
78
+ id='transfer-target-header'
79
+ description={children ? 'Target' : undefined}
80
+ >
81
+ {children ? children : 'Target'}
82
+ </SelectAllCheckbox>
83
+ </div>
84
+ );
85
+ };
86
+
87
+ export { Target };
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { BacktransferButton } from './BacktransferButton';
3
+ import { Item } from './Item';
4
+ import { ResetButton } from './ResetButton';
5
+ import { Source } from './Source';
6
+ import { Target } from './Target';
7
+ import { TransferButton } from './TransferButton';
8
+ import { TransferContextProvider, useTransfer } from './useTransferContext';
9
+
10
+ const Transfer = ({ children }: { children: React.ReactNode }) => {
11
+ return (
12
+ <TransferContextProvider>
13
+ <div data-testid='test-id-transfer'>{children}</div>
14
+ </TransferContextProvider>
15
+ );
16
+ };
17
+
18
+ Transfer.Source = Source;
19
+ Transfer.Target = Target;
20
+
21
+ Transfer.Item = Item;
22
+
23
+ Transfer.TransferButton = TransferButton;
24
+
25
+ Transfer.BacktransferButton = BacktransferButton;
26
+
27
+ Transfer.ResetButton = ResetButton;
28
+
29
+ Transfer.useTransfer = useTransfer;
30
+
31
+ export { Transfer };
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { Icon } from '../Icon/Icon';
3
+ import { useTransferContext } from './useTransferContext';
4
+
5
+ const TransferButton = () => {
6
+ const { sourceCandidates, sourceItems, targetItems, setSourceItems, setTargetItems, setSourceCandidates } =
7
+ useTransferContext();
8
+ const handleTransfer: React.MouseEventHandler<HTMLAnchorElement> = (e) => {
9
+ e.preventDefault();
10
+ const newSourceItems = sourceItems.filter((item) => !sourceCandidates.includes(item.id));
11
+ const newTargetItems = targetItems.concat(sourceItems.filter((item) => sourceCandidates.includes(item.id)));
12
+ setSourceItems({ items: newSourceItems });
13
+ setTargetItems({ items: newTargetItems });
14
+ setSourceCandidates([]);
15
+ };
16
+ return (
17
+ <>
18
+ <a
19
+ data-testid='transfer-button'
20
+ className={`transfer ${sourceCandidates.length > 0 ? 'active' : ''}`}
21
+ href='#'
22
+ role='button'
23
+ aria-label='Sposta avanti'
24
+ onClick={handleTransfer}
25
+ >
26
+ <Icon icon='it-arrow-right' />
27
+ </a>
28
+ <span className='visually-hidden'>Etichetta per freccia destra</span>
29
+ </>
30
+ );
31
+ };
32
+
33
+ export { TransferButton };