@atlaskit/teams-app-internal-popup-adaptor 1.1.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 +10 -0
- package/README.md +17 -0
- package/__tests__/unit/PopupTriggerWithHover.test.tsx +141 -0
- package/__tests__/unit/useHoverDelay.test.tsx +99 -0
- package/__tests__/unit/useHoverTriggerRef.test.tsx +121 -0
- package/__tests__/unit/usePreloadRef.test.tsx +118 -0
- package/__tests__/unit/usePressableTriggerRef.test.tsx +104 -0
- package/__tests__/unit/utils.test.tsx +86 -0
- package/afm-cc/tsconfig.json +40 -0
- package/afm-products/tsconfig.json +40 -0
- package/dist/cjs/PopupTriggerWithHover.js +158 -0
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/useHoverDelay.js +38 -0
- package/dist/cjs/useHoverTriggerRef.js +155 -0
- package/dist/cjs/usePreloadRef.js +119 -0
- package/dist/cjs/usePressableTriggerRef.js +69 -0
- package/dist/cjs/utils.js +48 -0
- package/dist/es2019/PopupTriggerWithHover.js +139 -0
- package/dist/es2019/index.js +1 -0
- package/dist/es2019/useHoverDelay.js +32 -0
- package/dist/es2019/useHoverTriggerRef.js +139 -0
- package/dist/es2019/usePreloadRef.js +115 -0
- package/dist/es2019/usePressableTriggerRef.js +62 -0
- package/dist/es2019/utils.js +40 -0
- package/dist/esm/PopupTriggerWithHover.js +149 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/useHoverDelay.js +34 -0
- package/dist/esm/useHoverTriggerRef.js +149 -0
- package/dist/esm/usePreloadRef.js +113 -0
- package/dist/esm/usePressableTriggerRef.js +63 -0
- package/dist/esm/utils.js +41 -0
- package/dist/types/PopupTriggerWithHover.d.ts +28 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/useHoverDelay.d.ts +8 -0
- package/dist/types/useHoverTriggerRef.d.ts +19 -0
- package/dist/types/usePreloadRef.d.ts +13 -0
- package/dist/types/usePressableTriggerRef.d.ts +9 -0
- package/dist/types/utils.d.ts +16 -0
- package/dist/types-ts4.5/PopupTriggerWithHover.d.ts +28 -0
- package/dist/types-ts4.5/index.d.ts +1 -0
- package/dist/types-ts4.5/useHoverDelay.d.ts +8 -0
- package/dist/types-ts4.5/useHoverTriggerRef.d.ts +19 -0
- package/dist/types-ts4.5/usePreloadRef.d.ts +13 -0
- package/dist/types-ts4.5/usePressableTriggerRef.d.ts +9 -0
- package/dist/types-ts4.5/utils.d.ts +16 -0
- package/package.json +81 -0
- package/popup-trigger-with-hover/package.json +17 -0
- package/src/PopupTriggerWithHover.tsx +240 -0
- package/src/index.ts +5 -0
- package/src/useHoverDelay.ts +42 -0
- package/src/useHoverTriggerRef.ts +177 -0
- package/src/usePreloadRef.ts +152 -0
- package/src/usePressableTriggerRef.ts +89 -0
- package/src/utils.ts +49 -0
- package/tsconfig.app.json +46 -0
- package/tsconfig.dev.json +45 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type MutableRefObject, useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import { bind, type UnbindFn } from 'bind-event-listener';
|
|
4
|
+
|
|
5
|
+
export type UsePressableTriggerRefProps = {
|
|
6
|
+
onOpen: () => void;
|
|
7
|
+
onClose?: () => void;
|
|
8
|
+
isOpen?: boolean;
|
|
9
|
+
isDisabled?: boolean;
|
|
10
|
+
openedByRef?: MutableRefObject<'hover' | 'click' | null>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function usePressableTriggerRef({
|
|
14
|
+
onOpen,
|
|
15
|
+
onClose,
|
|
16
|
+
isOpen = false,
|
|
17
|
+
isDisabled = false,
|
|
18
|
+
openedByRef,
|
|
19
|
+
}: UsePressableTriggerRefProps): (element: HTMLElement | null) => void {
|
|
20
|
+
const nodeRef = useRef<HTMLElement | null>(null);
|
|
21
|
+
const eventListeners = useRef<UnbindFn[]>([]);
|
|
22
|
+
|
|
23
|
+
const handleOpen = useCallback(() => {
|
|
24
|
+
if (isOpen) {
|
|
25
|
+
onClose?.();
|
|
26
|
+
} else {
|
|
27
|
+
if (openedByRef) {
|
|
28
|
+
openedByRef.current = 'click';
|
|
29
|
+
}
|
|
30
|
+
onOpen();
|
|
31
|
+
}
|
|
32
|
+
}, [isOpen, onClose, openedByRef, onOpen]);
|
|
33
|
+
|
|
34
|
+
const keydownListener = useCallback(
|
|
35
|
+
(event: Event) => {
|
|
36
|
+
if (event instanceof KeyboardEvent && (event.key === 'Enter' || event.key === ' ')) {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
handleOpen();
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
[handleOpen],
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const cleanUpEventListeners = useCallback(() => {
|
|
45
|
+
eventListeners.current.forEach((unbind) => {
|
|
46
|
+
unbind();
|
|
47
|
+
});
|
|
48
|
+
eventListeners.current = [];
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
const addEventListener = useCallback(
|
|
52
|
+
(element: HTMLElement, type: string, callback: EventListener) => {
|
|
53
|
+
const unbind = bind(element, {
|
|
54
|
+
type,
|
|
55
|
+
listener: callback,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
eventListeners.current.push(unbind);
|
|
59
|
+
},
|
|
60
|
+
[],
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const refCallback = useCallback(
|
|
64
|
+
(element: HTMLElement | null) => {
|
|
65
|
+
if (element !== nodeRef.current) {
|
|
66
|
+
if (nodeRef.current) {
|
|
67
|
+
cleanUpEventListeners();
|
|
68
|
+
}
|
|
69
|
+
if (element) {
|
|
70
|
+
if (!isDisabled) {
|
|
71
|
+
addEventListener(element, 'click', handleOpen);
|
|
72
|
+
}
|
|
73
|
+
// Always attach keydown for keyboard accessibility
|
|
74
|
+
addEventListener(element, 'keydown', keydownListener);
|
|
75
|
+
}
|
|
76
|
+
nodeRef.current = element;
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
[handleOpen, keydownListener, addEventListener, cleanUpEventListeners, isDisabled],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
return () => {
|
|
84
|
+
cleanUpEventListeners();
|
|
85
|
+
};
|
|
86
|
+
}, [cleanUpEventListeners]);
|
|
87
|
+
|
|
88
|
+
return refCallback;
|
|
89
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard to check if an event has a relatedTarget property.
|
|
3
|
+
*/
|
|
4
|
+
function hasRelatedTarget(event: Event): event is Event & { relatedTarget: EventTarget | null } {
|
|
5
|
+
return 'relatedTarget' in event;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Checks if the mouse is moving to an atlaskit portal or kudos modal
|
|
10
|
+
* where we should prevent the popup from closing
|
|
11
|
+
*/
|
|
12
|
+
function isMovingToKudos(relatedTarget: EventTarget | null): boolean {
|
|
13
|
+
if (!(relatedTarget instanceof HTMLElement)) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const iframe =
|
|
18
|
+
relatedTarget.closest('.atlaskit-portal')?.querySelector('iframe') ||
|
|
19
|
+
relatedTarget.closest('iframe');
|
|
20
|
+
return iframe instanceof HTMLIFrameElement && iframe.src.includes('give-kudos');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns the child portal if mouse is moving to a portal controlled by the parent popup.
|
|
25
|
+
*/
|
|
26
|
+
function getChildPortalFromParentPopup(
|
|
27
|
+
relatedTarget: EventTarget | null,
|
|
28
|
+
parentPopupElement: HTMLElement | null,
|
|
29
|
+
): Element | null {
|
|
30
|
+
if (!(relatedTarget instanceof HTMLElement) || !parentPopupElement) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const portal = relatedTarget.closest('.atlaskit-portal');
|
|
35
|
+
if (!portal) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const trigger of Array.from(parentPopupElement.querySelectorAll('[aria-controls]'))) {
|
|
40
|
+
const controlsId = trigger.getAttribute('aria-controls');
|
|
41
|
+
if (controlsId && portal.querySelector(`#${controlsId}`)) {
|
|
42
|
+
return portal;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { hasRelatedTarget, isMovingToKudos, getChildPortalFromParentPopup };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.base.json",
|
|
3
|
+
"include": ["./src/**/*.ts", "./src/**/*.tsx"],
|
|
4
|
+
"exclude": [
|
|
5
|
+
"**/docs/**/*",
|
|
6
|
+
"**/__tests__/**/*",
|
|
7
|
+
"**/vr-tests/**/*",
|
|
8
|
+
"**/__perf__/**/*",
|
|
9
|
+
"**/*.test.*",
|
|
10
|
+
"**/test.*",
|
|
11
|
+
"**/test-*",
|
|
12
|
+
"**/examples.ts",
|
|
13
|
+
"**/examples.tsx",
|
|
14
|
+
"**/examples/*.ts",
|
|
15
|
+
"**/examples/*.tsx",
|
|
16
|
+
"**/examples/**/*.ts",
|
|
17
|
+
"**/examples/**/*.tsx",
|
|
18
|
+
"**/stories.ts",
|
|
19
|
+
"**/stories.tsx",
|
|
20
|
+
"**/stories/*.ts",
|
|
21
|
+
"**/stories/*.tsx",
|
|
22
|
+
"**/stories/**/*.ts",
|
|
23
|
+
"**/stories/**/*.tsx",
|
|
24
|
+
"**/storybook/**/*",
|
|
25
|
+
"**/constellation/**/*",
|
|
26
|
+
".storybook/*",
|
|
27
|
+
"./__fixtures__/**/*",
|
|
28
|
+
"./__generated__/**/*",
|
|
29
|
+
"./mocks/**/*",
|
|
30
|
+
"./__mocks__/**/*",
|
|
31
|
+
"**/mock.*",
|
|
32
|
+
"**/codemods/**/*.ts",
|
|
33
|
+
"**/codemods/**/*.tsx"
|
|
34
|
+
],
|
|
35
|
+
"compilerOptions": {
|
|
36
|
+
"composite": true,
|
|
37
|
+
"outDir": "../../../tsDist/@atlaskit__teams-app-internal-popup-adaptor/app",
|
|
38
|
+
"isolatedDeclarations": true
|
|
39
|
+
},
|
|
40
|
+
"references": [
|
|
41
|
+
{
|
|
42
|
+
"path": "../../design-system/popup/tsconfig.app.json"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"files": []
|
|
46
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.base.json",
|
|
3
|
+
"include": [
|
|
4
|
+
"**/docs/**/*",
|
|
5
|
+
"**/__tests__/**/*",
|
|
6
|
+
"**/vr-tests/**/*",
|
|
7
|
+
"**/__perf__/**/*",
|
|
8
|
+
"**/*.test.*",
|
|
9
|
+
"**/test.*",
|
|
10
|
+
"**/test-*",
|
|
11
|
+
"**/examples.ts",
|
|
12
|
+
"**/examples.tsx",
|
|
13
|
+
"**/examples/*.ts",
|
|
14
|
+
"**/examples/*.tsx",
|
|
15
|
+
"**/examples/**/*.ts",
|
|
16
|
+
"**/examples/**/*.tsx",
|
|
17
|
+
"**/storybook/**/*",
|
|
18
|
+
"**/constellation/**/*",
|
|
19
|
+
".storybook/*",
|
|
20
|
+
"./__fixtures__/**/*",
|
|
21
|
+
"./__generated__/**/*",
|
|
22
|
+
"./mocks/**/*",
|
|
23
|
+
"./__mocks__/**/*",
|
|
24
|
+
"**/mock.*",
|
|
25
|
+
"**/codemods/**/*.ts",
|
|
26
|
+
"**/codemods/**/*.tsx",
|
|
27
|
+
"**/stories.ts",
|
|
28
|
+
"**/stories.tsx",
|
|
29
|
+
"**/stories/*.ts",
|
|
30
|
+
"**/stories/*.tsx",
|
|
31
|
+
"**/stories/**/*.ts",
|
|
32
|
+
"**/stories/**/*.tsx"
|
|
33
|
+
],
|
|
34
|
+
"exclude": ["./dist/**/*", "./build/**/*", "./node_modules/**/*"],
|
|
35
|
+
"compilerOptions": {
|
|
36
|
+
"composite": true,
|
|
37
|
+
"outDir": "../../../tsDist/@atlaskit__teams-app-internal-popup-adaptor/dev",
|
|
38
|
+
"isolatedDeclarations": true
|
|
39
|
+
},
|
|
40
|
+
"references": [
|
|
41
|
+
{
|
|
42
|
+
"path": "./tsconfig.app.json"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.json",
|
|
3
|
+
"include": [
|
|
4
|
+
"__tests__/**/*.ts",
|
|
5
|
+
"__tests__/**/*.tsx",
|
|
6
|
+
"docs/**/*.ts",
|
|
7
|
+
"docs/**/*.tsx",
|
|
8
|
+
"examples/**/*.ts",
|
|
9
|
+
"examples/**/*.tsx",
|
|
10
|
+
"src/**/*.ts",
|
|
11
|
+
"src/**/*.tsx",
|
|
12
|
+
"**/stories.ts",
|
|
13
|
+
"**/stories.tsx",
|
|
14
|
+
"**/stories/*.ts",
|
|
15
|
+
"**/stories/*.tsx",
|
|
16
|
+
"**/stories/**/*.ts",
|
|
17
|
+
"**/stories/**/*.tsx"
|
|
18
|
+
]
|
|
19
|
+
}
|