@instructure/canvas-rce 5.13.2 → 5.13.6

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 (130) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/es/bridge/Bridge.js +0 -4
  3. package/es/defaultTinymceConfig.js +3 -3
  4. package/es/enhance-user-content/enhance_user_content.js +31 -3
  5. package/es/index.js +2 -0
  6. package/es/rce/RCE.js +3 -1
  7. package/es/rce/RCEVariants.js +121 -0
  8. package/es/rce/RCEWrapper.js +96 -47
  9. package/es/rce/RCEWrapperProps.js +5 -2
  10. package/es/rce/StatusBar.js +67 -17
  11. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Footer.js +1 -0
  12. package/es/rce/plugins/instructure_icon_maker/components/IconMakerTray.js +6 -1
  13. package/es/rce/plugins/instructure_rce_external_tools/RceToolWrapper.js +9 -9
  14. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.js +25 -3
  15. package/es/rce/plugins/instructure_rce_external_tools/plugin.js +4 -1
  16. package/es/rce/plugins/shared/CanvasContentTray.js +6 -158
  17. package/es/rce/plugins/shared/ContentSelection.js +1 -1
  18. package/es/rce/plugins/shared/Filter.js +0 -17
  19. package/es/rce/plugins/shared/FixedContentTray.js +7 -4
  20. package/es/rce/plugins/shared/ImageOptionsForm.js +3 -2
  21. package/es/rce/plugins/shared/Upload/CanvasContentPanel.js +160 -0
  22. package/es/rce/plugins/shared/Upload/ComputerPanel.js +1 -0
  23. package/es/rce/plugins/shared/Upload/PanelFilter.js +144 -0
  24. package/es/rce/plugins/shared/Upload/UploadFile.js +10 -2
  25. package/es/rce/plugins/shared/Upload/UploadFileModal.js +47 -11
  26. package/es/rce/plugins/shared/Upload/UsageRightsSelectBox.js +20 -20
  27. package/es/rce/plugins/shared/Upload/index.js +19 -0
  28. package/es/rce/plugins/shared/ai_tools/AIResponseModal.js +70 -0
  29. package/es/rce/plugins/shared/ai_tools/AIToolsTray.js +510 -0
  30. package/es/rce/plugins/shared/ai_tools/aiicons.js +59 -0
  31. package/es/rce/plugins/shared/ai_tools/index.js +20 -0
  32. package/es/rce/plugins/shared/canvasContentUtils.js +190 -0
  33. package/es/rce/plugins/shared/do-fetch-api-effect/defaultFetchOptions.js +31 -0
  34. package/es/rce/plugins/shared/do-fetch-api-effect/doFetchApi.js +85 -0
  35. package/es/rce/plugins/shared/do-fetch-api-effect/get-cookie.js +29 -0
  36. package/es/rce/plugins/shared/do-fetch-api-effect/index.js +22 -0
  37. package/es/rce/plugins/shared/do-fetch-api-effect/parse-link-header.js +116 -0
  38. package/es/rce/plugins/shared/do-fetch-api-effect/query-string-encoding.js +51 -0
  39. package/es/rce/plugins/shared/useFilterSettings.js +35 -0
  40. package/es/sidebar/actions/upload.js +5 -2
  41. package/es/translations/locales/ar.js +64 -1
  42. package/es/translations/locales/ca.js +65 -2
  43. package/es/translations/locales/cy.js +64 -1
  44. package/es/translations/locales/da-x-k12.js +65 -2
  45. package/es/translations/locales/da.js +65 -2
  46. package/es/translations/locales/de.js +65 -2
  47. package/es/translations/locales/el.js +9 -0
  48. package/es/translations/locales/en-AU-x-unimelb.js +65 -2
  49. package/es/translations/locales/en-GB-x-ukhe.js +65 -2
  50. package/es/translations/locales/en.js +65 -2
  51. package/es/translations/locales/en_AU.js +65 -2
  52. package/es/translations/locales/en_CA.js +65 -2
  53. package/es/translations/locales/en_CY.js +65 -2
  54. package/es/translations/locales/en_GB.js +65 -2
  55. package/es/translations/locales/es.js +64 -1
  56. package/es/translations/locales/es_ES.js +64 -1
  57. package/es/translations/locales/fa_IR.js +9 -0
  58. package/es/translations/locales/fi.js +64 -1
  59. package/es/translations/locales/fr.js +65 -2
  60. package/es/translations/locales/fr_CA.js +65 -2
  61. package/es/translations/locales/ga.js +62 -2
  62. package/es/translations/locales/he.js +9 -0
  63. package/es/translations/locales/hi.js +119 -2
  64. package/es/translations/locales/ht.js +65 -2
  65. package/es/translations/locales/hu.js +15 -3
  66. package/es/translations/locales/hy.js +9 -0
  67. package/es/translations/locales/id.js +65 -2
  68. package/es/translations/locales/is.js +65 -2
  69. package/es/translations/locales/it.js +65 -2
  70. package/es/translations/locales/ja.js +65 -2
  71. package/es/translations/locales/ko.js +3 -0
  72. package/es/translations/locales/mi.js +65 -2
  73. package/es/translations/locales/ms.js +64 -1
  74. package/es/translations/locales/nb-x-k12.js +65 -2
  75. package/es/translations/locales/nb.js +65 -2
  76. package/es/translations/locales/nl.js +65 -2
  77. package/es/translations/locales/nn.js +15 -3
  78. package/es/translations/locales/pl.js +64 -1
  79. package/es/translations/locales/pt.js +64 -1
  80. package/es/translations/locales/pt_BR.js +65 -2
  81. package/es/translations/locales/ru.js +64 -1
  82. package/es/translations/locales/sl.js +65 -2
  83. package/es/translations/locales/sv-x-k12.js +65 -2
  84. package/es/translations/locales/sv.js +65 -2
  85. package/es/translations/locales/th.js +64 -1
  86. package/es/translations/locales/tr.js +9 -0
  87. package/es/translations/locales/uk_UA.js +9 -0
  88. package/es/translations/locales/vi.js +64 -1
  89. package/es/translations/locales/zh-Hans.js +64 -1
  90. package/es/translations/locales/zh-Hant.js +65 -2
  91. package/es/translations/locales/zh.js +64 -1
  92. package/es/translations/locales/zh_HK.js +65 -2
  93. package/es/translations/tinymce/ar_SA.js +4 -0
  94. package/es/translations/tinymce/bg_BG.js +4 -0
  95. package/es/translations/tinymce/ca.js +4 -0
  96. package/es/translations/tinymce/cs.js +4 -0
  97. package/es/translations/tinymce/cy.js +4 -0
  98. package/es/translations/tinymce/da.js +4 -0
  99. package/es/translations/tinymce/de.js +4 -0
  100. package/es/translations/tinymce/el.js +4 -0
  101. package/es/translations/tinymce/es.js +4 -0
  102. package/es/translations/tinymce/fa_IR.js +4 -0
  103. package/es/translations/tinymce/fr_FR.js +4 -0
  104. package/es/translations/tinymce/ga.js +4 -0
  105. package/es/translations/tinymce/he_IL.js +4 -0
  106. package/es/translations/tinymce/hu_HU.js +4 -0
  107. package/es/translations/tinymce/hy.js +4 -0
  108. package/es/translations/tinymce/id.js +4 -0
  109. package/es/translations/tinymce/it.js +4 -0
  110. package/es/translations/tinymce/ja.js +4 -0
  111. package/es/translations/tinymce/ko_KR.js +4 -0
  112. package/es/translations/tinymce/nb_NO.js +4 -0
  113. package/es/translations/tinymce/nl.js +4 -0
  114. package/es/translations/tinymce/pl.js +4 -0
  115. package/es/translations/tinymce/pt_BR.js +4 -0
  116. package/es/translations/tinymce/pt_PT.js +4 -0
  117. package/es/translations/tinymce/ro.js +4 -0
  118. package/es/translations/tinymce/ru.js +4 -0
  119. package/es/translations/tinymce/ru_RU.js +5 -1
  120. package/es/translations/tinymce/sl.js +4 -0
  121. package/es/translations/tinymce/sr.js +4 -0
  122. package/es/translations/tinymce/sv_SE.js +4 -0
  123. package/es/translations/tinymce/th.js +5 -1
  124. package/es/translations/tinymce/tr_TR.js +4 -0
  125. package/es/translations/tinymce/uk_UA.js +4 -0
  126. package/es/translations/tinymce/vi_VN.js +4 -0
  127. package/es/translations/tinymce/zh_CN.js +4 -0
  128. package/es/translations/tinymce/zh_TW.js +4 -0
  129. package/jest/jest-setup.js +1 -0
  130. package/package.json +1 -1
