@aarhus-university/au-lib-react-components 10.21.1 → 10.22.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/.storybook/preview.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"sideEffects": false,
|
|
3
3
|
"name": "@aarhus-university/au-lib-react-components",
|
|
4
|
-
"version": "10.
|
|
4
|
+
"version": "10.22.0",
|
|
5
5
|
"description": "Library for shared React components for various applications on au.dk",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "jest",
|
|
@@ -72,9 +72,9 @@
|
|
|
72
72
|
"webpack-cli": "^4.9.2"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@aarhus-university/au-designsystem-delphinus": "0.
|
|
75
|
+
"@aarhus-university/au-designsystem-delphinus": "0.34.2",
|
|
76
76
|
"@aarhus-university/au-designsystem-delphinus-dev": "0.2.0",
|
|
77
|
-
"@aarhus-university/types": "0.
|
|
77
|
+
"@aarhus-university/types": "^0.15.0",
|
|
78
78
|
"@reduxjs/toolkit": "^1.8.3",
|
|
79
79
|
"@types/google.analytics": "^0.0.42",
|
|
80
80
|
"@types/history": "^5.0.0",
|
|
@@ -3,28 +3,33 @@ import React, {
|
|
|
3
3
|
useRef,
|
|
4
4
|
FC,
|
|
5
5
|
} from 'react';
|
|
6
|
+
// import { setContentToggle } from '@aarhus-university/au-desi
|
|
7
|
+
// gnsystem-delphinus/source/js/components/content-toggle';
|
|
6
8
|
import { setContentToggle } from '@aarhus-university/au-designsystem-delphinus/source/js/components/content-toggle';
|
|
7
9
|
|
|
8
10
|
const AUContentToggleComponent: FC<AUContentToggleComponentProps> = ({
|
|
9
11
|
toggled,
|
|
10
12
|
classNames,
|
|
11
13
|
children,
|
|
14
|
+
onClick,
|
|
12
15
|
beforeCallback,
|
|
13
16
|
afterCallback,
|
|
14
17
|
}: AUContentToggleComponentProps) => {
|
|
15
18
|
const toggleContainer = useRef<HTMLDivElement>(null);
|
|
16
19
|
useEffect(() => {
|
|
17
20
|
const toggle = toggleContainer?.current?.querySelector('.content-toggle__content');
|
|
18
|
-
let cleanUp: CleanUpPair
|
|
21
|
+
let cleanUp: CleanUpPair[];
|
|
19
22
|
if (toggle) {
|
|
20
|
-
cleanUp = setContentToggle(toggle, beforeCallback, afterCallback);
|
|
21
|
-
cleanUp
|
|
23
|
+
cleanUp = setContentToggle(toggle, onClick, beforeCallback, afterCallback);
|
|
24
|
+
if (cleanUp.length > 0) { // Den første er altid knappen (og ikke luk-knappen)
|
|
25
|
+
cleanUp[0].element.setAttribute('aria-expanded', toggled ? 'true' : 'false');
|
|
26
|
+
}
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
return () => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
30
|
+
cleanUp.forEach((c) => {
|
|
31
|
+
c.element.removeEventListener('click', c.clickEvent);
|
|
32
|
+
});
|
|
28
33
|
};
|
|
29
34
|
}, []);
|
|
30
35
|
return (
|
|
@@ -1,52 +1,101 @@
|
|
|
1
1
|
/* eslint-disable react/no-array-index-key */
|
|
2
2
|
/* eslint-env browser */
|
|
3
|
-
import React, { useEffect, FC } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import React, { useEffect, FC, useRef } from 'react';
|
|
4
|
+
import {
|
|
5
|
+
setToolbar, closeAllContentTogglesInToolbar, ExtendedCleanUpPair, setOverflow,
|
|
6
|
+
} from '@aarhus-university/au-designsystem-delphinus/source/js/components/toolbar';
|
|
7
7
|
|
|
8
8
|
const AUToolbarComponent: FC<AUToolbarComponentProps> = ({
|
|
9
9
|
lang,
|
|
10
10
|
elements,
|
|
11
|
+
noCollapse,
|
|
12
|
+
center,
|
|
13
|
+
title,
|
|
14
|
+
buttonClass,
|
|
15
|
+
buttonElements,
|
|
11
16
|
}: AUToolbarComponentProps) => {
|
|
17
|
+
const toolbarRef = useRef<HTMLDivElement>(null);
|
|
12
18
|
useEffect(() => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (windowInnerWidth > 0 && window.innerWidth !== windowInnerWidth) {
|
|
18
|
-
windowInnerWidth = window.innerWidth;
|
|
19
|
-
setToolbars();
|
|
19
|
+
const onResize = () => {
|
|
20
|
+
if (toolbarRef.current?.querySelector('.toolbar__items')) {
|
|
21
|
+
closeAllContentTogglesInToolbar(toolbarRef.current);
|
|
22
|
+
setOverflow(toolbarRef.current);
|
|
20
23
|
}
|
|
21
|
-
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
let toolbarCleanUp: ExtendedCleanUpPair[];
|
|
27
|
+
if (toolbarRef.current) {
|
|
28
|
+
// eslint-disable-next-line max-len
|
|
29
|
+
toolbarCleanUp = setToolbar(toolbarRef.current, (toolbar: HTMLElement) => {
|
|
30
|
+
closeAllContentTogglesInToolbar(toolbar);
|
|
31
|
+
}, false);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
window.addEventListener('resize', onResize);
|
|
22
35
|
|
|
23
36
|
// Runs on unmounting
|
|
24
37
|
return () => {
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
toolbarCleanUp.forEach((pair: ExtendedCleanUpPair) => {
|
|
39
|
+
if (typeof pair.clickEvent === 'function') {
|
|
40
|
+
pair.element.removeEventListener('click', pair.clickEvent);
|
|
41
|
+
}
|
|
42
|
+
if (typeof pair.keyUpEvent === 'function') {
|
|
43
|
+
pair.element.removeEventListener('keyup', pair.keyUpEvent);
|
|
44
|
+
}
|
|
27
45
|
});
|
|
46
|
+
|
|
47
|
+
window.removeEventListener('resize', onResize);
|
|
28
48
|
};
|
|
29
49
|
}, []);
|
|
30
50
|
|
|
31
51
|
const renderElements = elements.map((e, i) => <React.Fragment key={i}>{e}</React.Fragment>);
|
|
32
52
|
const label = lang === 'da' ? 'Muligheder' : 'Options';
|
|
33
53
|
|
|
54
|
+
let toolbarClass = 'toolbar';
|
|
55
|
+
if (noCollapse) {
|
|
56
|
+
toolbarClass = `${toolbarClass} toolbar--no-collapse`;
|
|
57
|
+
}
|
|
58
|
+
if (center) {
|
|
59
|
+
toolbarClass = `${toolbarClass} toolbar--center-align-items`;
|
|
60
|
+
}
|
|
61
|
+
|
|
34
62
|
return (
|
|
35
|
-
<div
|
|
63
|
+
<div
|
|
64
|
+
ref={toolbarRef}
|
|
65
|
+
className={toolbarClass}
|
|
66
|
+
>
|
|
36
67
|
<div className="toolbar__items">
|
|
37
68
|
{renderElements}
|
|
38
69
|
</div>
|
|
39
70
|
<button
|
|
40
71
|
type="button"
|
|
41
|
-
className=
|
|
42
|
-
title={label}
|
|
72
|
+
className={buttonClass}
|
|
73
|
+
title={title || label}
|
|
74
|
+
aria-label={title}
|
|
43
75
|
aria-haspopup="true"
|
|
44
76
|
>
|
|
45
|
-
{
|
|
77
|
+
{
|
|
78
|
+
buttonElements?.length === 0 && (title || label)
|
|
79
|
+
}
|
|
80
|
+
{
|
|
81
|
+
(buttonElements || []).map((element, index) => (
|
|
82
|
+
<React.Fragment key={index}>
|
|
83
|
+
{element}
|
|
84
|
+
</React.Fragment>
|
|
85
|
+
))
|
|
86
|
+
}
|
|
46
87
|
</button>
|
|
47
88
|
</div>
|
|
48
89
|
);
|
|
49
90
|
};
|
|
50
91
|
|
|
92
|
+
AUToolbarComponent.defaultProps = {
|
|
93
|
+
noCollapse: false,
|
|
94
|
+
center: false,
|
|
95
|
+
title: undefined,
|
|
96
|
+
buttonClass: 'toolbar__toggle',
|
|
97
|
+
buttonElements: [],
|
|
98
|
+
};
|
|
99
|
+
|
|
51
100
|
AUToolbarComponent.displayName = 'AUToolbarComponent';
|
|
52
101
|
export default AUToolbarComponent;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
FC, useEffect, useRef, useState,
|
|
3
|
+
} from 'react';
|
|
4
|
+
import { overflowsY } from '@aarhus-university/au-designsystem-delphinus/source/js/components/helpers';
|
|
5
|
+
|
|
6
|
+
const focusableSelector = 'a[href]:not([tabindex="-1"]), area[href]:not([tabindex="-1"]), input:not([disabled]):not([tabindex="-1"]), select:not([disabled]):not([tabindex="-1"]), textarea:not([disabled]):not([tabindex="-1"]), button:not([disabled]):not([tabindex="-1"]), iframe:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"]), [contentEditable=true]:not([tabindex="-1"])';
|
|
7
|
+
const getMaxLinesStyle = (maxLines: number): React.CSSProperties => ({ '--max-lines': maxLines } as React.CSSProperties);
|
|
8
|
+
|
|
9
|
+
const setTruncate = (
|
|
10
|
+
expandElement: HTMLElement,
|
|
11
|
+
contentElement: HTMLElement,
|
|
12
|
+
expanded: boolean,
|
|
13
|
+
): void => {
|
|
14
|
+
if (!expanded && overflowsY(contentElement)) {
|
|
15
|
+
expandElement.removeAttribute('hidden');
|
|
16
|
+
} else {
|
|
17
|
+
expandElement.parentElement?.classList.add('truncator--no-truncation');
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const AUTruncatorComponent: FC<AUTruncatorComponentProps> = ({
|
|
22
|
+
maxLines,
|
|
23
|
+
header,
|
|
24
|
+
headerLevel,
|
|
25
|
+
children,
|
|
26
|
+
classNames,
|
|
27
|
+
buttonChild,
|
|
28
|
+
}: AUTruncatorComponentProps) => {
|
|
29
|
+
const expandRef = useRef<HTMLButtonElement>(null);
|
|
30
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
31
|
+
const [expanded, setExpanded] = useState<boolean>(false);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
setTruncate(expandRef.current as HTMLElement, contentRef.current as HTMLElement, expanded);
|
|
34
|
+
const focusElements = contentRef.current?.querySelectorAll(focusableSelector);
|
|
35
|
+
|
|
36
|
+
const onFocus = (event: Event) => {
|
|
37
|
+
if (overflowsY(event.target as HTMLElement)) {
|
|
38
|
+
setExpanded(true);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (focusElements) {
|
|
43
|
+
focusElements.forEach((f) => {
|
|
44
|
+
f.addEventListener('focus', onFocus);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const onResize = () => {
|
|
49
|
+
setTruncate(expandRef.current as HTMLElement, contentRef.current as HTMLElement, expanded);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
window.addEventListener('resize', onResize);
|
|
53
|
+
|
|
54
|
+
return () => {
|
|
55
|
+
if (focusElements) {
|
|
56
|
+
focusElements.forEach((f) => {
|
|
57
|
+
f.removeEventListener('focus', onFocus);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
window.removeEventListener('resize', onResize);
|
|
61
|
+
};
|
|
62
|
+
}, [expanded]);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div className={`${classNames}${expanded ? ' truncator--no-truncation' : ''}`} style={getMaxLinesStyle(maxLines)}>
|
|
66
|
+
{
|
|
67
|
+
header && (
|
|
68
|
+
<>
|
|
69
|
+
{
|
|
70
|
+
headerLevel === 1 && (
|
|
71
|
+
<h1 className="truncator__header">{header}</h1>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
{
|
|
75
|
+
headerLevel === 2 && (
|
|
76
|
+
<h2 className="truncator__header">{header}</h2>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
{
|
|
80
|
+
headerLevel === 3 && (
|
|
81
|
+
<h3 className="truncator__header">{header}</h3>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
{
|
|
85
|
+
headerLevel === 4 && (
|
|
86
|
+
<h4 className="truncator__header">{header}</h4>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
{
|
|
90
|
+
headerLevel === 5 && (
|
|
91
|
+
<h5 className="truncator__header">{header}</h5>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
{
|
|
95
|
+
headerLevel === 6 && (
|
|
96
|
+
<h6 className="truncator__header">{header}</h6>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
</>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
<button
|
|
103
|
+
ref={expandRef}
|
|
104
|
+
type="button"
|
|
105
|
+
className="truncator__expand"
|
|
106
|
+
hidden
|
|
107
|
+
aria-expanded={expanded}
|
|
108
|
+
onClick={() => setExpanded(true)}
|
|
109
|
+
>
|
|
110
|
+
{buttonChild}
|
|
111
|
+
</button>
|
|
112
|
+
<div
|
|
113
|
+
ref={contentRef}
|
|
114
|
+
className="truncator__content"
|
|
115
|
+
>
|
|
116
|
+
{children}
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
AUTruncatorComponent.defaultProps = {
|
|
123
|
+
header: undefined,
|
|
124
|
+
headerLevel: 2,
|
|
125
|
+
classNames: 'truncator',
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
AUTruncatorComponent.displayName = 'AUTruncatorComponent';
|
|
129
|
+
export default AUTruncatorComponent;
|