@instructure/canvas-rce 5.13.2 → 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 (128) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/es/bridge/Bridge.js +0 -4
  3. package/es/defaultTinymceConfig.js +1 -1
  4. package/es/index.js +2 -0
  5. package/es/rce/RCE.js +3 -1
  6. package/es/rce/RCEVariants.js +121 -0
  7. package/es/rce/RCEWrapper.js +96 -47
  8. package/es/rce/RCEWrapperProps.js +5 -2
  9. package/es/rce/StatusBar.js +67 -17
  10. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Footer.js +1 -0
  11. package/es/rce/plugins/instructure_icon_maker/components/IconMakerTray.js +6 -1
  12. package/es/rce/plugins/instructure_rce_external_tools/RceToolWrapper.js +9 -9
  13. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.js +25 -3
  14. package/es/rce/plugins/instructure_rce_external_tools/plugin.js +4 -1
  15. package/es/rce/plugins/shared/CanvasContentTray.js +6 -158
  16. package/es/rce/plugins/shared/Filter.js +0 -17
  17. package/es/rce/plugins/shared/FixedContentTray.js +7 -4
  18. package/es/rce/plugins/shared/ImageOptionsForm.js +3 -2
  19. package/es/rce/plugins/shared/Upload/CanvasContentPanel.js +160 -0
  20. package/es/rce/plugins/shared/Upload/ComputerPanel.js +1 -0
  21. package/es/rce/plugins/shared/Upload/PanelFilter.js +144 -0
  22. package/es/rce/plugins/shared/Upload/UploadFile.js +10 -2
  23. package/es/rce/plugins/shared/Upload/UploadFileModal.js +47 -11
  24. package/es/rce/plugins/shared/Upload/UsageRightsSelectBox.js +20 -20
  25. package/es/rce/plugins/shared/Upload/index.js +19 -0
  26. package/es/rce/plugins/shared/ai_tools/AIResponseModal.js +70 -0
  27. package/es/rce/plugins/shared/ai_tools/AIToolsTray.js +510 -0
  28. package/es/rce/plugins/shared/ai_tools/aiicons.js +59 -0
  29. package/es/rce/plugins/shared/ai_tools/index.js +20 -0
  30. package/es/rce/plugins/shared/canvasContentUtils.js +190 -0
  31. package/es/rce/plugins/shared/do-fetch-api-effect/defaultFetchOptions.js +31 -0
  32. package/es/rce/plugins/shared/do-fetch-api-effect/doFetchApi.js +85 -0
  33. package/es/rce/plugins/shared/do-fetch-api-effect/get-cookie.js +29 -0
  34. package/es/rce/plugins/shared/do-fetch-api-effect/index.js +22 -0
  35. package/es/rce/plugins/shared/do-fetch-api-effect/parse-link-header.js +116 -0
  36. package/es/rce/plugins/shared/do-fetch-api-effect/query-string-encoding.js +51 -0
  37. package/es/rce/plugins/shared/useFilterSettings.js +35 -0
  38. package/es/sidebar/actions/upload.js +5 -2
  39. package/es/translations/locales/ar.js +64 -1
  40. package/es/translations/locales/ca.js +65 -2
  41. package/es/translations/locales/cy.js +64 -1
  42. package/es/translations/locales/da-x-k12.js +65 -2
  43. package/es/translations/locales/da.js +65 -2
  44. package/es/translations/locales/de.js +65 -2
  45. package/es/translations/locales/el.js +9 -0
  46. package/es/translations/locales/en-AU-x-unimelb.js +65 -2
  47. package/es/translations/locales/en-GB-x-ukhe.js +65 -2
  48. package/es/translations/locales/en.js +65 -2
  49. package/es/translations/locales/en_AU.js +65 -2
  50. package/es/translations/locales/en_CA.js +65 -2
  51. package/es/translations/locales/en_CY.js +65 -2
  52. package/es/translations/locales/en_GB.js +65 -2
  53. package/es/translations/locales/es.js +64 -1
  54. package/es/translations/locales/es_ES.js +64 -1
  55. package/es/translations/locales/fa_IR.js +9 -0
  56. package/es/translations/locales/fi.js +64 -1
  57. package/es/translations/locales/fr.js +65 -2
  58. package/es/translations/locales/fr_CA.js +65 -2
  59. package/es/translations/locales/ga.js +62 -2
  60. package/es/translations/locales/he.js +9 -0
  61. package/es/translations/locales/hi.js +119 -2
  62. package/es/translations/locales/ht.js +65 -2
  63. package/es/translations/locales/hu.js +15 -3
  64. package/es/translations/locales/hy.js +9 -0
  65. package/es/translations/locales/id.js +65 -2
  66. package/es/translations/locales/is.js +65 -2
  67. package/es/translations/locales/it.js +65 -2
  68. package/es/translations/locales/ja.js +65 -2
  69. package/es/translations/locales/ko.js +3 -0
  70. package/es/translations/locales/mi.js +65 -2
  71. package/es/translations/locales/ms.js +64 -1
  72. package/es/translations/locales/nb-x-k12.js +65 -2
  73. package/es/translations/locales/nb.js +65 -2
  74. package/es/translations/locales/nl.js +65 -2
  75. package/es/translations/locales/nn.js +15 -3
  76. package/es/translations/locales/pl.js +64 -1
  77. package/es/translations/locales/pt.js +64 -1
  78. package/es/translations/locales/pt_BR.js +65 -2
  79. package/es/translations/locales/ru.js +64 -1
  80. package/es/translations/locales/sl.js +65 -2
  81. package/es/translations/locales/sv-x-k12.js +65 -2
  82. package/es/translations/locales/sv.js +65 -2
  83. package/es/translations/locales/th.js +64 -1
  84. package/es/translations/locales/tr.js +9 -0
  85. package/es/translations/locales/uk_UA.js +9 -0
  86. package/es/translations/locales/vi.js +64 -1
  87. package/es/translations/locales/zh-Hans.js +64 -1
  88. package/es/translations/locales/zh-Hant.js +65 -2
  89. package/es/translations/locales/zh.js +64 -1
  90. package/es/translations/locales/zh_HK.js +65 -2
  91. package/es/translations/tinymce/ar_SA.js +4 -0
  92. package/es/translations/tinymce/bg_BG.js +4 -0
  93. package/es/translations/tinymce/ca.js +4 -0
  94. package/es/translations/tinymce/cs.js +4 -0
  95. package/es/translations/tinymce/cy.js +4 -0
  96. package/es/translations/tinymce/da.js +4 -0
  97. package/es/translations/tinymce/de.js +4 -0
  98. package/es/translations/tinymce/el.js +4 -0
  99. package/es/translations/tinymce/es.js +4 -0
  100. package/es/translations/tinymce/fa_IR.js +4 -0
  101. package/es/translations/tinymce/fr_FR.js +4 -0
  102. package/es/translations/tinymce/ga.js +4 -0
  103. package/es/translations/tinymce/he_IL.js +4 -0
  104. package/es/translations/tinymce/hu_HU.js +4 -0
  105. package/es/translations/tinymce/hy.js +4 -0
  106. package/es/translations/tinymce/id.js +4 -0
  107. package/es/translations/tinymce/it.js +4 -0
  108. package/es/translations/tinymce/ja.js +4 -0
  109. package/es/translations/tinymce/ko_KR.js +4 -0
  110. package/es/translations/tinymce/nb_NO.js +4 -0
  111. package/es/translations/tinymce/nl.js +4 -0
  112. package/es/translations/tinymce/pl.js +4 -0
  113. package/es/translations/tinymce/pt_BR.js +4 -0
  114. package/es/translations/tinymce/pt_PT.js +4 -0
  115. package/es/translations/tinymce/ro.js +4 -0
  116. package/es/translations/tinymce/ru.js +4 -0
  117. package/es/translations/tinymce/ru_RU.js +5 -1
  118. package/es/translations/tinymce/sl.js +4 -0
  119. package/es/translations/tinymce/sr.js +4 -0
  120. package/es/translations/tinymce/sv_SE.js +4 -0
  121. package/es/translations/tinymce/th.js +5 -1
  122. package/es/translations/tinymce/tr_TR.js +4 -0
  123. package/es/translations/tinymce/uk_UA.js +4 -0
  124. package/es/translations/tinymce/vi_VN.js +4 -0
  125. package/es/translations/tinymce/zh_CN.js +4 -0
  126. package/es/translations/tinymce/zh_TW.js +4 -0
  127. package/jest/jest-setup.js +1 -0
  128. package/package.json +1 -1
