From d8cd929ebe63dd4625dbc38693b504e765888980 Mon Sep 17 00:00:00 2001 From: zsviczian Date: Sun, 16 Jul 2023 09:39:46 +0200 Subject: [PATCH] migrated from "iframe" to "embeddable" --- src/ExcalidrawAutomate.ts | 76 +++++++++-------- src/ExcalidrawData.ts | 32 +++---- src/ExcalidrawView.ts | 83 +++++++------------ src/constants.ts | 4 +- ...{customIFrame.tsx => customEmbeddable.tsx} | 48 +++++------ src/dialogs/SuggesterInfo.ts | 4 +- src/dialogs/UniversalInsertFileModal.ts | 5 +- ...ionsMenu.tsx => EmbeddableActionsMenu.tsx} | 22 ++--- ...FrameUtils.ts => CustomEmbeddableUtils.ts} | 0 src/utils/ExcalidrawViewUtils.ts | 4 +- src/utils/ModifierkeyHelper.ts | 12 +-- styles.css | 6 +- 12 files changed, 141 insertions(+), 155 deletions(-) rename src/{customIFrame.tsx => customEmbeddable.tsx} (88%) rename src/menu/{IFrameActionsMenu.tsx => EmbeddableActionsMenu.tsx} (92%) rename src/utils/{CustomIFrameUtils.ts => CustomEmbeddableUtils.ts} (100%) diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts index dda00c8..acf5e8d 100644 --- a/src/ExcalidrawAutomate.ts +++ b/src/ExcalidrawAutomate.ts @@ -651,44 +651,48 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { }; } - /** - * - * @param topX - * @param topY - * @param width - * @param height - * @returns - */ - addIFrame(topX: number, topY: number, width: number, height: number, url?: string, file?: TFile): string { - //@ts-ignore - if (!this.targetView || !this.targetView?._loaded) { - errorMessage("targetView not set", "addIFrame()"); - return null; - } + //retained for backward compatibility + addIFrame(topX: number, topY: number, width: number, height: number, url?: string, file?: TFile): string { + return this.addEmbeddable(topX, topY, width, height, url, file); + } + /** + * + * @param topX + * @param topY + * @param width + * @param height + * @returns + */ + addEmbeddable(topX: number, topY: number, width: number, height: number, url?: string, file?: TFile): string { + //@ts-ignore + if (!this.targetView || !this.targetView?._loaded) { + errorMessage("targetView not set", "addEmbeddable()"); + return null; + } - if (!url && !file) { - errorMessage("Either the url or the file must be set. If both are provided the URL takes precedence", "addIFrame()"); - return null; - } + if (!url && !file) { + errorMessage("Either the url or the file must be set. If both are provided the URL takes precedence", "addEmbeddable()"); + return null; + } - const id = nanoid(); - this.elementsDict[id] = this.boxedElement( - id, - "iframe", - topX, - topY, - width, - height, - url ? url : file ? `[[${ - app.metadataCache.fileToLinktext( - file, - this.targetView.file.path, - file.extension === "md", - ) - }]]` : "", - ); - return id; - }; + const id = nanoid(); + this.elementsDict[id] = this.boxedElement( + id, + "embeddable", + topX, + topY, + width, + height, + url ? url : file ? `[[${ + app.metadataCache.fileToLinktext( + file, + this.targetView.file.path, + file.extension === "md", + ) + }]]` : "", + ); + return id; + }; /** * diff --git a/src/ExcalidrawData.ts b/src/ExcalidrawData.ts index e587418..966b897 100644 --- a/src/ExcalidrawData.ts +++ b/src/ExcalidrawData.ts @@ -17,9 +17,9 @@ import { FRONTMATTER_KEY_LINKBUTTON_OPACITY, FRONTMATTER_KEY_ONLOAD_SCRIPT, FRONTMATTER_KEY_AUTOEXPORT, - FRONTMATTER_KEY_IFRAME_THEME, + FRONTMATTER_KEY_EMBEDDABLE_THEME, DEVICE, - IFRAME_THEME_FRONTMATTER_VALUES, + EMBEDDABLE_THEME_FRONTMATTER_VALUES, getBoundTextMaxWidth, getDefaultLineHeight, getFontString, @@ -252,7 +252,7 @@ export class ExcalidrawData { private app: App; private showLinkBrackets: boolean; private linkPrefix: string; - public iFrameTheme: "light" | "dark" | "auto" | "default" = "auto"; + public embeddableTheme: "light" | "dark" | "auto" | "default" = "auto"; private urlPrefix: string; public autoexportPreference: AutoexportPreference = AutoexportPreference.inherit; private textMode: TextMode = TextMode.raw; @@ -282,6 +282,10 @@ export class ExcalidrawData { const elements = this.scene.elements; for (const el of elements) { + if(el.type === "iframe") { + el.type = "embeddable"; + } + if (el.boundElements) { const map = new Map(); el.boundElements.forEach((item: { id: string; type: string }) => { @@ -440,7 +444,7 @@ export class ExcalidrawData { this.setLinkPrefix(); this.setUrlPrefix(); this.setAutoexportPreferences(); - this.setIFrameThemePreference(); + this.setembeddableThemePreference(); this.scene = null; @@ -620,7 +624,7 @@ export class ExcalidrawData { this.setShowLinkBrackets(); this.setLinkPrefix(); this.setUrlPrefix(); - this.setIFrameThemePreference(); + this.setembeddableThemePreference(); this.scene = JSON.parse(data); if (!this.scene.files) { this.scene.files = {}; //loading legacy scenes without the files element @@ -1304,7 +1308,7 @@ export class ExcalidrawData { this.setLinkPrefix() || this.setUrlPrefix() || this.setShowLinkBrackets() || - this.setIFrameThemePreference() || + this.setembeddableThemePreference() || this.findNewElementLinksInScene(); await this.updateTextElementsFromScene(); if (result || this.findNewTextElementsInScene()) { @@ -1475,21 +1479,21 @@ export class ExcalidrawData { } } - private setIFrameThemePreference(): boolean { - const iFrameTheme = this.iFrameTheme; + private setembeddableThemePreference(): boolean { + const embeddableTheme = this.embeddableTheme; const fileCache = this.app.metadataCache.getFileCache(this.file); if ( fileCache?.frontmatter && - fileCache.frontmatter[FRONTMATTER_KEY_IFRAME_THEME] != null + fileCache.frontmatter[FRONTMATTER_KEY_EMBEDDABLE_THEME] != null ) { - this.iFrameTheme = fileCache.frontmatter[FRONTMATTER_KEY_IFRAME_THEME].toLowerCase(); - if (!IFRAME_THEME_FRONTMATTER_VALUES.includes(this.iFrameTheme)) { - this.iFrameTheme = "default"; + this.embeddableTheme = fileCache.frontmatter[FRONTMATTER_KEY_EMBEDDABLE_THEME].toLowerCase(); + if (!EMBEDDABLE_THEME_FRONTMATTER_VALUES.includes(this.embeddableTheme)) { + this.embeddableTheme = "default"; } } else { - this.iFrameTheme = this.plugin.settings.iframeMatchExcalidrawTheme ? "auto" : "default"; + this.embeddableTheme = this.plugin.settings.iframeMatchExcalidrawTheme ? "auto" : "default"; } - return iFrameTheme != this.iFrameTheme; + return embeddableTheme != this.embeddableTheme; } private setShowLinkBrackets(): boolean { diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts index 62ac71b..a30e572 100644 --- a/src/ExcalidrawView.ts +++ b/src/ExcalidrawView.ts @@ -117,12 +117,12 @@ import { getEA } from "src"; import { emulateCTRLClickForLinks, externalDragModifierType, internalDragModifierType, isALT, isCTRL, isMETA, isSHIFT, linkClickModifierType, mdPropModifier, ModifierKeys } from "./utils/ModifierkeyHelper"; import { setDynamicStyle } from "./utils/DynamicStyling"; import { InsertPDFModal } from "./dialogs/InsertPDFModal"; -import { CustomIFrame, renderWebView } from "./customIFrame"; -import { insertIFrameToView, insertImageToView } from "./utils/ExcalidrawViewUtils"; +import { CustomEmbeddable, renderWebView } from "./customEmbeddable"; +import { insertEmbeddableToView, insertImageToView } from "./utils/ExcalidrawViewUtils"; import { imageCache } from "./utils/ImageCache"; import { CanvasNodeFactory } from "./utils/CanvasNodeFactory"; -import { IFrameMenu } from "./menu/IFrameActionsMenu"; -import { useDefaultExcalidrawFrame } from "./utils/CustomIFrameUtils"; +import { EmbeddableMenu } from "./menu/EmbeddableActionsMenu"; +import { useDefaultExcalidrawFrame } from "./utils/CustomEmbeddableUtils"; declare const PLUGIN_VERSION:string; @@ -235,7 +235,7 @@ export default class ExcalidrawView extends TextFileView { public excalidrawAPI: any = null; public excalidrawWrapperRef: React.MutableRefObject = null; public toolsPanelRef: React.MutableRefObject = null; - public iframeMenuRef: React.MutableRefObject = null; + public embeddableMenuRef: React.MutableRefObject = null; private parentMoveObserver: MutationObserver; public linksAlwaysOpenInANewPane: boolean = false; //override the need for SHIFT+CTRL+click (used by ExcaliBrain) private hookServer: ExcalidrawAutomate; @@ -251,7 +251,7 @@ export default class ExcalidrawView extends TextFileView { public ownerDocument: Document; private draginfoDiv: HTMLDivElement; public canvasNodeFactory: CanvasNodeFactory; - private iFrameRefs = new Map(); + private embeddableRefs = new Map(); public semaphores: { popoutUnload: boolean; //the unloaded Excalidraw view was the last leaf in the popout window @@ -311,7 +311,7 @@ export default class ExcalidrawView extends TextFileView { private linkAction_Element: HTMLElement; public compatibilityMode: boolean = false; private obsidianMenu: ObsidianMenu; - private iframeMenu: IFrameMenu; + private embeddableMenu: EmbeddableMenu; //https://stackoverflow.com/questions/27132796/is-there-any-javascript-event-fired-when-the-on-screen-keyboard-on-mobile-safari private isEditingTextResetTimer: NodeJS.Timeout = null; @@ -856,25 +856,7 @@ export default class ExcalidrawView extends TextFileView { openExternalLink(link:string, element?: ExcalidrawElement):boolean { if (link.match(REG_LINKINDEX_HYPERLINK)) { - - /*if(element) { - const sceneCoordsToViewportCoords = this.plugin.getPackage(this.ownerWindow).excalidrawLib.sceneCoordsToViewportCoords; - const top = sceneCoordsToViewportCoords({sceneX: element.x, sceneY:element.y}, this.excalidrawAPI.getAppState()); - const bottom = sceneCoordsToViewportCoords({sceneX: element.x+element.width, sceneY:element.y+element.height}, this.excalidrawAPI.getAppState()); - const iframe = createEl("iframe",{ - attr: { - style: `position: absolute;top: ${top.y}px; left: ${top.x}px; width: ${bottom.x-top.x}px; height: ${bottom.y-top.y}px; margin: 0px; padding: 0px;z-index: 15; overflow: hidden; border: 0`, - frameborder: "0", - allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture", - allowfullscreen: "true", - src: element.link, - scrolling: "no" - } - }) - this.contentEl.appendChild(iframe); - } else {*/ window.open(link, "_blank"); - //} return true; } return false; @@ -1633,7 +1615,7 @@ export default class ExcalidrawView extends TextFileView { // clear the view content clear() { this.canvasNodeFactory.purgeNodes(); - this.iFrameRefs.clear(); + this.embeddableRefs.clear(); delete this.exportDialog; const api = this.excalidrawAPI; @@ -2322,7 +2304,7 @@ export default class ExcalidrawView extends TextFileView { const reactElement = React.createElement(() => { const excalidrawWrapperRef = React.useRef(null); const toolsPanelRef = React.useRef(null); - const iframeMenuRef = React.useRef(null); + const embeddableMenuRef = React.useRef(null); const [dimensions, setDimensions] = React.useState({ width: undefined, @@ -2337,9 +2319,9 @@ export default class ExcalidrawView extends TextFileView { let blockOnMouseButtonDown = false; this.toolsPanelRef = toolsPanelRef; - this.iframeMenuRef = iframeMenuRef; + this.embeddableMenuRef = embeddableMenuRef; this.obsidianMenu = new ObsidianMenu(this.plugin, toolsPanelRef, this); - this.iframeMenu = new IFrameMenu(this, iframeMenuRef); + this.embeddableMenu = new EmbeddableMenu(this, embeddableMenuRef); //excalidrawRef readypromise based on //https://codesandbox.io/s/eexcalidraw-resolvable-promise-d0qg3?file=/src/App.js:167-760 @@ -3069,7 +3051,7 @@ export default class ExcalidrawView extends TextFileView { case "link": msg = `Insert link\n${DEVICE.isMacOS || DEVICE.isIOS ? "try SHIFT and CTRL combinations for other drop actions" : "try SHIFT, CTRL, ALT combinations for other drop actions"}`; break; - case "iframe": msg = "Insert in interactive frame"; break; + case "embeddable": msg = "Insert in interactive frame"; break; } } else if(e.dataTransfer.types.length === 1 && e.dataTransfer.types.includes("Files")) { //drag from OS file manager @@ -3082,7 +3064,7 @@ export default class ExcalidrawView extends TextFileView { ? "try SHIFT, OPT, CTRL combinations for other drop actions" : "try SHIFT, CTRL, ALT combinations for other drop actions"}`; break; case "insert-link": msg = "Insert link"; break; - case "iframe": msg = "Insert in interactive frame"; break; + case "embeddable": msg = "Insert in interactive frame"; break; } } if(this.draginfoDiv.innerText !== msg) this.draginfoDiv.innerText = msg; @@ -3252,7 +3234,7 @@ export default class ExcalidrawView extends TextFileView { })(); }, renderTopRightUI: (isMobile: boolean, appState: AppState) => this.obsidianMenu.renderButton (isMobile, appState), - renderIFrameMenu: (appState: AppState) => this.iframeMenu.renderButtons(appState), + renderEmbeddableMenu: (appState: AppState) => this.embeddableMenu.renderButtons(appState), onPaste: (data: ClipboardData) => { //, event: ClipboardEvent | null /*if(data && data.text && hyperlinkIsYouTubeLink(data.text)) { @@ -3365,11 +3347,11 @@ export default class ExcalidrawView extends TextFileView { return false; } - if (internalDragAction === "iframe") { + if (internalDragAction === "embeddable") { (async () => { const ea: ExcalidrawAutomate = getEA(this); ea.selectElementsInView([ - await insertIFrameToView( + await insertEmbeddableToView( ea, this.currentPosition, file, @@ -3417,13 +3399,13 @@ export default class ExcalidrawView extends TextFileView { return; } - if (internalDragAction === "iframe") { + if (internalDragAction === "embeddable") { const ea:ExcalidrawAutomate = getEA(this); let column:number = 0; let row:number = 0; const ids:string[] = []; for (const f of draggable.files) { - ids.push(await insertIFrameToView( + ids.push(await insertEmbeddableToView( ea, { x:this.currentPosition.x + column*500, @@ -3481,8 +3463,8 @@ export default class ExcalidrawView extends TextFileView { return false; } } - if(text && (externalDragAction === "iframe")) { - insertIFrameToView( + if(text && (externalDragAction === "embeddable")) { + insertEmbeddableToView( getEA(this), this.currentPosition, undefined, @@ -3511,8 +3493,8 @@ export default class ExcalidrawView extends TextFileView { return false; } } - if(src && (externalDragAction === "iframe")) { - insertIFrameToView( + if(src && (externalDragAction === "embeddable")) { + insertEmbeddableToView( getEA(this), this.currentPosition, undefined, @@ -3540,7 +3522,7 @@ export default class ExcalidrawView extends TextFileView { return true; } if (!onDropHook("text", null, text)) { - if(text && (externalDragAction==="iframe") && /^(blob:)?(http|https):\/\/[^\s/$.?#].[^\s]*$/.test(text)) { + if(text && (externalDragAction==="embeddable") && /^(blob:)?(http|https):\/\/[^\s/$.?#].[^\s]*$/.test(text)) { return true; } if(text && (externalDragAction==="image-url") && hyperlinkIsYouTubeLink(text)) { @@ -3813,11 +3795,10 @@ export default class ExcalidrawView extends TextFileView { } }, - validateIFrame: true, + validateEmbeddable: true, renderWebview: DEVICE.isDesktop, - renderCustomIFrame: ( + renderEmbeddable: ( element: NonDeletedExcalidrawElement, - radius: number, appState: UIAppState, ) => { try { @@ -3829,7 +3810,7 @@ export default class ExcalidrawView extends TextFileView { if(element.link.match(REG_LINKINDEX_HYPERLINK)) { if(!useExcalidrawFrame) { - return renderWebView(element.link, radius, this, element.id, appState); + return renderWebView(element.link, this, element.id, appState); } else { return null; } @@ -3844,13 +3825,13 @@ export default class ExcalidrawView extends TextFileView { if(linkText.match(REG_LINKINDEX_HYPERLINK)) { if(!useExcalidrawFrame) { - return renderWebView(linkText, radius, this, element.id, appState); + return renderWebView(linkText, this, element.id, appState); } else { return null; } } - return React.createElement(CustomIFrame, {element,radius,view:this, appState, linkText}); + return React.createElement(CustomEmbeddable, {element,view:this, appState, linkText}); } catch(e) { return null; } @@ -4369,14 +4350,14 @@ export default class ExcalidrawView extends TextFileView { } } - public updateIFrameRef(id: string, ref: HTMLIFrameElement | HTMLWebViewElement | null) { + public updateEmbeddableRef(id: string, ref: HTMLIFrameElement | HTMLWebViewElement | null) { if (ref) { - this.iFrameRefs.set(id, ref); + this.embeddableRefs.set(id, ref); } } - public getIFrameElementById(id: string): HTMLIFrameElement | HTMLWebViewElement | undefined { - return this.iFrameRefs.get(id); + public getEmbeddableElementById(id: string): HTMLIFrameElement | HTMLWebViewElement | undefined { + return this.embeddableRefs.get(id); } } diff --git a/src/constants.ts b/src/constants.ts index 7234629..2f54ca9 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -100,8 +100,8 @@ export const FRONTMATTER_KEY_FONTCOLOR = "excalidraw-font-color"; export const FRONTMATTER_KEY_BORDERCOLOR = "excalidraw-border-color"; export const FRONTMATTER_KEY_MD_STYLE = "excalidraw-css"; export const FRONTMATTER_KEY_AUTOEXPORT = "excalidraw-autoexport" -export const FRONTMATTER_KEY_IFRAME_THEME = "excalidraw-iframe-theme"; -export const IFRAME_THEME_FRONTMATTER_VALUES = ["light", "dark", "auto", "dafault"]; +export const FRONTMATTER_KEY_EMBEDDABLE_THEME = "excalidraw-iframe-theme"; +export const EMBEDDABLE_THEME_FRONTMATTER_VALUES = ["light", "dark", "auto", "dafault"]; export const VIEW_TYPE_EXCALIDRAW = "excalidraw"; export const ICON_NAME = "excalidraw-icon"; export const MAX_COLORS = 5; diff --git a/src/customIFrame.tsx b/src/customEmbeddable.tsx similarity index 88% rename from src/customIFrame.tsx rename to src/customEmbeddable.tsx index daecb53..15ac44c 100644 --- a/src/customIFrame.tsx +++ b/src/customEmbeddable.tsx @@ -6,7 +6,7 @@ import { ConstructableWorkspaceSplit, getContainerForDocument, isObsidianThemeDa import { DEVICE, EXTENDED_EVENT_TYPES, KEYBOARD_EVENT_TYPES, TWITTER_REG } from "./Constants"; import { ExcalidrawImperativeAPI, UIAppState } from "@zsviczian/excalidraw/types/types"; import { ObsidianCanvasNode } from "./utils/CanvasNodeFactory"; -import { processLinkText, patchMobileView } from "./utils/CustomIFrameUtils"; +import { processLinkText, patchMobileView } from "./utils/CustomEmbeddableUtils"; declare module "obsidian" { interface Workspace { @@ -23,16 +23,16 @@ declare module "obsidian" { //Vimeo and Youtube are rendered by Excalidraw because of the window messaging //required to control the video //-------------------------------------------------------------------------------- -export const renderWebView = (src: string, radius: number, view: ExcalidrawView, id: string, appState: UIAppState):JSX.Element =>{ +export const renderWebView = (src: string, view: ExcalidrawView, id: string, appState: UIAppState):JSX.Element =>{ const twitterLink = src.match(TWITTER_REG); if (twitterLink) { const tweetID = src.match(/.*\/(\d*)\?/)[1]; if (tweetID) { - const theme = view.excalidrawData.iFrameTheme === "dark" + const theme = view.excalidrawData.embeddableTheme === "dark" ? "dark" - : view.excalidrawData.iFrameTheme === "light" + : view.excalidrawData.embeddableTheme === "light" ? "light" - : view.excalidrawData.iFrameTheme === "auto" + : view.excalidrawData.embeddableTheme === "auto" ? appState.theme === "dark" ? "dark" : "light" : isObsidianThemeDark() ? "dark" : "light"; src =`https://platform.twitter.com/embed/Tweet.html?frame=false&hideCard=false&hideThread=false&id=${tweetID}&lang=en&theme=${theme}&width=550px` @@ -43,29 +43,29 @@ export const renderWebView = (src: string, radius: number, view: ExcalidrawView, if(DEVICE.isDesktop && !twitterLink) { return ( view.updateIFrameRef(id, ref)} - className="excalidraw__iframe" + ref={(ref) => view.updateEmbeddableRef(id, ref)} + className="excalidraw__embeddable" title="Excalidraw Embedded Content" allowFullScreen={true} src={src} style={{ overflow: "hidden", - borderRadius: `${radius}px`, + borderRadius: "var(--embeddable-radius)", }} /> ); } return (