@azure/communication-react 1.16.0-alpha-202404160012 → 1.16.0-alpha-202404180013
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/communication-react.d.ts +1 -1
- package/dist/dist-cjs/communication-react/{ChatMessageComponentAsRichTextEditBox-CmrzlNzS.js → ChatMessageComponentAsRichTextEditBox-C3t87AZz.js} +3 -3
- package/dist/dist-cjs/communication-react/{ChatMessageComponentAsRichTextEditBox-CmrzlNzS.js.map → ChatMessageComponentAsRichTextEditBox-C3t87AZz.js.map} +1 -1
- package/dist/dist-cjs/communication-react/{index-CC43HCUd.js → index-BP7335hI.js} +207 -71
- package/dist/dist-cjs/communication-react/index-BP7335hI.js.map +1 -0
- package/dist/dist-cjs/communication-react/index.js +2 -2
- package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js +1 -1
- package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js.map +1 -1
- package/dist/dist-esm/calling-stateful-client/src/DeviceManagerDeclarative.js +68 -4
- package/dist/dist-esm/calling-stateful-client/src/DeviceManagerDeclarative.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/RichTextEditor/Buttons/RichTextRibbonButtons.js +8 -4
- package/dist/dist-esm/react-components/src/components/RichTextEditor/Buttons/RichTextRibbonButtons.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/RichTextEditor/Plugins/CopyPastePlugin.d.ts +9 -2
- package/dist/dist-esm/react-components/src/components/RichTextEditor/Plugins/CopyPastePlugin.js +44 -2
- package/dist/dist-esm/react-components/src/components/RichTextEditor/Plugins/CopyPastePlugin.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/RichTextEditor/RichTextEditor.d.ts +14 -0
- package/dist/dist-esm/react-components/src/components/RichTextEditor/RichTextEditor.js +5 -0
- package/dist/dist-esm/react-components/src/components/RichTextEditor/RichTextEditor.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/RichTextEditor/RichTextSendBox.js +49 -13
- package/dist/dist-esm/react-components/src/components/RichTextEditor/RichTextSendBox.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/VideoGallery/DefaultLayout.js +3 -2
- package/dist/dist-esm/react-components/src/components/VideoGallery/DefaultLayout.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/VideoGallery/FloatingLocalVideoLayout.js +3 -2
- package/dist/dist-esm/react-components/src/components/VideoGallery/FloatingLocalVideoLayout.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/VideoGallery/LargeGalleryLayout.js +1 -2
- package/dist/dist-esm/react-components/src/components/VideoGallery/LargeGalleryLayout.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/VideoGallery/SpeakerVideoLayout.js +1 -1
- package/dist/dist-esm/react-components/src/components/VideoGallery/SpeakerVideoLayout.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/VideoGallery/utils/videoGalleryLayoutUtils.d.ts +5 -1
- package/dist/dist-esm/react-components/src/components/VideoGallery/utils/videoGalleryLayoutUtils.js +17 -17
- package/dist/dist-esm/react-components/src/components/VideoGallery/utils/videoGalleryLayoutUtils.js.map +1 -1
- package/dist/dist-esm/react-components/src/components/styles/RichTextEditor.styles.d.ts +1 -1
- package/dist/dist-esm/react-components/src/components/styles/RichTextEditor.styles.js +10 -21
- package/dist/dist-esm/react-components/src/components/styles/RichTextEditor.styles.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/CallComposite/adapter/CallAdapter.d.ts +1 -1
- package/dist/dist-esm/react-composites/src/composites/CallComposite/adapter/CallAdapter.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/CallComposite/components/LocalDeviceSettings.js +2 -6
- package/dist/dist-esm/react-composites/src/composites/CallComposite/components/LocalDeviceSettings.js.map +1 -1
- package/dist/dist-esm/react-composites/src/composites/common/SidePaneHeader.js +1 -1
- package/dist/dist-esm/react-composites/src/composites/common/SidePaneHeader.js.map +1 -1
- package/package.json +1 -1
- package/dist/dist-cjs/communication-react/index-CC43HCUd.js.map +0 -1
@@ -1,6 +1,6 @@
|
|
1
1
|
'use strict';
|
2
2
|
|
3
|
-
var index = require('./index-
|
3
|
+
var index = require('./index-BP7335hI.js');
|
4
4
|
require('react');
|
5
5
|
require('@fluentui/react');
|
6
6
|
require('@fluentui/react-components');
|
@@ -20,9 +20,9 @@ require('@fluentui/react-file-type-icons');
|
|
20
20
|
require('@griffel/react');
|
21
21
|
require('roosterjs-editor-plugins');
|
22
22
|
require('roosterjs-editor-core');
|
23
|
+
require('roosterjs-editor-types-compatible');
|
23
24
|
require('roosterjs-react');
|
24
25
|
require('roosterjs-editor-dom');
|
25
|
-
require('roosterjs-editor-types-compatible');
|
26
26
|
require('roosterjs-editor-api');
|
27
27
|
require('uuid');
|
28
28
|
require('html-react-parser');
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"telemetryVersion.js","sourceRoot":"","sources":["../../../../../acs-ui-common/src/telemetryVersion.js"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,kCAAkC;AAElC,wCAAwC;AAExC,MAAM,CAAC,OAAO,GAAG,2BAA2B,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\n// GENERATED FILE. DO NOT EDIT MANUALLY.\n\nmodule.exports = '1.16.0-alpha-
|
1
|
+
{"version":3,"file":"telemetryVersion.js","sourceRoot":"","sources":["../../../../../acs-ui-common/src/telemetryVersion.js"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,kCAAkC;AAElC,wCAAwC;AAExC,MAAM,CAAC,OAAO,GAAG,2BAA2B,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\n// GENERATED FILE. DO NOT EDIT MANUALLY.\n\nmodule.exports = '1.16.0-alpha-202404180013';\n"]}
|
@@ -31,6 +31,24 @@ class ProxyDeviceManager {
|
|
31
31
|
this._deviceManager.on('audioDevicesUpdated', this.audioDevicesUpdated);
|
32
32
|
this._deviceManager.on('selectedMicrophoneChanged', this.selectedMicrophoneChanged);
|
33
33
|
this._deviceManager.on('selectedSpeakerChanged', this.selectedSpeakerChanged);
|
34
|
+
// Subscribe to browser camera permissions changes
|
35
|
+
try {
|
36
|
+
navigator.permissions.query({ name: 'camera' }).then((cameraPermissions) => {
|
37
|
+
cameraPermissions.addEventListener('change', this.permissionsApiStateChangeHandler);
|
38
|
+
});
|
39
|
+
}
|
40
|
+
catch (e) {
|
41
|
+
console.info('Could not subscribe to Permissions API Camera changed events, API is not supported by browser', e);
|
42
|
+
}
|
43
|
+
// Subscribe to browser microphone permissions changes
|
44
|
+
try {
|
45
|
+
navigator.permissions.query({ name: 'microphone' }).then((micPermissions) => {
|
46
|
+
micPermissions.addEventListener('change', this.permissionsApiStateChangeHandler);
|
47
|
+
});
|
48
|
+
}
|
49
|
+
catch (e) {
|
50
|
+
console.info('Could not subscribe to Permissions API Microphone changed events, API is not supported by browser', e);
|
51
|
+
}
|
34
52
|
};
|
35
53
|
/**
|
36
54
|
* This is used to unsubscribe DeclarativeDeviceManager from the DeviceManager events.
|
@@ -40,7 +58,28 @@ class ProxyDeviceManager {
|
|
40
58
|
this._deviceManager.off('audioDevicesUpdated', this.audioDevicesUpdated);
|
41
59
|
this._deviceManager.off('selectedMicrophoneChanged', this.selectedMicrophoneChanged);
|
42
60
|
this._deviceManager.off('selectedSpeakerChanged', this.selectedSpeakerChanged);
|
61
|
+
// Unsubscribe from browser camera permissions changes
|
62
|
+
try {
|
63
|
+
navigator.permissions.query({ name: 'camera' }).then((cameraPermissions) => {
|
64
|
+
cameraPermissions.removeEventListener('change', this.permissionsApiStateChangeHandler);
|
65
|
+
});
|
66
|
+
}
|
67
|
+
catch (e) {
|
68
|
+
console.info('Could not Unsubscribe to Permissions API Camera changed events, API is not supported by browser', e);
|
69
|
+
}
|
70
|
+
// Unsubscribe from browser microphone permissions changes
|
71
|
+
try {
|
72
|
+
navigator.permissions.query({ name: 'microphone' }).then((micPermissions) => {
|
73
|
+
micPermissions.removeEventListener('change', this.permissionsApiStateChangeHandler);
|
74
|
+
});
|
75
|
+
}
|
76
|
+
catch (e) {
|
77
|
+
console.info('Could not Unsubscribe to Permissions API Camera changed events, API is not supported by browser', e);
|
78
|
+
}
|
43
79
|
};
|
80
|
+
this.permissionsApiStateChangeHandler = () => __awaiter(this, void 0, void 0, function* () {
|
81
|
+
yield this.updateDevicePermissionState();
|
82
|
+
});
|
44
83
|
/**
|
45
84
|
* Used to set a camera inside the proxy device manager.
|
46
85
|
*
|
@@ -65,6 +104,32 @@ class ProxyDeviceManager {
|
|
65
104
|
this.selectedSpeakerChanged = () => {
|
66
105
|
this._context.setDeviceManagerSelectedSpeaker(this._deviceManager.selectedSpeaker);
|
67
106
|
};
|
107
|
+
this.updateDevicePermissionState = (sdkDeviceAccessState) => __awaiter(this, void 0, void 0, function* () {
|
108
|
+
let hasCameraPermission = !!(sdkDeviceAccessState === null || sdkDeviceAccessState === void 0 ? void 0 : sdkDeviceAccessState.video);
|
109
|
+
let hasMicPermission = !!(sdkDeviceAccessState === null || sdkDeviceAccessState === void 0 ? void 0 : sdkDeviceAccessState.audio);
|
110
|
+
// Supplement the SDK values with values from the Permissions API to get a better understanding of the device
|
111
|
+
// permission state. The SDK only uses the getUserMedia API to determine the device permission state. However,
|
112
|
+
// this returns false if the camera is in use by another application. The Permissions API can provide more
|
113
|
+
// information about the device permission state, but is not supported yet in Firefox or Android WebView.
|
114
|
+
// Note: It also has the limitation where it cannot detect if the device is blocked by the Operating System
|
115
|
+
// permissions.
|
116
|
+
try {
|
117
|
+
const [cameraPermissions, micPermissions] = yield Promise.all([
|
118
|
+
navigator.permissions.query({ name: 'camera' }),
|
119
|
+
navigator.permissions.query({ name: 'microphone' })
|
120
|
+
]);
|
121
|
+
hasCameraPermission = cameraPermissions.state === 'granted';
|
122
|
+
hasMicPermission = micPermissions.state === 'granted';
|
123
|
+
}
|
124
|
+
catch (e) {
|
125
|
+
console.info('Permissions API is not supported by browser', e);
|
126
|
+
}
|
127
|
+
this._context.setDeviceManagerDeviceAccess({
|
128
|
+
video: hasCameraPermission,
|
129
|
+
audio: hasMicPermission
|
130
|
+
});
|
131
|
+
this.setDeviceManager();
|
132
|
+
});
|
68
133
|
this._deviceManager = deviceManager;
|
69
134
|
this._context = context;
|
70
135
|
this.setDeviceManager();
|
@@ -115,11 +180,10 @@ class ProxyDeviceManager {
|
|
115
180
|
}
|
116
181
|
case 'askDevicePermission': {
|
117
182
|
return this._context.withAsyncErrorTeedToState((...args) => {
|
118
|
-
return target.askDevicePermission(...args).then((deviceAccess) => {
|
119
|
-
this.
|
120
|
-
this.setDeviceManager();
|
183
|
+
return target.askDevicePermission(...args).then((deviceAccess) => __awaiter(this, void 0, void 0, function* () {
|
184
|
+
yield this.updateDevicePermissionState(deviceAccess);
|
121
185
|
return deviceAccess;
|
122
|
-
});
|
186
|
+
}));
|
123
187
|
}, 'DeviceManager.askDevicePermission');
|
124
188
|
}
|
125
189
|
default:
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"DeviceManagerDeclarative.js","sourceRoot":"","sources":["../../../../../calling-stateful-client/src/DeviceManagerDeclarative.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport { AudioDeviceInfo, DeviceAccess, DeviceManager, VideoDeviceInfo } from '@azure/communication-calling';\nimport { CallContext } from './CallContext';\nimport { InternalCallContext } from './InternalCallContext';\n\nimport { LocalVideoStream } from '@azure/communication-calling';\n\n/**\n * Defines the additional methods added by the stateful on top of {@link @azure/communication-calling#DeviceManager}.\n *\n * @public\n */\nexport interface StatefulDeviceManager extends DeviceManager {\n /**\n * Sets the selectedCamera in the {@link DeviceManagerState}. This is completely developer driven and is not tied in\n * any way to {@link @azure/communication-calling#DeviceManager}. It is entirely contained in\n * {@link StatefulDeviceManager}. See also {@link DeviceManagerState.selectedCamera}.\n */\n selectCamera: (device: VideoDeviceInfo) => void;\n\n /**\n * Gets the list of unparented video streams. This is a list of video streams that have not been added to a\n * {@link @azure/communication-calling#Call}. This is useful for developers who want to interact with rendered\n * video streams before they have started a call. See also {@link @azure/communication-react#CallClient.createView}.\n *\n * @public\n */\n getUnparentedVideoStreams: () => LocalVideoStream[];\n}\n\n/**\n * ProxyDeviceManager proxies DeviceManager and subscribes to all events that affect device manager state. State updates\n * are set on the provided context. Also any queries for state are proxied and stored in state as well. Only one device\n * manager should exist for a given CallClient so if CallClient.getDeviceManager is called multiple times, either a\n * cached ProxyDeviceManager should be returned or the existing ProxyDeviceManager should be destructed via destructor()\n * and a new ProxyDeviceManager created.\n */\nclass ProxyDeviceManager implements ProxyHandler<DeviceManager> {\n private _deviceManager: DeviceManager;\n private _context: CallContext;\n\n constructor(deviceManager: DeviceManager, context: CallContext) {\n this._deviceManager = deviceManager;\n this._context = context;\n\n this.setDeviceManager();\n this.subscribe();\n }\n\n private setDeviceManager = (): void => {\n // isSpeakerSelectionAvailable, selectedMicrophone, and selectedSpeaker are properties on DeviceManager. Since they\n // are not functions we can't proxy them so we'll update whenever we think they may need updating such as at\n // construction time or when certain events happen.\n this._context.setDeviceManagerIsSpeakerSelectionAvailable(this._deviceManager.isSpeakerSelectionAvailable);\n this._context.setDeviceManagerSelectedMicrophone(this._deviceManager.selectedMicrophone);\n this._context.setDeviceManagerSelectedSpeaker(this._deviceManager.selectedSpeaker);\n };\n\n private subscribe = (): void => {\n this._deviceManager.on('videoDevicesUpdated', this.videoDevicesUpdated);\n this._deviceManager.on('audioDevicesUpdated', this.audioDevicesUpdated);\n this._deviceManager.on('selectedMicrophoneChanged', this.selectedMicrophoneChanged);\n this._deviceManager.on('selectedSpeakerChanged', this.selectedSpeakerChanged);\n };\n\n /**\n * This is used to unsubscribe DeclarativeDeviceManager from the DeviceManager events.\n */\n public unsubscribe = (): void => {\n this._deviceManager.off('videoDevicesUpdated', this.videoDevicesUpdated);\n this._deviceManager.off('audioDevicesUpdated', this.audioDevicesUpdated);\n this._deviceManager.off('selectedMicrophoneChanged', this.selectedMicrophoneChanged);\n this._deviceManager.off('selectedSpeakerChanged', this.selectedSpeakerChanged);\n };\n\n /**\n * Used to set a camera inside the proxy device manager.\n *\n * @param videoDeviceInfo VideoDeviceInfo\n */\n public selectCamera = (videoDeviceInfo: VideoDeviceInfo): void => {\n this._context.setDeviceManagerSelectedCamera(videoDeviceInfo);\n };\n\n private videoDevicesUpdated = async (): Promise<void> => {\n // Device Manager always has a camera with '' name if there are no real camera devices available.\n // We don't want to show that in the UI.\n const realCameras = (await this._deviceManager.getCameras()).filter((c) => !!c.name);\n this._context.setDeviceManagerCameras(dedupeById(realCameras));\n };\n\n private audioDevicesUpdated = async (): Promise<void> => {\n this._context.setDeviceManagerMicrophones(dedupeById(await this._deviceManager.getMicrophones()));\n this._context.setDeviceManagerSpeakers(dedupeById(await this._deviceManager.getSpeakers()));\n };\n\n private selectedMicrophoneChanged = (): void => {\n this._context.setDeviceManagerSelectedMicrophone(this._deviceManager.selectedMicrophone);\n };\n\n private selectedSpeakerChanged = (): void => {\n this._context.setDeviceManagerSelectedSpeaker(this._deviceManager.selectedSpeaker);\n };\n\n public get<P extends keyof DeviceManager>(target: DeviceManager, prop: P): any {\n switch (prop) {\n case 'getCameras': {\n return this._context.withAsyncErrorTeedToState((): Promise<VideoDeviceInfo[]> => {\n return target.getCameras().then((cameras: VideoDeviceInfo[]) => {\n // Device Manager always has a camera with '' name if there are no real camera devices available.\n // We don't want to show that in the UI.\n const realCameras = cameras.filter((c) => !!c.name);\n this._context.setDeviceManagerCameras(dedupeById(realCameras));\n return realCameras;\n });\n }, 'DeviceManager.getCameras');\n }\n case 'getMicrophones': {\n return this._context.withAsyncErrorTeedToState((): Promise<AudioDeviceInfo[]> => {\n return target.getMicrophones().then((microphones: AudioDeviceInfo[]) => {\n this._context.setDeviceManagerMicrophones(dedupeById(microphones));\n return microphones;\n });\n }, 'DeviceManager.getMicrophones');\n }\n case 'getSpeakers': {\n return this._context.withAsyncErrorTeedToState((): Promise<AudioDeviceInfo[]> => {\n return target.getSpeakers().then((speakers: AudioDeviceInfo[]) => {\n this._context.setDeviceManagerSpeakers(dedupeById(speakers));\n return speakers;\n });\n }, 'DeviceManager.getSpeakers');\n }\n case 'selectMicrophone': {\n return this._context.withAsyncErrorTeedToState(\n (...args: Parameters<DeviceManager['selectMicrophone']>): Promise<void> => {\n return target.selectMicrophone(...args).then(() => {\n this._context.setDeviceManagerSelectedMicrophone(target.selectedMicrophone);\n });\n },\n 'DeviceManager.selectMicrophone'\n );\n }\n case 'selectSpeaker': {\n return this._context.withAsyncErrorTeedToState(\n (...args: Parameters<DeviceManager['selectSpeaker']>): Promise<void> => {\n return target.selectSpeaker(...args).then(() => {\n this._context.setDeviceManagerSelectedSpeaker(target.selectedSpeaker);\n });\n },\n 'DeviceManager.selectSpeaker'\n );\n }\n case 'askDevicePermission': {\n return this._context.withAsyncErrorTeedToState(\n (...args: Parameters<DeviceManager['askDevicePermission']>): Promise<DeviceAccess> => {\n return target.askDevicePermission(...args).then((deviceAccess: DeviceAccess) => {\n this._context.setDeviceManagerDeviceAccess(deviceAccess);\n this.setDeviceManager();\n return deviceAccess;\n });\n },\n 'DeviceManager.askDevicePermission'\n );\n }\n default:\n return Reflect.get(target, prop);\n }\n }\n}\n\n// TODO: Remove this when SDK no longer returns duplicate audio and video devices\n/** Helper function to dedupe duplicate audio and video devices obtained from SDK */\nconst dedupeById = <T extends { id: string }>(devices: T[]): T[] => {\n const ids = new Set();\n const uniqueDevices: T[] = [];\n devices.forEach((device: T) => {\n if (!ids.has(device.id)) {\n uniqueDevices.push(device);\n ids.add(device.id);\n }\n });\n return uniqueDevices;\n};\n\n/**\n * Creates a declarative DeviceManager by proxying DeviceManager with ProxyDeviceManager. The declarative DeviceManager\n * will put state updates in the given context.\n *\n * @param deviceManager - DeviceManager from SDK\n * @param context - CallContext from StatefulCallClient\n *\n * @private\n */\nexport const deviceManagerDeclaratify = (\n deviceManager: DeviceManager,\n context: CallContext,\n internalContext: InternalCallContext\n): DeviceManager => {\n const proxyDeviceManager = new ProxyDeviceManager(deviceManager, context);\n Object.defineProperty(deviceManager, 'unsubscribe', {\n configurable: false,\n value: () => proxyDeviceManager.unsubscribe()\n });\n Object.defineProperty(deviceManager, 'selectCamera', {\n configurable: false,\n value: (videoDeviceInfo: VideoDeviceInfo) => proxyDeviceManager.selectCamera(videoDeviceInfo)\n });\n\n Object.defineProperty(deviceManager, 'getUnparentedVideoStreams', {\n configurable: false,\n value: (): LocalVideoStream[] => internalContext.getUnparentedRenderInfos()\n });\n return new Proxy(deviceManager, proxyDeviceManager) as StatefulDeviceManager;\n};\n"]}
|
1
|
+
{"version":3,"file":"DeviceManagerDeclarative.js","sourceRoot":"","sources":["../../../../../calling-stateful-client/src/DeviceManagerDeclarative.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport { AudioDeviceInfo, DeviceAccess, DeviceManager, VideoDeviceInfo } from '@azure/communication-calling';\nimport { CallContext } from './CallContext';\nimport { InternalCallContext } from './InternalCallContext';\n\nimport { LocalVideoStream } from '@azure/communication-calling';\n\n/**\n * Defines the additional methods added by the stateful on top of {@link @azure/communication-calling#DeviceManager}.\n *\n * @public\n */\nexport interface StatefulDeviceManager extends DeviceManager {\n /**\n * Sets the selectedCamera in the {@link DeviceManagerState}. This is completely developer driven and is not tied in\n * any way to {@link @azure/communication-calling#DeviceManager}. It is entirely contained in\n * {@link StatefulDeviceManager}. See also {@link DeviceManagerState.selectedCamera}.\n */\n selectCamera: (device: VideoDeviceInfo) => void;\n\n /**\n * Gets the list of unparented video streams. This is a list of video streams that have not been added to a\n * {@link @azure/communication-calling#Call}. This is useful for developers who want to interact with rendered\n * video streams before they have started a call. See also {@link @azure/communication-react#CallClient.createView}.\n *\n * @public\n */\n getUnparentedVideoStreams: () => LocalVideoStream[];\n}\n\n/**\n * ProxyDeviceManager proxies DeviceManager and subscribes to all events that affect device manager state. State updates\n * are set on the provided context. Also any queries for state are proxied and stored in state as well. Only one device\n * manager should exist for a given CallClient so if CallClient.getDeviceManager is called multiple times, either a\n * cached ProxyDeviceManager should be returned or the existing ProxyDeviceManager should be destructed via destructor()\n * and a new ProxyDeviceManager created.\n */\nclass ProxyDeviceManager implements ProxyHandler<DeviceManager> {\n private _deviceManager: DeviceManager;\n private _context: CallContext;\n\n constructor(deviceManager: DeviceManager, context: CallContext) {\n this._deviceManager = deviceManager;\n this._context = context;\n\n this.setDeviceManager();\n this.subscribe();\n }\n\n private setDeviceManager = (): void => {\n // isSpeakerSelectionAvailable, selectedMicrophone, and selectedSpeaker are properties on DeviceManager. Since they\n // are not functions we can't proxy them so we'll update whenever we think they may need updating such as at\n // construction time or when certain events happen.\n this._context.setDeviceManagerIsSpeakerSelectionAvailable(this._deviceManager.isSpeakerSelectionAvailable);\n this._context.setDeviceManagerSelectedMicrophone(this._deviceManager.selectedMicrophone);\n this._context.setDeviceManagerSelectedSpeaker(this._deviceManager.selectedSpeaker);\n };\n\n private subscribe = (): void => {\n this._deviceManager.on('videoDevicesUpdated', this.videoDevicesUpdated);\n this._deviceManager.on('audioDevicesUpdated', this.audioDevicesUpdated);\n this._deviceManager.on('selectedMicrophoneChanged', this.selectedMicrophoneChanged);\n this._deviceManager.on('selectedSpeakerChanged', this.selectedSpeakerChanged);\n\n // Subscribe to browser camera permissions changes\n try {\n navigator.permissions.query({ name: 'camera' as PermissionName }).then((cameraPermissions): void => {\n cameraPermissions.addEventListener('change', this.permissionsApiStateChangeHandler);\n });\n } catch (e) {\n console.info('Could not subscribe to Permissions API Camera changed events, API is not supported by browser', e);\n }\n\n // Subscribe to browser microphone permissions changes\n try {\n navigator.permissions.query({ name: 'microphone' as PermissionName }).then((micPermissions): void => {\n micPermissions.addEventListener('change', this.permissionsApiStateChangeHandler);\n });\n } catch (e) {\n console.info(\n 'Could not subscribe to Permissions API Microphone changed events, API is not supported by browser',\n e\n );\n }\n };\n\n /**\n * This is used to unsubscribe DeclarativeDeviceManager from the DeviceManager events.\n */\n public unsubscribe = (): void => {\n this._deviceManager.off('videoDevicesUpdated', this.videoDevicesUpdated);\n this._deviceManager.off('audioDevicesUpdated', this.audioDevicesUpdated);\n this._deviceManager.off('selectedMicrophoneChanged', this.selectedMicrophoneChanged);\n this._deviceManager.off('selectedSpeakerChanged', this.selectedSpeakerChanged);\n\n // Unsubscribe from browser camera permissions changes\n try {\n navigator.permissions.query({ name: 'camera' as PermissionName }).then((cameraPermissions): void => {\n cameraPermissions.removeEventListener('change', this.permissionsApiStateChangeHandler);\n });\n } catch (e) {\n console.info(\n 'Could not Unsubscribe to Permissions API Camera changed events, API is not supported by browser',\n e\n );\n }\n\n // Unsubscribe from browser microphone permissions changes\n try {\n navigator.permissions.query({ name: 'microphone' as PermissionName }).then((micPermissions): void => {\n micPermissions.removeEventListener('change', this.permissionsApiStateChangeHandler);\n });\n } catch (e) {\n console.info(\n 'Could not Unsubscribe to Permissions API Camera changed events, API is not supported by browser',\n e\n );\n }\n };\n\n private permissionsApiStateChangeHandler = async (): Promise<void> => {\n await this.updateDevicePermissionState();\n };\n\n /**\n * Used to set a camera inside the proxy device manager.\n *\n * @param videoDeviceInfo VideoDeviceInfo\n */\n public selectCamera = (videoDeviceInfo: VideoDeviceInfo): void => {\n this._context.setDeviceManagerSelectedCamera(videoDeviceInfo);\n };\n\n private videoDevicesUpdated = async (): Promise<void> => {\n // Device Manager always has a camera with '' name if there are no real camera devices available.\n // We don't want to show that in the UI.\n const realCameras = (await this._deviceManager.getCameras()).filter((c) => !!c.name);\n this._context.setDeviceManagerCameras(dedupeById(realCameras));\n };\n\n private audioDevicesUpdated = async (): Promise<void> => {\n this._context.setDeviceManagerMicrophones(dedupeById(await this._deviceManager.getMicrophones()));\n this._context.setDeviceManagerSpeakers(dedupeById(await this._deviceManager.getSpeakers()));\n };\n\n private selectedMicrophoneChanged = (): void => {\n this._context.setDeviceManagerSelectedMicrophone(this._deviceManager.selectedMicrophone);\n };\n\n private selectedSpeakerChanged = (): void => {\n this._context.setDeviceManagerSelectedSpeaker(this._deviceManager.selectedSpeaker);\n };\n\n private updateDevicePermissionState = async (sdkDeviceAccessState?: DeviceAccess): Promise<void> => {\n let hasCameraPermission = !!sdkDeviceAccessState?.video;\n let hasMicPermission = !!sdkDeviceAccessState?.audio;\n\n // Supplement the SDK values with values from the Permissions API to get a better understanding of the device\n // permission state. The SDK only uses the getUserMedia API to determine the device permission state. However,\n // this returns false if the camera is in use by another application. The Permissions API can provide more\n // information about the device permission state, but is not supported yet in Firefox or Android WebView.\n // Note: It also has the limitation where it cannot detect if the device is blocked by the Operating System\n // permissions.\n try {\n const [cameraPermissions, micPermissions] = await Promise.all([\n navigator.permissions.query({ name: 'camera' as PermissionName }),\n navigator.permissions.query({ name: 'microphone' as PermissionName })\n ]);\n\n hasCameraPermission = cameraPermissions.state === 'granted';\n hasMicPermission = micPermissions.state === 'granted';\n } catch (e) {\n console.info('Permissions API is not supported by browser', e);\n }\n\n this._context.setDeviceManagerDeviceAccess({\n video: hasCameraPermission,\n audio: hasMicPermission\n });\n this.setDeviceManager();\n };\n\n public get<P extends keyof DeviceManager>(target: DeviceManager, prop: P): any {\n switch (prop) {\n case 'getCameras': {\n return this._context.withAsyncErrorTeedToState((): Promise<VideoDeviceInfo[]> => {\n return target.getCameras().then((cameras: VideoDeviceInfo[]) => {\n // Device Manager always has a camera with '' name if there are no real camera devices available.\n // We don't want to show that in the UI.\n const realCameras = cameras.filter((c) => !!c.name);\n this._context.setDeviceManagerCameras(dedupeById(realCameras));\n return realCameras;\n });\n }, 'DeviceManager.getCameras');\n }\n case 'getMicrophones': {\n return this._context.withAsyncErrorTeedToState((): Promise<AudioDeviceInfo[]> => {\n return target.getMicrophones().then((microphones: AudioDeviceInfo[]) => {\n this._context.setDeviceManagerMicrophones(dedupeById(microphones));\n return microphones;\n });\n }, 'DeviceManager.getMicrophones');\n }\n case 'getSpeakers': {\n return this._context.withAsyncErrorTeedToState((): Promise<AudioDeviceInfo[]> => {\n return target.getSpeakers().then((speakers: AudioDeviceInfo[]) => {\n this._context.setDeviceManagerSpeakers(dedupeById(speakers));\n return speakers;\n });\n }, 'DeviceManager.getSpeakers');\n }\n case 'selectMicrophone': {\n return this._context.withAsyncErrorTeedToState(\n (...args: Parameters<DeviceManager['selectMicrophone']>): Promise<void> => {\n return target.selectMicrophone(...args).then(() => {\n this._context.setDeviceManagerSelectedMicrophone(target.selectedMicrophone);\n });\n },\n 'DeviceManager.selectMicrophone'\n );\n }\n case 'selectSpeaker': {\n return this._context.withAsyncErrorTeedToState(\n (...args: Parameters<DeviceManager['selectSpeaker']>): Promise<void> => {\n return target.selectSpeaker(...args).then(() => {\n this._context.setDeviceManagerSelectedSpeaker(target.selectedSpeaker);\n });\n },\n 'DeviceManager.selectSpeaker'\n );\n }\n case 'askDevicePermission': {\n return this._context.withAsyncErrorTeedToState(\n (...args: Parameters<DeviceManager['askDevicePermission']>): Promise<DeviceAccess> => {\n return target.askDevicePermission(...args).then(async (deviceAccess: DeviceAccess) => {\n await this.updateDevicePermissionState(deviceAccess);\n return deviceAccess;\n });\n },\n 'DeviceManager.askDevicePermission'\n );\n }\n default:\n return Reflect.get(target, prop);\n }\n }\n}\n\n// TODO: Remove this when SDK no longer returns duplicate audio and video devices\n/** Helper function to dedupe duplicate audio and video devices obtained from SDK */\nconst dedupeById = <T extends { id: string }>(devices: T[]): T[] => {\n const ids = new Set();\n const uniqueDevices: T[] = [];\n devices.forEach((device: T) => {\n if (!ids.has(device.id)) {\n uniqueDevices.push(device);\n ids.add(device.id);\n }\n });\n return uniqueDevices;\n};\n\n/**\n * Creates a declarative DeviceManager by proxying DeviceManager with ProxyDeviceManager. The declarative DeviceManager\n * will put state updates in the given context.\n *\n * @param deviceManager - DeviceManager from SDK\n * @param context - CallContext from StatefulCallClient\n *\n * @private\n */\nexport const deviceManagerDeclaratify = (\n deviceManager: DeviceManager,\n context: CallContext,\n internalContext: InternalCallContext\n): DeviceManager => {\n const proxyDeviceManager = new ProxyDeviceManager(deviceManager, context);\n Object.defineProperty(deviceManager, 'unsubscribe', {\n configurable: false,\n value: () => proxyDeviceManager.unsubscribe()\n });\n Object.defineProperty(deviceManager, 'selectCamera', {\n configurable: false,\n value: (videoDeviceInfo: VideoDeviceInfo) => proxyDeviceManager.selectCamera(videoDeviceInfo)\n });\n\n Object.defineProperty(deviceManager, 'getUnparentedVideoStreams', {\n configurable: false,\n value: (): LocalVideoStream[] => internalContext.getUnparentedRenderInfos()\n });\n return new Proxy(deviceManager, proxyDeviceManager) as StatefulDeviceManager;\n};\n"]}
|
@@ -1,24 +1,28 @@
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
2
2
|
// Licensed under the MIT License.
|
3
|
-
import { ContextualMenuItemType } from '@fluentui/react';
|
3
|
+
import { ContextualMenuItemType, Icon } from '@fluentui/react';
|
4
4
|
import { KnownRibbonButtonKey, getButtons } from 'roosterjs-react';
|
5
5
|
import { ribbonButtonStyle, ribbonDividerStyle } from '../../styles/RichTextEditor.styles';
|
6
6
|
import { insertTableButton } from './Table/RichTextInsertTableButton';
|
7
|
+
import React from 'react';
|
7
8
|
const MaxRowsNumber = 5;
|
8
9
|
const MaxColumnsNumber = 5;
|
9
10
|
const dividerRibbonButton = (theme, key) => {
|
10
11
|
return {
|
11
12
|
key: key,
|
12
|
-
|
13
|
+
// the icon will be set in `onRender` callback
|
14
|
+
// this is needed to make the divider unavailable for keyboard navigation
|
15
|
+
iconName: '',
|
16
|
+
// no text is needed here as we don't want to show a tooltip for the divider
|
13
17
|
unlocalizedText: '',
|
14
18
|
onClick: () => { },
|
15
19
|
isDisabled: () => true,
|
16
20
|
commandBarProperties: {
|
17
21
|
// show the item correctly for the overflow menu
|
18
22
|
itemType: ContextualMenuItemType.Divider,
|
19
|
-
buttonStyles: ribbonDividerStyle(theme),
|
20
23
|
// this is still needed to remove checkmark icon space even though it is a divider
|
21
|
-
canCheck: false
|
24
|
+
canCheck: false,
|
25
|
+
onRender: () => React.createElement(Icon, { iconName: "RichTextDividerIcon", className: ribbonDividerStyle(theme) })
|
22
26
|
}
|
23
27
|
};
|
24
28
|
};
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"RichTextRibbonButtons.js","sourceRoot":"","sources":["../../../../../../../../react-components/src/components/RichTextEditor/Buttons/RichTextRibbonButtons.tsx"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,EAAE,sBAAsB,EAAS,MAAM,iBAAiB,CAAC;
|
1
|
+
{"version":3,"file":"RichTextRibbonButtons.js","sourceRoot":"","sources":["../../../../../../../../react-components/src/components/RichTextEditor/Buttons/RichTextRibbonButtons.tsx"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport { ContextualMenuItemType, Icon, Theme } from '@fluentui/react';\nimport { KnownRibbonButtonKey, RibbonButton, getButtons } from 'roosterjs-react';\nimport { ribbonButtonStyle, ribbonDividerStyle } from '../../styles/RichTextEditor.styles';\nimport { insertTableButton } from './Table/RichTextInsertTableButton';\nimport React from 'react';\n\nconst MaxRowsNumber = 5;\nconst MaxColumnsNumber = 5;\n\nconst dividerRibbonButton = (theme: Theme, key: string): RibbonButton<string> => {\n return {\n key: key,\n // the icon will be set in `onRender` callback\n // this is needed to make the divider unavailable for keyboard navigation\n iconName: '',\n // no text is needed here as we don't want to show a tooltip for the divider\n unlocalizedText: '',\n onClick: () => {},\n isDisabled: () => true,\n commandBarProperties: {\n // show the item correctly for the overflow menu\n itemType: ContextualMenuItemType.Divider,\n // this is still needed to remove checkmark icon space even though it is a divider\n canCheck: false,\n onRender: () => <Icon iconName=\"RichTextDividerIcon\" className={ribbonDividerStyle(theme)} />\n }\n };\n};\n\nconst boldButton = (theme: Theme): RibbonButton<string> | undefined => {\n return createKnownRibbonButton(KnownRibbonButtonKey.Bold, theme, 'RichTextBoldButtonIcon');\n};\n\nconst italicButton = (theme: Theme): RibbonButton<string> | undefined => {\n return createKnownRibbonButton(KnownRibbonButtonKey.Italic, theme, 'RichTextItalicButtonIcon');\n};\n\nconst underlineButton = (theme: Theme): RibbonButton<string> | undefined => {\n return createKnownRibbonButton(KnownRibbonButtonKey.Underline, theme, 'RichTextUnderlineButtonIcon');\n};\n\nconst bulletListButton = (theme: Theme): RibbonButton<string> | undefined => {\n return createKnownRibbonButton(KnownRibbonButtonKey.BulletedList, theme, 'RichTextBulletListButtonIcon');\n};\n\nconst numberListButton = (theme: Theme): RibbonButton<string> | undefined => {\n return createKnownRibbonButton(KnownRibbonButtonKey.NumberedList, theme, 'RichTextNumberListButtonIcon');\n};\n\nconst indentIncreaseButton = (theme: Theme): RibbonButton<string> | undefined => {\n return createKnownRibbonButton(KnownRibbonButtonKey.IncreaseIndent, theme, 'RichTextIndentIncreaseButtonIcon');\n};\n\nconst indentDecreaseButton = (theme: Theme): RibbonButton<string> | undefined => {\n return createKnownRibbonButton(KnownRibbonButtonKey.DecreaseIndent, theme, 'RichTextIndentDecreaseButtonIcon');\n};\n\nconst createKnownRibbonButton = (\n key: KnownRibbonButtonKey,\n theme: Theme,\n icon: string\n): RibbonButton<string> | undefined => {\n const buttons = getButtons([key]);\n if (buttons.length > 0) {\n const button = buttons[0];\n // AllButtonStringKeys is a union of all the string keys of all the buttons\n const result = buttons[0] as RibbonButton<typeof button.key>;\n button.iconName = icon;\n button.commandBarProperties = {\n ...button.commandBarProperties,\n buttonStyles: {\n ...button.commandBarProperties?.buttonStyles,\n ...ribbonButtonStyle(theme)\n },\n canCheck: false\n };\n return result;\n }\n return undefined;\n};\n\n/**\n * @private\n */\nexport const ribbonButtons = (theme: Theme): RibbonButton<string>[] => {\n const buttons: RibbonButton<string>[] = [];\n [\n boldButton(theme),\n italicButton(theme),\n underlineButton(theme),\n dividerRibbonButton(theme, 'RichTextRibbonTextFormatDivider'),\n bulletListButton(theme),\n numberListButton(theme),\n indentDecreaseButton(theme),\n indentIncreaseButton(theme),\n dividerRibbonButton(theme, 'RichTextRibbonTableDivider'),\n insertTableButton(theme, MaxRowsNumber, MaxColumnsNumber)\n ].forEach((item) => {\n if (item !== undefined) {\n buttons.push(item);\n }\n });\n\n return buttons;\n};\n"]}
|
package/dist/dist-esm/react-components/src/components/RichTextEditor/Plugins/CopyPastePlugin.d.ts
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
import type { PluginEvent, EditorPlugin } from 'roosterjs-editor-types';
|
1
|
+
import type { PluginEvent, EditorPlugin, IEditor } from 'roosterjs-editor-types';
|
2
2
|
/**
|
3
3
|
* CopyPastePlugin is a plugin for handling copy and paste events in the editor.
|
4
4
|
*/
|
5
5
|
export default class CopyPastePlugin implements EditorPlugin {
|
6
|
+
private editor;
|
6
7
|
getName(): string;
|
7
|
-
initialize(): void;
|
8
|
+
initialize(editor: IEditor): void;
|
8
9
|
dispose(): void;
|
9
10
|
onPluginEvent(event: PluginEvent): void;
|
10
11
|
}
|
@@ -13,4 +14,10 @@ export default class CopyPastePlugin implements EditorPlugin {
|
|
13
14
|
* Exported only for unit testing
|
14
15
|
*/
|
15
16
|
export declare const removeImageElement: (event: PluginEvent) => void;
|
17
|
+
/**
|
18
|
+
* Scrolls the editor's scroll container to the bottom after content is pasted.
|
19
|
+
* @param event - The plugin event.
|
20
|
+
* @param editor - The editor instance.
|
21
|
+
*/
|
22
|
+
export declare const scrollToBottomAfterContentPaste: (event: PluginEvent, editor: IEditor) => void;
|
16
23
|
//# sourceMappingURL=CopyPastePlugin.d.ts.map
|
package/dist/dist-esm/react-components/src/components/RichTextEditor/Plugins/CopyPastePlugin.js
CHANGED
@@ -1,17 +1,27 @@
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
2
2
|
// Licensed under the MIT License.
|
3
|
-
import {
|
3
|
+
import { getPositionRect, normalizeRect } from 'roosterjs-editor-dom';
|
4
|
+
import { CompatiblePluginEventType, CompatiblePasteType, CompatibleChangeSource } from 'roosterjs-editor-types-compatible';
|
4
5
|
/**
|
5
6
|
* CopyPastePlugin is a plugin for handling copy and paste events in the editor.
|
6
7
|
*/
|
7
8
|
export default class CopyPastePlugin {
|
9
|
+
constructor() {
|
10
|
+
this.editor = null;
|
11
|
+
}
|
8
12
|
getName() {
|
9
13
|
return 'CopyPastePlugin';
|
10
14
|
}
|
11
|
-
initialize() {
|
15
|
+
initialize(editor) {
|
16
|
+
this.editor = editor;
|
17
|
+
}
|
12
18
|
dispose() { }
|
13
19
|
onPluginEvent(event) {
|
14
20
|
removeImageElement(event);
|
21
|
+
if (this.editor !== null && !this.editor.isDisposed()) {
|
22
|
+
// scroll the editor to the correct position after pasting content
|
23
|
+
scrollToBottomAfterContentPaste(event, this.editor);
|
24
|
+
}
|
15
25
|
}
|
16
26
|
}
|
17
27
|
/**
|
@@ -33,4 +43,36 @@ export const removeImageElement = (event) => {
|
|
33
43
|
});
|
34
44
|
}
|
35
45
|
};
|
46
|
+
/**
|
47
|
+
* Scrolls the editor's scroll container to the bottom after content is pasted.
|
48
|
+
* @param event - The plugin event.
|
49
|
+
* @param editor - The editor instance.
|
50
|
+
*/
|
51
|
+
export const scrollToBottomAfterContentPaste = (event, editor) => {
|
52
|
+
if (event.eventType === CompatiblePluginEventType.ContentChanged && event.source === CompatibleChangeSource.Paste) {
|
53
|
+
// current focused position in the editor
|
54
|
+
const focusedPosition = editor.getFocusedPosition();
|
55
|
+
// the cursor position relative to the viewport
|
56
|
+
const cursorRect = focusedPosition && getPositionRect(focusedPosition);
|
57
|
+
// the scroll container of the editor
|
58
|
+
const scrollContainer = editor.getScrollContainer();
|
59
|
+
// the scrollContainer position relative to the viewport
|
60
|
+
const scrollContainerRect = normalizeRect(scrollContainer.getBoundingClientRect());
|
61
|
+
if (focusedPosition !== null && cursorRect !== null && cursorRect !== undefined && scrollContainerRect !== null) {
|
62
|
+
const textElement = focusedPosition.element;
|
63
|
+
// the caret height is typically the same as the font size of the text
|
64
|
+
const caretHeight = parseFloat(window.getComputedStyle(textElement).fontSize);
|
65
|
+
// 1. scrollContainer.scrollTop represents the number of pixels that the content of scrollContainer is scrolled upward.
|
66
|
+
// 2. subtract the top position of the scrollContainer element (scrollContainerRect.top) to
|
67
|
+
// translate the scroll position from being relative to the document to being relative to the viewport.
|
68
|
+
// 3. add the top position of the cursor (cursorRect.top) to moves the scroll position to the cursor's position.
|
69
|
+
// 4. subtract a caret height to add some space between the cursor and the top edge of the scrollContainer.
|
70
|
+
const updatedScrollTop = scrollContainer.scrollTop - scrollContainerRect.top + cursorRect.top - caretHeight;
|
71
|
+
scrollContainer.scrollTo({
|
72
|
+
top: updatedScrollTop,
|
73
|
+
behavior: 'smooth'
|
74
|
+
});
|
75
|
+
}
|
76
|
+
}
|
77
|
+
};
|
36
78
|
//# sourceMappingURL=CopyPastePlugin.js.map
|
package/dist/dist-esm/react-components/src/components/RichTextEditor/Plugins/CopyPastePlugin.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"CopyPastePlugin.js","sourceRoot":"","sources":["../../../../../../../../react-components/src/components/RichTextEditor/Plugins/CopyPastePlugin.tsx"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;
|
1
|
+
{"version":3,"file":"CopyPastePlugin.js","sourceRoot":"","sources":["../../../../../../../../react-components/src/components/RichTextEditor/Plugins/CopyPastePlugin.tsx"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport { getPositionRect, normalizeRect } from 'roosterjs-editor-dom';\nimport type { PluginEvent, EditorPlugin, IEditor } from 'roosterjs-editor-types';\nimport {\n CompatiblePluginEventType,\n CompatiblePasteType,\n CompatibleChangeSource\n} from 'roosterjs-editor-types-compatible';\n\n/**\n * CopyPastePlugin is a plugin for handling copy and paste events in the editor.\n */\nexport default class CopyPastePlugin implements EditorPlugin {\n private editor: IEditor | null = null;\n\n getName(): string {\n return 'CopyPastePlugin';\n }\n\n initialize(editor: IEditor): void {\n this.editor = editor;\n }\n\n dispose(): void {}\n\n onPluginEvent(event: PluginEvent): void {\n removeImageElement(event);\n\n if (this.editor !== null && !this.editor.isDisposed()) {\n // scroll the editor to the correct position after pasting content\n scrollToBottomAfterContentPaste(event, this.editor);\n }\n }\n}\n\n/**\n * @internal\n * Exported only for unit testing\n */\nexport const removeImageElement = (event: PluginEvent): void => {\n // We don't support the pasting options such as paste as image yet.\n if (event.eventType === CompatiblePluginEventType.BeforePaste && event.pasteType === CompatiblePasteType.Normal) {\n event.fragment.querySelectorAll('img').forEach((image) => {\n // If the image is the only child of its parent, remove all the parents of this img element.\n let parentNode: HTMLElement | null = image.parentElement;\n let currentNode: HTMLElement = image;\n while (parentNode?.childNodes.length === 1) {\n currentNode = parentNode;\n parentNode = parentNode.parentElement;\n }\n currentNode?.remove();\n });\n }\n};\n\n/**\n * Scrolls the editor's scroll container to the bottom after content is pasted.\n * @param event - The plugin event.\n * @param editor - The editor instance.\n */\nexport const scrollToBottomAfterContentPaste = (event: PluginEvent, editor: IEditor): void => {\n if (event.eventType === CompatiblePluginEventType.ContentChanged && event.source === CompatibleChangeSource.Paste) {\n // current focused position in the editor\n const focusedPosition = editor.getFocusedPosition();\n // the cursor position relative to the viewport\n const cursorRect = focusedPosition && getPositionRect(focusedPosition);\n // the scroll container of the editor\n const scrollContainer = editor.getScrollContainer();\n // the scrollContainer position relative to the viewport\n const scrollContainerRect = normalizeRect(scrollContainer.getBoundingClientRect());\n if (focusedPosition !== null && cursorRect !== null && cursorRect !== undefined && scrollContainerRect !== null) {\n const textElement = focusedPosition.element;\n // the caret height is typically the same as the font size of the text\n const caretHeight = parseFloat(window.getComputedStyle(textElement).fontSize);\n // 1. scrollContainer.scrollTop represents the number of pixels that the content of scrollContainer is scrolled upward.\n // 2. subtract the top position of the scrollContainer element (scrollContainerRect.top) to\n // translate the scroll position from being relative to the document to being relative to the viewport.\n // 3. add the top position of the cursor (cursorRect.top) to moves the scroll position to the cursor's position.\n // 4. subtract a caret height to add some space between the cursor and the top edge of the scrollContainer.\n const updatedScrollTop = scrollContainer.scrollTop - scrollContainerRect.top + cursorRect.top - caretHeight;\n scrollContainer.scrollTo({\n top: updatedScrollTop,\n behavior: 'smooth'\n });\n }\n }\n};\n"]}
|
@@ -30,9 +30,23 @@ export interface RichTextEditorProps {
|
|
30
30
|
*
|
31
31
|
* @beta
|
32
32
|
*/
|
33
|
+
/**
|
34
|
+
* Represents a reference to the RichTextEditor component.
|
35
|
+
*/
|
33
36
|
export interface RichTextEditorComponentRef {
|
37
|
+
/**
|
38
|
+
* Sets focus on the RichTextEditor component.
|
39
|
+
*/
|
34
40
|
focus: () => void;
|
41
|
+
/**
|
42
|
+
* Sets the content of the RichTextEditor component to an empty string.
|
43
|
+
*/
|
35
44
|
setEmptyContent: () => void;
|
45
|
+
/**
|
46
|
+
* Retrieves the plain text content of the RichTextEditor component.
|
47
|
+
* @returns The plain text content of the RichTextEditor component, or undefined if the editor isn't available.
|
48
|
+
*/
|
49
|
+
getPlainContent: () => string | undefined;
|
36
50
|
}
|
37
51
|
/**
|
38
52
|
* A component to wrap RoosterJS Rich Text Editor.
|
@@ -3,6 +3,7 @@
|
|
3
3
|
import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react';
|
4
4
|
import { ContentEdit, Watermark } from 'roosterjs-editor-plugins';
|
5
5
|
import { Editor } from 'roosterjs-editor-core';
|
6
|
+
import { CompatibleGetContentMode } from 'roosterjs-editor-types-compatible';
|
6
7
|
import { Rooster, createUpdateContentPlugin, UpdateMode, createRibbonPlugin, Ribbon, createContextMenuPlugin } from 'roosterjs-react';
|
7
8
|
import { ribbonButtonStyle, ribbonOverflowButtonStyle, ribbonStyle, richTextEditorWrapperStyle, richTextEditorStyle } from '../styles/RichTextEditor.styles';
|
8
9
|
import { useTheme } from '../../theming';
|
@@ -32,6 +33,10 @@ export const RichTextEditor = React.forwardRef((props, ref) => {
|
|
32
33
|
if (editor.current) {
|
33
34
|
editor.current.setContent('');
|
34
35
|
}
|
36
|
+
},
|
37
|
+
getPlainContent() {
|
38
|
+
var _a;
|
39
|
+
return (_a = editor === null || editor === void 0 ? void 0 : editor.current) === null || _a === void 0 ? void 0 : _a.getContent(CompatibleGetContentMode.PlainTextFast);
|
35
40
|
}
|
36
41
|
};
|
37
42
|
}, []);
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"RichTextEditor.js","sourceRoot":"","sources":["../../../../../../../react-components/src/components/RichTextEditor/RichTextEditor.tsx"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\nimport React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react';\nimport { ContentEdit, Watermark } from 'roosterjs-editor-plugins';\nimport { Editor } from 'roosterjs-editor-core';\nimport type { DefaultFormat, EditorOptions, IEditor } from 'roosterjs-editor-types-compatible';\nimport {\n Rooster,\n createUpdateContentPlugin,\n UpdateMode,\n createRibbonPlugin,\n Ribbon,\n createContextMenuPlugin\n} from 'roosterjs-react';\nimport {\n ribbonButtonStyle,\n ribbonOverflowButtonStyle,\n ribbonStyle,\n richTextEditorWrapperStyle,\n richTextEditorStyle\n} from '../styles/RichTextEditor.styles';\nimport { useTheme } from '../../theming';\nimport { ribbonButtons } from './Buttons/RichTextRibbonButtons';\nimport { RichTextSendBoxStrings } from './RichTextSendBox';\nimport { isDarkThemed } from '../../theming/themeUtils';\nimport { ribbonButtonsStrings } from '../utils/RichTextEditorStringsUtils';\nimport { createTableEditMenuProvider } from './Buttons/Table/RichTextTableContextMenu';\nimport CopyPastePlugin from './Plugins/CopyPastePlugin';\n\n/**\n * Props for {@link RichTextEditor}.\n *\n * @private\n */\nexport interface RichTextEditorStyleProps {\n minHeight: string;\n maxHeight: string;\n}\n\n/**\n * Props for {@link RichTextEditor}.\n *\n * @private\n */\nexport interface RichTextEditorProps {\n // the initial content of editor that is set when editor is created (e.g. when editing a message)\n initialContent?: string;\n // the current content of the editor\n content?: string;\n onChange: (newValue?: string) => void;\n onKeyDown?: (ev: React.KeyboardEvent<HTMLElement>) => void;\n placeholderText?: string;\n strings: Partial<RichTextSendBoxStrings>;\n showRichTextEditorFormatting: boolean;\n styles: RichTextEditorStyleProps;\n autoFocus?: 'sendBoxTextField';\n}\n\n/**\n * Props for {@link RichTextEditor}.\n *\n * @beta\n */\nexport interface RichTextEditorComponentRef {\n focus: () => void;\n setEmptyContent: () => void;\n}\n\n/**\n * A component to wrap RoosterJS Rich Text Editor.\n *\n * @beta\n */\nexport const RichTextEditor = React.forwardRef<RichTextEditorComponentRef, RichTextEditorProps>((props, ref) => {\n const { initialContent, onChange, placeholderText, strings, showRichTextEditorFormatting, content, autoFocus } =\n props;\n const editor = useRef<IEditor | null>(null);\n const contentValue = useRef<string | undefined>(content);\n const theme = useTheme();\n useImperativeHandle(\n ref,\n () => {\n return {\n focus() {\n if (editor.current) {\n editor.current.focus();\n }\n },\n setEmptyContent() {\n if (editor.current) {\n editor.current.setContent('');\n }\n }\n };\n },\n []\n );\n\n const ribbonPlugin = React.useMemo(() => {\n return createRibbonPlugin();\n }, []);\n\n const editorCreator = useCallback(\n (div: HTMLDivElement, options: EditorOptions) => {\n const editorValue = new Editor(div, options);\n // this is to fix issue when editor is created or re-rendered and has existing text\n // Content model package has a correct behavior and this fix can be deleted\n if (contentValue.current !== undefined && contentValue.current.length > 0) {\n // in case if initialContent is not empty, RoosterJS doesn't set caret position to the end.\n focusAndUpdateContent(editorValue, contentValue.current);\n } else if (initialContent !== undefined && initialContent.length > 0) {\n // changing layout in rich text send box cause the editor to be recreated\n // to keep the content, we need to set messageContent to the current content\n focusAndUpdateContent(editorValue, initialContent);\n }\n editor.current = editorValue;\n return editorValue;\n },\n // trigger force editor reset when strings are changed to update context menu strings\n // see RosterJS documentation for 'editorCreator' for more details\n // the editorCreator callback shouldn't be updated when the initialContent is changed\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [strings]\n );\n\n const placeholderPlugin = React.useMemo(() => {\n return new Watermark('');\n }, []);\n\n useEffect(() => {\n if (placeholderText !== undefined) {\n placeholderPlugin.updateWatermark(placeholderText);\n }\n }, [placeholderPlugin, placeholderText]);\n\n const plugins = useMemo(() => {\n // contextPlugin and tableEditMenuProvider allow to show insert/delete menu for the table\n const contextPlugin = createContextMenuPlugin();\n const tableEditMenuProvider = createTableEditMenuProvider(strings);\n const contentEdit = new ContentEdit();\n const updateContentPlugin = createUpdateContentPlugin(\n UpdateMode.OnContentChangedEvent | UpdateMode.OnUserInput,\n (content: string) => {\n onChange && onChange(content);\n }\n );\n const copyPastePlugin = new CopyPastePlugin();\n return [\n contentEdit,\n placeholderPlugin,\n updateContentPlugin,\n ribbonPlugin,\n contextPlugin,\n tableEditMenuProvider,\n copyPastePlugin\n ];\n }, [onChange, placeholderPlugin, ribbonPlugin, strings]);\n\n const ribbon = useMemo(() => {\n const buttons = ribbonButtons(theme);\n\n return (\n <Ribbon\n styles={ribbonStyle}\n buttons={buttons}\n plugin={ribbonPlugin}\n overflowButtonProps={{\n styles: ribbonButtonStyle(theme),\n menuProps: {\n items: [], // CommandBar will determine items rendered in overflow\n isBeakVisible: false,\n styles: ribbonOverflowButtonStyle(theme)\n }\n }}\n strings={ribbonButtonsStrings(strings)}\n data-testid={'rich-text-editor-ribbon'}\n />\n );\n }, [strings, ribbonPlugin, theme]);\n\n const defaultFormat: DefaultFormat = useMemo(() => {\n // without setting any styles, text input is not handled properly for tables (when insert or paste one in the editor)\n // because of https://github.com/microsoft/roosterjs/blob/14dbb947e3ae94580109cbd05e48ceb05327c4dc/packages/roosterjs-editor-core/lib/corePlugins/TypeInContainerPlugin.ts#L75\n // this issue is fixed for content model package\n return {\n backgroundColor: 'transparent'\n };\n }, []);\n\n return (\n <div data-testid={'rich-text-editor-wrapper'}>\n {showRichTextEditorFormatting && ribbon}\n <div className={richTextEditorWrapperStyle(theme, !showRichTextEditorFormatting)}>\n <Rooster\n defaultFormat={defaultFormat}\n inDarkMode={isDarkThemed(theme)}\n plugins={plugins}\n className={richTextEditorStyle(props.styles)}\n editorCreator={editorCreator}\n // TODO: confirm the color during inline images implementation\n imageSelectionBorderColor={'blue'}\n // doNotAdjustEditorColor is used to fix the default background color for Rooster component\n doNotAdjustEditorColor={true}\n data-testid={'rooster-rich-text-editor'}\n // if we don't use 'allowKeyboardEventPropagation' only the enter key is caught\n onKeyDown={props.onKeyDown}\n focusOnInit={autoFocus === 'sendBoxTextField'}\n />\n </div>\n </div>\n );\n});\n\nconst focusAndUpdateContent = (editor: Editor, content: string): void => {\n // focus the editor to set correct selection position\n editor.focus();\n // set initial content\n editor.setContent(content);\n};\n"]}
|
1
|
+
{"version":3,"file":"RichTextEditor.js","sourceRoot":"","sources":["../../../../../../../react-components/src/components/RichTextEditor/RichTextEditor.tsx"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\nimport React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react';\nimport { ContentEdit, Watermark } from 'roosterjs-editor-plugins';\nimport { Editor } from 'roosterjs-editor-core';\nimport type { DefaultFormat, EditorOptions, IEditor } from 'roosterjs-editor-types-compatible';\nimport { CompatibleGetContentMode } from 'roosterjs-editor-types-compatible';\nimport {\n Rooster,\n createUpdateContentPlugin,\n UpdateMode,\n createRibbonPlugin,\n Ribbon,\n createContextMenuPlugin\n} from 'roosterjs-react';\nimport {\n ribbonButtonStyle,\n ribbonOverflowButtonStyle,\n ribbonStyle,\n richTextEditorWrapperStyle,\n richTextEditorStyle\n} from '../styles/RichTextEditor.styles';\nimport { useTheme } from '../../theming';\nimport { ribbonButtons } from './Buttons/RichTextRibbonButtons';\nimport { RichTextSendBoxStrings } from './RichTextSendBox';\nimport { isDarkThemed } from '../../theming/themeUtils';\nimport { ribbonButtonsStrings } from '../utils/RichTextEditorStringsUtils';\nimport { createTableEditMenuProvider } from './Buttons/Table/RichTextTableContextMenu';\nimport CopyPastePlugin from './Plugins/CopyPastePlugin';\n\n/**\n * Props for {@link RichTextEditor}.\n *\n * @private\n */\nexport interface RichTextEditorStyleProps {\n minHeight: string;\n maxHeight: string;\n}\n\n/**\n * Props for {@link RichTextEditor}.\n *\n * @private\n */\nexport interface RichTextEditorProps {\n // the initial content of editor that is set when editor is created (e.g. when editing a message)\n initialContent?: string;\n // the current content of the editor\n content?: string;\n onChange: (newValue?: string) => void;\n onKeyDown?: (ev: React.KeyboardEvent<HTMLElement>) => void;\n placeholderText?: string;\n strings: Partial<RichTextSendBoxStrings>;\n showRichTextEditorFormatting: boolean;\n styles: RichTextEditorStyleProps;\n autoFocus?: 'sendBoxTextField';\n}\n\n/**\n * Props for {@link RichTextEditor}.\n *\n * @beta\n */\n/**\n * Represents a reference to the RichTextEditor component.\n */\nexport interface RichTextEditorComponentRef {\n /**\n * Sets focus on the RichTextEditor component.\n */\n focus: () => void;\n\n /**\n * Sets the content of the RichTextEditor component to an empty string.\n */\n setEmptyContent: () => void;\n\n /**\n * Retrieves the plain text content of the RichTextEditor component.\n * @returns The plain text content of the RichTextEditor component, or undefined if the editor isn't available.\n */\n getPlainContent: () => string | undefined;\n}\n\n/**\n * A component to wrap RoosterJS Rich Text Editor.\n *\n * @beta\n */\nexport const RichTextEditor = React.forwardRef<RichTextEditorComponentRef, RichTextEditorProps>((props, ref) => {\n const { initialContent, onChange, placeholderText, strings, showRichTextEditorFormatting, content, autoFocus } =\n props;\n const editor = useRef<IEditor | null>(null);\n const contentValue = useRef<string | undefined>(content);\n const theme = useTheme();\n useImperativeHandle(\n ref,\n () => {\n return {\n focus() {\n if (editor.current) {\n editor.current.focus();\n }\n },\n setEmptyContent() {\n if (editor.current) {\n editor.current.setContent('');\n }\n },\n getPlainContent() {\n return editor?.current?.getContent(CompatibleGetContentMode.PlainTextFast);\n }\n };\n },\n []\n );\n\n const ribbonPlugin = React.useMemo(() => {\n return createRibbonPlugin();\n }, []);\n\n const editorCreator = useCallback(\n (div: HTMLDivElement, options: EditorOptions) => {\n const editorValue = new Editor(div, options);\n // this is to fix issue when editor is created or re-rendered and has existing text\n // Content model package has a correct behavior and this fix can be deleted\n if (contentValue.current !== undefined && contentValue.current.length > 0) {\n // in case if initialContent is not empty, RoosterJS doesn't set caret position to the end.\n focusAndUpdateContent(editorValue, contentValue.current);\n } else if (initialContent !== undefined && initialContent.length > 0) {\n // changing layout in rich text send box cause the editor to be recreated\n // to keep the content, we need to set messageContent to the current content\n focusAndUpdateContent(editorValue, initialContent);\n }\n editor.current = editorValue;\n return editorValue;\n },\n // trigger force editor reset when strings are changed to update context menu strings\n // see RosterJS documentation for 'editorCreator' for more details\n // the editorCreator callback shouldn't be updated when the initialContent is changed\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [strings]\n );\n\n const placeholderPlugin = React.useMemo(() => {\n return new Watermark('');\n }, []);\n\n useEffect(() => {\n if (placeholderText !== undefined) {\n placeholderPlugin.updateWatermark(placeholderText);\n }\n }, [placeholderPlugin, placeholderText]);\n\n const plugins = useMemo(() => {\n // contextPlugin and tableEditMenuProvider allow to show insert/delete menu for the table\n const contextPlugin = createContextMenuPlugin();\n const tableEditMenuProvider = createTableEditMenuProvider(strings);\n const contentEdit = new ContentEdit();\n const updateContentPlugin = createUpdateContentPlugin(\n UpdateMode.OnContentChangedEvent | UpdateMode.OnUserInput,\n (content: string) => {\n onChange && onChange(content);\n }\n );\n const copyPastePlugin = new CopyPastePlugin();\n return [\n contentEdit,\n placeholderPlugin,\n updateContentPlugin,\n ribbonPlugin,\n contextPlugin,\n tableEditMenuProvider,\n copyPastePlugin\n ];\n }, [onChange, placeholderPlugin, ribbonPlugin, strings]);\n\n const ribbon = useMemo(() => {\n const buttons = ribbonButtons(theme);\n\n return (\n <Ribbon\n styles={ribbonStyle}\n buttons={buttons}\n plugin={ribbonPlugin}\n overflowButtonProps={{\n styles: ribbonButtonStyle(theme),\n menuProps: {\n items: [], // CommandBar will determine items rendered in overflow\n isBeakVisible: false,\n styles: ribbonOverflowButtonStyle(theme)\n }\n }}\n strings={ribbonButtonsStrings(strings)}\n data-testid={'rich-text-editor-ribbon'}\n />\n );\n }, [strings, ribbonPlugin, theme]);\n\n const defaultFormat: DefaultFormat = useMemo(() => {\n // without setting any styles, text input is not handled properly for tables (when insert or paste one in the editor)\n // because of https://github.com/microsoft/roosterjs/blob/14dbb947e3ae94580109cbd05e48ceb05327c4dc/packages/roosterjs-editor-core/lib/corePlugins/TypeInContainerPlugin.ts#L75\n // this issue is fixed for content model package\n return {\n backgroundColor: 'transparent'\n };\n }, []);\n\n return (\n <div data-testid={'rich-text-editor-wrapper'}>\n {showRichTextEditorFormatting && ribbon}\n <div className={richTextEditorWrapperStyle(theme, !showRichTextEditorFormatting)}>\n <Rooster\n defaultFormat={defaultFormat}\n inDarkMode={isDarkThemed(theme)}\n plugins={plugins}\n className={richTextEditorStyle(props.styles)}\n editorCreator={editorCreator}\n // TODO: confirm the color during inline images implementation\n imageSelectionBorderColor={'blue'}\n // doNotAdjustEditorColor is used to fix the default background color for Rooster component\n doNotAdjustEditorColor={true}\n data-testid={'rooster-rich-text-editor'}\n // if we don't use 'allowKeyboardEventPropagation' only the enter key is caught\n onKeyDown={props.onKeyDown}\n focusOnInit={autoFocus === 'sendBoxTextField'}\n />\n </div>\n </div>\n );\n});\n\nconst focusAndUpdateContent = (editor: Editor, content: string): void => {\n // focus the editor to set correct selection position\n editor.focus();\n // set initial content\n editor.setContent(content);\n};\n"]}
|
@@ -53,7 +53,7 @@ export const RichTextSendBox = (props) => {
|
|
53
53
|
setContentValue(newValue);
|
54
54
|
}, []);
|
55
55
|
const sendMessageOnClick = useCallback(() => {
|
56
|
-
var _a, _b;
|
56
|
+
var _a, _b, _c;
|
57
57
|
if (disabled || contentValueOverflow) {
|
58
58
|
return;
|
59
59
|
}
|
@@ -66,20 +66,29 @@ export const RichTextSendBox = (props) => {
|
|
66
66
|
return;
|
67
67
|
}
|
68
68
|
const message = contentValue;
|
69
|
+
// get plain text content from the editor to check if the message is empty
|
70
|
+
// as the content may contain tags even when the content is empty
|
71
|
+
const plainTextContent = (_a = editorComponentRef.current) === null || _a === void 0 ? void 0 : _a.getPlainContent();
|
72
|
+
const hasContent = !isContentEmpty({
|
73
|
+
plainTextContent,
|
74
|
+
content: message,
|
75
|
+
placeholder: strings.placeholderText
|
76
|
+
});
|
69
77
|
// we don't want to send empty messages including spaces, newlines, tabs
|
70
78
|
// Message can be empty if there is a valid attachment upload
|
71
|
-
if (
|
79
|
+
if (hasContent ||
|
72
80
|
/* @conditional-compile-remove(attachment-upload) */ hasCompletedAttachmentUploads(activeAttachmentUploads)) {
|
73
81
|
onSendMessage(message);
|
74
82
|
setContentValue('');
|
75
|
-
(
|
83
|
+
(_b = editorComponentRef.current) === null || _b === void 0 ? void 0 : _b.setEmptyContent();
|
76
84
|
}
|
77
|
-
(
|
85
|
+
(_c = editorComponentRef.current) === null || _c === void 0 ? void 0 : _c.focus();
|
78
86
|
}, [
|
79
87
|
contentValue,
|
80
88
|
contentValueOverflow,
|
81
89
|
disabled,
|
82
90
|
onSendMessage,
|
91
|
+
strings.placeholderText,
|
83
92
|
/* @conditional-compile-remove(attachment-upload) */ activeAttachmentUploads,
|
84
93
|
/* @conditional-compile-remove(attachment-upload) */ strings.attachmentUploadsPendingError
|
85
94
|
]);
|
@@ -99,15 +108,26 @@ export const RichTextSendBox = (props) => {
|
|
99
108
|
attachmentUploadsPendingError,
|
100
109
|
systemMessage
|
101
110
|
]);
|
102
|
-
const onRenderSendIcon = useCallback((isHover) =>
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
+
const onRenderSendIcon = useCallback((isHover) => {
|
112
|
+
var _a;
|
113
|
+
// get plain text content from the editor to check if the message is empty
|
114
|
+
// as the content may contain tags even when the content is empty
|
115
|
+
const plainTextContent = (_a = editorComponentRef.current) === null || _a === void 0 ? void 0 : _a.getPlainContent();
|
116
|
+
const hasContent = !isContentEmpty({
|
117
|
+
plainTextContent: plainTextContent,
|
118
|
+
content: contentValue,
|
119
|
+
placeholder: strings.placeholderText
|
120
|
+
});
|
121
|
+
return (React.createElement(Icon, { iconName: isHover && hasContent ? 'SendBoxSendHovered' : 'SendBoxSend', className: sendIconStyle({
|
122
|
+
theme,
|
123
|
+
hasText: hasContent,
|
124
|
+
/* @conditional-compile-remove(attachment-upload) */
|
125
|
+
hasAttachment: false,
|
126
|
+
hasErrorMessage: hasErrorMessage,
|
127
|
+
defaultTextColor: theme.palette.neutralSecondary,
|
128
|
+
disabled: disabled
|
129
|
+
}) }));
|
130
|
+
}, [contentValue, disabled, hasErrorMessage, strings.placeholderText, theme]);
|
111
131
|
const sendBoxErrorsProps = useMemo(() => {
|
112
132
|
var _a;
|
113
133
|
return {
|
@@ -169,4 +189,20 @@ export const RichTextSendBox = (props) => {
|
|
169
189
|
/* @conditional-compile-remove(attachment-upload) */
|
170
190
|
hasAttachments: hasAttachmentUploads })));
|
171
191
|
};
|
192
|
+
/**
|
193
|
+
* Checks if the content of the rich text editor is empty.
|
194
|
+
*
|
195
|
+
* @param {Object} params - The parameters for the function.
|
196
|
+
* @param {string | undefined} params.plainTextContent - The plain text content of the editor.
|
197
|
+
* @param {string} params.content - The HTML content of the editor.
|
198
|
+
* @param {string} params.placeholder - The placeholder text of the editor.
|
199
|
+
* @returns {boolean} - True if the content is empty, false otherwise.
|
200
|
+
*/
|
201
|
+
const isContentEmpty = ({ plainTextContent, content, placeholder }) => {
|
202
|
+
// RoosterJS returns placeholder text as plain text when the editor is empty and in this case,
|
203
|
+
// plainTextContent contains only placeholder text but content doesn't include the placeholder text
|
204
|
+
// this needs to be reviewed after migration to the content model packages.
|
205
|
+
const plainTextContainsPlaceholderOnly = plainTextContent === placeholder && !content.includes(placeholder);
|
206
|
+
return plainTextContainsPlaceholderOnly || sanitizeText(plainTextContent !== null && plainTextContent !== void 0 ? plainTextContent : '').length === 0;
|
207
|
+
};
|
172
208
|
//# sourceMappingURL=RichTextSendBox.js.map
|