@@ -31,6 +31,7 @@ import { IconA11yLine, IconKeyboardShortcutsLine, IconMiniArrowEndLine, IconFull
31
31
  import formatMessage from '../format-message';
32
32
  import ResizeHandle from './ResizeHandle';
33
33
  import { FS_ENABLED } from '../util/fullscreenHelpers';
34
+ import { AIWandSVG } from './plugins/shared/ai_tools';
34
35
  export const WYSIWYG_VIEW = 'WYSIWYG';
35
36
  export const PRETTY_HTML_EDITOR_VIEW = 'PRETTY';
36
37
  export const RAW_HTML_EDITOR_VIEW = 'RAW'; // I don't know why eslint is reporting this, the props are all used
@@ -54,7 +55,10 @@ StatusBar.propTypes = {
54
55
  a11yBadgeColor: string,
55
56
  a11yErrorsCount: number,
56
57
  onWordcountModalOpen: func.isRequired,
57
- disabledPlugins: arrayOf(string)
58
+ disabledPlugins: arrayOf(string),
59
+ features: arrayOf(string),
60
+ // StatusBarFeature[]
61
+ onAI: func
58
62
  };
59
63
  StatusBar.defaultProps = {
60
64
  a11yBadgeColor: '#0374B5',
@@ -127,6 +131,10 @@ export default function StatusBar(props) {
127
131
  return !props.disabledPlugins.includes(plugin);
128
132
  }
129
133
 
134
+ function isFeature(feature_name) {
135
+ return props.features.includes(feature_name);
136
+ }
137
+
130
138
  function preferredHtmlEditor() {
131
139
  if (props.preferredHtmlEditor) return props.preferredHtmlEditor;
132
140
  return PRETTY_HTML_EDITOR_VIEW;
@@ -177,7 +185,7 @@ export default function StatusBar(props) {
177
185
  const a11yButtonId = 'rce-a11y-btn';
178
186
  const button = /*#__PURE__*/React.createElement(IconButton, {
179
187
  "data-btn-id": a11yButtonId,
180
- color: "primary",
188
+ color: "secondary",
181
189
  title: a11y,
182
190
  tabIndex: tabIndexForBtn(a11yButtonId),
183
191
  onClick: event => {
@@ -233,13 +241,39 @@ export default function StatusBar(props) {
233
241
 
234
242
  function renderIconButtons() {
235
243
  if (isHtmlView()) return null;
244
+ const ai_tools = isFeature('ai_tools');
245
+ const kb_shortcuts = isFeature('keyboard_shortcuts');
246
+ const a11y_checker = isFeature('a11y_checker');
247
+ if (!(ai_tools || kb_shortcuts || a11y_checker)) return null;
236
248
  const kbshortcut = formatMessage('View keyboard shortcuts');
237
249
  return /*#__PURE__*/React.createElement(View, {
238
250
  display: "inline-block",
239
251
  padding: "0 x-small"
240
- }, /*#__PURE__*/React.createElement(IconButton, {
252
+ }, ai_tools && props.onAI && /*#__PURE__*/React.createElement(IconButton, {
253
+ "data-btn-id": "rce-ai-btn",
254
+ color: "secondary",
255
+ "aria-haspopup": "dialog",
256
+ title: formatMessage('AI Tools'),
257
+ tabIndex: tabIndexForBtn('rce-ai-btn'),
258
+ onClick: event => {
259
+ event.target.focus(); // FF doesn't focus buttons on click
260
+
261
+ props.onAI();
262
+ },
263
+ onFocus: () => setFocusedBtnId('rce-ai-btn'),
264
+ screenReaderLabel: formatMessage('AI Tools'),
265
+ withBackground: false,
266
+ withBorder: false
267
+ }, /*#__PURE__*/React.createElement("span", {
268
+ style: {
269
+ color: 'dodgerBlue'
270
+ }
271
+ }, /*#__PURE__*/React.createElement(SVGIcon, {
272
+ src: AIWandSVG,
273
+ size: "x-small"
274
+ }))), kb_shortcuts && /*#__PURE__*/React.createElement(IconButton, {
241
275
  "data-btn-id": "rce-kbshortcut-btn",
242
- color: "primary",
276
+ color: "secondary",
243
277
  "aria-haspopup": "dialog",
244
278
  title: kbshortcut,
245
279
  tabIndex: tabIndexForBtn('rce-kbshortcut-btn'),
@@ -252,7 +286,7 @@ export default function StatusBar(props) {
252
286
  screenReaderLabel: kbshortcut,
253
287
  withBackground: false,
254
288
  withBorder: false
255
- }, /*#__PURE__*/React.createElement(IconKeyboardShortcutsLine, null)), !props.readOnly && isAvailable('ally_checker') && renderA11yButton());
289
+ }, /*#__PURE__*/React.createElement(IconKeyboardShortcutsLine, null)), a11y_checker && !props.readOnly && isAvailable('ally_checker') && renderA11yButton());
256
290
  }
257
291
 
258
292
  function renderWordCount() {
@@ -264,17 +298,25 @@ export default function StatusBar(props) {
264
298
  }`, {
265
299
  count: props.wordCount
266
300
  });
267
- return /*#__PURE__*/React.createElement(View, {
301
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
302
+ className: css(styles.separator)
303
+ }), /*#__PURE__*/React.createElement(View, {
268
304
  display: "inline-block",
269
305
  padding: "0 small",
270
306
  "data-testid": "status-bar-word-count"
271
307
  }, /*#__PURE__*/React.createElement(CondensedButton, {
272
308
  "data-btn-id": "rce-wordcount-btn",
273
- color: "primary",
309
+ color: "secondary",
274
310
  onClick: props.onWordcountModalOpen,
275
311
  tabIndex: tabIndexForBtn('rce-wordcount-btn'),
276
312
  title: formatMessage('View word and character counts')
277
- }, wordCount));
313
+ }, wordCount)));
314
+ }
315
+
316
+ function renderSection3(html_view, fullscreen, resize_handle) {
317
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
318
+ className: css(styles.separator)
319
+ }), html_view && renderToggleHtml(), fullscreen && renderFullscreen(), resize_handle && renderResizeHandle());
278
320
  }
279
321
 
280
322
  function descMsg() {
@@ -292,7 +334,7 @@ export default function StatusBar(props) {
292
334
  padding: "0 0 0 x-small"
293
335
  }, !props.readOnly && /*#__PURE__*/React.createElement(IconButton, {
294
336
  "data-btn-id": "rce-edit-btn",
295
- color: "primary",
337
+ color: "secondary",
296
338
  onClick: event => {
297
339
  props.onChangeView(isHtmlView() ? WYSIWYG_VIEW : getHtmlEditorView(event));
298
340
  },
@@ -329,7 +371,7 @@ export default function StatusBar(props) {
329
371
  const fullscreen = props.rceIsFullscreen ? formatMessage('Exit Fullscreen') : formatMessage('Fullscreen');
330
372
  return /*#__PURE__*/React.createElement(IconButton, {
331
373
  "data-btn-id": "rce-fullscreen-btn",
332
- color: "primary",
374
+ color: "secondary",
333
375
  title: fullscreen,
334
376
  tabIndex: tabIndexForBtn('rce-fullscreen-btn'),
335
377
  onClick: event => {
@@ -356,7 +398,19 @@ export default function StatusBar(props) {
356
398
  }
357
399
 
358
400
  const flexJustify = isHtmlView() ? 'end' : 'start';
359
- return /*#__PURE__*/React.createElement(Flex, {
401
+ const html_view = isFeature('html_view') && isAvailable('instructure_html_view');
402
+ const fullscreen = isFeature('fullscreen') && isAvailable('instructure_fullscreen');
403
+ const resize_handle = isFeature('resize_handle');
404
+ return /*#__PURE__*/React.createElement(InstUISettingsProvider, {
405
+ theme: {
406
+ componentOverrides: {
407
+ IconButton: {
408
+ secondaryGhostColor: 'rgb(34, 47, 62)' // to match tinymce's button color
409
+
410
+ }
411
+ }
412
+ }
413
+ }, /*#__PURE__*/React.createElement(Flex, {
360
414
  id: props.id,
361
415
  padding: "x-small 0 x-small x-small",
362
416
  "data-testid": "RCEStatusBar",
@@ -367,12 +421,8 @@ export default function StatusBar(props) {
367
421
  shouldGrow: true
368
422
  }, isHtmlView() ? renderHtmlEditorMessage() : renderPath()), /*#__PURE__*/React.createElement(Flex.Item, {
369
423
  role: "toolbar",
370
- title: formatMessage('Editor Statusbar')
371
- }, renderIconButtons(), /*#__PURE__*/React.createElement("div", {
372
- className: css(styles.separator)
373
- }), isAvailable('instructure_wordcount') && renderWordCount(), /*#__PURE__*/React.createElement("div", {
374
- className: css(styles.separator)
375
- }), isAvailable('instructure_html_view') && renderToggleHtml(), isAvailable('instructure_fullscreen') && renderFullscreen(), renderResizeHandle()));
424
+ title: formatMessage('Editor Status Bar')
425
+ }, renderIconButtons(), isFeature('word_count') && isAvailable('instructure_wordcount') && renderWordCount(), (html_view || fullscreen || resize_handle) && renderSection3(html_view, fullscreen, resize_handle))));
376
426
  }
377
427
  const styles = StyleSheet.create({
378
428
  separator: {
@@ -66,6 +66,7 @@ export const Footer = _ref => {
66
66
  margin: "0 0 0 x-small",
67
67
  "data-testid": "icon-maker-save"
68
68
  }, replaceAll ? formatMessage('Save') : formatMessage('Save Copy'))) : /*#__PURE__*/React.createElement(Button, {
69
+ id: "create-icon-button",
69
70
  disabled: disabled,
70
71
  margin: "0 0 0 x-small",
71
72
  color: "primary",
@@ -160,7 +160,9 @@ export function IconMakerTray(_ref) {
160
160
 
161
161
  const onClose = event => {
162
162
  if (shouldIgnoreClose(event === null || event === void 0 ? void 0 : event.target, editor === null || editor === void 0 ? void 0 : editor.id)) return;
163
- if ((statusRef === null || statusRef === void 0 ? void 0 : statusRef.current) === statuses.LOADING) return; // RCE already uses browser's confirm dialog for unsaved changes
163
+ if ((statusRef === null || statusRef === void 0 ? void 0 : statusRef.current) === statuses.LOADING) return; // Uploading an image creates a modal on the page. If that modal is open, we don't want to close the tray
164
+
165
+ if (!!hasOpenModal()) return; // RCE already uses browser's confirm dialog for unsaved changes
164
166
  // Its use here in the Icon Maker tray keeps that consistency
165
167
  // eslint-disable-next-line no-restricted-globals, no-alert
166
168
 
@@ -171,6 +173,8 @@ export function IconMakerTray(_ref) {
171
173
  setIsOpen(false);
172
174
  };
173
175
 
176
+ const hasOpenModal = () => document.querySelector('[data-cid="Modal"]');
177
+
174
178
  const isLoading = () => status === statuses.LOADING;
175
179
 
176
180
  const onKeyDown = event => {
@@ -304,6 +308,7 @@ export function IconMakerTray(_ref) {
304
308
  onDismiss: onClose,
305
309
  onUnmount: onUnmount,
306
310
  mountNode: mountNode,
311
+ shouldCloseOnDocumentClick: false,
307
312
  renderHeader: () => renderHeader(title, settings, onKeyDown, handleAlertDismissal, onClose),
308
313
  renderBody: () => renderBody(settings, dispatch, editor, editing, !replaceAll, nameRef, canvasOrigin, isLoading),
309
314
  renderFooter: () => renderFooter(status, onClose, handleSubmit, editing, replaceAll, setReplaceAll, applyRef, isModified.current),
@@ -21,20 +21,20 @@ import { instUiIconsArray } from '../../../util/instui-icon-helper'; // @ts-igno
21
21
 
22
22
  import { IconLtiSolid } from '@instructure/ui-icons/es/svg';
23
23
  export function externalToolsForToolbar(tools) {
24
- const favorited = tools.filter(it => it.favorite).slice(0, 2) || []; // There's no limit to always on apps, but in practice there shouldn't be more than 2 as well.
25
-
26
- const alwaysOn = tools.filter(it => it.always_on) || [];
27
- const set = new Map(); // Remove possible overlaps between favorited and alwaysOn, otherwise
24
+ // Limit of not on_by_default but favorited tools is 2
25
+ const favorited = tools.filter(it => it.favorite && !it.on_by_default).slice(0, 2) || [];
26
+ const onByDefault = tools.filter(it => it.on_by_default && it.favorite) || [];
27
+ const set = new Map(); // Remove possible overlaps between favorited and onByDefault, otherwise
28
28
  // we'd have duplicate buttons in the toolbar.
29
29
 
30
- for (const toolInfo of favorited.concat(alwaysOn)) {
30
+ for (const toolInfo of favorited.concat(onByDefault)) {
31
31
  set.set(toolInfo.id, toolInfo);
32
32
  }
33
33
 
34
34
  return Array.from(set.values()).sort((a, b) => {
35
- if (a.always_on && !b.always_on) {
35
+ if (a.on_by_default && !b.on_by_default) {
36
36
  return -1;
37
- } else if (!a.always_on && b.always_on) {
37
+ } else if (!a.on_by_default && b.on_by_default) {
38
38
  return 1;
39
39
  } else {
40
40
  // This *should* always be a string, but there might be cases where it isn't,
@@ -107,8 +107,8 @@ export class RceToolWrapper {
107
107
  return this.toolInfo.use_tray;
108
108
  }
109
109
 
110
- get always_on() {
111
- return this.toolInfo.always_on;
110
+ get on_by_default() {
111
+ return this.toolInfo.on_by_default;
112
112
  }
113
113
 
114
114
  asToolbarButton() {
@@ -131,6 +131,26 @@ export default class ExternalToolDialog extends React.Component {
131
131
  this.handleInfoAlertBlur = () => this.setState({
132
132
  infoAlert: null
133
133
  });
134
+
135
+ this.calcIFrameHeight = () => {
136
+ var _this$state$button, _this$state$button2;
137
+
138
+ if ((_this$state$button = this.state.button) !== null && _this$state$button !== void 0 && _this$state$button.use_tray) {
139
+ return '100%';
140
+ }
141
+
142
+ const toolDefinedHeight = (_this$state$button2 = this.state.button) === null || _this$state$button2 === void 0 ? void 0 : _this$state$button2.height;
143
+ const iFrameHeight = toolDefinedHeight !== null && toolDefinedHeight !== void 0 ? toolDefinedHeight : Math.max(Math.min(window.innerHeight - 100, 550), 100);
144
+ const modalMaxHeight = '95';
145
+ const modalHeaderHeightWithPadding = '5.5rem';
146
+ const complexHeightWithDVH = `min(${iFrameHeight}px, calc(${modalMaxHeight}dvh - ${modalHeaderHeightWithPadding}))`;
147
+
148
+ if (CSS.supports('height', complexHeightWithDVH)) {
149
+ return complexHeightWithDVH;
150
+ } else {
151
+ return `${iFrameHeight}px`;
152
+ }
153
+ };
134
154
  }
135
155
 
136
156
  open(button) {
@@ -202,12 +222,11 @@ export default class ExternalToolDialog extends React.Component {
202
222
  }
203
223
 
204
224
  render() {
205
- var _state$button, _props$env$rceWrapper, _props$env$rceWrapper2, _state$button$title, _state$button2, _state$button3, _state$button$width, _state$button4, _state$button5, _state$button$height, _state$button6;
225
+ var _state$button, _props$env$rceWrapper, _props$env$rceWrapper2, _state$button$title, _state$button2, _state$button3, _state$button$width, _state$button4;
206
226
 
207
227
  const state = this.state;
208
228
  const props = this.props;
209
229
  const label = formatMessage('Embed content from External Tool');
210
- const frameHeight = Math.max(Math.min(window.innerHeight - 100, 550), 100);
211
230
  const Overlay = (_state$button = state.button) !== null && _state$button !== void 0 && _state$button.use_tray ? ExternalToolDialogTray : ExternalToolDialogModal;
212
231
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("form", {
213
232
  ref: this.formRef,
@@ -272,8 +291,8 @@ export default class ExternalToolDialog extends React.Component {
272
291
  src: "",
273
292
  id: "external_tool_button_frame",
274
293
  style: {
294
+ height: this.calcIFrameHeight(),
275
295
  width: (_state$button3 = state.button) !== null && _state$button3 !== void 0 && _state$button3.use_tray ? '100%' : (_state$button$width = (_state$button4 = state.button) === null || _state$button4 === void 0 ? void 0 : _state$button4.width) !== null && _state$button$width !== void 0 ? _state$button$width : 800,
276
- height: (_state$button5 = state.button) !== null && _state$button5 !== void 0 && _state$button5.use_tray ? '100%' : (_state$button$height = (_state$button6 = state.button) === null || _state$button6 === void 0 ? void 0 : _state$button6.height) !== null && _state$button$height !== void 0 ? _state$button$height : frameHeight,
277
296
  border: '0',
278
297
  display: 'block',
279
298
  visibility: state.iframeLoaded ? 'visible' : 'hidden'
@@ -288,6 +307,9 @@ export default class ExternalToolDialog extends React.Component {
288
307
  ,
289
308
  onFocus: this.handleInfoAlertFocus,
290
309
  onBlur: this.handleInfoAlertBlur,
310
+ style: this.afterInfoAlertRef.current != null && state.infoAlert === this.afterInfoAlertRef.current ? {} : {
311
+ bottom: '0'
312
+ },
291
313
  className: this.afterInfoAlertRef.current != null && state.infoAlert === this.afterInfoAlertRef.current ? '' : 'screenreader-only'
292
314
  }, /*#__PURE__*/React.createElement(Alert, {
293
315
  margin: "small"
@@ -68,7 +68,7 @@ function registerFavoriteAppsToolbarButtons(editor) {
68
68
  }
69
69
 
70
70
  function registerAppsToolbarButton(editor) {
71
- const tooltip = formatMessage('Apps');
71
+ const tooltip = 'apps-temp';
72
72
  editor.ui.registry.addMenuButton('lti_mru_button', {
73
73
  tooltip,
74
74
  icon: 'lti',
@@ -79,6 +79,9 @@ function registerAppsToolbarButton(editor) {
79
79
  },
80
80
 
81
81
  onSetup(_api) {
82
+ const e = document.querySelector("button[title='apps-temp']");
83
+ e === null || e === void 0 ? void 0 : e.setAttribute('title', formatMessage('Apps'));
84
+ e === null || e === void 0 ? void 0 : e.setAttribute('id', 'plug-apps-button');
82
85
  return () => undefined;
83
86
  }
84
87
 
@@ -15,23 +15,24 @@
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
- import React, { Suspense, useCallback, useEffect, useRef, useState } from 'react';
18
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
19
19
  import { bool, element, func, instanceOf, oneOfType, shape, string } from 'prop-types';
20
20
  import { Tray } from '@instructure/ui-tray';
21
21
  import { CloseButton, Button } from '@instructure/ui-buttons';
22
22
  import { Heading } from '@instructure/ui-heading';
23
- import { Spinner } from '@instructure/ui-spinner';
24
23
  import { Flex } from '@instructure/ui-flex';
25
24
  import { View } from '@instructure/ui-view';
26
25
  import ErrorBoundary from './ErrorBoundary';
27
26
  import Bridge from '../../../bridge/Bridge';
28
27
  import formatMessage from '../../../format-message';
29
- import Filter, { useFilterSettings } from './Filter';
28
+ import Filter from './Filter';
29
+ import { useFilterSettings } from './useFilterSettings';
30
30
  import { getTrayHeight } from './trayUtils';
31
31
  import { ICON_MAKER_ICONS } from '../instructure_icon_maker/svg/constants';
32
32
  import { getLinkContentFromEditor } from './ContentSelection';
33
33
  import { LinkDisplay } from './LinkDisplay';
34
34
  import { showFlashAlert } from '../../../common/FlashAlert';
35
+ import { FILTER_SETTINGS_BY_PLUGIN, DynamicPanel, isLoading } from './canvasContentUtils';
35
36
  /**
36
37
  * Returns the translated tray label
37
38
  * @param {string} contentType - The type of content showing on tray
@@ -71,160 +72,6 @@ function getTrayLabel(contentType, contentSubtype, contextType) {
71
72
  // Shouldn't ever get here
72
73
  }
73
74
  }
74
-
75
- const thePanels = {
76
- icon_maker_icons: /*#__PURE__*/React.lazy(() => import('../instructure_icon_maker/components/SavedIconMakerList')),
77
- links: /*#__PURE__*/React.lazy(() => import('../instructure_links/components/LinksPanel')),
78
- images: /*#__PURE__*/React.lazy(() => import('../instructure_image/Images')),
79
- documents: /*#__PURE__*/React.lazy(() => import('../instructure_documents/components/DocumentsPanel')),
80
- media: /*#__PURE__*/React.lazy(() => import('../instructure_record/MediaPanel')),
81
- all: /*#__PURE__*/React.lazy(() => import('./RceFileBrowser')),
82
- unknown: /*#__PURE__*/React.lazy(() => import('./UnknownFileTypePanel'))
83
- }; // Returns a Suspense wrapped lazy loaded component
84
- // pulled from useLazy's cache
85
-
86
- function DynamicPanel(props) {
87
- let key = '';
88
-
89
- if (props.contentType === 'links') {
90
- key = 'links';
91
- } else {
92
- key = props.contentSubtype in thePanels ? props.contentSubtype : 'unknown';
93
- }
94
-
95
- const Component = thePanels[key];
96
- return /*#__PURE__*/React.createElement(Suspense, {
97
- fallback: /*#__PURE__*/React.createElement(Spinner, {
98
- renderTitle: renderLoading,
99
- size: "large"
100
- })
101
- }, /*#__PURE__*/React.createElement(Component, props));
102
- }
103
-
104
- function renderLoading() {
105
- return formatMessage('Loading');
106
- }
107
-
108
- const FILTER_SETTINGS_BY_PLUGIN = {
109
- user_documents: {
110
- contextType: 'user',
111
- contentType: 'user_files',
112
- contentSubtype: 'documents',
113
- sortValue: 'date_added',
114
- sortDir: 'desc',
115
- searchString: ''
116
- },
117
- course_documents: {
118
- contextType: 'course',
119
- contentType: 'course_files',
120
- contentSubtype: 'documents',
121
- sortValue: 'date_added',
122
- sortDir: 'desc',
123
- searchString: ''
124
- },
125
- group_documents: {
126
- contextType: 'group',
127
- contentType: 'group_files',
128
- contentSubtype: 'documents',
129
- sortValue: 'date_added',
130
- sortDir: 'desc',
131
- searchString: ''
132
- },
133
- user_images: {
134
- contextType: 'user',
135
- contentType: 'user_files',
136
- contentSubtype: 'images',
137
- sortValue: 'date_added',
138
- sortDir: 'desc',
139
- searchString: ''
140
- },
141
- course_images: {
142
- contextType: 'course',
143
- contentType: 'course_files',
144
- contentSubtype: 'images',
145
- sortValue: 'date_added',
146
- sortDir: 'desc',
147
- searchString: ''
148
- },
149
- group_images: {
150
- contextType: 'group',
151
- contentType: 'group_files',
152
- contentSubtype: 'images',
153
- sortValue: 'date_added',
154
- sortDir: 'desc',
155
- searchString: ''
156
- },
157
- user_media: {
158
- contextType: 'user',
159
- contentType: 'user_files',
160
- contentSubtype: 'media',
161
- sortValue: 'date_added',
162
- sortDir: 'desc',
163
- searchString: ''
164
- },
165
- course_media: {
166
- contextType: 'course',
167
- contentType: 'course_files',
168
- contentSubtype: 'media',
169
- sortValue: 'date_added',
170
- sortDir: 'desc',
171
- searchString: ''
172
- },
173
- group_media: {
174
- contextType: 'group',
175
- contentType: 'group_files',
176
- contentSubtype: 'media',
177
- sortValue: 'date_added',
178
- sortDir: 'desc',
179
- searchString: ''
180
- },
181
- course_links: {
182
- contextType: 'course',
183
- contentType: 'links',
184
- contentSubtype: 'all',
185
- sortValue: 'date_added',
186
- sortDir: 'desc',
187
- searchString: ''
188
- },
189
- course_link_edit: {
190
- contextType: 'course',
191
- contentType: 'links',
192
- contentSubtype: 'edit',
193
- sortValue: 'date_added',
194
- sortDir: 'desc',
195
- searchString: ''
196
- },
197
- group_links: {
198
- contextType: 'group',
199
- contentType: 'links',
200
- contentSubtype: 'all',
201
- sortValue: 'date_added',
202
- sortDir: 'desc',
203
- searchString: ''
204
- },
205
- list_icon_maker_icons: {
206
- contextType: 'course',
207
- contentType: 'course_files',
208
- contentSubtype: ICON_MAKER_ICONS,
209
- sortValue: 'date_added',
210
- sortDir: 'desc',
211
- searchString: ''
212
- },
213
- all: {
214
- contextType: 'course',
215
- contentType: 'course_files',
216
- contentSubtype: 'all',
217
- sortValue: 'alphabetical',
218
- sortDir: 'asc',
219
- searchString: ''
220
- }
221
- };
222
-
223
- function isLoading(sprops) {
224
- var _sprops$collections$a, _sprops$collections$a2, _sprops$collections$d, _sprops$collections$m, _sprops$collections$q, _sprops$collections$w, _sprops$documents$cou, _sprops$documents$use, _sprops$documents$gro, _sprops$media$course, _sprops$media$user, _sprops$media$group, _sprops$all_files;
225
-
226
- return ((_sprops$collections$a = sprops.collections.announcements) === null || _sprops$collections$a === void 0 ? void 0 : _sprops$collections$a.isLoading) || ((_sprops$collections$a2 = sprops.collections.assignments) === null || _sprops$collections$a2 === void 0 ? void 0 : _sprops$collections$a2.isLoading) || ((_sprops$collections$d = sprops.collections.discussions) === null || _sprops$collections$d === void 0 ? void 0 : _sprops$collections$d.isLoading) || ((_sprops$collections$m = sprops.collections.modules) === null || _sprops$collections$m === void 0 ? void 0 : _sprops$collections$m.isLoading) || ((_sprops$collections$q = sprops.collections.quizzes) === null || _sprops$collections$q === void 0 ? void 0 : _sprops$collections$q.isLoading) || ((_sprops$collections$w = sprops.collections.wikiPages) === null || _sprops$collections$w === void 0 ? void 0 : _sprops$collections$w.isLoading) || ((_sprops$documents$cou = sprops.documents.course) === null || _sprops$documents$cou === void 0 ? void 0 : _sprops$documents$cou.isLoading) || ((_sprops$documents$use = sprops.documents.user) === null || _sprops$documents$use === void 0 ? void 0 : _sprops$documents$use.isLoading) || ((_sprops$documents$gro = sprops.documents.group) === null || _sprops$documents$gro === void 0 ? void 0 : _sprops$documents$gro.isLoading) || ((_sprops$media$course = sprops.media.course) === null || _sprops$media$course === void 0 ? void 0 : _sprops$media$course.isLoading) || ((_sprops$media$user = sprops.media.user) === null || _sprops$media$user === void 0 ? void 0 : _sprops$media$user.isLoading) || ((_sprops$media$group = sprops.media.group) === null || _sprops$media$group === void 0 ? void 0 : _sprops$media$group.isLoading) || ((_sprops$all_files = sprops.all_files) === null || _sprops$all_files === void 0 ? void 0 : _sprops$all_files.isLoading);
227
- }
228
75
  /**
229
76
  * This component is used within various plugins to handle loading in content
230
77
  * from Canvas. It is essentially the main component.
@@ -533,7 +380,8 @@ function requiredWithoutSource(props, propName, componentName) {
533
380
  if (props.source == null && props[propName] == null) {
534
381
  throw new Error(`The prop \`${propName}\` is marked as required in \`${componentName}\`, but its value is \`${props[propName]}\`.`);
535
382
  }
536
- }
383
+ } // Changes made here may need to be reflected in the trayProps type in CanvasContentPanel
384
+
537
385
 
538
386
  const trayPropsMap = {
539
387
  canUploadFiles: bool.isRequired,
@@ -75,7 +75,7 @@ export function asLink($element, editor) {
75
75
  const contentType = $link.getAttribute('data-course-type');
76
76
  const fileName = $link.getAttribute('title');
77
77
  const published = $link.getAttribute('data-published') === 'true';
78
- const isPreviewable = $link.hasAttribute('data-canvas-previewable') || $link.classList.contains('instructure_scribd_file'); // needed to cover docs linked while there was a bug didn't add the data attr.
78
+ const isPreviewable = $link.getAttribute('data-canvas-previewable') === 'true' || $link.classList.contains('instructure_scribd_file'); // needed to cover docs linked while there was a bug didn't add the data attr.
79
79
 
80
80
  return {
81
81
  $element: $link,
@@ -26,23 +26,6 @@ import { IconButton } from '@instructure/ui-buttons';
26
26
  import { ScreenReaderContent } from '@instructure/ui-a11y-content';
27
27
  import { ICON_MAKER_ICONS } from '../instructure_icon_maker/svg/constants';
28
28
  import { IconLinkLine, IconFolderLine, IconImageLine, IconDocumentLine, IconAttachMediaLine, IconSearchLine, IconXLine } from '@instructure/ui-icons';
29
- const DEFAULT_FILTER_SETTINGS = {
30
- contentSubtype: 'all',
31
- contentType: 'links',
32
- sortValue: 'date_added',
33
- searchString: ''
34
- };
35
- export function useFilterSettings(default_settings) {
36
- const [filterSettings, setFilterSettings] = useState(default_settings || DEFAULT_FILTER_SETTINGS);
37
-
38
- function updateFilterSettings(nextSettings) {
39
- setFilterSettings({ ...filterSettings,
40
- ...nextSettings
41
- });
42
- }
43
-
44
- return [filterSettings, updateFilterSettings];
45
- }
46
29
 
47
30
  function fileLabelFromContext(contextType) {
48
31
  switch (contextType) {
@@ -59,7 +59,8 @@ export const FixedContentTray = _ref => {
59
59
  renderBody,
60
60
  renderFooter,
61
61
  bodyAs,
62
- shouldJoinBodyAndFooter
62
+ shouldJoinBodyAndFooter,
63
+ shouldCloseOnDocumentClick
63
64
  } = _ref;
64
65
  return /*#__PURE__*/React.createElement(Tray, {
65
66
  "data-mce-component": true,
@@ -69,7 +70,7 @@ export const FixedContentTray = _ref => {
69
70
  onExited: onUnmount,
70
71
  open: isOpen,
71
72
  placement: "end",
72
- shouldCloseOnDocumentClick: true,
73
+ shouldCloseOnDocumentClick: shouldCloseOnDocumentClick,
73
74
  shouldContainFocus: true,
74
75
  shouldReturnFocus: true,
75
76
  size: "regular"
@@ -103,7 +104,8 @@ FixedContentTray.propTypes = {
103
104
  onUnmount: PropTypes.func,
104
105
  mountNode: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
105
106
  bodyAs: PropTypes.string,
106
- shouldJoinBodyAndFooter: PropTypes.bool
107
+ shouldJoinBodyAndFooter: PropTypes.bool,
108
+ shouldCloseOnDocumentClick: PropTypes.bool
107
109
  };
108
110
  FixedContentTray.defaultProps = {
109
111
  title: null,
@@ -111,5 +113,6 @@ FixedContentTray.defaultProps = {
111
113
  onDismiss: () => {},
112
114
  onUnmount: () => {},
113
115
  bodyAs: 'div',
114
- shouldJoinBodyAndFooter: false
116
+ shouldJoinBodyAndFooter: false,
117
+ shouldCloseOnDocumentClick: true
115
118
  };
@@ -44,7 +44,8 @@ const ImageOptionsForm = _ref => {
44
44
  messagesForSize,
45
45
  hideDimensions,
46
46
  id = 'image-options-form',
47
- isIconMaker = false
47
+ isIconMaker = false,
48
+ forBlockEditorUse = false
48
49
  } = _ref;
49
50
  const TYPE = isIconMaker ? formatMessage('icon') : formatMessage('image');
50
51
  const tooltipText = formatMessage('Used by screen readers to describe the content of an {TYPE}', {
@@ -95,7 +96,7 @@ const ImageOptionsForm = _ref => {
95
96
  TYPE_UPPER
96
97
  }),
97
98
  onChange: handleIsDecorativeChange
98
- })), !isIconMaker && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Flex.Item, {
99
+ })), !isIconMaker && /*#__PURE__*/React.createElement(React.Fragment, null, !forBlockEditorUse && /*#__PURE__*/React.createElement(Flex.Item, {
99
100
  padding: "small"
100
101
  }, /*#__PURE__*/React.createElement(RadioInputGroup, {
101
102
  description: formatMessage('Display Options'),