design-react-kit 5.4.0 → 5.5.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 (105) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/Autocomplete/Autocomplete.cjs +2 -0
  3. package/dist/Autocomplete/Autocomplete.cjs.map +1 -0
  4. package/dist/Autocomplete/Autocomplete.js +21 -0
  5. package/dist/Autocomplete/Autocomplete.js.map +1 -0
  6. package/dist/BackToTop/BackToTop.cjs +1 -1
  7. package/dist/BackToTop/BackToTop.cjs.map +1 -1
  8. package/dist/BackToTop/BackToTop.js +3 -4
  9. package/dist/BackToTop/BackToTop.js.map +1 -1
  10. package/dist/Collapse/Collapse.cjs +1 -1
  11. package/dist/Collapse/Collapse.cjs.map +1 -1
  12. package/dist/Collapse/Collapse.js +14 -9
  13. package/dist/Collapse/Collapse.js.map +1 -1
  14. package/dist/Dropdown/Dropdown.cjs +1 -1
  15. package/dist/Dropdown/Dropdown.cjs.map +1 -1
  16. package/dist/Dropdown/Dropdown.js +3 -3
  17. package/dist/Dropdown/Dropdown.js.map +1 -1
  18. package/dist/Dropdown/DropdownMenu.cjs +1 -1
  19. package/dist/Dropdown/DropdownMenu.cjs.map +1 -1
  20. package/dist/Dropdown/DropdownMenu.js +3 -6
  21. package/dist/Dropdown/DropdownMenu.js.map +1 -1
  22. package/dist/Dropdown/DropdownToggle.cjs +1 -1
  23. package/dist/Dropdown/DropdownToggle.cjs.map +1 -1
  24. package/dist/Dropdown/DropdownToggle.js +1 -2
  25. package/dist/Dropdown/DropdownToggle.js.map +1 -1
  26. package/dist/Header/HeaderContent.cjs +1 -1
  27. package/dist/Header/HeaderContent.cjs.map +1 -1
  28. package/dist/Header/HeaderContent.js +1 -1
  29. package/dist/Header/HeaderContent.js.map +1 -1
  30. package/dist/Header/HeaderToggler.cjs +1 -1
  31. package/dist/Header/HeaderToggler.cjs.map +1 -1
  32. package/dist/Header/HeaderToggler.js +3 -2
  33. package/dist/Header/HeaderToggler.js.map +1 -1
  34. package/dist/Input/Input.cjs +1 -1
  35. package/dist/Input/Input.cjs.map +1 -1
  36. package/dist/Input/TextArea.cjs +1 -1
  37. package/dist/Input/TextArea.cjs.map +1 -1
  38. package/dist/Input/TextArea.js +4 -1
  39. package/dist/Input/TextArea.js.map +1 -1
  40. package/dist/Input/utils.cjs +1 -1
  41. package/dist/Input/utils.cjs.map +1 -1
  42. package/dist/Input/utils.js +2 -2
  43. package/dist/Input/utils.js.map +1 -1
  44. package/dist/Megamenu/MegamenuItem.cjs +1 -1
  45. package/dist/Megamenu/MegamenuItem.cjs.map +1 -1
  46. package/dist/Megamenu/MegamenuItem.js +4 -2
  47. package/dist/Megamenu/MegamenuItem.js.map +1 -1
  48. package/dist/NavScroll/index.cjs +1 -1
  49. package/dist/NavScroll/index.cjs.map +1 -1
  50. package/dist/NavScroll/types.cjs +1 -1
  51. package/dist/NavScroll/types.cjs.map +1 -1
  52. package/dist/NavScroll/useNavScroll.cjs +1 -1
  53. package/dist/NavScroll/useNavScroll.cjs.map +1 -1
  54. package/dist/NavScroll/useNavScroll.js +84 -28
  55. package/dist/NavScroll/useNavScroll.js.map +1 -1
  56. package/dist/Rating/Rating.cjs +1 -1
  57. package/dist/Rating/Rating.cjs.map +1 -1
  58. package/dist/Rating/Rating.js +1 -1
  59. package/dist/Rating/Rating.js.map +1 -1
  60. package/dist/Skiplink/Skiplink.cjs +1 -1
  61. package/dist/Skiplink/Skiplink.cjs.map +1 -1
  62. package/dist/Skiplink/Skiplink.js +9 -3
  63. package/dist/Skiplink/Skiplink.js.map +1 -1
  64. package/dist/Skiplink/SkiplinkItem.cjs +1 -1
  65. package/dist/Skiplink/SkiplinkItem.cjs.map +1 -1
  66. package/dist/Skiplink/SkiplinkItem.js +8 -2
  67. package/dist/Skiplink/SkiplinkItem.js.map +1 -1
  68. package/dist/index.cjs +1 -1
  69. package/dist/index.cjs.map +1 -1
  70. package/dist/index.js +2 -0
  71. package/dist/index.js.map +1 -1
  72. package/dist/track-focus.cjs +2 -0
  73. package/dist/track-focus.cjs.map +1 -0
  74. package/dist/track-focus.js +2 -0
  75. package/dist/track-focus.js.map +1 -0
  76. package/dist/types/Autocomplete/Autocomplete.d.ts +32 -0
  77. package/dist/types/BackToTop/BackToTop.d.ts +10 -6
  78. package/dist/types/Collapse/Collapse.d.ts +2 -0
  79. package/dist/types/Dropdown/Dropdown.d.ts +3 -2
  80. package/dist/types/Dropdown/DropdownMenu.d.ts +1 -0
  81. package/dist/types/Header/HeaderToggler.d.ts +2 -1
  82. package/dist/types/Input/TextArea.d.ts +1 -1
  83. package/dist/types/NavScroll/types.d.ts +4 -0
  84. package/dist/types/Skiplink/Skiplink.d.ts +3 -0
  85. package/dist/types/Skiplink/SkiplinkItem.d.ts +2 -0
  86. package/dist/types/index.d.ts +2 -0
  87. package/package.json +25 -16
  88. package/src/Autocomplete/Autocomplete.tsx +80 -0
  89. package/src/BackToTop/BackToTop.tsx +14 -11
  90. package/src/Collapse/Collapse.tsx +20 -7
  91. package/src/Dropdown/Dropdown.tsx +9 -6
  92. package/src/Dropdown/DropdownMenu.tsx +10 -6
  93. package/src/Dropdown/DropdownToggle.tsx +1 -2
  94. package/src/Header/HeaderContent.tsx +1 -1
  95. package/src/Header/HeaderToggler.tsx +4 -2
  96. package/src/Input/TextArea.tsx +9 -3
  97. package/src/Input/utils.tsx +2 -2
  98. package/src/Megamenu/MegamenuItem.tsx +4 -2
  99. package/src/NavScroll/types.ts +4 -0
  100. package/src/NavScroll/useNavScroll.ts +102 -34
  101. package/src/Rating/Rating.tsx +1 -1
  102. package/src/Skiplink/Skiplink.tsx +16 -3
  103. package/src/Skiplink/SkiplinkItem.tsx +12 -1
  104. package/src/index.ts +3 -0
  105. package/src/track-focus.js +49 -0
