@instructure/canvas-rce 7.0.0 → 7.3.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.
Files changed (152) hide show
  1. package/CHANGELOG.md +60 -1
  2. package/__tests__/common/indicate.test.js +5 -6
  3. package/es/bridge/Bridge.js +2 -4
  4. package/es/canvasFileBrowser/FileBrowser.js +2 -4
  5. package/es/defaultTinymceConfig.d.ts +1 -1
  6. package/es/defaultTinymceConfig.js +149 -114
  7. package/es/enhance-user-content/doc_previews.js +1 -14
  8. package/es/enhance-user-content/enhance_user_content.js +7 -1
  9. package/es/enhance-user-content/instructure_helper.js +4 -0
  10. package/es/enhance-user-content/youtube_overlay.d.ts +1 -0
  11. package/es/enhance-user-content/youtube_overlay.js +87 -0
  12. package/es/format-message.d.js +1 -0
  13. package/es/format-message.js +5 -0
  14. package/es/index.d.ts +2 -1
  15. package/es/index.js +2 -1
  16. package/es/rce/AlertMessageArea.d.ts +2 -2
  17. package/es/rce/AlertMessageArea.js +4 -6
  18. package/es/rce/RCE.d.ts +0 -1
  19. package/es/rce/RCE.js +5 -10
  20. package/es/rce/RCEGlobals.d.ts +2 -0
  21. package/es/rce/RCEGlobals.js +1 -0
  22. package/es/rce/RCEVariants.d.ts +8 -3
  23. package/es/rce/RCEVariants.js +31 -5
  24. package/es/rce/RCEWrapper.d.ts +16 -14
  25. package/es/rce/RCEWrapper.js +260 -244
  26. package/es/rce/RCEWrapperProps.d.ts +1 -1
  27. package/es/rce/ShowOnFocusButton/index.js +4 -2
  28. package/es/rce/StatusBar.js +61 -15
  29. package/es/rce/alertHandler.js +6 -7
  30. package/es/rce/plugins/instructure-ui-icons/plugin.js +2 -2
  31. package/es/rce/plugins/instructure_equation/EquationEditorModal/index.js +6 -10
  32. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/Course.d.ts +5 -15
  33. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/Course.js +4 -10
  34. package/es/rce/plugins/instructure_image/ImageEmbedOptions.d.ts +7 -0
  35. package/es/rce/plugins/instructure_image/ImageEmbedOptions.js +45 -2
  36. package/es/rce/plugins/instructure_keyboard_shortcuts_header/clickCallback.d.ts +2 -0
  37. package/es/rce/plugins/instructure_keyboard_shortcuts_header/clickCallback.js +45 -0
  38. package/es/rce/plugins/instructure_keyboard_shortcuts_header/plugin.d.ts +1 -0
  39. package/es/rce/plugins/instructure_keyboard_shortcuts_header/plugin.js +43 -0
  40. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.d.ts +1 -8
  41. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.js +13 -33
  42. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.d.ts +1 -2
  43. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.js +2 -1
  44. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.d.ts +1 -1
  45. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.js +25 -25
  46. package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.d.ts +1 -1
  47. package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.js +2 -1
  48. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.d.ts +1 -1
  49. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +2 -1
  50. package/es/rce/plugins/instructure_record/VideoOptionsTray/index.js +10 -7
  51. package/es/rce/plugins/instructure_record/mediaTranslations.js +1 -1
  52. package/es/rce/plugins/instructure_studio_media_options/plugin.js +109 -14
  53. package/es/rce/plugins/instructure_studio_media_options/studioToolbarIcons.d.ts +5 -0
  54. package/es/rce/plugins/instructure_studio_media_options/studioToolbarIcons.js +23 -0
  55. package/es/rce/plugins/instructure_wordcount_header/plugin.d.ts +1 -0
  56. package/es/rce/plugins/instructure_wordcount_header/plugin.js +75 -0
  57. package/es/rce/plugins/shared/ContentSelection.d.ts +1 -2
  58. package/es/rce/plugins/shared/ContentSelection.js +1 -18
  59. package/es/rce/plugins/shared/DimensionsInput/index.js +3 -3
  60. package/es/rce/plugins/shared/FixedContentTray.d.ts +7 -23
  61. package/es/rce/plugins/shared/FixedContentTray.js +7 -16
  62. package/es/rce/plugins/shared/ImageCropper/constants.d.ts +1 -1
  63. package/es/rce/plugins/shared/ImageCropper/constants.js +1 -1
  64. package/es/rce/plugins/shared/ImageCropper/controls/CustomNumberInput.js +1 -1
  65. package/es/rce/plugins/shared/PreviewIcon.js +1 -1
  66. package/es/rce/plugins/shared/StudioLtiSupportUtils.d.ts +9 -1
  67. package/es/rce/plugins/shared/StudioLtiSupportUtils.js +94 -1
  68. package/es/rce/plugins/shared/Upload/ComputerPanel.js +1 -1
  69. package/es/rce/plugins/shared/Upload/UploadFileModal.js +37 -4
  70. package/es/rce/plugins/shared/Upload/VideoUrlPanel.d.ts +15 -0
  71. package/es/rce/plugins/shared/Upload/VideoUrlPanel.js +51 -0
  72. package/es/rce/plugins/shared/Upload/videoValidationUtils.d.ts +7 -0
  73. package/es/rce/plugins/shared/Upload/videoValidationUtils.js +58 -0
  74. package/es/rce/plugins/shared/ai_tools/aiicons.d.ts +3 -3
  75. package/es/rce/plugins/shared/ai_tools/aiicons.js +11 -11
  76. package/es/rce/plugins/shared/iframeUtils.d.ts +1 -0
  77. package/es/rce/plugins/shared/iframeUtils.js +37 -0
  78. package/es/rce/plugins/tinymce-a11y-checker/components/checker.js +7 -1
  79. package/es/rce/plugins/tinymce-a11y-checker/utils/indicate.d.ts +1 -1
  80. package/es/rce/plugins/tinymce-a11y-checker/utils/indicate.js +1 -1
  81. package/es/rce/style.js +19 -17
  82. package/es/rce/tinyRCE.js +2 -0
  83. package/es/sidebar/actions/upload.d.ts +1 -0
  84. package/es/sidebar/actions/upload.js +56 -0
  85. package/es/sidebar/containers/sidebarHandlers.d.ts +1 -0
  86. package/es/sidebar/containers/sidebarHandlers.js +2 -1
  87. package/es/translations/locales/ar.js +44 -11
  88. package/es/translations/locales/ca.js +47 -14
  89. package/es/translations/locales/cy.js +44 -11
  90. package/es/translations/locales/da-x-k12.js +44 -11
  91. package/es/translations/locales/da.js +44 -11
  92. package/es/translations/locales/de.js +44 -11
  93. package/es/translations/locales/el.js +6 -0
  94. package/es/translations/locales/en-AU-x-unimelb.js +44 -11
  95. package/es/translations/locales/en-GB-x-ukhe.js +44 -11
  96. package/es/translations/locales/en.js +47 -11
  97. package/es/translations/locales/en_AU.js +44 -11
  98. package/es/translations/locales/en_CA.js +44 -11
  99. package/es/translations/locales/en_CY.js +44 -11
  100. package/es/translations/locales/en_GB.js +44 -11
  101. package/es/translations/locales/es.js +44 -11
  102. package/es/translations/locales/es_ES.js +44 -11
  103. package/es/translations/locales/fa_IR.js +6 -6
  104. package/es/translations/locales/fi.js +44 -11
  105. package/es/translations/locales/fr.js +44 -11
  106. package/es/translations/locales/fr_CA.js +49 -16
  107. package/es/translations/locales/ga.js +61 -28
  108. package/es/translations/locales/he.js +6 -0
  109. package/es/translations/locales/hi.js +44 -11
  110. package/es/translations/locales/ht.js +44 -11
  111. package/es/translations/locales/hu.js +6 -12
  112. package/es/translations/locales/hy.js +6 -0
  113. package/es/translations/locales/id.js +44 -11
  114. package/es/translations/locales/is.js +44 -11
  115. package/es/translations/locales/it.js +44 -11
  116. package/es/translations/locales/ja.js +44 -11
  117. package/es/translations/locales/ko.js +6 -0
  118. package/es/translations/locales/mi.js +44 -11
  119. package/es/translations/locales/ms.js +44 -11
  120. package/es/translations/locales/nb-x-k12.js +44 -11
  121. package/es/translations/locales/nb.js +44 -11
  122. package/es/translations/locales/nl.js +44 -11
  123. package/es/translations/locales/nn.js +6 -12
  124. package/es/translations/locales/pl.js +44 -11
  125. package/es/translations/locales/pt.js +44 -11
  126. package/es/translations/locales/pt_BR.js +44 -11
  127. package/es/translations/locales/ru.js +44 -11
  128. package/es/translations/locales/sl.js +44 -11
  129. package/es/translations/locales/sv-x-k12.js +44 -11
  130. package/es/translations/locales/sv.js +44 -11
  131. package/es/translations/locales/th.js +44 -11
  132. package/es/translations/locales/tr.js +6 -3
  133. package/es/translations/locales/uk_UA.js +6 -9
  134. package/es/translations/locales/vi.js +44 -11
  135. package/es/translations/locales/zh-Hans.js +44 -11
  136. package/es/translations/locales/zh-Hant.js +44 -11
  137. package/es/translations/locales/zh.js +44 -11
  138. package/es/translations/locales/zh_HK.js +44 -11
  139. package/es/util/contextHelper.d.ts +7 -0
  140. package/{testcafe/axe.test.js → es/util/contextHelper.js} +10 -21
  141. package/es/util/loadingPlaceholder.js +11 -11
  142. package/eslint.config.js +3 -25
  143. package/jest/jest-setup.js +27 -2
  144. package/jest.config.js +5 -1
  145. package/package.json +61 -84
  146. package/testcafe/RCEWrapper.test.js +0 -319
  147. package/testcafe/StatusBar.test.js +0 -108
  148. package/testcafe/enhanceUserContent.html +0 -58
  149. package/testcafe/enhanceUserContent.test.js +0 -44
  150. package/testcafe/entry.jsx +0 -77
  151. package/testcafe/testcafe.html +0 -14
  152. package/webpack.testcafe.config.js +0 -61
