From dca61505ba138eef3819b16b760ec81becf9329e Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 13 May 2023 09:55:48 -0400 Subject: changed EditableViews to support oneline and multiline. Also added transformer UI to allow documents to be entered. changed transformer to write doc id's, not variables.. made schema view support oneline and fixed bug with docdecoration hader occluding things invisibly. updated web pages to be zoomable and for its anchors to update web page and scroll location properly. made autolinkanchor directly go to target on click. --- .../views/nodes/CollectionFreeFormDocumentView.tsx | 3 +- src/client/views/nodes/DocumentIcon.tsx | 56 ++-- src/client/views/nodes/DocumentView.tsx | 4 +- src/client/views/nodes/KeyValueBox.tsx | 6 +- src/client/views/nodes/KeyValuePair.scss | 64 ++-- src/client/views/nodes/KeyValuePair.tsx | 8 +- src/client/views/nodes/WebBox.tsx | 359 ++++++++++----------- src/client/views/nodes/button/FontIconBox.tsx | 13 +- .../views/nodes/formattedText/DashFieldView.tsx | 150 ++------- .../views/nodes/formattedText/FormattedTextBox.tsx | 3 +- src/client/views/nodes/formattedText/marks_rts.ts | 2 +- src/client/views/nodes/trails/PresBox.tsx | 18 +- 12 files changed, 299 insertions(+), 387 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index a8c8a0d2e..862d0da8c 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -12,12 +12,11 @@ import { SelectionManager } from '../../util/SelectionManager'; import { Transform } from '../../util/Transform'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { DocComponent } from '../DocComponent'; +import { InkingStroke } from '../InkingStroke'; import { StyleProp } from '../StyleProvider'; import './CollectionFreeFormDocumentView.scss'; import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView'; import React = require('react'); -import { DocumentType } from '../../documents/DocumentTypes'; -import { InkingStroke } from '../InkingStroke'; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; rotation?: number; color?: string; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined; diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx index 56de2d1fc..6e2ed72b8 100644 --- a/src/client/views/nodes/DocumentIcon.tsx +++ b/src/client/views/nodes/DocumentIcon.tsx @@ -1,23 +1,39 @@ - -import { observer } from "mobx-react"; -import * as React from "react"; -import { DocumentView } from "./DocumentView"; -import { DocumentManager } from "../../util/DocumentManager"; -import { Transformer, ts } from "../../util/Scripting"; -import { Field } from "../../../fields/Doc"; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { DocumentView } from './DocumentView'; +import { DocumentManager } from '../../util/DocumentManager'; +import { Transformer, ts } from '../../util/Scripting'; +import { Field } from '../../../fields/Doc'; +import { Tooltip } from '@material-ui/core'; +import { action, observable } from 'mobx'; +import { Id } from '../../../fields/FieldSymbols'; +import { factory } from 'typescript'; +import { LightboxView } from '../LightboxView'; @observer -export class DocumentIcon extends React.Component<{ view: DocumentView, index: number }> { +export class DocumentIcon extends React.Component<{ view: DocumentView; index: number }> { + @observable _hovered = false; + static get DocViews() { + return LightboxView.LightboxDoc ? DocumentManager.Instance.DocumentViews.filter(v => LightboxView.IsLightboxDocView(v.props.docViewPath())) : DocumentManager.Instance.DocumentViews; + } render() { const view = this.props.view; const { left, top, right, bottom } = view.getBounds() || { left: 0, top: 0, right: 0, bottom: 0 }; return ( -
-

d{this.props.index}

+
(this._hovered = true))} + onPointerLeave={action(e => (this._hovered = false))} + style={{ + pointerEvents: 'all', + opacity: this._hovered ? 0.3 : 1, + position: 'absolute', + transform: `translate(${(left + right) / 2}px, ${top}px)`, + }}> + {this.props.view.rootDoc.title}}> +

d{this.props.index}

+
); } @@ -41,7 +57,9 @@ export class DocumentIconContainer extends React.Component { const match = node.text.match(/d([0-9]+)/); if (match) { const m = parseInt(match[1]); + const doc = DocumentIcon.DocViews[m].rootDoc; usedDocuments.add(m); + return factory.createIdentifier(`idToDoc("${doc[Id]}")`); } } } @@ -52,14 +70,14 @@ export class DocumentIconContainer extends React.Component { }; }, getVars() { - const docs = Array.from(DocumentManager.Instance.DocumentViews); + const docs = DocumentIcon.DocViews; const capturedVariables: { [name: string]: Field } = {}; - usedDocuments.forEach(index => capturedVariables[`d${index}`] = docs[index].props.Document); - return { capturedVariables }; - } + usedDocuments.forEach(index => (capturedVariables[`d${index}`] = docs.length > index ? docs[index].props.Document : `d${index}`)); + return capturedVariables; + }, }; } render() { - return Array.from(DocumentManager.Instance.DocumentViews).map((dv, i) => ); + return DocumentIcon.DocViews.map((dv, i) => ); } -} \ No newline at end of file +} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b61a468bd..ffbde18ca 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -48,6 +48,7 @@ import { PresEffect, PresEffectDirection } from './trails'; import { PinProps, PresBox } from './trails/PresBox'; import React = require('react'); import { InkingStroke } from '../InkingStroke'; +import { RefField } from '../../../fields/RefField'; const { Howl } = require('howler'); interface Window { @@ -127,6 +128,7 @@ export interface DocComponentView { IsPlaying?: () => boolean; // is a media document playing TogglePause?: (keep?: boolean) => void; // toggle media document playing state setFocus?: () => void; // sets input focus to the componentView + setData?: (data: Field | Promise) => boolean; componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null; incrementalRendering?: () => void; fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox) @@ -434,7 +436,7 @@ export class DocumentViewInternal extends DocComponent (func().result?.select === true ? this.props.select(false) : ''), 'on double click'); } else if (!Doc.IsSystem(this.rootDoc) && (defaultDblclick === undefined || defaultDblclick === 'default')) { - UndoManager.RunInBatch(() => this.props.addDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap'); + UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap'); SelectionManager.DeselectAll(); Doc.UnBrushDoc(this.props.Document); } else { diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index e317de11e..f3c8ba62d 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -20,6 +20,10 @@ import { ImageBox } from './ImageBox'; import './KeyValueBox.scss'; import { KeyValuePair } from './KeyValuePair'; import React = require('react'); +import { DocumentManager } from '../../util/DocumentManager'; +import { ScriptingGlobals } from '../../util/ScriptingGlobals'; +import { ScriptingRepl } from '../ScriptingRepl'; +import { DocumentIconContainer } from './DocumentIcon'; export type KVPScript = { script: CompiledScript; @@ -75,7 +79,7 @@ export class KeyValueBox extends React.Component { value = dubEq ? value.substring(2) : value; const options: ScriptOptions = { addReturn: true, typecheck: false, params: { this: Doc.name, self: Doc.name, _last_: 'any', _readOnly_: 'boolean' }, editable: true }; if (dubEq) options.typecheck = false; - const script = CompileScript(value, options); + const script = CompileScript(value, { ...options, transformer: DocumentIconContainer.getTransformer() }); return !script.compiled ? undefined : { script, type: dubEq, onDelegate: eq }; } diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss index 5b660e582..57d36932e 100644 --- a/src/client/views/nodes/KeyValuePair.scss +++ b/src/client/views/nodes/KeyValuePair.scss @@ -1,60 +1,52 @@ -@import "../global/globalCssVariables"; - +@import '../global/globalCssVariables'; .keyValuePair-td-key { - display:inline-block; + display: inline-block; - .keyValuePair-td-key-container{ - width:100%; - height:100%; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - justify-content: space-between; - align-items: center; - .keyValuePair-td-key-delete{ + .keyValuePair-td-key-container { + width: 100%; + height: 100%; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: space-between; + .keyValuePair-td-key-delete { position: relative; background-color: transparent; - color:red; + color: red; } .keyValuePair-td-key-check { position: relative; margin: 0; } .keyValuePair-keyField { - width:100%; + width: 100%; margin-left: 20px; - margin-top: -1px; font-family: monospace; - // text-align: center; - align-self: center; position: relative; overflow: auto; + display: inline; } } } .keyValuePair-td-value { - display:inline-block; + display: inline-block; overflow: scroll; font-family: monospace; height: 30px; - .keyValuePair-td-value-container { - display: flex; - align-items: center; - align-content: center; - flex-direction: row; - justify-content: space-between; - flex-wrap: nowrap; - width: 100%; - height: 100%; + .keyValuePair-td-value-container { + display: inline; + justify-content: space-between; + width: 100%; + height: 100%; - img { - max-height: 36px; - width: auto; - } - .videoBox-cont{ - width: auto; - max-height: 36px; - } + img { + max-height: 36px; + width: auto; + } + .videoBox-cont { + width: auto; + max-height: 36px; + } } -} \ No newline at end of file +} diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 7ea6d42ff..85641124f 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -118,13 +118,7 @@ export class KeyValuePair extends React.Component {
- Field.toKeyValueString(props.Document, props.fieldKey)} - SetValue={(value: string) => KeyValueBox.SetField(props.Document, props.fieldKey, value)} - /> + Field.toKeyValueString(props.Document, props.fieldKey)} SetValue={(value: string) => KeyValueBox.SetField(props.Document, props.fieldKey, value)} />
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index e05b48c0b..82c8e796d 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -2,16 +2,16 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as WebRequest from 'web-request'; -import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { HtmlField } from '../../../fields/HtmlField'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; -import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; +import { Cast, ImageCast, NumCast, StrCast, WebCast } from '../../../fields/Types'; import { ImageField, WebField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, getWordAtPoint, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, removeStyleSheetRule, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; @@ -31,12 +31,13 @@ import { Annotation } from '../pdf/Annotation'; import { GPTPopup } from '../pdf/GPTPopup/GPTPopup'; import { SidebarAnnos } from '../SidebarAnnos'; import { StyleProp } from '../StyleProvider'; -import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from './DocumentView'; +import { DocComponentView, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { LinkDocPreview } from './LinkDocPreview'; import { PinProps, PresBox } from './trails'; import './WebBox.scss'; import React = require('react'); +import { RefField } from '../../../fields/RefField'; const { CreateImage } = require('./WebBoxRenderer'); const _global = (window /* browser */ || global) /* node */ as any; const htmlToText = require('html-to-text'); @@ -47,6 +48,7 @@ export class WebBox extends ViewBoxAnnotatableComponent void); private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }) => void); private _mainCont: React.RefObject = React.createRef(); @@ -58,15 +60,15 @@ export class WebBox extends ViewBoxAnnotatableComponent(); private _searchRef = React.createRef(); private _searchString = ''; + private _scrollTimer: any; private get _getAnchor() { return AnchorMenu.Instance?.GetAnchor; } - @observable private _webUrl = ''; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't wan the src parameter to also change as that would cause an unnecessary re-render. + @observable private _webUrl = ''; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't want the src parameter to also change as that would cause an unnecessary re-render. @observable private _hackHide = false; // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe. so thisforces the ifrmae to be rebuilt when allowScripts is toggled @observable private _searching: boolean = false; @observable private _showSidebar = false; - @observable private _scrollTimer: any; @observable private _webPageHasBeenRendered = false; @observable private _overlayAnnoInfo: Opt; @observable private _marqueeing: number[] | undefined; @@ -141,51 +143,40 @@ export class WebBox extends ViewBoxAnnotatableComponent { - const imageBitmap = ImageCast(this.layoutDoc['thumb-frozen'])?.url.href; + if (!this._iframe) return; const scrollTop = NumCast(this.layoutDoc._scrollTop); const nativeWidth = NumCast(this.layoutDoc.nativeWidth); const nativeHeight = (nativeWidth * this.props.PanelHeight()) / this.props.PanelWidth(); - if ( - !this.props.isSelected(true) && - !Doc.IsBrushedDegree(this.rootDoc) && - !this.isAnyChildContentActive() && - !this.rootDoc.thumbLockout && - !this.props.dontRegisterView && - this._iframe && - !imageBitmap && - (scrollTop !== this.layoutDoc.thumbScrollTop || nativeWidth !== this.layoutDoc.thumbNativeWidth || nativeHeight !== this.layoutDoc.thumbNativeHeight) - ) { - var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument); - if (!htmlString) { - htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text(); - } - this.layoutDoc.thumb = undefined; - this.rootDoc.thumbLockout = true; // lock to prevent multiple thumb updates. - CreateImage(this._webUrl.endsWith('/') ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl, this._iframe.contentDocument?.styleSheets ?? [], htmlString, nativeWidth, nativeHeight, scrollTop) - .then((data_url: any) => { - if (data_url.includes(' - setTimeout( - action(() => { - this.rootDoc.thumbLockout = false; - this.layoutDoc.thumb = new ImageField(returnedfilename); - this.layoutDoc.thumbScrollTop = scrollTop; - this.layoutDoc.thumbNativeWidth = nativeWidth; - this.layoutDoc.thumbNativeHeight = nativeHeight; - }), - 500 - ) - ); - }) - .catch(function (error: any) { - console.error('oops, something went wrong!', error); - }); + var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument); + if (!htmlString) { + htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text(); } + this.layoutDoc.thumb = undefined; + this.rootDoc.thumbLockout = true; // lock to prevent multiple thumb updates. + CreateImage(this._webUrl.endsWith('/') ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl, this._iframe.contentDocument?.styleSheets ?? [], htmlString, nativeWidth, nativeHeight, scrollTop) + .then((data_url: any) => { + if (data_url.includes(' + setTimeout( + action(() => { + this.rootDoc.thumbLockout = false; + this.layoutDoc.thumb = new ImageField(returnedfilename); + this.layoutDoc.thumbScrollTop = scrollTop; + this.layoutDoc.thumbNativeWidth = nativeWidth; + this.layoutDoc.thumbNativeHeight = nativeHeight; + }), + 500 + ) + ); + }) + .catch(function (error: any) { + console.error('oops, something went wrong!', error); + }); }; - _thumbTimer: any; + async componentDidMount() { this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document. this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons) @@ -196,24 +187,13 @@ export class WebBox extends ViewBoxAnnotatableComponent this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document), - async selected => { - if (selected) { - this._thumbTimer && clearTimeout(this._thumbTimer); - this._webPageHasBeenRendered = true; - } else if ( - (!this.props.isContentActive(true) || SnappingManager.GetIsDragging()) && // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail) - LightboxView.LightboxDoc !== this.rootDoc - ) { - // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty. - this._thumbTimer && clearTimeout(this._thumbTimer); - this._thumbTimer = setTimeout(this.updateThumb, 2000); - } - }, - { fireImmediately: this.props.isSelected(true) || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegreeUnmemoized(this.props.Document) ? true : false) } + this._disposers.urlchange = reaction( + () => WebCast(this.rootDoc.data), + url => { + this.submitURL(url.url.href, false, false); + } ); this._disposers.autoHeight = reaction( @@ -296,8 +276,6 @@ export class WebBox extends ViewBoxAnnotatableComponent this.urlEditor; // controls to be added to the top bar when a document of this type is selected - setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => (this._setBrushViewer = func); brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view); focus = (anchor: Doc, options: DocFocusOptions) => { @@ -316,9 +294,10 @@ export class WebBox extends ViewBoxAnnotatableComponent { + @action + getView = (doc: Doc) => { if (this.rootDoc.layoutKey === 'layout_icon') this.props.DocumentView?.().iconify(); - if (this._url && StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl)); + if (this._url && WebCast(doc.presData).url.href !== this._url) this.setData(WebCast(doc.presData).url.href); if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false); return new Promise>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); }; @@ -341,13 +320,13 @@ export class WebBox extends ViewBoxAnnotatableComponent this._iframeClick; iframeScaling = () => 1 / this.props.ScreenToLocalTransform().Scale; - addStyleSheet(document: any, styleType: string = 'text/css') { + addWebStyleSheet(document: any, styleType: string = 'text/css') { if (document) { const style = document.createElement('style'); style.type = styleType; @@ -421,7 +400,7 @@ export class WebBox extends ViewBoxAnnotatableComponent; try { @@ -463,12 +442,19 @@ export class WebBox extends ViewBoxAnnotatableComponent { + // e.ctrlKey && e.preventDefault(); + // }, + // { passive: false } + // ); const initHeights = () => { this._scrollHeight = Math.max(this._scrollHeight, (iframeContent.body.children[0] as any)?.scrollHeight || 0); if (this._scrollHeight) { @@ -482,11 +468,11 @@ export class WebBox extends ViewBoxAnnotatableComponent initHeights), 5000 ); - iframe.setAttribute('enable-annotation', 'true'); iframeContent.addEventListener( 'click', undoBatch( action((e: MouseEvent) => { + const batch = UndoManager.StartBatch('webclick'); let href = ''; for (let ele = e.target as any; ele; ele = ele.parentElement) { href = (typeof ele.href === 'string' ? ele.href : ele.href?.baseVal) || ele.parentElement?.href || href; @@ -494,7 +480,10 @@ export class WebBox extends ViewBoxAnnotatableComponent this.submitURL(href.replace(Utils.prepend(''), origin))); + setTimeout(() => { + this.setData(href.replace(Utils.prepend(''), origin)); + batch.end(); + }); if (this._outerRef.current) { this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop); this._outerRef.current.scrollLeft = 0; @@ -503,39 +492,49 @@ export class WebBox extends ViewBoxAnnotatableComponent !this.active() && this._iframe && (this._iframe.scrollTop = NumCast(this.layoutDoc._scrollTop), false)); + iframe.contentDocument.addEventListener('wheel', this.iframeWheel, { passive: false }); } }; @action - iframeWheel = (e: any) => { + iframeWheel = (e: WheelEvent) => { if (!this._scrollTimer) { - this._scrollTimer = setTimeout( - action(() => (this._scrollTimer = undefined)), - 250 - ); // this turns events off on the iframe which allows scrolling to change direction smoothly + addStyleSheetRule(WebBox.webStyleSheet, 'webBox-iframe', { 'pointer-events': 'none' }); + this._scrollTimer = setTimeout(() => { + this._scrollTimer = undefined; + clearStyleSheetRules(WebBox.webStyleSheet); + }, 250); // this turns events off on the iframe which allows scrolling to change direction smoothly + } + if (e.ctrlKey) { + if (this._innerCollectionView) { + this._innerCollectionView.zoom(e.screenX, e.screenY, e.deltaY); + const offset = e.clientY - NumCast(this.layoutDoc._scrollTop); + this.layoutDoc.panY = offset - offset / NumCast(this.layoutDoc._viewScale) + NumCast(this.layoutDoc._scrollTop) - NumCast(this.layoutDoc._scrollTop) / NumCast(this.layoutDoc._viewScale); + } + e.preventDefault(); } }; @action setDashScrollTop = (scrollTop: number, timeout: number = 250) => { const iframeHeight = Math.max(scrollTop, this._scrollHeight - this.panelHeight()); - this._scrollTimer && clearTimeout(this._scrollTimer); - this._scrollTimer = setTimeout( - action(() => { - this._scrollTimer = undefined; - const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop; - if (!LinkDocPreview.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) { - this.layoutDoc.thumb = undefined; - this.layoutDoc.thumbScrollTop = undefined; - this.layoutDoc.thumbNativeWidth = undefined; - this.layoutDoc.thumbNativeHeight = undefined; - this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = newScrollTop; - } else if (this._outerRef.current) this._outerRef.current.scrollTop = newScrollTop; - }), - timeout - ); + if (this._scrollTimer) { + clearTimeout(this._scrollTimer); + clearStyleSheetRules(WebBox.webStyleSheet); + } + addStyleSheetRule(WebBox.webStyleSheet, 'webBox-iframe', { 'pointer-events': 'none' }); + this._scrollTimer = setTimeout(() => { + clearStyleSheetRules(WebBox.webStyleSheet); + this._scrollTimer = undefined; + const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop; + if (!LinkDocPreview.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) { + this.layoutDoc.thumb = undefined; + this.layoutDoc.thumbScrollTop = undefined; + this.layoutDoc.thumbNativeWidth = undefined; + this.layoutDoc.thumbNativeHeight = undefined; + this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = newScrollTop; + } else if (this._outerRef.current) this._outerRef.current.scrollTop = newScrollTop; + }, timeout); }; goTo = (scrollTop: number, duration: number, easeFunc: 'linear' | 'ease' | undefined) => { @@ -557,8 +556,9 @@ export class WebBox extends ViewBoxAnnotatableComponent { if (future.length) { const curUrl = this._url; - this.rootDoc[this.fieldKey + '-history'] = new List([...history, this._url]); - this.rootDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); + this.dataDoc[this.fieldKey + '-history'] = new List([...history, this._url]); + this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); + this._scrollHeight = 0; if (this._webUrl === this._url) { this._webUrl = curUrl; setTimeout(action(() => (this._webUrl = this._url))); @@ -578,9 +578,10 @@ export class WebBox extends ViewBoxAnnotatableComponent { if (history.length) { const curUrl = this._url; - if (future === undefined) this.rootDoc[this.fieldKey + '-future'] = new List([this._url]); - else this.rootDoc[this.fieldKey + '-future'] = new List([...future, this._url]); - this.layoutDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); + if (future === undefined) this.dataDoc[this.fieldKey + '-future'] = new List([this._url]); + else this.dataDoc[this.fieldKey + '-future'] = new List([...future, this._url]); + this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); + this._scrollHeight = 0; if (this._webUrl === this._url) { this._webUrl = curUrl; setTimeout(action(() => (this._webUrl = this._url))); @@ -607,23 +608,18 @@ export class WebBox extends ViewBoxAnnotatableComponent([...(history || []), url]); - this.layoutDoc._scrollTop = 0; + if (!preview) { if (this._webPageHasBeenRendered) { this.layoutDoc.thumb = undefined; this.layoutDoc.thumbScrollTop = undefined; this.layoutDoc.thumbNativeWidth = undefined; this.layoutDoc.thumbNativeHeight = undefined; } - future && (future.length = 0); } if (!preview) { - this.layoutDoc[this.fieldKey] = new WebField(new URL(newUrl)); - !dontUpdateIframe && (this._webUrl = this._url); + if (!dontUpdateIframe) { + this._webUrl = this._url; + } } } catch (e) { console.log('WebBox URL error:' + this._url); @@ -637,48 +633,28 @@ export class WebBox extends ViewBoxAnnotatableComponent) => { + if (!(typeof data === 'string') && !(data instanceof WebField)) return false; + if (Field.toString(data) === this._url) return false; + this._scrollHeight = 0; + const oldUrl = this._url; + const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'), []); + const weburl = new WebField(Field.toString(data)); + this.dataDoc[this.fieldKey + '-future'] = new List([]); + this.dataDoc[this.fieldKey + '-history'] = new List([...(history || []), oldUrl]); + this.dataDoc[this.fieldKey] = weburl; + return true; + }; onWebUrlValueKeyDown = (e: React.KeyboardEvent) => { - e.key === 'Enter' && this.submitURL(this._keyInput.current!.value); + if (e.key === 'Enter') this.setData(this._keyInput.current!.value); e.stopPropagation(); }; - @computed get urlEditor() { - return ( -
e.preventDefault()}> - e.preventDefault()} - onKeyDown={this.onWebUrlValueKeyDown} - onClick={e => { - this._keyInput.current!.select(); - e.stopPropagation(); - }} - ref={this._keyInput} - /> -
- - - -
-
- ); - } - specificContextMenu = (e: React.MouseEvent | PointerEvent): void => { const cm = ContextMenu.Instance; const funcs: ContextMenuProps[] = []; @@ -706,6 +682,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this.updateThumb(), icon: 'portrait' }); cm.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' }); } }; @@ -746,19 +723,23 @@ export class WebBox extends ViewBoxAnnotatableComponent { + if (this._initialScroll === undefined && !this._webPageHasBeenRendered) { + this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop))); + } + this._webPageHasBeenRendered = true; + }) + ); const field = this.rootDoc[this.props.fieldKey]; - let view; if (field instanceof HtmlField) { - view = e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />; - } else if (field instanceof WebField) { + return e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />; + } + if (field instanceof WebField) { const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._webUrl) : this._webUrl; - view = ( + return (