@@ -15,12 +15,13 @@ export interface HeaderTogglerProps extends ButtonHTMLAttributes<HTMLButtonEleme
15
15
  * Se fornito questo sovrascriverà il valore di default.
16
16
  */
17
17
  type?: 'button' | 'submit' | 'reset';
18
+ isOpen?: boolean;
18
19
  testId?: string;
19
20
  }
20
21
 
21
22
  const BUTTON = 'button';
22
23
 
23
- export const HeaderToggler = ({ className, tag, type, testId, ...attributes }: HeaderTogglerProps) => {
24
+ export const HeaderToggler = ({ className, tag, type, isOpen=false, testId, ...attributes }: HeaderTogglerProps) => {
24
25
  const HeaderType = useHeaderContext();
25
26
  const defaultTag = HeaderType === SLIM ? 'a' : BUTTON;
26
27
  const defaultType = HeaderType === SLIM ? undefined : BUTTON;
@@ -31,6 +32,7 @@ export const HeaderToggler = ({ className, tag, type, testId, ...attributes }: H
31
32
  },
32
33
  className
33
34
  );
35
+ const expanded = isOpen ? "true" : "false"
34
36
  useEffect(() => {
35
37
  document.querySelectorAll('.container-fluid').forEach((element) => {
36
38
  element.classList.remove('container-fluid');
@@ -42,8 +44,8 @@ export const HeaderToggler = ({ className, tag, type, testId, ...attributes }: H
42
44
  {...attributes}
43
45
  tag={tag || defaultTag}
44
46
  type={type || defaultType}
45
- href='#'
46
47
  data-testid={testId}
48
+ aria-expanded={expanded}
47
49
  />
48
50
  );
49
51
  };
@@ -1,8 +1,8 @@
1
- import React, { Ref, ReactNode, TextareaHTMLAttributes } from 'react';
1
+ import React, { ReactNode, Ref, TextareaHTMLAttributes } from 'react';
2
2
 
3
- import { InputContainer } from './InputContainer';
4
- import { getClasses, getValidationTextControlClass, useFocus } from './utils';
5
3
  import type { CSSModule } from 'reactstrap/types/lib/utils';
4
+ import { InputContainer } from './InputContainer';
5
+ import { getClasses, getFormControlClass, getValidationTextControlClass, useFocus } from './utils';
6
6
 
7
7
  export interface TextAreaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
8
8
  /** Etichetta del campo TextArea. */
@@ -56,6 +56,11 @@ export const TextArea = ({
56
56
 
57
57
  const extraAttributes: { ['aria-describedby']?: string } = {};
58
58
 
59
+ //Chiamo questa funzione per impostare classNames a 'form-control'
60
+ const formControlClass = getFormControlClass(
61
+ {},
62
+ cssModule
63
+ );
59
64
  // associate the input field with the help text
60
65
  const infoId = id ? `${id}Description` : undefined;
61
66
  if (id) {
@@ -73,6 +78,7 @@ export const TextArea = ({
73
78
  label,
74
79
  validationText,
75
80
  normalized: Boolean(normalized),
81
+ formControlClass,
76
82
  validationTextControlClass,
77
83
  isFocused,
78
84
  originalWrapperClass
@@ -1,5 +1,5 @@
1
1
  import classNames from 'classnames';
2
- import { useState, useCallback } from 'react';
2
+ import { useCallback, useState } from 'react';
3
3
  import type { CSSModule } from 'reactstrap/types/lib/utils';
4
4
  import { mapToCssModules } from '../utils';
5
5
  import type { InputProps } from './Input';
@@ -98,7 +98,7 @@ export function getClasses(
98
98
  ),
99
99
  cssModule
100
100
  );
101
- const wrapperClass = mapToCssModules(classNames(className, originalWrapperClass, 'form-group'), cssModule);
101
+ const wrapperClass = mapToCssModules(classNames(originalWrapperClass, 'form-group'), cssModule);
102
102
  const validationTextClass = mapToCssModules(
103
103
  classNames(
104
104
  {
@@ -13,10 +13,12 @@ export interface MegamenuItemProps extends HTMLAttributes<HTMLUListElement> {
13
13
 
14
14
  export const MegamenuItem: FC<MegamenuItemProps> = ({ itemName, className, children, ...attributes }) => {
15
15
  const classes = classNames(className, 'megamenu');
16
- const toggleClasses = classNames('px-lg-2', 'px-xl-3');
16
+ const [dropdownOpen, setDropdownOpen] = React.useState(false);
17
+ const toggle = () => setDropdownOpen((prevState) => !prevState);
18
+ const toggleClasses = classNames('px-lg-2', 'px-xl-3', dropdownOpen ? 'show' : '');
17
19
 
18
20
  return (
19
- <Dropdown className={classes} inNavbar {...attributes}>
21
+ <Dropdown className={classes} inNavbar {...attributes} isOpen={dropdownOpen} toggle={toggle}>
20
22
  <DropdownToggle inNavbar caret className={toggleClasses}>
21
23
  <span>{itemName}</span>
22
24
  </DropdownToggle>
@@ -86,6 +86,10 @@ export type useNavScrollResult = {
86
86
  * A function to retrieve the reference of the current active element (only the last element, not the elements hierarchy).
87
87
  */
88
88
  getActiveRef: () => RefObject<Element> | null;
89
+ /**
90
+ * A list of active ids (the full hierarchy).
91
+ */
92
+ percentage: number;
89
93
  };
90
94
 
91
95
  // @private
@@ -1,15 +1,81 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /*
2
+ /*--------------------------------------------------------------------------
3
3
  * This work derives from the React Use Navscroll library
4
- * Released under the MIT license by Marco Liberati
4
+ * Released under the MIT license by Marco Liberati (@dej611)
5
5
  * Code: https://github.com/dej611/react-use-navscroll
6
+ * --------------------------------------------------------------------------
7
+ * Parts of this code has been modified using Bootstrap Italia source code
8
+ * --------------------------------------------------------------------------
9
+ * Bootstrap Italia (https://italia.github.io/bootstrap-italia/)
10
+ * Authors: https://github.com/italia/bootstrap-italia/blob/main/AUTHORS
11
+ * License: BSD-3-Clause (https://github.com/italia/bootstrap-italia/blob/main/LICENSE)
12
+ * --------------------------------------------------------------------------
6
13
  */
7
14
 
8
15
  import { createRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
9
16
  import { debounce } from './debounce';
10
- import type { TrackedElement, useNavScrollArgs, useNavScrollResult } from './types';
17
+ import type { useNavScrollArgs, useNavScrollResult, TrackedElement } from './types';
11
18
  import { useSizeDetector } from './useSizeDetector';
12
19
 
20
+ import { v4 as uuidv4 } from 'uuid';
21
+
22
+ let ticking: boolean = false;
23
+ let callbacks: any[] = [];
24
+
25
+ class ScrollCallback {
26
+ private _callback: any;
27
+ id: string;
28
+ constructor(id: string, callback: any) {
29
+ this.id = id;
30
+ this._callback = callback;
31
+ }
32
+
33
+ //Public
34
+ dispose() {
35
+ removeCallBack(this.id);
36
+ }
37
+
38
+ //Private
39
+ _execute(data: any) {
40
+ this._callback(data);
41
+ }
42
+ }
43
+
44
+ const removeCallBack = (id: string) => {
45
+ callbacks = callbacks.filter((cb) => cb.id !== id);
46
+ };
47
+
48
+ const onDocumentScroll = (callback: any) => {
49
+ if (typeof document === 'undefined') {
50
+ return;
51
+ }
52
+ if (!callbacks.length) {
53
+ if (typeof window !== 'undefined' && typeof document !== 'undefined') {
54
+ document.addEventListener('scroll', (evt) => {
55
+ if (!ticking) {
56
+ window.requestAnimationFrame(() => {
57
+ callbacks.forEach((cbObj) => cbObj.cb._execute(evt));
58
+ ticking = false;
59
+ });
60
+ ticking = true;
61
+ }
62
+ });
63
+ }
64
+ }
65
+
66
+ if (typeof callback === 'function') {
67
+ const newCb = new ScrollCallback(uuidv4(), callback);
68
+ callbacks.push({
69
+ id: newCb.id,
70
+ cb: newCb
71
+ });
72
+ return newCb;
73
+ }
74
+
75
+ console.error('[onDocumentScroll] the provided data has to be of type function');
76
+ return null;
77
+ };
78
+
13
79
  const hasWindow = typeof window !== 'undefined';
14
80
  const REGISTER_DELAY = 50;
15
81
 
@@ -34,6 +100,7 @@ export function useNavScroll(args: useNavScrollArgs = {}): useNavScrollResult {
34
100
  const [counter, setCounter] = useState(0);
35
101
  const [forceRecompute, setForceRecompute] = useState(false);
36
102
  const [activeId, updateActiveId] = useState<string | null>(null);
103
+ const [percentageValue, setPercentageValue] = useState(0);
37
104
 
38
105
  const { targetSize, useViewport } = useSizeDetector({
39
106
  root,
@@ -63,7 +130,7 @@ export function useNavScroll(args: useNavScrollArgs = {}): useNavScrollResult {
63
130
  lookup[id] = parent;
64
131
  }
65
132
  return lookup;
66
- }, []);
133
+ }, [counter]);
67
134
  const activeIds = useMemo(() => (activeId ? resolveHierarchyIds(activeId, elsLookup) : []), [activeId, elsLookup]);
68
135
 
69
136
  const activeLookups = useMemo(() => new Set(activeIds), [activeIds]);
@@ -71,17 +138,23 @@ export function useNavScroll(args: useNavScrollArgs = {}): useNavScrollResult {
71
138
  if (!hasWindow) {
72
139
  return;
73
140
  }
74
- const handleIntersection: IntersectionObserverCallback = (entries) => {
141
+ const _onScroll = () => {
75
142
  let intersectionId = null;
76
- let topMin = Infinity;
77
- entries.forEach((entry) => {
78
- if (entry.isIntersecting) {
79
- if (topMin > entry.boundingClientRect.top) {
80
- topMin = entry.boundingClientRect.top;
81
- intersectionId = entry.target.id;
82
- }
143
+ for (let k = 0; k < els.current.length; k++) {
144
+ const entry = els.current[k].ref.current;
145
+ const min = entry?.getBoundingClientRect().top ? entry?.getBoundingClientRect().top : 0;
146
+ if (!min) {
147
+ break;
83
148
  }
84
- });
149
+ if (min > 0 && k > 0) {
150
+ const totEls =
151
+ root?.previousSibling?.firstChild?.parentNode?.querySelectorAll('.it-navscroll-wrapper .nav-link').length ||
152
+ 1;
153
+ setPercentageValue((k / (totEls / 2)) * 100);
154
+ intersectionId = els.current[k - 1].ref.current?.id;
155
+ break;
156
+ }
157
+ }
85
158
  if (intersectionId != null) {
86
159
  updateActiveId(intersectionId);
87
160
  if (onChange) {
@@ -94,21 +167,11 @@ export function useNavScroll(args: useNavScrollArgs = {}): useNavScrollResult {
94
167
  }
95
168
  };
96
169
 
97
- const observer = new IntersectionObserver(handleIntersection, observerOptions);
98
-
99
- els.current.forEach(({ ref }) => {
100
- if (ref && ref.current) {
101
- observer.observe(ref.current);
102
- }
103
- });
170
+ onDocumentScroll(_onScroll);
104
171
 
105
- if (forceRecompute) {
106
- handleIntersection(observer.takeRecords(), observer);
107
- setForceRecompute(false);
108
- }
109
- return () => {
110
- observer.disconnect();
111
- };
172
+ setTimeout(() => {
173
+ _onScroll();
174
+ }, 300);
112
175
  }, [
113
176
  activeIds,
114
177
  updateActiveId,
@@ -131,28 +194,32 @@ export function useNavScroll(args: useNavScrollArgs = {}): useNavScrollResult {
131
194
  );
132
195
 
133
196
  const register = useCallback(
134
- (id: string, options = {}) => {
197
+ (id: string, options: any = {}) => {
135
198
  if (!hasWindow) {
136
199
  return { id, ref: null };
137
200
  }
138
201
  const alreadyRegistered = id in elsLookup;
139
- const entry = (alreadyRegistered ? els.current.find(({ id: existingId }) => existingId === id) : options) as any;
202
+ const entry = alreadyRegistered ? els.current.find(({ id: existingId }) => existingId === id) : options;
140
203
  const ref = (entry && entry.ref) || createRef();
141
204
 
142
205
  if (!alreadyRegistered) {
143
- els.current = [...els.current, { id, ref, parent: (options as any).parent }];
206
+ els.current = [...els.current, { id, ref, parent: options.parent }];
144
207
  refresh();
145
208
  }
146
209
  return { id, ref };
147
210
  },
148
- [elsLookup, refresh]
211
+ [counter]
149
212
  );
150
213
 
151
- const unregister = useCallback((idToUnregister: string) => {
152
- els.current = els.current.filter(({ id }) => id !== idToUnregister);
153
- }, []);
214
+ const unregister = useCallback(
215
+ (idToUnregister: string) => {
216
+ els.current = els.current.filter(({ id }) => id !== idToUnregister);
217
+ },
218
+ [counter]
219
+ );
154
220
 
155
221
  const isActive = useCallback((id: string) => activeLookups.has(id), [activeLookups]);
222
+ const percentage = useMemo(() => percentageValue, [percentageValue]);
156
223
 
157
224
  const getActiveRef = useCallback(() => {
158
225
  const entry = els.current.find(({ id }) => id === activeId);
@@ -160,6 +227,7 @@ export function useNavScroll(args: useNavScrollArgs = {}): useNavScrollResult {
160
227
  }, [activeId]);
161
228
 
162
229
  return {
230
+ percentage,
163
231
  register,
164
232
  unregister,
165
233
  activeIds,
@@ -119,7 +119,7 @@ export const Rating: FC<RatingProps> = ({
119
119
  {...extraFieldAttrs}
120
120
  />
121
121
  <Label className='full' for={id}>
122
- <Icon icon='it-star-full' size='sm' />
122
+ <Icon icon='it-star-full' size='sm' aria-hidden='true' />
123
123
  <span className='visually-hidden'>{labelFn(currentValue)}</span>
124
124
  </Label>
125
125
  </Fragment>
@@ -2,16 +2,29 @@ import React, { FC, HTMLAttributes, ElementType } from 'react';
2
2
  import classNames from 'classnames';
3
3
 
4
4
  export interface SkiplinkProps extends HTMLAttributes<HTMLElement> {
5
+ ariaLabel?: string;
5
6
  /** Utilizzarlo in caso di utilizzo di componenti personalizzati */
6
7
  tag?: ElementType;
8
+ /** Utilizzare il componente in modalità `nav` */
9
+ nav?: boolean;
7
10
  /** Classi aggiuntive da usare per il componente Skiplink */
8
11
  className?: string;
9
12
  testId?: string;
10
13
  }
11
14
 
12
- export const Skiplink: FC<SkiplinkProps> = ({ className, tag = 'div', testId, ...attributes }) => {
13
- const Tag = tag;
15
+ export const Skiplink: FC<SkiplinkProps> = ({ ariaLabel=null, className, tag = 'div', nav=false, testId, children, ...attributes }) => {
16
+ const Tag = nav ? 'nav' : tag;
14
17
  const classes = classNames(className, 'skiplinks');
18
+ if (nav) {
19
+ return (
20
+ <Tag aria-label={ariaLabel} className={classes} {...attributes} data-testid={testId} >
21
+ <ul>
22
+ {children}
23
+ </ul>
24
+ </Tag>
25
+ )
15
26
 
16
- return <Tag className={classes} {...attributes} data-testid={testId} />;
27
+ } else {
28
+ return <Tag aria-label={ariaLabel} className={classes} {...attributes} data-testid={testId} />;
29
+ }
17
30
  };
@@ -4,6 +4,8 @@ import classNames from 'classnames';
4
4
  export interface SkiplinkItemProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
5
5
  /** Utilizzarlo in caso di utilizzo di componenti personalizzati */
6
6
  tag?: ElementType;
7
+ /** Utilizzare il componente come elemento di uno Skiplink in modalità `nav` */
8
+ navItem?: boolean;
7
9
  /** Classi aggiuntive da usare per il componente Skiplink */
8
10
  className?: string;
9
11
  /** Abilitare questo attributo per renderizzare lo SkipLinkItem al focus */
@@ -15,6 +17,7 @@ export const SkiplinkItem: FC<SkiplinkItemProps> = ({
15
17
  className,
16
18
  tag = 'a',
17
19
  focusable = true,
20
+ navItem = false,
18
21
  testId,
19
22
  ...attributes
20
23
  }) => {
@@ -26,5 +29,13 @@ export const SkiplinkItem: FC<SkiplinkItemProps> = ({
26
29
  // Add an extra href for focusable if the user passes an onClick rather than href prop
27
30
  const extraHref = attributes.onClick ? { href: '#' } : {};
28
31
 
29
- return <Tag className={classes} {...attributes} {...extraHref} data-testid={testId} />;
32
+ if (navItem) {
33
+ return (
34
+ <li className={classes} >
35
+ <Tag {...attributes} {...extraHref} data-testid={testId} />
36
+ </li>
37
+ );
38
+ } else {
39
+ return <Tag className={classes} {...attributes} {...extraHref} data-testid={testId} />;
40
+ }
30
41
  };
package/src/index.ts CHANGED
@@ -47,6 +47,7 @@ export {
47
47
  Util
48
48
  } from 'reactstrap';
49
49
 
50
+ export { Autocomplete } from './Autocomplete/Autocomplete';
50
51
  export { Accordion } from './Accordion/Accordion';
51
52
  export { AccordionBody } from './Accordion/AccordionBody';
52
53
  export { AccordionHeader } from './Accordion/AccordionHeader';
@@ -297,3 +298,5 @@ export type {
297
298
  UncontrolledCollapseProps,
298
299
  UncontrolledTooltipProps
299
300
  } from 'reactstrap';
301
+
302
+ import "./track-focus.js"
@@ -0,0 +1,49 @@
1
+
2
+ // Focus Management
3
+
4
+ /**
5
+ * --------------------------------------------------------------------------
6
+ * Bootstrap Italia (https://italia.github.io/bootstrap-italia/)
7
+ * Authors: https://github.com/italia/bootstrap-italia/blob/main/AUTHORS
8
+ * Licensed under BSD-3-Clause license (https://github.com/italia/bootstrap-italia/blob/main/LICENSE)
9
+ * --------------------------------------------------------------------------
10
+ */
11
+
12
+ const DATA_MOUSE_FOCUS = 'data-focus-mouse'
13
+ const CLASS_NAME_MOUSE_FOCUS = 'focus--mouse'
14
+
15
+ class TrackFocus {
16
+ constructor() {
17
+ this._usingMouse = false
18
+
19
+ this._bindEvents()
20
+ }
21
+
22
+ _bindEvents() {
23
+ if (typeof document === 'undefined') {
24
+ return
25
+ }
26
+ const events = ['keydown', 'mousedown']
27
+ events.forEach((evtName) => {
28
+ document.addEventListener(evtName, (evt) => {
29
+ this._usingMouse = evt.type === 'mousedown'
30
+ })
31
+ })
32
+ document.addEventListener('focusin', (evt) => {
33
+ if (this._usingMouse) {
34
+ if (evt.target) {
35
+ evt.target.classList.add(CLASS_NAME_MOUSE_FOCUS);
36
+ evt.target.setAttribute(DATA_MOUSE_FOCUS, 'true')
37
+ }
38
+ }
39
+ })
40
+ document.addEventListener('focusout', (evt) => {
41
+ if (evt.target) {
42
+ evt.target.classList.remove(CLASS_NAME_MOUSE_FOCUS);
43
+ evt.target.setAttribute(DATA_MOUSE_FOCUS, 'false')
44
+ }
45
+ })
46
+ }
47
+ }
48
+
49
+ new TrackFocus()