@@ -1,4 +1,4 @@
1
- import { RceToolWrapper } from '../../RceToolWrapper';
1
+ import { RceToolWrapper } from "../../RceToolWrapper";
2
2
  /**
3
3
  * Returns a filtered list of items based on the term.
4
4
  *
@@ -17,22 +17,22 @@ import _pt from "prop-types";
17
17
  * with this program. If not, see <http://www.gnu.org/licenses/>.
18
18
  */
19
19
 
20
- import React, { useState } from 'react';
21
- import { Modal } from '@instructure/ui-modal';
22
- import { Button, CloseButton } from '@instructure/ui-buttons';
23
- import { Heading } from '@instructure/ui-heading';
24
- import { View } from '@instructure/ui-view';
25
- import { ScreenReaderContent } from '@instructure/ui-a11y-content';
26
- import { List } from '@instructure/ui-list';
27
- import { Flex } from '@instructure/ui-flex';
28
- import { TextInput } from '@instructure/ui-text-input';
29
- import { IconSearchLine } from '@instructure/ui-icons';
30
- import { Alert } from '@instructure/ui-alerts';
31
- import formatMessage from '../../../../../format-message';
32
- import ExternalToolSelectionItem from './ExternalToolSelectionItem';
33
- import { instuiPopupMountNodeFn } from '../../../../../util/fullscreenHelpers';
20
+ import React, { useState } from "react";
21
+ import { Modal } from "@instructure/ui-modal";
22
+ import { Button, CloseButton } from "@instructure/ui-buttons";
23
+ import { Heading } from "@instructure/ui-heading";
24
+ import { View } from "@instructure/ui-view";
25
+ import { ScreenReaderContent } from "@instructure/ui-a11y-content";
26
+ import { List } from "@instructure/ui-list";
27
+ import { Flex } from "@instructure/ui-flex";
28
+ import { TextInput } from "@instructure/ui-text-input";
29
+ import { IconSearchLine } from "@instructure/ui-icons";
30
+ import { Alert } from "@instructure/ui-alerts";
31
+ import formatMessage from "../../../../../format-message";
32
+ import ExternalToolSelectionItem from "./ExternalToolSelectionItem";
33
+ import { instuiPopupMountNodeFn } from "../../../../../util/fullscreenHelpers";
34
34
  // TODO: we really need a way for the client to pass this to the RCE
35
- const getLiveRegion = () => document.getElementById('flash_screenreader_holder');
35
+ const getLiveRegion = () => document.getElementById("flash_screenreader_holder");
36
36
 
