botframework-webchat 4.15.3-main.20220706.75d867c → 4.15.3-main.20220728.008aa26
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/dist/webchat-es5.js +1 -1
- package/dist/webchat-minimal.js +1 -1
- package/dist/webchat.js +1 -1
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/closest.d.ts +2 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/closest.d.ts.map +1 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/closest.js +25 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/findDOMNodeOwner.d.ts +3 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/findDOMNodeOwner.d.ts.map +1 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/findDOMNodeOwner.js +32 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/useAdaptiveCardModEffect.d.ts +13 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/useAdaptiveCardModEffect.d.ts.map +1 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/useAdaptiveCardModEffect.js +132 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/useLazyRef.d.ts +3 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/useLazyRef.d.ts.map +1 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/useLazyRef.js +21 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/usePrevious.d.ts +2 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/usePrevious.d.ts.map +1 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/usePrevious.js +18 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/useValueRef.d.ts +3 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/useValueRef.d.ts.map +1 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/private/useValueRef.js +15 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/useActionSetShouldNotBeMenuBarModEffect.d.ts +16 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/useActionSetShouldNotBeMenuBarModEffect.d.ts.map +1 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/useActionSetShouldNotBeMenuBarModEffect.js +45 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/useActionShouldBePushButtonModEffect.d.ts +15 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/useActionShouldBePushButtonModEffect.d.ts.map +1 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/useActionShouldBePushButtonModEffect.js +93 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/useActiveElementModEffect.d.ts +6 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/useActiveElementModEffect.d.ts.map +1 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/useActiveElementModEffect.js +44 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/useDisabledModEffect.d.ts +9 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/useDisabledModEffect.d.ts.map +1 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/useDisabledModEffect.js +50 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/usePersistValuesModEffect.d.ts +6 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/usePersistValuesModEffect.d.ts.map +1 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardHacks/usePersistValuesModEffect.js +103 -0
- package/lib/adaptiveCards/Attachment/AdaptiveCardRenderer.d.ts.map +1 -1
- package/lib/adaptiveCards/Attachment/AdaptiveCardRenderer.js +97 -557
- package/lib/adaptiveCards/Attachment/private/renderAdaptiveCard.d.ts +15 -0
- package/lib/adaptiveCards/Attachment/private/renderAdaptiveCard.d.ts.map +1 -0
- package/lib/adaptiveCards/Attachment/private/renderAdaptiveCard.js +79 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/addEventListenerWithUndo.d.ts +6 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/addEventListenerWithUndo.d.ts.map +1 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/addEventListenerWithUndo.js +25 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/bunchUndos.d.ts +3 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/bunchUndos.d.ts.map +1 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/bunchUndos.js +23 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/durableAddClassWithUndo.d.ts +8 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/durableAddClassWithUndo.d.ts.map +1 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/durableAddClassWithUndo.js +38 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/durableDisableInputElementAccessiblyWithUndo.d.ts +22 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/durableDisableInputElementAccessiblyWithUndo.d.ts.map +1 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/durableDisableInputElementAccessiblyWithUndo.js +96 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/private/addClass.d.ts +5 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/private/addClass.d.ts.map +1 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/private/addClass.js +19 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/private/getAttributeOrFalse.d.ts +7 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/private/getAttributeOrFalse.d.ts.map +1 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/private/getAttributeOrFalse.js +16 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/private/noOp.d.ts +3 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/private/noOp.d.ts.map +1 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/private/noOp.js +14 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/private/setOrRemoveAttributeIfFalse.d.ts +9 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/private/setOrRemoveAttributeIfFalse.d.ts.map +1 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/private/setOrRemoveAttributeIfFalse.js +22 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/setOrRemoveAttributeIfFalseWithUndo.d.ts +12 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/setOrRemoveAttributeIfFalseWithUndo.d.ts.map +1 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/setOrRemoveAttributeIfFalseWithUndo.js +41 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/types/UndoFunction.d.ts +3 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/types/UndoFunction.d.ts.map +1 -0
- package/lib/adaptiveCards/DOMManipulationWithUndo/types/UndoFunction.js +2 -0
- package/lib/addVersion.js +1 -1
- package/lib/createFullStyleSet.d.ts +2 -2
- package/package.json +7 -7
- package/src/adaptiveCards/Attachment/AdaptiveCardHacks/private/closest.ts +17 -0
- package/src/adaptiveCards/Attachment/AdaptiveCardHacks/private/findDOMNodeOwner.ts +25 -0
- package/src/adaptiveCards/Attachment/AdaptiveCardHacks/private/useAdaptiveCardModEffect.ts +93 -0
- package/src/adaptiveCards/Attachment/AdaptiveCardHacks/private/useLazyRef.ts +15 -0
- package/src/adaptiveCards/Attachment/AdaptiveCardHacks/private/usePrevious.ts +12 -0
- package/src/adaptiveCards/Attachment/AdaptiveCardHacks/private/useValueRef.ts +11 -0
- package/src/adaptiveCards/Attachment/AdaptiveCardHacks/useActionSetShouldNotBeMenuBarModEffect.ts +39 -0
- package/src/adaptiveCards/Attachment/AdaptiveCardHacks/useActionShouldBePushButtonModEffect.ts +105 -0
- package/src/adaptiveCards/Attachment/AdaptiveCardHacks/useActiveElementModEffect.ts +35 -0
- package/src/adaptiveCards/Attachment/AdaptiveCardHacks/useDisabledModEffect.ts +45 -0
- package/src/adaptiveCards/Attachment/AdaptiveCardHacks/usePersistValuesModEffect.ts +110 -0
- package/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx +83 -582
- package/src/adaptiveCards/Attachment/private/renderAdaptiveCard.ts +75 -0
- package/src/adaptiveCards/DOMManipulationWithUndo/addEventListenerWithUndo.ts +21 -0
- package/src/adaptiveCards/DOMManipulationWithUndo/bunchUndos.tsx +12 -0
- package/src/adaptiveCards/DOMManipulationWithUndo/durableAddClassWithUndo.ts +28 -0
- package/src/adaptiveCards/DOMManipulationWithUndo/durableDisableInputElementAccessiblyWithUndo.ts +84 -0
- package/src/adaptiveCards/DOMManipulationWithUndo/private/addClass.tsx +13 -0
- package/src/adaptiveCards/DOMManipulationWithUndo/private/getAttributeOrFalse.ts +8 -0
- package/src/adaptiveCards/DOMManipulationWithUndo/private/noOp.ts +5 -0
- package/src/adaptiveCards/DOMManipulationWithUndo/private/setOrRemoveAttributeIfFalse.ts +18 -0
- package/src/adaptiveCards/DOMManipulationWithUndo/setOrRemoveAttributeIfFalseWithUndo.ts +34 -0
- package/src/adaptiveCards/DOMManipulationWithUndo/types/UndoFunction.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "botframework-webchat",
|
|
3
|
-
"version": "4.15.3-main.
|
|
3
|
+
"version": "4.15.3-main.20220728.008aa26",
|
|
4
4
|
"description": "A highly-customizable web-based chat client for Azure Bot Services.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"@babel/runtime": "7.17.2",
|
|
49
49
|
"adaptivecards": "2.10.0",
|
|
50
50
|
"botframework-directlinejs": "0.15.1",
|
|
51
|
-
"botframework-directlinespeech-sdk": "4.15.3-main.
|
|
52
|
-
"botframework-webchat-api": "4.15.3-main.
|
|
53
|
-
"botframework-webchat-component": "4.15.3-main.
|
|
54
|
-
"botframework-webchat-core": "4.15.3-main.
|
|
51
|
+
"botframework-directlinespeech-sdk": "4.15.3-main.20220728.008aa26",
|
|
52
|
+
"botframework-webchat-api": "4.15.3-main.20220728.008aa26",
|
|
53
|
+
"botframework-webchat-component": "4.15.3-main.20220728.008aa26",
|
|
54
|
+
"botframework-webchat-core": "4.15.3-main.20220728.008aa26",
|
|
55
55
|
"classnames": "2.3.1",
|
|
56
56
|
"core-js": "3.21.1",
|
|
57
57
|
"markdown-it": "12.3.2",
|
|
@@ -84,8 +84,8 @@
|
|
|
84
84
|
"babel-plugin-transform-inline-environment-variables": "^0.4.3",
|
|
85
85
|
"concurrently": "^7.0.0",
|
|
86
86
|
"esbuild": "^0.14.47",
|
|
87
|
-
"isomorphic-react": "4.15.3-main.
|
|
88
|
-
"isomorphic-react-dom": "4.15.3-main.
|
|
87
|
+
"isomorphic-react": "4.15.3-main.20220728.008aa26",
|
|
88
|
+
"isomorphic-react-dom": "4.15.3-main.20220728.008aa26",
|
|
89
89
|
"source-map-loader": "^3.0.1",
|
|
90
90
|
"terser-webpack-plugin": "^5.3.1",
|
|
91
91
|
"typescript": "^4.6.2",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Ponyfill `HTMLElement.closest`.
|
|
2
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
|
|
3
|
+
export default function closest(element: HTMLElement, selector: string): HTMLElement | undefined {
|
|
4
|
+
if (typeof element.closest === 'function') {
|
|
5
|
+
return element.closest(selector);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let current: HTMLElement | null = element;
|
|
9
|
+
|
|
10
|
+
while (current) {
|
|
11
|
+
if (current.matches(selector)) {
|
|
12
|
+
return current;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
current = current.parentElement;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AdaptiveCard, CardObject, ShowCardAction } from 'adaptivecards';
|
|
2
|
+
|
|
3
|
+
// TODO: [P2] Remove this when Adaptive Card fixed their bug #7606.
|
|
4
|
+
// https://github.com/microsoft/AdaptiveCards/issues/7606
|
|
5
|
+
// Currently, their findDOMNodeOwner() returns bad result when passing an Action attached to the card.
|
|
6
|
+
export default function findDOMNodeOwner(adaptiveCard: AdaptiveCard, element: HTMLElement): CardObject | undefined {
|
|
7
|
+
for (let count = adaptiveCard.getActionCount(), index = 0; index < count; index++) {
|
|
8
|
+
const action = adaptiveCard.getActionAt(index);
|
|
9
|
+
|
|
10
|
+
if (action.renderedElement === element) {
|
|
11
|
+
return action;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (action.getJsonTypeName() === 'Action.ShowCard') {
|
|
15
|
+
const { card } = action as ShowCardAction;
|
|
16
|
+
const cardObject = card && findDOMNodeOwner(card, element);
|
|
17
|
+
|
|
18
|
+
if (cardObject) {
|
|
19
|
+
return cardObject;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return adaptiveCard.findDOMNodeOwner(element);
|
|
25
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import useLazyRef from './useLazyRef';
|
|
4
|
+
import useValueRef from './useValueRef';
|
|
5
|
+
|
|
6
|
+
import type { AdaptiveCard } from 'adaptivecards';
|
|
7
|
+
|
|
8
|
+
type ModFunction<TArgs extends unknown[] = []> = (
|
|
9
|
+
adaptiveCard: AdaptiveCard,
|
|
10
|
+
cardElement: HTMLElement,
|
|
11
|
+
...args: TArgs
|
|
12
|
+
) => () => void;
|
|
13
|
+
|
|
14
|
+
class Mod<TArgs extends unknown[]> {
|
|
15
|
+
constructor(mod: ModFunction<TArgs>) {
|
|
16
|
+
this.#mod = mod;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// @ts-ignore We are using Babel to transpile and it will transpile private modifier.
|
|
20
|
+
#mod: ModFunction<TArgs>;
|
|
21
|
+
// @ts-ignore We are using Babel to transpile and it will transpile private modifier.
|
|
22
|
+
#undo: (() => void) | undefined;
|
|
23
|
+
|
|
24
|
+
apply(adaptiveCard: AdaptiveCard | undefined, cardElement: HTMLElement | undefined, ...args: TArgs) {
|
|
25
|
+
this.#undo?.();
|
|
26
|
+
this.#undo = adaptiveCard && cardElement && this.#mod(adaptiveCard, cardElement, ...args);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
undo() {
|
|
30
|
+
this.#undo?.();
|
|
31
|
+
this.#undo = undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates a mod effect for Adaptive Card.
|
|
37
|
+
*
|
|
38
|
+
* When this hook is executed, it will return two functions for applying and undo the mod.
|
|
39
|
+
* It will also monitor the DOM tree and undo-then-reapply if mutation occurred.
|
|
40
|
+
*
|
|
41
|
+
* The first function must be called right after DOM is mounted. The second function must be called right before re-render.
|
|
42
|
+
*
|
|
43
|
+
* @return {[function, function]} Two functions, the first one to apply the mod, the second one to undo the mod.
|
|
44
|
+
*/
|
|
45
|
+
export default function useAdaptiveCardModEffect<TArgs extends unknown[]>(
|
|
46
|
+
modder: (adaptiveCard: AdaptiveCard, cardElement: HTMLElement, ...args: TArgs) => () => void,
|
|
47
|
+
adaptiveCard: AdaptiveCard
|
|
48
|
+
): readonly [(cardElement: HTMLElement) => void, () => void] {
|
|
49
|
+
const adaptiveCardRef = useValueRef(adaptiveCard);
|
|
50
|
+
const mod = useMemo(() => new Mod<TArgs>(modder), [modder]);
|
|
51
|
+
const reapplyRef = useRef<() => void>();
|
|
52
|
+
|
|
53
|
+
const observerRef = useLazyRef<MutationObserver>(
|
|
54
|
+
() =>
|
|
55
|
+
new MutationObserver(() => {
|
|
56
|
+
reapplyRef.current?.();
|
|
57
|
+
})
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
useEffect(
|
|
61
|
+
() => () => {
|
|
62
|
+
observerRef.current.disconnect();
|
|
63
|
+
},
|
|
64
|
+
[observerRef]
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const handleApply = useCallback(
|
|
68
|
+
(cardElement: HTMLElement, ...args: TArgs) => {
|
|
69
|
+
if (adaptiveCardRef.current && cardElement) {
|
|
70
|
+
// Apply the mod immediately, then assign the function to reapply() so we can call later when mutation happens.
|
|
71
|
+
(reapplyRef.current = () => mod.apply(adaptiveCardRef.current, cardElement, ...args))();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const { current: observer } = observerRef;
|
|
75
|
+
|
|
76
|
+
observer.disconnect();
|
|
77
|
+
observer.observe(cardElement, { childList: true, subtree: true });
|
|
78
|
+
},
|
|
79
|
+
[adaptiveCardRef, observerRef, mod]
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const handleUndo = useCallback(() => {
|
|
83
|
+
mod.undo();
|
|
84
|
+
|
|
85
|
+
// If we have undo-ed the mod, calling reapply() through MutationObserver should be no-op.
|
|
86
|
+
reapplyRef.current = undefined;
|
|
87
|
+
}, [mod, reapplyRef]);
|
|
88
|
+
|
|
89
|
+
return useMemo(
|
|
90
|
+
() => Object.freeze([handleApply, handleUndo]) as readonly [(cardElement: HTMLElement) => void, () => void],
|
|
91
|
+
[handleApply, handleUndo]
|
|
92
|
+
);
|
|
93
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { MutableRefObject } from 'react';
|
|
4
|
+
|
|
5
|
+
const UNINITIALIZED = Symbol();
|
|
6
|
+
|
|
7
|
+
export default function useLazyRef<T>(refInit: () => T): MutableRefObject<T> {
|
|
8
|
+
const ref = useRef<T | typeof UNINITIALIZED>(UNINITIALIZED);
|
|
9
|
+
|
|
10
|
+
if (ref.current === UNINITIALIZED) {
|
|
11
|
+
ref.current = refInit();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return ref as MutableRefObject<T>;
|
|
15
|
+
}
|
package/src/adaptiveCards/Attachment/AdaptiveCardHacks/useActionSetShouldNotBeMenuBarModEffect.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import bunchUndos from '../../DOMManipulationWithUndo/bunchUndos';
|
|
4
|
+
import setOrRemoveAttributeIfFalseWithUndo from '../../DOMManipulationWithUndo/setOrRemoveAttributeIfFalseWithUndo';
|
|
5
|
+
import useAdaptiveCardModEffect from './private/useAdaptiveCardModEffect';
|
|
6
|
+
|
|
7
|
+
import type { AdaptiveCard } from 'adaptivecards';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Accessibility: ActionSet should not be menu bar.
|
|
11
|
+
*
|
|
12
|
+
* Menu bar is not accessible through screen reader keyboard shortcut keys:
|
|
13
|
+
*
|
|
14
|
+
* - <kbd>B</kbd> will jump to next button, which the end-user can quickly the chosen action;
|
|
15
|
+
* - <kbd>F</kbd> will jump to next form field, which is very similar but also jump to text fields;
|
|
16
|
+
* - There are no keyboard shortcut keys for menu.
|
|
17
|
+
*
|
|
18
|
+
* Marking action button as menu item in a menu bar hurts accessibility. End-user will not be able to jump to buttons quickly.
|
|
19
|
+
*
|
|
20
|
+
* Thus, ActionSet should not be menu bar.
|
|
21
|
+
*/
|
|
22
|
+
export default function useActionShouldBePushButtonModEffect(adaptiveCard: AdaptiveCard) {
|
|
23
|
+
const modder = useMemo(
|
|
24
|
+
() => (_: AdaptiveCard, cardElement: HTMLElement) => {
|
|
25
|
+
const actionSetElements = Array.from(
|
|
26
|
+
cardElement.querySelectorAll('.ac-actionSet[role="menubar"]') as NodeListOf<HTMLElement>
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const undoStack = actionSetElements.map(actionSetElement =>
|
|
30
|
+
setOrRemoveAttributeIfFalseWithUndo(actionSetElement, 'role', false)
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return () => bunchUndos(undoStack)();
|
|
34
|
+
},
|
|
35
|
+
[]
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return useAdaptiveCardModEffect(modder, adaptiveCard);
|
|
39
|
+
}
|
package/src/adaptiveCards/Attachment/AdaptiveCardHacks/useActionShouldBePushButtonModEffect.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { useMemo, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import addEventListenerWithUndo from '../../DOMManipulationWithUndo/addEventListenerWithUndo';
|
|
4
|
+
import bunchUndos from '../../DOMManipulationWithUndo/bunchUndos';
|
|
5
|
+
import closest from './private/closest';
|
|
6
|
+
import durableAddClassWithUndo from '../../DOMManipulationWithUndo/durableAddClassWithUndo';
|
|
7
|
+
import findDOMNodeOwner from './private/findDOMNodeOwner';
|
|
8
|
+
import setOrRemoveAttributeIfFalseWithUndo from '../../DOMManipulationWithUndo/setOrRemoveAttributeIfFalseWithUndo';
|
|
9
|
+
import useAdaptiveCardModEffect from './private/useAdaptiveCardModEffect';
|
|
10
|
+
import usePrevious from './private/usePrevious';
|
|
11
|
+
|
|
12
|
+
import type { AdaptiveCard, CardObject } from 'adaptivecards';
|
|
13
|
+
import type { UndoFunction } from '../../DOMManipulationWithUndo/types/UndoFunction';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Accessibility: Action in ActionSet/CardElement should be push button.
|
|
17
|
+
*
|
|
18
|
+
* Pressing the action button is a decision-making process. The decision made by the end-user need to be read by the screen reader.
|
|
19
|
+
* Thus, we need to indicate what decision the end-user made.
|
|
20
|
+
*
|
|
21
|
+
* Since action buttons are button, the intuitive way to indicate selection of a button is marking it as pressed.
|
|
22
|
+
*
|
|
23
|
+
* One exception is the `Action.ShowUrl` action. This button represents expand/collapse header of an accordion.
|
|
24
|
+
* Thus, their state is indicated by `aria-expanded`, instead of `aria-pressed`.
|
|
25
|
+
* However, we still need to remove other unnecessary ARIA fields.
|
|
26
|
+
*/
|
|
27
|
+
export default function useActionShouldBePushButtonModEffect(
|
|
28
|
+
adaptiveCard: AdaptiveCard
|
|
29
|
+
): readonly [(cardElement: HTMLElement, actionPerformedClassName?: string) => void, () => void] {
|
|
30
|
+
const prevAdaptiveCard = usePrevious(adaptiveCard);
|
|
31
|
+
const pushedCardObjectsRef = useRef<Set<CardObject>>(new Set());
|
|
32
|
+
|
|
33
|
+
prevAdaptiveCard === adaptiveCard || pushedCardObjectsRef.current.clear();
|
|
34
|
+
|
|
35
|
+
const modder = useMemo(
|
|
36
|
+
() => (adaptiveCard: AdaptiveCard, cardElement: HTMLElement, actionPerformedClassName?: string) => {
|
|
37
|
+
const undoStack: UndoFunction[] = [];
|
|
38
|
+
|
|
39
|
+
Array.from(cardElement.querySelectorAll('button.ac-pushButton') as NodeListOf<HTMLButtonElement>).forEach(
|
|
40
|
+
actionElement => {
|
|
41
|
+
const cardObject = findDOMNodeOwner(adaptiveCard, actionElement);
|
|
42
|
+
|
|
43
|
+
if (!actionElement.hasAttribute('aria-expanded')) {
|
|
44
|
+
if (pushedCardObjectsRef.current.has(cardObject)) {
|
|
45
|
+
actionPerformedClassName &&
|
|
46
|
+
undoStack.push(durableAddClassWithUndo(actionElement, actionPerformedClassName));
|
|
47
|
+
|
|
48
|
+
undoStack.push(setOrRemoveAttributeIfFalseWithUndo(actionElement, 'aria-pressed', 'true'));
|
|
49
|
+
} else {
|
|
50
|
+
undoStack.push(setOrRemoveAttributeIfFalseWithUndo(actionElement, 'aria-pressed', 'false'));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
undoStack.push(
|
|
55
|
+
setOrRemoveAttributeIfFalseWithUndo(actionElement, 'aria-posinset', false),
|
|
56
|
+
setOrRemoveAttributeIfFalseWithUndo(actionElement, 'aria-setsize', false),
|
|
57
|
+
setOrRemoveAttributeIfFalseWithUndo(actionElement, 'role', false)
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
undoStack.push(
|
|
63
|
+
addEventListenerWithUndo(
|
|
64
|
+
cardElement,
|
|
65
|
+
'click',
|
|
66
|
+
({ target }) => {
|
|
67
|
+
// Depends on click location, `target` could be the <div> inside the <button class="ac-pushButton">.
|
|
68
|
+
// Thus, we need to check if we the `target` is inside `button.ac-pushButton` or not.
|
|
69
|
+
const actionElement = closest(target as HTMLButtonElement, 'button.ac-pushButton');
|
|
70
|
+
|
|
71
|
+
if (!actionElement) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const cardObject = findDOMNodeOwner(adaptiveCard, actionElement);
|
|
76
|
+
|
|
77
|
+
if (
|
|
78
|
+
// Not an AC action.
|
|
79
|
+
!cardObject ||
|
|
80
|
+
// Ignores buttons which are supposed to be disabled.
|
|
81
|
+
actionElement.getAttribute('aria-disabled') === 'true' ||
|
|
82
|
+
// Mods all AC action buttons except those for `Action.ShowCard`, which has `aria-expanded` attribute.
|
|
83
|
+
actionElement.hasAttribute('aria-expanded')
|
|
84
|
+
) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
actionPerformedClassName &&
|
|
89
|
+
undoStack.push(durableAddClassWithUndo(actionElement, actionPerformedClassName));
|
|
90
|
+
|
|
91
|
+
undoStack.push(setOrRemoveAttributeIfFalseWithUndo(actionElement, 'aria-pressed', 'true'));
|
|
92
|
+
|
|
93
|
+
cardObject && pushedCardObjectsRef.current.add(cardObject);
|
|
94
|
+
},
|
|
95
|
+
{ capture: true }
|
|
96
|
+
)
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return () => bunchUndos(undoStack)();
|
|
100
|
+
},
|
|
101
|
+
[pushedCardObjectsRef]
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return useAdaptiveCardModEffect(modder, adaptiveCard);
|
|
105
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useMemo, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import findDOMNodeOwner from './private/findDOMNodeOwner';
|
|
4
|
+
import useAdaptiveCardModEffect from './private/useAdaptiveCardModEffect';
|
|
5
|
+
import usePrevious from './private/usePrevious';
|
|
6
|
+
|
|
7
|
+
import type { AdaptiveCard, CardObject } from 'adaptivecards';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Re-rendering: Last focused element must be persisted during render cycle.
|
|
11
|
+
*/
|
|
12
|
+
export default function useActiveElementModEffect(adaptiveCard: AdaptiveCard) {
|
|
13
|
+
const activeCardObjectRef = useRef<CardObject | undefined>();
|
|
14
|
+
const prevAdaptiveCard = usePrevious(adaptiveCard);
|
|
15
|
+
|
|
16
|
+
if (prevAdaptiveCard !== adaptiveCard) {
|
|
17
|
+
activeCardObjectRef.current = undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const modder = useMemo(
|
|
21
|
+
() => (adaptiveCard: AdaptiveCard) => {
|
|
22
|
+
// When apply, if we have saved the `CardObject` that was focused, restore its focused to the newly rendered element.
|
|
23
|
+
activeCardObjectRef.current?.renderedElement?.focus?.();
|
|
24
|
+
|
|
25
|
+
// When undo, we are preparing for the next rendering.
|
|
26
|
+
// So, find and save the `CardObject` that is currently focused.
|
|
27
|
+
return () => {
|
|
28
|
+
activeCardObjectRef.current = findDOMNodeOwner(adaptiveCard, document.activeElement as HTMLElement);
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
[activeCardObjectRef]
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return useAdaptiveCardModEffect(modder, adaptiveCard);
|
|
35
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import bunchUndos from '../../DOMManipulationWithUndo/bunchUndos';
|
|
4
|
+
import durableDisableInputElementAccessiblyWithUndo from '../../DOMManipulationWithUndo/durableDisableInputElementAccessiblyWithUndo';
|
|
5
|
+
import useAdaptiveCardModEffect from './private/useAdaptiveCardModEffect';
|
|
6
|
+
|
|
7
|
+
import type { AdaptiveCard } from 'adaptivecards';
|
|
8
|
+
import type { UndoFunction } from '../../DOMManipulationWithUndo/types/UndoFunction';
|
|
9
|
+
|
|
10
|
+
// This is intended. This is a no-op function and intended to do nothing.
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
12
|
+
const NO_OP: UndoFunction = () => {};
|
|
13
|
+
|
|
14
|
+
// In Adaptive Cards, <button> with "aria-expanded" attribute means it is makeshift of <details> and it is Action.ShowCard.
|
|
15
|
+
// In HTML, <details> should not be disabled unless the accordion does not permit the panel to be collapsed.
|
|
16
|
+
// So when we look for input elements, should skip <button> that mimick <details>.
|
|
17
|
+
const INPUT_ELEMENT_SELECTOR = 'button:not([aria-expanded]), input, select, textarea';
|
|
18
|
+
type InputElementType = HTMLButtonElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Accessibility: Form fields in Adaptive Cards need to be disabled to reduce confusion for screen reader users.
|
|
22
|
+
*
|
|
23
|
+
* One exception is the `Action.ShowUrl`, this is because this action is expand/collapse of an accordion control.
|
|
24
|
+
* Similar to `<details>`/`<summary>`, accordion must not allowed to be disabled.
|
|
25
|
+
*/
|
|
26
|
+
export default function useDisabledModEffect(
|
|
27
|
+
adaptiveCard: AdaptiveCard
|
|
28
|
+
): readonly [(cardElement: HTMLElement, disabled: boolean) => void, () => void] {
|
|
29
|
+
const modder = useMemo(
|
|
30
|
+
() => (_, cardElement: HTMLElement, disabled: boolean) => {
|
|
31
|
+
if (!disabled) {
|
|
32
|
+
return NO_OP;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const undoStack: (() => void)[] = Array.from(
|
|
36
|
+
cardElement.querySelectorAll(INPUT_ELEMENT_SELECTOR) as NodeListOf<InputElementType>
|
|
37
|
+
).map(element => durableDisableInputElementAccessiblyWithUndo(element));
|
|
38
|
+
|
|
39
|
+
return () => bunchUndos(undoStack)();
|
|
40
|
+
},
|
|
41
|
+
[]
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return useAdaptiveCardModEffect(modder, adaptiveCard);
|
|
45
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useMemo, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import useAdaptiveCardModEffect from './private/useAdaptiveCardModEffect';
|
|
4
|
+
import usePrevious from './private/usePrevious';
|
|
5
|
+
|
|
6
|
+
import type { AdaptiveCard, CardObject } from 'adaptivecards';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Gets all user-inputted values under a DOM node.
|
|
10
|
+
*
|
|
11
|
+
* We assume values are ID-ed. If not ID-ed (such as `<textarea>`), there will be only a single instance (no two `<textarea>`).
|
|
12
|
+
*/
|
|
13
|
+
function getUserValues(element: HTMLElement | undefined): Set<string> {
|
|
14
|
+
if (!element) {
|
|
15
|
+
return new Set();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return Array.from(
|
|
19
|
+
element.querySelectorAll('input, option, textarea') as NodeListOf<
|
|
20
|
+
HTMLInputElement | HTMLOptionElement | HTMLTextAreaElement
|
|
21
|
+
>
|
|
22
|
+
).reduce<Set<string>>((values, element) => {
|
|
23
|
+
if (element instanceof HTMLInputElement) {
|
|
24
|
+
const { type } = element;
|
|
25
|
+
|
|
26
|
+
if (type === 'checkbox' || type === 'radio') {
|
|
27
|
+
element.checked && values.add(element.value);
|
|
28
|
+
} else {
|
|
29
|
+
// ASSUMPTION: We expect CardObject will NOT mix <input type="text"> with <input type="checkbox">.
|
|
30
|
+
values.clear();
|
|
31
|
+
values.add(element.value);
|
|
32
|
+
}
|
|
33
|
+
} else if (element instanceof HTMLOptionElement) {
|
|
34
|
+
element.selected && values.add(element.value);
|
|
35
|
+
} else {
|
|
36
|
+
// ASSUMPTION: We expect CardObject will NOT mix <textarea> with <input type="checkbox">.
|
|
37
|
+
values.clear();
|
|
38
|
+
values.add(element.value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return values;
|
|
42
|
+
}, new Set());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Set multiple user-inputted values under a DOM node.
|
|
47
|
+
*
|
|
48
|
+
* This function must be paired with `getUserValues`.
|
|
49
|
+
*/
|
|
50
|
+
function setUserValues(element: HTMLElement | undefined, values: Set<string>): void {
|
|
51
|
+
if (!element) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// If the element does not support multiple choices, say <input type="text"> or <textarea>, then, use the first value.
|
|
56
|
+
const defaultValue = Array.from(values)[0] || '';
|
|
57
|
+
|
|
58
|
+
(
|
|
59
|
+
element.querySelectorAll('input, option, textarea') as NodeListOf<
|
|
60
|
+
HTMLInputElement | HTMLOptionElement | HTMLTextAreaElement
|
|
61
|
+
>
|
|
62
|
+
).forEach(element => {
|
|
63
|
+
if (element instanceof HTMLInputElement) {
|
|
64
|
+
const { type } = element;
|
|
65
|
+
|
|
66
|
+
if (type === 'checkbox' || type === 'radio') {
|
|
67
|
+
element.checked = values.has(element.value);
|
|
68
|
+
} else {
|
|
69
|
+
element.value = defaultValue;
|
|
70
|
+
}
|
|
71
|
+
} else if (element instanceof HTMLOptionElement) {
|
|
72
|
+
element.selected = values.has(element.value);
|
|
73
|
+
} else {
|
|
74
|
+
element.value = defaultValue;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Re-rendering: Current user-inputted values must be saved and restored on re-render.
|
|
81
|
+
*/
|
|
82
|
+
export default function usePersistValuesModEffect(adaptiveCard: AdaptiveCard) {
|
|
83
|
+
const prevAdaptiveCard = usePrevious(adaptiveCard);
|
|
84
|
+
const valuesMapRef = useRef<Map<CardObject, Set<string>>>(new Map());
|
|
85
|
+
|
|
86
|
+
prevAdaptiveCard === adaptiveCard || valuesMapRef.current.clear();
|
|
87
|
+
|
|
88
|
+
const modder = useMemo(
|
|
89
|
+
() => (adaptiveCard: AdaptiveCard) => {
|
|
90
|
+
const { current: valuesMap } = valuesMapRef;
|
|
91
|
+
|
|
92
|
+
adaptiveCard.getAllInputs().forEach(cardObject => {
|
|
93
|
+
valuesMap.has(cardObject) && setUserValues(cardObject.renderedElement, valuesMap.get(cardObject));
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return () => {
|
|
97
|
+
valuesMapRef.current = adaptiveCard
|
|
98
|
+
.getAllInputs()
|
|
99
|
+
.reduce<Map<CardObject, Set<string>>>((valuesMap, cardObject) => {
|
|
100
|
+
const value = getUserValues(cardObject.renderedElement);
|
|
101
|
+
|
|
102
|
+
return typeof value !== 'undefined' ? valuesMap.set(cardObject, value) : valuesMap;
|
|
103
|
+
}, new Map());
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
[valuesMapRef]
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
return useAdaptiveCardModEffect(modder, adaptiveCard);
|
|
110
|
+
}
|