@instructure/canvas-rce 7.3.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -0
- package/{es/rce/plugins/shared/ai_tools/index.js → __mocks__/@instructure/ui-media-player/_mockUiMediaPlayer.js} +4 -4
- package/__tests__/common/mimeClass.test.js +25 -1
- package/__tests__/rcs/api.test.js +280 -251
- package/es/canvasFileBrowser/FileBrowser.d.ts +2 -2
- package/es/canvasFileBrowser/FileBrowser.js +8 -7
- package/es/common/mimeClass.js +3 -1
- package/es/defaultTinymceConfig.js +47 -49
- package/es/enhance-user-content/doc_previews.js +5 -0
- package/es/enhance-user-content/enhance_user_content.js +6 -8
- package/es/enhance-user-content/index.d.ts +3 -1
- package/es/enhance-user-content/index.js +3 -1
- package/es/enhance-user-content/instructure_helper.js +1 -0
- package/es/enhance-user-content/youtube_overlay.js +18 -0
- package/es/getThemeVars.d.ts +1 -1
- package/es/getThemeVars.js +23 -26
- package/es/rce/AlertMessageArea.d.ts +2 -2
- package/es/rce/AlertMessageArea.js +3 -3
- package/es/rce/KeyboardShortcutModal.js +2 -2
- package/es/rce/RCE.d.ts +9 -0
- package/es/rce/RCE.js +4 -0
- package/es/rce/RCEGlobals.d.ts +2 -0
- package/es/rce/RCEGlobals.js +1 -0
- package/es/rce/RCEVariants.d.ts +1 -2
- package/es/rce/RCEVariants.js +1 -2
- package/es/rce/RCEWrapper.d.ts +16 -26
- package/es/rce/RCEWrapper.js +227 -271
- package/es/rce/RCEWrapper.utils.d.ts +1 -1
- package/es/rce/RCEWrapperProps.d.ts +2 -1
- package/es/rce/RCEWrapperProps.js +2 -1
- package/es/rce/StatusBar.d.ts +0 -1
- package/es/rce/StatusBar.js +3 -28
- package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedOnlySyntax.d.ts +2 -1
- package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedOnlySyntax.js +3 -1
- package/es/rce/plugins/instructure_equation/EquationEditorModal/index.d.ts +1 -0
- package/es/rce/plugins/instructure_equation/EquationEditorModal/index.js +12 -2
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageOptions.js +2 -2
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageSection.js +3 -3
- package/es/rce/plugins/instructure_icon_maker/svg/constants.d.ts +20 -5
- package/es/rce/plugins/instructure_icon_maker/svg/utils.d.ts +1 -1
- package/es/rce/plugins/instructure_icon_maker/utils/IconMakerFormHasChanges.js +2 -2
- package/es/rce/plugins/instructure_image/ImageEmbedOptions.d.ts +0 -2
- package/es/rce/plugins/instructure_image/ImageEmbedOptions.js +2 -9
- package/es/rce/plugins/instructure_paste/plugin.js +18 -12
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.d.ts +1 -1
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.d.ts +1 -1
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.js +25 -25
- package/es/rce/plugins/instructure_rce_external_tools/components/util/ToolLaunchIframe.d.ts +4 -0
- package/es/rce/plugins/instructure_rce_external_tools/components/util/ToolLaunchIframe.js +4 -0
- package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.d.ts +11 -2
- package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.js +92 -10
- package/es/rce/plugins/instructure_record/AudioOptionsTray/index.d.ts +13 -1
- package/es/rce/plugins/instructure_record/AudioOptionsTray/index.js +216 -24
- package/es/rce/plugins/instructure_record/MediaPanel/index.js +16 -5
- package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.d.ts +14 -13
- package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +110 -39
- package/es/rce/plugins/instructure_record/VideoOptionsTray/index.d.ts +11 -1
- package/es/rce/plugins/instructure_record/VideoOptionsTray/index.js +242 -67
- package/es/rce/plugins/instructure_record/clickCallback.js +19 -4
- package/es/rce/plugins/instructure_record/mediaTranslations.js +1 -1
- package/es/rce/plugins/instructure_record/playerLayoutOptions.d.ts +25 -0
- package/es/rce/plugins/instructure_record/playerLayoutOptions.js +91 -0
- package/es/rce/plugins/instructure_record/plugin.js +2 -5
- package/es/rce/plugins/instructure_record/utils.d.ts +3 -0
- package/es/rce/plugins/instructure_record/utils.js +31 -0
- package/es/rce/plugins/instructure_studio_media_options/plugin.js +82 -24
- package/es/rce/plugins/instructure_wordcount/components/WordCountModal.js +1 -0
- package/es/rce/plugins/shared/ContentSelection.d.ts +6 -1
- package/es/rce/plugins/shared/ContentSelection.js +15 -6
- package/es/rce/plugins/shared/DimensionsInput/DimensionInput.js +1 -2
- package/es/rce/plugins/shared/DimensionsInput/index.js +11 -12
- package/es/rce/plugins/shared/DimensionsInput/useDimensionsState.d.ts +1 -1
- package/es/rce/plugins/shared/DimensionsInput/useDimensionsState.js +4 -3
- package/es/rce/plugins/shared/StudioLtiSupportUtils.d.ts +27 -5
- package/es/rce/plugins/shared/StudioLtiSupportUtils.js +94 -9
- package/es/rce/plugins/shared/Upload/UploadFile.js +1 -8
- package/es/rce/style.d.ts +2 -1
- package/es/rce/style.js +4 -2
- package/es/rcs/api.d.ts +5 -10
- package/es/rcs/api.js +15 -21
- package/es/rcs/fake.d.ts +1 -7
- package/es/rcs/fake.js +1 -47
- package/es/sidebar/actions/media.d.ts +19 -6
- package/es/sidebar/actions/media.js +17 -4
- package/es/sidebar/actions/upload.d.ts +3 -3
- package/es/sidebar/actions/upload.js +9 -9
- package/es/sidebar/containers/Sidebar.js +0 -2
- package/es/sidebar/containers/sidebarHandlers.d.ts +2 -4
- package/es/sidebar/containers/sidebarHandlers.js +2 -5
- package/es/sidebar/reducers/index.d.ts +0 -1
- package/es/sidebar/reducers/index.js +0 -2
- package/es/sidebar/store/initialState.d.ts +0 -1
- package/es/sidebar/store/initialState.js +0 -5
- package/es/translations/locales/ar.js +77 -77
- package/es/translations/locales/ca.js +77 -77
- package/es/translations/locales/cy.js +77 -77
- package/es/translations/locales/da-x-k12.js +77 -77
- package/es/translations/locales/da.js +77 -77
- package/es/translations/locales/de.js +77 -77
- package/es/translations/locales/el.js +0 -9
- package/es/translations/locales/en-AU-x-unimelb.js +77 -77
- package/es/translations/locales/en-GB-x-ukhe.js +77 -77
- package/es/translations/locales/en.js +67 -79
- package/es/translations/locales/en_AU.js +77 -77
- package/es/translations/locales/en_CA.js +77 -77
- package/es/translations/locales/en_CY.js +77 -77
- package/es/translations/locales/en_GB.js +77 -77
- package/es/translations/locales/es.js +77 -77
- package/es/translations/locales/es_ES.js +77 -77
- package/es/translations/locales/fa_IR.js +0 -9
- package/es/translations/locales/fi.js +77 -77
- package/es/translations/locales/fr.js +77 -77
- package/es/translations/locales/fr_CA.js +77 -77
- package/es/translations/locales/ga.js +77 -77
- package/es/translations/locales/he.js +0 -9
- package/es/translations/locales/hi.js +77 -77
- package/es/translations/locales/ht.js +77 -77
- package/es/translations/locales/hu.js +0 -36
- package/es/translations/locales/hy.js +0 -9
- package/es/translations/locales/id.js +77 -77
- package/es/translations/locales/is.js +77 -77
- package/es/translations/locales/it.js +77 -77
- package/es/translations/locales/ja.js +77 -77
- package/es/translations/locales/ko.js +2455 -133
- package/es/translations/locales/mi.js +77 -77
- package/es/translations/locales/ms.js +77 -77
- package/es/translations/locales/nb-x-k12.js +77 -77
- package/es/translations/locales/nb.js +77 -77
- package/es/translations/locales/nl.js +78 -78
- package/es/translations/locales/nn.js +0 -36
- package/es/translations/locales/pl.js +77 -77
- package/es/translations/locales/pt.js +77 -77
- package/es/translations/locales/pt_BR.js +77 -77
- package/es/translations/locales/ru.js +77 -77
- package/es/translations/locales/sl.js +77 -77
- package/es/translations/locales/sv-x-k12.js +77 -77
- package/es/translations/locales/sv.js +77 -77
- package/es/translations/locales/th.js +77 -77
- package/es/translations/locales/tr.js +1962 -18
- package/es/translations/locales/uk_UA.js +0 -9
- package/es/translations/locales/vi.js +77 -77
- package/es/translations/locales/zh-Hans.js +77 -77
- package/es/translations/locales/zh-Hant.js +77 -77
- package/es/translations/locales/zh.js +77 -77
- package/es/translations/locales/zh_HK.js +77 -77
- package/eslint.config.js +16 -147
- package/jest/jest-setup.js +1 -0
- package/jest.config.js +2 -0
- package/oxlint.json +84 -0
- package/package.json +86 -62
- package/tsconfig.json +3 -2
- package/es/rce/plugins/shared/ai_tools/AIResponseModal.d.ts +0 -10
- package/es/rce/plugins/shared/ai_tools/AIResponseModal.js +0 -67
- package/es/rce/plugins/shared/ai_tools/AIToolsTray.d.ts +0 -18
- package/es/rce/plugins/shared/ai_tools/AIToolsTray.js +0 -489
- package/es/rce/plugins/shared/ai_tools/aiicons.d.ts +0 -7
- package/es/rce/plugins/shared/ai_tools/aiicons.js +0 -60
- package/es/rce/plugins/shared/ai_tools/index.d.ts +0 -3
- package/es/sidebar/actions/flickr.d.ts +0 -20
- package/es/sidebar/actions/flickr.js +0 -60
- package/es/sidebar/reducers/flickr.d.ts +0 -1
- package/es/sidebar/reducers/flickr.js +0 -49
|
@@ -21,4 +21,4 @@ export declare function focusToolbar(el: HTMLElement): void;
|
|
|
21
21
|
export declare function focusFirstMenuButton(el: HTMLElement): void;
|
|
22
22
|
export declare function isElementWithinTable(node: Element | null): boolean;
|
|
23
23
|
export declare function parsePluginsToExclude(plugins: string[]): string[];
|
|
24
|
-
export declare function patchAutosavedContent(content: string, asText?: boolean): string
|
|
24
|
+
export declare function patchAutosavedContent(content: string, asText?: boolean): string;
|
|
@@ -154,6 +154,8 @@ export declare const rceWrapperPropTypes: {
|
|
|
154
154
|
}>;
|
|
155
155
|
instRecordDisabled: PropTypes.Requireable<boolean>;
|
|
156
156
|
highContrastCSS: PropTypes.Requireable<(string | null | undefined)[]>;
|
|
157
|
+
useHighContrast: PropTypes.Requireable<boolean>;
|
|
158
|
+
fontFamily: PropTypes.Requireable<string>;
|
|
157
159
|
maxInitRenderedRCEs: PropTypes.Requireable<number>;
|
|
158
160
|
use_rce_icon_maker: PropTypes.Requireable<boolean>;
|
|
159
161
|
features: PropTypes.Requireable<{
|
|
@@ -169,7 +171,6 @@ export declare const rceWrapperPropTypes: {
|
|
|
169
171
|
isA2StudentView: PropTypes.Requireable<boolean>;
|
|
170
172
|
maxMruTools: PropTypes.Requireable<number>;
|
|
171
173
|
}>>;
|
|
172
|
-
ai_text_tools: PropTypes.Requireable<boolean>;
|
|
173
174
|
variant: PropTypes.Requireable<"full" | "lite" | "text-only" | "text-block" | "block-content-editor">;
|
|
174
175
|
};
|
|
175
176
|
export type RCEWrapperProps = PropTypes.InferProps<typeof rceWrapperPropTypes>;
|
|
@@ -126,6 +126,8 @@ export const rceWrapperPropTypes = {
|
|
|
126
126
|
menu: menuPropType,
|
|
127
127
|
instRecordDisabled: PropTypes.bool,
|
|
128
128
|
highContrastCSS: PropTypes.arrayOf(PropTypes.string),
|
|
129
|
+
useHighContrast: PropTypes.bool,
|
|
130
|
+
fontFamily: PropTypes.string,
|
|
129
131
|
maxInitRenderedRCEs: PropTypes.number,
|
|
130
132
|
use_rce_icon_maker: PropTypes.bool,
|
|
131
133
|
features: PropTypes.objectOf(PropTypes.bool),
|
|
@@ -133,6 +135,5 @@ export const rceWrapperPropTypes = {
|
|
|
133
135
|
timezone: PropTypes.string,
|
|
134
136
|
userCacheKey: PropTypes.string,
|
|
135
137
|
externalToolsConfig: externalToolsConfigPropType,
|
|
136
|
-
ai_text_tools: PropTypes.bool,
|
|
137
138
|
variant: PropTypes.oneOf(RCEVariantValues)
|
|
138
139
|
};
|
package/es/rce/StatusBar.d.ts
CHANGED
|
@@ -18,7 +18,6 @@ declare namespace StatusBar {
|
|
|
18
18
|
export let onWordcountModalOpen: import("prop-types").Validator<(...args: any[]) => any>;
|
|
19
19
|
export let disabledPlugins: import("prop-types").Requireable<(string | null | undefined)[]>;
|
|
20
20
|
export let features: import("prop-types").Requireable<(string | null | undefined)[]>;
|
|
21
|
-
export { func as onAI };
|
|
22
21
|
}
|
|
23
22
|
namespace defaultProps {
|
|
24
23
|
export let a11yBadgeColor: string;
|
package/es/rce/StatusBar.js
CHANGED
|
@@ -32,7 +32,6 @@ import { IconA11yLine, IconKeyboardShortcutsLine, IconMiniArrowEndLine, IconFull
|
|
|
32
32
|
import formatMessage from '../format-message';
|
|
33
33
|
import ResizeHandle from './ResizeHandle';
|
|
34
34
|
import { FS_ENABLED } from '../util/fullscreenHelpers';
|
|
35
|
-
import { AIWandSVG } from './plugins/shared/ai_tools';
|
|
36
35
|
export const WYSIWYG_VIEW = 'WYSIWYG';
|
|
37
36
|
export const PRETTY_HTML_EDITOR_VIEW = 'PRETTY';
|
|
38
37
|
export const RAW_HTML_EDITOR_VIEW = 'RAW';
|
|
@@ -57,9 +56,7 @@ StatusBar.propTypes = {
|
|
|
57
56
|
a11yErrorsCount: number,
|
|
58
57
|
onWordcountModalOpen: func.isRequired,
|
|
59
58
|
disabledPlugins: arrayOf(string),
|
|
60
|
-
features: arrayOf(string)
|
|
61
|
-
// StatusBarFeature[]
|
|
62
|
-
onAI: func
|
|
59
|
+
features: arrayOf(string) // StatusBarFeature[]
|
|
63
60
|
};
|
|
64
61
|
StatusBar.defaultProps = {
|
|
65
62
|
a11yBadgeColor: '#2B7ABC',
|
|
@@ -218,36 +215,14 @@ export default function StatusBar(props) {
|
|
|
218
215
|
}
|
|
219
216
|
function renderIconButtons() {
|
|
220
217
|
if (isHtmlView()) return null;
|
|
221
|
-
const ai_tools = isFeature('ai_tools');
|
|
222
218
|
const kb_shortcuts = isFeature('keyboard_shortcuts');
|
|
223
219
|
const a11y_checker = isFeature('a11y_checker');
|
|
224
|
-
if (!(
|
|
220
|
+
if (!(kb_shortcuts || a11y_checker)) return null;
|
|
225
221
|
const kbshortcut = formatMessage('View keyboard shortcuts');
|
|
226
222
|
return /*#__PURE__*/React.createElement(View, {
|
|
227
223
|
display: "inline-block",
|
|
228
224
|
padding: "0 x-small"
|
|
229
|
-
},
|
|
230
|
-
"data-btn-id": "rce-ai-btn",
|
|
231
|
-
color: "secondary",
|
|
232
|
-
"aria-haspopup": "dialog",
|
|
233
|
-
title: formatMessage('AI Tools'),
|
|
234
|
-
tabIndex: tabIndexForBtn('rce-ai-btn'),
|
|
235
|
-
onClick: event => {
|
|
236
|
-
event.target.focus(); // FF doesn't focus buttons on click
|
|
237
|
-
props.onAI();
|
|
238
|
-
},
|
|
239
|
-
onFocus: () => setFocusedBtnId('rce-ai-btn'),
|
|
240
|
-
screenReaderLabel: formatMessage('AI Tools'),
|
|
241
|
-
withBackground: false,
|
|
242
|
-
withBorder: false
|
|
243
|
-
}, /*#__PURE__*/React.createElement("span", {
|
|
244
|
-
style: {
|
|
245
|
-
color: 'dodgerBlue'
|
|
246
|
-
}
|
|
247
|
-
}, /*#__PURE__*/React.createElement(SVGIcon, {
|
|
248
|
-
src: AIWandSVG,
|
|
249
|
-
size: "x-small"
|
|
250
|
-
}))), kb_shortcuts && /*#__PURE__*/React.createElement(IconButton, {
|
|
225
|
+
}, kb_shortcuts && /*#__PURE__*/React.createElement(IconButton, {
|
|
251
226
|
"data-btn-id": "rce-kbshortcut-btn",
|
|
252
227
|
color: "secondary",
|
|
253
228
|
"aria-haspopup": "dialog",
|
|
@@ -16,12 +16,14 @@
|
|
|
16
16
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { memoize } from '
|
|
19
|
+
import { memoize } from 'es-toolkit/compat';
|
|
20
20
|
|
|
21
21
|
// These commands all work fine with MathJax but either don't work, don't work well
|
|
22
22
|
// (bad UX for editing), or look strange when rendered by Mathlive. Add new ones
|
|
23
23
|
// here if you discover anything else or customers report unexpected experiences.
|
|
24
24
|
const advancedOnlyCommands = ['begin', 'end', 'cases', 'cr', 'rm', 'text', 'hbox', 'mbox', 'unicode', 'cal', 'frak', 'it', 'scr', 'sf', '#', 'def', 'newcommand', 'operatorname', 'DeclareMathOperator', 'displaystyle', 'textstyle', 'scriptstyle', 'scriptscriptstyle', 'displaylines', 'abovewithdelims', 'array', 'bmatrix', 'buildrel', 'ddddot', 'dddot', 'eqalign', 'eqalignno', 'gcd', 'genfrac', 'hdashline', 'hfil', 'hfill', 'hfilll', 'hline', 'idotsint', 'iiiint', 'injlim', 'kern', 'label', 'LaTeX', 'leftroot', 'lgroup', 'lower', 'mathchoice', 'mathfrak', 'matrix', 'mit', 'mkern', 'mspace', 'negmedspace', 'negthickspace', 'negthinspace', 'newline', 'nobreakspace', 'oldstyle', 'overset', 'pmatrix', 'raise', 'rgroup', 'rule', 'Rule', 'skew', 'space', 'tag', 'TeX', 'underbrace', 'uproot', 'varinjlim', 'varliminf', 'varlimsup', 'varprojlim', 'vcenter', 'vmatrix'];
|
|
25
25
|
const advancedOnlyRegex = new RegExp(advancedOnlyCommands.join('|'), 'm');
|
|
26
|
+
|
|
27
|
+
/** @type {(latex: string) => boolean} */
|
|
26
28
|
const containsAdvancedSyntax = memoize(latex => advancedOnlyRegex.test(latex));
|
|
27
29
|
export { advancedOnlyCommands, containsAdvancedSyntax };
|
|
@@ -11,6 +11,7 @@ declare class EquationEditorModal extends React.Component<any, any, any> {
|
|
|
11
11
|
insertNewRange(): void;
|
|
12
12
|
advancedModeOnly(latex: any): boolean;
|
|
13
13
|
executeCommand: (cmd: any, advancedCmd: any) => void;
|
|
14
|
+
clearMathFieldBeforeDismiss: () => void;
|
|
14
15
|
handleModalCancel: () => void;
|
|
15
16
|
handleModalDone: () => void;
|
|
16
17
|
renderMathInAdvancedPreview: import("@instructure/debounce").Debounced<() => void>;
|
|
@@ -68,7 +68,16 @@ export default class EquationEditorModal extends Component {
|
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
|
+
// Clear the math-field before the modal unmounts to prevent XSS.
|
|
72
|
+
// Mathlive's dispose method sets element.innerHTML = this.model.getValue(),
|
|
73
|
+
// which would parse any HTML in the formula as real DOM elements.
|
|
74
|
+
this.clearMathFieldBeforeDismiss = () => {
|
|
75
|
+
if (this.mathField) {
|
|
76
|
+
this.setMathField('');
|
|
77
|
+
}
|
|
78
|
+
};
|
|
71
79
|
this.handleModalCancel = () => {
|
|
80
|
+
this.clearMathFieldBeforeDismiss();
|
|
72
81
|
this.props.onModalDismiss();
|
|
73
82
|
};
|
|
74
83
|
this.handleModalDone = () => {
|
|
@@ -80,11 +89,12 @@ export default class EquationEditorModal extends Component {
|
|
|
80
89
|
if (output) {
|
|
81
90
|
onEquationSubmit(output);
|
|
82
91
|
}
|
|
92
|
+
this.clearMathFieldBeforeDismiss();
|
|
83
93
|
onModalDismiss();
|
|
84
94
|
};
|
|
85
95
|
this.renderMathInAdvancedPreview = debounce(() => {
|
|
86
96
|
if (this.previewElement.current) {
|
|
87
|
-
this.previewElement.current.
|
|
97
|
+
this.previewElement.current.textContent = String.raw`\(${this.state.workingFormula}\)`;
|
|
88
98
|
this.mathml.processNewMathInElem(this.previewElement.current);
|
|
89
99
|
}
|
|
90
100
|
}, EquationEditorModal.debounceRate, {
|
|
@@ -257,7 +267,7 @@ export default class EquationEditorModal extends Component {
|
|
|
257
267
|
if (this.state.workingFormula) {
|
|
258
268
|
this.renderMathInAdvancedPreview();
|
|
259
269
|
} else {
|
|
260
|
-
this.previewElement.current.
|
|
270
|
+
this.previewElement.current.textContent = '';
|
|
261
271
|
}
|
|
262
272
|
}
|
|
263
273
|
componentDidMount() {
|
|
@@ -35,7 +35,7 @@ import { MAX_IMAGE_SIZE_BYTES } from '../../../../shared/compressionUtils';
|
|
|
35
35
|
import { createCroppedImageSvg } from '../../../../shared/ImageCropper/imageCropUtils';
|
|
36
36
|
import { convertFileToBase64 } from '../../../../shared/fileUtils';
|
|
37
37
|
import { ImageSettingsPropTypes } from './propTypes';
|
|
38
|
-
import
|
|
38
|
+
import { isEqual } from 'es-toolkit/compat';
|
|
39
39
|
const getCompressionMessage = () => formatMessage('Your image has been compressed for Icon Maker. Images less than {size} KB will not be compressed.', {
|
|
40
40
|
size: MAX_IMAGE_SIZE_BYTES / 1024
|
|
41
41
|
});
|
|
@@ -99,7 +99,7 @@ export const ImageOptions = ({
|
|
|
99
99
|
|
|
100
100
|
// After submitting cropper modal a new embedded image should be generated
|
|
101
101
|
useEffect(() => {
|
|
102
|
-
if (state.cropperSettings && settings.imageSettings && !
|
|
102
|
+
if (state.cropperSettings && settings.imageSettings && !isEqual(state.cropperSettings, settings.imageSettings?.cropperSettings)) {
|
|
103
103
|
if (state.cropperSettings.shape !== settings.shape) {
|
|
104
104
|
trayDispatch({
|
|
105
105
|
shape: state.cropperSettings.shape
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import React, { useReducer, useEffect, useRef, Suspense } from 'react';
|
|
20
|
-
import
|
|
20
|
+
import { isEqual } from 'es-toolkit/compat';
|
|
21
21
|
import PropTypes from 'prop-types';
|
|
22
22
|
import formatMessage from '../../../../../../format-message';
|
|
23
23
|
import reducer, { actions, initialState, modes } from '../../../reducers/imageSection';
|
|
@@ -161,7 +161,7 @@ export const ImageSection = ({
|
|
|
161
161
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
162
162
|
}, [settings.shape]);
|
|
163
163
|
useEffect(() => {
|
|
164
|
-
if (settings.imageSettings && !isMetadataLoaded.current && !
|
|
164
|
+
if (settings.imageSettings && !isMetadataLoaded.current && !isEqual(settings.imageSettings, metadata)) {
|
|
165
165
|
isMetadataLoaded.current = true;
|
|
166
166
|
dispatch({
|
|
167
167
|
type: actions.UPDATE_SETTINGS.type,
|
|
@@ -171,7 +171,7 @@ export const ImageSection = ({
|
|
|
171
171
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
172
172
|
}, [settings.imageSettings]);
|
|
173
173
|
useEffect(() => {
|
|
174
|
-
if (!
|
|
174
|
+
if (!isEqual(metadata, settings.imageSettings)) {
|
|
175
175
|
onChange({
|
|
176
176
|
type: svgActions.SET_IMAGE_SETTINGS,
|
|
177
177
|
payload: metadata
|
|
@@ -47,19 +47,34 @@ export namespace DEFAULT_SETTINGS {
|
|
|
47
47
|
export let error: null;
|
|
48
48
|
}
|
|
49
49
|
export const BASE_SIZE: {
|
|
50
|
-
[
|
|
50
|
+
[Size.ExtraSmall]: number;
|
|
51
|
+
[Size.Small]: number;
|
|
52
|
+
[Size.Medium]: number;
|
|
53
|
+
[Size.Large]: number;
|
|
51
54
|
};
|
|
52
55
|
export const STROKE_WIDTH: {
|
|
53
|
-
[
|
|
56
|
+
[Size.None]: number;
|
|
57
|
+
[Size.Small]: number;
|
|
58
|
+
[Size.Medium]: number;
|
|
59
|
+
[Size.Large]: number;
|
|
54
60
|
};
|
|
55
61
|
export const TEXT_SIZE: {
|
|
56
|
-
[
|
|
62
|
+
[Size.Small]: number;
|
|
63
|
+
[Size.Medium]: number;
|
|
64
|
+
[Size.Large]: number;
|
|
65
|
+
[Size.ExtraLarge]: number;
|
|
57
66
|
};
|
|
58
67
|
export const TEXT_SIZE_FONT_DIFF: {
|
|
59
|
-
[
|
|
68
|
+
[Size.Small]: number;
|
|
69
|
+
[Size.Medium]: number;
|
|
70
|
+
[Size.Large]: number;
|
|
71
|
+
[Size.ExtraLarge]: number;
|
|
60
72
|
};
|
|
61
73
|
export const MAX_CHAR_COUNT: {
|
|
62
|
-
[
|
|
74
|
+
[Size.Small]: number;
|
|
75
|
+
[Size.Medium]: number;
|
|
76
|
+
[Size.Large]: number;
|
|
77
|
+
[Size.ExtraLarge]: number;
|
|
63
78
|
};
|
|
64
79
|
export const MAX_TOTAL_TEXT_CHARS: 32;
|
|
65
80
|
export const TEXT_BACKGROUND_PADDING: 4;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import
|
|
19
|
+
import { isEqual } from 'es-toolkit/compat';
|
|
20
20
|
export class IconMakerFormHasChanges {
|
|
21
21
|
constructor(initSettings, currSettings) {
|
|
22
22
|
this.initialSettings = void 0;
|
|
@@ -61,7 +61,7 @@ export class IconMakerFormHasChanges {
|
|
|
61
61
|
return 'textPosition' in this.currentSettings && this.initialSettings.textPosition !== this.currentSettings.textPosition;
|
|
62
62
|
}
|
|
63
63
|
hasImageSettingsChange() {
|
|
64
|
-
return 'imageSettings' in this.currentSettings && !
|
|
64
|
+
return 'imageSettings' in this.currentSettings && !isEqual(this.initialSettings.imageSettings, this.currentSettings.imageSettings);
|
|
65
65
|
}
|
|
66
66
|
hasChanges() {
|
|
67
67
|
return this.hasNameChange() || this.hasAltChange() || this.hasShapeNameChange() || this.hasShapeSizeChange() || this.hasColorNameChange() || this.hasOutlineColorChange() || this.hasOutlineSizeChange() || this.hasTextChange() || this.hasTextSizeChange() || this.hasTextColorChange() || this.hasTextBackgroundColorChange() || this.hasTextPositionChange() || this.hasImageSettingsChange();
|
|
@@ -42,7 +42,6 @@ export function scaleVideoSize(videoSize: any, naturalWidth: any, naturalHeight:
|
|
|
42
42
|
export function labelForImageSize(imageSize: any): string;
|
|
43
43
|
export const MIN_HEIGHT: 10;
|
|
44
44
|
export const MIN_WIDTH: 10;
|
|
45
|
-
export const MIN_WIDTH_VIDEO: 320;
|
|
46
45
|
export const MIN_PERCENTAGE: 10;
|
|
47
46
|
export const SMALL: "small";
|
|
48
47
|
export const MEDIUM: "medium";
|
|
@@ -50,7 +49,6 @@ export const LARGE: "large";
|
|
|
50
49
|
export const EXTRA_LARGE: "extra-large";
|
|
51
50
|
export const CUSTOM: "custom";
|
|
52
51
|
export const imageSizes: string[];
|
|
53
|
-
export const videoSizes: string[];
|
|
54
52
|
export const studioPlayerSizes: string[];
|
|
55
53
|
export const MIN_WIDTH_STUDIO_PLAYER: number;
|
|
56
54
|
export const MIN_HEIGHT_STUDIO_PLAYER: number;
|
|
@@ -18,10 +18,8 @@
|
|
|
18
18
|
|
|
19
19
|
import formatMessage from '../../../format-message';
|
|
20
20
|
import { scaleForHeight, scaleForWidth } from '../shared/DimensionUtils';
|
|
21
|
-
import RCEGlobals from '../../../rce/RCEGlobals';
|
|
22
21
|
export const MIN_HEIGHT = 10;
|
|
23
22
|
export const MIN_WIDTH = 10;
|
|
24
|
-
export const MIN_WIDTH_VIDEO = 320;
|
|
25
23
|
export const MIN_PERCENTAGE = 10;
|
|
26
24
|
export const SMALL = 'small';
|
|
27
25
|
export const MEDIUM = 'medium';
|
|
@@ -29,7 +27,6 @@ export const LARGE = 'large';
|
|
|
29
27
|
export const EXTRA_LARGE = 'extra-large';
|
|
30
28
|
export const CUSTOM = 'custom';
|
|
31
29
|
export const imageSizes = [SMALL, MEDIUM, LARGE, EXTRA_LARGE, CUSTOM];
|
|
32
|
-
export const videoSizes = [MEDIUM, LARGE, EXTRA_LARGE, CUSTOM];
|
|
33
30
|
export const studioPlayerSizes = [SMALL, MEDIUM, LARGE, CUSTOM];
|
|
34
31
|
const sizeByMaximumDimension = {
|
|
35
32
|
200: SMALL,
|
|
@@ -137,12 +134,8 @@ export function fromVideoEmbed($element) {
|
|
|
137
134
|
} catch (_ignore) {
|
|
138
135
|
// bad json?
|
|
139
136
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
videoOptions.videoSize = [SMALL, MEDIUM, LARGE].find(size => width === studioPlayerDimensions[size].width) || CUSTOM;
|
|
143
|
-
} else {
|
|
144
|
-
videoOptions.videoSize = imageSizeFromKnownOptions(videoOptions);
|
|
145
|
-
}
|
|
137
|
+
const width = videoOptions.appliedWidth || videoOptions.naturalWidth;
|
|
138
|
+
videoOptions.videoSize = [SMALL, MEDIUM, LARGE].find(size => width === studioPlayerDimensions[size].width) || CUSTOM;
|
|
146
139
|
const source = $videoIframe.getAttribute('src');
|
|
147
140
|
const matches = source?.match(/\/media_attachments_iframe\/(\d+)/);
|
|
148
141
|
if (matches) {
|
|
@@ -16,16 +16,19 @@
|
|
|
16
16
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { trackPendoEvent } from '@instructure/canvas-media';
|
|
19
20
|
import tinymce from 'tinymce';
|
|
20
21
|
import bridge from '../../../bridge';
|
|
21
|
-
import
|
|
22
|
+
import { showFlashAlert } from '../../../common/FlashAlert';
|
|
23
|
+
import formatMessage from '../../../format-message';
|
|
22
24
|
import { get as getSession } from '../../../sidebar/actions/session';
|
|
23
25
|
import { uploadToMediaFolder } from '../../../sidebar/actions/upload';
|
|
24
|
-
import
|
|
25
|
-
import
|
|
26
|
-
import { isAudioOrVideo, isImage } from '../shared/fileTypeUtils';
|
|
27
|
-
import { showFlashAlert } from '../../../common/FlashAlert';
|
|
26
|
+
import configureStore from '../../../sidebar/store/configureStore';
|
|
27
|
+
import RCEGlobals from '../../RCEGlobals';
|
|
28
28
|
import { isMicrosoftWordContentInEvent } from '../shared/EventUtils';
|
|
29
|
+
import { isAudioOrVideo } from '../shared/fileTypeUtils';
|
|
30
|
+
import doFileUpload from '../shared/Upload/doFileUpload';
|
|
31
|
+
|
|
29
32
|
// assume that if there are multiple RCEs on the page,
|
|
30
33
|
// they all talk to the same canvas
|
|
31
34
|
const config = {
|
|
@@ -110,13 +113,7 @@ tinymce.PluginManager.add('instructure_paste', function (editor) {
|
|
|
110
113
|
size: file.size,
|
|
111
114
|
domObject: file
|
|
112
115
|
};
|
|
113
|
-
|
|
114
|
-
if (isImage(file.type)) {
|
|
115
|
-
tabContext = 'images';
|
|
116
|
-
} else if (isAudioOrVideo(file.type)) {
|
|
117
|
-
tabContext = 'media';
|
|
118
|
-
}
|
|
119
|
-
store.dispatch(uploadToMediaFolder(tabContext, fileMetaProps));
|
|
116
|
+
store.dispatch(uploadToMediaFolder(fileMetaProps));
|
|
120
117
|
return 'submitted';
|
|
121
118
|
}
|
|
122
119
|
}
|
|
@@ -158,6 +155,15 @@ tinymce.PluginManager.add('instructure_paste', function (editor) {
|
|
|
158
155
|
// Skip audio and video files when disabled
|
|
159
156
|
continue;
|
|
160
157
|
}
|
|
158
|
+
if (isAudioOrVideo(file.type) && RCEGlobals.getFeatures()?.rce_asr_captioning_improvements) {
|
|
159
|
+
const trayProps = bridge.trayProps.get(editor);
|
|
160
|
+
trackPendoEvent('canvas_native_media_embedded', {
|
|
161
|
+
insertion_method: isPaste ? 'paste_file' : 'drag_drop',
|
|
162
|
+
media_kind: file.type.startsWith('audio/') ? 'audio' : 'video',
|
|
163
|
+
resourceType: trayProps?.contextType,
|
|
164
|
+
resourceId: trayProps?.contextId
|
|
165
|
+
});
|
|
166
|
+
}
|
|
161
167
|
|
|
162
168
|
// This will finish once the dialog is closed, if one was created, putting this in a loop allows us
|
|
163
169
|
// to show a dialog for each file without them conflicting.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { ModalProps } from '@instructure/ui-modal
|
|
2
|
+
import { type ModalProps } from '@instructure/ui-modal';
|
|
3
3
|
export declare function ExternalToolDialogModal(props: Pick<ModalProps, 'label' | 'open' | 'onOpen' | 'onClose' | 'mountNode'> & {
|
|
4
4
|
onCloseButton: () => void;
|
|
5
5
|
name: string;
|
|
@@ -17,22 +17,22 @@ import _pt from "prop-types";
|
|
|
17
17
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import React, { useState } from
|
|
21
|
-
import { Modal } from
|
|
22
|
-
import { Button, CloseButton } from
|
|
23
|
-
import { Heading } from
|
|
24
|
-
import { View } from
|
|
25
|
-
import { ScreenReaderContent } from
|
|
26
|
-
import { List } from
|
|
27
|
-
import { Flex } from
|
|
28
|
-
import { TextInput } from
|
|
29
|
-
import { IconSearchLine } from
|
|
30
|
-
import { Alert } from
|
|
31
|
-
import formatMessage from
|
|
32
|
-
import ExternalToolSelectionItem from
|
|
33
|
-
import { instuiPopupMountNodeFn } from
|
|
20
|
+
import React, { useState } from 'react';
|
|
21
|
+
import { Modal } from '@instructure/ui-modal';
|
|
22
|
+
import { Button, CloseButton } from '@instructure/ui-buttons';
|
|
23
|
+
import { Heading } from '@instructure/ui-heading';
|
|
24
|
+
import { View } from '@instructure/ui-view';
|
|
25
|
+
import { ScreenReaderContent } from '@instructure/ui-a11y-content';
|
|
26
|
+
import { List } from '@instructure/ui-list';
|
|
27
|
+
import { Flex } from '@instructure/ui-flex';
|
|
28
|
+
import { TextInput } from '@instructure/ui-text-input';
|
|
29
|
+
import { IconSearchLine } from '@instructure/ui-icons';
|
|
30
|
+
import { Alert } from '@instructure/ui-alerts';
|
|
31
|
+
import formatMessage from '../../../../../format-message';
|
|
32
|
+
import ExternalToolSelectionItem from './ExternalToolSelectionItem';
|
|
33
|
+
import { instuiPopupMountNodeFn } from '../../../../../util/fullscreenHelpers';
|
|
34
34
|
// TODO: we really need a way for the client to pass this to the RCE
|
|
35
|
-
const getLiveRegion = () => document.getElementById(
|
|
35
|
+
const getLiveRegion = () => document.getElementById('flash_screenreader_holder');
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Returns a filtered list of items based on the term.
|
|
@@ -51,7 +51,7 @@ export function filterItemsByTitleSubstring(searchString, items) {
|
|
|
51
51
|
return items.filter(item => item.title.toLocaleLowerCase().includes(lowerTerm));
|
|
52
52
|
}
|
|
53
53
|
export function ExternalToolSelectionDialog(props) {
|
|
54
|
-
const [filterTerm, setFilterTerm] = useState(
|
|
54
|
+
const [filterTerm, setFilterTerm] = useState('');
|
|
55
55
|
const [filteredResults, setFilteredResults] = useState(props.ltiButtons);
|
|
56
56
|
const handleFilterChange = e => {
|
|
57
57
|
setFilterTerm(e.target?.value);
|
|
@@ -63,31 +63,31 @@ export function ExternalToolSelectionDialog(props) {
|
|
|
63
63
|
liveRegion: getLiveRegion,
|
|
64
64
|
size: "medium",
|
|
65
65
|
themeOverride: {
|
|
66
|
-
mediumMaxWidth:
|
|
66
|
+
mediumMaxWidth: '42rem'
|
|
67
67
|
},
|
|
68
|
-
label: formatMessage(
|
|
68
|
+
label: formatMessage('Apps'),
|
|
69
69
|
mountNode: instuiPopupMountNodeFn(),
|
|
70
70
|
onDismiss: props.onDismiss,
|
|
71
71
|
open: true,
|
|
72
72
|
shouldCloseOnDocumentClick: false
|
|
73
73
|
}, /*#__PURE__*/React.createElement(Modal.Header, {
|
|
74
74
|
themeOverride: {
|
|
75
|
-
padding:
|
|
75
|
+
padding: '0.5rem'
|
|
76
76
|
}
|
|
77
77
|
}, /*#__PURE__*/React.createElement(CloseButton, {
|
|
78
78
|
placement: "end",
|
|
79
79
|
offset: "medium",
|
|
80
80
|
onClick: props.onDismiss,
|
|
81
|
-
screenReaderLabel: formatMessage(
|
|
81
|
+
screenReaderLabel: formatMessage('Close')
|
|
82
82
|
}), /*#__PURE__*/React.createElement(Heading, {
|
|
83
83
|
margin: "small"
|
|
84
|
-
}, formatMessage(
|
|
84
|
+
}, formatMessage('All Apps')), /*#__PURE__*/React.createElement(View, {
|
|
85
85
|
as: "div",
|
|
86
86
|
padding: "x-small none x-small medium"
|
|
87
87
|
}, /*#__PURE__*/React.createElement(TextInput, {
|
|
88
88
|
type: "search",
|
|
89
|
-
renderLabel: /*#__PURE__*/React.createElement(ScreenReaderContent, null, formatMessage(
|
|
90
|
-
placeholder: formatMessage(
|
|
89
|
+
renderLabel: /*#__PURE__*/React.createElement(ScreenReaderContent, null, formatMessage('Search')),
|
|
90
|
+
placeholder: formatMessage('Search'),
|
|
91
91
|
renderAfterInput: /*#__PURE__*/React.createElement(IconSearchLine, {
|
|
92
92
|
inline: false
|
|
93
93
|
}),
|
|
@@ -105,7 +105,7 @@ export function ExternalToolSelectionDialog(props) {
|
|
|
105
105
|
liveRegion: getLiveRegion,
|
|
106
106
|
variant: "info",
|
|
107
107
|
screenReaderOnly: !filterEmpty
|
|
108
|
-
}, filterEmpty && formatMessage(
|
|
108
|
+
}, filterEmpty && formatMessage('No results found for {filterTerm}', {
|
|
109
109
|
filterTerm
|
|
110
110
|
}), !filterEmpty && formatMessage(`Found { count, plural,
|
|
111
111
|
=0 {# results}
|
|
@@ -116,7 +116,7 @@ export function ExternalToolSelectionDialog(props) {
|
|
|
116
116
|
})), renderTools(filteredResults)))), /*#__PURE__*/React.createElement(Modal.Footer, null, /*#__PURE__*/React.createElement(Button, {
|
|
117
117
|
onClick: props.onDismiss,
|
|
118
118
|
color: "primary"
|
|
119
|
-
}, formatMessage(
|
|
119
|
+
}, formatMessage('Done'))));
|
|
120
120
|
function renderTools(ltiButtons) {
|
|
121
121
|
return /*#__PURE__*/React.createElement(List, {
|
|
122
122
|
isUnstyled: true
|
|
@@ -3,6 +3,10 @@ import React from 'react';
|
|
|
3
3
|
* Provide an iframe for launching an LTI tool directly from the frontend.
|
|
4
4
|
* Works just like all existing usages of the LTI <iframe> element, including
|
|
5
5
|
* extracting a ref of the <iframe> directly and setting things on it later.
|
|
6
|
+
*
|
|
7
|
+
* TODO: consider moving allow stuff here instead of having it be the
|
|
8
|
+
* responsibility of the caller, need to check if RCE uses same
|
|
9
|
+
* iframeAllowances() as elsewhere
|
|
6
10
|
*/
|
|
7
11
|
declare const ToolLaunchIframe: React.ForwardRefExoticComponent<React.IframeHTMLAttributes<HTMLIFrameElement> & {
|
|
8
12
|
children?: React.ReactNode | undefined;
|
|
@@ -23,6 +23,10 @@ import formatMessage from '../../../../../format-message';
|
|
|
23
23
|
* Provide an iframe for launching an LTI tool directly from the frontend.
|
|
24
24
|
* Works just like all existing usages of the LTI <iframe> element, including
|
|
25
25
|
* extracting a ref of the <iframe> directly and setting things on it later.
|
|
26
|
+
*
|
|
27
|
+
* TODO: consider moving allow stuff here instead of having it be the
|
|
28
|
+
* responsibility of the caller, need to check if RCE uses same
|
|
29
|
+
* iframeAllowances() as elsewhere
|
|
26
30
|
*/
|
|
27
31
|
const ToolLaunchIframe = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
28
32
|
return /*#__PURE__*/React.createElement("iframe", Object.assign({
|
|
@@ -4,14 +4,23 @@ export default class TrayController {
|
|
|
4
4
|
_shouldOpen: boolean;
|
|
5
5
|
_editor: any;
|
|
6
6
|
_audioContainer: Element | null;
|
|
7
|
+
_captionsModified: boolean;
|
|
8
|
+
requestSubtitlesFromIframe(cb: any): void;
|
|
7
9
|
get container(): HTMLElement;
|
|
8
10
|
get isOpen(): boolean;
|
|
9
11
|
showTrayForEditor(editor: any): void;
|
|
12
|
+
_isPlayerReady: boolean | undefined;
|
|
10
13
|
hideTrayForEditor(editor: any): void;
|
|
14
|
+
_listenForPlayerIframeToLoad(currentMediaId: any): void;
|
|
15
|
+
_iframeLoadingListener: AbortController | undefined;
|
|
16
|
+
_reloadAudioPlayer(): void;
|
|
11
17
|
_dismissTray(): void;
|
|
12
18
|
_resetController(): Node;
|
|
19
|
+
_resizeContainer({ height, width }: {
|
|
20
|
+
height: any;
|
|
21
|
+
width: any;
|
|
22
|
+
}): void;
|
|
13
23
|
_applyAudioOptions(audioOptions: any): any;
|
|
14
|
-
requestSubtitlesFromIframe(cb: any): void;
|
|
15
24
|
_subtitleListener: AbortController | undefined;
|
|
16
|
-
_renderTray(
|
|
25
|
+
_renderTray(): void;
|
|
17
26
|
}
|