@instructure/canvas-rce 5.13.1 → 5.13.5

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 (134) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +1 -3
  3. package/es/bridge/Bridge.js +0 -4
  4. package/es/defaultTinymceConfig.js +1 -1
  5. package/es/getTranslations.js +5 -1
  6. package/es/index.js +2 -0
  7. package/es/rce/RCE.js +3 -1
  8. package/es/rce/RCEVariants.js +121 -0
  9. package/es/rce/RCEWrapper.js +96 -47
  10. package/es/rce/RCEWrapperProps.js +5 -2
  11. package/es/rce/StatusBar.js +67 -17
  12. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Footer.js +1 -0
  13. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Preview.js +1 -1
  14. package/es/rce/plugins/instructure_icon_maker/components/IconMakerTray.js +6 -1
  15. package/es/rce/plugins/instructure_rce_external_tools/RceToolWrapper.js +9 -9
  16. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.js +25 -3
  17. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.js +1 -1
  18. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogTray.js +1 -1
  19. package/es/rce/plugins/instructure_rce_external_tools/plugin.js +4 -1
  20. package/es/rce/plugins/shared/CanvasContentTray.js +6 -158
  21. package/es/rce/plugins/shared/Filter.js +0 -17
  22. package/es/rce/plugins/shared/FixedContentTray.js +7 -4
  23. package/es/rce/plugins/shared/ImageOptionsForm.js +3 -2
  24. package/es/rce/plugins/shared/Upload/CanvasContentPanel.js +160 -0
  25. package/es/rce/plugins/shared/Upload/ComputerPanel.js +1 -0
  26. package/es/rce/plugins/shared/Upload/PanelFilter.js +144 -0
  27. package/es/rce/plugins/shared/Upload/UploadFile.js +10 -2
  28. package/es/rce/plugins/shared/Upload/UploadFileModal.js +47 -11
  29. package/es/rce/plugins/shared/Upload/UsageRightsSelectBox.js +20 -20
  30. package/es/rce/plugins/shared/Upload/index.js +19 -0
  31. package/es/rce/plugins/shared/ai_tools/AIResponseModal.js +70 -0
  32. package/es/rce/plugins/shared/ai_tools/AIToolsTray.js +510 -0
  33. package/es/rce/plugins/shared/ai_tools/aiicons.js +59 -0
  34. package/es/rce/plugins/shared/ai_tools/index.js +20 -0
  35. package/es/rce/plugins/shared/canvasContentUtils.js +190 -0
  36. package/es/rce/plugins/shared/do-fetch-api-effect/defaultFetchOptions.js +31 -0
  37. package/es/rce/plugins/shared/do-fetch-api-effect/doFetchApi.js +85 -0
  38. package/es/rce/plugins/shared/do-fetch-api-effect/get-cookie.js +29 -0
  39. package/es/rce/plugins/shared/do-fetch-api-effect/index.js +22 -0
  40. package/es/rce/plugins/shared/do-fetch-api-effect/parse-link-header.js +116 -0
  41. package/es/rce/plugins/shared/do-fetch-api-effect/query-string-encoding.js +51 -0
  42. package/es/rce/plugins/shared/linkUtils.js +1 -1
  43. package/es/rce/plugins/shared/useFilterSettings.js +35 -0
  44. package/es/sidebar/actions/upload.js +5 -2
  45. package/es/translations/locales/ar.js +64 -1
  46. package/es/translations/locales/ca.js +65 -2
  47. package/es/translations/locales/cy.js +64 -1
  48. package/es/translations/locales/da-x-k12.js +65 -2
  49. package/es/translations/locales/da.js +65 -2
  50. package/es/translations/locales/de.js +65 -2
  51. package/es/translations/locales/el.js +9 -0
  52. package/es/translations/locales/en-AU-x-unimelb.js +65 -2
  53. package/es/translations/locales/en-GB-x-ukhe.js +65 -2
  54. package/es/translations/locales/en.js +65 -2
  55. package/es/translations/locales/en_AU.js +65 -2
  56. package/es/translations/locales/en_CA.js +65 -2
  57. package/es/translations/locales/en_CY.js +65 -2
  58. package/es/translations/locales/en_GB.js +65 -2
  59. package/es/translations/locales/es.js +64 -1
  60. package/es/translations/locales/es_ES.js +64 -1
  61. package/es/translations/locales/fa_IR.js +9 -0
  62. package/es/translations/locales/fi.js +64 -1
  63. package/es/translations/locales/fr.js +65 -2
  64. package/es/translations/locales/fr_CA.js +65 -2
  65. package/es/translations/locales/ga.js +116 -2
  66. package/es/translations/locales/he.js +9 -0
  67. package/es/translations/locales/hi.js +2543 -0
  68. package/es/translations/locales/ht.js +65 -2
  69. package/es/translations/locales/hu.js +440 -2
  70. package/es/translations/locales/hy.js +9 -0
  71. package/es/translations/locales/id.js +65 -2
  72. package/es/translations/locales/is.js +65 -2
  73. package/es/translations/locales/it.js +65 -2
  74. package/es/translations/locales/ja.js +65 -2
  75. package/es/translations/locales/ko.js +3 -0
  76. package/es/translations/locales/mi.js +65 -2
  77. package/es/translations/locales/ms.js +64 -1
  78. package/es/translations/locales/nb-x-k12.js +65 -2
  79. package/es/translations/locales/nb.js +65 -2
  80. package/es/translations/locales/nl.js +65 -2
  81. package/es/translations/locales/nn.js +156 -3
  82. package/es/translations/locales/pl.js +64 -1
  83. package/es/translations/locales/pt.js +64 -1
  84. package/es/translations/locales/pt_BR.js +65 -2
  85. package/es/translations/locales/ru.js +64 -1
  86. package/es/translations/locales/sl.js +65 -2
  87. package/es/translations/locales/sv-x-k12.js +65 -2
  88. package/es/translations/locales/sv.js +65 -2
  89. package/es/translations/locales/th.js +64 -1
  90. package/es/translations/locales/tr.js +9 -0
  91. package/es/translations/locales/uk_UA.js +9 -0
  92. package/es/translations/locales/vi.js +64 -1
  93. package/es/translations/locales/zh-Hans.js +64 -1
  94. package/es/translations/locales/zh-Hant.js +65 -2
  95. package/es/translations/locales/zh.js +64 -1
  96. package/es/translations/locales/zh_HK.js +65 -2
  97. package/es/translations/tinymce/ar_SA.js +4 -0
  98. package/es/translations/tinymce/bg_BG.js +4 -0
  99. package/es/translations/tinymce/ca.js +4 -0
  100. package/es/translations/tinymce/cs.js +4 -0
  101. package/es/translations/tinymce/cy.js +4 -0
  102. package/es/translations/tinymce/da.js +4 -0
  103. package/es/translations/tinymce/de.js +4 -0
  104. package/es/translations/tinymce/el.js +4 -0
  105. package/es/translations/tinymce/es.js +4 -0
  106. package/es/translations/tinymce/fa_IR.js +4 -0
  107. package/es/translations/tinymce/fr_FR.js +4 -0
  108. package/es/translations/tinymce/ga.js +4 -0
  109. package/es/translations/tinymce/he_IL.js +4 -0
  110. package/es/translations/tinymce/hu_HU.js +4 -0
  111. package/es/translations/tinymce/hy.js +4 -0
  112. package/es/translations/tinymce/id.js +4 -0
  113. package/es/translations/tinymce/it.js +4 -0
  114. package/es/translations/tinymce/ja.js +4 -0
  115. package/es/translations/tinymce/ko_KR.js +4 -0
  116. package/es/translations/tinymce/nb_NO.js +4 -0
  117. package/es/translations/tinymce/nl.js +4 -0
  118. package/es/translations/tinymce/pl.js +4 -0
  119. package/es/translations/tinymce/pt_BR.js +4 -0
  120. package/es/translations/tinymce/pt_PT.js +4 -0
  121. package/es/translations/tinymce/ro.js +4 -0
  122. package/es/translations/tinymce/ru.js +4 -0
  123. package/es/translations/tinymce/ru_RU.js +5 -1
  124. package/es/translations/tinymce/sl.js +4 -0
  125. package/es/translations/tinymce/sr.js +4 -0
  126. package/es/translations/tinymce/sv_SE.js +4 -0
  127. package/es/translations/tinymce/th.js +5 -1
  128. package/es/translations/tinymce/tr_TR.js +4 -0
  129. package/es/translations/tinymce/uk_UA.js +4 -0
  130. package/es/translations/tinymce/vi_VN.js +4 -0
  131. package/es/translations/tinymce/zh_CN.js +4 -0
  132. package/es/translations/tinymce/zh_TW.js +4 -0
  133. package/jest/jest-setup.js +1 -0
  134. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## 5.13.4 - 2024-08-12
