UNPKG

@instructure/canvas-rce

Version:

A component wrapping Canvas's usage of Tinymce

429 lines (428 loc) 17.7 kB
import React, { ReactNode } from 'react'; import { Editor } from '@tinymce/tinymce-react'; import tinymce from 'tinymce'; import type { Editor as TinyMCEEditor } from 'tinymce'; import { PlaceHoldableThingInfo } from '../util/loadingPlaceholder'; import EncryptedStorage from '../util/encrypted-storage'; import { RCEVariant } from './RCEVariants'; import { mergeMenu, mergeMenuItems, mergePlugins, mergeToolbar, parsePluginsToExclude } from './RCEWrapper.utils'; import { AlertMessage, EditorOptions, RCETrayProps } from './types'; export declare function storageAvailable(): boolean | null; interface RCEWrapperProps { ai_text_tools?: boolean; autosave?: { enabled?: boolean; maxAge?: number; }; canvasOrigin: string; defaultContent?: string; editorOptions: EditorOptions; editorView?: string; features: Record<string, unknown>; handleUnmount?: () => void; instRecordDisabled?: boolean; language?: string; liveRegion?: HTMLElement | null | (() => HTMLElement | null | undefined); ltiToolFavorites?: string[]; maxInitRenderedRCEs: number; name?: string; onBlur?: (event: React.FocusEvent<HTMLElement>) => void; onContentChange?: (content: string) => void; onFocus?: (rce: RCEWrapper) => void; onInitted?: (editor: TinyMCEEditor) => void; onRemove?: (arg1: RCEWrapper) => void; readOnly?: boolean; renderKBShortcutModal?: boolean; textareaClassName?: string; textareaId?: string; tinymce: typeof tinymce; trayProps: RCETrayProps; use_rce_icon_maker?: boolean; userCacheKey?: string; } interface RCEWrapperState { a11yErrorsCount: number; AIToolsOpen: boolean; AITToolsFocusReturn: unknown; alertId?: number; announcement: string | null; autoSavedContent: string; confirmAutoSave: boolean; editor: Editor; editorView: string; fullscreenState: { prevHeight: number; isTinyFullscreen?: boolean; }; height: string; id: string; KBShortcutFocusReturn?: HTMLElement; KBShortcutModalOpen: boolean; messages: AlertMessage[]; path: string[]; shouldShowEditor: boolean; shouldShowOnFocusButton: boolean; wordCount: number; } declare class RCEWrapper extends React.Component<RCEWrapperProps, RCEWrapperState> { _destroyCalled: boolean; _editorPlaceholderRef: React.RefObject<HTMLElement>; _elementRef: React.RefObject<HTMLElement>; _focusRegio?: Element; _focusRegion?: Element; _mceSerializedInitialHtmlCached?: string | null; _showOnFocusButton?: HTMLElement; _statusBarId: string; _textareaEl?: HTMLTextAreaElement; _effectiveContainingContext: RCETrayProps['containingContext']; AIToolsTray?: ReactNode; editor: TinyMCEEditor | null; initialContent?: string; intersectionObserver?: IntersectionObserver; language: string; ltiToolFavorites: unknown[]; mutationObserver?: MutationObserver; pendingEventHandlers: Array<() => void>; pluginsToExclude: string[]; resizeObserver: ResizeObserver; storage?: EncryptedStorage; variant: RCEVariant; style: { css: string; }; insert_code: typeof this.insertCode; get_code: typeof this.getCode; set_code: typeof this.setCode; static getByEditor(editor: TinyMCEEditor): any; static propTypes: { autosave: import("prop-types").Requireable<import("prop-types").InferProps<{ enabled: import("prop-types").Requireable<boolean>; maxAge: import("prop-types").Requireable<number>; }>>; canvasOrigin: import("prop-types").Requireable<string>; defaultContent: import("prop-types").Requireable<string>; editorOptions: import("prop-types").Requireable<import("prop-types").InferProps<{ height: import("prop-types").Requireable<NonNullable<string | number | null | undefined>>; toolbar: import("prop-types").Requireable<(import("prop-types").InferProps<{ name: import("prop-types").Validator<string>; items: import("prop-types").Validator<(string | null | undefined)[]>; }> | null | undefined)[]>; menu: import("prop-types").Requireable<{ [x: string]: import("prop-types").InferProps<{ title: import("prop-types").Requireable<string>; items: import("prop-types").Validator<string>; }> | null | undefined; }>; plugins: import("prop-types").Requireable<(string | null | undefined)[]>; readonly: import("prop-types").Requireable<boolean>; selector: import("prop-types").Requireable<string>; init_instance_callback: import("prop-types").Requireable<(...args: any[]) => any>; }>>; handleUnmount: import("prop-types").Requireable<(...args: any[]) => any>; editorView: import("prop-types").Requireable<string>; renderKBShortcutModal: import("prop-types").Requireable<boolean>; id: import("prop-types").Requireable<string>; language: import("prop-types").Requireable<string>; liveRegion: import("prop-types").Validator<(...args: any[]) => any>; ltiTools: import("prop-types").Requireable<(import("prop-types").InferProps<{ id: import("prop-types").Requireable<NonNullable<string | number | null | undefined>>; favorite: import("prop-types").Requireable<boolean>; on_by_default: import("prop-types").Requireable<boolean>; name: import("prop-types").Requireable<string>; description: import("prop-types").Requireable<string>; icon_url: import("prop-types").Requireable<string>; height: import("prop-types").Requireable<number>; width: import("prop-types").Requireable<number>; use_tray: import("prop-types").Requireable<boolean>; canvas_icon_class: import("prop-types").Requireable<any>; }> | null | undefined)[]>; onContentChange: import("prop-types").Requireable<(...args: any[]) => any>; onFocus: import("prop-types").Requireable<(...args: any[]) => any>; onBlur: import("prop-types").Requireable<(...args: any[]) => any>; onInitted: import("prop-types").Requireable<(...args: any[]) => any>; onRemove: import("prop-types").Requireable<(...args: any[]) => any>; textareaClassName: import("prop-types").Requireable<string>; textareaId: import("prop-types").Validator<string>; readOnly: import("prop-types").Requireable<boolean>; tinymce: import("prop-types").Requireable<object>; trayProps: import("prop-types").Requireable<import("prop-types").InferProps<{ canUploadFiles: import("prop-types").Validator<boolean>; contextId: import("prop-types").Validator<string>; contextType: import("prop-types").Validator<string>; containingContext: import("prop-types").Requireable<import("prop-types").InferProps<{ contextType: import("prop-types").Validator<string>; contextId: import("prop-types").Validator<string>; userId: import("prop-types").Validator<string>; }>>; filesTabDisabled: import("prop-types").Requireable<boolean>; host: import("prop-types").Requireable<string>; jwt: import("prop-types").Requireable<string>; refreshToken: import("prop-types").Requireable<(...args: any[]) => any>; source: import("prop-types").Requireable<import("prop-types").InferProps<{ fetchImages: import("prop-types").Validator<(...args: any[]) => any>; }>>; themeUrl: import("prop-types").Requireable<string>; }>>; toolbar: import("prop-types").Requireable<(import("prop-types").InferProps<{ name: import("prop-types").Validator<string>; items: import("prop-types").Validator<(string | null | undefined)[]>; }> | null | undefined)[]>; menu: import("prop-types").Requireable<{ [x: string]: import("prop-types").InferProps<{ title: import("prop-types").Requireable<string>; items: import("prop-types").Validator<string>; }> | null | undefined; }>; instRecordDisabled: import("prop-types").Requireable<boolean>; highContrastCSS: import("prop-types").Requireable<(string | null | undefined)[]>; maxInitRenderedRCEs: import("prop-types").Requireable<number>; use_rce_icon_maker: import("prop-types").Requireable<boolean>; features: import("prop-types").Requireable<{ [x: string]: boolean | null | undefined; }>; flashAlertTimeout: import("prop-types").Requireable<number>; timezone: import("prop-types").Requireable<string>; userCacheKey: import("prop-types").Requireable<string>; externalToolsConfig: import("prop-types").Requireable<import("prop-types").InferProps<{ ltiIframeAllowances: import("prop-types").Requireable<(string | null | undefined)[]>; containingCanvasLtiToolId: import("prop-types").Requireable<string>; resourceSelectionUrlOverride: import("prop-types").Requireable<string>; isA2StudentView: import("prop-types").Requireable<boolean>; maxMruTools: import("prop-types").Requireable<number>; }>>; ai_text_tools: import("prop-types").Requireable<boolean>; variant: import("prop-types").Requireable<"full" | "lite" | "text-only" | "text-block" | "block-content-editor">; }; static defaultProps: { trayProps: null; autosave: { enabled: boolean; }; highContrastCSS: never[]; ltiTools: never[]; maxInitRenderedRCEs: number; features: {}; timezone: string; canvasOrigin: string; variant: string; }; static skinCssInjected: boolean; constructor(props: RCEWrapperProps); _tagTinymceAuxDiv(): void; _myTinymceAuxDiv(): HTMLElement | null; getRequiredFeatureStatuses(): { new_math_equation_handling: unknown; explicit_latex_typesetting: unknown; rce_transform_loaded_content: unknown; rce_studio_embed_improvements: unknown; file_verifiers_for_quiz_links: unknown; rce_find_replace: unknown; consolidated_media_player: unknown; }; getRequiredConfigValues(): { locale: string; flashAlertTimeout: any; timezone: any; }; getCanvasUrl(): string; getResourceIdentifiers(): { resourceType: any; resourceId: any; }; getCode(): string; checkReadyToGetCode(promptFunc: any): boolean; setCode(newContent: string): void; RCEClosed(): void; indicateEditor(element: Element): void; contentInserted(element: Element): void; sizeEditorForContent(elem: Element): void; checkImageLoadError(element: Element): void; insertCode(code: string): void; replaceCode(code: string): void; insertEmbedCode(code: string): void; insertImage(image: unknown): { imageElem: any; loadingPromise: Promise<void>; }; insertImagePlaceholder(fileMetaProps: PlaceHoldableThingInfo): Promise<HTMLElement>; insertVideo(video: unknown): void; insertAudio(audio: unknown): void; insertMathEquation(tex: unknown): void; removePlaceholders(name: string): void; insertLink(link: unknown): void; existingContentToLink(): any; existingContentToLinkIsImg(): any; tinymceOn(tinymceEventName: any, handler: any): void; mceInstance(): TinyMCEEditor; onTinyMCEInstance(command: string, ...args: any[]): void; destroy(): void; onRemove: () => void; getTextarea(): HTMLTextAreaElement | null; textareaValue(): string; get id(): string; getHtmlEditorStorage(): any; toggleView: (newView: string) => void; toggleFullscreen: () => void; _isFullscreen(): boolean; _enterFullscreen(): void; _exitFullscreen(): void; _onFullscreenChange: (event: any) => void; _handleFullscreenResize: () => void; _getStatusBarHeight(): number; _setHeight(newHeight: number): void; focus(): void; focusCurrentView(): void; is_dirty(): boolean; /** * Holds a copy of the initial content of the editor as serialized by tinyMCE to normalize it. */ get _mceSerializedInitialHtml(): string; isHtmlView(): boolean; isHidden(): boolean; get iframe(): HTMLIFrameElement; get focused(): boolean; handleFocus(): void; contentTrayClosing: boolean; handleContentTrayClosing(isClosing: boolean): void; blurTimer: number; handleBlur(event: React.FocusEvent<HTMLElement>): void; handleFocusRCE: () => void; handleBlurRCE: (event: any) => void; handleFocusEditor: (_event: Event) => void; handleBlurEditor: (event: React.FocusEvent<HTMLElement>) => void; call(methodName: string, ...args: any[]): any; handleKey: (event: KeyboardEvent) => void; handleClickFullscreen: () => void; handleInputChange: () => void; onInit: (_event: Event, editor: TinyMCEEditor) => void; /** * Fix keyboard navigation in the expanded toolbar * * NOTE: This is a workaround for https://github.com/tinymce/tinymce/issues/8618 * and should be removed once that issue is resolved and the tinymce dependency is updated to include it. */ fixToolbarKeyboardNavigation: () => void; /** * Sets up selection saving and restoration logic. * * There are certain actions a user can take when the RCE is not focused that clear the selection inside the * editor, such as invoking the Find feature of the browser. If the user then tries to insert content without * going back to the editor, the content would be inserted at the top of the RCE, instead of where their cursor * was. * * This method adds logic that saves and restores the selection to work around the issue. * * @private */ _setupSelectionSaving: (editor: any) => void; announcing: number; _isMounted: boolean; announceContextToolbars(editor: TinyMCEEditor): void; initAutoSave: (editor: TinyMCEEditor) => void; cleanupAutoSave: (deleteAll?: boolean) => void; restoreAutoSave: (ans: any) => void; getAutoSaved(key: string): any; get isAutoSaving(): boolean | null | undefined; get autoSaveKey(): string; doAutoSave: (e: any, retry?: boolean) => void; onWordCountUpdate: (e: { wordCount: { words: number; }; }) => void; onNodeChange: (e: any) => void; onEditorChange: (content: string, _editor: unknown) => void; onResize: (_e: unknown, coordinates: { deltaY: number; }) => void; onA11yChecker: (triggerElementId: string) => void; checkAccessibility: () => void; openKBShortcutModal: () => void; closeKBShortcutModal: () => void; KBShortcutModalExited: () => void; handleAIClick: () => void; closeAITools: () => void; AIToolsExited: () => void; handleInsertAIContent: (content: string) => void; handleReplaceAIContent: (content: string) => void; getCurrentContentForAI: () => { type: string; content: string; }; componentWillUnmount(): void; wrapOptions(options?: {}): { readonly: boolean | undefined; theme: string; height: any; language: string | undefined; document_base_url: string; block_formats: any; setup: (editor: TinyMCEEditor) => void; content_css: any; content_style: string; menubar: string; menu: Record<string, { items: string; }>; toolbar: { items: string[]; name: string; }[]; contextmenu: string; toolbar_mode: string; toolbar_sticky: boolean; plugins: string[]; textpattern_patterns: { start: string; cmd: string; }[]; auto_focus: boolean; body_class: string; directionality: string; color_map: string[]; branding: boolean; browser_spellcheck: boolean; convert_urls: boolean; font_formats: string; language_load: boolean; language_url: string; mobile: { theme: string; }; preview_styles: string; remove_script_host: boolean; resize: boolean; skin: boolean; statusbar: boolean; valid_elements: string; extended_valid_elements: string; non_empty_elements: string; target_list: boolean; link_title: boolean; default_link_target: string; style_formats: { title: string; items: { title: string; format: string; }[]; }[]; }; handleTextareaChange: () => void; unhandleTextareaChange(): void; registerTextareaChange(): void; componentDidMount(): void; componentDidUpdate(prevProps: RCEWrapperProps, prevState: RCEWrapperState): void; editorReallyDidMount(): void; setEditorView(view: any): void; addAlert: (alert: AlertMessage) => void; removeAlert: (messageId: number) => void; /** * Used for reseting the value during tests */ resetAlertId: () => void; renderHtmlEditor(): React.JSX.Element; render(): React.JSX.Element; } export default RCEWrapper; export { mergeMenuItems, mergeMenu, mergeToolbar, mergePlugins, parsePluginsToExclude };