@instructure/canvas-rce 7.3.1 → 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 +65 -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/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/youtube_overlay.js +18 -0
- package/es/getThemeVars.d.ts +1 -1
- package/es/getThemeVars.js +23 -26
- package/es/rce/KeyboardShortcutModal.js +1 -1
- 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 +6 -16
- package/es/rce/RCEWrapper.js +18 -87
- 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/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 -26
- 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 -6
- package/es/rce/plugins/shared/StudioLtiSupportUtils.js +82 -13
- 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 +65 -80
- package/es/translations/locales/ca.js +65 -80
- package/es/translations/locales/cy.js +65 -80
- package/es/translations/locales/da-x-k12.js +65 -80
- package/es/translations/locales/da.js +65 -80
- package/es/translations/locales/de.js +65 -80
- package/es/translations/locales/el.js +0 -9
- package/es/translations/locales/en-AU-x-unimelb.js +65 -80
- package/es/translations/locales/en-GB-x-ukhe.js +65 -80
- package/es/translations/locales/en.js +61 -79
- package/es/translations/locales/en_AU.js +65 -80
- package/es/translations/locales/en_CA.js +65 -80
- package/es/translations/locales/en_CY.js +65 -80
- package/es/translations/locales/en_GB.js +65 -80
- package/es/translations/locales/es.js +65 -80
- package/es/translations/locales/es_ES.js +65 -80
- package/es/translations/locales/fa_IR.js +0 -9
- package/es/translations/locales/fi.js +65 -80
- package/es/translations/locales/fr.js +65 -80
- package/es/translations/locales/fr_CA.js +65 -80
- package/es/translations/locales/ga.js +65 -80
- package/es/translations/locales/he.js +0 -9
- package/es/translations/locales/hi.js +65 -80
- package/es/translations/locales/ht.js +65 -80
- package/es/translations/locales/hu.js +0 -36
- package/es/translations/locales/hy.js +0 -9
- package/es/translations/locales/id.js +65 -80
- package/es/translations/locales/is.js +65 -80
- package/es/translations/locales/it.js +65 -80
- package/es/translations/locales/ja.js +65 -80
- package/es/translations/locales/ko.js +2455 -133
- package/es/translations/locales/mi.js +65 -80
- package/es/translations/locales/ms.js +65 -80
- package/es/translations/locales/nb-x-k12.js +65 -80
- package/es/translations/locales/nb.js +65 -80
- package/es/translations/locales/nl.js +66 -81
- package/es/translations/locales/nn.js +0 -36
- package/es/translations/locales/pl.js +65 -80
- package/es/translations/locales/pt.js +65 -80
- package/es/translations/locales/pt_BR.js +65 -80
- package/es/translations/locales/ru.js +65 -80
- package/es/translations/locales/sl.js +65 -80
- package/es/translations/locales/sv-x-k12.js +65 -80
- package/es/translations/locales/sv.js +65 -80
- package/es/translations/locales/th.js +65 -80
- package/es/translations/locales/tr.js +1962 -18
- package/es/translations/locales/uk_UA.js +0 -9
- package/es/translations/locales/vi.js +65 -80
- package/es/translations/locales/zh-Hans.js +65 -80
- package/es/translations/locales/zh-Hant.js +65 -80
- package/es/translations/locales/zh.js +65 -80
- package/es/translations/locales/zh_HK.js +65 -80
- 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
package/es/rce/RCEWrapper.js
CHANGED
|
@@ -20,7 +20,7 @@ import _pt from "prop-types";
|
|
|
20
20
|
import React, { Suspense } from 'react';
|
|
21
21
|
import { Editor } from '@tinymce/tinymce-react';
|
|
22
22
|
import tinymce from 'tinymce';
|
|
23
|
-
import
|
|
23
|
+
import { uniqBy } from 'es-toolkit/compat';
|
|
24
24
|
import { StoreProvider } from './plugins/shared/StoreContext';
|
|
25
25
|
import { IconKeyboardShortcutsLine } from '@instructure/ui-icons';
|
|
26
26
|
import { Alert } from '@instructure/ui-alerts';
|
|
@@ -144,7 +144,6 @@ class RCEWrapper extends React.Component {
|
|
|
144
144
|
this._statusBarId = void 0;
|
|
145
145
|
this._textareaEl = void 0;
|
|
146
146
|
this._effectiveContainingContext = void 0;
|
|
147
|
-
this.AIToolsTray = void 0;
|
|
148
147
|
this.editor = void 0;
|
|
149
148
|
this.initialContent = void 0;
|
|
150
149
|
this.intersectionObserver = void 0;
|
|
@@ -332,6 +331,13 @@ class RCEWrapper extends React.Component {
|
|
|
332
331
|
tinyapp.setAttribute('aria-label', formatMessage('Rich Content Editor'));
|
|
333
332
|
tinyapp.setAttribute('role', 'document');
|
|
334
333
|
tinyapp.setAttribute('tabIndex', '-1');
|
|
334
|
+
|
|
335
|
+
// tinyMCE browser detection is wrong for Edge tinymce.Env.browser.isEdge
|
|
336
|
+
const isEdge = /edg/i.test(navigator.userAgent);
|
|
337
|
+
if (isEdge) {
|
|
338
|
+
// Edge translation service breaks the editor
|
|
339
|
+
tinyapp.setAttribute('translate', 'no');
|
|
340
|
+
}
|
|
335
341
|
}
|
|
336
342
|
|
|
337
343
|
// remove role="aplication" attribute from the iframe body
|
|
@@ -512,7 +518,6 @@ class RCEWrapper extends React.Component {
|
|
|
512
518
|
if (autosavedContent !== editorContent) {
|
|
513
519
|
this.setState({
|
|
514
520
|
confirmAutoSave: true,
|
|
515
|
-
// @ts-expect-error
|
|
516
521
|
autoSavedContent: patchAutosavedContent(autosaved.content)
|
|
517
522
|
});
|
|
518
523
|
} else {
|
|
@@ -691,64 +696,6 @@ class RCEWrapper extends React.Component {
|
|
|
691
696
|
this.state.KBShortcutFocusReturn?.focus();
|
|
692
697
|
}
|
|
693
698
|
};
|
|
694
|
-
this.handleAIClick = () => {
|
|
695
|
-
import('./plugins/shared/ai_tools').then(module => {
|
|
696
|
-
// @ts-expect-error
|
|
697
|
-
this.AIToolsTray = module.AIToolsTray;
|
|
698
|
-
this.setState({
|
|
699
|
-
AIToolsOpen: true,
|
|
700
|
-
AITToolsFocusReturn: document.activeElement
|
|
701
|
-
});
|
|
702
|
-
}).catch(ex => {
|
|
703
|
-
console.error('Failed loading the AIToolsTray', ex);
|
|
704
|
-
});
|
|
705
|
-
};
|
|
706
|
-
this.closeAITools = () => {
|
|
707
|
-
this.setState({
|
|
708
|
-
AIToolsOpen: false
|
|
709
|
-
});
|
|
710
|
-
};
|
|
711
|
-
this.AIToolsExited = () => {
|
|
712
|
-
if (this.state.AITToolsFocusReturn === this.iframe) {
|
|
713
|
-
// launched using a kb shortcut
|
|
714
|
-
// the iframe has focus so we need to forward it on to tinymce editor
|
|
715
|
-
if (this.editor) {
|
|
716
|
-
this.editor.focus(false);
|
|
717
|
-
}
|
|
718
|
-
} else if (this.state.AITToolsFocusReturn === document.getElementById(`show-on-focus-btn-${this.id}`)) {
|
|
719
|
-
// launched from showOnFocus button
|
|
720
|
-
// edge case where focusing KBShortcutFocusReturn doesn't work
|
|
721
|
-
this._showOnFocusButton?.focus();
|
|
722
|
-
} else {
|
|
723
|
-
// launched from kb shortcut button on status bar
|
|
724
|
-
// @ts-expect-error
|
|
725
|
-
this.state.AITToolsFocusReturn?.focus();
|
|
726
|
-
}
|
|
727
|
-
};
|
|
728
|
-
this.handleInsertAIContent = content => {
|
|
729
|
-
const editor = this.mceInstance();
|
|
730
|
-
contentInsertion.insertContent(editor, content);
|
|
731
|
-
};
|
|
732
|
-
this.handleReplaceAIContent = content => {
|
|
733
|
-
const ed = this.mceInstance();
|
|
734
|
-
const selection = ed.selection;
|
|
735
|
-
if (selection.getContent().length > 0) {
|
|
736
|
-
selection.setContent(content);
|
|
737
|
-
} else {
|
|
738
|
-
ed.selection.select(ed.getBody(), true);
|
|
739
|
-
selection.setContent(content);
|
|
740
|
-
}
|
|
741
|
-
};
|
|
742
|
-
this.getCurrentContentForAI = () => {
|
|
743
|
-
const selected = this.mceInstance().selection.getContent();
|
|
744
|
-
return selected ? {
|
|
745
|
-
type: 'selection',
|
|
746
|
-
content: selected
|
|
747
|
-
} : {
|
|
748
|
-
type: 'full',
|
|
749
|
-
content: this.mceInstance().getContent()
|
|
750
|
-
};
|
|
751
|
-
};
|
|
752
699
|
this.handleTextareaChange = () => {
|
|
753
700
|
if (this.isHidden()) {
|
|
754
701
|
this.setCode(this.textareaValue());
|
|
@@ -760,7 +707,7 @@ class RCEWrapper extends React.Component {
|
|
|
760
707
|
alert.id = alertIdValue++;
|
|
761
708
|
this.setState(state => {
|
|
762
709
|
let messages = state.messages.concat(alert);
|
|
763
|
-
messages =
|
|
710
|
+
messages = uniqBy(messages, 'text'); // Don't show the same message twice
|
|
764
711
|
return {
|
|
765
712
|
messages
|
|
766
713
|
};
|
|
@@ -783,7 +730,7 @@ class RCEWrapper extends React.Component {
|
|
|
783
730
|
}
|
|
784
731
|
alertIdValue = 0;
|
|
785
732
|
};
|
|
786
|
-
this.style = buildStyle();
|
|
733
|
+
this.style = buildStyle(!!props.useHighContrast, props.fontFamily);
|
|
787
734
|
|
|
788
735
|
// Set up some limited global state that can be referenced
|
|
789
736
|
// as needed in RCE's components and function / plugin definitions
|
|
@@ -842,8 +789,7 @@ class RCEWrapper extends React.Component {
|
|
|
842
789
|
prevHeight: _ht
|
|
843
790
|
},
|
|
844
791
|
a11yErrorsCount: 0,
|
|
845
|
-
shouldShowEditor: typeof IntersectionObserver === 'undefined' || maxInitRenderedRCEs <= 0 || currentRCECount < maxInitRenderedRCEs
|
|
846
|
-
AIToolsOpen: false
|
|
792
|
+
shouldShowEditor: typeof IntersectionObserver === 'undefined' || maxInitRenderedRCEs <= 0 || currentRCECount < maxInitRenderedRCEs
|
|
847
793
|
};
|
|
848
794
|
this._statusBarId = `${this.state.id}_statusbar`;
|
|
849
795
|
this.pendingEventHandlers = [];
|
|
@@ -867,7 +813,6 @@ class RCEWrapper extends React.Component {
|
|
|
867
813
|
this.resizeObserver = new ResizeObserver(() => {
|
|
868
814
|
this._handleFullscreenResize();
|
|
869
815
|
});
|
|
870
|
-
this.AIToolsTray = undefined;
|
|
871
816
|
this._effectiveContainingContext = normalizeContainingContext(this.props.trayProps?.containingContext);
|
|
872
817
|
}
|
|
873
818
|
|
|
@@ -896,17 +841,17 @@ class RCEWrapper extends React.Component {
|
|
|
896
841
|
rce_transform_loaded_content = false,
|
|
897
842
|
rce_find_replace = false,
|
|
898
843
|
rce_studio_embed_improvements = false,
|
|
899
|
-
|
|
900
|
-
|
|
844
|
+
rce_asr_captioning_improvements = false,
|
|
845
|
+
file_verifiers_for_quiz_links = false
|
|
901
846
|
} = this.props.features;
|
|
902
847
|
return {
|
|
903
848
|
new_math_equation_handling,
|
|
904
849
|
explicit_latex_typesetting,
|
|
905
850
|
rce_transform_loaded_content,
|
|
906
851
|
rce_studio_embed_improvements,
|
|
852
|
+
rce_asr_captioning_improvements,
|
|
907
853
|
file_verifiers_for_quiz_links,
|
|
908
|
-
rce_find_replace
|
|
909
|
-
consolidated_media_player
|
|
854
|
+
rce_find_replace
|
|
910
855
|
};
|
|
911
856
|
}
|
|
912
857
|
getRequiredConfigValues() {
|
|
@@ -1744,7 +1689,6 @@ class RCEWrapper extends React.Component {
|
|
|
1744
1689
|
});
|
|
1745
1690
|
}
|
|
1746
1691
|
const statusBarOptions = {
|
|
1747
|
-
aiTextTools: this.props.ai_text_tools,
|
|
1748
1692
|
isDesktop: tinymce.Env.deviceType.isDesktop(),
|
|
1749
1693
|
a11yResizers: !!this.props.features?.rce_a11y_resize
|
|
1750
1694
|
};
|
|
@@ -1834,8 +1778,7 @@ class RCEWrapper extends React.Component {
|
|
|
1834
1778
|
skipEditorFocus: true
|
|
1835
1779
|
}),
|
|
1836
1780
|
disabledPlugins: this.pluginsToExclude,
|
|
1837
|
-
features: statusBarFeatures
|
|
1838
|
-
onAI: this.handleAIClick
|
|
1781
|
+
features: statusBarFeatures
|
|
1839
1782
|
}), this._effectiveContainingContext && /*#__PURE__*/React.createElement(CanvasContentTray, Object.assign({
|
|
1840
1783
|
mountNode: instuiPopupMountNodeFn,
|
|
1841
1784
|
key: this.id,
|
|
@@ -1853,19 +1796,6 @@ class RCEWrapper extends React.Component {
|
|
|
1853
1796
|
onExited: this.KBShortcutModalExited,
|
|
1854
1797
|
onDismiss: this.closeKBShortcutModal,
|
|
1855
1798
|
open: this.state.KBShortcutModalOpen
|
|
1856
|
-
}), this.props.ai_text_tools && this.AIToolsTray &&
|
|
1857
|
-
/*#__PURE__*/
|
|
1858
|
-
// @ts-expect-error
|
|
1859
|
-
React.createElement(this.AIToolsTray, {
|
|
1860
|
-
open: this.state.AIToolsOpen,
|
|
1861
|
-
container: document.querySelector('[role="main"]'),
|
|
1862
|
-
mountNode: instuiPopupMountNodeFn,
|
|
1863
|
-
contextId: trayProps.contextId,
|
|
1864
|
-
contextType: trayProps.contextId,
|
|
1865
|
-
currentContent: this.getCurrentContentForAI(),
|
|
1866
|
-
onClose: this.closeAITools,
|
|
1867
|
-
onInsertContent: this.handleInsertAIContent,
|
|
1868
|
-
onReplaceContent: this.handleReplaceAIContent
|
|
1869
1799
|
}), this.state.confirmAutoSave ? /*#__PURE__*/React.createElement(Suspense, {
|
|
1870
1800
|
fallback: /*#__PURE__*/React.createElement(Spinner, {
|
|
1871
1801
|
renderTitle: renderLoading,
|
|
@@ -1884,7 +1814,6 @@ class RCEWrapper extends React.Component {
|
|
|
1884
1814
|
}
|
|
1885
1815
|
}
|
|
1886
1816
|
RCEWrapper.propTypes = {
|
|
1887
|
-
ai_text_tools: _pt.bool,
|
|
1888
1817
|
autosave: _pt.shape({
|
|
1889
1818
|
enabled: _pt.bool,
|
|
1890
1819
|
maxAge: _pt.number
|
|
@@ -1910,6 +1839,8 @@ RCEWrapper.propTypes = {
|
|
|
1910
1839
|
textareaId: _pt.string,
|
|
1911
1840
|
tinymce: _pt.any.isRequired,
|
|
1912
1841
|
use_rce_icon_maker: _pt.bool,
|
|
1842
|
+
useHighContrast: _pt.bool,
|
|
1843
|
+
fontFamily: _pt.string,
|
|
1913
1844
|
userCacheKey: _pt.string
|
|
1914
1845
|
};
|
|
1915
1846
|
RCEWrapper.propTypes = rceWrapperPropTypes;
|
|
@@ -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;
|
|
@@ -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({
|