@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.
- package/CHANGELOG.md +41 -0
- package/es/bridge/Bridge.js +0 -4
- package/es/defaultTinymceConfig.js +3 -3
- package/es/enhance-user-content/enhance_user_content.js +31 -3
- package/es/index.js +2 -0
- package/es/rce/RCE.js +3 -1
- package/es/rce/RCEVariants.js +121 -0
- package/es/rce/RCEWrapper.js +96 -47
- package/es/rce/RCEWrapperProps.js +5 -2
- package/es/rce/StatusBar.js +67 -17
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Footer.js +1 -0
- package/es/rce/plugins/instructure_icon_maker/components/IconMakerTray.js +6 -1
- package/es/rce/plugins/instructure_rce_external_tools/RceToolWrapper.js +9 -9
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.js +25 -3
- package/es/rce/plugins/instructure_rce_external_tools/plugin.js +4 -1
- package/es/rce/plugins/shared/CanvasContentTray.js +6 -158
- package/es/rce/plugins/shared/ContentSelection.js +1 -1
- package/es/rce/plugins/shared/Filter.js +0 -17
- package/es/rce/plugins/shared/FixedContentTray.js +7 -4
- package/es/rce/plugins/shared/ImageOptionsForm.js +3 -2
- package/es/rce/plugins/shared/Upload/CanvasContentPanel.js +160 -0
- package/es/rce/plugins/shared/Upload/ComputerPanel.js +1 -0
- package/es/rce/plugins/shared/Upload/PanelFilter.js +144 -0
- package/es/rce/plugins/shared/Upload/UploadFile.js +10 -2
- package/es/rce/plugins/shared/Upload/UploadFileModal.js +47 -11
- package/es/rce/plugins/shared/Upload/UsageRightsSelectBox.js +20 -20
- package/es/rce/plugins/shared/Upload/index.js +19 -0
- package/es/rce/plugins/shared/ai_tools/AIResponseModal.js +70 -0
- package/es/rce/plugins/shared/ai_tools/AIToolsTray.js +510 -0
- package/es/rce/plugins/shared/ai_tools/aiicons.js +59 -0
- package/es/rce/plugins/shared/ai_tools/index.js +20 -0
- package/es/rce/plugins/shared/canvasContentUtils.js +190 -0
- package/es/rce/plugins/shared/do-fetch-api-effect/defaultFetchOptions.js +31 -0
- package/es/rce/plugins/shared/do-fetch-api-effect/doFetchApi.js +85 -0
- package/es/rce/plugins/shared/do-fetch-api-effect/get-cookie.js +29 -0
- package/es/rce/plugins/shared/do-fetch-api-effect/index.js +22 -0
- package/es/rce/plugins/shared/do-fetch-api-effect/parse-link-header.js +116 -0
- package/es/rce/plugins/shared/do-fetch-api-effect/query-string-encoding.js +51 -0
- package/es/rce/plugins/shared/useFilterSettings.js +35 -0
- package/es/sidebar/actions/upload.js +5 -2
- package/es/translations/locales/ar.js +64 -1
- package/es/translations/locales/ca.js +65 -2
- package/es/translations/locales/cy.js +64 -1
- package/es/translations/locales/da-x-k12.js +65 -2
- package/es/translations/locales/da.js +65 -2
- package/es/translations/locales/de.js +65 -2
- package/es/translations/locales/el.js +9 -0
- package/es/translations/locales/en-AU-x-unimelb.js +65 -2
- package/es/translations/locales/en-GB-x-ukhe.js +65 -2
- package/es/translations/locales/en.js +65 -2
- package/es/translations/locales/en_AU.js +65 -2
- package/es/translations/locales/en_CA.js +65 -2
- package/es/translations/locales/en_CY.js +65 -2
- package/es/translations/locales/en_GB.js +65 -2
- package/es/translations/locales/es.js +64 -1
- package/es/translations/locales/es_ES.js +64 -1
- package/es/translations/locales/fa_IR.js +9 -0
- package/es/translations/locales/fi.js +64 -1
- package/es/translations/locales/fr.js +65 -2
- package/es/translations/locales/fr_CA.js +65 -2
- package/es/translations/locales/ga.js +62 -2
- package/es/translations/locales/he.js +9 -0
- package/es/translations/locales/hi.js +119 -2
- package/es/translations/locales/ht.js +65 -2
- package/es/translations/locales/hu.js +15 -3
- package/es/translations/locales/hy.js +9 -0
- package/es/translations/locales/id.js +65 -2
- package/es/translations/locales/is.js +65 -2
- package/es/translations/locales/it.js +65 -2
- package/es/translations/locales/ja.js +65 -2
- package/es/translations/locales/ko.js +3 -0
- package/es/translations/locales/mi.js +65 -2
- package/es/translations/locales/ms.js +64 -1
- package/es/translations/locales/nb-x-k12.js +65 -2
- package/es/translations/locales/nb.js +65 -2
- package/es/translations/locales/nl.js +65 -2
- package/es/translations/locales/nn.js +15 -3
- package/es/translations/locales/pl.js +64 -1
- package/es/translations/locales/pt.js +64 -1
- package/es/translations/locales/pt_BR.js +65 -2
- package/es/translations/locales/ru.js +64 -1
- package/es/translations/locales/sl.js +65 -2
- package/es/translations/locales/sv-x-k12.js +65 -2
- package/es/translations/locales/sv.js +65 -2
- package/es/translations/locales/th.js +64 -1
- package/es/translations/locales/tr.js +9 -0
- package/es/translations/locales/uk_UA.js +9 -0
- package/es/translations/locales/vi.js +64 -1
- package/es/translations/locales/zh-Hans.js +64 -1
- package/es/translations/locales/zh-Hant.js +65 -2
- package/es/translations/locales/zh.js +64 -1
- package/es/translations/locales/zh_HK.js +65 -2
- package/es/translations/tinymce/ar_SA.js +4 -0
- package/es/translations/tinymce/bg_BG.js +4 -0
- package/es/translations/tinymce/ca.js +4 -0
- package/es/translations/tinymce/cs.js +4 -0
- package/es/translations/tinymce/cy.js +4 -0
- package/es/translations/tinymce/da.js +4 -0
- package/es/translations/tinymce/de.js +4 -0
- package/es/translations/tinymce/el.js +4 -0
- package/es/translations/tinymce/es.js +4 -0
- package/es/translations/tinymce/fa_IR.js +4 -0
- package/es/translations/tinymce/fr_FR.js +4 -0
- package/es/translations/tinymce/ga.js +4 -0
- package/es/translations/tinymce/he_IL.js +4 -0
- package/es/translations/tinymce/hu_HU.js +4 -0
- package/es/translations/tinymce/hy.js +4 -0
- package/es/translations/tinymce/id.js +4 -0
- package/es/translations/tinymce/it.js +4 -0
- package/es/translations/tinymce/ja.js +4 -0
- package/es/translations/tinymce/ko_KR.js +4 -0
- package/es/translations/tinymce/nb_NO.js +4 -0
- package/es/translations/tinymce/nl.js +4 -0
- package/es/translations/tinymce/pl.js +4 -0
- package/es/translations/tinymce/pt_BR.js +4 -0
- package/es/translations/tinymce/pt_PT.js +4 -0
- package/es/translations/tinymce/ro.js +4 -0
- package/es/translations/tinymce/ru.js +4 -0
- package/es/translations/tinymce/ru_RU.js +5 -1
- package/es/translations/tinymce/sl.js +4 -0
- package/es/translations/tinymce/sr.js +4 -0
- package/es/translations/tinymce/sv_SE.js +4 -0
- package/es/translations/tinymce/th.js +5 -1
- package/es/translations/tinymce/tr_TR.js +4 -0
- package/es/translations/tinymce/uk_UA.js +4 -0
- package/es/translations/tinymce/vi_VN.js +4 -0
- package/es/translations/tinymce/zh_CN.js +4 -0
- package/es/translations/tinymce/zh_TW.js +4 -0
- package/jest/jest-setup.js +1 -0
- package/package.json +1 -1
|
@@ -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;
|
|
@@ -0,0 +1,144 @@
|
|
|
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 { ScreenReaderContent } from '@instructure/ui-a11y-content';
|
|
21
|
+
import { IconButton } from '@instructure/ui-buttons';
|
|
22
|
+
import { Flex } from '@instructure/ui-flex';
|
|
23
|
+
import { IconSearchLine, IconXLine } from '@instructure/ui-icons';
|
|
24
|
+
import { SimpleSelect } from '@instructure/ui-simple-select';
|
|
25
|
+
import { TextInput } from '@instructure/ui-text-input';
|
|
26
|
+
import formatMessage from 'format-message';
|
|
27
|
+
import React, { useEffect, useState } from 'react';
|
|
28
|
+
|
|
29
|
+
function shouldSearch(searchString) {
|
|
30
|
+
return searchString.length === 0 || searchString.length >= 3;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default function PanelFilter(_ref) {
|
|
34
|
+
let {
|
|
35
|
+
mountNode,
|
|
36
|
+
onChange,
|
|
37
|
+
sortValue,
|
|
38
|
+
searchString,
|
|
39
|
+
contentType
|
|
40
|
+
} = _ref;
|
|
41
|
+
const [pendingSearchString, setPendingSearchString] = useState(searchString);
|
|
42
|
+
const [searchInputTimer, setSearchInputTimer] = useState(0); // only run on mounting to trigger change to correct contextType
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
onChange({
|
|
46
|
+
contentType
|
|
47
|
+
});
|
|
48
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
49
|
+
|
|
50
|
+
function doSearch(value) {
|
|
51
|
+
if (shouldSearch(value)) {
|
|
52
|
+
if (searchInputTimer) {
|
|
53
|
+
window.clearTimeout(searchInputTimer);
|
|
54
|
+
setSearchInputTimer(0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
onChange({
|
|
58
|
+
searchString: value
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function handleChangeSearch(value) {
|
|
64
|
+
setPendingSearchString(value);
|
|
65
|
+
|
|
66
|
+
if (searchInputTimer) {
|
|
67
|
+
window.clearTimeout(searchInputTimer);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const tid = window.setTimeout(() => doSearch(value), 250);
|
|
71
|
+
setSearchInputTimer(tid);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function handleClear() {
|
|
75
|
+
handleChangeSearch('');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function renderClearButton() {
|
|
79
|
+
if (pendingSearchString) {
|
|
80
|
+
return /*#__PURE__*/React.createElement(IconButton, {
|
|
81
|
+
screenReaderLabel: formatMessage('Clear'),
|
|
82
|
+
onClick: handleClear,
|
|
83
|
+
withBorder: false,
|
|
84
|
+
withBackground: false,
|
|
85
|
+
size: "small"
|
|
86
|
+
}, /*#__PURE__*/React.createElement(IconXLine, null));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return /*#__PURE__*/React.createElement(Flex, {
|
|
93
|
+
margin: "none xx-large none none",
|
|
94
|
+
gap: "small",
|
|
95
|
+
alignItems: "start"
|
|
96
|
+
}, /*#__PURE__*/React.createElement(Flex.Item, {
|
|
97
|
+
shouldShrink: true,
|
|
98
|
+
shouldGrow: false,
|
|
99
|
+
margin: "none none none none"
|
|
100
|
+
}, /*#__PURE__*/React.createElement(SimpleSelect, {
|
|
101
|
+
"data-testid": "filter-sort-by",
|
|
102
|
+
mountNode: mountNode,
|
|
103
|
+
renderLabel: /*#__PURE__*/React.createElement(ScreenReaderContent, null, formatMessage('Sort By')),
|
|
104
|
+
assistiveText: formatMessage('Use arrow keys to navigate options.'),
|
|
105
|
+
onChange: (e, selection) => {
|
|
106
|
+
onChange({
|
|
107
|
+
sortValue: selection.value
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
value: sortValue
|
|
111
|
+
}, /*#__PURE__*/React.createElement(SimpleSelect.Option, {
|
|
112
|
+
id: "date_added",
|
|
113
|
+
value: "date_added"
|
|
114
|
+
}, formatMessage('Date Added')), /*#__PURE__*/React.createElement(SimpleSelect.Option, {
|
|
115
|
+
id: "alphabetical",
|
|
116
|
+
value: "alphabetical"
|
|
117
|
+
}, formatMessage('Alphabetical')))), /*#__PURE__*/React.createElement(Flex.Item, {
|
|
118
|
+
shouldGrow: true,
|
|
119
|
+
margin: "none xx-large none none"
|
|
120
|
+
}, /*#__PURE__*/React.createElement(TextInput, {
|
|
121
|
+
renderLabel: /*#__PURE__*/React.createElement(ScreenReaderContent, null, formatMessage('Search')),
|
|
122
|
+
renderBeforeInput: /*#__PURE__*/React.createElement(IconSearchLine, {
|
|
123
|
+
inline: false
|
|
124
|
+
}),
|
|
125
|
+
renderAfterInput: renderClearButton(),
|
|
126
|
+
messages: [{
|
|
127
|
+
type: 'hint',
|
|
128
|
+
text: formatMessage('Enter at least 3 characters to search')
|
|
129
|
+
}],
|
|
130
|
+
placeholder: formatMessage('Search'),
|
|
131
|
+
value: pendingSearchString,
|
|
132
|
+
onChange: (e, value) => handleChangeSearch(value),
|
|
133
|
+
onKeyDown: e => {
|
|
134
|
+
if (e.key === 'Enter') {
|
|
135
|
+
doSearch(pendingSearchString);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
})));
|
|
139
|
+
}
|
|
140
|
+
PanelFilter.propTypes = {
|
|
141
|
+
sortValue: _pt.string.isRequired,
|
|
142
|
+
searchString: _pt.string.isRequired,
|
|
143
|
+
contentType: _pt.string.isRequired
|
|
144
|
+
};
|
|
@@ -28,12 +28,14 @@ import { StoreProvider } from '../StoreContext';
|
|
|
28
28
|
import Bridge from '../../../../bridge';
|
|
29
29
|
import UploadFileModal from './UploadFileModal';
|
|
30
30
|
import RCEWrapper from '../../../RCEWrapper';
|
|
31
|
+
import { UploadCanvasPanelIds } from '../canvasContentUtils';
|
|
31
32
|
export const UploadFilePanelIds = ['COMPUTER', 'URL'];
|
|
33
|
+
export const FullPanelIds = [...UploadCanvasPanelIds, ...UploadFilePanelIds];
|
|
32
34
|
|
|
33
35
|
/**
|
|
34
36
|
* Handles uploading data based on what type of data is submitted.
|
|
35
37
|
*/
|
|
36
|
-
export const handleSubmit = function (editor, accept, selectedPanel, uploadData, storeProps,
|
|
38
|
+
export const handleSubmit = function (editor, accept, selectedPanel, uploadData, storeProps, _source) {
|
|
37
39
|
let afterInsert = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : () => undefined;
|
|
38
40
|
Bridge.focusEditor(RCEWrapper.getByEditor(editor)); // necessary since it blurred when the modal opened
|
|
39
41
|
|
|
@@ -116,6 +118,8 @@ export function UploadFile(_ref) {
|
|
|
116
118
|
panels,
|
|
117
119
|
onDismiss,
|
|
118
120
|
requireA11yAttributes = true,
|
|
121
|
+
forBlockEditorUse = false,
|
|
122
|
+
uploading = false,
|
|
119
123
|
trayProps,
|
|
120
124
|
canvasOrigin,
|
|
121
125
|
onSubmit = handleSubmit,
|
|
@@ -158,7 +162,9 @@ export function UploadFile(_ref) {
|
|
|
158
162
|
accept: accept,
|
|
159
163
|
modalBodyWidth: modalBodyWidth,
|
|
160
164
|
modalBodyHeight: modalBodyHeight,
|
|
161
|
-
requireA11yAttributes: requireA11yAttributes
|
|
165
|
+
requireA11yAttributes: requireA11yAttributes,
|
|
166
|
+
forBlockEditorUse: forBlockEditorUse,
|
|
167
|
+
uploading: uploading
|
|
162
168
|
}));
|
|
163
169
|
}
|
|
164
170
|
UploadFile.propTypes = {
|
|
@@ -166,6 +172,8 @@ UploadFile.propTypes = {
|
|
|
166
172
|
label: _pt.string.isRequired,
|
|
167
173
|
panels: _pt.array,
|
|
168
174
|
requireA11yAttributes: _pt.bool,
|
|
175
|
+
forBlockEditorUse: _pt.bool,
|
|
176
|
+
uploading: _pt.bool,
|
|
169
177
|
trayProps: _pt.object,
|
|
170
178
|
canvasOrigin: _pt.string
|
|
171
179
|
};
|
|
@@ -29,6 +29,8 @@ import RceApiSource from '../../../../rcs/api';
|
|
|
29
29
|
import ImageOptionsForm from '../ImageOptionsForm';
|
|
30
30
|
import UsageRightsSelectBox from './UsageRightsSelectBox';
|
|
31
31
|
import { View } from '@instructure/ui-view';
|
|
32
|
+
import { UploadCanvasPanelIds, CanvasPanelTitles } from '../canvasContentUtils';
|
|
33
|
+
const CanvasContentPanel = /*#__PURE__*/React.lazy(() => import('./CanvasContentPanel'));
|
|
32
34
|
const ComputerPanel = /*#__PURE__*/React.lazy(() => import('./ComputerPanel'));
|
|
33
35
|
const UrlPanel = /*#__PURE__*/React.lazy(() => import('./UrlPanel'));
|
|
34
36
|
|
|
@@ -51,6 +53,7 @@ function shouldBeDisabled(_ref, selectedPanel, usageRightNotSet) {
|
|
|
51
53
|
return !fileUrl;
|
|
52
54
|
|
|
53
55
|
default:
|
|
56
|
+
if (UploadCanvasPanelIds.includes(selectedPanel)) return !fileUrl;
|
|
54
57
|
return false;
|
|
55
58
|
// When in doubt, don't disable (but we shouldn't get here either)
|
|
56
59
|
}
|
|
@@ -72,7 +75,9 @@ const UploadFileModal = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
|
72
75
|
accept,
|
|
73
76
|
modalBodyWidth,
|
|
74
77
|
modalBodyHeight,
|
|
75
|
-
requireA11yAttributes = true
|
|
78
|
+
requireA11yAttributes = true,
|
|
79
|
+
forBlockEditorUse = false,
|
|
80
|
+
uploading = false
|
|
76
81
|
} = _ref2;
|
|
77
82
|
const [theFile, setFile] = useState(preselectedFile);
|
|
78
83
|
const [error, setError] = useState(null);
|
|
@@ -103,6 +108,11 @@ const UploadFileModal = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
|
103
108
|
setDisplayAs(event.target.value);
|
|
104
109
|
}
|
|
105
110
|
|
|
111
|
+
const handleRequestTabChange = index => {
|
|
112
|
+
setSelectedPanel(panels[index]);
|
|
113
|
+
setFileUrl('');
|
|
114
|
+
};
|
|
115
|
+
|
|
106
116
|
const submitDisabled = shouldBeDisabled({
|
|
107
117
|
fileUrl,
|
|
108
118
|
theFile,
|
|
@@ -119,6 +129,11 @@ const UploadFileModal = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
|
119
129
|
host: trayProps.host,
|
|
120
130
|
canvasOrigin
|
|
121
131
|
});
|
|
132
|
+
|
|
133
|
+
if (forBlockEditorUse && !['COMPUTER', 'URL'].includes(selectedPanel)) {
|
|
134
|
+
requireA11yAttributes = false;
|
|
135
|
+
}
|
|
136
|
+
|
|
122
137
|
return /*#__PURE__*/React.createElement(Modal, {
|
|
123
138
|
"data-mce-component": true,
|
|
124
139
|
as: "form",
|
|
@@ -130,7 +145,7 @@ const UploadFileModal = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
|
130
145
|
onSubmit: e => {
|
|
131
146
|
e.preventDefault();
|
|
132
147
|
|
|
133
|
-
if (submitDisabled) {
|
|
148
|
+
if (submitDisabled || uploading) {
|
|
134
149
|
return false;
|
|
135
150
|
}
|
|
136
151
|
|
|
@@ -160,7 +175,7 @@ const UploadFileModal = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
|
160
175
|
let {
|
|
161
176
|
index
|
|
162
177
|
} = _ref3;
|
|
163
|
-
return
|
|
178
|
+
return handleRequestTabChange(index);
|
|
164
179
|
}
|
|
165
180
|
}, panels.map(panel => {
|
|
166
181
|
switch (panel) {
|
|
@@ -177,7 +192,6 @@ const UploadFileModal = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
|
177
192
|
size: "large"
|
|
178
193
|
})
|
|
179
194
|
}, /*#__PURE__*/React.createElement(ComputerPanel, {
|
|
180
|
-
editor: editor,
|
|
181
195
|
theFile: theFile,
|
|
182
196
|
setFile: setFile,
|
|
183
197
|
setError: setError,
|
|
@@ -205,9 +219,28 @@ const UploadFileModal = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
|
205
219
|
fileUrl: fileUrl,
|
|
206
220
|
setFileUrl: setFileUrl
|
|
207
221
|
})));
|
|
208
|
-
}
|
|
209
222
|
|
|
210
|
-
|
|
223
|
+
default:
|
|
224
|
+
if (UploadCanvasPanelIds.includes(panel)) {
|
|
225
|
+
return /*#__PURE__*/React.createElement(Tabs.Panel, {
|
|
226
|
+
key: panel,
|
|
227
|
+
renderTitle: () => CanvasPanelTitles[panel],
|
|
228
|
+
isSelected: selectedPanel === panel
|
|
229
|
+
}, /*#__PURE__*/React.createElement(Suspense, {
|
|
230
|
+
fallback: /*#__PURE__*/React.createElement(Spinner, {
|
|
231
|
+
renderTitle: formatMessage('Loading'),
|
|
232
|
+
size: "large"
|
|
233
|
+
})
|
|
234
|
+
}, /*#__PURE__*/React.createElement(CanvasContentPanel, {
|
|
235
|
+
trayProps: trayProps,
|
|
236
|
+
canvasOrigin: canvasOrigin,
|
|
237
|
+
plugin: panel,
|
|
238
|
+
setFileUrl: setFileUrl
|
|
239
|
+
})));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
211
244
|
})), // We shouldn't show the accordions until the session data is loaded.
|
|
212
245
|
Object.keys(contentProps.session || {}).length > 0 && /*#__PURE__*/React.createElement(React.Fragment, null, selectedPanel === 'COMPUTER' && requiresUsageRights && /*#__PURE__*/React.createElement(View, {
|
|
213
246
|
as: "div",
|
|
@@ -245,28 +278,31 @@ const UploadFileModal = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
|
245
278
|
handleAltTextChange: handleAltTextChange,
|
|
246
279
|
handleIsDecorativeChange: handleIsDecorativeChange,
|
|
247
280
|
handleDisplayAsChange: handleDisplayAsChange,
|
|
248
|
-
hideDimensions: true
|
|
281
|
+
hideDimensions: true,
|
|
282
|
+
forBlockEditorUse: forBlockEditorUse
|
|
249
283
|
}))))), /*#__PURE__*/React.createElement(Modal.Footer, null, /*#__PURE__*/React.createElement(Button, {
|
|
250
284
|
onClick: onDismiss
|
|
251
285
|
}, formatMessage('Close')), "\xA0", /*#__PURE__*/React.createElement(Button, {
|
|
252
286
|
color: "primary",
|
|
253
287
|
type: "submit",
|
|
254
|
-
disabled: submitDisabled
|
|
255
|
-
}, formatMessage('Submit'))));
|
|
288
|
+
disabled: submitDisabled || uploading
|
|
289
|
+
}, uploading ? formatMessage('Submitting...') : formatMessage('Submit'))));
|
|
256
290
|
});
|
|
257
291
|
UploadFileModal.propTypes = {
|
|
258
|
-
editor: object
|
|
292
|
+
editor: object,
|
|
259
293
|
contentProps: object,
|
|
260
294
|
trayProps: object,
|
|
261
295
|
canvasOrigin: string,
|
|
262
296
|
onSubmit: func,
|
|
263
297
|
onDismiss: func.isRequired,
|
|
264
|
-
panels: arrayOf(oneOf(['COMPUTER', 'URL'])),
|
|
298
|
+
panels: arrayOf(oneOf(['COMPUTER', 'URL', ...UploadCanvasPanelIds])),
|
|
265
299
|
label: string.isRequired,
|
|
266
300
|
accept: oneOfType([arrayOf(string), string]),
|
|
267
301
|
modalBodyWidth: number,
|
|
268
302
|
modalBodyHeight: number,
|
|
269
303
|
requireA11yAttributes: bool,
|
|
304
|
+
forBlockEditorUse: bool,
|
|
305
|
+
uploading: bool,
|
|
270
306
|
preselectedFile: object // JS File
|
|
271
307
|
|
|
272
308
|
};
|
|
@@ -21,25 +21,6 @@ import formatMessage from '../../../../format-message';
|
|
|
21
21
|
import { SimpleSelect } from '@instructure/ui-simple-select';
|
|
22
22
|
import { View } from '@instructure/ui-view';
|
|
23
23
|
import { TextInput } from '@instructure/ui-text-input';
|
|
24
|
-
const CONTENT_OPTIONS = [{
|
|
25
|
-
display: formatMessage('Choose usage rights...'),
|
|
26
|
-
value: 'choose'
|
|
27
|
-
}, {
|
|
28
|
-
display: formatMessage('I hold the copyright'),
|
|
29
|
-
value: 'own_copyright'
|
|
30
|
-
}, {
|
|
31
|
-
display: formatMessage('I have obtained permission to use this file.'),
|
|
32
|
-
value: 'used_by_permission'
|
|
33
|
-
}, {
|
|
34
|
-
display: formatMessage('The material is in the public domain'),
|
|
35
|
-
value: 'public_domain'
|
|
36
|
-
}, {
|
|
37
|
-
display: formatMessage('The material is subject to an exception - e.g. fair use, the right to quote, or others under applicable copyright laws'),
|
|
38
|
-
value: 'fair_use'
|
|
39
|
-
}, {
|
|
40
|
-
display: formatMessage('The material is licensed under Creative Commons'),
|
|
41
|
-
value: 'creative_commons'
|
|
42
|
-
}];
|
|
43
24
|
|
|
44
25
|
const ShowCreativeCommonsOptions = _ref => {
|
|
45
26
|
let {
|
|
@@ -96,6 +77,25 @@ const UsageRightsSelectBox = _ref3 => {
|
|
|
96
77
|
const showCreativeCommonsOptions = usageRight === 'creative_commons';
|
|
97
78
|
const [licenseOptions, setLicenseOptions] = React.useState([]);
|
|
98
79
|
const [showMessage, setShowMessage] = React.useState(showMessageProp);
|
|
80
|
+
const CONTENT_OPTIONS = [{
|
|
81
|
+
display: formatMessage('Choose usage rights...'),
|
|
82
|
+
value: 'choose'
|
|
83
|
+
}, {
|
|
84
|
+
display: formatMessage('I hold the copyright'),
|
|
85
|
+
value: 'own_copyright'
|
|
86
|
+
}, {
|
|
87
|
+
display: formatMessage('I have obtained permission to use this file.'),
|
|
88
|
+
value: 'used_by_permission'
|
|
89
|
+
}, {
|
|
90
|
+
display: formatMessage('The material is in the public domain'),
|
|
91
|
+
value: 'public_domain'
|
|
92
|
+
}, {
|
|
93
|
+
display: formatMessage('The material is subject to an exception - e.g. fair use, the right to quote, or others under applicable copyright laws'),
|
|
94
|
+
value: 'fair_use'
|
|
95
|
+
}, {
|
|
96
|
+
display: formatMessage('The material is licensed under Creative Commons'),
|
|
97
|
+
value: 'creative_commons'
|
|
98
|
+
}];
|
|
99
99
|
React.useEffect(() => {
|
|
100
100
|
function getUsageRightsOptions() {
|
|
101
101
|
fetch(apiUrl()).then(res => res.text()).then(res => setLicenseOptions(JSON.parse(res))).catch(() => {});
|
|
@@ -161,7 +161,7 @@ const UsageRightsSelectBox = _ref3 => {
|
|
|
161
161
|
UsageRightsSelectBox.propTypes = {
|
|
162
162
|
usageRightsState: PropTypes.shape({
|
|
163
163
|
ccLicense: PropTypes.string,
|
|
164
|
-
usageRight: PropTypes.oneOf(
|
|
164
|
+
usageRight: PropTypes.oneOf(['choose', 'own_copyright', 'used_by_permission', 'public_domain', 'fair_use', 'creative_commons']),
|
|
165
165
|
copyrightHolder: PropTypes.string
|
|
166
166
|
}),
|
|
167
167
|
setUsageRightsState: PropTypes.func,
|
|
@@ -0,0 +1,19 @@
|
|
|
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 { UploadFilePanelIds, handleSubmit, UploadFile } from './UploadFile';
|
|
19
|
+
export { UploadFilePanelIds, handleSubmit, UploadFile };
|
|
@@ -0,0 +1,70 @@
|
|
|
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 from 'react';
|
|
21
|
+
import formatMessage from '../../../../format-message';
|
|
22
|
+
import { Button, CloseButton } from '@instructure/ui-buttons';
|
|
23
|
+
import { Heading } from '@instructure/ui-heading';
|
|
24
|
+
import { Modal } from '@instructure/ui-modal';
|
|
25
|
+
|
|
26
|
+
const AIResponseModal = _ref => {
|
|
27
|
+
let {
|
|
28
|
+
open,
|
|
29
|
+
html,
|
|
30
|
+
onClose,
|
|
31
|
+
onInsert,
|
|
32
|
+
onReplace
|
|
33
|
+
} = _ref;
|
|
34
|
+
return /*#__PURE__*/React.createElement(Modal, {
|
|
35
|
+
open: open,
|
|
36
|
+
onDismiss: onClose,
|
|
37
|
+
size: "medium",
|
|
38
|
+
label: formatMessage('AI Response')
|
|
39
|
+
}, /*#__PURE__*/React.createElement(Modal.Header, null, /*#__PURE__*/React.createElement(CloseButton, {
|
|
40
|
+
onClick: onClose,
|
|
41
|
+
placement: "end",
|
|
42
|
+
offset: "medium",
|
|
43
|
+
screenReaderLabel: formatMessage('Close')
|
|
44
|
+
}), /*#__PURE__*/React.createElement(Heading, {
|
|
45
|
+
level: "h3"
|
|
46
|
+
}, "AI Response")), /*#__PURE__*/React.createElement(Modal.Body, null, /*#__PURE__*/React.createElement("div", {
|
|
47
|
+
dangerouslySetInnerHTML: {
|
|
48
|
+
__html: html
|
|
49
|
+
}
|
|
50
|
+
})), /*#__PURE__*/React.createElement(Modal.Footer, null, /*#__PURE__*/React.createElement(Button, {
|
|
51
|
+
onClick: onClose,
|
|
52
|
+
margin: "medium 0 0 0"
|
|
53
|
+
}, formatMessage('Close')), /*#__PURE__*/React.createElement(Button, {
|
|
54
|
+
onClick: onReplace,
|
|
55
|
+
margin: "medium 0 0 medium"
|
|
56
|
+
}, formatMessage('Replace')), /*#__PURE__*/React.createElement(Button, {
|
|
57
|
+
onClick: onInsert,
|
|
58
|
+
color: "primary",
|
|
59
|
+
margin: "medium 0 0 medium"
|
|
60
|
+
}, formatMessage('Insert'))));
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
AIResponseModal.propTypes = {
|
|
64
|
+
open: _pt.bool.isRequired,
|
|
65
|
+
html: _pt.string.isRequired,
|
|
66
|
+
onClose: _pt.func.isRequired,
|
|
67
|
+
onInsert: _pt.func.isRequired,
|
|
68
|
+
onReplace: _pt.func.isRequired
|
|
69
|
+
};
|
|
70
|
+
export { AIResponseModal };
|