@@ -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,
@@ -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'),
@@ -0,0 +1,160 @@
1
+ import _pt from "prop-types";
2
+
3
+ /*
4
+ * Copyright (C) 2024 - present Instructure, Inc.
5
+ *
6
+ * This file is part of Canvas.
7
+ *
8
+ * Canvas is free software: you can redistribute it and/or modify it under
9
+ * the terms of the GNU Affero General Public License as published by the Free
10
+ * Software Foundation, version 3 of the License.
11
+ *
12
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
13
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
15
+ * details.
16
+ *
17
+ * You should have received a copy of the GNU Affero General Public License along
18
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ */
20
+ import React, { useState } from 'react';
21
+ import { Flex } from '@instructure/ui-flex';
22
+ import ErrorBoundary from '../ErrorBoundary';
23
+ import { useFilterSettings } from '../useFilterSettings';
24
+ import PanelFilter from './PanelFilter';
25
+ import { FILTER_SETTINGS_BY_PLUGIN, DynamicPanel } from '../canvasContentUtils';
26
+ import { useStoreProps } from '../StoreContext';
27
+ // TODO: Component is only validated for images, need to validate for other content types
28
+ export default function CanvasContentPanel(_ref) {
29
+ let {
30
+ trayProps,
31
+ canvasOrigin,
32
+ plugin,
33
+ setFileUrl
34
+ } = _ref;
35
+ const [filterSettings, setFilterSettings] = useFilterSettings(FILTER_SETTINGS_BY_PLUGIN[plugin]);
36
+ const [link, setLink] = useState(null);
37
+ const [hasLoaded, setHasLoaded] = useState(false); // storeProps has functions that collide with what we want to do in a block editor setting
38
+
39
+ const baseStoreProps = useStoreProps();
40
+ const {
41
+ onImageEmbed: _,
42
+ ...storeProps
43
+ } = baseStoreProps;
44
+
45
+ function handleFilterChange(newFilter, onChangeContext, onChangeSearchString, onChangeSortBy) {
46
+ const newFilterSettings = { ...newFilter
47
+ };
48
+
49
+ if (newFilterSettings.sortValue) {
50
+ newFilterSettings.sortDir = newFilterSettings.sortValue === 'alphabetical' ? 'asc' : 'desc';
51
+ onChangeSortBy({
52
+ sort: newFilterSettings.sortValue,
53
+ dir: newFilterSettings.sortDir
54
+ });
55
+ }
56
+
57
+ if ('searchString' in newFilterSettings && filterSettings.searchString !== newFilterSettings.searchString) {
58
+ onChangeSearchString(newFilterSettings.searchString);
59
+ }
60
+
61
+ setFilterSettings(newFilterSettings);
62
+
63
+ if (newFilterSettings.contentType) {
64
+ let contextType, contextId;
65
+
66
+ switch (newFilterSettings.contentType) {
67
+ case 'user_files':
68
+ contextType = 'user';
69
+ contextId = trayProps.containingContext.userId;
70
+ break;
71
+
72
+ case 'group_files':
73
+ contextType = 'group';
74
+ contextId = trayProps.containingContext.contextId;
75
+ break;
76
+
77
+ case 'course_files':
78
+ contextType = trayProps.contextType;
79
+ contextId = trayProps.containingContext.contextId;
80
+ break;
81
+
82
+ case 'links':
83
+ contextType = trayProps.containingContext.contextType;
84
+ contextId = trayProps.containingContext.contextId;
85
+ }
86
+
87
+ onChangeContext({
88
+ contextType,
89
+ contextId
90
+ }); // context is only changed on load
91
+
92
+ setHasLoaded(true);
93
+ }
94
+ }
95
+
96
+ const handleImageClick = image => {
97
+ setFileUrl(image.href);
98
+ };
99
+
100
+ return /*#__PURE__*/React.createElement(Flex, {
101
+ as: "div",
102
+ direction: "column",
103
+ tabIndex: -1
104
+ }, /*#__PURE__*/React.createElement(Flex.Item, {
105
+ padding: "medium"
106
+ }, /*#__PURE__*/React.createElement(PanelFilter, Object.assign({}, filterSettings, {
107
+ onChange: newFilter => {
108
+ handleFilterChange(newFilter, storeProps.onChangeContext, storeProps.onChangeSearchString, storeProps.onChangeSortBy);
109
+ }
110
+ }))), /*#__PURE__*/React.createElement(Flex.Item, {
111
+ shouldGrow: true,
112
+ shouldShrink: true,
113
+ margin: "xx-small xxx-small 0"
114
+ }, hasLoaded && /*#__PURE__*/React.createElement(Flex, {
115
+ justifyItems: "space-between",
116
+ direction: "column",
117
+ height: "100%"
118
+ }, /*#__PURE__*/React.createElement(Flex.Item, {
119
+ shouldGrow: true,
120
+ shouldShrink: true
121
+ }, /*#__PURE__*/React.createElement(ErrorBoundary, null, /*#__PURE__*/React.createElement(DynamicPanel, Object.assign({
122
+ contentType: filterSettings.contentType,
123
+ contentSubtype: filterSettings.contentSubtype,
124
+ sortBy: {
125
+ sort: filterSettings.sortValue,
126
+ order: filterSettings.sortDir
127
+ },
128
+ searchString: filterSettings.searchString,
129
+ canvasOrigin: canvasOrigin,
130
+ context: {
131
+ type: trayProps.contextType,
132
+ id: trayProps.contextId
133
+ },
134
+ editing: false,
135
+ onEditClick: setLink,
136
+ selectedLink: link,
137
+ onImageEmbed: handleImageClick
138
+ }, storeProps)))))));
139
+ }
140
+ CanvasContentPanel.propTypes = {
141
+ trayProps: _pt.shape({
142
+ canUploadFiles: _pt.bool.isRequired,
143
+ contextId: _pt.string.isRequired,
144
+ contextType: _pt.string.isRequired,
145
+ containingContext: _pt.shape({
146
+ contextType: _pt.string.isRequired,
147
+ contextId: _pt.string.isRequired,
148
+ userId: _pt.string.isRequired
149
+ }).isRequired,
150
+ filesTabDisabled: _pt.bool.isRequired,
151
+ host: _pt.string.isRequired,
152
+ jwt: _pt.string.isRequired,
153
+ source: _pt.shape({}).isRequired,
154
+ themeUrl: _pt.string.isRequired,
155
+ storeProps: _pt.any.isRequired
156
+ }).isRequired,
157
+ canvasOrigin: _pt.string.isRequired,
158
+ plugin: _pt.any.isRequired,
159
+ setFileUrl: _pt.func.isRequired
160
+ };
@@ -273,6 +273,7 @@ export default function ComputerPanel(_ref) {
273
273
  return /*#__PURE__*/React.createElement("div", {
274
274
  ref: panelRef
275
275
  }, /*#__PURE__*/React.createElement(FileDrop, {
276
+ "data-testid": "filedrop",
276
277
  accept: accept,
277
278
  onDropAccepted: _ref2 => {
278
279
  let [file] = _ref2;