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.
- package/CHANGELOG.md +30 -0
- package/dist/Autocomplete/Autocomplete.cjs +2 -0
- package/dist/Autocomplete/Autocomplete.cjs.map +1 -0
- package/dist/Autocomplete/Autocomplete.js +21 -0
- package/dist/Autocomplete/Autocomplete.js.map +1 -0
- package/dist/BackToTop/BackToTop.cjs +1 -1
- package/dist/BackToTop/BackToTop.cjs.map +1 -1
- package/dist/BackToTop/BackToTop.js +3 -4
- package/dist/BackToTop/BackToTop.js.map +1 -1
- package/dist/Collapse/Collapse.cjs +1 -1
- package/dist/Collapse/Collapse.cjs.map +1 -1
- package/dist/Collapse/Collapse.js +14 -9
- package/dist/Collapse/Collapse.js.map +1 -1
- package/dist/Dropdown/Dropdown.cjs +1 -1
- package/dist/Dropdown/Dropdown.cjs.map +1 -1
- package/dist/Dropdown/Dropdown.js +3 -3
- package/dist/Dropdown/Dropdown.js.map +1 -1
- package/dist/Dropdown/DropdownMenu.cjs +1 -1
- package/dist/Dropdown/DropdownMenu.cjs.map +1 -1
- package/dist/Dropdown/DropdownMenu.js +3 -6
- package/dist/Dropdown/DropdownMenu.js.map +1 -1
- package/dist/Dropdown/DropdownToggle.cjs +1 -1
- package/dist/Dropdown/DropdownToggle.cjs.map +1 -1
- package/dist/Dropdown/DropdownToggle.js +1 -2
- package/dist/Dropdown/DropdownToggle.js.map +1 -1
- package/dist/Header/HeaderContent.cjs +1 -1
- package/dist/Header/HeaderContent.cjs.map +1 -1
- package/dist/Header/HeaderContent.js +1 -1
- package/dist/Header/HeaderContent.js.map +1 -1
- package/dist/Header/HeaderToggler.cjs +1 -1
- package/dist/Header/HeaderToggler.cjs.map +1 -1
- package/dist/Header/HeaderToggler.js +3 -2
- package/dist/Header/HeaderToggler.js.map +1 -1
- package/dist/Input/Input.cjs +1 -1
- package/dist/Input/Input.cjs.map +1 -1
- package/dist/Input/TextArea.cjs +1 -1
- package/dist/Input/TextArea.cjs.map +1 -1
- package/dist/Input/TextArea.js +4 -1
- package/dist/Input/TextArea.js.map +1 -1
- package/dist/Input/utils.cjs +1 -1
- package/dist/Input/utils.cjs.map +1 -1
- package/dist/Input/utils.js +2 -2
- package/dist/Input/utils.js.map +1 -1
- package/dist/Megamenu/MegamenuItem.cjs +1 -1
- package/dist/Megamenu/MegamenuItem.cjs.map +1 -1
- package/dist/Megamenu/MegamenuItem.js +4 -2
- package/dist/Megamenu/MegamenuItem.js.map +1 -1
- package/dist/NavScroll/index.cjs +1 -1
- package/dist/NavScroll/index.cjs.map +1 -1
- package/dist/NavScroll/types.cjs +1 -1
- package/dist/NavScroll/types.cjs.map +1 -1
- package/dist/NavScroll/useNavScroll.cjs +1 -1
- package/dist/NavScroll/useNavScroll.cjs.map +1 -1
- package/dist/NavScroll/useNavScroll.js +84 -28
- package/dist/NavScroll/useNavScroll.js.map +1 -1
- package/dist/Rating/Rating.cjs +1 -1
- package/dist/Rating/Rating.cjs.map +1 -1
- package/dist/Rating/Rating.js +1 -1
- package/dist/Rating/Rating.js.map +1 -1
- package/dist/Skiplink/Skiplink.cjs +1 -1
- package/dist/Skiplink/Skiplink.cjs.map +1 -1
- package/dist/Skiplink/Skiplink.js +9 -3
- package/dist/Skiplink/Skiplink.js.map +1 -1
- package/dist/Skiplink/SkiplinkItem.cjs +1 -1
- package/dist/Skiplink/SkiplinkItem.cjs.map +1 -1
- package/dist/Skiplink/SkiplinkItem.js +8 -2
- package/dist/Skiplink/SkiplinkItem.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/track-focus.cjs +2 -0
- package/dist/track-focus.cjs.map +1 -0
- package/dist/track-focus.js +2 -0
- package/dist/track-focus.js.map +1 -0
- package/dist/types/Autocomplete/Autocomplete.d.ts +32 -0
- package/dist/types/BackToTop/BackToTop.d.ts +10 -6
- package/dist/types/Collapse/Collapse.d.ts +2 -0
- package/dist/types/Dropdown/Dropdown.d.ts +3 -2
- package/dist/types/Dropdown/DropdownMenu.d.ts +1 -0
- package/dist/types/Header/HeaderToggler.d.ts +2 -1
- package/dist/types/Input/TextArea.d.ts +1 -1
- package/dist/types/NavScroll/types.d.ts +4 -0
- package/dist/types/Skiplink/Skiplink.d.ts +3 -0
- package/dist/types/Skiplink/SkiplinkItem.d.ts +2 -0
- package/dist/types/index.d.ts +2 -0
- package/package.json +25 -16
- package/src/Autocomplete/Autocomplete.tsx +80 -0
- package/src/BackToTop/BackToTop.tsx +14 -11
- package/src/Collapse/Collapse.tsx +20 -7
- package/src/Dropdown/Dropdown.tsx +9 -6
- package/src/Dropdown/DropdownMenu.tsx +10 -6
- package/src/Dropdown/DropdownToggle.tsx +1 -2
- package/src/Header/HeaderContent.tsx +1 -1
- package/src/Header/HeaderToggler.tsx +4 -2
- package/src/Input/TextArea.tsx +9 -3
- package/src/Input/utils.tsx +2 -2
- package/src/Megamenu/MegamenuItem.tsx +4 -2
- package/src/NavScroll/types.ts +4 -0
- package/src/NavScroll/useNavScroll.ts +102 -34
- package/src/Rating/Rating.tsx +1 -1
- package/src/Skiplink/Skiplink.tsx +16 -3
- package/src/Skiplink/SkiplinkItem.tsx +12 -1
- package/src/index.ts +3 -0
- 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
|
};
|
package/src/Input/TextArea.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import 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
|
package/src/Input/utils.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import {
|
|
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(
|
|
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
|
|
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>
|
package/src/NavScroll/types.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
141
|
+
const _onScroll = () => {
|
|
75
142
|
let intersectionId = null;
|
|
76
|
-
let
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
els.current.forEach(({ ref }) => {
|
|
100
|
-
if (ref && ref.current) {
|
|
101
|
-
observer.observe(ref.current);
|
|
102
|
-
}
|
|
103
|
-
});
|
|
170
|
+
onDocumentScroll(_onScroll);
|
|
104
171
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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 =
|
|
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:
|
|
206
|
+
els.current = [...els.current, { id, ref, parent: options.parent }];
|
|
144
207
|
refresh();
|
|
145
208
|
}
|
|
146
209
|
return { id, ref };
|
|
147
210
|
},
|
|
148
|
-
[
|
|
211
|
+
[counter]
|
|
149
212
|
);
|
|
150
213
|
|
|
151
|
-
const unregister = useCallback(
|
|
152
|
-
|
|
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,
|
package/src/Rating/Rating.tsx
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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()
|