9
+
10
+ ### Fixed
11
+
12
+ - RCE "Lato Extended" now properly uses the "Lato Extended" font
13
+
14
+ ## 5.13.4 - 2024-08-12
15
+
16
+ ### Changed
17
+
18
+ - Icon Maker tray now stays open until the user closes it with the close button
19
+
20
+ ## 5.13.3 - 2024-07-22
21
+
22
+ ### Fixed
23
+
24
+ - Icon Maker tray now stays open while an image upload modal is present
25
+
26
+ ## 5.13.2 - 2024-06-26
27
+
28
+ ### Changed
29
+
30
+ - Removed polyfill.io reference from README
31
+
8
32
  ## 5.13.1 - 2024-06-03
9
33
 
10
34
  ### Changed
package/README.md CHANGED
@@ -65,11 +65,9 @@ polyfills in your own app, you can put this in your html above the script that i
65
65
  `@instructure/canvas-rce`:
66
66
 
67
67
  ```
68
- <script src="https://cdn.polyfill.io/v2/polyfill.min.js?rum=0"></script>
68
+ <script src="https://cdnjs.cloudflare.com/polyfill/v2/polyfill.min.js"></script>
69
69
  ```
70
70
 
71
- (See https://polyfill.io/v2/docs/ for more info)
72
-
73
71
  ## Development
74
72
 
75
73
  See [DEVELOPMENT.md](https://github.com/instructure/canvas-lms/blob/master/packages/canvas-rce/DEVELOPMENT.md)
@@ -252,8 +252,6 @@ export default class Bridge {
252
252
  const result = this.focusedEditor.insertImage(image);
253
253
  (_this$controller4 = this.controller(this.focusedEditor.id)) === null || _this$controller4 === void 0 ? void 0 : _this$controller4.hideTray();
254
254
  return result;
255
- } else {
256
- console.warn('clicked sidebar image without a focused editor');
257
255
  }
258
256
  }
259
257
 
@@ -264,8 +262,6 @@ export default class Bridge {
264
262
  if (!this.existingContentToLink()) {
265
263
  this.focusedEditor.insertImagePlaceholder(fileMetaProps);
266
264
  }
267
- } else {
268
- console.warn('clicked sidebar image without a focused editor');
269
265
  }
270
266
  }
