From 0eb9d37a9dd1a6539f331f953d5f20c761d5f940 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 17 Aug 2023 10:00:10 -0400 Subject: fixed brushing of pinned viewports for chrome to not flash. converted pres to config_ and treeView to treeView_Property. fixed text toolbar to show/set text properties based on insertion point. --- src/client/views/pdf/PDFViewer.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 1319a236d..395fbd1ca 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -68,7 +68,7 @@ export class PDFViewer extends React.Component { private _styleRule: any; // stylesheet rule for making hyperlinks clickable private _retries = 0; // number of times tried to create the PDF viewer private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void); - private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }) => void); + private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void); private _annotationLayer: React.RefObject = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewer: React.RefObject = React.createRef(); @@ -95,7 +95,7 @@ export class PDFViewer extends React.Component { return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), this.props.childFilters(), this.props.childFiltersByRanges()); } @computed get inlineTextAnnotations() { - return this.allAnnotations.filter(a => a.textInlineAnnotations); + return this.allAnnotations.filter(a => a.text_inlineAnnotations); } componentDidMount = async () => { @@ -195,7 +195,7 @@ export class PDFViewer extends React.Component { return focusSpeed; }; crop = (region: Doc | undefined, addCrop?: boolean) => this.props.crop(region, addCrop); - brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view); + brushView = (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => this._setBrushViewer?.(view, transTime); @action setupPdfJsViewer = async () => { @@ -470,7 +470,7 @@ export class PDFViewer extends React.Component { }; setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func); - setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => (this._setBrushViewer = func); + setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => (this._setBrushViewer = func); @action onZoomWheel = (e: React.WheelEvent) => { -- cgit v1.2.3-70-g09d2 From bf1777b93be0707e17e3b3c0ca6c965facebfe14 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 27 Aug 2023 02:50:01 -0400 Subject: fixed filters to filter by linkedTo instead of lat/lng. made links to pushpins when anything is added to sidebar and pushpin is selected --- src/client/documents/Documents.ts | 32 +++++--- src/client/views/MainView.tsx | 40 +++++++++- src/client/views/SidebarAnnos.tsx | 8 +- src/client/views/nodes/MapBox/MapBox.tsx | 127 ++++++++++++++++--------------- src/client/views/pdf/PDFViewer.tsx | 2 +- src/fields/Doc.ts | 6 +- src/fields/documentSchemas.ts | 11 ++- 7 files changed, 140 insertions(+), 86 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index add12896e..ceee8c76f 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1075,8 +1075,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.MAP), new List(documents), options); } - export function PushpinDocument(lat: number, lng: number, infoWindowOpen: boolean, documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.PUSHPIN), new List(documents), { latitude: lat, longitude: lng, infoWindowOpen, ...options }, id); + export function PushpinDocument(latitude: number, longitude: number, infoWindowOpen: boolean, documents: Array, options: DocumentOptions, id?: string) { + return InstanceFromProto(Prototypes.get(DocumentType.PUSHPIN), new List(documents), { latitude, longitude, infoWindowOpen, ...options }, id); } // shouldn't ever need to create a KVP document-- instead set the LayoutTemplateString to be a KeyValueBox for the DocumentView (see addDocTab in TabDocView) @@ -1097,9 +1097,6 @@ export namespace Docs { export function HTMLMarkerDocument(documents: Array, options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _type_collection: CollectionViewType.Freeform }, id); } - export function MapMarkerDocument(lat: number, lng: number, infoWindowOpen: boolean, documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { latitude: lat, longitude: lng, infoWindowOpen, ...options, _type_collection: CollectionViewType.Freeform }, id); - } export function PileDocument(documents: Array, options: DocumentOptions, id?: string) { return InstanceFromProto( @@ -1274,10 +1271,23 @@ export namespace DocUtils { if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) { return false; } - for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== Utils.noDragsDocFilter.split(Doc.FilterSep)[0])) { const facet = filterFacets[facetKey]; + let links = true; + const linkedTo = filterFacets['-linkedTo'] && Array.from(Object.keys(filterFacets['-linkedTo']))?.[0]; + const linkedToField = filterFacets['-linkedTo']?.[linkedTo]; + const allLinks = linkedTo && linkedToField ? LinkManager.Instance.getAllRelatedLinks(d) : []; + // prettier-ignore + if (linkedTo) { + if (allLinks.some(d => linkedTo === Field.toScriptString(DocCast(DocCast(d.link_anchor_1)?.[linkedToField]))) || // + allLinks.some(d => linkedTo === Field.toScriptString(DocCast(DocCast(d.link_anchor_2)?.[linkedToField])))) + { + links = true; + } + else links = false + } + // facets that match some value in the field of the document (e.g. some text field) const matches = Object.keys(facet).filter(value => value !== 'cookies' && facet[value] === 'match'); @@ -1293,7 +1303,7 @@ export namespace DocUtils { // facets that have an x next to them const xs = Object.keys(facet).filter(value => facet[value] === 'x'); - if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; + if (!linkedTo && !unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value)); const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value)); const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined); @@ -1312,11 +1322,11 @@ export namespace DocUtils { }); // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria if (parentCollection?.childFilters_boolean === 'OR') { - if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; + if (links && satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; } // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria else { - if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; + if (!links || !satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; } } return parentCollection?.childFilters_boolean === 'OR' ? false : true; @@ -1800,8 +1810,8 @@ export namespace DocUtils { const longitude = result.exifData?.data?.GPSLongitude; const longitudeDirection = result.exifData?.data?.GPSLongitudeRef; if (latitude !== undefined && longitude !== undefined && latitudeDirection !== undefined && longitudeDirection !== undefined) { - proto.lat = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection); - proto.lng = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection); + proto.latitude = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection); + proto.longitude = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection); } } if (Upload.isVideoInformation(result)) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 344a401f3..2a9f0cc02 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -60,6 +60,7 @@ import { ImageBox } from './nodes/ImageBox'; import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup'; import { LinkDocPreview } from './nodes/LinkDocPreview'; import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu'; +import { MapBox } from './nodes/MapBox/MapBox'; import { RadialMenu } from './nodes/RadialMenu'; import { TaskCompletionBox } from './nodes/TaskCompletedBox'; import { OverlayView } from './OverlayView'; @@ -956,6 +957,42 @@ export class MainView extends React.Component { @computed get linkDocPreview() { return LinkDocPreview.LinkInfo ? : null; } + @observable mapBoxHackBool = false; + @computed get mapBoxHack() { + return this.mapBoxHackBool ? null : ( + r && (this.mapBoxHackBool = true))} + fieldKey="data" + select={returnFalse} + isSelected={returnFalse} + Document={this.headerBarDoc} + DataDoc={undefined} + addDocTab={returnFalse} + pinToPres={emptyFunction} + docViewPath={returnEmptyDoclist} + styleProvider={DefaultStyleProvider} + rootSelected={returnTrue} + addDocument={returnFalse} + removeDocument={returnFalse} + fitContentsToBox={returnTrue} + isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events) + isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive + ScreenToLocalTransform={Transform.Identity} + childHideResizeHandles={returnTrue} + childDragAction="move" + dontRegisterView={true} + PanelWidth={this.headerBarDocWidth} + PanelHeight={this.headerBarDocHeight} + renderDepth={0} + focus={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} + bringToFront={emptyFunction} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + /> + ); + } render() { return ( @@ -1011,7 +1048,7 @@ export class MainView extends React.Component { - + @@ -1020,6 +1057,7 @@ export class MainView extends React.Component { {this.snapLines} + {this.mapBoxHack} {/* */} diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 520485a71..c48a7241a 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -66,7 +66,7 @@ export class SidebarAnnos extends React.Component { return '_' + this.sidebarKey + '_childFilters'; } - anchorMenuClick = (anchor: Doc) => { + anchorMenuClick = (anchor: Doc, filterExlusions?: string[]) => { const startup = StrListCast(this.props.rootDoc.childFilters) .map(filter => filter.split(':')[0]) .join(' '); @@ -79,6 +79,7 @@ export class SidebarAnnos extends React.Component { _layout_autoHeight: true, _text_fontSize: StrCast(Doc.UserDoc().fontSize), _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), + target: 'HELLO' as any, }); FormattedTextBox.SelectOnLoad = target[Id]; FormattedTextBox.DontSelectInitialText = true; @@ -87,7 +88,7 @@ export class SidebarAnnos extends React.Component { const taggedContent = this.childFilters() .filter(data => data.split(':')[0]) - .filter(data => data.split(':')[0] !== 'latitude' && data.split(':')[0] !== 'longitude') + .filter(data => !filterExlusions?.includes(data.split(':')[0])) .map(data => { const key = data.split(':')[0]; const val = Field.Copy(this.allMetadata.get(key)); @@ -188,7 +189,7 @@ export class SidebarAnnos extends React.Component { }; render() { const renderTag = (tag: string) => { - const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`tags:${tag}:check`); + const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`tags::${tag}::check`); return (
Doc.setDocFilter(this.props.rootDoc, 'tags', tag, 'check', true, this.sidebarKey, e.shiftKey)}> {tag} @@ -231,7 +232,6 @@ export class SidebarAnnos extends React.Component { .map(key => renderMeta(key, this.allMetadata.get(key)))} */}
-
(); - @computed get inlineTextAnnotations() { - return this.allMapMarkers.filter(a => a.text_inlineAnnotations); - } @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); } - @computed get allMapMarkers() { + // this list contains pushpins and configs + @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); } + @computed get allPushpins() { + return this.allAnnotations.filter(anno => anno.type === DocumentType.PUSHPIN); + } @computed get SidebarShown() { return this.layoutDoc._layout_showSidebar ? true : false; } @@ -113,14 +116,20 @@ 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); - if (existingMarker) { - Doc.AddDocToList(existingMarker, 'data', doc); - } else { - const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {}); - this.addDocument(marker, this.annotationKey); - } + let existingPin = this.allPushpins.find(pin => pin.latitude === doc.latitude && pin.longitude === doc.longitude) ?? this.selectedPin; + if (doc.latitude !== undefined && doc.longitude !== undefined && !existingPin) { + existingPin = this.createPushpin(NumCast(doc.latitude), NumCast(doc.longitude), StrCast(doc.map)); + } + if (existingPin) { + setTimeout(() => { + // we use a timeout in case this is called from the sidebar which may have just added a link that hasn't made its way into th elink manager yet + if (!LinkManager.Instance.getAllRelatedLinks(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) { + const anchor = this.getAnchor(true, undefined, existingPin); + anchor && DocUtils.MakeLink(anchor, doc, { link_relationship: 'link to map location' }); + doc.latitude = existingPin?.latitude; + doc.longitude = existingPin?.longitude; + } + }); } }); //add to annotation list @@ -199,7 +208,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { const createFunc = undoable( action(() => { - const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); + const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false), ['latitude', 'longitude', '-linkedTo']); if (note && this.selectedPin) { note.latitude = this.selectedPin.latitude; note.longitude = this.selectedPin.longitude; @@ -309,7 +318,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { // Stores the pushpin as a MapMarkerDocument - const mapMarker = Docs.Create.PushpinDocument( + const pushpin = Docs.Create.PushpinDocument( NumCast(latitude), NumCast(longitude), false, @@ -317,8 +326,8 @@ export class MapBox extends ViewBoxAnnotatableComponent { + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps, existingPin?: Doc) => { /// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER const anchor = Docs.Create.ConfigDocument({ title: 'MapAnchor:' + this.rootDoc.title, text: StrCast(this.selectedPin?.map) || StrCast(this.rootDoc.map) || 'map location', - config_latitude: NumCast(this.selectedPin?.latitude ?? this.dataDoc.latitude), - config_longitude: NumCast(this.selectedPin?.longitude ?? this.dataDoc.longitude), + config_latitude: NumCast((existingPin ?? this.selectedPin)?.latitude ?? this.dataDoc.latitude), + config_longitude: NumCast((existingPin ?? this.selectedPin)?.longitude ?? this.dataDoc.longitude), config_map_zoom: NumCast(this.dataDoc.map_zoom), config_map_type: StrCast(this.dataDoc.map_type), - config_map: StrCast(this.selectedPin?.map) || StrCast(this.dataDoc.map), + config_map: StrCast((existingPin ?? this.selectedPin)?.map) || StrCast(this.dataDoc.map), layout_unrendered: true, }); if (anchor) { - anchor.mapPin = this.selectedPin; + anchor.mapPin = existingPin ?? this.selectedPin; if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; /* addAsAnnotation &&*/ this.addDocument(anchor); PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc); @@ -495,6 +499,7 @@ export class MapBox extends ViewBoxAnnotatableComponent this.allMapPushpins.map(doc => doc[Highlight]), + () => this.allAnnotations.map(doc => doc[Highlight]), () => { - const allPins = this.allMapPushpins.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin); - allPins.forEach(({ doc, pushpin }) => { + const allConfigPins = this.allAnnotations.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin); + allConfigPins.forEach(({ doc, pushpin }) => { if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) { this.recolorPin(pushpin); this.map_pinHighlighted.delete(pushpin); } }); - allPins.forEach(({ doc, pushpin }) => { + allConfigPins.forEach(({ doc, pushpin }) => { if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) { this.recolorPin(pushpin, 'orange'); this.map_pinHighlighted.set(pushpin, true); @@ -737,30 +742,32 @@ export class MapBox extends ViewBoxAnnotatableComponent {!this._mapReady ? null - : this.allMapPushpins.map(pushpin => ( - - ))} + : this.allAnnotations + .filter(anno => !anno.layout_unrendered) + .map(pushpin => ( + + ))}
{/* boolean; loaded?: (nw: number, nh: number, np: number) => void; setPdfViewer: (view: PDFViewer) => void; - anchorMenuClick?: () => undefined | ((anchor: Doc, summarize?: boolean) => void); + anchorMenuClick?: () => undefined | ((anchor: Doc) => void); crop: (region: Doc | undefined, addCrop?: boolean) => Doc | undefined; } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 7ba4f0e6f..501114157 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1483,14 +1483,14 @@ export namespace Doc { // filters document in a container collection: // all documents with the specified value for the specified key are included/excluded // based on the modifiers :"check", "x", undefined - export function setDocFilter(container: Opt, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) { + export function setDocFilter(container: Opt, key: string, value: any, modifiers: 'removeAll' | 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) { if (!container) return; const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'childFilters'; const childFilters = StrListCast(container[filterField]); runInAction(() => { for (let i = 0; i < childFilters.length; i++) { const fields = childFilters[i].split(FilterSep); // split key:value:modifier - if (fields[0] === key && (fields[1] === value.toString() || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) { + if (fields[0] === key && (fields[1] === value.toString() || modifiers === 'match' || modifiers === 'removeAll' || (fields[2] === 'match' && modifiers === 'remove'))) { if (fields[2] === modifiers && modifiers && fields[1] === value.toString()) { if (toggle) modifiers = 'remove'; else return; @@ -1502,7 +1502,7 @@ export namespace Doc { } if (!childFilters.length && modifiers === 'match' && value === undefined) { container[filterField] = undefined; - } else if (modifiers !== 'remove') { + } else if (modifiers !== 'remove' && modifiers !== 'removeAll') { !append && (childFilters.length = 0); childFilters.push(key + FilterSep + value + FilterSep + modifiers); container[filterField] = new List(childFilters); diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index e33a17416..8eeb52709 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -1,8 +1,7 @@ -import { makeInterface, createSchema, listSpec } from './Schema'; -import { ScriptField } from './ScriptField'; -import { Doc } from './Doc'; import { DateField } from './DateField'; -import { SchemaHeaderField } from './SchemaHeaderField'; +import { Doc } from './Doc'; +import { createSchema, listSpec, makeInterface } from './Schema'; +import { ScriptField } from './ScriptField'; export const documentSchema = createSchema({ // content properties @@ -26,8 +25,8 @@ export const documentSchema = createSchema({ z: 'number', // z "coordinate" - non-zero specifies the overlay layer of a freeformview zIndex: 'number', // zIndex of a document in a freeform view _layout_scrollTop: 'number', // scroll position of a scrollable document (pdf, text, web) - lat: 'number', - lng: 'number', + latitude: 'number', + longitude: 'number', // appearance properties on the layout '_backgroundGrid-spacing': 'number', // the size of the grid for collection views -- cgit v1.2.3-70-g09d2