@instructure/canvas-rce 7.3.1 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/{es/rce/plugins/shared/ai_tools/index.js → __mocks__/@instructure/ui-media-player/_mockUiMediaPlayer.js} +4 -4
- package/__tests__/common/mimeClass.test.js +25 -1
- package/__tests__/rcs/api.test.js +280 -251
- package/es/canvasFileBrowser/FileBrowser.d.ts +2 -2
- package/es/canvasFileBrowser/FileBrowser.js +8 -7
- package/es/common/mimeClass.js +3 -1
- package/es/defaultTinymceConfig.js +47 -49
- package/es/enhance-user-content/enhance_user_content.js +6 -8
- package/es/enhance-user-content/index.d.ts +3 -1
- package/es/enhance-user-content/index.js +3 -1
- package/es/enhance-user-content/youtube_overlay.js +18 -0
- package/es/getThemeVars.d.ts +1 -1
- package/es/getThemeVars.js +23 -26
- package/es/rce/KeyboardShortcutModal.js +1 -1
- package/es/rce/RCE.d.ts +9 -0
- package/es/rce/RCE.js +4 -0
- package/es/rce/RCEGlobals.d.ts +2 -0
- package/es/rce/RCEGlobals.js +1 -0
- package/es/rce/RCEVariants.d.ts +1 -2
- package/es/rce/RCEVariants.js +1 -2
- package/es/rce/RCEWrapper.d.ts +6 -16
- package/es/rce/RCEWrapper.js +18 -87
- package/es/rce/RCEWrapper.utils.d.ts +1 -1
- package/es/rce/RCEWrapperProps.d.ts +2 -1
- package/es/rce/RCEWrapperProps.js +2 -1
- package/es/rce/StatusBar.d.ts +0 -1
- package/es/rce/StatusBar.js +3 -28
- package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedOnlySyntax.d.ts +2 -1
- package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedOnlySyntax.js +3 -1
- package/es/rce/plugins/instructure_equation/EquationEditorModal/index.d.ts +1 -0
- package/es/rce/plugins/instructure_equation/EquationEditorModal/index.js +12 -2
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageOptions.js +2 -2
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageSection.js +3 -3
- package/es/rce/plugins/instructure_icon_maker/svg/constants.d.ts +20 -5
- package/es/rce/plugins/instructure_icon_maker/svg/utils.d.ts +1 -1
- package/es/rce/plugins/instructure_icon_maker/utils/IconMakerFormHasChanges.js +2 -2
- package/es/rce/plugins/instructure_image/ImageEmbedOptions.d.ts +0 -2
- package/es/rce/plugins/instructure_image/ImageEmbedOptions.js +2 -9
- package/es/rce/plugins/instructure_paste/plugin.js +18 -12
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.d.ts +1 -1
- package/es/rce/plugins/instructure_rce_external_tools/components/util/ToolLaunchIframe.d.ts +4 -0
- package/es/rce/plugins/instructure_rce_external_tools/components/util/ToolLaunchIframe.js +4 -0
- package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.d.ts +11 -2
- package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.js +92 -10
- package/es/rce/plugins/instructure_record/AudioOptionsTray/index.d.ts +13 -1
- package/es/rce/plugins/instructure_record/AudioOptionsTray/index.js +216 -24
- package/es/rce/plugins/instructure_record/MediaPanel/index.js +16 -5
- package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.d.ts +14 -13
- package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +110 -39
- package/es/rce/plugins/instructure_record/VideoOptionsTray/index.d.ts +11 -1
- package/es/rce/plugins/instructure_record/VideoOptionsTray/index.js +242 -67
- package/es/rce/plugins/instructure_record/clickCallback.js +19 -4
- package/es/rce/plugins/instructure_record/mediaTranslations.js +1 -1
- package/es/rce/plugins/instructure_record/playerLayoutOptions.d.ts +25 -0
- package/es/rce/plugins/instructure_record/playerLayoutOptions.js +91 -0
- package/es/rce/plugins/instructure_record/plugin.js +2 -5
- package/es/rce/plugins/instructure_record/utils.d.ts +3 -0
- package/es/rce/plugins/instructure_record/utils.js +31 -0
- package/es/rce/plugins/instructure_studio_media_options/plugin.js +82 -26
- package/es/rce/plugins/shared/ContentSelection.d.ts +6 -1
- package/es/rce/plugins/shared/ContentSelection.js +15 -6
- package/es/rce/plugins/shared/DimensionsInput/DimensionInput.js +1 -2
- package/es/rce/plugins/shared/DimensionsInput/index.js +11 -12
- package/es/rce/plugins/shared/DimensionsInput/useDimensionsState.d.ts +1 -1
- package/es/rce/plugins/shared/DimensionsInput/useDimensionsState.js +4 -3
- package/es/rce/plugins/shared/StudioLtiSupportUtils.d.ts +27 -6
- package/es/rce/plugins/shared/StudioLtiSupportUtils.js +82 -13
- package/es/rce/plugins/shared/Upload/UploadFile.js +1 -8
- package/es/rce/style.d.ts +2 -1
- package/es/rce/style.js +4 -2
- package/es/rcs/api.d.ts +5 -10
- package/es/rcs/api.js +15 -21
- package/es/rcs/fake.d.ts +1 -7
- package/es/rcs/fake.js +1 -47
- package/es/sidebar/actions/media.d.ts +19 -6
- package/es/sidebar/actions/media.js +17 -4
- package/es/sidebar/actions/upload.d.ts +3 -3
- package/es/sidebar/actions/upload.js +9 -9
- package/es/sidebar/containers/Sidebar.js +0 -2
- package/es/sidebar/containers/sidebarHandlers.d.ts +2 -4
- package/es/sidebar/containers/sidebarHandlers.js +2 -5
- package/es/sidebar/reducers/index.d.ts +0 -1
- package/es/sidebar/reducers/index.js +0 -2
- package/es/sidebar/store/initialState.d.ts +0 -1
- package/es/sidebar/store/initialState.js +0 -5
- package/es/translations/locales/ar.js +65 -80
- package/es/translations/locales/ca.js +65 -80
- package/es/translations/locales/cy.js +65 -80
- package/es/translations/locales/da-x-k12.js +65 -80
- package/es/translations/locales/da.js +65 -80
- package/es/translations/locales/de.js +65 -80
- package/es/translations/locales/el.js +0 -9
- package/es/translations/locales/en-AU-x-unimelb.js +65 -80
- package/es/translations/locales/en-GB-x-ukhe.js +65 -80
- package/es/translations/locales/en.js +61 -79
- package/es/translations/locales/en_AU.js +65 -80
- package/es/translations/locales/en_CA.js +65 -80
- package/es/translations/locales/en_CY.js +65 -80
- package/es/translations/locales/en_GB.js +65 -80
- package/es/translations/locales/es.js +65 -80
- package/es/translations/locales/es_ES.js +65 -80
- package/es/translations/locales/fa_IR.js +0 -9
- package/es/translations/locales/fi.js +65 -80
- package/es/translations/locales/fr.js +65 -80
- package/es/translations/locales/fr_CA.js +65 -80
- package/es/translations/locales/ga.js +65 -80
- package/es/translations/locales/he.js +0 -9
- package/es/translations/locales/hi.js +65 -80
- package/es/translations/locales/ht.js +65 -80
- package/es/translations/locales/hu.js +0 -36
- package/es/translations/locales/hy.js +0 -9
- package/es/translations/locales/id.js +65 -80
- package/es/translations/locales/is.js +65 -80
- package/es/translations/locales/it.js +65 -80
- package/es/translations/locales/ja.js +65 -80
- package/es/translations/locales/ko.js +2455 -133
- package/es/translations/locales/mi.js +65 -80
- package/es/translations/locales/ms.js +65 -80
- package/es/translations/locales/nb-x-k12.js +65 -80
- package/es/translations/locales/nb.js +65 -80
- package/es/translations/locales/nl.js +66 -81
- package/es/translations/locales/nn.js +0 -36
- package/es/translations/locales/pl.js +65 -80
- package/es/translations/locales/pt.js +65 -80
- package/es/translations/locales/pt_BR.js +65 -80
- package/es/translations/locales/ru.js +65 -80
- package/es/translations/locales/sl.js +65 -80
- package/es/translations/locales/sv-x-k12.js +65 -80
- package/es/translations/locales/sv.js +65 -80
- package/es/translations/locales/th.js +65 -80
- package/es/translations/locales/tr.js +1962 -18
- package/es/translations/locales/uk_UA.js +0 -9
- package/es/translations/locales/vi.js +65 -80
- package/es/translations/locales/zh-Hans.js +65 -80
- package/es/translations/locales/zh-Hant.js +65 -80
- package/es/translations/locales/zh.js +65 -80
- package/es/translations/locales/zh_HK.js +65 -80
- package/eslint.config.js +16 -147
- package/jest/jest-setup.js +1 -0
- package/jest.config.js +2 -0
- package/oxlint.json +84 -0
- package/package.json +87 -62
- package/tsconfig.json +3 -2
- package/es/rce/plugins/shared/ai_tools/AIResponseModal.d.ts +0 -10
- package/es/rce/plugins/shared/ai_tools/AIResponseModal.js +0 -67
- package/es/rce/plugins/shared/ai_tools/AIToolsTray.d.ts +0 -18
- package/es/rce/plugins/shared/ai_tools/AIToolsTray.js +0 -489
- package/es/rce/plugins/shared/ai_tools/aiicons.d.ts +0 -7
- package/es/rce/plugins/shared/ai_tools/aiicons.js +0 -60
- package/es/rce/plugins/shared/ai_tools/index.d.ts +0 -3
- package/es/sidebar/actions/flickr.d.ts +0 -20
- package/es/sidebar/actions/flickr.js +0 -60
- package/es/sidebar/reducers/flickr.d.ts +0 -1
- package/es/sidebar/reducers/flickr.js +0 -49
|
@@ -15,32 +15,37 @@
|
|
|
15
15
|
* You should have received a copy of the GNU Affero General Public License along
|
|
16
16
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
-
|
|
19
|
-
import {
|
|
20
|
-
import { Button, CloseButton
|
|
18
|
+
|
|
19
|
+
import { ClosedCaptionPanel, ClosedCaptionPanelV2, CONSTANTS, trackPendoEvent } from '@instructure/canvas-media';
|
|
20
|
+
import { Button, CloseButton } from '@instructure/ui-buttons';
|
|
21
|
+
import { Checkbox, CheckboxGroup } from '@instructure/ui-checkbox';
|
|
22
|
+
import { Flex } from '@instructure/ui-flex';
|
|
23
|
+
import { FormFieldGroup } from '@instructure/ui-form-field';
|
|
21
24
|
import { Heading } from '@instructure/ui-heading';
|
|
25
|
+
import { IconExternalLinkLine } from '@instructure/ui-icons';
|
|
26
|
+
import { Link } from '@instructure/ui-link';
|
|
22
27
|
import { RadioInput, RadioInputGroup } from '@instructure/ui-radio-input';
|
|
23
28
|
import { SimpleSelect } from '@instructure/ui-simple-select';
|
|
24
|
-
import { TextArea } from '@instructure/ui-text-area';
|
|
25
|
-
import { Text } from '@instructure/ui-text';
|
|
26
|
-
import { IconQuestionLine } from '@instructure/ui-icons';
|
|
27
|
-
import { Flex } from '@instructure/ui-flex';
|
|
28
|
-
import { FormFieldGroup } from '@instructure/ui-form-field';
|
|
29
|
-
import { View } from '@instructure/ui-view';
|
|
30
29
|
import { Spinner } from '@instructure/ui-spinner';
|
|
30
|
+
import { Text } from '@instructure/ui-text';
|
|
31
|
+
import { TextInput } from '@instructure/ui-text-input';
|
|
31
32
|
import { Tooltip } from '@instructure/ui-tooltip';
|
|
32
33
|
import { Tray } from '@instructure/ui-tray';
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import
|
|
34
|
+
import { View } from '@instructure/ui-view';
|
|
35
|
+
import { arrayOf, bool, func, number, shape, string } from 'prop-types';
|
|
36
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
36
37
|
import Bridge from '../../../../bridge';
|
|
37
|
-
import RceApiSource from '../../../../rcs/api';
|
|
38
38
|
import formatMessage from '../../../../format-message';
|
|
39
|
-
import
|
|
40
|
-
import {
|
|
39
|
+
import RCEGlobals from '../../../../rce/RCEGlobals';
|
|
40
|
+
import RceApiSource, { originFromHost } from '../../../../rcs/api';
|
|
41
41
|
import { instuiPopupMountNodeFn } from '../../../../util/fullscreenHelpers';
|
|
42
|
+
import { CUSTOM, labelForImageSize, MIN_HEIGHT_STUDIO_PLAYER, MIN_PERCENTAGE, MIN_WIDTH_STUDIO_PLAYER, scaleVideoSize, studioPlayerSizes } from '../../instructure_image/ImageEmbedOptions';
|
|
43
|
+
import DimensionsInput, { useDimensionsState } from '../../shared/DimensionsInput';
|
|
44
|
+
import { StoreProvider } from '../../shared/StoreContext';
|
|
42
45
|
import { parsedStudioOptionsPropType } from '../../shared/StudioLtiSupportUtils';
|
|
43
|
-
import
|
|
46
|
+
import { getTrayHeight } from '../../shared/trayUtils';
|
|
47
|
+
import { getPlayerLayoutSizes, labelForPlayerLayoutSize, playerLayoutDimensions, SMALL, scalePlayerLayoutForHeight, scalePlayerLayoutForWidth } from '../playerLayoutOptions';
|
|
48
|
+
import { mapStudioEmbedOptions, mapViewerRestrictions, readViewerRestrictions } from '../utils';
|
|
44
49
|
const getLiveRegion = () => document.getElementById('flash_screenreader_holder');
|
|
45
50
|
export default function VideoOptionsTray({
|
|
46
51
|
videoOptions,
|
|
@@ -53,9 +58,13 @@ export default function VideoOptionsTray({
|
|
|
53
58
|
onExited = null,
|
|
54
59
|
id = 'video-options-tray',
|
|
55
60
|
studioOptions = null,
|
|
56
|
-
forBlockEditorUse = false
|
|
61
|
+
forBlockEditorUse = false,
|
|
62
|
+
onStudioEmbedOptionChanged = () => {},
|
|
63
|
+
onCaptionsModified = null,
|
|
64
|
+
isLoading = false
|
|
57
65
|
}) {
|
|
58
|
-
const
|
|
66
|
+
const isEmbedImprovements = RCEGlobals.getFeatures()?.rce_studio_embed_improvements;
|
|
67
|
+
const isAsrCaptioningImprovements = RCEGlobals.getFeatures()?.rce_asr_captioning_improvements;
|
|
59
68
|
const {
|
|
60
69
|
naturalHeight,
|
|
61
70
|
naturalWidth
|
|
@@ -64,25 +73,56 @@ export default function VideoOptionsTray({
|
|
|
64
73
|
const currentWidth = videoOptions.appliedWidth || naturalWidth;
|
|
65
74
|
const [titleText, setTitleText] = useState(videoOptions.titleText);
|
|
66
75
|
const [displayAs, setDisplayAs] = useState('embed');
|
|
67
|
-
const [videoSize, setVideoSize] = useState(
|
|
76
|
+
const [videoSize, setVideoSize] = useState(() => {
|
|
77
|
+
if (isAsrCaptioningImprovements) {
|
|
78
|
+
const match = Object.entries(playerLayoutDimensions).find(([, dims]) => dims.width === videoOptions.appliedWidth);
|
|
79
|
+
if (match) return match[0];
|
|
80
|
+
}
|
|
81
|
+
return videoOptions.videoSize;
|
|
82
|
+
});
|
|
68
83
|
const [videoHeight, setVideoHeight] = useState(currentHeight);
|
|
69
84
|
const [videoWidth, setVideoWidth] = useState(currentWidth);
|
|
70
85
|
const [subtitles, setSubtitles] = useState(videoOptions.tracks || []);
|
|
71
|
-
const
|
|
72
|
-
|
|
86
|
+
const {
|
|
87
|
+
width: minWidth,
|
|
88
|
+
height: minHeight
|
|
89
|
+
} = isAsrCaptioningImprovements ? playerLayoutDimensions[SMALL] : {
|
|
90
|
+
width: MIN_WIDTH_STUDIO_PLAYER,
|
|
91
|
+
height: MIN_HEIGHT_STUDIO_PLAYER
|
|
92
|
+
};
|
|
73
93
|
const [minPercentage] = useState(MIN_PERCENTAGE);
|
|
74
94
|
const [editLocked, setEditLocked] = useState(null);
|
|
75
95
|
const [loading, setLoading] = useState(true);
|
|
96
|
+
const [viewerRestrictions, setViewerRestrictions] = useState(() => readViewerRestrictions(videoOptions.viewerRestrictions));
|
|
97
|
+
const [studioEmbedOptions, setStudioEmbedOptions] = useState(() => mapStudioEmbedOptions(studioOptions?.embedOptions));
|
|
98
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
99
|
+
const fetchedFromIframeRef = useRef(false);
|
|
100
|
+
const titleInputRef = useRef(null);
|
|
76
101
|
const isStudio = !!studioOptions;
|
|
102
|
+
const applyDescribedBy = useCallback(playerLayoutInput => {
|
|
103
|
+
if (isAsrCaptioningImprovements && !isStudio && playerLayoutInput) {
|
|
104
|
+
const helperId = `${id}-size-helper-text`;
|
|
105
|
+
const existing = playerLayoutInput.getAttribute('aria-describedby') || '';
|
|
106
|
+
const ids = existing.split(' ').filter(Boolean);
|
|
107
|
+
if (!ids.includes(helperId)) {
|
|
108
|
+
playerLayoutInput.setAttribute('aria-describedby', [...ids, helperId].join(' '));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}, [isAsrCaptioningImprovements, isStudio, id]);
|
|
77
112
|
const showDisplayOptions = (!isStudio || studioOptions.convertibleToLink) && !forBlockEditorUse;
|
|
78
113
|
const showSizeControls = (!isStudio || studioOptions.resizable) && !forBlockEditorUse;
|
|
79
114
|
const dimensionsState = useDimensionsState(videoOptions, {
|
|
80
115
|
minHeight,
|
|
81
116
|
minWidth,
|
|
82
117
|
minPercentage
|
|
83
|
-
}
|
|
118
|
+
}, isAsrCaptioningImprovements ? {
|
|
119
|
+
scaleFns: {
|
|
120
|
+
width: scalePlayerLayoutForWidth,
|
|
121
|
+
height: scalePlayerLayoutForHeight
|
|
122
|
+
}
|
|
123
|
+
} : {});
|
|
84
124
|
const api = new RceApiSource(trayProps);
|
|
85
|
-
const videoSizeOptions =
|
|
125
|
+
const videoSizeOptions = isAsrCaptioningImprovements ? getPlayerLayoutSizes() : studioPlayerSizes;
|
|
86
126
|
useEffect(() => {
|
|
87
127
|
if (videoOptions.attachmentId) {
|
|
88
128
|
api.getFile(videoOptions.attachmentId, {
|
|
@@ -96,8 +136,20 @@ export default function VideoOptionsTray({
|
|
|
96
136
|
}
|
|
97
137
|
}, [videoOptions.attachmentId]);
|
|
98
138
|
useEffect(() => {
|
|
99
|
-
if (subtitles.length === 0)
|
|
100
|
-
|
|
139
|
+
if (!isLoading && subtitles.length === 0 && !fetchedFromIframeRef.current) {
|
|
140
|
+
// only request subtitle data after mount
|
|
141
|
+
fetchedFromIframeRef.current = true;
|
|
142
|
+
requestSubtitlesFromIframe(setSubtitles);
|
|
143
|
+
}
|
|
144
|
+
}, [isLoading, subtitles.length, requestSubtitlesFromIframe]);
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (open && isAsrCaptioningImprovements) {
|
|
147
|
+
trackPendoEvent('canvas_media_options_opened', {
|
|
148
|
+
entry_point: 'quick_menu',
|
|
149
|
+
media_kind: 'video'
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}, [open, isAsrCaptioningImprovements]);
|
|
101
153
|
function handleTitleTextChange(event) {
|
|
102
154
|
setTitleText(event.target.value);
|
|
103
155
|
}
|
|
@@ -110,11 +162,18 @@ export default function VideoOptionsTray({
|
|
|
110
162
|
if (selectedOption.value === CUSTOM) {
|
|
111
163
|
setVideoHeight(currentHeight);
|
|
112
164
|
setVideoWidth(currentWidth);
|
|
165
|
+
} else if (isAsrCaptioningImprovements) {
|
|
166
|
+
const {
|
|
167
|
+
width,
|
|
168
|
+
height
|
|
169
|
+
} = playerLayoutDimensions[selectedOption.value];
|
|
170
|
+
setVideoHeight(height);
|
|
171
|
+
setVideoWidth(width);
|
|
113
172
|
} else {
|
|
114
173
|
const {
|
|
115
174
|
height,
|
|
116
175
|
width
|
|
117
|
-
} =
|
|
176
|
+
} = scaleVideoSize(selectedOption.value, naturalWidth, naturalHeight);
|
|
118
177
|
setVideoHeight(height);
|
|
119
178
|
setVideoWidth(width);
|
|
120
179
|
}
|
|
@@ -122,14 +181,33 @@ export default function VideoOptionsTray({
|
|
|
122
181
|
function handleUpdateSubtitles(new_subtitles) {
|
|
123
182
|
setSubtitles(new_subtitles);
|
|
124
183
|
}
|
|
184
|
+
const handleEmbedOptionChange = useCallback(options => {
|
|
185
|
+
const mappedOptions = options.reduce((a, c) => {
|
|
186
|
+
a[c] = options.includes(c);
|
|
187
|
+
return a;
|
|
188
|
+
}, {});
|
|
189
|
+
setStudioEmbedOptions(options);
|
|
190
|
+
onStudioEmbedOptionChanged(mappedOptions);
|
|
191
|
+
}, [onStudioEmbedOptionChanged]);
|
|
125
192
|
function handleSave(event, updateMediaObject) {
|
|
126
193
|
event.preventDefault();
|
|
194
|
+
if (titleText.trim() === '') {
|
|
195
|
+
if (titleInputRef.current) {
|
|
196
|
+
titleInputRef.current.focus();
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
127
200
|
let appliedHeight = videoHeight;
|
|
128
201
|
let appliedWidth = videoWidth;
|
|
129
202
|
if (videoSize === CUSTOM) {
|
|
130
203
|
appliedHeight = dimensionsState.height;
|
|
131
204
|
appliedWidth = dimensionsState.width;
|
|
132
205
|
}
|
|
206
|
+
if (isAsrCaptioningImprovements) {
|
|
207
|
+
trackPendoEvent('canvas_player_layout_selected', {
|
|
208
|
+
layout_type: videoSize.replace('-', '_')
|
|
209
|
+
});
|
|
210
|
+
}
|
|
133
211
|
onSave({
|
|
134
212
|
media_object_id: videoOptions.id,
|
|
135
213
|
attachment_id: videoOptions.attachmentId,
|
|
@@ -139,31 +217,15 @@ export default function VideoOptionsTray({
|
|
|
139
217
|
displayAs,
|
|
140
218
|
subtitles,
|
|
141
219
|
updateMediaObject,
|
|
142
|
-
editLocked
|
|
220
|
+
editLocked,
|
|
221
|
+
viewerRestrictions: mapViewerRestrictions(viewerRestrictions)
|
|
143
222
|
});
|
|
144
223
|
}
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}, /*#__PURE__*/React.createElement(Flex.Item, null, formatMessage('Title')), /*#__PURE__*/React.createElement(Flex.Item, {
|
|
149
|
-
margin: "0 0 0 xx-small"
|
|
150
|
-
}, /*#__PURE__*/React.createElement(Tooltip, {
|
|
151
|
-
on: ['hover', 'focus'],
|
|
152
|
-
placement: "top",
|
|
153
|
-
renderTip: /*#__PURE__*/React.createElement(View, {
|
|
154
|
-
display: "block",
|
|
155
|
-
id: "alt-text-label-tooltip",
|
|
156
|
-
maxWidth: "14rem"
|
|
157
|
-
}, tooltipText)
|
|
158
|
-
}, /*#__PURE__*/React.createElement(IconButton, {
|
|
159
|
-
renderIcon: IconQuestionLine,
|
|
160
|
-
size: "small",
|
|
161
|
-
screenReaderLabel: tooltipText,
|
|
162
|
-
withBackground: false,
|
|
163
|
-
withBorder: false
|
|
164
|
-
}))));
|
|
224
|
+
const handleDirtyCheck = isDirty => {
|
|
225
|
+
setHasUnsavedChanges(isDirty);
|
|
226
|
+
};
|
|
165
227
|
const messagesForSize = [];
|
|
166
|
-
if (videoSize !== CUSTOM) {
|
|
228
|
+
if (videoSize !== CUSTOM && !isAsrCaptioningImprovements) {
|
|
167
229
|
messagesForSize.push({
|
|
168
230
|
text: formatMessage('{width} x {height}px', {
|
|
169
231
|
height: videoHeight,
|
|
@@ -172,7 +234,7 @@ export default function VideoOptionsTray({
|
|
|
172
234
|
type: 'hint'
|
|
173
235
|
});
|
|
174
236
|
}
|
|
175
|
-
const saveDisabled = displayAs === 'embed' &&
|
|
237
|
+
const saveDisabled = displayAs === 'embed' && videoSize === CUSTOM && !dimensionsState.isValid;
|
|
176
238
|
return /*#__PURE__*/React.createElement(StoreProvider, trayProps, contentProps => /*#__PURE__*/React.createElement(Tray, {
|
|
177
239
|
key: "video-options-tray",
|
|
178
240
|
"data-mce-component": true,
|
|
@@ -204,7 +266,7 @@ export default function VideoOptionsTray({
|
|
|
204
266
|
color: "primary",
|
|
205
267
|
onClick: onRequestClose,
|
|
206
268
|
screenReaderLabel: formatMessage('Close')
|
|
207
|
-
})))), loading && videoOptions.attachmentId ? /*#__PURE__*/React.createElement(Flex.Item, {
|
|
269
|
+
})))), loading && videoOptions.attachmentId || isLoading ? /*#__PURE__*/React.createElement(Flex.Item, {
|
|
208
270
|
textAlign: "center",
|
|
209
271
|
margin: "xx-large",
|
|
210
272
|
padding: "xx-large"
|
|
@@ -233,15 +295,18 @@ export default function VideoOptionsTray({
|
|
|
233
295
|
weight: "bold"
|
|
234
296
|
}, formatMessage('Media Title'))), /*#__PURE__*/React.createElement(Flex.Item, {
|
|
235
297
|
padding: "small none none small"
|
|
236
|
-
}, titleText)) : /*#__PURE__*/React.createElement(
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
height: "4rem",
|
|
240
|
-
label: textAreaLabel,
|
|
298
|
+
}, titleText)) : /*#__PURE__*/React.createElement(TextInput, {
|
|
299
|
+
interaction: displayAs === 'link' ? 'disabled' : 'enabled',
|
|
300
|
+
renderLabel: formatMessage('Title'),
|
|
241
301
|
onChange: handleTitleTextChange,
|
|
242
|
-
placeholder: formatMessage('
|
|
243
|
-
|
|
244
|
-
|
|
302
|
+
placeholder: formatMessage('Enter a media title'),
|
|
303
|
+
value: titleText,
|
|
304
|
+
inputRef: el => titleInputRef.current = el,
|
|
305
|
+
messages: titleText?.trim() === '' ? [{
|
|
306
|
+
text: formatMessage("Title can't be blank"),
|
|
307
|
+
type: 'newError'
|
|
308
|
+
}] : [],
|
|
309
|
+
isRequired: true
|
|
245
310
|
})), showDisplayOptions && /*#__PURE__*/React.createElement(Flex.Item, {
|
|
246
311
|
margin: "small none none none",
|
|
247
312
|
padding: "small"
|
|
@@ -262,10 +327,11 @@ export default function VideoOptionsTray({
|
|
|
262
327
|
as: "div",
|
|
263
328
|
padding: "small small xx-small small"
|
|
264
329
|
}, /*#__PURE__*/React.createElement(SimpleSelect, {
|
|
330
|
+
inputRef: applyDescribedBy,
|
|
265
331
|
id: `${id}-size`,
|
|
266
332
|
mountNode: instuiPopupMountNodeFn,
|
|
267
333
|
disabled: displayAs !== 'embed',
|
|
268
|
-
renderLabel: formatMessage('Size'),
|
|
334
|
+
renderLabel: isAsrCaptioningImprovements ? formatMessage('Player layout') : formatMessage('Size'),
|
|
269
335
|
messages: messagesForSize,
|
|
270
336
|
assistiveText: formatMessage('Use arrow keys to navigate options.'),
|
|
271
337
|
onChange: handleVideoSizeChange,
|
|
@@ -274,7 +340,13 @@ export default function VideoOptionsTray({
|
|
|
274
340
|
id: `${id}-size-${size}`,
|
|
275
341
|
key: size,
|
|
276
342
|
value: size
|
|
277
|
-
}, labelForImageSize(size))))
|
|
343
|
+
}, isAsrCaptioningImprovements ? labelForPlayerLayoutSize(size) : labelForImageSize(size)))), isAsrCaptioningImprovements && !isStudio && /*#__PURE__*/React.createElement(View, {
|
|
344
|
+
as: "div",
|
|
345
|
+
id: `${id}-size-helper-text`,
|
|
346
|
+
margin: "xx-small none none none"
|
|
347
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
348
|
+
size: "small"
|
|
349
|
+
}, formatMessage('Transcript panel is available at widths above 720px.')))), videoSize === CUSTOM && /*#__PURE__*/React.createElement(View, {
|
|
278
350
|
as: "div",
|
|
279
351
|
padding: "xx-small small"
|
|
280
352
|
}, /*#__PURE__*/React.createElement(DimensionsInput, {
|
|
@@ -284,11 +356,33 @@ export default function VideoOptionsTray({
|
|
|
284
356
|
minWidth: minWidth,
|
|
285
357
|
minPercentage: minPercentage,
|
|
286
358
|
hidePercentage: true
|
|
359
|
+
}))), isAsrCaptioningImprovements && !isStudio && /*#__PURE__*/React.createElement(Flex.Item, {
|
|
360
|
+
padding: "small"
|
|
361
|
+
}, /*#__PURE__*/React.createElement(CheckboxGroup, {
|
|
362
|
+
name: "viewer-restrictions",
|
|
363
|
+
onChange: setViewerRestrictions,
|
|
364
|
+
defaultValue: viewerRestrictions,
|
|
365
|
+
description: /*#__PURE__*/React.createElement(Heading, {
|
|
366
|
+
level: "h4",
|
|
367
|
+
as: "h3"
|
|
368
|
+
}, formatMessage('Viewer Restrictions'))
|
|
369
|
+
}, /*#__PURE__*/React.createElement(Checkbox, {
|
|
370
|
+
variant: "toggle",
|
|
371
|
+
label: formatMessage('Show Rolling Transcript'),
|
|
372
|
+
value: "show_rolling_transcript",
|
|
373
|
+
messages: [{
|
|
374
|
+
text: formatMessage('Transcript panel is available at widths above 720px.'),
|
|
375
|
+
type: 'screenreader-only'
|
|
376
|
+
}]
|
|
287
377
|
}))), !isStudio && !editLocked && /*#__PURE__*/React.createElement(Flex.Item, {
|
|
288
378
|
padding: "small"
|
|
289
379
|
}, /*#__PURE__*/React.createElement(FormFieldGroup, {
|
|
290
|
-
description:
|
|
291
|
-
|
|
380
|
+
description: /*#__PURE__*/React.createElement(Heading, {
|
|
381
|
+
level: "h4",
|
|
382
|
+
as: "h3"
|
|
383
|
+
}, isAsrCaptioningImprovements ? formatMessage('Caption Manager') : formatMessage('Closed Captions/Subtitles'))
|
|
384
|
+
}, !isAsrCaptioningImprovements && /*#__PURE__*/React.createElement(ClosedCaptionPanel, {
|
|
385
|
+
key: subtitles.reduce((acc, track) => acc + track.locale, ''),
|
|
292
386
|
subtitles: subtitles.map(st => ({
|
|
293
387
|
locale: st.locale,
|
|
294
388
|
inherited: st.inherited,
|
|
@@ -301,16 +395,91 @@ export default function VideoOptionsTray({
|
|
|
301
395
|
updateSubtitles: handleUpdateSubtitles,
|
|
302
396
|
liveRegion: getLiveRegion,
|
|
303
397
|
mountNode: instuiPopupMountNodeFn
|
|
304
|
-
})
|
|
398
|
+
}), isAsrCaptioningImprovements && /*#__PURE__*/React.createElement(ClosedCaptionPanelV2, {
|
|
399
|
+
subtitles: subtitles.map(st => ({
|
|
400
|
+
...st,
|
|
401
|
+
file: {
|
|
402
|
+
name: st.language || st.locale
|
|
403
|
+
},
|
|
404
|
+
asr: Boolean(st.asr)
|
|
405
|
+
})),
|
|
406
|
+
uploadMediaTranslations: Bridge.uploadMediaTranslations,
|
|
407
|
+
userLocale: Bridge.userLocale,
|
|
408
|
+
onUpdateSubtitles: handleUpdateSubtitles,
|
|
409
|
+
liveRegion: getLiveRegion,
|
|
410
|
+
mountNode: instuiPopupMountNodeFn,
|
|
411
|
+
uploadConfig: {
|
|
412
|
+
mediaObjectId: videoOptions.id,
|
|
413
|
+
attachmentId: videoOptions.attachmentId,
|
|
414
|
+
origin: originFromHost(api.host),
|
|
415
|
+
headers: api.jwt ? {
|
|
416
|
+
Authorization: `Bearer ${api.jwt}`
|
|
417
|
+
} : undefined,
|
|
418
|
+
maxBytes: CONSTANTS.CC_FILE_MAX_BYTES
|
|
419
|
+
},
|
|
420
|
+
onCaptionUploaded: subtitle => {
|
|
421
|
+
// Update local state so "Done" button knows about it
|
|
422
|
+
setSubtitles(prev => [...prev.filter(s => s.locale !== subtitle.locale), subtitle]);
|
|
423
|
+
onCaptionsModified?.();
|
|
424
|
+
},
|
|
425
|
+
onCaptionDeleted: locale => {
|
|
426
|
+
setSubtitles(prev => prev.filter(s => s.locale !== locale));
|
|
427
|
+
onCaptionsModified?.();
|
|
428
|
+
},
|
|
429
|
+
onDirtyStateChanged: handleDirtyCheck
|
|
430
|
+
}))), isStudio && isEmbedImprovements && studioOptions.isImprovedEmbed ? /*#__PURE__*/React.createElement(Flex.Item, {
|
|
431
|
+
padding: "small"
|
|
432
|
+
}, /*#__PURE__*/React.createElement(CheckboxGroup, {
|
|
433
|
+
name: "studio-embed-options",
|
|
434
|
+
onChange: handleEmbedOptionChange,
|
|
435
|
+
value: studioEmbedOptions,
|
|
436
|
+
description: /*#__PURE__*/React.createElement(Heading, {
|
|
437
|
+
level: "h4",
|
|
438
|
+
as: "h3"
|
|
439
|
+
}, formatMessage('Viewer Restrictions'))
|
|
440
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
441
|
+
variant: "contentSmall"
|
|
442
|
+
}, formatMessage('Changes will apply after you save this page.')), /*#__PURE__*/React.createElement(Checkbox, {
|
|
443
|
+
label: formatMessage('Lock speed at 1x'),
|
|
444
|
+
value: "lockSpeed",
|
|
445
|
+
variant: "toggle"
|
|
446
|
+
}), !studioOptions?.embedOptions?.isExternal ? /*#__PURE__*/React.createElement(Checkbox, {
|
|
447
|
+
label: formatMessage('Allow media download'),
|
|
448
|
+
value: "enableMediaDownload",
|
|
449
|
+
variant: "toggle"
|
|
450
|
+
}) : null, /*#__PURE__*/React.createElement(Checkbox, {
|
|
451
|
+
label: formatMessage('Allow transcript download'),
|
|
452
|
+
value: "enableTranscriptDownload",
|
|
453
|
+
variant: "toggle"
|
|
454
|
+
}), /*#__PURE__*/React.createElement(Checkbox, {
|
|
455
|
+
label: formatMessage('Show rolling transcript'),
|
|
456
|
+
value: "showRollingTranscript",
|
|
457
|
+
variant: "toggle"
|
|
458
|
+
}))) : null, !isStudio && isAsrCaptioningImprovements ? /*#__PURE__*/React.createElement(Flex.Item, {
|
|
459
|
+
padding: "small"
|
|
460
|
+
}, /*#__PURE__*/React.createElement(Link, {
|
|
461
|
+
id: "tray-transcript-help-link",
|
|
462
|
+
variant: "standalone",
|
|
463
|
+
renderIcon: /*#__PURE__*/React.createElement(IconExternalLinkLine, null),
|
|
464
|
+
href: "https://productmarketing.instructuremedia.com/embed/32388c5a-580c-40f0-85a2-6b4042ddcccb",
|
|
465
|
+
target: "_blank",
|
|
466
|
+
rel: "noopener noreferrer"
|
|
467
|
+
}, formatMessage('How to request and edit captions?'))) : null)), /*#__PURE__*/React.createElement(Flex.Item, {
|
|
305
468
|
background: "secondary",
|
|
306
469
|
borderWidth: "small none none none",
|
|
307
470
|
padding: "small medium",
|
|
308
471
|
textAlign: "end"
|
|
472
|
+
}, /*#__PURE__*/React.createElement(Tooltip, {
|
|
473
|
+
renderTip: formatMessage('Unsaved changes will be lost.'),
|
|
474
|
+
placement: "top",
|
|
475
|
+
on: ['hover', 'focus'],
|
|
476
|
+
preventTooltip: !hasUnsavedChanges,
|
|
477
|
+
mountNode: instuiPopupMountNodeFn
|
|
309
478
|
}, /*#__PURE__*/React.createElement(Button, {
|
|
310
|
-
disabled:
|
|
479
|
+
interaction: saveDisabled ? 'disabled' : 'enabled',
|
|
311
480
|
onClick: event => handleSave(event, contentProps.updateMediaObject),
|
|
312
481
|
color: "primary"
|
|
313
|
-
}, formatMessage('Done'))))))));
|
|
482
|
+
}, formatMessage('Done')))))))));
|
|
314
483
|
}
|
|
315
484
|
VideoOptionsTray.propTypes = {
|
|
316
485
|
videoOptions: shape({
|
|
@@ -322,7 +491,10 @@ VideoOptionsTray.propTypes = {
|
|
|
322
491
|
tracks: arrayOf(shape({
|
|
323
492
|
locale: string.isRequired,
|
|
324
493
|
inherited: bool
|
|
325
|
-
}))
|
|
494
|
+
})),
|
|
495
|
+
viewerRestrictions: shape({
|
|
496
|
+
show_rolling_transcript: bool
|
|
497
|
+
})
|
|
326
498
|
}).isRequired,
|
|
327
499
|
onEntered: func,
|
|
328
500
|
onExited: func,
|
|
@@ -335,5 +507,8 @@ VideoOptionsTray.propTypes = {
|
|
|
335
507
|
}),
|
|
336
508
|
id: string,
|
|
337
509
|
studioOptions: parsedStudioOptionsPropType,
|
|
338
|
-
requestSubtitlesFromIframe: func
|
|
510
|
+
requestSubtitlesFromIframe: func,
|
|
511
|
+
onStudioEmbedOptionChanged: func,
|
|
512
|
+
onCaptionsModified: func,
|
|
513
|
+
isLoading: bool
|
|
339
514
|
};
|
|
@@ -16,14 +16,15 @@
|
|
|
16
16
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { trackPendoEvent } from '@instructure/canvas-media';
|
|
19
20
|
import React from 'react';
|
|
20
21
|
import ReactDOM from 'react-dom';
|
|
21
22
|
import Bridge from '../../../bridge';
|
|
22
|
-
import { StoreProvider } from '../shared/StoreContext';
|
|
23
23
|
import formatMessage from '../../../format-message';
|
|
24
24
|
import { headerFor, originFromHost } from '../../../rcs/api';
|
|
25
25
|
import { instuiPopupMountNodeFn } from '../../../util/fullscreenHelpers';
|
|
26
26
|
import RCEGlobals from '../../RCEGlobals';
|
|
27
|
+
import { StoreProvider } from '../shared/StoreContext';
|
|
27
28
|
export const handleUpload = (error, uploadData, onUploadComplete, uploadBookmark) => {
|
|
28
29
|
let err_msg = error && Bridge.uploadMediaTranslations.UploadMediaStrings.UPLOADING_ERROR;
|
|
29
30
|
if (error?.name === 'FileSizeError') {
|
|
@@ -84,15 +85,29 @@ export default function (ed, document) {
|
|
|
84
85
|
open: true,
|
|
85
86
|
liveRegion: () => document.getElementById('flash_screenreader_holder'),
|
|
86
87
|
onStartUpload: fileProps => handleStartUpload(fileProps),
|
|
87
|
-
onUploadComplete: (err, data) =>
|
|
88
|
+
onUploadComplete: (err, data) => {
|
|
89
|
+
if (!err && data) {
|
|
90
|
+
if (RCEGlobals.getFeatures()?.rce_asr_captioning_improvements) {
|
|
91
|
+
var _data$uploadedFile$ty;
|
|
92
|
+
const fileType = (_data$uploadedFile$ty = data?.uploadedFile?.type) !== null && _data$uploadedFile$ty !== void 0 ? _data$uploadedFile$ty : '';
|
|
93
|
+
trackPendoEvent('canvas_native_media_embedded', {
|
|
94
|
+
insertion_method: 'insert_menu',
|
|
95
|
+
media_id: data?.mediaObject?.media_object?.media_id,
|
|
96
|
+
media_kind: fileType.startsWith('audio/') ? 'audio' : fileType.startsWith('video/') ? 'video' : undefined,
|
|
97
|
+
resourceType: ed.settings.canvas_rce_user_context.type,
|
|
98
|
+
resourceId: ed.settings.canvas_rce_user_context.id
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
handleUpload(err, data, contentProps.mediaUploadComplete, uploadBookmark);
|
|
103
|
+
},
|
|
88
104
|
onDismiss: handleDismiss,
|
|
89
105
|
tabs: {
|
|
90
106
|
record: true,
|
|
91
107
|
upload: true
|
|
92
108
|
},
|
|
93
109
|
uploadMediaTranslations: Bridge.uploadMediaTranslations,
|
|
94
|
-
media_links_use_attachment_id: RCEGlobals.getFeatures().media_links_use_attachment_id
|
|
95
|
-
useStudioPlayer: RCEGlobals.getFeatures()?.consolidated_media_player
|
|
110
|
+
media_links_use_attachment_id: RCEGlobals.getFeatures().media_links_use_attachment_id
|
|
96
111
|
})), container);
|
|
97
112
|
});
|
|
98
113
|
}
|
|
@@ -43,7 +43,7 @@ const uploadMediaTranslations = {
|
|
|
43
43
|
SUPPORTED_FILE_TYPES: formatMessage('Supported file types: SRT or WebVTT'),
|
|
44
44
|
NO_FILE_CHOSEN: formatMessage('No file chosen'),
|
|
45
45
|
REMOVE_FILE: 'Remove {lang} closed captions',
|
|
46
|
-
ADD_NEW_CAPTION_OR_SUBTITLE: formatMessage('Add another'),
|
|
46
|
+
ADD_NEW_CAPTION_OR_SUBTITLE: formatMessage('Add another closed caption or subtitle'),
|
|
47
47
|
ADDED_CAPTION: 'Captions for {lang} added',
|
|
48
48
|
DELETED_CAPTION: 'Deleted captions for {lang}',
|
|
49
49
|
PROGRESS_LABEL: formatMessage('Uploading'),
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const SMALL = "small";
|
|
2
|
+
export declare const MEDIUM = "medium";
|
|
3
|
+
export declare const LARGE = "large";
|
|
4
|
+
export declare const EXTRA_LARGE = "extra-large";
|
|
5
|
+
export declare const CUSTOM = "custom";
|
|
6
|
+
export declare const PLAYER_CONTROLS_HEIGHT = 48;
|
|
7
|
+
export declare const TRANSCRIPT_SIDEBAR_WIDTH = 300;
|
|
8
|
+
export declare const TRANSCRIPT_SIDEBAR_THRESHOLD = 720;
|
|
9
|
+
export declare const playerLayoutDimensions: Record<string, {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}>;
|
|
13
|
+
declare const playerLayoutSizes: readonly ["small", "medium", "large", "extra-large", "custom"];
|
|
14
|
+
export type PlayerLayoutSize = (typeof playerLayoutSizes)[number];
|
|
15
|
+
export declare function getPlayerLayoutSizes(): readonly PlayerLayoutSize[];
|
|
16
|
+
export declare function labelForPlayerLayoutSize(size: string): string;
|
|
17
|
+
export declare function scalePlayerLayoutForWidth(_naturalWidth: number, _naturalHeight: number, targetWidth: number | null): {
|
|
18
|
+
width: number | null;
|
|
19
|
+
height: number | null;
|
|
20
|
+
};
|
|
21
|
+
export declare function scalePlayerLayoutForHeight(_naturalWidth: number, _naturalHeight: number, targetHeight: number | null): {
|
|
22
|
+
width: number | null;
|
|
23
|
+
height: number | null;
|
|
24
|
+
};
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 - present Instructure, Inc.
|
|
3
|
+
*
|
|
4
|
+
* This file is part of Canvas.
|
|
5
|
+
*
|
|
6
|
+
* Canvas is free software: you can redistribute it and/or modify it under
|
|
7
|
+
* the terms of the GNU Affero General Public License as published by the Free
|
|
8
|
+
* Software Foundation, version 3 of the License.
|
|
9
|
+
*
|
|
10
|
+
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
11
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
12
|
+
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
13
|
+
* details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License along
|
|
16
|
+
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import formatMessage from '../../../format-message';
|
|
20
|
+
export const SMALL = 'small';
|
|
21
|
+
export const MEDIUM = 'medium';
|
|
22
|
+
export const LARGE = 'large';
|
|
23
|
+
export const EXTRA_LARGE = 'extra-large';
|
|
24
|
+
export const CUSTOM = 'custom';
|
|
25
|
+
export const PLAYER_CONTROLS_HEIGHT = 48;
|
|
26
|
+
export const TRANSCRIPT_SIDEBAR_WIDTH = 300;
|
|
27
|
+
export const TRANSCRIPT_SIDEBAR_THRESHOLD = 720;
|
|
28
|
+
export const playerLayoutDimensions = {
|
|
29
|
+
[SMALL]: {
|
|
30
|
+
width: 400,
|
|
31
|
+
height: 273
|
|
32
|
+
},
|
|
33
|
+
[MEDIUM]: {
|
|
34
|
+
width: 480,
|
|
35
|
+
height: 318
|
|
36
|
+
},
|
|
37
|
+
[LARGE]: {
|
|
38
|
+
width: 700,
|
|
39
|
+
height: 442
|
|
40
|
+
},
|
|
41
|
+
[EXTRA_LARGE]: {
|
|
42
|
+
width: 850,
|
|
43
|
+
height: 357
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const playerLayoutSizes = [SMALL, MEDIUM, LARGE, EXTRA_LARGE, CUSTOM];
|
|
47
|
+
export function getPlayerLayoutSizes() {
|
|
48
|
+
return playerLayoutSizes;
|
|
49
|
+
}
|
|
50
|
+
export function labelForPlayerLayoutSize(size) {
|
|
51
|
+
const dims = playerLayoutDimensions[size];
|
|
52
|
+
switch (size) {
|
|
53
|
+
case SMALL:
|
|
54
|
+
return formatMessage('Small ({width} x {height}px)', dims);
|
|
55
|
+
case MEDIUM:
|
|
56
|
+
return formatMessage('Medium ({width} x {height}px)', dims);
|
|
57
|
+
case LARGE:
|
|
58
|
+
return formatMessage('Large ({width} x {height}px)', dims);
|
|
59
|
+
case EXTRA_LARGE:
|
|
60
|
+
return formatMessage('Extra Large ({width} x {height}px)', dims);
|
|
61
|
+
default:
|
|
62
|
+
return formatMessage('Custom');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Scale functions for the Custom player layout option.
|
|
67
|
+
// Signature matches scaleForWidth/scaleForHeight in DimensionUtils.js
|
|
68
|
+
// (naturalWidth/naturalHeight are unused here — the formula is fixed).
|
|
69
|
+
export function scalePlayerLayoutForWidth(_naturalWidth, _naturalHeight, targetWidth) {
|
|
70
|
+
if (targetWidth == null) return {
|
|
71
|
+
width: null,
|
|
72
|
+
height: null
|
|
73
|
+
};
|
|
74
|
+
const videoWidth = targetWidth > TRANSCRIPT_SIDEBAR_THRESHOLD ? targetWidth - TRANSCRIPT_SIDEBAR_WIDTH : targetWidth;
|
|
75
|
+
return {
|
|
76
|
+
width: targetWidth,
|
|
77
|
+
height: Math.round(videoWidth * (9 / 16) + PLAYER_CONTROLS_HEIGHT)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function scalePlayerLayoutForHeight(_naturalWidth, _naturalHeight, targetHeight) {
|
|
81
|
+
if (targetHeight == null) return {
|
|
82
|
+
width: null,
|
|
83
|
+
height: null
|
|
84
|
+
};
|
|
85
|
+
const videoWidth = (targetHeight - PLAYER_CONTROLS_HEIGHT) * (16 / 9);
|
|
86
|
+
const totalWidth = videoWidth > TRANSCRIPT_SIDEBAR_THRESHOLD ? Math.round(videoWidth + TRANSCRIPT_SIDEBAR_WIDTH) : Math.round(videoWidth);
|
|
87
|
+
return {
|
|
88
|
+
width: totalWidth,
|
|
89
|
+
height: targetHeight
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -131,14 +131,12 @@ tinymce.PluginManager.add('instructure_record', function (ed) {
|
|
|
131
131
|
* Register the Video "Options" button that will open the Video Options
|
|
132
132
|
* tray.
|
|
133
133
|
*/
|
|
134
|
-
const buttonAriaLabel = formatMessage('Show video options');
|
|
135
134
|
ed.ui.registry.addButton('instructure-video-options', {
|
|
136
135
|
onAction() {
|
|
137
136
|
// show the tray
|
|
138
137
|
videoTrayController.showTrayForEditor(ed);
|
|
139
138
|
},
|
|
140
|
-
text: formatMessage('Video Options')
|
|
141
|
-
tooltip: buttonAriaLabel
|
|
139
|
+
text: formatMessage('Video Options')
|
|
142
140
|
});
|
|
143
141
|
ed.ui.registry.addContextToolbar('instructure-video-toolbar', {
|
|
144
142
|
items: 'instructure-video-options',
|
|
@@ -150,8 +148,7 @@ tinymce.PluginManager.add('instructure_record', function (ed) {
|
|
|
150
148
|
onAction() {
|
|
151
149
|
audioTrayController.showTrayForEditor(ed);
|
|
152
150
|
},
|
|
153
|
-
text: formatMessage('Audio Options')
|
|
154
|
-
tooltip: formatMessage('Show audio options')
|
|
151
|
+
text: formatMessage('Audio Options')
|
|
155
152
|
});
|
|
156
153
|
ed.ui.registry.addContextToolbar('instructure-audio-toolbar', {
|
|
157
154
|
items: 'instructure-audio-options',
|