271
267
 
@@ -51,7 +51,7 @@ const defaultTinymceConfig = {
51
51
  // this will always be provided by the RCE
52
52
  convert_urls: false,
53
53
  // fonts specified here need to either be web-safe or self-hosted and loaded in app/stylesheets/bundles/fonts.scss
54
- font_formats: "Lato=lato,Helvetica Neue,Helvetica,Arial,sans-serif; Balsamiq Sans=Balsamiq Sans,lato,Helvetica Neue,Helvetica,Arial,sans-serif; Architect's Daughter=Architects Daughter,lato,Helvetica Neue,Helvetica,Arial,sans-serif; Arial=arial,helvetica,sans-serif; Arial Black=arial black,avant garde; Courier New=courier new,courier; Georgia=georgia,palatino; Tahoma=tahoma,arial,helvetica,sans-serif; Times New Roman=times new roman,times; Trebuchet MS=trebuchet ms,geneva; Verdana=verdana,geneva",
54
+ font_formats: "Lato Extended=Lato Extended,Helvetica Neue,Helvetica,Arial,sans-serif; Balsamiq Sans=Balsamiq Sans,Lato Extended,Helvetica Neue,Helvetica,Arial,sans-serif; Architect's Daughter=Architects Daughter,Lato Extended,Helvetica Neue,Helvetica,Arial,sans-serif; Arial=arial,helvetica,sans-serif; Arial Black=arial black,avant garde; Courier New=courier new,courier; Georgia=georgia,palatino; Tahoma=tahoma,arial,helvetica,sans-serif; Times New Roman=times new roman,times; Trebuchet MS=trebuchet ms,geneva; Verdana=verdana,geneva",
55
55
  language_load: false,
56
56
  language_url: 'none',
57
57
  toolbar_mode: 'floating',
@@ -148,6 +148,10 @@ export default function getTranslations(locale) {
148
148
  p = import('./translations/locales/he');
149
149
  break;
150
150
 
151
+ case 'hi':
152
+ p = import('./translations/locales/hi');
153
+ break;
154
+
151
155
  case 'ht':
152
156
  p = import('./translations/locales/ht');
153
157
  break;
@@ -340,5 +344,5 @@ export default function getTranslations(locale) {
340
344
  return transReadyPromise;
341
345
  }
342
346
  export function getLocaleList() {
343
- return ['ab', 'ar', 'ca', 'cs', 'cs-CZ', 'cy', 'da', 'da-x-k12', 'da-DK', 'de', 'el', 'en', 'en-AU-x-unimelb', 'en-GB-x-ukhe', 'en-AU', 'en-CA', 'en-CY', 'en-GB', 'en-NZ', 'en-SE', 'en-US', 'es', 'es-ES', 'es-GT', 'fa-IR', 'fi', 'fr', 'fr-CA', 'ga', 'he', 'ht', 'hu', 'hu-HU', 'hy', 'id', 'id-ID', 'is', 'it', 'ja', 'ko', 'ko-KR', 'lt', 'lt-LT', 'mi', 'mn-MN', 'ms', 'nb', 'nb-x-k12', 'nl', 'nl-NL', 'nn', 'pl', 'pt', 'pt-BR', 'ro', 'ru', 'se', 'sl', 'sv', 'sv-x-k12', 'sv-SE', 'tg', 'th', 'th-TH', 'tl-PH', 'tr', 'uk-UA', 'vi', 'vi-VN', 'zh', 'zh-Hans', 'zh-Hant', 'zh-HK', 'zh-TW', 'zh-TW.Big5'];
347
+ return ['ab', 'ar', 'ca', 'cs', 'cs-CZ', 'cy', 'da', 'da-x-k12', 'da-DK', 'de', 'el', 'en', 'en-AU-x-unimelb', 'en-GB-x-ukhe', 'en-AU', 'en-CA', 'en-CY', 'en-GB', 'en-NZ', 'en-SE', 'en-US', 'es', 'es-ES', 'es-GT', 'fa-IR', 'fi', 'fr', 'fr-CA', 'ga', 'he', 'hi', 'ht', 'hu', 'hu-HU', 'hy', 'id', 'id-ID', 'is', 'it', 'ja', 'ko', 'ko-KR', 'lt', 'lt-LT', 'mi', 'mn-MN', 'ms', 'nb', 'nb-x-k12', 'nl', 'nl-NL', 'nn', 'pl', 'pt', 'pt-BR', 'ro', 'ru', 'se', 'sl', 'sv', 'sv-x-k12', 'sv-SE', 'tg', 'th', 'th-TH', 'tl-PH', 'tr', 'uk-UA', 'vi', 'vi-VN', 'zh', 'zh-Hans', 'zh-Hant', 'zh-HK', 'zh-TW', 'zh-TW.Big5'];
344
348
  }
package/es/index.js CHANGED
@@ -26,6 +26,8 @@ export * from './enhance-user-content/index';
26
26
  export const defaultConfiguration = defaultTinymceConfig;
27
27
  export { instuiPopupMountNode } from './util/fullscreenHelpers';
28
28
  export { Mathml };
29
+ export { RCEVariantValues } from './rce/RCEVariants';
30
+ export { UploadFilePanelIds, handleSubmit, UploadFile } from './rce/plugins/shared/Upload';
29
31
  export function renderIntoDiv(editorEl, props, cb) {
30
32
  const language = normalizeLocale(props.language);
31
33
  setLocale(language);
package/es/rce/RCE.js CHANGED
@@ -58,6 +58,7 @@ const RCE = /*#__PURE__*/forwardRef(function RCE(props, rceRef) {
58
58
  rcsProps,
59
59
  use_rce_icon_maker,
60
60
  features,
61
+ variant,
61
62
  onFocus,
62
63
  onBlur,
63
64
  onInit,
@@ -104,7 +105,8 @@ const RCE = /*#__PURE__*/forwardRef(function RCE(props, rceRef) {
104
105
  selector: (editorOptions === null || editorOptions === void 0 ? void 0 : editorOptions.selector) || `#${textareaId}`,
105
106
  height,
106
107
  language: editorLanguage(props.language)
107
- }
108
+ },
109
+ variant
108
110
  };
109
111
  wrapInitCb(mirroredAttrs, iProps.editorOptions);
110
112
  return iProps;
@@ -0,0 +1,121 @@
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
+ import formatMessage from '../format-message';
19
+ export const RCEVariantValues = ['full', 'lite', 'text-only'];
20
+ export function getMenubarForVariant(variant) {
21
+ if (variant === 'full') {
22
+ return 'edit view insert format tools table';
23
+ }
24
+
25
+ return '';
26
+ }
27
+ export function getMenuForVariant(variant) {
28
+ if (variant === 'full') {
29
+ return {
30
+ edit: {
31
+ title: formatMessage('Edit'),
32
+ items: `undo redo | cut copy paste | selectall`
33
+ },
34
+ format: {
35
+ title: formatMessage('Format'),
36
+ items: 'bold italic underline strikethrough superscript subscript codeformat | formats blockformats fontformats fontsizes align directionality | forecolor backcolor | removeformat'
37
+ },
38
+ insert: {
39
+ title: formatMessage('Insert'),
40
+ items: 'instructure_links instructure_image instructure_media instructure_document instructure_icon_maker | instructure_equation inserttable instructure_media_embed | hr'
41
+ },
42
+ tools: {
43
+ title: formatMessage('Tools'),
44
+ items: 'instructure_wordcount lti_tools_menuitem instructure_search_and_replace'
45
+ },
46
+ view: {
47
+ title: formatMessage('View'),
48
+ items: 'instructure_fullscreen instructure_exit_fullscreen instructure_html_view'
49
+ }
50
+ };
51
+ }
52
+
53
+ return {};
54
+ }
55
+ export function getToolbarForVariant(variant) {
56
+ let ltiToolFavorites = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
57
+
58
+ if (variant === 'lite') {
59
+ return [{
60
+ name: formatMessage('Styles'),
61
+ items: ['formatselect']
62
+ }, {
63
+ name: formatMessage('Formatting'),
64
+ items: ['bold', 'italic', 'underline', 'forecolor']
65
+ }, {
66
+ name: formatMessage('Content'),
67
+ items: ['instructure_links', 'instructure_image']
68
+ }, {
69
+ name: formatMessage('Lists'),
70
+ items: ['bullist']
71
+ }, {
72
+ name: formatMessage('Miscellaneous'),
73
+ items: ['instructure_equation']
74
+ }];
75
+ }
76
+
77
+ if (variant === 'text-only') {
78
+ return [{
79
+ name: formatMessage('Formatting'),
80
+ items: ['bold', 'italic', 'underline']
81
+ }, {
82
+ name: formatMessage('Content'),
83
+ items: ['instructure_links']
84
+ }];
85
+ }
86
+
87
+ return [{
88
+ name: formatMessage('Styles'),
89
+ items: ['fontsizeselect', 'formatselect']
90
+ }, {
91
+ name: formatMessage('Formatting'),
92
+ items: ['bold', 'italic', 'underline', 'forecolor', 'backcolor', 'inst_subscript', 'inst_superscript']
93
+ }, {
94
+ name: formatMessage('Content'),
95
+ items: ['instructure_links', 'instructure_image', 'instructure_record', 'instructure_documents', 'instructure_icon_maker']
96
+ }, {
97
+ name: formatMessage('External Tools'),
98
+ items: [...ltiToolFavorites, 'lti_tool_dropdown', 'lti_mru_button']
99
+ }, {
100
+ name: formatMessage('Alignment and Lists'),
101
+ items: ['align', 'bullist', 'inst_indent', 'inst_outdent']
102
+ }, {
103
+ name: formatMessage('Miscellaneous'),
104
+ items: ['removeformat', 'table', 'instructure_equation', 'instructure_media_embed']
105
+ }];
106
+ }
107
+ export function getStatusBarFeaturesForVariant(variant) {
108
+ let ai_text_tools = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
109
+
110
+ if (variant === 'lite' || variant === 'text-only') {
111
+ return ['keyboard_shortcuts', 'a11y_checker', 'word_count'];
112
+ }
113
+
114
+ const full_features = ['keyboard_shortcuts', 'a11y_checker', 'word_count', 'html_view', 'fullscreen', 'resize_handle'];
115
+
116
+ if (ai_text_tools) {
117
+ full_features.push('ai_tools');
118
+ }
119
+
120
+ return full_features;
121
+ }
@@ -60,6 +60,7 @@ import { IconMoreSolid } from '@instructure/ui-icons/es/svg';
60
60
  import EncryptedStorage from '../util/encrypted-storage';
61
61
  import buildStyle from './style';
62
62
  import { externalToolsForToolbar } from './plugins/instructure_rce_external_tools/RceToolWrapper';
63
+ import { getMenubarForVariant, getMenuForVariant, getToolbarForVariant, getStatusBarFeaturesForVariant } from './RCEVariants';
63
64
  const RestoreAutoSaveModal = /*#__PURE__*/React.lazy(() => import('./RestoreAutoSaveModal'));
64
65
  const RceHtmlEditor = /*#__PURE__*/React.lazy(() => import('./RceHtmlEditor'));
65
66
  const ASYNC_FOCUS_TIMEOUT = 250;
@@ -693,6 +694,72 @@ class RCEWrapper extends React.Component {
693
694
  }
694
695
  };
695
696
 
697
+ this.handleAIClick = () => {
698
+ import('./plugins/shared/ai_tools').then(module => {
699
+ this.AIToolsTray = module.AIToolsTray;
700
+ this.setState({
701
+ AIToolsOpen: true,
702
+ AITToolsFocusReturn: document.activeElement
703
+ });
704
+ }).catch(ex => {
705
+ // eslint-disable-next-line no-console
706
+ console.error('Failed loading the AIToolsTray', ex);
707
+ });
708
+ };
709
+
710
+ this.closeAITools = () => {
711
+ this.setState({
712
+ AIToolsOpen: false
713
+ });
714
+ };
715
+
716
+ this.AIToolsExited = () => {
717
+ if (this.state.AITToolsFocusReturn === this.iframe) {
718
+ // launched using a kb shortcut
719
+ // the iframe has focus so we need to forward it on to tinymce editor
720
+ this.editor.focus(false);
721
+ } else if (this.state.AITToolsFocusReturn === document.getElementById(`show-on-focus-btn-${this.id}`)) {
722
+ var _this$_showOnFocusBut2;
723
+
724
+ // launched from showOnFocus button
725
+ // edge case where focusing KBShortcutFocusReturn doesn't work
726
+ (_this$_showOnFocusBut2 = this._showOnFocusButton) === null || _this$_showOnFocusBut2 === void 0 ? void 0 : _this$_showOnFocusBut2.focus();
727
+ } else {
728
+ var _this$state$AITToolsF;
729
+
730
+ // launched from kb shortcut button on status bar
731
+ (_this$state$AITToolsF = this.state.AITToolsFocusReturn) === null || _this$state$AITToolsF === void 0 ? void 0 : _this$state$AITToolsF.focus();
732
+ }
733
+ };
734
+
735
+ this.handleInsertAIContent = content => {
736
+ const editor = this.mceInstance();
737
+ contentInsertion.insertContent(editor, content);
738
+ };
739
+
740
+ this.handleReplaceAIContent = content => {
741
+ const ed = this.mceInstance();
742
+ const selection = ed.selection;
743
+
744
+ if (selection.getContent().length > 0) {
745
+ selection.setContent(content);
746
+ } else {
747
+ ed.selection.select(ed.getBody(), true);
748
+ selection.setContent(content);
749
+ }
750
+ };
751
+
752
+ this.getCurrentContentForAI = () => {
753
+ const selected = this.mceInstance().selection.getContent();
754
+ return selected ? {
755
+ type: 'selection',
756
+ content: selected
757
+ } : {
758
+ type: 'full',
759
+ content: this.mceInstance().getContent()
760
+ };
761
+ };
762
+
696
763
  this.setFocusAbilityForHeader = focusable => {
697
764
  // Sets aria-hidden to prevent screen readers focus in RCE menus and toolbar
698
765
  const header = this._elementRef.current.querySelector('.tox-editor-header');
@@ -788,7 +855,8 @@ class RCEWrapper extends React.Component {
788
855
  prevHeight: _ht
789
856
  },
790
857
  a11yErrorsCount: 0,
791
- shouldShowEditor: typeof IntersectionObserver === 'undefined' || maxInitRenderedRCEs <= 0 || currentRCECount < maxInitRenderedRCEs
858
+ shouldShowEditor: typeof IntersectionObserver === 'undefined' || maxInitRenderedRCEs <= 0 || currentRCECount < maxInitRenderedRCEs,
859
+ AIToolsOpen: false
792
860
  };
793
861
  this._statusBarId = `${this.state.id}_statusbar`;
794
862
  this.pendingEventHandlers = [];
@@ -796,12 +864,15 @@ class RCEWrapper extends React.Component {
796
864
  this.pluginsToExclude = parsePluginsToExclude(((_props$editorOptions2 = props.editorOptions) === null || _props$editorOptions2 === void 0 ? void 0 : _props$editorOptions2.plugins) || []);
797
865
  this.resourceType = props.resourceType;
798
866
  this.resourceId = props.resourceId;
867
+ this.variant = window.RCE_VARIANT || props.variant; // to facilitate testing
868
+
799
869
  this.tinymceInitOptions = this.wrapOptions(props.editorOptions);
800
870
  alertHandler.alertFunc = this.addAlert;
801
871
  this.handleContentTrayClosing = this.handleContentTrayClosing.bind(this);
802
872
  this.resizeObserver = new ResizeObserver(_entries => {
803
873
  this._handleFullscreenResize();
804
874
  });
875
+ this.AIToolsTray = undefined;
805
876
  } // when the RCE is put into fullscreen we need to move the div
806
877
  // tinymce mounts popup menus into from the body to the rce-wrapper
807
878
  // or the menus wind up behind the RCE. I can't find a way to
@@ -979,7 +1050,7 @@ class RCEWrapper extends React.Component {
979
1050
  }
980
1051
 
981
1052
  replaceCode(code) {
982
- if (code !== "" && window.confirm(formatMessage('Content in the editor will be changed. Press Cancel to keep the original content.'))) {
1053
+ if (code !== '' && window.confirm(formatMessage('Content in the editor will be changed. Press Cancel to keep the original content.'))) {
983
1054
  this.mceInstance().setContent(code);
984
1055
  }
985
1056
  }
@@ -1551,54 +1622,15 @@ class RCEWrapper extends React.Component {
1551
1622
  // things like table resizing and stuff.
1552
1623
  content_css: options.content_css || [],
1553
1624
  content_style: contentCSS,
1554
- menubar: mergeMenuItems('edit view insert format tools table', possibleNewMenubarItems),
1625
+ menubar: mergeMenuItems(getMenubarForVariant(this.variant), possibleNewMenubarItems),
1555
1626
  // default menu options listed at https://www.tiny.cloud/docs/configure/editor-appearance/#menu
1556
1627
  // tinymce's default edit and table menus are fine
1557
1628
  // note: the tinymce paste command is used here instead of instructure_paste
1558
1629
  // since we currently can't effectively paste using the clipboard api anyway.
1559
1630
  // we include all the canvas specific items in the menu and toolbar
1560
1631
  // and rely on tinymce only showing them if the plugin is provided.
1561
- menu: mergeMenu({
1562
- edit: {
1563
- title: formatMessage('Edit'),
1564
- items: `undo redo | cut copy paste | selectall`
1565
- },
1566
- format: {
1567
- title: formatMessage('Format'),
1568
- items: 'bold italic underline strikethrough superscript subscript codeformat | formats blockformats fontformats fontsizes align directionality | forecolor backcolor | removeformat'
1569
- },
1570
- insert: {
1571
- title: formatMessage('Insert'),
1572
- items: 'instructure_links instructure_image instructure_media instructure_document instructure_icon_maker | instructure_equation inserttable instructure_media_embed | hr'
1573
- },
1574
- tools: {
1575
- title: formatMessage('Tools'),
1576
- items: 'instructure_wordcount lti_tools_menuitem instructure_search_and_replace'
1577
- },
1578
- view: {
1579
- title: formatMessage('View'),
1580
- items: 'instructure_fullscreen instructure_exit_fullscreen instructure_html_view'
1581
- }
1582
- }, options.menu),
1583
- toolbar: mergeToolbar([{
1584
- name: formatMessage('Styles'),
1585
- items: ['fontsizeselect', 'formatselect']
1586
- }, {
1587
- name: formatMessage('Formatting'),
1588
- items: ['bold', 'italic', 'underline', 'forecolor', 'backcolor', 'inst_subscript', 'inst_superscript']
1589
- }, {
1590
- name: formatMessage('Content'),
1591
- items: ['instructure_links', 'instructure_image', 'instructure_record', 'instructure_documents', 'instructure_icon_maker']
1592
- }, {
1593
- name: formatMessage('External Tools'),
1594
- items: [...this.ltiToolFavorites, 'lti_tool_dropdown', 'lti_mru_button']
1595
- }, {
1596
- name: formatMessage('Alignment and Lists'),
1597
- items: ['align', 'bullist', 'inst_indent', 'inst_outdent']
1598
- }, {
1599
- name: formatMessage('Miscellaneous'),
1600
- items: ['removeformat', 'table', 'instructure_equation', 'instructure_media_embed']
1601
- }], options.toolbar),
1632
+ menu: mergeMenu(getMenuForVariant(this.variant), options.menu),
1633
+ toolbar: mergeToolbar(getToolbarForVariant(this.variant, this.ltiToolFavorites), options.toolbar),
1602
1634
  contextmenu: '',
1603
1635
  // show the browser's native context menu
1604
1636
  toolbar_mode: 'sliding',
@@ -1797,6 +1829,7 @@ class RCEWrapper extends React.Component {
1797
1829
  });
1798
1830
  }
1799
1831
 
1832
+ const statusBarFeatures = getStatusBarFeaturesForVariant(this.variant, this.props.ai_text_tools);
1800
1833
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("style", null, this.style.css), /*#__PURE__*/React.createElement(StoreProvider, {
1801
1834
  jwt: (_this$props$trayProps5 = this.props.trayProps) === null || _this$props$trayProps5 === void 0 ? void 0 : _this$props$trayProps5.jwt,
1802
1835
  refreshToken: (_this$props$trayProps6 = this.props.trayProps) === null || _this$props$trayProps6 === void 0 ? void 0 : _this$props$trayProps6.refreshToken,
@@ -1811,6 +1844,9 @@ class RCEWrapper extends React.Component {
1811
1844
  key: this.id,
1812
1845
  className: `${this.style.classNames.root} rce-wrapper`,
1813
1846
  ref: this._elementRef,
1847
+ style: this.variant === 'full' ? {
1848
+ marginBottom: '.5rem'
1849
+ } : undefined,
1814
1850
  onFocus: this.handleFocusRCE,
1815
1851
  onBlur: this.handleBlurRCE
1816
1852
  }, this.state.shouldShowOnFocusButton && /*#__PURE__*/React.createElement(ShowOnFocusButton, {
@@ -1842,7 +1878,7 @@ class RCEWrapper extends React.Component {
1842
1878
  onNodeChange: this.onNodeChange,
1843
1879
  onEditorChange: this.onEditorChange,
1844
1880
  liveRegion: this.props.liveRegion
1845
- })), /*#__PURE__*/React.createElement(StatusBar, {
1881
+ })), statusBarFeatures.length > 0 && /*#__PURE__*/React.createElement(StatusBar, {
1846
1882
  id: this._statusBarId,
1847
1883
  rceIsFullscreen: this._isFullscreen(),
1848
1884
  readOnly: this.props.readOnly,
@@ -1860,7 +1896,9 @@ class RCEWrapper extends React.Component {
1860
1896
  onWordcountModalOpen: () => launchWordcountModal(this.mceInstance(), document, {
1861
1897
  skipEditorFocus: true
1862
1898
  }),
1863
- disabledPlugins: this.pluginsToExclude
1899
+ disabledPlugins: this.pluginsToExclude,
1900
+ features: statusBarFeatures,
1901
+ onAI: this.handleAIClick
1864
1902
  }), ((_this$props$trayProps10 = this.props.trayProps) === null || _this$props$trayProps10 === void 0 ? void 0 : _this$props$trayProps10.containingContext) && /*#__PURE__*/React.createElement(CanvasContentTray, Object.assign({
1865
1903
  mountNode: instuiPopupMountNode,
1866
1904
  key: this.id,
@@ -1875,6 +1913,16 @@ class RCEWrapper extends React.Component {
1875
1913
  onExited: this.KBShortcutModalExited,
1876
1914
  onDismiss: this.closeKBShortcutModal,
1877
1915
  open: this.state.KBShortcutModalOpen
1916
+ }), this.props.ai_text_tools && this.AIToolsTray && /*#__PURE__*/React.createElement(this.AIToolsTray, {
1917
+ open: this.state.AIToolsOpen,
1918
+ container: document.querySelector('[role="main"]'),
1919
+ mountNode: instuiPopupMountNode,
1920
+ contextId: trayProps.contextId,
1921
+ contextType: trayProps.contextId,
1922
+ currentContent: this.getCurrentContentForAI(),
1923
+ onClose: this.closeAITools,
1924
+ onInsertContent: this.handleInsertAIContent,
1925
+ onReplaceContent: this.handleReplaceAIContent
1878
1926
  }), this.state.confirmAutoSave ? /*#__PURE__*/React.createElement(Suspense, {
1879
1927
  fallback: /*#__PURE__*/React.createElement(Spinner, {
1880
1928
  renderTitle: renderLoading,
@@ -1909,7 +1957,8 @@ RCEWrapper.defaultProps = {
1909
1957
  maxInitRenderedRCEs: -1,
1910
1958
  features: {},
1911
1959
  timezone: (_Intl = Intl) === null || _Intl === void 0 ? void 0 : (_Intl$DateTimeFormat = _Intl.DateTimeFormat()) === null || _Intl$DateTimeFormat === void 0 ? void 0 : (_Intl$DateTimeFormat$ = _Intl$DateTimeFormat.resolvedOptions()) === null || _Intl$DateTimeFormat$ === void 0 ? void 0 : _Intl$DateTimeFormat$.timeZone,
1912
- canvasOrigin: ''
1960
+ canvasOrigin: '',
1961
+ variant: 'full'
1913
1962
  };
1914
1963
  RCEWrapper.skinCssInjected = false;
1915
1964
 
@@ -17,6 +17,7 @@
17
17
  */
18
18
  import PropTypes from 'prop-types';
19
19
  import { trayPropTypes } from './plugins/shared/CanvasContentTray';
20
+ import { RCEVariantValues } from './RCEVariants';
20
21
  import { PRETTY_HTML_EDITOR_VIEW, RAW_HTML_EDITOR_VIEW, WYSIWYG_VIEW } from './StatusBar'; // This file contains the prop types for the RCEWrapper component, so that types can be shared without having
21
22
  // to refactor RCEWrapper.js into typescript.
22
23
 
@@ -49,7 +50,7 @@ export const ltiToolsPropType = PropTypes.arrayOf(PropTypes.shape({
49
50
  id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
50
51
  // is this a favorite tool?
51
52
  favorite: PropTypes.bool,
52
- always_on: PropTypes.bool,
53
+ on_by_default: PropTypes.bool,
53
54
  name: PropTypes.string,
54
55
  description: PropTypes.string,
55
56
  icon_url: PropTypes.string,
@@ -127,5 +128,7 @@ export const rceWrapperPropTypes = {
127
128
  flashAlertTimeout: PropTypes.number,
128
129
  timezone: PropTypes.string,
129
130
  userCacheKey: PropTypes.string,
130
- externalToolsConfig: externalToolsConfigPropType
131
+ externalToolsConfig: externalToolsConfigPropType,
132
+ ai_text_tools: PropTypes.bool,
133
+ variant: PropTypes.oneOf(RCEVariantValues)
131
134
  };