37
37
  /**
38
38
  * Returns a filtered list of items based on the term.
@@ -51,7 +51,7 @@ export function filterItemsByTitleSubstring(searchString, items) {
51
51
  return items.filter(item => item.title.toLocaleLowerCase().includes(lowerTerm));
52
52
  }
53
53
  export function ExternalToolSelectionDialog(props) {
54
- const [filterTerm, setFilterTerm] = useState('');
54
+ const [filterTerm, setFilterTerm] = useState("");
55
55
  const [filteredResults, setFilteredResults] = useState(props.ltiButtons);
56
56
  const handleFilterChange = e => {
57
57
  setFilterTerm(e.target?.value);
@@ -63,31 +63,31 @@ export function ExternalToolSelectionDialog(props) {
63
63
  liveRegion: getLiveRegion,
64
64
  size: "medium",
65
65
  themeOverride: {
66
- mediumMaxWidth: '42rem'
66
+ mediumMaxWidth: "42rem"
67
67
  },
68
- label: formatMessage('Apps'),
68
+ label: formatMessage("Apps"),
69
69
  mountNode: instuiPopupMountNodeFn(),
70
70
  onDismiss: props.onDismiss,
71
71
  open: true,
72
72
  shouldCloseOnDocumentClick: false
73
73
  }, /*#__PURE__*/React.createElement(Modal.Header, {
74
74
  themeOverride: {
75
- padding: '0.5rem'
75
+ padding: "0.5rem"
76
76
  }
77
77
  }, /*#__PURE__*/React.createElement(CloseButton, {
78
78
  placement: "end",
79
79
  offset: "medium",
80
80
  onClick: props.onDismiss,
81
- screenReaderLabel: formatMessage('Close')
81
+ screenReaderLabel: formatMessage("Close")
82
82
  }), /*#__PURE__*/React.createElement(Heading, {
83
83
  margin: "small"
84
- }, formatMessage('All Apps')), /*#__PURE__*/React.createElement(View, {
84
+ }, formatMessage("All Apps")), /*#__PURE__*/React.createElement(View, {
85
85
  as: "div",
86
86
  padding: "x-small none x-small medium"
87
87
  }, /*#__PURE__*/React.createElement(TextInput, {
88
88
  type: "search",
89
- renderLabel: /*#__PURE__*/React.createElement(ScreenReaderContent, null, formatMessage('Search')),
90
- placeholder: formatMessage('Search'),
89
+ renderLabel: /*#__PURE__*/React.createElement(ScreenReaderContent, null, formatMessage("Search")),
90
+ placeholder: formatMessage("Search"),
91
91
  renderAfterInput: /*#__PURE__*/React.createElement(IconSearchLine, {
92
92
  inline: false
93
93
  }),
@@ -105,7 +105,7 @@ export function ExternalToolSelectionDialog(props) {
105
105
  liveRegion: getLiveRegion,
106
106
  variant: "info",
107
107
  screenReaderOnly: !filterEmpty
108
- }, filterEmpty && formatMessage('No results found for {filterTerm}', {
108
+ }, filterEmpty && formatMessage("No results found for {filterTerm}", {
109
109
  filterTerm
110
110
  }), !filterEmpty && formatMessage(`Found { count, plural,
111
111
  =0 {# results}
@@ -116,7 +116,7 @@ export function ExternalToolSelectionDialog(props) {
116
116
  })), renderTools(filteredResults)))), /*#__PURE__*/React.createElement(Modal.Footer, null, /*#__PURE__*/React.createElement(Button, {
117
117
  onClick: props.onDismiss,
118
118
  color: "primary"
119
- }, formatMessage('Done'))));
119
+ }, formatMessage("Done"))));
120
120
  function renderTools(ltiButtons) {
121
121
  return /*#__PURE__*/React.createElement(List, {
122
122
  isUnstyled: true
@@ -3,7 +3,7 @@ export default class TrayController {
3
3
  _isOpen: boolean;
4
4
  _shouldOpen: boolean;
5
5
  _editor: any;
6
- _audioContainer: any;
6
+ _audioContainer: Element | null;
7
7
  get container(): HTMLElement;
8
8
  get isOpen(): boolean;
9
9
  showTrayForEditor(editor: any): void;
@@ -19,7 +19,8 @@
19
19
  import React from 'react';
20
20
  import ReactDOM from 'react-dom';
21
21
  import bridge from '../../../../bridge';
22
- import { asAudioElement, findMediaPlayerIframe } from '../../shared/ContentSelection';
22
+ import { asAudioElement } from '../../shared/ContentSelection';
23
+ import { findMediaPlayerIframe } from '../../shared/iframeUtils';
23
24
  import AudioOptionsTray from '.';
24
25
  export const CONTAINER_ID = 'instructure-audio-options-tray-container';
25
26
  export default class TrayController {
@@ -27,7 +27,7 @@ export default class TrayController {
27
27
  get $container(): HTMLElement;
28
28
  get isOpen(): boolean;
29
29
  showTrayForEditor(editor: any): void;
30
- $videoContainer: any;
30
+ $videoContainer: Element | null | undefined;
31
31
  hideTrayForEditor(editor: any): void;
32
32
  _applyVideoOptions(videoOptions: any): void;
33
33
  _dismissTray(): void;
@@ -19,7 +19,8 @@
19
19
  import React from 'react';
20
20
  import ReactDOM from 'react-dom';
21
21
  import bridge from '../../../../bridge';
22
- import { asVideoElement, findMediaPlayerIframe } from '../../shared/ContentSelection';
22
+ import { asVideoElement } from '../../shared/ContentSelection';
23
+ import { findMediaPlayerIframe } from '../../shared/iframeUtils';
23
24
  import VideoOptionsTray from '.';
24
25
  import { isStudioEmbeddedMedia, parseStudioOptions } from '../../shared/StudioLtiSupportUtils';
25
26
  import RCEGlobals from '../../../RCEGlobals';
@@ -32,7 +32,7 @@ import { Tooltip } from '@instructure/ui-tooltip';
32
32
  import { Tray } from '@instructure/ui-tray';
33
33
  import { StoreProvider } from '../../shared/StoreContext';
34
34
  import { ClosedCaptionPanel } from '@instructure/canvas-media';
35
- import { CUSTOM, MIN_WIDTH_VIDEO, MIN_PERCENTAGE, videoSizes, labelForImageSize, scaleToSize } from '../../instructure_image/ImageEmbedOptions';
35
+ import { CUSTOM, MIN_WIDTH_VIDEO, MIN_PERCENTAGE, videoSizes, studioPlayerSizes, labelForImageSize, scaleVideoSize, scaleToSize, MIN_WIDTH_STUDIO_PLAYER, MIN_HEIGHT_STUDIO_PLAYER } from '../../instructure_image/ImageEmbedOptions';
36
36
  import Bridge from '../../../../bridge';
37
37
  import RceApiSource from '../../../../rcs/api';
38
38
  import formatMessage from '../../../../format-message';
@@ -40,6 +40,7 @@ import DimensionsInput, { useDimensionsState } from '../../shared/DimensionsInpu
40
40
  import { getTrayHeight } from '../../shared/trayUtils';
41
41
  import { instuiPopupMountNodeFn } from '../../../../util/fullscreenHelpers';
42
42
  import { parsedStudioOptionsPropType } from '../../shared/StudioLtiSupportUtils';
43
+ import RCEGlobals from '../../../../rce/RCEGlobals';
43
44
  const getLiveRegion = () => document.getElementById('flash_screenreader_holder');
44
45
  export default function VideoOptionsTray({
45
46
  videoOptions,
@@ -54,6 +55,7 @@ export default function VideoOptionsTray({
54
55
  studioOptions = null,
55
56
  forBlockEditorUse = false
56
57
  }) {
58
+ const isConsolidatedMediaPlayer = RCEGlobals.getFeatures()?.consolidated_media_player;
57
59
  const {
58
60
  naturalHeight,
59
61
  naturalWidth
@@ -66,8 +68,8 @@ export default function VideoOptionsTray({
66
68
  const [videoHeight, setVideoHeight] = useState(currentHeight);
67
69
  const [videoWidth, setVideoWidth] = useState(currentWidth);
68
70
  const [subtitles, setSubtitles] = useState(videoOptions.tracks || []);
69
- const [minWidth] = useState(MIN_WIDTH_VIDEO);
70
- const [minHeight] = useState(Math.round(videoHeight / videoWidth * MIN_WIDTH_VIDEO));
71
+ const [minWidth] = useState(isConsolidatedMediaPlayer ? MIN_WIDTH_STUDIO_PLAYER : MIN_WIDTH_VIDEO);
72
+ const [minHeight] = useState(isConsolidatedMediaPlayer ? MIN_HEIGHT_STUDIO_PLAYER : Math.round(videoHeight / videoWidth * MIN_WIDTH_VIDEO));
71
73
  const [minPercentage] = useState(MIN_PERCENTAGE);
72
74
  const [editLocked, setEditLocked] = useState(null);
73
75
  const [loading, setLoading] = useState(true);
@@ -80,6 +82,7 @@ export default function VideoOptionsTray({
80
82
  minPercentage
81
83
  });
82
84
  const api = new RceApiSource(trayProps);
85
+ const videoSizeOptions = isConsolidatedMediaPlayer ? studioPlayerSizes : videoSizes;
83
86
  useEffect(() => {
84
87
  if (videoOptions.attachmentId) {
85
88
  api.getFile(videoOptions.attachmentId, {
@@ -87,7 +90,7 @@ export default function VideoOptionsTray({
87
90
  }).then(response => {
88
91
  setEditLocked(response?.restricted_by_master_course && response?.is_master_course_child_content);
89
92
  setLoading(false);
90
- }).catch(error => {
93
+ }).catch(_error => {
91
94
  setLoading(false);
92
95
  });
93
96
  }
@@ -102,7 +105,7 @@ export default function VideoOptionsTray({
102
105
  event.target.focus();
103
106
  setDisplayAs(event.target.value);
104
107
  }
105
- function handleVideoSizeChange(event, selectedOption) {
108
+ function handleVideoSizeChange(_event, selectedOption) {
106
109
  setVideoSize(selectedOption.value);
107
110
  if (selectedOption.value === CUSTOM) {
108
111
  setVideoHeight(currentHeight);
@@ -111,7 +114,7 @@ export default function VideoOptionsTray({
111
114
  const {
112
115
  height,
113
116
  width
114
- } = scaleToSize(selectedOption.value, naturalWidth, naturalHeight);
117
+ } = isConsolidatedMediaPlayer ? scaleVideoSize(selectedOption.value, naturalWidth, naturalHeight) : scaleToSize(selectedOption.value, naturalWidth, naturalHeight);
115
118
  setVideoHeight(height);
116
119
  setVideoWidth(width);
117
120
  }
@@ -267,7 +270,7 @@ export default function VideoOptionsTray({
267
270
  assistiveText: formatMessage('Use arrow keys to navigate options.'),
268
271
  onChange: handleVideoSizeChange,
269
272
  value: videoSize
270
- }, videoSizes.map(size => /*#__PURE__*/React.createElement(SimpleSelect.Option, {
273
+ }, videoSizeOptions.map(size => /*#__PURE__*/React.createElement(SimpleSelect.Option, {
271
274
  id: `${id}-size-${size}`,
272
275
  key: size,
273
276
  value: size
@@ -24,7 +24,7 @@ import formatMessage from '../../../format-message';
24
24
  const uploadMediaTranslations = {
25
25
  UploadMediaStrings: {
26
26
  ADD_CLOSED_CAPTIONS_OR_SUBTITLES: formatMessage('Add CC/Subtitles'),
27
- CLEAR_FILE_TEXT: formatMessage('Clear selected file'),
27
+ CLEAR_FILE_TEXT: formatMessage('Remove'),
28
28
  CLOSE_TEXT: formatMessage('Close'),
29
29
  CLOSED_CAPTIONS_CHOOSE_FILE: formatMessage('Choose caption file'),
30
30
  CLOSED_CAPTIONS_SELECT_LANGUAGE: formatMessage('Select Language'),
@@ -17,24 +17,119 @@
17
17
  */
18
18
 
19
19
  import tinymce from 'tinymce';
20
- import { isStudioEmbeddedMedia, handleBeforeObjectSelected } from '../shared/StudioLtiSupportUtils';
20
+ import { isStudioEmbeddedMedia, handleBeforeObjectSelected, notifyStudioEmbedTypeChange, updateStudioIframeDimensions, isValidDimension, isValidEmbedType } from '../shared/StudioLtiSupportUtils';
21
21
  import VideoTrayController from '../instructure_record/VideoOptionsTray/TrayController';
22
22
  import formatMessage from '../../../format-message';
23
+ import RCEGlobals from '../../RCEGlobals';
24
+ import { thumbnailViewIcon, learnViewIcon, collabViewIcon, optionsIcon, removeIcon } from './studioToolbarIcons';
23
25
  const studioTrayController = new VideoTrayController();
26
+ const handleStudioMessage = e => {
27
+ if (e.data && e.data.subject === 'studio.embedTypeChanged.response' && isValidDimension(e.data.width) && isValidDimension(e.data.height) && isValidEmbedType(e.data.embedType)) {
28
+ updateStudioIframeDimensions(tinymce.activeEditor, e.data.width, e.data.height, e.data.embedType);
29
+ }
30
+ };
24
31
  tinymce.PluginManager.add('instructure_studio_media_options', function (ed) {
25
- ed.ui.registry.addButton('studio-media-options', {
26
- onAction() {
27
- studioTrayController.showTrayForEditor(ed);
28
- },
29
- text: formatMessage('Studio Media Options'),
30
- tooltip: formatMessage('Show Studio media options')
31
- });
32
- ed.ui.registry.addContextToolbar('studio-media-options-toolbar', {
33
- items: 'studio-media-options',
34
- position: 'node',
35
- predicate: isStudioEmbeddedMedia,
36
- scope: 'node'
37
- });
32
+ if (RCEGlobals.getFeatures().rce_studio_embed_improvements) {
33
+ window.addEventListener('message', handleStudioMessage);
34
+ ed.on('init', () => {
35
+ const existingStyle = document.getElementById('studio-toolbar-styles');
36
+ if (!existingStyle) {
37
+ const style = document.createElement('style');
38
+ style.id = 'studio-toolbar-styles';
39
+ style.textContent = `
40
+ .tox .tox-pop .tox-tbtn {
41
+ font-size: 16px;
42
+ border-radius: 0;
43
+ }
44
+
45
+ .tox .tox-pop .tox-tbtn:hover {
46
+ background-color: #2B7ABC;
47
+ color: white;
48
+ }
49
+ `;
50
+ document.head.appendChild(style);
51
+ }
52
+ });
53
+ ed.ui.registry.addIcon('thumbnail-view-icon', thumbnailViewIcon);
54
+ ed.ui.registry.addIcon('learn-view-icon', learnViewIcon);
55
+ ed.ui.registry.addIcon('collab-view-icon', collabViewIcon);
56
+ ed.ui.registry.addIcon('options-icon', optionsIcon);
57
+ ed.ui.registry.addIcon('remove-icon', removeIcon);
58
+ ed.ui.registry.addButton('thumbnail-view', {
59
+ onAction() {
60
+ notifyStudioEmbedTypeChange(ed, 'thumbnail_embed');
61
+ },
62
+ icon: 'thumbnail-view-icon',
63
+ text: formatMessage('Thumbnail'),
64
+ tooltip: formatMessage('Thumbnail')
65
+ });
66
+ ed.ui.registry.addButton('learn-view', {
67
+ onAction() {
68
+ notifyStudioEmbedTypeChange(ed, 'learn_embed');
69
+ },
70
+ icon: 'learn-view-icon',
71
+ text: formatMessage('Learn'),
72
+ tooltip: formatMessage('Learn')
73
+ });
74
+ ed.ui.registry.addButton('collab-view', {
75
+ onAction() {
76
+ notifyStudioEmbedTypeChange(ed, 'collaboration_embed');
77
+ },
78
+ icon: 'collab-view-icon',
79
+ text: formatMessage('Collab'),
80
+ tooltip: formatMessage('Collab')
81
+ });
82
+ ed.ui.registry.addButton('studio-media-options', {
83
+ onAction() {
84
+ if (!studioTrayController.isOpen) {
85
+ studioTrayController.showTrayForEditor(ed);
86
+ }
87
+ },
88
+ icon: 'options-icon',
89
+ text: formatMessage('Options'),
90
+ tooltip: formatMessage('Options')
91
+ });
92
+ ed.ui.registry.addButton('remove-studio-media', {
93
+ onAction() {
94
+ const selectedElement = ed.selection.getNode();
95
+ if (selectedElement && isStudioEmbeddedMedia(selectedElement)) {
96
+ studioTrayController.hideTrayForEditor(ed);
97
+
98
+ // Hide toolbar, reset selection
99
+ ed.fire('hidecontexttoolbar');
100
+ ed.dom.remove(selectedElement);
101
+ ed.nodeChanged();
102
+ ed.selection.select(ed.getBody());
103
+
104
+ // Force focus back to editor
105
+ ed.focus();
106
+ }
107
+ },
108
+ icon: 'remove-icon',
109
+ text: formatMessage('Remove'),
110
+ tooltip: formatMessage('Remove Studio Media')
111
+ });
112
+ ed.ui.registry.addContextToolbar('studio-extra-toolbar', {
113
+ items: 'thumbnail-view | learn-view | collab-view | studio-media-options | remove-studio-media',
114
+ position: 'node',
115
+ predicate: isStudioEmbeddedMedia,
116
+ scope: 'node'
117
+ });
118
+ } else {
119
+ ed.ui.registry.addButton('studio-media-options', {
120
+ onAction() {
121
+ studioTrayController.showTrayForEditor(ed);
122
+ },
123
+ text: formatMessage('Studio Media Options'),
124
+ tooltip: formatMessage('Show Studio media options')
125
+ });
126
+ ed.ui.registry.addContextToolbar('studio-media-options-toolbar', {
127
+ items: 'studio-media-options',
128
+ position: 'node',
129
+ predicate: isStudioEmbeddedMedia,
130
+ scope: 'node'
131
+ });
132
+ }
38
133
  ed.on('BeforeObjectSelected', handleBeforeObjectSelected);
39
134
  ed.on('remove', editor => {
40
135
  studioTrayController.hideTrayForEditor(editor);
@@ -0,0 +1,5 @@
1
+ export declare const thumbnailViewIcon = "<svg width=\"24\" height=\"17\" viewBox=\"0 0 24 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_1209_167295)\"><path d=\"M24 17H0V0H24V17ZM1.10983 1.10569V15.8943H22.8902V1.10569H1.10983Z\" fill=\"currentColor\"/><path d=\"M21.7804 14.7887H2.21973V2.21143H21.7804V14.7887ZM9.84978 11.4716L14.5665 8.63826L9.84978 5.80492V11.4716Z\" fill=\"currentColor\"/></g><defs><clipPath id=\"clip0_1209_167295\"><rect width=\"24\" height=\"17\" fill=\"white\"/></clipPath></defs></svg>";
2
+ export declare const learnViewIcon = "<svg width=\"31\" height=\"22\" viewBox=\"0 0 31 22\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_93_30328)\"><path d=\"M31 22H0V0H31V22ZM1.43353 1.43089V20.5691H29.5665V1.43089H1.43353Z\" fill=\"currentColor\"/><path d=\"M27.9537 3.04102H19.7109V18.9597H27.9537V3.04102Z\" fill=\"currentColor\"/><path d=\"M18.2781 18.9597H3.04688V3.04102H18.2781V18.9597ZM8.02537 14.7989L14.3165 11.0907L8.02537 7.38244V14.7989Z\" fill=\"currentColor\"/></g><defs><clipPath id=\"clip0_93_30328\"><rect width=\"31\" height=\"22\" fill=\"white\"/></clipPath></defs></svg>";
3
+ export declare const collabViewIcon = "<svg width=\"24\" height=\"17\" viewBox=\"0 0 24 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_1209_167302)\"><path d=\"M24 17H0V0H24V17ZM1.10983 1.10569V15.8943H22.8902V1.10569H1.10983Z\" fill=\"currentColor\"/><path d=\"M21.6416 2.34961H17.3411V8.15449H21.6416V2.34961Z\" fill=\"currentColor\"/><path d=\"M16.2312 8.15449H2.3584V2.34961H16.2312V8.15449ZM8.60117 7.18701L11.7919 5.39026L8.60117 3.59351V7.18701Z\" fill=\"currentColor\"/><path d=\"M21.6415 12.5771H4.99414V13.4064H21.6415V12.5771Z\" fill=\"currentColor\"/><path d=\"M21.6415 13.8213H4.99414V14.6506H21.6415V13.8213Z\" fill=\"currentColor\"/><path d=\"M3.39886 14.6503C3.97349 14.6503 4.43932 14.1862 4.43932 13.6137C4.43932 13.0412 3.97349 12.5771 3.39886 12.5771C2.82423 12.5771 2.3584 13.0412 2.3584 13.6137C2.3584 14.1862 2.82423 14.6503 3.39886 14.6503Z\" fill=\"currentColor\"/><path d=\"M21.6415 9.39844H4.99414V10.2277H21.6415V9.39844Z\" fill=\"currentColor\"/><path d=\"M21.6415 10.6421H4.99414V11.4714H21.6415V10.6421Z\" fill=\"currentColor\"/><path d=\"M3.39886 11.4716C3.97349 11.4716 4.43932 11.0075 4.43932 10.435C4.43932 9.86253 3.97349 9.39844 3.39886 9.39844C2.82423 9.39844 2.3584 9.86253 2.3584 10.435C2.3584 11.0075 2.82423 11.4716 3.39886 11.4716Z\" fill=\"currentColor\"/></g><defs><clipPath id=\"clip0_1209_167302\"><rect width=\"24\" height=\"17\" fill=\"white\"/></clipPath></defs></svg>";
4
+ export declare const optionsIcon = "<svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_1217_47603)\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M2.46326 3.85039H0V2.65039H2.46326C2.7388 1.36449 3.88181 0.400391 5.24999 0.400391C6.61818 0.400391 7.76119 1.36449 8.03673 2.65039H18V3.85039H8.03673C7.76119 5.13629 6.61818 6.10039 5.24999 6.10039C3.88181 6.10039 2.7388 5.13629 2.46326 3.85039ZM3.59999 3.25039C3.59999 2.33912 4.33872 1.60039 5.24999 1.60039C6.16126 1.60039 6.89999 2.33912 6.89999 3.25039C6.89999 4.16166 6.16126 4.90039 5.24999 4.90039C4.33872 4.90039 3.59999 4.16166 3.59999 3.25039Z\" fill=\"currentColor\"/><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M0 8.40039H9.46326C9.7388 7.11449 10.8818 6.15039 12.25 6.15039C13.6182 6.15039 14.7612 7.11449 15.0367 8.40039H18V9.60039H15.0367C14.7612 10.8863 13.6182 11.8504 12.25 11.8504C10.8818 11.8504 9.7388 10.8863 9.46326 9.60039H0V8.40039ZM12.25 7.35039C11.3387 7.35039 10.6 8.08912 10.6 9.00039C10.6 9.91166 11.3387 10.6504 12.25 10.6504C13.1613 10.6504 13.9 9.91166 13.9 9.00039C13.9 8.08912 13.1613 7.35039 12.25 7.35039Z\" fill=\"currentColor\"/><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M5.24999 17.6004C6.61818 17.6004 7.76119 16.6363 8.03673 15.3504H18V14.1504H8.03673C7.76119 12.8645 6.61818 11.9004 5.24999 11.9004C3.88181 11.9004 2.7388 12.8645 2.46326 14.1504H0V15.3504H2.46326C2.7388 16.6363 3.88181 17.6004 5.24999 17.6004ZM5.24999 13.1004C4.33872 13.1004 3.59999 13.8391 3.59999 14.7504C3.59999 15.6617 4.33872 16.4004 5.24999 16.4004C6.16126 16.4004 6.89999 15.6617 6.89999 14.7504C6.89999 13.8391 6.16126 13.1004 5.24999 13.1004Z\" fill=\"currentColor\"/></g><defs><clipPath id=\"clip0_1217_47603\"><rect width=\"18\" height=\"18\" fill=\"white\"/></clipPath></defs></svg>";
5
+ export declare const removeIcon = "<svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M14.8234 16.4118C14.8234 16.7029 14.5852 16.9412 14.294 16.9412H3.70577C3.41459 16.9412 3.17636 16.7029 3.17636 16.4118V5.29412H2.11753V16.4118C2.11753 17.2874 2.83012 18 3.70577 18H14.294C15.1697 18 15.8822 17.2874 15.8822 16.4118V5.29412H14.8234V16.4118ZM6.35284 14.8235H7.41166V6.35294H6.35284V14.8235ZM10.5881 14.8235H11.6469V6.35294H10.5881V14.8235ZM12.5682 3.17647L11.4099 0H6.51176L5.35553 3.17647H0V4.23529H18V3.17647H12.5682ZM6.48105 3.17647L7.25293 1.05882H10.6698L11.4416 3.17647H6.48105Z\" fill=\"currentColor\"/></svg>";
@@ -0,0 +1,23 @@
1
+ /*
2
+ * Copyright (C) 2023 - 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
+ export const thumbnailViewIcon = '<svg width="24" height="17" viewBox="0 0 24 17" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1209_167295)"><path d="M24 17H0V0H24V17ZM1.10983 1.10569V15.8943H22.8902V1.10569H1.10983Z" fill="currentColor"/><path d="M21.7804 14.7887H2.21973V2.21143H21.7804V14.7887ZM9.84978 11.4716L14.5665 8.63826L9.84978 5.80492V11.4716Z" fill="currentColor"/></g><defs><clipPath id="clip0_1209_167295"><rect width="24" height="17" fill="white"/></clipPath></defs></svg>';
20
+ export const learnViewIcon = '<svg width="31" height="22" viewBox="0 0 31 22" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_93_30328)"><path d="M31 22H0V0H31V22ZM1.43353 1.43089V20.5691H29.5665V1.43089H1.43353Z" fill="currentColor"/><path d="M27.9537 3.04102H19.7109V18.9597H27.9537V3.04102Z" fill="currentColor"/><path d="M18.2781 18.9597H3.04688V3.04102H18.2781V18.9597ZM8.02537 14.7989L14.3165 11.0907L8.02537 7.38244V14.7989Z" fill="currentColor"/></g><defs><clipPath id="clip0_93_30328"><rect width="31" height="22" fill="white"/></clipPath></defs></svg>';
21
+ export const collabViewIcon = '<svg width="24" height="17" viewBox="0 0 24 17" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1209_167302)"><path d="M24 17H0V0H24V17ZM1.10983 1.10569V15.8943H22.8902V1.10569H1.10983Z" fill="currentColor"/><path d="M21.6416 2.34961H17.3411V8.15449H21.6416V2.34961Z" fill="currentColor"/><path d="M16.2312 8.15449H2.3584V2.34961H16.2312V8.15449ZM8.60117 7.18701L11.7919 5.39026L8.60117 3.59351V7.18701Z" fill="currentColor"/><path d="M21.6415 12.5771H4.99414V13.4064H21.6415V12.5771Z" fill="currentColor"/><path d="M21.6415 13.8213H4.99414V14.6506H21.6415V13.8213Z" fill="currentColor"/><path d="M3.39886 14.6503C3.97349 14.6503 4.43932 14.1862 4.43932 13.6137C4.43932 13.0412 3.97349 12.5771 3.39886 12.5771C2.82423 12.5771 2.3584 13.0412 2.3584 13.6137C2.3584 14.1862 2.82423 14.6503 3.39886 14.6503Z" fill="currentColor"/><path d="M21.6415 9.39844H4.99414V10.2277H21.6415V9.39844Z" fill="currentColor"/><path d="M21.6415 10.6421H4.99414V11.4714H21.6415V10.6421Z" fill="currentColor"/><path d="M3.39886 11.4716C3.97349 11.4716 4.43932 11.0075 4.43932 10.435C4.43932 9.86253 3.97349 9.39844 3.39886 9.39844C2.82423 9.39844 2.3584 9.86253 2.3584 10.435C2.3584 11.0075 2.82423 11.4716 3.39886 11.4716Z" fill="currentColor"/></g><defs><clipPath id="clip0_1209_167302"><rect width="24" height="17" fill="white"/></clipPath></defs></svg>';
22
+ export const optionsIcon = '<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1217_47603)"><path fill-rule="evenodd" clip-rule="evenodd" d="M2.46326 3.85039H0V2.65039H2.46326C2.7388 1.36449 3.88181 0.400391 5.24999 0.400391C6.61818 0.400391 7.76119 1.36449 8.03673 2.65039H18V3.85039H8.03673C7.76119 5.13629 6.61818 6.10039 5.24999 6.10039C3.88181 6.10039 2.7388 5.13629 2.46326 3.85039ZM3.59999 3.25039C3.59999 2.33912 4.33872 1.60039 5.24999 1.60039C6.16126 1.60039 6.89999 2.33912 6.89999 3.25039C6.89999 4.16166 6.16126 4.90039 5.24999 4.90039C4.33872 4.90039 3.59999 4.16166 3.59999 3.25039Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M0 8.40039H9.46326C9.7388 7.11449 10.8818 6.15039 12.25 6.15039C13.6182 6.15039 14.7612 7.11449 15.0367 8.40039H18V9.60039H15.0367C14.7612 10.8863 13.6182 11.8504 12.25 11.8504C10.8818 11.8504 9.7388 10.8863 9.46326 9.60039H0V8.40039ZM12.25 7.35039C11.3387 7.35039 10.6 8.08912 10.6 9.00039C10.6 9.91166 11.3387 10.6504 12.25 10.6504C13.1613 10.6504 13.9 9.91166 13.9 9.00039C13.9 8.08912 13.1613 7.35039 12.25 7.35039Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M5.24999 17.6004C6.61818 17.6004 7.76119 16.6363 8.03673 15.3504H18V14.1504H8.03673C7.76119 12.8645 6.61818 11.9004 5.24999 11.9004C3.88181 11.9004 2.7388 12.8645 2.46326 14.1504H0V15.3504H2.46326C2.7388 16.6363 3.88181 17.6004 5.24999 17.6004ZM5.24999 13.1004C4.33872 13.1004 3.59999 13.8391 3.59999 14.7504C3.59999 15.6617 4.33872 16.4004 5.24999 16.4004C6.16126 16.4004 6.89999 15.6617 6.89999 14.7504C6.89999 13.8391 6.16126 13.1004 5.24999 13.1004Z" fill="currentColor"/></g><defs><clipPath id="clip0_1217_47603"><rect width="18" height="18" fill="white"/></clipPath></defs></svg>';
23
+ export const removeIcon = '<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M14.8234 16.4118C14.8234 16.7029 14.5852 16.9412 14.294 16.9412H3.70577C3.41459 16.9412 3.17636 16.7029 3.17636 16.4118V5.29412H2.11753V16.4118C2.11753 17.2874 2.83012 18 3.70577 18H14.294C15.1697 18 15.8822 17.2874 15.8822 16.4118V5.29412H14.8234V16.4118ZM6.35284 14.8235H7.41166V6.35294H6.35284V14.8235ZM10.5881 14.8235H11.6469V6.35294H10.5881V14.8235ZM12.5682 3.17647L11.4099 0H6.51176L5.35553 3.17647H0V4.23529H18V3.17647H12.5682ZM6.48105 3.17647L7.25293 1.05882H10.6698L11.4416 3.17647H6.48105Z" fill="currentColor"/></svg>';
@@ -0,0 +1,75 @@
1
+ /*
2
+ * Copyright (C) 2024 - 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 tinymce from 'tinymce';
20
+ import formatMessage from '../../../format-message';
21
+ import { debounce } from '@instructure/debounce';
22
+ const clickCallbackPromise = import('../instructure_wordcount/clickCallback');
23
+ const TOOLTIP_MESSAGE = formatMessage('View word and character counts');
24
+ const UPDATE_DEBOUNCE_MS = 100;
25
+ function formatWordCount(count) {
26
+ return formatMessage(`{count, plural,
27
+ =0 {0 words}
28
+ one {1 word}
29
+ other {# words}
30
+ }`, {
31
+ count
32
+ });
33
+ }
34
+ tinymce.PluginManager.add('instructure_wordcount_header', function (ed) {
35
+ function updateWordCountDisplay() {
36
+ const count = ed.plugins.wordcount.body.getWordCount();
37
+ const button = ed.getContainer()?.querySelector(`[title*="${TOOLTIP_MESSAGE}"]`);
38
+ if (!button) {
39
+ return;
40
+ }
41
+ const textSpan = button.querySelector('.tox-tbtn__select-label');
42
+ if (textSpan) {
43
+ textSpan.textContent = formatWordCount(count);
44
+ }
45
+ const tooltip = `${TOOLTIP_MESSAGE} - ${formatWordCount(count)}`;
46
+ button.setAttribute('title', tooltip);
47
+ button.setAttribute('aria-label', tooltip);
48
+ }
49
+ ed.addCommand('instructureWordCountHeader', () => {
50
+ clickCallbackPromise.then(module => module.default(ed, document, {
51
+ skipEditorFocus: false
52
+ }));
53
+ });
54
+ ed.ui.registry.addButton('instructure_wordcount_header', {
55
+ text: formatWordCount(0),
56
+ tooltip: TOOLTIP_MESSAGE,
57
+ onAction: () => ed.execCommand('instructureWordCountHeader')
58
+ });
59
+ ed.on('PostRender', () => {
60
+ updateWordCountDisplay();
61
+ });
62
+ const debouncedUpdate = debounce(updateWordCountDisplay, UPDATE_DEBOUNCE_MS, {
63
+ trailing: true
64
+ });
65
+ ed.on('NodeChange', debouncedUpdate);
66
+ ed.on('KeyUp', debouncedUpdate);
67
+ ed.on('SetContent', debouncedUpdate);
68
+ ed.on('Change', debouncedUpdate);
69
+ ed.on('Undo', debouncedUpdate);
70
+ ed.on('Redo', debouncedUpdate);
71
+ ed.on('Paste', debouncedUpdate);
72
+ ed.on('init', () => {
73
+ updateWordCountDisplay();
74
+ });
75
+ });
@@ -26,7 +26,7 @@ export function asLink($element: any, editor: any): {
26
26
  export function asVideoElement($element: any): {
27
27
  $element: any;
28
28
  type: string;
29
- id: any;
29
+ id: string | null;
30
30
  titleText: any;
31
31
  appliedHeight: any;
32
32
  appliedWidth: any;
@@ -62,7 +62,6 @@ export function isFileLink($element: any, editor: any): boolean;
62
62
  export function isImageEmbed($element: any): boolean;
63
63
  export function isVideoElement($element: any): boolean;
64
64
  export function isAudioElement($element: any): boolean;
65
- export function findMediaPlayerIframe(elem: any): any;
66
65
  export const LINK_TYPE: "link";
67
66
  export const FILE_LINK_TYPE: "file-link";
68
67
  export const IMAGE_EMBED_TYPE: "image-embed";
@@ -21,6 +21,7 @@ import { isOnlyTextSelected } from '../../contentInsertionUtils';
21
21
  import formatMessage from '../../../format-message';
22
22
  import { isStudioEmbeddedMedia } from './StudioLtiSupportUtils';
23
23
  import { parseUrlPath } from '../../../util/url-util';
24
+ import { findMediaPlayerIframe } from './iframeUtils';
24
25
  const FILE_DOWNLOAD_PATH_REGEX = /^\/(courses\/\d+\/)?files\/\d+\/download$/;
25
26
  export const LINK_TYPE = 'link';
26
27
  export const FILE_LINK_TYPE = 'file-link';
@@ -208,22 +209,4 @@ export function isVideoElement($element) {
208
209
  }
209
210
  export function isAudioElement($element) {
210
211
  return isMediaElement($element, 'audio');
211
- }
212
- export function findMediaPlayerIframe(elem) {
213
- if (!elem) return null;
214
- if (elem.tagName === 'IFRAME') {
215
- // we have the iframe
216
- return elem;
217
- }
218
- if (elem.firstElementChild?.tagName === 'IFRAME') {
219
- // we have the shim tinymce puts around the iframe
220
- return elem.firstElementChild;
221
- }
222
- if (elem.classList.contains('mce-shim')) {
223
- // tinymce puts a <span class='mce-shin'> after the iframe (since v5, I think)
224
- if (elem.previousSibling?.tagName === 'IFRAME') {
225
- return elem.previousSibling;
226
- }
227
- }
228
- return null;
229
212
  }
@@ -113,11 +113,11 @@ export default function DimensionsInput(props) {
113
113
  onChange: handleDimensionTypeChange,
114
114
  value: dimensionsState.usePercentageUnits ? 'percentage' : 'pixels'
115
115
  }, /*#__PURE__*/React.createElement(RadioInput, {
116
- label: formatMessage('Pixels'),
117
- value: "pixels"
118
- }), /*#__PURE__*/React.createElement(RadioInput, {
119
116
  label: formatMessage('Percentage'),
120
117
  value: "percentage"
118
+ }), /*#__PURE__*/React.createElement(RadioInput, {
119
+ label: formatMessage('Pixels'),
120
+ value: "pixels"
121
121
  }))), /*#__PURE__*/React.createElement(Flex.Item, {
122
122
  padding: "small"
123
123
  }, /*#__PURE__*/React.createElement(FormFieldGroup, {
@@ -1,15 +1,15 @@
1
1
  export function FixedContentTray({ title, isOpen, onDismiss, onUnmount, mountNode, renderHeader, renderBody, renderFooter, bodyAs, shouldJoinBodyAndFooter, shouldCloseOnDocumentClick, }: {
2
- title: any;
3
- isOpen: any;
4
- onDismiss: any;
5
- onUnmount: any;
2
+ title?: null | undefined;
3
+ isOpen?: boolean | undefined;
4
+ onDismiss?: (() => void) | undefined;
5
+ onUnmount?: (() => void) | undefined;
6
6
  mountNode: any;
7
7
  renderHeader: any;
8
8
  renderBody: any;
9
9
  renderFooter: any;
10
- bodyAs: any;
11
- shouldJoinBodyAndFooter: any;
12
- shouldCloseOnDocumentClick: any;
10
+ bodyAs?: string | undefined;
11
+ shouldJoinBodyAndFooter?: boolean | undefined;
12
+ shouldCloseOnDocumentClick?: boolean | undefined;
13
13
  }): React.JSX.Element;
14
14
  export namespace FixedContentTray {
15
15
  namespace propTypes {
@@ -25,22 +25,6 @@ export namespace FixedContentTray {
25
25
  let shouldJoinBodyAndFooter: PropTypes.Requireable<boolean>;
26
26
  let shouldCloseOnDocumentClick: PropTypes.Requireable<boolean>;
27
27
  }
28
- namespace defaultProps {
29
- let title_1: null;
30
- export { title_1 as title };
31
- let isOpen_1: boolean;
32
- export { isOpen_1 as isOpen };
33
- export function onDismiss_1(): void;
34
- export { onDismiss_1 as onDismiss };
35
- export function onUnmount_1(): void;
36
- export { onUnmount_1 as onUnmount };
37
- let bodyAs_1: string;
38
- export { bodyAs_1 as bodyAs };
39
- let shouldJoinBodyAndFooter_1: boolean;
40
- export { shouldJoinBodyAndFooter_1 as shouldJoinBodyAndFooter };
41
- let shouldCloseOnDocumentClick_1: boolean;
42
- export { shouldCloseOnDocumentClick_1 as shouldCloseOnDocumentClick };
43
- }
44
28
  }
45
29
  import React from 'react';
46
30
  import PropTypes from 'prop-types';