From e17b1bdb09bfcadc717e687b09d2c18596341a10 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 9 Feb 2023 21:15:58 -0500 Subject: fixed childLayoutString to work. made images capable of fitWidth. fixed animating data field pres changes. fixed lightbox to ignore annotations on collections. fixed double-click on icon to open in lightbox. added options for turning off ink labels, and opening ink in lightbox. fixed closing ink strokes by dragging. fixed drawing ink to use coord sys of starting point and to render ink the correct width and to honor GestureOverlay mode properly. . --- src/client/views/nodes/MapBox/MapBox.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes/MapBox') diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 95cb49037..5940fc075 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -542,8 +542,8 @@ export class MapBox extends ViewBoxAnnotatableComponent this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); - panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); + panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); + panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; -- cgit v1.2.3-70-g09d2 From 32f5040c44dc302e3dd53cecd9be4cd51a474d3f Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 14 Feb 2023 14:57:25 -0500 Subject: fixed pinning regions on pdf/web pages to allow proper pres following. added click on pres item# to select w/o navigation. fixed dashDocView to be selectable without losing selection to parent textbox. added BingMaps rudimentary option to googleMaps --- package-lock.json | 18 ++- package.json | 2 + src/client/views/nodes/MapBox/MapBox.tsx | 148 +++++++++++++-------- src/client/views/nodes/PDFBox.tsx | 1 + src/client/views/nodes/WebBox.tsx | 1 + .../views/nodes/formattedText/DashDocView.tsx | 5 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 3 + src/client/views/nodes/trails/PresBox.tsx | 20 +-- src/client/views/nodes/trails/PresElementBox.scss | 6 + src/client/views/nodes/trails/PresElementBox.tsx | 12 +- 10 files changed, 143 insertions(+), 73 deletions(-) (limited to 'src/client/views/nodes/MapBox') diff --git a/package-lock.json b/package-lock.json index 547a6e1d7..95a128419 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3675,6 +3675,11 @@ "file-uri-to-path": "1.0.0" } }, + "bingmaps-react": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/bingmaps-react/-/bingmaps-react-1.2.10.tgz", + "integrity": "sha512-fM887Sr6OIlo6ThmSpGfNEDssSytdnsMrbDTDs+YH/43etf0dlfcR4oaJvygy+fwn21hpP2lVvwTHUDZvGxZqA==" + }, "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -4970,9 +4975,9 @@ } }, "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.28.0.tgz", + "integrity": "sha512-GiZn9D4Z/rSYvTeg1ljAIsEqFm0LaN9gVtwDCrKL80zHtS31p9BAjmTxVqTQDMpwlMolJZOFntUG2uwyj7DAqw==" }, "core-js-pure": { "version": "3.22.8", @@ -8260,6 +8265,13 @@ "promise": "^7.1.1", "setimmediate": "^1.0.5", "ua-parser-js": "^0.7.30" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA==" + } } }, "fd-slicer": { diff --git a/package.json b/package.json index 712354355..00ce356f9 100644 --- a/package.json +++ b/package.json @@ -165,6 +165,7 @@ "bcrypt-nodejs": "0.0.3", "bezier-curve": "^1.0.0", "bezier-js": "^4.1.1", + "bingmaps-react": "^1.2.10", "bluebird": "^3.7.2", "body-parser": "^1.19.2", "bootstrap": "^4.6.1", @@ -181,6 +182,7 @@ "connect-mongo": "^2.0.3", "cookie-parser": "^1.4.6", "cookie-session": "^2.0.0", + "core-js": "^3.28.0", "cors": "^2.8.5", "depcheck": "^0.9.2", "equation-editor-react": "github:bobzel/equation-editor-react#useLocally", diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 5940fc075..039f11cd1 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,5 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api'; +import BingMapsReact from 'bingmaps-react'; import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -43,20 +44,22 @@ const mapContainerStyle = { }; const defaultCenter = { - lat: 38.685, - lng: -115.234, + lat: 42.360081, + lng: -71.058884, }; const mapOptions = { fullscreenControl: false, }; +const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/ and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS= const apiKey = process.env.GOOGLE_MAPS; const script = document.createElement('script'); script.defer = true; script.async = true; script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`; +console.log(script.src); document.head.appendChild(script); /** @@ -84,6 +87,7 @@ const options = { @observer export class MapBox extends ViewBoxAnnotatableComponent>() { + static UseBing = true; private _dropDisposer?: DragManager.DragDropDisposer; private _disposers: { [name: string]: IReactionDisposer } = {}; private _annotationLayer: React.RefObject = React.createRef(); @@ -551,69 +555,99 @@ export class MapBox extends ViewBoxAnnotatableComponent this.props.PanelHeight() / 5; anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick; savedAnnotations = () => this._savedAnnotations; + + _bingSearchManager: any; + _bingMap: any; + get MicrosoftMaps() { + return (window as any).Microsoft.Maps; + } + // uses Bing Search to retrieve lat/lng for a location. eg., + // const results = this.geocodeQuery(map.map, 'Philadelphia, PA'); + // to move the map to that location: + // const location = await this.geocodeQuery(this._bingMap, 'Philadelphia, PA'); + // this._bingMap.current.setView({ + // mapTypeId: this.MicrosoftMaps.MapTypeId.aerial, + // center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude), + // }); + // + bingGeocode = (map: any, query: string) => { + return new Promise<{ latitude: number; longitude: number }>((res, reject) => { + //If search manager is not defined, load the search module. + if (!this._bingSearchManager) { + //Create an instance of the search manager and call the geocodeQuery function again. + this.MicrosoftMaps.loadModule('Microsoft.Maps.Search', () => { + this._bingSearchManager = new this.MicrosoftMaps.Search.SearchManager(map.current); + res(this.bingGeocode(map, query)); + }); + } else { + this._bingSearchManager.geocode({ + where: query, + callback: action((r: any) => { + res(r.results[0].location); + }), + errorCallback: (e: any) => reject(), + }); + } + }); + }; + + bingViewOptions = { + center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng }, + mapTypeId: 'grayscale', + }; + bingMapOptions = { + navigationBarMode: 'square', + }; + bingMapReady = (map: any) => (this._bingMap = map.map); render() { const renderAnnotations = (docFilters?: () => string[]) => null; - // bcz: commmented this out. Otherwise, any documents that are rendered with an InfoWindow of a marker - // will also be rendered as freeform annotations on the map. However, it doesn't seem that rendering - // freeform documents on the map does anything anyway, so getting rid of it for now. Also, since documents - // are rendered twice, adding a new note to the InfoWindow loses focus immediately on creation since it gets - // shifted to the non-visible view of the document in this freeform view. - // ; return (
- {/*console.log(apiKey)*/} - {/* */} -
e.stopPropagation()} onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}> +
e.stopPropagation()} + onPointerDown={async e => { + e.button === 0 && !e.ctrlKey && e.stopPropagation(); + // just a simple test of bing maps geocode api + // const loc = await this.bingGeocode(this._bingMap, 'Philadelphia, PA'); + // this._bingMap.current.setView({ + // mapTypeId: this.MicrosoftMaps.MapTypeId.aerial, + // center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude), + // zoom: 15, + // }); + }} + style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
{renderAnnotations(this.transparentFilter)}
{renderAnnotations(this.opaqueFilter)} {SnappingManager.GetIsDragging() ? null : renderAnnotations()} {this.annotationLayer} - - - e.stopPropagation()} placeholder="Enter location" /> - - - {this.renderMarkers()} - {this.allMapMarkers - .filter(marker => marker.infoWindowOpen) - .map(marker => ( - - ))} - {/* {this.handleMapCenter(this._map)} */} - + + {!MapBox.UseBing ? null : } +
+ + + e.stopPropagation()} placeholder="Enter location" /> + + + {this.renderMarkers()} + {this.allMapMarkers + .filter(marker => marker.infoWindowOpen) + .map(marker => ( + + ))} + {/* {this.handleMapCenter(this._map)} */} + +
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : ( { Document={this._finalLayout} DataDoc={this._resolvedDataDoc} addDocument={returnFalse} - rootSelected={this._textBox.props.isSelected} + rootSelected={returnFalse} //{this._textBox.props.isSelected} removeDocument={this.removeDoc} isDocumentActive={returnFalse} - isContentActive={this._textBox.props.isContentActive} + isContentActive={emptyFunction} styleProvider={this._textBox.props.styleProvider} docViewPath={this._textBox.props.docViewPath} ScreenToLocalTransform={this.getDocTransform} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 80b18b8b9..49acd8a57 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1487,6 +1487,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent node that wraps the hyerlink while (target && !target.dataset?.targethrefs) target = target.parentElement; FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true'); + if (pcords && pcords.inside > 0 && this._editorView.state.doc.nodeAt(pcords.inside)?.type === this._editorView.state.schema.nodes.dashDoc) { + return; + } } if (e.button === 0 && this.props.isSelected(true) && !e.altKey) { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 0a96297b7..e07517113 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -14,7 +14,7 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Ty import { AudioField } from '../../../../fields/URLField'; import { emptyFunction, emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; -import { Docs } from '../../../documents/Documents'; +import { Docs, DocumentOptions } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocumentManager } from '../../../util/DocumentManager'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; @@ -869,13 +869,13 @@ export class PresBox extends ViewBoxBaseComponent() { presDocView && SelectionManager.SelectView(presDocView, false); }; + focusElement = (doc: Doc, options: DocFocusOptions) => this.selectElement(doc); + //Regular click @action - selectElement = async (doc: Doc) => { + selectElement = async (doc: Doc, noNav = false) => { CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.()); - this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem); - // if (doc.presPinView) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0); - // else + !noNav && this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem); this.updateCurrentPresentation(DocCast(doc.context)); }; @@ -911,19 +911,19 @@ export class PresBox extends ViewBoxBaseComponent() { //regular click @action - regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true) => { + regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true, noNav = false) => { this.clearSelectedArray(); this.addToSelectedArray(doc); this._eleArray.splice(0, this._eleArray.length, ref); this._dragArray.splice(0, this._dragArray.length, drag); - focus && this.selectElement(doc); + focus && this.selectElement(doc, noNav); selectPres && this.selectPres(); }; - modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean) => { + modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean, noNav: boolean = false) => { if (cmdClick) this.multiSelect(doc, ref, drag); else if (shiftClick) this.shiftSelect(doc, ref, drag); - else this.regularSelect(doc, ref, drag, focus); + else this.regularSelect(doc, ref, drag, focus, noNav); }; static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance?.keyEvents(e); @@ -2253,7 +2253,7 @@ export class PresBox extends ViewBoxBaseComponent() { filterAddDocument={this.addDocumentFilter} removeDocument={returnFalse} dontRegisterView={true} - focus={this.selectElement} + focus={this.focusElement} scriptContext={this} ScreenToLocalTransform={this.getTransform} AddToMap={this.AddToMap} diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss index 415253af1..4f95f0c1f 100644 --- a/src/client/views/nodes/trails/PresElementBox.scss +++ b/src/client/views/nodes/trails/PresElementBox.scss @@ -44,6 +44,12 @@ $slide-active: #5b9fdd; display: flex; align-items: center; + .presItem-number { + cursor: pointer; + &:hover { + background-color: $light-blue; + } + } .presItem-name { display: flex; min-width: 20px; diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 788900b46..f265c1315 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -592,7 +592,17 @@ export class PresElementBox extends ViewBoxBaseComponent() { width: `calc(100% ${this.rootDoc.presExpandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`, cursor: isSelected ? 'text' : 'grab', }}> -
{`${this.indexInPres + 1}. `}
+
{ + e.stopPropagation(); + if (this._itemRef.current && this._dragRef.current) { + this.presBoxView?.modifierSelect(activeItem, this._itemRef.current, this._dragRef.current, false, false, false, true); + } + }} + onClick={e => e.stopPropagation()}>{`${this.indexInPres + 1}. `}
StrCast(activeItem.title)} SetValue={this.onSetValue} />
{/*
{"Movement speed"}
}>
{this.transition}
*/} -- cgit v1.2.3-70-g09d2 From 9c63f3833f15cc995c2255b83923384686127f3e Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 1 Mar 2023 11:57:05 -0500 Subject: changed inPlace link following to be lightbox. allowed collections to be labeled as lightboxes. lightbox collections display link targets in an overlay that hides the rest of their content --- src/client/documents/Documents.ts | 1 + src/client/util/DocumentManager.ts | 28 ++++++---- src/client/util/LinkFollower.ts | 10 ++-- src/client/views/DocumentDecorations.tsx | 13 ++--- src/client/views/MainView.tsx | 1 - src/client/views/PropertiesButtons.tsx | 62 +++++++++++----------- src/client/views/PropertiesView.tsx | 1 - src/client/views/collections/CollectionView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 22 ++++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 17 +++--- src/client/views/nodes/MapBox/MapBox.tsx | 1 - src/fields/documentSchemas.ts | 4 +- 13 files changed, 88 insertions(+), 78 deletions(-) (limited to 'src/client/views/nodes/MapBox') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d2ab67849..70c57e8fa 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -160,6 +160,7 @@ export class DocumentOptions { _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed _followLinkToggle?: boolean; // whether document, when clicked, toggles display of its link target _showTitle?: string; // field name to display in header (:hover is an optional suffix) + _isLightbox?: boolean; // whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target _showCaption?: string; // which field to display in the caption area. leave empty to have no caption _scrollTop?: number; // scroll location for pdfs _noAutoscroll?: boolean; // whether collections autoscroll when this item is dragged diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index d9273c2c9..0d10bed43 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -10,7 +10,7 @@ import { CollectionDockingView } from '../views/collections/CollectionDockingVie import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; import { CollectionView } from '../views/collections/CollectionView'; import { LightboxView } from '../views/LightboxView'; -import { DocFocusOptions, DocumentView, OpenWhereMod, ViewAdjustment } from '../views/nodes/DocumentView'; +import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod, ViewAdjustment } from '../views/nodes/DocumentView'; import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; @@ -307,16 +307,22 @@ export class DocumentManager { } //} if (options.playAudio) DocumentManager.playAudioAnno(focusView.rootDoc); - const doFocus = (forceDidFocus: boolean) => - focusView.focus(originalTarget, { - ...options, - originalTarget, - afterFocus: (didFocus: boolean) => - new Promise(res => { - focusAndFinish(forceDidFocus || didFocus); - res(ViewAdjustment.doNothing); - }), - }); + const doFocus = (forceDidFocus: boolean) => { + if (options.openInLightbox || (!options.originatingDoc?.followLinkLocation && DocCast(focusView.rootDoc.context)?._isLightbox)) { + focusView.props.addDocTab(targetDoc, OpenWhere.lightbox); + focusAndFinish(true); + } else { + focusView.focus(originalTarget, { + ...options, + originalTarget, + afterFocus: (didFocus: boolean) => + new Promise(res => { + focusAndFinish(forceDidFocus || didFocus); + res(ViewAdjustment.doNothing); + }), + }); + } + }; if (focusView.props.Document.layoutKey === 'layout_icon' && focusView.rootDoc.type !== DocumentType.SCRIPTING) { focusView.iconify(() => doFocus(true)); } else { diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index 50bc89ed2..e86e68c21 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -3,6 +3,7 @@ import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { List } from '../../fields/List'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; +import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; import { DocumentDecorations } from '../views/DocumentDecorations'; import { LightboxView } from '../views/LightboxView'; import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, ViewAdjustment } from '../views/nodes/DocumentView'; @@ -36,7 +37,9 @@ export class LinkFollower { const createTabForTarget = (didFocus: boolean) => new Promise(res => { const where = LightboxView.LightboxDoc ? OpenWhere.lightbox : (StrCast(sourceDoc.followLinkLocation, followLoc) as OpenWhere); - docViewProps.addDocTab(doc, where); + const lightbox = where === OpenWhere.lightbox && DocumentManager.GetContextPath(doc).find(container => container.isLightbox && DocumentManager.Instance.getDocumentView(container)); + const addDocTab = lightbox ? DocumentManager.Instance.getDocumentView(lightbox)?.ComponentView?.addDocTab : undefined; + (addDocTab ?? docViewProps.addDocTab)(doc, where); setTimeout(() => { const targDocView = DocumentManager.Instance.getFirstDocumentView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise. if (targDocView) { @@ -51,7 +54,7 @@ export class LinkFollower { }); } else { finished?.(); - res(where !== OpenWhere.inPlace || BoolCast(sourceDoc.followLinkZoom) ? ViewAdjustment.resetView : ViewAdjustment.doNothing); // for 'inPlace' resetting the initial focus&zoom would negate the zoom into the target + res(BoolCast(sourceDoc.followLinkZoom) ? ViewAdjustment.resetView : ViewAdjustment.doNothing); } }, 100); }); @@ -109,6 +112,7 @@ export class LinkFollower { zoomTime: NumCast(LinkManager.getOppositeAnchor(linkDoc, target)?.followLinkTransitionTime, 500), zoomScale: Cast(sourceDoc.followLinkZoomScale, 'number', null), easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any, + openInLightbox: sourceDoc.followLinkLocation === OpenWhere.lightbox, effect: sourceDoc, originatingDoc: sourceDoc, zoomTextSelections: BoolCast(sourceDoc.followLinkZoomText), @@ -123,7 +127,7 @@ export class LinkFollower { } else { const containerDocContext = DocumentManager.GetContextPath(target); const targetContexts = !sourceDoc.followLinkToOuterContext && containerDocContext.length ? [containerDocContext.lastElement()] : containerDocContext; - DocumentManager.Instance.jumpToDocument(target, options, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, OpenWhere.inPlace), finished), targetContexts, allFinished); + DocumentManager.Instance.jumpToDocument(target, options, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, OpenWhere.lightbox), finished), targetContexts, allFinished); } }; let movedTarget = false; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4e51f10a8..eeef01d17 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -27,7 +27,7 @@ import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { InkStrokeProperties } from './InkStrokeProperties'; import { LightboxView } from './LightboxView'; -import { DocumentView, OpenWhereMod } from './nodes/DocumentView'; +import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ImageBox } from './nodes/ImageBox'; import React = require('react'); @@ -283,11 +283,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc); Doc.deiconifyView(openDoc); } - LightboxView.SetLightboxDoc( - openDoc, - undefined, - selectedDocs.slice(1).map(view => view.props.Document) - ); + selectedDocs[0].props.addDocTab(openDoc, OpenWhere.lightbox); + // LightboxView.SetLightboxDoc( + // openDoc, + // undefined, + // selectedDocs.slice(1).map(view => view.props.Document) + // ); } } SelectionManager.DeselectAll(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 1e9220e1e..95c0f3755 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -692,7 +692,6 @@ export class MainView extends React.Component { if (doc.dockingConfig) return DashboardView.openDashboard(doc); // prettier-ignore switch (whereFields[0]) { - case OpenWhere.inPlace: // fall through to lightbox case OpenWhere.lightbox: return LightboxView.AddDocTab(doc, location); case OpenWhere.dashboard: return DashboardView.openDashboard(doc); case OpenWhere.fullScreen: return CollectionDockingView.OpenFullScreen(doc); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 7e33ee495..a5c01490f 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -7,7 +7,7 @@ import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { RichTextField } from '../../fields/RichTextField'; import { ScriptField } from '../../fields/ScriptField'; -import { BoolCast, StrCast } from '../../fields/Types'; +import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; import { DocUtils } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; @@ -20,6 +20,7 @@ import { VideoBox } from './nodes/VideoBox'; import { pasteImageBitmap } from './nodes/WebBoxRenderer'; import './PropertiesButtons.scss'; import React = require('react'); +import { LinkManager } from '../util/LinkManager'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -118,14 +119,14 @@ export class PropertiesButtons extends React.Component<{}, {}> { on => 'object-group' ); } - // this implments a container pattern by marking the targetDoc (collection) as an inPlace container, - // and then making the contained collection be a "menu" such that when any of its contents are clicked, - // they will open their targets in the outer container. To get back to the "menu", you click on the main container. - @computed get inPlaceContainerButton() { + // this implments a container pattern by marking the targetDoc (collection) as a lightbox + // that always fits its contents to its container and that hides all other documents when + // a link is followed that targets a 'lightbox' destination + @computed get isLightboxButton() { return this.propertyToggleBtn( - 'In Place', - 'isInPlaceContainer', - on => `${on ? 'Make' : 'Remove'} in place container flag`, + 'Lightbox', + 'isLightbox', + on => `${on ? 'Set' : 'Remove'} lightbox flag`, on => 'window-restore', onClick => { SelectionManager.Views().forEach(dv => { @@ -134,16 +135,12 @@ export class PropertiesButtons extends React.Component<{}, {}> { // containerDoc.noShadow = // containerDoc.noHighlighting = // containerDoc._forceActive = - containerDoc._fitContentsToBox = containerDoc._isInPlaceContainer = !containerDoc._isInPlaceContainer; - containerDoc._xPadding = containerDoc._yPadding = containerDoc._isInPlaceContainer ? 10 : undefined; + containerDoc._fitContentsToBox = containerDoc._isLightbox = !containerDoc._isLightbox; + containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined; const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]); dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); containerContents.forEach(doc => { - DocListCast(doc.links).forEach(link => { - doc.isLinkButton = true; - //doc.followLinkLocation = OpenWhere.inPlace; - link.linkDisplay = false; - }); + DocListCast(doc.links).forEach(link => (link.linkDisplay = false)); }); }); } @@ -290,7 +287,6 @@ export class PropertiesButtons extends React.Component<{}, {}> { @undoBatch @action handleOptionChange = (onClick: string) => { - this.selectedDoc && (this.selectedDoc.onClickBehavior = onClick); SelectionManager.Views() .filter(dv => dv.docView) .map(dv => dv.docView!) @@ -305,7 +301,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { break; case 'linkInPlace': docView.toggleFollowLink(false, false); - docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.inPlace : undefined; + docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.lightbox : undefined; break; case 'linkOnRight': docView.toggleFollowLink(false, false); @@ -327,24 +323,26 @@ export class PropertiesButtons extends React.Component<{}, {}> { ['nothing', 'Select Document'], ['enterPortal', 'Enter Portal'], ['toggleDetail', 'Toggle Detail'], - ['linkInPlace', 'Open in Place'], + ['linkInPlace', 'Open Link in Lightbox'], ['linkOnRight', 'Open Link on Right'], ]; - const currentSelection = this.selectedDoc.onClickBehavior; - // Get items to place into the list - const list = buttonList.map(value => { - const click = () => { - this.handleOptionChange(value[0]); - }; + const click = () => this.handleOptionChange(value[0]); + const linkButton = BoolCast(this.selectedDoc._isLinkButton); + const followLoc = this.selectedDoc._followLinkLocation; + const linkedToLightboxView = () => DocListCast(this.selectedDoc.links).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); + + let active = false; + // prettier-ignore + switch (value[0]) { + case 'linkInPlace': active = linkButton && followLoc === OpenWhere.lightbox && !linkedToLightboxView(); break; + case 'linkOnRight': active = linkButton && followLoc === OpenWhere.addRight; break; + case 'enterPortal': active = linkButton && this.selectedDoc._followLinkLocation === OpenWhere.lightbox && linkedToLightboxView(); break; + case 'toggleDetail':active = ScriptCast(this.selectedDoc.onClick)?.script.originalScript.includes('toggleDetail'); break; + case 'nothing': active = !linkButton && this.selectedDoc.onClick === undefined;break; + } return ( -
+
{value[1]}
); @@ -413,7 +411,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { {toggle(this.freezeThumb)} {toggle(this.forceActiveButton, { display: !isFreeForm && !isMap ? 'none' : '' })} {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })} - {toggle(this.inPlaceContainerButton, { display: !isFreeForm && !isMap ? 'none' : '' })} + {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })} {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })} {toggle(this.maskButton, { display: !isInk ? 'none' : '' })} {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })} diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index e9d1433c3..0fe165783 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1626,7 +1626,6 @@ export class PropertiesView extends React.Component { - {LinkManager.currentLink?.linksToAnnotation ? : null}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 90406ed3c..316f1e4e9 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -191,7 +191,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, OpenWhere.addRight), icon: 'project-diagram' }); } - !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? 'Unset' : 'Set'} inPlace Container`, event: () => (this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer), icon: 'project-diagram' }); + !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc._isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => (this.rootDoc._isLightbox = !this.rootDoc._isLightbox), icon: 'project-diagram' }); if (!Doc.noviceMode && false) { optionItems.push({ diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 01872df84..042c2b71b 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -350,7 +350,7 @@ export class TabDocView extends React.Component { // replace[:{left,right,top,bottom,}] - e.g., "replace" will replace the current stack contents, // "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name, // "replace:monkeys" - will replace any tab that has the label 'monkeys', or a tab with that label will be created by default on the right - // inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack + // lightbox - will add the document to any collection along the path from the document to the docking view that has a field isLightbox. if none is found, it adds to the full screen lightbox addDocTab = (doc: Doc, location: OpenWhere) => { SelectionManager.DeselectAll(); const whereFields = doc._viewType === CollectionViewType.Docking ? [OpenWhere.dashboard] : location.split(':'); @@ -359,17 +359,15 @@ export class TabDocView extends React.Component { // prettier-ignore switch (whereFields[0]) { case undefined: - case OpenWhere.inPlace: { - if (this.layoutDoc?.isInPlaceContainer) { - const inPlaceView = !doc.annotationOn && DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined; - const data = inPlaceView?.dataDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)]; - if (inPlaceView && (!data || data instanceof List)) { - inPlaceView.layoutDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)] = new List([doc]); - return true; - } - } - } - case OpenWhere.lightbox: return LightboxView.AddDocTab(doc, location); + case OpenWhere.lightbox: if (this.layoutDoc?._isLightbox) { + const lightboxView = !doc.annotationOn && DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined; + const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)]; + if (lightboxView && (!data || data instanceof List)) { + lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)] = new List([doc]); + return true; + } + } + return LightboxView.AddDocTab(doc, location); case OpenWhere.dashboard: return DashboardView.openDashboard(doc); case OpenWhere.fullScreen: return CollectionDockingView.OpenFullScreen(doc); case OpenWhere.close: return CollectionDockingView.CloseSplit(doc, whereMods); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 78804b070..be8a72369 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1414,8 +1414,8 @@ export class CollectionFreeFormView extends CollectionSubView(doc instanceof Doc ? [doc] : doc); return true; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index beb0f8e3a..8257c9c49 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -72,7 +72,6 @@ export enum ViewAdjustment { } export enum OpenWhere { - inPlace = 'inPlace', lightbox = 'lightbox', add = 'add', addLeft = 'add:left', @@ -109,6 +108,7 @@ export interface DocFocusOptions { effect?: Doc; // animation effect for focus noSelect?: boolean; // whether target should be selected after focusing playAudio?: boolean; // whether to play audio annotation on focus + openInLightbox?: boolean; // whether to open target in lightbox or just focus on it zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections toggleTarget?: boolean; // whether to toggle target on and off originatingDoc?: Doc; // document that triggered the focus @@ -123,6 +123,7 @@ export interface DocComponentView { scrollPreview?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt; // returns the duration of the focus scrollFocus?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt; // returns the duration of the focus brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void; + addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected. @@ -640,7 +641,8 @@ export class DocumentViewInternal extends DocComponent (func().result?.select === true ? this.props.select(false) : ''), 'on double click'); } else if (!Doc.IsSystem(this.rootDoc) && (![DocumentType.INK].includes(this.rootDoc.type as any) || Doc.UserDoc().openInkInLightbox) && !this.rootDoc.isLinkButton) { - UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.()), 'double tap'); + //UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.()), 'double tap'); + UndoManager.RunInBatch(() => this.props.addDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap'); SelectionManager.DeselectAll(); Doc.UnBrushDoc(this.props.Document); } @@ -870,13 +872,16 @@ export class DocumentViewInternal extends DocComponent { + makeIntoPortal = () => { const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document); if (!portalLink) { - const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }); - DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, 'portal to:portal from'); + DocUtils.MakeLink( + { doc: this.props.Document }, + { doc: Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }) }, + 'portal to:portal from' + ); } - this.Document.followLinkLocation = OpenWhere.inPlace; + this.Document.followLinkLocation = OpenWhere.lightbox; this.Document.followLinkZoom = true; this.Document._isLinkButton = true; }; diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 039f11cd1..c182dfd19 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -331,7 +331,6 @@ export class MapBox extends ViewBoxAnnotatableComponent { if (doc.lat !== undefined && doc.lng !== undefined) { const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng); - doc.onClickBehavior = 'enterPortal'; if (existingMarker) { Doc.AddDocToList(existingMarker, 'data', doc); } else { diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index 10324449f..5b489a96c 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -84,11 +84,11 @@ export const documentSchema = createSchema({ onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onPointerUp: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. - followLinkLocation: 'string', // flag for where to place content when following a click interaction (e.g., add:right, inPlace, default, ) + followLinkLocation: 'string', // flag for where to place content when following a click interaction (e.g., add:right, lightbox, default, ) hideLinkButton: 'boolean', // whether the blue link counter button should be hidden hideAllLinks: 'boolean', // whether all individual blue anchor dots should be hidden linkDisplay: 'boolean', // whether a link connection should be shown between link anchor endpoints. - isInPlaceContainer: 'boolean', // whether the marked object will display addDocTab() calls that target "inPlace" destinations + isLightbox: 'boolean', // whether the marked object will display addDocTab() calls that target "lightbox" destinations isLinkButton: 'boolean', // whether document functions as a link follow button to follow the first link on the document when clicked layers: listSpec('string'), // which layers the document is part of _lockedPosition: 'boolean', // whether the document can be moved (dragged) -- cgit v1.2.3-70-g09d2